2830 lines
98 KiB
JavaScript
2830 lines
98 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
|
|
tracks <[s]> Track names, like "English" or "Director's Commentary"
|
|
audioTrack <s|''> Two-letter ISO 639-1 language code or track name
|
|
censored <a|[]> Array of censored ranges
|
|
censoredIcon <s|''> 'Censored' icon
|
|
censoredTooltip <s|''> Tooltip for 'censored' icon
|
|
chapters <[o]|[]> List of chapter objects with position and title
|
|
controlsBottom <[s]|[]> Bottom controls, from left to right
|
|
Can be 'close', fullscreen', 'scale', 'title', 'chapterTitle',
|
|
'find', 'open', 'play', 'playInToOut', 'previous', 'next', 'loop',
|
|
'mute', 'volume', 'size', 'timeline', 'position', 'settings', and
|
|
'space[int]'. A 'space16' control, for example, is empty space that
|
|
is 16px wide, and a 'space' control is empty space that separates
|
|
left-aligned from right-aligned controls.
|
|
controlsTooltips <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 'mouseenter'
|
|
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
|
|
logo <s|''> Logo image URL
|
|
logoLink <s|''> Logo link URL
|
|
logoTitle <s|''> Text for Logo tooltip // fixme: shouldn't this be logoTooltip then?s
|
|
loop <b|false> If true, video loops
|
|
muted <b|false> If true, video is muted
|
|
paused <b|false> If true, video is paused
|
|
playbackRate: <n|1> playback rate
|
|
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)
|
|
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
|
|
tracks <[s]> Track names, like "English" or "Director's Commentary"
|
|
subtitlesOffset <n|0> bottom offset for subtitles in % of video height
|
|
subtitlesDefaultTrack <s|'English'> Track name
|
|
subtitlesTrack <s|'English'> Track name
|
|
timeline <s> Timeline image URL
|
|
timelineType <s|''> Current timeline type id
|
|
timelineTypes <[o]|[]> Array of timeline type objects (id and title)
|
|
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 array of objects
|
|
({duration, index, resolution, src, track})
|
|
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
|
|
positioning <!> positioning
|
|
resolution <!> resolution
|
|
scale <!> scale
|
|
select <!> select
|
|
setpoint <!> setpoint
|
|
size <!> size
|
|
subtitles <!> subtitles
|
|
timeline <!> timeline
|
|
volume <!> volume
|
|
zap <!> zap
|
|
@*/
|
|
|
|
Ox.VideoPlayer = function(options, self) {
|
|
|
|
self = self || {};
|
|
var that = Ox.Element({}, self)
|
|
.defaults({
|
|
annotations: [],
|
|
audioTrack: '',
|
|
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,
|
|
logo: '',
|
|
logoLink: '',
|
|
logoTitle: '',
|
|
loop: false,
|
|
muted: false,
|
|
paused: false,
|
|
playbackRate: 1,
|
|
playInToOut: false,
|
|
position: 0,
|
|
poster: '',
|
|
posterFrame: -1,
|
|
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: [],
|
|
subtitlesDefaultTrack: 'English',
|
|
subtitlesOffset: 0,
|
|
subtitlesTrack: 'English',
|
|
timeline: '',
|
|
timelineType: '',
|
|
timelineTypes: [],
|
|
title: '',
|
|
type: 'play',
|
|
video: '',
|
|
volume: 1,
|
|
width: 256
|
|
})
|
|
.options(options || {})
|
|
.update({
|
|
audioTrack: setAudioTrack,
|
|
enableSubtitles: setSubtitlesTrack,
|
|
find: setSubtitleText,
|
|
fullscreen: function() {
|
|
self.options.fullscreen = !self.options.fullscreen;
|
|
toggleFullscreen();
|
|
},
|
|
height: function() {
|
|
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();
|
|
},
|
|
playbackRate: function() {
|
|
self.$video.options({
|
|
playbackRate: self.options.playbackRate
|
|
});
|
|
},
|
|
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();
|
|
},
|
|
subtitles: function() {
|
|
loadSubtitles();
|
|
self.$settings && self.$settings.replaceWith(self.$settings = renderSettings());
|
|
},
|
|
subtitlesTrack: setSubtitlesTrack,
|
|
timeline: function() {
|
|
self.$timeline.options({imageURL: self.options.timeline});
|
|
},
|
|
video: function() {
|
|
setVideo();
|
|
self.$video.options({
|
|
items: self.video
|
|
});
|
|
},
|
|
volume: function() {
|
|
setVolume(self.options.volume);
|
|
},
|
|
width: function() {
|
|
setSizes();
|
|
}
|
|
})
|
|
.addClass('OxVideoPlayer');
|
|
|
|
Ox.$window.on({
|
|
resize: function() {
|
|
self.options.fullscreen && setSizes();
|
|
}
|
|
});
|
|
|
|
Ox.Fullscreen.bind('change', function() {
|
|
//FIXME: is change fired before window size is updated to fullscreen?
|
|
setTimeout(function() {
|
|
setSizes(self.options.fullscreen);
|
|
}, 250);
|
|
});
|
|
|
|
if (Ox.isEmpty(self.options.annotations)) {
|
|
self.options.annotations = self.options.subtitles;
|
|
}
|
|
|
|
if (Ox.isObject(self.options.video[0]) && 'index' in self.options.video[0]) {
|
|
self.options.video = Ox.sortBy(self.options.video, 'index');
|
|
}
|
|
|
|
setVideo();
|
|
|
|
if (self.options.playInToOut) {
|
|
self['in'] = self.options['in'];
|
|
self.out = self.options.out;
|
|
self.options.duration = self.out - self['in'];
|
|
} else {
|
|
self['in'] = 0;
|
|
self.out = self.options.duration || 86399; // fixme: ugly
|
|
}
|
|
self.options.position = Ox.limit(self.options.position, self['in'], self.out);
|
|
if (!self.options.paused) {
|
|
self.options.showIconOnLoad = false;
|
|
}
|
|
|
|
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 = [];
|
|
|
|
loadSubtitles();
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Keyboard
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
if (self.options.enableKeyboard) {
|
|
that.bindEvent({
|
|
key_0: toggleMuted,
|
|
key_1: toggleScale,
|
|
key_down: function() {
|
|
goToNext('chapter', 1);
|
|
},
|
|
key_equal: function() {
|
|
changeVolume(0.1);
|
|
},
|
|
key_escape: hideControlMenus,
|
|
key_f: focusFind,
|
|
key_g: function() {
|
|
goToNext('result', 1);
|
|
},
|
|
key_k: function togglePlaybackRate() {
|
|
that.options({
|
|
playbackRate: self.options.playbackRate == 1 ? 2 : self.options.playbackRate == 2 ? 0.5 : 1
|
|
});
|
|
},
|
|
key_l: toggleLoop,
|
|
key_left: function() {
|
|
setPosition(self.options.position - self.secondsPerFrame);
|
|
that.triggerEvent('position', {
|
|
position: self.options.position
|
|
});
|
|
},
|
|
key_minus: function() {
|
|
changeVolume(-0.1);
|
|
},
|
|
key_p: playInToOut,
|
|
key_right: function() {
|
|
setPosition(self.options.position + self.secondsPerFrame);
|
|
that.triggerEvent('position', {
|
|
position: self.options.position
|
|
});
|
|
},
|
|
key_shift_f: function() {
|
|
self.options.enableFullscreen && toggleFullscreen();
|
|
},
|
|
key_shift_g: function() {
|
|
goToNext('result', -1);
|
|
},
|
|
key_shift_left: function() {
|
|
setPosition(self.options.position - 1);
|
|
that.triggerEvent('position', {
|
|
position: self.options.position
|
|
});
|
|
},
|
|
key_shift_right: function() {
|
|
setPosition(self.options.position + 1);
|
|
that.triggerEvent('position', {
|
|
position: self.options.position
|
|
});
|
|
},
|
|
key_space: togglePaused,
|
|
key_up: function() {
|
|
goToNext('chapter', -1);
|
|
}
|
|
});
|
|
if (self.options.focus == 'mouseenter') {
|
|
that.on({
|
|
mouseenter: function() {
|
|
if (!self.inputHasFocus) {
|
|
that.gainFocus();
|
|
}
|
|
},
|
|
mouseleave: function() {
|
|
that.loseFocus();
|
|
}
|
|
});
|
|
} else {
|
|
that.on({
|
|
click: function() {
|
|
if (!Ox.Focus.focusedElementIsInput()) {
|
|
that.gainFocus();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Mouse
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
if ((
|
|
!self.options.externalControls && (
|
|
self.options.controlsTop.length
|
|
|| self.options.controlsBottom.length
|
|
)
|
|
)) {
|
|
that.on({
|
|
mouseenter: function() {
|
|
showControls();
|
|
self.mouseHasLeft = false;
|
|
},
|
|
mouseleave: function() {
|
|
hideControls();
|
|
self.mouseHasLeft = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
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({
|
|
items: self.video,
|
|
loop: self.options.loop,
|
|
muted: self.options.muted,
|
|
playbackRate: self.options.playbackRate,
|
|
volume: self.options.volume
|
|
})
|
|
.bindEvent(Ox.extend({
|
|
durationchange: durationchange,
|
|
ended: ended,
|
|
itemchange: itemchange,
|
|
loadedmetadata: loadedmetadata,
|
|
requiresusergesture: requiresusergesture,
|
|
seeked: seeked,
|
|
seeking: seeking,
|
|
sizechange: sizechange
|
|
}, self.options.progress ? {
|
|
progress: progress
|
|
} : {}))
|
|
.appendTo(self.$videoContainer);
|
|
|
|
self.$video.$element.css({position: 'absolute'});
|
|
// avoid showing first frame
|
|
self.$video.hide();
|
|
|
|
} 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
|
|
})
|
|
.hide()
|
|
.one({
|
|
load: function() {
|
|
self.$poster
|
|
.css(getVideoCSS(
|
|
self.$poster[0].width,
|
|
self.$poster[0].height
|
|
))
|
|
.show();
|
|
self.posterIsVisible = true;
|
|
}
|
|
})
|
|
.appendTo(self.$videoContainer);
|
|
}
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Logo
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
if (self.options.logo) {
|
|
self.$logo = $('<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})
|
|
.hide()
|
|
.appendTo(self.$videoContainer);
|
|
if (!Ox.isEmpty(
|
|
Ox.isObject(self.options.video[0])
|
|
? getVideo()
|
|
: self.options.video
|
|
)) {
|
|
showLoadingIcon();
|
|
}
|
|
|
|
if (self.options.showIcon || self.options.showIconOnLoad) {
|
|
self.$playIcon = $('<img>')
|
|
.addClass('OxPlayIcon OxVideo')
|
|
.attr({
|
|
src: Ox.UI.getImageURL('symbol' + (
|
|
self.options.paused ? 'Play' : 'Pause'
|
|
), 'videoIcon')
|
|
})
|
|
.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, 'videoIcon'
|
|
)
|
|
})
|
|
.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 == 'chapterTitle') {
|
|
|
|
self.$chapterTitle = $('<div>')
|
|
.addClass('OxTitle')
|
|
.html(getChapterTitle())
|
|
.appendTo(self['$controls' + titleCase].$element);
|
|
|
|
} else if (control == 'close') {
|
|
|
|
self.$closeButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'close',
|
|
tooltip: Ox._('Close'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
that.triggerEvent('close');
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'find') {
|
|
|
|
self.$findButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'find',
|
|
tooltip: Ox._('Find'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: toggleFind
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'fullscreen') {
|
|
|
|
self.$fullscreenButton = Ox.Button({
|
|
style: 'video',
|
|
tooltip: [
|
|
Ox._('Enter Fullscreen'), Ox._('Exit Fullscreen')
|
|
],
|
|
type: 'image',
|
|
value: self.options.fullscreen ? 'shrink' : 'grow',
|
|
values: ['grow', 'shrink']
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
toggleFullscreen('button');
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'goto') {
|
|
|
|
self.$setButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'goTo' + Ox.toTitleCase(self.options.type),
|
|
tooltip: Ox._(
|
|
'Go to ' + Ox.toTitleCase(self.options.type)
|
|
+ ' Point'
|
|
),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: goToPoint
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'loop') {
|
|
|
|
self.$loopButton = Ox.Button({
|
|
style: 'video',
|
|
tooltip: [Ox._('Don\'t Loop'), Ox._('Loop')],
|
|
type: 'image',
|
|
value: self.options.loop ? 'RepeatAll' : 'RepeatNone',
|
|
values: ['RepeatAll', 'RepeatNone']
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
toggleLoop('button');
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'mute') {
|
|
|
|
self.$muteButton = Ox.Button({
|
|
style: 'video',
|
|
tooltip: [Ox._('Mute'), Ox._('Unmute')],
|
|
type: 'image',
|
|
value: self.options.muted ? 'unmute' : 'mute',
|
|
values: ['mute', 'unmute']
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
toggleMuted('button');
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'next') {
|
|
|
|
self.$nextChapterButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'playNext',
|
|
tooltip: Ox._('Next'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
goToNext('chapter', 1);
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'open') {
|
|
|
|
self.$openButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'arrowRight',
|
|
tooltip: self.options.controlsTooltips.open || '',
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
that.triggerEvent('open');
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'play') {
|
|
|
|
self.$playButton = Ox.Button({
|
|
style: 'video',
|
|
// FIXME: this is retarded, fix Ox.Button
|
|
tooltip: [Ox._('Play'), Ox._('Pause')],
|
|
type: 'image',
|
|
value: self.options.paused ? 'play' : 'pause',
|
|
values: ['play', 'pause']
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
togglePaused('button');
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'playInToOut') {
|
|
|
|
self.$playInToOutButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'playInToOut',
|
|
tooltip: Ox._('Play In to Out'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: playInToOut
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'position') {
|
|
|
|
self.positionWidth = getPositionWidth();
|
|
|
|
self.$position = Ox.Element({
|
|
tooltip: Ox._(
|
|
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.$previousChapterButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'playPrevious',
|
|
tooltip: Ox._('Previous'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
goToNext('chapter', -1);
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'scale') {
|
|
|
|
self.$scaleButton = Ox.Button({
|
|
style: 'video',
|
|
tooltip: [Ox._('Scale to Fill'), Ox._('Scale to Fit')],
|
|
type: 'image',
|
|
value: self.options.scaleToFill ? 'fit' : 'fill',
|
|
values: ['fill', 'fit']
|
|
})
|
|
.bindEvent('change', function() {
|
|
toggleScale('button');
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'set') {
|
|
|
|
self.$setButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'set' + Ox.toTitleCase(self.options.type),
|
|
tooltip: Ox._(
|
|
'Set ' + Ox.toTitleCase(self.options.type)
|
|
+ ' Point'
|
|
),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: setPoint
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'settings') {
|
|
|
|
self.$settingsButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'set',
|
|
tooltip: Ox._('Settings'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
self.$settings.toggle();
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
self.$settings = renderSettings().appendTo(that);
|
|
|
|
} else if (control == 'size') {
|
|
|
|
self.$sizeButton = Ox.Button({
|
|
style: 'video',
|
|
tooltip: [Ox._('Larger'), Ox._('Smaller')],
|
|
type: 'image',
|
|
value: self.options.sizeIsLarge ? 'shrink' : 'grow',
|
|
values: ['grow', 'shrink']
|
|
})
|
|
.bindEvent('change', toggleSize)
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'space') {
|
|
|
|
self['$space' + titleCase] = $('<div>')
|
|
.html(' ') // fixme: ??
|
|
.appendTo(self['$controls' + titleCase].$element);
|
|
|
|
} else if (Ox.startsWith(control, 'space')) {
|
|
|
|
$('<div>')
|
|
.css({
|
|
width: parseInt(control.substr(5)) + 'px',
|
|
height: '16px'
|
|
})
|
|
.appendTo(self['$controls' + titleCase].$element);
|
|
|
|
} else if (control == 'timeline') {
|
|
|
|
/*
|
|
if (self.options.showProgress) {
|
|
self.$progress = $('<img>')
|
|
.attr({
|
|
src: getProgressImageURL()
|
|
})
|
|
.css({
|
|
float: 'left',
|
|
height: self.barHeight + 'px',
|
|
})
|
|
.appendTo(self.$timelineImages.$element);
|
|
}
|
|
*/
|
|
|
|
if (self.options.duration) {
|
|
self.$timeline = getTimeline()
|
|
} else {
|
|
self.$timeline = Ox.Element()
|
|
.html(' ');
|
|
}
|
|
self.$timeline.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'title') {
|
|
|
|
self.$title = $('<div>')
|
|
.addClass('OxTitle')
|
|
.html(self.options.title)
|
|
.appendTo(self['$controls' + titleCase].$element);
|
|
|
|
} else if (control == 'volume') {
|
|
|
|
self.$volumeButton = Ox.Button({
|
|
style: 'video',
|
|
title: getVolumeImage(),
|
|
tooltip: Ox._('Volume'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: toggleVolume
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'zapHome') {
|
|
|
|
self.$zapHomeButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'up',
|
|
tooltip: Ox._('Home Channel'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
that.triggerEvent('zap', {direction: 0});
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'zapNext') {
|
|
|
|
self.$zapNextButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'right',
|
|
tooltip: Ox._('Next Channel'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
that.triggerEvent('zap', {direction: 1});
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
} else if (control == 'zapPrevious') {
|
|
|
|
self.$zapPreviousButton = Ox.Button({
|
|
style: 'video',
|
|
title: 'left',
|
|
tooltip: Ox._('Previous Channel'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
that.triggerEvent('zap', {direction: -1});
|
|
}
|
|
})
|
|
.appendTo(self['$controls' + titleCase]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Find
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
if (self.options.enableFind) {
|
|
|
|
self.$find = $('<div>')
|
|
.addClass('OxControls OxFind')
|
|
.css({
|
|
top: self.options.controlsTop.length ? '16px' : 0
|
|
})
|
|
.appendTo(that);
|
|
|
|
self.$results = Ox.Element({
|
|
tooltip: Ox._('Results')
|
|
})
|
|
.addClass('OxResults')
|
|
.html('0')
|
|
.appendTo(self.$find);
|
|
|
|
self.$previousResultButton = Ox.Button({
|
|
disabled: true,
|
|
style: 'symbol',
|
|
title: 'arrowLeft',
|
|
tooltip: Ox._('Previous'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
goToNext('result', -1);
|
|
}
|
|
})
|
|
.appendTo(self.$find);
|
|
|
|
self.$nextResultButton = Ox.Button({
|
|
disabled: true,
|
|
style: 'symbol',
|
|
title: 'arrowRight',
|
|
tooltip: Ox._('Next'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
goToNext('result', 1);
|
|
}
|
|
})
|
|
.appendTo(self.$find);
|
|
|
|
self.$findInput = Ox.Input({
|
|
changeOnKeypress: true,
|
|
value: self.options.find
|
|
})
|
|
.bindEvent({
|
|
blur: function() {
|
|
self.inputHasFocus = false;
|
|
},
|
|
focus: function() {
|
|
self.inputHasFocus = true;
|
|
},
|
|
change: function(data) {
|
|
submitFindInput(data.value, false);
|
|
},
|
|
submit: function(data) {
|
|
self.inputHasFocus = false;
|
|
submitFindInput(data.value, true);
|
|
}
|
|
})
|
|
.appendTo(self.$find);
|
|
|
|
self.$clearButton = Ox.Button({
|
|
disabled: !self.options.find,
|
|
style: 'symbol',
|
|
title: 'delete',
|
|
tooltip: Ox._('Clear'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
self.options.find = '';
|
|
self.results = [];
|
|
self.$results.html('0');
|
|
self.$findInput.clearInput();
|
|
self.subtitle && setSubtitleText();
|
|
self.$timeline && self.$timeline.options({
|
|
find: self.options.find,
|
|
results: self.results
|
|
});
|
|
//setTimeout(self.$findInput.focusInput, 10);
|
|
}
|
|
})
|
|
.appendTo(self.$find);
|
|
|
|
self.$hideFindButton = Ox.Button({
|
|
style: 'symbol',
|
|
title: 'close',
|
|
tooltip: Ox._('Hide'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: toggleFind
|
|
})
|
|
.appendTo(self.$find);
|
|
|
|
}
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Volume
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
if (self.hasVolumeControl) {
|
|
|
|
self.$volume = $('<div>')
|
|
.addClass('OxControls OxVolume')
|
|
.css({
|
|
bottom: self.options.controlsBottom.length ? '16px' : 0
|
|
})
|
|
.appendTo(that);
|
|
|
|
self.$hideVolumeButton = Ox.Button({
|
|
style: 'symbol',
|
|
title: 'close',
|
|
tooltip: Ox._('Hide'),
|
|
type: 'image'
|
|
})
|
|
.bindEvent({
|
|
click: toggleVolume
|
|
})
|
|
.appendTo(self.$volume);
|
|
|
|
self.$muteButton = Ox.Button({
|
|
style: 'symbol',
|
|
tooltip: [Ox._('Mute'), Ox._('Unmute')],
|
|
type: 'image',
|
|
value: self.options.muted ? 'unmute' : 'mute',
|
|
values: ['mute', 'unmute']
|
|
})
|
|
.bindEvent({
|
|
click: function() {
|
|
toggleMuted('button');
|
|
}
|
|
})
|
|
.appendTo(self.$volume);
|
|
|
|
self.$volumeInput = Ox.Range({
|
|
changeOnDrag: true,
|
|
max: 1,
|
|
min: 0,
|
|
step: 0.001,
|
|
value: self.options.muted ? 0 : self.options.volume
|
|
})
|
|
.bindEvent({
|
|
change: function(data) {
|
|
setVolume(data.value);
|
|
}
|
|
})
|
|
.appendTo(self.$volume);
|
|
|
|
self.$volumeValue = $('<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 = [];
|
|
|
|
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() {
|
|
Ox.$body.addClass('OxDragging');
|
|
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('positioning', {
|
|
position: self.options.position
|
|
});
|
|
}
|
|
|
|
function dragend() {
|
|
Ox.$body.removeClass('OxDragging');
|
|
!self.drag.paused && togglePaused();
|
|
that.triggerEvent('position', {
|
|
position: self.options.position
|
|
});
|
|
}
|
|
|
|
function durationchange() {
|
|
self.videoWidth = self.$video.videoWidth();
|
|
self.videoHeight = self.$video.videoHeight();
|
|
self.videoCSS = getVideoCSS();
|
|
self.posterMarkerCSS = getPosterMarkerCSS();
|
|
self.$video.css(self.videoCSS);
|
|
self.$poster && self.$poster.css(self.videoCSS);
|
|
self.$posterMarker && Ox.forEach(
|
|
self.$posterMarker,
|
|
function(marker, position) {
|
|
marker.css(self.posterMarkerCSS[position]);
|
|
}
|
|
);
|
|
self.out = self.options.playInToOut && self.out < self.$video.duration()
|
|
? self.out : self.$video.duration();
|
|
self.options.duration = self.out - self['in'];
|
|
self.$timeline && self.$timeline.replaceWith(
|
|
self.$timeline = getTimeline()
|
|
);
|
|
if(self.loadedMetadata) {
|
|
setPosition(self.$video.currentTime());
|
|
} else {
|
|
self.loadedMetadata = true;
|
|
setPosition(self.options.position);
|
|
if (self.options.paused && self.playOnLoad) {
|
|
self.playOnLoad = false;
|
|
togglePaused('button');
|
|
}
|
|
}
|
|
self.$subtitle && self.$subtitle.animate({
|
|
bottom: getCSS('subtitle').bottom
|
|
}, 250);
|
|
that.triggerEvent('durationchange', {
|
|
duration: self.options.duration
|
|
});
|
|
}
|
|
|
|
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 && setTimeout(rewind, 250);
|
|
that.triggerEvent('ended');
|
|
}
|
|
|
|
function enterFullscreen() {
|
|
that.on({
|
|
mousemove: function() {
|
|
showControls();
|
|
hideControls();
|
|
}
|
|
});
|
|
showControls();
|
|
hideControls();
|
|
that.find('.OxControls').on({
|
|
mouseenter: function() {
|
|
self.mouseIsInControls = true;
|
|
},
|
|
mouseleave: function() {
|
|
self.mouseIsInControls = false;
|
|
}
|
|
});
|
|
that.gainFocus();
|
|
}
|
|
|
|
function find(query) {
|
|
var results = [];
|
|
if (query.length) {
|
|
query = query.toLowerCase();
|
|
results = Ox.filter(self.options.annotations, function(annotation) {
|
|
return Ox.decodeHTMLEntities(Ox.stripTags(
|
|
annotation.text.toLowerCase()
|
|
)).indexOf(query) > -1;
|
|
}).map(function(annotation) {
|
|
return {
|
|
id: annotation.id,
|
|
'in': annotation['in'],
|
|
out: annotation.out
|
|
};
|
|
})
|
|
results = Ox.filter(self.options.annotations, function(annotation) {
|
|
return Ox.decodeHTMLEntities(Ox.stripTags(
|
|
annotation.text.toLowerCase()
|
|
)).indexOf(query) > -1;
|
|
}).map(function(annotation) {
|
|
return {
|
|
id: annotation.id,
|
|
'in': annotation['in'],
|
|
out: annotation.out
|
|
};
|
|
});
|
|
}
|
|
return results;
|
|
}
|
|
|
|
function focusFind() {
|
|
!self.interfaceIsVisible && showControls();
|
|
// need timeout so the "f" doesn't appear in the input field
|
|
setTimeout(function() {
|
|
if (self.$find.is(':hidden')) {
|
|
toggleFind();
|
|
} else {
|
|
self.$findInput.focusInput(true);
|
|
}
|
|
}, 0);
|
|
}
|
|
|
|
function formatPosition(position) {
|
|
position = Ox.isUndefined(position) ? self.options.position : position;
|
|
return Ox.formatDuration(position, self.options.showMilliseconds);
|
|
}
|
|
|
|
function getCensored() {
|
|
var censored = false;
|
|
Ox.forEach(self.options.censored, function(v) {
|
|
if (
|
|
v['in'] < self.options.position
|
|
&& v.out > self.options.position
|
|
) {
|
|
censored = true;
|
|
return false; // break
|
|
}
|
|
});
|
|
return censored;
|
|
}
|
|
|
|
function getChapterTitle() {
|
|
var chapterTitle = '';
|
|
self.options.chapters && Ox.forEach(self.options.chapters, function(v, i) {
|
|
if (
|
|
v.position <= self.options.position && (
|
|
i == self.options.chapters.length - 1
|
|
|| self.options.chapters[i + 1].position > self.options.position
|
|
)
|
|
) {
|
|
chapterTitle = v.title;
|
|
return false; // break
|
|
}
|
|
});
|
|
return chapterTitle;
|
|
}
|
|
|
|
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') {
|
|
var offset = 0,
|
|
videoCSS;
|
|
if (self.options.subtitlesOffset) {
|
|
videoCSS = getVideoCSS();
|
|
offset = Math.floor((
|
|
self.options.subtitlesOffset / 100
|
|
) * videoCSS.height) + videoCSS.top;
|
|
offset = Math.max(offset, 0);
|
|
}
|
|
css = {
|
|
bottom: (
|
|
Math.floor(self.height / 16)
|
|
+ offset
|
|
+ (!!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) {
|
|
return Ox.limit(
|
|
(e.layerX - 48 - self.barHeight / 2) / self.timelineImageWidth * self.$video.duration(),
|
|
0, self.$video.duration()
|
|
);
|
|
} else {
|
|
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() {
|
|
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 = '';
|
|
if (self.options.enableSubtitles) {
|
|
Ox.forEach(self.options.subtitles, function(v) {
|
|
if (
|
|
v['in'] <= self.options.position
|
|
&& v.out >= self.options.position
|
|
&& (!v.tracks || Ox.contains(v.tracks, self.options.subtitlesTrack))
|
|
) {
|
|
subtitle = v.text;
|
|
return false; // break
|
|
}
|
|
});
|
|
}
|
|
return subtitle;
|
|
}
|
|
|
|
function getSubtitles() {
|
|
return self.options.enableSubtitles
|
|
? self.options.subtitles.filter(function(v) {
|
|
return !v.tracks || Ox.contains(v.tracks, self.options.subtitlesTrack);
|
|
})
|
|
: [];
|
|
}
|
|
|
|
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
|
|
});
|
|
}
|
|
});
|
|
$timeline.children().css({
|
|
marginLeft: getTimelineLeft() + 'px'
|
|
});
|
|
$timeline.find('.OxInterface').css({
|
|
marginLeft: getTimelineLeft() + 'px'
|
|
});
|
|
return $timeline;
|
|
}
|
|
|
|
function getTimelineLeft() {
|
|
var left = 0;
|
|
Ox.forEach(self.options.controlsBottom, function(control) {
|
|
if (control == 'timeline') {
|
|
return false; // break
|
|
}
|
|
left += control == 'position' ? self.positionWidth : 16
|
|
});
|
|
return left;
|
|
}
|
|
|
|
function getTimelineWidth() {
|
|
return (self.options.fullscreen ? window.innerWidth : self.options.width) -
|
|
self.options.controlsBottom.reduce(function(prev, curr) {
|
|
return prev + (
|
|
curr == 'timeline' || curr == 'space' ? 0
|
|
: Ox.startsWith(curr, 'space') ? parseInt(curr.substr(5))
|
|
: curr == 'position' ? getPositionWidth()
|
|
: 16
|
|
);
|
|
}, 0);
|
|
}
|
|
|
|
function getTitleWidth() {
|
|
return (self.options.fullscreen ? window.innerWidth : self.options.width)
|
|
- self.options.controlsTop.reduce(function(prev, curr) {
|
|
return prev + (
|
|
curr == 'title' || curr == 'chapterTitle' || curr == 'space' ? 0
|
|
: Ox.startsWith(curr, 'space') ? parseInt(curr.substr(5))
|
|
: 16
|
|
);
|
|
}, 0);
|
|
}
|
|
|
|
function getVideo() {
|
|
return self.options.video.filter(function(video) {
|
|
return (
|
|
!self.options.audioTrack
|
|
|| video.track == self.options.audioTrack
|
|
) && (
|
|
!self.options.resolution
|
|
|| video.resolution == self.options.resolution
|
|
);
|
|
});
|
|
}
|
|
|
|
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 goToNext(type, direction) {
|
|
// type can be 'chapter' or 'result'
|
|
var position, positions;
|
|
if (type == 'chapter') {
|
|
positions = self.options.chapters.map(function(chapter) {
|
|
return chapter.position;
|
|
});
|
|
} else if (type == 'result') {
|
|
positions = Ox.unique(self.results.map(function(result) {
|
|
return result['in'];
|
|
}));
|
|
}
|
|
position = Ox.nextValue(positions, self.options.position, direction);
|
|
setPosition(position);
|
|
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.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 hidePoster() {
|
|
if (self.loadedMetadata && self.posterIsVisible) {
|
|
self.$poster.animate({
|
|
opacity: 0
|
|
}, 250);
|
|
self.posterIsVisible = false;
|
|
}
|
|
}
|
|
|
|
function isEqual(a, b) {
|
|
return Math.abs(a - b) < 0.001;
|
|
}
|
|
|
|
function itemchange(data) {
|
|
var item = self.$video.options('items')[data.item];
|
|
Ox.Log('Video', 'ITEMCHANGE', item);
|
|
}
|
|
|
|
function requiresusergesture() {
|
|
Ox.Log('Video', 'requires user gesture');
|
|
var $playIcon;
|
|
function removeBehaviorsRestrictions() {
|
|
window.removeEventListener('keydown', removeBehaviorsRestrictions);
|
|
window.removeEventListener('mousedown', removeBehaviorsRestrictions);
|
|
window.removeEventListener('touchstart', removeBehaviorsRestrictions);
|
|
$playIcon.remove();
|
|
showLoadingIcon();
|
|
self.options.showIconOnLoad = false;
|
|
}
|
|
window.addEventListener('keydown', removeBehaviorsRestrictions);
|
|
window.addEventListener('mousedown', removeBehaviorsRestrictions);
|
|
window.addEventListener('touchstart', removeBehaviorsRestrictions);
|
|
hideLoadingIcon();
|
|
//FIXME: also load frame at current position?
|
|
$playIcon = $('<img>')
|
|
.addClass('OxPlayIcon OxVideo OxInterface')
|
|
.attr({
|
|
src: Ox.UI.getImageURL('symbolPlay', 'videoIcon')
|
|
})
|
|
.css(getCSS('playIcon'))
|
|
.css({
|
|
opacity: 1
|
|
})
|
|
.appendTo(self.$videoContainer);
|
|
}
|
|
|
|
function loadImage() {
|
|
self.$image
|
|
.one({
|
|
load: function() {
|
|
hideLoadingIcon();
|
|
}
|
|
})
|
|
.attr({
|
|
src: self.options.video(
|
|
// fixme: this keeps the frame from being beyond the end,
|
|
// but what should be avoided is setting position to a point
|
|
// beyond the beginning of the last frame
|
|
Math.min(
|
|
self.options.position,
|
|
Math.floor(self.options.duration * self.options.fps) / self.options.fps
|
|
),
|
|
self.options.width
|
|
)
|
|
});
|
|
}
|
|
|
|
function loadedmetadata() {
|
|
|
|
Ox.Log('Video', 'LOADEDMETADATA')
|
|
|
|
var hadDuration = !!self.options.duration;
|
|
|
|
self.loadedMetadata = true;
|
|
|
|
self.videoWidth = self.$video.videoWidth();
|
|
self.videoHeight = self.$video.videoHeight();
|
|
self.videoCSS = getVideoCSS();
|
|
self.posterMarkerCSS = getPosterMarkerCSS();
|
|
self.$video.css(self.videoCSS);
|
|
self.$poster && self.$poster.css(self.videoCSS);
|
|
self.$posterMarker && Ox.forEach(
|
|
self.$posterMarker,
|
|
function(marker, position) {
|
|
marker.css(self.posterMarkerCSS[position]);
|
|
}
|
|
);
|
|
|
|
self.out = self.options.playInToOut && self.out < self.$video.duration()
|
|
? self.out : self.$video.duration();
|
|
self.options.duration = self.out - self['in'];
|
|
Ox.Log('Video', '---------------------------------------- POS', self.options.position)
|
|
Ox.Log('Video', '----------------------------------- DURATION', self.options.duration)
|
|
|
|
//self.options.position = Ox.limit(self.options.position, self['in'], self.out);
|
|
//self.$video.currentTime(self.options.position);
|
|
|
|
setPosition(self.options.position);
|
|
self.$video.muted(self.options.muted).volume(self.options.volume);
|
|
|
|
if (!self.options.paused) {
|
|
self.options.paused = true;
|
|
togglePaused('loadedmetadata');
|
|
} else if (self.options.paused && self.playOnLoad) {
|
|
togglePaused('loadedmetadata');
|
|
}
|
|
self.$playButton && self.$playButton.options({disabled: false});
|
|
|
|
if (self.options.showIcon || self.options.showIconOnLoad) {
|
|
self.$playIcon.animate({
|
|
opacity: 1
|
|
}, 250);
|
|
}
|
|
if (self.options.showControlsOnLoad) {
|
|
showControls();
|
|
}
|
|
!hadDuration && self.$timeline && self.$timeline.replaceWith(
|
|
self.$timeline = getTimeline()
|
|
);
|
|
|
|
if (self.options.enableKeyboard && self.options.focus == 'load') {
|
|
that.gainFocus();
|
|
}
|
|
self.$subtitle && self.$subtitle.animate({
|
|
bottom: getCSS('subtitle').bottom
|
|
}, 250);
|
|
that.triggerEvent('loadedmetadata');
|
|
}
|
|
|
|
function loadedsubtitles() {
|
|
if (!self.subtitlesTracks || Ox.isEmpty(self.subtitlesTracks)) {
|
|
self.subtitlesTracks = [self.options.subtitlesDefaultTrack];
|
|
}
|
|
self.subtitlesTracks.push('None');
|
|
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
|
|
});
|
|
}
|
|
} else {
|
|
// needed on options change
|
|
self.options.enableSubtitles && self.$subtitle && setSubtitle();
|
|
}
|
|
}
|
|
|
|
function loadSubtitles() {
|
|
if (self.options.subtitles.length) {
|
|
if (Ox.isArray(self.options.subtitles)) {
|
|
self.subtitlesTracks = Ox.sort(Ox.unique(Ox.flatten(
|
|
self.options.subtitles.map(function(subtitle) {
|
|
return subtitle.tracks;
|
|
})
|
|
))).filter(function(track) {
|
|
return !!track;
|
|
});
|
|
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 = [];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function playing() {
|
|
self.options.position = self.$video.currentTime();
|
|
if (
|
|
(self.playInToOut && self.options.position >= self.options.out)
|
|
|| (self.options.playInToOut && self.options.position >= self.out)
|
|
) {
|
|
if (self.options.loop) {
|
|
setPosition(self.options['in']);
|
|
self.$video.play();
|
|
} else {
|
|
togglePaused();
|
|
if (self.options.rewind) {
|
|
setTimeout(rewind, 250);
|
|
} else {
|
|
setPosition(self.options.out ? self.options.out : self.out/*, 'video'*/);
|
|
}
|
|
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 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() {
|
|
return Ox.VideoPlayerMenu({
|
|
items: [{disabled: true, title: Ox._('Resolution')}].concat(
|
|
self.resolutions.map(function(resolution) {
|
|
return {
|
|
group: 'resolution',
|
|
id: resolution,
|
|
checked: resolution == self.options.resolution,
|
|
title: resolution + 'p'
|
|
};
|
|
}),
|
|
self.audioTracks.length > 1
|
|
? [{}, {disabled: true, title: Ox._('Audio')}].concat(
|
|
self.audioTracks.map(function(track) {
|
|
return {
|
|
group: 'audioTrack',
|
|
id: track,
|
|
checked: track == self.options.audioTrack,
|
|
title: Ox._(track)
|
|
};
|
|
})
|
|
)
|
|
: [],
|
|
self.options.subtitles.length
|
|
? [{}, {disabled: true, title: Ox._('Subtitles')}].concat(
|
|
self.subtitlesTracks.map(function(track) {
|
|
return {
|
|
group: 'subtitlesTrack',
|
|
id: track,
|
|
checked: self.options.enableSubtitles
|
|
? track == self.options.subtitlesTrack
|
|
: track == '',
|
|
title: Ox._(track)
|
|
};
|
|
})
|
|
)
|
|
: [],
|
|
self.options.timelineTypes.length
|
|
? [{}, {disabled: true, title: Ox._('Timeline')}].concat(
|
|
self.options.timelineTypes.map(function(type) {
|
|
return {
|
|
group: 'timeline',
|
|
id: type.id,
|
|
checked: type.id == self.options.timelineType,
|
|
title: type.title
|
|
};
|
|
})
|
|
)
|
|
: [],
|
|
self.options.enableDownload
|
|
? [{}, {id: 'download', title: Ox._('Download')}]
|
|
: []
|
|
)
|
|
})
|
|
.addClass('OxControls OxSettings')
|
|
.bindEvent({
|
|
click: function(data) {
|
|
var resolution, type;
|
|
if (data.group == 'resolution') {
|
|
resolution = parseInt(data.id, 10);
|
|
if (resolution != self.options.resolution) {
|
|
self.options.resolution = resolution;
|
|
setResolution();
|
|
}
|
|
} else if (data.group == 'audioTrack') {
|
|
self.options.audioTrack = data.id;
|
|
setAudioTrack();
|
|
} else if (data.group == 'subtitlesTrack') {
|
|
self.options.subtitlesTrack = data.id == 'None'
|
|
? '' : data.id;
|
|
self.options.enableSubtitles = !!self.options.subtitlesTrack;
|
|
setSubtitlesTrack();
|
|
} else if (data.group == 'timeline') {
|
|
type = self.options.timelineTypes[
|
|
Ox.indexOf(self.options.timelineTypes, function(type) {
|
|
return type.title == title;
|
|
})
|
|
].id;
|
|
if (type != self.options.timelineType) {
|
|
self.options.timelineType = type;
|
|
setTimelineType();
|
|
}
|
|
} else if (data.id == 'download') {
|
|
that.triggerEvent('download');
|
|
}
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
function rewind() {
|
|
setPosition(self.options.playInToOut ? self.options['in'] : 0);
|
|
}
|
|
|
|
function seeked() {
|
|
Ox.Log('Video', 'seeked')
|
|
clearTimeout(self.seekTimeout);
|
|
self.seekTimeout = 0;
|
|
Ox.Log('Video', 'hide loading icon')
|
|
hideLoadingIcon();
|
|
self.$video.show();
|
|
self.$playIcon && self.$playIcon.show();
|
|
hidePoster();
|
|
}
|
|
|
|
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 setAudioTrack() {
|
|
updateVideo();
|
|
}
|
|
|
|
function setCensored() {
|
|
var censored = getCensored();
|
|
if (censored != self.censored) {
|
|
self.censored = censored;
|
|
censor();
|
|
}
|
|
}
|
|
|
|
function setChapterTitle() {
|
|
var chapterTitle = getChapterTitle();
|
|
if (chapterTitle != self.chapterTitle) {
|
|
self.chapterTitle = chapterTitle;
|
|
self.$chapterTitle.html(self.chapterTitle)
|
|
}
|
|
}
|
|
|
|
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) {
|
|
// 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.$chapterTitle && setChapterTitle();
|
|
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;
|
|
}
|
|
self.$timeline /*&& from != 'timeline'*/ && self.$timeline.options({
|
|
position: self.options.position
|
|
});
|
|
} else {
|
|
//showLoadingIcon();
|
|
loadImage();
|
|
}
|
|
}
|
|
|
|
function setResolution() {
|
|
updateVideo();
|
|
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.$chapterTitle, 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/>')
|
|
: ' <br/> '
|
|
// FIXME: weird bug, only in fullscreen, only in chrome
|
|
);
|
|
}
|
|
|
|
function setSubtitlesTrack() {
|
|
var enableSubtitles = self.options.enableSubtitles && !!self.options.subtitlesTrack;
|
|
self.options.enableSubtitles = enableSubtitles;
|
|
setSubtitle();
|
|
self.$timeline && self.$timeline.options({
|
|
subtitles: getSubtitles()
|
|
});
|
|
if (enableSubtitles && !!self.options.subtitlesTrack) {
|
|
that.triggerEvent('subtitlestrack', {
|
|
track: self.options.subtitlesTrack
|
|
});
|
|
} else {
|
|
that.triggerEvent('subtitles', {
|
|
subtitles: self.options.enableSubtitles
|
|
});
|
|
}
|
|
}
|
|
|
|
function setTimelineType() {
|
|
that.triggerEvent('timeline', {timeline: self.options.timelineType});
|
|
}
|
|
|
|
function setVideo() {
|
|
if (Ox.isObject(self.options.video[0])) {
|
|
self.audioTracks = Ox.sort(Ox.unique(
|
|
self.options.video.map(function(video) {
|
|
return video.track;
|
|
})
|
|
));
|
|
if (!Ox.contains(self.audioTracks, self.options.audioTrack)) {
|
|
self.options.audioTrack = self.audioTracks[0];
|
|
}
|
|
self.resolutions = Ox.sort(Ox.unique(
|
|
self.options.video.map(function(video) {
|
|
return video.resolution;
|
|
})
|
|
));
|
|
if (!Ox.contains(self.resolutions, self.options.resolution)) {
|
|
self.options.resolution = self.resolutions[0];
|
|
}
|
|
self.video = getVideo();
|
|
} else {
|
|
self.video = [{src: self.options.video}];
|
|
self.resolutions = [];
|
|
self.audioTracks = [];
|
|
}
|
|
}
|
|
|
|
function setVolume(volume) {
|
|
self.options.volume = volume;
|
|
if (!!self.options.volume == self.options.muted) {
|
|
toggleMuted();
|
|
} else {
|
|
self.$volumeButton && self.$volumeButton.options({
|
|
title: getVolumeImage()
|
|
});
|
|
self.$volumeValue && self.$volumeValue.html(
|
|
self.options.muted ? 0 : Math.round(self.options.volume * 100)
|
|
);
|
|
}
|
|
!self.censored && self.$video.volume(self.options.volume);
|
|
that.triggerEvent('volume', {
|
|
volume: self.options.volume
|
|
});
|
|
}
|
|
|
|
function showControls() {
|
|
//Ox.Log('Video', 'showControls');
|
|
clearTimeout(self.interfaceTimeout);
|
|
if (!self.interfaceIsVisible) {
|
|
self.interfaceIsVisible = true;
|
|
if (self.$controlsTop) {
|
|
self.controlsTopAreVisible = true;
|
|
}
|
|
if (self.$controlsBottom) {
|
|
self.controlsBottomAreVisible = true;
|
|
}
|
|
self.$controlsTop && self.$controlsTop.animate({
|
|
opacity: 1
|
|
}, 250);
|
|
self.$controlsBottom && self.$controlsBottom.animate({
|
|
opacity: 1
|
|
}, 250);
|
|
['find', 'settings', 'volume'].forEach(function(element) {
|
|
var $element = self['$' + element];
|
|
$element && $element.is(':visible') && $element.animate({
|
|
opacity: 1
|
|
}, 250);
|
|
});
|
|
self.$logo && self.$logo.animate({
|
|
top: getCSS('logo').top,
|
|
opacity: 0.5
|
|
}, 250, function() {
|
|
self.options.logoLink && self.$logo
|
|
.on({
|
|
click: function() {
|
|
document.location.href = self.options.logoLink;
|
|
}
|
|
});
|
|
self.options.logoTitle && self.$logo
|
|
.on({
|
|
mouseenter: function(e) {
|
|
self.$logoTooltip.show(e);
|
|
},
|
|
mouseleave: self.$logoTooltip.hide
|
|
});
|
|
});
|
|
self.$subtitle && self.$subtitle.animate({
|
|
bottom: getCSS('subtitle').bottom
|
|
}, 250);
|
|
}
|
|
}
|
|
|
|
function showLoadingIcon() {
|
|
self.$loadingIcon.start().show();
|
|
}
|
|
|
|
function showVolume() {
|
|
if (self.$volume) {
|
|
!self.interfaceIsVisible && showControls();
|
|
self.$volume.is(':hidden') && toggleVolume();
|
|
}
|
|
}
|
|
|
|
function sizechange() {
|
|
self.videoWidth = self.$video.videoWidth();
|
|
self.videoHeight = self.$video.videoHeight();
|
|
self.videoCSS = getVideoCSS();
|
|
self.$video.css(self.videoCSS);
|
|
};
|
|
|
|
function submitFindInput(value, hasPressedEnter) {
|
|
self.options.find = value;
|
|
self.results = find(self.options.find);
|
|
if (self.$find) {
|
|
self.$results.html(self.results.length);
|
|
self.$previousResultButton.options({
|
|
disabled: !self.results.length
|
|
});
|
|
self.$nextResultButton.options({
|
|
disabled: !self.results.length
|
|
});
|
|
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) {
|
|
goToNext('result', 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
|
|
});
|
|
that.triggerEvent('submit');
|
|
}
|
|
|
|
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.$body);
|
|
if (self.options.externalControls) {
|
|
self.externalControls = true;
|
|
self.options.externalControls = false;
|
|
self.$videoContainer.css({top: 0});
|
|
}
|
|
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);
|
|
if (self.externalControls) {
|
|
self.options.externalControls = true;
|
|
self.$videoContainer.css({top: '16px'});
|
|
}
|
|
setSizes(true, function() {
|
|
self.exitFullscreen = false;
|
|
that.detach()
|
|
.removeClass('OxFullscreen')
|
|
.css({
|
|
left: self.relativeOffset.left + 'px',
|
|
top: self.relativeOffset.top + 'px',
|
|
zIndex: 1
|
|
})
|
|
.appendTo(self.$parent);
|
|
playOnFullscreen && self.$video.play();
|
|
self.options.enableKeyboard && that.gainFocus();
|
|
//showControls();
|
|
});
|
|
}
|
|
if (self.$fullscreenButton && from != 'button') {
|
|
self.$fullscreenButton.toggle();
|
|
}
|
|
that.triggerEvent('fullscreen', {
|
|
fullscreen: self.options.fullscreen
|
|
});
|
|
}
|
|
|
|
function toggleLoop(from) {
|
|
self.options.loop = !self.options.loop;
|
|
self.$video.options('loop', self.options.loop);
|
|
if (self.$loopButton && from != 'button') {
|
|
self.$loopButton.toggle();
|
|
}
|
|
that.triggerEvent('loop', {
|
|
loop: self.options.loop
|
|
});
|
|
}
|
|
|
|
function toggleMuted(from) {
|
|
self.hasVolumeControl && showVolume();
|
|
self.options.muted = !self.options.muted;
|
|
self.$video.muted(self.options.muted);
|
|
if (!self.options.muted && !self.options.volume) {
|
|
self.options.volume = 1;
|
|
self.$video.volume(1);
|
|
}
|
|
if (self.$muteButton && from != 'button') {
|
|
self.$muteButton.toggle();
|
|
}
|
|
self.$volumeButton && self.$volumeButton.options({
|
|
title: getVolumeImage()
|
|
});
|
|
self.$volumeInput && self.$volumeInput.value(
|
|
self.options.muted ? 0 : self.options.volume
|
|
);
|
|
self.$volumeValue && self.$volumeValue.html(
|
|
self.options.muted ? 0 : Math.round(self.options.volume * 100)
|
|
);
|
|
that.triggerEvent('muted', {
|
|
muted: self.options.muted
|
|
});
|
|
}
|
|
|
|
function togglePaused(from) {
|
|
self.options.paused = !self.options.paused;
|
|
self.$timeline && self.$timeline.options({
|
|
paused: self.options.paused
|
|
});
|
|
if (!self.loadedMetadata) {
|
|
return;
|
|
}
|
|
if (self.options.paused) {
|
|
self.$video.pause();
|
|
clearInterval(self.playInterval);
|
|
if (self.options.showIcon) {
|
|
togglePlayIcon();
|
|
self.$playIcon.animate({
|
|
opacity: 1
|
|
}, 250);
|
|
}
|
|
self.playInToOut = false;
|
|
} else {
|
|
hidePoster();
|
|
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.$playIcon.animate({
|
|
opacity: 0
|
|
}, 250, togglePlayIcon);
|
|
} else if (self.options.showIconOnLoad) {
|
|
self.$playIcon.animate({
|
|
opacity: 0
|
|
}, 250);
|
|
}
|
|
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'
|
|
), 'videoIcon')
|
|
});
|
|
}
|
|
|
|
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);
|
|
self.$subtitle && self.$subtitle.animate({
|
|
bottom: getCSS('subtitle').bottom
|
|
}, 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();
|
|
}
|
|
|
|
function updateVideo() {
|
|
if (!self.options.paused) {
|
|
self.playOnLoad = true;
|
|
togglePaused('button');
|
|
}
|
|
self.loadedMetadata = false;
|
|
showLoadingIcon();
|
|
self.video = getVideo();
|
|
self.$video.options({
|
|
items: self.video
|
|
});
|
|
self.$playButton && self.$playButton.options({disabled: true});
|
|
}
|
|
|
|
/*@
|
|
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 loop state
|
|
() -> <o> toggle loop state
|
|
@*/
|
|
that.toggleLoop = function() {
|
|
toggleLoop();
|
|
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;
|
|
|
|
};
|