diff --git a/source/Ox.UI/css/Ox.UI.css b/source/Ox.UI/css/Ox.UI.css index c6ce1af8..eb61dfbe 100644 --- a/source/Ox.UI/css/Ox.UI.css +++ b/source/Ox.UI/css/Ox.UI.css @@ -628,7 +628,7 @@ OxArrayEditable */ .OxArrayEditable { - display: table-cell; + //display: table-cell; padding: 4px; } @@ -784,6 +784,8 @@ div.OxInput > .OxInputLabel { input.OxInput { border-width: 1px; border-style: solid; +} +div.OxInput > input.OxInput { float: left; } /* diff --git a/source/Ox.UI/js/Form/Ox.ArrayEditable.js b/source/Ox.UI/js/Form/Ox.ArrayEditable.js index 3d67728e..b0df9034 100644 --- a/source/Ox.UI/js/Form/Ox.ArrayEditable.js +++ b/source/Ox.UI/js/Form/Ox.ArrayEditable.js @@ -8,42 +8,88 @@ Ox.ArrayEditable = function(options, self) { self = self || {}; var that = Ox.Element(options.editable === false ? {} : { - tooltip: 'Doubleclick to add ' + (options.itemName : 'item') - }) + tooltip: 'Doubleclick to add ' + (options.itemName || 'item') + }, self) .defaults({ editable: true, itemName: 'item', items: [], position: -1, - selected: -1, + selected: '', width: 256 }) .options(options || {}) .addClass('OxArrayEditable') .css({width: self.options.width - 8 + 'px'}) // 2 x 4 px padding .bindEvent({ - doubleclick: doubleclick + anyclick: anyclick, + doubleclick: doubleclick, + key_delete: deleteItem, + key_enter: editItem, + key_escape: selectNone, + key_down: selectLast, + key_left: selectPrevious, + key_right: selectNext, + key_up: selectFirst }); self.$items = []; - self.values = []; + self.selected = getSelectedPosition(); + + renderItems(); + + function anyclick(e) { + var $target = $(e.target), + $parent = $target.parent(); + $target.is(':not(input)') && that.gainFocus(); + if ($parent.is('.OxEditableElement')) { + selectItem($parent.data('position')); + } else { + selectNone(); + } + } + + function deleteItem() { + self.options.items.splice(self.selected, 1); + renderItems(); + that.triggerEvent('delete', { + id: self.options.selected + }); + } function doubleclick(e) { - var $target = $(e.target); - if ($target.is('.OxEditable')) { - that.editItem($target.data('position')); + var $parent = $(e.target).parent(); + if ($parent.is('.OxEditableElement')) { + that.editItem(self.options.selected); } else { - that.addItem(position == -1 ? self.options.items.length : position); + that.triggerEvent('add'); } } + function editItem() { + if (self.selected > -1) { + Ox.forEach(self.$items, function($item) { + if ($item.data('position') == self.selected) { + $item.triggerEvent('doubleclick'); + return false; + } + }); + } + } + + function getSelectedId() { + return self.selected > -1 ? self.options.items[self.selected].id : ''; + } + + function getSelectedPosition() { + return Ox.getPositionById(self.options.items, self.options.selected); + } + function renderItems() { that.empty(); self.options.items.forEach(function(item, i) { - self.values[i] = item.value; - i && $('
') - .css({float: 'left'}) - .html(', ') + i && $('') + .html(', ') .appendTo(that); self.$items[i] = Ox.Editable({ editable: item.editable, @@ -55,49 +101,92 @@ Ox.ArrayEditable = function(options, self) { ), value: item.value }) - .css({float: 'left'}) + .addClass(item.id == self.options.selected ? 'OxSelected' : '') .data({position: i}) .bindEvent({ - anyclick: function() { - that.find('.OxSelected').removeClass('.OxSelected'); - self.$items[i].addClass('OxSelected'); - }, cancel: function(data) { }, submit: function(data) { - submit(position, data.value); + submit(i, data.value); } }) .appendTo(that); }); } - function submit(position, value) { - if (value === '') { - self.values.splice(position, 1); - } else { - Array.prototype.splice.apply(self.values, Ox.merge( - [position, 1], - value.split(',').map(function(v) { - return v.trim(); - }) - )); - } - renderItems(); + function selectFirst() { + self.selected > -1 && selectItem(0); } - that.addItem = function(position) { + function selectItem(position) { + self.selected = position; + self.options.selected = getSelectedId(); + that.selectItem(self.options.selected); + } + + function selectLast() { + self.selected > -1 && selectItem(self.options.items.length - 1); + } + + function selectNext() { + self.selected > -1 + && self.selected < self.options.items.length - 1 + && selectItem(self.selected + 1); + } + + function selectNone() { + selectItem(-1); + } + + function selectPrevious() { + self.selected > 0 && selectItem(self.selected - 1); + } + + function submit(position, value) { + var item = self.options.items[position]; + if (value === '') { + deleteItem(); + } else { + item.value != value && that.triggerEvent('submit', { + id: item.id, + value: value + }); + item.value = value; + } + } + + self.setOption = function(key, value) { + if (key == 'items') { + renderItems(); + } + } + + that.addItem = function(position, item) { + self.options.items.splice(position, 0, item); + renderItems(); + //that.triggerEvent('add'); + /* self.values = Ox.filter(values, function(value) { return value; }); self.values.push(''); renderItems(); Ox.last(self.$items).triggerEvent('doubleclick'); + */ }; that.editItem = function(position) { - + selectItem(position); + editItem(); + }; + + that.selectItem = function(id) { + self.options.selected = id; + self.selected = getSelectedPosition(); + that.find('.OxSelected').removeClass('OxSelected'); + self.selected > -1 && self.$items[self.selected].addClass('OxSelected'); + that.triggerEvent('select', {id: self.options.selected}); }; that.removeItem = function(position) { diff --git a/source/Ox.UI/js/Form/Ox.Editable.js b/source/Ox.UI/js/Form/Ox.Editable.js index eb3edf9b..8b7e3cce 100644 --- a/source/Ox.UI/js/Form/Ox.Editable.js +++ b/source/Ox.UI/js/Form/Ox.Editable.js @@ -15,7 +15,7 @@ Ox.Editable = function(options, self) { self = self || {}; var that = Ox.Element({ - element: '
', + element: options.type == 'textarea' ? '
' : '', tooltip: options.tooltip }, self) .defaults({ @@ -25,6 +25,7 @@ Ox.Editable = function(options, self) { format: null, height: 0, placeholder: '', + submitOnBlur: true, tooltip: '', value: '', width: 0, @@ -125,18 +126,19 @@ Ox.Editable = function(options, self) { .appendTo(that.$element); self.$input = Ox.Input({ changeOnKeypress: true, + element: self.options.type == 'input' ? '' : '
', style: 'square', type: self.options.type, value: formatInputValue(), }) .css(self.css) .bindEvent({ - blur: submit, cancel: cancel, change: change, submit: submit }) .appendTo(that.$element); + self.options.submitOnBlur && self.$input.bindEvent({blur: submit}); self.$input.find('input').css(self.css); } self.$input.options({ diff --git a/source/Ox.UI/js/Form/Ox.Input.js b/source/Ox.UI/js/Form/Ox.Input.js index e2c75219..83fd869f 100644 --- a/source/Ox.UI/js/Form/Ox.Input.js +++ b/source/Ox.UI/js/Form/Ox.Input.js @@ -65,7 +65,9 @@ Ox.Input Input Element Ox.Input = function(options, self) { self = self || {}; - var that = Ox.Element({}, self) + var that = Ox.Element({ + element: options.element || '
' + }, self) .defaults({ arrows: false, arrowStep: 1, diff --git a/source/Ox.UI/js/List/Ox.List.js b/source/Ox.UI/js/List/Ox.List.js index 5cfedc9a..a5fcc687 100644 --- a/source/Ox.UI/js/List/Ox.List.js +++ b/source/Ox.UI/js/List/Ox.List.js @@ -1409,25 +1409,33 @@ Ox.List = function(options, self) { } function updateSort() { - var key = self.options.sort[0].key, - map = self.options.sort[0].map, - operator = self.options.sort[0].operator, - selectedIds, - sort = {}; + var length = self.options.sort.length, + operator = [], + sort = []; //if (self.listLength > 1) { if (!self.isAsync) { getSelectedIds(function(selectedIds) { - self.options.items.forEach(function(item) { - sort[item.id] = map ? map(item[key], item) : item[key]; - }); + self.options.sort.forEach(function(v, i) { + operator.push(v.operator); + sort.push({}); + self.options.items.forEach(function(item) { + sort[i][item.id] = v.map + ? v.map(item[v.key], item) + : item[v.key] + }); + }); self.options.items.sort(function(a, b) { - var aValue = sort[a.id], - bValue = sort[b.id], - ret = 0; - if (aValue < bValue) { - ret = operator == '+' ? -1 : 1; - } else if (aValue > bValue) { - ret = operator == '+' ? 1 : -1; + var aValue, bValue, index = 0, ret = 0; + while (ret == 0 && index < length) { + aValue = sort[index][a.id]; + bValue = sort[index][b.id]; + if (aValue < bValue) { + ret = operator[index] == '+' ? -1 : 1; + } else if (aValue > bValue) { + ret = operator[index] == '+' ? 1 : -1; + } else { + index++; + } } return ret; }); diff --git a/source/Ox.UI/js/Video/Ox.AnnotationPanel.js b/source/Ox.UI/js/Video/Ox.AnnotationPanel.js index 2e5053ff..f62c615d 100644 --- a/source/Ox.UI/js/Video/Ox.AnnotationPanel.js +++ b/source/Ox.UI/js/Video/Ox.AnnotationPanel.js @@ -26,13 +26,27 @@ Ox.AnnotationPanel = function(options, self) { id: '', items: [], range: 'all', + selected: -1, + sort: 'position', title: '', type: 'text', width: 0 }) .options(options || {}); - self.selected = -1; + self.sort = self.options.sort == 'duration' ? [ + {key: 'duration', operator: '-'}, + {key: 'position', operator: '+'}, + {key: 'value', operator: '+'} + ] : self.options.sort == 'position' ? [ + {key: 'position', operator: '+'}, + {key: 'duration', operator: '-'}, + {key: 'value', operator: '+'} + ] : [ // 'text' + {key: 'value', operator: '+'}, + {key: 'position', operator: '+'}, + {key: 'duration', operator: '-'} + ]; that.setElement( Ox.CollapsePanel({ @@ -60,59 +74,86 @@ Ox.AnnotationPanel = function(options, self) { ); that.$content = that.$element.$content; - self.$annotations = Ox.List({ - construct: function(data) { - var $item = Ox.Element() - .addClass('OxAnnotation OxTarget') - .css({padding: '4px 4px 0 4px'}) - .append( - Ox.Editable({ - type: 'textarea', - width: self.options.width - 8, - value: data.value - }) - .bindEvent({ - edit: function() { - $item.removeClass('OxTarget'); - }, - submit: function(newData) { - $item.addClass('OxTarget'); - updateAnnotation({ - id: data.id, - value: newData.value - }); - } - }) - ) - .append($('
').css({height: '4px'})); - return $item; - }, - items: getAnnotations(), - max: 1, - min: 0, - sort: [{key: 'in', operator: '+'}], - type: 'none', // fixme - unique: 'id' - }) - .bindEvent({ - cancel: function(item) { - //reset in/out points - selectAnnotation({}, {ids: [item.id]}); - }, - open: function(data) { - return; - if (data.ids.length == 1) { - var pos = Ox.getPositionById(self.$annotations.options('items'), data.ids[0]); - self.$annotations.editItem(pos); - } - }, - 'delete': function(data) { - that.triggerEvent('remove', {id: data.ids[0]}); - }, - select: selectAnnotation, - submit: updateAnnotation - }) - .appendTo(that.$content); + if (self.options.type == 'event') { + self.$annotations = Ox.Element(); + } else if (self.options.type == 'place') { + self.$annotations = Ox.Element(); + } else if (self.options.type == 'string') { + self.$annotations = Ox.ArrayEditable({ + editable: self.options.editable, + items: getAnnotations(), + sort: self.sort, + width: pandora.user.ui.annotationsSize - Ox.UI.SCROLLBAR_SIZE + }) + .bindEvent({ + add: function(data) { + that.triggerEvent('add', { + value: data.value || '' + }); + }, + 'delete': function(data) { + that.triggerEvent('remove', {id: data.id}); + }, + select: function(data) { + selectAnnotation({ids: [data.id]}); + }, + submit: updateAnnotation + }); + } else if (self.options.type == 'text') { + self.$annotations = Ox.List({ + construct: function(data) { + var $item = Ox.Element() + .addClass('OxAnnotation OxTarget') + .css({padding: '4px 4px 0 4px'}) + .append( + Ox.Editable({ + type: 'textarea', + width: self.options.width - 8, + value: data.value + }) + .bindEvent({ + edit: function() { + $item.removeClass('OxTarget'); + }, + submit: function(newData) { + $item.addClass('OxTarget'); + updateAnnotation({ + id: data.id, + value: newData.value + }); + } + }) + ) + .append($('
').css({height: '4px'})); + return $item; + }, + items: getAnnotations(), + max: 1, + min: 0, + sort: self.sort, + type: 'none', // fixme + unique: 'id' + }) + .bindEvent({ + cancel: function(item) { + //reset in/out points + selectAnnotation({ids: [item.id]}); + }, + open: function(data) { + return; + if (data.ids.length == 1) { + var pos = Ox.getPositionById(self.$annotations.options('items'), data.ids[0]); + self.$annotations.editItem(pos); + } + }, + 'delete': function(data) { + that.triggerEvent('remove', {id: data.ids[0]}); + }, + select: selectAnnotation, + submit: updateAnnotation + }); + } + self.$annotations.appendTo(that.$content); /* self.$annotations = Ox.Element() @@ -139,7 +180,7 @@ Ox.AnnotationPanel = function(options, self) { self.options.range == 'position' && item['in'] <= self.options.position && item.out > self.options.position - ); + ) }); } @@ -180,6 +221,8 @@ Ox.AnnotationPanel = function(options, self) { self.$annotations.options({ items: getAnnotations() }); + } else if (key == 'sort') { + self.$annotations.options({sort: value}); } }; @@ -189,7 +232,11 @@ Ox.AnnotationPanel = function(options, self) { that.addItem = function(item) { var pos = 0; self.options.items.splice(pos, 0, item); - self.$annotations.addItems(pos, [item]); + if (self.$annotations.addItem) { + self.$annotations.addItem(pos, item); + } else { + self.$annotations.addItems(pos, [item]); + } self.$annotations.editItem(pos); }; @@ -204,7 +251,9 @@ Ox.AnnotationPanel = function(options, self) { removeItems removeItems @*/ that.removeItem = function(id) { - self.$annotations.removeItems([id]); + var pos = Ox.getPositionById(self.options.items, id); + self.options.items.splice(pos, 1); + self.$annotations.removeItems && self.$annotations.removeItems([id]); }; return that; diff --git a/source/Ox.UI/js/Video/Ox.VideoEditor.js b/source/Ox.UI/js/Video/Ox.VideoEditor.js index 9c262186..9ac65862 100644 --- a/source/Ox.UI/js/Video/Ox.VideoEditor.js +++ b/source/Ox.UI/js/Video/Ox.VideoEditor.js @@ -21,6 +21,7 @@ Ox.VideoEditor = function(options, self) { annotationsFont: 'small', annotationsRange: 'all', annotationsSize: 0, + annotationsSort: 'position', censored: [], cuts: [], duration: 0, @@ -312,6 +313,7 @@ Ox.VideoEditor = function(options, self) { out: self.options.out, position: self.options.position, range: self.options.annotationsRange, + sort: self.options.annotationsSort, width: self.options.annotationsSize - Ox.UI.SCROLLBAR_SIZE }, layer) ) @@ -637,6 +639,13 @@ Ox.VideoEditor = function(options, self) { {id: 'all', title: 'All', checked: self.options.annotationsRange == 'all'} ]}, {}, + {id: 'sortannotations', title: 'Sort Annotations', disabled: true}, + {group: 'sort', min: 1, max: 1, items: [ + {id: 'position', title: 'By Position', checked: self.options.annotationsSort == 'position'}, + {id: 'duration', title: 'By Duration', checked: self.options.annotationsSort == 'duration'}, + {id: 'text', title: 'By Text', checked: self.options.annotationsSort == 'text'} + ]}, + {}, {id: 'fontsize', title: 'Font Size', disabled: true}, {group: 'font', min: 1, max: 1, items: [ {id: 'small', title: 'Small', checked: self.options.annotationsFont == 'small'}, @@ -911,7 +920,7 @@ Ox.VideoEditor = function(options, self) { } function selectAnnotation(data) { - setPosition(data['in']); + //setPosition(data['in']); setPoint('in', data['in']); setPoint('out', data.out); } diff --git a/source/Ox/js/Array.js b/source/Ox/js/Array.js index 054b6ffd..bc052918 100644 --- a/source/Ox/js/Array.js +++ b/source/Ox/js/Array.js @@ -1,19 +1,19 @@ 'use strict'; /*@ -Ox.compact Returns an array w/o null or undefined +Ox.compact Returns an array w/o undefined values > Ox.compact([null,,1,,2,,3]) [1, 2, 3] @*/ Ox.compact = function(arr) { - return Ox.map(arr, function(val) { - return Ox.isUndefined(val) ? null : val; + return arr.filter(function(val) { + return !Ox.isNull(val) && !Ox.isUndefined(val); }); }; /*@ - Ox.flatten Flattens an array +Ox.flatten Flattens an array > Ox.flatten([1, [2, [3], 2], 1]) [1, 2, 3, 2, 1] @*/ @@ -40,11 +40,11 @@ Ox.merge Merges an array with one or more other arrays [1, 2, 3, 2, 1] @*/ Ox.merge = function(arr) { - arr = Ox.isArray(arr) ? arr : [arr]; + arr = Ox.toArray(arr); Ox.forEach(Array.prototype.slice.call(arguments, 1), function(arg) { - Ox.isArray(arg) ? Ox.forEach(arg, function(val) { + Ox.forEach(Ox.toArray(arg), function(val) { arr.push(val); - }) : arr.push(arg); + }); }); return arr; }; @@ -98,10 +98,9 @@ Ox.unique Returns an array without duplicate values > Ox.unique([NaN, NaN]) [] @*/ - Ox.unique = function(arr) { - return Ox.map(arr, function(val, i) { - return arr.indexOf(val) == i ? val : null; + return Ox.filter(arr, function(val, i) { + return arr.indexOf(val) == i; }); };