// vim: et:ts=4:sw=4:sts=4:ft=javascript 'use strict'; /*@ 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 censored Array of censored ranges 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 and drag changes position 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 Logo tooltip // fixme: shouldn't this be logoTooltip then?s 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 rewind If true, video will rewind when ended 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: [], brightness: 1, censored: [], controlsBottom: [], controlsTop: [], duration: 0, enableFind: false, enableFullscreen: false, enableKeyboard: false, enableMouse: false, enablePosition: false, enableTimeline: 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, rewind: false, 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.Log('VIDEO', '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; // fixme: ugly } 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: function() { var focused = Ox.Focus.focused(); if ( !focused || !Ox.UI.elements[focused].is('.OxInput') ) { 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.Log('Video', 'MOUSE HAS ENTERED') }, mouseleave: function() { hideControls(); self.mouseHasLeft = true; //Ox.Log('Video', '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( // autoplay seems to always play from the beginning, // and poster doesn't seem to work at all Ox.extend({ preload: self.options.preload, src: self.video, }, !self.options.paused && !self.options.playInToOut ? { /*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 = $('
') .appendTo(self.$videoContainer); self.$image = $('') .attr({ src: Ox.UI.PATH + 'png/transparent.png' }) .css({ position: 'absolute', width: '100%', height: '100%' }) .appendTo(self.$video) self.$brightness = $('
') .css({ position: 'absolute', width: '100%', height: '100%', background: 'rgb(0, 0, 0)', opacity: 1 - self.options.brightness }) .appendTo(self.$video); } /* ---------------------------------------------------------------------------- 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; } } if (self.options.censored.length) { self.$copyrightIcon = Ox.Element({ element: '', // fixme: make this configurable tooltip: 'This part of this video is not available in
your part of your country. Sorry for that.' }) .addClass('OxCopyrightIcon OxVideo') .attr({ src: Ox.UI.getImageURL('symbolNoCopyright', 'modern'), }) .hide() .appendTo(self.$videoContainer); } /* ---------------------------------------------------------------------------- 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 == 'close') { self.$closeButton = Ox.Button({ style: 'symbol', title: 'close', tooltip: 'Close', type: 'image' }) .bindEvent({ click: function() { that.triggerEvent('close'); } }) .appendTo(self['$controls' + titleCase]); } else 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.enablePosition) { 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('button'); } }) .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' && setPosition(self.options.position); 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 censor() { if (self.options.type == 'play') { self.$video .brightness(self.censored ? 0.05 : self.options.brightness) .volume(self.censored ? 0.01 : self.options.volume); } else { self.$brightness.css({ opacity: 1 - (self.censored ? 0.05 : self.options.brightness) }); } self.$copyrightIcon[self.censored ? 'show' : 'hide'](); } 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; } self.options.rewind && rewind(); that.triggerEvent('ended'); } 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(true); } }, 0); } function formatPosition(position) { position = Ox.isUndefined(position) ? self.options.position : position; return Ox.formatDuration(position, self.options.showMilliseconds); } function getCensored() { var censored = false; Ox.forEach(self.options.censored, function(v) { if ( v['in'] < self.options.position && v.out > self.options.position ) { censored = true; return false; } }); return censored; } function getCSS(element) { var css; if (element == 'copyrightIcon') { css = { width: self.iconSize + 'px', height: self.iconSize + 'px' }; } else if (element == 'controlsTop' || element == 'controlsBottom') { css = { width: self.width + 'px' }; } else if (element == 'find') { css = { width: Math.min(216, self.width) + 'px' // 128 + 4 * 16 + 24 }; } 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.Log('Video', e, e.layerX - 56) return Ox.limit( (e.layerX - 48 - self.barHeight / 2) / self.timelineImageWidth * self.$video.duration(), 0, self.$video.duration() ); } else { /*Ox.Log('Video', 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.Log('Video', '---', 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(), disabled: !self.options.enableTimeline, 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.Log('Video', '??', $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); } function goToNextClip(direction) { self.$video[direction == 1 ? 'playNext' : 'playPrevious'](); } function goToNextResult(direction) { var found = false, position = 0; if (self.results.length) { 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); that.triggerEvent('position', { position: self.options.position }); } } 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.Log('Video', '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.$image .one({ load: function() { hideLoadingIcon(); } }) .attr({ src: self.options.video( // fixme: this keeps the frame from being beyond the end, // but what should be avoided is setting position to a point // beyond the beginning of the last frame Math.min( self.options.position, Math.floor(self.options.duration * self.options.fps) / self.options.fps ), self.options.width ) }); } function loadedmetadata() { Ox.Log('Video', '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.Log('Video', '---------------------------------------- POS', self.options.position) //self.options.position = Ox.limit(self.options.position, self['in'], self.out); //self.$video.currentTime(self.options.position); setPosition(self.options.position); self.$video.muted(self.options.muted).volume(self.options.volume); if (!self.options.paused) { self.options.paused = true; togglePaused('button'); } else if (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() { var minute, previousMinute = parseInt(self.options.position / 60); self.options.position = self.$video.currentTime(); minute = parseInt(self.options.position / 60); if ( (self.playInToOut && self.options.position >= self.options.out) || (self.options.playInToOut && self.options.position >= self.out) ) { if (self.isPlaylist) { self.$video.playNext(); } else { togglePaused(); if (self.options.rewind) { rewind(); } else { setPosition(self.playInToOut ? self.options.out : self.out/*, 'video'*/); } self.playInToOut = false; //ended(); that.triggerEvent('ended'); } } else { setPosition(self.options.position, 'video'); } that.triggerEvent('playing', { position: self.options.position }); minute != previousMinute && that.triggerEvent('position', { position: minute * 60 }); } function playInToOut() { if (self.options.out > 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.Log('Video', '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 rewind() { setTimeout(function() { setPosition(self.options.playInToOut ? self.options['in'] : 0); }, 250); } function seeked() { Ox.Log('Video', 'seeked') clearTimeout(self.seekTimeout); self.seekTimeout = 0; Ox.Log('Video', 'hide loading icon') hideLoadingIcon(); self.$playIcon && self.$playIcon.show(); } function seeking() { Ox.Log('Video', 'XX seeking') if (!self.seekTimeout) { self.seekTimeout = setTimeout(function() { self.$playIcon && self.$playIcon.hide(); Ox.Log('Video', 'XX show') showLoadingIcon(); }, 250); } } function setMarkers() { //Ox.Log('Video', '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.Log('Video', 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.options.censored.length && setCensored(); 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 }); that.triggerEvent('resolution', { resolution: self.options.resolution }); } 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 = Ox.limit(Math.round(self.height / 10), 16, 32); 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.$copyrightIcon, getCSS('copyrightIcon'), 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); // 4 * 16 + 24 self.$findInput.options({ width: width }); self.$findInput.children('input').css({ width: (width - 12) + '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 setCensored() { var censored = getCensored(); if (censored != self.censored) { self.censored = censored; censor(); } } function setSubtitle() { var subtitle = getSubtitle(); if (subtitle != self.subtitle) { self.subtitle = subtitle; setSubtitleText(); } } function setSubtitleText() { //Ox.Log('Video', '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.Log('Video', '?!?', 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) { toggleMuted(); } else { self.$volumeButton.attr({ src: getVolumeImageURL() }); self.$volumeValue.html( self.options.muted ? 0 : Math.round(self.options.volume * 100) ); } !self.censored && self.$video.volume(self.options.volume); that.triggerEvent('volume', { volume: self.options.volume }); } function showControls() { //Ox.Log('Video', '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) { self.options.find = value; self.results = find(self.options.find); 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(true); } that.triggerEvent('find', {find: self.options.find}); } function submitPositionInput() { self.$positionInput.hide(); self.$position.html('').show(); //Ox.Log('Video', '###', 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() .addClass('OxFullscreen') .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() .removeClass('OxFullscreen') .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(); } that.triggerEvent('fullscreen', { fullscreen: self.options.fullscreen }); } 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) { if (!self.loadedMetadata) { return; } 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(); } that.triggerEvent('paused', { paused: self.options.paused }); self.options.paused && that.triggerEvent('position', { position: self.options.position }); } 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 == 'resolution') { setResolution(); } else if (key == 'scaleToFill') { toggleScale(); } }; /*@ playInToOut play in to out () -> play in to out @*/ that.playInToOut = function() { playInToOut(); return that; }; /*@ togglePaused toggle paused state () -> toggle paused state @*/ that.togglePaused = function() { togglePaused(); return that; } /*@ toggleMuted toggle muted state () -> toggle muted state @*/ that.toggleMuted = function() { toggleMuted(); return that; } return that; };