node_modules ignore

This commit is contained in:
2025-05-08 23:43:47 +02:00
parent e19d52f172
commit 4574544c9f
65041 changed files with 10593536 additions and 0 deletions

7
server/node_modules/sanitize-html/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,7 @@
Copyright (c) 2013, 2014, 2015 P'unk Avenue LLC
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.

775
server/node_modules/sanitize-html/README.md generated vendored Normal file
View File

@@ -0,0 +1,775 @@
# sanitize-html
<a href="https://apostrophecms.com/"><img src="https://raw.githubusercontent.com/apostrophecms/sanitize-html/main/logos/logo-box-madefor.png" align="right" /></a>
sanitize-html provides a simple HTML sanitizer with a clear API.
sanitize-html is tolerant. It is well suited for cleaning up HTML fragments such as those created by CKEditor and other rich text editors. It is especially handy for removing unwanted CSS when copying and pasting from Word.
sanitize-html allows you to specify the tags you want to permit, and the permitted
attributes for each of those tags. If an attribute is a known non-boolean value,
and it is empty, it will be removed. For example `checked` can be empty, but `href`
cannot.
If a tag is not permitted, the contents of the tag are not discarded. There are
some exceptions to this, discussed below in the "Discarding the entire contents
of a disallowed tag" section.
The syntax of poorly closed `p` and `img` elements is cleaned up.
`href` attributes are validated to ensure they only contain `http`, `https`, `ftp` and `mailto` URLs. Relative URLs are also allowed. Ditto for `src` attributes.
Allowing particular urls as a `src` to an iframe tag by filtering hostnames is also supported.
HTML comments are not preserved.
Additionally, `sanitize-html` escapes _ALL_ text content - this means that ampersands, greater-than, and less-than signs are converted to their equivalent HTML character references (`&` --> `&amp;`, `<` --> `&lt;`, and so on). Additionally, in attribute values, quotation marks are escaped as well (`"` --> `&quot;`).
## Requirements
sanitize-html is intended for use with Node.js and supports Node 10+. All of its npm dependencies are pure JavaScript. sanitize-html is built on the excellent `htmlparser2` module.
### Regarding TypeScript
sanitize-html is not written in TypeScript and there is no plan to directly support it. There is a community supported typing definition, [`@types/sanitize-html`](https://www.npmjs.com/package/@types/sanitize-html), however.
```bash
npm install -D @types/sanitize-html
```
If `esModuleInterop=true` is not set in your `tsconfig.json` file, you have to import it with:
```javascript
import * as sanitizeHtml from 'sanitize-html';
```
When using TypeScript, there is a minimum supported version of >=4.5 because of a dependency on the `htmlparser2` types.
Any questions or problems while using `@types/sanitize-html` should be directed to its maintainers as directed by that project's contribution guidelines.
## How to use
### Browser
*Think first: why do you want to use it in the browser?* Remember, *servers must never trust browsers.* You can't sanitize HTML for saving on the server anywhere else but on the server.
But, perhaps you'd like to display sanitized HTML immediately in the browser for preview. Or ask the browser to do the sanitization work on every page load. You can if you want to!
* Install the package:
```bash
npm install sanitize-html
```
or
```
yarn add sanitize-html
```
The primary change in the 2.x version of sanitize-html is that it no longer includes a build that is ready for browser use. Developers are expected to include sanitize-html in their project builds (e.g., webpack) as they would any other dependency. So while sanitize-html is no longer ready to link to directly in HTML, developers can now more easily process it according to their needs.
Once built and linked in the browser with other project Javascript, it can be used to sanitize HTML strings in front end code:
```javascript
import sanitizeHtml from 'sanitize-html';
const html = "<strong>hello world</strong>";
console.log(sanitizeHtml(html));
console.log(sanitizeHtml("<img src=x onerror=alert('img') />"));
console.log(sanitizeHtml("console.log('hello world')"));
console.log(sanitizeHtml("<script>alert('hello world')</script>"));
```
### Node (Recommended)
Install module from console:
```bash
npm install sanitize-html
```
Import the module:
```js
// In ES modules
import sanitizeHtml from 'sanitize-html';
// Or in CommonJS
const sanitizeHtml = require('sanitize-html');
```
Use it in your JavaScript app:
```js
const dirty = 'some really tacky HTML';
const clean = sanitizeHtml(dirty);
```
That will allow our [default list of allowed tags and attributes](#default-options) through. It's a nice set, but probably not quite what you want. So:
```js
// Allow only a super restricted set of tags and attributes
const clean = sanitizeHtml(dirty, {
allowedTags: [ 'b', 'i', 'em', 'strong', 'a' ],
allowedAttributes: {
'a': [ 'href' ]
},
allowedIframeHostnames: ['www.youtube.com']
});
```
Boom!
### Default options
```js
allowedTags: [
"address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
"h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
"dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
"ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
"em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
"small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
"col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr"
],
nonBooleanAttributes: [
'abbr', 'accept', 'accept-charset', 'accesskey', 'action',
'allow', 'alt', 'as', 'autocapitalize', 'autocomplete',
'blocking', 'charset', 'cite', 'class', 'color', 'cols',
'colspan', 'content', 'contenteditable', 'coords', 'crossorigin',
'data', 'datetime', 'decoding', 'dir', 'dirname', 'download',
'draggable', 'enctype', 'enterkeyhint', 'fetchpriority', 'for',
'form', 'formaction', 'formenctype', 'formmethod', 'formtarget',
'headers', 'height', 'hidden', 'high', 'href', 'hreflang',
'http-equiv', 'id', 'imagesizes', 'imagesrcset', 'inputmode',
'integrity', 'is', 'itemid', 'itemprop', 'itemref', 'itemtype',
'kind', 'label', 'lang', 'list', 'loading', 'low', 'max',
'maxlength', 'media', 'method', 'min', 'minlength', 'name',
'nonce', 'optimum', 'pattern', 'ping', 'placeholder', 'popover',
'popovertarget', 'popovertargetaction', 'poster', 'preload',
'referrerpolicy', 'rel', 'rows', 'rowspan', 'sandbox', 'scope',
'shape', 'size', 'sizes', 'slot', 'span', 'spellcheck', 'src',
'srcdoc', 'srclang', 'srcset', 'start', 'step', 'style',
'tabindex', 'target', 'title', 'translate', 'type', 'usemap',
'value', 'width', 'wrap',
// Event handlers
'onauxclick', 'onafterprint', 'onbeforematch', 'onbeforeprint',
'onbeforeunload', 'onbeforetoggle', 'onblur', 'oncancel',
'oncanplay', 'oncanplaythrough', 'onchange', 'onclick', 'onclose',
'oncontextlost', 'oncontextmenu', 'oncontextrestored', 'oncopy',
'oncuechange', 'oncut', 'ondblclick', 'ondrag', 'ondragend',
'ondragenter', 'ondragleave', 'ondragover', 'ondragstart',
'ondrop', 'ondurationchange', 'onemptied', 'onended',
'onerror', 'onfocus', 'onformdata', 'onhashchange', 'oninput',
'oninvalid', 'onkeydown', 'onkeypress', 'onkeyup',
'onlanguagechange', 'onload', 'onloadeddata', 'onloadedmetadata',
'onloadstart', 'onmessage', 'onmessageerror', 'onmousedown',
'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout',
'onmouseover', 'onmouseup', 'onoffline', 'ononline', 'onpagehide',
'onpageshow', 'onpaste', 'onpause', 'onplay', 'onplaying',
'onpopstate', 'onprogress', 'onratechange', 'onreset', 'onresize',
'onrejectionhandled', 'onscroll', 'onscrollend',
'onsecuritypolicyviolation', 'onseeked', 'onseeking', 'onselect',
'onslotchange', 'onstalled', 'onstorage', 'onsubmit', 'onsuspend',
'ontimeupdate', 'ontoggle', 'onunhandledrejection', 'onunload',
'onvolumechange', 'onwaiting', 'onwheel'
],
disallowedTagsMode: 'discard',
allowedAttributes: {
a: [ 'href', 'name', 'target' ],
// We don't currently allow img itself by default, but
// these attributes would make sense if we did.
img: [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ]
},
// Lots of these won't come up by default because we don't allow them
selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ],
// URL schemes we permit
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto', 'tel' ],
allowedSchemesByTag: {},
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
allowProtocolRelative: true,
enforceHtmlBoundary: false,
parseStyleAttributes: true
```
### Common use cases
#### "I like your set but I want to add one more tag. Is there a convenient way?"
Sure:
```js
const clean = sanitizeHtml(dirty, {
allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'img' ])
});
```
If you do not specify `allowedTags` or `allowedAttributes`, our default list is applied. So if you really want an empty list, specify one.
#### "What if I want to allow all tags or all attributes?"
Simple! Instead of leaving `allowedTags` or `allowedAttributes` out of the options, set either
one or both to `false`:
```js
allowedTags: false,
allowedAttributes: false
```
#### "What if I want to allow empty attributes, even for cases like href that normally don't make sense?"
Very simple! Set `nonBooleanAttributes` to `[]`.
```js
nonBooleanAttributes: []
```
#### "What if I want to remove all empty attributes, including valid ones?"
Also very simple! Set `nonBooleanAttributes` to `['*']`.
**Note**: This will break common valid cases like `checked` and `selected`, so this is
unlikely to be what you want. For most ordinary HTML use, it is best to avoid making
this change.
```js
nonBooleanAttributes: ['*']
```
#### "What if I don't want to allow *any* tags?"
Also simple! Set `allowedTags` to `[]` and `allowedAttributes` to `{}`.
```js
allowedTags: [],
allowedAttributes: {}
```
#### "What if I want disallowed tags to be escaped rather than discarded?"
If you set `disallowedTagsMode` to `discard` (the default), disallowed tags are discarded. Any text content or subtags are still included, depending on whether the individual subtags are allowed.
If you set `disallowedTagsMode` to `completelyDiscard`, disallowed tags and any content they contain are discarded. Any subtags are still included, as long as those individual subtags are allowed.
If you set `disallowedTagsMode` to `escape`, the disallowed tags are escaped rather than discarded. Any text or subtags are handled normally.
If you set `disallowedTagsMode` to `recursiveEscape`, the disallowed tags are escaped rather than discarded, and the same treatment is applied to all subtags, whether otherwise allowed or not.
#### "What if I want to allow only specific values on some attributes?"
When configuring the attribute in `allowedAttributes` simply use an object with attribute `name` and an allowed `values` array. In the following example `sandbox="allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-scripts"` would become `sandbox="allow-popups allow-scripts"`:
```js
allowedAttributes: {
iframe: [
{
name: 'sandbox',
multiple: true,
values: ['allow-popups', 'allow-same-origin', 'allow-scripts']
}
]
}
```
With `multiple: true`, several allowed values may appear in the same attribute, separated by spaces. Otherwise the attribute must exactly match one and only one of the allowed values.
#### "What if I want to maintain the original case for SVG elements and attributes?"
If you're incorporating SVG elements like `linearGradient` into your content and notice that they're not rendering as expected due to case sensitivity issues, it's essential to prevent `sanitize-html` from converting element and attribute names to lowercase. This situation often arises when SVGs fail to display correctly because their case-sensitive tags, such as `linearGradient` and attributes like `viewBox`, are inadvertently lowercased.
To address this, ensure you set `lowerCaseTags: false` and `lowerCaseAttributeNames: false` in the parser options of your sanitize-html configuration. This adjustment stops the library from altering the case of your tags and attributes, preserving the integrity of your SVG content.
```js
allowedTags: [ 'svg', 'g', 'defs', 'linearGradient', 'stop', 'circle' ],
allowedAttributes: false,
parser: {
lowerCaseTags: false,
lowerCaseAttributeNames: false
}
```
### Wildcards for attributes
You can use the `*` wildcard to allow all attributes with a certain prefix:
```js
allowedAttributes: {
a: [ 'href', 'data-*' ]
}
```
Also you can use the `*` as name for a tag, to allow listed attributes to be valid for any tag:
```js
allowedAttributes: {
'*': [ 'href', 'align', 'alt', 'center', 'bgcolor' ]
}
```
## Additional options
### Allowed CSS Classes
If you wish to allow specific CSS classes on a particular element, you can do so with the `allowedClasses` option. Any other CSS classes are discarded.
This implies that the `class` attribute is allowed on that element.
```javascript
// Allow only a restricted set of CSS classes and only on the p tag
const clean = sanitizeHtml(dirty, {
allowedTags: [ 'p', 'em', 'strong' ],
allowedClasses: {
'p': [ 'fancy', 'simple' ]
}
});
```
Similar to `allowedAttributes`, you can use `*` to allow classes with a certain prefix, or use `*` as a tag name to allow listed classes to be valid for any tag:
```js
allowedClasses: {
'code': [ 'language-*', 'lang-*' ],
'*': [ 'fancy', 'simple' ]
}
```
Furthermore, regular expressions are supported too:
```js
allowedClasses: {
p: [ /^regex\d{2}$/ ]
}
```
If `allowedClasses` for a certain tag is `false`, all the classes for this tag will be allowed.
> Note: It is advised that your regular expressions always begin with `^` so that you are requiring a known prefix. A regular expression with neither `^` nor `$` just requires that something appear in the middle.
### Allowed CSS Styles
If you wish to allow specific CSS _styles_ on a particular element, you can do that with the `allowedStyles` option. Simply declare your desired attributes as regular expression options within an array for the given attribute. Specific elements will inherit allowlisted attributes from the global (`*`) attribute. Any other CSS classes are discarded.
**You must also use `allowedAttributes`** to activate the `style` attribute for the relevant elements. Otherwise this feature will never come into play.
**When constructing regular expressions, don't forget `^` and `$`.** It's not enough to say "the string should contain this." It must also say "and only this."
**URLs in inline styles are NOT filtered by any mechanism other than your regular expression.**
```javascript
const clean = sanitizeHtml(dirty, {
allowedTags: ['p'],
allowedAttributes: {
'p': ["style"],
},
allowedStyles: {
'*': {
// Match HEX and RGB
'color': [/^#(0x)?[0-9a-f]+$/i, /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/],
'text-align': [/^left$/, /^right$/, /^center$/],
// Match any number with px, em, or %
'font-size': [/^\d+(?:px|em|%)$/]
},
'p': {
'font-size': [/^\d+rem$/]
}
}
});
```
### Discarding text outside of ```<html></html>``` tags
Some text editing applications generate HTML to allow copying over to a web application. These can sometimes include undesirable control characters after terminating `html` tag. By default sanitize-html will not discard these characters, instead returning them in sanitized string. This behaviour can be modified using `enforceHtmlBoundary` option.
Setting this option to true will instruct sanitize-html to discard all characters outside of `html` tag boundaries -- before `<html>` and after `</html>` tags.
```js
enforceHtmlBoundary: true
```
### htmlparser2 Options
sanitize-html is built on `htmlparser2`. By default the only option passed down is `decodeEntities: true`. You can set the options to pass by using the parser option.
**Security note: changing the `parser` settings can be risky.** In particular, `decodeEntities: false` has known security concerns and a complete test suite does not exist for every possible combination of settings when used with `sanitize-html`. If security is your goal we recommend you use the defaults rather than changing `parser`, except for the `lowerCaseTags` option.
```javascript
const clean = sanitizeHtml(dirty, {
allowedTags: ['a'],
parser: {
lowerCaseTags: true
}
});
```
See the [htmlparser2 wiki](https://github.com/fb55/htmlparser2/wiki/Parser-options) for the full list of possible options.
### Transformations
What if you want to add or change an attribute? What if you want to transform one tag to another? No problem, it's simple!
The easiest way (will change all `ol` tags to `ul` tags):
```js
const clean = sanitizeHtml(dirty, {
transformTags: {
'ol': 'ul',
}
});
```
The most advanced usage:
```js
const clean = sanitizeHtml(dirty, {
transformTags: {
'ol': function(tagName, attribs) {
// My own custom magic goes here
return {
tagName: 'ul',
attribs: {
class: 'foo'
}
};
}
}
});
```
You can specify the `*` wildcard instead of a tag name to transform all tags.
There is also a helper method which should be enough for simple cases in which you want to change the tag and/or add some attributes:
```js
const clean = sanitizeHtml(dirty, {
transformTags: {
'ol': sanitizeHtml.simpleTransform('ul', {class: 'foo'}),
}
});
```
The `simpleTransform` helper method has 3 parameters:
```js
simpleTransform(newTag, newAttributes, shouldMerge)
```
The last parameter (`shouldMerge`) is set to `true` by default. When `true`, `simpleTransform` will merge the current attributes with the new ones (`newAttributes`). When `false`, all existing attributes are discarded.
You can also add or modify the text contents of a tag:
```js
const clean = sanitizeHtml(dirty, {
transformTags: {
'a': function(tagName, attribs) {
return {
tagName: 'a',
text: 'Some text'
};
}
}
});
```
For example, you could transform a link element with missing anchor text:
```js
<a href="http://somelink.com"></a>
```
To a link with anchor text:
```js
<a href="http://somelink.com">Some text</a>
```
### Filters
You can provide a filter function to remove unwanted tags. Let's suppose we need to remove empty `a` tags like:
```html
<a href="page.html"></a>
```
We can do that with the following filter:
```js
sanitizeHtml(
'<p>This is <a href="http://www.linux.org"></a><br/>Linux</p>',
{
exclusiveFilter: function(frame) {
return frame.tag === 'a' && !frame.text.trim();
}
}
);
```
The `frame` object supplied to the callback provides the following attributes:
- `tag`: The tag name, i.e. `'img'`.
- `attribs`: The tag's attributes, i.e. `{ src: "/path/to/tux.png" }`.
- `text`: The text content of the tag.
- `mediaChildren`: Immediate child tags that are likely to represent self-contained media (e.g., `img`, `video`, `picture`, `iframe`). See the `mediaTags` variable in `src/index.js` for the full list.
- `tagPosition`: The index of the tag's position in the result string.
You can also process all text content with a provided filter function. Let's say we want an ellipsis instead of three dots.
```html
<p>some text...</p>
```
We can do that with the following filter:
```js
sanitizeHtml(
'<p>some text...</p>',
{
textFilter: function(text, tagName) {
if (['a'].indexOf(tagName) > -1) return //Skip anchor tags
return text.replace(/\.\.\./, '&hellip;');
}
}
);
```
Note that the text passed to the `textFilter` method is already escaped for safe display as HTML. You may add markup and use entity escape sequences in your `textFilter`.
### Iframe Filters
If you would like to allow iframe tags but want to control the domains that are allowed through, you can provide an array of hostnames and/or array of domains that you would like to allow as iframe sources. This hostname is a property in the options object passed as an argument to the sanitize-html function.
These arrays will be checked against the html that is passed to the function and return only `src` urls that include the allowed hostnames or domains in the object. The url in the html that is passed must be formatted correctly (valid hostname) as an embedded iframe otherwise the module will strip out the src from the iframe.
Make sure to pass a valid hostname along with the domain you wish to allow, i.e.:
```js
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
allowedIframeDomains: ['zoom.us']
```
You may also specify whether or not to allow relative URLs as iframe sources.
```js
allowIframeRelativeUrls: true
```
Note that if unspecified, relative URLs will be allowed by default if no hostname or domain filter is provided but removed by default if a hostname or domain filter is provided.
**Remember that the `iframe` tag must be allowed as well as the `src` attribute.**
For example:
```javascript
const clean = sanitizeHtml('<p><iframe src="https://www.youtube.com/embed/nykIhs12345"></iframe><p>', {
allowedTags: [ 'p', 'em', 'strong', 'iframe' ],
allowedClasses: {
'p': [ 'fancy', 'simple' ],
},
allowedAttributes: {
'iframe': ['src']
},
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com']
});
```
will pass through as safe whereas:
```javascript
const clean = sanitizeHtml('<p><iframe src="https://www.youtube.net/embed/nykIhs12345"></iframe><p>', {
allowedTags: [ 'p', 'em', 'strong', 'iframe' ],
allowedClasses: {
'p': [ 'fancy', 'simple' ],
},
allowedAttributes: {
'iframe': ['src']
},
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com']
});
```
or
```javascript
const clean = sanitizeHtml('<p><iframe src="https://www.vimeo/video/12345"></iframe><p>', {
allowedTags: [ 'p', 'em', 'strong', 'iframe' ],
allowedClasses: {
'p': [ 'fancy', 'simple' ],
},
allowedAttributes: {
'iframe': ['src']
},
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com']
});
```
will return an empty iframe tag.
If you want to allow any subdomain of any level you can provide the domain in `allowedIframeDomains`
```javascript
// This iframe markup will pass through as safe.
const clean = sanitizeHtml('<p><iframe src="https://us02web.zoom.us/embed/12345"></iframe><p>', {
allowedTags: [ 'p', 'em', 'strong', 'iframe' ],
allowedClasses: {
'p': [ 'fancy', 'simple' ],
},
allowedAttributes: {
'iframe': ['src']
},
allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'],
allowedIframeDomains: ['zoom.us']
});
```
### Script Filters
Similarly to iframes you can allow a script tag on a list of allowlisted domains
```js
const clean = sanitizeHtml('<script src="https://www.safe.authorized.com/lib.js"></script>', {
allowedTags: ['script'],
allowedAttributes: {
script: ['src']
},
allowedScriptDomains: ['authorized.com'],
})
```
You can allow a script tag on a list of allowlisted hostnames too
```js
const clean = sanitizeHtml('<script src="https://www.authorized.com/lib.js"></script>', {
allowedTags: ['script'],
allowedAttributes: {
script: ['src']
},
allowedScriptHostnames: [ 'www.authorized.com' ],
})
```
### Allowed URL schemes
By default, we allow the following URL schemes in cases where `href`, `src`, etc. are allowed:
```js
[ 'http', 'https', 'ftp', 'mailto' ]
```
You can override this if you want to:
```js
sanitizeHtml(
// teeny-tiny valid transparent GIF in a data URL
'<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" />',
{
allowedTags: [ 'img', 'p' ],
allowedSchemes: [ 'data', 'http' ]
}
);
```
You can also allow a scheme for a particular tag only:
```js
allowedSchemes: [ 'http', 'https' ],
allowedSchemesByTag: {
img: [ 'data' ]
}
```
And you can forbid the use of protocol-relative URLs (starting with `//`) to access another site using the current protocol, which is allowed by default:
```js
allowProtocolRelative: false
```
### Discarding the entire contents of a disallowed tag
Normally, with a few exceptions, if a tag is not allowed, all of the text within it is preserved, and so are any allowed tags within it.
The exceptions are:
`style`, `script`, `textarea`, `option`
If you wish to replace this list, for instance to discard whatever is found
inside a `noscript` tag, use the `nonTextTags` option:
```js
nonTextTags: [ 'style', 'script', 'textarea', 'option', 'noscript' ]
```
Note that if you use this option you are responsible for stating the entire list. This gives you the power to retain the content of `textarea`, if you want to.
The content still gets escaped properly, with the exception of the `script` and
`style` tags. *Allowing either `script` or `style` leaves you open to XSS
attacks. Don't do that* unless you have good reason to trust their origin.
sanitize-html will log a warning if these tags are allowed, which can be
disabled with the `allowVulnerableTags: true` option.
### Choose what to do with disallowed tags
Instead of discarding, or keeping text only, you may enable escaping of the entire content:
```js
disallowedTagsMode: 'escape'
```
This will transform `<disallowed>content</disallowed>` to `&lt;disallowed&gt;content&lt;/disallowed&gt;`
Valid values are: `'discard'` (default), `'completelyDiscard'` (remove disallowed tag's content), `'escape'` (escape the tag) and `'recursiveEscape'` (to escape the tag and all its content).
#### Discard disallowed but but the inner content of disallowed tags is kept.
If you set `disallowedTagsMode` to `discard`, disallowed tags are discarded but but the inner content of disallowed tags is kept.
```js
disallowedTagsMode: 'discard'
```
This will transform `<disallowed>content</disallowed>` to `content`
#### Discard entire content of a disallowed tag
If you set `disallowedTagsMode` to `completelyDiscard`, disallowed tags and any content they contain are discarded. Any subtags are still included, as long as those individual subtags are allowed.
```js
disallowedTagsMode: 'completelyDiscard'
```
This will transform `<disallowed>content <allowed>content</allowed> </disallowed>` to `<allowed>content</allowed>`
#### Escape the disallowed tag and all its children even for allowed tags.
if you set `disallowedTagsMode` to `recursiveEscape`, disallowed tag and its children will be escaped even for allowed tags
```js
disallowedTagsMode: `recursiveEscape`
```
This will transform `<disallowed>hello<p>world</p></disallowed>` to `&lt;disallowed&gt;hello&lt;p&gt;world&lt;/p&gt;&lt;/disallowed&gt;`
### Ignore style attribute contents
Instead of discarding faulty style attributes, you can allow them by disabling the parsing of style attributes:
```js
parseStyleAttributes: false
```
This will transform `<div style="invalid-prop: non-existing-value">content</div>` to `<div style="invalid-prop: non-existing-value">content</div>` instead of stripping it: `<div>content</div>`
By default the parseStyleAttributes option is true.
When you disable parsing of the style attribute (`parseStyleAttributes: false`) and you pass in options for the allowedStyles property, an error will be thrown. This combination is not permitted.
we recommend sanitizing content server-side in a Node.js environment, as you cannot trust a browser to sanitize things anyway. Consider what a malicious user could do via the network panel,
the browser console, or just by writing scripts that submit content similar to what your JavaScript submits. But if you really need to run it on the client in the browser,
you may find you need to disable parseStyleAttributes. This is subject to change as it is [an upstream issue with postcss](https://github.com/postcss/postcss/issues/1727), not sanitize-html itself.
### Restricting deep nesting
You can limit the depth of HTML tags in the document with the `nestingLimit` option:
```javascript
nestingLimit: 6
```
This will prevent the user from nesting tags more than 6 levels deep. Tags deeper than that are stripped out exactly as if they were disallowed. Note that this means text is preserved in the usual ways where appropriate.
## About ApostropheCMS
sanitize-html was created at [P'unk Avenue](https://punkave.com) for use in [ApostropheCMS](https://apostrophecms.com), an open-source content management system built on Node.js. If you like sanitize-html you should definitely check out ApostropheCMS.
## Support
Feel free to open issues on [github](https://github.com/apostrophecms/sanitize-html).

918
server/node_modules/sanitize-html/index.js generated vendored Normal file
View File

@@ -0,0 +1,918 @@
const htmlparser = require('htmlparser2');
const escapeStringRegexp = require('escape-string-regexp');
const { isPlainObject } = require('is-plain-object');
const deepmerge = require('deepmerge');
const parseSrcset = require('parse-srcset');
const { parse: postcssParse } = require('postcss');
// Tags that can conceivably represent stand-alone media.
const mediaTags = [
'img', 'audio', 'video', 'picture', 'svg',
'object', 'map', 'iframe', 'embed'
];
// Tags that are inherently vulnerable to being used in XSS attacks.
const vulnerableTags = [ 'script', 'style' ];
function each(obj, cb) {
if (obj) {
Object.keys(obj).forEach(function (key) {
cb(obj[key], key);
});
}
}
// Avoid false positives with .__proto__, .hasOwnProperty, etc.
function has(obj, key) {
return ({}).hasOwnProperty.call(obj, key);
}
// Returns those elements of `a` for which `cb(a)` returns truthy
function filter(a, cb) {
const n = [];
each(a, function(v) {
if (cb(v)) {
n.push(v);
}
});
return n;
}
function isEmptyObject(obj) {
for (const key in obj) {
if (has(obj, key)) {
return false;
}
}
return true;
}
function stringifySrcset(parsedSrcset) {
return parsedSrcset.map(function(part) {
if (!part.url) {
throw new Error('URL missing');
}
return (
part.url +
(part.w ? ` ${part.w}w` : '') +
(part.h ? ` ${part.h}h` : '') +
(part.d ? ` ${part.d}x` : '')
);
}).join(', ');
}
module.exports = sanitizeHtml;
// A valid attribute name.
// We use a tolerant definition based on the set of strings defined by
// html.spec.whatwg.org/multipage/parsing.html#before-attribute-name-state
// and html.spec.whatwg.org/multipage/parsing.html#attribute-name-state .
// The characters accepted are ones which can be appended to the attribute
// name buffer without triggering a parse error:
// * unexpected-equals-sign-before-attribute-name
// * unexpected-null-character
// * unexpected-character-in-attribute-name
// We exclude the empty string because it's impossible to get to the after
// attribute name state with an empty attribute name buffer.
const VALID_HTML_ATTRIBUTE_NAME = /^[^\0\t\n\f\r /<=>]+$/;
// Ignore the _recursing flag; it's there for recursive
// invocation as a guard against this exploit:
// https://github.com/fb55/htmlparser2/issues/105
function sanitizeHtml(html, options, _recursing) {
if (html == null) {
return '';
}
if (typeof html === 'number') {
html = html.toString();
}
let result = '';
// Used for hot swapping the result variable with an empty string in order to "capture" the text written to it.
let tempResult = '';
function Frame(tag, attribs) {
const that = this;
this.tag = tag;
this.attribs = attribs || {};
this.tagPosition = result.length;
this.text = ''; // Node inner text
this.mediaChildren = [];
this.updateParentNodeText = function() {
if (stack.length) {
const parentFrame = stack[stack.length - 1];
parentFrame.text += that.text;
}
};
this.updateParentNodeMediaChildren = function() {
if (stack.length && mediaTags.includes(this.tag)) {
const parentFrame = stack[stack.length - 1];
parentFrame.mediaChildren.push(this.tag);
}
};
}
options = Object.assign({}, sanitizeHtml.defaults, options);
options.parser = Object.assign({}, htmlParserDefaults, options.parser);
const tagAllowed = function (name) {
return options.allowedTags === false || (options.allowedTags || []).indexOf(name) > -1;
};
// vulnerableTags
vulnerableTags.forEach(function (tag) {
if (tagAllowed(tag) && !options.allowVulnerableTags) {
console.warn(`\n\n⚠️ Your \`allowedTags\` option includes, \`${tag}\`, which is inherently\nvulnerable to XSS attacks. Please remove it from \`allowedTags\`.\nOr, to disable this warning, add the \`allowVulnerableTags\` option\nand ensure you are accounting for this risk.\n\n`);
}
});
// Tags that contain something other than HTML, or where discarding
// the text when the tag is disallowed makes sense for other reasons.
// If we are not allowing these tags, we should drop their content too.
// For other tags you would drop the tag but keep its content.
const nonTextTagsArray = options.nonTextTags || [
'script',
'style',
'textarea',
'option'
];
let allowedAttributesMap;
let allowedAttributesGlobMap;
if (options.allowedAttributes) {
allowedAttributesMap = {};
allowedAttributesGlobMap = {};
each(options.allowedAttributes, function(attributes, tag) {
allowedAttributesMap[tag] = [];
const globRegex = [];
attributes.forEach(function(obj) {
if (typeof obj === 'string' && obj.indexOf('*') >= 0) {
globRegex.push(escapeStringRegexp(obj).replace(/\\\*/g, '.*'));
} else {
allowedAttributesMap[tag].push(obj);
}
});
if (globRegex.length) {
allowedAttributesGlobMap[tag] = new RegExp('^(' + globRegex.join('|') + ')$');
}
});
}
const allowedClassesMap = {};
const allowedClassesGlobMap = {};
const allowedClassesRegexMap = {};
each(options.allowedClasses, function(classes, tag) {
// Implicitly allows the class attribute
if (allowedAttributesMap) {
if (!has(allowedAttributesMap, tag)) {
allowedAttributesMap[tag] = [];
}
allowedAttributesMap[tag].push('class');
}
allowedClassesMap[tag] = classes;
if (Array.isArray(classes)) {
const globRegex = [];
allowedClassesMap[tag] = [];
allowedClassesRegexMap[tag] = [];
classes.forEach(function(obj) {
if (typeof obj === 'string' && obj.indexOf('*') >= 0) {
globRegex.push(escapeStringRegexp(obj).replace(/\\\*/g, '.*'));
} else if (obj instanceof RegExp) {
allowedClassesRegexMap[tag].push(obj);
} else {
allowedClassesMap[tag].push(obj);
}
});
if (globRegex.length) {
allowedClassesGlobMap[tag] = new RegExp('^(' + globRegex.join('|') + ')$');
}
}
});
const transformTagsMap = {};
let transformTagsAll;
each(options.transformTags, function(transform, tag) {
let transFun;
if (typeof transform === 'function') {
transFun = transform;
} else if (typeof transform === 'string') {
transFun = sanitizeHtml.simpleTransform(transform);
}
if (tag === '*') {
transformTagsAll = transFun;
} else {
transformTagsMap[tag] = transFun;
}
});
let depth;
let stack;
let skipMap;
let transformMap;
let skipText;
let skipTextDepth;
let addedText = false;
initializeState();
const parser = new htmlparser.Parser({
onopentag: function(name, attribs) {
// If `enforceHtmlBoundary` is `true` and this has found the opening
// `html` tag, reset the state.
if (options.enforceHtmlBoundary && name === 'html') {
initializeState();
}
if (skipText) {
skipTextDepth++;
return;
}
const frame = new Frame(name, attribs);
stack.push(frame);
let skip = false;
const hasText = !!frame.text;
let transformedTag;
if (has(transformTagsMap, name)) {
transformedTag = transformTagsMap[name](name, attribs);
frame.attribs = attribs = transformedTag.attribs;
if (transformedTag.text !== undefined) {
frame.innerText = transformedTag.text;
}
if (name !== transformedTag.tagName) {
frame.name = name = transformedTag.tagName;
transformMap[depth] = transformedTag.tagName;
}
}
if (transformTagsAll) {
transformedTag = transformTagsAll(name, attribs);
frame.attribs = attribs = transformedTag.attribs;
if (name !== transformedTag.tagName) {
frame.name = name = transformedTag.tagName;
transformMap[depth] = transformedTag.tagName;
}
}
if (!tagAllowed(name) || (options.disallowedTagsMode === 'recursiveEscape' && !isEmptyObject(skipMap)) || (options.nestingLimit != null && depth >= options.nestingLimit)) {
skip = true;
skipMap[depth] = true;
if (options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') {
if (nonTextTagsArray.indexOf(name) !== -1) {
skipText = true;
skipTextDepth = 1;
}
}
skipMap[depth] = true;
}
depth++;
if (skip) {
if (options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') {
// We want the contents but not this tag
return;
}
tempResult = result;
result = '';
}
result += '<' + name;
if (name === 'script') {
if (options.allowedScriptHostnames || options.allowedScriptDomains) {
frame.innerText = '';
}
}
if (!allowedAttributesMap || has(allowedAttributesMap, name) || allowedAttributesMap['*']) {
each(attribs, function(value, a) {
if (!VALID_HTML_ATTRIBUTE_NAME.test(a)) {
// This prevents part of an attribute name in the output from being
// interpreted as the end of an attribute, or end of a tag.
delete frame.attribs[a];
return;
}
// If the value is empty, check if the attribute is in the allowedEmptyAttributes array.
// If it is not in the allowedEmptyAttributes array, and it is a known non-boolean attribute, delete it
// List taken from https://html.spec.whatwg.org/multipage/indices.html#attributes-3
if (value === '' && (!options.allowedEmptyAttributes.includes(a)) &&
(options.nonBooleanAttributes.includes(a) || options.nonBooleanAttributes.includes('*'))) {
delete frame.attribs[a];
return;
}
// check allowedAttributesMap for the element and attribute and modify the value
// as necessary if there are specific values defined.
let passedAllowedAttributesMapCheck = false;
if (!allowedAttributesMap ||
(has(allowedAttributesMap, name) && allowedAttributesMap[name].indexOf(a) !== -1) ||
(allowedAttributesMap['*'] && allowedAttributesMap['*'].indexOf(a) !== -1) ||
(has(allowedAttributesGlobMap, name) && allowedAttributesGlobMap[name].test(a)) ||
(allowedAttributesGlobMap['*'] && allowedAttributesGlobMap['*'].test(a))) {
passedAllowedAttributesMapCheck = true;
} else if (allowedAttributesMap && allowedAttributesMap[name]) {
for (const o of allowedAttributesMap[name]) {
if (isPlainObject(o) && o.name && (o.name === a)) {
passedAllowedAttributesMapCheck = true;
let newValue = '';
if (o.multiple === true) {
// verify the values that are allowed
const splitStrArray = value.split(' ');
for (const s of splitStrArray) {
if (o.values.indexOf(s) !== -1) {
if (newValue === '') {
newValue = s;
} else {
newValue += ' ' + s;
}
}
}
} else if (o.values.indexOf(value) >= 0) {
// verified an allowed value matches the entire attribute value
newValue = value;
}
value = newValue;
}
}
}
if (passedAllowedAttributesMapCheck) {
if (options.allowedSchemesAppliedToAttributes.indexOf(a) !== -1) {
if (naughtyHref(name, value)) {
delete frame.attribs[a];
return;
}
}
if (name === 'script' && a === 'src') {
let allowed = true;
try {
const parsed = parseUrl(value);
if (options.allowedScriptHostnames || options.allowedScriptDomains) {
const allowedHostname = (options.allowedScriptHostnames || []).find(function (hostname) {
return hostname === parsed.url.hostname;
});
const allowedDomain = (options.allowedScriptDomains || []).find(function(domain) {
return parsed.url.hostname === domain || parsed.url.hostname.endsWith(`.${domain}`);
});
allowed = allowedHostname || allowedDomain;
}
} catch (e) {
allowed = false;
}
if (!allowed) {
delete frame.attribs[a];
return;
}
}
if (name === 'iframe' && a === 'src') {
let allowed = true;
try {
const parsed = parseUrl(value);
if (parsed.isRelativeUrl) {
// default value of allowIframeRelativeUrls is true
// unless allowedIframeHostnames or allowedIframeDomains specified
allowed = has(options, 'allowIframeRelativeUrls')
? options.allowIframeRelativeUrls
: (!options.allowedIframeHostnames && !options.allowedIframeDomains);
} else if (options.allowedIframeHostnames || options.allowedIframeDomains) {
const allowedHostname = (options.allowedIframeHostnames || []).find(function (hostname) {
return hostname === parsed.url.hostname;
});
const allowedDomain = (options.allowedIframeDomains || []).find(function(domain) {
return parsed.url.hostname === domain || parsed.url.hostname.endsWith(`.${domain}`);
});
allowed = allowedHostname || allowedDomain;
}
} catch (e) {
// Unparseable iframe src
allowed = false;
}
if (!allowed) {
delete frame.attribs[a];
return;
}
}
if (a === 'srcset') {
try {
let parsed = parseSrcset(value);
parsed.forEach(function(value) {
if (naughtyHref('srcset', value.url)) {
value.evil = true;
}
});
parsed = filter(parsed, function(v) {
return !v.evil;
});
if (!parsed.length) {
delete frame.attribs[a];
return;
} else {
value = stringifySrcset(filter(parsed, function(v) {
return !v.evil;
}));
frame.attribs[a] = value;
}
} catch (e) {
// Unparseable srcset
delete frame.attribs[a];
return;
}
}
if (a === 'class') {
const allowedSpecificClasses = allowedClassesMap[name];
const allowedWildcardClasses = allowedClassesMap['*'];
const allowedSpecificClassesGlob = allowedClassesGlobMap[name];
const allowedSpecificClassesRegex = allowedClassesRegexMap[name];
const allowedWildcardClassesGlob = allowedClassesGlobMap['*'];
const allowedClassesGlobs = [
allowedSpecificClassesGlob,
allowedWildcardClassesGlob
]
.concat(allowedSpecificClassesRegex)
.filter(function (t) {
return t;
});
if (allowedSpecificClasses && allowedWildcardClasses) {
value = filterClasses(value, deepmerge(allowedSpecificClasses, allowedWildcardClasses), allowedClassesGlobs);
} else {
value = filterClasses(value, allowedSpecificClasses || allowedWildcardClasses, allowedClassesGlobs);
}
if (!value.length) {
delete frame.attribs[a];
return;
}
}
if (a === 'style') {
if (options.parseStyleAttributes) {
try {
const abstractSyntaxTree = postcssParse(name + ' {' + value + '}', { map: false });
const filteredAST = filterCss(abstractSyntaxTree, options.allowedStyles);
value = stringifyStyleAttributes(filteredAST);
if (value.length === 0) {
delete frame.attribs[a];
return;
}
} catch (e) {
if (typeof window !== 'undefined') {
console.warn('Failed to parse "' + name + ' {' + value + '}' + '", If you\'re running this in a browser, we recommend to disable style parsing: options.parseStyleAttributes: false, since this only works in a node environment due to a postcss dependency, More info: https://github.com/apostrophecms/sanitize-html/issues/547');
}
delete frame.attribs[a];
return;
}
} else if (options.allowedStyles) {
throw new Error('allowedStyles option cannot be used together with parseStyleAttributes: false.');
}
}
result += ' ' + a;
if (value && value.length) {
result += '="' + escapeHtml(value, true) + '"';
} else if (options.allowedEmptyAttributes.includes(a)) {
result += '=""';
}
} else {
delete frame.attribs[a];
}
});
}
if (options.selfClosing.indexOf(name) !== -1) {
result += ' />';
} else {
result += '>';
if (frame.innerText && !hasText && !options.textFilter) {
result += escapeHtml(frame.innerText);
addedText = true;
}
}
if (skip) {
result = tempResult + escapeHtml(result);
tempResult = '';
}
},
ontext: function(text) {
if (skipText) {
return;
}
const lastFrame = stack[stack.length - 1];
let tag;
if (lastFrame) {
tag = lastFrame.tag;
// If inner text was set by transform function then let's use it
text = lastFrame.innerText !== undefined ? lastFrame.innerText : text;
}
if (options.disallowedTagsMode === 'completelyDiscard' && !tagAllowed(tag)) {
text = '';
} else if ((options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') && ((tag === 'script') || (tag === 'style'))) {
// htmlparser2 gives us these as-is. Escaping them ruins the content. Allowing
// script tags is, by definition, game over for XSS protection, so if that's
// your concern, don't allow them. The same is essentially true for style tags
// which have their own collection of XSS vectors.
result += text;
} else {
const escaped = escapeHtml(text, false);
if (options.textFilter && !addedText) {
result += options.textFilter(escaped, tag);
} else if (!addedText) {
result += escaped;
}
}
if (stack.length) {
const frame = stack[stack.length - 1];
frame.text += text;
}
},
onclosetag: function(name, isImplied) {
if (skipText) {
skipTextDepth--;
if (!skipTextDepth) {
skipText = false;
} else {
return;
}
}
const frame = stack.pop();
if (!frame) {
// Do not crash on bad markup
return;
}
if (frame.tag !== name) {
// Another case of bad markup.
// Push to stack, so that it will be used in future closing tags.
stack.push(frame);
return;
}
skipText = options.enforceHtmlBoundary ? name === 'html' : false;
depth--;
const skip = skipMap[depth];
if (skip) {
delete skipMap[depth];
if (options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') {
frame.updateParentNodeText();
return;
}
tempResult = result;
result = '';
}
if (transformMap[depth]) {
name = transformMap[depth];
delete transformMap[depth];
}
if (options.exclusiveFilter && options.exclusiveFilter(frame)) {
result = result.substr(0, frame.tagPosition);
return;
}
frame.updateParentNodeMediaChildren();
frame.updateParentNodeText();
if (
// Already output />
options.selfClosing.indexOf(name) !== -1 ||
// Escaped tag, closing tag is implied
(isImplied && !tagAllowed(name) && [ 'escape', 'recursiveEscape' ].indexOf(options.disallowedTagsMode) >= 0)
) {
if (skip) {
result = tempResult;
tempResult = '';
}
return;
}
result += '</' + name + '>';
if (skip) {
result = tempResult + escapeHtml(result);
tempResult = '';
}
addedText = false;
}
}, options.parser);
parser.write(html);
parser.end();
return result;
function initializeState() {
result = '';
depth = 0;
stack = [];
skipMap = {};
transformMap = {};
skipText = false;
skipTextDepth = 0;
}
function escapeHtml(s, quote) {
if (typeof (s) !== 'string') {
s = s + '';
}
if (options.parser.decodeEntities) {
s = s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
if (quote) {
s = s.replace(/"/g, '&quot;');
}
}
// TODO: this is inadequate because it will pass `&0;`. This approach
// will not work, each & must be considered with regard to whether it
// is followed by a 100% syntactically valid entity or not, and escaped
// if it is not. If this bothers you, don't set parser.decodeEntities
// to false. (The default is true.)
s = s.replace(/&(?![a-zA-Z0-9#]{1,20};)/g, '&amp;') // Match ampersands not part of existing HTML entity
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
if (quote) {
s = s.replace(/"/g, '&quot;');
}
return s;
}
function naughtyHref(name, href) {
// Browsers ignore character codes of 32 (space) and below in a surprising
// number of situations. Start reading here:
// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet#Embedded_tab
// eslint-disable-next-line no-control-regex
href = href.replace(/[\x00-\x20]+/g, '');
// Clobber any comments in URLs, which the browser might
// interpret inside an XML data island, allowing
// a javascript: URL to be snuck through
while (true) {
const firstIndex = href.indexOf('<!--');
if (firstIndex === -1) {
break;
}
const lastIndex = href.indexOf('-->', firstIndex + 4);
if (lastIndex === -1) {
break;
}
href = href.substring(0, firstIndex) + href.substring(lastIndex + 3);
}
// Case insensitive so we don't get faked out by JAVASCRIPT #1
// Allow more characters after the first so we don't get faked
// out by certain schemes browsers accept
const matches = href.match(/^([a-zA-Z][a-zA-Z0-9.\-+]*):/);
if (!matches) {
// Protocol-relative URL starting with any combination of '/' and '\'
if (href.match(/^[/\\]{2}/)) {
return !options.allowProtocolRelative;
}
// No scheme
return false;
}
const scheme = matches[1].toLowerCase();
if (has(options.allowedSchemesByTag, name)) {
return options.allowedSchemesByTag[name].indexOf(scheme) === -1;
}
return !options.allowedSchemes || options.allowedSchemes.indexOf(scheme) === -1;
}
function parseUrl(value) {
value = value.replace(/^(\w+:)?\s*[\\/]\s*[\\/]/, '$1//');
if (value.startsWith('relative:')) {
// An attempt to exploit our workaround for base URLs being
// mandatory for relative URL validation in the WHATWG
// URL parser, reject it
throw new Error('relative: exploit attempt');
}
// naughtyHref is in charge of whether protocol relative URLs
// are cool. Here we are concerned just with allowed hostnames and
// whether to allow relative URLs.
//
// Build a placeholder "base URL" against which any reasonable
// relative URL may be parsed successfully
let base = 'relative://relative-site';
for (let i = 0; (i < 100); i++) {
base += `/${i}`;
}
const parsed = new URL(value, base);
const isRelativeUrl = parsed && parsed.hostname === 'relative-site' && parsed.protocol === 'relative:';
return {
isRelativeUrl,
url: parsed
};
}
/**
* Filters user input css properties by allowlisted regex attributes.
* Modifies the abstractSyntaxTree object.
*
* @param {object} abstractSyntaxTree - Object representation of CSS attributes.
* @property {array[Declaration]} abstractSyntaxTree.nodes[0] - Each object cointains prop and value key, i.e { prop: 'color', value: 'red' }.
* @param {object} allowedStyles - Keys are properties (i.e color), value is list of permitted regex rules (i.e /green/i).
* @return {object} - The modified tree.
*/
function filterCss(abstractSyntaxTree, allowedStyles) {
if (!allowedStyles) {
return abstractSyntaxTree;
}
const astRules = abstractSyntaxTree.nodes[0];
let selectedRule;
// Merge global and tag-specific styles into new AST.
if (allowedStyles[astRules.selector] && allowedStyles['*']) {
selectedRule = deepmerge(
allowedStyles[astRules.selector],
allowedStyles['*']
);
} else {
selectedRule = allowedStyles[astRules.selector] || allowedStyles['*'];
}
if (selectedRule) {
abstractSyntaxTree.nodes[0].nodes = astRules.nodes.reduce(filterDeclarations(selectedRule), []);
}
return abstractSyntaxTree;
}
/**
* Extracts the style attributes from an AbstractSyntaxTree and formats those
* values in the inline style attribute format.
*
* @param {AbstractSyntaxTree} filteredAST
* @return {string} - Example: "color:yellow;text-align:center !important;font-family:helvetica;"
*/
function stringifyStyleAttributes(filteredAST) {
return filteredAST.nodes[0].nodes
.reduce(function(extractedAttributes, attrObject) {
extractedAttributes.push(
`${attrObject.prop}:${attrObject.value}${attrObject.important ? ' !important' : ''}`
);
return extractedAttributes;
}, [])
.join(';');
}
/**
* Filters the existing attributes for the given property. Discards any attributes
* which don't match the allowlist.
*
* @param {object} selectedRule - Example: { color: red, font-family: helvetica }
* @param {array} allowedDeclarationsList - List of declarations which pass the allowlist.
* @param {object} attributeObject - Object representing the current css property.
* @property {string} attributeObject.type - Typically 'declaration'.
* @property {string} attributeObject.prop - The CSS property, i.e 'color'.
* @property {string} attributeObject.value - The corresponding value to the css property, i.e 'red'.
* @return {function} - When used in Array.reduce, will return an array of Declaration objects
*/
function filterDeclarations(selectedRule) {
return function (allowedDeclarationsList, attributeObject) {
// If this property is allowlisted...
if (has(selectedRule, attributeObject.prop)) {
const matchesRegex = selectedRule[attributeObject.prop].some(function(regularExpression) {
return regularExpression.test(attributeObject.value);
});
if (matchesRegex) {
allowedDeclarationsList.push(attributeObject);
}
}
return allowedDeclarationsList;
};
}
function filterClasses(classes, allowed, allowedGlobs) {
if (!allowed) {
// The class attribute is allowed without filtering on this tag
return classes;
}
classes = classes.split(/\s+/);
return classes.filter(function(clss) {
return allowed.indexOf(clss) !== -1 || allowedGlobs.some(function(glob) {
return glob.test(clss);
});
}).join(' ');
}
}
// Defaults are accessible to you so that you can use them as a starting point
// programmatically if you wish
const htmlParserDefaults = {
decodeEntities: true
};
sanitizeHtml.defaults = {
allowedTags: [
// Sections derived from MDN element categories and limited to the more
// benign categories.
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
// Content sectioning
'address', 'article', 'aside', 'footer', 'header',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hgroup',
'main', 'nav', 'section',
// Text content
'blockquote', 'dd', 'div', 'dl', 'dt', 'figcaption', 'figure',
'hr', 'li', 'main', 'ol', 'p', 'pre', 'ul',
// Inline text semantics
'a', 'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'dfn',
'em', 'i', 'kbd', 'mark', 'q',
'rb', 'rp', 'rt', 'rtc', 'ruby',
's', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'time', 'u', 'var', 'wbr',
// Table content
'caption', 'col', 'colgroup', 'table', 'tbody', 'td', 'tfoot', 'th',
'thead', 'tr'
],
// Tags that cannot be boolean
nonBooleanAttributes: [
'abbr', 'accept', 'accept-charset', 'accesskey', 'action',
'allow', 'alt', 'as', 'autocapitalize', 'autocomplete',
'blocking', 'charset', 'cite', 'class', 'color', 'cols',
'colspan', 'content', 'contenteditable', 'coords', 'crossorigin',
'data', 'datetime', 'decoding', 'dir', 'dirname', 'download',
'draggable', 'enctype', 'enterkeyhint', 'fetchpriority', 'for',
'form', 'formaction', 'formenctype', 'formmethod', 'formtarget',
'headers', 'height', 'hidden', 'high', 'href', 'hreflang',
'http-equiv', 'id', 'imagesizes', 'imagesrcset', 'inputmode',
'integrity', 'is', 'itemid', 'itemprop', 'itemref', 'itemtype',
'kind', 'label', 'lang', 'list', 'loading', 'low', 'max',
'maxlength', 'media', 'method', 'min', 'minlength', 'name',
'nonce', 'optimum', 'pattern', 'ping', 'placeholder', 'popover',
'popovertarget', 'popovertargetaction', 'poster', 'preload',
'referrerpolicy', 'rel', 'rows', 'rowspan', 'sandbox', 'scope',
'shape', 'size', 'sizes', 'slot', 'span', 'spellcheck', 'src',
'srcdoc', 'srclang', 'srcset', 'start', 'step', 'style',
'tabindex', 'target', 'title', 'translate', 'type', 'usemap',
'value', 'width', 'wrap',
// Event handlers
'onauxclick', 'onafterprint', 'onbeforematch', 'onbeforeprint',
'onbeforeunload', 'onbeforetoggle', 'onblur', 'oncancel',
'oncanplay', 'oncanplaythrough', 'onchange', 'onclick', 'onclose',
'oncontextlost', 'oncontextmenu', 'oncontextrestored', 'oncopy',
'oncuechange', 'oncut', 'ondblclick', 'ondrag', 'ondragend',
'ondragenter', 'ondragleave', 'ondragover', 'ondragstart',
'ondrop', 'ondurationchange', 'onemptied', 'onended',
'onerror', 'onfocus', 'onformdata', 'onhashchange', 'oninput',
'oninvalid', 'onkeydown', 'onkeypress', 'onkeyup',
'onlanguagechange', 'onload', 'onloadeddata', 'onloadedmetadata',
'onloadstart', 'onmessage', 'onmessageerror', 'onmousedown',
'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout',
'onmouseover', 'onmouseup', 'onoffline', 'ononline', 'onpagehide',
'onpageshow', 'onpaste', 'onpause', 'onplay', 'onplaying',
'onpopstate', 'onprogress', 'onratechange', 'onreset', 'onresize',
'onrejectionhandled', 'onscroll', 'onscrollend',
'onsecuritypolicyviolation', 'onseeked', 'onseeking', 'onselect',
'onslotchange', 'onstalled', 'onstorage', 'onsubmit', 'onsuspend',
'ontimeupdate', 'ontoggle', 'onunhandledrejection', 'onunload',
'onvolumechange', 'onwaiting', 'onwheel'
],
disallowedTagsMode: 'discard',
allowedAttributes: {
a: [ 'href', 'name', 'target' ],
// We don't currently allow img itself by default, but
// these attributes would make sense if we did.
img: [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ]
},
allowedEmptyAttributes: [
'alt'
],
// Lots of these won't come up by default because we don't allow them
selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ],
// URL schemes we permit
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto', 'tel' ],
allowedSchemesByTag: {},
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
allowProtocolRelative: true,
enforceHtmlBoundary: false,
parseStyleAttributes: true
};
sanitizeHtml.simpleTransform = function(newTagName, newAttribs, merge) {
merge = (merge === undefined) ? true : merge;
newAttribs = newAttribs || {};
return function(tagName, attribs) {
let attrib;
if (merge) {
for (attrib in newAttribs) {
attribs[attrib] = newAttribs[attrib];
}
} else {
attribs = newAttribs;
}
return {
tagName: newTagName,
attribs: attribs
};
};
};

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.

View File

@@ -0,0 +1,109 @@
# dom-serializer [![Build Status](https://travis-ci.com/cheeriojs/dom-serializer.svg?branch=master)](https://travis-ci.com/cheeriojs/dom-serializer)
Renders a [domhandler](https://github.com/fb55/domhandler) DOM node or an array of domhandler DOM nodes to a string.
```js
import render from "dom-serializer";
// OR
const render = require("dom-serializer").default;
```
# API
## `render`
**render**(`node`: Node \| Node[], `options?`: [_Options_](#Options)): _string_
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).
#### Parameters:
| Name | Type | Default value | Description |
| :-------- | :--------------------------------- | :------------ | :----------------------------- |
| `node` | Node \| Node[] | - | Node to be rendered. |
| `options` | [_DomSerializerOptions_](#Options) | {} | Changes serialization behavior |
**Returns:** _string_
## Options
### `encodeEntities`
`Optional` **decodeEntities**: _boolean | "utf8"_
Encode characters that are either reserved in HTML or XML.
If `xmlMode` is `true` or the value not `'utf8'`, characters outside of the utf8 range will be encoded as well.
**`default`** `decodeEntities`
---
### `decodeEntities`
`Optional` **decodeEntities**: _boolean_
Option inherited from parsing; will be used as the default value for `encodeEntities`.
**`default`** true
---
### `emptyAttrs`
`Optional` **emptyAttrs**: _boolean_
Print an empty attribute's value.
**`default`** xmlMode
**`example`** With <code>emptyAttrs: false</code>: <code>&lt;input checked&gt;</code>
**`example`** With <code>emptyAttrs: true</code>: <code>&lt;input checked=""&gt;</code>
---
### `selfClosingTags`
`Optional` **selfClosingTags**: _boolean_
Print self-closing tags for tags without contents.
**`default`** xmlMode
**`example`** With <code>selfClosingTags: false</code>: <code>&lt;foo&gt;&lt;/foo&gt;</code>
**`example`** With <code>selfClosingTags: true</code>: <code>&lt;foo /&gt;</code>
---
### `xmlMode`
`Optional` **xmlMode**: _boolean_ \| _"foreign"_
Treat the input as an XML document; enables the `emptyAttrs` and `selfClosingTags` options.
If the value is `"foreign"`, it will try to correct mixed-case attribute names.
**`default`** false
---
## Ecosystem
| Name | Description |
| ------------------------------------------------------------- | ------------------------------------------------------- |
| [htmlparser2](https://github.com/fb55/htmlparser2) | Fast & forgiving HTML/XML parser |
| [domhandler](https://github.com/fb55/domhandler) | Handler for htmlparser2 that turns documents into a DOM |
| [domutils](https://github.com/fb55/domutils) | Utilities for working with domhandler's DOM |
| [css-select](https://github.com/fb55/css-select) | CSS selector engine, compatible with domhandler's DOM |
| [cheerio](https://github.com/cheeriojs/cheerio) | The jQuery API for domhandler's DOM |
| [dom-serializer](https://github.com/cheeriojs/dom-serializer) | Serializer for domhandler's DOM |
---
LICENSE: MIT

View File

@@ -0,0 +1,3 @@
export declare const elementNames: Map<string, string>;
export declare const attributeNames: Map<string, string>;
//# sourceMappingURL=foreignNames.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"foreignNames.d.ts","sourceRoot":"","sources":["../../src/foreignNames.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,qBAwCxB,CAAC;AACF,eAAO,MAAM,cAAc,qBA8D1B,CAAC"}

View File

@@ -0,0 +1,100 @@
export const elementNames = new Map([
"altGlyph",
"altGlyphDef",
"altGlyphItem",
"animateColor",
"animateMotion",
"animateTransform",
"clipPath",
"feBlend",
"feColorMatrix",
"feComponentTransfer",
"feComposite",
"feConvolveMatrix",
"feDiffuseLighting",
"feDisplacementMap",
"feDistantLight",
"feDropShadow",
"feFlood",
"feFuncA",
"feFuncB",
"feFuncG",
"feFuncR",
"feGaussianBlur",
"feImage",
"feMerge",
"feMergeNode",
"feMorphology",
"feOffset",
"fePointLight",
"feSpecularLighting",
"feSpotLight",
"feTile",
"feTurbulence",
"foreignObject",
"glyphRef",
"linearGradient",
"radialGradient",
"textPath",
].map((val) => [val.toLowerCase(), val]));
export const attributeNames = new Map([
"definitionURL",
"attributeName",
"attributeType",
"baseFrequency",
"baseProfile",
"calcMode",
"clipPathUnits",
"diffuseConstant",
"edgeMode",
"filterUnits",
"glyphRef",
"gradientTransform",
"gradientUnits",
"kernelMatrix",
"kernelUnitLength",
"keyPoints",
"keySplines",
"keyTimes",
"lengthAdjust",
"limitingConeAngle",
"markerHeight",
"markerUnits",
"markerWidth",
"maskContentUnits",
"maskUnits",
"numOctaves",
"pathLength",
"patternContentUnits",
"patternTransform",
"patternUnits",
"pointsAtX",
"pointsAtY",
"pointsAtZ",
"preserveAlpha",
"preserveAspectRatio",
"primitiveUnits",
"refX",
"refY",
"repeatCount",
"repeatDur",
"requiredExtensions",
"requiredFeatures",
"specularConstant",
"specularExponent",
"spreadMethod",
"startOffset",
"stdDeviation",
"stitchTiles",
"surfaceScale",
"systemLanguage",
"tableValues",
"targetX",
"targetY",
"textLength",
"viewBox",
"viewTarget",
"xChannelSelector",
"yChannelSelector",
"zoomAndPan",
].map((val) => [val.toLowerCase(), val]));

View File

@@ -0,0 +1,52 @@
import type { AnyNode } from "domhandler";
export interface DomSerializerOptions {
/**
* Print an empty attribute's value.
*
* @default xmlMode
* @example With <code>emptyAttrs: false</code>: <code>&lt;input checked&gt;</code>
* @example With <code>emptyAttrs: true</code>: <code>&lt;input checked=""&gt;</code>
*/
emptyAttrs?: boolean;
/**
* Print self-closing tags for tags without contents.
*
* @default xmlMode
* @example With <code>selfClosingTags: false</code>: <code>&lt;foo&gt;&lt;/foo&gt;</code>
* @example With <code>selfClosingTags: true</code>: <code>&lt;foo /&gt;</code>
*/
selfClosingTags?: boolean;
/**
* Treat the input as an XML document; enables the `emptyAttrs` and `selfClosingTags` options.
*
* If the value is `"foreign"`, it will try to correct mixed-case attribute names.
*
* @default false
*/
xmlMode?: boolean | "foreign";
/**
* Encode characters that are either reserved in HTML or XML.
*
* If `xmlMode` is `true` or the value not `'utf8'`, characters outside of the utf8 range will be encoded as well.
*
* @default `decodeEntities`
*/
encodeEntities?: boolean | "utf8";
/**
* Option inherited from parsing; will be used as the default value for `encodeEntities`.
*
* @default true
*/
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 node Node to be rendered.
* @param options Changes serialization behavior
*/
export declare function render(node: AnyNode | ArrayLike<AnyNode>, options?: DomSerializerOptions): string;
export default render;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EAMR,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AA4ED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,EAClC,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAUR;AAED,eAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,190 @@
/*
* Module dependencies
*/
import * as ElementType from "domelementtype";
import { encodeXML, escapeAttribute, escapeText } from "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
*/
import { elementNames, attributeNames } from "./foreignNames.js";
const unencodedElements = new Set([
"style",
"script",
"xmp",
"iframe",
"noembed",
"noframes",
"plaintext",
"noscript",
]);
function replaceQuotes(value) {
return value.replace(/"/g, "&quot;");
}
/**
* Format attributes
*/
function formatAttributes(attributes, opts) {
var _a;
if (!attributes)
return;
const encode = ((_a = opts.encodeEntities) !== null && _a !== void 0 ? _a : opts.decodeEntities) === false
? replaceQuotes
: opts.xmlMode || opts.encodeEntities !== "utf8"
? encodeXML
: escapeAttribute;
return Object.keys(attributes)
.map((key) => {
var _a, _b;
const value = (_a = attributes[key]) !== null && _a !== void 0 ? _a : "";
if (opts.xmlMode === "foreign") {
/* Fix up mixed-case attribute names */
key = (_b = attributeNames.get(key)) !== null && _b !== void 0 ? _b : key;
}
if (!opts.emptyAttrs && !opts.xmlMode && value === "") {
return key;
}
return `${key}="${encode(value)}"`;
})
.join(" ");
}
/**
* Self-enclosing tags
*/
const singleTag = new Set([
"area",
"base",
"basefont",
"br",
"col",
"command",
"embed",
"frame",
"hr",
"img",
"input",
"isindex",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr",
]);
/**
* 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 node Node to be rendered.
* @param options Changes serialization behavior
*/
export function render(node, options = {}) {
const nodes = "length" in node ? node : [node];
let output = "";
for (let i = 0; i < nodes.length; i++) {
output += renderNode(nodes[i], options);
}
return output;
}
export default render;
function renderNode(node, options) {
switch (node.type) {
case ElementType.Root:
return render(node.children, options);
// @ts-expect-error We don't use `Doctype` yet
case ElementType.Doctype:
case ElementType.Directive:
return renderDirective(node);
case ElementType.Comment:
return renderComment(node);
case ElementType.CDATA:
return renderCdata(node);
case ElementType.Script:
case ElementType.Style:
case ElementType.Tag:
return renderTag(node, options);
case ElementType.Text:
return renderText(node, options);
}
}
const foreignModeIntegrationPoints = new Set([
"mi",
"mo",
"mn",
"ms",
"mtext",
"annotation-xml",
"foreignObject",
"desc",
"title",
]);
const foreignElements = new Set(["svg", "math"]);
function renderTag(elem, opts) {
var _a;
// Handle SVG / MathML in HTML
if (opts.xmlMode === "foreign") {
/* Fix up mixed-case element names */
elem.name = (_a = elementNames.get(elem.name)) !== null && _a !== void 0 ? _a : elem.name;
/* Exit foreign mode at integration points */
if (elem.parent &&
foreignModeIntegrationPoints.has(elem.parent.name)) {
opts = { ...opts, xmlMode: false };
}
}
if (!opts.xmlMode && foreignElements.has(elem.name)) {
opts = { ...opts, xmlMode: "foreign" };
}
let tag = `<${elem.name}`;
const attribs = formatAttributes(elem.attribs, opts);
if (attribs) {
tag += ` ${attribs}`;
}
if (elem.children.length === 0 &&
(opts.xmlMode
? // In XML mode or foreign mode, and user hasn't explicitly turned off self-closing tags
opts.selfClosingTags !== false
: // User explicitly asked for self-closing tags, even in HTML mode
opts.selfClosingTags && singleTag.has(elem.name))) {
if (!opts.xmlMode)
tag += " ";
tag += "/>";
}
else {
tag += ">";
if (elem.children.length > 0) {
tag += render(elem.children, opts);
}
if (opts.xmlMode || !singleTag.has(elem.name)) {
tag += `</${elem.name}>`;
}
}
return tag;
}
function renderDirective(elem) {
return `<${elem.data}>`;
}
function renderText(elem, opts) {
var _a;
let data = elem.data || "";
// If entities weren't decoded, no need to encode them back
if (((_a = opts.encodeEntities) !== null && _a !== void 0 ? _a : opts.decodeEntities) !== false &&
!(!opts.xmlMode &&
elem.parent &&
unencodedElements.has(elem.parent.name))) {
data =
opts.xmlMode || opts.encodeEntities !== "utf8"
? encodeXML(data)
: escapeText(data);
}
return data;
}
function renderCdata(elem) {
return `<![CDATA[${elem.children[0].data}]]>`;
}
function renderComment(elem) {
return `<!--${elem.data}-->`;
}

View File

@@ -0,0 +1 @@
{"type":"module"}

View File

@@ -0,0 +1,3 @@
export declare const elementNames: Map<string, string>;
export declare const attributeNames: Map<string, string>;
//# sourceMappingURL=foreignNames.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"foreignNames.d.ts","sourceRoot":"","sources":["../src/foreignNames.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,qBAwCxB,CAAC;AACF,eAAO,MAAM,cAAc,qBA8D1B,CAAC"}

View File

@@ -0,0 +1,103 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.attributeNames = exports.elementNames = void 0;
exports.elementNames = new Map([
"altGlyph",
"altGlyphDef",
"altGlyphItem",
"animateColor",
"animateMotion",
"animateTransform",
"clipPath",
"feBlend",
"feColorMatrix",
"feComponentTransfer",
"feComposite",
"feConvolveMatrix",
"feDiffuseLighting",
"feDisplacementMap",
"feDistantLight",
"feDropShadow",
"feFlood",
"feFuncA",
"feFuncB",
"feFuncG",
"feFuncR",
"feGaussianBlur",
"feImage",
"feMerge",
"feMergeNode",
"feMorphology",
"feOffset",
"fePointLight",
"feSpecularLighting",
"feSpotLight",
"feTile",
"feTurbulence",
"foreignObject",
"glyphRef",
"linearGradient",
"radialGradient",
"textPath",
].map(function (val) { return [val.toLowerCase(), val]; }));
exports.attributeNames = new Map([
"definitionURL",
"attributeName",
"attributeType",
"baseFrequency",
"baseProfile",
"calcMode",
"clipPathUnits",
"diffuseConstant",
"edgeMode",
"filterUnits",
"glyphRef",
"gradientTransform",
"gradientUnits",
"kernelMatrix",
"kernelUnitLength",
"keyPoints",
"keySplines",
"keyTimes",
"lengthAdjust",
"limitingConeAngle",
"markerHeight",
"markerUnits",
"markerWidth",
"maskContentUnits",
"maskUnits",
"numOctaves",
"pathLength",
"patternContentUnits",
"patternTransform",
"patternUnits",
"pointsAtX",
"pointsAtY",
"pointsAtZ",
"preserveAlpha",
"preserveAspectRatio",
"primitiveUnits",
"refX",
"refY",
"repeatCount",
"repeatDur",
"requiredExtensions",
"requiredFeatures",
"specularConstant",
"specularExponent",
"spreadMethod",
"startOffset",
"stdDeviation",
"stitchTiles",
"surfaceScale",
"systemLanguage",
"tableValues",
"targetX",
"targetY",
"textLength",
"viewBox",
"viewTarget",
"xChannelSelector",
"yChannelSelector",
"zoomAndPan",
].map(function (val) { return [val.toLowerCase(), val]; }));

View File

@@ -0,0 +1,52 @@
import type { AnyNode } from "domhandler";
export interface DomSerializerOptions {
/**
* Print an empty attribute's value.
*
* @default xmlMode
* @example With <code>emptyAttrs: false</code>: <code>&lt;input checked&gt;</code>
* @example With <code>emptyAttrs: true</code>: <code>&lt;input checked=""&gt;</code>
*/
emptyAttrs?: boolean;
/**
* Print self-closing tags for tags without contents.
*
* @default xmlMode
* @example With <code>selfClosingTags: false</code>: <code>&lt;foo&gt;&lt;/foo&gt;</code>
* @example With <code>selfClosingTags: true</code>: <code>&lt;foo /&gt;</code>
*/
selfClosingTags?: boolean;
/**
* Treat the input as an XML document; enables the `emptyAttrs` and `selfClosingTags` options.
*
* If the value is `"foreign"`, it will try to correct mixed-case attribute names.
*
* @default false
*/
xmlMode?: boolean | "foreign";
/**
* Encode characters that are either reserved in HTML or XML.
*
* If `xmlMode` is `true` or the value not `'utf8'`, characters outside of the utf8 range will be encoded as well.
*
* @default `decodeEntities`
*/
encodeEntities?: boolean | "utf8";
/**
* Option inherited from parsing; will be used as the default value for `encodeEntities`.
*
* @default true
*/
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 node Node to be rendered.
* @param options Changes serialization behavior
*/
export declare function render(node: AnyNode | ArrayLike<AnyNode>, options?: DomSerializerOptions): string;
export default render;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EAMR,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAClC;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AA4ED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,EAClC,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAUR;AAED,eAAe,MAAM,CAAC"}

View File

@@ -0,0 +1,229 @@
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.render = void 0;
/*
* Module dependencies
*/
var ElementType = __importStar(require("domelementtype"));
var entities_1 = 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_js_1 = require("./foreignNames.js");
var unencodedElements = new Set([
"style",
"script",
"xmp",
"iframe",
"noembed",
"noframes",
"plaintext",
"noscript",
]);
function replaceQuotes(value) {
return value.replace(/"/g, "&quot;");
}
/**
* Format attributes
*/
function formatAttributes(attributes, opts) {
var _a;
if (!attributes)
return;
var encode = ((_a = opts.encodeEntities) !== null && _a !== void 0 ? _a : opts.decodeEntities) === false
? replaceQuotes
: opts.xmlMode || opts.encodeEntities !== "utf8"
? entities_1.encodeXML
: entities_1.escapeAttribute;
return Object.keys(attributes)
.map(function (key) {
var _a, _b;
var value = (_a = attributes[key]) !== null && _a !== void 0 ? _a : "";
if (opts.xmlMode === "foreign") {
/* Fix up mixed-case attribute names */
key = (_b = foreignNames_js_1.attributeNames.get(key)) !== null && _b !== void 0 ? _b : key;
}
if (!opts.emptyAttrs && !opts.xmlMode && value === "") {
return key;
}
return "".concat(key, "=\"").concat(encode(value), "\"");
})
.join(" ");
}
/**
* Self-enclosing tags
*/
var singleTag = new Set([
"area",
"base",
"basefont",
"br",
"col",
"command",
"embed",
"frame",
"hr",
"img",
"input",
"isindex",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr",
]);
/**
* 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 node Node to be rendered.
* @param options Changes serialization behavior
*/
function render(node, options) {
if (options === void 0) { options = {}; }
var nodes = "length" in node ? node : [node];
var output = "";
for (var i = 0; i < nodes.length; i++) {
output += renderNode(nodes[i], options);
}
return output;
}
exports.render = render;
exports.default = render;
function renderNode(node, options) {
switch (node.type) {
case ElementType.Root:
return render(node.children, options);
// @ts-expect-error We don't use `Doctype` yet
case ElementType.Doctype:
case ElementType.Directive:
return renderDirective(node);
case ElementType.Comment:
return renderComment(node);
case ElementType.CDATA:
return renderCdata(node);
case ElementType.Script:
case ElementType.Style:
case ElementType.Tag:
return renderTag(node, options);
case ElementType.Text:
return renderText(node, options);
}
}
var foreignModeIntegrationPoints = new Set([
"mi",
"mo",
"mn",
"ms",
"mtext",
"annotation-xml",
"foreignObject",
"desc",
"title",
]);
var foreignElements = new Set(["svg", "math"]);
function renderTag(elem, opts) {
var _a;
// Handle SVG / MathML in HTML
if (opts.xmlMode === "foreign") {
/* Fix up mixed-case element names */
elem.name = (_a = foreignNames_js_1.elementNames.get(elem.name)) !== null && _a !== void 0 ? _a : elem.name;
/* Exit foreign mode at integration points */
if (elem.parent &&
foreignModeIntegrationPoints.has(elem.parent.name)) {
opts = __assign(__assign({}, opts), { xmlMode: false });
}
}
if (!opts.xmlMode && foreignElements.has(elem.name)) {
opts = __assign(__assign({}, opts), { xmlMode: "foreign" });
}
var tag = "<".concat(elem.name);
var attribs = formatAttributes(elem.attribs, opts);
if (attribs) {
tag += " ".concat(attribs);
}
if (elem.children.length === 0 &&
(opts.xmlMode
? // In XML mode or foreign mode, and user hasn't explicitly turned off self-closing tags
opts.selfClosingTags !== false
: // User explicitly asked for self-closing tags, even in HTML mode
opts.selfClosingTags && singleTag.has(elem.name))) {
if (!opts.xmlMode)
tag += " ";
tag += "/>";
}
else {
tag += ">";
if (elem.children.length > 0) {
tag += render(elem.children, opts);
}
if (opts.xmlMode || !singleTag.has(elem.name)) {
tag += "</".concat(elem.name, ">");
}
}
return tag;
}
function renderDirective(elem) {
return "<".concat(elem.data, ">");
}
function renderText(elem, opts) {
var _a;
var data = elem.data || "";
// If entities weren't decoded, no need to encode them back
if (((_a = opts.encodeEntities) !== null && _a !== void 0 ? _a : opts.decodeEntities) !== false &&
!(!opts.xmlMode &&
elem.parent &&
unencodedElements.has(elem.parent.name))) {
data =
opts.xmlMode || opts.encodeEntities !== "utf8"
? (0, entities_1.encodeXML)(data)
: (0, entities_1.escapeText)(data);
}
return data;
}
function renderCdata(elem) {
return "<![CDATA[".concat(elem.children[0].data, "]]>");
}
function renderComment(elem) {
return "<!--".concat(elem.data, "-->");
}

View File

@@ -0,0 +1,69 @@
{
"name": "dom-serializer",
"version": "2.0.0",
"description": "render domhandler DOM nodes to a string",
"author": "Felix Boehm <me@feedic.com>",
"sideEffects": false,
"keywords": [
"html",
"xml",
"render"
],
"repository": {
"type": "git",
"url": "git://github.com/cheeriojs/dom-serializer.git"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
"module": "lib/esm/index.js",
"exports": {
"require": "./lib/index.js",
"import": "./lib/esm/index.js"
},
"files": [
"lib/**/*"
],
"dependencies": {
"domelementtype": "^2.3.0",
"domhandler": "^5.0.2",
"entities": "^4.2.0"
},
"devDependencies": {
"@types/jest": "^27.4.1",
"@types/node": "^17.0.23",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"cheerio": "^1.0.0-rc.9",
"eslint": "^8.12.0",
"eslint-config-prettier": "^8.5.0",
"htmlparser2": "^7.2.0",
"jest": "^27.5.1",
"prettier": "^2.6.2",
"ts-jest": "^27.1.4",
"typescript": "^4.6.3"
},
"scripts": {
"test": "npm run test:jest && npm run lint",
"test:jest": "jest",
"lint": "npm run lint:es && npm run lint:prettier",
"lint:es": "eslint --ignore-path .gitignore .",
"lint:prettier": "npm run prettier -- --check",
"format": "npm run format:es && npm run format:prettier",
"format:es": "npm run lint:es -- --fix",
"format:prettier": "npm run prettier -- --write",
"prettier": "prettier \"**/*.{ts,md,json,yml}\" --ignore-path .gitignore",
"build": "npm run build:cjs && npm run build:esm",
"build:cjs": "tsc",
"build:esm": "tsc --module esnext --target es2019 --outDir lib/esm && echo '{\"type\":\"module\"}' > lib/esm/package.json",
"prepare": "npm run build"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"moduleNameMapper": {
"^(.*)\\.js$": "$1"
}
},
"funding": "https://github.com/cheeriojs/dom-serializer?sponsor=1",
"license": "MIT"
}

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.

View File

@@ -0,0 +1,76 @@
import { ChildNode, Element, DataNode, Document, ParentNode } from "./node.js";
export * from "./node.js";
export interface DomHandlerOptions {
/**
* Add a `startIndex` property to nodes.
* When the parser is used in a non-streaming fashion, `startIndex` is an integer
* indicating the position of the start of the node in the document.
*
* @default false
*/
withStartIndices?: boolean;
/**
* Add an `endIndex` property to nodes.
* When the parser is used in a non-streaming fashion, `endIndex` is an integer
* indicating the position of the end of the node in the document.
*
* @default false
*/
withEndIndices?: boolean;
/**
* Treat the markup as XML.
*
* @default false
*/
xmlMode?: boolean;
}
interface ParserInterface {
startIndex: number | null;
endIndex: number | null;
}
declare type Callback = (error: Error | null, dom: ChildNode[]) => void;
declare type ElementCallback = (element: Element) => void;
export declare class DomHandler {
/** The elements of the DOM */
dom: ChildNode[];
/** The root element for the DOM */
root: Document;
/** Called once parsing has completed. */
private readonly callback;
/** Settings for the handler. */
private readonly options;
/** Callback whenever a tag is closed. */
private readonly elementCB;
/** Indicated whether parsing has been completed. */
private done;
/** Stack of open tags. */
protected tagStack: ParentNode[];
/** A data node that is still being written to. */
protected lastNode: DataNode | null;
/** Reference to the parser instance. Used for location information. */
private parser;
/**
* @param callback Called once parsing has completed.
* @param options Settings for the handler.
* @param elementCB Callback whenever a tag is closed.
*/
constructor(callback?: Callback | null, options?: DomHandlerOptions | null, elementCB?: ElementCallback);
onparserinit(parser: ParserInterface): void;
onreset(): void;
onend(): void;
onerror(error: Error): void;
onclosetag(): void;
onopentag(name: string, attribs: {
[key: string]: string;
}): void;
ontext(data: string): void;
oncomment(data: string): void;
oncommentend(): void;
oncdatastart(): void;
oncdataend(): void;
onprocessinginstruction(name: string, data: string): void;
protected handleCallback(error: Error | null): void;
protected addNode(node: ChildNode): void;
}
export default DomHandler;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACH,SAAS,EACT,OAAO,EACP,QAAQ,EAIR,QAAQ,EAER,UAAU,EACb,MAAM,WAAW,CAAC;AAEnB,cAAc,WAAW,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IAC9B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AASD,UAAU,eAAe;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,aAAK,QAAQ,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;AAChE,aAAK,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAElD,qBAAa,UAAU;IACnB,8BAA8B;IACvB,GAAG,EAAE,SAAS,EAAE,CAAM;IAE7B,mCAAmC;IAC5B,IAAI,WAA0B;IAErC,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAE3C,gCAAgC;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAE5C,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IAEnD,oDAAoD;IACpD,OAAO,CAAC,IAAI,CAAS;IAErB,0BAA0B;IAC1B,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAe;IAE/C,kDAAkD;IAClD,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAE3C,uEAAuE;IACvE,OAAO,CAAC,MAAM,CAAgC;IAE9C;;;;OAIG;gBAEC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,EAC1B,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI,EAClC,SAAS,CAAC,EAAE,eAAe;IAiBxB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAK3C,OAAO,IAAI,IAAI;IAUf,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAI3B,UAAU,IAAI,IAAI;IAYlB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI;IAOjE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAe1B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAW7B,YAAY,IAAI,IAAI;IAIpB,YAAY,IAAI,IAAI;IAUpB,UAAU,IAAI,IAAI;IAIlB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKhE,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI;IAQnD,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;CAwB3C;AAED,eAAe,UAAU,CAAC"}

View File

@@ -0,0 +1,146 @@
import { ElementType } from "domelementtype";
import { Element, Text, Comment, CDATA, Document, ProcessingInstruction, } from "./node.js";
export * from "./node.js";
// Default options
const defaultOpts = {
withStartIndices: false,
withEndIndices: false,
xmlMode: false,
};
export class DomHandler {
/**
* @param callback Called once parsing has completed.
* @param options Settings for the handler.
* @param elementCB Callback whenever a tag is closed.
*/
constructor(callback, options, elementCB) {
/** The elements of the DOM */
this.dom = [];
/** The root element for the DOM */
this.root = new Document(this.dom);
/** Indicated whether parsing has been completed. */
this.done = false;
/** Stack of open tags. */
this.tagStack = [this.root];
/** A data node that is still being written to. */
this.lastNode = null;
/** Reference to the parser instance. Used for location information. */
this.parser = null;
// Make it possible to skip arguments, for backwards-compatibility
if (typeof options === "function") {
elementCB = options;
options = defaultOpts;
}
if (typeof callback === "object") {
options = callback;
callback = undefined;
}
this.callback = callback !== null && callback !== void 0 ? callback : null;
this.options = options !== null && options !== void 0 ? options : defaultOpts;
this.elementCB = elementCB !== null && elementCB !== void 0 ? elementCB : null;
}
onparserinit(parser) {
this.parser = parser;
}
// Resets the handler back to starting state
onreset() {
this.dom = [];
this.root = new Document(this.dom);
this.done = false;
this.tagStack = [this.root];
this.lastNode = null;
this.parser = null;
}
// Signals the handler that parsing is done
onend() {
if (this.done)
return;
this.done = true;
this.parser = null;
this.handleCallback(null);
}
onerror(error) {
this.handleCallback(error);
}
onclosetag() {
this.lastNode = null;
const elem = this.tagStack.pop();
if (this.options.withEndIndices) {
elem.endIndex = this.parser.endIndex;
}
if (this.elementCB)
this.elementCB(elem);
}
onopentag(name, attribs) {
const type = this.options.xmlMode ? ElementType.Tag : undefined;
const element = new Element(name, attribs, undefined, type);
this.addNode(element);
this.tagStack.push(element);
}
ontext(data) {
const { lastNode } = this;
if (lastNode && lastNode.type === ElementType.Text) {
lastNode.data += data;
if (this.options.withEndIndices) {
lastNode.endIndex = this.parser.endIndex;
}
}
else {
const node = new Text(data);
this.addNode(node);
this.lastNode = node;
}
}
oncomment(data) {
if (this.lastNode && this.lastNode.type === ElementType.Comment) {
this.lastNode.data += data;
return;
}
const node = new Comment(data);
this.addNode(node);
this.lastNode = node;
}
oncommentend() {
this.lastNode = null;
}
oncdatastart() {
const text = new Text("");
const node = new CDATA([text]);
this.addNode(node);
text.parent = node;
this.lastNode = text;
}
oncdataend() {
this.lastNode = null;
}
onprocessinginstruction(name, data) {
const node = new ProcessingInstruction(name, data);
this.addNode(node);
}
handleCallback(error) {
if (typeof this.callback === "function") {
this.callback(error, this.dom);
}
else if (error) {
throw error;
}
}
addNode(node) {
const parent = this.tagStack[this.tagStack.length - 1];
const previousSibling = parent.children[parent.children.length - 1];
if (this.options.withStartIndices) {
node.startIndex = this.parser.startIndex;
}
if (this.options.withEndIndices) {
node.endIndex = this.parser.endIndex;
}
parent.children.push(node);
if (previousSibling) {
node.prev = previousSibling;
previousSibling.next = node;
}
node.parent = parent;
this.lastNode = null;
}
}
export default DomHandler;

View File

@@ -0,0 +1,245 @@
import { ElementType } from "domelementtype";
interface SourceCodeLocation {
/** One-based line index of the first character. */
startLine: number;
/** One-based column index of the first character. */
startCol: number;
/** Zero-based first character index. */
startOffset: number;
/** One-based line index of the last character. */
endLine: number;
/** One-based column index of the last character. Points directly *after* the last character. */
endCol: number;
/** Zero-based last character index. Points directly *after* the last character. */
endOffset: number;
}
interface TagSourceCodeLocation extends SourceCodeLocation {
startTag?: SourceCodeLocation;
endTag?: SourceCodeLocation;
}
export declare type ParentNode = Document | Element | CDATA;
export declare type ChildNode = Text | Comment | ProcessingInstruction | Element | CDATA | Document;
export declare type AnyNode = ParentNode | ChildNode;
/**
* This object will be used as the prototype for Nodes when creating a
* DOM-Level-1-compliant structure.
*/
export declare abstract class Node {
/** The type of the node. */
abstract readonly type: ElementType;
/** Parent of the node */
parent: ParentNode | null;
/** Previous sibling */
prev: ChildNode | null;
/** Next sibling */
next: ChildNode | null;
/** The start index of the node. Requires `withStartIndices` on the handler to be `true. */
startIndex: number | null;
/** The end index of the node. Requires `withEndIndices` on the handler to be `true. */
endIndex: number | null;
/**
* `parse5` source code location info.
*
* Available if parsing with parse5 and location info is enabled.
*/
sourceCodeLocation?: SourceCodeLocation | null;
/**
* [DOM spec](https://dom.spec.whatwg.org/#dom-node-nodetype)-compatible
* node {@link type}.
*/
abstract readonly nodeType: number;
/**
* Same as {@link parent}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get parentNode(): ParentNode | null;
set parentNode(parent: ParentNode | null);
/**
* Same as {@link prev}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get previousSibling(): ChildNode | null;
set previousSibling(prev: ChildNode | null);
/**
* Same as {@link next}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get nextSibling(): ChildNode | null;
set nextSibling(next: ChildNode | null);
/**
* Clone this node, and optionally its children.
*
* @param recursive Clone child nodes as well.
* @returns A clone of the node.
*/
cloneNode<T extends Node>(this: T, recursive?: boolean): T;
}
/**
* A node that contains some data.
*/
export declare abstract class DataNode extends Node {
data: string;
/**
* @param data The content of the data node
*/
constructor(data: string);
/**
* Same as {@link data}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get nodeValue(): string;
set nodeValue(data: string);
}
/**
* Text within the document.
*/
export declare class Text extends DataNode {
type: ElementType.Text;
get nodeType(): 3;
}
/**
* Comments within the document.
*/
export declare class Comment extends DataNode {
type: ElementType.Comment;
get nodeType(): 8;
}
/**
* Processing instructions, including doc types.
*/
export declare class ProcessingInstruction extends DataNode {
name: string;
type: ElementType.Directive;
constructor(name: string, data: string);
get nodeType(): 1;
/** If this is a doctype, the document type name (parse5 only). */
"x-name"?: string;
/** If this is a doctype, the document type public identifier (parse5 only). */
"x-publicId"?: string;
/** If this is a doctype, the document type system identifier (parse5 only). */
"x-systemId"?: string;
}
/**
* A `Node` that can have children.
*/
export declare abstract class NodeWithChildren extends Node {
children: ChildNode[];
/**
* @param children Children of the node. Only certain node types can have children.
*/
constructor(children: ChildNode[]);
/** First child of the node. */
get firstChild(): ChildNode | null;
/** Last child of the node. */
get lastChild(): ChildNode | null;
/**
* Same as {@link children}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get childNodes(): ChildNode[];
set childNodes(children: ChildNode[]);
}
export declare class CDATA extends NodeWithChildren {
type: ElementType.CDATA;
get nodeType(): 4;
}
/**
* The root node of the document.
*/
export declare class Document extends NodeWithChildren {
type: ElementType.Root;
get nodeType(): 9;
/** [Document mode](https://dom.spec.whatwg.org/#concept-document-limited-quirks) (parse5 only). */
"x-mode"?: "no-quirks" | "quirks" | "limited-quirks";
}
/**
* The description of an individual attribute.
*/
interface Attribute {
name: string;
value: string;
namespace?: string;
prefix?: string;
}
/**
* An element within the DOM.
*/
export declare class Element extends NodeWithChildren {
name: string;
attribs: {
[name: string]: string;
};
type: ElementType.Tag | ElementType.Script | ElementType.Style;
/**
* @param name Name of the tag, eg. `div`, `span`.
* @param attribs Object mapping attribute names to attribute values.
* @param children Children of the node.
*/
constructor(name: string, attribs: {
[name: string]: string;
}, children?: ChildNode[], type?: ElementType.Tag | ElementType.Script | ElementType.Style);
get nodeType(): 1;
/**
* `parse5` source code location info, with start & end tags.
*
* Available if parsing with parse5 and location info is enabled.
*/
sourceCodeLocation?: TagSourceCodeLocation | null;
/**
* Same as {@link name}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get tagName(): string;
set tagName(name: string);
get attributes(): Attribute[];
/** Element namespace (parse5 only). */
namespace?: string;
/** Element attribute namespaces (parse5 only). */
"x-attribsNamespace"?: Record<string, string>;
/** Element attribute namespace-related prefixes (parse5 only). */
"x-attribsPrefix"?: Record<string, string>;
}
/**
* @param node Node to check.
* @returns `true` if the node is a `Element`, `false` otherwise.
*/
export declare function isTag(node: Node): node is Element;
/**
* @param node Node to check.
* @returns `true` if the node has the type `CDATA`, `false` otherwise.
*/
export declare function isCDATA(node: Node): node is CDATA;
/**
* @param node Node to check.
* @returns `true` if the node has the type `Text`, `false` otherwise.
*/
export declare function isText(node: Node): node is Text;
/**
* @param node Node to check.
* @returns `true` if the node has the type `Comment`, `false` otherwise.
*/
export declare function isComment(node: Node): node is Comment;
/**
* @param node Node to check.
* @returns `true` if the node has the type `ProcessingInstruction`, `false` otherwise.
*/
export declare function isDirective(node: Node): node is ProcessingInstruction;
/**
* @param node Node to check.
* @returns `true` if the node has the type `ProcessingInstruction`, `false` otherwise.
*/
export declare function isDocument(node: Node): node is Document;
/**
* @param node Node to check.
* @returns `true` if the node has children, `false` otherwise.
*/
export declare function hasChildren(node: Node): node is ParentNode;
/**
* Clone a node, and optionally its children.
*
* @param recursive Clone child nodes as well.
* @returns A clone of the node.
*/
export declare function cloneNode<T extends Node>(node: T, recursive?: boolean): T;
export {};
//# sourceMappingURL=node.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/node.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,338 @@
import { ElementType, isTag as isTagRaw } from "domelementtype";
/**
* This object will be used as the prototype for Nodes when creating a
* DOM-Level-1-compliant structure.
*/
export class Node {
constructor() {
/** Parent of the node */
this.parent = null;
/** Previous sibling */
this.prev = null;
/** Next sibling */
this.next = null;
/** The start index of the node. Requires `withStartIndices` on the handler to be `true. */
this.startIndex = null;
/** The end index of the node. Requires `withEndIndices` on the handler to be `true. */
this.endIndex = null;
}
// Read-write aliases for properties
/**
* Same as {@link parent}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get parentNode() {
return this.parent;
}
set parentNode(parent) {
this.parent = parent;
}
/**
* Same as {@link prev}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get previousSibling() {
return this.prev;
}
set previousSibling(prev) {
this.prev = prev;
}
/**
* Same as {@link next}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get nextSibling() {
return this.next;
}
set nextSibling(next) {
this.next = next;
}
/**
* Clone this node, and optionally its children.
*
* @param recursive Clone child nodes as well.
* @returns A clone of the node.
*/
cloneNode(recursive = false) {
return cloneNode(this, recursive);
}
}
/**
* A node that contains some data.
*/
export class DataNode extends Node {
/**
* @param data The content of the data node
*/
constructor(data) {
super();
this.data = data;
}
/**
* Same as {@link data}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get nodeValue() {
return this.data;
}
set nodeValue(data) {
this.data = data;
}
}
/**
* Text within the document.
*/
export class Text extends DataNode {
constructor() {
super(...arguments);
this.type = ElementType.Text;
}
get nodeType() {
return 3;
}
}
/**
* Comments within the document.
*/
export class Comment extends DataNode {
constructor() {
super(...arguments);
this.type = ElementType.Comment;
}
get nodeType() {
return 8;
}
}
/**
* Processing instructions, including doc types.
*/
export class ProcessingInstruction extends DataNode {
constructor(name, data) {
super(data);
this.name = name;
this.type = ElementType.Directive;
}
get nodeType() {
return 1;
}
}
/**
* A `Node` that can have children.
*/
export class NodeWithChildren extends Node {
/**
* @param children Children of the node. Only certain node types can have children.
*/
constructor(children) {
super();
this.children = children;
}
// Aliases
/** First child of the node. */
get firstChild() {
var _a;
return (_a = this.children[0]) !== null && _a !== void 0 ? _a : null;
}
/** Last child of the node. */
get lastChild() {
return this.children.length > 0
? this.children[this.children.length - 1]
: null;
}
/**
* Same as {@link children}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get childNodes() {
return this.children;
}
set childNodes(children) {
this.children = children;
}
}
export class CDATA extends NodeWithChildren {
constructor() {
super(...arguments);
this.type = ElementType.CDATA;
}
get nodeType() {
return 4;
}
}
/**
* The root node of the document.
*/
export class Document extends NodeWithChildren {
constructor() {
super(...arguments);
this.type = ElementType.Root;
}
get nodeType() {
return 9;
}
}
/**
* An element within the DOM.
*/
export class Element extends NodeWithChildren {
/**
* @param name Name of the tag, eg. `div`, `span`.
* @param attribs Object mapping attribute names to attribute values.
* @param children Children of the node.
*/
constructor(name, attribs, children = [], type = name === "script"
? ElementType.Script
: name === "style"
? ElementType.Style
: ElementType.Tag) {
super(children);
this.name = name;
this.attribs = attribs;
this.type = type;
}
get nodeType() {
return 1;
}
// DOM Level 1 aliases
/**
* Same as {@link name}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get tagName() {
return this.name;
}
set tagName(name) {
this.name = name;
}
get attributes() {
return Object.keys(this.attribs).map((name) => {
var _a, _b;
return ({
name,
value: this.attribs[name],
namespace: (_a = this["x-attribsNamespace"]) === null || _a === void 0 ? void 0 : _a[name],
prefix: (_b = this["x-attribsPrefix"]) === null || _b === void 0 ? void 0 : _b[name],
});
});
}
}
/**
* @param node Node to check.
* @returns `true` if the node is a `Element`, `false` otherwise.
*/
export function isTag(node) {
return isTagRaw(node);
}
/**
* @param node Node to check.
* @returns `true` if the node has the type `CDATA`, `false` otherwise.
*/
export function isCDATA(node) {
return node.type === ElementType.CDATA;
}
/**
* @param node Node to check.
* @returns `true` if the node has the type `Text`, `false` otherwise.
*/
export function isText(node) {
return node.type === ElementType.Text;
}
/**
* @param node Node to check.
* @returns `true` if the node has the type `Comment`, `false` otherwise.
*/
export function isComment(node) {
return node.type === ElementType.Comment;
}
/**
* @param node Node to check.
* @returns `true` if the node has the type `ProcessingInstruction`, `false` otherwise.
*/
export function isDirective(node) {
return node.type === ElementType.Directive;
}
/**
* @param node Node to check.
* @returns `true` if the node has the type `ProcessingInstruction`, `false` otherwise.
*/
export function isDocument(node) {
return node.type === ElementType.Root;
}
/**
* @param node Node to check.
* @returns `true` if the node has children, `false` otherwise.
*/
export function hasChildren(node) {
return Object.prototype.hasOwnProperty.call(node, "children");
}
/**
* Clone a node, and optionally its children.
*
* @param recursive Clone child nodes as well.
* @returns A clone of the node.
*/
export function cloneNode(node, recursive = false) {
let result;
if (isText(node)) {
result = new Text(node.data);
}
else if (isComment(node)) {
result = new Comment(node.data);
}
else if (isTag(node)) {
const children = recursive ? cloneChildren(node.children) : [];
const clone = new Element(node.name, { ...node.attribs }, children);
children.forEach((child) => (child.parent = clone));
if (node.namespace != null) {
clone.namespace = node.namespace;
}
if (node["x-attribsNamespace"]) {
clone["x-attribsNamespace"] = { ...node["x-attribsNamespace"] };
}
if (node["x-attribsPrefix"]) {
clone["x-attribsPrefix"] = { ...node["x-attribsPrefix"] };
}
result = clone;
}
else if (isCDATA(node)) {
const children = recursive ? cloneChildren(node.children) : [];
const clone = new CDATA(children);
children.forEach((child) => (child.parent = clone));
result = clone;
}
else if (isDocument(node)) {
const children = recursive ? cloneChildren(node.children) : [];
const clone = new Document(children);
children.forEach((child) => (child.parent = clone));
if (node["x-mode"]) {
clone["x-mode"] = node["x-mode"];
}
result = clone;
}
else if (isDirective(node)) {
const instruction = new ProcessingInstruction(node.name, node.data);
if (node["x-name"] != null) {
instruction["x-name"] = node["x-name"];
instruction["x-publicId"] = node["x-publicId"];
instruction["x-systemId"] = node["x-systemId"];
}
result = instruction;
}
else {
throw new Error(`Not implemented yet: ${node.type}`);
}
result.startIndex = node.startIndex;
result.endIndex = node.endIndex;
if (node.sourceCodeLocation != null) {
result.sourceCodeLocation = node.sourceCodeLocation;
}
return result;
}
function cloneChildren(childs) {
const children = childs.map((child) => cloneNode(child, true));
for (let i = 1; i < children.length; i++) {
children[i].prev = children[i - 1];
children[i - 1].next = children[i];
}
return children;
}

View File

@@ -0,0 +1 @@
{"type":"module"}

View File

@@ -0,0 +1,76 @@
import { ChildNode, Element, DataNode, Document, ParentNode } from "./node.js";
export * from "./node.js";
export interface DomHandlerOptions {
/**
* Add a `startIndex` property to nodes.
* When the parser is used in a non-streaming fashion, `startIndex` is an integer
* indicating the position of the start of the node in the document.
*
* @default false
*/
withStartIndices?: boolean;
/**
* Add an `endIndex` property to nodes.
* When the parser is used in a non-streaming fashion, `endIndex` is an integer
* indicating the position of the end of the node in the document.
*
* @default false
*/
withEndIndices?: boolean;
/**
* Treat the markup as XML.
*
* @default false
*/
xmlMode?: boolean;
}
interface ParserInterface {
startIndex: number | null;
endIndex: number | null;
}
declare type Callback = (error: Error | null, dom: ChildNode[]) => void;
declare type ElementCallback = (element: Element) => void;
export declare class DomHandler {
/** The elements of the DOM */
dom: ChildNode[];
/** The root element for the DOM */
root: Document;
/** Called once parsing has completed. */
private readonly callback;
/** Settings for the handler. */
private readonly options;
/** Callback whenever a tag is closed. */
private readonly elementCB;
/** Indicated whether parsing has been completed. */
private done;
/** Stack of open tags. */
protected tagStack: ParentNode[];
/** A data node that is still being written to. */
protected lastNode: DataNode | null;
/** Reference to the parser instance. Used for location information. */
private parser;
/**
* @param callback Called once parsing has completed.
* @param options Settings for the handler.
* @param elementCB Callback whenever a tag is closed.
*/
constructor(callback?: Callback | null, options?: DomHandlerOptions | null, elementCB?: ElementCallback);
onparserinit(parser: ParserInterface): void;
onreset(): void;
onend(): void;
onerror(error: Error): void;
onclosetag(): void;
onopentag(name: string, attribs: {
[key: string]: string;
}): void;
ontext(data: string): void;
oncomment(data: string): void;
oncommentend(): void;
oncdatastart(): void;
oncdataend(): void;
onprocessinginstruction(name: string, data: string): void;
protected handleCallback(error: Error | null): void;
protected addNode(node: ChildNode): void;
}
export default DomHandler;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACH,SAAS,EACT,OAAO,EACP,QAAQ,EAIR,QAAQ,EAER,UAAU,EACb,MAAM,WAAW,CAAC;AAEnB,cAAc,WAAW,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IAC9B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AASD,UAAU,eAAe;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,aAAK,QAAQ,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;AAChE,aAAK,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAElD,qBAAa,UAAU;IACnB,8BAA8B;IACvB,GAAG,EAAE,SAAS,EAAE,CAAM;IAE7B,mCAAmC;IAC5B,IAAI,WAA0B;IAErC,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAE3C,gCAAgC;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAE5C,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IAEnD,oDAAoD;IACpD,OAAO,CAAC,IAAI,CAAS;IAErB,0BAA0B;IAC1B,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAe;IAE/C,kDAAkD;IAClD,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAE3C,uEAAuE;IACvE,OAAO,CAAC,MAAM,CAAgC;IAE9C;;;;OAIG;gBAEC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,EAC1B,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI,EAClC,SAAS,CAAC,EAAE,eAAe;IAiBxB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAK3C,OAAO,IAAI,IAAI;IAUf,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAI3B,UAAU,IAAI,IAAI;IAYlB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,GAAG,IAAI;IAOjE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAe1B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAW7B,YAAY,IAAI,IAAI;IAIpB,YAAY,IAAI,IAAI;IAUpB,UAAU,IAAI,IAAI;IAIlB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKhE,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI;IAQnD,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI;CAwB3C;AAED,eAAe,UAAU,CAAC"}

View File

@@ -0,0 +1,165 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DomHandler = void 0;
var domelementtype_1 = require("domelementtype");
var node_js_1 = require("./node.js");
__exportStar(require("./node.js"), exports);
// Default options
var defaultOpts = {
withStartIndices: false,
withEndIndices: false,
xmlMode: false,
};
var DomHandler = /** @class */ (function () {
/**
* @param callback Called once parsing has completed.
* @param options Settings for the handler.
* @param elementCB Callback whenever a tag is closed.
*/
function DomHandler(callback, options, elementCB) {
/** The elements of the DOM */
this.dom = [];
/** The root element for the DOM */
this.root = new node_js_1.Document(this.dom);
/** Indicated whether parsing has been completed. */
this.done = false;
/** Stack of open tags. */
this.tagStack = [this.root];
/** A data node that is still being written to. */
this.lastNode = null;
/** Reference to the parser instance. Used for location information. */
this.parser = null;
// Make it possible to skip arguments, for backwards-compatibility
if (typeof options === "function") {
elementCB = options;
options = defaultOpts;
}
if (typeof callback === "object") {
options = callback;
callback = undefined;
}
this.callback = callback !== null && callback !== void 0 ? callback : null;
this.options = options !== null && options !== void 0 ? options : defaultOpts;
this.elementCB = elementCB !== null && elementCB !== void 0 ? elementCB : null;
}
DomHandler.prototype.onparserinit = function (parser) {
this.parser = parser;
};
// Resets the handler back to starting state
DomHandler.prototype.onreset = function () {
this.dom = [];
this.root = new node_js_1.Document(this.dom);
this.done = false;
this.tagStack = [this.root];
this.lastNode = null;
this.parser = null;
};
// Signals the handler that parsing is done
DomHandler.prototype.onend = function () {
if (this.done)
return;
this.done = true;
this.parser = null;
this.handleCallback(null);
};
DomHandler.prototype.onerror = function (error) {
this.handleCallback(error);
};
DomHandler.prototype.onclosetag = function () {
this.lastNode = null;
var elem = this.tagStack.pop();
if (this.options.withEndIndices) {
elem.endIndex = this.parser.endIndex;
}
if (this.elementCB)
this.elementCB(elem);
};
DomHandler.prototype.onopentag = function (name, attribs) {
var type = this.options.xmlMode ? domelementtype_1.ElementType.Tag : undefined;
var element = new node_js_1.Element(name, attribs, undefined, type);
this.addNode(element);
this.tagStack.push(element);
};
DomHandler.prototype.ontext = function (data) {
var lastNode = this.lastNode;
if (lastNode && lastNode.type === domelementtype_1.ElementType.Text) {
lastNode.data += data;
if (this.options.withEndIndices) {
lastNode.endIndex = this.parser.endIndex;
}
}
else {
var node = new node_js_1.Text(data);
this.addNode(node);
this.lastNode = node;
}
};
DomHandler.prototype.oncomment = function (data) {
if (this.lastNode && this.lastNode.type === domelementtype_1.ElementType.Comment) {
this.lastNode.data += data;
return;
}
var node = new node_js_1.Comment(data);
this.addNode(node);
this.lastNode = node;
};
DomHandler.prototype.oncommentend = function () {
this.lastNode = null;
};
DomHandler.prototype.oncdatastart = function () {
var text = new node_js_1.Text("");
var node = new node_js_1.CDATA([text]);
this.addNode(node);
text.parent = node;
this.lastNode = text;
};
DomHandler.prototype.oncdataend = function () {
this.lastNode = null;
};
DomHandler.prototype.onprocessinginstruction = function (name, data) {
var node = new node_js_1.ProcessingInstruction(name, data);
this.addNode(node);
};
DomHandler.prototype.handleCallback = function (error) {
if (typeof this.callback === "function") {
this.callback(error, this.dom);
}
else if (error) {
throw error;
}
};
DomHandler.prototype.addNode = function (node) {
var parent = this.tagStack[this.tagStack.length - 1];
var previousSibling = parent.children[parent.children.length - 1];
if (this.options.withStartIndices) {
node.startIndex = this.parser.startIndex;
}
if (this.options.withEndIndices) {
node.endIndex = this.parser.endIndex;
}
parent.children.push(node);
if (previousSibling) {
node.prev = previousSibling;
previousSibling.next = node;
}
node.parent = parent;
this.lastNode = null;
};
return DomHandler;
}());
exports.DomHandler = DomHandler;
exports.default = DomHandler;

View File

@@ -0,0 +1,245 @@
import { ElementType } from "domelementtype";
interface SourceCodeLocation {
/** One-based line index of the first character. */
startLine: number;
/** One-based column index of the first character. */
startCol: number;
/** Zero-based first character index. */
startOffset: number;
/** One-based line index of the last character. */
endLine: number;
/** One-based column index of the last character. Points directly *after* the last character. */
endCol: number;
/** Zero-based last character index. Points directly *after* the last character. */
endOffset: number;
}
interface TagSourceCodeLocation extends SourceCodeLocation {
startTag?: SourceCodeLocation;
endTag?: SourceCodeLocation;
}
export declare type ParentNode = Document | Element | CDATA;
export declare type ChildNode = Text | Comment | ProcessingInstruction | Element | CDATA | Document;
export declare type AnyNode = ParentNode | ChildNode;
/**
* This object will be used as the prototype for Nodes when creating a
* DOM-Level-1-compliant structure.
*/
export declare abstract class Node {
/** The type of the node. */
abstract readonly type: ElementType;
/** Parent of the node */
parent: ParentNode | null;
/** Previous sibling */
prev: ChildNode | null;
/** Next sibling */
next: ChildNode | null;
/** The start index of the node. Requires `withStartIndices` on the handler to be `true. */
startIndex: number | null;
/** The end index of the node. Requires `withEndIndices` on the handler to be `true. */
endIndex: number | null;
/**
* `parse5` source code location info.
*
* Available if parsing with parse5 and location info is enabled.
*/
sourceCodeLocation?: SourceCodeLocation | null;
/**
* [DOM spec](https://dom.spec.whatwg.org/#dom-node-nodetype)-compatible
* node {@link type}.
*/
abstract readonly nodeType: number;
/**
* Same as {@link parent}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get parentNode(): ParentNode | null;
set parentNode(parent: ParentNode | null);
/**
* Same as {@link prev}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get previousSibling(): ChildNode | null;
set previousSibling(prev: ChildNode | null);
/**
* Same as {@link next}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get nextSibling(): ChildNode | null;
set nextSibling(next: ChildNode | null);
/**
* Clone this node, and optionally its children.
*
* @param recursive Clone child nodes as well.
* @returns A clone of the node.
*/
cloneNode<T extends Node>(this: T, recursive?: boolean): T;
}
/**
* A node that contains some data.
*/
export declare abstract class DataNode extends Node {
data: string;
/**
* @param data The content of the data node
*/
constructor(data: string);
/**
* Same as {@link data}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get nodeValue(): string;
set nodeValue(data: string);
}
/**
* Text within the document.
*/
export declare class Text extends DataNode {
type: ElementType.Text;
get nodeType(): 3;
}
/**
* Comments within the document.
*/
export declare class Comment extends DataNode {
type: ElementType.Comment;
get nodeType(): 8;
}
/**
* Processing instructions, including doc types.
*/
export declare class ProcessingInstruction extends DataNode {
name: string;
type: ElementType.Directive;
constructor(name: string, data: string);
get nodeType(): 1;
/** If this is a doctype, the document type name (parse5 only). */
"x-name"?: string;
/** If this is a doctype, the document type public identifier (parse5 only). */
"x-publicId"?: string;
/** If this is a doctype, the document type system identifier (parse5 only). */
"x-systemId"?: string;
}
/**
* A `Node` that can have children.
*/
export declare abstract class NodeWithChildren extends Node {
children: ChildNode[];
/**
* @param children Children of the node. Only certain node types can have children.
*/
constructor(children: ChildNode[]);
/** First child of the node. */
get firstChild(): ChildNode | null;
/** Last child of the node. */
get lastChild(): ChildNode | null;
/**
* Same as {@link children}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get childNodes(): ChildNode[];
set childNodes(children: ChildNode[]);
}
export declare class CDATA extends NodeWithChildren {
type: ElementType.CDATA;
get nodeType(): 4;
}
/**
* The root node of the document.
*/
export declare class Document extends NodeWithChildren {
type: ElementType.Root;
get nodeType(): 9;
/** [Document mode](https://dom.spec.whatwg.org/#concept-document-limited-quirks) (parse5 only). */
"x-mode"?: "no-quirks" | "quirks" | "limited-quirks";
}
/**
* The description of an individual attribute.
*/
interface Attribute {
name: string;
value: string;
namespace?: string;
prefix?: string;
}
/**
* An element within the DOM.
*/
export declare class Element extends NodeWithChildren {
name: string;
attribs: {
[name: string]: string;
};
type: ElementType.Tag | ElementType.Script | ElementType.Style;
/**
* @param name Name of the tag, eg. `div`, `span`.
* @param attribs Object mapping attribute names to attribute values.
* @param children Children of the node.
*/
constructor(name: string, attribs: {
[name: string]: string;
}, children?: ChildNode[], type?: ElementType.Tag | ElementType.Script | ElementType.Style);
get nodeType(): 1;
/**
* `parse5` source code location info, with start & end tags.
*
* Available if parsing with parse5 and location info is enabled.
*/
sourceCodeLocation?: TagSourceCodeLocation | null;
/**
* Same as {@link name}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get tagName(): string;
set tagName(name: string);
get attributes(): Attribute[];
/** Element namespace (parse5 only). */
namespace?: string;
/** Element attribute namespaces (parse5 only). */
"x-attribsNamespace"?: Record<string, string>;
/** Element attribute namespace-related prefixes (parse5 only). */
"x-attribsPrefix"?: Record<string, string>;
}
/**
* @param node Node to check.
* @returns `true` if the node is a `Element`, `false` otherwise.
*/
export declare function isTag(node: Node): node is Element;
/**
* @param node Node to check.
* @returns `true` if the node has the type `CDATA`, `false` otherwise.
*/
export declare function isCDATA(node: Node): node is CDATA;
/**
* @param node Node to check.
* @returns `true` if the node has the type `Text`, `false` otherwise.
*/
export declare function isText(node: Node): node is Text;
/**
* @param node Node to check.
* @returns `true` if the node has the type `Comment`, `false` otherwise.
*/
export declare function isComment(node: Node): node is Comment;
/**
* @param node Node to check.
* @returns `true` if the node has the type `ProcessingInstruction`, `false` otherwise.
*/
export declare function isDirective(node: Node): node is ProcessingInstruction;
/**
* @param node Node to check.
* @returns `true` if the node has the type `ProcessingInstruction`, `false` otherwise.
*/
export declare function isDocument(node: Node): node is Document;
/**
* @param node Node to check.
* @returns `true` if the node has children, `false` otherwise.
*/
export declare function hasChildren(node: Node): node is ParentNode;
/**
* Clone a node, and optionally its children.
*
* @param recursive Clone child nodes as well.
* @returns A clone of the node.
*/
export declare function cloneNode<T extends Node>(node: T, recursive?: boolean): T;
export {};
//# sourceMappingURL=node.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,474 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.cloneNode = exports.hasChildren = exports.isDocument = exports.isDirective = exports.isComment = exports.isText = exports.isCDATA = exports.isTag = exports.Element = exports.Document = exports.CDATA = exports.NodeWithChildren = exports.ProcessingInstruction = exports.Comment = exports.Text = exports.DataNode = exports.Node = void 0;
var domelementtype_1 = require("domelementtype");
/**
* This object will be used as the prototype for Nodes when creating a
* DOM-Level-1-compliant structure.
*/
var Node = /** @class */ (function () {
function Node() {
/** Parent of the node */
this.parent = null;
/** Previous sibling */
this.prev = null;
/** Next sibling */
this.next = null;
/** The start index of the node. Requires `withStartIndices` on the handler to be `true. */
this.startIndex = null;
/** The end index of the node. Requires `withEndIndices` on the handler to be `true. */
this.endIndex = null;
}
Object.defineProperty(Node.prototype, "parentNode", {
// Read-write aliases for properties
/**
* Same as {@link parent}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get: function () {
return this.parent;
},
set: function (parent) {
this.parent = parent;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Node.prototype, "previousSibling", {
/**
* Same as {@link prev}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get: function () {
return this.prev;
},
set: function (prev) {
this.prev = prev;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Node.prototype, "nextSibling", {
/**
* Same as {@link next}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get: function () {
return this.next;
},
set: function (next) {
this.next = next;
},
enumerable: false,
configurable: true
});
/**
* Clone this node, and optionally its children.
*
* @param recursive Clone child nodes as well.
* @returns A clone of the node.
*/
Node.prototype.cloneNode = function (recursive) {
if (recursive === void 0) { recursive = false; }
return cloneNode(this, recursive);
};
return Node;
}());
exports.Node = Node;
/**
* A node that contains some data.
*/
var DataNode = /** @class */ (function (_super) {
__extends(DataNode, _super);
/**
* @param data The content of the data node
*/
function DataNode(data) {
var _this = _super.call(this) || this;
_this.data = data;
return _this;
}
Object.defineProperty(DataNode.prototype, "nodeValue", {
/**
* Same as {@link data}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get: function () {
return this.data;
},
set: function (data) {
this.data = data;
},
enumerable: false,
configurable: true
});
return DataNode;
}(Node));
exports.DataNode = DataNode;
/**
* Text within the document.
*/
var Text = /** @class */ (function (_super) {
__extends(Text, _super);
function Text() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = domelementtype_1.ElementType.Text;
return _this;
}
Object.defineProperty(Text.prototype, "nodeType", {
get: function () {
return 3;
},
enumerable: false,
configurable: true
});
return Text;
}(DataNode));
exports.Text = Text;
/**
* Comments within the document.
*/
var Comment = /** @class */ (function (_super) {
__extends(Comment, _super);
function Comment() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = domelementtype_1.ElementType.Comment;
return _this;
}
Object.defineProperty(Comment.prototype, "nodeType", {
get: function () {
return 8;
},
enumerable: false,
configurable: true
});
return Comment;
}(DataNode));
exports.Comment = Comment;
/**
* Processing instructions, including doc types.
*/
var ProcessingInstruction = /** @class */ (function (_super) {
__extends(ProcessingInstruction, _super);
function ProcessingInstruction(name, data) {
var _this = _super.call(this, data) || this;
_this.name = name;
_this.type = domelementtype_1.ElementType.Directive;
return _this;
}
Object.defineProperty(ProcessingInstruction.prototype, "nodeType", {
get: function () {
return 1;
},
enumerable: false,
configurable: true
});
return ProcessingInstruction;
}(DataNode));
exports.ProcessingInstruction = ProcessingInstruction;
/**
* A `Node` that can have children.
*/
var NodeWithChildren = /** @class */ (function (_super) {
__extends(NodeWithChildren, _super);
/**
* @param children Children of the node. Only certain node types can have children.
*/
function NodeWithChildren(children) {
var _this = _super.call(this) || this;
_this.children = children;
return _this;
}
Object.defineProperty(NodeWithChildren.prototype, "firstChild", {
// Aliases
/** First child of the node. */
get: function () {
var _a;
return (_a = this.children[0]) !== null && _a !== void 0 ? _a : null;
},
enumerable: false,
configurable: true
});
Object.defineProperty(NodeWithChildren.prototype, "lastChild", {
/** Last child of the node. */
get: function () {
return this.children.length > 0
? this.children[this.children.length - 1]
: null;
},
enumerable: false,
configurable: true
});
Object.defineProperty(NodeWithChildren.prototype, "childNodes", {
/**
* Same as {@link children}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get: function () {
return this.children;
},
set: function (children) {
this.children = children;
},
enumerable: false,
configurable: true
});
return NodeWithChildren;
}(Node));
exports.NodeWithChildren = NodeWithChildren;
var CDATA = /** @class */ (function (_super) {
__extends(CDATA, _super);
function CDATA() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = domelementtype_1.ElementType.CDATA;
return _this;
}
Object.defineProperty(CDATA.prototype, "nodeType", {
get: function () {
return 4;
},
enumerable: false,
configurable: true
});
return CDATA;
}(NodeWithChildren));
exports.CDATA = CDATA;
/**
* The root node of the document.
*/
var Document = /** @class */ (function (_super) {
__extends(Document, _super);
function Document() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.type = domelementtype_1.ElementType.Root;
return _this;
}
Object.defineProperty(Document.prototype, "nodeType", {
get: function () {
return 9;
},
enumerable: false,
configurable: true
});
return Document;
}(NodeWithChildren));
exports.Document = Document;
/**
* An element within the DOM.
*/
var Element = /** @class */ (function (_super) {
__extends(Element, _super);
/**
* @param name Name of the tag, eg. `div`, `span`.
* @param attribs Object mapping attribute names to attribute values.
* @param children Children of the node.
*/
function Element(name, attribs, children, type) {
if (children === void 0) { children = []; }
if (type === void 0) { type = name === "script"
? domelementtype_1.ElementType.Script
: name === "style"
? domelementtype_1.ElementType.Style
: domelementtype_1.ElementType.Tag; }
var _this = _super.call(this, children) || this;
_this.name = name;
_this.attribs = attribs;
_this.type = type;
return _this;
}
Object.defineProperty(Element.prototype, "nodeType", {
get: function () {
return 1;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Element.prototype, "tagName", {
// DOM Level 1 aliases
/**
* Same as {@link name}.
* [DOM spec](https://dom.spec.whatwg.org)-compatible alias.
*/
get: function () {
return this.name;
},
set: function (name) {
this.name = name;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Element.prototype, "attributes", {
get: function () {
var _this = this;
return Object.keys(this.attribs).map(function (name) {
var _a, _b;
return ({
name: name,
value: _this.attribs[name],
namespace: (_a = _this["x-attribsNamespace"]) === null || _a === void 0 ? void 0 : _a[name],
prefix: (_b = _this["x-attribsPrefix"]) === null || _b === void 0 ? void 0 : _b[name],
});
});
},
enumerable: false,
configurable: true
});
return Element;
}(NodeWithChildren));
exports.Element = Element;
/**
* @param node Node to check.
* @returns `true` if the node is a `Element`, `false` otherwise.
*/
function isTag(node) {
return (0, domelementtype_1.isTag)(node);
}
exports.isTag = isTag;
/**
* @param node Node to check.
* @returns `true` if the node has the type `CDATA`, `false` otherwise.
*/
function isCDATA(node) {
return node.type === domelementtype_1.ElementType.CDATA;
}
exports.isCDATA = isCDATA;
/**
* @param node Node to check.
* @returns `true` if the node has the type `Text`, `false` otherwise.
*/
function isText(node) {
return node.type === domelementtype_1.ElementType.Text;
}
exports.isText = isText;
/**
* @param node Node to check.
* @returns `true` if the node has the type `Comment`, `false` otherwise.
*/
function isComment(node) {
return node.type === domelementtype_1.ElementType.Comment;
}
exports.isComment = isComment;
/**
* @param node Node to check.
* @returns `true` if the node has the type `ProcessingInstruction`, `false` otherwise.
*/
function isDirective(node) {
return node.type === domelementtype_1.ElementType.Directive;
}
exports.isDirective = isDirective;
/**
* @param node Node to check.
* @returns `true` if the node has the type `ProcessingInstruction`, `false` otherwise.
*/
function isDocument(node) {
return node.type === domelementtype_1.ElementType.Root;
}
exports.isDocument = isDocument;
/**
* @param node Node to check.
* @returns `true` if the node has children, `false` otherwise.
*/
function hasChildren(node) {
return Object.prototype.hasOwnProperty.call(node, "children");
}
exports.hasChildren = hasChildren;
/**
* Clone a node, and optionally its children.
*
* @param recursive Clone child nodes as well.
* @returns A clone of the node.
*/
function cloneNode(node, recursive) {
if (recursive === void 0) { recursive = false; }
var result;
if (isText(node)) {
result = new Text(node.data);
}
else if (isComment(node)) {
result = new Comment(node.data);
}
else if (isTag(node)) {
var children = recursive ? cloneChildren(node.children) : [];
var clone_1 = new Element(node.name, __assign({}, node.attribs), children);
children.forEach(function (child) { return (child.parent = clone_1); });
if (node.namespace != null) {
clone_1.namespace = node.namespace;
}
if (node["x-attribsNamespace"]) {
clone_1["x-attribsNamespace"] = __assign({}, node["x-attribsNamespace"]);
}
if (node["x-attribsPrefix"]) {
clone_1["x-attribsPrefix"] = __assign({}, node["x-attribsPrefix"]);
}
result = clone_1;
}
else if (isCDATA(node)) {
var children = recursive ? cloneChildren(node.children) : [];
var clone_2 = new CDATA(children);
children.forEach(function (child) { return (child.parent = clone_2); });
result = clone_2;
}
else if (isDocument(node)) {
var children = recursive ? cloneChildren(node.children) : [];
var clone_3 = new Document(children);
children.forEach(function (child) { return (child.parent = clone_3); });
if (node["x-mode"]) {
clone_3["x-mode"] = node["x-mode"];
}
result = clone_3;
}
else if (isDirective(node)) {
var instruction = new ProcessingInstruction(node.name, node.data);
if (node["x-name"] != null) {
instruction["x-name"] = node["x-name"];
instruction["x-publicId"] = node["x-publicId"];
instruction["x-systemId"] = node["x-systemId"];
}
result = instruction;
}
else {
throw new Error("Not implemented yet: ".concat(node.type));
}
result.startIndex = node.startIndex;
result.endIndex = node.endIndex;
if (node.sourceCodeLocation != null) {
result.sourceCodeLocation = node.sourceCodeLocation;
}
return result;
}
exports.cloneNode = cloneNode;
function cloneChildren(childs) {
var children = childs.map(function (child) { return cloneNode(child, true); });
for (var i = 1; i < children.length; i++) {
children[i].prev = children[i - 1];
children[i - 1].next = children[i];
}
return children;
}

View File

@@ -0,0 +1,73 @@
{
"name": "domhandler",
"version": "5.0.3",
"description": "Handler for htmlparser2 that turns pages into a dom",
"author": "Felix Boehm <me@feedic.com>",
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
},
"license": "BSD-2-Clause",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"module": "lib/esm/index.js",
"exports": {
"require": "./lib/index.js",
"import": "./lib/esm/index.js"
},
"sideEffects": false,
"files": [
"lib"
],
"scripts": {
"test": "npm run test:jest && npm run lint",
"test:jest": "jest",
"lint": "npm run lint:es && npm run lint:prettier",
"lint:es": "eslint --ignore-path .gitignore .",
"lint:prettier": "npm run prettier -- --check",
"format": "npm run format:es && npm run format:prettier",
"format:es": "npm run lint:es -- --fix",
"format:prettier": "npm run prettier -- --write",
"prettier": "prettier \"**/*.{ts,md,json,yml}\" --ignore-path .gitignore",
"build": "npm run build:cjs && npm run build:esm",
"build:cjs": "tsc",
"build:esm": "tsc --module esnext --target es2019 --outDir lib/esm && echo '{\"type\":\"module\"}' > lib/esm/package.json",
"prepare": "npm run build"
},
"repository": {
"type": "git",
"url": "git://github.com/fb55/domhandler.git"
},
"keywords": [
"dom",
"htmlparser2"
],
"engines": {
"node": ">= 4"
},
"dependencies": {
"domelementtype": "^2.3.0"
},
"devDependencies": {
"@types/jest": "^27.4.1",
"@types/node": "^17.0.30",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.21.0",
"eslint": "^8.14.0",
"eslint-config-prettier": "^8.5.0",
"htmlparser2": "^8.0.0",
"jest": "^27.5.1",
"prettier": "^2.6.2",
"ts-jest": "^27.1.4",
"typescript": "^4.6.4"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"moduleNameMapper": {
"^(.*)\\.js$": "$1"
}
},
"prettier": {
"tabWidth": 4
}
}

View File

@@ -0,0 +1,92 @@
# domhandler [![Build Status](https://travis-ci.com/fb55/domhandler.svg?branch=master)](https://travis-ci.com/fb55/domhandler)
The DOM handler creates a tree containing all nodes of a page.
The tree can be manipulated using the [domutils](https://github.com/fb55/domutils)
or [cheerio](https://github.com/cheeriojs/cheerio) libraries and
rendered using [dom-serializer](https://github.com/cheeriojs/dom-serializer) .
## Usage
```javascript
const handler = new DomHandler([ <func> callback(err, dom), ] [ <obj> options ]);
// const parser = new Parser(handler[, options]);
```
Available options are described below.
## Example
```javascript
const { Parser } = require("htmlparser2");
const { DomHandler } = require("domhandler");
const rawHtml =
"Xyz <script language= javascript>var foo = '<<bar>>';</script><!--<!-- Waah! -- -->";
const handler = new DomHandler((error, dom) => {
if (error) {
// Handle error
} else {
// Parsing completed, do something
console.log(dom);
}
});
const parser = new Parser(handler);
parser.write(rawHtml);
parser.end();
```
Output:
```javascript
[
{
data: "Xyz ",
type: "text",
},
{
type: "script",
name: "script",
attribs: {
language: "javascript",
},
children: [
{
data: "var foo = '<bar>';<",
type: "text",
},
],
},
{
data: "<!-- Waah! -- ",
type: "comment",
},
];
```
## Option: `withStartIndices`
Add a `startIndex` property to nodes.
When the parser is used in a non-streaming fashion, `startIndex` is an integer
indicating the position of the start of the node in the document.
The default value is `false`.
## Option: `withEndIndices`
Add an `endIndex` property to nodes.
When the parser is used in a non-streaming fashion, `endIndex` is an integer
indicating the position of the end of the node in the document.
The default value is `false`.
---
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.
## `domhandler` for enterprise
Available as part of the Tidelift Subscription
The maintainers of `domhandler` 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-domhandler?utm_source=npm-domhandler&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

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.

View File

@@ -0,0 +1,71 @@
import type { AnyNode } from "domhandler";
/**
* The medium of a media item.
*
* @category Feeds
*/
export type FeedItemMediaMedium = "image" | "audio" | "video" | "document" | "executable";
/**
* The type of a media item.
*
* @category Feeds
*/
export type FeedItemMediaExpression = "sample" | "full" | "nonstop";
/**
* A media item of a feed entry.
*
* @category Feeds
*/
export interface FeedItemMedia {
medium: FeedItemMediaMedium | undefined;
isDefault: boolean;
url?: string;
fileSize?: number;
type?: string;
expression?: FeedItemMediaExpression;
bitrate?: number;
framerate?: number;
samplingrate?: number;
channels?: number;
duration?: number;
height?: number;
width?: number;
lang?: string;
}
/**
* An entry of a feed.
*
* @category Feeds
*/
export interface FeedItem {
id?: string;
title?: string;
link?: string;
description?: string;
pubDate?: Date;
media: FeedItemMedia[];
}
/**
* The root of a feed.
*
* @category Feeds
*/
export interface Feed {
type: string;
id?: string;
title?: string;
link?: string;
description?: string;
updated?: Date;
author?: string;
items: FeedItem[];
}
/**
* Get the feed object from the root of a DOM tree.
*
* @category Feeds
* @param doc - The DOM to to extract the feed from.
* @returns The feed.
*/
export declare function getFeed(doc: AnyNode[]): Feed | null;
//# sourceMappingURL=feeds.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"feeds.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["feeds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAW,MAAM,YAAY,CAAC;AAInD;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GACzB,OAAO,GACP,OAAO,GACP,OAAO,GACP,UAAU,GACV,YAAY,CAAC;AAEnB;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAEpE;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI,CAQnD"}

View File

@@ -0,0 +1,183 @@
import { textContent } from "./stringify.js";
import { getElementsByTagName } from "./legacy.js";
/**
* Get the feed object from the root of a DOM tree.
*
* @category Feeds
* @param doc - The DOM to to extract the feed from.
* @returns The feed.
*/
export function getFeed(doc) {
const feedRoot = getOneElement(isValidFeed, doc);
return !feedRoot
? null
: feedRoot.name === "feed"
? getAtomFeed(feedRoot)
: getRssFeed(feedRoot);
}
/**
* Parse an Atom feed.
*
* @param feedRoot The root of the feed.
* @returns The parsed feed.
*/
function getAtomFeed(feedRoot) {
var _a;
const childs = feedRoot.children;
const feed = {
type: "atom",
items: getElementsByTagName("entry", childs).map((item) => {
var _a;
const { children } = item;
const entry = { media: getMediaElements(children) };
addConditionally(entry, "id", "id", children);
addConditionally(entry, "title", "title", children);
const href = (_a = getOneElement("link", children)) === null || _a === void 0 ? void 0 : _a.attribs["href"];
if (href) {
entry.link = href;
}
const description = fetch("summary", children) || fetch("content", children);
if (description) {
entry.description = description;
}
const pubDate = fetch("updated", children);
if (pubDate) {
entry.pubDate = new Date(pubDate);
}
return entry;
}),
};
addConditionally(feed, "id", "id", childs);
addConditionally(feed, "title", "title", childs);
const href = (_a = getOneElement("link", childs)) === null || _a === void 0 ? void 0 : _a.attribs["href"];
if (href) {
feed.link = href;
}
addConditionally(feed, "description", "subtitle", childs);
const updated = fetch("updated", childs);
if (updated) {
feed.updated = new Date(updated);
}
addConditionally(feed, "author", "email", childs, true);
return feed;
}
/**
* Parse a RSS feed.
*
* @param feedRoot The root of the feed.
* @returns The parsed feed.
*/
function getRssFeed(feedRoot) {
var _a, _b;
const childs = (_b = (_a = getOneElement("channel", feedRoot.children)) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : [];
const feed = {
type: feedRoot.name.substr(0, 3),
id: "",
items: getElementsByTagName("item", feedRoot.children).map((item) => {
const { children } = item;
const entry = { media: getMediaElements(children) };
addConditionally(entry, "id", "guid", children);
addConditionally(entry, "title", "title", children);
addConditionally(entry, "link", "link", children);
addConditionally(entry, "description", "description", children);
const pubDate = fetch("pubDate", children) || fetch("dc:date", children);
if (pubDate)
entry.pubDate = new Date(pubDate);
return entry;
}),
};
addConditionally(feed, "title", "title", childs);
addConditionally(feed, "link", "link", childs);
addConditionally(feed, "description", "description", childs);
const updated = fetch("lastBuildDate", childs);
if (updated) {
feed.updated = new Date(updated);
}
addConditionally(feed, "author", "managingEditor", childs, true);
return feed;
}
const MEDIA_KEYS_STRING = ["url", "type", "lang"];
const MEDIA_KEYS_INT = [
"fileSize",
"bitrate",
"framerate",
"samplingrate",
"channels",
"duration",
"height",
"width",
];
/**
* Get all media elements of a feed item.
*
* @param where Nodes to search in.
* @returns Media elements.
*/
function getMediaElements(where) {
return getElementsByTagName("media:content", where).map((elem) => {
const { attribs } = elem;
const media = {
medium: attribs["medium"],
isDefault: !!attribs["isDefault"],
};
for (const attrib of MEDIA_KEYS_STRING) {
if (attribs[attrib]) {
media[attrib] = attribs[attrib];
}
}
for (const attrib of MEDIA_KEYS_INT) {
if (attribs[attrib]) {
media[attrib] = parseInt(attribs[attrib], 10);
}
}
if (attribs["expression"]) {
media.expression = attribs["expression"];
}
return media;
});
}
/**
* Get one element by tag name.
*
* @param tagName Tag name to look for
* @param node Node to search in
* @returns The element or null
*/
function getOneElement(tagName, node) {
return getElementsByTagName(tagName, node, true, 1)[0];
}
/**
* Get the text content of an element with a certain tag name.
*
* @param tagName Tag name to look for.
* @param where Node to search in.
* @param recurse Whether to recurse into child nodes.
* @returns The text content of the element.
*/
function fetch(tagName, where, recurse = false) {
return textContent(getElementsByTagName(tagName, where, recurse, 1)).trim();
}
/**
* Adds a property to an object if it has a value.
*
* @param obj Object to be extended
* @param prop Property name
* @param tagName Tag name that contains the conditionally added property
* @param where Element to search for the property
* @param recurse Whether to recurse into child nodes.
*/
function addConditionally(obj, prop, tagName, where, recurse = false) {
const val = fetch(tagName, where, recurse);
if (val)
obj[prop] = val;
}
/**
* Checks if an element is a feed root node.
*
* @param value The name of the element to check.
* @returns Whether an element is a feed root node.
*/
function isValidFeed(value) {
return value === "rss" || value === "feed" || value === "rdf:RDF";
}
//# sourceMappingURL=feeds.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,59 @@
import { AnyNode } from "domhandler";
/**
* Given an array of nodes, remove any member that is contained by another
* member.
*
* @category Helpers
* @param nodes Nodes to filter.
* @returns Remaining nodes that aren't contained by other nodes.
*/
export declare function removeSubsets(nodes: AnyNode[]): AnyNode[];
/**
* @category Helpers
* @see {@link http://dom.spec.whatwg.org/#dom-node-comparedocumentposition}
*/
export declare const enum DocumentPosition {
DISCONNECTED = 1,
PRECEDING = 2,
FOLLOWING = 4,
CONTAINS = 8,
CONTAINED_BY = 16
}
/**
* Compare the position of one node against another node in any other document,
* returning a bitmask with the values from {@link DocumentPosition}.
*
* 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
*
* @category Helpers
* @param nodeA The first node to use in the comparison
* @param nodeB The second node to use in the comparison
* @returns A bitmask describing the input nodes' relative position.
*
* See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for
* a description of these values.
*/
export declare function compareDocumentPosition(nodeA: AnyNode, nodeB: AnyNode): number;
/**
* Sort an array of nodes based on their relative position in the document,
* removing any duplicate nodes. If the array contains nodes that do not belong
* to the same document, sort order is unspecified.
*
* @category Helpers
* @param nodes Array of DOM nodes.
* @returns Collection of unique nodes, sorted in document order.
*/
export declare function uniqueSort<T extends AnyNode>(nodes: T[]): T[];
//# sourceMappingURL=helpers.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"helpers.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,OAAO,EAAc,MAAM,YAAY,CAAC;AAE9D;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CA6BzD;AACD;;;GAGG;AACH,0BAAkB,gBAAgB;IAC9B,YAAY,IAAI;IAChB,SAAS,IAAI;IACb,SAAS,IAAI;IACb,QAAQ,IAAI;IACZ,YAAY,KAAK;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,uBAAuB,CACnC,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,OAAO,GACf,MAAM,CA4CR;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAc7D"}

View File

@@ -0,0 +1,136 @@
import { hasChildren } from "domhandler";
/**
* Given an array of nodes, remove any member that is contained by another
* member.
*
* @category Helpers
* @param nodes Nodes to filter.
* @returns Remaining nodes that aren't contained by other nodes.
*/
export function removeSubsets(nodes) {
let idx = nodes.length;
/*
* Check if each node (or one of its ancestors) is already contained in the
* array.
*/
while (--idx >= 0) {
const node = nodes[idx];
/*
* Remove the node if it is not unique.
* We are going through the array from the end, so we only
* have to check nodes that preceed the node under consideration in the array.
*/
if (idx > 0 && nodes.lastIndexOf(node, idx - 1) >= 0) {
nodes.splice(idx, 1);
continue;
}
for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
if (nodes.includes(ancestor)) {
nodes.splice(idx, 1);
break;
}
}
}
return nodes;
}
/**
* @category Helpers
* @see {@link http://dom.spec.whatwg.org/#dom-node-comparedocumentposition}
*/
export var DocumentPosition;
(function (DocumentPosition) {
DocumentPosition[DocumentPosition["DISCONNECTED"] = 1] = "DISCONNECTED";
DocumentPosition[DocumentPosition["PRECEDING"] = 2] = "PRECEDING";
DocumentPosition[DocumentPosition["FOLLOWING"] = 4] = "FOLLOWING";
DocumentPosition[DocumentPosition["CONTAINS"] = 8] = "CONTAINS";
DocumentPosition[DocumentPosition["CONTAINED_BY"] = 16] = "CONTAINED_BY";
})(DocumentPosition || (DocumentPosition = {}));
/**
* Compare the position of one node against another node in any other document,
* returning a bitmask with the values from {@link DocumentPosition}.
*
* 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
*
* @category Helpers
* @param nodeA The first node to use in the comparison
* @param nodeB The second node to use in the comparison
* @returns A bitmask describing the input nodes' relative position.
*
* See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for
* a description of these values.
*/
export function compareDocumentPosition(nodeA, nodeB) {
const aParents = [];
const bParents = [];
if (nodeA === nodeB) {
return 0;
}
let current = hasChildren(nodeA) ? nodeA : nodeA.parent;
while (current) {
aParents.unshift(current);
current = current.parent;
}
current = hasChildren(nodeB) ? nodeB : nodeB.parent;
while (current) {
bParents.unshift(current);
current = current.parent;
}
const maxIdx = Math.min(aParents.length, bParents.length);
let idx = 0;
while (idx < maxIdx && aParents[idx] === bParents[idx]) {
idx++;
}
if (idx === 0) {
return DocumentPosition.DISCONNECTED;
}
const sharedParent = aParents[idx - 1];
const siblings = sharedParent.children;
const aSibling = aParents[idx];
const bSibling = bParents[idx];
if (siblings.indexOf(aSibling) > siblings.indexOf(bSibling)) {
if (sharedParent === nodeB) {
return DocumentPosition.FOLLOWING | DocumentPosition.CONTAINED_BY;
}
return DocumentPosition.FOLLOWING;
}
if (sharedParent === nodeA) {
return DocumentPosition.PRECEDING | DocumentPosition.CONTAINS;
}
return DocumentPosition.PRECEDING;
}
/**
* Sort an array of nodes based on their relative position in the document,
* removing any duplicate nodes. If the array contains nodes that do not belong
* to the same document, sort order is unspecified.
*
* @category Helpers
* @param nodes Array of DOM nodes.
* @returns Collection of unique nodes, sorted in document order.
*/
export function uniqueSort(nodes) {
nodes = nodes.filter((node, i, arr) => !arr.includes(node, i + 1));
nodes.sort((a, b) => {
const relative = compareDocumentPosition(a, b);
if (relative & DocumentPosition.PRECEDING) {
return -1;
}
else if (relative & DocumentPosition.FOLLOWING) {
return 1;
}
return 0;
});
return nodes;
}
//# sourceMappingURL=helpers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"helpers.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["helpers.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,10 @@
export * from "./stringify.js";
export * from "./traversal.js";
export * from "./manipulation.js";
export * from "./querying.js";
export * from "./legacy.js";
export * from "./helpers.js";
export * from "./feeds.js";
/** @deprecated Use these methods from `domhandler` directly. */
export { isTag, isCDATA, isText, isComment, isDocument, hasChildren, } from "domhandler";
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,gEAAgE;AAChE,OAAO,EACH,KAAK,EACL,OAAO,EACP,MAAM,EACN,SAAS,EACT,UAAU,EACV,WAAW,GACd,MAAM,YAAY,CAAC"}

View File

@@ -0,0 +1,10 @@
export * from "./stringify.js";
export * from "./traversal.js";
export * from "./manipulation.js";
export * from "./querying.js";
export * from "./legacy.js";
export * from "./helpers.js";
export * from "./feeds.js";
/** @deprecated Use these methods from `domhandler` directly. */
export { isTag, isCDATA, isText, isComment, isDocument, hasChildren, } from "domhandler";
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,gEAAgE;AAChE,OAAO,EACH,KAAK,EACL,OAAO,EACP,MAAM,EACN,SAAS,EACT,UAAU,EACV,WAAW,GACd,MAAM,YAAY,CAAC"}

View File

@@ -0,0 +1,79 @@
import { AnyNode, Element } from "domhandler";
import type { ElementType } from "domelementtype";
/**
* An object with keys to check elements against. If a key is `tag_name`,
* `tag_type` or `tag_contains`, it will check the value against that specific
* value. Otherwise, it will check an attribute with the key's name.
*
* @category Legacy Query Functions
*/
export interface TestElementOpts {
tag_name?: string | ((name: string) => boolean);
tag_type?: string | ((name: string) => boolean);
tag_contains?: string | ((data?: string) => boolean);
[attributeName: string]: undefined | string | ((attributeValue: string) => boolean);
}
/**
* Checks whether a node matches the description in `options`.
*
* @category Legacy Query Functions
* @param options An object describing nodes to look for.
* @param node The element to test.
* @returns Whether the element matches the description in `options`.
*/
export declare function testElement(options: TestElementOpts, node: AnyNode): boolean;
/**
* Returns all nodes that match `options`.
*
* @category Legacy Query Functions
* @param options An object describing nodes to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes that match `options`.
*/
export declare function getElements(options: TestElementOpts, nodes: AnyNode | AnyNode[], recurse: boolean, limit?: number): AnyNode[];
/**
* Returns the node with the supplied ID.
*
* @category Legacy Query Functions
* @param id The unique ID attribute value to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @returns The node with the supplied ID.
*/
export declare function getElementById(id: string | ((id: string) => boolean), nodes: AnyNode | AnyNode[], recurse?: boolean): Element | null;
/**
* Returns all nodes with the supplied `tagName`.
*
* @category Legacy Query Functions
* @param tagName Tag name to search for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `tagName`.
*/
export declare function getElementsByTagName(tagName: string | ((name: string) => boolean), nodes: AnyNode | AnyNode[], recurse?: boolean, limit?: number): Element[];
/**
* Returns all nodes with the supplied `className`.
*
* @category Legacy Query Functions
* @param className Class name to search for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `className`.
*/
export declare function getElementsByClassName(className: string | ((name: string) => boolean), nodes: AnyNode | AnyNode[], recurse?: boolean, limit?: number): Element[];
/**
* Returns all nodes with the supplied `type`.
*
* @category Legacy Query Functions
* @param type Element type to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `type`.
*/
export declare function getElementsByTagType(type: ElementType | ((type: ElementType) => boolean), nodes: AnyNode | AnyNode[], recurse?: boolean, limit?: number): AnyNode[];
//# sourceMappingURL=legacy.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"legacy.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["legacy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAKlD;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACrD,CAAC,aAAa,EAAE,MAAM,GAChB,SAAS,GACT,MAAM,GACN,CAAC,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;CAC/C;AAkFD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAG5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACvB,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,EAAE,OAAO,EAChB,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAGX;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC1B,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,EACtC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,UAAO,GACf,OAAO,GAAG,IAAI,CAGhB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAChC,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,EAC7C,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,UAAO,EACd,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAOX;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAClC,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,EAC/C,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,UAAO,EACd,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAOX;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAChC,IAAI,EAAE,WAAW,GAAG,CAAC,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,EACpD,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,UAAO,EACd,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAEX"}

View File

@@ -0,0 +1,152 @@
import { isTag, isText } from "domhandler";
import { filter, findOne } from "./querying.js";
/**
* A map of functions to check nodes against.
*/
const Checks = {
tag_name(name) {
if (typeof name === "function") {
return (elem) => isTag(elem) && name(elem.name);
}
else if (name === "*") {
return isTag;
}
return (elem) => isTag(elem) && elem.name === name;
},
tag_type(type) {
if (typeof type === "function") {
return (elem) => type(elem.type);
}
return (elem) => elem.type === type;
},
tag_contains(data) {
if (typeof data === "function") {
return (elem) => isText(elem) && data(elem.data);
}
return (elem) => isText(elem) && elem.data === data;
},
};
/**
* Returns a function to check whether a node has an attribute with a particular
* value.
*
* @param attrib Attribute to check.
* @param value Attribute value to look for.
* @returns A function to check whether the a node has an attribute with a
* particular value.
*/
function getAttribCheck(attrib, value) {
if (typeof value === "function") {
return (elem) => isTag(elem) && value(elem.attribs[attrib]);
}
return (elem) => isTag(elem) && elem.attribs[attrib] === value;
}
/**
* Returns a function that returns `true` if either of the input functions
* returns `true` for a node.
*
* @param a First function to combine.
* @param b Second function to combine.
* @returns A function taking a node and returning `true` if either of the input
* functions returns `true` for the node.
*/
function combineFuncs(a, b) {
return (elem) => a(elem) || b(elem);
}
/**
* Returns a function that executes all checks in `options` and returns `true`
* if any of them match a node.
*
* @param options An object describing nodes to look for.
* @returns A function that executes all checks in `options` and returns `true`
* if any of them match a node.
*/
function compileTest(options) {
const funcs = Object.keys(options).map((key) => {
const value = options[key];
return Object.prototype.hasOwnProperty.call(Checks, key)
? Checks[key](value)
: getAttribCheck(key, value);
});
return funcs.length === 0 ? null : funcs.reduce(combineFuncs);
}
/**
* Checks whether a node matches the description in `options`.
*
* @category Legacy Query Functions
* @param options An object describing nodes to look for.
* @param node The element to test.
* @returns Whether the element matches the description in `options`.
*/
export function testElement(options, node) {
const test = compileTest(options);
return test ? test(node) : true;
}
/**
* Returns all nodes that match `options`.
*
* @category Legacy Query Functions
* @param options An object describing nodes to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes that match `options`.
*/
export function getElements(options, nodes, recurse, limit = Infinity) {
const test = compileTest(options);
return test ? filter(test, nodes, recurse, limit) : [];
}
/**
* Returns the node with the supplied ID.
*
* @category Legacy Query Functions
* @param id The unique ID attribute value to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @returns The node with the supplied ID.
*/
export function getElementById(id, nodes, recurse = true) {
if (!Array.isArray(nodes))
nodes = [nodes];
return findOne(getAttribCheck("id", id), nodes, recurse);
}
/**
* Returns all nodes with the supplied `tagName`.
*
* @category Legacy Query Functions
* @param tagName Tag name to search for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `tagName`.
*/
export function getElementsByTagName(tagName, nodes, recurse = true, limit = Infinity) {
return filter(Checks["tag_name"](tagName), nodes, recurse, limit);
}
/**
* Returns all nodes with the supplied `className`.
*
* @category Legacy Query Functions
* @param className Class name to search for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `className`.
*/
export function getElementsByClassName(className, nodes, recurse = true, limit = Infinity) {
return filter(getAttribCheck("class", className), nodes, recurse, limit);
}
/**
* Returns all nodes with the supplied `type`.
*
* @category Legacy Query Functions
* @param type Element type to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `type`.
*/
export function getElementsByTagType(type, nodes, recurse = true, limit = Infinity) {
return filter(Checks["tag_type"](type), nodes, recurse, limit);
}
//# sourceMappingURL=legacy.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"legacy.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["legacy.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,49 @@
import type { ChildNode, ParentNode } from "domhandler";
/**
* Remove an element from the dom
*
* @category Manipulation
* @param elem The element to be removed
*/
export declare function removeElement(elem: ChildNode): void;
/**
* Replace an element in the dom
*
* @category Manipulation
* @param elem The element to be replaced
* @param replacement The element to be added
*/
export declare function replaceElement(elem: ChildNode, replacement: ChildNode): void;
/**
* Append a child to an element.
*
* @category Manipulation
* @param parent The element to append to.
* @param child The element to be added as a child.
*/
export declare function appendChild(parent: ParentNode, child: ChildNode): void;
/**
* Append an element after another.
*
* @category Manipulation
* @param elem The element to append after.
* @param next The element be added.
*/
export declare function append(elem: ChildNode, next: ChildNode): void;
/**
* Prepend a child to an element.
*
* @category Manipulation
* @param parent The element to prepend before.
* @param child The element to be added as a child.
*/
export declare function prependChild(parent: ParentNode, child: ChildNode): void;
/**
* Prepend an element before another.
*
* @category Manipulation
* @param elem The element to prepend before.
* @param prev The element be added.
*/
export declare function prepend(elem: ChildNode, prev: ChildNode): void;
//# sourceMappingURL=manipulation.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"manipulation.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["manipulation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAcnD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,GAAG,IAAI,CAiB5E;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAatE;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAoB7D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAavE;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAiB9D"}

View File

@@ -0,0 +1,134 @@
/**
* Remove an element from the dom
*
* @category Manipulation
* @param elem The element to be removed
*/
export function removeElement(elem) {
if (elem.prev)
elem.prev.next = elem.next;
if (elem.next)
elem.next.prev = elem.prev;
if (elem.parent) {
const childs = elem.parent.children;
const childsIndex = childs.lastIndexOf(elem);
if (childsIndex >= 0) {
childs.splice(childsIndex, 1);
}
}
elem.next = null;
elem.prev = null;
elem.parent = null;
}
/**
* Replace an element in the dom
*
* @category Manipulation
* @param elem The element to be replaced
* @param replacement The element to be added
*/
export function replaceElement(elem, replacement) {
const prev = (replacement.prev = elem.prev);
if (prev) {
prev.next = replacement;
}
const next = (replacement.next = elem.next);
if (next) {
next.prev = replacement;
}
const parent = (replacement.parent = elem.parent);
if (parent) {
const childs = parent.children;
childs[childs.lastIndexOf(elem)] = replacement;
elem.parent = null;
}
}
/**
* Append a child to an element.
*
* @category Manipulation
* @param parent The element to append to.
* @param child The element to be added as a child.
*/
export function appendChild(parent, child) {
removeElement(child);
child.next = null;
child.parent = parent;
if (parent.children.push(child) > 1) {
const sibling = parent.children[parent.children.length - 2];
sibling.next = child;
child.prev = sibling;
}
else {
child.prev = null;
}
}
/**
* Append an element after another.
*
* @category Manipulation
* @param elem The element to append after.
* @param next The element be added.
*/
export function append(elem, next) {
removeElement(next);
const { parent } = elem;
const currNext = elem.next;
next.next = currNext;
next.prev = elem;
elem.next = next;
next.parent = parent;
if (currNext) {
currNext.prev = next;
if (parent) {
const childs = parent.children;
childs.splice(childs.lastIndexOf(currNext), 0, next);
}
}
else if (parent) {
parent.children.push(next);
}
}
/**
* Prepend a child to an element.
*
* @category Manipulation
* @param parent The element to prepend before.
* @param child The element to be added as a child.
*/
export function prependChild(parent, child) {
removeElement(child);
child.parent = parent;
child.prev = null;
if (parent.children.unshift(child) !== 1) {
const sibling = parent.children[1];
sibling.prev = child;
child.next = sibling;
}
else {
child.next = null;
}
}
/**
* Prepend an element before another.
*
* @category Manipulation
* @param elem The element to prepend before.
* @param prev The element be added.
*/
export function prepend(elem, prev) {
removeElement(prev);
const { parent } = elem;
if (parent) {
const childs = parent.children;
childs.splice(childs.indexOf(elem), 0, prev);
}
if (elem.prev) {
elem.prev.next = prev;
}
prev.parent = parent;
prev.prev = elem.prev;
prev.next = elem;
elem.prev = prev;
}
//# sourceMappingURL=manipulation.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"manipulation.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["manipulation.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1 @@
{"type":"module"}

View File

@@ -0,0 +1,64 @@
import { Element, AnyNode, ParentNode } from "domhandler";
/**
* Search a node and its children for nodes passing a test function. If `node` is not an array, it will be wrapped in one.
*
* @category Querying
* @param test Function to test nodes on.
* @param node Node to search. Will be included in the result set if it matches.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes passing `test`.
*/
export declare function filter(test: (elem: AnyNode) => boolean, node: AnyNode | AnyNode[], recurse?: boolean, limit?: number): AnyNode[];
/**
* Search an array of nodes and their children for nodes passing a test function.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes passing `test`.
*/
export declare function find(test: (elem: AnyNode) => boolean, nodes: AnyNode[] | ParentNode, recurse: boolean, limit: number): AnyNode[];
/**
* Finds the first element inside of an array that matches a test function. This is an alias for `Array.prototype.find`.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns The first node in the array that passes `test`.
* @deprecated Use `Array.prototype.find` directly.
*/
export declare function findOneChild<T>(test: (elem: T) => boolean, nodes: T[]): T | undefined;
/**
* Finds one element in a tree that passes a test.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Node or array of nodes to search.
* @param recurse Also consider child nodes.
* @returns The first node that passes `test`.
*/
export declare function findOne(test: (elem: Element) => boolean, nodes: AnyNode[] | ParentNode, recurse?: boolean): Element | null;
/**
* Checks if a tree of nodes contains at least one node passing a test.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns Whether a tree of nodes contains at least one node passing the test.
*/
export declare function existsOne(test: (elem: Element) => boolean, nodes: AnyNode[] | ParentNode): boolean;
/**
* Search an array of nodes and their children for elements passing a test function.
*
* Same as `find`, but limited to elements and with less options, leading to reduced complexity.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns All nodes passing `test`.
*/
export declare function findAll(test: (elem: Element) => boolean, nodes: AnyNode[] | ParentNode): Element[];
//# sourceMappingURL=querying.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"querying.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["querying.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE9E;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAClB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,EACzB,OAAO,UAAO,EACd,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAEX;AAED;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAChB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,EAC7B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GACd,OAAO,EAAE,CAuCX;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC1B,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,EAC1B,KAAK,EAAE,CAAC,EAAE,GACX,CAAC,GAAG,SAAS,CAEf;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CACnB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,EAC7B,OAAO,UAAO,GACf,OAAO,GAAG,IAAI,CAchB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACrB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,GAC9B,OAAO,CAMT;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CACnB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,GAC9B,OAAO,EAAE,CA4BX"}

View File

@@ -0,0 +1,142 @@
import { isTag, hasChildren } from "domhandler";
/**
* Search a node and its children for nodes passing a test function. If `node` is not an array, it will be wrapped in one.
*
* @category Querying
* @param test Function to test nodes on.
* @param node Node to search. Will be included in the result set if it matches.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes passing `test`.
*/
export function filter(test, node, recurse = true, limit = Infinity) {
return find(test, Array.isArray(node) ? node : [node], recurse, limit);
}
/**
* Search an array of nodes and their children for nodes passing a test function.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes passing `test`.
*/
export function find(test, nodes, recurse, limit) {
const result = [];
/** Stack of the arrays we are looking at. */
const nodeStack = [Array.isArray(nodes) ? nodes : [nodes]];
/** Stack of the indices within the arrays. */
const indexStack = [0];
for (;;) {
// First, check if the current array has any more elements to look at.
if (indexStack[0] >= nodeStack[0].length) {
// If we have no more arrays to look at, we are done.
if (indexStack.length === 1) {
return result;
}
// Otherwise, remove the current array from the stack.
nodeStack.shift();
indexStack.shift();
// Loop back to the start to continue with the next array.
continue;
}
const elem = nodeStack[0][indexStack[0]++];
if (test(elem)) {
result.push(elem);
if (--limit <= 0)
return result;
}
if (recurse && hasChildren(elem) && elem.children.length > 0) {
/*
* Add the children to the stack. We are depth-first, so this is
* the next array we look at.
*/
indexStack.unshift(0);
nodeStack.unshift(elem.children);
}
}
}
/**
* Finds the first element inside of an array that matches a test function. This is an alias for `Array.prototype.find`.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns The first node in the array that passes `test`.
* @deprecated Use `Array.prototype.find` directly.
*/
export function findOneChild(test, nodes) {
return nodes.find(test);
}
/**
* Finds one element in a tree that passes a test.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Node or array of nodes to search.
* @param recurse Also consider child nodes.
* @returns The first node that passes `test`.
*/
export function findOne(test, nodes, recurse = true) {
const searchedNodes = Array.isArray(nodes) ? nodes : [nodes];
for (let i = 0; i < searchedNodes.length; i++) {
const node = searchedNodes[i];
if (isTag(node) && test(node)) {
return node;
}
if (recurse && hasChildren(node) && node.children.length > 0) {
const found = findOne(test, node.children, true);
if (found)
return found;
}
}
return null;
}
/**
* Checks if a tree of nodes contains at least one node passing a test.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns Whether a tree of nodes contains at least one node passing the test.
*/
export function existsOne(test, nodes) {
return (Array.isArray(nodes) ? nodes : [nodes]).some((node) => (isTag(node) && test(node)) ||
(hasChildren(node) && existsOne(test, node.children)));
}
/**
* Search an array of nodes and their children for elements passing a test function.
*
* Same as `find`, but limited to elements and with less options, leading to reduced complexity.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns All nodes passing `test`.
*/
export function findAll(test, nodes) {
const result = [];
const nodeStack = [Array.isArray(nodes) ? nodes : [nodes]];
const indexStack = [0];
for (;;) {
if (indexStack[0] >= nodeStack[0].length) {
if (nodeStack.length === 1) {
return result;
}
// Otherwise, remove the current array from the stack.
nodeStack.shift();
indexStack.shift();
// Loop back to the start to continue with the next array.
continue;
}
const elem = nodeStack[0][indexStack[0]++];
if (isTag(elem) && test(elem))
result.push(elem);
if (hasChildren(elem) && elem.children.length > 0) {
indexStack.unshift(0);
nodeStack.unshift(elem.children);
}
}
}
//# sourceMappingURL=querying.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"querying.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["querying.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,46 @@
import { AnyNode } from "domhandler";
import { DomSerializerOptions } from "dom-serializer";
/**
* @category Stringify
* @deprecated Use the `dom-serializer` module directly.
* @param node Node to get the outer HTML of.
* @param options Options for serialization.
* @returns `node`'s outer HTML.
*/
export declare function getOuterHTML(node: AnyNode | ArrayLike<AnyNode>, options?: DomSerializerOptions): string;
/**
* @category Stringify
* @deprecated Use the `dom-serializer` module directly.
* @param node Node to get the inner HTML of.
* @param options Options for serialization.
* @returns `node`'s inner HTML.
*/
export declare function getInnerHTML(node: AnyNode, options?: DomSerializerOptions): string;
/**
* Get a node's inner text. Same as `textContent`, but inserts newlines for `<br>` tags. Ignores comments.
*
* @category Stringify
* @deprecated Use `textContent` instead.
* @param node Node to get the inner text of.
* @returns `node`'s inner text.
*/
export declare function getText(node: AnyNode | AnyNode[]): string;
/**
* Get a node's text content. Ignores comments.
*
* @category Stringify
* @param node Node to get the text content of.
* @returns `node`'s text content.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent}
*/
export declare function textContent(node: AnyNode | AnyNode[]): string;
/**
* Get a node's inner text, ignoring `<script>` and `<style>` tags. Ignores comments.
*
* @category Stringify
* @param node Node to get the inner text of.
* @returns `node`'s inner text.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/innerText}
*/
export declare function innerText(node: AnyNode | AnyNode[]): string;
//# sourceMappingURL=stringify.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stringify.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["stringify.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,OAAO,EAEV,MAAM,YAAY,CAAC;AACpB,OAAmB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGlE;;;;;;GAMG;AACH,wBAAgB,YAAY,CACxB,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,EAClC,OAAO,CAAC,EAAE,oBAAoB,GAC/B,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CACxB,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,oBAAoB,GAC/B,MAAM,CAIR;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,MAAM,CAMzD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,MAAM,CAO7D;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,MAAM,CAO3D"}

View File

@@ -0,0 +1,81 @@
import { isTag, isCDATA, isText, hasChildren, isComment, } from "domhandler";
import renderHTML from "dom-serializer";
import { ElementType } from "domelementtype";
/**
* @category Stringify
* @deprecated Use the `dom-serializer` module directly.
* @param node Node to get the outer HTML of.
* @param options Options for serialization.
* @returns `node`'s outer HTML.
*/
export function getOuterHTML(node, options) {
return renderHTML(node, options);
}
/**
* @category Stringify
* @deprecated Use the `dom-serializer` module directly.
* @param node Node to get the inner HTML of.
* @param options Options for serialization.
* @returns `node`'s inner HTML.
*/
export function getInnerHTML(node, options) {
return hasChildren(node)
? node.children.map((node) => getOuterHTML(node, options)).join("")
: "";
}
/**
* Get a node's inner text. Same as `textContent`, but inserts newlines for `<br>` tags. Ignores comments.
*
* @category Stringify
* @deprecated Use `textContent` instead.
* @param node Node to get the inner text of.
* @returns `node`'s inner text.
*/
export function getText(node) {
if (Array.isArray(node))
return node.map(getText).join("");
if (isTag(node))
return node.name === "br" ? "\n" : getText(node.children);
if (isCDATA(node))
return getText(node.children);
if (isText(node))
return node.data;
return "";
}
/**
* Get a node's text content. Ignores comments.
*
* @category Stringify
* @param node Node to get the text content of.
* @returns `node`'s text content.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent}
*/
export function textContent(node) {
if (Array.isArray(node))
return node.map(textContent).join("");
if (hasChildren(node) && !isComment(node)) {
return textContent(node.children);
}
if (isText(node))
return node.data;
return "";
}
/**
* Get a node's inner text, ignoring `<script>` and `<style>` tags. Ignores comments.
*
* @category Stringify
* @param node Node to get the inner text of.
* @returns `node`'s inner text.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/innerText}
*/
export function innerText(node) {
if (Array.isArray(node))
return node.map(innerText).join("");
if (hasChildren(node) && (node.type === ElementType.Tag || isCDATA(node))) {
return innerText(node.children);
}
if (isText(node))
return node.data;
return "";
}
//# sourceMappingURL=stringify.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stringify.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["stringify.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,EACL,OAAO,EACP,MAAM,EACN,WAAW,EAEX,SAAS,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,UAAoC,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CACxB,IAAkC,EAClC,OAA8B;IAE9B,OAAO,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CACxB,IAAa,EACb,OAA8B;IAE9B,OAAO,WAAW,CAAC,IAAI,CAAC;QACpB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,CAAC,CAAC,EAAE,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,IAAyB;IAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3D,IAAI,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3E,IAAI,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnC,OAAO,EAAE,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,IAAyB;IACjD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/D,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,OAAO,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnC,OAAO,EAAE,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAyB;IAC/C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,WAAW,CAAC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnC,OAAO,EAAE,CAAC;AACd,CAAC"}

View File

@@ -0,0 +1,67 @@
import { AnyNode, ChildNode, Element, ParentNode } from "domhandler";
/**
* Get a node's children.
*
* @category Traversal
* @param elem Node to get the children of.
* @returns `elem`'s children, or an empty array.
*/
export declare function getChildren(elem: AnyNode): ChildNode[];
export declare function getParent(elem: AnyNode): ParentNode | null;
/**
* Gets an elements siblings, including the element itself.
*
* Attempts to get the children through the element's parent first. If we don't
* have a parent (the element is a root node), we walk the element's `prev` &
* `next` to get all remaining nodes.
*
* @category Traversal
* @param elem Element to get the siblings of.
* @returns `elem`'s siblings, including `elem`.
*/
export declare function getSiblings(elem: AnyNode): AnyNode[];
/**
* Gets an attribute from an element.
*
* @category Traversal
* @param elem Element to check.
* @param name Attribute name to retrieve.
* @returns The element's attribute value, or `undefined`.
*/
export declare function getAttributeValue(elem: Element, name: string): string | undefined;
/**
* Checks whether an element has an attribute.
*
* @category Traversal
* @param elem Element to check.
* @param name Attribute name to look for.
* @returns Returns whether `elem` has the attribute `name`.
*/
export declare function hasAttrib(elem: Element, name: string): boolean;
/**
* Get the tag name of an element.
*
* @category Traversal
* @param elem The element to get the name for.
* @returns The tag name of `elem`.
*/
export declare function getName(elem: Element): string;
/**
* Returns the next element sibling of a node.
*
* @category Traversal
* @param elem The element to get the next sibling of.
* @returns `elem`'s next sibling that is a tag, or `null` if there is no next
* sibling.
*/
export declare function nextElementSibling(elem: AnyNode): Element | null;
/**
* Returns the previous element sibling of a node.
*
* @category Traversal
* @param elem The element to get the previous sibling of.
* @returns `elem`'s previous sibling that is a tag, or `null` if there is no
* previous sibling.
*/
export declare function prevElementSibling(elem: AnyNode): Element | null;
//# sourceMappingURL=traversal.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"traversal.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["traversal.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,OAAO,EACP,SAAS,EACT,OAAO,EACP,UAAU,EAEb,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,CAEtD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;AAY5D;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,CAepD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC7B,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,MAAM,GACb,MAAM,GAAG,SAAS,CAEpB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAM9D;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAIhE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAIhE"}

View File

@@ -0,0 +1,112 @@
import { isTag, hasChildren, } from "domhandler";
/**
* Get a node's children.
*
* @category Traversal
* @param elem Node to get the children of.
* @returns `elem`'s children, or an empty array.
*/
export function getChildren(elem) {
return hasChildren(elem) ? elem.children : [];
}
/**
* Get a node's parent.
*
* @category Traversal
* @param elem Node to get the parent of.
* @returns `elem`'s parent node, or `null` if `elem` is a root node.
*/
export function getParent(elem) {
return elem.parent || null;
}
/**
* Gets an elements siblings, including the element itself.
*
* Attempts to get the children through the element's parent first. If we don't
* have a parent (the element is a root node), we walk the element's `prev` &
* `next` to get all remaining nodes.
*
* @category Traversal
* @param elem Element to get the siblings of.
* @returns `elem`'s siblings, including `elem`.
*/
export function getSiblings(elem) {
const parent = getParent(elem);
if (parent != null)
return getChildren(parent);
const siblings = [elem];
let { prev, next } = elem;
while (prev != null) {
siblings.unshift(prev);
({ prev } = prev);
}
while (next != null) {
siblings.push(next);
({ next } = next);
}
return siblings;
}
/**
* Gets an attribute from an element.
*
* @category Traversal
* @param elem Element to check.
* @param name Attribute name to retrieve.
* @returns The element's attribute value, or `undefined`.
*/
export function getAttributeValue(elem, name) {
var _a;
return (_a = elem.attribs) === null || _a === void 0 ? void 0 : _a[name];
}
/**
* Checks whether an element has an attribute.
*
* @category Traversal
* @param elem Element to check.
* @param name Attribute name to look for.
* @returns Returns whether `elem` has the attribute `name`.
*/
export function hasAttrib(elem, name) {
return (elem.attribs != null &&
Object.prototype.hasOwnProperty.call(elem.attribs, name) &&
elem.attribs[name] != null);
}
/**
* Get the tag name of an element.
*
* @category Traversal
* @param elem The element to get the name for.
* @returns The tag name of `elem`.
*/
export function getName(elem) {
return elem.name;
}
/**
* Returns the next element sibling of a node.
*
* @category Traversal
* @param elem The element to get the next sibling of.
* @returns `elem`'s next sibling that is a tag, or `null` if there is no next
* sibling.
*/
export function nextElementSibling(elem) {
let { next } = elem;
while (next !== null && !isTag(next))
({ next } = next);
return next;
}
/**
* Returns the previous element sibling of a node.
*
* @category Traversal
* @param elem The element to get the previous sibling of.
* @returns `elem`'s previous sibling that is a tag, or `null` if there is no
* previous sibling.
*/
export function prevElementSibling(elem) {
let { prev } = elem;
while (prev !== null && !isTag(prev))
({ prev } = prev);
return prev;
}
//# sourceMappingURL=traversal.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"traversal.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["traversal.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,EAKL,WAAW,GACd,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,IAAa;IACrC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,CAAC;AAGD;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAa;IACnC,OAAO,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,IAAa;IACrC,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,MAAM,IAAI,IAAI;QAAE,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,CAAC;IACxB,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IAC1B,OAAO,IAAI,IAAI,IAAI,EAAE,CAAC;QAClB,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,IAAI,IAAI,IAAI,EAAE,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC7B,IAAa,EACb,IAAY;;IAEZ,OAAO,MAAA,IAAI,CAAC,OAAO,0CAAG,IAAI,CAAC,CAAC;AAChC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAa,EAAE,IAAY;IACjD,OAAO,CACH,IAAI,CAAC,OAAO,IAAI,IAAI;QACpB,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;QACxD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAC7B,CAAC;AACN,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,IAAa;IACjC,OAAO,IAAI,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC5C,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IACpB,OAAO,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC5C,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IACpB,OAAO,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC;AAChB,CAAC"}

View File

@@ -0,0 +1,71 @@
import type { AnyNode } from "domhandler";
/**
* The medium of a media item.
*
* @category Feeds
*/
export type FeedItemMediaMedium = "image" | "audio" | "video" | "document" | "executable";
/**
* The type of a media item.
*
* @category Feeds
*/
export type FeedItemMediaExpression = "sample" | "full" | "nonstop";
/**
* A media item of a feed entry.
*
* @category Feeds
*/
export interface FeedItemMedia {
medium: FeedItemMediaMedium | undefined;
isDefault: boolean;
url?: string;
fileSize?: number;
type?: string;
expression?: FeedItemMediaExpression;
bitrate?: number;
framerate?: number;
samplingrate?: number;
channels?: number;
duration?: number;
height?: number;
width?: number;
lang?: string;
}
/**
* An entry of a feed.
*
* @category Feeds
*/
export interface FeedItem {
id?: string;
title?: string;
link?: string;
description?: string;
pubDate?: Date;
media: FeedItemMedia[];
}
/**
* The root of a feed.
*
* @category Feeds
*/
export interface Feed {
type: string;
id?: string;
title?: string;
link?: string;
description?: string;
updated?: Date;
author?: string;
items: FeedItem[];
}
/**
* Get the feed object from the root of a DOM tree.
*
* @category Feeds
* @param doc - The DOM to to extract the feed from.
* @returns The feed.
*/
export declare function getFeed(doc: AnyNode[]): Feed | null;
//# sourceMappingURL=feeds.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"feeds.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["feeds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAW,MAAM,YAAY,CAAC;AAInD;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GACzB,OAAO,GACP,OAAO,GACP,OAAO,GACP,UAAU,GACV,YAAY,CAAC;AAEnB;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAEpE;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,IAAI;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI,CAQnD"}

View File

@@ -0,0 +1,190 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFeed = getFeed;
var stringify_js_1 = require("./stringify.js");
var legacy_js_1 = require("./legacy.js");
/**
* Get the feed object from the root of a DOM tree.
*
* @category Feeds
* @param doc - The DOM to to extract the feed from.
* @returns The feed.
*/
function getFeed(doc) {
var feedRoot = getOneElement(isValidFeed, doc);
return !feedRoot
? null
: feedRoot.name === "feed"
? getAtomFeed(feedRoot)
: getRssFeed(feedRoot);
}
/**
* Parse an Atom feed.
*
* @param feedRoot The root of the feed.
* @returns The parsed feed.
*/
function getAtomFeed(feedRoot) {
var _a;
var childs = feedRoot.children;
var feed = {
type: "atom",
items: (0, legacy_js_1.getElementsByTagName)("entry", childs).map(function (item) {
var _a;
var children = item.children;
var entry = { media: getMediaElements(children) };
addConditionally(entry, "id", "id", children);
addConditionally(entry, "title", "title", children);
var href = (_a = getOneElement("link", children)) === null || _a === void 0 ? void 0 : _a.attribs["href"];
if (href) {
entry.link = href;
}
var description = fetch("summary", children) || fetch("content", children);
if (description) {
entry.description = description;
}
var pubDate = fetch("updated", children);
if (pubDate) {
entry.pubDate = new Date(pubDate);
}
return entry;
}),
};
addConditionally(feed, "id", "id", childs);
addConditionally(feed, "title", "title", childs);
var href = (_a = getOneElement("link", childs)) === null || _a === void 0 ? void 0 : _a.attribs["href"];
if (href) {
feed.link = href;
}
addConditionally(feed, "description", "subtitle", childs);
var updated = fetch("updated", childs);
if (updated) {
feed.updated = new Date(updated);
}
addConditionally(feed, "author", "email", childs, true);
return feed;
}
/**
* Parse a RSS feed.
*
* @param feedRoot The root of the feed.
* @returns The parsed feed.
*/
function getRssFeed(feedRoot) {
var _a, _b;
var childs = (_b = (_a = getOneElement("channel", feedRoot.children)) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : [];
var feed = {
type: feedRoot.name.substr(0, 3),
id: "",
items: (0, legacy_js_1.getElementsByTagName)("item", feedRoot.children).map(function (item) {
var children = item.children;
var entry = { media: getMediaElements(children) };
addConditionally(entry, "id", "guid", children);
addConditionally(entry, "title", "title", children);
addConditionally(entry, "link", "link", children);
addConditionally(entry, "description", "description", children);
var pubDate = fetch("pubDate", children) || fetch("dc:date", children);
if (pubDate)
entry.pubDate = new Date(pubDate);
return entry;
}),
};
addConditionally(feed, "title", "title", childs);
addConditionally(feed, "link", "link", childs);
addConditionally(feed, "description", "description", childs);
var updated = fetch("lastBuildDate", childs);
if (updated) {
feed.updated = new Date(updated);
}
addConditionally(feed, "author", "managingEditor", childs, true);
return feed;
}
var MEDIA_KEYS_STRING = ["url", "type", "lang"];
var MEDIA_KEYS_INT = [
"fileSize",
"bitrate",
"framerate",
"samplingrate",
"channels",
"duration",
"height",
"width",
];
/**
* Get all media elements of a feed item.
*
* @param where Nodes to search in.
* @returns Media elements.
*/
function getMediaElements(where) {
return (0, legacy_js_1.getElementsByTagName)("media:content", where).map(function (elem) {
var attribs = elem.attribs;
var media = {
medium: attribs["medium"],
isDefault: !!attribs["isDefault"],
};
for (var _i = 0, MEDIA_KEYS_STRING_1 = MEDIA_KEYS_STRING; _i < MEDIA_KEYS_STRING_1.length; _i++) {
var attrib = MEDIA_KEYS_STRING_1[_i];
if (attribs[attrib]) {
media[attrib] = attribs[attrib];
}
}
for (var _a = 0, MEDIA_KEYS_INT_1 = MEDIA_KEYS_INT; _a < MEDIA_KEYS_INT_1.length; _a++) {
var attrib = MEDIA_KEYS_INT_1[_a];
if (attribs[attrib]) {
media[attrib] = parseInt(attribs[attrib], 10);
}
}
if (attribs["expression"]) {
media.expression = attribs["expression"];
}
return media;
});
}
/**
* Get one element by tag name.
*
* @param tagName Tag name to look for
* @param node Node to search in
* @returns The element or null
*/
function getOneElement(tagName, node) {
return (0, legacy_js_1.getElementsByTagName)(tagName, node, true, 1)[0];
}
/**
* Get the text content of an element with a certain tag name.
*
* @param tagName Tag name to look for.
* @param where Node to search in.
* @param recurse Whether to recurse into child nodes.
* @returns The text content of the element.
*/
function fetch(tagName, where, recurse) {
if (recurse === void 0) { recurse = false; }
return (0, stringify_js_1.textContent)((0, legacy_js_1.getElementsByTagName)(tagName, where, recurse, 1)).trim();
}
/**
* Adds a property to an object if it has a value.
*
* @param obj Object to be extended
* @param prop Property name
* @param tagName Tag name that contains the conditionally added property
* @param where Element to search for the property
* @param recurse Whether to recurse into child nodes.
*/
function addConditionally(obj, prop, tagName, where, recurse) {
if (recurse === void 0) { recurse = false; }
var val = fetch(tagName, where, recurse);
if (val)
obj[prop] = val;
}
/**
* Checks if an element is a feed root node.
*
* @param value The name of the element to check.
* @returns Whether an element is a feed root node.
*/
function isValidFeed(value) {
return value === "rss" || value === "feed" || value === "rdf:RDF";
}
//# sourceMappingURL=feeds.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,59 @@
import { AnyNode } from "domhandler";
/**
* Given an array of nodes, remove any member that is contained by another
* member.
*
* @category Helpers
* @param nodes Nodes to filter.
* @returns Remaining nodes that aren't contained by other nodes.
*/
export declare function removeSubsets(nodes: AnyNode[]): AnyNode[];
/**
* @category Helpers
* @see {@link http://dom.spec.whatwg.org/#dom-node-comparedocumentposition}
*/
export declare const enum DocumentPosition {
DISCONNECTED = 1,
PRECEDING = 2,
FOLLOWING = 4,
CONTAINS = 8,
CONTAINED_BY = 16
}
/**
* Compare the position of one node against another node in any other document,
* returning a bitmask with the values from {@link DocumentPosition}.
*
* 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
*
* @category Helpers
* @param nodeA The first node to use in the comparison
* @param nodeB The second node to use in the comparison
* @returns A bitmask describing the input nodes' relative position.
*
* See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for
* a description of these values.
*/
export declare function compareDocumentPosition(nodeA: AnyNode, nodeB: AnyNode): number;
/**
* Sort an array of nodes based on their relative position in the document,
* removing any duplicate nodes. If the array contains nodes that do not belong
* to the same document, sort order is unspecified.
*
* @category Helpers
* @param nodes Array of DOM nodes.
* @returns Collection of unique nodes, sorted in document order.
*/
export declare function uniqueSort<T extends AnyNode>(nodes: T[]): T[];
//# sourceMappingURL=helpers.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"helpers.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,OAAO,EAAc,MAAM,YAAY,CAAC;AAE9D;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CA6BzD;AACD;;;GAGG;AACH,0BAAkB,gBAAgB;IAC9B,YAAY,IAAI;IAChB,SAAS,IAAI;IACb,SAAS,IAAI;IACb,QAAQ,IAAI;IACZ,YAAY,KAAK;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,uBAAuB,CACnC,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,OAAO,GACf,MAAM,CA4CR;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAc7D"}

View File

@@ -0,0 +1,142 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DocumentPosition = void 0;
exports.removeSubsets = removeSubsets;
exports.compareDocumentPosition = compareDocumentPosition;
exports.uniqueSort = uniqueSort;
var domhandler_1 = require("domhandler");
/**
* Given an array of nodes, remove any member that is contained by another
* member.
*
* @category Helpers
* @param nodes Nodes to filter.
* @returns Remaining nodes that aren't contained by other nodes.
*/
function removeSubsets(nodes) {
var idx = nodes.length;
/*
* Check if each node (or one of its ancestors) is already contained in the
* array.
*/
while (--idx >= 0) {
var node = nodes[idx];
/*
* Remove the node if it is not unique.
* We are going through the array from the end, so we only
* have to check nodes that preceed the node under consideration in the array.
*/
if (idx > 0 && nodes.lastIndexOf(node, idx - 1) >= 0) {
nodes.splice(idx, 1);
continue;
}
for (var ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
if (nodes.includes(ancestor)) {
nodes.splice(idx, 1);
break;
}
}
}
return nodes;
}
/**
* @category Helpers
* @see {@link http://dom.spec.whatwg.org/#dom-node-comparedocumentposition}
*/
var DocumentPosition;
(function (DocumentPosition) {
DocumentPosition[DocumentPosition["DISCONNECTED"] = 1] = "DISCONNECTED";
DocumentPosition[DocumentPosition["PRECEDING"] = 2] = "PRECEDING";
DocumentPosition[DocumentPosition["FOLLOWING"] = 4] = "FOLLOWING";
DocumentPosition[DocumentPosition["CONTAINS"] = 8] = "CONTAINS";
DocumentPosition[DocumentPosition["CONTAINED_BY"] = 16] = "CONTAINED_BY";
})(DocumentPosition || (exports.DocumentPosition = DocumentPosition = {}));
/**
* Compare the position of one node against another node in any other document,
* returning a bitmask with the values from {@link DocumentPosition}.
*
* 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
*
* @category Helpers
* @param nodeA The first node to use in the comparison
* @param nodeB The second node to use in the comparison
* @returns A bitmask describing the input nodes' relative position.
*
* See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for
* a description of these values.
*/
function compareDocumentPosition(nodeA, nodeB) {
var aParents = [];
var bParents = [];
if (nodeA === nodeB) {
return 0;
}
var current = (0, domhandler_1.hasChildren)(nodeA) ? nodeA : nodeA.parent;
while (current) {
aParents.unshift(current);
current = current.parent;
}
current = (0, domhandler_1.hasChildren)(nodeB) ? nodeB : nodeB.parent;
while (current) {
bParents.unshift(current);
current = current.parent;
}
var maxIdx = Math.min(aParents.length, bParents.length);
var idx = 0;
while (idx < maxIdx && aParents[idx] === bParents[idx]) {
idx++;
}
if (idx === 0) {
return DocumentPosition.DISCONNECTED;
}
var sharedParent = aParents[idx - 1];
var siblings = sharedParent.children;
var aSibling = aParents[idx];
var bSibling = bParents[idx];
if (siblings.indexOf(aSibling) > siblings.indexOf(bSibling)) {
if (sharedParent === nodeB) {
return DocumentPosition.FOLLOWING | DocumentPosition.CONTAINED_BY;
}
return DocumentPosition.FOLLOWING;
}
if (sharedParent === nodeA) {
return DocumentPosition.PRECEDING | DocumentPosition.CONTAINS;
}
return DocumentPosition.PRECEDING;
}
/**
* Sort an array of nodes based on their relative position in the document,
* removing any duplicate nodes. If the array contains nodes that do not belong
* to the same document, sort order is unspecified.
*
* @category Helpers
* @param nodes Array of DOM nodes.
* @returns Collection of unique nodes, sorted in document order.
*/
function uniqueSort(nodes) {
nodes = nodes.filter(function (node, i, arr) { return !arr.includes(node, i + 1); });
nodes.sort(function (a, b) {
var relative = compareDocumentPosition(a, b);
if (relative & DocumentPosition.PRECEDING) {
return -1;
}
else if (relative & DocumentPosition.FOLLOWING) {
return 1;
}
return 0;
});
return nodes;
}
//# sourceMappingURL=helpers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"helpers.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["helpers.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,10 @@
export * from "./stringify.js";
export * from "./traversal.js";
export * from "./manipulation.js";
export * from "./querying.js";
export * from "./legacy.js";
export * from "./helpers.js";
export * from "./feeds.js";
/** @deprecated Use these methods from `domhandler` directly. */
export { isTag, isCDATA, isText, isComment, isDocument, hasChildren, } from "domhandler";
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,gEAAgE;AAChE,OAAO,EACH,KAAK,EACL,OAAO,EACP,MAAM,EACN,SAAS,EACT,UAAU,EACV,WAAW,GACd,MAAM,YAAY,CAAC"}

View File

@@ -0,0 +1,33 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.hasChildren = exports.isDocument = exports.isComment = exports.isText = exports.isCDATA = exports.isTag = void 0;
__exportStar(require("./stringify.js"), exports);
__exportStar(require("./traversal.js"), exports);
__exportStar(require("./manipulation.js"), exports);
__exportStar(require("./querying.js"), exports);
__exportStar(require("./legacy.js"), exports);
__exportStar(require("./helpers.js"), exports);
__exportStar(require("./feeds.js"), exports);
/** @deprecated Use these methods from `domhandler` directly. */
var domhandler_1 = require("domhandler");
Object.defineProperty(exports, "isTag", { enumerable: true, get: function () { return domhandler_1.isTag; } });
Object.defineProperty(exports, "isCDATA", { enumerable: true, get: function () { return domhandler_1.isCDATA; } });
Object.defineProperty(exports, "isText", { enumerable: true, get: function () { return domhandler_1.isText; } });
Object.defineProperty(exports, "isComment", { enumerable: true, get: function () { return domhandler_1.isComment; } });
Object.defineProperty(exports, "isDocument", { enumerable: true, get: function () { return domhandler_1.isDocument; } });
Object.defineProperty(exports, "hasChildren", { enumerable: true, get: function () { return domhandler_1.hasChildren; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,iDAA+B;AAC/B,iDAA+B;AAC/B,oDAAkC;AAClC,gDAA8B;AAC9B,8CAA4B;AAC5B,+CAA6B;AAC7B,6CAA2B;AAC3B,gEAAgE;AAChE,yCAOoB;AANhB,mGAAA,KAAK,OAAA;AACL,qGAAA,OAAO,OAAA;AACP,oGAAA,MAAM,OAAA;AACN,uGAAA,SAAS,OAAA;AACT,wGAAA,UAAU,OAAA;AACV,yGAAA,WAAW,OAAA"}

View File

@@ -0,0 +1,79 @@
import { AnyNode, Element } from "domhandler";
import type { ElementType } from "domelementtype";
/**
* An object with keys to check elements against. If a key is `tag_name`,
* `tag_type` or `tag_contains`, it will check the value against that specific
* value. Otherwise, it will check an attribute with the key's name.
*
* @category Legacy Query Functions
*/
export interface TestElementOpts {
tag_name?: string | ((name: string) => boolean);
tag_type?: string | ((name: string) => boolean);
tag_contains?: string | ((data?: string) => boolean);
[attributeName: string]: undefined | string | ((attributeValue: string) => boolean);
}
/**
* Checks whether a node matches the description in `options`.
*
* @category Legacy Query Functions
* @param options An object describing nodes to look for.
* @param node The element to test.
* @returns Whether the element matches the description in `options`.
*/
export declare function testElement(options: TestElementOpts, node: AnyNode): boolean;
/**
* Returns all nodes that match `options`.
*
* @category Legacy Query Functions
* @param options An object describing nodes to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes that match `options`.
*/
export declare function getElements(options: TestElementOpts, nodes: AnyNode | AnyNode[], recurse: boolean, limit?: number): AnyNode[];
/**
* Returns the node with the supplied ID.
*
* @category Legacy Query Functions
* @param id The unique ID attribute value to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @returns The node with the supplied ID.
*/
export declare function getElementById(id: string | ((id: string) => boolean), nodes: AnyNode | AnyNode[], recurse?: boolean): Element | null;
/**
* Returns all nodes with the supplied `tagName`.
*
* @category Legacy Query Functions
* @param tagName Tag name to search for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `tagName`.
*/
export declare function getElementsByTagName(tagName: string | ((name: string) => boolean), nodes: AnyNode | AnyNode[], recurse?: boolean, limit?: number): Element[];
/**
* Returns all nodes with the supplied `className`.
*
* @category Legacy Query Functions
* @param className Class name to search for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `className`.
*/
export declare function getElementsByClassName(className: string | ((name: string) => boolean), nodes: AnyNode | AnyNode[], recurse?: boolean, limit?: number): Element[];
/**
* Returns all nodes with the supplied `type`.
*
* @category Legacy Query Functions
* @param type Element type to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `type`.
*/
export declare function getElementsByTagType(type: ElementType | ((type: ElementType) => boolean), nodes: AnyNode | AnyNode[], recurse?: boolean, limit?: number): AnyNode[];
//# sourceMappingURL=legacy.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"legacy.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["legacy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAKlD;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACrD,CAAC,aAAa,EAAE,MAAM,GAChB,SAAS,GACT,MAAM,GACN,CAAC,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;CAC/C;AAkFD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAG5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACvB,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,EAAE,OAAO,EAChB,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAGX;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC1B,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,EACtC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,UAAO,GACf,OAAO,GAAG,IAAI,CAGhB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAChC,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,EAC7C,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,UAAO,EACd,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAOX;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAClC,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,EAC/C,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,UAAO,EACd,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAOX;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAChC,IAAI,EAAE,WAAW,GAAG,CAAC,CAAC,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,EACpD,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,EAC1B,OAAO,UAAO,EACd,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAEX"}

View File

@@ -0,0 +1,168 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.testElement = testElement;
exports.getElements = getElements;
exports.getElementById = getElementById;
exports.getElementsByTagName = getElementsByTagName;
exports.getElementsByClassName = getElementsByClassName;
exports.getElementsByTagType = getElementsByTagType;
var domhandler_1 = require("domhandler");
var querying_js_1 = require("./querying.js");
/**
* A map of functions to check nodes against.
*/
var Checks = {
tag_name: function (name) {
if (typeof name === "function") {
return function (elem) { return (0, domhandler_1.isTag)(elem) && name(elem.name); };
}
else if (name === "*") {
return domhandler_1.isTag;
}
return function (elem) { return (0, domhandler_1.isTag)(elem) && elem.name === name; };
},
tag_type: function (type) {
if (typeof type === "function") {
return function (elem) { return type(elem.type); };
}
return function (elem) { return elem.type === type; };
},
tag_contains: function (data) {
if (typeof data === "function") {
return function (elem) { return (0, domhandler_1.isText)(elem) && data(elem.data); };
}
return function (elem) { return (0, domhandler_1.isText)(elem) && elem.data === data; };
},
};
/**
* Returns a function to check whether a node has an attribute with a particular
* value.
*
* @param attrib Attribute to check.
* @param value Attribute value to look for.
* @returns A function to check whether the a node has an attribute with a
* particular value.
*/
function getAttribCheck(attrib, value) {
if (typeof value === "function") {
return function (elem) { return (0, domhandler_1.isTag)(elem) && value(elem.attribs[attrib]); };
}
return function (elem) { return (0, domhandler_1.isTag)(elem) && elem.attribs[attrib] === value; };
}
/**
* Returns a function that returns `true` if either of the input functions
* returns `true` for a node.
*
* @param a First function to combine.
* @param b Second function to combine.
* @returns A function taking a node and returning `true` if either of the input
* functions returns `true` for the node.
*/
function combineFuncs(a, b) {
return function (elem) { return a(elem) || b(elem); };
}
/**
* Returns a function that executes all checks in `options` and returns `true`
* if any of them match a node.
*
* @param options An object describing nodes to look for.
* @returns A function that executes all checks in `options` and returns `true`
* if any of them match a node.
*/
function compileTest(options) {
var funcs = Object.keys(options).map(function (key) {
var value = options[key];
return Object.prototype.hasOwnProperty.call(Checks, key)
? Checks[key](value)
: getAttribCheck(key, value);
});
return funcs.length === 0 ? null : funcs.reduce(combineFuncs);
}
/**
* Checks whether a node matches the description in `options`.
*
* @category Legacy Query Functions
* @param options An object describing nodes to look for.
* @param node The element to test.
* @returns Whether the element matches the description in `options`.
*/
function testElement(options, node) {
var test = compileTest(options);
return test ? test(node) : true;
}
/**
* Returns all nodes that match `options`.
*
* @category Legacy Query Functions
* @param options An object describing nodes to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes that match `options`.
*/
function getElements(options, nodes, recurse, limit) {
if (limit === void 0) { limit = Infinity; }
var test = compileTest(options);
return test ? (0, querying_js_1.filter)(test, nodes, recurse, limit) : [];
}
/**
* Returns the node with the supplied ID.
*
* @category Legacy Query Functions
* @param id The unique ID attribute value to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @returns The node with the supplied ID.
*/
function getElementById(id, nodes, recurse) {
if (recurse === void 0) { recurse = true; }
if (!Array.isArray(nodes))
nodes = [nodes];
return (0, querying_js_1.findOne)(getAttribCheck("id", id), nodes, recurse);
}
/**
* Returns all nodes with the supplied `tagName`.
*
* @category Legacy Query Functions
* @param tagName Tag name to search for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `tagName`.
*/
function getElementsByTagName(tagName, nodes, recurse, limit) {
if (recurse === void 0) { recurse = true; }
if (limit === void 0) { limit = Infinity; }
return (0, querying_js_1.filter)(Checks["tag_name"](tagName), nodes, recurse, limit);
}
/**
* Returns all nodes with the supplied `className`.
*
* @category Legacy Query Functions
* @param className Class name to search for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `className`.
*/
function getElementsByClassName(className, nodes, recurse, limit) {
if (recurse === void 0) { recurse = true; }
if (limit === void 0) { limit = Infinity; }
return (0, querying_js_1.filter)(getAttribCheck("class", className), nodes, recurse, limit);
}
/**
* Returns all nodes with the supplied `type`.
*
* @category Legacy Query Functions
* @param type Element type to look for.
* @param nodes Nodes to search through.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes with the supplied `type`.
*/
function getElementsByTagType(type, nodes, recurse, limit) {
if (recurse === void 0) { recurse = true; }
if (limit === void 0) { limit = Infinity; }
return (0, querying_js_1.filter)(Checks["tag_type"](type), nodes, recurse, limit);
}
//# sourceMappingURL=legacy.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"legacy.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["legacy.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,49 @@
import type { ChildNode, ParentNode } from "domhandler";
/**
* Remove an element from the dom
*
* @category Manipulation
* @param elem The element to be removed
*/
export declare function removeElement(elem: ChildNode): void;
/**
* Replace an element in the dom
*
* @category Manipulation
* @param elem The element to be replaced
* @param replacement The element to be added
*/
export declare function replaceElement(elem: ChildNode, replacement: ChildNode): void;
/**
* Append a child to an element.
*
* @category Manipulation
* @param parent The element to append to.
* @param child The element to be added as a child.
*/
export declare function appendChild(parent: ParentNode, child: ChildNode): void;
/**
* Append an element after another.
*
* @category Manipulation
* @param elem The element to append after.
* @param next The element be added.
*/
export declare function append(elem: ChildNode, next: ChildNode): void;
/**
* Prepend a child to an element.
*
* @category Manipulation
* @param parent The element to prepend before.
* @param child The element to be added as a child.
*/
export declare function prependChild(parent: ParentNode, child: ChildNode): void;
/**
* Prepend an element before another.
*
* @category Manipulation
* @param elem The element to prepend before.
* @param prev The element be added.
*/
export declare function prepend(elem: ChildNode, prev: ChildNode): void;
//# sourceMappingURL=manipulation.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"manipulation.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["manipulation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAcnD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,GAAG,IAAI,CAiB5E;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAatE;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAoB7D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAavE;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAiB9D"}

View File

@@ -0,0 +1,142 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.removeElement = removeElement;
exports.replaceElement = replaceElement;
exports.appendChild = appendChild;
exports.append = append;
exports.prependChild = prependChild;
exports.prepend = prepend;
/**
* Remove an element from the dom
*
* @category Manipulation
* @param elem The element to be removed
*/
function removeElement(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;
var childsIndex = childs.lastIndexOf(elem);
if (childsIndex >= 0) {
childs.splice(childsIndex, 1);
}
}
elem.next = null;
elem.prev = null;
elem.parent = null;
}
/**
* Replace an element in the dom
*
* @category Manipulation
* @param elem The element to be replaced
* @param replacement The element to be added
*/
function replaceElement(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;
elem.parent = null;
}
}
/**
* Append a child to an element.
*
* @category Manipulation
* @param parent The element to append to.
* @param child The element to be added as a child.
*/
function appendChild(parent, child) {
removeElement(child);
child.next = null;
child.parent = parent;
if (parent.children.push(child) > 1) {
var sibling = parent.children[parent.children.length - 2];
sibling.next = child;
child.prev = sibling;
}
else {
child.prev = null;
}
}
/**
* Append an element after another.
*
* @category Manipulation
* @param elem The element to append after.
* @param next The element be added.
*/
function append(elem, next) {
removeElement(next);
var parent = elem.parent;
var 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);
}
}
/**
* Prepend a child to an element.
*
* @category Manipulation
* @param parent The element to prepend before.
* @param child The element to be added as a child.
*/
function prependChild(parent, child) {
removeElement(child);
child.parent = parent;
child.prev = null;
if (parent.children.unshift(child) !== 1) {
var sibling = parent.children[1];
sibling.prev = child;
child.next = sibling;
}
else {
child.next = null;
}
}
/**
* Prepend an element before another.
*
* @category Manipulation
* @param elem The element to prepend before.
* @param prev The element be added.
*/
function prepend(elem, prev) {
removeElement(prev);
var parent = elem.parent;
if (parent) {
var childs = parent.children;
childs.splice(childs.indexOf(elem), 0, prev);
}
if (elem.prev) {
elem.prev.next = prev;
}
prev.parent = parent;
prev.prev = elem.prev;
prev.next = elem;
elem.prev = prev;
}
//# sourceMappingURL=manipulation.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"manipulation.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["manipulation.ts"],"names":[],"mappings":";;AAQA,sCAcC;AASD,wCAiBC;AASD,kCAaC;AASD,wBAoBC;AASD,oCAaC;AASD,0BAiBC;AAjJD;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,IAAe;IACzC,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IAC1C,IAAI,IAAI,CAAC,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IAE1C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QACpC,IAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;YACnB,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAClC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;AACvB,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,IAAe,EAAE,WAAsB;IAClE,IAAM,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,IAAI,EAAE,CAAC;QACP,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC5B,CAAC;IAED,IAAM,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,IAAI,EAAE,CAAC;QACP,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC5B,CAAC;IAED,IAAM,MAAM,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAClD,IAAI,MAAM,EAAE,CAAC;QACT,IAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC;QAC/C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACvB,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,WAAW,CAAC,MAAkB,EAAE,KAAgB;IAC5D,aAAa,CAAC,KAAK,CAAC,CAAC;IAErB,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IAEtB,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,IAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC5D,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC;QACrB,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC;IACzB,CAAC;SAAM,CAAC;QACJ,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;IACtB,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,MAAM,CAAC,IAAe,EAAE,IAAe;IACnD,aAAa,CAAC,IAAI,CAAC,CAAC;IAEZ,IAAA,MAAM,GAAK,IAAI,OAAT,CAAU;IACxB,IAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;IAE3B,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC;IACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IAErB,IAAI,QAAQ,EAAE,CAAC;QACX,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,IAAI,MAAM,EAAE,CAAC;YACT,IAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QACzD,CAAC;IACL,CAAC;SAAM,IAAI,MAAM,EAAE,CAAC;QAChB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,YAAY,CAAC,MAAkB,EAAE,KAAgB;IAC7D,aAAa,CAAC,KAAK,CAAC,CAAC;IAErB,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;IAElB,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,IAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC;QACrB,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC;IACzB,CAAC;SAAM,CAAC;QACJ,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;IACtB,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,OAAO,CAAC,IAAe,EAAE,IAAe;IACpD,aAAa,CAAC,IAAI,CAAC,CAAC;IAEZ,IAAA,MAAM,GAAK,IAAI,OAAT,CAAU;IACxB,IAAI,MAAM,EAAE,CAAC;QACT,IAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AACrB,CAAC"}

View File

@@ -0,0 +1,64 @@
import { Element, AnyNode, ParentNode } from "domhandler";
/**
* Search a node and its children for nodes passing a test function. If `node` is not an array, it will be wrapped in one.
*
* @category Querying
* @param test Function to test nodes on.
* @param node Node to search. Will be included in the result set if it matches.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes passing `test`.
*/
export declare function filter(test: (elem: AnyNode) => boolean, node: AnyNode | AnyNode[], recurse?: boolean, limit?: number): AnyNode[];
/**
* Search an array of nodes and their children for nodes passing a test function.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes passing `test`.
*/
export declare function find(test: (elem: AnyNode) => boolean, nodes: AnyNode[] | ParentNode, recurse: boolean, limit: number): AnyNode[];
/**
* Finds the first element inside of an array that matches a test function. This is an alias for `Array.prototype.find`.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns The first node in the array that passes `test`.
* @deprecated Use `Array.prototype.find` directly.
*/
export declare function findOneChild<T>(test: (elem: T) => boolean, nodes: T[]): T | undefined;
/**
* Finds one element in a tree that passes a test.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Node or array of nodes to search.
* @param recurse Also consider child nodes.
* @returns The first node that passes `test`.
*/
export declare function findOne(test: (elem: Element) => boolean, nodes: AnyNode[] | ParentNode, recurse?: boolean): Element | null;
/**
* Checks if a tree of nodes contains at least one node passing a test.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns Whether a tree of nodes contains at least one node passing the test.
*/
export declare function existsOne(test: (elem: Element) => boolean, nodes: AnyNode[] | ParentNode): boolean;
/**
* Search an array of nodes and their children for elements passing a test function.
*
* Same as `find`, but limited to elements and with less options, leading to reduced complexity.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns All nodes passing `test`.
*/
export declare function findAll(test: (elem: Element) => boolean, nodes: AnyNode[] | ParentNode): Element[];
//# sourceMappingURL=querying.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"querying.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["querying.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE9E;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAClB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,EACzB,OAAO,UAAO,EACd,KAAK,GAAE,MAAiB,GACzB,OAAO,EAAE,CAEX;AAED;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAChB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,EAC7B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GACd,OAAO,EAAE,CAuCX;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC1B,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,EAC1B,KAAK,EAAE,CAAC,EAAE,GACX,CAAC,GAAG,SAAS,CAEf;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CACnB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,EAC7B,OAAO,UAAO,GACf,OAAO,GAAG,IAAI,CAchB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACrB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,GAC9B,OAAO,CAMT;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CACnB,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,EAChC,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,GAC9B,OAAO,EAAE,CA4BX"}

View File

@@ -0,0 +1,155 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.filter = filter;
exports.find = find;
exports.findOneChild = findOneChild;
exports.findOne = findOne;
exports.existsOne = existsOne;
exports.findAll = findAll;
var domhandler_1 = require("domhandler");
/**
* Search a node and its children for nodes passing a test function. If `node` is not an array, it will be wrapped in one.
*
* @category Querying
* @param test Function to test nodes on.
* @param node Node to search. Will be included in the result set if it matches.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes passing `test`.
*/
function filter(test, node, recurse, limit) {
if (recurse === void 0) { recurse = true; }
if (limit === void 0) { limit = Infinity; }
return find(test, Array.isArray(node) ? node : [node], recurse, limit);
}
/**
* Search an array of nodes and their children for nodes passing a test function.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @param recurse Also consider child nodes.
* @param limit Maximum number of nodes to return.
* @returns All nodes passing `test`.
*/
function find(test, nodes, recurse, limit) {
var result = [];
/** Stack of the arrays we are looking at. */
var nodeStack = [Array.isArray(nodes) ? nodes : [nodes]];
/** Stack of the indices within the arrays. */
var indexStack = [0];
for (;;) {
// First, check if the current array has any more elements to look at.
if (indexStack[0] >= nodeStack[0].length) {
// If we have no more arrays to look at, we are done.
if (indexStack.length === 1) {
return result;
}
// Otherwise, remove the current array from the stack.
nodeStack.shift();
indexStack.shift();
// Loop back to the start to continue with the next array.
continue;
}
var elem = nodeStack[0][indexStack[0]++];
if (test(elem)) {
result.push(elem);
if (--limit <= 0)
return result;
}
if (recurse && (0, domhandler_1.hasChildren)(elem) && elem.children.length > 0) {
/*
* Add the children to the stack. We are depth-first, so this is
* the next array we look at.
*/
indexStack.unshift(0);
nodeStack.unshift(elem.children);
}
}
}
/**
* Finds the first element inside of an array that matches a test function. This is an alias for `Array.prototype.find`.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns The first node in the array that passes `test`.
* @deprecated Use `Array.prototype.find` directly.
*/
function findOneChild(test, nodes) {
return nodes.find(test);
}
/**
* Finds one element in a tree that passes a test.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Node or array of nodes to search.
* @param recurse Also consider child nodes.
* @returns The first node that passes `test`.
*/
function findOne(test, nodes, recurse) {
if (recurse === void 0) { recurse = true; }
var searchedNodes = Array.isArray(nodes) ? nodes : [nodes];
for (var i = 0; i < searchedNodes.length; i++) {
var node = searchedNodes[i];
if ((0, domhandler_1.isTag)(node) && test(node)) {
return node;
}
if (recurse && (0, domhandler_1.hasChildren)(node) && node.children.length > 0) {
var found = findOne(test, node.children, true);
if (found)
return found;
}
}
return null;
}
/**
* Checks if a tree of nodes contains at least one node passing a test.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns Whether a tree of nodes contains at least one node passing the test.
*/
function existsOne(test, nodes) {
return (Array.isArray(nodes) ? nodes : [nodes]).some(function (node) {
return ((0, domhandler_1.isTag)(node) && test(node)) ||
((0, domhandler_1.hasChildren)(node) && existsOne(test, node.children));
});
}
/**
* Search an array of nodes and their children for elements passing a test function.
*
* Same as `find`, but limited to elements and with less options, leading to reduced complexity.
*
* @category Querying
* @param test Function to test nodes on.
* @param nodes Array of nodes to search.
* @returns All nodes passing `test`.
*/
function findAll(test, nodes) {
var result = [];
var nodeStack = [Array.isArray(nodes) ? nodes : [nodes]];
var indexStack = [0];
for (;;) {
if (indexStack[0] >= nodeStack[0].length) {
if (nodeStack.length === 1) {
return result;
}
// Otherwise, remove the current array from the stack.
nodeStack.shift();
indexStack.shift();
// Loop back to the start to continue with the next array.
continue;
}
var elem = nodeStack[0][indexStack[0]++];
if ((0, domhandler_1.isTag)(elem) && test(elem))
result.push(elem);
if ((0, domhandler_1.hasChildren)(elem) && elem.children.length > 0) {
indexStack.unshift(0);
nodeStack.unshift(elem.children);
}
}
}
//# sourceMappingURL=querying.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"querying.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["querying.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,46 @@
import { AnyNode } from "domhandler";
import { DomSerializerOptions } from "dom-serializer";
/**
* @category Stringify
* @deprecated Use the `dom-serializer` module directly.
* @param node Node to get the outer HTML of.
* @param options Options for serialization.
* @returns `node`'s outer HTML.
*/
export declare function getOuterHTML(node: AnyNode | ArrayLike<AnyNode>, options?: DomSerializerOptions): string;
/**
* @category Stringify
* @deprecated Use the `dom-serializer` module directly.
* @param node Node to get the inner HTML of.
* @param options Options for serialization.
* @returns `node`'s inner HTML.
*/
export declare function getInnerHTML(node: AnyNode, options?: DomSerializerOptions): string;
/**
* Get a node's inner text. Same as `textContent`, but inserts newlines for `<br>` tags. Ignores comments.
*
* @category Stringify
* @deprecated Use `textContent` instead.
* @param node Node to get the inner text of.
* @returns `node`'s inner text.
*/
export declare function getText(node: AnyNode | AnyNode[]): string;
/**
* Get a node's text content. Ignores comments.
*
* @category Stringify
* @param node Node to get the text content of.
* @returns `node`'s text content.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent}
*/
export declare function textContent(node: AnyNode | AnyNode[]): string;
/**
* Get a node's inner text, ignoring `<script>` and `<style>` tags. Ignores comments.
*
* @category Stringify
* @param node Node to get the inner text of.
* @returns `node`'s inner text.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/innerText}
*/
export declare function innerText(node: AnyNode | AnyNode[]): string;
//# sourceMappingURL=stringify.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stringify.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["stringify.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,OAAO,EAEV,MAAM,YAAY,CAAC;AACpB,OAAmB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGlE;;;;;;GAMG;AACH,wBAAgB,YAAY,CACxB,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,EAClC,OAAO,CAAC,EAAE,oBAAoB,GAC/B,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CACxB,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,oBAAoB,GAC/B,MAAM,CAIR;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,MAAM,CAMzD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,MAAM,CAO7D;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,MAAM,CAO3D"}

View File

@@ -0,0 +1,91 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOuterHTML = getOuterHTML;
exports.getInnerHTML = getInnerHTML;
exports.getText = getText;
exports.textContent = textContent;
exports.innerText = innerText;
var domhandler_1 = require("domhandler");
var dom_serializer_1 = __importDefault(require("dom-serializer"));
var domelementtype_1 = require("domelementtype");
/**
* @category Stringify
* @deprecated Use the `dom-serializer` module directly.
* @param node Node to get the outer HTML of.
* @param options Options for serialization.
* @returns `node`'s outer HTML.
*/
function getOuterHTML(node, options) {
return (0, dom_serializer_1.default)(node, options);
}
/**
* @category Stringify
* @deprecated Use the `dom-serializer` module directly.
* @param node Node to get the inner HTML of.
* @param options Options for serialization.
* @returns `node`'s inner HTML.
*/
function getInnerHTML(node, options) {
return (0, domhandler_1.hasChildren)(node)
? node.children.map(function (node) { return getOuterHTML(node, options); }).join("")
: "";
}
/**
* Get a node's inner text. Same as `textContent`, but inserts newlines for `<br>` tags. Ignores comments.
*
* @category Stringify
* @deprecated Use `textContent` instead.
* @param node Node to get the inner text of.
* @returns `node`'s inner text.
*/
function getText(node) {
if (Array.isArray(node))
return node.map(getText).join("");
if ((0, domhandler_1.isTag)(node))
return node.name === "br" ? "\n" : getText(node.children);
if ((0, domhandler_1.isCDATA)(node))
return getText(node.children);
if ((0, domhandler_1.isText)(node))
return node.data;
return "";
}
/**
* Get a node's text content. Ignores comments.
*
* @category Stringify
* @param node Node to get the text content of.
* @returns `node`'s text content.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent}
*/
function textContent(node) {
if (Array.isArray(node))
return node.map(textContent).join("");
if ((0, domhandler_1.hasChildren)(node) && !(0, domhandler_1.isComment)(node)) {
return textContent(node.children);
}
if ((0, domhandler_1.isText)(node))
return node.data;
return "";
}
/**
* Get a node's inner text, ignoring `<script>` and `<style>` tags. Ignores comments.
*
* @category Stringify
* @param node Node to get the inner text of.
* @returns `node`'s inner text.
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Node/innerText}
*/
function innerText(node) {
if (Array.isArray(node))
return node.map(innerText).join("");
if ((0, domhandler_1.hasChildren)(node) && (node.type === domelementtype_1.ElementType.Tag || (0, domhandler_1.isCDATA)(node))) {
return innerText(node.children);
}
if ((0, domhandler_1.isText)(node))
return node.data;
return "";
}
//# sourceMappingURL=stringify.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"stringify.js","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["stringify.ts"],"names":[],"mappings":";;;;;AAkBA,oCAKC;AASD,oCAOC;AAUD,0BAMC;AAUD,kCAOC;AAUD,8BAOC;AAzFD,yCAOoB;AACpB,kEAAkE;AAClE,iDAA6C;AAE7C;;;;;;GAMG;AACH,SAAgB,YAAY,CACxB,IAAkC,EAClC,OAA8B;IAE9B,OAAO,IAAA,wBAAU,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,YAAY,CACxB,IAAa,EACb,OAA8B;IAE9B,OAAO,IAAA,wBAAW,EAAC,IAAI,CAAC;QACpB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAC,IAAI,IAAK,OAAA,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EAA3B,CAA2B,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACnE,CAAC,CAAC,EAAE,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,OAAO,CAAC,IAAyB;IAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3D,IAAI,IAAA,kBAAK,EAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3E,IAAI,IAAA,oBAAO,EAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,IAAA,mBAAM,EAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnC,OAAO,EAAE,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,WAAW,CAAC,IAAyB;IACjD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/D,IAAI,IAAA,wBAAW,EAAC,IAAI,CAAC,IAAI,CAAC,IAAA,sBAAS,EAAC,IAAI,CAAC,EAAE,CAAC;QACxC,OAAO,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,IAAA,mBAAM,EAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnC,OAAO,EAAE,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,SAAS,CAAC,IAAyB;IAC/C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,IAAI,IAAA,wBAAW,EAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,4BAAW,CAAC,GAAG,IAAI,IAAA,oBAAO,EAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,IAAA,mBAAM,EAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACnC,OAAO,EAAE,CAAC;AACd,CAAC"}

View File

@@ -0,0 +1,67 @@
import { AnyNode, ChildNode, Element, ParentNode } from "domhandler";
/**
* Get a node's children.
*
* @category Traversal
* @param elem Node to get the children of.
* @returns `elem`'s children, or an empty array.
*/
export declare function getChildren(elem: AnyNode): ChildNode[];
export declare function getParent(elem: AnyNode): ParentNode | null;
/**
* Gets an elements siblings, including the element itself.
*
* Attempts to get the children through the element's parent first. If we don't
* have a parent (the element is a root node), we walk the element's `prev` &
* `next` to get all remaining nodes.
*
* @category Traversal
* @param elem Element to get the siblings of.
* @returns `elem`'s siblings, including `elem`.
*/
export declare function getSiblings(elem: AnyNode): AnyNode[];
/**
* Gets an attribute from an element.
*
* @category Traversal
* @param elem Element to check.
* @param name Attribute name to retrieve.
* @returns The element's attribute value, or `undefined`.
*/
export declare function getAttributeValue(elem: Element, name: string): string | undefined;
/**
* Checks whether an element has an attribute.
*
* @category Traversal
* @param elem Element to check.
* @param name Attribute name to look for.
* @returns Returns whether `elem` has the attribute `name`.
*/
export declare function hasAttrib(elem: Element, name: string): boolean;
/**
* Get the tag name of an element.
*
* @category Traversal
* @param elem The element to get the name for.
* @returns The tag name of `elem`.
*/
export declare function getName(elem: Element): string;
/**
* Returns the next element sibling of a node.
*
* @category Traversal
* @param elem The element to get the next sibling of.
* @returns `elem`'s next sibling that is a tag, or `null` if there is no next
* sibling.
*/
export declare function nextElementSibling(elem: AnyNode): Element | null;
/**
* Returns the previous element sibling of a node.
*
* @category Traversal
* @param elem The element to get the previous sibling of.
* @returns `elem`'s previous sibling that is a tag, or `null` if there is no
* previous sibling.
*/
export declare function prevElementSibling(elem: AnyNode): Element | null;
//# sourceMappingURL=traversal.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"traversal.d.ts","sourceRoot":"https://raw.githubusercontent.com/fb55/domutils/0ab8bcf1ecfc70dfc93291a4cb2496578ac25e9c/src/","sources":["traversal.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,OAAO,EACP,SAAS,EACT,OAAO,EACP,UAAU,EAEb,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,EAAE,CAEtD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;AAY5D;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,CAepD;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC7B,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,MAAM,GACb,MAAM,GAAG,SAAS,CAEpB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAM9D;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE7C;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAIhE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAIhE"}

View File

@@ -0,0 +1,125 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getChildren = getChildren;
exports.getParent = getParent;
exports.getSiblings = getSiblings;
exports.getAttributeValue = getAttributeValue;
exports.hasAttrib = hasAttrib;
exports.getName = getName;
exports.nextElementSibling = nextElementSibling;
exports.prevElementSibling = prevElementSibling;
var domhandler_1 = require("domhandler");
/**
* Get a node's children.
*
* @category Traversal
* @param elem Node to get the children of.
* @returns `elem`'s children, or an empty array.
*/
function getChildren(elem) {
return (0, domhandler_1.hasChildren)(elem) ? elem.children : [];
}
/**
* Get a node's parent.
*
* @category Traversal
* @param elem Node to get the parent of.
* @returns `elem`'s parent node, or `null` if `elem` is a root node.
*/
function getParent(elem) {
return elem.parent || null;
}
/**
* Gets an elements siblings, including the element itself.
*
* Attempts to get the children through the element's parent first. If we don't
* have a parent (the element is a root node), we walk the element's `prev` &
* `next` to get all remaining nodes.
*
* @category Traversal
* @param elem Element to get the siblings of.
* @returns `elem`'s siblings, including `elem`.
*/
function getSiblings(elem) {
var _a, _b;
var parent = getParent(elem);
if (parent != null)
return getChildren(parent);
var siblings = [elem];
var prev = elem.prev, next = elem.next;
while (prev != null) {
siblings.unshift(prev);
(_a = prev, prev = _a.prev);
}
while (next != null) {
siblings.push(next);
(_b = next, next = _b.next);
}
return siblings;
}
/**
* Gets an attribute from an element.
*
* @category Traversal
* @param elem Element to check.
* @param name Attribute name to retrieve.
* @returns The element's attribute value, or `undefined`.
*/
function getAttributeValue(elem, name) {
var _a;
return (_a = elem.attribs) === null || _a === void 0 ? void 0 : _a[name];
}
/**
* Checks whether an element has an attribute.
*
* @category Traversal
* @param elem Element to check.
* @param name Attribute name to look for.
* @returns Returns whether `elem` has the attribute `name`.
*/
function hasAttrib(elem, name) {
return (elem.attribs != null &&
Object.prototype.hasOwnProperty.call(elem.attribs, name) &&
elem.attribs[name] != null);
}
/**
* Get the tag name of an element.
*
* @category Traversal
* @param elem The element to get the name for.
* @returns The tag name of `elem`.
*/
function getName(elem) {
return elem.name;
}
/**
* Returns the next element sibling of a node.
*
* @category Traversal
* @param elem The element to get the next sibling of.
* @returns `elem`'s next sibling that is a tag, or `null` if there is no next
* sibling.
*/
function nextElementSibling(elem) {
var _a;
var next = elem.next;
while (next !== null && !(0, domhandler_1.isTag)(next))
(_a = next, next = _a.next);
return next;
}
/**
* Returns the previous element sibling of a node.
*
* @category Traversal
* @param elem The element to get the previous sibling of.
* @returns `elem`'s previous sibling that is a tag, or `null` if there is no
* previous sibling.
*/
function prevElementSibling(elem) {
var _a;
var prev = elem.prev;
while (prev !== null && !(0, domhandler_1.isTag)(prev))
(_a = prev, prev = _a.prev);
return prev;
}
//# sourceMappingURL=traversal.js.map

Some files were not shown because too many files have changed in this diff Show More