UNPKG

5.63 kBJavaScriptView Raw
1const realDefineProp = (function () {
2 let sentinel = {};
3 try {
4 Object.defineProperty(sentinel, 'a', {});
5 return 'a' in sentinel;
6 } catch (e) {
7 return false;
8 }
9 })();
10
11// Need a workaround for getters in ES3
12export const es3 = !realDefineProp && !Object.prototype.__defineGetter__;
13
14// We use this a lot (and need it for proto-less objects)
15export const hop = Object.prototype.hasOwnProperty;
16
17// Naive defineProperty for compatibility
18export const defineProperty = realDefineProp ? Object.defineProperty : function (obj, name, desc) {
19 if ('get' in desc && obj.__defineGetter__)
20 obj.__defineGetter__(name, desc.get);
21
22 else if (!hop.call(obj, name) || 'value' in desc)
23 obj[name] = desc.value;
24};
25
26// Array.prototype.indexOf, as good as we need it to be
27export const arrIndexOf = Array.prototype.indexOf || function (search) {
28 /*jshint validthis:true */
29 let t = this;
30 if (!t.length)
31 return -1;
32
33 for (let i = arguments[1] || 0, max = t.length; i < max; i++) {
34 if (t[i] === search)
35 return i;
36 }
37
38 return -1;
39};
40
41// Create an object with the specified prototype (2nd arg required for Record)
42export const objCreate = Object.create || function (proto, props) {
43 let obj;
44
45 function F() {}
46 F.prototype = proto;
47 obj = new F();
48
49 for (let k in props) {
50 if (hop.call(props, k))
51 defineProperty(obj, k, props[k]);
52 }
53
54 return obj;
55};
56
57// Snapshot some (hopefully still) native built-ins
58export const arrSlice = Array.prototype.slice;
59export const arrConcat = Array.prototype.concat;
60export const arrPush = Array.prototype.push;
61export const arrJoin = Array.prototype.join;
62export const arrShift = Array.prototype.shift;
63
64// Naive Function.prototype.bind for compatibility
65export const fnBind = Function.prototype.bind || function (thisObj) {
66 let fn = this,
67 args = arrSlice.call(arguments, 1);
68
69 // All our (presently) bound functions have either 1 or 0 arguments. By returning
70 // different function signatures, we can pass some tests in ES3 environments
71 if (fn.length === 1) {
72 return function () {
73 return fn.apply(thisObj, arrConcat.call(args, arrSlice.call(arguments)));
74 };
75 }
76 return function () {
77 return fn.apply(thisObj, arrConcat.call(args, arrSlice.call(arguments)));
78 };
79};
80
81// Object housing internal properties for constructors
82export const internals = objCreate(null);
83
84// Keep internal properties internal
85export const secret = Math.random();
86
87// Helper functions
88// ================
89
90/**
91 * A function to deal with the inaccuracy of calculating log10 in pre-ES6
92 * JavaScript environments. Math.log(num) / Math.LN10 was responsible for
93 * causing issue #62.
94 */
95export function log10Floor (n) {
96 // ES6 provides the more accurate Math.log10
97 if (typeof Math.log10 === 'function')
98 return Math.floor(Math.log10(n));
99
100 let x = Math.round(Math.log(n) * Math.LOG10E);
101 return x - (Number('1e' + x) > n);
102}
103
104/**
105 * A map that doesn't contain Object in its prototype chain
106 */
107export function Record (obj) {
108 // Copy only own properties over unless this object is already a Record instance
109 for (let k in obj) {
110 if (obj instanceof Record || hop.call(obj, k))
111 defineProperty(this, k, { value: obj[k], enumerable: true, writable: true, configurable: true });
112 }
113}
114Record.prototype = objCreate(null);
115
116/**
117 * An ordered list
118 */
119export function List() {
120 defineProperty(this, 'length', { writable:true, value: 0 });
121
122 if (arguments.length)
123 arrPush.apply(this, arrSlice.call(arguments));
124}
125List.prototype = objCreate(null);
126
127/**
128 * Constructs a regular expression to restore tainted RegExp properties
129 */
130export function createRegExpRestore () {
131 let esc = /[.?*+^$[\]\\(){}|-]/g,
132 lm = RegExp.lastMatch || '',
133 ml = RegExp.multiline ? 'm' : '',
134 ret = { input: RegExp.input },
135 reg = new List(),
136 has = false,
137 cap = {};
138
139 // Create a snapshot of all the 'captured' properties
140 for (let i = 1; i <= 9; i++)
141 has = (cap['$'+i] = RegExp['$'+i]) || has;
142
143 // Now we've snapshotted some properties, escape the lastMatch string
144 lm = lm.replace(esc, '\\$&');
145
146 // If any of the captured strings were non-empty, iterate over them all
147 if (has) {
148 for (let i = 1; i <= 9; i++) {
149 let m = cap['$'+i];
150
151 // If it's empty, add an empty capturing group
152 if (!m)
153 lm = '()' + lm;
154
155 // Else find the string in lm and escape & wrap it to capture it
156 else {
157 m = m.replace(esc, '\\$&');
158 lm = lm.replace(m, '(' + m + ')');
159 }
160
161 // Push it to the reg and chop lm to make sure further groups come after
162 arrPush.call(reg, lm.slice(0, lm.indexOf('(') + 1));
163 lm = lm.slice(lm.indexOf('(') + 1);
164 }
165 }
166
167 // Create the regular expression that will reconstruct the RegExp properties
168 ret.exp = new RegExp(arrJoin.call(reg, '') + lm, ml);
169
170 return ret;
171}
172
173/**
174 * Mimics ES5's abstract ToObject() function
175 */
176export function toObject (arg) {
177 if (arg === null)
178 throw new TypeError('Cannot convert null or undefined to object');
179
180 return Object(arg);
181}
182
183/**
184 * Returns "internal" properties for an object
185 */
186export function getInternalProperties (obj) {
187 if (hop.call(obj, '__getInternalProperties'))
188 return obj.__getInternalProperties(secret);
189
190 return objCreate(null);
191}