| 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; |