vendor/assets/javascripts/webshims/shims/combos/22.js in webshims-rails-0.4.7 vs vendor/assets/javascripts/webshims/shims/combos/22.js in webshims-rails-1.10.3

- old
+ new

@@ -1,2378 +1,967 @@ -// vim: ts=4 sts=4 sw=4 expandtab -// -- kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License -// -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project) -// -- dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA -// -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License -// -- Gozala Irakli Gozalishvili Copyright (C) 2010 MIT License -// -- kitcambridge Kit Cambridge Copyright (C) 2011 MIT License -// -- kossnocorp Sasha Koss XXX TODO License or CLA -// -- bryanforbes Bryan Forbes XXX TODO License or CLA -// -- killdream Quildreen Motta Copyright (C) 2011 MIT Licence -// -- michaelficarra Michael Ficarra Copyright (C) 2011 3-clause BSD License -// -- sharkbrainguy Gerard Paapu Copyright (C) 2011 MIT License -// -- bbqsrc Brendan Molloy XXX TODO License or CLA -// -- iwyg XXX TODO License or CLA -// -- DomenicDenicola Domenic Denicola XXX TODO License or CLA -// -- xavierm02 Montillet Xavier XXX TODO License or CLA -// -- Raynos Raynos XXX TODO License or CLA -// -- samsonjs Sami Samhuri XXX TODO License or CLA -// -- rwldrn Rick Waldron Copyright (C) 2011 MIT License -// -- lexer Alexey Zakharov XXX TODO License or CLA - -/*! - Copyright (c) 2009, 280 North Inc. http://280north.com/ - MIT License. http://github.com/280north/narwhal/blob/master/README.md -*/ - -(function (undefined) { - -/** - * Brings an environment as close to ECMAScript 5 compliance - * as is possible with the facilities of erstwhile engines. - * - * ES5 Draft - * http://www.ecma-international.org/publications/files/drafts/tc39-2009-050.pdf - * - * NOTE: this is a draft, and as such, the URL is subject to change. If the - * link is broken, check in the parent directory for the latest TC39 PDF. - * http://www.ecma-international.org/publications/files/drafts/ - * - * Previous ES5 Draft - * http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf - * This is a broken link to the previous draft of ES5 on which most of the - * numbered specification references and quotes herein were taken. Updating - * these references and quotes to reflect the new document would be a welcome - * volunteer project. - * - * @module - */ - -/*whatsupdoc*/ - -// Shortcut to an often accessed properties, in order to avoid multiple -// dereference that costs universally. -// _Please note: Shortcuts are defined after `Function.prototype.bind` as we -// us it in defining shortcuts. -var call = Function.prototype.call; -var prototypeOfArray = Array.prototype; -var prototypeOfObject = Object.prototype; -var slice = prototypeOfArray.slice; -var owns; -var toString; - -// ES-5 15.3.4.5 -// http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf - -if (!Function.prototype.bind) { - Function.prototype.bind = function (that) { // .length is 1 - // 1. Let Target be the this value. - var target = this; - // 2. If IsCallable(Target) is false, throw a TypeError exception. - if (typeof target != "function") - throw new TypeError(); // TODO message - // 3. Let A be a new (possibly empty) internal list of all of the - // argument values provided after thisArg (arg1, arg2 etc), in order. - // XXX slicedArgs will stand in for "A" if used - var args = slice.call(arguments, 1); // for normal call - // 4. Let F be a new native ECMAScript object. - // 9. Set the [[Prototype]] internal property of F to the standard - // built-in Function prototype object as specified in 15.3.3.1. - // 10. Set the [[Call]] internal property of F as described in - // 15.3.4.5.1. - // 11. Set the [[Construct]] internal property of F as described in - // 15.3.4.5.2. - // 12. Set the [[HasInstance]] internal property of F as described in - // 15.3.4.5.3. - // 13. The [[Scope]] internal property of F is unused and need not - // exist. - var bound = function () { - - if (this instanceof bound) { - // 15.3.4.5.2 [[Construct]] - // When the [[Construct]] internal method of a function object, - // F that was created using the bind function is called with a - // list of arguments ExtraArgs the following steps are taken: - // 1. Let target be the value of F's [[TargetFunction]] - // internal property. - // 2. If target has no [[Construct]] internal method, a - // TypeError exception is thrown. - // 3. Let boundArgs be the value of F's [[BoundArgs]] internal - // property. - // 4. Let args be a new list containing the same values as the - // list boundArgs in the same order followed by the same - // values as the list ExtraArgs in the same order. - - var F = function(){}; - F.prototype = target.prototype; - var self = new F; - - var result = target.apply( - self, - args.concat(slice.call(arguments)) - ); - if (result !== null && Object(result) === result) - return result; - return self; - - } else { - // 15.3.4.5.1 [[Call]] - // When the [[Call]] internal method of a function object, F, - // which was created using the bind function is called with a - // this value and a list of arguments ExtraArgs the following - // steps are taken: - // 1. Let boundArgs be the value of F's [[BoundArgs]] internal - // property. - // 2. Let boundThis be the value of F's [[BoundThis]] internal - // property. - // 3. Let target be the value of F's [[TargetFunction]] internal - // property. - // 4. Let args be a new list containing the same values as the list - // boundArgs in the same order followed by the same values as - // the list ExtraArgs in the same order. 5. Return the - // result of calling the [[Call]] internal method of target - // providing boundThis as the this value and providing args - // as the arguments. - - // equiv: target.call(this, ...boundArgs, ...args) - return target.apply( - that, - args.concat(slice.call(arguments)) - ); - - } - - }; - // XXX bound.length is never writable, so don't even try - // - // 16. The length own property of F is given attributes as specified in - // 15.3.5.1. - // TODO - // 17. Set the [[Extensible]] internal property of F to true. - // TODO - // 18. Call the [[DefineOwnProperty]] internal method of F with - // arguments "caller", PropertyDescriptor {[[Value]]: null, - // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: - // false}, and false. - // TODO - // 19. Call the [[DefineOwnProperty]] internal method of F with - // arguments "arguments", PropertyDescriptor {[[Value]]: null, - // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: - // false}, and false. - // TODO - // NOTE Function objects created using Function.prototype.bind do not - // have a prototype property. - // XXX can't delete it in pure-js. - return bound; - }; -} - -toString = call.bind(prototypeOfObject.toString); -owns = call.bind(prototypeOfObject.hasOwnProperty); - - -// -// Array -// ===== -// - -// ES5 15.4.3.2 -if (!Array.isArray) { - Array.isArray = function (obj) { - return toString(obj) == "[object Array]"; - }; -} - -// The IsCallable() check in the Array functions -// has been replaced with a strict check on the -// internal class of the object to trap cases where -// the provided function was actually a regular -// expression literal, which in V8 and -// JavaScriptCore is a typeof "function". Only in -// V8 are regular expression literals permitted as -// reduce parameters, so it is desirable in the -// general case for the shim to match the more -// strict and common behavior of rejecting regular -// expressions. - -// ES5 15.4.4.18 -// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach -if (!Array.prototype.forEach) { - Array.prototype.forEach = function (fun /*, thisp*/) { - var self = toObject(this), - thisp = arguments[1], - i = 0, - length = self.length >>> 0; - - // If no callback function or if callback is not a callable function - if (toString(fun) != "[object Function]") { - throw new TypeError(); // TODO message - } - - while (i < length) { - if (i in self) { - // Invoke the callback function with call, passing arguments: - // context, property value, property key, thisArg object context - fun.call(thisp, self[i], i, self); - } - i++; - } - }; -} - -// ES5 15.4.4.19 -// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map -if (!Array.prototype.map) { - Array.prototype.map = function (fun /*, thisp*/) { - var self = toObject(this), - length = self.length >>> 0, - result = Array(length), - thisp = arguments[1]; - - // If no callback function or if callback is not a callable function - if (toString(fun) != "[object Function]") { - throw new TypeError(); // TODO message - } - - for (var i = 0; i < length; i++) { - if (i in self) - result[i] = fun.call(thisp, self[i], i, self); - } - return result; - }; -} - -// ES5 15.4.4.20 -if (!Array.prototype.filter) { - Array.prototype.filter = function (fun /*, thisp */) { - var self = toObject(this), - length = self.length >>> 0, - result = [], - thisp = arguments[1]; - - // If no callback function or if callback is not a callable function - if (toString(fun) != "[object Function]") { - throw new TypeError(); // TODO message - } - - for (var i = 0; i < length; i++) { - if (i in self && fun.call(thisp, self[i], i, self)) - result.push(self[i]); - } - return result; - }; -} - -// ES5 15.4.4.16 -if (!Array.prototype.every) { - Array.prototype.every = function (fun /*, thisp */) { - var self = toObject(this), - length = self.length >>> 0, - thisp = arguments[1]; - - // If no callback function or if callback is not a callable function - if (toString(fun) != "[object Function]") { - throw new TypeError(); // TODO message - } - - for (var i = 0; i < length; i++) { - if (i in self && !fun.call(thisp, self[i], i, self)) - return false; - } - return true; - }; -} - -// ES5 15.4.4.17 -// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some -if (!Array.prototype.some) { - Array.prototype.some = function (fun /*, thisp */) { - var self = toObject(this), - length = self.length >>> 0, - thisp = arguments[1]; - - // If no callback function or if callback is not a callable function - if (toString(fun) != "[object Function]") { - throw new TypeError(); // TODO message - } - - for (var i = 0; i < length; i++) { - if (i in self && fun.call(thisp, self[i], i, self)) - return true; - } - return false; - }; -} - -// ES5 15.4.4.21 -// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce -if (!Array.prototype.reduce) { - Array.prototype.reduce = function (fun /*, initial*/) { - var self = toObject(this), - length = self.length >>> 0; - - // If no callback function or if callback is not a callable function - if (toString(fun) != "[object Function]") { - throw new TypeError(); // TODO message - } - - // no value to return if no initial value and an empty array - if (!length && arguments.length == 1) - throw new TypeError(); // TODO message - - var i = 0; - var result; - if (arguments.length >= 2) { - result = arguments[1]; - } else { - do { - if (i in self) { - result = self[i++]; - break; - } - - // if array contains no values, no initial value to return - if (++i >= length) - throw new TypeError(); // TODO message - } while (true); - } - - for (; i < length; i++) { - if (i in self) - result = fun.call(void 0, result, self[i], i, self); - } - - return result; - }; -} - -// ES5 15.4.4.22 -// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight -if (!Array.prototype.reduceRight) { - Array.prototype.reduceRight = function (fun /*, initial*/) { - var self = toObject(this), - length = self.length >>> 0; - - // If no callback function or if callback is not a callable function - if (toString(fun) != "[object Function]") { - throw new TypeError(); // TODO message - } - - // no value to return if no initial value, empty array - if (!length && arguments.length == 1) - throw new TypeError(); // TODO message - - var result, i = length - 1; - if (arguments.length >= 2) { - result = arguments[1]; - } else { - do { - if (i in self) { - result = self[i--]; - break; - } - - // if array contains no values, no initial value to return - if (--i < 0) - throw new TypeError(); // TODO message - } while (true); - } - - do { - if (i in this) - result = fun.call(void 0, result, self[i], i, self); - } while (i--); - - return result; - }; -} - -// ES5 15.4.4.14 -// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf -if (!Array.prototype.indexOf) { - Array.prototype.indexOf = function (sought /*, fromIndex */ ) { - var self = toObject(this), - length = self.length >>> 0; - - if (!length) - return -1; - - var i = 0; - if (arguments.length > 1) - i = toInteger(arguments[1]); - - // handle negative indices - i = i >= 0 ? i : length - Math.abs(i); - for (; i < length; i++) { - if (i in self && self[i] === sought) { - return i; - } - } - return -1; - }; -} - -// ES5 15.4.4.15 -if (!Array.prototype.lastIndexOf) { - Array.prototype.lastIndexOf = function (sought /*, fromIndex */) { - var self = toObject(this), - length = self.length >>> 0; - - if (!length) - return -1; - var i = length - 1; - if (arguments.length > 1) - i = toInteger(arguments[1]); - // handle negative indices - i = i >= 0 ? i : length - Math.abs(i); - for (; i >= 0; i--) { - if (i in self && sought === self[i]) - return i; - } - return -1; - }; -} - -// -// Array -// ===== -// - -// ES5 15.4.4.12 -// http://es5.github.com/#x15.4.4.12 -// Default value for second param -// [bugfix, ielt9, old browsers] -// IE < 9 bug: [1,2].splice(0).join("") == "" but should be "12" -if([1,2].splice(0).length != 2) { - var _origArraySplice = Array.prototype.splice; - - Array.prototype.splice = function(start, deleteCount) { - if(!arguments.length)return []; - - return _origArraySplice.apply(this, [ - start === void 0 ? 0 : start, - deleteCount === void 0 ? (this.length - start) : deleteCount - ].concat(slice.call(arguments, 2))) - }; -} - -// -// Object -// ====== -// - - -// ES5 15.2.3.14 -// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation -if (!Object.keys) { - - var hasDontEnumBug = true, - dontEnums = [ - "toString", - "toLocaleString", - "valueOf", - "hasOwnProperty", - "isPrototypeOf", - "propertyIsEnumerable", - "constructor" - ], - dontEnumsLength = dontEnums.length; - - for (var key in {"toString": null}) - hasDontEnumBug = false; - - Object.keys = function keys(object) { - - if ((typeof object != "object" && typeof object != "function") || object === null) - throw new TypeError("Object.keys called on a non-object"); - - var keys = []; - for (var name in object) { - if (owns(object, name)) { - keys.push(name); - } - } - - if (hasDontEnumBug) { - for (var i = 0, ii = dontEnumsLength; i < ii; i++) { - var dontEnum = dontEnums[i]; - if (owns(object, dontEnum)) { - keys.push(dontEnum); - } - } - } - - return keys; - }; - -} - -// -// Date -// ==== -// - -// ES5 15.9.5.43 -// Format a Date object as a string according to a simplified subset of the ISO 8601 -// standard as defined in 15.9.1.15. -if (!Date.prototype.toISOString) { - Date.prototype.toISOString = function toISOString() { - var result, length, value; - if (!isFinite(this)) - throw new RangeError; - - // the date time string format is specified in 15.9.1.15. - result = [this.getUTCFullYear(), this.getUTCMonth() + 1, this.getUTCDate(), - this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()]; - - length = result.length; - while (length--) { - value = result[length]; - // pad months, days, hours, minutes, and seconds to have two digits. - if (value < 10) - result[length] = "0" + value; - } - // pad milliseconds to have three digits. - return result.slice(0, 3).join("-") + "T" + result.slice(3).join(":") + "." + - ("000" + this.getUTCMilliseconds()).slice(-3) + "Z"; - } -} - -// ES5 15.9.4.4 -if (!Date.now) { - Date.now = function now() { - return new Date().getTime(); - }; -} - -// ES5 15.9.5.44 -if (!Date.prototype.toJSON) { - Date.prototype.toJSON = function toJSON(key) { - // This function provides a String representation of a Date object for - // use by JSON.stringify (15.12.3). When the toJSON method is called - // with argument key, the following steps are taken: - - // 1. Let O be the result of calling ToObject, giving it the this - // value as its argument. - // 2. Let tv be ToPrimitive(O, hint Number). - // 3. If tv is a Number and is not finite, return null. - // XXX - // 4. Let toISO be the result of calling the [[Get]] internal method of - // O with argument "toISOString". - // 5. If IsCallable(toISO) is false, throw a TypeError exception. - if (typeof this.toISOString != "function") - throw new TypeError(); // TODO message - // 6. Return the result of calling the [[Call]] internal method of - // toISO with O as the this value and an empty argument list. - return this.toISOString(); - - // NOTE 1 The argument is ignored. - - // NOTE 2 The toJSON function is intentionally generic; it does not - // require that its this value be a Date object. Therefore, it can be - // transferred to other kinds of objects for use as a method. However, - // it does require that any such object have a toISOString method. An - // object is free to use the argument key to filter its - // stringification. - }; -} - - -// -// String -// ====== -// - -// ES5 15.5.4.20 -var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" + - "\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" + - "\u2029\uFEFF"; -if (!String.prototype.trim || ws.trim()) { - // http://blog.stevenlevithan.com/archives/faster-trim-javascript - // http://perfectionkills.com/whitespace-deviations/ - ws = "[" + ws + "]"; - var trimBeginRegexp = new RegExp("^" + ws + ws + "*"), - trimEndRegexp = new RegExp(ws + ws + "*$"); - String.prototype.trim = function trim() { - return String(this).replace(trimBeginRegexp, "").replace(trimEndRegexp, ""); - }; -} - -// ES5 15.5.4.14 -// http://es5.github.com/#x15.5.4.14 -// [bugfix, chrome] -// If separator is undefined, then the result array contains just one String, which is the this value (converted to a String). If limit is not undefined, then the output array is truncated so that it contains no more than limit elements. -// "0".split(undefined, 0) -> [] -if("0".split(void 0, 0).length) { - var oldSplit = String.prototype.split; - String.prototype.split = function(separator, limit) { - if(separator === void 0 && limit === 0)return []; - return oldSplit.apply(this, arguments); - } -} - -// ECMA-262, 3rd B.2.3 -// Note an ECMAScript standart, although ECMAScript 3rd Edition has a non-normative section suggesting uniform semantics -// and it should be normalized across all browsers -// [bugfix, IE lt 9] IE < 9 substr() with negative value not working in IE -if("".substr && "0b".substr(-1) !== "b") { - var oldSubstr = String.prototype.substr; - /** -* Get the substring of a string -* @param {integer} start where to start the substring -* @param {integer} length how many characters to return -* @return {string} -*/ - String.prototype.substr = function(start, length) { - return oldSubstr.call(this, start < 0 ? (start = this.length + start) < 0 ? 0 : start : start, length); - } -} - -// -// Util -// ====== -// - -// http://jsperf.com/to-integer -var toInteger = function (n) { - n = +n; - if (n !== n) // isNaN - n = -1; - else if (n !== 0 && n !== (1/0) && n !== -(1/0)) - n = (n > 0 || -1) * Math.floor(Math.abs(n)); - return n; -}; - -var prepareString = "a"[0] != "a", - // ES5 9.9 - toObject = function (o) { - if (o == null) { // this matches both null and undefined - throw new TypeError(); // TODO message - } - // If the implementation doesn't support by-index access of - // string characters (ex. IE < 7), split the string - if (prepareString && typeof o == "string" && o) { - return o.split(""); - } - return Object(o); - }; - -})(); - - - -(function($, shims){ - var defineProperty = 'defineProperty'; - var advancedObjectProperties = !!(Object.create && Object.defineProperties && Object.getOwnPropertyDescriptor); - //safari5 has defineProperty-interface, but it can't be used on dom-object - if (advancedObjectProperties && Object[defineProperty] && Object.prototype.__defineGetter__) { - (function(){ - try { - var foo = document.createElement('foo'); - Object[defineProperty](foo, 'bar', { - get: function(){ - return true; - } - }); - advancedObjectProperties = !!foo.bar; - } - catch (e) { - advancedObjectProperties = false; - } - foo = null; - })(); - } - - Modernizr.objectAccessor = !!((advancedObjectProperties || (Object.prototype.__defineGetter__ && Object.prototype.__lookupSetter__))); - Modernizr.advancedObjectProperties = advancedObjectProperties; - -if((!advancedObjectProperties || !Object.create || !Object.defineProperties || !Object.getOwnPropertyDescriptor || !Object.defineProperty)){ - var call = Function.prototype.call; - var prototypeOfObject = Object.prototype; - var owns = call.bind(prototypeOfObject.hasOwnProperty); - - shims.objectCreate = function(proto, props, opts, no__proto__){ - var o; - var f = function(){}; - - f.prototype = proto; - o = new f(); - - if(!no__proto__ && !('__proto__' in o) && !Modernizr.objectAccessor){ - o.__proto__ = proto; +jQuery.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; } - - if(props){ - shims.defineProperties(o, props); - } - - if(opts){ - o.options = jQuery.extend(true, {}, o.options || {}, opts); - opts = o.options; - } - - if(o._create && jQuery.isFunction(o._create)){ - o._create(opts); - } - return o; }; - shims.defineProperties = function(object, props){ - for (var name in props) { - if (owns(props, name)) { - shims.defineProperty(object, name, props[name]); + 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 + .unbind('.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')); } - return object; }; - - var descProps = ['configurable', 'enumerable', 'writable']; - shims.defineProperty = function(proto, property, descriptor){ - if(typeof descriptor != "object" || descriptor === null){return proto;} - - if(owns(descriptor, "value")){ - proto[property] = descriptor.value; - return proto; - } - - if(proto.__defineGetter__){ - if (typeof descriptor.get == "function") { - proto.__defineGetter__(property, descriptor.get); + var getSummary = function(details){ + var summary = $.data(details, 'summaryElement'); + if(!summary){ + summary = $('> summary:first-child', details); + if(!summary[0]){ + $(details).prependPolyfill('<summary class="fallback-summary">'+ options.text +'</summary>'); + summary = $.data(details, 'summaryElement'); + } else { + bindDetailsSummary(summary, details); } - if (typeof descriptor.set == "function"){ - proto.__defineSetter__(property, descriptor.set); - } - } - return proto; - }; - - shims.getPrototypeOf = function (object) { - return Object.getPrototypeOf && Object.getPrototypeOf(object) || object.__proto__ || object.constructor && object.constructor.prototype; - }; - - //based on http://www.refactory.org/s/object_getownpropertydescriptor/view/latest - shims.getOwnPropertyDescriptor = function(obj, prop){ - if (typeof obj !== "object" && typeof obj !== "function" || obj === null){ - throw new TypeError("Object.getOwnPropertyDescriptor called on a non-object"); } - var descriptor; - if(Object.defineProperty && Object.getOwnPropertyDescriptor){ - try{ - descriptor = Object.getOwnPropertyDescriptor(obj, prop); - return descriptor; - } catch(e){} - } - descriptor = { - configurable: true, - enumerable: true, - writable: true, - value: undefined - }; - var getter = obj.__lookupGetter__ && obj.__lookupGetter__(prop), - setter = obj.__lookupSetter__ && obj.__lookupSetter__(prop) - ; - - if (!getter && !setter) { // not an accessor so return prop - if(!owns(obj, prop)){ - return; - } - descriptor.value = obj[prop]; - return descriptor; - } - - // there is an accessor, remove descriptor.writable; populate descriptor.get and descriptor.set - delete descriptor.writable; - delete descriptor.value; - descriptor.get = descriptor.set = undefined; - - if(getter){ - descriptor.get = getter; - } - - if(setter){ - descriptor.set = setter; - } - - return descriptor; - }; - -} -})(jQuery, jQuery.webshims); - - - -(function($, Modernizr, webshims){ - "use strict"; - var hasNative = Modernizr.audio && Modernizr.video; - var supportsLoop = false; - var bugs = webshims.bugs; - - var loadSwf = function(){ - webshims.ready(swfType, function(){ - if(!webshims.mediaelement.createSWF){ - webshims.mediaelement.loadSwf = true; - webshims.reTest([swfType], hasNative); - } - }); + return summary; }; - var options = webshims.cfg.mediaelement; - var swfType = options && options.player == 'jwplayer' ? 'mediaelement-swf' : 'mediaelement-jaris'; - var hasSwf; - if(!options){ - webshims.error("mediaelement wasn't implemented but loaded"); - return; - } - if(hasNative){ - var videoElem = document.createElement('video'); - Modernizr.videoBuffered = ('buffered' in videoElem); - supportsLoop = ('loop' in videoElem); - - webshims.capturingEvents(['play', 'playing', 'waiting', 'paused', 'ended', 'durationchange', 'loadedmetadata', 'canplay', 'volumechange']); - - if(!Modernizr.videoBuffered){ - webshims.addPolyfill('mediaelement-native-fix', { - f: 'mediaelement', - test: Modernizr.videoBuffered, - d: ['dom-support'] - }); - - webshims.reTest('mediaelement-native-fix'); - } - } - if(hasNative && !options.preferFlash){ - var switchOptions = function(e){ - var parent = e.target.parentNode; - if(!options.preferFlash && ($(e.target).is('audio, video') || (parent && $('source:last', parent)[0] == e.target)) ){ - webshims.ready('DOM mediaelement', function(){ - if(hasSwf){ - loadSwf(); - } - webshims.ready('WINDOWLOAD '+swfType, function(){ - setTimeout(function(){ - if(hasSwf && !options.preferFlash && webshims.mediaelement.createSWF && !$(e.target).closest('audio, video').is('.nonnative-api-active')){ - options.preferFlash = true; - document.removeEventListener('error', switchOptions, true); - $('audio, video').each(function(){ - webshims.mediaelement.selectSource(this); - }); - webshims.info("switching mediaelements option to 'preferFlash', due to an error with native player: "+e.target.src); - } else if(!hasSwf){ - document.removeEventListener('error', switchOptions, true); - } - }, 20); - }); - }); - } - }; - document.addEventListener('error', switchOptions, true); - $('audio, video').each(function(){ - if(this.error){ - switchOptions({target: this}); - } - }); - } +// var isOriginalPrevented = function(e){ +// var src = e.originalEvent; +// if(!src){return e.isDefaultPrevented();} +// +// return src.defaultPrevented || src.returnValue === false || +// src.getPreventDefault && src.getPreventDefault(); +// }; - - if(Modernizr.track && !bugs.track){ - (function(){ - - if(!bugs.track){ - bugs.track = typeof $('<track />')[0].readyState != 'number'; - } - - if(!bugs.track){ - try { - new TextTrackCue(2, 3, ''); - } catch(e){ - bugs.track = true; - } - } - - var trackOptions = webshims.cfg.track; - var trackListener = function(e){ - $(e.target).filter('track').each(changeApi); - }; - var changeApi = function(){ - if(bugs.track || (!trackOptions.override && $.prop(this, 'readyState') == 3)){ - trackOptions.override = true; - webshims.reTest('track'); - document.removeEventListener('error', trackListener, true); - if(this && $.nodeName(this, 'track')){ - webshims.error("track support was overwritten. Please check your vtt including your vtt mime-type"); - } else { - webshims.info("track support was overwritten. due to bad browser support"); + 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() }; - var detectTrackError = function(){ - document.addEventListener('error', trackListener, true); - - if(bugs.track){ - changeApi(); - } else { - $('track').each(changeApi); - } + } + 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() }; - if(!trackOptions.override){ - if(webshims.isReady('track')){ - detectTrackError(); - } else { - $(detectTrackError); + 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-core', function($, webshims, window, document, undefined){ - hasSwf = swfobject.hasFlashPlayerVersion('9.0.115'); - $('html').addClass(hasSwf ? 'swf' : 'no-swf'); +jQuery.webshims.register('track', function($, webshims, window, document, undefined){ + "use strict"; var mediaelement = webshims.mediaelement; - mediaelement.parseRtmp = function(data){ - var src = data.src.split('://'); - var paths = src[1].split('/'); - var i, len, found; - data.server = src[0]+'://'+paths[0]+'/'; - data.streamId = []; - for(i = 1, len = paths.length; i < len; i++){ - if(!found && paths[i].indexOf(':') !== -1){ - paths[i] = paths[i].split(':')[1]; - found = true; - } - if(!found){ - data.server += paths[i]+'/'; - } else { - data.streamId.push(paths[i]); - } - } - if(!data.streamId.length){ - webshims.error('Could not parse rtmp url'); - } - data.streamId = data.streamId.join('/'); + var id = new Date().getTime(); + var ADDBACK = $.fn.addBack ? 'addBack' : 'andSelf'; + //descriptions are not really shown, but they are inserted into the dom + var showTracks = {subtitles: 1, captions: 1, descriptions: 1}; + var notImplemented = function(){ + webshims.error('not implemented yet'); }; - var getSrcObj = function(elem, nodeName){ - elem = $(elem); - var src = {src: elem.attr('src') || '', elem: elem, srcProp: elem.prop('src')}; - var tmp; - - if(!src.src){return src;} - - tmp = elem.attr('data-server'); - if(tmp != null){ - src.server = tmp; - } - - tmp = elem.attr('type'); - if(tmp){ - src.type = tmp; - src.container = $.trim(tmp.split(';')[0]); - } else { - if(!nodeName){ - nodeName = elem[0].nodeName.toLowerCase(); - if(nodeName == 'source'){ - nodeName = (elem.closest('video, audio')[0] || {nodeName: 'video'}).nodeName.toLowerCase(); - } + var dummyTrack = $('<track />'); + var supportTrackMod = Modernizr.ES5 && Modernizr.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); } - if(src.server){ - src.type = nodeName+'/rtmp'; - src.container = nodeName+'/rtmp'; - } else { - - tmp = mediaelement.getTypeForSrc( src.src, nodeName, src ); - - if(tmp){ - src.type = tmp; - src.container = tmp; - } + 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); } - } - tmp = elem.attr('media'); - if(tmp){ - src.media = tmp; - } - if(src.type == 'audio/rtmp' || src.type == 'video/rtmp'){ - if(src.server){ - src.streamId = src.src; - } else { - mediaelement.parseRtmp(src); + if(eventList[name]){ + delete eventList[name]; } - } - return src; - }; - - - - var hasYt = !hasSwf && ('postMessage' in window) && hasNative; - - var loadTrackUi = function(){ - if(loadTrackUi.loaded){return;} - loadTrackUi.loaded = true; - $(function(){ - webshims.loader.loadList(['track-ui']); - }); - }; - var loadYt = (function(){ - var loaded; - return function(){ - if(loaded || !hasYt){return;} - loaded = true; - webshims.loader.loadScript("https://www.youtube.com/player_api"); - $(function(){ - webshims.polyfill("mediaelement-yt"); - }); }; - })(); - var loadThird = function(){ - if(hasSwf){ - loadSwf(); - } else { - loadYt(); - } + return obj; }; - webshims.addPolyfill('mediaelement-yt', { - test: !hasYt, - d: ['dom-support'] - }); - - mediaelement.mimeTypes = { - audio: { - //ogm shouldn´t be used! - 'audio/ogg': ['ogg','oga', 'ogm'], - 'audio/ogg;codecs="opus"': 'opus', - 'audio/mpeg': ['mp2','mp3','mpga','mpega'], - 'audio/mp4': ['mp4','mpg4', 'm4r', 'm4a', 'm4p', 'm4b', 'aac'], - 'audio/wav': ['wav'], - 'audio/3gpp': ['3gp','3gpp'], - 'audio/webm': ['webm'], - 'audio/fla': ['flv', 'f4a', 'fla'], - 'application/x-mpegURL': ['m3u8', 'm3u'] - }, - video: { - //ogm shouldn´t be used! - 'video/ogg': ['ogg','ogv', 'ogm'], - 'video/mpeg': ['mpg','mpeg','mpe'], - 'video/mp4': ['mp4','mpg4', 'm4v'], - 'video/quicktime': ['mov','qt'], - 'video/x-msvideo': ['avi'], - 'video/x-ms-asf': ['asf', 'asx'], - 'video/flv': ['flv', 'f4v'], - 'video/3gpp': ['3gp','3gpp'], - 'video/webm': ['webm'], - 'application/x-mpegURL': ['m3u8', 'm3u'], - 'video/MP2T': ['ts'] + 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; } - ; - - mediaelement.mimeTypes.source = $.extend({}, mediaelement.mimeTypes.audio, mediaelement.mimeTypes.video); - - mediaelement.getTypeForSrc = function(src, nodeName, data){ - if(src.indexOf('youtube.com/watch?') != -1 || src.indexOf('youtube.com/v/') != -1){ - return 'video/youtube'; - } - if(src.indexOf('rtmp') === 0){ - return nodeName+'/rtmp'; - } - src = src.split('?')[0].split('.'); - src = src[src.length - 1]; - var mt; - - $.each(mediaelement.mimeTypes[nodeName], function(mimeType, exts){ - if(exts.indexOf(src) !== -1){ - mt = mimeType; - return false; - } - }); - return mt; }; + var numericModes = { + 0: 'disabled', + 1: 'hidden', + 2: 'showing' + }; - - mediaelement.srces = function(mediaElem, srces){ - mediaElem = $(mediaElem); - if(!srces){ - srces = []; - var nodeName = mediaElem[0].nodeName.toLowerCase(); - var src = getSrcObj(mediaElem, nodeName); - - if(!src.src){ - - $('source', mediaElem).each(function(){ - src = getSrcObj(this, nodeName); - if(src.src){srces.push(src);} - }); + var textTrackProto = { + shimActiveCues: null, + _shimActiveCues: null, + activeCues: null, + cues: null, + kind: 'subtitles', + label: '', + language: '', + mode: 'disabled', + readyState: 0, + oncuechange: null, + toString: function() { + return "[object TextTrack]"; + }, + addCue: function(cue){ + if(!this.cues){ + this.cues = mediaelement.createCueList(); } else { - srces.push(src); + var lastCue = this.cues[this.cues.length-1]; + if(lastCue && lastCue.startTime > cue.startTime){ + webshims.error("cue startTime higher than previous cue's startTime"); + } } - return srces; - } else { - mediaElem.removeAttr('src').removeAttr('type').find('source').remove(); - if(!$.isArray(srces)){ - srces = [srces]; + if(cue.track && cue.track.removeCue){ + cue.track.removeCue(cue); } - srces.forEach(function(src){ - var source = document.createElement('source'); - if(typeof src == 'string'){ - src = {src: src}; - } - source.setAttribute('src', src.src); - if(src.type){ - source.setAttribute('type', src.type); + 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(src.media){ - source.setAttribute('media', src.media); - } - mediaElem.append(source); - }); - - } + } + 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 owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty); - $.fn.loadMediaSrc = function(srces, poster){ - return this.each(function(){ - if(poster !== undefined){ - $(this).removeAttr('poster'); - if(poster){ - $.attr(this, 'poster', poster); - } + 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); } - mediaelement.srces(this, srces); - $(this).mediaLoad(); }); - }; - - mediaelement.swfMimeTypes = ['video/3gpp', 'video/x-msvideo', 'video/quicktime', 'video/x-m4v', 'video/mp4', 'video/m4p', 'video/x-flv', 'video/flv', 'audio/mpeg', 'audio/aac', 'audio/mp4', 'audio/x-m4a', 'audio/m4a', 'audio/mp3', 'audio/x-fla', 'audio/fla', 'youtube/flv', 'jwplayer/jwplayer', 'video/youtube', 'video/rtmp', 'audio/rtmp']; - - mediaelement.canThirdPlaySrces = function(mediaElem, srces){ - var ret = ''; - if(hasSwf || hasYt){ - mediaElem = $(mediaElem); - srces = srces || mediaelement.srces(mediaElem); - $.each(srces, function(i, src){ - if(src.container && src.src && ((hasSwf && mediaelement.swfMimeTypes.indexOf(src.container) != -1) || (hasYt && src.container == 'video/youtube'))){ - ret = src; - return false; + + 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]); } - }); - + } } - return ret; - }; - - var nativeCanPlayType = {}; - mediaelement.canNativePlaySrces = function(mediaElem, srces){ - var ret = ''; - if(hasNative){ - mediaElem = $(mediaElem); - var nodeName = (mediaElem[0].nodeName || '').toLowerCase(); - if(!nativeCanPlayType[nodeName]){return ret;} - srces = srces || mediaelement.srces(mediaElem); - - $.each(srces, function(i, src){ - if(src.type && nativeCanPlayType[nodeName].prop._supvalue.call(mediaElem[0], src.type) ){ - ret = src; - return false; - } - }); + for(i = 0, len = trackList.length; i < len; i++){ + if(newTracks.indexOf(trackList[i]) == -1){ + removed.push(trackList[i]); + } } - return ret; - }; - - mediaelement.setError = function(elem, message){ - if(!message){ - message = "can't play sources"; - } - $(elem).pause().data('mediaerror', message); - webshims.warn('mediaelementError: '+ message); - setTimeout(function(){ - if($(elem).data('mediaerror')){ - $(elem).trigger('mediaerror'); + if(removed.length || added.length){ + trackList.splice(0); + + for(i = 0, len = newTracks.length; i < len; i++){ + trackList.push(newTracks[i]); } - }, 1); - }; - - var handleThird = (function(){ - var requested; - return function( mediaElem, ret, data ){ - if(!requested){ - loadTrackUi(); + for(i = 0, len = removed.length; i < len; i++){ + $([trackList]).triggerHandler($.Event({type: 'removetrack', track: removed[i]})); } - webshims.ready(hasSwf ? swfType : 'mediaelement-yt', function(){ - if(mediaelement.createSWF){ - mediaelement.createSWF( mediaElem, ret, data ); - } else if(!requested) { - requested = true; - loadThird(); - //readd to ready - handleThird( mediaElem, ret, data ); - } - }); - if(!requested && hasYt && !mediaelement.createSWF){ - loadYt(); + 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 stepSources = function(elem, data, useSwf, _srces, _noLoop){ - var ret; - if(useSwf || (useSwf !== false && data && data.isActive == 'third')){ - ret = mediaelement.canThirdPlaySrces(elem, _srces); - if(!ret){ - if(_noLoop){ - mediaelement.setError(elem, false); + var refreshTrack = function(track, trackData){ + if(!trackData){ + trackData = webshims.data(track, 'trackData'); + } + if(trackData && !trackData.isTriggering){ + trackData.isTriggering = true; + setTimeout(function(){ + if(!(trackData.track || {}).readyState){ + $(track).triggerHandler('checktrackmode'); } else { - stepSources(elem, data, false, _srces, true); + $(track).closest('audio, video').triggerHandler('updatetrackdisplay'); } - } else { - handleThird(elem, ret, data); - } - } else { - ret = mediaelement.canNativePlaySrces(elem, _srces); - if(!ret){ - if(_noLoop){ - mediaelement.setError(elem, false); - if(data && data.isActive == 'third') { - mediaelement.setActive(elem, 'html5', data); - } - } else { - stepSources(elem, data, true, _srces, true); - } - } else if(data && data.isActive == 'third') { - mediaelement.setActive(elem, 'html5', data); - } + trackData.isTriggering = false; + }, 1); } }; - var stopParent = /^(?:embed|object|datalist)$/i; - var selectSource = function(elem, data){ - var baseData = webshims.data(elem, 'mediaelementBase') || webshims.data(elem, 'mediaelementBase', {}); - var _srces = mediaelement.srces(elem); - var parent = elem.parentNode; + + var emptyDiv = $('<div />')[0]; + window.TextTrackCue = function(startTime, endTime, text){ + if(arguments.length != 3){ + webshims.error("wrong arguments.length for TextTrackCue.constructor"); + } - clearTimeout(baseData.loadTimer); - $.data(elem, 'mediaerror', false); + this.startTime = startTime; + this.endTime = endTime; + this.text = text; - if(!_srces.length || !parent || parent.nodeType != 1 || stopParent.test(parent.nodeName || '')){return;} - data = data || webshims.data(elem, 'mediaelement'); - stepSources(elem, data, options.preferFlash || undefined, _srces); + this.id = ""; + this.pauseOnExit = false; + + createEventTarget(this); }; - mediaelement.selectSource = selectSource; - $(document).on('ended', function(e){ - var data = webshims.data(e.target, 'mediaelement'); - if( supportsLoop && (!data || data.isActive == 'html5') && !$.prop(e.target, 'loop')){return;} - setTimeout(function(){ - if( $.prop(e.target, 'paused') || !$.prop(e.target, 'loop') ){return;} - $(e.target).prop('currentTime', 0).play(); - }, 1); + window.TextTrackCue.prototype = { - }); - if(!supportsLoop){ - webshims.defineNodeNamesBooleanProperty(['audio', 'video'], 'loop'); - } - - ['audio', 'video'].forEach(function(nodeName){ - var supLoad = webshims.defineNodeNameProperty(nodeName, 'load', { - prop: { - value: function(){ - var data = webshims.data(this, 'mediaelement'); - selectSource(this, data); - if(hasNative && (!data || data.isActive == 'html5') && supLoad.prop._supvalue){ - supLoad.prop._supvalue.apply(this, arguments); - } - } - } - }); - nativeCanPlayType[nodeName] = webshims.defineNodeNameProperty(nodeName, 'canPlayType', { - prop: { - value: function(type){ - var ret = ''; - if(hasNative && nativeCanPlayType[nodeName].prop._supvalue){ - ret = nativeCanPlayType[nodeName].prop._supvalue.call(this, type); - if(ret == 'no'){ - ret = ''; + onenter: null, + onexit: null, + pauseOnExit: false, + getCueAsHTML: function(){ + var lastText = ""; + var parsedText = ""; + var fragment = document.createDocumentFragment(); + var fn; + if(!owns(this, 'getCueAsHTML')){ + fn = this.getCueAsHTML = function(){ + var i, len; + 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)); } } - if(!ret && hasSwf){ - type = $.trim((type || '').split(';')[0]); - if(mediaelement.swfMimeTypes.indexOf(type) != -1){ - ret = 'maybe'; - } - } - return ret; - } + return fragment.cloneNode(true); + }; + } - }); - }); - webshims.onNodeNamesPropertyModify(['audio', 'video'], ['src', 'poster'], { - set: function(){ - var elem = this; - var baseData = webshims.data(elem, 'mediaelementBase') || webshims.data(elem, 'mediaelementBase', {}); - clearTimeout(baseData.loadTimer); - baseData.loadTimer = setTimeout(function(){ - selectSource(elem); - elem = null; - }, 9); - } - }); + return fn ? fn.apply(this, arguments) : fragment.cloneNode(true); + }, + track: null, - var initMediaElements = function(){ - webshims.addReady(function(context, insertedElement){ - var media = $('video, audio', context) - .add(insertedElement.filter('video, audio')) - .each(function(){ - var data = webshims.data(this, 'mediaelement'); - - if(hasNative && $.prop(this, 'paused') && !$.prop(this, 'readyState') && $(this).is('audio[preload="none"][controls]:not([autoplay])') && (!data || data.isActive == 'html5')){ - //IE controls not visible bug - $(this).prop('preload', 'metadata').mediaLoad(); - } else { - selectSource(this, data); - } - - if(hasNative){ - - //FF progress bug - (function(){ - var bufferTimer; - var lastBuffered; - var elem = this; - var getBufferedString = function(){ - var buffered = $.prop(elem, 'buffered'); - if(!buffered){return;} - var bufferString = ""; - for(var i = 0, len = buffered.length; i < len;i++){ - bufferString += buffered.end(i); - } - return bufferString; - }; - var testBuffer = function(){ - var buffered = getBufferedString(); - if(buffered != lastBuffered){ - lastBuffered = buffered; - $(elem).triggerHandler('progress'); - } - }; - - $(this) - .on({ - 'play loadstart progress': function(e){ - if(e.type == 'progress'){ - lastBuffered = getBufferedString(); - } - clearTimeout(bufferTimer); - bufferTimer = setTimeout(testBuffer, 999); - }, - 'emptied stalled mediaerror abort suspend': function(e){ - if(e.type == 'emptied'){ - lastBuffered = false; - } - clearTimeout(bufferTimer); - } - }) - ; - })(); - } - - }) - ; - if(!loadTrackUi.loaded && $('track', media).length){ - loadTrackUi(); - } - media = null; - }); + id: '' + //todo--> +// , +// snapToLines: true, +// line: 'auto', +// size: 100, +// position: 50, +// vertical: '', +// align: 'middle' }; - if(Modernizr.track && !bugs.track){ - webshims.defineProperty(TextTrack.prototype, 'shimActiveCues', { - get: function(){ - return this._shimActiveCues || this.activeCues; - } - }); - } - //set native implementation ready, before swf api is retested - if(hasNative){ - webshims.isReady('mediaelement-core', true); - initMediaElements(); - webshims.ready('WINDOWLOAD mediaelement', loadThird); - } else { - webshims.ready(swfType, initMediaElements); - } - webshims.ready('WINDOWLOAD mediaelement', loadTrackUi); -}); -})(jQuery, Modernizr, jQuery.webshims); -/* - * todos: - * - decouple muted/volume (needs improvement) - * - implement video <-> flashcanvas pro API - * - improve buffered-property with youtube/rtmp - * - use jwplayer5 api instead of old flash4 api - */ - -jQuery.webshims.register('mediaelement-swf', function($, webshims, window, document, undefined, options){ - "use strict"; - var SENDEVENT = 'sendEvent'; - var mediaelement = webshims.mediaelement; - var swfobject = window.swfobject; - var hasNative = Modernizr.audio && Modernizr.video; - var hasFlash = swfobject.hasFlashPlayerVersion('9.0.115'); - var loadedSwf = 0; - var getProps = { - paused: true, - ended: false, - currentSrc: '', - duration: window.NaN, - - readyState: 0, - networkState: 0, - videoHeight: 0, - videoWidth: 0, - 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, - _bufferedEnd: 0, - _bufferedStart: 0, - _metadata: false, - _durationCalcs: -1, - _callMeta: false, - currentTime: 0, - _ppFlag: undefined - }, getProps, getSetProps); - var idRep = /^jwplayer-/; - var getSwfDataFromID = function(id){ - - var elem = document.getElementById(id.replace(idRep, '')); - if(!elem){return;} - var data = webshims.data(elem, 'mediaelement'); - return data.isActive == 'third' ? data : null; - }; - - var getSwfDataFromElem = function(elem){ - try { - (elem.nodeName); - } catch(er){ - return null; - } - var data = webshims.data(elem, 'mediaelement'); - return (data && data.isActive== 'third') ? data : null; + mediaelement.createCueList = function(){ + return $.extend([], cueListProto); }; - var trigger = function(elem, evt){ - evt = $.Event(evt); - evt.preventDefault(); - $.event.trigger(evt, undefined, elem); - }; - - var playerSwfPath = options.playerPath || webshims.cfg.basePath + "jwplayer/" + (options.playerName || "player.swf"); - var jwplugin = options.pluginPath || webshims.cfg.basePath +'swf/jwwebshims.swf'; - - webshims.extendUNDEFProp(options.params, { - allowscriptaccess: 'always', - allowfullscreen: 'true', - wmode: 'transparent' - }); - webshims.extendUNDEFProp(options.vars, { - screencolor: 'ffffffff' - }); - webshims.extendUNDEFProp(options.attrs, { - bgcolor: '#000000' - }); - - var getDuration = function(data, obj){ - var curDuration = data.duration; - if(curDuration && data._durationCalcs > 0){return;} - try { - data.duration = data.jwapi.getPlaylist()[0].duration; - if(!data.duration || data.duration <= 0 || data.duration === data._lastDuration){ - data.duration = curDuration; + 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, '&#34;')) +'">'; } - } catch(er){} - if(data.duration && data.duration != data._lastDuration){ - trigger(data._elem, 'durationchange'); - if(data._elemNodeName == 'audio' || data._callMeta){ - mediaelement.jwEvents.Model.META($.extend({duration: data.duration}, obj), data); + 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 = ""; + } } - data._durationCalcs--; - } else { - data._durationCalcs++; + return html; + }; + + return function(cueText){ + return cueText.replace(tagSplits, replacer); + }; + })(); + + mediaelement.loadTextTrack = function(mediaelem, track, trackData, _default){ + var loadEvents = 'play playing timeupdate updatetrackdisplay'; + var obj = trackData.track; + var load = function(){ + var src = $.prop(track, 'src'); + var error; + var ajax; + if(obj.mode != 'disabled' && src && $.attr(track, 'src')){ + $(mediaelem).unbind(loadEvents, load); + $(track).unbind('checktrackmode', load); + if(!obj.readyState){ + error = function(){ + obj.readyState = 3; + obj.cues = null; + obj.activeCues = obj.shimActiveCues = obj._shimActiveCues = null; + $(track).triggerHandler('error'); + }; + obj.readyState = 1; + try { + obj.cues = mediaelement.createCueList(); + obj.activeCues = obj.shimActiveCues = obj._shimActiveCues = mediaelement.createCueList(); + ajax = $.ajax({ + dataType: 'text', + url: src, + success: function(text){ + if(ajax.getResponseHeader('content-type') != '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){ + obj.readyState = 2; + $(track).triggerHandler('load'); + $(mediaelem).triggerHandler('updatetrackdisplay'); + } else { + error(); + } + }); + + }, + error: error + }); + } catch(er){ + error(); + webshims.warn(er); + } + } + } + }; + obj.readyState = 0; + obj.shimActiveCues = null; + obj._shimActiveCues = null; + obj.activeCues = null; + obj.cues = null; + $(mediaelem).unbind(loadEvents, load); + $(track).unbind('checktrackmode', load); + $(mediaelem).on(loadEvents, load); + $(track).on('checktrackmode', load); + if(_default){ + obj.mode = showTracks[obj.kind] ? 'showing' : 'hidden'; + load(); } }; - var setReadyState = function(readyState, data){ - if(readyState < 3){ - clearTimeout(data._canplaythroughTimer); - } - if(readyState >= 3 && data.readyState < 3){ - data.readyState = readyState; - trigger(data._elem, 'canplay'); - 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; - }; - $.extend($.event.customEvent, { - updatemediaelementdimensions: true, - flashblocker: true, - swfstageresize: true, - mediaelementapichange: true - }); - - mediaelement.jwEvents = { - View: { + mediaelement.createTextTrack = function(mediaelem, track){ + var obj, trackData; + if(track.nodeName){ + trackData = webshims.data(track, 'trackData'); - PLAY: function(obj){ - var data = getSwfDataFromID(obj.id); - if(!data || data.stopPlayPause){return;} - data._ppFlag = true; - if(data.paused == obj.state){ - data.paused = !obj.state; - if(data.ended){ - data.ended = false; + 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; } - trigger(data._elem, obj.state ? 'play' : 'pause'); - } + }); } - }, - Model: { - BUFFER: function(obj){ - var data = getSwfDataFromID(obj.id); - if(!data || !('percentage' in obj) || data._bufferedEnd == obj.percentage){return;} - data.networkState = (obj.percentage == 100) ? 1 : 2; - if(isNaN(data.duration) || (obj.percentage > 5 && obj.percentage < 25) || (obj.percentage === 100)){ - getDuration(data, obj); - } + + if(track.nodeName){ - if(data.ended){ - data.ended = false; + if(supportTrackMod){ + copyProps.forEach(function(copyProp){ + webshims.defineProperty(obj, copyName[copyProp] || copyProp, { + get: function(){ + return $.prop(track, copyProp); + } + }); + }); } - if(!data.duration){ - return; - } - if(obj.percentage > 2 && obj.percentage < 20){ - setReadyState(3, data); - } else if(obj.percentage > 20){ - setReadyState(4, data); - } - if(data._bufferedEnd && (data._bufferedEnd > obj.percentage)){ - data._bufferedStart = data.currentTime || 0; - } - data._bufferedEnd = obj.percentage; - data.buffered.length = 1; - if(obj.percentage == 100){ - data.networkState = 1; - setReadyState(4, data); + trackData = webshims.data(track, 'trackData', {track: obj}); + mediaelement.loadTextTrack(mediaelem, track, trackData, ($.prop(track, 'default') && $(track).siblings('track[default]')[ADDBACK]()[0] == track)); + } else { + if(supportTrackMod){ + copyProps.forEach(function(copyProp){ + webshims.defineProperty(obj, copyName[copyProp] || copyProp, { + value: track[copyProp], + writeable: false + }); + }); } - $.event.trigger('progress', undefined, data._elem, true); - }, - META: function(obj, data){ - - data = data && data.networkState ? data : getSwfDataFromID(obj.id); - - if(!data){return;} - if( !('duration' in obj) ){ - data._callMeta = true; - return; - } - - if( data._metadata && (!obj.height || data.videoHeight == obj.height) && (obj.duration === data.duration) ){return;} - - data._metadata = true; - - var oldDur = data.duration; - if(obj.duration){ - data.duration = obj.duration; - } - data._lastDuration = data.duration; - if(obj.height || obj.width){ - data.videoHeight = obj.height || 0; - data.videoWidth = obj.width || 0; - } - if(!data.networkState){ - data.networkState = 2; - } - if(data.readyState < 1){ - setReadyState(1, data); - } - if(data.duration && oldDur !== data.duration){ - trigger(data._elem, 'durationchange'); - } - - trigger(data._elem, 'loadedmetadata'); - }, - TIME: function(obj){ - var data = getSwfDataFromID(obj.id); - if(!data || data.currentTime === obj.position){return;} - data.currentTime = obj.position; - if(data.duration && data.duration < data.currentTime){ - getDuration(data, obj); - } - if(data.readyState < 2){ - setReadyState(2, data); - } - if(data.ended){ - data.ended = false; - } - trigger(data._elem, 'timeupdate'); - - }, - STATE: function(obj){ - var data = getSwfDataFromID(obj.id); - if(!data){return;} - switch(obj.newstate) { - case 'BUFFERING': - - if(data.ended){ - data.ended = false; - } - setReadyState(1, data); - trigger(data._elem, 'waiting'); - break; - case 'PLAYING': - data.paused = false; - data._ppFlag = true; - if(!data.duration){ - getDuration(data, obj); - } - if(data.readyState < 3){ - setReadyState(3, data); - } - if(data.ended){ - data.ended = false; - } - trigger(data._elem, 'playing'); - break; - case 'PAUSED': - if(!data.paused && !data.stopPlayPause){ - data.paused = true; - data._ppFlag = true; - trigger(data._elem, 'pause'); - } - break; - case 'COMPLETED': - if(data.readyState < 4){ - setReadyState(4, data); - } - data.ended = true; - trigger(data._elem, 'ended'); - break; - } + obj.cues = mediaelement.createCueList(); + obj.activeCues = obj._shimActiveCues = obj.shimActiveCues = mediaelement.createCueList(); + obj.mode = 'hidden'; + obj.readyState = 2; } } - ,Controller: { - - ERROR: function(obj){ - var data = getSwfDataFromID(obj.id); - if(!data){return;} - mediaelement.setError(data._elem, obj.message); - }, - SEEK: function(obj){ - var data = getSwfDataFromID(obj.id); - if(!data){return;} - if(data.ended){ - data.ended = false; - } - if(data.paused){ - try { - data.jwapi[SENDEVENT]('play', 'false'); - } catch(er){} - } - if(data.currentTime != obj.position){ - data.currentTime = obj.position; - trigger(data._elem, 'timeupdate'); - } - - - }, - VOLUME: function(obj){ - var data = getSwfDataFromID(obj.id); - if(!data){return;} - var newVolume = obj.percentage / 100; - if(data.volume == newVolume){return;} - data.volume = newVolume; - trigger(data._elem, 'volumechange'); - }, - MUTE: function(obj){ - if(obj.state){return;} - var data = getSwfDataFromID(obj.id); - if(!data){return;} - if(data.muted == obj.state){return;} - data.muted = obj.state; - trigger(data._elem, 'volumechange'); - } - } + return obj; }; - var initEvents = function(data){ - var passed = true; - $.each(mediaelement.jwEvents, function(mvcName, evts){ - $.each(evts, function(evtName){ - try { - data.jwapi['add'+ mvcName +'Listener'](evtName, 'jQuery.webshims.mediaelement.jwEvents.'+ mvcName +'.'+ evtName); - } catch(er){ - passed = false; - return false; - } - }); - }); - return passed; - }; - 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(); - data.jwapi[operation.fn].apply(data.jwapi, operation.args); +/* +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 GoogleTimestampParser = /^([\d\.]+)\s+\+([\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; + + return function(subtitleElement,objectCount){ + var cueDefaults = []; + + var subtitleParts, timeIn, timeOut, html, timeData, subtitlePartIndex, cueSettings = "", id, specialCueData; + var timestampMatch, tmpCue; + + // WebVTT Special Cue Logic + if ((specialCueData = WebVTTDEFAULTSCueParser.exec(subtitleElement))) { +// cueDefaults = specialCueData.slice(2).join(""); +// cueDefaults = cueDefaults.split(/\s+/g).filter(function(def) { return def && !!def.length; }); + return null; + } else if ((specialCueData = WebVTTSTYLECueParser.exec(subtitleElement))) { + return null; + } else if ((specialCueData = WebVTTCOMMENTCueParser.exec(subtitleElement))) { + return null; // At this stage, we don't want to do anything with these. } - } - 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(); - } catch(er){} - } - }, 1); - } - }; - - - mediaelement.playerResize = function(id){ - if(!id){return;} - var elem = document.getElementById(id.replace(idRep, '')); + + subtitleParts = subtitleElement.split(/\n/g); - if(elem){ - $(elem).triggerHandler('swfstageresize'); - } - elem = null; - }; - - - $(document).on('emptied', function(e){ - var data = getSwfDataFromElem(e.target); - startAutoPlay(data); - }); - - var localConnectionTimer; - mediaelement.jwPlayerReady = function(jwData){ - var data = getSwfDataFromID(jwData.id); - var passed = true; - var i = 0; - var doneFn = function(){ - if(i > 9){return;} - i++; - if(initEvents(data)){ - if(!data.wasSwfReady){ - var version = parseFloat( jwData.version, 10); - if(version < 5.1 || version >= 6){ - webshims.warn('mediaelement-swf is only testet with jwplayer 5.6+'); - } - } else { - $(data._elem).mediaLoad(); - - } - data.wasSwfReady = true; - data.tryedReframeing = 0; - workActionQueue(data); - startAutoPlay(data); - } else { - clearTimeout(data.reframeTimer); - data.reframeTimer = setTimeout(doneFn, 9 * i); - if(i > 2 && data.tryedReframeing < 9){ - data.tryedReframeing++; - data.shadowElem.css({overflow: 'visible'}); - setTimeout(function(){ - data.shadowElem.css({overflow: 'hidden'}); - }, 16); - } + // 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(!data || !data.jwapi){return;} - if(!data.tryedReframeing){ - data.tryedReframeing = 0; - } - clearTimeout(localConnectionTimer); - data.jwData = jwData; - data.shadowElem.removeClass('flashblocker-assumed'); - $.prop(data._elem, 'volume', data.volume); - $.prop(data._elem, 'muted', data.muted); - doneFn(); - }; - - var addMediaToStopEvents = $.noop; - if(hasNative){ - var stopEvents = { - play: 1, - playing: 1 - }; - var hideEvtArray = ['play', 'pause', 'playing', 'canplay', 'progress', 'waiting', 'ended', 'loadedmetadata', 'durationchange', 'emptied']; - var hidevents = hideEvtArray.map(function(evt){ - return evt +'.webshimspolyfill'; - }).join(' '); - var opposite = { - 'html5': 'third', - 'third': 'html5' - }; - 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 { - data.isActive = opposite[data.isActive]; - $(event.target).pause(); - data.isActive = opposite[data.isActive]; + 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))) { + + // 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; } - }; - - - 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 = ['_bufferedEnd', '_bufferedStart', '_metadata', '_ppFlag', 'currentSrc', 'currentTime', 'duration', 'ended', 'networkState', 'paused', 'videoHeight', 'videoWidth', '_callMeta', '_durationCalcs']; - var len = resetProtoProps.length; - return function(data){ + + 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; + },{}); - if(!data){return;} - var lenI = len; - var networkState = data.networkState; - setReadyState(0, data); - while(--lenI){ - delete data[resetProtoProps[lenI]]; + // 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 TextTrackCue constructor expects + cueSettings = ""; + for (var key in compositeCueSettings) { + if (compositeCueSettings.hasOwnProperty(key)) { + cueSettings += !!cueSettings.length ? " " : ""; + cueSettings += key + ":" + compositeCueSettings[key]; + } } - data.actionQueue = []; - data.buffered.length = 0; - if(networkState){ - trigger(data._elem, 'emptied'); +*/ + // The remaining lines are the subtitle payload itself (after removing an ID if present, and the time); + html = subtitleParts.join("\n"); + tmpCue = new TextTrackCue(timeIn, timeOut, html); + if(id){ + tmpCue.id = id; } + return tmpCue; }; })(); - var setElementDimension = function(data, hasControls){ - var elem = data._elem; - var box = data.shadowElem; - $(elem)[hasControls ? 'addClass' : 'removeClass']('webshims-controls'); - if(data._elemNodeName == 'audio' && !hasControls){ - box.css({width: 0, height: 0}); - } else { - box.css({ - width: elem.style.width || $(elem).width(), - height: elem.style.height || $(elem).height() - }); - } - }; - - mediaelement.createSWF = function( elem, canPlaySrc, data ){ - if(!hasFlash){ - setTimeout(function(){ - $(elem).mediaLoad(); //<- this should produce a mediaerror - }, 1); - return; - } - - if(loadedSwf < 1){ - loadedSwf = 1; - } else { - loadedSwf++; - } - var vars = $.extend({}, options.vars, { - image: $.attr(elem, 'poster') && $.prop(elem, 'poster') || '', - file: canPlaySrc.streamId || canPlaySrc.srcProp - }); - var elemVars = $(elem).data('vars') || {}; - - if(canPlaySrc.server){ - vars.streamer = canPlaySrc.server; - } - if(!data){ - data = webshims.data(elem, 'mediaelement'); - } - - if(data && data.swfCreated){ - mediaelement.setActive(elem, 'third', data); - resetSwfProps(data); - data.currentSrc = canPlaySrc.srcProp; - $.extend(vars, elemVars); - options.changeSWF(vars, elem, canPlaySrc, data, 'load'); - queueSwfMethod(elem, SENDEVENT, ['LOAD', vars]); - return; - } - - - var hasControls = $.prop(elem, 'controls'); - var elemId = 'jwplayer-'+ webshims.getID(elem); - var params = $.extend( - {}, - options.params, - $(elem).data('params') - ); - var elemNodeName = elem.nodeName.toLowerCase(); - var attrs = $.extend( - {}, - options.attrs, - { - name: elemId, - id: elemId - }, - $(elem).data('attrs') - ); - var box = $('<div class="polyfill-'+ (elemNodeName) +' polyfill-mediaelement" id="wrapper-'+ elemId +'"><div id="'+ elemId +'"></div>') - .css({ - position: 'relative', - overflow: 'hidden' - }) - ; - var setDimensions = function(){ - setElementDimension(data, $.prop(elem, 'controls')); - }; + mediaelement.parseCaptions = function(captionData, track, complete) { + var subtitles = mediaelement.createCueList(); + var cue, lazyProcess, regWevVTT; + var startDate; + var isWEBVTT; + if (captionData) { - data = webshims.data(elem, 'mediaelement', webshims.objectCreate(playerStateObj, { - actionQueue: { - value: [] - }, - shadowElem: { - value: box - }, - _elemNodeName: { - value: elemNodeName - }, - _elem: { - value: elem - }, - currentSrc: { - value: canPlaySrc.srcProp - }, - swfCreated: { - value: true - }, - buffered: { - value: { - start: function(index){ - if(index >= data.buffered.length){ - webshims.error('buffered index size error'); - return; + 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){ + if(!isWEBVTT){ + webshims.error('please use WebVTT format. This is the standard'); + complete(null); + break; } - return 0; - }, - end: function(index){ - if(index >= data.buffered.length){ - webshims.error('buffered index size error'); - return; + cue = mediaelement.parseCaptionChunk(cue, i); + if(cue){ + track.addCue(cue); } - return ( (data.duration - data._bufferedStart) * data._bufferedEnd / 100) + data._bufferedStart; - }, - length: 0 + } + if(startDate < (new Date().getTime()) - 30){ + i++; + setTimeout(function(){ + startDate = new Date().getTime(); + lazyProcess(i, len); + }, 90); + + break; + } } - } - })); - - setElementDimension(data, hasControls); - - box.insertBefore(elem); - - if(hasNative){ - $.extend(data, {volume: $.prop(elem, 'volume'), muted: $.prop(elem, 'muted')}); - } - - $.extend(vars, - { - id: elemId, - controlbar: hasControls ? options.vars.controlbar || (elemNodeName == 'video' ? 'over' : 'bottom') : (elemNodeName == 'video') ? 'none' : 'bottom', - icons: ''+ (hasControls && elemNodeName == 'video') - }, - elemVars, - {playerready: 'jQuery.webshims.mediaelement.jwPlayerReady'} - ); - if(vars.plugins){ - vars.plugins += ','+jwplugin; - } else { - vars.plugins = jwplugin; - } - - - webshims.addShadowDom(elem, box); - - addMediaToStopEvents(elem); - - mediaelement.setActive(elem, 'third', data); - - options.changeSWF(vars, elem, canPlaySrc, data, 'embed'); - - - $(document).on('updateshadowdom', setDimensions); - $(elem).on('updatemediaelementdimensions', setDimensions); - - - swfobject.embedSWF(playerSwfPath, elemId, "100%", "100%", "9.0.0", false, vars, params, attrs, function(swfData){ - - if(swfData.success){ - data.jwapi = swfData.ref; - - if(!hasControls){ - $(swfData.ref).attr('tabindex', '-1').css('outline', 'none'); + 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(){ - 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'}); + startDate = new Date().getTime(); + captionData = captionData.split(/\n\n+/g); + lazyProcess(0, captionData.length); }, 9); - if(!localConnectionTimer){ - clearTimeout(localConnectionTimer); - localConnectionTimer = setTimeout(function(){ - 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); - } - } - }); + }, 9); + + } else { + webshims.error("Required parameter captionData not supplied."); + } }; - var queueSwfMethod = function(elem, fn, args, data){ - data = data || getSwfDataFromElem(elem); - if(data){ - if(data.jwapi && data.jwapi[fn]){ - data.jwapi[fn].apply(data.jwapi, 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; + 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} + }); + createEventTarget(baseData.textTracks); } - return false; + return baseData.textTracks; }; - ['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; - }; + if(!Modernizr.track){ + webshims.defineNodeNamesBooleanProperty(['track'], 'default'); + webshims.reflectProperties(['track'], ['srclang', 'label']); - createGetSetProp('volume', function(v){ - var data = getSwfDataFromElem(this); - if(data){ - v *= 100; - if(!isNaN(v)){ - var muted = data.muted; - if(v < 0 || v > 100){ - webshims.error('volume greater or less than allowed '+ (v / 100)); - } - - queueSwfMethod(this, SENDEVENT, ['VOLUME', v], data); - if(muted){ - try { - data.jwapi.sendEvent('mute', 'true'); - } catch(er){} - } - v /= 100; - if(data.volume == v || data.isActive != 'third'){return;} - data.volume = v; - trigger(data._elem, 'volumechange'); - data = null; - } - } else if(mediaSup.volume.prop._supset) { - return mediaSup.volume.prop._supset.apply(this, arguments); + webshims.defineNodeNameProperties('track', { + src: { + //attr: {}, + reflect: true, + propType: 'src' } }); - - createGetSetProp('muted', function(m){ - var data = getSwfDataFromElem(this); - if(data){ - m = !!m; - queueSwfMethod(this, SENDEVENT, ['mute', ''+m], data); - if(data.muted == m || data.isActive != 'third'){return;} - 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)) { - if(data.paused){ - clearTimeout(data.stopPlayPause); - data.stopPlayPause = setTimeout(function(){ - data.paused = true; - data.stopPlayPause = false; - }, 50); + } + + webshims.defineNodeNameProperties('track', { + kind: { + attr: Modernizr.track ? { + set: function(value){ + var trackData = webshims.data(this, 'trackData'); + this.setAttribute('data-kind', value); + if(trackData){ + trackData.attrKind = value; } - queueSwfMethod(this, SENDEVENT, ['SEEK', '' + t], data); - - if(data.paused){ - if(data.readyState > 0){ - data.currentTime = t; - trigger(data._elem, 'timeupdate'); - } - try { - data.jwapi[SENDEVENT]('play', 'false'); - } catch(er){} - + }, + get: function(){ + var trackData = webshims.data(this, 'trackData'); + if(trackData && ('attrKind' in trackData)){ + return trackData.attrKind; } + return this.getAttribute('kind'); } - - } else if(mediaSup.currentTime.prop._supset) { - return mediaSup.currentTime.prop._supset.apply(this, arguments); + } : {}, + 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'); + var track = this; + if(trackData){ + if(copyProp == 'kind'){ + refreshTrack(this, trackData); + } + if(!supportTrackMod){ + trackData.track[name] = $.prop(this, copyProp); + } + clearTimeout(trackData.changedTrackPropTimer); + trackData.changedTrackPropTimer = setTimeout(function(){ + $(track).trigger('updatesubtitlestate'); + }, 1); } }); - - ['play', 'pause'].forEach(function(fn){ - descs[fn] = { - value: function(){ - var data = getSwfDataFromElem(this); - if(data){ - if(data.stopPlayPause){ - clearTimeout(data.stopPlayPause); - } - queueSwfMethod(this, SENDEVENT, ['play', fn == 'play'], data); - setTimeout(function(){ - if(data.isActive == 'third'){ - data._ppFlag = true; - if(data.paused != (fn != 'play')){ - data.paused = fn != 'play'; - trigger(data._elem, fn); - } - } - }, 1); - } else if(mediaSup[fn].prop._supvalue) { - return mediaSup[fn].prop._supvalue.apply(this, arguments); - } + }); + + + 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); } - }; - }); + } + } - getPropKeys.forEach(createGetProp); - - webshims.onNodeNamesPropertyModify(nodeName, 'controls', function(val, boolProp){ - var data = getSwfDataFromElem(this); - $(this)[boolProp ? 'addClass' : 'removeClass']('webshims-controls'); - - if(data){ - try { - queueSwfMethod(this, boolProp ? 'showControls' : 'hideControls', [nodeName], data); - } catch(er){ - webshims.warn("you need to generate a crossdomain.xml"); + }); + + // + + webshims.defineNodeNamesProperties(['track'], { + ERROR: { + value: 3 + }, + LOADED: { + value: 2 + }, + LOADING: { + value: 1 + }, + NONE: { + value: 0 + }, + readyState: { + get: function(){ + return ($.prop(this, 'track') || {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); } - if(nodeName == 'audio'){ - setElementDimension(data, boolProp); + 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 = []; } - $(data.jwapi).attr('tabindex', boolProp ? '0' : '-1'); + baseData.scriptedTextTracks.push(textTrack); + updateMediaTrackList.call(this); + return textTrack; } - }); - - mediaSup = webshims.defineNodeNameProperties(nodeName, descs, 'prop'); + } + }, 'prop'); + + + $(document).on('emptied ended updatetracklist', 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); + } + } }); - if(hasFlash && $.cleanData){ - var oldClean = $.cleanData; - var flashNames = { - object: 1, - OBJECT: 1 - }; - $.cleanData = function(elems){ - var i, len, prop; - if(elems && (len = elems.length) && loadedSwf){ + var getNativeReadyState = function(trackElem, textTrack){ + return textTrack.readyState || trackElem.readyState; + }; + var stopOriginalEvent = function(e){ + if(e.originalEvent){ + e.stopImmediatePropagation(); + } + }; + var startTrackImplementation = function(){ + if(webshims.implement(this, 'track')){ + var shimedTrack = $.prop(this, 'track'); + var origTrack = this.track; + var kind; + var readyState; + if(origTrack){ + kind = $.prop(this, 'kind'); + readyState = getNativeReadyState(this, origTrack); + if (origTrack.mode || readyState) { + shimedTrack.mode = numericModes[origTrack.mode] || origTrack.mode; + } + //disable track from showing + remove UI + if(kind != 'descriptions'){ + origTrack.mode = (typeof origTrack.mode == 'string') ? 'disabled' : 0; + this.kind = 'metadata'; + $(this).attr({kind: kind}); + } - for(i = 0; i < len; i++){ - if(flashNames[elems[i].nodeName]){ - if(SENDEVENT in elems[i]){ - loadedSwf--; - try { - elems[i][SENDEVENT]('play', false); - } catch(er){} - } - try { - for (prop in elems[i]) { - if (typeof elems[i][prop] == "function") { - elems[i][prop] = null; - } - } - } catch(er){} + } + $(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); + }) + .each(function(){ + if(Modernizr.track){ + var shimedTextTracks = $.prop(this, 'textTracks'); + var origTextTracks = this.textTracks; + if(shimedTextTracks.length != origTextTracks.length){ + webshims.error("textTracks couldn't be copied"); } + + $('track', this).each(startTrackImplementation); } - + }) + ; + 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); } - return oldClean.apply(this, arguments); - }; - } - - if(!hasNative){ - - ['poster', 'src'].forEach(function(prop){ - webshims.defineNodeNamesProperty(prop == 'src' ? ['audio', 'video', 'source'] : ['video'], prop, { - //attr: {}, - reflect: true, - propType: 'src' - }); }); - - - ['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(Modernizr.track){ + $('video, audio').trigger('trackapichange'); } }); \ No newline at end of file