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
JavaScript
#!/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