123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- '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
- 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) {
- 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); });
- };
|