// vim: et:ts=4:sw=4:sts=4:ft=javascript 'use strict'; /*@ Ox.VideoPlayer <f> Generic Video Player (options, self) -> <o> Video Player options <o> Options annotation <[o]> Array of annotation tracks name <s> Name of the annotation track data <[o]> Annotation data in <n> In point (sec) out <n> Out point (sec) text <s> Text censored <a|[]> Array of censored ranges controlsBottom <[s]|[]> Bottom controls, from left to right Can be 'close', fullscreen', 'scale', 'title', 'find', 'open', 'play', 'playInToOut', 'previous', 'next', 'mute', 'volume', 'size', 'timeline', 'space', 'position', 'settings'. The 'space' control is empty space that separates left-aligned from right-aligned controls. controlsTop <[s]|[]> Top controls, from left to right duration <n|-1> Duration (sec) enableFind <b|false> If true, enable find enableFullscreen <b|false> If true, enable fullscreen enableKeyboard <b|false> If true, enable keyboard controls enableMouse <b|false> If true, click toggles paused and drag changes position externalControls <b|false> If true, controls are outside the video find <s|''> Query string focus <s|'click'> focus on 'click', 'load' or 'mouseover' format <s|''> video format (like 'webm') fps <n|25> Frames per second fullscreen <b|false> If true, video is in fullscreen height <n|144> Height in px (excluding external controls) in <n> In point (sec) keepIconVisible <b|false> If true, play icon stays visible after mouseleave keepLargeTimelineVisible <b|false> If true, large timeline stays visible after mouseleave keepLogoVisible <b|false> If true, logo stays visible after mouseleave logo <s|''> Logo image URL logoLink <s|''> Logo link URL logoTitle <s|''> Text for Logo tooltip // fixme: shouldn't this be logoTooltip then?s muted <b|false> If true, video is muted paused <b|false> If true, video is paused playInToOut <b|false> If true, video plays only from in to out position <n|0> Initial position (sec) poster <s|''> Poster URL posterFrame <n|-1> Position of poster frame (sec) preload <s|'auto'> 'auto', 'metadata' or 'none' out <n> Out point (sec) resolution <n|0> resolution rewind <b|false> If true, video will rewind when ended scaleToFill <b|false> If true, scale to fill (otherwise, scale to fit) showControlsOnLoad <b|false> If true, show controls on load showFind <b|false> If true, show find input showHours <b|false> If true, don't show hours for videos shorter than one hour showIcon <b|false> If true, show play icon showIconOnLoad <b|false> If true, show icon on load showLargeTimeline <b|false> If true, show large timeline showMilliseconds <n|0> Number of decimals to show showMarkers <b|false> If true, show in/out/poster markers showProgress <|false> If true, show buffering progress sizeIsLarge <b|false> If true, initial state of the size control is large subtitles <s|[o]|[]> URL or SRT or array of subtitles in <n> In point (sec) out <n> Out point (sec) text <s> Text timeline <s> Timeline image URL title <s|''> Video title type <s|'play'> 'play', 'in' or 'out' video <s|[s]|o|''> Video URL String or array of strings ([part1, part2, ...]) or object ({resolution: url, ...} or {resolution: [part1, part2, ...], ...}) volume <n|1> Volume (0-1) width <n|256> Width in px @*/ Ox.VideoPlayer = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ annotations: [], brightness: 1, censored: [], controlsBottom: [], controlsTop: [], duration: 0, enableDownload: false, enableFind: false, enableFullscreen: false, enableKeyboard: false, enableMouse: false, enablePosition: false, enableSubtitles: 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; self.results = []; /* ---------------------------------------------------------------------------- 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 = $('<div>') .appendTo(self.$videoContainer); self.$image = $('<img>') .attr({ src: Ox.UI.PATH + 'png/transparent.png' }) .css({ position: 'absolute', width: '100%', height: '100%' }) .appendTo(self.$video) self.$brightness = $('<div>') .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 = $('<img>') .addClass('OxPoster') .attr({ src: self.options.poster }) .appendTo(self.$videoContainer); self.posterIsVisible = true; } /* ---------------------------------------------------------------------------- Logo ---------------------------------------------------------------------------- */ if (self.options.logo) { self.$logo = $('<img>') .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 = $('<img>') .addClass('OxLoadingIcon OxVideo') .attr({ src: Ox.UI.getImageURL('symbolLoadingAnimated', 'modern') }) .appendTo(self.$videoContainer); if (self.options.showIcon || self.options.showIconOnLoad) { self.$playIcon = $('<img>') .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: '<img>', // fixme: make this configurable tooltip: 'This part of this video is not available in<br/>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] = $('<div>') .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] = $('<img>') .addClass('OxPointMarker OxPointMarker' + titleCase) .attr({ src: Ox.UI.getImageURL('marker' + titleCase) }) .appendTo(self.$videoContainer); }); }); } /* ---------------------------------------------------------------------------- Subtitles ---------------------------------------------------------------------------- */ if (self.options.subtitles.length || true) { // FIXME self.$subtitle = $('<div>') .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', tooltip: ['Enter Fullscreen', 'Exit Fullscreen'], type: 'image', value: self.options.fullscreen ? 'shrink' : 'grow', values: ['grow', 'shrink'] }) .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', tooltip: ['Mute', 'Unmute'], type: 'image', value: self.options.muted ? 'unmute' : 'mute', values: ['mute', 'unmute'] }) .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 == 'open') { self.$openButton = Ox.Button({ style: 'symbol', title: 'arrowRight', tooltip: 'Open in ' + Ox.getObjectById( pandora.site.itemViews, pandora.user.ui.videoView ).title + ' View', type: 'image' }) .bindEvent({ click: function() { that.triggerEvent('open'); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'play') { self.$playButton = Ox.Button({ style: 'symbol', // FIXME: this is retarded, fix Ox.Button tooltip: ['Play', 'Pause'], type: 'image', value: self.options.paused ? 'play' : 'pause', values: ['play', 'pause'] }) .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 .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 == 'scale') { self.$scaleButton = Ox.Button({ style: 'symbol', tooltip: ['Scale to Fill', 'Scale to Fit'], type: 'image', value: self.options.scaleToFill ? 'fit' : 'fill', values: ['fill', 'fit'] }) .bindEvent('change', 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 == 'settings') { self.$settingsButton = Ox.Button({ style: 'symbol', title: 'set', tooltip: 'Settings', type: 'image' }) .bindEvent({ click: function() { self.$settings.toggle(); } }) .appendTo(self['$controls' + titleCase]); self.$settings = renderSettings().appendTo(that.$element); } else if (control == 'size') { self.$sizeButton = Ox.Button({ style: 'symbol', tooltip: ['Larger', 'Smaller'], type: 'image', value: self.options.sizeIsLarge ? 'shrink' : 'grow', values: ['grow', 'shrink'] }) .bindEvent('change', toggleSize) .appendTo(self['$controls' + titleCase]); } else if (control == 'space') { self['$space' + titleCase] = $('<div>') .html(' ') // fixme: ?? .appendTo(self['$controls' + titleCase].$element); } else if (control == 'timeline') { /* if (self.options.showProgress) { self.$progress = $('<img>') .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 = $('<div>') .addClass('OxTitle') .html(self.options.title) .appendTo(self['$controls' + titleCase].$element); } else if (control == 'volume') { self.$volumeButton = Ox.Button({ style: 'symbol', title: getVolumeImage(), tooltip: 'Volume', type: 'image' }) .bindEvent({ click: toggleVolume }) .appendTo(self['$controls' + titleCase]); } else if (control == 'zapHome') { self.$zapHomeButton = Ox.Button({ style: 'symbol', title: 'up', tooltip: 'Home Channel', type: 'image' }) .bindEvent({ click: function() { that.triggerEvent('zap', {direction: 0}); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'zapNext') { self.$zapNextButton = Ox.Button({ style: 'symbol', title: 'right', tooltip: 'Next Channel', type: 'image' }) .bindEvent({ click: function() { that.triggerEvent('zap', {direction: 1}); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'zapPrevious') { self.$zapPreviousButton = Ox.Button({ style: 'symbol', title: 'left', tooltip: 'Previous Channel', type: 'image' }) .bindEvent({ click: function() { that.triggerEvent('zap', {direction: -1}); } }) .appendTo(self['$controls' + titleCase]); } }); } }); /* ---------------------------------------------------------------------------- Find ---------------------------------------------------------------------------- */ if (self.options.enableFind) { self.$find = $('<div>') .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 = $('<div>') .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', tooltip: ['Mute', 'Unmute'], type: 'image', value: self.options.muted ? 'unmute' : 'mute', values: ['mute', 'unmute'] }) .bindEvent({ click: function() { toggleMuted('button'); } }) .appendTo(self.$volume); self.$volumeInput = Ox.Range({ changeOnDrag: true, 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 = $('<div>') .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(false, function() { self.options.fullscreen && enterFullscreen(); }); 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 enterFullscreen() { that.bind({ mousemove: function() { showControls(); hideControls(); } }); showControls(); hideControls(); that.find('.OxControls').bind({ mouseenter: function() { self.mouseIsInControls = true; }, mouseleave: function() { self.mouseIsInControls = false; } }); that.gainFocus(); } 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 = $('<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 = ''; self.options.enableSubtitles && 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, position: self.options.position, 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 : 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 getVolumeImage() { 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 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', 'settings', 'volume'].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 renderSettings() { var $settings = $('<div>') .addClass('OxControls OxSettings') .bind({ click: function(e) { var $target = $(e.target), resolution, title; self.$settings.hide(); if (!$target.is('.OxLine') && !$target.is('.OxSpace')) { title = $(e.target).parent().children()[0].innerHTML; if (title == 'Download') { that.triggerEvent('download'); } else if (title == 'Subtitles') { self.options.enableSubtitles = !self.options.enableSubtitles; setSubtitle(); that.triggerEvent('subtitles', { subtitles: self.options.enableSubtitles }); } else { resolution = parseInt(title); if (resolution != self.options.resolution) { self.options.resolution = resolution; setResolution(); } } self.$settings.children('.OxItem').each(function() { var children = $(this).children(), title = children[0].innerHTML, checked = ( title == 'Subtitles' && self.options.enableSubtitles ) || ( Ox.last(title) == 'p' && parseInt(title) == self.options.resolution ); $(children[1]).attr({ src: Ox.UI.getImageURL( 'symbol' + (checked ? 'Check' : 'None') ) }); }); } } }), items = Ox.merge( self.resolutions.map(function(resolution) { return { checked: resolution == self.options.resolution, title: resolution + 'p' } }), self.options.subtitles.length ? [{}, { checked: self.options.enableSubtitles, title: 'Subtitles' }] : [], self.options.enableDownload ? [{}, {title: 'Download'}] : [] ), height = 0; items.forEach(function(item) { var $item; if (item.title) { $item = $('<div>') .addClass('OxItem') .bind({ mouseenter: function() { $(this).addClass('OxSelected'); }, mouseleave: function() { $(this).removeClass('OxSelected'); } }) .appendTo($settings); $('<div>').html(item.title).appendTo($item); $('<img>').attr({ src: Ox.UI.getImageURL( 'symbol' + (item.checked ? 'Check' : 'None') ) }).appendTo($item); height += 16; } else { $('<div>').addClass('OxSpace').appendTo($settings); $('<div>').addClass('OxLine').appendTo($settings); $('<div>').addClass('OxSpace').appendTo($settings); height += 1 } }); $settings.css({height: height + 'px'}); return $settings; } 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.options.enableSubtitles && 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(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, '<br/>') : ' <br/> ' // 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.value(self.options.volume); } function setVolume(volume) { self.options.volume = volume; if (!!self.options.volume == self.options.muted) { toggleMuted(); } else { self.$volumeButton.options({ title: getVolumeImage() }); 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); ['find', 'settings', 'volume'].forEach(function(element) { var $element = self['$' + element]; $element && $element.is(':visible') && $element.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.value())) setPosition(parsePositionInput(self.$positionInput.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(true, function() { playOnFullscreen && self.$video.play(); enterFullscreen(); }); } else { // exitFullscreen 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(true, 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.toggle(); } 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.toggle(); } self.$volumeButton && self.$volumeButton.options({ title: getVolumeImage() }); self.$volumeInput && self.$volumeInput.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.toggle(); } 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.toggle(); } 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 == 'find') { setSubtitleText(); } else 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 <f> play in to out () -> <o> play in to out @*/ that.playInToOut = function() { playInToOut(); return that; }; /*@ togglePaused <f> toggle paused state () -> <o> toggle paused state @*/ that.togglePaused = function() { togglePaused(); return that; } /*@ toggleMuted <f> toggle muted state () -> <o> toggle muted state @*/ that.toggleMuted = function() { toggleMuted(); return that; } return that; };