This commit is contained in:
2022-07-18 02:50:52 +00:00
parent befd344ab0
commit 06181b34d6
8569 changed files with 818704 additions and 352705 deletions
+11
View File
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+221
View File
@@ -0,0 +1,221 @@
# css-select [![NPM version](http://img.shields.io/npm/v/css-select.svg)](https://npmjs.org/package/css-select) [![Build Status](https://travis-ci.org/fb55/css-select.svg?branch=master)](http://travis-ci.org/fb55/css-select) [![Downloads](https://img.shields.io/npm/dm/css-select.svg)](https://npmjs.org/package/css-select) [![Coverage](https://coveralls.io/repos/fb55/css-select/badge.svg?branch=master)](https://coveralls.io/r/fb55/css-select)
a CSS selector compiler/engine
## What?
css-select turns CSS selectors into functions that tests if elements match them. When searching for elements, testing is executed "from the top", similar to how browsers execute CSS selectors.
In its default configuration, css-select queries the DOM structure of the [`domhandler`](https://github.com/fb55/domhandler) module (also known as htmlparser2 DOM).
It uses [`domutils`](https://github.com/fb55/domutils) as its default adapter over the DOM structure. See Options below for details on querying alternative DOM structures.
__Features:__
- Full implementation of CSS3 selectors
- Partial implementation of jQuery/Sizzle extensions
- Very high test coverage
- Pretty good performance
## Why?
The traditional approach of executing CSS selectors, named left-to-right execution, is to execute every component of the selector in order, from left to right _(duh)_. The execution of the selector `a b` for example will first query for `a` elements, then search these for `b` elements. (That's the approach of eg. [`Sizzle`](https://github.com/jquery/sizzle), [`nwmatcher`](https://github.com/dperini/nwmatcher/) and [`qwery`](https://github.com/ded/qwery).)
While this works, it has some downsides: Children of `a`s will be checked multiple times; first, to check if they are also `a`s, then, for every superior `a` once, if they are `b`s. Using [Big O notation](http://en.wikipedia.org/wiki/Big_O_notation), that would be `O(n^(k+1))`, where `k` is the number of descendant selectors (that's the space in the example above).
The far more efficient approach is to first look for `b` elements, then check if they have superior `a` elements: Using big O notation again, that would be `O(n)`. That's called right-to-left execution.
And that's what css-select does and why it's quite performant.
## How does it work?
By building a stack of functions.
_Wait, what?_
Okay, so let's suppose we want to compile the selector `a b` again, for right-to-left execution. We start by _parsing_ the selector, which means we turn the selector into an array of the building-blocks of the selector, so we can distinguish them easily. That's what the [`css-what`](https://github.com/fb55/css-what) module is for, if you want to have a look.
Anyway, after parsing, we end up with an array like this one:
```js
[
{ type: 'tag', name: 'a' },
{ type: 'descendant' },
{ type: 'tag', name: 'b' }
]
```
Actually, this array is wrapped in another array, but that's another story (involving commas in selectors).
Now that we know the meaning of every part of the selector, we can compile it. That's where it becomes interesting.
The basic idea is to turn every part of the selector into a function, which takes an element as its only argument. The function checks whether a passed element matches its part of the selector: If it does, the element is passed to the next turned-into-a-function part of the selector, which does the same. If an element is accepted by all parts of the selector, it _matches_ the selector and double rainbow ALL THE WAY.
As said before, we want to do right-to-left execution with all the big O improvements nonsense, so elements are passed from the rightmost part of the selector (`b` in our example) to the leftmost (~~which would be `c`~~ of course `a`).
_//TODO: More in-depth description. Implementation details. Build a spaceship._
## API
```js
const CSSselect = require("css-select");
```
__Note:__ css-select throws errors when invalid selectors are passed to it, contrary to the behavior in browsers, which swallow them. This is done to aid with writing css selectors, but can be unexpected when processing arbitrary strings.
#### `CSSselect(query, elems, options)`
Queries `elems`, returns an array containing all matches.
- `query` can be either a CSS selector or a function.
- `elems` can be either an array of elements, or a single element. If it is an element, its children will be queried.
- `options` is described below.
Aliases: `CSSselect.selectAll(query, elems)`, `CSSselect.iterate(query, elems)`.
#### `CSSselect.compile(query)`
Compiles the query, returns a function.
#### `CSSselect.is(elem, query, options)`
Tests whether or not an element is matched by `query`. `query` can be either a CSS selector or a function.
#### `CSSselect.selectOne(query, elems, options)`
Arguments are the same as for `CSSselect(query, elems)`. Only returns the first match, or `null` if there was no match.
### Options
- `xmlMode`: When enabled, tag names will be case-sensitive. Default: `false`.
- `strict`: Limits the module to only use CSS3 selectors. Default: `false`.
- `rootFunc`: The last function in the stack, will be called with the last element that's looked at. Should return `true`.
- `adapter`: The adapter to use when interacting with the backing DOM structure. By default it uses [`domutils`](https://github.com/fb55/domutils).
#### Custom Adapters
A custom adapter must implement the following functions:
```
isTag, existsOne, getAttributeValue, getChildren, getName, getParent,
getSiblings, getText, hasAttrib, removeSubsets, findAll, findOne
```
The method signature notation used below should be fairly intuitive - if not,
see the [`rtype`](https://github.com/ericelliott/rtype) or
[`TypeScript`](https://www.typescriptlang.org/) docs, as it is very similar to
both of those. You may also want to look at
-[`domutils`](https://github.com/fb55/domutils) to see the default
-implementation, or at
-[`css-select-browser-adapter`](https://github.com/nrkn/css-select-browser-adapter/blob/master/index.js)
-for an implementation backed by the DOM.
```ts
{
// is the node a tag?
isTag: ( node:Node ) => isTag:Boolean,
// does at least one of passed element nodes pass the test predicate?
existsOne: ( test:Predicate, elems:[ElementNode] ) => existsOne:Boolean,
// get the attribute value
getAttributeValue: ( elem:ElementNode, name:String ) => value:String,
// get the node's children
getChildren: ( node:Node ) => children:[Node],
// get the name of the tag
getName: ( elem:ElementNode ) => tagName:String,
// get the parent of the node
getParent: ( node:Node ) => parentNode:Node,
/*
get the siblings of the node. Note that unlike jQuery's `siblings` method,
this is expected to include the current node as well
*/
getSiblings: ( node:Node ) => siblings:[Node],
// get the text content of the node, and its children if it has any
getText: ( node:Node ) => text:String,
// does the element have the named attribute?
hasAttrib: ( elem:ElementNode, name:String ) => hasAttrib:Boolean,
// takes an array of nodes, and removes any duplicates, as well as any nodes
// whose ancestors are also in the array
removeSubsets: ( nodes:[Node] ) => unique:[Node],
// finds all of the element nodes in the array that match the test predicate,
// as well as any of their children that match it
findAll: ( test:Predicate, nodes:[Node] ) => elems:[ElementNode],
// finds the first node in the array that matches the test predicate, or one
// of its children
findOne: ( test:Predicate, elems:[ElementNode] ) => findOne:ElementNode,
/*
The adapter can also optionally include an equals method, if your DOM
structure needs a custom equality test to compare two objects which refer
to the same underlying node. If not provided, `css-select` will fall back to
`a === b`.
*/
equals: ( a:Node, b:Node ) => Boolean
}
```
## Supported selectors
_As defined by CSS 4 and / or jQuery._
* Universal (`*`)
* Tag (`<tagname>`)
* Descendant (` `)
* Child (`>`)
* Parent (`<`) *
* Sibling (`+`)
* Adjacent (`~`)
* Attribute (`[attr=foo]`), with supported comparisons:
* `[attr]` (existential)
* `=`
* `~=`
* `|=`
* `*=`
* `^=`
* `$=`
* `!=` *
* Also, `i` can be added after the comparison to make the comparison case-insensitive (eg. `[attr=foo i]`) *
* Pseudos:
* `:not`
* `:contains` *
* `:icontains` * (case-insensitive version of `:contains`)
* `:has` *
* `:root`
* `:empty`
* `:parent` *
* `:[first|last]-child[-of-type]`
* `:only-of-type`, `:only-child`
* `:nth-[last-]child[-of-type]`
* `:link`
* `:visited`, `:hover`, `:active` * (these depend on optional Adapter methods, so these will work only if implemented in Adapter)
* `:selected` *, `:checked`
* `:enabled`, `:disabled`
* `:required`, `:optional`
* `:header`, `:button`, `:input`, `:text`, `:checkbox`, `:file`, `:password`, `:reset`, `:radio` etc. *
* `:matches` *
__*__: Not part of CSS3
---
License: BSD-2-Clause
## Security contact information
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.
## `css-select` for enterprise
Available as part of the Tidelift Subscription
The maintainers of `css-select` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-css-select?utm_source=npm-css-select&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
+219
View File
@@ -0,0 +1,219 @@
export = CSSselect;
/**
* Alias for CSSselect.selectAll(query, elems, options).
* @see [CSSselect.compile] for supported selector queries.
*/
declare function CSSselect<Node, ElementNode extends Node>(
query: CSSselect.Query,
elems: Array<ElementNode> | ElementNode,
options?: CSSselect.Options<Node, ElementNode>
): Array<ElementNode>;
declare namespace CSSselect {
type Predicate<Value> = (v: Value) => boolean;
interface Adapter<Node, ElementNode extends Node> {
/**
* is the node a tag?
*/
isTag(node: Node): node is ElementNode;
/**
* Does at least one of passed element nodes pass the test predicate?
*/
existsOne(test: Predicate<ElementNode>, elems: Array<ElementNode>): boolean;
/**
* get the attribute value.
*/
getAttributeValue(elem: ElementNode, name: string): string;
/**
* get the node's children
*/
getChildren(node: Node): Array<Node>;
/**
* get the name of the tag
*/
getName(elem: ElementNode): string;
/**
* get the parent of the node
*/
getParent(node: Node): Node;
/*
Get the siblings of the node. Note that unlike jQuery's `siblings` method,
this is expected to include the current node as well
*/
getSiblings(node: Node): Array<Node>;
/*
* Get the text content of the node, and its children if it has any.
*/
getText(node: Node): string;
/**
* Does the element have the named attribute?
*/
hasAttrib(elem: ElementNode, name: string): boolean;
/**
* takes an array of nodes, and removes any duplicates, as well as any
* nodes whose ancestors are also in the array.
*/
removeSubsets(nodes: Array<Node>): Array<Node>;
/**
* finds all of the element nodes in the array that match the test predicate,
* as well as any of their children that match it.
*/
findAll(test: Predicate<ElementNode>, nodes: Array<Node>): Array<ElementNode>;
/**
* finds the first node in the array that matches the test predicate, or one
* of its children.
*/
findOne(test: Predicate<ElementNode>, elems: Array<ElementNode>): ElementNode | undefined,
/**
The adapter can also optionally include an equals method, if your DOM
structure needs a custom equality test to compare two objects which refer
to the same underlying node. If not provided, `css-select` will fall back to
`a === b`.
*/
equals?: (a: Node, b: Node) => boolean;
/**
* is the element in hovered state?
*/
isHovered?: (elem: ElementNode) => boolean;
/**
* is the element in visited state?
*/
isVisited?: (elem: ElementNode) => boolean;
/**
* is the element in active state?
*/
isActive?: (elem: ElementNode) => boolean;
}
// TODO default types to the domutil/httpparser2 types
interface Options<Node, ElementNode extends Node> {
/**
* When enabled, tag names will be case-sensitive. Default: false.
*/
xmlMode?: boolean;
/**
* Limits the module to only use CSS3 selectors. Default: false.
*/
strict?: boolean;
/**
* The last function in the stack, will be called with the last element
* that's looked at. Should return true.
*/
rootFunc?: (element: ElementNode) => true;
/**
* The adapter to use when interacting with the backing DOM structure. By
* default it uses domutils.
*/
adapter?: Adapter<Node, ElementNode>;
}
type CompiledQuery = (node: any) => boolean;
type Query = string | CompiledQuery;
/**
* Compiles the query, returns a function.
*
* Supported simple selectors:
* * Universal (*)
* * Tag (<tagname>)
* * Attribute ([attr=foo]), with supported comparisons:
* * [attr] (existential)
* * =
* * ~=
* * |=
* * *=
* * ^=
* * $=
* * !=
* * Can be case insensitive (E.g. [attr=foo i])
* * Pseudos:
* * :not
* * :root
* * :empty
* * :[first|last]-child[-of-type]
* * :only-of-type, :only-child
* * :nth-[last-]child[-of-type]
* * :link, :visited (the latter doesn't match any elements)
* * :checked
* * :enabled, :disabled
* * :required, :optional
* * Nonstandard Pseudos (available when strict mode is not enabled):
* * `:contains`
* * `:icontains` (case-insensitive version of :contains)
* * `:has`
* * `:parent`
* * `:selected`
* * `:header, :button, :input, :text, :checkbox, :file, :password, :reset, :radio etc.
* * :matches
*
* Supported Combinators:
*
* * Descendant (` `)
* * Child (`>`)
* * Parent (`<`) (when strict mode is not enabled)
* * Sibling (`~`)
* * Adjacent (`+`)
*/
function compile(query: string): CompiledQuery;
/**
* @template Node The generic Node type for the DOM adapter being used.
* @template ElementNode The Node type for elements for the DOM adapter being used.
* @param elems Elements to query. If it is an element, its children will be queried..
* @param query can be either a CSS selector string or a compiled query function.
* @param [options] options for querying the document.
* @see CSSselect.compile for supported selector queries.
* @returns All matching elements.
*/
function selectAll<Node, ElementNode extends Node>(
query: Query,
elems: Array<ElementNode> | ElementNode,
options?: Options<Node, ElementNode>
): Array<ElementNode>;
/**
* @template Node The generic Node type for the DOM adapter being used.
* @template ElementNode The Node type for elements for the DOM adapter being used.
* @param elems Elements to query. If it is an element, its children will be queried..
* @param query can be either a CSS selector string or a compiled query function.
* @param [options] options for querying the document.
* @see CSSselect.compile for supported selector queries.
* @returns the first match, or null if there was no match.
*/
function selectOne<Node, ElementNode extends Node>(
query: Query,
elems: Array<ElementNode> | ElementNode,
options?: Options<Node, ElementNode>
): ElementNode | null;
/**
* Tests whether or not an element is matched by query.
*
* @template Node The generic Node type for the DOM adapter being used.
* @template ElementNode The Node type for elements for the DOM adapter being used.
* @param elem The element to test if it matches the query.
* @param query can be either a CSS selector string or a compiled query function.
* @param [options] options for querying the document.
* @see CSSselect.compile for supported selector queries.
* @returns
*/
function is<Node, ElementNode extends Node>(
elem: ElementNode,
query: Query,
options?: Options<Node, ElementNode>
): boolean;
}
+94
View File
@@ -0,0 +1,94 @@
"use strict";
module.exports = CSSselect;
var DomUtils = require("domutils");
var falseFunc = require("boolbase").falseFunc;
var compileRaw = require("./lib/compile.js");
function wrapCompile(func) {
return function addAdapter(selector, options, context) {
options = options || {};
options.adapter = options.adapter || DomUtils;
return func(selector, options, context);
};
}
var compile = wrapCompile(compileRaw);
var compileUnsafe = wrapCompile(compileRaw.compileUnsafe);
function getSelectorFunc(searchFunc) {
return function select(query, elems, options) {
options = options || {};
options.adapter = options.adapter || DomUtils;
if (typeof query !== "function") {
query = compileUnsafe(query, options, elems);
}
if (query.shouldTestNextSiblings) {
elems = appendNextSiblings((options && options.context) || elems, options.adapter);
}
if (!Array.isArray(elems)) elems = options.adapter.getChildren(elems);
else elems = options.adapter.removeSubsets(elems);
return searchFunc(query, elems, options);
};
}
function getNextSiblings(elem, adapter) {
var siblings = adapter.getSiblings(elem);
if (!Array.isArray(siblings)) return [];
siblings = siblings.slice(0);
while (siblings.shift() !== elem);
return siblings;
}
function appendNextSiblings(elems, adapter) {
// Order matters because jQuery seems to check the children before the siblings
if (!Array.isArray(elems)) elems = [elems];
var newElems = elems.slice(0);
for (var i = 0, len = elems.length; i < len; i++) {
var nextSiblings = getNextSiblings(newElems[i], adapter);
newElems.push.apply(newElems, nextSiblings);
}
return newElems;
}
var selectAll = getSelectorFunc(function selectAll(query, elems, options) {
return query === falseFunc || !elems || elems.length === 0 ? [] : options.adapter.findAll(query, elems);
});
var selectOne = getSelectorFunc(function selectOne(query, elems, options) {
return query === falseFunc || !elems || elems.length === 0 ? null : options.adapter.findOne(query, elems);
});
function is(elem, query, options) {
options = options || {};
options.adapter = options.adapter || DomUtils;
return (typeof query === "function" ? query : compile(query, options))(elem);
}
/*
the exported interface
*/
function CSSselect(query, elems, options) {
return selectAll(query, elems, options);
}
CSSselect.compile = compile;
CSSselect.filters = compileRaw.Pseudos.filters;
CSSselect.pseudos = compileRaw.Pseudos.pseudos;
CSSselect.selectAll = selectAll;
CSSselect.selectOne = selectOne;
CSSselect.is = is;
//legacy methods (might be removed)
CSSselect.parse = compile;
CSSselect.iterate = selectAll;
//hooks
CSSselect._compileUnsafe = compileUnsafe;
CSSselect._compileToken = compileRaw.compileToken;
+190
View File
@@ -0,0 +1,190 @@
var falseFunc = require("boolbase").falseFunc;
//https://github.com/slevithan/XRegExp/blob/master/src/xregexp.js#L469
var reChars = /[-[\]{}()*+?.,\\^$|#\s]/g;
/*
attribute selectors
*/
var attributeRules = {
__proto__: null,
equals: function(next, data, options) {
var name = data.name;
var value = data.value;
var adapter = options.adapter;
if (data.ignoreCase) {
value = value.toLowerCase();
return function equalsIC(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && attr.toLowerCase() === value && next(elem);
};
}
return function equals(elem) {
return adapter.getAttributeValue(elem, name) === value && next(elem);
};
},
hyphen: function(next, data, options) {
var name = data.name;
var value = data.value;
var len = value.length;
var adapter = options.adapter;
if (data.ignoreCase) {
value = value.toLowerCase();
return function hyphenIC(elem) {
var attr = adapter.getAttributeValue(elem, name);
return (
attr != null &&
(attr.length === len || attr.charAt(len) === "-") &&
attr.substr(0, len).toLowerCase() === value &&
next(elem)
);
};
}
return function hyphen(elem) {
var attr = adapter.getAttributeValue(elem, name);
return (
attr != null &&
attr.substr(0, len) === value &&
(attr.length === len || attr.charAt(len) === "-") &&
next(elem)
);
};
},
element: function(next, data, options) {
var name = data.name;
var value = data.value;
var adapter = options.adapter;
if (/\s/.test(value)) {
return falseFunc;
}
value = value.replace(reChars, "\\$&");
var pattern = "(?:^|\\s)" + value + "(?:$|\\s)",
flags = data.ignoreCase ? "i" : "",
regex = new RegExp(pattern, flags);
return function element(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && regex.test(attr) && next(elem);
};
},
exists: function(next, data, options) {
var name = data.name;
var adapter = options.adapter;
return function exists(elem) {
return adapter.hasAttrib(elem, name) && next(elem);
};
},
start: function(next, data, options) {
var name = data.name;
var value = data.value;
var len = value.length;
var adapter = options.adapter;
if (len === 0) {
return falseFunc;
}
if (data.ignoreCase) {
value = value.toLowerCase();
return function startIC(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && attr.substr(0, len).toLowerCase() === value && next(elem);
};
}
return function start(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && attr.substr(0, len) === value && next(elem);
};
},
end: function(next, data, options) {
var name = data.name;
var value = data.value;
var len = -value.length;
var adapter = options.adapter;
if (len === 0) {
return falseFunc;
}
if (data.ignoreCase) {
value = value.toLowerCase();
return function endIC(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && attr.substr(len).toLowerCase() === value && next(elem);
};
}
return function end(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && attr.substr(len) === value && next(elem);
};
},
any: function(next, data, options) {
var name = data.name;
var value = data.value;
var adapter = options.adapter;
if (value === "") {
return falseFunc;
}
if (data.ignoreCase) {
var regex = new RegExp(value.replace(reChars, "\\$&"), "i");
return function anyIC(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && regex.test(attr) && next(elem);
};
}
return function any(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && attr.indexOf(value) >= 0 && next(elem);
};
},
not: function(next, data, options) {
var name = data.name;
var value = data.value;
var adapter = options.adapter;
if (value === "") {
return function notEmpty(elem) {
return !!adapter.getAttributeValue(elem, name) && next(elem);
};
} else if (data.ignoreCase) {
value = value.toLowerCase();
return function notIC(elem) {
var attr = adapter.getAttributeValue(elem, name);
return attr != null && attr.toLowerCase() !== value && next(elem);
};
}
return function not(elem) {
return adapter.getAttributeValue(elem, name) !== value && next(elem);
};
}
};
module.exports = {
compile: function(next, data, options) {
if (options && options.strict && (data.ignoreCase || data.action === "not")) {
throw new Error("Unsupported attribute selector");
}
return attributeRules[data.action](next, data, options);
},
rules: attributeRules
};
+219
View File
@@ -0,0 +1,219 @@
/*
compiles a selector to an executable function
*/
module.exports = compile;
var parse = require("css-what").parse;
var BaseFuncs = require("boolbase");
var sortRules = require("./sort.js");
var procedure = require("./procedure.json");
var Rules = require("./general.js");
var Pseudos = require("./pseudos.js");
var trueFunc = BaseFuncs.trueFunc;
var falseFunc = BaseFuncs.falseFunc;
var filters = Pseudos.filters;
function compile(selector, options, context) {
var next = compileUnsafe(selector, options, context);
return wrap(next, options);
}
function wrap(next, options) {
var adapter = options.adapter;
return function base(elem) {
return adapter.isTag(elem) && next(elem);
};
}
function compileUnsafe(selector, options, context) {
var token = parse(selector, options);
return compileToken(token, options, context);
}
function includesScopePseudo(t) {
return (
t.type === "pseudo" &&
(t.name === "scope" ||
(Array.isArray(t.data) &&
t.data.some(function(data) {
return data.some(includesScopePseudo);
})))
);
}
var DESCENDANT_TOKEN = { type: "descendant" };
var FLEXIBLE_DESCENDANT_TOKEN = { type: "_flexibleDescendant" };
var SCOPE_TOKEN = { type: "pseudo", name: "scope" };
var PLACEHOLDER_ELEMENT = {};
//CSS 4 Spec (Draft): 3.3.1. Absolutizing a Scope-relative Selector
//http://www.w3.org/TR/selectors4/#absolutizing
function absolutize(token, options, context) {
var adapter = options.adapter;
//TODO better check if context is document
var hasContext =
!!context &&
!!context.length &&
context.every(function(e) {
return e === PLACEHOLDER_ELEMENT || !!adapter.getParent(e);
});
token.forEach(function(t) {
if (t.length > 0 && isTraversal(t[0]) && t[0].type !== "descendant") {
//don't return in else branch
} else if (hasContext && !(Array.isArray(t) ? t.some(includesScopePseudo) : includesScopePseudo(t))) {
t.unshift(DESCENDANT_TOKEN);
} else {
return;
}
t.unshift(SCOPE_TOKEN);
});
}
function compileToken(token, options, context) {
token = token.filter(function(t) {
return t.length > 0;
});
token.forEach(sortRules);
var isArrayContext = Array.isArray(context);
context = (options && options.context) || context;
if (context && !isArrayContext) context = [context];
absolutize(token, options, context);
var shouldTestNextSiblings = false;
var query = token
.map(function(rules) {
if (rules[0] && rules[1] && rules[0].name === "scope") {
var ruleType = rules[1].type;
if (isArrayContext && ruleType === "descendant") {
rules[1] = FLEXIBLE_DESCENDANT_TOKEN;
} else if (ruleType === "adjacent" || ruleType === "sibling") {
shouldTestNextSiblings = true;
}
}
return compileRules(rules, options, context);
})
.reduce(reduceRules, falseFunc);
query.shouldTestNextSiblings = shouldTestNextSiblings;
return query;
}
function isTraversal(t) {
return procedure[t.type] < 0;
}
function compileRules(rules, options, context) {
return rules.reduce(function(func, rule) {
if (func === falseFunc) return func;
if (!(rule.type in Rules)) {
throw new Error("Rule type " + rule.type + " is not supported by css-select");
}
return Rules[rule.type](func, rule, options, context);
}, (options && options.rootFunc) || trueFunc);
}
function reduceRules(a, b) {
if (b === falseFunc || a === trueFunc) {
return a;
}
if (a === falseFunc || b === trueFunc) {
return b;
}
return function combine(elem) {
return a(elem) || b(elem);
};
}
function containsTraversal(t) {
return t.some(isTraversal);
}
//:not, :has and :matches have to compile selectors
//doing this in lib/pseudos.js would lead to circular dependencies,
//so we add them here
filters.not = function(next, token, options, context) {
var opts = {
xmlMode: !!(options && options.xmlMode),
strict: !!(options && options.strict),
adapter: options.adapter
};
if (opts.strict) {
if (token.length > 1 || token.some(containsTraversal)) {
throw new Error("complex selectors in :not aren't allowed in strict mode");
}
}
var func = compileToken(token, opts, context);
if (func === falseFunc) return next;
if (func === trueFunc) return falseFunc;
return function not(elem) {
return !func(elem) && next(elem);
};
};
filters.has = function(next, token, options) {
var adapter = options.adapter;
var opts = {
xmlMode: !!(options && options.xmlMode),
strict: !!(options && options.strict),
adapter: adapter
};
//FIXME: Uses an array as a pointer to the current element (side effects)
var context = token.some(containsTraversal) ? [PLACEHOLDER_ELEMENT] : null;
var func = compileToken(token, opts, context);
if (func === falseFunc) return falseFunc;
if (func === trueFunc) {
return function hasChild(elem) {
return adapter.getChildren(elem).some(adapter.isTag) && next(elem);
};
}
func = wrap(func, options);
if (context) {
return function has(elem) {
return next(elem) && ((context[0] = elem), adapter.existsOne(func, adapter.getChildren(elem)));
};
}
return function has(elem) {
return next(elem) && adapter.existsOne(func, adapter.getChildren(elem));
};
};
filters.matches = function(next, token, options, context) {
var opts = {
xmlMode: !!(options && options.xmlMode),
strict: !!(options && options.strict),
rootFunc: next,
adapter: options.adapter
};
return compileToken(token, opts, context);
};
compile.compileToken = compileToken;
compile.compileUnsafe = compileUnsafe;
compile.Pseudos = Pseudos;
+117
View File
@@ -0,0 +1,117 @@
var attributes = require("./attributes.js");
var Pseudos = require("./pseudos");
/*
all available rules
*/
module.exports = {
__proto__: null,
attribute: attributes.compile,
pseudo: Pseudos.compile,
//tags
tag: function(next, data, options) {
var name = data.name;
var adapter = options.adapter;
return function tag(elem) {
return adapter.getName(elem) === name && next(elem);
};
},
//traversal
descendant: function(next, data, options) {
// eslint-disable-next-line no-undef
var isFalseCache = typeof WeakSet !== "undefined" ? new WeakSet() : null;
var adapter = options.adapter;
return function descendant(elem) {
var found = false;
while (!found && (elem = adapter.getParent(elem))) {
if (!isFalseCache || !isFalseCache.has(elem)) {
found = next(elem);
if (!found && isFalseCache) {
isFalseCache.add(elem);
}
}
}
return found;
};
},
_flexibleDescendant: function(next, data, options) {
var adapter = options.adapter;
// Include element itself, only used while querying an array
return function descendant(elem) {
var found = next(elem);
while (!found && (elem = adapter.getParent(elem))) {
found = next(elem);
}
return found;
};
},
parent: function(next, data, options) {
if (options && options.strict) {
throw new Error("Parent selector isn't part of CSS3");
}
var adapter = options.adapter;
return function parent(elem) {
return adapter.getChildren(elem).some(test);
};
function test(elem) {
return adapter.isTag(elem) && next(elem);
}
},
child: function(next, data, options) {
var adapter = options.adapter;
return function child(elem) {
var parent = adapter.getParent(elem);
return !!parent && next(parent);
};
},
sibling: function(next, data, options) {
var adapter = options.adapter;
return function sibling(elem) {
var siblings = adapter.getSiblings(elem);
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
if (next(siblings[i])) return true;
}
}
return false;
};
},
adjacent: function(next, data, options) {
var adapter = options.adapter;
return function adjacent(elem) {
var siblings = adapter.getSiblings(elem),
lastElement;
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
lastElement = siblings[i];
}
}
return !!lastElement && next(lastElement);
};
},
universal: function(next) {
return next;
}
};
+11
View File
@@ -0,0 +1,11 @@
{
"universal": 50,
"tag": 30,
"attribute": 1,
"pseudo": 0,
"descendant": -1,
"child": -1,
"parent": -1,
"sibling": -1,
"adjacent": -1
}
+453
View File
@@ -0,0 +1,453 @@
/*
pseudo selectors
---
they are available in two forms:
* filters called when the selector
is compiled and return a function
that needs to return next()
* pseudos get called on execution
they need to return a boolean
*/
var getNCheck = require("nth-check");
var BaseFuncs = require("boolbase");
var attributes = require("./attributes.js");
var trueFunc = BaseFuncs.trueFunc;
var falseFunc = BaseFuncs.falseFunc;
var checkAttrib = attributes.rules.equals;
function getAttribFunc(name, value) {
var data = { name: name, value: value };
return function attribFunc(next, rule, options) {
return checkAttrib(next, data, options);
};
}
function getChildFunc(next, adapter) {
return function(elem) {
return !!adapter.getParent(elem) && next(elem);
};
}
var filters = {
contains: function(next, text, options) {
var adapter = options.adapter;
return function contains(elem) {
return next(elem) && adapter.getText(elem).indexOf(text) >= 0;
};
},
icontains: function(next, text, options) {
var itext = text.toLowerCase();
var adapter = options.adapter;
return function icontains(elem) {
return (
next(elem) &&
adapter
.getText(elem)
.toLowerCase()
.indexOf(itext) >= 0
);
};
},
//location specific methods
"nth-child": function(next, rule, options) {
var func = getNCheck(rule);
var adapter = options.adapter;
if (func === falseFunc) return func;
if (func === trueFunc) return getChildFunc(next, adapter);
return function nthChild(elem) {
var siblings = adapter.getSiblings(elem);
for (var i = 0, pos = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
else pos++;
}
}
return func(pos) && next(elem);
};
},
"nth-last-child": function(next, rule, options) {
var func = getNCheck(rule);
var adapter = options.adapter;
if (func === falseFunc) return func;
if (func === trueFunc) return getChildFunc(next, adapter);
return function nthLastChild(elem) {
var siblings = adapter.getSiblings(elem);
for (var pos = 0, i = siblings.length - 1; i >= 0; i--) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
else pos++;
}
}
return func(pos) && next(elem);
};
},
"nth-of-type": function(next, rule, options) {
var func = getNCheck(rule);
var adapter = options.adapter;
if (func === falseFunc) return func;
if (func === trueFunc) return getChildFunc(next, adapter);
return function nthOfType(elem) {
var siblings = adapter.getSiblings(elem);
for (var pos = 0, i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) pos++;
}
}
return func(pos) && next(elem);
};
},
"nth-last-of-type": function(next, rule, options) {
var func = getNCheck(rule);
var adapter = options.adapter;
if (func === falseFunc) return func;
if (func === trueFunc) return getChildFunc(next, adapter);
return function nthLastOfType(elem) {
var siblings = adapter.getSiblings(elem);
for (var pos = 0, i = siblings.length - 1; i >= 0; i--) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) break;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) pos++;
}
}
return func(pos) && next(elem);
};
},
//TODO determine the actual root element
root: function(next, rule, options) {
var adapter = options.adapter;
return function(elem) {
return !adapter.getParent(elem) && next(elem);
};
},
scope: function(next, rule, options, context) {
var adapter = options.adapter;
if (!context || context.length === 0) {
//equivalent to :root
return filters.root(next, rule, options);
}
function equals(a, b) {
if (typeof adapter.equals === "function") return adapter.equals(a, b);
return a === b;
}
if (context.length === 1) {
//NOTE: can't be unpacked, as :has uses this for side-effects
return function(elem) {
return equals(context[0], elem) && next(elem);
};
}
return function(elem) {
return context.indexOf(elem) >= 0 && next(elem);
};
},
//jQuery extensions (others follow as pseudos)
checkbox: getAttribFunc("type", "checkbox"),
file: getAttribFunc("type", "file"),
password: getAttribFunc("type", "password"),
radio: getAttribFunc("type", "radio"),
reset: getAttribFunc("type", "reset"),
image: getAttribFunc("type", "image"),
submit: getAttribFunc("type", "submit"),
//dynamic state pseudos. These depend on optional Adapter methods.
hover: function(next, rule, options) {
var adapter = options.adapter;
if (typeof adapter.isHovered === 'function') {
return function hover(elem) {
return next(elem) && adapter.isHovered(elem);
};
}
return falseFunc;
},
visited: function(next, rule, options) {
var adapter = options.adapter;
if (typeof adapter.isVisited === 'function') {
return function visited(elem) {
return next(elem) && adapter.isVisited(elem);
};
}
return falseFunc;
},
active: function(next, rule, options) {
var adapter = options.adapter;
if (typeof adapter.isActive === 'function') {
return function active(elem) {
return next(elem) && adapter.isActive(elem);
};
}
return falseFunc;
}
};
//helper methods
function getFirstElement(elems, adapter) {
for (var i = 0; elems && i < elems.length; i++) {
if (adapter.isTag(elems[i])) return elems[i];
}
}
//while filters are precompiled, pseudos get called when they are needed
var pseudos = {
empty: function(elem, adapter) {
return !adapter.getChildren(elem).some(function(elem) {
return adapter.isTag(elem) || elem.type === "text";
});
},
"first-child": function(elem, adapter) {
return getFirstElement(adapter.getSiblings(elem), adapter) === elem;
},
"last-child": function(elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = siblings.length - 1; i >= 0; i--) {
if (siblings[i] === elem) return true;
if (adapter.isTag(siblings[i])) break;
}
return false;
},
"first-of-type": function(elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) return true;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) break;
}
}
return false;
},
"last-of-type": function(elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = siblings.length - 1; i >= 0; i--) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) return true;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) break;
}
}
return false;
},
"only-of-type": function(elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = 0, j = siblings.length; i < j; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) continue;
if (adapter.getName(siblings[i]) === adapter.getName(elem)) {
return false;
}
}
}
return true;
},
"only-child": function(elem, adapter) {
var siblings = adapter.getSiblings(elem);
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i]) && siblings[i] !== elem) return false;
}
return true;
},
//:matches(a, area, link)[href]
link: function(elem, adapter) {
return adapter.hasAttrib(elem, "href");
},
//TODO: :any-link once the name is finalized (as an alias of :link)
//forms
//to consider: :target
//:matches([selected], select:not([multiple]):not(> option[selected]) > option:first-of-type)
selected: function(elem, adapter) {
if (adapter.hasAttrib(elem, "selected")) return true;
else if (adapter.getName(elem) !== "option") return false;
//the first <option> in a <select> is also selected
var parent = adapter.getParent(elem);
if (!parent || adapter.getName(parent) !== "select" || adapter.hasAttrib(parent, "multiple")) {
return false;
}
var siblings = adapter.getChildren(parent);
var sawElem = false;
for (var i = 0; i < siblings.length; i++) {
if (adapter.isTag(siblings[i])) {
if (siblings[i] === elem) {
sawElem = true;
} else if (!sawElem) {
return false;
} else if (adapter.hasAttrib(siblings[i], "selected")) {
return false;
}
}
}
return sawElem;
},
//https://html.spec.whatwg.org/multipage/scripting.html#disabled-elements
//:matches(
// :matches(button, input, select, textarea, menuitem, optgroup, option)[disabled],
// optgroup[disabled] > option),
// fieldset[disabled] * //TODO not child of first <legend>
//)
disabled: function(elem, adapter) {
return adapter.hasAttrib(elem, "disabled");
},
enabled: function(elem, adapter) {
return !adapter.hasAttrib(elem, "disabled");
},
//:matches(:matches(:radio, :checkbox)[checked], :selected) (TODO menuitem)
checked: function(elem, adapter) {
return adapter.hasAttrib(elem, "checked") || pseudos.selected(elem, adapter);
},
//:matches(input, select, textarea)[required]
required: function(elem, adapter) {
return adapter.hasAttrib(elem, "required");
},
//:matches(input, select, textarea):not([required])
optional: function(elem, adapter) {
return !adapter.hasAttrib(elem, "required");
},
//jQuery extensions
//:not(:empty)
parent: function(elem, adapter) {
return !pseudos.empty(elem, adapter);
},
//:matches(h1, h2, h3, h4, h5, h6)
header: namePseudo(["h1", "h2", "h3", "h4", "h5", "h6"]),
//:matches(button, input[type=button])
button: function(elem, adapter) {
var name = adapter.getName(elem);
return (
name === "button" || (name === "input" && adapter.getAttributeValue(elem, "type") === "button")
);
},
//:matches(input, textarea, select, button)
input: namePseudo(["input", "textarea", "select", "button"]),
//input:matches(:not([type!='']), [type='text' i])
text: function(elem, adapter) {
var attr;
return (
adapter.getName(elem) === "input" &&
(!(attr = adapter.getAttributeValue(elem, "type")) || attr.toLowerCase() === "text")
);
}
};
function namePseudo(names) {
if (typeof Set !== "undefined") {
// eslint-disable-next-line no-undef
var nameSet = new Set(names);
return function(elem, adapter) {
return nameSet.has(adapter.getName(elem));
};
}
return function(elem, adapter) {
return names.indexOf(adapter.getName(elem)) >= 0;
};
}
function verifyArgs(func, name, subselect) {
if (subselect === null) {
if (func.length > 2 && name !== "scope") {
throw new Error("pseudo-selector :" + name + " requires an argument");
}
} else {
if (func.length === 2) {
throw new Error("pseudo-selector :" + name + " doesn't have any arguments");
}
}
}
//FIXME this feels hacky
var re_CSS3 = /^(?:(?:nth|last|first|only)-(?:child|of-type)|root|empty|(?:en|dis)abled|checked|not)$/;
module.exports = {
compile: function(next, data, options, context) {
var name = data.name;
var subselect = data.data;
var adapter = options.adapter;
if (options && options.strict && !re_CSS3.test(name)) {
throw new Error(":" + name + " isn't part of CSS3");
}
if (typeof filters[name] === "function") {
return filters[name](next, subselect, options, context);
} else if (typeof pseudos[name] === "function") {
var func = pseudos[name];
verifyArgs(func, name, subselect);
if (func === falseFunc) {
return func;
}
if (next === trueFunc) {
return function pseudoRoot(elem) {
return func(elem, adapter, subselect);
};
}
return function pseudoArgs(elem) {
return func(elem, adapter, subselect) && next(elem);
};
} else {
throw new Error("unmatched pseudo-class :" + name);
}
},
filters: filters,
pseudos: pseudos
};
+80
View File
@@ -0,0 +1,80 @@
module.exports = sortByProcedure;
/*
sort the parts of the passed selector,
as there is potential for optimization
(some types of selectors are faster than others)
*/
var procedure = require("./procedure.json");
var attributes = {
__proto__: null,
exists: 10,
equals: 8,
not: 7,
start: 6,
end: 6,
any: 5,
hyphen: 4,
element: 4
};
function sortByProcedure(arr) {
var procs = arr.map(getProcedure);
for (var i = 1; i < arr.length; i++) {
var procNew = procs[i];
if (procNew < 0) continue;
for (var j = i - 1; j >= 0 && procNew < procs[j]; j--) {
var token = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = token;
procs[j + 1] = procs[j];
procs[j] = procNew;
}
}
}
function getProcedure(token) {
var proc = procedure[token.type];
if (proc === procedure.attribute) {
proc = attributes[token.action];
if (proc === attributes.equals && token.name === "id") {
//prefer ID selectors (eg. #ID)
proc = 9;
}
if (token.ignoreCase) {
//ignoreCase adds some overhead, prefer "normal" token
//this is a binary operation, to ensure it's still an int
proc >>= 1;
}
} else if (proc === procedure.pseudo) {
if (!token.data) {
proc = 3;
} else if (token.name === "has" || token.name === "contains") {
proc = 0; //expensive in any case
} else if (token.name === "matches" || token.name === "not") {
proc = 0;
for (var i = 0; i < token.data.length; i++) {
//TODO better handling of complex selectors
if (token.data[i].length !== 1) continue;
var cur = getProcedure(token.data[i][0]);
//avoid executing :has or :contains
if (cur === 0) {
proc = 0;
break;
}
if (cur > proc) proc = cur;
}
if (token.data.length > 1 && proc > 0) proc -= 1;
} else {
proc = 1;
}
}
return proc;
}
+82
View File
@@ -0,0 +1,82 @@
{
"_args": [
[
"css-select@2.1.0",
"/home/node/nuxt"
]
],
"_from": "css-select@2.1.0",
"_id": "css-select@2.1.0",
"_inBundle": false,
"_integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==",
"_location": "/svgo/css-select",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "css-select@2.1.0",
"name": "css-select",
"escapedName": "css-select",
"rawSpec": "2.1.0",
"saveSpec": null,
"fetchSpec": "2.1.0"
},
"_requiredBy": [
"/svgo"
],
"_resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz",
"_spec": "2.1.0",
"_where": "/home/node/nuxt",
"author": {
"name": "Felix Boehm",
"email": "me@feedic.com"
},
"bugs": {
"url": "https://github.com/fb55/css-select/issues"
},
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^3.2.1",
"domutils": "^1.7.0",
"nth-check": "^1.0.2"
},
"description": "a CSS selector compiler/engine",
"devDependencies": {
"cheerio-soupselect": "^0.1.1",
"coveralls": "^3.0.2",
"eslint": "^6.0.0",
"expect.js": "^0.3.1",
"htmlparser2": "^4.0.0",
"istanbul": "^0.4.5",
"mocha": "^6.0.0",
"mocha-lcov-reporter": "^1.3.0"
},
"files": [
"index.js",
"index.d.ts",
"lib"
],
"homepage": "https://github.com/fb55/css-select#readme",
"keywords": [
"css",
"selector",
"sizzle"
],
"license": "BSD-2-Clause",
"name": "css-select",
"prettier": {
"tabWidth": 4
},
"repository": {
"type": "git",
"url": "git://github.com/fb55/css-select.git"
},
"scripts": {
"coveralls": "npm run lint && npm run lcov && (cat coverage/lcov.info | coveralls || exit 0)",
"lcov": "istanbul cover _mocha --report lcovonly -- -R spec",
"lint": "eslint index.js lib/*.js test/*.js",
"test": "mocha && npm run lint"
},
"types": "index.d.ts",
"version": "2.1.0"
}
+11
View File
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+4
View File
@@ -0,0 +1,4 @@
export * from "./parse";
export { default as parse } from "./parse";
export { default as stringify } from "./stringify";
//# sourceMappingURL=index.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC"}
+21
View File
@@ -0,0 +1,21 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.stringify = exports.parse = void 0;
__exportStar(require("./parse"), exports);
var parse_1 = require("./parse");
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return __importDefault(parse_1).default; } });
var stringify_1 = require("./stringify");
Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return __importDefault(stringify_1).default; } });
+38
View File
@@ -0,0 +1,38 @@
export default parse;
export interface Options {
lowerCaseAttributeNames?: boolean;
lowerCaseTags?: boolean;
xmlMode?: boolean;
}
export declare type Selector = PseudoSelector | PseudoElement | AttributeSelector | TagSelector | UniversalSelector | Traversal;
export interface AttributeSelector {
type: "attribute";
name: string;
action: AttributeAction;
value: string;
ignoreCase: boolean;
}
declare type DataType = Selector[][] | null | string;
export interface PseudoSelector {
type: "pseudo";
name: string;
data: DataType;
}
export interface PseudoElement {
type: "pseudo-element";
name: string;
}
export interface TagSelector {
type: "tag";
name: string;
}
export interface UniversalSelector {
type: "universal";
}
export interface Traversal {
type: TraversalType;
}
export declare type AttributeAction = "any" | "element" | "end" | "equals" | "exists" | "hyphen" | "not" | "start";
export declare type TraversalType = "adjacent" | "child" | "descendant" | "parent" | "sibling";
declare function parse(selector: string, options?: Options): Selector[][];
//# sourceMappingURL=parse.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAEA,eAAe,KAAK,CAAC;AAErB,MAAM,WAAW,OAAO;IACpB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,oBAAY,QAAQ,GACd,cAAc,GACd,aAAa,GACb,iBAAiB,GACjB,WAAW,GACX,iBAAiB,GACjB,SAAS,CAAC;AAEhB,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;CACvB;AAED,aAAK,QAAQ,GAAG,QAAQ,EAAE,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,aAAa,CAAC;CACvB;AAED,oBAAY,eAAe,GACrB,KAAK,GACL,SAAS,GACT,KAAK,GACL,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,KAAK,GACL,OAAO,CAAC;AAEd,oBAAY,aAAa,GACnB,UAAU,GACV,OAAO,GACP,YAAY,GACZ,QAAQ,GACR,SAAS,CAAC;AAkEhB,iBAAS,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE,EAAE,CAUhE"}
+239
View File
@@ -0,0 +1,239 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = parse;
var reName = /^[^\\]?(?:\\(?:[\da-f]{1,6}\s?|.)|[\w\-\u00b0-\uFFFF])+/;
var reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi;
// Modified version of https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L87
var reAttr = /^\s*((?:\\.|[\w\u00b0-\uFFFF-])+)\s*(?:(\S?)=\s*(?:(['"])((?:[^\\]|\\[^])*?)\3|(#?(?:\\.|[\w\u00b0-\uFFFF-])*)|)|)\s*(i)?\]/;
var actionTypes = {
undefined: "exists",
"": "equals",
"~": "element",
"^": "start",
$: "end",
"*": "any",
"!": "not",
"|": "hyphen",
};
var Traversals = {
">": "child",
"<": "parent",
"~": "sibling",
"+": "adjacent",
};
var attribSelectors = {
"#": ["id", "equals"],
".": ["class", "element"],
};
// Pseudos, whose data property is parsed as well.
var unpackPseudos = new Set([
"has",
"not",
"matches",
"is",
"host",
"host-context",
]);
var stripQuotesFromPseudos = new Set(["contains", "icontains"]);
var quotes = new Set(['"', "'"]);
// Unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
function funescape(_, escaped, escapedWhitespace) {
var high = parseInt(escaped, 16) - 0x10000;
// NaN means non-codepoint
return high !== high || escapedWhitespace
? escaped
: high < 0
? // BMP codepoint
String.fromCharCode(high + 0x10000)
: // Supplemental Plane codepoint (surrogate pair)
String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
}
function unescapeCSS(str) {
return str.replace(reEscape, funescape);
}
function isWhitespace(c) {
return c === " " || c === "\n" || c === "\t" || c === "\f" || c === "\r";
}
function parse(selector, options) {
var subselects = [];
selector = parseSelector(subselects, "" + selector, options);
if (selector !== "") {
throw new Error("Unmatched selector: " + selector);
}
return subselects;
}
function parseSelector(subselects, selector, options) {
var _a, _b;
if (options === void 0) { options = {}; }
var tokens = [];
var sawWS = false;
function getName() {
var match = selector.match(reName);
if (!match) {
throw new Error("Expected name, found " + selector);
}
var sub = match[0];
selector = selector.substr(sub.length);
return unescapeCSS(sub);
}
function stripWhitespace(start) {
while (isWhitespace(selector.charAt(start)))
start++;
selector = selector.substr(start);
}
function isEscaped(pos) {
var slashCount = 0;
while (selector.charAt(--pos) === "\\")
slashCount++;
return (slashCount & 1) === 1;
}
stripWhitespace(0);
while (selector !== "") {
var firstChar = selector.charAt(0);
if (isWhitespace(firstChar)) {
sawWS = true;
stripWhitespace(1);
}
else if (firstChar in Traversals) {
tokens.push({ type: Traversals[firstChar] });
sawWS = false;
stripWhitespace(1);
}
else if (firstChar === ",") {
if (tokens.length === 0) {
throw new Error("Empty sub-selector");
}
subselects.push(tokens);
tokens = [];
sawWS = false;
stripWhitespace(1);
}
else {
if (sawWS) {
if (tokens.length > 0) {
tokens.push({ type: "descendant" });
}
sawWS = false;
}
if (firstChar === "*") {
selector = selector.substr(1);
tokens.push({ type: "universal" });
}
else if (firstChar in attribSelectors) {
var _c = attribSelectors[firstChar], name_1 = _c[0], action = _c[1];
selector = selector.substr(1);
tokens.push({
type: "attribute",
name: name_1,
action: action,
value: getName(),
ignoreCase: false,
});
}
else if (firstChar === "[") {
selector = selector.substr(1);
var attributeMatch = selector.match(reAttr);
if (!attributeMatch) {
throw new Error("Malformed attribute selector: " + selector);
}
var completeSelector = attributeMatch[0], baseName = attributeMatch[1], actionType = attributeMatch[2], _d = attributeMatch[4], quotedValue = _d === void 0 ? "" : _d, _e = attributeMatch[5], value = _e === void 0 ? quotedValue : _e, ignoreCase = attributeMatch[6];
selector = selector.substr(completeSelector.length);
var name_2 = unescapeCSS(baseName);
if ((_a = options.lowerCaseAttributeNames) !== null && _a !== void 0 ? _a : !options.xmlMode) {
name_2 = name_2.toLowerCase();
}
tokens.push({
type: "attribute",
name: name_2,
action: actionTypes[actionType],
value: unescapeCSS(value),
ignoreCase: !!ignoreCase,
});
}
else if (firstChar === ":") {
if (selector.charAt(1) === ":") {
selector = selector.substr(2);
tokens.push({
type: "pseudo-element",
name: getName().toLowerCase(),
});
continue;
}
selector = selector.substr(1);
var name_3 = getName().toLowerCase();
var data = null;
if (selector.startsWith("(")) {
if (unpackPseudos.has(name_3)) {
var quot = selector.charAt(1);
var quoted = quotes.has(quot);
selector = selector.substr(quoted ? 2 : 1);
data = [];
selector = parseSelector(data, selector, options);
if (quoted) {
if (!selector.startsWith(quot)) {
throw new Error("Unmatched quotes in :" + name_3);
}
else {
selector = selector.substr(1);
}
}
if (!selector.startsWith(")")) {
throw new Error("Missing closing parenthesis in :" + name_3 + " (" + selector + ")");
}
selector = selector.substr(1);
}
else {
var pos = 1;
var counter = 1;
for (; counter > 0 && pos < selector.length; pos++) {
if (selector.charAt(pos) === "(" &&
!isEscaped(pos)) {
counter++;
}
else if (selector.charAt(pos) === ")" &&
!isEscaped(pos)) {
counter--;
}
}
if (counter) {
throw new Error("Parenthesis not matched");
}
data = selector.substr(1, pos - 2);
selector = selector.substr(pos);
if (stripQuotesFromPseudos.has(name_3)) {
var quot = data.charAt(0);
if (quot === data.slice(-1) && quotes.has(quot)) {
data = data.slice(1, -1);
}
data = unescapeCSS(data);
}
}
}
tokens.push({ type: "pseudo", name: name_3, data: data });
}
else if (reName.test(selector)) {
var name_4 = getName();
if ((_b = options.lowerCaseTags) !== null && _b !== void 0 ? _b : !options.xmlMode) {
name_4 = name_4.toLowerCase();
}
tokens.push({ type: "tag", name: name_4 });
}
else {
if (tokens.length &&
tokens[tokens.length - 1].type === "descendant") {
tokens.pop();
}
addToken(subselects, tokens);
return selector;
}
}
}
addToken(subselects, tokens);
return selector;
}
function addToken(subselects, tokens) {
if (subselects.length > 0 && tokens.length === 0) {
throw new Error("Empty sub-selector");
}
subselects.push(tokens);
}
+3
View File
@@ -0,0 +1,3 @@
import { Selector } from "./parse";
export default function stringify(token: Selector[][]): string;
//# sourceMappingURL=stringify.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stringify.d.ts","sourceRoot":"","sources":["../src/stringify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAuBnC,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAE7D"}
+83
View File
@@ -0,0 +1,83 @@
"use strict";
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var actionTypes = {
equals: "",
element: "~",
start: "^",
end: "$",
any: "*",
not: "!",
hyphen: "|",
};
var charsToEscape = new Set(__spreadArrays(Object.keys(actionTypes)
.map(function (typeKey) { return actionTypes[typeKey]; })
.filter(Boolean), [
":",
"[",
"]",
" ",
"\\",
]));
function stringify(token) {
return token.map(stringifySubselector).join(", ");
}
exports.default = stringify;
function stringifySubselector(token) {
return token.map(stringifyToken).join("");
}
function stringifyToken(token) {
switch (token.type) {
// Simple types
case "child":
return " > ";
case "parent":
return " < ";
case "sibling":
return " ~ ";
case "adjacent":
return " + ";
case "descendant":
return " ";
case "universal":
return "*";
case "tag":
return escapeName(token.name);
case "pseudo-element":
return "::" + escapeName(token.name);
case "pseudo":
if (token.data === null)
return ":" + escapeName(token.name);
if (typeof token.data === "string") {
return ":" + escapeName(token.name) + "(" + token.data + ")";
}
return ":" + escapeName(token.name) + "(" + stringify(token.data) + ")";
case "attribute":
if (token.action === "exists") {
return "[" + escapeName(token.name) + "]";
}
if (token.name === "id" &&
token.action === "equals" &&
!token.ignoreCase) {
return "#" + escapeName(token.value);
}
if (token.name === "class" &&
token.action === "element" &&
!token.ignoreCase) {
return "." + escapeName(token.value);
}
return "[" + escapeName(token.name) + actionTypes[token.action] + "='" + escapeName(token.value) + "'" + (token.ignoreCase ? "i" : "") + "]";
}
}
function escapeName(str) {
return str
.split("")
.map(function (c) { return (charsToEscape.has(c) ? "\\" + c : c); })
.join("");
}
+91
View File
@@ -0,0 +1,91 @@
{
"_args": [
[
"css-what@3.4.2",
"/home/node/nuxt"
]
],
"_from": "css-what@3.4.2",
"_id": "css-what@3.4.2",
"_inBundle": false,
"_integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==",
"_location": "/svgo/css-what",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "css-what@3.4.2",
"name": "css-what",
"escapedName": "css-what",
"rawSpec": "3.4.2",
"saveSpec": null,
"fetchSpec": "3.4.2"
},
"_requiredBy": [
"/svgo/css-select"
],
"_resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
"_spec": "3.4.2",
"_where": "/home/node/nuxt",
"author": {
"name": "Felix Böhm",
"email": "me@feedic.com",
"url": "http://feedic.com"
},
"bugs": {
"url": "https://github.com/fb55/css-what/issues"
},
"dependencies": {},
"description": "a CSS selector parser",
"devDependencies": {
"@types/jest": "^26.0.3",
"@types/node": "^14.0.5",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"coveralls": "^3.0.5",
"eslint": "^7.0.0",
"eslint-config-prettier": "^6.0.0",
"eslint-plugin-node": "^11.1.0",
"jest": "^26.0.1",
"prettier": "^2.0.5",
"ts-jest": "^26.0.0",
"typescript": "^4.0.2"
},
"engines": {
"node": ">= 6"
},
"files": [
"lib/**/*"
],
"funding": "https://github.com/sponsors/fb55",
"homepage": "https://github.com/fb55/css-what#readme",
"jest": {
"preset": "ts-jest",
"testEnvironment": "node"
},
"license": "BSD-2-Clause",
"main": "lib/index.js",
"name": "css-what",
"optionalDependencies": {},
"prettier": {
"tabWidth": 4
},
"repository": {
"url": "git+https://github.com/fb55/css-what.git"
},
"scripts": {
"build": "tsc",
"coverage": "cat coverage/lcov.info | coveralls",
"format": "npm run format:es && npm run format:prettier",
"format:es": "npm run lint:es -- --fix",
"format:prettier": "npm run prettier -- --write",
"lint": "npm run lint:es && npm run lint:prettier",
"lint:es": "eslint src",
"lint:prettier": "npm run prettier -- --check",
"prepare": "npm run build",
"prettier": "prettier '**/*.{ts,md,json,yml}'",
"test": "jest --coverage -u && npm run lint"
},
"types": "lib/index.d.ts",
"version": "3.4.2"
}
+70
View File
@@ -0,0 +1,70 @@
# css-what [![Build Status](https://secure.travis-ci.org/fb55/css-what.svg?branch=master)](http://travis-ci.org/fb55/css-what)
a CSS selector parser
## Example
```js
const CSSwhat = require("css-what")
CSSwhat.parse("foo[bar]:baz")
~> [
[
{ type: "tag", name: "foo" },
{
type: "attribute",
name: "bar",
action: "exists",
value: "",
ignoreCase: false
},
{ type: "pseudo", name: "baz", data: null }
]
]
```
## API
**`CSSwhat.parse(str, options)` - Parses `str`, optionally with the passed `options`.**
The function returns a two-dimensional array. The first array represents selectors separated by commas (eg. `sub1, sub2`), the second contains the relevant tokens for that selector. Possible token types are:
| name | attributes | example | output |
| ---------------- | --------------------------------------- | ------------- | ---------------------------------------------------------------------------------------- |
| `tag` | `name` | `div` | `{ type: 'tag', name: 'div' }` |
| `universal` | - | `*` | `{ type: 'universal' }` |
| `pseudo` | `name`, `data` | `:name(data)` | `{ type: 'pseudo', name: 'name', data: 'data' }` |
| `pseudo` | `name`, `data` | `:name` | `{ type: 'pseudo', name: 'name', data: null }` |
| `pseudo-element` | `name` | `::name` | `{ type: 'pseudo-element', name: 'name' }` |
| `attribute` | `name`, `action`, `value`, `ignoreCase` | `[attr]` | `{ type: 'attribute', name: 'attr', action: 'exists', value: '', ignoreCase: false }` |
| `attribute` | `name`, `action`, `value`, `ignoreCase` | `[attr=val]` | `{ type: 'attribute', name: 'attr', action: 'equals', value: 'val', ignoreCase: false }` |
| `attribute` | `name`, `action`, `value`, `ignoreCase` | `[attr^=val]` | `{ type: 'attribute', name: 'attr', action: 'start', value: 'val', ignoreCase: false }` |
| `attribute` | `name`, `action`, `value`, `ignoreCase` | `[attr$=val]` | `{ type: 'attribute', name: 'attr', action: 'end', value: 'val', ignoreCase: false }` |
| `child` | - | `>` | `{ type: 'child' }` |
| `parent` | - | `<` | `{ type: 'parent' }` |
| `sibling` | - | `~` | `{ type: 'sibling' }` |
| `adjacent` | - | `+` | `{ type: 'adjacent' }` |
| `descendant` | - | | `{ type: 'descendant' }` |
**Options:**
- `lowerCaseTags`: When false, tag names will not be lowercased. Defaults to `true`.
- `lowerCaseAttributeNames`: When false, attribute names will not be lowercased. Defaults to `true`.
- `xmlMode`: When `true`, `xmlMode` implies both `lowerCaseTags` and `lowerCaseAttributeNames` are set to `false`.
**`CSSwhat.stringify(selector)` - Turns `selector` back into a string.**
---
License: BSD-2-Clause
## Security contact information
To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.
## `css-what` for enterprise
Available as part of the Tidelift Subscription
The maintainers of `css-what` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-css-what?utm_source=npm-css-what&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
+11
View File
@@ -0,0 +1,11 @@
License
(The MIT License)
Copyright (c) 2014 The cheeriojs contributors
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.
+1
View File
@@ -0,0 +1 @@
Renders a DOM node or an array of DOM nodes to a string.
+102
View File
@@ -0,0 +1,102 @@
{
"elementNames" : {
"altglyph" : "altGlyph",
"altglyphdef" : "altGlyphDef",
"altglyphitem" : "altGlyphItem",
"animatecolor" : "animateColor",
"animatemotion" : "animateMotion",
"animatetransform" : "animateTransform",
"clippath" : "clipPath",
"feblend" : "feBlend",
"fecolormatrix" : "feColorMatrix",
"fecomponenttransfer" : "feComponentTransfer",
"fecomposite" : "feComposite",
"feconvolvematrix" : "feConvolveMatrix",
"fediffuselighting" : "feDiffuseLighting",
"fedisplacementmap" : "feDisplacementMap",
"fedistantlight" : "feDistantLight",
"fedropshadow" : "feDropShadow",
"feflood" : "feFlood",
"fefunca" : "feFuncA",
"fefuncb" : "feFuncB",
"fefuncg" : "feFuncG",
"fefuncr" : "feFuncR",
"fegaussianblur" : "feGaussianBlur",
"feimage" : "feImage",
"femerge" : "feMerge",
"femergenode" : "feMergeNode",
"femorphology" : "feMorphology",
"feoffset" : "feOffset",
"fepointlight" : "fePointLight",
"fespecularlighting" : "feSpecularLighting",
"fespotlight" : "feSpotLight",
"fetile" : "feTile",
"feturbulence" : "feTurbulence",
"foreignobject" : "foreignObject",
"glyphref" : "glyphRef",
"lineargradient" : "linearGradient",
"radialgradient" : "radialGradient",
"textpath" : "textPath"
},
"attributeNames" : {
"definitionurl" : "definitionURL",
"attributename" : "attributeName",
"attributetype" : "attributeType",
"basefrequency" : "baseFrequency",
"baseprofile" : "baseProfile",
"calcmode" : "calcMode",
"clippathunits" : "clipPathUnits",
"diffuseconstant" : "diffuseConstant",
"edgemode" : "edgeMode",
"filterunits" : "filterUnits",
"glyphref" : "glyphRef",
"gradienttransform" : "gradientTransform",
"gradientunits" : "gradientUnits",
"kernelmatrix" : "kernelMatrix",
"kernelunitlength" : "kernelUnitLength",
"keypoints" : "keyPoints",
"keysplines" : "keySplines",
"keytimes" : "keyTimes",
"lengthadjust" : "lengthAdjust",
"limitingconeangle" : "limitingConeAngle",
"markerheight" : "markerHeight",
"markerunits" : "markerUnits",
"markerwidth" : "markerWidth",
"maskcontentunits" : "maskContentUnits",
"maskunits" : "maskUnits",
"numoctaves" : "numOctaves",
"pathlength" : "pathLength",
"patterncontentunits" : "patternContentUnits",
"patterntransform" : "patternTransform",
"patternunits" : "patternUnits",
"pointsatx" : "pointsAtX",
"pointsaty" : "pointsAtY",
"pointsatz" : "pointsAtZ",
"preservealpha" : "preserveAlpha",
"preserveaspectratio" : "preserveAspectRatio",
"primitiveunits" : "primitiveUnits",
"refx" : "refX",
"refy" : "refY",
"repeatcount" : "repeatCount",
"repeatdur" : "repeatDur",
"requiredextensions" : "requiredExtensions",
"requiredfeatures" : "requiredFeatures",
"specularconstant" : "specularConstant",
"specularexponent" : "specularExponent",
"spreadmethod" : "spreadMethod",
"startoffset" : "startOffset",
"stddeviation" : "stdDeviation",
"stitchtiles" : "stitchTiles",
"surfacescale" : "surfaceScale",
"systemlanguage" : "systemLanguage",
"tablevalues" : "tableValues",
"targetx" : "targetX",
"targety" : "targetY",
"textlength" : "textLength",
"viewbox" : "viewBox",
"viewtarget" : "viewTarget",
"xchannelselector" : "xChannelSelector",
"ychannelselector" : "yChannelSelector",
"zoomandpan" : "zoomAndPan"
}
}
+17
View File
@@ -0,0 +1,17 @@
export interface DomSerializerOptions {
xmlMode?: boolean | 'foreign';
decodeEntities?: boolean;
}
/**
* Renders a DOM node or an array of DOM nodes to a string.
*
* Can be thought of as the equivalent of the `outerHTML` of the passed node(s).
*
* @param nodes Nodes to be rendered.
* @param options Changes serialization behavior
*/
export default function render(
nodes: {} | {}[],
options?: DomSerializerOptions
): string;
+183
View File
@@ -0,0 +1,183 @@
/*
Module dependencies
*/
var ElementType = require('domelementtype');
var entities = require('entities');
/* mixed-case SVG and MathML tags & attributes
recognized by the HTML parser, see
https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inforeign
*/
var foreignNames = require('./foreignNames.json');
foreignNames.elementNames.__proto__ = null; /* use as a simple dictionary */
foreignNames.attributeNames.__proto__ = null;
var unencodedElements = {
__proto__: null,
style: true,
script: true,
xmp: true,
iframe: true,
noembed: true,
noframes: true,
plaintext: true,
noscript: true
};
/*
Format attributes
*/
function formatAttrs(attributes, opts) {
if (!attributes) return;
var output = '';
var value;
// Loop through the attributes
for (var key in attributes) {
value = attributes[key];
if (output) {
output += ' ';
}
if (opts.xmlMode === 'foreign') {
/* fix up mixed-case attribute names */
key = foreignNames.attributeNames[key] || key;
}
output += key;
if ((value !== null && value !== '') || opts.xmlMode) {
output +=
'="' +
(opts.decodeEntities
? entities.encodeXML(value)
: value.replace(/\"/g, '&quot;')) +
'"';
}
}
return output;
}
/*
Self-enclosing tags (stolen from node-htmlparser)
*/
var singleTag = {
__proto__: null,
area: true,
base: true,
basefont: true,
br: true,
col: true,
command: true,
embed: true,
frame: true,
hr: true,
img: true,
input: true,
isindex: true,
keygen: true,
link: true,
meta: true,
param: true,
source: true,
track: true,
wbr: true
};
var render = (module.exports = function(dom, opts) {
if (!Array.isArray(dom) && !dom.cheerio) dom = [dom];
opts = opts || {};
var output = '';
for (var i = 0; i < dom.length; i++) {
var elem = dom[i];
if (elem.type === 'root') output += render(elem.children, opts);
else if (ElementType.isTag(elem)) output += renderTag(elem, opts);
else if (elem.type === ElementType.Directive)
output += renderDirective(elem);
else if (elem.type === ElementType.Comment) output += renderComment(elem);
else if (elem.type === ElementType.CDATA) output += renderCdata(elem);
else output += renderText(elem, opts);
}
return output;
});
var foreignModeIntegrationPoints = [
'mi',
'mo',
'mn',
'ms',
'mtext',
'annotation-xml',
'foreignObject',
'desc',
'title'
];
function renderTag(elem, opts) {
// Handle SVG / MathML in HTML
if (opts.xmlMode === 'foreign') {
/* fix up mixed-case element names */
elem.name = foreignNames.elementNames[elem.name] || elem.name;
/* exit foreign mode at integration points */
if (
elem.parent &&
foreignModeIntegrationPoints.indexOf(elem.parent.name) >= 0
)
opts = Object.assign({}, opts, { xmlMode: false });
}
if (!opts.xmlMode && ['svg', 'math'].indexOf(elem.name) >= 0) {
opts = Object.assign({}, opts, { xmlMode: 'foreign' });
}
var tag = '<' + elem.name;
var attribs = formatAttrs(elem.attribs, opts);
if (attribs) {
tag += ' ' + attribs;
}
if (opts.xmlMode && (!elem.children || elem.children.length === 0)) {
tag += '/>';
} else {
tag += '>';
if (elem.children) {
tag += render(elem.children, opts);
}
if (!singleTag[elem.name] || opts.xmlMode) {
tag += '</' + elem.name + '>';
}
}
return tag;
}
function renderDirective(elem) {
return '<' + elem.data + '>';
}
function renderText(elem, opts) {
var data = elem.data || '';
// if entities weren't decoded, no need to encode them back
if (
opts.decodeEntities &&
!(elem.parent && elem.parent.name in unencodedElements)
) {
data = entities.encodeXML(data);
}
return data;
}
function renderCdata(elem) {
return '<![CDATA[' + elem.children[0].data + ']]>';
}
function renderComment(elem) {
return '<!--' + elem.data + '-->';
}
+75
View File
@@ -0,0 +1,75 @@
{
"_args": [
[
"dom-serializer@0.2.2",
"/home/node/nuxt"
]
],
"_from": "dom-serializer@0.2.2",
"_id": "dom-serializer@0.2.2",
"_inBundle": false,
"_integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
"_location": "/svgo/dom-serializer",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "dom-serializer@0.2.2",
"name": "dom-serializer",
"escapedName": "dom-serializer",
"rawSpec": "0.2.2",
"saveSpec": null,
"fetchSpec": "0.2.2"
},
"_requiredBy": [
"/svgo/domutils"
],
"_resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
"_spec": "0.2.2",
"_where": "/home/node/nuxt",
"author": {
"name": "Felix Boehm",
"email": "me@feedic.com"
},
"bugs": {
"url": "https://github.com/cheeriojs/dom-renderer/issues"
},
"dependencies": {
"domelementtype": "^2.0.1",
"entities": "^2.0.0"
},
"description": "render dom nodes to string",
"devDependencies": {
"cheerio": "^1.0.0-rc.2",
"expect.js": "~0.3.1",
"htmlparser2": "^3.10.0",
"lodash": "^4.17.11",
"mocha": "^6.2.0",
"xyz": "^3.0.0"
},
"files": [
"index.js",
"index.d.ts",
"foreignNames.json"
],
"homepage": "https://github.com/cheeriojs/dom-renderer#readme",
"keywords": [
"html",
"xml",
"render"
],
"license": "MIT",
"main": "./index.js",
"name": "dom-serializer",
"prettier": {
"singleQuote": true
},
"repository": {
"type": "git",
"url": "git://github.com/cheeriojs/dom-renderer.git"
},
"scripts": {
"test": "mocha test.js"
},
"version": "0.2.2"
}
+5
View File
@@ -0,0 +1,5 @@
sudo: true
language: node_js
node_js:
- 8
+11
View File
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+14
View File
@@ -0,0 +1,14 @@
var DomUtils = module.exports;
[
require("./lib/stringify"),
require("./lib/traversal"),
require("./lib/manipulation"),
require("./lib/querying"),
require("./lib/legacy"),
require("./lib/helpers")
].forEach(function(ext){
Object.keys(ext).forEach(function(key){
DomUtils[key] = ext[key].bind(DomUtils);
});
});
+141
View File
@@ -0,0 +1,141 @@
// removeSubsets
// Given an array of nodes, remove any member that is contained by another.
exports.removeSubsets = function(nodes) {
var idx = nodes.length, node, ancestor, replace;
// Check if each node (or one of its ancestors) is already contained in the
// array.
while (--idx > -1) {
node = ancestor = nodes[idx];
// Temporarily remove the node under consideration
nodes[idx] = null;
replace = true;
while (ancestor) {
if (nodes.indexOf(ancestor) > -1) {
replace = false;
nodes.splice(idx, 1);
break;
}
ancestor = ancestor.parent;
}
// If the node has been found to be unique, re-insert it.
if (replace) {
nodes[idx] = node;
}
}
return nodes;
};
// Source: http://dom.spec.whatwg.org/#dom-node-comparedocumentposition
var POSITION = {
DISCONNECTED: 1,
PRECEDING: 2,
FOLLOWING: 4,
CONTAINS: 8,
CONTAINED_BY: 16
};
// Compare the position of one node against another node in any other document.
// The return value is a bitmask with the following values:
//
// document order:
// > There is an ordering, document order, defined on all the nodes in the
// > document corresponding to the order in which the first character of the
// > XML representation of each node occurs in the XML representation of the
// > document after expansion of general entities. Thus, the document element
// > node will be the first node. Element nodes occur before their children.
// > Thus, document order orders element nodes in order of the occurrence of
// > their start-tag in the XML (after expansion of entities). The attribute
// > nodes of an element occur after the element and before its children. The
// > relative order of attribute nodes is implementation-dependent./
// Source:
// http://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-document-order
//
// @argument {Node} nodaA The first node to use in the comparison
// @argument {Node} nodeB The second node to use in the comparison
//
// @return {Number} A bitmask describing the input nodes' relative position.
// See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for
// a description of these values.
var comparePos = exports.compareDocumentPosition = function(nodeA, nodeB) {
var aParents = [];
var bParents = [];
var current, sharedParent, siblings, aSibling, bSibling, idx;
if (nodeA === nodeB) {
return 0;
}
current = nodeA;
while (current) {
aParents.unshift(current);
current = current.parent;
}
current = nodeB;
while (current) {
bParents.unshift(current);
current = current.parent;
}
idx = 0;
while (aParents[idx] === bParents[idx]) {
idx++;
}
if (idx === 0) {
return POSITION.DISCONNECTED;
}
sharedParent = aParents[idx - 1];
siblings = sharedParent.children;
aSibling = aParents[idx];
bSibling = bParents[idx];
if (siblings.indexOf(aSibling) > siblings.indexOf(bSibling)) {
if (sharedParent === nodeB) {
return POSITION.FOLLOWING | POSITION.CONTAINED_BY;
}
return POSITION.FOLLOWING;
} else {
if (sharedParent === nodeA) {
return POSITION.PRECEDING | POSITION.CONTAINS;
}
return POSITION.PRECEDING;
}
};
// Sort an array of nodes based on their relative position in the document and
// remove any duplicate nodes. If the array contains nodes that do not belong
// to the same document, sort order is unspecified.
//
// @argument {Array} nodes Array of DOM nodes
//
// @returns {Array} collection of unique nodes, sorted in document order
exports.uniqueSort = function(nodes) {
var idx = nodes.length, node, position;
nodes = nodes.slice();
while (--idx > -1) {
node = nodes[idx];
position = nodes.indexOf(node);
if (position > -1 && position < idx) {
nodes.splice(idx, 1);
}
}
nodes.sort(function(a, b) {
var relative = comparePos(a, b);
if (relative & POSITION.PRECEDING) {
return -1;
} else if (relative & POSITION.FOLLOWING) {
return 1;
}
return 0;
});
return nodes;
};
+87
View File
@@ -0,0 +1,87 @@
var ElementType = require("domelementtype");
var isTag = exports.isTag = ElementType.isTag;
exports.testElement = function(options, element){
for(var key in options){
if(!options.hasOwnProperty(key));
else if(key === "tag_name"){
if(!isTag(element) || !options.tag_name(element.name)){
return false;
}
} else if(key === "tag_type"){
if(!options.tag_type(element.type)) return false;
} else if(key === "tag_contains"){
if(isTag(element) || !options.tag_contains(element.data)){
return false;
}
} else if(!element.attribs || !options[key](element.attribs[key])){
return false;
}
}
return true;
};
var Checks = {
tag_name: function(name){
if(typeof name === "function"){
return function(elem){ return isTag(elem) && name(elem.name); };
} else if(name === "*"){
return isTag;
} else {
return function(elem){ return isTag(elem) && elem.name === name; };
}
},
tag_type: function(type){
if(typeof type === "function"){
return function(elem){ return type(elem.type); };
} else {
return function(elem){ return elem.type === type; };
}
},
tag_contains: function(data){
if(typeof data === "function"){
return function(elem){ return !isTag(elem) && data(elem.data); };
} else {
return function(elem){ return !isTag(elem) && elem.data === data; };
}
}
};
function getAttribCheck(attrib, value){
if(typeof value === "function"){
return function(elem){ return elem.attribs && value(elem.attribs[attrib]); };
} else {
return function(elem){ return elem.attribs && elem.attribs[attrib] === value; };
}
}
function combineFuncs(a, b){
return function(elem){
return a(elem) || b(elem);
};
}
exports.getElements = function(options, element, recurse, limit){
var funcs = Object.keys(options).map(function(key){
var value = options[key];
return key in Checks ? Checks[key](value) : getAttribCheck(key, value);
});
return funcs.length === 0 ? [] : this.filter(
funcs.reduce(combineFuncs),
element, recurse, limit
);
};
exports.getElementById = function(id, element, recurse){
if(!Array.isArray(element)) element = [element];
return this.findOne(getAttribCheck("id", id), element, recurse !== false);
};
exports.getElementsByTagName = function(name, element, recurse, limit){
return this.filter(Checks.tag_name(name), element, recurse, limit);
};
exports.getElementsByTagType = function(type, element, recurse, limit){
return this.filter(Checks.tag_type(type), element, recurse, limit);
};
+77
View File
@@ -0,0 +1,77 @@
exports.removeElement = function(elem){
if(elem.prev) elem.prev.next = elem.next;
if(elem.next) elem.next.prev = elem.prev;
if(elem.parent){
var childs = elem.parent.children;
childs.splice(childs.lastIndexOf(elem), 1);
}
};
exports.replaceElement = function(elem, replacement){
var prev = replacement.prev = elem.prev;
if(prev){
prev.next = replacement;
}
var next = replacement.next = elem.next;
if(next){
next.prev = replacement;
}
var parent = replacement.parent = elem.parent;
if(parent){
var childs = parent.children;
childs[childs.lastIndexOf(elem)] = replacement;
}
};
exports.appendChild = function(elem, child){
child.parent = elem;
if(elem.children.push(child) !== 1){
var sibling = elem.children[elem.children.length - 2];
sibling.next = child;
child.prev = sibling;
child.next = null;
}
};
exports.append = function(elem, next){
var parent = elem.parent,
currNext = elem.next;
next.next = currNext;
next.prev = elem;
elem.next = next;
next.parent = parent;
if(currNext){
currNext.prev = next;
if(parent){
var childs = parent.children;
childs.splice(childs.lastIndexOf(currNext), 0, next);
}
} else if(parent){
parent.children.push(next);
}
};
exports.prepend = function(elem, prev){
var parent = elem.parent;
if(parent){
var childs = parent.children;
childs.splice(childs.lastIndexOf(elem), 0, prev);
}
if(elem.prev){
elem.prev.next = prev;
}
prev.parent = parent;
prev.prev = elem.prev;
prev.next = elem;
elem.prev = prev;
};
+95
View File
@@ -0,0 +1,95 @@
var isTag = require("domelementtype").isTag;
module.exports = {
filter: filter,
find: find,
findOneChild: findOneChild,
findOne: findOne,
existsOne: existsOne,
findAll: findAll
};
function filter(test, element, recurse, limit){
if(!Array.isArray(element)) element = [element];
if(typeof limit !== "number" || !isFinite(limit)){
limit = Infinity;
}
return find(test, element, recurse !== false, limit);
}
function find(test, elems, recurse, limit){
var result = [], childs;
for(var i = 0, j = elems.length; i < j; i++){
if(test(elems[i])){
result.push(elems[i]);
if(--limit <= 0) break;
}
childs = elems[i].children;
if(recurse && childs && childs.length > 0){
childs = find(test, childs, recurse, limit);
result = result.concat(childs);
limit -= childs.length;
if(limit <= 0) break;
}
}
return result;
}
function findOneChild(test, elems){
for(var i = 0, l = elems.length; i < l; i++){
if(test(elems[i])) return elems[i];
}
return null;
}
function findOne(test, elems){
var elem = null;
for(var i = 0, l = elems.length; i < l && !elem; i++){
if(!isTag(elems[i])){
continue;
} else if(test(elems[i])){
elem = elems[i];
} else if(elems[i].children.length > 0){
elem = findOne(test, elems[i].children);
}
}
return elem;
}
function existsOne(test, elems){
for(var i = 0, l = elems.length; i < l; i++){
if(
isTag(elems[i]) && (
test(elems[i]) || (
elems[i].children.length > 0 &&
existsOne(test, elems[i].children)
)
)
){
return true;
}
}
return false;
}
function findAll(test, rootElems){
var result = [];
var stack = rootElems.slice();
while(stack.length){
var elem = stack.shift();
if(!isTag(elem)) continue;
if (elem.children && elem.children.length > 0) {
stack.unshift.apply(stack, elem.children);
}
if(test(elem)) result.push(elem);
}
return result;
}
+23
View File
@@ -0,0 +1,23 @@
var ElementType = require("domelementtype"),
getOuterHTML = require("dom-serializer"),
isTag = ElementType.isTag;
module.exports = {
getInnerHTML: getInnerHTML,
getOuterHTML: getOuterHTML,
getText: getText
};
function getInnerHTML(elem, opts){
return elem.children ? elem.children.map(function(elem){
return getOuterHTML(elem, opts);
}).join("") : "";
}
function getText(elem){
if(Array.isArray(elem)) return elem.map(getText).join("");
if(isTag(elem)) return elem.name === "br" ? "\n" : getText(elem.children);
if(elem.type === ElementType.CDATA) return getText(elem.children);
if(elem.type === ElementType.Text) return elem.data;
return "";
}
+24
View File
@@ -0,0 +1,24 @@
var getChildren = exports.getChildren = function(elem){
return elem.children;
};
var getParent = exports.getParent = function(elem){
return elem.parent;
};
exports.getSiblings = function(elem){
var parent = getParent(elem);
return parent ? getChildren(parent) : [elem];
};
exports.getAttributeValue = function(elem, name){
return elem.attribs && elem.attribs[name];
};
exports.hasAttrib = function(elem, name){
return !!elem.attribs && hasOwnProperty.call(elem.attribs, name);
};
exports.getName = function(elem){
return elem.name;
};
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,15 @@
//Types of elements found in the DOM
module.exports = {
Text: "text", //Text
Directive: "directive", //<? ... ?>
Comment: "comment", //<!-- ... -->
Script: "script", //<script> tags
Style: "style", //<style> tags
Tag: "tag", //Any tag
CDATA: "cdata", //<![CDATA[ ... ]]>
Doctype: "doctype",
isTag: function(elem){
return elem.type === "tag" || elem.type === "script" || elem.type === "style";
}
};
@@ -0,0 +1,51 @@
{
"_args": [
[
"domelementtype@1.3.1",
"/home/node/nuxt"
]
],
"_from": "domelementtype@1.3.1",
"_id": "domelementtype@1.3.1",
"_inBundle": false,
"_integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
"_location": "/svgo/domutils/domelementtype",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "domelementtype@1.3.1",
"name": "domelementtype",
"escapedName": "domelementtype",
"rawSpec": "1.3.1",
"saveSpec": null,
"fetchSpec": "1.3.1"
},
"_requiredBy": [
"/svgo/domutils"
],
"_resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
"_spec": "1.3.1",
"_where": "/home/node/nuxt",
"author": {
"name": "Felix Boehm",
"email": "me@feedic.com"
},
"bugs": {
"url": "https://github.com/fb55/domelementtype/issues"
},
"description": "all the types of nodes in htmlparser2's dom",
"homepage": "https://github.com/fb55/domelementtype#readme",
"keywords": [
"dom",
"htmlparser2"
],
"license": "BSD-2-Clause",
"main": "index.js",
"name": "domelementtype",
"repository": {
"type": "git",
"url": "git://github.com/fb55/domelementtype.git"
},
"version": "1.3.1"
}
@@ -0,0 +1 @@
all the types of nodes in htmlparser2's dom
+82
View File
@@ -0,0 +1,82 @@
{
"_args": [
[
"domutils@1.7.0",
"/home/node/nuxt"
]
],
"_from": "domutils@1.7.0",
"_id": "domutils@1.7.0",
"_inBundle": false,
"_integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
"_location": "/svgo/domutils",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "domutils@1.7.0",
"name": "domutils",
"escapedName": "domutils",
"rawSpec": "1.7.0",
"saveSpec": null,
"fetchSpec": "1.7.0"
},
"_requiredBy": [
"/svgo/css-select"
],
"_resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
"_spec": "1.7.0",
"_where": "/home/node/nuxt",
"author": {
"name": "Felix Boehm",
"email": "me@feedic.com"
},
"bugs": {
"url": "https://github.com/FB55/domutils/issues"
},
"dependencies": {
"dom-serializer": "0",
"domelementtype": "1"
},
"description": "utilities for working with htmlparser2's dom",
"devDependencies": {
"domhandler": "2",
"htmlparser2": "~3.9.2",
"jshint": "~2.9.4",
"mocha": "~3.2.0"
},
"directories": {
"test": "tests"
},
"homepage": "https://github.com/FB55/domutils#readme",
"jshintConfig": {
"proto": true,
"unused": true,
"eqnull": true,
"undef": true,
"quotmark": "double",
"eqeqeq": true,
"trailing": true,
"node": true,
"globals": {
"describe": true,
"it": true,
"beforeEach": true
}
},
"keywords": [
"dom",
"htmlparser2"
],
"license": "BSD-2-Clause",
"main": "index.js",
"name": "domutils",
"repository": {
"type": "git",
"url": "git://github.com/FB55/domutils.git"
},
"scripts": {
"test": "mocha test/tests/**.js && jshint index.js test/**/*.js lib/*.js"
},
"version": "1.7.0"
}
+3
View File
@@ -0,0 +1,3 @@
Utilities for working with htmlparser2's dom
[![Build Status](https://travis-ci.org/fb55/domutils.svg?branch=master)](https://travis-ci.org/fb55/domutils)
+6
View File
@@ -0,0 +1,6 @@
var makeDom = require("./utils").makeDom;
var markup = Array(21).join(
"<?xml><tag1 id='asdf'> <script>text</script> <!-- comment --> <tag2> text </tag1>"
);
module.exports = makeDom(markup);
+89
View File
@@ -0,0 +1,89 @@
var makeDom = require("../utils").makeDom;
var helpers = require("../..");
var assert = require("assert");
describe("helpers", function() {
describe("removeSubsets", function() {
var removeSubsets = helpers.removeSubsets;
var dom = makeDom("<div><p><span></span></p><p></p></div>")[0];
it("removes identical trees", function() {
var matches = removeSubsets([dom, dom]);
assert.equal(matches.length, 1);
});
it("Removes subsets found first", function() {
var matches = removeSubsets([dom, dom.children[0].children[0]]);
assert.equal(matches.length, 1);
});
it("Removes subsets found last", function() {
var matches = removeSubsets([dom.children[0], dom]);
assert.equal(matches.length, 1);
});
it("Does not remove unique trees", function() {
var matches = removeSubsets([dom.children[0], dom.children[1]]);
assert.equal(matches.length, 2);
});
});
describe("compareDocumentPosition", function() {
var compareDocumentPosition = helpers.compareDocumentPosition;
var markup = "<div><p><span></span></p><a></a></div>";
var dom = makeDom(markup)[0];
var p = dom.children[0];
var span = p.children[0];
var a = dom.children[1];
it("reports when the first node occurs before the second indirectly", function() {
assert.equal(compareDocumentPosition(span, a), 2);
});
it("reports when the first node contains the second", function() {
assert.equal(compareDocumentPosition(p, span), 10);
});
it("reports when the first node occurs after the second indirectly", function() {
assert.equal(compareDocumentPosition(a, span), 4);
});
it("reports when the first node is contained by the second", function() {
assert.equal(compareDocumentPosition(span, p), 20);
});
it("reports when the nodes belong to separate documents", function() {
var other = makeDom(markup)[0].children[0].children[0];
assert.equal(compareDocumentPosition(span, other), 1);
});
it("reports when the nodes are identical", function() {
assert.equal(compareDocumentPosition(span, span), 0);
});
});
describe("uniqueSort", function() {
var uniqueSort = helpers.uniqueSort;
var dom, p, span, a;
beforeEach(function() {
dom = makeDom("<div><p><span></span></p><a></a></div>")[0];
p = dom.children[0];
span = p.children[0];
a = dom.children[1];
});
it("leaves unique elements untouched", function() {
assert.deepEqual(uniqueSort([p, a]), [p, a]);
});
it("removes duplicate elements", function() {
assert.deepEqual(uniqueSort([p, a, p]), [p, a]);
});
it("sorts nodes in document order", function() {
assert.deepEqual(uniqueSort([a, dom, span, p]), [dom, p, span, a]);
});
});
});
+119
View File
@@ -0,0 +1,119 @@
var DomUtils = require("../..");
var fixture = require("../fixture");
var assert = require("assert");
// Set up expected structures
var expected = {
idAsdf: fixture[1],
tag2: [],
typeScript: []
};
for (var idx = 0; idx < 20; ++idx) {
expected.tag2.push(fixture[idx*2 + 1].children[5]);
expected.typeScript.push(fixture[idx*2 + 1].children[1]);
}
describe("legacy", function() {
describe("getElements", function() {
var getElements = DomUtils.getElements;
it("returns the node with the specified ID", function() {
assert.deepEqual(
getElements({ id: "asdf" }, fixture, true, 1),
[expected.idAsdf]
);
});
it("returns empty array for unknown IDs", function() {
assert.deepEqual(getElements({ id: "asdfs" }, fixture, true), []);
});
it("returns the nodes with the specified tag name", function() {
assert.deepEqual(
getElements({ tag_name:"tag2" }, fixture, true),
expected.tag2
);
});
it("returns empty array for unknown tag names", function() {
assert.deepEqual(
getElements({ tag_name : "asdfs" }, fixture, true),
[]
);
});
it("returns the nodes with the specified tag type", function() {
assert.deepEqual(
getElements({ tag_type: "script" }, fixture, true),
expected.typeScript
);
});
it("returns empty array for unknown tag types", function() {
assert.deepEqual(
getElements({ tag_type: "video" }, fixture, true),
[]
);
});
});
describe("getElementById", function() {
var getElementById = DomUtils.getElementById;
it("returns the specified node", function() {
assert.equal(
expected.idAsdf,
getElementById("asdf", fixture, true)
);
});
it("returns `null` for unknown IDs", function() {
assert.equal(null, getElementById("asdfs", fixture, true));
});
});
describe("getElementsByTagName", function() {
var getElementsByTagName = DomUtils.getElementsByTagName;
it("returns the specified nodes", function() {
assert.deepEqual(
getElementsByTagName("tag2", fixture, true),
expected.tag2
);
});
it("returns empty array for unknown tag names", function() {
assert.deepEqual(
getElementsByTagName("tag23", fixture, true),
[]
);
});
});
describe("getElementsByTagType", function() {
var getElementsByTagType = DomUtils.getElementsByTagType;
it("returns the specified nodes", function() {
assert.deepEqual(
getElementsByTagType("script", fixture, true),
expected.typeScript
);
});
it("returns empty array for unknown tag types", function() {
assert.deepEqual(
getElementsByTagType("video", fixture, true),
[]
);
});
});
describe("getOuterHTML", function() {
var getOuterHTML = DomUtils.getOuterHTML;
it("Correctly renders the outer HTML", function() {
assert.equal(
getOuterHTML(fixture[1]),
"<tag1 id=\"asdf\"> <script>text</script> <!-- comment --> <tag2> text </tag2></tag1>"
);
});
});
describe("getInnerHTML", function() {
var getInnerHTML = DomUtils.getInnerHTML;
it("Correctly renders the inner HTML", function() {
assert.equal(
getInnerHTML(fixture[1]),
" <script>text</script> <!-- comment --> <tag2> text </tag2>"
);
});
});
});
+17
View File
@@ -0,0 +1,17 @@
var makeDom = require("../utils").makeDom;
var traversal = require("../..");
var assert = require("assert");
describe("traversal", function() {
describe("hasAttrib", function() {
var hasAttrib = traversal.hasAttrib;
it("doesn't throw on text nodes", function() {
var dom = makeDom("textnode");
assert.doesNotThrow(function() {
hasAttrib(dom[0], "some-attrib");
});
});
});
});
+9
View File
@@ -0,0 +1,9 @@
var htmlparser = require("htmlparser2");
exports.makeDom = function(markup) {
var handler = new htmlparser.DomHandler(),
parser = new htmlparser.Parser(handler);
parser.write(markup);
parser.done();
return handler.dom;
};
+11
View File
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+51
View File
@@ -0,0 +1,51 @@
# nth-check [![Build Status](https://travis-ci.org/fb55/nth-check.svg)](https://travis-ci.org/fb55/nth-check)
A performant nth-check parser & compiler.
### About
This module can be used to parse & compile nth-checks, as they are found in CSS 3's `nth-child()` and `nth-last-of-type()`.
`nth-check` focusses on speed, providing optimized functions for different kinds of nth-child formulas, while still following the [spec](http://www.w3.org/TR/css3-selectors/#nth-child-pseudo).
### API
```js
var nthCheck = require("nth-check");
```
##### `nthCheck(formula)`
First parses, then compiles the formula.
##### `nthCheck.parse(formula)`
Parses the expression, throws a `SyntaxError` if it fails, otherwise returns an array containing two elements.
__Example:__
```js
nthCheck.parse("2n+3") //[2, 3]
```
##### `nthCheck.compile([a, b])`
Takes an array with two elements (as returned by `.parse`) and returns a highly optimized function.
If the formula doesn't match any elements, it returns [`boolbase`](https://github.com/fb55/boolbase)'s `falseFunc`, otherwise, a function accepting an _index_ is returned, which returns whether or not a passed _index_ matches the formula. (Note: The spec starts counting at `1`, the returned function at `0`).
__Example:__
```js
var check = nthCheck.compile([2, 3]);
check(0) //false
check(1) //false
check(2) //true
check(3) //false
check(4) //true
check(5) //false
check(6) //true
```
---
License: BSD
+40
View File
@@ -0,0 +1,40 @@
module.exports = compile;
var BaseFuncs = require("boolbase"),
trueFunc = BaseFuncs.trueFunc,
falseFunc = BaseFuncs.falseFunc;
/*
returns a function that checks if an elements index matches the given rule
highly optimized to return the fastest solution
*/
function compile(parsed){
var a = parsed[0],
b = parsed[1] - 1;
//when b <= 0, a*n won't be possible for any matches when a < 0
//besides, the specification says that no element is matched when a and b are 0
if(b < 0 && a <= 0) return falseFunc;
//when a is in the range -1..1, it matches any element (so only b is checked)
if(a ===-1) return function(pos){ return pos <= b; };
if(a === 0) return function(pos){ return pos === b; };
//when b <= 0 and a === 1, they match any element
if(a === 1) return b < 0 ? trueFunc : function(pos){ return pos >= b; };
//when a > 0, modulo can be used to check if there is a match
var bMod = b % a;
if(bMod < 0) bMod += a;
if(a > 1){
return function(pos){
return pos >= b && pos % a === bMod;
};
}
a *= -1; //make `a` positive
return function(pos){
return pos <= b && pos % a === bMod;
};
}
+9
View File
@@ -0,0 +1,9 @@
var parse = require("./parse.js"),
compile = require("./compile.js");
module.exports = function nthCheck(formula){
return compile(parse(formula));
};
module.exports.parse = parse;
module.exports.compile = compile;
+63
View File
@@ -0,0 +1,63 @@
{
"_args": [
[
"nth-check@1.0.2",
"/home/node/nuxt"
]
],
"_from": "nth-check@1.0.2",
"_id": "nth-check@1.0.2",
"_inBundle": false,
"_integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
"_location": "/svgo/nth-check",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "nth-check@1.0.2",
"name": "nth-check",
"escapedName": "nth-check",
"rawSpec": "1.0.2",
"saveSpec": null,
"fetchSpec": "1.0.2"
},
"_requiredBy": [
"/svgo/css-select"
],
"_resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
"_spec": "1.0.2",
"_where": "/home/node/nuxt",
"author": {
"name": "Felix Boehm",
"email": "me@feedic.com"
},
"bugs": {
"url": "https://github.com/fb55/nth-check/issues"
},
"dependencies": {
"boolbase": "~1.0.0"
},
"description": "performant nth-check parser & compiler",
"files": [
"compile.js",
"index.js",
"parse.js"
],
"homepage": "https://github.com/fb55/nth-check",
"keywords": [
"nth-child",
"nth",
"css"
],
"license": "BSD-2-Clause",
"main": "index.js",
"name": "nth-check",
"repository": {
"type": "git",
"url": "git+https://github.com/fb55/nth-check.git"
},
"scripts": {
"test": "node test"
},
"version": "1.0.2"
}
+40
View File
@@ -0,0 +1,40 @@
module.exports = parse;
//following http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
//[ ['-'|'+']? INTEGER? {N} [ S* ['-'|'+'] S* INTEGER ]?
var re_nthElement = /^([+\-]?\d*n)?\s*(?:([+\-]?)\s*(\d+))?$/;
/*
parses a nth-check formula, returns an array of two numbers
*/
function parse(formula){
formula = formula.trim().toLowerCase();
if(formula === "even"){
return [2, 0];
} else if(formula === "odd"){
return [2, 1];
} else {
var parsed = formula.match(re_nthElement);
if(!parsed){
throw new SyntaxError("n-th rule couldn't be parsed ('" + formula + "')");
}
var a;
if(parsed[1]){
a = parseInt(parsed[1], 10);
if(isNaN(a)){
if(parsed[1].charAt(0) === "-") a = -1;
else a = 1;
}
} else a = 0;
return [
a,
parsed[3] ? parseInt((parsed[2] || "") + parsed[3], 10) : 0
];
}
}