├── .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 | ![](docs/screenshot3.png) 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 |
161 | gblock component by 3d.io for A-Frame loading remixable models from Ti Kawamoto and Alberto Calvo 162 |
163 |
164 | 165 |
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 --------------------------------------------------------------------------------