From 72f1b47e1c1208b8b4fd2407c8940c552b6d65cc Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Sat, 13 Jul 2013 23:07:25 +0000 Subject: [PATCH] add VideoEditPanel, ClipPanel --- source/Ox.UI/js/Video/ClipPanel.js | 297 ++++++++++++++++++ source/Ox.UI/js/Video/VideoEditPanel.js | 398 ++++++++++++++++++++++++ 2 files changed, 695 insertions(+) create mode 100644 source/Ox.UI/js/Video/ClipPanel.js create mode 100644 source/Ox.UI/js/Video/VideoEditPanel.js diff --git a/source/Ox.UI/js/Video/ClipPanel.js b/source/Ox.UI/js/Video/ClipPanel.js new file mode 100644 index 00000000..cab08fb2 --- /dev/null +++ b/source/Ox.UI/js/Video/ClipPanel.js @@ -0,0 +1,297 @@ +'use strict'; + +Ox.ClipPanel = function(options, self) { + + self = self || {}; + var that = Ox.Element({}, self) + .defaults({ + clips: [], + editable: false, + 'in': 0, + out: 0, + position: 0, + selected: '', + sort: [], + sortOptions: [], + view: 'list', + width: 0 + }) + .options(options || {}) + .update({ + clips: function() { + self.$list.options({items: self.options.clips}); + } + }); + + self.$menubar = Ox.Bar({ + size: 24 + }) + .bindEvent({ + doubleclick: function(e) { + if ($(e.target).is('.OxBar')) { + self.$list.animate({scrollTop: 0}, 250); + } + } + }); + + self.$viewElement = Ox.ButtonGroup({ + buttons: [ + {id: 'list', title: 'list', tooltip: Ox._('View as List')}, + {id: 'grid', title: 'grid', tooltip: Ox._('View as Grid')} + ], + selectable: true, + type: 'image', + value: self.options.view + }) + .css({ + float: 'left', + margin: '4px 2px 4px 4px' + }) + .bindEvent({ + change: function(data) { + Ox.print(data); + } + }) + .appendTo(self.$menubar); + + self.$sortSelect = Ox.Select({ + items: self.options.sortOptions, + value: self.options.sort[0].key, + width: 84 + Ox.UI.SCROLLBAR_SIZE + }) + .bindEvent({ + change: function(data) { + that.triggerEvent('sort', { + key: data.value, + operator: Ox.getObjectById( + self.options.sortOptions, data.value + ).operator + }); + } + }); + + self.$orderButton = Ox.Button({ + overlap: 'left', + title: getButtonTitle(), + tooltip: getButtonTooltip(), + type: 'image' + }) + .bindEvent({ + click: function() { + that.triggerEvent('sort', { + key: self.options.sort[0].key, + operator: self.options.sort[0].operator == '+' ? '-' : '+' + }); + } + }); + + self.$sortElement = Ox.FormElementGroup({ + elements: [self.$sortSelect, self.$orderButton], + float: 'right' + }) + .css({ + float: 'right', + margin: '4px 4px 4px 2px' + }) + .appendTo(self.$menubar); + + if (self.options.view == 'list') { + self.$list = Ox.TableList({ + columns: [ + { + align: 'right', + id: 'index', + operator: '+', + title: Ox._('Index'), + visible: false, + width: 60 + }, + { + id: 'id', + operator: '+', + title: Ox._('ID'), + unique: true, + visible: false, + width: 60 + }, + { + id: 'item', + operator: '+', + title: Ox._(pandora.site.itemName.singular), + visible: true, + width: 60 + }, + { + align: 'right', + editable: isEditable, + format: function(value) { + return Ox.formatDuration(value, 3); + }, + id: 'in', + operator: '+', + title: Ox._('In'), + visible: true, + width: 90 + }, + { + align: 'right', + editable: isEditable, + format: function(value) { + return Ox.formatDuration(value, 3); + }, + id: 'out', + operator: '+', + title: Ox._('Out'), + visible: true, + width: 90 + }, + { + align: 'right', + editable: isEditable, + format: function(value) { + return Ox.formatDuration(value, 3); + }, + id: 'duration', + operator: '+', + title: Ox._('Duration'), + visible: true, + width: 90 + } + ], + columnsMovable: true, + columnsRemovable: true, + columnsResizable: true, + columnsVisible: true, + items: self.options.clips, + scrollbarVisible: true, + sort: self.options.sort, + sortable: self.options.editable && self.options.sort[0].key == 'index', + unique: 'id' + }); + } else { + self.$list = Ox.IconList({ + + }); + } + self.$list.bindEvent({ + copy: function(data) { + that.triggerEvent('copy', data); + }, + cut: function(data) { + that.triggerEvent('cut', data); + }, + 'delete': function(data) { + that.triggerEvent('remove', data); + }, + move: function(data) { + data.ids.forEach(function(id, index) { + Ox.print('????', id, index); + self.$list.value(id, 'index', index); + }); + that.triggerEvent('move', data); + }, + open: function(data) { + that.triggerEvent('open', data); + }, + paste: function() { + that.triggerEvent('paste'); + }, + select: function(data) { + that.triggerEvent('select', data); + }, + sort: function(data) { + self.options.sort = self.$list.options('sort'); + self.$list.options({sortable: isSortable()}); + that.triggerEvent('sort', data); + }, + submit: function(data) { + data.value = Ox.parseDuration(data.value); + self.$list.value(data.id, data.key, data.value); + that.triggerEvent('edit', data); + } + }); + + self.$statusbar = Ox.Bar({ + size: 16 + }); + + that.setElement( + Ox.SplitPanel({ + elements: [ + { + element: self.$menubar, + size: 24 + }, + { + element: self.$list + }, + { + element: self.$statusbar, + size: 16 + } + ], + orientation: 'vertical' + }) + ); + + function cutClips() { + + } + + function editClip(data) { + var value = self.$list.value(data.id, data.key); + if (data.value != value && !(data.value === '' && value === null)) { + self.$list.value(data.id, data.key, data.value || null); + that.triggerEvent('edit', data); + } + } + + function getButtonTitle() { + return self.options.sort[0].operator == '+' ? 'up' : 'down'; + } + + function getButtonTooltip() { + return Ox._(self.options.sort[0].operator == '+' ? 'Ascending' : 'Descending'); + } + + function isEditable(data) { + return self.options.editable && !data.annotation; + } + + function isSortable() { + return self.options.editable && self.options.sort[0].key == 'index'; + } + + function moveClips(data) { + Ox.Request.clearCache(); + pandora.api.sortClips({ + edit: pandora.user.ui.edit, + ids: data.ids + }) + } + + function openClips(data) { + + } + + function pasteClips() { + + } + + function removeClips() { + + } + + function updateSortElement() { + + } + + that.updateItem = function(id, data) { + ['in', 'out', 'duration'].forEach(function(key) { + self.$list.value(id, key, data[key]); + }); + }; + + return that; + +}; diff --git a/source/Ox.UI/js/Video/VideoEditPanel.js b/source/Ox.UI/js/Video/VideoEditPanel.js new file mode 100644 index 00000000..87c7aa18 --- /dev/null +++ b/source/Ox.UI/js/Video/VideoEditPanel.js @@ -0,0 +1,398 @@ +'use strict'; + +Ox.VideoEditPanel = function(options, self) { + + self = self || {}; + var that = Ox.Element({}, self) + .defaults({ + clips: [], + clipSize: 256, + clipSort: [], + clipSortOptions: [], + clipTooltip: 'clips', + clipView: 'list', + clickLink: null, + cuts: [], + duration: 0, + editable: false, + enableSubtitles: false, + fullscreen: false, + getLargeTimelineURL: null, + height: 0, + 'in': 0, + loop: false, + muted: false, + out: 0, + paused: true, + playInToOut: false, + position: 0, + resolution: 0, + scaleToFill: false, + selected: '', + showClips: false, + showTimeline: false, + smallTimelineURL: '', + subtitles: [], + timeline: '', + timelineTooltip: 'timeline', + type: 'static', + video: [], + volume: 1, + width: 0 + }) + .options(options || {}) + .update({ + clips: function() { + self.$clipPanel.options({clips: Ox.clone(self.options.clips)}); + }, + duration: function() { + self.$timeline.options({duration: self.options.duration}); + }, + fullscreen: function() { + self.$video.options({fullscreen: self.options.fullscreen}); + }, + height: function() { + self.$video.options({height: getPlayerHeight()}); + }, + 'in': function() { + setPoint('in', self.options['in']); + }, + out: function() { + setPoint('out', self.options.out); + }, + paused: function() { + self.$video.options({paused: self.options.paused}); + }, + position: function() { + self.$video.options({position: self.options.position}); + self.$timeline.options({position: self.options.position}); + self.$annotationPanel.options({position: self.options.position}); + }, + selected: function() { + self.$annotationPanel.options({selected: self.options.selected}); + }, + showClips: function() { + that.$element.toggle(1); + }, + showTimeline: function() { + self.$videoPanel.toggle(1); + }, + smallTimelineURL: function() { + self.$video.options({timeline: self.options.smallTimelineURL}); + }, + timeline: function() { + self.$timeline.options({type: self.options.timeline}); + }, + video: function() { + self.$video.options({video: self.options.video}); + }, + width: function() { + self.$video.options({width: getPlayerWidth()}); + self.$timeline.options({width: getTimelineWidth()}); + } + }) + .bindEvent({ + //resize: resizeElement, + key_0: toggleMuted, + key_equal: function() { + self.$video.changeVolume(0.1); + }, + key_minus: function() { + self.$video.changeVolume(-0.1); + }, + key_space: togglePaused + }); + + self.fullscreen = false; + self.listSizes = [ + 144 + Ox.UI.SCROLLBAR_SIZE, + 280 + Ox.UI.SCROLLBAR_SIZE, + 416 + Ox.UI.SCROLLBAR_SIZE + ], + + self.$menubar = Ox.Bar({size: 24}); + + self.$player = Ox.Element().css({overflowX: 'hidden'}); + + self.$video = Ox.VideoPlayer({ + controlsTop: ['fullscreen', 'space', 'open'], + controlsBottom: ['play', 'volume', 'scale', 'timeline', 'previous', 'next', 'loop', 'position'], + enableKeyboard: true, + enableMouse: true, + enablePosition: true, + enableTimeline: true, + height: getPlayerHeight(), + 'in': self.options['in'], + loop: self.options.loop, + muted: self.options.muted, + out: self.options.out, + paused: self.options.paused, + position: self.options.position, + resolution: self.options.resolution, + scaleToFill: self.options.scaleToFill, + subtitles: self.options.subtitles, + timeline: self.options.smallTimelineURL, + video: self.options.video, + volume: self.options.volume, + width: getPlayerWidth() + }) + .bindEvent({ + fullscreen: function(data) { + self.options.fullscreen = data.fullscreen; + }, + loop: function(data) { + that.triggerEvent('loop', data); + }, + muted: function(data) { + that.triggerEvent('muted', data); + }, + paused: function(data) { + self.options.paused = data.paused; + that.triggerEvent('paused', data); + }, + playing: function(data) { + setPosition(data.position, true); + }, + position: function(data) { + setPosition(data.position); + }, + resolution: function(data) { + that.triggerEvent('resolution', data); + }, + scale: function(data) { + that.triggerEvent('scale', data); + }, + subtitles: function(data) { + self.$timeline.options({ + subtitles: data.subtitles ? self.options.subtitles : [] + }); + that.triggerEvent('subtitles', data); + }, + volume: function(data) { + that.triggerEvent('volume', data); + } + }) + .appendTo(self.$player); + + self.$controls = Ox.Element() + .addClass('OxMedia') + .bindEvent({ + toggle: toggleControls + }); + + self.$timeline = Ox.LargeVideoTimeline({ + duration: self.options.duration, + getImageURL: self.options.getLargeTimelineURL, + 'in': self.options['in'], + out: self.options.out, + position: self.options.position, + subtitles: self.options.enableSubtitles ? self.options.subtitles : [], + type: self.options.timeline, + width: getTimelineWidth() + }) + .css({left: '4px', top: '4px'}) + .bindEvent({ + mousedown: that.gainFocus, + position: changeTimeline + }) + .appendTo(self.$controls); + + self.$videoPanel = Ox.SplitPanel({ + elements: [ + { + element: self.$menubar, + size: 24 + }, + { + element: self.$player + }, + { + collapsed: !self.options.showTimeline, + collapsible: true, + element: self.$controls, + size: 80, + tooltip: self.options.timelineTooltip + } + ], + orientation: 'vertical' + }); + + self.$clipPanel = Ox.ClipPanel({ + clips: Ox.clone(self.options.clips), + editable: self.options.editable, + 'in': self.options['in'], + out: self.options.out, + position: self.options.position, + selected: self.options.selected, + sort: self.options.clipSort, + sortOptions: self.options.sortOptions, + view: self.options.clipView, + width: self.options.clipSize + }) + .bindEvent({ + copy: function(data) { + that.triggerEvent('copy', data); + }, + cut: function(data) { + that.triggerEvent('cut', data); + }, + edit: function(data) { + that.triggerEvent('edit', data); + }, + move: function(data) { + that.triggerEvent('move', data); + }, + open: function(data) { + // ... + }, + paste: function() { + that.triggerEvent('paste'); + }, + remove: function(data) { + that.triggerEvent('remove', data); + }, + resize: resizeClips, + resizeend: resizeendClips, + select: function(data) { + that.triggerEvent('select', data); + }, + sort: function(data) { + self.options.clipSort = data.sort; + that.triggerEvent('sort', data); + }, + toggle: toggleClips + }); + + that.setElement( + Ox.SplitPanel({ + elements: [ + { + element: self.$videoPanel + }, + { + collapsed: !self.options.showClips, + collapsible: true, + element: self.$clipPanel, + resizable: true, + resize: self.listSizes, + size: self.options.clipSize, + tooltip: self.options.clipTooltip + } + ], + orientation: 'horizontal' + }) + ); + + function changeTimeline(data) { + self.options.position = data.position; + self.$video.options({position: self.options.position}); + self.$clipPanel.options({position: self.options.position}); + that.triggerEvent('position', {position: self.options.position}); + } + + function getPlayerHeight() { + return self.options.height - 24 - + self.options.showTimeline * 80 - 1; + } + + function getPlayerWidth() { + return self.options.width - + (self.options.showClips && !self.fullscreen) + * self.options.clipSize - 1; + } + + function getTimelineWidth() { + return self.options.width - + (self.options.showClips && !self.fullscreen) + * self.options.clipSize - 16 - 1; + } + + function resizeClips(data) { + // called on clips resize + self.options.clipSize = data.size; + self.$video.options({ + width: getPlayerWidth() + }); + self.$timeline.options({ + width: getTimelineWidth() + }); + self.$clipPanel.options({width: data.size}); + } + + function resizeendClips(data) { + that.triggerEvent('clipsize', data.size); + } + + function selectClip(data) { + self.options.selected = data.id; + if (self.options.selected) { + setPosition(data['in']); + setPoint('in', data['in']); + setPoint('out', data.out); + } + self.$clipPanel.options({selected: self.options.selected}); + that.triggerEvent('select', {id: self.options.selected}); + } + + function setPoint(point, position) { + self.options[point] = position; + self.$video.options(point, position); + self.$timeline.options(point, position); + self.$clipPanel.options(point, position); + } + + function setPosition(position, playing) { + var minute = Math.floor(position / 60), + previousMinute = Math.floor(self.options.position / 60); + self.options.position = position; + !playing && self.$video.options({position: self.options.position}); + self.$timeline.options({position: self.options.position}); + self.$clipPanel.options({position: self.options.position}); + if (!playing || minute != previousMinute) { + that.triggerEvent('position', { + position: !playing ? self.options.position : minute * 60 + }); + } + } + + function toggleClips(data) { + self.options.showClips = !data.collapsed; + self.$video.options({ + width: getPlayerWidth() + }); + self.$timeline.options({ + width: getTimelineWidth() + }); + that.triggerEvent('toggleclips', { + showClips: self.options.showAnnotations + }); + } + + function toggleControls(data) { + self.options.showTimeline = !data.collapsed; + self.$video.options({ + height: getPlayerHeight() + }); + that.triggerEvent('toggletimeline', { + showTimeline: self.options.showTimeline + }); + } + + function toggleMuted() { + self.$video.toggleMuted(); + } + + function togglePaused() { + self.$video.togglePaused(); + self.$video.options('paused') && that.triggerEvent('position', { + position: self.$video.options('position') + }); + } + + that.updateClip = function(id, data) { + self.$clipPanel.updateItem(id, data); + }; + + return that; + +};