You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
3.8 KiB
117 lines
3.8 KiB
'use strict'; |
|
|
|
var map = require('es5-ext/object/map') |
|
, isCallable = require('es5-ext/object/is-callable') |
|
, validValue = require('es5-ext/object/valid-value') |
|
, contains = require('es5-ext/string/#/contains') |
|
|
|
, call = Function.prototype.call |
|
, defineProperty = Object.defineProperty |
|
, getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor |
|
, getPrototypeOf = Object.getPrototypeOf |
|
, hasOwnProperty = Object.prototype.hasOwnProperty |
|
, cacheDesc = { configurable: false, enumerable: false, writable: false, |
|
value: null } |
|
, define; |
|
|
|
define = function (name, options) { |
|
var value, dgs, cacheName, desc, writable = false, resolvable |
|
, flat; |
|
options = Object(validValue(options)); |
|
cacheName = options.cacheName; |
|
flat = options.flat; |
|
if (cacheName == null) cacheName = name; |
|
delete options.cacheName; |
|
value = options.value; |
|
resolvable = isCallable(value); |
|
delete options.value; |
|
dgs = { configurable: Boolean(options.configurable), |
|
enumerable: Boolean(options.enumerable) }; |
|
if (name !== cacheName) { |
|
dgs.get = function () { |
|
if (hasOwnProperty.call(this, cacheName)) return this[cacheName]; |
|
cacheDesc.value = resolvable ? call.call(value, this, options) : value; |
|
cacheDesc.writable = writable; |
|
defineProperty(this, cacheName, cacheDesc); |
|
cacheDesc.value = null; |
|
if (desc) defineProperty(this, name, desc); |
|
return this[cacheName]; |
|
}; |
|
} else if (!flat) { |
|
dgs.get = function self() { |
|
var ownDesc; |
|
if (hasOwnProperty.call(this, name)) { |
|
ownDesc = getOwnPropertyDescriptor(this, name); |
|
// It happens in Safari, that getter is still called after property |
|
// was defined with a value, following workarounds that |
|
// While in IE11 it may happen that here ownDesc is undefined (go figure) |
|
if (ownDesc) { |
|
if (ownDesc.hasOwnProperty('value')) return ownDesc.value; |
|
if ((typeof ownDesc.get === 'function') && (ownDesc.get !== self)) { |
|
return ownDesc.get.call(this); |
|
} |
|
return value; |
|
} |
|
} |
|
desc.value = resolvable ? call.call(value, this, options) : value; |
|
defineProperty(this, name, desc); |
|
desc.value = null; |
|
return this[name]; |
|
}; |
|
} else { |
|
dgs.get = function self() { |
|
var base = this, ownDesc; |
|
if (hasOwnProperty.call(this, name)) { |
|
// It happens in Safari, that getter is still called after property |
|
// was defined with a value, following workarounds that |
|
ownDesc = getOwnPropertyDescriptor(this, name); |
|
if (ownDesc.hasOwnProperty('value')) return ownDesc.value; |
|
if ((typeof ownDesc.get === 'function') && (ownDesc.get !== self)) { |
|
return ownDesc.get.call(this); |
|
} |
|
} |
|
while (!hasOwnProperty.call(base, name)) base = getPrototypeOf(base); |
|
desc.value = resolvable ? call.call(value, base, options) : value; |
|
defineProperty(base, name, desc); |
|
desc.value = null; |
|
return base[name]; |
|
}; |
|
} |
|
dgs.set = function (value) { |
|
if (hasOwnProperty.call(this, name)) { |
|
throw new TypeError("Cannot assign to lazy defined '" + name + "' property of " + this); |
|
} |
|
dgs.get.call(this); |
|
this[cacheName] = value; |
|
}; |
|
if (options.desc) { |
|
desc = { |
|
configurable: contains.call(options.desc, 'c'), |
|
enumerable: contains.call(options.desc, 'e') |
|
}; |
|
if (cacheName === name) { |
|
desc.writable = contains.call(options.desc, 'w'); |
|
desc.value = null; |
|
} else { |
|
writable = contains.call(options.desc, 'w'); |
|
desc.get = dgs.get; |
|
desc.set = dgs.set; |
|
} |
|
delete options.desc; |
|
} else if (cacheName === name) { |
|
desc = { |
|
configurable: Boolean(options.configurable), |
|
enumerable: Boolean(options.enumerable), |
|
writable: Boolean(options.writable), |
|
value: null |
|
}; |
|
} |
|
delete options.configurable; |
|
delete options.enumerable; |
|
delete options.writable; |
|
return dgs; |
|
}; |
|
|
|
module.exports = function (props) { |
|
return map(props, function (desc, name) { return define(name, desc); }); |
|
};
|
|
|