UNPKG

jsdom-jscore-rn

Version:

A fork of brentvatne/jsdom-jscore to run within react-native

298 lines (250 loc) 7.79 kB
var HtmlToDom = require('./htmltodom').HtmlToDom, domToHtml = require('./domtohtml').domToHtml, NOT_IMPLEMENTED = require('./utils').NOT_IMPLEMENTED, defineGetter = require('../utils').defineGetter, defineSetter = require('../utils').defineSetter; //Caching for HTMLParser require. HUGE performace boost. /** * 5000 iterations * Without cache: ~1800+ms * With cache: ~80ms */ var defaultParser = null; var getDefaultParser = exports.getDefaultParser = function () { if (defaultParser === null) { defaultParser = require('../../builtins').htmlparser2; } return defaultParser; }; /** * Export getter/setter of default parser to facilitate testing * with different HTML parsers. */ exports.setDefaultParser = function (parser) { if (typeof parser == 'object') { defaultParser = parser; } else if (typeof parser == 'string') {defaultParser = require(parser);} }; /** * Augments the given DOM by adding browser-specific properties and methods (BOM). * Returns the augmented DOM. */ exports.browserAugmentation = function(dom, options) { if(!options) { options = {}; } // set up html parser - use a provided one or try and load from library var parser = options.parser || getDefaultParser(); if (dom._augmented && dom._parser === parser) { return dom; } dom._parser = parser; var htmltodom = new HtmlToDom(parser); if (!dom.HTMLDocument) { dom.HTMLDocument = dom.Document; } if (!dom.HTMLDocument.prototype.write) { dom.HTMLDocument.prototype.write = function(html) { this.innerHTML = html; }; } dom.Element.prototype.getElementsByClassName = function(className) { function filterByClassName(child) { if (!child) { return false; } if (child.nodeType && child.nodeType === dom.Node.ENTITY_REFERENCE_NODE) { child = child._entity; } var classString = child.className; if (classString) { var s = classString.split(' '); for (var i=0; i<s.length; i++) { if (s[i] === className) { return true; } } } return false; } return new dom.NodeList(this.ownerDocument || this, dom.mapper(this, filterByClassName)); }; defineGetter(dom.Element.prototype, 'sourceIndex', function() { /* * According to QuirksMode: * Get the sourceIndex of element x. This is also the index number for * the element in the document.getElementsByTagName('*') array. * http://www.quirksmode.org/dom/w3c_core.html#t77 */ var items = this.ownerDocument.getElementsByTagName('*'), len = items.length; for (var i = 0; i < len; i++) { if (items[i] === this) { return i; } } }); defineGetter(dom.Document.prototype, 'outerHTML', function() { return domToHtml(this, true); }); defineGetter(dom.Element.prototype, 'outerHTML', function() { return domToHtml(this, true); }); defineGetter(dom.Element.prototype, 'innerHTML', function() { if (/^(?:script|style)$/.test(this._tagName)) { var type = this.getAttribute('type'); if (!type || /^text\//i.test(type) || /\/javascript$/i.test(type)) { return domToHtml(this._childNodes, true, true); } } return domToHtml(this._childNodes, true); }); defineSetter(dom.Element.prototype, 'doctype', function() { throw new dom.DOMException(dom.NO_MODIFICATION_ALLOWED_ERR); }); defineGetter(dom.Element.prototype, 'doctype', function() { var r = null; if (this.nodeName == '#document') { if (this._doctype) { r = this._doctype; } } return r; }); defineSetter(dom.Element.prototype, 'innerHTML', function(html) { //Clear the children first: var child; while (child = this._childNodes[0]) { this.removeChild(child); } if (this.nodeName === '#document') { parseDocType(this, html); } if (html !== '' && html != null) { htmltodom.appendHtmlToElement(html, this); } return html; }); defineGetter(dom.Document.prototype, 'innerHTML', function() { return domToHtml(this._childNodes, true); }); defineSetter(dom.Document.prototype, 'innerHTML', function(html) { //Clear the children first: var child; while (child = this._childNodes[0]) { this.removeChild(child); } if (this.nodeName === '#document') { parseDocType(this, html); } if (html !== '' && html != null) { htmltodom.appendHtmlToElement(html, this); } return html; }); var DOC_HTML5 = /<!doctype html>/i, DOC_TYPE = /<!DOCTYPE (\w(.|\n)*)">/i, DOC_TYPE_START = '<!DOCTYPE ', DOC_TYPE_END = '">'; function parseDocType(doc, html) { var publicID = '', systemID = '', fullDT = '', name = 'HTML', doctype = html.match(DOC_HTML5); //Default, No doctype === null doc._doctype = null; if (doctype && doctype[0]) { //Handle the HTML shorty doctype fullDT = doctype[0]; } else { //Parse the doctype // find the start var start = html.indexOf(DOC_TYPE_START), end = html.indexOf(DOC_TYPE_END), docString; if (start < 0 || end < 0) { return; } docString = html.substr(start, end-start+DOC_TYPE_END.length); doctype = docString.replace(/[\n\r]/g,'').match(DOC_TYPE); if (!doctype) { return; } fullDT = doctype[0]; doctype = doctype[1].split(' "'); var _id1 = doctype.length ? doctype.pop().replace(/"/g, '') : '', _id2 = doctype.length ? doctype.pop().replace(/"/g, '') : ''; if (_id1.indexOf('-//') !== -1) { publicID = _id1; } if (_id2.indexOf('-//') !== -1) { publicID = _id2; } if (_id1.indexOf('://') !== -1) { systemID = _id1; } if (_id2.indexOf('://') !== -1) { systemID = _id2; } if (doctype.length) { doctype = doctype[0].split(' '); name = doctype[0].toUpperCase(); } } doc._doctype = new dom.DOMImplementation().createDocumentType(name, publicID, systemID); doc._doctype._ownerDocument = doc; doc._doctype._fullDT = fullDT; doc._doctype.toString = function() { return this._fullDT; }; } dom.Document.prototype.getElementsByClassName = function(className) { function filterByClassName(child) { if (!child) { return false; } if (child.nodeType && child.nodeType === dom.Node.ENTITY_REFERENCE_NODE) { child = child._entity; } var classString = child.className; if (classString) { var s = classString.split(' '); for (var i=0; i<s.length; i++) { if (s[i] === className) { return true; } } } return false; } return new dom.NodeList(this.ownerDocument || this, dom.mapper(this, filterByClassName)); }; defineGetter(dom.Element.prototype, 'nodeName', function(val) { return this._nodeName.toUpperCase(); }); defineGetter(dom.Element.prototype, 'tagName', function(val) { var t = this._tagName.toUpperCase(); //Document should not return a tagName if (this.nodeName === '#document') { t = null; } return t; }); dom.Element.prototype.scrollTop = 0; dom.Element.prototype.scrollLeft = 0; defineGetter(dom.Document.prototype, 'parentWindow', function() { return null; }); defineSetter(dom.Document.prototype, 'parentWindow', function(window) { NOT_IMPLEMENTED(null,'parentWindow'); }); defineGetter(dom.Document.prototype, 'defaultView', function() { return this.parentWindow; }); dom._augmented = true; return dom; };