better filesystem structure for modules and themes; 'minified' ui if debug option not set; dynamially generated map markers
This commit is contained in:
parent
358ee1bc96
commit
4489e88f44
596 changed files with 115093 additions and 17682 deletions
118
source/Ox.UI/js/Video/Ox.AnnotationPanel.js
Normal file
118
source/Ox.UI/js/Video/Ox.AnnotationPanel.js
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.AnnotationPanel = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
id: '',
|
||||
items: [],
|
||||
title: '',
|
||||
type: 'text',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {});
|
||||
|
||||
self.selected = -1;
|
||||
|
||||
that.$element = new Ox.CollapsePanel({
|
||||
collapsed: false,
|
||||
extras: [
|
||||
new Ox.Button({
|
||||
id: 'add',
|
||||
style: 'symbol',
|
||||
title: 'Add',
|
||||
type: 'image'
|
||||
}).bindEvent({
|
||||
click: function(event, data) {
|
||||
that.triggerEvent('add', {value: ''});
|
||||
}
|
||||
})
|
||||
],
|
||||
size: 16,
|
||||
title: self.options.title
|
||||
})
|
||||
.addClass('OxAnnotationPanel')
|
||||
.bindEvent({
|
||||
toggle: togglePanel
|
||||
});
|
||||
that.$content = that.$element.$content;
|
||||
|
||||
self.$annotations = new Ox.List({
|
||||
construct: function(data) {
|
||||
return new Ox.Element('div')
|
||||
.addClass('OxAnnotation OxEditable OxTarget')
|
||||
.html(Ox.parseHTML(data.value));
|
||||
},
|
||||
items: $.map(self.options.items, function(v, i) {
|
||||
return {
|
||||
id: v.id || i + '',
|
||||
value: v.value
|
||||
};
|
||||
}),
|
||||
unique: 'id'
|
||||
})
|
||||
.bindEvent({
|
||||
open: function(event, data) {
|
||||
if (data.ids.length == 1) {
|
||||
var pos = Ox.getPositionById(self.$annotations.options('items'), data.ids[0]);
|
||||
self.$annotations.editItem(pos);
|
||||
|
||||
}
|
||||
},
|
||||
'delete': function(event, data) {
|
||||
that.triggerEvent('delete', data);
|
||||
},
|
||||
select: selectAnnotation,
|
||||
submit: updateAnnotation
|
||||
})
|
||||
.appendTo(that.$content);
|
||||
|
||||
/*
|
||||
self.$annotations = new Ox.Element('div')
|
||||
.appendTo(that.$content);
|
||||
self.$annotation = [];
|
||||
self.options.items.forEach(function(item, i) {
|
||||
self.$annotation[i] = new Ox.Element('div')
|
||||
.addClass('OxAnnotation')
|
||||
.html(item.value.replace(/\n/g, '<br/>'))
|
||||
.click(function() {
|
||||
clickAnnotation(i);
|
||||
})
|
||||
.appendTo(self.$annotations);
|
||||
});
|
||||
*/
|
||||
function selectAnnotation(event, data) {
|
||||
var item = Ox.getObjectById(self.options.items, data.ids[0]);
|
||||
that.triggerEvent('select', {
|
||||
'in': item['in'],
|
||||
'out': item.out,
|
||||
'layer': self.options.id
|
||||
});
|
||||
}
|
||||
function updateAnnotation(event, data) {
|
||||
var item = Ox.getObjectById(self.options.items, data.id);
|
||||
item.value = data.value;
|
||||
that.triggerEvent('submit', item);
|
||||
}
|
||||
|
||||
function togglePanel() {
|
||||
|
||||
}
|
||||
|
||||
that.addItem = function(item) {
|
||||
var pos = 0;
|
||||
self.options.items.splice(pos, 0, item);
|
||||
self.$annotations.addItems(pos, [item]);
|
||||
self.$annotations.editItem(pos);
|
||||
}
|
||||
|
||||
that.removeItems = function(ids) {
|
||||
self.$annotations.removeItems(ids);
|
||||
}
|
||||
that.deselectItems = function() {
|
||||
if(self.$annotations.options('selected'))
|
||||
self.$annotations.options('selected',[]);
|
||||
}
|
||||
return that;
|
||||
|
||||
};
|
||||
364
source/Ox.UI/js/Video/Ox.BlockTimeline.js
Normal file
364
source/Ox.UI/js/Video/Ox.BlockTimeline.js
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.BlockTimeline = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
cuts: [],
|
||||
duration: 0,
|
||||
find: '',
|
||||
matches: [],
|
||||
points: [0, 0],
|
||||
position: 0,
|
||||
subtitles: [],
|
||||
videoId: '',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.addClass('OxTimelineSmall')
|
||||
.mousedown(mousedown)
|
||||
.mouseleave(mouseleave)
|
||||
.mousemove(mousemove)
|
||||
.bindEvent({
|
||||
drag: function(event, e) {
|
||||
mousedown(e);
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(self, {
|
||||
$images: [],
|
||||
$lines: [],
|
||||
$markerPoint: [],
|
||||
$selection: [],
|
||||
$subtitles: [],
|
||||
$tooltip: new Ox.Tooltip({
|
||||
animate: false
|
||||
}).css({
|
||||
textAlign: 'center'
|
||||
}),
|
||||
hasSubtitles: self.options.subtitles.length,
|
||||
height: 16,
|
||||
lines: Math.ceil(self.options.duration / self.options.width),
|
||||
margin: 8
|
||||
});
|
||||
|
||||
that.css({
|
||||
width: (self.options.width + self.margin) + 'px',
|
||||
height: ((self.height + self.margin) * self.lines + 4) + 'px'
|
||||
});
|
||||
|
||||
getTimelineImageURL(function(url) {
|
||||
|
||||
self.timelineImageURL = url;
|
||||
|
||||
Ox.range(0, self.lines).forEach(function(i) {
|
||||
addLine(i);
|
||||
});
|
||||
|
||||
self.$markerPosition = $('<img>')
|
||||
.addClass('OxMarkerPosition')
|
||||
.attr({
|
||||
src: Ox.PATH + 'png/Ox.UI/videoMarkerPlay.png'
|
||||
})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
width: '9px',
|
||||
height: '5px',
|
||||
zIndex: 10
|
||||
})
|
||||
.appendTo(that.$element);
|
||||
|
||||
setPosition();
|
||||
|
||||
['in', 'out'].forEach(function(v, i) {
|
||||
var titleCase = Ox.toTitleCase(v);
|
||||
self.$markerPoint[i] = $('<img>')
|
||||
.addClass('OxMarkerPoint' + titleCase)
|
||||
.attr({
|
||||
src: Ox.PATH + 'png/Ox.UI/videoMarker' + titleCase + '.png'
|
||||
})
|
||||
.appendTo(that.$element);
|
||||
setMarkerPoint(i);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function addLine(i) {
|
||||
// fixme: get URLs once, not once for every line
|
||||
self.$lines[i] = new Ox.Element('div')
|
||||
.css({
|
||||
top: i * (self.height + self.margin) + 'px',
|
||||
width: self.options.width + 'px'
|
||||
})
|
||||
.appendTo(that);
|
||||
self.$images[i] = $('<img>')
|
||||
.addClass('OxTimelineSmallImage')
|
||||
.attr({
|
||||
src: self.timelineImageURL
|
||||
})
|
||||
.css({
|
||||
marginLeft: (-i * self.options.width) + 'px'
|
||||
})
|
||||
.appendTo(self.$lines[i].$element)
|
||||
if (self.hasSubtitles) {
|
||||
self.subtitlesImageURL = getSubtitlesImageURL();
|
||||
self.$subtitles[i] = $('<img>')
|
||||
.addClass('OxTimelineSmallSubtitles')
|
||||
.attr({
|
||||
src: self.subtitlesImageURL
|
||||
})
|
||||
.css({
|
||||
marginLeft: (-i * self.options.width) + 'px'
|
||||
})
|
||||
.appendTo(self.$lines[i].$element);
|
||||
}
|
||||
if (self.options.points[0] != self.options.points[1]) {
|
||||
addSelection[i];
|
||||
}
|
||||
}
|
||||
|
||||
function addSelection(i) {
|
||||
self.selectionImageURL = getSelectionImageURL();
|
||||
self.$selection[i] && self.$selection[i].remove();
|
||||
self.$selection[i] = $('<img>')
|
||||
.addClass('OxTimelineSmallSelection')
|
||||
.attr({
|
||||
src: self.selectionImageURL
|
||||
})
|
||||
.css({
|
||||
marginLeft: (-i * self.options.width) + 'px'
|
||||
})
|
||||
.appendTo(self.$lines[i].$element);
|
||||
}
|
||||
|
||||
function getPosition(e) {
|
||||
//FIXME: this might still be broken in opera according to http://acko.net/blog/mouse-handling-and-absolute-positions-in-javascript
|
||||
return (e.offsetX ? e.offsetX : e.clientX - $(e.target).offset().left);
|
||||
}
|
||||
|
||||
function getSelectionImageURL() {
|
||||
var height = 18,
|
||||
width = Math.ceil(self.options.duration),
|
||||
$canvas = $('<canvas>')
|
||||
.attr({
|
||||
height: height,
|
||||
width: width
|
||||
}),
|
||||
canvas = $canvas[0],
|
||||
context = canvas.getContext('2d'),
|
||||
imageData = context.createImageData(width, height),
|
||||
data = imageData.data,
|
||||
points = $.map(self.options.points, function(v, i) {
|
||||
return Math.round(v) + i;
|
||||
}),
|
||||
top = 0,
|
||||
bottom = 18;
|
||||
Ox.range(points[0], points[1]).forEach(function(x) {
|
||||
Ox.range(top, bottom).forEach(function(y) {
|
||||
var color = (y == top || y == bottom - 1) ? [255, 255, 255, 255] : [255, 255, 255, 64],
|
||||
index = x * 4 + y * 4 * width;
|
||||
data[index] = color[0];
|
||||
data[index + 1] = color[1];
|
||||
data[index + 2] = color[2];
|
||||
data[index + 3] = color[3]
|
||||
});
|
||||
});
|
||||
context.putImageData(imageData, 0, 0);
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
|
||||
function getSubtitle(position) {
|
||||
var subtitle = null;
|
||||
Ox.forEach(self.options.subtitles, function(v) {
|
||||
if (v['in'] <= position && v['out'] >= position) {
|
||||
subtitle = v;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
function getSubtitlesImageURL() {
|
||||
var height = 18,
|
||||
width = Math.ceil(self.options.duration),
|
||||
$canvas = $('<canvas>')
|
||||
.attr({
|
||||
height: height,
|
||||
width: width
|
||||
}),
|
||||
canvas = $canvas[0],
|
||||
context = canvas.getContext('2d'),
|
||||
imageData = context.createImageData(width, height),
|
||||
data = imageData.data;
|
||||
self.options.subtitles.forEach(function(v) {
|
||||
//var color = self.options.matches.indexOf(i) > -1 ? [255, 255, 0] : [255, 255, 255]
|
||||
var inPoint = Math.round(v['in']),
|
||||
outPoint = Math.round(v.out) + 1,
|
||||
lines = v.value.split('\n').length,
|
||||
bottom = 15,
|
||||
top = bottom - lines - 2;
|
||||
Ox.range(inPoint, outPoint).forEach(function(x) {
|
||||
Ox.range(top, bottom).forEach(function(y) {
|
||||
var color = (y == top || y == bottom - 1) ? [0, 0, 0] : [255, 255, 255],
|
||||
index = x * 4 + y * 4 * width;
|
||||
data[index] = color[0];
|
||||
data[index + 1] = color[1];
|
||||
data[index + 2] = color[2];
|
||||
data[index + 3] = 128
|
||||
});
|
||||
});
|
||||
});
|
||||
context.putImageData(imageData, 0, 0);
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
|
||||
function getTimelineImageURL(callback) {
|
||||
var height = 16,
|
||||
images = Math.ceil(self.options.duration / 3600),
|
||||
loaded = 0,
|
||||
width = Math.ceil(self.options.duration),
|
||||
$canvas = $('<canvas>')
|
||||
.attr({
|
||||
height: height,
|
||||
width: width
|
||||
}),
|
||||
canvas = $canvas[0],
|
||||
context = canvas.getContext('2d');
|
||||
Ox.range(images).forEach(function(i) {
|
||||
var $img = $('<img>')
|
||||
.attr({
|
||||
src: '/' + self.options.videoId + '/timelines/timeline.16.' + i + '.png'
|
||||
})
|
||||
.load(function() {
|
||||
context.drawImage($img[0], i * 3600, 0);
|
||||
//Ox.print('loaded, images', loaded, images, $img[0])
|
||||
if (++loaded == images) {
|
||||
//Ox.print('callback', canvas.toDataURL().length)
|
||||
callback(canvas.toDataURL());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mousedown(e) {
|
||||
var $target = $(e.target);
|
||||
if (
|
||||
$target.hasClass('OxTimelineSmallImage') ||
|
||||
$target.hasClass('OxTimelineSmallSubtitles') ||
|
||||
$target.hasClass('OxTimelineSmallSelection')
|
||||
) {
|
||||
self.options.position = getPosition(e);
|
||||
setPosition();
|
||||
that.triggerEvent('change', {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function mouseleave(e) {
|
||||
self.$tooltip.hide();
|
||||
}
|
||||
|
||||
function mousemove(e) {
|
||||
var $target = $(e.target),
|
||||
position,
|
||||
subtitle;
|
||||
if (
|
||||
$target.hasClass('OxTimelineSmallImage') ||
|
||||
$target.hasClass('OxTimelineSmallSubtitles') ||
|
||||
$target.hasClass('OxTimelineSmallSelection')
|
||||
) {
|
||||
position = getPosition(e),
|
||||
subtitle = getSubtitle(position);
|
||||
self.$tooltip.options({
|
||||
title: subtitle ?
|
||||
'<span class=\'OxBright\'>' +
|
||||
Ox.highlight(subtitle.value, self.options.find).replace(/\n/g, '<br/>') + '</span><br/>' +
|
||||
Ox.formatDuration(subtitle['in'], 3) + ' - ' + Ox.formatDuration(subtitle['out'], 3) :
|
||||
Ox.formatDuration(position, 3)
|
||||
})
|
||||
.show(e.clientX, e.clientY);
|
||||
} else {
|
||||
self.$tooltip.hide();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function setMarker() {
|
||||
self.$markerPosition
|
||||
.css({
|
||||
left: (self.options.position % self.options.width) + 'px',
|
||||
top: (parseInt(self.options.position / self.options.width) * (self.height + self.margin) + 2) + 'px',
|
||||
});
|
||||
}
|
||||
|
||||
function setMarkerPoint(i) {
|
||||
var position = Math.round(self.options.points[i]);
|
||||
self.$markerPoint[i]
|
||||
.css({
|
||||
left: (position % self.options.width) + 'px',
|
||||
top: (parseInt(position / self.options.width) * (self.height + self.margin) + 16) + 'px',
|
||||
});
|
||||
}
|
||||
|
||||
function setPosition() {
|
||||
self.options.position = Ox.limit(self.options.position, 0, self.options.duration);
|
||||
setMarker();
|
||||
}
|
||||
|
||||
function setWidth() {
|
||||
self.lines = Math.ceil(self.options.duration / self.options.width);
|
||||
that.css({
|
||||
width: (self.options.width + self.margin) + 'px',
|
||||
height: ((self.height + self.margin) * self.lines + 4) + 'px'
|
||||
});
|
||||
Ox.range(self.lines).forEach(function(i) {
|
||||
if (self.$lines[i]) {
|
||||
self.$lines[i].css({
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
self.$images[i].css({
|
||||
marginLeft: (-i * self.options.width) + 'px'
|
||||
});
|
||||
if (self.hasSubtitles) {
|
||||
self.$subtitles[i].css({
|
||||
marginLeft: (-i * self.options.width) + 'px'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
addLine(i);
|
||||
}
|
||||
});
|
||||
while (self.$lines.length > self.lines) {
|
||||
self.$lines[self.$lines.length - 1].removeElement();
|
||||
self.$lines.pop();
|
||||
}
|
||||
setMarker();
|
||||
setMarkerPoint(0);
|
||||
setMarkerPoint(1);
|
||||
}
|
||||
|
||||
function updateSelection() {
|
||||
self.$lines.forEach(function($line, i) {
|
||||
addSelection(i);
|
||||
});
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
//Ox.print('onChange:', key, value)
|
||||
if (key == 'points') {
|
||||
//Ox.print('key', key, 'value', value)
|
||||
setMarkerPoint(0);
|
||||
setMarkerPoint(1);
|
||||
updateSelection()
|
||||
} else if (key == 'position') {
|
||||
setPosition();
|
||||
} else if (key == 'width') {
|
||||
setWidth();
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
183
source/Ox.UI/js/Video/Ox.FilesView.js
Normal file
183
source/Ox.UI/js/Video/Ox.FilesView.js
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
// fixme: this is not necessarily part of OxUI
|
||||
|
||||
/*
|
||||
============================================================================
|
||||
Pan.do/ra
|
||||
============================================================================
|
||||
*/
|
||||
|
||||
Ox.FilesView = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
id: ''
|
||||
})
|
||||
.options(options || {});
|
||||
|
||||
self.$toolbar = new Ox.Bar({
|
||||
size: 24
|
||||
});
|
||||
|
||||
self.$orderButton = new Ox.Button({
|
||||
title: 'Change Order of Users...'
|
||||
})
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '4px'
|
||||
})
|
||||
.appendTo(self.$toolbar);
|
||||
|
||||
self.$moveButton = new Ox.Button({
|
||||
disabled: 'true',
|
||||
title: 'Move Selected Files...'
|
||||
})
|
||||
.css({
|
||||
float: 'right',
|
||||
margin: '4px'
|
||||
})
|
||||
.appendTo(self.$toolbar);
|
||||
|
||||
self.$filesList = new Ox.TextList({
|
||||
columns: [
|
||||
{
|
||||
align: 'left',
|
||||
id: 'users',
|
||||
operator: '+',
|
||||
title: 'Users',
|
||||
visible: true,
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
id: 'folder',
|
||||
operator: '+',
|
||||
title: 'Folder',
|
||||
visible: true,
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
id: 'name',
|
||||
operator: '+',
|
||||
title: 'Name',
|
||||
visible: true,
|
||||
width: 360
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
id: 'type',
|
||||
operator: '+',
|
||||
title: 'Type',
|
||||
visible: true,
|
||||
width: 60
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
id: 'part',
|
||||
operator: '+',
|
||||
title: 'Part',
|
||||
visible: true,
|
||||
width: 60
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: {type: 'value', args: ['B']},
|
||||
id: 'size',
|
||||
operator: '-',
|
||||
title: 'Size',
|
||||
visible: true,
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: {type: 'resolution', args: ['px']},
|
||||
id: 'resolution',
|
||||
operator: '-',
|
||||
title: 'Resolution',
|
||||
visible: true,
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: {type: 'duration', args: [0, 'short']},
|
||||
id: 'duration',
|
||||
operator: '-',
|
||||
title: 'Duration',
|
||||
visible: true,
|
||||
width: 90
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
id: 'oshash',
|
||||
operator: '+',
|
||||
title: 'Hash',
|
||||
unique: true,
|
||||
visible: false,
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
align: 'left',
|
||||
id: 'instances',
|
||||
operator: '+',
|
||||
title: 'Instances',
|
||||
visible: false,
|
||||
width: 120
|
||||
}
|
||||
],
|
||||
columnsMovable: true,
|
||||
columnsRemovable: true,
|
||||
columnsResizable: true,
|
||||
columnsVisible: true,
|
||||
id: 'files',
|
||||
items: function(data, callback) {
|
||||
pandora.api.findFiles($.extend(data, {
|
||||
query: {
|
||||
conditions: [{
|
||||
key: 'id',
|
||||
value: self.options.id,
|
||||
operator: '='
|
||||
}]
|
||||
}
|
||||
}), callback);
|
||||
},
|
||||
scrollbarVisible: true,
|
||||
sort: [{key: 'name', operator:'+'}]
|
||||
})
|
||||
.bindEvent({
|
||||
open: openFiles,
|
||||
select: selectFiles
|
||||
});
|
||||
|
||||
self.$instancesList = new Ox.Element()
|
||||
.html('No files selected');
|
||||
|
||||
that.$element = new Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: self.$toolbar,
|
||||
size: 24
|
||||
},
|
||||
{
|
||||
element: self.$filesList
|
||||
},
|
||||
{
|
||||
element: self.$instancesList,
|
||||
size: 80
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
});
|
||||
|
||||
function openFiles(event, data) {
|
||||
//alert(JSON.stringify(self.$filesList.value(data.ids[0], 'instances')))
|
||||
}
|
||||
|
||||
function selectFiles(event, data) {
|
||||
//alert(JSON.stringify(self.$filesList.value(data.ids[0], 'instances')))
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
101
source/Ox.UI/js/Video/Ox.Flipbook.js
Normal file
101
source/Ox.UI/js/Video/Ox.Flipbook.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
// fixme: rename!
|
||||
|
||||
Ox.Flipbook = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
frame = $('<img>').css({
|
||||
'position': 'absolute',
|
||||
'width': '100%',
|
||||
'height': 'auto'
|
||||
})
|
||||
.hide(),
|
||||
icon = $('<img>').css({
|
||||
'position': 'absolute',
|
||||
'width': '100%',
|
||||
'height': 'auto'
|
||||
}),
|
||||
frames = {},
|
||||
timestamp = $('<div>').css({
|
||||
'position': 'absolute',
|
||||
'text-align': 'center',
|
||||
'width': '100%'
|
||||
})
|
||||
.hide(),
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
frames: {},
|
||||
duration: 0,
|
||||
icon: ''
|
||||
})
|
||||
.options(options || {})
|
||||
.append(icon)
|
||||
.append(frame)
|
||||
.append(timestamp)
|
||||
.mouseover(function() {
|
||||
frame.show();
|
||||
timestamp.show();
|
||||
icon.hide();
|
||||
})
|
||||
.mousemove(function(event) {
|
||||
var position = getPosition(event),
|
||||
image = getFrame(position),
|
||||
frameHeight = image?image.height:that.height();
|
||||
frame.attr('src', image.src);
|
||||
timestamp.html(Ox.formatDuration(position, 'short'));
|
||||
|
||||
var height = (that.height() - frameHeight)/2;
|
||||
frame.css({'top': height + 'px'});
|
||||
timestamp.css({'top': (frameHeight + height) + 'px'});
|
||||
})
|
||||
.mouseout(function() {
|
||||
frame.hide();
|
||||
timestamp.hide();
|
||||
icon.show();
|
||||
})
|
||||
.mousedown(function(event) {
|
||||
that.triggerEvent('click', {
|
||||
'position': getPosition(event)
|
||||
});
|
||||
});
|
||||
|
||||
function getPosition(event) {
|
||||
var position = Math.floor(event.clientX - that.offset().left);
|
||||
position = (position / that.width()) * self.options.duration;
|
||||
return position;
|
||||
}
|
||||
|
||||
function getFrame(position) {
|
||||
var frame;
|
||||
Ox.forEach(frames, function(img, i) {
|
||||
if (!frame || i <= position) {
|
||||
frame = img;
|
||||
}
|
||||
});
|
||||
return frame;
|
||||
}
|
||||
|
||||
function cacheFrames() {
|
||||
Ox.forEach(self.options.frames, function(src, i) {
|
||||
frames[i] = new Image();
|
||||
frames[i].onload = function() {
|
||||
frameHeight = frames[i].height / frames[i].width * that.width();
|
||||
};
|
||||
frames[i].src = src;
|
||||
});
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
if (key == 'frames') {
|
||||
cacheFrames();
|
||||
} else if (key == 'icon') {
|
||||
icon.attr('src', value);
|
||||
}
|
||||
};
|
||||
|
||||
if(options.icon) {
|
||||
icon.attr('src', options.icon);
|
||||
}
|
||||
cacheFrames();
|
||||
return that;
|
||||
};
|
||||
213
source/Ox.UI/js/Video/Ox.LargeTimeline.js
Normal file
213
source/Ox.UI/js/Video/Ox.LargeTimeline.js
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.LargeTimeline = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
cuts: [],
|
||||
duration: 0,
|
||||
find: '',
|
||||
matches: [],
|
||||
points: [0, 0],
|
||||
position: 0,
|
||||
style: 'default',
|
||||
subtitles: [],
|
||||
videoId: '',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.addClass('OxTimelineLarge')
|
||||
.mouseleave(mouseleave)
|
||||
.mousemove(mousemove)
|
||||
.bindEvent({
|
||||
anyclick: click,
|
||||
dragstart: dragstart,
|
||||
drag: drag
|
||||
});
|
||||
|
||||
$.extend(self, {
|
||||
$cuts: [],
|
||||
$markerPoint: [],
|
||||
$subtitles: [],
|
||||
$tiles: {},
|
||||
$tooltip: new Ox.Tooltip({
|
||||
animate: false
|
||||
}),
|
||||
center: parseInt(self.options.width / 2),
|
||||
element: that.$element[0],
|
||||
fps: 25,
|
||||
height: 64,
|
||||
tileWidth: 1500
|
||||
});
|
||||
self.tiles = self.options.duration * self.fps / self.tileWidth;
|
||||
|
||||
self.$timeline = $('<div>')
|
||||
.css({
|
||||
left: self.center + 'px'
|
||||
})
|
||||
.appendTo(that.$element)
|
||||
|
||||
self.options.subtitles.forEach(function(v, i) {
|
||||
self.$subtitles[i] = $('<div>')
|
||||
.addClass('OxSubtitle' + (self.options.matches.indexOf(i) > -1 ? ' OxHighlight' : ''))
|
||||
.css({
|
||||
left: (v['in'] * self.fps) + 'px',
|
||||
width: (((v['out'] - v['in']) * self.fps) - 2) + 'px'
|
||||
})
|
||||
.html(Ox.highlight(v.value, self.options.find))
|
||||
.appendTo(self.$timeline)
|
||||
});
|
||||
|
||||
self.options.cuts.forEach(function(v, i) {
|
||||
self.$cuts[i] = $('<img>')
|
||||
.addClass('OxCut')
|
||||
.attr({
|
||||
src: Ox.PATH + 'png/Ox.UI/videoMarkerCut.png'
|
||||
})
|
||||
.css({
|
||||
left: (v * self.fps) + 'px'
|
||||
})
|
||||
.appendTo(self.$timeline)
|
||||
});
|
||||
|
||||
self.$markerPosition = $('<img>')
|
||||
.addClass('OxMarkerPosition')
|
||||
.attr({
|
||||
src: Ox.PATH + 'png/Ox.UI/videoMarkerPlay.png'
|
||||
})
|
||||
.appendTo(that.$element);
|
||||
setMarker();
|
||||
|
||||
['In', 'Out'].forEach(function(v, i) {
|
||||
self.$markerPoint[i] = $('<img>')
|
||||
.addClass('OxMarkerPoint' + v)
|
||||
.attr({
|
||||
src: Ox.PATH + 'png/Ox.UI/videoMarker' + v + '.png'
|
||||
})
|
||||
.appendTo(self.$timeline);
|
||||
setMarkerPoint(i);
|
||||
});
|
||||
|
||||
setWidth();
|
||||
setPosition();
|
||||
|
||||
function click(event, e) {
|
||||
self.options.position = Ox.limit(
|
||||
getPosition(e), 0, self.options.duration
|
||||
);
|
||||
setPosition();
|
||||
triggerChangeEvent();
|
||||
}
|
||||
|
||||
function dragstart(event, e) {
|
||||
self.drag = {x: e.clientX};
|
||||
}
|
||||
|
||||
function drag(event, e) {
|
||||
self.options.position = Ox.limit(
|
||||
self.options.position + (self.drag.x - e.clientX) / self.fps,
|
||||
0, self.options.duration
|
||||
);
|
||||
self.drag.x = e.clientX;
|
||||
setPosition();
|
||||
triggerChangeEvent();
|
||||
}
|
||||
|
||||
function getPosition(e) {
|
||||
return self.options.position + (e.clientX - that.offset().left - self.center - 1) / self.fps
|
||||
}
|
||||
|
||||
function mouseleave(e) {
|
||||
self.clientX = 0;
|
||||
self.clientY = 0;
|
||||
self.$tooltip.hide();
|
||||
}
|
||||
|
||||
function mousemove(e) {
|
||||
self.clientX = e.clientX;
|
||||
self.clientY = e.clientY;
|
||||
updateTooltip();
|
||||
}
|
||||
|
||||
function setMarkerPoint(i) {
|
||||
self.$markerPoint[i].css({
|
||||
left: (self.options.points[i] * self.fps) + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
function setMarker() {
|
||||
self.$markerPosition.css({
|
||||
left: (self.center - 4) + 'px',
|
||||
});
|
||||
}
|
||||
|
||||
function setPosition() {
|
||||
self.tile = parseInt(self.options.position * self.fps / self.tileWidth);
|
||||
self.$timeline.css({
|
||||
marginLeft: (-self.options.position * self.fps) + 'px'
|
||||
});
|
||||
Ox.range(
|
||||
Math.max(self.tile - 1, 0), Math.min(self.tile + 2, self.tiles)
|
||||
).forEach(function(v) {
|
||||
if (!self.$tiles[v]) {
|
||||
self.$tiles[v] = $('<img>')
|
||||
.attr({
|
||||
src: '/' + self.options.videoId + '/timelines/' + (
|
||||
self.options.style == 'default' ? 'timeline' : self.options.style
|
||||
) + '.64.' + v + '.png'
|
||||
})
|
||||
.css({
|
||||
left: (v * self.tileWidth) + 'px'
|
||||
})
|
||||
.appendTo(self.$timeline);
|
||||
}
|
||||
});
|
||||
if (self.clientX && self.clientY) {
|
||||
updateTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
function setWidth() {
|
||||
self.center = parseInt(self.options.width / 2);
|
||||
that.css({
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
self.$timeline.css({
|
||||
left: self.center + 'px'
|
||||
});
|
||||
setMarker();
|
||||
}
|
||||
|
||||
function triggerChangeEvent() {
|
||||
that.triggerEvent('change', {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function updateTooltip() {
|
||||
var position = getPosition(self);
|
||||
if (position >= 0 && position <= self.options.duration) {
|
||||
self.$tooltip
|
||||
.options({
|
||||
title: Ox.formatDuration(position, 3)
|
||||
})
|
||||
.show(self.clientX, self.clientY);
|
||||
} else {
|
||||
self.$tooltip.hide();
|
||||
}
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
if (key == 'points') {
|
||||
setMarkerPoint(0);
|
||||
setMarkerPoint(1);
|
||||
} else if (key == 'position') {
|
||||
setPosition();
|
||||
} else if (key == 'width') {
|
||||
setWidth();
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
192
source/Ox.UI/js/Video/Ox.SmallTimeline.js
Normal file
192
source/Ox.UI/js/Video/Ox.SmallTimeline.js
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.SmallTimeline = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
duration: 0,
|
||||
find: '',
|
||||
matches: [],
|
||||
points: [0, 0],
|
||||
position: 0,
|
||||
subtitles: [],
|
||||
videoId: '',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.addClass('OxTimelineSmall')
|
||||
.mousedown(mousedown)
|
||||
.mouseleave(mouseleave)
|
||||
.mousemove(mousemove)
|
||||
.bindEvent({
|
||||
drag: function(event, e) {
|
||||
mousedown(e);
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(self, {
|
||||
$images: [],
|
||||
$markerPoint: [],
|
||||
$subtitles: [],
|
||||
$tooltip: new Ox.Tooltip({
|
||||
animate: false
|
||||
}).css({
|
||||
textAlign: 'center'
|
||||
}),
|
||||
hasSubtitles: self.options.subtitles.length,
|
||||
height: 16,
|
||||
margin: 8
|
||||
});
|
||||
|
||||
that.css({
|
||||
width: (self.options.width + self.margin) + 'px',
|
||||
height: (self.height + self.margin) + 'px'
|
||||
});
|
||||
|
||||
self.$line = $('<img>')
|
||||
.addClass('OxTimelineSmallImage')
|
||||
.attr({
|
||||
src: '/' + self.options.videoId + '/timelines/timeline.16.0.png'
|
||||
})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
left: '4px',
|
||||
top: '4px',
|
||||
width: self.options.width,
|
||||
height: '16px'
|
||||
})
|
||||
.appendTo(that.$element);
|
||||
|
||||
self.$markerPosition = $('<img>')
|
||||
.addClass('OxMarkerPosition')
|
||||
.attr({
|
||||
src: Ox.PATH + 'png/Ox.UI/videoMarkerPlay.png'
|
||||
})
|
||||
.css({
|
||||
position: 'absolute',
|
||||
width: '9px',
|
||||
height: '5px',
|
||||
zIndex: 10
|
||||
})
|
||||
.appendTo(that.$element);
|
||||
|
||||
setPosition();
|
||||
|
||||
['in', 'out'].forEach(function(v, i) {
|
||||
var titleCase = Ox.toTitleCase(v);
|
||||
self.$markerPoint[i] = $('<img>')
|
||||
.addClass('OxMarkerPoint' + titleCase)
|
||||
.attr({
|
||||
src: Ox.PATH + 'png/Ox.UI/videoMarker' + titleCase + '.png'
|
||||
})
|
||||
.appendTo(that.$element);
|
||||
setMarkerPoint(i);
|
||||
});
|
||||
|
||||
function getPosition(e) {
|
||||
return e.offsetX / self.options.width * self.options.duration;
|
||||
}
|
||||
|
||||
function getSubtitle(position) {
|
||||
var subtitle = null;
|
||||
Ox.forEach(self.options.subtitles, function(v) {
|
||||
if (v['in'] <= position && v['out'] >= position) {
|
||||
subtitle = v;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
function mousedown(e) {
|
||||
var $target = $(e.target);
|
||||
if (
|
||||
$target.hasClass('OxTimelineSmallImage') ||
|
||||
$target.hasClass('OxTimelineSmallSubtitles')
|
||||
) {
|
||||
self.options.position = getPosition(e);
|
||||
setPosition();
|
||||
that.triggerEvent('change', {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function mouseleave(e) {
|
||||
self.$tooltip.hide();
|
||||
}
|
||||
|
||||
function mousemove(e) {
|
||||
var $target = $(e.target),
|
||||
position,
|
||||
subtitle;
|
||||
if (
|
||||
$target.hasClass('OxTimelineSmallImage') ||
|
||||
$target.hasClass('OxTimelineSmallSubtitles')
|
||||
) {
|
||||
position = getPosition(e),
|
||||
subtitle = getSubtitle(position);
|
||||
self.$tooltip.options({
|
||||
title: subtitle ?
|
||||
'<span class=\'OxBright\'>' +
|
||||
Ox.highlight(subtitle.value, self.options.find).replace(/\n/g, '<br/>') + '</span><br/>' +
|
||||
Ox.formatDuration(subtitle['in'], 3) + ' - ' + Ox.formatDuration(subtitle['out'], 3) :
|
||||
Ox.formatDuration(position, 3)
|
||||
})
|
||||
.show(e.clientX, e.clientY);
|
||||
} else {
|
||||
self.$tooltip.hide();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function setMarker() {
|
||||
self.$markerPosition
|
||||
.css({
|
||||
left: parseInt(
|
||||
self.options.position / self.options.duration * self.options.width
|
||||
) + 'px',
|
||||
top: '2px',
|
||||
});
|
||||
}
|
||||
|
||||
function setMarkerPoint(i) {
|
||||
var position = self.options.points[i];
|
||||
self.$markerPoint[i]
|
||||
.css({
|
||||
left: (position % self.options.width) + 'px',
|
||||
top: (parseInt(position / self.options.width) * (self.height + self.margin) + 16) + 'px',
|
||||
});
|
||||
}
|
||||
|
||||
function setPosition() {
|
||||
self.options.position = Ox.limit(self.options.position, 0, self.options.duration);
|
||||
setMarker();
|
||||
}
|
||||
|
||||
function setWidth() {
|
||||
self.$line.css({
|
||||
width: self.options.width + 'px',
|
||||
});
|
||||
setMarker();
|
||||
setMarkerPoint(0);
|
||||
setMarkerPoint(1);
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
//Ox.print('onChange:', key, value)
|
||||
if (key == 'points') {
|
||||
//Ox.print('key', key, 'value', value)
|
||||
setMarkerPoint(0);
|
||||
setMarkerPoint(1);
|
||||
} else if (key == 'position') {
|
||||
setPosition();
|
||||
} else if (key == 'width') {
|
||||
setWidth();
|
||||
}
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
414
source/Ox.UI/js/Video/Ox.VideoEditorPlayer.js
Normal file
414
source/Ox.UI/js/Video/Ox.VideoEditorPlayer.js
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.VideoEditorPlayer = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
duration: 0,
|
||||
find: '',
|
||||
height: 0,
|
||||
points: [0, 0],
|
||||
position: 0,
|
||||
posterFrame: 0,
|
||||
size: 'small',
|
||||
subtitles: [],
|
||||
type: 'play',
|
||||
url: '',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.addClass('OxVideoPlayer')
|
||||
.css({
|
||||
height: (self.options.height + 16) + 'px',
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
|
||||
self.controlsHeight = 16;
|
||||
|
||||
if (self.options.type == 'play') {
|
||||
self.$video = new Ox.VideoElement({
|
||||
height: self.options.height,
|
||||
paused: true,
|
||||
points: self.options.points,
|
||||
position: self.options.position,
|
||||
url: self.options.url,
|
||||
width: self.options.width
|
||||
})
|
||||
.bindEvent({
|
||||
paused: paused,
|
||||
playing: playing
|
||||
})
|
||||
.appendTo(that);
|
||||
self.video = self.$video.$element[0];
|
||||
} else {
|
||||
self.$video = $('<img>')
|
||||
.css({
|
||||
height: self.options.height + 'px',
|
||||
width: self.options.width + 'px'
|
||||
})
|
||||
.appendTo(that.$element)
|
||||
}
|
||||
|
||||
self.$subtitle = $('<div>')
|
||||
.addClass('OxSubtitle')
|
||||
.appendTo(that.$element);
|
||||
|
||||
setSubtitleSize();
|
||||
|
||||
self.$markerFrame = $('<div>')
|
||||
.addClass('OxMarkerFrame')
|
||||
.append(
|
||||
$('<div>')
|
||||
.addClass('OxFrame')
|
||||
.css({
|
||||
width: Math.floor((self.options.width - self.options.height) / 2) + 'px',
|
||||
height: self.options.height + 'px'
|
||||
})
|
||||
)
|
||||
.append(
|
||||
$('<div>')
|
||||
.addClass('OxPoster')
|
||||
.css({
|
||||
width: (self.options.height - 2) + 'px',
|
||||
height: (self.options.height - 2) + 'px'
|
||||
})
|
||||
)
|
||||
.append(
|
||||
$('<div>')
|
||||
.addClass('OxFrame')
|
||||
.css({
|
||||
width: Math.ceil((self.options.width - self.options.height) / 2) + 'px',
|
||||
height: self.options.height + 'px'
|
||||
})
|
||||
)
|
||||
.hide()
|
||||
.appendTo(that.$element);
|
||||
|
||||
self.$markerPoint = {};
|
||||
['in', 'out'].forEach(function(point, i) {
|
||||
self.$markerPoint[point] = {};
|
||||
['top', 'bottom'].forEach(function(edge) {
|
||||
var titleCase = Ox.toTitleCase(point) + Ox.toTitleCase(edge);
|
||||
self.$markerPoint[point][edge] = $('<img>')
|
||||
.addClass('OxMarkerPoint OxMarker' + titleCase)
|
||||
.attr({
|
||||
src: Ox.PATH + 'png/Ox.UI/videoMarker' + titleCase + '.png'
|
||||
})
|
||||
.hide()
|
||||
.appendTo(that.$element);
|
||||
if (self.options.points[i] == self.options.position) {
|
||||
self.$markerPoint[point][edge].show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
self.$controls = new Ox.Bar({
|
||||
size: self.controlsHeight
|
||||
})
|
||||
.css({
|
||||
marginTop: '-2px'
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
if (self.options.type == 'play') {
|
||||
// fixme: $buttonPlay etc. ?
|
||||
self.$playButton = new Ox.Button({
|
||||
id: self.options.id + 'Play',
|
||||
title: [
|
||||
{id: 'play', title: 'play'},
|
||||
{id: 'pause', title: 'pause'}
|
||||
],
|
||||
tooltip: ['Play', 'Pause'],
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent('click', togglePlay)
|
||||
.appendTo(self.$controls);
|
||||
self.$playInToOutButton = new Ox.Button({
|
||||
id: self.options.id + 'PlayInToOut',
|
||||
title: 'PlayInToOut',
|
||||
tooltip: 'Play In to Out',
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent('click', function() {
|
||||
that.playInToOut();
|
||||
})
|
||||
.appendTo(self.$controls);
|
||||
self.$muteButton = new Ox.Button({
|
||||
id: self.options.id + 'Mute',
|
||||
title: [
|
||||
{id: 'mute', title: 'mute'},
|
||||
{id: 'unmute', title: 'unmute'}
|
||||
],
|
||||
tooltip: ['Mute', 'Unmute'],
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent('click', toggleMute)
|
||||
.appendTo(self.$controls);
|
||||
self.$sizeButton = new Ox.Button({
|
||||
id: self.options.id + 'Size',
|
||||
title: self.options.size == 'small' ? [
|
||||
{id: 'large', title: 'grow'},
|
||||
{id: 'small', title: 'shrink'}
|
||||
] : [
|
||||
{id: 'small', title: 'shrink'},
|
||||
{id: 'large', title: 'grow'}
|
||||
],
|
||||
tooltip: ['Larger', 'Smaller'],
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent('click', toggleSize)
|
||||
.appendTo(self.$controls);
|
||||
} else {
|
||||
self.$goToPointButton = new Ox.Button({
|
||||
id: self.options.id + 'GoTo' + Ox.toTitleCase(self.options.type),
|
||||
title: 'GoTo' + Ox.toTitleCase(self.options.type),
|
||||
tooltip: 'Go to ' + Ox.toTitleCase(self.options.type) + ' Point',
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent('click', goToPoint)
|
||||
.appendTo(self.$controls);
|
||||
self.$setPointButton = new Ox.Button({
|
||||
id: self.options.id + 'Set' + Ox.toTitleCase(self.options.type),
|
||||
title: 'Set' + Ox.toTitleCase(self.options.type),
|
||||
tooltip: 'Set ' + Ox.toTitleCase(self.options.type) + ' Point',
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent('click', setPoint)
|
||||
.appendTo(self.$controls);
|
||||
}
|
||||
|
||||
self.$positionInput = new Ox.TimeInput({
|
||||
milliseconds: true,
|
||||
seconds: true,
|
||||
value: Ox.formatDuration(self.options.position, 3)
|
||||
})
|
||||
.css({
|
||||
float: 'right',
|
||||
})
|
||||
.appendTo(self.$controls)
|
||||
|
||||
// fixme: strange positioning hack
|
||||
self.$positionInput.css({
|
||||
width: '98px'
|
||||
});
|
||||
$.browser.mozilla && self.$positionInput.css({
|
||||
marginTop: '-19px'
|
||||
});
|
||||
// fixme: children doesnt work w/o $element
|
||||
self.$positionInput.$element.children('.OxLabel').each(function(i, element) {
|
||||
$(this).css({
|
||||
width: '22px',
|
||||
marginLeft: (i == 0 ? 8 : 0) + 'px',
|
||||
background: 'rgb(32, 32, 32)'
|
||||
});
|
||||
});
|
||||
self.$positionInput.$element.children('div.OxInput').each(function(i) {
|
||||
var marginLeft = [-82, -58, -34, -10];
|
||||
$(this).css({
|
||||
marginLeft: marginLeft[i] + 'px'
|
||||
});
|
||||
});
|
||||
|
||||
if (self.options.type == 'play') {
|
||||
self.$loadingIcon = new Ox.LoadingIcon()
|
||||
.appendTo(that)
|
||||
.start();
|
||||
self.loadingInterval = setInterval(function() {
|
||||
if (self.video.readyState) {
|
||||
clearInterval(self.loadingInterval);
|
||||
self.$loadingIcon.stop();
|
||||
setPosition();
|
||||
}
|
||||
}, 50);
|
||||
} else {
|
||||
setPosition();
|
||||
}
|
||||
|
||||
function getSubtitle() {
|
||||
var subtitle = '';
|
||||
Ox.forEach(self.options.subtitles, function(v) {
|
||||
if (v['in'] <= self.options.position && v['out'] > self.options.position) {
|
||||
subtitle = v.value;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
function goToPoint() {
|
||||
that.triggerEvent('change', {
|
||||
position: self.options.points[self.options.type == 'in' ? 0 : 1]
|
||||
});
|
||||
}
|
||||
|
||||
function paused(event, data) {
|
||||
self.$playButton.toggleTitle();
|
||||
}
|
||||
|
||||
function playing(event, data) {
|
||||
self.options.position = data.position;
|
||||
setMarkers();
|
||||
setSubtitle();
|
||||
self.$positionInput.options({
|
||||
value: Ox.formatDuration(self.options.position, 3)
|
||||
});
|
||||
that.triggerEvent('playing', {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function setHeight() {
|
||||
that.css({
|
||||
height: (self.options.height + 16) + 'px'
|
||||
});
|
||||
self.options.type == 'play' ? self.$video.options({
|
||||
height: self.options.height
|
||||
}) : self.$video.css({
|
||||
height: self.options.height + 'px'
|
||||
});
|
||||
}
|
||||
|
||||
function setMarkers() {
|
||||
self.options.position == self.options.posterFrame ?
|
||||
self.$markerFrame.show() : self.$markerFrame.hide();
|
||||
Ox.forEach(self.$markerPoint, function(markers, point) {
|
||||
Ox.forEach(markers, function(marker) {
|
||||
self.options.position == self.options.points[point == 'in' ? 0 : 1] ?
|
||||
marker.show() : marker.hide();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function setPoint() {
|
||||
var data = {};
|
||||
self.options.points[self.options.type == 'in' ? 0 : 1] = self.options.position;
|
||||
setMarkers();
|
||||
data[self.options.type] = self.options.position;
|
||||
that.triggerEvent('set', data);
|
||||
}
|
||||
|
||||
function setPosition() {
|
||||
var position = Ox.limit(
|
||||
self.options.position - (self.options.type == 'out' ? 0.01 : 0),
|
||||
0, self.options.duration - 0.01
|
||||
),
|
||||
url;
|
||||
if (self.options.type == 'play') {
|
||||
self.$video.position(self.options.position);
|
||||
} else {
|
||||
self.$loadingIcon && self.$loadingIcon.stop();
|
||||
url = self.options.url(position);
|
||||
if (self.$video.attr('src') != url) {
|
||||
self.$loadingIcon = new Ox.LoadingIcon()
|
||||
.appendTo(that)
|
||||
.start();
|
||||
self.$video.attr({
|
||||
src: url
|
||||
})
|
||||
.load(self.$loadingIcon.stop);
|
||||
}
|
||||
}
|
||||
setMarkers();
|
||||
setSubtitle();
|
||||
self.$positionInput.options({
|
||||
value: Ox.formatDuration(self.options.position, 3)
|
||||
});
|
||||
}
|
||||
|
||||
function setSubtitle() {
|
||||
var subtitle = getSubtitle();
|
||||
if (subtitle != self.subtitle) {
|
||||
self.subtitle = subtitle;
|
||||
self.$subtitle.html(Ox.highlight(self.subtitle, self.options.find).replace(/\n/g, '<br/>'));
|
||||
}
|
||||
}
|
||||
|
||||
function setSubtitleSize() {
|
||||
self.$subtitle.css({
|
||||
bottom: parseInt(self.controlsHeight + self.options.height / 16) + 'px',
|
||||
width: self.options.width + 'px',
|
||||
fontSize: parseInt(self.options.height / 20) + 'px',
|
||||
WebkitTextStroke: (self.options.height / 1000) + 'px rgb(0, 0, 0)'
|
||||
});
|
||||
}
|
||||
|
||||
function setWidth() {
|
||||
that.css({
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
self.options.type == 'play' ? self.$video.options({
|
||||
width: self.options.width
|
||||
}) : self.$video.css({
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
setSubtitleSize();
|
||||
}
|
||||
|
||||
function toggleMute() {
|
||||
self.$video.toggleMute();
|
||||
}
|
||||
|
||||
function togglePlay() {
|
||||
self.video.paused ? that.play() : that.pause();
|
||||
}
|
||||
|
||||
function toggleSize(event, data) {
|
||||
self.options.size = data.id
|
||||
that.triggerEvent('togglesize', {
|
||||
size: self.options.size
|
||||
});
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
if (key == 'height') {
|
||||
setHeight();
|
||||
} else if (key == 'points') {
|
||||
setMarkers();
|
||||
} else if (key == 'position') {
|
||||
setPosition();
|
||||
} else if (key == 'posterFrame') {
|
||||
setMarkers();
|
||||
} else if (key == 'width') {
|
||||
setWidth();
|
||||
}
|
||||
}
|
||||
|
||||
that.mute = function() {
|
||||
self.$video.mute();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.pause = function() {
|
||||
self.$video.pause();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.play = function() {
|
||||
self.$video.play();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.playInToOut = function() {
|
||||
self.$video.paused() && self.$playButton.toggleTitle();
|
||||
self.$video.playInToOut();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.toggleMute = function() {
|
||||
self.$muteButton.trigger('click');
|
||||
return that;
|
||||
}
|
||||
|
||||
that.togglePlay = function() {
|
||||
self.$playButton.trigger('click');
|
||||
return that;
|
||||
}
|
||||
|
||||
that.unmute = function() {
|
||||
self.$video.unmute();
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
159
source/Ox.UI/js/Video/Ox.VideoElement.js
Normal file
159
source/Ox.UI/js/Video/Ox.VideoElement.js
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.VideoElement = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('video', self)
|
||||
.defaults({
|
||||
fps: 25,
|
||||
height: 0,
|
||||
loop: false,
|
||||
muted: false,
|
||||
paused: false,
|
||||
playInToOut: false,
|
||||
points: [0, 0],
|
||||
position: 0,
|
||||
poster: '',
|
||||
url: '',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.attr({
|
||||
poster: self.options.poster,
|
||||
preload: 'auto',
|
||||
src: self.options.url
|
||||
})
|
||||
.css({
|
||||
height: self.options.height + 'px',
|
||||
width: self.options.width + 'px'
|
||||
})
|
||||
.bind({
|
||||
ended: ended,
|
||||
loadedmetadata: function() {
|
||||
self.video.currentTime = self.options.position;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(self, {
|
||||
millisecondsPerFrame: 1000 / self.options.fps,
|
||||
video: that.$element[0]
|
||||
});
|
||||
|
||||
function ended() {
|
||||
that.pause()
|
||||
.triggerEvent('paused', {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function playing() {
|
||||
var event = 'playing';
|
||||
self.options.position = Math.round(self.video.currentTime * self.options.fps) / self.options.fps;
|
||||
if (self.options.playInToOut && self.options.position >= self.options.points[1]) {
|
||||
event = 'paused';
|
||||
that.position(self.options.points[1]).pause();
|
||||
}
|
||||
that.triggerEvent(event, {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
if (key == 'height') {
|
||||
that.size(self.options.width, value);
|
||||
} else if (key == 'muted') {
|
||||
that[value ? 'mute' : 'unmute']();
|
||||
} else if (key == 'paused') {
|
||||
that[value ? 'pause' : 'play']();
|
||||
} else if (key == 'points') {
|
||||
that.points(value);
|
||||
} else if (key == 'width') {
|
||||
that.size(value, self.options.height);
|
||||
}
|
||||
};
|
||||
|
||||
that.mute = function() {
|
||||
self.options.muted = true;
|
||||
self.video.muted = true;
|
||||
return that;
|
||||
};
|
||||
|
||||
that.muted = function() {
|
||||
return self.options.muted;
|
||||
}
|
||||
|
||||
that.pause = function() {
|
||||
self.options.paused = true;
|
||||
self.options.playInToOut = false;
|
||||
self.video.pause();
|
||||
clearInterval(self.playInterval);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.paused = function() {
|
||||
return self.options.paused;
|
||||
}
|
||||
|
||||
that.play = function() {
|
||||
self.options.paused = false;
|
||||
self.video.play();
|
||||
self.playInterval = setInterval(playing, self.millisecondsPerFrame);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.playInToOut = function() {
|
||||
self.options.playInToOut = true;
|
||||
that.position(self.options.points[0]);
|
||||
self.options.paused && that.play();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.points = function(points) {
|
||||
self.options.points = points;
|
||||
}
|
||||
|
||||
that.position = function(pos) {
|
||||
if (arguments.length == 0) {
|
||||
return self.video.currentTime;
|
||||
} else {
|
||||
self.options.position = pos;
|
||||
self.video.currentTime = self.options.position;
|
||||
return that;
|
||||
}
|
||||
};
|
||||
|
||||
that.size = function(width, height) {
|
||||
// fixme: why options? use css!
|
||||
if (arguments.length == 0) {
|
||||
return {
|
||||
width: self.options.width,
|
||||
height: self.options.height
|
||||
};
|
||||
} else {
|
||||
self.options.width = width;
|
||||
self.options.height = height;
|
||||
that.css({
|
||||
width: width + 'px',
|
||||
height: height + 'px'
|
||||
});
|
||||
return that;
|
||||
}
|
||||
};
|
||||
|
||||
that.toggleMute = function() {
|
||||
self.video.muted = !self.video.muted;
|
||||
return that;
|
||||
}
|
||||
|
||||
that.togglePlay = function() {
|
||||
self.options.paused = !self.options.paused;
|
||||
that[self.options.paused ? 'pause' : 'play']();
|
||||
}
|
||||
|
||||
that.unmute = function() {
|
||||
self.video.muted = false;
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
422
source/Ox.UI/js/Video/Ox.VideoPanelPlayer.js
Normal file
422
source/Ox.UI/js/Video/Ox.VideoPanelPlayer.js
Normal file
|
|
@ -0,0 +1,422 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.VideoPanelPlayer = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
annotationsSize: 256,
|
||||
duration: 0,
|
||||
height: 0,
|
||||
loop: false,
|
||||
muted: false,
|
||||
paused: false,
|
||||
playInToOut: false,
|
||||
points: [0, 0],
|
||||
position: 0,
|
||||
poster: '',
|
||||
showAnnotations: true,
|
||||
showControls: true,
|
||||
subtitles: [],
|
||||
videoHeight: 0,
|
||||
videoSize: 'fit',
|
||||
videoWidth: 0,
|
||||
videoURL: '',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.css({
|
||||
height: self.options.height + 'px',
|
||||
width: self.options.width + 'px'
|
||||
})
|
||||
.bindEvent({
|
||||
resize: resizeElement,
|
||||
key_shift_a: function() {
|
||||
that.toggleAnnotations();
|
||||
},
|
||||
key_shift_c: function() {
|
||||
that.toggleControls();
|
||||
},
|
||||
key_shift_s: function() {
|
||||
that.toggleSize();
|
||||
},
|
||||
key_space: function() {
|
||||
that.togglePlay();
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(self, {
|
||||
fullscreen: false,
|
||||
videoCSS: getVideoCSS()
|
||||
});
|
||||
//alert(JSON.stringify([self.playerHeight, self.playerWidth, self.videoCSS]))
|
||||
|
||||
self.$player = new Ox.Element()
|
||||
.css({
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'hidden'
|
||||
})
|
||||
.bind({
|
||||
mousedown: that.gainFocus
|
||||
})
|
||||
.bindEvent({
|
||||
resize: resizeVideo
|
||||
});
|
||||
|
||||
self.$video = new Ox.VideoElement({
|
||||
height: self.videoCSS.height,
|
||||
paused: true,
|
||||
points: self.options.points,
|
||||
position: self.options.position,
|
||||
url: self.options.videoURL,
|
||||
width: self.videoCSS.width
|
||||
})
|
||||
.css(self.videoCSS)
|
||||
.bindEvent({
|
||||
paused: paused,
|
||||
playing: playing
|
||||
})
|
||||
.appendTo(self.$player);
|
||||
|
||||
self.$controls = new Ox.Element()
|
||||
.bindEvent({
|
||||
toggle: toggleControls
|
||||
});
|
||||
|
||||
self.$buttons = new Ox.Element()
|
||||
.css({
|
||||
float: 'left',
|
||||
width: '16px',
|
||||
margin: '4px'
|
||||
})
|
||||
.appendTo(self.$controls);
|
||||
|
||||
self.$button = {
|
||||
play: new Ox.Button({
|
||||
id: 'play',
|
||||
title: [
|
||||
{id: 'play', title: 'play'},
|
||||
{id: 'pause', title: 'pause'}
|
||||
],
|
||||
tooltip: ['Play', 'Pause'],
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: self.$video.togglePlay
|
||||
}),
|
||||
mute: new Ox.Button({
|
||||
id: 'mute',
|
||||
title: [
|
||||
{id: 'mute', title: 'mute'},
|
||||
{id: 'unmute', title: 'unmute'}
|
||||
],
|
||||
tooltip: ['Mute', 'Unmute'],
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: self.$video.toggleMute
|
||||
}),
|
||||
size: new Ox.Button({
|
||||
id: 'size',
|
||||
title: self.options.videoSize == 'fit' ? [
|
||||
{id: 'fill', title: 'fill'},
|
||||
{id: 'fit', title: 'fit'}
|
||||
] : [
|
||||
{id: 'fit', title: 'fit'},
|
||||
{id: 'fill', title: 'fill'}
|
||||
],
|
||||
tooltip: self.options.videoSize == 'fit' ? [
|
||||
'Fill Screen', 'Fit to Screen'
|
||||
] : [
|
||||
'Fit to Screen', 'Fill Screen'
|
||||
],
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: toggleSize
|
||||
}),
|
||||
fullscreen: new Ox.Button({
|
||||
id: 'size',
|
||||
title: [
|
||||
{id: 'grow', title: 'grow'},
|
||||
{id: 'shrink', title: 'shrink'}
|
||||
],
|
||||
tooltip: [
|
||||
'Enter Fullscreen', 'Exit Fullscreen'
|
||||
],
|
||||
type: 'image'
|
||||
})
|
||||
.bindEvent({
|
||||
click: toggleFullscreen
|
||||
})
|
||||
}
|
||||
var i = 0;
|
||||
Ox.forEach(self.$button, function($button) {
|
||||
$button.css({
|
||||
position: 'absolute',
|
||||
left: '8px',
|
||||
top: (8 + i++ * 24) + 'px'
|
||||
})
|
||||
.appendTo(self.$buttons);
|
||||
});
|
||||
|
||||
self.$timelines = new Ox.Element()
|
||||
.css({
|
||||
float: 'left',
|
||||
margin: '4px'
|
||||
})
|
||||
.appendTo(self.$controls);
|
||||
|
||||
self.$timeline = {
|
||||
large: new Ox.LargeTimeline({
|
||||
duration: self.options.duration,
|
||||
position: self.options.position,
|
||||
subtitles: self.options.subtitles,
|
||||
videoId: self.options.videoId,
|
||||
width: getTimelineWidth()
|
||||
})
|
||||
.css({
|
||||
top: '4px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: changeLargeTimeline
|
||||
}),
|
||||
small: new Ox.SmallTimeline({
|
||||
duration: self.options.duration,
|
||||
position: self.options.position,
|
||||
subtitles: self.options.subtitles,
|
||||
videoId: self.options.videoId,
|
||||
width: getTimelineWidth()
|
||||
})
|
||||
.css({
|
||||
top: '76px'
|
||||
})
|
||||
.bindEvent({
|
||||
change: changeSmallTimeline
|
||||
})
|
||||
};
|
||||
Ox.forEach(self.$timeline, function($timeline) {
|
||||
$timeline.appendTo(self.$timelines);
|
||||
});
|
||||
|
||||
self.$panel = new Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: self.$player
|
||||
},
|
||||
{
|
||||
collapsed: !self.options.showControls,
|
||||
collapsible: true,
|
||||
element: self.$controls,
|
||||
size: 104
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
})
|
||||
.bindEvent({
|
||||
resize: resizePanel
|
||||
});
|
||||
|
||||
self.$annotations = new Ox.Element()
|
||||
.bindEvent({
|
||||
resize: resizeAnnotations,
|
||||
resizeend: resizeendAnnotations,
|
||||
toggle: toggleAnnotations
|
||||
});
|
||||
|
||||
that.$element = new Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: self.$panel
|
||||
},
|
||||
{
|
||||
collapsed: !self.options.showAnnotations,
|
||||
collapsible: true,
|
||||
element: self.$annotations,
|
||||
resizable: true,
|
||||
resize: [192, 256, 320, 384],
|
||||
size: self.options.annotationsSize
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
});
|
||||
|
||||
function changeLargeTimeline(event, data) {
|
||||
self.options.position = data.position;
|
||||
self.$video.position(self.options.position);
|
||||
self.$timeline.small.options({
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function changeSmallTimeline(event, data) {
|
||||
self.options.position = data.position;
|
||||
self.$video.position(self.options.position);
|
||||
self.$timeline.large.options({
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function getPlayerHeight() {
|
||||
return self.options.height -
|
||||
self.options.showControls * 104 - 1;
|
||||
}
|
||||
|
||||
function getPlayerWidth() {
|
||||
return self.options.width -
|
||||
(self.options.showAnnotations && !self.fullscreen) *
|
||||
self.options.annotationsSize - 1;
|
||||
}
|
||||
|
||||
function getTimelineWidth() {
|
||||
return self.options.width -
|
||||
(self.options.showAnnotations && !self.fullscreen) *
|
||||
self.options.annotationsSize - 40
|
||||
}
|
||||
|
||||
function getVideoCSS() {
|
||||
var width = getPlayerWidth(),
|
||||
height = getPlayerHeight(),
|
||||
ratio = width / height,
|
||||
videoRatio = self.options.videoWidth / self.options.videoHeight,
|
||||
isWide = ratio < videoRatio;
|
||||
return self.options.videoSize == 'fit' ? {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: (isWide ? width : Math.round(height * videoRatio)) + 'px',
|
||||
height: (isWide ? Math.round(width / videoRatio) : height) + 'px',
|
||||
margin: 'auto'
|
||||
} : {
|
||||
width: (isWide ? Math.round(height * videoRatio) : width) + 'px',
|
||||
height: (isWide ? height : Math.round(width / videoRatio)) + 'px',
|
||||
margin: [
|
||||
isWide ? '0' : Math.floor((height - width / videoRatio) / 2) + 'px',
|
||||
isWide ? Math.ceil((width - height * videoRatio) / 2) + 'px' : '0',
|
||||
isWide ? '0' : Math.ceil((height - width / videoRatio) / 2) + 'px',
|
||||
isWide ? Math.floor((width - height * videoRatio) / 2) + 'px' : '0'
|
||||
].join(' ')
|
||||
};
|
||||
}
|
||||
|
||||
function paused() {
|
||||
|
||||
}
|
||||
|
||||
function playing(event, data) {
|
||||
self.options.position = data.position;
|
||||
self.$timeline.large.options({
|
||||
position: self.options.position
|
||||
});
|
||||
self.$timeline.small.options({
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function resizeAnnotations(event, data) {
|
||||
self.options.annotationsSize = data;
|
||||
resizeVideoAndControls();
|
||||
}
|
||||
|
||||
function resizeendAnnotations(event, data) {
|
||||
self.options.annotationsSize = data;
|
||||
that.triggerEvent('change', {
|
||||
annotationsSize: self.options.annotationsSize
|
||||
});
|
||||
}
|
||||
|
||||
function resizeControls() {
|
||||
self.$timeline.large.options({
|
||||
width: getTimelineWidth()
|
||||
});
|
||||
self.$timeline.small.options({
|
||||
width: getTimelineWidth()
|
||||
});
|
||||
}
|
||||
|
||||
function resizeElement(event, data) {
|
||||
// called on browser toggle
|
||||
self.options.height = data;
|
||||
resizeVideo();
|
||||
}
|
||||
|
||||
function resizePanel(event, data) {
|
||||
// called on annotations toggle
|
||||
resizeVideoAndControls();
|
||||
}
|
||||
|
||||
function resizeVideoAndControls() {
|
||||
resizeVideo();
|
||||
resizeControls();
|
||||
}
|
||||
|
||||
function resizeVideo() {
|
||||
self.videoCSS = getVideoCSS();
|
||||
self.$video.css(self.videoCSS);
|
||||
};
|
||||
|
||||
function toggleAnnotations(event, data) {
|
||||
self.options.showAnnotations = !data.collapsed;
|
||||
that.triggerEvent('change', {
|
||||
showAnnotations: self.options.showAnnotations
|
||||
});
|
||||
}
|
||||
|
||||
function toggleControls(event, data) {
|
||||
self.options.showControls = !data.collapsed;
|
||||
that.triggerEvent('change', {
|
||||
showControls: self.options.showControls
|
||||
});
|
||||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
self.fullscreen = !self.fullscreen;
|
||||
self.options.showAnnotations && that.$element.toggle(1);
|
||||
self.fullscreen && self.options.showControls && self.$panel.toggle(1);
|
||||
that.triggerEvent((self.fullscreen ? 'enter' : 'exit') + 'fullscreen', {});
|
||||
}
|
||||
|
||||
function toggleSize() {
|
||||
self.options.videoSize = self.options.videoSize == 'fit' ? 'fill' : 'fit';
|
||||
resizeVideo();
|
||||
that.triggerEvent('change', {
|
||||
videoSize: self.options.videoSize
|
||||
});
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
if (key == 'height') {
|
||||
resizeVideo();
|
||||
} else if (key == 'position') {
|
||||
self.$video.position(value);
|
||||
} else if (key == 'width') {
|
||||
resizeVideoAndControls();
|
||||
}
|
||||
}
|
||||
|
||||
that.toggleAnnotations = function() {
|
||||
that.$element.toggle(1);
|
||||
//that.toggleAnnotations(null, !self.options.showAnnotations);
|
||||
};
|
||||
|
||||
that.toggleControls = function() {
|
||||
self.$panel.toggle(1);
|
||||
//that.toggleControls(null, !self.options.showControls);
|
||||
};
|
||||
|
||||
that.toggleMute = function() {
|
||||
self.$button.mute.trigger('click');
|
||||
};
|
||||
|
||||
that.togglePlay = function() {
|
||||
self.$button.play.trigger('click');
|
||||
};
|
||||
|
||||
that.toggleSize = function() {
|
||||
self.$button.size.trigger('click');
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
}
|
||||
589
source/Ox.UI/js/Video/Ox.VideoPlayer.js
Normal file
589
source/Ox.UI/js/Video/Ox.VideoPlayer.js
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.VideoEditor = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
annotationsSize: 0,
|
||||
cuts: [],
|
||||
duration: 0,
|
||||
find: '',
|
||||
frameURL: function() {},
|
||||
fps: 25, // fixme: doesn't get handed through to player
|
||||
height: 0,
|
||||
largeTimeline: true,
|
||||
layers: [],
|
||||
matches: [],
|
||||
points: [0, 0],
|
||||
position: 0,
|
||||
posterFrame: 0,
|
||||
showAnnotations: false,
|
||||
subtitles: [],
|
||||
videoHeight: 0,
|
||||
videoId: '',
|
||||
videoWidth: 0,
|
||||
videoSize: 'small',
|
||||
videoURL: '',
|
||||
width: 0
|
||||
})
|
||||
.options(options || {})
|
||||
.mousedown(function() {
|
||||
that.gainFocus();
|
||||
})
|
||||
.bindEvent({
|
||||
key_shift_0: function() {
|
||||
movePositionBy(-self.options.position);
|
||||
},
|
||||
key_alt_left: function() {
|
||||
},
|
||||
key_alt_right: function() {
|
||||
},
|
||||
key_alt_shift_left: function() {
|
||||
},
|
||||
key_alt_shift_right: function() {
|
||||
},
|
||||
key_backslash: function() {
|
||||
select('subtitle');
|
||||
},
|
||||
key_closebracket: function() {
|
||||
movePositionTo('subtitle', 1);
|
||||
},
|
||||
key_comma: function() {
|
||||
movePositionTo('cut', -1);
|
||||
},
|
||||
key_dot: function() {
|
||||
movePositionTo('cut', 1);
|
||||
},
|
||||
key_down: function() {
|
||||
movePositionBy(self.sizes.timeline[0].width);
|
||||
},
|
||||
key_i: function() {
|
||||
setPoint('in');
|
||||
},
|
||||
key_left: function() {
|
||||
movePositionBy(-1);
|
||||
},
|
||||
key_m: toggleMute,
|
||||
key_o: function() {
|
||||
setPoint('out');
|
||||
},
|
||||
key_openbracket: function() {
|
||||
movePositionTo('subtitle', -1);
|
||||
},
|
||||
key_p: playInToOut,
|
||||
key_right: function() {
|
||||
movePositionBy(1);
|
||||
},
|
||||
key_s: function() {
|
||||
// toggleSize
|
||||
},
|
||||
key_shift_comma: function() {
|
||||
movePositionTo('match', -1)
|
||||
},
|
||||
key_shift_dot: function() {
|
||||
movePositionTo('match', 1)
|
||||
},
|
||||
key_shift_down: function() {
|
||||
movePositionBy(self.options.duration);
|
||||
},
|
||||
key_shift_left: function() {
|
||||
movePositionBy(-0.04);
|
||||
//movePositionBy(-60);
|
||||
},
|
||||
key_shift_i: function() {
|
||||
goToPoint('in');
|
||||
},
|
||||
key_shift_o: function() {
|
||||
goToPoint('out');
|
||||
},
|
||||
key_shift_right: function() {
|
||||
movePositionBy(0.04);
|
||||
//movePositionBy(60);
|
||||
},
|
||||
key_shift_up: function() {
|
||||
movePositionBy(-self.options.duration);
|
||||
},
|
||||
key_slash: function() {
|
||||
select('cut');
|
||||
},
|
||||
key_space: togglePlay,
|
||||
key_up: function() {
|
||||
movePositionBy(-self.sizes.timeline[0].width);
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(self, {
|
||||
$player: [],
|
||||
$timeline: [],
|
||||
controlsHeight: 16,
|
||||
margin: 8,
|
||||
videoRatio: self.options.videoWidth / self.options.videoHeight
|
||||
});
|
||||
|
||||
self.$editor = new Ox.Element()
|
||||
.addClass('OxVideoEditor')
|
||||
.click(function() {
|
||||
that.gainFocus()
|
||||
});
|
||||
|
||||
self.sizes = getSizes();
|
||||
|
||||
['play', 'in', 'out'].forEach(function(type, i) {
|
||||
self.$player[i] = new Ox.VideoEditorPlayer({
|
||||
duration: self.options.duration,
|
||||
find: self.options.find,
|
||||
height: self.sizes.player[i].height,
|
||||
id: 'player' + Ox.toTitleCase(type),
|
||||
points: self.options.points,
|
||||
position: type == 'play' ? self.options.position : self.options.points[type == 'in' ? 0 : 1],
|
||||
posterFrame: self.options.posterFrame,
|
||||
subtitles: self.options.subtitles,
|
||||
type: type,
|
||||
url: type == 'play' ? self.options.videoURL : self.options.frameURL,
|
||||
width: self.sizes.player[i].width
|
||||
})
|
||||
.css({
|
||||
left: self.sizes.player[i].left + 'px',
|
||||
top: self.sizes.player[i].top + 'px'
|
||||
})
|
||||
.bindEvent(type == 'play' ? {
|
||||
playing: changePlayer,
|
||||
togglesize: toggleSize
|
||||
} : {
|
||||
change: function() {
|
||||
goToPoint(type);
|
||||
},
|
||||
set: function() {
|
||||
setPoint(type);
|
||||
}
|
||||
})
|
||||
.appendTo(self.$editor);
|
||||
});
|
||||
|
||||
self.$timeline[0] = new Ox.LargeTimeline({
|
||||
cuts: self.options.cuts,
|
||||
duration: self.options.duration,
|
||||
find: self.options.find,
|
||||
id: 'timelineLarge',
|
||||
matches: self.options.matches,
|
||||
points: self.options.points,
|
||||
position: self.options.position,
|
||||
subtitles: self.options.subtitles,
|
||||
videoId: self.options.videoId,
|
||||
width: self.sizes.timeline[0].width
|
||||
})
|
||||
.css({
|
||||
left: self.sizes.timeline[0].left + 'px',
|
||||
top: self.sizes.timeline[0].top + 'px'
|
||||
})
|
||||
.bindEvent('change', changeTimelineLarge)
|
||||
.bindEvent('changeEnd', changeTimelineLarge)
|
||||
.appendTo(self.$editor);
|
||||
|
||||
self.$timeline[1] = new Ox.BlockTimeline({
|
||||
cuts: self.options.cuts,
|
||||
duration: self.options.duration,
|
||||
find: self.options.find,
|
||||
id: 'timelineSmall',
|
||||
matches: self.options.matches,
|
||||
points: self.options.points,
|
||||
position: self.options.position,
|
||||
subtitles: self.options.subtitles,
|
||||
videoId: self.options.videoId,
|
||||
width: self.sizes.timeline[1].width
|
||||
})
|
||||
.css({
|
||||
left: self.sizes.timeline[1].left + 'px',
|
||||
top: self.sizes.timeline[1].top + 'px'
|
||||
})
|
||||
.bindEvent('change', changeTimelineSmall)
|
||||
.appendTo(self.$editor);
|
||||
|
||||
self.$annotations = new Ox.Element()
|
||||
.css({
|
||||
overflowY: 'auto'
|
||||
})
|
||||
.bindEvent({
|
||||
resize: resizeAnnotations,
|
||||
toggle: toggleAnnotations
|
||||
});
|
||||
self.$annotationPanel = [];
|
||||
|
||||
self.options.layers.forEach(function(layer, i) {
|
||||
self.$annotationPanel[i] = new Ox.AnnotationPanel(
|
||||
$.extend({
|
||||
width: self.options.annotationSize
|
||||
}, layer)
|
||||
)
|
||||
.bindEvent({
|
||||
add: function(event, data) {
|
||||
data.layer = layer.id;
|
||||
data['in'] = self.options.points[0];
|
||||
data.out = self.options.points[1];
|
||||
that.triggerEvent('addAnnotation', data);
|
||||
},
|
||||
'delete': function(event, data) {
|
||||
data.layer = layer.id;
|
||||
that.triggerEvent('removeAnnotations', data);
|
||||
},
|
||||
select: function(event, data) {
|
||||
self.options.layers.forEach(function(l, j) { // fixme: l? j?
|
||||
if(l.id != layer.id) {
|
||||
self.$annotationPanel[j].deselectItems();
|
||||
}
|
||||
});
|
||||
selectAnnotation(event, data);
|
||||
},
|
||||
submit: updateAnnotation
|
||||
});
|
||||
self.$annotationPanel[i]
|
||||
.appendTo(self.$annotations);
|
||||
});
|
||||
|
||||
that.$element = new Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: self.$editor
|
||||
},
|
||||
{
|
||||
collapsed: !self.options.showAnnotations,
|
||||
collapsible: true,
|
||||
element: self.$annotations,
|
||||
resizable: true,
|
||||
resize: [192, 256, 320, 384],
|
||||
size: self.options.annotationsSize
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
});
|
||||
|
||||
function changePlayer(event, data) {
|
||||
self.options.position = data.position;
|
||||
self.$timeline[0].options({
|
||||
position: data.position
|
||||
});
|
||||
self.$timeline[1].options({
|
||||
position: data.position
|
||||
});
|
||||
}
|
||||
|
||||
function changeTimelineLarge(event, data) {
|
||||
self.options.position = data.position;
|
||||
self.$player[0].options({
|
||||
position: data.position
|
||||
});
|
||||
self.$timeline[1].options({
|
||||
position: data.position
|
||||
});
|
||||
}
|
||||
|
||||
function changeTimelineSmall(event, data) {
|
||||
self.options.position = data.position;
|
||||
self.$player[0].options({
|
||||
position: data.position
|
||||
});
|
||||
self.$timeline[0].options({
|
||||
position: data.position
|
||||
});
|
||||
}
|
||||
|
||||
function getNextPosition(type, direction) {
|
||||
var found = false,
|
||||
position = 0,
|
||||
positions;
|
||||
if (type == 'cut') {
|
||||
positions = self.options.cuts;
|
||||
} else if (type == 'match') {
|
||||
positions = $.map(self.options.matches, function(v, i) {
|
||||
return self.options.subtitles[v]['in'];
|
||||
});
|
||||
} else if (type == 'subtitle') {
|
||||
positions = $.map(self.options.subtitles, function(v, i) {
|
||||
return v['in'];
|
||||
});
|
||||
}
|
||||
direction == -1 && positions.reverse();
|
||||
Ox.forEach(positions, function(v) {
|
||||
if (direction == 1 ? v > self.options.position : v < self.options.position) {
|
||||
position = v;
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
direction == -1 && positions.reverse();
|
||||
if (!found) {
|
||||
position = positions[direction == 1 ? 0 : positions.length - 1];
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
function getPoints(type) {
|
||||
var found = false,
|
||||
points,
|
||||
positions = [];
|
||||
if (type == 'cut') {
|
||||
positions = self.options.cuts;
|
||||
} else if (type == 'match') {
|
||||
// ...
|
||||
} else if (type == 'subtitle') {
|
||||
self.options.subtitles.forEach(function(v, i) {
|
||||
positions.push(v['in']);
|
||||
positions.push(v.out);
|
||||
});
|
||||
}
|
||||
positions.indexOf(0) == -1 && positions.unshift(0);
|
||||
positions.indexOf(self.options.duration) == -1 &&
|
||||
positions.push(self.options.duration);
|
||||
Ox.forEach(positions, function(v, i) {
|
||||
if (v > self.options.position) {
|
||||
points = [positions[i - 1], positions[i]];
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return points;
|
||||
}
|
||||
|
||||
function getSizes(scrollbarIsVisible) {
|
||||
//Ox.print('getSizes', scrollbarIsVisible)
|
||||
var scrollbarWidth = Ox.UI.SCROLLBAR_SIZE,
|
||||
contentWidth = self.options.width -
|
||||
(self.options.showAnnotations * self.options.annotationsSize) - 1 -
|
||||
(scrollbarIsVisible ? scrollbarWidth : 0),
|
||||
height,
|
||||
lines,
|
||||
size = {
|
||||
player: [],
|
||||
timeline: []
|
||||
},
|
||||
width, widths;
|
||||
if (self.options.videoSize == 'small') {
|
||||
width = 0;
|
||||
widths = Ox.divideInt(contentWidth - 4 * self.margin, 3);
|
||||
[1, 0, 2].forEach(function(v, i) {
|
||||
size.player[v] = {
|
||||
left: (i + 0.5) * self.margin + width,
|
||||
top: self.margin / 2,
|
||||
width: widths[i],
|
||||
height: Math.round(widths[1] / self.videoRatio)
|
||||
}
|
||||
width += widths[i];
|
||||
});
|
||||
} else {
|
||||
size.player[0] = {
|
||||
left: self.margin / 2,
|
||||
top: self.margin / 2,
|
||||
width: Math.round((contentWidth - 3 * self.margin + (self.controlsHeight + self.margin) / 2 * self.videoRatio) * 2/3),
|
||||
}
|
||||
size.player[0].height = Math.round(size.player[0].width / self.videoRatio);
|
||||
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
|
||||
}
|
||||
size.player[1].height = Math.ceil(size.player[1].width / self.videoRatio)
|
||||
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
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
size.timeline[1] = {
|
||||
left: size.timeline[0].left,
|
||||
top: size.timeline[0].top + size.timeline[0].height + self.margin,
|
||||
width: size.timeline[0].width
|
||||
}
|
||||
lines = Math.ceil(self.options.duration / size.timeline[1].width);
|
||||
height = getHeight();
|
||||
self.$editor.css({
|
||||
overflowY: (scrollbarIsVisible && height <= self.options.height) ? 'scroll' : 'auto'
|
||||
});
|
||||
return (!scrollbarIsVisible && height > self.options.height) ? getSizes(true) : size;
|
||||
function getHeight() {
|
||||
return size.player[0].height + self.controlsHeight +
|
||||
size.timeline[0].height + lines * 16 +
|
||||
(lines + 3) * self.margin;
|
||||
}
|
||||
}
|
||||
|
||||
function goToPoint(point) {
|
||||
self.options.position = self.options.points[point == 'in' ? 0 : 1];
|
||||
setPosition();
|
||||
that.triggerEvent('change', {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function movePositionBy(sec) {
|
||||
self.options.position = Ox.limit(self.options.position + sec, 0, self.options.duration);
|
||||
setPosition();
|
||||
that.triggerEvent('change', {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function movePositionTo(type, direction) {
|
||||
self.options.position = getNextPosition(type, direction);
|
||||
setPosition();
|
||||
that.triggerEvent('change', {
|
||||
position: self.options.position
|
||||
});
|
||||
}
|
||||
|
||||
function playInToOut() {
|
||||
self.$player[0].playInToOut();
|
||||
}
|
||||
|
||||
function resizeAnnotations(event, data) {
|
||||
self.options.annotationsSize = data;
|
||||
setSizes();
|
||||
}
|
||||
|
||||
function resizeEditor(event, data) {
|
||||
var width = data - 2 * margin + 100;
|
||||
resizeVideoPlayers(width);
|
||||
$timelineLarge.options({
|
||||
width: width
|
||||
});
|
||||
$timelineSmall.options({
|
||||
width: width
|
||||
});
|
||||
}
|
||||
|
||||
function resizePlayers() {
|
||||
self.$player.forEach(function(v, i) {
|
||||
v.options({
|
||||
width: size[i].width,
|
||||
height: size[i].height
|
||||
})
|
||||
.css({
|
||||
left: size[i].left + 'px',
|
||||
top: size[i].top + 'px',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function selectAnnotation(event, data) {
|
||||
self.options.position = data['in']
|
||||
self.options.points = [data['in'], data.out];
|
||||
setPosition();
|
||||
setPoints();
|
||||
}
|
||||
function updateAnnotation(event, data) {
|
||||
data['in'] = self.options.points[0];
|
||||
data.out = self.options.points[1];
|
||||
that.triggerEvent('updateAnnotation', data);
|
||||
}
|
||||
|
||||
function select(type) {
|
||||
self.options.points = getPoints(type);
|
||||
setPoints();
|
||||
}
|
||||
|
||||
function setPoint(point) {
|
||||
self.options.points[point == 'in' ? 0 : 1] = self.options.position;
|
||||
if (self.options.points[1] < self.options.points[0]) {
|
||||
self.options.points[point == 'in' ? 1 : 0] = self.options.position;
|
||||
}
|
||||
setPoints();
|
||||
}
|
||||
|
||||
function setPoints() {
|
||||
self.$player.forEach(function(v, i) {
|
||||
v.options($.extend({
|
||||
points: self.options.points
|
||||
}, i ? {
|
||||
position: self.options.points[i - 1]
|
||||
} : {}));
|
||||
});
|
||||
self.$timeline.forEach(function(v) {
|
||||
v.options({
|
||||
points: self.options.points
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setPosition() {
|
||||
self.$player[0].options({
|
||||
position: self.options.position
|
||||
});
|
||||
self.$timeline.forEach(function(v) {
|
||||
v.options({
|
||||
position: self.options.position
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setSizes() {
|
||||
self.sizes = getSizes();
|
||||
self.$player.forEach(function(v, i) {
|
||||
v.options({
|
||||
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'
|
||||
});
|
||||
});
|
||||
self.$timeline.forEach(function(v, i) {
|
||||
v.options({
|
||||
width: self.sizes.timeline[i].width
|
||||
})
|
||||
.css({
|
||||
left: self.sizes.timeline[i].left + 'px',
|
||||
top: self.sizes.timeline[i].top + 'px'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toggleAnnotations(event, data) {
|
||||
self.options.showAnnotations = !data.collapsed;
|
||||
setSizes();
|
||||
}
|
||||
|
||||
function toggleMute() {
|
||||
self.$player[0].toggleMute();
|
||||
}
|
||||
|
||||
function togglePlay() {
|
||||
self.$player[0].togglePlay();
|
||||
}
|
||||
|
||||
function toggleSize(event, data) {
|
||||
self.options.videoSize = data.size
|
||||
setSizes();
|
||||
that.triggerEvent('togglesize', {
|
||||
size: self.options.videoSize
|
||||
});
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
if (key == 'width' || key == 'height') {
|
||||
//Ox.print('XXXX setSizes', key, value, self.options.width, self.options.height)
|
||||
setSizes();
|
||||
} else if (key == 'position') {
|
||||
self.$player[0].position(value);
|
||||
}
|
||||
};
|
||||
|
||||
that.addAnnotation = function(layer, item) {
|
||||
var i = Ox.getPositionById(self.options.layers, layer);
|
||||
self.$annotationPanel[i].addItem(item);
|
||||
}
|
||||
|
||||
that.removeAnnotations = function(layer, ids) {
|
||||
var i = Ox.getPositionById(self.options.layers, layer);
|
||||
self.$annotationPanel[i].removeItems(ids);
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue