diff --git a/front/src/css/rule.css b/front/src/css/rule.css index cbdcfee..4f5f41d 100644 --- a/front/src/css/rule.css +++ b/front/src/css/rule.css @@ -82,7 +82,7 @@ font-size: 3em; margin-bottom: 1em; } -.offenders .eltButton { +.offenders .offenderButton { display: inline-block; position: relative; background: #efe; @@ -91,16 +91,16 @@ border-radius: 0.4em; z-index: 1; } -.offenders .eltButton.opens { +.offenders .offenderButton.opens { padding-right: 0.75em; } -.offenders .eltButton.opens:after { +.offenders .offenderButton.opens:after { position: relative; left: 0.5em; content: '\25BC'; font-size: 0.8em; } -.offenders .eltButton > div { +.offenders .offenderButton > div { display: none; position: absolute; right: 0; @@ -110,23 +110,26 @@ border-bottom-right-radius: 0.4em; border-top: 1px solid #999; } -.offenders .eltButton .domTree { +.offenders .offenderButton .domTree { text-align: left; white-space: nowrap; } -.offenders .eltButton .domTree > div { +.offenders .offenderButton .domTree > div { margin: 0.5em; } -.offenders .eltButton .domTree > div div { +.offenders .offenderButton .domTree > div div { margin-left: 1em; } -.offenders .eltButton:hover { +.offenders .offenderButton .backtrace { + white-space: nowrap; +} +.offenders .offenderButton:hover { border-bottom-left-radius: 0; border-bottom-right-radius: 0; background: #ffe0cc; z-index: 2; } -.offenders .eltButton:hover > div { +.offenders .offenderButton:hover > div { display: block; } .offendersHtml { diff --git a/front/src/less/rule.less b/front/src/less/rule.less index a54a753..7a19d52 100644 --- a/front/src/less/rule.less +++ b/front/src/less/rule.less @@ -91,7 +91,7 @@ } .offenders { - .eltButton { + .offenderButton { display: inline-block; position: relative; background: #efe; @@ -135,6 +135,10 @@ } } + .backtrace { + white-space: nowrap; + } + &:hover { border-bottom-left-radius: 0; border-bottom-right-radius: 0; diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index 046d9a2..8016d11 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -44,7 +44,7 @@ var policies = { debug('DOMidDuplicated offenders transform function error with "%s"', offender); return offender; } - + return '#' + parts[1] + ': ' + parts[2] + ' occurrences'; }); } @@ -77,15 +77,6 @@ var policies = { "isBadThreshold": 1000, "isAbnormalThreshold": 2000 }, - "DOMqueriesAvoidable": { - "tool": "phantomas", - "label": "Duplicated DOM queries", - "message": "
This is the number of queries that could be avoided by removing all duplicated queries.
Simply save the result of a query in a variable. Ok it is not always simple, especially with third-party scripts, but at least do it with your own code.
", - "isOkThreshold": 0, - "isBadThreshold": 200, - "isAbnormalThreshold": 500, - "takeOffendersFrom": "DOMqueriesDuplicated" - }, "DOMqueriesWithoutResults": { "tool": "phantomas", "label": "DOM queries without result", @@ -102,7 +93,28 @@ var policies = { return offender; } - return '' + parts[1] + ' (in ' + offendersHelpers.domPathToButton(parts[2]) + ') using ' + parts[3] + ''; + return '' + parts[1] + ' (in ' + offendersHelpers.domPathToButton(parts[2]) + ') using ' + parts[3]; + }); + } + }, + "DOMqueriesAvoidable": { + "tool": "phantomas", + "label": "Duplicated DOM queries", + "message": "This is the number of queries that could be avoided by removing all duplicated queries.
Simply save the result of a query in a variable. Ok it is not always simple, especially with third-party scripts, but at least do it with your own code.
", + "isOkThreshold": 0, + "isBadThreshold": 200, + "isAbnormalThreshold": 500, + "takeOffendersFrom": "DOMqueriesDuplicated", + "offendersTransformFn": function(offenders) { + return offenders.map(function(offender) { + var parts = /^.* "(.*)" with (.*) \(in context (.*)\): (.*)\s?queries$/.exec(offender); + + if (!parts) { + debug('DOMqueriesAvoidable offenders transform function error with "%s"', offender); + return offender; + } + + return '' + parts[1] + ' (in ' + offendersHelpers.domPathToButton(parts[3]) + ') using ' + parts[2] + ': ' + parts[4] + ' queries'; }); } }, @@ -112,7 +124,19 @@ var policies = { "message": "Binding too many events has a cost.
It can be avoided by using \"event delegation\". Instead of binding events on each element one by one, events delegation binds them on the top level document element and uses the bubbling principle. It will imperceptibly slow down the event when it occurs, but the loading of the page will speed-up.
", "isOkThreshold": 100, "isBadThreshold": 800, - "isAbnormalThreshold": 1500 + "isAbnormalThreshold": 1500, + "offendersTransformFn": function(offenders) { + return offenders.map(function(offender) { + var parts = /^"(.*)" bound to "(.*)"$/.exec(offender); + + if (!parts) { + debug('eventsBound offenders transform function error with "%s"', offender); + return offender; + } + + return '' + parts[1] + ' bound to ' + offendersHelpers.domPathToButton(parts[2]); + }); + } }, "jsErrors": { "tool": "phantomas", @@ -120,7 +144,24 @@ var policies = { "message": "Just to let you know there are some errors on the page.
Please note that some errors only occur in the PhantomJS browser, so you might need to double check on other browsers.
", "isOkThreshold": 0, "isBadThreshold": 1, - "isAbnormalThreshold": 4 + "isAbnormalThreshold": 4, + "offendersTransformFn": function(offenders) { + return offenders.map(function(offender) { + var parts = /^(.*) - (.*)$/.exec(offender); + + if (!parts) { + debug('jsErrors offenders transform function error with "%s"', offender); + return offender; + } + + var backtraceArray = offendersHelpers.backtraceToArray(parts[2]); + if (backtraceArray === null) { + return offender; + } else { + return '' + parts[1] + ' ' + offendersHelpers.backtraceArrayToHtml(backtraceArray); + } + }); + } }, "evalCalls": { "tool": "phantomas", @@ -152,7 +193,10 @@ var policies = { "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": 10, "isBadThreshold": 50, - "isAbnormalThreshold": 200 + "isAbnormalThreshold": 200, + "offendersTransformFn": function(offenders) { + return offendersHelpers.sortVarsLikeChromeDevTools(offenders); + } }, "jQueryVersion": { "label": "jQuery version", @@ -210,7 +254,7 @@ var policies = { "jQueryDifferentVersions": { "tool": "phantomas", "label": "Several versions loaded", - "message": "jQuery is a heavy library. You should never load jQuery more than one on the same page.
", + "message": "jQuery is a heavy library. You should never load jQuery more than once on the same page.
", "isOkThreshold": 1, "isBadThreshold": 2, "isAbnormalThreshold": 2 diff --git a/lib/offendersHelpers.js b/lib/offendersHelpers.js index 1ce47ef..6b427ab 100644 --- a/lib/offendersHelpers.js +++ b/lib/offendersHelpers.js @@ -11,7 +11,7 @@ var OffendersHelpers = function() { function recursiveTreeBuilder(tree, domArray) { if (domArray.length > 0) { - var currentDomElement = domArray.shift(domArray); + var currentDomElement = domArray.shift(); if (tree === null) { tree = {}; } @@ -64,37 +64,85 @@ var OffendersHelpers = function() { var domTree = this.listOfDomPathsToHTML([domPath]); if (domArray[0] === 'html') { - return '