UNPKG

utility2

Version:

this zero-dependency package will provide high-level functions to to build, test, and deploy webapps

1,457 lines (1,340 loc) 571 kB
#!/usr/bin/env node /* * lib.jslint.js (2020.11.3) * https://github.com/kaizhu256/node-jslint-lite * this zero-dependency package will provide browser-compatible versions of jslint (v2020.7.2) and csslint (v2018.2.25), with working web-demo * */ /* istanbul instrument in package jslint */ // assets.utility2.header.js - start /* jslint utility2:true */ /* istanbul ignore next */ // run shared js-env code - init-local (function () { "use strict"; let isBrowser; let isWebWorker; let local; // polyfill globalThis if (!(typeof globalThis === "object" && globalThis)) { if (typeof window === "object" && window && window.window === window) { window.globalThis = window; } if (typeof global === "object" && global && global.global === global) { global.globalThis = global; } } // init debugInline if (!globalThis.debugInline) { let consoleError; consoleError = console.error; globalThis.debugInline = function (...argList) { /* * this function will both print <argList> to stderr * and return <argList>[0] */ consoleError("\n\ndebugInline"); consoleError(...argList); consoleError("\n"); return argList[0]; }; } // init isBrowser isBrowser = ( typeof globalThis.XMLHttpRequest === "function" && globalThis.navigator && typeof globalThis.navigator.userAgent === "string" ); // init isWebWorker isWebWorker = ( isBrowser && typeof globalThis.importScripts === "function" ); // init function function objectDeepCopyWithKeysSorted(obj) { /* * this function will recursively deep-copy <obj> with keys sorted */ let sorted; if (typeof obj !== "object" || !obj) { return obj; } // recursively deep-copy list with child-keys sorted if (Array.isArray(obj)) { return obj.map(objectDeepCopyWithKeysSorted); } // recursively deep-copy obj with keys sorted sorted = {}; Object.keys(obj).sort().forEach(function (key) { sorted[key] = objectDeepCopyWithKeysSorted(obj[key]); }); return sorted; } function assertJsonEqual(aa, bb) { /* * this function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>) */ aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa)); bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb)); if (aa !== bb) { throw new Error(JSON.stringify(aa) + " !== " + JSON.stringify(bb)); } } function assertOrThrow(passed, msg) { /* * this function will throw <msg> if <passed> is falsy */ if (passed) { return; } throw ( ( msg && typeof msg.message === "string" && typeof msg.stack === "string" ) // if msg is err, then leave as is ? msg : new Error( typeof msg === "string" // if msg is string, then leave as is ? msg // else JSON.stringify(msg) : JSON.stringify(msg, undefined, 4) ) ); } function coalesce(...argList) { /* * this function will coalesce null, undefined, or "" in <argList> */ let arg; let ii; ii = 0; while (ii < argList.length) { arg = argList[ii]; if (arg !== undefined && arg !== null && arg !== "") { return arg; } ii += 1; } return arg; } function identity(val) { /* * this function will return <val> */ return val; } function noop() { /* * this function will do nothing */ return; } function objectAssignDefault(tgt = {}, src = {}, depth = 0) { /* * this function will if items from <tgt> are null, undefined, or "", * then overwrite them with items from <src> */ let recurse; recurse = function (tgt, src, depth) { Object.entries(src).forEach(function ([ key, bb ]) { let aa; aa = tgt[key]; if (aa === undefined || aa === null || aa === "") { tgt[key] = bb; return; } if ( depth !== 0 && typeof aa === "object" && aa && !Array.isArray(aa) && typeof bb === "object" && bb && !Array.isArray(bb) ) { recurse(aa, bb, depth - 1); } }); }; recurse(tgt, src, depth | 0); return tgt; } function onErrorThrow(err) { /* * this function will throw <err> if exists */ if (err) { throw err; } } // bug-workaround - throw unhandledRejections in node-process if ( typeof process === "object" && process && typeof process.on === "function" && process.unhandledRejections !== "strict" ) { process.unhandledRejections = "strict"; process.on("unhandledRejection", function (err) { throw err; }); } // init local local = { assertJsonEqual, assertOrThrow, coalesce, identity, isBrowser, isWebWorker, local, noop, objectAssignDefault, objectDeepCopyWithKeysSorted, onErrorThrow }; globalThis.globalLocal = local; }()); // assets.utility2.header.js - end (function (local) { "use strict"; /* istanbul ignore next */ // run shared js-env code - init-before (function () { // init local local = ( globalThis.utility2_rollup // || globalThis.utility2_rollup_old // || require("./assets.utility2.rollup.js") || globalThis.globalLocal ); // init exports if (local.isBrowser) { globalThis.utility2_jslint = local; } else { module.exports = local; module.exports.__dirname = __dirname; } // init lib main local.jslint = local; /* validateLineSortedReset */ local.cliRun = function ({ rgxComment }) { /* * this function will run cli */ let cliDict; cliDict = local.cliDict; cliDict._eval = cliDict._eval || function () { /* * <code> * will eval <code> */ globalThis.local = local; require("vm").runInThisContext(process.argv[3]); }; cliDict._help = cliDict._help || function () { /* * * will print help */ let commandList; let file; let packageJson; let str; let strDict; commandList = [ { argList: "<arg2> ...", description: "usage:", command: [ "<arg1>" ] }, { argList: "'console.log(\"hello world\")'", description: "example:", command: [ "--eval" ] } ]; file = __filename.replace(( /.*\// ), ""); packageJson = require("./package.json"); // validate comment rgxComment = rgxComment || ( /\)\u0020\{\n(?:|\u0020{4})\/\*\n(?:\u0020|\u0020{5})\*((?:\u0020<[^>]*?>|\u0020\.\.\.)*?)\n(?:\u0020|\u0020{5})\*\u0020(will\u0020.*?\S)\n(?:\u0020|\u0020{5})\*\/\n(?:\u0020{4}|\u0020{8})\S/ ); strDict = {}; Object.keys(cliDict).sort().forEach(function (key, ii) { if (key[0] === "_" && key !== "_default") { return; } str = String(cliDict[key]); if (key === "_default") { key = ""; } strDict[str] = strDict[str] || (ii + 2); ii = strDict[str]; if (commandList[ii]) { commandList[ii].command.push(key); return; } commandList[ii] = rgxComment.exec(str); local.assertOrThrow(commandList[ii], ( "cliRun - cannot parse comment in COMMAND " + key + ":\nnew RegExp(" + JSON.stringify(rgxComment.source) + ").exec(" + JSON.stringify(str).replace(( /\\\\/g ), "\u0000").replace(( /\\n/g ), "\\n\\\n").replace(( /\u0000/g ), "\\\\") + ");" )); commandList[ii] = { argList: local.coalesce(commandList[ii][1], "").trim(), command: [ key ], description: commandList[ii][2] }; }); str = ""; str += packageJson.name + " (" + packageJson.version + ")\n\n"; str += commandList.filter(function (elem) { return elem; }).map(function (elem, ii) { elem.command = elem.command.filter(function (elem) { return elem; }); switch (ii) { case 0: case 1: elem.argList = [ elem.argList ]; break; default: elem.argList = elem.argList.split(" "); elem.description = ( "# COMMAND " + (elem.command[0] || "<none>") + "\n# " + elem.description ); } return ( elem.description + "\n " + file + " " + elem.command.sort().join("|") + " " + elem.argList.join(" ") ); }).join("\n\n"); console.log(str); }; cliDict["--eval"] = cliDict["--eval"] || cliDict._eval; cliDict["--help"] = cliDict["--help"] || cliDict._help; cliDict["-e"] = cliDict["-e"] || cliDict._eval; cliDict["-h"] = cliDict["-h"] || cliDict._help; cliDict._default = cliDict._default || cliDict._help; cliDict.help = cliDict.help || cliDict._help; cliDict._interactive = cliDict._interactive || function () { /* * * will start interactive-mode */ globalThis.local = local; local.identity(local.replStart || require("repl").start)({ useGlobal: true }); }; cliDict["--interactive"] = cliDict["--interactive"] || cliDict._interactive; cliDict["-i"] = cliDict["-i"] || cliDict._interactive; cliDict._version = cliDict._version || function () { /* * * will print version */ console.log(require(__dirname + "/package.json").version); }; cliDict["--version"] = cliDict["--version"] || cliDict._version; cliDict["-v"] = cliDict["-v"] || cliDict._version; // default to --help command if no arguments are given if (process.argv.length <= 2) { cliDict._help(); return; } if (cliDict[process.argv[2]]) { cliDict[process.argv[2]](); return; } cliDict._default(); }; }()); // run shared js-env code - function (function () { /* jslint ignore:start */ /* repo https://github.com/CSSLint/csslint/tree/e8aeeda06c928636e21428e09b1af93f66621209 committed 2018-02-25T11:28:16Z */ /* file https://github.com/CSSLint/csslint/blob/e8aeeda06c928636e21428e09b1af93f66621209/dist/csslint.js */ /*! CSSLint v1.0.5 Copyright (c) 2017 Nicole Sullivan and Nicholas C. Zakas. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* istanbul ignore next */ var CSSLint = (function(){ var module = module || {}, exports = exports || {}; /*! Parser-Lib Copyright (c) 2009-2016 Nicholas C. Zakas. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Version v1.1.0, Build time: 6-December-2016 10:31:29 */ var parserlib = (function () { var require; require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ "use strict"; /* exported Colors */ var Colors = module.exports = { __proto__ :null, aliceblue :"#f0f8ff", antiquewhite :"#faebd7", aqua :"#00ffff", aquamarine :"#7fffd4", azure :"#f0ffff", beige :"#f5f5dc", bisque :"#ffe4c4", black :"#000000", blanchedalmond :"#ffebcd", blue :"#0000ff", blueviolet :"#8a2be2", brown :"#a52a2a", burlywood :"#deb887", cadetblue :"#5f9ea0", chartreuse :"#7fff00", chocolate :"#d2691e", coral :"#ff7f50", cornflowerblue :"#6495ed", cornsilk :"#fff8dc", crimson :"#dc143c", cyan :"#00ffff", darkblue :"#00008b", darkcyan :"#008b8b", darkgoldenrod :"#b8860b", darkgray :"#a9a9a9", darkgrey :"#a9a9a9", darkgreen :"#006400", darkkhaki :"#bdb76b", darkmagenta :"#8b008b", darkolivegreen :"#556b2f", darkorange :"#ff8c00", darkorchid :"#9932cc", darkred :"#8b0000", darksalmon :"#e9967a", darkseagreen :"#8fbc8f", darkslateblue :"#483d8b", darkslategray :"#2f4f4f", darkslategrey :"#2f4f4f", darkturquoise :"#00ced1", darkviolet :"#9400d3", deeppink :"#ff1493", deepskyblue :"#00bfff", dimgray :"#696969", dimgrey :"#696969", dodgerblue :"#1e90ff", firebrick :"#b22222", floralwhite :"#fffaf0", forestgreen :"#228b22", fuchsia :"#ff00ff", gainsboro :"#dcdcdc", ghostwhite :"#f8f8ff", gold :"#ffd700", goldenrod :"#daa520", gray :"#808080", grey :"#808080", green :"#008000", greenyellow :"#adff2f", honeydew :"#f0fff0", hotpink :"#ff69b4", indianred :"#cd5c5c", indigo :"#4b0082", ivory :"#fffff0", khaki :"#f0e68c", lavender :"#e6e6fa", lavenderblush :"#fff0f5", lawngreen :"#7cfc00", lemonchiffon :"#fffacd", lightblue :"#add8e6", lightcoral :"#f08080", lightcyan :"#e0ffff", lightgoldenrodyellow :"#fafad2", lightgray :"#d3d3d3", lightgrey :"#d3d3d3", lightgreen :"#90ee90", lightpink :"#ffb6c1", lightsalmon :"#ffa07a", lightseagreen :"#20b2aa", lightskyblue :"#87cefa", lightslategray :"#778899", lightslategrey :"#778899", lightsteelblue :"#b0c4de", lightyellow :"#ffffe0", lime :"#00ff00", limegreen :"#32cd32", linen :"#faf0e6", magenta :"#ff00ff", maroon :"#800000", mediumaquamarine:"#66cdaa", mediumblue :"#0000cd", mediumorchid :"#ba55d3", mediumpurple :"#9370d8", mediumseagreen :"#3cb371", mediumslateblue :"#7b68ee", mediumspringgreen :"#00fa9a", mediumturquoise :"#48d1cc", mediumvioletred :"#c71585", midnightblue :"#191970", mintcream :"#f5fffa", mistyrose :"#ffe4e1", moccasin :"#ffe4b5", navajowhite :"#ffdead", navy :"#000080", oldlace :"#fdf5e6", olive :"#808000", olivedrab :"#6b8e23", orange :"#ffa500", orangered :"#ff4500", orchid :"#da70d6", palegoldenrod :"#eee8aa", palegreen :"#98fb98", paleturquoise :"#afeeee", palevioletred :"#d87093", papayawhip :"#ffefd5", peachpuff :"#ffdab9", peru :"#cd853f", pink :"#ffc0cb", plum :"#dda0dd", powderblue :"#b0e0e6", purple :"#800080", red :"#ff0000", rosybrown :"#bc8f8f", royalblue :"#4169e1", saddlebrown :"#8b4513", salmon :"#fa8072", sandybrown :"#f4a460", seagreen :"#2e8b57", seashell :"#fff5ee", sienna :"#a0522d", silver :"#c0c0c0", skyblue :"#87ceeb", slateblue :"#6a5acd", slategray :"#708090", slategrey :"#708090", snow :"#fffafa", springgreen :"#00ff7f", steelblue :"#4682b4", tan :"#d2b48c", teal :"#008080", thistle :"#d8bfd8", tomato :"#ff6347", turquoise :"#40e0d0", violet :"#ee82ee", wheat :"#f5deb3", white :"#ffffff", whitesmoke :"#f5f5f5", yellow :"#ffff00", yellowgreen :"#9acd32", //'currentColor' color keyword https://www.w3.org/TR/css3-color/#currentcolor currentColor :"The value of the 'color' property.", //CSS2 system colors https://www.w3.org/TR/css3-color/#css2-system activeBorder :"Active window border.", activecaption :"Active window caption.", appworkspace :"Background color of multiple document interface.", background :"Desktop background.", buttonface :"The face background color for 3-D elements that appear 3-D due to one layer of surrounding border.", buttonhighlight :"The color of the border facing the light source for 3-D elements that appear 3-D due to one layer of surrounding border.", buttonshadow :"The color of the border away from the light source for 3-D elements that appear 3-D due to one layer of surrounding border.", buttontext :"Text on push buttons.", captiontext :"Text in caption, size box, and scrollbar arrow box.", graytext :"Grayed (disabled) text. This color is set to #000 if the current display driver does not support a solid gray color.", greytext :"Greyed (disabled) text. This color is set to #000 if the current display driver does not support a solid grey color.", highlight :"Item(s) selected in a control.", highlighttext :"Text of item(s) selected in a control.", inactiveborder :"Inactive window border.", inactivecaption :"Inactive window caption.", inactivecaptiontext :"Color of text in an inactive caption.", infobackground :"Background color for tooltip controls.", infotext :"Text color for tooltip controls.", menu :"Menu background.", menutext :"Text in menus.", scrollbar :"Scroll bar gray area.", threeddarkshadow :"The color of the darker (generally outer) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", threedface :"The face background color for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", threedhighlight :"The color of the lighter (generally outer) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", threedlightshadow :"The color of the darker (generally inner) of the two borders facing the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", threedshadow :"The color of the lighter (generally inner) of the two borders away from the light source for 3-D elements that appear 3-D due to two concentric layers of surrounding border.", window :"Window background.", windowframe :"Window frame.", windowtext :"Text in windows." }; },{}],2:[function(require,module,exports){ "use strict"; module.exports = Combinator; var SyntaxUnit = require("../util/SyntaxUnit"); var Parser = require("./Parser"); /** * Represents a selector combinator (whitespace, +, >). * @namespace parserlib.css * @class Combinator * @extends parserlib.util.SyntaxUnit * @constructor * @param {String} text The text representation of the unit. * @param {int} line The line of text on which the unit resides. * @param {int} col The column of text on which the unit resides. */ function Combinator(text, line, col) { SyntaxUnit.call(this, text, line, col, Parser.COMBINATOR_TYPE); /** * The type of modifier. * @type String * @property type */ this.type = "unknown"; //pretty simple if (/^\s+$/.test(text)) { this.type = "descendant"; } else if (text === ">") { this.type = "child"; } else if (text === "+") { this.type = "adjacent-sibling"; } else if (text === "~") { this.type = "sibling"; } } Combinator.prototype = new SyntaxUnit(); Combinator.prototype.constructor = Combinator; },{"../util/SyntaxUnit":26,"./Parser":6}],3:[function(require,module,exports){ "use strict"; module.exports = Matcher; var StringReader = require("../util/StringReader"); var SyntaxError = require("../util/SyntaxError"); /** * This class implements a combinator library for matcher functions. * The combinators are described at: * https://developer.mozilla.org/en-US/docs/Web/CSS/Value_definition_syntax#Component_value_combinators */ function Matcher(matchFunc, toString) { this.match = function(expression) { // Save/restore marks to ensure that failed matches always restore // the original location in the expression. var result; expression.mark(); result = matchFunc(expression); if (result) { expression.drop(); } else { expression.restore(); } return result; }; this.toString = typeof toString === "function" ? toString : function() { return toString; }; } /** Precedence table of combinators. */ Matcher.prec = { MOD: 5, SEQ: 4, ANDAND: 3, OROR: 2, ALT: 1 }; /** Simple recursive-descent grammar to build matchers from strings. */ Matcher.parse = function(str) { var reader, eat, expr, oror, andand, seq, mod, term, result; reader = new StringReader(str); eat = function(matcher) { var result = reader.readMatch(matcher); if (result === null) { throw new SyntaxError( "Expected "+matcher, reader.getLine(), reader.getCol()); } return result; }; expr = function() { // expr = oror (" | " oror)* var m = [ oror() ]; while (reader.readMatch(" | ") !== null) { m.push(oror()); } return m.length === 1 ? m[0] : Matcher.alt.apply(Matcher, m); }; oror = function() { // oror = andand ( " || " andand)* var m = [ andand() ]; while (reader.readMatch(" || ") !== null) { m.push(andand()); } return m.length === 1 ? m[0] : Matcher.oror.apply(Matcher, m); }; andand = function() { // andand = seq ( " && " seq)* var m = [ seq() ]; while (reader.readMatch(" && ") !== null) { m.push(seq()); } return m.length === 1 ? m[0] : Matcher.andand.apply(Matcher, m); }; seq = function() { // seq = mod ( " " mod)* var m = [ mod() ]; while (reader.readMatch(/^ (?![&|\]])/) !== null) { m.push(mod()); } return m.length === 1 ? m[0] : Matcher.seq.apply(Matcher, m); }; mod = function() { // mod = term ( "?" | "*" | "+" | "#" | "{<num>,<num>}" )? var m = term(); if (reader.readMatch("?") !== null) { return m.question(); } else if (reader.readMatch("*") !== null) { return m.star(); } else if (reader.readMatch("+") !== null) { return m.plus(); } else if (reader.readMatch("#") !== null) { return m.hash(); } else if (reader.readMatch(/^\{\s*/) !== null) { var min = eat(/^\d+/); eat(/^\s*,\s*/); var max = eat(/^\d+/); eat(/^\s*\}/); return m.braces(+min, +max); } return m; }; term = function() { // term = <nt> | literal | "[ " expression " ]" if (reader.readMatch("[ ") !== null) { var m = expr(); eat(" ]"); return m; } return Matcher.fromType(eat(/^[^ ?*+#{]+/)); }; result = expr(); if (!reader.eof()) { throw new SyntaxError( "Expected end of string", reader.getLine(), reader.getCol()); } return result; }; /** * Convert a string to a matcher (parsing simple alternations), * or do nothing if the argument is already a matcher. */ Matcher.cast = function(m) { if (m instanceof Matcher) { return m; } return Matcher.parse(m); }; /** * Create a matcher for a single type. */ Matcher.fromType = function(type) { // Late require of ValidationTypes to break a dependency cycle. var ValidationTypes = require("./ValidationTypes"); return new Matcher(function(expression) { return expression.hasNext() && ValidationTypes.isType(expression, type); }, type); }; /** * Create a matcher for one or more juxtaposed words, which all must * occur, in the given order. */ Matcher.seq = function() { var ms = Array.prototype.slice.call(arguments).map(Matcher.cast); if (ms.length === 1) { return ms[0]; } return new Matcher(function(expression) { var i, result = true; for (i = 0; result && i < ms.length; i++) { result = ms[i].match(expression); } return result; }, function(prec) { var p = Matcher.prec.SEQ; var s = ms.map(function(m) { return m.toString(p); }).join(" "); if (prec > p) { s = "[ " + s + " ]"; } return s; }); }; /** * Create a matcher for one or more alternatives, where exactly one * must occur. */ Matcher.alt = function() { var ms = Array.prototype.slice.call(arguments).map(Matcher.cast); if (ms.length === 1) { return ms[0]; } return new Matcher(function(expression) { var i, result = false; for (i = 0; !result && i < ms.length; i++) { result = ms[i].match(expression); } return result; }, function(prec) { var p = Matcher.prec.ALT; var s = ms.map(function(m) { return m.toString(p); }).join(" | "); if (prec > p) { s = "[ " + s + " ]"; } return s; }); }; /** * Create a matcher for two or more options. This implements the * double bar (||) and double ampersand (&&) operators, as well as * variants of && where some of the alternatives are optional. * This will backtrack through even successful matches to try to * maximize the number of items matched. */ Matcher.many = function(required) { var ms = Array.prototype.slice.call(arguments, 1).reduce(function(acc, v) { if (v.expand) { // Insert all of the options for the given complex rule as // individual options. var ValidationTypes = require("./ValidationTypes"); acc.push.apply(acc, ValidationTypes.complex[v.expand].options); } else { acc.push(Matcher.cast(v)); } return acc; }, []); if (required === true) { required = ms.map(function() { return true; }); } var result = new Matcher(function(expression) { var seen = [], max = 0, pass = 0; var success = function(matchCount) { if (pass === 0) { max = Math.max(matchCount, max); return matchCount === ms.length; } else { return matchCount === max; } }; var tryMatch = function(matchCount) { for (var i = 0; i < ms.length; i++) { if (seen[i]) { continue; } expression.mark(); if (ms[i].match(expression)) { seen[i] = true; // Increase matchCount iff this was a required element // (or if all the elements are optional) if (tryMatch(matchCount + ((required === false || required[i]) ? 1 : 0))) { expression.drop(); return true; } // Backtrack: try *not* matching using this rule, and // let's see if it leads to a better overall match. expression.restore(); seen[i] = false; } else { expression.drop(); } } return success(matchCount); }; if (!tryMatch(0)) { // Couldn't get a complete match, retrace our steps to make the // match with the maximum # of required elements. pass++; tryMatch(0); } if (required === false) { return max > 0; } // Use finer-grained specification of which matchers are required. for (var i = 0; i < ms.length; i++) { if (required[i] && !seen[i]) { return false; } } return true; }, function(prec) { var p = required === false ? Matcher.prec.OROR : Matcher.prec.ANDAND; var s = ms.map(function(m, i) { if (required !== false && !required[i]) { return m.toString(Matcher.prec.MOD) + "?"; } return m.toString(p); }).join(required === false ? " || " : " && "); if (prec > p) { s = "[ " + s + " ]"; } return s; }); result.options = ms; return result; }; /** * Create a matcher for two or more options, where all options are * mandatory but they may appear in any order. */ Matcher.andand = function() { var args = Array.prototype.slice.call(arguments); args.unshift(true); return Matcher.many.apply(Matcher, args); }; /** * Create a matcher for two or more options, where options are * optional and may appear in any order, but at least one must be * present. */ Matcher.oror = function() { var args = Array.prototype.slice.call(arguments); args.unshift(false); return Matcher.many.apply(Matcher, args); }; /** Instance methods on Matchers. */ Matcher.prototype = { constructor: Matcher, // These are expected to be overridden in every instance. match: function() { throw new Error("unimplemented"); }, toString: function() { throw new Error("unimplemented"); }, // This returns a standalone function to do the matching. func: function() { return this.match.bind(this); }, // Basic combinators then: function(m) { return Matcher.seq(this, m); }, or: function(m) { return Matcher.alt(this, m); }, andand: function(m) { return Matcher.many(true, this, m); }, oror: function(m) { return Matcher.many(false, this, m); }, // Component value multipliers star: function() { return this.braces(0, Infinity, "*"); }, plus: function() { return this.braces(1, Infinity, "+"); }, question: function() { return this.braces(0, 1, "?"); }, hash: function() { return this.braces(1, Infinity, "#", Matcher.cast(",")); }, braces: function(min, max, marker, optSep) { var m1 = this, m2 = optSep ? optSep.then(this) : this; if (!marker) { marker = "{" + min + "," + max + "}"; } return new Matcher(function(expression) { var result = true, i; for (i = 0; i < max; i++) { if (i > 0 && optSep) { result = m2.match(expression); } else { result = m1.match(expression); } if (!result) { break; } } return i >= min; }, function() { return m1.toString(Matcher.prec.MOD) + marker; }); } }; },{"../util/StringReader":24,"../util/SyntaxError":25,"./ValidationTypes":21}],4:[function(require,module,exports){ "use strict"; module.exports = MediaFeature; var SyntaxUnit = require("../util/SyntaxUnit"); var Parser = require("./Parser"); /** * Represents a media feature, such as max-width:500. * @namespace parserlib.css * @class MediaFeature * @extends parserlib.util.SyntaxUnit * @constructor * @param {SyntaxUnit} name The name of the feature. * @param {SyntaxUnit} value The value of the feature or null if none. */ function MediaFeature(name, value) { SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol, Parser.MEDIA_FEATURE_TYPE); /** * The name of the media feature * @type String * @property name */ this.name = name; /** * The value for the feature or null if there is none. * @type SyntaxUnit * @property value */ this.value = value; } MediaFeature.prototype = new SyntaxUnit(); MediaFeature.prototype.constructor = MediaFeature; },{"../util/SyntaxUnit":26,"./Parser":6}],5:[function(require,module,exports){ "use strict"; module.exports = MediaQuery; var SyntaxUnit = require("../util/SyntaxUnit"); var Parser = require("./Parser"); /** * Represents an individual media query. * @namespace parserlib.css * @class MediaQuery * @extends parserlib.util.SyntaxUnit * @constructor * @param {String} modifier The modifier "not" or "only" (or null). * @param {String} mediaType The type of media (i.e., "print"). * @param {Array} parts Array of selectors parts making up this selector. * @param {int} line The line of text on which the unit resides. * @param {int} col The column of text on which the unit resides. */ function MediaQuery(modifier, mediaType, features, line, col) { SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType : "") + (mediaType && features.length > 0 ? " and " : "") + features.join(" and "), line, col, Parser.MEDIA_QUERY_TYPE); /** * The media modifier ("not" or "only") * @type String * @property modifier */ this.modifier = modifier; /** * The mediaType (i.e., "print") * @type String * @property mediaType */ this.mediaType = mediaType; /** * The parts that make up the selector. * @type Array * @property features */ this.features = features; } MediaQuery.prototype = new SyntaxUnit(); MediaQuery.prototype.constructor = MediaQuery; },{"../util/SyntaxUnit":26,"./Parser":6}],6:[function(require,module,exports){ "use strict"; module.exports = Parser; var EventTarget = require("../util/EventTarget"); var SyntaxError = require("../util/SyntaxError"); var SyntaxUnit = require("../util/SyntaxUnit"); var Combinator = require("./Combinator"); var MediaFeature = require("./MediaFeature"); var MediaQuery = require("./MediaQuery"); var PropertyName = require("./PropertyName"); var PropertyValue = require("./PropertyValue"); var PropertyValuePart = require("./PropertyValuePart"); var Selector = require("./Selector"); var SelectorPart = require("./SelectorPart"); var SelectorSubPart = require("./SelectorSubPart"); var TokenStream = require("./TokenStream"); var Tokens = require("./Tokens"); var Validation = require("./Validation"); /** * A CSS3 parser. * @namespace parserlib.css * @class Parser * @constructor * @param {Object} options (Optional) Various options for the parser: * starHack (true|false) to allow IE6 star hack as valid, * underscoreHack (true|false) to interpret leading underscores * as IE6-7 targeting for known properties, ieFilters (true|false) * to indicate that IE < 8 filters should be accepted and not throw * syntax errors. */ function Parser(options) { //inherit event functionality EventTarget.call(this); this.options = options || {}; this._tokenStream = null; } //Static constants Parser.DEFAULT_TYPE = 0; Parser.COMBINATOR_TYPE = 1; Parser.MEDIA_FEATURE_TYPE = 2; Parser.MEDIA_QUERY_TYPE = 3; Parser.PROPERTY_NAME_TYPE = 4; Parser.PROPERTY_VALUE_TYPE = 5; Parser.PROPERTY_VALUE_PART_TYPE = 6; Parser.SELECTOR_TYPE = 7; Parser.SELECTOR_PART_TYPE = 8; Parser.SELECTOR_SUB_PART_TYPE = 9; Parser.prototype = function() { var proto = new EventTarget(), //new prototype prop, additions = { __proto__: null, //restore constructor constructor: Parser, //instance constants - yuck DEFAULT_TYPE : 0, COMBINATOR_TYPE : 1, MEDIA_FEATURE_TYPE : 2, MEDIA_QUERY_TYPE : 3, PROPERTY_NAME_TYPE : 4, PROPERTY_VALUE_TYPE : 5, PROPERTY_VALUE_PART_TYPE : 6, SELECTOR_TYPE : 7, SELECTOR_PART_TYPE : 8, SELECTOR_SUB_PART_TYPE : 9, //----------------------------------------------------------------- // Grammar //----------------------------------------------------------------- _stylesheet: function() { /* * stylesheet * : [ CHARSET_SYM S* STRING S* ';' ]? * [S|CDO|CDC]* [ import [S|CDO|CDC]* ]* * [ namespace [S|CDO|CDC]* ]* * [ [ ruleset | media | page | font_face | keyframes_rule | supports_rule ] [S|CDO|CDC]* ]* * ; */ var tokenStream = this._tokenStream, count, token, tt; this.fire("startstylesheet"); //try to read character set this._charset(); this._skipCruft(); //try to read imports - may be more than one while (tokenStream.peek() === Tokens.IMPORT_SYM) { this._import(); this._skipCruft(); } //try to read namespaces - may be more than one while (tokenStream.peek() === Tokens.NAMESPACE_SYM) { this._namespace(); this._skipCruft(); } //get the next token tt = tokenStream.peek(); //try to read the rest while (tt > Tokens.EOF) { try { switch (tt) { case Tokens.MEDIA_SYM: this._media(); this._skipCruft(); break; case Tokens.PAGE_SYM: this._page(); this._skipCruft(); break; case Tokens.FONT_FACE_SYM: this._font_face(); this._skipCruft(); break; case Tokens.KEYFRAMES_SYM: this._keyframes(); this._skipCruft(); break; case Tokens.VIEWPORT_SYM: this._viewport(); this._skipCruft(); break; case Tokens.DOCUMENT_SYM: this._document(); this._skipCruft(); break; case Tokens.SUPPORTS_SYM: this._supports(); this._skipCruft(); break; case Tokens.UNKNOWN_SYM: //unknown @ rule tokenStream.get(); if (!this.options.strict) { //fire error event this.fire({ type: "error", error: null, message: "Unknown @ rule: " + tokenStream.LT(0).value + ".", line: tokenStream.LT(0).startLine, col: tokenStream.LT(0).startCol }); //skip braces count=0; while (tokenStream.advance([Tokens.LBRACE, Tokens.RBRACE]) === Tokens.LBRACE) { count++; //keep track of nesting depth } while (count) { tokenStream.advance([Tokens.RBRACE]); count--; } } else { //not a syntax error, rethrow it throw new SyntaxError("Unknown @ rule.", tokenStream.LT(0).startLine, tokenStream.LT(0).startCol); } break; case Tokens.S: this._readWhitespace(); break; default: if (!this._ruleset()) { //error handling for known issues switch (tt) { case Tokens.CHARSET_SYM: token = tokenStream.LT(1); this._charset(false); throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol); case Tokens.IMPORT_SYM: token = tokenStream.LT(1); this._import(false); throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol); case Tokens.NAMESPACE_SYM: token = tokenStream.LT(1); this._namespace(false); throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol); default: tokenStream.get(); //get the last token this._unexpectedToken(tokenStream.token()); } } } } catch (ex) { if (ex instanceof SyntaxError && !this.options.strict) { this.fire({ type: "error", error: ex, message: ex.message, line: ex.line, col: ex.col }); } else { throw ex; } } tt = tokenStream.peek(); } if (tt !== Tokens.EOF) { this._unexpectedToken(tokenStream.token()); } this.fire("endstylesheet"); }, _charset: function(emit) { var tokenStream = this._tokenStream, charset, token, line, col; if (tokenStream.match(Tokens.CHARSET_SYM)) { line = tokenStream.token().startLine; col = tokenStream.token().startCol; this._readWhitespace(); tokenStream.mustMatch(Tokens.STRING); token = tokenStream.token(); charset = token.value; this._readWhitespace(); tokenStream.mustMatch(Tokens.SEMICOLON); if (emit !== false) { this.fire({ type: "charset", charset:charset, line: line, col: col }); } } }, _import: function(emit) { /* * import * : IMPORT_SYM S* * [STRING|URI] S* media_query_list? ';' S* */ var tokenStream = this._tokenStream, uri, importToken, mediaList = []; //read import symbol tokenStream.mustMatch(Tokens.IMPORT_SYM); importToken = tokenStream.token(); this._readWhitespace(); tokenStream.mustMatch([Tokens.STRING, Tokens.URI]); //grab the URI value uri = tokenStream.token().value.replace(/^(?:url\()?["']?([^"']+?)["']?\)?$/, "$1"); this._readWhitespace(); mediaList = this._media_query_list(); //must end with a semicolon tokenStream.mustMatch(Tokens.SEMICOLON); this._readWhitespace(); if (emit !== false) { this.fire({ type: "import", uri: uri, media: mediaList, line: importToken.startLine, col: importToken.startCol }); } }, _namespace: function(emit) { /* * namespace * : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S* */ var tokenStream = this._tokenStream, line, col, prefix, uri; //read import symbol tokenStream.mustMatch(Tokens.NAMESPACE_SYM); line = tokenStream.token().startLine; col = tokenStream.token().startCol; this._readWhitespace(); //it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT if (tokenStream.match(Tokens.IDENT)) { prefix = tokenStream.token().value; this._readWhitespace(); } tokenStream.mustMatch([Tokens.STRING, Tokens.URI]); /*if (!tokenStre