diff --git a/Recipe.min.js b/Recipe.min.js index f1937f0..f797fe0 100644 --- a/Recipe.min.js +++ b/Recipe.min.js @@ -1781,45 +1781,77 @@ void function() { try { } catch (ex) { /* do something maybe */ throw ex; } }(); -/* - RECIPE: Edit controls on the web +/* + RECIPE: scriptElemStats ------------------------------------------------------------- - Author: Grisha Lyukshin - Description: Counts pages that have either input, textarea, or content editable elements + Author: thomasmo + Description: Collect stats for script elements */ + void function() { - window.CSSUsage.StyleWalker.recipesToRun.push( function editControls(/*HTML DOM Element*/ element, results) { - - // we only care about special kind of inputs - if(element.nodeName.toLowerCase() === "input" && - (element.getAttribute("type").toLowerCase() === "email" || - element.getAttribute("type").toLowerCase() === "number" || - element.getAttribute("type").toLowerCase() === "search" || - element.getAttribute("type").toLowerCase() === "tel" || - element.getAttribute("type").toLowerCase() === "url" || - element.getAttribute("type").toLowerCase() === "text")) - { - results["input"] = results["input"] || { count: 0 }; - results["input"].count++; - } - else if (element.nodeName.toLowerCase() === "textarea") - { - results["textarea"] = results["textarea"] || { count: 0 }; - results["textarea"].count++; - } - else if (element.nodeName.toLowerCase() === "div" || element.nodeName.toLowerCase() === "p" || element.nodeName.toLowerCase() === "table") - { - if(element.getAttribute("contenteditable").toLowerCase() === "true" || element.getAttribute("contenteditable").toLowerCase() === "plain-text") - { - results["contenteditable"] = results["contenteditable"] || { count: 0 }; - results["contenteditable"].count++; - } + window.CSSUsage.StyleWalker.recipesToRun.push( function scriptElemStats( element, results) { + window._elementCount = window._elementCount || 0; + window._elementCount++; + if(element.nodeName.toLowerCase() == "script") { + results["data"] = results["data"] || []; + + var elemData = { + index : window._elementCount, + async : element.hasAttribute("async") ? 1 : 0, + defer : element.hasAttribute("defer") ? 1 : 0, + externalSrc : element.hasAttribute("src") ? 1 : 0, + parentElem : element.parentNode.nodeName.toLowerCase() + }; + + results["data"].push(elemData); } + return results; }); }(); +/* +void function() { + window.CSSUsage.StyleWalker.recipesToRun.push( function scriptElemStats( element, results) { + if(element.nodeName.toLowerCase() == "script") { + results["data"] = results["data"] || { + total : 0, + async : 0, + defer : 0, + externalSrc : 0, + parentElemHead : 0, + parentElemBody : 0, + parentElemOther : 0 + }; + + var result = results["data"]; + result.total++; + + if (element.hasAttribute("async")) { + result.async++; + } + if (element.hasAttribute("defer")) { + result.defer++; + } + if (element.hasAttribute("src")) { + result.externalSrc++; + } + + parentElemName = element.parentNode.nodeName.toLowerCase(); + if (parentElemName === "head") { + result.parentElemHead++; + } else if (parentElemName === "body") { + result.parentElemBody++; + } else { + result.parentElemOther++; + } + } + + return results; + }); +}(); +*/ // // This file is only here to create the TSV // necessary to collect the data from the crawler diff --git a/cssUsage.src.js b/cssUsage.src.js index 43d4d45..8c96c4f 100644 --- a/cssUsage.src.js +++ b/cssUsage.src.js @@ -437,63 +437,63 @@ function getPatternUsage(results, domClasses, cssClasses) { return results; } -void function() { - - window.HtmlUsage = {}; - - // This function has been added to the elementAnalyzers in - // CSSUsage.js under onready() - // is an HTMLElement passed in by elementAnalyzers - window.HtmlUsage.GetNodeName = function (element) { - - // If the browser doesn't recognize the element - throw it away - if(element instanceof HTMLUnknownElement) { - return; - } - - var node = element.nodeName; - - var tags = HtmlUsageResults.tags || (HtmlUsageResults.tags = {}); - var tag = tags[node] || (tags[node] = 0); - tags[node]++; - - GetAttributes(element, node); - } - - function GetAttributes(element, node) { - for(var i = 0; i < element.attributes.length; i++) { - var att = element.attributes[i]; - - if(IsValidAttribute(element, att.nodeName)) { - var attributes = HtmlUsageResults.attributes || (HtmlUsageResults.attributes = {}); - var attribute = attributes[att.nodeName] || (attributes[att.nodeName] = {}); - var attributeTag = attribute[node] || (attribute[node] = {count: 0}); - attributeTag.count++; - } - } - } - - function IsValidAttribute(element, attname) { - // We need to convert className - if(attname == "class") { - attname = "className"; - } - - if(attname == "classname") { - return false; - } - - // Only keep attributes that are not data - if(attname.indexOf('data-') != -1) { - return false; - } - - if(typeof(element[attname]) == "undefined") { - return false; - } - - return true; - } +void function() { + + window.HtmlUsage = {}; + + // This function has been added to the elementAnalyzers in + // CSSUsage.js under onready() + // is an HTMLElement passed in by elementAnalyzers + window.HtmlUsage.GetNodeName = function (element) { + + // If the browser doesn't recognize the element - throw it away + if(element instanceof HTMLUnknownElement) { + return; + } + + var node = element.nodeName; + + var tags = HtmlUsageResults.tags || (HtmlUsageResults.tags = {}); + var tag = tags[node] || (tags[node] = 0); + tags[node]++; + + GetAttributes(element, node); + } + + function GetAttributes(element, node) { + for(var i = 0; i < element.attributes.length; i++) { + var att = element.attributes[i]; + + if(IsValidAttribute(element, att.nodeName)) { + var attributes = HtmlUsageResults.attributes || (HtmlUsageResults.attributes = {}); + var attribute = attributes[att.nodeName] || (attributes[att.nodeName] = {}); + var attributeTag = attribute[node] || (attribute[node] = {count: 0}); + attributeTag.count++; + } + } + } + + function IsValidAttribute(element, attname) { + // We need to convert className + if(attname == "class") { + attname = "className"; + } + + if(attname == "classname") { + return false; + } + + // Only keep attributes that are not data + if(attname.indexOf('data-') != -1) { + return false; + } + + if(typeof(element[attname]) == "undefined") { + return false; + } + + return true; + } }(); void function() { try { @@ -1781,217 +1781,261 @@ void function() { try { } catch (ex) { /* do something maybe */ throw ex; } }(); -/* - RECIPE: z-index on static flex items +/* + RECIPE: scriptElemStats ------------------------------------------------------------- - Author: Francois Remy - Description: Get count of flex items who should create a stacking context but do not really + Author: thomasmo + Description: Collect stats for script elements */ -void function() { - - window.CSSUsage.StyleWalker.recipesToRun.push( function zstaticflex(/*HTML DOM Element*/ element, results) { - if(!element.parentElement) return; - // the problem happens if the element is a flex item with static position and non-auto z-index - if(getComputedStyle(element.parentElement).display != 'flex') return results; - if(getComputedStyle(element).position != 'static') return results; - if(getComputedStyle(element).zIndex != 'auto') { - results.likely = 1; - } - - // the problem might happen if z-index could ever be non-auto - if(element.CSSUsage["z-index"] && element.CSSUsage["z-index"].valuesArray.length > 0) { - results.possible = 1; +void function() { + window.CSSUsage.StyleWalker.recipesToRun.push( function scriptElemStats( element, results) { + window._elementCount = window._elementCount || 0; + window._elementCount++; + if(element.nodeName.toLowerCase() == "script") { + results["data"] = results["data"] || []; + + var elemData = { + index : window._elementCount, + async : element.hasAttribute("async") ? 1 : 0, + defer : element.hasAttribute("defer") ? 1 : 0, + externalSrc : element.hasAttribute("src") ? 1 : 0, + parentElem : element.parentNode.nodeName.toLowerCase() + }; + + results["data"].push(elemData); } + return results; }); }(); -// -// This file is only here to create the TSV -// necessary to collect the data from the crawler -// +/* void function() { - - /* String hash function - /* credits goes to http://erlycoder.com/49/javascript-hash-functions-to-convert-string-into-integer-hash- */ - const hashCodeOf = (str) => { - var hash = 5381; var char = 0; - for (var i = 0; i < str.length; i++) { - char = str.charCodeAt(i); - hash = ((hash << 5) + hash) + char; - } - return hash; - } - - var ua = navigator.userAgent; - var uaName = ua.indexOf('Edge')>=0 ? 'EDGE' :ua.indexOf('Chrome')>=0 ? 'CHROME' : 'FIREFOX'; - window.INSTRUMENTATION_RESULTS = { - UA: uaName, - UASTRING: ua, - UASTRING_HASH: hashCodeOf(ua), - URL: location.href, - TIMESTAMP: Date.now(), - css: {/* see CSSUsageResults */}, - html: {/* see HtmlUsageResults */}, - dom: {}, - scripts: {/* "bootstrap.js": 1 */}, - }; - window.INSTRUMENTATION_RESULTS_TSV = []; - - /* make the script work in the context of a webview */ - try { - var console = window.console || (window.console={log:function(){},warn:function(){},error:function(){}}); - console.unsafeLog = console.log; - console.log = function() { - try { - this.unsafeLog.apply(this,arguments); - } catch(ex) { - // ignore - } - }; - } catch (ex) { - // we tried... - } -}(); - -window.onCSSUsageResults = function onCSSUsageResults(CSSUsageResults) { - // Collect the results (css) - INSTRUMENTATION_RESULTS.css = CSSUsageResults; - INSTRUMENTATION_RESULTS.html = HtmlUsageResults; - INSTRUMENTATION_RESULTS.recipe = RecipeResults; - - // Convert it to a more efficient format - INSTRUMENTATION_RESULTS_TSV = convertToTSV(INSTRUMENTATION_RESULTS); - - // Remove tabs and new lines from the data - for(var i = INSTRUMENTATION_RESULTS_TSV.length; i--;) { - var row = INSTRUMENTATION_RESULTS_TSV[i]; - for(var j = row.length; j--;) { - row[j] = (''+row[j]).replace(/(\s|\r|\n)+/g, ' '); - } - } - - // Convert into one signle tsv file - var tsvString = INSTRUMENTATION_RESULTS_TSV.map((row) => (row.join('\t'))).join('\n'); - appendTSV(tsvString); - - // Add it to the document dom - function appendTSV(content) { - if(window.debugCSSUsage) console.log("Trying to append"); - var output = document.createElement('script'); - output.id = "css-usage-tsv-results"; - output.textContent = tsvString; - output.type = 'text/plain'; - document.querySelector('head').appendChild(output); - var successfulAppend = checkAppend(); - } - - function checkAppend() { - if(window.debugCSSUsage) if(window.debugCSSUsage) console.log("Checking append"); - var elem = document.getElementById('css-usage-tsv-results'); - if(elem === null) { - if(window.debugCSSUsage) console.log("Element not appended"); - if(window.debugCSSUsage) console.log("Trying to append again"); - appendTSV(); - } - else { - if(window.debugCSSUsage) console.log("Element successfully found"); - } - } - - /** convert the instrumentation results to a spreadsheet for analysis */ - function convertToTSV(INSTRUMENTATION_RESULTS) { - if(window.debugCSSUsage) console.log("Converting to TSV"); - - var VALUE_COLUMN = 4; - var finishedRows = []; - var currentRowTemplate = [ - INSTRUMENTATION_RESULTS.UA, - INSTRUMENTATION_RESULTS.UASTRING_HASH, - INSTRUMENTATION_RESULTS.URL, - INSTRUMENTATION_RESULTS.TIMESTAMP, - 0 - ]; - - currentRowTemplate.push('ua'); - convertToTSV({identifier: INSTRUMENTATION_RESULTS.UASTRING}); - currentRowTemplate.pop(); - - currentRowTemplate.push('css'); - convertToTSV(INSTRUMENTATION_RESULTS['css']); - currentRowTemplate.pop(); - - currentRowTemplate.push('dom'); - convertToTSV(INSTRUMENTATION_RESULTS['dom']); - currentRowTemplate.pop(); - - currentRowTemplate.push('html'); - convertToTSV(INSTRUMENTATION_RESULTS['html']); - currentRowTemplate.pop(); + window.CSSUsage.StyleWalker.recipesToRun.push( function scriptElemStats( element, results) { + if(element.nodeName.toLowerCase() == "script") { + results["data"] = results["data"] || { + total : 0, + async : 0, + defer : 0, + externalSrc : 0, + parentElemHead : 0, + parentElemBody : 0, + parentElemOther : 0 + }; + + var result = results["data"]; + result.total++; + + if (element.hasAttribute("async")) { + result.async++; + } + if (element.hasAttribute("defer")) { + result.defer++; + } + if (element.hasAttribute("src")) { + result.externalSrc++; + } - currentRowTemplate.push('recipe'); - convertToTSV(INSTRUMENTATION_RESULTS['recipe']); - currentRowTemplate.pop(); - - var l = finishedRows[0].length; - finishedRows.sort((a,b) => { - for(var i = VALUE_COLUMN+1; ib[i]) return +1; - } - return 0; - }); - - return finishedRows; - - /** helper function doing the actual conversion */ - function convertToTSV(object) { - if(object==null || object==undefined || typeof object == 'number' || typeof object == 'string') { - finishedRows.push(new Row(currentRowTemplate, ''+object)); - } else { - for(var key in object) { - if({}.hasOwnProperty.call(object,key)) { - currentRowTemplate.push(key); - convertToTSV(object[key]); - currentRowTemplate.pop(); - } - } - } - } - - /** constructor for a row of our table */ - function Row(currentRowTemplate, value) { - - // Initialize an empty row with enough columns - var row = [ - /*UANAME: edge */'', - /*UASTRING: mozilla/5.0 (...) */'', - /*URL: http://.../... */'', - /*TIMESTAMP: 1445622257303 */'', - /*VALUE: 0|1|... */'', - /*DATATYPE: css|dom|html... */'', - /*SUBTYPE: props|types|api|... */'', - /*NAME: font-size|querySelector|... */'', - /*CONTEXT: count|values|... */'', - /*SUBCONTEXT: px|em|... */'', - /*... */'', - /*... */'', - ]; - - // Copy the column values from the template - for(var i = currentRowTemplate.length; i--;) { - row[i] = currentRowTemplate[i]; - } - - // Add the value to the row - row[VALUE_COLUMN] = value; - - return row; - } + parentElemName = element.parentNode.nodeName.toLowerCase(); + if (parentElemName === "head") { + result.parentElemHead++; + } else if (parentElemName === "body") { + result.parentElemBody++; + } else { + result.parentElemOther++; + } + } - } + return results; + }); +}(); +*/ +// +// This file is only here to create the TSV +// necessary to collect the data from the crawler +// +void function() { + + /* String hash function + /* credits goes to http://erlycoder.com/49/javascript-hash-functions-to-convert-string-into-integer-hash- */ + const hashCodeOf = (str) => { + var hash = 5381; var char = 0; + for (var i = 0; i < str.length; i++) { + char = str.charCodeAt(i); + hash = ((hash << 5) + hash) + char; + } + return hash; + } + + var ua = navigator.userAgent; + var uaName = ua.indexOf('Edge')>=0 ? 'EDGE' :ua.indexOf('Chrome')>=0 ? 'CHROME' : 'FIREFOX'; + window.INSTRUMENTATION_RESULTS = { + UA: uaName, + UASTRING: ua, + UASTRING_HASH: hashCodeOf(ua), + URL: location.href, + TIMESTAMP: Date.now(), + css: {/* see CSSUsageResults */}, + html: {/* see HtmlUsageResults */}, + dom: {}, + scripts: {/* "bootstrap.js": 1 */}, + }; + window.INSTRUMENTATION_RESULTS_TSV = []; + + /* make the script work in the context of a webview */ + try { + var console = window.console || (window.console={log:function(){},warn:function(){},error:function(){}}); + console.unsafeLog = console.log; + console.log = function() { + try { + this.unsafeLog.apply(this,arguments); + } catch(ex) { + // ignore + } + }; + } catch (ex) { + // we tried... + } +}(); + +window.onCSSUsageResults = function onCSSUsageResults(CSSUsageResults) { + // Collect the results (css) + INSTRUMENTATION_RESULTS.css = CSSUsageResults; + INSTRUMENTATION_RESULTS.html = HtmlUsageResults; + INSTRUMENTATION_RESULTS.recipe = RecipeResults; + + // Convert it to a more efficient format + INSTRUMENTATION_RESULTS_TSV = convertToTSV(INSTRUMENTATION_RESULTS); + + // Remove tabs and new lines from the data + for(var i = INSTRUMENTATION_RESULTS_TSV.length; i--;) { + var row = INSTRUMENTATION_RESULTS_TSV[i]; + for(var j = row.length; j--;) { + row[j] = (''+row[j]).replace(/(\s|\r|\n)+/g, ' '); + } + } + + // Convert into one signle tsv file + var tsvString = INSTRUMENTATION_RESULTS_TSV.map((row) => (row.join('\t'))).join('\n'); + appendTSV(tsvString); + + // Add it to the document dom + function appendTSV(content) { + if(window.debugCSSUsage) console.log("Trying to append"); + var output = document.createElement('script'); + output.id = "css-usage-tsv-results"; + output.textContent = tsvString; + output.type = 'text/plain'; + document.querySelector('head').appendChild(output); + var successfulAppend = checkAppend(); + } + + function checkAppend() { + if(window.debugCSSUsage) if(window.debugCSSUsage) console.log("Checking append"); + var elem = document.getElementById('css-usage-tsv-results'); + if(elem === null) { + if(window.debugCSSUsage) console.log("Element not appended"); + if(window.debugCSSUsage) console.log("Trying to append again"); + appendTSV(); + } + else { + if(window.debugCSSUsage) console.log("Element successfully found"); + } + } + + /** convert the instrumentation results to a spreadsheet for analysis */ + function convertToTSV(INSTRUMENTATION_RESULTS) { + if(window.debugCSSUsage) console.log("Converting to TSV"); + + var VALUE_COLUMN = 4; + var finishedRows = []; + var currentRowTemplate = [ + INSTRUMENTATION_RESULTS.UA, + INSTRUMENTATION_RESULTS.UASTRING_HASH, + INSTRUMENTATION_RESULTS.URL, + INSTRUMENTATION_RESULTS.TIMESTAMP, + 0 + ]; + + currentRowTemplate.push('ua'); + convertToTSV({identifier: INSTRUMENTATION_RESULTS.UASTRING}); + currentRowTemplate.pop(); + + currentRowTemplate.push('css'); + convertToTSV(INSTRUMENTATION_RESULTS['css']); + currentRowTemplate.pop(); + + currentRowTemplate.push('dom'); + convertToTSV(INSTRUMENTATION_RESULTS['dom']); + currentRowTemplate.pop(); + + currentRowTemplate.push('html'); + convertToTSV(INSTRUMENTATION_RESULTS['html']); + currentRowTemplate.pop(); + + currentRowTemplate.push('recipe'); + convertToTSV(INSTRUMENTATION_RESULTS['recipe']); + currentRowTemplate.pop(); + + var l = finishedRows[0].length; + finishedRows.sort((a,b) => { + for(var i = VALUE_COLUMN+1; ib[i]) return +1; + } + return 0; + }); + + return finishedRows; + + /** helper function doing the actual conversion */ + function convertToTSV(object) { + if(object==null || object==undefined || typeof object == 'number' || typeof object == 'string') { + finishedRows.push(new Row(currentRowTemplate, ''+object)); + } else { + for(var key in object) { + if({}.hasOwnProperty.call(object,key)) { + currentRowTemplate.push(key); + convertToTSV(object[key]); + currentRowTemplate.pop(); + } + } + } + } + + /** constructor for a row of our table */ + function Row(currentRowTemplate, value) { + + // Initialize an empty row with enough columns + var row = [ + /*UANAME: edge */'', + /*UASTRING: mozilla/5.0 (...) */'', + /*URL: http://.../... */'', + /*TIMESTAMP: 1445622257303 */'', + /*VALUE: 0|1|... */'', + /*DATATYPE: css|dom|html... */'', + /*SUBTYPE: props|types|api|... */'', + /*NAME: font-size|querySelector|... */'', + /*CONTEXT: count|values|... */'', + /*SUBCONTEXT: px|em|... */'', + /*... */'', + /*... */'', + ]; + + // Copy the column values from the template + for(var i = currentRowTemplate.length; i--;) { + row[i] = currentRowTemplate[i]; + } + + // Add the value to the row + row[VALUE_COLUMN] = value; + + return row; + } + + } }; // // Execution scheduler: diff --git a/src/recipes/editControls.js b/src/recipes/archive/editControls.js similarity index 100% rename from src/recipes/editControls.js rename to src/recipes/archive/editControls.js diff --git a/src/recipes/scriptelem.js b/src/recipes/scriptelem.js new file mode 100644 index 0000000..e71fef9 --- /dev/null +++ b/src/recipes/scriptelem.js @@ -0,0 +1,71 @@ +/* + RECIPE: scriptElemStats + ------------------------------------------------------------- + Author: thomasmo + Description: Collect stats for script elements +*/ + + +void function() { + window.CSSUsage.StyleWalker.recipesToRun.push( function scriptElemStats( element, results) { + window._elementCount = window._elementCount || 0; + window._elementCount++; + if(element.nodeName.toLowerCase() == "script") { + results["data"] = results["data"] || []; + + var elemData = { + index : window._elementCount, + async : element.hasAttribute("async") ? 1 : 0, + defer : element.hasAttribute("defer") ? 1 : 0, + externalSrc : element.hasAttribute("src") ? 1 : 0, + parentElem : element.parentNode.nodeName.toLowerCase() + }; + + results["data"].push(elemData); + } + + return results; + }); +}(); + +/* +void function() { + window.CSSUsage.StyleWalker.recipesToRun.push( function scriptElemStats( element, results) { + if(element.nodeName.toLowerCase() == "script") { + results["data"] = results["data"] || { + total : 0, + async : 0, + defer : 0, + externalSrc : 0, + parentElemHead : 0, + parentElemBody : 0, + parentElemOther : 0 + }; + + var result = results["data"]; + result.total++; + + if (element.hasAttribute("async")) { + result.async++; + } + if (element.hasAttribute("defer")) { + result.defer++; + } + if (element.hasAttribute("src")) { + result.externalSrc++; + } + + parentElemName = element.parentNode.nodeName.toLowerCase(); + if (parentElemName === "head") { + result.parentElemHead++; + } else if (parentElemName === "body") { + result.parentElemBody++; + } else { + result.parentElemOther++; + } + } + + return results; + }); +}(); +*/ \ No newline at end of file diff --git a/tests/recipes/scriptElemTest.html b/tests/recipes/scriptElemTest.html new file mode 100644 index 0000000..aa4334a --- /dev/null +++ b/tests/recipes/scriptElemTest.html @@ -0,0 +1,27 @@ + + + + + + + + + + +Tests scriptElemStats + +To see results in F12 easily, paste + window.RecipeResults.scriptElemStats.data) + + + + + + \ No newline at end of file