oxjs/source/UI/js/Video/VideoAnnotationPanel.js

1751 lines
64 KiB
JavaScript
Raw Normal View History

2011-11-05 16:46:53 +00:00
'use strict';
2011-05-16 08:24:46 +00:00
/*@
2013-10-22 14:45:17 +00:00
Ox.VideoAnnotationPanel <f> VideoAnnotationPanel Object
options <o> Options object
self <o> Shared private variable
2013-10-22 14:45:17 +00:00
([options[, self]]) -> <o:Ox.SplitPanel> VideoAnnotationPanel Object
2012-06-17 22:38:26 +00:00
addannotation <!> addannotation
annotationsrange <!> annotationsrange
annotationssize <!> annotationssize
annotationssort <!> annotationssort
2014-09-02 12:09:12 +00:00
audioTrack <s|''> Two-letter ISO 639-1 language code or track name
2012-06-17 22:38:26 +00:00
censored <!> censored
define <!> define
downloadselection <!> downloadselection
2018-09-12 17:58:00 +00:00
downloadframe <!> downloadframe
2012-06-17 22:38:26 +00:00
downloadvideo <!> downloadvideo
editannotation <!> editannotation
embedselection <!> embedselection
findannotations <!> findannotations
find <!> find
2018-08-07 16:12:22 +00:00
findLayer <s|*> limit find to layer
2012-06-17 22:38:26 +00:00
importannotations <!> importannotations
info <!> info
key_* <!> key_*
muted <!> muted
paused <!> paused
points <!> points
position <!> position
posterframe <!> posterframe
removeannotation <!> removeannotation
resizecalendar <!> resizecalendar
resizemap <!> resizemap
resolution <!> resolution
select <!> select
subtitles <!> subtitles
timeline <!> timeline
toggleannotations <!> toggleannotations
togglecalendar <!> togglecalendar
togglelayer <!> togglelayer
togglemap <!> togglemap
volume <!> volume
2011-05-16 08:24:46 +00:00
@*/
2013-10-22 14:45:17 +00:00
Ox.VideoAnnotationPanel = function(options, self) {
self = self || {};
var that = Ox.Element({}, self)
2011-04-22 22:03:10 +00:00
.defaults({
2012-01-13 16:25:47 +00:00
annotationsCalendarSize: 256,
annotationsHighlight: 'none',
2012-01-13 16:25:47 +00:00
annotationsMapSize: 256,
annotationsRange: 'all',
annotationsSeparator: ';',
2012-01-12 10:39:05 +00:00
annotationsSize: 256,
annotationsSort: 'position',
2013-08-12 12:31:13 +00:00
annotationsTooltip: Ox._('annotations'),
2014-09-02 12:09:12 +00:00
audioTrack: '',
autocomplete: null,
2011-10-22 21:03:42 +00:00
censored: [],
censoredIcon: '',
censoredTooltip: '',
2012-01-17 15:43:46 +00:00
clickLink: null,
2011-04-22 22:03:10 +00:00
cuts: [],
duration: 0,
2012-01-30 23:27:27 +00:00
enableDownload: false,
2012-02-17 09:06:26 +00:00
enableImport: false,
enableExport: false,
enableSetPosterFrame: false,
2011-12-22 15:47:46 +00:00
enableSubtitles: false,
2011-04-22 22:03:10 +00:00
find: '',
2018-08-07 16:12:22 +00:00
findLayer: '*',
2011-05-18 16:00:29 +00:00
fps: 25,
2011-05-18 18:30:58 +00:00
getFrameURL: null,
getLargeTimelineURL: null,
getSmallTimelineURL: null,
2011-05-18 16:00:29 +00:00
'in': 0,
itemName: {singular: 'video', plural: 'videos'},
2013-06-02 20:19:36 +00:00
height: 0,
2011-04-22 22:03:10 +00:00
layers: [],
loop: false,
muted: false,
2011-05-18 16:00:29 +00:00
out: 0,
paused: true,
2016-03-29 12:26:04 +00:00
playbackRate: 1,
2011-04-22 22:03:10 +00:00
position: 0,
posterFrame: 0,
resolution: 0,
selected: '',
selectResult: false,
2011-04-22 22:03:10 +00:00
showAnnotations: false,
2012-01-13 16:25:47 +00:00
showAnnotationsCalendar: false,
showAnnotationsMap: false,
2011-05-24 19:19:27 +00:00
showLargeTimeline: true,
2012-01-11 07:52:01 +00:00
showLayers: {},
2012-01-12 10:39:05 +00:00
showUsers: false,
2011-04-22 22:03:10 +00:00
subtitles: [],
subtitlesDefaultTrack: 'en',
subtitlesLayer: null,
2015-04-14 14:50:52 +00:00
subtitlesOffset: 0,
subtitlesTrack: 'en',
timeline: '',
timelines: [],
2011-08-19 14:44:03 +00:00
videoRatio: 16/9,
2011-04-22 22:03:10 +00:00
videoSize: 'small',
video: '',
volume: 1,
2011-04-22 22:03:10 +00:00
width: 0
})
.options(options || {})
2012-05-28 19:35:41 +00:00
.update({
height: setSizes,
'in': function() {
setPoint('in', self.options['in']);
},
loop: function() {
self.$video.options({loop: self.options.loop});
},
2012-05-28 19:35:41 +00:00
out: function() {
setPoint('out', self.options.out);
},
paused: function() {
self.$player[0].options({
paused: self.options.paused
});
},
2016-03-29 12:26:04 +00:00
playbackRate: function() {
self.$player[0].options({
playbackRate: self.options.playbackRate
});
},
2012-05-28 19:35:41 +00:00
position: function() {
setPosition(self.options.position);
},
selected: function() {
selectAnnotation(
self.options.selected
? Ox.getObjectById(self.annotations, self.options.selected)
: {id: ''}
);
},
showAnnotations: function() {
2014-11-17 21:08:10 +00:00
self.$mainPanel.toggleElement(1);
2012-05-28 19:35:41 +00:00
},
2015-03-23 11:56:41 +00:00
showLayers: function() {
self.$annotationPanel.options({
showLayers: Ox.clone(self.options.showLayers)
});
},
2012-06-15 13:20:07 +00:00
timeline: function() {
2014-10-10 14:30:39 +00:00
self.$menuButton.checkItem('timelines_' + self.options.timeline);
2012-06-15 13:20:07 +00:00
updateTimelines();
},
2017-04-15 11:45:01 +00:00
volume: function() {
self.$player[0].options({
volume: self.options.volume
});
},
2012-05-28 19:35:41 +00:00
width: setSizes
})
2011-04-22 22:03:10 +00:00
.bindEvent({
2011-05-18 16:00:29 +00:00
key_0: toggleMuted,
2011-04-22 22:03:10 +00:00
key_alt_left: function() {
},
key_alt_right: function() {
},
key_alt_shift_left: function() {
},
key_alt_shift_right: function() {
},
key_b: function() {
self.annotations.length && selectAnnotation(
getNextAnnotation('annotation', -1)
);
},
2011-04-22 22:03:10 +00:00
key_backslash: function() {
self.annotations.length && selectAnnotation();
2011-04-22 22:03:10 +00:00
},
key_closebracket: function() {
self.annotations.length && movePositionTo('annotation', 1);
2011-04-22 22:03:10 +00:00
},
key_comma: function() {
movePositionTo('cut', -1);
},
2013-07-13 21:05:04 +00:00
key_control_c: function() {
that.triggerEvent('copy', [{
annotation: self.options.selected,
'in': self.options['in'],
out: self.options.out
}]);
},
key_control_shift_c: function() {
that.triggerEvent('copyadd', [{
annotation: self.options.selected,
'in': self.options['in'],
out: self.options.out
}]);
},
key_delete: function() {
self.$annotationPanel.removeItem(true);
},
2011-04-22 22:03:10 +00:00
key_dot: function() {
movePositionTo('cut', 1);
},
key_down: function() {
movePositionBy(self.sizes.timeline[0].width);
},
2012-01-10 14:49:28 +00:00
key_enter: function() {
2012-01-15 15:05:17 +00:00
if (self.editing) {
blurAnnotation();
} else if (isEditable()) {
2012-01-10 14:49:28 +00:00
editAnnotation();
}
},
2012-01-11 07:52:01 +00:00
key_equal: function() {
self.$player[0].changeVolume(0.1);
2012-01-11 07:52:01 +00:00
},
2012-01-10 14:49:28 +00:00
key_escape: function() {
if (self.editing) {
blurAnnotation();
} else if (self.options.selected) {
2012-01-12 10:39:05 +00:00
selectAnnotation({id: ''});
2012-01-10 14:49:28 +00:00
}
},
key_f: function() {
setTimeout(function() {
2011-12-18 09:44:11 +00:00
self.$findInput.focusInput(true);
});
},
key_g: function() {
self.results.length && selectAnnotation(
getNextAnnotation('result', 1)
);
},
key_h: showKeyboardShortcuts,
2011-04-22 22:03:10 +00:00
key_i: function() {
2011-05-19 10:18:39 +00:00
setPoint('in', self.options.position);
2011-04-22 22:03:10 +00:00
},
key_k: function togglePlaybackRate() {
that.options({
playbackRate: self.options.playbackRate == 1 ? 2 : self.options.playbackRate == 2 ? 0.5 : 1
});
},
key_l: toggleLoop,
2011-04-22 22:03:10 +00:00
key_left: function() {
2013-08-01 08:42:14 +00:00
movePositionBy(-1 / self.options.fps);
2011-04-22 22:03:10 +00:00
},
2012-01-11 07:52:01 +00:00
key_minus: function() {
self.$player[0].changeVolume(-0.1);
2012-01-11 07:52:01 +00:00
},
key_n: function() {
self.annotations.length && selectAnnotation(
getNextAnnotation('annotation', 1)
);
},
2011-04-22 22:03:10 +00:00
key_o: function() {
2011-05-19 10:18:39 +00:00
setPoint('out', self.options.position);
2011-04-22 22:03:10 +00:00
},
key_openbracket: function() {
self.annotations.length && movePositionTo('annotation', -1);
2011-04-22 22:03:10 +00:00
},
key_p: playInToOut,
key_right: function() {
2013-08-01 08:42:14 +00:00
movePositionBy(1 / self.options.fps);
2011-04-22 22:03:10 +00:00
},
key_shift_0: function() {
movePositionBy(-self.options.position);
},
2011-04-22 22:03:10 +00:00
key_shift_down: function() {
movePositionBy(self.options.duration);
},
key_shift_enter: function() {
if (self.editing) {
blurAnnotation();
} else if (isEditable()) {
editAnnotation();
}
},
key_shift_equal: function() {
self.options.videoSize == 'small' && toggleSize();
},
key_shift_g: function() {
self.results.length && selectAnnotation(
getNextAnnotation('result', -1)
);
},
2011-04-22 22:03:10 +00:00
key_shift_i: function() {
goToPoint('in');
},
2013-08-01 08:42:14 +00:00
key_shift_left: function() {
movePositionBy(-1);
},
key_shift_minus: function() {
self.options.videoSize == 'large' && toggleSize();
},
2011-04-22 22:03:10 +00:00
key_shift_o: function() {
goToPoint('out');
},
key_shift_p: function() {
setPosition(self.options.posterFrame);
},
2011-04-22 22:03:10 +00:00
key_shift_right: function() {
2011-05-18 18:30:58 +00:00
movePositionBy(1);
2011-04-22 22:03:10 +00:00
},
key_shift_up: function() {
2011-05-18 18:30:58 +00:00
movePositionBy(-self.options.position);
2011-04-22 22:03:10 +00:00
},
2013-08-01 08:42:14 +00:00
key_slash: selectCut,
2011-05-18 16:00:29 +00:00
key_space: togglePaused,
2011-04-22 22:03:10 +00:00
key_up: function() {
movePositionBy(-self.sizes.timeline[0].width);
}
});
self.options.subtitles = options.subtitles !== void 0
? self.options.subtitles : parseSubtitles();
self.subtitlesTracks = Ox.sort(Ox.unique(Ox.flatten(
self.options.subtitles.map(function(subtitle) {
return subtitle.tracks;
})
))).map(function(track) {
return {
id: track,
title: Ox._(track),
checked: self.options.enableSubtitles
&& track == self.options.subtitlesTrack
};
}).concat([{
id: '',
title: Ox._('None'),
checked: !self.options.enableSubtitles
}]);
if (Ox.isObject(self.options.video[0])) {
self.resolutions = Ox.sort(Ox.unique(
self.options.video.map(function(video) {
return video.resolution;
})
)).map(function(resolution) {
return {
id: resolution + '',
title: resolution + 'p',
checked: self.options.resolution == resolution
};
});
self.audioTracks = Ox.sort(Ox.unique(
self.options.video.map(function(video) {
return video.track;
})
)).map(function(track) {
return {
id: track,
title: Ox._(track),
checked: self.options.audioTrack == track
};
});
}
self.options.layers.forEach(function(layer, i) {
that.bindEvent('key_' + (i + 1), function() {
2012-02-04 11:44:19 +00:00
layer.editable
? addAnnotation(layer.id)
: that.triggerEvent('info', {layer: layer.id});
2012-01-10 14:49:28 +00:00
});
});
self.$player = [];
self.$timeline = [];
self.annotations = getAnnotations();
self.controlsHeight = 16;
self.editing = false;
self.margin = 8;
self.positions = getPositions();
self.results = [];
self.words = getWords();
2011-04-22 22:03:10 +00:00
self.$editor = Ox.Element()
2013-10-22 14:45:17 +00:00
.addClass('OxVideoAnnotationPanel OxMedia')
2013-12-06 20:43:00 +00:00
.on({
mousedown: function(e) {
var $target = $(e.target);
if (!$target.is('.OxPosition') && !$target.is('input')) {
that.gainFocus();
}
// ignore mousedown inside a focused input element
if ($target.is('.OxKeyboardFocus')) {
return;
}
2013-12-06 20:43:00 +00:00
// the following is needed to determine
// how to handle annotation input blur
if (self.editing) {
self.focused = true;
setTimeout(function() {
// annotation folder will gain focus on blur
// so we need to get focus back
that.gainFocus();
self.focused = false;
}, 25);
}
2012-01-09 20:25:38 +00:00
}
2011-04-22 22:03:10 +00:00
});
self.sizes = getSizes();
['play', 'in', 'out'].forEach(function(type, i) {
self.$player[i] = Ox.VideoPlayer({
2011-10-22 21:03:42 +00:00
censored: self.options.censored,
censoredIcon: self.options.censoredIcon,
censoredTooltip: self.options.censoredTooltip,
2011-05-18 16:00:29 +00:00
controlsBottom: type == 'play' ?
2011-05-20 14:11:42 +00:00
['play', 'playInToOut', 'volume', 'size', 'space', 'position'] :
2011-05-18 16:00:29 +00:00
['goto', 'set', 'space', 'position'],
2011-04-22 22:03:10 +00:00
duration: self.options.duration,
2011-08-18 14:03:48 +00:00
enableMouse: true,
2011-12-19 21:13:11 +00:00
enablePosition: true,
2011-12-22 15:47:46 +00:00
enableSubtitles: self.options.enableSubtitles,
2011-05-18 16:00:29 +00:00
externalControls: true,
2018-08-07 16:12:22 +00:00
find: getSubtitlesFind(),
2011-04-22 22:03:10 +00:00
height: self.sizes.player[i].height,
id: 'player' + Ox.toTitleCase(type),
2011-05-18 16:00:29 +00:00
'in': self.options['in'],
loop: self.options.loop,
muted: self.options.muted,
2011-05-18 16:00:29 +00:00
out: self.options.out,
paused: self.options.paused,
2016-03-29 12:26:04 +00:00
playbackRate: self.options.playbackRate,
2011-05-18 16:00:29 +00:00
position: type == 'play' ? self.options.position : self.options[type],
2011-04-22 22:03:10 +00:00
posterFrame: self.options.posterFrame,
resolution: self.options.resolution,
2011-05-18 16:00:29 +00:00
showMarkers: true,
showMilliseconds: 3,
2011-08-19 06:41:48 +00:00
sizeIsLarge: self.options.videoSize == 'large',
2013-10-22 17:43:38 +00:00
subtitles: Ox.clone(self.options.subtitles, true),
subtitlesDefaultTrack: self.options.subtitlesDefaultTrack,
2015-04-14 14:50:52 +00:00
subtitlesOffset: self.options.subtitlesOffset,
subtitlesTrack: self.options.subtitlesTrack,
2011-04-22 22:03:10 +00:00
type: type,
2011-05-18 18:30:58 +00:00
video: type == 'play' ? self.options.video : self.options.getFrameURL,
volume: self.options.volume,
2011-04-22 22:03:10 +00:00
width: self.sizes.player[i].width
})
.css({
left: self.sizes.player[i].left + 'px',
top: self.sizes.player[i].top + 'px'
})
.bindEvent(
Ox.extend({
censored: function() {
that.triggerEvent('censored');
}
}, type == 'play' ? {
loop: function(data) {
that.triggerEvent('loop', data);
},
muted: function(data) {
that.triggerEvent('muted', data);
},
paused: function(data) {
self.options.paused = data.paused;
that.triggerEvent('paused', data);
},
playing: function(data) {
setPosition(data.position, true);
},
position: function(data) {
setPosition(data.position);
},
positioning: function(data) {
setPosition(data.position, false, true);
},
resolution: function(data) {
that.triggerEvent('resolution', data);
},
size: function() {
toggleSize();
},
submit: function() {
that.gainFocus();
},
subtitles: function(data) {
that.triggerEvent('subtitles', data);
},
volume: function(data) {
that.triggerEvent('volume', data);
}
} : {
gotopoint: function() {
goToPoint(type);
},
position: function(data) {
setPoint(type, data.position);
},
setpoint: function() {
setPoint(type, self.options.position);
}
})
)
2011-04-22 22:03:10 +00:00
.appendTo(self.$editor);
});
self.$timeline[0] = Ox.LargeVideoTimeline({
2011-04-22 22:03:10 +00:00
cuts: self.options.cuts,
duration: self.options.duration,
2018-08-07 16:12:22 +00:00
find: getSubtitlesFind(),
getImageURL: self.options.getLargeTimelineURL,
2011-04-22 22:03:10 +00:00
id: 'timelineLarge',
2011-05-18 16:00:29 +00:00
'in': self.options['in'],
//matches: self.options.matches,
out: self.options.out,
2011-04-22 22:03:10 +00:00
position: self.options.position,
subtitles: getSubtitles(),
type: self.options.timeline,
2011-04-22 22:03:10 +00:00
width: self.sizes.timeline[0].width
})
.css({
left: self.sizes.timeline[0].left + 'px',
top: self.sizes.timeline[0].top + 'px'
})
2011-08-17 19:34:34 +00:00
.bindEvent({
position: function(data) {
setPosition(data.position);
},
positioning: function(data) {
setPosition(data.position, false, true);
}
2011-08-17 19:34:34 +00:00
})
2011-04-22 22:03:10 +00:00
.appendTo(self.$editor);
self.$timeline[1] = Ox.BlockVideoTimeline({
2011-04-22 22:03:10 +00:00
cuts: self.options.cuts,
duration: self.options.duration,
2018-08-07 16:12:22 +00:00
find: getSubtitlesFind(),
getImageURL: self.options.getSmallTimelineURL,
2011-04-22 22:03:10 +00:00
id: 'timelineSmall',
2011-05-18 16:00:29 +00:00
'in': self.options['in'],
out: self.options.out,
2011-04-22 22:03:10 +00:00
position: self.options.position,
2011-10-20 13:32:53 +00:00
results: find(self.options.find),
2011-10-17 10:23:16 +00:00
showPointMarkers: true,
2012-01-09 20:25:38 +00:00
state: self.options.selected ? 'selected' : 'default',
subtitles: getSubtitles(),
type: self.options.timeline,
2011-04-22 22:03:10 +00:00
videoId: self.options.videoId,
width: self.sizes.timeline[1].width
})
.css({
left: self.sizes.timeline[1].left + 'px',
2012-05-26 15:48:19 +00:00
top: self.sizes.timeline[1].top + 'px'
2011-04-22 22:03:10 +00:00
})
2011-08-17 19:34:34 +00:00
.bindEvent({
edit: function() {
if (isEditable() && !self.editing) {
editAnnotation();
}
},
position: function(data) {
setPosition(data.position);
2012-01-10 14:49:28 +00:00
},
select: function() {
2012-02-04 11:44:19 +00:00
selectAnnotation(void 0, true);
}
2011-08-17 19:34:34 +00:00
})
2011-04-22 22:03:10 +00:00
.appendTo(self.$editor);
2012-01-12 10:39:05 +00:00
self.$menubar = Ox.Bar({
size: 16
})
.addClass('OxVideoPlayer')
.bindEvent({
doubleclick: function(e) {
if ($(e.target).is('.OxBar')) {
self.$editor.animate({scrollTop: 0}, 250);
}
}
});
self.$keyboardShortcuts = $('<div>').css({margin: '16px'});
2012-05-24 07:45:33 +00:00
[
2014-09-25 09:50:41 +00:00
{key: Ox.SYMBOLS.space, action: Ox._('Play/Pause')},
2013-05-10 10:45:24 +00:00
{key: 'P', action: Ox._('Play In to Out')},
{key: 'K', action: Ox._('Toggle Playback Speed')},
{key: 'L', action: Ox._('Loop')},
2013-05-10 10:45:24 +00:00
{key: '0', action: Ox._('Mute/Unmute')},
{key: '-', action: Ox._('Turn Volume Down')},
{key: '+', action: Ox._('Turn Volume Up')},
2014-09-25 09:50:41 +00:00
{key: Ox.SYMBOLS.shift + '-', action: Ox._('Small Player')},
{key: Ox.SYMBOLS.shift + '+', action: Ox._('Large Player')},
{key: Ox.SYMBOLS.arrow_left, action: Ox._('Go One Frame Back')},
{key: Ox.SYMBOLS.arrow_right, action: Ox._('Go One Frame Forward')},
{key: Ox.SYMBOLS.shift + Ox.SYMBOLS.arrow_left, action: Ox._('Go One Second Back')},
{key: Ox.SYMBOLS.shift + Ox.SYMBOLS.arrow_right, action: Ox._('Go One Second Forward')},
{key: Ox.SYMBOLS.arrow_up, action: Ox._('Go One Line Up')},
{key: Ox.SYMBOLS.arrow_down, action: Ox._('Go One Line Down')},
{key: Ox.SYMBOLS.shift + Ox.SYMBOLS.arrow_up, action: Ox._('Go to First Frame')},
{key: Ox.SYMBOLS.shift + Ox.SYMBOLS.arrow_down, action: Ox._('Go to Last Frame')},
2013-05-10 10:45:24 +00:00
{key: 'I', action: Ox._('Set In Point')},
{key: 'O', action: Ox._('Set Out Point')},
2014-09-25 09:50:41 +00:00
{key: Ox.SYMBOLS.shift + 'I', action: Ox._('Go to In Point')},
{key: Ox.SYMBOLS.shift + 'O', action: Ox._('Go to Out Point')},
2013-05-10 10:45:24 +00:00
{key: '[', action: Ox._('Go to Previous Annotation')},
{key: ']', action: Ox._('Go to Next Annotation')},
{key: '\\', action: Ox._('Select Current Annotation')},
{key: 'B', action: Ox._('Select Previous Annotation')},
{key: 'N', action: Ox._('Select Next Annotation')},
{key: '<', action: Ox._('Go to Previous Cut')},
{key: '>', action: Ox._('Go to Next Cut')},
{key: '/', action: Ox._('Select Current Cut')},
{key: 'F', action: Ox._('Find')},
2014-09-25 09:50:41 +00:00
{key: Ox.SYMBOLS.shift + 'G', action: Ox._('Go to Previous Result')},
2013-05-10 10:45:24 +00:00
{key: 'G', action: Ox._('Go to Next Result')},
2014-09-25 09:50:41 +00:00
{key: Ox.SYMBOLS['return'], action: Ox._('Edit/Submit')},
{key: Ox.SYMBOLS.escape, action: Ox._('Cancel/Deselect')},
{key: Ox.SYMBOLS.delete, action: Ox._('Delete')}
2012-05-24 07:45:33 +00:00
].concat(
2015-04-15 17:25:40 +00:00
Ox.filter(self.options.layers.slice(0, 9).map(function(layer, i) {
2012-05-24 07:45:33 +00:00
return layer.editable
2013-05-10 10:45:24 +00:00
? {key: i + 1, action: Ox._('Add {0}', [layer.item])}
2012-05-24 07:45:33 +00:00
: null;
}))
).forEach(function(shortcut) {
2011-05-20 14:11:42 +00:00
self.$keyboardShortcuts.append(
$('<div>').css({display: 'table-row'})
.append(
$('<div>').css({
display: 'table-cell',
height: '16px',
paddingRight: '16px',
//fontWeight: 'bold',
textAlign: 'right'
})
.html(shortcut.key)
)
.append(
$('<div>').css({display: 'table-cell'})
.html(shortcut.action)
)
);
});
2011-05-20 14:11:42 +00:00
2012-04-17 11:59:08 +00:00
self.$menuButton = Ox.MenuButton({
2012-05-24 07:45:33 +00:00
items: [].concat(
[
2013-05-09 13:03:33 +00:00
{id: 'size', title: Ox._('Large Player'), checked: self.options.videoSize == 'large'},
{id: 'loop', title: Ox._('Loop'), checked: self.options.loop, keyboard: 'l'},
{},
{id: 'resolutions', title: Ox._('Resolution'), items: [
{group: 'resolution', min: 1, max: 1, items: self.resolutions}
]}
],
self.audioTracks.length > 1 ? [
{id: 'audioTracks', title: Ox._('Audio'), items: [
{group: 'audioTrack', min: 1, max: 1, items: self.audioTracks}
]}
] : [],
self.options.subtitles.length ? [
{id: 'subtitlesTracks', title: Ox._('Subtitles'), items: [
{group: 'subtitlesTrack', min: 1, max: 1, items: self.subtitlesTracks}
]}
] : [],
[
{id: 'timelines', title: Ox._('Timeline'), items: [
{group: 'timeline', min: 1, max: 1, items: Ox.map(
self.options.timelines,
function(timeline) {
return Ox.extend({
checked: timeline.id == self.options.timeline
}, timeline);
}
)}
]},
{},
{id: 'gotoPosterFrame', title: Ox._('Go to Poster Frame'), keyboard: 'shift p'},
{id: 'setPosterFrame', title: Ox._('Set Poster Frame'), disabled: !self.options.enableSetPosterFrame},
{},
2013-05-09 13:03:33 +00:00
{id: 'downloadVideo', title: Ox._('Download Video...'), disabled: !self.options.enableDownload },
2018-09-12 17:58:00 +00:00
{id: 'downloadFrame', title: Ox._('Download Frame...'), disabled: !self.options.enableDownload },
2013-05-09 13:03:33 +00:00
{id: 'downloadSelection', title: Ox._('Download Selection...'), disabled: !self.options.enableDownload},
{id: 'embedSelection', title: Ox._('Embed Selection...')},
{id: 'linkToSelection', title: Ox._('Link to Selection...')},
{},
2013-05-09 13:03:33 +00:00
{id: 'keyboard', title: Ox._('Keyboard Shortcuts...'), keyboard: 'h'}
]
),
style: 'square',
title: 'set',
2013-05-09 13:03:33 +00:00
tooltip: Ox._('Options'),
type: 'image'
})
.css({float: 'left'})
.bindEvent({
2011-05-20 14:11:42 +00:00
click: function(data) {
var id = data.id;
if (id == 'gotoPosterFrame') {
setPosition(self.options.posterFrame);
} else if (id == 'setPosterFrame') {
self.options.posterFrame = self.options.position;
self.$player.forEach(function($player) {
$player.options('posterFrame', self.options.posterFrame);
});
that.triggerEvent('posterframe', {
position: self.options.posterFrame
});
2012-01-30 22:26:38 +00:00
} else if (id == 'downloadVideo') {
2012-02-03 14:43:20 +00:00
that.triggerEvent('downloadvideo');
2018-09-12 17:58:00 +00:00
} else if (id == 'downloadFrame') {
that.triggerEvent('downloadframe', {
'position': self.options.position
});
} else if (id == 'downloadSelection') {
2012-02-03 14:43:20 +00:00
that.triggerEvent('downloadselection', {
'in': self.options['in'],
2012-05-26 15:48:19 +00:00
out: self.options.out
});
} else if (id == 'embedSelection') {
2012-02-03 14:43:20 +00:00
that.triggerEvent('embedselection', {
'in': self.options['in'],
2012-05-26 15:48:19 +00:00
out: self.options.out
});
} else if (id == 'linkToSelection') {
that.triggerEvent('linktoselection', {
'in': self.options['in'],
out: self.options.out
});
} else if (id == 'keyboard') {
showKeyboardShortcuts();
}
},
change: function(data) {
var enableSubtitles,
id = data.id;
if (id == 'size') {
toggleSize();
} else if (id == 'loop') {
toggleLoop();
} else if (id == 'resolution') {
self.options.resolution = parseInt(data.checked[0].id, 10);
2011-12-20 13:45:24 +00:00
self.$player[0].options({resolution: self.options.resolution});
} else if (id == 'audioTrack') {
self.options.audioTrack = data.checked[0].id;
self.$player[0].options({audioTrack: self.options.audioTrack});
} else if (id == 'subtitlesTrack') {
enableSubtitles = !!data.checked[0].id;
if (enableSubtitles != self.options.enableSubtitles) {
that.triggerEvent('subtitles', {subtitles: enableSubtitles});
}
self.options.subtitlesTrack = data.checked[0].id;
setSubtitlesTrack(); // will set self.options.enableSubtitles
} else if (id == 'timeline') {
2012-06-15 13:20:07 +00:00
self.options.timeline = data.checked[0].id;
updateTimelines();
that.triggerEvent('timeline', {
timeline: self.options.timeline
});
2011-05-20 14:11:42 +00:00
}
},
hide: function() {
that.gainFocus();
}
})
2012-01-12 10:39:05 +00:00
.appendTo(self.$menubar);
2011-11-03 15:42:41 +00:00
self.$clearButton = Ox.Button({
disabled: self.options.find === '',
style: 'symbol',
title: 'close',
2013-05-09 13:03:33 +00:00
tooltip: Ox._('Clear'),
type: 'image'
})
.css({float: 'right'})
.bindEvent({
click: function() {
self.$findInput.clearInput();
submitFindInput('');
}
})
2012-01-12 10:39:05 +00:00
.appendTo(self.$menubar);
self.$findInput = Ox.Input({
autocomplete: self.words.map(function(word) {
return word.value;
}),
autocompleteReplace: false,
2011-05-19 19:03:35 +00:00
autocompleteSelect: true,
autocompleteSelectHighlight: true,
autocompleteSelectMax: 10,
2011-05-19 19:03:35 +00:00
autocompleteSelectSubmit: true,
changeOnKeypress: true,
2013-05-09 13:03:33 +00:00
placeholder: Ox._('Find...'),
2011-10-20 13:32:53 +00:00
value: self.options.find,
width: 128
})
.css({float: 'right', background: 'transparent'})
.bindEvent({
change: function(data) {
submitFindInput(data.value, false);
},
submit: function(data) {
submitFindInput(data.value, true);
}
})
2012-01-12 10:39:05 +00:00
.appendTo(self.$menubar);
self.$nextButton = Ox.Button({
disabled: true,
style: 'symbol',
title: 'arrowRight',
2013-05-09 13:03:33 +00:00
tooltip: Ox._('Next Result'),
type: 'image'
})
.css({float: 'right'})
.bindEvent({
click: function() {
selectAnnotation(getNextAnnotation('result', 1));
}
})
2012-01-12 10:39:05 +00:00
.appendTo(self.$menubar);
self.$previousButton = Ox.Button({
disabled: true,
style: 'symbol',
title: 'arrowLeft',
2013-05-09 13:03:33 +00:00
tooltip: Ox._('Previous Result'),
type: 'image'
})
.css({float: 'right'})
.bindEvent({
click: function() {
selectAnnotation(getNextAnnotation('result', -1));
}
})
2012-01-12 10:39:05 +00:00
.appendTo(self.$menubar);
self.$results = $('<div>')
.css({float: 'right', width: '36px', padding: '2px 4px 0 0', fontSize: '9px', textAlign: 'right', cursor: 'default', opacity: 0.25})
.html('0')
2012-01-12 10:39:05 +00:00
.appendTo(self.$menubar.$element);
2011-05-20 14:11:42 +00:00
2012-01-12 10:39:05 +00:00
self.$annotationPanel = Ox.AnnotationPanel({
autocomplete: self.options.autocomplete,
2012-01-13 16:25:47 +00:00
calendarSize: self.options.annotationsCalendarSize,
2012-01-17 15:43:46 +00:00
clickLink: self.options.clickLink,
2012-01-12 10:39:05 +00:00
editable: true,
enableExport: self.options.enableExport,
enableImport: self.options.enableImport,
highlight: self.options.find,
highlightAnnotations: self.options.annotationsHighlight,
2018-08-07 16:12:22 +00:00
highlightLayer: self.options.findLayer,
2012-01-12 10:39:05 +00:00
'in': self.options['in'],
itemName: self.options.itemName,
2012-01-12 10:39:05 +00:00
layers: self.options.layers,
2012-01-13 16:25:47 +00:00
mapSize: self.options.annotationsMapSize,
2012-01-12 10:39:05 +00:00
out: self.options.out,
position: self.options.position,
range: self.options.annotationsRange,
selected: self.options.selected,
separator: self.options.annotationsSeparator,
2012-01-13 16:25:47 +00:00
showCalendar: self.options.showAnnotationsCalendar,
showFind: true,
2012-02-20 21:51:08 +00:00
showLayers: Ox.clone(self.options.showLayers),
2012-01-13 16:25:47 +00:00
showMap: self.options.showAnnotationsMap,
2012-01-12 10:39:05 +00:00
showUsers: self.options.showUsers,
2012-01-17 09:25:58 +00:00
sort: self.options.annotationsSort,
2012-01-12 10:39:05 +00:00
width: self.options.annotationsSize
2011-05-20 14:11:42 +00:00
})
.bindEvent({
2012-01-12 10:39:05 +00:00
add: function(data) {
addAnnotation(data.layer);
},
annotationsrange: function(data) {
2012-01-15 15:05:17 +00:00
self.options.annotationsRange = data.range;
2012-01-12 10:39:05 +00:00
that.triggerEvent('annotationsrange', data);
},
annotationssort: function(data) {
2012-01-15 15:05:17 +00:00
self.options.annotationsSort = data.sort;
2012-01-12 10:39:05 +00:00
that.triggerEvent('annotationssort', data);
},
2012-01-15 15:05:17 +00:00
blur: function(data) {
2012-02-16 16:35:59 +00:00
// Only blur if the video editor did not receive the click,
// no dialog is open, and no menu was visible
if (
!self.focused
&& !$('.OxDialogLayer').length
&& !$('.OxMenuLayer').length
) {
blurAnnotation();
}
2012-01-15 15:05:17 +00:00
},
change: function(data) {
if (data.layer == self.options.subtitlesLayer) {
updateSubtitles();
}
2012-01-15 15:05:17 +00:00
that.triggerEvent('editannotation', data);
},
define: function(data) {
that.triggerEvent('define', data);
2012-01-12 10:39:05 +00:00
},
edit: function(data) {
updateWords('remove');
2012-01-12 10:39:05 +00:00
self.editing = true;
setTimelineState();
},
exportannotations: function() {
that.triggerEvent('exportannotations');
},
find: function(data) {
self.$findInput.options({value: data.value});
submitFindInput(data.value, true);
},
findannotations: function(data) {
that.triggerEvent('findannotations', data);
},
focus: that.gainFocus,
highlightannotations: function(data) {
self.options.annotationsHighlight = data;
that.triggerEvent('annotationshighlight', data);
},
highlightlayer: function(data) {
self.options.findLayer = data;
submitFindInput(self.$findInput.value(), false);
2018-08-07 16:53:56 +00:00
that.triggerEvent('findlayer', data);
},
importannotations: function() {
that.triggerEvent('importannotations');
},
2012-01-27 14:29:11 +00:00
info: function(data) {
that.triggerEvent('info', data);
},
open: function() {
setPosition(self.options['in']);
},
remove: removeAnnotation,
2012-01-12 19:04:32 +00:00
resize: resizeAnnotations,
resizeend: resizeendAnnotations,
resizecalendar: function(data) {
that.triggerEvent('resizecalendar', data);
},
resizemap: function(data) {
that.triggerEvent('resizemap', data);
},
2012-02-04 11:44:19 +00:00
select: function(data) {
selectAnnotation(data, true);
},
2015-04-16 20:03:38 +00:00
showentityinfo: function(data) {
that.triggerEvent('showentityinfo', data);
},
2012-01-12 10:39:05 +00:00
submit: submitAnnotation,
2012-01-12 19:04:32 +00:00
toggle: toggleAnnotations,
togglecalendar: function(data) {
2012-01-15 15:05:17 +00:00
self.options.showAnnotationsCalendar = !data.collapsed;
that.triggerEvent('togglecalendar', data);
},
2012-01-12 19:04:32 +00:00
togglelayer: function(data) {
2012-01-12 10:39:05 +00:00
that.triggerEvent('togglelayer', {
collapsed: data.collapsed,
layer: data.layer
});
},
togglemap: function(data) {
2012-01-15 15:05:17 +00:00
self.options.showAnnotationsMap = !data.collapsed;
that.triggerEvent('togglemap', data);
2012-05-26 15:48:19 +00:00
}
2012-01-12 10:39:05 +00:00
});
2012-01-11 07:52:01 +00:00
[
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'b', 'backslash', 'closebracket', 'comma', 'dot',
'equal', 'f', 'g', 'h', 'i', 'minus', 'n', 'o',
'openbracket', 'p', 'shift_0', 'shift_equal',
'shift_g', 'shift_i', 'shift_minus', 'shift_o',
'slash', 'space'
].forEach(function(key) {
key = 'key.' + key;
self.$annotationPanel.bindEvent(key, function() {
that.triggerEvent(key);
});
});
that.setElement(
2013-07-14 13:42:09 +00:00
self.$mainPanel = Ox.SplitPanel({
elements: [
{
element: Ox.SplitPanel({
elements: [
{
2012-01-12 10:39:05 +00:00
element: self.$menubar,
size: 16
},
{
element: self.$editor
}
],
orientation: 'vertical'
})
},
{
collapsed: !self.options.showAnnotations,
collapsible: true,
2012-01-12 10:39:05 +00:00
element: self.$annotationPanel,
resizable: true,
2012-01-15 15:05:17 +00:00
resize: [192, 256, 320, 384, 448, 512],
size: self.options.annotationsSize,
tooltip: self.options.annotationsTooltip
}
],
orientation: 'horizontal'
})
);
2011-04-22 22:03:10 +00:00
// we need a timeout so that a chained bindEvent
// actually catches the event
self.options.find && self.options.selectResult && setTimeout(function() {
2012-02-05 01:56:23 +00:00
// only submit if no annotation is selected
submitFindInput(self.options.find, !self.options.selected);
2013-03-06 12:26:57 +00:00
});
2012-01-10 14:49:28 +00:00
function addAnnotation(layer) {
if (self.editing) {
self.editing = false;
setTimelineState();
self.$annotationPanel.blurItem();
}
2012-01-10 14:49:28 +00:00
that.triggerEvent('addannotation', {
'in': self.options['in'],
layer: layer,
out: self.options.out,
value: ''
});
}
function blurAnnotation() {
updateWords('add');
2012-01-09 20:25:38 +00:00
self.editing = false;
2012-01-10 14:49:28 +00:00
setTimelineState();
2012-01-15 15:05:17 +00:00
if (
self.options.annotationsRange == 'position' && (
self.options.position < self.options['in']
|| self.options.position > self.options.out
)
) {
setPosition(self.options['in']);
}
// setPosition causes a folder redraw
// so blur once that's done
setTimeout(self.$annotationPanel.blurItem);
2012-01-10 14:49:28 +00:00
}
function editAnnotation() {
updateWords('remove');
2012-01-10 14:49:28 +00:00
self.editing = true;
setTimelineState();
2012-01-12 10:39:05 +00:00
self.$annotationPanel.editItem();
2012-01-03 10:26:15 +00:00
}
function find(query) {
var results = [];
if (query.length) {
query = query.toLowerCase();
2012-02-04 11:44:19 +00:00
results = self.annotations.filter(function(annotation) {
2018-08-07 16:12:22 +00:00
return Ox.contains(['*', annotation.layer], self.options.findLayer)
&& Ox.decodeHTMLEntities(Ox.stripTags(
annotation.value.toLowerCase()
)).indexOf(query) > -1;
2012-02-04 11:44:19 +00:00
});
}
return results;
}
2012-01-10 14:49:28 +00:00
function getAnnotation() {
// Get annotation at current position
var annotations = self.annotations.filter(function(annotation) {
return annotation['in'] <= self.options.position
&& annotation.out >= self.options.position
}).sort(function(a, b) {
2012-01-10 14:49:28 +00:00
var aValue = self.options.position - a['in'],
bValue = self.options.position - b['in'],
ret = 0;
if (aValue < bValue) {
ret = -1;
} else if (aValue > bValue) {
ret = 1;
} else if (a.duration < b.duration) {
ret = -1
} else if (a.duration > b.duration) {
ret = 1;
} else if (a.value < b.value) {
ret = -1
} else if (a.value > b.value) {
ret = 1;
}
return ret;
});
return annotations.length ? annotations[0] : {id: ''};
}
function getAnnotations() {
2012-05-24 07:45:33 +00:00
return Ox.flatten(self.options.layers.map(function(layer) {
2018-08-07 16:12:22 +00:00
return layer.items.map(function(item) {
return Ox.extend({layer: layer.id}, item)
});
2012-05-24 07:45:33 +00:00
})).sort(sortAnnotations);
}
function getAnnotationValue(annotationId) {
var found = false, value;
Ox.forEach(self.options.layers, function(layer, i) {
Ox.forEach(layer.items, function(item) {
if (item.id == annotationId) {
value = item.value;
found = true;
2012-07-05 08:58:08 +00:00
return false; // break
}
});
2012-07-05 08:58:08 +00:00
if (found) {
return false; // break
}
});
return value;
}
function getNextAnnotation(type, direction) {
2013-07-18 12:56:19 +00:00
// type can be 'annotation' or 'result'
var annotation,
annotations = type == 'annotation' ? self.annotations : self.results,
index,
position;
if (self.options.selected) {
index = Ox.getIndexById(annotations, self.options.selected);
if (index > -1 && self.options.position == annotations[index]['in']) {
annotation = annotations[Ox.mod(index + direction, annotations.length)];
}
}
if (!annotation) {
position = getNextPosition(type, direction);
annotations = annotations.filter(function(annotation) {
return annotation['in'] == position;
});
annotation = annotations[direction == 1 ? 0 : annotations.length - 1];
}
return annotation;
}
// fixme: why not goToNextPosition()?
2011-04-22 22:03:10 +00:00
function getNextPosition(type, direction) {
// type can be 'annotation', 'cut' or 'result'
2013-07-18 12:56:19 +00:00
var positions;
2012-01-10 14:49:28 +00:00
if (type == 'annotation') {
positions = self.positions;
2012-01-10 14:49:28 +00:00
} else if (type == 'cut') {
2012-05-24 07:45:33 +00:00
positions = [0].concat(self.options.cuts, self.options.duration);
} else if (type == 'result') {
positions = Ox.unique(self.results.map(function(result) {
return result['in'];
}));
}
2013-07-18 12:56:19 +00:00
return Ox.nextValue(positions, self.options.position, direction);
2011-04-22 22:03:10 +00:00
}
function getPositions() {
return Ox.unique(self.annotations.map(function(annotation) {
return annotation['in'];
}));
2011-04-22 22:03:10 +00:00
}
function getSelectedLayer() {
2013-10-22 17:43:38 +00:00
var selectedLayer;
Ox.forEach(self.options.layers, function(layer) {
Ox.forEach(layer.items, function(item) {
if (item.id == self.options.selected) {
2013-10-22 17:43:38 +00:00
selectedLayer = layer.id;
return false;
}
});
2013-10-22 17:43:38 +00:00
if (selectedLayer) {
return false;
}
});
2013-10-22 17:43:38 +00:00
return selectedLayer;
}
2011-04-22 22:03:10 +00:00
function getSizes(scrollbarIsVisible) {
var scrollbarWidth = Ox.UI.SCROLLBAR_SIZE,
2013-06-02 20:19:36 +00:00
contentWidth = self.options.width
- (self.options.showAnnotations * self.options.annotationsSize) - 1
- (scrollbarIsVisible ? scrollbarWidth : 0),
2011-04-22 22:03:10 +00:00
height,
lines,
2013-06-02 20:19:36 +00:00
size = {
2011-04-22 22:03:10 +00:00
player: [],
timeline: []
},
width, widths;
2012-01-09 20:25:38 +00:00
function getHeight() {
return size.player[0].height + self.controlsHeight
+ size.timeline[0].height + lines * 16
+ (lines + 3) * self.margin;
}
2011-04-22 22:03:10 +00:00
if (self.options.videoSize == 'small') {
width = 0;
2012-06-25 14:43:03 +00:00
widths = Ox.splitInt(contentWidth - 4 * self.margin, 3);
2011-04-22 22:03:10 +00:00
[1, 0, 2].forEach(function(v, i) {
size.player[v] = {
left: (i + 0.5) * self.margin + width,
top: self.margin / 2,
width: widths[i],
2011-08-19 14:44:03 +00:00
height: Math.round(widths[1] / self.options.videoRatio)
};
2011-04-22 22:03:10 +00:00
width += widths[i];
});
} else {
size.player[0] = {
left: self.margin / 2,
top: self.margin / 2,
2012-05-26 15:48:19 +00:00
width: Math.round((contentWidth - 3 * self.margin + (self.controlsHeight + self.margin) / 2 * self.options.videoRatio) * 2/3)
};
2011-08-19 14:44:03 +00:00
size.player[0].height = Math.round(size.player[0].width / self.options.videoRatio);
2011-04-22 22:03:10 +00:00
size.player[1] = {
left: size.player[0].left + size.player[0].width + self.margin,
top: size.player[0].top,
width: contentWidth - 3 * self.margin - size.player[0].width
};
2011-08-19 14:44:03 +00:00
size.player[1].height = Math.ceil(size.player[1].width / self.options.videoRatio);
2011-04-22 22:03:10 +00:00
size.player[2] = {
left: size.player[1].left,
top: size.player[0].top + size.player[1].height + self.controlsHeight + self.margin,
width: size.player[1].width,
height: size.player[0].height - size.player[1].height - self.controlsHeight - self.margin
};
2011-04-22 22:03:10 +00:00
}
size.timeline[0] = {
left: self.margin / 2,
top: size.player[0].height + self.controlsHeight + 1.5 * self.margin,
width: contentWidth - 2 * self.margin,
height: 64
};
2011-04-22 22:03:10 +00:00
size.timeline[1] = {
left: size.timeline[0].left,
top: size.timeline[0].top + size.timeline[0].height + self.margin,
width: size.timeline[0].width
};
2011-04-22 22:03:10 +00:00
lines = Math.ceil(self.options.duration / size.timeline[1].width);
height = getHeight();
self.$editor.css({
overflowY: (scrollbarIsVisible && height <= self.options.height - 16) ? 'scroll' : 'auto'
2011-04-22 22:03:10 +00:00
});
return (!scrollbarIsVisible && height > self.options.height - 16) ? getSizes(true) : size;
2011-04-22 22:03:10 +00:00
}
function getSubtitles() {
return self.options.enableSubtitles
? self.options.subtitles.filter(function(v) {
return Ox.contains(v.tracks, self.options.subtitlesTrack);
})
: [];
}
2018-08-07 16:12:22 +00:00
function getSubtitlesFind() {
return Ox.contains(
[self.options.subtitlesLayer, '*'], self.options.findLayer
) ? self.options.find : '';
}
function getWords() {
var words = [];
Ox.forEach(Ox.count(Ox.words(
2012-02-04 11:44:19 +00:00
self.annotations.map(function(annotation) {
return Ox.decodeHTMLEntities(
Ox.stripTags(annotation.value.toLowerCase())
);
2012-02-04 11:44:19 +00:00
}).join(' ')
)), function(count, value) {
words.push({count: count, value: value});
})
return words.sort(function(a, b) {
return b.count - a.count;
});
}
2011-04-22 22:03:10 +00:00
function goToPoint(point) {
2011-05-19 10:18:39 +00:00
setPosition(self.options[point]);
2011-04-22 22:03:10 +00:00
}
function isEditable() {
var annotation = Ox.getObjectById(self.annotations, self.options.selected);
return annotation && annotation.editable;
}
2011-04-22 22:03:10 +00:00
function movePositionBy(sec) {
2011-05-19 10:18:39 +00:00
setPosition(Ox.limit(self.options.position + sec, 0, self.options.duration));
2011-04-22 22:03:10 +00:00
}
function movePositionTo(type, direction) {
2011-05-19 10:18:39 +00:00
setPosition(getNextPosition(type, direction));
2011-04-22 22:03:10 +00:00
}
function parseSubtitles() {
return self.options.subtitlesLayer ? self.options.layers.filter(function(layer) {
return layer.id == self.options.subtitlesLayer;
})[0].items.map(function(subtitle) {
return {
id: subtitle.id,
'in': subtitle['in'],
out: subtitle.out,
text: subtitle.value.replace(/\n/g, ' ').replace(/<br\/?>/g, '\n'),
tracks: subtitle.languages || [self.options.subtitlesDefaultTrack]
};
}) : [];
}
2011-04-22 22:03:10 +00:00
function playInToOut() {
self.$player[0].playInToOut();
}
function removeAnnotation(data) {
var layer = Ox.getObjectById(self.options.layers, data.layer),
index = Ox.getIndexById(layer.items, data.id);
// deselect event will have fired before
self.options.selected = data.id;
updateWords('remove');
self.options.selected = '';
layer.items.splice(index, 1);
self.annotations = getAnnotations();
self.positions = getPositions();
2012-02-04 11:44:19 +00:00
self.options.find && submitFindInput(self.options.find);
self.editing = false;
if (data.layer == self.options.subtitlesLayer) {
updateSubtitles();
}
setTimelineState();
self.$annotationPanel.removeItem();
that.triggerEvent('removeannotation', data);
}
function resizeAnnotations(data) {
self.options.annotationsSize = data.size;
2011-04-22 22:03:10 +00:00
setSizes();
2012-01-12 19:04:32 +00:00
self.$annotationPanel.options({width: data.size});
2011-04-22 22:03:10 +00:00
}
function resizeendAnnotations(data) {
2012-01-12 19:04:32 +00:00
that.triggerEvent('annotationssize', {size: data.size});
2011-04-22 22:03:10 +00:00
}
2012-02-04 11:44:19 +00:00
function selectAnnotation(data, stayAtPosition) {
2012-01-10 14:49:28 +00:00
if (Ox.isUndefined(data)) {
2012-01-12 10:39:05 +00:00
// doubleclick on small timeline
2012-01-10 14:49:28 +00:00
data = getAnnotation();
2014-09-25 16:47:29 +00:00
} else if (!data.id && Ox.$elements[that.oxid]) {
2013-03-02 08:07:02 +00:00
// focus only if in the dom
2012-01-12 19:04:32 +00:00
that.gainFocus();
2012-01-10 14:49:28 +00:00
}
// FIXME
// self.editing = false;
2012-02-04 11:44:19 +00:00
if (data.id) {
if (!stayAtPosition || (
self.options.annotationsRange != 'position' && (
self.options.position < data['in']
|| self.options.position > data.out
)
)
2012-02-04 11:44:19 +00:00
) {
setPosition(data['in']);
// if annotationsRange is 'position',
// this may cause a deselect
}
if (!self.editing) {
setPoint('in', data['in'], true);
setPoint('out', data.out, true);
}
}
if (!self.editing) {
self.options.selected = data.id;
self.$annotationPanel.options({selected: self.options.selected});
setTimelineState();
that.triggerEvent('select', {id: self.options.selected});
}
2011-04-22 22:03:10 +00:00
}
2012-01-10 14:49:28 +00:00
function selectCut() {
var points = {
'in': Ox.last(self.options.cuts),
out: self.options.duration
};
Ox.forEach(self.options.cuts, function(cut, i) {
if (cut > self.options.position) {
points = {
2013-07-18 13:55:57 +00:00
'in': i == 0 ? 0 : self.options.cuts[i - 1],
2012-01-10 14:49:28 +00:00
out: cut - 1 / self.options.fps
};
2012-07-05 08:58:08 +00:00
return false; // break
}
2012-01-10 14:49:28 +00:00
});
setPoint('in', points['in']);
setPoint('out', points.out);
2011-04-22 22:03:10 +00:00
}
2012-01-15 15:05:17 +00:00
function setPoint(point, position, keepSelected) {
2011-05-19 10:18:39 +00:00
self.options[point] = position;
2012-01-15 15:05:17 +00:00
if (self.options.selected && !self.editing && !keepSelected) {
selectAnnotation({id: ''});
}
2011-05-18 18:30:58 +00:00
self.$player.forEach(function($player) {
$player.options(point, self.options[point]);
});
2011-05-18 16:00:29 +00:00
self.$player[point == 'in' ? 1 : 2].options({
position: self.options[point]
2011-04-22 22:03:10 +00:00
});
2011-05-18 16:00:29 +00:00
self.$timeline.forEach(function($timeline) {
$timeline.options(point, self.options[point]);
2011-04-22 22:03:10 +00:00
});
2011-05-18 16:00:29 +00:00
if (self.options['in'] > self.options.out) {
2012-01-15 15:05:17 +00:00
setPoint(point == 'in' ? 'out' : 'in', position, keepSelected);
} else {
self.$annotationPanel.options({
'in': self.options['in'],
out: self.options.out
});
that.triggerEvent('points', {
2012-01-09 20:25:38 +00:00
'in': self.options['in'],
out: self.options.out,
position: self.options.position
2012-01-09 20:25:38 +00:00
});
2018-03-31 08:30:15 +00:00
if (self.editing && self.options.selected.length && self.options.selected[0] != '_') {
that.triggerEvent('editannotation', {
id: self.options.selected,
'in': self.options['in'],
out: self.options.out,
value: $('.OxEditableElement input:visible').val()
});
}
2012-01-09 20:25:38 +00:00
}
2011-04-22 22:03:10 +00:00
}
function setPosition(position, playing, dragging) {
var minute = Math.floor(position / 60),
previousMinute = Math.floor(self.options.position / 60);
2011-05-19 10:18:39 +00:00
self.options.position = position;
2013-08-01 08:42:14 +00:00
!playing && self.$player[0].options({position: self.options.position});
self.$timeline.forEach(function($timeline) {
2013-08-01 08:42:14 +00:00
$timeline.options({position: self.options.position});
2011-04-22 22:03:10 +00:00
});
2013-08-01 08:42:14 +00:00
self.$annotationPanel.options({position: self.options.position});
if ((!playing || minute != previousMinute) && !dragging) {
that.triggerEvent(playing ? 'playing' : 'position', {
position: !playing ? self.options.position : minute * 60
});
}
2011-04-22 22:03:10 +00:00
}
function setSizes() {
self.sizes = getSizes();
2011-05-18 16:00:29 +00:00
self.$player.forEach(function($player, i) {
$player.options({
2011-04-22 22:03:10 +00:00
height: self.sizes.player[i].height,
width: self.sizes.player[i].width
})
.css({
left: self.sizes.player[i].left + 'px',
top: self.sizes.player[i].top + 'px'
});
});
2011-05-18 16:00:29 +00:00
self.$timeline.forEach(function($timeline, i) {
$timeline.options({
2011-04-22 22:03:10 +00:00
width: self.sizes.timeline[i].width
})
.css({
left: self.sizes.timeline[i].left + 'px',
top: self.sizes.timeline[i].top + 'px'
});
});
}
function setSubtitlesTrack() {
var enableSubtitles = self.options.subtitlesTrack != '',
subtitles,
toggleSubtitles = enableSubtitles != self.options.enableSubtitles;
self.options.enableSubtitles = enableSubtitles;
if (toggleSubtitles) {
self.$player.forEach(function($player) {
$player.options({
enableSubtitles: self.options.enableSubtitles
});
});
that.triggerEvent('subtitles', {
subtitles: self.options.enableSubtitles
});
} else {
self.$player.forEach(function($player) {
$player.options({
subtitlesTrack: self.options.subtitlesTrack
});
});
}
self.$timeline.forEach(function($timeline) {
$timeline.options({subtitles: getSubtitles()});
});
}
function showKeyboardShortcuts() {
var dialog = Ox.Dialog({
buttons: [
2013-05-09 13:03:33 +00:00
Ox.Button({id: 'close', title: Ox._('Close')})
.bindEvent({click: function() { dialog.close(); }})
],
content: self.$keyboardShortcuts,
height: 384,
keys: {enter: 'close', escape: 'close'},
2013-05-09 13:03:33 +00:00
title: Ox._('Keyboard Shortcuts'),
width: 256
}).open();
}
2012-01-10 14:49:28 +00:00
function setTimelineState() {
2012-01-09 20:25:38 +00:00
self.$timeline[1].options({
state: self.editing ? 'editing'
: isEditable() ? 'editable'
2012-01-09 20:25:38 +00:00
: self.options.selected ? 'selected'
: 'default'
});
}
function sortAnnotations(a, b) {
var ret = 0;
if (a['in'] < b['in']) {
ret = -1;
} else if (a['in'] > b['in']) {
ret = 1;
} else if (a.out < b.out) {
ret = -1;
} else if (a.out > b.out) {
ret = 1;
} else if (a.value < b.value) {
ret = -1;
} else if (a.value > b.value) {
ret = 1;
}
return ret;
}
2012-01-10 14:49:28 +00:00
function submitAnnotation(data) {
self.annotations = getAnnotations();
self.positions = getPositions();
updateWords('add');
2012-02-04 11:44:19 +00:00
self.options.find && submitFindInput(self.options.find);
2012-01-10 14:49:28 +00:00
self.editing = false;
if (data.layer == self.options.subtitlesLayer) {
updateSubtitles();
}
2012-01-10 14:49:28 +00:00
setTimelineState();
2012-01-15 15:05:17 +00:00
if (
self.options.annotationsRange == 'position'
&& (
2012-01-15 15:05:17 +00:00
self.options.position < self.options['in']
|| self.options.position > self.options.out
)
) {
setPosition(self.options['in']);
}
2012-01-10 14:49:28 +00:00
data['in'] = self.options['in'];
data.out = self.options.out;
that.triggerEvent('editannotation', data);
}
function submitFindInput(value, hasPressedEnter) {
self.options.find = value;
self.results = find(self.options.find);
2011-12-23 06:08:41 +00:00
self.$results
.css({opacity: self.results.length ? 1 : 0.25})
.html(self.results.length);
self.$previousButton.options({
disabled: !self.results.length
});
self.$nextButton.options({
disabled: !self.results.length
});
self.$clearButton.options({
disabled: !self.options.find
});
2018-08-07 16:12:22 +00:00
2011-12-23 06:08:41 +00:00
self.$player.forEach(function($player) {
2018-08-07 16:12:22 +00:00
$player.options({find: getSubtitlesFind()});
});
2011-12-23 06:08:41 +00:00
self.$timeline.forEach(function($timeline) {
2018-08-07 16:12:22 +00:00
$timeline.options({find: getSubtitlesFind()});
});
2011-12-23 06:08:41 +00:00
self.$timeline[1].options({results: self.results});
if (hasPressedEnter) {
that.triggerEvent('find', {find: self.options.find});
if (self.results.length) {
selectAnnotation(getNextAnnotation('result', 1));
that.gainFocus();
} else {
2011-12-18 09:44:11 +00:00
self.$findInput.focusInput(true);
}
}
2018-08-07 16:12:22 +00:00
self.$annotationPanel.options({
highlight: self.options.find,
highlightLayer: self.options.findLayer,
});
}
function toggleAnnotations(data) {
2011-04-22 22:03:10 +00:00
self.options.showAnnotations = !data.collapsed;
setSizes();
2011-08-17 19:34:34 +00:00
that.triggerEvent('toggleannotations', {
showAnnotations: self.options.showAnnotations
});
2011-04-22 22:03:10 +00:00
}
function toggleLoop() {
self.options.loop = !self.options.loop;
self.$menuButton[
self.options.loop ? 'checkItem' : 'uncheckItem'
]('loop');
self.$player[0].toggleLoop();
}
2011-05-18 16:00:29 +00:00
function toggleMuted() {
self.$player[0].toggleMuted();
2011-04-22 22:03:10 +00:00
}
2011-05-18 16:00:29 +00:00
function togglePaused() {
self.$player[0].togglePaused();
self.$player[0].options('paused') && that.triggerEvent('position', {
2012-01-11 11:54:58 +00:00
position: self.$player[0].options('position')
});
2011-04-22 22:03:10 +00:00
}
function toggleSize() {
self.options.videoSize = self.options.videoSize == 'small'
? 'large' : 'small';
2011-04-22 22:03:10 +00:00
setSizes();
self.$menuButton[
self.options.videoSize == 'small' ? 'uncheckItem' : 'checkItem'
]('size');
self.$player[0].options({
sizeIsLarge: self.options.videoSize == 'large'
});
2011-04-22 22:03:10 +00:00
that.triggerEvent('togglesize', {
size: self.options.videoSize
});
}
function updateSubtitles() {
2014-09-13 21:59:55 +00:00
// FIXME: missing: re-render subtitles submenu
self.options.subtitles = parseSubtitles();
self.$player.forEach(function($player) {
2013-10-22 17:43:38 +00:00
$player.options({subtitles: Ox.clone(self.options.subtitles, true)});
2013-10-22 15:58:47 +00:00
});
2013-10-22 17:43:38 +00:00
if (self.options.enableSubtitles) {
self.$timeline.forEach(function($timeline) {
$timeline.options({subtitles: getSubtitles()});
2013-10-22 17:43:38 +00:00
});
}
}
2012-06-15 13:20:07 +00:00
function updateTimelines() {
self.$timeline.forEach(function($timeline) {
$timeline.options({type: self.options.timeline});
});
2012-06-15 13:20:07 +00:00
}
function updateWords(action) {
// action can be 'add' or 'remove'
var words = [];
Ox.forEach(Ox.count(Ox.words(
getAnnotationValue(self.options.selected) || ''
)), function(count, value) {
words.push({count: count, value: value});
});
words.forEach(function(word) {
2012-05-21 06:41:57 +00:00
var index = Ox.indexOf(self.words, function(w) {
return w.value === word.value;
});
if (action == 'add') {
if (index == -1) {
self.words.push({count: 1, value: word.value});
} else {
self.words[index].count++;
}
} else if (index > -1) {
// index is -1 when removing an annotation by editing
// (which removes the words) and clearing its value
if (self.words[index].count == 1) {
self.words.splice(index, 1);
} else {
self.words[index].count--;
}
}
});
self.words.sort(function(a, b) {
return b.count - a.count;
});
self.$findInput.options({
autocomplete: self.words.map(function(word) {
return word.value;
})
});
}
2011-09-14 14:34:33 +00:00
/*@
addAnnotation <f> add annotation
(layer, item) -> <o> add annotation to layer
layer <s> layer id
2012-01-12 10:39:05 +00:00
annotation <o> annotation to add
2011-09-14 14:34:33 +00:00
@*/
2012-01-12 10:39:05 +00:00
that.addAnnotation = function(layer, annotation) {
2012-01-15 15:05:17 +00:00
// called from addannotation callback
2012-01-12 10:39:05 +00:00
self.$annotationPanel.addItem(layer, annotation);
};
2011-04-22 22:03:10 +00:00
that.getCurrentAnnotations = function() {
return self.$annotationPanel.getCurrentAnnotations();
};
2012-05-21 10:38:18 +00:00
/*@
updateAnnotation <f> updateAnnotation
(id, annotation) -> <o> update annotation with id
@*/
that.updateAnnotation = function(id, annotation) {
2012-01-15 15:05:17 +00:00
// called from editannotation callback
2018-03-31 08:30:15 +00:00
// id might have changed if new annotation was created
if (annotation.id) {
self.options.selected = annotation.id;
}
2013-10-22 17:43:38 +00:00
if (getSelectedLayer() == self.options.subtitlesLayer) {
updateSubtitles();
}
self.$annotationPanel.updateItem(id, annotation);
if (id != annotation.id) {
self.annotations = getAnnotations();
setTimelineState();
}
};
2012-01-15 15:05:17 +00:00
2011-09-14 14:34:33 +00:00
/*@
2012-01-03 10:26:15 +00:00
removeAnnotation <f> remove annotation
2011-09-14 14:34:33 +00:00
(layer, ids) -> <o> remove annotation from layer
layer <s> layer id
ids <a> array of item ids to remove
@*/
2012-01-12 10:39:05 +00:00
/*
2012-01-03 10:26:15 +00:00
that.removeAnnotation = function(layer, id) {
var i = Ox.getIndexById(self.options.layers, layer);
2012-01-03 10:26:15 +00:00
self.$annotationPanel[i].removeItem(id);
};
2012-01-12 10:39:05 +00:00
*/
2011-04-22 22:03:10 +00:00
return that;
};