Line | Hits | Source |
---|---|---|
1 | 1 | var arraySlice = Array.prototype.slice, |
2 | compare = require('./compare'), | |
3 | is = require('./is'), | |
4 | compare = compare.compare; | |
5 | ||
6 | 1 | function _createIteratorFn(fn, c) { |
7 | 136 | var config = c || {}; |
8 | ||
9 | 136 | if(is.isFunction(fn) && (config.type !== 'strict')) { |
10 | 50 | return c ? fn.bind(c) : fn; |
11 | } | |
12 | ||
13 | 86 | if(config.type === undefined) { |
14 | 41 | config.type = 'loose'; |
15 | } | |
16 | ||
17 | 86 | return function(value) { |
18 | 218 | return compare(fn, value, config); |
19 | }; | |
20 | } | |
21 | ||
22 | 1 | function _createIteratorNotFn(fn, config) { |
23 | 47 | var functionToNot = _createIteratorFn(fn, config); |
24 | ||
25 | 47 | return function() { |
26 | 96 | return !functionToNot.apply(this, arguments); |
27 | }; | |
28 | } | |
29 | ||
30 | ||
31 | /** | |
32 | * @class Luc.Array | |
33 | * Package for Array methods. <br> | |
34 | * | |
35 | * Keep in mind that Luc is optionally packaged with es5 shim so you can write es5 code in non es5 browsers. | |
36 | * It comes with your favorite {@link Array Array} methods such as Array.forEach, Array.filter, Array.some, Array.every Array.reduceRight .. | |
37 | * | |
38 | * Also don't forget about Luc.Array.each and Luc.Array.toArray, they are great utility methods | |
39 | * that are used all over the framework. | |
40 | * | |
41 | * All remove\* / find\* methods follow the same api. \*All functions will return an array of removed or found | |
42 | * items. The items will be added to the array in the order they are | |
43 | * found. \*First functions will return the first item and stop iterating after that, if none | |
44 | * is found false is returned. remove\* functions will directly change the passed in array. | |
45 | * \*Not functions only do the following actions if the comparison is not true. | |
46 | * All remove\* / find\* take the following api: array, objectToCompareOrIterator, compareConfigOrThisArg <br>for example: | |
47 | * | |
48 | //most common use case | |
49 | Luc.Array.findFirst([1,2,3, {}], {}); | |
50 | >Object {} | |
51 | ||
52 | //pass in optional config for a strict === comparison | |
53 | Luc.Array.findFirst([1,2,3,{}], {}, {type: 'strict'}); | |
54 | >false | |
55 | ||
56 | //pass in an iterator and thisArg | |
57 | Luc.Array.findFirst([1,2,3,{}], function(val, index, array){ | |
58 | return val === 3 || this.num === val; | |
59 | }, {num: 1}); | |
60 | >1 | |
61 | ||
62 | //you can see remove modifies the passed in array. | |
63 | var arr = [1,2,{a:1},1, {a:1}]; | |
64 | Luc.Array.removeFirst(arr, {a:1}) | |
65 | >{a:1} | |
66 | arr; | |
67 | >[1, 2, 1, {a:1}] | |
68 | Luc.Array.removeLast(arr, 1) | |
69 | >1 | |
70 | arr; | |
71 | >[1,2, {a:1}] | |
72 | ||
73 | ||
74 | Luc.Array.findAll([1,2,3, {a:1,b:2}], function() {return true;}) | |
75 | > [1,2,3, {a:1,b:2}] | |
76 | //show how not works with an iterator | |
77 | Luc.Array.findAllNot([1,2,3, {a:1,b:2}], function() {return true;}) | |
78 | >[] | |
79 | * | |
80 | * For commonly used find/remove functions check out Luc.ArrayFns for example a | |
81 | * "compact" like function | |
82 | * | |
83 | Luc.Array.findAllNotFalsy([false, '', undefined, 0, {}, []]) | |
84 | >[0, {}, []] | |
85 | * | |
86 | * Or remove all empty items | |
87 | * | |
88 | var arr = ['', 0 , [], {a:1}, true, {}, [1]] | |
89 | Luc.Array.removeAllEmpty(arr) | |
90 | >['', [], {}] | |
91 | arr | |
92 | >[0, {a:1}, true, [1]] | |
93 | */ | |
94 | ||
95 | /** | |
96 | * Turn the passed in item into an array if it | |
97 | * isn't one already, if the item is an array just return it. | |
98 | * It returns an empty array if item is null or undefined. | |
99 | * If it is just a single item return an array containing the item. | |
100 | * | |
101 | Luc.Array.toArray() | |
102 | >[] | |
103 | Luc.Array.toArray(null) | |
104 | >[] | |
105 | Luc.Array.toArray(1) | |
106 | >[1] | |
107 | Luc.Array.toArray([1,2]) | |
108 | >[1, 2] | |
109 | * | |
110 | * @param {Object} item item to turn into an array. | |
111 | * @return the array | |
112 | */ | |
113 | 1 | function toArray(item) { |
114 | 62 | if (Array.isArray(item)) { |
115 | 28 | return item; |
116 | } | |
117 | 34 | return (item === null || item === undefined) ? [] : [item]; |
118 | } | |
119 | ||
120 | /** | |
121 | * Return the last item of the array | |
122 | * @param {Array} arr | |
123 | * @return {Object} the item | |
124 | ||
125 | var myLongArrayNameForThingsThatIWantToKeepTrackOf = [1,2,3] | |
126 | ||
127 | Luc.Array.last(myLongArrayNameForThingsThatIWantToKeepTrackOf); | |
128 | vs. | |
129 | myLongArrayNameForThingsThatIWantToKeepTrackOf[myLongArrayNameForThingsThatIWantToKeepTrackOf.length -1] | |
130 | * | |
131 | */ | |
132 | 1 | function last(arr) { |
133 | 2 | return arr[arr.length -1]; |
134 | } | |
135 | ||
136 | /** | |
137 | * Flatten out an array of objects based of their value for the passed in key. | |
138 | * This also takes account for null/undefined values. | |
139 | * | |
140 | Luc.Array.pluck([undefined, {a:'1', b:2}, {b:3}, {b:4}], 'b') | |
141 | >[undefined, 2, 3, 4] | |
142 | * @param {Object[]} arr | |
143 | * @param {String} key | |
144 | * @return {Array} | |
145 | */ | |
146 | 1 | function pluck(arr, key) { |
147 | 1 | return arr.map(function(value) { |
148 | 3 | return value && value[key]; |
149 | }); | |
150 | } | |
151 | ||
152 | /** | |
153 | * Return the items in between the passed in index | |
154 | * and the end of the array. | |
155 | * | |
156 | Luc.Array.fromIndex([1,2,3,4,5], 1) | |
157 | >[2, 3, 4, 5] | |
158 | ||
159 | * @param {Array/arguments} arr | |
160 | * @param {Number} index | |
161 | * @return {Array} the new array. | |
162 | * | |
163 | */ | |
164 | 1 | function fromIndex(a, index) { |
165 | 15 | var arr = is.isArguments(a) ? arraySlice.call(a) : a; |
166 | 15 | return arraySlice.call(arr, index, arr.length); |
167 | } | |
168 | ||
169 | /** | |
170 | * Runs an Array.forEach after calling Luc.Array.toArray on the item. | |
171 | It is very useful for setting up flexible api's that can handle none one or many. | |
172 | ||
173 | Luc.Array.each(this.items, function(item) { | |
174 | this._addItem(item); | |
175 | }); | |
176 | ||
177 | vs. | |
178 | ||
179 | if(Array.isArray(this.items)){ | |
180 | this.items.forEach(function(item) { | |
181 | this._addItem(item); | |
182 | }) | |
183 | } | |
184 | else if(this.items !== undefined) { | |
185 | this._addItem(this.items); | |
186 | } | |
187 | ||
188 | * @param {Object} item | |
189 | * @param {Function} callback | |
190 | * @param {Object} thisArg | |
191 | * | |
192 | */ | |
193 | 1 | function each(item, fn, thisArg) { |
194 | 57 | var arr = toArray(item); |
195 | 57 | return arr.forEach.call(arr, fn, thisArg); |
196 | } | |
197 | ||
198 | /** | |
199 | * Insert or append the second array/arguments into the | |
200 | * first array/arguments. This method does not alter | |
201 | * the passed in array/arguments. | |
202 | * | |
203 | * @param {Array/arguments} firstArrayOrArgs | |
204 | * @param {Array/arguments} secondArrayOrArgs | |
205 | * @param {Number/true} indexOrAppend true to append | |
206 | * the second array to the end of the first one. If it is a number | |
207 | * insert the secondArray into the first one at the passed in index. | |
208 | * @return {Array} the newly created array. | |
209 | * | |
210 | Luc.Array.insert([0,4], [1,2,3], 1); | |
211 | >[0, 1, 2, 3, 4] | |
212 | Luc.Array.insert([0,4], [1,2,3], true); | |
213 | >[0, 4, 1, 2, 3] | |
214 | Luc.Array.insert([0,4], [1,2,3], 0); | |
215 | >[1, 2, 3, 0, 4] | |
216 | * | |
217 | */ | |
218 | 1 | function insert(firstArrayOrArgs, secondArrayOrArgs, indexOrAppend) { |
219 | 22 | var firstArray = arraySlice.call(firstArrayOrArgs), |
220 | secondArray = arraySlice.call(secondArrayOrArgs), | |
221 | spliceArgs; | |
222 | ||
223 | 22 | if(indexOrAppend === true) { |
224 | 8 | return firstArray.concat(secondArray); |
225 | } | |
226 | ||
227 | 14 | spliceArgs = [indexOrAppend, 0].concat(secondArray); |
228 | 14 | firstArray.splice.apply(firstArray, spliceArgs); |
229 | 14 | return firstArray; |
230 | } | |
231 | ||
232 | /** | |
233 | * Remove an item from the passed in arr | |
234 | * from the index. | |
235 | * @param {Array} arr | |
236 | * @param {Number} index | |
237 | * @return {Object} the item removed. | |
238 | * | |
239 | var arr = [1,2,3]; | |
240 | Luc.Array.removeAtIndex(arr, 1); | |
241 | >2 | |
242 | arr; | |
243 | >[1,3] | |
244 | ||
245 | */ | |
246 | 1 | function removeAtIndex(arr, index) { |
247 | 43 | var item = arr[index]; |
248 | 43 | arr.splice(index, 1); |
249 | 43 | return item; |
250 | } | |
251 | ||
252 | 1 | function _removeFirst(arr, fn) { |
253 | 29 | var removed = false; |
254 | ||
255 | 29 | arr.some(function(value, index) { |
256 | 44 | if (fn.apply(this, arguments)) { |
257 | 24 | removed = removeAtIndex(arr, index); |
258 | 24 | return true; |
259 | } | |
260 | }); | |
261 | ||
262 | 29 | return removed; |
263 | } | |
264 | ||
265 | /** | |
266 | * Remove the first item from the passed in array | |
267 | * that {@link Luc#compare matches} the passed in object. Instead of | |
268 | * comparing an object an iterator function can be | |
269 | * used. | |
270 | * | |
271 | {copyDoc#arrParams} | |
272 | {copyDoc#arrRemoveSingle} | |
273 | */ | |
274 | 1 | function removeFirst(arr, obj, config) { |
275 | 13 | var fn = _createIteratorFn(obj, config); |
276 | 13 | return _removeFirst(arr, fn); |
277 | } | |
278 | ||
279 | /** | |
280 | * Remove the first item from the passed in array | |
281 | * that does not {@link Luc#compare match} the passed in object. Instead of | |
282 | * comparing an object an iterator function can be | |
283 | * used. | |
284 | * | |
285 | {copyDoc#arrParams} | |
286 | {copyDoc#arrRemoveSingle} | |
287 | */ | |
288 | 1 | function removeFirstNot(arr, obj, config) { |
289 | 16 | var fn = _createIteratorNotFn(obj, config); |
290 | 16 | return _removeFirst(arr, fn); |
291 | } | |
292 | ||
293 | ||
294 | 1 | function _removeAll(arr, fn) { |
295 | 16 | var indexsToRemove = [], |
296 | removed = []; | |
297 | ||
298 | 16 | arr.forEach(function(value, index) { |
299 | 39 | if (fn.apply(this, arguments)) { |
300 | 19 | indexsToRemove.unshift(index); |
301 | 19 | removed.push(value); |
302 | } | |
303 | }); | |
304 | ||
305 | 16 | indexsToRemove.forEach(function(index){ |
306 | 19 | removeAtIndex(arr, index); |
307 | }); | |
308 | ||
309 | 16 | return removed; |
310 | } | |
311 | ||
312 | /** | |
313 | * Remove the all the items from the passed in array | |
314 | * that do not {@link Luc#compare match} the passed in object. Instead of | |
315 | * comparing an object an iterator function can be | |
316 | * used. | |
317 | * | |
318 | {copyDoc#arrParams} | |
319 | {copyDoc#arrRemoveAll} | |
320 | */ | |
321 | 1 | function removeAllNot(arr, obj, config) { |
322 | 7 | var fn = _createIteratorNotFn(obj, config); |
323 | 7 | return _removeAll(arr, fn); |
324 | } | |
325 | ||
326 | /** | |
327 | * Remove the all the items from the passed in array | |
328 | * that {@link Luc#compare matches} the passed in object. Instead of | |
329 | * comparing an object an iterator function can be | |
330 | * used. | |
331 | * | |
332 | {copyDoc#arrParams} | |
333 | {copyDoc#arrRemoveAll} | |
334 | */ | |
335 | 1 | function removeAll(arr, obj, config) { |
336 | 9 | var fn = _createIteratorFn(obj, config); |
337 | 9 | return _removeAll(arr, fn); |
338 | } | |
339 | ||
340 | 1 | function _findFirst(arr, fn) { |
341 | 61 | var item = false; |
342 | 61 | arr.some(function(value, index) { |
343 | 121 | if (fn.apply(this, arguments)) { |
344 | 39 | item = arr[index]; |
345 | 39 | return true; |
346 | } | |
347 | }); | |
348 | ||
349 | 61 | return item; |
350 | } | |
351 | ||
352 | /** | |
353 | * Find the first item from the passed in array | |
354 | * that does {@link Luc#compare matches} the passed in object. Instead of | |
355 | * comparing an object an iterator function can be | |
356 | * used. | |
357 | * | |
358 | {copyDoc#arrParams} | |
359 | {copyDoc#arrFindSingle} | |
360 | */ | |
361 | 1 | function findFirst(arr, obj, config) { |
362 | 48 | var fn = _createIteratorFn(obj, config); |
363 | 48 | return _findFirst(arr, fn); |
364 | } | |
365 | ||
366 | /** | |
367 | * Find the first item from the passed in array | |
368 | * that does not {@link Luc#compare match} the passed in object. Instead of | |
369 | * comparing an object an iterator function can be | |
370 | * used. | |
371 | * | |
372 | {copyDoc#arrParams} | |
373 | {copyDoc#arrFindSingle} | |
374 | */ | |
375 | 1 | function findFirstNot(arr, obj, config) { |
376 | 13 | var fn = _createIteratorNotFn(obj, config); |
377 | 13 | return _findFirst(arr, fn); |
378 | } | |
379 | ||
380 | 1 | function _findAll(arr, fn) { |
381 | 30 | return arr.filter(fn); |
382 | } | |
383 | ||
384 | /** | |
385 | * Find all of the the items from the passed in array | |
386 | * that {@link Luc#compare matches} the passed in object. Instead of | |
387 | * comparing an object an iterator function can be | |
388 | * used. | |
389 | * | |
390 | {copyDoc#arrParams} | |
391 | {copyDoc#arrFindAll} | |
392 | */ | |
393 | 1 | function findAll(arr, obj, config) { |
394 | 19 | var fn = _createIteratorFn(obj, config); |
395 | 19 | return _findAll(arr, fn); |
396 | } | |
397 | ||
398 | /** | |
399 | * Find all of the the items from the passed in array | |
400 | * that do not {@link Luc#compare match} the passed in object. Instead of | |
401 | * comparing an object an iterator function can be | |
402 | * used. | |
403 | * | |
404 | {copyDoc#arrParams} | |
405 | {copyDoc#arrFindAll} | |
406 | */ | |
407 | 1 | function findAllNot(arr, obj, config) { |
408 | 11 | var fn = _createIteratorNotFn(obj, config); |
409 | 11 | return _findAll(arr, fn); |
410 | } | |
411 | ||
412 | ||
413 | 1 | exports.toArray = toArray; |
414 | 1 | exports.each = each; |
415 | 1 | exports.insert = insert; |
416 | 1 | exports.fromIndex = fromIndex; |
417 | 1 | exports.last = last; |
418 | 1 | exports.pluck = pluck; |
419 | ||
420 | 1 | exports.removeAtIndex = removeAtIndex; |
421 | 1 | exports.findFirstNot = findFirstNot; |
422 | 1 | exports.findAllNot = findAllNot; |
423 | 1 | exports.findFirst = findFirst; |
424 | 1 | exports.findAll = findAll; |
425 | ||
426 | 1 | exports.removeFirstNot = removeFirstNot; |
427 | 1 | exports.removeAllNot = removeAllNot; |
428 | 1 | exports.removeFirst = removeFirst; |
429 | 1 | exports.removeAll = removeAll; |
430 | ||
431 | 1 | (function(){ |
432 | 1 | var _createLastFn = function(fnName) { |
433 | 4 | var lastName = fnName.replace('First', 'Last'); |
434 | ||
435 | 4 | exports[lastName] = function(arr, obj, config) { |
436 | 14 | var ret; |
437 | ||
438 | 14 | arr.reverse(); |
439 | 14 | ret = exports[fnName](arr, obj, config); |
440 | 14 | arr.reverse(); |
441 | ||
442 | 14 | return ret; |
443 | }; | |
444 | ||
445 | }, namesToAddLast = ['findFirstNot', 'findFirst', 'removeFirstNot', 'removeFirst']; | |
446 | ||
447 | 1 | namesToAddLast.forEach(function(fnName) { |
448 | 4 | _createLastFn(fnName); |
449 | }); | |
450 | ||
451 | }()); | |
452 | ||
453 | /** | |
454 | * @member Luc.Array | |
455 | * @method findLastNot | |
456 | * Same as Luc.Array.findFirstNot except start at the end. | |
457 | */ | |
458 | ||
459 | /** | |
460 | * @member Luc.Array | |
461 | * @method findLast | |
462 | * Same as Luc.Array.findFirst except start at the end. | |
463 | */ | |
464 | ||
465 | /** | |
466 | * @member Luc.Array | |
467 | * @method removeLastNot | |
468 | * Same as Luc.Array.removeFirstNot except start at the end. | |
469 | */ | |
470 | ||
471 | /** | |
472 | * @member Luc.Array | |
473 | * @method removeLast | |
474 | * Same as Luc.Array.removeFirst except start at the end. | |
475 | */ |
Line | Hits | Source |
---|---|---|
1 | 1 | var array = require('./array'), |
2 | is = require('./is'), | |
3 | Generator; | |
4 | ||
5 | 1 | Generator = { |
6 | arrayFnNames: ['findFirstNot', 'findAllNot', 'findFirst', 'findAll', | |
7 | 'removeFirstNot', 'removeAllNot', 'removeFirst', 'removeAll', | |
8 | 'removeLastNot', 'removeLast', 'findLast', 'findLastNot' | |
9 | ], | |
10 | ||
11 | createFn: function(arrayFnName, fn) { | |
12 | 132 | return function(arr) { |
13 | 36 | return array[arrayFnName](arr, fn); |
14 | }; | |
15 | }, | |
16 | ||
17 | createBoundFn: function(arrayFnName, fnToBind) { | |
18 | 24 | return function(arr, value) { |
19 | 11 | var fn = fnToBind.apply(this, array.fromIndex(arguments, 1)); |
20 | 11 | return array[arrayFnName](arr, fn); |
21 | }; | |
22 | } | |
23 | }; | |
24 | ||
25 | 1 | module.exports = Generator; |
26 | ||
27 | /** | |
28 | * @class Luc.ArrayFns | |
29 | * This is documented as a separate package but it actually exists under the | |
30 | * Luc.Array namespace. Check out the "Filter class members" input box | |
31 | * just to the right when searching for functions. | |
32 | *<br> | |
33 | * | |
34 | * There are a lot of functions in this package but all of them | |
35 | * follow the same api. \*All functions will return an array of removed or found | |
36 | * items. The items will be added to the array in the order they are | |
37 | * found. \*First functions will return the first item and stop iterating after that, if none | |
38 | * is found false is returned. remove\* functions will directly change the passed in array. | |
39 | * \*Not functions only do the following actions if the comparison is not true. | |
40 | * \*Last functions do the same as their \*First counterparts except that the iterating | |
41 | * starts at the end of the array. Almost every public method of Luc.is is available it | |
42 | * uses the following grammar Luc.Array["methodName""isMethodName"] | |
43 | * | |
44 | Luc.Array.findAllNotEmpty([false, true, null, undefined, 0, '', [], [1]]) | |
45 | > [true, 0, [1]] | |
46 | ||
47 | //Or remove all empty items | |
48 | var arr = ['', 0 , [], {a:1}, true, {}, [1]] | |
49 | Luc.Array.removeAllEmpty(arr) | |
50 | >['', [], {}] | |
51 | arr | |
52 | >[0, {a:1}, true, [1]] | |
53 | ||
54 | Luc.Array.findFirstNotString([1,2,3,'5']) | |
55 | >1 | |
56 | var arr = [1,2,3,'5']; | |
57 | Luc.Array.removeAllNotString(arr); | |
58 | >[1,2,3] | |
59 | arr | |
60 | >["5"] | |
61 | * | |
62 | * As of right now there are two function sets which differ from the is | |
63 | * api. | |
64 | * | |
65 | * InstanceOf | |
66 | * | |
67 | Luc.Array.findAllInstanceOf([1,2, new Date(), {}, []], Object) | |
68 | >[date, {}, []] | |
69 | >Luc.Array.findAllNotInstanceOf([1,2, new Date(), {}, []], Object) | |
70 | [1, 2] | |
71 | * | |
72 | * In | |
73 | * | |
74 | Luc.Array.findAllIn([1,2,3], [1,2]) | |
75 | >[1, 2] | |
76 | Luc.Array.findFirstIn([1,2,3], [1,2]) | |
77 | >1 | |
78 | ||
79 | //defaults to loose comparison | |
80 | Luc.Array.findAllIn([1,2,3, {a:1, b:2}], [1,{a:1}]) | |
81 | > [1, {a:1,b:2}] | |
82 | ||
83 | Luc.Array.findAllIn([1,2,3, {a:1, b:2}], [1,{a:1}], {type: 'deep'}) | |
84 | >[1] | |
85 | */ | |
86 | ||
87 | 1 | (function _createIsFns() { |
88 | 1 | var isToIgnore = ['isRegExp', 'isArguments']; |
89 | ||
90 | 1 | Object.keys(is).forEach(function(key) { |
91 | 10 | var name = key.split('is')[1]; |
92 | 10 | Generator.arrayFnNames.forEach(function(fnName) { |
93 | 120 | if(isToIgnore.indexOf(key) === -1) { |
94 | 96 | array[fnName + name] = Generator.createFn(fnName, is[key]); |
95 | } | |
96 | }); | |
97 | }); | |
98 | }()); | |
99 | ||
100 | 1 | (function _createFalsyFns() { |
101 | 1 | var usefullFalsyFns = ['findFirstNot', 'findAllNot', 'removeFirstNot', 'removeAllNot', |
102 | 'removeFirst', 'removeAll', 'removeLastNot', 'removeLast', 'findLastNot']; | |
103 | ||
104 | 1 | var fns = { |
105 | 'False': function(val) { | |
106 | 15 | return val === false; |
107 | }, | |
108 | 'True': function(val) { | |
109 | 15 | return val === true; |
110 | }, | |
111 | 'Null': function(val) { | |
112 | 15 | return val === null; |
113 | }, | |
114 | 'Undefined': function(val) { | |
115 | 15 | return val === undefined; |
116 | } | |
117 | }; | |
118 | ||
119 | 1 | Object.keys(fns).forEach(function(key) { |
120 | 4 | usefullFalsyFns.forEach(function(fnName) { |
121 | 36 | array[fnName + key] = Generator.createFn(fnName, fns[key]); |
122 | }); | |
123 | }); | |
124 | }()); | |
125 | ||
126 | 1 | (function _createBoundFns() { |
127 | 1 | var fns = { |
128 | 'InstanceOf': function(Constructor) { | |
129 | 2 | return function(value) { |
130 | 1 | return (value instanceof Constructor); |
131 | }; | |
132 | },'In': function(arr, c) { | |
133 | 9 | var defaultC = {type:'looseRight'}; |
134 | 9 | return function(value) { |
135 | 31 | if(value !== false) { |
136 | 30 | var cfg = c || defaultC; |
137 | //this is a right to left comparison | |
138 | //expected loose behavior should be looseRight | |
139 | 30 | return array.findFirst(arr, value, cfg.type === 'loose' ? defaultC : cfg) !== false; |
140 | } | |
141 | ||
142 | 1 | return arr.indexOf(false) > -1; |
143 | }; | |
144 | } | |
145 | }; | |
146 | ||
147 | 1 | Object.keys(fns).forEach(function(key) { |
148 | 2 | Generator.arrayFnNames.forEach(function(fnName) { |
149 | 24 | array[fnName + key] = Generator.createBoundFn(fnName, fns[key]); |
150 | }); | |
151 | }); | |
152 | }()); |
Line | Hits | Source |
---|---|---|
1 | 1 | var emptyFn = require('../function').emptyFn, |
2 | apply = require('../object').apply; | |
3 | ||
4 | /** | |
5 | * @class Luc.Base | |
6 | * Simple class that by default {@link Luc#apply applies} the | |
7 | * first argument to the instance and then calls | |
8 | * Luc.Base.init. | |
9 | * | |
10 | var b = new Luc.Base({ | |
11 | a: 1, | |
12 | init: function() { | |
13 | console.log('hey') | |
14 | } | |
15 | }) | |
16 | b.a | |
17 | >hey | |
18 | >1 | |
19 | * | |
20 | * We found that most of our classes do this so we made | |
21 | * it the default. Having a config object as the first and only | |
22 | * param keeps a clean api as well. | |
23 | * | |
24 | var C = Luc.define({ | |
25 | init: function() { | |
26 | Luc.Array.each(this.items, this.logItems) | |
27 | }, | |
28 | ||
29 | logItems: function(item) { | |
30 | console.log(item); | |
31 | } | |
32 | }); | |
33 | ||
34 | var c = new C({items: [1,2,3]}); | |
35 | >1 | |
36 | >2 | |
37 | >3 | |
38 | var d = new C({items: 'A'}); | |
39 | >'A' | |
40 | var e = new C(); | |
41 | * | |
42 | * If you don't like the applying of the config to the instance it | |
43 | * can always be "disabled" | |
44 | * | |
45 | var NoApply = Luc.define({ | |
46 | beforeInit: function() { | |
47 | ||
48 | }, | |
49 | init: function() { | |
50 | Luc.Array.each(this.items, this.logItems) | |
51 | }, | |
52 | ||
53 | logItems: function(item) { | |
54 | console.log(item); | |
55 | } | |
56 | }); | |
57 | ||
58 | var c = new NoApply({items: [1,2,3]}); | |
59 | * | |
60 | */ | |
61 | 1 | function Base() { |
62 | 18 | this.beforeInit.apply(this, arguments); |
63 | 18 | this.init(); |
64 | } | |
65 | ||
66 | 1 | Base.prototype = { |
67 | /** | |
68 | * By default apply the config to the | |
69 | * instance. | |
70 | */ | |
71 | beforeInit: function(config) { | |
72 | 18 | apply(this, config); |
73 | }, | |
74 | /** | |
75 | * @method | |
76 | * Simple hook to initialize | |
77 | * the class. Defaults to Luc.emptyFn | |
78 | */ | |
79 | init: emptyFn | |
80 | }; | |
81 | ||
82 | 1 | module.exports = Base; |
Line | Hits | Source |
---|---|---|
1 | 1 | var obj = require('../object'), |
2 | array = require('../array'), | |
3 | apply = obj.apply, | |
4 | mix = obj.mix, | |
5 | oFilter = obj.filter, | |
6 | emptyFn = ('../function').emptyFn, | |
7 | is = require('../is'); | |
8 | ||
9 | /** | |
10 | * @class Luc.Composition | |
11 | * @protected | |
12 | * Class that wraps {@link Luc.define#$compositions composition} config objects | |
13 | * to conform to an api. This class is not available externally. The config object | |
14 | * will override any protected methods and default configs. Defaults | |
15 | * can be used for often used configs, keys that are not defaults will | |
16 | * override the defaults. | |
17 | * | |
18 | var C = Luc.define({ | |
19 | $compositions: { | |
20 | defaults: Luc.compositionEnums.EventEmitter, | |
21 | methods: ['emit'] | |
22 | } | |
23 | }); | |
24 | ||
25 | var c = new C() | |
26 | typeof c.emit | |
27 | >"function" | |
28 | typeof c.on | |
29 | >"undefined" | |
30 | * | |
31 | * If you want to add your own composition all you need to have is | |
32 | * a name and a Constructor, the rest of the configs of this class and Luc.Composition.create | |
33 | * can be used to inject behavior if needed. | |
34 | * | |
35 | function Counter() { | |
36 | this.count = 0; | |
37 | }; | |
38 | ||
39 | Counter.prototype = { | |
40 | getCount: function() { | |
41 | return this.count; | |
42 | }, | |
43 | increaseCount: function() { | |
44 | this.count++; | |
45 | } | |
46 | } | |
47 | ||
48 | var C = Luc.define({ | |
49 | $compositions: { | |
50 | name: 'counter', | |
51 | Constructor: Counter, | |
52 | methods: 'allMethods' | |
53 | } | |
54 | }); | |
55 | ||
56 | var c = new C() | |
57 | ||
58 | c.increaseCount(); | |
59 | c.increaseCount(); | |
60 | c.increaseCount(); | |
61 | c.getCount(); | |
62 | >3 | |
63 | c.count | |
64 | >undefined | |
65 | */ | |
66 | 1 | function Composition(c) { |
67 | 27 | var defaults = c.defaults, |
68 | config = c; | |
69 | ||
70 | 27 | if(defaults) { |
71 | 4 | mix(config, config.defaults); |
72 | 4 | delete config.defaults; |
73 | } | |
74 | ||
75 | 27 | apply(this, config); |
76 | } | |
77 | ||
78 | 1 | Composition.prototype = { |
79 | /** | |
80 | * @cfg {String} name (required) the name which the composition | |
81 | * will be referred to by the instance. | |
82 | */ | |
83 | ||
84 | /** | |
85 | * @cfg {Object} defaults | |
86 | */ | |
87 | ||
88 | /** | |
89 | * @cfg {Boolean} initAfter defaults to false | |
90 | * pass in true to init the composition instance after the | |
91 | * superclass has been called. | |
92 | */ | |
93 | ||
94 | /** | |
95 | * @cfg {Function} Constructor (required) the Constructor | |
96 | * to use when creating the composition instance. This | |
97 | * is required if Luc.Composition.create is not overwritten by | |
98 | * the passed in composition config object. | |
99 | */ | |
100 | ||
101 | /** | |
102 | * @protected | |
103 | * By default just return a newly created Constructor instance. | |
104 | * | |
105 | * When create is called the following properties can be used : | |
106 | * | |
107 | * this.instance The instance that is creating | |
108 | * the composition. | |
109 | * | |
110 | * this.Constructor the constructor that is passed in from | |
111 | * the composition config. | |
112 | * | |
113 | * this.instanceArgs the arguments passed into the instance when it | |
114 | * is being created. For example | |
115 | ||
116 | new MyClassWithAComposition({plugins: []}) | |
117 | //inside of the create method | |
118 | this.instanceArgs | |
119 | >[{plugins: []}] | |
120 | ||
121 | * @return {Object} | |
122 | * the composition instance. | |
123 | * | |
124 | * For example set the emitters maxListeners | |
125 | * to what the instance has configed. | |
126 | ||
127 | maxListeners: 100, | |
128 | $compositions: { | |
129 | Constructor: Luc.EventEmitter, | |
130 | create: function() { | |
131 | var emitter = new this.Constructor(); | |
132 | emitter.setMaxListeners(this.instance.maxListeners); | |
133 | return emitter; | |
134 | }, | |
135 | name: 'emitter' | |
136 | } | |
137 | ||
138 | */ | |
139 | create: function() { | |
140 | 9 | return new this.Constructor(); |
141 | }, | |
142 | ||
143 | getInstance: function() { | |
144 | 12 | return this.create(); |
145 | }, | |
146 | ||
147 | validate: function() { | |
148 | 15 | if(this.name === undefined) { |
149 | 1 | throw new Error('A name must be defined'); |
150 | } | |
151 | 14 | if(!is.isFunction(this.Constructor) && this.create === Composition.prototype.create) { |
152 | 1 | throw new Error('The Constructor must be function if create is not overridden'); |
153 | } | |
154 | }, | |
155 | ||
156 | /** | |
157 | * @property filterMethodFns | |
158 | * @type {Object} | |
159 | * @property filterMethodFns.allMethods return all methods from the | |
160 | * constructors prototype | |
161 | * @property filterMethodFns.public return all methods that don't | |
162 | * start with _. We know not everyone follows this convention, but we | |
163 | * do and so do many others. | |
164 | * @type {Function} | |
165 | */ | |
166 | filterMethodFns: { | |
167 | allMethods: function(key, value) { | |
168 | 36 | return is.isFunction(value); |
169 | }, | |
170 | publicMethods: function(key, value) { | |
171 | 30 | return is.isFunction(value) && key.charAt(0) !== '_'; |
172 | } | |
173 | }, | |
174 | ||
175 | /** | |
176 | * @cfg {Function/String/Array[]} methods | |
177 | * The keys to add to the definers prototype that will in turn call | |
178 | * the compositions method. | |
179 | * | |
180 | * Defaults to Luc.emptyFn. | |
181 | * If an array is passed it will just use that Array. | |
182 | * | |
183 | * If a string is passed and matches a method from | |
184 | * Luc.Composition.filterMethodFns it will call that instead. | |
185 | * | |
186 | * If a function is defined it | |
187 | * will get called while iterating over each key value pair of the | |
188 | * Constructor's prototype, if a truthy value is | |
189 | * returned the property will be added to the defining | |
190 | * classes prototype. | |
191 | * | |
192 | * For example this config will only expose the emit method | |
193 | * to the defining class | |
194 | ||
195 | $compositions: { | |
196 | Constructor: Luc.EventEmitter, | |
197 | methods: function(key, value) { | |
198 | return key === 'emit'; | |
199 | }, | |
200 | name: 'emitter' | |
201 | } | |
202 | * this is also a valid config | |
203 | * | |
204 | $compositions: { | |
205 | Constructor: Luc.EventEmitter, | |
206 | methods: ['emitter'], | |
207 | name: 'emitter' | |
208 | } | |
209 | * | |
210 | */ | |
211 | methods: emptyFn, | |
212 | ||
213 | /** | |
214 | * @cfg {String[]/String} ignoreMethods methods that will always | |
215 | * be ignored if methods is not an Array. | |
216 | * | |
217 | ||
218 | var C = Luc.define({ | |
219 | $compositions: { | |
220 | defaults: Luc.compositionEnums.EventEmitter, | |
221 | methods: 'allMethods', | |
222 | ignoreMethods: ['emit'] | |
223 | } | |
224 | }); | |
225 | ||
226 | var c = new C(); | |
227 | typeof c.emit | |
228 | >"undefined" | |
229 | */ | |
230 | ignoreMethods: undefined, | |
231 | ||
232 | getObjectWithMethods: function() { | |
233 | 13 | var methodsObj = this.Constructor && this.Constructor.prototype; |
234 | 13 | if (this.ignoreMethods) { |
235 | 3 | methodsObj = apply({}, methodsObj); |
236 | 3 | array.each(this.ignoreMethods, function(value) { |
237 | 3 | delete methodsObj[value]; |
238 | }); | |
239 | } | |
240 | ||
241 | 13 | return methodsObj; |
242 | }, | |
243 | ||
244 | getMethodsToCompose: function() { | |
245 | 13 | var methods = this.methods, |
246 | filterFn; | |
247 | ||
248 | 13 | if (is.isArray(methods)) { |
249 | 0 | return methods; |
250 | } | |
251 | ||
252 | 13 | filterFn = methods; |
253 | ||
254 | 13 | if (is.isString(methods)) { |
255 | 7 | filterFn = this.filterMethodFns[methods]; |
256 | } | |
257 | ||
258 | //Constructors are not needed if create is overwritten | |
259 | 13 | return oFilter(this.getObjectWithMethods(), filterFn, this, { |
260 | ownProperties: false, | |
261 | keys: true | |
262 | }); | |
263 | } | |
264 | }; | |
265 | ||
266 | 1 | module.exports = Composition; |
Line | Hits | Source |
---|---|---|
1 | 1 | var EventEmitter = require('../events/eventEmitter'), |
2 | PluginManager = require('./pluginManager'); | |
3 | ||
4 | /** | |
5 | * @class Luc.compositionEnums | |
6 | * Composition enums are just common config objects for Luc.Composition. | |
7 | * Here is an example of a composition that uses EventEmitter but only | |
8 | * puts the emit method on the prototype. | |
9 | * | |
10 | var C = Luc.define({ | |
11 | $compositions: { | |
12 | defaults: Luc.compositionEnums.EventEmitter, | |
13 | methods: ['emit'] | |
14 | } | |
15 | }); | |
16 | ||
17 | var c = new C(); | |
18 | ||
19 | typeof c.emit | |
20 | >"function" | |
21 | typeof c.on | |
22 | "undefined" | |
23 | * | |
24 | */ | |
25 | ||
26 | /** | |
27 | * @property {Object} EventEmitter | |
28 | */ | |
29 | 1 | module.exports.EventEmitter = { |
30 | Constructor: EventEmitter, | |
31 | name: 'emitter', | |
32 | methods: 'allMethods' | |
33 | }; | |
34 | ||
35 | ||
36 | /** | |
37 | * @property {Object} PluginManager | |
38 | */ | |
39 | 1 | module.exports.PluginManager = { |
40 | name: 'plugins', | |
41 | initAfter: true, | |
42 | Constructor: PluginManager, | |
43 | create: function() { | |
44 | 3 | return new this.Constructor({ |
45 | instance: this.instance, | |
46 | instanceArgs: this.instanceArgs | |
47 | }); | |
48 | }, | |
49 | ignoreMethods: 'defaultPlugin', | |
50 | methods: 'publicMethods' | |
51 | }; |
Line | Hits | Source |
---|---|---|
1 | 1 | var Base = require('./base'), |
2 | Composition = require('./composition'), | |
3 | obj = require('../object'), | |
4 | arrayFns = require('../array'), | |
5 | emptyFn = require('../function').emptyFn, | |
6 | is = require('../is'), | |
7 | aEach = arrayFns.each, | |
8 | apply = obj.apply, | |
9 | oEach = obj.each, | |
10 | oFilter = obj.filter, | |
11 | mix = obj.mix, | |
12 | arraySlice = Array.prototype.slice, | |
13 | ClassDefiner; | |
14 | ||
15 | /** | |
16 | * @class Luc.ClassDefiner | |
17 | * @singleton | |
18 | * | |
19 | * Singleton that {@link Luc.define#define Luc.define} uses to define classes. The defualt type can | |
20 | * be changed to any Constructor | |
21 | * | |
22 | function MyClass(){}; | |
23 | Luc.ClassDefiner.defaultType = MyClass; | |
24 | var C = Luc.define(); | |
25 | new C() instanceof Luc.Base | |
26 | >false | |
27 | new C() instanceof MyClass | |
28 | >true | |
29 | */ | |
30 | ||
31 | /** | |
32 | * @cfg {Function} defaultType this can be changed to any Constructor. Defaults | |
33 | * to Luc.Base. | |
34 | */ | |
35 | ||
36 | 1 | ClassDefiner = { |
37 | ||
38 | COMPOSITIONS_NAME: '$compositions', | |
39 | ||
40 | defaultType: Base, | |
41 | ||
42 | processorKeys: { | |
43 | $mixins: '_applyMixins', | |
44 | $statics: '_applyStatics', | |
45 | $compositions: '_applyComposerMethods', | |
46 | $super: '_applySuper' | |
47 | }, | |
48 | ||
49 | define: function(opts, after) { | |
50 | 28 | var options = opts || {}, |
51 | //if super is a falsy value besides undefined that means no superclass | |
52 | Super = options.$super || (options.$super === undefined ? this.defaultType : false), | |
53 | afterDefine = after || emptyFn, | |
54 | Constructor; | |
55 | ||
56 | 28 | options.$super = Super; |
57 | ||
58 | 28 | Constructor = this._createConstructor(options); |
59 | ||
60 | 28 | this._processAfterCreate(Constructor, options); |
61 | ||
62 | 26 | afterDefine.call(Constructor, Constructor); |
63 | ||
64 | 26 | return Constructor; |
65 | }, | |
66 | ||
67 | _createConstructor: function(options) { | |
68 | 28 | var superclass = options.$super, |
69 | Constructor = this._createConstructorFn(options); | |
70 | ||
71 | 28 | if(superclass) { |
72 | 26 | Constructor.prototype = Object.create(superclass.prototype); |
73 | } | |
74 | ||
75 | 28 | return Constructor; |
76 | }, | |
77 | ||
78 | _createConstructorFn: function(options) { | |
79 | 28 | var superclass = options.$super, |
80 | Constructor; | |
81 | ||
82 | 28 | if (this._hasConstructorModifyingOptions(options)) { |
83 | 11 | Constructor = this._createConstructorFromOptions(options); |
84 | } | |
85 | 17 | else if(!superclass) { |
86 | 1 | Constructor = function() {}; |
87 | } | |
88 | else { | |
89 | 16 | Constructor = function() { |
90 | 22 | superclass.apply(this, arguments); |
91 | }; | |
92 | } | |
93 | ||
94 | 28 | return Constructor; |
95 | }, | |
96 | ||
97 | _hasConstructorModifyingOptions: function(options) { | |
98 | 28 | return options.$compositions; |
99 | }, | |
100 | ||
101 | _createConstructorFromOptions: function(options) { | |
102 | 11 | var superclass = options.$super, |
103 | me = this, | |
104 | initBeforeSuperclass, | |
105 | initAfterSuperclass, | |
106 | init; | |
107 | ||
108 | 11 | if (!superclass) { |
109 | 1 | init = this._createInitClassFn(options, { |
110 | all: true | |
111 | }); | |
112 | ||
113 | 1 | return function() { |
114 | 1 | var args = arraySlice.call(arguments); |
115 | 1 | init.call(this, options, args); |
116 | }; | |
117 | } | |
118 | ||
119 | 10 | initBeforeSuperclass = this._createInitClassFn(options, { |
120 | before: true | |
121 | }); | |
122 | ||
123 | 10 | initAfterSuperclass = this._createInitClassFn(options, { |
124 | before: false | |
125 | }); | |
126 | ||
127 | 10 | return function() { |
128 | 7 | var args = arraySlice.call(arguments); |
129 | ||
130 | 7 | initBeforeSuperclass.call(this, options, args); |
131 | 7 | superclass.apply(this, arguments); |
132 | 7 | initAfterSuperclass.call(this, options, args); |
133 | }; | |
134 | }, | |
135 | ||
136 | _createInitClassFn: function(options, config) { | |
137 | 21 | var me = this, |
138 | compositions = this._filterCompositions(config, options.$compositions); | |
139 | ||
140 | 21 | if(compositions.length === 0) { |
141 | 9 | return emptyFn; |
142 | } | |
143 | ||
144 | 12 | return function(options, instanceArgs) { |
145 | 9 | me._initCompositions.call(this, compositions, instanceArgs); |
146 | }; | |
147 | }, | |
148 | ||
149 | _filterCompositions: function(config, compositions) { | |
150 | 21 | var before = config.before, |
151 | filtered = []; | |
152 | ||
153 | 21 | if(config.all) { |
154 | 1 | return compositions; |
155 | } | |
156 | ||
157 | 20 | aEach(compositions, function(composition) { |
158 | 28 | if(before && composition.initAfter !== true || (!before && composition.initAfter === true)) { |
159 | 14 | filtered.push(composition); |
160 | } | |
161 | }); | |
162 | ||
163 | 20 | return filtered; |
164 | }, | |
165 | ||
166 | _processAfterCreate: function($class, options) { | |
167 | 28 | this._applyValuesToProto($class, options); |
168 | 28 | this._handlePostProcessors($class, options); |
169 | }, | |
170 | ||
171 | _applyValuesToProto: function($class, options) { | |
172 | 28 | var proto = $class.prototype, |
173 | values = apply({ | |
174 | $class: $class | |
175 | }, options); | |
176 | ||
177 | //Don't put the define specific properties | |
178 | //on the prototype | |
179 | 28 | oEach(values, function(key, value) { |
180 | 92 | if (!this._getProcessorKey(key)) { |
181 | 38 | proto[key] = value; |
182 | } | |
183 | }, this); | |
184 | }, | |
185 | ||
186 | _getProcessorKey: function(key) { | |
187 | 154 | return this.processorKeys[key]; |
188 | }, | |
189 | ||
190 | _handlePostProcessors: function($class, options) { | |
191 | 28 | oEach(options, function(key, value) { |
192 | 62 | var method = this._getProcessorKey(key); |
193 | ||
194 | 62 | if (is.isFunction(this[method])) { |
195 | 52 | this[method].call(this, $class, options[key]); |
196 | } | |
197 | }, this); | |
198 | }, | |
199 | ||
200 | _applyMixins: function($class, mixins) { | |
201 | 6 | var proto = $class.prototype; |
202 | 6 | aEach(mixins, function(mixin) { |
203 | //accept Constructors or Objects | |
204 | 7 | var toMix = mixin.prototype || mixin; |
205 | 7 | mix(proto, toMix); |
206 | }); | |
207 | }, | |
208 | ||
209 | _applyStatics: function($class, statics) { | |
210 | 9 | var prototype = $class.prototype; |
211 | ||
212 | 9 | apply($class, statics); |
213 | ||
214 | 9 | if(prototype.getStaticValue === undefined) { |
215 | 7 | prototype.getStaticValue = this.getStaticValue; |
216 | } | |
217 | }, | |
218 | ||
219 | _applyComposerMethods: function($class, compositions) { | |
220 | 11 | var prototype = $class.prototype, |
221 | methodsToCompose; | |
222 | ||
223 | 11 | aEach(compositions, function(compositionConfig) { |
224 | 15 | var composition = new Composition(compositionConfig), |
225 | name = composition.name, | |
226 | Constructor = composition.Constructor; | |
227 | ||
228 | 15 | composition.validate(); |
229 | ||
230 | 13 | methodsToCompose = composition.getMethodsToCompose(); |
231 | ||
232 | 13 | methodsToCompose.forEach(function(key) { |
233 | 47 | if (prototype[key] === undefined) { |
234 | 46 | prototype[key] = this._createComposerProtoFn(key, name); |
235 | } | |
236 | }, this); | |
237 | ||
238 | 13 | if(prototype.getComposition === undefined) { |
239 | 9 | prototype.getComposition = this.getComposition; |
240 | } | |
241 | ||
242 | }, this); | |
243 | }, | |
244 | ||
245 | _applySuper: function($class, $super) { | |
246 | 26 | var proto, |
247 | superObj; | |
248 | ||
249 | //super can be falsy to signify no superclass | |
250 | 26 | if ($super) { |
251 | 24 | superObj = { |
252 | $super: $super, | |
253 | $superclass: $super.prototype | |
254 | }; | |
255 | ||
256 | 24 | proto = $class.prototype; |
257 | ||
258 | 24 | apply(proto, superObj); |
259 | 24 | apply($class, superObj); |
260 | ||
261 | 24 | this._addSuperMethod(proto); |
262 | } | |
263 | }, | |
264 | ||
265 | _addSuperMethod: function(proto) { | |
266 | ||
267 | 24 | function getSuperMethod(callee, sp) { |
268 | 17 | var $super = sp || proto, |
269 | key; | |
270 | ||
271 | 17 | for (key in $super) { |
272 | 74 | if ($super[key] === callee) { |
273 | ||
274 | 10 | return $super.$superclass[key]; |
275 | ||
276 | //we could be caching this here on the fn | |
277 | //but then devs would have to know the edge cases | |
278 | //of how to invalidate it | |
279 | } | |
280 | } | |
281 | ||
282 | 7 | return getSuperMethod(callee, $super.$superclass); |
283 | } | |
284 | ||
285 | 24 | function callSuper(args) { |
286 | 10 | var superMethod = getSuperMethod(callSuper.caller); |
287 | ||
288 | 10 | if(superMethod) { |
289 | 9 | return superMethod.apply(this, args); |
290 | } | |
291 | ||
292 | 1 | throw new Error('super method not found.'); |
293 | ||
294 | } | |
295 | ||
296 | 24 | proto.callSuper = callSuper; |
297 | }, | |
298 | ||
299 | _createComposerProtoFn: function(methodName, compositionName) { | |
300 | 46 | return function() { |
301 | 26 | var comp = this[ClassDefiner.COMPOSITIONS_NAME][compositionName]; |
302 | 26 | return comp[methodName].apply(comp, arguments); |
303 | }; | |
304 | }, | |
305 | ||
306 | /** | |
307 | * @private | |
308 | * @ignore | |
309 | * options {Object} the composition config object | |
310 | * instanceArgs {Array} the arguments passed to the instance's | |
311 | * constructor. | |
312 | */ | |
313 | _initCompositions: function(compositions, instanceArgs) { | |
314 | 9 | if(!this[ClassDefiner.COMPOSITIONS_NAME]) { |
315 | 8 | this[ClassDefiner.COMPOSITIONS_NAME] = {}; |
316 | } | |
317 | ||
318 | 9 | aEach(compositions, function(compositionConfig) { |
319 | 12 | var config = apply({ |
320 | instance: this, | |
321 | instanceArgs: instanceArgs | |
322 | }, compositionConfig), | |
323 | composition; | |
324 | ||
325 | 12 | composition = new Composition(config); |
326 | ||
327 | 12 | this[ClassDefiner.COMPOSITIONS_NAME][composition.name] = composition.getInstance(); |
328 | }, this); | |
329 | }, | |
330 | ||
331 | //Methods that can get added to the prototype | |
332 | //they will be called in the context of the instance. | |
333 | // | |
334 | getComposition: function(key) { | |
335 | 5 | return this[ClassDefiner.COMPOSITIONS_NAME][key]; |
336 | }, | |
337 | ||
338 | getStaticValue: function (key, $class) { | |
339 | 19 | var classToFindValue = $class || this.$class, |
340 | $super, | |
341 | value; | |
342 | ||
343 | 19 | value = classToFindValue[key]; |
344 | ||
345 | 19 | if(value === undefined) { |
346 | 12 | $super = classToFindValue.prototype.$super; |
347 | 12 | if($super) { |
348 | 9 | return this.getStaticValue(key, $super); |
349 | } | |
350 | } | |
351 | ||
352 | 10 | return value; |
353 | } | |
354 | ||
355 | }; | |
356 | ||
357 | 1 | ClassDefiner.define = ClassDefiner.define.bind(ClassDefiner); |
358 | ||
359 | 1 | module.exports = ClassDefiner; |
360 | ||
361 | /** | |
362 | * @class Luc.define | |
363 | * This is actually a function but has a decent amount of important options | |
364 | * so we are documenting it like it is a class. Properties are things that will get | |
365 | * applied to instances of classes defined with {@link Luc.define#define define}. None | |
366 | * are needed for {@link Luc.define#define defining} a class. {@link Luc.define#define define} | |
367 | * just takes the passed in config and puts the properties on the prototype and returns | |
368 | * a Constructor. | |
369 | * | |
370 | ||
371 | var C = Luc.define({ | |
372 | a: 1, | |
373 | doLog: true, | |
374 | logA: function() { | |
375 | if (this.doLog) { | |
376 | console.log(this.a); | |
377 | } | |
378 | } | |
379 | }); | |
380 | var c = new C(); | |
381 | c.logA(); | |
382 | >1 | |
383 | c.a = 45; | |
384 | c.logA(); | |
385 | >45 | |
386 | c.doLog = false; | |
387 | c.logA(); | |
388 | ||
389 | new C().logA() | |
390 | >1 | |
391 | ||
392 | * | |
393 | * Check out the following configs to add functionality to a class without messing | |
394 | * up the inheritance chain. All the configs have examples and documentation on | |
395 | * how to use them. | |
396 | * | |
397 | * {@link Luc.define#$super super} <br> | |
398 | * {@link Luc.define#$compositions compositions} <br> | |
399 | * {@link Luc.define#$mixins mixins} <br> | |
400 | * {@link Luc.define#$statics statics} <br> | |
401 | * | |
402 | * | |
403 | */ | |
404 | ||
405 | /** | |
406 | * @method define | |
407 | * @param {Object} config config object used when creating the class. Any property that | |
408 | * is not apart of the special configs will be applied to the prototype. Check out | |
409 | * Luc.define for all the config options. No configs are needed to define a class. | |
410 | * | |
411 | * @param {Function} afterDefine (optional) function to run after the Constructor has been created. | |
412 | * The first an only argument is the newly created Constructor. | |
413 | * | |
414 | * @return {Function} the defined class | |
415 | * | |
416 | var C = Luc.define({ | |
417 | logA: function() { | |
418 | console.log(this.a) | |
419 | }, | |
420 | a: 1 | |
421 | }); | |
422 | var c = new C(); | |
423 | c.logA(); | |
424 | >1 | |
425 | ||
426 | c.a = 4; | |
427 | c.logA(); | |
428 | >4 | |
429 | * | |
430 | * | |
431 | */ | |
432 | ||
433 | /** | |
434 | * @property {Function} $class reference to the instance's own constructor. This | |
435 | * will get added to any class that is defined with Luc.define. | |
436 | * | |
437 | var C = Luc.define() | |
438 | var c = new C() | |
439 | c.$class === C | |
440 | >true | |
441 | * | |
442 | * There are some really good use cases to have a reference to it's | |
443 | * own constructor. <br> Add functionality to an instance in a simple | |
444 | * and generic way: | |
445 | * | |
446 | var C = Luc.define({ | |
447 | add: function(a,b) { | |
448 | return a + b; | |
449 | } | |
450 | }); | |
451 | ||
452 | //Luc.Base applies first | |
453 | //arg to the instance | |
454 | ||
455 | var c = new C({ | |
456 | add: function(a,b,c) { | |
457 | return this.$class.prototype.add.call(this, a,b) + c; | |
458 | } | |
459 | }); | |
460 | ||
461 | c.add(1,2,3) | |
462 | >6 | |
463 | new C().add(1,2,3) | |
464 | >3 | |
465 | * | |
466 | * Or have a simple generic clone method : | |
467 | * | |
468 | var C = Luc.define({ | |
469 | clone: function() { | |
470 | var myOwnProps = {}; | |
471 | Luc.Object.each(this, function(key, value) { | |
472 | myOwnProps[key] = value; | |
473 | }); | |
474 | ||
475 | return new this.$class(myOwnProps); | |
476 | } | |
477 | }); | |
478 | ||
479 | var c = new C({a:1,b:2,c:3}); | |
480 | c.d = 4; | |
481 | var clone = c.clone(); | |
482 | ||
483 | clone === c | |
484 | >false | |
485 | ||
486 | clone.a | |
487 | >1 | |
488 | clone.b | |
489 | >2 | |
490 | clone.c | |
491 | >3 | |
492 | clone.d | |
493 | >4 | |
494 | */ | |
495 | ||
496 | /** | |
497 | * @property {Function} [$super] If $super is not false or null | |
498 | * the $super property will be added to every instance of the defined class, | |
499 | * $super is the Constructor passed in with the $super config or the {@link Luc.ClassDefiner#defaultType default} | |
500 | * | |
501 | var C = Luc.define() | |
502 | var c = new C() | |
503 | //Luc.Base is the default | |
504 | c.$super === Luc.Base | |
505 | >true | |
506 | */ | |
507 | ||
508 | /** | |
509 | * @property {Function} [callSuper] If $super is defined it | |
510 | * will be on the prototype of $super. It can be used to call a super's | |
511 | * method. This can be used instead of the class's static $superclass reference. | |
512 | * Check out {@link Luc.define#callSuper callSuper} for more extensive documentation. | |
513 | * | |
514 | * | |
515 | function MyCoolClass() {} | |
516 | MyCoolClass.prototype.addNums = function(a,b) { | |
517 | return a + b; | |
518 | } | |
519 | ||
520 | var MyOtherCoolClass = Luc.define({ | |
521 | $super: MyCoolClass, | |
522 | addNums: function(a, b, c) { | |
523 | return this.callSuper([a, b]) + c; | |
524 | } | |
525 | }) | |
526 | ||
527 | var m = new MyOtherCoolClass(); | |
528 | m.addNums(1,2,3); | |
529 | >6 | |
530 | */ | |
531 | ||
532 | /** | |
533 | * @method callSuper If $super is defined it | |
534 | * will be on the prototype of $super. It can be used to call a super's | |
535 | * method. | |
536 | * | |
537 | * @param {Array/Arguments} args(optional) The arguments for the super methods apply call. | |
538 | * | |
539 | * | |
540 | function MyCoolClass() {} | |
541 | MyCoolClass.prototype.addNums = function(a,b) { | |
542 | return a + b; | |
543 | } | |
544 | ||
545 | var MC = Luc.define({ | |
546 | $super: MyCoolClass, | |
547 | addNums: function(a, b, c) { | |
548 | return this.callSuper([a, b]) + c; | |
549 | } | |
550 | }); | |
551 | ||
552 | * | |
553 | * produces the same code as : | |
554 | * | |
555 | var MC = Luc.define({ | |
556 | $super: MyCoolClass, | |
557 | addNums: function(a, b, c) { | |
558 | return MC.$superclass.addNums.apply(this, [a, b]) + c; | |
559 | } | |
560 | }); | |
561 | ||
562 | function Counter() { | |
563 | this.count = 0; | |
564 | }; | |
565 | ||
566 | Counter.prototype = { | |
567 | getCount: function() { | |
568 | return this.count; | |
569 | }, | |
570 | increaseCount: function() { | |
571 | this.count++; | |
572 | } | |
573 | } | |
574 | ||
575 | var C = Luc.define({ | |
576 | $super:Counter, | |
577 | increaseCount: function () { | |
578 | this.count += 2; | |
579 | this.callSuper(); | |
580 | } | |
581 | }); | |
582 | ||
583 | * | |
584 | * is the same as | |
585 | * | |
586 | ||
587 | var C = Luc.define({ | |
588 | $super:Counter, | |
589 | increaseCount: function () { | |
590 | this.count += 2; | |
591 | C.$superclass.increaseCount.call(this); | |
592 | } | |
593 | }); | |
594 | ||
595 | * | |
596 | * Caveats <br> | |
597 | * | |
598 | * callSuper can not be used as an instance method or inside of method | |
599 | * that is overwritten for a particular instance. | |
600 | * | |
601 | var c = new C(); | |
602 | //this will throw an error with the message of method not found. | |
603 | c.callSuper() | |
604 | * | |
605 | * What callSuper makes up for in terseness it loses it in | |
606 | * efficiency. | |
607 | * | |
608 | this.count += 2; | |
609 | C.$superclass.increaseCount | |
610 | ||
611 | * | |
612 | * is much faster and more efficient that : | |
613 | * | |
614 | this.count += 2; | |
615 | this.callSuper(); | |
616 | ||
617 | ||
618 | */ | |
619 | ||
620 | ||
621 | /** | |
622 | * @property {Function} getStaticValue this method | |
623 | * will be added to instances that use the {@link Luc.define#$statics $statics} | |
624 | * config. | |
625 | * | |
626 | * | |
627 | * This should be used over this.$class.staticName to | |
628 | * get the value of static. If the class gets inherited | |
629 | * from, this.$class will not be the same. getStatic value | |
630 | * deals with this issue. | |
631 | * | |
632 | var A = Luc.define({ | |
633 | $statics: { | |
634 | a: 1 | |
635 | }, | |
636 | getABetter: function() { | |
637 | return this.getStaticValue('a'); | |
638 | }, | |
639 | getA: function() { | |
640 | return this.$class.a; | |
641 | } | |
642 | }); | |
643 | ||
644 | var B = Luc.define({ | |
645 | $super: A, | |
646 | $statics: { | |
647 | b: 2, | |
648 | c: 3 | |
649 | } | |
650 | }); | |
651 | ||
652 | ||
653 | var b = new B(); | |
654 | b.getA(); | |
655 | >undefined | |
656 | b.getABetter(); | |
657 | >1 | |
658 | ||
659 | * @return {Object} the static value of the key | |
660 | */ | |
661 | ||
662 | ||
663 | /** | |
664 | * @property {Function} getComposition this method will be added | |
665 | * to instances that use the {@link Luc.define#$compositions $compositions} config | |
666 | * | |
667 | * This will return the composition instance based off the composition {@link Luc.Composition#name name} | |
668 | ||
669 | this.getComposition("name"); | |
670 | ||
671 | * | |
672 | */ | |
673 | ||
674 | ||
675 | /** | |
676 | * @cfg {Object} $statics (optional) Add static properties or methods | |
677 | * to the class. These properties/methods will not be able to be | |
678 | * directly modified by the instance so they are good for defining default | |
679 | * configs. Using this config adds the {@link Luc.define#getStaticValue getStaticValue} | |
680 | * method to instances. | |
681 | * | |
682 | var C = Luc.define({ | |
683 | $statics: { | |
684 | number: 1 | |
685 | } | |
686 | }); | |
687 | ||
688 | var c = new C(); | |
689 | c.number | |
690 | >undefined | |
691 | C.number | |
692 | >1 | |
693 | ||
694 | * | |
695 | * Bad things can happen if non primitives are placed on the | |
696 | * prototype and instance sharing is not wanted. Using statics | |
697 | * prevent subclasses and instances from unknowingly modifying | |
698 | * all instances. | |
699 | * | |
700 | var C = Luc.define({ | |
701 | cfg: { | |
702 | a: 1 | |
703 | } | |
704 | }); | |
705 | ||
706 | var c = new C(); | |
707 | c.cfg.a | |
708 | >1 | |
709 | c.cfg.a = 5 | |
710 | new C().cfg.a | |
711 | >5 | |
712 | * | |
713 | */ | |
714 | ||
715 | /** | |
716 | * @cfg {Object/Constructor/Object[]/Constructor[]} $mixins (optional) Mixins are a way to add functionality | |
717 | * to a class that should not add state to the instance unknowingly. Mixins can be either objects or Constructors. | |
718 | * | |
719 | function Logger() {} | |
720 | Logger.prototype.log = function() { | |
721 | console.log(arguments) | |
722 | } | |
723 | ||
724 | var C = Luc.define({ | |
725 | $mixins: [Logger, { | |
726 | warn: function() { | |
727 | console.warn(arguments) | |
728 | } | |
729 | }] | |
730 | }); | |
731 | ||
732 | var c = new C(); | |
733 | ||
734 | c.log(1,2) | |
735 | >[1,2] | |
736 | ||
737 | c.warn(3,4) | |
738 | >[3,4] | |
739 | * | |
740 | */ | |
741 | /** | |
742 | * @cfg {Constructor} $super (optional) super for the defining class. By Luc.Base | |
743 | * is the default if super is not passed in. To define a class without a superclass | |
744 | * you can pass in false or null. | |
745 | * | |
746 | function Counter() { | |
747 | this.count = 0; | |
748 | }; | |
749 | ||
750 | Counter.prototype = { | |
751 | getCount: function() { | |
752 | return this.count; | |
753 | }, | |
754 | increaseCount: function() { | |
755 | this.count++; | |
756 | } | |
757 | } | |
758 | ||
759 | var C = Luc.define({ | |
760 | $super:Counter | |
761 | }); | |
762 | ||
763 | var c = new C() | |
764 | ||
765 | c instanceof Counter | |
766 | >true | |
767 | c.increaseCount(); | |
768 | c.getCount(); | |
769 | >1 | |
770 | c.count | |
771 | >1 | |
772 | ||
773 | * A reference to the superclass's methods can be obtained through | |
774 | * the defined class's property $superclass. Similar functionality | |
775 | * can be done with {@link Luc.define#callSuper callSuper} but callSuper | |
776 | * is much less efficient. | |
777 | * | |
778 | var C = Luc.define({ | |
779 | $super:Counter, | |
780 | increaseCount: function () { | |
781 | this.count += 2; | |
782 | C.$superclass.increaseCount.call(this); | |
783 | } | |
784 | }); | |
785 | ||
786 | var c = new C(); | |
787 | c.increaseCount(); | |
788 | c.count | |
789 | >3 | |
790 | * | |
791 | * Check out Luc.Base to see why we have it as the default. | |
792 | * | |
793 | var B = Luc.define({ | |
794 | amIALucBase: function() { | |
795 | return this instanceof Luc.Base | |
796 | } | |
797 | }) | |
798 | var b = new B(); | |
799 | b.amIALucBase(); | |
800 | >true | |
801 | * | |
802 | * | |
803 | */ | |
804 | ||
805 | ||
806 | ||
807 | /** | |
808 | * @cfg {Object/Object[]} $compositions (optional) config objects for | |
809 | * Luc.Composition. Compositions are a great way to add behavior to a class | |
810 | * without extending it. A {@link Luc.define#$mixins mixin} can offer similar functionality but should | |
811 | * not be adding an unneeded state. A Constructor and a name are needed for the config object. | |
812 | * Using this config adds the {@link Luc.define#getComposition getComposition} | |
813 | * method to instances. | |
814 | * <br> | |
815 | * The methods property is optional here but it is saying take all of | |
816 | * Luc.EventEmitter's instance methods and make them instance methods for C. | |
817 | * You can check out all of the config options by looking at Luc.Composition. | |
818 | * | |
819 | var C = Luc.define({ | |
820 | $compositions: { | |
821 | Constructor: Luc.EventEmitter, | |
822 | name: 'emitter', | |
823 | methods: 'allMethods' | |
824 | } | |
825 | }); | |
826 | ||
827 | var c = new C(); | |
828 | ||
829 | c.on('hey', function() { | |
830 | console.log(arguments); | |
831 | }); | |
832 | ||
833 | c.emit('hey', 1,2,3, 'a'); | |
834 | >[1, 2, 3, "a"] | |
835 | c instanceof Luc.EventEmitter | |
836 | >false | |
837 | c._events | |
838 | >undefined | |
839 | * | |
840 | * Luc.EventEmitter is preferred as a composition over a mixin because | |
841 | * it adds a state "_events" to the this instance when on is called. It | |
842 | * also shouldn't have to know that it may be instantiated alone or mixed into classes | |
843 | * so the initing of state is not done in the constructor like it probably should. | |
844 | * It is not terrible practice by any means but it is not good to have a standalone class | |
845 | * that knows that it may be mixing. Even worse than that would be a mixin that needs | |
846 | * to be inited by the defining class. Encapsulating logic in a class | |
847 | * and using it anywhere seamlessly is where compositions shine. Luc comes with two common | |
848 | * {@link Luc#compositionEnums enums} that we expect will be used often. | |
849 | * | |
850 | * <br> | |
851 | * Here is an example of a simple composition see how the functionality | |
852 | * is added but we are not inheriting and this.count is | |
853 | * undefined. | |
854 | * | |
855 | function Counter() { | |
856 | this.count = 0; | |
857 | }; | |
858 | ||
859 | Counter.prototype = { | |
860 | getCount: function() { | |
861 | return this.count; | |
862 | }, | |
863 | increaseCount: function() { | |
864 | this.count++; | |
865 | } | |
866 | } | |
867 | ||
868 | var C = Luc.define({ | |
869 | $compositions: { | |
870 | name: 'counter', | |
871 | Constructor: Counter, | |
872 | methods: 'allMethods' | |
873 | } | |
874 | }); | |
875 | ||
876 | var c = new C() | |
877 | ||
878 | c.increaseCount(); | |
879 | c.increaseCount(); | |
880 | c.increaseCount(); | |
881 | c.getCount(); | |
882 | >3 | |
883 | c.count | |
884 | >undefined | |
885 | * | |
886 | * Luc comes with two default composition objects Luc.compositionEnums.PluginManager | |
887 | * and Luc.compositionEnums.EventEmitter. | |
888 | * | |
889 | * Here is the plugin manager enum, keep in mind that this | |
890 | * functionality can be added to any class, not just ones defined with | |
891 | * Luc.define. Check out Luc.PluginManager to see all of the public | |
892 | * methods that gets added to the defined instance. | |
893 | ||
894 | * A plugin follows the following life-cycle: <br> | |
895 | ||
896 | *plugin is added to the instance -> plugin is created -> plugin init is called with instance -> if needed destroy called by instance -> destroy called on plugin <br> | |
897 | *Here is the most basic example using the {@link Luc.Plugin default} plugin. | |
898 | ||
899 | var C = Luc.define({ | |
900 | $compositions: Luc.compositionEnums.PluginManager | |
901 | }); | |
902 | ||
903 | var c = new C({ | |
904 | plugins: [{ | |
905 | init: function() { | |
906 | console.log('im getting initted') | |
907 | }, | |
908 | myCoolName: 'cool' | |
909 | } | |
910 | ] | |
911 | }); | |
912 | ||
913 | >im getting initted | |
914 | ||
915 | c.getPlugin({myCoolName: 'coo'}) instanceof Luc.Plugin | |
916 | > true | |
917 | ||
918 | * Plugins can be of any class and can be added with {@link Luc.PluginManager#addPlugin addPlugin} | |
919 | ||
920 | function MyPlugin(){} | |
921 | ||
922 | var C = Luc.define({ | |
923 | $compositions: Luc.compositionEnums.PluginManager | |
924 | }); | |
925 | ||
926 | var c = new C(); | |
927 | ||
928 | c.addPlugin({Constructor: MyPlugin}); | |
929 | //getPlugin takes a Constructor or match object | |
930 | c.getPlugin(MyPlugin) instanceof MyPlugin | |
931 | >true | |
932 | c.getPlugin(Luc.Plugin) | |
933 | >false | |
934 | ||
935 | * Plugins can also be destroyed individually or all of them at once | |
936 | ||
937 | var C = Luc.define({ | |
938 | $compositions: Luc.compositionEnums.PluginManager | |
939 | }); | |
940 | ||
941 | var c = new C({ | |
942 | plugins: [{ | |
943 | init: function() { | |
944 | console.log('im getting initted ' + this.name) | |
945 | }, | |
946 | destroy: function() { | |
947 | console.log('destroyed : ' + this.name) | |
948 | }, | |
949 | name: '1' | |
950 | },{ | |
951 | init: function() { | |
952 | console.log('im getting initted ' + this.name) | |
953 | }, | |
954 | destroy: function() { | |
955 | console.log('destroyed : ' + this.name) | |
956 | }, | |
957 | name: '2' | |
958 | }] | |
959 | }); | |
960 | ||
961 | >im getting initted 1 | |
962 | >im getting initted 2 | |
963 | ||
964 | ||
965 | c.destroyPlugin({name: '1'}); | |
966 | >destroyed : 1 | |
967 | //a plugin is returned if it is found and destroyed | |
968 | >Plugin {init: function, destroy: function, name: "1", owner: Object, init: functionâ¦} | |
969 | ||
970 | c.destroyPlugin({name: '1'}); | |
971 | //false is returned if it is not found | |
972 | >false | |
973 | ||
974 | c.destroyAllPlugins(); | |
975 | >destroyed : 2 | |
976 | * | |
977 | * You can see that it can add plugin like behavior to any defining | |
978 | * class with Luc.PluginManager which is less than 75 SLOC. | |
979 | */ |
Line | Hits | Source |
---|---|---|
1 | 1 | var aEach = require('../array').each, |
2 | obj = require('../object'), | |
3 | emptyFn = require('../function').emptyFn, | |
4 | apply = obj.apply; | |
5 | ||
6 | /** | |
7 | * @class Luc.Plugin | |
8 | * Simple class that is the default plugin type for Luc.PluginManager | |
9 | */ | |
10 | 1 | function Plugin(config) { |
11 | 5 | apply(this, config); |
12 | } | |
13 | ||
14 | 1 | Plugin.prototype = { |
15 | /** | |
16 | * @method | |
17 | * @param {Object} owner the owner instance | |
18 | * Simple hook to initialize the plugin | |
19 | * Defaults to Luc.emptyFn. | |
20 | */ | |
21 | init: emptyFn, | |
22 | /** | |
23 | * @method | |
24 | * Defaults to Luc.emptyFn. | |
25 | * Called when the plugin is being {@link Luc.PluginManager#destroyPlugin destroyed}. | |
26 | */ | |
27 | destroy: emptyFn | |
28 | }; | |
29 | ||
30 | 1 | module.exports = Plugin; |
Line | Hits | Source |
---|---|---|
1 | 1 | var Plugin = require('./plugin'), |
2 | is = require('../is'), | |
3 | obj = require('../object'), | |
4 | arr = require('../array'), | |
5 | aEach = arr.each, | |
6 | mix = obj.mix, | |
7 | apply = obj.apply; | |
8 | ||
9 | 1 | function PluginManager(config) { |
10 | 3 | this._init(config); |
11 | } | |
12 | ||
13 | /** | |
14 | * @protected | |
15 | * @class Luc.PluginManager | |
16 | * This class is used by Luc.compositionEnums#PluginManager to add its functionality | |
17 | * to any class. By {@link Luc.compositionEnums#PluginManager default} it adds | |
18 | * all of these public methods to the instance.This class is designed to work as a composition, | |
19 | * it is exposed as not private so it can be extended if needed. Check "protected" which | |
20 | * is a part of the Show v dropdown on the right to see the protected methods. | |
21 | * | |
22 | function MyPlugin() { | |
23 | this.myCoolName = 'coo'; | |
24 | ||
25 | this.init = function() { | |
26 | console.log('im getting initted'); | |
27 | } | |
28 | this.destroy = function() { | |
29 | console.log('MyPlugin instance being destroyed') | |
30 | } | |
31 | } | |
32 | ||
33 | var C = Luc.define({ | |
34 | $compositions: Luc.compositionEnums.PluginManager | |
35 | }); | |
36 | ||
37 | var c = new C({ | |
38 | plugins: [{ | |
39 | Constructor: MyPlugin, | |
40 | myCoolName: 'coo' | |
41 | } | |
42 | ] | |
43 | }); | |
44 | ||
45 | >im getting initted | |
46 | ||
47 | var plugInstance = c.addPlugin({ | |
48 | destroy: function() { | |
49 | console.log('Im getting destroyed') | |
50 | } | |
51 | }); | |
52 | ||
53 | c.getPlugin(Luc.Plugin) | |
54 | > Plugin {destroy: function, owner: MyClass, init: function, destroy: function} | |
55 | ||
56 | c.getPlugin(MyPlugin) | |
57 | > MyPlugin {myCoolName: "coo", init: function, destroy: function} | |
58 | ||
59 | c.destroyAllPlugins() | |
60 | ||
61 | >MyPlugin instance being destroyed | |
62 | >Im getting destroyed | |
63 | ||
64 | c.getPlugin(MyPlugin) | |
65 | >false | |
66 | ||
67 | */ | |
68 | 1 | PluginManager.prototype = { |
69 | /** | |
70 | * @cfg {Constructor} defaultPlugin | |
71 | */ | |
72 | defaultPlugin: Plugin, | |
73 | ||
74 | /** | |
75 | * @protected | |
76 | */ | |
77 | _init: function(instanceValues) { | |
78 | 3 | apply(this, instanceValues); |
79 | 3 | this.plugins = []; |
80 | 3 | this._createPlugins(); |
81 | }, | |
82 | ||
83 | /** | |
84 | * @protected | |
85 | */ | |
86 | _createPlugins: function() { | |
87 | 3 | aEach(this._getPluginConfigFromInstance(), function(pluginConfig) { |
88 | 5 | this.addPlugin(pluginConfig); |
89 | }, this); | |
90 | }, | |
91 | ||
92 | /** | |
93 | * @protected | |
94 | */ | |
95 | _getPluginConfigFromInstance: function() { | |
96 | 3 | var config = this.instanceArgs[0] || {}; |
97 | 3 | return config.plugins; |
98 | }, | |
99 | ||
100 | /** | |
101 | * Add a plugin to the instance and init the | |
102 | * plugin. | |
103 | * @param {Object} pluginConfig | |
104 | * @return {Object} the created plugin instance | |
105 | */ | |
106 | addPlugin: function(pluginConfig) { | |
107 | 7 | var pluginInstance = this._createPlugin(pluginConfig); |
108 | ||
109 | 7 | this._initPlugin(pluginInstance); |
110 | ||
111 | 7 | this.plugins.push(pluginInstance); |
112 | ||
113 | 7 | return pluginInstance; |
114 | }, | |
115 | ||
116 | /** | |
117 | * @protected | |
118 | */ | |
119 | _createPlugin: function(config) { | |
120 | 7 | config.owner = this.instance; |
121 | ||
122 | 7 | if (config.Constructor) { |
123 | //call the configed Constructor with the | |
124 | //passed in config but take off the Constructor | |
125 | //config. | |
126 | ||
127 | //The plugin Constructor | |
128 | //should not need to know about itself | |
129 | 2 | return new config.Constructor(apply(config, { |
130 | Constructor: undefined | |
131 | })); | |
132 | } | |
133 | ||
134 | 5 | return new this.defaultPlugin(config); |
135 | }, | |
136 | ||
137 | /** | |
138 | * @protected | |
139 | */ | |
140 | _initPlugin: function(plugin) { | |
141 | 7 | if (is.isFunction(plugin.init)) { |
142 | 5 | plugin.init(this.instance); |
143 | } | |
144 | }, | |
145 | ||
146 | /** | |
147 | * Call destroy on all of the plugins | |
148 | * and remove them. | |
149 | */ | |
150 | destroyAllPlugins: function() { | |
151 | 1 | this.plugins.forEach(function(plugin) { |
152 | 2 | this._destroyPlugin(plugin); |
153 | }, this); | |
154 | ||
155 | 1 | this.plugins = []; |
156 | }, | |
157 | ||
158 | _destroyPlugin: function(plugin) { | |
159 | 3 | if (is.isFunction(plugin.destroy)) { |
160 | 3 | plugin.destroy(this.instance); |
161 | } | |
162 | }, | |
163 | ||
164 | /** | |
165 | * Remove the plugin and if found destroy it. | |
166 | * @param {Object/Constructor} object to use to match | |
167 | * the plugin to remove. | |
168 | * @return {Object} the destroyed plugin. | |
169 | */ | |
170 | destroyPlugin: function(obj) { | |
171 | 1 | var plugin = this.getPlugin(obj); |
172 | ||
173 | 1 | if(plugin) { |
174 | 1 | this._destroyPlugin(plugin); |
175 | 1 | arr.removeFirst(this.plugins, plugin, {type: 'strict'}); |
176 | } | |
177 | ||
178 | 1 | return plugin; |
179 | }, | |
180 | ||
181 | /** | |
182 | * Get a plugin instance. A Constructor or an object can be used | |
183 | * to find a plugin. | |
184 | * | |
185 | c.addPlugin({a:1}) | |
186 | c.getPlugin({a:1}) | |
187 | >Luc.Plugin({a:1}) | |
188 | ||
189 | * @param {Object} obj | |
190 | * @return {Object} the plugin instance if found. | |
191 | */ | |
192 | getPlugin: function(obj) { | |
193 | 4 | if (is.isFunction(obj)) { |
194 | 2 | return arr.findFirstInstanceOf(this.plugins, obj); |
195 | } | |
196 | 2 | return arr.findFirst(this.plugins, obj, {type: 'loose'}); |
197 | } | |
198 | }; | |
199 | ||
200 | 1 | module.exports = PluginManager; |
Line | Hits | Source |
---|---|---|
1 | 1 | var is = require('./is'); |
2 | ||
3 | 1 | function _strict(val1, val2){ |
4 | 237 | return val1 === val2; |
5 | } | |
6 | ||
7 | 1 | function _compareArrayLength(val1, val2) { |
8 | 26 | return(is.isArray(val1) && is.isArray(val2) && val1.length === val2.length); |
9 | } | |
10 | ||
11 | 1 | function _shallowArray(val1, val2) { |
12 | 1 | var i = 0, |
13 | len; | |
14 | ||
15 | 1 | if(!_compareArrayLength(val1, val2)) { |
16 | 0 | return false; |
17 | } | |
18 | ||
19 | 1 | for(len = val1.length; i < len; ++i) { |
20 | 0 | if(val1[i] !== val2[i]) { |
21 | 0 | return false; |
22 | } | |
23 | } | |
24 | ||
25 | 1 | return true; |
26 | } | |
27 | ||
28 | 1 | function _deepArray(val1, val2, config) { |
29 | 25 | var i = 0, |
30 | len; | |
31 | ||
32 | 25 | if(!_compareArrayLength(val1, val2)) { |
33 | 11 | return false; |
34 | } | |
35 | ||
36 | 14 | for(len = val1.length; i < len; ++i) { |
37 | 25 | if(!compare(val1[i],val2[i], config)) { |
38 | 4 | return false; |
39 | } | |
40 | } | |
41 | ||
42 | 10 | return true; |
43 | } | |
44 | ||
45 | 1 | function _compareObjectKeysLength(val1, val2) { |
46 | 27 | return (is.isObject(val1) && is.isObject(val2) && Object.keys(val1).length === Object.keys(val2).length); |
47 | } | |
48 | ||
49 | 1 | function _shallowObject(val1, val2) { |
50 | 2 | var key, val; |
51 | ||
52 | 2 | if (!_compareObjectKeysLength(val1, val2)) { |
53 | 0 | return false; |
54 | } | |
55 | ||
56 | 2 | for (key in val1) { |
57 | 3 | if (val1.hasOwnProperty(key)) { |
58 | 3 | value = val1[key]; |
59 | 3 | if (!val2.hasOwnProperty(key) || val2[key] !== value) { |
60 | 1 | return false; |
61 | } | |
62 | } | |
63 | } | |
64 | ||
65 | 1 | return true; |
66 | } | |
67 | ||
68 | 1 | function _deepObject(val1, val2, config) { |
69 | 25 | var key, val; |
70 | ||
71 | 25 | if (!_compareObjectKeysLength(val1, val2)) { |
72 | 9 | return false; |
73 | } | |
74 | ||
75 | 16 | for (key in val1) { |
76 | 18 | if (val1.hasOwnProperty(key)) { |
77 | 18 | value = val1[key]; |
78 | 18 | if (!val2.hasOwnProperty(key) || compare(value, val2[key], config) !== true) { |
79 | 4 | return false; |
80 | } | |
81 | } | |
82 | } | |
83 | ||
84 | 12 | return true; |
85 | ||
86 | } | |
87 | ||
88 | 1 | function _looseObject(val1, val2, config) { |
89 | 54 | var key, val; |
90 | ||
91 | 54 | if(!(is.isObject(val1) && is.isObject(val2))) { |
92 | 6 | return false; |
93 | } | |
94 | ||
95 | 48 | if(config.type === 'looseRight') { |
96 | 4 | for (key in val2) { |
97 | 4 | if (val2.hasOwnProperty(key)) { |
98 | 4 | value = val2[key]; |
99 | 4 | if (compare(value, val1[key], config) !== true) { |
100 | 2 | return false; |
101 | } | |
102 | } | |
103 | } | |
104 | } | |
105 | else { | |
106 | 44 | for (key in val1) { |
107 | 67 | if (val1.hasOwnProperty(key)) { |
108 | 67 | value = val1[key]; |
109 | 67 | if (compare(value, val2[key], config) !== true) { |
110 | 24 | return false; |
111 | } | |
112 | } | |
113 | } | |
114 | } | |
115 | ||
116 | ||
117 | 22 | return true; |
118 | ||
119 | } | |
120 | ||
121 | 1 | function _date(val1, val2) { |
122 | 21 | if(is.isDate(val1) && is.isDate(val2)) { |
123 | 16 | return val1.getTime() === val2.getTime(); |
124 | } | |
125 | ||
126 | 5 | return false; |
127 | } | |
128 | ||
129 | 1 | function _createBoundCompare(object, fn) { |
130 | 0 | return function(value) { |
131 | 0 | return fn(object, value); |
132 | }; | |
133 | } | |
134 | ||
135 | 1 | function getCompareFn(object, c) { |
136 | 366 | var compareFn = _strict, |
137 | config = c || {}, | |
138 | type = config.type; | |
139 | ||
140 | 366 | if (type === 'deep' || type === 'loose' || type === 'looseRight' || type === undefined) { |
141 | 326 | if (is.isObject(object)) { |
142 | 79 | compareFn = type === 'loose' || type === 'looseRight' ? _looseObject : _deepObject; |
143 | 247 | } else if (is.isArray(object)) { |
144 | 25 | compareFn = _deepArray; |
145 | 222 | } else if (is.isDate(object)) { |
146 | 18 | compareFn = _date; |
147 | } | |
148 | 40 | } else if (type === 'shallow') { |
149 | 6 | if (is.isObject(object)) { |
150 | 2 | compareFn = _shallowObject; |
151 | 4 | } else if (is.isArray(object)) { |
152 | 1 | compareFn = _shallowArray; |
153 | 3 | } else if (is.isDate(object)) { |
154 | 3 | compareFn = _date; |
155 | } | |
156 | 34 | } else if (type !== 'strict') { |
157 | //we would be doing a strict comparison on a type-o | |
158 | //I think an error is good here. | |
159 | 1 | throw new Error('You passed in an invalid comparison type'); |
160 | } | |
161 | ||
162 | 365 | return compareFn; |
163 | } | |
164 | ||
165 | /** | |
166 | * @member Luc | |
167 | * @method compare | |
168 | * | |
169 | * Return true if the values are equal to each | |
170 | * other. By default a deep comparison is | |
171 | * done on arrays, dates and objects and a strict comparison | |
172 | * is done on other types. | |
173 | * | |
174 | * @param {Any} val1 | |
175 | * @param {Any} val2 | |
176 | * @param {Object} [config] | |
177 | * @param {String} config.type pass in 'shallow' for a shallow | |
178 | * comparison, 'deep' (default) for a deep comparison | |
179 | * 'strict' for a strict === comparison for all objects or | |
180 | * 'loose' for a loose comparison on objects. A loose comparison | |
181 | * will compare the keys and values of val1 to val2 and does not | |
182 | * check if keys from val2 are equal to the keys in val1. | |
183 | * | |
184 | * | |
185 | Luc.compare('1', 1) | |
186 | >false | |
187 | Luc.compare({a: 1}, {a: 1}) | |
188 | >true | |
189 | Luc.compare({a: 1, b: {}}, {a: 1, b: {} }, {type:'shallow'}) | |
190 | >false | |
191 | Luc.compare({a: 1, b: {}}, {a: 1, b: {} }, {type: 'deep'}) | |
192 | >true | |
193 | Luc.compare({a: 1, b: {}}, {a: 1, b: {} }, {type: 'strict'}) | |
194 | >false | |
195 | Luc.compare({a: 1}, {a:1,b:1}) | |
196 | >false | |
197 | Luc.compare({a: 1}, {a:1,b:1}, {type: 'loose'}) | |
198 | >true | |
199 | Luc.compare({a: 1}, {a:1,b:1}, {type: 'loose'}) | |
200 | >true | |
201 | Luc.compare([{a: 1}], [{a:1,b:1}], {type: 'loose'}) | |
202 | >true | |
203 | Luc.compare([{a: 1}, {}], [{a:1,b:1}], {type: 'loose'}) | |
204 | >false | |
205 | Luc.compare([{a: 1}, {}], [{a:1,b:1}, {}], {type: 'loose'}) | |
206 | >true | |
207 | Luc.compare([{a:1,b:1}], [{a: 1}], {type: 'loose'}) | |
208 | >false | |
209 | ||
210 | * @return {Boolean} | |
211 | */ | |
212 | 1 | function compare(val1, val2, config) { |
213 | 366 | return getCompareFn(val1, config)(val1, val2, config); |
214 | } | |
215 | ||
216 | 1 | exports.compare = compare; |
Line | Hits | Source |
---|---|---|
1 | 1 | var EventEmitter = require('events').EventEmitter; |
2 | /** | |
3 | * @license https://raw.github.com/joyent/node/v0.10.11/LICENSE | |
4 | * Node js license. EventEmitter will be in the client | |
5 | * only code. | |
6 | */ | |
7 | /** | |
8 | * @class Luc.EventEmitter | |
9 | * The wonderful event emmiter that comes with node, | |
10 | * that works in the supported browsers. | |
11 | * [http://nodejs.org/api/events.html](http://nodejs.org/api/events.html) | |
12 | */ | |
13 | 1 | EventEmitter.prototype.once = function(type, listener) { |
14 | //put in fix for IE 9 and below | |
15 | 5 | var self = this, |
16 | g = function() { | |
17 | 5 | self.removeListener(type, g); |
18 | 5 | listener.apply(this, arguments); |
19 | }; | |
20 | ||
21 | 5 | self.on(type, g); |
22 | ||
23 | 5 | return this; |
24 | }; | |
25 | ||
26 | 1 | module.exports = EventEmitter; |
Line | Hits | Source |
---|---|---|
1 | 1 | var is = require('./is'), |
2 | aInsert = require('./array').insert, | |
3 | aEach = require('./array').each; | |
4 | ||
5 | /** | |
6 | * @class Luc.Function | |
7 | * Package for function methods. Most of them follow the same api: | |
8 | * function or function[], relevant args ... with an optional config | |
9 | * to Luc.Function.createAugmenter as the last argument. | |
10 | */ | |
11 | ||
12 | 1 | function _augmentArgs(config, callArgs) { |
13 | 16 | var configArgs = config.args, |
14 | index = config.index, | |
15 | argsArray; | |
16 | ||
17 | 16 | if (!configArgs) { |
18 | 5 | return callArgs; |
19 | } | |
20 | ||
21 | 11 | if(index === true || is.isNumber(index)) { |
22 | 10 | if(config.argumentsFirst === false) { |
23 | 2 | return aInsert(configArgs, callArgs, index); |
24 | } | |
25 | 8 | return aInsert(callArgs, configArgs, index); |
26 | } | |
27 | ||
28 | 1 | return configArgs; |
29 | } | |
30 | ||
31 | /** | |
32 | * A reusable empty function | |
33 | * @return {Function} | |
34 | */ | |
35 | 1 | exports.emptyFn = function() {}; |
36 | ||
37 | /** | |
38 | * A function that throws an error when called. | |
39 | * Useful when defining abstract like classes | |
40 | * @return {Function} | |
41 | */ | |
42 | 1 | exports.abstractFn = function() { |
43 | 0 | throw new Error('abstractFn must be implemented'); |
44 | }; | |
45 | ||
46 | /** | |
47 | * Augment the passed in function's thisArg and or arguments object | |
48 | * based on the passed in config. | |
49 | * | |
50 | * @param {Function} fn the function to call | |
51 | * @param {Object} config | |
52 | * | |
53 | * @param {Object} [config.thisArg] the thisArg for the function being executed. | |
54 | * If this is the only property on your config object the preferred way would | |
55 | * be just to use Function.bind | |
56 | * | |
57 | * @param {Array} [config.args] the arguments used for the function being executed. | |
58 | * This will replace the functions call args if index is not a number or | |
59 | * true. | |
60 | * | |
61 | * @param {Number/True} [config.index] By default the the configured arguments | |
62 | * will be inserted into the functions passed in call arguments. If index is true | |
63 | * append the args together if it is a number insert it at the passed in index. | |
64 | * | |
65 | * @param {Array} [config.argumentsFirst] pass in false to | |
66 | * augment the configured args first with Luc.Array.insert. Defaults | |
67 | * to true | |
68 | ||
69 | function fn() { | |
70 | console.log(this) | |
71 | console.log(arguments) | |
72 | } | |
73 | ||
74 | //Luc.Array.insert([4], [1,2,3], 0) | |
75 | Luc.Function.createAugmenter(fn, { | |
76 | thisArg: {configedThisArg: true}, | |
77 | args: [1,2,3], | |
78 | index:0 | |
79 | })(4) | |
80 | ||
81 | >Object {configedThisArg: true} | |
82 | >[1, 2, 3, 4] | |
83 | ||
84 | //Luc.Array.insert([1,2,3], [4], 0) | |
85 | Luc.Function.createAugmenter(fn, { | |
86 | thisArg: {configedThisArg: true}, | |
87 | args: [1,2,3], | |
88 | index:0, | |
89 | argumentsFirst:false | |
90 | })(4) | |
91 | ||
92 | >Object {configedThisArg: true} | |
93 | >[4, 1, 2, 3] | |
94 | ||
95 | Luc.Array.insert([4], [1,2,3], true) | |
96 | var f = Luc.Function.createAugmenter(fn, { | |
97 | args: [1,2,3], | |
98 | index: true | |
99 | }); | |
100 | ||
101 | f.apply({config: false}, [4]) | |
102 | ||
103 | >Object {config: false} | |
104 | >[4, 1, 2, 3] | |
105 | ||
106 | * @return {Function} the augmented function. | |
107 | */ | |
108 | 1 | exports.createAugmenter = function(fn, config) { |
109 | 17 | var thisArg = config.thisArg; |
110 | ||
111 | 17 | return function() { |
112 | 16 | return fn.apply(thisArg || this, _augmentArgs(config, arguments)); |
113 | }; | |
114 | }; | |
115 | ||
116 | 1 | function _initSequenceFunctions(fns, config) { |
117 | 4 | var toRun = []; |
118 | 4 | aEach(fns, function(f) { |
119 | 12 | var fn = f; |
120 | ||
121 | 12 | if (config) { |
122 | 9 | fn = exports.createAugmenter(f, config); |
123 | } | |
124 | ||
125 | 12 | toRun.push(fn); |
126 | }); | |
127 | ||
128 | 4 | return toRun; |
129 | } | |
130 | ||
131 | /** | |
132 | * Return a function that runs the passed in functions | |
133 | * and returns the result of the last function called. | |
134 | * | |
135 | * @param {Function[]} fns | |
136 | * @param {Object} [config] Config object | |
137 | * for Luc.Function.createAugmenter. If defined all of the functions | |
138 | * will get created with the passed in config; | |
139 | * | |
140 | Luc.Function.createSequence([ | |
141 | function() { | |
142 | console.log(1) | |
143 | }, | |
144 | function() { | |
145 | console.log(2) | |
146 | }, | |
147 | function() { | |
148 | console.log(3) | |
149 | console.log('finished logging') | |
150 | return 4; | |
151 | } | |
152 | ])() | |
153 | >1 | |
154 | >2 | |
155 | >3 | |
156 | >finished logging | |
157 | >4 | |
158 | * | |
159 | * @return {Function} | |
160 | */ | |
161 | 1 | exports.createSequence = function(fns, config) { |
162 | 2 | var functions = _initSequenceFunctions(fns, config); |
163 | ||
164 | 2 | return function() { |
165 | 2 | var i = 0, |
166 | lastFnIndex = functions.length -1; | |
167 | ||
168 | 2 | for(;i < lastFnIndex; ++i) { |
169 | 4 | functions[i].apply(this, arguments); |
170 | } | |
171 | ||
172 | 2 | return functions[lastFnIndex].apply(this, arguments); |
173 | }; | |
174 | }; | |
175 | ||
176 | /** | |
177 | * Return a function that runs the passed in functions | |
178 | * if one of the functions returns false the rest of the | |
179 | * functions won't run and false will be returned. | |
180 | * | |
181 | * If no false is returned the value of the last function return will be returned | |
182 | * | |
183 | Luc.Function.createSequenceIf([ | |
184 | function() { | |
185 | console.log(1) | |
186 | }, | |
187 | function() { | |
188 | console.log(2) | |
189 | }, | |
190 | function() { | |
191 | console.log(3) | |
192 | console.log('finished logging') | |
193 | return 4; | |
194 | }, function() { | |
195 | return false; | |
196 | }, function() { | |
197 | console.log('i cant log') | |
198 | } | |
199 | ])() | |
200 | ||
201 | >1 | |
202 | >2 | |
203 | >3 | |
204 | >finished logging | |
205 | >false | |
206 | * | |
207 | * | |
208 | * @param {Function[]} fns | |
209 | * @param {Object} [config] Config object | |
210 | * for Luc.Function.createAugmenter. If defined all of the functions | |
211 | * will get created with the passed in config; | |
212 | * @return {Function} | |
213 | */ | |
214 | 1 | exports.createSequenceIf = function(fns, config) { |
215 | 1 | var functions = _initSequenceFunctions(fns, config); |
216 | ||
217 | 1 | return function() { |
218 | 1 | var value, |
219 | args = arguments; | |
220 | ||
221 | 1 | functions.some(function(fn){ |
222 | 2 | value = fn.apply(this, args); |
223 | ||
224 | 2 | return value === false; |
225 | }, this); | |
226 | ||
227 | 1 | return value; |
228 | }; | |
229 | }; | |
230 | ||
231 | /** | |
232 | * Return a functions that runs the passed in functions | |
233 | * the result of each function will be the the call args | |
234 | * for the next function. The value of the last function | |
235 | * return will be returned. | |
236 | * | |
237 | ||
238 | Luc.Function.createRelayer([ | |
239 | function(str) { | |
240 | return str + 'b' | |
241 | }, | |
242 | function(str) { | |
243 | return str + 'c' | |
244 | }, | |
245 | function(str) { | |
246 | return str + 'd' | |
247 | } | |
248 | ])('a') | |
249 | ||
250 | >"abcd" | |
251 | ||
252 | * @param {Function[]} fns | |
253 | * @param {Object} [config] Config object | |
254 | * for Luc.Function.createAugmenter. If defined all of the functions | |
255 | * will get created with the passed in config; | |
256 | * @return {Function} | |
257 | */ | |
258 | 1 | exports.createRelayer = function(fns, config) { |
259 | 1 | var functions = _initSequenceFunctions(fns, config); |
260 | ||
261 | 1 | return function() { |
262 | 1 | var value, |
263 | args = arguments; | |
264 | ||
265 | 1 | functions.forEach(function(fn, index) { |
266 | 3 | if (index === 0) { |
267 | 1 | value = fn.apply(this, args); |
268 | } else { | |
269 | 2 | value = fn.apply(this, [value]); |
270 | } | |
271 | }, this); | |
272 | ||
273 | 1 | return value; |
274 | }; | |
275 | }; | |
276 | ||
277 | /** | |
278 | * Create a throttled function from the passed in function | |
279 | * that will only get called once the number of milliseconds | |
280 | * have been exceeded. | |
281 | * | |
282 | var logArgs = function() { | |
283 | console.log(arguments) | |
284 | }; | |
285 | ||
286 | var a = Luc.Function.createThrottled(logArgs, 1); | |
287 | ||
288 | for(var i = 0; i < 100; ++i) { | |
289 | a(1,2,3); | |
290 | } | |
291 | ||
292 | setTimeout(function() { | |
293 | a(1) | |
294 | }, 100) | |
295 | setTimeout(function() { | |
296 | a(2) | |
297 | }, 400) | |
298 | ||
299 | >[1, 2, 3] | |
300 | >[1] | |
301 | >[2] | |
302 | * | |
303 | * @param {Function} fn | |
304 | * @param {Number} millis Number of milliseconds to | |
305 | * throttle the function. | |
306 | * @param {Object} [config] Config object | |
307 | * for Luc.Function.createAugmenter. If defined all of the functions | |
308 | * will get created with the passed in config; | |
309 | * @return {Function} | |
310 | */ | |
311 | 1 | exports.createThrottled = function(f, millis, config) { |
312 | 2 | var fn = config ? exports.createAugmenter(f, config) : f, |
313 | timeOutId = false; | |
314 | ||
315 | 2 | if(!millis) { |
316 | 1 | return fn; |
317 | } | |
318 | ||
319 | 1 | return function() { |
320 | 201 | var args = arguments; |
321 | ||
322 | 201 | if(timeOutId) { |
323 | 200 | clearTimeout(timeOutId); |
324 | } | |
325 | ||
326 | 201 | timeOutId = setTimeout(function() { |
327 | 1 | timeOutId = false; |
328 | 1 | fn.apply(this, args); |
329 | }, millis); | |
330 | }; | |
331 | }; | |
332 | ||
333 | /** | |
334 | * Defer a function's execution for the passed in | |
335 | * milliseconds. | |
336 | * | |
337 | * @param {Function} fn | |
338 | * @param {Number} millis Number of milliseconds to | |
339 | * defer | |
340 | * @param {Object} [config] Config object | |
341 | * for Luc.Function.createAugmenter. If defined all of the functions | |
342 | * will get created with the passed in config; | |
343 | * | |
344 | * @return {Function} | |
345 | */ | |
346 | 1 | exports.createDeferred = function(f, millis, config) { |
347 | 2 | var fn = config ? exports.createAugmenter(f, config) : f; |
348 | ||
349 | 2 | if(!millis) { |
350 | 1 | return fn; |
351 | } | |
352 | ||
353 | 1 | return function() { |
354 | 1 | var args = arguments; |
355 | ||
356 | 1 | setTimeout(function() { |
357 | 1 | fn.apply(this, args); |
358 | }, millis); | |
359 | }; | |
360 | }; |
Line | Hits | Source |
---|---|---|
1 | 1 | var ids = {}; |
2 | /** | |
3 | * @member Luc | |
4 | * @method id | |
5 | * | |
6 | * Return a unique id. | |
7 | * @param {String} [prefix] Optional prefix to use | |
8 | * | |
9 | * | |
10 | Luc.id() | |
11 | >"luc-0" | |
12 | Luc.id() | |
13 | >"luc-1" | |
14 | Luc.id('my-prefix') | |
15 | >"my-prefix0" | |
16 | Luc.id('') | |
17 | >"0" | |
18 | * | |
19 | * @return {String} | |
20 | * | |
21 | */ | |
22 | 1 | module.exports = function(p) { |
23 | 0 | var prefix = p === undefined ? 'luc-' : p; |
24 | ||
25 | 0 | if(ids[prefix] === undefined) { |
26 | 0 | ids[prefix] = 0; |
27 | } | |
28 | ||
29 | 0 | return prefix + ids[prefix]++; |
30 | }; |
Line | Hits | Source |
---|---|---|
1 | 1 | var oToString = Object.prototype.toString; |
2 | ||
3 | ||
4 | /** | |
5 | * @member Luc | |
6 | * Return true if the passed in object is of | |
7 | * the type {@link Array Array} | |
8 | * @param {Object} obj | |
9 | * @return {Boolean} | |
10 | */ | |
11 | 1 | function isArray(obj) { |
12 | 323 | return Array.isArray(obj); |
13 | } | |
14 | ||
15 | /** | |
16 | * @member Luc | |
17 | * Return true if the passed in object is of | |
18 | * the type {@link Object Object} | |
19 | * @param {Object} obj | |
20 | * @return {Boolean} | |
21 | */ | |
22 | 1 | function isObject(obj) { |
23 | 511 | return obj && oToString.call(obj) === '[object Object]'; |
24 | } | |
25 | ||
26 | /** | |
27 | * @member Luc | |
28 | * Return true if the passed in object is of | |
29 | * the type {@link Function Function} | |
30 | * @param {Object} obj | |
31 | * @return {Boolean} | |
32 | */ | |
33 | 1 | function isFunction(obj) { |
34 | 295 | return oToString.call(obj) === '[object Function]'; |
35 | } | |
36 | ||
37 | /** | |
38 | * @member Luc | |
39 | * Return true if the passed in object is of | |
40 | * the type {@link Date Date} | |
41 | * @param {Object} obj | |
42 | * @return {Boolean} | |
43 | */ | |
44 | 1 | function isDate(obj) { |
45 | 269 | return oToString.call(obj) === '[object Date]'; |
46 | } | |
47 | ||
48 | /** | |
49 | * @member Luc | |
50 | * Return true if the passed in object is of | |
51 | * the type {@link RegExp RegExp} | |
52 | * @param {Object} obj | |
53 | * @return {Boolean} | |
54 | */ | |
55 | 1 | function isRegExp(obj) { |
56 | 2 | return oToString.call(obj) === '[object RegExp]'; |
57 | } | |
58 | ||
59 | /** | |
60 | * @member Luc | |
61 | * Return true if the passed in object is of | |
62 | * the type {@link Number Number} | |
63 | * @param {Object} obj | |
64 | * @return {Boolean} | |
65 | */ | |
66 | 1 | function isNumber(obj) { |
67 | 7 | return oToString.call(obj) === '[object Number]'; |
68 | } | |
69 | ||
70 | /** | |
71 | * @member Luc | |
72 | * Return true if the passed in object is of | |
73 | * the type {@link String String} | |
74 | * @param {Object} obj | |
75 | * @return {Boolean} | |
76 | */ | |
77 | 1 | function isString(obj) { |
78 | 16 | return oToString.call(obj) === '[object String]'; |
79 | } | |
80 | ||
81 | /** | |
82 | * @member Luc | |
83 | * Return true if the passed in object is of | |
84 | * the type arguments. | |
85 | * | |
86 | * @param {Object} obj | |
87 | * @return {Boolean} | |
88 | */ | |
89 | 1 | function isArguments(obj) { |
90 | 18 | return oToString.call(obj) === '[object Arguments]' || obj && !!obj.callee; |
91 | } | |
92 | ||
93 | /** | |
94 | * @member Luc | |
95 | * Return true if the object is falsy but not zero. | |
96 | Luc.isFalsy(false) | |
97 | >true | |
98 | Luc.isFalsy(0) | |
99 | >false | |
100 | * @param {Object} obj | |
101 | * @return {Boolean} | |
102 | */ | |
103 | 1 | function isFalsy(obj) { |
104 | 16 | return (!obj && obj !== 0); |
105 | } | |
106 | ||
107 | /** | |
108 | * @member Luc | |
109 | * Return true if the object is empty. | |
110 | * {}, [], '',false, null, undefined, NaN | |
111 | * are all treated as empty. | |
112 | * | |
113 | Luc.isEmpty(true) | |
114 | >false | |
115 | Luc.isEmpty([]) | |
116 | >true | |
117 | ||
118 | * @param {Object} obj | |
119 | * @return {Boolean} | |
120 | */ | |
121 | 1 | function isEmpty(obj) { |
122 | 9 | var empty = false; |
123 | ||
124 | 9 | if (isFalsy(obj)) { |
125 | 4 | empty = true; |
126 | 5 | } else if (isArray(obj)) { |
127 | 2 | empty = obj.length === 0; |
128 | 3 | } else if (isObject(obj)) { |
129 | 2 | empty = Object.keys(obj).length === 0; |
130 | } | |
131 | ||
132 | 9 | return empty; |
133 | } | |
134 | ||
135 | 1 | module.exports = { |
136 | isArray: isArray, | |
137 | isObject: isObject, | |
138 | isFunction: isFunction, | |
139 | isDate: isDate, | |
140 | isString: isString, | |
141 | isNumber: isNumber, | |
142 | isRegExp: isRegExp, | |
143 | isArguments: isArguments, | |
144 | isFalsy: isFalsy, | |
145 | isEmpty: isEmpty | |
146 | }; |
Line | Hits | Source |
---|---|---|
1 | 1 | var Luc = {}, |
2 | isBrowser = false; | |
3 | ||
4 | 1 | if(typeof window !== 'undefined') { |
5 | 0 | isBrowser = true; |
6 | } | |
7 | /** | |
8 | * @class Luc | |
9 | * Aliases for common Luc methods and packages. Check out Luc.define | |
10 | * to look at the class system Luc provides. | |
11 | */ | |
12 | 1 | module.exports = Luc; |
13 | ||
14 | 1 | var object = require('./object'); |
15 | 1 | Luc.Object = object; |
16 | /** | |
17 | * @member Luc | |
18 | * @property O Luc.O | |
19 | * Alias for Luc.Object | |
20 | */ | |
21 | 1 | Luc.O = object; |
22 | ||
23 | ||
24 | /** | |
25 | * @member Luc | |
26 | * @method apply | |
27 | * @inheritdoc Luc.Object#apply | |
28 | */ | |
29 | 1 | Luc.apply = Luc.Object.apply; |
30 | ||
31 | /** | |
32 | * @member Luc | |
33 | * @method mix | |
34 | * @inheritdoc Luc.Object#mix | |
35 | */ | |
36 | 1 | Luc.mix = Luc.Object.mix; |
37 | ||
38 | ||
39 | 1 | var fun = require('./function'); |
40 | 1 | Luc.Function = fun; |
41 | ||
42 | /** | |
43 | * @member Luc | |
44 | * @property F Luc.F | |
45 | * Alias for Luc.Function | |
46 | */ | |
47 | 1 | Luc.F = fun; |
48 | ||
49 | /** | |
50 | * @member Luc | |
51 | * @method emptyFn | |
52 | * @inheritdoc Luc.Function#emptyFn | |
53 | */ | |
54 | 1 | Luc.emptyFn = Luc.Function.emptyFn; |
55 | ||
56 | /** | |
57 | * @member Luc | |
58 | * @method abstractFn | |
59 | * @inheritdoc Luc.Function#abstractFn | |
60 | */ | |
61 | 1 | Luc.abstractFn = Luc.Function.abstractFn; |
62 | ||
63 | 1 | var array = require('./array'); |
64 | 1 | Luc.Array = array; |
65 | ||
66 | /** | |
67 | * @member Luc | |
68 | * @property A Luc.A | |
69 | * Alias for Luc.Array | |
70 | */ | |
71 | 1 | Luc.A = array; |
72 | ||
73 | 1 | Luc.ArrayFnGenerator = require('./arrayFnGenerator'); |
74 | ||
75 | 1 | Luc.apply(Luc, require('./is')); |
76 | ||
77 | 1 | var EventEmitter = require('./events/eventEmitter'); |
78 | ||
79 | 1 | Luc.EventEmitter = EventEmitter; |
80 | ||
81 | 1 | var Base = require('./class/base'); |
82 | ||
83 | 1 | Luc.Base = Base; |
84 | ||
85 | 1 | var Definer = require('./class/definer'); |
86 | ||
87 | 1 | Luc.ClassDefiner = Definer; |
88 | ||
89 | /** | |
90 | * @member Luc | |
91 | * @method define | |
92 | * @inheritdoc Luc.define#define | |
93 | */ | |
94 | 1 | Luc.define = Definer.define; |
95 | ||
96 | 1 | Luc.Plugin = require('./class/plugin'); |
97 | ||
98 | 1 | Luc.PluginManager = require('./class/pluginManager'); |
99 | ||
100 | 1 | Luc.apply(Luc, { |
101 | compositionEnums: require('./class/compositionEnums') | |
102 | }); | |
103 | ||
104 | 1 | Luc.compare = require('./compare').compare; |
105 | ||
106 | 1 | Luc.id = require('./id'); |
107 | ||
108 | ||
109 | 1 | if(isBrowser) { |
110 | 0 | window.Luc = Luc; |
111 | } | |
112 | ||
113 | /** | |
114 | * @member Luc | |
115 | * @method addSubmodule | |
116 | * Method used by submodule authors to add their module into Luc. | |
117 | * By default the submodule will only be added to Luc if Luc is in | |
118 | * the context of a browser. Node already has a nice module system. | |
119 | * This behavior can be overridden by setting | |
120 | * Luc.alwaysAddSubmodule to true. | |
121 | * | |
122 | * @param {String} namespace the namespace of the submodule | |
123 | * @param {Object} obj the object to add to the namespace. If keys already exist in | |
124 | * the namespace they will not be overwritten. | |
125 | * | |
126 | function Tooltip() {} | |
127 | var coolTooltip = {Tooltip: Tooltip}; | |
128 | Luc.addSubmodule('ui', coolTooltip); | |
129 | Luc.ui.Tooltip; | |
130 | >function Tooltip() {} | |
131 | ||
132 | *use another submodule | |
133 | ||
134 | Luc.addSubmodule('ui', {SomeonesObject: {a:true}}); | |
135 | Luc.ui.Tooltip; | |
136 | >function Tooltip() {} | |
137 | Luc.ui.SomeonesObject; | |
138 | >{a:true} | |
139 | */ | |
140 | 1 | Luc.addSubmodule = function(namespace, obj) { |
141 | 2 | var toAdd; |
142 | 2 | if (Luc.alwaysAddSubmodule || isBrowser) { |
143 | 1 | toAdd = {}; |
144 | 1 | toAdd[namespace] = obj; |
145 | 1 | Luc.Object.merge(Luc, toAdd); |
146 | } | |
147 | }; |
Line | Hits | Source |
---|---|---|
1 | 1 | var is = require('./is'); |
2 | ||
3 | /** | |
4 | * @class Luc.Object | |
5 | * Package for Object methods. Luc.Object.apply and Luc.Object.each | |
6 | * are used very often. mix and apply are aliased to Luc.apply and Luc.mix. | |
7 | */ | |
8 | ||
9 | /** | |
10 | * Apply the properties from fromObject to the toObject. fromObject will | |
11 | * overwrite any shared keys. It can also be used as a simple shallow clone. | |
12 | * | |
13 | var to = {a:1, c:1}, from = {a:2, b:2} | |
14 | Luc.Object.apply(to, from) | |
15 | >Object {a: 2, c: 1, b: 2} | |
16 | to === to | |
17 | >true | |
18 | var clone = Luc.Object.apply({}, from) | |
19 | >undefined | |
20 | clone | |
21 | >Object {a: 2, b: 2} | |
22 | clone === from | |
23 | >false | |
24 | * | |
25 | * No null checks are needed. | |
26 | ||
27 | Luc.apply(undefined, {a:1}) | |
28 | >{a:1} | |
29 | Luc.apply({a: 1}) | |
30 | >{a:1} | |
31 | ||
32 | * | |
33 | * | |
34 | * @param {Object} [toObject] Object to put the properties fromObject on. | |
35 | * @param {Object} [fromObject] Object to put the properties on the toObject | |
36 | * @return {Object} the toObject | |
37 | */ | |
38 | 1 | exports.apply = function(toObject, fromObject) { |
39 | 161 | var to = toObject || {}, |
40 | from = fromObject || {}, | |
41 | prop; | |
42 | ||
43 | 161 | for (prop in from) { |
44 | 394 | if (from.hasOwnProperty(prop)) { |
45 | 394 | to[prop] = from[prop]; |
46 | } | |
47 | } | |
48 | ||
49 | 161 | return to; |
50 | }; | |
51 | ||
52 | /** | |
53 | * Similar to Luc.Object.apply except that the fromObject will | |
54 | * NOT overwrite the keys of the toObject if they are defined. | |
55 | * | |
56 | Luc.mix({a:1,b:2}, {a:3,b:4,c:5}) | |
57 | >{a: 1, b: 2, c: 5} | |
58 | ||
59 | * No null checks are needed. | |
60 | ||
61 | Luc.mix(undefined, {a:1}) | |
62 | >{a:1} | |
63 | Luc.mix({a: 1}) | |
64 | >{a:1} | |
65 | ||
66 | * | |
67 | ||
68 | * @param {Object} [toObject] Object to put the properties fromObject on. | |
69 | * @param {Object} [fromObject] fromObject Object to put the properties on the toObject | |
70 | * @return {Object} the toObject | |
71 | */ | |
72 | 1 | exports.mix = function(toObject, fromObject) { |
73 | 12 | var to = toObject || {}, |
74 | from = fromObject || {}, | |
75 | prop; | |
76 | ||
77 | 12 | for (prop in from) { |
78 | 45 | if (from.hasOwnProperty(prop) && to[prop] === undefined) { |
79 | 44 | to[prop] = from[prop]; |
80 | } | |
81 | } | |
82 | ||
83 | 12 | return to; |
84 | }; | |
85 | ||
86 | /** | |
87 | * Iterate over an objects properties | |
88 | * as key value "pairs" with the passed in function. | |
89 | * | |
90 | var thisArg = {val:'c'}; | |
91 | Luc.Object.each({ | |
92 | u: 'L' | |
93 | }, function(key, value) { | |
94 | console.log(value + key + this.val) | |
95 | }, thisArg) | |
96 | ||
97 | >Luc | |
98 | ||
99 | * @param {Object} obj the object to iterate over | |
100 | * @param {Function} fn the function to call | |
101 | * @param {String} fn.key the object key | |
102 | * @param {Object} fn.value the object value | |
103 | * @param {Object} [thisArg] | |
104 | * @param {Object} [config] | |
105 | * @param {Boolean} config.ownProperties set to false | |
106 | * to iterate over all of the objects enumerable properties. | |
107 | */ | |
108 | 1 | exports.each = function(obj, fn, thisArg, config) { |
109 | 86 | var key, value, |
110 | allProperties = config && config.ownProperties === false; | |
111 | ||
112 | 86 | if (allProperties) { |
113 | 14 | for (key in obj) { |
114 | 68 | fn.call(thisArg, key, obj[key]); |
115 | } | |
116 | } else { | |
117 | 72 | for (key in obj) { |
118 | 180 | if (obj.hasOwnProperty(key)) { |
119 | 176 | fn.call(thisArg, key, obj[key]); |
120 | } | |
121 | } | |
122 | } | |
123 | }; | |
124 | ||
125 | /** | |
126 | * Take an array of strings and an array/arguments of | |
127 | * values and return an object of key value pairs | |
128 | * based off each arrays index. It is useful for taking | |
129 | * a long list of arguments and creating an object that can | |
130 | * be passed to other methods. | |
131 | * | |
132 | function longArgs(a,b,c,d,e,f) { | |
133 | return Luc.Object.toObject(['a','b', 'c', 'd', 'e', 'f'], arguments) | |
134 | } | |
135 | ||
136 | longArgs(1,2,3,4,5,6,7,8,9) | |
137 | ||
138 | >Object {a: 1, b: 2, c: 3, d: 4, e: 5â¦} | |
139 | a: 1 | |
140 | b: 2 | |
141 | c: 3 | |
142 | d: 4 | |
143 | e: 5 | |
144 | f: 6 | |
145 | ||
146 | longArgs(1,2,3) | |
147 | ||
148 | >Object {a: 1, b: 2, c: 3, d: undefined, e: undefinedâ¦} | |
149 | a: 1 | |
150 | b: 2 | |
151 | c: 3 | |
152 | d: undefined | |
153 | e: undefined | |
154 | f: undefined | |
155 | ||
156 | * @param {String[]} strings | |
157 | * @param {Array/arguments} values | |
158 | * @return {Object} | |
159 | */ | |
160 | 1 | exports.toObject = function(strings, values) { |
161 | 2 | var obj = {}, |
162 | i = 0, | |
163 | len = strings.length; | |
164 | 2 | for (; i < len; ++i) { |
165 | 4 | obj[strings[i]] = values[i]; |
166 | } | |
167 | ||
168 | 2 | return obj; |
169 | }; | |
170 | ||
171 | /** | |
172 | * Return key value pairs from the object if the | |
173 | * filterFn returns a truthy value. | |
174 | * | |
175 | Luc.Object.filter({ | |
176 | a: false, | |
177 | b: true, | |
178 | c: false | |
179 | }, function(key, value) { | |
180 | return key === 'a' || value | |
181 | }) | |
182 | >[{key: 'a', value: false}, {key: 'b', value: true}] | |
183 | ||
184 | Luc.Object.filter({ | |
185 | a: false, | |
186 | b: true, | |
187 | c: false | |
188 | }, function(key, value) { | |
189 | return key === 'a' || value | |
190 | }, undefined, { | |
191 | keys: true | |
192 | }) | |
193 | >['a', 'b'] | |
194 | * | |
195 | * @param {Object} obj the object to iterate over | |
196 | * @param {Function} filterFn the function to call, return a truthy value | |
197 | * to add the key value pair | |
198 | * @param {String} filterFn.key the object key | |
199 | * @param {Object} filterFn.value the object value | |
200 | * @param {Object} [thisArg] | |
201 | * @param {Object} [config] | |
202 | * @param {Boolean} config.ownProperties set to false | |
203 | * to iterate over all of the objects enumerable properties. | |
204 | * | |
205 | * @param {Boolean} config.keys set to true to return | |
206 | * just the keys. | |
207 | * | |
208 | * @param {Boolean} config.values set to true to return | |
209 | * just the values. | |
210 | * | |
211 | * @return {Object[]/String[]} Array of key value pairs in the form | |
212 | * of {key: 'key', value: value}. If keys or values is true on the config | |
213 | * just the keys or values are returned. | |
214 | * | |
215 | */ | |
216 | 1 | exports.filter = function(obj, filterFn, thisArg, c) { |
217 | 16 | var values = [], |
218 | config = c || {}; | |
219 | ||
220 | 16 | exports.each(obj, function(key, value) { |
221 | 70 | if (filterFn.call(thisArg, key, value)) { |
222 | 49 | if (config.keys === true) { |
223 | 47 | values.push(key); |
224 | 2 | } else if (config.values === true) { |
225 | 0 | values.push(value); |
226 | } else { | |
227 | 2 | values.push({ |
228 | value: value, | |
229 | key: key | |
230 | }); | |
231 | } | |
232 | } | |
233 | }, thisArg, config); | |
234 | ||
235 | 16 | return values; |
236 | }; | |
237 | ||
238 | /** | |
239 | * Merge the values from object2 into object1. The values will only be | |
240 | * merged in if object1's value for the key is null or undefined. Nested objects | |
241 | * are handled in the same way. Array values will not be merged. | |
242 | * | |
243 | Luc.Object.merge({a: 1}, {b: 2}) | |
244 | >{a: 1, b: 2} | |
245 | ||
246 | Luc.Object.merge({a: {a: 1} }, {b:2, a: {b: 2}}) | |
247 | >{b: 2, a: {a:1, b:2} } | |
248 | ||
249 | * @param {Object} object1 | |
250 | * @param {Object} object2 | |
251 | * @return {Object} | |
252 | */ | |
253 | 1 | function merge(obj1, obj2) { |
254 | 13 | exports.each(obj2, function(key, value) { |
255 | 17 | var obj1Value = obj1[key]; |
256 | 17 | if (obj1Value == undefined) { |
257 | 5 | obj1[key] = value; |
258 | 12 | } else if (is.isObject(obj1Value)) { |
259 | 6 | merge(obj1[key], obj2[key]); |
260 | } | |
261 | }); | |
262 | ||
263 | 13 | return obj1; |
264 | } | |
265 | ||
266 | 1 | exports.merge = merge; |