|
120 | 120 | return target;
|
121 | 121 | };
|
122 | 122 |
|
| 123 | + // Adds a function to the pending array that sets the value if it's not created yet (for circular references) |
| 124 | + // e.g. var a = {}; var b = {parent: a}; a.b = b; |
| 125 | + // otherwise it looks if the value got already cloned it set's a reference to it |
| 126 | + // e.g. var a = {answer: 42}; var b = {first: a, second: a}; |
| 127 | + // otherwise it clones the value |
| 128 | + function cloneIfNotExsists(clone, key, value, history) { |
| 129 | + var stack = history.stack; |
| 130 | + var processed = history.processed; |
| 131 | + var clones = history.clones; |
| 132 | + var pending = history.pending; |
| 133 | + |
| 134 | + for (var stackIndex = 0; stackIndex < stack.length; stackIndex++) { |
| 135 | + if (value[key] === stack[stackIndex]) { |
| 136 | + pending[stackIndex].push(function (cloned) { |
| 137 | + clone[key] = cloned; |
| 138 | + }); |
| 139 | + return; |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + for (var processedIndex = 0; processedIndex < processed.length; processedIndex++) { |
| 144 | + if (value[key] === processed[processedIndex]) { |
| 145 | + clone[key] = clones[processedIndex]; |
| 146 | + return; |
| 147 | + } |
| 148 | + } |
| 149 | + clone[key] = internalClone(value[key], true, history); |
| 150 | + } |
| 151 | + |
| 152 | + // internal clone helper function |
| 153 | + function internalClone(value, deepClone, history) { |
| 154 | + if (value == null) { |
| 155 | + return value; |
| 156 | + } |
| 157 | + |
| 158 | + history.stack.push(value); |
| 159 | + history.pending.push([]); |
| 160 | + |
| 161 | + var type = blocks.type(value); |
| 162 | + var clone; |
| 163 | + var key; |
| 164 | + var pending; |
| 165 | + |
| 166 | + if (blocks.isFunction(value.clone)) { |
| 167 | + clone = value.clone(deepClone); |
| 168 | + } |
| 169 | + |
| 170 | + if (type == 'object' || type == 'array') { |
| 171 | + if (value.constructor === Object) { |
| 172 | + clone = {}; |
| 173 | + } else if (type == 'object'){ |
| 174 | + clone = new value.constructor(); |
| 175 | + } else { |
| 176 | + clone = []; |
| 177 | + } |
| 178 | + |
| 179 | + for (key in value) { |
| 180 | + if (!deepClone) { |
| 181 | + clone[key] = value[key]; |
| 182 | + } else { |
| 183 | + cloneIfNotExsists(clone, key, value, history); |
| 184 | + } |
| 185 | + } |
| 186 | + } else if (type == 'date') { |
| 187 | + clone = new Date(value.getFullYear(), value.getMonth(), value.getDate(), |
| 188 | + value.getHours(), value.getMinutes(), value.getSeconds(), value.getMilliseconds()); |
| 189 | + } else if (type == 'string') { |
| 190 | + history.pending.pop(); |
| 191 | + history.stack.pop(); |
| 192 | + return value.toString(); |
| 193 | + } else if (type == 'regexp') { |
| 194 | + var flags = ''; |
| 195 | + if (value.global) { |
| 196 | + flags += 'g'; |
| 197 | + } |
| 198 | + if (value.ignoreCase) { |
| 199 | + flags += 'i'; |
| 200 | + } |
| 201 | + if (value.multiline) { |
| 202 | + flags += 'm'; |
| 203 | + } |
| 204 | + clone = new RegExp(value.source, flags); |
| 205 | + clone.lastIndex = value.lastIndex; |
| 206 | + } |
| 207 | + |
| 208 | + history.stack.pop(); |
| 209 | + if (clone) { |
| 210 | + history.processed.push(value); |
| 211 | + history.clones.push(clone); |
| 212 | + pending = history.pending.pop(); |
| 213 | + |
| 214 | + for (var pendingIndex = 0; pendingIndex < pending.length; pendingIndex++) { |
| 215 | + pending[pendingIndex](clone || value); |
| 216 | + } |
| 217 | + } else { |
| 218 | + history.pending.pop(); |
| 219 | + } |
| 220 | + |
| 221 | + return clone || value; |
| 222 | + } |
| 223 | + |
123 | 224 | /**
|
124 | 225 | * @callback iterateCallback
|
125 | 226 | * @param {*} value - The value
|
|
277 | 378 | Class.prototype._super = _super;
|
278 | 379 | } else if (prototype) {
|
279 | 380 | Class.prototype = prototype;
|
| 381 | + Class.prototype.constructor = Class; |
280 | 382 | }
|
281 | 383 |
|
282 | 384 | return Class;
|
|
556 | 658 | * Clones value. If deepClone is set to true the value will be cloned recursively
|
557 | 659 | *
|
558 | 660 | * @memberof blocks
|
559 |
| - * @param {*} value - |
560 |
| - * @param {boolean} [deepClone] - Description |
561 |
| - * @returns {*} Description |
| 661 | + * @param {*} value - The value to clone. |
| 662 | + * @param {boolean} [deepClone] - If true it will be a deep clone. |
| 663 | + * @returns {*} - The cloned object. |
562 | 664 | *
|
563 | 665 | * @example {javascript}
|
564 | 666 | * var array = [3, 1, 4];
|
|
567 | 669 | * var areEqual = array == cloned;
|
568 | 670 | * // -> false
|
569 | 671 | */
|
570 |
| - clone: function(value, deepClone) { |
571 |
| - if (value == null) { |
572 |
| - return value; |
573 |
| - } |
574 |
| - |
575 |
| - var type = blocks.type(value); |
576 |
| - var clone; |
577 |
| - var key; |
578 |
| - |
579 |
| - if (blocks.isFunction(value.clone)) { |
580 |
| - return value.clone(deepClone); |
| 672 | + clone: function (value, deepClone) { |
| 673 | + var rootClone = false; |
| 674 | + if (!blocks.core.currentClone) { |
| 675 | + blocks.core.currentClone = {stack: [], processed: [], clones: [], pending: []}; |
| 676 | + rootClone = true; |
581 | 677 | }
|
582 |
| - |
583 |
| - if (type == 'array') { |
584 |
| - return value.slice(0); |
585 |
| - } else if (type == 'object') { |
586 |
| - if (value.constructor === Object) { |
587 |
| - clone = {}; |
588 |
| - } else { |
589 |
| - clone = new value.constructor(); |
590 |
| - } |
591 |
| - |
592 |
| - for (key in value) { |
593 |
| - clone[key] = deepClone ? blocks.clone(value[key], true) : value[key]; |
594 |
| - } |
595 |
| - return clone; |
596 |
| - } else if (type == 'date') { |
597 |
| - return new Date(value.getFullYear(), value.getMonth(), value.getDate(), |
598 |
| - value.getHours(), value.getMinutes(), value.getSeconds(), value.getMilliseconds()); |
599 |
| - } else if (type == 'string') { |
600 |
| - return value.toString(); |
601 |
| - } else if (type == 'regexp') { |
602 |
| - var flags = ''; |
603 |
| - if (value.global) { |
604 |
| - flags += 'g'; |
605 |
| - } |
606 |
| - if (value.ignoreCase) { |
607 |
| - flags += 'i'; |
608 |
| - } |
609 |
| - if (value.multiline) { |
610 |
| - flags += 'm'; |
611 |
| - } |
612 |
| - clone = new RegExp(value.source, flags); |
613 |
| - clone.lastIndex = value.lastIndex; |
614 |
| - return clone; |
| 678 | + var clone = internalClone(value, deepClone, blocks.core.currentClone); |
| 679 | + if (rootClone) { |
| 680 | + blocks.core.currentClone = null; |
615 | 681 | }
|
616 |
| - |
617 |
| - return value; |
| 682 | + return clone; |
618 | 683 | },
|
619 | 684 |
|
620 | 685 | /**
|
|
857 | 922 | return bound;
|
858 | 923 | },
|
859 | 924 |
|
| 925 | + /** |
| 926 | + * Converts an array of keys and an array of values to an object. |
| 927 | + * If no value array is specified the values will be set to true. |
| 928 | + * @param {String[]|Object[]} array - The array of keys or an array of |
| 929 | + * objects containing objects with key and value properties. |
| 930 | + * @param {*[]} [values] - The array of values. |
| 931 | + * @returns {Object} - The object with the combined keys and values. |
| 932 | + * |
| 933 | + * @example {javascript} |
| 934 | + * |
| 935 | + * var keys = ['a', 'b', 'c']; |
| 936 | + * var values = [1, 2, 3]; |
| 937 | + * blocks.toObject(keys, values); |
| 938 | + * // -> {a: 1, b: 2, c: 3} |
| 939 | + * |
| 940 | + * blocks.toObject(keys); |
| 941 | + * // -> {a: true, b: true, c: true} |
| 942 | + * |
| 943 | + * blocks.toObject([{key: 'a', value: 1}, {key: 'b', value: 2}]); |
| 944 | + * // -> {a: 1, b: 2} |
| 945 | + */ |
| 946 | + toObject: function (array, values) { |
| 947 | + var result = {}, |
| 948 | + useValuesArray = arguments.length > 1 && values, |
| 949 | + i = 0, |
| 950 | + length = array.length, |
| 951 | + value; |
| 952 | + |
| 953 | + for (; i < length; i++) { |
| 954 | + value = array[i]; |
| 955 | + if (blocks.isArray(value)) { |
| 956 | + result[value[0]] = value[1]; |
| 957 | + } else if (blocks.isObject(value)) { |
| 958 | + result[value.key] = value.value; |
| 959 | + } else { |
| 960 | + result[value] = useValuesArray ? values[i] : true; |
| 961 | + } |
| 962 | + } |
| 963 | + return result; |
| 964 | + }, |
| 965 | + |
| 966 | + /** |
| 967 | + * Converts an given object into an array of objects with properties key and value. |
| 968 | + * Where key is the property name in the original object and value is the value of the property. |
| 969 | + * @param {Object} obj - The object to convert. |
| 970 | + * @returns {Object[]} - An array of objects with the key/value pairs. |
| 971 | + * |
| 972 | + * @example {javascript} |
| 973 | + * var myObject = { |
| 974 | + * a: 1, |
| 975 | + * b: "something" |
| 976 | + * }; |
| 977 | + * blocks.pairs(myObject); |
| 978 | + * // -> [{key: a, value: 1}, {key: b, value: "someting"}] |
| 979 | + */ |
| 980 | + pairs: function (obj) { |
| 981 | + var pairs = []; |
| 982 | + for (var key in obj) { |
| 983 | + pairs.push({key: key, value: obj[key]}); |
| 984 | + } |
| 985 | + return pairs; |
| 986 | + }, |
| 987 | + |
| 988 | + /** |
| 989 | + * Flattens an array of values. |
| 990 | + * @param {Array} collection - The array to flatten. |
| 991 | + * @param {boolean} [shallow=false] - If true makes the flatten shallow. |
| 992 | + * @returns {Array} - The flattend array. |
| 993 | + * |
| 994 | + * @example {javascript} |
| 995 | + * var unflattend = [1, 2, [3, [4, 5]]]; |
| 996 | + * blocks.flatten(unflattend); |
| 997 | + * // -> [1, 2, 3, 4, 5] |
| 998 | + * blocks.flatten(unflattend, true); |
| 999 | + * // -> [1, 2, 3, [4, 5]] |
| 1000 | + */ |
| 1001 | + flatten: function (collection, shallow) { |
| 1002 | + var flatten = blocks.core.flatten; |
| 1003 | + var result = []; |
| 1004 | + var length = collection.length; |
| 1005 | + var value; |
| 1006 | + var index = -1; |
| 1007 | + |
| 1008 | + while (++index < length) { |
| 1009 | + value = collection[index]; |
| 1010 | + flatten(shallow, value, result); |
| 1011 | + } |
| 1012 | + return result; |
| 1013 | + }, |
| 1014 | + |
860 | 1015 | /**
|
861 | 1016 | * Determines if two values are deeply equal.
|
862 | 1017 | * Set deepEqual to false to stop recusively equality checking
|
|
1066 | 1221 | return callback;
|
1067 | 1222 | }
|
1068 | 1223 |
|
| 1224 | + core.flatten = function (shallow, value, result) { |
| 1225 | + if (blocks.isArray(value) || blocks.isArguments(value)) { |
| 1226 | + if (shallow) { |
| 1227 | + result.push.apply(result, value); |
| 1228 | + } else { |
| 1229 | + for (var i = 0; i < value.length; i++) { |
| 1230 | + core.flatten(shallow, value[i], result); |
| 1231 | + } |
| 1232 | + } |
| 1233 | + } else { |
| 1234 | + result.push(value); |
| 1235 | + } |
| 1236 | + }; |
1069 | 1237 |
|
1070 | 1238 | // @debug-code
|
1071 | 1239 |
|
|
0 commit comments