|
@@ -1,959 +0,0 @@
|
|
-<html>
|
|
|
|
-<head>
|
|
|
|
- <meta charset="utf-8">
|
|
|
|
- <title>Yellow Lab Tools - Results page</title>
|
|
|
|
- <link rel="icon" type="image/png" href="/public/img/favicon.png">
|
|
|
|
- <link rel="stylesheet" type="text/css" href="/public/styles/main.css">
|
|
|
|
- <link rel="stylesheet" type="text/css" href="/public/styles/results.css">
|
|
|
|
- <script src="/bower_components/angular/angular.min.js"></script>
|
|
|
|
- <script src="/bower_components/ngModal/dist/ng-modal.js"></script>
|
|
|
|
- <script src="/public/scripts/app.js"></script>
|
|
|
|
- <script src="/public/scripts/resultsCtrl.js"></script>
|
|
|
|
- <script src="/public/scripts/showOffenders.js"></script>
|
|
|
|
-</head>
|
|
|
|
-<body ng-app="YellowLabTools" ng-controller="ResultsCtrl">
|
|
|
|
- <h1>Yellow Lab <span class="icon-lab"></span> Tools</h1>
|
|
|
|
-
|
|
|
|
- <div ng-if="undefined">Untangling and counting the spaghettis...</div>
|
|
|
|
-
|
|
|
|
- <div class="ng-cloak">
|
|
|
|
- <div>Tested url: <a class="testedUrl" href="{{phantomasResults.url}}" target="_blank">{{phantomasResults.url}}</a></div>
|
|
|
|
-
|
|
|
|
- <div class="resultsMenu">
|
|
|
|
- <a class="menuItem back" href="/"><div class="icon-back"></div><span>New test<span></a>
|
|
|
|
- <div class="menuItem" ng-if="!phantomasResults.error" ng-class="{active: view == 'execution'}" ng-click="setView('execution')"><div class="icon-spaghetti"></div><span>JS Timeline</span></div>
|
|
|
|
- <div class="menuItem" ng-if="!phantomasResults.error" ng-class="{active: view == 'summary'}" ng-click="setView('summary')"><div class="icon-summary"></div><span>Grades</span></div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div ng-if="phantomasResults.error">
|
|
|
|
- <h2>Error: {{phantomasResults.error}}</h2>
|
|
|
|
- <div ng-if="phantomasResults.error == 252">Phantomas timed out</div>
|
|
|
|
- <div ng-if="phantomasResults.error == 253">Phantomas config error</div>
|
|
|
|
- <div ng-if="phantomasResults.error == 254">Phantomas failed to load page</div>
|
|
|
|
- <div ng-if="phantomasResults.error == 255">Phantomas internal error</div>
|
|
|
|
- <div ng-if="phantomasResults.error == 1001">Javascript execution tree error</div>
|
|
|
|
- <div ng-if="phantomasResults.error == 1002">JSON undefined error</div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div ng-show="view == 'summary' && !phantomasResults.error" class="summary">
|
|
|
|
- <h2>Grades</h2>
|
|
|
|
-
|
|
|
|
- <div class="notations">
|
|
|
|
- <div>
|
|
|
|
- <div ng-class="notations.domComplexity">{{notations.domComplexity}}</div>
|
|
|
|
- <div class="notation">DOM complexity</div>
|
|
|
|
- <div class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-if="phantomasResults.metrics.DOMelementsCount" ng-class="{'warning': phantomasResults.metrics.DOMelementsCount > 5000}">
|
|
|
|
- <div class="label">DOM elements count</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.DOMelementsCount}}
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="DOMelementsCountTooltip = true"></div>
|
|
|
|
- <modal-dialog show="DOMelementsCountTooltip" dialog-title="Avoid too many DOM elements">
|
|
|
|
- <p>A high number of DOM elements means a lot of work for the browser to render the page.</p>
|
|
|
|
- <p>It also slows down Javascript DOM queries, as there are more elements to search through.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.DOMelementMaxDepth" ng-class="{'warning': phantomasResults.metrics.DOMelementMaxDepth > 30}">
|
|
|
|
- <div class="label">DOM max depth</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.DOMelementMaxDepth}}
|
|
|
|
- <show-offenders modal-title="DOM max depth" metric-name="DOMelementMaxDepth" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="DOMelementMaxDepthTooltip = true"></div>
|
|
|
|
- <modal-dialog show="DOMelementMaxDepthTooltip" dialog-title="Reduce DOM depth">
|
|
|
|
- <p>A deep DOM makes the CSS matching with DOM elements difficult.</p>
|
|
|
|
- <p>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.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.iframesCount" ng-class="{'warning': phantomasResults.metrics.iframesCount > 30}">
|
|
|
|
- <div class="label">Number of iframes</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.iframesCount}}
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="DOMelementMaxDepthTooltip = true"></div>
|
|
|
|
- <modal-dialog show="DOMelementMaxDepthTooltip" dialog-title="iFrames are slow">
|
|
|
|
- <p>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.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.DOMidDuplicated" ng-class="{'warning': phantomasResults.metrics.DOMidDuplicated > 20}">
|
|
|
|
- <div class="label">IDs duplicated</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.DOMidDuplicated}}
|
|
|
|
- <show-offenders modal-title="DOM IDs duplicated" metric-name="DOMidDuplicated" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="DOMidDuplicatedTooltip = true"></div>
|
|
|
|
- <modal-dialog show="DOMidDuplicatedTooltip" dialog-title="Warning: duplicated DOM IDs">
|
|
|
|
- <p>IDs of HTML elements must be document-wide unique. This can cause problems with getElementById returning the wrong element.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <div ng-class="notations.jsDomManipulations">{{notations.jsDomManipulations}}</div>
|
|
|
|
- <div class="notation">DOM manipulations</div>
|
|
|
|
- <div class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-if="phantomasResults.metrics.DOMinserts" ng-class="{'warning': phantomasResults.metrics.DOMinserts > 1000}">
|
|
|
|
- <div class="label">DOM inserts</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.DOMinserts}}
|
|
|
|
- <show-offenders modal-title="DOM inserts" metric-name="DOMinserts" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="DOMinsertsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="DOMinsertsTooltip" dialog-title="DOM insertions are slow">
|
|
|
|
- <p>Working with the DOM in Javascript triggers layout calculations and slows down the page.</p>
|
|
|
|
- <p>Try, as much as possible, to have an HTML page fully generated by the server instead of making changes with JS.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.DOMqueries" ng-class="{'warning': phantomasResults.metrics.DOMqueries > 2000}">
|
|
|
|
- <div class="label">DOM queries</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.DOMqueries}}
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="DOMqueriesTooltip = true"></div>
|
|
|
|
- <modal-dialog show="DOMqueriesTooltip" dialog-title="Avoid having too many DOM queries">
|
|
|
|
- <p>DOM queries are like looking in a large catalog of items. Even if the browsers made progress on the performances of queries, websites often make hundreds of them.</p>
|
|
|
|
- <p>Try to reduce the number of queries by refactoring your Javascript code.</p>
|
|
|
|
- <p>Avoid also to have a read query between two write queries. To be able to reduce the number repaints and optimize performances, browsers buffer the DOM writing operations and treat them in bulk. But each time a DOM reading is asked, the browser needs to empty the buffer. This can be particularly slow inside a loop.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.DOMqueriesAvoidable" ng-class="{'warning': phantomasResults.metrics.DOMqueriesAvoidable > 500}">
|
|
|
|
- <div class="label">Avoidable queries</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.DOMqueriesAvoidable}}
|
|
|
|
- <show-offenders modal-title="Duplicated DOM queries" metric-name="DOMqueriesDuplicated" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="duplicatedQueriesCountAllTooltip = true"></div>
|
|
|
|
- <modal-dialog show="duplicatedQueriesCountAllTooltip" dialog-title="Cache duplicated DOM queries">
|
|
|
|
- <p>This is the number of queries that could be avoided by removing all duplicated queries.</p>
|
|
|
|
- <p>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.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.eventsBound" ng-class="{'warning': phantomasResults.metrics.eventsBound > 2000}">
|
|
|
|
- <div class="label">Events bound</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.eventsBound}}
|
|
|
|
- <show-offenders modal-title="Events bound" metric-name="eventsBound" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="eventsBoundTooltip = true"></div>
|
|
|
|
- <modal-dialog show="eventsBoundTooltip" dialog-title="Reduce the number of events binding">
|
|
|
|
- <p>Binding too many events has a cost.</p>
|
|
|
|
- <p>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.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <div ng-class="notations.jsBadPractices">{{notations.jsBadPractices}}</div>
|
|
|
|
- <div class="notation">Bad Javascript</div>
|
|
|
|
- <div class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-if="phantomasResults.metrics.jsErrors" ng-class="{'warning': phantomasResults.metrics.jsErrors > 5}">
|
|
|
|
- <div class="label">Javascript errors</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.jsErrors}}
|
|
|
|
- <show-offenders modal-title="Javascript errors" metric-name="jsErrors" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="jsErrorsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="jsErrorsTooltip" dialog-title="Javascript errors">
|
|
|
|
- <p>Just to let you know there are some errors on the page.</p>
|
|
|
|
- <p><b>Please note that some errors only occur in the PhantomJS browser, so you might need to double check on other browsers.</b></p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.evalCalls">
|
|
|
|
- <div class="label">eval calls</div>
|
|
|
|
- <div class="result">{{phantomasResults.metrics.evalCalls}}</div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="evalCallsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="evalCallsTooltip" dialog-title="Eval is evil">
|
|
|
|
- <p>The 'eval' function is slow and a bad coding practice. Try to get rid of it.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.documentWriteCalls">
|
|
|
|
- <div class="label">document.write calls</div>
|
|
|
|
- <div class="result">{{phantomasResults.metrics.documentWriteCalls}}</div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="documentWriteCallsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="documentWriteCallsTooltip" dialog-title="document.write should have never existed">
|
|
|
|
- <p>They slow down the page construction, especially if they are used to insert scripts in the page. Remove them ASAP.</p>
|
|
|
|
- <p>If you cannot remove them because they come from a third-party script (such as ads), have a look at <a href="https://github.com/krux/postscribe" target="_blank">PostScribe</a>.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.consoleMessages">
|
|
|
|
- <div class="label">Console messages</div>
|
|
|
|
- <div class="result">{{phantomasResults.metrics.consoleMessages}}</div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="consoleMessagesTooltip = true"></div>
|
|
|
|
- <modal-dialog show="consoleMessagesTooltip" dialog-title="Avoid using console.log()">
|
|
|
|
- <p>Try to keep your console clean when in production. Debugging is good for development only.</p>
|
|
|
|
- <p>Writing in the console has a cost, especially when dumping large object variables.</p>
|
|
|
|
- <p>There is also a problem with Internet Explorer 8, not knowing the console object.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.globalVariables">
|
|
|
|
- <div class="label">Global variables</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.globalVariables}}
|
|
|
|
- <show-offenders modal-title="Global variables" metric-name="globalVariables" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="globalVariablesTooltip = true"></div>
|
|
|
|
- <modal-dialog show="globalVariablesTooltip" dialog-title="Minimize the use of global variables">
|
|
|
|
- <p>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.</p>
|
|
|
|
- <p>Global variables also take a (very) little bit longer to be accessed than variables in the local scope of a function.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="inBodyDomManipulations > 10" ng-class="{'warning': inBodyDomManipulations > 200}">
|
|
|
|
- <div class="label">DOM manipulations in body</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{inBodyDomManipulations}}
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="inBodyDomManipulationsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="inBodyDomManipulationsTooltip" dialog-title="Javascript executed inside the body">
|
|
|
|
- <p>This metric counts the number of DOM queries, DOM inserts, binds, etc. made by the Javascript before the DOMContentLoaded event.</p>
|
|
|
|
- <p>Wait for this event before manipulating the DOM. Do not execute Javascript in the middle of the BODY as it slows down the construction of the DOM and makes a poor maintainability. This is what i call spaghetti code.</p>
|
|
|
|
- <p>The JS Timeline tab can help you identify what's happening.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <div ng-class="notations.jQueryLoading">{{notations.jQueryLoading}}</div>
|
|
|
|
- <div class="notation">jQuery version</div>
|
|
|
|
- <div class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-if="phantomasResults.metrics.jQueryDifferentVersions == 1">
|
|
|
|
- <div class="label">jQuery version</div>
|
|
|
|
- <div class="result">{{phantomasResults.metrics.jQueryVersion}}</div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="jQueryVersionTooltip = true"></div>
|
|
|
|
- <modal-dialog show="jQueryVersionTooltip" dialog-title="Use the latest version of jQuery">
|
|
|
|
- <p>Current latest versions of jQuery are 1.11 (with support for old IE versions) and 2.1 (without).</p>
|
|
|
|
- <p>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.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.jQueryDifferentVersions > 1" class="warning">
|
|
|
|
- <div class="label">{{phantomasResults.metrics.jQueryDifferentVersions}} versions loaded</div>
|
|
|
|
- <div class="result">
|
|
|
|
- <span ng-repeat="version in phantomasResults.offenders.jQueryDifferentVersions">{{version}}<span ng-show="!$last"> & </span></span>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="jQueryDifferentVersionsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="jQueryDifferentVersionsTooltip" dialog-title="Several versions of jQuery loaded">
|
|
|
|
- <p>jQuery is a heavy library. You should **never** load jQuery more than one on the same page.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <div ng-if="!phantomasResults.metrics.cssParsingErrors" class="A">A</div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssParsingErrors" class="F">F</div>
|
|
|
|
- <div class="notation">CSS syntax errors</div>
|
|
|
|
- <div ng-if="!phantomasResults.metrics.cssParsingErrors" class="criteria"></div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssParsingErrors" class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div class="warning">
|
|
|
|
- <div class="label">CSS files with syntax errors</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssParsingErrors}}
|
|
|
|
- <show-offenders modal-title="CSS syntax errors" metric-name="cssParsingErrors" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssParsingErrorsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssParsingErrorsTooltip" dialog-title="Parsing error while analyzing CSS">
|
|
|
|
- <p>Yellow Lab Tools failed to parse a CSS file. I doubt the problem comes from the css parser.</p>
|
|
|
|
- <p>
|
|
|
|
- Direct links to the <b>W3C CSS Validator</b> for the following stylesheet(s):
|
|
|
|
- <ul>
|
|
|
|
- <li ng-repeat="stylesheet in cssW3cDirectUrls">
|
|
|
|
- <a href="{{stylesheet.w3c}}" target="_blank">{{stylesheet.url}}</a>
|
|
|
|
- </li>
|
|
|
|
- </ul>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <div ng-class="notations.cssComplexity">{{notations.cssComplexity}}</div>
|
|
|
|
- <div class="notation">CSS complexity</div>
|
|
|
|
- <div class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssRules" ng-class="{'warning': phantomasResults.metrics.cssRules > 4000 || (phantomasResults.metrics.cssRules > 1000 && phantomasResults.metrics.cssRules > phantomasResults.metrics.DOMelementsCount)}">
|
|
|
|
- <div class="label">Rules count</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssRules}}
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssRulesTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssRulesTooltip" dialog-title="Clean up the CSS files">
|
|
|
|
- <p>Having a huge number of CSS rules hurts performances. If the number of CSS rules is higher than the number of DOM elements, there is clearly a problem.</p>
|
|
|
|
- <p>Huge stylesheets generally occur when the different pages of a website load all the CSS, concatenated in a single stylesheet, even if a large part of the rules are page-specific. Solution is to create one main CSS file with global rules and one custom files per page.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssComplexSelectors" ng-class="{'warning': phantomasResults.metrics.cssComplexSelectors > 2000}">
|
|
|
|
- <div class="label">Complex selectors</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssComplexSelectors}}
|
|
|
|
- <show-offenders modal-title="Complex selectors" metric-name="cssComplexSelectors" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssComplexSelectorsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssComplexSelectorsTooltip" dialog-title="Avoid complex selectors">
|
|
|
|
- <p>Complex selectors are CSS selectors with 4 or more expressions, like "#header ul li .foo".</p>
|
|
|
|
- <p>They are adding more work for the browser, and this could be avoided by simplifying selectors.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssComplexSelectorsByAttribute" ng-class="{'warning': phantomasResults.metrics.cssComplexSelectorsByAttribute > 100}">
|
|
|
|
- <div class="label">Complex attributes selector</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssComplexSelectorsByAttribute}}
|
|
|
|
- <show-offenders modal-title="Complex attributes selector" metric-name="cssComplexSelectorsByAttribute" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssComplexSelectorsByAttributeTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssComplexSelectorsByAttributeTooltip" dialog-title="Avoid complex attribute expressions">
|
|
|
|
- <p>Complex attributes selectors are one of these:
|
|
|
|
- <ul>
|
|
|
|
- <li>.foo[type*=bar] (contains bar)</li>
|
|
|
|
- <li>.foo[type^=bar] (starts with bar)</li>
|
|
|
|
- <li>.foo[type|=bar] (starts with bar or bar-)</li>
|
|
|
|
- <li>.foo[type$=bar] (ends with bar)</li>
|
|
|
|
- <li>.foo[type~=bar baz] (bar or baz)</li>
|
|
|
|
- </ul>
|
|
|
|
- </p>
|
|
|
|
- <p>Their matching process needs more CPU and it has a cost on performances.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssParsingErrors">
|
|
|
|
- <div class="label">(<ng-pluralize count="phantomasResults.metrics.cssParsingErrors" when="{'one': '1 file', 'other': '{} files'}"></ng-pluralize> couldn't be parsed)</div>
|
|
|
|
- <div class="result"></div>
|
|
|
|
- <div class="info"></div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <div ng-class="notations.badCss">{{notations.badCss}}</div>
|
|
|
|
- <div class="notation">Bad CSS</div>
|
|
|
|
- <div class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssImports" class="warning">
|
|
|
|
- <div class="label">Uses of @import</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssImports}}
|
|
|
|
- <show-offenders modal-title="Uses of @import" metric-name="cssImports" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssImportsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssImportsTooltip" dialog-title="Don't use @import">
|
|
|
|
- <p>It’s bad for performance to use @import because CSS files don't get downloaded in parallel.</p>
|
|
|
|
- <p>You should use <link rel='stylesheet' href='a.css'> instead.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssDuplicatedSelectors" ng-class="{'warning': phantomasResults.metrics.cssDuplicatedSelectors > 80}">
|
|
|
|
- <div class="label">Duplicated selectors</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssDuplicatedSelectors}}
|
|
|
|
- <show-offenders modal-title="Duplicated selectors" metric-name="cssDuplicatedSelectors" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssDuplicatedSelectorsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssDuplicatedSelectorsTooltip" dialog-title="Merge duplicated selectors">
|
|
|
|
- <p>This is when two or more selectors are strictly identical and should be merged.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssDuplicatedProperties" ng-class="{'warning': phantomasResults.metrics.cssDuplicatedProperties > 100}">
|
|
|
|
- <div class="label">Duplicated properties</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssDuplicatedProperties}}
|
|
|
|
- <show-offenders modal-title="Duplicated properties" metric-name="cssDuplicatedProperties" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssDuplicatedPropertiesTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssDuplicatedPropertiesTooltip" dialog-title="Remove duplicated properties">
|
|
|
|
- <p>This is the number of property definitions duplicated within a selector.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssEmptyRules" ng-class="{'warning': phantomasResults.metrics.cssEmptyRules > 100}">
|
|
|
|
- <div class="label">Empty rules</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssEmptyRules}}
|
|
|
|
- <show-offenders modal-title="Empty rules" metric-name="cssEmptyRules" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssEmptyRulesTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssEmptyRulesTooltip" dialog-title="Eliminate empty rules">
|
|
|
|
- <p>Very easy to fix.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssExpressions">
|
|
|
|
- <div class="label">CSS expressions</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssExpressions}}
|
|
|
|
- <show-offenders modal-title="CSS expressions" metric-name="cssExpressions" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssExpressionsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssExpressionsTooltip" dialog-title="Number of rules with CSS expressions">
|
|
|
|
- <p>Such as: expression( document.body.clientWidth > 600 ? "600px" : "auto" )</p>
|
|
|
|
- <p>This is a bad practice as it slows down browsers. There are some simpler CSS3 methods for doing this.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssImportants">
|
|
|
|
- <div class="label">Uses of !important</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssImportants}}
|
|
|
|
- <show-offenders modal-title="Uses of !important" metric-name="cssImportants" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssImportantsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssImportantsTooltip" dialog-title="Don't over-use !important">
|
|
|
|
- <p>It can be useful, but only as a last resort. It is a bad practice because it overrides the normal cascading logic. The more you use !important, the more you need it again to over-override. This conducts to a poor maintainability.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssOldIEFixes">
|
|
|
|
- <div class="label">Old IE fixes</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssOldIEFixes}}
|
|
|
|
- <show-offenders modal-title="Old IE fixes" metric-name="cssOldIEFixes" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssOldIEFixesTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssOldIEFixesTooltip" dialog-title="Clean up old IE fixes">
|
|
|
|
- <p>What browser do you need to support? Once you've got the answer, take a look at these old rules that pollute your CSS code and remove them.</p>
|
|
|
|
- <p>IE6:
|
|
|
|
- <ul>
|
|
|
|
- <li>* html</li>
|
|
|
|
- <li>html > body (everything but IE6)</li>
|
|
|
|
- </ul>
|
|
|
|
- <p>
|
|
|
|
- <p>IE7:
|
|
|
|
- <ul>
|
|
|
|
- <li><b>*</b>height: 123px;</li>
|
|
|
|
- <li>height: 123px <b>!ie</b>;</li>
|
|
|
|
- </ul>
|
|
|
|
- <p>
|
|
|
|
- <p>IE9:
|
|
|
|
- <ul>
|
|
|
|
- <li>-ms-filter</li>
|
|
|
|
- <li>progid:DXImageTransform.Microsoft</li>
|
|
|
|
- </ul>
|
|
|
|
- </p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssOldPropertyPrefixes">
|
|
|
|
- <div class="label">Old prefixes</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssOldPropertyPrefixes}}
|
|
|
|
- <show-offenders modal-title="Old prefixes" metric-name="cssOldPropertyPrefixes" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssOldPropertyPrefixesTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssOldPropertyPrefixesTooltip" dialog-title="Clean up old CSS3 prefixes">
|
|
|
|
- <p>Many property prefixes such as -moz- or -webkit- are not needed anymore, or by very few people. You can remove them or replace them with the non-prefixed version. This will help reducing your stylesheets weight.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssUniversalSelectors">
|
|
|
|
- <div class="label">Universal selectors</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssUniversalSelectors}}
|
|
|
|
- <show-offenders modal-title="Universal selectors" metric-name="cssUniversalSelectors" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssUniversalSelectorsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssUniversalSelectorsTooltip" dialog-title="Avoid universal selectors">
|
|
|
|
- <p>Universal selectors are the most expensive CSS selectors.</p>
|
|
|
|
- <p>More informations <a href="http://perfectionkills.com/profiling-css-for-fun-and-profit-optimization-notes/" target="_blank">here</a>.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssRedundantBodySelectors">
|
|
|
|
- <div class="label">Redundant body selectors</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssRedundantBodySelectors}}
|
|
|
|
- <show-offenders modal-title="Redundant body selectors" metric-name="cssRedundantBodySelectors" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssRedundantBodySelectorsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssRedundantBodySelectorsTooltip" dialog-title="Useless redundant selectors">
|
|
|
|
- <p>This is one way to remove complexity from a CSS rule. Generally, when "body" is specified in a rule it can be removed, because an element is necessarily inside the body.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssRedundantChildNodesSelectors">
|
|
|
|
- <div class="label">Redundant tags selectors</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssRedundantChildNodesSelectors}}
|
|
|
|
- <show-offenders modal-title="Redundant tags selectors" metric-name="cssRedundantChildNodesSelectors" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cssRedundantChildNodesSelectorsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cssRedundantChildNodesSelectorsTooltip" dialog-title="Useless redundant selectors">
|
|
|
|
- <p>Some tags included inside other tags are obvious. For example, when "ul li" is specified in a rule, "ul" can be removed because the "li" element is <b>always</b> inside a "ul". Same thing for "tr td", "select option", ...</p>
|
|
|
|
- <p>Lowering compexity in CSS selectors can make the page load a little faster.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssParsingErrors">
|
|
|
|
- <div class="label">(<ng-pluralize count="phantomasResults.metrics.cssParsingErrors" when="{'one': '1 file', 'other': '{} files'}"></ng-pluralize> couldn't be parsed)</div>
|
|
|
|
- <div class="result"></div>
|
|
|
|
- <div class="info"></div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <div ng-class="notations.requests">{{notations.requests}}</div>
|
|
|
|
- <div class="notation">Requests number</div>
|
|
|
|
- <div class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-class="{'warning': phantomasResults.metrics.requests > 200}">
|
|
|
|
- <div class="label">Total requests</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.requests}}
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="requestsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="requestsTooltip" dialog-title="Minimize the number of requests">
|
|
|
|
- <p>This is one of the most important performance rule. Every request is slowing down the page loading.</p>
|
|
|
|
- <p>There are several technics to reduce their number:
|
|
|
|
- <ul>
|
|
|
|
- <li>Concatenate JS files</li>
|
|
|
|
- <li>Concatenate CSS files</li>
|
|
|
|
- <li>Embed or inline small JS or CSS files in the HTML</li>
|
|
|
|
- <li>Create sprites or icon fonts</li>
|
|
|
|
- <li>Base64 encode small images in HTML or stylesheets</li>
|
|
|
|
- <li>Use lazyloading for images</li>
|
|
|
|
- </ul>
|
|
|
|
- </p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.htmlCount">
|
|
|
|
- <div class="label">Documents</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.htmlCount}}
|
|
|
|
- <show-offenders modal-title="HTML count" metric-name="htmlCount" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div></div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.jsCount" ng-class="{'warning': phantomasResults.metrics.jsCount > 40}">
|
|
|
|
- <div class="label">Scripts</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.jsCount}}
|
|
|
|
- <show-offenders modal-title="JS count" metric-name="jsCount" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div></div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cssCount" ng-class="{'warning': phantomasResults.metrics.cssCount > 25}">
|
|
|
|
- <div class="label">Stylesheets</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cssCount}}
|
|
|
|
- <show-offenders modal-title="CSS count" metric-name="cssCount" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div></div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.imageCount" ng-class="{'warning': phantomasResults.metrics.imageCount > 80}">
|
|
|
|
- <div class="label">Images</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.imageCount}}
|
|
|
|
- <show-offenders modal-title="Image count" metric-name="imageCount" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div></div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.webfontCount" ng-class="{'warning': phantomasResults.metrics.webfontCount > 4}">
|
|
|
|
- <div class="label">Fonts</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.webfontCount}}
|
|
|
|
- <show-offenders modal-title="Webfont count" metric-name="webfontCount" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div></div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.videoCount">
|
|
|
|
- <div class="label">Videos</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.videoCount}}
|
|
|
|
- <show-offenders modal-title="Video count" metric-name="videoCount" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div></div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.jsonCount">
|
|
|
|
- <div class="label">JSON</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.jsonCount}}
|
|
|
|
- <show-offenders modal-title="JSON count" metric-name="jsonCount" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div></div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.otherCount">
|
|
|
|
- <div class="label">Other</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.otherCount}}
|
|
|
|
- <show-offenders modal-title="Other count" metric-name="otherCount" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div></div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <div ng-class="notations.network">{{notations.network}}</div>
|
|
|
|
- <div class="notation">Network</div>
|
|
|
|
- <div class="criteria">
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-if="phantomasResults.metrics.notFound" class="warning">
|
|
|
|
- <div class="label">404 not found</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.notFound}}
|
|
|
|
- <show-offenders modal-title="404 not found" metric-name="notFound" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="notFoundTooltip = true"></div>
|
|
|
|
- <modal-dialog show="notFoundTooltip" dialog-title="404 errors">
|
|
|
|
- <p>404 errors are never cached, so each time a page ask for it, it hits se server. Even if it is behind a CDN or a reverse-proxy cache.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.closedConnections" ng-class="{'warning': phantomasResults.metrics.closedConnections > 20}">
|
|
|
|
- <div class="label">Connections closed</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.closedConnections}}
|
|
|
|
- <show-offenders modal-title="Connections closed" metric-name="closedConnections" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="closedConnectionsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="closedConnectionsTooltip" dialog-title="Connection keep-alive is important">
|
|
|
|
- <p>This counts the number of requests not keeping the connection alive (specifying "Connection: close" in the response headers). It is only counting a request if it is followed by another request on the same domain.</p>
|
|
|
|
- <p>This is slowing down the next request, because the brower needs to open a new connection to the server, which means a additional round-trip.</p>
|
|
|
|
- <p>Correct the problem by setting a Keep-Alive header on the guilty server.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.multipleRequests" ng-class="{'warning': phantomasResults.metrics.multipleRequests > 10}">
|
|
|
|
- <div class="label">Duplicated requests</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.multipleRequests}}
|
|
|
|
- <show-offenders modal-title="Static assets requested more than once" metric-name="multipleRequests" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="multipleRequestsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="multipleRequestsTooltip" dialog-title="The same file is requested twice">
|
|
|
|
- <p>This only happens when the asset has no cache and is requested more than once on the same page. Be very careful about it.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cachingDisabled" ng-class="{'warning': phantomasResults.metrics.cachingDisabled > 25}">
|
|
|
|
- <div class="label">Caching disabled</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cachingDisabled}}
|
|
|
|
- <show-offenders modal-title="Caching disabled" metric-name="cachingDisabled" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cachingDisabledTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cachingDisabledTooltip" dialog-title="Caching disabled">
|
|
|
|
- <p>Counts responses with caching disabled (max-age=0)</p>
|
|
|
|
- <p>Fix immediatly if on static assets.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cachingNotSpecified" ng-class="{'warning': phantomasResults.metrics.cachingNotSpecified > 50}">
|
|
|
|
- <div class="label">Caching not specified</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cachingNotSpecified}}
|
|
|
|
- <show-offenders modal-title="Caching not specified" metric-name="cachingNotSpecified" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cachingNotSpecifiedTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cachingNotSpecifiedTooltip" dialog-title="No caching header">
|
|
|
|
- <p>Responses with no caching header sent (either Cache-Control or Expires).</p>
|
|
|
|
- <p>Every request should have a cache time specified. If you really don't want cache, specify "max-age=0", otherwise some browsers will try to cache.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.cachingTooShort">
|
|
|
|
- <div class="label">Caching too short</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.cachingTooShort}}
|
|
|
|
- <show-offenders modal-title="Caching too short" metric-name="cachingTooShort" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="cachingTooShortTooltip = true"></div>
|
|
|
|
- <modal-dialog show="cachingTooShortTooltip" dialog-title="Increase cache time">
|
|
|
|
- <p>Responses with too short caching time (less than a week).</p>
|
|
|
|
- <p>The longer you cache, the better. Add versionning to your static assets, if it's not already done, and set their cache time to one year.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="phantomasResults.metrics.domains" ng-class="{'warning': phantomasResults.metrics.domains > 40}">
|
|
|
|
- <div class="label">Different domains</div>
|
|
|
|
- <div class="result">
|
|
|
|
- {{phantomasResults.metrics.domains}}
|
|
|
|
- <show-offenders modal-title="Different domains" metric-name="domains" phantomas-results="phantomasResults"></show-offenders>
|
|
|
|
- </div>
|
|
|
|
- <div class="info">
|
|
|
|
- <div class="icon-question" ng-click="domainsTooltip = true"></div>
|
|
|
|
- <modal-dialog show="domainsTooltip" dialog-title="Reduce the number of domains">
|
|
|
|
- <p>For each domain met, the browser needs to make a DNS look-up, which is slow. Avoid having to many different domains and the page should render faster.</p>
|
|
|
|
- <p>By the way, domain sharding is not a good practice anymore.</p>
|
|
|
|
- </modal-dialog>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div ng-show="view == 'execution' && !phantomasResults.error" class="execution">
|
|
|
|
- <h2>Javascript Timeline</h2>
|
|
|
|
- <p>
|
|
|
|
- This graph gives a quick view of when the Javascript interactions with the DOM occur during the loading of the page.
|
|
|
|
- </p>
|
|
|
|
- <div class="timeline">
|
|
|
|
- <div class="chart">
|
|
|
|
- <div class="chartPoints">
|
|
|
|
- <div ng-repeat="duration in timeline track by $index"
|
|
|
|
- class="interval"
|
|
|
|
- ng-class="{
|
|
|
|
- domCreation: $index * timelineIntervalDuration < phantomasResults.metrics.domInteractive,
|
|
|
|
- domInteractive: $index * timelineIntervalDuration >= phantomasResults.metrics.domInteractive
|
|
|
|
- && $index * timelineIntervalDuration < phantomasResults.metrics.domContentLoaded,
|
|
|
|
- domContentLoaded: $index * timelineIntervalDuration >= phantomasResults.metrics.domContentLoaded
|
|
|
|
- && $index * timelineIntervalDuration < phantomasResults.metrics.domContentLoadedEnd,
|
|
|
|
- domContentLoadedEnd: $index * timelineIntervalDuration >= phantomasResults.metrics.domContentLoadedEnd
|
|
|
|
- && $index * timelineIntervalDuration < phantomasResults.metrics.domComplete,
|
|
|
|
- domComplete: $index * timelineIntervalDuration >= phantomasResults.metrics.domComplete
|
|
|
|
- }">
|
|
|
|
- <div style="height: {{100 * duration / timelineMax | number: 0}}px" class="color"></div>
|
|
|
|
- <div class="tooltip detailsOverlay">
|
|
|
|
- <div>Timestamp: {{$index * timelineIntervalDuration | number: 0}} ms</div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div class="startTime">0 ms</div>
|
|
|
|
- <div class="endTime">{{endTime | number: 0}} ms</div>
|
|
|
|
- </div>
|
|
|
|
- <div class="legend">
|
|
|
|
- <div class="titles">
|
|
|
|
- <div class="domCreation"><div class="color"></div>DOM creation</div>
|
|
|
|
- <div class="domInteractive"><div class="color"></div>DOM interactive</div>
|
|
|
|
- <div class="domContentLoaded"><div class="color"></div>DOM content loaded event</div>
|
|
|
|
- <div class="domContentLoadedEnd"><div class="color"></div>Page completion</div>
|
|
|
|
- <div class="domComplete"><div class="color"></div>Page is complete</div>
|
|
|
|
- </div>
|
|
|
|
- <div class="tips">
|
|
|
|
- <div>Executing Javascript and DOM queries here is a <b>bad practice</b> and slows down the DOM construction.</div>
|
|
|
|
- <div>Some frameworks do things here, but it's not reliable and should be avoided.</div>
|
|
|
|
- <div>Also known as "document ready". This is where you should execute <b>top-priority</b> scripts, like binding action buttons or launch a video player.</div>
|
|
|
|
- <div>Here you can execute <b>mid-priority</b> tasks. Loading a script with createElement('script') is one way to do so.</div>
|
|
|
|
- <div>The page is considered loaded, it's time for low <b>priority things</b> : trackers, social plugins, easter egg...</div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <h2>Javascript Profiler</h2>
|
|
|
|
- <p>
|
|
|
|
- The table below shows the interactions between Javascript and the DOM. It is useful to understand what happens while the page loads.
|
|
|
|
- </p>
|
|
|
|
- <div class="filters">
|
|
|
|
- <div>
|
|
|
|
- <input type="checkbox" ng-model="warningsFilterOn" id="warningsFilterOn" />
|
|
|
|
- <label for="warningsFilterOn">Show warnings only</label>
|
|
|
|
- </div>
|
|
|
|
- <div>
|
|
|
|
- <input type="checkbox" ng-model="textFilterOn" />
|
|
|
|
- Filter by
|
|
|
|
- <input type="text" ng-model="textFilter" placeholder="search..." class="textFilter" ng-change="textFilterOn = true" />
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div class="table">
|
|
|
|
-
|
|
|
|
- <toto data-info="mavariable"></toto>
|
|
|
|
- <div class="headers">
|
|
|
|
- <div><!-- index --></div>
|
|
|
|
- <div>Type</div>
|
|
|
|
- <div>Params</div>
|
|
|
|
- <div><!-- details --></div>
|
|
|
|
- <div>Timestamp</div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-repeat="node in javascript.children"
|
|
|
|
- ng-if="(!warningsFilterOn
|
|
|
|
- || (node.data.type == 'jQuery - bind' && node.data.callDetails.context.length > 5)
|
|
|
|
- || node.data.resultsNumber === 0
|
|
|
|
- || node.data.callDetails.context.length === 0
|
|
|
|
- || node.data.type == 'error'
|
|
|
|
- || node.data.type == 'jQuery version change')
|
|
|
|
- && (!textFilterOn
|
|
|
|
- || !textFilter.length
|
|
|
|
- || node.data.type.indexOf(textFilter) >= 0
|
|
|
|
- || node.data.callDetails.arguments[0].indexOf(textFilter) >= 0
|
|
|
|
- || node.data.callDetails.arguments[1].indexOf(textFilter) >= 0
|
|
|
|
- || node.data.callDetails.arguments[2].indexOf(textFilter) >= 0
|
|
|
|
- || node.data.callDetails.arguments[3].indexOf(textFilter) >= 0)"
|
|
|
|
- ng-class="{
|
|
|
|
- showingDetails: node.data.showDetails,
|
|
|
|
- jsError: node.data.type == 'error' || node.data.type == 'jQuery version change',
|
|
|
|
- windowPerformance: node.data.type == 'domInteractive' || node.data.type == 'domContentLoaded' || node.data.type == 'domContentLoadedEnd' || node.data.type == 'domComplete'
|
|
|
|
- }">
|
|
|
|
- <div class="index">{{$index + 1}}</div>
|
|
|
|
- <div class="type">{{node.data.type}}</div>
|
|
|
|
-
|
|
|
|
- <div class="value">
|
|
|
|
- {{node.data.callDetails.arguments[0]}}
|
|
|
|
- <span ng-if="node.data.callDetails.arguments.length > 1"> : {{node.data.callDetails.arguments[1]}}</span>
|
|
|
|
- <span ng-if="node.data.callDetails.arguments.length > 2"> : {{node.data.callDetails.arguments[2]}}</span>
|
|
|
|
- <span ng-if="node.data.callDetails.arguments.length > 3"> : {{node.data.callDetails.arguments[3]}}</span>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div class="details">
|
|
|
|
- <div ng-class="{
|
|
|
|
- 'icon-question': !(node.data.type == 'jQuery - bind' && node.data.callDetails.context.length > 5) && node.data.resultsNumber !== 0 && node.data.callDetails.context.length !== 0,
|
|
|
|
- 'icon-warning': node.data.type == 'jQuery - bind' && node.data.callDetails.context.length > 5 || node.data.resultsNumber === 0 || node.data.callDetails.context.length === 0
|
|
|
|
- }"
|
|
|
|
- ng-click="onNodeDetailsClick(node)"
|
|
|
|
- ng-if="node.data.type != 'jQuery loaded'
|
|
|
|
- && node.data.type != 'jQuery version change'
|
|
|
|
- && node.data.type != 'domInteractive'
|
|
|
|
- && node.data.type != 'domContentLoaded'
|
|
|
|
- && node.data.type != 'domContentLoadedEnd'
|
|
|
|
- && node.data.type != 'domComplete'"></div>
|
|
|
|
- <div class="detailsOverlay" ng-show="node.data.showDetails">
|
|
|
|
- <div class="closeBtn" ng-click="onNodeDetailsClick(node)">✖</div>
|
|
|
|
-
|
|
|
|
- <div ng-if="node.data.callDetails.context.domElement">
|
|
|
|
- <h4>Called on DOM element</h4>
|
|
|
|
- <div>{{node.data.callDetails.context.domElement}}</div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div ng-if="node.data.callDetails.context.length === 0">
|
|
|
|
- <h4>Called on 0 jQuery element</h4>
|
|
|
|
- <p class="advice">Useless function call, as the jQuery object is empty.</p>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div ng-if="node.data.callDetails.context.length == 1 && node.data.callDetails.context.firstElementPath">
|
|
|
|
- <h4>Called on 1 jQuery element</h4>
|
|
|
|
- <div>{{node.data.callDetails.context.firstElementPath}}</div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div ng-if="node.data.callDetails.context.length > 1">
|
|
|
|
- <h4>Called on {{node.data.callDetails.context.length}} jQuery elements</h4>
|
|
|
|
- <p class="advice" ng-if="node.data.type == 'jQuery - bind' && node.data.callDetails.context.length > 5">
|
|
|
|
- The .bind() method attaches the event listener to each jQuery element one by one. Using the .on() method is preferable if available (from v1.7).
|
|
|
|
- </p>
|
|
|
|
- <p ng-if="node.data.callDetails.context.firstElementPath"><b>First one is:</b> {{node.data.callDetails.context.firstElementPath}}</p>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <p class="advice" ng-if="node.data.resultsNumber === 0">
|
|
|
|
- The query returned 0 results. Could it be unused or dead code?
|
|
|
|
- </p>
|
|
|
|
-
|
|
|
|
- <div ng-if="node.data.parsedBacktrace">
|
|
|
|
- <h4>Backtrace</h4>
|
|
|
|
- <div class="table">
|
|
|
|
- <div ng-repeat="trace in node.data.parsedBacktrace track by $index">
|
|
|
|
- <div>{{trace.fnName || '(anonymous)'}}</div>
|
|
|
|
- <div class="trace"><a href="{{trace.filePath}}" title="{{trace.filePath}}" target="_blank">{{trace.fileName || 'HTML'}}</a>:{{trace.line}}</div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-if="node.data.parsedBacktrace.length == 0 && node.data.type != 'script loaded'">
|
|
|
|
- <div>can't find any backtrace :/</div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div ng-if="node.children.length > 0">
|
|
|
|
- <h4>Sub processes</h4>
|
|
|
|
- <div class="table">
|
|
|
|
- <div class="headers">
|
|
|
|
- <div><!-- index --></div>
|
|
|
|
- <div>Type</div>
|
|
|
|
- <div>Params</div>
|
|
|
|
- <div>Duration</div>
|
|
|
|
- </div>
|
|
|
|
- <div ng-repeat="node in node.children">
|
|
|
|
- <div class="index">{{$index}}</div>
|
|
|
|
- <div class="type">{{node.data.type}}</div>
|
|
|
|
- <div class="value">
|
|
|
|
- {{node.data.callDetails.arguments[0]}}
|
|
|
|
- <span ng-if="node.data.callDetails.arguments.length > 1"> : {{node.data.callDetails.arguments[1]}}</span>
|
|
|
|
- <span ng-if="node.data.callDetails.arguments.length > 2"> : {{node.data.callDetails.arguments[2]}}</span>
|
|
|
|
- <span ng-if="node.data.callDetails.arguments.length > 3"> : {{node.data.callDetails.arguments[3]}}</span>
|
|
|
|
- </div>
|
|
|
|
- <div class="duration" ng-if="node.data.time != undefined">{{node.data.time}} ms</div>
|
|
|
|
- <div class="duration" ng-if="node.data.time == undefined"></div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- <div class="startTime" ng-class="node.data.loadingStep">{{node.data.timestamp | number: 0}} ms</div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div class="footer">
|
|
|
|
- <p><b>Yellow Lab Tools</b> is an open source project by <a href="http://www.gaelmetais.com" target="_blank">Gaël Métais</a>, based on <a href="https://github.com/macbre/phantomas" target="_blank">Phantomas</a>.<br>If you like it, <a href="https://github.com/gmetais/YellowLabTools" target="_blank" class="star">give it a <span>★</span> on GitHub</a>!</p>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <script>
|
|
|
|
-
|
|
|
|
- var _phantomas_results = %%RESULTS%%;
|
|
|
|
-
|
|
|
|
- var GA_ID = '%%GA_ID%%';
|
|
|
|
- if (GA_ID.length > 0 && window.location.host === 'yellowlab.tools') {
|
|
|
|
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
|
|
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
|
|
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
|
|
- })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
|
|
-
|
|
|
|
- ga('create', GA_ID, 'auto');
|
|
|
|
- ga('send', 'pageview');
|
|
|
|
- }
|
|
|
|
- </script>
|
|
|
|
-</body>
|
|
|
|
-</html>
|
|
|