// vim: et:ts=4:sw=4:sts=4:ft=javascript 'use strict'; Ox.VideoPreview = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ duration: 0, getFrame: null, fps: 25, frameRatio: 16/9, height: 256, position: void 0, scaleToFill: false, timeline: '', width: 256 }) .options(options || {}) .addClass('OxVideoPreview') .css({ width: self.options.width + 'px', height: self.options.height + 'px' }); self.loaded = []; self.queue = []; self.$frameElement = $('
') .addClass('OxFrame') .appendTo(that.$element) self.$frame = $('') .attr({src: self.options.getFrame(self.options.position)}) .css(getFrameCSS()) .appendTo(self.$frameElement); self.$timeline = $('') .addClass('OxTimeline') .attr({src: self.options.timeline}) .css({width: self.options.width + 'px'}) .appendTo(that.$element); self.$interface = Ox.Element({ tooltip: function(event) { //event.offsetX does not work in Firefox // fixme: use layerX then var position = getPosition(event.clientX - that.offset().left); self.$frame.attr({src: self.options.getFrame(position)}); return Ox.formatDuration(position, 2); } }) .addClass('OxInterface') .bind({ click: click, mouseenter: startLoading, mouseleave: function() { self.$frame.attr({src: self.options.getFrame(self.options.position)}); stopLoading(); } }) .appendTo(that.$element); function click(e) { that.triggerEvent('click', { // e.offsetX does not work in Firefox position: getPosition(e.clientX - that.offset().left) }); } function getFrameCSS() { var css = {}, elementWidth = self.options.width, elementHeight = self.options.height - 16, elementRatio = elementWidth / elementHeight, frameRatio = self.options.frameRatio, frameIsWider = frameRatio > elementRatio; if (self.options.scaleToFill) { css.width = frameIsWider ? elementHeight * frameRatio : elementWidth; css.height = frameIsWider ? elementHeight : elementWidth / frameRatio; css.marginLeft = frameIsWider ? (elementWidth - css.width) / 2 : 0; css.marginTop = frameIsWider ? 0 : (elementHeight - css.height) / 2; } else { css.width = frameIsWider ? elementWidth : elementHeight * frameRatio; css.height = frameIsWider ? elementWidth / frameRatio : elementHeight; css.marginLeft = frameIsWider ? 0 : (css.width - elementWidth) / 2; css.marginTop = frameIsWider ? (css.height - elementHeight) / 2 : 0; } return Ox.map(css, function(value) { return Math.round(value) + 'px'; }); } function getPosition(x) { return Math.round( self.options.duration * x / self.options.width * self.options.fps ) / self.options.fps; } function startLoading() { var last, steps = [Math.round(self.options.width / 2)]; while ((last = steps[steps.length - 1]) > 1) { steps.push(Math.round(last / 2)); } steps.forEach(function(step) { Ox.loop(0, self.options.width, step, function(x) { var frame = self.options.getFrame(getPosition(x)); if ( self.loaded.indexOf(frame) == -1 && self.queue.indexOf(frame) == -1 ) { self.queue.push(frame); } }); }); loadFrame(); function loadFrame() { $('') .load(function() { self.loaded.push(self.queue.shift()); self.queue.length && loadFrame(); }) .attr({src: self.queue[0]}); } } function stopLoading() { self.queue = []; } self.setOption = function(key, value) { if (key == 'height') { that.css({height: value + 'px'}); self.$frame.css(getFrameCSS()); } else if (key == 'position') { self.$frame.attr({src: self.options.getFrame(value)}); } else if (key == 'width') { that.css({width: value + 'px'}); self.$frame.attr({src: self.options.getFrame()}) .css(getFrameCSS()); self.$timeline.css({width: value + 'px'}); stopLoading(); } } return that; };