'use strict'; /*@ Ox.VideoPreview Video Preview (options[, self]) -> Video Preview options Options self Shared private variable @*/ 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) { // e.offsetX does not work in Firefox var position = getPosition(event.clientX - that.offset().left); self.$frame.attr({src: getClosestFrame(position)}); self.timeout && clearTimeout(self.timeout); self.timeout = setTimeout(function() { self.$frame.attr({src: self.options.getFrame(position)}); }, 250); return Ox.formatDuration(position, 2); } }) .addClass('OxInterface') .bind({ click: click, mouseenter: startLoading, mouseleave: function() { stopLoading(); self.$frame.attr({src: self.options.getFrame(self.options.position)}); } }) .appendTo(that.$element); function click(e) { that.triggerEvent('click', { // e.offsetX does not work in Firefox position: getPosition(e.clientX - that.offset().left) }); } function getClosestFrame(position) { return self.loaded.length == 0 ? self.options.getFrame(self.options.position) : self.loaded.sort(function(a, b) { return Math.abs(a.position - position) - Math.abs(b.position - position); })[0].frame; } 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 position = getPosition(x), frame = self.options.getFrame(position); if ( !Ox.getObject(self.loaded, 'frame', frame) && !Ox.getObject(self.queue, 'frame', frame) ) { self.queue.push({frame: frame, position: position}); } }); }); self.queue.length && loadFrame(); function loadFrame() { var image = self.queue.shift(); $('') .load(function() { self.loaded.push(image); self.queue.length && loadFrame(); }) .attr({src: image.frame}) } } function stopLoading() { self.queue = []; self.timeout && clearTimeout(self.timeout); } 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'}); stopLoading(); self.$frame.attr({src: self.options.getFrame()}) .css(getFrameCSS()); self.$timeline.css({width: value + 'px'}); } } return that; };