oxjs/source/Ox.UI/js/Video/VideoPlayer.js

2486 lines
86 KiB
JavaScript

'use strict';
/*@
Ox.VideoPlayer <f> Generic Video Player
options <o> Options
annotations <[]> Array of annotations
id <s> Optional id
in <n> In point (sec)
out <n> Out point (sec)
text <s> Text
censored <a|[]> Array of censored ranges
censoredIcon <s|''> 'Censored' icon
censoredTooltip <s|''> Tooltip for 'censored' icon
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.
controlsTooltips <o|{}> Tooltip text per control id
controlsTop <[s]|[]> Top controls, from left to right
duration <n|-1> Duration (sec)
enableDownload <b|false> If true, enable download
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
enablePosition <b|false> If true, enable position control
enableSubtitles <b|false> If true, enable subtitles
externalControls <b|false> If true, controls are outside the video
enableTimeline <b|false> If true, show timeline
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)
invertHighlight <b|false> If true, invert selection highlight on timeline
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
id <s> Optional id
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
self <o> shared private variable
([options[, self]]) -> <o:Ox.Element> 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
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: '',
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: '',
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();
},
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;
}
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.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,
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.on({
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
})
.one({
load: function() {
self.$poster.css(getVideoCSS(
self.$poster[0].width,
self.$poster[0].height
)).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 = Ox.LoadingIcon({video: true})
.appendTo(self.$videoContainer)
.start();
if (self.options.showIcon || self.options.showIconOnLoad) {
self.$playIcon = $('<img>')
.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: '<img>',
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] = $('<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);
self.options['controls' + titleCase].forEach(function(control) {
if (control == 'close') {
self.$closeButton = Ox.Button({
style: 'video',
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: 'video',
title: 'find',
tooltip: 'Find',
type: 'image'
})
.bindEvent({
click: toggleFind
})
.appendTo(self['$controls' + titleCase]);
} else if (control == 'fullscreen') {
self.$fullscreenButton = Ox.Button({
style: 'video',
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: 'video',
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: 'video',
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: 'video',
title: 'arrowRight',
tooltip: '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: ['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: 'video',
title: 'playInToOut',
tooltip: '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: 'Previous',
type: 'image'
})
.bindEvent({
click: function() {
goToNextClip(-1);
}
})
.appendTo(self['$controls' + titleCase]);
} else if (control == 'scale') {
self.$scaleButton = Ox.Button({
style: 'video',
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: 'video',
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: 'video',
title: 'set',
tooltip: '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: ['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('&nbsp;') // 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('&nbsp;');
}
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: 'video',
title: getVolumeImage(),
tooltip: 'Volume',
type: 'image'
})
.bindEvent({
click: toggleVolume
})
.appendTo(self['$controls' + titleCase]);
} else if (control == 'zapHome') {
self.$zapHomeButton = Ox.Button({
style: 'video',
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: 'video',
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: 'video',
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);
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.hasVolumeControl) {
self.$volume = $('<div>')
.addClass('OxControls OxVolume')
.css({
bottom: self.options.controlsBottom.length ? '16px' : 0
})
.appendTo(that);
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 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 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.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 = $('<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 :
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 : 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 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();
}
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.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
});
}
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')
.on({
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') {
toggleSubtitles();
} else {
resolution = parseInt(title, 10);
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, 10) == self.options.resolution
);
$(children[1]).attr({
src: Ox.UI.getImageURL(
'symbol' + (checked ? 'Check' : 'None')
)
});
});
}
}
}),
items = [].concat(
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')
.on({
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 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.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 && 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, '<br/>')
: '&nbsp;<br/>&nbsp;'
// FIXME: weird bug, only in fullscreen, only in chrome
);
}
function sizechange() {
self.videoWidth = self.$video.videoWidth();
self.videoHeight = self.$video.videoHeight();
self.videoCSS = getVideoCSS();
self.$video.css(self.videoCSS);
};
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 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 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 <f> change volume
(num) -> <o> change volume
@*/
that.changeVolume = function(num) {
changeVolume(num);
return that;
};
/*@
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;
};