UNPKG

57.2 kBJavaScriptView Raw
1/**
2* statful-client-javascript 2.0.2
3* Copyright 2017 Statful <https://www.statful.com/>
4*/
5
6(function(window) {
7 "use strict";
8 function Logger(enableDebug) {
9 this.debugEnabled = enableDebug || false;
10 }
11 Logger.prototype.info = function() {
12 if (this.debugEnabled) {
13 var args = Array.prototype.slice.call(arguments);
14 console.info.apply(console, args);
15 }
16 };
17 Logger.prototype.debug = function() {
18 if (this.debugEnabled) {
19 var args = Array.prototype.slice.call(arguments);
20 console.debug.apply(console, args);
21 }
22 };
23 Logger.prototype.error = function() {
24 if (this.debugEnabled) {
25 var args = Array.prototype.slice.call(arguments);
26 console.error.apply(console, args);
27 }
28 };
29 window.StatfulLogger = Logger;
30})(window);
31
32(function(window) {
33 "use strict";
34 function StatfulUtil(config) {
35 config = config || {};
36 var self = this;
37 var logger;
38 //Merge configs
39 this.config = {};
40 this.constants = {
41 aggregationList: [ "avg", "count", "sum", "first", "last", "p90", "p95", "min", "max" ],
42 aggregationFrequencyList: [ 10, 30, 60, 120, 180, 300 ]
43 };
44 Object.keys(config).forEach(function(key) {
45 self.config[key] = config[key];
46 });
47 this.listQueues = [];
48 logger = new StatfulLogger(self.config.debug);
49 /**
50 * Sends HTTP request to the api
51 * @param {string} endpoint - action
52 * @param {string} requestData - request data
53 */
54 this.sendRequest = function(endpoint, requestData) {
55 var requestArr = [ this.config.apiAddress, endpoint ];
56 var requestUrl = requestArr.join("/");
57 logger.debug("Request: " + requestUrl, requestData);
58 var xmlHttp = new XMLHttpRequest();
59 xmlHttp.open("POST", requestUrl, true);
60 xmlHttp.timeout = config.timeout;
61 //Send the proper header information along with the request
62 xmlHttp.setRequestHeader("Content-type", "application/json");
63 xmlHttp.send(requestData);
64 xmlHttp.onreadystatechange = function() {
65 if (xmlHttp.status == 200 || xmlHttp.status == 201) {
66 logger.debug("Successfully send metric");
67 } else {
68 logger.debug("Error send metric", requestUrl, xmlHttp.status);
69 }
70 };
71 };
72 /**
73 * Register a new queue
74 * @param {string} queueName - queue name
75 * @param {string} endpoint - endpoint to send requests
76 * @param {int} timeInterval - interval in milliseconds, default 30000 ms
77 */
78 this.registerQueue = function(queueName, endpoint, timeInterval) {
79 timeInterval = timeInterval || this.config.flushInterval;
80 if (typeof queueName === "string" && typeof timeInterval === "number") {
81 var self = this;
82 this.listQueues[queueName] = {
83 data: [],
84 endpoint: endpoint
85 };
86 this.listQueues[queueName].timer = setInterval(function() {
87 var queue = self.listQueues[queueName];
88 if (queue.data.length > 0) {
89 if (!self.config.dryrun) {
90 self.sendRequest(queue.endpoint, JSON.stringify(queue.data));
91 } else {
92 logger.debug("Dryrun data", queue.endpoint, queue.data);
93 }
94 queue.data = [];
95 }
96 }, timeInterval);
97 return true;
98 } else {
99 return false;
100 }
101 };
102 /**
103 * Unregister queue
104 * @param {string} queueName - queue name
105 */
106 this.unregisterQueue = function(queueName) {
107 if (this.listQueues[queueName]) {
108 clearInterval(this.listQueues[queueName].timer);
109 this.listQueues[queueName] = undefined;
110 }
111 };
112 /**
113 * Sends an Item to a specific queue
114 * @param {string} queueName - queue name
115 * @param {object} item - object to be sent
116 */
117 this.addItemToQueue = function(queueName, item) {
118 var sampleRateNormalized = (item.sampleRate || this.config.sampleRate || 100) / 100;
119 if (this.listQueues[queueName] && Math.random() <= sampleRateNormalized) {
120 this.listQueues[queueName].data.push(item);
121 return true;
122 } else {
123 logger.debug("Metric was discarded due to sample rate.");
124 return false;
125 }
126 };
127 /**
128 * Define aggregations for a metric type
129 * @param {Array} methodAggregations - list of method aggregations
130 * @param {Array} globalAggregations - list of global aggregations
131 * @param {Array} typeAggregations - list of type aggregations
132 * @returns {*|Array}
133 */
134 this.setAggregations = function(methodAggregations, globalAggregations, typeAggregations) {
135 function uniq(item, index, array) {
136 return item && array.indexOf(item) === index;
137 }
138 var aggregations = globalAggregations;
139 aggregations = aggregations.concat(typeAggregations).filter(uniq);
140 if (!methodAggregations) {
141 aggregations = aggregations || [];
142 } else {
143 aggregations = aggregations.concat(methodAggregations).filter(uniq);
144 }
145 return this.filterAggregations(aggregations);
146 };
147 /**
148 * Define tags for a metric type
149 * @param {object} methodTags - list of method tags
150 * @param {object} globalTags - list of global tags
151 * @param {string} typeTags - list of type tags
152 * @param {string} app - app tag value
153 * @returns {*}
154 */
155 this.setTags = function(methodTags, globalTags, typeTags, app) {
156 var tags = {};
157 Object.keys(globalTags).forEach(function(key) {
158 tags[key] = globalTags[key];
159 });
160 Object.keys(typeTags).forEach(function(key) {
161 tags[key] = typeTags[key];
162 });
163 Object.keys(methodTags).forEach(function(key) {
164 tags[key] = methodTags[key];
165 });
166 if (!tags.app && app) {
167 tags.app = app;
168 }
169 return tags;
170 };
171 /**
172 * Define aggregation frequency
173 * @param {number} methodFrequency - method aggregation frequency
174 * @param {number} globalFrequency - global aggregation frequency
175 * @param {number} typeFrequency - type aggregation frequency
176 */
177 this.setAggregationFrequency = function(methodFrequency, globalFrequency, typeFrequency) {
178 var frequency = globalFrequency;
179 if (typeFrequency) {
180 frequency = typeFrequency;
181 }
182 if (methodFrequency) {
183 frequency = methodFrequency;
184 }
185 return this.filterAggregationFrequency(frequency);
186 };
187 /**
188 * Filter unsupported aggregations
189 * @param {Array} aggregations - list of aggregations
190 * @returns {Array}
191 */
192 this.filterAggregations = function(aggregations) {
193 var agg = this.constants.aggregationList;
194 aggregations = aggregations || [];
195 return aggregations.filter(function(item) {
196 return agg.indexOf(item) !== -1;
197 });
198 };
199 /**
200 * Filter unsupported frequencies
201 * @param {number} frequency - aggregation frequency
202 * @returns {*}
203 */
204 this.filterAggregationFrequency = function(frequency) {
205 var frequencyList = this.constants.aggregationFrequencyList;
206 var freq = 10;
207 if (frequencyList.indexOf(frequency) > -1) {
208 freq = frequency;
209 }
210 return freq;
211 };
212 }
213 window.StatfulUtil = StatfulUtil;
214})(window);
215
216(function(window) {
217 "use strict";
218 /**
219 * @description
220 *
221 * This object provides a client for the Statful service to register
222 * application metrics. It can be called as follows:
223 *
224 * statful.initialize({
225 * app: 'example-app'
226 * namespace: 'mobile',
227 * dryrun: false,
228 * debug: false
229 * });
230 *
231 * statful.counter('metricName', 1);
232 *
233 */
234 var defaultConfig = {
235 dryrun: false,
236 debug: false,
237 app: undefined,
238 namespace: "web",
239 tags: {},
240 aggregations: [],
241 aggregationFrequency: 10,
242 timer: {
243 tags: {
244 unit: "ms"
245 },
246 aggregations: [ "avg", "p90", "count" ]
247 },
248 counter: {
249 tags: {},
250 aggregations: [ "sum", "count" ]
251 },
252 gauge: {
253 tags: {},
254 aggregations: [ "last" ]
255 },
256 timeout: 2e3,
257 flushInterval: 1e4,
258 sampleRate: 100
259 };
260 var logger;
261 var statful = {
262 config: {
263 apiAddress: "https://beacon.statful.com"
264 },
265 endpoints: {
266 metrics: "beacon/metrics"
267 },
268 perf: window.performance,
269 /**
270 * Initialize the Statful client settings and register events
271 */
272 initialize: function(clientConfig) {
273 var self = this;
274 //Private functions
275 self.mergeConfigs = function(clientConfig) {
276 if (typeof clientConfig !== "object" || clientConfig === null) {
277 clientConfig = {};
278 }
279 // Set default properties
280 Object.keys(defaultConfig).forEach(function(key) {
281 self.config[key] = defaultConfig[key];
282 });
283 Object.keys(clientConfig).forEach(function(key) {
284 self.config[key] = clientConfig[key];
285 });
286 };
287 self.metricsData = function(name, type, value, tags, aggregations, aggregationFrequency, namespace, sampleRate) {
288 return {
289 name: name,
290 type: type,
291 value: value,
292 tags: self.util.setTags(tags || {}, self.config.tags, self.config[type].tags, self.config.app),
293 aggregations: self.util.setAggregations(aggregations, self.config.aggregations, self.config[type].aggregations),
294 aggregationFrequency: self.util.setAggregationFrequency(aggregationFrequency, self.config.aggregationFrequency, self.config[type].aggregationFrequency),
295 namespace: namespace || self.config.namespace,
296 sampleRate: sampleRate || self.config.sampleRate
297 };
298 };
299 this.mergeConfigs(clientConfig);
300 // Create Logger
301 logger = new StatfulLogger(self.config.debug);
302 // Create Util
303 self.util = new StatfulUtil({
304 apiAddress: this.config.apiAddress,
305 debug: this.config.debug,
306 dryrun: this.config.dryrun,
307 flushInterval: this.config.flushInterval,
308 timeout: this.config.timeout
309 });
310 //Register queue to send metrics
311 self.util.registerQueue("metrics", this.endpoints.metrics, this.config.flushInterval);
312 },
313 /**
314 * Measure a timer using the user timing specification
315 * @param {string} measureName name of the measure to create
316 * @returns {number}
317 */
318 measureTimeUserTiming: function(measureName) {
319 var time;
320 var measure = statful.perf.getEntriesByName(measureName).filter(function filterMeasures(entry) {
321 return entry.entryType === "measure";
322 });
323 if (measure.length > 0) {
324 // Always use the most recent measure if more exist
325 time = measure[measure.length - 1].duration;
326 } else {
327 logger.debug("Measure " + measureName + " not found");
328 }
329 return time;
330 },
331 /**
332 * Clear marks
333 * @param {Array} marks - list of marks to clear (optional)
334 */
335 clearMarks: function(marks) {
336 try {
337 if (marks) {
338 marks.forEach(function(mark) {
339 if (mark) {
340 statful.perf.clearMarks(mark);
341 }
342 });
343 } else {
344 statful.perf.clearMarks();
345 }
346 } catch (ex) {
347 logger.error(ex);
348 }
349 },
350 /**
351 * Clear resources
352 */
353 clearResources: function() {
354 try {
355 statful.perf.clearResourceTimings();
356 } catch (ex) {
357 logger.error(ex);
358 }
359 },
360 /**
361 * Clear measures
362 * @param {Array} measures - list of measures to clear (optional)
363 */
364 clearMeasures: function(measures) {
365 try {
366 if (measures) {
367 measures.forEach(function(measure) {
368 statful.perf.clearMeasures(measure);
369 });
370 } else {
371 statful.perf.clearMeasures();
372 }
373 } catch (ex) {
374 logger.error(ex);
375 }
376 },
377 /**
378 * Register a mark using the user timing specification
379 * @param markName - name of the mark to add
380 */
381 registerMark: function(markName) {
382 try {
383 logger.debug("Register Mark", markName);
384 if (markName) {
385 statful.perf.mark(markName);
386 } else {
387 logger.error("Undefined resource name to register as a mark");
388 }
389 } catch (ex) {
390 logger.error(ex);
391 }
392 },
393 /**
394 * Register a measure and sends a timer using the user timing specification and metric options
395 * @param {string} measureName - name of the measure to create in the browser (ie. timeto.apploaded)
396 * @param {string} metricName - name of the metric to send to statful (ie. timeto)
397 * @param {object} options - set of option (clearMarks, clearMeasures, startMark, endMark, tags and aggregations)
398 */
399 registerMeasure: function(measureName, metricName, options) {
400 try {
401 logger.debug("Register Measure", measureName, metricName, options);
402 if (measureName) {
403 var defaults = {
404 clearMarks: false,
405 clearMeasures: false
406 };
407 options = options || {};
408 Object.keys(options).forEach(function(key) {
409 defaults[key] = options[key];
410 });
411 // Create endMark if none is set
412 if (!defaults.endMark) {
413 this.registerMark(measureName);
414 defaults.endMark = measureName;
415 }
416 statful.perf.measure(measureName, defaults.startMark, defaults.endMark);
417 // Measure timer
418 var time = this.measureTimeUserTiming(measureName);
419 if (time) {
420 // Push metrics to queue
421 this.util.addItemToQueue("metrics", new this.metricsData(metricName, "timer", time, defaults.tags, defaults.aggregations, defaults.aggregationFrequency, defaults.namespace, defaults.sampleRate));
422 } else {
423 logger.error("Failed to get measure time to register as timer value");
424 }
425 if (defaults.clearMarks) {
426 this.clearMarks([ defaults.startMark, defaults.endMark ]);
427 }
428 if (defaults.clearMeasures) {
429 this.clearMeasures([ measureName ]);
430 }
431 } else {
432 logger.error("Undefined resource name to register as a measure");
433 }
434 } catch (ex) {
435 logger.error(ex);
436 }
437 },
438 /**
439 * Register timer
440 * @param {string} metricName - metric name to be used as metric name
441 * @param {number} metricValue - timer value to be sent
442 * @param {object} options - set of option (tags, agg, aggFreq, namespace)
443 */
444 timer: function(metricName, metricValue, options) {
445 try {
446 logger.debug("Register Timer", metricName, metricValue, options);
447 if (metricName && metricValue >= 0) {
448 options = options || {};
449 // Push metrics to queue
450 var item = new this.metricsData(metricName, "timer", metricValue, options.tags, options.agg, options.aggFreq, options.namespace, options.sampleRate);
451 this.util.addItemToQueue("metrics", item);
452 } else {
453 logger.error("Undefined metric name or invalid value to register as a timer");
454 }
455 } catch (ex) {
456 logger.error(ex);
457 }
458 },
459 /**
460 * Register counter
461 * @param {string} metricName - metric name to be sent
462 * @param {number} metricValue - count value to be sent
463 * @param {object} options - set of option (tags, agg, aggFreq, namespace)
464 */
465 counter: function(metricName, metricValue, options) {
466 try {
467 logger.debug("Register Counter", metricName, options);
468 metricValue = metricValue || 1;
469 if (metricName) {
470 options = options || {};
471 // Push metrics to queue
472 var item = new this.metricsData(metricName, "counter", metricValue, options.tags, options.agg, options.aggFreq, options.namespace, options.sampleRate);
473 this.util.addItemToQueue("metrics", item);
474 } else {
475 logger.error("Undefined metric name to register as a counter");
476 }
477 } catch (ex) {
478 logger.error(ex);
479 }
480 },
481 /**
482 * Register gauge
483 * @param {string} metricName metric name to be sent
484 * @param {number} metricValue gauge value to be sent
485 * @param {object} options - set of option (tags, agg, aggFreq, namespace)
486 */
487 gauge: function(metricName, metricValue, options) {
488 try {
489 logger.debug("Register Gauge", metricName, metricValue, options);
490 if (metricName && metricValue) {
491 options = options || {};
492 // Push metrics to queue
493 var item = new this.metricsData(metricName, "gauge", metricValue, options.tags, options.agg, options.aggFreq, options.namespace, options.sampleRate);
494 this.util.addItemToQueue("metrics", item);
495 } else {
496 logger.error("Undefined metric name/value to register as a gauge");
497 }
498 } catch (ex) {
499 logger.error(ex);
500 }
501 }
502 };
503 window.statful = statful;
504})(window);
505
506/* eslint-env browser,amd,node */
507//
508// usertiming.js
509//
510// A polyfill for UserTiming (http://www.w3.org/TR/user-timing/)
511//
512// Copyright 2013 Nic Jansma
513// http://nicj.net
514//
515// https://github.com/nicjansma/usertiming.js
516//
517// Licensed under the MIT license
518//
519(function(window) {
520 "use strict";
521 // allow running in Node.js environment
522 if (typeof window === "undefined") {
523 window = {};
524 }
525 // prepare base perf object
526 if (typeof window.performance === "undefined") {
527 window.performance = {};
528 }
529 // We need to keep a global reference to the window.performance object to
530 // prevent any added properties from being garbage-collected in Safari 8.
531 // https://bugs.webkit.org/show_bug.cgi?id=137407
532 window._perfRefForUserTimingPolyfill = window.performance;
533 //
534 // Note what we shimmed
535 //
536 window.performance.userTimingJsNow = false;
537 window.performance.userTimingJsNowPrefixed = false;
538 window.performance.userTimingJsUserTiming = false;
539 window.performance.userTimingJsUserTimingPrefixed = false;
540 window.performance.userTimingJsPerformanceTimeline = false;
541 window.performance.userTimingJsPerformanceTimelinePrefixed = false;
542 // for prefixed support
543 var prefixes = [];
544 var methods = [];
545 var methodTest = null;
546 var i, j;
547 //
548 // window.performance.now() shim
549 // http://www.w3.org/TR/hr-time/
550 //
551 if (typeof window.performance.now !== "function") {
552 window.performance.userTimingJsNow = true;
553 // copy prefixed version over if it exists
554 methods = [ "webkitNow", "msNow", "mozNow" ];
555 for (i = 0; i < methods.length; i++) {
556 if (typeof window.performance[methods[i]] === "function") {
557 window.performance.now = window.performance[methods[i]];
558 window.performance.userTimingJsNowPrefixed = true;
559 break;
560 }
561 }
562 //
563 // now() should be a DOMHighResTimeStamp, which is defined as being a time relative
564 // to navigationStart of the PerformanceTiming (PT) interface. If this browser supports
565 // PT, use that as our relative start. Otherwise, use "now" as the start and all other
566 // now() calls will be relative to our initialization.
567 //
568 var nowOffset = +new Date();
569 if (window.performance.timing && window.performance.timing.navigationStart) {
570 nowOffset = window.performance.timing.navigationStart;
571 } else if (typeof process !== "undefined" && typeof process.hrtime === "function") {
572 nowOffset = process.hrtime();
573 window.performance.now = function() {
574 var time = process.hrtime(nowOffset);
575 return time[0] * 1e3 + time[1] * 1e-6;
576 };
577 }
578 if (typeof window.performance.now !== "function") {
579 // No browser support, fall back to Date.now
580 if (Date.now) {
581 window.performance.now = function() {
582 return Date.now() - nowOffset;
583 };
584 } else {
585 // no Date.now support, get the time from new Date()
586 window.performance.now = function() {
587 return +new Date() - nowOffset;
588 };
589 }
590 }
591 }
592 //
593 // PerformanceTimeline (PT) shims
594 // http://www.w3.org/TR/performance-timeline/
595 //
596 /**
597 * Adds an object to our internal Performance Timeline array.
598 *
599 * Will be blank if the environment supports PT.
600 */
601 var addToPerformanceTimeline = function() {};
602 /**
603 * Clears the specified entry types from our timeline array.
604 *
605 * Will be blank if the environment supports PT.
606 */
607 var clearEntriesFromPerformanceTimeline = function() {};
608 // performance timeline array
609 var performanceTimeline = [];
610 // whether or not the timeline will require sort on getEntries()
611 var performanceTimelineRequiresSort = false;
612 // whether or not ResourceTiming is natively supported but UserTiming is
613 // not (eg Firefox 35)
614 var hasNativeGetEntriesButNotUserTiming = false;
615 //
616 // If getEntries() and mark() aren't defined, we'll assume
617 // we have to shim at least some PT functions.
618 //
619 if (typeof window.performance.getEntries !== "function" || typeof window.performance.mark !== "function") {
620 if (typeof window.performance.getEntries === "function" && typeof window.performance.mark !== "function") {
621 hasNativeGetEntriesButNotUserTiming = true;
622 }
623 window.performance.userTimingJsPerformanceTimeline = true;
624 // copy prefixed version over if it exists
625 prefixes = [ "webkit", "moz" ];
626 methods = [ "getEntries", "getEntriesByName", "getEntriesByType" ];
627 for (i = 0; i < methods.length; i++) {
628 for (j = 0; j < prefixes.length; j++) {
629 // prefixed method will likely have an upper-case first letter
630 methodTest = prefixes[j] + methods[i].substr(0, 1).toUpperCase() + methods[i].substr(1);
631 if (typeof window.performance[methodTest] === "function") {
632 window.performance[methods[i]] = window.performance[methodTest];
633 window.performance.userTimingJsPerformanceTimelinePrefixed = true;
634 }
635 }
636 }
637 /**
638 * Adds an object to our internal Performance Timeline array.
639 *
640 * @param {Object} obj PerformanceEntry
641 */
642 addToPerformanceTimeline = function(obj) {
643 performanceTimeline.push(obj);
644 //
645 // If we insert a measure, its startTime may be out of order
646 // from the rest of the entries because the use can use any
647 // mark as the start time. If so, note we have to sort it before
648 // returning getEntries();
649 //
650 if (obj.entryType === "measure") {
651 performanceTimelineRequiresSort = true;
652 }
653 };
654 /**
655 * Ensures our PT array is in the correct sorted order (by startTime)
656 */
657 var ensurePerformanceTimelineOrder = function() {
658 if (!performanceTimelineRequiresSort) {
659 return;
660 }
661 //
662 // Measures, which may be in this list, may enter the list in
663 // an unsorted order. For example:
664 //
665 // 1. measure("a")
666 // 2. mark("start_mark")
667 // 3. measure("b", "start_mark")
668 // 4. measure("c")
669 // 5. getEntries()
670 //
671 // When calling #5, we should return [a,c,b] because technically the start time
672 // of c is "0" (navigationStart), which will occur before b's start time due to the mark.
673 //
674 performanceTimeline.sort(function(a, b) {
675 return a.startTime - b.startTime;
676 });
677 performanceTimelineRequiresSort = false;
678 };
679 /**
680 * Clears the specified entry types from our timeline array.
681 *
682 * @param {string} entryType Entry type (eg "mark" or "measure")
683 * @param {string} [name] Entry name (optional)
684 */
685 clearEntriesFromPerformanceTimeline = function(entryType, name) {
686 // clear all entries from the perf timeline
687 i = 0;
688 while (i < performanceTimeline.length) {
689 if (performanceTimeline[i].entryType !== entryType) {
690 // unmatched entry type
691 i++;
692 continue;
693 }
694 if (typeof name !== "undefined" && performanceTimeline[i].name !== name) {
695 // unmatched name
696 i++;
697 continue;
698 }
699 // this entry matches our criteria, remove just it
700 performanceTimeline.splice(i, 1);
701 }
702 };
703 if (typeof window.performance.getEntries !== "function" || hasNativeGetEntriesButNotUserTiming) {
704 var origGetEntries = window.performance.getEntries;
705 /**
706 * Gets all entries from the Performance Timeline.
707 * http://www.w3.org/TR/performance-timeline/#dom-performance-getentries
708 *
709 * NOTE: This will only ever return marks and measures.
710 *
711 * @returns {PerformanceEntry[]} Array of PerformanceEntrys
712 */
713 window.performance.getEntries = function() {
714 ensurePerformanceTimelineOrder();
715 // get a copy of all of our entries
716 var entries = performanceTimeline.slice(0);
717 // if there was a native version of getEntries, add that
718 if (hasNativeGetEntriesButNotUserTiming && origGetEntries) {
719 // merge in native
720 Array.prototype.push.apply(entries, origGetEntries.call(window.performance));
721 // sort by startTime
722 entries.sort(function(a, b) {
723 return a.startTime - b.startTime;
724 });
725 }
726 return entries;
727 };
728 }
729 if (typeof window.performance.getEntriesByType !== "function" || hasNativeGetEntriesButNotUserTiming) {
730 var origGetEntriesByType = window.performance.getEntriesByType;
731 /**
732 * Gets all entries from the Performance Timeline of the specified type.
733 * http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbytype
734 *
735 * NOTE: This will only work for marks and measures.
736 *
737 * @param {string} entryType Entry type (eg "mark" or "measure")
738 *
739 * @returns {PerformanceEntry[]} Array of PerformanceEntrys
740 */
741 window.performance.getEntriesByType = function(entryType) {
742 // we only support marks/measures
743 if (typeof entryType === "undefined" || entryType !== "mark" && entryType !== "measure") {
744 if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByType) {
745 // native version exists, forward
746 return origGetEntriesByType.call(window.performance, entryType);
747 }
748 return [];
749 }
750 // see note in ensurePerformanceTimelineOrder() on why this is required
751 if (entryType === "measure") {
752 ensurePerformanceTimelineOrder();
753 }
754 // find all entries of entryType
755 var entries = [];
756 for (i = 0; i < performanceTimeline.length; i++) {
757 if (performanceTimeline[i].entryType === entryType) {
758 entries.push(performanceTimeline[i]);
759 }
760 }
761 return entries;
762 };
763 }
764 if (typeof window.performance.getEntriesByName !== "function" || hasNativeGetEntriesButNotUserTiming) {
765 var origGetEntriesByName = window.performance.getEntriesByName;
766 /**
767 * Gets all entries from the Performance Timeline of the specified
768 * name, and optionally, type.
769 * http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbyname
770 *
771 * NOTE: This will only work for marks and measures.
772 *
773 * @param {string} name Entry name
774 * @param {string} [entryType] Entry type (eg "mark" or "measure")
775 *
776 * @returns {PerformanceEntry[]} Array of PerformanceEntrys
777 */
778 window.performance.getEntriesByName = function(name, entryType) {
779 if (entryType && entryType !== "mark" && entryType !== "measure") {
780 if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByName) {
781 // native version exists, forward
782 return origGetEntriesByName.call(window.performance, name, entryType);
783 }
784 return [];
785 }
786 // see note in ensurePerformanceTimelineOrder() on why this is required
787 if (typeof entryType !== "undefined" && entryType === "measure") {
788 ensurePerformanceTimelineOrder();
789 }
790 // find all entries of the name and (optionally) type
791 var entries = [];
792 for (i = 0; i < performanceTimeline.length; i++) {
793 if (typeof entryType !== "undefined" && performanceTimeline[i].entryType !== entryType) {
794 continue;
795 }
796 if (performanceTimeline[i].name === name) {
797 entries.push(performanceTimeline[i]);
798 }
799 }
800 if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByName) {
801 // merge in native
802 Array.prototype.push.apply(entries, origGetEntriesByName.call(window.performance, name, entryType));
803 // sort by startTime
804 entries.sort(function(a, b) {
805 return a.startTime - b.startTime;
806 });
807 }
808 return entries;
809 };
810 }
811 }
812 //
813 // UserTiming support
814 //
815 if (typeof window.performance.mark !== "function") {
816 window.performance.userTimingJsUserTiming = true;
817 // copy prefixed version over if it exists
818 prefixes = [ "webkit", "moz", "ms" ];
819 methods = [ "mark", "measure", "clearMarks", "clearMeasures" ];
820 for (i = 0; i < methods.length; i++) {
821 for (j = 0; j < prefixes.length; j++) {
822 // prefixed method will likely have an upper-case first letter
823 methodTest = prefixes[j] + methods[i].substr(0, 1).toUpperCase() + methods[i].substr(1);
824 if (typeof window.performance[methodTest] === "function") {
825 window.performance[methods[i]] = window.performance[methodTest];
826 window.performance.userTimingJsUserTimingPrefixed = true;
827 }
828 }
829 }
830 // only used for measure(), to quickly see the latest timestamp of a mark
831 var marks = {};
832 if (typeof window.performance.mark !== "function") {
833 /**
834 * UserTiming mark
835 * http://www.w3.org/TR/user-timing/#dom-performance-mark
836 *
837 * @param {string} markName Mark name
838 */
839 window.performance.mark = function(markName) {
840 var now = window.performance.now();
841 // mark name is required
842 if (typeof markName === "undefined") {
843 throw new SyntaxError("Mark name must be specified");
844 }
845 // mark name can't be a NT timestamp
846 if (window.performance.timing && markName in window.performance.timing) {
847 throw new SyntaxError("Mark name is not allowed");
848 }
849 if (!marks[markName]) {
850 marks[markName] = [];
851 }
852 marks[markName].push(now);
853 // add to perf timeline as well
854 addToPerformanceTimeline({
855 entryType: "mark",
856 name: markName,
857 startTime: now,
858 duration: 0
859 });
860 };
861 }
862 if (typeof window.performance.clearMarks !== "function") {
863 /**
864 * UserTiming clear marks
865 * http://www.w3.org/TR/user-timing/#dom-performance-clearmarks
866 *
867 * @param {string} markName Mark name
868 */
869 window.performance.clearMarks = function(markName) {
870 if (!markName) {
871 // clear all marks
872 marks = {};
873 } else {
874 marks[markName] = [];
875 }
876 clearEntriesFromPerformanceTimeline("mark", markName);
877 };
878 }
879 if (typeof window.performance.measure !== "function") {
880 /**
881 * UserTiming measure
882 * http://www.w3.org/TR/user-timing/#dom-performance-measure
883 *
884 * @param {string} measureName Measure name
885 * @param {string} [startMark] Start mark name
886 * @param {string} [endMark] End mark name
887 */
888 window.performance.measure = function(measureName, startMark, endMark) {
889 var now = window.performance.now();
890 if (typeof measureName === "undefined") {
891 throw new SyntaxError("Measure must be specified");
892 }
893 // if there isn't a startMark, we measure from navigationStart to now
894 if (!startMark) {
895 // add to perf timeline as well
896 addToPerformanceTimeline({
897 entryType: "measure",
898 name: measureName,
899 startTime: 0,
900 duration: now
901 });
902 return;
903 }
904 //
905 // If there is a startMark, check for it first in the NavigationTiming interface,
906 // then check our own marks.
907 //
908 var startMarkTime = 0;
909 if (window.performance.timing && startMark in window.performance.timing) {
910 // mark cannot have a timing of 0
911 if (startMark !== "navigationStart" && window.performance.timing[startMark] === 0) {
912 throw new Error(startMark + " has a timing of 0");
913 }
914 // time is the offset of this mark to navigationStart's time
915 startMarkTime = window.performance.timing[startMark] - window.performance.timing.navigationStart;
916 } else if (startMark in marks) {
917 startMarkTime = marks[startMark][marks[startMark].length - 1];
918 } else {
919 throw new Error(startMark + " mark not found");
920 }
921 //
922 // If there is a endMark, check for it first in the NavigationTiming interface,
923 // then check our own marks.
924 //
925 var endMarkTime = now;
926 if (endMark) {
927 endMarkTime = 0;
928 if (window.performance.timing && endMark in window.performance.timing) {
929 // mark cannot have a timing of 0
930 if (endMark !== "navigationStart" && window.performance.timing[endMark] === 0) {
931 throw new Error(endMark + " has a timing of 0");
932 }
933 // time is the offset of this mark to navigationStart's time
934 endMarkTime = window.performance.timing[endMark] - window.performance.timing.navigationStart;
935 } else if (endMark in marks) {
936 endMarkTime = marks[endMark][marks[endMark].length - 1];
937 } else {
938 throw new Error(endMark + " mark not found");
939 }
940 }
941 // add to our measure array
942 var duration = endMarkTime - startMarkTime;
943 // add to perf timeline as well
944 addToPerformanceTimeline({
945 entryType: "measure",
946 name: measureName,
947 startTime: startMarkTime,
948 duration: duration
949 });
950 };
951 }
952 if (typeof window.performance.clearMeasures !== "function") {
953 /**
954 * UserTiming clear measures
955 * http://www.w3.org/TR/user-timing/#dom-performance-clearmeasures
956 *
957 * @param {string} measureName Measure name
958 */
959 window.performance.clearMeasures = function(measureName) {
960 clearEntriesFromPerformanceTimeline("measure", measureName);
961 };
962 }
963 }
964 //
965 // Export UserTiming to the appropriate location.
966 //
967 // When included directly via a script tag in the browser, we're good as we've been
968 // updating the window.performance object.
969 //
970 if (typeof define === "function" && define.amd) {
971 //
972 // AMD / RequireJS
973 //
974 define([], function() {
975 return window.performance;
976 });
977 } else if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
978 //
979 // Node.js
980 //
981 module.exports = window.performance;
982 }
983})(typeof window !== "undefined" ? window : undefined);
984
985//----------------------------------------------------------------------
986//
987// ECMAScript 5 Polyfills
988//
989//----------------------------------------------------------------------
990//----------------------------------------------------------------------
991// ES5 15.2 Object Objects
992//----------------------------------------------------------------------
993//
994// ES5 15.2.3 Properties of the Object Constructor
995//
996// ES5 15.2.3.2 Object.getPrototypeOf ( O )
997// From http://ejohn.org/blog/objectgetprototypeof/
998// NOTE: won't work for typical function T() {}; T.prototype = {}; new T; case
999// since the constructor property is destroyed.
1000if (!Object.getPrototypeOf) {
1001 Object.getPrototypeOf = function(o) {
1002 if (o !== Object(o)) {
1003 throw TypeError("Object.getPrototypeOf called on non-object");
1004 }
1005 return o.__proto__ || o.constructor.prototype || Object.prototype;
1006 };
1007}
1008
1009// // ES5 15.2.3.3 Object.getOwnPropertyDescriptor ( O, P )
1010// if (typeof Object.getOwnPropertyDescriptor !== "function") {
1011// Object.getOwnPropertyDescriptor = function (o, name) {
1012// if (o !== Object(o)) { throw TypeError(); }
1013// if (o.hasOwnProperty(name)) {
1014// return {
1015// value: o[name],
1016// enumerable: true,
1017// writable: true,
1018// configurable: true
1019// };
1020// }
1021// };
1022// }
1023// ES5 15.2.3.4 Object.getOwnPropertyNames ( O )
1024if (typeof Object.getOwnPropertyNames !== "function") {
1025 Object.getOwnPropertyNames = function(o) {
1026 if (o !== Object(o)) {
1027 throw TypeError("Object.getOwnPropertyNames called on non-object");
1028 }
1029 var props = [], p;
1030 for (p in o) {
1031 if (Object.prototype.hasOwnProperty.call(o, p)) {
1032 props.push(p);
1033 }
1034 }
1035 return props;
1036 };
1037}
1038
1039// ES5 15.2.3.5 Object.create ( O [, Properties] )
1040if (typeof Object.create !== "function") {
1041 Object.create = function(prototype, properties) {
1042 if (typeof prototype !== "object") {
1043 throw TypeError();
1044 }
1045 function Ctor() {}
1046 Ctor.prototype = prototype;
1047 var o = new Ctor();
1048 if (prototype) {
1049 o.constructor = Ctor;
1050 }
1051 if (properties !== undefined) {
1052 if (properties !== Object(properties)) {
1053 throw TypeError();
1054 }
1055 Object.defineProperties(o, properties);
1056 }
1057 return o;
1058 };
1059}
1060
1061// ES 15.2.3.6 Object.defineProperty ( O, P, Attributes )
1062// Partial support for most common case - getters, setters, and values
1063(function() {
1064 if (!Object.defineProperty || !function() {
1065 try {
1066 Object.defineProperty({}, "x", {});
1067 return true;
1068 } catch (e) {
1069 return false;
1070 }
1071 }()) {
1072 var orig = Object.defineProperty;
1073 Object.defineProperty = function(o, prop, desc) {
1074 // In IE8 try built-in implementation for defining properties on DOM prototypes.
1075 if (orig) {
1076 try {
1077 return orig(o, prop, desc);
1078 } catch (e) {}
1079 }
1080 if (o !== Object(o)) {
1081 throw TypeError("Object.defineProperty called on non-object");
1082 }
1083 if (Object.prototype.__defineGetter__ && "get" in desc) {
1084 Object.prototype.__defineGetter__.call(o, prop, desc.get);
1085 }
1086 if (Object.prototype.__defineSetter__ && "set" in desc) {
1087 Object.prototype.__defineSetter__.call(o, prop, desc.set);
1088 }
1089 if ("value" in desc) {
1090 o[prop] = desc.value;
1091 }
1092 return o;
1093 };
1094 }
1095})();
1096
1097// ES 15.2.3.7 Object.defineProperties ( O, Properties )
1098if (typeof Object.defineProperties !== "function") {
1099 Object.defineProperties = function(o, properties) {
1100 if (o !== Object(o)) {
1101 throw TypeError("Object.defineProperties called on non-object");
1102 }
1103 var name;
1104 for (name in properties) {
1105 if (Object.prototype.hasOwnProperty.call(properties, name)) {
1106 Object.defineProperty(o, name, properties[name]);
1107 }
1108 }
1109 return o;
1110 };
1111}
1112
1113// ES5 15.2.3.14 Object.keys ( O )
1114// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
1115if (!Object.keys) {
1116 Object.keys = function(o) {
1117 if (o !== Object(o)) {
1118 throw TypeError("Object.keys called on non-object");
1119 }
1120 var ret = [], p;
1121 for (p in o) {
1122 if (Object.prototype.hasOwnProperty.call(o, p)) {
1123 ret.push(p);
1124 }
1125 }
1126 return ret;
1127 };
1128}
1129
1130//----------------------------------------------------------------------
1131// ES5 15.3 Function Objects
1132//----------------------------------------------------------------------
1133//
1134// ES5 15.3.4 Properties of the Function Prototype Object
1135//
1136// ES5 15.3.4.5 Function.prototype.bind ( thisArg [, arg1 [, arg2, ... ]] )
1137// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
1138if (!Function.prototype.bind) {
1139 Function.prototype.bind = function(o) {
1140 if (typeof this !== "function") {
1141 throw TypeError("Bind must be called on a function");
1142 }
1143 var args = Array.prototype.slice.call(arguments, 1), self = this, nop = function() {}, bound = function() {
1144 return self.apply(this instanceof nop ? this : o, args.concat(Array.prototype.slice.call(arguments)));
1145 };
1146 if (this.prototype) nop.prototype = this.prototype;
1147 bound.prototype = new nop();
1148 return bound;
1149 };
1150}
1151
1152//----------------------------------------------------------------------
1153// ES5 15.4 Array Objects
1154//----------------------------------------------------------------------
1155//
1156// ES5 15.4.3 Properties of the Array Constructor
1157//
1158// ES5 15.4.3.2 Array.isArray ( arg )
1159// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/isArray
1160Array.isArray = Array.isArray || function(o) {
1161 return Boolean(o && Object.prototype.toString.call(Object(o)) === "[object Array]");
1162};
1163
1164//
1165// ES5 15.4.4 Properties of the Array Prototype Object
1166//
1167// ES5 15.4.4.14 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
1168// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
1169if (!Array.prototype.indexOf) {
1170 Array.prototype.indexOf = function(searchElement) {
1171 if (this === void 0 || this === null) {
1172 throw TypeError();
1173 }
1174 var t = Object(this);
1175 var len = t.length >>> 0;
1176 if (len === 0) {
1177 return -1;
1178 }
1179 var n = 0;
1180 if (arguments.length > 0) {
1181 n = Number(arguments[1]);
1182 if (isNaN(n)) {
1183 n = 0;
1184 } else if (n !== 0 && n !== 1 / 0 && n !== -(1 / 0)) {
1185 n = (n > 0 || -1) * Math.floor(Math.abs(n));
1186 }
1187 }
1188 if (n >= len) {
1189 return -1;
1190 }
1191 var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
1192 for (;k < len; k++) {
1193 if (k in t && t[k] === searchElement) {
1194 return k;
1195 }
1196 }
1197 return -1;
1198 };
1199}
1200
1201// ES5 15.4.4.15 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
1202// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf
1203if (!Array.prototype.lastIndexOf) {
1204 Array.prototype.lastIndexOf = function(searchElement) {
1205 if (this === void 0 || this === null) {
1206 throw TypeError();
1207 }
1208 var t = Object(this);
1209 var len = t.length >>> 0;
1210 if (len === 0) {
1211 return -1;
1212 }
1213 var n = len;
1214 if (arguments.length > 1) {
1215 n = Number(arguments[1]);
1216 if (n !== n) {
1217 n = 0;
1218 } else if (n !== 0 && n !== 1 / 0 && n !== -(1 / 0)) {
1219 n = (n > 0 || -1) * Math.floor(Math.abs(n));
1220 }
1221 }
1222 var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);
1223 for (;k >= 0; k--) {
1224 if (k in t && t[k] === searchElement) {
1225 return k;
1226 }
1227 }
1228 return -1;
1229 };
1230}
1231
1232// ES5 15.4.4.16 Array.prototype.every ( callbackfn [ , thisArg ] )
1233// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
1234if (!Array.prototype.every) {
1235 Array.prototype.every = function(fun) {
1236 if (this === void 0 || this === null) {
1237 throw TypeError();
1238 }
1239 var t = Object(this);
1240 var len = t.length >>> 0;
1241 if (typeof fun !== "function") {
1242 throw TypeError();
1243 }
1244 var thisp = arguments[1], i;
1245 for (i = 0; i < len; i++) {
1246 if (i in t && !fun.call(thisp, t[i], i, t)) {
1247 return false;
1248 }
1249 }
1250 return true;
1251 };
1252}
1253
1254// ES5 15.4.4.17 Array.prototype.some ( callbackfn [ , thisArg ] )
1255// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
1256if (!Array.prototype.some) {
1257 Array.prototype.some = function(fun) {
1258 if (this === void 0 || this === null) {
1259 throw TypeError();
1260 }
1261 var t = Object(this);
1262 var len = t.length >>> 0;
1263 if (typeof fun !== "function") {
1264 throw TypeError();
1265 }
1266 var thisp = arguments[1], i;
1267 for (i = 0; i < len; i++) {
1268 if (i in t && fun.call(thisp, t[i], i, t)) {
1269 return true;
1270 }
1271 }
1272 return false;
1273 };
1274}
1275
1276// ES5 15.4.4.18 Array.prototype.forEach ( callbackfn [ , thisArg ] )
1277// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
1278if (!Array.prototype.forEach) {
1279 Array.prototype.forEach = function(fun) {
1280 if (this === void 0 || this === null) {
1281 throw TypeError();
1282 }
1283 var t = Object(this);
1284 var len = t.length >>> 0;
1285 if (typeof fun !== "function") {
1286 throw TypeError();
1287 }
1288 var thisp = arguments[1], i;
1289 for (i = 0; i < len; i++) {
1290 if (i in t) {
1291 fun.call(thisp, t[i], i, t);
1292 }
1293 }
1294 };
1295}
1296
1297// ES5 15.4.4.19 Array.prototype.map ( callbackfn [ , thisArg ] )
1298// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Map
1299if (!Array.prototype.map) {
1300 Array.prototype.map = function(fun) {
1301 if (this === void 0 || this === null) {
1302 throw TypeError();
1303 }
1304 var t = Object(this);
1305 var len = t.length >>> 0;
1306 if (typeof fun !== "function") {
1307 throw TypeError();
1308 }
1309 var res = [];
1310 res.length = len;
1311 var thisp = arguments[1], i;
1312 for (i = 0; i < len; i++) {
1313 if (i in t) {
1314 res[i] = fun.call(thisp, t[i], i, t);
1315 }
1316 }
1317 return res;
1318 };
1319}
1320
1321// ES5 15.4.4.20 Array.prototype.filter ( callbackfn [ , thisArg ] )
1322// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Filter
1323if (!Array.prototype.filter) {
1324 Array.prototype.filter = function(fun) {
1325 if (this === void 0 || this === null) {
1326 throw TypeError();
1327 }
1328 var t = Object(this);
1329 var len = t.length >>> 0;
1330 if (typeof fun !== "function") {
1331 throw TypeError();
1332 }
1333 var res = [];
1334 var thisp = arguments[1], i;
1335 for (i = 0; i < len; i++) {
1336 if (i in t) {
1337 var val = t[i];
1338 // in case fun mutates this
1339 if (fun.call(thisp, val, i, t)) {
1340 res.push(val);
1341 }
1342 }
1343 }
1344 return res;
1345 };
1346}
1347
1348// ES5 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue ] )
1349// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Reduce
1350if (!Array.prototype.reduce) {
1351 Array.prototype.reduce = function(fun) {
1352 if (this === void 0 || this === null) {
1353 throw TypeError();
1354 }
1355 var t = Object(this);
1356 var len = t.length >>> 0;
1357 if (typeof fun !== "function") {
1358 throw TypeError();
1359 }
1360 // no value to return if no initial value and an empty array
1361 if (len === 0 && arguments.length === 1) {
1362 throw TypeError();
1363 }
1364 var k = 0;
1365 var accumulator;
1366 if (arguments.length >= 2) {
1367 accumulator = arguments[1];
1368 } else {
1369 do {
1370 if (k in t) {
1371 accumulator = t[k++];
1372 break;
1373 }
1374 // if array contains no values, no initial value to return
1375 if (++k >= len) {
1376 throw TypeError();
1377 }
1378 } while (true);
1379 }
1380 while (k < len) {
1381 if (k in t) {
1382 accumulator = fun.call(undefined, accumulator, t[k], k, t);
1383 }
1384 k++;
1385 }
1386 return accumulator;
1387 };
1388}
1389
1390// ES5 15.4.4.22 Array.prototype.reduceRight ( callbackfn [, initialValue ] )
1391// From https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/ReduceRight
1392if (!Array.prototype.reduceRight) {
1393 Array.prototype.reduceRight = function(callbackfn) {
1394 if (this === void 0 || this === null) {
1395 throw TypeError();
1396 }
1397 var t = Object(this);
1398 var len = t.length >>> 0;
1399 if (typeof callbackfn !== "function") {
1400 throw TypeError();
1401 }
1402 // no value to return if no initial value, empty array
1403 if (len === 0 && arguments.length === 1) {
1404 throw TypeError();
1405 }
1406 var k = len - 1;
1407 var accumulator;
1408 if (arguments.length >= 2) {
1409 accumulator = arguments[1];
1410 } else {
1411 do {
1412 if (k in this) {
1413 accumulator = this[k--];
1414 break;
1415 }
1416 // if array contains no values, no initial value to return
1417 if (--k < 0) {
1418 throw TypeError();
1419 }
1420 } while (true);
1421 }
1422 while (k >= 0) {
1423 if (k in t) {
1424 accumulator = callbackfn.call(undefined, accumulator, t[k], k, t);
1425 }
1426 k--;
1427 }
1428 return accumulator;
1429 };
1430}
1431
1432//----------------------------------------------------------------------
1433// ES5 15.5 String Objects
1434//----------------------------------------------------------------------
1435//
1436// ES5 15.5.4 Properties of the String Prototype Object
1437//
1438// ES5 15.5.4.20 String.prototype.trim()
1439if (!String.prototype.trim) {
1440 String.prototype.trim = function() {
1441 return String(this).replace(/^\s+/, "").replace(/\s+$/, "");
1442 };
1443}
1444
1445//----------------------------------------------------------------------
1446// ES5 15.9 Date Objects
1447//----------------------------------------------------------------------
1448//
1449// ES 15.9.4 Properties of the Date Constructor
1450//
1451// ES5 15.9.4.4 Date.now ( )
1452// From https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Date/now
1453if (!Date.now) {
1454 Date.now = function now() {
1455 return Number(new Date());
1456 };
1457}
1458
1459//
1460// ES5 15.9.5 Properties of the Date Prototype Object
1461//
1462// ES5 15.9.4.43 Date.prototype.toISOString ( )
1463// Inspired by http://www.json.org/json2.js
1464if (!Date.prototype.toISOString) {
1465 Date.prototype.toISOString = function() {
1466 function pad2(n) {
1467 return ("00" + n).slice(-2);
1468 }
1469 function pad3(n) {
1470 return ("000" + n).slice(-3);
1471 }
1472 return this.getUTCFullYear() + "-" + pad2(this.getUTCMonth() + 1) + "-" + pad2(this.getUTCDate()) + "T" + pad2(this.getUTCHours()) + ":" + pad2(this.getUTCMinutes()) + ":" + pad2(this.getUTCSeconds()) + "." + pad3(this.getUTCMilliseconds()) + "Z";
1473 };
1474}
\No newline at end of file