UNPKG

13.4 kBJavaScriptView Raw
1!function() {
2 'use strict';
3 var _undefined;
4
5 var var1 = 'roperties';
6 var var2 = 'ropertyDescriptors';
7 var var3 = 'static';
8 var var4 = 'onfiguration';
9 var _properties = 'p' + var1;
10 var _deepProperties = 'deepP' + var1;
11 var _propertyDescriptors = 'p' + var2;
12 var _staticProperties = var3 + 'P' + var1;
13 var _staticDeepProperties = var3 + 'DeepP' + var1;
14 var _staticPropertyDescriptors = var3 + 'P' + var2;
15 var _configuration = 'c' + var4;
16 var _deepConfiguration = 'deepC' + var4;
17 var _deepProps = 'deepProps';
18 var _deepStatics = 'deepStatics';
19 var _deepConf = 'deepConf';
20 var _initializers = 'initializers';
21 var _methods = 'methods';
22 var _composers = 'composers';
23 var _compose = 'compose';
24 var _object = 'object';
25 var _length = 'length';
26 var _create = 'create';
27 var _Object = Object;
28 var isArray = Array.isArray;
29 var defineProperties = _Object.defineProperties;
30 var objectKeys = _Object.keys;
31 var baseStampit = Array.prototype; // temporary reusing the variable
32 var concat = baseStampit.concat;
33 var slice = baseStampit.slice;
34
35 function _mergeOrAssign(action, dst) {
36 return slice.call(arguments, 2).reduce(action, dst);
37 }
38
39 function assignOne(dst, src) {
40 if (src) {
41 var keys = objectKeys(src);
42 for (var i = 0; i < keys[_length]; i++) {
43 dst[keys[i]] = src[keys[i]];
44 }
45 }
46 return dst;
47 }
48
49 var assign = _Object.assign || _mergeOrAssign.bind(0, assignOne);
50
51 function isFunction(obj) {
52 return typeof obj == 'function';
53 }
54
55 function isObject(obj) {
56 return obj && typeof obj == _object || isFunction(obj);
57 }
58
59 function isPlainObject(value) {
60 return value && typeof value == _object &&
61 value.__proto__ == _Object.prototype;
62 }
63
64 /**
65 * The 'src' argument plays the command role.
66 * The returned values is always of the same type as the 'src'.
67 * @param {Array|Object|*} dst Destination
68 * @param {Array|Object|*} src Source
69 * @returns {Array|Object|*} The `dst` argument
70 */
71 function mergeOne(dst, src) {
72 if (src === _undefined) return dst;
73
74 // According to specification arrays must be concatenated.
75 // Also, the '.concat' creates a new array instance. Overrides the 'dst'.
76 if (isArray(src)) return (isArray(dst) ? dst : []).concat(src);
77
78 // Now deal with non plain 'src' object. 'src' overrides 'dst'
79 // Note that functions are also assigned! We do not deep merge functions.
80 if (!isPlainObject(src)) return src;
81
82 var keys = objectKeys(src), i = 0, key;
83 for (; i < keys[_length];) {
84 key = keys[i++];
85
86 // Do not merge properties with the '_undefined' value.
87 if (src[key] !== _undefined) {
88 // deep merge each property. Recursion!
89 dst[key] = mergeOne(
90 // Recursive calls to mergeOne() must allow only plain objects or arrays in dst
91 isPlainObject(dst[key]) || isArray(src[key]) ? dst[key] : {},
92 src[key]
93 );
94 }
95 }
96
97 return dst;
98 }
99
100 var merge = _mergeOrAssign.bind(0, mergeOne);
101
102 function extractUniqueFunctions() {
103 var1 = concat.apply([], arguments).filter(function(elem, index, array) {
104 return isFunction(elem) && array.indexOf(elem) === index;
105 });
106 return var1[_length] ? var1 : _undefined;
107 }
108
109
110 /**
111 * Converts stampit extended descriptor to a standard one.
112 * @param {Object|*} descr
113 * methods
114 * properties
115 * props
116 * initializers
117 * init
118 * deepProperties
119 * deepProps
120 * propertyDescriptors
121 * staticProperties
122 * statics
123 * staticDeepProperties
124 * deepStatics
125 * staticPropertyDescriptors
126 * configuration
127 * conf
128 * deepConfiguration
129 * deepConf
130 * composers
131 * @returns {Descriptor} Standardised descriptor
132 */
133 function standardiseDescriptor(descr) {
134 var4 = {};
135
136 var4[_methods] = descr[_methods] || _undefined;
137
138 var1 = descr[_properties];
139 var2 = descr.props;
140 var4[_properties] = isObject(var1 || var2) ? assign({}, var2, var1) : _undefined;
141
142 var4[_initializers] = extractUniqueFunctions(descr.init, descr[_initializers]);
143
144 var4[_composers] = extractUniqueFunctions(descr[_composers]);
145
146 var1 = descr[_deepProperties];
147 var2 = descr[_deepProps];
148 var4[_deepProperties] = isObject(var1 || var2) ? merge({}, var2, var1) : _undefined;
149
150 var4[_propertyDescriptors] = descr[_propertyDescriptors];
151
152 var1 = descr[_staticProperties];
153 var2 = descr.statics;
154 var4[_staticProperties] = isObject(var1 || var2) ? assign({}, var2, var1) : _undefined;
155
156 var1 = descr[_staticDeepProperties];
157 var2 = descr[_deepStatics];
158 var4[_staticDeepProperties] = isObject(var1 || var2) ? merge({}, var2, var1) : _undefined;
159
160 var4[_staticPropertyDescriptors] = descr[_staticPropertyDescriptors];
161
162 var1 = descr[_configuration];
163 var2 = descr.conf;
164 var4[_configuration] = isObject(var1 || var2) ? assign({}, var2, var1) : _undefined;
165
166 var1 = descr[_deepConfiguration];
167 var2 = descr[_deepConf];
168 var3 = isObject(var1 || var2) ? merge({}, var2, var1) : _undefined;
169
170 var4[_deepConfiguration] = var3;
171
172 return var4;
173 }
174
175 /**
176 * Creates new factory instance.
177 * @returns {Function} The new factory function.
178 */
179 function createFactory() {
180 return function Stamp(options) {
181 var i = Stamp[_compose] || {};
182 // Next line was optimized for most JS VMs. Please, be careful here!
183 var obj = {__proto__: i[_methods]};
184
185 var inits = i[_initializers], args = slice.apply(arguments);
186 var initializer, returnedValue;
187
188 var tmp = i[_deepProperties];
189 if (tmp) merge(obj, tmp);
190 tmp = i[_properties];
191 if (tmp) assign(obj, tmp);
192 tmp = i[_propertyDescriptors];
193 if (tmp) defineProperties(obj, tmp);
194
195 if (!inits || !inits[_length]) return obj;
196
197 if (options === _undefined) options = {};
198 for (i = 0; i < inits[_length];) {
199 initializer = inits[i++];
200 if (isFunction(initializer)) {
201 returnedValue = initializer.call(obj, options,
202 {instance: obj, stamp: Stamp, args: args});
203 obj = returnedValue === _undefined ? obj : returnedValue;
204 }
205 }
206
207 return obj;
208 };
209 }
210
211 /**
212 * Returns a new stamp given a descriptor and a compose function implementation.
213 * @param {Descriptor} [descriptor={}] The information about the object the stamp will be creating.
214 * @returns {Stamp} The new stamp
215 */
216 function createStamp(descriptor) {
217 var1 = createFactory();
218
219 var2 = descriptor[_staticDeepProperties];
220 if (var2) merge(var1, var2);
221
222 var2 = descriptor[_staticProperties];
223 if (var2) assign(var1, var2);
224
225 var2 = descriptor[_staticPropertyDescriptors];
226 if (var2) defineProperties(var1, var2);
227
228 var2 = isFunction(var1[_compose]) ? var1[_compose] : compose;
229 assign(var1[_compose] = function() {
230 return var2.apply(this, arguments);
231 }, descriptor);
232
233 return var1;
234 }
235
236 /**
237 * Mutates the dstDescriptor by merging the srcComposable data into it.
238 * @param {Descriptor} dstDescriptor The descriptor object to merge into.
239 * @param {Composable} [srcComposable] The composable
240 * (either descriptor or stamp) to merge data form.
241 * @returns {Descriptor} Returns the dstDescriptor argument.
242 */
243 function mergeComposable(dstDescriptor, srcComposable) {
244 function mergeAssign(propName, deep) {
245 if (!isObject(srcComposable[propName])) {
246 return;
247 }
248 if (!isObject(dstDescriptor[propName])) {
249 dstDescriptor[propName] = {};
250 }
251 (deep || assign)(dstDescriptor[propName], srcComposable[propName]);
252 }
253
254 function concatAssignFunctions(propName) {
255 var1 = extractUniqueFunctions(dstDescriptor[propName], srcComposable[propName]);
256 if (var1) dstDescriptor[propName] = var1;
257 }
258
259 if (srcComposable && isObject(srcComposable = srcComposable[_compose] || srcComposable)) {
260 mergeAssign(_methods);
261 mergeAssign(_properties);
262 mergeAssign(_deepProperties, merge);
263 mergeAssign(_propertyDescriptors);
264 mergeAssign(_staticProperties);
265 mergeAssign(_staticDeepProperties, merge);
266 mergeAssign(_staticPropertyDescriptors);
267 mergeAssign(_configuration);
268 mergeAssign(_deepConfiguration, merge);
269 concatAssignFunctions(_initializers);
270 concatAssignFunctions(_composers);
271 }
272
273 return dstDescriptor;
274 }
275
276 /**
277 * Given the list of composables (stamp descriptors and stamps) returns
278 * a new stamp (composable factory function).
279 * @typedef {Function} Compose
280 * Parameters: {...Composable} The list of composables.
281 * @returns {Stamp} A new stamp (aka composable factory function)
282 */
283 function compose() {
284 var descriptor = concat.apply([this], arguments)
285 .reduce(mergeComposable, {});
286 return createStamp(descriptor);
287 }
288
289
290 /**
291 * The Stamp Descriptor
292 * @typedef {Function|Object} Descriptor
293 * @returns {Stamp} A new stamp based on this Stamp
294 * @property {Object} [methods] Methods or other data used as object instances' prototype
295 * @property {Array<Function>} [initializers] List of initializers called for each object instance
296 * @property {Object} [properties] Shallow assigned properties of object instances
297 * @property {Object} [deepProperties] Deeply merged properties of object instances
298 * @property {Object} [staticProperties] Shallow assigned properties of Stamps
299 * @property {Object} [staticDeepProperties] Deeply merged properties of Stamps
300 * @property {Object} [configuration] Shallow assigned properties of Stamp arbitrary metadata
301 * @property {Object} [deepConfiguration] Deeply merged properties of Stamp arbitrary metadata
302 * @property {Object} [propertyDescriptors] ES5 Property Descriptors applied to object instances
303 * @property {Object} [staticPropertyDescriptors] ES5 Property Descriptors applied to Stamps
304 */
305
306 /**
307 * The Stamp factory function
308 * @typedef {Function} Stamp
309 * @returns {*} Instantiated object
310 * @property {Descriptor} compose - The Stamp descriptor and composition function
311 */
312
313 /**
314 * A composable object - stamp or descriptor
315 * @typedef {Stamp|Descriptor} Composable
316 */
317
318 /**
319 * Returns true if argument is a stamp.
320 * @param {*} obj Any object
321 * @returns {Boolean} True is the obj is a stamp
322 */
323 function isStamp(obj) {
324 return isFunction(obj) && isFunction(obj[_compose]);
325 }
326
327 var allUtilities = {};
328
329 allUtilities[_methods] = createUtilityFunction(_methods, assign);
330
331 allUtilities[_properties] = allUtilities.props =
332 createUtilityFunction(_properties, assign);
333
334 allUtilities[_initializers] = allUtilities.init =
335 createUtilityFunction(_initializers, extractUniqueFunctions);
336
337 allUtilities[_composers] = createUtilityFunction(_composers, extractUniqueFunctions);
338
339 allUtilities[_deepProperties] = allUtilities[_deepProps] =
340 createUtilityFunction(_deepProperties, merge);
341
342 allUtilities[_staticProperties] = allUtilities.statics =
343 createUtilityFunction(_staticProperties, assign);
344
345 allUtilities[_staticDeepProperties] = allUtilities[_deepStatics] =
346 createUtilityFunction(_staticDeepProperties, merge);
347
348 allUtilities[_configuration] = allUtilities.conf =
349 createUtilityFunction(_configuration, assign);
350
351 allUtilities[_deepConfiguration] = allUtilities[_deepConf] =
352 createUtilityFunction(_deepConfiguration, merge);
353
354 allUtilities[_propertyDescriptors] = createUtilityFunction(_propertyDescriptors, assign);
355
356 allUtilities[_staticPropertyDescriptors] = createUtilityFunction(_staticPropertyDescriptors, assign);
357
358 function createUtilityFunction(propName, action) {
359 return function() {
360 var4 = {};
361 var4[propName] = action.apply(_undefined, concat.apply([{}], arguments));
362 var1 = this;
363
364 return ((var1 && var1[_compose]) || var2).call(var1, var4);
365 };
366 }
367
368 /**
369 * Infected compose
370 * Parameters: {...Composable} The list of composables.
371 * @return {Stamp} The Stampit-flavoured stamp
372 */
373 var2 = allUtilities[_compose] = assign(function stampit() {
374 var i = 0, composable, composables = [], array = arguments, composerResult = this;
375 for (; i < array[_length];) {
376 composable = array[i++];
377 if (isObject(composable)) {
378 composables.push(isStamp(composable) ? composable : standardiseDescriptor(composable));
379 }
380 }
381
382 // Calling the standard pure compose function here.
383 composable = compose.apply(composerResult || baseStampit, composables);
384 if (composerResult) composables.unshift(composerResult);
385
386 array = composable[_compose][_composers];
387 if (isArray(array)) {
388 for (i = 0; i < array[_length];) {
389 composerResult = array[i++]({stamp: composable, composables: composables});
390 composable = isStamp(composerResult) ? composerResult : composable;
391 }
392 }
393
394 return composable;
395 }, allUtilities); // Setting up the shortcut functions
396
397 allUtilities[_create] = function() {
398 return this.apply(_undefined, arguments);
399 };
400
401 var4 = {};
402 var4[_staticProperties] = allUtilities;
403
404 /**
405 * Infected stamp. Used as a storage of the infection metadata
406 * @type {Function}
407 * @return {Stamp}
408 */
409 baseStampit = compose(var4);
410
411 var2[_compose] = var2.bind(); // bind to undefined
412 var2.version = 'VERSION';
413
414 if (typeof _undefined != typeof module) module.exports = var2; else self.stampit = var2;
415}();