webshims.register('jmebase', function($, webshims, window, doc, undefined){
"use strict";
var props = {};
var fns = {};
var slice = Array.prototype.slice;
var readyLength = 0;
var options = $.extend({selector: '.mediaplayer'}, webshims.cfg.mediaelement.jme);
var baseSelector = options.selector;
webshims.cfg.mediaelement.jme = options;
if(!$.jme){
$.jme = {};
}
$.extend($.jme, {
pluginsClasses: [],
pluginsSel: '',
plugins: {},
props: props,
fns: fns,
data: function(elem, name, value){
var data = $(elem).data('jme') || $.data(elem, 'jme', {});
if(value === undefined){
return (name) ? data[name] : data;
} else {
data[name] = value;
}
},
runPlugin: function(sel){
if(readyLength){
$(document.querySelectorAll(baseSelector)).each(function(){
var controls = this.querySelectorAll(sel);
if(controls.length){
$(this).jmeFn('addControls', controls);
}
});
}
},
registerPlugin: function(name, plugin){
this.plugins[name] = plugin;
if(!plugin.nodeName){
plugin.nodeName = '';
}
if(!plugin.className){
plugin.className = name;
}
this.pluginsClasses.push('.'+plugin.className);
this.pluginsSel = this.pluginsClasses.join(', ');
options[name] = $.extend(plugin.options || {}, options[name]);
if(options[name] && options[name].text){
plugin.text = options[name].text;
} else if(options.i18n && options.i18n[name]){
plugin.text = options.i18n[name];
}
this.runPlugin('.'+plugin.className);
},
configmenuPlugins: {},
addToConfigmenu: function(name, create){
this.configmenuPlugins[name] = create;
},
defineMethod: function(name, fn){
fns[name] = fn;
},
defineProp: function(name, desc){
if(!desc){
desc = {};
}
if(!desc.set){
if(desc.readonly){
desc.set = function(){
throw(name +' is readonly');
};
} else {
desc.set = $.noop;
}
}
if(!desc.get){
desc.get = function(elem){
return $.jme.data(elem, name);
};
}
props[name] = desc;
},
prop: function(elem, name, value){
if(!props[name]){
return $.prop(elem, name, value);
}
if(value === undefined){
return props[name].get( elem );
} else {
var setValue = props[name].set(elem, value);
if(setValue === undefined){
setValue = value;
}
if(setValue != 'noDataSet'){
$.jme.data(elem, name, setValue);
}
}
}
});
$.fn.jmeProp = function(name, value){
return $.access( this, $.jme.prop, name, value, arguments.length > 1 );
};
$.fn.jmeFn = function(fn){
var args = slice.call( arguments, 1 );
var ret;
this.each(function(){
if(!$.jme.data(this).media){
$(this).closest(baseSelector).jmePlayer();
webshims.warn('jmeFn called to early or on wrong element!');
}
ret = (fns[fn] || $.prop(this, fn)).apply(this, args);
if(ret !== undefined){
return false;
}
});
return (ret !== undefined) ? ret : this;
};
var idlStates = {
emptied: 1,
pause: 1
};
var unwaitingEvents = {
canplay: 1, canplaythrough: 1
};
$.jme.initJME = function(context, insertedElement){
readyLength += $(context.querySelectorAll(baseSelector)).add(insertedElement.filter(baseSelector)).jmePlayer().length;
};
$.jme.getDOMList = function(attr){
var list = [];
if(!attr){
attr = [];
}
if(typeof attr == 'string'){
attr = attr.split(' ');
}
$.each(attr, function(i, id){
if(id){
id = document.getElementById(id);
if(id){
list.push(id);
}
}
});
return list;
};
$.jme.getButtonText = function(button, classes){
var isCheckbox;
var lastState;
var txtChangeFn = function(state){
if(lastState === state){return;}
lastState = state;
button
.removeClass(classes[(state) ? 0 : 1])
.addClass(classes[state])
;
if(isCheckbox){
button.prop('checked', !!state);
(button.data('checkboxradio') || {refresh: $.noop}).refresh();
}
};
if (button.is('[type="checkbox"], [type="radio"]')){
button.prop('checked', function(){
return this.defaultChecked;
});
isCheckbox = true;
} else if(button.is('a')){
button.on('click', function(e){
e.preventDefault();
});
}
return txtChangeFn;
};
$.fn.jmePlayer = function(opts){
return this.each(function(){
var mediaUpdateFn, canPlay, removeCanPlay, canplayTimer, lastState, stopEmptiedEvent, forceRender;
var media = $('audio, video', this).eq(0);
var base = $(this);
var jmeData = $.jme.data(this);
var mediaData = $.jme.data(media[0]);
base.addClass(media.prop('nodeName').toLowerCase()+'player');
mediaData.player = base;
mediaData.media = media;
if(!jmeData.media){
forceRender = function(){
base[0].className = base[0].className;
};
removeCanPlay = function(){
media.off('canplay', canPlay);
clearTimeout(canplayTimer);
};
canPlay = function(){
var state = (media.prop('paused')) ? 'idle' : 'playing';
base.attr('data-state', state);
};
mediaUpdateFn = function(e){
var state = e.type;
var readyState;
var paused;
removeCanPlay();
if(unwaitingEvents[state] && lastState != 'waiting'){
return;
}
if(stopEmptiedEvent && state == 'emptied'){
return;
}
if(state == 'ended' || $.prop(this, 'ended')){
state = 'ended';
} else if(state == 'waiting'){
if($.prop(this, 'readyState') > 2){
state = '';
} else {
canplayTimer = setTimeout(function(){
if(media.prop('readyState') > 2){
canPlay();
}
}, 9);
media.on('canPlay', canPlay);
}
} else if(idlStates[state]){
state = 'idle';
} else {
readyState = $.prop(this, 'readyState');
paused = $.prop(this, 'paused');
if(!paused && readyState < 3){
state = 'waiting';
} else if(!paused && readyState > 2){
state = 'playing';
} else {
state = 'idle';
}
}
if(state == 'idle' && base._seekpause){
state = false;
}
if(state){
lastState = state;
base.attr('data-state', state);
setTimeout(forceRender);
}
};
jmeData.media = media;
jmeData.player = base;
media
.on('emptied waiting canplay canplaythrough playing ended pause mediaerror', mediaUpdateFn)
.on('volumechange updateJMEState', function(){
var volume = $.prop(this, 'volume');
base[!volume || $.prop(this, 'muted') ? 'addClass' : 'removeClass']('state-muted');
if(volume < 0.01){
volume = 'no';
} else if(volume < 0.36){
volume = 'low';
} else if(volume < 0.7){
volume = 'medium';
} else {
volume = 'high';
}
base.attr('data-volume', volume);
})
;
if($.jme.pluginsSel){
base.jmeFn('addControls', $(base[0].querySelectorAll($.jme.pluginsSel)));
}
if(mediaUpdateFn){
media.on('updateJMEState', mediaUpdateFn).triggerHandler('updateJMEState');
}
}
});
};
$.jme.defineProp('isPlaying', {
get: function(elem){
return (!$.prop(elem, 'ended') && !$.prop(elem, 'paused') && $.prop(elem, 'readyState') > 1 && !$.data(elem, 'mediaerror'));
},
readonly: true
});
$.jme.defineProp('player', {
readonly: true
});
$.jme.defineProp('media', {
readonly: true
});
$.jme.defineProp('srces', {
get: function(elem){
var srces;
var data = $.jme.data(elem);
var src = data.media.prop('src');
if(src){
return [{src: src}];
}
srces = $.map($('source', data.media).get(), function(source){
var i, len;
var src = {
src: $.prop(source, 'src')
};
var attributes = source.attributes;
for(i = 0, len = attributes.length; i < len; i++){
if(!('specified' in attributes[i]) || attributes[i].specified){
src[attributes[i].nodeName] = attributes[i].nodeValue;
}
}
return src;
});
return srces;
},
set: function(elem, srces){
var data = $.jme.data(elem);
var setSrc = function(i, src){
if(typeof src == 'string'){
src = {src: src};
}
$(document.createElement('source')).attr(src).appendTo(data.media);
};
data.media.removeAttr('src').find('source').remove();
if($.isArray(srces)){
$.each(srces, setSrc);
} else {
setSrc(0, srces);
}
data.media.jmeFn('load');
return 'noDataSet';
}
});
$.jme.defineMethod('togglePlay', function(){
$(this).jmeFn( ( props.isPlaying.get(this) ) ? 'pause' : 'play' );
});
$.jme.defineMethod('addControls', function(controls){
var data = $.jme.data(this) || {};
if(!data.media){return;}
var oldControls = $.jme.data(data.player[0], 'controlElements') || $([]);
controls = $(controls);
if($.jme.pluginsSel){
controls = controls.find($.jme.pluginsSel).add(controls.filter($.jme.pluginsSel));
}
if(controls.length){
$.each($.jme.plugins, function(name, plugin){
var control, options, i, opt;
var pluginControls = controls.filter('.'+plugin.className);
for(i = 0; i < pluginControls.length; i++){
control = $(pluginControls[i]);
options = $.jme.data(pluginControls[i]);
options.player = data.player;
options.media = data.media;
if(!options._rendered){
options._rendered = true;
if(plugin.options){
for(opt in plugin.options){
if(!(opt in options)){
options[opt] = plugin.options[opt];
}
}
}
plugin._create(control, data.media, data.player, options);
}
}
});
$.jme.data(data.player[0], 'controlElements', oldControls.add(controls));
data.player.triggerHandler('controlsadded');
}
});
webshims.ready('DOM mediaelement', function(){
webshims.isReady('jme', true);
webshims.addReady($.jme.initJME);
webshims.isReady('jme-base', true);
if(webshims.cfg.debug !== false && document.getElementsByTagName('video').length && !document.querySelector(baseSelector)){
webshims.warn("found video element but video wasn't wrapped inside a ."+ baseSelector +" element. Will not add control UI");
}
});
});
;webshims.register('mediacontrols', function($, webshims, window){
"use strict";
var pseudoClasses = 'pseudoClasses';
var options = webshims.cfg.mediaelement.jme;
var baseSelector = options.selector;
var jme = $.jme;
var unknownStructure = '
';
var btnStructure = '';
var slideStructure = '';
var timeStructure = '00:00
';
var noVolumeClass = (function(){
var audio;
var ret = '';
if(window.Audio){
try {
audio = new Audio();
audio.volume = 0.55;
ret = ((Math.round(audio.volume * 100) / 100) == 0.55) ? '' : ' no-volume-api';
} catch(e){}
}
return ret;
})();
var getBarHtml = (function(){
var cache = {};
var regTemplate = /\{\{(.+?)\}\}/igm;
return function(template, invalidCache){
if(!template){
template = options.barTemplate;
}
if(!cache[template] || invalidCache){
cache[template] = template.replace(regTemplate, function(match, matchName){
var plugin = jme.plugins[matchName];
if(plugin){
if(!plugin.structure){
webshims.warn('no structure option provided for plugin: '+ matchName +'. Fallback to standard div');
plugin.structure = unknownStructure;
}
return plugin.structure.replace('{%class%}', matchName).replace('{%text%}', plugin.text || '');
}
return match;
});
}
return cache[template] || '';
};
})();
var ios = /iP(hone|od|ad)/i.test(navigator.platform);
var ios6 = ios && parseInt(((navigator.appVersion).match(/OS (\d+)_\d+/) || ['','8'])[1], 10) < 7;
var hasYtBug = (!window.Modernizr || !Modernizr.videoautoplay) && (ios || /android/i.test(navigator.userAgent));
var loadLazy = function(){
if(!loadLazy.loaded){
loadLazy.loaded = true;
webshims.loader.loadList(['mediacontrols-lazy', 'range-ui']);
}
};
var lazyLoadPlugin = function(fn){
if(!fn){
fn = '_create';
}
var rfn = function(c, media){
var obj = this;
var args = arguments;
loadLazy();
webshims.ready('mediacontrols-lazy', function(){
if(rfn != obj[fn] && $.data(media[0])){
return obj[fn].apply(obj, args);
} else {
webshims.error('stop too much recursion');
}
});
};
return rfn;
};
webshims.loader.addModule('mediacontrols-lazy', {
src: 'jme/mediacontrols-lazy',
d: ['dom-support']
});
var userActivity = {
_create: lazyLoadPlugin()
};
jme.plugins.useractivity = userActivity;
jme.defineProp('controlbar', {
set: function(elem, value){
value = !!value;
var controls, playerSize;
var data = jme.data(elem);
var controlBar = $('div.jme-mediaoverlay, div.jme-controlbar', data.player);
var structure = '';
if(value && !controlBar[0]){
if(data._controlbar){
data._controlbar.appendTo(data.player);
} else {
if(ios6){
data.media.removeAttr('controls');
data.media.mediaLoad();
}
if(hasYtBug){
data.player.addClass('has-yt-bug');
}
data.media.prop('controls', false);
structure = getBarHtml();
data._controlbar = $( options.barStructure );
controlBar = data._controlbar.find('div.jme-cb-box').addClass('media-controls');
controls = data._controlbar.filter('.jme-media-overlay');
controls = controls.add( controlBar );
$(structure).appendTo(controlBar);
data._controlbar.appendTo(data.player);
data.player.jmeFn('addControls', controls);
playerSize = (function(){
var lastSize = {};
var sizes = [
{size: 290, name: 'xx-small', names: 's xs xxs'},
{size: 380, name: 'x-small', names: 's xs'},
{size: 478, name: 'small', names: 's'},
{size: 756, name: 'medium', names: 'm'},
{size: 1024, name: 'large', names: 'l'},
{size: Number.MAX_VALUE, name: 'x-large', names: 'l xl'}
];
var len = sizes.length;
return function(){
var size;
var i = 0;
var width = data.player.outerWidth();
var fSize = Math.max(parseInt(data.player.css('fontSize'), 10) || 16, 13);
width = width * (16 / fSize);
for(; i < len; i++){
if(sizes[i].size >= width){
size = sizes[i];
break;
}
}
if(lastSize.name != size.name){
lastSize = size;
data.player.attr('data-playersize', size.name);
data.player.attr('data-playersizes', size.names);
}
};
})();
var $poster = $('').insertAfter(data.media);
var posterState = (function(){
var lastPosterState, lastYoutubeState, lastPoster, isYt;
var hasFlash = window.swfmini && swfmini.hasFlashPlayerVersion('10.0.3');
var regYt = /youtube\.com\/[watch\?|v\/]+/i;
var isInitial = data.media.prop('paused');
var isEnded = data.media.prop('ended');
if(isInitial){
data.player.addClass('initial-state');
}
if(isEnded){
data.player.addClass('ended-state');
}
if(!('backgroundSize' in $poster[0].style)){
data.player.addClass('no-backgroundsize');
}
data.media.on('play playing waiting seeked seeking', function(e){
if(!e){
e.type = 'playing';
}
if(isInitial && (!isYt || !hasYtBug || e.type == 'playing' || data.media.prop('readyState') > 1)){
isInitial = false;
data.player.removeClass('initial-state');
}
if(isEnded){
isEnded = false;
data.player.removeClass('ended-state');
}
});
data.media.on('ended', function(){
if(!isEnded && !data.media.prop('loop') && data.media.prop('ended')){
isEnded = true;
data.player.addClass('ended-state');
}
});
return function(){
var hasYt;
var poster = data.media.attr('poster');
var hasPoster = !!poster;
var currentSrc = data.media.prop('currentSrc') || '';
isYt = regYt.test(currentSrc);
hasYt = (hasFlash && hasPoster) ? false : isYt;
if(!hasPoster && isYt){
poster = currentSrc.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/ ]{11})/i) || '';
if(poster){
poster = 'https://img.youtube.com/vi/'+ poster[1] +'/0.jpg';
hasPoster = !!poster;
}
}
if(lastPoster !== poster){
lastPoster = poster;
$poster[0].style.backgroundImage = poster ? 'url('+poster+')' : '';
}
if(lastPosterState !== hasPoster){
lastPosterState = hasPoster;
data.player[hasPoster ? 'removeClass' : 'addClass']('no-poster');
}
if(data.media.prop('paused')){
data.player.addClass('initial-state');
isInitial = true;
}
if(isEnded){
isEnded = false;
data.player.removeClass('ended-state');
}
//https://code.google.com/p/gdata-issues/issues/detail?id=5415
data.player[isYt ? 'addClass' : 'removeClass']('yt-video');
if(lastYoutubeState !== hasYt){
lastYoutubeState = hasYt;
data.player[hasYt ? 'addClass' : 'removeClass']('has-ytposter');
}
};
})();
userActivity._create(data.player, data.media, data.player);
data.media.on('emptied loadstart', function(){
setTimeout(posterState);
});
playerSize();
posterState();
webshims.ready('dom-support', function(){
data.player.onWSOff('updateshadowdom', playerSize);
controls.add(data._controlbar).add($poster).addClass(webshims.shadowClass);
webshims.addShadowDom();
});
}
} else if(!value) {
controlBar.detach();
}
return value;
}
});
jme.registerPlugin('play-pause', {
structure: btnStructure,
text: 'play / pause',
_create: lazyLoadPlugin()
});
jme.registerPlugin('mute-unmute', {
structure: btnStructure,
text: 'mute / unmute',
_create: lazyLoadPlugin()
});
jme.registerPlugin('jme-media-overlay', {
_create: lazyLoadPlugin()
});
jme.registerPlugin('volume-slider', {
structure: slideStructure,
text: 'volume level',
_create: lazyLoadPlugin()
});
jme.registerPlugin('time-slider', {
structure: slideStructure,
options: {
format: ['mm', 'ss']
},
text: 'time position',
_create: lazyLoadPlugin()
});
jme.defineProp('format', {
set: function(elem, format){
if(!$.isArray(format)){
format = format.split(':');
}
var data = jme.data(elem);
data.format = format;
$(elem).triggerHandler('updatetimeformat');
data.player.triggerHandler('updatetimeformat');
return 'noDataSet';
}
});
jme.registerPlugin('duration-display', {
structure: timeStructure,
options: {
format: "mm:ss"
},
_create: lazyLoadPlugin()
});
jme.defineProp('countdown', {
set: function(elem, value){
var data = jme.data(elem);
data.countdown = !!value;
$(elem).triggerHandler('updatetimeformat');
data.player.triggerHandler('updatetimeformat');
return 'noDataSet';
}
});
jme.registerPlugin('currenttime-display', {
structure: timeStructure,
options: {
format: "mm:ss",
countdown: false
},
_create: lazyLoadPlugin()
});
/**
* Added Poster Plugin
* @author mderting
*/
/*
* the old technique wasn't fully bullet proof
* beside this, jme2 adovactes to use the new improved state-classes to handle visual effect on specific state (see CSS change)
*/
jme.registerPlugin('poster-display', {
structure: '',
options: {
},
_create: lazyLoadPlugin()
});
jme.registerPlugin('fullscreen', {
options: {
fullscreen: true,
autoplayfs: false
},
structure: btnStructure,
text: 'enter fullscreen / exit fullscreen',
_create: lazyLoadPlugin()
});
jme.registerPlugin('mediaconfigmenu', {
structure: btnStructure,
text: 'configuration',
_create: lazyLoadPlugin()
});
jme.registerPlugin('captions', {
structure: btnStructure,
text: 'subtitles',
_create: function(control, media, base){
var trackElems = media.find('track').filter(':not([kind]), [kind="subtitles"], [data-kind="subtitles"], [kind="captions"], [data-kind="captions"]');
control.wsclonedcheckbox = $(control).clone().attr({role: 'checkbox'}).insertBefore(control);
base.attr('data-tracks', trackElems.length > 1 ? 'many' : trackElems.length);
control.attr('aria-haspopup', 'true');
lazyLoadPlugin().apply(this, arguments);
}
});
jme.registerPlugin('chapters', {
structure: btnStructure,
text: 'chapters',
_create: function(control, media, base){
var trackElems = media.find('track').filter('[kind="chapters"], [data-kind="chapters"]');
control.attr('aria-haspopup', 'true');
if(trackElems.length){
webshims._polyfill(['track']);
base.addClass('has-chapter-tracks');
}
lazyLoadPlugin().apply(this, arguments);
}
});
webshims.ready(webshims.cfg.mediaelement.plugins.concat(['mediaelement', 'jme-base']), function(){
if(!options.barTemplate){
options.barTemplate = '{{play-pause}}
{{playlist-prev}}
{{playlist-next}}
{{currenttime-display}}
{{time-slider}}
{{duration-display}}
{{mute-unmute}}
{{volume-slider}}
{{fullscreen}}
';
}
if(!options.barStructure){
options.barStructure = '';
}
webshims.addReady(function(context, insertedElement){
$(baseSelector, context).add(insertedElement.filter(baseSelector)).jmeProp('controlbar', true);
});
});
webshims.ready('WINDOWLOAD', loadLazy);
});
;webshims.ready('jme-base DOM', function(){
"use strict";
var webshims = window.webshims;
var $ = webshims.$;
var jme = $.jme;
var listId = 0;
var btnStructure = '';
function PlaylistList(data){
this._data = data;
this.lists = {};
this.on('showcontrolschange', this._updateControlsClass);
}
$.extend(PlaylistList.prototype, {
on: function(){
$.fn.on.apply($(this), arguments);
},
off: function(){
$.fn.off.apply($(this), arguments);
},
_getListId: function(list){
var id;
if(typeof list == 'string'){
id = list;
} else {
id = list.id;
}
return id;
},
_updateControlsClass: function(){
this._data.player[this.getShowcontrolsList() ? 'addClass' : 'removeClass']('has-playlist');
},
add: function(list, opts){
list = new Playlist(list, this, opts);
if(!list.id){
listId++;
list.id = 'list-'+listId;
}
this.lists[list.id] = list;
if(list.options.showcontrols){
this._data.player.addClass('has-playlist');
}
return list;
},
remove: function(list){
var id = this._getListId(list);
if(this.lists[id]){
this.lists[id]._remove();
delete this.lists[id];
}
if(!this.getShowcontrolsList()){
this._data.player.removeClass('has-playlist');
}
},
getAutoplayList: function(){
var clist = null;
$.each(this.lists, function(id, list){
if(list.options.autoplay){
clist = list;
return false;
}
});
return clist;
},
getShowcontrolsList: function(){
var clist = null;
$.each(this.lists, function(id, list){
if(list.options.showcontrols){
clist = list;
return false;
}
});
return clist;
}
});
function Playlist(list, parent, opts){
this.list = list || [];
this.playlists = parent;
this.media = parent._data.media;
this.player = parent._data.player;
this.options = $.extend(true, {}, Playlist.defaults, opts);
this.options.itemTmpl = this.options.itemTmpl.trim();
this.deferred = $.Deferred();
this._selectedIndex = -1;
this._selectedItem = null;
this.$rendered = null;
this._detectListType();
this.autoplay(this.options.autoplay);
this.deferred.done(function(){
this._addEvents(this);
if(this.options.defaultSelected == 'auto' && !this.media.jmeProp('srces').length){
this.options.defaultSelected = 0;
}
if(this.list[this.options.defaultSelected]){
this.selectedIndex(this.options.defaultSelected);
}
this._fire('addlist');
});
}
Playlist.getText = function($elem){
return $elem.attr('content') || ($elem.text() || '').trim();
};
Playlist.getUrl = function($elem){
return $elem.attr('content') || $elem.attr('url') || $elem.attr('href') || $elem.attr('src') || ($elem.text() || '').trim();
};
Playlist.defaults = {
loop: false,
autoplay: false,
defaultSelected: 'auto',
addItemEvents: true,
showcontrols: true,
ajax: {},
itemTmpl: '' +
'<% if(typeof poster == "string" && poster) {%><% }%>' +
'<%=title%>
' +
'<% if(typeof description == "string" && description) {%><%=description%>
<% }%>' +
'',
renderer: function(item, template){
return $.jme.tmpl(template, item);
},
mapDom: function(element){
return {
title: Playlist.getText($('[itemprop="name"], h1, h2, h3, h4, h5, h6, a', element)),
srces: $('[itemprop="contentUrl"], a[type^="video"], a[type^="audio"]', element).map(function(){
var tmp;
var src = {src: Playlist.getUrl($(this))};
if(this.nodeName.toLowerCase() == 'a'){
tmp = $.prop(this, 'type');
} else {
tmp = Playlist.getText($('[itemprop="encodingFormat"]', element));
}
if(tmp){
src.type = tmp;
}
tmp = $.attr(this, 'data-media');
if(tmp){
src.media = tmp;
}
return src;
}).get(),
tracks: $('a[type="text/vtt"]').map(mapTrackUrl).get(),
poster: Playlist.getUrl($('[itemprop="thumbnailUrl"], a[type^="image"], img', element)) || null,
description: Playlist.getText($('[itemprop="description"], .item-description, p', element)) || null
};
},
mapUrl: function(opts, callback){
$.ajax($.extend(opts, {
success: function(data){
var list;
if($.isArray(data)){
list = data;
} else if(data.responseData && data.responseData.feed){
data = data.responseData.feed;
list = (data.entries || []).map(mapJsonFeed);
} else {
list = [];
$('item', data).each(function(){
var srces = $('enclosure, media\\:content', this)
.filter('[type^="video"], [type^="audio"]')
.map(mapUrl)
.get()
;
if(srces.length){
list.push({
title: $('title', this).html(),
srces: srces,
publishedDate: $('pubDate', this).html() || null,
description: $('description', this).text() || null,
poster: Playlist.getUrl($('itunes\\:image, media\\:thumbnail, enclosure[type^="image"], media\\:content[type^="image"]', this)) || null,
author: $('itunes\\:author', this).html() || null,
duration: $('itunes\\:duration', this).html() || null,
tracks: $('media\\:subTitle', this).map(mapTrackUrl).get() || null
});
}
});
}
if(list != data){
list.fullData = data;
}
callback(list);
}
}));
}
};
function mapJsonFeed(item){
item.description = item.description || item.content;
item.srces = [];
(item.mediaGroups || []).forEach(function(mediagroup){
(mediagroup.contents || []).forEach(function(itemSrc){
itemSrc.src = itemSrc.src || itemSrc.url;
item.srces.push(itemSrc);
});
});
return item;
}
function mapTrackUrl(){
return {
src: $.attr(this, 'href'),
srclang: $.attr(this, 'lang'),
label: $.attr(this, 'data-label')
};
}
function mapUrl(){
return {
src: $.attr(this, 'url') || $.attr(this, 'href'),
type: $.attr(this, 'type')
};
}
function filterNode(){
return this.nodeType == 1;
}
$.extend(Playlist.prototype, {
on: function(){
$.fn.on.apply($(this), arguments);
},
off: function(){
$.fn.off.apply($(this), arguments);
},
_detectListType: function(){
var fullData;
if(typeof this.list == 'string'){
this._createListFromUrl();
return;
}
if(this.list.nodeName || (this.list.length > 0 && this.list[0].nodeName)){
this._createListFromDom();
} else if(this.list.responseData && this.list.responseData.feed){
fullData = this.list.responseData.feed;
this.list = (fullData.entries || []).map(mapJsonFeed);
this.list.fullData = fullData;
}
this.deferred.resolveWith(this);
},
_createListFromUrl: function(){
var that = this;
this.options.ajax.url = this.list;
this.options.mapUrl(this.options.ajax, function(list){
that.list = list;
that.deferred.resolveWith(that);
});
},
_createListFromDom: function(){
var that = this;
this.$rendered = $(this.list).eq(0);
this.list = [];
if(this.$rendered){
this._addDomList();
this.list = this.$rendered.children().map(function(){
return that._createItemFromDom(this);
}).get();
}
},
_createItemFromDom: function(dom){
var item = this.options.mapDom(dom);
this._addItemData(item, dom);
return item;
},
_fire: function(evt, extra){
var evt = $.Event(evt);
$(this).triggerHandler(evt, extra);
$(this.playlists).triggerHandler(evt, $.merge([{list: this}], extra || []));
if(this.$rendered){
this.$rendered.triggerHandler(evt, extra);
}
},
_addDomList: function(){
this.$rendered
.attr({
'data-autoplay': this.options.autoplay,
'data-loop': this.options.loop
})
.addClass('media-playlist')
.data('playlist', this)
;
},
_addItemData: function(item, dom){
var that = this;
item.$item = $(dom).data('itemData', item);
if(item == this._selectedItem){
item.$item.addClass('selected-item');
}
if(this.options.addItemEvents){
item.$item.on('click.playlist', function(e){
if(that.options.addItemEvents){
that.playItem(item, e);
return false;
}
});
}
},
_addEvents: function(that){
var o = that.options;
var onEnded = function(e){
if(o.autoplay){
that.playNext(e);
}
};
this.media.on('ended', onEnded);
this._remove = function(){
that.media.off('ended', onEnded);
that.autoplay(false);
if(that.$rendered){
that.$rendered.remove();
}
that._fire('removelist');
};
},
_remove: function(){
this._fire('removelist');
},
render: function(callback){
if(this.$rendered){
callback(this.$rendered, this.player, this);
} else {
this.deferred.done(function(){
var nodeName;
var that = this;
var items = [];
if(!this.$rendered){
$.each(this.list, function(i, item){
var domItem = $($.parseHTML(that.options.renderer(item, that.options.itemTmpl))).filter(filterNode)[0];
that._addItemData(item, domItem);
items.push(domItem);
});
nodeName = (items[0] && items[0].nodeName || '').toLowerCase();
switch (nodeName){
case 'li':
this.$rendered = $.parseHTML('');
break;
case 'option':
this.$rendered = $.parseHTML('');
break;
default:
this.$rendered = $.parseHTML('');
break;
}
this.$rendered = $(this.$rendered).html(items);
this._addDomList();
}
callback(this.$rendered, this.player, this);
});
}
},
/*
addItem: function(item, pos){
},
removeItem: function(item){
},
*/
_loadItem: function(item){
var media = this.media;
media.attr('poster', item.poster || '');
$('track', media).remove();
$.each(item.tracks || [], function(i, track){
$('').attr(track).appendTo(media);
});
if(!item.srces){
item.srces = item;
}
media.jmeProp('srces', item.srces);
},
_getItem: function(item){
if(item && (item.nodeName || item.jquery || typeof item == 'string')){
item = $(item).data('itemData');
}
return item;
},
playItem: function(item, e){
var media;
this.selectedItem(item, e);
if(item){
media = this.media.play();
setTimeout(function(){
media.play();
}, 9);
}
},
selectedIndex: function(index, e){
if(arguments.length){
this.selectedItem(this.list[index], e);
} else {
return this._selectedIndex;
}
},
selectedItem: function(item, e){
var oldItem, found;
if(arguments.length){
found = -1;
item = this._getItem(item);
if(item){
$.each(this.list, function(i){
if(item == this){
found = i;
return false;
}
});
}
if(found >= 0){
this._loadItem(this.list[found]);
}
if(found != this._selectedIndex){
oldItem = this._selectedItem || null;
if(oldItem && oldItem.$item){
oldItem.$item.removeClass('selected-item');
}
this._selectedItem = this.list[found] || null;
this._selectedIndex = found;
if(this._selectedItem && this._selectedItem.$item){
this._selectedItem.$item.addClass('selected-item');
}
if(oldItem !== this._selectedItem){
this._fire('itemchange', [{oldItem: oldItem, from: e || null}]);
}
}
} else {
return this._selectedItem;
}
},
playNext: function(){
var item = this.getNext();
if(item){
this.playItem(item);
}
},
playPrev: function(){
var item = this.getPrev();
if(item){
this.playItem(item);
}
},
getNext: function(){
var index = this._selectedIndex + 1;
return this.list[index] || (this.options.loop ? this.list[0] : null);
},
getPrev: function(){
var index = this._selectedIndex - 1;
return this.list[index] || (this.options.loop ? this.list[this.list.length - 1] : null);
}
});
[{name: 'autoplay', fn: 'getAutoplayList'}, {name: 'showcontrols', fn: 'getShowcontrolsList'}, {name: 'loop'}].forEach(function(desc){
Playlist.prototype[desc.name] = function(value){
var curList;
if(arguments.length){
value = !!value;
if(value && desc.fn){
curList = this.playlists[desc.fn]();
if(curList && curList != this){
curList[desc.name](false);
}
}
if(this.options[desc.name] != value){
this.options[desc.name] = value;
if(this.$rendered){
this.$rendered.attr('data-'+desc.name, value);
}
this._fire(desc.name+'change');
}
} else {
return this.options[desc.name];
}
}
});
jme.defineProp('playlists', {
writable: false,
get: function(elem){
var data = $.jme.data(elem);
if(elem != data.player[0]){return null;}
if(!data.playlists){
data.playlists = new PlaylistList(data);
}
return data.playlists;
}
});
jme.defineMethod('addPlaylist', function(list, options){
var playlists = $.jme.prop(this, 'playlists');
if(playlists && playlists.add){
return playlists.add(list, options);
}
return null;
});
[
{name: 'playlist-prev', text: 'previous', get: 'getPrev', play: 'playPrev'},
{name: 'playlist-next', text: 'next', get: 'getNext', play: 'playNext'}
]
.forEach(function(desc){
$.jme.registerPlugin(desc.name, {
structure: btnStructure,
text: desc.text,
_create: function(control, media, base){
var cList;
var playlists = base.jmeProp('playlists');
function itemChange(){
var item = cList[desc.get]();
if(item){
control.prop({'disabled': false, title: item.title});
} else {
control.prop({'disabled': true, title: ''});
}
}
function listchange(){
var newClist = playlists.getShowcontrolsList();
if(newClist != cList){
if(cList){
cList.off('itemchange', itemChange);
}
cList = newClist;
if(cList){
cList.on('itemchange', itemChange);
itemChange();
}
}
}
control.on('click', function(){
if(cList){
cList[desc.play]();
}
});
playlists.on({
'addlist removelist showcontrolschange':listchange
});
listchange();
}
});
})
;
// Simple JavaScript Templating
(function() {
var cache = {};
$.jme.tmpl = function tmpl(str, data) {
// Figure out if we're getting a template, or if we need to
// load the template - and be sure to cache the result.
if(!cache[str]){
cache[str] = new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};" +
// Introduce the data as local variables using with(){}
"with(obj){p.push('" +
// Convert the template into pure JavaScript
str.replace(/[\r\t\n]/g, " ")
.replace(/'(?=[^%]*%>)/g,"\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(/<%=(.+?)%>/g, "',$1,'")
.split("<%").join("');")
.split("%>").join("p.push('")
+ "');}return p.join('');");
}
// Provide some basic currying to the user
return data ? cache[str](data) : cache[str];
};
})();
$.jme.Playlist = Playlist;
webshims.isReady('playlist', true);
});