1
0
Fork 0
forked from 0x2620/oxjs

better filesystem structure for modules and themes; 'minified' ui if debug option not set; dynamially generated map markers

This commit is contained in:
rolux 2011-04-27 21:24:33 +02:00
commit 4489e88f44
596 changed files with 115093 additions and 17682 deletions

View 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;
};

View 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;
};

View 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;
};

View 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;
};

View 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;
};

View 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;
};

View 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;
};

View 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;
};

View 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;
}

View 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;
};