diff --git a/source/Ox.UI/js/Video/Ox.VideoTimelinePanel.js b/source/Ox.UI/js/Video/Ox.VideoTimelinePanel.js index 707e7a0e..a001154b 100644 --- a/source/Ox.UI/js/Video/Ox.VideoTimelinePanel.js +++ b/source/Ox.UI/js/Video/Ox.VideoTimelinePanel.js @@ -21,6 +21,9 @@ Ox.VideoTimelinePanel = function(options, self) { clickLink: null, cuts: [], duration: 0, + followPlayer: false, + getFrameURL: null, + getTimelineImageURL: null, height: 0, layers: [], loop: false, // fixme: used? @@ -36,6 +39,9 @@ Ox.VideoTimelinePanel = function(options, self) { showLayers: {}, showUsers: false, subtitles: [], + timeline: '', + timelineImageURL: '', + timelines: [], video: '', volume: 1, width: 0 @@ -60,12 +66,23 @@ Ox.VideoTimelinePanel = function(options, self) { self.$player = Ox.VideoTimelinePlayer({ censored: self.options.censored, cuts: self.options.cuts, - duration: self.options.durations, + duration: self.options.duration, + followPlayer: self.options.followPlayer, + getFrameURL: self.options.getFrameURL, + getTimelineImageURL: self.options.getTimelineImageURL, + height: self.options.height, muted: self.options.muted, paused: true, position: self.options.position, resolution: self.options.resolution, - volume: self.options.volume + subtitles: self.options.subtitles, + timeline: self.options.timeline, + timelineImageURL: self.options.timelineImageURL, + timelines: self.options.timelines, + video: self.options.video, + videoRatio: self.options.videoRatio, + volume: self.options.volume, + width: getPlayerWidth() }) .bindEvent({ muted: function(data) { @@ -85,6 +102,69 @@ Ox.VideoTimelinePanel = function(options, self) { } }); + self.$annotationPanel = Ox.AnnotationPanel({ + calendarSize: self.options.annotationsCalendarSize, + clickLink: self.options.clickLink, + editable: false, + font: self.options.annotationsFont, + highlight: self.options.find, + 'in': self.options['in'], + layers: self.options.layers, + mapSize: self.options.annotationsMapSize, + out: self.options.out, + position: self.options.position, + range: self.options.annotationsRange, + selected: self.options.selected, + showCalendar: self.options.showAnnotationsCalendar, + showFonts: true, + showLayers: Ox.clone(self.options.showLayers), + showMap: self.options.showAnnotationsMap, + showUsers: self.options.showUsers, + sort: self.options.annotationsSort, + width: self.options.annotationsSize + }) + .bindEvent({ + annotationsfont: function(data) { + self.options.annotationsFont = data.font; + that.triggerEvent('annotationsfont', data); + }, + annotationsrange: function(data) { + self.options.annotationsRange = data.range; + that.triggerEvent('annotationsrange', data); + }, + annotationssort: function(data) { + self.options.annotationsSort = data.sort; + that.triggerEvent('annotationssort', data); + }, + open: function() { + setPosition(self.options['in']); + }, + resize: resizeAnnotations, + resizeend: resizeendAnnotations, + resizecalendar: function(data) { + that.triggerEvent('resizecalendar', data); + }, + resizemap: function(data) { + that.triggerEvent('resizemap', data); + }, + select: selectAnnotation, + toggle: toggleAnnotations, + togglecalendar: function(data) { + self.options.showAnnotationsCalendar = !data.collapsed; + that.triggerEvent('togglecalendar', data); + }, + togglelayer: function(data) { + that.triggerEvent('togglelayer', { + collapsed: data.collapsed, + layer: data.layer + }); + }, + togglemap: function(data) { + self.options.showAnnotationsMap = !data.collapsed; + that.triggerEvent('togglemap', data); + } + }); + that.setElement( Ox.SplitPanel({ elements: [ @@ -174,6 +254,14 @@ Ox.VideoTimelinePanel = function(options, self) { }); } + self.setOption = function(key, value) { + if (key == 'showAnnotations') { + that.$element.toggle(1); + } else if (key == 'width') { + self.$player.options({width: value}); + } + }; + that.toggleAnnotations = function() { that.$element.toggle(1); }; diff --git a/source/Ox.UI/js/Video/Ox.VideoTimelinePlayer.js b/source/Ox.UI/js/Video/Ox.VideoTimelinePlayer.js index 7c4efcff..761fa23f 100644 --- a/source/Ox.UI/js/Video/Ox.VideoTimelinePlayer.js +++ b/source/Ox.UI/js/Video/Ox.VideoTimelinePlayer.js @@ -10,8 +10,9 @@ Ox.VideoTimelinePlayer = function(options, self) { cuts: [], duration: 0, find: '', + followPlayer: false, getFrameURL: null, - getImageURL: null, + getTimelineImageURL: null, height: 0, 'in': 0, matches: [], @@ -19,14 +20,17 @@ Ox.VideoTimelinePlayer = function(options, self) { out: 0, paused: false, position: 0, + showMilliseconds: false, subtitles: [], - timelineURL: '', + timeline: '', + timelineImageURL: '', + timelines: [], + video: '', videoRatio: 1, volume: 1, width: 0 }) - .options(options || {}) - .addClass('OxVideoTimelinePlayer'); + .options(options || {}); self.fps = 25; self.frame = self.options.position * self.fps; @@ -36,12 +40,53 @@ Ox.VideoTimelinePlayer = function(options, self) { self.margin = 8; self.contentWidth = self.options.width - 2 * self.margin - Ox.UI.SCROLLBAR_SIZE; self.contentHeight = self.options.height - 32; + self.positionWidth = 48 + + !!self.options.showMilliseconds * 2 + + self.options.showMilliseconds * 6; self.tiles = Math.ceil(self.frames / self.tileWidth); self.videoWidth = Math.round(self.tileHeight * self.options.videoRatio); self.videoLines = getVideoLines(); self.lines = getLines(); - self.$topBar = Ox.Bar({size: 16}); + self.$menubar = Ox.Bar({size: 16}); + + self.$menuButton = Ox.MenuButton({ + items: [ + {id: 'timelines', title: 'Timeline', disabled: true}, + {group: 'timeline', min: 1, max: 1, items: Ox.map( + self.options.timelines, + function(timeline) { + return Ox.extend({ + checked: timeline.id == self.options.timeline, + disabled: true + }, timeline); + } + )}, + {}, + { + id: 'followPlayer', + title: 'Follow Player While Playing', + checked: self.options.followPlayer + } + ], + style: 'square', + title: 'set', + tooltip: 'Options', + type: 'image' + }) + .css({float: 'left'}) + .bindEvent({ + change: function(data) { + var id = data.id; + if (id == 'timeline') { + // ... + } else if (id == 'followPlayer') { + // ... + } + Ox.print('DATA:::::::::', data); + } + }) + .appendTo(self.$menubar); self.$content = Ox.Element() .addClass('OxVideoTimelinePlayer') @@ -58,10 +103,14 @@ Ox.VideoTimelinePlayer = function(options, self) { mousedown: function() { this.gainFocus(); }, + key_0: toggleMuted, key_down: function() { self.options.position += self.contentWidth / self.fps; setPosition(); }, + key_equal: function() { + changeVolume(0.1); + }, key_enter: function() { scrollToPosition(); }, @@ -69,6 +118,9 @@ Ox.VideoTimelinePlayer = function(options, self) { self.options.position -= self.videoWidth / self.fps; setPosition(); }, + key_minus: function() { + changeVolume(-0.1); + }, key_right: function() { self.options.position += self.videoWidth / self.fps; setPosition(); @@ -91,7 +143,8 @@ Ox.VideoTimelinePlayer = function(options, self) { }); - self.$bottomBar = Ox.Bar({size: 16}); + self.$playerbar = Ox.Bar({size: 16}); + self.$playButton = Ox.Button({ style: 'symbol', title: 'play', @@ -105,65 +158,75 @@ Ox.VideoTimelinePlayer = function(options, self) { togglePaused(); } }) - .appendTo(self.$bottomBar); + .appendTo(self.$playerbar); + self.$muteButton = Ox.Button({ style: 'symbol', title: 'mute', type: 'image' }) - .css({ - float: 'left' - }) + .css({float: 'left'}) .bindEvent({ + click: toggleMuted + }) + .appendTo(self.$playerbar); - }) - .appendTo(self.$bottomBar); - self.$smallTimeline = $('
') + self.$smallTimeline = getSmallTimeline().appendTo(self.$playerbar); + + self.$position = Ox.Element() + .addClass('OxPosition') .css({ - float: 'left', - width: self.options.width - 80 + 'px', - height: '16px', - borderRadius: '8px', - background: 'rgb(0, 0, 0)' + width: self.positionWidth - 4 + 'px', }) - .appendTo(self.$bottomBar); - self.$smallTimelineImage = $('') - .attr({ - src: self.options.timelineURL + .html(formatPosition()) + .bind({ + click: function() { + if (!self.options.paused) { + self.playOnSubmit = true; + togglePaused(); + } + self.$position.hide(); + self.$positionInput + .value(formatPosition()) + .show() + .focusInput(false); + } }) - .css({ - width: self.options.width - 96 + 'px', - height: '16px', - margin: '0 8px 0 8px' + .appendTo(self.$playerbar); + + self.$positionInput = Ox.Input({ + value: formatPosition(), + width: self.positionWidth }) - .appendTo(self.$smallTimeline); - self.$position = $('
') - .css({ - float: 'left', - width: '40px', - height: '12px', - padding: '2px 4px 2px 4px', + .addClass('OxPositionInput') + .bindEvent({ + blur: submitPositionInput, + submit: submitPositionInput + }) + .appendTo(self.$playerbar); + + self.$positionInput.children('input').css({ + width: (self.positionWidth - 6) + 'px', fontSize: '9px' - }) - .html(Ox.formatDuration(self.options.position)) - .appendTo(self.$bottomBar); + }); self.$panel = Ox.SplitPanel({ - elements: [ - { - element: self.$topBar, - size: 16 - }, - { - element: self.$content - }, - { - element: self.$bottomBar, - size: 16 - } - ], - orientation: 'vertical' - }); + elements: [ + { + element: self.$menubar, + size: 16 + }, + { + element: self.$content + }, + { + element: self.$playerbar, + size: 16 + } + ], + orientation: 'vertical' + }) + .addClass('OxVideoTimelinePlayer'); that.setElement(self.$panel); @@ -189,6 +252,7 @@ Ox.VideoTimelinePlayer = function(options, self) { background: 'rgb(0, 0, 0)' }) .appendTo(self.$timelines[self.videoLines[1]][0]); + self.$frame = $('') .attr({ src: self.options.getFrameURL(self.options.position) @@ -198,6 +262,7 @@ Ox.VideoTimelinePlayer = function(options, self) { height: self.tileHeight + 'px', }) .appendTo(self.$frameBox) + $('
') .addClass('OxFrameInterface') .css({ @@ -222,13 +287,14 @@ Ox.VideoTimelinePlayer = function(options, self) { zIndex: 5 }) .appendTo(self.$timelines[self.videoLines[0]][0]); + self.$video = Ox.VideoPlayer({ duration: self.options.duration, height: self.tileHeight, paused: self.options.paused, position: self.options.position, scaleToFill: true, - video: self.options.videoURL, + video: self.options.video, width: self.videoWidth }) .bindEvent({ @@ -241,6 +307,7 @@ Ox.VideoTimelinePlayer = function(options, self) { } }) .appendTo(self.$videoBox); + $('
') .addClass('OxFrameInterface OxVideoInterface') .css({ @@ -291,6 +358,16 @@ Ox.VideoTimelinePlayer = function(options, self) { } + function changeVolume(num) { + self.options.volume = Ox.limit(self.options.volume + num, 0, 1); + setVolume(); + } + + function formatPosition(position) { + position = Ox.isUndefined(position) ? self.options.position : position; + return Ox.formatDuration(position, self.options.showMilliseconds); + } + function getLines() { return Math.ceil((self.frames - 1 + self.videoWidth) / self.contentWidth) } @@ -302,6 +379,38 @@ Ox.VideoTimelinePlayer = function(options, self) { ) / self.fps; } + function getSmallTimeline() { + var $timeline = Ox.SmallVideoTimeline({ + duration: self.options.duration, + paused: self.options.paused, + position: self.options.position, + showMilliseconds: self.options.showMilliseconds, + timeline: self.options.timelineImageURL, + type: 'player', + width: getSmallTimelineWidth() + }) + .css({float: 'left'}) + .css({background: '-moz-linear-gradient(top, rgba(0, 0, 0, 0.5), rgba(64, 64, 64, 0.5))'}) + .css({background: '-o-linear-gradient(top, rgba(0, 0, 0, 0.5), rgba(64, 64, 64, 0.5))'}) + .css({background: '-webkit-linear-gradient(top, rgba(0, 0, 0, 0.5), rgba(64, 64, 64, 0.5))'}) + .bindEvent({ + position: function(data) { + self.options.position = data.position; + setPosition(); + that.triggerEvent('position', { + position: self.options.position + }); + } + }); + $timeline.children().css({marginLeft: '32px'}); + $timeline.find('.OxInterface').css({marginLeft: '32px'}); + return $timeline; + } + + function getSmallTimelineWidth() { + return self.options.width - 32 - self.positionWidth; + } + function getSubtitle(position) { var subtitle = ''; Ox.forEach(self.options.subtitles, function(v) { @@ -394,7 +503,7 @@ Ox.VideoTimelinePlayer = function(options, self) { Ox.loop(self.tiles, function(i) { $('') .attr({ - src: self.options.getImageURL(i) + src: self.options.getTimelineImageURL(i) }) .css({ position: 'absolute', @@ -471,7 +580,8 @@ Ox.VideoTimelinePlayer = function(options, self) { }); scrollToPosition(); } - self.$position.html(Ox.formatDuration(self.options.position)) + self.$smallTimeline.options({position: self.options.position}); + self.$position.html(formatPosition()) } function setSubtitles() { @@ -492,6 +602,13 @@ Ox.VideoTimelinePlayer = function(options, self) { }); } + function setVolume() { + self.$video.options({volume: self.options.volume}); + that.triggerEvent('volume', { + volume: self.options.volume + }); + } + function setWidth() { self.contentWidth = self.options.width - 2 * self.margin - Ox.UI.SCROLLBAR_SIZE; self.lines = getLines(); @@ -515,6 +632,32 @@ Ox.VideoTimelinePlayer = function(options, self) { self.$lines.pop(); self.$timelines.pop(); } + self.$smallTimeline.options({width: getSmallTimelineWidth()}) + } + + function submitPositionInput() { + self.$positionInput.hide(); + self.$position.html('').show(); + self.options.position = Ox.parseDuration(self.$positionInput.value()); + setPosition(); + if (self.playOnSubmit) { + togglePaused(); + self.playOnSubmit = false; + } + that.triggerEvent('position', { + position: self.options.position + }); + } + + function toggleMuted() { + self.options.muted = !self.options.muted; + self.$video.options({muted: self.options.muted}); + self.$muteButton.options({ + title: self.options.muted ? 'unmute' : 'mute' + }); + that.triggerEvent('muted', { + muted: self.options.muted + }); } function togglePaused(fromVideo) { @@ -523,7 +666,7 @@ Ox.VideoTimelinePlayer = function(options, self) { paused: self.options.paused }); self.$playButton.options({ - title: !self.options.paused ? 'pause' : 'play' + title: self.options.paused ? 'play' : 'pause' }); }