Skip to content
2 changes: 1 addition & 1 deletion src/shim/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export interface MapConstructor {
export let Map: MapConstructor = global.Map;

if (!has('es6-map')) {
Map = class Map<K, V> {
Map = global.Map = class Map<K, V> {
protected readonly _keys: K[] = [];
protected readonly _values: V[] = [];

Expand Down
2 changes: 1 addition & 1 deletion src/shim/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This package provides functional shims for ECMAScript, access to the Typescript helpers, and a quick way to include the polyfills needed to run Dojo in the browser.

It is targeted at providing function shims for ECMAScript 6 and beyond targeted at ECMAScript 5. It is different than other solutions of shimming or polyfilling functionality, in that it does not provide the functionality via augmenting the built-in classes in the global namespace.
It is targeted at providing polyfills for ECMAScript 6 and beyond targeted at ECMAScript 5. For backwards compatibility function shims are also provided in some cases.

There are two exceptions to this. One is the `Promise` object, which needs to be globally available for async/await operations. The other exception is the `Symbol` functionality, in that the well-known symbols need to be located off of the global `Symbol` object in order to ensure that the correct symbol is referenced.

Expand Down
2 changes: 1 addition & 1 deletion src/shim/Set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export interface SetConstructor {
export let Set: SetConstructor = global.Set;

if (!has('es6-set')) {
Set = class Set<T> {
Set = global.Set = class Set<T> {
private readonly _setData: T[] = [];

static [Symbol.species] = Set;
Expand Down
2 changes: 1 addition & 1 deletion src/shim/WeakMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ if (!has('es6-weakmap')) {
};
})();

WeakMap = class WeakMap<K, V> {
WeakMap = global.WeakMap = class WeakMap<K, V> {
private readonly _name: string;
private readonly _frozenEntries: Entry<K, V>[];

Expand Down
202 changes: 88 additions & 114 deletions src/shim/array.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import global from './global';
import { isArrayLike, isIterable, Iterable } from './iterator';
import { MAX_SAFE_INTEGER } from './number';
import has from '../core/has';
Expand All @@ -25,11 +24,6 @@ export interface FindCallback<T> {
(element: T, index: number, array: ArrayLike<T>): boolean;
}

interface WritableArrayLike<T> {
readonly length: number;
[n: number]: T;
}

/* ES6 Array static methods */

export interface From {
Expand Down Expand Up @@ -119,66 +113,57 @@ export let findIndex: <T>(target: ArrayLike<T>, callback: FindCallback<T>, thisA
*/
export let includes: <T>(target: ArrayLike<T>, searchElement: T, fromIndex?: number) => boolean;

if (has('es6-array') && has('es6-array-fill')) {
from = global.Array.from;
of = global.Array.of;
copyWithin = wrapNative(global.Array.prototype.copyWithin);
fill = wrapNative(global.Array.prototype.fill);
find = wrapNative(global.Array.prototype.find);
findIndex = wrapNative(global.Array.prototype.findIndex);
} else {
// It is only older versions of Safari/iOS that have a bad fill implementation and so aren't in the wild
// To make things easier, if there is a bad fill implementation, the whole set of functions will be filled

/**
* Ensures a non-negative, non-infinite, safe integer.
*
* @param length The number to validate
* @return A proper length
*/
const toLength = function toLength(length: number): number {
if (isNaN(length)) {
return 0;
}

length = Number(length);
if (isFinite(length)) {
length = Math.floor(length);
}
// Ensure a non-negative, real, safe integer
return Math.min(Math.max(length, 0), MAX_SAFE_INTEGER);
};

/**
* From ES6 7.1.4 ToInteger()
*
* @param value A value to convert
* @return An integer
*/
const toInteger = function toInteger(value: any): number {
value = Number(value);
if (isNaN(value)) {
return 0;
}
if (value === 0 || !isFinite(value)) {
return value;
}
// Util functions for filled implementations
/**
* Ensures a non-negative, non-infinite, safe integer.
*
* @param length The number to validate
* @return A proper length
*/
const toLength = function toLength(length: number): number {
if (isNaN(length)) {
return 0;
}

length = Number(length);
if (isFinite(length)) {
length = Math.floor(length);
}
// Ensure a non-negative, real, safe integer
return Math.min(Math.max(length, 0), MAX_SAFE_INTEGER);
};

return (value > 0 ? 1 : -1) * Math.floor(Math.abs(value));
};
/**
* From ES6 7.1.4 ToInteger()
*
* @param value A value to convert
* @return An integer
*/
const toInteger = function toInteger(value: any): number {
value = Number(value);
if (isNaN(value)) {
return 0;
}
if (value === 0 || !isFinite(value)) {
return value;
}

return (value > 0 ? 1 : -1) * Math.floor(Math.abs(value));
};

/**
* Normalizes an offset against a given length, wrapping it if negative.
*
* @param value The original offset
* @param length The total length to normalize against
* @return If negative, provide a distance from the end (length); otherwise provide a distance from 0
*/
const normalizeOffset = function normalizeOffset(value: number, length: number): number {
return value < 0 ? Math.max(length + value, 0) : Math.min(value, length);
};
/**
* Normalizes an offset against a given length, wrapping it if negative.
*
* @param value The original offset
* @param length The total length to normalize against
* @return If negative, provide a distance from the end (length); otherwise provide a distance from 0
*/
const normalizeOffset = function normalizeOffset(value: number, length: number): number {
return value < 0 ? Math.max(length + value, 0) : Math.min(value, length);
};

from = function from(
if (!has('es6-array')) {
Array.from = function from(
this: ArrayConstructor,
arrayLike: Iterable<any> | ArrayLike<any>,
mapFunction?: MapCallback<any, any>,
Expand Down Expand Up @@ -229,21 +214,16 @@ if (has('es6-array') && has('es6-array-fill')) {
return array;
};

of = function of<T>(...items: T[]): Array<T> {
Array.of = function of<T>(...items: T[]): Array<T> {
return Array.prototype.slice.call(items);
};

copyWithin = function copyWithin<T>(
target: ArrayLike<T>,
offset: number,
start: number,
end?: number
): ArrayLike<T> {
if (target == null) {
Array.prototype.copyWithin = function copyWithin(offset: number, start: number, end?: number) {
if (this == null) {
throw new TypeError('copyWithin: target must be an array-like object');
}

const length = toLength(target.length);
const length = toLength(this.length);
offset = normalizeOffset(toInteger(offset), length);
start = normalizeOffset(toInteger(start), length);
end = normalizeOffset(end === undefined ? length : toInteger(end), length);
Expand All @@ -257,39 +237,29 @@ if (has('es6-array') && has('es6-array-fill')) {
}

while (count > 0) {
if (start in target) {
(target as WritableArrayLike<T>)[offset] = target[start];
if (start in this) {
this[offset] = this[start];
} else {
delete (target as WritableArrayLike<T>)[offset];
delete this[offset];
}

offset += direction;
start += direction;
count--;
}

return target;
return this;
};

fill = function fill<T>(target: ArrayLike<T>, value: any, start?: number, end?: number): ArrayLike<T> {
const length = toLength(target.length);
let i = normalizeOffset(toInteger(start), length);
end = normalizeOffset(end === undefined ? length : toInteger(end), length);

while (i < end) {
(target as WritableArrayLike<T>)[i++] = value;
}

return target;
};
type Predicate = (this: void, value: any, index: number, obj: any[]) => boolean;

find = function find<T>(target: ArrayLike<T>, callback: FindCallback<T>, thisArg?: {}): T | undefined {
const index = findIndex<T>(target, callback, thisArg);
return index !== -1 ? target[index] : undefined;
Array.prototype.find = function find(callback: Predicate, thisArg?: {}) {
const index = this.findIndex(callback, thisArg);
return index !== -1 ? this[index] : undefined;
};

findIndex = function findIndex<T>(target: ArrayLike<T>, callback: FindCallback<T>, thisArg?: {}): number {
const length = toLength(target.length);
Array.prototype.findIndex = function findIndex(callback: Predicate, thisArg?: {}): number {
const length = toLength(this.length);

if (!callback) {
throw new TypeError('find: second argument must be a function');
Expand All @@ -300,7 +270,7 @@ if (has('es6-array') && has('es6-array-fill')) {
}

for (let i = 0; i < length; i++) {
if (callback(target[i], i, target)) {
if (callback(this[i], i, this)) {
return i;
}
}
Expand All @@ -309,32 +279,26 @@ if (has('es6-array') && has('es6-array-fill')) {
};
}

if (has('es7-array')) {
includes = wrapNative(global.Array.prototype.includes);
} else {
/**
* Ensures a non-negative, non-infinite, safe integer.
*
* @param length The number to validate
* @return A proper length
*/
const toLength = function toLength(length: number): number {
length = Number(length);
if (isNaN(length)) {
return 0;
}
if (isFinite(length)) {
length = Math.floor(length);
if (!has('es-array-fill')) {
Array.prototype.fill = function fill(value: any, start?: number, end?: number) {
const length = toLength(this.length);
let i = normalizeOffset(toInteger(start), length);
end = normalizeOffset(end === undefined ? length : toInteger(end), length);

while (i < end) {
this[i++] = value;
}
// Ensure a non-negative, real, safe integer
return Math.min(Math.max(length, 0), MAX_SAFE_INTEGER);

return this;
};
}

includes = function includes<T>(target: ArrayLike<T>, searchElement: T, fromIndex: number = 0): boolean {
let len = toLength(target.length);
if (!has('es7-array')) {
Array.prototype.includes = function includes(searchElement, fromIndex = 0) {
let len = toLength(this.length);

for (let i = fromIndex; i < len; ++i) {
const currentElement = target[i];
const currentElement = this[i];
if (
searchElement === currentElement ||
(searchElement !== searchElement && currentElement !== currentElement)
Expand All @@ -346,3 +310,13 @@ if (has('es7-array')) {
return false;
};
}

from = Array.from;
of = Array.of;
copyWithin = wrapNative(Array.prototype.copyWithin);
fill = wrapNative(Array.prototype.fill);
find = wrapNative(Array.prototype.find);
findIndex = wrapNative(Array.prototype.findIndex);
includes = wrapNative(Array.prototype.includes);

export default Array;
Loading