378 lines
22 KiB
HTML
378 lines
22 KiB
HTML
<div ng-include="'views/resultSubHeader.html'"></div>
|
|
<div class="rule board">
|
|
<div class="backToDashboard"><a href="#" ng-click="backToDashboard()">Back to dashboard</a></div>
|
|
|
|
<div ng-if="rule" class="ruleTable">
|
|
<div class="left">
|
|
<h2>{{rule.policy.label}}</h2>
|
|
<grade score="rule.score" class="score"></grade>
|
|
<div>{{rule.score}}/100</div>
|
|
</div>
|
|
<div class="right">
|
|
<h3>
|
|
Value:
|
|
<span ng-if="rule.policy.unit == 'bytes'">{{rule.value | bytes}}</span>
|
|
<span ng-if="rule.policy.unit != 'bytes'">{{rule.value}}</span>
|
|
</h3>
|
|
<div class="okThreshold" ng-if="rule.score < 100 && rule.policy.isOkThreshold !== undefined">
|
|
Have
|
|
<span ng-if="rule.policy.unit == 'bytes'">{{rule.policy.isOkThreshold | bytes}}</span>
|
|
<span ng-if="rule.policy.unit != 'bytes'">{{rule.policy.isOkThreshold}}</span>
|
|
<span ng-if="rule.policy.isOkThreshold > 0 && rule.policy.isOkThreshold < rule.policy.isBadThreshold">or less</span>
|
|
<span ng-if="rule.policy.isOkThreshold > rule.policy.isBadThreshold">or more</span>
|
|
to get the 100/100 score.
|
|
</div>
|
|
<div ng-bind-html="rule.policy.message" class="message"></div>
|
|
</div>
|
|
</div>
|
|
<div ng-if="rule.abnormal" class="warning">
|
|
<h3>Warning</h3>
|
|
<p>This rule reached the abnormality threshold, which means there is a real problem you should care about.</p>
|
|
</div>
|
|
<div class="offenders" ng-if="rule.policy.hasOffenders">
|
|
<h3><ng-pluralize count="rule.offendersObj.count" when="{'0': 'No offenders', 'one': '1 offender', 'other': '{} offenders'}"></ng-pluralize></h3>
|
|
|
|
<div ng-if="rule.offendersObj.list" class="offendersTable">
|
|
<div ng-repeat="offender in rule.offendersObj.list track by $index">
|
|
<div ng-if="offender.parseError">
|
|
{{offender.parseError}}
|
|
</div>
|
|
<div ng-if="!offender.parseError">
|
|
|
|
<div ng-if="policyName === 'DOMidDuplicated'">
|
|
<b>{{offender.id}}</b>: {{offender.occurrences}} occurrences
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'DOMqueriesAvoidable'">
|
|
<b>{{offender.query}}</b> (in <dom-element-button obj="offender.context"></dom-element-button>) using {{offender.fn}}: <b>{{offender.count}} queries</b>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'eventsScrollBound'">
|
|
<span ng-if="offender.target == 'window'">Scroll event bound on <b>window</b></span>
|
|
<span ng-if="offender.target == '#document'">Scroll event bound on <b>document</b></span>
|
|
<span ng-if="offender.target == 'window.onscroll'"><b>window.onscroll</b> function declared</span>
|
|
<div class="offenderButton" ng-if="offender.backtrace.length == 0">no backtrace</div>
|
|
<div class="offenderButton opens" ng-if="offender.backtrace.length > 0">
|
|
backtrace
|
|
<div class="backtrace">
|
|
<div ng-repeat="obj in offender.backtrace track by $index">
|
|
<span ng-if="obj.functionName">{{obj.functionName}}()</span>
|
|
<url-link url="obj.file" max-length="60"></url-link>
|
|
line {{obj.line}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'jsErrors'">
|
|
<b>{{offender.error}}</b>
|
|
<div class="offenderButton" ng-if="offender.backtrace.length == 0">no backtrace</div>
|
|
<div class="offenderButton opens" ng-if="offender.backtrace.length > 0">
|
|
backtrace
|
|
<div class="backtrace">
|
|
<div ng-repeat="obj in offender.backtrace track by $index">
|
|
<span ng-if="obj.functionName">{{obj.functionName}}()</span>
|
|
<url-link url="obj.file" max-length="60"></url-link>
|
|
line {{obj.line}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'jQueryFunctionsUsed'">
|
|
function <b>{{offender.functionName}}</b> used {{offender.count}} times
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'documentWriteCalls'">
|
|
<b>{{offender.writeFn}}</b>
|
|
<span ng-if="offender.from">
|
|
called from
|
|
<span ng-if="offender.from.functionName">{{offender.from.functionName}}()</span>
|
|
<url-link url="offender.from.file" max-length="50"></url-link>
|
|
line {{offender.from.line}}
|
|
</span>
|
|
<span ng-if="!offender.from">
|
|
called from (no backtrace available)
|
|
</span>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'similarColors'">
|
|
<div class="similarColors checker"><div ng-style="{'background-color': offender.color1, 'color': offender.isDark ? '#FFF' : '#000'}">{{offender.color1}}</div><div ng-style="{'background-color': offender.color2, 'color': offender.isDark ? '#FFF' : '#000'}">{{offender.color2}}</div></div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssMobileFirst'">
|
|
<b>{{offender.query}}</b> for <ng-pluralize count="offender.rules" when="{'one':'1 rule','other':'{} rules'}"></ng-pluralize>
|
|
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssParsingErrors'">
|
|
<b>{{offender.error}}</b>
|
|
<file-and-line file="offender.file" line="offender.line" column="offender.column"></file-and-line>
|
|
<span ng-if="offender.file">(<a href="http://jigsaw.w3.org/css-validator/validator?profile=css3&usermedium=all&warning=no&uri={{offender.file | encodeURIComponent}}" target="_blank">Check on the W3C validator</a>)</span>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssComplexSelectors' || policyName === 'cssComplexSelectorsByAttribute' || policyName === 'cssImports' || policyName === 'cssUniversalSelectors' || policyName === 'cssRedundantBodySelectors' || policyName === 'cssRedundantChildNodesSelectors'">
|
|
<span ng-if="offender.bolded" ng-bind-html="offender.bolded"></span>
|
|
<b ng-if="!offender.bolded">{{offender.css}}</b>
|
|
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssDuplicatedSelectors'">
|
|
{{offender.rule}} (<b>x{{offender.occurrences}}</b>)
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssDuplicatedProperties'">
|
|
Property <b>{{offender.property}}</b> duplicated in <b>{{offender.rule}} { }</b>
|
|
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssEmptyRules'">
|
|
<b>{{offender.css}} { }</b>
|
|
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssExpressions'">
|
|
{{offender.rule}} {{ '{' + offender.property}}: <b>expression(</b>{{offender.expression}}<b>)</b>}
|
|
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssImportants'">
|
|
{{offender.rule}} {{ '{' + offender.property}}: {{offender.value}} <b>!important</b>}
|
|
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssOldIEFixes'">
|
|
<span ng-if="offender.browser"><b>{{offender.browser}} fix:</b></span>
|
|
<span ng-bind-html="offender.bolded"></span>
|
|
<file-and-line-button file="offender.file" line="offender.line" column="offender.column"></file-and-line-button>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssOldPropertyPrefixes'">
|
|
<b>{{offender.property}} {{offender.message}}</b>
|
|
<div ng-if="offender.rules.length" ng-click="offender.showMore = !offender.showMore" class="offenderButton">
|
|
<span ng-if="!offender.showMore">show</span>
|
|
<span ng-if="offender.showMore">hide</span>
|
|
<ng-pluralize count="offender.rules.length" when="{'one':'1 offender','other':'{} offenders'}"></ng-pluralize>
|
|
</div>
|
|
<div ng-if="offender.showMore" class="smallerOffenders">
|
|
<div ng-repeat="cssRule in offender.rules">
|
|
{{cssRule.rule}} {{'{' + offender.property}}: {{cssRule.value + '}' }}
|
|
<file-and-line-button file="cssRule.file" line="cssRule.line" column="cssRule.column"></file-and-line-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'lazyLoadableImagesBelowTheFold' || policyName === 'hiddenImages'">
|
|
<img ng-src="{{offender}}" class="smallPreview checker"></img>
|
|
<url-link url="offender" max-length="100"></url-link>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'notFound' || policyName === 'closedConnections' || policyName === 'multipleRequests' || policyName === 'cachingDisabled' || policyName === 'cachingNotSpecified'">
|
|
<url-link url="offender" max-length="100"></url-link>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cachingTooShort'">
|
|
<url-link url="offender.file" max-length="100"></url-link>
|
|
cached for <b>{{offender.ttlWithUnit}} {{offender.unit}}</b>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'domains'">
|
|
<b>{{offender.domain}}</b>
|
|
(<ng-pluralize count="offender.requests" when="{'one':'1 request','other':'{} requests'}"></ng-pluralize>)
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'globalVariables' || policyName === 'jQueryVersionsLoaded'">
|
|
{{offender}}
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="!rule.offendersObj.list" class="offendersHtml">
|
|
|
|
<div ng-if="policyName === 'DOMelementMaxDepth'">
|
|
<dom-tree tree="rule.offendersObj.tree"></dom-tree>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'DOMaccessesOnScroll' && rule.offendersObj.children.length > 0">
|
|
<p>The table below shows the interactions between the JavaScript and the DOM on a scroll event.</p>
|
|
<div class="table" ng-class="{warningsFilterOn: warningsFilterOn}">
|
|
<div class="headers">
|
|
<div><!-- index --></div>
|
|
<div>Type</div>
|
|
<div>Params</div>
|
|
<div><!-- details --></div>
|
|
</div>
|
|
<profiler-line ng-repeat="node in rule.offendersObj.children" data-index="$index" node="node"></profiler-line>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssColors' && rule.offendersObj.count > 0">
|
|
<p>This is the colors palette, sized by total occurrences:</p>
|
|
<div class="colorPalette checker">
|
|
<div ng-repeat="offender in rule.offendersObj.palette" style="background-color: {{offender.color}}; width: {{offender.occurrences * 100 / rule.offendersObj.palette[0].occurrences}}%"><div>{{offender.color}} ({{offender.occurrences}} times)</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'totalWeight'">
|
|
<h3>Weight by MIME type</h3>
|
|
<div class="totalWeightPie">
|
|
<canvas class="chart chart-doughnut" data="weightData" labels="weightLabels" options="weightOptions" colours="weightColours" legend="true"></canvas>
|
|
</div>
|
|
<div ng-repeat="type in weightLabels">
|
|
<h3>{{rule.offendersObj.list.byType[type].totalWeight | bytes}} of {{type}}</h3>
|
|
<div class="offendersTable">
|
|
<div ng-repeat="request in rule.offendersObj.list.byType[type].requests | orderBy:'-weight'" ng-if="request.weight > 0">
|
|
<div><url-link url="request.url" max-length="60"></url-link></div>
|
|
<div ng-class="{hugeFile: request.weight > 102400}">{{request.weight | bytes}}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'imageOptimization'">
|
|
<h3 ng-if="rule.value > 0">{{rule.value | bytes}} could be saved on <ng-pluralize count="rule.offendersObj.list.images.length" when="{'one': '1 image', 'other': '{} images'}"></ng-pluralize></h3>
|
|
<div class="imageOffenders">
|
|
<div ng-repeat="image in rule.offendersObj.list.images | orderBy:'-gain'">
|
|
<div>
|
|
Current file: <url-link url="image.url" max-length="50"></url-link>
|
|
<div><a href="{{image.url}}" target="_blank"><img ng-src="{{image.url}}" class="checker" /></a></div>
|
|
</div>
|
|
<div>
|
|
<p ng-if="!image.afterCompression">Current weight: {{image.original | bytes}}</p>
|
|
<p ng-if="image.afterCompression">Current weight: {{image.original | bytes}} ({{image.afterCompression | bytes}} gzipped)</p>
|
|
<p ng-if="image.lossless && image.afterOptimizationAndCompression">With a lossless optimization:<br/>{{image.afterOptimizationAndCompression | bytes}} gzipped (<b>-{{image.gain | bytes}}</b> gzipped)</p>
|
|
<p ng-if="image.lossless && !image.afterOptimizationAndCompression">With a lossless optimization:<br/>{{image.lossless | bytes}} <span ng-if="!image.lossy">(<b>-{{image.gain | bytes}}</b>)</span></p>
|
|
<p ng-if="image.lossy && image.afterOptimizationAndCompression">With a lossy optimization:<br/>{{image.afterOptimizationAndCompression | bytes}} gzipped (<b>-{{image.gain | bytes}} gzipped</b>)</p>
|
|
<p ng-if="image.lossy && !image.afterOptimizationAndCompression">With a lossy optimization:<br/>{{image.lossy | bytes}} (<b>-{{image.gain | bytes}}</b>)</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'gzipCompression'">
|
|
<h3 ng-if="rule.value > 0">{{rule.value | bytes}} could be saved on <ng-pluralize count="rule.offendersObj.list.images.length" when="{'one': '1 file', 'other': '{} files'}"></ng-pluralize></h3>
|
|
<div class="table">
|
|
<div class="headers">
|
|
<div>File</div>
|
|
<div>Current weight</div>
|
|
<div>Gzipped</div>
|
|
<div>Gain</div>
|
|
</div>
|
|
<div ng-repeat="file in rule.offendersObj.list.files | orderBy:'-gain'">
|
|
<div>
|
|
<url-link url="file.url" max-length="70"></url-link>
|
|
</div>
|
|
<div>{{file.original | bytes}}</div>
|
|
<div>{{file.gzipped | bytes}}</div>
|
|
<div><b>-{{file.gain | bytes}}</b></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'fileMinification'">
|
|
<h3 ng-if="rule.value > 0">{{rule.value | bytes}} could be saved on <ng-pluralize count="rule.offendersObj.list.files.length" when="{'one': '1 file', 'other': '{} files'}"></ng-pluralize></h3>
|
|
<div class="table">
|
|
<div class="headers">
|
|
<div>File</div>
|
|
<div>Current weight</div>
|
|
<div>Minified</div>
|
|
<div>Gain</div>
|
|
</div>
|
|
<div ng-repeat="file in rule.offendersObj.list.files | orderBy:'-gain'">
|
|
<div>
|
|
<url-link url="file.url" max-length="70"></url-link>
|
|
</div>
|
|
<div ng-if="!file.afterCompression">{{file.original | bytes}} (gzipped)</div>
|
|
<div ng-if="file.afterCompression">{{file.original | bytes}} ({{file.afterCompression | bytes}} gzipped)</div>
|
|
<div ng-if="!file.afterCompression">{{file.afterOptimizationAndCompression | bytes}} (gzipped)</div>
|
|
<div ng-if="file.afterCompression">{{file.optimized | bytes}} ({{file.afterOptimizationAndCompression | bytes}} gzipped)</div>
|
|
<div><b>-{{file.gain | bytes}}</b></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'totalRequests'">
|
|
<h3>Requests by MIME type</h3>
|
|
<div ng-repeat="(type, requests) in rule.offendersObj.list.byType">
|
|
<h3><ng-pluralize count="requests.length" when="{'0': 'No ' + type + ' request', 'one': '1 ' + type + ' request', 'other': '{} ' + type + ' requests'}"></ng-pluralize></h3>
|
|
<p ng-if="type == 'css' && requests.length > 2">Reduce the number of stylesheets by concatenating them.</p>
|
|
<p ng-if="type == 'js' && requests.length > 3">Reduce the number of scripts by concatenating them.</p>
|
|
<p ng-if="type == 'image' && requests.length > 5">Reduce the number of images by lazyloading them or by spriting them.</p>
|
|
<p ng-if="type == 'webfont' && requests.length > 1">Fonts are generally loaded on the critical path of the head. Load as few as possible.</p>
|
|
<p ng-if="type == 'other' && requests.length > 0">They can be Flash, XML, music or any undetected format.</p>
|
|
<div class="offendersTable">
|
|
<div ng-repeat="request in requests track by $index">
|
|
<div><url-link url="request" max-length="100"></url-link></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'smallRequests'">
|
|
<div ng-repeat="(type, requests) in rule.offendersObj.list.byType">
|
|
<h3><ng-pluralize count="requests.length" when="{'0': 'small ' + type + ' file', 'one': '1 small ' + type + ' file', 'other': '{} small ' + type + ' files'}"></ng-pluralize></h3>
|
|
<p ng-if="type == 'css' && requests.length > 0">Try to inline these styles in the head of the HTML or to merge them with other files.</p>
|
|
<p ng-if="type == 'js' && requests.length > 0">Try to inline these scripts in the HTML or merge them with other files.</p>
|
|
<p ng-if="type == 'image' && requests.length > 0">Try to inline these images (with base64 encoding for most image types except SVG that don't need base64 encoding). You can also create sprites.</p>
|
|
<div class="table">
|
|
<div class="headers">
|
|
<div ng-if="type == 'image'">Preview</div>
|
|
<div>File</div>
|
|
<div>Weight (bytes)</div>
|
|
</div>
|
|
<div ng-repeat="request in requests track by $index">
|
|
<div ng-if="type == 'image'"><img ng-src="{{request.url}}" class="smallPreview checker" /></div>
|
|
<div><url-link url="request.url" max-length="100"></url-link></div>
|
|
<div>{{request.size}}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'DOMaccesses'">
|
|
<h3>{{rule.value}} offenders</h3>
|
|
Please open the <a href="/result/{{runId}}/timeline">JS timeline</a>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'queriesWithoutResults'">
|
|
<h3>{{rule.value}} offenders</h3>
|
|
Please open the <a href="/result/{{runId}}/timeline#filter=queryWithoutResults">JS timeline, filtered by "Queries without results"</a>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'jQueryCallsOnEmptyObject'">
|
|
<h3>{{rule.value}} offenders</h3>
|
|
Please open the <a href="/result/{{runId}}/timeline#filter=jQueryCallOnEmptyObject">JS timeline, filtered by "jQuery calls on empty object"</a>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'jQueryNotDelegatedEvents'">
|
|
<h3>{{rule.value}} offenders</h3>
|
|
Please open the <a href="/result/{{runId}}/timeline#filter=eventNotDelegated">JS timeline, filtered by "Events not delegated"</a>
|
|
</div>
|
|
|
|
<div ng-if="policyName === 'cssBreakpoints'">
|
|
<div ng-if="rule.value > 0" class="cssBreakpointsGraph">
|
|
<h3>Breakpoints distribution graph</h3>
|
|
<canvas class="chart chart-line" chart-data="breakpointsData" chart-labels="breakpointsLabels" chart-options="breakpointsOptions" chart-legend="true" chart-colours="breakpointsColours" width="600" height="250"></canvas>
|
|
<h3>Breakpoints list</h3>
|
|
<div class="offendersTable">
|
|
<div ng-repeat="offender in rule.offendersObj | orderBy:'pixels'">
|
|
<div>Breakpoint <b>{{offender.breakpoint}}</b> involves <ng-pluralize count="offender.count" when="{'one': '1 rule', 'other': '{} rules'}"></ng-pluralize></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div ng-if="rule.value === 0">
|
|
No breakpoint
|
|
</div>
|
|
</div>
|
|
|
|
<div ng-if="!rule && rule !== null" class="notFound">
|
|
<h2>404</h2>
|
|
Rule "{{policyName}}"" not found
|
|
</div>
|
|
|
|
<div class="backToDashboard"><a href="#" ng-click="backToDashboard()">Back to dashboard</a></div>
|
|
</div> |