@@ -148,3 +148,45 @@
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
}
|
||||
.colorPalette {
|
||||
width: 30em;
|
||||
border: 2px solid #000;
|
||||
text-align: left;
|
||||
/* Checkerboard background */
|
||||
background-color: #ddd;
|
||||
background-image: linear-gradient(45deg, #aaaaaa 25%, transparent 25%, transparent 75%, #aaaaaa 75%, #aaaaaa), linear-gradient(45deg, #aaaaaa 25%, transparent 25%, transparent 75%, #aaaaaa 75%, #aaaaaa);
|
||||
background-size: 1em 1em;
|
||||
background-position: 0 0, 0.5em 0.5em;
|
||||
}
|
||||
.colorPalette > div {
|
||||
display: inline-block;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
}
|
||||
.colorPalette > div div {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 100%;
|
||||
background: #FFF;
|
||||
padding: 0.5em;
|
||||
border: 2px solid #f1c40f;
|
||||
border-radius: 0.5em;
|
||||
white-space: nowrap;
|
||||
z-index: 3;
|
||||
font-weight: bold;
|
||||
}
|
||||
.colorPalette > div:hover div {
|
||||
display: block;
|
||||
}
|
||||
.colorPalette > div:hover:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
background-color: inherit;
|
||||
left: -0.2em;
|
||||
top: -0.2em;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
border: 0.2em solid #f1c40f;
|
||||
}
|
||||
|
||||
@@ -168,4 +168,52 @@
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.colorPalette {
|
||||
width: 30em;
|
||||
border: 2px solid #000;
|
||||
text-align: left;
|
||||
|
||||
/* Checkerboard background */
|
||||
background-color: #ddd;
|
||||
background-image: linear-gradient(45deg, #AAA 25%, transparent 25%, transparent 75%, #AAA 75%, #AAA), linear-gradient(45deg, #AAA 25%, transparent 25%, transparent 75%, #AAA 75%, #AAA);
|
||||
background-size:1em 1em;
|
||||
background-position:0 0, 0.5em 0.5em;
|
||||
|
||||
> div {
|
||||
display: inline-block;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
|
||||
div {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 100%;
|
||||
background: #FFF;
|
||||
padding: 0.5em;
|
||||
border: 2px solid #f1c40f;
|
||||
border-radius: 0.5em;
|
||||
white-space: nowrap;
|
||||
z-index: 3;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&:hover div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
background-color: inherit;
|
||||
left: -0.2em;
|
||||
top: -0.2em;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
border: 0.2em solid #f1c40f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@
|
||||
(<ng-pluralize count="offender.requests" when="{'one':'1 request','other':'{} requests'}"></ng-pluralize>)
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'globalVariables' || policyName === 'jQueryDifferentVersions'">
|
||||
<div ng-if="policyName === 'globalVariables' || policyName === 'jQueryVersionsLoaded'">
|
||||
{{offender}}
|
||||
</div>
|
||||
|
||||
@@ -143,6 +143,13 @@
|
||||
<dom-tree tree="rule.offendersObj.tree"></dom-tree>
|
||||
</div>
|
||||
|
||||
<div ng-if="policyName === 'cssColors' && rule.offendersObj.count > 0">
|
||||
<p>This is the color palette, sized by total occurrences:</p>
|
||||
<div class="colorPalette">
|
||||
<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>
|
||||
|
||||
@@ -273,7 +273,7 @@ var policies = {
|
||||
"message": "<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>",
|
||||
"hasOffenders": false,
|
||||
"scoreFn": function(data) {
|
||||
var differentVersions = data.toolsResults.phantomas.metrics.jQueryDifferentVersions;
|
||||
var differentVersions = data.toolsResults.phantomas.metrics.jQueryVersionsLoaded;
|
||||
|
||||
if (differentVersions === 0 || differentVersions > 1 || !data.toolsResults.phantomas.metrics.jQueryVersion) {
|
||||
// Not applicable
|
||||
@@ -322,7 +322,7 @@ var policies = {
|
||||
}
|
||||
}
|
||||
},
|
||||
"jQueryDifferentVersions": {
|
||||
"jQueryVersionsLoaded": {
|
||||
"tool": "phantomas",
|
||||
"label": "Several versions loaded",
|
||||
"message": "<p>jQuery is a heavy library. You should <b>never</b> load jQuery more than once on the same page.</p>",
|
||||
@@ -410,6 +410,52 @@ var policies = {
|
||||
};
|
||||
}
|
||||
},
|
||||
"cssColors": {
|
||||
"tool": "phantomas",
|
||||
"label": "Different colors",
|
||||
"message": "<p>This is the number of different colors defined in CSS.</p><p>Your CSS project will be easier to maintain if you keep a small color set.</p>",
|
||||
"isOkThreshold": 30,
|
||||
"isBadThreshold": 150,
|
||||
"isAbnormalThreshold": 300,
|
||||
"hasOffenders": true,
|
||||
"offendersTransformFn": function(offenders, ruleObject) {
|
||||
var deduplicatedObj = {};
|
||||
|
||||
offenders.map(function(offender) {
|
||||
var parts = /^([^ ]*) \((\d+) times\)$/.exec(offender);
|
||||
|
||||
if (!parts) {
|
||||
debug('cssColors offenders transform function error with "%s"', offender);
|
||||
return;
|
||||
}
|
||||
|
||||
var color = parts[1];
|
||||
var count = parseInt(parts[2], 10);
|
||||
|
||||
deduplicatedObj[color] = (deduplicatedObj[color] || 0) + count;
|
||||
});
|
||||
|
||||
var deduplicatedTable = [];
|
||||
for (var color in deduplicatedObj) {
|
||||
deduplicatedTable.push({
|
||||
color: color,
|
||||
occurrences: deduplicatedObj[color]
|
||||
});
|
||||
}
|
||||
|
||||
deduplicatedTable.sort(function(a, b) {
|
||||
return b.occurrences - a.occurrences;
|
||||
});
|
||||
|
||||
// Override rules.value
|
||||
ruleObject.value = deduplicatedTable.length;
|
||||
|
||||
return {
|
||||
count: deduplicatedTable.length,
|
||||
palette: deduplicatedTable
|
||||
};
|
||||
}
|
||||
},
|
||||
"cssImports": {
|
||||
"tool": "phantomas",
|
||||
"label": "Uses of @import",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"label": "jQuery version",
|
||||
"policies": {
|
||||
"jQueryVersion": 5,
|
||||
"jQueryDifferentVersions": 0.1
|
||||
"jQueryVersionsLoaded": 0.1
|
||||
}
|
||||
},
|
||||
"cssSyntaxError": {
|
||||
@@ -46,7 +46,8 @@
|
||||
"policies": {
|
||||
"cssRules": 2,
|
||||
"cssComplexSelectors": 2,
|
||||
"cssComplexSelectorsByAttribute": 1.5
|
||||
"cssComplexSelectorsByAttribute": 1.5,
|
||||
"cssColors": 0.5
|
||||
}
|
||||
},
|
||||
"badCSS": {
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ var RulesChecker = function() {
|
||||
if (policy.offendersTransformFn) {
|
||||
|
||||
try {
|
||||
offendersObj = policy.offendersTransformFn(offenders);
|
||||
offendersObj = policy.offendersTransformFn(offenders, rule);
|
||||
} catch(err) {
|
||||
debug('Error while transforming offenders for %s', metricName);
|
||||
debug(err);
|
||||
|
||||
@@ -7,16 +7,17 @@
|
||||
/* global document: true, window: true */
|
||||
/* jshint -W030 */
|
||||
|
||||
exports.version = '0.2.a';
|
||||
exports.version = '1.0.a';
|
||||
|
||||
exports.module = function(phantomas) {
|
||||
'use strict';
|
||||
|
||||
phantomas.setMetric('jQueryVersion', ''); // @desc version of jQuery framework (if loaded) [string]
|
||||
phantomas.setMetric('jQueryVersionsLoaded'); // @desc number of loaded jQuery "instances" (even in the same version)
|
||||
phantomas.setMetric('jQueryOnDOMReadyFunctions'); // @desc number of functions bound to onDOMReady event
|
||||
phantomas.setMetric('jQueryWindowOnLoadFunctions'); // @desc number of functions bound to windowOnLoad event
|
||||
phantomas.setMetric('jQuerySizzleCalls'); // @desc number of calls to Sizzle (including those that will be resolved using querySelectorAll)
|
||||
phantomas.setMetric('jQuerySizzleCallsDuplicated'); // @desc number of calls on the same Sizzle request
|
||||
phantomas.setMetric('jQueryDifferentVersions'); //@desc number of different jQuery versions loaded on the page (not counting iframes)
|
||||
//phantomas.setMetric('jQueryEventTriggers'); // @desc number of jQuery event triggers
|
||||
|
||||
var jQueryFunctions = [
|
||||
// DOM manipulations
|
||||
@@ -110,30 +111,28 @@ exports.module = function(phantomas) {
|
||||
phantomas.evaluate(function(jQueryFunctions) {
|
||||
(function(phantomas) {
|
||||
var jQuery;
|
||||
var oldJQuery;
|
||||
|
||||
// TODO: create a helper - phantomas.spyGlobalVar() ?
|
||||
window.__defineSetter__('jQuery', function(val) {
|
||||
phantomas.spyGlobalVar('jQuery', function(jQuery) {
|
||||
var version;
|
||||
var jQueryFn;
|
||||
var oldJQuery = jQuery;
|
||||
|
||||
if (!val || !val.fn) {
|
||||
if (!jQuery || !jQuery.fn) {
|
||||
phantomas.log('jQuery: unable to detect version!');
|
||||
return;
|
||||
}
|
||||
|
||||
version = val.fn.jquery;
|
||||
jQuery = val;
|
||||
jQueryFn = val.fn;
|
||||
// Older jQuery (v?.?) compatibility
|
||||
if (!jQueryFn) {
|
||||
jQueryFn = jQuery;
|
||||
// Tag the current version of jQuery to avoid multiple reports of jQuery being loaded
|
||||
// when it's actually only restored via $.noConflict(true) - see comments in #435
|
||||
if (jQuery.__phantomas === true) {
|
||||
phantomas.log('jQuery: this instance has already been seen by phantomas');
|
||||
return;
|
||||
}
|
||||
jQuery.__phantomas = true;
|
||||
|
||||
phantomas.log('jQuery: loaded v' + version);
|
||||
phantomas.setMetric('jQueryVersion', version);
|
||||
// report the version of jQuery
|
||||
version = jQuery.fn.jquery;
|
||||
phantomas.emit('jQueryLoaded', version);
|
||||
|
||||
|
||||
phantomas.pushContext({
|
||||
type: (oldJQuery) ? 'jQuery version change' : 'jQuery loaded',
|
||||
callDetails: {
|
||||
@@ -141,11 +140,13 @@ exports.module = function(phantomas) {
|
||||
},
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
oldJQuery = version;
|
||||
|
||||
// jQuery.ready.promise
|
||||
// works for jQuery 1.8.0+ (released Aug 09 2012)
|
||||
phantomas.spy(val.ready, 'promise', function(func) {
|
||||
phantomas.spy(jQuery.ready, 'promise', function(func) {
|
||||
phantomas.incrMetric('jQueryOnDOMReadyFunctions');
|
||||
phantomas.addOffender('jQueryOnDOMReadyFunctions', phantomas.getCaller(3));
|
||||
|
||||
phantomas.pushContext({
|
||||
type: 'jQuery - onDOMReady',
|
||||
@@ -160,9 +161,9 @@ exports.module = function(phantomas) {
|
||||
|
||||
// Sizzle calls - jQuery.find
|
||||
// works for jQuery 1.3+ (released Jan 13 2009)
|
||||
phantomas.spy(val, 'find', function(selector, context) {
|
||||
phantomas.spy(jQuery, 'find', function(selector, context) {
|
||||
phantomas.incrMetric('jQuerySizzleCalls');
|
||||
phantomas.emit('onSizzleCall', selector + ' (context: ' + (phantomas.getDOMPath(context) || 'unknown') + ')');
|
||||
phantomas.addOffender('jQuerySizzleCalls', '%s (in %s)', selector, (phantomas.getDOMPath(context) || 'unknown'));
|
||||
|
||||
phantomas.enterContext({
|
||||
type: 'jQuery - find',
|
||||
@@ -183,12 +184,39 @@ exports.module = function(phantomas) {
|
||||
phantomas.leaveContext(moreData);
|
||||
}) || phantomas.log('jQuery: can not measure jQuerySizzleCalls (jQuery used on the page is too old)!');
|
||||
|
||||
/*if (!jQuery.event) {
|
||||
phantomas.spy(jQuery.event, 'trigger', function(ev, data, elem) {
|
||||
var path = phantomas.getDOMPath(elem),
|
||||
type = ev.type || ev;
|
||||
|
||||
phantomas.log('Event: triggered "%s" on "%s"', type, path);
|
||||
|
||||
phantomas.incrMetric('jQueryEventTriggers');
|
||||
phantomas.addOffender('jQueryEventTriggers', '"%s" on "%s"', type, path);
|
||||
});
|
||||
}*/
|
||||
|
||||
// jQuery events bound to window' onLoad event (#451)
|
||||
phantomas.spy(jQuery.fn, 'on', function(eventName, func) {
|
||||
if ((eventName === 'load') && (this[0] === window)) {
|
||||
phantomas.incrMetric('jQueryWindowOnLoadFunctions');
|
||||
phantomas.addOffender('jQueryWindowOnLoadFunctions', phantomas.getCaller(2));
|
||||
|
||||
phantomas.pushContext({
|
||||
type: 'jQuery - windowOnLoad',
|
||||
callDetails: {
|
||||
arguments: [func]
|
||||
},
|
||||
backtrace: phantomas.getBacktrace()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add spys on many jQuery functions
|
||||
jQueryFunctions.forEach(function(functionName) {
|
||||
var capitalizedName = functionName.substring(0,1).toUpperCase() + functionName.substring(1);
|
||||
|
||||
phantomas.spy(jQueryFn, functionName, function(args) {
|
||||
phantomas.spy(jQuery.fn, functionName, function(args) {
|
||||
|
||||
// Clean args
|
||||
args = [].slice.call(arguments);
|
||||
@@ -263,42 +291,28 @@ exports.module = function(phantomas) {
|
||||
phantomas.leaveContext();
|
||||
}) || phantomas.log('jQuery: can not track jQuery - ' + capitalizedName + ' (this version of jQuery doesn\'t support it)');
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
window.__defineGetter__('jQuery', function() {
|
||||
return jQuery;
|
||||
});
|
||||
})(window.__phantomas);
|
||||
}, jQueryFunctions);
|
||||
});
|
||||
|
||||
|
||||
// count Sizzle calls to detect duplicated queries
|
||||
var Collection = require('../../util/collection'),
|
||||
sizzleCalls = new Collection(),
|
||||
jQueryLoading = new Collection();
|
||||
|
||||
phantomas.on('onSizzleCall', function(request) {
|
||||
sizzleCalls.push(request);
|
||||
// store the last resource that was received
|
||||
// try to report where given jQuery version was loaded from
|
||||
phantomas.on('recv', function(entry) {
|
||||
if (entry.isJS) {
|
||||
lastUrl = entry.url;
|
||||
}
|
||||
});
|
||||
|
||||
phantomas.on('jQueryLoaded', function(version) {
|
||||
jQueryLoading.push(version);
|
||||
});
|
||||
phantomas.log('jQuery: loaded v' + version);
|
||||
phantomas.setMetric('jQueryVersion', version);
|
||||
|
||||
phantomas.on('report', function() {
|
||||
sizzleCalls.sort().forEach(function(id, cnt) {
|
||||
if (cnt > 1) {
|
||||
phantomas.incrMetric('jQuerySizzleCallsDuplicated');
|
||||
phantomas.addOffender('jQuerySizzleCallsDuplicated', '%s: %d', id, cnt);
|
||||
}
|
||||
});
|
||||
// report multiple jQuery "instances" (issue #435)
|
||||
phantomas.incrMetric('jQueryVersionsLoaded');
|
||||
phantomas.addOffender('jQueryVersionsLoaded', 'v%s', version);
|
||||
|
||||
jQueryLoading.forEach(function(version) {
|
||||
phantomas.incrMetric('jQueryDifferentVersions');
|
||||
phantomas.addOffender('jQueryDifferentVersions', '%s', version);
|
||||
});
|
||||
phantomas.log('jQuery: v%s (probably loaded from <%s>)', version, lastUrl);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -404,20 +404,20 @@ describe('rulesChecker', function() {
|
||||
results.should.deep.equals({});
|
||||
|
||||
|
||||
// If jQueryDifferentVersions is 0
|
||||
// If jQueryVersionsLoaded is 0
|
||||
results = rulesChecker.check({
|
||||
"toolsResults": {
|
||||
"phantomas": {
|
||||
"metrics": {
|
||||
"jQueryVersion": "1.6.0",
|
||||
"jQueryDifferentVersions": 0
|
||||
"jQueryVersionsLoaded": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}, policies);
|
||||
results.should.not.have.a.property('jQueryVersion');
|
||||
results.should.have.a.property('jQueryDifferentVersions');
|
||||
results.jQueryDifferentVersions.should.have.a.property('score').that.equals(100);
|
||||
results.should.have.a.property('jQueryVersionsLoaded');
|
||||
results.jQueryVersionsLoaded.should.have.a.property('score').that.equals(100);
|
||||
|
||||
|
||||
// If there are more than 1 jQuery version
|
||||
@@ -426,15 +426,15 @@ describe('rulesChecker', function() {
|
||||
"phantomas": {
|
||||
"metrics": {
|
||||
"jQueryVersion": "1.6.0",
|
||||
"jQueryDifferentVersions": 2
|
||||
"jQueryVersionsLoaded": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}, policies);
|
||||
results.should.not.have.a.property('jQueryVersion');
|
||||
results.should.have.a.property('jQueryDifferentVersions');
|
||||
results.jQueryDifferentVersions.should.have.a.property('score').that.equals(0);
|
||||
results.jQueryDifferentVersions.should.have.a.property('abnormal').that.equals(true);
|
||||
results.should.have.a.property('jQueryVersionsLoaded');
|
||||
results.jQueryVersionsLoaded.should.have.a.property('score').that.equals(0);
|
||||
results.jQueryVersionsLoaded.should.have.a.property('abnormal').that.equals(true);
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user