Commit 199c8068cdba50d9a6871e20fbc74e513514ecc8
1 parent
cb9bedfcdf
Exists in
master
included bind shim
Showing 4 changed files with 1554 additions and 1 deletions Side-by-side Diff
Makefile
View file @
199c806
es5-shim.js
View file @
199c806
Diff suppressed. Click to show
1 | +/*! | |
2 | + * https://github.com/es-shims/es5-shim | |
3 | + * @license es5-shim Copyright 2009-2015 by contributors, MIT License | |
4 | + * see https://github.com/es-shims/es5-shim/blob/master/LICENSE | |
5 | + */ | |
6 | + | |
7 | +// vim: ts=4 sts=4 sw=4 expandtab | |
8 | + | |
9 | +// Add semicolon to prevent IIFE from being passed as argument to concatenated code. | |
10 | +; | |
11 | + | |
12 | +// UMD (Universal Module Definition) | |
13 | +// see https://github.com/umdjs/umd/blob/master/returnExports.js | |
14 | +(function (root, factory) { | |
15 | + 'use strict'; | |
16 | + | |
17 | + /*global define, exports, module */ | |
18 | + if (typeof define === 'function' && define.amd) { | |
19 | + // AMD. Register as an anonymous module. | |
20 | + define(factory); | |
21 | + } else if (typeof exports === 'object') { | |
22 | + // Node. Does not work with strict CommonJS, but | |
23 | + // only CommonJS-like enviroments that support module.exports, | |
24 | + // like Node. | |
25 | + module.exports = factory(); | |
26 | + } else { | |
27 | + // Browser globals (root is window) | |
28 | + root.returnExports = factory(); | |
29 | + } | |
30 | +}(this, function () { | |
31 | + | |
32 | +/** | |
33 | + * Brings an environment as close to ECMAScript 5 compliance | |
34 | + * as is possible with the facilities of erstwhile engines. | |
35 | + * | |
36 | + * Annotated ES5: http://es5.github.com/ (specific links below) | |
37 | + * ES5 Spec: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf | |
38 | + * Required reading: http://javascriptweblog.wordpress.com/2011/12/05/extending-javascript-natives/ | |
39 | + */ | |
40 | + | |
41 | +// Shortcut to an often accessed properties, in order to avoid multiple | |
42 | +// dereference that costs universally. | |
43 | +var ArrayPrototype = Array.prototype; | |
44 | +var ObjectPrototype = Object.prototype; | |
45 | +var FunctionPrototype = Function.prototype; | |
46 | +var StringPrototype = String.prototype; | |
47 | +var NumberPrototype = Number.prototype; | |
48 | +var array_slice = ArrayPrototype.slice; | |
49 | +var array_splice = ArrayPrototype.splice; | |
50 | +var array_push = ArrayPrototype.push; | |
51 | +var array_unshift = ArrayPrototype.unshift; | |
52 | +var call = FunctionPrototype.call; | |
53 | + | |
54 | +// Having a toString local variable name breaks in Opera so use to_string. | |
55 | +var to_string = ObjectPrototype.toString; | |
56 | + | |
57 | +var isArray = Array.isArray || function isArray(obj) { | |
58 | + return to_string.call(obj) === '[object Array]'; | |
59 | +}; | |
60 | + | |
61 | +var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol'; | |
62 | +var isCallable; /* inlined from https://npmjs.com/is-callable */ var fnToStr = Function.prototype.toString, tryFunctionObject = function tryFunctionObject(value) { try { fnToStr.call(value); return true; } catch (e) { return false; } }, fnClass = '[object Function]', genClass = '[object GeneratorFunction]'; isCallable = function isCallable(value) { if (typeof value !== 'function') { return false; } if (hasToStringTag) { return tryFunctionObject(value); } var strClass = to_string.call(value); return strClass === fnClass || strClass === genClass; }; | |
63 | +var isRegex; /* inlined from https://npmjs.com/is-regex */ var regexExec = RegExp.prototype.exec, tryRegexExec = function tryRegexExec(value) { try { regexExec.call(value); return true; } catch (e) { return false; } }, regexClass = '[object RegExp]'; isRegex = function isRegex(value) { if (typeof value !== 'object') { return false; } return hasToStringTag ? tryRegexExec(value) : to_string.call(value) === regexClass; }; | |
64 | +var isString; /* inlined from https://npmjs.com/is-string */ var strValue = String.prototype.valueOf, tryStringObject = function tryStringObject(value) { try { strValue.call(value); return true; } catch (e) { return false; } }, stringClass = '[object String]'; isString = function isString(value) { if (typeof value === 'string') { return true; } if (typeof value !== 'object') { return false; } return hasToStringTag ? tryStringObject(value) : to_string.call(value) === stringClass; }; | |
65 | + | |
66 | +var isArguments = function isArguments(value) { | |
67 | + var str = to_string.call(value); | |
68 | + var isArgs = str === '[object Arguments]'; | |
69 | + if (!isArgs) { | |
70 | + isArgs = !isArray(value) && | |
71 | + value !== null && | |
72 | + typeof value === 'object' && | |
73 | + typeof value.length === 'number' && | |
74 | + value.length >= 0 && | |
75 | + isCallable(value.callee); | |
76 | + } | |
77 | + return isArgs; | |
78 | +}; | |
79 | + | |
80 | +/* inlined from http://npmjs.com/define-properties */ | |
81 | +var defineProperties = (function (has) { | |
82 | + var supportsDescriptors = Object.defineProperty && (function () { | |
83 | + try { | |
84 | + Object.defineProperty({}, 'x', {}); | |
85 | + return true; | |
86 | + } catch (e) { /* this is ES3 */ | |
87 | + return false; | |
88 | + } | |
89 | + }()); | |
90 | + | |
91 | + // Define configurable, writable and non-enumerable props | |
92 | + // if they don't exist. | |
93 | + var defineProperty; | |
94 | + if (supportsDescriptors) { | |
95 | + defineProperty = function (object, name, method, forceAssign) { | |
96 | + if (!forceAssign && (name in object)) { return; } | |
97 | + Object.defineProperty(object, name, { | |
98 | + configurable: true, | |
99 | + enumerable: false, | |
100 | + writable: true, | |
101 | + value: method | |
102 | + }); | |
103 | + }; | |
104 | + } else { | |
105 | + defineProperty = function (object, name, method, forceAssign) { | |
106 | + if (!forceAssign && (name in object)) { return; } | |
107 | + object[name] = method; | |
108 | + }; | |
109 | + } | |
110 | + return function defineProperties(object, map, forceAssign) { | |
111 | + for (var name in map) { | |
112 | + if (has.call(map, name)) { | |
113 | + defineProperty(object, name, map[name], forceAssign); | |
114 | + } | |
115 | + } | |
116 | + }; | |
117 | +}(ObjectPrototype.hasOwnProperty)); | |
118 | + | |
119 | +// | |
120 | +// Util | |
121 | +// ====== | |
122 | +// | |
123 | + | |
124 | +/* replaceable with https://npmjs.com/package/es-abstract /helpers/isPrimitive */ | |
125 | +var isPrimitive = function isPrimitive(input) { | |
126 | + var type = typeof input; | |
127 | + return input === null || (type !== 'object' && type !== 'function'); | |
128 | +}; | |
129 | + | |
130 | +var ES = { | |
131 | + // ES5 9.4 | |
132 | + // http://es5.github.com/#x9.4 | |
133 | + // http://jsperf.com/to-integer | |
134 | + /* replaceable with https://npmjs.com/package/es-abstract ES5.ToInteger */ | |
135 | + ToInteger: function ToInteger(num) { | |
136 | + var n = +num; | |
137 | + if (n !== n) { // isNaN | |
138 | + n = 0; | |
139 | + } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) { | |
140 | + n = (n > 0 || -1) * Math.floor(Math.abs(n)); | |
141 | + } | |
142 | + return n; | |
143 | + }, | |
144 | + | |
145 | + /* replaceable with https://npmjs.com/package/es-abstract ES5.ToPrimitive */ | |
146 | + ToPrimitive: function ToPrimitive(input) { | |
147 | + var val, valueOf, toStr; | |
148 | + if (isPrimitive(input)) { | |
149 | + return input; | |
150 | + } | |
151 | + valueOf = input.valueOf; | |
152 | + if (isCallable(valueOf)) { | |
153 | + val = valueOf.call(input); | |
154 | + if (isPrimitive(val)) { | |
155 | + return val; | |
156 | + } | |
157 | + } | |
158 | + toStr = input.toString; | |
159 | + if (isCallable(toStr)) { | |
160 | + val = toStr.call(input); | |
161 | + if (isPrimitive(val)) { | |
162 | + return val; | |
163 | + } | |
164 | + } | |
165 | + throw new TypeError(); | |
166 | + }, | |
167 | + | |
168 | + // ES5 9.9 | |
169 | + // http://es5.github.com/#x9.9 | |
170 | + /* replaceable with https://npmjs.com/package/es-abstract ES5.ToObject */ | |
171 | + ToObject: function (o) { | |
172 | + /*jshint eqnull: true */ | |
173 | + if (o == null) { // this matches both null and undefined | |
174 | + throw new TypeError("can't convert " + o + ' to object'); | |
175 | + } | |
176 | + return Object(o); | |
177 | + }, | |
178 | + | |
179 | + /* replaceable with https://npmjs.com/package/es-abstract ES5.ToUint32 */ | |
180 | + ToUint32: function ToUint32(x) { | |
181 | + return x >>> 0; | |
182 | + } | |
183 | +}; | |
184 | + | |
185 | +// | |
186 | +// Function | |
187 | +// ======== | |
188 | +// | |
189 | + | |
190 | +// ES-5 15.3.4.5 | |
191 | +// http://es5.github.com/#x15.3.4.5 | |
192 | + | |
193 | +var Empty = function Empty() {}; | |
194 | + | |
195 | +defineProperties(FunctionPrototype, { | |
196 | + bind: function bind(that) { // .length is 1 | |
197 | + // 1. Let Target be the this value. | |
198 | + var target = this; | |
199 | + // 2. If IsCallable(Target) is false, throw a TypeError exception. | |
200 | + if (!isCallable(target)) { | |
201 | + throw new TypeError('Function.prototype.bind called on incompatible ' + target); | |
202 | + } | |
203 | + // 3. Let A be a new (possibly empty) internal list of all of the | |
204 | + // argument values provided after thisArg (arg1, arg2 etc), in order. | |
205 | + // XXX slicedArgs will stand in for "A" if used | |
206 | + var args = array_slice.call(arguments, 1); // for normal call | |
207 | + // 4. Let F be a new native ECMAScript object. | |
208 | + // 11. Set the [[Prototype]] internal property of F to the standard | |
209 | + // built-in Function prototype object as specified in 15.3.3.1. | |
210 | + // 12. Set the [[Call]] internal property of F as described in | |
211 | + // 15.3.4.5.1. | |
212 | + // 13. Set the [[Construct]] internal property of F as described in | |
213 | + // 15.3.4.5.2. | |
214 | + // 14. Set the [[HasInstance]] internal property of F as described in | |
215 | + // 15.3.4.5.3. | |
216 | + var bound; | |
217 | + var binder = function () { | |
218 | + | |
219 | + if (this instanceof bound) { | |
220 | + // 15.3.4.5.2 [[Construct]] | |
221 | + // When the [[Construct]] internal method of a function object, | |
222 | + // F that was created using the bind function is called with a | |
223 | + // list of arguments ExtraArgs, the following steps are taken: | |
224 | + // 1. Let target be the value of F's [[TargetFunction]] | |
225 | + // internal property. | |
226 | + // 2. If target has no [[Construct]] internal method, a | |
227 | + // TypeError exception is thrown. | |
228 | + // 3. Let boundArgs be the value of F's [[BoundArgs]] internal | |
229 | + // property. | |
230 | + // 4. Let args be a new list containing the same values as the | |
231 | + // list boundArgs in the same order followed by the same | |
232 | + // values as the list ExtraArgs in the same order. | |
233 | + // 5. Return the result of calling the [[Construct]] internal | |
234 | + // method of target providing args as the arguments. | |
235 | + | |
236 | + var result = target.apply( | |
237 | + this, | |
238 | + args.concat(array_slice.call(arguments)) | |
239 | + ); | |
240 | + if (Object(result) === result) { | |
241 | + return result; | |
242 | + } | |
243 | + return this; | |
244 | + | |
245 | + } else { | |
246 | + // 15.3.4.5.1 [[Call]] | |
247 | + // When the [[Call]] internal method of a function object, F, | |
248 | + // which was created using the bind function is called with a | |
249 | + // this value and a list of arguments ExtraArgs, the following | |
250 | + // steps are taken: | |
251 | + // 1. Let boundArgs be the value of F's [[BoundArgs]] internal | |
252 | + // property. | |
253 | + // 2. Let boundThis be the value of F's [[BoundThis]] internal | |
254 | + // property. | |
255 | + // 3. Let target be the value of F's [[TargetFunction]] internal | |
256 | + // property. | |
257 | + // 4. Let args be a new list containing the same values as the | |
258 | + // list boundArgs in the same order followed by the same | |
259 | + // values as the list ExtraArgs in the same order. | |
260 | + // 5. Return the result of calling the [[Call]] internal method | |
261 | + // of target providing boundThis as the this value and | |
262 | + // providing args as the arguments. | |
263 | + | |
264 | + // equiv: target.call(this, ...boundArgs, ...args) | |
265 | + return target.apply( | |
266 | + that, | |
267 | + args.concat(array_slice.call(arguments)) | |
268 | + ); | |
269 | + | |
270 | + } | |
271 | + | |
272 | + }; | |
273 | + | |
274 | + // 15. If the [[Class]] internal property of Target is "Function", then | |
275 | + // a. Let L be the length property of Target minus the length of A. | |
276 | + // b. Set the length own property of F to either 0 or L, whichever is | |
277 | + // larger. | |
278 | + // 16. Else set the length own property of F to 0. | |
279 | + | |
280 | + var boundLength = Math.max(0, target.length - args.length); | |
281 | + | |
282 | + // 17. Set the attributes of the length own property of F to the values | |
283 | + // specified in 15.3.5.1. | |
284 | + var boundArgs = []; | |
285 | + for (var i = 0; i < boundLength; i++) { | |
286 | + boundArgs.push('$' + i); | |
287 | + } | |
288 | + | |
289 | + // XXX Build a dynamic function with desired amount of arguments is the only | |
290 | + // way to set the length property of a function. | |
291 | + // In environments where Content Security Policies enabled (Chrome extensions, | |
292 | + // for ex.) all use of eval or Function costructor throws an exception. | |
293 | + // However in all of these environments Function.prototype.bind exists | |
294 | + // and so this code will never be executed. | |
295 | + bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder); | |
296 | + | |
297 | + if (target.prototype) { | |
298 | + Empty.prototype = target.prototype; | |
299 | + bound.prototype = new Empty(); | |
300 | + // Clean up dangling references. | |
301 | + Empty.prototype = null; | |
302 | + } | |
303 | + | |
304 | + // TODO | |
305 | + // 18. Set the [[Extensible]] internal property of F to true. | |
306 | + | |
307 | + // TODO | |
308 | + // 19. Let thrower be the [[ThrowTypeError]] function Object (13.2.3). | |
309 | + // 20. Call the [[DefineOwnProperty]] internal method of F with | |
310 | + // arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]: | |
311 | + // thrower, [[Enumerable]]: false, [[Configurable]]: false}, and | |
312 | + // false. | |
313 | + // 21. Call the [[DefineOwnProperty]] internal method of F with | |
314 | + // arguments "arguments", PropertyDescriptor {[[Get]]: thrower, | |
315 | + // [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: false}, | |
316 | + // and false. | |
317 | + | |
318 | + // TODO | |
319 | + // NOTE Function objects created using Function.prototype.bind do not | |
320 | + // have a prototype property or the [[Code]], [[FormalParameters]], and | |
321 | + // [[Scope]] internal properties. | |
322 | + // XXX can't delete prototype in pure-js. | |
323 | + | |
324 | + // 22. Return F. | |
325 | + return bound; | |
326 | + } | |
327 | +}); | |
328 | + | |
329 | +// _Please note: Shortcuts are defined after `Function.prototype.bind` as we | |
330 | +// us it in defining shortcuts. | |
331 | +var owns = call.bind(ObjectPrototype.hasOwnProperty); | |
332 | + | |
333 | +// | |
334 | +// Array | |
335 | +// ===== | |
336 | +// | |
337 | + | |
338 | +// ES5 15.4.4.12 | |
339 | +// http://es5.github.com/#x15.4.4.12 | |
340 | +var spliceNoopReturnsEmptyArray = (function () { | |
341 | + var a = [1, 2]; | |
342 | + var result = a.splice(); | |
343 | + return a.length === 2 && isArray(result) && result.length === 0; | |
344 | +}()); | |
345 | +defineProperties(ArrayPrototype, { | |
346 | + // Safari 5.0 bug where .splice() returns undefined | |
347 | + splice: function splice(start, deleteCount) { | |
348 | + if (arguments.length === 0) { | |
349 | + return []; | |
350 | + } else { | |
351 | + return array_splice.apply(this, arguments); | |
352 | + } | |
353 | + } | |
354 | +}, !spliceNoopReturnsEmptyArray); | |
355 | + | |
356 | +var spliceWorksWithEmptyObject = (function () { | |
357 | + var obj = {}; | |
358 | + ArrayPrototype.splice.call(obj, 0, 0, 1); | |
359 | + return obj.length === 1; | |
360 | +}()); | |
361 | +defineProperties(ArrayPrototype, { | |
362 | + splice: function splice(start, deleteCount) { | |
363 | + if (arguments.length === 0) { return []; } | |
364 | + var args = arguments; | |
365 | + this.length = Math.max(ES.ToInteger(this.length), 0); | |
366 | + if (arguments.length > 0 && typeof deleteCount !== 'number') { | |
367 | + args = array_slice.call(arguments); | |
368 | + if (args.length < 2) { | |
369 | + args.push(this.length - start); | |
370 | + } else { | |
371 | + args[1] = ES.ToInteger(deleteCount); | |
372 | + } | |
373 | + } | |
374 | + return array_splice.apply(this, args); | |
375 | + } | |
376 | +}, !spliceWorksWithEmptyObject); | |
377 | + | |
378 | +// ES5 15.4.4.12 | |
379 | +// http://es5.github.com/#x15.4.4.13 | |
380 | +// Return len+argCount. | |
381 | +// [bugfix, ielt8] | |
382 | +// IE < 8 bug: [].unshift(0) === undefined but should be "1" | |
383 | +var hasUnshiftReturnValueBug = [].unshift(0) !== 1; | |
384 | +defineProperties(ArrayPrototype, { | |
385 | + unshift: function () { | |
386 | + array_unshift.apply(this, arguments); | |
387 | + return this.length; | |
388 | + } | |
389 | +}, hasUnshiftReturnValueBug); | |
390 | + | |
391 | +// ES5 15.4.3.2 | |
392 | +// http://es5.github.com/#x15.4.3.2 | |
393 | +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray | |
394 | +defineProperties(Array, { isArray: isArray }); | |
395 | + | |
396 | +// The IsCallable() check in the Array functions | |
397 | +// has been replaced with a strict check on the | |
398 | +// internal class of the object to trap cases where | |
399 | +// the provided function was actually a regular | |
400 | +// expression literal, which in V8 and | |
401 | +// JavaScriptCore is a typeof "function". Only in | |
402 | +// V8 are regular expression literals permitted as | |
403 | +// reduce parameters, so it is desirable in the | |
404 | +// general case for the shim to match the more | |
405 | +// strict and common behavior of rejecting regular | |
406 | +// expressions. | |
407 | + | |
408 | +// ES5 15.4.4.18 | |
409 | +// http://es5.github.com/#x15.4.4.18 | |
410 | +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/forEach | |
411 | + | |
412 | +// Check failure of by-index access of string characters (IE < 9) | |
413 | +// and failure of `0 in boxedString` (Rhino) | |
414 | +var boxedString = Object('a'); | |
415 | +var splitString = boxedString[0] !== 'a' || !(0 in boxedString); | |
416 | + | |
417 | +var properlyBoxesContext = function properlyBoxed(method) { | |
418 | + // Check node 0.6.21 bug where third parameter is not boxed | |
419 | + var properlyBoxesNonStrict = true; | |
420 | + var properlyBoxesStrict = true; | |
421 | + if (method) { | |
422 | + method.call('foo', function (_, __, context) { | |
423 | + if (typeof context !== 'object') { properlyBoxesNonStrict = false; } | |
424 | + }); | |
425 | + | |
426 | + method.call([1], function () { | |
427 | + 'use strict'; | |
428 | + | |
429 | + properlyBoxesStrict = typeof this === 'string'; | |
430 | + }, 'x'); | |
431 | + } | |
432 | + return !!method && properlyBoxesNonStrict && properlyBoxesStrict; | |
433 | +}; | |
434 | + | |
435 | +defineProperties(ArrayPrototype, { | |
436 | + forEach: function forEach(callbackfn /*, thisArg*/) { | |
437 | + var object = ES.ToObject(this); | |
438 | + var self = splitString && isString(this) ? this.split('') : object; | |
439 | + var i = -1; | |
440 | + var length = self.length >>> 0; | |
441 | + var T; | |
442 | + if (arguments.length > 1) { | |
443 | + T = arguments[1]; | |
444 | + } | |
445 | + | |
446 | + // If no callback function or if callback is not a callable function | |
447 | + if (!isCallable(callbackfn)) { | |
448 | + throw new TypeError('Array.prototype.forEach callback must be a function'); | |
449 | + } | |
450 | + | |
451 | + while (++i < length) { | |
452 | + if (i in self) { | |
453 | + // Invoke the callback function with call, passing arguments: | |
454 | + // context, property value, property key, thisArg object | |
455 | + if (typeof T !== 'undefined') { | |
456 | + callbackfn.call(T, self[i], i, object); | |
457 | + } else { | |
458 | + callbackfn(self[i], i, object); | |
459 | + } | |
460 | + } | |
461 | + } | |
462 | + } | |
463 | +}, !properlyBoxesContext(ArrayPrototype.forEach)); | |
464 | + | |
465 | +// ES5 15.4.4.19 | |
466 | +// http://es5.github.com/#x15.4.4.19 | |
467 | +// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map | |
468 | +defineProperties(ArrayPrototype, { | |
469 | + map: function map(callbackfn/*, thisArg*/) { | |
470 | + var object = ES.ToObject(this); | |
471 | + var self = splitString && isString(this) ? this.split('') : object; | |
472 | + var length = self.length >>> 0; | |
473 | + var result = Array(length); | |
474 | + var T; | |
475 | + if (arguments.length > 1) { | |
476 | + T = arguments[1]; | |
477 | + } | |
478 | + | |
479 | + // If no callback function or if callback is not a callable function | |
480 | + if (!isCallable(callbackfn)) { | |
481 | + throw new TypeError('Array.prototype.map callback must be a function'); | |
482 | + } | |
483 | + | |
484 | + for (var i = 0; i < length; i++) { | |
485 | + if (i in self) { | |
486 | + if (typeof T !== 'undefined') { | |
487 | + result[i] = callbackfn.call(T, self[i], i, object); | |
488 | + } else { | |
489 | + result[i] = callbackfn(self[i], i, object); | |
490 | + } | |
491 | + } | |
492 | + } | |
493 | + return result; | |
494 | + } | |
495 | +}, !properlyBoxesContext(ArrayPrototype.map)); | |
496 | + | |
497 | +// ES5 15.4.4.20 | |
498 | +// http://es5.github.com/#x15.4.4.20 | |
499 | +// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter | |
500 | +defineProperties(ArrayPrototype, { | |
501 | + filter: function filter(callbackfn /*, thisArg*/) { | |
502 | + var object = ES.ToObject(this); | |
503 | + var self = splitString && isString(this) ? this.split('') : object; | |
504 | + var length = self.length >>> 0; | |
505 | + var result = []; | |
506 | + var value; | |
507 | + var T; | |
508 | + if (arguments.length > 1) { | |
509 | + T = arguments[1]; | |
510 | + } | |
511 | + | |
512 | + // If no callback function or if callback is not a callable function | |
513 | + if (!isCallable(callbackfn)) { | |
514 | + throw new TypeError('Array.prototype.filter callback must be a function'); | |
515 | + } | |
516 | + | |
517 | + for (var i = 0; i < length; i++) { | |
518 | + if (i in self) { | |
519 | + value = self[i]; | |
520 | + if (typeof T === 'undefined' ? callbackfn(value, i, object) : callbackfn.call(T, value, i, object)) { | |
521 | + result.push(value); | |
522 | + } | |
523 | + } | |
524 | + } | |
525 | + return result; | |
526 | + } | |
527 | +}, !properlyBoxesContext(ArrayPrototype.filter)); | |
528 | + | |
529 | +// ES5 15.4.4.16 | |
530 | +// http://es5.github.com/#x15.4.4.16 | |
531 | +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every | |
532 | +defineProperties(ArrayPrototype, { | |
533 | + every: function every(callbackfn /*, thisArg*/) { | |
534 | + var object = ES.ToObject(this); | |
535 | + var self = splitString && isString(this) ? this.split('') : object; | |
536 | + var length = self.length >>> 0; | |
537 | + var T; | |
538 | + if (arguments.length > 1) { | |
539 | + T = arguments[1]; | |
540 | + } | |
541 | + | |
542 | + // If no callback function or if callback is not a callable function | |
543 | + if (!isCallable(callbackfn)) { | |
544 | + throw new TypeError('Array.prototype.every callback must be a function'); | |
545 | + } | |
546 | + | |
547 | + for (var i = 0; i < length; i++) { | |
548 | + if (i in self && !(typeof T === 'undefined' ? callbackfn(self[i], i, object) : callbackfn.call(T, self[i], i, object))) { | |
549 | + return false; | |
550 | + } | |
551 | + } | |
552 | + return true; | |
553 | + } | |
554 | +}, !properlyBoxesContext(ArrayPrototype.every)); | |
555 | + | |
556 | +// ES5 15.4.4.17 | |
557 | +// http://es5.github.com/#x15.4.4.17 | |
558 | +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some | |
559 | +defineProperties(ArrayPrototype, { | |
560 | + some: function some(callbackfn/*, thisArg */) { | |
561 | + var object = ES.ToObject(this); | |
562 | + var self = splitString && isString(this) ? this.split('') : object; | |
563 | + var length = self.length >>> 0; | |
564 | + var T; | |
565 | + if (arguments.length > 1) { | |
566 | + T = arguments[1]; | |
567 | + } | |
568 | + | |
569 | + // If no callback function or if callback is not a callable function | |
570 | + if (!isCallable(callbackfn)) { | |
571 | + throw new TypeError('Array.prototype.some callback must be a function'); | |
572 | + } | |
573 | + | |
574 | + for (var i = 0; i < length; i++) { | |
575 | + if (i in self && (typeof T === 'undefined' ? callbackfn(self[i], i, object) : callbackfn.call(T, self[i], i, object))) { | |
576 | + return true; | |
577 | + } | |
578 | + } | |
579 | + return false; | |
580 | + } | |
581 | +}, !properlyBoxesContext(ArrayPrototype.some)); | |
582 | + | |
583 | +// ES5 15.4.4.21 | |
584 | +// http://es5.github.com/#x15.4.4.21 | |
585 | +// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce | |
586 | +var reduceCoercesToObject = false; | |
587 | +if (ArrayPrototype.reduce) { | |
588 | + reduceCoercesToObject = typeof ArrayPrototype.reduce.call('es5', function (_, __, ___, list) { return list; }) === 'object'; | |
589 | +} | |
590 | +defineProperties(ArrayPrototype, { | |
591 | + reduce: function reduce(callbackfn /*, initialValue*/) { | |
592 | + var object = ES.ToObject(this); | |
593 | + var self = splitString && isString(this) ? this.split('') : object; | |
594 | + var length = self.length >>> 0; | |
595 | + | |
596 | + // If no callback function or if callback is not a callable function | |
597 | + if (!isCallable(callbackfn)) { | |
598 | + throw new TypeError('Array.prototype.reduce callback must be a function'); | |
599 | + } | |
600 | + | |
601 | + // no value to return if no initial value and an empty array | |
602 | + if (length === 0 && arguments.length === 1) { | |
603 | + throw new TypeError('reduce of empty array with no initial value'); | |
604 | + } | |
605 | + | |
606 | + var i = 0; | |
607 | + var result; | |
608 | + if (arguments.length >= 2) { | |
609 | + result = arguments[1]; | |
610 | + } else { | |
611 | + do { | |
612 | + if (i in self) { | |
613 | + result = self[i++]; | |
614 | + break; | |
615 | + } | |
616 | + | |
617 | + // if array contains no values, no initial value to return | |
618 | + if (++i >= length) { | |
619 | + throw new TypeError('reduce of empty array with no initial value'); | |
620 | + } | |
621 | + } while (true); | |
622 | + } | |
623 | + | |
624 | + for (; i < length; i++) { | |
625 | + if (i in self) { | |
626 | + result = callbackfn(result, self[i], i, object); | |
627 | + } | |
628 | + } | |
629 | + | |
630 | + return result; | |
631 | + } | |
632 | +}, !reduceCoercesToObject); | |
633 | + | |
634 | +// ES5 15.4.4.22 | |
635 | +// http://es5.github.com/#x15.4.4.22 | |
636 | +// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight | |
637 | +var reduceRightCoercesToObject = false; | |
638 | +if (ArrayPrototype.reduceRight) { | |
639 | + reduceRightCoercesToObject = typeof ArrayPrototype.reduceRight.call('es5', function (_, __, ___, list) { return list; }) === 'object'; | |
640 | +} | |
641 | +defineProperties(ArrayPrototype, { | |
642 | + reduceRight: function reduceRight(callbackfn/*, initial*/) { | |
643 | + var object = ES.ToObject(this); | |
644 | + var self = splitString && isString(this) ? this.split('') : object; | |
645 | + var length = self.length >>> 0; | |
646 | + | |
647 | + // If no callback function or if callback is not a callable function | |
648 | + if (!isCallable(callbackfn)) { | |
649 | + throw new TypeError('Array.prototype.reduceRight callback must be a function'); | |
650 | + } | |
651 | + | |
652 | + // no value to return if no initial value, empty array | |
653 | + if (length === 0 && arguments.length === 1) { | |
654 | + throw new TypeError('reduceRight of empty array with no initial value'); | |
655 | + } | |
656 | + | |
657 | + var result; | |
658 | + var i = length - 1; | |
659 | + if (arguments.length >= 2) { | |
660 | + result = arguments[1]; | |
661 | + } else { | |
662 | + do { | |
663 | + if (i in self) { | |
664 | + result = self[i--]; | |
665 | + break; | |
666 | + } | |
667 | + | |
668 | + // if array contains no values, no initial value to return | |
669 | + if (--i < 0) { | |
670 | + throw new TypeError('reduceRight of empty array with no initial value'); | |
671 | + } | |
672 | + } while (true); | |
673 | + } | |
674 | + | |
675 | + if (i < 0) { | |
676 | + return result; | |
677 | + } | |
678 | + | |
679 | + do { | |
680 | + if (i in self) { | |
681 | + result = callbackfn(result, self[i], i, object); | |
682 | + } | |
683 | + } while (i--); | |
684 | + | |
685 | + return result; | |
686 | + } | |
687 | +}, !reduceRightCoercesToObject); | |
688 | + | |
689 | +// ES5 15.4.4.14 | |
690 | +// http://es5.github.com/#x15.4.4.14 | |
691 | +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf | |
692 | +var hasFirefox2IndexOfBug = Array.prototype.indexOf && [0, 1].indexOf(1, 2) !== -1; | |
693 | +defineProperties(ArrayPrototype, { | |
694 | + indexOf: function indexOf(searchElement /*, fromIndex */) { | |
695 | + var self = splitString && isString(this) ? this.split('') : ES.ToObject(this); | |
696 | + var length = self.length >>> 0; | |
697 | + | |
698 | + if (length === 0) { | |
699 | + return -1; | |
700 | + } | |
701 | + | |
702 | + var i = 0; | |
703 | + if (arguments.length > 1) { | |
704 | + i = ES.ToInteger(arguments[1]); | |
705 | + } | |
706 | + | |
707 | + // handle negative indices | |
708 | + i = i >= 0 ? i : Math.max(0, length + i); | |
709 | + for (; i < length; i++) { | |
710 | + if (i in self && self[i] === searchElement) { | |
711 | + return i; | |
712 | + } | |
713 | + } | |
714 | + return -1; | |
715 | + } | |
716 | +}, hasFirefox2IndexOfBug); | |
717 | + | |
718 | +// ES5 15.4.4.15 | |
719 | +// http://es5.github.com/#x15.4.4.15 | |
720 | +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf | |
721 | +var hasFirefox2LastIndexOfBug = Array.prototype.lastIndexOf && [0, 1].lastIndexOf(0, -3) !== -1; | |
722 | +defineProperties(ArrayPrototype, { | |
723 | + lastIndexOf: function lastIndexOf(searchElement /*, fromIndex */) { | |
724 | + var self = splitString && isString(this) ? this.split('') : ES.ToObject(this); | |
725 | + var length = self.length >>> 0; | |
726 | + | |
727 | + if (length === 0) { | |
728 | + return -1; | |
729 | + } | |
730 | + var i = length - 1; | |
731 | + if (arguments.length > 1) { | |
732 | + i = Math.min(i, ES.ToInteger(arguments[1])); | |
733 | + } | |
734 | + // handle negative indices | |
735 | + i = i >= 0 ? i : length - Math.abs(i); | |
736 | + for (; i >= 0; i--) { | |
737 | + if (i in self && searchElement === self[i]) { | |
738 | + return i; | |
739 | + } | |
740 | + } | |
741 | + return -1; | |
742 | + } | |
743 | +}, hasFirefox2LastIndexOfBug); | |
744 | + | |
745 | +// | |
746 | +// Object | |
747 | +// ====== | |
748 | +// | |
749 | + | |
750 | +// ES5 15.2.3.14 | |
751 | +// http://es5.github.com/#x15.2.3.14 | |
752 | + | |
753 | +// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation | |
754 | +var hasDontEnumBug = !({'toString': null}).propertyIsEnumerable('toString'), | |
755 | + hasProtoEnumBug = function () {}.propertyIsEnumerable('prototype'), | |
756 | + hasStringEnumBug = !owns('x', '0'), | |
757 | + dontEnums = [ | |
758 | + 'toString', | |
759 | + 'toLocaleString', | |
760 | + 'valueOf', | |
761 | + 'hasOwnProperty', | |
762 | + 'isPrototypeOf', | |
763 | + 'propertyIsEnumerable', | |
764 | + 'constructor' | |
765 | + ], | |
766 | + dontEnumsLength = dontEnums.length; | |
767 | + | |
768 | +defineProperties(Object, { | |
769 | + keys: function keys(object) { | |
770 | + var isFn = isCallable(object), | |
771 | + isArgs = isArguments(object), | |
772 | + isObject = object !== null && typeof object === 'object', | |
773 | + isStr = isObject && isString(object); | |
774 | + | |
775 | + if (!isObject && !isFn && !isArgs) { | |
776 | + throw new TypeError('Object.keys called on a non-object'); | |
777 | + } | |
778 | + | |
779 | + var theKeys = []; | |
780 | + var skipProto = hasProtoEnumBug && isFn; | |
781 | + if ((isStr && hasStringEnumBug) || isArgs) { | |
782 | + for (var i = 0; i < object.length; ++i) { | |
783 | + theKeys.push(String(i)); | |
784 | + } | |
785 | + } | |
786 | + | |
787 | + if (!isArgs) { | |
788 | + for (var name in object) { | |
789 | + if (!(skipProto && name === 'prototype') && owns(object, name)) { | |
790 | + theKeys.push(String(name)); | |
791 | + } | |
792 | + } | |
793 | + } | |
794 | + | |
795 | + if (hasDontEnumBug) { | |
796 | + var ctor = object.constructor, | |
797 | + skipConstructor = ctor && ctor.prototype === object; | |
798 | + for (var j = 0; j < dontEnumsLength; j++) { | |
799 | + var dontEnum = dontEnums[j]; | |
800 | + if (!(skipConstructor && dontEnum === 'constructor') && owns(object, dontEnum)) { | |
801 | + theKeys.push(dontEnum); | |
802 | + } | |
803 | + } | |
804 | + } | |
805 | + return theKeys; | |
806 | + } | |
807 | +}); | |
808 | + | |
809 | +var keysWorksWithArguments = Object.keys && (function () { | |
810 | + // Safari 5.0 bug | |
811 | + return Object.keys(arguments).length === 2; | |
812 | +}(1, 2)); | |
813 | +var originalKeys = Object.keys; | |
814 | +defineProperties(Object, { | |
815 | + keys: function keys(object) { | |
816 | + if (isArguments(object)) { | |
817 | + return originalKeys(ArrayPrototype.slice.call(object)); | |
818 | + } else { | |
819 | + return originalKeys(object); | |
820 | + } | |
821 | + } | |
822 | +}, !keysWorksWithArguments); | |
823 | + | |
824 | +// | |
825 | +// Date | |
826 | +// ==== | |
827 | +// | |
828 | + | |
829 | +// ES5 15.9.5.43 | |
830 | +// http://es5.github.com/#x15.9.5.43 | |
831 | +// This function returns a String value represent the instance in time | |
832 | +// represented by this Date object. The format of the String is the Date Time | |
833 | +// string format defined in 15.9.1.15. All fields are present in the String. | |
834 | +// The time zone is always UTC, denoted by the suffix Z. If the time value of | |
835 | +// this object is not a finite Number a RangeError exception is thrown. | |
836 | +var negativeDate = -62198755200000; | |
837 | +var negativeYearString = '-000001'; | |
838 | +var hasNegativeDateBug = Date.prototype.toISOString && new Date(negativeDate).toISOString().indexOf(negativeYearString) === -1; | |
839 | + | |
840 | +defineProperties(Date.prototype, { | |
841 | + toISOString: function toISOString() { | |
842 | + var result, length, value, year, month; | |
843 | + if (!isFinite(this)) { | |
844 | + throw new RangeError('Date.prototype.toISOString called on non-finite value.'); | |
845 | + } | |
846 | + | |
847 | + year = this.getUTCFullYear(); | |
848 | + | |
849 | + month = this.getUTCMonth(); | |
850 | + // see https://github.com/es-shims/es5-shim/issues/111 | |
851 | + year += Math.floor(month / 12); | |
852 | + month = (month % 12 + 12) % 12; | |
853 | + | |
854 | + // the date time string format is specified in 15.9.1.15. | |
855 | + result = [month + 1, this.getUTCDate(), this.getUTCHours(), this.getUTCMinutes(), this.getUTCSeconds()]; | |
856 | + year = ( | |
857 | + (year < 0 ? '-' : (year > 9999 ? '+' : '')) + | |
858 | + ('00000' + Math.abs(year)).slice((0 <= year && year <= 9999) ? -4 : -6) | |
859 | + ); | |
860 | + | |
861 | + length = result.length; | |
862 | + while (length--) { | |
863 | + value = result[length]; | |
864 | + // pad months, days, hours, minutes, and seconds to have two | |
865 | + // digits. | |
866 | + if (value < 10) { | |
867 | + result[length] = '0' + value; | |
868 | + } | |
869 | + } | |
870 | + // pad milliseconds to have three digits. | |
871 | + return ( | |
872 | + year + '-' + result.slice(0, 2).join('-') + | |
873 | + 'T' + result.slice(2).join(':') + '.' + | |
874 | + ('000' + this.getUTCMilliseconds()).slice(-3) + 'Z' | |
875 | + ); | |
876 | + } | |
877 | +}, hasNegativeDateBug); | |
878 | + | |
879 | +// ES5 15.9.5.44 | |
880 | +// http://es5.github.com/#x15.9.5.44 | |
881 | +// This function provides a String representation of a Date object for use by | |
882 | +// JSON.stringify (15.12.3). | |
883 | +var dateToJSONIsSupported = (function () { | |
884 | + try { | |
885 | + return Date.prototype.toJSON && | |
886 | + new Date(NaN).toJSON() === null && | |
887 | + new Date(negativeDate).toJSON().indexOf(negativeYearString) !== -1 && | |
888 | + Date.prototype.toJSON.call({ // generic | |
889 | + toISOString: function () { return true; } | |
890 | + }); | |
891 | + } catch (e) { | |
892 | + return false; | |
893 | + } | |
894 | +}()); | |
895 | +if (!dateToJSONIsSupported) { | |
896 | + Date.prototype.toJSON = function toJSON(key) { | |
897 | + // When the toJSON method is called with argument key, the following | |
898 | + // steps are taken: | |
899 | + | |
900 | + // 1. Let O be the result of calling ToObject, giving it the this | |
901 | + // value as its argument. | |
902 | + // 2. Let tv be ES.ToPrimitive(O, hint Number). | |
903 | + var O = Object(this); | |
904 | + var tv = ES.ToPrimitive(O); | |
905 | + // 3. If tv is a Number and is not finite, return null. | |
906 | + if (typeof tv === 'number' && !isFinite(tv)) { | |
907 | + return null; | |
908 | + } | |
909 | + // 4. Let toISO be the result of calling the [[Get]] internal method of | |
910 | + // O with argument "toISOString". | |
911 | + var toISO = O.toISOString; | |
912 | + // 5. If IsCallable(toISO) is false, throw a TypeError exception. | |
913 | + if (!isCallable(toISO)) { | |
914 | + throw new TypeError('toISOString property is not callable'); | |
915 | + } | |
916 | + // 6. Return the result of calling the [[Call]] internal method of | |
917 | + // toISO with O as the this value and an empty argument list. | |
918 | + return toISO.call(O); | |
919 | + | |
920 | + // NOTE 1 The argument is ignored. | |
921 | + | |
922 | + // NOTE 2 The toJSON function is intentionally generic; it does not | |
923 | + // require that its this value be a Date object. Therefore, it can be | |
924 | + // transferred to other kinds of objects for use as a method. However, | |
925 | + // it does require that any such object have a toISOString method. An | |
926 | + // object is free to use the argument key to filter its | |
927 | + // stringification. | |
928 | + }; | |
929 | +} | |
930 | + | |
931 | +// ES5 15.9.4.2 | |
932 | +// http://es5.github.com/#x15.9.4.2 | |
933 | +// based on work shared by Daniel Friesen (dantman) | |
934 | +// http://gist.github.com/303249 | |
935 | +var supportsExtendedYears = Date.parse('+033658-09-27T01:46:40.000Z') === 1e15; | |
936 | +var acceptsInvalidDates = !isNaN(Date.parse('2012-04-04T24:00:00.500Z')) || !isNaN(Date.parse('2012-11-31T23:59:59.000Z')); | |
937 | +var doesNotParseY2KNewYear = isNaN(Date.parse('2000-01-01T00:00:00.000Z')); | |
938 | +if (!Date.parse || doesNotParseY2KNewYear || acceptsInvalidDates || !supportsExtendedYears) { | |
939 | + // XXX global assignment won't work in embeddings that use | |
940 | + // an alternate object for the context. | |
941 | + /*global Date: true */ | |
942 | + /*eslint-disable no-undef*/ | |
943 | + Date = (function (NativeDate) { | |
944 | + /*eslint-enable no-undef*/ | |
945 | + // Date.length === 7 | |
946 | + var DateShim = function Date(Y, M, D, h, m, s, ms) { | |
947 | + var length = arguments.length; | |
948 | + var date; | |
949 | + if (this instanceof NativeDate) { | |
950 | + date = length === 1 && String(Y) === Y ? // isString(Y) | |
951 | + // We explicitly pass it through parse: | |
952 | + new NativeDate(DateShim.parse(Y)) : | |
953 | + // We have to manually make calls depending on argument | |
954 | + // length here | |
955 | + length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) : | |
956 | + length >= 6 ? new NativeDate(Y, M, D, h, m, s) : | |
957 | + length >= 5 ? new NativeDate(Y, M, D, h, m) : | |
958 | + length >= 4 ? new NativeDate(Y, M, D, h) : | |
959 | + length >= 3 ? new NativeDate(Y, M, D) : | |
960 | + length >= 2 ? new NativeDate(Y, M) : | |
961 | + length >= 1 ? new NativeDate(Y) : | |
962 | + new NativeDate(); | |
963 | + } else { | |
964 | + date = NativeDate.apply(this, arguments); | |
965 | + } | |
966 | + // Prevent mixups with unfixed Date object | |
967 | + defineProperties(date, { constructor: DateShim }, true); | |
968 | + return date; | |
969 | + }; | |
970 | + | |
971 | + // 15.9.1.15 Date Time String Format. | |
972 | + var isoDateExpression = new RegExp('^' + | |
973 | + '(\\d{4}|[+-]\\d{6})' + // four-digit year capture or sign + | |
974 | + // 6-digit extended year | |
975 | + '(?:-(\\d{2})' + // optional month capture | |
976 | + '(?:-(\\d{2})' + // optional day capture | |
977 | + '(?:' + // capture hours:minutes:seconds.milliseconds | |
978 | + 'T(\\d{2})' + // hours capture | |
979 | + ':(\\d{2})' + // minutes capture | |
980 | + '(?:' + // optional :seconds.milliseconds | |
981 | + ':(\\d{2})' + // seconds capture | |
982 | + '(?:(\\.\\d{1,}))?' + // milliseconds capture | |
983 | + ')?' + | |
984 | + '(' + // capture UTC offset component | |
985 | + 'Z|' + // UTC capture | |
986 | + '(?:' + // offset specifier +/-hours:minutes | |
987 | + '([-+])' + // sign capture | |
988 | + '(\\d{2})' + // hours offset capture | |
989 | + ':(\\d{2})' + // minutes offset capture | |
990 | + ')' + | |
991 | + ')?)?)?)?' + | |
992 | + '$'); | |
993 | + | |
994 | + var months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; | |
995 | + | |
996 | + var dayFromMonth = function dayFromMonth(year, month) { | |
997 | + var t = month > 1 ? 1 : 0; | |
998 | + return ( | |
999 | + months[month] + | |
1000 | + Math.floor((year - 1969 + t) / 4) - | |
1001 | + Math.floor((year - 1901 + t) / 100) + | |
1002 | + Math.floor((year - 1601 + t) / 400) + | |
1003 | + 365 * (year - 1970) | |
1004 | + ); | |
1005 | + }; | |
1006 | + | |
1007 | + var toUTC = function toUTC(t) { | |
1008 | + return Number(new NativeDate(1970, 0, 1, 0, 0, 0, t)); | |
1009 | + }; | |
1010 | + | |
1011 | + // Copy any custom methods a 3rd party library may have added | |
1012 | + for (var key in NativeDate) { | |
1013 | + if (owns(NativeDate, key)) { | |
1014 | + DateShim[key] = NativeDate[key]; | |
1015 | + } | |
1016 | + } | |
1017 | + | |
1018 | + // Copy "native" methods explicitly; they may be non-enumerable | |
1019 | + DateShim.now = NativeDate.now; | |
1020 | + DateShim.UTC = NativeDate.UTC; | |
1021 | + DateShim.prototype = NativeDate.prototype; | |
1022 | + DateShim.prototype.constructor = DateShim; | |
1023 | + | |
1024 | + // Upgrade Date.parse to handle simplified ISO 8601 strings | |
1025 | + DateShim.parse = function parse(string) { | |
1026 | + var match = isoDateExpression.exec(string); | |
1027 | + if (match) { | |
1028 | + // parse months, days, hours, minutes, seconds, and milliseconds | |
1029 | + // provide default values if necessary | |
1030 | + // parse the UTC offset component | |
1031 | + var year = Number(match[1]), | |
1032 | + month = Number(match[2] || 1) - 1, | |
1033 | + day = Number(match[3] || 1) - 1, | |
1034 | + hour = Number(match[4] || 0), | |
1035 | + minute = Number(match[5] || 0), | |
1036 | + second = Number(match[6] || 0), | |
1037 | + millisecond = Math.floor(Number(match[7] || 0) * 1000), | |
1038 | + // When time zone is missed, local offset should be used | |
1039 | + // (ES 5.1 bug) | |
1040 | + // see https://bugs.ecmascript.org/show_bug.cgi?id=112 | |
1041 | + isLocalTime = Boolean(match[4] && !match[8]), | |
1042 | + signOffset = match[9] === '-' ? 1 : -1, | |
1043 | + hourOffset = Number(match[10] || 0), | |
1044 | + minuteOffset = Number(match[11] || 0), | |
1045 | + result; | |
1046 | + if ( | |
1047 | + hour < ( | |
1048 | + minute > 0 || second > 0 || millisecond > 0 ? | |
1049 | + 24 : 25 | |
1050 | + ) && | |
1051 | + minute < 60 && second < 60 && millisecond < 1000 && | |
1052 | + month > -1 && month < 12 && hourOffset < 24 && | |
1053 | + minuteOffset < 60 && // detect invalid offsets | |
1054 | + day > -1 && | |
1055 | + day < ( | |
1056 | + dayFromMonth(year, month + 1) - | |
1057 | + dayFromMonth(year, month) | |
1058 | + ) | |
1059 | + ) { | |
1060 | + result = ( | |
1061 | + (dayFromMonth(year, month) + day) * 24 + | |
1062 | + hour + | |
1063 | + hourOffset * signOffset | |
1064 | + ) * 60; | |
1065 | + result = ( | |
1066 | + (result + minute + minuteOffset * signOffset) * 60 + | |
1067 | + second | |
1068 | + ) * 1000 + millisecond; | |
1069 | + if (isLocalTime) { | |
1070 | + result = toUTC(result); | |
1071 | + } | |
1072 | + if (-8.64e15 <= result && result <= 8.64e15) { | |
1073 | + return result; | |
1074 | + } | |
1075 | + } | |
1076 | + return NaN; | |
1077 | + } | |
1078 | + return NativeDate.parse.apply(this, arguments); | |
1079 | + }; | |
1080 | + | |
1081 | + return DateShim; | |
1082 | + }(Date)); | |
1083 | + /*global Date: false */ | |
1084 | +} | |
1085 | + | |
1086 | +// ES5 15.9.4.4 | |
1087 | +// http://es5.github.com/#x15.9.4.4 | |
1088 | +if (!Date.now) { | |
1089 | + Date.now = function now() { | |
1090 | + return new Date().getTime(); | |
1091 | + }; | |
1092 | +} | |
1093 | + | |
1094 | +// | |
1095 | +// Number | |
1096 | +// ====== | |
1097 | +// | |
1098 | + | |
1099 | +// ES5.1 15.7.4.5 | |
1100 | +// http://es5.github.com/#x15.7.4.5 | |
1101 | +var hasToFixedBugs = NumberPrototype.toFixed && ( | |
1102 | + (0.00008).toFixed(3) !== '0.000' || | |
1103 | + (0.9).toFixed(0) !== '1' || | |
1104 | + (1.255).toFixed(2) !== '1.25' || | |
1105 | + (1000000000000000128).toFixed(0) !== '1000000000000000128' | |
1106 | +); | |
1107 | + | |
1108 | +var toFixedHelpers = { | |
1109 | + base: 1e7, | |
1110 | + size: 6, | |
1111 | + data: [0, 0, 0, 0, 0, 0], | |
1112 | + multiply: function multiply(n, c) { | |
1113 | + var i = -1; | |
1114 | + var c2 = c; | |
1115 | + while (++i < toFixedHelpers.size) { | |
1116 | + c2 += n * toFixedHelpers.data[i]; | |
1117 | + toFixedHelpers.data[i] = c2 % toFixedHelpers.base; | |
1118 | + c2 = Math.floor(c2 / toFixedHelpers.base); | |
1119 | + } | |
1120 | + }, | |
1121 | + divide: function divide(n) { | |
1122 | + var i = toFixedHelpers.size, c = 0; | |
1123 | + while (--i >= 0) { | |
1124 | + c += toFixedHelpers.data[i]; | |
1125 | + toFixedHelpers.data[i] = Math.floor(c / n); | |
1126 | + c = (c % n) * toFixedHelpers.base; | |
1127 | + } | |
1128 | + }, | |
1129 | + numToString: function numToString() { | |
1130 | + var i = toFixedHelpers.size; | |
1131 | + var s = ''; | |
1132 | + while (--i >= 0) { | |
1133 | + if (s !== '' || i === 0 || toFixedHelpers.data[i] !== 0) { | |
1134 | + var t = String(toFixedHelpers.data[i]); | |
1135 | + if (s === '') { | |
1136 | + s = t; | |
1137 | + } else { | |
1138 | + s += '0000000'.slice(0, 7 - t.length) + t; | |
1139 | + } | |
1140 | + } | |
1141 | + } | |
1142 | + return s; | |
1143 | + }, | |
1144 | + pow: function pow(x, n, acc) { | |
1145 | + return (n === 0 ? acc : (n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc))); | |
1146 | + }, | |
1147 | + log: function log(x) { | |
1148 | + var n = 0; | |
1149 | + var x2 = x; | |
1150 | + while (x2 >= 4096) { | |
1151 | + n += 12; | |
1152 | + x2 /= 4096; | |
1153 | + } | |
1154 | + while (x2 >= 2) { | |
1155 | + n += 1; | |
1156 | + x2 /= 2; | |
1157 | + } | |
1158 | + return n; | |
1159 | + } | |
1160 | +}; | |
1161 | + | |
1162 | +defineProperties(NumberPrototype, { | |
1163 | + toFixed: function toFixed(fractionDigits) { | |
1164 | + var f, x, s, m, e, z, j, k; | |
1165 | + | |
1166 | + // Test for NaN and round fractionDigits down | |
1167 | + f = Number(fractionDigits); | |
1168 | + f = f !== f ? 0 : Math.floor(f); | |
1169 | + | |
1170 | + if (f < 0 || f > 20) { | |
1171 | + throw new RangeError('Number.toFixed called with invalid number of decimals'); | |
1172 | + } | |
1173 | + | |
1174 | + x = Number(this); | |
1175 | + | |
1176 | + // Test for NaN | |
1177 | + if (x !== x) { | |
1178 | + return 'NaN'; | |
1179 | + } | |
1180 | + | |
1181 | + // If it is too big or small, return the string value of the number | |
1182 | + if (x <= -1e21 || x >= 1e21) { | |
1183 | + return String(x); | |
1184 | + } | |
1185 | + | |
1186 | + s = ''; | |
1187 | + | |
1188 | + if (x < 0) { | |
1189 | + s = '-'; | |
1190 | + x = -x; | |
1191 | + } | |
1192 | + | |
1193 | + m = '0'; | |
1194 | + | |
1195 | + if (x > 1e-21) { | |
1196 | + // 1e-21 < x < 1e21 | |
1197 | + // -70 < log2(x) < 70 | |
1198 | + e = toFixedHelpers.log(x * toFixedHelpers.pow(2, 69, 1)) - 69; | |
1199 | + z = (e < 0 ? x * toFixedHelpers.pow(2, -e, 1) : x / toFixedHelpers.pow(2, e, 1)); | |
1200 | + z *= 0x10000000000000; // Math.pow(2, 52); | |
1201 | + e = 52 - e; | |
1202 | + | |
1203 | + // -18 < e < 122 | |
1204 | + // x = z / 2 ^ e | |
1205 | + if (e > 0) { | |
1206 | + toFixedHelpers.multiply(0, z); | |
1207 | + j = f; | |
1208 | + | |
1209 | + while (j >= 7) { | |
1210 | + toFixedHelpers.multiply(1e7, 0); | |
1211 | + j -= 7; | |
1212 | + } | |
1213 | + | |
1214 | + toFixedHelpers.multiply(toFixedHelpers.pow(10, j, 1), 0); | |
1215 | + j = e - 1; | |
1216 | + | |
1217 | + while (j >= 23) { | |
1218 | + toFixedHelpers.divide(1 << 23); | |
1219 | + j -= 23; | |
1220 | + } | |
1221 | + | |
1222 | + toFixedHelpers.divide(1 << j); | |
1223 | + toFixedHelpers.multiply(1, 1); | |
1224 | + toFixedHelpers.divide(2); | |
1225 | + m = toFixedHelpers.numToString(); | |
1226 | + } else { | |
1227 | + toFixedHelpers.multiply(0, z); | |
1228 | + toFixedHelpers.multiply(1 << (-e), 0); | |
1229 | + m = toFixedHelpers.numToString() + '0.00000000000000000000'.slice(2, 2 + f); | |
1230 | + } | |
1231 | + } | |
1232 | + | |
1233 | + if (f > 0) { | |
1234 | + k = m.length; | |
1235 | + | |
1236 | + if (k <= f) { | |
1237 | + m = s + '0.0000000000000000000'.slice(0, f - k + 2) + m; | |
1238 | + } else { | |
1239 | + m = s + m.slice(0, k - f) + '.' + m.slice(k - f); | |
1240 | + } | |
1241 | + } else { | |
1242 | + m = s + m; | |
1243 | + } | |
1244 | + | |
1245 | + return m; | |
1246 | + } | |
1247 | +}, hasToFixedBugs); | |
1248 | + | |
1249 | +// | |
1250 | +// String | |
1251 | +// ====== | |
1252 | +// | |
1253 | + | |
1254 | +// ES5 15.5.4.14 | |
1255 | +// http://es5.github.com/#x15.5.4.14 | |
1256 | + | |
1257 | +// [bugfix, IE lt 9, firefox 4, Konqueror, Opera, obscure browsers] | |
1258 | +// Many browsers do not split properly with regular expressions or they | |
1259 | +// do not perform the split correctly under obscure conditions. | |
1260 | +// See http://blog.stevenlevithan.com/archives/cross-browser-split | |
1261 | +// I've tested in many browsers and this seems to cover the deviant ones: | |
1262 | +// 'ab'.split(/(?:ab)*/) should be ["", ""], not [""] | |
1263 | +// '.'.split(/(.?)(.?)/) should be ["", ".", "", ""], not ["", ""] | |
1264 | +// 'tesst'.split(/(s)*/) should be ["t", undefined, "e", "s", "t"], not | |
1265 | +// [undefined, "t", undefined, "e", ...] | |
1266 | +// ''.split(/.?/) should be [], not [""] | |
1267 | +// '.'.split(/()()/) should be ["."], not ["", "", "."] | |
1268 | + | |
1269 | +var string_split = StringPrototype.split; | |
1270 | +if ( | |
1271 | + 'ab'.split(/(?:ab)*/).length !== 2 || | |
1272 | + '.'.split(/(.?)(.?)/).length !== 4 || | |
1273 | + 'tesst'.split(/(s)*/)[1] === 't' || | |
1274 | + 'test'.split(/(?:)/, -1).length !== 4 || | |
1275 | + ''.split(/.?/).length || | |
1276 | + '.'.split(/()()/).length > 1 | |
1277 | +) { | |
1278 | + (function () { | |
1279 | + var compliantExecNpcg = typeof (/()??/).exec('')[1] === 'undefined'; // NPCG: nonparticipating capturing group | |
1280 | + | |
1281 | + StringPrototype.split = function (separator, limit) { | |
1282 | + var string = this; | |
1283 | + if (typeof separator === 'undefined' && limit === 0) { | |
1284 | + return []; | |
1285 | + } | |
1286 | + | |
1287 | + // If `separator` is not a regex, use native split | |
1288 | + if (!isRegex(separator)) { | |
1289 | + return string_split.call(this, separator, limit); | |
1290 | + } | |
1291 | + | |
1292 | + var output = []; | |
1293 | + var flags = (separator.ignoreCase ? 'i' : '') + | |
1294 | + (separator.multiline ? 'm' : '') + | |
1295 | + (separator.extended ? 'x' : '') + // Proposed for ES6 | |
1296 | + (separator.sticky ? 'y' : ''), // Firefox 3+ | |
1297 | + lastLastIndex = 0, | |
1298 | + // Make `global` and avoid `lastIndex` issues by working with a copy | |
1299 | + separator2, match, lastIndex, lastLength; | |
1300 | + var separatorCopy = new RegExp(separator.source, flags + 'g'); | |
1301 | + string += ''; // Type-convert | |
1302 | + if (!compliantExecNpcg) { | |
1303 | + // Doesn't need flags gy, but they don't hurt | |
1304 | + separator2 = new RegExp('^' + separatorCopy.source + '$(?!\\s)', flags); | |
1305 | + } | |
1306 | + /* Values for `limit`, per the spec: | |
1307 | + * If undefined: 4294967295 // Math.pow(2, 32) - 1 | |
1308 | + * If 0, Infinity, or NaN: 0 | |
1309 | + * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296; | |
1310 | + * If negative number: 4294967296 - Math.floor(Math.abs(limit)) | |
1311 | + * If other: Type-convert, then use the above rules | |
1312 | + */ | |
1313 | + var splitLimit = typeof limit === 'undefined' ? | |
1314 | + -1 >>> 0 : // Math.pow(2, 32) - 1 | |
1315 | + ES.ToUint32(limit); | |
1316 | + match = separatorCopy.exec(string); | |
1317 | + while (match) { | |
1318 | + // `separatorCopy.lastIndex` is not reliable cross-browser | |
1319 | + lastIndex = match.index + match[0].length; | |
1320 | + if (lastIndex > lastLastIndex) { | |
1321 | + output.push(string.slice(lastLastIndex, match.index)); | |
1322 | + // Fix browsers whose `exec` methods don't consistently return `undefined` for | |
1323 | + // nonparticipating capturing groups | |
1324 | + if (!compliantExecNpcg && match.length > 1) { | |
1325 | + /*eslint-disable no-loop-func */ | |
1326 | + match[0].replace(separator2, function () { | |
1327 | + for (var i = 1; i < arguments.length - 2; i++) { | |
1328 | + if (typeof arguments[i] === 'undefined') { | |
1329 | + match[i] = void 0; | |
1330 | + } | |
1331 | + } | |
1332 | + }); | |
1333 | + /*eslint-enable no-loop-func */ | |
1334 | + } | |
1335 | + if (match.length > 1 && match.index < string.length) { | |
1336 | + array_push.apply(output, match.slice(1)); | |
1337 | + } | |
1338 | + lastLength = match[0].length; | |
1339 | + lastLastIndex = lastIndex; | |
1340 | + if (output.length >= splitLimit) { | |
1341 | + break; | |
1342 | + } | |
1343 | + } | |
1344 | + if (separatorCopy.lastIndex === match.index) { | |
1345 | + separatorCopy.lastIndex++; // Avoid an infinite loop | |
1346 | + } | |
1347 | + match = separatorCopy.exec(string); | |
1348 | + } | |
1349 | + if (lastLastIndex === string.length) { | |
1350 | + if (lastLength || !separatorCopy.test('')) { | |
1351 | + output.push(''); | |
1352 | + } | |
1353 | + } else { | |
1354 | + output.push(string.slice(lastLastIndex)); | |
1355 | + } | |
1356 | + return output.length > splitLimit ? output.slice(0, splitLimit) : output; | |
1357 | + }; | |
1358 | + }()); | |
1359 | + | |
1360 | +// [bugfix, chrome] | |
1361 | +// If separator is undefined, then the result array contains just one String, | |
1362 | +// which is the this value (converted to a String). If limit is not undefined, | |
1363 | +// then the output array is truncated so that it contains no more than limit | |
1364 | +// elements. | |
1365 | +// "0".split(undefined, 0) -> [] | |
1366 | +} else if ('0'.split(void 0, 0).length) { | |
1367 | + StringPrototype.split = function split(separator, limit) { | |
1368 | + if (typeof separator === 'undefined' && limit === 0) { return []; } | |
1369 | + return string_split.call(this, separator, limit); | |
1370 | + }; | |
1371 | +} | |
1372 | + | |
1373 | +var str_replace = StringPrototype.replace; | |
1374 | +var replaceReportsGroupsCorrectly = (function () { | |
1375 | + var groups = []; | |
1376 | + 'x'.replace(/x(.)?/g, function (match, group) { | |
1377 | + groups.push(group); | |
1378 | + }); | |
1379 | + return groups.length === 1 && typeof groups[0] === 'undefined'; | |
1380 | +}()); | |
1381 | + | |
1382 | +if (!replaceReportsGroupsCorrectly) { | |
1383 | + StringPrototype.replace = function replace(searchValue, replaceValue) { | |
1384 | + var isFn = isCallable(replaceValue); | |
1385 | + var hasCapturingGroups = isRegex(searchValue) && (/\)[*?]/).test(searchValue.source); | |
1386 | + if (!isFn || !hasCapturingGroups) { | |
1387 | + return str_replace.call(this, searchValue, replaceValue); | |
1388 | + } else { | |
1389 | + var wrappedReplaceValue = function (match) { | |
1390 | + var length = arguments.length; | |
1391 | + var originalLastIndex = searchValue.lastIndex; | |
1392 | + searchValue.lastIndex = 0; | |
1393 | + var args = searchValue.exec(match) || []; | |
1394 | + searchValue.lastIndex = originalLastIndex; | |
1395 | + args.push(arguments[length - 2], arguments[length - 1]); | |
1396 | + return replaceValue.apply(this, args); | |
1397 | + }; | |
1398 | + return str_replace.call(this, searchValue, wrappedReplaceValue); | |
1399 | + } | |
1400 | + }; | |
1401 | +} | |
1402 | + | |
1403 | +// ECMA-262, 3rd B.2.3 | |
1404 | +// Not an ECMAScript standard, although ECMAScript 3rd Edition has a | |
1405 | +// non-normative section suggesting uniform semantics and it should be | |
1406 | +// normalized across all browsers | |
1407 | +// [bugfix, IE lt 9] IE < 9 substr() with negative value not working in IE | |
1408 | +var string_substr = StringPrototype.substr; | |
1409 | +var hasNegativeSubstrBug = ''.substr && '0b'.substr(-1) !== 'b'; | |
1410 | +defineProperties(StringPrototype, { | |
1411 | + substr: function substr(start, length) { | |
1412 | + var normalizedStart = start; | |
1413 | + if (start < 0) { | |
1414 | + normalizedStart = Math.max(this.length + start, 0); | |
1415 | + } | |
1416 | + return string_substr.call(this, normalizedStart, length); | |
1417 | + } | |
1418 | +}, hasNegativeSubstrBug); | |
1419 | + | |
1420 | +// ES5 15.5.4.20 | |
1421 | +// whitespace from: http://es5.github.io/#x15.5.4.20 | |
1422 | +var ws = '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003' + | |
1423 | + '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028' + | |
1424 | + '\u2029\uFEFF'; | |
1425 | +var zeroWidth = '\u200b'; | |
1426 | +var wsRegexChars = '[' + ws + ']'; | |
1427 | +var trimBeginRegexp = new RegExp('^' + wsRegexChars + wsRegexChars + '*'); | |
1428 | +var trimEndRegexp = new RegExp(wsRegexChars + wsRegexChars + '*$'); | |
1429 | +var hasTrimWhitespaceBug = StringPrototype.trim && (ws.trim() || !zeroWidth.trim()); | |
1430 | +defineProperties(StringPrototype, { | |
1431 | + // http://blog.stevenlevithan.com/archives/faster-trim-javascript | |
1432 | + // http://perfectionkills.com/whitespace-deviations/ | |
1433 | + trim: function trim() { | |
1434 | + if (typeof this === 'undefined' || this === null) { | |
1435 | + throw new TypeError("can't convert " + this + ' to object'); | |
1436 | + } | |
1437 | + return String(this).replace(trimBeginRegexp, '').replace(trimEndRegexp, ''); | |
1438 | + } | |
1439 | +}, hasTrimWhitespaceBug); | |
1440 | + | |
1441 | +// ES-5 15.1.2.2 | |
1442 | +if (parseInt(ws + '08') !== 8 || parseInt(ws + '0x16') !== 22) { | |
1443 | + /*global parseInt: true */ | |
1444 | + parseInt = (function (origParseInt) { | |
1445 | + var hexRegex = /^0[xX]/; | |
1446 | + return function parseInt(str, radix) { | |
1447 | + var string = String(str).trim(); | |
1448 | + var defaultedRadix = Number(radix) || (hexRegex.test(string) ? 16 : 10); | |
1449 | + return origParseInt(string, defaultedRadix); | |
1450 | + }; | |
1451 | + }(parseInt)); | |
1452 | +} | |
1453 | + | |
1454 | +})); |
shim.js
View file @
199c806
1 | +casper.on( 'page.initialized', function(){ | |
2 | + this.evaluate(function(){ | |
3 | + var isFunction = function(o) { | |
4 | + return typeof o == 'function'; | |
5 | + }; | |
6 | + | |
7 | + var bind, | |
8 | + slice = [].slice, | |
9 | + proto = Function.prototype, | |
10 | + featureMap; | |
11 | + | |
12 | + featureMap = { | |
13 | + 'function-bind': 'bind' | |
14 | + }; | |
15 | + | |
16 | + function has(feature) { | |
17 | + var prop = featureMap[feature]; | |
18 | + return isFunction(proto[prop]); | |
19 | + } | |
20 | + | |
21 | + // check for missing features | |
22 | + if (!has('function-bind')) { | |
23 | + // adapted from Mozilla Developer Network example at | |
24 | + // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind | |
25 | + bind = function bind(obj) { | |
26 | + var args = slice.call(arguments, 1), | |
27 | + self = this, | |
28 | + nop = function() { | |
29 | + }, | |
30 | + bound = function() { | |
31 | + return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments))); | |
32 | + }; | |
33 | + nop.prototype = this.prototype || {}; // Firefox cries sometimes if prototype is undefined | |
34 | + bound.prototype = new nop(); | |
35 | + return bound; | |
36 | + }; | |
37 | + proto.bind = bind; | |
38 | + } | |
39 | + }); | |
40 | +}); |
take_screenshots.js
View file @
199c806
1 | +//http://docs.casperjs.org/en/latest/modules/casper.html#captureselector | |
2 | +//Run with: casperjs --ssl-protocol=tlsv1 ss_test.js | |
3 | + | |
4 | +var utils = require('utils'); | |
5 | + | |
6 | +var casper = require('casper').create({ | |
7 | + verbose: true, | |
8 | + logLevel: "debug", | |
9 | + viewportSize: {width: 1200, height: 800}, | |
10 | + onResourceReceived: function(C, requestData, request) { | |
11 | + //utils.dump(requestData); | |
12 | + }, | |
13 | +}); | |
14 | +casper.on('remote.msg', function(data){ | |
15 | + this.echo(data); | |
16 | +}); | |
17 | + | |
18 | +casper.on( 'page.error', function (msg, trace) { | |
19 | + this.echo(msg); | |
20 | +}); | |
21 | +phantom.page.injectJs( 'shim.js'); | |
22 | +function screenshot(pagename) { | |
23 | + casper.capture('built/screenshots/'+pagename+'.png', { | |
24 | + top: 0, | |
25 | + left: 0, | |
26 | + width: 1200, | |
27 | + height: 600, | |
28 | + quality:100 | |
29 | + }); | |
30 | +} | |
31 | + | |
32 | +casper.start('https://flashy.cards/app/login',function(){ | |
33 | + screenshot('login'); | |
34 | +/* this.fill('form', { | |
35 | + 'email': 'casper@flashy.cards', | |
36 | + 'password': 'casper' | |
37 | + }, true); | |
38 | + this.echo('logged in?');*/ | |
39 | +}).thenOpen('https://flashy.cards/app/requestpasswordreset', function () { | |
40 | + screenshot('requestpasswordreset.png'); | |
41 | +}).thenOpen('https://flashy.cards/app/resetpassword', function () { | |
42 | + screenshot('resetpassword.png'); | |
43 | +}).thenOpen('https://flashy.cards/app/addclass', function () { | |
44 | + screenshot('addclass.png'); | |
45 | +}).thenOpen('https://flashy.cards/app/deck', function () { | |
46 | + screenshot('deck.png'); | |
47 | +}).thenOpen('https://flashy.cards/app/feed', function () { | |
48 | + screenshot('feed.png'); | |
49 | +}).thenOpen('https://flashy.cards/app/study', function () { | |
50 | + screenshot('study.png'); | |
51 | +}).thenOpen('https://flashy.cards/app/flashcard', function () { | |
52 | + screenshot('flashcard.png'); | |
53 | +}).thenOpen('https://flashy.cards/app/logout', function () { | |
54 | + screenshot('logout.png'); | |
55 | +}).thenOpen('https://flashy.cards/app/verifyemail', function () { | |
56 | + screenshot('verifyemail.png'); | |
57 | +}); | |
58 | + | |
59 | +casper.run(); |