<script language="javascript">
    /* Define the {{ widget_name }} class */
    function {{ widget_name }}(frames, id, slider_ids, keyMap, dim_vals, notFound, load_json, mode, cached){
        this.frames = frames;
        this.fig_id = "fig_" + id;
        this.img_id = "_anim_img" + id;
        this.id = id;
        this.slider_ids = slider_ids;
        this.keyMap = keyMap
        this.current_frame = 0;
        this.current_vals = dim_vals;
        this.load_json = load_json;
        this.mode = mode;
        this.notFound = notFound;
	this.cached = cached;
	this.cache = {};
	this.init_slider(this.current_vals[0]);
    }

    {% block init_slider %}
    {{ widget_name }}.prototype.init_slider = function(init_val){
	if(this.cached) {
	    this.update_cache();
	    this.update(0);
	} else {
	    this.dynamic_update(0);
	}
    }
    {% endblock %}

    {% block populate_cache %}
    {{ widget_name }}.prototype.populate_cache = function(idx){
	if(this.load_json) {
	    var data_url = "{{ server }}/" + this.fig_id + "/" + idx;
	    this.cache[idx].load(data_url);
	} else {
	    this.cache[idx].html(this.frames[idx]);
	    if (this.embed) {
		delete this.frames[idx];
	    }
	}
    }
    {% endblock %}


    {% block dynamic_update %}
    {{ widget_name }}.prototype.dynamic_update = function(current){
	function callback(msg){
	    /* This callback receives data from Python as a string
	       in order to parse it correctly quotes are sliced off*/
	    var data = msg.content.data['text/plain'].slice(1, -1);
	    this.frames[current] = data;
	    this.update_cache();
	    this.update(current);
	}
        if(!(current in this.cache)) {
	    var kernel = IPython.notebook.kernel;
	    callbacks = {iopub: {output: $.proxy(callback, this)}};
	    var cmd = "holoviews.plotting.widgets.NdWidget.widgets['" + this.id + "'].update(" + current + ")";
	    kernel.execute("import holoviews;" + cmd, callbacks, {silent : false});
	} else {
	    this.update(current);
	}
    }
    {% endblock %}


    {{ widget_name }}.prototype.update_cache = function(){
	if(this.load_json) {
	    var frame_len = Object.keys(this.keyMap).length;
	} else {
	    var frame_len = Object.keys(this.frames).length;
	}
	for (var i=0; i<frame_len; i++) {
	    if(!this.load_json) {
		i = Object.keys(this.frames)[i];
	    }
	    if(!(i in this.cache)) {
		this.cache[i] = $('<div />').appendTo("#" + this.img_id).hide();
		var cache_id = this.img_id+"_"+i;
		this.cache[i].attr("id", cache_id);
		this.populate_cache(i);
	    }
	}
    }

    {{ widget_name }}.prototype.update = function(current){
	if(current in this.cache) {
	    $.each(this.cache, function(index, value) {
		value.hide();
	    });
	    this.cache[current].show();
	}
    }

    {{ widget_name }}.prototype.set_frame = function(dim_val, dim_idx){
        this.current_vals[dim_idx] = dim_val;
        var key = "(";
        for (var i=0; i<this.slider_ids.length; i++)
        {
            val = this.current_vals[i];
            if (!(isNaN(val))) {
                if (val % 1 === 0) { var fixed = 1;}
                else { var fixed = 10;}
                val = val.toFixed(fixed)
            }
            key += "'" + val + "'";

            if(i != this.slider_ids.length-1) { key += ', ';}
            else if(this.slider_ids.length == 1) { key += ',';}
        }
        key += ")";
	var current = this.keyMap[key];
	this.current_frame = current;
	if(this.cached) {
	    this.update(current)
	} else {
            this.dynamic_update(current)
	}
    }
</script>


<style>
div.hololayout {
    display: flex;
    align-items: center;
    margin: 0;
}

form.holoform {
    background-color: #fafafa;
    border-radius: 5px;
    overflow: hidden;
    padding: 0.8em;
}

div.holoslider {
    min-height: 0 !important;
    height: 0.8em;
}

div.holoformgroup {
    padding-right: 0.5em;
    padding-top: 0.5em;
    margin-bottom: 0.5em;
}

div.hologroup {
    padding-left: 0;
    padding-right: 0.6em;
}

.holoselect {
    width: 92%;
    margin-left: 0;
    margin-right: 0;
}

.holotext {
    width: 100%;
    padding-left:  0.5em;
    padding-right: 0;
}
</style>

<div class="hololayout row row-fluid">
    <div class="holoframe span9 col-xs-8 col-md-9">
      <div id="_anim_img{{ id }}"></div>
    </div>
    <div class="span3 col-xs-4 col-md-3">
    <form class="holoform well">
      {% for widget_data in widgets %}
      {% if widget_data['type'] == 'slider' %}
      <div class="form-group control-group holoformgroup">
	<label for="textInput{{ id }}_{{ widget_data['dim'] }}"><strong>{{ widget_data['dim_label'] }}:</strong></label>
	<div class="hololayout">
	  <div class="hologroup col-xs-4 span4">
            <input type="text" class="holotext form-control input-small" id="textInput{{ id }}_{{ widget_data['dim'] }}" value="" disabled>
	  </div><!--
          --><div class="holoslider span7 offset1 col-xs-7 col-xs-offset-1" id="_anim_widget{{ id }}_{{ widget_data['dim'] }}"></div>
	</div>
      </div>
      <script>
	/* Load JQuery UI CSS */
        function loadcssfile(filename){
            var fileref=document.createElement("link")
            fileref.setAttribute("rel", "stylesheet")
            fileref.setAttribute("type", "text/css")
            fileref.setAttribute("href", filename)
            document.getElementsByTagName("head")[0].appendChild(fileref)
        }
        loadcssfile("https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css");
        /* Check if jQuery and jQueryUI have been loaded
	   otherwise load with require.js */
        var jQuery = window.jQuery,
        // check for old versions of jQuery
        oldjQuery = jQuery && !!jQuery.fn.jquery.match(/^1\.[0-4](\.|$)/),
        jquery_path = '',
        paths = {},
        noConflict;
        var jQueryUI = jQuery.ui;
        // check for jQuery
        if (!jQuery || oldjQuery) {
            // load if it's not available or doesn't meet min standards
            paths.jQuery = jQuery;
            noConflict = !!oldjQuery;
        } else {
            // register the current jQuery
            define('jquery', [], function() { return jQuery; });
        }
        if (!jQueryUI) {
            paths.jQueryUI = "{{ CDN['jQueryUI'] }}"
        } else {
            define('jQueryUI', [], function() { return jQuery.ui; });
        }
	paths.underscore = "{{ CDN['underscore'] }}";
        var jquery_require = {paths: paths,
                              shim: {
                                  "jQueryUI": {
                                      exports:"$" ,
                                      deps: ['jquery']
                                  },
				  "underscore": {
				      exports: '_'
				  }
	                      }}
        require.config(jquery_require);
        require(["jquery", "jQueryUI", "underscore"], function(){
            if (noConflict) $.noConflict(true);
            var valMap{{ id }}_{{ widget_data['dim'] }} = {{ widget_data['vals'] }};
            $('#_anim_widget{{ id }}_{{ widget_data['dim'] }}').slider({
                animate: "fast",
                min: 0,
                max: valMap{{ id }}_{{ widget_data['dim'] }}.length - 1,
                value: 0,
                slide: _.throttle(function(event, ui) {
                    var dim_val = valMap{{ id }}_{{ widget_data['dim'] }}[ui.value];
                    $('#textInput{{ id }}_{{ widget_data['dim'] }}').val(dim_val);
                    anim{{ id }}.set_frame(dim_val, {{ widget_data['dim_idx'] }});
		    }, {{ throttle }})
            });
            $('#_anim_widget{{ id }}_{{ widget_data['dim'] }}').keypress(function(event) {
                if (event.which == 80 || event.which == 112) {
                    var start = $(this).slider("option", "value");
                    var stop =  $(this).slider("option", "max");
                    for (var i=start; i<=stop; i++) {
                        var delay = i*{{ delay }};
                        $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {
                            var val = {value:i};
                            $(this).slider('value',i);
                            $(this).slider("option", "slide")(null, val);
                        }, $(this)), delay);
							 }, $(this))(i);
                    }
                }
                if (event.which == 82 || event.which == 114) {
                    var start = $(this).slider("option", "value");
                    var stop =  $(this).slider("option", "min");
                    var count = 0;
                    for (var i=start; i>=stop; i--) {
                        var delay = count*{{ delay }};
                        count = count + 1;
                        $.proxy(function doSetTimeout(i) { setTimeout($.proxy(function() {
                            var val = {value:i};
                            $(this).slider('value',i);
                            $(this).slider("option", "slide")(null, val);
                        }, $(this)), delay);
							 }, $(this))(i);
                    }
                }
            });
            $('#textInput{{ id }}_{{ widget_data['dim'] }}').val(valMap{{ id }}_{{ widget_data['dim'] }}[0]);
        });
      </script>
      {% elif widget_data['type']=='dropdown' %}
      <div class="form-group control-group">
	<label for="textInput{{ id }}_{{ widget_data['dim'] }}"><strong>{{ widget_data['dim'] }}:</strong></label>
        <select class="holoselect form-control" id="_anim_widget{{ id }}_{{ widget_data['dim'] }}" >
        </select>
      </div>
      <script>
        var valMap{{ id }}_{{ widget_data['dim'] }} = {{ widget_data['vals'] }};
        var options = $("#_anim_widget{{ id }}_{{ widget_data['dim'] }}");
        for (var i=0; i<valMap{{ id }}_{{ widget_data['dim'] }}.length; i++){
            options.append($("<option>", {
                value: i,
                text:  valMap{{ id }}_{{ widget_data['dim'] }}[i]
            }));
        };
        $('#_anim_widget{{ id }}_{{ widget_data['dim'] }}').on('change', function(event, ui) {
            var dim_val = valMap{{ id }}_{{ widget_data['dim'] }}[this.value];
            if(anim{{ id }}) {
                anim{{ id }}.set_frame(dim_val, {{ widget_data['dim_idx'] }});
            }
        });
      </script>
      {% endif %}
      {% endfor %}
    </form>
    </div>
</div>


<script language="javascript">
    /* Instantiate the {{ widget_name }} class. */
    /* The IDs given should match those used in the template above. */
    (function() {
        var widget_ids = new Array({{ Nwidget }});
        {% for dim in dimensions %}
        widget_ids[{{ loop.index0 }}] = "_anim_widget{{ id }}_{{ dim }}";
        {% endfor %}
        var frame_data = {{ frames | safe }};
        var dim_vals = {{ init_dim_vals }};
        var keyMap = {{ key_data }};
        var notFound = "{{ notFound }}";

    	function create_widget() {
            setTimeout(function() {
	            anim{{ id }} = new {{ widget_name }}(frame_data, "{{ id }}", widget_ids,
                        keyMap, dim_vals, notFound, {{ load_json }}, {{ mode }}, {{ cached }});
	    }, 0);
	}	

	{% block create_widget %}
        create_widget();
	{% endblock %}
    })();
</script>
