Coverage

97%
629
614
15

array.js

100%
114
114
0
LineHitsSource
11var arraySlice = Array.prototype.slice,
2 compare = require('./compare'),
3 is = require('./is'),
4 compare = compare.compare;
5
61function _createIteratorFn(fn, c) {
7136 var config = c || {};
8
9136 if(is.isFunction(fn) && (config.type !== 'strict')) {
1050 return c ? fn.bind(c) : fn;
11 }
12
1386 if(config.type === undefined) {
1441 config.type = 'loose';
15 }
16
1786 return function(value) {
18218 return compare(fn, value, config);
19 };
20}
21
221function _createIteratorNotFn(fn, config) {
2347 var functionToNot = _createIteratorFn(fn, config);
24
2547 return function() {
2696 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 */
1131function toArray(item) {
11462 if (Array.isArray(item)) {
11528 return item;
116 }
11734 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 */
1321function last(arr) {
1332 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 */
1461function pluck(arr, key) {
1471 return arr.map(function(value) {
1483 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 */
1641function fromIndex(a, index) {
16515 var arr = is.isArguments(a) ? arraySlice.call(a) : a;
16615 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 */
1931function each(item, fn, thisArg) {
19457 var arr = toArray(item);
19557 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 */
2181function insert(firstArrayOrArgs, secondArrayOrArgs, indexOrAppend) {
21922 var firstArray = arraySlice.call(firstArrayOrArgs),
220 secondArray = arraySlice.call(secondArrayOrArgs),
221 spliceArgs;
222
22322 if(indexOrAppend === true) {
2248 return firstArray.concat(secondArray);
225 }
226
22714 spliceArgs = [indexOrAppend, 0].concat(secondArray);
22814 firstArray.splice.apply(firstArray, spliceArgs);
22914 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 */
2461function removeAtIndex(arr, index) {
24743 var item = arr[index];
24843 arr.splice(index, 1);
24943 return item;
250}
251
2521function _removeFirst(arr, fn) {
25329 var removed = false;
254
25529 arr.some(function(value, index) {
25644 if (fn.apply(this, arguments)) {
25724 removed = removeAtIndex(arr, index);
25824 return true;
259 }
260 });
261
26229 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 */
2741function removeFirst(arr, obj, config) {
27513 var fn = _createIteratorFn(obj, config);
27613 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 */
2881function removeFirstNot(arr, obj, config) {
28916 var fn = _createIteratorNotFn(obj, config);
29016 return _removeFirst(arr, fn);
291}
292
293
2941function _removeAll(arr, fn) {
29516 var indexsToRemove = [],
296 removed = [];
297
29816 arr.forEach(function(value, index) {
29939 if (fn.apply(this, arguments)) {
30019 indexsToRemove.unshift(index);
30119 removed.push(value);
302 }
303 });
304
30516 indexsToRemove.forEach(function(index){
30619 removeAtIndex(arr, index);
307 });
308
30916 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 */
3211function removeAllNot(arr, obj, config) {
3227 var fn = _createIteratorNotFn(obj, config);
3237 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 */
3351function removeAll(arr, obj, config) {
3369 var fn = _createIteratorFn(obj, config);
3379 return _removeAll(arr, fn);
338}
339
3401function _findFirst(arr, fn) {
34161 var item = false;
34261 arr.some(function(value, index) {
343121 if (fn.apply(this, arguments)) {
34439 item = arr[index];
34539 return true;
346 }
347 });
348
34961 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 */
3611function findFirst(arr, obj, config) {
36248 var fn = _createIteratorFn(obj, config);
36348 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 */
3751function findFirstNot(arr, obj, config) {
37613 var fn = _createIteratorNotFn(obj, config);
37713 return _findFirst(arr, fn);
378}
379
3801function _findAll(arr, fn) {
38130 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 */
3931function findAll(arr, obj, config) {
39419 var fn = _createIteratorFn(obj, config);
39519 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 */
4071function findAllNot(arr, obj, config) {
40811 var fn = _createIteratorNotFn(obj, config);
40911 return _findAll(arr, fn);
410}
411
412
4131exports.toArray = toArray;
4141exports.each = each;
4151exports.insert = insert;
4161exports.fromIndex = fromIndex;
4171exports.last = last;
4181exports.pluck = pluck;
419
4201exports.removeAtIndex = removeAtIndex;
4211exports.findFirstNot = findFirstNot;
4221exports.findAllNot = findAllNot;
4231exports.findFirst = findFirst;
4241exports.findAll = findAll;
425
4261exports.removeFirstNot = removeFirstNot;
4271exports.removeAllNot = removeAllNot;
4281exports.removeFirst = removeFirst;
4291exports.removeAll = removeAll;
430
4311(function(){
4321 var _createLastFn = function(fnName) {
4334 var lastName = fnName.replace('First', 'Last');
434
4354 exports[lastName] = function(arr, obj, config) {
43614 var ret;
437
43814 arr.reverse();
43914 ret = exports[fnName](arr, obj, config);
44014 arr.reverse();
441
44214 return ret;
443 };
444
445 }, namesToAddLast = ['findFirstNot', 'findFirst', 'removeFirstNot', 'removeFirst'];
446
4471 namesToAddLast.forEach(function(fnName) {
4484 _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 */

arrayFnGenerator.js

100%
38
38
0
LineHitsSource
11var array = require('./array'),
2 is = require('./is'),
3 Generator;
4
51Generator = {
6 arrayFnNames: ['findFirstNot', 'findAllNot', 'findFirst', 'findAll',
7 'removeFirstNot', 'removeAllNot', 'removeFirst', 'removeAll',
8 'removeLastNot', 'removeLast', 'findLast', 'findLastNot'
9 ],
10
11 createFn: function(arrayFnName, fn) {
12132 return function(arr) {
1336 return array[arrayFnName](arr, fn);
14 };
15 },
16
17 createBoundFn: function(arrayFnName, fnToBind) {
1824 return function(arr, value) {
1911 var fn = fnToBind.apply(this, array.fromIndex(arguments, 1));
2011 return array[arrayFnName](arr, fn);
21 };
22 }
23};
24
251module.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
871(function _createIsFns() {
881 var isToIgnore = ['isRegExp', 'isArguments'];
89
901 Object.keys(is).forEach(function(key) {
9110 var name = key.split('is')[1];
9210 Generator.arrayFnNames.forEach(function(fnName) {
93120 if(isToIgnore.indexOf(key) === -1) {
9496 array[fnName + name] = Generator.createFn(fnName, is[key]);
95 }
96 });
97 });
98}());
99
1001(function _createFalsyFns() {
1011 var usefullFalsyFns = ['findFirstNot', 'findAllNot', 'removeFirstNot', 'removeAllNot',
102 'removeFirst', 'removeAll', 'removeLastNot', 'removeLast', 'findLastNot'];
103
1041 var fns = {
105 'False': function(val) {
10615 return val === false;
107 },
108 'True': function(val) {
10915 return val === true;
110 },
111 'Null': function(val) {
11215 return val === null;
113 },
114 'Undefined': function(val) {
11515 return val === undefined;
116 }
117 };
118
1191 Object.keys(fns).forEach(function(key) {
1204 usefullFalsyFns.forEach(function(fnName) {
12136 array[fnName + key] = Generator.createFn(fnName, fns[key]);
122 });
123 });
124}());
125
1261(function _createBoundFns() {
1271 var fns = {
128 'InstanceOf': function(Constructor) {
1292 return function(value) {
1301 return (value instanceof Constructor);
131 };
132 },'In': function(arr, c) {
1339 var defaultC = {type:'looseRight'};
1349 return function(value) {
13531 if(value !== false) {
13630 var cfg = c || defaultC;
137 //this is a right to left comparison
138 //expected loose behavior should be looseRight
13930 return array.findFirst(arr, value, cfg.type === 'loose' ? defaultC : cfg) !== false;
140 }
141
1421 return arr.indexOf(false) > -1;
143 };
144 }
145 };
146
1471 Object.keys(fns).forEach(function(key) {
1482 Generator.arrayFnNames.forEach(function(fnName) {
14924 array[fnName + key] = Generator.createBoundFn(fnName, fns[key]);
150 });
151 });
152}());

class/base.js

100%
7
7
0
LineHitsSource
11var 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 */
611function Base() {
6218 this.beforeInit.apply(this, arguments);
6318 this.init();
64}
65
661Base.prototype = {
67 /**
68 * By default apply the config to the
69 * instance.
70 */
71 beforeInit: function(config) {
7218 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
821module.exports = Base;

class/composition.js

96%
30
29
1
LineHitsSource
11var 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 */
661function Composition(c) {
6727 var defaults = c.defaults,
68 config = c;
69
7027 if(defaults) {
714 mix(config, config.defaults);
724 delete config.defaults;
73 }
74
7527 apply(this, config);
76}
77
781Composition.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() {
1409 return new this.Constructor();
141 },
142
143 getInstance: function() {
14412 return this.create();
145 },
146
147 validate: function() {
14815 if(this.name === undefined) {
1491 throw new Error('A name must be defined');
150 }
15114 if(!is.isFunction(this.Constructor) && this.create === Composition.prototype.create) {
1521 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) {
16836 return is.isFunction(value);
169 },
170 publicMethods: function(key, value) {
17130 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() {
23313 var methodsObj = this.Constructor && this.Constructor.prototype;
23413 if (this.ignoreMethods) {
2353 methodsObj = apply({}, methodsObj);
2363 array.each(this.ignoreMethods, function(value) {
2373 delete methodsObj[value];
238 });
239 }
240
24113 return methodsObj;
242 },
243
244 getMethodsToCompose: function() {
24513 var methods = this.methods,
246 filterFn;
247
24813 if (is.isArray(methods)) {
2490 return methods;
250 }
251
25213 filterFn = methods;
253
25413 if (is.isString(methods)) {
2557 filterFn = this.filterMethodFns[methods];
256 }
257
258 //Constructors are not needed if create is overwritten
25913 return oFilter(this.getObjectWithMethods(), filterFn, this, {
260 ownProperties: false,
261 keys: true
262 });
263 }
264};
265
2661module.exports = Composition;

class/compositionEnums.js

100%
4
4
0
LineHitsSource
11var 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 */
291module.exports.EventEmitter = {
30 Constructor: EventEmitter,
31 name: 'emitter',
32 methods: 'allMethods'
33};
34
35
36/**
37 * @property {Object} PluginManager
38 */
391module.exports.PluginManager = {
40 name: 'plugins',
41 initAfter: true,
42 Constructor: PluginManager,
43 create: function() {
443 return new this.Constructor({
45 instance: this.instance,
46 instanceArgs: this.instanceArgs
47 });
48 },
49 ignoreMethods: 'defaultPlugin',
50 methods: 'publicMethods'
51};

class/definer.js

100%
113
113
0
LineHitsSource
11var 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
361ClassDefiner = {
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) {
5028 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
5628 options.$super = Super;
57
5828 Constructor = this._createConstructor(options);
59
6028 this._processAfterCreate(Constructor, options);
61
6226 afterDefine.call(Constructor, Constructor);
63
6426 return Constructor;
65 },
66
67 _createConstructor: function(options) {
6828 var superclass = options.$super,
69 Constructor = this._createConstructorFn(options);
70
7128 if(superclass) {
7226 Constructor.prototype = Object.create(superclass.prototype);
73 }
74
7528 return Constructor;
76 },
77
78 _createConstructorFn: function(options) {
7928 var superclass = options.$super,
80 Constructor;
81
8228 if (this._hasConstructorModifyingOptions(options)) {
8311 Constructor = this._createConstructorFromOptions(options);
84 }
8517 else if(!superclass) {
861 Constructor = function() {};
87 }
88 else {
8916 Constructor = function() {
9022 superclass.apply(this, arguments);
91 };
92 }
93
9428 return Constructor;
95 },
96
97 _hasConstructorModifyingOptions: function(options) {
9828 return options.$compositions;
99 },
100
101 _createConstructorFromOptions: function(options) {
10211 var superclass = options.$super,
103 me = this,
104 initBeforeSuperclass,
105 initAfterSuperclass,
106 init;
107
10811 if (!superclass) {
1091 init = this._createInitClassFn(options, {
110 all: true
111 });
112
1131 return function() {
1141 var args = arraySlice.call(arguments);
1151 init.call(this, options, args);
116 };
117 }
118
11910 initBeforeSuperclass = this._createInitClassFn(options, {
120 before: true
121 });
122
12310 initAfterSuperclass = this._createInitClassFn(options, {
124 before: false
125 });
126
12710 return function() {
1287 var args = arraySlice.call(arguments);
129
1307 initBeforeSuperclass.call(this, options, args);
1317 superclass.apply(this, arguments);
1327 initAfterSuperclass.call(this, options, args);
133 };
134 },
135
136 _createInitClassFn: function(options, config) {
13721 var me = this,
138 compositions = this._filterCompositions(config, options.$compositions);
139
14021 if(compositions.length === 0) {
1419 return emptyFn;
142 }
143
14412 return function(options, instanceArgs) {
1459 me._initCompositions.call(this, compositions, instanceArgs);
146 };
147 },
148
149 _filterCompositions: function(config, compositions) {
15021 var before = config.before,
151 filtered = [];
152
15321 if(config.all) {
1541 return compositions;
155 }
156
15720 aEach(compositions, function(composition) {
15828 if(before && composition.initAfter !== true || (!before && composition.initAfter === true)) {
15914 filtered.push(composition);
160 }
161 });
162
16320 return filtered;
164 },
165
166 _processAfterCreate: function($class, options) {
16728 this._applyValuesToProto($class, options);
16828 this._handlePostProcessors($class, options);
169 },
170
171 _applyValuesToProto: function($class, options) {
17228 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
17928 oEach(values, function(key, value) {
18092 if (!this._getProcessorKey(key)) {
18138 proto[key] = value;
182 }
183 }, this);
184 },
185
186 _getProcessorKey: function(key) {
187154 return this.processorKeys[key];
188 },
189
190 _handlePostProcessors: function($class, options) {
19128 oEach(options, function(key, value) {
19262 var method = this._getProcessorKey(key);
193
19462 if (is.isFunction(this[method])) {
19552 this[method].call(this, $class, options[key]);
196 }
197 }, this);
198 },
199
200 _applyMixins: function($class, mixins) {
2016 var proto = $class.prototype;
2026 aEach(mixins, function(mixin) {
203 //accept Constructors or Objects
2047 var toMix = mixin.prototype || mixin;
2057 mix(proto, toMix);
206 });
207 },
208
209 _applyStatics: function($class, statics) {
2109 var prototype = $class.prototype;
211
2129 apply($class, statics);
213
2149 if(prototype.getStaticValue === undefined) {
2157 prototype.getStaticValue = this.getStaticValue;
216 }
217 },
218
219 _applyComposerMethods: function($class, compositions) {
22011 var prototype = $class.prototype,
221 methodsToCompose;
222
22311 aEach(compositions, function(compositionConfig) {
22415 var composition = new Composition(compositionConfig),
225 name = composition.name,
226 Constructor = composition.Constructor;
227
22815 composition.validate();
229
23013 methodsToCompose = composition.getMethodsToCompose();
231
23213 methodsToCompose.forEach(function(key) {
23347 if (prototype[key] === undefined) {
23446 prototype[key] = this._createComposerProtoFn(key, name);
235 }
236 }, this);
237
23813 if(prototype.getComposition === undefined) {
2399 prototype.getComposition = this.getComposition;
240 }
241
242 }, this);
243 },
244
245 _applySuper: function($class, $super) {
24626 var proto,
247 superObj;
248
249 //super can be falsy to signify no superclass
25026 if ($super) {
25124 superObj = {
252 $super: $super,
253 $superclass: $super.prototype
254 };
255
25624 proto = $class.prototype;
257
25824 apply(proto, superObj);
25924 apply($class, superObj);
260
26124 this._addSuperMethod(proto);
262 }
263 },
264
265 _addSuperMethod: function(proto) {
266
26724 function getSuperMethod(callee, sp) {
26817 var $super = sp || proto,
269 key;
270
27117 for (key in $super) {
27274 if ($super[key] === callee) {
273
27410 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
2827 return getSuperMethod(callee, $super.$superclass);
283 }
284
28524 function callSuper(args) {
28610 var superMethod = getSuperMethod(callSuper.caller);
287
28810 if(superMethod) {
2899 return superMethod.apply(this, args);
290 }
291
2921 throw new Error('super method not found.');
293
294 }
295
29624 proto.callSuper = callSuper;
297 },
298
299 _createComposerProtoFn: function(methodName, compositionName) {
30046 return function() {
30126 var comp = this[ClassDefiner.COMPOSITIONS_NAME][compositionName];
30226 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) {
3149 if(!this[ClassDefiner.COMPOSITIONS_NAME]) {
3158 this[ClassDefiner.COMPOSITIONS_NAME] = {};
316 }
317
3189 aEach(compositions, function(compositionConfig) {
31912 var config = apply({
320 instance: this,
321 instanceArgs: instanceArgs
322 }, compositionConfig),
323 composition;
324
32512 composition = new Composition(config);
326
32712 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) {
3355 return this[ClassDefiner.COMPOSITIONS_NAME][key];
336 },
337
338 getStaticValue: function (key, $class) {
33919 var classToFindValue = $class || this.$class,
340 $super,
341 value;
342
34319 value = classToFindValue[key];
344
34519 if(value === undefined) {
34612 $super = classToFindValue.prototype.$super;
34712 if($super) {
3489 return this.getStaticValue(key, $super);
349 }
350 }
351
35210 return value;
353 }
354
355};
356
3571ClassDefiner.define = ClassDefiner.define.bind(ClassDefiner);
358
3591module.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 */

class/plugin.js

100%
5
5
0
LineHitsSource
11var 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 */
101function Plugin(config) {
115 apply(this, config);
12}
13
141Plugin.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
301module.exports = Plugin;

class/pluginManager.js

100%
35
35
0
LineHitsSource
11var 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
91function PluginManager(config) {
103 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 */
681PluginManager.prototype = {
69 /**
70 * @cfg {Constructor} defaultPlugin
71 */
72 defaultPlugin: Plugin,
73
74 /**
75 * @protected
76 */
77 _init: function(instanceValues) {
783 apply(this, instanceValues);
793 this.plugins = [];
803 this._createPlugins();
81 },
82
83 /**
84 * @protected
85 */
86 _createPlugins: function() {
873 aEach(this._getPluginConfigFromInstance(), function(pluginConfig) {
885 this.addPlugin(pluginConfig);
89 }, this);
90 },
91
92 /**
93 * @protected
94 */
95 _getPluginConfigFromInstance: function() {
963 var config = this.instanceArgs[0] || {};
973 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) {
1077 var pluginInstance = this._createPlugin(pluginConfig);
108
1097 this._initPlugin(pluginInstance);
110
1117 this.plugins.push(pluginInstance);
112
1137 return pluginInstance;
114 },
115
116 /**
117 * @protected
118 */
119 _createPlugin: function(config) {
1207 config.owner = this.instance;
121
1227 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
1292 return new config.Constructor(apply(config, {
130 Constructor: undefined
131 }));
132 }
133
1345 return new this.defaultPlugin(config);
135 },
136
137 /**
138 * @protected
139 */
140 _initPlugin: function(plugin) {
1417 if (is.isFunction(plugin.init)) {
1425 plugin.init(this.instance);
143 }
144 },
145
146 /**
147 * Call destroy on all of the plugins
148 * and remove them.
149 */
150 destroyAllPlugins: function() {
1511 this.plugins.forEach(function(plugin) {
1522 this._destroyPlugin(plugin);
153 }, this);
154
1551 this.plugins = [];
156 },
157
158 _destroyPlugin: function(plugin) {
1593 if (is.isFunction(plugin.destroy)) {
1603 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) {
1711 var plugin = this.getPlugin(obj);
172
1731 if(plugin) {
1741 this._destroyPlugin(plugin);
1751 arr.removeFirst(this.plugins, plugin, {type: 'strict'});
176 }
177
1781 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) {
1934 if (is.isFunction(obj)) {
1942 return arr.findFirstInstanceOf(this.plugins, obj);
195 }
1962 return arr.findFirst(this.plugins, obj, {type: 'loose'});
197 }
198};
199
2001module.exports = PluginManager;

compare.js

93%
88
82
6
LineHitsSource
11var is = require('./is');
2
31function _strict(val1, val2){
4237 return val1 === val2;
5}
6
71function _compareArrayLength(val1, val2) {
826 return(is.isArray(val1) && is.isArray(val2) && val1.length === val2.length);
9}
10
111function _shallowArray(val1, val2) {
121 var i = 0,
13 len;
14
151 if(!_compareArrayLength(val1, val2)) {
160 return false;
17 }
18
191 for(len = val1.length; i < len; ++i) {
200 if(val1[i] !== val2[i]) {
210 return false;
22 }
23 }
24
251 return true;
26}
27
281function _deepArray(val1, val2, config) {
2925 var i = 0,
30 len;
31
3225 if(!_compareArrayLength(val1, val2)) {
3311 return false;
34 }
35
3614 for(len = val1.length; i < len; ++i) {
3725 if(!compare(val1[i],val2[i], config)) {
384 return false;
39 }
40 }
41
4210 return true;
43}
44
451function _compareObjectKeysLength(val1, val2) {
4627 return (is.isObject(val1) && is.isObject(val2) && Object.keys(val1).length === Object.keys(val2).length);
47}
48
491function _shallowObject(val1, val2) {
502 var key, val;
51
522 if (!_compareObjectKeysLength(val1, val2)) {
530 return false;
54 }
55
562 for (key in val1) {
573 if (val1.hasOwnProperty(key)) {
583 value = val1[key];
593 if (!val2.hasOwnProperty(key) || val2[key] !== value) {
601 return false;
61 }
62 }
63 }
64
651 return true;
66}
67
681function _deepObject(val1, val2, config) {
6925 var key, val;
70
7125 if (!_compareObjectKeysLength(val1, val2)) {
729 return false;
73 }
74
7516 for (key in val1) {
7618 if (val1.hasOwnProperty(key)) {
7718 value = val1[key];
7818 if (!val2.hasOwnProperty(key) || compare(value, val2[key], config) !== true) {
794 return false;
80 }
81 }
82 }
83
8412 return true;
85
86}
87
881function _looseObject(val1, val2, config) {
8954 var key, val;
90
9154 if(!(is.isObject(val1) && is.isObject(val2))) {
926 return false;
93 }
94
9548 if(config.type === 'looseRight') {
964 for (key in val2) {
974 if (val2.hasOwnProperty(key)) {
984 value = val2[key];
994 if (compare(value, val1[key], config) !== true) {
1002 return false;
101 }
102 }
103 }
104 }
105 else {
10644 for (key in val1) {
10767 if (val1.hasOwnProperty(key)) {
10867 value = val1[key];
10967 if (compare(value, val2[key], config) !== true) {
11024 return false;
111 }
112 }
113 }
114 }
115
116
11722 return true;
118
119}
120
1211function _date(val1, val2) {
12221 if(is.isDate(val1) && is.isDate(val2)) {
12316 return val1.getTime() === val2.getTime();
124 }
125
1265 return false;
127}
128
1291function _createBoundCompare(object, fn) {
1300 return function(value) {
1310 return fn(object, value);
132 };
133}
134
1351function getCompareFn(object, c) {
136366 var compareFn = _strict,
137 config = c || {},
138 type = config.type;
139
140366 if (type === 'deep' || type === 'loose' || type === 'looseRight' || type === undefined) {
141326 if (is.isObject(object)) {
14279 compareFn = type === 'loose' || type === 'looseRight' ? _looseObject : _deepObject;
143247 } else if (is.isArray(object)) {
14425 compareFn = _deepArray;
145222 } else if (is.isDate(object)) {
14618 compareFn = _date;
147 }
14840 } else if (type === 'shallow') {
1496 if (is.isObject(object)) {
1502 compareFn = _shallowObject;
1514 } else if (is.isArray(object)) {
1521 compareFn = _shallowArray;
1533 } else if (is.isDate(object)) {
1543 compareFn = _date;
155 }
15634 } else if (type !== 'strict') {
157 //we would be doing a strict comparison on a type-o
158 //I think an error is good here.
1591 throw new Error('You passed in an invalid comparison type');
160 }
161
162365 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 */
2121function compare(val1, val2, config) {
213366 return getCompareFn(val1, config)(val1, val2, config);
214}
215
2161exports.compare = compare;

events/eventEmitter.js

100%
8
8
0
LineHitsSource
11var 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 */
131EventEmitter.prototype.once = function(type, listener) {
14 //put in fix for IE 9 and below
155 var self = this,
16 g = function() {
175 self.removeListener(type, g);
185 listener.apply(this, arguments);
19 };
20
215 self.on(type, g);
22
235 return this;
24};
25
261module.exports = EventEmitter;

function.js

98%
68
67
1
LineHitsSource
11var 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
121function _augmentArgs(config, callArgs) {
1316 var configArgs = config.args,
14 index = config.index,
15 argsArray;
16
1716 if (!configArgs) {
185 return callArgs;
19 }
20
2111 if(index === true || is.isNumber(index)) {
2210 if(config.argumentsFirst === false) {
232 return aInsert(configArgs, callArgs, index);
24 }
258 return aInsert(callArgs, configArgs, index);
26 }
27
281 return configArgs;
29}
30
31/**
32 * A reusable empty function
33 * @return {Function}
34 */
351exports.emptyFn = function() {};
36
37/**
38 * A function that throws an error when called.
39 * Useful when defining abstract like classes
40 * @return {Function}
41 */
421exports.abstractFn = function() {
430 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 */
1081exports.createAugmenter = function(fn, config) {
10917 var thisArg = config.thisArg;
110
11117 return function() {
11216 return fn.apply(thisArg || this, _augmentArgs(config, arguments));
113 };
114};
115
1161function _initSequenceFunctions(fns, config) {
1174 var toRun = [];
1184 aEach(fns, function(f) {
11912 var fn = f;
120
12112 if (config) {
1229 fn = exports.createAugmenter(f, config);
123 }
124
12512 toRun.push(fn);
126 });
127
1284 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 */
1611exports.createSequence = function(fns, config) {
1622 var functions = _initSequenceFunctions(fns, config);
163
1642 return function() {
1652 var i = 0,
166 lastFnIndex = functions.length -1;
167
1682 for(;i < lastFnIndex; ++i) {
1694 functions[i].apply(this, arguments);
170 }
171
1722 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 */
2141exports.createSequenceIf = function(fns, config) {
2151 var functions = _initSequenceFunctions(fns, config);
216
2171 return function() {
2181 var value,
219 args = arguments;
220
2211 functions.some(function(fn){
2222 value = fn.apply(this, args);
223
2242 return value === false;
225 }, this);
226
2271 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 */
2581exports.createRelayer = function(fns, config) {
2591 var functions = _initSequenceFunctions(fns, config);
260
2611 return function() {
2621 var value,
263 args = arguments;
264
2651 functions.forEach(function(fn, index) {
2663 if (index === 0) {
2671 value = fn.apply(this, args);
268 } else {
2692 value = fn.apply(this, [value]);
270 }
271 }, this);
272
2731 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 */
3111exports.createThrottled = function(f, millis, config) {
3122 var fn = config ? exports.createAugmenter(f, config) : f,
313 timeOutId = false;
314
3152 if(!millis) {
3161 return fn;
317 }
318
3191 return function() {
320201 var args = arguments;
321
322201 if(timeOutId) {
323200 clearTimeout(timeOutId);
324 }
325
326201 timeOutId = setTimeout(function() {
3271 timeOutId = false;
3281 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 */
3461exports.createDeferred = function(f, millis, config) {
3472 var fn = config ? exports.createAugmenter(f, config) : f;
348
3492 if(!millis) {
3501 return fn;
351 }
352
3531 return function() {
3541 var args = arguments;
355
3561 setTimeout(function() {
3571 fn.apply(this, args);
358 }, millis);
359 };
360};

id.js

33%
6
2
4
LineHitsSource
11var 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 */
221module.exports = function(p) {
230 var prefix = p === undefined ? 'luc-' : p;
24
250 if(ids[prefix] === undefined) {
260 ids[prefix] = 0;
27 }
28
290 return prefix + ids[prefix]++;
30};

is.js

100%
29
29
0
LineHitsSource
11var 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 */
111function isArray(obj) {
12323 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 */
221function isObject(obj) {
23511 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 */
331function isFunction(obj) {
34295 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 */
441function isDate(obj) {
45269 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 */
551function isRegExp(obj) {
562 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 */
661function isNumber(obj) {
677 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 */
771function isString(obj) {
7816 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 */
891function isArguments(obj) {
9018 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 */
1031function isFalsy(obj) {
10416 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 */
1211function isEmpty(obj) {
1229 var empty = false;
123
1249 if (isFalsy(obj)) {
1254 empty = true;
1265 } else if (isArray(obj)) {
1272 empty = obj.length === 0;
1283 } else if (isObject(obj)) {
1292 empty = Object.keys(obj).length === 0;
130 }
131
1329 return empty;
133}
134
1351module.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};

luc.js

94%
39
37
2
LineHitsSource
11var Luc = {},
2 isBrowser = false;
3
41if(typeof window !== 'undefined') {
50 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 */
121module.exports = Luc;
13
141var object = require('./object');
151Luc.Object = object;
16/**
17 * @member Luc
18 * @property O Luc.O
19 * Alias for Luc.Object
20 */
211Luc.O = object;
22
23
24/**
25 * @member Luc
26 * @method apply
27 * @inheritdoc Luc.Object#apply
28 */
291Luc.apply = Luc.Object.apply;
30
31/**
32 * @member Luc
33 * @method mix
34 * @inheritdoc Luc.Object#mix
35 */
361Luc.mix = Luc.Object.mix;
37
38
391var fun = require('./function');
401Luc.Function = fun;
41
42/**
43 * @member Luc
44 * @property F Luc.F
45 * Alias for Luc.Function
46 */
471Luc.F = fun;
48
49/**
50 * @member Luc
51 * @method emptyFn
52 * @inheritdoc Luc.Function#emptyFn
53 */
541Luc.emptyFn = Luc.Function.emptyFn;
55
56/**
57 * @member Luc
58 * @method abstractFn
59 * @inheritdoc Luc.Function#abstractFn
60 */
611Luc.abstractFn = Luc.Function.abstractFn;
62
631var array = require('./array');
641Luc.Array = array;
65
66/**
67 * @member Luc
68 * @property A Luc.A
69 * Alias for Luc.Array
70 */
711Luc.A = array;
72
731Luc.ArrayFnGenerator = require('./arrayFnGenerator');
74
751Luc.apply(Luc, require('./is'));
76
771var EventEmitter = require('./events/eventEmitter');
78
791Luc.EventEmitter = EventEmitter;
80
811var Base = require('./class/base');
82
831Luc.Base = Base;
84
851var Definer = require('./class/definer');
86
871Luc.ClassDefiner = Definer;
88
89/**
90 * @member Luc
91 * @method define
92 * @inheritdoc Luc.define#define
93 */
941Luc.define = Definer.define;
95
961Luc.Plugin = require('./class/plugin');
97
981Luc.PluginManager = require('./class/pluginManager');
99
1001Luc.apply(Luc, {
101 compositionEnums: require('./class/compositionEnums')
102});
103
1041Luc.compare = require('./compare').compare;
105
1061Luc.id = require('./id');
107
108
1091if(isBrowser) {
1100 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 */
1401Luc.addSubmodule = function(namespace, obj) {
1412 var toAdd;
1422 if (Luc.alwaysAddSubmodule || isBrowser) {
1431 toAdd = {};
1441 toAdd[namespace] = obj;
1451 Luc.Object.merge(Luc, toAdd);
146 }
147};

object.js

97%
45
44
1
LineHitsSource
11var 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 */
381exports.apply = function(toObject, fromObject) {
39161 var to = toObject || {},
40 from = fromObject || {},
41 prop;
42
43161 for (prop in from) {
44394 if (from.hasOwnProperty(prop)) {
45394 to[prop] = from[prop];
46 }
47 }
48
49161 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 */
721exports.mix = function(toObject, fromObject) {
7312 var to = toObject || {},
74 from = fromObject || {},
75 prop;
76
7712 for (prop in from) {
7845 if (from.hasOwnProperty(prop) && to[prop] === undefined) {
7944 to[prop] = from[prop];
80 }
81 }
82
8312 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 */
1081exports.each = function(obj, fn, thisArg, config) {
10986 var key, value,
110 allProperties = config && config.ownProperties === false;
111
11286 if (allProperties) {
11314 for (key in obj) {
11468 fn.call(thisArg, key, obj[key]);
115 }
116 } else {
11772 for (key in obj) {
118180 if (obj.hasOwnProperty(key)) {
119176 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 */
1601exports.toObject = function(strings, values) {
1612 var obj = {},
162 i = 0,
163 len = strings.length;
1642 for (; i < len; ++i) {
1654 obj[strings[i]] = values[i];
166 }
167
1682 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 */
2161exports.filter = function(obj, filterFn, thisArg, c) {
21716 var values = [],
218 config = c || {};
219
22016 exports.each(obj, function(key, value) {
22170 if (filterFn.call(thisArg, key, value)) {
22249 if (config.keys === true) {
22347 values.push(key);
2242 } else if (config.values === true) {
2250 values.push(value);
226 } else {
2272 values.push({
228 value: value,
229 key: key
230 });
231 }
232 }
233 }, thisArg, config);
234
23516 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 */
2531function merge(obj1, obj2) {
25413 exports.each(obj2, function(key, value) {
25517 var obj1Value = obj1[key];
25617 if (obj1Value == undefined) {
2575 obj1[key] = value;
25812 } else if (is.isObject(obj1Value)) {
2596 merge(obj1[key], obj2[key]);
260 }
261 });
262
26313 return obj1;
264}
265
2661exports.merge = merge;