diff --git a/src/.eslintrc b/src/.eslintrc index 98bd042585b..428c1ee3b53 100644 --- a/src/.eslintrc +++ b/src/.eslintrc @@ -6,7 +6,8 @@ "globals": { "Promise": true, "Float32Array": true, - "Uint8Array": true + "Uint8Array": true, + "ArrayBuffer": true }, "rules": { "strict": [2, "global"], diff --git a/src/lib/index.js b/src/lib/index.js index d1ccb36f1a6..6f9a3d57c90 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -15,6 +15,7 @@ var lib = module.exports = {}; lib.nestedProperty = require('./nested_property'); lib.isPlainObject = require('./is_plain_object'); +lib.isArray = require('./is_array'); var coerceModule = require('./coerce'); lib.valObjects = coerceModule.valObjects; diff --git a/src/lib/is_array.js b/src/lib/is_array.js new file mode 100644 index 00000000000..c01cdac4007 --- /dev/null +++ b/src/lib/is_array.js @@ -0,0 +1,16 @@ +/** +* Copyright 2012-2016, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +/** + * Return true for arrays, whether they're untyped or not. + */ +module.exports = function isArray(a) { + return Array.isArray(a) || ArrayBuffer.isView(a); +}; diff --git a/src/lib/nested_property.js b/src/lib/nested_property.js index ac51e2ab4bd..a3e004c2caa 100644 --- a/src/lib/nested_property.js +++ b/src/lib/nested_property.js @@ -10,6 +10,7 @@ 'use strict'; var isNumeric = require('fast-isnumeric'); +var isArray = require('./is_array'); /** * convert a string s (such as 'xaxis.range[0]') @@ -93,7 +94,7 @@ function npGet(cont, parts) { } return allSame ? out[0] : out; } - if(typeof curPart === 'number' && !Array.isArray(curCont)) { + if(typeof curPart === 'number' && !isArray(curCont)) { return undefined; } curCont = curCont[curPart]; @@ -122,7 +123,7 @@ function isDataArray(val, key) { var containers = ['annotations', 'shapes', 'range', 'domain', 'buttons'], isNotAContainer = containers.indexOf(key) === -1; - return Array.isArray(val) && isNotAContainer; + return isArray(val) && isNotAContainer; } function npSet(cont, parts) { @@ -136,7 +137,7 @@ function npSet(cont, parts) { for(i = 0; i < parts.length - 1; i++) { curPart = parts[i]; - if(typeof curPart === 'number' && !Array.isArray(curCont)) { + if(typeof curPart === 'number' && !isArray(curCont)) { throw 'array index but container is not an array'; } @@ -170,7 +171,7 @@ function npSet(cont, parts) { // handle special -1 array index function setArrayAll(containerArray, innerParts, val) { - var arrayVal = Array.isArray(val), + var arrayVal = isArray(val), allSet = true, thisVal = val, deleteThis = arrayVal ? false : emptyObj(val), @@ -215,7 +216,7 @@ function pruneContainers(containerLevels) { for(i = containerLevels.length - 1; i >= 0; i--) { curCont = containerLevels[i]; remainingKeys = false; - if(Array.isArray(curCont)) { + if(isArray(curCont)) { for(j = curCont.length - 1; j >= 0; j--) { if(emptyObj(curCont[j])) { if(remainingKeys) curCont[j] = undefined; @@ -239,7 +240,7 @@ function pruneContainers(containerLevels) { function emptyObj(obj) { if(obj === undefined || obj === null) return true; if(typeof obj !== 'object') return false; // any plain value - if(Array.isArray(obj)) return !obj.length; // [] + if(isArray(obj)) return !obj.length; // [] return !Object.keys(obj).length; // {} } diff --git a/test/jasmine/tests/is_array_test.js b/test/jasmine/tests/is_array_test.js new file mode 100644 index 00000000000..bea361516db --- /dev/null +++ b/test/jasmine/tests/is_array_test.js @@ -0,0 +1,47 @@ +var Lib = require('@src/lib'); + +describe('isArray', function() { + 'use strict'; + + var isArray = Lib.isArray; + + function A() {} + + var shouldPass = [ + [], + new Array(10), + new Float32Array(1), + new Int32Array([1, 2, 3]) + ]; + + var shouldFail = [ + A, + new A(), + document, + window, + null, + undefined, + 'string', + true, + false, + NaN, + Infinity, + /foo/, + '\n', + new Date(), + new RegExp('foo'), + new String('string') + ]; + + shouldPass.forEach(function(obj) { + it('treats ' + JSON.stringify(obj) + ' as an array', function() { + expect(isArray(obj)).toBe(true); + }); + }); + + shouldFail.forEach(function(obj) { + it('treats ' + JSON.stringify(obj !== window ? obj : 'window') + ' as NOT an array', function() { + expect(isArray(obj)).toBe(false); + }); + }); +}); diff --git a/test/jasmine/tests/is_plain_object_test.js b/test/jasmine/tests/is_plain_object_test.js index 95b199db902..cf2ba311f25 100644 --- a/test/jasmine/tests/is_plain_object_test.js +++ b/test/jasmine/tests/is_plain_object_test.js @@ -20,6 +20,7 @@ describe('isPlainObject', function() { null, undefined, [], + new Float32Array(1), 'string', true, false,