oxjs/source/Ox.UI/js/Video/Ox.VideoPlayer.js
2011-12-29 15:40:08 +05:30

2384 lines
83 KiB
JavaScript

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