mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #6340 from imgntn/usertiming
Adapt UserTiming library and Add Example
This commit is contained in:
commit
ceba1c4b02
2 changed files with 565 additions and 0 deletions
547
examples/libraries/usertiming.js
Normal file
547
examples/libraries/usertiming.js
Normal file
|
@ -0,0 +1,547 @@
|
|||
//
|
||||
// usertiming.js
|
||||
//
|
||||
// A polyfill for UserTiming (http://www.w3.org/TR/user-timing/)
|
||||
//
|
||||
// Copyright 2013 Nic Jansma
|
||||
// http://nicj.net
|
||||
//
|
||||
// https://github.com/nicjansma/usertiming.js
|
||||
//
|
||||
// Licensed under the MIT license
|
||||
//
|
||||
// Adapted for High Fidelity by James B. Pollack @imgntn on 11/6/2015
|
||||
|
||||
function userTiming() {
|
||||
"use strict";
|
||||
|
||||
// allow running in Node.js environment
|
||||
if (typeof window === "undefined") {
|
||||
window = {};
|
||||
}
|
||||
|
||||
// prepare base perf object
|
||||
if (typeof window.performance === "undefined") {
|
||||
window.performance = {};
|
||||
}
|
||||
|
||||
// We need to keep a global reference to the window.performance object to
|
||||
// prevent any added properties from being garbage-collected in Safari 8.
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=137407
|
||||
window._perfRefForUserTimingPolyfill = window.performance;
|
||||
|
||||
//
|
||||
// Note what we shimmed
|
||||
//
|
||||
window.performance.userTimingJsNow = false;
|
||||
window.performance.userTimingJsNowPrefixed = false;
|
||||
window.performance.userTimingJsUserTiming = false;
|
||||
window.performance.userTimingJsUserTimingPrefixed = false;
|
||||
window.performance.userTimingJsPerformanceTimeline = false;
|
||||
window.performance.userTimingJsPerformanceTimelinePrefixed = false;
|
||||
|
||||
// for prefixed support
|
||||
var prefixes = [];
|
||||
var methods = [];
|
||||
var methodTest = null;
|
||||
var i, j;
|
||||
|
||||
//
|
||||
// window.performance.now() shim
|
||||
// http://www.w3.org/TR/hr-time/
|
||||
//
|
||||
if (typeof window.performance.now !== "function") {
|
||||
window.performance.userTimingJsNow = true;
|
||||
|
||||
// copy prefixed version over if it exists
|
||||
methods = ["webkitNow", "msNow", "mozNow"];
|
||||
|
||||
for (i = 0; i < methods.length; i++) {
|
||||
if (typeof window.performance[methods[i]] === "function") {
|
||||
window.performance.now = window.performance[methods[i]];
|
||||
|
||||
window.performance.userTimingJsNowPrefixed = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// now() should be a DOMHighResTimeStamp, which is defined as being a time relative
|
||||
// to navigationStart of the PerformanceTiming (PT) interface. If this browser supports
|
||||
// PT, use that as our relative start. Otherwise, use "now" as the start and all other
|
||||
// now() calls will be relative to our initialization.
|
||||
//
|
||||
|
||||
var nowOffset = +(new Date());
|
||||
if (window.performance.timing && window.performance.timing.navigationStart) {
|
||||
nowOffset = window.performance.timing.navigationStart;
|
||||
}
|
||||
|
||||
if (typeof window.performance.now !== "function") {
|
||||
// No browser support, fall back to Date.now
|
||||
if (Date.now) {
|
||||
window.performance.now = function() {
|
||||
return Date.now() - nowOffset;
|
||||
};
|
||||
} else {
|
||||
// no Date.now support, get the time from new Date()
|
||||
window.performance.now = function() {
|
||||
return +(new Date()) - nowOffset;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// PerformanceTimeline (PT) shims
|
||||
// http://www.w3.org/TR/performance-timeline/
|
||||
//
|
||||
|
||||
/**
|
||||
* Adds an object to our internal Performance Timeline array.
|
||||
*
|
||||
* Will be blank if the environment supports PT.
|
||||
*/
|
||||
var addToPerformanceTimeline = function() {};
|
||||
|
||||
/**
|
||||
* Clears the specified entry types from our timeline array.
|
||||
*
|
||||
* Will be blank if the environment supports PT.
|
||||
*/
|
||||
var clearEntriesFromPerformanceTimeline = function() {};
|
||||
|
||||
// performance timeline array
|
||||
var performanceTimeline = [];
|
||||
|
||||
// whether or not the timeline will require sort on getEntries()
|
||||
var performanceTimelineRequiresSort = false;
|
||||
|
||||
// whether or not ResourceTiming is natively supported but UserTiming is
|
||||
// not (eg Firefox 35)
|
||||
var hasNativeGetEntriesButNotUserTiming = false;
|
||||
|
||||
//
|
||||
// If getEntries() and mark() aren't defined, we'll assume
|
||||
// we have to shim at least some PT functions.
|
||||
//
|
||||
if (typeof window.performance.getEntries !== "function" ||
|
||||
typeof window.performance.mark !== "function") {
|
||||
|
||||
if (typeof window.performance.getEntries === "function" &&
|
||||
typeof window.performance.mark !== "function") {
|
||||
hasNativeGetEntriesButNotUserTiming = true;
|
||||
}
|
||||
|
||||
window.performance.userTimingJsPerformanceTimeline = true;
|
||||
|
||||
// copy prefixed version over if it exists
|
||||
prefixes = ["webkit", "moz"];
|
||||
methods = ["getEntries", "getEntriesByName", "getEntriesByType"];
|
||||
|
||||
for (i = 0; i < methods.length; i++) {
|
||||
for (j = 0; j < prefixes.length; j++) {
|
||||
// prefixed method will likely have an upper-case first letter
|
||||
methodTest = prefixes[j] + methods[i].substr(0, 1).toUpperCase() + methods[i].substr(1);
|
||||
|
||||
if (typeof window.performance[methodTest] === "function") {
|
||||
window.performance[methods[i]] = window.performance[methodTest];
|
||||
|
||||
window.performance.userTimingJsPerformanceTimelinePrefixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an object to our internal Performance Timeline array.
|
||||
*
|
||||
* @param {Object} obj PerformanceEntry
|
||||
*/
|
||||
addToPerformanceTimeline = function(obj) {
|
||||
performanceTimeline.push(obj);
|
||||
|
||||
//
|
||||
// If we insert a measure, its startTime may be out of order
|
||||
// from the rest of the entries because the use can use any
|
||||
// mark as the start time. If so, note we have to sort it before
|
||||
// returning getEntries();
|
||||
//
|
||||
if (obj.entryType === "measure") {
|
||||
performanceTimelineRequiresSort = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensures our PT array is in the correct sorted order (by startTime)
|
||||
*/
|
||||
var ensurePerformanceTimelineOrder = function() {
|
||||
if (!performanceTimelineRequiresSort) {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Measures, which may be in this list, may enter the list in
|
||||
// an unsorted order. For example:
|
||||
//
|
||||
// 1. measure("a")
|
||||
// 2. mark("start_mark")
|
||||
// 3. measure("b", "start_mark")
|
||||
// 4. measure("c")
|
||||
// 5. getEntries()
|
||||
//
|
||||
// When calling #5, we should return [a,c,b] because technically the start time
|
||||
// of c is "0" (navigationStart), which will occur before b's start time due to the mark.
|
||||
//
|
||||
performanceTimeline.sort(function(a, b) {
|
||||
return a.startTime - b.startTime;
|
||||
});
|
||||
|
||||
performanceTimelineRequiresSort = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the specified entry types from our timeline array.
|
||||
*
|
||||
* @param {string} entryType Entry type (eg "mark" or "measure")
|
||||
* @param {string} [name] Entry name (optional)
|
||||
*/
|
||||
clearEntriesFromPerformanceTimeline = function(entryType, name) {
|
||||
// clear all entries from the perf timeline
|
||||
i = 0;
|
||||
while (i < performanceTimeline.length) {
|
||||
if (performanceTimeline[i].entryType !== entryType) {
|
||||
// unmatched entry type
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof name !== "undefined" && performanceTimeline[i].name !== name) {
|
||||
// unmatched name
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// this entry matches our criteria, remove just it
|
||||
performanceTimeline.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof window.performance.getEntries !== "function" || hasNativeGetEntriesButNotUserTiming) {
|
||||
var origGetEntries = window.performance.getEntries;
|
||||
|
||||
/**
|
||||
* Gets all entries from the Performance Timeline.
|
||||
* http://www.w3.org/TR/performance-timeline/#dom-performance-getentries
|
||||
*
|
||||
* NOTE: This will only ever return marks and measures.
|
||||
*
|
||||
* @returns {PerformanceEntry[]} Array of PerformanceEntrys
|
||||
*/
|
||||
window.performance.getEntries = function() {
|
||||
ensurePerformanceTimelineOrder();
|
||||
|
||||
// get a copy of all of our entries
|
||||
var entries = performanceTimeline.slice(0);
|
||||
|
||||
// if there was a native version of getEntries, add that
|
||||
if (hasNativeGetEntriesButNotUserTiming && origGetEntries) {
|
||||
// merge in native
|
||||
Array.prototype.push.apply(entries, origGetEntries.call(window.performance));
|
||||
|
||||
// sort by startTime
|
||||
entries.sort(function(a, b) {
|
||||
return a.startTime - b.startTime;
|
||||
});
|
||||
}
|
||||
|
||||
return entries;
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.performance.getEntriesByType !== "function" || hasNativeGetEntriesButNotUserTiming) {
|
||||
var origGetEntriesByType = window.performance.getEntriesByType;
|
||||
|
||||
/**
|
||||
* Gets all entries from the Performance Timeline of the specified type.
|
||||
* http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbytype
|
||||
*
|
||||
* NOTE: This will only work for marks and measures.
|
||||
*
|
||||
* @param {string} entryType Entry type (eg "mark" or "measure")
|
||||
*
|
||||
* @returns {PerformanceEntry[]} Array of PerformanceEntrys
|
||||
*/
|
||||
window.performance.getEntriesByType = function(entryType) {
|
||||
// we only support marks/measures
|
||||
if (typeof entryType === "undefined" ||
|
||||
(entryType !== "mark" && entryType !== "measure")) {
|
||||
|
||||
if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByType) {
|
||||
// native version exists, forward
|
||||
return origGetEntriesByType.call(window.performance, entryType);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// see note in ensurePerformanceTimelineOrder() on why this is required
|
||||
if (entryType === "measure") {
|
||||
ensurePerformanceTimelineOrder();
|
||||
}
|
||||
|
||||
// find all entries of entryType
|
||||
var entries = [];
|
||||
for (i = 0; i < performanceTimeline.length; i++) {
|
||||
if (performanceTimeline[i].entryType === entryType) {
|
||||
entries.push(performanceTimeline[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.performance.getEntriesByName !== "function" || hasNativeGetEntriesButNotUserTiming) {
|
||||
var origGetEntriesByName = window.performance.getEntriesByName;
|
||||
|
||||
/**
|
||||
* Gets all entries from the Performance Timeline of the specified
|
||||
* name, and optionally, type.
|
||||
* http://www.w3.org/TR/performance-timeline/#dom-performance-getentriesbyname
|
||||
*
|
||||
* NOTE: This will only work for marks and measures.
|
||||
*
|
||||
* @param {string} name Entry name
|
||||
* @param {string} [entryType] Entry type (eg "mark" or "measure")
|
||||
*
|
||||
* @returns {PerformanceEntry[]} Array of PerformanceEntrys
|
||||
*/
|
||||
window.performance.getEntriesByName = function(name, entryType) {
|
||||
if (entryType && entryType !== "mark" && entryType !== "measure") {
|
||||
if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByName) {
|
||||
// native version exists, forward
|
||||
return origGetEntriesByName.call(window.performance, name, entryType);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// see note in ensurePerformanceTimelineOrder() on why this is required
|
||||
if (typeof entryType !== "undefined" && entryType === "measure") {
|
||||
ensurePerformanceTimelineOrder();
|
||||
}
|
||||
|
||||
// find all entries of the name and (optionally) type
|
||||
var entries = [];
|
||||
for (i = 0; i < performanceTimeline.length; i++) {
|
||||
if (typeof entryType !== "undefined" &&
|
||||
performanceTimeline[i].entryType !== entryType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (performanceTimeline[i].name === name) {
|
||||
entries.push(performanceTimeline[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNativeGetEntriesButNotUserTiming && origGetEntriesByName) {
|
||||
// merge in native
|
||||
Array.prototype.push.apply(entries, origGetEntriesByName.call(window.performance, name, entryType));
|
||||
|
||||
// sort by startTime
|
||||
entries.sort(function(a, b) {
|
||||
return a.startTime - b.startTime;
|
||||
});
|
||||
}
|
||||
|
||||
return entries;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// UserTiming support
|
||||
//
|
||||
if (typeof window.performance.mark !== "function") {
|
||||
window.performance.userTimingJsUserTiming = true;
|
||||
|
||||
// copy prefixed version over if it exists
|
||||
prefixes = ["webkit", "moz", "ms"];
|
||||
methods = ["mark", "measure", "clearMarks", "clearMeasures"];
|
||||
|
||||
for (i = 0; i < methods.length; i++) {
|
||||
for (j = 0; j < prefixes.length; j++) {
|
||||
// prefixed method will likely have an upper-case first letter
|
||||
methodTest = prefixes[j] + methods[i].substr(0, 1).toUpperCase() + methods[i].substr(1);
|
||||
|
||||
if (typeof window.performance[methodTest] === "function") {
|
||||
window.performance[methods[i]] = window.performance[methodTest];
|
||||
|
||||
window.performance.userTimingJsUserTimingPrefixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only used for measure(), to quickly see the latest timestamp of a mark
|
||||
var marks = {};
|
||||
|
||||
if (typeof window.performance.mark !== "function") {
|
||||
/**
|
||||
* UserTiming mark
|
||||
* http://www.w3.org/TR/user-timing/#dom-performance-mark
|
||||
*
|
||||
* @param {string} markName Mark name
|
||||
*/
|
||||
window.performance.mark = function(markName) {
|
||||
var now = window.performance.now();
|
||||
|
||||
// mark name is required
|
||||
if (typeof markName === "undefined") {
|
||||
throw new SyntaxError("Mark name must be specified");
|
||||
}
|
||||
|
||||
// mark name can't be a NT timestamp
|
||||
if (window.performance.timing && markName in window.performance.timing) {
|
||||
throw new SyntaxError("Mark name is not allowed");
|
||||
}
|
||||
|
||||
if (!marks[markName]) {
|
||||
marks[markName] = [];
|
||||
}
|
||||
|
||||
marks[markName].push(now);
|
||||
|
||||
// add to perf timeline as well
|
||||
addToPerformanceTimeline({
|
||||
entryType: "mark",
|
||||
name: markName,
|
||||
startTime: now,
|
||||
duration: 0
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.performance.clearMarks !== "function") {
|
||||
/**
|
||||
* UserTiming clear marks
|
||||
* http://www.w3.org/TR/user-timing/#dom-performance-clearmarks
|
||||
*
|
||||
* @param {string} markName Mark name
|
||||
*/
|
||||
window.performance.clearMarks = function(markName) {
|
||||
if (!markName) {
|
||||
// clear all marks
|
||||
marks = {};
|
||||
} else {
|
||||
marks[markName] = [];
|
||||
}
|
||||
|
||||
clearEntriesFromPerformanceTimeline("mark", markName);
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.performance.measure !== "function") {
|
||||
/**
|
||||
* UserTiming measure
|
||||
* http://www.w3.org/TR/user-timing/#dom-performance-measure
|
||||
*
|
||||
* @param {string} measureName Measure name
|
||||
* @param {string} [startMark] Start mark name
|
||||
* @param {string} [endMark] End mark name
|
||||
*/
|
||||
window.performance.measure = function(measureName, startMark, endMark) {
|
||||
var now = window.performance.now();
|
||||
|
||||
if (typeof measureName === "undefined") {
|
||||
throw new SyntaxError("Measure must be specified");
|
||||
}
|
||||
|
||||
// if there isn't a startMark, we measure from navigationStart to now
|
||||
if (!startMark) {
|
||||
// add to perf timeline as well
|
||||
addToPerformanceTimeline({
|
||||
entryType: "measure",
|
||||
name: measureName,
|
||||
startTime: 0,
|
||||
duration: now
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// If there is a startMark, check for it first in the NavigationTiming interface,
|
||||
// then check our own marks.
|
||||
//
|
||||
var startMarkTime = 0;
|
||||
if (window.performance.timing && startMark in window.performance.timing) {
|
||||
// mark cannot have a timing of 0
|
||||
if (startMark !== "navigationStart" && window.performance.timing[startMark] === 0) {
|
||||
throw new Error(startMark + " has a timing of 0");
|
||||
}
|
||||
|
||||
// time is the offset of this mark to navigationStart's time
|
||||
startMarkTime = window.performance.timing[startMark] - window.performance.timing.navigationStart;
|
||||
} else if (startMark in marks) {
|
||||
startMarkTime = marks[startMark][marks[startMark].length - 1];
|
||||
} else {
|
||||
throw new Error(startMark + " mark not found");
|
||||
}
|
||||
|
||||
//
|
||||
// If there is a endMark, check for it first in the NavigationTiming interface,
|
||||
// then check our own marks.
|
||||
//
|
||||
var endMarkTime = now;
|
||||
|
||||
if (endMark) {
|
||||
endMarkTime = 0;
|
||||
|
||||
if (window.performance.timing && endMark in window.performance.timing) {
|
||||
// mark cannot have a timing of 0
|
||||
if (endMark !== "navigationStart" && window.performance.timing[endMark] === 0) {
|
||||
throw new Error(endMark + " has a timing of 0");
|
||||
}
|
||||
|
||||
// time is the offset of this mark to navigationStart's time
|
||||
endMarkTime = window.performance.timing[endMark] - window.performance.timing.navigationStart;
|
||||
} else if (endMark in marks) {
|
||||
endMarkTime = marks[endMark][marks[endMark].length - 1];
|
||||
} else {
|
||||
throw new Error(endMark + " mark not found");
|
||||
}
|
||||
}
|
||||
|
||||
// add to our measure array
|
||||
var duration = endMarkTime - startMarkTime;
|
||||
|
||||
// add to perf timeline as well
|
||||
addToPerformanceTimeline({
|
||||
entryType: "measure",
|
||||
name: measureName,
|
||||
startTime: startMarkTime,
|
||||
duration: duration
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof window.performance.clearMeasures !== "function") {
|
||||
/**
|
||||
* UserTiming clear measures
|
||||
* http://www.w3.org/TR/user-timing/#dom-performance-clearmeasures
|
||||
*
|
||||
* @param {string} measureName Measure name
|
||||
*/
|
||||
window.performance.clearMeasures = function(measureName) {
|
||||
clearEntriesFromPerformanceTimeline("measure", measureName);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return window
|
||||
}
|
||||
|
||||
loadUserTiming = function() {
|
||||
return userTiming();
|
||||
}
|
18
examples/libraries/usertimingExample.js
Normal file
18
examples/libraries/usertimingExample.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
Script.include('usertiming.js');
|
||||
var timing = loadUserTiming();
|
||||
//set a mark
|
||||
timing.performance.mark('firstMark');
|
||||
|
||||
//do something that takes time -- we're just going to set a timeout here as an example
|
||||
|
||||
Script.setTimeout(function() {
|
||||
//and set another mark
|
||||
timing.performance.mark('secondMark');
|
||||
|
||||
//measure time between marks (first parameter is a name for the measurement)
|
||||
timing.performance.measure('howlong', 'firstMark', 'secondMark');
|
||||
|
||||
//you can also get the marks by changing the type
|
||||
var measures = timing.performance.getEntriesByType('measure');
|
||||
print('measures:::' + JSON.stringify(measures))
|
||||
}, 1000)
|
Loading…
Reference in a new issue