UNPKG

netscape-bookmark-parser

Version:

A TypeScript/JavaScript library for parsing browser bookmark files (HTML format) and manipulating them as structured data. Compatible with both Deno and Node.js runtimes.

323 lines (322 loc) 11.7 kB
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _DocumentType_qualifiedName, _DocumentType_publicId, _DocumentType_systemId, _Document_documentURI, _Document_nwapi; import { CTOR_KEY } from "../constructor-lock.js"; import { Comment, Node, NodeType, Text } from "./node.js"; import { NodeList, nodeListMutatorSym } from "./node-list.js"; import { Element } from "./element.js"; import { DocumentFragment } from "./document-fragment.js"; import { HTMLTemplateElement } from "./elements/html-template-element.js"; import { getSelectorEngine } from "./selectors/selectors.js"; import { getElementsByClassName } from "./utils.js"; import UtilTypes from "./utils-types.js"; import { getUpperCase } from "./string-cache.js"; export class DOMImplementation { constructor(key) { if (key !== CTOR_KEY) { throw new TypeError("Illegal constructor."); } } createDocument() { throw new Error("Unimplemented"); // TODO } createHTMLDocument(titleStr) { titleStr += ""; const doc = new HTMLDocument(CTOR_KEY); const docType = new DocumentType("html", "", "", CTOR_KEY); doc.appendChild(docType); const html = new Element("html", doc, [], CTOR_KEY); html._setOwnerDocument(doc); const head = new Element("head", html, [], CTOR_KEY); const body = new Element("body", html, [], CTOR_KEY); const title = new Element("title", head, [], CTOR_KEY); const titleText = new Text(titleStr); title.appendChild(titleText); doc.head = head; doc.body = body; return doc; } createDocumentType(qualifiedName, publicId, systemId) { const doctype = new DocumentType(qualifiedName, publicId, systemId, CTOR_KEY); return doctype; } } export class DocumentType extends Node { constructor(name, publicId, systemId, key) { super("html", NodeType.DOCUMENT_TYPE_NODE, null, key); _DocumentType_qualifiedName.set(this, ""); _DocumentType_publicId.set(this, ""); _DocumentType_systemId.set(this, ""); __classPrivateFieldSet(this, _DocumentType_qualifiedName, name, "f"); __classPrivateFieldSet(this, _DocumentType_publicId, publicId, "f"); __classPrivateFieldSet(this, _DocumentType_systemId, systemId, "f"); } get name() { return __classPrivateFieldGet(this, _DocumentType_qualifiedName, "f"); } get publicId() { return __classPrivateFieldGet(this, _DocumentType_publicId, "f"); } get systemId() { return __classPrivateFieldGet(this, _DocumentType_systemId, "f"); } _shallowClone() { return new DocumentType(__classPrivateFieldGet(this, _DocumentType_qualifiedName, "f"), __classPrivateFieldGet(this, _DocumentType_publicId, "f"), __classPrivateFieldGet(this, _DocumentType_systemId, "f"), CTOR_KEY); } } _DocumentType_qualifiedName = new WeakMap(), _DocumentType_publicId = new WeakMap(), _DocumentType_systemId = new WeakMap(); export class Document extends Node { constructor() { super("#document", NodeType.DOCUMENT_NODE, null, CTOR_KEY); Object.defineProperty(this, "head", { enumerable: true, configurable: true, writable: true, value: null }); Object.defineProperty(this, "body", { enumerable: true, configurable: true, writable: true, value: null }); Object.defineProperty(this, "implementation", { enumerable: true, configurable: true, writable: true, value: void 0 }); _Document_documentURI.set(this, "about:blank"); // TODO _Document_nwapi.set(this, null); this.implementation = new DOMImplementation(CTOR_KEY); } _shallowClone() { return new Document(); } // Expose the document's NWAPI for Element's access to // querySelector/querySelectorAll get _nwapi() { return __classPrivateFieldGet(this, _Document_nwapi, "f") || (__classPrivateFieldSet(this, _Document_nwapi, getSelectorEngine()(this), "f")); } get documentURI() { return __classPrivateFieldGet(this, _Document_documentURI, "f"); } get title() { return this.querySelector("title")?.textContent || ""; } set title(value) { let titleElement = this.querySelector("title"); if (!titleElement) { const { head } = this; if (!head) return; titleElement = this.createElement("title"); head.appendChild(titleElement); } titleElement.textContent = value; } get cookie() { return ""; // TODO } set cookie(newCookie) { // TODO } get visibilityState() { return "visible"; } get hidden() { return false; } get compatMode() { return "CSS1Compat"; } get documentElement() { for (const node of this.childNodes) { if (node.nodeType === NodeType.ELEMENT_NODE) { return node; } } return null; } get doctype() { for (const node of this.childNodes) { if (node.nodeType === NodeType.DOCUMENT_TYPE_NODE) { return node; } } return null; } get childElementCount() { let count = 0; for (const { nodeType } of this.childNodes) { if (nodeType === NodeType.ELEMENT_NODE) { count++; } } return count; } appendChild(child) { super.appendChild(child); child._setOwnerDocument(this); return child; } createElement(tagName, options) { tagName = getUpperCase(tagName); switch (tagName) { case "TEMPLATE": { const frag = new DocumentFragment(); const elm = new HTMLTemplateElement(null, [], CTOR_KEY, frag); elm._setOwnerDocument(this); return elm; } default: { const elm = new Element(tagName, null, [], CTOR_KEY); elm._setOwnerDocument(this); return elm; } } } createElementNS(namespace, qualifiedName, options) { if (namespace === "http://www.w3.org/1999/xhtml") { return this.createElement(qualifiedName, options); } else { throw new Error(`createElementNS: "${namespace}" namespace unimplemented`); // TODO } } createTextNode(data) { return new Text(data); } createComment(data) { return new Comment(data); } createDocumentFragment() { const fragment = new DocumentFragment(); fragment._setOwnerDocument(this); return fragment; } importNode(node, deep = false) { const copy = node.cloneNode(deep); copy._setOwnerDocument(this); return copy; } adoptNode(node) { if (node instanceof Document) { throw new DOMException("Adopting a Document node is not supported.", "NotSupportedError"); } node._setParent(null); node._setOwnerDocument(this); return node; } // FIXME: This is a bad solution. The correct solution // would be to make `.body` and `.head` dynamic getters, // but that would be a breaking change since `.body` // and `.head` would need to be typed as `Element | null`. // Currently they're typed as `Element` which is incorrect... cloneNode(deep) { const doc = super.cloneNode(deep); for (const child of doc.documentElement?.childNodes || []) { switch (child.nodeName) { case "BODY": { doc.body = child; break; } case "HEAD": { doc.head = child; break; } } } return doc; } querySelector(selectors) { return this._nwapi.first(selectors, this); } querySelectorAll(selectors) { const nodeList = new NodeList(); const mutator = nodeList[nodeListMutatorSym](); for (const match of this._nwapi.select(selectors, this)) { mutator.push(match); } return nodeList; } // TODO: DRY!!! getElementById(id) { if (!this._hasInitializedChildNodes()) { return null; } for (const child of this.childNodes) { if (child.nodeType === NodeType.ELEMENT_NODE) { if (child.id === id) { return child; } const search = child.getElementById(id); if (search) { return search; } } } return null; } getElementsByTagName(tagName) { if (tagName === "*") { return this.documentElement ? this._getElementsByTagNameWildcard(this.documentElement, []) : []; } else { return this._getElementsByTagName(getUpperCase(tagName), []); } } _getElementsByTagNameWildcard(node, search) { for (const child of this.childNodes) { if (child.nodeType === NodeType.ELEMENT_NODE) { search.push(child); child._getElementsByTagNameWildcard(search); } } return search; } _getElementsByTagName(tagName, search) { for (const child of this.childNodes) { if (child.nodeType === NodeType.ELEMENT_NODE) { if (child.tagName === tagName) { search.push(child); } child._getElementsByTagName(tagName, search); } } return search; } getElementsByTagNameNS(_namespace, localName) { return this.getElementsByTagName(localName); } getElementsByClassName(className) { return getElementsByClassName(this, className.trim().split(/\s+/), []); } hasFocus() { return true; } } _Document_documentURI = new WeakMap(), _Document_nwapi = new WeakMap(); export class HTMLDocument extends Document { constructor(key) { if (key !== CTOR_KEY) { throw new TypeError("Illegal constructor."); } super(); } _shallowClone() { return new HTMLDocument(CTOR_KEY); } } UtilTypes.Document = Document;