var debug = require('debug')('ylt:policies'); var offendersHelpers = require('../offendersHelpers'); var policies = { "DOMelementsCount": { "tool": "phantomas", "label": "DOM elements count", "message": "

A high number of DOM elements means a lot of work for the browser to render the page.

It also slows down JavaScript DOM queries, as there are more elements to search through.

", "isOkThreshold": 1500, "isBadThreshold": 3000, "isAbnormalThreshold": 4500, "hasOffenders": false }, "DOMelementMaxDepth": { "tool": "phantomas", "label": "DOM max depth", "message": "

A deep DOM makes the CSS matching with DOM elements difficult.

It also slows down JavaScript modifications to the DOM because changing the dimensions of an element makes the browser re-calculate the dimensions of it's parents. Same thing for JavaScript events, that bubble up to the document root.

", "isOkThreshold": 15, "isBadThreshold": 25, "isAbnormalThreshold": 32, "hasOffenders": true, "offendersTransformFn": function(offenders) { var domArrays = offenders.map(offendersHelpers.domPathToArray); return { count: offenders.length, tree: offendersHelpers.listOfDomArraysToTree(domArrays) }; } }, "iframesCount": { "tool": "phantomas", "label": "Number of iframes", "message": "

iFrames are the most complex HTML elements. They are pages, just like the main page, and the browser needs to create a new page context, which has a cost.

", "isOkThreshold": 4, "isBadThreshold": 15, "isAbnormalThreshold": 30, "hasOffenders": true }, "DOMidDuplicated": { "tool": "phantomas", "label": "IDs duplicated", "message": "

IDs of HTML elements must be document-wide unique. This can cause problems with getElementById returning the wrong element.

", "isOkThreshold": 0, "isBadThreshold": 10, "isAbnormalThreshold": 50, "hasOffenders": true }, "scriptDuration": { "tool": "phantomas", "label": "Total JS execution time", "message": "

This is the number of milliseconds spent by the browser on JavaScript execution during page load.

For more details, try using the performance tab in Chrome DevTools. It is a bit complicated at first sight, but you'll be able to analyze exactly where this execution time is spent.

", "isOkThreshold": 500, "isBadThreshold": 2000, "isAbnormalThreshold": 4000, "hasOffenders": false, "unit": "ms" }, "DOMaccesses": { "tool": "domAccessAgregator", "label": "DOM access", "message": "

This metric estimates the number of times the JavaScript reads, changes or binds the DOM.

The more your JavaScript code accesses the DOM, the slower the page will load.

Try, as much as possible, to have an HTML page fully generated by the server instead of making changes with JS.

Try to reduce the number of queries by refactoring your JavaScript code.

Binding too many events also has a cost.

", "isOkThreshold": 500, "isBadThreshold": 2500, "isAbnormalThreshold": 5000, "hasOffenders": true }, "eventsScrollBound": { "tool": "phantomas", "label": "Scroll events bound", "message": "

Number of 'scroll' event listeners binded to 'window' or 'document'.

Asking too much work to the browser on scroll hurts the smoothness of the scroll. Merging all your event listeners into an unique listener can help you factorize their code and reduce their footprint on scroll.

", "isOkThreshold": 1, "isBadThreshold": 8, "isAbnormalThreshold": 15, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { count: offenders.length, list: offenders.map(function(offender) { var parts = /^bound by (.*) on ([^ ]+)$/.exec(offender); if (!parts) { debug('eventsScrollBound offenders transform function error with "%s"', offender); return { parseError: offender }; } var backtraceArray = offendersHelpers.backtraceToArray(parts[1]); return { backtrace: backtraceArray || [], target: parts[2] }; }) }; } }, "jsErrors": { "tool": "phantomas", "label": "JavaScript errors", "message": "

Just to let you know there are some errors on the page.

", "isOkThreshold": 0, "isBadThreshold": 1, "isAbnormalThreshold": 5, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { count: offenders.length, list: offenders.map(function(offender) { var parts = /^(.*) - (.*)$/.exec(offender); if (!parts) { debug('jsErrors offenders transform function error with "%s"', offender); return { parseError: offender }; } var backtraceArray = offendersHelpers.backtraceToArray(parts[2]); return { error: parts[1], backtrace: backtraceArray || [] }; }) }; } }, "documentWriteCalls": { "tool": "phantomas", "label": "document.write calls", "message": "

They slow down the page construction, especially if they are used to insert scripts in the page. Remove them ASAP.

If you cannot remove them because they come from a third-party script (such as ads), have a look at PostScribe.

", "isOkThreshold": 0, "isBadThreshold": 2, "isAbnormalThreshold": 6, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { count: offenders.length, list: offenders.map(function(offender) { var parts = /^document.write(ln)?\(\) used from (.*)$/.exec(offender); if (parts) { var writeFn = 'document.write' + (parts[1] || ''); var methodParts = /^([^\s]+) \((.+):(\d+)\)$/.exec(parts[2]); if (methodParts) { return { writeFn: writeFn, from: { functionName: methodParts[1], file: methodParts[2], line: methodParts[3] } }; } else { var noMethodParts = /^(.+):(\d+)$/.exec(parts[2]); if (noMethodParts) { return { writeFn: writeFn, from: { file: noMethodParts[1], line: noMethodParts[2] } }; } } } debug('documentWriteCalls offenders transform function error with "%s"', offender); return { parseError: offender }; }) }; } }, "synchronousXHR": { "tool": "phantomas", "label": "Synchronous Ajax requests", "message": "

Making an XMLHttpRequest with the async option set to false is deprecated due to the negative effect to performances. The browser's main thread needs to stop everything until the response is received.

", "isOkThreshold": 0, "isBadThreshold": 1, "isAbnormalThreshold": 1, "hasOffenders": true }, "globalVariables": { "tool": "phantomas", "label": "Global variables", "message": "

It is a bad practice because they clutter up the global namespace. If two scripts use the same variable name in the global scope, it can cause conflicts and it is generally hard to debug.

Global variables also take a (very) little bit longer to be accessed than variables in the local scope of a function.

", "isOkThreshold": 20, "isBadThreshold": 300, "isAbnormalThreshold": 800, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { count: offenders.length, list: offendersHelpers.sortVarsLikeChromeDevTools(offenders) }; } }, "jQueryVersion": { "label": "jQuery version", "message": "

The current latest version of jQuery is 3.5

Each new version of jQuery optimizes performances. Do not keep an old version of jQuery. Updating can sometimes break a few things, but it is generally quite easy to fix them up. So don't hesitate.

", "hasOffenders": false, "scoreFn": function(data) { var differentVersions = data.toolsResults.phantomas.metrics.jQueryVersionsLoaded; if (differentVersions === 0 || differentVersions > 1 || !data.toolsResults.phantomas.metrics.jQueryVersion) { // Not applicable return null; } else { var value = data.toolsResults.phantomas.metrics.jQueryVersion; var score; if (value.indexOf('3.3.') === 0 || value.indexOf('3.4.') === 0 || value.indexOf('3.5.') === 0 || value.indexOf('3.6.') === 0 || value.indexOf('3.7.') === 0 || value.indexOf('4.0.') === 0) { score = 100; } else if (value.indexOf('3.2.') === 0) { score = 90; } else if (value.indexOf('3.1.') === 0) { score = 70; } else if (value.indexOf('3.0.') === 0) { score = 50; } else if (value.indexOf('1.12.') === 0 || value.indexOf('2.2.') === 0) { score = 40; } else if (value.indexOf('1.11.') === 0 || value.indexOf('2.1.') === 0) { score = 30; } else if (value.indexOf('1.10.') === 0 || value.indexOf('2.0.') === 0) { score = 20; } else if (value.indexOf('1.9') === 0) { score = 10; } else if (value.indexOf('1.9') === 0 || value.indexOf('1.8') === 0 || value.indexOf('1.7') === 0 || value.indexOf('1.6') === 0 || value.indexOf('1.5') === 0 || value.indexOf('1.4') === 0 || value.indexOf('1.3') === 0 || value.indexOf('1.2') === 0) { score = 0; } else { debug('Unknown jQuery version "%s"', value); return null; } // Truncate version number (can be long sometimes, no clue why but it can...) if (value.length > 30) { value = value.substr(0, 28) + '...'; } return { value: value, score: score, bad: value < 100, abnormal: false, abnormalityScore: 0 }; } } }, "jQueryVersionsLoaded": { "tool": "phantomas", "label": "Several jQuery loaded", "message": "

jQuery is a heavy library. You should never load jQuery more than once on the same page.

", "isOkThreshold": 1, "isBadThreshold": 2, "isAbnormalThreshold": 3, "hasOffenders": true }, "cssParsingErrors": { "tool": "phantomas", "label": "CSS syntax error", "message": "

Yellow Lab Tools failed to parse a CSS file. I doubt the problem comes from the css parser.

Maybe a CSS validator can help you.

", "isOkThreshold": 0, "isBadThreshold": 2, "isAbnormalThreshold": 20, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { count: offenders.length, list: offenders.map(function(offender) { if (offender === '[inline CSS]') { return { error: 'Empty