2569 lines
66 KiB
JavaScript
2569 lines
66 KiB
JavaScript
(function($){
|
|
var webshims = window.webshims;
|
|
|
|
setTimeout(function(){
|
|
webshims.isReady('geolocation', true);
|
|
});
|
|
var domWrite = function(){
|
|
setTimeout(function(){
|
|
throw('document.write is overwritten by geolocation shim. This method is incompatible with this plugin');
|
|
}, 1);
|
|
},
|
|
id = 0
|
|
;
|
|
var geoOpts = webshims.cfg.geolocation || {};
|
|
if(!navigator.geolocation){
|
|
navigator.geolocation = {};
|
|
}
|
|
$.extend(navigator.geolocation, (function(){
|
|
var pos;
|
|
var api = {
|
|
getCurrentPosition: function(success, error, opts){
|
|
var locationAPIs = 2,
|
|
errorTimer,
|
|
googleTimer,
|
|
calledEnd,
|
|
createAjax,
|
|
endCallback = function(){
|
|
if(calledEnd){return;}
|
|
if(pos){
|
|
calledEnd = true;
|
|
success($.extend({timestamp: new Date().getTime()}, pos));
|
|
resetCallback();
|
|
if(window.JSON && window.sessionStorage){
|
|
try{
|
|
sessionStorage.setItem('storedGeolocationData654321', JSON.stringify(pos));
|
|
} catch(e){}
|
|
}
|
|
} else if(error && !locationAPIs) {
|
|
calledEnd = true;
|
|
resetCallback();
|
|
error({ code: 2, message: "POSITION_UNAVAILABLE"});
|
|
}
|
|
},
|
|
googleCallback = function(){
|
|
locationAPIs--;
|
|
getGoogleCoords();
|
|
endCallback();
|
|
},
|
|
resetCallback = function(){
|
|
$(document).off('google-loader', resetCallback);
|
|
clearTimeout(googleTimer);
|
|
clearTimeout(errorTimer);
|
|
},
|
|
getGoogleCoords = function(){
|
|
if(pos || !window.google || !google.loader || !google.loader.ClientLocation){return false;}
|
|
var cl = google.loader.ClientLocation;
|
|
pos = {
|
|
coords: {
|
|
latitude: cl.latitude,
|
|
longitude: cl.longitude,
|
|
altitude: null,
|
|
accuracy: 43000,
|
|
altitudeAccuracy: null,
|
|
heading: parseInt('NaN', 10),
|
|
velocity: null
|
|
},
|
|
//extension similiar to FF implementation
|
|
address: $.extend({streetNumber: '', street: '', premises: '', county: '', postalCode: ''}, cl.address)
|
|
};
|
|
return true;
|
|
},
|
|
getInitCoords = function(){
|
|
if(pos){return;}
|
|
getGoogleCoords();
|
|
if(pos || !window.JSON || !window.sessionStorage){return;}
|
|
try{
|
|
pos = sessionStorage.getItem('storedGeolocationData654321');
|
|
pos = (pos) ? JSON.parse(pos) : false;
|
|
if(!pos.coords){pos = false;}
|
|
} catch(e){
|
|
pos = false;
|
|
}
|
|
}
|
|
;
|
|
|
|
getInitCoords();
|
|
|
|
if(!pos){
|
|
if(geoOpts.confirmText && !confirm(geoOpts.confirmText.replace('{location}', location.hostname))){
|
|
if(error){
|
|
error({ code: 1, message: "PERMISSION_DENIED"});
|
|
}
|
|
return;
|
|
}
|
|
createAjax = function(){
|
|
$.ajax({
|
|
url: 'http://freegeoip.net/json/',
|
|
dataType: 'jsonp',
|
|
cache: true,
|
|
jsonp: 'callback',
|
|
success: function(data){
|
|
locationAPIs--;
|
|
if(!data){return;}
|
|
pos = pos || {
|
|
coords: {
|
|
latitude: data.latitude,
|
|
longitude: data.longitude,
|
|
altitude: null,
|
|
accuracy: 43000,
|
|
altitudeAccuracy: null,
|
|
heading: parseInt('NaN', 10),
|
|
velocity: null
|
|
},
|
|
//extension similiar to FF implementation
|
|
address: {
|
|
city: data.city,
|
|
country: data.country_name,
|
|
countryCode: data.country_code,
|
|
county: "",
|
|
postalCode: data.zipcode,
|
|
premises: "",
|
|
region: data.region_name,
|
|
street: "",
|
|
streetNumber: ""
|
|
}
|
|
};
|
|
endCallback();
|
|
},
|
|
error: function(){
|
|
locationAPIs--;
|
|
endCallback();
|
|
}
|
|
});
|
|
};
|
|
if($.ajax){
|
|
createAjax();
|
|
} else {
|
|
webshims.ready('jajax', createAjax);
|
|
webshims.loader.loadList(['jajax']);
|
|
}
|
|
clearTimeout(googleTimer);
|
|
if (!window.google || !window.google.loader) {
|
|
googleTimer = setTimeout(function(){
|
|
//destroys document.write!!!
|
|
if (geoOpts.destroyWrite) {
|
|
document.write = domWrite;
|
|
document.writeln = domWrite;
|
|
}
|
|
$(document).one('google-loader', googleCallback);
|
|
webshims.loader.loadScript('http://www.google.com/jsapi', false, 'google-loader');
|
|
}, 800);
|
|
} else {
|
|
locationAPIs--;
|
|
}
|
|
} else {
|
|
setTimeout(endCallback, 1);
|
|
return;
|
|
}
|
|
if(opts && opts.timeout){
|
|
errorTimer = setTimeout(function(){
|
|
resetCallback();
|
|
if(error) {
|
|
error({ code: 3, message: "TIMEOUT"});
|
|
}
|
|
}, opts.timeout);
|
|
} else {
|
|
errorTimer = setTimeout(function(){
|
|
locationAPIs = 0;
|
|
endCallback();
|
|
}, 10000);
|
|
}
|
|
},
|
|
clearWatch: $.noop
|
|
};
|
|
api.watchPosition = function(a, b, c){
|
|
api.getCurrentPosition(a, b, c);
|
|
id++;
|
|
return id;
|
|
};
|
|
return api;
|
|
})());
|
|
|
|
webshims.ready('WINDOWLOAD', function(){
|
|
webshims.loader.loadList(['jajax']);
|
|
});
|
|
webshims.isReady('geolocation', true);
|
|
})(webshims.$);
|
|
;webshims.register('details', function($, webshims, window, doc, undefined, options){
|
|
var isInterActiveSummary = function(summary){
|
|
var details = $(summary).parent('details');
|
|
if(details[0] && details.children(':first').get(0) === summary){
|
|
return details;
|
|
}
|
|
};
|
|
|
|
var bindDetailsSummary = function(summary, details){
|
|
summary = $(summary);
|
|
details = $(details);
|
|
var oldSummary = $.data(details[0], 'summaryElement');
|
|
$.data(summary[0], 'detailsElement', details);
|
|
if(!oldSummary || summary[0] !== oldSummary[0]){
|
|
if(oldSummary){
|
|
if(oldSummary.hasClass('fallback-summary')){
|
|
oldSummary.remove();
|
|
} else {
|
|
oldSummary
|
|
.off('.summaryPolyfill')
|
|
.removeData('detailsElement')
|
|
.removeAttr('role')
|
|
.removeAttr('tabindex')
|
|
.removeAttr('aria-expanded')
|
|
.removeClass('summary-button')
|
|
.find('span.details-open-indicator')
|
|
.remove()
|
|
;
|
|
}
|
|
}
|
|
$.data(details[0], 'summaryElement', summary);
|
|
details.prop('open', details.prop('open'));
|
|
}
|
|
};
|
|
var getSummary = function(details){
|
|
var summary = $.data(details, 'summaryElement');
|
|
if(!summary){
|
|
summary = $(details).children('summary:first-child');
|
|
if(!summary[0]){
|
|
$(details).prependPolyfill('<summary class="fallback-summary">'+ options.text +'</summary>');
|
|
summary = $.data(details, 'summaryElement');
|
|
} else {
|
|
bindDetailsSummary(summary, details);
|
|
}
|
|
}
|
|
return summary;
|
|
};
|
|
|
|
// var isOriginalPrevented = function(e){
|
|
// var src = e.originalEvent;
|
|
// if(!src){return e.isDefaultPrevented();}
|
|
//
|
|
// return src.defaultPrevented || src.returnValue === false ||
|
|
// src.getPreventDefault && src.getPreventDefault();
|
|
// };
|
|
|
|
webshims.createElement('summary', function(){
|
|
var details = isInterActiveSummary(this);
|
|
if(!details || $.data(this, 'detailsElement')){return;}
|
|
var timer;
|
|
var stopNativeClickTest;
|
|
var tabindex = $.attr(this, 'tabIndex') || '0';
|
|
bindDetailsSummary(this, details);
|
|
$(this)
|
|
.on({
|
|
'focus.summaryPolyfill': function(){
|
|
$(this).addClass('summary-has-focus');
|
|
},
|
|
'blur.summaryPolyfill': function(){
|
|
$(this).removeClass('summary-has-focus');
|
|
},
|
|
'mouseenter.summaryPolyfill': function(){
|
|
$(this).addClass('summary-has-hover');
|
|
},
|
|
'mouseleave.summaryPolyfill': function(){
|
|
$(this).removeClass('summary-has-hover');
|
|
},
|
|
'click.summaryPolyfill': function(e){
|
|
var details = isInterActiveSummary(this);
|
|
if(details){
|
|
if(!stopNativeClickTest && e.originalEvent){
|
|
stopNativeClickTest = true;
|
|
e.stopImmediatePropagation();
|
|
e.preventDefault();
|
|
$(this).trigger('click');
|
|
stopNativeClickTest = false;
|
|
return false;
|
|
} else {
|
|
clearTimeout(timer);
|
|
|
|
timer = setTimeout(function(){
|
|
if(!e.isDefaultPrevented()){
|
|
details.prop('open', !details.prop('open'));
|
|
}
|
|
}, 0);
|
|
}
|
|
}
|
|
},
|
|
'keydown.summaryPolyfill': function(e){
|
|
if( (e.keyCode == 13 || e.keyCode == 32) && !e.isDefaultPrevented()){
|
|
stopNativeClickTest = true;
|
|
e.preventDefault();
|
|
$(this).trigger('click');
|
|
stopNativeClickTest = false;
|
|
}
|
|
}
|
|
})
|
|
.attr({tabindex: tabindex, role: 'button'})
|
|
.prepend('<span class="details-open-indicator" />')
|
|
;
|
|
webshims.moveToFirstEvent(this, 'click');
|
|
});
|
|
|
|
var initDetails;
|
|
webshims.defineNodeNamesBooleanProperty('details', 'open', function(val){
|
|
var summary = $($.data(this, 'summaryElement'));
|
|
if(!summary){return;}
|
|
var action = (val) ? 'removeClass' : 'addClass';
|
|
var details = $(this);
|
|
if (!initDetails && options.animate){
|
|
details.stop().css({width: '', height: ''});
|
|
var start = {
|
|
width: details.width(),
|
|
height: details.height()
|
|
};
|
|
}
|
|
summary.attr('aria-expanded', ''+val);
|
|
details[action]('closed-details-summary').children().not(summary[0])[action]('closed-details-child');
|
|
if(!initDetails && options.animate){
|
|
var end = {
|
|
width: details.width(),
|
|
height: details.height()
|
|
};
|
|
details.css(start).animate(end, {
|
|
complete: function(){
|
|
$(this).css({width: '', height: ''});
|
|
}
|
|
});
|
|
}
|
|
|
|
});
|
|
webshims.createElement('details', function(){
|
|
initDetails = true;
|
|
var summary = getSummary(this);
|
|
$.prop(this, 'open', $.prop(this, 'open'));
|
|
initDetails = false;
|
|
});
|
|
});
|
|
;webshims.register('mediaelement-jaris', function($, webshims, window, document, undefined, options){
|
|
"use strict";
|
|
|
|
var mediaelement = webshims.mediaelement;
|
|
var swfmini = window.swfmini;
|
|
var support = webshims.support;
|
|
var hasNative = support.mediaelement;
|
|
var hasFlash = swfmini.hasFlashPlayerVersion('11.3');
|
|
var loadedSwf = 0;
|
|
var needsLoadPreload = 'ActiveXObject' in window && hasNative;
|
|
var getProps = {
|
|
paused: true,
|
|
ended: false,
|
|
currentSrc: '',
|
|
duration: window.NaN,
|
|
readyState: 0,
|
|
networkState: 0,
|
|
videoHeight: 0,
|
|
videoWidth: 0,
|
|
seeking: false,
|
|
error: null,
|
|
buffered: {
|
|
start: function(index){
|
|
if(index){
|
|
webshims.error('buffered index size error');
|
|
return;
|
|
}
|
|
return 0;
|
|
},
|
|
end: function(index){
|
|
if(index){
|
|
webshims.error('buffered index size error');
|
|
return;
|
|
}
|
|
return 0;
|
|
},
|
|
length: 0
|
|
}
|
|
};
|
|
var getPropKeys = Object.keys(getProps);
|
|
|
|
var getSetProps = {
|
|
currentTime: 0,
|
|
volume: 1,
|
|
muted: false
|
|
};
|
|
var getSetPropKeys = Object.keys(getSetProps);
|
|
|
|
var playerStateObj = $.extend({
|
|
isActive: 'html5',
|
|
activating: 'html5',
|
|
wasSwfReady: false,
|
|
_usermedia: null,
|
|
_bufferedEnd: 0,
|
|
_bufferedStart: 0,
|
|
currentTime: 0,
|
|
lastCalledTime: -500,
|
|
_ppFlag: undefined,
|
|
_calledMeta: false,
|
|
lastDuration: 0,
|
|
_timeDif: 0.3
|
|
}, getProps, getSetProps);
|
|
|
|
|
|
var getSwfDataFromElem = function(elem){
|
|
try {
|
|
(elem.nodeName);
|
|
} catch(er){
|
|
return null;
|
|
}
|
|
var data = webshims.data(elem, 'mediaelement');
|
|
return (data && data.isActive == 'third') ? data : null;
|
|
};
|
|
|
|
var trigger = function(elem, evt){
|
|
evt = $.Event(evt);
|
|
evt.preventDefault();
|
|
$.event.trigger(evt, undefined, elem);
|
|
};
|
|
|
|
var playerSwfPath = options.playerPath || webshims.cfg.basePath + "swf/" + (options.playerName || 'JarisFLVPlayer.swf');
|
|
|
|
webshims.extendUNDEFProp(options.params, {
|
|
allowscriptaccess: 'always',
|
|
allowfullscreen: 'true',
|
|
wmode: 'transparent',
|
|
allowNetworking: 'all'
|
|
});
|
|
webshims.extendUNDEFProp(options.vars, {
|
|
controltype: '1',
|
|
jsapi: '1'
|
|
});
|
|
webshims.extendUNDEFProp(options.attrs, {
|
|
bgcolor: '#000000'
|
|
});
|
|
options.playerPath = playerSwfPath;
|
|
|
|
var setReadyState = function(readyState, data){
|
|
if(readyState < 3){
|
|
clearTimeout(data._canplaythroughTimer);
|
|
}
|
|
if(readyState >= 3 && data.readyState < 3){
|
|
data.readyState = readyState;
|
|
trigger(data._elem, 'canplay');
|
|
if(!data.paused){
|
|
trigger(data._elem, 'playing');
|
|
}
|
|
clearTimeout(data._canplaythroughTimer);
|
|
data._canplaythroughTimer = setTimeout(function(){
|
|
setReadyState(4, data);
|
|
}, 4000);
|
|
}
|
|
if(readyState >= 4 && data.readyState < 4){
|
|
data.readyState = readyState;
|
|
trigger(data._elem, 'canplaythrough');
|
|
}
|
|
data.readyState = readyState;
|
|
};
|
|
var callSeeked = function(data){
|
|
if(data.seeking && Math.abs(data.currentTime - data._lastSeektime) < 2){
|
|
data.seeking = false;
|
|
$(data._elem).triggerHandler('seeked');
|
|
}
|
|
};
|
|
|
|
|
|
mediaelement.jarisEvent = {};
|
|
var localConnectionTimer;
|
|
var onEvent = {
|
|
onPlayPause: function(jaris, data, override){
|
|
var playing, type;
|
|
var idled = data.paused || data.ended;
|
|
if(override == null){
|
|
try {
|
|
playing = data.api.api_get("isPlaying");
|
|
} catch(e){}
|
|
} else {
|
|
playing = override;
|
|
}
|
|
if(playing == idled || playing == null){
|
|
|
|
data.paused = !playing;
|
|
type = data.paused ? 'pause' : 'play';
|
|
data._ppFlag = true;
|
|
trigger(data._elem, type);
|
|
if(data.readyState < 3){
|
|
setReadyState(3, data);
|
|
}
|
|
if(!data.paused){
|
|
trigger(data._elem, 'playing');
|
|
}
|
|
}
|
|
},
|
|
onSeek: function(jaris, data){
|
|
data._lastSeektime = jaris.seekTime;
|
|
|
|
data.seeking = true;
|
|
$(data._elem).triggerHandler('seeking');
|
|
clearTimeout(data._seekedTimer);
|
|
data._seekedTimer = setTimeout(function(){
|
|
callSeeked(data);
|
|
data.seeking = false;
|
|
}, 300);
|
|
},
|
|
onConnectionFailed: function(jaris, data){
|
|
mediaelement.setError(data._elem, 'flash connection error');
|
|
},
|
|
onNotBuffering: function(jaris, data){
|
|
setReadyState(3, data);
|
|
},
|
|
onDataInitialized: function(jaris, data){
|
|
|
|
var oldDur = data.duration;
|
|
var durDelta;
|
|
data.duration = jaris.duration;
|
|
if(oldDur == data.duration || isNaN(data.duration)){return;}
|
|
|
|
if(data._calledMeta && ((durDelta = Math.abs(data.lastDuration - data.duration)) < 2)){return;}
|
|
|
|
|
|
|
|
data.videoHeight = jaris.height;
|
|
data.videoWidth = jaris.width;
|
|
|
|
if(!data.networkState){
|
|
data.networkState = 2;
|
|
}
|
|
if(data.readyState < 1){
|
|
setReadyState(1, data);
|
|
}
|
|
clearTimeout(data._durationChangeTimer);
|
|
if(data._calledMeta && data.duration){
|
|
data._durationChangeTimer = setTimeout(function(){
|
|
data.lastDuration = data.duration;
|
|
trigger(data._elem, 'durationchange');
|
|
}, durDelta > 50 ? 0 : durDelta > 9 ? 9 : 99);
|
|
} else {
|
|
data.lastDuration = data.duration;
|
|
if(data.duration){
|
|
trigger(data._elem, 'durationchange');
|
|
}
|
|
if(!data._calledMeta){
|
|
trigger(data._elem, 'loadedmetadata');
|
|
}
|
|
|
|
if(data.duration > 1 && data.duration < 140){
|
|
data._timeDif = 0.2;
|
|
} else if(data.duration < 600) {
|
|
data._timeDif = 0.25;
|
|
} else {
|
|
data._timeDif = 0.30;
|
|
}
|
|
}
|
|
data._calledMeta = true;
|
|
},
|
|
onBuffering: function(jaris, data){
|
|
if(data.ended){
|
|
data.ended = false;
|
|
}
|
|
setReadyState(1, data);
|
|
trigger(data._elem, 'waiting');
|
|
},
|
|
onTimeUpdate: function(jaris, data){
|
|
var timeDif = data.currentTime - data.lastCalledTime;
|
|
if(data.ended){
|
|
data.ended = false;
|
|
}
|
|
if(data.readyState < 3){
|
|
setReadyState(3, data);
|
|
trigger(data._elem, 'playing');
|
|
}
|
|
if(data.seeking){
|
|
callSeeked(data);
|
|
}
|
|
|
|
if(timeDif > data._timeDif || timeDif < -0.3){
|
|
data.lastCalledTime = data.currentTime;
|
|
$.event.trigger('timeupdate', undefined, data._elem, true);
|
|
}
|
|
|
|
},
|
|
onProgress: function(jaris, data){
|
|
if(data.ended){
|
|
data.ended = false;
|
|
}
|
|
if(!data.duration || isNaN(data.duration)){
|
|
return;
|
|
}
|
|
var percentage = jaris.loaded / jaris.total;
|
|
|
|
if(percentage > 0.02 && percentage < 0.2){
|
|
setReadyState(3, data);
|
|
} else if(percentage > 0.2){
|
|
if(percentage > 0.95){
|
|
percentage = 1;
|
|
data.networkState = 1;
|
|
}
|
|
setReadyState(4, data);
|
|
}
|
|
if(data._bufferedEnd && (data._bufferedEnd > percentage)){
|
|
data._bufferedStart = data.currentTime || 0;
|
|
}
|
|
|
|
data._bufferedEnd = percentage;
|
|
data.buffered.length = 1;
|
|
|
|
$.event.trigger('progress', undefined, data._elem, true);
|
|
},
|
|
onPlaybackFinished: function(jaris, data){
|
|
if(data.readyState < 4){
|
|
setReadyState(4, data);
|
|
}
|
|
data.ended = true;
|
|
trigger(data._elem, 'ended');
|
|
},
|
|
onVolumeChange: function(jaris, data){
|
|
if(data.volume != jaris.volume || data.muted != jaris.mute){
|
|
data.volume = jaris.volume;
|
|
data.muted = jaris.mute;
|
|
trigger(data._elem, 'volumechange');
|
|
}
|
|
},
|
|
ready: (function(){
|
|
var testAPI = function(data){
|
|
var passed = true;
|
|
|
|
try {
|
|
data.api.api_get('volume');
|
|
} catch(er){
|
|
passed = false;
|
|
}
|
|
return passed;
|
|
};
|
|
|
|
return function(jaris, data){
|
|
var i = 0;
|
|
|
|
var doneFn = function(){
|
|
if(i > 9){
|
|
data.tryedReframeing = 0;
|
|
return;
|
|
}
|
|
i++;
|
|
|
|
data.tryedReframeing++;
|
|
if(testAPI(data)){
|
|
data.wasSwfReady = true;
|
|
data.tryedReframeing = 0;
|
|
startAutoPlay(data);
|
|
workActionQueue(data);
|
|
} else if(data.tryedReframeing < 6) {
|
|
if(data.tryedReframeing < 3){
|
|
data.reframeTimer = setTimeout(doneFn, 9);
|
|
data.shadowElem.css({overflow: 'visible'});
|
|
setTimeout(function(){
|
|
data.shadowElem.css({overflow: 'hidden'});
|
|
}, 1);
|
|
} else {
|
|
data.shadowElem.css({overflow: 'hidden'});
|
|
$(data._elem).mediaLoad();
|
|
}
|
|
} else {
|
|
clearTimeout(data.reframeTimer);
|
|
webshims.error("reframing error");
|
|
}
|
|
};
|
|
if(!data || !data.api){return;}
|
|
if(!data.tryedReframeing){
|
|
data.tryedReframeing = 0;
|
|
}
|
|
clearTimeout(localConnectionTimer);
|
|
clearTimeout(data.reframeTimer);
|
|
data.shadowElem.removeClass('flashblocker-assumed');
|
|
|
|
if(!i){
|
|
doneFn();
|
|
} else {
|
|
data.reframeTimer = setTimeout(doneFn, 9);
|
|
}
|
|
|
|
};
|
|
})()
|
|
};
|
|
|
|
onEvent.onMute = onEvent.onVolumeChange;
|
|
mediaelement.onEvent = onEvent;
|
|
|
|
var workActionQueue = function(data){
|
|
var actionLen = data.actionQueue.length;
|
|
var i = 0;
|
|
var operation;
|
|
|
|
if(actionLen && data.isActive == 'third'){
|
|
while(data.actionQueue.length && actionLen > i){
|
|
i++;
|
|
operation = data.actionQueue.shift();
|
|
try{
|
|
data.api[operation.fn].apply(data.api, operation.args);
|
|
} catch(er){
|
|
webshims.warn(er);
|
|
}
|
|
}
|
|
}
|
|
if(data.actionQueue.length){
|
|
data.actionQueue = [];
|
|
}
|
|
};
|
|
var startAutoPlay = function(data){
|
|
if(!data){return;}
|
|
if( (data._ppFlag === undefined && ($.prop(data._elem, 'autoplay')) || !data.paused)){
|
|
setTimeout(function(){
|
|
if(data.isActive == 'third' && (data._ppFlag === undefined || !data.paused)){
|
|
|
|
try {
|
|
$(data._elem).play();
|
|
data._ppFlag = true;
|
|
} catch(er){}
|
|
}
|
|
}, 1);
|
|
}
|
|
|
|
if(data.muted){
|
|
$.prop(data._elem, 'muted', true);
|
|
}
|
|
if(data.volume != 1){
|
|
$.prop(data._elem, 'volume', data.volume);
|
|
}
|
|
};
|
|
|
|
|
|
var addMediaToStopEvents = $.noop;
|
|
if(hasNative){
|
|
var stopEvents = {
|
|
play: 1,
|
|
playing: 1
|
|
};
|
|
var hideEvtArray = ['play', 'pause', 'playing', 'loadstart', 'canplay', 'progress', 'waiting', 'ended', 'loadedmetadata', 'durationchange', 'emptied'];
|
|
var hidevents = hideEvtArray.map(function(evt){
|
|
return evt +'.webshimspolyfill';
|
|
}).join(' ');
|
|
|
|
var hidePlayerEvents = function(event){
|
|
var data = webshims.data(event.target, 'mediaelement');
|
|
if(!data){return;}
|
|
var isNativeHTML5 = ( event.originalEvent && event.originalEvent.type === event.type );
|
|
if( isNativeHTML5 == (data.activating == 'third') ){
|
|
event.stopImmediatePropagation();
|
|
|
|
if(stopEvents[event.type]){
|
|
if(data.isActive != data.activating){
|
|
$(event.target).pause();
|
|
} else if(isNativeHTML5){
|
|
($.prop(event.target, 'pause')._supvalue || $.noop).apply(event.target);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
addMediaToStopEvents = function(elem){
|
|
$(elem)
|
|
.off(hidevents)
|
|
.on(hidevents, hidePlayerEvents)
|
|
;
|
|
hideEvtArray.forEach(function(evt){
|
|
webshims.moveToFirstEvent(elem, evt);
|
|
});
|
|
};
|
|
addMediaToStopEvents(document);
|
|
}
|
|
|
|
|
|
mediaelement.setActive = function(elem, type, data){
|
|
if(!data){
|
|
data = webshims.data(elem, 'mediaelement');
|
|
}
|
|
if(!data || data.isActive == type){return;}
|
|
if(type != 'html5' && type != 'third'){
|
|
webshims.warn('wrong type for mediaelement activating: '+ type);
|
|
}
|
|
var shadowData = webshims.data(elem, 'shadowData');
|
|
data.activating = type;
|
|
$(elem).pause();
|
|
data.isActive = type;
|
|
if(type == 'third'){
|
|
shadowData.shadowElement = shadowData.shadowFocusElement = data.shadowElem[0];
|
|
$(elem).addClass('swf-api-active nonnative-api-active').hide().getShadowElement().show();
|
|
} else {
|
|
$(elem).removeClass('swf-api-active nonnative-api-active').show().getShadowElement().hide();
|
|
shadowData.shadowElement = shadowData.shadowFocusElement = false;
|
|
}
|
|
$(elem).trigger('mediaelementapichange');
|
|
};
|
|
|
|
|
|
|
|
var resetSwfProps = (function(){
|
|
var resetProtoProps = ['_calledMeta', 'lastDuration', '_bufferedEnd', 'lastCalledTime', '_usermedia', '_bufferedStart', '_ppFlag', 'currentSrc', 'currentTime', 'duration', 'ended', 'networkState', 'paused', 'seeking', 'videoHeight', 'videoWidth'];
|
|
var len = resetProtoProps.length;
|
|
return function(data){
|
|
|
|
if(!data){return;}
|
|
clearTimeout(data._seekedTimer);
|
|
var lenI = len;
|
|
var networkState = data.networkState;
|
|
setReadyState(0, data);
|
|
clearTimeout(data._durationChangeTimer);
|
|
while(--lenI > -1){
|
|
delete data[resetProtoProps[lenI]];
|
|
}
|
|
data.actionQueue = [];
|
|
data.buffered.length = 0;
|
|
if(networkState){
|
|
trigger(data._elem, 'emptied');
|
|
}
|
|
};
|
|
})();
|
|
|
|
|
|
var getComputedDimension = (function(){
|
|
var dimCache = {};
|
|
var getVideoDims = function(data){
|
|
var ret, poster, img;
|
|
if(dimCache[data.currentSrc]){
|
|
ret = dimCache[data.currentSrc];
|
|
} else if(data.videoHeight && data.videoWidth){
|
|
dimCache[data.currentSrc] = {
|
|
width: data.videoWidth,
|
|
height: data.videoHeight
|
|
};
|
|
ret = dimCache[data.currentSrc];
|
|
} else if((poster = $.attr(data._elem, 'poster'))){
|
|
ret = dimCache[poster];
|
|
if(!ret){
|
|
img = document.createElement('img');
|
|
img.onload = function(){
|
|
dimCache[poster] = {
|
|
width: this.width,
|
|
height: this.height
|
|
};
|
|
|
|
if(dimCache[poster].height && dimCache[poster].width){
|
|
setElementDimension(data, $.prop(data._elem, 'controls'));
|
|
} else {
|
|
delete dimCache[poster];
|
|
}
|
|
img.onload = null;
|
|
};
|
|
img.src = poster;
|
|
if(img.complete && img.onload){
|
|
img.onload();
|
|
}
|
|
}
|
|
}
|
|
return ret || {width: 300, height: data._elemNodeName == 'video' ? 150 : 50};
|
|
};
|
|
|
|
var getCssStyle = function(elem, style){
|
|
return elem.style[style] || (elem.currentStyle && elem.currentStyle[style]) || (window.getComputedStyle && (window.getComputedStyle( elem, null ) || {} )[style]) || '';
|
|
};
|
|
var minMaxProps = ['minWidth', 'maxWidth', 'minHeight', 'maxHeight'];
|
|
|
|
var addMinMax = function(elem, ret){
|
|
var i, prop;
|
|
var hasMinMax = false;
|
|
for (i = 0; i < 4; i++) {
|
|
prop = getCssStyle(elem, minMaxProps[i]);
|
|
if(parseFloat(prop, 10)){
|
|
hasMinMax = true;
|
|
ret[minMaxProps[i]] = prop;
|
|
}
|
|
}
|
|
return hasMinMax;
|
|
};
|
|
var retFn = function(data){
|
|
var videoDims, ratio;
|
|
var elem = data._elem;
|
|
var autos = {
|
|
width: getCssStyle(elem, 'width') == 'auto',
|
|
height: getCssStyle(elem, 'height') == 'auto'
|
|
};
|
|
var ret = {
|
|
width: !autos.width && $(elem).width(),
|
|
height: !autos.height && $(elem).height()
|
|
};
|
|
|
|
if(autos.width || autos.height){
|
|
videoDims = getVideoDims(data);
|
|
ratio = videoDims.width / videoDims.height;
|
|
|
|
if(autos.width && autos.height){
|
|
ret.width = videoDims.width;
|
|
ret.height = videoDims.height;
|
|
} else if(autos.width){
|
|
ret.width = ret.height * ratio;
|
|
} else if(autos.height){
|
|
ret.height = ret.width / ratio;
|
|
}
|
|
|
|
if(addMinMax(elem, ret)){
|
|
data.shadowElem.css(ret);
|
|
if(autos.width){
|
|
ret.width = data.shadowElem.height() * ratio;
|
|
}
|
|
if(autos.height){
|
|
ret.height = ((autos.width) ? ret.width : data.shadowElem.width()) / ratio;
|
|
}
|
|
if(autos.width && autos.height){
|
|
data.shadowElem.css(ret);
|
|
ret.height = data.shadowElem.width() / ratio;
|
|
ret.width = ret.height * ratio;
|
|
|
|
data.shadowElem.css(ret);
|
|
ret.width = data.shadowElem.height() * ratio;
|
|
ret.height = ret.width / ratio;
|
|
|
|
}
|
|
if(!webshims.support.mediaelement){
|
|
ret.width = data.shadowElem.width();
|
|
ret.height = data.shadowElem.height();
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
return retFn;
|
|
})();
|
|
|
|
var setElementDimension = function(data, hasControls){
|
|
var dims;
|
|
|
|
var box = data.shadowElem;
|
|
$(data._elem)[hasControls ? 'addClass' : 'removeClass']('webshims-controls');
|
|
|
|
if(data.isActive == 'third' || data.activating == 'third'){
|
|
if(data._elemNodeName == 'audio' && !hasControls){
|
|
box.css({width: 0, height: 0});
|
|
} else {
|
|
data._elem.style.display = '';
|
|
dims = getComputedDimension(data);
|
|
data._elem.style.display = 'none';
|
|
box.css(dims);
|
|
}
|
|
}
|
|
};
|
|
|
|
var bufferSrc = (function(){
|
|
var preloads = {
|
|
'': 1,
|
|
'auto': 1
|
|
};
|
|
return function(elem){
|
|
var preload = $.attr(elem, 'preload');
|
|
if(preload == null || preload == 'none' || $.prop(elem, 'autoplay')){
|
|
return false;
|
|
}
|
|
preload = $.prop(elem, 'preload');
|
|
return !!(preloads[preload] || (preload == 'metadata' && $(elem).is('.preload-in-doubt, video:not([poster])')));
|
|
};
|
|
})();
|
|
|
|
var regs = {
|
|
A: /&/g,
|
|
a: /&/g,
|
|
e: /\=/g,
|
|
q: /\?/g
|
|
},
|
|
replaceVar = function(val){
|
|
return (val.replace) ? val.replace(regs.A, '%26').replace(regs.a, '%26').replace(regs.e, '%3D').replace(regs.q, '%3F') : val;
|
|
};
|
|
|
|
if('matchMedia' in window){
|
|
var allowMediaSorting = false;
|
|
try {
|
|
allowMediaSorting = window.matchMedia('only all').matches;
|
|
} catch(er){}
|
|
if(allowMediaSorting){
|
|
mediaelement.sortMedia = function(src1, src2){
|
|
try {
|
|
src1 = !src1.media || matchMedia( src1.media ).matches;
|
|
src2 = !src2.media || matchMedia( src2.media ).matches;
|
|
} catch(er){
|
|
return 0;
|
|
}
|
|
return src1 == src2 ?
|
|
0 :
|
|
src1 ? -1
|
|
: 1;
|
|
};
|
|
}
|
|
}
|
|
|
|
mediaelement.resetSwfProps = resetSwfProps;
|
|
mediaelement.createSWF = function( elem, canPlaySrc, data ){
|
|
if(!hasFlash){
|
|
setTimeout(function(){
|
|
$(elem).mediaLoad(); //<- this should produce a mediaerror
|
|
}, 1);
|
|
return;
|
|
}
|
|
|
|
var attrStyle = {};
|
|
|
|
if(loadedSwf < 1){
|
|
loadedSwf = 1;
|
|
} else {
|
|
loadedSwf++;
|
|
}
|
|
if(!data){
|
|
data = webshims.data(elem, 'mediaelement');
|
|
}
|
|
|
|
if((attrStyle.height = $.attr(elem, 'height') || '') || (attrStyle.width = $.attr(elem, 'width') || '')){
|
|
$(elem).css(attrStyle);
|
|
webshims.warn("width or height content attributes used. Webshims prefers the usage of CSS (computed styles or inline styles) to detect size of a video/audio. It's really more powerfull.");
|
|
}
|
|
var box;
|
|
var streamRequest = canPlaySrc.streamrequest;
|
|
var isStream = canPlaySrc.type == 'jarisplayer/stream';
|
|
|
|
|
|
var hasControls = $.prop(elem, 'controls');
|
|
var elemId = 'jarisplayer-'+ webshims.getID(elem);
|
|
|
|
|
|
var elemNodeName = elem.nodeName.toLowerCase();
|
|
|
|
var setDimension = function(){
|
|
if(data.isActive == 'third'){
|
|
setElementDimension(data, $.prop(elem, 'controls'));
|
|
}
|
|
};
|
|
|
|
if(isStream && !streamRequest){
|
|
webshim.usermedia.attach(elem, canPlaySrc, data);
|
|
return;
|
|
}
|
|
|
|
if(data && data.swfCreated){
|
|
mediaelement.setActive(elem, 'third', data);
|
|
|
|
data.currentSrc = '';
|
|
|
|
data.shadowElem.html('<div id="'+ elemId +'">');
|
|
|
|
data.api = false;
|
|
data.actionQueue = [];
|
|
box = data.shadowElem;
|
|
resetSwfProps(data);
|
|
data.currentSrc = canPlaySrc.srcProp;
|
|
|
|
} else {
|
|
$(document.getElementById('wrapper-'+ elemId )).remove();
|
|
box = $('<div class="polyfill-'+ (elemNodeName) +' polyfill-mediaelement '+ webshims.shadowClass +'" id="wrapper-'+ elemId +'"><div id="'+ elemId +'"></div>')
|
|
.css({
|
|
position: 'relative',
|
|
overflow: 'hidden'
|
|
})
|
|
;
|
|
data = webshims.data(elem, 'mediaelement', webshims.objectCreate(playerStateObj, {
|
|
actionQueue: {
|
|
value: []
|
|
},
|
|
shadowElem: {
|
|
value: box
|
|
},
|
|
_elemNodeName: {
|
|
value: elemNodeName
|
|
},
|
|
_elem: {
|
|
value: elem
|
|
},
|
|
currentSrc: {
|
|
value: streamRequest ? '' : canPlaySrc.srcProp
|
|
},
|
|
swfCreated: {
|
|
value: true
|
|
},
|
|
id: {
|
|
value: elemId.replace(/-/g, '')
|
|
},
|
|
buffered: {
|
|
value: {
|
|
start: function(index){
|
|
if(index >= data.buffered.length){
|
|
webshims.error('buffered index size error');
|
|
return;
|
|
}
|
|
return 0;
|
|
},
|
|
end: function(index){
|
|
if(index >= data.buffered.length){
|
|
webshims.error('buffered index size error');
|
|
return;
|
|
}
|
|
return ( (data.duration - data._bufferedStart) * data._bufferedEnd) + data._bufferedStart;
|
|
},
|
|
length: 0
|
|
}
|
|
}
|
|
}));
|
|
|
|
|
|
|
|
box.insertBefore(elem);
|
|
|
|
if(hasNative){
|
|
$.extend(data, {volume: $.prop(elem, 'volume'), muted: $.prop(elem, 'muted'), paused: $.prop(elem, 'paused')});
|
|
}
|
|
|
|
webshims.addShadowDom(elem, box);
|
|
if(!webshims.data(elem, 'mediaelement')){
|
|
webshims.data(elem, 'mediaelement', data);
|
|
}
|
|
addMediaToStopEvents(elem);
|
|
|
|
mediaelement.setActive(elem, 'third', data);
|
|
|
|
setElementDimension(data, hasControls);
|
|
|
|
$(elem)
|
|
|
|
.on({
|
|
'updatemediaelementdimensions loadedmetadata emptied': setDimension,
|
|
'remove': function(e){
|
|
if(!e.originalEvent && mediaelement.jarisEvent[data.id] && mediaelement.jarisEvent[data.id].elem == elem){
|
|
delete mediaelement.jarisEvent[data.id];
|
|
clearTimeout(localConnectionTimer);
|
|
clearTimeout(data.flashBlock);
|
|
}
|
|
}
|
|
})
|
|
.onWSOff('updateshadowdom', setDimension)
|
|
;
|
|
}
|
|
|
|
if(mediaelement.jarisEvent[data.id] && mediaelement.jarisEvent[data.id].elem != elem){
|
|
webshims.error('something went wrong');
|
|
return;
|
|
} else if(!mediaelement.jarisEvent[data.id]){
|
|
|
|
mediaelement.jarisEvent[data.id] = function(jaris){
|
|
if(jaris.type == 'ready'){
|
|
var onReady = function(){
|
|
if(data.api){
|
|
if(!data.paused){
|
|
data.api.api_play();
|
|
}
|
|
if(bufferSrc(elem)){
|
|
data.api.api_preload();
|
|
}
|
|
onEvent.ready(jaris, data);
|
|
}
|
|
};
|
|
if(data.api){
|
|
onReady();
|
|
} else {
|
|
setTimeout(onReady, 9);
|
|
}
|
|
} else {
|
|
data.currentTime = jaris.position;
|
|
|
|
if(data.api){
|
|
if(!data._calledMeta && isNaN(jaris.duration) && data.duration != jaris.duration && isNaN(data.duration)){
|
|
onEvent.onDataInitialized(jaris, data);
|
|
}
|
|
|
|
if(!data._ppFlag && jaris.type != 'onPlayPause'){
|
|
onEvent.onPlayPause(jaris, data);
|
|
}
|
|
|
|
if(onEvent[jaris.type]){
|
|
onEvent[jaris.type](jaris, data);
|
|
}
|
|
}
|
|
data.duration = jaris.duration;
|
|
}
|
|
};
|
|
mediaelement.jarisEvent[data.id].elem = elem;
|
|
}
|
|
|
|
createSwf(elem, canPlaySrc, data, elemId, hasControls, elemNodeName);
|
|
|
|
if(!streamRequest){
|
|
trigger(data._elem, 'loadstart');
|
|
}
|
|
};
|
|
|
|
var createSwf = function(elem, canPlaySrc, data, elemId, hasControls, elemNodeName){
|
|
var vars, elemVars, params, attrs;
|
|
var isRtmp = canPlaySrc.type == 'audio/rtmp' || canPlaySrc.type == 'video/rtmp';
|
|
var isUserStream = canPlaySrc.type == 'jarisplayer/stream';
|
|
|
|
vars = $.extend({}, options.vars, {
|
|
poster: replaceVar($.attr(elem, 'poster') && $.prop(elem, 'poster') || ''),
|
|
source: replaceVar(canPlaySrc.streamId || canPlaySrc.srcProp),
|
|
server: replaceVar(canPlaySrc.server || '')
|
|
});
|
|
|
|
elemVars = $(elem).data('vars') || {};
|
|
|
|
$.extend(vars,
|
|
{
|
|
id: elemId,
|
|
evtId: data.id,
|
|
controls: ''+(!isUserStream && hasControls),
|
|
autostart: 'false',
|
|
nodename: elemNodeName
|
|
},
|
|
elemVars
|
|
);
|
|
|
|
if(isRtmp){
|
|
vars.streamtype = 'rtmp';
|
|
} else if(isUserStream){
|
|
vars.streamtype = 'usermedia';
|
|
} else if(canPlaySrc.type == 'audio/mpeg' || canPlaySrc.type == 'audio/mp3'){
|
|
vars.type = 'audio';
|
|
vars.streamtype = 'file';
|
|
} else if(canPlaySrc.type == 'video/youtube'){
|
|
vars.streamtype = 'youtube';
|
|
}
|
|
|
|
attrs = $.extend(
|
|
{},
|
|
options.attrs,
|
|
{
|
|
name: elemId,
|
|
id: elemId
|
|
},
|
|
$(elem).data('attrs')
|
|
);
|
|
|
|
params = $.extend(
|
|
{},
|
|
options.params,
|
|
$(elem).data('params')
|
|
);
|
|
|
|
options.changeSWF(vars, elem, canPlaySrc, data, 'embed');
|
|
clearTimeout(data.flashBlock);
|
|
|
|
swfmini.embedSWF(playerSwfPath, elemId, "100%", "100%", "9.0.115", false, vars, params, attrs, function(swfData){
|
|
if(swfData.success){
|
|
var fBlocker = function(){
|
|
if((!swfData.ref.parentNode && box[0].parentNode) || swfData.ref.style.display == "none"){
|
|
box.addClass('flashblocker-assumed');
|
|
$(elem).trigger('flashblocker');
|
|
webshims.warn("flashblocker assumed");
|
|
}
|
|
$(swfData.ref).css({'minHeight': '2px', 'minWidth': '2px', display: 'block'});
|
|
};
|
|
data.api = swfData.ref;
|
|
|
|
if(!hasControls){
|
|
$(swfData.ref).attr('tabindex', '-1').css('outline', 'none');
|
|
}
|
|
|
|
data.flashBlock = setTimeout(fBlocker, 99);
|
|
|
|
if(!localConnectionTimer){
|
|
clearTimeout(localConnectionTimer);
|
|
localConnectionTimer = setTimeout(function(){
|
|
fBlocker();
|
|
var flash = $(swfData.ref);
|
|
if(flash[0].offsetWidth > 1 && flash[0].offsetHeight > 1 && location.protocol.indexOf('file:') === 0){
|
|
webshims.error("Add your local development-directory to the local-trusted security sandbox: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html");
|
|
} else if(flash[0].offsetWidth < 2 || flash[0].offsetHeight < 2) {
|
|
webshims.warn("JS-SWF connection can't be established on hidden or unconnected flash objects");
|
|
}
|
|
flash = null;
|
|
}, 8000);
|
|
}
|
|
if(isUserStream){
|
|
webshim.usermedia.request(elem, canPlaySrc, data);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
var queueSwfMethod = function(elem, fn, args, data){
|
|
data = data || getSwfDataFromElem(elem);
|
|
|
|
if(data){
|
|
if(data.api && data.api[fn]){
|
|
data.api[fn].apply(data.api, args || []);
|
|
} else {
|
|
//todo add to queue
|
|
data.actionQueue.push({fn: fn, args: args});
|
|
|
|
if(data.actionQueue.length > 10){
|
|
setTimeout(function(){
|
|
if(data.actionQueue.length > 5){
|
|
data.actionQueue.shift();
|
|
}
|
|
}, 99);
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
return false;
|
|
};
|
|
mediaelement.queueSwfMethod = queueSwfMethod;
|
|
|
|
['audio', 'video'].forEach(function(nodeName){
|
|
var descs = {};
|
|
var mediaSup;
|
|
var createGetProp = function(key){
|
|
if(nodeName == 'audio' && (key == 'videoHeight' || key == 'videoWidth')){return;}
|
|
|
|
descs[key] = {
|
|
get: function(){
|
|
var data = getSwfDataFromElem(this);
|
|
if(data){
|
|
return data[key];
|
|
} else if(hasNative && mediaSup[key].prop._supget) {
|
|
return mediaSup[key].prop._supget.apply(this);
|
|
} else {
|
|
return playerStateObj[key];
|
|
}
|
|
},
|
|
writeable: false
|
|
};
|
|
};
|
|
var createGetSetProp = function(key, setFn){
|
|
createGetProp(key);
|
|
delete descs[key].writeable;
|
|
descs[key].set = setFn;
|
|
};
|
|
|
|
createGetSetProp('seeking');
|
|
|
|
createGetSetProp('volume', function(v){
|
|
var data = getSwfDataFromElem(this);
|
|
if(data){
|
|
v *= 1;
|
|
if(!isNaN(v)){
|
|
|
|
if(v < 0 || v > 1){
|
|
webshims.error('volume greater or less than allowed '+ (v / 100));
|
|
}
|
|
|
|
queueSwfMethod(this, 'api_volume', [v], data);
|
|
|
|
|
|
if(data.volume != v){
|
|
data.volume = v;
|
|
trigger(data._elem, 'volumechange');
|
|
}
|
|
data = null;
|
|
}
|
|
} else if(mediaSup.volume.prop._supset) {
|
|
return mediaSup.volume.prop._supset.apply(this, arguments);
|
|
}
|
|
});
|
|
|
|
createGetSetProp('muted', function(m){
|
|
var data = getSwfDataFromElem(this);
|
|
if(data){
|
|
m = !!m;
|
|
queueSwfMethod(this, 'api_muted', [m], data);
|
|
if(data.muted != m){
|
|
data.muted = m;
|
|
trigger(data._elem, 'volumechange');
|
|
}
|
|
data = null;
|
|
} else if(mediaSup.muted.prop._supset) {
|
|
return mediaSup.muted.prop._supset.apply(this, arguments);
|
|
}
|
|
});
|
|
|
|
|
|
createGetSetProp('currentTime', function(t){
|
|
var data = getSwfDataFromElem(this);
|
|
if(data){
|
|
t *= 1;
|
|
if (!isNaN(t)) {
|
|
queueSwfMethod(this, 'api_seek', [t], data);
|
|
}
|
|
|
|
} else if(mediaSup.currentTime.prop._supset) {
|
|
return mediaSup.currentTime.prop._supset.apply(this, arguments);
|
|
}
|
|
});
|
|
|
|
['play', 'pause'].forEach(function(fn){
|
|
descs[fn] = {
|
|
value: function(){
|
|
var data = getSwfDataFromElem(this);
|
|
|
|
if(data){
|
|
if(data.stopPlayPause){
|
|
clearTimeout(data.stopPlayPause);
|
|
}
|
|
queueSwfMethod(this, fn == 'play' ? 'api_play' : 'api_pause', [], data);
|
|
|
|
data._ppFlag = true;
|
|
if(data.paused != (fn != 'play')){
|
|
data.paused = fn != 'play';
|
|
trigger(data._elem, fn);
|
|
}
|
|
} else if(mediaSup[fn].prop._supvalue) {
|
|
return mediaSup[fn].prop._supvalue.apply(this, arguments);
|
|
}
|
|
}
|
|
};
|
|
});
|
|
|
|
getPropKeys.forEach(createGetProp);
|
|
|
|
webshims.onNodeNamesPropertyModify(nodeName, 'controls', function(val, boolProp){
|
|
var data = getSwfDataFromElem(this);
|
|
|
|
$(this)[boolProp ? 'addClass' : 'removeClass']('webshims-controls');
|
|
|
|
if(data){
|
|
if(nodeName == 'audio'){
|
|
setElementDimension(data, boolProp);
|
|
}
|
|
queueSwfMethod(this, 'api_controls', [boolProp], data);
|
|
}
|
|
});
|
|
|
|
|
|
webshims.onNodeNamesPropertyModify(nodeName, 'preload', function(val){
|
|
var data, baseData, elem;
|
|
|
|
|
|
if(bufferSrc(this)){
|
|
data = getSwfDataFromElem(this);
|
|
if(data){
|
|
queueSwfMethod(this, 'api_preload', [], data);
|
|
} else if(needsLoadPreload && this.paused && !this.error && !$.data(this, 'mediaerror') && !this.readyState && !this.networkState && !this.autoplay && $(this).is(':not(.nonnative-api-active)')){
|
|
elem = this;
|
|
baseData = webshims.data(elem, 'mediaelementBase') || webshims.data(elem, 'mediaelementBase', {});
|
|
clearTimeout(baseData.loadTimer);
|
|
baseData.loadTimer = setTimeout(function(){
|
|
$(elem).mediaLoad();
|
|
}, 9);
|
|
}
|
|
}
|
|
});
|
|
|
|
mediaSup = webshims.defineNodeNameProperties(nodeName, descs, 'prop');
|
|
|
|
if(!support.mediaDefaultMuted){
|
|
webshims.defineNodeNameProperties(nodeName, {
|
|
defaultMuted: {
|
|
get: function(){
|
|
return $.attr(this, 'muted') != null;
|
|
},
|
|
set: function(val){
|
|
if(val){
|
|
$.attr(this, 'muted', '');
|
|
} else {
|
|
$(this).removeAttr('muted');
|
|
}
|
|
}
|
|
}
|
|
}, 'prop');
|
|
}
|
|
});
|
|
|
|
var addCanvasBridge = function(){
|
|
if(!window.CanvasRenderingContext2D){
|
|
return false;
|
|
}
|
|
var _drawImage = CanvasRenderingContext2D.prototype.drawImage;
|
|
var slice = Array.prototype.slice;
|
|
var isVideo = {
|
|
video: 1,
|
|
VIDEO: 1
|
|
};
|
|
var tested = {};
|
|
|
|
if(!_drawImage){
|
|
webshim.error('canvas.drawImage feature is needed. In IE8 flashvanvas pro can be used');
|
|
}
|
|
|
|
CanvasRenderingContext2D.prototype.drawImage = function(elem){
|
|
var data, img, args, imgData;
|
|
var context = this;
|
|
|
|
if(isVideo[elem.nodeName] && (data = webshims.data(elem, 'mediaelement')) && data.isActive == 'third' && data.api.api_image){
|
|
|
|
try {
|
|
imgData = data.api.api_image();
|
|
} catch (er){
|
|
webshims.error(er);
|
|
}
|
|
if(!tested[data.currentSrc]){
|
|
tested[data.currentSrc] = true;
|
|
if(imgData == null){
|
|
webshims.error('video has to be same origin or a crossdomain.xml has to be provided. Video has to be visible for flash API');
|
|
}
|
|
}
|
|
|
|
args = slice.call(arguments, 1);
|
|
img = new Image();
|
|
|
|
//todo find a performant sync way
|
|
img.onload = function(){
|
|
args.unshift(this);
|
|
_drawImage.apply(context, args);
|
|
img.onload = null;
|
|
};
|
|
|
|
img.src = 'data:image/jpeg;base64,'+imgData;
|
|
|
|
if(img.complete){
|
|
img.onload();
|
|
}
|
|
return;
|
|
}
|
|
return _drawImage.apply(this, arguments);
|
|
};
|
|
return true;
|
|
};
|
|
|
|
if(!addCanvasBridge()){
|
|
webshims.ready('canvas', addCanvasBridge);
|
|
}
|
|
|
|
|
|
if(hasFlash && $.cleanData){
|
|
var oldClean = $.cleanData;
|
|
var objElem = document.createElement('object');
|
|
var noRemove = {
|
|
SetVariable: 1,
|
|
GetVariable: 1,
|
|
SetReturnValue: 1,
|
|
GetReturnValue: 1
|
|
};
|
|
var flashNames = {
|
|
object: 1,
|
|
OBJECT: 1
|
|
};
|
|
|
|
$.cleanData = function(elems){
|
|
var i, len, prop;
|
|
var ret = oldClean.apply(this, arguments);
|
|
if(elems && (len = elems.length) && loadedSwf){
|
|
|
|
for(i = 0; i < len; i++){
|
|
if(flashNames[elems[i].nodeName] && 'api_destroy' in elems[i]){
|
|
loadedSwf--;
|
|
try {
|
|
elems[i].api_destroy();
|
|
if(elems[i].readyState == 4){
|
|
for (prop in elems[i]) {
|
|
if (!noRemove[prop] && !objElem[prop] && typeof elems[i][prop] == "function") {
|
|
elems[i][prop] = null;
|
|
}
|
|
}
|
|
}
|
|
} catch(er){console.log(er);}
|
|
}
|
|
}
|
|
|
|
}
|
|
return ret;
|
|
};
|
|
}
|
|
|
|
if(!hasNative){
|
|
|
|
['poster', 'src'].forEach(function(prop){
|
|
webshims.defineNodeNamesProperty(prop == 'src' ? ['audio', 'video', 'source'] : ['video'], prop, {
|
|
//attr: {},
|
|
reflect: true,
|
|
propType: 'src'
|
|
});
|
|
});
|
|
|
|
webshims.defineNodeNamesProperty(['audio', 'video'], 'preload', {
|
|
reflect: true,
|
|
propType: 'enumarated',
|
|
defaultValue: '',
|
|
limitedTo: ['', 'auto', 'metadata', 'none']
|
|
});
|
|
|
|
webshims.reflectProperties('source', ['type', 'media']);
|
|
|
|
|
|
['autoplay', 'controls'].forEach(function(name){
|
|
webshims.defineNodeNamesBooleanProperty(['audio', 'video'], name);
|
|
});
|
|
|
|
webshims.defineNodeNamesProperties(['audio', 'video'], {
|
|
HAVE_CURRENT_DATA: {
|
|
value: 2
|
|
},
|
|
HAVE_ENOUGH_DATA: {
|
|
value: 4
|
|
},
|
|
HAVE_FUTURE_DATA: {
|
|
value: 3
|
|
},
|
|
HAVE_METADATA: {
|
|
value: 1
|
|
},
|
|
HAVE_NOTHING: {
|
|
value: 0
|
|
},
|
|
NETWORK_EMPTY: {
|
|
value: 0
|
|
},
|
|
NETWORK_IDLE: {
|
|
value: 1
|
|
},
|
|
NETWORK_LOADING: {
|
|
value: 2
|
|
},
|
|
NETWORK_NO_SOURCE: {
|
|
value: 3
|
|
}
|
|
|
|
}, 'prop');
|
|
|
|
|
|
if(hasFlash){
|
|
webshims.ready('WINDOWLOAD', function(){
|
|
setTimeout(function(){
|
|
if(!loadedSwf){
|
|
document.createElement('img').src = playerSwfPath;
|
|
}
|
|
}, 9);
|
|
});
|
|
}
|
|
} else if(!('media' in document.createElement('source'))){
|
|
webshims.reflectProperties('source', ['media']);
|
|
}
|
|
|
|
if(hasNative && hasFlash && !options.preferFlash){
|
|
var switchErrors = {
|
|
3: 1,
|
|
4: 1
|
|
};
|
|
var switchOptions = function(e){
|
|
var media, error, parent;
|
|
if(
|
|
($(e.target).is('audio, video') || ((parent = e.target.parentNode) && $('source', parent).last()[0] == e.target)) &&
|
|
(media = $(e.target).closest('audio, video')) && !media.hasClass('nonnative-api-active')
|
|
){
|
|
error = media.prop('error');
|
|
setTimeout(function(){
|
|
if(!media.hasClass('nonnative-api-active')){
|
|
if(error && switchErrors[error.code]){
|
|
options.preferFlash = true;
|
|
document.removeEventListener('error', switchOptions, true);
|
|
$('audio, video').each(function(){
|
|
webshims.mediaelement.selectSource(this);
|
|
});
|
|
webshims.error("switching mediaelements option to 'preferFlash', due to an error with native player: "+e.target.currentSrc+" Mediaerror: "+ media.prop('error')+ ' error.code: '+ error.code);
|
|
}
|
|
webshims.warn('There was a mediaelement error. Run the following line in your console to get more info: webshim.mediaelement.loadDebugger();')
|
|
}
|
|
});
|
|
|
|
|
|
}
|
|
};
|
|
|
|
document.addEventListener('error', switchOptions, true);
|
|
setTimeout(function(){
|
|
$('audio, video').each(function(){
|
|
var error = $.prop(this, 'error');
|
|
if(error && switchErrors[error]){
|
|
switchOptions({target: this});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
});
|
|
;webshims.register('track', function($, webshims, window, document, undefined){
|
|
"use strict";
|
|
var mediaelement = webshims.mediaelement;
|
|
var id = new Date().getTime();
|
|
//descriptions are not really shown, but they are inserted into the dom
|
|
var showTracks = {subtitles: 1, captions: 1, descriptions: 1};
|
|
var dummyTrack = $('<track />');
|
|
var support = webshims.support;
|
|
var supportTrackMod = support.ES5 && support.objectAccessor;
|
|
var createEventTarget = function(obj){
|
|
var eventList = {};
|
|
obj.addEventListener = function(name, fn){
|
|
if(eventList[name]){
|
|
webshims.error('always use $.on to the shimed event: '+ name +' already bound fn was: '+ eventList[name] +' your fn was: '+ fn);
|
|
}
|
|
eventList[name] = fn;
|
|
|
|
};
|
|
obj.removeEventListener = function(name, fn){
|
|
if(eventList[name] && eventList[name] != fn){
|
|
webshims.error('always use $.on/$.off to the shimed event: '+ name +' already bound fn was: '+ eventList[name] +' your fn was: '+ fn);
|
|
}
|
|
if(eventList[name]){
|
|
delete eventList[name];
|
|
}
|
|
};
|
|
return obj;
|
|
};
|
|
|
|
var cueListProto = {
|
|
getCueById: function(id){
|
|
var cue = null;
|
|
for(var i = 0, len = this.length; i < len; i++){
|
|
if(this[i].id === id){
|
|
cue = this[i];
|
|
break;
|
|
}
|
|
}
|
|
return cue;
|
|
}
|
|
};
|
|
var numericModes = {
|
|
0: 'disabled',
|
|
1: 'hidden',
|
|
2: 'showing'
|
|
};
|
|
|
|
var textTrackProto = {
|
|
shimActiveCues: null,
|
|
_shimActiveCues: null,
|
|
activeCues: null,
|
|
cues: null,
|
|
kind: 'subtitles',
|
|
label: '',
|
|
language: '',
|
|
id: '',
|
|
mode: 'disabled',
|
|
oncuechange: null,
|
|
toString: function() {
|
|
return "[object TextTrack]";
|
|
},
|
|
addCue: function(cue){
|
|
if(!this.cues){
|
|
this.cues = mediaelement.createCueList();
|
|
} else {
|
|
var lastCue = this.cues[this.cues.length-1];
|
|
if(lastCue && lastCue.startTime > cue.startTime){
|
|
webshims.error("cue startTime higher than previous cue's startTime");
|
|
}
|
|
}
|
|
if(cue.track && cue.track.removeCue){
|
|
cue.track.removeCue(cue);
|
|
}
|
|
cue.track = this;
|
|
this.cues.push(cue);
|
|
},
|
|
//ToDo: make it more dynamic
|
|
removeCue: function(cue){
|
|
var cues = this.cues || [];
|
|
var i = 0;
|
|
var len = cues.length;
|
|
if(cue.track != this){
|
|
webshims.error("cue not part of track");
|
|
return;
|
|
}
|
|
for(; i < len; i++){
|
|
if(cues[i] === cue){
|
|
cues.splice(i, 1);
|
|
cue.track = null;
|
|
break;
|
|
}
|
|
}
|
|
if(cue.track){
|
|
webshims.error("cue not part of track");
|
|
return;
|
|
}
|
|
}/*,
|
|
DISABLED: 'disabled',
|
|
OFF: 'disabled',
|
|
HIDDEN: 'hidden',
|
|
SHOWING: 'showing',
|
|
ERROR: 3,
|
|
LOADED: 2,
|
|
LOADING: 1,
|
|
NONE: 0*/
|
|
};
|
|
var copyProps = ['kind', 'label', 'srclang'];
|
|
var copyName = {srclang: 'language'};
|
|
|
|
var updateMediaTrackList = function(baseData, trackList){
|
|
var removed = [];
|
|
var added = [];
|
|
var newTracks = [];
|
|
var i, len;
|
|
if(!baseData){
|
|
baseData = webshims.data(this, 'mediaelementBase') || webshims.data(this, 'mediaelementBase', {});
|
|
}
|
|
|
|
if(!trackList){
|
|
baseData.blockTrackListUpdate = true;
|
|
trackList = $.prop(this, 'textTracks');
|
|
baseData.blockTrackListUpdate = false;
|
|
}
|
|
|
|
clearTimeout(baseData.updateTrackListTimer);
|
|
|
|
$('track', this).each(function(){
|
|
var track = $.prop(this, 'track');
|
|
newTracks.push(track);
|
|
if(trackList.indexOf(track) == -1){
|
|
added.push(track);
|
|
}
|
|
});
|
|
|
|
if(baseData.scriptedTextTracks){
|
|
for(i = 0, len = baseData.scriptedTextTracks.length; i < len; i++){
|
|
newTracks.push(baseData.scriptedTextTracks[i]);
|
|
if(trackList.indexOf(baseData.scriptedTextTracks[i]) == -1){
|
|
added.push(baseData.scriptedTextTracks[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(i = 0, len = trackList.length; i < len; i++){
|
|
if(newTracks.indexOf(trackList[i]) == -1){
|
|
removed.push(trackList[i]);
|
|
}
|
|
}
|
|
|
|
if(removed.length || added.length){
|
|
trackList.splice(0);
|
|
|
|
for(i = 0, len = newTracks.length; i < len; i++){
|
|
trackList.push(newTracks[i]);
|
|
}
|
|
for(i = 0, len = removed.length; i < len; i++){
|
|
$([trackList]).triggerHandler($.Event({type: 'removetrack', track: removed[i]}));
|
|
}
|
|
for(i = 0, len = added.length; i < len; i++){
|
|
$([trackList]).triggerHandler($.Event({type: 'addtrack', track: added[i]}));
|
|
}
|
|
if(baseData.scriptedTextTracks || removed.length){
|
|
$(this).triggerHandler('updatetrackdisplay');
|
|
}
|
|
}
|
|
};
|
|
|
|
var refreshTrack = function(track, trackData){
|
|
if(!trackData){
|
|
trackData = webshims.data(track, 'trackData');
|
|
}
|
|
|
|
if(trackData && !trackData.isTriggering){
|
|
trackData.isTriggering = true;
|
|
setTimeout(function(){
|
|
$(track).closest('audio, video').triggerHandler('updatetrackdisplay');
|
|
trackData.isTriggering = false;
|
|
}, 1);
|
|
}
|
|
};
|
|
var isDefaultTrack = (function(){
|
|
var defaultKinds = {
|
|
subtitles: {
|
|
subtitles: 1,
|
|
captions: 1
|
|
},
|
|
descriptions: {descriptions: 1},
|
|
chapters: {chapters: 1}
|
|
};
|
|
defaultKinds.captions = defaultKinds.subtitles;
|
|
|
|
return function(track){
|
|
var kind, firstDefaultTrack;
|
|
var isDefault = $.prop(track, 'default');
|
|
if(isDefault && (kind = $.prop(track, 'kind')) != 'metadata'){
|
|
firstDefaultTrack = $(track)
|
|
.parent()
|
|
.find('track[default]')
|
|
.filter(function(){
|
|
return !!(defaultKinds[kind][$.prop(this, 'kind')]);
|
|
})[0]
|
|
;
|
|
if(firstDefaultTrack != track){
|
|
isDefault = false;
|
|
webshims.error('more than one default track of a specific kind detected. Fall back to default = false');
|
|
}
|
|
}
|
|
return isDefault;
|
|
};
|
|
})();
|
|
var emptyDiv = $('<div />')[0];
|
|
|
|
function VTTCue(startTime, endTime, text){
|
|
if(arguments.length != 3){
|
|
webshims.error("wrong arguments.length for VTTCue.constructor");
|
|
}
|
|
|
|
this.startTime = startTime;
|
|
this.endTime = endTime;
|
|
this.text = text;
|
|
this.onenter = null;
|
|
this.onexit = null;
|
|
this.pauseOnExit = false;
|
|
this.track = null;
|
|
this.id = null;
|
|
this.getCueAsHTML = (function(){
|
|
var lastText = "";
|
|
var parsedText = "";
|
|
var fragment;
|
|
|
|
return function(){
|
|
var i, len;
|
|
if(!fragment){
|
|
fragment = document.createDocumentFragment();
|
|
}
|
|
if(lastText != this.text){
|
|
lastText = this.text;
|
|
parsedText = mediaelement.parseCueTextToHTML(lastText);
|
|
emptyDiv.innerHTML = parsedText;
|
|
|
|
for(i = 0, len = emptyDiv.childNodes.length; i < len; i++){
|
|
fragment.appendChild(emptyDiv.childNodes[i].cloneNode(true));
|
|
}
|
|
}
|
|
return fragment.cloneNode(true);
|
|
};
|
|
|
|
})();
|
|
}
|
|
|
|
window.VTTCue = VTTCue;
|
|
window.TextTrackCue = function(){
|
|
webshims.error("Use VTTCue constructor instead of abstract TextTrackCue constructor.");
|
|
VTTCue.apply(this, arguments);
|
|
};
|
|
|
|
window.TextTrackCue.prototype = VTTCue.prototype;
|
|
|
|
mediaelement.createCueList = function(){
|
|
return $.extend([], cueListProto);
|
|
};
|
|
|
|
mediaelement.parseCueTextToHTML = (function(){
|
|
var tagSplits = /(<\/?[^>]+>)/ig;
|
|
var allowedTags = /^(?:c|v|ruby|rt|b|i|u)/;
|
|
var regEnd = /\<\s*\//;
|
|
var addToTemplate = function(localName, attribute, tag, html){
|
|
var ret;
|
|
if(regEnd.test(html)){
|
|
ret = '</'+ localName +'>';
|
|
} else {
|
|
tag.splice(0, 1);
|
|
ret = '<'+ localName +' '+ attribute +'="'+ (tag.join(' ').replace(/\"/g, '"')) +'">';
|
|
}
|
|
return ret;
|
|
};
|
|
var replacer = function(html){
|
|
var tag = html.replace(/[<\/>]+/ig,"").split(/[\s\.]+/);
|
|
if(tag[0]){
|
|
tag[0] = tag[0].toLowerCase();
|
|
if(allowedTags.test(tag[0])){
|
|
if(tag[0] == 'c'){
|
|
html = addToTemplate('span', 'class', tag, html);
|
|
} else if(tag[0] == 'v'){
|
|
html = addToTemplate('q', 'title', tag, html);
|
|
}
|
|
} else {
|
|
html = "";
|
|
}
|
|
}
|
|
return html;
|
|
};
|
|
|
|
return function(cueText){
|
|
return cueText.replace(tagSplits, replacer);
|
|
};
|
|
})();
|
|
var mapTtmlToVtt = function(i){
|
|
var content = i+'';
|
|
var begin = this.getAttribute('begin') || '';
|
|
var end = this.getAttribute('end') || '';
|
|
var text = $.trim($.text(this));
|
|
if(!/\./.test(begin)){
|
|
begin += '.000';
|
|
}
|
|
if(!/\./.test(end)){
|
|
end += '.000';
|
|
}
|
|
content += '\n';
|
|
content += begin +' --> '+end+'\n';
|
|
content += text;
|
|
return content;
|
|
};
|
|
var ttmlTextToVTT = function(ttml){
|
|
ttml = $.parseXML(ttml) || [];
|
|
return $(ttml).find('[begin][end]').map(mapTtmlToVtt).get().join('\n\n') || '';
|
|
};
|
|
var loadingTracks = 0;
|
|
|
|
mediaelement.loadTextTrack = function(mediaelem, track, trackData, _default){
|
|
var loadEvents = 'play playing loadedmetadata loadstart';
|
|
var obj = trackData.track;
|
|
var load = function(){
|
|
var error, ajax, createAjax;
|
|
var isDisabled = obj.mode == 'disabled';
|
|
var videoState = !!($.prop(mediaelem, 'readyState') > 0 || $.prop(mediaelem, 'networkState') == 2 || !$.prop(mediaelem, 'paused'));
|
|
var src = (!isDisabled || videoState) && ($.attr(track, 'src') && $.prop(track, 'src'));
|
|
|
|
if(src){
|
|
$(mediaelem).off(loadEvents, load).off('updatetrackdisplay', load);
|
|
|
|
if(!trackData.readyState){
|
|
error = function(){
|
|
loadingTracks--;
|
|
trackData.readyState = 3;
|
|
obj.cues = null;
|
|
obj.activeCues = obj.shimActiveCues = obj._shimActiveCues = null;
|
|
$(track).triggerHandler('error');
|
|
};
|
|
trackData.readyState = 1;
|
|
try {
|
|
obj.cues = mediaelement.createCueList();
|
|
obj.activeCues = obj.shimActiveCues = obj._shimActiveCues = mediaelement.createCueList();
|
|
loadingTracks++;
|
|
createAjax = function(){
|
|
ajax = $.ajax({
|
|
dataType: 'text',
|
|
url: src,
|
|
success: function(text){
|
|
loadingTracks--;
|
|
var contentType = ajax.getResponseHeader('content-type') || '';
|
|
|
|
if(!contentType.indexOf('application/xml')){
|
|
text = ttmlTextToVTT(text);
|
|
} else if(contentType.indexOf('text/vtt')){
|
|
webshims.error('set the mime-type of your WebVTT files to text/vtt. see: http://dev.w3.org/html5/webvtt/#text/vtt');
|
|
}
|
|
mediaelement.parseCaptions(text, obj, function(cues){
|
|
if(cues && 'length' in cues){
|
|
trackData.readyState = 2;
|
|
$(track).triggerHandler('load');
|
|
$(mediaelem).triggerHandler('updatetrackdisplay');
|
|
} else {
|
|
error();
|
|
}
|
|
});
|
|
},
|
|
error: error
|
|
});
|
|
};
|
|
if($.ajax && $.ajaxSettings.xhr){
|
|
if(isDisabled){
|
|
setTimeout(createAjax, loadingTracks * 2);
|
|
} else {
|
|
createAjax();
|
|
}
|
|
} else {
|
|
webshims.ready('jajax', createAjax);
|
|
webshims.loader.loadList(['jajax']);
|
|
}
|
|
} catch(er){
|
|
error();
|
|
webshims.error(er);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
trackData.readyState = 0;
|
|
obj.shimActiveCues = null;
|
|
obj._shimActiveCues = null;
|
|
obj.activeCues = null;
|
|
obj.cues = null;
|
|
|
|
$(mediaelem).on(loadEvents, load);
|
|
|
|
if(_default){
|
|
obj.mode = showTracks[obj.kind] ? 'showing' : 'hidden';
|
|
load();
|
|
} else {
|
|
$(mediaelem).on('updatetrackdisplay', load);
|
|
}
|
|
};
|
|
|
|
mediaelement.createTextTrack = function(mediaelem, track){
|
|
var obj, trackData;
|
|
if(track.nodeName){
|
|
trackData = webshims.data(track, 'trackData');
|
|
|
|
if(trackData){
|
|
refreshTrack(track, trackData);
|
|
obj = trackData.track;
|
|
}
|
|
}
|
|
|
|
if(!obj){
|
|
obj = createEventTarget(webshims.objectCreate(textTrackProto));
|
|
|
|
if(!supportTrackMod){
|
|
copyProps.forEach(function(copyProp){
|
|
var prop = $.prop(track, copyProp);
|
|
if(prop){
|
|
obj[copyName[copyProp] || copyProp] = prop;
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
if(track.nodeName){
|
|
if(supportTrackMod){
|
|
copyProps.forEach(function(copyProp){
|
|
webshims.defineProperty(obj, copyName[copyProp] || copyProp, {
|
|
get: function(){
|
|
return $.prop(track, copyProp);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
obj.id = $(track).prop('id');
|
|
trackData = webshims.data(track, 'trackData', {track: obj});
|
|
mediaelement.loadTextTrack(mediaelem, track, trackData, isDefaultTrack(track));
|
|
} else {
|
|
if(supportTrackMod){
|
|
copyProps.forEach(function(copyProp){
|
|
webshims.defineProperty(obj, copyName[copyProp] || copyProp, {
|
|
value: track[copyProp],
|
|
writeable: false
|
|
});
|
|
});
|
|
}
|
|
obj.cues = mediaelement.createCueList();
|
|
obj.activeCues = obj._shimActiveCues = obj.shimActiveCues = mediaelement.createCueList();
|
|
obj.mode = 'hidden';
|
|
obj.readyState = 2;
|
|
}
|
|
if(obj.kind == 'subtitles' && !obj.language){
|
|
webshims.error('you must provide a language for track in subtitles state');
|
|
}
|
|
obj.__wsmode = obj.mode;
|
|
|
|
webshims.defineProperty(obj, '_wsUpdateMode', {
|
|
value: function(){
|
|
$(mediaelem).triggerHandler('updatetrackdisplay');
|
|
},
|
|
enumerable: false
|
|
});
|
|
}
|
|
|
|
return obj;
|
|
};
|
|
|
|
if(!$.propHooks.mode){
|
|
$.propHooks.mode = {
|
|
set: function(obj, value){
|
|
obj.mode = value;
|
|
if(obj._wsUpdateMode && obj._wsUpdateMode.call){
|
|
obj._wsUpdateMode();
|
|
}
|
|
return obj.mode;
|
|
}
|
|
};
|
|
}
|
|
|
|
/*
|
|
taken from:
|
|
Captionator 0.5.1 [CaptionCrunch]
|
|
Christopher Giffard, 2011
|
|
Share and enjoy
|
|
|
|
https://github.com/cgiffard/Captionator
|
|
|
|
modified for webshims
|
|
*/
|
|
mediaelement.parseCaptionChunk = (function(){
|
|
// Set up timestamp parsers
|
|
var WebVTTTimestampParser = /^(\d{2})?:?(\d{2}):(\d{2})\.(\d+)\s+\-\-\>\s+(\d{2})?:?(\d{2}):(\d{2})\.(\d+)\s*(.*)/;
|
|
var WebVTTDEFAULTSCueParser = /^(DEFAULTS|DEFAULT)\s+\-\-\>\s+(.*)/g;
|
|
var WebVTTSTYLECueParser = /^(STYLE|STYLES)\s+\-\-\>\s*\n([\s\S]*)/g;
|
|
var WebVTTCOMMENTCueParser = /^(COMMENT|COMMENTS)\s+\-\-\>\s+(.*)/g;
|
|
var SRTTimestampParser = /^(\d{2})?:?(\d{2}):(\d{2})[\.\,](\d+)\s+\-\-\>\s+(\d{2})?:?(\d{2}):(\d{2})[\.\,](\d+)\s*(.*)/;
|
|
|
|
return function(subtitleElement,objectCount){
|
|
|
|
var subtitleParts, timeIn, timeOut, html, timeData, subtitlePartIndex, id;
|
|
var timestampMatch, tmpCue;
|
|
|
|
// WebVTT Special Cue Logic
|
|
if (WebVTTDEFAULTSCueParser.exec(subtitleElement) || WebVTTCOMMENTCueParser.exec(subtitleElement) || WebVTTSTYLECueParser.exec(subtitleElement)) {
|
|
return null;
|
|
}
|
|
|
|
subtitleParts = subtitleElement.split(/\n/g);
|
|
|
|
// Trim off any blank lines (logically, should only be max. one, but loop to be sure)
|
|
while (!subtitleParts[0].replace(/\s+/ig,"").length && subtitleParts.length > 0) {
|
|
subtitleParts.shift();
|
|
}
|
|
|
|
if (subtitleParts[0].match(/^\s*[a-z0-9-\_]+\s*$/ig)) {
|
|
// The identifier becomes the cue ID (when *we* load the cues from file. Programatically created cues can have an ID of whatever.)
|
|
id = String(subtitleParts.shift().replace(/\s*/ig,""));
|
|
}
|
|
|
|
for (subtitlePartIndex = 0; subtitlePartIndex < subtitleParts.length; subtitlePartIndex ++) {
|
|
var timestamp = subtitleParts[subtitlePartIndex];
|
|
|
|
if ((timestampMatch = WebVTTTimestampParser.exec(timestamp)) || (timestampMatch = SRTTimestampParser.exec(timestamp))) {
|
|
|
|
// WebVTT
|
|
|
|
timeData = timestampMatch.slice(1);
|
|
|
|
timeIn = parseInt((timeData[0]||0) * 60 * 60,10) + // Hours
|
|
parseInt((timeData[1]||0) * 60,10) + // Minutes
|
|
parseInt((timeData[2]||0),10) + // Seconds
|
|
parseFloat("0." + (timeData[3]||0)); // MS
|
|
|
|
timeOut = parseInt((timeData[4]||0) * 60 * 60,10) + // Hours
|
|
parseInt((timeData[5]||0) * 60,10) + // Minutes
|
|
parseInt((timeData[6]||0),10) + // Seconds
|
|
parseFloat("0." + (timeData[7]||0)); // MS
|
|
/*
|
|
if (timeData[8]) {
|
|
cueSettings = timeData[8];
|
|
}
|
|
*/
|
|
}
|
|
|
|
// We've got the timestamp - return all the other unmatched lines as the raw subtitle data
|
|
subtitleParts = subtitleParts.slice(0,subtitlePartIndex).concat(subtitleParts.slice(subtitlePartIndex+1));
|
|
break;
|
|
}
|
|
|
|
if (!timeIn && !timeOut) {
|
|
// We didn't extract any time information. Assume the cue is invalid!
|
|
webshims.warn("couldn't extract time information: "+[timeIn, timeOut, subtitleParts.join("\n"), id].join(' ; '));
|
|
return null;
|
|
}
|
|
/*
|
|
// Consolidate cue settings, convert defaults to object
|
|
var compositeCueSettings =
|
|
cueDefaults
|
|
.reduce(function(previous,current,index,array){
|
|
previous[current.split(":")[0]] = current.split(":")[1];
|
|
return previous;
|
|
},{});
|
|
|
|
// Loop through cue settings, replace defaults with cue specific settings if they exist
|
|
compositeCueSettings =
|
|
cueSettings
|
|
.split(/\s+/g)
|
|
.filter(function(set) { return set && !!set.length; })
|
|
// Convert array to a key/val object
|
|
.reduce(function(previous,current,index,array){
|
|
previous[current.split(":")[0]] = current.split(":")[1];
|
|
return previous;
|
|
},compositeCueSettings);
|
|
|
|
// Turn back into string like the VTTCue constructor expects
|
|
cueSettings = "";
|
|
for (var key in compositeCueSettings) {
|
|
if (compositeCueSettings.hasOwnProperty(key)) {
|
|
cueSettings += !!cueSettings.length ? " " : "";
|
|
cueSettings += key + ":" + compositeCueSettings[key];
|
|
}
|
|
}
|
|
*/
|
|
// The remaining lines are the subtitle payload itself (after removing an ID if present, and the time);
|
|
html = subtitleParts.join("\n");
|
|
tmpCue = new VTTCue(timeIn, timeOut, html);
|
|
if(id){
|
|
tmpCue.id = id;
|
|
}
|
|
return tmpCue;
|
|
};
|
|
})();
|
|
|
|
mediaelement.parseCaptions = function(captionData, track, complete) {
|
|
|
|
var cue, lazyProcess, regWevVTT, startDate, isWEBVTT;
|
|
|
|
mediaelement.createCueList();
|
|
if (captionData) {
|
|
|
|
regWevVTT = /^WEBVTT(\s*FILE)?/ig;
|
|
|
|
lazyProcess = function(i, len){
|
|
|
|
for(; i < len; i++){
|
|
cue = captionData[i];
|
|
if(regWevVTT.test(cue)){
|
|
isWEBVTT = true;
|
|
} else if(cue.replace(/\s*/ig,"").length){
|
|
cue = mediaelement.parseCaptionChunk(cue, i);
|
|
if(cue){
|
|
track.addCue(cue);
|
|
}
|
|
}
|
|
if(startDate < (new Date().getTime()) - 30){
|
|
i++;
|
|
setTimeout(function(){
|
|
startDate = new Date().getTime();
|
|
lazyProcess(i, len);
|
|
}, 90);
|
|
|
|
break;
|
|
}
|
|
}
|
|
if(i >= len){
|
|
if(!isWEBVTT){
|
|
webshims.error('please use WebVTT format. This is the standard');
|
|
}
|
|
complete(track.cues);
|
|
}
|
|
};
|
|
|
|
captionData = captionData.replace(/\r\n/g,"\n");
|
|
|
|
setTimeout(function(){
|
|
captionData = captionData.replace(/\r/g,"\n");
|
|
setTimeout(function(){
|
|
startDate = new Date().getTime();
|
|
captionData = captionData.split(/\n\n+/g);
|
|
lazyProcess(0, captionData.length);
|
|
}, 9);
|
|
}, 9);
|
|
|
|
} else {
|
|
webshims.error("Required parameter captionData not supplied.");
|
|
}
|
|
};
|
|
|
|
|
|
mediaelement.createTrackList = function(mediaelem, baseData){
|
|
baseData = baseData || webshims.data(mediaelem, 'mediaelementBase') || webshims.data(mediaelem, 'mediaelementBase', {});
|
|
if(!baseData.textTracks){
|
|
baseData.textTracks = [];
|
|
webshims.defineProperties(baseData.textTracks, {
|
|
onaddtrack: {value: null},
|
|
onremovetrack: {value: null},
|
|
onchange: {value: null},
|
|
getTrackById: {
|
|
value: function(id){
|
|
var track = null;
|
|
for(var i = 0; i < baseData.textTracks.length; i++){
|
|
if(id == baseData.textTracks[i].id){
|
|
track = baseData.textTracks[i];
|
|
break;
|
|
}
|
|
}
|
|
return track;
|
|
}
|
|
}
|
|
});
|
|
createEventTarget(baseData.textTracks);
|
|
$(mediaelem).on('updatetrackdisplay', function(){
|
|
var track;
|
|
for(var i = 0; i < baseData.textTracks.length; i++){
|
|
track = baseData.textTracks[i];
|
|
if(track.__wsmode != track.mode){
|
|
track.__wsmode = track.mode;
|
|
$([ baseData.textTracks ]).triggerHandler('change');
|
|
}
|
|
}
|
|
});
|
|
|
|
}
|
|
return baseData.textTracks;
|
|
};
|
|
|
|
if(!support.track){
|
|
webshims.defineNodeNamesBooleanProperty(['track'], 'default');
|
|
webshims.reflectProperties(['track'], ['srclang', 'label']);
|
|
|
|
webshims.defineNodeNameProperties('track', {
|
|
src: {
|
|
//attr: {},
|
|
reflect: true,
|
|
propType: 'src'
|
|
}
|
|
});
|
|
}
|
|
|
|
webshims.defineNodeNameProperties('track', {
|
|
kind: {
|
|
attr: support.track ? {
|
|
set: function(value){
|
|
var trackData = webshims.data(this, 'trackData');
|
|
this.setAttribute('data-kind', value);
|
|
if(trackData){
|
|
trackData.attrKind = value;
|
|
}
|
|
},
|
|
get: function(){
|
|
var trackData = webshims.data(this, 'trackData');
|
|
if(trackData && ('attrKind' in trackData)){
|
|
return trackData.attrKind;
|
|
}
|
|
return this.getAttribute('kind');
|
|
}
|
|
} : {},
|
|
reflect: true,
|
|
propType: 'enumarated',
|
|
defaultValue: 'subtitles',
|
|
limitedTo: ['subtitles', 'captions', 'descriptions', 'chapters', 'metadata']
|
|
}
|
|
});
|
|
|
|
$.each(copyProps, function(i, copyProp){
|
|
var name = copyName[copyProp] || copyProp;
|
|
webshims.onNodeNamesPropertyModify('track', copyProp, function(){
|
|
var trackData = webshims.data(this, 'trackData');
|
|
|
|
if(trackData){
|
|
if(copyProp == 'kind'){
|
|
refreshTrack(this, trackData);
|
|
}
|
|
if(!supportTrackMod){
|
|
trackData.track[name] = $.prop(this, copyProp);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
webshims.onNodeNamesPropertyModify('track', 'src', function(val){
|
|
if(val){
|
|
var data = webshims.data(this, 'trackData');
|
|
var media;
|
|
if(data){
|
|
media = $(this).closest('video, audio');
|
|
if(media[0]){
|
|
mediaelement.loadTextTrack(media, this, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
//
|
|
webshims.defineNodeNamesProperties(['track'], {
|
|
ERROR: {
|
|
value: 3
|
|
},
|
|
LOADED: {
|
|
value: 2
|
|
},
|
|
LOADING: {
|
|
value: 1
|
|
},
|
|
NONE: {
|
|
value: 0
|
|
},
|
|
readyState: {
|
|
get: function(){
|
|
return (webshims.data(this, 'trackData') || {readyState: 0}).readyState;
|
|
},
|
|
writeable: false
|
|
},
|
|
track: {
|
|
get: function(){
|
|
return mediaelement.createTextTrack($(this).closest('audio, video')[0], this);
|
|
},
|
|
writeable: false
|
|
}
|
|
}, 'prop');
|
|
|
|
webshims.defineNodeNamesProperties(['audio', 'video'], {
|
|
textTracks: {
|
|
get: function(){
|
|
var media = this;
|
|
var baseData = webshims.data(media, 'mediaelementBase') || webshims.data(media, 'mediaelementBase', {});
|
|
var tracks = mediaelement.createTrackList(media, baseData);
|
|
if(!baseData.blockTrackListUpdate){
|
|
updateMediaTrackList.call(media, baseData, tracks);
|
|
}
|
|
return tracks;
|
|
},
|
|
writeable: false
|
|
},
|
|
addTextTrack: {
|
|
value: function(kind, label, lang){
|
|
var textTrack = mediaelement.createTextTrack(this, {
|
|
kind: dummyTrack.prop('kind', kind || '').prop('kind'),
|
|
label: label || '',
|
|
srclang: lang || ''
|
|
});
|
|
var baseData = webshims.data(this, 'mediaelementBase') || webshims.data(this, 'mediaelementBase', {});
|
|
if (!baseData.scriptedTextTracks) {
|
|
baseData.scriptedTextTracks = [];
|
|
}
|
|
baseData.scriptedTextTracks.push(textTrack);
|
|
updateMediaTrackList.call(this);
|
|
return textTrack;
|
|
}
|
|
}
|
|
}, 'prop');
|
|
|
|
//wsmediareload
|
|
var thUpdateList = function(e){
|
|
if($(e.target).is('audio, video')){
|
|
var baseData = webshims.data(e.target, 'mediaelementBase');
|
|
if(baseData){
|
|
clearTimeout(baseData.updateTrackListTimer);
|
|
baseData.updateTrackListTimer = setTimeout(function(){
|
|
updateMediaTrackList.call(e.target, baseData);
|
|
}, 0);
|
|
}
|
|
}
|
|
};
|
|
|
|
var getNativeReadyState = function(trackElem, textTrack){
|
|
return textTrack.readyState || trackElem.readyState;
|
|
};
|
|
var stopOriginalEvent = function(e){
|
|
if(e.originalEvent){
|
|
e.stopImmediatePropagation();
|
|
}
|
|
};
|
|
var hideNativeTracks = function(){
|
|
if(webshims.implement(this, 'track')){
|
|
var kind;
|
|
var origTrack = this.track;
|
|
if(origTrack){
|
|
|
|
if (!webshims.bugs.track && (origTrack.mode || getNativeReadyState(this, origTrack))) {
|
|
$.prop(this, 'track').mode = numericModes[origTrack.mode] || origTrack.mode;
|
|
}
|
|
//disable track from showing + remove UI
|
|
kind = $.prop(this, 'kind');
|
|
origTrack.mode = (typeof origTrack.mode == 'string') ? 'disabled' : 0;
|
|
this.kind = 'metadata';
|
|
|
|
$(this).attr({kind: kind});
|
|
|
|
}
|
|
$(this).on('load error', stopOriginalEvent);
|
|
}
|
|
};
|
|
|
|
webshims.addReady(function(context, insertedElement){
|
|
var insertedMedia = insertedElement.filter('video, audio, track').closest('audio, video');
|
|
$('video, audio', context)
|
|
.add(insertedMedia)
|
|
.each(function(){
|
|
updateMediaTrackList.call(this);
|
|
})
|
|
.on('emptied updatetracklist wsmediareload', thUpdateList)
|
|
.each(function(){
|
|
if(support.track){
|
|
var shimedTextTracks = $.prop(this, 'textTracks');
|
|
var origTextTracks = this.textTracks;
|
|
|
|
if(shimedTextTracks.length != origTextTracks.length){
|
|
webshims.warn("textTracks couldn't be copied");
|
|
}
|
|
|
|
$('track', this).each(hideNativeTracks);
|
|
}
|
|
})
|
|
;
|
|
|
|
insertedMedia.each(function(){
|
|
var media = this;
|
|
var baseData = webshims.data(media, 'mediaelementBase');
|
|
if(baseData){
|
|
clearTimeout(baseData.updateTrackListTimer);
|
|
baseData.updateTrackListTimer = setTimeout(function(){
|
|
updateMediaTrackList.call(media, baseData);
|
|
}, 9);
|
|
}
|
|
});
|
|
});
|
|
|
|
if(support.texttrackapi){
|
|
$('video, audio').trigger('trackapichange');
|
|
}
|
|
});
|