Remove old phantomas modules
This commit is contained in:
@@ -1,251 +0,0 @@
|
||||
/**
|
||||
* Overwritting the original spying functions in scope.js
|
||||
* This is done so we now have a before AND an after callback on the spy
|
||||
*
|
||||
* @see http://code.jquery.com/jquery-1.10.2.js
|
||||
* @see http://code.jquery.com/jquery-2.0.3.js
|
||||
*/
|
||||
/* global document: true, window: true */
|
||||
|
||||
exports.version = '0.2';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
var responseEndTime = Date.now();
|
||||
|
||||
phantomas.on('responseEnd', function() {
|
||||
responseEndTime = Date.now();
|
||||
});
|
||||
|
||||
phantomas.on('init', function() {
|
||||
phantomas.evaluate(function(responseEndTime) {
|
||||
(function(phantomas) {
|
||||
|
||||
// Overwritting phantomas spy function
|
||||
(function() {
|
||||
var enabled = true;
|
||||
|
||||
// turn off spying to not include internal phantomas actions
|
||||
function spyEnabled(state, reason) {
|
||||
enabled = (state === true);
|
||||
|
||||
phantomas.log('Spying ' + (enabled ? 'enabled' : 'disabled') + (reason ? ' - ' + reason : ''));
|
||||
}
|
||||
|
||||
phantomas.log('Overwritting phantomas spy function');
|
||||
|
||||
function spy(obj, fn, callbackBefore, callbackAfter) {
|
||||
var origFn = obj && obj[fn];
|
||||
|
||||
if (typeof origFn !== 'function') {
|
||||
return false;
|
||||
}
|
||||
|
||||
phantomas.log('Attaching a YLT spy to "' + fn + '" function...');
|
||||
|
||||
obj[fn] = function() {
|
||||
var result;
|
||||
var err;
|
||||
|
||||
// Before
|
||||
if (enabled && callbackBefore) {
|
||||
callbackBefore.apply(this, arguments);
|
||||
}
|
||||
|
||||
// Execute
|
||||
try {
|
||||
|
||||
result = origFn.apply(this, arguments);
|
||||
|
||||
} catch(e) {
|
||||
|
||||
// Catching the err for the moment, because we need to make sure the callbackAfter function is called.
|
||||
|
||||
phantomas.log('Error catched on spyed function "' + fn + '": ' + e);
|
||||
phantomas.log(arguments);
|
||||
|
||||
err = e;
|
||||
|
||||
} finally {
|
||||
|
||||
// After
|
||||
if (enabled && callbackAfter) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
callbackAfter.apply(this, [result].concat(args));
|
||||
}
|
||||
|
||||
if (err) {
|
||||
phantomas.log('Re-throwing the error');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// copy custom properties of original function to the mocked one
|
||||
Object.keys(origFn).forEach(function(key) {
|
||||
obj[fn][key] = origFn[key];
|
||||
});
|
||||
|
||||
obj[fn].prototype = origFn.prototype;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
phantomas.spyEnabled = spyEnabled;
|
||||
phantomas.spy = spy;
|
||||
})();
|
||||
|
||||
|
||||
|
||||
// Adding some code for the Javascript execution tree construction
|
||||
(function() {
|
||||
|
||||
var root = new ContextTreeNode(null, {type: 'main'});
|
||||
var currentContext = root;
|
||||
var depth = 0;
|
||||
|
||||
|
||||
// Add a child but don't enter its context
|
||||
function pushContext(data) {
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth === 0) {
|
||||
data.timestamp = Date.now() - responseEndTime;
|
||||
data.loadingStep = phantomas.currentStep || '';
|
||||
}
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth > 0) {
|
||||
if (data.backtrace) {
|
||||
delete data.backtrace;
|
||||
}
|
||||
if (data.resultsNumber) {
|
||||
delete data.resultsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
currentContext.addChild(data);
|
||||
}
|
||||
|
||||
// Add a child to the current context and enter its context
|
||||
function enterContext(data) {
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth === 0) {
|
||||
data.timestamp = Date.now() - responseEndTime;
|
||||
data.loadingStep = phantomas.currentStep || '';
|
||||
}
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth > 0) {
|
||||
if (data.backtrace) {
|
||||
delete data.backtrace;
|
||||
}
|
||||
if (data.resultsNumber) {
|
||||
delete data.resultsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
currentContext = currentContext.addChild(data);
|
||||
|
||||
depth ++;
|
||||
}
|
||||
|
||||
// Save given data in the current context and jump change current context to its parent
|
||||
function leaveContext(moreData) {
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth === 1) {
|
||||
currentContext.data.time = Date.now() - currentContext.data.timestamp - responseEndTime;
|
||||
}
|
||||
|
||||
// Some data is not needed on subchildren
|
||||
if (depth > 1) {
|
||||
if (moreData && moreData.backtrace) {
|
||||
delete moreData.backtrace;
|
||||
}
|
||||
if (moreData && moreData.resultsNumber) {
|
||||
delete moreData.resultsNumber;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge previous data with moreData (ovewrites if exists)
|
||||
if (moreData) {
|
||||
for (var key in moreData) {
|
||||
currentContext.data[key] = moreData[key];
|
||||
}
|
||||
}
|
||||
|
||||
var parent = currentContext.parent;
|
||||
if (parent === null) {
|
||||
console.error('Error: trying to close root context in ContextTree');
|
||||
} else {
|
||||
currentContext = parent;
|
||||
}
|
||||
|
||||
depth --;
|
||||
}
|
||||
|
||||
function getContextData() {
|
||||
return currentContext.data;
|
||||
}
|
||||
|
||||
// Returns a clean object, without the parent which causes recursive loops
|
||||
function readFullTree() {
|
||||
// Return null if the contextTree is not correctly closed
|
||||
if (root !== currentContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function recusiveRead(node) {
|
||||
if (node.children.length === 0) {
|
||||
delete node.children;
|
||||
} else {
|
||||
for (var i=0, max=node.children.length ; i<max ; i++) {
|
||||
recusiveRead(node.children[i]);
|
||||
}
|
||||
}
|
||||
delete node.parent;
|
||||
}
|
||||
recusiveRead(root);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
// Empty the tree
|
||||
function resetTree() {
|
||||
root = new ContextTreeNode(null, {type: 'main'});
|
||||
currentContext = root;
|
||||
depth = 0;
|
||||
}
|
||||
|
||||
|
||||
function ContextTreeNode(parent, data) {
|
||||
this.data = data;
|
||||
this.parent = parent;
|
||||
this.children = [];
|
||||
|
||||
this.addChild = function(data) {
|
||||
var child = new ContextTreeNode(this, data);
|
||||
this.children.push(child);
|
||||
return child;
|
||||
};
|
||||
}
|
||||
|
||||
phantomas.log('Adding some contextTree functions to phantomas');
|
||||
phantomas.pushContext = pushContext;
|
||||
phantomas.enterContext = enterContext;
|
||||
phantomas.leaveContext = leaveContext;
|
||||
phantomas.getContextData = getContextData;
|
||||
phantomas.readFullTree = readFullTree;
|
||||
phantomas.resetTree = resetTree;
|
||||
|
||||
})();
|
||||
|
||||
})(window.__phantomas);
|
||||
}, responseEndTime);
|
||||
});
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Analyzes AJAX requests
|
||||
*/
|
||||
/* global window: true */
|
||||
|
||||
exports.version = '0.2.a';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
phantomas.setMetric('ajaxRequests'); // @desc number of AJAX requests
|
||||
phantomas.setMetric('synchronousXHR'); // @desc number of synchronous
|
||||
|
||||
phantomas.on('init', function() {
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
phantomas.spy(window.XMLHttpRequest.prototype, 'open', null, function(result, method, url, async) {
|
||||
phantomas.incrMetric('ajaxRequests');
|
||||
phantomas.addOffender('ajaxRequests', '<%s> [%s]', url, method);
|
||||
|
||||
if (async === false) {
|
||||
phantomas.incrMetric('synchronousXHR');
|
||||
phantomas.addOffender('synchronousXHR', url);
|
||||
phantomas.log('ajaxRequests: synchronous XMLHttpRequest call to <%s>', url);
|
||||
}
|
||||
}, true);
|
||||
})(window.__phantomas);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
/**
|
||||
* Analyzes DOM hidden content
|
||||
*/
|
||||
/* global document: true, Node: true, window: true */
|
||||
|
||||
exports.version = '1.0.a';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
// total length of HTML of hidden elements (i.e. display: none)
|
||||
phantomas.setMetric('hiddenContentSize'); // @desc the size of content of hidden elements on the page (with CSS display: none) @offenders
|
||||
phantomas.setMetric('hiddenImages'); // @desc number of hidden images that can be lazy-loaded @offenders
|
||||
|
||||
// HTML size
|
||||
phantomas.on('report', function() {
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
var runner = new phantomas.nodeRunner(),
|
||||
lazyLoadableImages = {};
|
||||
|
||||
phantomas.spyEnabled(false, 'analyzing hidden content');
|
||||
|
||||
runner.walk(document.body, function(node, depth) {
|
||||
switch (node.nodeType) {
|
||||
case Node.ELEMENT_NODE:
|
||||
// @see https://developer.mozilla.org/en/DOM%3awindow.getComputedStyle
|
||||
var styles = window.getComputedStyle(node);
|
||||
|
||||
if (styles && styles.getPropertyValue('display') === 'none') {
|
||||
if (typeof node.innerHTML === 'string') {
|
||||
var size = node.innerHTML.length;
|
||||
phantomas.incrMetric('hiddenContentSize', size);
|
||||
|
||||
// log hidden containers bigger than 1 kB
|
||||
if (size > 1024) {
|
||||
phantomas.addOffender('hiddenContentSize', phantomas.getDOMPath(node) + ' (' + size + ' characters)');
|
||||
}
|
||||
}
|
||||
|
||||
// count hidden images that can be lazy loaded (issue #524)
|
||||
var images = [];
|
||||
if (node.tagName === 'IMG') {
|
||||
images = [node];
|
||||
} else if (typeof node.querySelectorAll === 'function') {
|
||||
images = node.querySelectorAll('img') || [];
|
||||
}
|
||||
|
||||
for (var i = 0, len = images.length; i < len; i++) {
|
||||
var src = images[i].src,
|
||||
path;
|
||||
|
||||
if (src === '' || src.indexOf('data:image') === 0) continue;
|
||||
|
||||
if (images[i].width === 1 && images[i].height === 1) continue;
|
||||
|
||||
if (!lazyLoadableImages[src]) {
|
||||
path = phantomas.getDOMPath(images[i]);
|
||||
|
||||
lazyLoadableImages[src] = {
|
||||
path: path
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// don't run for child nodes as they're hidden as well
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(lazyLoadableImages).forEach(function(img) {
|
||||
var entry = lazyLoadableImages[img];
|
||||
|
||||
phantomas.incrMetric('hiddenImages');
|
||||
phantomas.addOffender('hiddenImages', img);
|
||||
|
||||
phantomas.log('hiddenImages: <%s> image (%s) is hidden and can be lazy-loaded', img, entry.path);
|
||||
});
|
||||
|
||||
phantomas.spyEnabled(true);
|
||||
}(window.__phantomas));
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,82 +0,0 @@
|
||||
/**
|
||||
* Analyzes events bound to DOM elements
|
||||
*/
|
||||
/* global Document: true, Element: true, window: true */
|
||||
|
||||
exports.version = '0.4.a';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
phantomas.setMetric('eventsBound'); // @desc number of EventTarget.addEventListener calls
|
||||
phantomas.setMetric('eventsDispatched'); // @desc number of EventTarget.dispatchEvent calls
|
||||
phantomas.setMetric('eventsScrollBound'); // @desc number of scroll event bounds
|
||||
|
||||
phantomas.on('init', function() {
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
// spy calls to EventTarget.addEventListener
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener
|
||||
function eventSpyBefore(eventType) {
|
||||
/* jshint validthis: true */
|
||||
var path = phantomas.getDOMPath(this);
|
||||
//phantomas.log('DOM event: "' + eventType + '" bound to "' + path + '"');
|
||||
|
||||
phantomas.incrMetric('eventsBound');
|
||||
phantomas.addOffender('eventsBound', '"%s" bound to "%s"', eventType, path);
|
||||
phantomas.log('event ' + eventType + ' bound');
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'addEventListener',
|
||||
callDetails: {
|
||||
context: {
|
||||
length: 1,
|
||||
elements: [path]
|
||||
},
|
||||
arguments: [eventType]
|
||||
},
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
|
||||
// count window.addEventListener('scroll', ...) - issue #508
|
||||
if (eventType === 'scroll' && (path === 'window' || path === '#document')) {
|
||||
phantomas.incrMetric('eventsScrollBound');
|
||||
phantomas.addOffender('eventsScrollBound', 'bound by %s on %s', phantomas.getBacktrace(), path);
|
||||
}
|
||||
}
|
||||
|
||||
function eventSpyAfter(result) {
|
||||
phantomas.leaveContext();
|
||||
}
|
||||
|
||||
phantomas.spy(Element.prototype, 'addEventListener', eventSpyBefore, eventSpyAfter);
|
||||
phantomas.spy(Document.prototype, 'addEventListener', eventSpyBefore, eventSpyAfter);
|
||||
phantomas.spy(window, 'addEventListener', eventSpyBefore, eventSpyAfter);
|
||||
|
||||
// spy calls to EventTarget.dispatchEvent
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.dispatchEvent
|
||||
phantomas.spy(Element.prototype, 'dispatchEvent', function(ev) {
|
||||
/* jshint validthis: true */
|
||||
var path = phantomas.getDOMPath(this);
|
||||
|
||||
phantomas.log('Core JS event: triggered "%s" on "%s"', ev.type, path);
|
||||
|
||||
phantomas.incrMetric('eventsDispatched');
|
||||
phantomas.addOffender('eventsDispatched', '"%s" on "%s"', ev.type, path);
|
||||
});
|
||||
})(window.__phantomas);
|
||||
});
|
||||
});
|
||||
|
||||
phantomas.on('report', function() {
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
// Check if a window.onscroll function is defined
|
||||
if (typeof(window.onscroll) === "function") {
|
||||
phantomas.incrMetric('eventsScrollBound');
|
||||
phantomas.addOffender('eventsScrollBound', 'bound by %s on %s', '', 'window.onscroll');
|
||||
}
|
||||
}(window.__phantomas));
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Meters the number of page errors, and provides traces as offenders for "jsErrors" metric
|
||||
*/
|
||||
|
||||
exports.version = '0.3.a';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
phantomas.setMetric('jsErrors'); // @desc number of JavaScript errors
|
||||
|
||||
function formatTrace(trace) {
|
||||
var ret = [];
|
||||
|
||||
if(Array.isArray(trace)) {
|
||||
trace.forEach(function(entry) {
|
||||
ret.push((entry.function ? entry.function + ' ' : '') + (entry.sourceURL || entry.file) + ':' + entry.line);
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
phantomas.on('jserror', function(msg, trace) {
|
||||
trace = formatTrace(trace);
|
||||
|
||||
phantomas.log(msg);
|
||||
phantomas.log('Backtrace: ' + trace.join(' / '));
|
||||
|
||||
phantomas.incrMetric('jsErrors');
|
||||
phantomas.addOffender('jsErrors', msg + ' - ' + trace.join(' / '));
|
||||
|
||||
// Yeah, this is weird, i'm sending the error back to the browser...
|
||||
phantomas.evaluate(function(msg, caller, trace) {
|
||||
(function(phantomas) {
|
||||
|
||||
phantomas.pushContext({
|
||||
type: 'error',
|
||||
callDetails: {
|
||||
arguments: [msg]
|
||||
},
|
||||
caller: caller,
|
||||
backtrace: trace
|
||||
});
|
||||
|
||||
})(window.__phantomas);
|
||||
}, msg, trace[0], trace.join(' / '));
|
||||
});
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Analyzes images and detects which one can be lazy-loaded (are below the fold)
|
||||
*
|
||||
* @see https://github.com/macbre/phantomas/issues/494
|
||||
*/
|
||||
/* global document: true, window: true */
|
||||
|
||||
exports.version = '1.0.a';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
phantomas.setMetric('lazyLoadableImagesBelowTheFold'); // @desc number of images displayed below the fold that can be lazy-loaded
|
||||
|
||||
phantomas.on('report', function() {
|
||||
phantomas.log('lazyLoadableImages: analyzing which images can be lazy-loaded...');
|
||||
|
||||
phantomas.evaluate(function() {
|
||||
(function(phantomas) {
|
||||
phantomas.spyEnabled(false, 'analyzing which images can be lazy-loaded');
|
||||
|
||||
var images = document.body.getElementsByTagName('img'),
|
||||
i,
|
||||
len = images.length,
|
||||
offset,
|
||||
path,
|
||||
processedImages = {},
|
||||
src,
|
||||
viewportHeight = window.innerHeight,
|
||||
// Add an offset of 100px under the height of the screen
|
||||
LAZYLOAD_OFFSET = 100;
|
||||
|
||||
phantomas.log('lazyLoadableImages: %d image(s) found, assuming %dpx offset to be the fold', len, viewportHeight);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
// @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
|
||||
offset = images[i].getBoundingClientRect().top;
|
||||
src = images[i].src;
|
||||
|
||||
// ignore base64-encoded images
|
||||
if (src === '' || /^data:/.test(src)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
path = phantomas.getDOMPath(images[i]);
|
||||
|
||||
// get the most top position for a given image (deduplicate by src)
|
||||
if (typeof processedImages[src] === 'undefined') {
|
||||
processedImages[src] = {
|
||||
offset: offset,
|
||||
path: path
|
||||
};
|
||||
}
|
||||
|
||||
// maybe there's the same image loaded above the fold?
|
||||
if (offset < processedImages[src].offset) {
|
||||
processedImages[src] = {
|
||||
offset: offset,
|
||||
path: path
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
phantomas.log('lazyLoadableImages: checking %d unique image(s)', Object.keys(processedImages).length);
|
||||
|
||||
Object.keys(processedImages).forEach(function(src) {
|
||||
var img = processedImages[src];
|
||||
|
||||
if (img.offset > viewportHeight + LAZYLOAD_OFFSET) {
|
||||
phantomas.log('lazyLoadableImages: <%s> image (%s) is below the fold (at %dpx)', src, img.path, img.offset);
|
||||
|
||||
phantomas.incrMetric('lazyLoadableImagesBelowTheFold');
|
||||
phantomas.addOffender('lazyLoadableImagesBelowTheFold', src);
|
||||
}
|
||||
});
|
||||
|
||||
phantomas.spyEnabled(true);
|
||||
})(window.__phantomas);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Retries download on every request to get the real file size
|
||||
*
|
||||
*/
|
||||
|
||||
exports.version = '0.1';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
phantomas.setMetric('requestsList');
|
||||
|
||||
var requests = [];
|
||||
|
||||
phantomas.on('recv', function(entry, res) {
|
||||
requests.push(entry);
|
||||
});
|
||||
|
||||
phantomas.on('report', function() {
|
||||
phantomas.setMetric('requestsList', true, true);
|
||||
phantomas.addOffender('requestsList', JSON.stringify(requests));
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user