first commit

This commit is contained in:
2026-09-08 20:28:54 +08:00
commit 2ada8d3d5a
37380 changed files with 4886169 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
Copyright 2010, 2011, Chris Winberry <chris@winberry.net>. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
+182
View File
@@ -0,0 +1,182 @@
# stricter-htmlparser2
[![NPM version](http://img.shields.io/npm/v/stricter-htmlparser2.svg?style=flat)](https://npmjs.org/package/stricter-htmlparser2)
[![Downloads](https://img.shields.io/npm/dm/stricter-htmlparser2.svg?style=flat)](https://npmjs.org/package/stricter-htmlparser2)
[![Build Status](http://img.shields.io/travis/yanghuabei/stricter-htmlparser2/master.svg?style=flat)](http://travis-ci.org/yanghuabei/htmlparser2)
[![Coverage](http://img.shields.io/coveralls/yanghuabei/stricter-htmlparser2.svg?style=flat)](https://coveralls.io/r/yanghuabei/htmlparser2)
A forgiving HTML/XML/RSS parser. The parser can handle streams and provides a callback interface.
## Installation
npm install 'stricter-htmlparser2'
A live demo of htmlparser2 is available [here](https://astexplorer.net/#/2AmVrGuGVJ).
## Differences to htmlparser2
1. Attribute value without quote wrapped is not allowed.
```
<foo name=value />
// output
{
attribs: {
name: "",
value: ""
}
}
```
2. "\"" is allowed in attribute value.
```
<foo name="hello \"world" />
// output
{
attribs: {
name: "hello \\"world"
}
}
```
3. Record attributes key in a object that value is wrapped by single quote. The object is passed to `onopentag` method of parser's domhandler through the third argument.
```
<foo name='{{flag ? "true" : "false"}}' title="title" class='hidden'> </foo>
// The third argument passed to onopentag:
{
name: true,
class: true
}
```
// If used with x-domhandler which is exported by this module, the parse result is like this:
```javascript
{
type: "tag",
name: "foo",
attribs: {
name: "{{flag ? \"true\" : \"false\"}}",
title: "title",
class: "hidden"
},
singleQuoteAttribs: {
name: true,
class: true
},
selfClose: false
}
```
4. Add `selfClose` flag to parsed node element.
**Input:**
```html
<foo name='{{flag ? "true" : "false"}}' title="title" class='hidden' />
```
**Parser code:**
```javascript
const {Parser, DomHandler} = require('stricter-htmlparser2');
const parser = new Parser(new DomHandler(), {}).end(inputStr);
```
**Output**
```javascript
{
type: "tag",
name: "foo",
attribs: {
name: "{{flag ? \"true\" : \"false\"}}",
title: "title",
class: "hidden"
},
singleQuoteAttribs: {
name: true,
class: true
},
selfClose: true
}
```
## Usage
```javascript
var htmlparser = require("stricter-htmlparser2");
var parser = new htmlparser.Parser({
onopentag: function(name, attribs){
if(name === "script" && attribs.type === "text/javascript"){
console.log("JS! Hooray!");
}
},
ontext: function(text){
console.log("-->", text);
},
onclosetag: function(tagname){
if(tagname === "script"){
console.log("That's it?!");
}
}
}, {decodeEntities: true});
parser.write("Xyz <script type='text/javascript'>var foo = '<<bar>>';</ script>");
parser.end();
```
Output (simplified):
```
--> Xyz
JS! Hooray!
--> var foo = '<<bar>>';
That's it?!
```
## Documentation
Read more about the parser and its options in the [wiki](https://github.com/fb55/htmlparser2/wiki/Parser-options).
## Get a DOM
The `DomHandler` (known as `DefaultHandler` in the original `htmlparser` module) produces a DOM (document object model) that can be manipulated using the [`DomUtils`](https://github.com/fb55/DomUtils) helper.
The `DomHandler`, while still bundled with this module, was moved to its [own module](https://github.com/fb55/domhandler). Have a look at it for further information.
## Parsing RSS/RDF/Atom Feeds
```javascript
new htmlparser.FeedHandler(function(<error> error, <object> feed){
...
});
```
Note: While the provided feed handler works for most feeds, you might want to use [danmactough/node-feedparser](https://github.com/danmactough/node-feedparser), which is much better tested and actively maintained.
## Performance
After having some artificial benchmarks for some time, __@AndreasMadsen__ published his [`htmlparser-benchmark`](https://github.com/AndreasMadsen/htmlparser-benchmark), which benchmarks HTML parses based on real-world websites.
At the time of writing, the latest versions of all supported parsers show the following performance characteristics on [Travis CI](https://travis-ci.org/AndreasMadsen/htmlparser-benchmark/builds/10805007) (please note that Travis doesn't guarantee equal conditions for all tests):
```
gumbo-parser : 34.9208 ms/file ± 21.4238
html-parser : 24.8224 ms/file ± 15.8703
html5 : 419.597 ms/file ± 264.265
htmlparser : 60.0722 ms/file ± 384.844
htmlparser2-dom: 12.0749 ms/file ± 6.49474
htmlparser2 : 7.49130 ms/file ± 5.74368
hubbub : 30.4980 ms/file ± 16.4682
libxmljs : 14.1338 ms/file ± 18.6541
parse5 : 22.0439 ms/file ± 15.3743
sax : 49.6513 ms/file ± 26.6032
```
## How does this module differ from [node-htmlparser](https://github.com/tautologistics/node-htmlparser)?
This is a fork of the `htmlparser` module. The main difference is that this is intended to be used only with node (it runs on other platforms using [browserify](https://github.com/substack/node-browserify)). `htmlparser2` was rewritten multiple times and, while it maintains an API that's compatible with `htmlparser` in most cases, the projects don't share any code anymore.
The parser now provides a callback interface close to [sax.js](https://github.com/isaacs/sax-js) (originally targeted at [readabilitySAX](https://github.com/fb55/readabilitysax)). As a result, old handlers won't work anymore.
The `DefaultHandler` and the `RssHandler` were renamed to clarify their purpose (to `DomHandler` and `FeedHandler`). The old names are still available when requiring `htmlparser2`, your code should work as expected.
+55
View File
@@ -0,0 +1,55 @@
module.exports = CollectingHandler;
function CollectingHandler(cbs){
this._cbs = cbs || {};
this.events = [];
}
var EVENTS = require("./").EVENTS;
Object.keys(EVENTS).forEach(function(name){
if(EVENTS[name] === 0){
name = "on" + name;
CollectingHandler.prototype[name] = function(){
this.events.push([name]);
if(this._cbs[name]) this._cbs[name]();
};
} else if(EVENTS[name] === 1){
name = "on" + name;
CollectingHandler.prototype[name] = function(a){
this.events.push([name, a]);
if(this._cbs[name]) this._cbs[name](a);
};
} else if(EVENTS[name] === 2){
name = "on" + name;
CollectingHandler.prototype[name] = function(a, b){
this.events.push([name, a, b]);
if(this._cbs[name]) this._cbs[name](a, b);
};
} else {
throw Error("wrong number of arguments");
}
});
CollectingHandler.prototype.onreset = function(){
this.events = [];
if(this._cbs.onreset) this._cbs.onreset();
};
CollectingHandler.prototype.restart = function(){
if(this._cbs.onreset) this._cbs.onreset();
for(var i = 0, len = this.events.length; i < len; i++){
if(this._cbs[this.events[i][0]]){
var num = this.events[i].length;
if(num === 1){
this._cbs[this.events[i][0]]();
} else if(num === 2){
this._cbs[this.events[i][0]](this.events[i][1]);
} else {
this._cbs[this.events[i][0]](this.events[i][1], this.events[i][2]);
}
}
}
};
+95
View File
@@ -0,0 +1,95 @@
var index = require("./index.js");
var DomHandler = index.DomHandler;
var DomUtils = index.DomUtils;
//TODO: make this a streamable handler
function FeedHandler(callback, options){
this.init(callback, options);
}
require("inherits")(FeedHandler, DomHandler);
FeedHandler.prototype.init = DomHandler;
function getElements(what, where){
return DomUtils.getElementsByTagName(what, where, true);
}
function getOneElement(what, where){
return DomUtils.getElementsByTagName(what, where, true, 1)[0];
}
function fetch(what, where, recurse){
return DomUtils.getText(
DomUtils.getElementsByTagName(what, where, recurse, 1)
).trim();
}
function addConditionally(obj, prop, what, where, recurse){
var tmp = fetch(what, where, recurse);
if(tmp) obj[prop] = tmp;
}
var isValidFeed = function(value){
return value === "rss" || value === "feed" || value === "rdf:RDF";
};
FeedHandler.prototype.onend = function(){
var feed = {},
feedRoot = getOneElement(isValidFeed, this.dom),
tmp, childs;
if(feedRoot){
if(feedRoot.name === "feed"){
childs = feedRoot.children;
feed.type = "atom";
addConditionally(feed, "id", "id", childs);
addConditionally(feed, "title", "title", childs);
if((tmp = getOneElement("link", childs)) && (tmp = tmp.attribs) && (tmp = tmp.href)) feed.link = tmp;
addConditionally(feed, "description", "subtitle", childs);
if((tmp = fetch("updated", childs))) feed.updated = new Date(tmp);
addConditionally(feed, "author", "email", childs, true);
feed.items = getElements("entry", childs).map(function(item){
var entry = {}, tmp;
item = item.children;
addConditionally(entry, "id", "id", item);
addConditionally(entry, "title", "title", item);
if((tmp = getOneElement("link", item)) && (tmp = tmp.attribs) && (tmp = tmp.href)) entry.link = tmp;
if((tmp = fetch("summary", item) || fetch("content", item))) entry.description = tmp;
if((tmp = fetch("updated", item))) entry.pubDate = new Date(tmp);
return entry;
});
} else {
childs = getOneElement("channel", feedRoot.children).children;
feed.type = feedRoot.name.substr(0, 3);
feed.id = "";
addConditionally(feed, "title", "title", childs);
addConditionally(feed, "link", "link", childs);
addConditionally(feed, "description", "description", childs);
if((tmp = fetch("lastBuildDate", childs))) feed.updated = new Date(tmp);
addConditionally(feed, "author", "managingEditor", childs, true);
feed.items = getElements("item", feedRoot.children).map(function(item){
var entry = {}, tmp;
item = item.children;
addConditionally(entry, "id", "guid", item);
addConditionally(entry, "title", "title", item);
addConditionally(entry, "link", "link", item);
addConditionally(entry, "description", "description", item);
if((tmp = fetch("pubDate", item))) entry.pubDate = new Date(tmp);
return entry;
});
}
}
this.dom = feed;
DomHandler.prototype._handleCallback.call(
this, feedRoot ? null : Error("couldn't find root of feed")
);
};
module.exports = FeedHandler;
+391
View File
@@ -0,0 +1,391 @@
/**
* @file parser
* @author https://github.com/fb55/htmlparser2
* @modify tanghao03 yanghuabei(yanghuabei@outlook.com)
* @date 2018/8/16
*/
var Tokenizer = require('./Tokenizer.js');
/*
Options:
xmlMode: Disables the special behavior for script/style tags (false by default)
lowerCaseAttributeNames: call .toLowerCase for each attribute name (true if xmlMode is `false`)
lowerCaseTags: call .toLowerCase for each tag name (true if xmlMode is `false`)
*/
/*
Callbacks:
oncdataend,
oncdatastart,
onclosetag,
oncomment,
oncommentend,
onerror,
onopentag,
onprocessinginstruction,
onreset,
ontext
*/
var formTags = {
input: true,
option: true,
optgroup: true,
select: true,
button: true,
datalist: true,
textarea: true
};
var openImpliesClose = {
tr: {
tr: true,
th: true,
td: true
},
th: {
th: true
},
td: {
thead: true,
th: true,
td: true
},
body: {
head: true,
link: true,
script: true
},
li: {
li: true
},
p: {
p: true
},
h1: {
p: true
},
h2: {
p: true
},
h3: {
p: true
},
h4: {
p: true
},
h5: {
p: true
},
h6: {
p: true
},
select: formTags,
input: formTags,
output: formTags,
button: formTags,
datalist: formTags,
textarea: formTags,
option: {
option: true
},
optgroup: {
optgroup: true
}
};
var voidElements = {
__proto__: null,
area: true,
base: true,
basefont: true,
br: true,
col: true,
command: true,
embed: true,
frame: true,
hr: true,
img: true,
input: true,
isindex: true,
keygen: true,
link: true,
meta: true,
param: true,
source: true,
track: true,
wbr: true,
//common self closing svg elements
path: true,
circle: true,
ellipse: true,
line: true,
rect: true,
use: true,
stop: true,
polyline: true,
polygon: true
};
var re_nameEnd = /\s|\//;
function Parser(cbs, options) {
this._options = options || {};
this._cbs = cbs || {};
this._tagname = '';
this._attribname = '';
this._attribvalue = '';
this._attribs = null;
this._stack = [];
this.startIndex = 0;
this.endIndex = null;
this._lowerCaseTagNames = 'lowerCaseTags' in this._options ? !!this._options.lowerCaseTags : !this._options.xmlMode;
this._lowerCaseAttributeNames =
'lowerCaseAttributeNames' in this._options ? !!this._options.lowerCaseAttributeNames : !this._options.xmlMode;
if (this._options.Tokenizer) {
Tokenizer = this._options.Tokenizer;
}
this._tokenizer = new Tokenizer(this._options, this);
if (this._cbs.onparserinit) this._cbs.onparserinit(this);
}
require('inherits')(Parser, require('events').EventEmitter);
Parser.prototype._updatePosition = function (initialOffset) {
if (this.endIndex === null) {
if (this._tokenizer._sectionStart <= initialOffset) {
this.startIndex = 0;
} else {
this.startIndex = this._tokenizer._sectionStart - initialOffset;
}
} else this.startIndex = this.endIndex + 1;
this.endIndex = this._tokenizer.getAbsoluteIndex();
};
//Tokenizer event handlers
Parser.prototype.ontext = function (data) {
this._updatePosition(1);
this.endIndex--;
if (this._cbs.ontext) this._cbs.ontext(data);
};
Parser.prototype.onopentagname = function (name) {
if (this._lowerCaseTagNames) {
name = name.toLowerCase();
}
this._tagname = name;
if (!this._options.xmlMode && name in openImpliesClose) {
for (var el;
(el = this._stack[this._stack.length - 1]) in openImpliesClose[name]; this.onclosetag(el));
}
if (this._options.xmlMode || !(name in voidElements)) {
this._stack.push(name);
}
if (this._cbs.onopentagname) this._cbs.onopentagname(name);
if (this._cbs.onopentag) {
this._attribs = {};
this._singleQuoteAttribs = {};
}
};
Parser.prototype.onopentagend = function (isSelfClose = false) {
this._updatePosition(1);
if (this._attribs) {
if (this._cbs.onopentag) this._cbs.onopentag(this._tagname, this._attribs, this._singleQuoteAttribs);
this._attribs = null;
this._singleQuoteAttribs = null;
}
if (!this._options.xmlMode && this._cbs.onclosetag && this._tagname in voidElements) {
this._cbs.onclosetag(this._tagname, isSelfClose);
}
this._tagname = '';
};
Parser.prototype.onclosetag = function (name) {
this._updatePosition(1);
if (this._lowerCaseTagNames) {
name = name.toLowerCase();
}
if (this._stack.length && (!(name in voidElements) || this._options.xmlMode)) {
var pos = this._stack.lastIndexOf(name);
if (pos !== -1) {
if (this._cbs.onclosetag) {
pos = this._stack.length - pos;
while (pos--) this._cbs.onclosetag(this._stack.pop());
} else this._stack.length = pos;
} else if (name === 'p' && !this._options.xmlMode) {
this.onopentagname(name);
this._closeCurrentTag();
}
} else if (!this._options.xmlMode && (name === 'br' || name === 'p')) {
this.onopentagname(name);
this._closeCurrentTag();
}
};
Parser.prototype.onselfclosingtag = function () {
if (this._options.xmlMode || this._options.recognizeSelfClosing) {
this._closeCurrentTag(true);
} else {
this.onopentagend();
}
};
Parser.prototype._closeCurrentTag = function (isSelfClose = false) {
var name = this._tagname;
this.onopentagend(isSelfClose);
//self-closing tags will be on the top of the stack
//(cheaper check than in onclosetag)
if (this._stack[this._stack.length - 1] === name) {
if (this._cbs.onclosetag) {
this._cbs.onclosetag(name, isSelfClose);
}
this._stack.pop();
}
};
Parser.prototype.onattribname = function (name) {
if (this._lowerCaseAttributeNames) {
name = name.toLowerCase();
}
this._attribname = name;
};
Parser.prototype.onattribvalueusesinglequote = function () {
this._isAttribWithSingleQuote = true;
};
Parser.prototype.onattribdata = function (value) {
this._attribvalue += value;
};
Parser.prototype.onattribend = function () {
if (this._cbs.onattribute) this._cbs.onattribute(this._attribname, this._attribvalue);
if (this._attribs && !Object.prototype.hasOwnProperty.call(this._attribs, this._attribname)) {
this._attribs[this._attribname] = this._attribvalue;
// 记录一下值是单引号包裹的属性
if (this._isAttribWithSingleQuote) {
this._singleQuoteAttribs[this._attribname] = true;
}
}
this._attribname = '';
this._attribvalue = '';
this._isAttribWithSingleQuote = null;
};
Parser.prototype._getInstructionName = function (value) {
var idx = value.search(re_nameEnd),
name = idx < 0 ? value : value.substr(0, idx);
if (this._lowerCaseTagNames) {
name = name.toLowerCase();
}
return name;
};
Parser.prototype.ondeclaration = function (value) {
if (this._cbs.onprocessinginstruction) {
var name = this._getInstructionName(value);
this._cbs.onprocessinginstruction('!' + name, '!' + value);
}
};
Parser.prototype.onprocessinginstruction = function (value) {
if (this._cbs.onprocessinginstruction) {
var name = this._getInstructionName(value);
this._cbs.onprocessinginstruction('?' + name, '?' + value);
}
};
Parser.prototype.oncomment = function (value) {
this._updatePosition(4);
if (this._cbs.oncomment) this._cbs.oncomment(value);
if (this._cbs.oncommentend) this._cbs.oncommentend();
};
Parser.prototype.oncdata = function (value) {
this._updatePosition(1);
if (this._options.xmlMode || this._options.recognizeCDATA) {
if (this._cbs.oncdatastart) this._cbs.oncdatastart();
if (this._cbs.ontext) this._cbs.ontext(value);
if (this._cbs.oncdataend) this._cbs.oncdataend();
} else {
this.oncomment('[CDATA[' + value + ']]');
}
};
Parser.prototype.onerror = function (err) {
if (this._cbs.onerror) this._cbs.onerror(err);
};
Parser.prototype.onend = function () {
if (this._cbs.onclosetag) {
for (var i = this._stack.length; i > 0; this._cbs.onclosetag(this._stack[--i]));
}
if (this._cbs.onend) this._cbs.onend();
};
//Resets the parser to a blank state, ready to parse a new HTML document
Parser.prototype.reset = function () {
if (this._cbs.onreset) this._cbs.onreset();
this._tokenizer.reset();
this._tagname = '';
this._attribname = '';
this._attribs = null;
this._stack = [];
if (this._cbs.onparserinit) this._cbs.onparserinit(this);
};
//Parses a complete HTML document and pushes it to the handler
Parser.prototype.parseComplete = function (data) {
this.reset();
this.end(data);
};
Parser.prototype.write = function (chunk) {
this._tokenizer.write(chunk);
};
Parser.prototype.end = function (chunk) {
this._tokenizer.end(chunk);
};
Parser.prototype.pause = function () {
this._tokenizer.pause();
};
Parser.prototype.resume = function () {
this._tokenizer.resume();
};
//alias for backwards compat
Parser.prototype.parseChunk = Parser.prototype.write;
Parser.prototype.done = Parser.prototype.end;
module.exports = Parser;
+27
View File
@@ -0,0 +1,27 @@
module.exports = ProxyHandler;
function ProxyHandler(cbs){
this._cbs = cbs || {};
}
var EVENTS = require("./").EVENTS;
Object.keys(EVENTS).forEach(function(name){
if(EVENTS[name] === 0){
name = "on" + name;
ProxyHandler.prototype[name] = function(){
if(this._cbs[name]) this._cbs[name]();
};
} else if(EVENTS[name] === 1){
name = "on" + name;
ProxyHandler.prototype[name] = function(a){
if(this._cbs[name]) this._cbs[name](a);
};
} else if(EVENTS[name] === 2){
name = "on" + name;
ProxyHandler.prototype[name] = function(a, b){
if(this._cbs[name]) this._cbs[name](a, b);
};
} else {
throw Error("wrong number of arguments");
}
});
+35
View File
@@ -0,0 +1,35 @@
module.exports = Stream;
var Parser = require("./WritableStream.js");
function Stream(options){
Parser.call(this, new Cbs(this), options);
}
require("inherits")(Stream, Parser);
Stream.prototype.readable = true;
function Cbs(scope){
this.scope = scope;
}
var EVENTS = require("../").EVENTS;
Object.keys(EVENTS).forEach(function(name){
if(EVENTS[name] === 0){
Cbs.prototype["on" + name] = function(){
this.scope.emit(name);
};
} else if(EVENTS[name] === 1){
Cbs.prototype["on" + name] = function(a){
this.scope.emit(name, a);
};
} else if(EVENTS[name] === 2){
Cbs.prototype["on" + name] = function(a, b){
this.scope.emit(name, a, b);
};
} else {
throw Error("wrong number of arguments!");
}
});
+902
View File
@@ -0,0 +1,902 @@
module.exports = Tokenizer;
var decodeCodePoint = require("entities/lib/decode_codepoint.js");
var entityMap = require("entities/maps/entities.json");
var legacyMap = require("entities/maps/legacy.json");
var xmlMap = require("entities/maps/xml.json");
var i = 0;
var TEXT = i++;
var BEFORE_TAG_NAME = i++; //after <
var IN_TAG_NAME = i++;
var IN_SELF_CLOSING_TAG = i++;
var BEFORE_CLOSING_TAG_NAME = i++;
var IN_CLOSING_TAG_NAME = i++;
var AFTER_CLOSING_TAG_NAME = i++;
//attributes
var BEFORE_ATTRIBUTE_NAME = i++;
var IN_ATTRIBUTE_NAME = i++;
var AFTER_ATTRIBUTE_NAME = i++;
var BEFORE_ATTRIBUTE_VALUE = i++;
var IN_ATTRIBUTE_VALUE_DQ = i++; // "
var IN_ATTRIBUTE_VALUE_SQ = i++; // '
var IN_ATTRIBUTE_VALUE_NQ = i++;
//declarations
var BEFORE_DECLARATION = i++; // !
var IN_DECLARATION = i++;
//processing instructions
var IN_PROCESSING_INSTRUCTION = i++; // ?
//comments
var BEFORE_COMMENT = i++;
var IN_COMMENT = i++;
var AFTER_COMMENT_1 = i++;
var AFTER_COMMENT_2 = i++;
//cdata
var BEFORE_CDATA_1 = i++; // [
var BEFORE_CDATA_2 = i++; // C
var BEFORE_CDATA_3 = i++; // D
var BEFORE_CDATA_4 = i++; // A
var BEFORE_CDATA_5 = i++; // T
var BEFORE_CDATA_6 = i++; // A
var IN_CDATA = i++; // [
var AFTER_CDATA_1 = i++; // ]
var AFTER_CDATA_2 = i++; // ]
//special tags
var BEFORE_SPECIAL = i++; //S
var BEFORE_SPECIAL_END = i++; //S
var BEFORE_SCRIPT_1 = i++; //C
var BEFORE_SCRIPT_2 = i++; //R
var BEFORE_SCRIPT_3 = i++; //I
var BEFORE_SCRIPT_4 = i++; //P
var BEFORE_SCRIPT_5 = i++; //T
var AFTER_SCRIPT_1 = i++; //C
var AFTER_SCRIPT_2 = i++; //R
var AFTER_SCRIPT_3 = i++; //I
var AFTER_SCRIPT_4 = i++; //P
var AFTER_SCRIPT_5 = i++; //T
var BEFORE_STYLE_1 = i++; //T
var BEFORE_STYLE_2 = i++; //Y
var BEFORE_STYLE_3 = i++; //L
var BEFORE_STYLE_4 = i++; //E
var AFTER_STYLE_1 = i++; //T
var AFTER_STYLE_2 = i++; //Y
var AFTER_STYLE_3 = i++; //L
var AFTER_STYLE_4 = i++; //E
var BEFORE_ENTITY = i++; //&
var BEFORE_NUMERIC_ENTITY = i++; //#
var IN_NAMED_ENTITY = i++;
var IN_NUMERIC_ENTITY = i++;
var IN_HEX_ENTITY = i++; //X
var j = 0;
var SPECIAL_NONE = j++;
var SPECIAL_SCRIPT = j++;
var SPECIAL_STYLE = j++;
function whitespace(c) {
return c === " " || c === "\n" || c === "\t" || c === "\f" || c === "\r";
}
function ifElseState(upper, SUCCESS, FAILURE) {
var lower = upper.toLowerCase();
if (upper === lower) {
return function (c) {
if (c === lower) {
this._state = SUCCESS;
} else {
this._state = FAILURE;
this._index--;
}
};
} else {
return function (c) {
if (c === lower || c === upper) {
this._state = SUCCESS;
} else {
this._state = FAILURE;
this._index--;
}
};
}
}
function consumeSpecialNameChar(upper, NEXT_STATE) {
var lower = upper.toLowerCase();
return function (c) {
if (c === lower || c === upper) {
this._state = NEXT_STATE;
} else {
this._state = IN_TAG_NAME;
this._index--; //consume the token again
}
};
}
function Tokenizer(options, cbs) {
this._state = TEXT;
this._buffer = "";
this._sectionStart = 0;
this._index = 0;
this._bufferOffset = 0; //chars removed from _buffer
this._baseState = TEXT;
this._special = SPECIAL_NONE;
this._cbs = cbs;
this._running = true;
this._ended = false;
this._xmlMode = !!(options && options.xmlMode);
this._decodeEntities = !!(options && options.decodeEntities);
}
Tokenizer.prototype._stateText = function (c) {
if (c === "<") {
if (this._index > this._sectionStart) {
this._cbs.ontext(this._getSection());
}
this._state = BEFORE_TAG_NAME;
this._sectionStart = this._index;
} else if (this._decodeEntities && this._special === SPECIAL_NONE && c === "&") {
if (this._index > this._sectionStart) {
this._cbs.ontext(this._getSection());
}
this._baseState = TEXT;
this._state = BEFORE_ENTITY;
this._sectionStart = this._index;
}
};
Tokenizer.prototype._stateBeforeTagName = function (c) {
if (c === "/") {
this._state = BEFORE_CLOSING_TAG_NAME;
} else if (c === "<") {
this._cbs.ontext(this._getSection());
this._sectionStart = this._index;
} else if (c === ">" || this._special !== SPECIAL_NONE || whitespace(c)) {
this._state = TEXT;
} else if (c === "!") {
this._state = BEFORE_DECLARATION;
this._sectionStart = this._index + 1;
} else if (c === "?") {
this._state = IN_PROCESSING_INSTRUCTION;
this._sectionStart = this._index + 1;
} else {
this._state = (!this._xmlMode && (c === "s" || c === "S")) ?
BEFORE_SPECIAL : IN_TAG_NAME;
this._sectionStart = this._index;
}
};
Tokenizer.prototype._stateInTagName = function (c) {
if (c === "/" || c === ">" || whitespace(c)) {
this._emitToken("onopentagname");
this._state = BEFORE_ATTRIBUTE_NAME;
this._index--;
}
};
Tokenizer.prototype._stateBeforeCloseingTagName = function (c) {
if (whitespace(c));
else if (c === ">") {
this._state = TEXT;
} else if (this._special !== SPECIAL_NONE) {
if (c === "s" || c === "S") {
this._state = BEFORE_SPECIAL_END;
} else {
this._state = TEXT;
this._index--;
}
} else {
this._state = IN_CLOSING_TAG_NAME;
this._sectionStart = this._index;
}
};
Tokenizer.prototype._stateInCloseingTagName = function (c) {
if (c === ">" || whitespace(c)) {
this._emitToken("onclosetag");
this._state = AFTER_CLOSING_TAG_NAME;
this._index--;
}
};
Tokenizer.prototype._stateAfterCloseingTagName = function (c) {
//skip everything until ">"
if (c === ">") {
this._state = TEXT;
this._sectionStart = this._index + 1;
}
};
Tokenizer.prototype._stateBeforeAttributeName = function (c) {
if (c === ">") {
this._cbs.onopentagend();
this._state = TEXT;
this._sectionStart = this._index + 1;
} else if (c === "/") {
this._state = IN_SELF_CLOSING_TAG;
} else if (!whitespace(c)) {
this._state = IN_ATTRIBUTE_NAME;
this._sectionStart = this._index;
}
};
Tokenizer.prototype._stateInSelfClosingTag = function (c) {
if (c === ">") {
this._cbs.onselfclosingtag();
this._state = TEXT;
this._sectionStart = this._index + 1;
} else if (!whitespace(c)) {
this._state = BEFORE_ATTRIBUTE_NAME;
this._index--;
}
};
Tokenizer.prototype._stateInAttributeName = function (c) {
if (c === "=" || c === "/" || c === ">" || whitespace(c)) {
this._cbs.onattribname(this._getSection());
this._sectionStart = -1;
this._state = AFTER_ATTRIBUTE_NAME;
this._index--;
}
};
Tokenizer.prototype._stateAfterAttributeName = function (c) {
if (c === "=") {
this._state = BEFORE_ATTRIBUTE_VALUE;
} else if (c === "/" || c === ">") {
this._cbs.onattribend();
this._state = BEFORE_ATTRIBUTE_NAME;
this._index--;
} else if (!whitespace(c)) {
this._cbs.onattribend();
this._state = IN_ATTRIBUTE_NAME;
this._sectionStart = this._index;
}
};
Tokenizer.prototype._stateBeforeAttributeValue = function (c) {
if (c === "\"") {
this._state = IN_ATTRIBUTE_VALUE_DQ;
this._sectionStart = this._index + 1;
} else if (c === "'") {
this._state = IN_ATTRIBUTE_VALUE_SQ;
this._sectionStart = this._index + 1;
this._cbs.onattribvalueusesinglequote();
} else if (!whitespace(c)) {
this._sectionStart = this._index;
this._emitToken("onattribdata");
this._cbs.onattribend();
this._state = BEFORE_ATTRIBUTE_NAME;
this._index--; //reconsume token
}
};
Tokenizer.prototype._stateInAttributeValueDoubleQuotes = function (c) {
if (c === "\"") {
if (this._buffer.charAt(this._index - 1) !== "\\") {
this._emitToken("onattribdata");
this._cbs.onattribend();
this._state = BEFORE_ATTRIBUTE_NAME;
}
} else if (this._decodeEntities && c === "&") {
this._emitToken("onattribdata");
this._baseState = this._state;
this._state = BEFORE_ENTITY;
this._sectionStart = this._index;
}
};
Tokenizer.prototype._stateInAttributeValueSingleQuotes = function (c) {
if (c === "'") {
this._emitToken("onattribdata");
this._cbs.onattribend();
this._state = BEFORE_ATTRIBUTE_NAME;
} else if (this._decodeEntities && c === "&") {
this._emitToken("onattribdata");
this._baseState = this._state;
this._state = BEFORE_ENTITY;
this._sectionStart = this._index;
}
};
Tokenizer.prototype._stateInAttributeValueNoQuotes = function (c) {
if (whitespace(c) || c === ">") {
this._emitToken("onattribdata");
this._cbs.onattribend();
this._state = BEFORE_ATTRIBUTE_NAME;
this._index--;
} else if (this._decodeEntities && c === "&") {
this._emitToken("onattribdata");
this._baseState = this._state;
this._state = BEFORE_ENTITY;
this._sectionStart = this._index;
}
};
Tokenizer.prototype._stateBeforeDeclaration = function (c) {
this._state = c === "[" ? BEFORE_CDATA_1 :
c === "-" ? BEFORE_COMMENT :
IN_DECLARATION;
};
Tokenizer.prototype._stateInDeclaration = function (c) {
if (c === ">") {
this._cbs.ondeclaration(this._getSection());
this._state = TEXT;
this._sectionStart = this._index + 1;
}
};
Tokenizer.prototype._stateInProcessingInstruction = function (c) {
if (c === ">") {
this._cbs.onprocessinginstruction(this._getSection());
this._state = TEXT;
this._sectionStart = this._index + 1;
}
};
Tokenizer.prototype._stateBeforeComment = function (c) {
if (c === "-") {
this._state = IN_COMMENT;
this._sectionStart = this._index + 1;
} else {
this._state = IN_DECLARATION;
}
};
Tokenizer.prototype._stateInComment = function (c) {
if (c === "-") this._state = AFTER_COMMENT_1;
};
Tokenizer.prototype._stateAfterComment1 = function (c) {
if (c === "-") {
this._state = AFTER_COMMENT_2;
} else {
this._state = IN_COMMENT;
}
};
Tokenizer.prototype._stateAfterComment2 = function (c) {
if (c === ">") {
//remove 2 trailing chars
this._cbs.oncomment(this._buffer.substring(this._sectionStart, this._index - 2));
this._state = TEXT;
this._sectionStart = this._index + 1;
} else if (c !== "-") {
this._state = IN_COMMENT;
}
// else: stay in AFTER_COMMENT_2 (`--->`)
};
Tokenizer.prototype._stateBeforeCdata1 = ifElseState("C", BEFORE_CDATA_2, IN_DECLARATION);
Tokenizer.prototype._stateBeforeCdata2 = ifElseState("D", BEFORE_CDATA_3, IN_DECLARATION);
Tokenizer.prototype._stateBeforeCdata3 = ifElseState("A", BEFORE_CDATA_4, IN_DECLARATION);
Tokenizer.prototype._stateBeforeCdata4 = ifElseState("T", BEFORE_CDATA_5, IN_DECLARATION);
Tokenizer.prototype._stateBeforeCdata5 = ifElseState("A", BEFORE_CDATA_6, IN_DECLARATION);
Tokenizer.prototype._stateBeforeCdata6 = function (c) {
if (c === "[") {
this._state = IN_CDATA;
this._sectionStart = this._index + 1;
} else {
this._state = IN_DECLARATION;
this._index--;
}
};
Tokenizer.prototype._stateInCdata = function (c) {
if (c === "]") this._state = AFTER_CDATA_1;
};
Tokenizer.prototype._stateAfterCdata1 = function (c) {
if (c === "]") this._state = AFTER_CDATA_2;
else this._state = IN_CDATA;
};
Tokenizer.prototype._stateAfterCdata2 = function (c) {
if (c === ">") {
//remove 2 trailing chars
this._cbs.oncdata(this._buffer.substring(this._sectionStart, this._index - 2));
this._state = TEXT;
this._sectionStart = this._index + 1;
} else if (c !== "]") {
this._state = IN_CDATA;
}
//else: stay in AFTER_CDATA_2 (`]]]>`)
};
Tokenizer.prototype._stateBeforeSpecial = function (c) {
if (c === "c" || c === "C") {
this._state = BEFORE_SCRIPT_1;
} else if (c === "t" || c === "T") {
this._state = BEFORE_STYLE_1;
} else {
this._state = IN_TAG_NAME;
this._index--; //consume the token again
}
};
Tokenizer.prototype._stateBeforeSpecialEnd = function (c) {
if (this._special === SPECIAL_SCRIPT && (c === "c" || c === "C")) {
this._state = AFTER_SCRIPT_1;
} else if (this._special === SPECIAL_STYLE && (c === "t" || c === "T")) {
this._state = AFTER_STYLE_1;
} else this._state = TEXT;
};
Tokenizer.prototype._stateBeforeScript1 = consumeSpecialNameChar("R", BEFORE_SCRIPT_2);
Tokenizer.prototype._stateBeforeScript2 = consumeSpecialNameChar("I", BEFORE_SCRIPT_3);
Tokenizer.prototype._stateBeforeScript3 = consumeSpecialNameChar("P", BEFORE_SCRIPT_4);
Tokenizer.prototype._stateBeforeScript4 = consumeSpecialNameChar("T", BEFORE_SCRIPT_5);
Tokenizer.prototype._stateBeforeScript5 = function (c) {
if (c === "/" || c === ">" || whitespace(c)) {
this._special = SPECIAL_SCRIPT;
}
this._state = IN_TAG_NAME;
this._index--; //consume the token again
};
Tokenizer.prototype._stateAfterScript1 = ifElseState("R", AFTER_SCRIPT_2, TEXT);
Tokenizer.prototype._stateAfterScript2 = ifElseState("I", AFTER_SCRIPT_3, TEXT);
Tokenizer.prototype._stateAfterScript3 = ifElseState("P", AFTER_SCRIPT_4, TEXT);
Tokenizer.prototype._stateAfterScript4 = ifElseState("T", AFTER_SCRIPT_5, TEXT);
Tokenizer.prototype._stateAfterScript5 = function (c) {
if (c === ">" || whitespace(c)) {
this._special = SPECIAL_NONE;
this._state = IN_CLOSING_TAG_NAME;
this._sectionStart = this._index - 6;
this._index--; //reconsume the token
} else this._state = TEXT;
};
Tokenizer.prototype._stateBeforeStyle1 = consumeSpecialNameChar("Y", BEFORE_STYLE_2);
Tokenizer.prototype._stateBeforeStyle2 = consumeSpecialNameChar("L", BEFORE_STYLE_3);
Tokenizer.prototype._stateBeforeStyle3 = consumeSpecialNameChar("E", BEFORE_STYLE_4);
Tokenizer.prototype._stateBeforeStyle4 = function (c) {
if (c === "/" || c === ">" || whitespace(c)) {
this._special = SPECIAL_STYLE;
}
this._state = IN_TAG_NAME;
this._index--; //consume the token again
};
Tokenizer.prototype._stateAfterStyle1 = ifElseState("Y", AFTER_STYLE_2, TEXT);
Tokenizer.prototype._stateAfterStyle2 = ifElseState("L", AFTER_STYLE_3, TEXT);
Tokenizer.prototype._stateAfterStyle3 = ifElseState("E", AFTER_STYLE_4, TEXT);
Tokenizer.prototype._stateAfterStyle4 = function (c) {
if (c === ">" || whitespace(c)) {
this._special = SPECIAL_NONE;
this._state = IN_CLOSING_TAG_NAME;
this._sectionStart = this._index - 5;
this._index--; //reconsume the token
} else this._state = TEXT;
};
Tokenizer.prototype._stateBeforeEntity = ifElseState("#", BEFORE_NUMERIC_ENTITY, IN_NAMED_ENTITY);
Tokenizer.prototype._stateBeforeNumericEntity = ifElseState("X", IN_HEX_ENTITY, IN_NUMERIC_ENTITY);
//for entities terminated with a semicolon
Tokenizer.prototype._parseNamedEntityStrict = function () {
//offset = 1
if (this._sectionStart + 1 < this._index) {
var entity = this._buffer.substring(this._sectionStart + 1, this._index),
map = this._xmlMode ? xmlMap : entityMap;
if (map.hasOwnProperty(entity)) {
this._emitPartial(map[entity]);
this._sectionStart = this._index + 1;
}
}
};
//parses legacy entities (without trailing semicolon)
Tokenizer.prototype._parseLegacyEntity = function () {
var start = this._sectionStart + 1,
limit = this._index - start;
if (limit > 6) limit = 6; //the max length of legacy entities is 6
while (limit >= 2) { //the min length of legacy entities is 2
var entity = this._buffer.substr(start, limit);
if (legacyMap.hasOwnProperty(entity)) {
this._emitPartial(legacyMap[entity]);
this._sectionStart += limit + 1;
return;
} else {
limit--;
}
}
};
Tokenizer.prototype._stateInNamedEntity = function (c) {
if (c === ";") {
this._parseNamedEntityStrict();
if (this._sectionStart + 1 < this._index && !this._xmlMode) {
this._parseLegacyEntity();
}
this._state = this._baseState;
} else if ((c < "a" || c > "z") && (c < "A" || c > "Z") && (c < "0" || c > "9")) {
if (this._xmlMode);
else if (this._sectionStart + 1 === this._index);
else if (this._baseState !== TEXT) {
if (c !== "=") {
this._parseNamedEntityStrict();
}
} else {
this._parseLegacyEntity();
}
this._state = this._baseState;
this._index--;
}
};
Tokenizer.prototype._decodeNumericEntity = function (offset, base) {
var sectionStart = this._sectionStart + offset;
if (sectionStart !== this._index) {
//parse entity
var entity = this._buffer.substring(sectionStart, this._index);
var parsed = parseInt(entity, base);
this._emitPartial(decodeCodePoint(parsed));
this._sectionStart = this._index;
} else {
this._sectionStart--;
}
this._state = this._baseState;
};
Tokenizer.prototype._stateInNumericEntity = function (c) {
if (c === ";") {
this._decodeNumericEntity(2, 10);
this._sectionStart++;
} else if (c < "0" || c > "9") {
if (!this._xmlMode) {
this._decodeNumericEntity(2, 10);
} else {
this._state = this._baseState;
}
this._index--;
}
};
Tokenizer.prototype._stateInHexEntity = function (c) {
if (c === ";") {
this._decodeNumericEntity(3, 16);
this._sectionStart++;
} else if ((c < "a" || c > "f") && (c < "A" || c > "F") && (c < "0" || c > "9")) {
if (!this._xmlMode) {
this._decodeNumericEntity(3, 16);
} else {
this._state = this._baseState;
}
this._index--;
}
};
Tokenizer.prototype._cleanup = function () {
if (this._sectionStart < 0) {
this._buffer = "";
this._bufferOffset += this._index;
this._index = 0;
} else if (this._running) {
if (this._state === TEXT) {
if (this._sectionStart !== this._index) {
this._cbs.ontext(this._buffer.substr(this._sectionStart));
}
this._buffer = "";
this._bufferOffset += this._index;
this._index = 0;
} else if (this._sectionStart === this._index) {
//the section just started
this._buffer = "";
this._bufferOffset += this._index;
this._index = 0;
} else {
//remove everything unnecessary
this._buffer = this._buffer.substr(this._sectionStart);
this._index -= this._sectionStart;
this._bufferOffset += this._sectionStart;
}
this._sectionStart = 0;
}
};
//TODO make events conditional
Tokenizer.prototype.write = function (chunk) {
if (this._ended) this._cbs.onerror(Error(".write() after done!"));
this._buffer += chunk;
this._parse();
};
Tokenizer.prototype._parse = function () {
while (this._index < this._buffer.length && this._running) {
var c = this._buffer.charAt(this._index);
if (this._state === TEXT) {
this._stateText(c);
} else if (this._state === BEFORE_TAG_NAME) {
this._stateBeforeTagName(c);
} else if (this._state === IN_TAG_NAME) {
this._stateInTagName(c);
} else if (this._state === BEFORE_CLOSING_TAG_NAME) {
this._stateBeforeCloseingTagName(c);
} else if (this._state === IN_CLOSING_TAG_NAME) {
this._stateInCloseingTagName(c);
} else if (this._state === AFTER_CLOSING_TAG_NAME) {
this._stateAfterCloseingTagName(c);
} else if (this._state === IN_SELF_CLOSING_TAG) {
this._stateInSelfClosingTag(c);
}
/*
* attributes
*/
else if (this._state === BEFORE_ATTRIBUTE_NAME) {
this._stateBeforeAttributeName(c);
} else if (this._state === IN_ATTRIBUTE_NAME) {
this._stateInAttributeName(c);
} else if (this._state === AFTER_ATTRIBUTE_NAME) {
this._stateAfterAttributeName(c);
} else if (this._state === BEFORE_ATTRIBUTE_VALUE) {
this._stateBeforeAttributeValue(c);
} else if (this._state === IN_ATTRIBUTE_VALUE_DQ) {
this._stateInAttributeValueDoubleQuotes(c);
} else if (this._state === IN_ATTRIBUTE_VALUE_SQ) {
this._stateInAttributeValueSingleQuotes(c);
} else if (this._state === IN_ATTRIBUTE_VALUE_NQ) {
this._stateInAttributeValueNoQuotes(c);
}
/*
* declarations
*/
else if (this._state === BEFORE_DECLARATION) {
this._stateBeforeDeclaration(c);
} else if (this._state === IN_DECLARATION) {
this._stateInDeclaration(c);
}
/*
* processing instructions
*/
else if (this._state === IN_PROCESSING_INSTRUCTION) {
this._stateInProcessingInstruction(c);
}
/*
* comments
*/
else if (this._state === BEFORE_COMMENT) {
this._stateBeforeComment(c);
} else if (this._state === IN_COMMENT) {
this._stateInComment(c);
} else if (this._state === AFTER_COMMENT_1) {
this._stateAfterComment1(c);
} else if (this._state === AFTER_COMMENT_2) {
this._stateAfterComment2(c);
}
/*
* cdata
*/
else if (this._state === BEFORE_CDATA_1) {
this._stateBeforeCdata1(c);
} else if (this._state === BEFORE_CDATA_2) {
this._stateBeforeCdata2(c);
} else if (this._state === BEFORE_CDATA_3) {
this._stateBeforeCdata3(c);
} else if (this._state === BEFORE_CDATA_4) {
this._stateBeforeCdata4(c);
} else if (this._state === BEFORE_CDATA_5) {
this._stateBeforeCdata5(c);
} else if (this._state === BEFORE_CDATA_6) {
this._stateBeforeCdata6(c);
} else if (this._state === IN_CDATA) {
this._stateInCdata(c);
} else if (this._state === AFTER_CDATA_1) {
this._stateAfterCdata1(c);
} else if (this._state === AFTER_CDATA_2) {
this._stateAfterCdata2(c);
}
/*
* special tags
*/
else if (this._state === BEFORE_SPECIAL) {
this._stateBeforeSpecial(c);
} else if (this._state === BEFORE_SPECIAL_END) {
this._stateBeforeSpecialEnd(c);
}
/*
* script
*/
else if (this._state === BEFORE_SCRIPT_1) {
this._stateBeforeScript1(c);
} else if (this._state === BEFORE_SCRIPT_2) {
this._stateBeforeScript2(c);
} else if (this._state === BEFORE_SCRIPT_3) {
this._stateBeforeScript3(c);
} else if (this._state === BEFORE_SCRIPT_4) {
this._stateBeforeScript4(c);
} else if (this._state === BEFORE_SCRIPT_5) {
this._stateBeforeScript5(c);
} else if (this._state === AFTER_SCRIPT_1) {
this._stateAfterScript1(c);
} else if (this._state === AFTER_SCRIPT_2) {
this._stateAfterScript2(c);
} else if (this._state === AFTER_SCRIPT_3) {
this._stateAfterScript3(c);
} else if (this._state === AFTER_SCRIPT_4) {
this._stateAfterScript4(c);
} else if (this._state === AFTER_SCRIPT_5) {
this._stateAfterScript5(c);
}
/*
* style
*/
else if (this._state === BEFORE_STYLE_1) {
this._stateBeforeStyle1(c);
} else if (this._state === BEFORE_STYLE_2) {
this._stateBeforeStyle2(c);
} else if (this._state === BEFORE_STYLE_3) {
this._stateBeforeStyle3(c);
} else if (this._state === BEFORE_STYLE_4) {
this._stateBeforeStyle4(c);
} else if (this._state === AFTER_STYLE_1) {
this._stateAfterStyle1(c);
} else if (this._state === AFTER_STYLE_2) {
this._stateAfterStyle2(c);
} else if (this._state === AFTER_STYLE_3) {
this._stateAfterStyle3(c);
} else if (this._state === AFTER_STYLE_4) {
this._stateAfterStyle4(c);
}
/*
* entities
*/
else if (this._state === BEFORE_ENTITY) {
this._stateBeforeEntity(c);
} else if (this._state === BEFORE_NUMERIC_ENTITY) {
this._stateBeforeNumericEntity(c);
} else if (this._state === IN_NAMED_ENTITY) {
this._stateInNamedEntity(c);
} else if (this._state === IN_NUMERIC_ENTITY) {
this._stateInNumericEntity(c);
} else if (this._state === IN_HEX_ENTITY) {
this._stateInHexEntity(c);
} else {
this._cbs.onerror(Error("unknown _state"), this._state);
}
this._index++;
}
this._cleanup();
};
Tokenizer.prototype.pause = function () {
this._running = false;
};
Tokenizer.prototype.resume = function () {
this._running = true;
if (this._index < this._buffer.length) {
this._parse();
}
if (this._ended) {
this._finish();
}
};
Tokenizer.prototype.end = function (chunk) {
if (this._ended) this._cbs.onerror(Error(".end() after done!"));
if (chunk) this.write(chunk);
this._ended = true;
if (this._running) this._finish();
};
Tokenizer.prototype._finish = function () {
//if there is remaining data, emit it in a reasonable way
if (this._sectionStart < this._index) {
this._handleTrailingData();
}
this._cbs.onend();
};
Tokenizer.prototype._handleTrailingData = function () {
var data = this._buffer.substr(this._sectionStart);
if (this._state === IN_CDATA || this._state === AFTER_CDATA_1 || this._state === AFTER_CDATA_2) {
this._cbs.oncdata(data);
} else if (this._state === IN_COMMENT || this._state === AFTER_COMMENT_1 || this._state === AFTER_COMMENT_2) {
this._cbs.oncomment(data);
} else if (this._state === IN_NAMED_ENTITY && !this._xmlMode) {
this._parseLegacyEntity();
if (this._sectionStart < this._index) {
this._state = this._baseState;
this._handleTrailingData();
}
} else if (this._state === IN_NUMERIC_ENTITY && !this._xmlMode) {
this._decodeNumericEntity(2, 10);
if (this._sectionStart < this._index) {
this._state = this._baseState;
this._handleTrailingData();
}
} else if (this._state === IN_HEX_ENTITY && !this._xmlMode) {
this._decodeNumericEntity(3, 16);
if (this._sectionStart < this._index) {
this._state = this._baseState;
this._handleTrailingData();
}
} else if (
this._state !== IN_TAG_NAME &&
this._state !== BEFORE_ATTRIBUTE_NAME &&
this._state !== BEFORE_ATTRIBUTE_VALUE &&
this._state !== AFTER_ATTRIBUTE_NAME &&
this._state !== IN_ATTRIBUTE_NAME &&
this._state !== IN_ATTRIBUTE_VALUE_SQ &&
this._state !== IN_ATTRIBUTE_VALUE_DQ &&
this._state !== IN_ATTRIBUTE_VALUE_NQ &&
this._state !== IN_CLOSING_TAG_NAME
) {
this._cbs.ontext(data);
}
//else, ignore remaining data
//TODO add a way to remove current tag
};
Tokenizer.prototype.reset = function () {
Tokenizer.call(this, {
xmlMode: this._xmlMode,
decodeEntities: this._decodeEntities
}, this._cbs);
};
Tokenizer.prototype.getAbsoluteIndex = function () {
return this._bufferOffset + this._index;
};
Tokenizer.prototype._getSection = function () {
return this._buffer.substring(this._sectionStart, this._index);
};
Tokenizer.prototype._emitToken = function (name) {
this._cbs[name](this._getSection());
this._sectionStart = -1;
};
Tokenizer.prototype._emitPartial = function (value) {
if (this._baseState !== TEXT) {
this._cbs.onattribdata(value); //TODO implement the new event
} else {
this._cbs.ontext(value);
}
};
+25
View File
@@ -0,0 +1,25 @@
module.exports = Stream;
var Parser = require("./Parser.js");
var WritableStream = require("readable-stream").Writable;
var StringDecoder = require("string_decoder").StringDecoder;
var Buffer = require("buffer").Buffer;
function Stream(cbs, options){
var parser = this._parser = new Parser(cbs, options);
var decoder = this._decoder = new StringDecoder();
WritableStream.call(this, {decodeStrings: false});
this.once("finish", function(){
parser.end(decoder.end());
});
}
require("inherits")(Stream, WritableStream);
WritableStream.prototype._write = function(chunk, encoding, cb){
if(chunk instanceof Buffer) chunk = this._decoder.write(chunk);
this._parser.write(chunk);
cb();
};
+68
View File
@@ -0,0 +1,68 @@
var Parser = require("./Parser.js");
var DomHandler = require("x-domhandler");
function defineProp(name, value) {
delete module.exports[name];
module.exports[name] = value;
return value;
}
module.exports = {
Parser: Parser,
Tokenizer: require("./Tokenizer.js"),
ElementType: require("domelementtype"),
DomHandler: DomHandler,
get FeedHandler() {
return defineProp("FeedHandler", require("./FeedHandler.js"));
},
get Stream() {
return defineProp("Stream", require("./Stream.js"));
},
get WritableStream() {
return defineProp("WritableStream", require("./WritableStream.js"));
},
get ProxyHandler() {
return defineProp("ProxyHandler", require("./ProxyHandler.js"));
},
get DomUtils() {
return defineProp("DomUtils", require("domutils"));
},
get CollectingHandler() {
return defineProp("CollectingHandler", require("./CollectingHandler.js"));
},
// For legacy support
DefaultHandler: DomHandler,
get RssHandler() {
return defineProp("RssHandler", this.FeedHandler);
},
//helper methods
parseDOM: function (data, options) {
var handler = new DomHandler(options);
new Parser(handler, options).end(data);
return handler.dom;
},
parseFeed: function (feed, options) {
var handler = new module.exports.FeedHandler(options);
new Parser(handler, options).end(feed);
return handler.dom;
},
createDomStream: function (cb, options, elementCb) {
var handler = new DomHandler(cb, options, elementCb);
return new Parser(handler, options);
},
// List of all events that the parser emits
EVENTS: { /* Format: eventname: number of arguments */
attribute: 2,
cdatastart: 0,
cdataend: 0,
text: 1,
processinginstruction: 2,
comment: 1,
commentend: 0,
closetag: 1,
opentag: 2,
opentagname: 1,
error: 1,
end: 0
}
};
+11
View File
@@ -0,0 +1,11 @@
License
(The MIT License)
Copyright (c) 2014 The cheeriojs contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
Renders a DOM node or an array of DOM nodes to a string.
@@ -0,0 +1,102 @@
{
"elementNames" : {
"altglyph" : "altGlyph",
"altglyphdef" : "altGlyphDef",
"altglyphitem" : "altGlyphItem",
"animatecolor" : "animateColor",
"animatemotion" : "animateMotion",
"animatetransform" : "animateTransform",
"clippath" : "clipPath",
"feblend" : "feBlend",
"fecolormatrix" : "feColorMatrix",
"fecomponenttransfer" : "feComponentTransfer",
"fecomposite" : "feComposite",
"feconvolvematrix" : "feConvolveMatrix",
"fediffuselighting" : "feDiffuseLighting",
"fedisplacementmap" : "feDisplacementMap",
"fedistantlight" : "feDistantLight",
"fedropshadow" : "feDropShadow",
"feflood" : "feFlood",
"fefunca" : "feFuncA",
"fefuncb" : "feFuncB",
"fefuncg" : "feFuncG",
"fefuncr" : "feFuncR",
"fegaussianblur" : "feGaussianBlur",
"feimage" : "feImage",
"femerge" : "feMerge",
"femergenode" : "feMergeNode",
"femorphology" : "feMorphology",
"feoffset" : "feOffset",
"fepointlight" : "fePointLight",
"fespecularlighting" : "feSpecularLighting",
"fespotlight" : "feSpotLight",
"fetile" : "feTile",
"feturbulence" : "feTurbulence",
"foreignobject" : "foreignObject",
"glyphref" : "glyphRef",
"lineargradient" : "linearGradient",
"radialgradient" : "radialGradient",
"textpath" : "textPath"
},
"attributeNames" : {
"definitionurl" : "definitionURL",
"attributename" : "attributeName",
"attributetype" : "attributeType",
"basefrequency" : "baseFrequency",
"baseprofile" : "baseProfile",
"calcmode" : "calcMode",
"clippathunits" : "clipPathUnits",
"diffuseconstant" : "diffuseConstant",
"edgemode" : "edgeMode",
"filterunits" : "filterUnits",
"glyphref" : "glyphRef",
"gradienttransform" : "gradientTransform",
"gradientunits" : "gradientUnits",
"kernelmatrix" : "kernelMatrix",
"kernelunitlength" : "kernelUnitLength",
"keypoints" : "keyPoints",
"keysplines" : "keySplines",
"keytimes" : "keyTimes",
"lengthadjust" : "lengthAdjust",
"limitingconeangle" : "limitingConeAngle",
"markerheight" : "markerHeight",
"markerunits" : "markerUnits",
"markerwidth" : "markerWidth",
"maskcontentunits" : "maskContentUnits",
"maskunits" : "maskUnits",
"numoctaves" : "numOctaves",
"pathlength" : "pathLength",
"patterncontentunits" : "patternContentUnits",
"patterntransform" : "patternTransform",
"patternunits" : "patternUnits",
"pointsatx" : "pointsAtX",
"pointsaty" : "pointsAtY",
"pointsatz" : "pointsAtZ",
"preservealpha" : "preserveAlpha",
"preserveaspectratio" : "preserveAspectRatio",
"primitiveunits" : "primitiveUnits",
"refx" : "refX",
"refy" : "refY",
"repeatcount" : "repeatCount",
"repeatdur" : "repeatDur",
"requiredextensions" : "requiredExtensions",
"requiredfeatures" : "requiredFeatures",
"specularconstant" : "specularConstant",
"specularexponent" : "specularExponent",
"spreadmethod" : "spreadMethod",
"startoffset" : "startOffset",
"stddeviation" : "stdDeviation",
"stitchtiles" : "stitchTiles",
"surfacescale" : "surfaceScale",
"systemlanguage" : "systemLanguage",
"tablevalues" : "tableValues",
"targetx" : "targetX",
"targety" : "targetY",
"textlength" : "textLength",
"viewbox" : "viewBox",
"viewtarget" : "viewTarget",
"xchannelselector" : "xChannelSelector",
"ychannelselector" : "yChannelSelector",
"zoomandpan" : "zoomAndPan"
}
}
@@ -0,0 +1,17 @@
export interface DomSerializerOptions {
xmlMode?: boolean | 'foreign';
decodeEntities?: boolean;
}
/**
* Renders a DOM node or an array of DOM nodes to a string.
*
* Can be thought of as the equivalent of the `outerHTML` of the passed node(s).
*
* @param nodes Nodes to be rendered.
* @param options Changes serialization behavior
*/
export default function render(
nodes: {} | {}[],
options?: DomSerializerOptions
): string;
+183
View File
@@ -0,0 +1,183 @@
/*
Module dependencies
*/
var ElementType = require('domelementtype');
var entities = require('entities');
/* mixed-case SVG and MathML tags & attributes
recognized by the HTML parser, see
https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inforeign
*/
var foreignNames = require('./foreignNames.json');
foreignNames.elementNames.__proto__ = null; /* use as a simple dictionary */
foreignNames.attributeNames.__proto__ = null;
var unencodedElements = {
__proto__: null,
style: true,
script: true,
xmp: true,
iframe: true,
noembed: true,
noframes: true,
plaintext: true,
noscript: true
};
/*
Format attributes
*/
function formatAttrs(attributes, opts) {
if (!attributes) return;
var output = '';
var value;
// Loop through the attributes
for (var key in attributes) {
value = attributes[key];
if (output) {
output += ' ';
}
if (opts.xmlMode === 'foreign') {
/* fix up mixed-case attribute names */
key = foreignNames.attributeNames[key] || key;
}
output += key;
if ((value !== null && value !== '') || opts.xmlMode) {
output +=
'="' +
(opts.decodeEntities
? entities.encodeXML(value)
: value.replace(/\"/g, '&quot;')) +
'"';
}
}
return output;
}
/*
Self-enclosing tags (stolen from node-htmlparser)
*/
var singleTag = {
__proto__: null,
area: true,
base: true,
basefont: true,
br: true,
col: true,
command: true,
embed: true,
frame: true,
hr: true,
img: true,
input: true,
isindex: true,
keygen: true,
link: true,
meta: true,
param: true,
source: true,
track: true,
wbr: true
};
var render = (module.exports = function(dom, opts) {
if (!Array.isArray(dom) && !dom.cheerio) dom = [dom];
opts = opts || {};
var output = '';
for (var i = 0; i < dom.length; i++) {
var elem = dom[i];
if (elem.type === 'root') output += render(elem.children, opts);
else if (ElementType.isTag(elem)) output += renderTag(elem, opts);
else if (elem.type === ElementType.Directive)
output += renderDirective(elem);
else if (elem.type === ElementType.Comment) output += renderComment(elem);
else if (elem.type === ElementType.CDATA) output += renderCdata(elem);
else output += renderText(elem, opts);
}
return output;
});
var foreignModeIntegrationPoints = [
'mi',
'mo',
'mn',
'ms',
'mtext',
'annotation-xml',
'foreignObject',
'desc',
'title'
];
function renderTag(elem, opts) {
// Handle SVG / MathML in HTML
if (opts.xmlMode === 'foreign') {
/* fix up mixed-case element names */
elem.name = foreignNames.elementNames[elem.name] || elem.name;
/* exit foreign mode at integration points */
if (
elem.parent &&
foreignModeIntegrationPoints.indexOf(elem.parent.name) >= 0
)
opts = Object.assign({}, opts, { xmlMode: false });
}
if (!opts.xmlMode && ['svg', 'math'].indexOf(elem.name) >= 0) {
opts = Object.assign({}, opts, { xmlMode: 'foreign' });
}
var tag = '<' + elem.name;
var attribs = formatAttrs(elem.attribs, opts);
if (attribs) {
tag += ' ' + attribs;
}
if (opts.xmlMode && (!elem.children || elem.children.length === 0)) {
tag += '/>';
} else {
tag += '>';
if (elem.children) {
tag += render(elem.children, opts);
}
if (!singleTag[elem.name] || opts.xmlMode) {
tag += '</' + elem.name + '>';
}
}
return tag;
}
function renderDirective(elem) {
return '<' + elem.data + '>';
}
function renderText(elem, opts) {
var data = elem.data || '';
// if entities weren't decoded, no need to encode them back
if (
opts.decodeEntities &&
!(elem.parent && elem.parent.name in unencodedElements)
) {
data = entities.encodeXML(data);
}
return data;
}
function renderCdata(elem) {
return '<![CDATA[' + elem.children[0].data + ']]>';
}
function renderComment(elem) {
return '<!--' + elem.data + '-->';
}
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,48 @@
/** Types of elements found in htmlparser2's DOM */
export declare enum ElementType {
/** Type for the root element of a document */
Root = "root",
/** Type for Text */
Text = "text",
/** Type for <? ... ?> */
Directive = "directive",
/** Type for <!-- ... --> */
Comment = "comment",
/** Type for <script> tags */
Script = "script",
/** Type for <style> tags */
Style = "style",
/** Type for Any tag */
Tag = "tag",
/** Type for <![CDATA[ ... ]]> */
CDATA = "cdata",
/** Type for <!doctype ...> */
Doctype = "doctype"
}
/**
* Tests whether an element is a tag or not.
*
* @param elem Element to test
*/
export declare function isTag(elem: {
type: ElementType;
}): boolean;
/** Type for the root element of a document */
export declare const Root = ElementType.Root;
/** Type for Text */
export declare const Text = ElementType.Text;
/** Type for <? ... ?> */
export declare const Directive = ElementType.Directive;
/** Type for <!-- ... --> */
export declare const Comment = ElementType.Comment;
/** Type for <script> tags */
export declare const Script = ElementType.Script;
/** Type for <style> tags */
export declare const Style = ElementType.Style;
/** Type for Any tag */
export declare const Tag = ElementType.Tag;
/** Type for <![CDATA[ ... ]]> */
export declare const CDATA = ElementType.CDATA;
/** Type for <!doctype ...> */
export declare const Doctype = ElementType.Doctype;
//# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,oBAAY,WAAW;IACnB,8CAA8C;IAC9C,IAAI,SAAS;IACb,oBAAoB;IACpB,IAAI,SAAS;IACb,yBAAyB;IACzB,SAAS,cAAc;IACvB,4BAA4B;IAC5B,OAAO,YAAY;IACnB,6BAA6B;IAC7B,MAAM,WAAW;IACjB,4BAA4B;IAC5B,KAAK,UAAU;IACf,uBAAuB;IACvB,GAAG,QAAQ;IACX,iCAAiC;IACjC,KAAK,UAAU;IACf,8BAA8B;IAC9B,OAAO,YAAY;CACtB;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAM1D;AAGD,8CAA8C;AAC9C,eAAO,MAAM,IAAI,mBAAmB,CAAC;AACrC,oBAAoB;AACpB,eAAO,MAAM,IAAI,mBAAmB,CAAC;AACrC,yBAAyB;AACzB,eAAO,MAAM,SAAS,wBAAwB,CAAC;AAC/C,4BAA4B;AAC5B,eAAO,MAAM,OAAO,sBAAsB,CAAC;AAC3C,6BAA6B;AAC7B,eAAO,MAAM,MAAM,qBAAqB,CAAC;AACzC,4BAA4B;AAC5B,eAAO,MAAM,KAAK,oBAAoB,CAAC;AACvC,uBAAuB;AACvB,eAAO,MAAM,GAAG,kBAAkB,CAAC;AACnC,iCAAiC;AACjC,eAAO,MAAM,KAAK,oBAAoB,CAAC;AACvC,8BAA8B;AAC9B,eAAO,MAAM,OAAO,sBAAsB,CAAC"}
@@ -0,0 +1,51 @@
/** Types of elements found in htmlparser2's DOM */
export var ElementType;
(function (ElementType) {
/** Type for the root element of a document */
ElementType["Root"] = "root";
/** Type for Text */
ElementType["Text"] = "text";
/** Type for <? ... ?> */
ElementType["Directive"] = "directive";
/** Type for <!-- ... --> */
ElementType["Comment"] = "comment";
/** Type for <script> tags */
ElementType["Script"] = "script";
/** Type for <style> tags */
ElementType["Style"] = "style";
/** Type for Any tag */
ElementType["Tag"] = "tag";
/** Type for <![CDATA[ ... ]]> */
ElementType["CDATA"] = "cdata";
/** Type for <!doctype ...> */
ElementType["Doctype"] = "doctype";
})(ElementType || (ElementType = {}));
/**
* Tests whether an element is a tag or not.
*
* @param elem Element to test
*/
export function isTag(elem) {
return (elem.type === ElementType.Tag ||
elem.type === ElementType.Script ||
elem.type === ElementType.Style);
}
// Exports for backwards compatibility
/** Type for the root element of a document */
export const Root = ElementType.Root;
/** Type for Text */
export const Text = ElementType.Text;
/** Type for <? ... ?> */
export const Directive = ElementType.Directive;
/** Type for <!-- ... --> */
export const Comment = ElementType.Comment;
/** Type for <script> tags */
export const Script = ElementType.Script;
/** Type for <style> tags */
export const Style = ElementType.Style;
/** Type for Any tag */
export const Tag = ElementType.Tag;
/** Type for <![CDATA[ ... ]]> */
export const CDATA = ElementType.CDATA;
/** Type for <!doctype ...> */
export const Doctype = ElementType.Doctype;
@@ -0,0 +1 @@
{"type":"module"}
@@ -0,0 +1,48 @@
/** Types of elements found in htmlparser2's DOM */
export declare enum ElementType {
/** Type for the root element of a document */
Root = "root",
/** Type for Text */
Text = "text",
/** Type for <? ... ?> */
Directive = "directive",
/** Type for <!-- ... --> */
Comment = "comment",
/** Type for <script> tags */
Script = "script",
/** Type for <style> tags */
Style = "style",
/** Type for Any tag */
Tag = "tag",
/** Type for <![CDATA[ ... ]]> */
CDATA = "cdata",
/** Type for <!doctype ...> */
Doctype = "doctype"
}
/**
* Tests whether an element is a tag or not.
*
* @param elem Element to test
*/
export declare function isTag(elem: {
type: ElementType;
}): boolean;
/** Type for the root element of a document */
export declare const Root = ElementType.Root;
/** Type for Text */
export declare const Text = ElementType.Text;
/** Type for <? ... ?> */
export declare const Directive = ElementType.Directive;
/** Type for <!-- ... --> */
export declare const Comment = ElementType.Comment;
/** Type for <script> tags */
export declare const Script = ElementType.Script;
/** Type for <style> tags */
export declare const Style = ElementType.Style;
/** Type for Any tag */
export declare const Tag = ElementType.Tag;
/** Type for <![CDATA[ ... ]]> */
export declare const CDATA = ElementType.CDATA;
/** Type for <!doctype ...> */
export declare const Doctype = ElementType.Doctype;
//# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,oBAAY,WAAW;IACnB,8CAA8C;IAC9C,IAAI,SAAS;IACb,oBAAoB;IACpB,IAAI,SAAS;IACb,yBAAyB;IACzB,SAAS,cAAc;IACvB,4BAA4B;IAC5B,OAAO,YAAY;IACnB,6BAA6B;IAC7B,MAAM,WAAW;IACjB,4BAA4B;IAC5B,KAAK,UAAU;IACf,uBAAuB;IACvB,GAAG,QAAQ;IACX,iCAAiC;IACjC,KAAK,UAAU;IACf,8BAA8B;IAC9B,OAAO,YAAY;CACtB;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAM1D;AAGD,8CAA8C;AAC9C,eAAO,MAAM,IAAI,mBAAmB,CAAC;AACrC,oBAAoB;AACpB,eAAO,MAAM,IAAI,mBAAmB,CAAC;AACrC,yBAAyB;AACzB,eAAO,MAAM,SAAS,wBAAwB,CAAC;AAC/C,4BAA4B;AAC5B,eAAO,MAAM,OAAO,sBAAsB,CAAC;AAC3C,6BAA6B;AAC7B,eAAO,MAAM,MAAM,qBAAqB,CAAC;AACzC,4BAA4B;AAC5B,eAAO,MAAM,KAAK,oBAAoB,CAAC;AACvC,uBAAuB;AACvB,eAAO,MAAM,GAAG,kBAAkB,CAAC;AACnC,iCAAiC;AACjC,eAAO,MAAM,KAAK,oBAAoB,CAAC;AACvC,8BAA8B;AAC9B,eAAO,MAAM,OAAO,sBAAsB,CAAC"}
@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Doctype = exports.CDATA = exports.Tag = exports.Style = exports.Script = exports.Comment = exports.Directive = exports.Text = exports.Root = exports.isTag = exports.ElementType = void 0;
/** Types of elements found in htmlparser2's DOM */
var ElementType;
(function (ElementType) {
/** Type for the root element of a document */
ElementType["Root"] = "root";
/** Type for Text */
ElementType["Text"] = "text";
/** Type for <? ... ?> */
ElementType["Directive"] = "directive";
/** Type for <!-- ... --> */
ElementType["Comment"] = "comment";
/** Type for <script> tags */
ElementType["Script"] = "script";
/** Type for <style> tags */
ElementType["Style"] = "style";
/** Type for Any tag */
ElementType["Tag"] = "tag";
/** Type for <![CDATA[ ... ]]> */
ElementType["CDATA"] = "cdata";
/** Type for <!doctype ...> */
ElementType["Doctype"] = "doctype";
})(ElementType = exports.ElementType || (exports.ElementType = {}));
/**
* Tests whether an element is a tag or not.
*
* @param elem Element to test
*/
function isTag(elem) {
return (elem.type === ElementType.Tag ||
elem.type === ElementType.Script ||
elem.type === ElementType.Style);
}
exports.isTag = isTag;
// Exports for backwards compatibility
/** Type for the root element of a document */
exports.Root = ElementType.Root;
/** Type for Text */
exports.Text = ElementType.Text;
/** Type for <? ... ?> */
exports.Directive = ElementType.Directive;
/** Type for <!-- ... --> */
exports.Comment = ElementType.Comment;
/** Type for <script> tags */
exports.Script = ElementType.Script;
/** Type for <style> tags */
exports.Style = ElementType.Style;
/** Type for Any tag */
exports.Tag = ElementType.Tag;
/** Type for <![CDATA[ ... ]]> */
exports.CDATA = ElementType.CDATA;
/** Type for <!doctype ...> */
exports.Doctype = ElementType.Doctype;
@@ -0,0 +1,54 @@
{
"name": "domelementtype",
"version": "2.3.0",
"description": "all the types of nodes in htmlparser2's dom",
"author": "Felix Boehm <me@feedic.com>",
"license": "BSD-2-Clause",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"sideEffects": false,
"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/**/*"
],
"repository": {
"type": "git",
"url": "git://github.com/fb55/domelementtype.git"
},
"keywords": [
"dom",
"element",
"types",
"htmlparser2"
],
"scripts": {
"test": "npm run lint && prettier --check **/*.{ts,json,md}",
"lint": "eslint src",
"format": "prettier --write **/*.{ts,json,md}",
"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"
},
"prettier": {
"tabWidth": 4
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"eslint": "^8.12.0",
"eslint-config-prettier": "^8.5.0",
"prettier": "^2.6.2",
"typescript": "^4.6.3"
}
}
@@ -0,0 +1 @@
All the types of nodes in htmlparser2's DOM.
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,5 @@
export declare const decodeXML: (str: string) => string;
export declare const decodeHTMLStrict: (str: string) => string;
export declare type MapType = Record<string, string>;
export declare const decodeHTML: (str: string) => string;
//# sourceMappingURL=decode.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../src/decode.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,SAAS,QAOL,MAAM,WAP0B,CAAC;AAClD,eAAO,MAAM,gBAAgB,QAMZ,MAAM,WANoC,CAAC;AAE5D,oBAAY,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAS7C,eAAO,MAAM,UAAU,QAyBN,MAAM,WACnB,CAAC"}
@@ -0,0 +1,53 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeHTML = exports.decodeHTMLStrict = exports.decodeXML = void 0;
var entities_json_1 = __importDefault(require("./maps/entities.json"));
var legacy_json_1 = __importDefault(require("./maps/legacy.json"));
var xml_json_1 = __importDefault(require("./maps/xml.json"));
var decode_codepoint_1 = __importDefault(require("./decode_codepoint"));
var strictEntityRe = /&(?:[a-zA-Z0-9]+|#[xX][\da-fA-F]+|#\d+);/g;
exports.decodeXML = getStrictDecoder(xml_json_1.default);
exports.decodeHTMLStrict = getStrictDecoder(entities_json_1.default);
function getStrictDecoder(map) {
var replace = getReplacer(map);
return function (str) { return String(str).replace(strictEntityRe, replace); };
}
var sorter = function (a, b) { return (a < b ? 1 : -1); };
exports.decodeHTML = (function () {
var legacy = Object.keys(legacy_json_1.default).sort(sorter);
var keys = Object.keys(entities_json_1.default).sort(sorter);
for (var i = 0, j = 0; i < keys.length; i++) {
if (legacy[j] === keys[i]) {
keys[i] += ";?";
j++;
}
else {
keys[i] += ";";
}
}
var re = new RegExp("&(?:" + keys.join("|") + "|#[xX][\\da-fA-F]+;?|#\\d+;?)", "g");
var replace = getReplacer(entities_json_1.default);
function replacer(str) {
if (str.substr(-1) !== ";")
str += ";";
return replace(str);
}
// TODO consider creating a merged map
return function (str) { return String(str).replace(re, replacer); };
})();
function getReplacer(map) {
return function replace(str) {
if (str.charAt(1) === "#") {
var secondChar = str.charAt(2);
if (secondChar === "X" || secondChar === "x") {
return decode_codepoint_1.default(parseInt(str.substr(3), 16));
}
return decode_codepoint_1.default(parseInt(str.substr(2), 10));
}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return map[str.slice(1, -1)] || str;
};
}
@@ -0,0 +1,2 @@
export default function decodeCodePoint(codePoint: number): string;
//# sourceMappingURL=decode_codepoint.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"decode_codepoint.d.ts","sourceRoot":"","sources":["../src/decode_codepoint.ts"],"names":[],"mappings":"AAsBA,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUjE"}
@@ -0,0 +1,30 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var decode_json_1 = __importDefault(require("./maps/decode.json"));
// Adapted from https://github.com/mathiasbynens/he/blob/master/src/he.js#L94-L119
var fromCodePoint =
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
String.fromCodePoint ||
function (codePoint) {
var output = "";
if (codePoint > 0xffff) {
codePoint -= 0x10000;
output += String.fromCharCode(((codePoint >>> 10) & 0x3ff) | 0xd800);
codePoint = 0xdc00 | (codePoint & 0x3ff);
}
output += String.fromCharCode(codePoint);
return output;
};
function decodeCodePoint(codePoint) {
if ((codePoint >= 0xd800 && codePoint <= 0xdfff) || codePoint > 0x10ffff) {
return "\uFFFD";
}
if (codePoint in decode_json_1.default) {
codePoint = decode_json_1.default[codePoint];
}
return fromCodePoint(codePoint);
}
exports.default = decodeCodePoint;
@@ -0,0 +1,47 @@
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using XML entities.
*
* If a character has no equivalent entity, a
* numeric hexadecimal reference (eg. `&#xfc;`) will be used.
*/
export declare const encodeXML: (data: string) => string;
/**
* Encodes all entities and non-ASCII characters in the input.
*
* This includes characters that are valid ASCII characters in HTML documents.
* For example `#` will be encoded as `&num;`. To get a more compact output,
* consider using the `encodeNonAsciiHTML` function.
*
* If a character has no equivalent entity, a
* numeric hexadecimal reference (eg. `&#xfc;`) will be used.
*/
export declare const encodeHTML: (data: string) => string;
/**
* Encodes all non-ASCII characters, as well as characters not valid in HTML
* documents using HTML entities.
*
* If a character has no equivalent entity, a
* numeric hexadecimal reference (eg. `&#xfc;`) will be used.
*/
export declare const encodeNonAsciiHTML: (data: string) => string;
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using numeric hexadecimal reference (eg. `&#xfc;`).
*
* Have a look at `escapeUTF8` if you want a more concise output at the expense
* of reduced transportability.
*
* @param data String to escape.
*/
export declare function escape(data: string): string;
/**
* Encodes all characters not valid in XML documents using numeric hexadecimal
* reference (eg. `&#xfc;`).
*
* Note that the output will be character-set dependent.
*
* @param data String to escape.
*/
export declare function escapeUTF8(data: string): string;
//# sourceMappingURL=encode.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../src/encode.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,SAwIJ,MAAM,WAxI4B,CAAC;AAOrD;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,SAkFL,MAAM,WAlFuC,CAAC;AAChE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,SA+Gb,MAAM,WA/GsC,CAAC;AAqF/D;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C"}
@@ -0,0 +1,136 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.escapeUTF8 = exports.escape = exports.encodeNonAsciiHTML = exports.encodeHTML = exports.encodeXML = void 0;
var xml_json_1 = __importDefault(require("./maps/xml.json"));
var inverseXML = getInverseObj(xml_json_1.default);
var xmlReplacer = getInverseReplacer(inverseXML);
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using XML entities.
*
* If a character has no equivalent entity, a
* numeric hexadecimal reference (eg. `&#xfc;`) will be used.
*/
exports.encodeXML = getASCIIEncoder(inverseXML);
var entities_json_1 = __importDefault(require("./maps/entities.json"));
var inverseHTML = getInverseObj(entities_json_1.default);
var htmlReplacer = getInverseReplacer(inverseHTML);
/**
* Encodes all entities and non-ASCII characters in the input.
*
* This includes characters that are valid ASCII characters in HTML documents.
* For example `#` will be encoded as `&num;`. To get a more compact output,
* consider using the `encodeNonAsciiHTML` function.
*
* If a character has no equivalent entity, a
* numeric hexadecimal reference (eg. `&#xfc;`) will be used.
*/
exports.encodeHTML = getInverse(inverseHTML, htmlReplacer);
/**
* Encodes all non-ASCII characters, as well as characters not valid in HTML
* documents using HTML entities.
*
* If a character has no equivalent entity, a
* numeric hexadecimal reference (eg. `&#xfc;`) will be used.
*/
exports.encodeNonAsciiHTML = getASCIIEncoder(inverseHTML);
function getInverseObj(obj) {
return Object.keys(obj)
.sort()
.reduce(function (inverse, name) {
inverse[obj[name]] = "&" + name + ";";
return inverse;
}, {});
}
function getInverseReplacer(inverse) {
var single = [];
var multiple = [];
for (var _i = 0, _a = Object.keys(inverse); _i < _a.length; _i++) {
var k = _a[_i];
if (k.length === 1) {
// Add value to single array
single.push("\\" + k);
}
else {
// Add value to multiple array
multiple.push(k);
}
}
// Add ranges to single characters.
single.sort();
for (var start = 0; start < single.length - 1; start++) {
// Find the end of a run of characters
var end = start;
while (end < single.length - 1 &&
single[end].charCodeAt(1) + 1 === single[end + 1].charCodeAt(1)) {
end += 1;
}
var count = 1 + end - start;
// We want to replace at least three characters
if (count < 3)
continue;
single.splice(start, count, single[start] + "-" + single[end]);
}
multiple.unshift("[" + single.join("") + "]");
return new RegExp(multiple.join("|"), "g");
}
// /[^\0-\x7F]/gu
var reNonASCII = /(?:[\x80-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g;
var getCodePoint =
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
String.prototype.codePointAt != null
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
function (str) { return str.codePointAt(0); }
: // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
function (c) {
return (c.charCodeAt(0) - 0xd800) * 0x400 +
c.charCodeAt(1) -
0xdc00 +
0x10000;
};
function singleCharReplacer(c) {
return "&#x" + (c.length > 1 ? getCodePoint(c) : c.charCodeAt(0))
.toString(16)
.toUpperCase() + ";";
}
function getInverse(inverse, re) {
return function (data) {
return data
.replace(re, function (name) { return inverse[name]; })
.replace(reNonASCII, singleCharReplacer);
};
}
var reEscapeChars = new RegExp(xmlReplacer.source + "|" + reNonASCII.source, "g");
/**
* Encodes all non-ASCII characters, as well as characters not valid in XML
* documents using numeric hexadecimal reference (eg. `&#xfc;`).
*
* Have a look at `escapeUTF8` if you want a more concise output at the expense
* of reduced transportability.
*
* @param data String to escape.
*/
function escape(data) {
return data.replace(reEscapeChars, singleCharReplacer);
}
exports.escape = escape;
/**
* Encodes all characters not valid in XML documents using numeric hexadecimal
* reference (eg. `&#xfc;`).
*
* Note that the output will be character-set dependent.
*
* @param data String to escape.
*/
function escapeUTF8(data) {
return data.replace(xmlReplacer, singleCharReplacer);
}
exports.escapeUTF8 = escapeUTF8;
function getASCIIEncoder(obj) {
return function (data) {
return data.replace(reEscapeChars, function (c) { return obj[c] || singleCharReplacer(c); });
};
}
@@ -0,0 +1,27 @@
/**
* Decodes a string with entities.
*
* @param data String to decode.
* @param level Optional level to decode at. 0 = XML, 1 = HTML. Default is 0.
* @deprecated Use `decodeXML` or `decodeHTML` directly.
*/
export declare function decode(data: string, level?: number): string;
/**
* Decodes a string with entities. Does not allow missing trailing semicolons for entities.
*
* @param data String to decode.
* @param level Optional level to decode at. 0 = XML, 1 = HTML. Default is 0.
* @deprecated Use `decodeHTMLStrict` or `decodeXML` directly.
*/
export declare function decodeStrict(data: string, level?: number): string;
/**
* Encodes a string with entities.
*
* @param data String to encode.
* @param level Optional level to encode at. 0 = XML, 1 = HTML. Default is 0.
* @deprecated Use `encodeHTML`, `encodeXML` or `encodeNonAsciiHTML` directly.
*/
export declare function encode(data: string, level?: number): string;
export { encodeXML, encodeHTML, encodeNonAsciiHTML, escape, escapeUTF8, encodeHTML as encodeHTML4, encodeHTML as encodeHTML5, } from "./encode";
export { decodeXML, decodeHTML, decodeHTMLStrict, decodeHTML as decodeHTML4, decodeHTML as decodeHTML5, decodeHTMLStrict as decodeHTML4Strict, decodeHTMLStrict as decodeHTML5Strict, decodeXML as decodeXMLStrict, } from "./decode";
//# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,OAAO,EACH,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,MAAM,EACN,UAAU,EAEV,UAAU,IAAI,WAAW,EACzB,UAAU,IAAI,WAAW,GAC5B,MAAM,UAAU,CAAC;AAElB,OAAO,EACH,SAAS,EACT,UAAU,EACV,gBAAgB,EAEhB,UAAU,IAAI,WAAW,EACzB,UAAU,IAAI,WAAW,EACzB,gBAAgB,IAAI,iBAAiB,EACrC,gBAAgB,IAAI,iBAAiB,EACrC,SAAS,IAAI,eAAe,GAC/B,MAAM,UAAU,CAAC"}
@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeXMLStrict = exports.decodeHTML5Strict = exports.decodeHTML4Strict = exports.decodeHTML5 = exports.decodeHTML4 = exports.decodeHTMLStrict = exports.decodeHTML = exports.decodeXML = exports.encodeHTML5 = exports.encodeHTML4 = exports.escapeUTF8 = exports.escape = exports.encodeNonAsciiHTML = exports.encodeHTML = exports.encodeXML = exports.encode = exports.decodeStrict = exports.decode = void 0;
var decode_1 = require("./decode");
var encode_1 = require("./encode");
/**
* Decodes a string with entities.
*
* @param data String to decode.
* @param level Optional level to decode at. 0 = XML, 1 = HTML. Default is 0.
* @deprecated Use `decodeXML` or `decodeHTML` directly.
*/
function decode(data, level) {
return (!level || level <= 0 ? decode_1.decodeXML : decode_1.decodeHTML)(data);
}
exports.decode = decode;
/**
* Decodes a string with entities. Does not allow missing trailing semicolons for entities.
*
* @param data String to decode.
* @param level Optional level to decode at. 0 = XML, 1 = HTML. Default is 0.
* @deprecated Use `decodeHTMLStrict` or `decodeXML` directly.
*/
function decodeStrict(data, level) {
return (!level || level <= 0 ? decode_1.decodeXML : decode_1.decodeHTMLStrict)(data);
}
exports.decodeStrict = decodeStrict;
/**
* Encodes a string with entities.
*
* @param data String to encode.
* @param level Optional level to encode at. 0 = XML, 1 = HTML. Default is 0.
* @deprecated Use `encodeHTML`, `encodeXML` or `encodeNonAsciiHTML` directly.
*/
function encode(data, level) {
return (!level || level <= 0 ? encode_1.encodeXML : encode_1.encodeHTML)(data);
}
exports.encode = encode;
var encode_2 = require("./encode");
Object.defineProperty(exports, "encodeXML", { enumerable: true, get: function () { return encode_2.encodeXML; } });
Object.defineProperty(exports, "encodeHTML", { enumerable: true, get: function () { return encode_2.encodeHTML; } });
Object.defineProperty(exports, "encodeNonAsciiHTML", { enumerable: true, get: function () { return encode_2.encodeNonAsciiHTML; } });
Object.defineProperty(exports, "escape", { enumerable: true, get: function () { return encode_2.escape; } });
Object.defineProperty(exports, "escapeUTF8", { enumerable: true, get: function () { return encode_2.escapeUTF8; } });
// Legacy aliases (deprecated)
Object.defineProperty(exports, "encodeHTML4", { enumerable: true, get: function () { return encode_2.encodeHTML; } });
Object.defineProperty(exports, "encodeHTML5", { enumerable: true, get: function () { return encode_2.encodeHTML; } });
var decode_2 = require("./decode");
Object.defineProperty(exports, "decodeXML", { enumerable: true, get: function () { return decode_2.decodeXML; } });
Object.defineProperty(exports, "decodeHTML", { enumerable: true, get: function () { return decode_2.decodeHTML; } });
Object.defineProperty(exports, "decodeHTMLStrict", { enumerable: true, get: function () { return decode_2.decodeHTMLStrict; } });
// Legacy aliases (deprecated)
Object.defineProperty(exports, "decodeHTML4", { enumerable: true, get: function () { return decode_2.decodeHTML; } });
Object.defineProperty(exports, "decodeHTML5", { enumerable: true, get: function () { return decode_2.decodeHTML; } });
Object.defineProperty(exports, "decodeHTML4Strict", { enumerable: true, get: function () { return decode_2.decodeHTMLStrict; } });
Object.defineProperty(exports, "decodeHTML5Strict", { enumerable: true, get: function () { return decode_2.decodeHTMLStrict; } });
Object.defineProperty(exports, "decodeXMLStrict", { enumerable: true, get: function () { return decode_2.decodeXML; } });
@@ -0,0 +1 @@
{"0":65533,"128":8364,"130":8218,"131":402,"132":8222,"133":8230,"134":8224,"135":8225,"136":710,"137":8240,"138":352,"139":8249,"140":338,"142":381,"145":8216,"146":8217,"147":8220,"148":8221,"149":8226,"150":8211,"151":8212,"152":732,"153":8482,"154":353,"155":8250,"156":339,"158":382,"159":376}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"Aacute":"Á","aacute":"á","Acirc":"Â","acirc":"â","acute":"´","AElig":"Æ","aelig":"æ","Agrave":"À","agrave":"à","amp":"&","AMP":"&","Aring":"Å","aring":"å","Atilde":"Ã","atilde":"ã","Auml":"Ä","auml":"ä","brvbar":"¦","Ccedil":"Ç","ccedil":"ç","cedil":"¸","cent":"¢","copy":"©","COPY":"©","curren":"¤","deg":"°","divide":"÷","Eacute":"É","eacute":"é","Ecirc":"Ê","ecirc":"ê","Egrave":"È","egrave":"è","ETH":"Ð","eth":"ð","Euml":"Ë","euml":"ë","frac12":"½","frac14":"¼","frac34":"¾","gt":">","GT":">","Iacute":"Í","iacute":"í","Icirc":"Î","icirc":"î","iexcl":"¡","Igrave":"Ì","igrave":"ì","iquest":"¿","Iuml":"Ï","iuml":"ï","laquo":"«","lt":"<","LT":"<","macr":"¯","micro":"µ","middot":"·","nbsp":" ","not":"¬","Ntilde":"Ñ","ntilde":"ñ","Oacute":"Ó","oacute":"ó","Ocirc":"Ô","ocirc":"ô","Ograve":"Ò","ograve":"ò","ordf":"ª","ordm":"º","Oslash":"Ø","oslash":"ø","Otilde":"Õ","otilde":"õ","Ouml":"Ö","ouml":"ö","para":"¶","plusmn":"±","pound":"£","quot":"\"","QUOT":"\"","raquo":"»","reg":"®","REG":"®","sect":"§","shy":"­","sup1":"¹","sup2":"²","sup3":"³","szlig":"ß","THORN":"Þ","thorn":"þ","times":"×","Uacute":"Ú","uacute":"ú","Ucirc":"Û","ucirc":"û","Ugrave":"Ù","ugrave":"ù","uml":"¨","Uuml":"Ü","uuml":"ü","Yacute":"Ý","yacute":"ý","yen":"¥","yuml":"ÿ"}
@@ -0,0 +1 @@
{"amp":"&","apos":"'","gt":">","lt":"<","quot":"\""}
@@ -0,0 +1,64 @@
{
"name": "entities",
"version": "2.2.0",
"description": "Encode & decode XML and HTML entities with ease",
"author": "Felix Boehm <me@feedic.com>",
"funding": "https://github.com/fb55/entities?sponsor=1",
"sideEffects": false,
"keywords": [
"entity",
"decoding",
"encoding",
"html",
"xml",
"html entities"
],
"directories": {
"lib": "lib/"
},
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib/**/*"
],
"devDependencies": {
"@types/jest": "^26.0.0",
"@types/node": "^14.11.8",
"@typescript-eslint/eslint-plugin": "^4.4.1",
"@typescript-eslint/parser": "^4.4.1",
"coveralls": "*",
"eslint": "^7.11.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-node": "^11.1.0",
"jest": "^26.5.3",
"prettier": "^2.0.5",
"ts-jest": "^26.1.0",
"typescript": "^4.0.2"
},
"scripts": {
"test": "jest --coverage && npm run lint",
"coverage": "cat coverage/lcov.info | coveralls",
"lint": "npm run lint:es && npm run lint:prettier",
"lint:es": "eslint .",
"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}'",
"build": "tsc && cp -r src/maps lib",
"prepare": "npm run build"
},
"repository": {
"type": "git",
"url": "git://github.com/fb55/entities.git"
},
"license": "BSD-2-Clause",
"jest": {
"preset": "ts-jest",
"testEnvironment": "node"
},
"prettier": {
"tabWidth": 4,
"proseWrap": "always"
}
}
@@ -0,0 +1,57 @@
# entities [![NPM version](http://img.shields.io/npm/v/entities.svg)](https://npmjs.org/package/entities) [![Downloads](https://img.shields.io/npm/dm/entities.svg)](https://npmjs.org/package/entities) [![Build Status](http://img.shields.io/travis/fb55/entities.svg)](http://travis-ci.org/fb55/entities) [![Coverage](http://img.shields.io/coveralls/fb55/entities.svg)](https://coveralls.io/r/fb55/entities)
Encode & decode HTML & XML entities with ease & speed.
## How to…
### …install `entities`
npm install entities
### …use `entities`
```javascript
const entities = require("entities");
//encoding
entities.escape("&#38;"); // "&#x26;#38;"
entities.encodeXML("&#38;"); // "&amp;#38;"
entities.encodeHTML("&#38;"); // "&amp;&num;38&semi;"
//decoding
entities.decodeXML("asdf &amp; &#xFF; &#xFC; &apos;"); // "asdf & ÿ ü '"
entities.decodeHTML("asdf &amp; &yuml; &uuml; &apos;"); // "asdf & ÿ ü '"
```
## Performance
This is how `entities` compares to other libraries on a very basic benchmark
(see `scripts/benchmark.ts`, for 10,000,000 iterations):
| Library | `decode` performance | `encode` performance | Bundle size |
| -------------- | -------------------- | -------------------- | -------------------------------------------------------------------------- |
| entities | 10.809s | 17.683s | ![npm bundle size](https://img.shields.io/bundlephobia/min/entities) |
| html-entities | 14.029s | 22.670s | ![npm bundle size](https://img.shields.io/bundlephobia/min/html-entities) |
| he | 16.163s | 44.010s | ![npm bundle size](https://img.shields.io/bundlephobia/min/he) |
| parse-entities | 28.507s | N/A | ![npm bundle size](https://img.shields.io/bundlephobia/min/parse-entities) |
---
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.
## `entities` for enterprise
Available as part of the Tidelift Subscription
The maintainers of `entities` 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-entities?utm_source=npm-entities&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
@@ -0,0 +1,40 @@
{
"name": "dom-serializer",
"version": "0.2.2",
"description": "render dom nodes to string",
"author": "Felix Boehm <me@feedic.com>",
"keywords": [
"html",
"xml",
"render"
],
"repository": {
"type": "git",
"url": "git://github.com/cheeriojs/dom-renderer.git"
},
"main": "./index.js",
"files": [
"index.js",
"index.d.ts",
"foreignNames.json"
],
"dependencies": {
"domelementtype": "^2.0.1",
"entities": "^2.0.0"
},
"devDependencies": {
"cheerio": "^1.0.0-rc.2",
"expect.js": "~0.3.1",
"htmlparser2": "^3.10.0",
"lodash": "^4.17.11",
"mocha": "^6.2.0",
"xyz": "^3.0.0"
},
"scripts": {
"test": "mocha test.js"
},
"prettier": {
"singleQuote": true
},
"license": "MIT"
}
+11
View File
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+15
View File
@@ -0,0 +1,15 @@
//Types of elements found in the DOM
module.exports = {
Text: "text", //Text
Directive: "directive", //<? ... ?>
Comment: "comment", //<!-- ... -->
Script: "script", //<script> tags
Style: "style", //<style> tags
Tag: "tag", //Any tag
CDATA: "cdata", //<![CDATA[ ... ]]>
Doctype: "doctype",
isTag: function(elem){
return elem.type === "tag" || elem.type === "script" || elem.type === "style";
}
};
@@ -0,0 +1,16 @@
{
"name": "domelementtype",
"version": "1.3.1",
"description": "all the types of nodes in htmlparser2's dom",
"main": "index.js",
"repository": {
"type": "git",
"url": "git://github.com/fb55/domelementtype.git"
},
"keywords": [
"dom",
"htmlparser2"
],
"author": "Felix Boehm <me@feedic.com>",
"license": "BSD-2-Clause"
}
@@ -0,0 +1 @@
all the types of nodes in htmlparser2's dom
+5
View File
@@ -0,0 +1,5 @@
sudo: true
language: node_js
node_js:
- 8
+11
View File
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+14
View File
@@ -0,0 +1,14 @@
var DomUtils = module.exports;
[
require("./lib/stringify"),
require("./lib/traversal"),
require("./lib/manipulation"),
require("./lib/querying"),
require("./lib/legacy"),
require("./lib/helpers")
].forEach(function(ext){
Object.keys(ext).forEach(function(key){
DomUtils[key] = ext[key].bind(DomUtils);
});
});
+141
View File
@@ -0,0 +1,141 @@
// removeSubsets
// Given an array of nodes, remove any member that is contained by another.
exports.removeSubsets = function(nodes) {
var idx = nodes.length, node, ancestor, replace;
// Check if each node (or one of its ancestors) is already contained in the
// array.
while (--idx > -1) {
node = ancestor = nodes[idx];
// Temporarily remove the node under consideration
nodes[idx] = null;
replace = true;
while (ancestor) {
if (nodes.indexOf(ancestor) > -1) {
replace = false;
nodes.splice(idx, 1);
break;
}
ancestor = ancestor.parent;
}
// If the node has been found to be unique, re-insert it.
if (replace) {
nodes[idx] = node;
}
}
return nodes;
};
// Source: http://dom.spec.whatwg.org/#dom-node-comparedocumentposition
var POSITION = {
DISCONNECTED: 1,
PRECEDING: 2,
FOLLOWING: 4,
CONTAINS: 8,
CONTAINED_BY: 16
};
// Compare the position of one node against another node in any other document.
// The return value is a bitmask with the following values:
//
// document order:
// > There is an ordering, document order, defined on all the nodes in the
// > document corresponding to the order in which the first character of the
// > XML representation of each node occurs in the XML representation of the
// > document after expansion of general entities. Thus, the document element
// > node will be the first node. Element nodes occur before their children.
// > Thus, document order orders element nodes in order of the occurrence of
// > their start-tag in the XML (after expansion of entities). The attribute
// > nodes of an element occur after the element and before its children. The
// > relative order of attribute nodes is implementation-dependent./
// Source:
// http://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-document-order
//
// @argument {Node} nodaA The first node to use in the comparison
// @argument {Node} nodeB The second node to use in the comparison
//
// @return {Number} A bitmask describing the input nodes' relative position.
// See http://dom.spec.whatwg.org/#dom-node-comparedocumentposition for
// a description of these values.
var comparePos = exports.compareDocumentPosition = function(nodeA, nodeB) {
var aParents = [];
var bParents = [];
var current, sharedParent, siblings, aSibling, bSibling, idx;
if (nodeA === nodeB) {
return 0;
}
current = nodeA;
while (current) {
aParents.unshift(current);
current = current.parent;
}
current = nodeB;
while (current) {
bParents.unshift(current);
current = current.parent;
}
idx = 0;
while (aParents[idx] === bParents[idx]) {
idx++;
}
if (idx === 0) {
return POSITION.DISCONNECTED;
}
sharedParent = aParents[idx - 1];
siblings = sharedParent.children;
aSibling = aParents[idx];
bSibling = bParents[idx];
if (siblings.indexOf(aSibling) > siblings.indexOf(bSibling)) {
if (sharedParent === nodeB) {
return POSITION.FOLLOWING | POSITION.CONTAINED_BY;
}
return POSITION.FOLLOWING;
} else {
if (sharedParent === nodeA) {
return POSITION.PRECEDING | POSITION.CONTAINS;
}
return POSITION.PRECEDING;
}
};
// Sort an array of nodes based on their relative position in the document and
// remove any duplicate nodes. If the array contains nodes that do not belong
// to the same document, sort order is unspecified.
//
// @argument {Array} nodes Array of DOM nodes
//
// @returns {Array} collection of unique nodes, sorted in document order
exports.uniqueSort = function(nodes) {
var idx = nodes.length, node, position;
nodes = nodes.slice();
while (--idx > -1) {
node = nodes[idx];
position = nodes.indexOf(node);
if (position > -1 && position < idx) {
nodes.splice(idx, 1);
}
}
nodes.sort(function(a, b) {
var relative = comparePos(a, b);
if (relative & POSITION.PRECEDING) {
return -1;
} else if (relative & POSITION.FOLLOWING) {
return 1;
}
return 0;
});
return nodes;
};
+87
View File
@@ -0,0 +1,87 @@
var ElementType = require("domelementtype");
var isTag = exports.isTag = ElementType.isTag;
exports.testElement = function(options, element){
for(var key in options){
if(!options.hasOwnProperty(key));
else if(key === "tag_name"){
if(!isTag(element) || !options.tag_name(element.name)){
return false;
}
} else if(key === "tag_type"){
if(!options.tag_type(element.type)) return false;
} else if(key === "tag_contains"){
if(isTag(element) || !options.tag_contains(element.data)){
return false;
}
} else if(!element.attribs || !options[key](element.attribs[key])){
return false;
}
}
return true;
};
var Checks = {
tag_name: function(name){
if(typeof name === "function"){
return function(elem){ return isTag(elem) && name(elem.name); };
} else if(name === "*"){
return isTag;
} else {
return function(elem){ return isTag(elem) && elem.name === name; };
}
},
tag_type: function(type){
if(typeof type === "function"){
return function(elem){ return type(elem.type); };
} else {
return function(elem){ return elem.type === type; };
}
},
tag_contains: function(data){
if(typeof data === "function"){
return function(elem){ return !isTag(elem) && data(elem.data); };
} else {
return function(elem){ return !isTag(elem) && elem.data === data; };
}
}
};
function getAttribCheck(attrib, value){
if(typeof value === "function"){
return function(elem){ return elem.attribs && value(elem.attribs[attrib]); };
} else {
return function(elem){ return elem.attribs && elem.attribs[attrib] === value; };
}
}
function combineFuncs(a, b){
return function(elem){
return a(elem) || b(elem);
};
}
exports.getElements = function(options, element, recurse, limit){
var funcs = Object.keys(options).map(function(key){
var value = options[key];
return key in Checks ? Checks[key](value) : getAttribCheck(key, value);
});
return funcs.length === 0 ? [] : this.filter(
funcs.reduce(combineFuncs),
element, recurse, limit
);
};
exports.getElementById = function(id, element, recurse){
if(!Array.isArray(element)) element = [element];
return this.findOne(getAttribCheck("id", id), element, recurse !== false);
};
exports.getElementsByTagName = function(name, element, recurse, limit){
return this.filter(Checks.tag_name(name), element, recurse, limit);
};
exports.getElementsByTagType = function(type, element, recurse, limit){
return this.filter(Checks.tag_type(type), element, recurse, limit);
};
@@ -0,0 +1,77 @@
exports.removeElement = function(elem){
if(elem.prev) elem.prev.next = elem.next;
if(elem.next) elem.next.prev = elem.prev;
if(elem.parent){
var childs = elem.parent.children;
childs.splice(childs.lastIndexOf(elem), 1);
}
};
exports.replaceElement = function(elem, replacement){
var prev = replacement.prev = elem.prev;
if(prev){
prev.next = replacement;
}
var next = replacement.next = elem.next;
if(next){
next.prev = replacement;
}
var parent = replacement.parent = elem.parent;
if(parent){
var childs = parent.children;
childs[childs.lastIndexOf(elem)] = replacement;
}
};
exports.appendChild = function(elem, child){
child.parent = elem;
if(elem.children.push(child) !== 1){
var sibling = elem.children[elem.children.length - 2];
sibling.next = child;
child.prev = sibling;
child.next = null;
}
};
exports.append = function(elem, next){
var parent = elem.parent,
currNext = elem.next;
next.next = currNext;
next.prev = elem;
elem.next = next;
next.parent = parent;
if(currNext){
currNext.prev = next;
if(parent){
var childs = parent.children;
childs.splice(childs.lastIndexOf(currNext), 0, next);
}
} else if(parent){
parent.children.push(next);
}
};
exports.prepend = function(elem, prev){
var parent = elem.parent;
if(parent){
var childs = parent.children;
childs.splice(childs.lastIndexOf(elem), 0, prev);
}
if(elem.prev){
elem.prev.next = prev;
}
prev.parent = parent;
prev.prev = elem.prev;
prev.next = elem;
elem.prev = prev;
};
@@ -0,0 +1,95 @@
var isTag = require("domelementtype").isTag;
module.exports = {
filter: filter,
find: find,
findOneChild: findOneChild,
findOne: findOne,
existsOne: existsOne,
findAll: findAll
};
function filter(test, element, recurse, limit){
if(!Array.isArray(element)) element = [element];
if(typeof limit !== "number" || !isFinite(limit)){
limit = Infinity;
}
return find(test, element, recurse !== false, limit);
}
function find(test, elems, recurse, limit){
var result = [], childs;
for(var i = 0, j = elems.length; i < j; i++){
if(test(elems[i])){
result.push(elems[i]);
if(--limit <= 0) break;
}
childs = elems[i].children;
if(recurse && childs && childs.length > 0){
childs = find(test, childs, recurse, limit);
result = result.concat(childs);
limit -= childs.length;
if(limit <= 0) break;
}
}
return result;
}
function findOneChild(test, elems){
for(var i = 0, l = elems.length; i < l; i++){
if(test(elems[i])) return elems[i];
}
return null;
}
function findOne(test, elems){
var elem = null;
for(var i = 0, l = elems.length; i < l && !elem; i++){
if(!isTag(elems[i])){
continue;
} else if(test(elems[i])){
elem = elems[i];
} else if(elems[i].children.length > 0){
elem = findOne(test, elems[i].children);
}
}
return elem;
}
function existsOne(test, elems){
for(var i = 0, l = elems.length; i < l; i++){
if(
isTag(elems[i]) && (
test(elems[i]) || (
elems[i].children.length > 0 &&
existsOne(test, elems[i].children)
)
)
){
return true;
}
}
return false;
}
function findAll(test, rootElems){
var result = [];
var stack = rootElems.slice();
while(stack.length){
var elem = stack.shift();
if(!isTag(elem)) continue;
if (elem.children && elem.children.length > 0) {
stack.unshift.apply(stack, elem.children);
}
if(test(elem)) result.push(elem);
}
return result;
}
@@ -0,0 +1,23 @@
var ElementType = require("domelementtype"),
getOuterHTML = require("dom-serializer"),
isTag = ElementType.isTag;
module.exports = {
getInnerHTML: getInnerHTML,
getOuterHTML: getOuterHTML,
getText: getText
};
function getInnerHTML(elem, opts){
return elem.children ? elem.children.map(function(elem){
return getOuterHTML(elem, opts);
}).join("") : "";
}
function getText(elem){
if(Array.isArray(elem)) return elem.map(getText).join("");
if(isTag(elem)) return elem.name === "br" ? "\n" : getText(elem.children);
if(elem.type === ElementType.CDATA) return getText(elem.children);
if(elem.type === ElementType.Text) return elem.data;
return "";
}
@@ -0,0 +1,24 @@
var getChildren = exports.getChildren = function(elem){
return elem.children;
};
var getParent = exports.getParent = function(elem){
return elem.parent;
};
exports.getSiblings = function(elem){
var parent = getParent(elem);
return parent ? getChildren(parent) : [elem];
};
exports.getAttributeValue = function(elem, name){
return elem.attribs && elem.attribs[name];
};
exports.hasAttrib = function(elem, name){
return !!elem.attribs && hasOwnProperty.call(elem.attribs, name);
};
exports.getName = function(elem){
return elem.name;
};
+47
View File
@@ -0,0 +1,47 @@
{
"name": "domutils",
"version": "1.7.0",
"description": "utilities for working with htmlparser2's dom",
"main": "index.js",
"directories": {
"test": "tests"
},
"scripts": {
"test": "mocha test/tests/**.js && jshint index.js test/**/*.js lib/*.js"
},
"repository": {
"type": "git",
"url": "git://github.com/FB55/domutils.git"
},
"keywords": [
"dom",
"htmlparser2"
],
"dependencies": {
"dom-serializer": "0",
"domelementtype": "1"
},
"devDependencies": {
"htmlparser2": "~3.9.2",
"domhandler": "2",
"jshint": "~2.9.4",
"mocha": "~3.2.0"
},
"author": "Felix Boehm <me@feedic.com>",
"license": "BSD-2-Clause",
"jshintConfig": {
"proto": true,
"unused": true,
"eqnull": true,
"undef": true,
"quotmark": "double",
"eqeqeq": true,
"trailing": true,
"node": true,
"globals": {
"describe": true,
"it": true,
"beforeEach": true
}
}
}
+3
View File
@@ -0,0 +1,3 @@
Utilities for working with htmlparser2's dom
[![Build Status](https://travis-ci.org/fb55/domutils.svg?branch=master)](https://travis-ci.org/fb55/domutils)
@@ -0,0 +1,6 @@
var makeDom = require("./utils").makeDom;
var markup = Array(21).join(
"<?xml><tag1 id='asdf'> <script>text</script> <!-- comment --> <tag2> text </tag1>"
);
module.exports = makeDom(markup);
@@ -0,0 +1,89 @@
var makeDom = require("../utils").makeDom;
var helpers = require("../..");
var assert = require("assert");
describe("helpers", function() {
describe("removeSubsets", function() {
var removeSubsets = helpers.removeSubsets;
var dom = makeDom("<div><p><span></span></p><p></p></div>")[0];
it("removes identical trees", function() {
var matches = removeSubsets([dom, dom]);
assert.equal(matches.length, 1);
});
it("Removes subsets found first", function() {
var matches = removeSubsets([dom, dom.children[0].children[0]]);
assert.equal(matches.length, 1);
});
it("Removes subsets found last", function() {
var matches = removeSubsets([dom.children[0], dom]);
assert.equal(matches.length, 1);
});
it("Does not remove unique trees", function() {
var matches = removeSubsets([dom.children[0], dom.children[1]]);
assert.equal(matches.length, 2);
});
});
describe("compareDocumentPosition", function() {
var compareDocumentPosition = helpers.compareDocumentPosition;
var markup = "<div><p><span></span></p><a></a></div>";
var dom = makeDom(markup)[0];
var p = dom.children[0];
var span = p.children[0];
var a = dom.children[1];
it("reports when the first node occurs before the second indirectly", function() {
assert.equal(compareDocumentPosition(span, a), 2);
});
it("reports when the first node contains the second", function() {
assert.equal(compareDocumentPosition(p, span), 10);
});
it("reports when the first node occurs after the second indirectly", function() {
assert.equal(compareDocumentPosition(a, span), 4);
});
it("reports when the first node is contained by the second", function() {
assert.equal(compareDocumentPosition(span, p), 20);
});
it("reports when the nodes belong to separate documents", function() {
var other = makeDom(markup)[0].children[0].children[0];
assert.equal(compareDocumentPosition(span, other), 1);
});
it("reports when the nodes are identical", function() {
assert.equal(compareDocumentPosition(span, span), 0);
});
});
describe("uniqueSort", function() {
var uniqueSort = helpers.uniqueSort;
var dom, p, span, a;
beforeEach(function() {
dom = makeDom("<div><p><span></span></p><a></a></div>")[0];
p = dom.children[0];
span = p.children[0];
a = dom.children[1];
});
it("leaves unique elements untouched", function() {
assert.deepEqual(uniqueSort([p, a]), [p, a]);
});
it("removes duplicate elements", function() {
assert.deepEqual(uniqueSort([p, a, p]), [p, a]);
});
it("sorts nodes in document order", function() {
assert.deepEqual(uniqueSort([a, dom, span, p]), [dom, p, span, a]);
});
});
});
@@ -0,0 +1,119 @@
var DomUtils = require("../..");
var fixture = require("../fixture");
var assert = require("assert");
// Set up expected structures
var expected = {
idAsdf: fixture[1],
tag2: [],
typeScript: []
};
for (var idx = 0; idx < 20; ++idx) {
expected.tag2.push(fixture[idx*2 + 1].children[5]);
expected.typeScript.push(fixture[idx*2 + 1].children[1]);
}
describe("legacy", function() {
describe("getElements", function() {
var getElements = DomUtils.getElements;
it("returns the node with the specified ID", function() {
assert.deepEqual(
getElements({ id: "asdf" }, fixture, true, 1),
[expected.idAsdf]
);
});
it("returns empty array for unknown IDs", function() {
assert.deepEqual(getElements({ id: "asdfs" }, fixture, true), []);
});
it("returns the nodes with the specified tag name", function() {
assert.deepEqual(
getElements({ tag_name:"tag2" }, fixture, true),
expected.tag2
);
});
it("returns empty array for unknown tag names", function() {
assert.deepEqual(
getElements({ tag_name : "asdfs" }, fixture, true),
[]
);
});
it("returns the nodes with the specified tag type", function() {
assert.deepEqual(
getElements({ tag_type: "script" }, fixture, true),
expected.typeScript
);
});
it("returns empty array for unknown tag types", function() {
assert.deepEqual(
getElements({ tag_type: "video" }, fixture, true),
[]
);
});
});
describe("getElementById", function() {
var getElementById = DomUtils.getElementById;
it("returns the specified node", function() {
assert.equal(
expected.idAsdf,
getElementById("asdf", fixture, true)
);
});
it("returns `null` for unknown IDs", function() {
assert.equal(null, getElementById("asdfs", fixture, true));
});
});
describe("getElementsByTagName", function() {
var getElementsByTagName = DomUtils.getElementsByTagName;
it("returns the specified nodes", function() {
assert.deepEqual(
getElementsByTagName("tag2", fixture, true),
expected.tag2
);
});
it("returns empty array for unknown tag names", function() {
assert.deepEqual(
getElementsByTagName("tag23", fixture, true),
[]
);
});
});
describe("getElementsByTagType", function() {
var getElementsByTagType = DomUtils.getElementsByTagType;
it("returns the specified nodes", function() {
assert.deepEqual(
getElementsByTagType("script", fixture, true),
expected.typeScript
);
});
it("returns empty array for unknown tag types", function() {
assert.deepEqual(
getElementsByTagType("video", fixture, true),
[]
);
});
});
describe("getOuterHTML", function() {
var getOuterHTML = DomUtils.getOuterHTML;
it("Correctly renders the outer HTML", function() {
assert.equal(
getOuterHTML(fixture[1]),
"<tag1 id=\"asdf\"> <script>text</script> <!-- comment --> <tag2> text </tag2></tag1>"
);
});
});
describe("getInnerHTML", function() {
var getInnerHTML = DomUtils.getInnerHTML;
it("Correctly renders the inner HTML", function() {
assert.equal(
getInnerHTML(fixture[1]),
" <script>text</script> <!-- comment --> <tag2> text </tag2>"
);
});
});
});
@@ -0,0 +1,17 @@
var makeDom = require("../utils").makeDom;
var traversal = require("../..");
var assert = require("assert");
describe("traversal", function() {
describe("hasAttrib", function() {
var hasAttrib = traversal.hasAttrib;
it("doesn't throw on text nodes", function() {
var dom = makeDom("textnode");
assert.doesNotThrow(function() {
hasAttrib(dom[0], "some-attrib");
});
});
});
});
@@ -0,0 +1,9 @@
var htmlparser = require("htmlparser2");
exports.makeDom = function(markup) {
var handler = new htmlparser.DomHandler(),
parser = new htmlparser.Parser(handler);
parser.write(markup);
parser.done();
return handler.dom;
};
+5
View File
@@ -0,0 +1,5 @@
sudo: true
language: node_js
node_js:
- 8
script: npm run coveralls
+11
View File
@@ -0,0 +1,11 @@
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+26
View File
@@ -0,0 +1,26 @@
var encode = require("./lib/encode.js"),
decode = require("./lib/decode.js");
exports.decode = function(data, level) {
return (!level || level <= 0 ? decode.XML : decode.HTML)(data);
};
exports.decodeStrict = function(data, level) {
return (!level || level <= 0 ? decode.XML : decode.HTMLStrict)(data);
};
exports.encode = function(data, level) {
return (!level || level <= 0 ? encode.XML : encode.HTML)(data);
};
exports.encodeXML = encode.XML;
exports.encodeHTML4 = exports.encodeHTML5 = exports.encodeHTML = encode.HTML;
exports.decodeXML = exports.decodeXMLStrict = decode.XML;
exports.decodeHTML4 = exports.decodeHTML5 = exports.decodeHTML = decode.HTML;
exports.decodeHTML4Strict = exports.decodeHTML5Strict = exports.decodeHTMLStrict = decode.HTMLStrict;
exports.escape = encode.escape;
+70
View File
@@ -0,0 +1,70 @@
var entityMap = require("../maps/entities.json"),
legacyMap = require("../maps/legacy.json"),
xmlMap = require("../maps/xml.json"),
decodeCodePoint = require("./decode_codepoint.js");
var decodeXMLStrict = getStrictDecoder(xmlMap),
decodeHTMLStrict = getStrictDecoder(entityMap);
function getStrictDecoder(map) {
var keys = Object.keys(map).join("|"),
replace = getReplacer(map);
keys += "|#[xX][\\da-fA-F]+|#\\d+";
var re = new RegExp("&(?:" + keys + ");", "g");
return function(str) {
return String(str).replace(re, replace);
};
}
var decodeHTML = (function() {
var legacy = Object.keys(legacyMap).sort(sorter);
var keys = Object.keys(entityMap).sort(sorter);
for (var i = 0, j = 0; i < keys.length; i++) {
if (legacy[j] === keys[i]) {
keys[i] += ";?";
j++;
} else {
keys[i] += ";";
}
}
var re = new RegExp("&(?:" + keys.join("|") + "|#[xX][\\da-fA-F]+;?|#\\d+;?)", "g"),
replace = getReplacer(entityMap);
function replacer(str) {
if (str.substr(-1) !== ";") str += ";";
return replace(str);
}
//TODO consider creating a merged map
return function(str) {
return String(str).replace(re, replacer);
};
})();
function sorter(a, b) {
return a < b ? 1 : -1;
}
function getReplacer(map) {
return function replace(str) {
if (str.charAt(1) === "#") {
if (str.charAt(2) === "X" || str.charAt(2) === "x") {
return decodeCodePoint(parseInt(str.substr(3), 16));
}
return decodeCodePoint(parseInt(str.substr(2), 10));
}
return map[str.slice(1, -1)];
};
}
module.exports = {
XML: decodeXMLStrict,
HTML: decodeHTML,
HTMLStrict: decodeHTMLStrict
};
@@ -0,0 +1,25 @@
var decodeMap = require("../maps/decode.json");
module.exports = decodeCodePoint;
// modified version of https://github.com/mathiasbynens/he/blob/master/src/he.js#L94-L119
function decodeCodePoint(codePoint) {
if ((codePoint >= 0xd800 && codePoint <= 0xdfff) || codePoint > 0x10ffff) {
return "\uFFFD";
}
if (codePoint in decodeMap) {
codePoint = decodeMap[codePoint];
}
var output = "";
if (codePoint > 0xffff) {
codePoint -= 0x10000;
output += String.fromCharCode(((codePoint >>> 10) & 0x3ff) | 0xd800);
codePoint = 0xdc00 | (codePoint & 0x3ff);
}
output += String.fromCharCode(codePoint);
return output;
}
+82
View File
@@ -0,0 +1,82 @@
var inverseXML = getInverseObj(require("../maps/xml.json")),
xmlReplacer = getInverseReplacer(inverseXML);
exports.XML = getInverse(inverseXML, xmlReplacer);
var inverseHTML = getInverseObj(require("../maps/entities.json")),
htmlReplacer = getInverseReplacer(inverseHTML);
exports.HTML = getInverse(inverseHTML, htmlReplacer);
function getInverseObj(obj) {
return Object.keys(obj)
.sort()
.reduce(function(inverse, name) {
inverse[obj[name]] = "&" + name + ";";
return inverse;
}, {});
}
function getInverseReplacer(inverse) {
var single = [],
multiple = [];
Object.keys(inverse).forEach(function(k) {
if (k.length === 1) {
single.push("\\" + k);
} else {
multiple.push(k);
}
});
//TODO add ranges
multiple.unshift("[" + single.join("") + "]");
return new RegExp(multiple.join("|"), "g");
}
var re_nonASCII = /[^\0-\x7F]/g,
re_astralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
function singleCharReplacer(c) {
return (
"&#x" +
c
.charCodeAt(0)
.toString(16)
.toUpperCase() +
";"
);
}
function astralReplacer(c) {
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
var high = c.charCodeAt(0);
var low = c.charCodeAt(1);
var codePoint = (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
return "&#x" + codePoint.toString(16).toUpperCase() + ";";
}
function getInverse(inverse, re) {
function func(name) {
return inverse[name];
}
return function(data) {
return data
.replace(re, func)
.replace(re_astralSymbols, astralReplacer)
.replace(re_nonASCII, singleCharReplacer);
};
}
var re_xmlChars = getInverseReplacer(inverseXML);
function escapeXML(data) {
return data
.replace(re_xmlChars, singleCharReplacer)
.replace(re_astralSymbols, astralReplacer)
.replace(re_nonASCII, singleCharReplacer);
}
exports.escape = escapeXML;
@@ -0,0 +1 @@
{"0":65533,"128":8364,"130":8218,"131":402,"132":8222,"133":8230,"134":8224,"135":8225,"136":710,"137":8240,"138":352,"139":8249,"140":338,"142":381,"145":8216,"146":8217,"147":8220,"148":8221,"149":8226,"150":8211,"151":8212,"152":732,"153":8482,"154":353,"155":8250,"156":339,"158":382,"159":376}
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"Aacute":"\u00C1","aacute":"\u00E1","Acirc":"\u00C2","acirc":"\u00E2","acute":"\u00B4","AElig":"\u00C6","aelig":"\u00E6","Agrave":"\u00C0","agrave":"\u00E0","amp":"&","AMP":"&","Aring":"\u00C5","aring":"\u00E5","Atilde":"\u00C3","atilde":"\u00E3","Auml":"\u00C4","auml":"\u00E4","brvbar":"\u00A6","Ccedil":"\u00C7","ccedil":"\u00E7","cedil":"\u00B8","cent":"\u00A2","copy":"\u00A9","COPY":"\u00A9","curren":"\u00A4","deg":"\u00B0","divide":"\u00F7","Eacute":"\u00C9","eacute":"\u00E9","Ecirc":"\u00CA","ecirc":"\u00EA","Egrave":"\u00C8","egrave":"\u00E8","ETH":"\u00D0","eth":"\u00F0","Euml":"\u00CB","euml":"\u00EB","frac12":"\u00BD","frac14":"\u00BC","frac34":"\u00BE","gt":">","GT":">","Iacute":"\u00CD","iacute":"\u00ED","Icirc":"\u00CE","icirc":"\u00EE","iexcl":"\u00A1","Igrave":"\u00CC","igrave":"\u00EC","iquest":"\u00BF","Iuml":"\u00CF","iuml":"\u00EF","laquo":"\u00AB","lt":"<","LT":"<","macr":"\u00AF","micro":"\u00B5","middot":"\u00B7","nbsp":"\u00A0","not":"\u00AC","Ntilde":"\u00D1","ntilde":"\u00F1","Oacute":"\u00D3","oacute":"\u00F3","Ocirc":"\u00D4","ocirc":"\u00F4","Ograve":"\u00D2","ograve":"\u00F2","ordf":"\u00AA","ordm":"\u00BA","Oslash":"\u00D8","oslash":"\u00F8","Otilde":"\u00D5","otilde":"\u00F5","Ouml":"\u00D6","ouml":"\u00F6","para":"\u00B6","plusmn":"\u00B1","pound":"\u00A3","quot":"\"","QUOT":"\"","raquo":"\u00BB","reg":"\u00AE","REG":"\u00AE","sect":"\u00A7","shy":"\u00AD","sup1":"\u00B9","sup2":"\u00B2","sup3":"\u00B3","szlig":"\u00DF","THORN":"\u00DE","thorn":"\u00FE","times":"\u00D7","Uacute":"\u00DA","uacute":"\u00FA","Ucirc":"\u00DB","ucirc":"\u00FB","Ugrave":"\u00D9","ugrave":"\u00F9","uml":"\u00A8","Uuml":"\u00DC","uuml":"\u00FC","Yacute":"\u00DD","yacute":"\u00FD","yen":"\u00A5","yuml":"\u00FF"}
@@ -0,0 +1 @@
{"amp":"&","apos":"'","gt":">","lt":"<","quot":"\""}
+57
View File
@@ -0,0 +1,57 @@
{
"name": "entities",
"version": "1.1.2",
"description": "Encode & decode XML/HTML entities with ease",
"author": "Felix Boehm <me@feedic.com>",
"keywords": [
"html",
"xml",
"entity",
"decoding",
"encoding"
],
"main": "./index.js",
"directories": {
"test": "test"
},
"devDependencies": {
"mocha": "^5.0.1",
"mocha-lcov-reporter": "*",
"coveralls": "*",
"istanbul": "*",
"jshint": "2"
},
"scripts": {
"test": "mocha && npm run lint",
"lint": "jshint index.js lib/*.js test/*.js",
"lcov": "istanbul cover _mocha --report lcovonly -- -R spec",
"coveralls": "npm run lint && npm run lcov && (cat coverage/lcov.info | coveralls || exit 0)"
},
"repository": {
"type": "git",
"url": "git://github.com/fb55/entities.git"
},
"license": "BSD-2-Clause",
"jshintConfig": {
"eqeqeq": true,
"freeze": true,
"latedef": "nofunc",
"noarg": true,
"nonbsp": true,
"quotmark": "double",
"undef": true,
"unused": true,
"trailing": true,
"eqnull": true,
"proto": true,
"smarttabs": true,
"node": true,
"globals": {
"describe": true,
"it": true
}
},
"prettier": {
"tabWidth": 4
}
}
+27
View File
@@ -0,0 +1,27 @@
# entities [![NPM version](http://img.shields.io/npm/v/entities.svg)](https://npmjs.org/package/entities) [![Downloads](https://img.shields.io/npm/dm/entities.svg)](https://npmjs.org/package/entities) [![Build Status](http://img.shields.io/travis/fb55/entities.svg)](http://travis-ci.org/fb55/entities) [![Coverage](http://img.shields.io/coveralls/fb55/entities.svg)](https://coveralls.io/r/fb55/entities)
En- & decoder for XML/HTML entities.
## How to…
### …install `entities`
npm i entities
### …use `entities`
```javascript
var entities = require("entities");
//encoding
entities.encodeXML("&#38;"); // "&amp;#38;"
entities.encodeHTML("&#38;"); // "&amp;&num;38&semi;"
//decoding
entities.decodeXML("asdf &amp; &#xFF; &#xFC; &apos;"); // "asdf & ÿ ü '"
entities.decodeHTML("asdf &amp; &yuml; &uuml; &apos;"); // "asdf & ÿ ü '"
```
<!-- TODO extend API -->
---
License: BSD-2-Clause
@@ -0,0 +1,2 @@
--check-leaks
--reporter spec
+170
View File
@@ -0,0 +1,170 @@
var assert = require("assert"),
path = require("path"),
entities = require("../");
describe("Encode->decode test", function() {
var testcases = [
{
input: "asdf & ÿ ü '",
xml: "asdf &amp; &#xFF; &#xFC; &apos;",
html: "asdf &amp; &yuml; &uuml; &apos;"
},
{
input: "&#38;",
xml: "&amp;#38;",
html: "&amp;&num;38&semi;"
}
];
testcases.forEach(function(tc) {
var encodedXML = entities.encodeXML(tc.input);
it("should XML encode " + tc.input, function() {
assert.equal(encodedXML, tc.xml);
});
it("should default to XML encode " + tc.input, function() {
assert.equal(entities.encode(tc.input), tc.xml);
});
it("should XML decode " + encodedXML, function() {
assert.equal(entities.decodeXML(encodedXML), tc.input);
});
it("should default to XML encode " + encodedXML, function() {
assert.equal(entities.decode(encodedXML), tc.input);
});
it("should default strict to XML encode " + encodedXML, function() {
assert.equal(entities.decodeStrict(encodedXML), tc.input);
});
var encodedHTML5 = entities.encodeHTML5(tc.input);
it("should HTML5 encode " + tc.input, function() {
assert.equal(encodedHTML5, tc.html);
});
it("should HTML5 decode " + encodedHTML5, function() {
assert.equal(entities.decodeHTML(encodedHTML5), tc.input);
});
});
it("should encode data URIs (issue 16)", function() {
var data = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAALAAABAAEAAAIBRAA7";
assert.equal(entities.decode(entities.encode(data)), data);
});
});
describe("Decode test", function() {
var testcases = [
{ input: "&amp;amp;", output: "&amp;" },
{ input: "&amp;#38;", output: "&#38;" },
{ input: "&amp;#x26;", output: "&#x26;" },
{ input: "&amp;#X26;", output: "&#X26;" },
{ input: "&#38;#38;", output: "&#38;" },
{ input: "&#x26;#38;", output: "&#38;" },
{ input: "&#X26;#38;", output: "&#38;" },
{ input: "&#x3a;", output: ":" },
{ input: "&#x3A;", output: ":" },
{ input: "&#X3a;", output: ":" },
{ input: "&#X3A;", output: ":" }
];
testcases.forEach(function(tc) {
it("should XML decode " + tc.input, function() {
assert.equal(entities.decodeXML(tc.input), tc.output);
});
it("should HTML4 decode " + tc.input, function() {
assert.equal(entities.decodeHTML(tc.input), tc.output);
});
it("should HTML5 decode " + tc.input, function() {
assert.equal(entities.decodeHTML(tc.input), tc.output);
});
});
});
var levels = ["xml", "entities"];
describe("Documents", function() {
levels
.map(function(n) {
return path.join("..", "maps", n);
})
.map(require)
.forEach(function(doc, i) {
describe("Decode", function() {
it(levels[i], function() {
Object.keys(doc).forEach(function(e) {
for (var l = i; l < levels.length; l++) {
assert.equal(entities.decode("&" + e + ";", l), doc[e]);
}
});
});
});
describe("Decode strict", function() {
it(levels[i], function() {
Object.keys(doc).forEach(function(e) {
for (var l = i; l < levels.length; l++) {
assert.equal(entities.decodeStrict("&" + e + ";", l), doc[e]);
}
});
});
});
describe("Encode", function() {
it(levels[i], function() {
Object.keys(doc).forEach(function(e) {
for (var l = i; l < levels.length; l++) {
assert.equal(entities.decode(entities.encode(doc[e], l), l), doc[e]);
}
});
});
});
});
var legacy = require("../maps/legacy.json");
describe("Legacy", function() {
it("should decode", runLegacy);
});
function runLegacy() {
Object.keys(legacy).forEach(function(e) {
assert.equal(entities.decodeHTML("&" + e), legacy[e]);
});
}
});
var astral = {
"1D306": "\uD834\uDF06",
"1D11E": "\uD834\uDD1E"
};
var astralSpecial = {
"80": "\u20AC",
"110000": "\uFFFD"
};
describe("Astral entities", function() {
Object.keys(astral).forEach(function(c) {
it("should decode " + astral[c], function() {
assert.equal(entities.decode("&#x" + c + ";"), astral[c]);
});
it("should encode " + astral[c], function() {
assert.equal(entities.encode(astral[c]), "&#x" + c + ";");
});
it("should escape " + astral[c], function() {
assert.equal(entities.escape(astral[c]), "&#x" + c + ";");
});
});
Object.keys(astralSpecial).forEach(function(c) {
it("special should decode \\u" + c, function() {
assert.equal(entities.decode("&#x" + c + ";"), astralSpecial[c]);
});
});
});
describe("Escape", function() {
it("should always decode ASCII chars", function() {
for (var i = 0; i < 0x7f; i++) {
var c = String.fromCharCode(i);
assert.equal(entities.decodeXML(entities.escape(c)), c);
}
});
});
+56
View File
@@ -0,0 +1,56 @@
{
"name": "stricter-htmlparser2",
"description": "Fast & forgiving HTML/XML/RSS parser with stricter attribute parsing rules.",
"version": "3.9.6",
"author": "Felix Boehm <me@feedic.com> hiby",
"keywords": [
"html",
"parser",
"streams",
"xml",
"dom",
"rss",
"feed",
"atom"
],
"repository": {
"type": "git",
"url": "git@github.com:yanghuabei/htmlparser2.git"
},
"bugs": {
"mail": "me@feedic.com",
"url": "http://github.com/yanghuabei/htmlparser2/issues"
},
"directories": {
"lib": "lib"
},
"main": "lib/index.js",
"files": [
"lib"
],
"scripts": {
"lcov": "istanbul cover _mocha --report lcovonly -- -R spec",
"coveralls": "npm run lcov && (cat coverage/lcov.info | coveralls || exit 0)",
"test": "mocha",
"lint": "eslint lib"
},
"dependencies": {
"domelementtype": "^1.3.0",
"domutils": "^1.5.1",
"entities": "^1.1.1",
"inherits": "^2.0.1",
"readable-stream": "^2.0.2",
"x-domhandler": "^2.4.2"
},
"devDependencies": {
"coveralls": "^3.0.1",
"eslint": "^4.19.1",
"istanbul": "^0.4.3",
"mocha": "^5.2.0",
"mocha-lcov-reporter": "^1.2.0"
},
"browser": {
"readable-stream": false
},
"license": "MIT"
}