nxkit
Version:
This is a collection of tools, independent of any other libraries
538 lines (537 loc) • 18 kB
JavaScript
;
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2015, xuewen.chu
* 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.
* * Neither the name of xuewen.chu nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE 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 xuewen.chu 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
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = require("../util");
const html_tag = /^(br|hr|input|frame|img|area|link|col|meta|area|base|basefont|param)$/i;
const html = /^html$/i;
var NODE_TYPE;
(function (NODE_TYPE) {
NODE_TYPE[NODE_TYPE["NODE_NODE"] = 1] = "NODE_NODE";
NODE_TYPE[NODE_TYPE["ELEMENT_NODE"] = 1] = "ELEMENT_NODE";
NODE_TYPE[NODE_TYPE["ATTRIBUTE_NODE"] = 2] = "ATTRIBUTE_NODE";
NODE_TYPE[NODE_TYPE["TEXT_NODE"] = 3] = "TEXT_NODE";
NODE_TYPE[NODE_TYPE["CDATA_SECTION_NODE"] = 4] = "CDATA_SECTION_NODE";
NODE_TYPE[NODE_TYPE["ENTITY_REFERENCE_NODE"] = 5] = "ENTITY_REFERENCE_NODE";
NODE_TYPE[NODE_TYPE["ENTITY_NODE"] = 6] = "ENTITY_NODE";
NODE_TYPE[NODE_TYPE["PROCESSING_INSTRUCTION_NODE"] = 7] = "PROCESSING_INSTRUCTION_NODE";
NODE_TYPE[NODE_TYPE["COMMENT_NODE"] = 8] = "COMMENT_NODE";
NODE_TYPE[NODE_TYPE["DOCUMENT_NODE"] = 9] = "DOCUMENT_NODE";
NODE_TYPE[NODE_TYPE["DOCUMENT_TYPE_NODE"] = 10] = "DOCUMENT_TYPE_NODE";
NODE_TYPE[NODE_TYPE["DOCUMENT_FRAGMENT_NODE"] = 11] = "DOCUMENT_FRAGMENT_NODE";
NODE_TYPE[NODE_TYPE["NOTATION_NODE"] = 12] = "NOTATION_NODE";
})(NODE_TYPE = exports.NODE_TYPE || (exports.NODE_TYPE = {}));
;
function xmlEncoder(c) {
return c == '<' && '<' || c == '&' && '&' ||
c == '"' && '"' || '&#' + c.charCodeAt(0) + ';';
}
function findNSMap(self) {
var e = self;
while (e && e.nodeType !== NODE_TYPE.ELEMENT_NODE) {
if (e.nodeType === NODE_TYPE.ATTRIBUTE_NODE) {
e = e.ownerElement;
}
else {
e = e.parentNode;
}
}
return e ? (e.namespaceMap || {}) : {};
}
function serializeToString(node, buf) {
switch (node.nodeType) {
case NODE_TYPE.ELEMENT_NODE:
var e = node;
var attrs = e.attributes;
var len = attrs.length;
var child = node.firstChild;
var nodeName = e.tagName;
buf.push('<', nodeName);
for (var i = 0; i < len; i++) {
serializeToString(attrs.item(i), buf);
}
if (child) {
buf.push('>');
while (child) {
serializeToString(child, buf);
child = child.nextSibling;
}
buf.push('</', nodeName, '>');
}
else {
var doc = node.ownerDocument;
var doctype = doc.doctype;
if (doctype && html.test(doctype.name)) {
if (html_tag.test(nodeName))
buf.push(' />');
else
buf.push('></', nodeName, '>');
}
else
buf.push(' />');
}
return;
case NODE_TYPE.DOCUMENT_NODE:
case NODE_TYPE.DOCUMENT_FRAGMENT_NODE:
var child = node.firstChild;
while (child) {
serializeToString(child, buf);
child = child.nextSibling;
}
return;
case NODE_TYPE.ATTRIBUTE_NODE:
return buf.push(' ', node.name, '="', node.value.replace(/[<&"]/g, xmlEncoder), '"');
case NODE_TYPE.TEXT_NODE:
return buf.push(node.data.replace(/[<&]/g, xmlEncoder)); //(?!#?[\w\d]+;)
case NODE_TYPE.CDATA_SECTION_NODE:
return buf.push('<![CDATA[', node.data, ']]>');
case NODE_TYPE.COMMENT_NODE:
return buf.push("<!--", node.data, "-->");
case NODE_TYPE.DOCUMENT_TYPE_NODE:
var pubid = node.publicId;
var sysid = node.systemId;
buf.push('<!DOCTYPE ', node.name);
if (pubid) {
buf.push(' PUBLIC "', pubid);
if (sysid && sysid != '.') {
buf.push('" "', sysid);
}
buf.push('">');
}
else if (sysid && sysid != '.') {
buf.push(' SYSTEM "', sysid, '">');
}
else {
var sub = node.internalSubset;
if (sub) {
buf.push(" [", sub, "]");
}
buf.push(">");
}
return;
case NODE_TYPE.PROCESSING_INSTRUCTION_NODE:
// readonly target: string;
// readonly data: string;
return buf.push("<?", node.nodeName, " ", node.data, "?>");
case NODE_TYPE.ENTITY_REFERENCE_NODE:
return buf.push('&', node.nodeName, ';');
//case ENTITY_NODE:
//case NOTATION_NODE:
default:
buf.push('??', node.nodeName || '?');
}
}
/*
* attributes;
* children;
*
* writeable properties:
* nodeValue,Attr:value,CharacterData:data
* prefix
*/
function update(self, e, attr) {
var doc = self.ownerDocument || self;
doc._inc++;
if (attr) {
if (attr.namespaceURI == 'http://www.w3.org/2000/xmlns/') {
//update namespace
}
}
else { //node
//update childNodes
var cs = e.childNodes;
if (cs) {
var child = e.firstChild;
var i = 0;
while (child) {
cs[i++] = child;
child = child.nextSibling;
}
cs._length = i; // TODO private visit
}
}
}
class Node {
constructor(ownerDocument) {
this.ownerDocument = ownerDocument;
}
// Modified in DOM Level 2:
insertBefore(newChild, refChild) {
util_1.default.assert(newChild.ownerDocument == this.ownerDocument, 'OwnerDocument mismatch');
util_1.default.assert(this.childNodes, 'Cannot add child node');
var parentNode = this;
var cp = newChild.parentNode;
if (cp) {
cp.removeChild(newChild); //remove and update
}
var newLast, newFirst;
if (newChild.nodeType === NODE_TYPE.DOCUMENT_FRAGMENT_NODE) {
util_1.default.assert(newChild.firstChild, 'DOCUMENT_FRAGMENT_NODE cannot be empty');
newFirst = newChild.firstChild;
newLast = newChild.lastChild;
}
else
newFirst = newLast = newChild;
if (!refChild) {
var pre = parentNode.lastChild;
parentNode.lastChild = newLast;
}
else {
var pre = refChild.previousSibling;
newLast.nextSibling = refChild;
;
refChild.previousSibling = newLast;
}
if (pre)
pre.nextSibling = newFirst;
else
parentNode.firstChild = newFirst;
newFirst.previousSibling = pre;
do
newFirst.parentNode = parentNode;
while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
update(this, parentNode);
}
replaceChild(newChild, oldChild) {
this.insertBefore(newChild, oldChild);
if (oldChild) {
this.removeChild(oldChild);
}
}
removeAllChild() {
var ns = this.childNodes;
if (ns) {
for (var i = 0, l = ns.length; i < l; i++) {
ns.item(i).parentNode = undefined;
delete ns[i];
}
ns._length = 0; // TODO private visit
this.firstChild = undefined;
this.lastChild = undefined;
update(this, this);
}
}
removeChild(oldChild) {
var parentNode = this;
var previous;
var child = this.firstChild;
while (child) {
var next = child.nextSibling;
if (child === oldChild) {
oldChild.parentNode = undefined; //remove it as a flag of not in document
if (previous)
previous.nextSibling = next;
else
parentNode.firstChild = next;
if (next)
next.previousSibling = previous;
else
parentNode.lastChild = previous;
update(this, parentNode);
return child;
}
previous = child;
child = next;
}
return null;
}
appendChild(newChild) {
return this.insertBefore(newChild, null);
}
hasChildNodes() {
return this.firstChild != null;
}
cloneNode(deep = false) {
// TODO Unrealized
return null;
}
// Modified in DOM Level 2:
normalize() {
var child = this.firstChild;
while (child) {
var next = child.nextSibling;
if (next && next.nodeType == NODE_TYPE.TEXT_NODE && child.nodeType == NODE_TYPE.TEXT_NODE) {
this.removeChild(next);
child.appendData(next.data);
}
else {
child.normalize();
child = next;
}
}
}
// Introduced in DOM Level 2:
isSupported(feature, version) {
// TODO Unrealized
// return this.ownerDocument.implementation.hasFeature(feature, version);
return false;
}
// Introduced in DOM Level 2:
hasAttributes() {
if (this.attributes)
return this.attributes.length > 0;
return false;
}
lookupPrefix(namespaceURI) {
var map = findNSMap(this);
if (namespaceURI in map) {
return map[namespaceURI];
}
return null;
}
// Introduced in DOM Level 3:
isDefaultNamespace(namespaceURI) {
var prefix = this.lookupPrefix(namespaceURI);
return prefix == null;
}
// Introduced in DOM Level 3:
lookupNamespaceURI(prefix) {
var map = findNSMap(this);
for (var n in map) {
if (map[n] == prefix)
return n;
}
return null;
}
toString() {
var buf = [];
serializeToString(this, buf);
return buf.join('');
}
}
exports.Node = Node;
/**
* @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
* The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
* The items in the NodeList are accessible via an integral index, starting from 0.
*/
class NodeList {
constructor() {
/**
* The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
* @standard level1
*/
this._length = 0;
}
get length() {
return this._length;
}
/**
* Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
* @standard level1
* @param index unsigned long
* Index into the collection.
* @return Node
* The node at the indexth position in the NodeList, or null if that is not a valid index.
*/
item(index) {
return this[index] || null;
}
}
exports.NodeList = NodeList;
class LiveNodeList extends NodeList {
constructor(node, refresh) {
super();
this._node = node;
this._refresh = refresh;
}
_updateLiveNodeList() {
var self = this;
var inc = self._node.ownerDocument._inc;
if (self._inc != inc) {
var ls = self._refresh(self._node);
var l = ls.length;
self._length = l;
for (var i = 0; i < l; i++)
self[i] = ls[i];
self._inc = inc;
}
}
get length() {
this._updateLiveNodeList();
return this._length;
}
item(index) {
this._updateLiveNodeList();
return this[index] || null;
}
}
exports.LiveNodeList = LiveNodeList;
class CharacterData extends Node {
constructor() {
super(...arguments);
this.data = '';
this.length = 0;
}
get nodeValue() {
return this.data;
}
substringData(offset, count) {
return this.data.substring(offset, offset + count);
}
appendData(text) {
text = this.data + text;
this.data = text;
this.length = text.length;
}
insertData(offset, text) {
this.replaceData(offset, 0, text);
}
deleteData(offset, count) {
this.replaceData(offset, count, '');
}
replaceData(offset, count, text) {
var start = this.data.substring(0, offset);
var end = this.data.substring(offset + count);
text = start + text + end;
this.data = text;
this.length = text.length;
}
}
class Attribute extends CharacterData {
constructor(doc, name, value, specified = false) {
super(doc);
this.nodeType = NODE_TYPE.ATTRIBUTE_NODE;
this.ownerElement = null;
// this.ownerElement = ownerElement;
this.name = name;
this.value = value;
this.specified = specified;
}
get nodeValue() {
return this.value;
}
get nodeName() {
return this.name;
}
}
exports.Attribute = Attribute;
class CDATASection extends CharacterData {
constructor() {
super(...arguments);
this.nodeType = NODE_TYPE.CDATA_SECTION_NODE;
this.nodeName = "#cdata-section";
}
}
exports.CDATASection = CDATASection;
class Comment extends CharacterData {
constructor() {
super(...arguments);
this.nodeType = NODE_TYPE.COMMENT_NODE;
this.nodeName = "#comment";
}
}
exports.Comment = Comment;
class DocumentFragment extends Node {
constructor() {
super(...arguments);
this.nodeName = '#document-fragment';
this.childNodes = new NodeList();
}
}
exports.DocumentFragment = DocumentFragment;
class DocumentType extends Node {
// readonly attribute NamedNodeMap entities;
// readonly attribute NamedNodeMap notations;
/**
* constructor function
* @constructor
* @param {String} qualifiedName
* @param {String} publicId
* @param {String} systemId
*/
constructor(doc, qualifiedName, publicId, systemId, internalSubset) {
// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
super(doc);
this.nodeType = NODE_TYPE.DOCUMENT_TYPE_NODE;
this.name = qualifiedName;
this.nodeName = qualifiedName;
this.publicId = publicId;
this.systemId = systemId;
this.internalSubset = internalSubset;
}
}
exports.DocumentType = DocumentType;
class Entity extends Node {
constructor() {
super(...arguments);
this.nodeType = NODE_TYPE.ENTITY_NODE;
this.nodeName = "#entity";
}
}
exports.Entity = Entity;
class EntityReference extends Node {
constructor(doc, nodeName, nodeValue) {
super(doc);
this.nodeType = NODE_TYPE.ENTITY_REFERENCE_NODE;
this.nodeName = nodeName;
this.nodeValue = nodeValue;
}
get text() { return this.nodeValue; }
}
exports.EntityReference = EntityReference;
class Notation extends Node {
constructor() {
super(...arguments);
this.nodeType = NODE_TYPE.NOTATION_NODE;
this.nodeName = "#notation";
}
}
exports.Notation = Notation;
class ProcessingInstruction extends Node {
constructor(doc, name, data) {
super(doc);
this.nodeType = NODE_TYPE.PROCESSING_INSTRUCTION_NODE;
this.name = name;
this.data = data;
}
get nodeName() { return this.name; }
}
exports.ProcessingInstruction = ProcessingInstruction;
class Text extends CharacterData {
constructor() {
super(...arguments);
this.nodeName = "#text";
this.nodeType = NODE_TYPE.TEXT_NODE;
}
splitText(offset) {
var text = this.data;
var newText = text.substring(offset);
text = text.substring(0, offset);
this.data = text;
this.length = text.length;
var newNode = this.ownerDocument.createTextNode(newText);
if (this.parentNode) {
this.parentNode.insertBefore(newNode, this.nextSibling || null);
}
return newNode;
}
}
exports.Text = Text;