Ox.VideoPlayer = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ height: 192, position: 0, timeline: '', url: '', width: 256 }) .options(options || {}) .css({ width: self.options.width + 'px', height: self.options.height + 'px' //background: 'red' }) .bind({ mouseenter: showControls, mouseleave: hideControls }); self.buffered = []; self.controlsTimeout; self.dragTimeout; self.barHeight = 16; self.outerBarWidth = self.options.width - 96; self.innerBarWidth = self.outerBarWidth - self.barHeight; self.markerSize = 12; self.markerBorderSize = 2; self.markerOffset = -self.innerBarWidth - self.markerSize / 2; self.$video = Ox.VideoElement({ height: self.options.height, paused: true, url: self.options.url, width: self.options.width }) .css({ position: 'absolute', }) .bindEvent({ loadedmetadata: loadedmetadata, paused: function(data) { // called when playback ends self.$playButton.toggleTitle(); self.$positionMarker.css({ borderColor: 'rgb(192, 192, 192)' }); }, playing: function(data) { setPosition(data.position); }, progress: function(data) { var buffered = data.video.buffered; for (var i = 0; i < buffered.length; i++) { self.buffered[i] = [buffered.start(i), buffered.end(i)]; // fixme: firefox weirdness if (self.buffered[i][0] > self.buffered[i][1]) { self.buffered[i][0] = 0; } Ox.print(i, self.buffered[i][0], self.buffered[i][1]) } self.$buffered.attr({ src: getBufferedImageURL() }) //self.$bar.html(Ox.formatDuration(data.video.buffered.end(data.video.buffered.length - 1))) }, seeked: hideLoadingIcon, seeking: showLoadingIcon }) .appendTo(that); self.$loadingIcon = $('') .attr({ src: Ox.UI.getImagePath('symbolLoadingAnimated.svg') }) .css({ position: 'absolute', left: self.options.width / 2 - 16 + 'px', top: self.options.height / 2 - 16 + 'px', width: '32px', height: '32px' }) .appendTo(that.$element); self.$controls = Ox.Bar({ size: self.barHeight }) .css({ position: 'absolute', width: self.options.width + 'px', marginTop: self.options.height - self.barHeight + 'px', }) .appendTo(that); self.$buttons = Ox.Element() .css({ float: 'left', width: '48px' }) .appendTo(self.$controls); self.$playButton = Ox.Button({ style: 'symbol', title: [ {id: 'play', title: 'play'}, {id: 'pause', title: 'pause'} ], tooltip: ['Play', 'Pause'], type: 'image' }) .css({ borderRadius: 0 }) .bindEvent('click', togglePlay) .appendTo(self.$buttons); self.$muteButton = Ox.Button({ style: 'symbol', title: [ {id: 'mute', title: 'mute'}, {id: 'unmute', title: 'unmute'} ], tooltip: ['Mute', 'Unmute'], type: 'image' }) .css({ borderRadius: 0 }) .bindEvent('click', toggleMute) .appendTo(self.$buttons); self.$menuButton = Ox.Button({ id: 'select', style: 'symbol', title: 'select', tooltip: ['Menu'], type: 'image' }) .css({ borderRadius: 0 }) .bindEvent('click', togglePlay) .appendTo(self.$buttons); self.$outerBar = Ox.Element() .css({ float: 'left', width: self.outerBarWidth + 'px', height: self.barHeight + 'px', background: 'rgb(0, 0, 0)', borderRadius: self.barHeight / 2 + 'px' }) .appendTo(self.$controls); self.$innerBar = Ox.Element() .css({ float: 'left', width: self.innerBarWidth + 'px', height: self.barHeight + 'px', marginLeft: self.barHeight / 2 + 'px' }) .appendTo(self.$outerBar); self.$timeline = $('') .attr({ src: self.options.timeline }) .css({ float: 'left', width: self.innerBarWidth + 'px', height: self.barHeight + 'px' }) .appendTo(self.$innerBar.$element); self.$buffered = $('') .attr({ src: getBufferedImageURL() }) .css({ float: 'left', marginLeft: -self.innerBarWidth + 'px', width: self.innerBarWidth + 'px', height: self.barHeight + 'px', }) .appendTo(self.$innerBar.$element); self.$positionMarker = Ox.Element() .css({ float: 'left', width: self.markerSize - self.markerBorderSize * 2 + 'px', height: self.markerSize - self.markerBorderSize * 2 + 'px', marginTop: (self.barHeight - self.markerSize) / 2 + 'px', marginLeft: self.markerOffset + 'px', border: self.markerBorderSize + 'px solid rgb(192, 192, 192)', borderRadius: self.markerSize - self.markerBorderSize * 2 + 'px', //background: 'rgba(0, 0, 0, 0.5)', boxShadow: '0 0 ' + (self.barHeight - self.markerSize) / 2 + 'px black' }) .appendTo(self.$outerBar); self.$interfaceBar = Ox.Element() .css({ float: 'left', width: self.outerBarWidth + 'px', height: self.barHeight + 'px', marginLeft: - self.outerBarWidth + 'px' }) .appendTo(self.$controls); self.$tooltip = Ox.Tooltip({ animate: false }); self.$position = Ox.Element() .css({ float: 'left', width: '44px', height: '12px', padding: '2px', fontSize: '9px', textAlign: 'center' }) .html(Ox.formatDuration(self.options.position)) .appendTo(self.$controls) function getPosition(e) { // fixme: no offsetX in firefox??? if ($.browser.mozilla) { //Ox.print(e, e.layerX - 56) return Ox.limit( (e.layerX - 48 - self.barHeight / 2) / self.innerBarWidth * self.duration, 0, self.duration ); } else { Ox.print(e.offsetX) return Ox.limit( (e.offsetX - self.barHeight / 2) / self.innerBarWidth * self.duration, 0, self.duration ); } } function getBufferedImageURL() { var width = self.innerBarWidth, height = self.barHeight, $canvas = $('') .attr({ width: width, height: height, }), canvas = $canvas[0], context = canvas.getContext('2d'); //Ox.print(width, height) context.fillStyle = 'rgba(255, 0, 0, 0.5)'; context.fillRect(0, 0, width, height); var imageData = context.getImageData(0, 0, width, height), data = imageData.data; self.buffered.forEach(function(range) { var left = Math.round(range[0] * width / self.duration), right = Math.round(range[1] * width / self.duration); Ox.loop(left, right, function(x) { Ox.loop(height, function(y) { index = x * 4 + y * 4 * width; data[index + 3] = 0; }); }); }); context.putImageData(imageData, 0, 0); return canvas.toDataURL(); } function hideControls() { self.controlsTimeout = setTimeout(function() { self.$controls.animate({ opacity: 0 }, 250); }, 1000); } function hideLoadingIcon() { self.$loadingIcon.animate({ opacity: 0 }, 0); } function loadedmetadata(data) { //self.$position.html(Ox.formatDuration(data.video.duration)) Ox.print('!!!!', data.video.width, data.video.height, data.video.videoWidth, data.video.videoHeight) self.duration = data.video.duration; Ox.print('DURATION', Ox.formatDuration(self.duration)); hideLoadingIcon(); that.gainFocus().bindEvent({ key_space: function() { self.$playButton.toggleTitle(); togglePlay(); } }); self.$interfaceBar .bind({ mousedown: mousedownBar, mouseleave: mouseleaveBar, mousemove: mousemoveBar, }) .bindEvent({ drag: dragBar, }); } function dragBar(e) { setPosition(getPosition(e)); if (self.dragTimeout) { clearTimeout(self.dragTimeout); self.dragTimeout = 0; } self.dragTimeout = setTimeout(function() { self.$video.position(self.options.position); }, 1000); } function mousedownBar(e) { setPosition(getPosition(e)); self.$video.position(self.options.position); } function mouseleaveBar(e) { self.$tooltip.hide(); } function mousemoveBar(e) { self.$tooltip.options({ title: Ox.formatDuration(getPosition(e)) }).show(e.clientX, e.clientY); } function showControls() { clearTimeout(self.controlsTimeout); self.$controls.animate({ opacity: 1 }, 250); } function setPosition(position) { self.options.position = position; //Ox.print(-self.barWidth - 4 + self.barWidth * position / self.duration) self.$positionMarker.css({ marginLeft: self.innerBarWidth * position / self.duration + self.markerOffset + 'px', }); self.$position.html(Ox.formatDuration(position)); } function showLoadingIcon() { self.$loadingIcon.animate({ opacity: 1 }, 0); } function toggleMute() { self.$video.toggleMute(); } function togglePlay() { self.$video.togglePlay(); self.$positionMarker.css({ borderColor: self.$video.paused() ? 'rgb(192, 192, 192)' : 'rgb(255, 255, 255)' }); } return that; };