diff --git a/source/Ox.UI/css/Ox.UI.css b/source/Ox.UI/css/Ox.UI.css
index eb61dfbe..763e38c2 100644
--- a/source/Ox.UI/css/Ox.UI.css
+++ b/source/Ox.UI/css/Ox.UI.css
@@ -627,10 +627,16 @@ OxArrayEditable
--------------------------------------------------------------------------------
*/
-.OxArrayEditable {
- //display: table-cell;
+.OxArrayEditable.OxArrayEditableInput {
padding: 4px;
}
+.OxArrayEditable.OxArrayEditableTextarea .OxEditableElement {
+ padding: 4px;
+ border-top: 1px solid rgb(128, 128, 128);
+}
+.OxArrayEditable.OxArrayEditableTextarea .OxEditableElement:first-child {
+ border-top: 0px
+}
/*
--------------------------------------------------------------------------------
diff --git a/source/Ox.UI/js/Form/Ox.ArrayEditable.js b/source/Ox.UI/js/Form/Ox.ArrayEditable.js
index bd25c07a..25869871 100644
--- a/source/Ox.UI/js/Form/Ox.ArrayEditable.js
+++ b/source/Ox.UI/js/Form/Ox.ArrayEditable.js
@@ -21,7 +21,7 @@ Ox.ArrayEditable = function(options, self) {
width: 256
})
.options(options || {})
- .addClass('OxArrayEditable')
+ .addClass('OxArrayEditable OxArrayEditable' + Ox.toTitleCase(self.options.type))
.css({width: self.options.width - 8 + 'px'}) // 2 x 4 px padding
.bindEvent({
anyclick: anyclick,
@@ -29,10 +29,10 @@ Ox.ArrayEditable = function(options, self) {
key_delete: deleteItem,
key_enter: editItem,
key_escape: selectNone,
- key_down: selectLast,
- key_left: selectPrevious,
- key_right: selectNext,
- key_up: selectFirst
+ key_down: self.options.type == 'input' ? selectLast : selectNext,
+ key_left: self.options.type == 'input' ? selectPrevious : selectFirst,
+ key_right: self.options.type == 'input' ? selectNext : selectLast,
+ key_up: self.options.type == 'input' ? selectFirst : selectPrevious
});
self.$items = [];
@@ -122,7 +122,7 @@ Ox.ArrayEditable = function(options, self) {
),
type: self.options.type,
value: item.value,
- width: self.options.type == 'input' ? 0 : self.options.width
+ width: self.options.type == 'input' ? 0 : self.options.width - 8
})
.addClass(item.id == self.options.selected ? 'OxSelected' : '')
.data({position: i})
@@ -160,7 +160,7 @@ Ox.ArrayEditable = function(options, self) {
}
that.find('.OxSelected').removeClass('OxSelected');
self.selected > -1 && self.$items[self.selected].addClass('OxSelected');
- that.triggerEvent('select', {id: self.options.selected});
+ triggerSelectEvent();
}
function selectLast() {
@@ -197,6 +197,16 @@ Ox.ArrayEditable = function(options, self) {
}
}
+ function triggerSelectEvent() {
+ if (!self.triggered) {
+ that.triggerEvent('select', {id: self.options.selected});
+ self.triggered = true;
+ setTimeout(function() {
+ self.triggered = false;
+ }, 250);
+ }
+ };
+
self.setOption = function(key, value) {
if (key == 'items') {
renderItems();
diff --git a/source/Ox.UI/js/Form/Ox.Editable.js b/source/Ox.UI/js/Form/Ox.Editable.js
index 5a9243ff..3f4b9471 100644
--- a/source/Ox.UI/js/Form/Ox.Editable.js
+++ b/source/Ox.UI/js/Form/Ox.Editable.js
@@ -87,7 +87,7 @@ Ox.Editable = function(options, self) {
//height = self.options.height || Ox.limit(self.$test.height() + 2, self.minHeight, self.maxHeight);
height = self.options.height || Math.max(self.$test.height() + 2, self.minHeight);
width = self.options.width || Ox.limit(self.$test.width() + 2, self.minWidth, self.maxWidth);
- Ox.Log('Form', self.options.width, self.$test.width(), 'wxh', width, height)
+ Ox.Log('Form', self.options.width, self.$test.html(), self.$test.width(), self.$test.height(), 'wxh', width, height)
if (self.options.type == 'input') {
self.$input.options({
width: width
@@ -184,8 +184,14 @@ Ox.Editable = function(options, self) {
function formatTestValue() {
return self.options.type == 'input'
? self.options.value.replace(/ /g, ' ')
+ : Ox.encodeHTML(self.options.value || ' ')
+ //.replace(/ /g, ' ')
+ .replace(/\n$/, '\n ')
+ .replace(/\n/g, '
')
+ /*
: Ox.parseHTML(self.options.value || ' ')
- .replace(/
$/, '
');
+ .replace(/(\n|
)$/, '
');
+ */
}
function formatValue() {
diff --git a/source/Ox.UI/js/List/Ox.List.js b/source/Ox.UI/js/List/Ox.List.js
index b6983db4..603058d5 100644
--- a/source/Ox.UI/js/List/Ox.List.js
+++ b/source/Ox.UI/js/List/Ox.List.js
@@ -1282,6 +1282,8 @@ Ox.List = function(options, self) {
}
function triggerSelectEvent() {
+ // FIXME: the first select event should fire immediately
+ // see ArrayEditable
getSelectedIds(function(ids) {
self.options.selected = ids;
setTimeout(function() {
diff --git a/source/Ox.UI/js/Video/Ox.AnnotationPanel.js b/source/Ox.UI/js/Video/Ox.AnnotationPanel.js
index b0880080..b5b59cbc 100644
--- a/source/Ox.UI/js/Video/Ox.AnnotationPanel.js
+++ b/source/Ox.UI/js/Video/Ox.AnnotationPanel.js
@@ -85,14 +85,12 @@ Ox.AnnotationPanel = function(options, self) {
selected: self.options.selected,
sort: self.sort,
submitOnBlur: false,
- width: self.options.width,
+ width: self.options.type == 'text' ? self.options.width + 8 : self.options.width,
type: self.options.type == 'text' ? 'textarea' : 'input'
})
.bindEvent({
add: function(data) {
- that.triggerEvent('add', {
- value: data.value || ''
- });
+ that.triggerEvent('add', {value: data.value || ''});
},
blur: function() {
that.triggerEvent('blur');
@@ -196,6 +194,10 @@ Ox.AnnotationPanel = function(options, self) {
self.$annotations.blurItem();
};
+ that.editItem = function(id) {
+ self.$annotations.editItem(id);
+ };
+
/*@
removeItems removeItems
@*/
diff --git a/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js b/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js
index 97860c85..d9724d02 100644
--- a/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js
+++ b/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js
@@ -31,6 +31,7 @@ Ox.BlockVideoTimeline = function(options, self) {
mousemove: mousemove
})
.bindEvent({
+ doubleclick: doubleclick,
drag: function(data) {
mousedown(data);
}
@@ -56,6 +57,7 @@ Ox.BlockVideoTimeline = function(options, self) {
'in': self.options['in'],
out: self.options.out,
results: self.options.results,
+ state: self.options.state,
subtitles: self.options.showSubtitles ? self.options.subtitles : [],
timeline: self.options.getImageURL,
width: Math.round(self.options.duration),
@@ -118,6 +120,22 @@ Ox.BlockVideoTimeline = function(options, self) {
.appendTo(self.$lines[i]);
}
+ function doubleclick(e) {
+ var position;
+ if ($(e.target).is('.OxInterface')) {
+ position = getPosition(e);
+ if (
+ self.options.state == 'selected'
+ && position >= self.options['in']
+ && position <= self.options.out
+ ) {
+ that.triggerEvent('edit');
+ } else if (self.options.state != 'editing') {
+ that.triggerEvent('select');
+ }
+ }
+ }
+
function getLines() {
return Math.ceil(self.options.duration / self.options.width);
}
@@ -138,6 +156,10 @@ Ox.BlockVideoTimeline = function(options, self) {
return subtitle;
}
+ function getTooltip(e) {
+
+ }
+
function mousedown(e) {
if ($(e.target).is('.OxInterface')) {
self.options.position = getPosition(e);
diff --git a/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js b/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js
index 889d9198..b8f31449 100644
--- a/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js
+++ b/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js
@@ -1,11 +1,17 @@
'use strict';
+/*@
+Ox.SmallVideoTimeline Small Video Timeline
+@*/
+
Ox.SmallVideoTimeline = function(options, self) {
self = self || {};
var that = Ox.Element({}, self)
.defaults({
- _offset: 0, // hack for cases where all these position: absolute elements have to go into a float: left
+ // _offset is a hack for cases where all these position: absolute
+ // elements have to go into a float: left
+ _offset: 0,
disabled: false,
duration: 0,
find: '',
@@ -53,9 +59,6 @@ Ox.SmallVideoTimeline = function(options, self) {
width: self.interfaceWidth + 'px',
height: '20px',
})
- .bind({
- mousedown: mousedown
- })
.bindEvent({
drag: function(data) {
mousedown(data);
@@ -63,7 +66,8 @@ Ox.SmallVideoTimeline = function(options, self) {
dragend: function(data) {
self.triggered = false;
mousedown(data);
- }
+ },
+ mousedown: mousedown
})
.appendTo(that);
diff --git a/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImage.js b/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImage.js
index f7048d8f..714f2b43 100644
--- a/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImage.js
+++ b/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImage.js
@@ -151,7 +151,7 @@ Ox.SmallVideoTimelineImage = function(options, self) {
Ox.loop(left, right, function(x) {
Ox.loop(top, bottom, function(y) {
var alpha = self.options.type == 'editor'
- && (y == top || y == bottom - 1) ? 192 : 64,
+ && (y == top || y == bottom - 1) ? 255 : 128,
color = rgb[[2, 3, 6].indexOf(x % 4 + y % 4) > -1 ? 0 : 1],
index = x * 4 + y * 4 * width;
data[index] = color[0];
diff --git a/source/Ox.UI/js/Video/Ox.VideoEditor.js b/source/Ox.UI/js/Video/Ox.VideoEditor.js
index 71de34e4..e2de1bae 100644
--- a/source/Ox.UI/js/Video/Ox.VideoEditor.js
+++ b/source/Ox.UI/js/Video/Ox.VideoEditor.js
@@ -69,10 +69,10 @@ Ox.VideoEditor = function(options, self) {
key_alt_shift_right: function() {
},
key_backslash: function() {
- select('subtitle');
+ selectAnnotation();
},
key_closebracket: function() {
- movePositionTo('subtitle', 1);
+ movePositionTo('annotation', 1);
},
key_comma: function() {
movePositionTo('cut', -1);
@@ -83,6 +83,20 @@ Ox.VideoEditor = function(options, self) {
key_down: function() {
movePositionBy(self.sizes.timeline[0].width);
},
+ key_enter: function() {
+ if (self.editiing) {
+ submitAnnotation();
+ } else if (self.options.selected) {
+ editAnnotation();
+ }
+ },
+ key_escape: function() {
+ if (self.editing) {
+ blurAnnotation();
+ } else if (self.options.selected) {
+ deselectAnnotation();
+ }
+ },
key_f: function() {
setTimeout(function() {
self.$findInput.focusInput(true);
@@ -101,7 +115,7 @@ Ox.VideoEditor = function(options, self) {
setPoint('out', self.options.position);
},
key_openbracket: function() {
- movePositionTo('subtitle', -1);
+ movePositionTo('annotation', -1);
},
key_p: playInToOut,
key_right: function() {
@@ -141,7 +155,7 @@ Ox.VideoEditor = function(options, self) {
movePositionBy(-self.options.position);
},
key_slash: function() {
- select('cut');
+ selectCut();
},
key_space: togglePaused,
key_up: function() {
@@ -149,6 +163,12 @@ Ox.VideoEditor = function(options, self) {
}
});
+ Ox.loop(1, self.options.layers.length + 1, function(i) {
+ that.bindEvent('key_' + i, function() {
+ addAnnotation(self.options.layers[i - 1].id);
+ });
+ });
+
Ox.extend(self, {
$player: [],
$timeline: [],
@@ -157,7 +177,7 @@ Ox.VideoEditor = function(options, self) {
margin: 8,
});
- //Ox.print('VIDEO EDITOR OPTIONS', self.options)
+ Ox.print('VIDEO EDITOR OPTIONS', self.options)
self.words = [];
Ox.forEach(Ox.count(Ox.words(self.options.subtitles.map(function(subtitle) {
@@ -306,8 +326,12 @@ Ox.VideoEditor = function(options, self) {
top: self.sizes.timeline[1].top + 'px',
})
.bindEvent({
+ edit: editAnnotation,
position: function(data) {
setPosition(data.position);
+ },
+ select: function() {
+ selectAnnotation();
}
})
.appendTo(self.$editor);
@@ -335,10 +359,7 @@ Ox.VideoEditor = function(options, self) {
)
.bindEvent({
add: function(data) {
- data.layer = layer.id;
- data['in'] = self.options['in'];
- data.out = self.options.out;
- that.triggerEvent('addannotation', data);
+ addAnnotation(layer.id);
},
blur: function() {
//Ox.print('VIDEO EDITOR BLUR FOCUSED?', self.focused)
@@ -346,13 +367,13 @@ Ox.VideoEditor = function(options, self) {
// ...
} else {
self.editing = false;
- setState();
+ setTimelineState();
this.blurItem();
}
},
edit: function() {
self.editing = true;
- setState();
+ setTimelineState();
},
remove: function(data) {
that.triggerEvent('removeannotation', {
@@ -372,7 +393,7 @@ Ox.VideoEditor = function(options, self) {
// ...
}
},
- submit: editAnnotation
+ submit: submitAnnotation
})
.appendTo(self.$annotations);
});
@@ -756,12 +777,31 @@ Ox.VideoEditor = function(options, self) {
submitFindInput(self.options.find, true);
}, 0);
- function editAnnotation(data) {
+ function addAnnotation(layer) {
+ that.triggerEvent('addannotation', {
+ 'in': self.options['in'],
+ layer: layer,
+ out: self.options.out,
+ value: ''
+ });
+ }
+
+ function blurAnnotation() {
self.editing = false;
- setState();
- data['in'] = self.options['in'];
- data.out = self.options.out;
- that.triggerEvent('editannotation', data);
+ setTimelineState();
+ getPanel(self.options.selected).blurItem(self.options.selected);
+ }
+
+ function deselectAnnotation() {
+ // FIXME: there is selectAnnotation({id: ''})
+ self.options.selected = '';
+ setTimelineState();
+ }
+
+ function editAnnotation() {
+ self.editing = true;
+ setTimelineState();
+ getPanel(self.options.selected).editItem(self.options.selected);
}
function find(query) {
@@ -778,21 +818,60 @@ Ox.VideoEditor = function(options, self) {
return results;
}
+ function getAnnotation() {
+ // Get annotation at current position
+ var annotations = [];
+ self.options.layers.forEach(function(layer) {
+ layer.items.forEach(function(item) {
+ if (
+ item['in'] <= self.options.position
+ && item.out >= self.options.position
+ ) {
+ annotations.push(item);
+ }
+ });
+ });
+ annotations.sort(function(a, b) {
+ var aValue = self.options.position - a['in'],
+ bValue = self.options.position - b['in'],
+ ret = 0;
+ if (aValue < bValue) {
+ ret = -1;
+ } else if (aValue > bValue) {
+ ret = 1;
+ } else if (a.duration < b.duration) {
+ ret = -1
+ } else if (a.duration > b.duration) {
+ ret = 1;
+ } else if (a.value < b.value) {
+ ret = -1
+ } else if (a.value > b.value) {
+ ret = 1;
+ }
+ return ret;
+ });
+ return annotations.length ? annotations[0] : {id: ''};
+ }
+
// fixme: why not goToNextPosition()?
function getNextPosition(type, direction) {
var found = false,
position = 0,
positions;
- if (type == 'cut') {
- positions = self.options.cuts;
+ if (type == 'annotation') {
+ positions = Ox.sort(Ox.unique(Ox.flatten(
+ self.options.layers.map(function(layer) {
+ return layer.items.map(function(item) {
+ return item['in'];
+ });
+ })
+ )));
+ } else if (type == 'cut') {
+ positions = Ox.merge(0, self.options.cuts, self.options.duration);
} else if (type == 'result') {
positions = self.results.map(function(v) {
return v['in'];
});
- } else if (type == 'subtitle') {
- positions = self.options.subtitles.map(function(v) {
- return v['in'];
- });
}
direction == -1 && positions.reverse();
Ox.forEach(positions, function(v) {
@@ -809,23 +888,42 @@ Ox.VideoEditor = function(options, self) {
return position;
}
+ function getPanel(annotationId) {
+ var found = false, panel;
+ Ox.forEach(self.options.layers, function(layer, i) {
+ Ox.forEach(layer.items, function(item) {
+ if (item.id == annotationId) {
+ panel = self.$annotationPanel[i];
+ found = true;
+ return false;
+ }
+ });
+ return !found;
+ });
+ return panel;
+ }
+
+ /*
function getPoints(type) {
var found = false,
points,
positions = [];
- if (type == 'cut') {
+ if (type == 'annotation') {
+
+ } else if (type == 'cut') {
positions = self.options.cuts;
- } else if (type == 'match') {
+ } else if (type == 'result') {
// ...
} else if (type == 'subtitle') {
+ // FIXME: remove? annotation?
self.options.subtitles.forEach(function(v, i) {
positions.push(v['in']);
positions.push(v.out);
});
}
positions.indexOf(0) == -1 && positions.unshift(0);
- positions.indexOf(self.options.duration) == -1 &&
- positions.push(self.options.duration);
+ positions.indexOf(self.options.duration) == -1
+ && positions.push(self.options.duration);
Ox.forEach(positions, function(v, i) {
if (v > self.options.position) {
points = [positions[i - 1], positions[i]];
@@ -835,6 +933,7 @@ Ox.VideoEditor = function(options, self) {
});
return points;
}
+ */
function getSizes(scrollbarIsVisible) {
var scrollbarWidth = Ox.UI.SCROLLBAR_SIZE,
@@ -932,12 +1031,8 @@ Ox.VideoEditor = function(options, self) {
function resizeEditor(data) {
var width = data.size - 2 * margin + 100;
resizeVideoPlayers(width);
- $timelineLarge.options({
- width: width
- });
- $timelineSmall.options({
- width: width
- });
+ $timelineLarge.options({width: width});
+ $timelineSmall.options({width: width});
}
function resizePlayers() {
@@ -955,22 +1050,44 @@ Ox.VideoEditor = function(options, self) {
function selectAnnotation(data) {
//Ox.print('VE.sA')
+ if (Ox.isUndefined(data)) {
+ data = getAnnotation();
+ }
self.editing = false;
- self.options.selected = data.id || '';
- self.options.annotationsRange != 'position' && setPosition(data['in']);
- setState();
- that.triggerEvent('select', {
- id: self.options.selected
- });
+ self.options.selected = data.id;
+ if (self.options.selected && self.options.annotationsRange != 'position') {
+ setPosition(data['in']);
+ }
if (self.options.selected) {
setPoint('in', data['in']);
setPoint('out', data.out);
+ Ox.print('???', JSON.stringify(self.options.selected), getPanel(self.options.selected))
+ getPanel(self.options.selected).options({selected: self.options.selected});
}
+ setTimelineState();
+ that.triggerEvent('select', {
+ id: self.options.selected
+ });
}
- function select(type) {
- self.options.points = getPoints(type);
- setPoints();
+ function selectCut() {
+ var points = {
+ 'in': Ox.last(self.options.cuts),
+ out: self.options.duration
+ };
+ Ox.forEach(self.options.cuts, function(cut, i) {
+ if (cut > self.options.position) {
+ points = {
+ 'in': i ? self.options.cuts[i - 1] : 0,
+ out: cut - 1 / self.options.fps
+ };
+ return false;
+ }
+ });
+ self.options.selected = '';
+ setTimelineState();
+ setPoint('in', points['in']);
+ setPoint('out', points.out);
}
function setPoint(point, position, annotation) {
@@ -980,7 +1097,7 @@ Ox.VideoEditor = function(options, self) {
// should only happen through interaction
if (self.options.selected && !self.editing) {
self.options.selected = '';
- setState();
+ setTimelineState();
}
*/
self.$player.forEach(function($player) {
@@ -1057,7 +1174,7 @@ Ox.VideoEditor = function(options, self) {
});
}
- function setState() {
+ function setTimelineState() {
self.$timeline[1].options({
state: self.editing ? 'editing'
: self.options.selected ? 'selected'
@@ -1066,6 +1183,14 @@ Ox.VideoEditor = function(options, self) {
//Ox.print('SET STATE', self.$timeline[1].options('state'))
}
+ function submitAnnotation(data) {
+ self.editing = false;
+ setTimelineState();
+ data['in'] = self.options['in'];
+ data.out = self.options.out;
+ that.triggerEvent('editannotation', data);
+ }
+
function submitFindInput(value, hasPressedEnter) {
self.options.find = value;
self.results = find(self.options.find);
diff --git a/source/Ox.UI/themes/classic/css/classic.css b/source/Ox.UI/themes/classic/css/classic.css
index 38d4af1d..495d9022 100644
--- a/source/Ox.UI/themes/classic/css/classic.css
+++ b/source/Ox.UI/themes/classic/css/classic.css
@@ -747,6 +747,9 @@ Video
background: rgb(160, 224, 160);
box-shadow: 0 0 1px rgb(96, 96, 96);
}
+.OxThemeClassic .OxAnnotationPanel .OxEditableElement textarea {
+ background: rgb(160, 224, 160);
+}
/*
================================================================================