browser-x
Version:
A partial implementation of the W3C DOM API on top of an HTML5 parser and serializer.
192 lines (164 loc) • 5.36 kB
JavaScript
'use strict';
var VError = require('verror');
var path = require('path');
var cssom = require('./style');
var Attr = require('./attr');
var Comment = require('./comment');
var Element = require('./element');
var Node = require('./node');
var Text = require('./text');
var Window = require('./window');
var StyleSheetList = require('./style-sheet-list');
var createElement = require('./elements').create;
function Document(options) {
Node.call(this, this, '#document', null, Node.DOCUMENT_NODE);
this.defaultView = new Window(this);
this._doctype = null;
this._documentElement = null;
this._styleSheets = null;
this._options = options;
this._url = options.url;
// TODO about:blank
if (!/^https?\:/.test(this._url)) {
this._url = path.resolve(this._url);
}
}
Document.prototype = Object.create(Node.prototype, {
URL: {
get: function() {
// TODO
return this._url;
}
},
doctype: {
get: function() {
return this._doctype;
},
set: function(doctype) {
// 设置一次后就不许修改了
if (!this._doctype) {
this._doctype = doctype;
}
}
},
compatMode: {
get: function() {
return 'CSS1Compat';
}
},
documentElement: {
get: function() {
if (this._documentElement) {
return this._documentElement;
} else {
var child = this._first;
while (child) {
if (child.nodeType === Node.ELEMENT_NODE) {
return this._documentElement = child;
}
child = child._next;
}
return null;
}
}
},
head: {
get: function() {
return this.getElementsByTagName('head').item(0);
}
},
title: {
get: function() {
return this.getElementsByTagName('title').item(0).textContent;
}
},
body: {
get: function() {
return this.getElementsByTagName('body').item(0);
}
},
styleSheets: {
get: function() {
var silent = this._options.silent;
if (!this._styleSheets) {
this._styleSheets = new StyleSheetList();
// TODO test media
var nodeList = this.querySelectorAll('style,link[rel=stylesheet]:not([disabled])');
for (var i = 0; i < nodeList.length; i++) {
var ownerNode = nodeList.item(i);
var textContent = ownerNode.textContent;
var cssStyleSheet = cssParse(textContent, this.URL, silent);
if (ownerNode.nodeName === 'LINK') {
cssStyleSheet.cssRules = null;
cssStyleSheet.href = ownerNode.href;
} else {
cssStyleSheet.href = null;
}
cssStyleSheet.ownerNode = ownerNode;
this._styleSheets.push(cssStyleSheet);
}
}
return this._styleSheets;
function cssParse(data, file, silent) {
try {
return cssom.parse(data);
} catch (errors) {
if (!silent) {
throw new VError(errors, 'parse "%s" failed. <style>%s</style>', file, data);
}
return cssom.parse('');
}
}
}
}
});
Document.prototype.constructor = Document;
Document.prototype.createElement = function(tagName) {
var namespaceURI = this.documentElement.namespaceURI;
return this.createElementNS(namespaceURI, tagName);
};
Document.prototype.createElementNS = createElement;
Document.prototype.createDocumentFragment = function() {
throw new Error('not yet implemented');
};
Document.prototype.createTextNode = function(data) {
return new Text(this, data);
};
Document.prototype.createComment = function(data) {
return new Comment(this, data);
};
Document.prototype.createAttribute = function(name) {
return new Attr(this, name, false, '');
};
Document.prototype.getElementsByTagName = function(tagName) {
return Element.prototype.getElementsByTagName.call(this, tagName);
};
// TODO restrict to just Element types
// TODO 性能优化
Document.prototype.getElementById = function(id) {
var child = this.firstChild;
out: while (child) {
if (child.id === id) {
return child;
}
if (child.firstChild) {
child = child.firstChild;
} else if (child.nextSibling) {
child = child.nextSibling;
} else {
do {
child = child.parentNode;
if (child === this) break out;
} while (!child.nextSibling);
child = child.nextSibling;
}
}
return null;
};
Document.prototype.querySelector = function(selector) {
return Element.prototype.querySelector.call(this, selector);
};
Document.prototype.querySelectorAll = function(selector) {
return Element.prototype.querySelectorAll.call(this, selector);
};
module.exports = Document;