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

1420 lines
No EOL
49 KiB
JavaScript

/*@
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
controls <[s]> Controls, from left to right
Can be 'play', 'playInToOut', 'mute', 'volume', 'size', 'scale',
'timeline', 'space', 'position', 'settings'. The 'space' control
is just empty space that separates left-aligned from right-aligned
controls
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
externalControls <b|false> If true, controls are outside the video
find <s|''> Query string
focus <s|'click'> focus on 'click', 'load' or 'mouseover'
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 tooltip
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|-1> Out point (sec)
scaleToFill <b|false> If true, scale to fill (otherwise, scale to fit)
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
showInterfaceOnLoad <b|false> If true, show controls and title on load
showLargeTimeline <b|false> If true, show large timeline
showMilliseconds <n|0> Number of decimals to show
showPointMarkers <b|false> If true, show in/out 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|''> Video URL
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: [],
controls: [],
duration: 0,
enableFind: false,
enableFullscreen: false,
enableKeyboard: false,
externalControls: false,
find: '',
focus: 'click',
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: '',
preload: 'auto',
out: 0,
scaleToFill: false,
showFind: false,
showHours: false,
showIcon: false,
showIconOnLoad: false,
showInterfaceOnLoad: false,
showLargeTimeline: false,
showMilliseconds: 0,
showPointMarkers: false,
showProgress: false,
subtitles: [],
timeline: '',
title: '',
type: 'play',
video: '',
volume: 1,
width: 256
})
.options(options || {})
.addClass('OxVideo')
.css({
position: 'absolute'
});
Ox.UI.$window.bind({
resize: function() {
self.options.fullscreen && setSizes();
}
});
self['in'] = self.options.playInToOut ? self.options['in'] : 0,
self.out = self.options.playInToOut ? self.options.out : self.options.duration;
self.options.duration = self.out - self['in'];
self.options.position = Ox.limit(self.options.position, self['in'], self.out);
Ox.print('p/d', self.options.position, self.options.duration);
// fixme: this is _relative_, resizing can happen
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;
if (self.options.enableKeyboard) {
that.bindEvent({
key_1: function() {
toggleScale();
},
key_f: function() {
// need timeout so the "f" doesn't appear in the input field
setTimeout(self.$findInput.focusInput, 0);
},
key_g: function() {
goToNextResult(1);
},
key_left: function() {
setPosition(self.options.position - self.secondsPerFrame, true);
},
key_p: function() {
playInToOut();
},
key_right: function() {
setPosition(self.options.position + self.secondsPerFrame, true);
},
key_shift_f: function() {
self.options.enableFullscreen && toggleFullscreen(true);
},
key_shift_g: function() {
goToNextResult(-1);
},
key_space: function() {
togglePaused(true);
}
})
}
if (self.options.enableKeyboard) {
if (self.options.focus == 'mouseenter') {
that.bind({
mouseenter: function() {
if (!self.inputHasFocus) {
that.gainFocus();
}
},
mouseleave: function() {
that.loseFocus();
}
});
} else {
that.bind({
click: that.gainFocus
});
}
}
if (
(!self.options.externalControls && self.options.controls.length) ||
self.options.showIcon || self.options.title
) {
that.bind({
mouseenter: function() {
showInterface();
self.mouseHasLeft = false;
Ox.print('MOUSE HAS ENTERED')
},
mouseleave: function() {
hideInterface();
self.mouseHasLeft = true;
Ox.print('MOUSE HAS LEFT')
}
});
}
if (Ox.isString(self.options.subtitles)) {
if (self.options.subtitles.indexOf('\n') > -1) {
self.options.subtitles = Ox.parseSRT(self.options.subtitles);
} else {
Ox.get(self.options.subtitles, function(data) {
self.options.subtitles = Ox.parseSRT(data);
self.results = find(self.options.find);
Ox.print('--setting results--', self.$timeline)
if (self.options.duration) {
self.$timeline && self.$timeline.options({
results: self.results,
subtitles: self.options.subtitles
});
} else {
}
});
self.options.subtitles = [];
}
}
self.results = find(self.options.find);
self.buffered = [];
self.controlsTimeout;
self.$videoContainer = $('<div>')
.css({
position: 'absolute',
background: 'rgb(0, 0, 0)',
overflow: 'hidden'
})
.bind({
click: function() {
togglePaused(true);
}
})
.appendTo(that.$element)
self.$video = $('<video>')
.attr(Ox.extend({
preload: self.options.preload,
src: self.options.video
}, !self.options.paused ? {
autoplay: 'autoplay'
} : {}/*, self.options.poster ? {
poster: self.options.poster
} : {}*/))
.css({
position: 'absolute'
})
.bind(Ox.extend({
ended: ended,
loadedmetadata: loadedmetadata,
seeked: seeked,
seeking: seeking
}, self.options.progress ? {
progress: progress
} : {}))
.appendTo(self.$videoContainer);
self.video = self.$video[0];
if (self.options.poster) {
self.$poster = $('<img>')
.attr({
src: self.options.poster
})
.css({
position: 'absolute'
})
.appendTo(self.$videoContainer);
self.posterIsVisible = true;
}
if (self.options.logo) {
self.$logo = $('<img>')
.attr({
src: self.options.logo
})
.css({
position: 'absolute',
cursor: self.options.logoLink ? 'pointer' : 'default',
opacity: 0.25,
})
.appendTo(self.$videoContainer);
if (self.options.logoTitle) {
self.$logoTooltip = Ox.Tooltip({
title: self.options.logoTitle
});
}
}
self.$loadingIcon = $('<img>')
.attr({
src: Ox.UI.getImagePath('symbolLoadingAnimated.svg')
.replace('/classic/', '/modern/')
})
.css({
position: 'absolute'
})
.appendTo(self.$videoContainer);
if (self.options.showIcon || self.options.showIconOnLoad) {
self.$playIcon = $('<img>')
.attr({
src: Ox.UI.getImagePath(
'symbol' + (self.options.paused ? 'Play' : 'Pause') + '.svg'
).replace('/classic/', '/modern/')
})
.css({
position: 'absolute',
border: '2px solid rgb(255, 255, 255)',
background: 'rgba(0, 0, 0, 0.5)',
opacity: 0
})
.appendTo(self.$videoContainer);
if (self.options.showIcon) {
self.$playIcon.addClass('OxInterface');
}
if (self.options.showIconOnLoad) {
self.iconIsVisible = true;
}
}
if (self.options.subtitles.length || true) { // fixme
self.$subtitle = $('<div>')
//.addClass('OxSubtitle')
.css({
position: 'absolute',
left: 0,
right: 0,
textAlign: 'center',
textShadow: 'rgba(0, 0, 0, 1) 0 0 4px',
color: 'rgb(255, 255, 255)'
})
.appendTo(self.$videoContainer);
}
if (self.options.title) {
self.$title = $('<div>')
.addClass('OxInterface')
.css({
position: 'absolute',
height: (self.barHeight - 1) + 'px',
paddingTop: '1px',
textAlign: 'center',
color: 'rgb(255, 255, 255)',
opacity: 0
})
.css({
backgroundImage: '-moz-linear-gradient(top, rgba(64, 64, 64, 0.5), rgba(0, 0, 0, 0.5))'
})
.css({
backgroundImage: '-webkit-linear-gradient(top, rgba(64, 64, 64, 0.5), rgba(0, 0, 0, 0.5))'
})
.html(self.options.title)
.appendTo(that.$element);
}
if (self.options.enableFind) {
self.$find = $('<div>')
.addClass('OxInterface')
.css({
position: 'absolute',
right: '16px',
top: (self.options.title ? 32 : 16) + 'px',
width: '128px',
borderRadius: '8px',
opacity: 0
})
.css({
backgroundImage: '-moz-linear-gradient(top, rgba(64, 64, 64, 0.5), rgba(0, 0, 0, 0.5))'
})
.css({
backgroundImage: '-webkit-linear-gradient(top, rgba(64, 64, 64, 0.5), rgba(0, 0, 0, 0.5))'
})
.appendTo(that.$element);
self.$previousButton = Ox.Button({
style: 'symbol',
title: 'arrowLeft',
tooltip: 'Previous [Shift+G]',
type: 'image'
})
.css({float: 'left', opacity: 0.25})
.bindEvent({
click: function() {
goToNextResult(-1);
}
})
.appendTo(self.$find);
self.$nextButton = Ox.Button({
style: 'symbol',
title: 'arrowRight',
tooltip: 'Next [G]',
type: 'image'
})
.css({float: 'left', opacity: 0.25})
.bindEvent({
click: function() {
goToNextResult(1);
}
})
.appendTo(self.$find);
self.$findInput = Ox.Input({
placeholder: 'Find',
value: self.options.find,
width: 86
})
.css({
float: 'left',
background: 'rgba(0, 0, 0, 0)',
MozBoxShadow: '0 0 0',
WebkitBoxShadow: '0 0 0'
})
.bindEvent({
focus: function() {
self.inputHasFocus = true;
},
blur: function() {
self.inputHasFocus = false;
submitFindInput();
}
})
.appendTo(self.$find);
self.$findInput.children('input').css({
width: (self.positionWidth - 6) + 'px',
height: '16px',
padding: '0 3px 0 3px',
border: '0px',
borderRadius: '8px',
fontSize: '11px',
color: 'rgb(255, 255, 255)'
})
.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))'
});
self.$clearButton = Ox.Button({
style: 'symbol',
title: 'close',
tooltip: 'Clear',
type: 'image'
})
.css({float: 'left'})
.bindEvent({
click: function() {
self.$findInput
.options({value: ''})
.focusInput();
self.results = [];
self.$timeline && self.$timeline.options({
results: self.results
});
}
})
.appendTo(self.$find);
}
if (self.options.controls.length) {
self.$controls = Ox.Bar({
size: self.barHeight
})
.addClass('OxInterface')
.css({
position: 'absolute',
bottom: 0,
opacity: self.options.externalControls ? 1 : 0
})
.appendTo(that.$element);
if (!self.options.externalControls) {
self.$controls.css({
backgroundImage: '-moz-linear-gradient(top, rgba(64, 64, 64, 0.5), rgba(0, 0, 0, 0.5))'
})
.css({
backgroundImage: '-webkit-linear-gradient(top, rgba(64, 64, 64, 0.5), rgba(0, 0, 0, 0.5))'
});
}
self.$control = {};
self.options.controls.forEach(function(control) {
if (control == 'play') {
self.$playButton = Ox.Button({
style: 'symbol',
// FIXME: this is retarded, fix Ox.Button
title: [
{id: 'play', title: 'play', selected: self.options.paused},
{id: 'pause', title: 'pause', selected: !self.options.paused}
],
tooltip: ['Play', 'Pause'],
type: 'image'
})
.css({float: 'left'})
.bindEvent('click', function() {
togglePaused();
})
.appendTo(self.$controls);
} else if (control == 'playInToOut') {
self.$playInToOutButton = Ox.Button({
style: 'symbol',
title: 'playInToOut',
tooltip: 'Play In to Out',
type: 'image'
})
.css({float: 'left'})
.bindEvent('click', playInToOut)
.appendTo(self.$controls);
} else if (control == 'mute') {
self.$muteButton = Ox.Button({
style: 'symbol',
title: [
{id: 'mute', title: 'mute', selected: !self.options.muted},
{id: 'unmute', title: 'unmute', selected: self.options.muted}
],
tooltip: ['Mute', 'Unmute'],
type: 'image'
})
.css({float: 'left'})
.bindEvent('click', function() {
toggleMuted();
})
.appendTo(self.$controls);
} else if (control == 'volume') {
} else if (control == 'scale') {
self.$scaleButton = Ox.Button({
style: 'symbol',
title: [
{id: 'fill', title: 'fill', selected: !self.options.scaleToFill},
{id: 'fit', title: 'fit', selected: self.options.scaleToFill}
],
tooltip: ['Scale to Fill', 'Scale to Fit'],
type: 'image'
})
.css({float: 'left'})
.bindEvent('click', function() {
toggleScale();
})
.appendTo(self.$controls);
} else if (control == 'fullscreen') {
self.$fullscreenButton = Ox.Button({
style: 'symbol',
title: [
{id: 'grow', title: 'grow', selected: !self.options.fullscreen},
{id: 'shrink', title: 'shrink', selected: self.options.fullscreen}
],
tooltip: ['Enter Fullscreen', 'Exit Fullscreen'],
type: 'image'
})
.css({float: 'left'})
.bindEvent('click', function() {
toggleFullscreen();
})
.appendTo(self.$controls);
} else if (control == 'size') {
self.$sizeButton = Ox.Button({
style: 'symbol',
title: [
{id: 'grow', title: 'grow', selected: !self.options.sizeIsLarge},
{id: 'shrink', title: 'shrink', selected: self.options.sizeIsLarge}
],
tooltip: ['Larger', 'Smaller'],
type: 'image'
})
.css({float: 'left'})
.bindEvent('click', toggleSize)
.appendTo(self.$controls);
} 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()
.css({
float: 'left'
})
.html('&nbsp;');
}
self.$timeline.appendTo(self.$controls);
} else if (control == 'space') {
self.$space = $('<div>')
.css({float: 'left'})
.html('&nbsp;') // fixme: ??
.appendTo(self.$controls.$element);
} else if (control == 'position') {
self.positionWidth = 48 +
!!self.options.showMilliseconds * 2
+ self.options.showMilliseconds * 6;
self.$position = $('<div>')
.addClass('foo')
.css({
float: 'left',
width: (self.positionWidth - 4) + 'px',
height: '12px',
padding: '2px',
fontSize: '9px',
textAlign: 'center',
color: 'rgb(255, 255, 255)'
})
.html(formatPosition)
.bind({
click: function() {
if (!self.options.paused) {
self.wasPlaying = true;
togglePaused(true);
}
self.$position.hide();
self.$positionInput
.options({
value: formatPosition()
})
.show()
.focusInput(false);
}
})
.appendTo(self.$controls.$element);
self.$positionInput = Ox.Input({
value: formatPosition(),
width: self.positionWidth
})
.css({
float: 'left',
background: 'rgba(0, 0, 0, 0)',
MozBoxShadow: '0 0 0',
WebkitBoxShadow: '0 0 0'
})
.bindEvent({
focus: function() {
self.inputHasFocus = true;
},
blur: function() {
self.inputHasFocus = false;
submitPositionInput();
}
})
.hide()
.appendTo(self.$controls.$element);
self.$positionInput.children('input').css({
width: (self.positionWidth - 6) + 'px',
height: '16px',
padding: '0 3px 0 3px',
border: '0px',
borderRadius: '8px',
fontSize: '9px',
color: 'rgb(255, 255, 255)'
})
.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))'
});
}
});
}
setSizes();
function clearInterfaceTimeout() {
clearTimeout(self.interfaceTimeout);
self.interfaceTimeout = 0;
}
function ended() {
if (!self.options.paused) {
togglePaused(true);
}
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;
}
}
function find(query) {
query = query.toLowerCase();
return query.length ? Ox.map(self.options.subtitles, function(subtitle) {
return subtitle.text.toLowerCase().indexOf(query) > -1 ? {
'in': subtitle['in'],
out: subtitle.out
} : null;
}) : [];
}
function formatPosition(position) {
position = Ox.isUndefined(position) ? self.options.position : position;
return Ox.formatDuration(position, self.options.showMilliseconds);
}
function getCSS(element) {
var css;
if (element == 'controls' || element == 'title') {
css = {
width: self.width + 'px'
};
} else if (element == 'loadingIcon') {
css = {
left: self.iconLeft + 'px',
top: self.iconTop + 'px',
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.titleIsVisible * 16) + 'px',
height: logoHeight + 'px',
};
} else if (element == 'player') {
css = Ox.extend({
width: self.width + 'px',
height: (self.options.fullscreen
? window.innerHeight
: self.height + (
self.options.externalControls
? (!!self.options.controls.length + !!self.options.title) * self.barHeight
: 0)) + '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 = {
left: self.iconLeft + 'px',
top: self.iconTop + 'px',
width: playIconSize + 'px',
height: playIconSize + 'px',
padding: playIconPadding + 'px',
borderRadius: Math.round(self.iconSize / 2) + 'px'
};
} else if (element == 'poster' || element == 'video') {
var playerWidth = self.width,
playerHeight = self.height,
playerRatio = playerWidth / playerHeight,
videoWidth = self.video.videoWidth,
videoHeight = self.video.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);
css = {
width: width + 'px',
height: height + 'px',
marginLeft: parseInt((playerWidth - width) / 2),
marginTop: parseInt((playerHeight - height) / 2)
};
} else if (element == 'progress') {
css = {
width: self.timelineImageWidth + 'px',
marginLeft: -self.timelineImageWidth + 'px'
};
} else if (element == 'subtitle') {
css = {
bottom: (parseInt(self.height / 16) + !!self.controlsAreVisible * 16) + 'px',
width: self.width + 'px',
fontSize: parseInt(self.height / 20) + 'px',
WebkitTextStroke: (self.height / 1000) + 'px rgb(0, 0, 0)'
};
} else if (element == 'space' || element == 'timeline') {
css = {
width: self.timelineWidth + 'px'
};
} else if (element == 'videoContainer') {
css = {
width: self.width + 'px',
height: self.height + 'px'
};
}
return css;
}
function getPosition(e) {
// fixme: no offsetX in firefox???
if ($.browser.mozilla) {
//Ox.print(e, e.layerX - 56)
return Ox.limit(
(e.layerX - 48 - self.barHeight / 2) / self.timelineImageWidth * self.video.duration,
0, self.video.duration
);
} else {
/*Ox.print(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 getProgressImageURL() {
Ox.print('---', 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 = '';
Ox.forEach(self.options.subtitles, function(v) {
if (
v['in'] <= self.options.position &&
v.out > self.options.position
) {
subtitle = Ox.highlight(v.text, self.options.find, 'OxHighlight');
return false;
}
});
return subtitle;
}
function getTimeline() {
var $timeline = Ox.SmallVideoTimeline({
_offset: getTimelineLeft(),
duration: self.options.duration,
getTimelineURL: Ox.isString(self.options.timeline) ?
function() { return self.options.timeline; } :
self.options.timeline,
'in': self.options['in'],
out: self.options.out,
paused: self.options.paused,
results: self.results,
showMilliseconds: self.options.showMilliseconds,
subtitles: self.options.subtitles,
type: 'player',
width: getTimelineWidth()
})
.css({
float: 'left'
})
.bindEvent({
position: function(data) {
setPosition(data.position, true);
}
});
//Ox.print('??', $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.controls, 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.controls.reduce(function(prev, curr) {
return prev + (
curr == 'timeline' || curr == 'space' ? 0 :
curr == 'position' ? self.positionWidth : 16
);
}, 0);
}
function hideInterface() {
Ox.print('hideInterface');
self.interfaceTimeout = setTimeout(function() {
if (!self.exitFullscreen && !self.inputHasFocus && !self.mouseIsInControls) {
self.titleIsVisible = false;
self.controlsAreVisible = false;
that.find('.OxInterface').animate({
opacity: 0
}, 250);
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.getImagePath('symbolLoading.svg')
.replace('/classic/', '/modern/')
});
}
function loadedmetadata() {
var hadDuration = !!self.options.duration;
self.loaded = true;
self.out = self.options.playInToOut &&
self.options.out < self.video.duration ?
self.options.out : self.video.duration;
self.options.duration = self.out - self['in'];
self.video.currentTime = self.options.position;
self.$video.css(getCSS('video'));
self.$poster && self.$poster.css(getCSS('poster'));
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);
}
}
if (self.$timeline) {
if (!hadDuration) {
self.$timeline.replaceWith(self.$timeline = getTimeline());
}
self.$timeline.options({
duration: self.options.duration
});
}
if (self.options.enableKeyboard && self.options.focus == 'load') {
that.gainFocus();
}
}
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() {
self.options.position = self.video.currentTime;
if (
(self.options.playInToOut || self.playInToOut) &&
self.options.position >= self.options.out
) {
togglePaused(true);
setPosition(self.options.out/*, true*/);
//ended();
self.playInToOut = false;
} else {
setPosition(self.options.position);
}
that.triggerEvent('position', {
position: self.options.position
});
}
function playInToOut() {
self.playInToOut = true;
setPosition(self.options['in'], true);
if (self.options.paused) {
togglePaused(true);
}
}
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 seeked() {
clearTimeout(self.seekTimeout);
hideLoadingIcon();
self.$playIcon && self.$playIcon.show();
}
function seeking() {
self.seekTimeout = setTimeout(function() {
self.$playIcon && self.$playIcon.hide();
showLoadingIcon();
}, 250);
}
function setPosition(position, setVideo) {
position = Ox.limit(position, self['in'], self['out']);
self.options.position = Math.round(
position * self.options.fps
) / self.options.fps;
if (setVideo && self.loaded) {
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.$subtitle && setSubtitle();
self.$timeline && self.$timeline.options({
position: self.options.position
});
self.$position && self.$position.html(formatPosition());
}
function setSizes(callback) {
var ms = callback ? 250 : 0;
self.width = self.options.fullscreen ? window.innerWidth : self.options.width;
self.height = self.options.fullscreen ? window.innerHeight : self.options.height;
self.iconSize = Math.max(Math.round(self.height / 10), 16),
self.iconLeft = parseInt((self.width - self.iconSize) / 2),
self.iconTop = parseInt((self.height - self.iconSize) / 2);
if (self.$timeline || self.$space) {
self.timelineWidth = self.width -
self.options.controls.reduce(function(prev, curr) {
return prev + (
curr == 'timeline' || curr == 'space' ? 0 :
curr == 'position' ? self.positionWidth : 16
);
}, 0);
if (self.$timeline) {
self.timelineImageWidth = self.timelineWidth - self.barHeight;
}
}
that.animate(getCSS('player'), ms, callback);
self.$videoContainer.animate(getCSS('videoContainer'), ms);
self.loaded && self.$video.animate(getCSS('video'), ms);
self.$poster && self.$poster.animate(getCSS('poster'), ms);
self.$logo && self.$logo.animate(getCSS('logo'), ms);
self.$loadingIcon.animate(getCSS('loadingIcon'), ms);
self.$playIcon && self.$playIcon.animate(getCSS('playIcon'), ms);
self.$subtitle && self.$subtitle.animate(getCSS('subtitle'), ms);
self.$title && self.$title.animate(getCSS('title'), ms);
self.$controls && self.$controls.animate(getCSS('controls'), ms);
if (self.$timeline) {
self.$timeline.animate(getCSS('timeline'), ms, function() {
self.$timeline.options({
width: self.timelineWidth
})
});
}
self.$space && self.$space.animate(getCSS('space'), ms);
}
function setSubtitle() {
var subtitle = getSubtitle();
if (subtitle != self.subtitle) {
self.subtitle = subtitle;
self.$subtitle.html(
Ox.highlight(self.subtitle, self.options.find, 'Ox.Highlight')
.replace(/\n/g, '<br/>')
);
}
}
function showInterface() {
Ox.print('showInterface');
clearTimeout(self.interfaceTimeout);
if (self.$title) {
self.titleIsVisible = true;
}
if (self.$controls) {
self.controlsAreVisible = true;
}
that.find('.OxInterface').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.getImagePath('symbolLoadingAnimated.svg')
.replace('/classic/', '/modern/')
}).show();
}
function submitFindInput() {
self.options.find = self.$findInput.options('value');
self.results = find(self.options.find);
self.$timeline && self.$timeline.options({
results: self.results
});
if (self.results.length) {
setPosition(self.results[0]['in'] + self.secondsPerFrame, true);
self.currentResult = 0;
}
}
function goToNextResult(direction) {
var found = false,
position = 0;
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'];
}
Ox.print('>>', self.results, position)
setPosition(position + self.secondsPerFrame, true);
}
function submitPositionInput() {
self.$positionInput.hide();
self.$position.html('').show();
setPosition(parsePositionInput(self.$positionInput.options('value')), true);
if (self.wasPlaying) {
togglePaused(true);
self.video.play();
self.wasPlaying = false;
}
if (self.focus == 'mouseenter' && !self.mouseHasLeft) {
that.gainFocus();
}
self.mouseHasLeft && hideInterface();
}
function toggleFullscreen(toggleButton) {
var parentOffset, wasPlaying;
self.options.fullscreen = !self.options.fullscreen;
if (!self.options.paused) {
// video won't keep playing accross detach/append
self.video.pause();
wasPlaying = 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()
.css({
left: self.absoluteOffset.left + 'px',
top: self.absoluteOffset.top + 'px',
zIndex: 1000
})
.appendTo(Ox.UI.$body);
setSizes(function() {
wasPlaying && self.video.play();
that.bind({
mousemove: function() {
if (!self.mouseIsInControls) {
showInterface();
hideInterface();
}
}
});
self.$controls && self.$controls.bind({
mouseenter: function() {
self.mouseIsInControls = true;
},
mouseleave: function() {
self.mouseIsInControls = false;
}
});
showInterface();
hideInterface();
});
} else {
// flag makes the animation end on absolute position
self.exitFullscreen = true;
that.unbind('mousemove');
self.$controls && self.$controls
.trigger('mouseleave')
.unbind('mouseenter')
.unbind('mouseleave');
Ox.print('???', self.mouseIsInControls);
setSizes(function() {
self.exitFullscreen = false;
that.detach()
.css({
left: self.relativeOffset.left + 'px',
top: self.relativeOffset.top + 'px'
})
.appendTo(self.$parent);
wasPlaying && self.video.play();
self.options.enableKeyboard && that.gainFocus();
//showInterface();
});
}
if (toggleButton && self.$fullscreenButton) {
self.$fullscreenButton.toggleTitle();
}
}
function toggleMuted(toggleButton) {
self.options.muted = !self.options.muted;
self.video.muted = self.options.muted;
if (toggleButton && self.$muteButton) {
self.$muteButton.toggleTitle();
}
}
function togglePaused(toggleButton) {
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'], true);
}
self.video.play();
self.playInterval = setInterval(playing, self.millisecondsPerFrame);
if (self.options.showIcon) {
self.options.showIcon && self.$playIcon.animate({
opacity: 0
}, 250, togglePlayIcon);
}
}
if (toggleButton && self.$playButton) {
self.$playButton.toggleTitle();
}
}
function togglePlayIcon() {
self.$playIcon.attr({
src: Ox.UI.getImagePath(
'symbol' + (self.options.paused ? 'Play' : 'Pause'
)+ '.svg').replace('/classic/', '/modern/')
});
}
function toggleScale(toggleButton) {
self.options.scaleToFill = !self.options.scaleToFill;
self.$video.animate(getCSS('video'), 250);
self.$poster && self.$poster.animate(getCSS('poster'), 250);
if (toggleButton && self.$scaleButton) {
self.$scaleButton.toggleTitle();
}
}
function toggleSize() {
self.options.sizeIsLarge = !self.options.sizeIsLarge;
that.triggerEvent('size', {
size: self.options.sizeIsLarge ? 'large' : 'small'
});
}
self.setOption = function(key, value) {
if (key == 'fullscreen') {
toggleFullscreen(true);
} else if (key == 'height' || key == 'width') {
setSizes();
} else if (key == 'muted') {
toggleMuted(true);
} else if (key == 'paused') {
togglePaused(true);
} else if (key == 'position') {
setPosition(value, true);
} else if (key == 'scaleToFill') {
toggleScale(true);
}
};
that.playInToOut = function() {
playInToOut();
return that;
};
return that;
};