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

+
+

Here's some text

+
+
+ + \ No newline at end of file