jsdom-jscore-rn
Version:
A fork of brentvatne/jsdom-jscore to run within react-native
298 lines (250 loc) • 7.79 kB
JavaScript
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;
};