diff options
Diffstat (limited to 'chimere/static/jme/plugins')
| -rw-r--r-- | chimere/static/jme/plugins/fullwindow.js | 549 | ||||
| -rw-r--r-- | chimere/static/jme/plugins/playlist.js | 301 | ||||
| -rw-r--r-- | chimere/static/jme/plugins/poster.js | 145 | ||||
| -rw-r--r-- | chimere/static/jme/plugins/timerange.js | 165 | ||||
| -rw-r--r-- | chimere/static/jme/plugins/track.js | 403 | ||||
| -rw-r--r-- | chimere/static/jme/plugins/ui.sounds.js | 69 |
6 files changed, 1632 insertions, 0 deletions
diff --git a/chimere/static/jme/plugins/fullwindow.js b/chimere/static/jme/plugins/fullwindow.js new file mode 100644 index 0000000..46ae983 --- /dev/null +++ b/chimere/static/jme/plugins/fullwindow.js @@ -0,0 +1,549 @@ +/** + * fullwindow plugin for the jMediaelement project | http://github.com/aFarkas/jMediaelement + * @author Alexander Farkas + * Copyright 2010, Alexander Farkas + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * API: + * $('video').enterFullWindow() - enters fullWindow + * $('video').exitFullWindow() - exits fullWindow + * $('video').supportsFullWindow() - is fullwindow / position fixed supported (feature detection, not browser sniffing!) + * + * Controls: + * an element with the class 'fullscreen' generates a fullwindow-togglebutton + * + * <a class="fullscreen" role="button" tabindex="0">toggle fullscreen</a> + * + * Documentation: + * http://protofunc.com/jme/plugins/fullwindow.html + */ + +(function($){ + $.support.cssPointerEvents = ('pointerEvents' in $('<div />')[0].style); + $.support.getBoundingClientRect = ('getBoundingClientRect' in $('<div />')[0]); + + /* helper methods */ + var pos = { + relative: 1, + absolute: 1 + }, + getPosedAncestors = function(elem){ + var ret = [], + bodyReg = /^body|html$/i + ; + if(elem.jquery){ + elem = elem[0]; + } + elem = elem.parentNode; + while( elem && !bodyReg.test(elem.nodeName) ){ + if( pos[ $.curCSS(elem, 'position') ] ){ + ret.push(elem); + } + elem = elem.parentNode; + } + + return $(ret); + }; + + var zIndexReg = /zIndex/; + $.fn.storeInlineStyle = function(styles, name){ + if(!styles && !name){ + name = 'storedInlineStyle'; + } else if(typeof styles === 'string'){ + name = styles; + styles = false; + } else { + name = name || 'storedInlineStyle'; + } + + return this.each(function(){ + var data = $.data(this, name) || $.data(this, name, {}), + elemS = this.style, + elem = this + ; + + if(!styles){ + if(!data){return;} + $(this).css(data); + $.data(this, name, {}); + } else { + $.each(styles, function(prop, val){ + data[prop] = elemS[prop]; + //ie7 reports zIndex always as inline-style + if( prop === 'zIndex' && data[prop] !== '' && !$.support.style && !zIndexReg.test( elem.style.cssText ) ){ + data[prop] = ''; + } + }); + $(this).css(styles); + } + }); + }; + + var videoBaseCSS = { + position: 'fixed', + zIndex: 99999, + width: 'auto', + height: 'auto' + }, + parentsCss = { + position: 'static' + }, + bodyCSS = { + overflow: 'hidden' + }, + win = $(window), + doc = $(document), + doSize = function(winDim, max, model){ + var ret = {}; + ret[model[0]] = max[model[0]]; + ret[model[1]] = max[model[0]] * model[2]; + + ret.bottom = (winDim.height / 2) - (ret.height / 2); + ret.top = ret.bottom; + ret.left = (winDim.width / 2) - (ret.width / 2); + ret.right = ret.left; + return ret; + }, + getSize = function(rel, maxWidth, maxHeight){ + var width = win.width(), + height = win.height() + ; + + var winDim = { + width: win.width(), + height: win.height() + }, + max = { + width: maxWidth ? Math.min(winDim.width, maxWidth) : winDim.width, + height: maxHeight ? Math.min(winDim.height, maxHeight) : winDim.height + } + ; + + return doSize(winDim, max, (max.width / max.height > rel) ? ['height', 'width', rel] : ['width', 'height', 1 / rel] ); + }, + supportsFullWindow + ; + + $.each(['Top', 'Left', 'Right', 'Bottom'], function(i, name){ + videoBaseCSS['padding'+ name] = 0; + videoBaseCSS['margin'+ name] = 0; + videoBaseCSS['border'+ name +'Width'] = 0; + }); + + var windowOverlay = (function(){ + var trans = /transparent|rgba\(0, 0, 0, 0\)/, + overlay = $('<div class="fullwindow-overlay" />') + .css({ + position: 'fixed', + display: 'none', + right: 0, + bottom: 0, + top: 0, + left: 0, + zIndex: 99990 + }), + isVisible, timer + ; + + // IE7/IE8 retrigger + win.bind('resize', function(){ + if(isVisible){ + clearTimeout(timer); + timer = setTimeout(function(){ + overlay.css({top: -1, left: -1, right: -1, bottom: -1}); + setTimeout(function(){ + overlay.css({top: 0, left: 0, right: 0, bottom: 0}); + }, 1); + }, 100); + } + }); + + var pub = { + show: function(video){ + if(!overlay || isVisible){return;} + var bgCol = overlay.css('backgroundColor'), + bgImg = overlay.css('backgroundImage') + ; + if( (!bgCol || trans.test( bgCol ) ) && ( !bgImg || bgImg == 'none' ) ){ + overlay.css('backgroundColor', '#000'); + } + overlay.insertAfter(video).show(); + isVisible = true; + }, + hide: function(){ + if(!overlay || !isVisible){return;} + overlay.hide().css('backgroundColor', '').detach(); + isVisible = false; + } + }; + + return pub; + })(); + + $(function(){ + var div = $('<div style="visibility: hidden; postion: absolute; top: 0; left: 10px; padding: 10px;"><div style="position: fixed; top: 0;left: 0;" /></div>').appendTo('body'); + supportsFullWindow = !($('div', div).offset().left); + div.remove(); + }); + + + var defaultOverlayCSS = { + position: 'fixed', + zIndex: 999996 + }; + + $.fn.videoOverlay = function(o){ + o = $.extend(true, {}, $.fn.videoOverlay.defaults, o); + if( !o.video ){return this;} + o.video = $(o.video); + var overlayCSS = $.extend({}, defaultOverlayCSS, o.startCSS), + dynPos = o.position + ; + if( !$.isFunction( o.position ) ){ + $.each(o.position, function(styleName, posval){ + overlayCSS[styleName] = posval; + }); + o.position = function(css){ + var ret = {}; + for(var name in dynPos){ + ret[name] = css[name]; + } + return ret; + }; + } + + return this.each(function(){ + var overlay = $(this); + o.video + .bind({ + fullwindow: function(e, evt){ + if( !evt.isFullwindow ){ + //restore old css + overlay + .storeInlineStyle('fsstoredOverlay') + .removeClass(o.fullscreenClass) + ; + } else { + //store pre css + overlay + .storeInlineStyle(overlayCSS, 'fsstoredOverlay') + .addClass(o.fullscreenClass) + ; + + } + }, + fullwindowresize: function(e, evt){ + overlay.css( o.position(evt) ); + } + }) + ; + }); + }; + + $.fn.videoOverlay.defaults = { + video: false, + fullscreenClass: 'videooverlay-infullscreen', + startCSS: {}, + position: {} + }; + + /* + * extend jme api + */ + $.multimediaSupport.fn._extend({ + supportsFullWindow: function(){ + return supportsFullWindow; + }, + enterFullWindow: function(maxWidth, maxHeight){ + if(this.visualElem.hasClass('displays-fullscreen') || !supportsFullWindow){return;} + var data = $.data(this.element, 'mediaElemSupport'), + that = this, + curDim = { + width: this.visualElem.width(), + height: this.visualElem.height() + }, + rel = curDim.width / curDim.height, + wrapper = ( data.controlWrapper && data.controlWrapper[0]) ? data.controlWrapper : this.visualElem, + vidCss, + videoCSS + ; + + data._$fullwindowScrollPosition = { + top: win.scrollTop(), + left: win.scrollLeft() + }; + + this._posedAncestors = getPosedAncestors(wrapper[0]).storeInlineStyle(parentsCss, 'fsstoredZindexInlineStyle'); + + $('html, body') + .addClass('contains-fullscreenvideo') + .storeInlineStyle(bodyCSS, 'fsstoredInlineStyle') + ; + + if(data.controlWrapper && data.controlWrapper[0]){ + data.controlWrapper.addClass('wraps-fullscreen'); + } + + windowOverlay.show(wrapper); + + vidCss = getSize(rel, maxWidth, maxHeight); + videoCSS= $.extend({}, videoBaseCSS, vidCss); + + this.visualElem + .addClass('displays-fullscreen') + .storeInlineStyle(videoCSS, 'fsstoredInlineStyle') + ; + + doc.bind('keydown.jmefullscreen', function(e){ + if(e.keyCode === 27){ + that.exitFullWindow(); + } + }); + //IE 7 triggers resize event on enterFullWindow + setTimeout(function(){ + win.bind('resize.jmefullscreen', function(){ + vidCss = getSize(rel, maxWidth, maxHeight); + that.visualElem.css(vidCss); + $(that.element).triggerHandler('fullwindowresize', vidCss); + $(that.element).triggerHandler('resize'); + }); + $(that.element).triggerHandler('fullwindowresize', vidCss); + }, 0); + + $(this.element).addClass('displays-fullscreen'); + + this._trigger({type: 'fullwindow', isFullwindow: true, bbox: vidCss}); + $(this.element).triggerHandler('fullwindowresize', vidCss); + $(this.element).triggerHandler('resize'); + }, + exitFullWindow: function(){ + if(!this.visualElem.hasClass('displays-fullscreen') || !supportsFullWindow){return;} + var data = $.data(this.element, 'mediaElemSupport'), + that = this, + ancestors + ; + if(this._posedAncestors){ + this._posedAncestors.storeInlineStyle('fsstoredZindexInlineStyle'); + } + + $('html, body') + .css({overflow: 'auto'}) + .storeInlineStyle('fsstoredInlineStyle') + .removeClass('contains-fullscreenvideo') + ; + + this.visualElem + .storeInlineStyle('fsstoredInlineStyle') + .removeClass('displays-fullscreen') + ; + if(data.controlWrapper){ + data.controlWrapper.removeClass('wraps-fullscreen'); + } + + windowOverlay.hide(); + + win.unbind('.jmefullscreen'); + doc.unbind('.jmefullscreen'); + $(this.element).removeClass('displays-fullscreen').unbind('.jmefullscreen'); + + this._trigger({type: 'fullwindow', isFullwindow: false}); + $(this.element).triggerHandler('resize'); + if( data._$fullwindowScrollPosition ){ + win.scrollTop( data._$fullwindowScrollPosition.top ); + win.scrollLeft( data._$fullwindowScrollPosition.left ); + data._$fullwindowScrollPosition = false; + } + } + }, true); + + + /* + * extend jme controls + */ + + if ( $.fn.jmeControl ) { + var supportJmefsButton = $.fn.jmeEmbed.defaults.jwPlayer && $.support.getBoundingClientRect && $.support.cssPointerEvents; + if( $.fn.jmeEmbed.defaults.jwPlayer ){ + $.fn.jmeEmbed.defaults.jwPlayer.plugins.jmefs = $.multimediaSupport.jsPath + 'jmefs.swf'; + $(function(){ + var path = ($('script.jme-jwPlayer')[0] || {}).src; + if(path){ + $.fn.jmeEmbed.defaults.jwPlayer.plugins.jmefs = path; + } + }); + } + + var fsID = 0, + jmefsButton = { + create: function(control, video, data, o){ + if(!supportJmefsButton || !data.controlWrapper || !$.contains(data.controlWrapper[0], control[0])){return;} + var that = this, + initActive = function(){ + that.jwPlayer = data.apis.jwPlayer; + that.activate(); + }, + activate = function(){ + video + .jmeReady(initActive) + .one('jmeflashRefresh', initActive) + ; + } + ; + + this.control = control; + if(!this.control.attr('id')){ + fsID++; + this.control.attr('id', 'fs-btn-'+ fsID); + } + this.wrapper = data.controlWrapper; + this.video = video; + this.data = data; + video + .bind('apiActivated', function(e, evt){ + if(evt.api === 'jwPlayer'){ + activate(); + } + }) + .bind('apiDeActivated', function(e, evt){ + if(evt.api === 'jwPlayer'){ + that.deactivate(); + } + }) + ; + if(data.name === 'jwPlayer'){ + activate(); + } + }, + activate: function(){ + if(!this.jwPlayer.apiElem.jmefsSetButtonCursor || this.activated || this.data.name != 'jwPlayer'){return;} + this.activated = true; + var that = this, + rePos = function(){ + if(that.timer){ + clearTimeout(that.timer); + } + that.timer = setTimeout(function(){ + that.setPos(); + }, 30); + } + ; + try{ + this.jwPlayer.apiElem.jmefsSetButtonCursor(true); + } catch(e){return;} + this.control.addClass('jme-flashbutton'); + this.wrapper.addClass('jme-flashbutton-wrapper'); + if(!this.setBtnCallback){ + this.jwPlayer.apiElem.jmefsSetButtonCallback('$.fn.jmeControl.defaults.fullscreen.jmeBtn', this.video.attr('id'), this.control.attr('id')); + this.setBtnCallback = true; + } + this.video.bind('resize.jmeFSBtn', rePos); + this.wrapper.bind('DOMSubtreeModified.jmeFSBtn', rePos); + this.setPos(); + }, + deactivate: function(){ + this.activated = false; + this.control.removeClass('jme-flashbutton'); + this.wrapper.removeClass('jme-flashbutton-wrapper').unbind('DOMSubtreeModified.jmeFSBtn'); + this.video.unbind('resize.jmeFSBtn'); + if(this.timer){ + clearTimeout(this.timer); + } + if(this.jwPlayer && this.jwPlayer.apiElem && this.jwPlayer.apiElem.jmefsSetButtonSize){ + try{ + this.jwPlayer.apiElem.jmefsSetButtonSize(0, 0); + this.jwPlayer.apiElem.jmefsSetButtonPosition(-1, -1); + } catch(e){return;} + } + }, + setPos: function(){ + if(!this.jwPlayer.isAPIReady || !this.jwPlayer.apiElem.jmefsSetButtonPosition){return;} + var displayBBox = this.jwPlayer.apiElem.getBoundingClientRect(), + btnBBox = this.control[0].getBoundingClientRect() + ; + this.jwPlayer.apiElem.jmefsSetButtonPosition(btnBBox.left - displayBBox.left, btnBBox.top - displayBBox.top); + this.jwPlayer.apiElem.jmefsSetButtonSize(btnBBox.width, btnBBox.height); + } + }; + + + $.fn.jmeControl.addControl('video-box', function(control, video, data, o){ + control.videoOverlay({ + video: video, + startCSS: { + width: 'auto', + height: 'auto', + zIndex: 99998 + }, + position: { + bottom: 0, + left: 0, + right: 0, + top: 0, + wdith: 0, + height: 0 + } + }); + }); + + $.fn.jmeControl.addControl('fullscreen', function(control, video, data, o){ + + if ( !supportsFullWindow && !video.supportsFullScreen() ) { + control.addClass('fullscreen-unsupported ui-disabled'); + if(data.controlWrapper){ + data.controlWrapper.addClass('fullscreen-unsupported'); + } + return; + } + var elems = $.fn.jmeControl.getBtn(control), changeState = function(){ + if (video.hasClass('displays-fullscreen')) { + elems.text.text(elems.names[1]); + elems.title.attr('title', elems.titleText[1]); + elems.icon.addClass('ui-icon-circle-zoomout').removeClass('ui-icon-circle-zoomin'); + } + else { + elems.text.text(elems.names[0]); + elems.title.attr('title', elems.titleText[0]); + elems.icon.addClass('ui-icon-circle-zoomin').removeClass('ui-icon-circle-zoomout'); + } + }; + if (o.addThemeRoller) { + control.addClass('ui-state-default ui-corner-all'); + } + if(o.fullscreen.tryFullScreen){ + $.multimediaSupport.beget(jmefsButton).create(control, video, data, o); + } + + control.bind('ariaclick', function(){ + var isFullscreen = video.hasClass('displays-fullscreen'); + if( !isFullscreen ){ + video.play(); + } + if ( o.fullscreen.tryFullScreen && !isFullscreen && video.supportsFullScreen() && video.enterFullScreen() ){ + return; + } + if ( isFullscreen ) { + video.exitFullWindow(); + } else { + video.enterFullWindow(o.fullscreen['max-width'], o.fullscreen['max-height']); + } + + return false; + }); + changeState(); + video.bind('fullwindow', changeState); + }, + { + tryFullScreen: true, + 'max-width': false, + 'max-height': false, + jmeBtn: function(type, vid, cid){ + if(type === 'resize'){ + $(document.getElementById(vid)).triggerHandler('resize'); + } else { + $(document.getElementById(cid))[(type === 'jmefsButtonOver') ? 'addClass' : 'removeClass']('jme-over ui-state-over'); + } + } + }); + + } +})(jQuery); diff --git a/chimere/static/jme/plugins/playlist.js b/chimere/static/jme/plugins/playlist.js new file mode 100644 index 0000000..70b5b6d --- /dev/null +++ b/chimere/static/jme/plugins/playlist.js @@ -0,0 +1,301 @@ +/** + * playlist plugin for the jMediaelement project | http://github.com/aFarkas/jMediaelement + * @author Alexander Farkas + * Copyright 2010, Alexander Farkas + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Documentation: + * http://protofunc.com/jme/plugins/playlist.html + */ +(function($){ + //helpers for api + var split = /\s*\|\s*|\s*\,\s*/g, + itemSel = 'li[data-srces], li.play-item', + getItemProps = function(item){ + var props = { + label: item.attr('data-label'), + srces: [] + }, + poster = item.attr('data-poster'), + srces = $('a', item), + nameElem + ; + + if(poster){ + props.poster = $.multimediaSupport.makeAbsURI(poster); + } else { + poster = $('img', item)[0]; + if(poster){ + props.poster = poster.src; + } + } + if(!props.label){ + nameElem = $('.item-name', item); + props.label = $.trim( ( ( nameElem[0] ) ? nameElem : item ).text() ); + } + + if( srces.length ){ + $.each(srces, function(i, src){ + props.srces[i] = {src: this.href}; + var type = $.attr(src, 'type'); + if( type ){ + props.srces[i].type = type; + } + }); + + } else { + props.srces = (item.attr('data-srces') || '').split(split); + } + return props; + }, + getPlayList = function( elem ){ + var data = $.data(elem, 'mediaElemSupport'); + if( !data.playlist ){ + data.playlist = $([]); + } + return data; + }, + loadPrevNext = function(api, list, dir, autoplay){ + var items = $(itemSel, list), + index = items.index( items.filter('.ui-state-active') ) + dir + ; + if( index >= items.length || index < 0 ){ + if( !list.hasClass('loop') ){return;} + if( index >= items.length ){ + index = 0; + } else { + index = items.length - 1; + } + } + if ( items[index] ) { + list.loadPlaylistItem(items[index], items, autoplay); + } + }, + createItemString = function(_i, item){ + if( !$.isArray(item.srces) ){ + item.srces = [item.srces]; + } + + if( typeof item === 'string' ){ + item = {src: item}; + } + + var domItem = '<li class="play-item"'; + if(item.poster){ + domItem += ' data-poster="'+ item.poster +'"'; + } + + if( item.name ){ + domItem += ' data-label="'+ item.name +'"><span class="item-name">'+item.name+'</span>'; + } + + for(var i = 0, len = item.srces.length; i < len; i++){ + if(typeof item.srces[i] === 'string'){ + item.srces[i] = {src: item.srces[i]}; + } + domItem += ' <a href="'+item.srces[i].src +'"'; + + if( item.srces[i].type ){ + domItem += ' type="'+ item.srces[i].type +'"'; + } + + domItem += '>'+item.srces[i].src +'</a>'; + } + + domItem += '</li>'; + return domItem; + }, + createJDOMList = function(list){ + if(!list.length){return $([]);} + //getItemProps, + var domList = '<ul class="playlist">'; + for(var i = 0, len = list.length; i < len; i++){ + domList += createItemString(i, list[i]); + } + + domList += '</ul>'; + + return $(domList); + + } + ; + + $.each(['activatePlaylist', 'loadPlaylistItem', 'loadNextPlaylistItem', 'loadPreviousPlaylistItem'], function(i, name){ + var _name = '_'+ name; + + $.fn[name] = function(){ + var args = Array.prototype.slice.call(arguments, 0); + return this.each(function(){ + var jme = $.data(this, 'playlistFor'), + api + ; + if( jme ){ + args.unshift(this); + api = jme.getJMEAPI(); + if( name !== 'activatePlaylist' ){ + api._activatePlaylist.call(api, this); + } + } else { + api = getPlayList(this); + args.unshift(api.playlist); + api = api.apis[api.name]; + } + + if( api ){ + api[_name].apply(api, args); + } + }); + }; + }); + + $.multimediaSupport.fn._extend({ + _activatePlaylist: function(list){ + list = $(list); + var data = getPlayList(this.element); + if( data.playlist[0] === list[0] || list.hasClass('active-playlist') ){return;} + + var oldList = data.playlist.removeClass('active-playlist'); + $(itemSel, data.playlist).removeClass('ui-state-active'); + if(!list[0]){ + list = data.playlist; + } + data.playlist = list; + list.addClass('active-playlist'); + + // if we have no source, load and play ui-state-active marked or first playlist-item + var items = $(itemSel, list), + activeItem = items.filter('.ui-state-active') + ; + + if( !activeItem[0] ){ + activeItem = items; + } + + this._loadPlaylistItem(list, activeItem[0], items); + + this._trigger({ + type: 'playlistchange', + playlist: list, + oldPlaylist: oldList + }); + }, + _loadPlaylistItem: function(list, item, _items, _autoplay){ + if(!_items){ + _items = $(itemSel, list); + } + _items = $(_items); + item = $(item); + + var oldItem = _items + .filter('.ui-state-active') + .removeClass('ui-state-active'), + itemProps = getItemProps(item), + curIndex = _items.index( item ), + elem = $(this.element), + available = {next: true, prev: true} + ; + + item.addClass('ui-state-active'); + + this.loadSrc(itemProps.srces, itemProps.poster, itemProps.label ); + + this._trigger({ + type: 'playlistitemchange', + list: list, + items: _items, + props: itemProps, + currentIndex: curIndex, + currentItem: item, + previousItem: oldItem, + autoplay: _autoplay + }); + + if( _autoplay ){ + setTimeout(function(){ + elem.play(); + }, 0); + } + }, + _loadNextPlaylistItem: function(list, autoplay){ + loadPrevNext(this, list, 1, autoplay); + }, + _loadPreviousPlaylistItem: function(list, autoplay){ + loadPrevNext(this, list, -1, autoplay); + } + }); + + $.multimediaSupport.fn._extend({ + playlist: function(list, _addThemeRoller, activate){ + var elem = $(this.element); + if( !list ){ + return getPlayList(this.element).playlist; + } + + if( $.isArray(list) ){ + list = createJDOMList( list ); + } else { + list = $( list ); + } + + if(activate || !$.attr(this.element, 'srces').length ){ + this._activatePlaylist(list); + } + + if( !list.data('playlistFor') ) { + var items = $(itemSel, list); + elem + .bind('ended.playlist', function(){ + var autoplay = list.hasClass('autoplay-next'); + if( list.hasClass('active-playlist') && ( autoplay || list.hasClass('autoload-next') ) ){ + //opera is not responding + // ToDo: should delay ended event instead + setTimeout(function(){ + elem.loadNextPlaylistItem(list, autoplay); + }, 9); + } + }) + ; + + list + .delegate(itemSel, 'ariaclick', function(e){ + list.loadPlaylistItem(this, undefined, true); + e.preventDefault(); + }) + ; + + + if(_addThemeRoller){ + list.addClass('ui-corner-all ui-widget-header'); + items.addClass('ui-state-default ui-widget-content ui-corner-all'); + } + + if( !items.attr('role') ){ + items + .attr({ + role: 'button', + tabindex: '0' + }) + .find('a') + .attr({ + role: 'presentation', + tabindex: '-1' + }) + ; + } + + + list.data('playlistFor', elem); + + this._trigger({ + type: 'playlistcreated', + playlist: list + }); + } + return list; + } + }, true); + + $.fn.jmeControl.addControl('playlist', function(playlist, element, api, o){ + element.playlist( playlist, o.addThemeRoller, o.playlist.activate ); + }); +})(jQuery); diff --git a/chimere/static/jme/plugins/poster.js b/chimere/static/jme/plugins/poster.js new file mode 100644 index 0000000..a4c038c --- /dev/null +++ b/chimere/static/jme/plugins/poster.js @@ -0,0 +1,145 @@ +/** + * Simple Poster Plugin for jme + * @author Matt Dertinger + * @version 1.0.0 + * + * http://protofunc.com/jme + * http://github.com/aFarkas/jMediaelement + * + * @description + * + * HTML: + * + * <video class="player" preload="none" poster="../media/big-buck-bunny-trailer.png" controls="controls"> + * ... + * </video> + * + * OR + * + * <div class="fallback"> + * <img class="photo" src="../media/big-buck-bunny-trailer.png" alt="" data-enabled="enabled" /> + * ... + * </div> + * + * API: + * + * $('video, audio').setPosterAttribute() + * + * $('video, audio').enablePoster(index|object) + * + * $('video, audio').disablePoster(index|object) + * + * Config: + * + * Documentation: + * + */ +(function($){ + //enable posters + $(document).bind('jmeEmbed', function(e, data){ + data = data.data; + var mm = $(e.target); + mm.setPosterAttribute(); + if ($.attr(e.target, 'poster')) { + data.posterDisplay = $('<img />', { + src : $.attr(e.target, 'poster'), + "class": "poster-display inactive-poster-display", + alt : "" + }).insertAfter(e.target); + if( data.posterDisplay ){ + mm.enablePoster(data.posterDisplay, data); + } + //add fullwindow support + if(data.posterDisplay.videoOverlay && mm.is('video')){ + data.posterDisplay + .videoOverlay({ + fullscreenClass: 'poster-in-fullscreen', + video: mm, + startCSS: { + width: 'auto', + zIndex: 99999 + }, + position: { + bottom: 0, + left: 0, + right: 0 + } + }) + ; + } + mm + .bind('play', function(){ + mm.disablePoster(data.posterDisplay, data); + }) + .bind('ended', function(){ + mm.enablePoster(data.posterDisplay, data); + //worarkound: + mm.pause(); + }) + ; + } + }); + + /* + * extend jme api + */ + $.multimediaSupport.fn._extend({ + positionPoster: function(object, _data){ + object = (isFinite(object)) ? posters.filter(':eq('+ object +')') : $(object); + if( !_data ){ + _data = $.data(this.element, 'mediaElemSupport'); + } + // Only if the poster is visible + if (!_data.posterDisplay || _data.posterDisplay.is(":hidden")) { return; } + _data.posterDisplay.height($(this.element).height() + "px"); // Need incase controlsBelow + _data.posterDisplay.width($(this.element).width() + "px"); // Could probably do 100% of box + }, + disablePoster: function(object, _data){ + object = (isFinite(object)) ? posters.filter(':eq('+ object +')') : $(object); + if( !_data ){ + _data = $.data(this.element, 'mediaElemSupport'); + } + object.removeAttr('data-enabled'); + _data.posterDisplay.addClass('inactive-poster-display').fadeOut('slow'); + }, + enablePoster: function(object, _data){ + var posters = $('img.poster-display', this.element), + that = this, + mm = $(this.element), + posterData, + found + ; + + if( !_data ){ + _data = mm.data('mediaElemSupport'); + } + object = (isFinite(object)) ? posters.filter(':eq('+ object +')') : $(object); + posters + .filter('[data-enabled]') + .each(function(){ + if(this !== object[0]){ + that.disablePoster(this, _data); + } + }) + ; + if (!object[0]) { return; } + + posterData = $.data(object[0], 'jmePoster') || $.data(object[0], 'jmePoster', {load: false}); + posterData.posterDisplay = _data.posterDisplay; + posterData.posterDisplay.removeClass('inactive-poster-display').fadeIn("slow"); + // We may not need this. But if we do, we should make it respect config. + /* if (this.options.fit) { + that.positionPoster(this, _data); + } */ + object.attr('data-enabled', 'enabled'); + + }, + setPosterAttribute: function() { + if (!$.attr(this.element, 'poster')) { + var image = $('img.photo[data-enabled]', this.element).first(); + if (image) { $.attr(this.element, 'poster', image.attr('src')); } + } + } + }, true); + +})(jQuery); diff --git a/chimere/static/jme/plugins/timerange.js b/chimere/static/jme/plugins/timerange.js new file mode 100644 index 0000000..e01352b --- /dev/null +++ b/chimere/static/jme/plugins/timerange.js @@ -0,0 +1,165 @@ +/** + * timerange plugin for the jMediaelement project | http://github.com/aFarkas/jMediaelement + * @author Alexander Farkas + * Copyright 2010, Alexander Farkas + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * + * $('video').addTimeRange('timeRangeID-1', { + * enter: 1, + * leave: 12, + * callback:function(e){ + * console.log(e); + * } + * } + * ); + * + * $('video').addTimeRange('timeRangeID-1', { + * enter: 23, + * leave: 26 + * } + * ); + * + * $('video').addTimeRange('timeRangeID-2', { + * enter: 3, + * leave: 21 + * } + * ); + * + * $('video').addTimeRange('timeRangeID-2', { + * enter: 25, + * leave: 31, + * callback:function(e){ + * console.log(e); + * } + * } + * ); + */ +(function($){ + function isBetweenRange(elem, timerange, time){ + if(!timerange.active){return;} + + var e = {time: time}; + + if(!timerange.entered){ + var i = timerange.lastIndex, + len = timerange.enterRanges.length, + createEvent = function(index){ + e.rangeEnter = timerange.enterRanges[index]; + e.rangeLeave = timerange.leaveRanges[index]; + e.rangeIndex = index; + timerange.lastIndex = e.rangeIndex; + timerange.lastTime = timerange.enterRanges[index]; + e.type = 'rangeenter'; + timerange.entered = [ e.rangeEnter, e.rangeLeave, e.rangeIndex ]; + } + ; + if(timerange.lastTime > time){ + while(i--){ + if(timerange.enterRanges[i] <= time && timerange.leaveRanges[i] >= time){ + createEvent(i); + break; + } else if(timerange.leaveRanges[i] < time){ + timerange.lastIndex = i; + timerange.lastTime = timerange.enterRanges[i]; + break; + } + } + } else { + for(; i < len; i++){ + if(timerange.enterRanges[i] <= time && timerange.leaveRanges[i] >= time){ + createEvent(i); + break; + } else if(timerange.leaveRanges[i] > time){ + if(timerange.enterRanges[i] < time){ + timerange.lastIndex = i; + timerange.lastTime = timerange.leaveRanges[i]; + } + break; + } + } + } + } else if(time < timerange.entered[0] || timerange.entered[1] < time){ + e.rangeEnter = timerange.entered[0]; + e.rangeLeave = timerange.entered[1]; + e.rangeIndex = timerange.entered[2]; + e.type = 'rangeleave'; + timerange.entered = false; + } + if(e.type){ + if(timerange.callback){ + timerange.callback.call(elem, e ); + } + $(elem).triggerHandler(e); + } + + } + + var numsort = function(a, b) { + return a - b; + }; + + $.fn.addTimeRange = function(name, o){ + if(typeof o === 'boolean'){ + o = {activate: o}; + } + + o = $.extend({}, $.fn.addTimeRange.defaults, o); + + return this.each(function(){ + var api = $.data(this, 'mediaElemSupport'); + if(!api){ + return; + } + if(!api.timeRanges){ + api.timeRanges = {}; + } + + if(!api.timeRanges[name]){ + api.timeRanges[name] = { + enterRanges: [], + leaveRanges: [], + lastIndex: 0, + lastTime: 0, + entered: false, + active: false, + callback: o.callback + }; + } + + if(o.callback){ + api.timeRanges[name].callback = o.callback; + } + if( typeof o.enter !== 'boolean' && isFinite(o.enter) && typeof o.leave !== 'boolean' && isFinite(o.leave) ) { + api.timeRanges[name].enterRanges.push(o.enter); + api.timeRanges[name].leaveRanges.push(o.leave); + } + + if(o.activate && !api.timeRanges[name].active){ + api.timeRanges[name].active = true; + $(this).bind('timechange.'+name, function(e, evt){ + isBetweenRange(this, api.timeRanges[name], evt.time); + }); + } else if(!o.activate && api.timeRanges[name].active){ + api.timeRanges[name].active = false; + api.timeRanges[name].entered = false; + $(this).unbind('timechange.'+ name); + } + + + if(o.resort){ + api.timeRanges[name].enterRanges.sort(numsort); + api.timeRanges[name].leaveRanges.sort(numsort); + } + + }); + }; + + $.fn.addTimeRange.defaults = { + enter: false, + leave: false, + callback: false, + resort: false, + activate: true + }; +})(jQuery); diff --git a/chimere/static/jme/plugins/track.js b/chimere/static/jme/plugins/track.js new file mode 100644 index 0000000..742fce4 --- /dev/null +++ b/chimere/static/jme/plugins/track.js @@ -0,0 +1,403 @@ +/** + * Simple Caption Plugin for jme + * @version 1.0 + * + * http://protofunc.com/jme + * http://github.com/aFarkas/jMediaelement + * + * ------------------- + * Uses a modified Version of Silvia Pfeifers srt-parser + * http://www.annodex.net/~silvia/itext/ + * ------------------- + * + * @description + * + * HTML: + * <a class="track" href="srtfile.srt" lang="en" data-enabled="enabled" data-sanitize="sanitize" data-role="textaudiodesc">name</a> + * <a class="track" href="caption-dfxp.xml" type="application/ttaf+xml" data-enabled="enabled">name</a> + * + * API: + * + * $('video, audio').getTrackContent(index|object, callback) + * + * $('video, audio').enableTrack(index|object) + * + * $('video, audio').disableTrack(index|object) + * + * + * HTML-Display-Area: + * <video></video> + * <div class="track-display"> + * <div>Text</div> + * </div> + * and + * <div class="track-display tad-track" aria-live="assertive" style="position: absolute; left: -9999em; width: 5px; height: 5px; overflow: hidden; z-index: -100;"> + * <div>Text</div> + * </div> + * + * <video></video> + * <div class="track-display inactive-track-display"></div> + * and + * <div class="track-display tad-track inactive-track-display" aria-live="assertive" style="position: absolute; left: -9999em; width: 5px; height: 5px; overflow: hidden; z-index: -100;"></div> + * + * + * HTML-UI: + * only toggles first track on/off. for more functionality script your own UI. API is powerfull enough + * <a class="toggle-track">toggle track</a> + */ + +(function($){ + + //enable tracks + $(document).bind('jmeEmbed', function(e, data){ + data = data.data; + var mm = $(e.target), + dir = ( mm.css('direction') === 'rtl' ) ? 'right' : 'left', + activeTracks = $('a.track[data-enabled]', mm) + ; + data.trackDisplay = $('<div class="track-display inactive-track-display" style="display: none;"></div>').insertAfter(e.target); + data.tadDisplay = $('<div class="track-display tad-track inactive-track-display" aria-live="assertive" style="display: none; position: absolute; '+ dir +': -9999em; width: 5px; height: 5px; overflow: hidden; z-index: -100;"></div>').insertAfter(e.target); + data.trackDisplays = data.trackDisplay.add(data.tadDisplay); + if( activeTracks[0] ){ + mm.enableTrack(activeTracks[0], data); + } + //add fullwindow support + if(data.trackDisplay.videoOverlay && mm.is('video')){ + data.trackDisplay + .videoOverlay({ + fullscreenClass: 'track-in-fullscreen', + video: mm, + startCSS: { + width: 'auto' + }, + position: { + bottom: 0, + left: 0, + right: 0 + } + }) + ; + } + }); + + /* + * extend the api + */ + var capTypes = { + 'text/srt': ['text', 'parseSrt'], + 'application/x-srt': ['text', 'parseSrt'], + 'application/ttaf+xml': ['xml', 'parseDfxp'] + }; + $.multimediaSupport.fn._extend({ + disableTrack: function(object, _data){ + object = (isFinite(object)) ? tracks.filter(':eq('+ object +')') : $(object); + if( !_data ){ + _data = $.data(this.element, 'mediaElemSupport'); + } + object.removeAttr('data-enabled'); + $(this.element).addTimeRange(object[0].href, false); + _data.trackDisplays.addClass('inactive-track-display').hide().empty(); + this._trigger('trackChange', {track: object, enabled: false}); + }, + getTrackContent: function(object, fn, _trackData){ + object = (isFinite(object)) ? $('a.track', this.element).filter(':eq('+ object +')') : $(object); + _trackData = _trackData || $.data(object[0], 'jmeTrack') || $.data(object[0], 'jmeTrack', {load: false}); + if( !_trackData.load ){ + _trackData.load = 'loading'; + var type = object.attr('type') || 'text/srt'; + type = capTypes[type]; + if(!type){ + setTimeout( function(){ + throw("we don't know. captions type: "+ type); + }, 0); + return; + } + + $.ajax({ + url: object[0].href, + dataType: type[0], + success: function(srt){ + _trackData.load = 'loaded'; + $[type[1]]( + srt, + function(caps){ + _trackData.captions = caps; + fn( caps ); + }, + (object[0].attributes['data-sanitize'] || {}).specified + ); + } + }); + } else { + fn(trackData.captions); + } + }, + enableTrack: function(object, _data){ + var tracks = $('a.track', this.element), + that = this, + mm = $(this.element), + trackData, + found + ; + if( !_data ){ + _data = mm.data('mediaElemSupport'); + } + object = (isFinite(object)) ? tracks.filter(':eq('+ object +')') : $(object); + + tracks + .filter('[data-enabled]') + .each(function(){ + if(this !== object[0]){ + that.disableTrack(this, _data); + } + }) + ; + if( !object[0] ){return;} + + trackData = $.data(object[0], 'jmeTrack') || $.data(object[0], 'jmeTrack', {load: false}); + trackData.trackDisplay = ( object.is('[data-role=textaudiodesc]') ) ? _data.tadDisplay : _data.trackDisplay; + trackData.trackDisplay.removeClass('inactive-track-display').show(); + if( !trackData.load ){ + this.getTrackContent(object, + function(){ + var captionChange = function (e){ + e.target = mm[0]; + e = $.extend({}, e, { + target: mm[0], + captions: trackData.captions, + caption: trackData.captions[e.rangeIndex], + type: (e.type === 'rangeenter') ? 'showCaption' : 'hideCaption' + }); + if( e.type === 'showCaption' ){ + trackData.trackDisplay.html( '<div>'+ e.caption.content +'</div>' ); + } else { + trackData.trackDisplay[0].innerHTML = ''; + } + mm.triggerHandler(e.type, e); + }; + $.each(trackData.captions, function(i, caption){ + mm.addTimeRange(object[0].href, { + enter: caption.start, + leave: caption.end, + callback: captionChange, + activate: true + }); + }); + }, + trackData + ); + } else { + mm.addTimeRange(object[0].href, true); + } + object.attr('data-enabled', 'enabled'); + this._trigger('trackChange', {track: object, enabled: true, trackData: trackData}); + } + }, true); + + /* + * extend jme controls + */ + + $.fn.jmeControl.addControl('toggle-track', function(control, mm, data, o){ + var elems = $.fn.jmeControl.getBtn(control), + tracks = $('a.track', this.element), + changeState = function(){ + var enabled = tracks.filter('[data-enabled]'); + if( enabled[0] ){ + elems.text.text(elems.names[1]); + elems.title.attr('title', elems.titleText[1]); + elems.icon + .addClass('ui-icon-document') + .removeClass('ui-icon-document-b') + ; + } else { + elems.text.text(elems.names[0]); + elems.title.attr('title', elems.titleText[0]); + elems.icon + .addClass('ui-icon-document-b') + .removeClass('ui-icon-document') + ; + } + } + ; + + if(o.addThemeRoller){ + control.addClass('ui-state-default ui-corner-all'); + } + if( !tracks[0] ){ + control.addClass(o.classPrefix +'no-track'); + } + control + .bind('ariaclick', function(){ + var enabled = tracks.filter('[data-enabled]'); + if(enabled[0]){ + mm.disableTrack(enabled); + } else if( tracks[0] ) { + mm.enableTrack(tracks[0]); + } + return false; + }) + ; + changeState(); + mm.bind('trackChange', changeState); + }); + +$.backgroundEach = function(arr, processFn, completeFn){ + var i = 0, + l = arr.length + ; + var process = function(){ + var start = new Date().getTime(); + for(; i < l; i++){ + processFn(i, arr[i], arr); + if(new Date().getTime() - start > 100){ + setTimeout(process, 50); + break; + } + } + if( i >= l - 1 ){ + completeFn(arr, i, l); + } + }; + process(); +}; +$.parseDfxp = (function(){ + var sanitizeReg = /<[a-zA-Z\/][^>]*>/g; + var getTime = function(time){ + time = (time || '').split(':'); + if(time.length === 3){ + time = (parseInt(time[0], 10) * 60 * 60) + + (parseInt(time[1], 10) * 60) + + (parseInt(time[2], 10)) + ; + return isNaN(time) ? false : time; + } + return false; + }, + doc = document, + allowedNodes = { + span: 1, + div: 1, + p: 1, + em: 1, + strong: 1, + br: 1 + }, + getContent = function(elem){ + var childs = elem.childNodes, + div = doc.createElement('div'), + childElem, childContent + ; + + for(var i = 0, len = childs.length; i < len; i++){ + if(childs[i].nodeType === 3){ + div.appendChild( doc.createTextNode(childs[i].data) ); + } else if(childs[i].nodeType === 1 && allowedNodes[childs[i].nodeName.toLowerCase()]){ + childElem = doc.createElement(childs[i].nodeName); + childContent = getContent(childs[i]); + if(childContent){ + childElem.innerHTML = childContent; + } + div.appendChild( childElem ); + } + + } + return div.innerHTML; + } + ; + + + return function(xml, complete, sanitize){ + var caps = $('p, div, span', xml).filter('[begin][end]'), + captions = [] + ; + var e, s, c; + $.backgroundEach(caps, function(i){ + s = getTime(caps[i].getAttribute('begin')); + e = getTime(caps[i].getAttribute('end')); + + if(s !== false && e !== false){ + c = getContent(caps[i]) || ''; + captions.push({content: c, start: s, end: e}); + } + }, function(){ + complete(captions); + }); + }; +})(); + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is HTML5 video itext demonstration code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Silvia Pfeiffer <silvia@siliva-pfeiffer.de> + * + * ***** END LICENSE BLOCK ***** */ + +// SRT specification from http://forum.doom9.org/archive/index.php/t-73953.html +// but without the formatting, which is just interpreted as text + +// Function to parse srt file +var regs = { + sanitize: /<[a-zA-Z\/][^>]*>/g, + dosLines: /\r+/g, + index: /^\d+$/, + time: /(\d+):(\d+):(\d+)(?:,(\d+))?\s*--?>\s*(\d+):(\d+):(\d+)(?:,(\d+))?/ + +}; +$.parseSrt = function(srt, complete, sanitize) { + srt = srt.replace(regs.dosLines, ''); // remove dos newlines + srt = $.trim(srt); // trim white space start and end + if(sanitize){ + srt = srt.replace(regs.sanitize, ''); // remove all html tags for security reasons + } + + // get captions + var captions = []; + var caplist = srt.split('\n\n'); + $.backgroundEach(caplist, function(i){ + var caption = ""; + var content, start, end, s; + caption = caplist[i]; + s = caption.split(/\n/); + if (s[0].match(regs.index) && s[1]) { + // ignore caption number in s[0] + // parse time string + var m = s[1].match(regs.time); + if (m) { + start = + (parseInt(m[1], 10) * 60 * 60) + + (parseInt(m[2], 10) * 60) + + (parseInt(m[3], 10)) + + (parseInt(m[4], 10) / 1000); + end = + (parseInt(m[5], 10) * 60 * 60) + + (parseInt(m[6], 10) * 60) + + (parseInt(m[7], 10)) + + (parseInt(m[8], 10) / 1000); + content = s.slice(2).join("<br>"); + captions.push({start: start, end: end, content: content}); + } + } + + }, function(){ + complete(captions); + }); +}; +})(jQuery); diff --git a/chimere/static/jme/plugins/ui.sounds.js b/chimere/static/jme/plugins/ui.sounds.js new file mode 100644 index 0000000..1ec4a78 --- /dev/null +++ b/chimere/static/jme/plugins/ui.sounds.js @@ -0,0 +1,69 @@ +(function($){ + + var $m = $.multimediaSupport, + bgWrapper = $('<div class="jme-bg-wrapper" style="position: absolute; overflow: hidden;display: block; width: 10px; height: 10px;" />'), + appended = false + ; + + bgWrapper.css( ($('html').css('dir') === 'rtl') ? 'right' : 'left', '-99999px' ); + + $m.createBGSound = function(mediasrces, embedOpts){ + if( !appended ){ + bgWrapper + .appendTo(document.documentElement) + .bind('play playing loadedmeta pause waiting ended mediareset mute volumelevelchange', function(e){ + e.stopPropagation(); + }) + ; + appended = true; + } + + var audio = $( $.fixHTML5('<audio style="display: block; width: 10px; height: 10px;" role="presentation" tabindex="-1" preload="auto" />') ) + .appendTo(bgWrapper) + .attr('srces', mediasrces) + .jmeEmbed(embedOpts) + ; + + return audio; + }; + var _createWidget = $.Widget.prototype._createWidget; + + + $.extend(true, $.Widget.prototype, { + options: {sound: {}, soundEmbed: {}}, + _createWidget: function(){ + var ret = _createWidget.apply(this, arguments); + this._createSound(); + return ret; + }, + _soundAPI: {}, + _createSound: function(){ + var o = this.options, + element = this.element, + that = this + ; + $.each(o.sound || {}, function(type, mediasrces){ + type = ( type === that.widgetEventPrefix ? + type : + that.widgetEventPrefix + type ).toLowerCase(); + that._soundAPI[type] = $m.createBGSound(mediasrces, o.soundEmbed || {}); + element.bind(type, function(){ + that._soundAPI[type].stopAndPlay(); + }); + }); + }//, + /* todo: + option: function(){}, + destroy: function(){} + */ + + }); + + $.multimediaSupport.fn._extend({ + stopAndPlay: function(){ + this.currentTime(0); + this.play(); + } + }); + +})(jQuery); |
