knockout-es5
Version:
Knockout.js meets ECMAScript 5 properties
270 lines (222 loc) • 8.53 kB
JavaScript
/*! WeakMap shim
* (The MIT License)
*
* Copyright (c) 2012 Brandon Benvie <http://bbenvie.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the 'Software'), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included with all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Original WeakMap implementation by Gozala @ https://gist.github.com/1269991
// Updated and bugfixed by Raynos @ https://gist.github.com/1638059
// Expanded by Benvie @ https://github.com/Benvie/harmony-collections
// This is the version used by knockout-es5. Modified by Steve Sanderson as follows:
// [1] Deleted weakmap.min.js (it's not useful as it would be out of sync with weakmap.js now I'm editing it)
// [2] Since UglifyJS strips inline function names (and you can't disable that without disabling name mangling
// entirely), insert code that re-adds function names
void function(global, undefined_, undefined){
var getProps = Object.getOwnPropertyNames,
cachedWindowNames = typeof window === 'object' ? Object.getOwnPropertyNames(window) : [],
defProp = Object.defineProperty,
toSource = Function.prototype.toString,
create = Object.create,
hasOwn = Object.prototype.hasOwnProperty,
funcName = /^\n?function\s?(\w*)?_?\(/;
function define(object, key, value){
if (typeof key === 'function') {
value = key;
key = nameOf(value).replace(/_$/, '');
}
return defProp(object, key, { configurable: true, writable: true, value: value });
}
function nameOf(func){
return typeof func !== 'function'
? '' : '_name' in func
? func._name : 'name' in func
? func.name : toSource.call(func).match(funcName)[1];
}
function namedFunction(name, func) {
// Undo the name-stripping that UglifyJS does
func._name = name;
return func;
}
// ############
// ### Data ###
// ############
var Data = (function(){
var dataDesc = { value: { writable: true, value: undefined } },
uids = create(null),
createUID = function(){
var key = Math.random().toString(36).slice(2);
return key in uids ? createUID() : uids[key] = key;
},
globalID = createUID(),
storage = function(obj){
if (hasOwn.call(obj, globalID))
return obj[globalID];
if (!Object.isExtensible(obj))
throw new TypeError("Object must be extensible");
var store = create(null);
defProp(obj, globalID, { value: store });
return store;
};
// common per-object storage area made visible by patching getOwnPropertyNames'
define(Object, namedFunction('getOwnPropertyNames', function getOwnPropertyNames(obj){
// gh-43
var coercedObj = Object(obj), props;
// Fixes for debuggers:
// 1) Some objects lack .toString(), calling it on them make Chrome
// debugger fail when inspecting variables.
// 2) Window.prototype methods and properties are private in IE11 and
// throw 'Invalid calling object'.
if (coercedObj !== Window.prototype && 'toString' in coercedObj
&& coercedObj.toString() === '[object Window]')
{
try {
props = getProps(obj);
} catch (e) {
props = cachedWindowNames;
}
} else {
props = getProps(obj);
}
if (hasOwn.call(obj, globalID))
props.splice(props.indexOf(globalID), 1);
return props;
}));
function Data(){
var puid = createUID(),
secret = {};
this.unlock = function(obj){
var store = storage(obj);
if (hasOwn.call(store, puid))
return store[puid](secret);
var data = create(null, dataDesc);
defProp(store, puid, {
value: function(key){ if (key === secret) return data; }
});
return data;
}
}
define(Data.prototype, namedFunction('get', function get(o){ return this.unlock(o).value }));
define(Data.prototype, namedFunction('set', function set(o, v){ this.unlock(o).value = v }));
return Data;
}());
var WM = (function(data){
var validate = function(key){
if (key == null || typeof key !== 'object' && typeof key !== 'function')
throw new TypeError("Invalid WeakMap key");
}
var wrap = function(collection, value){
var store = data.unlock(collection);
if (store.value)
throw new TypeError("Object is already a WeakMap");
store.value = value;
}
var unwrap = function(collection){
var storage = data.unlock(collection).value;
if (!storage)
throw new TypeError("WeakMap is not generic");
return storage;
}
var initialize = function(weakmap, iterable){
if (iterable !== null && typeof iterable === 'object' && typeof iterable.forEach === 'function') {
iterable.forEach(function(item, i){
if (item instanceof Array && item.length === 2)
set.call(weakmap, iterable[i][0], iterable[i][1]);
});
}
}
function WeakMap(iterable){
if (this === global || this == null || this === WeakMap.prototype)
return new WeakMap(iterable);
wrap(this, new Data);
initialize(this, iterable);
}
function get(key){
validate(key);
var value = unwrap(this).get(key);
return value === undefined_ ? undefined : value;
}
function set(key, value){
validate(key);
// store a token for explicit undefined so that "has" works correctly
unwrap(this).set(key, value === undefined ? undefined_ : value);
}
function has(key){
validate(key);
return unwrap(this).get(key) !== undefined;
}
function delete_(key){
validate(key);
var data = unwrap(this),
had = data.get(key) !== undefined;
data.set(key, undefined);
return had;
}
function toString(){
unwrap(this);
return '[object WeakMap]';
}
// Undo the function-name stripping that UglifyJS does
get._name = 'get';
set._name = 'set';
has._name = 'has';
toString._name = 'toString';
var src = (''+Object).split('Object');
var stringifier = namedFunction('toString', function toString(){
return src[0] + nameOf(this) + src[1];
});
define(stringifier, stringifier);
var prep = { __proto__: [] } instanceof Array
? function(f){ f.__proto__ = stringifier }
: function(f){ define(f, stringifier) };
prep(WeakMap);
[toString, get, set, has, delete_].forEach(function(method){
define(WeakMap.prototype, method);
prep(method);
});
return WeakMap;
}(new Data));
var defaultCreator = Object.create
? function(){ return Object.create(null) }
: function(){ return {} };
function createStorage(creator){
var weakmap = new WM;
creator || (creator = defaultCreator);
function storage(object, value){
if (value || arguments.length === 2) {
weakmap.set(object, value);
} else {
value = weakmap.get(object);
if (value === undefined) {
value = creator(object);
weakmap.set(object, value);
}
}
return value;
}
return storage;
}
if (typeof module !== 'undefined') {
module.exports = WM;
} else if (typeof exports !== 'undefined') {
exports.WeakMap = WM;
} else if (!('WeakMap' in global)) {
global.WeakMap = WM;
}
WM.createStorage = createStorage;
if (global.WeakMap)
global.WeakMap.createStorage = createStorage;
}(function(){ return this }());