diff --git a/demos/video/js/video.js b/demos/video/js/video.js index 0de7ac3e..62e0b560 100644 --- a/demos/video/js/video.js +++ b/demos/video/js/video.js @@ -2,8 +2,31 @@ Ox.load('UI', { debug: true, theme: 'modern' }, function() { + + var $foo, $bar; + var id = '0393109', poster = 'png/poster.png', + results = [ + {'in': 190.335, out: 192.353}, + {'in': 1185.215, out: 1188.115}, + {'in': 1784.525, out: 1785.915}, + {'in': 1786.015, out: 1787.835}, + {'in': 3087.365, out: 3090.275}, + {'in': 3704.915, out: 3706.795}, + {'in': 3706.895, out: 3709.615}, + {'in': 4061.655, out: 4063.95}, + {'in': 4531.775, out: 4533.415}, + {'in': 4946.095, out: 4948.875}, + {'in': 5292.885, out: 5294.665}, + {'in': 5381.965, out: 5383.338}, + {'in': 5385.415, out: 5389.58}, + {'in': 5434.93, out: 5436.93}, + {'in': 5437.405, out: 5439.405}, + {'in': 5749.535, out: 5751.915}, + {'in': 5780.235, out: 5782.435}, + {'in': 5881.365, out: 5886.635} + ], timeline = 'http://next.0xdb.org/' + id + '/timeline.16.png', url = 'http://next.0xdb.org/' + id + '/96p.webm', videoSize = getVideoSize(), @@ -13,9 +36,11 @@ Ox.load('UI', { enableKeyboard: true, focus: 'mouseenter', height: 192, + 'in': 3128.725, logoLink: 'http://next.0xdb.org/' + id, logoTitle: 'Watch on 0xdb', logo: 'png/logo.png', + out: 3130.725, paused: true, poster: poster, showIconOnLoad: true, @@ -51,6 +76,7 @@ Ox.load('UI', { Ox.VideoPlayer({ controls: ['play', 'playInToOut', 'mute', 'size', 'space', 'position'], externalControls: true, + find: 'brick', height: 192, 'in': 3128.725, out: 3130.725, @@ -66,8 +92,10 @@ Ox.load('UI', { top: '16px' }) .bindEvent({ - size: function() { - + position: function(data) { + $blockTimeline.options({ + position: data.position + }); } }) ]; @@ -90,7 +118,7 @@ Ox.load('UI', { resize: [100, 400] }, { - element: Ox.Element() + element: $foo = Ox.Element() } ], orientation: 'vertical' @@ -127,7 +155,7 @@ Ox.load('UI', { resize: [100, 400] }, { - element: Ox.Element() + element: $bar = Ox.Element() } ], orientation: 'vertical' @@ -135,6 +163,7 @@ Ox.load('UI', { .bindEvent({ resize: function(foo, size) { $videos[2].options({width: size - 32}); + $blockTimeline.options({width: size - 40}); } }), size: 392, @@ -160,4 +189,49 @@ Ox.load('UI', { }); } + Ox.get('srt/0393109.srt', function(srt) { + var subtitles = Ox.parseSRT(srt); + $foo.append( + Ox.SmallVideoTimelineImages({ + duration: 6336.08, + getTimelineURL: function(i) { + return 'png/timeline.16.' + i + '.png'; + }, + 'in': 1800, + out: 1900, + results: [ + {'in': 3600, out: 3700} + ], + subtitles: subtitles, + type: 'editor', + width: 392 + }) + ); + $bar.append( + $blockTimeline = Ox.BlockVideoTimeline({ + duration: 6336.08, + find: 'brick', + getTimelineURL: function(i) { + return 'png/timeline.16.' + i + '.png'; + }, + 'in': 3128.725, + out: 3130.725, + results: results, + subtitles: subtitles, + width: 353 + }) + .css({ + position: 'absolute', + left: '16px', + top: '16px' + }) + .bindEvent('position', function(data) { + $videos[2].options({ + position: data.position + }); + }) + ); + }) + + }); diff --git a/source/Ox.UI/js/Core/Ox.Element.js b/source/Ox.UI/js/Core/Ox.Element.js index 8298aa55..c22b0bfb 100644 --- a/source/Ox.UI/js/Core/Ox.Element.js +++ b/source/Ox.UI/js/Core/Ox.Element.js @@ -331,7 +331,7 @@ Ox.Element = function() { Ox.forEach(Ox.makeObject(arguments), function(data, event) { if ([ 'mousedown', 'mouserepeat', 'anyclick', 'singleclick', 'doubleclick', - 'dragstart', 'drag', 'dragpause', 'dragend', 'playing', 'progress' + 'dragstart', 'drag', 'dragpause', 'dragend', 'playing', 'position', 'progress' ].indexOf(event) == -1) { Ox.print(that.id, self.options.id, 'trigger', event, data); } diff --git a/source/Ox.UI/js/Form/Ox.Input.js b/source/Ox.UI/js/Form/Ox.Input.js index efc9f891..f95e2f64 100644 --- a/source/Ox.UI/js/Form/Ox.Input.js +++ b/source/Ox.UI/js/Form/Ox.Input.js @@ -516,7 +516,7 @@ Ox.Input = function(options, self) { Ox.UI.$document.unbind('keydown', keypress); Ox.UI.$document.unbind('keypress', keypress); } - that.triggerEvent('blur', {}); + that.triggerEvent('blur'); } function cancel() { @@ -602,6 +602,7 @@ Ox.Input = function(options, self) { Ox.UI.$document.keypress(keypress); self.options.autocompleteSelect && setTimeout(autocomplete, 0); // fixme: why is the timeout needed? } + that.triggerEvent('focus'); } function getInputWidth() { diff --git a/source/Ox.UI/js/Video/Ox.BlockTimeline.js b/source/Ox.UI/js/Video/Ox.BlockTimeline.js index b88b6dbd..a63da8e7 100644 --- a/source/Ox.UI/js/Video/Ox.BlockTimeline.js +++ b/source/Ox.UI/js/Video/Ox.BlockTimeline.js @@ -4,7 +4,6 @@ Ox.BlockTimeline = function(options, self) { var self = self || {}, that = new Ox.Element({}, self) .defaults({ - cuts: [], duration: 0, find: '', matches: [], @@ -113,7 +112,7 @@ Ox.BlockTimeline = function(options, self) { .appendTo(self.$lines[i].$element); } if (self.options.points[0] != self.options.points[1]) { - addSelection[i]; + addSelection(i); } } diff --git a/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js b/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js new file mode 100644 index 00000000..b02ca61a --- /dev/null +++ b/source/Ox.UI/js/Video/Ox.BlockVideoTimeline.js @@ -0,0 +1,246 @@ +Ox.BlockVideoTimeline = function(options, self) { + + self = self || {}; + var that = new Ox.Element({}, self) + .defaults({ + duration: 0, + find: '', + getTimelineURL: null, + 'in': 0, + out: 0, + position: 0, + results: [], + subtitles: [], + width: 0 + }) + .options(options || {}) + .css({ + position: 'absolute', + }) + .bind({ + mousedown: mousedown, + mouseleave: mouseleave, + mousemove: mousemove + }) + .bindEvent({ + drag: function(event, e) { + mousedown(e); + } + }); + + self.$images = []; + self.$interfaces = []; + self.$lines = []; + self.$tooltip = new Ox.Tooltip({ + animate: false + }).css({ + textAlign: 'center' + }); + self.height = 16; + self.lines = getLines(); + self.margin = 8; + + setCSS(); + + Ox.loop(self.lines, function(i) { + addLine(i); + }); + + self.$positionMarker = $('') + .attr({ + src: Ox.UI.PATH + 'png/videoMarkerPlay.png' + }) + .css({ + position: 'absolute', + width: '9px', + height: '5px', + zIndex: 10 + }) + .appendTo(that.$element); + setPositionMarker(); + + self.$pointMarker = {}; + ['in', 'out'].forEach(function(point) { + var titleCase = Ox.toTitleCase(point); + self.$pointMarker[point] = $('') + .addClass('OxPointMarker' + titleCase) + .attr({ + src: Ox.UI.PATH + 'png/videoMarker' + titleCase + '.png' + }) + .css({ + position: 'absolute', + width: '6px', + height: '6px', + marginLeft: (point == 'in' ? -1 : 4) + 'px', + zIndex: 10 + }) + .appendTo(that.$element); + setPointMarker(point); + }); + + function addLine(i) { + self.$lines[i] = $('
') + .css({ + position: 'absolute', + left: (self.margin / 2) + 'px', + top: i * (self.height + self.margin) + 'px', + width: self.options.width + 'px', + height: '24px', + overflow: 'hidden' + }) + .appendTo(that.$element); + self.$images[i] = Ox.SmallVideoTimelineImages({ + duration: self.options.duration, + editing: self.options.editing, + getTimelineURL: self.options.getTimelineURL, + 'in': self.options['in'], + out: self.options.out, + results: self.options.results, + subtitles: self.options.subtitles, + width: Math.ceil(self.options.duration), + type: self.options.type + }) + .css({ + position: 'absolute', + marginLeft: (-i * self.options.width) + 'px' + }) + .appendTo(self.$lines[i]); + self.$interfaces[i] = $('
').addClass('OxInterface') + .css({ + position: 'absolute', + top: '2px', + width: Math.ceil(self.options.duration) + 'px', + height: '20px', + marginLeft: (-i * self.options.width) + 'px', + zIndex: 11 + }) + .appendTo(self.$lines[i]); + } + + function getLines() { + return Math.ceil(self.options.duration / self.options.width); + } + + function getPosition(e) { + //FIXME: this might still be broken in opera according to http://acko.net/blog/mouse-handling-and-absolute-positions-in-javascript + return (e.offsetX ? e.offsetX : e.clientX - $(e.target).offset().left); + } + + function getSubtitle(position) { + var subtitle = ''; + Ox.forEach(self.options.subtitles, function(v) { + if (v['in'] <= position && v.out > position) { + subtitle = v; + return false; + } + }); + return subtitle; + } + + function mousedown(e) { + if ($(e.target).is('.OxInterface')) { + self.options.position = getPosition(e); + setPositionMarker(); + // fixme: check if this pattern is better + // than the one used for list selection + if (!self.triggered) { + Ox.print('$$$$$$$$$$$$$$$') + that.triggerEvent('position', { + position: self.options.position + }); + self.triggered = true; + setTimeout(function() { + self.triggered = false; + }, 250); + } + } + } + + function mouseleave() { + self.$tooltip.hide(); + } + + function mousemove(e) { + var position, subtitle; + if ($(e.target).is('.OxInterface')) { + position = getPosition(e); + subtitle = getSubtitle(position); + self.$tooltip.options({ + title: subtitle ? + '' + + Ox.highlight(subtitle.text, self.options.find, 'OxHighlight').replace(/\n/g, '
') + + '

' + + Ox.formatDuration(subtitle['in'], 3) + ' - ' + Ox.formatDuration(subtitle['out'], 3) : + Ox.formatDuration(position, 3) + }) + .show(e.clientX, e.clientY); + + } else { + self.$tooltip.hide(); + } + } + + function setCSS() { + that.css({ + width: (self.options.width + self.margin) + 'px', + height: ((self.height + self.margin) * self.lines) + 'px' + }); + } + + function setPointMarker(point) { + var position = Math.round(self.options[point]); + Ox.print('$$ position', position) + self.$pointMarker[point].css({ + left: (position % self.options.width) + 'px', + top: (parseInt(position / self.options.width) * + (self.height + self.margin) + 16) + 'px' + }); + } + + function setPositionMarker() { + self.$positionMarker.css({ + left: (self.options.position % self.options.width) + 'px', + top: (parseInt(self.options.position / self.options.width) * + (self.height + self.margin) + 2) + 'px' + }); + } + + function setWidth() { + self.lines = getLines(); + setCSS(); + Ox.loop(self.lines, function(i) { + if (self.$lines[i]) { + self.$lines[i].css({ + width: self.options.width + 'px' + }); + self.$images[i].css({ + marginLeft: (-i * self.options.width) + 'px' + }); + self.$interfaces[i].css({ + marginLeft: (-i * self.options.width) + 'px' + }); + } else { + addLine(i); + } + }); + while (self.$lines.length > self.lines) { + self.$lines[self.$lines.length - 1].remove(); + self.$lines.pop(); + self.$images.pop(); + } + setPositionMarker(); + setPointMarker('in'); + setPointMarker('out'); + } + + self.setOption = function(key, value) { + if (key == 'position') { + setPositionMarker(); + } else if (key == 'width') { + setWidth(); + } + } + + return that; + +}; \ No newline at end of file diff --git a/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js b/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js new file mode 100644 index 00000000..93e734c4 --- /dev/null +++ b/source/Ox.UI/js/Video/Ox.SmallVideoTimeline.js @@ -0,0 +1,54 @@ +Ox.SmallVideoTimeline = function(options, self) { + + self = self || {}; + var that = Ox.Element({}, self) + .defaults({ + duration: 0, + editing: false, + getTimelineURL: null, + 'in': 0, + out: 0, + width: 256, + type: 'player' + }) + .options(options || {}) + .addClass('OxSmallVideoTimeline') + .css({ + width: self.options.width + 'px' + }); + + self.height = self.options.type == 'player' ? 16 : 24; + self.imageHeight = self.options.type == 'player' ? 16 : 18; + self.imageTop = self.options.type == 'player' ? 0 : 3; + + that.css({ + height: self.height + 'px' + }); + + self.tooltip = Ox.Tooltip({ + animate: false + }).css({ + textAlign: 'center' + }); + + getImageURL('timeline', function(timelineURL) { + + $('') + .attr({ + src: timelineURL + }) + .css({ + width: self.options.width + 'px', + height: self.imageHeight + 'px', + top: self.imageTop + 'px' + }) + .appendTo(that.$element); + + + + }); + + + return that; + +}; \ No newline at end of file diff --git a/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImages.js b/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImages.js new file mode 100644 index 00000000..5e0df72c --- /dev/null +++ b/source/Ox.UI/js/Video/Ox.SmallVideoTimelineImages.js @@ -0,0 +1,187 @@ +Ox.SmallVideoTimelineImages = function(options, self) { + + self = self || {}; + var that = Ox.Element({}, self) + .defaults({ + duration: 0, + editing: false, + getTimelineURL: null, + 'in': 0, + out: 0, + results: [], + subtitles: [], + width: 256, + type: 'player' + }) + .options(options || {}) + .addClass('OxSmallVideoTimeline') + .css({ + position: 'absolute', + width: self.options.width + 'px' + }); + + self.height = self.options.type == 'player' ? 16 : 24; + self.imageHeight = self.options.type == 'player' ? 16 : 18; + self.imageTop = self.options.type == 'player' ? 0 : 3; + + that.css({ + height: self.height + 'px' + }); + + self.$timeline = $('') + .attr({ + src: Ox.UI.PATH + 'png/transparent.png' + }) + .css({ + position: 'absolute', + width: self.options.width + 'px', + height: self.imageHeight + 'px', + top: self.imageTop + 'px' + }) + .appendTo(that.$element); + + getImageURL('timeline', function(imageURL) { + self.$timeline.attr({ + src: imageURL + }); + }); + + self.$subtitles = $('') + .attr({ + src: getImageURL('subtitles') + }) + .css({ + position: 'absolute', + width: self.options.width + 'px', + height: self.imageHeight + 'px', + top: self.imageTop + 'px' + }) + .appendTo(that.$element); + + self.$results = $('') + .attr({ + src: getImageURL('results') + }) + .css({ + position: 'absolute', + width: self.options.width + 'px', + height: self.imageHeight + 'px', + top: self.imageTop + 'px' + }) + .appendTo(that.$element); + + self.$selection = $('') + .attr({ + src: getImageURL('selection') + }) + .css({ + position: 'absolute', + width: self.options.width + 'px', + height: self.imageHeight + 'px', + top: self.imageTop + 'px' + }) + .appendTo(that.$element); + + function getImageURL(image, callback) { + var width = Math.ceil(self.options.duration), + height = self.imageHeight, + canvas = $('') + .attr({ + width: width, + height: height + })[0], + context = canvas.getContext('2d'), + imageData = context.createImageData(width, height), + data = imageData.data; + if (image == 'results') { + var top = 0, + bottom = height; + self.options.results.forEach(function(result) { + var left = Math.round(result['in']), + right = Math.round(result.out) + 1; + Ox.loop(left, right, function(x) { + Ox.loop(top, bottom, function(y) { + var color = (y == top || y == bottom - 1) ? [255, 255, 0, 255] : [255, 255, 0, 64], + index = x * 4 + y * 4 * width; + data[index] = color[0]; + data[index + 1] = color[1]; + data[index + 2] = color[2]; + data[index + 3] = color[3]; + }); + }); + }); + } else if (image == 'selection') { + var left = Math.round(self.options['in']), + right = Math.round(self.options.out) + 1, + top = 0, + bottom = height, + rgb = self.options.editing ? [128, 255, 255] : [255, 255, 255]; + Ox.loop(left, right, function(x) { + Ox.loop(top, bottom, function(y) { + var color = [rgb[0], rgb[1], rgb[2], (y == top || y == bottom - 1) ? 255 : 64], + index = x * 4 + y * 4 * width; + data[index] = color[0]; + data[index + 1] = color[1]; + data[index + 2] = color[2]; + data[index + 3] = color[3]; + }); + }); + } else if (image == 'subtitles') { + var bottom = self.options.type == 'player' ? 14 : 15; + self.options.subtitles.forEach(function(subtitle) { + var left = Math.round(subtitle['in']), + right = Math.round(subtitle.out) + 1, + top = bottom - subtitle.text.split('\n').length - 2; + Ox.loop(left, right, function(x) { + Ox.loop(top, bottom, function(y) { + var color = (y == top || y == bottom - 1) ? [0, 0, 0] : [255, 255, 255], + index = x * 4 + y * 4 * width; + data[index] = color[0]; + data[index + 1] = color[1]; + data[index + 2] = color[2]; + data[index + 3] = 128; + }); + }); + }); + } else if (image == 'timeline') { + var counter = 0, + images = Math.ceil(self.options.duration / 3600), + top = self.options.type == 'player' ? 0 : 1; + Ox.loop(images, function(i) { + var $image = $('') + .attr({ + src: self.options.getTimelineURL(i) + }) + .load(function() { + context.drawImage($image[0], i * 3600, top); + if (++counter == images) { + callback(canvas.toDataURL()); + } + }); + }) + } + if (image != 'timeline') { + context.putImageData(imageData, 0, 0); + return canvas.toDataURL(); + } + } + + self.setOption = function(key, value) { + if (key == 'in' || key == 'out') { + self.$selection.attr({ + src: getImageURL('selection') + }); + } else if (key == 'results') { + self.$results.attr({ + src: getImageURL('results') + }); + } else if (key == 'subtitles') { + self.$subtitles.attr({ + src: getImageURL('subtitles') + }); + } + } + + return that; + +} \ No newline at end of file diff --git a/source/Ox.UI/js/Video/Ox.VideoPlayer.js b/source/Ox.UI/js/Video/Ox.VideoPlayer.js index 5e7c7fa2..a465e6e8 100644 --- a/source/Ox.UI/js/Video/Ox.VideoPlayer.js +++ b/source/Ox.UI/js/Video/Ox.VideoPlayer.js @@ -16,6 +16,7 @@ Ox.VideoPlayer Generic Video Player duration Duration (sec) enableKeyboard If true, enable keyboard controls externalControls If true, controls are outside the video + find Query string focus focus on 'click', 'load' or 'mouseover' fps Frames per second fullscreen If true, video is in fullscreen @@ -68,6 +69,7 @@ Ox.VideoPlayer = function(options, self) { duration: 86399, enableKeyboard: false, externalControls: false, + find: '', focus: 'click', fps: 25, fullscreen: false, @@ -143,7 +145,7 @@ Ox.VideoPlayer = function(options, self) { setPosition(self.options.position + self.secondsPerFrame, true); }, key_shift_f: function() { - toggleFullscreen(); + toggleFullscreen(true); }, key_space: function() { togglePaused(true); @@ -154,8 +156,14 @@ Ox.VideoPlayer = function(options, self) { if (self.options.enableKeyboard) { if (self.options.focus == 'mouseenter') { that.bind({ - mouseenter: that.gainFocus, - mouseleave: that.loseFocus + mouseenter: function() { + if (!self.inputHasFocus) { + that.gainFocus(); + } + }, + mouseleave: function() { + that.loseFocus(); + } }); } else { that.bind({ @@ -164,10 +172,21 @@ Ox.VideoPlayer = function(options, self) { } } - if ((!self.options.externalControls && self.options.controls.length) || self.options.showIcon || self.options.title) { + if ( + (!self.options.externalControls && self.options.controls.length) || + self.options.showIcon || self.options.title + ) { that.bind({ - mouseenter: showInterface, - mouseleave: hideInterface + mouseenter: function() { + showInterface(); + self.mouseHasLeft = false; + Ox.print('MOUSE HAS ENTERED') + }, + mouseleave: function() { + hideInterface(); + self.mouseHasLeft = true; + Ox.print('MOUSE HAS LEFT') + } }); } @@ -300,7 +319,7 @@ Ox.VideoPlayer = function(options, self) { } if (self.options.title) { - self.$titlebar = $('
') + self.$title = $('
') .addClass('OxInterface') .css({ position: 'absolute', @@ -388,7 +407,9 @@ Ox.VideoPlayer = function(options, self) { type: 'image' }) .css({float: 'left'}) - .bindEvent('click', toggleMuted) + .bindEvent('click', function() { + toggleMuted(); + }) .appendTo(self.$controls); } else if (control == 'volume') { @@ -420,7 +441,9 @@ Ox.VideoPlayer = function(options, self) { type: 'image' }) .css({float: 'left'}) - .bindEvent('click', toggleFullscreen) + .bindEvent('click', function() { + toggleFullscreen(); + }) .appendTo(self.$controls); } else if (control == 'size') { @@ -576,12 +599,6 @@ Ox.VideoPlayer = function(options, self) { MozBoxShadow: '0 0 0', WebkitBoxShadow: '0 0 0' }) - .bind({ - blur: function() { - }, - focus: function() { - } - }) .bindEvent({ focus: function() { self.inputHasFocus = true; @@ -618,6 +635,11 @@ Ox.VideoPlayer = function(options, self) { setSizes(); + function clearInterfaceTimeout() { + clearTimeout(self.interfaceTimeout); + self.interfaceTimeout = 0; + } + function ended() { if (!self.options.paused) { togglePaused(true); @@ -643,7 +665,7 @@ Ox.VideoPlayer = function(options, self) { function getCSS(element) { var css; - if (element == 'controls' || element == 'titlebar') { + if (element == 'controls' || element == 'title') { css = { width: self.width + 'px' }; @@ -655,11 +677,11 @@ Ox.VideoPlayer = function(options, self) { height: self.iconSize + 'px' }; } else if (element == 'logo') { - var logoHeight = Math.round(self.height / 10); - self.logoMargin = Math.round(self.height / 20); + var logoHeight = Math.round(self.height / 10), + logoMargin = Math.round(self.height / 20); css = { - left: self.logoMargin + 'px', - top: self.logoMargin + 'px', + left: logoMargin + 'px', + top: (logoMargin + !!self.titleIsVisible * 16) + 'px', height: logoHeight + 'px', }; } else if (element == 'player') { @@ -727,7 +749,7 @@ Ox.VideoPlayer = function(options, self) { }; } else if (element == 'subtitle') { css = { - bottom: parseInt(self.height / 16) + 'px', + bottom: (parseInt(self.height / 16) + !!self.controlsAreVisible * 16) + 'px', width: self.width + 'px', fontSize: parseInt(self.height / 20) + 'px', WebkitTextStroke: (self.height / 1000) + 'px rgb(0, 0, 0)' @@ -811,7 +833,7 @@ Ox.VideoPlayer = function(options, self) { v['in'] <= self.options.position && v.out > self.options.position ) { - subtitle = v.text; + subtitle = Ox.highlight(v.text, self.options.find, 'OxHighlight'); return false; } }); @@ -819,13 +841,16 @@ Ox.VideoPlayer = function(options, self) { } function hideInterface() { - if (!self.inputHasFocus) { - self.controlsTimeout = setTimeout(function() { + Ox.print('hideInterface'); + self.interfaceTimeout = setTimeout(function() { + if (!self.exitFullscreen && !self.inputHasFocus && !self.mouseIsInControls) { + self.titleIsVisible = false; + self.controlsAreVisible = false; that.find('.OxInterface').animate({ opacity: 0 }, 250); self.$logo && self.$logo.animate({ - top: self.logoMargin + 'px', + top: getCSS('logo').top, opacity: 0.25 }, 250, function() { self.options.logoLink && self.$logo.unbind('click'); @@ -833,10 +858,10 @@ Ox.VideoPlayer = function(options, self) { self.$logo.unbind('mouseenter').unbind('mouseleave'); }); self.$subtitle && self.$subtitle.animate({ - bottom: parseInt(self.options.height / 16) + 'px', - }); - }, 1000); - } + bottom: getCSS('subtitle').bottom, + }, 250); + } + }, self.options.fullscreen ? 2500 : 1000); } function hideLoadingIcon() { @@ -932,6 +957,9 @@ Ox.VideoPlayer = function(options, self) { } else { setPosition(self.options.position); } + that.triggerEvent('position', { + position: self.options.position + }); } function playInToOut() { @@ -1020,7 +1048,7 @@ Ox.VideoPlayer = function(options, self) { self.$loadingIcon.animate(getCSS('loadingIcon'), ms); self.$playIcon && self.$playIcon.animate(getCSS('playIcon'), ms); self.$subtitle && self.$subtitle.animate(getCSS('subtitle'), ms); - self.$titlebar && self.$titlebar.animate(getCSS('titlebar'), ms); + self.$title && self.$title.animate(getCSS('title'), ms); self.$controls && self.$controls.animate(getCSS('controls'), ms); if (self.$timeline) { self.$timeline.animate(getCSS('timeline'), ms); @@ -1045,12 +1073,19 @@ Ox.VideoPlayer = function(options, self) { } function showInterface() { - clearTimeout(self.controlsTimeout); + Ox.print('showInterface'); + clearTimeout(self.interfaceTimeout); + if (self.$title) { + self.titleIsVisible = true; + } + if (self.$controls) { + self.controlsAreVisible = true; + } that.find('.OxInterface').animate({ opacity: 1 }, 250); self.$logo && self.$logo.animate({ - top: self.logoMargin + 16 + 'px', + top: getCSS('logo').top, opacity: 0.5 }, 250, function() { self.options.logoLink && self.$logo @@ -1067,9 +1102,9 @@ Ox.VideoPlayer = function(options, self) { mouseleave: self.$logoTooltip.hide }); }); - self.$subtitle.animate({ - bottom: self.barHeight + parseInt(self.options.height / 16) + 'px', - }); + self.$subtitle && self.$subtitle.animate({ + bottom: getCSS('subtitle').bottom, + }, 250); } function showLoadingIcon() { @@ -1088,12 +1123,17 @@ Ox.VideoPlayer = function(options, self) { self.video.play(); self.wasPlaying = false; } + if (self.focus == 'mouseenter' && !self.mouseHasLeft) { + that.gainFocus(); + } + self.mouseHasLeft && hideInterface(); } - function toggleFullscreen() { + function toggleFullscreen(toggleButton) { var parentOffset, wasPlaying; self.options.fullscreen = !self.options.fullscreen; if (!self.options.paused) { + // video won't keep playing accross detach/append self.video.pause(); wasPlaying = true; } @@ -1114,10 +1154,36 @@ Ox.VideoPlayer = function(options, self) { .appendTo(Ox.UI.$body); setSizes(function() { wasPlaying && self.video.play(); + that.bind({ + mousemove: function() { + if (!self.mouseIsInControls) { + showInterface(); + hideInterface(); + } + } + }); + self.$controls && self.$controls.bind({ + mouseenter: function() { + self.mouseIsInControls = true; + }, + mouseleave: function() { + self.mouseIsInControls = false; + } + }); + showInterface(); + hideInterface(); }); } else { + // flag makes the animation end on absolute position self.exitFullscreen = true; + that.unbind('mousemove'); + self.$controls && self.$controls + .trigger('mouseleave') + .unbind('mouseenter') + .unbind('mouseleave'); + Ox.print('???', self.mouseIsInControls); setSizes(function() { + self.exitFullscreen = false; that.detach() .css({ left: self.relativeOffset.left + 'px', @@ -1125,17 +1191,24 @@ Ox.VideoPlayer = function(options, self) { }) .appendTo(self.$parent); wasPlaying && self.video.play(); - self.exitFullscreen = false; + self.options.enableKeyboard && that.gainFocus(); + //showInterface(); }); } + if (toggleButton && self.$fullscreenButton) { + self.$fullscreenButton.toggleTitle(); + } } - function toggleMuted() { + function toggleMuted(toggleButton) { self.options.muted = !self.options.muted; self.video.muted = self.options.muted; + if (toggleButton && self.$muteButton) { + self.$muteButton.toggleTitle(); + } } - function togglePaused(togglePlayButton) { + function togglePaused(toggleButton) { self.options.paused = !self.options.paused; self.$timeline && self.$positionMarkerRing.css({ borderColor: 'rgba(255, 255, 255, ' + (self.options.paused ? 0.5 : 1) + ')' @@ -1161,7 +1234,7 @@ Ox.VideoPlayer = function(options, self) { }, 250, togglePlayIcon); } } - if (togglePlayButton && self.$playButton) { + if (toggleButton && self.$playButton) { self.$playButton.toggleTitle(); } } @@ -1174,10 +1247,13 @@ Ox.VideoPlayer = function(options, self) { }); } - function toggleScale() { + function toggleScale(toggleButton) { self.options.scaleToFill = !self.options.scaleToFill; self.$video.animate(getCSS('video'), 250); self.$poster && self.$poster.animate(getCSS('poster'), 250); + if (toggleButton && self.$scaleButton) { + self.$scaleButton.toggleTitle(); + } } function toggleSize() { @@ -1189,18 +1265,17 @@ Ox.VideoPlayer = function(options, self) { self.setOption = function(key, value) { if (key == 'fullscreen') { - toggleFullscreen(); + toggleFullscreen(true); } else if (key == 'height' || key == 'width') { setSizes(); } else if (key == 'muted') { - toggleMuted(); - self.$muteButton && self.$muteButton.toggleTitle(); + toggleMuted(true); } else if (key == 'paused') { togglePaused(true); } else if (key == 'position') { - setPosition(value); + setPosition(value, true); } else if (key == 'scaleToFill') { - self.$video.css(getVideoCSS()); + toggleScale(true); } };