├── .gitignore
├── LICENSE
├── Procfile
├── README.md
├── dist
├── gblock.js
└── gblock.js.map
├── docs
├── index.html
└── screenshot3.png
├── package.json
├── rollup.config.js
├── server
├── api-methods.js
└── server.js
└── src
├── gblock.js
├── shaders
├── fragment-placeholder.glsl
├── fragment.glsl
├── vertex-placeholder.glsl
└── vertex.glsl
└── utils
├── fetch-script.js
└── promise-cache.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (http://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # Typescript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | node_modules
61 | **/.DS_Store
62 | .DS_Store
63 | .idea
64 | .idea/**
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Tomas Polach
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node server/server.js
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gBlock Component for A-Frame
2 |
3 | [A-Frame](https://aframe.io) Component loading [Google Poly](https://poly.google.com) models from https://poly.google.com. Maintained by [archilogic.com](https://archilogic.com).
4 |
5 | ## Demo
6 |
7 | #### [Live demo](http://project.archilogic.com/aframe-gblock/)
8 |
9 | 
10 |
11 | ## Usage
12 |
13 | ```html
14 |
15 |
16 |
17 |
18 |
19 |
20 | // Using Poly API Key
21 |
22 |
23 |
24 | ```
25 |
26 | #### [Run Example](https://codepen.io/tomas-polach/pen/NvJRJe/right?editors=1000)
27 |
28 | ## Want to make changes?
29 |
30 | ### Installation
31 |
32 | #### 1. Make sure you have Node installed.
33 |
34 | On Mac OS X, it's recommended to use [Homebrew](http://brew.sh/) to install Node + [npm](https://www.npmjs.com):
35 |
36 | brew install node
37 |
38 | #### 2. Clone git repo
39 |
40 | git clone https://github.com/archilogic-com/aframe-gblock.git
41 |
42 | #### 3. Install dependencies
43 |
44 | npm install
45 |
46 | #### 5. Start local development server
47 |
48 | npm start
49 |
50 | #### 6. Launch site from your favourite browser:
51 |
52 | [http://localhost:3000/](http://localhost:3000/)
53 |
54 | ## Acknowledgements
55 |
56 | Based on [gltf component](https://aframe.io/docs/0.6.0/components/gltf-model.html) from [A-Frame](https://aframe.io/) using [GLTF loader](https://threejs.org/examples/#webgl_loader_gltf) from [three.js](https://threejs.org/).
57 |
58 | ## License
59 |
60 | Distributed under an [MIT License](LICENSE).
61 |
--------------------------------------------------------------------------------
/dist/gblock.js:
--------------------------------------------------------------------------------
1 | // https://github.com/archilogic-com/aframe-gblock
2 | (function () {
3 | 'use strict';
4 |
5 | /**
6 | * Appends the elements of `values` to `array`.
7 | *
8 | * @private
9 | * @param {Array} array The array to modify.
10 | * @param {Array} values The values to append.
11 | * @returns {Array} Returns `array`.
12 | */
13 | function arrayPush(array, values) {
14 | var index = -1,
15 | length = values.length,
16 | offset = array.length;
17 |
18 | while (++index < length) {
19 | array[offset + index] = values[index];
20 | }
21 | return array;
22 | }
23 |
24 | var _arrayPush = arrayPush;
25 |
26 | var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
27 |
28 |
29 |
30 |
31 |
32 | function createCommonjsModule(fn, module) {
33 | return module = { exports: {} }, fn(module, module.exports), module.exports;
34 | }
35 |
36 | /** Detect free variable `global` from Node.js. */
37 | var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
38 |
39 | var _freeGlobal = freeGlobal;
40 |
41 | /** Detect free variable `self`. */
42 | var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
43 |
44 | /** Used as a reference to the global object. */
45 | var root = _freeGlobal || freeSelf || Function('return this')();
46 |
47 | var _root = root;
48 |
49 | /** Built-in value references. */
50 | var Symbol = _root.Symbol;
51 |
52 | var _Symbol = Symbol;
53 |
54 | /** Used for built-in method references. */
55 | var objectProto$1 = Object.prototype;
56 |
57 | /** Used to check objects for own properties. */
58 | var hasOwnProperty$1 = objectProto$1.hasOwnProperty;
59 |
60 | /**
61 | * Used to resolve the
62 | * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
63 | * of values.
64 | */
65 | var nativeObjectToString = objectProto$1.toString;
66 |
67 | /** Built-in value references. */
68 | var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
69 |
70 | /**
71 | * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
72 | *
73 | * @private
74 | * @param {*} value The value to query.
75 | * @returns {string} Returns the raw `toStringTag`.
76 | */
77 | function getRawTag(value) {
78 | var isOwn = hasOwnProperty$1.call(value, symToStringTag$1),
79 | tag = value[symToStringTag$1];
80 |
81 | try {
82 | value[symToStringTag$1] = undefined;
83 | var unmasked = true;
84 | } catch (e) {}
85 |
86 | var result = nativeObjectToString.call(value);
87 | if (unmasked) {
88 | if (isOwn) {
89 | value[symToStringTag$1] = tag;
90 | } else {
91 | delete value[symToStringTag$1];
92 | }
93 | }
94 | return result;
95 | }
96 |
97 | var _getRawTag = getRawTag;
98 |
99 | /** Used for built-in method references. */
100 | var objectProto$2 = Object.prototype;
101 |
102 | /**
103 | * Used to resolve the
104 | * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
105 | * of values.
106 | */
107 | var nativeObjectToString$1 = objectProto$2.toString;
108 |
109 | /**
110 | * Converts `value` to a string using `Object.prototype.toString`.
111 | *
112 | * @private
113 | * @param {*} value The value to convert.
114 | * @returns {string} Returns the converted string.
115 | */
116 | function objectToString(value) {
117 | return nativeObjectToString$1.call(value);
118 | }
119 |
120 | var _objectToString = objectToString;
121 |
122 | /** `Object#toString` result references. */
123 | var nullTag = '[object Null]';
124 | var undefinedTag = '[object Undefined]';
125 |
126 | /** Built-in value references. */
127 | var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
128 |
129 | /**
130 | * The base implementation of `getTag` without fallbacks for buggy environments.
131 | *
132 | * @private
133 | * @param {*} value The value to query.
134 | * @returns {string} Returns the `toStringTag`.
135 | */
136 | function baseGetTag(value) {
137 | if (value == null) {
138 | return value === undefined ? undefinedTag : nullTag;
139 | }
140 | return (symToStringTag && symToStringTag in Object(value))
141 | ? _getRawTag(value)
142 | : _objectToString(value);
143 | }
144 |
145 | var _baseGetTag = baseGetTag;
146 |
147 | /**
148 | * Checks if `value` is object-like. A value is object-like if it's not `null`
149 | * and has a `typeof` result of "object".
150 | *
151 | * @static
152 | * @memberOf _
153 | * @since 4.0.0
154 | * @category Lang
155 | * @param {*} value The value to check.
156 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
157 | * @example
158 | *
159 | * _.isObjectLike({});
160 | * // => true
161 | *
162 | * _.isObjectLike([1, 2, 3]);
163 | * // => true
164 | *
165 | * _.isObjectLike(_.noop);
166 | * // => false
167 | *
168 | * _.isObjectLike(null);
169 | * // => false
170 | */
171 | function isObjectLike(value) {
172 | return value != null && typeof value == 'object';
173 | }
174 |
175 | var isObjectLike_1 = isObjectLike;
176 |
177 | /** `Object#toString` result references. */
178 | var argsTag = '[object Arguments]';
179 |
180 | /**
181 | * The base implementation of `_.isArguments`.
182 | *
183 | * @private
184 | * @param {*} value The value to check.
185 | * @returns {boolean} Returns `true` if `value` is an `arguments` object,
186 | */
187 | function baseIsArguments(value) {
188 | return isObjectLike_1(value) && _baseGetTag(value) == argsTag;
189 | }
190 |
191 | var _baseIsArguments = baseIsArguments;
192 |
193 | /** Used for built-in method references. */
194 | var objectProto = Object.prototype;
195 |
196 | /** Used to check objects for own properties. */
197 | var hasOwnProperty = objectProto.hasOwnProperty;
198 |
199 | /** Built-in value references. */
200 | var propertyIsEnumerable = objectProto.propertyIsEnumerable;
201 |
202 | /**
203 | * Checks if `value` is likely an `arguments` object.
204 | *
205 | * @static
206 | * @memberOf _
207 | * @since 0.1.0
208 | * @category Lang
209 | * @param {*} value The value to check.
210 | * @returns {boolean} Returns `true` if `value` is an `arguments` object,
211 | * else `false`.
212 | * @example
213 | *
214 | * _.isArguments(function() { return arguments; }());
215 | * // => true
216 | *
217 | * _.isArguments([1, 2, 3]);
218 | * // => false
219 | */
220 | var isArguments = _baseIsArguments(function() { return arguments; }()) ? _baseIsArguments : function(value) {
221 | return isObjectLike_1(value) && hasOwnProperty.call(value, 'callee') &&
222 | !propertyIsEnumerable.call(value, 'callee');
223 | };
224 |
225 | var isArguments_1 = isArguments;
226 |
227 | /**
228 | * Checks if `value` is classified as an `Array` object.
229 | *
230 | * @static
231 | * @memberOf _
232 | * @since 0.1.0
233 | * @category Lang
234 | * @param {*} value The value to check.
235 | * @returns {boolean} Returns `true` if `value` is an array, else `false`.
236 | * @example
237 | *
238 | * _.isArray([1, 2, 3]);
239 | * // => true
240 | *
241 | * _.isArray(document.body.children);
242 | * // => false
243 | *
244 | * _.isArray('abc');
245 | * // => false
246 | *
247 | * _.isArray(_.noop);
248 | * // => false
249 | */
250 | var isArray = Array.isArray;
251 |
252 | var isArray_1 = isArray;
253 |
254 | /** Built-in value references. */
255 | var spreadableSymbol = _Symbol ? _Symbol.isConcatSpreadable : undefined;
256 |
257 | /**
258 | * Checks if `value` is a flattenable `arguments` object or array.
259 | *
260 | * @private
261 | * @param {*} value The value to check.
262 | * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
263 | */
264 | function isFlattenable(value) {
265 | return isArray_1(value) || isArguments_1(value) ||
266 | !!(spreadableSymbol && value && value[spreadableSymbol]);
267 | }
268 |
269 | var _isFlattenable = isFlattenable;
270 |
271 | /**
272 | * The base implementation of `_.flatten` with support for restricting flattening.
273 | *
274 | * @private
275 | * @param {Array} array The array to flatten.
276 | * @param {number} depth The maximum recursion depth.
277 | * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
278 | * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
279 | * @param {Array} [result=[]] The initial result value.
280 | * @returns {Array} Returns the new flattened array.
281 | */
282 | function baseFlatten(array, depth, predicate, isStrict, result) {
283 | var index = -1,
284 | length = array.length;
285 |
286 | predicate || (predicate = _isFlattenable);
287 | result || (result = []);
288 |
289 | while (++index < length) {
290 | var value = array[index];
291 | if (depth > 0 && predicate(value)) {
292 | if (depth > 1) {
293 | // Recursively flatten arrays (susceptible to call stack limits).
294 | baseFlatten(value, depth - 1, predicate, isStrict, result);
295 | } else {
296 | _arrayPush(result, value);
297 | }
298 | } else if (!isStrict) {
299 | result[result.length] = value;
300 | }
301 | }
302 | return result;
303 | }
304 |
305 | var _baseFlatten = baseFlatten;
306 |
307 | /**
308 | * A specialized version of `_.map` for arrays without support for iteratee
309 | * shorthands.
310 | *
311 | * @private
312 | * @param {Array} [array] The array to iterate over.
313 | * @param {Function} iteratee The function invoked per iteration.
314 | * @returns {Array} Returns the new mapped array.
315 | */
316 | function arrayMap(array, iteratee) {
317 | var index = -1,
318 | length = array == null ? 0 : array.length,
319 | result = Array(length);
320 |
321 | while (++index < length) {
322 | result[index] = iteratee(array[index], index, array);
323 | }
324 | return result;
325 | }
326 |
327 | var _arrayMap = arrayMap;
328 |
329 | /**
330 | * Removes all key-value entries from the list cache.
331 | *
332 | * @private
333 | * @name clear
334 | * @memberOf ListCache
335 | */
336 | function listCacheClear() {
337 | this.__data__ = [];
338 | this.size = 0;
339 | }
340 |
341 | var _listCacheClear = listCacheClear;
342 |
343 | /**
344 | * Performs a
345 | * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
346 | * comparison between two values to determine if they are equivalent.
347 | *
348 | * @static
349 | * @memberOf _
350 | * @since 4.0.0
351 | * @category Lang
352 | * @param {*} value The value to compare.
353 | * @param {*} other The other value to compare.
354 | * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
355 | * @example
356 | *
357 | * var object = { 'a': 1 };
358 | * var other = { 'a': 1 };
359 | *
360 | * _.eq(object, object);
361 | * // => true
362 | *
363 | * _.eq(object, other);
364 | * // => false
365 | *
366 | * _.eq('a', 'a');
367 | * // => true
368 | *
369 | * _.eq('a', Object('a'));
370 | * // => false
371 | *
372 | * _.eq(NaN, NaN);
373 | * // => true
374 | */
375 | function eq(value, other) {
376 | return value === other || (value !== value && other !== other);
377 | }
378 |
379 | var eq_1 = eq;
380 |
381 | /**
382 | * Gets the index at which the `key` is found in `array` of key-value pairs.
383 | *
384 | * @private
385 | * @param {Array} array The array to inspect.
386 | * @param {*} key The key to search for.
387 | * @returns {number} Returns the index of the matched value, else `-1`.
388 | */
389 | function assocIndexOf(array, key) {
390 | var length = array.length;
391 | while (length--) {
392 | if (eq_1(array[length][0], key)) {
393 | return length;
394 | }
395 | }
396 | return -1;
397 | }
398 |
399 | var _assocIndexOf = assocIndexOf;
400 |
401 | /** Used for built-in method references. */
402 | var arrayProto = Array.prototype;
403 |
404 | /** Built-in value references. */
405 | var splice = arrayProto.splice;
406 |
407 | /**
408 | * Removes `key` and its value from the list cache.
409 | *
410 | * @private
411 | * @name delete
412 | * @memberOf ListCache
413 | * @param {string} key The key of the value to remove.
414 | * @returns {boolean} Returns `true` if the entry was removed, else `false`.
415 | */
416 | function listCacheDelete(key) {
417 | var data = this.__data__,
418 | index = _assocIndexOf(data, key);
419 |
420 | if (index < 0) {
421 | return false;
422 | }
423 | var lastIndex = data.length - 1;
424 | if (index == lastIndex) {
425 | data.pop();
426 | } else {
427 | splice.call(data, index, 1);
428 | }
429 | --this.size;
430 | return true;
431 | }
432 |
433 | var _listCacheDelete = listCacheDelete;
434 |
435 | /**
436 | * Gets the list cache value for `key`.
437 | *
438 | * @private
439 | * @name get
440 | * @memberOf ListCache
441 | * @param {string} key The key of the value to get.
442 | * @returns {*} Returns the entry value.
443 | */
444 | function listCacheGet(key) {
445 | var data = this.__data__,
446 | index = _assocIndexOf(data, key);
447 |
448 | return index < 0 ? undefined : data[index][1];
449 | }
450 |
451 | var _listCacheGet = listCacheGet;
452 |
453 | /**
454 | * Checks if a list cache value for `key` exists.
455 | *
456 | * @private
457 | * @name has
458 | * @memberOf ListCache
459 | * @param {string} key The key of the entry to check.
460 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
461 | */
462 | function listCacheHas(key) {
463 | return _assocIndexOf(this.__data__, key) > -1;
464 | }
465 |
466 | var _listCacheHas = listCacheHas;
467 |
468 | /**
469 | * Sets the list cache `key` to `value`.
470 | *
471 | * @private
472 | * @name set
473 | * @memberOf ListCache
474 | * @param {string} key The key of the value to set.
475 | * @param {*} value The value to set.
476 | * @returns {Object} Returns the list cache instance.
477 | */
478 | function listCacheSet(key, value) {
479 | var data = this.__data__,
480 | index = _assocIndexOf(data, key);
481 |
482 | if (index < 0) {
483 | ++this.size;
484 | data.push([key, value]);
485 | } else {
486 | data[index][1] = value;
487 | }
488 | return this;
489 | }
490 |
491 | var _listCacheSet = listCacheSet;
492 |
493 | /**
494 | * Creates an list cache object.
495 | *
496 | * @private
497 | * @constructor
498 | * @param {Array} [entries] The key-value pairs to cache.
499 | */
500 | function ListCache(entries) {
501 | var index = -1,
502 | length = entries == null ? 0 : entries.length;
503 |
504 | this.clear();
505 | while (++index < length) {
506 | var entry = entries[index];
507 | this.set(entry[0], entry[1]);
508 | }
509 | }
510 |
511 | // Add methods to `ListCache`.
512 | ListCache.prototype.clear = _listCacheClear;
513 | ListCache.prototype['delete'] = _listCacheDelete;
514 | ListCache.prototype.get = _listCacheGet;
515 | ListCache.prototype.has = _listCacheHas;
516 | ListCache.prototype.set = _listCacheSet;
517 |
518 | var _ListCache = ListCache;
519 |
520 | /**
521 | * Removes all key-value entries from the stack.
522 | *
523 | * @private
524 | * @name clear
525 | * @memberOf Stack
526 | */
527 | function stackClear() {
528 | this.__data__ = new _ListCache;
529 | this.size = 0;
530 | }
531 |
532 | var _stackClear = stackClear;
533 |
534 | /**
535 | * Removes `key` and its value from the stack.
536 | *
537 | * @private
538 | * @name delete
539 | * @memberOf Stack
540 | * @param {string} key The key of the value to remove.
541 | * @returns {boolean} Returns `true` if the entry was removed, else `false`.
542 | */
543 | function stackDelete(key) {
544 | var data = this.__data__,
545 | result = data['delete'](key);
546 |
547 | this.size = data.size;
548 | return result;
549 | }
550 |
551 | var _stackDelete = stackDelete;
552 |
553 | /**
554 | * Gets the stack value for `key`.
555 | *
556 | * @private
557 | * @name get
558 | * @memberOf Stack
559 | * @param {string} key The key of the value to get.
560 | * @returns {*} Returns the entry value.
561 | */
562 | function stackGet(key) {
563 | return this.__data__.get(key);
564 | }
565 |
566 | var _stackGet = stackGet;
567 |
568 | /**
569 | * Checks if a stack value for `key` exists.
570 | *
571 | * @private
572 | * @name has
573 | * @memberOf Stack
574 | * @param {string} key The key of the entry to check.
575 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
576 | */
577 | function stackHas(key) {
578 | return this.__data__.has(key);
579 | }
580 |
581 | var _stackHas = stackHas;
582 |
583 | /**
584 | * Checks if `value` is the
585 | * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
586 | * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
587 | *
588 | * @static
589 | * @memberOf _
590 | * @since 0.1.0
591 | * @category Lang
592 | * @param {*} value The value to check.
593 | * @returns {boolean} Returns `true` if `value` is an object, else `false`.
594 | * @example
595 | *
596 | * _.isObject({});
597 | * // => true
598 | *
599 | * _.isObject([1, 2, 3]);
600 | * // => true
601 | *
602 | * _.isObject(_.noop);
603 | * // => true
604 | *
605 | * _.isObject(null);
606 | * // => false
607 | */
608 | function isObject(value) {
609 | var type = typeof value;
610 | return value != null && (type == 'object' || type == 'function');
611 | }
612 |
613 | var isObject_1 = isObject;
614 |
615 | /** `Object#toString` result references. */
616 | var asyncTag = '[object AsyncFunction]';
617 | var funcTag = '[object Function]';
618 | var genTag = '[object GeneratorFunction]';
619 | var proxyTag = '[object Proxy]';
620 |
621 | /**
622 | * Checks if `value` is classified as a `Function` object.
623 | *
624 | * @static
625 | * @memberOf _
626 | * @since 0.1.0
627 | * @category Lang
628 | * @param {*} value The value to check.
629 | * @returns {boolean} Returns `true` if `value` is a function, else `false`.
630 | * @example
631 | *
632 | * _.isFunction(_);
633 | * // => true
634 | *
635 | * _.isFunction(/abc/);
636 | * // => false
637 | */
638 | function isFunction(value) {
639 | if (!isObject_1(value)) {
640 | return false;
641 | }
642 | // The use of `Object#toString` avoids issues with the `typeof` operator
643 | // in Safari 9 which returns 'object' for typed arrays and other constructors.
644 | var tag = _baseGetTag(value);
645 | return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
646 | }
647 |
648 | var isFunction_1 = isFunction;
649 |
650 | /** Used to detect overreaching core-js shims. */
651 | var coreJsData = _root['__core-js_shared__'];
652 |
653 | var _coreJsData = coreJsData;
654 |
655 | /** Used to detect methods masquerading as native. */
656 | var maskSrcKey = (function() {
657 | var uid = /[^.]+$/.exec(_coreJsData && _coreJsData.keys && _coreJsData.keys.IE_PROTO || '');
658 | return uid ? ('Symbol(src)_1.' + uid) : '';
659 | }());
660 |
661 | /**
662 | * Checks if `func` has its source masked.
663 | *
664 | * @private
665 | * @param {Function} func The function to check.
666 | * @returns {boolean} Returns `true` if `func` is masked, else `false`.
667 | */
668 | function isMasked(func) {
669 | return !!maskSrcKey && (maskSrcKey in func);
670 | }
671 |
672 | var _isMasked = isMasked;
673 |
674 | /** Used for built-in method references. */
675 | var funcProto$1 = Function.prototype;
676 |
677 | /** Used to resolve the decompiled source of functions. */
678 | var funcToString$1 = funcProto$1.toString;
679 |
680 | /**
681 | * Converts `func` to its source code.
682 | *
683 | * @private
684 | * @param {Function} func The function to convert.
685 | * @returns {string} Returns the source code.
686 | */
687 | function toSource(func) {
688 | if (func != null) {
689 | try {
690 | return funcToString$1.call(func);
691 | } catch (e) {}
692 | try {
693 | return (func + '');
694 | } catch (e) {}
695 | }
696 | return '';
697 | }
698 |
699 | var _toSource = toSource;
700 |
701 | /**
702 | * Used to match `RegExp`
703 | * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
704 | */
705 | var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
706 |
707 | /** Used to detect host constructors (Safari). */
708 | var reIsHostCtor = /^\[object .+?Constructor\]$/;
709 |
710 | /** Used for built-in method references. */
711 | var funcProto = Function.prototype;
712 | var objectProto$3 = Object.prototype;
713 |
714 | /** Used to resolve the decompiled source of functions. */
715 | var funcToString = funcProto.toString;
716 |
717 | /** Used to check objects for own properties. */
718 | var hasOwnProperty$2 = objectProto$3.hasOwnProperty;
719 |
720 | /** Used to detect if a method is native. */
721 | var reIsNative = RegExp('^' +
722 | funcToString.call(hasOwnProperty$2).replace(reRegExpChar, '\\$&')
723 | .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
724 | );
725 |
726 | /**
727 | * The base implementation of `_.isNative` without bad shim checks.
728 | *
729 | * @private
730 | * @param {*} value The value to check.
731 | * @returns {boolean} Returns `true` if `value` is a native function,
732 | * else `false`.
733 | */
734 | function baseIsNative(value) {
735 | if (!isObject_1(value) || _isMasked(value)) {
736 | return false;
737 | }
738 | var pattern = isFunction_1(value) ? reIsNative : reIsHostCtor;
739 | return pattern.test(_toSource(value));
740 | }
741 |
742 | var _baseIsNative = baseIsNative;
743 |
744 | /**
745 | * Gets the value at `key` of `object`.
746 | *
747 | * @private
748 | * @param {Object} [object] The object to query.
749 | * @param {string} key The key of the property to get.
750 | * @returns {*} Returns the property value.
751 | */
752 | function getValue(object, key) {
753 | return object == null ? undefined : object[key];
754 | }
755 |
756 | var _getValue = getValue;
757 |
758 | /**
759 | * Gets the native function at `key` of `object`.
760 | *
761 | * @private
762 | * @param {Object} object The object to query.
763 | * @param {string} key The key of the method to get.
764 | * @returns {*} Returns the function if it's native, else `undefined`.
765 | */
766 | function getNative(object, key) {
767 | var value = _getValue(object, key);
768 | return _baseIsNative(value) ? value : undefined;
769 | }
770 |
771 | var _getNative = getNative;
772 |
773 | /* Built-in method references that are verified to be native. */
774 | var Map = _getNative(_root, 'Map');
775 |
776 | var _Map = Map;
777 |
778 | /* Built-in method references that are verified to be native. */
779 | var nativeCreate = _getNative(Object, 'create');
780 |
781 | var _nativeCreate = nativeCreate;
782 |
783 | /**
784 | * Removes all key-value entries from the hash.
785 | *
786 | * @private
787 | * @name clear
788 | * @memberOf Hash
789 | */
790 | function hashClear() {
791 | this.__data__ = _nativeCreate ? _nativeCreate(null) : {};
792 | this.size = 0;
793 | }
794 |
795 | var _hashClear = hashClear;
796 |
797 | /**
798 | * Removes `key` and its value from the hash.
799 | *
800 | * @private
801 | * @name delete
802 | * @memberOf Hash
803 | * @param {Object} hash The hash to modify.
804 | * @param {string} key The key of the value to remove.
805 | * @returns {boolean} Returns `true` if the entry was removed, else `false`.
806 | */
807 | function hashDelete(key) {
808 | var result = this.has(key) && delete this.__data__[key];
809 | this.size -= result ? 1 : 0;
810 | return result;
811 | }
812 |
813 | var _hashDelete = hashDelete;
814 |
815 | /** Used to stand-in for `undefined` hash values. */
816 | var HASH_UNDEFINED = '__lodash_hash_undefined__';
817 |
818 | /** Used for built-in method references. */
819 | var objectProto$4 = Object.prototype;
820 |
821 | /** Used to check objects for own properties. */
822 | var hasOwnProperty$3 = objectProto$4.hasOwnProperty;
823 |
824 | /**
825 | * Gets the hash value for `key`.
826 | *
827 | * @private
828 | * @name get
829 | * @memberOf Hash
830 | * @param {string} key The key of the value to get.
831 | * @returns {*} Returns the entry value.
832 | */
833 | function hashGet(key) {
834 | var data = this.__data__;
835 | if (_nativeCreate) {
836 | var result = data[key];
837 | return result === HASH_UNDEFINED ? undefined : result;
838 | }
839 | return hasOwnProperty$3.call(data, key) ? data[key] : undefined;
840 | }
841 |
842 | var _hashGet = hashGet;
843 |
844 | /** Used for built-in method references. */
845 | var objectProto$5 = Object.prototype;
846 |
847 | /** Used to check objects for own properties. */
848 | var hasOwnProperty$4 = objectProto$5.hasOwnProperty;
849 |
850 | /**
851 | * Checks if a hash value for `key` exists.
852 | *
853 | * @private
854 | * @name has
855 | * @memberOf Hash
856 | * @param {string} key The key of the entry to check.
857 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
858 | */
859 | function hashHas(key) {
860 | var data = this.__data__;
861 | return _nativeCreate ? (data[key] !== undefined) : hasOwnProperty$4.call(data, key);
862 | }
863 |
864 | var _hashHas = hashHas;
865 |
866 | /** Used to stand-in for `undefined` hash values. */
867 | var HASH_UNDEFINED$1 = '__lodash_hash_undefined__';
868 |
869 | /**
870 | * Sets the hash `key` to `value`.
871 | *
872 | * @private
873 | * @name set
874 | * @memberOf Hash
875 | * @param {string} key The key of the value to set.
876 | * @param {*} value The value to set.
877 | * @returns {Object} Returns the hash instance.
878 | */
879 | function hashSet(key, value) {
880 | var data = this.__data__;
881 | this.size += this.has(key) ? 0 : 1;
882 | data[key] = (_nativeCreate && value === undefined) ? HASH_UNDEFINED$1 : value;
883 | return this;
884 | }
885 |
886 | var _hashSet = hashSet;
887 |
888 | /**
889 | * Creates a hash object.
890 | *
891 | * @private
892 | * @constructor
893 | * @param {Array} [entries] The key-value pairs to cache.
894 | */
895 | function Hash(entries) {
896 | var index = -1,
897 | length = entries == null ? 0 : entries.length;
898 |
899 | this.clear();
900 | while (++index < length) {
901 | var entry = entries[index];
902 | this.set(entry[0], entry[1]);
903 | }
904 | }
905 |
906 | // Add methods to `Hash`.
907 | Hash.prototype.clear = _hashClear;
908 | Hash.prototype['delete'] = _hashDelete;
909 | Hash.prototype.get = _hashGet;
910 | Hash.prototype.has = _hashHas;
911 | Hash.prototype.set = _hashSet;
912 |
913 | var _Hash = Hash;
914 |
915 | /**
916 | * Removes all key-value entries from the map.
917 | *
918 | * @private
919 | * @name clear
920 | * @memberOf MapCache
921 | */
922 | function mapCacheClear() {
923 | this.size = 0;
924 | this.__data__ = {
925 | 'hash': new _Hash,
926 | 'map': new (_Map || _ListCache),
927 | 'string': new _Hash
928 | };
929 | }
930 |
931 | var _mapCacheClear = mapCacheClear;
932 |
933 | /**
934 | * Checks if `value` is suitable for use as unique object key.
935 | *
936 | * @private
937 | * @param {*} value The value to check.
938 | * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
939 | */
940 | function isKeyable(value) {
941 | var type = typeof value;
942 | return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
943 | ? (value !== '__proto__')
944 | : (value === null);
945 | }
946 |
947 | var _isKeyable = isKeyable;
948 |
949 | /**
950 | * Gets the data for `map`.
951 | *
952 | * @private
953 | * @param {Object} map The map to query.
954 | * @param {string} key The reference key.
955 | * @returns {*} Returns the map data.
956 | */
957 | function getMapData(map, key) {
958 | var data = map.__data__;
959 | return _isKeyable(key)
960 | ? data[typeof key == 'string' ? 'string' : 'hash']
961 | : data.map;
962 | }
963 |
964 | var _getMapData = getMapData;
965 |
966 | /**
967 | * Removes `key` and its value from the map.
968 | *
969 | * @private
970 | * @name delete
971 | * @memberOf MapCache
972 | * @param {string} key The key of the value to remove.
973 | * @returns {boolean} Returns `true` if the entry was removed, else `false`.
974 | */
975 | function mapCacheDelete(key) {
976 | var result = _getMapData(this, key)['delete'](key);
977 | this.size -= result ? 1 : 0;
978 | return result;
979 | }
980 |
981 | var _mapCacheDelete = mapCacheDelete;
982 |
983 | /**
984 | * Gets the map value for `key`.
985 | *
986 | * @private
987 | * @name get
988 | * @memberOf MapCache
989 | * @param {string} key The key of the value to get.
990 | * @returns {*} Returns the entry value.
991 | */
992 | function mapCacheGet(key) {
993 | return _getMapData(this, key).get(key);
994 | }
995 |
996 | var _mapCacheGet = mapCacheGet;
997 |
998 | /**
999 | * Checks if a map value for `key` exists.
1000 | *
1001 | * @private
1002 | * @name has
1003 | * @memberOf MapCache
1004 | * @param {string} key The key of the entry to check.
1005 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
1006 | */
1007 | function mapCacheHas(key) {
1008 | return _getMapData(this, key).has(key);
1009 | }
1010 |
1011 | var _mapCacheHas = mapCacheHas;
1012 |
1013 | /**
1014 | * Sets the map `key` to `value`.
1015 | *
1016 | * @private
1017 | * @name set
1018 | * @memberOf MapCache
1019 | * @param {string} key The key of the value to set.
1020 | * @param {*} value The value to set.
1021 | * @returns {Object} Returns the map cache instance.
1022 | */
1023 | function mapCacheSet(key, value) {
1024 | var data = _getMapData(this, key),
1025 | size = data.size;
1026 |
1027 | data.set(key, value);
1028 | this.size += data.size == size ? 0 : 1;
1029 | return this;
1030 | }
1031 |
1032 | var _mapCacheSet = mapCacheSet;
1033 |
1034 | /**
1035 | * Creates a map cache object to store key-value pairs.
1036 | *
1037 | * @private
1038 | * @constructor
1039 | * @param {Array} [entries] The key-value pairs to cache.
1040 | */
1041 | function MapCache(entries) {
1042 | var index = -1,
1043 | length = entries == null ? 0 : entries.length;
1044 |
1045 | this.clear();
1046 | while (++index < length) {
1047 | var entry = entries[index];
1048 | this.set(entry[0], entry[1]);
1049 | }
1050 | }
1051 |
1052 | // Add methods to `MapCache`.
1053 | MapCache.prototype.clear = _mapCacheClear;
1054 | MapCache.prototype['delete'] = _mapCacheDelete;
1055 | MapCache.prototype.get = _mapCacheGet;
1056 | MapCache.prototype.has = _mapCacheHas;
1057 | MapCache.prototype.set = _mapCacheSet;
1058 |
1059 | var _MapCache = MapCache;
1060 |
1061 | /** Used as the size to enable large array optimizations. */
1062 | var LARGE_ARRAY_SIZE = 200;
1063 |
1064 | /**
1065 | * Sets the stack `key` to `value`.
1066 | *
1067 | * @private
1068 | * @name set
1069 | * @memberOf Stack
1070 | * @param {string} key The key of the value to set.
1071 | * @param {*} value The value to set.
1072 | * @returns {Object} Returns the stack cache instance.
1073 | */
1074 | function stackSet(key, value) {
1075 | var data = this.__data__;
1076 | if (data instanceof _ListCache) {
1077 | var pairs = data.__data__;
1078 | if (!_Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
1079 | pairs.push([key, value]);
1080 | this.size = ++data.size;
1081 | return this;
1082 | }
1083 | data = this.__data__ = new _MapCache(pairs);
1084 | }
1085 | data.set(key, value);
1086 | this.size = data.size;
1087 | return this;
1088 | }
1089 |
1090 | var _stackSet = stackSet;
1091 |
1092 | /**
1093 | * Creates a stack cache object to store key-value pairs.
1094 | *
1095 | * @private
1096 | * @constructor
1097 | * @param {Array} [entries] The key-value pairs to cache.
1098 | */
1099 | function Stack(entries) {
1100 | var data = this.__data__ = new _ListCache(entries);
1101 | this.size = data.size;
1102 | }
1103 |
1104 | // Add methods to `Stack`.
1105 | Stack.prototype.clear = _stackClear;
1106 | Stack.prototype['delete'] = _stackDelete;
1107 | Stack.prototype.get = _stackGet;
1108 | Stack.prototype.has = _stackHas;
1109 | Stack.prototype.set = _stackSet;
1110 |
1111 | var _Stack = Stack;
1112 |
1113 | /** Used to stand-in for `undefined` hash values. */
1114 | var HASH_UNDEFINED$2 = '__lodash_hash_undefined__';
1115 |
1116 | /**
1117 | * Adds `value` to the array cache.
1118 | *
1119 | * @private
1120 | * @name add
1121 | * @memberOf SetCache
1122 | * @alias push
1123 | * @param {*} value The value to cache.
1124 | * @returns {Object} Returns the cache instance.
1125 | */
1126 | function setCacheAdd(value) {
1127 | this.__data__.set(value, HASH_UNDEFINED$2);
1128 | return this;
1129 | }
1130 |
1131 | var _setCacheAdd = setCacheAdd;
1132 |
1133 | /**
1134 | * Checks if `value` is in the array cache.
1135 | *
1136 | * @private
1137 | * @name has
1138 | * @memberOf SetCache
1139 | * @param {*} value The value to search for.
1140 | * @returns {number} Returns `true` if `value` is found, else `false`.
1141 | */
1142 | function setCacheHas(value) {
1143 | return this.__data__.has(value);
1144 | }
1145 |
1146 | var _setCacheHas = setCacheHas;
1147 |
1148 | /**
1149 | *
1150 | * Creates an array cache object to store unique values.
1151 | *
1152 | * @private
1153 | * @constructor
1154 | * @param {Array} [values] The values to cache.
1155 | */
1156 | function SetCache(values) {
1157 | var index = -1,
1158 | length = values == null ? 0 : values.length;
1159 |
1160 | this.__data__ = new _MapCache;
1161 | while (++index < length) {
1162 | this.add(values[index]);
1163 | }
1164 | }
1165 |
1166 | // Add methods to `SetCache`.
1167 | SetCache.prototype.add = SetCache.prototype.push = _setCacheAdd;
1168 | SetCache.prototype.has = _setCacheHas;
1169 |
1170 | var _SetCache = SetCache;
1171 |
1172 | /**
1173 | * A specialized version of `_.some` for arrays without support for iteratee
1174 | * shorthands.
1175 | *
1176 | * @private
1177 | * @param {Array} [array] The array to iterate over.
1178 | * @param {Function} predicate The function invoked per iteration.
1179 | * @returns {boolean} Returns `true` if any element passes the predicate check,
1180 | * else `false`.
1181 | */
1182 | function arraySome(array, predicate) {
1183 | var index = -1,
1184 | length = array == null ? 0 : array.length;
1185 |
1186 | while (++index < length) {
1187 | if (predicate(array[index], index, array)) {
1188 | return true;
1189 | }
1190 | }
1191 | return false;
1192 | }
1193 |
1194 | var _arraySome = arraySome;
1195 |
1196 | /**
1197 | * Checks if a `cache` value for `key` exists.
1198 | *
1199 | * @private
1200 | * @param {Object} cache The cache to query.
1201 | * @param {string} key The key of the entry to check.
1202 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
1203 | */
1204 | function cacheHas(cache, key) {
1205 | return cache.has(key);
1206 | }
1207 |
1208 | var _cacheHas = cacheHas;
1209 |
1210 | /** Used to compose bitmasks for value comparisons. */
1211 | var COMPARE_PARTIAL_FLAG$2 = 1;
1212 | var COMPARE_UNORDERED_FLAG$1 = 2;
1213 |
1214 | /**
1215 | * A specialized version of `baseIsEqualDeep` for arrays with support for
1216 | * partial deep comparisons.
1217 | *
1218 | * @private
1219 | * @param {Array} array The array to compare.
1220 | * @param {Array} other The other array to compare.
1221 | * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
1222 | * @param {Function} customizer The function to customize comparisons.
1223 | * @param {Function} equalFunc The function to determine equivalents of values.
1224 | * @param {Object} stack Tracks traversed `array` and `other` objects.
1225 | * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
1226 | */
1227 | function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
1228 | var isPartial = bitmask & COMPARE_PARTIAL_FLAG$2,
1229 | arrLength = array.length,
1230 | othLength = other.length;
1231 |
1232 | if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
1233 | return false;
1234 | }
1235 | // Assume cyclic values are equal.
1236 | var stacked = stack.get(array);
1237 | if (stacked && stack.get(other)) {
1238 | return stacked == other;
1239 | }
1240 | var index = -1,
1241 | result = true,
1242 | seen = (bitmask & COMPARE_UNORDERED_FLAG$1) ? new _SetCache : undefined;
1243 |
1244 | stack.set(array, other);
1245 | stack.set(other, array);
1246 |
1247 | // Ignore non-index properties.
1248 | while (++index < arrLength) {
1249 | var arrValue = array[index],
1250 | othValue = other[index];
1251 |
1252 | if (customizer) {
1253 | var compared = isPartial
1254 | ? customizer(othValue, arrValue, index, other, array, stack)
1255 | : customizer(arrValue, othValue, index, array, other, stack);
1256 | }
1257 | if (compared !== undefined) {
1258 | if (compared) {
1259 | continue;
1260 | }
1261 | result = false;
1262 | break;
1263 | }
1264 | // Recursively compare arrays (susceptible to call stack limits).
1265 | if (seen) {
1266 | if (!_arraySome(other, function(othValue, othIndex) {
1267 | if (!_cacheHas(seen, othIndex) &&
1268 | (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
1269 | return seen.push(othIndex);
1270 | }
1271 | })) {
1272 | result = false;
1273 | break;
1274 | }
1275 | } else if (!(
1276 | arrValue === othValue ||
1277 | equalFunc(arrValue, othValue, bitmask, customizer, stack)
1278 | )) {
1279 | result = false;
1280 | break;
1281 | }
1282 | }
1283 | stack['delete'](array);
1284 | stack['delete'](other);
1285 | return result;
1286 | }
1287 |
1288 | var _equalArrays = equalArrays;
1289 |
1290 | /** Built-in value references. */
1291 | var Uint8Array = _root.Uint8Array;
1292 |
1293 | var _Uint8Array = Uint8Array;
1294 |
1295 | /**
1296 | * Converts `map` to its key-value pairs.
1297 | *
1298 | * @private
1299 | * @param {Object} map The map to convert.
1300 | * @returns {Array} Returns the key-value pairs.
1301 | */
1302 | function mapToArray(map) {
1303 | var index = -1,
1304 | result = Array(map.size);
1305 |
1306 | map.forEach(function(value, key) {
1307 | result[++index] = [key, value];
1308 | });
1309 | return result;
1310 | }
1311 |
1312 | var _mapToArray = mapToArray;
1313 |
1314 | /**
1315 | * Converts `set` to an array of its values.
1316 | *
1317 | * @private
1318 | * @param {Object} set The set to convert.
1319 | * @returns {Array} Returns the values.
1320 | */
1321 | function setToArray(set) {
1322 | var index = -1,
1323 | result = Array(set.size);
1324 |
1325 | set.forEach(function(value) {
1326 | result[++index] = value;
1327 | });
1328 | return result;
1329 | }
1330 |
1331 | var _setToArray = setToArray;
1332 |
1333 | /** Used to compose bitmasks for value comparisons. */
1334 | var COMPARE_PARTIAL_FLAG$3 = 1;
1335 | var COMPARE_UNORDERED_FLAG$2 = 2;
1336 |
1337 | /** `Object#toString` result references. */
1338 | var boolTag = '[object Boolean]';
1339 | var dateTag = '[object Date]';
1340 | var errorTag = '[object Error]';
1341 | var mapTag = '[object Map]';
1342 | var numberTag = '[object Number]';
1343 | var regexpTag = '[object RegExp]';
1344 | var setTag = '[object Set]';
1345 | var stringTag = '[object String]';
1346 | var symbolTag = '[object Symbol]';
1347 |
1348 | var arrayBufferTag = '[object ArrayBuffer]';
1349 | var dataViewTag = '[object DataView]';
1350 |
1351 | /** Used to convert symbols to primitives and strings. */
1352 | var symbolProto = _Symbol ? _Symbol.prototype : undefined;
1353 | var symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;
1354 |
1355 | /**
1356 | * A specialized version of `baseIsEqualDeep` for comparing objects of
1357 | * the same `toStringTag`.
1358 | *
1359 | * **Note:** This function only supports comparing values with tags of
1360 | * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
1361 | *
1362 | * @private
1363 | * @param {Object} object The object to compare.
1364 | * @param {Object} other The other object to compare.
1365 | * @param {string} tag The `toStringTag` of the objects to compare.
1366 | * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
1367 | * @param {Function} customizer The function to customize comparisons.
1368 | * @param {Function} equalFunc The function to determine equivalents of values.
1369 | * @param {Object} stack Tracks traversed `object` and `other` objects.
1370 | * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
1371 | */
1372 | function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
1373 | switch (tag) {
1374 | case dataViewTag:
1375 | if ((object.byteLength != other.byteLength) ||
1376 | (object.byteOffset != other.byteOffset)) {
1377 | return false;
1378 | }
1379 | object = object.buffer;
1380 | other = other.buffer;
1381 |
1382 | case arrayBufferTag:
1383 | if ((object.byteLength != other.byteLength) ||
1384 | !equalFunc(new _Uint8Array(object), new _Uint8Array(other))) {
1385 | return false;
1386 | }
1387 | return true;
1388 |
1389 | case boolTag:
1390 | case dateTag:
1391 | case numberTag:
1392 | // Coerce booleans to `1` or `0` and dates to milliseconds.
1393 | // Invalid dates are coerced to `NaN`.
1394 | return eq_1(+object, +other);
1395 |
1396 | case errorTag:
1397 | return object.name == other.name && object.message == other.message;
1398 |
1399 | case regexpTag:
1400 | case stringTag:
1401 | // Coerce regexes to strings and treat strings, primitives and objects,
1402 | // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
1403 | // for more details.
1404 | return object == (other + '');
1405 |
1406 | case mapTag:
1407 | var convert = _mapToArray;
1408 |
1409 | case setTag:
1410 | var isPartial = bitmask & COMPARE_PARTIAL_FLAG$3;
1411 | convert || (convert = _setToArray);
1412 |
1413 | if (object.size != other.size && !isPartial) {
1414 | return false;
1415 | }
1416 | // Assume cyclic values are equal.
1417 | var stacked = stack.get(object);
1418 | if (stacked) {
1419 | return stacked == other;
1420 | }
1421 | bitmask |= COMPARE_UNORDERED_FLAG$2;
1422 |
1423 | // Recursively compare objects (susceptible to call stack limits).
1424 | stack.set(object, other);
1425 | var result = _equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
1426 | stack['delete'](object);
1427 | return result;
1428 |
1429 | case symbolTag:
1430 | if (symbolValueOf) {
1431 | return symbolValueOf.call(object) == symbolValueOf.call(other);
1432 | }
1433 | }
1434 | return false;
1435 | }
1436 |
1437 | var _equalByTag = equalByTag;
1438 |
1439 | /**
1440 | * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
1441 | * `keysFunc` and `symbolsFunc` to get the enumerable property names and
1442 | * symbols of `object`.
1443 | *
1444 | * @private
1445 | * @param {Object} object The object to query.
1446 | * @param {Function} keysFunc The function to get the keys of `object`.
1447 | * @param {Function} symbolsFunc The function to get the symbols of `object`.
1448 | * @returns {Array} Returns the array of property names and symbols.
1449 | */
1450 | function baseGetAllKeys(object, keysFunc, symbolsFunc) {
1451 | var result = keysFunc(object);
1452 | return isArray_1(object) ? result : _arrayPush(result, symbolsFunc(object));
1453 | }
1454 |
1455 | var _baseGetAllKeys = baseGetAllKeys;
1456 |
1457 | /**
1458 | * A specialized version of `_.filter` for arrays without support for
1459 | * iteratee shorthands.
1460 | *
1461 | * @private
1462 | * @param {Array} [array] The array to iterate over.
1463 | * @param {Function} predicate The function invoked per iteration.
1464 | * @returns {Array} Returns the new filtered array.
1465 | */
1466 | function arrayFilter(array, predicate) {
1467 | var index = -1,
1468 | length = array == null ? 0 : array.length,
1469 | resIndex = 0,
1470 | result = [];
1471 |
1472 | while (++index < length) {
1473 | var value = array[index];
1474 | if (predicate(value, index, array)) {
1475 | result[resIndex++] = value;
1476 | }
1477 | }
1478 | return result;
1479 | }
1480 |
1481 | var _arrayFilter = arrayFilter;
1482 |
1483 | /**
1484 | * This method returns a new empty array.
1485 | *
1486 | * @static
1487 | * @memberOf _
1488 | * @since 4.13.0
1489 | * @category Util
1490 | * @returns {Array} Returns the new empty array.
1491 | * @example
1492 | *
1493 | * var arrays = _.times(2, _.stubArray);
1494 | *
1495 | * console.log(arrays);
1496 | * // => [[], []]
1497 | *
1498 | * console.log(arrays[0] === arrays[1]);
1499 | * // => false
1500 | */
1501 | function stubArray() {
1502 | return [];
1503 | }
1504 |
1505 | var stubArray_1 = stubArray;
1506 |
1507 | /** Used for built-in method references. */
1508 | var objectProto$8 = Object.prototype;
1509 |
1510 | /** Built-in value references. */
1511 | var propertyIsEnumerable$1 = objectProto$8.propertyIsEnumerable;
1512 |
1513 | /* Built-in method references for those with the same name as other `lodash` methods. */
1514 | var nativeGetSymbols = Object.getOwnPropertySymbols;
1515 |
1516 | /**
1517 | * Creates an array of the own enumerable symbols of `object`.
1518 | *
1519 | * @private
1520 | * @param {Object} object The object to query.
1521 | * @returns {Array} Returns the array of symbols.
1522 | */
1523 | var getSymbols = !nativeGetSymbols ? stubArray_1 : function(object) {
1524 | if (object == null) {
1525 | return [];
1526 | }
1527 | object = Object(object);
1528 | return _arrayFilter(nativeGetSymbols(object), function(symbol) {
1529 | return propertyIsEnumerable$1.call(object, symbol);
1530 | });
1531 | };
1532 |
1533 | var _getSymbols = getSymbols;
1534 |
1535 | /**
1536 | * The base implementation of `_.times` without support for iteratee shorthands
1537 | * or max array length checks.
1538 | *
1539 | * @private
1540 | * @param {number} n The number of times to invoke `iteratee`.
1541 | * @param {Function} iteratee The function invoked per iteration.
1542 | * @returns {Array} Returns the array of results.
1543 | */
1544 | function baseTimes(n, iteratee) {
1545 | var index = -1,
1546 | result = Array(n);
1547 |
1548 | while (++index < n) {
1549 | result[index] = iteratee(index);
1550 | }
1551 | return result;
1552 | }
1553 |
1554 | var _baseTimes = baseTimes;
1555 |
1556 | /**
1557 | * This method returns `false`.
1558 | *
1559 | * @static
1560 | * @memberOf _
1561 | * @since 4.13.0
1562 | * @category Util
1563 | * @returns {boolean} Returns `false`.
1564 | * @example
1565 | *
1566 | * _.times(2, _.stubFalse);
1567 | * // => [false, false]
1568 | */
1569 | function stubFalse() {
1570 | return false;
1571 | }
1572 |
1573 | var stubFalse_1 = stubFalse;
1574 |
1575 | var isBuffer_1 = createCommonjsModule(function (module, exports) {
1576 | /** Detect free variable `exports`. */
1577 | var freeExports = 'object' == 'object' && exports && !exports.nodeType && exports;
1578 |
1579 | /** Detect free variable `module`. */
1580 | var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;
1581 |
1582 | /** Detect the popular CommonJS extension `module.exports`. */
1583 | var moduleExports = freeModule && freeModule.exports === freeExports;
1584 |
1585 | /** Built-in value references. */
1586 | var Buffer = moduleExports ? _root.Buffer : undefined;
1587 |
1588 | /* Built-in method references for those with the same name as other `lodash` methods. */
1589 | var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
1590 |
1591 | /**
1592 | * Checks if `value` is a buffer.
1593 | *
1594 | * @static
1595 | * @memberOf _
1596 | * @since 4.3.0
1597 | * @category Lang
1598 | * @param {*} value The value to check.
1599 | * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
1600 | * @example
1601 | *
1602 | * _.isBuffer(new Buffer(2));
1603 | * // => true
1604 | *
1605 | * _.isBuffer(new Uint8Array(2));
1606 | * // => false
1607 | */
1608 | var isBuffer = nativeIsBuffer || stubFalse_1;
1609 |
1610 | module.exports = isBuffer;
1611 | });
1612 |
1613 | /** Used as references for various `Number` constants. */
1614 | var MAX_SAFE_INTEGER = 9007199254740991;
1615 |
1616 | /** Used to detect unsigned integer values. */
1617 | var reIsUint = /^(?:0|[1-9]\d*)$/;
1618 |
1619 | /**
1620 | * Checks if `value` is a valid array-like index.
1621 | *
1622 | * @private
1623 | * @param {*} value The value to check.
1624 | * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
1625 | * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
1626 | */
1627 | function isIndex(value, length) {
1628 | length = length == null ? MAX_SAFE_INTEGER : length;
1629 | return !!length &&
1630 | (typeof value == 'number' || reIsUint.test(value)) &&
1631 | (value > -1 && value % 1 == 0 && value < length);
1632 | }
1633 |
1634 | var _isIndex = isIndex;
1635 |
1636 | /** Used as references for various `Number` constants. */
1637 | var MAX_SAFE_INTEGER$1 = 9007199254740991;
1638 |
1639 | /**
1640 | * Checks if `value` is a valid array-like length.
1641 | *
1642 | * **Note:** This method is loosely based on
1643 | * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
1644 | *
1645 | * @static
1646 | * @memberOf _
1647 | * @since 4.0.0
1648 | * @category Lang
1649 | * @param {*} value The value to check.
1650 | * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
1651 | * @example
1652 | *
1653 | * _.isLength(3);
1654 | * // => true
1655 | *
1656 | * _.isLength(Number.MIN_VALUE);
1657 | * // => false
1658 | *
1659 | * _.isLength(Infinity);
1660 | * // => false
1661 | *
1662 | * _.isLength('3');
1663 | * // => false
1664 | */
1665 | function isLength(value) {
1666 | return typeof value == 'number' &&
1667 | value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER$1;
1668 | }
1669 |
1670 | var isLength_1 = isLength;
1671 |
1672 | /** `Object#toString` result references. */
1673 | var argsTag$2 = '[object Arguments]';
1674 | var arrayTag$1 = '[object Array]';
1675 | var boolTag$1 = '[object Boolean]';
1676 | var dateTag$1 = '[object Date]';
1677 | var errorTag$1 = '[object Error]';
1678 | var funcTag$1 = '[object Function]';
1679 | var mapTag$1 = '[object Map]';
1680 | var numberTag$1 = '[object Number]';
1681 | var objectTag$1 = '[object Object]';
1682 | var regexpTag$1 = '[object RegExp]';
1683 | var setTag$1 = '[object Set]';
1684 | var stringTag$1 = '[object String]';
1685 | var weakMapTag = '[object WeakMap]';
1686 |
1687 | var arrayBufferTag$1 = '[object ArrayBuffer]';
1688 | var dataViewTag$1 = '[object DataView]';
1689 | var float32Tag = '[object Float32Array]';
1690 | var float64Tag = '[object Float64Array]';
1691 | var int8Tag = '[object Int8Array]';
1692 | var int16Tag = '[object Int16Array]';
1693 | var int32Tag = '[object Int32Array]';
1694 | var uint8Tag = '[object Uint8Array]';
1695 | var uint8ClampedTag = '[object Uint8ClampedArray]';
1696 | var uint16Tag = '[object Uint16Array]';
1697 | var uint32Tag = '[object Uint32Array]';
1698 |
1699 | /** Used to identify `toStringTag` values of typed arrays. */
1700 | var typedArrayTags = {};
1701 | typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
1702 | typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
1703 | typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
1704 | typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
1705 | typedArrayTags[uint32Tag] = true;
1706 | typedArrayTags[argsTag$2] = typedArrayTags[arrayTag$1] =
1707 | typedArrayTags[arrayBufferTag$1] = typedArrayTags[boolTag$1] =
1708 | typedArrayTags[dataViewTag$1] = typedArrayTags[dateTag$1] =
1709 | typedArrayTags[errorTag$1] = typedArrayTags[funcTag$1] =
1710 | typedArrayTags[mapTag$1] = typedArrayTags[numberTag$1] =
1711 | typedArrayTags[objectTag$1] = typedArrayTags[regexpTag$1] =
1712 | typedArrayTags[setTag$1] = typedArrayTags[stringTag$1] =
1713 | typedArrayTags[weakMapTag] = false;
1714 |
1715 | /**
1716 | * The base implementation of `_.isTypedArray` without Node.js optimizations.
1717 | *
1718 | * @private
1719 | * @param {*} value The value to check.
1720 | * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
1721 | */
1722 | function baseIsTypedArray(value) {
1723 | return isObjectLike_1(value) &&
1724 | isLength_1(value.length) && !!typedArrayTags[_baseGetTag(value)];
1725 | }
1726 |
1727 | var _baseIsTypedArray = baseIsTypedArray;
1728 |
1729 | /**
1730 | * The base implementation of `_.unary` without support for storing metadata.
1731 | *
1732 | * @private
1733 | * @param {Function} func The function to cap arguments for.
1734 | * @returns {Function} Returns the new capped function.
1735 | */
1736 | function baseUnary(func) {
1737 | return function(value) {
1738 | return func(value);
1739 | };
1740 | }
1741 |
1742 | var _baseUnary = baseUnary;
1743 |
1744 | var _nodeUtil = createCommonjsModule(function (module, exports) {
1745 | /** Detect free variable `exports`. */
1746 | var freeExports = 'object' == 'object' && exports && !exports.nodeType && exports;
1747 |
1748 | /** Detect free variable `module`. */
1749 | var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;
1750 |
1751 | /** Detect the popular CommonJS extension `module.exports`. */
1752 | var moduleExports = freeModule && freeModule.exports === freeExports;
1753 |
1754 | /** Detect free variable `process` from Node.js. */
1755 | var freeProcess = moduleExports && _freeGlobal.process;
1756 |
1757 | /** Used to access faster Node.js helpers. */
1758 | var nodeUtil = (function() {
1759 | try {
1760 | return freeProcess && freeProcess.binding && freeProcess.binding('util');
1761 | } catch (e) {}
1762 | }());
1763 |
1764 | module.exports = nodeUtil;
1765 | });
1766 |
1767 | /* Node.js helper references. */
1768 | var nodeIsTypedArray = _nodeUtil && _nodeUtil.isTypedArray;
1769 |
1770 | /**
1771 | * Checks if `value` is classified as a typed array.
1772 | *
1773 | * @static
1774 | * @memberOf _
1775 | * @since 3.0.0
1776 | * @category Lang
1777 | * @param {*} value The value to check.
1778 | * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
1779 | * @example
1780 | *
1781 | * _.isTypedArray(new Uint8Array);
1782 | * // => true
1783 | *
1784 | * _.isTypedArray([]);
1785 | * // => false
1786 | */
1787 | var isTypedArray = nodeIsTypedArray ? _baseUnary(nodeIsTypedArray) : _baseIsTypedArray;
1788 |
1789 | var isTypedArray_1 = isTypedArray;
1790 |
1791 | /** Used for built-in method references. */
1792 | var objectProto$9 = Object.prototype;
1793 |
1794 | /** Used to check objects for own properties. */
1795 | var hasOwnProperty$7 = objectProto$9.hasOwnProperty;
1796 |
1797 | /**
1798 | * Creates an array of the enumerable property names of the array-like `value`.
1799 | *
1800 | * @private
1801 | * @param {*} value The value to query.
1802 | * @param {boolean} inherited Specify returning inherited property names.
1803 | * @returns {Array} Returns the array of property names.
1804 | */
1805 | function arrayLikeKeys(value, inherited) {
1806 | var isArr = isArray_1(value),
1807 | isArg = !isArr && isArguments_1(value),
1808 | isBuff = !isArr && !isArg && isBuffer_1(value),
1809 | isType = !isArr && !isArg && !isBuff && isTypedArray_1(value),
1810 | skipIndexes = isArr || isArg || isBuff || isType,
1811 | result = skipIndexes ? _baseTimes(value.length, String) : [],
1812 | length = result.length;
1813 |
1814 | for (var key in value) {
1815 | if ((inherited || hasOwnProperty$7.call(value, key)) &&
1816 | !(skipIndexes && (
1817 | // Safari 9 has enumerable `arguments.length` in strict mode.
1818 | key == 'length' ||
1819 | // Node.js 0.10 has enumerable non-index properties on buffers.
1820 | (isBuff && (key == 'offset' || key == 'parent')) ||
1821 | // PhantomJS 2 has enumerable non-index properties on typed arrays.
1822 | (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
1823 | // Skip index properties.
1824 | _isIndex(key, length)
1825 | ))) {
1826 | result.push(key);
1827 | }
1828 | }
1829 | return result;
1830 | }
1831 |
1832 | var _arrayLikeKeys = arrayLikeKeys;
1833 |
1834 | /** Used for built-in method references. */
1835 | var objectProto$11 = Object.prototype;
1836 |
1837 | /**
1838 | * Checks if `value` is likely a prototype object.
1839 | *
1840 | * @private
1841 | * @param {*} value The value to check.
1842 | * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
1843 | */
1844 | function isPrototype(value) {
1845 | var Ctor = value && value.constructor,
1846 | proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto$11;
1847 |
1848 | return value === proto;
1849 | }
1850 |
1851 | var _isPrototype = isPrototype;
1852 |
1853 | /**
1854 | * Creates a unary function that invokes `func` with its argument transformed.
1855 | *
1856 | * @private
1857 | * @param {Function} func The function to wrap.
1858 | * @param {Function} transform The argument transform.
1859 | * @returns {Function} Returns the new function.
1860 | */
1861 | function overArg(func, transform) {
1862 | return function(arg) {
1863 | return func(transform(arg));
1864 | };
1865 | }
1866 |
1867 | var _overArg = overArg;
1868 |
1869 | /* Built-in method references for those with the same name as other `lodash` methods. */
1870 | var nativeKeys = _overArg(Object.keys, Object);
1871 |
1872 | var _nativeKeys = nativeKeys;
1873 |
1874 | /** Used for built-in method references. */
1875 | var objectProto$10 = Object.prototype;
1876 |
1877 | /** Used to check objects for own properties. */
1878 | var hasOwnProperty$8 = objectProto$10.hasOwnProperty;
1879 |
1880 | /**
1881 | * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
1882 | *
1883 | * @private
1884 | * @param {Object} object The object to query.
1885 | * @returns {Array} Returns the array of property names.
1886 | */
1887 | function baseKeys(object) {
1888 | if (!_isPrototype(object)) {
1889 | return _nativeKeys(object);
1890 | }
1891 | var result = [];
1892 | for (var key in Object(object)) {
1893 | if (hasOwnProperty$8.call(object, key) && key != 'constructor') {
1894 | result.push(key);
1895 | }
1896 | }
1897 | return result;
1898 | }
1899 |
1900 | var _baseKeys = baseKeys;
1901 |
1902 | /**
1903 | * Checks if `value` is array-like. A value is considered array-like if it's
1904 | * not a function and has a `value.length` that's an integer greater than or
1905 | * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
1906 | *
1907 | * @static
1908 | * @memberOf _
1909 | * @since 4.0.0
1910 | * @category Lang
1911 | * @param {*} value The value to check.
1912 | * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
1913 | * @example
1914 | *
1915 | * _.isArrayLike([1, 2, 3]);
1916 | * // => true
1917 | *
1918 | * _.isArrayLike(document.body.children);
1919 | * // => true
1920 | *
1921 | * _.isArrayLike('abc');
1922 | * // => true
1923 | *
1924 | * _.isArrayLike(_.noop);
1925 | * // => false
1926 | */
1927 | function isArrayLike(value) {
1928 | return value != null && isLength_1(value.length) && !isFunction_1(value);
1929 | }
1930 |
1931 | var isArrayLike_1 = isArrayLike;
1932 |
1933 | /**
1934 | * Creates an array of the own enumerable property names of `object`.
1935 | *
1936 | * **Note:** Non-object values are coerced to objects. See the
1937 | * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
1938 | * for more details.
1939 | *
1940 | * @static
1941 | * @since 0.1.0
1942 | * @memberOf _
1943 | * @category Object
1944 | * @param {Object} object The object to query.
1945 | * @returns {Array} Returns the array of property names.
1946 | * @example
1947 | *
1948 | * function Foo() {
1949 | * this.a = 1;
1950 | * this.b = 2;
1951 | * }
1952 | *
1953 | * Foo.prototype.c = 3;
1954 | *
1955 | * _.keys(new Foo);
1956 | * // => ['a', 'b'] (iteration order is not guaranteed)
1957 | *
1958 | * _.keys('hi');
1959 | * // => ['0', '1']
1960 | */
1961 | function keys(object) {
1962 | return isArrayLike_1(object) ? _arrayLikeKeys(object) : _baseKeys(object);
1963 | }
1964 |
1965 | var keys_1 = keys;
1966 |
1967 | /**
1968 | * Creates an array of own enumerable property names and symbols of `object`.
1969 | *
1970 | * @private
1971 | * @param {Object} object The object to query.
1972 | * @returns {Array} Returns the array of property names and symbols.
1973 | */
1974 | function getAllKeys(object) {
1975 | return _baseGetAllKeys(object, keys_1, _getSymbols);
1976 | }
1977 |
1978 | var _getAllKeys = getAllKeys;
1979 |
1980 | /** Used to compose bitmasks for value comparisons. */
1981 | var COMPARE_PARTIAL_FLAG$4 = 1;
1982 |
1983 | /** Used for built-in method references. */
1984 | var objectProto$7 = Object.prototype;
1985 |
1986 | /** Used to check objects for own properties. */
1987 | var hasOwnProperty$6 = objectProto$7.hasOwnProperty;
1988 |
1989 | /**
1990 | * A specialized version of `baseIsEqualDeep` for objects with support for
1991 | * partial deep comparisons.
1992 | *
1993 | * @private
1994 | * @param {Object} object The object to compare.
1995 | * @param {Object} other The other object to compare.
1996 | * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
1997 | * @param {Function} customizer The function to customize comparisons.
1998 | * @param {Function} equalFunc The function to determine equivalents of values.
1999 | * @param {Object} stack Tracks traversed `object` and `other` objects.
2000 | * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
2001 | */
2002 | function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
2003 | var isPartial = bitmask & COMPARE_PARTIAL_FLAG$4,
2004 | objProps = _getAllKeys(object),
2005 | objLength = objProps.length,
2006 | othProps = _getAllKeys(other),
2007 | othLength = othProps.length;
2008 |
2009 | if (objLength != othLength && !isPartial) {
2010 | return false;
2011 | }
2012 | var index = objLength;
2013 | while (index--) {
2014 | var key = objProps[index];
2015 | if (!(isPartial ? key in other : hasOwnProperty$6.call(other, key))) {
2016 | return false;
2017 | }
2018 | }
2019 | // Assume cyclic values are equal.
2020 | var stacked = stack.get(object);
2021 | if (stacked && stack.get(other)) {
2022 | return stacked == other;
2023 | }
2024 | var result = true;
2025 | stack.set(object, other);
2026 | stack.set(other, object);
2027 |
2028 | var skipCtor = isPartial;
2029 | while (++index < objLength) {
2030 | key = objProps[index];
2031 | var objValue = object[key],
2032 | othValue = other[key];
2033 |
2034 | if (customizer) {
2035 | var compared = isPartial
2036 | ? customizer(othValue, objValue, key, other, object, stack)
2037 | : customizer(objValue, othValue, key, object, other, stack);
2038 | }
2039 | // Recursively compare objects (susceptible to call stack limits).
2040 | if (!(compared === undefined
2041 | ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))
2042 | : compared
2043 | )) {
2044 | result = false;
2045 | break;
2046 | }
2047 | skipCtor || (skipCtor = key == 'constructor');
2048 | }
2049 | if (result && !skipCtor) {
2050 | var objCtor = object.constructor,
2051 | othCtor = other.constructor;
2052 |
2053 | // Non `Object` object instances with different constructors are not equal.
2054 | if (objCtor != othCtor &&
2055 | ('constructor' in object && 'constructor' in other) &&
2056 | !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
2057 | typeof othCtor == 'function' && othCtor instanceof othCtor)) {
2058 | result = false;
2059 | }
2060 | }
2061 | stack['delete'](object);
2062 | stack['delete'](other);
2063 | return result;
2064 | }
2065 |
2066 | var _equalObjects = equalObjects;
2067 |
2068 | /* Built-in method references that are verified to be native. */
2069 | var DataView = _getNative(_root, 'DataView');
2070 |
2071 | var _DataView = DataView;
2072 |
2073 | /* Built-in method references that are verified to be native. */
2074 | var Promise$1 = _getNative(_root, 'Promise');
2075 |
2076 | var _Promise = Promise$1;
2077 |
2078 | /* Built-in method references that are verified to be native. */
2079 | var Set = _getNative(_root, 'Set');
2080 |
2081 | var _Set = Set;
2082 |
2083 | /* Built-in method references that are verified to be native. */
2084 | var WeakMap = _getNative(_root, 'WeakMap');
2085 |
2086 | var _WeakMap = WeakMap;
2087 |
2088 | /** `Object#toString` result references. */
2089 | var mapTag$2 = '[object Map]';
2090 | var objectTag$2 = '[object Object]';
2091 | var promiseTag = '[object Promise]';
2092 | var setTag$2 = '[object Set]';
2093 | var weakMapTag$1 = '[object WeakMap]';
2094 |
2095 | var dataViewTag$2 = '[object DataView]';
2096 |
2097 | /** Used to detect maps, sets, and weakmaps. */
2098 | var dataViewCtorString = _toSource(_DataView);
2099 | var mapCtorString = _toSource(_Map);
2100 | var promiseCtorString = _toSource(_Promise);
2101 | var setCtorString = _toSource(_Set);
2102 | var weakMapCtorString = _toSource(_WeakMap);
2103 |
2104 | /**
2105 | * Gets the `toStringTag` of `value`.
2106 | *
2107 | * @private
2108 | * @param {*} value The value to query.
2109 | * @returns {string} Returns the `toStringTag`.
2110 | */
2111 | var getTag = _baseGetTag;
2112 |
2113 | // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
2114 | if ((_DataView && getTag(new _DataView(new ArrayBuffer(1))) != dataViewTag$2) ||
2115 | (_Map && getTag(new _Map) != mapTag$2) ||
2116 | (_Promise && getTag(_Promise.resolve()) != promiseTag) ||
2117 | (_Set && getTag(new _Set) != setTag$2) ||
2118 | (_WeakMap && getTag(new _WeakMap) != weakMapTag$1)) {
2119 | getTag = function(value) {
2120 | var result = _baseGetTag(value),
2121 | Ctor = result == objectTag$2 ? value.constructor : undefined,
2122 | ctorString = Ctor ? _toSource(Ctor) : '';
2123 |
2124 | if (ctorString) {
2125 | switch (ctorString) {
2126 | case dataViewCtorString: return dataViewTag$2;
2127 | case mapCtorString: return mapTag$2;
2128 | case promiseCtorString: return promiseTag;
2129 | case setCtorString: return setTag$2;
2130 | case weakMapCtorString: return weakMapTag$1;
2131 | }
2132 | }
2133 | return result;
2134 | };
2135 | }
2136 |
2137 | var _getTag = getTag;
2138 |
2139 | /** Used to compose bitmasks for value comparisons. */
2140 | var COMPARE_PARTIAL_FLAG$1 = 1;
2141 |
2142 | /** `Object#toString` result references. */
2143 | var argsTag$1 = '[object Arguments]';
2144 | var arrayTag = '[object Array]';
2145 | var objectTag = '[object Object]';
2146 |
2147 | /** Used for built-in method references. */
2148 | var objectProto$6 = Object.prototype;
2149 |
2150 | /** Used to check objects for own properties. */
2151 | var hasOwnProperty$5 = objectProto$6.hasOwnProperty;
2152 |
2153 | /**
2154 | * A specialized version of `baseIsEqual` for arrays and objects which performs
2155 | * deep comparisons and tracks traversed objects enabling objects with circular
2156 | * references to be compared.
2157 | *
2158 | * @private
2159 | * @param {Object} object The object to compare.
2160 | * @param {Object} other The other object to compare.
2161 | * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
2162 | * @param {Function} customizer The function to customize comparisons.
2163 | * @param {Function} equalFunc The function to determine equivalents of values.
2164 | * @param {Object} [stack] Tracks traversed `object` and `other` objects.
2165 | * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
2166 | */
2167 | function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
2168 | var objIsArr = isArray_1(object),
2169 | othIsArr = isArray_1(other),
2170 | objTag = objIsArr ? arrayTag : _getTag(object),
2171 | othTag = othIsArr ? arrayTag : _getTag(other);
2172 |
2173 | objTag = objTag == argsTag$1 ? objectTag : objTag;
2174 | othTag = othTag == argsTag$1 ? objectTag : othTag;
2175 |
2176 | var objIsObj = objTag == objectTag,
2177 | othIsObj = othTag == objectTag,
2178 | isSameTag = objTag == othTag;
2179 |
2180 | if (isSameTag && isBuffer_1(object)) {
2181 | if (!isBuffer_1(other)) {
2182 | return false;
2183 | }
2184 | objIsArr = true;
2185 | objIsObj = false;
2186 | }
2187 | if (isSameTag && !objIsObj) {
2188 | stack || (stack = new _Stack);
2189 | return (objIsArr || isTypedArray_1(object))
2190 | ? _equalArrays(object, other, bitmask, customizer, equalFunc, stack)
2191 | : _equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
2192 | }
2193 | if (!(bitmask & COMPARE_PARTIAL_FLAG$1)) {
2194 | var objIsWrapped = objIsObj && hasOwnProperty$5.call(object, '__wrapped__'),
2195 | othIsWrapped = othIsObj && hasOwnProperty$5.call(other, '__wrapped__');
2196 |
2197 | if (objIsWrapped || othIsWrapped) {
2198 | var objUnwrapped = objIsWrapped ? object.value() : object,
2199 | othUnwrapped = othIsWrapped ? other.value() : other;
2200 |
2201 | stack || (stack = new _Stack);
2202 | return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
2203 | }
2204 | }
2205 | if (!isSameTag) {
2206 | return false;
2207 | }
2208 | stack || (stack = new _Stack);
2209 | return _equalObjects(object, other, bitmask, customizer, equalFunc, stack);
2210 | }
2211 |
2212 | var _baseIsEqualDeep = baseIsEqualDeep;
2213 |
2214 | /**
2215 | * The base implementation of `_.isEqual` which supports partial comparisons
2216 | * and tracks traversed objects.
2217 | *
2218 | * @private
2219 | * @param {*} value The value to compare.
2220 | * @param {*} other The other value to compare.
2221 | * @param {boolean} bitmask The bitmask flags.
2222 | * 1 - Unordered comparison
2223 | * 2 - Partial comparison
2224 | * @param {Function} [customizer] The function to customize comparisons.
2225 | * @param {Object} [stack] Tracks traversed `value` and `other` objects.
2226 | * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
2227 | */
2228 | function baseIsEqual(value, other, bitmask, customizer, stack) {
2229 | if (value === other) {
2230 | return true;
2231 | }
2232 | if (value == null || other == null || (!isObjectLike_1(value) && !isObjectLike_1(other))) {
2233 | return value !== value && other !== other;
2234 | }
2235 | return _baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
2236 | }
2237 |
2238 | var _baseIsEqual = baseIsEqual;
2239 |
2240 | /** Used to compose bitmasks for value comparisons. */
2241 | var COMPARE_PARTIAL_FLAG = 1;
2242 | var COMPARE_UNORDERED_FLAG = 2;
2243 |
2244 | /**
2245 | * The base implementation of `_.isMatch` without support for iteratee shorthands.
2246 | *
2247 | * @private
2248 | * @param {Object} object The object to inspect.
2249 | * @param {Object} source The object of property values to match.
2250 | * @param {Array} matchData The property names, values, and compare flags to match.
2251 | * @param {Function} [customizer] The function to customize comparisons.
2252 | * @returns {boolean} Returns `true` if `object` is a match, else `false`.
2253 | */
2254 | function baseIsMatch(object, source, matchData, customizer) {
2255 | var index = matchData.length,
2256 | length = index,
2257 | noCustomizer = !customizer;
2258 |
2259 | if (object == null) {
2260 | return !length;
2261 | }
2262 | object = Object(object);
2263 | while (index--) {
2264 | var data = matchData[index];
2265 | if ((noCustomizer && data[2])
2266 | ? data[1] !== object[data[0]]
2267 | : !(data[0] in object)
2268 | ) {
2269 | return false;
2270 | }
2271 | }
2272 | while (++index < length) {
2273 | data = matchData[index];
2274 | var key = data[0],
2275 | objValue = object[key],
2276 | srcValue = data[1];
2277 |
2278 | if (noCustomizer && data[2]) {
2279 | if (objValue === undefined && !(key in object)) {
2280 | return false;
2281 | }
2282 | } else {
2283 | var stack = new _Stack;
2284 | if (customizer) {
2285 | var result = customizer(objValue, srcValue, key, object, source, stack);
2286 | }
2287 | if (!(result === undefined
2288 | ? _baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)
2289 | : result
2290 | )) {
2291 | return false;
2292 | }
2293 | }
2294 | }
2295 | return true;
2296 | }
2297 |
2298 | var _baseIsMatch = baseIsMatch;
2299 |
2300 | /**
2301 | * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
2302 | *
2303 | * @private
2304 | * @param {*} value The value to check.
2305 | * @returns {boolean} Returns `true` if `value` if suitable for strict
2306 | * equality comparisons, else `false`.
2307 | */
2308 | function isStrictComparable(value) {
2309 | return value === value && !isObject_1(value);
2310 | }
2311 |
2312 | var _isStrictComparable = isStrictComparable;
2313 |
2314 | /**
2315 | * Gets the property names, values, and compare flags of `object`.
2316 | *
2317 | * @private
2318 | * @param {Object} object The object to query.
2319 | * @returns {Array} Returns the match data of `object`.
2320 | */
2321 | function getMatchData(object) {
2322 | var result = keys_1(object),
2323 | length = result.length;
2324 |
2325 | while (length--) {
2326 | var key = result[length],
2327 | value = object[key];
2328 |
2329 | result[length] = [key, value, _isStrictComparable(value)];
2330 | }
2331 | return result;
2332 | }
2333 |
2334 | var _getMatchData = getMatchData;
2335 |
2336 | /**
2337 | * A specialized version of `matchesProperty` for source values suitable
2338 | * for strict equality comparisons, i.e. `===`.
2339 | *
2340 | * @private
2341 | * @param {string} key The key of the property to get.
2342 | * @param {*} srcValue The value to match.
2343 | * @returns {Function} Returns the new spec function.
2344 | */
2345 | function matchesStrictComparable(key, srcValue) {
2346 | return function(object) {
2347 | if (object == null) {
2348 | return false;
2349 | }
2350 | return object[key] === srcValue &&
2351 | (srcValue !== undefined || (key in Object(object)));
2352 | };
2353 | }
2354 |
2355 | var _matchesStrictComparable = matchesStrictComparable;
2356 |
2357 | /**
2358 | * The base implementation of `_.matches` which doesn't clone `source`.
2359 | *
2360 | * @private
2361 | * @param {Object} source The object of property values to match.
2362 | * @returns {Function} Returns the new spec function.
2363 | */
2364 | function baseMatches(source) {
2365 | var matchData = _getMatchData(source);
2366 | if (matchData.length == 1 && matchData[0][2]) {
2367 | return _matchesStrictComparable(matchData[0][0], matchData[0][1]);
2368 | }
2369 | return function(object) {
2370 | return object === source || _baseIsMatch(object, source, matchData);
2371 | };
2372 | }
2373 |
2374 | var _baseMatches = baseMatches;
2375 |
2376 | /** `Object#toString` result references. */
2377 | var symbolTag$1 = '[object Symbol]';
2378 |
2379 | /**
2380 | * Checks if `value` is classified as a `Symbol` primitive or object.
2381 | *
2382 | * @static
2383 | * @memberOf _
2384 | * @since 4.0.0
2385 | * @category Lang
2386 | * @param {*} value The value to check.
2387 | * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
2388 | * @example
2389 | *
2390 | * _.isSymbol(Symbol.iterator);
2391 | * // => true
2392 | *
2393 | * _.isSymbol('abc');
2394 | * // => false
2395 | */
2396 | function isSymbol(value) {
2397 | return typeof value == 'symbol' ||
2398 | (isObjectLike_1(value) && _baseGetTag(value) == symbolTag$1);
2399 | }
2400 |
2401 | var isSymbol_1 = isSymbol;
2402 |
2403 | /** Used to match property names within property paths. */
2404 | var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/;
2405 | var reIsPlainProp = /^\w*$/;
2406 |
2407 | /**
2408 | * Checks if `value` is a property name and not a property path.
2409 | *
2410 | * @private
2411 | * @param {*} value The value to check.
2412 | * @param {Object} [object] The object to query keys on.
2413 | * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
2414 | */
2415 | function isKey(value, object) {
2416 | if (isArray_1(value)) {
2417 | return false;
2418 | }
2419 | var type = typeof value;
2420 | if (type == 'number' || type == 'symbol' || type == 'boolean' ||
2421 | value == null || isSymbol_1(value)) {
2422 | return true;
2423 | }
2424 | return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
2425 | (object != null && value in Object(object));
2426 | }
2427 |
2428 | var _isKey = isKey;
2429 |
2430 | /** Error message constants. */
2431 | var FUNC_ERROR_TEXT = 'Expected a function';
2432 |
2433 | /**
2434 | * Creates a function that memoizes the result of `func`. If `resolver` is
2435 | * provided, it determines the cache key for storing the result based on the
2436 | * arguments provided to the memoized function. By default, the first argument
2437 | * provided to the memoized function is used as the map cache key. The `func`
2438 | * is invoked with the `this` binding of the memoized function.
2439 | *
2440 | * **Note:** The cache is exposed as the `cache` property on the memoized
2441 | * function. Its creation may be customized by replacing the `_.memoize.Cache`
2442 | * constructor with one whose instances implement the
2443 | * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
2444 | * method interface of `clear`, `delete`, `get`, `has`, and `set`.
2445 | *
2446 | * @static
2447 | * @memberOf _
2448 | * @since 0.1.0
2449 | * @category Function
2450 | * @param {Function} func The function to have its output memoized.
2451 | * @param {Function} [resolver] The function to resolve the cache key.
2452 | * @returns {Function} Returns the new memoized function.
2453 | * @example
2454 | *
2455 | * var object = { 'a': 1, 'b': 2 };
2456 | * var other = { 'c': 3, 'd': 4 };
2457 | *
2458 | * var values = _.memoize(_.values);
2459 | * values(object);
2460 | * // => [1, 2]
2461 | *
2462 | * values(other);
2463 | * // => [3, 4]
2464 | *
2465 | * object.a = 2;
2466 | * values(object);
2467 | * // => [1, 2]
2468 | *
2469 | * // Modify the result cache.
2470 | * values.cache.set(object, ['a', 'b']);
2471 | * values(object);
2472 | * // => ['a', 'b']
2473 | *
2474 | * // Replace `_.memoize.Cache`.
2475 | * _.memoize.Cache = WeakMap;
2476 | */
2477 | function memoize(func, resolver) {
2478 | if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
2479 | throw new TypeError(FUNC_ERROR_TEXT);
2480 | }
2481 | var memoized = function() {
2482 | var args = arguments,
2483 | key = resolver ? resolver.apply(this, args) : args[0],
2484 | cache = memoized.cache;
2485 |
2486 | if (cache.has(key)) {
2487 | return cache.get(key);
2488 | }
2489 | var result = func.apply(this, args);
2490 | memoized.cache = cache.set(key, result) || cache;
2491 | return result;
2492 | };
2493 | memoized.cache = new (memoize.Cache || _MapCache);
2494 | return memoized;
2495 | }
2496 |
2497 | // Expose `MapCache`.
2498 | memoize.Cache = _MapCache;
2499 |
2500 | var memoize_1 = memoize;
2501 |
2502 | /** Used as the maximum memoize cache size. */
2503 | var MAX_MEMOIZE_SIZE = 500;
2504 |
2505 | /**
2506 | * A specialized version of `_.memoize` which clears the memoized function's
2507 | * cache when it exceeds `MAX_MEMOIZE_SIZE`.
2508 | *
2509 | * @private
2510 | * @param {Function} func The function to have its output memoized.
2511 | * @returns {Function} Returns the new memoized function.
2512 | */
2513 | function memoizeCapped(func) {
2514 | var result = memoize_1(func, function(key) {
2515 | if (cache.size === MAX_MEMOIZE_SIZE) {
2516 | cache.clear();
2517 | }
2518 | return key;
2519 | });
2520 |
2521 | var cache = result.cache;
2522 | return result;
2523 | }
2524 |
2525 | var _memoizeCapped = memoizeCapped;
2526 |
2527 | /** Used to match property names within property paths. */
2528 | var reLeadingDot = /^\./;
2529 | var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
2530 |
2531 | /** Used to match backslashes in property paths. */
2532 | var reEscapeChar = /\\(\\)?/g;
2533 |
2534 | /**
2535 | * Converts `string` to a property path array.
2536 | *
2537 | * @private
2538 | * @param {string} string The string to convert.
2539 | * @returns {Array} Returns the property path array.
2540 | */
2541 | var stringToPath = _memoizeCapped(function(string) {
2542 | var result = [];
2543 | if (reLeadingDot.test(string)) {
2544 | result.push('');
2545 | }
2546 | string.replace(rePropName, function(match, number, quote, string) {
2547 | result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
2548 | });
2549 | return result;
2550 | });
2551 |
2552 | var _stringToPath = stringToPath;
2553 |
2554 | /** Used as references for various `Number` constants. */
2555 | var INFINITY = 1 / 0;
2556 |
2557 | /** Used to convert symbols to primitives and strings. */
2558 | var symbolProto$1 = _Symbol ? _Symbol.prototype : undefined;
2559 | var symbolToString = symbolProto$1 ? symbolProto$1.toString : undefined;
2560 |
2561 | /**
2562 | * The base implementation of `_.toString` which doesn't convert nullish
2563 | * values to empty strings.
2564 | *
2565 | * @private
2566 | * @param {*} value The value to process.
2567 | * @returns {string} Returns the string.
2568 | */
2569 | function baseToString(value) {
2570 | // Exit early for strings to avoid a performance hit in some environments.
2571 | if (typeof value == 'string') {
2572 | return value;
2573 | }
2574 | if (isArray_1(value)) {
2575 | // Recursively convert values (susceptible to call stack limits).
2576 | return _arrayMap(value, baseToString) + '';
2577 | }
2578 | if (isSymbol_1(value)) {
2579 | return symbolToString ? symbolToString.call(value) : '';
2580 | }
2581 | var result = (value + '');
2582 | return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
2583 | }
2584 |
2585 | var _baseToString = baseToString;
2586 |
2587 | /**
2588 | * Converts `value` to a string. An empty string is returned for `null`
2589 | * and `undefined` values. The sign of `-0` is preserved.
2590 | *
2591 | * @static
2592 | * @memberOf _
2593 | * @since 4.0.0
2594 | * @category Lang
2595 | * @param {*} value The value to convert.
2596 | * @returns {string} Returns the converted string.
2597 | * @example
2598 | *
2599 | * _.toString(null);
2600 | * // => ''
2601 | *
2602 | * _.toString(-0);
2603 | * // => '-0'
2604 | *
2605 | * _.toString([1, 2, 3]);
2606 | * // => '1,2,3'
2607 | */
2608 | function toString(value) {
2609 | return value == null ? '' : _baseToString(value);
2610 | }
2611 |
2612 | var toString_1 = toString;
2613 |
2614 | /**
2615 | * Casts `value` to a path array if it's not one.
2616 | *
2617 | * @private
2618 | * @param {*} value The value to inspect.
2619 | * @param {Object} [object] The object to query keys on.
2620 | * @returns {Array} Returns the cast property path array.
2621 | */
2622 | function castPath(value, object) {
2623 | if (isArray_1(value)) {
2624 | return value;
2625 | }
2626 | return _isKey(value, object) ? [value] : _stringToPath(toString_1(value));
2627 | }
2628 |
2629 | var _castPath = castPath;
2630 |
2631 | /** Used as references for various `Number` constants. */
2632 | var INFINITY$1 = 1 / 0;
2633 |
2634 | /**
2635 | * Converts `value` to a string key if it's not a string or symbol.
2636 | *
2637 | * @private
2638 | * @param {*} value The value to inspect.
2639 | * @returns {string|symbol} Returns the key.
2640 | */
2641 | function toKey(value) {
2642 | if (typeof value == 'string' || isSymbol_1(value)) {
2643 | return value;
2644 | }
2645 | var result = (value + '');
2646 | return (result == '0' && (1 / value) == -INFINITY$1) ? '-0' : result;
2647 | }
2648 |
2649 | var _toKey = toKey;
2650 |
2651 | /**
2652 | * The base implementation of `_.get` without support for default values.
2653 | *
2654 | * @private
2655 | * @param {Object} object The object to query.
2656 | * @param {Array|string} path The path of the property to get.
2657 | * @returns {*} Returns the resolved value.
2658 | */
2659 | function baseGet(object, path) {
2660 | path = _castPath(path, object);
2661 |
2662 | var index = 0,
2663 | length = path.length;
2664 |
2665 | while (object != null && index < length) {
2666 | object = object[_toKey(path[index++])];
2667 | }
2668 | return (index && index == length) ? object : undefined;
2669 | }
2670 |
2671 | var _baseGet = baseGet;
2672 |
2673 | /**
2674 | * Gets the value at `path` of `object`. If the resolved value is
2675 | * `undefined`, the `defaultValue` is returned in its place.
2676 | *
2677 | * @static
2678 | * @memberOf _
2679 | * @since 3.7.0
2680 | * @category Object
2681 | * @param {Object} object The object to query.
2682 | * @param {Array|string} path The path of the property to get.
2683 | * @param {*} [defaultValue] The value returned for `undefined` resolved values.
2684 | * @returns {*} Returns the resolved value.
2685 | * @example
2686 | *
2687 | * var object = { 'a': [{ 'b': { 'c': 3 } }] };
2688 | *
2689 | * _.get(object, 'a[0].b.c');
2690 | * // => 3
2691 | *
2692 | * _.get(object, ['a', '0', 'b', 'c']);
2693 | * // => 3
2694 | *
2695 | * _.get(object, 'a.b.c', 'default');
2696 | * // => 'default'
2697 | */
2698 | function get(object, path, defaultValue) {
2699 | var result = object == null ? undefined : _baseGet(object, path);
2700 | return result === undefined ? defaultValue : result;
2701 | }
2702 |
2703 | var get_1 = get;
2704 |
2705 | /**
2706 | * The base implementation of `_.hasIn` without support for deep paths.
2707 | *
2708 | * @private
2709 | * @param {Object} [object] The object to query.
2710 | * @param {Array|string} key The key to check.
2711 | * @returns {boolean} Returns `true` if `key` exists, else `false`.
2712 | */
2713 | function baseHasIn(object, key) {
2714 | return object != null && key in Object(object);
2715 | }
2716 |
2717 | var _baseHasIn = baseHasIn;
2718 |
2719 | /**
2720 | * Checks if `path` exists on `object`.
2721 | *
2722 | * @private
2723 | * @param {Object} object The object to query.
2724 | * @param {Array|string} path The path to check.
2725 | * @param {Function} hasFunc The function to check properties.
2726 | * @returns {boolean} Returns `true` if `path` exists, else `false`.
2727 | */
2728 | function hasPath(object, path, hasFunc) {
2729 | path = _castPath(path, object);
2730 |
2731 | var index = -1,
2732 | length = path.length,
2733 | result = false;
2734 |
2735 | while (++index < length) {
2736 | var key = _toKey(path[index]);
2737 | if (!(result = object != null && hasFunc(object, key))) {
2738 | break;
2739 | }
2740 | object = object[key];
2741 | }
2742 | if (result || ++index != length) {
2743 | return result;
2744 | }
2745 | length = object == null ? 0 : object.length;
2746 | return !!length && isLength_1(length) && _isIndex(key, length) &&
2747 | (isArray_1(object) || isArguments_1(object));
2748 | }
2749 |
2750 | var _hasPath = hasPath;
2751 |
2752 | /**
2753 | * Checks if `path` is a direct or inherited property of `object`.
2754 | *
2755 | * @static
2756 | * @memberOf _
2757 | * @since 4.0.0
2758 | * @category Object
2759 | * @param {Object} object The object to query.
2760 | * @param {Array|string} path The path to check.
2761 | * @returns {boolean} Returns `true` if `path` exists, else `false`.
2762 | * @example
2763 | *
2764 | * var object = _.create({ 'a': _.create({ 'b': 2 }) });
2765 | *
2766 | * _.hasIn(object, 'a');
2767 | * // => true
2768 | *
2769 | * _.hasIn(object, 'a.b');
2770 | * // => true
2771 | *
2772 | * _.hasIn(object, ['a', 'b']);
2773 | * // => true
2774 | *
2775 | * _.hasIn(object, 'b');
2776 | * // => false
2777 | */
2778 | function hasIn(object, path) {
2779 | return object != null && _hasPath(object, path, _baseHasIn);
2780 | }
2781 |
2782 | var hasIn_1 = hasIn;
2783 |
2784 | /** Used to compose bitmasks for value comparisons. */
2785 | var COMPARE_PARTIAL_FLAG$5 = 1;
2786 | var COMPARE_UNORDERED_FLAG$3 = 2;
2787 |
2788 | /**
2789 | * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
2790 | *
2791 | * @private
2792 | * @param {string} path The path of the property to get.
2793 | * @param {*} srcValue The value to match.
2794 | * @returns {Function} Returns the new spec function.
2795 | */
2796 | function baseMatchesProperty(path, srcValue) {
2797 | if (_isKey(path) && _isStrictComparable(srcValue)) {
2798 | return _matchesStrictComparable(_toKey(path), srcValue);
2799 | }
2800 | return function(object) {
2801 | var objValue = get_1(object, path);
2802 | return (objValue === undefined && objValue === srcValue)
2803 | ? hasIn_1(object, path)
2804 | : _baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG$5 | COMPARE_UNORDERED_FLAG$3);
2805 | };
2806 | }
2807 |
2808 | var _baseMatchesProperty = baseMatchesProperty;
2809 |
2810 | /**
2811 | * This method returns the first argument it receives.
2812 | *
2813 | * @static
2814 | * @since 0.1.0
2815 | * @memberOf _
2816 | * @category Util
2817 | * @param {*} value Any value.
2818 | * @returns {*} Returns `value`.
2819 | * @example
2820 | *
2821 | * var object = { 'a': 1 };
2822 | *
2823 | * console.log(_.identity(object) === object);
2824 | * // => true
2825 | */
2826 | function identity(value) {
2827 | return value;
2828 | }
2829 |
2830 | var identity_1 = identity;
2831 |
2832 | /**
2833 | * The base implementation of `_.property` without support for deep paths.
2834 | *
2835 | * @private
2836 | * @param {string} key The key of the property to get.
2837 | * @returns {Function} Returns the new accessor function.
2838 | */
2839 | function baseProperty(key) {
2840 | return function(object) {
2841 | return object == null ? undefined : object[key];
2842 | };
2843 | }
2844 |
2845 | var _baseProperty = baseProperty;
2846 |
2847 | /**
2848 | * A specialized version of `baseProperty` which supports deep paths.
2849 | *
2850 | * @private
2851 | * @param {Array|string} path The path of the property to get.
2852 | * @returns {Function} Returns the new accessor function.
2853 | */
2854 | function basePropertyDeep(path) {
2855 | return function(object) {
2856 | return _baseGet(object, path);
2857 | };
2858 | }
2859 |
2860 | var _basePropertyDeep = basePropertyDeep;
2861 |
2862 | /**
2863 | * Creates a function that returns the value at `path` of a given object.
2864 | *
2865 | * @static
2866 | * @memberOf _
2867 | * @since 2.4.0
2868 | * @category Util
2869 | * @param {Array|string} path The path of the property to get.
2870 | * @returns {Function} Returns the new accessor function.
2871 | * @example
2872 | *
2873 | * var objects = [
2874 | * { 'a': { 'b': 2 } },
2875 | * { 'a': { 'b': 1 } }
2876 | * ];
2877 | *
2878 | * _.map(objects, _.property('a.b'));
2879 | * // => [2, 1]
2880 | *
2881 | * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
2882 | * // => [1, 2]
2883 | */
2884 | function property(path) {
2885 | return _isKey(path) ? _baseProperty(_toKey(path)) : _basePropertyDeep(path);
2886 | }
2887 |
2888 | var property_1 = property;
2889 |
2890 | /**
2891 | * The base implementation of `_.iteratee`.
2892 | *
2893 | * @private
2894 | * @param {*} [value=_.identity] The value to convert to an iteratee.
2895 | * @returns {Function} Returns the iteratee.
2896 | */
2897 | function baseIteratee(value) {
2898 | // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
2899 | // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
2900 | if (typeof value == 'function') {
2901 | return value;
2902 | }
2903 | if (value == null) {
2904 | return identity_1;
2905 | }
2906 | if (typeof value == 'object') {
2907 | return isArray_1(value)
2908 | ? _baseMatchesProperty(value[0], value[1])
2909 | : _baseMatches(value);
2910 | }
2911 | return property_1(value);
2912 | }
2913 |
2914 | var _baseIteratee = baseIteratee;
2915 |
2916 | /**
2917 | * Creates a base function for methods like `_.forIn` and `_.forOwn`.
2918 | *
2919 | * @private
2920 | * @param {boolean} [fromRight] Specify iterating from right to left.
2921 | * @returns {Function} Returns the new base function.
2922 | */
2923 | function createBaseFor(fromRight) {
2924 | return function(object, iteratee, keysFunc) {
2925 | var index = -1,
2926 | iterable = Object(object),
2927 | props = keysFunc(object),
2928 | length = props.length;
2929 |
2930 | while (length--) {
2931 | var key = props[fromRight ? length : ++index];
2932 | if (iteratee(iterable[key], key, iterable) === false) {
2933 | break;
2934 | }
2935 | }
2936 | return object;
2937 | };
2938 | }
2939 |
2940 | var _createBaseFor = createBaseFor;
2941 |
2942 | /**
2943 | * The base implementation of `baseForOwn` which iterates over `object`
2944 | * properties returned by `keysFunc` and invokes `iteratee` for each property.
2945 | * Iteratee functions may exit iteration early by explicitly returning `false`.
2946 | *
2947 | * @private
2948 | * @param {Object} object The object to iterate over.
2949 | * @param {Function} iteratee The function invoked per iteration.
2950 | * @param {Function} keysFunc The function to get the keys of `object`.
2951 | * @returns {Object} Returns `object`.
2952 | */
2953 | var baseFor = _createBaseFor();
2954 |
2955 | var _baseFor = baseFor;
2956 |
2957 | /**
2958 | * The base implementation of `_.forOwn` without support for iteratee shorthands.
2959 | *
2960 | * @private
2961 | * @param {Object} object The object to iterate over.
2962 | * @param {Function} iteratee The function invoked per iteration.
2963 | * @returns {Object} Returns `object`.
2964 | */
2965 | function baseForOwn(object, iteratee) {
2966 | return object && _baseFor(object, iteratee, keys_1);
2967 | }
2968 |
2969 | var _baseForOwn = baseForOwn;
2970 |
2971 | /**
2972 | * Creates a `baseEach` or `baseEachRight` function.
2973 | *
2974 | * @private
2975 | * @param {Function} eachFunc The function to iterate over a collection.
2976 | * @param {boolean} [fromRight] Specify iterating from right to left.
2977 | * @returns {Function} Returns the new base function.
2978 | */
2979 | function createBaseEach(eachFunc, fromRight) {
2980 | return function(collection, iteratee) {
2981 | if (collection == null) {
2982 | return collection;
2983 | }
2984 | if (!isArrayLike_1(collection)) {
2985 | return eachFunc(collection, iteratee);
2986 | }
2987 | var length = collection.length,
2988 | index = fromRight ? length : -1,
2989 | iterable = Object(collection);
2990 |
2991 | while ((fromRight ? index-- : ++index < length)) {
2992 | if (iteratee(iterable[index], index, iterable) === false) {
2993 | break;
2994 | }
2995 | }
2996 | return collection;
2997 | };
2998 | }
2999 |
3000 | var _createBaseEach = createBaseEach;
3001 |
3002 | /**
3003 | * The base implementation of `_.forEach` without support for iteratee shorthands.
3004 | *
3005 | * @private
3006 | * @param {Array|Object} collection The collection to iterate over.
3007 | * @param {Function} iteratee The function invoked per iteration.
3008 | * @returns {Array|Object} Returns `collection`.
3009 | */
3010 | var baseEach = _createBaseEach(_baseForOwn);
3011 |
3012 | var _baseEach = baseEach;
3013 |
3014 | /**
3015 | * The base implementation of `_.map` without support for iteratee shorthands.
3016 | *
3017 | * @private
3018 | * @param {Array|Object} collection The collection to iterate over.
3019 | * @param {Function} iteratee The function invoked per iteration.
3020 | * @returns {Array} Returns the new mapped array.
3021 | */
3022 | function baseMap(collection, iteratee) {
3023 | var index = -1,
3024 | result = isArrayLike_1(collection) ? Array(collection.length) : [];
3025 |
3026 | _baseEach(collection, function(value, key, collection) {
3027 | result[++index] = iteratee(value, key, collection);
3028 | });
3029 | return result;
3030 | }
3031 |
3032 | var _baseMap = baseMap;
3033 |
3034 | /**
3035 | * The base implementation of `_.sortBy` which uses `comparer` to define the
3036 | * sort order of `array` and replaces criteria objects with their corresponding
3037 | * values.
3038 | *
3039 | * @private
3040 | * @param {Array} array The array to sort.
3041 | * @param {Function} comparer The function to define sort order.
3042 | * @returns {Array} Returns `array`.
3043 | */
3044 | function baseSortBy(array, comparer) {
3045 | var length = array.length;
3046 |
3047 | array.sort(comparer);
3048 | while (length--) {
3049 | array[length] = array[length].value;
3050 | }
3051 | return array;
3052 | }
3053 |
3054 | var _baseSortBy = baseSortBy;
3055 |
3056 | /**
3057 | * Compares values to sort them in ascending order.
3058 | *
3059 | * @private
3060 | * @param {*} value The value to compare.
3061 | * @param {*} other The other value to compare.
3062 | * @returns {number} Returns the sort order indicator for `value`.
3063 | */
3064 | function compareAscending(value, other) {
3065 | if (value !== other) {
3066 | var valIsDefined = value !== undefined,
3067 | valIsNull = value === null,
3068 | valIsReflexive = value === value,
3069 | valIsSymbol = isSymbol_1(value);
3070 |
3071 | var othIsDefined = other !== undefined,
3072 | othIsNull = other === null,
3073 | othIsReflexive = other === other,
3074 | othIsSymbol = isSymbol_1(other);
3075 |
3076 | if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
3077 | (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||
3078 | (valIsNull && othIsDefined && othIsReflexive) ||
3079 | (!valIsDefined && othIsReflexive) ||
3080 | !valIsReflexive) {
3081 | return 1;
3082 | }
3083 | if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
3084 | (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||
3085 | (othIsNull && valIsDefined && valIsReflexive) ||
3086 | (!othIsDefined && valIsReflexive) ||
3087 | !othIsReflexive) {
3088 | return -1;
3089 | }
3090 | }
3091 | return 0;
3092 | }
3093 |
3094 | var _compareAscending = compareAscending;
3095 |
3096 | /**
3097 | * Used by `_.orderBy` to compare multiple properties of a value to another
3098 | * and stable sort them.
3099 | *
3100 | * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
3101 | * specify an order of "desc" for descending or "asc" for ascending sort order
3102 | * of corresponding values.
3103 | *
3104 | * @private
3105 | * @param {Object} object The object to compare.
3106 | * @param {Object} other The other object to compare.
3107 | * @param {boolean[]|string[]} orders The order to sort by for each property.
3108 | * @returns {number} Returns the sort order indicator for `object`.
3109 | */
3110 | function compareMultiple(object, other, orders) {
3111 | var index = -1,
3112 | objCriteria = object.criteria,
3113 | othCriteria = other.criteria,
3114 | length = objCriteria.length,
3115 | ordersLength = orders.length;
3116 |
3117 | while (++index < length) {
3118 | var result = _compareAscending(objCriteria[index], othCriteria[index]);
3119 | if (result) {
3120 | if (index >= ordersLength) {
3121 | return result;
3122 | }
3123 | var order = orders[index];
3124 | return result * (order == 'desc' ? -1 : 1);
3125 | }
3126 | }
3127 | // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
3128 | // that causes it, under certain circumstances, to provide the same value for
3129 | // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
3130 | // for more details.
3131 | //
3132 | // This also ensures a stable sort in V8 and other engines.
3133 | // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
3134 | return object.index - other.index;
3135 | }
3136 |
3137 | var _compareMultiple = compareMultiple;
3138 |
3139 | /**
3140 | * The base implementation of `_.orderBy` without param guards.
3141 | *
3142 | * @private
3143 | * @param {Array|Object} collection The collection to iterate over.
3144 | * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
3145 | * @param {string[]} orders The sort orders of `iteratees`.
3146 | * @returns {Array} Returns the new sorted array.
3147 | */
3148 | function baseOrderBy(collection, iteratees, orders) {
3149 | var index = -1;
3150 | iteratees = _arrayMap(iteratees.length ? iteratees : [identity_1], _baseUnary(_baseIteratee));
3151 |
3152 | var result = _baseMap(collection, function(value, key, collection) {
3153 | var criteria = _arrayMap(iteratees, function(iteratee) {
3154 | return iteratee(value);
3155 | });
3156 | return { 'criteria': criteria, 'index': ++index, 'value': value };
3157 | });
3158 |
3159 | return _baseSortBy(result, function(object, other) {
3160 | return _compareMultiple(object, other, orders);
3161 | });
3162 | }
3163 |
3164 | var _baseOrderBy = baseOrderBy;
3165 |
3166 | /**
3167 | * A faster alternative to `Function#apply`, this function invokes `func`
3168 | * with the `this` binding of `thisArg` and the arguments of `args`.
3169 | *
3170 | * @private
3171 | * @param {Function} func The function to invoke.
3172 | * @param {*} thisArg The `this` binding of `func`.
3173 | * @param {Array} args The arguments to invoke `func` with.
3174 | * @returns {*} Returns the result of `func`.
3175 | */
3176 | function apply(func, thisArg, args) {
3177 | switch (args.length) {
3178 | case 0: return func.call(thisArg);
3179 | case 1: return func.call(thisArg, args[0]);
3180 | case 2: return func.call(thisArg, args[0], args[1]);
3181 | case 3: return func.call(thisArg, args[0], args[1], args[2]);
3182 | }
3183 | return func.apply(thisArg, args);
3184 | }
3185 |
3186 | var _apply = apply;
3187 |
3188 | /* Built-in method references for those with the same name as other `lodash` methods. */
3189 | var nativeMax = Math.max;
3190 |
3191 | /**
3192 | * A specialized version of `baseRest` which transforms the rest array.
3193 | *
3194 | * @private
3195 | * @param {Function} func The function to apply a rest parameter to.
3196 | * @param {number} [start=func.length-1] The start position of the rest parameter.
3197 | * @param {Function} transform The rest array transform.
3198 | * @returns {Function} Returns the new function.
3199 | */
3200 | function overRest(func, start, transform) {
3201 | start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
3202 | return function() {
3203 | var args = arguments,
3204 | index = -1,
3205 | length = nativeMax(args.length - start, 0),
3206 | array = Array(length);
3207 |
3208 | while (++index < length) {
3209 | array[index] = args[start + index];
3210 | }
3211 | index = -1;
3212 | var otherArgs = Array(start + 1);
3213 | while (++index < start) {
3214 | otherArgs[index] = args[index];
3215 | }
3216 | otherArgs[start] = transform(array);
3217 | return _apply(func, this, otherArgs);
3218 | };
3219 | }
3220 |
3221 | var _overRest = overRest;
3222 |
3223 | /**
3224 | * Creates a function that returns `value`.
3225 | *
3226 | * @static
3227 | * @memberOf _
3228 | * @since 2.4.0
3229 | * @category Util
3230 | * @param {*} value The value to return from the new function.
3231 | * @returns {Function} Returns the new constant function.
3232 | * @example
3233 | *
3234 | * var objects = _.times(2, _.constant({ 'a': 1 }));
3235 | *
3236 | * console.log(objects);
3237 | * // => [{ 'a': 1 }, { 'a': 1 }]
3238 | *
3239 | * console.log(objects[0] === objects[1]);
3240 | * // => true
3241 | */
3242 | function constant(value) {
3243 | return function() {
3244 | return value;
3245 | };
3246 | }
3247 |
3248 | var constant_1 = constant;
3249 |
3250 | var defineProperty = (function() {
3251 | try {
3252 | var func = _getNative(Object, 'defineProperty');
3253 | func({}, '', {});
3254 | return func;
3255 | } catch (e) {}
3256 | }());
3257 |
3258 | var _defineProperty = defineProperty;
3259 |
3260 | /**
3261 | * The base implementation of `setToString` without support for hot loop shorting.
3262 | *
3263 | * @private
3264 | * @param {Function} func The function to modify.
3265 | * @param {Function} string The `toString` result.
3266 | * @returns {Function} Returns `func`.
3267 | */
3268 | var baseSetToString = !_defineProperty ? identity_1 : function(func, string) {
3269 | return _defineProperty(func, 'toString', {
3270 | 'configurable': true,
3271 | 'enumerable': false,
3272 | 'value': constant_1(string),
3273 | 'writable': true
3274 | });
3275 | };
3276 |
3277 | var _baseSetToString = baseSetToString;
3278 |
3279 | /** Used to detect hot functions by number of calls within a span of milliseconds. */
3280 | var HOT_COUNT = 800;
3281 | var HOT_SPAN = 16;
3282 |
3283 | /* Built-in method references for those with the same name as other `lodash` methods. */
3284 | var nativeNow = Date.now;
3285 |
3286 | /**
3287 | * Creates a function that'll short out and invoke `identity` instead
3288 | * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
3289 | * milliseconds.
3290 | *
3291 | * @private
3292 | * @param {Function} func The function to restrict.
3293 | * @returns {Function} Returns the new shortable function.
3294 | */
3295 | function shortOut(func) {
3296 | var count = 0,
3297 | lastCalled = 0;
3298 |
3299 | return function() {
3300 | var stamp = nativeNow(),
3301 | remaining = HOT_SPAN - (stamp - lastCalled);
3302 |
3303 | lastCalled = stamp;
3304 | if (remaining > 0) {
3305 | if (++count >= HOT_COUNT) {
3306 | return arguments[0];
3307 | }
3308 | } else {
3309 | count = 0;
3310 | }
3311 | return func.apply(undefined, arguments);
3312 | };
3313 | }
3314 |
3315 | var _shortOut = shortOut;
3316 |
3317 | /**
3318 | * Sets the `toString` method of `func` to return `string`.
3319 | *
3320 | * @private
3321 | * @param {Function} func The function to modify.
3322 | * @param {Function} string The `toString` result.
3323 | * @returns {Function} Returns `func`.
3324 | */
3325 | var setToString = _shortOut(_baseSetToString);
3326 |
3327 | var _setToString = setToString;
3328 |
3329 | /**
3330 | * The base implementation of `_.rest` which doesn't validate or coerce arguments.
3331 | *
3332 | * @private
3333 | * @param {Function} func The function to apply a rest parameter to.
3334 | * @param {number} [start=func.length-1] The start position of the rest parameter.
3335 | * @returns {Function} Returns the new function.
3336 | */
3337 | function baseRest(func, start) {
3338 | return _setToString(_overRest(func, start, identity_1), func + '');
3339 | }
3340 |
3341 | var _baseRest = baseRest;
3342 |
3343 | /**
3344 | * Checks if the given arguments are from an iteratee call.
3345 | *
3346 | * @private
3347 | * @param {*} value The potential iteratee value argument.
3348 | * @param {*} index The potential iteratee index or key argument.
3349 | * @param {*} object The potential iteratee object argument.
3350 | * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
3351 | * else `false`.
3352 | */
3353 | function isIterateeCall(value, index, object) {
3354 | if (!isObject_1(object)) {
3355 | return false;
3356 | }
3357 | var type = typeof index;
3358 | if (type == 'number'
3359 | ? (isArrayLike_1(object) && _isIndex(index, object.length))
3360 | : (type == 'string' && index in object)
3361 | ) {
3362 | return eq_1(object[index], value);
3363 | }
3364 | return false;
3365 | }
3366 |
3367 | var _isIterateeCall = isIterateeCall;
3368 |
3369 | /**
3370 | * Creates an array of elements, sorted in ascending order by the results of
3371 | * running each element in a collection thru each iteratee. This method
3372 | * performs a stable sort, that is, it preserves the original sort order of
3373 | * equal elements. The iteratees are invoked with one argument: (value).
3374 | *
3375 | * @static
3376 | * @memberOf _
3377 | * @since 0.1.0
3378 | * @category Collection
3379 | * @param {Array|Object} collection The collection to iterate over.
3380 | * @param {...(Function|Function[])} [iteratees=[_.identity]]
3381 | * The iteratees to sort by.
3382 | * @returns {Array} Returns the new sorted array.
3383 | * @example
3384 | *
3385 | * var users = [
3386 | * { 'user': 'fred', 'age': 48 },
3387 | * { 'user': 'barney', 'age': 36 },
3388 | * { 'user': 'fred', 'age': 40 },
3389 | * { 'user': 'barney', 'age': 34 }
3390 | * ];
3391 | *
3392 | * _.sortBy(users, [function(o) { return o.user; }]);
3393 | * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
3394 | *
3395 | * _.sortBy(users, ['user', 'age']);
3396 | * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
3397 | */
3398 | var sortBy = _baseRest(function(collection, iteratees) {
3399 | if (collection == null) {
3400 | return [];
3401 | }
3402 | var length = iteratees.length;
3403 | if (length > 1 && _isIterateeCall(collection, iteratees[0], iteratees[1])) {
3404 | iteratees = [];
3405 | } else if (length > 2 && _isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
3406 | iteratees = [iteratees[0]];
3407 | }
3408 | return _baseOrderBy(collection, _baseFlatten(iteratees, 1), []);
3409 | });
3410 |
3411 | var sortBy_1$1 = sortBy;
3412 |
3413 | // main
3414 |
3415 | function PromiseCache (args) {
3416 |
3417 | var args = args || {};
3418 |
3419 | this.maxResolvedCache = args.maxResolvedCache || 1000;
3420 |
3421 | this._pendingPromises = {};
3422 | this._resolvedPromises = {};
3423 |
3424 | }
3425 |
3426 | PromiseCache.prototype = {
3427 |
3428 | add: function (key, promise) {
3429 |
3430 | var self = this;
3431 |
3432 | // check if it already exists
3433 | if (this._pendingPromises[ key ]) {
3434 | return this._pendingPromises[ key ]
3435 | }
3436 | if (this._resolvedPromises[ key ]) {
3437 | return this._resolvedPromises[ key ]
3438 | }
3439 |
3440 | // create cache object
3441 | var cacheObject = {
3442 | key: key,
3443 | timestamp: Date.now(),
3444 | promise: promise
3445 | };
3446 |
3447 | // add to store
3448 | this._pendingPromises[ key ] = cacheObject;
3449 |
3450 | // move to resolved store and update state when resolved
3451 | promise.then(function (data) {
3452 |
3453 | var cacheObject = self._pendingPromises[ key ];
3454 | delete self._pendingPromises[ key ];
3455 |
3456 | cacheObject.data = data;
3457 |
3458 | self._resolvedPromises[ key ] = cacheObject;
3459 |
3460 | }, function () {
3461 |
3462 | delete self._pendingPromises[ key ];
3463 |
3464 | });
3465 |
3466 | // collect garbage
3467 | this._collectGarbage();
3468 |
3469 | // return cache object
3470 | return cacheObject
3471 |
3472 | },
3473 |
3474 | get: function (key) {
3475 |
3476 | // check store
3477 | var cacheObject = this._pendingPromises[ key ] || this._resolvedPromises[ key ];
3478 | if (!cacheObject) {
3479 | return false
3480 | }
3481 |
3482 | // update timestamp
3483 | cacheObject.timestamp = Date.now();
3484 |
3485 | // return promise
3486 | return cacheObject.promise
3487 |
3488 | },
3489 |
3490 | purge: function () {
3491 |
3492 | for (var key in this._resolvedPromises) {
3493 | delete this._resolvedPromises[ key ];
3494 | }
3495 |
3496 | },
3497 |
3498 | _collectGarbage: function () {
3499 |
3500 | // sort archive by timestamp
3501 | var sortedPromises = sortBy_1$1(this._resolvedPromises, function (obj) {
3502 | return obj.timestamp
3503 | });
3504 |
3505 | // the amount of cache objects that have to be removed
3506 | var removeCount = (sortedPromises.length - this.maxResolvedCache);
3507 | if (removeCount <= 0) {
3508 | return
3509 | }
3510 |
3511 | for (var i = 0; i < removeCount; i++) {
3512 | delete this._resolvedPromises[ sortedPromises[ i ].key ];
3513 | }
3514 |
3515 | }
3516 |
3517 | };
3518 |
3519 | // from https://github.com/archilogic-com/3dio-js/blob/master/src/utils/io/fetch-script.js
3520 |
3521 | var promiseCache$1 = new PromiseCache();
3522 |
3523 | function fetchScript (url) {
3524 |
3525 | // module wrapper
3526 | window.___modules = window.___modules || {};
3527 |
3528 | // return module if it has been loaded already
3529 | if (window.___modules[url]) {
3530 | return Promise.resolve(window.___modules[url])
3531 |
3532 | } else {
3533 |
3534 | // try promise cache (could be in loading state)
3535 | var promiseFromCache = promiseCache$1.get(url);
3536 | if (promiseFromCache) return promiseFromCache
3537 |
3538 | // load code and use module wrapper
3539 | var fetchPromise = fetch(url).then(function(response){
3540 | if (!response.ok) throw 'Could not load script from URL: '+url
3541 | return response.text()
3542 | }).then(function(code){
3543 |
3544 | // check module type
3545 | var moduleWrapper;
3546 | if (code.indexOf('define(function()') > -1) {
3547 | // AMD
3548 | moduleWrapper = code+'\nfunction define(cb){ window.___modules["'+url+'"] = cb(); };';
3549 | } else {
3550 | // CommonJS
3551 | moduleWrapper = 'window.___modules["'+url+'"] = (function(){ var exports = {}, module = {exports:exports};'+code+'\nreturn module.exports\n})()';
3552 | }
3553 |
3554 | var script = document.createElement('script');
3555 | try {
3556 | script.appendChild(document.createTextNode(moduleWrapper));
3557 | document.body.appendChild(script);
3558 | } catch (e) {
3559 | script.text = moduleWrapper;
3560 | document.body.appendChild(script);
3561 | }
3562 | return window.___modules[url]
3563 | });
3564 |
3565 | // add to cache
3566 | promiseCache$1.add(url, fetchPromise);
3567 |
3568 | return fetchPromise
3569 |
3570 | }
3571 |
3572 | }
3573 |
3574 | var fragmentShader = {"text":"uniform vec3 u_color;\r\nuniform float u_metallic;\r\nuniform float u_roughness;\r\nuniform vec3 u_light0Pos;\r\nuniform vec3 u_light0Color;\r\nuniform vec3 u_light1Pos;\r\nuniform vec3 u_light1Color;\r\nuniform mat4 u_modelMatrix;\r\nuniform sampler2D u_reflectionCube;\r\nuniform sampler2D u_reflectionCubeBlur;","base64":"data:text/plain;base64,dW5pZm9ybSB2ZWMzIHVfY29sb3I7DQp1bmlmb3JtIGZsb2F0IHVfbWV0YWxsaWM7DQp1bmlmb3JtIGZsb2F0IHVfcm91Z2huZXNzOw0KdW5pZm9ybSB2ZWMzIHVfbGlnaHQwUG9zOw0KdW5pZm9ybSB2ZWMzIHVfbGlnaHQwQ29sb3I7DQp1bmlmb3JtIHZlYzMgdV9saWdodDFQb3M7DQp1bmlmb3JtIHZlYzMgdV9saWdodDFDb2xvcjsNCnVuaWZvcm0gbWF0NCB1X21vZGVsTWF0cml4Ow0KdW5pZm9ybSBzYW1wbGVyMkQgdV9yZWZsZWN0aW9uQ3ViZTsNCnVuaWZvcm0gc2FtcGxlcjJEIHVfcmVmbGVjdGlvbkN1YmVCbHVyOw=="};
3575 |
3576 | var vertexShader = {"text":"varying vec3 v_normal;\r\nvarying vec3 v_position;\r\nvarying vec3 v_binormal;\r\nvarying vec3 v_tangent;\r\n","base64":"data:text/plain;base64,dmFyeWluZyB2ZWMzIHZfbm9ybWFsOw0KdmFyeWluZyB2ZWMzIHZfcG9zaXRpb247DQp2YXJ5aW5nIHZlYzMgdl9iaW5vcm1hbDsNCnZhcnlpbmcgdmVjMyB2X3RhbmdlbnQ7DQo="};
3577 |
3578 | // TODO: Replace placeholder shaders by original ones (requires fixing projection matrix)
3579 | // configs
3580 |
3581 | var LEGACY_GLFT_V1_LOADER_URL = 'https://cdn.rawgit.com/mrdoob/three.js/r86/examples/js/loaders/GLTFLoader.js';
3582 | var POLY_API_URL = 'https://poly.googleapis.com/v1/assets/';
3583 | var UNOFFICIAL_LEGACY_API_URL = 'https://gblock.3d.io/api/get-gltf-url/';
3584 | // ADD API KEY or provide it in a-frame param by adding "?key=xxxxxxxxxxxxxx" to the poly url
3585 | var API_KEY = '';
3586 |
3587 |
3588 | // for local development using unofficial legacy API:
3589 | // 1. uncomment the following line
3590 | //var UNOFFICIAL_LEGACY_API_URL = 'http://localhost:3000/api/get-gltf-url/'
3591 | // 2. start local server: npm run start
3592 | // 3. compile aframe component: npm run build
3593 | // 4. go to http://localhost:3000
3594 |
3595 | // internals
3596 |
3597 | var promiseCache = new PromiseCache();
3598 |
3599 | // aframe module
3600 |
3601 | AFRAME.registerComponent('gblock', {
3602 |
3603 | schema: {type: 'asset'},
3604 |
3605 | init: function () {
3606 |
3607 | this.model = null;
3608 |
3609 | },
3610 |
3611 | update: function () {
3612 |
3613 | var self = this;
3614 | var el = this.el;
3615 | var src = this.data;
3616 |
3617 | if (!src) { return; }
3618 |
3619 | // check if API key is provided...
3620 | var apiKey;
3621 | if (src.indexOf('?key=') > -1) {
3622 | // ... in aframe parameter
3623 | apiKey = src.split('?key=').pop(); // extract API key
3624 | src = src.substring(0,src.indexOf('?key=')); // remove key from src
3625 | } else if (API_KEY !== '') {
3626 | // ... as hardcoded constant
3627 | apiKey = API_KEY;
3628 | }
3629 | if (apiKey) {
3630 | var id = src.substr(src.lastIndexOf('/') + 1); // get GLTF id
3631 | if (!id) { return; }
3632 | }
3633 |
3634 | self.remove()
3635 |
3636 | ;(apiKey ? getGltfUrl(id, apiKey) : getGltfUrlFromLegacyApi(src))
3637 | .then(loadGblockModel)
3638 | .then(function onLoaded (gltfModel) {
3639 |
3640 | self.model = gltfModel.scene || gltfModel.scenes[0];
3641 | self.model.animations = gltfModel.animations;
3642 |
3643 | el.setObject3D('mesh', self.model);
3644 | el.emit('model-loaded', {format: 'gltf', model: self.model});
3645 |
3646 | })
3647 | .catch(function(errorMessage){
3648 |
3649 | console.error('ERROR loading gblock model from "' + src +'" : ' + errorMessage);
3650 | el.emit('model-error', { message: errorMessage });
3651 |
3652 | });
3653 |
3654 | },
3655 |
3656 | remove: function () {
3657 |
3658 | if (!this.model) { return; }
3659 | this.el.removeObject3D('mesh');
3660 |
3661 | }
3662 |
3663 | });
3664 |
3665 | // private shared methods
3666 |
3667 | // This API call is only needed to obtain the official glTF URL of a google block model.
3668 | // The glTF itself is not being proxied and gets fetched from https://vr.google.com/downloads/* directly.
3669 | // https://github.com/archilogic-com/aframe-gblock/issues/1
3670 | // API server code: server/index.js
3671 | // try promise cache (could be in loading state)
3672 | function getGltfUrl (id, apiKey) {
3673 | var url = POLY_API_URL + id + '/?key=' + apiKey;
3674 |
3675 | // try cache
3676 | var getUrlPromise = promiseCache.get(url);
3677 |
3678 | if (!getUrlPromise) {
3679 |
3680 | getUrlPromise = fetch(url).then(function (response) {
3681 |
3682 | // parse response
3683 | return response.json().catch(function(error){
3684 | // handle JSON parsing error
3685 | console.log('ERROR parsing gblock API server response JSON.\nRequested Model: "' + url + '"\nError: "' + JSON.stringify(error) + '"');
3686 | return Promise.reject('gblock API server error. Check console for details.')
3687 | }).then(function (info) {
3688 | if (info.error !== undefined) {
3689 | return Promise.reject('Poly API error: ' + info.error.message)
3690 | }
3691 | var format = info.formats.find( format => { return format.formatType === 'GLTF' || format.formatType === 'GLTF2'; } );
3692 | if ( format !== undefined ) {
3693 | return format.root.url
3694 | } else {
3695 | return Promise.reject('Poly asset id:' + id + ' not provided in GLTF or GLTF2 format.')
3696 | }
3697 | })
3698 |
3699 | });
3700 |
3701 | // add to cache
3702 | promiseCache.add(url, getUrlPromise);
3703 |
3704 | }
3705 |
3706 | return getUrlPromise
3707 |
3708 | }
3709 |
3710 | // Legacy mode using unofficial API
3711 | // This API call is only needed to obtain the official glTF URL of a google block model.
3712 | // The glTF itself is not being proxied and gets fetched from https://vr.google.com/downloads/* directly.
3713 | // https://github.com/archilogic-com/aframe-gblock/issues/1
3714 | // API server code: server/index.js
3715 | // try promise cache (could be in loading state)
3716 | function getGltfUrlFromLegacyApi (src) {
3717 |
3718 | // try cache
3719 | var getUrlPromise = promiseCache.get(src);
3720 |
3721 | if (!getUrlPromise) {
3722 |
3723 | getUrlPromise = fetch(UNOFFICIAL_LEGACY_API_URL + '?url=' + src).then(function (response) {
3724 |
3725 | // parse response
3726 | return response.json().catch(function(error){
3727 | // handle JSON parsing error
3728 | console.log('ERROR parsing gblock API server response JSON.\nRequested Model: "' + src + '"\nError: "' + JSON.stringify(error) + '"');
3729 | return Promise.reject('gblock API server error. Check console for details.')
3730 | }).then(function (message) {
3731 | if (response.ok) {
3732 | // return glTF URL
3733 | return message.gltfUrl
3734 | } else {
3735 | // handle error response
3736 | console.error('ERROR loading gblock model "'+ src +'" : ' + response.status + ' "' + message.message);
3737 | return Promise.reject(message.message)
3738 | }
3739 | })
3740 |
3741 | });
3742 |
3743 | // add to cache
3744 | promiseCache.add(src, getUrlPromise);
3745 |
3746 | }
3747 |
3748 | return getUrlPromise
3749 |
3750 | }
3751 |
3752 | // loads google block models (poly.google.com)
3753 | function loadGblockModel(url, onProgress) {
3754 | return new Promise(function(resolve, reject) {
3755 |
3756 | // create unviresal GLTF loader for google blocks
3757 | // this one will inherit methods from GLTF V1 or V2 based on file version
3758 | function GBlockLoader () {
3759 | this.manager = THREE.DefaultLoadingManager;
3760 | this.path = THREE.Loader.prototype.extractUrlBase( url );
3761 | }
3762 |
3763 | // load model
3764 | var loader = new THREE.FileLoader( GBlockLoader.manager );
3765 | loader.setResponseType( 'arraybuffer' );
3766 | loader.load( url, function onLoad( data ) {
3767 | try {
3768 |
3769 | // convert uint8 to json
3770 | var json = JSON.parse(convertUint8ArrayToString(data));
3771 |
3772 | // check GLTF version
3773 | var isGLTF1 = json.asset === undefined || json.asset.version[ 0 ] < 2;
3774 |
3775 | if (isGLTF1) {
3776 |
3777 | fetchGLTF1Loader().then(function(GLTF1Loader){
3778 |
3779 | // inherit methods from GLTF V1 loader
3780 | GBlockLoader.prototype = GLTF1Loader.prototype;
3781 | var gblockLoader = new GBlockLoader();
3782 | GLTF1Loader.call(gblockLoader);
3783 |
3784 | // Replace original shaders with placeholders
3785 | Object.keys(json.shaders).forEach(function (key, i) {
3786 | if (key.indexOf('fragment') > -1) json.shaders[key].uri = fragmentShader.base64;
3787 | else if (key.indexOf('vertex') > -1) json.shaders[key].uri = vertexShader.base64;
3788 | });
3789 |
3790 | // convert json back to uint8 data
3791 | var modifiedData = new TextEncoder('utf-8').encode(JSON.stringify(json));
3792 |
3793 | // parse data
3794 | gblockLoader.parse( modifiedData, function onParsingDone (gltf) {
3795 |
3796 | // FIXME: adapt projection matrix in original shaders and do not replace materials
3797 | (gltf.scene || gltf.scenes[0]).traverse(function (child) {
3798 | if (child.material) child.material = new THREE.MeshPhongMaterial({ vertexColors: THREE.VertexColors });
3799 | });
3800 |
3801 | // GLTF V1 ready
3802 | resolve(gltf);
3803 |
3804 | }, gblockLoader.path);
3805 |
3806 | });
3807 |
3808 | } else {
3809 |
3810 | // inferit methods from GLTF V2 loader
3811 | GBlockLoader.prototype = THREE.GLTFLoader.prototype;
3812 | var gblockLoader = new GBlockLoader();
3813 | THREE.GLTFLoader.call(gblockLoader);
3814 |
3815 | // parse data
3816 | gblockLoader.parse( data, gblockLoader.path, resolve, reject);
3817 |
3818 | }
3819 |
3820 | } catch ( e ) {
3821 |
3822 | // For SyntaxError or TypeError, return a generic failure message.
3823 | reject( e.constructor === Error ? e : new Error( 'THREE.GLTFLoader: Unable to parse model.' ) );
3824 |
3825 | }
3826 |
3827 | }, onProgress, reject );
3828 |
3829 | })
3830 | }
3831 |
3832 | // fetch legacy GLTF v1 loader on demand
3833 | var GLFT1LoaderPromise;
3834 | function fetchGLTF1Loader () {
3835 | if (!GLFT1LoaderPromise ) {
3836 | // legacy loader will overwrite THREE.GLTFLoader so we need to keep reference to it
3837 | THREE.___GLTF2Loader = THREE.GLTFLoader;
3838 | // fetch legacy loader for GLTF1
3839 | GLFT1LoaderPromise = fetchScript(LEGACY_GLFT_V1_LOADER_URL).then(function(){
3840 | // keep reference GLTF V1 loader
3841 | var GLTF1Loader = THREE.GLTFLoader;
3842 | // restore GLTF V2 loader reference
3843 | THREE.GLTFLoader = THREE.___GLTF2Loader;
3844 |
3845 | return GLTF1Loader
3846 | });
3847 | }
3848 | return GLFT1LoaderPromise
3849 | }
3850 |
3851 | // from https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/GLTFLoader.js
3852 | function convertUint8ArrayToString (array) {
3853 | if (window.TextDecoder !== undefined) {
3854 | return new TextDecoder().decode(array)
3855 | }
3856 | // Avoid the String.fromCharCode.apply(null, array) shortcut, which
3857 | // throws a "maximum call stack size exceeded" error for large arrays.
3858 | var s = '';
3859 | for (var i = 0, il = array.length; i < il; i++) {
3860 | s += String.fromCharCode(array[i]);
3861 | }
3862 | return s;
3863 | }
3864 |
3865 | }());
3866 | //# sourceMappingURL=gblock.js.map
3867 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | gBlock Component - A-Frame
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
163 |
166 |
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/docs/screenshot3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/archilogic-com/aframe-gblock/b8eef360c5276b8e1b1b0066f74d8f0f86c73a01/docs/screenshot3.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aframe-gblock",
3 | "description": "Aframe component loading remixable google blocks models.",
4 | "version": "1.0.2",
5 | "homepage": "https://archilogic-com.github.io/aframe-gblock/",
6 | "repository": "archilogic-com/aframe-gblock",
7 | "scripts": {
8 | "start": "node server/server.js",
9 | "build": "rollup -c rollup.config.js"
10 | },
11 | "license": "MIT",
12 | "author": {
13 | "name": "archilogic",
14 | "email": "dev.rocks@3d.io",
15 | "url": "https://3d.io"
16 | },
17 | "main": "dist/gblock.js",
18 | "files": ["dist"],
19 | "keywords": [
20 | "aframe",
21 | "3d",
22 | "cardboard",
23 | "components",
24 | "oculus",
25 | "vive",
26 | "rift",
27 | "vr",
28 | "WebVR",
29 | "WegGL",
30 | "three",
31 | "three.js",
32 | "3D model",
33 | "3d.io"
34 | ],
35 | "dependencies": {
36 | "cors": "^2.8.4",
37 | "express": "^4.15.5",
38 | "googleapis": "^22.1.0",
39 | "request": "^2.82.0"
40 | },
41 | "devDependencies": {
42 | "lodash": "^4.17.4",
43 | "rollup": "^0.49.3",
44 | "rollup-plugin-commonjs": "^8.2.1",
45 | "rollup-plugin-node-resolve": "^3.0.0"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import resolve from 'rollup-plugin-node-resolve'
2 | import commonjs from 'rollup-plugin-commonjs'
3 |
4 | // https://rollupjs.org/#javascript-api
5 | export default {
6 | input: 'src/gblock.js',
7 | indent: '\t',
8 | sourcemap: true,
9 | plugins: [
10 | glsl(),
11 | commonjs({
12 | include: [
13 | 'node_modules/lodash/**',
14 | ]
15 | }),
16 | resolve()
17 | ],
18 | watch: {
19 | include: 'src/**'
20 | },
21 | output: [
22 | {
23 | format: 'iife',
24 | banner: '// https://github.com/archilogic-com/aframe-gblock',
25 | name: 'gblock', // and global object name in browser environment
26 | globals: {
27 | THREE: 'THREE',
28 | AFRAME: 'AFRAME'
29 | },
30 | file: 'dist/gblock.js'
31 | }
32 | ]
33 | }
34 |
35 | // plugins
36 |
37 | // inspired by https://github.com/mrdoob/three.js/blob/86424d9b318f617254eb857b31be07502ea27ce9/rollup.config.js
38 | function glsl () {
39 | return {
40 | transform(glsl, id) {
41 | if (/\.glsl$/.test(id) === false) return
42 | // remove comments and fix new line chars
43 | var shaderAsText = glsl
44 | .replace(/[ \t]*\/\/.*\n/g, '') // remove //
45 | .replace(/[ \t]*\/\*[\s\S]*?\*\//g, '') // remove /* */
46 | .replace(/\n{2,}/g, '\n') // # \n+ to \n
47 | //
48 | var shaderAsBase64 = 'data:text/plain;base64,' + new Buffer(shaderAsText).toString('base64')
49 | //
50 | var moduleApi = { text: shaderAsText, base64: shaderAsBase64 }
51 | // add module wrapper
52 | var code = 'export default ' + JSON.stringify(moduleApi) + ';'
53 | return {
54 | code: code,
55 | map: {mappings: ''}
56 | }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/server/api-methods.js:
--------------------------------------------------------------------------------
1 | // TODO: Replace this with an official API once available
2 | // This API only fetches the official glTF URL of a google block model from google blocks website.
3 | // The glTF itself is not being proxied and gets fetched from https://vr.google.com/downloads/* directly.
4 | // https://github.com/archilogic-com/aframe-gblock/issues/1
5 |
6 | const request = require('request')
7 | const google = require('googleapis')
8 | const customsearch = google.customsearch('v1')
9 |
10 | // configs
11 |
12 | var SEARCH_CACHE_MAX_AGE = 1000 * 60 * 60 * 24 * 3
13 | var SEARCH_COUNTER_MAX = 5000 // max daily requests to google API
14 | var GOOGLE_API_KEY = process.env.GOOGLE_CLOUD_API_KEY
15 | var GOOGLE_CSE_ID = process.env.GOOGLE_CSE_ID // custom search engine (CSE) ID
16 | // Get your CSE ID from: https://cse.google.com/cse/all
17 | // - include the following pages in your CSE configs: >> "Basics" >> "Sites to search"
18 | // https://vr.google.com/objects/*
19 | // - exclude the following pages in your CSE configs: >> "Basics" >> "Sites to search" >> "Advanced" >> "Sites to exclude"
20 | // https://vr.google.com/objects/user*
21 | // https://vr.google.com/objects/category*
22 | // https://vr.google.com/objects/*/sources*
23 | // https://vr.google.com/objects/*/remixes*
24 |
25 | // shared
26 |
27 | var gblockCache = {}
28 | var searchCache = {}
29 | var searchCounter = 0
30 | var searchCounterDay = getDayTimestamp()
31 |
32 | // main
33 |
34 | exports.search = function searchGoogleBlocksSite(req, res) {
35 |
36 | var query = req.query.query
37 | var limit = Number.isInteger(parseInt(req.query.limit)) ? parseInt(req.query.limit) : 10
38 | var offset = Number.isInteger(parseInt(req.query.offset)) ? parseInt(req.query.offset) : 1
39 |
40 | // check params
41 | if (offset < 1 || offset > (101 - limit)) {
42 | // limitation by google apis. https://productforums.google.com/forum/#!topic/customsearch/iafqT6dl2VM;context-place=topicsearchin/customsearch/category$3Atroubleshooting-and-bugs%7Csort:relevance%7Cspell:false
43 | res.status(400).send({ message: 'param "offset" must be a value between 1 and (101 - limit)' })
44 | return
45 | }
46 | if (limit < 1 || limit > 10) {
47 | // limitation by google apis. https://productforums.google.com/forum/#!topic/customsearch/iafqT6dl2VM;context-place=topicsearchin/customsearch/category$3Atroubleshooting-and-bugs%7Csort:relevance%7Cspell:false
48 | res.status(400).send({ message: 'param "limit" must be between 0 and 10' })
49 | return
50 | }
51 |
52 | // get from cache if available and not too old
53 | var cacheKey = offset + '-' + limit + '-' + query
54 | if (searchCache[cacheKey]) {
55 | var timeDelta = Date.now() - searchCache[cacheKey].timestamp
56 | if (timeDelta < SEARCH_CACHE_MAX_AGE) {
57 | res.status(200).send(searchCache[cacheKey])
58 | return
59 | }
60 | }
61 |
62 | // stop API if daily limit has been reached
63 | var currentDay = getDayTimestamp()
64 | if (searchCounter > SEARCH_COUNTER_MAX && currentDay === searchCounterDay) {
65 | res.status(400).send('API quota exceed. Pls Try again tomorrow.')
66 | return
67 | }
68 |
69 | customsearch.cse.list({
70 | // params infos:
71 | // https://developers.google.com/apis-explorer/?hl=en_GB#p/customsearch/v1/search.cse.list
72 | // https://github.com/google/google-api-nodejs-client/blob/master/apis/customsearch/v1.ts
73 | // https://developers.google.com/custom-search/docs/xml_results
74 | auth: GOOGLE_API_KEY,
75 | cx: GOOGLE_CSE_ID,
76 | q: query,
77 | start: offset,
78 | num: limit // limit amount of search results
79 | //sort: '?'
80 | }, function onSearchResult(error, result) {
81 |
82 | if (error) {
83 | res.status(400).send(error)
84 | return
85 | }
86 |
87 | if (currentDay === searchCounterDay) {
88 | // update counter
89 | searchCounter++
90 | console.log('google search count: '+searchCounter)
91 | } else {
92 | // reset counter
93 | searchCounter = 0
94 | searchCounterDay = currentDay
95 | console.log('counter has been reset to day '+currentDay)
96 | }
97 |
98 | var searchResults = {
99 | searchTime: result.searchInformation.searchTime,
100 | totalResults: result.searchInformation.totalResults, // .formattedTotalResults
101 | items: result.items ? result.items.map(parseItem) : [],
102 | timestamp: Date.now()
103 | }
104 |
105 | searchCache[cacheKey] = searchResults
106 |
107 | res.status(200).send(searchResults)
108 |
109 | })
110 |
111 | }
112 |
113 | exports.getGltfUrl = function getGltfUrlFromGoogleBlocksSite (req, res) {
114 |
115 | var url = req.query.url
116 |
117 | // check params
118 | if (!req.query.url || req.query.url === '') {
119 | res.status(400).send({ message: 'Please provide "url" query param in URL: i.e. "https://vr.google.com/objects/?url="' })
120 | return
121 | }
122 | if (
123 | req.query.url.substring(0,30) !== 'https://vr.google.com/objects/'
124 | && req.query.url.substring(0,29) !== 'https://poly.google.com/view/'
125 | ) {
126 | res.status(400).send({ message: 'param "url" must start with "https://vr.google.com/objects/" or "https://poly.google.com/view/"' })
127 | return
128 | }
129 |
130 | // replace vr.google.com URLs (legacy support)
131 | url = url.replace('https://vr.google.com/objects/', 'https://poly.google.com/view/')
132 |
133 | // remove trailing slash
134 | if (url[url.length-1] === '/') url = url.substring(0, url.length-1)
135 | // get id from url
136 | var id = url.split('/').pop()
137 |
138 | // get result from cache if possible
139 | if (gblockCache[id]) {
140 | res.send(gblockCache[id])
141 | return
142 | }
143 |
144 | request({
145 | method: 'GET',
146 | url: url
147 | }, function (error, response, body) {
148 |
149 | var path = 'https:\\/\\/poly\\.googleusercontent\\.com\\/downloads\\/'
150 |
151 | var isRemixable = body.indexOf('Not remixable') === -1
152 | var objUrl = new RegExp(`"(${path}${id}[^"]*_obj\\.zip)"`).exec(body)
153 | var gltfUrl = new RegExp(`"(${path}${id}[^"]*\\.gltf)"`).exec(body)
154 |
155 | if (!isRemixable) {
156 | res.status(405).send({ message: 'Model is not remixable' })
157 |
158 | } else if (gltfUrl) {
159 | var result = {
160 | gltfUrl: gltfUrl[1],
161 | objUrl: objUrl ? objUrl[1] : undefined,
162 | timestamp: Date.now()
163 | }
164 | gblockCache[id] = result
165 | res.send(result)
166 |
167 | } else {
168 | res.status(404).send({ message: 'Model not found' })
169 | }
170 | })
171 |
172 | }
173 |
174 | // helpers
175 |
176 | function getDayTimestamp() {
177 | return Math.floor(Date.now() / (1000 * 60 * 60 * 24))
178 | }
179 |
180 | function parseItem(rawItem){
181 |
182 | var item = {}
183 |
184 | item.url = rawItem.link // see also: .formattedUrl .htmlFormattedUrl
185 |
186 | if (rawItem.pagemap && rawItem.pagemap) {
187 | var pagemap = rawItem.pagemap
188 |
189 | if (pagemap.metatags && pagemap.metatags[0]) {
190 | var metatags = pagemap.metatags[0]
191 | // title
192 | item.title = metatags['og:title'] // (nice title) alternative: item.title (html page title)
193 | // model author
194 | item.author = metatags['og:description']
195 | // images
196 | item.image = metatags['og:image']
197 | }
198 |
199 | //item.smallImage = pagemap.cse_thumbnail && pagemap.cse_thumbnail[0] ? pagemap.cse_thumbnail[0].src : null
200 | //item.largeImage = pagemap.cse_image && pagemap.cse_image[0] ? pagemap.cse_image[0].src : null
201 |
202 | }
203 |
204 | return item
205 |
206 | }
207 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express')
2 | var corsMiddleware = require('cors')
3 | var methods = require('./api-methods.js')
4 |
5 | // setup
6 | var app = express()
7 |
8 | // CORS
9 | app.use(corsMiddleware({
10 | credentials: true,
11 | origin: function (origin, callback) {
12 | callback(null, origin)
13 | }
14 | }))
15 |
16 | // API
17 | app.get('/api/search*', methods.search)
18 | app.get('/api/get-gltf-url*', methods.getGltfUrl)
19 |
20 | // static
21 | app.use(express.static('docs'))
22 | app.use(express.static('dist'))
23 |
24 | // start server
25 | var listener = app.listen(process.env.PORT || 3000, function onServerStarted () {
26 | console.log('Server started on port '+listener.address().port)
27 | })
28 |
--------------------------------------------------------------------------------
/src/gblock.js:
--------------------------------------------------------------------------------
1 | // TODO: Replace placeholder shaders by original ones (requires fixing projection matrix)
2 | import fetchScript from './utils/fetch-script.js'
3 | import PromiseCache from './utils/promise-cache.js'
4 | import fragmentShader from './shaders/fragment-placeholder.glsl'
5 | import vertexShader from './shaders/vertex-placeholder.glsl'
6 |
7 | // configs
8 |
9 | var LEGACY_GLFT_V1_LOADER_URL = 'https://cdn.rawgit.com/mrdoob/three.js/r86/examples/js/loaders/GLTFLoader.js'
10 | var POLY_API_URL = 'https://poly.googleapis.com/v1/assets/'
11 | var UNOFFICIAL_LEGACY_API_URL = 'https://gblock.3d.io/api/get-gltf-url/'
12 | // ADD API KEY or provide it in a-frame param by adding "?key=xxxxxxxxxxxxxx" to the poly url
13 | var API_KEY = ''
14 |
15 |
16 | // for local development using unofficial legacy API:
17 | // 1. uncomment the following line
18 | //var UNOFFICIAL_LEGACY_API_URL = 'http://localhost:3000/api/get-gltf-url/'
19 | // 2. start local server: npm run start
20 | // 3. compile aframe component: npm run build
21 | // 4. go to http://localhost:3000
22 |
23 | // internals
24 |
25 | var promiseCache = new PromiseCache()
26 |
27 | // aframe module
28 |
29 | AFRAME.registerComponent('gblock', {
30 |
31 | schema: {type: 'asset'},
32 |
33 | init: function () {
34 |
35 | this.model = null
36 |
37 | },
38 |
39 | update: function () {
40 |
41 | var self = this
42 | var el = this.el
43 | var src = this.data
44 |
45 | if (!src) { return; }
46 |
47 | // check if API key is provided...
48 | var apiKey
49 | if (src.indexOf('?key=') > -1) {
50 | // ... in aframe parameter
51 | apiKey = src.split('?key=').pop() // extract API key
52 | src = src.substring(0,src.indexOf('?key=')) // remove key from src
53 | } else if (API_KEY !== '') {
54 | // ... as hardcoded constant
55 | apiKey = API_KEY
56 | }
57 | if (apiKey) {
58 | var id = src.substr(src.lastIndexOf('/') + 1) // get GLTF id
59 | if (!id) { return; }
60 | }
61 |
62 | self.remove()
63 |
64 | ;(apiKey ? getGltfUrl(id, apiKey) : getGltfUrlFromLegacyApi(src))
65 | .then(loadGblockModel)
66 | .then(function onLoaded (gltfModel) {
67 |
68 | self.model = gltfModel.scene || gltfModel.scenes[0]
69 | self.model.animations = gltfModel.animations
70 |
71 | el.setObject3D('mesh', self.model)
72 | el.emit('model-loaded', {format: 'gltf', model: self.model})
73 |
74 | })
75 | .catch(function(errorMessage){
76 |
77 | console.error('ERROR loading gblock model from "' + src +'" : ' + errorMessage)
78 | el.emit('model-error', { message: errorMessage })
79 |
80 | })
81 |
82 | },
83 |
84 | remove: function () {
85 |
86 | if (!this.model) { return; }
87 | this.el.removeObject3D('mesh')
88 |
89 | }
90 |
91 | })
92 |
93 | // private shared methods
94 |
95 | // This API call is only needed to obtain the official glTF URL of a google block model.
96 | // The glTF itself is not being proxied and gets fetched from https://vr.google.com/downloads/* directly.
97 | // https://github.com/archilogic-com/aframe-gblock/issues/1
98 | // API server code: server/index.js
99 | // try promise cache (could be in loading state)
100 | function getGltfUrl (id, apiKey) {
101 | var url = POLY_API_URL + id + '/?key=' + apiKey;
102 |
103 | // try cache
104 | var getUrlPromise = promiseCache.get(url)
105 |
106 | if (!getUrlPromise) {
107 |
108 | getUrlPromise = fetch(url).then(function (response) {
109 |
110 | // parse response
111 | return response.json().catch(function(error){
112 | // handle JSON parsing error
113 | console.log('ERROR parsing gblock API server response JSON.\nRequested Model: "' + url + '"\nError: "' + JSON.stringify(error) + '"')
114 | return Promise.reject('gblock API server error. Check console for details.')
115 | }).then(function (info) {
116 | if (info.error !== undefined) {
117 | return Promise.reject('Poly API error: ' + info.error.message)
118 | }
119 | var format = info.formats.find( format => { return format.formatType === 'GLTF' || format.formatType === 'GLTF2'; } );
120 | if ( format !== undefined ) {
121 | return format.root.url
122 | } else {
123 | return Promise.reject('Poly asset id:' + id + ' not provided in GLTF or GLTF2 format.')
124 | }
125 | })
126 |
127 | })
128 |
129 | // add to cache
130 | promiseCache.add(url, getUrlPromise)
131 |
132 | }
133 |
134 | return getUrlPromise
135 |
136 | }
137 |
138 | // Legacy mode using unofficial API
139 | // This API call is only needed to obtain the official glTF URL of a google block model.
140 | // The glTF itself is not being proxied and gets fetched from https://vr.google.com/downloads/* directly.
141 | // https://github.com/archilogic-com/aframe-gblock/issues/1
142 | // API server code: server/index.js
143 | // try promise cache (could be in loading state)
144 | function getGltfUrlFromLegacyApi (src) {
145 |
146 | // try cache
147 | var getUrlPromise = promiseCache.get(src)
148 |
149 | if (!getUrlPromise) {
150 |
151 | getUrlPromise = fetch(UNOFFICIAL_LEGACY_API_URL + '?url=' + src).then(function (response) {
152 |
153 | // parse response
154 | return response.json().catch(function(error){
155 | // handle JSON parsing error
156 | console.log('ERROR parsing gblock API server response JSON.\nRequested Model: "' + src + '"\nError: "' + JSON.stringify(error) + '"')
157 | return Promise.reject('gblock API server error. Check console for details.')
158 | }).then(function (message) {
159 | if (response.ok) {
160 | // return glTF URL
161 | return message.gltfUrl
162 | } else {
163 | // handle error response
164 | console.error('ERROR loading gblock model "'+ src +'" : ' + response.status + ' "' + message.message)
165 | return Promise.reject(message.message)
166 | }
167 | })
168 |
169 | })
170 |
171 | // add to cache
172 | promiseCache.add(src, getUrlPromise)
173 |
174 | }
175 |
176 | return getUrlPromise
177 |
178 | }
179 |
180 | // loads google block models (poly.google.com)
181 | function loadGblockModel(url, onProgress) {
182 | return new Promise(function(resolve, reject) {
183 |
184 | // create unviresal GLTF loader for google blocks
185 | // this one will inherit methods from GLTF V1 or V2 based on file version
186 | function GBlockLoader () {
187 | this.manager = THREE.DefaultLoadingManager
188 | this.path = THREE.Loader.prototype.extractUrlBase( url )
189 | }
190 |
191 | // load model
192 | var loader = new THREE.FileLoader( GBlockLoader.manager )
193 | loader.setResponseType( 'arraybuffer' )
194 | loader.load( url, function onLoad( data ) {
195 | try {
196 |
197 | // convert uint8 to json
198 | var json = JSON.parse(convertUint8ArrayToString(data))
199 |
200 | // check GLTF version
201 | var isGLTF1 = json.asset === undefined || json.asset.version[ 0 ] < 2
202 |
203 | if (isGLTF1) {
204 |
205 | fetchGLTF1Loader().then(function(GLTF1Loader){
206 |
207 | // inherit methods from GLTF V1 loader
208 | GBlockLoader.prototype = GLTF1Loader.prototype
209 | var gblockLoader = new GBlockLoader()
210 | GLTF1Loader.call(gblockLoader)
211 |
212 | // Replace original shaders with placeholders
213 | Object.keys(json.shaders).forEach(function (key, i) {
214 | if (key.indexOf('fragment') > -1) json.shaders[key].uri = fragmentShader.base64
215 | else if (key.indexOf('vertex') > -1) json.shaders[key].uri = vertexShader.base64
216 | })
217 |
218 | // convert json back to uint8 data
219 | var modifiedData = new TextEncoder('utf-8').encode(JSON.stringify(json))
220 |
221 | // parse data
222 | gblockLoader.parse( modifiedData, function onParsingDone (gltf) {
223 |
224 | // FIXME: adapt projection matrix in original shaders and do not replace materials
225 | (gltf.scene || gltf.scenes[0]).traverse(function (child) {
226 | if (child.material) child.material = new THREE.MeshPhongMaterial({ vertexColors: THREE.VertexColors })
227 | })
228 |
229 | // GLTF V1 ready
230 | resolve(gltf)
231 |
232 | }, gblockLoader.path)
233 |
234 | })
235 |
236 | } else {
237 |
238 | // inferit methods from GLTF V2 loader
239 | GBlockLoader.prototype = THREE.GLTFLoader.prototype
240 | var gblockLoader = new GBlockLoader()
241 | THREE.GLTFLoader.call(gblockLoader)
242 |
243 | // parse data
244 | gblockLoader.parse( data, gblockLoader.path, resolve, reject)
245 |
246 | }
247 |
248 | } catch ( e ) {
249 |
250 | // For SyntaxError or TypeError, return a generic failure message.
251 | reject( e.constructor === Error ? e : new Error( 'THREE.GLTFLoader: Unable to parse model.' ) )
252 |
253 | }
254 |
255 | }, onProgress, reject )
256 |
257 | })
258 | }
259 |
260 | // fetch legacy GLTF v1 loader on demand
261 | var GLFT1LoaderPromise
262 | function fetchGLTF1Loader () {
263 | if (!GLFT1LoaderPromise ) {
264 | // legacy loader will overwrite THREE.GLTFLoader so we need to keep reference to it
265 | THREE.___GLTF2Loader = THREE.GLTFLoader
266 | // fetch legacy loader for GLTF1
267 | GLFT1LoaderPromise = fetchScript(LEGACY_GLFT_V1_LOADER_URL).then(function(){
268 | // keep reference GLTF V1 loader
269 | var GLTF1Loader = THREE.GLTFLoader
270 | // restore GLTF V2 loader reference
271 | THREE.GLTFLoader = THREE.___GLTF2Loader
272 |
273 | return GLTF1Loader
274 | })
275 | }
276 | return GLFT1LoaderPromise
277 | }
278 |
279 | // from https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/GLTFLoader.js
280 | function convertUint8ArrayToString (array) {
281 | if (window.TextDecoder !== undefined) {
282 | return new TextDecoder().decode(array)
283 | }
284 | // Avoid the String.fromCharCode.apply(null, array) shortcut, which
285 | // throws a "maximum call stack size exceeded" error for large arrays.
286 | var s = '';
287 | for (var i = 0, il = array.length; i < il; i++) {
288 | s += String.fromCharCode(array[i])
289 | }
290 | return s;
291 | }
292 |
--------------------------------------------------------------------------------
/src/shaders/fragment-placeholder.glsl:
--------------------------------------------------------------------------------
1 | uniform vec3 u_color;
2 | uniform float u_metallic;
3 | uniform float u_roughness;
4 | uniform vec3 u_light0Pos;
5 | uniform vec3 u_light0Color;
6 | uniform vec3 u_light1Pos;
7 | uniform vec3 u_light1Color;
8 | uniform mat4 u_modelMatrix;
9 | uniform sampler2D u_reflectionCube;
10 | uniform sampler2D u_reflectionCubeBlur;
--------------------------------------------------------------------------------
/src/shaders/fragment.glsl:
--------------------------------------------------------------------------------
1 | precision highp float;
2 |
3 | const float INV_PI = 0.31830988618;
4 | const float PI = 3.141592654;
5 | const float _RefractiveIndex = 1.2;
6 | const float environmentStrength = 1.5;
7 |
8 | varying vec3 v_normal;
9 | varying vec3 v_position;
10 | varying vec3 v_binormal;
11 | varying vec3 v_tangent;
12 |
13 | uniform vec3 u_color;
14 | uniform float u_metallic;
15 | uniform float u_roughness;
16 | uniform vec3 u_light0Pos;
17 | uniform vec3 u_light0Color;
18 | uniform vec3 u_light1Pos;
19 | uniform vec3 u_light1Color;
20 | uniform mat4 u_modelMatrix;
21 | uniform sampler2D u_reflectionCube;
22 | uniform sampler2D u_reflectionCubeBlur;
23 |
24 | const float u_noiseIntensity = 0.015;
25 | const float colorNoiseAmount = 0.015;
26 | const float noiseScale = 700.0;
27 |
28 | uniform vec3 cameraPosition;
29 |
30 | // Noise functions from https://github.com/ashima/webgl-noise
31 | // Used under the MIT license - license text in MITLICENSE
32 | // Copyright (C) 2011 by Ashima Arts (Simplex noise)
33 | // Copyright (C) 2011-2016 by Stefan Gustavson (Classic noise and others)
34 | vec3 mod289(vec3 x) {
35 | return x - floor(x * (1.0 / 289.0)) * 289.0;
36 | }
37 |
38 | vec4 mod289(vec4 x) {
39 | return x - floor(x * (1.0 / 289.0)) * 289.0;
40 | }
41 |
42 | vec4 permute(vec4 x) {
43 | return mod289(((x*34.0)+1.0)*x);
44 | }
45 |
46 | vec4 taylorInvSqrt(vec4 r)
47 | {
48 | return 1.79284291400159 - 0.85373472095314 * r;
49 | }
50 |
51 | float snoise(vec3 v, out vec3 gradient)
52 | {
53 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
54 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
55 |
56 | // First corner
57 | vec3 i = floor(v + dot(v, C.yyy) );
58 | vec3 x0 = v - i + dot(i, C.xxx) ;
59 |
60 | // Other corners
61 | vec3 g = step(x0.yzx, x0.xyz);
62 | vec3 l = 1.0 - g;
63 | vec3 i1 = min( g.xyz, l.zxy );
64 | vec3 i2 = max( g.xyz, l.zxy );
65 |
66 | // x0 = x0 - 0.0 + 0.0 * C.xxx;
67 | // x1 = x0 - i1 + 1.0 * C.xxx;
68 | // x2 = x0 - i2 + 2.0 * C.xxx;
69 | // x3 = x0 - 1.0 + 3.0 * C.xxx;
70 | vec3 x1 = x0 - i1 + C.xxx;
71 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
72 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
73 |
74 | // Permutations
75 | i = mod289(i);
76 | vec4 p = permute( permute( permute(
77 | i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
78 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
79 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
80 |
81 | // Gradients: 7x7 points over a square, mapped onto an octahedron.
82 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
83 | float n_ = 0.142857142857; // 1.0/7.0
84 | vec3 ns = n_ * D.wyz - D.xzx;
85 |
86 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
87 |
88 | vec4 x_ = floor(j * ns.z);
89 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
90 |
91 | vec4 x = x_ *ns.x + ns.yyyy;
92 | vec4 y = y_ *ns.x + ns.yyyy;
93 | vec4 h = 1.0 - abs(x) - abs(y);
94 |
95 | vec4 b0 = vec4( x.xy, y.xy );
96 | vec4 b1 = vec4( x.zw, y.zw );
97 |
98 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
99 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
100 | vec4 s0 = floor(b0)*2.0 + 1.0;
101 | vec4 s1 = floor(b1)*2.0 + 1.0;
102 | vec4 sh = -step(h, vec4(0.0));
103 |
104 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
105 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
106 |
107 | vec3 p0 = vec3(a0.xy,h.x);
108 | vec3 p1 = vec3(a0.zw,h.y);
109 | vec3 p2 = vec3(a1.xy,h.z);
110 | vec3 p3 = vec3(a1.zw,h.w);
111 |
112 | //Normalise gradients
113 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
114 | p0 *= norm.x;
115 | p1 *= norm.y;
116 | p2 *= norm.z;
117 | p3 *= norm.w;
118 |
119 | // Mix final noise value
120 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
121 | vec4 m2 = m * m;
122 | vec4 m4 = m2 * m2;
123 | vec4 pdotx = vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3));
124 |
125 | // Determine noise gradient
126 | vec4 temp = m2 * m * pdotx;
127 | gradient = -8.0 * (temp.x * x0 + temp.y * x1 + temp.z * x2 + temp.w * x3);
128 | gradient += m4.x * p0 + m4.y * p1 + m4.z * p2 + m4.w * p3;
129 | gradient *= 42.0;
130 |
131 | return 42.0 * dot(m4, pdotx);
132 | }
133 | // End of noise code
134 |
135 | float GGX(float nDotH, float roughness2) {
136 | float nDotH2 = nDotH * nDotH;
137 | float alpha = nDotH2 * roughness2 + 1.0 - nDotH2;
138 | float denominator = PI * alpha * alpha;
139 | return (nDotH2 > 0.0 ? 1.0 : 0.0) * roughness2 / denominator;
140 | }
141 |
142 | float BlinnPhongNDF(float nDotH) {
143 | float exponent = (2.0 / (u_roughness * u_roughness) - 2.0);
144 | float coeff = 1.0 / (PI * u_roughness * u_roughness);
145 | return coeff * pow(nDotH, exponent);
146 | }
147 |
148 | float CT_GeoAtten(float nDotV, float nDotH, float vDotH, float nDotL, float lDotH) {
149 | float a = (2.0 * nDotH * nDotV) / vDotH;
150 | float b = (2.0 * nDotH * nDotL) / lDotH;
151 | return min(1.0, min(a, b));
152 | }
153 |
154 | float GeoAtten(float nDotV) {
155 | float c = nDotV / (u_roughness * sqrt(1.0 - nDotV * nDotV));
156 | return c >= 1.6 ? 1.0 :
157 | (3.535 * c + 2.181 * c * c) / (1.0 + 2.276 * c + 2.577 * c * c);
158 |
159 | }
160 |
161 | vec3 evaluateFresnelSchlick(float vDotH, vec3 f0) {
162 | return f0 + (1.0 - f0) * pow(1.0 - vDotH, 5.0);
163 | }
164 |
165 | float saturate(float value) {
166 | return clamp(value, 0.0, 1.0);
167 | }
168 |
169 | vec3 saturate(vec3 value) {
170 | return clamp(value, 0.0, 1.0);
171 | }
172 |
173 |
174 | mat3 transpose(mat3 inMat) {
175 | return mat3(inMat[0][0], inMat[0][1], inMat[0][2],
176 | inMat[1][0], inMat[1][1], inMat[1][2],
177 | inMat[2][0], inMat[2][1], inMat[2][2]);
178 | }
179 |
180 | void generatePapercraftColorNormal(vec3 normal, vec3 tangent, vec3 binormal, vec3 noisePos, inout vec4 outColorMult, inout vec3 outNormal) {
181 | mat3 tangentToObject;
182 | tangentToObject[0] = vec3(tangent.x, tangent.y, tangent.z);
183 | tangentToObject[1] = vec3(binormal.x, binormal.y, binormal.z);
184 | tangentToObject[2] = vec3(normal.x, normal.y, normal.z);
185 |
186 | mat3 objectToTangent = transpose(tangentToObject);
187 |
188 |
189 | vec3 intensificator = vec3(u_noiseIntensity, u_noiseIntensity, 1.0);
190 | vec3 tangentPos = objectToTangent * noisePos;
191 | vec3 gradient = vec3(0.0);
192 | float noiseOut = snoise(tangentPos * noiseScale, gradient);
193 |
194 | vec3 tangentSpaceNormal = normalize(intensificator * vec3(gradient.xy, 1.0));
195 | outNormal = tangentToObject * tangentSpaceNormal;
196 |
197 | outColorMult = vec4(vec3(1.0 + noiseOut * colorNoiseAmount), 1.0);
198 | }
199 |
200 | void evaluatePBRLight(
201 | vec3 materialColor,
202 | vec3 lightColor,
203 | float nDotL,
204 | float nDotV,
205 | float nDotH,
206 | float vDotH,
207 | float lDotH,
208 | inout vec3 diffuseOut,
209 | inout vec3 specularOut,
210 | inout vec3 debug,
211 | float specAmount) {
212 | vec3 diffuse = INV_PI * nDotL * lightColor;
213 |
214 | vec3 d = vec3(GGX(nDotH, u_roughness * u_roughness));
215 | vec3 g = vec3(CT_GeoAtten(nDotV, nDotH, vDotH, nDotL, lDotH));
216 | vec3 f0 = vec3(abs((1.0 - _RefractiveIndex) / (1.0 + _RefractiveIndex)));
217 | f0 = f0 * f0;
218 | f0 = mix(f0, materialColor, u_metallic);
219 | vec3 f = evaluateFresnelSchlick(vDotH, f0);
220 | diffuseOut = diffuseOut + (1.0 - saturate(f)) * (1.0 - u_metallic) * lightColor * diffuse;
221 | specularOut = specularOut + specAmount * lightColor * saturate((d * g * f) / saturate(4.0 * saturate(nDotH) * nDotV));
222 | debug = saturate(g);
223 | }
224 |
225 | void setParams(vec3 worldPosition,
226 | inout vec3 normal,
227 | inout vec3 view,
228 | inout float nDotV) {
229 | normal = normalize(normal);
230 | view = normalize(cameraPosition - worldPosition);
231 | nDotV = saturate(dot(normal, view));
232 | }
233 |
234 | void setLightParams(vec3 lightPosition,
235 | vec3 worldPosition,
236 | vec3 V,
237 | vec3 N,
238 | inout vec3 L,
239 | inout vec3 H,
240 | inout float nDotL,
241 | inout float nDotH,
242 | inout float vDotH,
243 | inout float lDotH) {
244 | L = normalize(lightPosition - worldPosition);
245 | H = normalize(L + V);
246 |
247 | nDotL = saturate(dot(N, L));
248 | nDotH = saturate(dot(N, H));
249 | vDotH = saturate(dot(V, H));
250 | lDotH = saturate(dot(L, H));
251 | }
252 |
253 | void main() {
254 | vec3 materialColor = u_color;
255 |
256 | vec4 outColorMult;
257 | vec3 normalisedNormal = v_normal;
258 | vec3 normalisedView;
259 | float nDotV;
260 |
261 | generatePapercraftColorNormal(v_normal, v_tangent, v_binormal, v_position, outColorMult, normalisedNormal);
262 | setParams(v_position, normalisedNormal, normalisedView, nDotV);
263 |
264 | vec3 normalisedLight;
265 | vec3 normalisedHalf;
266 |
267 | float nDotL;
268 | float nDotH;
269 | float vDotH;
270 | float lDotH;
271 |
272 | setLightParams(u_light0Pos, v_position, normalisedView, normalisedNormal,
273 | normalisedLight, normalisedHalf, nDotL, nDotH, vDotH, lDotH);
274 |
275 | vec3 diffuse = vec3(0.0, 0.0, 0.0);
276 | vec3 specular = vec3(0.0, 0.0, 0.0);
277 | vec3 debug = vec3(0.0, 0.0, 0.0);
278 |
279 | evaluatePBRLight(materialColor * outColorMult.rgb, u_light0Color, nDotL, nDotV, nDotH, vDotH, lDotH, diffuse, specular, debug, 1.0);
280 | vec3 ambient = (1.0 - u_metallic) * materialColor * outColorMult.rgb * 0.0;
281 |
282 | setLightParams(u_light1Pos, v_position, normalisedView, normalisedNormal,
283 | normalisedLight, normalisedHalf, nDotL, nDotH, vDotH, lDotH);
284 | evaluatePBRLight(materialColor * outColorMult.rgb, u_light1Color, nDotL, nDotV, nDotH, vDotH, lDotH, diffuse, specular, debug, 1.0);
285 |
286 | vec3 R = -reflect(normalisedView, normalisedNormal);
287 | setLightParams(v_position + R, v_position, normalisedView, normalisedNormal,
288 | normalisedLight, normalisedHalf, nDotL, nDotH, vDotH, lDotH);
289 | vec3 envColor = mix(materialColor, vec3(1.0, 1.0, 1.0), 0.7);
290 | evaluatePBRLight(materialColor * outColorMult.rgb, envColor * environmentStrength, nDotL, nDotV, nDotH, vDotH, lDotH, diffuse, specular, debug, 0.25);
291 | gl_FragColor = vec4(specular + diffuse * materialColor, 1.0);
292 | }
293 |
--------------------------------------------------------------------------------
/src/shaders/vertex-placeholder.glsl:
--------------------------------------------------------------------------------
1 | varying vec3 v_normal;
2 | varying vec3 v_position;
3 | varying vec3 v_binormal;
4 | varying vec3 v_tangent;
5 |
--------------------------------------------------------------------------------
/src/shaders/vertex.glsl:
--------------------------------------------------------------------------------
1 | uniform mat4 u_modelViewMatrix;
2 | uniform mat4 u_projectionMatrix;
3 | uniform mat3 u_normalMatrix;
4 |
5 | attribute vec3 a_position;
6 | attribute vec3 a_normal;
7 |
8 | varying vec3 v_normal;
9 | varying vec3 v_position;
10 | varying vec3 v_binormal;
11 | varying vec3 v_tangent;
12 |
13 | void main() {
14 | vec3 objPosition = a_position;
15 | vec4 worldPosition = vec4(objPosition, 1.0);
16 |
17 | // Our object space has no rotation and no scale, so this is fine.
18 | v_normal = a_normal;
19 | v_position = worldPosition.xyz;
20 | // Looking for an arbitrary vector that isn't parallel to the normal. Avoiding axis directions should improve our chances.
21 | vec3 arbitraryVector = normalize(vec3(0.42, -0.21, 0.15));
22 | vec3 alternateArbitraryVector = normalize(vec3(0.43, 1.5, 0.15));
23 | // If arbitrary vector is parallel to the normal, choose a different one.
24 | v_tangent = normalize(abs(dot(v_normal, arbitraryVector)) < 1.0 ? cross(v_normal, arbitraryVector) : cross(v_normal, alternateArbitraryVector));
25 | v_binormal = normalize(cross(v_normal, v_tangent));
26 |
27 | gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(objPosition, 1.0);
28 | }
29 |
--------------------------------------------------------------------------------
/src/utils/fetch-script.js:
--------------------------------------------------------------------------------
1 | // from https://github.com/archilogic-com/3dio-js/blob/master/src/utils/io/fetch-script.js
2 |
3 | import PromiseCache from './promise-cache.js'
4 |
5 | var promiseCache = new PromiseCache()
6 |
7 | export default function fetchScript (url) {
8 |
9 | // module wrapper
10 | window.___modules = window.___modules || {}
11 |
12 | // return module if it has been loaded already
13 | if (window.___modules[url]) {
14 | return Promise.resolve(window.___modules[url])
15 |
16 | } else {
17 |
18 | // try promise cache (could be in loading state)
19 | var promiseFromCache = promiseCache.get(url)
20 | if (promiseFromCache) return promiseFromCache
21 |
22 | // load code and use module wrapper
23 | var fetchPromise = fetch(url).then(function(response){
24 | if (!response.ok) throw 'Could not load script from URL: '+url
25 | return response.text()
26 | }).then(function(code){
27 |
28 | // check module type
29 | var moduleWrapper
30 | if (code.indexOf('define(function()') > -1) {
31 | // AMD
32 | moduleWrapper = code+'\nfunction define(cb){ window.___modules["'+url+'"] = cb(); };'
33 | } else {
34 | // CommonJS
35 | moduleWrapper = 'window.___modules["'+url+'"] = (function(){ var exports = {}, module = {exports:exports};'+code+'\nreturn module.exports\n})()'
36 | }
37 |
38 | var script = document.createElement('script')
39 | try {
40 | script.appendChild(document.createTextNode(moduleWrapper))
41 | document.body.appendChild(script)
42 | } catch (e) {
43 | script.text = moduleWrapper
44 | document.body.appendChild(script)
45 | }
46 | return window.___modules[url]
47 | })
48 |
49 | // add to cache
50 | promiseCache.add(url, fetchPromise)
51 |
52 | return fetchPromise
53 |
54 | }
55 |
56 | }
--------------------------------------------------------------------------------
/src/utils/promise-cache.js:
--------------------------------------------------------------------------------
1 | import sortBy from 'lodash/sortBy'
2 |
3 | // main
4 |
5 | function PromiseCache (args) {
6 |
7 | var args = args || {}
8 |
9 | this.maxResolvedCache = args.maxResolvedCache || 1000
10 |
11 | this._pendingPromises = {}
12 | this._resolvedPromises = {}
13 |
14 | }
15 |
16 | PromiseCache.prototype = {
17 |
18 | add: function (key, promise) {
19 |
20 | var self = this
21 |
22 | // check if it already exists
23 | if (this._pendingPromises[ key ]) {
24 | return this._pendingPromises[ key ]
25 | }
26 | if (this._resolvedPromises[ key ]) {
27 | return this._resolvedPromises[ key ]
28 | }
29 |
30 | // create cache object
31 | var cacheObject = {
32 | key: key,
33 | timestamp: Date.now(),
34 | promise: promise
35 | }
36 |
37 | // add to store
38 | this._pendingPromises[ key ] = cacheObject
39 |
40 | // move to resolved store and update state when resolved
41 | promise.then(function (data) {
42 |
43 | var cacheObject = self._pendingPromises[ key ]
44 | delete self._pendingPromises[ key ]
45 |
46 | cacheObject.data = data
47 |
48 | self._resolvedPromises[ key ] = cacheObject
49 |
50 | }, function () {
51 |
52 | delete self._pendingPromises[ key ]
53 |
54 | })
55 |
56 | // collect garbage
57 | this._collectGarbage()
58 |
59 | // return cache object
60 | return cacheObject
61 |
62 | },
63 |
64 | get: function (key) {
65 |
66 | // check store
67 | var cacheObject = this._pendingPromises[ key ] || this._resolvedPromises[ key ]
68 | if (!cacheObject) {
69 | return false
70 | }
71 |
72 | // update timestamp
73 | cacheObject.timestamp = Date.now()
74 |
75 | // return promise
76 | return cacheObject.promise
77 |
78 | },
79 |
80 | purge: function () {
81 |
82 | for (var key in this._resolvedPromises) {
83 | delete this._resolvedPromises[ key ]
84 | }
85 |
86 | },
87 |
88 | _collectGarbage: function () {
89 |
90 | // sort archive by timestamp
91 | var sortedPromises = sortBy(this._resolvedPromises, function (obj) {
92 | return obj.timestamp
93 | })
94 |
95 | // the amount of cache objects that have to be removed
96 | var removeCount = (sortedPromises.length - this.maxResolvedCache)
97 | if (removeCount <= 0) {
98 | return
99 | }
100 |
101 | for (var i = 0; i < removeCount; i++) {
102 | delete this._resolvedPromises[ sortedPromises[ i ].key ]
103 | }
104 |
105 | }
106 |
107 | }
108 |
109 | export default PromiseCache
--------------------------------------------------------------------------------