Skip to content
This repository
  • 13 commits
  • 27 files changed
  • 0 comments
  • 1 contributor
1  .gitignore
@@ -2,3 +2,4 @@
2 2 build/
3 3 pkg/
4 4 *.swp
  5 +.DS_Store
75 Makefile
@@ -14,6 +14,7 @@ SRC_OPERA := $(SRC)/opera
14 14 SRC_SAFARI := $(SRC)/safari
15 15 SRC_WSH := $(SRC)/wsh
16 16 SRC_RHINO := $(SRC)/rhino
  17 +SRC_PHANTOMJS := $(SRC)/phantomjs
17 18
18 19 # build directories
19 20 BUILD := build
@@ -28,6 +29,7 @@ BUILD_SAFARI_ROOT := $(BUILD)/safari
28 29 BUILD_SAFARI := $(BUILD_SAFARI_ROOT)/yslow.safariextension
29 30 BUILD_WSH := $(BUILD)/wsh
30 31 BUILD_RHINO := $(BUILD)/rhino
  32 +BUILD_PHANTOMJS := $(BUILD)/phantomjs
31 33
32 34 # package directories
33 35 PKG := pkg
@@ -39,6 +41,7 @@ PKG_OPERA := $(PKG)/opera
39 41 PKG_SAFARI := $(PKG)/safari
40 42 PKG_WSH := $(PKG)/wsh
41 43 PKG_RHINO := $(PKG)/rhino
  44 +PKG_PHANTOMJS := $(PKG)/phantomjs
42 45
43 46 # file names / versions / licenses
44 47 BOOKMARKLET_YSLOW_JS := yslow-files-bookmarklet.js
@@ -59,11 +62,13 @@ YUI_LIB := $(SRC_YUI)/build
59 62 IMG := img
60 63 YUICOMPRESSOR := java -jar ~/bin/yuicompressor-2.4.7.jar
61 64
62   -all: show-version bookmarklet chrome firefox har nodejs opera safari wsh rhino
  65 +all: show-version bookmarklet chrome firefox har nodejs opera safari wsh rhino phantomjs
63 66
64   -clean: clean-bookmarklet clean-chrome clean-firefox clean-har clean-nodejs clean-opera clean-safari clean-wsh clean-rhino
  67 +clean: clean-bookmarklet clean-chrome clean-firefox clean-har clean-nodejs clean-opera clean-safari clean-wsh clean-rhino clean-phantomjs
65 68 @if [ -d $(BUILD) ]; then rmdir $(BUILD); fi
66 69
  70 +pkg: pkg-bookmarklet pkg-chrome pkg-firefox pkg-nodejs pkg-opera pkg-safari pkg-wsh pkg-rhino pkg-phantomjs
  71 +
67 72 show-version:
68 73 @echo "YSLOW version: $(YSLOW_VERSION)"
69 74
@@ -95,7 +100,7 @@ bookmarklet-files:
95 100 $(SRC_COMMON)/version.js \
96 101 $(SRC_COMMON)/componentSet.js \
97 102 $(SRC_COMMON)/component.js \
98   - $(SRC_COMMON)/component-bm-chrome.js \
  103 + $(SRC_COMMON)/component-bm-ch.js \
99 104 $(SRC_COMMON)/controller.js \
100 105 $(SRC_COMMON)/util.js \
101 106 $(SRC_COMMON)/doc.js \
@@ -105,7 +110,7 @@ bookmarklet-files:
105 110 $(SRC_COMMON)/context.js \
106 111 $(SRC_COMMON)/renderers.js \
107 112 $(SRC_COMMON)/peeler.js \
108   - $(SRC_BOOKMARKLET)/peeler.js \
  113 + $(SRC_COMMON)/peeler-bm-ch-ph.js \
109 114 $(SRC_BOOKMARKLET)/$(BM_CONFIG) \
110 115 $(SRC_BOOKMARKLET)/controller.js | \
111 116 sed s/{{YSLOW_VERSION}}/$(YSLOW_VERSION)/ | \
@@ -148,7 +153,7 @@ chrome:
148 153 $(SRC_COMMON)/version.js \
149 154 $(SRC_COMMON)/componentSet.js \
150 155 $(SRC_COMMON)/component.js \
151   - $(SRC_COMMON)/component-bm-chrome.js \
  156 + $(SRC_COMMON)/component-bm-ch.js \
152 157 $(SRC_COMMON)/controller.js \
153 158 $(SRC_COMMON)/util.js \
154 159 $(SRC_COMMON)/doc.js \
@@ -158,7 +163,7 @@ chrome:
158 163 $(SRC_COMMON)/context.js \
159 164 $(SRC_COMMON)/renderers.js \
160 165 $(SRC_COMMON)/peeler.js \
161   - $(SRC_CHROME)/peeler.js \
  166 + $(SRC_COMMON)/peeler-bm-ch-ph.js \
162 167 $(SRC_CHROME)/yslow-chrome-pref.js | \
163 168 sed s/{{YSLOW_VERSION}}/$(YSLOW_VERSION)/ \
164 169 > $(BUILD_CHROME)/yslow-chrome.js
@@ -327,6 +332,30 @@ rhino: har
327 332 $(BUILD_RHINO)/yslow.js
328 333 @echo "done"
329 334
  335 +phantomjs:
  336 + @echo "building PHANTOMJS..."
  337 + @if [ ! -d $(BUILD_PHANTOMJS) ]; then mkdir -p $(BUILD_PHANTOMJS); fi
  338 + @(sed '/YSLOW HERE/q;' $(SRC_PHANTOMJS)/controller.js; \
  339 + cat $(SRC_COMMON)/yslow.js \
  340 + $(SRC_COMMON)/version.js \
  341 + $(SRC_COMMON)/componentSet.js \
  342 + $(SRC_COMMON)/component.js \
  343 + $(SRC_PHANTOMJS)/component.js \
  344 + $(SRC_COMMON)/controller.js \
  345 + $(SRC_COMMON)/util.js \
  346 + $(SRC_COMMON)/doc.js \
  347 + $(SRC_COMMON)/rules.js \
  348 + $(SRC_COMMON)/resultset.js \
  349 + $(SRC_COMMON)/view.js \
  350 + $(SRC_COMMON)/context.js \
  351 + $(SRC_COMMON)/renderers.js \
  352 + $(SRC_COMMON)/peeler.js \
  353 + $(SRC_COMMON)/peeler-bm-ch-ph.js; \
  354 + tail -r $(SRC_PHANTOMJS)/controller.js | sed '/YSLOW HERE/q' | tail -r ) | sed '/YSLOW HERE/d' \
  355 + > $(BUILD_PHANTOMJS)/yslow.js
  356 + @sed -i -e "s/{{YSLOW_VERSION}}/$(YSLOW_VERSION)/" $(BUILD_PHANTOMJS)/yslow.js
  357 + @echo "done"
  358 +
330 359 clean-yui:
331 360 @echo "cleaning YUI..."
332 361 @if [ -f $(BUILD_YUI)/yui.js ]; then rm $(BUILD_YUI)/yui.js; fi
@@ -474,7 +503,11 @@ clean-rhino:
474 503 @if [ -d $(BUILD_RHINO) ]; then rmdir $(BUILD_RHINO); fi
475 504 @echo "done"
476 505
477   -pkg: pkg-bookmarklet pkg-chrome pkg-firefox pkg-nodejs pkg-opera pkg-safari pkg-wsh pkg-rhino
  506 +clean-phantomjs:
  507 + @echo "cleaning PHANTOMJS..."
  508 + @if [ -f $(BUILD_PHANTOMJS)/yslow.js ]; then rm $(BUILD_PHANTOMJS)/yslow.js; fi
  509 + @if [ -d $(BUILD_PHANTOMJS) ]; then rmdir $(BUILD_PHANTOMJS); fi
  510 + @echo "done"
478 511
479 512 pkg-bookmarklet: BM_CONFIG := config-ycs.js
480 513 pkg-bookmarklet: yui bookmarklet-files
@@ -499,14 +532,15 @@ pkg-bookmarklet: yui bookmarklet-files
499 532 @rm $(BUILD_BOOKMARKLET)/$(BOOKMARKLET_YSLOW_CSS)
500 533 @echo " done"
501 534 @echo " merging minified YUI and BOOKMARKLET..."
502   - @# add line break between yui and yslow license
503   - @for i in 1; do \
504   - cat $(YUI_LICENSE); \
505   - cat $(BUILD_YUI)/yui$(YUI_MODE)-min.js; \
506   - echo; \
507   - cat $(YSLOW_LICENSE); \
508   - cat $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_YSLOW_JS); \
509   - done > $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_JS)
  535 + @cat $(YUI_LICENSE) \
  536 + > $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_JS)
  537 + @cat $(BUILD_YUI)/yui$(YUI_MODE)-min.js \
  538 + >> $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_JS)
  539 + @echo "" >> $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_JS)
  540 + @cat $(YSLOW_LICENSE) \
  541 + >> $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_JS)
  542 + @cat $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_YSLOW_JS) \
  543 + >> $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_JS)
510 544 @rm $(PKG_BOOKMARKLET)/$(YSLOW_VERSION)/$(BOOKMARKLET_YSLOW_JS) \
511 545 $(BUILD_YUI)/yui$(YUI_MODE)-min.js
512 546 @cat $(YSLOW_LICENSE) \
@@ -641,3 +675,14 @@ pkg-rhino: rhino
641 675 $(BUILD_RHINO)/lib/blank.html \
642 676 $(PKG_RHINO)/yslow-$(YSLOW_VERSION)lib/
643 677 @echo "done"
  678 +
  679 +pkg-phantomjs: phantomjs
  680 + @echo "packaging PHANTOMJS..."
  681 + @if [ -d $(PKG_PHANTOMJS)/$(YSLOW_VERSION) ]; then \
  682 + echo "$(PKG_PHANTOMJS)/$(YSLOW_VERSION) already exists"; \
  683 + exit 1; \
  684 + fi
  685 + @mkdir -p $(PKG_PHANTOMJS)/$(YSLOW_VERSION)
  686 + @cat $(YSLOW_LICENSE) > $(PKG_PHANTOMJS)/$(YSLOW_VERSION)/yslow.js
  687 + @$(YUICOMPRESSOR) $(BUILD_PHANTOMJS)/yslow.js >> $(PKG_PHANTOMJS)/$(YSLOW_VERSION)/yslow.js
  688 + @echo "done"
13 README.md
Source Rendered
@@ -15,12 +15,13 @@ Specific flavor:
15 15
16 16 Available flavors:
17 17
18   -* **firefox**: Mozilla Firefox add-on
19   -* **chrome**: Google Chrome extension
20   -* **bookmarklet**: Mobile/Any-browser bookmarklet
21   -* **opera**: Opera extension
22   -* **safari**: Apple Safari extension
23   -* **nodejs**: Command Line powered by Node.JS and NPM
  18 +* **firefox**: Mozilla [Firefox add-on](https://addons.mozilla.org/en-US/firefox/)
  19 +* **chrome**: Google [Chrome extension](https://chrome.google.com/webstore/category/extensions)
  20 +* **bookmarklet**: Mobile/Desktop browser [bookmarklet](http://en.wikipedia.org/wiki/Bookmarklet)
  21 +* **opera**: [Opera extension](http://extensions.opera.com/)
  22 +* **safari**: Apple [Safari extension](http://extensions.apple.com/)
  23 +* **nodejs**: Command line for [HAR](http://www.softwareishard.com/blog/har-12-spec/) files powered by [Node.JS](http://nodejs.org/) and [NPM](http://npmjs.org/)
  24 +* **phantomjs**: Command line with headless [WebKit](http://www.webkit.org/) powered by [PhantomJS](http://www.phantomjs.org/)
24 25
25 26 e.g.:
26 27
4 src/chrome/options.html
@@ -90,3 +90,7 @@
90 90 </script>
91 91 </body>
92 92 </html>
  93 +<!--
  94 +Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  95 +Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  96 +-->
38 src/chrome/peeler.js
... ... @@ -1,38 +0,0 @@
1   -/*global YSLOW*/
2   -
3   -YSLOW.peeler.peel = function (node) {
4   - var url, docs, doc, doct, baseHref,
5   - comps = [];
6   -
7   - try {
8   - // Find all documents in the window.
9   - docs = this.findDocuments(node);
10   -
11   - for (url in docs) {
12   - if (docs.hasOwnProperty(url)) {
13   - doc = docs[url];
14   - if (doc) {
15   - // add the document.
16   - comps.push({
17   - type: doc.type,
18   - href: url
19   - });
20   -
21   - doct = doc.document;
22   - if (doct && url) {
23   - baseHref = this.getBaseHref(doct);
24   - comps = comps.concat(this.findComponentsInNode(doct,
25   - baseHref, doc.type));
26   - }
27   - }
28   - }
29   - }
30   - } catch (err) {
31   - YSLOW.util.dump(err);
32   - YSLOW.util.event.fire('peelError', {
33   - 'message': err
34   - });
35   - }
36   -
37   - return comps;
38   -};
5 src/chrome/pref-init.js
... ... @@ -1,3 +1,8 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 // initialize defaults
2 7 (function () {
3 8 var i, len, item, value,
5 src/chrome/yslow-chrome-pref.js
... ... @@ -1,3 +1,8 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 /*global YSLOW, Components*/
2 7 /*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
3 8
4 src/chrome/yslow.html
@@ -10,3 +10,7 @@
10 10 <script src="controller.js"></script>
11 11 </body>
12 12 </html>
  13 +<!--
  14 +Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  15 +Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  16 +-->
5 src/common/component-bm-chrome.js → src/common/component-bm-ch.js
... ... @@ -1,4 +1,9 @@
1 1 /**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
  6 +/**
2 7 * Parse details (HTTP headers, content, etc) from a
3 8 * given source and set component properties.
4 9 * @param o The object containing component details.
2  src/common/component.js
@@ -116,7 +116,7 @@ YSLOW.Component.prototype.populateProperties = function (resolveRedirect, ignore
116 116 that.size_compressed = that.nsize;
117 117 } else {
118 118 // a hack
119   - that.size_compressed = that.size / 3;
  119 + that.size_compressed = Math.round(that.size / 3);
120 120 }
121 121 } else {
122 122 that.compressed = false;
1  src/common/license
@@ -2,4 +2,3 @@
2 2 * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
3 3 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 4 */
5   -
0  src/bookmarklet/peeler.js → src/common/peeler-bm-ch-ph.js
File renamed without changes
5 src/common/rules.js
... ... @@ -1,3 +1,8 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 /*global YSLOW*/
2 7 /*jslint white: true, onevar: true, undef: true, nomen: true, regexp: true, continue: true, plusplus: true, bitwise: true, newcap: true, type: true, unparam: true, maxerr: 50, indent: 4*/
3 8
246 src/common/util.js
... ... @@ -1,5 +1,10 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 /*global YSLOW, Firebug, Components, ActiveXObject, gBrowser, window, getBrowser*/
2   -/*jslint white: true, onevar: true, undef: true, newcap: true, nomen: true, regexp: true, plusplus: true, browser: true, devel: true, maxerr: 50, indent: 4 */
  7 +/*jslint sloppy: true, bitwise: true, browser: true, regexp: true*/
3 8
4 9 /**
5 10 * @namespace YSLOW
@@ -67,7 +72,7 @@ YSLOW.util = {
67 72 } catch (e2) {
68 73 // alert shouldn't be used due to its annoying modal behavior
69 74 }
70   - }
  75 + }
71 76 },
72 77
73 78 /**
@@ -799,9 +804,10 @@ YSLOW.util = {
799 804 getResults: function (yscontext, info) {
800 805 var i, l, results, url, type, comps, comp, encoded_url, obj, cr,
801 806 cs, etag, name, len, include_grade, include_comps, include_stats,
802   - result, len2, spaceid,
  807 + result, len2, spaceid, header, sourceHeaders, targetHeaders,
803 808 reButton = / <button [\s\S]+<\/button>/,
804   - isArray = YSLOW.util.isArray,
  809 + util = YSLOW.util,
  810 + isArray = util.isArray,
805 811 stats = {},
806 812 stats_c = {},
807 813 comp_objs = [],
@@ -834,12 +840,11 @@ YSLOW.util = {
834 840 params.o = parseInt(yscontext.PAGE.overallScore, 10);
835 841 params.u = encodeURIComponent(yscontext.result_set.url);
836 842 params.r = parseInt(yscontext.PAGE.totalRequests, 10);
837   - spaceid = YSLOW.util.getPageSpaceid(yscontext.component_set);
  843 + spaceid = util.getPageSpaceid(yscontext.component_set);
838 844 if (spaceid) {
839 845 params.s = encodeURI(spaceid);
840 846 }
841 847 params.i = yscontext.result_set.getRulesetApplied().id;
842   -
843 848 if (yscontext.PAGE.t_done) {
844 849 params.lt = parseInt(yscontext.PAGE.t_done, 10);
845 850 }
@@ -917,7 +922,7 @@ YSLOW.util = {
917 922 obj.gzip = comp.size_compressed;
918 923 }
919 924 if (comp.expires && comp.expires instanceof Date) {
920   - obj.expires = YSLOW.util.prettyExpiresDate(comp.expires);
  925 + obj.expires = util.prettyExpiresDate(comp.expires);
921 926 }
922 927 cr = comp.getReceivedCookieSize();
923 928 if (cr > 0) {
@@ -931,6 +936,30 @@ YSLOW.util = {
931 936 if (typeof etag === 'string' && etag.length > 0) {
932 937 obj.etag = etag;
933 938 }
  939 + // format req/res headers
  940 + obj.headers = {};
  941 + if (comp.req_headers) {
  942 + sourceHeaders = comp.req_headers;
  943 + obj.headers.request = {};
  944 + targetHeaders = obj.headers.request;
  945 + for (header in sourceHeaders) {
  946 + if (sourceHeaders.hasOwnProperty(header)) {
  947 + targetHeaders[util.formatHeaderName(header)] =
  948 + sourceHeaders[header];
  949 + }
  950 + }
  951 + }
  952 + if (comp.headers) {
  953 + sourceHeaders = comp.headers;
  954 + obj.headers.response = {};
  955 + targetHeaders = obj.headers.response;
  956 + for (header in sourceHeaders) {
  957 + if (sourceHeaders.hasOwnProperty(header)) {
  958 + targetHeaders[util.formatHeaderName(header)] =
  959 + sourceHeaders[header];
  960 + }
  961 + }
  962 + }
934 963 comp_objs.push(obj);
935 964 }
936 965 params.comps = comp_objs;
@@ -1096,6 +1125,34 @@ YSLOW.util = {
1096 1125 }
1097 1126 },
1098 1127
  1128 +
  1129 + /**
  1130 + * Wrapper for decodeURIComponent, try to decode
  1131 + * otherwise return the input value.
  1132 + * @param {String} value The value to be decoded.
  1133 + * @return {String} The decoded value.
  1134 + */
  1135 + decodeURIComponent: function (value) {
  1136 + try {
  1137 + return decodeURIComponent(value);
  1138 + } catch (err) {
  1139 + return value;
  1140 + }
  1141 + },
  1142 +
  1143 + /**
  1144 + * Decode html entities. e.g.: &lt; becomes <
  1145 + * @param {String} str the html string to decode entities from.
  1146 + * @return {String} the input html with entities decoded.
  1147 + */
  1148 + decodeEntities: function (str) {
  1149 + return String(str)
  1150 + .replace(/&amp;/g, '&')
  1151 + .replace(/&lt;/g, '<')
  1152 + .replace(/&gt;/g, '>')
  1153 + .replace(/&quot;/g, '"');
  1154 + },
  1155 +
1099 1156 /**
1100 1157 * convert Object to XML
1101 1158 * @param {Object} obj the Object to be converted to XML
@@ -1107,13 +1164,13 @@ YSLOW.util = {
1107 1164 util = YSLOW.util,
1108 1165 reInvalid = /[<&>]/,
1109 1166 xml = '<?xml version="1.0" encoding="UTF-8"?>';
1110   -
  1167 +
1111 1168 toXML = function (o) {
1112 1169 var item, value, i, len, val, type,
1113   -
  1170 +
1114 1171 safeXML = function (value, decode) {
1115 1172 if (decode) {
1116   - value = decodeURIComponent(value);
  1173 + value = util.decodeURIComponent(value);
1117 1174 }
1118 1175 if (reInvalid.test(value)) {
1119 1176 return '<![CDATA[' + value + ']]>';
@@ -1135,18 +1192,18 @@ YSLOW.util = {
1135 1192 } else {
1136 1193 toXML(val);
1137 1194 }
1138   - xml += '</item>';
  1195 + xml += '</item>';
1139 1196 }
1140 1197 } else if (util.isObject(value)) {
1141 1198 toXML(value);
1142 1199 } else {
1143 1200 xml += safeXML(value, item === 'u' || item === 'url');
1144 1201 }
1145   - xml += '</' + item + '>';
  1202 + xml += '</' + item + '>';
1146 1203 }
1147 1204 }
1148 1205 };
1149   -
  1206 +
1150 1207 root = root || 'results';
1151 1208 xml += '<' + root + '>';
1152 1209 toXML(obj);
@@ -1158,7 +1215,7 @@ YSLOW.util = {
1158 1215 /**
1159 1216 * Pretty print results
1160 1217 * @param {Object} obj the Object with YSlow results
1161   - * @return {String} the results in plain text (pretty preinted)
  1218 + * @return {String} the results in plain text (pretty printed)
1162 1219 */
1163 1220 prettyPrintResults: function (obj) {
1164 1221 var pp,
@@ -1187,15 +1244,18 @@ YSLOW.util = {
1187 1244 },
1188 1245
1189 1246 indent = function (n) {
1190   - var res = mem[n];
  1247 + var arr,
  1248 + res = mem[n];
1191 1249
1192 1250 if (typeof res === 'undefined') {
1193   - mem[n] = res = new Array((4 * n) + 1).join(' ');
  1251 + arr = [];
  1252 + arr.length = (4 * n) + 1;
  1253 + mem[n] = res = arr.join(' ');
1194 1254 }
1195 1255
1196 1256 return res;
1197 1257 };
1198   -
  1258 +
1199 1259 pp = function (o, level) {
1200 1260 var item, value, i, len, val, type;
1201 1261
@@ -1210,7 +1270,7 @@ YSLOW.util = {
1210 1270 type = typeof val;
1211 1271 if (type === 'string' || type === 'number') {
1212 1272 str += indent(level + 1) +
1213   - decodeURIComponent(val) + '\n';
  1273 + util.decodeURIComponent(val) + '\n';
1214 1274 } else {
1215 1275 pp(val, level + 1);
1216 1276 if (i < len - 1) {
@@ -1230,18 +1290,162 @@ YSLOW.util = {
1230 1290 item === 'cr' || item === 'cs') {
1231 1291 value = util.kbSize(value) + ' (' + value + ' bytes)';
1232 1292 }
1233   - str += ' ' + decodeURIComponent(value) + '\n';
  1293 + str += ' ' + util.decodeURIComponent(value) + '\n';
1234 1294 }
1235 1295 }
1236 1296 }
1237 1297 };
1238   -
  1298 +
1239 1299 pp(obj, 0);
1240 1300
1241 1301 return str;
1242 1302 },
1243 1303
1244 1304 /**
  1305 + * Test result against a certain threshold for CI
  1306 + * @param {Object} obj the Object with YSlow results
  1307 + * @param {String|Number|Object} threshold The definition of OK (inclusive)
  1308 + * Anything >= threshold == OK. It can be a number [0-100],
  1309 + * a letter [A-F] as follows:
  1310 + * 100 >= A >= 90 > B >= 80 > C >= 70 > D >= 60 > E >= 50 > F >= 0 > N/A = -1
  1311 + * It can also be a specific per rule. e.g:
  1312 + * {overall: 80, ycdn: 65, ynumreq: 'B'}
  1313 + * where overall is the common threshold to be
  1314 + * used by all rules except those listed
  1315 + * @return {Array} the test result array containing each test result details:
  1316 + */
  1317 + testResults: function (obj, threshold) {
  1318 + var overall, g, grade, grades, score, commonScore, i, len,
  1319 + tests = [],
  1320 + scores = {
  1321 + a: 90,
  1322 + b: 80,
  1323 + c: 70,
  1324 + d: 60,
  1325 + e: 50,
  1326 + f: 0,
  1327 + 'n/a': -1
  1328 + },
  1329 + yslow = YSLOW,
  1330 + util = yslow.util,
  1331 + isObj = util.isObject(threshold),
  1332 + rules = yslow.doc.rules,
  1333 +
  1334 + getScore = function (value) {
  1335 + var score = parseInt(value, 10);
  1336 +
  1337 + if (isNaN(score) && typeof value === 'string') {
  1338 + score = scores[value.toLowerCase()];
  1339 + }
  1340 +
  1341 + // edge case for F or 0
  1342 + if (score === 0) {
  1343 + return 0;
  1344 + }
  1345 +
  1346 + return score || overall || scores.b;
  1347 + },
  1348 +
  1349 + getThreshold = function (name) {
  1350 + if (commonScore) {
  1351 + return commonScore;
  1352 + }
  1353 +
  1354 + if (!isObj) {
  1355 + commonScore = getScore(threshold);
  1356 + return commonScore;
  1357 + } else if (threshold.hasOwnProperty(name)) {
  1358 + return getScore(threshold[name]);
  1359 + } else {
  1360 + return overall || scores.b;
  1361 + }
  1362 + },
  1363 +
  1364 + test = function (score, ts, name, offenders) {
  1365 + var desc = rules.hasOwnProperty(name) && rules[name].name;
  1366 +
  1367 + tests.push({
  1368 + ok: score >= ts,
  1369 + score: score,
  1370 + grade: util.prettyScore(score),
  1371 + name: name,
  1372 + description: desc || '',
  1373 + offenders: offenders
  1374 + });
  1375 + };
  1376 +
  1377 + // overall threshold (default b [80])
  1378 + overall = getThreshold('overall');
  1379 +
  1380 + // overall score
  1381 + test(obj.o, overall, 'overall score');
  1382 +
  1383 + // grades
  1384 + grades = obj.g;
  1385 + if (grades) {
  1386 + for (grade in grades) {
  1387 + if (grades.hasOwnProperty(grade)) {
  1388 + g = grades[grade];
  1389 + score = g.score;
  1390 + if (typeof score === 'undefined') {
  1391 + score = -1;
  1392 + }
  1393 + test(score, getThreshold(grade), grade, g.components);
  1394 + }
  1395 + }
  1396 + }
  1397 +
  1398 + return tests;
  1399 + },
  1400 +
  1401 + /**
  1402 + * Format test results as TAP for CI
  1403 + * @see: http://testanything.org/wiki/index.php/TAP_specification
  1404 + * @param {Array} tests the arrays containing the test results from testResults.
  1405 + * @return {String} the results as TAP plain text
  1406 + */
  1407 + formatAsTAP: function (results) {
  1408 + var i, res, line, offenders, j, lenJ,
  1409 + len = results.length,
  1410 + tap = [],
  1411 + util = YSLOW.util,
  1412 + decodeURI = util.decodeURIComponent,
  1413 + decodeEntities = util.decodeEntities;
  1414 +
  1415 + // test plan
  1416 + tap.push('1..' + len);
  1417 +
  1418 + for (i = 0; i < len; i += 1) {
  1419 + res = results[i];
  1420 + line = res.ok || res.score < 0 ? 'ok' : 'not ok';
  1421 + line += ' ' + (i + 1) + ' - ' + res.grade +
  1422 + '(' + res.score + ') ' + res.name;
  1423 + if (res.description) {
  1424 + line += ': ' + res.description;
  1425 + }
  1426 + if (res.score < 0) {
  1427 + line += ' # SKIP score N/A';
  1428 + }
  1429 + tap.push(line);
  1430 +
  1431 + // offenders
  1432 + offenders = res.offenders;
  1433 + if (offenders) {
  1434 + lenJ = offenders.length;
  1435 + if (lenJ > 0) {
  1436 + tap.push('#\tOffenders:');
  1437 + for (j = 0; j < lenJ; j += 1) {
  1438 + tap.push('#\t' +
  1439 + decodeEntities(decodeURI(offenders[j])));
  1440 + }
  1441 + }
  1442 + }
  1443 + }
  1444 +
  1445 + return tap.join('\n');
  1446 + },
  1447 +
  1448 + /**
1245 1449 * Try to find a spaceid in the HTML document source.
1246 1450 * @param {YSLOW.ComponentSet} cset Component set.
1247 1451 * @return spaceID string
@@ -1517,7 +1721,7 @@ YSLOW.util = {
1517 1721
1518 1722 els = node.getElementsByTagName(tag);
1519 1723 for (i = 0, len = els.length; i < len; i += 1) {
1520   - el = els[i];
  1724 + el = els[i];
1521 1725 if (!el.src) {
1522 1726 objs.push({
1523 1727 contentNode: contentNode,
5 src/common/version.js
... ... @@ -1 +1,6 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 YSLOW.version = '{{YSLOW_VERSION}}';
5 src/common/view.js
... ... @@ -1,3 +1,8 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 /*global YSLOW, window*/
2 7 /*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
3 8
5 src/common/yslow.css
... ... @@ -1,3 +1,8 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 #yslowDiv div, #yslowDiv span, #yslowDiv ul, #yslowDiv li, #yslowDiv hr, #yslowDiv p, #yslowDiv form, #yslowDiv em, #yslowDiv table, #yslowDiv th, #yslowDiv tr, #yslowDiv td, #yslowDiv img, #yslowDiv label {
2 7 margin: 0;
3 8 padding: 0;
6 src/common/yslow.js
... ... @@ -1,3 +1,8 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 /*global YSLOW:true*/
2 7 /*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
3 8
@@ -35,7 +40,6 @@ YSLOW.registerRule = function (rule) {
35 40 YSLOW.controller.addRule(rule);
36 41 };
37 42
38   -
39 43 /**
40 44 *
41 45 * Adds a new ruleset (new grading algorithm).
5 src/firefox/chrome/content/yslow-firefox-net.js
... ... @@ -1,3 +1,8 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 /*global YSLOW, Components*/
2 7 /*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, regexp: true, newcap: true, immed: true */
3 8
5 src/firefox/chrome/content/yslow-firefox.js
... ... @@ -1,3 +1,8 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
1 6 /*global YSLOW, Components, gBrowser, window, Firebug, FBL, FirebugContext*/
2 7 /*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
3 8
5 src/firefox/chrome/content/yslow/component.js
... ... @@ -1,4 +1,9 @@
1 1 /**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
  6 +/**
2 7 * Parse details (HTTP headers, content, etc) from
3 8 * component response and set as properties.
4 9 */
2  src/firefox/install.rdf
@@ -18,7 +18,7 @@ Copyrights licensed under the New BSD License. See the accompanying LICENSE file
18 18 <Description>
19 19 <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
20 20 <em:minVersion>3.0</em:minVersion>
21   - <em:maxVersion>11.*</em:maxVersion>
  21 + <em:maxVersion>13.*</em:maxVersion>
22 22 </Description>
23 23 </em:targetApplication>
24 24
5 src/har/component.js
... ... @@ -1,4 +1,9 @@
1 1 /**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
  6 +/**
2 7 * Parse details (HTTP headers, content, etc) from a
3 8 * given source and set component properties.
4 9 * @param o The object containing component details.
1  src/nodejs/controller.js
@@ -192,6 +192,7 @@ program
192 192 ' ' + n + ' -info all --format plain /tmp/*.har',
193 193 ' ' + n + ' -i basic --rulseset yslow1 -d < file.har',
194 194 ' curl example.com/file.har | ' + n + ' -i grade -b http://server.com/beacon -v',
  195 + ' curl www.webpagetest.org/export.php?test=ID | ' + n + ' -i grade | curl www.showslow.com/beacon/yslow -d @-',
195 196 '',
196 197 ' More Info:',
197 198 '',
4 src/nodejs/package.json
@@ -3,8 +3,8 @@
3 3 "version": "{{YSLOW_VERSION}}",
4 4 "description": "Commnad line version of YSlow web performance analysis tool from HAR files",
5 5 "keywords": ["yslow", "performance", "har", "yahoo", "wpo", "command", "line", "automation"],
6   - "homepage": "http://getyslow.com",
7   - "author": "Yahoo! Inc. <yslow@yahoo-inc.com> (http://getyslow.com)",
  6 + "homepage": "http://yslow.org",
  7 + "author": "Marcel Duran <yslow@marcelduran.com> (http://yslow.org)",
8 8 "main": "./lib/yslow",
9 9 "bin": {"yslow": "./bin/yslow"},
10 10 "repository": {"type": "", "url": ""},
80 src/phantomjs/component.js
... ... @@ -0,0 +1,80 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
  6 +/*global YSLOW*/
  7 +/*jslint browser: true, sloppy: true*/
  8 +
  9 +/**
  10 + * Parse details (HTTP headers, content, etc) from a
  11 + * given source and set component properties.
  12 + * @param o The object containing component details.
  13 + */
  14 +YSLOW.Component.prototype.setComponentDetails = function (o) {
  15 + var comp = this,
  16 +
  17 + parse = function (request, response) {
  18 + var xhr;
  19 +
  20 + // copy from the response object
  21 + comp.status = response.status;
  22 + comp.headers = {};
  23 + comp.raw_headers = '';
  24 + response.headers.forEach(function (header) {
  25 + comp.headers[header.name.toLowerCase()] = header.value;
  26 + comp.raw_headers += header.name + ': ' + header.value + '\n';
  27 + });
  28 + comp.req_headers = {};
  29 + request.headers.forEach(function (header) {
  30 + comp.req_headers[header.name.toLowerCase()] = header.value;
  31 + });
  32 + comp.method = request.method;
  33 + if (response.contentText) {
  34 + comp.body = response.contentText;
  35 + } else {
  36 + // try to fetch component again using sync xhr while
  37 + // content is not available through phantomjs.
  38 + // see: http://code.google.com/p/phantomjs/issues/detail?id=158
  39 + // and http://code.google.com/p/phantomjs/issues/detail?id=156
  40 + try {
  41 + xhr = new XMLHttpRequest();
  42 + xhr.open('GET', comp.url, false);
  43 + xhr.send();
  44 + comp.body = xhr.responseText;
  45 + } catch (err) {
  46 + comp.body = {
  47 + toString: function () {
  48 + return '';
  49 + },
  50 + length: response.bodySize || 0
  51 + };
  52 + }
  53 + }
  54 + // for security checking
  55 + comp.response_type = comp.type;
  56 + comp.cookie = (comp.headers['set-cookie'] || '') +
  57 + (comp.req_headers.cookie || '');
  58 + comp.nsize = parseInt(comp.headers['content-length'], 10) ||
  59 + response.bodySize;
  60 + comp.respTime = response.time;
  61 + comp.after_onload = (new Date(request.time)
  62 + .getTime()) > comp.parent.onloadTimestamp;
  63 +
  64 + // populate properties ignoring redirect
  65 + // resolution and image request
  66 + comp.populateProperties(false, true);
  67 +
  68 + comp.get_info_state = 'DONE';
  69 +
  70 + // notify parent ComponentSet that this component has gotten net response.
  71 + comp.parent.onComponentGetInfoStateChange({
  72 + 'comp': comp,
  73 + 'state': 'DONE'
  74 + });
  75 + };
  76 +
  77 + if (o.request && o.response) {
  78 + parse(o.request, o.response);
  79 + }
  80 +};
419 src/phantomjs/controller.js
... ... @@ -0,0 +1,419 @@
  1 +/**
  2 + * Copyright (c) 2012, Yahoo! Inc. All rights reserved.
  3 + * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
  4 + */
  5 +
  6 +/*global phantom, YSLOW*/
  7 +/*jslint browser: true, evil: true, sloppy: true, regexp: true*/
  8 +
  9 +/**
  10 + * JSLint is tolerating evil because there's a Function constructor needed to
  11 + * inject the content coming from phantom arguments and page resources which is
  12 + * later evaluated into the page in order to run YSlow.
  13 + */
  14 +
  15 +// parse args
  16 +var i, arg, page, urlCount, viewport,
  17 + webpage = require('webpage'),
  18 + args = phantom.args,
  19 + len = args.length,
  20 + urls = [],
  21 + yslowArgs = {
  22 + info: 'all',
  23 + format: 'json',
  24 + ruleset: 'ydefault',
  25 + beacon: false,
  26 + ua: false,
  27 + viewport: false,
  28 + headers: false,
  29 + console: 0,
  30 + threshold: 80
  31 + },
  32 + unaryArgs = {
  33 + help: false,
  34 + version: false,
  35 + dict: false,
  36 + verbose: false
  37 + },
  38 + argsAlias = {
  39 + i: 'info',
  40 + f: 'format',
  41 + r: 'ruleset',
  42 + h: 'help',
  43 + V: 'version',
  44 + d: 'dict',
  45 + u: 'ua',
  46 + vp: 'viewport',
  47 + c: 'console',
  48 + b: 'beacon',
  49 + v: 'verbose',
  50 + t: 'threshold',
  51 + ch: 'headers'
  52 + };
  53 +
  54 +// loop args
  55 +for (i = 0; i < len; i += 1) {
  56 + arg = args[i];
  57 + if (arg[0] !== '-') {
  58 + // url, normalize if needed
  59 + if (arg.indexOf('http') !== 0) {
  60 + arg = 'http://' + arg;
  61 + }
  62 + urls.push(arg);
  63 + }
  64 + arg = arg.replace(/^\-\-?/, '');
  65 + if (yslowArgs.hasOwnProperty(arg)) {
  66 + // yslow argument
  67 + i += 1;
  68 + yslowArgs[arg] = args[i];
  69 + } else if (yslowArgs.hasOwnProperty(argsAlias[arg])) {
  70 + // yslow argument alias
  71 + i += 1;
  72 + yslowArgs[argsAlias[arg]] = args[i];
  73 + } else if (unaryArgs.hasOwnProperty(arg)) {
  74 + // unary argument
  75 + unaryArgs[arg] = true;
  76 + } else if (unaryArgs.hasOwnProperty(argsAlias[arg])) {
  77 + // unary argument alias
  78 + unaryArgs[argsAlias[arg]] = true;
  79 + }
  80 +}
  81 +urlCount = urls.length;
  82 +
  83 +// check for version
  84 +if (unaryArgs.version) {
  85 + console.log('{{YSLOW_VERSION}}');
  86 + phantom.exit();
  87 +}
  88 +
  89 +// print usage
  90 +if (len === 0 || urlCount === 0 || unaryArgs.help) {
  91 + console.log([
  92 + '',
  93 + ' Usage: phantomjs [phantomjs options] ' + phantom.scriptName + ' [yslow options] [url ...]',
  94 + '',
  95 + ' PhantomJS Options:',
  96 + '',
  97 + ' http://y.ahoo.it/phantomjs/options',
  98 + '',
  99 + ' YSlow Options:',
  100 + '',
  101 + ' -h, --help output usage information',
  102 + ' -V, --version output the version number',
  103 + ' -i, --info <info> specify the information to display/log (basic|grade|stats|comps|all) [all]',
  104 + ' -f, --format <format> specify the output results format (json|xml|plain|tap) [json]',
  105 + ' -r, --ruleset <ruleset> specify the YSlow performance ruleset to be used (ydefault|yslow1|yblog) [ydefault]',
  106 + ' -b, --beacon <url> specify an URL to log the results',
  107 + ' -d, --dict include dictionary of results fields',
  108 + ' -v, --verbose output beacon response information',
  109 + ' -t, --threshold <score> for test formats, the threshold to test scores ([0-100]|[A-F]|{JSON}) [80]',
  110 + ' e.g.: -t B or -t 75 or -t \'{"overall": "B", "ycdn": "F", "yexpires": 85}\'',
  111 + ' -u, --ua "<user agent>" specify the user agent string sent to server when the page requests resources',
  112 + ' -vp, --viewport <WxH> specify page viewport size WxY, where W = width and H = height [400x300]',
  113 + ' -ch, --headers <JSON> specify custom request headers, e.g.: -ch \'{"Cookie": "foo=bar"}\'',
  114 + ' -c, --console <level> output page console messages (0: none, 1: message, 2: message + line + source) [0]',
  115 + '',
  116 + ' Examples:',
  117 + '',
  118 + ' phantomjs ' + phantom.scriptName + ' http://yslow.org',
  119 + ' phantomjs ' + phantom.scriptName + ' -i grade -f xml www.yahoo.com www.cnn.com www.nytimes.com',
  120 + ' phantomjs ' + phantom.scriptName + ' -info all --format plain --ua "MSIE 9.0" www.yahoo.com',
  121 + ' phantomjs ' + phantom.scriptName + ' -i basic --rulseset yslow1 -d http://yslow.org',
  122 + ' phantomjs ' + phantom.scriptName + ' -i grade -b http://beaconserver.com/ -v www.yahoo.com',
  123 + ' phantomjs --load-plugins=yes ' + phantom.scriptName + ' -vp 800x600 http://www.yahoo.com',
  124 + ' phantomjs ' + phantom.scriptName + ' -i grade -f tap -t 85 http://yslow.org',
  125 + ''
  126 + ].join('\n'));
  127 + phantom.exit();
  128 +}
  129 +
  130 +// set yslow unary args
  131 +yslowArgs.dict = unaryArgs.dict;
  132 +yslowArgs.verbose = unaryArgs.verbose;
  133 +
  134 +// loop through urls
  135 +urls.forEach(function (url) {
  136 + var page = webpage.create();
  137 +
  138 + page.resources = {};
  139 +
  140 + // allow x-domain requests, used to retrieve components content
  141 + page.settings.webSecurityEnabled = false;
  142 +
  143 + // request
  144 + page.onResourceRequested = function (req) {
  145 + page.resources[req.url] = {
  146 + request: req
  147 + };
  148 + };
  149 +
  150 + // response
  151 + page.onResourceReceived = function (res) {
  152 + var info,
  153 + resp = page.resources[res.url].response;
  154 +
  155 + if (!resp) {
  156 + page.resources[res.url].response = res;
  157 + } else {
  158 + for (info in res) {
  159 + if (res.hasOwnProperty(info)) {
  160 + resp[info] = res[info];
  161 + }
  162 + }
  163 + }
  164 + };
  165 +
  166 + // enable console output, useful for debugging
  167 + yslowArgs.console = parseInt(yslowArgs.console, 10) || 0;
  168 + if (yslowArgs.console) {
  169 + if (yslowArgs.console === 1) {
  170 + page.onConsoleMessage = function (msg) {
  171 + console.log(msg);
  172 + };
  173 + } else {
  174 + page.onConsoleMessage = function (msg, line, source) {
  175 + console.log(JSON.stringify({
  176 + message: msg,
  177 + lineNumber: line,
  178 + source: source
  179 + }, null, 4));
  180 + };
  181 + }
  182 + }
  183 +
  184 + // set user agent string
  185 + if (yslowArgs.ua) {
  186 + page.settings.userAgent = yslowArgs.ua;
  187 + }
  188 +
  189 + // set page viewport
  190 + if (yslowArgs.viewport) {
  191 + viewport = yslowArgs.viewport.toLowerCase();
  192 + page.viewportSize = {
  193 + width: parseInt(viewport.slice(0, viewport.indexOf('x')), 10) ||
  194 + page.viewportSize.width,
  195 + height: parseInt(viewport.slice(viewport.indexOf('x') + 1), 10) ||
  196 + page.viewportSize.height
  197 + };
  198 + }
  199 +
  200 + // set custom headers
  201 + if (yslowArgs.headers) {
  202 + try {
  203 + page.customHeaders = JSON.parse(yslowArgs.headers);
  204 + } catch (err) {
  205 + console.log('Invalid custom headers: ' + err);
  206 + }
  207 + }
  208 +
  209 + // open page
  210 + page.startTime = new Date();
  211 + page.open(url, function (status) {
  212 + var yslow, ysphantomjs, controller, evalFunc, loadTime, url, resp,
  213 + startTime = page.startTime,
  214 + resources = page.resources;
  215 +
  216 + if (status !== 'success') {
  217 + console.log('FAIL to load ' + url);
  218 + } else {
  219 + // page load time
  220 + loadTime = new Date() - startTime;
  221 +
  222 + // set resources response time
  223 + for (url in resources) {
  224 + if (resources.hasOwnProperty(url)) {
  225 + resp = resources[url].response;
  226 + if (resp) {
  227 + resp.time = new Date(resp.time) - startTime;
  228 + }
  229 + }
  230 + }
  231 +
  232 + // yslow wrapper to be evaluated by page
  233 + yslow = function () {
  234 + //YSLOW HERE
  235 + };
  236 +
  237 + // serialize YSlow phantomjs object
  238 + // resources, yslow args and page load time
  239 + ysphantomjs = 'YSLOW.phantomjs = {' +
  240 + 'resources: ' + JSON.stringify(resources) + ',' +
  241 + 'args: ' + JSON.stringify(yslowArgs) + ',' +
  242 + 'loadTime: ' + JSON.stringify(loadTime) + '};';
  243 +
  244 + // YSlow phantomjs controller
  245 + controller = function () {
  246 + YSLOW.phantomjs.run = function () {
  247 + try {
  248 + var results, xhr, output, threshold,
  249 + doc = document,
  250 + ys = YSLOW,
  251 + yscontext = new ys.context(doc),
  252 + yspeeler = ys.peeler,
  253 + comps = yspeeler.peel(doc),
  254 + baseHref = yspeeler.getBaseHref(doc),
  255 + cset = new ys.ComponentSet(doc),
  256 + ysphantomjs = ys.phantomjs,
  257 + resources = ysphantomjs.resources,
  258 + args = ysphantomjs.args,
  259 + ysutil = ys.util,
  260 +
  261 + // format out with appropriate content type
  262 + formatOutput = function (content) {
  263 + switch (args.format) {
  264 + case 'xml':
  265 + return {
  266 + content: ysutil.objToXML(content),
  267 + contentType: 'text/xml'
  268 + };
  269 + case 'plain':
  270 + return {
  271 + content: ysutil.prettyPrintResults(
  272 + content
  273 + ),
  274 + contentType: 'text/plain'
  275 + };
  276 + case 'tap':
  277 + try {
  278 + threshold = JSON.parse(args.threshold);
  279 + } catch (err) {
  280 + threshold = args.threshold;
  281 + }
  282 + return {
  283 + content: ysutil.formatAsTAP(
  284 + ysutil.testResults(
  285 + content,
  286 + threshold
  287 + )
  288 + ),
  289 + contentType: 'text/plain'
  290 + };
  291 + default:
  292 + return {
  293 + content: JSON.stringify(content),
  294 + contentType: 'application/json'
  295 + };
  296 + }
  297 + },
  298 +
  299 + // format raw headers into object
  300 + formatHeaders = function (headers) {
  301 + var reHeader = /^([^:]+):\s*([\s\S]+)$/,
  302 + reLineBreak = /[\n\r]/g,
  303 + header = {};
  304 +
  305 + headers.split('\n').forEach(function (h) {
  306 + var m = reHeader.exec(
  307 + h.replace(reLineBreak, '')
  308 + );
  309 +
  310 + if (m) {
  311 + header[m[1]] = m[2];
  312 + }
  313 + });
  314 +
  315 + return header;
  316 + };
  317 +
  318 + comps.forEach(function (comp) {
  319 + var res = resources[comp.href] || {};
  320 +
  321 + cset.addComponent(
  322 + comp.href,
  323 + comp.type,
  324 + comp.base || baseHref,
  325 + {
  326 + obj: comp.obj,
  327 + request: res.request,
  328 + response: res.response
  329 + }
  330 + );
  331 + });
  332 +
  333 + // refinement
  334 + cset.inline = ysutil.getInlineTags(doc);
  335 + cset.domElementsCount = ysutil.countDOMElements(doc);
  336 + cset.cookies = cset.doc_comp.cookie;
  337 + cset.components = ysutil.setInjected(doc,
  338 + cset.components, cset.doc_comp.body);
  339 +
  340 + // run analysis
  341 + yscontext.component_set = cset;
  342 + ys.controller.lint(doc, yscontext, args.ruleset);
  343 + yscontext.result_set.url = baseHref;
  344 + yscontext.PAGE.t_done = ysphantomjs.loadTime;
  345 + yscontext.collectStats();
  346 + results = ysutil.getResults(yscontext, args.info);
  347 +
  348 + // prepare output results
  349 + if (args.dict && args.format !== 'plain') {
  350 + results.dictionary = ysutil.getDict(args.info,
  351 + args.ruleset);
  352 + }
  353 + output = formatOutput(results);
  354 +
  355 + // send beacon
  356 + if (args.beacon) {
  357 + try {
  358 + xhr = new XMLHttpRequest();
  359 + xhr.onreadystatechange = function () {
  360 + // in verbose mode, include
  361 + // beacon response info
  362 + if (xhr.readyState === 4 && args.verbose) {
  363 + results.beacon = {
  364 + status: xhr.status,
  365 + headers: formatHeaders(
  366 + xhr.getAllResponseHeaders()
  367 + ),
  368 + body: xhr.responseText
  369 + };
  370 + output = formatOutput(results);
  371 + }
  372 + };
  373 + xhr.open('POST', args.beacon, false);
  374 + xhr.setRequestHeader('Content-Type',
  375 + output.contentType);
  376 + xhr.send(output.content);
  377 + } catch (xhrerr) {
  378 + // include error on beacon
  379 + if (args.verbose) {
  380 + results.beacon = {
  381 + error: xhrerr
  382 + };
  383 + output = formatOutput(results);
  384 + }
  385 + }
  386 + }
  387 +
  388 + return output.content;
  389 + } catch (err) {
  390 + return err;
  391 + }
  392 + };
  393 +
  394 + return YSLOW.phantomjs.run();
  395 + };
  396 +
  397 + // serialize then combine:
  398 + // YSlow + page resources + args + loadtime + controller
  399 + yslow = yslow.toString();
  400 + yslow = yslow.slice(13, yslow.length - 1);
  401 + // minification removes last ';'
  402 + if (yslow.slice(yslow.length - 1) !== ';') {
  403 + yslow += ';';
  404 + }
  405 + controller = controller.toString();
  406 + controller = controller.slice(13, controller.length - 1);
  407 + evalFunc = new Function(yslow + ysphantomjs + controller);
  408 +
  409 + // evaluate script and log results
  410 + console.log(page.evaluate(evalFunc));
  411 + }
  412 +
  413 + // finish phantomjs
  414 + urlCount -= 1;
  415 + if (urlCount === 0) {
  416 + phantom.exit();
  417 + }
  418 + });
  419 +});

No commit comments for this range

Something went wrong with that request. Please try again.