fedtools-utilities
Version:
Set of utilites for fedtools within nodejs
360 lines (313 loc) • 8.75 kB
JavaScript
/**
* Node polyfill for UserTiming (http://www.w3.org/TR/user-timing/)
*
* Heavily inspired by:
* Copyright 2013 Nic Jansma
* http://nicj.net
* https://github.com/nicjansma/usertiming.js
*
* Adapted to:
* - only support nodejs
* - pass our eslint rules
*
* Example:
*
* performance.mark('mark-1');
* // do something lenghty
* performance.mark('mark-2');
* // do something else lenghty too
* performance.mark('mark-3');
*
* performance.measure('perf1', 'mark-1', 'mark-2');
* performance.measure('perf2', 'mark-2', 'mark-3');
* performance.measure('total', 'mark-1', 'mark-3');
*
* // now you can find different performance mesures by their names:
* console.log(performance.getEntriesByName('total'));
* [{ entryType: 'measure',
name: 'total',
startTime: 0,
duration: 284.568687 }]
*
*/
var
performance = {},
marks = {},
addToPerformanceTimeline,
clearEntriesFromPerformanceTimeline,
ensurePerformanceTimelineOrder,
performanceTimeline = [],
performanceTimelineRequiresSort = false,
nowOffset = process.hrtime();
/**
/* window.performance.now() shim
/* http://www.w3.org/TR/hr-time/
*/
performance.now = function () {
var
MILLI = 1000,
NANO = 1000000,
time = process.hrtime(nowOffset);
return time[0] * MILLI + time[1] / NANO;
};
/**
/* PerformanceTimeline (PT) shims
/* http://www.w3.org/TR/performance-timeline/
*/
/**
* Adds an object to our internal Performance Timeline array.
*/
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 user 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)
*/
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
var 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 it
performanceTimeline.splice(i, 1);
}
};
/**
* 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
*/
performance.getEntries = function () {
var
entries;
ensurePerformanceTimelineOrder();
// get a copy of all of our entries
entries = performanceTimeline.slice(0);
return entries;
};
/**
* 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
*/
performance.getEntriesByType = function (entryType) {
var
i = 0,
len,
entries = [];
// we only support marks/measures
if (typeof entryType === 'undefined' ||
(entryType !== 'mark' && entryType !== 'measure')) {
return [];
}
// see note in ensurePerformanceTimelineOrder() on why this is required
if (entryType === 'measure') {
ensurePerformanceTimelineOrder();
}
// find all entries of entryType
len = performanceTimeline.length;
for (i = 0; i < len; i++) {
if (performanceTimeline[i].entryType === entryType) {
entries.push(performanceTimeline[i]);
}
}
return entries;
};
/**
* 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
*/
performance.getEntriesByName = function (name, entryType) {
var
i = 0,
len,
entries = [];
if (entryType && entryType !== 'mark' && entryType !== 'measure') {
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
len = performanceTimeline.length;
for (i = 0; i < len; i++) {
if (typeof entryType !== 'undefined' &&
performanceTimeline[i].entryType !== entryType) {
continue;
}
if (performanceTimeline[i].name === name) {
entries.push(performanceTimeline[i]);
}
}
return entries;
};
/**
* UserTiming mark
* http://www.w3.org/TR/user-timing/#dom-performance-mark
*
* @param {string} markName Mark name
*/
performance.mark = function (markName) {
var
now = 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 (performance.timing && markName in 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
});
};
/**
* UserTiming clear marks
* http://www.w3.org/TR/user-timing/#dom-performance-clearmarks
*
* @param {string} markName Mark name
*/
performance.clearMarks = function (markName) {
if (!markName) {
// clear all marks
marks = {};
} else {
marks[markName] = [];
}
clearEntriesFromPerformanceTimeline('mark', markName);
};
// }
// if (typeof 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
*/
performance.measure = function (measureName, startMark, endMark) {
var
duration,
now = performance.now(),
startMarkTime = 0,
endMarkTime = 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 (startMark in marks) {
startMarkTime = marks[startMark][marks[startMark].length - 1];
} else {
throw new Error(startMark + ' mark not found');
}
if (endMark) {
endMarkTime = 0;
if (endMark in marks) {
endMarkTime = marks[endMark][marks[endMark].length - 1];
} else {
throw new Error(endMark + ' mark not found');
}
}
// add to our measure array
duration = endMarkTime - startMarkTime;
// add to perf timeline as well
addToPerformanceTimeline({
entryType: 'measure',
name: measureName,
startTime: startMarkTime,
duration: duration
});
};
/**
* UserTiming clear measures
* http://www.w3.org/TR/user-timing/#dom-performance-clearmeasures
*
* @param {string} measureName Measure name
*/
performance.clearMeasures = function (measureName) {
clearEntriesFromPerformanceTimeline('measure', measureName);
};
module.exports = performance;