diff --git a/Recipe.min.js b/Recipe.min.js
index f1937f0..3b22c38 100644
--- a/Recipe.min.js
+++ b/Recipe.min.js
@@ -1,2077 +1,236 @@
-void function() {
-
- var _ = (a => new ArrayWrapper(a));
- _.mapInline = mapInline;
- _.map = map; /*.......*/ map.bind = (()=>map);
- _.filter = filter; /*.*/ filter.bind = (()=>filter);
- _.reduce = reduce; /*.*/ reduce.bind = (()=>reduce);
- window.CSSUsageLodash = _;
- // test case:
- // 35 = CSSUsageLodash([1,2,3,4,5]).map(v => v*v).filter(v => v%2).reduce(0, (a,b)=>(a+b)).value()
-
- function ArrayWrapper(array) {
- this.source = array;
- this.mapInline = function(f) { mapInline(this.source, f); return this; };
- this.map = function(f) { this.source = map(this.source, f); return this; };
- this.filter = function(f) { this.source = filter(this.source, f); return this; };
- this.reduce = function(v,f) { this.source = reduce(this.source, f, v); return this; };
- this.value = function() { return this.source };
- }
-
- function map(source, transform) {
- var clone = new Array(source.length);
- for(var i = source.length; i--;) {
- clone[i] = transform(source[i]);
- }
- return clone;
- }
-
- function mapInline(source, transform) {
- for(var i = source.length; i--;) {
- source[i] = transform(source[i]);
- }
- return source;
- }
-
- function filter(source, shouldValueBeIncluded) {
- var clone = new Array(source.length), i=0;
- for(var s = 0; s <= source.length; s++) {
- var value = source[s];
- if(shouldValueBeIncluded(value)) {
- clone[i++] = value
- }
- }
- clone.length = i;
- return clone;
- }
-
- function reduce(source, computeReduction, reduction) {
- for(var s = 0; s <= source.length; s++) {
- var value = source[s];
- reduction = computeReduction(reduction, value);
- }
- return reduction;
- }
-
-}();
-/*!
- * Based on:
- * https://github.com/gilmoreorless/css-shorthand-properties
- * MIT Licensed: http://gilmoreorless.mit-license.org/
- */
-void function () {
- /**
- * Data collated from multiple W3C specs: http://www.w3.org/Style/CSS/current-work
- */
- var shorthands = this.shorthandProperties = {
-
- // CSS 2.1: http://www.w3.org/TR/CSS2/propidx.html
- 'list-style': ['-type', '-position', '-image'],
- 'margin': ['-top', '-right', '-bottom', '-left'],
- 'outline': ['-width', '-style', '-color'],
- 'padding': ['-top', '-right', '-bottom', '-left'],
-
- // CSS Backgrounds and Borders Module Level 3: http://www.w3.org/TR/css3-background/
- 'background': ['-image', '-position', '-size', '-repeat', '-origin', '-clip', '-attachment', '-color'],
- 'background-repeat': ['-x','-y'],
- 'background-position': ['-x','-y'],
- 'border': ['-width', '-style', '-color'],
- 'border-color': ['border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color'],
- 'border-style': ['border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style'],
- 'border-width': ['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'],
- 'border-top': ['-width', '-style', '-color'],
- 'border-right': ['-width', '-style', '-color'],
- 'border-bottom': ['-width', '-style', '-color'],
- 'border-left': ['-width', '-style', '-color'],
- 'border-radius': ['border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius'],
- 'border-image': ['-source', '-slice', '-width', '-outset', '-repeat'],
-
- // CSS Fonts Module Level 3: http://www.w3.org/TR/css3-fonts/
- 'font': ['-style', '-variant', '-weight', '-stretch', '-size', 'line-height', '-family'],
- 'font-variant': ['-ligatures', '-alternates', '-caps', '-numeric', '-east-asian'],
-
- // CSS Masking Module Level 1: http://www.w3.org/TR/css-masking/
- 'mask': ['-image', '-mode', '-position', '-size', '-repeat', '-origin', '-clip'],
- 'mask-border': ['-source', '-slice', '-width', '-outset', '-repeat', '-mode'],
-
- // CSS Multi-column Layout Module: http://www.w3.org/TR/css3-multicol/
- 'columns': ['column-width', 'column-count'],
- 'column-rule': ['-width', '-style', '-color'],
-
- // CSS Speech Module: http://www.w3.org/TR/css3-speech/
- 'cue': ['-before', '-after'],
- 'pause': ['-before', '-after'],
- 'rest': ['-before', '-after'],
-
- // CSS Text Decoration Module Level 3: http://www.w3.org/TR/css-text-decor-3/
- 'text-decoration': ['-line', '-style', '-color'],
- 'text-emphasis': ['-style', '-color'],
-
- // CSS Animations (WD): http://www.w3.org/TR/css3-animations
- 'animation': ['-name', '-duration', '-timing-function', '-delay', '-iteration-count', '-direction', '-fill-mode', '-play-state'],
-
- // CSS Transitions (WD): http://www.w3.org/TR/css3-transitions/
- 'transition': ['-property', '-duration', '-timing-function', '-delay'],
-
- // CSS Flexible Box Layout Module Level 1 (WD): http://www.w3.org/TR/css3-flexbox/
- 'flex': ['-grow', '-shrink', '-basis'],
-
- // CSS Grid: https://drafts.csswg.org/css-grid/#grid-shorthand
- 'grid': ['-template', '-auto-flow', '-auto-rows','-auto-columns'],
- 'grid-template': ['-rows', '-columns', '-areas'],
-
- // Others:
- 'overflow': ['-x','-y','-style'], // https://drafts.csswg.org/css-overflow-3/
-
- };
-
- var expandCache = Object.create(null);
- var unexpandCache = Object.create(null);
-
- /**
- * Expand a shorthand property into an array of longhand properties which are set by it
- * @param {string} property CSS property name
- * @return {array} List of longhand properties, or an empty array if it's not a shorthand
- */
- this.expand = function (property) {
-
- var result = expandCache[property];
- if(result) { return result; }
-
- var prefixData = property.match(/^(-[a-zA-Z]+-)?(.*)$/);
- var prefix = prefixData[1]||'', prefixFreeProperty = prefixData[2]||'';
- if (!shorthands.hasOwnProperty(prefixFreeProperty)) {
- return [];
- }
-
- result = [];
- shorthands[prefixFreeProperty].forEach((p) => {
- var longhand = p[0] === '-' ? property + p : prefix + p;
- result.push(longhand);
- result.push.apply(result, this.expand(longhand));
- });
-
- return expandCache[property] = result;
-
- };
-
- /**
- * Expand a longhand property into an array of shorthand which may set the value
- * @param {string} property CSS property name
- * @return {array} List of shorthand properties, or the original property if it's not a shorthand
- */
- this.unexpand = function unexpand(property) {
-
- var result = unexpandCache[property];
- if(result) { return result; }
-
- var prefixData = property.match(/^(-[a-zA-Z]+-)?(.*)$/);
- var prefix = prefixData[1]||'', prefixFreeProperty = prefixData[2]||'';
-
- result = [];
- for(var sh = 0; sh <= shorthands.length; sh++) {
- var shorthand = shorthands[sh];
- if(this.expand(shorthand).indexOf(prefixFreeProperty) >= 0) {
- result.push(prefix+shorthand);
- result.push.apply(result,this.unexpand(prefix+shorthand));
- }
- }
-
- return unexpandCache[property] = result;
-
- }
-
-}.call(window.CSSShorthands={});
-
-
-// http://blueprintcss.org/tests/parts/grid.html
-var hasBluePrintUsage = function() {
-
- if(!document.querySelector(".container")) {
- return false;
- }
-
- for(var i = 24+1; --i;) {
- if(document.querySelector(".container > .span-"+i)) {
- return true;
- }
- }
- return false;
-
-}
-//
-// report how many times the classes in the following arrays have been used in the dom
-// (bootstrap stats)
-//
-
-var detectedBootstrapGridUsages = function(domClasses) {
- var _ = window.CSSUsageLodash;
- var reduce = _.reduce.bind(_);
- var trackedClasses = [];
-
- var sizes = ['xs','sm','md','lg'];
- for(var i = sizes.length; i--;) { var size = sizes[i];
- for(var j = 12+1; --j;) {
- trackedClasses.push('col-'+size+'-'+j);
- for(var k = 12+1; --k;) {
- trackedClasses.push('col-'+size+'-'+j+'-offset-'+k);
- trackedClasses.push('col-'+size+'-'+j+'-push-'+k);
- trackedClasses.push('col-'+size+'-'+j+'-pull-'+k);
- }
- }
- }
-
- return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
-
-};
-
-var detectedBootstrapFormUsages = function(domClasses) {
- var _ = window.CSSUsageLodash;
- var reduce = _.reduce.bind(_);
- var trackedClasses = [
- 'form-group', 'form-group-xs', 'form-group-sm', 'form-group-md', 'form-group-lg',
- 'form-control', 'form-horizontal', 'form-inline',
- 'btn','btn-primary','btn-secondary','btn-success','btn-warning','btn-danger','btn-error'
- ];
-
- return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
-
-};
-
-var detectedBootstrapAlertUsages = function(domClasses) {
- var _ = window.CSSUsageLodash;
- var reduce = _.reduce.bind(_);
- var trackedClasses = [
- 'alert','alert-primary','alert-secondary','alert-success','alert-warning','alert-danger','alert-error'
- ];
-
- return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
-
-};
-
-var detectedBootstrapFloatUsages = function(domClasses) {
- var _ = window.CSSUsageLodash;
- var reduce = _.reduce.bind(_);
- var trackedClasses = [
- 'pull-left','pull-right',
- ];
-
- return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
-
-};
-// https://github.com/Dogfalo/materialize/blob/master/sass/components/_grid.scss
-var hasDogfaloMaterializeUsage = function() {
-
- if(!document.querySelector(".container > .row > .col")) {
- return false;
- }
-
- for(var i = 12+1; --i;) {
- var classesToLookUp = ['s','m','l'];
- for(var d = 0; d < classesToLookUp.length; d++) {
- var s = classesToLookUp[d];
- if(document.querySelector(".container > .row > .col."+s+""+i)) {
- return true;
- }
- }
- }
- return false;
-
-}
-// http://www.gumbyframework.com/docs/grid/#!/basic-grid
-var hasGrumbyUsage = function() {
-
- if(!document.querySelector(".row .columns")) {
- return false;
- }
-
- var classesToLookUp = ["one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"];
- for(var cl = 0; cl < classesToLookUp.length; cl++ ) {
- var fraction = classesToLookUp[cl];
- if(document.querySelector(".row > .columns."+fraction)) {
- return true;
- }
- }
- return false;
-
-}
-// https://raw.githubusercontent.com/csswizardry/inuit.css/master/generic/_widths.scss
-var hasInuitUsage = function() {
-
- if(!document.querySelector(".grid .grid__item")) {
- return false;
- }
-
- var classesToLookUp = ["one-whole","one-half","one-third","two-thirds","one-quarter","two-quarters","one-half","three-quarters","one-fifth","two-fifths","three-fifths","four-fifths","one-sixth","two-sixths","one-third","three-sixths","one-half","four-sixths","two-thirds","five-sixths","one-eighth","two-eighths","one-quarter","three-eighths","four-eighths","one-half","five-eighths","six-eighths","three-quarters","seven-eighths","one-tenth","two-tenths","one-fifth","three-tenths","four-tenths","two-fifths","five-tenths","one-half","six-tenths","three-fifths","seven-tenths","eight-tenths","four-fifths","nine-tenths","one-twelfth","two-twelfths","one-sixth","three-twelfths","one-quarter","four-twelfths","one-third","five-twelfths","six-twelfths","one-half","seven-twelfths","eight-twelfths","two-thirds","nine-twelfths","three-quarters","ten-twelfths","five-sixths","eleven-twelfths"];
-
- for(var cu = 0; cu < classesToLookUp.length; cu++ ) {
- var fraction = classesToLookUp[cu];
+/*
+ JSAPI RECIPE: WebGL Function Calls
+ -------------------------------------------------------------
+ Author: adyoun
+ Description: Counting the number of times each WebGL function is called
+*/
- var subClassesToLookUp = ["","palm-","lap-","portable-","desk-"];
- for(var sc = 0; sc < subClassesToLookUp.length; sc++) {
- var ns = subClassesToLookUp[sc];
- if(document.querySelector(".grid > .grid__item."+ns+fraction)) {
- return true;
- }
+window.functionList =
+[
+ ["activeTexture", 0],
+ ["attachShader", 0],
+ ["bindAttribLocation", 0],
+ ["bindBuffer", 0],
+ ["bindFramebuffer", 0],
+ ["bindRenderbuffer", 0],
+ ["bindTexture", 0],
+ ["blendColor", 0],
+ ["blendEquation", 0],
+ ["blendEquationSeparate", 0],
+ ["blendFunc", 0],
+ ["blendFuncSeparate", 0],
+ ["bufferData", 0],
+ ["bufferSubData", 0],
+ ["checkFramebufferStatus", 0],
+ ["clear", 0],
+ ["clearColor", 0],
+ ["clearDepth", 0],
+ ["clearStencil", 0],
+ ["colorMask", 0],
+ ["compileShader", 0],
+ ["copyTexImage2D", 0],
+ ["copyTexSubImage2D", 0],
+ ["createBuffer", 0],
+ ["createByteArray", 0],
+ ["createFloatArray", 0],
+ ["createFramebuffer", 0],
+ ["createIntArray", 0],
+ ["createProgram", 0],
+ ["createRenderbuffer", 0],
+ ["createShader", 0],
+ ["createShortArray", 0],
+ ["createTexture", 0],
+ ["createUnsignedByteArray", 0],
+ ["createUnsignedIntArray", 0],
+ ["createUnsignedShortArray", 0],
+ ["cullFace", 0],
+ ["deleteBuffer", 0],
+ ["deleteFramebuffer", 0],
+ ["deleteProgram", 0],
+ ["deleteRenderbuffer", 0],
+ ["deleteShader", 0],
+ ["deleteTexture", 0],
+ ["depthFunc", 0],
+ ["depthMask", 0],
+ ["depthRange", 0],
+ ["detachShader", 0],
+ ["disable", 0],
+ ["disableVertexAttribArray", 0],
+ ["drawArrays", 0],
+ ["drawElements", 0],
+ ["enable", 0],
+ ["enableVertexAttribArray", 0],
+ ["finish", 0],
+ ["flush", 0],
+ ["framebufferRenderbuffer", 0],
+ ["framebufferTexture2D", 0],
+ ["frontFace", 0],
+ ["generateMipmap", 0],
+ ["getActiveAttrib", 0],
+ ["getActiveUniform", 0],
+ ["getAttachedShaders", 0],
+ ["getAttribLocation", 0],
+ ["getBufferParameter", 0],
+ ["getError", 0],
+ ["getFramebufferAttachmentParameter", 0],
+ ["getParameter", 0],
+ ["getProgramInfoLog", 0],
+ ["getProgramParameter", 0],
+ ["getRenderbufferParameter", 0],
+ ["getShaderInfoLog", 0],
+ ["getShaderParameter", 0],
+ ["getShaderSource", 0],
+ ["getTexParameter", 0],
+ ["getUniform", 0],
+ ["getUniformLocation", 0],
+ ["getVertexAttrib", 0],
+ ["isBuffer", 0],
+ ["isBuffergetParameter", 0],
+ ["isFramebuffer", 0],
+ ["isProgram", 0],
+ ["isRenderbuffer", 0],
+ ["isShader", 0],
+ ["isTexture", 0],
+ ["lineWidth", 0],
+ ["linkProgram", 0],
+ ["pixelStorei", 0],
+ ["polygonOffset", 0],
+ ["readPixels", 0],
+ ["renderbufferStorage", 0],
+ ["sampleCoverage", 0],
+ ["scissor", 0],
+ ["shaderSource", 0],
+ ["stencilFunc", 0],
+ ["stencilFuncSeparate", 0],
+ ["stencilMask", 0],
+ ["stencilMaskSeparate", 0],
+ ["stencilOp", 0],
+ ["stencilOpSeparate", 0],
+ ["texImage2D", 0],
+ ["texParameterf", 0],
+ ["texParameteri", 0],
+ ["texSubImage2D", 0],
+ ["uniform1f", 0],
+ ["uniform1fv", 0],
+ ["uniform1i", 0],
+ ["uniform1iv", 0],
+ ["uniform2f", 0],
+ ["uniform2fv", 0],
+ ["uniform2i", 0],
+ ["uniform2iv", 0],
+ ["uniform3f", 0],
+ ["uniform3fv", 0],
+ ["uniform3i", 0],
+ ["uniform3iv", 0],
+ ["uniform4f", 0],
+ ["uniform4fv", 0],
+ ["uniform4i", 0],
+ ["uniform4iv", 0],
+ ["uniformMatrix2fv", 0],
+ ["uniformMatrix3fv", 0],
+ ["uniformMatrix4fv", 0],
+ ["useProgram", 0],
+ ["validateProgram", 0],
+ ["vertexAttrib1f", 0],
+ ["vertexAttrib1fv", 0],
+ ["vertexAttrib2f", 0],
+ ["vertexAttrib2fv", 0],
+ ["vertexAttrib3f", 0],
+ ["vertexAttrib3fv", 0],
+ ["vertexAttrib4f", 0],
+ ["vertexAttrib4fv", 0],
+ ["vertexAttribPointer", 0],
+ ["viewport", 0]
+];
+
+var functionMap = new Map();
+
+for (var i = 0; i < window.functionList.length; i++)
+{
+
+ WebGLRenderingContext.prototype["_" + window.functionList[i][0]] = WebGLRenderingContext.prototype[window.functionList[i][0]];
+
+ WebGLRenderingContext.prototype[window.functionList[i][0]] = function() {
+ var index = functionMap.get(arguments.callee);
+ window.functionList[index][1] = window.functionList[index][1] + 1;
+
+ switch(arguments.length)
+ {
+ case 0:
+ return this["_" + window.functionList[index][0]]();
+ case 1:
+ return this["_" + window.functionList[index][0]](arguments[0]);
+ case 2:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1]);
+ case 3:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2]);
+ case 4:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3]);
+ case 5:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
+ case 6:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
+ case 7:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]);
+ case 8:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]);
+ case 9:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8]);
}
}
- return false;
-
-}
-var getLonelyGatesUsage = function (cssLonelyClassGates, domClasses, domIds, cssLonelyIdGates) {
-
- var _ = window.CSSUsageLodash;
- if((cssLonelyClassGates || domClasses || domIds || cssLonelyIdGates) == undefined) return;
-
- // get arrays of the .class gates used ({"hover":5} => ["hover"]), filter irrelevant entries
- var cssUniqueLonelyClassGatesArray = Object.keys(cssLonelyClassGates);
- var cssUniqueLonelyClassGatesUsedArray = _(cssUniqueLonelyClassGatesArray).filter((c) => domClasses[c]).value();
- var cssUniqueLonelyClassGatesUsedWorthArray = _(cssUniqueLonelyClassGatesUsedArray).filter((c)=>(cssLonelyClassGates[c]>9)).value();
- if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(cssLonelyClassGates);
- if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(cssUniqueLonelyClassGatesUsedWorthArray);
-
- // get arrays of the #id gates used ({"hover":5} => ["hover"]), filter irrelevant entries
- var cssUniqueLonelyIdGatesArray = Object.keys(cssLonelyIdGates);
- var cssUniqueLonelyIdGatesUsedArray = _(cssUniqueLonelyIdGatesArray).filter((c) => domIds[c]).value();
- var cssUniqueLonelyIdGatesUsedWorthArray = _(cssUniqueLonelyIdGatesUsedArray).filter((c)=>(cssLonelyIdGates[c]>9)).value();
- if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(cssLonelyIdGates);
- if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(cssUniqueLonelyIdGatesUsedWorthArray);
+ functionMap.set(WebGLRenderingContext.prototype[window.functionList[i][0]], i);
}
-//
-// report how many times the classes in the following arrays have been used as css gate
-// (modernizer stats)
-//
-// https://modernizr.com/docs#features
-var detectedModernizerUsages = function(cssLonelyClassGates) {
+window.getContextCounter = 0;
- if((cssLonelyClassGates) == undefined) return;
-
- var ModernizerUsages = {count:0,values:{/* "js":1, "no-js":2 */}};
- var trackedClasses = ["js","ambientlight","applicationcache","audio","batteryapi","blobconstructor","canvas","canvastext","contenteditable","contextmenu","cookies","cors","cryptography","customprotocolhandler","customevent","dart","dataview","emoji","eventlistener","exiforientation","flash","fullscreen","gamepads","geolocation","hashchange","hiddenscroll","history","htmlimports","ie8compat","indexeddb","indexeddbblob","input","search","inputtypes","intl","json","olreversed","mathml","notification","pagevisibility","performance","pointerevents","pointerlock","postmessage","proximity","queryselector","quotamanagement","requestanimationframe","serviceworker","svg","templatestrings","touchevents","typedarrays","unicoderange","unicode","userdata","vibrate","video","vml","webintents","animation","webgl","websockets","xdomainrequest","adownload","audioloop","audiopreload","webaudio","lowbattery","canvasblending","todataurljpeg,todataurlpng,todataurlwebp","canvaswinding","getrandomvalues","cssall","cssanimations","appearance","backdropfilter","backgroundblendmode","backgroundcliptext","bgpositionshorthand","bgpositionxy","bgrepeatspace,bgrepeatround","backgroundsize","bgsizecover","borderimage","borderradius","boxshadow","boxsizing","csscalc","checked","csschunit","csscolumns","cubicbezierrange","display-runin","displaytable","ellipsis","cssescape","cssexunit","cssfilters","flexbox","flexboxlegacy","flexboxtweener","flexwrap","fontface","generatedcontent","cssgradients","hsla","csshyphens,softhyphens,softhyphensfind","cssinvalid","lastchild","cssmask","mediaqueries","multiplebgs","nthchild","objectfit","opacity","overflowscrolling","csspointerevents","csspositionsticky","csspseudoanimations","csspseudotransitions","cssreflections","regions","cssremunit","cssresize","rgba","cssscrollbar","shapes","siblinggeneral","subpixelfont","supports","target","textalignlast","textshadow","csstransforms","csstransforms3d","preserve3d","csstransitions","userselect","cssvalid","cssvhunit","cssvmaxunit","cssvminunit","cssvwunit","willchange","wrapflow","classlist","createelementattrs,createelement-attrs","dataset","documentfragment","hidden","microdata","mutationobserver","bdi","datalistelem","details","outputelem","picture","progressbar,meter","ruby","template","time","texttrackapi,track","unknownelements","es5array","es5date","es5function","es5object","es5","strictmode","es5string","es5syntax","es5undefined","es6array","contains","generators","es6math","es6number","es6object","promises","es6string","devicemotion,deviceorientation","oninput","filereader","filesystem","capture","fileinput","directory","formattribute","localizednumber","placeholder","requestautocomplete","formvalidation","sandbox","seamless","srcdoc","apng","jpeg2000","jpegxr","sizes","srcset","webpalpha","webpanimation","webplossless,webp-lossless","webp","inputformaction","inputformenctype","inputformmethod","inputformtarget","beacon","lowbandwidth","eventsource","fetch","xhrresponsetypearraybuffer","xhrresponsetypeblob","xhrresponsetypedocument","xhrresponsetypejson","xhrresponsetypetext","xhrresponsetype","xhr2","scriptasync","scriptdefer","speechrecognition","speechsynthesis","localstorage","sessionstorage","websqldatabase","stylescoped","svgasimg","svgclippaths","svgfilters","svgforeignobject","inlinesvg","smil","textareamaxlength","bloburls","datauri","urlparser","videoautoplay","videoloop","videopreload","webglextensions","datachannel","getusermedia","peerconnection","websocketsbinary","atob-btoa","framed","matchmedia","blobworkers","dataworkers","sharedworkers","transferables","webworkers"];
- for(var tc = 0; tc < trackedClasses.length; tc++) {
- var c = trackedClasses[tc];
- countInstancesOfTheClass(c);
- countInstancesOfTheClass('no-'+c);
- }
- return ModernizerUsages;
-
- function countInstancesOfTheClass(c) {
- var count = cssLonelyClassGates[c]; if(!count) return;
- ModernizerUsages.count += count;
- ModernizerUsages.values[c]=count;
+HTMLCanvasElement.prototype._getContext = HTMLCanvasElement.prototype.getContext;
+HTMLCanvasElement.prototype.getContext = function (a) {
+ var lowerCase = a.toLowerCase();
+ if (lowerCase.includes("webgl"))
+ {
+ window.getContextCounter = window.getContextCounter + 1;
}
-
-}
-function getFwkUsage(results, cssLonelyClassGates, domClasses, domIds, cssLonelyIdGates, cssClasses) {
-
- // Modernizer
- getLonelyGatesUsage(cssLonelyClassGates, domClasses, domIds, cssLonelyIdGates);
- detectedModernizerUsages(cssLonelyClassGates);
- results.FwkModernizer = !!window.Modernizer;
- results.FwkModernizerDOMUsages = detectedModernizerUsages(domClasses);
- results.FwkModernizerCSSUsages = detectedModernizerUsages(cssLonelyClassGates);
-
- // Bootstrap
- results.FwkBootstrap = !!((window.jQuery||window.$) && (window.jQuery||window.$).fn && (window.jQuery||window.$).fn.modal)|0;
- results.FwkBootstrapGridUsage = detectedBootstrapGridUsages(domClasses);
- results.FwkBootstrapFormUsage = detectedBootstrapFormUsages(domClasses);
- results.FwkBootstrapFloatUsage = detectedBootstrapFloatUsages(domClasses);
- results.FwkBootstrapAlertUsage = detectedBootstrapAlertUsages(domClasses);
- results.FwkBootstrapGridRecognized = detectedBootstrapGridUsages(cssClasses);
- results.FwkBootstrapFormRecognized = detectedBootstrapFormUsages(cssClasses);
- results.FwkBootstrapFloatRecognized = detectedBootstrapFloatUsages(cssClasses);
- results.FwkBootstrapAlertRecognized = detectedBootstrapAlertUsages(cssClasses);
-
- // Grumby
- results.FwkGrumby = hasGrumbyUsage()|0;
-
- // Inuit
- results.FwkInuit = hasInuitUsage()|0;
-
- // Blueprint
- results.FwkBluePrint = hasBluePrintUsage()|0;
-
- // Dog Falo
- results.FwkDogfaloMaterialize = hasDogfaloMaterializeUsage()|0;
-
- return results;
-}
-//
-// report how many times the classes in the following arrays have been used in the dom
-// (general stats)
-//
-
-/** count how many times the usual clearfix classes are used */
-var detectedClearfixUsages = function(domClasses) {
-
- var _ = window.CSSUsageLodash;
- var reduce = _.reduce.bind(_);
-
- var trackedClasses = [
- 'clearfix','clear',
- ];
-
- return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
-
-};
-
-/** count how many times the usual hide/show classes are used */
-var detectedVisibilityUsages = function(domClasses) {
- var _ = window.CSSUsageLodash;
- var reduce = _.reduce.bind(_);
-
- var trackedClasses = [
- 'show', 'hide', 'visible', 'hidden',
- ];
-
- return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
-
+ return this._getContext(a);
};
-function getPatternUsage(results, domClasses, cssClasses) {
- results.PatClearfixUsage = detectedClearfixUsages(domClasses);
- results.PatVisibilityUsage = detectedVisibilityUsages(domClasses);
- results.PatClearfixRecognized = detectedClearfixUsages(cssClasses);
- results.PatVisibilityRecognized = detectedVisibilityUsages(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() { try {
-
- var _ = window.CSSUsageLodash;
- var map = _.map.bind(_);
- var mapInline = _.mapInline ? _.mapInline : map;
- var reduce = _.reduce.bind(_);
- var filter = _.filter.bind(_);
-
- var browserIsEdge = navigator.userAgent.indexOf('Edge')>=0;
- var browserIsFirefox = navigator.userAgent.indexOf('Firefox')>=0;
-
- //
- // Guards execution against invalid conditions
- //
- void function() {
-
- // Don't run in subframes for now
- if (top.location.href !== location.href) throw new Error("CSSUsage: the script doesn't run in frames for now");
-
- // Don't run if already ran
- if (window.CSSUsage) throw new Error("CSSUsage: second execution attempted; only one run can be executed; if you specified parameters, check the right ones were chosen");
-
- // Don't run if we don't have lodash
- if (!window.CSSUsageLodash) throw new Error("CSSUsage: missing CSSUsageLodash dependency");
-
- if (!window.HtmlUsage) throw new Error("APIUsage: missing HtmlUsage dependancy");
-
- // Do not allow buggy trim() to bother usage
- if((''+String.prototype.trim).indexOf("[native code]") == -1) {
- console.warn('Replaced custom trim function with something known to work. Might break website.');
- String.prototype.trim = function() {
- return this.replace(/^\s+|\s+$/g, '');
- }
- }
-
- }();
-
- //
- // Prepare our global namespace
- //
- void function() {
- if(window.debugCSSUsage) console.log("STAGE: Building up namespace");
- window.HtmlUsageResults = {
- // this will contain all of the HTML tags used on a page
- tags: {}, /*
- tags ~= [nodeName] */
-
- // this will contain all of the attributes used on an HTML tag
- // and their values if they are in the whitelist
- attributes: {} /*
- attributes ~= {
- name: , // The name of the attribute
- tag: , // The tag that the attr was used on
- value: // The value of the attr
- } */
- };
-
- window.RecipeResults = {};
- window.Recipes = {
- recipes: []
- };
-
- window.CSSUsage = {};
- window.CSSUsageResults = {
-
- // this will contain the usage stats of various at-rules and rules
- types: [ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, ], /*
- types ~= {
- "unknown":0, //0
- "style":0, //1
- "charset": 0, //2
- "import":0, //3
- "media":0, //4
- "font-face":0, //5
- "page":0, //6
- "keyframes":0, //7 This is the @keyframe at rule
- "keyframe":0, //8 This is the individual 0%, or from/to
- "reserved9":0, //9
- "namespace":0, //10
- "reserved11":0,//11
- "supports":0, //12
- "reserved13":0,//13
- "reserved14":0,//14
- "viewport":0, //15
- }*/
-
- // this will contain the usage stats of various css properties and values
- props: Object.create(null), /*
- props ~= {
- "background-color": {
- count: 10,
- values: {
- "": 9,
- "inherit": 1
- }
- }
- }*/
-
- // this will contains the various datapoints we measure on css selector usage
- usages: {"SuccessfulCrawls":1},
-
- // this will contain selectors and the properties they refer to
- rules: {"@stylerule":0,"@atrule":0,"@inline":0}, /*
- rules ~= {
- "#id:hover .class": {
- count: 10,
- props: {
- "background-color": 5,
- "color": 4,
- "opacity": 3,
- "transform": 3
- }
- }
- }*/
-
- atrules: {}/*
- atrules ~= {
- "@atrule:4": {
- count: 3,
- props: {
- "background-color": 1,
- "color": 4,
- "opacity": 3,
- "transform": 3
- },
- nested: {
- "h3": 1
- },
- conditions: {
- "screen": 1
- }
- }
- }*/
-
-
- }
- }();
-
- //
- // The StyleWalker API cover the extraction of style in the browser
- //
- void function() { "use strict";
-
- CSSUsage.StyleWalker = {
-
- // This array contains the list of functions being run on each CSSStyleDeclaration
- // [ function(style, selectorText, matchedElements, ruleType) { ... }, ... ]
- ruleAnalyzers: [],
-
- // This array contains the list of functions being run on each DOM element of the page
- // [ function(element) { ...} ]
- elementAnalyzers: [],
-
- recipesToRun: [],
- runRecipes: false,
-
- //
- walkOverCssStyles: walkOverCssStyles,
- walkOverDomElements: walkOverDomElements,
-
- // Those stats are being collected while walking over the css style rules
- amountOfInlineStyles: 0,
- amountOfSelectorsUnused: 0,
- amountOfSelectors: 0,
- }
-
- var hasWalkedDomElementsOnce = false;
- // holds @keyframes temporarily while we wait to know how much they are used
- var keyframes = Object.create(null);
-
- /**
- * For all stylesheets of the document,
- * walk through the stylerules and run analyzers
- */
- function walkOverCssStyles() {
- if(window.debugCSSUsage) console.log("STAGE: Walking over styles");
- var styleSheets = document.styleSheets;
-
- // Loop through StyeSheets
- for (var ssIndex = styleSheets.length; ssIndex--;) {
- var styleSheet = styleSheets[ssIndex];
- try {
- if(styleSheet.cssRules) {
- walkOverCssRules(styleSheet.cssRules, styleSheet);
- } else {
- console.warn("No content loaded for stylesheet: ", styleSheet.href||styleSheet);
- }
- }
- catch (e) {
- if(window.debugCSSUsage) console.log(e, e.stack);
- }
- }
-
- // Hack: rely on the results to find out which
- // animations actually run, and parse their keyframes
- var animations = (CSSUsageResults.props['animation-name']||{}).values||{};
- for(var animation in keyframes) {
- var keyframe = keyframes[animation];
- var matchCount = animations[animation]|0;
- var fakeElements = initArray(matchCount, (i)=>({tagName:'@keyframes '+animation+' ['+i+']'}));
- processRule(keyframe, fakeElements);
- }
-
- }
-
- /**
- * This is the css work horse, this will will loop over the
- * rules and then call the rule analyzers currently registered
- */
- function walkOverCssRules(/*CSSRuleList*/ cssRules, styleSheet, parentMatchedElements) {
- if(window.debugCSSUsage) console.log("STAGE: Walking over rules");
- for (var ruleIndex = cssRules.length; ruleIndex--;) {
-
- // Loop through the rules
- var rule = cssRules[ruleIndex];
-
- // Until we can correlate animation usage
- // to keyframes do not parse @keyframe rules
- if(rule.type == 7) {
- keyframes[rule.name] = rule;
- continue;
- }
-
- // Filter "@supports" which the current browser doesn't support
- if(rule.type == 12 && (!CSS.supports || !CSS.supports(rule.conditionText))) {
- continue;
- }
-
- // Other rules should be processed immediately
- processRule(rule,parentMatchedElements);
- }
- }
-
-
- /**
- * This function takes a css rule and:
- * [1] walk over its child rules if needed
- * [2] call rule analyzers for that rule if it has style data
- */
- function processRule(rule, parentMatchedElements) {
- // Increment the rule type's counter
- CSSUsageResults.types[rule.type|0]++;
-
- // Some CssRules have nested rules to walk through:
- if (rule.cssRules && rule.cssRules.length>0) {
-
- walkOverCssRules(rule.cssRules, rule.parentStyleSheet, parentMatchedElements);
-
- }
-
- // Some CssRules have style we can analyze
- if(rule.style) {
- // find what the rule applies to
- var selectorText;
- var matchedElements;
- if(rule.selectorText) {
- selectorText = CSSUsage.PropertyValuesAnalyzer.cleanSelectorText(rule.selectorText);
- try {
- if(parentMatchedElements) {
- matchedElements = [].slice.call(document.querySelectorAll(selectorText));
- matchedElements.parentMatchedElements = parentMatchedElements;
- } else {
- matchedElements = [].slice.call(document.querySelectorAll(selectorText));
- }
- } catch(ex) {
- matchedElements = [];
- console.warn(ex.stack||("Invalid selector: "+selectorText+" -- via "+rule.selectorText));
- }
- } else {
- selectorText = '@atrule:'+rule.type;
- if(parentMatchedElements) {
- matchedElements = parentMatchedElements;
- } else {
- matchedElements = [];
- }
- }
-
- // run an analysis on it
- runRuleAnalyzers(rule.style, selectorText, matchedElements, rule.type);
- }
-
- // run analysis on at rules to populate CSSUsageResults.atrules
- if(isRuleAnAtRule(rule)) {
- if(rule.conditionText) {
- processConditionalAtRules(rule);
- } else {
- processGeneralAtRules(rule);
- }
- }
- }
-
-
- /**
- * Checks whether an rule is an @atrule.
- */
- function isRuleAnAtRule(rule) {
- /**
- * @atrules types ~= {
- "charset": 0, //2
- "import":0, //3
- "media":0, //4
- "font-face":0, //5
- "page":0, //6
- "keyframes":0, //7 This is the @keyframe at rule
- "keyframe":0, //8 This is the individual 0%, or from/to
-
- "namespace":0, //10
- "supports":0, //12
- "viewport":0, //15
- */
- let type = rule.type;
- return (type >= 2 && type <= 8) || (type == 10) || (type == 12) || (type == 15);
- }
-
- /**
- * This process @atrules with conditional statements such as @supports.
- * [1] It will process any props and values used within the body of the rule.
- * [2] It will count the occurence of usage of nested atrules.
- * [3] It will process condition statements to conform to a standardized version.
- */
- function processConditionalAtRules(rule) {
- var selectorText = '@atrule:' + rule.type;
- var atrulesUsage = CSSUsageResults.atrules;
-
- if(!atrulesUsage[selectorText]) {
- atrulesUsage[selectorText] = Object.create(null);
- atrulesUsage[selectorText] = {"count": 1,
- "props": {},
- "conditions": {}}
- } else {
- var count = atrulesUsage[selectorText].count;
- atrulesUsage[selectorText].count = count + 1;
- }
-
- var selectedAtruleUsage = atrulesUsage[selectorText];
-
- if(rule.cssRules) {
- CSSUsage.PropertyValuesAnalyzer.anaylzeStyleOfRulePropCount(rule, selectedAtruleUsage);
- }
-
- processConditionText(rule.conditionText, selectedAtruleUsage.conditions);
- }
-
- /**
- * This processes the usage of conditions of conditional @atrules like @media.
- * Requires the condition of the rule to process and the current recorded usage
- * of the @atrule in question.
- */
- function processConditionText(conditionText, selectedAtruleConditionalUsage) {
- // replace numeric specific information from condition statements
- conditionText = CSSUsage.CSSValues.parseValues(conditionText);
-
- if(!selectedAtruleConditionalUsage[conditionText]) {
- selectedAtruleConditionalUsage[conditionText] = Object.create(null);
- selectedAtruleConditionalUsage[conditionText] = {"count": 1}
- } else {
- var count = selectedAtruleConditionalUsage[conditionText].count;
- selectedAtruleConditionalUsage[conditionText].count = count + 1;
- }
- }
-
- /**
- * This will process all other @atrules that don't have conditions or styles.
- * [1] It will process any props and values used within the body of the rule.
- * [2] It will count the occurence of usage of nested atrules.
- */
- function processGeneralAtRules(rule) {
- var selectorText = '@atrule:' + rule.type;
- var atrulesUsage = CSSUsageResults.atrules;
-
- if(!atrulesUsage[selectorText]) {
- atrulesUsage[selectorText] = Object.create(null);
- atrulesUsage[selectorText] = {"count": 1,
- "props": {}}
- } else {
- var count = atrulesUsage[selectorText].count;
- atrulesUsage[selectorText].count = count + 1;
- }
-
- // @keyframes rule type is 7
- if(rule.type == 7) {
- processKeyframeAtRules(rule);
- } else if(CSSUsageResults.rules[selectorText].props) {
- atrulesUsage[selectorText].props = CSSUsageResults.rules[selectorText].props;
- delete atrulesUsage[selectorText].props.values;
- }
- }
-
- /**
- * Processes on @keyframe to add the appropriate props from the frame and a counter of which
- * frames are used throughout the document.
- */
- function processKeyframeAtRules(rule) {
- var selectorText = '@atrule:' + rule.type;
- var atrulesUsageForSelector = CSSUsageResults.atrules[selectorText];
-
- if(!atrulesUsageForSelector["keyframes"]) {
- atrulesUsageForSelector["keyframes"] = Object.create(null);
- }
-
- /**
- * grab the props from the individual keyframe props that was already populated
- * under CSSUsageResults.rules. Note: @atrule:8 is the individual frames.
- * WARN: tightly coupled with previous processing of rules.
- */
- atrulesUsageForSelector.props = CSSUsageResults.rules["@atrule:8"].props;
- delete atrulesUsageForSelector.props.values;
-
- for(let index in rule.cssRules) {
- let keyframe = rule.cssRules[index];
- var atrulesUsageForKeyframeOfSelector = atrulesUsageForSelector.keyframes;
-
- if(!keyframe.keyText) {
- continue;
- }
-
- var frame = keyframe.keyText;
-
- // replace extra whitespaces
- frame = frame.replace(/\s/g, '');
-
- if(!atrulesUsageForKeyframeOfSelector[frame]) {
- atrulesUsageForKeyframeOfSelector[frame] = { "count" : 1 };
- } else {
- var keyframeCount = atrulesUsageForKeyframeOfSelector[frame].count;
- atrulesUsageForKeyframeOfSelector[frame].count = keyframeCount + 1;
- }
- }
- }
-
-
- /**
- * This is the dom work horse, this will will loop over the
- * dom elements and then call the element analyzers currently registered,
- * as well as rule analyzers for inline styles
- */
- function walkOverDomElements(obj, index) {
- if(window.debugCSSUsage) console.log("STAGE: Walking over DOM elements");
- var recipesToRun = CSSUsage.StyleWalker.recipesToRun;
- obj = obj || document.documentElement; index = index|0;
-
- // Loop through the elements
- var elements = [].slice.call(document.all,0);
- for(var i = 0; i < elements.length; i++) {
- var element=elements[i];
-
- // Analyze its style, if any
- if(!CSSUsage.StyleWalker.runRecipes) {
- // Analyze the element
- runElementAnalyzers(element, index);
-
- if (element.hasAttribute('style')) {
- // Inline styles count like a style rule with no selector but one matched element
- var ruleType = 1;
- var isInline = true;
- var selectorText = '@inline:'+element.tagName;
- var matchedElements = [element];
- runRuleAnalyzers(element.style, selectorText, matchedElements, ruleType, isInline);
- }
- } else { // We've already walked the DOM crawler and need to run the recipes
- for(var r = 0; r < recipesToRun.length ; r++) {
- var recipeToRun = recipesToRun[r];
- var results = RecipeResults[recipeToRun.name] || (RecipeResults[recipeToRun.name]={});
- recipeToRun(element, results, true);
- }
- }
- }
-
- }
-
- /**
- * Given a rule and its data, send it to all rule analyzers
- */
- function runRuleAnalyzers(style, selectorText, matchedElements, type, isInline) {
-
- // Keep track of the counters
- if(isInline) {
- CSSUsage.StyleWalker.amountOfInlineStyles++;
- } else {
- CSSUsage.StyleWalker.amountOfSelectors++;
- }
-
- // Run all rule analyzers
- for(var i = 0; i < CSSUsage.StyleWalker.ruleAnalyzers.length; i++) {
- var runAnalyzer = CSSUsage.StyleWalker.ruleAnalyzers[i];
- runAnalyzer(style, selectorText, matchedElements, type, isInline);
- }
-
- }
-
- /**
- * Given an element and its data, send it to all element analyzers
- */
- function runElementAnalyzers(element, index, depth) {
- for(var i = 0; i < CSSUsage.StyleWalker.elementAnalyzers.length; i++) {
- var runAnalyzer = CSSUsage.StyleWalker.elementAnalyzers[i];
- runAnalyzer(element, index, depth);
- }
- }
-
- /**
- * Creates an array of "length" elements, by calling initializer for each cell
- */
- function initArray(length, initializer) {
- var array = Array(length);
- for(var i = length; i--;) {
- array[i] = initializer(i);
- }
- return array;
- }
- }();
-
- //
- // helper to work with css values
- //
- void function() {
-
- CSSUsage.CSSValues = {
- createValueArray: createValueArray,
- parseValues: parseValues,
- normalizeValue: createValueArray
- };
-
- /**
- * This will take a string value and reduce it down
- * to only the aspects of the value we wish to keep
- */
- function parseValues(value,propertyName) {
-
- // Trim value on the edges
- value = value.trim();
-
- // Normalize letter-casing
- value = value.toLowerCase();
-
- // Map colors to a standard value (eg: white, blue, yellow)
- if (isKeywordColor(value)) { return ""; }
- value = value.replace(/[#][0-9a-fA-F]+/g, '#xxyyzz');
-
- // Escapce identifiers containing numbers
- var numbers = ['ZERO','ONE','TWO','THREE','FOUR','FIVE','SIX','SEVEN','EIGHT','NINE'];
- value = value.replace(
- /([_a-z][-_a-z]|[_a-df-z])[0-9]+[-_a-z0-9]*/g,
- s=>numbers.reduce(
- (m,nstr,nint)=>m.replace(RegExp(nint,'g'),nstr),
- s
- )
- );
-
- // Remove any digits eg: 55px -> px, 1.5 -> 0.0, 1 -> 0
- value = value.replace(/(?:[+]|[-]|)(?:(?:[0-9]+)(?:[.][0-9]+|)|(?:[.][0-9]+))(?:[e](?:[+]|[-]|)(?:[0-9]+))?(%|e[a-z]+|[a-df-z][a-z]*)/g, "$1");
- value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)(?:[.][0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))?/g, " ");
- value = value.replace(/(?:[+]|[-]|)(?:[.][0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))?/g, " ");
- value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))/g, " ");
- value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)/g, " ");
-
- // Unescapce identifiers containing numbers
- value = numbers.reduce(
- (m,nstr,nint)=>m.replace(RegExp(nstr,'g'),nint),
- value
- )
-
- // Remove quotes
- value = value.replace(/('|‘|’|")/g, "");
-
- //
- switch(propertyName) {
- case 'counter-increment':
- case 'counter-reset':
-
- // Anonymize the user identifier
- value = value.replace(/[-_a-zA-Z0-9]+/g,' ');
- break;
-
- case 'grid':
- case 'grid-template':
- case 'grid-template-rows':
- case 'grid-template-columns':
- case 'grid-template-areas':
-
- // Anonymize line names
- value = value.replace(/\[[-_a-zA-Z0-9 ]+\]/g,' ');
- break;
-
- case '--var':
-
- // Replace (...), {...} and [...]
- value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " ");
- value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " ");
- value = value.replace(/\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]*)\])*\])*\])*\])*\]/g, " ");
- value = value.replace(/\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]*)\])*\])*\])*\])*\]/g, " ");
- value = value.replace(/\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]*)\})*\})*\})*\})*\}/g, " ");
- value = value.replace(/\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]*)\})*\})*\})*\})*\}/g, " ");
- break;
-
- }
-
- return value.trim();
-
- }
-
- //-----------------------------------------------------------------------------
-
- /**
- * This will transform a value into an array of value identifiers
- */
- function createValueArray(value, propertyName) {
-
- // Trim value on the edges
- value = value.trim();
-
- // Normalize letter-casing
- value = value.toLowerCase();
-
- // Remove comments and !important
- value = value.replace(/([/][*](?:.|\r|\n)*[*][/]|[!]important.*)/g,'');
-
- // Do the right thing in function of the property
- switch(propertyName) {
- case 'font-family':
-
- // Remove various quotes
- if (value.indexOf("'") != -1 || value.indexOf("‘") != -1 || value.indexOf('"')) {
- value = value.replace(/('|‘|’|")/g, "");
- }
-
- // Divide at commas to separate different font names
- value = value.split(/\s*,\s*/g);
- return value;
-
- case '--var':
-
- // Replace strings by dummies
- value = value.replace(/"([^"\\]|\\[^"\\]|\\\\|\\")*"/g,' ')
- value = value.replace(/'([^'\\]|\\[^'\\]|\\\\|\\')*'/g,' ');
-
- // Replace url(...) functions by dummies
- value = value.replace(/([a-z]?)[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "$1()");
- value = value.replace(/([a-z]?)[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "$1()");
-
- // Remove group contents (...), {...} and [...]
- value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " ");
- value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " ");
- value = value.replace(/[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]*)[}])*[}])*[}])*[}])*[}]/g, " ");
- value = value.replace(/[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]*)[}])*[}])*[}])*[}])*[}]/g, " ");
- value = value.replace(/[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]*)[\]])*[\]])*[\]])*[\]])*[\]]/g, " ");
- value = value.replace(/[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]*)[\]])*[\]])*[\]])*[\]])*[\]]/g, " ");
-
- break;
-
- default:
-
- // Replace strings by dummies
- value = value.replace(/"([^"\\]|\\[^"\\]|\\\\|\\")*"/g,' ')
- .replace(/'([^'\\]|\\[^'\\]|\\\\|\\')*'/g,' ');
-
- // Replace url(...) functions by dummies
- if (value.indexOf("(") != -1) {
- value = value.replace(/([a-z]?)[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "$1() ");
- value = value.replace(/([a-z]?)[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "$1() ");
- }
-
- }
-
- // Collapse whitespace
- value = value.trim().replace(/\s+/g, " ");
-
- // Divide at commas and spaces to separate different values
- value = value.split(/\s*(?:,|[/])\s*|\s+/g);
-
- return value;
- }
-
- /**
- * So that we don't end up with a ton of color
- * values, this will determine if the color is a
- * keyword color value
- */
- function isKeywordColor(candidateColor) {
-
- // Keyword colors from the W3C specs
- var isColorKeyword = /^(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgrey|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|grey|green|greenyellow|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgreen|lightgray|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lighslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|navyblue|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)$/;
- return isColorKeyword.test(candidateColor);
-
- }
-
- }();
-
- //
- // computes various css stats (PropertyValuesAnalyzer)
- //
- void function() {
-
- CSSUsage.PropertyValuesAnalyzer = analyzeStyleOfRule;
- CSSUsage.PropertyValuesAnalyzer.cleanSelectorText = cleanSelectorText;
- CSSUsage.PropertyValuesAnalyzer.generalizedSelectorsOf = generalizedSelectorsOf;
- CSSUsage.PropertyValuesAnalyzer.finalize = finalize;
- CSSUsage.PropertyValuesAnalyzer.anaylzeStyleOfRulePropCount = anaylzeStyleOfRulePropCount;
-
- // We put a computed style in cache for filtering purposes
- var defaultStyle = getComputedStyle(document.createElement('div'));
- // As well as some basic lies
- var getBuggyValuesForThisBrowser = function() {
- var buggyValues = getBuggyValuesForThisBrowser.cache;
- if(buggyValues) { return buggyValues; }
- else { buggyValues = Object.create(null); }
-
- // Edge reports initial value instead of "initial", we have to be cautious
- if(browserIsEdge) {
-
- buggyValues['*'] = 1; // make 0 values automatic for longhand properties
-
- //buggyValues['list-style-position:outside'] = 0;
- buggyValues['list-style-image:none'] = 1;
- //buggyValues['outline-color:invert'] = 0;
- //buggyValues['outline-style:none'] = 0;
- //buggyValues['outline-width:medium'] = 0;
- //buggyValues['background-image:none'] = 0;
- //buggyValues['background-attachment:scroll'] = 0;
- //buggyValues['background-repeat:repeat'] = 0;
- //buggyValues['background-repeat-x:repeat'] = 0;
- //buggyValues['background-repeat-y:repeat'] = 0;
- //buggyValues['background-position-x:0%'] = 0;
- //buggyValues['background-position-y:0%'] = 0;
- //buggyValues['background-size:auto'] = 0;
- //buggyValues['background-origin:padding-box'] = 0;
- //buggyValues['background-clip:border-box'] = 0;
- //buggyValues['background-color:transparent'] = 0;
- buggyValues['border-top-color:currentcolor'] = 1;
- buggyValues['border-right-color:currentcolor'] = 1;
- buggyValues['border-bottom-color:currentcolor'] = 1;
- buggyValues['border-left-color:currentcolor'] = 1;
- //buggyValues['border-top-style:solid'] = 0;
- //buggyValues['border-right-style:solid'] = 0;
- //buggyValues['border-bottom-style:solid'] = 0;
- //buggyValues['border-left-style:solid'] = 0;
- buggyValues['border-top-width:medium'] = 1;
- buggyValues['border-right-width:medium'] = 1;
- buggyValues['border-bottom-width:medium'] = 1;
- buggyValues['border-left-width:medium'] = 1;
- buggyValues['border-image-source:none'] = 1;
- buggyValues['border-image-outset:0'] = 1;
- buggyValues['border-image-width:1'] = 1;
- buggyValues['border-image-repeat:repeat'] = 1;
- buggyValues['border-image-repeat-x:repeat'] = 1;
- buggyValues['border-image-repeat-y:repeat'] = 1;
- buggyValues['line-height:normal'] = 1;
- //buggyValues['font-size-adjust:none'] = 0;
- buggyValues['font-stretch:normal'] = 1;
-
- }
-
- // Firefox reports initial values instead of "initial", we have to be cautious
- if(browserIsFirefox) {
-
- buggyValues['*'] = 1; // make 0 values automatic for longhand properties
-
- }
-
- // Attempt to force to optimize the object somehow
- Object.create(buggyValues);
-
- return getBuggyValuesForThisBrowser.cache = buggyValues;
-
- };
- var valueExistsInRootProperty = (cssText,key,rootKey,value) => {
- value = value.trim().toLowerCase();
-
- // detect suspicious values
- var buggyValues = getBuggyValuesForThisBrowser();
-
- // apply common sense to the given value, per browser
- var buggyState = buggyValues[key+':'+value];
- if(buggyState === 1) { return false; }
- if(buggyState !== 0 && (!buggyValues['*'] || CSSShorthands.unexpand(key).length == 0)) { return true; }
-
- // root properties are unlikely to lie
- if(key==rootKey) return false;
-
- // ask the browser is the best we can do right now
- var values = value.split(/\s+|\s*,\s*/g);
- var validValues = ' ';
- var validValuesExtractor = new RegExp(' '+rootKey+'(?:[-][-_a-zA-Z0-9]+)?[:]([^;]*)','gi');
- var match; while(match = validValuesExtractor.exec(cssText)) {
- validValues += match[1] + ' ';
- }
- for(var i = 0; i < values.length; i++) {
- var value = values[i];
- if(validValues.indexOf(' '+value+' ')==-1) return false;
- }
- return true;
-
- };
-
- /** This will loop over the styles declarations */
- function analyzeStyleOfRule(style, selectorText, matchedElements, type, isInline) { isInline=!!isInline;
-
- // We want to filter rules that are not actually used
- var count = matchedElements.length;
- var selector = selectorText;
- var selectorCat = {'1:true':'@inline','1:false':'@stylerule'}[''+type+':'+isInline]||'@atrule';
-
- // Keep track of unused rules
- var isRuleUnused = (count == 0);
- if(isRuleUnused) {
- CSSUsage.StyleWalker.amountOfSelectorsUnused++;
- }
-
- // We need a generalized selector to collect some stats
- var generalizedSelectors = (
- (selectorCat=='@stylerule')
- ? [selectorCat].concat(generalizedSelectorsOf(selector))
- : [selectorCat, selector]
- );
-
- // Get the datastores of the generalized selectors
- var generalizedSelectorsData = map(generalizedSelectors, (generalizedSelector) => (
- CSSUsageResults.rules[generalizedSelector] || (CSSUsageResults.rules[generalizedSelector] = {count:0,props:Object.create(null)})
- ));
-
- // Increment the occurence counter of found generalized selectors
- for(var i = 0; i < generalizedSelectorsData.length; i++) {
- var generalizedSelectorData = generalizedSelectorsData[i];
- generalizedSelectorData.count++
- }
-
- // avoid most common browser lies
- var cssText = ' ' + style.cssText.toLowerCase();
- if(browserIsEdge) {
- cssText = cssText.replace(/border: medium; border-image: none;/,'border: none;');
- cssText = cssText.replace(/ border-image: none;/,' ');
- }
-
- // For each property declaration in this rule, we collect some stats
- for (var i = style.length; i--;) {
-
- var key = style[i], rootKeyIndex=key.indexOf('-'), rootKey = rootKeyIndex==-1 ? key : key.substr(0,rootKeyIndex);
- var normalizedKey = rootKeyIndex==0&&key.indexOf('-',1)==1 ? '--var' : key;
- var styleValue = style.getPropertyValue(key);
-
- // Only keep styles that were declared by the author
- // We need to make sure we're only checking string props
- var isValueInvalid = typeof styleValue !== 'string' && styleValue != "" && styleValue != undefined;
- if (isValueInvalid) {
- continue;
- }
-
- var isPropertyUndefined = (cssText.indexOf(' '+key+':') == -1) && (styleValue=='initial' || !valueExistsInRootProperty(cssText, key, rootKey, styleValue));
- if (isPropertyUndefined) {
- continue;
- }
-
- // divide the value into simplified components
- var specifiedValuesArray = CSSUsage.CSSValues.createValueArray(styleValue,normalizedKey);
- var values = new Array();
- for(var j = specifiedValuesArray.length; j--;) {
- values.push(CSSUsage.CSSValues.parseValues(specifiedValuesArray[j],normalizedKey));
- }
-
- // log the property usage per selector
- for(var gs = 0; gs < generalizedSelectorsData.length; gs++) {
- var generalizedSelectorData = generalizedSelectorsData[gs];
- // get the datastore for current property
- var propStats = generalizedSelectorData.props[normalizedKey] || (generalizedSelectorData.props[normalizedKey] = {count:0,values:Object.create(null)});
-
- // we saw the property one time
- propStats.count++;
-
- // we also saw a bunch of values
- for(var v = 0; v < values.length; v++) {
- var value = values[v];
- // increment the counts for those by one, too
- if(value.length>0) {
- propStats.values[value] = (propStats.values[value]|0) + 1
- }
-
- }
-
- }
-
- // if we may increment some counts due to this declaration
- if(count > 0) {
-
- // instanciate or fetch the property metadata
- var propObject = CSSUsageResults.props[normalizedKey];
- if (!propObject) {
- propObject = CSSUsageResults.props[normalizedKey] = {
- count: 0,
- values: Object.create(null)
- };
- }
-
- // update the occurence counts of the property and value
- for(var e = 0; e < matchedElements.length; e++) {
- var element = matchedElements[e];
-
- // check what the elements already contributed for this property
- var cssUsageMeta = element.CSSUsage || (element.CSSUsage=Object.create(null));
- var knownValues = cssUsageMeta[normalizedKey] || (cssUsageMeta[normalizedKey] = []);
-
- // For recipes, at times we want to look at the specified values as well so hang
- // these on the element so we don't have to recompute them
- knownValues.valuesArray = knownValues.valuesArray || (knownValues.valuesArray = []);
-
- for(var sv = 0; sv < specifiedValuesArray.length; sv++) {
- var currentSV = specifiedValuesArray[sv];
- if(knownValues.valuesArray.indexOf(currentSV) == -1) {
- knownValues.valuesArray.push(currentSV)
- }
- }
-
- // increment the amount of affected elements which we didn't count yet
- if(knownValues.length == 0) { propObject.count += 1; }
-
- // add newly found values too
- for(var v = 0; v < values.length; v++) {
- var value = values[v];
- if(knownValues.indexOf(value) >= 0) { return; }
- propObject.values[value] = (propObject.values[value]|0) + 1;
- knownValues.push(value);
- }
-
- }
-
- }
-
- }
- }
-
- function anaylzeStyleOfRulePropCount(rule, selectedAtrulesUsage) {
- for(let index in rule.cssRules) {
- let ruleBody = rule.cssRules[index];
- let style = ruleBody.style;
-
- // guard for non css objects
- if(!style) {
- continue;
- }
-
- if(ruleBody.selector) {
- try {
- var selectorText = CssPropertyValuesAnalyzer.cleanSelectorText(ruleBody.selectorText);
- var matchedElements = [].slice.call(document.querySelectorAll(selectorText));
-
- if (matchedElements.length == 0) {
- continue;
- }
- } catch (ex) {
- console.warn(ex.stack||("Invalid selector: "+selectorText+" -- via "+ruleBody.selectorText));
- continue;
- }
- }
-
- let cssText = ' ' + style.cssText.toLowerCase();
-
- for (var i = style.length; i--;) {
- // processes out normalized prop name for style
- var key = style[i], rootKeyIndex=key.indexOf('-'), rootKey = rootKeyIndex==-1 ? key : key.substr(0,rootKeyIndex);
- var normalizedKey = rootKeyIndex==0&&key.indexOf('-',1)==1 ? '--var' : key;
- var styleValue = style.getPropertyValue(key);
-
- // Only keep styles that were declared by the author
- // We need to make sure we're only checking string props
- var isValueInvalid = typeof styleValue !== 'string' && styleValue != "" && styleValue != undefined;
- if (isValueInvalid) {
- continue;
- }
-
- var isPropertyUndefined = (cssText.indexOf(' '+key+':') == -1) && (styleValue=='initial' || !valueExistsInRootProperty(cssText, key, rootKey, styleValue));
- if (isPropertyUndefined) {
- continue;
- }
-
- var propsForSelectedAtrule = selectedAtrulesUsage.props;
-
- if(!propsForSelectedAtrule[normalizedKey]) {
- propsForSelectedAtrule[normalizedKey] = Object.create(null);
- propsForSelectedAtrule[normalizedKey] = {"count": 1};
- } else {
- var propCount = propsForSelectedAtrule[normalizedKey].count;
- propsForSelectedAtrule[normalizedKey].count = propCount + 1;
- }
- }
- }
- }
-
- function finalize() {
-
- // anonymize identifiers used for animation-name
- function removeAnimationNames() {
-
- // anonymize identifiers used for animation-name globally
- if(CSSUsageResults.props["animation-name"]) {
- CSSUsageResults.props["animation-name"].values = {"":CSSUsageResults.props["animation-name"].count};
- }
-
- // anonymize identifiers used for animation-name per selector
- for(var selector in CSSUsageResults.rules) {
- var rule = CSSUsageResults.rules[selector];
- if(rule && rule.props && rule.props["animation-name"]) {
- rule.props["animation-name"].values = {"":rule.props["animation-name"].count};
- }
- }
-
- }
-
- removeAnimationNames();
- }
-
- //-------------------------------------------------------------------------
-
- /**
- * If you try to do querySelectorAll on pseudo selectors
- * it returns 0 because you are not actually doing the action the pseudo is stating those things,
- * but we will honor those declarations and we don't want them to be missed,
- * so we remove the pseudo selector from the selector text
- */
- function cleanSelectorText(text) {
- if(text.indexOf(':') == -1) {
- return text;
- } else {
- return text.replace(/([-_a-zA-Z0-9*\[\]]?):(?:hover|active|focus|before|after|not\(:(hover|active|focus)\))|::(?:before|after)/gi, '>>$1<<').replace(/(^| |>|\+|~)>><><<\)/g,'(*)').replace(/>>([-_a-zA-Z0-9*\[\]]?)<a.submenu" => "#id.class:hover > a.class"
- */
- function generalizedSelectorsOf(value) {
-
- // Trim
- value = value.trim();
-
- // Collapse whitespace
- if (value) {
- value = value.replace(/\s+/g, " ");
- }
-
- // Remove (...)
- if (value.indexOf("(") != -1) {
- value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "");
- value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "");
- }
-
- // Simplify "..." and '...'
- value = value.replace(/"([^"\\]|\\[^"\\]|\\\\|\\")*"/g,'""')
- value = value.replace(/'([^'\\]|\\[^'\\]|\\\\|\\')*'/g,"''");
-
-
- // Simplify [att]
- if (value.indexOf("[") != -1) {
- value = value.replace(/\[[^=\[\]]+="([^"\\]|\\[^"\\]|\\\\|\\")*"\]/g, "[a]");
- value = value.replace(/\[[^=\[\]]+='([^'\\]|\\[^'\\]|\\\\|\\')*'\]/g, "[a]");
- value = value.replace(/\[[^\[\]]+\]/g, "[a]");
- }
-
- // Simplify .class
- if (value.indexOf(".") != -1) {
- value = value.replace(/[.][-_a-zA-Z][-_a-zA-Z0-9]*/g, ".c");
- }
-
- // Simplify #id
- if (value.indexOf("#") != -1) {
- value = value.replace(/[#][-_a-zA-Z][-_a-zA-Z0-9]*/g, "#i");
- }
-
- // Normalize combinators
- value = value.replace(/[ ]*([>|+|~])[ ]*/g,' $1 ');
-
- // Trim whitespace
- value = value.trim();
-
- // Remove unnecessary * to match Chrome
- value = value.replace(/[*]([#.\x5B:])/g,'$1');
-
- // Now we can sort components so that all browsers give results similar to Chrome
- value = sortSelectorComponents(value)
-
- // Split multiple selectors
- value = value.split(/\s*,\s*/g);
-
- return value;
-
- }
-
- var ID_REGEXP = "[#]i"; // #id
- var CLASS_REGEXP = "[.]c"; // .class
- var ATTR_REGEXP = "\\[a\\]"; // [att]
- var PSEUDO_REGEXP = "[:][:]?[-_a-zA-Z][-_a-zA-Z0-9]*"; // :pseudo
- var SORT_REGEXPS = [
-
- // #id first
- new RegExp("("+CLASS_REGEXP+")("+ID_REGEXP+")",'g'),
- new RegExp("("+ATTR_REGEXP+")("+ID_REGEXP+")",'g'),
- new RegExp("("+PSEUDO_REGEXP+")("+ID_REGEXP+")",'g'),
-
- // .class second
- new RegExp("("+ATTR_REGEXP+")("+CLASS_REGEXP+")",'g'),
- new RegExp("("+PSEUDO_REGEXP+")("+CLASS_REGEXP+")",'g'),
-
- // [attr] third
- new RegExp("("+PSEUDO_REGEXP+")("+ATTR_REGEXP+")",'g'),
-
- // :pseudo last
-
- ];
- function sortSelectorComponents(value) {
-
- var oldValue; do { // Yeah this is a very inefficient bubble sort. I know.
-
- oldValue = value;
- for(var i = 0; i < SORT_REGEXPS.length; i++) {
- var wrongPair = SORT_REGEXPS[i];
- value = value.replace(wrongPair,'$2$1');
- }
-
- } while(oldValue != value); return value;
-
- }
-
- }();
-
- //
- // extracts valuable informations about selectors in use
- //
- void function() {
-
- //
- // To understand framework and general css usage, we collect stats about classes, ids and pseudos.
- // Those objects have the following shape:
- // {"hover":5,"active":1,"focus":2}
- //
- var cssPseudos = Object.create(null); // collect stats about which pseudo-classes and pseudo-elements are used in the css
- var domClasses = Object.create(null); // collect stats about which css classes are found in the <... class> attributes of the dom
- var cssClasses = Object.create(null); // collect stats about which css classes are used in the css
- var domIds = Object.create(null); // collect stats about which ids are found in the <... id> attributes of the dom
- var cssIds = Object.create(null); // collect stats about which ids are used in the css
-
- //
- // To understand Modernizer usage, we need to know how often some classes are used at the front of a selector
- // While we're at it, the code also collect the state for ids
- //
- var cssLonelyIdGates = Object.create(null); // .class something-else ==> {"class":1}
- var cssLonelyClassGates = Object.create(null); // #id something-else ==> {"id":1}
- var cssLonelyClassGatesMatches = []; // .class something-else ==> [".class something-else"]
- var cssLonelyIdGatesMatches = []; // #id something-else ==> ["#id something-else"]
-
- //
- // These regular expressions catch patterns we want to track (see before)
- //
- var ID_REGEXP = /[#][-_a-zA-Z][-_a-zA-Z0-9]*/g; // #id
- var ID_REGEXP1 = /[#][-_a-zA-Z][-_a-zA-Z0-9]*/; // #id (only the first one)
- var CLASS_REGEXP = /[.][-_a-zA-Z][-_a-zA-Z0-9]*/g; // .class
- var CLASS_REGEXP1 = /[.][-_a-zA-Z][-_a-zA-Z0-9]*/; // .class (only the first one)
- var PSEUDO_REGEXP = /[:][-_a-zA-Z][-_a-zA-Z0-9]*/g; // :pseudo (only the )
- var GATEID_REGEXP = /^\s*[#][-_a-zA-Z][-_a-zA-Z0-9]*([.][-_a-zA-Z][-_a-zA-Z0-9]*|[:][-_a-zA-Z][-_a-zA-Z0-9]*)*\s+[^>+{, ][^{,]+$/; // #id ...
- var GATECLASS_REGEXP = /^\s*[.][-_a-zA-Z][-_a-zA-Z0-9]*([:][-_a-zA-Z][-_a-zA-Z0-9]*)*\s+[^>+{, ][^{,]+$/; // .class ...
-
- /**
- * From a css selector text and a set of counters,
- * increment the counters for the matches in the selector of the 'feature' regular expression
- */
- function extractFeature(feature, selector, counters) {
- var instances = selector.match(feature)||[];
- for(var i = 0; i < instances.length; i++) {
- var instance = instances[i];
- instance = instance.substr(1);
- counters[instance] = (counters[instance]|0) + 1;
- }
- }
-
- /**
- * This analyzer will collect over the selectors the stats defined before
- */
- CSSUsage.SelectorAnalyzer = function parseSelector(style, selectorsText) {
- if(typeof selectorsText != 'string') return;
-
- var selectors = selectorsText.split(',');
- for(var i = selectors.length; i--;) { var selector = selectors[i];
-
- // extract all features from the selectors
- extractFeature(ID_REGEXP, selector, cssIds);
- extractFeature(CLASS_REGEXP, selector, cssClasses);
- extractFeature(PSEUDO_REGEXP, selector, cssPseudos);
-
- // detect specific selector patterns we're interested in
- if(GATEID_REGEXP.test(selector)) {
- cssLonelyIdGatesMatches.push(selector);
- extractFeature(ID_REGEXP1, selector, cssLonelyIdGates);
- }
- if(GATECLASS_REGEXP.test(selector)) {
- cssLonelyClassGatesMatches.push(selector);
- extractFeature(CLASS_REGEXP1, selector, cssLonelyClassGates);
- }
- }
-
- }
-
- /**
- * This analyzer will collect over the dom elements the stats defined before
- */
- CSSUsage.DOMClassAnalyzer = function(element) {
-
- // collect classes used in the wild
- if(element.className) {
- var elementClasses = element.classList;
- for(var cl = 0; cl < elementClasses.length; cl++) {
- var c = elementClasses[cl];
- domClasses[c] = (domClasses[c]|0) + 1;
- }
- }
-
- // collect ids used in the wild
- if(element.id) {
- domIds[element.id] = (domIds[element.id]|0) + 1;
- }
-
- }
-
- /**
- * This function will be called when all stats have been collected
- * at which point we will agregate some of them in useful values like Bootstrap usages, etc...
- */
- CSSUsage.SelectorAnalyzer.finalize = function() {
-
- // get arrays of the classes/ids used ({"hover":5} => ["hover"]))
- var domClassesArray = Object.keys(domClasses);
- var cssClassesArray = Object.keys(cssClasses);
- var domIdsArray = Object.keys(domIds);
- var cssIdsArray = Object.keys(cssIds);
-
- var results = {
- // how many crawls are aggregated in this file (one of course in this case)
- SuccessfulCrawls: 1,
-
- // how many elements on the page (used to compute percentages for css.props)
- DOMElements: document.all.length,
-
- // how many selectors vs inline style, and other usage stats
- SelectorsFound: CSSUsage.StyleWalker.amountOfSelectors,
- InlineStylesFound: CSSUsage.StyleWalker.amountOfInlineStyles,
- SelectorsUnused: CSSUsage.StyleWalker.amountOfSelectorsUnused,
-
- // ids stats
- IdsUsed: domIdsArray.length,
- IdsRecognized: Object.keys(cssIds).length,
- IdsUsedRecognized: filter(domIdsArray, i => cssIds[i]).length,
-
- // classes stats
- ClassesUsed: domClassesArray.length,
- ClassesRecognized: Object.keys(cssClasses).length,
- ClassesUsedRecognized: filter(domClassesArray, c => cssClasses[c]).length,
- };
-
- results = getFwkUsage(results, cssLonelyClassGates, domClasses, domIds, cssLonelyIdGates, cssClasses);
- results = getPatternUsage(results, domClasses, cssClasses);
-
- CSSUsageResults.usages = results;
- deleteDuplicatedAtRules(); // TODO: issue #52
-
- if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(CSSUsageResults.usages);
- }
-
- /**
- * Removes duplicated at rules data that was generated under CSSUsageResults.rules
- * TODO: should not be using such a function, refer to issue #52
- */
- function deleteDuplicatedAtRules() {
- var cssUsageRules = CSSUsageResults.rules;
- var keys = Object.keys(cssUsageRules);
-
- for(let key of keys) {
- // only remove specific atrules
- if (key.includes("atrule:")) {
- delete cssUsageRules[key];
- }
- }
-
- delete CSSUsageResults.atrules["@atrule:8"]; // delete duplicated data from atrule:7, keyframe
- }
- }();
-
-} catch (ex) { /* do something maybe */ throw ex; } }();
-
-/*
- RECIPE: Edit controls on the web
- -------------------------------------------------------------
- Author: Grisha Lyukshin
- Description: Counts pages that have either input, textarea, or content editable elements
-*/
+void function () {
+ document.addEventListener('DOMContentLoaded', function () {
+ console.log("Logging");
+ var results = {};
+ var recipeName = "WebGL_Function_Counter";
+ results[recipeName] = results[recipeName] || { href: location.href, };
-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"))
+ for (var i = 0; i < window.functionList.length; i++)
{
- results["input"] = results["input"] || { count: 0 };
- results["input"].count++;
+ results[recipeName][window.functionList[i][0]] = results[recipeName][window.functionList[i][0]] || {count: window.functionList[i][1]};
}
- 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++;
- }
- }
- 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('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:
-// This is where we decide what to run, and when
-//
-void function() {
- var browserIsEdge = navigator.userAgent.indexOf('Edge')>=0;
- var browserIsFirefox = navigator.userAgent.indexOf('Firefox')>=0;
+ results[recipeName]["getContext"] = results[recipeName]["getContext"] || {count: window.getContextCounter};
- if(document.readyState !== 'complete') {
-
- // if the document is loading, run when it loads or in 10s, whichever is less
- window.addEventListener('load', onready);
- setTimeout(onready, 10000);
-
- } else {
-
- // if the document is ready, run now
- onready();
-
- }
+ appendResults(results);
- /**
- * This is the main entrypoint of our script
- */
- function onready() {
-
- // Uncomment if you want to set breakpoints when running in the console
- //debugger;
-
- // Prevent this code from running multiple times
- var firstTime = !onready.hasAlreadyRun; onready.hasAlreadyRun = true;
- if(!firstTime) { return; /* for now... */ }
-
- // Prevent this code from running when the page has no stylesheet (probably a redirect page)
- if(document.styleSheets.length == 0) { return; }
+ // Add it to the document dom
+ function appendResults(results) {
+ if (window.debugCSSUsage) console.log("Trying to append");
+ var output = document.createElement('script');
+ output.id = "css-usage-tsv-results";
+ output.textContent = JSON.stringify(results);
+ output.type = 'text/plain';
+ document.querySelector('head').appendChild(output);
+ var successfulAppend = checkAppend();
+ }
- // Check to see if you're on a Firefox failure page
- if(document.styleSheets.length == 1 && browserIsFirefox) {
- if(document.styleSheets[0].href !== null && document.styleSheets[0].href.indexOf('aboutNetError') != -1) {
- return;
+ function checkAppend() {
+ 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");
+ }
+ else {
+ if (window.debugCSSUsage) console.log("Element successfully found");
}
}
- // Keep track of duration
- var startTime = performance.now();
-
- // register tools
- CSSUsage.StyleWalker.ruleAnalyzers.push(CSSUsage.PropertyValuesAnalyzer);
- CSSUsage.StyleWalker.ruleAnalyzers.push(CSSUsage.SelectorAnalyzer);
- CSSUsage.StyleWalker.elementAnalyzers.push(CSSUsage.DOMClassAnalyzer);
- CSSUsage.StyleWalker.elementAnalyzers.push(HtmlUsage.GetNodeName);
-
- // perform analysis
- CSSUsage.StyleWalker.walkOverDomElements();
- CSSUsage.StyleWalker.walkOverCssStyles();
- CSSUsage.PropertyValuesAnalyzer.finalize();
- CSSUsage.SelectorAnalyzer.finalize();
-
- // Walk over the dom elements again for Recipes
- CSSUsage.StyleWalker.runRecipes = true;
- CSSUsage.StyleWalker.walkOverDomElements();
-
- // Update duration
- CSSUsageResults.duration = (performance.now() - startTime)|0;
-
- // DO SOMETHING WITH THE CSS OBJECT HERE
- window.debugCSSUsage = false;
- if(window.onCSSUsageResults) {
- window.onCSSUsageResults(CSSUsageResults);
- }
- }
-}();
\ No newline at end of file
+ });
+}();
diff --git a/cssUsage.src.js b/cssUsage.src.js
index 43d4d45..2ae689f 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 {
@@ -1782,216 +1782,232 @@ void function() { try {
} catch (ex) { /* do something maybe */ throw ex; } }();
/*
- RECIPE: z-index on static flex items
+ RECIPE: negative_background_position
-------------------------------------------------------------
- Author: Francois Remy
- Description: Get count of flex items who should create a stacking context but do not really
+ Author: ADYOUN
+ Description: Looking for cases where background-position has a negative value
*/
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;
+ window.CSSUsage.StyleWalker.recipesToRun.push( function negative_background_position( element, results) {
+ if (element.CSSUsage)
+ {
+ var properties = [ "background-position-x", "background-position-y" ];
+ var found = false;
+
+ for (var i = 0; i < properties.length; i++)
+ {
+ if (found)
+ {
+ break;
+ }
+
+ if (element.CSSUsage[properties[i]])
+ {
+ var bpValues = element.CSSUsage[properties[i]].valuesArray;
+ for (var j = 0; j < bpValues.length; j++)
+ {
+ if (bpValues[j][0] == '-')
+ {
+ var key = "negative_background_position";
+ results[key] = results[key] || { count: 0, };
+ results[key].count++;
+ found = true;
+ break;
+ }
+ }
+ }
+ }
}
+ 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;
- }
-
- }
+//
+// 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/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..8216abd
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2845 @@
+{
+ "name": "ms-edge-css-usage",
+ "version": "0.1.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "5.5.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "dev": true,
+ "requires": {
+ "co": "4.6.0",
+ "fast-deep-equal": "1.1.0",
+ "fast-json-stable-stringify": "2.0.0",
+ "json-schema-traverse": "0.3.1"
+ }
+ },
+ "align-text": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+ "dev": true,
+ "requires": {
+ "kind-of": "3.2.2",
+ "longest": "1.0.1",
+ "repeat-string": "1.6.1"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "1.0.3"
+ },
+ "dependencies": {
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ }
+ }
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+ "dev": true
+ },
+ "array-last": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/array-last/-/array-last-0.1.0.tgz",
+ "integrity": "sha1-2gRLXhUk+KCPmeQFU6VgoFBTfl8=",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
+ "dev": true
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true
+ },
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
+ "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=",
+ "dev": true
+ },
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "dev": true,
+ "requires": {
+ "chalk": "1.1.3",
+ "esutils": "2.0.2",
+ "js-tokens": "3.0.2"
+ }
+ },
+ "babel-core": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz",
+ "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "6.26.0",
+ "babel-generator": "6.26.1",
+ "babel-helpers": "6.24.1",
+ "babel-messages": "6.23.0",
+ "babel-register": "6.26.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "convert-source-map": "1.5.1",
+ "debug": "2.6.9",
+ "json5": "0.5.1",
+ "lodash": "4.17.5",
+ "minimatch": "3.0.4",
+ "path-is-absolute": "1.0.1",
+ "private": "0.1.8",
+ "slash": "1.0.0",
+ "source-map": "0.5.7"
+ }
+ },
+ "babel-generator": {
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
+ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+ "dev": true,
+ "requires": {
+ "babel-messages": "6.23.0",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "detect-indent": "4.0.0",
+ "jsesc": "1.3.0",
+ "lodash": "4.17.5",
+ "source-map": "0.5.7",
+ "trim-right": "1.0.1"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+ "dev": true
+ }
+ }
+ },
+ "babel-helper-call-delegate": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
+ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
+ "dev": true,
+ "requires": {
+ "babel-helper-hoist-variables": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-define-map": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
+ "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "lodash": "4.17.5"
+ }
+ },
+ "babel-helper-function-name": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
+ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
+ "dev": true,
+ "requires": {
+ "babel-helper-get-function-arity": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-get-function-arity": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
+ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-hoist-variables": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
+ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-optimise-call-expression": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
+ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helper-regex": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
+ "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "lodash": "4.17.5"
+ }
+ },
+ "babel-helper-replace-supers": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
+ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
+ "dev": true,
+ "requires": {
+ "babel-helper-optimise-call-expression": "6.24.1",
+ "babel-messages": "6.23.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-helpers": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
+ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-check-es2015-constants": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
+ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-arrow-functions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
+ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-block-scoped-functions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
+ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-block-scoping": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
+ "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "lodash": "4.17.5"
+ }
+ },
+ "babel-plugin-transform-es2015-classes": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
+ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
+ "dev": true,
+ "requires": {
+ "babel-helper-define-map": "6.26.0",
+ "babel-helper-function-name": "6.24.1",
+ "babel-helper-optimise-call-expression": "6.24.1",
+ "babel-helper-replace-supers": "6.24.1",
+ "babel-messages": "6.23.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-computed-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
+ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-destructuring": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
+ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-duplicate-keys": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
+ "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-for-of": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
+ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-function-name": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
+ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-literals": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
+ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-amd": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
+ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-es2015-modules-commonjs": "6.26.0",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-commonjs": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz",
+ "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-strict-mode": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-systemjs": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
+ "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
+ "dev": true,
+ "requires": {
+ "babel-helper-hoist-variables": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-umd": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
+ "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-es2015-modules-amd": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-object-super": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
+ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
+ "dev": true,
+ "requires": {
+ "babel-helper-replace-supers": "6.24.1",
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-parameters": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
+ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
+ "dev": true,
+ "requires": {
+ "babel-helper-call-delegate": "6.24.1",
+ "babel-helper-get-function-arity": "6.24.1",
+ "babel-runtime": "6.26.0",
+ "babel-template": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-shorthand-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
+ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-spread": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
+ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-sticky-regex": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
+ "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
+ "dev": true,
+ "requires": {
+ "babel-helper-regex": "6.26.0",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-template-literals": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
+ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-typeof-symbol": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
+ "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-unicode-regex": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
+ "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
+ "dev": true,
+ "requires": {
+ "babel-helper-regex": "6.26.0",
+ "babel-runtime": "6.26.0",
+ "regexpu-core": "2.0.0"
+ }
+ },
+ "babel-plugin-transform-regenerator": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
+ "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=",
+ "dev": true,
+ "requires": {
+ "regenerator-transform": "0.10.1"
+ }
+ },
+ "babel-plugin-transform-strict-mode": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
+ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0"
+ }
+ },
+ "babel-preset-es2015": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
+ "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-check-es2015-constants": "6.22.0",
+ "babel-plugin-transform-es2015-arrow-functions": "6.22.0",
+ "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0",
+ "babel-plugin-transform-es2015-block-scoping": "6.26.0",
+ "babel-plugin-transform-es2015-classes": "6.24.1",
+ "babel-plugin-transform-es2015-computed-properties": "6.24.1",
+ "babel-plugin-transform-es2015-destructuring": "6.23.0",
+ "babel-plugin-transform-es2015-duplicate-keys": "6.24.1",
+ "babel-plugin-transform-es2015-for-of": "6.23.0",
+ "babel-plugin-transform-es2015-function-name": "6.24.1",
+ "babel-plugin-transform-es2015-literals": "6.22.0",
+ "babel-plugin-transform-es2015-modules-amd": "6.24.1",
+ "babel-plugin-transform-es2015-modules-commonjs": "6.26.0",
+ "babel-plugin-transform-es2015-modules-systemjs": "6.24.1",
+ "babel-plugin-transform-es2015-modules-umd": "6.24.1",
+ "babel-plugin-transform-es2015-object-super": "6.24.1",
+ "babel-plugin-transform-es2015-parameters": "6.24.1",
+ "babel-plugin-transform-es2015-shorthand-properties": "6.24.1",
+ "babel-plugin-transform-es2015-spread": "6.22.0",
+ "babel-plugin-transform-es2015-sticky-regex": "6.24.1",
+ "babel-plugin-transform-es2015-template-literals": "6.22.0",
+ "babel-plugin-transform-es2015-typeof-symbol": "6.23.0",
+ "babel-plugin-transform-es2015-unicode-regex": "6.24.1",
+ "babel-plugin-transform-regenerator": "6.26.0"
+ }
+ },
+ "babel-register": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
+ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
+ "dev": true,
+ "requires": {
+ "babel-core": "6.26.0",
+ "babel-runtime": "6.26.0",
+ "core-js": "2.5.3",
+ "home-or-tmp": "2.0.0",
+ "lodash": "4.17.5",
+ "mkdirp": "0.5.1",
+ "source-map-support": "0.4.18"
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "dev": true,
+ "requires": {
+ "core-js": "2.5.3",
+ "regenerator-runtime": "0.11.1"
+ }
+ },
+ "babel-template": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-traverse": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "lodash": "4.17.5"
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "6.26.0",
+ "babel-messages": "6.23.0",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "debug": "2.6.9",
+ "globals": "9.18.0",
+ "invariant": "2.2.4",
+ "lodash": "4.17.5"
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "esutils": "2.0.2",
+ "lodash": "4.17.5",
+ "to-fast-properties": "1.0.3"
+ }
+ },
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
+ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "tweetnacl": "0.14.5"
+ }
+ },
+ "boom": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
+ "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
+ "dev": true,
+ "requires": {
+ "hoek": "4.2.1"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
+ "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
+ "dev": true,
+ "requires": {
+ "pako": "0.2.9"
+ }
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+ "dev": true
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "dev": true,
+ "requires": {
+ "camelcase": "2.1.1",
+ "map-obj": "1.0.1"
+ }
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "center-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+ "dev": true,
+ "requires": {
+ "align-text": "0.1.4",
+ "lazy-cache": "1.0.4"
+ }
+ },
+ "chai": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
+ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
+ "dev": true,
+ "requires": {
+ "assertion-error": "1.1.0",
+ "deep-eql": "0.1.3",
+ "type-detect": "1.0.0"
+ }
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "2.2.1",
+ "escape-string-regexp": "1.0.5",
+ "has-ansi": "2.0.0",
+ "strip-ansi": "3.0.1",
+ "supports-color": "2.0.0"
+ }
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+ "dev": true,
+ "requires": {
+ "center-align": "0.1.3",
+ "right-align": "0.1.3",
+ "wordwrap": "0.0.2"
+ }
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
+ },
+ "coffeescript": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.10.0.tgz",
+ "integrity": "sha1-56qDAZF+9iGzXYo580jc3R234z4=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
+ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
+ "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.1.tgz",
+ "integrity": "sha512-gslSSJx03QKa59cIKqeJO9HQ/WZMotvYJCuaUULrLpjj8oG40kV2Z+gz82pVxlTkOADi4PJxQPPfhl1ELYrrXw==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "readable-stream": "2.3.5",
+ "typedarray": "0.0.6"
+ }
+ },
+ "convert-source-map": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
+ "integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=",
+ "dev": true
+ },
+ "core-js": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz",
+ "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "corser": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
+ "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=",
+ "dev": true
+ },
+ "cryptiles": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
+ "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
+ "dev": true,
+ "requires": {
+ "boom": "5.2.0"
+ },
+ "dependencies": {
+ "boom": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
+ "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
+ "dev": true,
+ "requires": {
+ "hoek": "4.2.1"
+ }
+ }
+ }
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "dev": true,
+ "requires": {
+ "array-find-index": "1.0.2"
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0"
+ }
+ },
+ "dateformat": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
+ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "4.0.1",
+ "meow": "3.7.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+ "dev": true
+ },
+ "deep-eql": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
+ "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
+ "dev": true,
+ "requires": {
+ "type-detect": "0.1.1"
+ },
+ "dependencies": {
+ "type-detect": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
+ "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
+ "dev": true
+ }
+ }
+ },
+ "define-properties": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
+ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
+ "dev": true,
+ "requires": {
+ "foreach": "2.0.5",
+ "object-keys": "1.0.11"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "detect-indent": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+ "dev": true,
+ "requires": {
+ "repeating": "2.0.1"
+ }
+ },
+ "diff": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
+ "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=",
+ "dev": true
+ },
+ "ecc-jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
+ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "jsbn": "0.1.1"
+ }
+ },
+ "ecstatic": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-0.7.6.tgz",
+ "integrity": "sha1-y6KqvqRrjNl/AWCFlxO3DSjmoCI=",
+ "dev": true,
+ "requires": {
+ "he": "0.5.0",
+ "mime": "1.6.0",
+ "minimist": "1.2.0",
+ "url-join": "0.0.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
+ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "0.2.1"
+ }
+ },
+ "es6-promise": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz",
+ "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "eventemitter2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz",
+ "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz",
+ "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=",
+ "dev": true
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "extend": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
+ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=",
+ "dev": true
+ },
+ "extract-zip": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz",
+ "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=",
+ "dev": true,
+ "requires": {
+ "concat-stream": "1.6.0",
+ "debug": "2.6.9",
+ "mkdirp": "0.5.0",
+ "yauzl": "2.4.1"
+ },
+ "dependencies": {
+ "concat-stream": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "readable-stream": "2.3.5",
+ "typedarray": "0.0.6"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
+ "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ }
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": true
+ },
+ "fd-slicer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
+ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
+ "dev": true,
+ "requires": {
+ "pend": "1.2.0"
+ }
+ },
+ "figures": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "1.0.5",
+ "object-assign": "4.1.1"
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "2.1.0",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "findup-sync": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz",
+ "integrity": "sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY=",
+ "dev": true,
+ "requires": {
+ "glob": "5.0.15"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
+ "dev": true,
+ "requires": {
+ "inflight": "1.0.6",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "path-is-absolute": "1.0.1"
+ }
+ }
+ }
+ },
+ "foreach": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
+ "dev": true
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
+ "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
+ "dev": true,
+ "requires": {
+ "asynckit": "0.4.0",
+ "combined-stream": "1.0.6",
+ "mime-types": "2.1.18"
+ }
+ },
+ "fs-extra": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
+ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "jsonfile": "2.4.0",
+ "klaw": "1.3.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+ "dev": true
+ },
+ "getobject": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz",
+ "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "1.0.0",
+ "inflight": "1.0.6",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "path-is-absolute": "1.0.1"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+ "dev": true
+ },
+ "growl": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
+ "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
+ "dev": true
+ },
+ "grunt": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.0.2.tgz",
+ "integrity": "sha1-TmpeaVtwRy/VME9fqeNCNoNqc7w=",
+ "dev": true,
+ "requires": {
+ "coffeescript": "1.10.0",
+ "dateformat": "1.0.12",
+ "eventemitter2": "0.4.14",
+ "exit": "0.1.2",
+ "findup-sync": "0.3.0",
+ "glob": "7.0.6",
+ "grunt-cli": "1.2.0",
+ "grunt-known-options": "1.1.0",
+ "grunt-legacy-log": "1.0.1",
+ "grunt-legacy-util": "1.0.0",
+ "iconv-lite": "0.4.19",
+ "js-yaml": "3.5.5",
+ "minimatch": "3.0.4",
+ "nopt": "3.0.6",
+ "path-is-absolute": "1.0.1",
+ "rimraf": "2.2.8"
+ },
+ "dependencies": {
+ "grunt-cli": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz",
+ "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=",
+ "dev": true,
+ "requires": {
+ "findup-sync": "0.3.0",
+ "grunt-known-options": "1.1.0",
+ "nopt": "3.0.6",
+ "resolve": "1.1.7"
+ }
+ }
+ }
+ },
+ "grunt-babel": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-babel/-/grunt-babel-6.0.0.tgz",
+ "integrity": "sha1-N4GJtIfeEWjExKn8iN1gBbNd+WA=",
+ "dev": true,
+ "requires": {
+ "babel-core": "6.26.0"
+ }
+ },
+ "grunt-contrib-concat": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-concat/-/grunt-contrib-concat-1.0.1.tgz",
+ "integrity": "sha1-YVCYYwhOhx1+ht5IwBUlntl3Rb0=",
+ "dev": true,
+ "requires": {
+ "chalk": "1.1.3",
+ "source-map": "0.5.7"
+ }
+ },
+ "grunt-contrib-uglify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-2.3.0.tgz",
+ "integrity": "sha1-s9AmDr3WzvoS/y+Onh4ln33kIW8=",
+ "dev": true,
+ "requires": {
+ "chalk": "1.1.3",
+ "maxmin": "1.1.0",
+ "object.assign": "4.1.0",
+ "uglify-js": "2.8.29",
+ "uri-path": "1.0.0"
+ }
+ },
+ "grunt-known-options": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz",
+ "integrity": "sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk=",
+ "dev": true
+ },
+ "grunt-legacy-log": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-1.0.1.tgz",
+ "integrity": "sha512-rwuyqNKlI0IPz0DvxzJjcEiQEBaBNVeb1LFoZKxSmHLETFUwhwUrqOsPIxURTKSwNZHZ4ht1YLBYmVU0YZAzHQ==",
+ "dev": true,
+ "requires": {
+ "colors": "1.1.2",
+ "grunt-legacy-log-utils": "1.0.0",
+ "hooker": "0.2.3",
+ "lodash": "4.17.5",
+ "underscore.string": "3.3.4"
+ }
+ },
+ "grunt-legacy-log-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-1.0.0.tgz",
+ "integrity": "sha1-p7ji0Ps1taUPSvmG/BEnSevJbz0=",
+ "dev": true,
+ "requires": {
+ "chalk": "1.1.3",
+ "lodash": "4.3.0"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz",
+ "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-legacy-util": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-1.0.0.tgz",
+ "integrity": "sha1-OGqnjcbtUJhsKxiVcmWxtIq7m4Y=",
+ "dev": true,
+ "requires": {
+ "async": "1.5.2",
+ "exit": "0.1.2",
+ "getobject": "0.1.0",
+ "hooker": "0.2.3",
+ "lodash": "4.3.0",
+ "underscore.string": "3.2.3",
+ "which": "1.2.14"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.3.0.tgz",
+ "integrity": "sha1-79nEpuxT87BUEkKZFcPkgk5NJaQ=",
+ "dev": true
+ },
+ "underscore.string": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.3.tgz",
+ "integrity": "sha1-gGmSYzZl1eX8tNsfs6hi62jp5to=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-lib-phantomjs": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/grunt-lib-phantomjs/-/grunt-lib-phantomjs-1.1.0.tgz",
+ "integrity": "sha1-np7c3Z/S3UDgwYHJQ3HVcqpe6tI=",
+ "dev": true,
+ "requires": {
+ "eventemitter2": "0.4.14",
+ "phantomjs-prebuilt": "2.1.16",
+ "rimraf": "2.6.2",
+ "semver": "5.5.0",
+ "temporary": "0.0.8"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "dev": true,
+ "requires": {
+ "glob": "7.0.6"
+ }
+ }
+ }
+ },
+ "grunt-mocha": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grunt-mocha/-/grunt-mocha-1.0.4.tgz",
+ "integrity": "sha1-7iYdxmoDAC4MqkLLf9QdFrKVar0=",
+ "dev": true,
+ "requires": {
+ "grunt-lib-phantomjs": "1.1.0",
+ "lodash": "3.10.1",
+ "mocha": "2.5.3"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
+ "dev": true
+ }
+ }
+ },
+ "grunt-strip-code": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/grunt-strip-code/-/grunt-strip-code-1.0.6.tgz",
+ "integrity": "sha1-1OzlbYf2UhU61Q0W+FyA4kNtL4U=",
+ "dev": true,
+ "requires": {
+ "array-last": "0.1.0",
+ "escape-string-regexp": "1.0.5"
+ }
+ },
+ "gzip-size": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz",
+ "integrity": "sha1-Zs+LEBBHInuVus5uodoMF37Vwi8=",
+ "dev": true,
+ "requires": {
+ "browserify-zlib": "0.1.4",
+ "concat-stream": "1.6.1"
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
+ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
+ "dev": true,
+ "requires": {
+ "ajv": "5.5.2",
+ "har-schema": "2.0.0"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "2.1.1"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+ "dev": true
+ },
+ "hasha": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz",
+ "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=",
+ "dev": true,
+ "requires": {
+ "is-stream": "1.1.0",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "hawk": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
+ "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
+ "dev": true,
+ "requires": {
+ "boom": "4.3.1",
+ "cryptiles": "3.1.2",
+ "hoek": "4.2.1",
+ "sntp": "2.1.0"
+ }
+ },
+ "he": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-0.5.0.tgz",
+ "integrity": "sha1-LAX/rvkLaOhg8/0rVO9YCYknfuI=",
+ "dev": true
+ },
+ "hoek": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
+ "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==",
+ "dev": true
+ },
+ "home-or-tmp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
+ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
+ "dev": true,
+ "requires": {
+ "os-homedir": "1.0.2",
+ "os-tmpdir": "1.0.2"
+ }
+ },
+ "hooker": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz",
+ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
+ "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw==",
+ "dev": true
+ },
+ "http-proxy": {
+ "version": "1.16.2",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz",
+ "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "1.2.0",
+ "requires-port": "1.0.0"
+ }
+ },
+ "http-server": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.8.5.tgz",
+ "integrity": "sha1-u/J8nwlJnlGh/h+Xmm85pFoE8vs=",
+ "dev": true,
+ "requires": {
+ "colors": "1.0.3",
+ "corser": "2.0.1",
+ "ecstatic": "0.7.6",
+ "http-proxy": "1.16.2",
+ "opener": "1.4.3",
+ "optimist": "0.6.1",
+ "portfinder": "0.4.0",
+ "union": "0.4.6"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
+ "dev": true
+ }
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "jsprim": "1.4.1",
+ "sshpk": "1.14.1"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "dev": true,
+ "requires": {
+ "repeating": "2.0.1"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "1.4.0",
+ "wrappy": "1.0.2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "1.3.1"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "1.1.1"
+ }
+ },
+ "is-finite": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "1.0.1"
+ }
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "jade": {
+ "version": "0.26.3",
+ "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
+ "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=",
+ "dev": true,
+ "requires": {
+ "commander": "0.6.1",
+ "mkdirp": "0.3.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
+ "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
+ "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=",
+ "dev": true
+ }
+ }
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.5.5",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.5.5.tgz",
+ "integrity": "sha1-A3fDgBfKvHMisNH7zSWkkWQfL74=",
+ "dev": true,
+ "requires": {
+ "argparse": "1.0.10",
+ "esprima": "2.7.3"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true,
+ "optional": true
+ },
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+ "dev": true
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "json5": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.1.11"
+ }
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "kew": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
+ "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "1.1.6"
+ }
+ },
+ "klaw": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.1.11"
+ }
+ },
+ "lazy-cache": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+ "dev": true
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "parse-json": "2.2.0",
+ "pify": "2.3.0",
+ "pinkie-promise": "2.0.1",
+ "strip-bom": "2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.5",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
+ "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==",
+ "dev": true
+ },
+ "longest": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+ "dev": true
+ },
+ "loose-envify": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
+ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+ "dev": true,
+ "requires": {
+ "js-tokens": "3.0.2"
+ }
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "dev": true,
+ "requires": {
+ "currently-unhandled": "0.4.1",
+ "signal-exit": "3.0.2"
+ }
+ },
+ "lru-cache": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
+ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
+ "dev": true
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+ "dev": true
+ },
+ "maxmin": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-1.1.0.tgz",
+ "integrity": "sha1-cTZehKmd2Piz99X94vANHn9zvmE=",
+ "dev": true,
+ "requires": {
+ "chalk": "1.1.3",
+ "figures": "1.7.0",
+ "gzip-size": "1.0.0",
+ "pretty-bytes": "1.0.4"
+ }
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "dev": true,
+ "requires": {
+ "camelcase-keys": "2.1.0",
+ "decamelize": "1.2.0",
+ "loud-rejection": "1.6.0",
+ "map-obj": "1.0.1",
+ "minimist": "1.2.0",
+ "normalize-package-data": "2.4.0",
+ "object-assign": "4.1.1",
+ "read-pkg-up": "1.0.1",
+ "redent": "1.0.0",
+ "trim-newlines": "1.0.0"
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+ "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.18",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.33.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "1.1.11"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ }
+ }
+ },
+ "mocha": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz",
+ "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=",
+ "dev": true,
+ "requires": {
+ "commander": "2.3.0",
+ "debug": "2.2.0",
+ "diff": "1.4.0",
+ "escape-string-regexp": "1.0.2",
+ "glob": "3.2.11",
+ "growl": "1.9.2",
+ "jade": "0.26.3",
+ "mkdirp": "0.5.1",
+ "supports-color": "1.2.0",
+ "to-iso-string": "0.0.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "dev": true,
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
+ "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=",
+ "dev": true
+ },
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3",
+ "minimatch": "0.3.0"
+ }
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "2.7.3",
+ "sigmund": "1.0.1"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz",
+ "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=",
+ "dev": true
+ }
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "requires": {
+ "abbrev": "1.1.1"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "2.6.0",
+ "is-builtin-module": "1.0.0",
+ "semver": "5.5.0",
+ "validate-npm-package-license": "3.0.3"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
+ "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=",
+ "dev": true
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "requires": {
+ "define-properties": "1.1.2",
+ "function-bind": "1.1.1",
+ "has-symbols": "1.0.0",
+ "object-keys": "1.0.11"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1.0.2"
+ }
+ },
+ "opener": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz",
+ "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=",
+ "dev": true
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.10",
+ "wordwrap": "0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
+ "dev": true
+ }
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "dev": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "package": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package/-/package-1.0.1.tgz",
+ "integrity": "sha1-0lofmeJQbcsn1nBLg9yooxLk7cw=",
+ "dev": true
+ },
+ "pako": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "1.3.1"
+ }
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "pify": "2.3.0",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+ "dev": true
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "phantomjs-prebuilt": {
+ "version": "2.1.16",
+ "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz",
+ "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=",
+ "dev": true,
+ "requires": {
+ "es6-promise": "4.2.4",
+ "extract-zip": "1.6.6",
+ "fs-extra": "1.0.0",
+ "hasha": "2.2.0",
+ "kew": "0.7.0",
+ "progress": "1.1.8",
+ "request": "2.85.0",
+ "request-progress": "2.0.1",
+ "which": "1.2.14"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "2.0.4"
+ }
+ },
+ "portfinder": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-0.4.0.tgz",
+ "integrity": "sha1-o/+t/6/k+5jgYBqF7aJ8J86Eyh4=",
+ "dev": true,
+ "requires": {
+ "async": "0.9.0",
+ "mkdirp": "0.5.1"
+ },
+ "dependencies": {
+ "async": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz",
+ "integrity": "sha1-rDYTsdqb7RtHUQu0ZRuJMeRxRsc=",
+ "dev": true
+ }
+ }
+ },
+ "pretty-bytes": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz",
+ "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "4.0.1",
+ "meow": "3.7.0"
+ }
+ },
+ "private": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "dev": true
+ },
+ "progress": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
+ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
+ "dev": true
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
+ "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "1.1.0",
+ "normalize-package-data": "2.4.0",
+ "path-type": "1.1.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "requires": {
+ "find-up": "1.1.2",
+ "read-pkg": "1.1.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.5.tgz",
+ "integrity": "sha512-tK0yDhrkygt/knjowCUiWP9YdV7c5R+8cR0r/kt9ZhBU906Fs6RpQJCEilamRJj1Nx2rWI6LkW9gKqjTkshhEw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "2.0.0",
+ "safe-buffer": "5.1.1",
+ "string_decoder": "1.0.3",
+ "util-deprecate": "1.0.2"
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "dev": true,
+ "requires": {
+ "indent-string": "2.1.0",
+ "strip-indent": "1.0.1"
+ }
+ },
+ "regenerate": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz",
+ "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==",
+ "dev": true
+ },
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+ "dev": true
+ },
+ "regenerator-transform": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
+ "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "private": "0.1.8"
+ }
+ },
+ "regexpu-core": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
+ "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=",
+ "dev": true,
+ "requires": {
+ "regenerate": "1.3.3",
+ "regjsgen": "0.2.0",
+ "regjsparser": "0.1.5"
+ }
+ },
+ "regjsgen": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+ "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
+ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
+ "dev": true,
+ "requires": {
+ "jsesc": "0.5.0"
+ }
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true,
+ "requires": {
+ "is-finite": "1.0.2"
+ }
+ },
+ "request": {
+ "version": "2.85.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz",
+ "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "0.7.0",
+ "aws4": "1.6.0",
+ "caseless": "0.12.0",
+ "combined-stream": "1.0.6",
+ "extend": "3.0.1",
+ "forever-agent": "0.6.1",
+ "form-data": "2.3.2",
+ "har-validator": "5.0.3",
+ "hawk": "6.0.2",
+ "http-signature": "1.2.0",
+ "is-typedarray": "1.0.0",
+ "isstream": "0.1.2",
+ "json-stringify-safe": "5.0.1",
+ "mime-types": "2.1.18",
+ "oauth-sign": "0.8.2",
+ "performance-now": "2.1.0",
+ "qs": "6.5.1",
+ "safe-buffer": "5.1.1",
+ "stringstream": "0.0.5",
+ "tough-cookie": "2.3.4",
+ "tunnel-agent": "0.6.0",
+ "uuid": "3.2.1"
+ }
+ },
+ "request-progress": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz",
+ "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=",
+ "dev": true,
+ "requires": {
+ "throttleit": "1.0.0"
+ }
+ },
+ "requirejs": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.5.tgz",
+ "integrity": "sha512-svnO+aNcR/an9Dpi44C7KSAy5fFGLtmPbaaCeQaklUz8BQhS64tWWIIlvEA5jrWICzlO/X9KSzSeXFnZdBu8nw==",
+ "dev": true
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ },
+ "right-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+ "dev": true,
+ "requires": {
+ "align-text": "0.1.4"
+ }
+ },
+ "rimraf": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
+ "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
+ "dev": true
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+ "dev": true
+ },
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "slash": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+ "dev": true
+ },
+ "sntp": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
+ "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
+ "dev": true,
+ "requires": {
+ "hoek": "4.2.1"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "0.5.7"
+ }
+ },
+ "spdx-correct": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
+ "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "3.0.0",
+ "spdx-license-ids": "3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
+ "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "2.1.0",
+ "spdx-license-ids": "3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
+ "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz",
+ "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz",
+ "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=",
+ "dev": true,
+ "requires": {
+ "asn1": "0.2.3",
+ "assert-plus": "1.0.0",
+ "bcrypt-pbkdf": "1.0.1",
+ "dashdash": "1.14.1",
+ "ecc-jsbn": "0.1.1",
+ "getpass": "0.1.7",
+ "jsbn": "0.1.1",
+ "tweetnacl": "0.14.5"
+ }
+ },
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
+ "stringstream": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+ "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "2.1.1"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "0.2.1"
+ }
+ },
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "4.0.1"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ },
+ "temporary": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/temporary/-/temporary-0.0.8.tgz",
+ "integrity": "sha1-oYqYHSi6jKNgJ/s8MFOMPst0CsA=",
+ "dev": true,
+ "requires": {
+ "package": "1.0.1"
+ }
+ },
+ "throttleit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
+ "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+ "dev": true
+ },
+ "to-iso-string": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz",
+ "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=",
+ "dev": true
+ },
+ "tough-cookie": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
+ "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
+ "dev": true,
+ "requires": {
+ "punycode": "1.4.1"
+ }
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+ "dev": true
+ },
+ "trim-right": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true,
+ "optional": true
+ },
+ "type-detect": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
+ "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
+ "dev": true
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "2.8.29",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+ "dev": true,
+ "requires": {
+ "source-map": "0.5.7",
+ "uglify-to-browserify": "1.0.2",
+ "yargs": "3.10.0"
+ }
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+ "dev": true,
+ "optional": true
+ },
+ "underscore.string": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz",
+ "integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "1.1.1",
+ "util-deprecate": "1.0.2"
+ }
+ },
+ "union": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/union/-/union-0.4.6.tgz",
+ "integrity": "sha1-GY+9rrolTniLDvy2MLwR8kopWeA=",
+ "dev": true,
+ "requires": {
+ "qs": "2.3.3"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz",
+ "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=",
+ "dev": true
+ }
+ }
+ },
+ "uri-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/uri-path/-/uri-path-1.0.0.tgz",
+ "integrity": "sha1-l0fwGDWJM8Md4PzP2C0TjmcmLjI=",
+ "dev": true
+ },
+ "url-join": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz",
+ "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g=",
+ "dev": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
+ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
+ "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "3.0.0",
+ "spdx-expression-parse": "3.0.0"
+ }
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "1.3.0"
+ }
+ },
+ "which": {
+ "version": "1.2.14",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
+ "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
+ "dev": true,
+ "requires": {
+ "isexe": "2.0.0"
+ }
+ },
+ "window-size": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+ "dev": true,
+ "requires": {
+ "camelcase": "1.2.1",
+ "cliui": "2.1.0",
+ "decamelize": "1.2.0",
+ "window-size": "0.1.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+ "dev": true
+ }
+ }
+ },
+ "yauzl": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
+ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
+ "dev": true,
+ "requires": {
+ "fd-slicer": "1.0.1"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index bfd9313..aef8c9c 100644
--- a/package.json
+++ b/package.json
@@ -18,4 +18,4 @@
"mocha": "^2.2.5",
"requirejs": "^2.1.18"
}
-}
\ No newline at end of file
+}
diff --git a/src/recipes/WebP.js b/src/recipes/WebP.js
new file mode 100644
index 0000000..ad5a254
--- /dev/null
+++ b/src/recipes/WebP.js
@@ -0,0 +1,2086 @@
+void function() {
+
+ var _ = (a => new ArrayWrapper(a));
+ _.mapInline = mapInline;
+ _.map = map; /*.......*/ map.bind = (()=>map);
+ _.filter = filter; /*.*/ filter.bind = (()=>filter);
+ _.reduce = reduce; /*.*/ reduce.bind = (()=>reduce);
+ window.CSSUsageLodash = _;
+ // test case:
+ // 35 = CSSUsageLodash([1,2,3,4,5]).map(v => v*v).filter(v => v%2).reduce(0, (a,b)=>(a+b)).value()
+
+ function ArrayWrapper(array) {
+ this.source = array;
+ this.mapInline = function(f) { mapInline(this.source, f); return this; };
+ this.map = function(f) { this.source = map(this.source, f); return this; };
+ this.filter = function(f) { this.source = filter(this.source, f); return this; };
+ this.reduce = function(v,f) { this.source = reduce(this.source, f, v); return this; };
+ this.value = function() { return this.source };
+ }
+
+ function map(source, transform) {
+ var clone = new Array(source.length);
+ for(var i = source.length; i--;) {
+ clone[i] = transform(source[i]);
+ }
+ return clone;
+ }
+
+ function mapInline(source, transform) {
+ for(var i = source.length; i--;) {
+ source[i] = transform(source[i]);
+ }
+ return source;
+ }
+
+ function filter(source, shouldValueBeIncluded) {
+ var clone = new Array(source.length), i=0;
+ for(var s = 0; s <= source.length; s++) {
+ var value = source[s];
+ if(shouldValueBeIncluded(value)) {
+ clone[i++] = value
+ }
+ }
+ clone.length = i;
+ return clone;
+ }
+
+ function reduce(source, computeReduction, reduction) {
+ for(var s = 0; s <= source.length; s++) {
+ var value = source[s];
+ reduction = computeReduction(reduction, value);
+ }
+ return reduction;
+ }
+
+}();
+/*!
+ * Based on:
+ * https://github.com/gilmoreorless/css-shorthand-properties
+ * MIT Licensed: http://gilmoreorless.mit-license.org/
+ */
+void function () {
+ /**
+ * Data collated from multiple W3C specs: http://www.w3.org/Style/CSS/current-work
+ */
+ var shorthands = this.shorthandProperties = {
+
+ // CSS 2.1: http://www.w3.org/TR/CSS2/propidx.html
+ 'list-style': ['-type', '-position', '-image'],
+ 'margin': ['-top', '-right', '-bottom', '-left'],
+ 'outline': ['-width', '-style', '-color'],
+ 'padding': ['-top', '-right', '-bottom', '-left'],
+
+ // CSS Backgrounds and Borders Module Level 3: http://www.w3.org/TR/css3-background/
+ 'background': ['-image', '-position', '-size', '-repeat', '-origin', '-clip', '-attachment', '-color'],
+ 'background-repeat': ['-x','-y'],
+ 'background-position': ['-x','-y'],
+ 'border': ['-width', '-style', '-color'],
+ 'border-color': ['border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color'],
+ 'border-style': ['border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style'],
+ 'border-width': ['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'],
+ 'border-top': ['-width', '-style', '-color'],
+ 'border-right': ['-width', '-style', '-color'],
+ 'border-bottom': ['-width', '-style', '-color'],
+ 'border-left': ['-width', '-style', '-color'],
+ 'border-radius': ['border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius'],
+ 'border-image': ['-source', '-slice', '-width', '-outset', '-repeat'],
+
+ // CSS Fonts Module Level 3: http://www.w3.org/TR/css3-fonts/
+ 'font': ['-style', '-variant', '-weight', '-stretch', '-size', 'line-height', '-family'],
+ 'font-variant': ['-ligatures', '-alternates', '-caps', '-numeric', '-east-asian'],
+
+ // CSS Masking Module Level 1: http://www.w3.org/TR/css-masking/
+ 'mask': ['-image', '-mode', '-position', '-size', '-repeat', '-origin', '-clip'],
+ 'mask-border': ['-source', '-slice', '-width', '-outset', '-repeat', '-mode'],
+
+ // CSS Multi-column Layout Module: http://www.w3.org/TR/css3-multicol/
+ 'columns': ['column-width', 'column-count'],
+ 'column-rule': ['-width', '-style', '-color'],
+
+ // CSS Speech Module: http://www.w3.org/TR/css3-speech/
+ 'cue': ['-before', '-after'],
+ 'pause': ['-before', '-after'],
+ 'rest': ['-before', '-after'],
+
+ // CSS Text Decoration Module Level 3: http://www.w3.org/TR/css-text-decor-3/
+ 'text-decoration': ['-line', '-style', '-color'],
+ 'text-emphasis': ['-style', '-color'],
+
+ // CSS Animations (WD): http://www.w3.org/TR/css3-animations
+ 'animation': ['-name', '-duration', '-timing-function', '-delay', '-iteration-count', '-direction', '-fill-mode', '-play-state'],
+
+ // CSS Transitions (WD): http://www.w3.org/TR/css3-transitions/
+ 'transition': ['-property', '-duration', '-timing-function', '-delay'],
+
+ // CSS Flexible Box Layout Module Level 1 (WD): http://www.w3.org/TR/css3-flexbox/
+ 'flex': ['-grow', '-shrink', '-basis'],
+
+ // CSS Grid: https://drafts.csswg.org/css-grid/#grid-shorthand
+ 'grid': ['-template', '-auto-flow', '-auto-rows','-auto-columns'],
+ 'grid-template': ['-rows', '-columns', '-areas'],
+
+ // Others:
+ 'overflow': ['-x','-y','-style'], // https://drafts.csswg.org/css-overflow-3/
+
+ };
+
+ var expandCache = Object.create(null);
+ var unexpandCache = Object.create(null);
+
+ /**
+ * Expand a shorthand property into an array of longhand properties which are set by it
+ * @param {string} property CSS property name
+ * @return {array} List of longhand properties, or an empty array if it's not a shorthand
+ */
+ this.expand = function (property) {
+
+ var result = expandCache[property];
+ if(result) { return result; }
+
+ var prefixData = property.match(/^(-[a-zA-Z]+-)?(.*)$/);
+ var prefix = prefixData[1]||'', prefixFreeProperty = prefixData[2]||'';
+ if (!shorthands.hasOwnProperty(prefixFreeProperty)) {
+ return [];
+ }
+
+ result = [];
+ shorthands[prefixFreeProperty].forEach((p) => {
+ var longhand = p[0] === '-' ? property + p : prefix + p;
+ result.push(longhand);
+ result.push.apply(result, this.expand(longhand));
+ });
+
+ return expandCache[property] = result;
+
+ };
+
+ /**
+ * Expand a longhand property into an array of shorthand which may set the value
+ * @param {string} property CSS property name
+ * @return {array} List of shorthand properties, or the original property if it's not a shorthand
+ */
+ this.unexpand = function unexpand(property) {
+
+ var result = unexpandCache[property];
+ if(result) { return result; }
+
+ var prefixData = property.match(/^(-[a-zA-Z]+-)?(.*)$/);
+ var prefix = prefixData[1]||'', prefixFreeProperty = prefixData[2]||'';
+
+ result = [];
+ for(var sh = 0; sh <= shorthands.length; sh++) {
+ var shorthand = shorthands[sh];
+ if(this.expand(shorthand).indexOf(prefixFreeProperty) >= 0) {
+ result.push(prefix+shorthand);
+ result.push.apply(result,this.unexpand(prefix+shorthand));
+ }
+ }
+
+ return unexpandCache[property] = result;
+
+ }
+
+}.call(window.CSSShorthands={});
+
+
+// http://blueprintcss.org/tests/parts/grid.html
+var hasBluePrintUsage = function() {
+
+ if(!document.querySelector(".container")) {
+ return false;
+ }
+
+ for(var i = 24+1; --i;) {
+ if(document.querySelector(".container > .span-"+i)) {
+ return true;
+ }
+ }
+ return false;
+
+}
+//
+// report how many times the classes in the following arrays have been used in the dom
+// (bootstrap stats)
+//
+
+var detectedBootstrapGridUsages = function(domClasses) {
+ var _ = window.CSSUsageLodash;
+ var reduce = _.reduce.bind(_);
+ var trackedClasses = [];
+
+ var sizes = ['xs','sm','md','lg'];
+ for(var i = sizes.length; i--;) { var size = sizes[i];
+ for(var j = 12+1; --j;) {
+ trackedClasses.push('col-'+size+'-'+j);
+ for(var k = 12+1; --k;) {
+ trackedClasses.push('col-'+size+'-'+j+'-offset-'+k);
+ trackedClasses.push('col-'+size+'-'+j+'-push-'+k);
+ trackedClasses.push('col-'+size+'-'+j+'-pull-'+k);
+ }
+ }
+ }
+
+ return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
+
+};
+
+var detectedBootstrapFormUsages = function(domClasses) {
+ var _ = window.CSSUsageLodash;
+ var reduce = _.reduce.bind(_);
+ var trackedClasses = [
+ 'form-group', 'form-group-xs', 'form-group-sm', 'form-group-md', 'form-group-lg',
+ 'form-control', 'form-horizontal', 'form-inline',
+ 'btn','btn-primary','btn-secondary','btn-success','btn-warning','btn-danger','btn-error'
+ ];
+
+ return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
+
+};
+
+var detectedBootstrapAlertUsages = function(domClasses) {
+ var _ = window.CSSUsageLodash;
+ var reduce = _.reduce.bind(_);
+ var trackedClasses = [
+ 'alert','alert-primary','alert-secondary','alert-success','alert-warning','alert-danger','alert-error'
+ ];
+
+ return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
+
+};
+
+var detectedBootstrapFloatUsages = function(domClasses) {
+ var _ = window.CSSUsageLodash;
+ var reduce = _.reduce.bind(_);
+ var trackedClasses = [
+ 'pull-left','pull-right',
+ ];
+
+ return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
+
+};
+// https://github.com/Dogfalo/materialize/blob/master/sass/components/_grid.scss
+var hasDogfaloMaterializeUsage = function() {
+
+ if(!document.querySelector(".container > .row > .col")) {
+ return false;
+ }
+
+ for(var i = 12+1; --i;) {
+ var classesToLookUp = ['s','m','l'];
+ for(var d = 0; d < classesToLookUp.length; d++) {
+ var s = classesToLookUp[d];
+ if(document.querySelector(".container > .row > .col."+s+""+i)) {
+ return true;
+ }
+ }
+ }
+ return false;
+
+}
+// http://www.gumbyframework.com/docs/grid/#!/basic-grid
+var hasGrumbyUsage = function() {
+
+ if(!document.querySelector(".row .columns")) {
+ return false;
+ }
+
+ var classesToLookUp = ["one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"];
+ for(var cl = 0; cl < classesToLookUp.length; cl++ ) {
+ var fraction = classesToLookUp[cl];
+ if(document.querySelector(".row > .columns."+fraction)) {
+ return true;
+ }
+ }
+ return false;
+
+}
+// https://raw.githubusercontent.com/csswizardry/inuit.css/master/generic/_widths.scss
+var hasInuitUsage = function() {
+
+ if(!document.querySelector(".grid .grid__item")) {
+ return false;
+ }
+
+ var classesToLookUp = ["one-whole","one-half","one-third","two-thirds","one-quarter","two-quarters","one-half","three-quarters","one-fifth","two-fifths","three-fifths","four-fifths","one-sixth","two-sixths","one-third","three-sixths","one-half","four-sixths","two-thirds","five-sixths","one-eighth","two-eighths","one-quarter","three-eighths","four-eighths","one-half","five-eighths","six-eighths","three-quarters","seven-eighths","one-tenth","two-tenths","one-fifth","three-tenths","four-tenths","two-fifths","five-tenths","one-half","six-tenths","three-fifths","seven-tenths","eight-tenths","four-fifths","nine-tenths","one-twelfth","two-twelfths","one-sixth","three-twelfths","one-quarter","four-twelfths","one-third","five-twelfths","six-twelfths","one-half","seven-twelfths","eight-twelfths","two-thirds","nine-twelfths","three-quarters","ten-twelfths","five-sixths","eleven-twelfths"];
+
+ for(var cu = 0; cu < classesToLookUp.length; cu++ ) {
+ var fraction = classesToLookUp[cu];
+
+ var subClassesToLookUp = ["","palm-","lap-","portable-","desk-"];
+ for(var sc = 0; sc < subClassesToLookUp.length; sc++) {
+ var ns = subClassesToLookUp[sc];
+ if(document.querySelector(".grid > .grid__item."+ns+fraction)) {
+ return true;
+ }
+ }
+ }
+ return false;
+
+}
+var getLonelyGatesUsage = function (cssLonelyClassGates, domClasses, domIds, cssLonelyIdGates) {
+
+ var _ = window.CSSUsageLodash;
+
+ if((cssLonelyClassGates || domClasses || domIds || cssLonelyIdGates) == undefined) return;
+
+ // get arrays of the .class gates used ({"hover":5} => ["hover"]), filter irrelevant entries
+ var cssUniqueLonelyClassGatesArray = Object.keys(cssLonelyClassGates);
+ var cssUniqueLonelyClassGatesUsedArray = _(cssUniqueLonelyClassGatesArray).filter((c) => domClasses[c]).value();
+ var cssUniqueLonelyClassGatesUsedWorthArray = _(cssUniqueLonelyClassGatesUsedArray).filter((c)=>(cssLonelyClassGates[c]>9)).value();
+ if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(cssLonelyClassGates);
+ if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(cssUniqueLonelyClassGatesUsedWorthArray);
+
+ // get arrays of the #id gates used ({"hover":5} => ["hover"]), filter irrelevant entries
+ var cssUniqueLonelyIdGatesArray = Object.keys(cssLonelyIdGates);
+ var cssUniqueLonelyIdGatesUsedArray = _(cssUniqueLonelyIdGatesArray).filter((c) => domIds[c]).value();
+ var cssUniqueLonelyIdGatesUsedWorthArray = _(cssUniqueLonelyIdGatesUsedArray).filter((c)=>(cssLonelyIdGates[c]>9)).value();
+ if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(cssLonelyIdGates);
+ if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(cssUniqueLonelyIdGatesUsedWorthArray);
+}
+//
+// report how many times the classes in the following arrays have been used as css gate
+// (modernizer stats)
+//
+
+// https://modernizr.com/docs#features
+var detectedModernizerUsages = function(cssLonelyClassGates) {
+
+ if((cssLonelyClassGates) == undefined) return;
+
+ var ModernizerUsages = {count:0,values:{/* "js":1, "no-js":2 */}};
+ var trackedClasses = ["js","ambientlight","applicationcache","audio","batteryapi","blobconstructor","canvas","canvastext","contenteditable","contextmenu","cookies","cors","cryptography","customprotocolhandler","customevent","dart","dataview","emoji","eventlistener","exiforientation","flash","fullscreen","gamepads","geolocation","hashchange","hiddenscroll","history","htmlimports","ie8compat","indexeddb","indexeddbblob","input","search","inputtypes","intl","json","olreversed","mathml","notification","pagevisibility","performance","pointerevents","pointerlock","postmessage","proximity","queryselector","quotamanagement","requestanimationframe","serviceworker","svg","templatestrings","touchevents","typedarrays","unicoderange","unicode","userdata","vibrate","video","vml","webintents","animation","webgl","websockets","xdomainrequest","adownload","audioloop","audiopreload","webaudio","lowbattery","canvasblending","todataurljpeg,todataurlpng,todataurlwebp","canvaswinding","getrandomvalues","cssall","cssanimations","appearance","backdropfilter","backgroundblendmode","backgroundcliptext","bgpositionshorthand","bgpositionxy","bgrepeatspace,bgrepeatround","backgroundsize","bgsizecover","borderimage","borderradius","boxshadow","boxsizing","csscalc","checked","csschunit","csscolumns","cubicbezierrange","display-runin","displaytable","ellipsis","cssescape","cssexunit","cssfilters","flexbox","flexboxlegacy","flexboxtweener","flexwrap","fontface","generatedcontent","cssgradients","hsla","csshyphens,softhyphens,softhyphensfind","cssinvalid","lastchild","cssmask","mediaqueries","multiplebgs","nthchild","objectfit","opacity","overflowscrolling","csspointerevents","csspositionsticky","csspseudoanimations","csspseudotransitions","cssreflections","regions","cssremunit","cssresize","rgba","cssscrollbar","shapes","siblinggeneral","subpixelfont","supports","target","textalignlast","textshadow","csstransforms","csstransforms3d","preserve3d","csstransitions","userselect","cssvalid","cssvhunit","cssvmaxunit","cssvminunit","cssvwunit","willchange","wrapflow","classlist","createelementattrs,createelement-attrs","dataset","documentfragment","hidden","microdata","mutationobserver","bdi","datalistelem","details","outputelem","picture","progressbar,meter","ruby","template","time","texttrackapi,track","unknownelements","es5array","es5date","es5function","es5object","es5","strictmode","es5string","es5syntax","es5undefined","es6array","contains","generators","es6math","es6number","es6object","promises","es6string","devicemotion,deviceorientation","oninput","filereader","filesystem","capture","fileinput","directory","formattribute","localizednumber","placeholder","requestautocomplete","formvalidation","sandbox","seamless","srcdoc","apng","jpeg2000","jpegxr","sizes","srcset","webpalpha","webpanimation","webplossless,webp-lossless","webp","inputformaction","inputformenctype","inputformmethod","inputformtarget","beacon","lowbandwidth","eventsource","fetch","xhrresponsetypearraybuffer","xhrresponsetypeblob","xhrresponsetypedocument","xhrresponsetypejson","xhrresponsetypetext","xhrresponsetype","xhr2","scriptasync","scriptdefer","speechrecognition","speechsynthesis","localstorage","sessionstorage","websqldatabase","stylescoped","svgasimg","svgclippaths","svgfilters","svgforeignobject","inlinesvg","smil","textareamaxlength","bloburls","datauri","urlparser","videoautoplay","videoloop","videopreload","webglextensions","datachannel","getusermedia","peerconnection","websocketsbinary","atob-btoa","framed","matchmedia","blobworkers","dataworkers","sharedworkers","transferables","webworkers"];
+ for(var tc = 0; tc < trackedClasses.length; tc++) {
+ var c = trackedClasses[tc];
+ countInstancesOfTheClass(c);
+ countInstancesOfTheClass('no-'+c);
+ }
+ return ModernizerUsages;
+
+ function countInstancesOfTheClass(c) {
+ var count = cssLonelyClassGates[c]; if(!count) return;
+ ModernizerUsages.count += count;
+ ModernizerUsages.values[c]=count;
+ }
+
+}
+function getFwkUsage(results, cssLonelyClassGates, domClasses, domIds, cssLonelyIdGates, cssClasses) {
+
+ // Modernizer
+ getLonelyGatesUsage(cssLonelyClassGates, domClasses, domIds, cssLonelyIdGates);
+ detectedModernizerUsages(cssLonelyClassGates);
+ results.FwkModernizer = !!window.Modernizer;
+ results.FwkModernizerDOMUsages = detectedModernizerUsages(domClasses);
+ results.FwkModernizerCSSUsages = detectedModernizerUsages(cssLonelyClassGates);
+
+ // Bootstrap
+ results.FwkBootstrap = !!((window.jQuery||window.$) && (window.jQuery||window.$).fn && (window.jQuery||window.$).fn.modal)|0;
+ results.FwkBootstrapGridUsage = detectedBootstrapGridUsages(domClasses);
+ results.FwkBootstrapFormUsage = detectedBootstrapFormUsages(domClasses);
+ results.FwkBootstrapFloatUsage = detectedBootstrapFloatUsages(domClasses);
+ results.FwkBootstrapAlertUsage = detectedBootstrapAlertUsages(domClasses);
+ results.FwkBootstrapGridRecognized = detectedBootstrapGridUsages(cssClasses);
+ results.FwkBootstrapFormRecognized = detectedBootstrapFormUsages(cssClasses);
+ results.FwkBootstrapFloatRecognized = detectedBootstrapFloatUsages(cssClasses);
+ results.FwkBootstrapAlertRecognized = detectedBootstrapAlertUsages(cssClasses);
+
+ // Grumby
+ results.FwkGrumby = hasGrumbyUsage()|0;
+
+ // Inuit
+ results.FwkInuit = hasInuitUsage()|0;
+
+ // Blueprint
+ results.FwkBluePrint = hasBluePrintUsage()|0;
+
+ // Dog Falo
+ results.FwkDogfaloMaterialize = hasDogfaloMaterializeUsage()|0;
+
+ return results;
+}
+//
+// report how many times the classes in the following arrays have been used in the dom
+// (general stats)
+//
+
+/** count how many times the usual clearfix classes are used */
+var detectedClearfixUsages = function(domClasses) {
+
+ var _ = window.CSSUsageLodash;
+ var reduce = _.reduce.bind(_);
+
+ var trackedClasses = [
+ 'clearfix','clear',
+ ];
+
+ return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
+
+};
+
+/** count how many times the usual hide/show classes are used */
+var detectedVisibilityUsages = function(domClasses) {
+ var _ = window.CSSUsageLodash;
+ var reduce = _.reduce.bind(_);
+
+ var trackedClasses = [
+ 'show', 'hide', 'visible', 'hidden',
+ ];
+
+ return reduce(trackedClasses, (a,b) => a+(domClasses[b]|0), 0);
+
+};
+function getPatternUsage(results, domClasses, cssClasses) {
+ results.PatClearfixUsage = detectedClearfixUsages(domClasses);
+ results.PatVisibilityUsage = detectedVisibilityUsages(domClasses);
+ results.PatClearfixRecognized = detectedClearfixUsages(cssClasses);
+ results.PatVisibilityRecognized = detectedVisibilityUsages(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() { try {
+
+ var _ = window.CSSUsageLodash;
+ var map = _.map.bind(_);
+ var mapInline = _.mapInline ? _.mapInline : map;
+ var reduce = _.reduce.bind(_);
+ var filter = _.filter.bind(_);
+
+ var browserIsEdge = navigator.userAgent.indexOf('Edge')>=0;
+ var browserIsFirefox = navigator.userAgent.indexOf('Firefox')>=0;
+
+ //
+ // Guards execution against invalid conditions
+ //
+ void function() {
+
+ // Don't run in subframes for now
+ if (top.location.href !== location.href) throw new Error("CSSUsage: the script doesn't run in frames for now");
+
+ // Don't run if already ran
+ if (window.CSSUsage) throw new Error("CSSUsage: second execution attempted; only one run can be executed; if you specified parameters, check the right ones were chosen");
+
+ // Don't run if we don't have lodash
+ if (!window.CSSUsageLodash) throw new Error("CSSUsage: missing CSSUsageLodash dependency");
+
+ if (!window.HtmlUsage) throw new Error("APIUsage: missing HtmlUsage dependancy");
+
+ // Do not allow buggy trim() to bother usage
+ if((''+String.prototype.trim).indexOf("[native code]") == -1) {
+ console.warn('Replaced custom trim function with something known to work. Might break website.');
+ String.prototype.trim = function() {
+ return this.replace(/^\s+|\s+$/g, '');
+ }
+ }
+
+ }();
+
+ //
+ // Prepare our global namespace
+ //
+ void function() {
+ if(window.debugCSSUsage) console.log("STAGE: Building up namespace");
+ window.HtmlUsageResults = {
+ // this will contain all of the HTML tags used on a page
+ tags: {}, /*
+ tags ~= [nodeName] */
+
+ // this will contain all of the attributes used on an HTML tag
+ // and their values if they are in the whitelist
+ attributes: {} /*
+ attributes ~= {
+ name: , // The name of the attribute
+ tag: , // The tag that the attr was used on
+ value: // The value of the attr
+ } */
+ };
+
+ window.RecipeResults = {};
+ window.Recipes = {
+ recipes: []
+ };
+
+ window.CSSUsage = {};
+ window.CSSUsageResults = {
+
+ // this will contain the usage stats of various at-rules and rules
+ types: [ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, ], /*
+ types ~= {
+ "unknown":0, //0
+ "style":0, //1
+ "charset": 0, //2
+ "import":0, //3
+ "media":0, //4
+ "font-face":0, //5
+ "page":0, //6
+ "keyframes":0, //7 This is the @keyframe at rule
+ "keyframe":0, //8 This is the individual 0%, or from/to
+ "reserved9":0, //9
+ "namespace":0, //10
+ "reserved11":0,//11
+ "supports":0, //12
+ "reserved13":0,//13
+ "reserved14":0,//14
+ "viewport":0, //15
+ }*/
+
+ // this will contain the usage stats of various css properties and values
+ props: Object.create(null), /*
+ props ~= {
+ "background-color": {
+ count: 10,
+ values: {
+ "": 9,
+ "inherit": 1
+ }
+ }
+ }*/
+
+ // this will contains the various datapoints we measure on css selector usage
+ usages: {"SuccessfulCrawls":1},
+
+ // this will contain selectors and the properties they refer to
+ rules: {"@stylerule":0,"@atrule":0,"@inline":0}, /*
+ rules ~= {
+ "#id:hover .class": {
+ count: 10,
+ props: {
+ "background-color": 5,
+ "color": 4,
+ "opacity": 3,
+ "transform": 3
+ }
+ }
+ }*/
+
+ atrules: {}/*
+ atrules ~= {
+ "@atrule:4": {
+ count: 3,
+ props: {
+ "background-color": 1,
+ "color": 4,
+ "opacity": 3,
+ "transform": 3
+ },
+ nested: {
+ "h3": 1
+ },
+ conditions: {
+ "screen": 1
+ }
+ }
+ }*/
+
+
+ }
+ }();
+
+ //
+ // The StyleWalker API cover the extraction of style in the browser
+ //
+ void function() { "use strict";
+
+ CSSUsage.StyleWalker = {
+
+ // This array contains the list of functions being run on each CSSStyleDeclaration
+ // [ function(style, selectorText, matchedElements, ruleType) { ... }, ... ]
+ ruleAnalyzers: [],
+
+ // This array contains the list of functions being run on each DOM element of the page
+ // [ function(element) { ...} ]
+ elementAnalyzers: [],
+
+ recipesToRun: [],
+ runRecipes: false,
+
+ //
+ walkOverCssStyles: walkOverCssStyles,
+ walkOverDomElements: walkOverDomElements,
+
+ // Those stats are being collected while walking over the css style rules
+ amountOfInlineStyles: 0,
+ amountOfSelectorsUnused: 0,
+ amountOfSelectors: 0,
+ }
+
+ var hasWalkedDomElementsOnce = false;
+ // holds @keyframes temporarily while we wait to know how much they are used
+ var keyframes = Object.create(null);
+
+ /**
+ * For all stylesheets of the document,
+ * walk through the stylerules and run analyzers
+ */
+ function walkOverCssStyles() {
+ if(window.debugCSSUsage) console.log("STAGE: Walking over styles");
+ var styleSheets = document.styleSheets;
+
+ // Loop through StyeSheets
+ for (var ssIndex = styleSheets.length; ssIndex--;) {
+ var styleSheet = styleSheets[ssIndex];
+ try {
+ if(styleSheet.cssRules) {
+ walkOverCssRules(styleSheet.cssRules, styleSheet);
+ } else {
+ console.warn("No content loaded for stylesheet: ", styleSheet.href||styleSheet);
+ }
+ }
+ catch (e) {
+ if(window.debugCSSUsage) console.log(e, e.stack);
+ }
+ }
+
+ // Hack: rely on the results to find out which
+ // animations actually run, and parse their keyframes
+ var animations = (CSSUsageResults.props['animation-name']||{}).values||{};
+ for(var animation in keyframes) {
+ var keyframe = keyframes[animation];
+ var matchCount = animations[animation]|0;
+ var fakeElements = initArray(matchCount, (i)=>({tagName:'@keyframes '+animation+' ['+i+']'}));
+ processRule(keyframe, fakeElements);
+ }
+
+ }
+
+ /**
+ * This is the css work horse, this will will loop over the
+ * rules and then call the rule analyzers currently registered
+ */
+ function walkOverCssRules(/*CSSRuleList*/ cssRules, styleSheet, parentMatchedElements) {
+ if(window.debugCSSUsage) console.log("STAGE: Walking over rules");
+ for (var ruleIndex = cssRules.length; ruleIndex--;) {
+
+ // Loop through the rules
+ var rule = cssRules[ruleIndex];
+
+ // Until we can correlate animation usage
+ // to keyframes do not parse @keyframe rules
+ if(rule.type == 7) {
+ keyframes[rule.name] = rule;
+ continue;
+ }
+
+ // Filter "@supports" which the current browser doesn't support
+ if(rule.type == 12 && (!CSS.supports || !CSS.supports(rule.conditionText))) {
+ continue;
+ }
+
+ // Other rules should be processed immediately
+ processRule(rule,parentMatchedElements);
+ }
+ }
+
+
+ /**
+ * This function takes a css rule and:
+ * [1] walk over its child rules if needed
+ * [2] call rule analyzers for that rule if it has style data
+ */
+ function processRule(rule, parentMatchedElements) {
+ // Increment the rule type's counter
+ CSSUsageResults.types[rule.type|0]++;
+
+ // Some CssRules have nested rules to walk through:
+ if (rule.cssRules && rule.cssRules.length>0) {
+
+ walkOverCssRules(rule.cssRules, rule.parentStyleSheet, parentMatchedElements);
+
+ }
+
+ // Some CssRules have style we can analyze
+ if(rule.style) {
+ // find what the rule applies to
+ var selectorText;
+ var matchedElements;
+ if(rule.selectorText) {
+ selectorText = CSSUsage.PropertyValuesAnalyzer.cleanSelectorText(rule.selectorText);
+ try {
+ if(parentMatchedElements) {
+ matchedElements = [].slice.call(document.querySelectorAll(selectorText));
+ matchedElements.parentMatchedElements = parentMatchedElements;
+ } else {
+ matchedElements = [].slice.call(document.querySelectorAll(selectorText));
+ }
+ } catch(ex) {
+ matchedElements = [];
+ console.warn(ex.stack||("Invalid selector: "+selectorText+" -- via "+rule.selectorText));
+ }
+ } else {
+ selectorText = '@atrule:'+rule.type;
+ if(parentMatchedElements) {
+ matchedElements = parentMatchedElements;
+ } else {
+ matchedElements = [];
+ }
+ }
+
+ // run an analysis on it
+ runRuleAnalyzers(rule.style, selectorText, matchedElements, rule.type);
+ }
+
+ // run analysis on at rules to populate CSSUsageResults.atrules
+ if(isRuleAnAtRule(rule)) {
+ if(rule.conditionText) {
+ processConditionalAtRules(rule);
+ } else {
+ processGeneralAtRules(rule);
+ }
+ }
+ }
+
+
+ /**
+ * Checks whether an rule is an @atrule.
+ */
+ function isRuleAnAtRule(rule) {
+ /**
+ * @atrules types ~= {
+ "charset": 0, //2
+ "import":0, //3
+ "media":0, //4
+ "font-face":0, //5
+ "page":0, //6
+ "keyframes":0, //7 This is the @keyframe at rule
+ "keyframe":0, //8 This is the individual 0%, or from/to
+
+ "namespace":0, //10
+ "supports":0, //12
+ "viewport":0, //15
+ */
+ let type = rule.type;
+ return (type >= 2 && type <= 8) || (type == 10) || (type == 12) || (type == 15);
+ }
+
+
+ /**
+ * This process @atrules with conditional statements such as @supports.
+ * [1] It will process any props and values used within the body of the rule.
+ * [2] It will count the occurence of usage of nested atrules.
+ * [3] It will process condition statements to conform to a standardized version.
+ */
+ function processConditionalAtRules(rule) {
+ var selectorText = '@atrule:' + rule.type;
+ var atrulesUsage = CSSUsageResults.atrules;
+
+ if(!atrulesUsage[selectorText]) {
+ atrulesUsage[selectorText] = Object.create(null);
+ atrulesUsage[selectorText] = {"count": 1,
+ "props": {},
+ "conditions": {}}
+ } else {
+ var count = atrulesUsage[selectorText].count;
+ atrulesUsage[selectorText].count = count + 1;
+ }
+
+ var selectedAtruleUsage = atrulesUsage[selectorText];
+
+ if(rule.cssRules) {
+ CSSUsage.PropertyValuesAnalyzer.anaylzeStyleOfRulePropCount(rule, selectedAtruleUsage);
+ }
+
+ processConditionText(rule.conditionText, selectedAtruleUsage.conditions);
+ }
+
+ /**
+ * This processes the usage of conditions of conditional @atrules like @media.
+ * Requires the condition of the rule to process and the current recorded usage
+ * of the @atrule in question.
+ */
+ function processConditionText(conditionText, selectedAtruleConditionalUsage) {
+ // replace numeric specific information from condition statements
+ conditionText = CSSUsage.CSSValues.parseValues(conditionText);
+
+ if(!selectedAtruleConditionalUsage[conditionText]) {
+ selectedAtruleConditionalUsage[conditionText] = Object.create(null);
+ selectedAtruleConditionalUsage[conditionText] = {"count": 1}
+ } else {
+ var count = selectedAtruleConditionalUsage[conditionText].count;
+ selectedAtruleConditionalUsage[conditionText].count = count + 1;
+ }
+ }
+
+ /**
+ * This will process all other @atrules that don't have conditions or styles.
+ * [1] It will process any props and values used within the body of the rule.
+ * [2] It will count the occurence of usage of nested atrules.
+ */
+ function processGeneralAtRules(rule) {
+ var selectorText = '@atrule:' + rule.type;
+ var atrulesUsage = CSSUsageResults.atrules;
+
+ if(!atrulesUsage[selectorText]) {
+ atrulesUsage[selectorText] = Object.create(null);
+ atrulesUsage[selectorText] = {"count": 1,
+ "props": {}}
+ } else {
+ var count = atrulesUsage[selectorText].count;
+ atrulesUsage[selectorText].count = count + 1;
+ }
+
+ // @keyframes rule type is 7
+ if(rule.type == 7) {
+ processKeyframeAtRules(rule);
+ } else if(CSSUsageResults.rules[selectorText].props) {
+ atrulesUsage[selectorText].props = CSSUsageResults.rules[selectorText].props;
+ delete atrulesUsage[selectorText].props.values;
+ }
+ }
+
+ /**
+ * Processes on @keyframe to add the appropriate props from the frame and a counter of which
+ * frames are used throughout the document.
+ */
+ function processKeyframeAtRules(rule) {
+ var selectorText = '@atrule:' + rule.type;
+ var atrulesUsageForSelector = CSSUsageResults.atrules[selectorText];
+
+ if(!atrulesUsageForSelector["keyframes"]) {
+ atrulesUsageForSelector["keyframes"] = Object.create(null);
+ }
+
+ /**
+ * grab the props from the individual keyframe props that was already populated
+ * under CSSUsageResults.rules. Note: @atrule:8 is the individual frames.
+ * WARN: tightly coupled with previous processing of rules.
+ */
+ atrulesUsageForSelector.props = CSSUsageResults.rules["@atrule:8"].props;
+ delete atrulesUsageForSelector.props.values;
+
+ for(let index in rule.cssRules) {
+ let keyframe = rule.cssRules[index];
+ var atrulesUsageForKeyframeOfSelector = atrulesUsageForSelector.keyframes;
+
+ if(!keyframe.keyText) {
+ continue;
+ }
+
+ var frame = keyframe.keyText;
+
+ // replace extra whitespaces
+ frame = frame.replace(/\s/g, '');
+
+ if(!atrulesUsageForKeyframeOfSelector[frame]) {
+ atrulesUsageForKeyframeOfSelector[frame] = { "count" : 1 };
+ } else {
+ var keyframeCount = atrulesUsageForKeyframeOfSelector[frame].count;
+ atrulesUsageForKeyframeOfSelector[frame].count = keyframeCount + 1;
+ }
+ }
+ }
+
+
+ /**
+ * This is the dom work horse, this will will loop over the
+ * dom elements and then call the element analyzers currently registered,
+ * as well as rule analyzers for inline styles
+ */
+ function walkOverDomElements(obj, index) {
+ if(window.debugCSSUsage) console.log("STAGE: Walking over DOM elements");
+ var recipesToRun = CSSUsage.StyleWalker.recipesToRun;
+ obj = obj || document.documentElement; index = index|0;
+
+ // Loop through the elements
+ var elements = [].slice.call(document.all,0);
+ for(var i = 0; i < elements.length; i++) {
+ var element=elements[i];
+
+ // Analyze its style, if any
+ if(!CSSUsage.StyleWalker.runRecipes) {
+ // Analyze the element
+ runElementAnalyzers(element, index);
+
+ if (element.hasAttribute('style')) {
+ // Inline styles count like a style rule with no selector but one matched element
+ var ruleType = 1;
+ var isInline = true;
+ var selectorText = '@inline:'+element.tagName;
+ var matchedElements = [element];
+ runRuleAnalyzers(element.style, selectorText, matchedElements, ruleType, isInline);
+ }
+ } else { // We've already walked the DOM crawler and need to run the recipes
+ for(var r = 0; r < recipesToRun.length ; r++) {
+ var recipeToRun = recipesToRun[r];
+ var results = RecipeResults[recipeToRun.name] || (RecipeResults[recipeToRun.name]={});
+ recipeToRun(element, results, true);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Given a rule and its data, send it to all rule analyzers
+ */
+ function runRuleAnalyzers(style, selectorText, matchedElements, type, isInline) {
+
+ // Keep track of the counters
+ if(isInline) {
+ CSSUsage.StyleWalker.amountOfInlineStyles++;
+ } else {
+ CSSUsage.StyleWalker.amountOfSelectors++;
+ }
+
+ // Run all rule analyzers
+ for(var i = 0; i < CSSUsage.StyleWalker.ruleAnalyzers.length; i++) {
+ var runAnalyzer = CSSUsage.StyleWalker.ruleAnalyzers[i];
+ runAnalyzer(style, selectorText, matchedElements, type, isInline);
+ }
+
+ }
+
+ /**
+ * Given an element and its data, send it to all element analyzers
+ */
+ function runElementAnalyzers(element, index, depth) {
+ for(var i = 0; i < CSSUsage.StyleWalker.elementAnalyzers.length; i++) {
+ var runAnalyzer = CSSUsage.StyleWalker.elementAnalyzers[i];
+ runAnalyzer(element, index, depth);
+ }
+ }
+
+ /**
+ * Creates an array of "length" elements, by calling initializer for each cell
+ */
+ function initArray(length, initializer) {
+ var array = Array(length);
+ for(var i = length; i--;) {
+ array[i] = initializer(i);
+ }
+ return array;
+ }
+ }();
+
+ //
+ // helper to work with css values
+ //
+ void function() {
+
+ CSSUsage.CSSValues = {
+ createValueArray: createValueArray,
+ parseValues: parseValues,
+ normalizeValue: createValueArray
+ };
+
+ /**
+ * This will take a string value and reduce it down
+ * to only the aspects of the value we wish to keep
+ */
+ function parseValues(value,propertyName) {
+
+ // Trim value on the edges
+ value = value.trim();
+
+ // Normalize letter-casing
+ value = value.toLowerCase();
+
+ // Map colors to a standard value (eg: white, blue, yellow)
+ if (isKeywordColor(value)) { return ""; }
+ value = value.replace(/[#][0-9a-fA-F]+/g, '#xxyyzz');
+
+ // Escapce identifiers containing numbers
+ var numbers = ['ZERO','ONE','TWO','THREE','FOUR','FIVE','SIX','SEVEN','EIGHT','NINE'];
+ value = value.replace(
+ /([_a-z][-_a-z]|[_a-df-z])[0-9]+[-_a-z0-9]*/g,
+ s=>numbers.reduce(
+ (m,nstr,nint)=>m.replace(RegExp(nint,'g'),nstr),
+ s
+ )
+ );
+
+ // Remove any digits eg: 55px -> px, 1.5 -> 0.0, 1 -> 0
+ value = value.replace(/(?:[+]|[-]|)(?:(?:[0-9]+)(?:[.][0-9]+|)|(?:[.][0-9]+))(?:[e](?:[+]|[-]|)(?:[0-9]+))?(%|e[a-z]+|[a-df-z][a-z]*)/g, "$1");
+ value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)(?:[.][0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))?/g, " ");
+ value = value.replace(/(?:[+]|[-]|)(?:[.][0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))?/g, " ");
+ value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)(?:[e](?:[+]|[-]|)(?:[0-9]+))/g, " ");
+ value = value.replace(/(?:[+]|[-]|)(?:[0-9]+)/g, " ");
+
+ // Unescapce identifiers containing numbers
+ value = numbers.reduce(
+ (m,nstr,nint)=>m.replace(RegExp(nstr,'g'),nint),
+ value
+ )
+
+ // Remove quotes
+ value = value.replace(/('|‘|’|")/g, "");
+
+ //
+ switch(propertyName) {
+ case 'counter-increment':
+ case 'counter-reset':
+
+ // Anonymize the user identifier
+ value = value.replace(/[-_a-zA-Z0-9]+/g,' ');
+ break;
+
+ case 'grid':
+ case 'grid-template':
+ case 'grid-template-rows':
+ case 'grid-template-columns':
+ case 'grid-template-areas':
+
+ // Anonymize line names
+ value = value.replace(/\[[-_a-zA-Z0-9 ]+\]/g,' ');
+ break;
+
+ case '--var':
+
+ // Replace (...), {...} and [...]
+ value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " ");
+ value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " ");
+ value = value.replace(/\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]*)\])*\])*\])*\])*\]/g, " ");
+ value = value.replace(/\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]+|\[(?:[^()]*)\])*\])*\])*\])*\]/g, " ");
+ value = value.replace(/\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]*)\})*\})*\})*\})*\}/g, " ");
+ value = value.replace(/\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]+|\{(?:[^()]*)\})*\})*\})*\})*\}/g, " ");
+ break;
+
+ }
+
+ return value.trim();
+
+ }
+
+ //-----------------------------------------------------------------------------
+
+ /**
+ * This will transform a value into an array of value identifiers
+ */
+ function createValueArray(value, propertyName) {
+
+ // Trim value on the edges
+ value = value.trim();
+
+ // Normalize letter-casing
+ value = value.toLowerCase();
+
+ // Remove comments and !important
+ value = value.replace(/([/][*](?:.|\r|\n)*[*][/]|[!]important.*)/g,'');
+
+ // Do the right thing in function of the property
+ switch(propertyName) {
+ case 'font-family':
+
+ // Remove various quotes
+ if (value.indexOf("'") != -1 || value.indexOf("‘") != -1 || value.indexOf('"')) {
+ value = value.replace(/('|‘|’|")/g, "");
+ }
+
+ // Divide at commas to separate different font names
+ value = value.split(/\s*,\s*/g);
+ return value;
+
+ case '--var':
+
+ // Replace strings by dummies
+ value = value.replace(/"([^"\\]|\\[^"\\]|\\\\|\\")*"/g,' ')
+ value = value.replace(/'([^'\\]|\\[^'\\]|\\\\|\\')*'/g,' ');
+
+ // Replace url(...) functions by dummies
+ value = value.replace(/([a-z]?)[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "$1()");
+ value = value.replace(/([a-z]?)[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "$1()");
+
+ // Remove group contents (...), {...} and [...]
+ value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " ");
+ value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, " ");
+ value = value.replace(/[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]*)[}])*[}])*[}])*[}])*[}]/g, " ");
+ value = value.replace(/[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]+|[{](?:[^{}]*)[}])*[}])*[}])*[}])*[}]/g, " ");
+ value = value.replace(/[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]*)[\]])*[\]])*[\]])*[\]])*[\]]/g, " ");
+ value = value.replace(/[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]+|[\[](?:[^\[\]]*)[\]])*[\]])*[\]])*[\]])*[\]]/g, " ");
+
+ break;
+
+ default:
+
+ // Replace strings by dummies
+ value = value.replace(/"([^"\\]|\\[^"\\]|\\\\|\\")*"/g,' ')
+ .replace(/'([^'\\]|\\[^'\\]|\\\\|\\')*'/g,' ');
+
+ // Replace url(...) functions by dummies
+ if (value.indexOf("(") != -1) {
+ value = value.replace(/([a-z]?)[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "$1() ");
+ value = value.replace(/([a-z]?)[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "$1() ");
+ }
+
+ }
+
+ // Collapse whitespace
+ value = value.trim().replace(/\s+/g, " ");
+
+ // Divide at commas and spaces to separate different values
+ value = value.split(/\s*(?:,|[/])\s*|\s+/g);
+
+ return value;
+ }
+
+ /**
+ * So that we don't end up with a ton of color
+ * values, this will determine if the color is a
+ * keyword color value
+ */
+ function isKeywordColor(candidateColor) {
+
+ // Keyword colors from the W3C specs
+ var isColorKeyword = /^(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgrey|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|grey|green|greenyellow|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgreen|lightgray|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lighslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|navyblue|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)$/;
+ return isColorKeyword.test(candidateColor);
+
+ }
+
+ }();
+
+ //
+ // computes various css stats (PropertyValuesAnalyzer)
+ //
+ void function() {
+
+ CSSUsage.PropertyValuesAnalyzer = analyzeStyleOfRule;
+ CSSUsage.PropertyValuesAnalyzer.cleanSelectorText = cleanSelectorText;
+ CSSUsage.PropertyValuesAnalyzer.generalizedSelectorsOf = generalizedSelectorsOf;
+ CSSUsage.PropertyValuesAnalyzer.finalize = finalize;
+ CSSUsage.PropertyValuesAnalyzer.anaylzeStyleOfRulePropCount = anaylzeStyleOfRulePropCount;
+
+ // We put a computed style in cache for filtering purposes
+ var defaultStyle = getComputedStyle(document.createElement('div'));
+ // As well as some basic lies
+ var getBuggyValuesForThisBrowser = function() {
+ var buggyValues = getBuggyValuesForThisBrowser.cache;
+ if(buggyValues) { return buggyValues; }
+ else { buggyValues = Object.create(null); }
+
+ // Edge reports initial value instead of "initial", we have to be cautious
+ if(browserIsEdge) {
+
+ buggyValues['*'] = 1; // make 0 values automatic for longhand properties
+
+ //buggyValues['list-style-position:outside'] = 0;
+ buggyValues['list-style-image:none'] = 1;
+ //buggyValues['outline-color:invert'] = 0;
+ //buggyValues['outline-style:none'] = 0;
+ //buggyValues['outline-width:medium'] = 0;
+ //buggyValues['background-image:none'] = 0;
+ //buggyValues['background-attachment:scroll'] = 0;
+ //buggyValues['background-repeat:repeat'] = 0;
+ //buggyValues['background-repeat-x:repeat'] = 0;
+ //buggyValues['background-repeat-y:repeat'] = 0;
+ //buggyValues['background-position-x:0%'] = 0;
+ //buggyValues['background-position-y:0%'] = 0;
+ //buggyValues['background-size:auto'] = 0;
+ //buggyValues['background-origin:padding-box'] = 0;
+ //buggyValues['background-clip:border-box'] = 0;
+ //buggyValues['background-color:transparent'] = 0;
+ buggyValues['border-top-color:currentcolor'] = 1;
+ buggyValues['border-right-color:currentcolor'] = 1;
+ buggyValues['border-bottom-color:currentcolor'] = 1;
+ buggyValues['border-left-color:currentcolor'] = 1;
+ //buggyValues['border-top-style:solid'] = 0;
+ //buggyValues['border-right-style:solid'] = 0;
+ //buggyValues['border-bottom-style:solid'] = 0;
+ //buggyValues['border-left-style:solid'] = 0;
+ buggyValues['border-top-width:medium'] = 1;
+ buggyValues['border-right-width:medium'] = 1;
+ buggyValues['border-bottom-width:medium'] = 1;
+ buggyValues['border-left-width:medium'] = 1;
+ buggyValues['border-image-source:none'] = 1;
+ buggyValues['border-image-outset:0'] = 1;
+ buggyValues['border-image-width:1'] = 1;
+ buggyValues['border-image-repeat:repeat'] = 1;
+ buggyValues['border-image-repeat-x:repeat'] = 1;
+ buggyValues['border-image-repeat-y:repeat'] = 1;
+ buggyValues['line-height:normal'] = 1;
+ //buggyValues['font-size-adjust:none'] = 0;
+ buggyValues['font-stretch:normal'] = 1;
+
+ }
+
+ // Firefox reports initial values instead of "initial", we have to be cautious
+ if(browserIsFirefox) {
+
+ buggyValues['*'] = 1; // make 0 values automatic for longhand properties
+
+ }
+
+ // Attempt to force to optimize the object somehow
+ Object.create(buggyValues);
+
+ return getBuggyValuesForThisBrowser.cache = buggyValues;
+
+ };
+ var valueExistsInRootProperty = (cssText,key,rootKey,value) => {
+ value = value.trim().toLowerCase();
+
+ // detect suspicious values
+ var buggyValues = getBuggyValuesForThisBrowser();
+
+ // apply common sense to the given value, per browser
+ var buggyState = buggyValues[key+':'+value];
+ if(buggyState === 1) { return false; }
+ if(buggyState !== 0 && (!buggyValues['*'] || CSSShorthands.unexpand(key).length == 0)) { return true; }
+
+ // root properties are unlikely to lie
+ if(key==rootKey) return false;
+
+ // ask the browser is the best we can do right now
+ var values = value.split(/\s+|\s*,\s*/g);
+ var validValues = ' ';
+ var validValuesExtractor = new RegExp(' '+rootKey+'(?:[-][-_a-zA-Z0-9]+)?[:]([^;]*)','gi');
+ var match; while(match = validValuesExtractor.exec(cssText)) {
+ validValues += match[1] + ' ';
+ }
+ for(var i = 0; i < values.length; i++) {
+ var value = values[i];
+ if(validValues.indexOf(' '+value+' ')==-1) return false;
+ }
+ return true;
+
+ };
+
+ /** This will loop over the styles declarations */
+ function analyzeStyleOfRule(style, selectorText, matchedElements, type, isInline) { isInline=!!isInline;
+
+ // We want to filter rules that are not actually used
+ var count = matchedElements.length;
+ var selector = selectorText;
+ var selectorCat = {'1:true':'@inline','1:false':'@stylerule'}[''+type+':'+isInline]||'@atrule';
+
+ // Keep track of unused rules
+ var isRuleUnused = (count == 0);
+ if(isRuleUnused) {
+ CSSUsage.StyleWalker.amountOfSelectorsUnused++;
+ }
+
+ // We need a generalized selector to collect some stats
+ var generalizedSelectors = (
+ (selectorCat=='@stylerule')
+ ? [selectorCat].concat(generalizedSelectorsOf(selector))
+ : [selectorCat, selector]
+ );
+
+ // Get the datastores of the generalized selectors
+ var generalizedSelectorsData = map(generalizedSelectors, (generalizedSelector) => (
+ CSSUsageResults.rules[generalizedSelector] || (CSSUsageResults.rules[generalizedSelector] = {count:0,props:Object.create(null)})
+ ));
+
+ // Increment the occurence counter of found generalized selectors
+ for(var i = 0; i < generalizedSelectorsData.length; i++) {
+ var generalizedSelectorData = generalizedSelectorsData[i];
+ generalizedSelectorData.count++
+ }
+
+ // avoid most common browser lies
+ var cssText = ' ' + style.cssText.toLowerCase();
+ if(browserIsEdge) {
+ cssText = cssText.replace(/border: medium; border-image: none;/,'border: none;');
+ cssText = cssText.replace(/ border-image: none;/,' ');
+ }
+
+ // For each property declaration in this rule, we collect some stats
+ for (var i = style.length; i--;) {
+
+ var key = style[i], rootKeyIndex=key.indexOf('-'), rootKey = rootKeyIndex==-1 ? key : key.substr(0,rootKeyIndex);
+ var normalizedKey = rootKeyIndex==0&&key.indexOf('-',1)==1 ? '--var' : key;
+ var styleValue = style.getPropertyValue(key);
+
+ // Only keep styles that were declared by the author
+ // We need to make sure we're only checking string props
+ var isValueInvalid = typeof styleValue !== 'string' && styleValue != "" && styleValue != undefined;
+ if (isValueInvalid) {
+ continue;
+ }
+
+ var isPropertyUndefined = (cssText.indexOf(' '+key+':') == -1) && (styleValue=='initial' || !valueExistsInRootProperty(cssText, key, rootKey, styleValue));
+ if (isPropertyUndefined) {
+ continue;
+ }
+
+ // divide the value into simplified components
+ var specifiedValuesArray = CSSUsage.CSSValues.createValueArray(styleValue,normalizedKey);
+ var values = new Array();
+ for(var j = specifiedValuesArray.length; j--;) {
+ values.push(CSSUsage.CSSValues.parseValues(specifiedValuesArray[j],normalizedKey));
+ }
+
+ // log the property usage per selector
+ for(var gs = 0; gs < generalizedSelectorsData.length; gs++) {
+ var generalizedSelectorData = generalizedSelectorsData[gs];
+ // get the datastore for current property
+ var propStats = generalizedSelectorData.props[normalizedKey] || (generalizedSelectorData.props[normalizedKey] = {count:0,values:Object.create(null)});
+
+ // we saw the property one time
+ propStats.count++;
+
+ // we also saw a bunch of values
+ for(var v = 0; v < values.length; v++) {
+ var value = values[v];
+ // increment the counts for those by one, too
+ if(value.length>0) {
+ propStats.values[value] = (propStats.values[value]|0) + 1
+ }
+
+ }
+
+ }
+
+ // if we may increment some counts due to this declaration
+ if(count > 0) {
+
+ // instanciate or fetch the property metadata
+ var propObject = CSSUsageResults.props[normalizedKey];
+ if (!propObject) {
+ propObject = CSSUsageResults.props[normalizedKey] = {
+ count: 0,
+ values: Object.create(null)
+ };
+ }
+
+ // update the occurence counts of the property and value
+ for(var e = 0; e < matchedElements.length; e++) {
+ var element = matchedElements[e];
+
+ // check what the elements already contributed for this property
+ var cssUsageMeta = element.CSSUsage || (element.CSSUsage=Object.create(null));
+ var knownValues = cssUsageMeta[normalizedKey] || (cssUsageMeta[normalizedKey] = []);
+
+ // For recipes, at times we want to look at the specified values as well so hang
+ // these on the element so we don't have to recompute them
+ knownValues.valuesArray = knownValues.valuesArray || (knownValues.valuesArray = []);
+
+ for(var sv = 0; sv < specifiedValuesArray.length; sv++) {
+ var currentSV = specifiedValuesArray[sv];
+ if(knownValues.valuesArray.indexOf(currentSV) == -1) {
+ knownValues.valuesArray.push(currentSV)
+ }
+ }
+
+ // increment the amount of affected elements which we didn't count yet
+ if(knownValues.length == 0) { propObject.count += 1; }
+
+ // add newly found values too
+ for(var v = 0; v < values.length; v++) {
+ var value = values[v];
+ if(knownValues.indexOf(value) >= 0) { return; }
+ propObject.values[value] = (propObject.values[value]|0) + 1;
+ knownValues.push(value);
+ }
+
+ }
+
+ }
+
+ }
+ }
+
+ function anaylzeStyleOfRulePropCount(rule, selectedAtrulesUsage) {
+ for(let index in rule.cssRules) {
+ let ruleBody = rule.cssRules[index];
+ let style = ruleBody.style;
+
+ // guard for non css objects
+ if(!style) {
+ continue;
+ }
+
+ if(ruleBody.selector) {
+ try {
+ var selectorText = CssPropertyValuesAnalyzer.cleanSelectorText(ruleBody.selectorText);
+ var matchedElements = [].slice.call(document.querySelectorAll(selectorText));
+
+ if (matchedElements.length == 0) {
+ continue;
+ }
+ } catch (ex) {
+ console.warn(ex.stack||("Invalid selector: "+selectorText+" -- via "+ruleBody.selectorText));
+ continue;
+ }
+ }
+
+ let cssText = ' ' + style.cssText.toLowerCase();
+
+ for (var i = style.length; i--;) {
+ // processes out normalized prop name for style
+ var key = style[i], rootKeyIndex=key.indexOf('-'), rootKey = rootKeyIndex==-1 ? key : key.substr(0,rootKeyIndex);
+ var normalizedKey = rootKeyIndex==0&&key.indexOf('-',1)==1 ? '--var' : key;
+ var styleValue = style.getPropertyValue(key);
+
+ // Only keep styles that were declared by the author
+ // We need to make sure we're only checking string props
+ var isValueInvalid = typeof styleValue !== 'string' && styleValue != "" && styleValue != undefined;
+ if (isValueInvalid) {
+ continue;
+ }
+
+ var isPropertyUndefined = (cssText.indexOf(' '+key+':') == -1) && (styleValue=='initial' || !valueExistsInRootProperty(cssText, key, rootKey, styleValue));
+ if (isPropertyUndefined) {
+ continue;
+ }
+
+ var propsForSelectedAtrule = selectedAtrulesUsage.props;
+
+ if(!propsForSelectedAtrule[normalizedKey]) {
+ propsForSelectedAtrule[normalizedKey] = Object.create(null);
+ propsForSelectedAtrule[normalizedKey] = {"count": 1};
+ } else {
+ var propCount = propsForSelectedAtrule[normalizedKey].count;
+ propsForSelectedAtrule[normalizedKey].count = propCount + 1;
+ }
+ }
+ }
+ }
+
+ function finalize() {
+
+ // anonymize identifiers used for animation-name
+ function removeAnimationNames() {
+
+ // anonymize identifiers used for animation-name globally
+ if(CSSUsageResults.props["animation-name"]) {
+ CSSUsageResults.props["animation-name"].values = {"":CSSUsageResults.props["animation-name"].count};
+ }
+
+ // anonymize identifiers used for animation-name per selector
+ for(var selector in CSSUsageResults.rules) {
+ var rule = CSSUsageResults.rules[selector];
+ if(rule && rule.props && rule.props["animation-name"]) {
+ rule.props["animation-name"].values = {"":rule.props["animation-name"].count};
+ }
+ }
+
+ }
+
+ removeAnimationNames();
+ }
+
+ //-------------------------------------------------------------------------
+
+ /**
+ * If you try to do querySelectorAll on pseudo selectors
+ * it returns 0 because you are not actually doing the action the pseudo is stating those things,
+ * but we will honor those declarations and we don't want them to be missed,
+ * so we remove the pseudo selector from the selector text
+ */
+ function cleanSelectorText(text) {
+ if(text.indexOf(':') == -1) {
+ return text;
+ } else {
+ return text.replace(/([-_a-zA-Z0-9*\[\]]?):(?:hover|active|focus|before|after|not\(:(hover|active|focus)\))|::(?:before|after)/gi, '>>$1<<').replace(/(^| |>|\+|~)>><><<\)/g,'(*)').replace(/>>([-_a-zA-Z0-9*\[\]]?)<a.submenu" => "#id.class:hover > a.class"
+ */
+ function generalizedSelectorsOf(value) {
+
+ // Trim
+ value = value.trim();
+
+ // Collapse whitespace
+ if (value) {
+ value = value.replace(/\s+/g, " ");
+ }
+
+ // Remove (...)
+ if (value.indexOf("(") != -1) {
+ value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "");
+ value = value.replace(/[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]+|[(](?:[^()]*)[)])*[)])*[)])*[)])*[)]/g, "");
+ }
+
+ // Simplify "..." and '...'
+ value = value.replace(/"([^"\\]|\\[^"\\]|\\\\|\\")*"/g,'""')
+ value = value.replace(/'([^'\\]|\\[^'\\]|\\\\|\\')*'/g,"''");
+
+
+ // Simplify [att]
+ if (value.indexOf("[") != -1) {
+ value = value.replace(/\[[^=\[\]]+="([^"\\]|\\[^"\\]|\\\\|\\")*"\]/g, "[a]");
+ value = value.replace(/\[[^=\[\]]+='([^'\\]|\\[^'\\]|\\\\|\\')*'\]/g, "[a]");
+ value = value.replace(/\[[^\[\]]+\]/g, "[a]");
+ }
+
+ // Simplify .class
+ if (value.indexOf(".") != -1) {
+ value = value.replace(/[.][-_a-zA-Z][-_a-zA-Z0-9]*/g, ".c");
+ }
+
+ // Simplify #id
+ if (value.indexOf("#") != -1) {
+ value = value.replace(/[#][-_a-zA-Z][-_a-zA-Z0-9]*/g, "#i");
+ }
+
+ // Normalize combinators
+ value = value.replace(/[ ]*([>|+|~])[ ]*/g,' $1 ');
+
+ // Trim whitespace
+ value = value.trim();
+
+ // Remove unnecessary * to match Chrome
+ value = value.replace(/[*]([#.\x5B:])/g,'$1');
+
+ // Now we can sort components so that all browsers give results similar to Chrome
+ value = sortSelectorComponents(value)
+
+ // Split multiple selectors
+ value = value.split(/\s*,\s*/g);
+
+ return value;
+
+ }
+
+ var ID_REGEXP = "[#]i"; // #id
+ var CLASS_REGEXP = "[.]c"; // .class
+ var ATTR_REGEXP = "\\[a\\]"; // [att]
+ var PSEUDO_REGEXP = "[:][:]?[-_a-zA-Z][-_a-zA-Z0-9]*"; // :pseudo
+ var SORT_REGEXPS = [
+
+ // #id first
+ new RegExp("("+CLASS_REGEXP+")("+ID_REGEXP+")",'g'),
+ new RegExp("("+ATTR_REGEXP+")("+ID_REGEXP+")",'g'),
+ new RegExp("("+PSEUDO_REGEXP+")("+ID_REGEXP+")",'g'),
+
+ // .class second
+ new RegExp("("+ATTR_REGEXP+")("+CLASS_REGEXP+")",'g'),
+ new RegExp("("+PSEUDO_REGEXP+")("+CLASS_REGEXP+")",'g'),
+
+ // [attr] third
+ new RegExp("("+PSEUDO_REGEXP+")("+ATTR_REGEXP+")",'g'),
+
+ // :pseudo last
+
+ ];
+ function sortSelectorComponents(value) {
+
+ var oldValue; do { // Yeah this is a very inefficient bubble sort. I know.
+
+ oldValue = value;
+ for(var i = 0; i < SORT_REGEXPS.length; i++) {
+ var wrongPair = SORT_REGEXPS[i];
+ value = value.replace(wrongPair,'$2$1');
+ }
+
+ } while(oldValue != value); return value;
+
+ }
+
+ }();
+
+ //
+ // extracts valuable informations about selectors in use
+ //
+ void function() {
+
+ //
+ // To understand framework and general css usage, we collect stats about classes, ids and pseudos.
+ // Those objects have the following shape:
+ // {"hover":5,"active":1,"focus":2}
+ //
+ var cssPseudos = Object.create(null); // collect stats about which pseudo-classes and pseudo-elements are used in the css
+ var domClasses = Object.create(null); // collect stats about which css classes are found in the <... class> attributes of the dom
+ var cssClasses = Object.create(null); // collect stats about which css classes are used in the css
+ var domIds = Object.create(null); // collect stats about which ids are found in the <... id> attributes of the dom
+ var cssIds = Object.create(null); // collect stats about which ids are used in the css
+
+ //
+ // To understand Modernizer usage, we need to know how often some classes are used at the front of a selector
+ // While we're at it, the code also collect the state for ids
+ //
+ var cssLonelyIdGates = Object.create(null); // .class something-else ==> {"class":1}
+ var cssLonelyClassGates = Object.create(null); // #id something-else ==> {"id":1}
+ var cssLonelyClassGatesMatches = []; // .class something-else ==> [".class something-else"]
+ var cssLonelyIdGatesMatches = []; // #id something-else ==> ["#id something-else"]
+
+ //
+ // These regular expressions catch patterns we want to track (see before)
+ //
+ var ID_REGEXP = /[#][-_a-zA-Z][-_a-zA-Z0-9]*/g; // #id
+ var ID_REGEXP1 = /[#][-_a-zA-Z][-_a-zA-Z0-9]*/; // #id (only the first one)
+ var CLASS_REGEXP = /[.][-_a-zA-Z][-_a-zA-Z0-9]*/g; // .class
+ var CLASS_REGEXP1 = /[.][-_a-zA-Z][-_a-zA-Z0-9]*/; // .class (only the first one)
+ var PSEUDO_REGEXP = /[:][-_a-zA-Z][-_a-zA-Z0-9]*/g; // :pseudo (only the )
+ var GATEID_REGEXP = /^\s*[#][-_a-zA-Z][-_a-zA-Z0-9]*([.][-_a-zA-Z][-_a-zA-Z0-9]*|[:][-_a-zA-Z][-_a-zA-Z0-9]*)*\s+[^>+{, ][^{,]+$/; // #id ...
+ var GATECLASS_REGEXP = /^\s*[.][-_a-zA-Z][-_a-zA-Z0-9]*([:][-_a-zA-Z][-_a-zA-Z0-9]*)*\s+[^>+{, ][^{,]+$/; // .class ...
+
+ /**
+ * From a css selector text and a set of counters,
+ * increment the counters for the matches in the selector of the 'feature' regular expression
+ */
+ function extractFeature(feature, selector, counters) {
+ var instances = selector.match(feature)||[];
+ for(var i = 0; i < instances.length; i++) {
+ var instance = instances[i];
+ instance = instance.substr(1);
+ counters[instance] = (counters[instance]|0) + 1;
+ }
+ }
+
+ /**
+ * This analyzer will collect over the selectors the stats defined before
+ */
+ CSSUsage.SelectorAnalyzer = function parseSelector(style, selectorsText) {
+ if(typeof selectorsText != 'string') return;
+
+ var selectors = selectorsText.split(',');
+ for(var i = selectors.length; i--;) { var selector = selectors[i];
+
+ // extract all features from the selectors
+ extractFeature(ID_REGEXP, selector, cssIds);
+ extractFeature(CLASS_REGEXP, selector, cssClasses);
+ extractFeature(PSEUDO_REGEXP, selector, cssPseudos);
+
+ // detect specific selector patterns we're interested in
+ if(GATEID_REGEXP.test(selector)) {
+ cssLonelyIdGatesMatches.push(selector);
+ extractFeature(ID_REGEXP1, selector, cssLonelyIdGates);
+ }
+ if(GATECLASS_REGEXP.test(selector)) {
+ cssLonelyClassGatesMatches.push(selector);
+ extractFeature(CLASS_REGEXP1, selector, cssLonelyClassGates);
+ }
+ }
+
+ }
+
+ /**
+ * This analyzer will collect over the dom elements the stats defined before
+ */
+ CSSUsage.DOMClassAnalyzer = function(element) {
+
+ // collect classes used in the wild
+ if(element.className) {
+ var elementClasses = element.classList;
+ for(var cl = 0; cl < elementClasses.length; cl++) {
+ var c = elementClasses[cl];
+ domClasses[c] = (domClasses[c]|0) + 1;
+ }
+ }
+
+ // collect ids used in the wild
+ if(element.id) {
+ domIds[element.id] = (domIds[element.id]|0) + 1;
+ }
+
+ }
+
+ /**
+ * This function will be called when all stats have been collected
+ * at which point we will agregate some of them in useful values like Bootstrap usages, etc...
+ */
+ CSSUsage.SelectorAnalyzer.finalize = function() {
+
+ // get arrays of the classes/ids used ({"hover":5} => ["hover"]))
+ var domClassesArray = Object.keys(domClasses);
+ var cssClassesArray = Object.keys(cssClasses);
+ var domIdsArray = Object.keys(domIds);
+ var cssIdsArray = Object.keys(cssIds);
+
+ var results = {
+ // how many crawls are aggregated in this file (one of course in this case)
+ SuccessfulCrawls: 1,
+
+ // how many elements on the page (used to compute percentages for css.props)
+ DOMElements: document.all.length,
+
+ // how many selectors vs inline style, and other usage stats
+ SelectorsFound: CSSUsage.StyleWalker.amountOfSelectors,
+ InlineStylesFound: CSSUsage.StyleWalker.amountOfInlineStyles,
+ SelectorsUnused: CSSUsage.StyleWalker.amountOfSelectorsUnused,
+
+ // ids stats
+ IdsUsed: domIdsArray.length,
+ IdsRecognized: Object.keys(cssIds).length,
+ IdsUsedRecognized: filter(domIdsArray, i => cssIds[i]).length,
+
+ // classes stats
+ ClassesUsed: domClassesArray.length,
+ ClassesRecognized: Object.keys(cssClasses).length,
+ ClassesUsedRecognized: filter(domClassesArray, c => cssClasses[c]).length,
+ };
+
+ results = getFwkUsage(results, cssLonelyClassGates, domClasses, domIds, cssLonelyIdGates, cssClasses);
+ results = getPatternUsage(results, domClasses, cssClasses);
+
+ CSSUsageResults.usages = results;
+ deleteDuplicatedAtRules(); // TODO: issue #52
+
+ if(window.debugCSSUsage) if(window.debugCSSUsage) console.log(CSSUsageResults.usages);
+ }
+
+ /**
+ * Removes duplicated at rules data that was generated under CSSUsageResults.rules
+ * TODO: should not be using such a function, refer to issue #52
+ */
+ function deleteDuplicatedAtRules() {
+ var cssUsageRules = CSSUsageResults.rules;
+ var keys = Object.keys(cssUsageRules);
+
+ for(let key of keys) {
+ // only remove specific atrules
+ if (key.includes("atrule:")) {
+ delete cssUsageRules[key];
+ }
+ }
+
+ delete CSSUsageResults.atrules["@atrule:8"]; // delete duplicated data from atrule:7, keyframe
+ }
+ }();
+
+} catch (ex) { /* do something maybe */ throw ex; } }();
+
+/*
+ RECIPE: WebP
+ -------------------------------------------------------------
+ Author: ADYOUN
+ Description: Looking for WebP images
+*/
+
+function strcmp(a, b)
+{
+ if (a.length != b.length)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < a.length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void function() {
+ window.CSSUsage.StyleWalker.recipesToRun.push( function WebP( element, results) {
+ if (strcmp(element.nodeName, "IMG") || strcmp(element.nodeName, "SOURCE"))
+ {
+ var key = "webp";
+ var properties = [ "src", "srcset" ];
+ var extension = ".webp";
+
+ for (var i = 0; i < properties.length; i++)
+ {
+ var imgSrc = element[properties[i]];
+ if (imgSrc && imgSrc.length > extension.length && strcmp(imgSrc.substr(imgSrc.length - extension.length).toLowerCase(), extension))
+ {
+ results[key] = results[key] || { count: 0, };
+ results[key].count++;
+ }
+ }
+ }
+
+ 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('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:
+// This is where we decide what to run, and when
+//
+void function() {
+
+ var browserIsEdge = navigator.userAgent.indexOf('Edge')>=0;
+ var browserIsFirefox = navigator.userAgent.indexOf('Firefox')>=0;
+
+ if(document.readyState !== 'complete') {
+
+ // if the document is loading, run when it loads or in 10s, whichever is less
+ window.addEventListener('load', onready);
+ setTimeout(onready, 10000);
+
+ } else {
+
+ // if the document is ready, run now
+ onready();
+
+ }
+
+ /**
+ * This is the main entrypoint of our script
+ */
+ function onready() {
+
+ // Uncomment if you want to set breakpoints when running in the console
+ //debugger;
+
+ // Prevent this code from running multiple times
+ var firstTime = !onready.hasAlreadyRun; onready.hasAlreadyRun = true;
+ if(!firstTime) { return; /* for now... */ }
+
+ // Prevent this code from running when the page has no stylesheet (probably a redirect page)
+ if(document.styleSheets.length == 0) { return; }
+
+ // Check to see if you're on a Firefox failure page
+ if(document.styleSheets.length == 1 && browserIsFirefox) {
+ if(document.styleSheets[0].href !== null && document.styleSheets[0].href.indexOf('aboutNetError') != -1) {
+ return;
+ }
+ }
+
+ // Keep track of duration
+ var startTime = performance.now();
+
+ // register tools
+ CSSUsage.StyleWalker.ruleAnalyzers.push(CSSUsage.PropertyValuesAnalyzer);
+ CSSUsage.StyleWalker.ruleAnalyzers.push(CSSUsage.SelectorAnalyzer);
+ CSSUsage.StyleWalker.elementAnalyzers.push(CSSUsage.DOMClassAnalyzer);
+ CSSUsage.StyleWalker.elementAnalyzers.push(HtmlUsage.GetNodeName);
+
+ // perform analysis
+ CSSUsage.StyleWalker.walkOverDomElements();
+ CSSUsage.StyleWalker.walkOverCssStyles();
+ CSSUsage.PropertyValuesAnalyzer.finalize();
+ CSSUsage.SelectorAnalyzer.finalize();
+
+ // Walk over the dom elements again for Recipes
+ CSSUsage.StyleWalker.runRecipes = true;
+ CSSUsage.StyleWalker.walkOverDomElements();
+
+ // Update duration
+ CSSUsageResults.duration = (performance.now() - startTime)|0;
+
+ // DO SOMETHING WITH THE CSS OBJECT HERE
+ window.debugCSSUsage = false;
+ if(window.onCSSUsageResults) {
+ window.onCSSUsageResults(CSSUsageResults);
+ }
+ }
+}();
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/negative-background-position.js b/src/recipes/negative-background-position.js
new file mode 100644
index 0000000..f3aaea9
--- /dev/null
+++ b/src/recipes/negative-background-position.js
@@ -0,0 +1,272 @@
+/*
+ JSAPI RECIPE: WebGL Function Calls
+ -------------------------------------------------------------
+ Author: adyoun
+ Description: Counting the number of times each WebGL function is called
+*/
+
+window.functionList =
+[
+ ["activeTexture", 0],
+ ["attachShader", 0],
+ ["bindAttribLocation", 0],
+ ["bindBuffer", 0],
+ ["bindFramebuffer", 0],
+ ["bindRenderbuffer", 0],
+ ["bindTexture", 0],
+ ["blendColor", 0],
+ ["blendEquation", 0],
+ ["blendEquationSeparate", 0],
+ ["blendFunc", 0],
+ ["blendFuncSeparate", 0],
+ ["bufferData", 0],
+ ["bufferSubData", 0],
+ ["checkFramebufferStatus", 0],
+ ["clear", 0],
+ ["clearColor", 0],
+ ["clearDepth", 0],
+ ["clearStencil", 0],
+ ["colorMask", 0],
+ ["compileShader", 0],
+ ["copyTexImage2D", 0],
+ ["copyTexSubImage2D", 0],
+ ["createBuffer", 0],
+ ["createByteArray", 0],
+ ["createFloatArray", 0],
+ ["createFramebuffer", 0],
+ ["createIntArray", 0],
+ ["createProgram", 0],
+ ["createRenderbuffer", 0],
+ ["createShader", 0],
+ ["createShortArray", 0],
+ ["createTexture", 0],
+ ["createUnsignedByteArray", 0],
+ ["createUnsignedIntArray", 0],
+ ["createUnsignedShortArray", 0],
+ ["cullFace", 0],
+ ["deleteBuffer", 0],
+ ["deleteFramebuffer", 0],
+ ["deleteProgram", 0],
+ ["deleteRenderbuffer", 0],
+ ["deleteShader", 0],
+ ["deleteTexture", 0],
+ ["depthFunc", 0],
+ ["depthMask", 0],
+ ["depthRange", 0],
+ ["detachShader", 0],
+ ["disable", 0],
+ ["disableVertexAttribArray", 0],
+ ["drawArrays", 0],
+ ["drawElements", 0],
+ ["enable", 0],
+ ["enableVertexAttribArray", 0],
+ ["finish", 0],
+ ["flush", 0],
+ ["framebufferRenderbuffer", 0],
+ ["framebufferTexture2D", 0],
+ ["frontFace", 0],
+ ["generateMipmap", 0],
+ ["getActiveAttrib", 0],
+ ["getActiveUniform", 0],
+ ["getAttachedShaders", 0],
+ ["getAttribLocation", 0],
+ ["getBufferParameter", 0],
+ ["getError", 0],
+ ["getFramebufferAttachmentParameter", 0],
+ ["getProgramInfoLog", 0],
+ ["getProgramParameter", 0],
+ ["getRenderbufferParameter", 0],
+ ["getShaderInfoLog", 0],
+ ["getShaderParameter", 0],
+ ["getShaderSource", 0],
+ ["getTexParameter", 0],
+ ["getUniform", 0],
+ ["getUniformLocation", 0],
+ ["getVertexAttrib", 0],
+ ["isBuffer", 0],
+ ["isBuffergetParameter", 0],
+ ["isFramebuffer", 0],
+ ["isProgram", 0],
+ ["isRenderbuffer", 0],
+ ["isShader", 0],
+ ["isTexture", 0],
+ ["lineWidth", 0],
+ ["linkProgram", 0],
+ ["pixelStorei", 0],
+ ["polygonOffset", 0],
+ ["readPixels", 0],
+ ["renderbufferStorage", 0],
+ ["sampleCoverage", 0],
+ ["scissor", 0],
+ ["shaderSource", 0],
+ ["stencilFunc", 0],
+ ["stencilFuncSeparate", 0],
+ ["stencilMask", 0],
+ ["stencilMaskSeparate", 0],
+ ["stencilOp", 0],
+ ["stencilOpSeparate", 0],
+ ["texImage2D", 0],
+ ["texParameterf", 0],
+ ["texParameteri", 0],
+ ["texSubImage2D", 0],
+ ["uniform1f", 0],
+ ["uniform1fv", 0],
+ ["uniform1i", 0],
+ ["uniform1iv", 0],
+ ["uniform2f", 0],
+ ["uniform2fv", 0],
+ ["uniform2i", 0],
+ ["uniform2iv", 0],
+ ["uniform3f", 0],
+ ["uniform3fv", 0],
+ ["uniform3i", 0],
+ ["uniform3iv", 0],
+ ["uniform4f", 0],
+ ["uniform4fv", 0],
+ ["uniform4i", 0],
+ ["uniform4iv", 0],
+ ["uniformMatrix2fv", 0],
+ ["uniformMatrix3fv", 0],
+ ["uniformMatrix4fv", 0],
+ ["useProgram", 0],
+ ["validateProgram", 0],
+ ["vertexAttrib1f", 0],
+ ["vertexAttrib1fv", 0],
+ ["vertexAttrib2f", 0],
+ ["vertexAttrib2fv", 0],
+ ["vertexAttrib3f", 0],
+ ["vertexAttrib3fv", 0],
+ ["vertexAttrib4f", 0],
+ ["vertexAttrib4fv", 0],
+ ["vertexAttribPointer", 0],
+ ["viewport", 0]
+];
+
+var functionMap = new Map();
+
+for (var i = 0; i < window.functionList.length; i++)
+{
+
+ WebGLRenderingContext.prototype["_" + window.functionList[i][0]] = WebGLRenderingContext.prototype[window.functionList[i][0]];
+
+ WebGLRenderingContext.prototype[window.functionList[i][0]] = function() {
+ var index = functionMap.get(arguments.callee);
+ window.functionList[index][1] = window.functionList[index][1] + 1;
+
+ switch(arguments.length)
+ {
+ case 0:
+ return this["_" + window.functionList[index][0]]();
+ case 1:
+ return this["_" + window.functionList[index][0]](arguments[0]);
+ case 2:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1]);
+ case 3:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2]);
+ case 4:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3]);
+ case 5:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
+ case 6:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
+ case 7:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]);
+ case 8:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]);
+ case 9:
+ return this["_" + window.functionList[index][0]](arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8]);
+ }
+ }
+
+ functionMap.set(WebGLRenderingContext.prototype[window.functionList[i][0]], i);
+}
+
+window.getContextCounter = 0;
+
+HTMLCanvasElement.prototype._getContext = HTMLCanvasElement.prototype.getContext;
+HTMLCanvasElement.prototype.getContext = function (a) {
+ var lowerCase = a.toLowerCase();
+ if (lowerCase.includes("webgl"))
+ {
+ window.getContextCounter = window.getContextCounter + 1;
+ }
+ return this._getContext(a);
+};
+
+
+var getParameterMap = new Map();
+window.getParameterCounter = 0;
+WebGLRenderingContext.prototype._getParameter = WebGLRenderingContext.prototype.getParameter;
+WebGLRenderingContext.prototype.getParameter = function (a) {
+ if (getParameterMap.get(a) != undefined)
+ {
+ getParameterMap.set(a, getParameterMap.get(a) + 1);
+ }
+ else
+ {
+ getParameterMap.set(a, 1);
+ }
+
+ window.getParameterCounter = window.getParameterCounter + 1
+
+ return this._getParameter(a);
+}
+
+void function () {
+ document.addEventListener('DOMContentLoaded', function () {
+ console.log("Logging");
+ var results = {};
+ var recipeName = "WebGL_Function_Counter";
+ results[recipeName] = results[recipeName] || { href: location.href, };
+
+ for (var i = 0; i < window.functionList.length; i++)
+ {
+ results[recipeName][window.functionList[i][0]] = results[recipeName][window.functionList[i][0]] || {count: window.functionList[i][1]};
+ }
+
+ /*
+ var k = Object.keys(getParameterMap);
+ for (var i = 0; i < k.length; i++) {
+ results[recipeName]["getParameterParameter" + k[i]] = results[recipeName]["getParameterParameter" + k[i]] || {count: getParameterMap.get(k[i])};
+ }
+ */
+
+ /*
+ for (const key in getParameterMap) {
+ results[recipeName]["getParameterParameter" + key] = results[recipeName]["getParameterParameter" + key] || {count: getParameterMap.get(key)};
+ }
+ */
+
+ for (var [key, value] of getParameterMap) {
+ results[recipeName]["getParameterParameter" + key] = results[recipeName]["getParameterParameter" + key] || {count: value};
+ }
+
+ results[recipeName]["getContext"] = results[recipeName]["getContext"] || {count: window.getContextCounter};
+ results[recipeName]["getParameter"] = results[recipeName]["getParameter"] || {count: window.getParameterCounter};
+
+ appendResults(results);
+
+ // Add it to the document dom
+ function appendResults(results) {
+ if (window.debugCSSUsage) console.log("Trying to append");
+ var output = document.createElement('script');
+ output.id = "css-usage-tsv-results";
+ output.textContent = JSON.stringify(results);
+ output.type = 'text/plain';
+ document.querySelector('head').appendChild(output);
+ var successfulAppend = checkAppend();
+ }
+
+ function checkAppend() {
+ 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");
+ }
+ else {
+ if (window.debugCSSUsage) console.log("Element successfully found");
+ }
+ }
+
+ });
+}();
diff --git a/tests/recipes/negative-background-position.html b/tests/recipes/negative-background-position.html
new file mode 100644
index 0000000..c558f73
--- /dev/null
+++ b/tests/recipes/negative-background-position.html
@@ -0,0 +1,23 @@
+
+
+
+ Negative Background Position Test
+
+
+
+
+
+
+
Negative Background Position Test
+
+
+
+
\ No newline at end of file