From e10ab5303038bb423fcfb465356af3088c53e7d6 Mon Sep 17 00:00:00 2001 From: rolux Date: Tue, 17 May 2011 10:58:03 +0200 Subject: [PATCH] add large timeline to video --- demos/video/js/video.js | 5 +- .../Ox.UI/js/Video/Ox.LargeVideoTimeline.js | 232 ++++++++++++++++++ source/Ox.UI/js/Video/Ox.VideoPlayer.js | 69 +++++- 3 files changed, 295 insertions(+), 11 deletions(-) create mode 100644 source/Ox.UI/js/Video/Ox.LargeVideoTimeline.js diff --git a/demos/video/js/video.js b/demos/video/js/video.js index 844bd9b5..4bb70bed 100644 --- a/demos/video/js/video.js +++ b/demos/video/js/video.js @@ -85,7 +85,10 @@ Ox.load('UI', { enableVolume: true, externalControls: true, height: 192, - 'in': 3128.725, + 'in': 3128.725, + largeTimeline: function(i) { + return 'http://next.0xdb.org/' + id + '/timelines/timeline.64.' + i + '.png'; + }, out: 3130.725, paused: true, showMilliseconds: 2, diff --git a/source/Ox.UI/js/Video/Ox.LargeVideoTimeline.js b/source/Ox.UI/js/Video/Ox.LargeVideoTimeline.js new file mode 100644 index 00000000..51e39676 --- /dev/null +++ b/source/Ox.UI/js/Video/Ox.LargeVideoTimeline.js @@ -0,0 +1,232 @@ +// vim: et:ts=4:sw=4:sts=4:ft=js + +/*@ +Ox.LargeVideoTimeline LargeTimeline Object + () -> LargeTimeline Object + (options) -> LargeTimeline Object + (options, self) -> LargeTimeline Object + options Options object + self shared private variable +@*/ + +Ox.LargeVideoTimeline = function(options, self) { + + var self = self || {}, + that = new Ox.Element({}, self) + .defaults({ + cuts: [], + duration: 0, + find: '', + getImageURL: null, + 'in': 0, + matches: [], + out: 0, + position: 0, + style: 'default', // fixme: there are no different styles yet + subtitles: [], + type: 'player', + width: 0 + }) + .options(options || {}) + .addClass('OxTimelineLarge') + .mouseleave(mouseleave) + .mousemove(mousemove) + .bindEvent({ + anyclick: click, + dragstart: dragstart, + drag: drag + }); + + $.extend(self, { + $cuts: [], + $markerPoint: {}, + $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 = $('
') + .css({ + left: self.center + 'px' + }) + .appendTo(that.$element) + + setSubtitles(); + + self.options.cuts.forEach(function(v, i) { + self.$cuts[i] = $('') + .addClass('OxCut') + .attr({ + src: Ox.UI.PATH + 'png/videoMarkerCut.png' + }) + .css({ + left: (v * self.fps) + 'px' + }) + .appendTo(self.$timeline) + }); + + self.$markerPosition = $('') + .addClass('OxMarkerPosition') + .attr({ + src: Ox.UI.PATH + 'png/videoMarkerPlay.png' + }) + .appendTo(that.$element); + setMarker(); + + ['in', 'out'].forEach(function(point) { + var titlecase = Ox.toTitleCase(point); + self.$markerPoint[point] = $('') + .addClass('OxMarkerPoint' + titlecase) + .attr({ + src: Ox.UI.PATH + 'png/videoMarker' + titlecase + '.png' + }) + .appendTo(self.$timeline); + setMarkerPoint(point); + }); + + setWidth(); + setPosition(); + + function click(event, e) { + self.options.position = Ox.limit( + getPosition(e), 0, self.options.duration + ); + setPosition(); + triggerPositionEvent(); + } + + 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(); + triggerPositionEvent(); + } + + 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(point) { + self.$markerPoint[point].css({ + left: (self.options[point] * 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.loop( + Math.max(self.tile - 1, 0), + Math.min(self.tile + 2, self.tiles), + function(i) { + if (!self.$tiles[i]) { + self.$tiles[i] = $('') + .attr({ + src: self.options.getImageURL(i) + }) + .css({ + left: (i * self.tileWidth) + 'px' + }) + .appendTo(self.$timeline); + } + } + ); + if (self.clientX && self.clientY) { + updateTooltip(); + } + } + + function setSubtitles() { + self.$subtitles = []; + self.options.subtitles.forEach(function(subtitle, i) { + self.$subtitles[i] = $('
') + .addClass('OxSubtitle' + (self.options.matches.indexOf(i) > -1 ? ' OxHighlight' : '')) + .css({ + left: (subtitle['in'] * self.fps) + 'px', + width: (((subtitle.out - subtitle['in']) * self.fps) - 2) + 'px' + }) + .html(Ox.highlight(subtitle.text, self.options.find, 'OxHighlight')) + .appendTo(self.$timeline) + }); + } + + 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 triggerPositionEvent() { + that.triggerEvent('position', { + 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.setOption = function(key, value) { + if (key == 'points') { + setMarkerPoint('in'); + setMarkerPoint('out'); + } else if (key == 'position') { + setPosition(); + } else if (key == 'subtitles') { + + } else if (key == 'width') { + setWidth(); + } + }; + + return that; + +}; diff --git a/source/Ox.UI/js/Video/Ox.VideoPlayer.js b/source/Ox.UI/js/Video/Ox.VideoPlayer.js index 4e170d3e..50008cf2 100644 --- a/source/Ox.UI/js/Video/Ox.VideoPlayer.js +++ b/source/Ox.UI/js/Video/Ox.VideoPlayer.js @@ -85,6 +85,7 @@ Ox.VideoPlayer = function(options, self) { keepIconVisible: false, keepLargeTimelineVisible: false, keepLogoVisible: false, + largeTimeline: false, logo: '', logoLink: '', logoTitle: '', @@ -220,12 +221,14 @@ Ox.VideoPlayer = function(options, self) { self.results = find(self.options.find); Ox.print('--setting results--', self.$timeline) if (self.options.duration) { + // video has loaded self.$timeline && self.$timeline.options({ results: self.results, subtitles: self.options.subtitles }); - } else { - + self.$largeTimeline && self.$largeTimeline.options({ + subtitles: self.options.subtitles + }); } }); self.options.subtitles = []; @@ -864,6 +867,20 @@ Ox.VideoPlayer = function(options, self) { } + if (self.options.largeTimeline) { + + if (self.options.duration) { + self.$largeTimeline = getLargeTimeline() + } else { + self.$largeTimeline = Ox.Element() + .css({ + float: 'left' + }) + .html(' '); + } + self.$largeTimeline.appendTo(that); + + } setSizes(); @@ -1092,6 +1109,31 @@ Ox.VideoPlayer = function(options, self) { return subtitle; } + function getLargeTimeline() { + var $timeline = Ox.LargeVideoTimeline({ + duration: self.options.duration, + find: self.options.find, + getImageURL: self.options.largeTimeline, + 'in': self.options['in'], + out: self.options.out, + position: self.options.position, + subtitles: self.options.subtitles, + type: 'player', + width: self.options.width + }) + .css({ + position: 'absolute', + bottom: '12px', + marginLeft: 0, + }) + .bindEvent({ + position: function(data) { + setPosition(data.position, true); + } + }); + return $timeline; + } + function getTimeline() { var $timeline = Ox.SmallVideoTimeline({ _offset: getTimelineLeft(), @@ -1239,13 +1281,13 @@ Ox.VideoPlayer = function(options, self) { } } - if (self.$timeline) { - if (!hadDuration) { - self.$timeline.replaceWith(self.$timeline = getTimeline()); - } - self.$timeline.options({ - duration: self.options.duration - }); + if (!hadDuration) { + self.$timeline && self.$timeline.replaceWith( + self.$timeline = getTimeline() + ); + self.$largeTimeline && self.$largeTimeline.replaceWith( + self.$largeTimeline = getLargeTimeline() + ); } if (self.options.enableKeyboard && self.options.focus == 'load') { @@ -1338,9 +1380,13 @@ Ox.VideoPlayer = function(options, self) { self.posterIsVisible = false; } self.$subtitle && setSubtitle(); + // fixme: setPosition may have been called from these timelines self.$timeline && self.$timeline.options({ position: self.options.position }); + self.$largeTimeline && self.$largeTimeline.options({ + position: self.options.position + }); self.$position && self.$position.html(formatPosition()); } @@ -1392,9 +1438,12 @@ Ox.VideoPlayer = function(options, self) { if (self.$volume) { self.$volume.animate(getCSS('volume'), ms); self.$volumeInput.options({ - size: Math.min(128, self.width - 56) + size: Math.min(128, self.width - 56) // fixme: should be width in Ox.Range }); } + self.$largeTimeline && self.$largeTimeline.options({ + width: self.width + }); } function setSubtitle() {