'use strict'; /*@ Ox.VideoPlayer Generic Video Player options Options annotations <[]> Array of annotations id Optional id in In point (sec) out Out point (sec) text Text censored Array of censored ranges censoredIcon 'Censored' icon censoredTooltip Tooltip for 'censored' icon controlsBottom <[s]|[]> Bottom controls, from left to right Can be 'close', fullscreen', 'scale', 'title', 'find', 'open', 'play', 'playInToOut', 'previous', 'next', 'loop', 'mute', 'volume', 'size', 'timeline', 'position', 'settings', and 'space[int]'. A 'space16' control, for example, is empty space that is 16px wide, and a 'space' control is empty space that separates left-aligned from right-aligned controls. controlsTooltips Tooltip text per control id controlsTop <[s]|[]> Top controls, from left to right duration Duration (sec) enableDownload If true, enable download 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 enablePosition If true, enable position control enableSubtitles If true, enable subtitles externalControls If true, controls are outside the video enableTimeline If true, show timeline 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) invertHighlight If true, invert selection highlight on timeline 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 loop If true, video loops 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 id Optional id in In point (sec) out Out point (sec) text Text timeline Timeline image URL timelineType Current timeline type id timelineTypes <[o]|[]> Array of timeline type objects (id and title) 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 self shared private variable ([options[, self]]) -> Video Player censored censored close close download download ended ended find find fullscreen fullscreen gotopoint gotopoint loadedmetadata loadedmetadata muted muted open open paused paused playing playing position position resolution resolution scale scale select select setpoint setpoint size size subtitles subtitles timeline timeline volume volume zap zap @*/ Ox.VideoPlayer = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ annotations: [], brightness: 1, censored: [], censoredIcon: '', censoredTooltip: '', controlsBottom: [], controlsTooltips: {}, 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: '', loop: false, 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, sizeIsLarge: false, subtitles: [], timeline: '', timelineType: '', timelineTypes: [], title: '', type: 'play', video: '', volume: 1, width: 256 }) .options(options || {}) .update({ enableSubtitles: function() { self.options.enableSubtitles = !self.options.enableSubtitles; toggleSubtitles(); }, find: setSubtitleText, fullscreen: function() { self.options.fullscreen = !self.options.fullscreen; toggleFullscreen(); }, height: setSizes, 'in': function() { self.options.paused && setMarkers(); self.$timeline && self.$timeline.options('in', self.options['in']); }, out: function() { self.options.paused && setMarkers(); self.$timeline && self.$timeline.options('out', self.options.out); }, muted: function() { self.options.muted = !self.options.muted; toggleMuted(); }, paused: function() { self.options.paused = !self.options.paused; togglePaused(); }, position: function() { setPosition(self.options.position); }, posterFrame: function() { self.options.paused && setMarkers(); }, resolution: setResolution, scaleToFill: function() { self.options.scaleToFill = !self.options.scaleToFill; toggleScale(); }, sizeIsLarge: function() { self.$sizeButton.toggle(); }, timeline: function() { self.$timeline.options({imageURL: self.options.timeline}); }, video: function() { setVideo(); self.$video.options({ items: self.video }); }, volume: function() { setVolume(self.options.volume); }, width: setSizes }) .addClass('OxVideoPlayer'); Ox.Log('Video', 'VIDEO PLAYER OPTIONS', self.options) Ox.UI.$window.on({ resize: function() { self.options.fullscreen && setSizes(); } }); Ox.Fullscreen.bind('change', function() { //FIXME: is change fired before window size is updated to fullscreen? self.options.fullscreen && setTimeout(function() { setSizes(true); }, 250); }); if (Ox.isEmpty(self.options.annotations)) { self.options.annotations = self.options.subtitles; } setVideo(); 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.hasVolumeControl = self.options.controlsTop.indexOf('volume') > -1 || self.options.controlsBottom.indexOf('volume') > -1; 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() { changeVolume(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() { changeVolume(-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.on({ mouseenter: function() { if (!self.inputHasFocus) { that.gainFocus(); } }, mouseleave: function() { that.loseFocus(); } }); } else { that.on({ 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.on({ 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) if (self.options.type == 'play') { self.options.enableMouse && self.$videoContainer.bindEvent({ anyclick: function(e) { var $target = $(e.target); if (!$target.is('.OxLogo') && !$target.is('.OxCensoredIcon')) { 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, items: self.video, }, !self.options.paused && !self.options.playInToOut ? { /*autoplay: 'autoplay'*/ } : {}/*, self.options.poster ? { poster: self.options.poster } : {}*/) ) .bindEvent(Ox.extend({ durationchange: durationchange, ended: ended, loadedmetadata: loadedmetadata, itemchange: itemchange, 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.on({ 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 }) .hide() .one({ load: function() { self.$poster .css(getVideoCSS( self.$poster[0].width, self.$poster[0].height )) .show(); self.posterIsVisible = true; } }) .appendTo(self.$videoContainer); } /* ---------------------------------------------------------------------------- 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 = Ox.LoadingIcon({video: true}) .appendTo(self.$videoContainer) .start(); if (self.options.showIcon || self.options.showIconOnLoad) { self.$playIcon = $('') .addClass('OxPlayIcon OxVideo') .attr({ src: Ox.UI.getImageURL('symbol' + ( self.options.paused ? 'Play' : 'Pause' ), 'video') }) .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: '', tooltip: self.options.censoredTooltip }) .addClass('OxCensoredIcon OxVideo') .attr({ src: Ox.UI.getImageURL( 'symbol' + self.options.censoredIcon, 'video' ) }) .hide() .bindEvent({ singleclick: function() { that.triggerEvent('censored'); } }) .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); self.options['controls' + titleCase].forEach(function(control) { if (control == 'close') { self.$closeButton = Ox.Button({ style: 'video', title: 'close', tooltip: Ox._('Close'), type: 'image' }) .bindEvent({ click: function() { that.triggerEvent('close'); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'find') { self.$findButton = Ox.Button({ style: 'video', title: 'find', tooltip: Ox._('Find'), type: 'image' }) .bindEvent({ click: toggleFind }) .appendTo(self['$controls' + titleCase]); } else if (control == 'fullscreen') { self.$fullscreenButton = Ox.Button({ style: 'video', tooltip: [Ox._('Enter Fullscreen'), Ox._('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: 'video', title: 'goTo' + Ox.toTitleCase(self.options.type), tooltip: Ox._('Go to ' + Ox.toTitleCase(self.options.type) + ' Point'), type: 'image' }) .bindEvent({ click: goToPoint }) .appendTo(self['$controls' + titleCase]); } else if (control == 'loop') { self.$loopButton = Ox.Button({ style: 'video', tooltip: [Ox._('Don\'t Loop'), Ox._('Loop')], type: 'image', value: self.options.loop ? 'RepeatAll' : 'RepeatNone', values: ['RepeatAll', 'RepeatNone'] }) .bindEvent({ click: function() { toggleLoop('button'); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'mute') { self.$muteButton = Ox.Button({ style: 'video', tooltip: [Ox._('Mute'), Ox._('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: 'video', title: 'arrowRight', tooltip: Ox._('Next'), type: 'image' }) .bindEvent({ click: function() { goToNextClip(1); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'open') { self.$openButton = Ox.Button({ style: 'video', title: 'arrowRight', tooltip: self.options.controlsTooltips.open || '', type: 'image' }) .bindEvent({ click: function() { that.triggerEvent('open'); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'play') { self.$playButton = Ox.Button({ style: 'video', // FIXME: this is retarded, fix Ox.Button tooltip: [Ox._('Play'), Ox._('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: 'video', title: 'playInToOut', tooltip: Ox._('Play In to Out'), type: 'image' }) .bindEvent({ click: playInToOut }) .appendTo(self['$controls' + titleCase]); } else if (control == 'position') { self.positionWidth = getPositionWidth(); self.$position = Ox.Element({ tooltip: self.options.type == 'play' ? 'Position' : self.options.type == 'in' ? 'In Point' : 'Out Point' }) .addClass('OxPosition') .css({ width: self.positionWidth - 4 + 'px' }) .html(formatPosition()) .on({ 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(); }, submit: 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: 'video', title: 'arrowLeft', tooltip: Ox._('Previous'), type: 'image' }) .bindEvent({ click: function() { goToNextClip(-1); } }) .appendTo(self['$controls' + titleCase]); } else if (control == 'scale') { self.$scaleButton = Ox.Button({ style: 'video', tooltip: [Ox._('Scale to Fill'), Ox._('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: 'video', title: 'set' + Ox.toTitleCase(self.options.type), tooltip: Ox._('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: 'video', title: 'set', tooltip: Ox._('Settings'), type: 'image' }) .bindEvent({ click: function() { self.$settings.toggle(); } }) .appendTo(self['$controls' + titleCase]); self.$settings = renderSettings().appendTo(that); } else if (control == 'size') { self.$sizeButton = Ox.Button({ style: 'video', tooltip: [Ox._('Larger'), Ox._('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] = $('
') .html(' ') // fixme: ?? .appendTo(self['$controls' + titleCase].$element); } else if (Ox.startsWith(control, 'space')) { $('
') .css({ width: parseInt(control.substr(5)) + 'px', height: '16px' }) .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: 'video', title: getVolumeImage(), tooltip: Ox._('Volume'), type: 'image' }) .bindEvent({ click: toggleVolume }) .appendTo(self['$controls' + titleCase]); } else if (control == 'zapHome') { self.$zapHomeButton = Ox.Button({ style: 'video', title: 'up', tooltip: Ox._('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: 'video', title: 'right', tooltip: Ox._('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: 'video', title: 'left', tooltip: Ox._('Previous Channel'), type: 'image' }) .bindEvent({ click: function() { that.triggerEvent('zap', {direction: -1}); } }) .appendTo(self['$controls' + titleCase]); } }); } }); /* ---------------------------------------------------------------------------- Find ---------------------------------------------------------------------------- */ if (self.options.enableFind) { self.$find = $('
') .addClass('OxControls OxFind') .css({ top: self.options.controlsTop.length ? '16px' : 0 }) .appendTo(that); self.$results = Ox.Element({ tooltip: Ox._('Results') }) .addClass('OxResults') .html('0') .appendTo(self.$find); self.$previousResultButton = Ox.Button({ disabled: true, style: 'symbol', title: 'arrowLeft', tooltip: Ox._('Previous'), type: 'image' }) .bindEvent({ click: function() { goToNextResult(-1); } }) .appendTo(self.$find); self.$nextResultButton = Ox.Button({ disabled: true, style: 'symbol', title: 'arrowRight', tooltip: Ox._('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: Ox._('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: Ox._('Hide'), type: 'image' }) .bindEvent({ click: toggleFind }) .appendTo(self.$find); } /* ---------------------------------------------------------------------------- Volume ---------------------------------------------------------------------------- */ if (self.hasVolumeControl) { self.$volume = $('
') .addClass('OxControls OxVolume') .css({ bottom: self.options.controlsBottom.length ? '16px' : 0 }) .appendTo(that); self.$hideVolumeButton = Ox.Button({ style: 'symbol', title: 'close', tooltip: Ox._('Hide'), type: 'image' }) .bindEvent({ click: toggleVolume }) .appendTo(self.$volume); self.$muteButton = Ox.Button({ style: 'symbol', tooltip: [Ox._('Mute'), Ox._('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 = $('
') .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 changeVolume(num) { self.hasVolumeControl && 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 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 durationchange() { 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']; self.$timeline && self.$timeline.replaceWith( self.$timeline = getTimeline() ); setPosition(self.$video.currentTime()); that.triggerEvent('durationchange', { duration: self.options.duration }); } function ended() { if (self.options.loop) { rewind(); self.$video.play(); } else { !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 && setTimeout(rewind, 250); that.triggerEvent('ended'); } } function enterFullscreen() { that.on({ mousemove: function() { showControls(); hideControls(); } }); showControls(); hideControls(); that.find('.OxControls').on({ 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.filter(self.options.annotations, function(annotation) { return Ox.decodeHTMLEntities(Ox.stripTags( annotation.text.toLowerCase() )).indexOf(query) > -1; }).map(function(annotation) { return { id: annotation.id, 'in': annotation['in'], out: annotation.out }; }) results = Ox.filter(self.options.annotations, function(annotation) { return Ox.decodeHTMLEntities(Ox.stripTags( annotation.text.toLowerCase() )).indexOf(query) > -1; }).map(function(annotation) { return { id: annotation.id, 'in': annotation['in'], out: annotation.out }; }); } 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; // break } }); 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: (Math.floor(self.height / 16) + !!self.controlsBottomAreVisible * 16) + 'px', width: self.width + 'px', fontSize: Math.floor(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 getPositionWidth() { return 48 + !!self.options.showMilliseconds * 2 + self.options.showMilliseconds * 6; } 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 = ''; 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; // break } }); return subtitle; } function getTimeline() { var $timeline = Ox.SmallVideoTimeline({ //_offset: getTimelineLeft(), disabled: !self.options.enableTimeline, duration: self.options.duration, find: self.options.find, imageURL: self.options.timeline, 'in': self.options['in'], invertHighlight: self.options.invertHighlight, mode: 'player', out: self.options.out, paused: self.options.paused, position: self.options.position, results: self.results, showInToOut: self.options.playInToOut, showMilliseconds: self.options.showMilliseconds, subtitles: self.options.enableSubtitles ? self.options.subtitles : [], 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: '-o-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; // break } 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 : Ox.startsWith(curr, 'space') ? parseInt(curr.substr(5)) : curr == 'position' ? getPositionWidth() : 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 : Ox.startsWith(curr, 'space') ? parseInt(curr.substr(5)) : 16 ); }, 0); } function getVideoCSS(videoWidth, videoHeight) { // optional arguments allow for this function to be used for poster CSS var playerWidth = self.width, playerHeight = self.height, playerRatio = playerWidth / playerHeight, videoWidth = videoWidth || self.videoWidth, videoHeight = 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: Math.floor((playerWidth - width) / 2), top: Math.floor((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, result; 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 ) { result = v found = true; return false; // break } }); direction == -1 && self.results.reverse(); if (!found) { result = self.results[direction == 1 ? 0 : self.results.length - 1]; } setPosition(result['in'] + self.secondsPerFrame); that.triggerEvent('position', { position: self.options.position }); result.id && that.triggerEvent('select', result); } } 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.off('click'); self.options.logoTitle && self.$logo.off('mouseenter mouseleave'); }); self.$subtitle && self.$subtitle.animate({ bottom: getCSS('subtitle').bottom }, 250); } }, self.options.fullscreen ? 2500 : 1000); } function hideLoadingIcon() { self.$loadingIcon.hide().stop(); } function hideMarkers() { self.$posterMarker && Ox.forEach(self.$posterMarker, function(marker) { marker.hide(); }); self.$pointMarker && 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 isResolution(str) { return str.slice(0, -1).match(/^\d+$/) && str.slice(-1) == 'p'; } 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) Ox.Log('Video', '----------------------------------- DURATION', self.options.duration) //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(); } that.triggerEvent('loadedmetadata'); } 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 playing() { self.options.position = self.$video.currentTime(); if ( (self.playInToOut && self.options.position >= self.options.out) || (self.options.playInToOut && self.options.position >= self.out) ) { if (self.options.loop) { rewind(); self.$video.play(); } else { togglePaused(); if (self.options.rewind) { setTimeout(rewind, 250); } 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 }); } function playInToOut() { if (self.options.out > self.options['in']) { self.playInToOut = true; setPosition(self.options['in']); self.options.paused && togglePaused(); } } function itemchange(data) { var item = self.$video.options('items')[data.item]; Ox.Log('Video', 'ITEMCHANGE', item); } 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 = $('
') .addClass('OxControls OxSettings') .on({ click: function(e) { var $target = $(e.target), resolution, title, type; self.$settings.hide(); if (!$target.is('.OxLine') && !$target.is('.OxSpace')) { title = $(e.target).parent().children()[0].innerHTML; if (title == Ox._('Download')) { that.triggerEvent('download'); } else if (title == Ox._('Subtitles')) { toggleSubtitles(); } else if (isResolution(title)) { resolution = parseInt(title, 10); if (resolution != self.options.resolution) { self.options.resolution = resolution; setResolution(); } } else { type = self.options.timelineTypes[ Ox.indexOf(self.options.timelineTypes, function(type) { return type.title == title; }) ].id; if (type != self.options.timelineType) { self.options.timelineType = type; setTimelineType(); } } self.$settings.children('.OxItem').each(function() { var children = $(this).children(), title = children[0].innerHTML, checked = ( title == Ox._('Subtitles') && self.options.enableSubtitles ) || ( isResolution(title) && parseInt(title, 10) == self.options.resolution ) || ( self.options.timelineTypes.length && title == Ox.getObjectById( self.options.timelineTypes, self.options.timelineType ).title ); $(children[1]).attr({ src: Ox.UI.getImageURL( 'symbol' + (checked ? 'Check' : 'None') ) }); }); } } }), items = [{ disabled: true, title: Ox._('Resolution') }].concat( self.resolutions.map(function(resolution) { return { checked: resolution == self.options.resolution, title: resolution + 'p' }; }), self.options.subtitles.length ? [{}, { checked: self.options.enableSubtitles, title: Ox._('Subtitles') }] : [], self.options.timelineTypes.length ? [{}, { disabled: true, title: Ox._('Timeline') }] : [], self.options.timelineTypes.length ? self.options.timelineTypes.map(function(type) { return { checked: type.id == self.options.timelineType, title: type.title }; }) : [], self.options.enableDownload ? [{}, {title: Ox._('Download')}] : [] ), height = 0; items.forEach(function(item) { var $item; if (item.title) { $item = $('
') .addClass('OxItem' + (item.disabled ? ' OxDisabled' : '')) .appendTo($settings); if (!item.disabled) { $item.on({ mouseenter: function() { $(this).addClass('OxSelected'); }, mouseleave: function() { $(this).removeClass('OxSelected'); } }); } $('
').html(item.title).appendTo($item); $('').attr({ src: Ox.UI.getImageURL( 'symbol' + (item.checked ? 'Check' : 'None') ) }).appendTo($item); height += 16; } else { $('
').addClass('OxSpace').appendTo($settings); $('
').addClass('OxLine').appendTo($settings); $('
').addClass('OxSpace').appendTo($settings); height += 1 } }); $settings.css({height: height + 'px'}); return $settings; } function rewind() { setPosition(self.options.playInToOut ? self.options['in'] : 0); } 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 setCensored() { var censored = getCensored(); if (censored != self.censored) { self.censored = censored; censor(); } } function setMarkers() { //Ox.Log('Video', 'SET MARKERS', self.options.position, self.options['in'], self.options.out, self.$pointMarker); self.$posterMarker && Ox.forEach(self.$posterMarker, function(marker) { isEqual(self.options.position, self.options.posterFrame) ? marker.show() : marker.hide(); }); self.$pointMarker && 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); /* // disabled 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 = self.options.video[self.options.resolution]; self.$video.options({ items: self.video }); 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 && 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 setSubtitle() { var subtitle = getSubtitle(); if (subtitle != self.subtitle) { self.subtitle = subtitle; setSubtitleText(); } } function setSubtitleText() { self.$subtitle.html( self.subtitle ? Ox.highlight(self.subtitle, self.options.find, 'OxHighlight', true) .replace(/\n/g, '
') : ' 
 ' // FIXME: weird bug, only in fullscreen, only in chrome ); } function setTimelineType() { that.triggerEvent('timeline', {timeline: self.options.timelineType}); } function setVideo() { 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.video = self.options.video; } } function setVolume(volume) { self.options.volume = volume; if (!!self.options.volume == self.options.muted) { toggleMuted(); } else { self.$volumeButton && self.$volumeButton.options({ title: getVolumeImage() }); self.$volumeValue && 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 .on({ click: function() { document.location.href = self.options.logoLink; } }); self.options.logoTitle && self.$logo .on({ 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.start().show(); } function showVolume() { if (self.$volume) { !self.interfaceIsVisible && showControls(); self.$volume.is(':hidden') && toggleVolume(); } } function sizechange() { self.videoWidth = self.$video.videoWidth(); self.videoHeight = self.$video.videoHeight(); self.videoCSS = getVideoCSS(); self.$video.css(self.videoCSS); }; 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) { if (self.results.length) { goToNextResult(1); that.gainFocus(); } else { self.$findInput.focusInput(true); } } that.triggerEvent('find', {find: self.options.find}); } function submitPositionInput() { self.$positionInput.hide(); self.$position.html('').show(); setPosition(Ox.parseDuration(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.off('mousemove'); that.find('.OxControls') .trigger('mouseleave') .off('mouseenter 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 toggleLoop(from) { self.options.loop = !self.options.loop; if (self.$loopButton && from != 'button') { self.$loopButton.toggle(); } that.triggerEvent('loop', { loop: self.options.loop }); } function toggleMuted(from) { self.hasVolumeControl && 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) { self.options.paused = !self.options.paused; self.$timeline && self.$timeline.options({ paused: self.options.paused }); if (!self.loadedMetadata) { return; } 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' ), 'video') }); } 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 toggleSubtitles() { self.options.enableSubtitles = !self.options.enableSubtitles; setSubtitle(); self.$timeline && self.$timeline.options({ subtitles: self.options.enableSubtitles ? self.options.subtitles : [] }); that.triggerEvent('subtitles', { subtitles: self.options.enableSubtitles }); } function toggleVolume() { self.$volume.toggle(); } /*@ changeVolume change volume (num) -> change volume @*/ that.changeVolume = function(num) { changeVolume(num); return that; }; /*@ 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; };