// vim: et:ts=4:sw=4:sts=4:ft=javascript /*@ Ox.VideoPlayer Generic Video Player (options, self) -> Video Player options Options annotation <[o]> Array of annotation tracks name Name of the annotation track data <[o]> Annotation data in In point (sec) out Out point (sec) text Text controlsBottom <[s]|[]> Bottom controls, from left to right Can be 'fullscreen', 'scale', 'title', 'find', 'menu', 'play', 'playInToOut', 'previous', 'next', 'mute', 'volume', 'size', 'timeline', 'space', 'position', 'resolution', 'settings'. The 'space' control is just empty space that separates left-aligned from right-aligned controls. controlsTop <[s]|[]> Top controls, from left to right duration Duration (sec) enableFind If true, enable find enableFullscreen If true, enable fullscreen enableKeyboard If true, enable keyboard controls enableMouse If true, click toggles paused externalControls If true, controls are outside the video find Query string focus focus on 'click', 'load' or 'mouseover' format video format (like 'webm') fps Frames per second fullscreen If true, video is in fullscreen height Height in px (excluding external controls) in In point (sec) keepIconVisible If true, play icon stays visible after mouseleave keepLargeTimelineVisible If true, large timeline stays visible after mouseleave keepLogoVisible If true, logo stays visible after mouseleave logo Logo image URL logoLink Logo link URL logoTitle Text for tooltip muted If true, video is muted paused If true, video is paused playInToOut If true, video plays only from in to out position Initial position (sec) poster Poster URL posterFrame Position of poster frame (sec) preload 'auto', 'metadata' or 'none' out Out point (sec) resolution resolution scaleToFill If true, scale to fill (otherwise, scale to fit) showControlsOnLoad If true, show controls on load showFind If true, show find input showHours If true, don't show hours for videos shorter than one hour showIcon If true, show play icon showIconOnLoad If true, show icon on load showLargeTimeline If true, show large timeline showMilliseconds Number of decimals to show showMarkers If true, show in/out/poster markers showProgress <|false> If true, show buffering progress sizeIsLarge If true, initial state of the size control is large subtitles URL or SRT or array of subtitles in In point (sec) out Out point (sec) text Text timeline Timeline image URL title Video title type 'play', 'in' or 'out' video Video URL String or array of strings ([part1, part2, ...]) or object ({resolution: url, ...} or {resolution: [part1, part2, ...], ...}) volume Volume (0-1) width Width in px @*/ Ox.VideoPlayer = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ annotations: [], controlsBottom: [], controlsTop: [], duration: 0, enableFind: false, enableFullscreen: false, enableKeyboard: false, externalControls: false, find: '', focus: 'click', format: '', fps: 25, fullscreen: false, height: 144, 'in': 0, keepIconVisible: false, keepLargeTimelineVisible: false, keepLogoVisible: false, logo: '', logoLink: '', logoTitle: '', muted: false, paused: false, playInToOut: false, position: 0, poster: '', posterFrame: -1, preload: 'auto', out: 0, resolution: 0, scaleToFill: false, showControlsOnLoad: false, showFind: false, showHours: false, showIcon: false, showIconOnLoad: false, showLargeTimeline: false, showMarkers: false, showMilliseconds: 0, showProgress: false, subtitles: [], timeline: '', title: '', type: 'play', video: '', volume: 1, width: 256 }) .options(options || {}) .addClass('OxVideoPlayer'); Ox.print('VIDEO PLAYER OPTIONS', self.options) Ox.UI.$window.bind({ resize: function() { self.options.fullscreen && setSizes(); } }); if (Ox.isObject(self.options.video)) { self.resolutions = Ox.sort(Object.keys(self.options.video)); if (!(self.options.resolution in self.options.video)) { self.options.resolution = self.resolutions[0]; } self.video = self.options.video[self.options.resolution]; } else { self.isPlaylist = Ox.isFunction(self.options.video); self.video = self.options.video; } if (self.options.playInToOut) { self['in'] = self.options['in']; self.out = self.options.out; self.options.duration = self.out - self['in']; } else { self['in'] = 0; self.out = self.options.duration || 86399; } self.options.position = Ox.limit(self.options.position, self['in'], self.out); self.millisecondsPerFrame = 1000 / self.options.fps; self.secondsPerFrame = 1 / self.options.fps; self.barHeight = 16; self.width = self.options.fullscreen ? window.innerWidth : self.options.width; self.height = self.options.fullscreen ? window.innerHeight : self.options.height; self.videoWidth = self.options.width; self.videoHeight = self.options.height; /* ---------------------------------------------------------------------------- Keyboard ---------------------------------------------------------------------------- */ if (self.options.enableKeyboard) { that.bindEvent({ key_0: toggleMuted, key_1: toggleScale, key_equal: function() { changeVolumeBy(0.1); }, key_escape: hideControlMenus, key_f: focusFind, key_g: function() { goToNextResult(1); }, key_left: function() { setPosition(self.options.position - self.secondsPerFrame); }, key_minus: function() { changeVolumeBy(-0.1); }, key_p: function() { playInToOut(); }, key_right: function() { setPosition(self.options.position + self.secondsPerFrame); }, key_shift_f: function() { self.options.enableFullscreen && toggleFullscreen(); }, key_shift_g: function() { goToNextResult(-1); }, key_shift_left: function() { setPosition(self.options.position - 1); }, key_shift_right: function() { setPosition(self.options.position + 1); }, key_space: togglePaused }); if (self.options.focus == 'mouseenter') { that.bind({ mouseenter: function() { if (!self.inputHasFocus) { that.gainFocus(); } }, mouseleave: function() { that.loseFocus(); } }); } else { that.bind({ click: that.gainFocus }); } } /* ---------------------------------------------------------------------------- Mouse ---------------------------------------------------------------------------- */ if ( (!self.options.externalControls && (self.options.controlsTop.length || self.options.controlsBottom.length)) || self.options.showIcon ) { that.bind({ mouseenter: function() { showControls(); self.mouseHasLeft = false; //Ox.print('MOUSE HAS ENTERED') }, mouseleave: function() { hideControls(); self.mouseHasLeft = true; //Ox.print('MOUSE HAS LEFT') } }); } /* ---------------------------------------------------------------------------- Video ---------------------------------------------------------------------------- */ self.$videoContainer = Ox.Element() .addClass('OxVideoContainer') .css({ top: self.options.externalControls && self.options.controlsTop.length ? '16px' : 0, }) .appendTo(that.$element) if (self.options.type == 'play') { self.options.enableMouse && self.$videoContainer.bindEvent({ anyclick: function(e) { if (!$(e.target).is('.OxLogo')) { togglePaused(); } }, dragstart: dragstart, drag: drag, dragend: dragend }); self.$video = Ox.VideoElement( Ox.extend({ preload: self.options.preload, src: self.video }, !self.options.paused ? { autoplay: 'autoplay' } : {}/*, self.options.poster ? { poster: self.options.poster } : {}*/) ) .bindEvent(Ox.extend({ ended: ended, loadedmetadata: loadedmetadata, pointschange: pointschange, seeked: seeked, seeking: seeking, sizechange: sizechange }, self.options.progress ? { progress: progress } : {})) .appendTo(self.$videoContainer); self.$video.$element.css({position: 'absolute'}); } else { self.options.enableMouse && self.$videoContainer.bind({ click: function(e) { if (!$(e.target).is('.OxLogo')) { goToPoint(); } } }); self.$video = $('') .attr({ src: Ox.UI.PATH + 'png/transparent.png' }) .appendTo(self.$videoContainer); loadImage(); } /* ---------------------------------------------------------------------------- Poster ---------------------------------------------------------------------------- */ if (self.options.poster) { self.$poster = $('') .addClass('OxPoster') .attr({ src: self.options.poster }) .appendTo(self.$videoContainer); self.posterIsVisible = true; } /* ---------------------------------------------------------------------------- Logo ---------------------------------------------------------------------------- */ if (self.options.logo) { self.$logo = $('') .addClass('OxLogo') .attr({ src: self.options.logo }) .css({ cursor: self.options.logoLink ? 'pointer' : 'default' }) .appendTo(self.$videoContainer); if (self.options.logoTitle) { self.$logoTooltip = Ox.Tooltip({ title: self.options.logoTitle }); } } /* ---------------------------------------------------------------------------- Icons ---------------------------------------------------------------------------- */ self.$loadingIcon = $('') .addClass('OxLoadingIcon OxVideo') .attr({ src: Ox.UI.getImageURL('symbolLoadingAnimated', 'modern') }) .appendTo(self.$videoContainer); if (self.options.showIcon || self.options.showIconOnLoad) { self.$playIcon = $('') .addClass('OxPlayIcon OxVideo') .attr({ src: Ox.UI.getImageURL('symbol' + ( self.options.paused ? 'Play' : 'Pause' ), 'modern') }) .appendTo(self.$videoContainer); if (self.options.showIcon) { self.$playIcon.addClass('OxInterface'); } if (self.options.showIconOnLoad) { self.iconIsVisible = true; } } /* ---------------------------------------------------------------------------- Markers ---------------------------------------------------------------------------- */ if (self.options.showMarkers) { self.$posterMarker = {}; ['left', 'center', 'right'].forEach(function(position) { var titleCase = Ox.toTitleCase(position); self.$posterMarker[position] = $('
') .addClass('OxPosterMarker OxPosterMarker' + titleCase) .appendTo(self.$videoContainer); }); self.$pointMarker = {}; ['in', 'out'].forEach(function(point) { self.$pointMarker[point] = {}; ['top', 'bottom'].forEach(function(edge) { var titleCase = Ox.toTitleCase(point) + Ox.toTitleCase(edge); self.$pointMarker[point][edge] = $('') .addClass('OxPointMarker OxPointMarker' + titleCase) .attr({ src: Ox.UI.getImageURL('marker' + titleCase) }) .appendTo(self.$videoContainer); }); }); } /* ---------------------------------------------------------------------------- Subtitles ---------------------------------------------------------------------------- */ if (self.options.subtitles.length || true) { // fixme self.$subtitle = $('
') .addClass('OxSubtitle') .appendTo(self.$videoContainer); } /* ---------------------------------------------------------------------------- Controls ---------------------------------------------------------------------------- */ ['top', 'bottom'].forEach(function(edge) { var titleCase = Ox.toTitleCase(edge); if (self.options['controls' + titleCase].length) { self['$controls' + titleCase] = Ox.Bar({ size: self.barHeight }) .addClass('OxControls' + (self.options.externalControls ? '' : ' OxOnScreen')) .css({ opacity: self.options.externalControls ? 1 : 0 }) .css(edge, 0) .appendTo(that.$element); self.options['controls' + titleCase].forEach(function(control) { if (control == 'find') { self.$findButton = Ox.Button({ style: 'symbol', title: 'find', tooltip: 'Find', type: 'image' }) .bindEvent({ click: toggleFind }) .appendTo(self['$controls' + titleCase]); } else if (control == 'fullscreen') { self.$fullscreenButton = Ox.Button({ style: 'symbol', title: [ {id: 'grow', title: 'grow', selected: !self.options.fullscreen}, {id: 'shrink', title: 'shrink', selected: self.options.fullscreen} ], tooltip: ['Enter Fullscreen', 'Exit Fullscreen'], type: 'image' }) .bindEvent({ click: function() { toggleFullscreen('button'); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'goto') { self.$setButton = Ox.Button({ style: 'symbol', title: 'goTo' + Ox.toTitleCase(self.options.type), tooltip: 'Go to ' + Ox.toTitleCase(self.options.type) + ' Point', type: 'image' }) .bindEvent({ click: goToPoint }) .appendTo(self['$controls' + titleCase]); } else if (control == 'mute') { self.$muteButton = Ox.Button({ style: 'symbol', title: [ {id: 'mute', title: 'mute', selected: !self.options.muted}, {id: 'unmute', title: 'unmute', selected: self.options.muted} ], tooltip: ['Mute', 'Unmute'], type: 'image' }) .bindEvent({ click: function() { toggleMuted('button'); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'next') { self.$nextClipButton = Ox.Button({ style: 'symbol', title: 'arrowRight', tooltip: 'Next', type: 'image' }) .bindEvent({ click: function() { goToNextClip(1); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'play') { self.$playButton = Ox.Button({ style: 'symbol', // FIXME: this is retarded, fix Ox.Button title: [ {id: 'play', title: 'play', selected: self.options.paused}, {id: 'pause', title: 'pause', selected: !self.options.paused} ], tooltip: ['Play', 'Pause'], type: 'image' }) .bindEvent({ click: function() { togglePaused('button'); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'playInToOut') { self.$playInToOutButton = Ox.Button({ style: 'symbol', title: 'playInToOut', tooltip: 'Play In to Out', type: 'image' }) .bindEvent({ click: playInToOut }) .appendTo(self['$controls' + titleCase]); } else if (control == 'position') { self.positionWidth = 48 + !!self.options.showMilliseconds * 2 + self.options.showMilliseconds * 6; self.$position = Ox.Element({ tooltip: 'Position' }) .addClass('OxPosition') .css({ width: (self.positionWidth - 4) + 'px', }) .html(formatPosition()) .bind({ click: function() { if (self.options.type == 'play') { if (!self.options.paused) { self.playOnSubmit = true; togglePaused(); } else if (self.playOnLoad) { // if clicked during resolution switch, // don't play on load self.playOnLoad = false; self.playOnSubmit = true; } } self.$position.hide(); self.$positionInput .options({ value: formatPosition() }) .show() .focusInput(false); } }) .appendTo(self['$controls' + titleCase]); self.$positionInput = Ox.Input({ value: formatPosition(), width: self.positionWidth }) .addClass('OxPositionInput') .bindEvent({ focus: function() { self.inputHasFocus = true; }, blur: function() { self.inputHasFocus = false; submitPositionInput(); } }) .appendTo(self['$controls' + titleCase].$element); self.$positionInput.children('input').css({ width: (self.positionWidth - 6) + 'px', fontSize: '9px' }); } else if (control == 'previous') { self.$previousClipButton = Ox.Button({ style: 'symbol', title: 'arrowLeft', tooltip: 'Previous', type: 'image' }) .bindEvent({ click: function() { goToNextClip(-1); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'resolution') { self.$resolutionButton = Ox.Element({ tooltip: 'Resolution' }) .addClass('OxResolutionButton') .html(self.options.resolution + 'p') .bind({ click: function() { self.$resolution.toggle(); } }) .appendTo(self['$controls' + titleCase]); self.$resolution = $('
') .addClass('OxResolution') .css({ height: (self.resolutions.length * 16) + 'px' }) .bind({ click: function(e) { var resolution = $(e.target).parent().data('resolution'); self.$resolution.hide(); if (resolution != self.options.resolution) { self.$resolution.children().each(function() { var $this = $(this); $this.children()[1].src = $this.data('resolution') == resolution ? Ox.UI.getImageURL('symbolCheck') : Ox.UI.PATH + 'png/transparent.png' }); self.$resolutionButton.html(resolution + 'p'); self.options.resolution = resolution setResolution(); } } }) .appendTo(that.$element); self.resolutions.forEach(function(resolution, i) { var $item = $('
') .data({ resolution: resolution }) .bind({ mouseenter: function() { $(this).addClass('OxSelected'); }, mouseleave: function() { $(this).removeClass('OxSelected'); } }) .appendTo(self.$resolution); $('
') .html(resolution + 'p') .appendTo($item); $('') .attr({ src: resolution == self.options.resolution ? Ox.UI.getImageURL('symbolCheck', 'modern') : Ox.UI.PATH + 'png/transparent.png' }) .appendTo($item); }); } else if (control == 'scale') { self.$scaleButton = Ox.Button({ style: 'symbol', title: [ {id: 'fill', title: 'fill', selected: !self.options.scaleToFill}, {id: 'fit', title: 'fit', selected: self.options.scaleToFill} ], tooltip: ['Scale to Fill', 'Scale to Fit'], type: 'image' }) .bindEvent('click', function() { toggleScale('button'); }) .appendTo(self['$controls' + titleCase]); } else if (control == 'set') { self.$setButton = Ox.Button({ style: 'symbol', title: 'set' + Ox.toTitleCase(self.options.type), tooltip: 'Set ' + Ox.toTitleCase(self.options.type) + ' Point', type: 'image' }) .bindEvent({ click: setPoint }) .appendTo(self['$controls' + titleCase]); } else if (control == 'size') { self.$sizeButton = Ox.Button({ style: 'symbol', title: [ {id: 'grow', title: 'grow', selected: !self.options.sizeIsLarge}, {id: 'shrink', title: 'shrink', selected: self.options.sizeIsLarge} ], tooltip: ['Larger', 'Smaller'], type: 'image' }) .bindEvent('click', toggleSize) .appendTo(self['$controls' + titleCase]); } else if (control == 'space') { self['$space' + titleCase] = $('
') .html(' ') // fixme: ?? .appendTo(self['$controls' + titleCase].$element); } else if (control == 'timeline') { /* if (self.options.showProgress) { self.$progress = $('') .attr({ src: getProgressImageURL() }) .css({ float: 'left', height: self.barHeight + 'px', }) .appendTo(self.$timelineImages.$element); } */ if (self.options.duration) { self.$timeline = getTimeline() } else { self.$timeline = Ox.Element() .html(' '); } self.$timeline.appendTo(self['$controls' + titleCase]); } else if (control == 'title') { self.$title = $('
') .addClass('OxTitle') .html(self.options.title) .appendTo(self['$controls' + titleCase].$element); } else if (control == 'volume') { self.$volumeButton = Ox.Button({ style: 'symbol', title: 'mute', tooltip: 'Volume', type: 'image' }) .bindEvent({ click: toggleVolume }) .appendTo(self['$controls' + titleCase]); } }); } }); /* ---------------------------------------------------------------------------- Find ---------------------------------------------------------------------------- */ if (self.options.enableFind) { self.$find = $('
') .addClass('OxControls OxFind') .css({ top: self.options.controlsTop.length ? '16px' : 0 }) .appendTo(that.$element); self.$results = Ox.Element({ tooltip: 'Results' }) .addClass('OxResults') .html('0') .appendTo(self.$find); self.$previousResultButton = Ox.Button({ disabled: true, style: 'symbol', title: 'arrowLeft', tooltip: 'Previous', type: 'image' }) .bindEvent({ click: function() { goToNextResult(-1); } }) .appendTo(self.$find); self.$nextResultButton = Ox.Button({ disabled: true, style: 'symbol', title: 'arrowRight', tooltip: 'Next', type: 'image' }) .bindEvent({ click: function() { goToNextResult(1); } }) .appendTo(self.$find); self.$findInput = Ox.Input({ changeOnKeypress: true, value: self.options.find }) .bindEvent({ blur: function() { self.inputHasFocus = false; }, focus: function() { self.inputHasFocus = true; }, change: function(data) { submitFindInput(data.value, false); }, submit: function(data) { self.inputHasFocus = false; submitFindInput(data.value, true); } }) .appendTo(self.$find); self.$clearButton = Ox.Button({ disabled: !self.options.find, style: 'symbol', title: 'delete', tooltip: 'Clear', type: 'image' }) .bindEvent({ click: function() { self.options.find = ''; self.results = []; self.$results.html('0'); self.$findInput.clearInput(); self.subtitle && setSubtitleText(); self.$timeline && self.$timeline.options({ find: self.options.find, results: self.results }); //setTimeout(self.$findInput.focusInput, 10); } }) .appendTo(self.$find); self.$hideFindButton = Ox.Button({ style: 'symbol', title: 'close', tooltip: 'Hide', type: 'image' }) .bindEvent({ click: toggleFind }) .appendTo(self.$find); } /* ---------------------------------------------------------------------------- Volume ---------------------------------------------------------------------------- */ if (self.options.enableVolume || true) { // fixme self.$volume = $('
') .addClass('OxControls OxVolume') .css({ bottom: self.options.controlsBottom.length ? '16px' : 0 }) .appendTo(that.$element); self.$hideVolumeButton = Ox.Button({ style: 'symbol', title: 'close', tooltip: 'Hide', type: 'image' }) .bindEvent({ click: toggleVolume }) .appendTo(self.$volume); self.$muteButton = Ox.Button({ style: 'symbol', title: [ {id: 'mute', title: 'mute', selected: !self.options.muted}, {id: 'unmute', title: 'unmute', selected: self.options.muted} ], tooltip: ['Mute', 'Unmute'], type: 'image' }) .bindEvent({ click: function() { toggleMuted(); } }) .appendTo(self.$volume); self.$volumeInput = Ox.Range({ max: 1, min: 0, step: 0.001, value: self.options.muted ? 0 : self.options.volume }) .bindEvent({ change: function(data) { setVolume(data.value); } }) .appendTo(self.$volume); self.$volumeValue = $('
') .addClass('OxVolumeValue') .html(self.options.muted ? 0 : Math.round(self.options.volume * 100)) .appendTo(self.$volume); } self.options.type != 'play' && self.options.showMarkers && setMarkers(); self.results = []; if (self.options.subtitles) { if (Ox.isArray(self.options.subtitles)) { loadedsubtitles(); } else { if (self.options.subtitles.indexOf('\n') > -1) { self.options.subtitles = Ox.parseSRT(self.options.subtitles); loadedsubtitles(); } else { Ox.get(self.options.subtitles, function(data) { self.options.subtitles = Ox.parseSRT(data); loadedsubtitles(); }); self.options.subtitles = []; } } } setSizes(); function clearInterfaceTimeout() { clearTimeout(self.interfaceTimeout); self.interfaceTimeout = 0; } function dragstart() { self.drag = { position: self.options.position, paused: self.options.paused } !self.options.paused && togglePaused(); } function drag(e) { setPosition(self.drag.position - e.clientDX / 25); that.triggerEvent('position', { position: self.options.position }); } function dragend() { !self.drag.paused && togglePaused(); } function ended() { !self.options.paused && togglePaused(); if (self.options.poster) { self.$poster.animate({ opacity: 1 }, 250); self.posterIsVisible = true; } if (self.options.showIconOnLoad) { self.$playIcon.animate({ opacity: 1 }, 250); self.iconIsVisible = true; } } function find(query) { var results = []; if (query.length) { query = query.toLowerCase(); results = Ox.map(self.options.subtitles, function(subtitle) { return subtitle.text.toLowerCase().indexOf(query) > -1 ? { 'in': subtitle['in'], out: subtitle.out } : null; }); } return results; } function focusFind() { !self.interfaceIsVisible && showControls(); // need timeout so the "f" doesn't appear in the input field setTimeout(function() { if (self.$find.is(':hidden')) { toggleFind(); } else { self.$findInput.focusInput(); } }, 0); } function formatPosition(position) { position = Ox.isUndefined(position) ? self.options.position : position; return Ox.formatDuration(position, self.options.showMilliseconds); } function getCSS(element) { var css; if (element == 'controlsTop' || element == 'controlsBottom') { css = { width: self.width + 'px' }; } else if (element == 'find') { css = { width: Math.min(216, self.width) + 'px' }; } else if (element == 'loadingIcon') { css = { width: self.iconSize + 'px', height: self.iconSize + 'px' }; } else if (element == 'logo') { var logoHeight = Math.round(self.height / 10), logoMargin = Math.round(self.height / 20); css = { left: logoMargin + 'px', top: logoMargin + (self.controlsTopAreVisible ? 16 : 0) + 'px', height: logoHeight + 'px', }; } else if (element == 'player') { var height = self.options.fullscreen ? window.innerHeight : self.height; if (self.options.externalControls) { height += ( !!self.options.controlsTop.length + !!self.options.controlsBottom.length ) * self.barHeight; } css = Ox.extend({ width: self.width + 'px', height: height + 'px' }, self.options.fullscreen ? { left: 0, top: 0 } : {}, self.exitFullscreen ? { left: self.absoluteOffset.left, top: self.absoluteOffset.top } : {}); } else if (element == 'playIcon') { var playIconPadding = Math.round(self.iconSize * 1/8), playIconSize = self.iconSize - 2 * playIconPadding - 4; css = { width: playIconSize + 'px', height: playIconSize + 'px', padding: playIconPadding + 'px', borderRadius: Math.round(self.iconSize / 2) + 'px' }; } else if (element == 'progress') { css = { width: self.timelineImageWidth + 'px', marginLeft: -self.timelineImageWidth + 'px' }; } else if (element == 'subtitle') { css = { bottom: (parseInt(self.height / 16) + !!self.controlsBottomAreVisible * 16) + 'px', width: self.width + 'px', fontSize: parseInt(self.height / 20) + 'px', WebkitTextStroke: (self.height / 1000) + 'px rgb(0, 0, 0)' }; } else if (element == 'spaceBottom' || element == 'timeline') { css = { width: self.timelineWidth + 'px' }; } else if (element == 'spaceTop' || element == 'title') { css = { width: getTitleWidth() + 'px' }; } else if (element == 'videoContainer') { css = { width: self.width + 'px', height: self.height + 'px' }; } else if (element == 'volume') { css = { width: Math.min(184, self.width) }; } return css; } function getPosition(e) { // fixme: no offsetX in firefox??? if ($.browser.mozilla) { //Ox.print(e, e.layerX - 56) return Ox.limit( (e.layerX - 48 - self.barHeight / 2) / self.timelineImageWidth * self.$video.duration(), 0, self.$video.duration() ); } else { /*Ox.print(e.offsetX, Ox.limit( (e.offsetX - self.barHeight / 2) / self.timelineImageWidth * self.video.duration, 0, self.video.duration ))*/ return Ox.limit( (e.offsetX - self.barHeight / 2) / self.timelineImageWidth * self.$video.duration(), 0, self.$video.duration() ); } } function getPosterMarkerCSS() { self.videoCSS = getVideoCSS(); var left = Math.floor((self.videoCSS.width - self.videoCSS.height) / 2), right = Math.ceil((self.videoCSS.width - self.videoCSS.height) / 2); return { center: { left: self.videoCSS.left + left + 'px', top: self.videoCSS.top + 'px', width: (self.videoCSS.height - 2) + 'px', height: (self.videoCSS.height - 2) + 'px' }, left: { left: self.videoCSS.left + 'px', top: self.videoCSS.top + 'px', width: left + 'px', height: self.videoCSS.height + 'px' }, right: { left: self.videoCSS.left + left + self.videoCSS.height + 'px', top: self.videoCSS.top + 'px', width: right + 'px', height: self.videoCSS.height + 'px' } }; } function getProgressImageURL() { //Ox.print('---', self.timelineImageWidth) if (!self.timelineImageWidth) return; var width = self.timelineImageWidth, height = self.barHeight, canvas = $('') .attr({ width: width, height: height })[0], context = canvas.getContext('2d'), imageData, data; context.fillStyle = 'rgba(255, 0, 0, 0.5)'; context.fillRect(0, 0, width, height); imageData = context.getImageData(0, 0, width, height), data = imageData.data; self.buffered.forEach(function(range) { var left = Math.round(range[0] * width / self.$video.duration()), right = Math.round(range[1] * width / self.$video.duration()); Ox.loop(left, right, function(x) { Ox.loop(height, function(y) { index = x * 4 + y * 4 * width; data[index + 3] = 0; }); }); }); context.putImageData(imageData, 0, 0); return canvas.toDataURL(); } function getSubtitle() { var subtitle = ''; Ox.forEach(self.options.subtitles, function(v) { if ( v['in'] <= self.options.position && v.out >= self.options.position ) { subtitle = v.text; return false; } }); return subtitle; } function getTimeline() { var $timeline = Ox.SmallVideoTimeline({ _offset: getTimelineLeft(), duration: self.options.duration, find: self.options.find, 'in': self.options['in'], out: self.options.out, paused: self.options.paused, results: self.results, showMilliseconds: self.options.showMilliseconds, subtitles: self.options.subtitles, timeline: self.options.timeline, type: 'player', width: getTimelineWidth() }) .css({ float: 'left' }) .css({ background: '-moz-linear-gradient(top, rgba(0, 0, 0, 0.5), rgba(64, 64, 64, 0.5))' }) .css({ background: '-webkit-linear-gradient(top, rgba(0, 0, 0, 0.5), rgba(64, 64, 64, 0.5))' }) .bindEvent({ position: function(data) { setPosition(data.position, 'timeline'); that.triggerEvent('position', { position: self.options.position }); } }); //Ox.print('??', $timeline.find('.OxInterface')) $timeline.children().css({ marginLeft: getTimelineLeft() + 'px' }); $timeline.find('.OxInterface').css({ marginLeft: getTimelineLeft() + 'px' }); return $timeline; } function getTimelineLeft() { var left = 0; Ox.forEach(self.options.controlsBottom, function(control) { if (control == 'timeline') { return false; } left += control == 'position' ? self.positionWidth : 16 }); return left; } function getTimelineWidth() { return (self.options.fullscreen ? window.innerWidth : self.options.width) - self.options.controlsBottom.reduce(function(prev, curr) { return prev + ( curr == 'timeline' || curr == 'space' ? 0 : curr == 'position' ? self.positionWidth : curr == 'resolution' ? 36 : 16 ); }, 0); } function getTitleWidth() { return (self.options.fullscreen ? window.innerWidth : self.options.width) - self.options.controlsTop.reduce(function(prev, curr) { return prev + ( curr == 'title' || curr == 'space' ? 0 : 16 ); }, 0); } function getVideoCSS() { var playerWidth = self.width, playerHeight = self.height, playerRatio = playerWidth / playerHeight, videoWidth = self.videoWidth, videoHeight = self.videoHeight, videoRatio = videoWidth / videoHeight, videoIsWider = videoRatio > playerRatio, width, height; if (self.options.scaleToFill) { width = videoIsWider ? playerHeight * videoRatio : playerWidth; height = videoIsWider ? playerHeight : playerWidth / videoRatio; } else { width = videoIsWider ? playerWidth : playerHeight * videoRatio; height = videoIsWider ? playerWidth / videoRatio : playerHeight; } width = Math.round(width); height = Math.round(height); return { left: parseInt((playerWidth - width) / 2), top: parseInt((playerHeight - height) / 2), width: width, height: height }; } function getVolumeImageURL() { var symbol; if (self.options.muted || self.options.volume == 0) { symbol = 'unmute'; } else if (self.options.volume < 1/3) { symbol = 'VolumeUp'; } else if (self.options.volume < 2/3) { symbol = 'VolumeDown'; } else { symbol = 'mute'; } return Ox.UI.getImageURL('symbol' + symbol, 'modern'); } function goToNextClip(direction) { self.$video[direction == 1 ? 'playNext' : 'playPrevious'](); } function goToNextResult(direction) { var found = false, position = 0; direction == -1 && self.results.reverse(); Ox.forEach(self.results, function(v) { if (direction == 1 ? v['in'] > self.options.position : v.out < self.options.position) { position = v['in']; found = true; return false; } }); direction == -1 && self.results.reverse(); if (!found) { position = self.results[direction == 1 ? 0 : self.results.length - 1]['in']; } setPosition(position + self.secondsPerFrame); } function goToPoint() { that.triggerEvent('gotopoint'); } function hideControlMenus() { ['find', 'volume', 'resolution'].forEach(function(element) { var $element = self['$' + element]; $element && $element.is(':visible') && $element.animate({ opacity: 0 }, 250, function() { $element.hide().css({opacity: 1}); }); }); //self.options.fullscreen && hideControls(); } function hideControls() { //Ox.print('hideControls'); clearTimeout(self.interfaceTimeout); self.interfaceTimeout = setTimeout(function() { if (!self.exitFullscreen && !self.inputHasFocus && !self.mouseIsInControls) { self.interfaceIsVisible = false; self.controlsTopAreVisible = false; self.controlsBottomAreVisible = false; self.$controlsTop && self.$controlsTop.animate({ opacity: 0 }, 250); self.$controlsBottom && self.$controlsBottom.animate({ opacity: 0 }, 250); hideControlMenus(); self.$logo && self.$logo.animate({ top: getCSS('logo').top, opacity: 0.25 }, 250, function() { self.options.logoLink && self.$logo.unbind('click'); self.options.logoTitle && self.$logo.unbind('mouseenter').unbind('mouseleave'); }); self.$subtitle && self.$subtitle.animate({ bottom: getCSS('subtitle').bottom, }, 250); } }, self.options.fullscreen ? 2500 : 1000); } function hideLoadingIcon() { self.$loadingIcon.hide().attr({ src: Ox.UI.getImageURL('symbolLoading') .replace('/classic/', '/modern/') }); } function hideMarkers() { Ox.forEach(self.$posterMarker, function(marker) { marker.hide(); }); Ox.forEach(self.$pointMarker, function(markers) { Ox.forEach(markers, function(marker) { marker.hide(); }); }); } function isEqual(a, b) { return Math.abs(a - b) < 0.001; } function loadImage() { self.$video .one({ load: function() { Ox.print('IMAGE LOADED', self.options.video(self.options.position, self.options.width)) hideLoadingIcon(); } }) .attr({ src: self.options.video(self.options.position, self.options.width) }); } function loadedmetadata() { Ox.print('LOADEDMETADATA') var hadDuration = !!self.options.duration; self.loadedMetadata = true; self.videoWidth = self.$video.videoWidth(); self.videoHeight = self.$video.videoHeight(); self.videoCSS = getVideoCSS(); self.posterMarkerCSS = getPosterMarkerCSS(); self.$video.css(self.videoCSS); self.$poster && self.$poster.css(self.videoCSS); self.$posterMarker && Ox.forEach(self.$posterMarker, function(marker, position) { marker.css(self.posterMarkerCSS[position]); }); self.out = self.options.playInToOut && self.out < self.$video.duration() ? self.out : self.$video.duration(); self.options.duration = self.out - self['in']; Ox.print('---------------------------------------- POS', self.options.position) //self.options.position = Ox.limit(self.options.position, self['in'], self.out); self.$video.currentTime(self.options.position); self.options.paused && self.options.showMarkers && setMarkers(); self.options.paused && self.playOnLoad && togglePaused('button'); self.$playButton && self.$playButton.options({ disabled: false }); hideLoadingIcon(); if (self.options.showIcon || self.options.showIconOnLoad) { //!self.options.keepIconVisible && self.$playIcon.addClass('OxInterface'); if (self.options.showIconOnLoad) { self.$playIcon.animate({ opacity: 1 }, 250); } } !hadDuration && self.$timeline && self.$timeline.replaceWith( self.$timeline = getTimeline() ); if (self.options.enableKeyboard && self.options.focus == 'load') { that.gainFocus(); } } function loadedsubtitles() { if (self.options.find) { submitFindInput(self.options.find); if (self.options.duration) { // duration was known or video has loaded before subtitles self.$timeline && self.$timeline.options({ results: self.results, subtitles: self.options.subtitles }); } } } function parsePositionInput(str) { var split = str.split(':').reverse(); while (split.length > 3) { split.pop(); } return split.reduce(function(prev, curr, i) { return prev + (parseFloat(curr) || 0) * Math.pow(60, i); }, 0); } function playing() { self.options.position = self.$video.currentTime(); if ( (self.options.playInToOut || self.playInToOut || self.isPlaylist) && self.options.position >= self.out ) { if (self.isPlaylist) { self.$video.playNext(); } else { togglePaused(); setPosition(self.out/*, 'video'*/); //ended(); self.playInToOut = false; } } else { setPosition(self.options.position, 'video'); } that.triggerEvent('position', { position: self.options.position }); } function playInToOut() { if (self.options.out > self.options['in']) { Ox.print('inToOut', self.options['in']) self.playInToOut = true; setPosition(self.options['in']); self.options.paused && togglePaused(); } } function pointschange() { var points = self.$video.points(); self['in'] = points[0]; self.out = points[1]; self.options.duration = self.out - self['in']; setPosition(self['in']); Ox.print('POINTSCHANGE', self['in'], self.out, self.options.position, self.options.duration) } function progress() { var buffered = self.$video.buffered(); for (var i = 0; i < buffered.length; i++) { self.buffered[i] = [buffered.start(i), buffered.end(i)]; // fixme: firefox weirdness if (self.buffered[i][0] > self.buffered[i][1]) { self.buffered[i][0] = 0; } } self.$progress.attr({ src: getProgressImageURL() }); } function seeked() { Ox.print('XX seeked') clearTimeout(self.seekTimeout); self.seekTimeout = 0; Ox.print('XX hide') hideLoadingIcon(); self.$playIcon && self.$playIcon.show(); } function seeking() { Ox.print('XX seeking') if (!self.seekTimeout) { self.seekTimeout = setTimeout(function() { self.$playIcon && self.$playIcon.hide(); Ox.print('XX show') showLoadingIcon(); }, 250); } } function setMarkers() { //Ox.print('SET MARKERS', self.options.position, self.options['in'], self.options.out, self.$pointMarker); Ox.forEach(self.$posterMarker, function(marker) { isEqual(self.options.position, self.options.posterFrame) ? marker.show() : marker.hide(); }); Ox.forEach(self.$pointMarker, function(markers, point) { Ox.forEach(markers, function(marker) { //Ox.print(self.options.position, self.options[point], isEqual(self.options.position, self.options[point])) // fixme: there's a bug in jquery and/or webkit // on load, show() doesn't work isEqual(self.options.position, self.options[point]) ? marker.css({display: 'block'}) : marker.hide(); }); }); } function setPoint() { that.triggerEvent('setpoint'); } function setPosition(position, from) { self.options.position = Ox.limit(position, self['in'], self.out); /* self.options.position = Math.round( position * self.options.fps ) / self.options.fps; */ self.options.paused && self.options.showMarkers && setMarkers(); self.$subtitle && setSubtitle(); self.$position && self.$position.html(formatPosition()); if (self.options.type == 'play') { if (self.loadedMetadata && from != 'video') { self.$video.currentTime(self.options.position); } if (self.iconIsVisible) { self.$playIcon.animate({ opacity: 0 }, 250); self.iconIsVisible = false; } if (self.posterIsVisible) { self.$poster.animate({ opacity: 0 }, 250); self.posterIsVisible = false; } self.$timeline /*&& from != 'timeline'*/ && self.$timeline.options({ position: self.options.position }); } else { //showLoadingIcon(); loadImage(); } } function setResolution() { if (!self.options.paused) { self.playOnLoad = true; togglePaused('button'); } self.loadedMetadata = false; showLoadingIcon(); self.$video.src(self.options.video[self.options.resolution]); self.$playButton && self.$playButton.options({ disabled: true }); } function setSize($element, css, animate, callback) { if ($element) { if (animate) { $element.animate(css, 250, function() { callback && callback(); }); } else { $element.css(css); callback && callback(); } } } function setSizes(callback) { var animate = !!callback; self.width = self.options.fullscreen ? window.innerWidth : self.options.width; self.height = self.options.fullscreen ? window.innerHeight : self.options.height; self.videoCSS = getVideoCSS(); self.iconSize = Math.max(Math.round(self.height / 10), 16); if (self.$timeline || self.$spaceBottom) { self.timelineWidth = getTimelineWidth(); if (self.$timeline) { self.timelineImageWidth = self.timelineWidth - self.barHeight; } } setSize(that, getCSS('player'), animate, callback); setSize(self.$videoContainer, getCSS('videoContainer'), animate); setSize(self.$video, self.videoCSS, animate); setSize(self.$poster, self.videoCSS, animate); setSize(self.$logo, getCSS('logo'), animate); setSize(self.$loadingIcon, getCSS('loadingIcon'), animate); setSize(self.$playIcon, getCSS('playIcon'), animate); setSize(self.$subtitle, getCSS('subtitle'), animate); setSize(self.$controlsTop, getCSS('controlsTop'), animate); setSize(self.$title, getCSS('title'), animate); setSize(self.$spaceTop, getCSS('spaceTop'), animate); setSize(self.$controlsBottom, getCSS('controlsBottom'), animate); setSize(self.$timeline, getCSS('timeline'), animate, function() { self.$timeline.options({ width: self.timelineWidth }); }); setSize(self.$spaceBottom, getCSS('spaceBottom'), animate); setSize(self.$find, getCSS('find'), animate, function() { var width = Math.min(128, self.width - 88); self.$findInput.options({ width: width }); self.$findInput.children('input').css({ width: (width - 6) + 'px', }); }); setSize(self.$volume, getCSS('volume'), animate, function() { self.$volumeInput.options({ size: Math.min(128, self.width - 56) }); }); if (self.$posterMarker) { self.posterMarkerCSS = getPosterMarkerCSS(); Ox.forEach(self.$posterMarker, function(marker, position) { setSize(marker, self.posterMarkerCSS[position], animate); }); } } function setSubtitle() { var subtitle = getSubtitle(); if (subtitle != self.subtitle) { self.subtitle = subtitle; setSubtitleText(); } } function setSubtitleText() { //Ox.print('setSubTx', self.subtitle, self.options.find) self.$subtitle.html( self.subtitle ? Ox.highlight(self.subtitle, self.options.find, 'OxHighlight') .replace(/\n/g, '
') : ' 
 ' // FIXME: weird bug, only in fullscreen, only in chrome ); //Ox.print('?!?', self.$subtitle.css('bottom'), self.$subtitle.height()) } function sizechange() { self.videoWidth = self.$video.videoWidth(); self.videoHeight = self.$video.videoHeight(); self.videoCSS = getVideoCSS(); self.$video.css(self.videoCSS); }; function changeVolumeBy(num) { showVolume(); self.options.volume = Ox.limit(self.options.volume + num, 0, 1); setVolume(self.options.volume); self.$volumeInput && self.$volumeInput.options({ value: self.options.volume }); } function setVolume(volume) { self.options.volume = volume; if (!!self.options.volume == self.options.muted) { self.options.muted = !self.options.muted; self.$video.muted(self.options.muted); self.$muteButton.toggleTitle(); } self.$video.volume(self.options.volume); self.$volumeButton.attr({ src: getVolumeImageURL() }); self.$volumeValue.html(self.options.muted ? 0 : Math.round(self.options.volume * 100)); that.triggerEvent('volume', { volume: self.options.volume }); } function showControls() { //Ox.print('showControls'); clearTimeout(self.interfaceTimeout); if (!self.interfaceIsVisible) { self.interfaceIsVisible = true; if (self.$controlsTop) { self.controlsTopAreVisible = true; } if (self.$controlsBottom) { self.controlsBottomAreVisible = true; } self.$controlsTop && self.$controlsTop.animate({ opacity: 1 }, 250); self.$controlsBottom && self.$controlsBottom.animate({ opacity: 1 }, 250); self.$find && self.$find.is(':visible') && self.$find.animate({ opacity: 1 }, 250); self.$volume && self.$volume.is(':visible') && self.$volume.animate({ opacity: 1 }, 250); self.$resolution && self.$resolution.is(':visible') && self.$resolution.animate({ opacity: 1 }, 250); self.$logo && self.$logo.animate({ top: getCSS('logo').top, opacity: 0.5 }, 250, function() { self.options.logoLink && self.$logo .bind({ click: function() { document.location.href = self.options.logoLink; } }); self.options.logoTitle && self.$logo .bind({ mouseenter: function(e) { self.$logoTooltip.show(e); }, mouseleave: self.$logoTooltip.hide }); }); self.$subtitle && self.$subtitle.animate({ bottom: getCSS('subtitle').bottom, }, 250); } } function showLoadingIcon() { self.$loadingIcon.attr({ src: Ox.UI.getImageURL('symbolLoadingAnimated') .replace('/classic/', '/modern/') }).show(); } function showVolume() { !self.interfaceIsVisible && showControls(); self.$volume && self.$volume.is(':hidden') && toggleVolume(); } function submitFindInput(value, hasPressedEnter) { //Ox.print('submitFindInput', value, hasPressedEnter) self.options.find = value; self.results = find(self.options.find); //Ox.print('results', self.results.length); if (self.$find) { self.$results.html(self.results.length); self.$previousResultButton.options({ disabled: self.results.length <= 1 }); self.$nextResultButton.options({ disabled: self.results.length <= 1 }); self.$clearButton.options({ disabled: !self.options.find }); } self.subtitle && setSubtitleText(); self.$timeline && self.$timeline.options({ find: self.options.find, results: self.results }); if (hasPressedEnter) { self.results.length ? goToNextResult(1) : self.$findInput.focusInput(); } } function submitPositionInput() { self.$positionInput.hide(); self.$position.html('').show(); //Ox.print('###', parsePositionInput(self.$positionInput.options('value'))) setPosition(parsePositionInput(self.$positionInput.options('value'))); if (self.playOnSubmit) { togglePaused(); self.$video.play(); self.playOnSubmit = false; } if (self.focus == 'mouseenter' && !self.mouseHasLeft) { that.gainFocus(); } self.mouseHasLeft && hideControls(); that.triggerEvent('position', { position: self.options.position }); } function toggleFind() { var show = self.$find.is(':hidden'); !show && self.$findInput.blurInput(); self.$find.toggle(); show && self.$findInput.focusInput(false); } function toggleFullscreen(from) { var parentOffset, playOnFullscreen; self.options.fullscreen = !self.options.fullscreen; if (!self.options.paused) { // video won't keep playing accross detach/append self.$video.pause(); playOnFullscreen = true; } if (self.options.fullscreen) { self.$parent = that.parent(); parentOffset = self.$parent.offset(); self.absoluteOffset = that.offset(); self.relativeOffset = { left: self.absoluteOffset.left - parentOffset.left, top: self.absoluteOffset.top - parentOffset.top }; that.detach() .css({ left: self.absoluteOffset.left + 'px', top: self.absoluteOffset.top + 'px', zIndex: 1000 }) .appendTo(Ox.UI.$body); setSizes(function() { playOnFullscreen && self.$video.play(); that.bind({ mousemove: function() { showControls(); hideControls(); } }); that.find('.OxControls').bind({ mouseenter: function() { self.mouseIsInControls = true; }, mouseleave: function() { self.mouseIsInControls = false; } }); showControls(); hideControls(); that.gainFocus(); }); } else { // flag makes the animation end on absolute position self.exitFullscreen = true; that.unbind('mousemove'); that.find('.OxControls') .trigger('mouseleave') .unbind('mouseenter') .unbind('mouseleave'); clearTimeout(self.interfaceTimeout); setSizes(function() { self.exitFullscreen = false; that.detach() .css({ left: self.relativeOffset.left + 'px', top: self.relativeOffset.top + 'px', zIndex: 1 }) .appendTo(self.$parent); playOnFullscreen && self.$video.play(); self.options.enableKeyboard && that.gainFocus(); //showControls(); }); } if (self.$fullscreenButton && from != 'button') { self.$fullscreenButton.toggleTitle(); } } function toggleMuted(from) { showVolume(); self.options.muted = !self.options.muted; self.$video.muted(self.options.muted); if (!self.options.muted && !self.options.volume) { self.options.volume = 1; self.$video.volume(1); } if (self.$muteButton && from != 'button') { self.$muteButton.toggleTitle(); } self.$volumeButton && self.$volumeButton.attr({ src: getVolumeImageURL() }); self.$volumeInput && self.$volumeInput.options({ value: self.options.muted ? 0 : self.options.volume }); self.$volumeValue && self.$volumeValue.html( self.options.muted ? 0 : Math.round(self.options.volume * 100) ); that.triggerEvent('muted', { muted: self.options.muted }); } function togglePaused(from) { self.options.paused = !self.options.paused; self.$timeline && self.$timeline.options({ paused: self.options.paused }); if (self.options.paused) { self.$video.pause(); clearInterval(self.playInterval); if (self.options.showIcon) { togglePlayIcon(); self.options.showIcon && self.$playIcon.animate({ opacity: 1 }, 250); } } else { if (self.options.playInToOut && self.options.position > self.options.out - self.secondsPerFrame) { setPosition(self.options['in']); } self.$video.play(); self.playInterval = setInterval(playing, self.millisecondsPerFrame); if (self.options.showIcon) { self.options.showIcon && self.$playIcon.animate({ opacity: 0 }, 250, togglePlayIcon); } self.options.showMarkers && hideMarkers(); } if (self.$playButton && from != 'button') { self.$playButton.toggleTitle(); } } function togglePlayIcon() { self.$playIcon.attr({ src: Ox.UI.getImageURL( 'symbol' + (self.options.paused ? 'Play' : 'Pause' ), 'modern') }); } function toggleScale(from) { self.options.scaleToFill = !self.options.scaleToFill; self.videoCSS = getVideoCSS(); self.$video.animate(self.videoCSS, 250); self.$poster && self.$poster.animate(self.videoCSS, 250); if (self.$scaleButton && from != 'button') { self.$scaleButton.toggleTitle(); } if (self.$posterMarker) { self.posterMarkerCSS = getPosterMarkerCSS(); Ox.forEach(self.$posterMarker, function(marker, position) { marker.animate(self.posterMarkerCSS[position], 250); }); } that.triggerEvent('scale', { scale: self.options.scaleToFill ? 'fill' : 'fit' }); } function toggleSize() { self.options.sizeIsLarge = !self.options.sizeIsLarge; that.triggerEvent('size', { size: self.options.sizeIsLarge ? 'large' : 'small' }); } function toggleVolume() { self.$volume.toggle(); } self.setOption = function(key, value) { if (key == 'fullscreen') { toggleFullscreen(); } else if (key == 'height' || key == 'width') { setSizes(); } else if (key == 'in' || key == 'out') { self.options.paused && setMarkers(); } else if (key == 'muted') { toggleMuted(); } else if (key == 'paused') { togglePaused(); } else if (key == 'position') { setPosition(value); } else if (key == 'posterFrame') { self.options.paused && setMarkers(); } else if (key == 'scaleToFill') { toggleScale(); } }; that.playInToOut = function() { playInToOut(); return that; }; that.togglePaused = function() { togglePaused(); return that; } that.toggleMuted = function() { toggleMuted(); return that; } return that; };