(function ($, GLOBAL) { var SLIDER_TEMPLATE = '
LABELDEFAULT
', SELECT_TEMPLATE = '
LABEL
', OPTION_TEMPLATE = '', EXCLUDE_ARGS = { "theta": true }; // ======================================================================== // Helper method // ======================================================================== function getRelativeLocation(element, mouseEvent) { var parentOffset = element.offset(), x = mouseEvent.pageX || mouseEvent.originalEvent.pageX || mouseEvent.originalEvent.mozMovementX, y = mouseEvent.pageY || mouseEvent.originalEvent.pageY || mouseEvent.originalEvent.mozMovementY, relX = x - parentOffset.left, relY = y - parentOffset.top; return [ relX, relY ]; } // ======================================================================== // Download manager // ======================================================================== function createDownloadManager(container, poolSize, basepath) { var idleImages = [], processingQueue = [], manager = { clearProcessingQueue: function() { processingQueue = []; }, download: function(url) { processingQueue.push(url); download(); }, downloadFiles: function(filePattern, argName, argValues, args) { var baseFileName = filePattern, rStr = '{'+argName+'}'; for(key in args) { if(key !== argName) { baseFileName = baseFileName.replace('{'+key+'}', args[key]); } } for(idx in argValues) { processingQueue.push(basepath + '/' + baseFileName.replace(rStr, argValues[idx])); } } }; // Attach download manager to container container.data('download-manager', manager); container.bind('load-image', function(e) { manager.download(basepath + '/' + e.filename); }); function download() { while(idleImages.length > 0 && processingQueue.length > 0) { var img = idleImages.pop(), url = processingQueue.pop(); img.src = url; } } function onLoadCallback(arg) { var me = $(this), url = me.attr('src'); idleImages.push(this); container.trigger({ type: "image-loaded", url: url }); download(); } function onError() { idleImages.push(this); download(); } for(var i = 0; i < poolSize; ++i) { var img = new Image(); img.onload = onLoadCallback; img.onabort = onError; img.onerror = onError; idleImages.push(img); } return manager; } // ======================================================================== // Events // ======================================================================== function fireLoadImage(container) { // Extrat container info var filename = container.data('info')['name_pattern'], args = container.data('active-args'); // Update filename for(key in args) { filename = filename.replace('{'+key+'}', args[key]); } // Trigger event container.trigger({ type: 'load-image', arguments: args, filename: filename }); } // ======================================================================== // Listeners // ======================================================================== function initializeListeners(container) { var play = $('.play', container), stop = $('.stop', container), currentArgs = container.data('active-args'), activeArgName = null, activeValues = [], activeValueIndex = 0, keepAnimation = false; function animate() { if(activeArgName !== null) { activeValueIndex++; activeValueIndex = activeValueIndex % activeValues.length; updateActiveArgument(container, activeArgName, activeValues[activeValueIndex]); if(keepAnimation) { setTimeout(animate, 150); } } } // Update Control UI when camera position change container.bind('invalidate-viewport', function(){ // Update phi var currentPhi = Number(currentArgs.phi), phiSlider = $('input[name="phi"]', container), values = phiSlider.attr('data-values').split(':'), newIdx = 0, count = values.length; // Find matching index while(count--) { if(Number(values[count]) === currentPhi) { newIdx = count; count = 0; } } // Update slider value phiSlider.val(newIdx).trigger('change'); $('span.phi-value', container).html(currentArgs.phi); }); // Attach slider listener $('input[type="range"]', container).bind('change keyup mousemove',function(){ var slider = $(this), name = slider.attr('name'), values = slider.attr('data-values').split(":"), idx = slider.val(); updateActiveArgument(container, name, values[idx]); }); // Attach select listener $('select', container).change(function(){ var select = $(this), name = select.attr('name'), value = select.val(); updateActiveArgument(container, name, value); }); $('.toggle', container).click(function(){ container.toggleClass('small'); }); $('.reset', container).click(function(){ container.trigger('invalidate-size'); }); $('.label', container).click(function(){ var me = $(this), all = $('.label', container), selectObj = $('select', me.parent()), sliderObj = $('input', me.parent()); // Handle flag visibility all.removeClass('active'); me.addClass('active'); // Extract active parameter if(selectObj.length) { activeArgName = selectObj.attr('name'); activeValueIndex = 0; activeValues = []; $('option', selectObj).each(function(idx, elm) { activeValues.push($(this).text()); }); } if(sliderObj.length) { activeArgName = sliderObj.attr('name'); activeValueIndex = sliderObj.val(); activeValues = sliderObj.attr('data-values').split(':'); } }); play.click(function(){ play.hide(); stop.show(); keepAnimation = true; animate(); }); stop.click(function(){ stop.hide(); play.show(); keepAnimation = false; }); } // ------------------------------------------------------------------------ function updateActiveArgument(container, name, value) { if(container.data('active-args')[name] !== value) { var downloadManager = container.data('download-manager'), info = container.data('info'); container.data('active-args')[name] = value; $('span.'+name+'-value', container).html(value); downloadManager.clearProcessingQueue(); fireLoadImage(container); // Try to cache all argument values if(container.data('preload')) { downloadManager.downloadFiles(info['name_pattern'], name, info['arguments'][name]['values'], container.data('active-args')); } } } // ======================================================================== // UI // ======================================================================== var WidgetFactory = { "range": function(name, label, values, defaultValue) { return templateReplace(SLIDER_TEMPLATE, name, label, values, defaultValue); }, "list": function(name, label, values, defaultValue) { var options = []; for(var idx in values) { options.push(OPTION_TEMPLATE.replace('VALUE', values[idx])); } return templateReplace(SELECT_TEMPLATE, name, label, [ options.join('') ], defaultValue); } }; // ------------------------------------------------------------------------ function templateReplace( templateString, name, label, values, defaultValue) { return templateString.replace(/NAME/g, name).replace(/LABEL/g, label).replace(/VALUES/g, values.join(':')).replace(/SIZE/g, values.length - 1).replace(/DEFAULT/g, defaultValue).replace(/INDEX/g, values.indexOf(defaultValue)); } // ------------------------------------------------------------------------ function createControlPanel(container, args) { var htmlBuffer = [], controlContainer = $('
', { class: 'control', html: '
' }); // Loop over each option for (key in args) { var name = key, type = args[key].type, label = args[key].label, values = args[key].values, defaultValue = args[key]['default']; // Update default value updateActiveArgument(container, name, defaultValue); // Filter out from UI some pre-defined args if(EXCLUDE_ARGS.hasOwnProperty(key)) { continue; } // Build widget if needed if(values.length > 1) { htmlBuffer.push(WidgetFactory[type](name, label, values, defaultValue)); } } // Add control panel to UI htmlBuffer.sort(); $('