diff --git a/source/Ox.UI/js/Panel/Ox.CollapsePanel.js b/source/Ox.UI/js/Panel/Ox.CollapsePanel.js index 3fb39eaf..c2018bf1 100644 --- a/source/Ox.UI/js/Panel/Ox.CollapsePanel.js +++ b/source/Ox.UI/js/Panel/Ox.CollapsePanel.js @@ -20,6 +20,7 @@ Ox.CollapsePanel = function(options, self) { self = self || {}; var that = Ox.Panel({}, self) .defaults({ + animate: true, collapsed: false, extras: [], size: 16, @@ -92,11 +93,18 @@ Ox.CollapsePanel = function(options, self) { !self.options.collapsed && that.$content.css({ marginTop: -that.$content.height() + 'px' }).show(); - that.$content.animate({ - marginTop: marginTop + 'px' - }, 250, function() { + if (self.options.animate) { + that.$content.animate({ + marginTop: marginTop + 'px' + }, 250, function() { + self.options.collapsed && that.$content.hide(); + }); + } else { + that.$content.css({ + marginTop: marginTop + 'px' + }); self.options.collapsed && that.$content.hide(); - }); + } that.triggerEvent('toggle', { collapsed: self.options.collapsed }); @@ -110,7 +118,8 @@ Ox.CollapsePanel = function(options, self) { if (key == 'collapsed') { // will be toggled again in toggleCollapsed self.options.collapsed = !self.options.collapsed; - self.$button.trigger('click'); + self.$button.toggle(); + toggleCollapsed(); } else if (key == 'title') { self.$title.html(self.options.title); } diff --git a/source/Ox.UI/js/Video/Ox.AnnotationFolder.js b/source/Ox.UI/js/Video/Ox.AnnotationFolder.js index bfe0051a..6c826224 100644 --- a/source/Ox.UI/js/Video/Ox.AnnotationFolder.js +++ b/source/Ox.UI/js/Video/Ox.AnnotationFolder.js @@ -45,6 +45,10 @@ Ox.AnnotationFolder = function(options, self) { }) .options(options || {}); + if (self.options.selected) { + self.options.collapsed = false; + } + self.annotations = getAnnotations(); self.points = getPoints(); self.position = self.options.position; @@ -526,8 +530,11 @@ Ox.AnnotationFolder = function(options, self) { if (value === '') { self.editing = false; } + value && Ox.print('----------------- select item in folder', value, self.options.collapsed) if (value && self.options.collapsed) { - toggleLayer(); + self.$panel.options({animate: false}); + self.$panel.options({collapsed: false}); + self.$panel.options({animate: true}); } self.$annotations.options({selected: value}); } else if (key == 'sort') { diff --git a/source/Ox.UI/js/Video/Ox.AnnotationPanel.js b/source/Ox.UI/js/Video/Ox.AnnotationPanel.js index a99d055d..0e24abcc 100644 --- a/source/Ox.UI/js/Video/Ox.AnnotationPanel.js +++ b/source/Ox.UI/js/Video/Ox.AnnotationPanel.js @@ -110,6 +110,7 @@ Ox.AnnotationPanel = function(options, self) { that.triggerEvent('submit', Ox.extend({layer: layer.id}, data)); }, togglelayer: function(data) { + self.options.showLayers[layer.id] = !data.collapsed; that.triggerEvent('togglelayer', Ox.extend({layer: layer.id}, data)); }, togglewidget: function(data) { diff --git a/source/Ox.UI/js/Video/Ox.VideoPanel.js b/source/Ox.UI/js/Video/Ox.VideoPanel.js index b6db25b4..ce8d73e2 100644 --- a/source/Ox.UI/js/Video/Ox.VideoPanel.js +++ b/source/Ox.UI/js/Video/Ox.VideoPanel.js @@ -74,6 +74,7 @@ Ox.VideoPanel = function(options, self) { }); self.$video = Ox.VideoPlayer({ + annotations: getAnnotations(), censored: self.options.censored, controlsTop: ['fullscreen', 'title', 'find'], controlsBottom: ['play', 'volume', 'scale', 'timeline', 'position', 'settings'], @@ -127,6 +128,7 @@ Ox.VideoPanel = function(options, self) { scale: function(data) { that.triggerEvent('scale', data); }, + select: selectAnnotation, subtitles: function(data) { that.triggerEvent('subtitles', data); }, @@ -269,6 +271,14 @@ Ox.VideoPanel = function(options, self) { that.triggerEvent('position', {position: self.options.position}); } + function getAnnotations() { + return Ox.flatten(Ox.merge(self.options.layers.map(function(layer) { + return layer.items.map(function(item) { + return {id: item.id, 'in': item['in'], out: item.out, text: item.value}; + }); + }))).sort(sortAnnotations); + } + function getPlayerHeight() { return self.options.height - self.options.showTimeline * 80 - 1; @@ -330,6 +340,7 @@ Ox.VideoPanel = function(options, self) { setPoint('in', data['in']); setPoint('out', data.out); } + self.$annotationPanel.options({selected: self.options.selected}); that.triggerEvent('select', {id: self.options.selected}); } @@ -347,6 +358,24 @@ Ox.VideoPanel = function(options, self) { self.$annotationPanel.options({position: self.options.position}); } + function sortAnnotations(a, b) { + var ret = 0; + if (a['in'] < b['in']) { + ret = -1; + } else if (a['in'] > b['in']) { + ret = 1; + } else if (a.out < b.out) { + ret = -1; + } else if (a.out > b.out) { + ret = 1; + } else if (a.value < b.value) { + ret = -1; + } else if (a.value > b.value) { + ret = 1; + } + return ret; + } + function toggleAnnotations(data) { self.options.showAnnotations = !data.collapsed; self.$video.options({ diff --git a/source/Ox.UI/js/Video/Ox.VideoPlayer.js b/source/Ox.UI/js/Video/Ox.VideoPlayer.js index e695a84d..b27e6c4f 100644 --- a/source/Ox.UI/js/Video/Ox.VideoPlayer.js +++ b/source/Ox.UI/js/Video/Ox.VideoPlayer.js @@ -6,12 +6,11 @@ Ox.VideoPlayer Generic Video Player (options, self) -> Video Player options Options - annotation <[o]> Array of annotation tracks - name Name of the annotation track - data <[o]> Annotation data - in In point (sec) - out Out point (sec) - text Text + annotations <[]> Array of annotations + id Optional id + in In point (sec) + out Out point (sec) + text Text censored Array of censored ranges controlsBottom <[s]|[]> Bottom controls, from left to right Can be 'close', fullscreen', 'scale', 'title', 'find', 'open', @@ -60,6 +59,7 @@ Ox.VideoPlayer Generic Video Player showProgress <|false> If true, show buffering progress sizeIsLarge If true, initial state of the size control is large subtitles URL or SRT or array of subtitles + id Optional id in In point (sec) out Out point (sec) text Text @@ -146,6 +146,10 @@ Ox.VideoPlayer = function(options, self) { } }); + if (Ox.isEmpty(self.options.annotations)) { + self.options.annotations = self.options.subtitles; + } + if (Ox.isObject(self.options.video)) { self.resolutions = Ox.sort(Object.keys(self.options.video)); if (!(self.options.resolution in self.options.video)) { @@ -1160,13 +1164,17 @@ Ox.VideoPlayer = function(options, self) { var results = []; if (query.length) { query = query.toLowerCase(); - results = Ox.map(self.options.subtitles, function(subtitle) { - return subtitle.text.toLowerCase().indexOf(query) > -1 ? { - 'in': subtitle['in'], - out: subtitle.out + results = Ox.map(self.options.annotations, function(annotation) { + return Ox.decodeHTML(Ox.stripTags( + annotation.text.toLowerCase() + )).indexOf(query) > -1 ? { + id: annotation.id, + 'in': annotation['in'], + out: annotation.out } : null; }); } + Ox.print('FIND RESULTS:', results); return results; } @@ -1500,24 +1508,29 @@ Ox.VideoPlayer = function(options, self) { function goToNextResult(direction) { var found = false, - position = 0; + result; if (self.results.length) { direction == -1 && self.results.reverse(); Ox.forEach(self.results, function(v) { - if (direction == 1 ? v['in'] > self.options.position : v.out < self.options.position) { - position = v['in']; + if ( + direction == 1 + ? v['in'] > self.options.position + : v.out < self.options.position + ) { + result = v found = true; return false; } }); direction == -1 && self.results.reverse(); if (!found) { - position = self.results[direction == 1 ? 0 : self.results.length - 1]['in']; + result = self.results[direction == 1 ? 0 : self.results.length - 1]; } - setPosition(position + self.secondsPerFrame); + setPosition(result['in'] + self.secondsPerFrame); that.triggerEvent('position', { position: self.options.position }); + result.id && that.triggerEvent('select', result); } } @@ -2132,7 +2145,12 @@ Ox.VideoPlayer = function(options, self) { results: self.results }); if (hasPressedEnter) { - self.results.length ? goToNextResult(1) : self.$findInput.focusInput(true); + if (self.results.length) { + goToNextResult(1); + that.gainFocus(); + } else { + self.$findInput.focusInput(true); + } } that.triggerEvent('find', {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 990f3d7e..20e368b8 100644 --- a/source/Ox.UI/themes/classic/css/classic.css +++ b/source/Ox.UI/themes/classic/css/classic.css @@ -794,21 +794,21 @@ Video .OxThemeClassic .OxAnnotationFolder .OxArrayEditable .OxEditableElement.OxSelected .OxHighlight { background-color: transparent; background-image: -moz-repeating-linear-gradient( - -45deg, rgb(192, 192, 255) 0, rgb(192, 192, 255) 25%, + -45deg, transparent 0%, transparent 25%, rgb(255, 255, 0) 25%, rgb(255, 255, 0) 50%, - rgb(192, 192, 255) 50%, rgb(192, 192, 255) 75%, + transparent 50%, transparent 75%, rgb(255, 255, 0) 75%, rgb(255, 255, 0) 100% ); background-image: -o-repeating-linear-gradient( - -45deg, rgb(192, 192, 255) 0, rgb(192, 192, 255) 25%, + -45deg, transparent 0%, transparent 25%, rgb(255, 255, 0) 25%, rgb(255, 255, 0) 50%, - rgb(192, 192, 255) 50%, rgb(192, 192, 255) 75%, + transparent 50%, transparent 75%, rgb(255, 255, 0) 75%, rgb(255, 255, 0) 100% ); background-image: -webkit-repeating-linear-gradient( - -45deg, rgb(192, 192, 255) 0, rgb(192, 192, 255) 25%, + -45deg, transparent 0%, transparent 25%, rgb(255, 255, 0) 25%, rgb(255, 255, 0) 50%, - rgb(192, 192, 255) 50%, rgb(192, 192, 255) 75%, + transparent 50%, transparent 75%, rgb(255, 255, 0) 75%, rgb(255, 255, 0) 100% ); background-size: 4px 4px; diff --git a/source/Ox/js/String.js b/source/Ox/js/String.js index db8cf29d..8b11bd69 100644 --- a/source/Ox/js/String.js +++ b/source/Ox/js/String.js @@ -75,7 +75,9 @@ Ox.highlightHTML Highlight matches in an HTML string > Ox.highlightHTML('AT&T', 'amp', 'h') 'AT&T' > Ox.highlightHTML('a <b> c', '', 'h') - 'a <b> c' + 'a <b> c' + > Ox.highlightHTML('a
c', 'b', 'h') + 'a
c' @*/ Ox.highlightHTML = function(html, str, classname, tags) { var count = 0, @@ -83,7 +85,19 @@ Ox.highlightHTML = function(html, str, classname, tags) { isTag = false, position, positions = []; - tags = Ox.merge(tags || [], ['a', 'b', 'code', 'i', 's', 'sub', 'sup', 'u']); + //fixme: default tags should be same as in parseHTML + tags = Ox.merge(tags || [], [ + // inline formatting + 'b', 'code', 'i', 's', 'sub', 'sup', 'u', + // block formatting + 'blockquote', 'h1', 'h2', 'h3', 'p', 'pre', + // lists + 'li', 'ol', 'ul', + // tables + 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', + // other + 'a', 'br', 'img', + ]); str = Ox.encodeHTML(str).toLowerCase(); Ox.forEach(html.toLowerCase(), function(chr, i) { // check for entity or tag start