diff --git a/demos/video/js/video.js b/demos/video/js/video.js
index 0de7ac3e..62e0b560 100644
--- a/demos/video/js/video.js
+++ b/demos/video/js/video.js
@@ -2,8 +2,31 @@ Ox.load('UI', {
debug: true,
theme: 'modern'
}, function() {
+
+ var $foo, $bar;
+
var id = '0393109',
poster = 'png/poster.png',
+ results = [
+ {'in': 190.335, out: 192.353},
+ {'in': 1185.215, out: 1188.115},
+ {'in': 1784.525, out: 1785.915},
+ {'in': 1786.015, out: 1787.835},
+ {'in': 3087.365, out: 3090.275},
+ {'in': 3704.915, out: 3706.795},
+ {'in': 3706.895, out: 3709.615},
+ {'in': 4061.655, out: 4063.95},
+ {'in': 4531.775, out: 4533.415},
+ {'in': 4946.095, out: 4948.875},
+ {'in': 5292.885, out: 5294.665},
+ {'in': 5381.965, out: 5383.338},
+ {'in': 5385.415, out: 5389.58},
+ {'in': 5434.93, out: 5436.93},
+ {'in': 5437.405, out: 5439.405},
+ {'in': 5749.535, out: 5751.915},
+ {'in': 5780.235, out: 5782.435},
+ {'in': 5881.365, out: 5886.635}
+ ],
timeline = 'http://next.0xdb.org/' + id + '/timeline.16.png',
url = 'http://next.0xdb.org/' + id + '/96p.webm',
videoSize = getVideoSize(),
@@ -13,9 +36,11 @@ Ox.load('UI', {
enableKeyboard: true,
focus: 'mouseenter',
height: 192,
+ 'in': 3128.725,
logoLink: 'http://next.0xdb.org/' + id,
logoTitle: 'Watch on 0xdb',
logo: 'png/logo.png',
+ out: 3130.725,
paused: true,
poster: poster,
showIconOnLoad: true,
@@ -51,6 +76,7 @@ Ox.load('UI', {
Ox.VideoPlayer({
controls: ['play', 'playInToOut', 'mute', 'size', 'space', 'position'],
externalControls: true,
+ find: 'brick',
height: 192,
'in': 3128.725,
out: 3130.725,
@@ -66,8 +92,10 @@ Ox.load('UI', {
top: '16px'
})
.bindEvent({
- size: function() {
-
+ position: function(data) {
+ $blockTimeline.options({
+ position: data.position
+ });
}
})
];
@@ -90,7 +118,7 @@ Ox.load('UI', {
resize: [100, 400]
},
{
- element: Ox.Element()
+ element: $foo = Ox.Element()
}
],
orientation: 'vertical'
@@ -127,7 +155,7 @@ Ox.load('UI', {
resize: [100, 400]
},
{
- element: Ox.Element()
+ element: $bar = Ox.Element()
}
],
orientation: 'vertical'
@@ -135,6 +163,7 @@ Ox.load('UI', {
.bindEvent({
resize: function(foo, size) {
$videos[2].options({width: size - 32});
+ $blockTimeline.options({width: size - 40});
}
}),
size: 392,
@@ -160,4 +189,49 @@ Ox.load('UI', {
});
}
+ Ox.get('srt/0393109.srt', function(srt) {
+ var subtitles = Ox.parseSRT(srt);
+ $foo.append(
+ Ox.SmallVideoTimelineImages({
+ duration: 6336.08,
+ getTimelineURL: function(i) {
+ return 'png/timeline.16.' + i + '.png';
+ },
+ 'in': 1800,
+ out: 1900,
+ results: [
+ {'in': 3600, out: 3700}
+ ],
+ subtitles: subtitles,
+ type: 'editor',
+ width: 392
+ })
+ );
+ $bar.append(
+ $blockTimeline = Ox.BlockVideoTimeline({
+ duration: 6336.08,
+ find: 'brick',
+ getTimelineURL: function(i) {
+ return 'png/timeline.16.' + i + '.png';
+ },
+ 'in': 3128.725,
+ out: 3130.725,
+ results: results,
+ subtitles: subtitles,
+ width: 353
+ })
+ .css({
+ position: 'absolute',
+ left: '16px',
+ top: '16px'
+ })
+ .bindEvent('position', function(data) {
+ $videos[2].options({
+ position: data.position
+ });
+ })
+ );
+ })
+
+
});
diff --git a/source/Ox.UI/js/Core/Ox.Element.js b/source/Ox.UI/js/Core/Ox.Element.js
index 8298aa55..c22b0bfb 100644
--- a/source/Ox.UI/js/Core/Ox.Element.js
+++ b/source/Ox.UI/js/Core/Ox.Element.js
@@ -331,7 +331,7 @@ Ox.Element = function() {
Ox.forEach(Ox.makeObject(arguments), function(data, event) {
if ([
'mousedown', 'mouserepeat', 'anyclick', 'singleclick', 'doubleclick',
- 'dragstart', 'drag', 'dragpause', 'dragend', 'playing', 'progress'
+ 'dragstart', 'drag', 'dragpause', 'dragend', 'playing', 'position', 'progress'
].indexOf(event) == -1) {
Ox.print(that.id, self.options.id, 'trigger', event, data);
}
diff --git a/source/Ox.UI/js/Form/Ox.Input.js b/source/Ox.UI/js/Form/Ox.Input.js
index efc9f891..f95e2f64 100644
--- a/source/Ox.UI/js/Form/Ox.Input.js
+++ b/source/Ox.UI/js/Form/Ox.Input.js
@@ -516,7 +516,7 @@ Ox.Input = function(options, self) {
Ox.UI.$document.unbind('keydown', keypress);
Ox.UI.$document.unbind('keypress', keypress);
}
- that.triggerEvent('blur', {});
+ that.triggerEvent('blur');
}
function cancel() {
@@ -602,6 +602,7 @@ Ox.Input = function(options, self) {
Ox.UI.$document.keypress(keypress);
self.options.autocompleteSelect && setTimeout(autocomplete, 0); // fixme: why is the timeout needed?
}
+ that.triggerEvent('focus');
}
function getInputWidth() {
diff --git a/source/Ox.UI/js/Video/Ox.BlockTimeline.js b/source/Ox.UI/js/Video/Ox.BlockTimeline.js
index b88b6dbd..a63da8e7 100644
--- a/source/Ox.UI/js/Video/Ox.BlockTimeline.js
+++ b/source/Ox.UI/js/Video/Ox.BlockTimeline.js
@@ -4,7 +4,6 @@ Ox.BlockTimeline = function(options, self) {
var self = self || {},
that = new Ox.Element({}, self)
.defaults({
- cuts: [],
duration: 0,
find: '',
matches: [],
@@ -113,7 +112,7 @@ Ox.BlockTimeline = function(options, self) {
.appendTo(self.$lines[i].$element);
}
if (self.options.points[0] != self.options.points[1]) {
- addSelection[i];
+ addSelection(i);
}
}
diff --git a/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js b/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js
new file mode 100644
index 00000000..b02ca61a
--- /dev/null
+++ b/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js
@@ -0,0 +1,246 @@
+Ox.BlockVideoTimeline = function(options, self) {
+
+ self = self || {};
+ var that = new Ox.Element({}, self)
+ .defaults({
+ duration: 0,
+ find: '',
+ getTimelineURL: null,
+ 'in': 0,
+ out: 0,
+ position: 0,
+ results: [],
+ subtitles: [],
+ width: 0
+ })
+ .options(options || {})
+ .css({
+ position: 'absolute',
+ })
+ .bind({
+ mousedown: mousedown,
+ mouseleave: mouseleave,
+ mousemove: mousemove
+ })
+ .bindEvent({
+ drag: function(event, e) {
+ mousedown(e);
+ }
+ });
+
+ self.$images = [];
+ self.$interfaces = [];
+ self.$lines = [];
+ self.$tooltip = new Ox.Tooltip({
+ animate: false
+ }).css({
+ textAlign: 'center'
+ });
+ self.height = 16;
+ self.lines = getLines();
+ self.margin = 8;
+
+ setCSS();
+
+ Ox.loop(self.lines, function(i) {
+ addLine(i);
+ });
+
+ self.$positionMarker = $('
')
+ .attr({
+ src: Ox.UI.PATH + 'png/videoMarkerPlay.png'
+ })
+ .css({
+ position: 'absolute',
+ width: '9px',
+ height: '5px',
+ zIndex: 10
+ })
+ .appendTo(that.$element);
+ setPositionMarker();
+
+ self.$pointMarker = {};
+ ['in', 'out'].forEach(function(point) {
+ var titleCase = Ox.toTitleCase(point);
+ self.$pointMarker[point] = $('
')
+ .addClass('OxPointMarker' + titleCase)
+ .attr({
+ src: Ox.UI.PATH + 'png/videoMarker' + titleCase + '.png'
+ })
+ .css({
+ position: 'absolute',
+ width: '6px',
+ height: '6px',
+ marginLeft: (point == 'in' ? -1 : 4) + 'px',
+ zIndex: 10
+ })
+ .appendTo(that.$element);
+ setPointMarker(point);
+ });
+
+ function addLine(i) {
+ self.$lines[i] = $('
')
+ .css({
+ position: 'absolute',
+ left: (self.margin / 2) + 'px',
+ top: i * (self.height + self.margin) + 'px',
+ width: self.options.width + 'px',
+ height: '24px',
+ overflow: 'hidden'
+ })
+ .appendTo(that.$element);
+ self.$images[i] = Ox.SmallVideoTimelineImages({
+ duration: self.options.duration,
+ editing: self.options.editing,
+ getTimelineURL: self.options.getTimelineURL,
+ 'in': self.options['in'],
+ out: self.options.out,
+ results: self.options.results,
+ subtitles: self.options.subtitles,
+ width: Math.ceil(self.options.duration),
+ type: self.options.type
+ })
+ .css({
+ position: 'absolute',
+ marginLeft: (-i * self.options.width) + 'px'
+ })
+ .appendTo(self.$lines[i]);
+ self.$interfaces[i] = $('
').addClass('OxInterface')
+ .css({
+ position: 'absolute',
+ top: '2px',
+ width: Math.ceil(self.options.duration) + 'px',
+ height: '20px',
+ marginLeft: (-i * self.options.width) + 'px',
+ zIndex: 11
+ })
+ .appendTo(self.$lines[i]);
+ }
+
+ function getLines() {
+ return Math.ceil(self.options.duration / self.options.width);
+ }
+
+ 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 getSubtitle(position) {
+ var subtitle = '';
+ Ox.forEach(self.options.subtitles, function(v) {
+ if (v['in'] <= position && v.out > position) {
+ subtitle = v;
+ return false;
+ }
+ });
+ return subtitle;
+ }
+
+ function mousedown(e) {
+ if ($(e.target).is('.OxInterface')) {
+ self.options.position = getPosition(e);
+ setPositionMarker();
+ // fixme: check if this pattern is better
+ // than the one used for list selection
+ if (!self.triggered) {
+ Ox.print('$$$$$$$$$$$$$$$')
+ that.triggerEvent('position', {
+ position: self.options.position
+ });
+ self.triggered = true;
+ setTimeout(function() {
+ self.triggered = false;
+ }, 250);
+ }
+ }
+ }
+
+ function mouseleave() {
+ self.$tooltip.hide();
+ }
+
+ function mousemove(e) {
+ var position, subtitle;
+ if ($(e.target).is('.OxInterface')) {
+ position = getPosition(e);
+ subtitle = getSubtitle(position);
+ self.$tooltip.options({
+ title: subtitle ?
+ '
' +
+ Ox.highlight(subtitle.text, self.options.find, 'OxHighlight').replace(/\n/g, '
') +
+ '' +
+ Ox.formatDuration(subtitle['in'], 3) + ' - ' + Ox.formatDuration(subtitle['out'], 3) :
+ Ox.formatDuration(position, 3)
+ })
+ .show(e.clientX, e.clientY);
+
+ } else {
+ self.$tooltip.hide();
+ }
+ }
+
+ function setCSS() {
+ that.css({
+ width: (self.options.width + self.margin) + 'px',
+ height: ((self.height + self.margin) * self.lines) + 'px'
+ });
+ }
+
+ function setPointMarker(point) {
+ var position = Math.round(self.options[point]);
+ Ox.print('$$ position', position)
+ self.$pointMarker[point].css({
+ left: (position % self.options.width) + 'px',
+ top: (parseInt(position / self.options.width) *
+ (self.height + self.margin) + 16) + 'px'
+ });
+ }
+
+ function setPositionMarker() {
+ self.$positionMarker.css({
+ left: (self.options.position % self.options.width) + 'px',
+ top: (parseInt(self.options.position / self.options.width) *
+ (self.height + self.margin) + 2) + 'px'
+ });
+ }
+
+ function setWidth() {
+ self.lines = getLines();
+ setCSS();
+ Ox.loop(self.lines, 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'
+ });
+ self.$interfaces[i].css({
+ marginLeft: (-i * self.options.width) + 'px'
+ });
+ } else {
+ addLine(i);
+ }
+ });
+ while (self.$lines.length > self.lines) {
+ self.$lines[self.$lines.length - 1].remove();
+ self.$lines.pop();
+ self.$images.pop();
+ }
+ setPositionMarker();
+ setPointMarker('in');
+ setPointMarker('out');
+ }
+
+ self.setOption = function(key, value) {
+ if (key == 'position') {
+ setPositionMarker();
+ } else if (key == 'width') {
+ setWidth();
+ }
+ }
+
+ return that;
+
+};
\ No newline at end of file
diff --git a/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js b/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js
new file mode 100644
index 00000000..93e734c4
--- /dev/null
+++ b/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js
@@ -0,0 +1,54 @@
+Ox.SmallVideoTimeline = function(options, self) {
+
+ self = self || {};
+ var that = Ox.Element({}, self)
+ .defaults({
+ duration: 0,
+ editing: false,
+ getTimelineURL: null,
+ 'in': 0,
+ out: 0,
+ width: 256,
+ type: 'player'
+ })
+ .options(options || {})
+ .addClass('OxSmallVideoTimeline')
+ .css({
+ width: self.options.width + 'px'
+ });
+
+ self.height = self.options.type == 'player' ? 16 : 24;
+ self.imageHeight = self.options.type == 'player' ? 16 : 18;
+ self.imageTop = self.options.type == 'player' ? 0 : 3;
+
+ that.css({
+ height: self.height + 'px'
+ });
+
+ self.tooltip = Ox.Tooltip({
+ animate: false
+ }).css({
+ textAlign: 'center'
+ });
+
+ getImageURL('timeline', function(timelineURL) {
+
+ $('
![]()
')
+ .attr({
+ src: timelineURL
+ })
+ .css({
+ width: self.options.width + 'px',
+ height: self.imageHeight + 'px',
+ top: self.imageTop + 'px'
+ })
+ .appendTo(that.$element);
+
+
+
+ });
+
+
+ return that;
+
+};
\ No newline at end of file
diff --git a/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImages.js b/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImages.js
new file mode 100644
index 00000000..5e0df72c
--- /dev/null
+++ b/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImages.js
@@ -0,0 +1,187 @@
+Ox.SmallVideoTimelineImages = function(options, self) {
+
+ self = self || {};
+ var that = Ox.Element({}, self)
+ .defaults({
+ duration: 0,
+ editing: false,
+ getTimelineURL: null,
+ 'in': 0,
+ out: 0,
+ results: [],
+ subtitles: [],
+ width: 256,
+ type: 'player'
+ })
+ .options(options || {})
+ .addClass('OxSmallVideoTimeline')
+ .css({
+ position: 'absolute',
+ width: self.options.width + 'px'
+ });
+
+ self.height = self.options.type == 'player' ? 16 : 24;
+ self.imageHeight = self.options.type == 'player' ? 16 : 18;
+ self.imageTop = self.options.type == 'player' ? 0 : 3;
+
+ that.css({
+ height: self.height + 'px'
+ });
+
+ self.$timeline = $('
![]()
')
+ .attr({
+ src: Ox.UI.PATH + 'png/transparent.png'
+ })
+ .css({
+ position: 'absolute',
+ width: self.options.width + 'px',
+ height: self.imageHeight + 'px',
+ top: self.imageTop + 'px'
+ })
+ .appendTo(that.$element);
+
+ getImageURL('timeline', function(imageURL) {
+ self.$timeline.attr({
+ src: imageURL
+ });
+ });
+
+ self.$subtitles = $('
![]()
')
+ .attr({
+ src: getImageURL('subtitles')
+ })
+ .css({
+ position: 'absolute',
+ width: self.options.width + 'px',
+ height: self.imageHeight + 'px',
+ top: self.imageTop + 'px'
+ })
+ .appendTo(that.$element);
+
+ self.$results = $('
![]()
')
+ .attr({
+ src: getImageURL('results')
+ })
+ .css({
+ position: 'absolute',
+ width: self.options.width + 'px',
+ height: self.imageHeight + 'px',
+ top: self.imageTop + 'px'
+ })
+ .appendTo(that.$element);
+
+ self.$selection = $('
![]()
')
+ .attr({
+ src: getImageURL('selection')
+ })
+ .css({
+ position: 'absolute',
+ width: self.options.width + 'px',
+ height: self.imageHeight + 'px',
+ top: self.imageTop + 'px'
+ })
+ .appendTo(that.$element);
+
+ function getImageURL(image, callback) {
+ var width = Math.ceil(self.options.duration),
+ height = self.imageHeight,
+ canvas = $('