You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
6.3 KiB

// Production steps of ECMA-262, Edition 6, 22.1.2.1
// Reference: http://www.ecma-international.org/ecma-262/6.0/#sec-array.from
module.exports = (function() {
var isCallable = function(fn) {
return typeof fn === 'function';
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};
var iteratorProp = function(value) {
if(value != null) {
if(['string','number','boolean','symbol'].indexOf(typeof value) > -1){
return Symbol.iterator;
} else if (
(typeof Symbol !== 'undefined') &&
('iterator' in Symbol) &&
(Symbol.iterator in value)
) {
return Symbol.iterator;
}
// Support "@@iterator" placeholder, Gecko 27 to Gecko 35
else if ('@@iterator' in value) {
return '@@iterator';
}
}
};
var getMethod = function(O, P) {
// Assert: IsPropertyKey(P) is true.
if (O != null && P != null) {
// Let func be GetV(O, P).
var func = O[P];
// ReturnIfAbrupt(func).
// If func is either undefined or null, return undefined.
if(func == null) {
return void 0;
}
// If IsCallable(func) is false, throw a TypeError exception.
if (!isCallable(func)) {
throw new TypeError(func + ' is not a function');
}
return func;
}
};
var iteratorStep = function(iterator) {
// Let result be IteratorNext(iterator).
// ReturnIfAbrupt(result).
var result = iterator.next();
// Let done be IteratorComplete(result).
// ReturnIfAbrupt(done).
var done = Boolean(result.done);
// If done is true, return false.
if(done) {
return false;
}
// Return result.
return result;
};
// The length property of the from method is 1.
return function from(items /*, mapFn, thisArg */ ) {
'use strict';
// 1. Let C be the this value.
var C = this;
// 2. If mapfn is undefined, let mapping be false.
var mapFn = arguments.length > 1 ? arguments[1] : void 0;
var T;
if (typeof mapFn !== 'undefined') {
// 3. else
// a. If IsCallable(mapfn) is false, throw a TypeError exception.
if (!isCallable(mapFn)) {
throw new TypeError(
'Array.from: when provided, the second argument must be a function'
);
}
// b. If thisArg was supplied, let T be thisArg; else let T
// be undefined.
if (arguments.length > 2) {
T = arguments[2];
}
// c. Let mapping be true (implied by mapFn)
}
var A, k;
// 4. Let usingIterator be GetMethod(items, @@iterator).
// 5. ReturnIfAbrupt(usingIterator).
var usingIterator = getMethod(items, iteratorProp(items));
// 6. If usingIterator is not undefined, then
if (usingIterator !== void 0) {
// a. If IsConstructor(C) is true, then
// i. Let A be the result of calling the [[Construct]]
// internal method of C with an empty argument list.
// b. Else,
// i. Let A be the result of the abstract operation ArrayCreate
// with argument 0.
// c. ReturnIfAbrupt(A).
A = isCallable(C) ? Object(new C()) : [];
// d. Let iterator be GetIterator(items, usingIterator).
var iterator = usingIterator.call(items);
// e. ReturnIfAbrupt(iterator).
if (iterator == null) {
throw new TypeError(
'Array.from requires an array-like or iterable object'
);
}
// f. Let k be 0.
k = 0;
// g. Repeat
var next, nextValue;
while (true) {
// i. Let Pk be ToString(k).
// ii. Let next be IteratorStep(iterator).
// iii. ReturnIfAbrupt(next).
next = iteratorStep(iterator);
// iv. If next is false, then
if (!next) {
// 1. Let setStatus be Set(A, "length", k, true).
// 2. ReturnIfAbrupt(setStatus).
A.length = k;
// 3. Return A.
return A;
}
// v. Let nextValue be IteratorValue(next).
// vi. ReturnIfAbrupt(nextValue)
nextValue = next.value;
// vii. If mapping is true, then
// 1. Let mappedValue be Call(mapfn, T, «nextValue, k»).
// 2. If mappedValue is an abrupt completion, return
// IteratorClose(iterator, mappedValue).
// 3. Let mappedValue be mappedValue.[[value]].
// viii. Else, let mappedValue be nextValue.
// ix. Let defineStatus be the result of
// CreateDataPropertyOrThrow(A, Pk, mappedValue).
// x. [TODO] If defineStatus is an abrupt completion, return
// IteratorClose(iterator, defineStatus).
if (mapFn) {
A[k] = mapFn.call(T, nextValue, k);
}
else {
A[k] = nextValue;
}
// xi. Increase k by 1.
k++;
}
// 7. Assert: items is not an Iterable so assume it is
// an array-like object.
} else {
// 8. Let arrayLike be ToObject(items).
var arrayLike = Object(items);
// 9. ReturnIfAbrupt(items).
if (items == null) {
throw new TypeError(
'Array.from requires an array-like object - not null or undefined'
);
}
// 10. Let len be ToLength(Get(arrayLike, "length")).
// 11. ReturnIfAbrupt(len).
var len = toLength(arrayLike.length);
// 12. If IsConstructor(C) is true, then
// a. Let A be Construct(C, «len»).
// 13. Else
// a. Let A be ArrayCreate(len).
// 14. ReturnIfAbrupt(A).
A = isCallable(C) ? Object(new C(len)) : new Array(len);
// 15. Let k be 0.
k = 0;
// 16. Repeat, while k < len… (also steps a - h)
var kValue;
while (k < len) {
kValue = arrayLike[k];
if (mapFn) {
A[k] = mapFn.call(T, kValue, k);
}
else {
A[k] = kValue;
}
k++;
}
// 17. Let setStatus be Set(A, "length", len, true).
// 18. ReturnIfAbrupt(setStatus).
A.length = len;
// 19. Return A.
}
return A;
};
})();