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