webshims.register('jme', function($, webshims, window, doc, undefined, options){ "use strict"; var props = {}; var fns = {}; var allowPreload = false; $(window).on('load', function(){ allowPreload = true; var scrollTimer; var allow = function(){ allowPreload = true; }; $(window).on('scroll', function(){ allowPreload = false; clearTimeout(scrollTimer); scrollTimer = setTimeout(allow, 999); }); }); $.jme = { version: '2.0.9', classNS: '', options: {}, plugins: {}, data: function(elem, name, value){ var data = $(elem).data(ns+'jme') || $.data(elem, ns+'jme', {}); if(value === undefined){ return (name) ? data[name] : data; } else { data[name] = value; } }, registerPlugin: function(name, plugin){ this.plugins[name] = plugin; if(!plugin.nodeName){ plugin.nodeName = ''; } if(!plugin.className){ plugin.className = name; } }, 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); } } }, setText: function(name, text){ var obj = name; if(name && text){ obj = {}; obj[name] = text; } $.each(obj, function(name, text){ if($.jme.plugins[name]){ $.jme.plugins[name].text = text; } }); } }; $.fn.jmeProp = function(name, value){ return $.access( this, $.jme.prop, name, value, arguments.length > 1 ); }; $.fn.jmeFn = function(fn){ var args = Array.prototype.slice.call( arguments, 1 ); var ret; this.each(function(){ ret = (fns[fn] || $.prop(this, fn)).apply(this, args); if(ret !== undefined){ return false; } }); return (ret !== undefined) ? ret : this; }; options = $.extend({selector: '.mediaplayer'}, webshims.cfg.mediaelement.jme); webshims.cfg.mediaelement.jme = options; var baseSelector = options.selector; var pluginSelectors = []; var ns = ''; $.jme.initJME = function(context, insertedElement){ $(baseSelector, context).add(insertedElement.filter(baseSelector)).jmePlayer(); }; var idlStates = { emptied: 1, pause: 1 }; $.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; }; webshims.ready('dom-support', function(){ if($('').prop('labels')){return;} webshims.defineNodeNamesProperty('button, input, keygen, meter, output, progress, select, textarea', 'labels', { prop: { get: function(){ var labels = []; var id = this.id; if(id){ labels = $('label[for="'+ id +'"]'); } if(!labels[0]) { labels = $(this).closest('label', this.form); } return labels.get(); }, writeable: false } }); }); $.jme.getButtonText = function(button, classes){ var btnTextElem = $('span.jme-text, +label span.jme-text', button); var btnLabelElem = button.prop('labels'); btnLabelElem = (btnLabelElem && btnLabelElem[0]) ? $(btnLabelElem).eq(0) : false; if(!btnTextElem[0]){ btnTextElem = btnLabelElem || button; } var txt = btnTextElem.text().split('/'); var title = button.prop('title').split('/'); var isCheckbox; var doText; var doTitle; var lastState; var txtChangeFn = function(state){ if(lastState === state){return;} lastState = state; if(doText){ btnTextElem.text(txt[state || 0]); } if(doTitle){ button.prop('title', txt[state || 0]); if (btnLabelElem) { btnLabelElem.prop('title', txt[state || 0]); } } if(classes){ button .removeClass(classes[(state) ? 0 : 1]) .addClass(classes[state]) ; } if(isCheckbox){ button.prop('checked', !!state); (button.data('checkboxradio') || {refresh: $.noop}).refresh(); } }; if(txt.length == 2){ txt[0] = txt[0].trim(); txt[1] = txt[1].trim(); doText = true; } if(title.length == 2){ title[0] = title[0].trim(); title[1] = title[1].trim(); doTitle = true; } 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(){ if(opts){ $.jme.data(this, $.extend(true, {}, opts)); } var mediaUpdateFn, init, canPlay, removeCanPlay, canplayTimer, needPreload, playerSize; var media = $('audio, video', this).filter(':first'); 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){ init = true; needPreload = !media.prop('autoplay'); removeCanPlay = function(){ media.off('canplay', canPlay); clearTimeout(canplayTimer); }; canPlay = function(){ var state = ($.prop(this, 'paused')) ? 'idle' : 'playing'; base.attr('data-state', state); }; mediaUpdateFn = function(e){ var state = e.type; var readyState; var paused; removeCanPlay(); 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){ base.attr('data-state', state); } }; playerSize = (function(){ var lastSize; var sizes = [ {size: 380, name: 'x-small'}, {size: 490, name: 'small'}, {size: 756, name: 'medium'}, {size: 1024, name: 'large'} ]; var len = sizes.length; return function(){ var size = 'x-large'; var i = 0; var width = base.outerWidth(); for(; i < len; i++){ if(sizes[i].size >= width){ size = sizes[i].name; break; } } if(lastSize != size){ lastSize = size; base.attr('data-playersize', size); } }; })(); jmeData.media = media; jmeData.player = base; media .on('ended', function(){ removeCanPlay(); media.jmeFn('pause'); if(!media.prop('autoplay') && !media.prop('loop') && !media.hasClass('no-reload')){ media.jmeFn('load'); } }) .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'](ns +'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); }) .on('emptied', function(e){ if(e.type == 'emptied'){ needPreload = !media.prop('autoplay'); } }) ; base .on({ useractive: function(){ base.attr('data-useractivity', 'true'); } }) .on('userinactive', {idletime: 3500}, function(){ base.attr('data-useractivity', 'false'); }) .triggerHandler('userinactive') ; playerSize(); webshims.ready('dom-support', function(){ base.onWSOff('updateshadowdom', playerSize); webshims.addShadowDom(); }); 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 src = { src: $.prop(source, 'src') }; var tmp = $.attr(source, 'media'); if(tmp){ src.media = tmp; } tmp = $.attr(source, 'type'); if(tmp){ src.type = tmp; } 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); $.each($.jme.plugins, function(name, plugin){ controls .filter('.'+plugin.className) .add(controls.find('.'+plugin.className)) .each(function(){ var control = $(this); var options = $.jme.data(this); options.player = data.player; options.media = data.media; if(options.rendered){return;} options.rendered = true; if(plugin.options){ $.each(plugin.options, function(option, value){ if(!(option in options)){ options[option] = value; } }); } plugin._create(control, data.media, data.player, options); control = null; }) ; }); $.jme.data(data.player[0], 'controlElements', oldControls.add(controls)); data.player.triggerHandler('controlsadded'); }); (function(){ var activity = { add: function(elem, cfg, name){ var data = $.data(elem, 'jmeuseractivity') || $.data(elem, 'jmeuseractivity', {idletime: 2500, idle: true, trigger: {}}), jElm = $(elem), setInactive = function(){ if(!data.idle){ data.idle = true; if ( data.trigger.userinactive ) { jElm.trigger('userinactive'); } } }, x, y, setActive = function(e){ if(!e || (e.type === 'mousemove' && e.pageX === x && e.pageY === y)){return;} if(e.type === 'mousemove'){ x = e.pageX; y = e.pageY; } if(data.idleTimer){ clearTimeout(data.idleTimer); } data.idleTimer = setTimeout(setInactive, data.idletime); if(data.idle){ data.idle = false; if( data.trigger.useractive ){ jElm.trigger('useractive'); } } } ; data.idletime = (cfg || {}).idletime || data.idletime; if(cfg && 'idle' in cfg){ data.idle = cfg.idle; } data.trigger[name] = true; if(!data.bound){ jElm .on('mouseleave.jmeuseractivity', setInactive) .on('mousemove.jmeuseractivity focusin.jmeuseractivity mouseenter.jmeuseractivity keydown.jmeuseractivity keyup.jmeuseractivity mousedown.jmeuseractivity', setActive) ; data.bound = true; } if(!data.idle){ setActive({type: 'initunidled'}); } }, remove: function(elem, name){ var data = $.data(elem, 'jmeuseractivity') || $.data(elem, 'jmeuseractivity', {idletime: 2500, idle: true, trigger: {}}); data.trigger[name] = false; if(!data.trigger.useractive && !data.trigger.userinactive){ $(elem).off('.jmeuseractivity'); data.bound = false; } } }; $.each(['useractive', 'userinactive'], function(i, name){ $.event.special[name] = { setup: function(cfg){ activity.add(this, cfg, name); }, teardown: function(){ activity.remove(this, name); } }; }); })(); webshims.ready('mediaelement', function(){ webshims.addReady($.jme.initJME); }); }); ;webshims.register('mediacontrols', function($, webshims, window, doc, undefined, options){ "use strict"; var pseudoClasses = 'pseudoClasses'; var baseSelector = webshims.cfg.mediaelement.jme.selector; var playStates = { play: 1, playing: 1 }; var pauseStates = { pause: 1, ended: 1 }; var loadRange = function(){ webshims.loader.loadList(['range-ui']); }; var onSliderReady = function(fn){ loadRange(); webshims.ready('range-ui', fn); }; var btnStructure = ''; var defaultStructure = '
'; var slideStructure = '
'; var ns = $.jme.classNS; var noVolumeClass = (function(){ var audio; var ret = ''; if(typeof window.Audio == 'function'){ audio = new Audio(); audio.volume = 0.55; ret = audio.volume = 0.55 ? '' : ' no-volume-api'; } return ret; })(); $.jme.defineProp('controlbar', { set: function(elem, value){ value = !!value; var data = $.jme.data(elem); var controlBar = $('div.jme-mediaoverlay, div.jme-controlbar', data.player); var mediaControls = $.jme.plugins["media-controls"] ; var structure = ''; var controls; if(value && !controlBar[0]){ if(data._controlbar){ data._controlbar.appendTo(data.player); } else { data.media.prop('controls', false); $.each(mediaControls.pluginOrder, function(i, name){ var plugin = $.jme.plugins[name]; if(plugin && plugin.structure){ structure += plugin.structure.replace('{%class%}', ns+name).replace('{%text%}', plugin.text || ''); } else if(name){ structure += name; } }); data._controlbar = $( mediaControls.barStructure ); controlBar = data._controlbar.find('div.jme-cb-box').addClass(ns+'media-controls'); controls = data._controlbar.filter('.jme-media-overlay').addClass(ns+'play-pause'); controls = controls.add( controlBar ); controls = controls.add( $(structure).appendTo(controlBar) ); data._controlbar.appendTo(data.player); data.player.jmeFn('addControls', controls); } } else if(!value) { controlBar.detach(); } controlBar = null; controls = null; return value; } }); $.jme.defineMethod('updateControlbar', function(){ var timeSlider = $('.'+ $.jme.classNS +'time-slider', this); if(timeSlider[0] && timeSlider.css('position') !== 'absolute'){ var width; var elemWidths = 0; width = Math.floor(timeSlider.parent().width()) - 0.2; timeSlider .siblings() .each(function(){ if(this !== timeSlider[0] && $.css(this, 'position') !== 'absolute' && $.css(this, 'display') !== 'none'){ elemWidths += Math.ceil($(this).outerWidth(true)) + 0.1; } }) ; timeSlider.width(Math.floor(width - elemWidths - Math.ceil(timeSlider.outerWidth(true) - timeSlider.width()) - 0.3)); } }); $.jme.registerPlugin('media-controls', { options: { calculateTimerange: false }, pluginOrder: ['
', 'play-pause', '
', '
', 'currenttime-display', '
', '
', 'time-slider', '
', '
', 'duration-display', '
', '
', 'mute-unmute', '
', '
', 'volume-slider', '
', '
', '
', 'captions', '
', '
', '
', 'fullscreen', '
'], barStructure: '
', _create: function(control, media, base, options){ var timer; var update = function(){ clearTimeout(timer); control.jmeFn('updateControlbar'); timer = setTimeout(function(){ control.jmeFn('updateControlbar'); }, 9); }; if(options.calculateTimerange){ setTimeout(function(){ media.on('loadedmetadata volumechange play pause ended emptied', update); base.on('updatetimeformat controlsadded controlschanged playerdimensionchange', update); $(window).on('resize emchange', update); }, 1); update(); } } }); $.jme.registerPlugin('play-pause', { pseudoClasses: { play: 'state-paused', pause: 'state-playing' }, structure: btnStructure, text: 'play / pause', _create: function(control, media){ var textFn = $.jme.getButtonText(control, [this[pseudoClasses].play, this[pseudoClasses].pause]); media .on('play playing ended pause updateJMEState', function(e){ var state = e.type; if(playStates[state]){ state = 1; } else if(pauseStates[state]) { state = 0; } else { state = (media.jmeProp('isPlaying') )? 1 : 0; } textFn(state); }) .triggerHandler('updateJMEState') ; control.on((control.is('select')) ? 'change' : 'click', function(e){ media.jmeFn('togglePlay'); e.stopPropagation(); }); } }); $.jme.registerPlugin('mute-unmute', { pseudoClasses: { mute: 'state-mute', unmute: 'state-unmute' }, structure: btnStructure, text: 'mute / unmute', _create: function(control, media, base){ var textFn = $.jme.getButtonText(control, [this[pseudoClasses].mute, this[pseudoClasses].unmute]); media .on('volumechange updateJMEState', function(e){ textFn(media.prop('muted') ? 1 : 0); }) .triggerHandler('updateJMEState') ; control.on((control.is('select')) ? 'change' : 'click', function(e){ media.prop('muted', !media.prop('muted')); e.stopPropagation(); }); } }); function createGetSetHandler(fns){ var throttleTimer; var blocked; if(fns.release === true){ fns.release = fns.set; } var getSetHelper = { start: function(){ if(!blocked){ blocked = true; if(fns.start){ fns.start(); } } }, release: function(){ if(blocked){ blocked = false; if(fns.release){ fns.release(); } } }, get: function(){ if(blocked){return;} return fns.get.apply(this, arguments); }, set: function(){ var that = this; var args = arguments; getSetHelper.start(); clearTimeout(throttleTimer); throttleTimer = setTimeout(function(){ fns.set.apply(that, args); }, 33); } }; getSetHelper.fns = fns; return getSetHelper; } $.jme.registerPlugin('volume-slider', { structure: slideStructure, _create: function(control, media, base){ var createFn = function(){ var api, volume; volume = createGetSetHandler({ get: function(){ var volume = media.prop('volume'); if(volume !== undefined){ api.value(volume); } }, set: function(){ media.prop({ muted: false, volume: api.options.value }); }, release: true }); api = control .rangeUI({ min: 0, max: 1, //animate: true, step: 'any', input: volume.set, change: volume.release, baseClass: 'media-range' }) .data('rangeUi') ; media.on('volumechange', volume.get); }; onSliderReady(createFn); } }); $.jme.registerPlugin('time-slider', { structure: slideStructure, pseudoClasses: { no: 'no-duration' }, options: { format: ['mm', 'ss'] }, _create: function(control, media, base){ var module = this; var createFn = function(){ var time, durationChange, api, timeShow, wasPaused; var hasDuration = $.jme.classNS+'has-duration'; var duration = media.prop('duration'); time = createGetSetHandler({ get: function(){ var time = media.prop('currentTime'); if(!isNaN(time)){ try { api.value(time); } catch(er){} } }, set: function(){ try { media.prop('currentTime', api.options.value).triggerHandler('timechanged', [api.options.value]); } catch(er){} }, start: function(){ if(wasPaused == null){ wasPaused = media.prop('paused'); if(!wasPaused){ base._seekpause = true; media.pause(); } else { base._seekpause = false; } } }, release: function(){ time.fns.set(); if(wasPaused === false){ media.play(); } if('_seekpause' in base){ delete base._seekpause; } wasPaused = null; } }); durationChange = function(){ duration = media.prop('duration'); hasDuration = duration && isFinite(duration) && !isNaN(duration); if(hasDuration){ api.disabled(false); api.max(duration); base.removeClass(module[pseudoClasses].no); } else { api.disabled(true); api.max(Number.MAX_VALUE); base.addClass(module[pseudoClasses].no); } }; api = control .rangeUI({ min: 0, value: media.prop('currentTime') || 0, step: 'any', input: time.set, change: time.release, textValue: function(val){ return media.jmeFn('formatTime', val); }, baseClass: 'media-range' }) .data('rangeUi') ; timeShow = $('').appendTo(control); control .on({ 'mouseenter': function(e){ if(hasDuration){ var widgetLeft = (control.offset() || {left: 0}).left; var widgetWidth = control.innerWidth(); var posLeft = function(x){ var perc = (x - widgetLeft) / widgetWidth * 100; timeShow .html(media.jmeFn('formatTime', duration * perc / 100)) .css({left: perc+'%'}) ; }; posLeft(e.pageX); timeShow.addClass($.jme.classNS +'show-time-select'); control .off('.jmetimeselect') .on('mousemove.jmetimeselect', function(e){ posLeft(e.pageX); }) ; } }, mouseleave: function(){ timeShow.removeClass($.jme.classNS +'show-time-select'); control.off('.jmetimeselect'); } }) ; media.on({ timeupdate: time.get, emptied: function(){ durationChange(); api.value(0); }, durationchange: durationChange }); base.jmeFn('addControls', $('
').prependTo(control) ); durationChange(); }; onSliderReady(createFn); } }); $.jme.defineMethod('concerningRange', function(type, time){ var elem = this; var ret = {start: 0, end: 0}; if(!type){ type = 'buffered'; } type = $.prop(elem, type); if(time == null){ time = $.prop(elem, 'currentTime'); } if(!type || !('length' in type)){return ret;} for(var i = 0, len = type.length; i < len; i++){ ret.start = type.start(i); ret.end = type.end(i); if(ret.start <= time && ret.end >= time){ break; } } return ret; }); $.jme.defineProp('progress', { get: function(elem){ var data = $.jme.data(elem); if(!data.media){return 0;} var progress = data.media.jmeFn('concerningRange').end / data.media.prop('duration') * 100; if(progress > 99.4){ progress = 100; } return progress || 0; }, readonly: true }); $.jme.registerPlugin('buffer-progress', { _create: function(control, media, base, options){ var indicator = $('
').appendTo(control); var drawBufferProgress = function(){ var progress = media.jmeProp('progress'); if(options.progress !== progress){ options.progress = progress; indicator.css('width', progress +'%'); } }; media.on({ progress: drawBufferProgress, emptied: function(){ indicator.css('width', 0); options.progress = 0; }, playing: drawBufferProgress }); drawBufferProgress(); } }); var times = { hh: 60000, mm: 60, ss: 1, ms: 1/1000 }; var formatTime = function(sec, format){ var data; if(!format){ format = ['mm', 'ss']; } if(sec == null){ data = $.jme.data(this); sec = $.prop(data.media, 'duration'); } if(!sec){ sec = 0; } var formated = []; var frac; for(var i = 0, len = format.length; i < len; i++){ if(format[i] == 'ms' && i == len -1 ){ frac = Math.round( (sec / times[format[i]]) / 10); } else { frac = parseInt(sec / times[format[i]], 10); sec = sec % times[format[i]]; } if(frac < 10){ frac = '0'+frac; } formated.push( frac ); } return formated.join(':'); }; $.jme.defineMethod('formatTime', formatTime); $.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: defaultStructure, options: { format: "mm:ss" }, _create: function(control, media, base, options){ if(typeof options.format == 'string'){ options.format = options.format.split(':'); } var showDuration = function(){ control.html(formatTime(media.prop('duration'), options.format)); }; media.on('durationchange emptied', showDuration); control .on('updatetimeformat', showDuration) .jmeProp('format', options.format) ; } }); $.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: defaultStructure, options: { format: "mm:ss", countdown: false }, _create: function(control, media, base, options){ if(typeof options.format == 'string'){ options.format = options.format.split(':'); } var showTime = function(e){ var currentTime = media.prop('currentTime'); if(options.countdown){ currentTime = (media.prop('duration') || 0) - currentTime; if(currentTime < 0.7){ currentTime = 0; } } control.html(formatTime(currentTime, options.format)); }; media.on('timeupdate emptied durationchange', showTime); control .on('updatetimeformat', showTime) .jmeProp('format', options.format) ; } }); /** * 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: function(control, media, base, options){ /* Empty span element used for vertical centering in IE7 - thanks to Bruno Fassino. * @see http://www.brunildo.org/test/img_center.html */ var updatePoster = function(){ var poster = media.prop('poster'); if(poster){ control.html(''); } else { control.empty(); } }; media.on('emptied', updatePoster); updatePoster(); } }); //taken from http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/ $.jme.fullscreen = (function() { var parentData; var frameData; var doc = document.documentElement; var fullScreenApi = { supportsFullScreen: Modernizr.prefixed('fullscreenEnabled', document, false) || Modernizr.prefixed('fullScreenEnabled', document, false), isFullScreen: function() { return false; }, requestFullScreen: function(elem){ var tmpData; parentData = []; $(elem).parentsUntil('body').each(function(){ var pos = $.css(this, 'position'); var left = this.scrollLeft; var top = this.scrollTop; var changed; tmpData = {elemStyle: this.style, elem: this}; if(pos !== 'static'){ changed = true; tmpData.pos = tmpData.elemStyle.position; this.style.position = 'static'; } if(left){ changed = true; tmpData.left = left; } if(top){ changed = true; tmpData.top = top; } if(changed){ parentData.push(tmpData); } }); frameData = false; try { frameData = {elemStyle: frameElement.style, elem: frameElement, css: {}}; frameData.css.position = frameData.elemStyle.position; frameData.elemStyle.position = 'fixed'; $.each(['top', 'left', 'right', 'bottom'], function(i, name){ frameData.css[name] = frameData.elemStyle[name]; frameData.elemStyle[name] = '0px'; }); $.each(['height', 'width'], function(i, name){ frameData.css[name] = frameData.elemStyle[name]; frameData.elemStyle[name] = '100%'; }); } catch(er){ frameData = false; } tmpData = null; }, cancelFullScreen: function(){ if(parentData){ $.each(parentData, function(i, data){ if('pos' in data){ data.elemStyle.position = data.pos; } if(data.left){ data.elem.scrollLeft = data.left; } if(data.top){ data.elem.scrollTop = data.top; } data = null; }); parentData = []; } if(frameData){ $.each(frameData.css, function(name, value){ frameData.elemStyle[name] = value; }); frameData = false; } }, eventName: 'fullscreenchange', exitName: 'exitFullscreen', requestName: 'requestFullscreen', elementName: 'fullscreenElement', enabledName: '' }; fullScreenApi.cancelFullWindow = fullScreenApi.cancelFullScreen; fullScreenApi.requestFullWindow = fullScreenApi.requestFullScreen; // update methods to do something useful if (fullScreenApi.supportsFullScreen) { fullScreenApi.enabledName = fullScreenApi.supportsFullScreen; fullScreenApi.exitName = Modernizr.prefixed("exitFullscreen", document, false) || Modernizr.prefixed("cancelFullScreen", document, false); fullScreenApi.elementName = Modernizr.prefixed("fullscreenElement", document, false) || Modernizr.prefixed("fullScreenElement", document, false); fullScreenApi.supportsFullScreen = !!fullScreenApi.supportsFullScreen; if(fullScreenApi.elementName != 'fullscreenElement' || fullScreenApi.exitName != 'exitFullscreen' || fullScreenApi.enabledName != 'fullscreenEnabled'){ $.each(Modernizr._domPrefixes, function(i, prefix){ var requestName = prefix+'RequestFullscreen'; if((requestName in doc) || ((requestName = prefix+'RequestFullScreen') && (requestName in doc))){ fullScreenApi.eventName = prefix + 'fullscreenchange'; fullScreenApi.requestName = requestName; return false; } }); } fullScreenApi.isFullScreen = function() { return document[fullScreenApi.elementName]; }; fullScreenApi.requestFullScreen = function(el) { return el[fullScreenApi.requestName](); }; fullScreenApi.cancelFullScreen = function() { return document[fullScreenApi.exitName](); }; } if(!window.Modernizr || !('fullscreen' in Modernizr)){ $('html').addClass(fullScreenApi.supportsFullScreen ? 'fullscreen' : 'no-fullscreen'); } if(window.parent != window){ (function(){ try{ var frame = window.frameElement; if (fullScreenApi.supportsFullScreen) { if('allowfullscreen' in frame && !frame.allowfullscreen) { frame.allowfullscreen = true; } else { if(frame.getAttribute('webkitallowfullscreen') == null){ frame.setAttribute('webkitallowfullscreen', ''); } if(frame.getAttribute('allowfullscreen') == null){ frame.setAttribute('allowfullscreen', 'allowfullscreen'); } } } } catch(er){ if(!fullScreenApi.supportsFullScreen){ $('html').addClass('no-fullwindow'); } } })(); } return fullScreenApi; })(); $.jme.defineProp('fullscreen', { set: function(elem, value){ var data = $.jme.data(elem); if((!data || !data.player) && !$(elem).hasClass($.jme.classNS+'player-fullscreen')){return 'noDataSet';} if(value){ if(data.player.hasClass($.jme.classNS+'player-fullscreen')){return 'noDataSet';} data.scrollPos = { top: $(window).scrollTop(), left: $(window).scrollLeft() }; $(document) .off('.jmefullscreen') .on('keydown.jmefullscreen', function(e){ if(e.keyCode == 27){ data.player.jmeProp('fullscreen', false); return false; } if(e.keyCode === 32 && !('form' in e.target)){ data.media.jmeFn('togglePlay'); return false; } }) ; if(value == 'fullwindow'){ $.jme.fullscreen.requestFullWindow(data.player[0]); } else { try { $.jme.fullscreen.requestFullScreen(data.player[0]); } catch(er){} } $('html').addClass($.jme.classNS+'has-media-fullscreen'); data.player.addClass($.jme.classNS+'player-fullscreen'); data.media.addClass($.jme.classNS+'media-fullscreen'); $('button.play-pause', data.player).focus(); if($.jme.fullscreen.supportsFullScreen){ $(document) .on($.jme.fullscreen.eventName+'.jmefullscreen', function(e){ var fullScreenElem = $.jme.fullscreen.isFullScreen(); if(fullScreenElem && elem == fullScreenElem){ data.media.trigger('playerdimensionchange', ['fullscreen']); } else { data.player.jmeProp('fullscreen', false); } }) ; } data.media.trigger('playerdimensionchange', ['fullwindow']); } else { if(data.player && !data.player.hasClass($.jme.classNS+'player-fullscreen')){return 'noDataSet';} $(document).off('.jmefullscreen'); $('html').removeClass($.jme.classNS+'has-media-fullscreen'); if(data.player && data.media){ data.player.removeClass($.jme.classNS+'player-fullscreen'); data.media.removeClass($.jme.classNS+'media-fullscreen'); } if($.jme.fullscreen.isFullScreen()){ try { $.jme.fullscreen.cancelFullScreen(); } catch(er){} } else { $.jme.fullscreen.cancelFullWindow(); } if(data.scrollPos){ $(window).scrollTop(data.scrollPos.top); $(window).scrollLeft(data.scrollPos.left); delete data.scrollPos; } if(data.media){ data.media.trigger('playerdimensionchange'); } } return 'noDataSet'; }, get: function(elem){ var data = $.jme.data(elem); if(!data || !data.player){return;} var fs = data.player.hasClass($.jme.classNS+'player-fullscreen'); if(!fs){return false;} return $.jme.fullscreen.isFullScreen() || 'fullwindow'; } }); $.jme.defineProp('autoplayfs'); $.jme.registerPlugin('fullscreen', { pseudoClasses: { enter: 'state-enterfullscreen', exit: 'state-exitfullscreen' }, options: { fullscreen: true, autoplayfs: false }, structure: btnStructure, text: 'enter fullscreen / exit fullscreen', _create: function(control, media, base){ var textFn = $.jme.getButtonText(control, [this[pseudoClasses].enter, this[pseudoClasses].exit]); var updateControl = function(){ textFn(base.hasClass($.jme.classNS+'player-fullscreen') ? 1 : 0); }; var options = this.options; var addDoubbleClick = function(){ $(base.data('jme').controlElements) .filter('.jme-media-overlay') .off('.dblfullscreen') .on('dblclick.dblfullscreen', function(e){ base.jmeProp('fullscreen', !base.jmeProp('fullscreen')); }) ; }; base.on('controlsadded', addDoubbleClick); base.on('playerdimensionchange', updateControl); control.on((control.is('select')) ? 'change' : 'click', function(){ var value = base.hasClass($.jme.classNS+'player-fullscreen') ? false : options.fullscreen; base.jmeProp('fullscreen', value); if(value && options.autoplayfs){ media.jmeFn('play'); } }); addDoubbleClick(); updateControl(); } }); $.jme.ButtonMenu = function(button, menu, clickHandler){ this.button = $(button).attr({'aria-haspopup': 'true'}); this.clickHandler = clickHandler; this.toggle = $.proxy(this, 'toggle'); this.keyIndex = $.proxy(this, 'keyIndex'); this._buttonClick = $.proxy(this, '_buttonClick'); this.addMenu(menu); this._closeFocusOut(); this.button.on('click', this.toggle); }; $.jme.ButtonMenu.prototype = { addMenu: function(menu){ if(this.menu){ this.menu.remove(); } this.menu = $(menu); this.buttons = $('button', this.menu); this.menu.insertAfter(this.button); this.menu .on('keydown', this.keyIndex) .delegate('button', 'click', this._buttonClick) ; }, _closeFocusOut: function(){ var that = this; var timer; var stopFocusOut = function(){ clearTimeout(timer); setTimeout(function(){ clearTimeout(timer); }, 9); }; this.menu .parent() .on('focusin', stopFocusOut) .on('mousedown', stopFocusOut) .on('focusout', function(e){ timer = setTimeout(function(){ that.hide(); }, 40); }) ; }, _buttonClick: function(e){ this.clickHandler(this.buttons.index(e.currentTarget), e.currentTarget); this.hide(); }, keyIndex: function(e){ var dir = (e.keyCode == 40) ? 1 : (e.keyCode == 38) ? -1 : 0; if(dir){ var buttons = this.buttons.not(':disabled'); var activeButton = buttons.filter(':focus'); activeButton = buttons[buttons.index(activeButton) + dir] || buttons.filter(dir > 0 ? ':first' : ':last'); activeButton.focus(); e.preventDefault(); } }, show: function(){ if(this.isVisible){return;} var buttons = this.buttons.not(':disabled'); this.isVisible = true; this.menu.addClass('visible-menu'); try { this.activeElement = document.activeElement || this.button[0]; } catch(er){ this.activeElement = this.button[0]; } setTimeout(function(){ $(buttons.filter('[aria-checked="true"]')[0] || buttons[0]).focus(); }, 60); }, toggle: function(){ this[this.isVisible ? 'hide' : 'show'](); }, hide: function(){ if(!this.isVisible){return;} this.isVisible = false; this.menu.removeClass('visible-menu'); if(this.activeElement){ try { this.activeElement.focus(); } catch(er){} } this.activeElement = false; } }; var showKinds = {subtitles: 1, caption: 1}; var getTrackMenu = function(tracks){ var items = $.map(tracks, function(track){ var className = (track.kind == 'caption') ? 'caption-type' : 'subtitle-type'; var lang = track.language; lang = (lang) ? ' '+ lang +'' : ''; return ''; }) ; return '
    ' + items.join('') +'
'; }; $.jme.registerPlugin('captions', { pseudoClasses: { menu: 'subtitle-menu' }, structure: btnStructure, text: 'subtitles', _create: function(control, media, base, options){ var that = this; var trackElems = media.find('track'); var checkbox = $(control).clone().attr({role: 'checkbox'}).insertBefore(control); base.attr('data-tracks', trackElems.length > 1 ? 'many' : trackElems.length); control.attr('aria-haspopup', 'true'); webshims.ready('track', function(){ var menuObj, throttledUpdateMode; var tracks = []; var textTracks = media.prop('textTracks'); var throttledUpdate = (function(){ var timer; var triggerTimer; return function(e){ clearTimeout(timer); clearTimeout(triggerTimer); if(e.type == 'updatesubtitlestate'){ triggerTimer = setTimeout(function(){ media.trigger('updatetracklist'); }, 0); } timer = setTimeout(updateTrackMenu, 19); }; })(); function createSubtitleMenu(menu){ var menuClick; if(!menuObj){ menuClick = function(index, button){ if($.attr(button, 'aria-checked') == 'true'){ tracks[index].mode = 'disabled'; } else { $.each(tracks, function(i, track){ track.mode = (i == index) ? 'showing' : 'disabled'; }); } media.prop('textTracks'); updateMode(); }; menuObj = new $.jme.ButtonMenu(control, menu, menuClick); checkbox.on('click', function(){ menuClick(0, this); return false; }); } else { menuObj.addMenu(menu); } updateMode(); } function updateMode(){ $('button', menuObj.menu).each(function(i){ var checked = (tracks[i].mode == 'showing') ? 'true' : 'false'; if(!i){ checkbox.attr('aria-checked', checked); } $.attr(this, 'aria-checked', checked); }); } function updateTrackMenu(){ tracks = []; $.each(textTracks, function(i, track){ if(showKinds[track.kind] && track.readyState != 3){ tracks.push(track); } }); base.attr('data-tracks', tracks.length > 1 ? 'many' : tracks.length); if(tracks.length){ createSubtitleMenu('
'+ (getTrackMenu(tracks)) +'
'); $('span.jme-text, +label span.jme-text', checkbox).text((tracks[0].label || ' ') + (tracks[0].lang || '')); if(!base.hasClass(that[pseudoClasses].hasTrack) || base.hasClass(that[pseudoClasses].noTrack)){ control.prop('disabled', false); base.triggerHandler('controlschanged'); } } else if(!base.hasClass(that[pseudoClasses].noTrack) || base.hasClass(that[pseudoClasses].hasTrack)){ control.prop('disabled', true); base .triggerHandler('controlschanged') ; } } if(!textTracks){ textTracks = []; updateTrackMenu(); } else { throttledUpdateMode = (function(){ var timer; return function(){ clearTimeout(timer); timer = setTimeout(updateMode, 20); }; })(); updateTrackMenu(); $([textTracks]) .on('addtrack removetrack', throttledUpdate) .on('change', throttledUpdateMode) ; base.on('updatesubtitlestate', throttledUpdate); media.on('updatetrackdisplay', throttledUpdateMode); } }); } }); $('.mediaplayer').each(function(){ if(($.data(this, 'jme')|| {}).controlbar){ $(this).jmeProp('controlbar', true); } }); webshims.ready('mediaelement', function(){ webshims.addReady(function(context, insertedElement){ $(baseSelector, context).add(insertedElement.filter(baseSelector)).jmeProp('controlbar', true); }); }); webshims.ready('WINDOWLOAD', loadRange); });