| /* ! |
| * type-detect |
| * Copyright(c) 2013 jake luer <jake@alogicalparadox.com> |
| * MIT Licensed |
| */ |
| const promiseExists = typeof Promise === 'function'; |
| |
| /* eslint-disable no-undef */ |
| const globalObject = typeof self === 'object' ? self : global; // eslint-disable-line id-blacklist |
| |
| const symbolExists = typeof Symbol !== 'undefined'; |
| const mapExists = typeof Map !== 'undefined'; |
| const setExists = typeof Set !== 'undefined'; |
| const weakMapExists = typeof WeakMap !== 'undefined'; |
| const weakSetExists = typeof WeakSet !== 'undefined'; |
| const dataViewExists = typeof DataView !== 'undefined'; |
| const symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined'; |
| const symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; |
| const setEntriesExists = setExists && typeof Set.prototype.entries === 'function'; |
| const mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function'; |
| const setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries()); |
| const mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries()); |
| const arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; |
| const arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]()); |
| const stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function'; |
| const stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]()); |
| const toStringLeftSliceLength = 8; |
| const toStringRightSliceLength = -1; |
| /** |
| * ### typeOf (obj) |
| * |
| * Uses `Object.prototype.toString` to determine the type of an object, |
| * normalising behaviour across engine versions & well optimised. |
| * |
| * @param {Mixed} object |
| * @return {String} object type |
| * @api public |
| */ |
| export default function typeDetect(obj) { |
| /* ! Speed optimisation |
| * Pre: |
| * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled) |
| * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled) |
| * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled) |
| * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled) |
| * function x 2,556,769 ops/sec ±1.73% (77 runs sampled) |
| * Post: |
| * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled) |
| * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled) |
| * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled) |
| * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled) |
| * function x 31,296,870 ops/sec ±0.96% (83 runs sampled) |
| */ |
| const typeofObj = typeof obj; |
| if (typeofObj !== 'object') { |
| return typeofObj; |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * null x 28,645,765 ops/sec ±1.17% (82 runs sampled) |
| * Post: |
| * null x 36,428,962 ops/sec ±1.37% (84 runs sampled) |
| */ |
| if (obj === null) { |
| return 'null'; |
| } |
| |
| /* ! Spec Conformance |
| * Test: `Object.prototype.toString.call(window)`` |
| * - Node === "[object global]" |
| * - Chrome === "[object global]" |
| * - Firefox === "[object Window]" |
| * - PhantomJS === "[object Window]" |
| * - Safari === "[object Window]" |
| * - IE 11 === "[object Window]" |
| * - IE Edge === "[object Window]" |
| * Test: `Object.prototype.toString.call(this)`` |
| * - Chrome Worker === "[object global]" |
| * - Firefox Worker === "[object DedicatedWorkerGlobalScope]" |
| * - Safari Worker === "[object DedicatedWorkerGlobalScope]" |
| * - IE 11 Worker === "[object WorkerGlobalScope]" |
| * - IE Edge Worker === "[object WorkerGlobalScope]" |
| */ |
| if (obj === globalObject) { |
| return 'global'; |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled) |
| * Post: |
| * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled) |
| */ |
| if ( |
| Array.isArray(obj) && |
| (symbolToStringTagExists === false || !(Symbol.toStringTag in obj)) |
| ) { |
| return 'Array'; |
| } |
| |
| // Not caching existence of `window` and related properties due to potential |
| // for `window` to be unset before tests in quasi-browser environments. |
| if (typeof window === 'object' && window !== null) { |
| /* ! Spec Conformance |
| * (https://html.spec.whatwg.org/multipage/browsers.html#location) |
| * WhatWG HTML$7.7.3 - The `Location` interface |
| * Test: `Object.prototype.toString.call(window.location)`` |
| * - IE <=11 === "[object Object]" |
| * - IE Edge <=13 === "[object Object]" |
| */ |
| if (typeof window.location === 'object' && obj === window.location) { |
| return 'Location'; |
| } |
| |
| /* ! Spec Conformance |
| * (https://html.spec.whatwg.org/#document) |
| * WhatWG HTML$3.1.1 - The `Document` object |
| * Note: Most browsers currently adher to the W3C DOM Level 2 spec |
| * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268) |
| * which suggests that browsers should use HTMLTableCellElement for |
| * both TD and TH elements. WhatWG separates these. |
| * WhatWG HTML states: |
| * > For historical reasons, Window objects must also have a |
| * > writable, configurable, non-enumerable property named |
| * > HTMLDocument whose value is the Document interface object. |
| * Test: `Object.prototype.toString.call(document)`` |
| * - Chrome === "[object HTMLDocument]" |
| * - Firefox === "[object HTMLDocument]" |
| * - Safari === "[object HTMLDocument]" |
| * - IE <=10 === "[object Document]" |
| * - IE 11 === "[object HTMLDocument]" |
| * - IE Edge <=13 === "[object HTMLDocument]" |
| */ |
| if (typeof window.document === 'object' && obj === window.document) { |
| return 'Document'; |
| } |
| |
| if (typeof window.navigator === 'object') { |
| /* ! Spec Conformance |
| * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray) |
| * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray |
| * Test: `Object.prototype.toString.call(navigator.mimeTypes)`` |
| * - IE <=10 === "[object MSMimeTypesCollection]" |
| */ |
| if (typeof window.navigator.mimeTypes === 'object' && |
| obj === window.navigator.mimeTypes) { |
| return 'MimeTypeArray'; |
| } |
| |
| /* ! Spec Conformance |
| * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) |
| * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray |
| * Test: `Object.prototype.toString.call(navigator.plugins)`` |
| * - IE <=10 === "[object MSPluginsCollection]" |
| */ |
| if (typeof window.navigator.plugins === 'object' && |
| obj === window.navigator.plugins) { |
| return 'PluginArray'; |
| } |
| } |
| |
| if ((typeof window.HTMLElement === 'function' || |
| typeof window.HTMLElement === 'object') && |
| obj instanceof window.HTMLElement) { |
| /* ! Spec Conformance |
| * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) |
| * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement` |
| * Test: `Object.prototype.toString.call(document.createElement('blockquote'))`` |
| * - IE <=10 === "[object HTMLBlockElement]" |
| */ |
| if (obj.tagName === 'BLOCKQUOTE') { |
| return 'HTMLQuoteElement'; |
| } |
| |
| /* ! Spec Conformance |
| * (https://html.spec.whatwg.org/#htmltabledatacellelement) |
| * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement` |
| * Note: Most browsers currently adher to the W3C DOM Level 2 spec |
| * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) |
| * which suggests that browsers should use HTMLTableCellElement for |
| * both TD and TH elements. WhatWG separates these. |
| * Test: Object.prototype.toString.call(document.createElement('td')) |
| * - Chrome === "[object HTMLTableCellElement]" |
| * - Firefox === "[object HTMLTableCellElement]" |
| * - Safari === "[object HTMLTableCellElement]" |
| */ |
| if (obj.tagName === 'TD') { |
| return 'HTMLTableDataCellElement'; |
| } |
| |
| /* ! Spec Conformance |
| * (https://html.spec.whatwg.org/#htmltableheadercellelement) |
| * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement` |
| * Note: Most browsers currently adher to the W3C DOM Level 2 spec |
| * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) |
| * which suggests that browsers should use HTMLTableCellElement for |
| * both TD and TH elements. WhatWG separates these. |
| * Test: Object.prototype.toString.call(document.createElement('th')) |
| * - Chrome === "[object HTMLTableCellElement]" |
| * - Firefox === "[object HTMLTableCellElement]" |
| * - Safari === "[object HTMLTableCellElement]" |
| */ |
| if (obj.tagName === 'TH') { |
| return 'HTMLTableHeaderCellElement'; |
| } |
| } |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled) |
| * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled) |
| * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled) |
| * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled) |
| * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled) |
| * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled) |
| * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled) |
| * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled) |
| * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled) |
| * Post: |
| * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled) |
| * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled) |
| * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled) |
| * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled) |
| * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled) |
| * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled) |
| * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled) |
| * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled) |
| * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled) |
| */ |
| const stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]); |
| if (typeof stringTag === 'string') { |
| return stringTag; |
| } |
| |
| const objPrototype = Object.getPrototypeOf(obj); |
| /* ! Speed optimisation |
| * Pre: |
| * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled) |
| * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled) |
| * Post: |
| * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled) |
| * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled) |
| */ |
| if (objPrototype === RegExp.prototype) { |
| return 'RegExp'; |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * date x 2,130,074 ops/sec ±4.42% (68 runs sampled) |
| * Post: |
| * date x 3,953,779 ops/sec ±1.35% (77 runs sampled) |
| */ |
| if (objPrototype === Date.prototype) { |
| return 'Date'; |
| } |
| |
| /* ! Spec Conformance |
| * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag) |
| * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise": |
| * Test: `Object.prototype.toString.call(Promise.resolve())`` |
| * - Chrome <=47 === "[object Object]" |
| * - Edge <=20 === "[object Object]" |
| * - Firefox 29-Latest === "[object Promise]" |
| * - Safari 7.1-Latest === "[object Promise]" |
| */ |
| if (promiseExists && objPrototype === Promise.prototype) { |
| return 'Promise'; |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * set x 2,222,186 ops/sec ±1.31% (82 runs sampled) |
| * Post: |
| * set x 4,545,879 ops/sec ±1.13% (83 runs sampled) |
| */ |
| if (setExists && objPrototype === Set.prototype) { |
| return 'Set'; |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * map x 2,396,842 ops/sec ±1.59% (81 runs sampled) |
| * Post: |
| * map x 4,183,945 ops/sec ±6.59% (82 runs sampled) |
| */ |
| if (mapExists && objPrototype === Map.prototype) { |
| return 'Map'; |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled) |
| * Post: |
| * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled) |
| */ |
| if (weakSetExists && objPrototype === WeakSet.prototype) { |
| return 'WeakSet'; |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled) |
| * Post: |
| * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled) |
| */ |
| if (weakMapExists && objPrototype === WeakMap.prototype) { |
| return 'WeakMap'; |
| } |
| |
| /* ! Spec Conformance |
| * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag) |
| * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView": |
| * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))`` |
| * - Edge <=13 === "[object Object]" |
| */ |
| if (dataViewExists && objPrototype === DataView.prototype) { |
| return 'DataView'; |
| } |
| |
| /* ! Spec Conformance |
| * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag) |
| * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator": |
| * Test: `Object.prototype.toString.call(new Map().entries())`` |
| * - Edge <=13 === "[object Object]" |
| */ |
| if (mapExists && objPrototype === mapIteratorPrototype) { |
| return 'Map Iterator'; |
| } |
| |
| /* ! Spec Conformance |
| * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag) |
| * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator": |
| * Test: `Object.prototype.toString.call(new Set().entries())`` |
| * - Edge <=13 === "[object Object]" |
| */ |
| if (setExists && objPrototype === setIteratorPrototype) { |
| return 'Set Iterator'; |
| } |
| |
| /* ! Spec Conformance |
| * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag) |
| * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator": |
| * Test: `Object.prototype.toString.call([][Symbol.iterator]())`` |
| * - Edge <=13 === "[object Object]" |
| */ |
| if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) { |
| return 'Array Iterator'; |
| } |
| |
| /* ! Spec Conformance |
| * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag) |
| * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator": |
| * Test: `Object.prototype.toString.call(''[Symbol.iterator]())`` |
| * - Edge <=13 === "[object Object]" |
| */ |
| if (stringIteratorExists && objPrototype === stringIteratorPrototype) { |
| return 'String Iterator'; |
| } |
| |
| /* ! Speed optimisation |
| * Pre: |
| * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled) |
| * Post: |
| * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled) |
| */ |
| if (objPrototype === null) { |
| return 'Object'; |
| } |
| |
| return Object |
| .prototype |
| .toString |
| .call(obj) |
| .slice(toStringLeftSliceLength, toStringRightSliceLength); |
| } |