Plato on Github
Report Home
node_modules/jsdom/lib/jsdom/level1/core.js
Maintainability
69.53
Lines of code
2100
Difficulty
225.92
Estimated Errors
23.42
Function weight
By Complexity
By SLOC
/* ServerJS Javascript DOM Level 1 */ var inheritFrom = require("../utils").inheritFrom; // utility functions var attachId = function(id,elm,doc) { if (id && elm && doc) { if (!doc._ids[id]) { doc._ids[id] = []; } doc._ids[id].push(elm); } }; var detachId = function(id,elm,doc) { var elms, i; if (id && elm && doc) { if (doc._ids && doc._ids[id]) { elms = doc._ids[id]; for (i=0;i<elms.length;i++) { if (elms[i] === elm) { elms.splice(i,1); i--; } } if (elms.length === 0) { delete doc._ids[id]; } } } }; var core = { mapper: function(parent, filter, recursive) { return function() { return core.mapDOMNodes(parent, recursive !== false, filter); }; }, memoizeQuery: function(fn) { var type = core.memoizeQueryType = core.memoizeQueryType || 1; core.memoizeQueryType++; // Only memoize query functions with arity <= 2 if (fn.length > 2) { return fn; } return function() { if (!this._memoizedQueries) { return fn.apply(this, arguments); } if (!this._memoizedQueries[type]) { this._memoizedQueries[type] = {}; } if (arguments.length !== 0 && typeof arguments[0] === 'string' && (arguments.length === 1 || (typeof arguments[1] === 'string' && arguments.length === 2))) { var k = String(arguments[0]); if (arguments.length === 2) { k += '::' + String(arguments[1]); } if (this._memoizedQueries[type].hasOwnProperty(k)) { return this._memoizedQueries[type][k]; } return (this._memoizedQueries[type][k] = fn.apply(this, arguments)); } return fn.apply(this, arguments); } }, // Returns Array mapDOMNodes : function(parent, recursive, callback) { function visit(parent, result) { return Array.prototype.reduce.call(parent.childNodes, reducer, result); } function reducer(array, child) { if (callback(child)) { array.push(child); } if (recursive && child._childNodes) { visit(child, array); } return array; } return visit(parent, []); }, visitTree: function(root, callback) { var cur = root; // TODO: Unroll this. function visit(el) { if (el) { callback(el); if (el._childNodes) { var i = 0, children = el._childNodes, l = children.length; for (i; i<l; i++) { visit(children[i]); } } } } visit(root); }, markTreeReadonly: function(node) { function markLevel(el) { el._readonly = true; // also mark attributes and their children read-only if (el.attributes) { var attributes = el.attributes, l = attributes.length, i=0; attributes._readonly = true; for (i; i<l; i++) { core.visitTree(attributes[i], markLevel); } } } core.visitTree(node, markLevel); } }; // ExceptionCode var INDEX_SIZE_ERR = core.INDEX_SIZE_ERR = 1, DOMSTRING_SIZE_ERR = core.DOMSTRING_SIZE_ERR = 2, HIERARCHY_REQUEST_ERR = core.HIERARCHY_REQUEST_ERR = 3, WRONG_DOCUMENT_ERR = core.WRONG_DOCUMENT_ERR = 4, INVALID_CHARACTER_ERR = core.INVALID_CHARACTER_ERR = 5, NO_DATA_ALLOWED_ERR = core.NO_DATA_ALLOWED_ERR = 6, NO_MODIFICATION_ALLOWED_ERR = core.NO_MODIFICATION_ALLOWED_ERR = 7, NOT_FOUND_ERR = core.NOT_FOUND_ERR = 8, NOT_SUPPORTED_ERR = core.NOT_SUPPORTED_ERR = 9, INUSE_ATTRIBUTE_ERR = core.INUSE_ATTRIBUTE_ERR = 10, // Node Types ELEMENT_NODE = 1, ATTRIBUTE_NODE = 2, TEXT_NODE = 3, CDATA_SECTION_NODE = 4, ENTITY_REFERENCE_NODE = 5, ENTITY_NODE = 6, PROCESSING_INSTRUCTION_NODE = 7, COMMENT_NODE = 8, DOCUMENT_NODE = 9, DOCUMENT_TYPE_NODE = 10, DOCUMENT_FRAGMENT_NODE = 11, NOTATION_NODE = 12; var messages = core.exceptionMessages = { }; messages[INDEX_SIZE_ERR] = "Index size error"; messages[DOMSTRING_SIZE_ERR] = "DOMString size error"; messages[HIERARCHY_REQUEST_ERR] = "Hierarchy request error"; messages[WRONG_DOCUMENT_ERR] = "Wrong document"; messages[INVALID_CHARACTER_ERR] = "Invalid character"; messages[NO_DATA_ALLOWED_ERR] = "No data allowed"; messages[NO_MODIFICATION_ALLOWED_ERR] = "No modification allowed"; messages[NOT_FOUND_ERR] = "Not found"; messages[NOT_SUPPORTED_ERR] = "Not supported"; messages[INUSE_ATTRIBUTE_ERR] = "Attribute in use"; core.DOMException = function(code, message) { this.code = code; Error.call(this, core.exceptionMessages[code]); this.message = core.exceptionMessages[code]; if(message) this.message = this.message + ": " + message; if(Error.captureStackTrace) Error.captureStackTrace(this, core.DOMException); }; core.DOMException.INDEX_SIZE_ERR = INDEX_SIZE_ERR; core.DOMException.DOMSTRING_SIZE_ERR = DOMSTRING_SIZE_ERR; core.DOMException.HIERARCHY_REQUEST_ERR = HIERARCHY_REQUEST_ERR; core.DOMException.WRONG_DOCUMENT_ERR = WRONG_DOCUMENT_ERR; core.DOMException.INVALID_CHARACTER_ERR = INVALID_CHARACTER_ERR; core.DOMException.NO_DATA_ALLOWED_ERR = NO_DATA_ALLOWED_ERR; core.DOMException.NO_MODIFICATION_ALLOWED_ERR = NO_MODIFICATION_ALLOWED_ERR; core.DOMException.NOT_FOUND_ERR = NOT_FOUND_ERR; core.DOMException.NOT_SUPPORTED_ERR = NOT_SUPPORTED_ERR; core.DOMException.INUSE_ATTRIBUTE_ERR = INUSE_ATTRIBUTE_ERR; inheritFrom(Error, core.DOMException, { INDEX_SIZE_ERR : INDEX_SIZE_ERR, DOMSTRING_SIZE_ERR : DOMSTRING_SIZE_ERR, HIERARCHY_REQUEST_ERR : HIERARCHY_REQUEST_ERR, WRONG_DOCUMENT_ERR : WRONG_DOCUMENT_ERR, INVALID_CHARACTER_ERR : INVALID_CHARACTER_ERR, NO_DATA_ALLOWED_ERR : NO_DATA_ALLOWED_ERR, NO_MODIFICATION_ALLOWED_ERR : NO_MODIFICATION_ALLOWED_ERR, NOT_FOUND_ERR : NOT_FOUND_ERR, NOT_SUPPORTED_ERR : NOT_SUPPORTED_ERR, INUSE_ATTRIBUTE_ERR : INUSE_ATTRIBUTE_ERR }); core.NodeList = function NodeList(element, query) { if (!query) { // Non-live NodeList if (Array.isArray(element)) { Array.prototype.push.apply(this, element); } Object.defineProperties(this, { _length: {value: element ? element.length : 0, writable:true} }); } else { Object.defineProperties(this, { _element: {value: element}, _query: {value: query}, _snapshot: {writable: true}, _length: {value: 0, writable: true}, _version: {value: -1, writable: true} }); this._update(); } }; function lengthFromProperties(arrayLike) { var max = -1; for (var i in arrayLike) { var asNumber = +i; if (!isNaN(asNumber) && asNumber > max) { max = asNumber; } } return max + 1; } core.NodeList.prototype = { _update: function() { var i; if (!this._element) { this._length = lengthFromProperties(this); } else { if (this._version < this._element._version) { var nodes = this._snapshot = this._query(); this._resetTo(nodes); this._version = this._element._version; } } }, _resetTo: function(array) { var startingLength = lengthFromProperties(this); for (var i = 0; i < startingLength; ++i) { delete this[i]; } for (var j = 0; j < array.length; ++j) { this[j] = array[j]; } this._length = array.length; }, _toArray: function() { if (this._element) { this._update(); return this._snapshot; } return Array.prototype.slice.call(this); }, get length() { this._update(); return this._length || 0; }, set length(length) { return this._length; }, item: function(index) { this._update(); return this[index] || null; }, toString: function() { return '[ jsdom NodeList ]: contains ' + this.length + ' items'; } }; Object.defineProperty(core.NodeList.prototype, 'constructor', { value: core.NodeList, writable: true, configurable: true }); core.DOMImplementation = function DOMImplementation(document, /* Object */ features) { this._ownerDocument = document; this._features = {}; if (features) { for (var feature in features) { if (features.hasOwnProperty(feature)) { this.addFeature(feature.toLowerCase(), features[feature]); } } } }; core.DOMImplementation.prototype = { get ownerDocument() { return this._ownerDocument }, removeFeature : function(feature, version) { feature = feature.toLowerCase(); if (this._features[feature]) { if (version) { var j = 0, versions = this._features[feature], l = versions.length; for (j; j<l; j++) { if (versions[j] === version) { versions.splice(j,1); return; } } } else { delete this._features[feature]; } } }, addFeature: function(feature, version) { feature = feature.toLowerCase(); if (version) { if (!this._features[feature]) { this._features[feature] = []; } if (version instanceof Array) { Array.prototype.push.apply(this._features[feature], version); } else { this._features[feature].push(version); } } }, hasFeature: function(/* string */ feature, /* string */ version) { feature = (feature) ? feature.toLowerCase() : ''; var versions = (this._features[feature]) ? this._features[feature] : false; if (!version && versions.length && versions.length > 0) { return true; } else if (typeof versions === 'string') { return versions === version; } else if (versions.indexOf && versions.length > 0) { for (var i = 0; i < versions.length; i++) { var found = versions[i] instanceof RegExp ? versions[i].test(version) : versions[i] === version; if (found) { return true; } } return false; } else { return false; } } }; var attrCopy = function(src, dest, fn) { if (src.attributes) { var attrs = src.attributes, i, l = attrs.length, attr, copied; for (i=0;i<l;i++) { attr = attrs[i]; // skip over default attributes if (!attr.specified) { continue; } // TODO: consider duplicating this code and moving it into level2/core if (attr.namespaceURI) { dest.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.nodeValue); var localName = attr.nodeName.split(':').pop(); copied = dest.getAttributeNodeNS(attr.namespaceURI, localName); } else { dest.setAttribute(attr.nodeName, attr.nodeValue); copied = dest.getAttributeNode(attr.nodeName); } if (typeof fn == "function") { fn(attr, copied); } } } return dest; }; core.Node = function Node(ownerDocument) { this._childNodes = new core.NodeList(); this._ownerDocument = ownerDocument; this._attributes = new AttributeList(ownerDocument, this); this._nodeName = null; this._childrenList = null; this._version = 0; this._nodeValue = null; this._parentNode = null; this._nodeName = null; this._memoizedQueries = {}; this._readonly = false; }; core.Node.ELEMENT_NODE = ELEMENT_NODE; core.Node.ATTRIBUTE_NODE = ATTRIBUTE_NODE; core.Node.TEXT_NODE = TEXT_NODE; core.Node.CDATA_SECTION_NODE = CDATA_SECTION_NODE; core.Node.ENTITY_REFERENCE_NODE = ENTITY_REFERENCE_NODE; core.Node.ENTITY_NODE = ENTITY_NODE; core.Node.PROCESSING_INSTRUCTION_NODE = PROCESSING_INSTRUCTION_NODE; core.Node.COMMENT_NODE = COMMENT_NODE; core.Node.DOCUMENT_NODE = DOCUMENT_NODE; core.Node.DOCUMENT_TYPE_NODE = DOCUMENT_TYPE_NODE; core.Node.DOCUMENT_FRAGMENT_NODE = DOCUMENT_FRAGMENT_NODE; core.Node.NOTATION_NODE = NOTATION_NODE; core.Node.prototype = { ELEMENT_NODE : ELEMENT_NODE, ATTRIBUTE_NODE : ATTRIBUTE_NODE, TEXT_NODE : TEXT_NODE, CDATA_SECTION_NODE : CDATA_SECTION_NODE, ENTITY_REFERENCE_NODE : ENTITY_REFERENCE_NODE, ENTITY_NODE : ENTITY_NODE, PROCESSING_INSTRUCTION_NODE : PROCESSING_INSTRUCTION_NODE, COMMENT_NODE : COMMENT_NODE, DOCUMENT_NODE : DOCUMENT_NODE, DOCUMENT_TYPE_NODE : DOCUMENT_TYPE_NODE, DOCUMENT_FRAGMENT_NODE : DOCUMENT_FRAGMENT_NODE, NOTATION_NODE : NOTATION_NODE, get children() { if (!this._childrenList) { var self = this; this._childrenList = new core.NodeList(this, function() { return Array.prototype.filter.call(self._childNodes, function(node) { return node.tagName; }); }); } return this._childrenList; }, get nodeValue() { return this._nodeValue; }, set nodeValue(value) { // readonly if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR, 'Attempting to modify a read-only node'); } this._nodeValue = value; }, get parentNode() { return this._parentNode;}, get nodeName() { var name = this._nodeName || this._tagName; if (this.nodeType === ELEMENT_NODE && this._ownerDocument && this._ownerDocument._doctype && this._ownerDocument._doctype.name.toLowerCase().indexOf("html") !== -1) { return name.toUpperCase(); } return name; }, set nodeName(unused) { throw new core.DOMException();}, get attributes() { return this._attributes;}, get firstChild() { return this._childNodes.length > 0 ? this._childNodes[0] : null; }, set firstChild(unused) { throw new core.DOMException();}, get ownerDocument() { return this._ownerDocument;}, get readonly() { return this._readonly;}, get lastChild() { var len = this._childNodes.length; return len > 0 ? this._childNodes[len -1] : null; }, set lastChild(unused) { throw new core.DOMException();}, get childNodes() { return this._childNodes; }, set childNodes(unused) { throw new core.DOMException();}, _indexOf: function(/*Node*/ child) { if (!this._childNodes || !this._childNodes.length) { return -1; } var currentNode, index = 0, children = this._childNodes; while ((currentNode = children[index])) { if (currentNode == child) { break; } index++; } if (currentNode == child) { return index; } return -1; }, get nextSibling() { // find this node's index in the parentNode, add one and call it a day if (!this._parentNode || !this._parentNode._indexOf) { return null; } var index = this._parentNode._indexOf(this); if (index == -1 || index+1 >= this._parentNode._childNodes.length) { return null; } return this._parentNode._childNodes[index+1] || null; }, set nextSibling(unused) { throw new core.DOMException();}, get previousSibling() { if (!this._parentNode || !this._parentNode._indexOf) { return null; } var index = this._parentNode._indexOf(this); if (index == -1 || index-1 < 0) { return null; } return this._parentNode._childNodes[index-1] || null; }, set previousSibling(unused) { throw new core.DOMException();}, /* returns Node */ insertBefore : function(/* Node */ newChild, /* Node*/ refChild) { if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR, 'Attempting to modify a read-only node'); } // Adopt unowned children, for weird nodes like DocumentType if (!newChild._ownerDocument) newChild._ownerDocument = this._ownerDocument; // TODO - if (!newChild) then? if (newChild._ownerDocument !== this._ownerDocument) { throw new core.DOMException(WRONG_DOCUMENT_ERR); } if (newChild.nodeType && newChild.nodeType === ATTRIBUTE_NODE) { throw new core.DOMException(HIERARCHY_REQUEST_ERR); } // search for parents matching the newChild var current = this; do { if (current === newChild) { throw new core.DOMException(HIERARCHY_REQUEST_ERR); } } while((current = current._parentNode)); // fragments are merged into the element (except parser-created fragments in <template>) if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE && !newChild._templateContent) { var tmpNode, i = newChild._childNodes.length; while (i-- > 0) { tmpNode = newChild.removeChild(newChild.firstChild); this.insertBefore(tmpNode, refChild); } } else if (newChild === refChild) { return newChild; } else { // if the newChild is already in the tree elsewhere, remove it first if (newChild._parentNode) { newChild._parentNode.removeChild(newChild); } if (refChild == null) { var refChildIndex = this._childNodes.length; } else { var refChildIndex = this._indexOf(refChild); if (refChildIndex == -1) { throw new core.DOMException(NOT_FOUND_ERR); } } Array.prototype.splice.call(this._childNodes, refChildIndex, 0, newChild); newChild._parentNode = this; if (this._attached && newChild._attach) { newChild._attach(); } this._modified(); } return newChild; }, // raises(DOMException); _modified: function() { this._version++; if (this._ownerDocument) { this._ownerDocument._version++; } if (this._childrenList) { this._childrenList._update(); } this._clearMemoizedQueries() }, _clearMemoizedQueries: function() { this._memoizedQueries = {}; if (this._parentNode && this._parentNode !== this) { this._parentNode._clearMemoizedQueries(); } }, _attrModified: function(name, value, oldValue) { if (name == 'id' && this._attached) { var doc = this._ownerDocument; detachId(oldValue,this,doc); attachId(value,this,doc); } // Check for inline event handlers. // We can't set these like other attributes then look it up in // dispatchEvent() because that would create 2 'traditional' event handlers // in the case where there's an inline event handler attribute, plus one // set using element.on* in a script. // // @see http://www.w3.org/TR/2011/WD-html5-20110405/webappapis.html#event-handler-content-attributes if ((name.length > 2) && (name[0] == 'o') && (name[1] == 'n')) { if (value) { var self = this; // Check whether we're the window. This can happen because inline // handlers on the body are proxied to the window. var w = (typeof self.run !== 'undefined') ? self : self._ownerDocument.parentWindow; self[name] = function (event) { // The handler code probably refers to functions declared in the // window context, so we need to call run(). // Use awesome hacks to get the correct `this` context for the // inline event handler. This would only be necessary if we're an // element, but for the sake of simplicity we also do it on window. // Also set event variable and support `return false`. w.__tempContextForInlineEventHandler = self; w.__tempEvent = event; w.run("if ((function (event) {" + value + "}).call(" + "window.__tempContextForInlineEventHandler, window.__tempEvent) === false) {" + "window.__tempEvent.preventDefault()}"); delete w.__tempContextForInlineEventHandler; delete w.__tempEvent; }; } else { this[name] = null; } } }, /* returns Node */ replaceChild : function(/* Node */ newChild, /* Node */ oldChild){ this.insertBefore(newChild, oldChild); return this.removeChild(oldChild); }, //raises(DOMException); /* returns void */ _attach : function() { this._attached = true; if (this.id) { attachId(this.id,this,this._ownerDocument); } for (var i=0,len=this._childNodes.length;i<len;i++) { if (this._childNodes[i]._attach) { this._childNodes[i]._attach(); } } }, /* returns void */ _detach : function() { var i, elms; this._attached = false; if (this.id) { detachId(this.id,this,this._ownerDocument); } for (var i=0,len=this._childNodes.length;i<len;i++) { this._childNodes[i]._detach(); } }, /* returns Node */ removeChild : function(/* Node */ oldChild){ if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } // TODO - if (!oldChild) then? var oldChildIndex = this._indexOf(oldChild); if (oldChildIndex == -1) { throw new core.DOMException(NOT_FOUND_ERR); } Array.prototype.splice.call(this._childNodes, oldChildIndex, 1); oldChild._parentNode = null; this._modified(); oldChild._detach(); return oldChild; }, // raises(DOMException); /* returns Node */ appendChild : function(/* Node */ newChild) { return this.insertBefore(newChild, null); }, // raises(DOMException); /* returns boolean */ hasChildNodes : function() { return this._childNodes.length > 0; }, /* returns Node */ cloneNode : function(/* bool */ deep, fn) { var object = null; switch (this.nodeType) { case this.ELEMENT_NODE: object = attrCopy(this,this._ownerDocument.createElement(this.tagName), fn); break; case this.TEXT_NODE: object = attrCopy(this,this._ownerDocument.createTextNode(this.tagName)); object.nodeValue = this.nodeValue; break; case this.CDATA_SECTION_NODE: object = this._ownerDocument.createCDATASection(this.tagName); object.nodeValue = this.nodeValue; break; case this.ENTITY_REFERENCE_NODE: var name = (this._entity) ? this._entity.name : this._entityName, ref = this._ownerDocument.createEntityReference(name); object = attrCopy(this, ref); object.nodeValue = this.nodeValue; break; case this.ATTRIBUTE_NODE: object = this._ownerDocument.createAttribute(this.name); break; case this.ENTITY_NODE: var entity = this._ownerDocument.createEntityNode(this.name); object = attrCopy(this, entity); object.nodeValue = this.nodeValue; object._publicId = this._publicId; object._systemId = this._systemId; object._notationName = this.notationName; break; case this.PROCESSING_INSTRUCTION_NODE: var pi = this._ownerDocument.createProcessingInstruction(this._target, this._data); object = attrCopy(this, pi); object.nodeValue = this.nodeValue; break; case this.COMMENT_NODE: object = this._ownerDocument.createComment(this.tagName); object.nodeValue = this.nodeValue; break; case this.DOCUMENT_NODE: object = attrCopy(this, new core.Document()); // TODO: clone the doctype/entities/notations/etc? break; case this.DOCUMENT_TYPE_NODE: object = attrCopy(this, new core.DocumentType()); object.nodeValue = this.nodeValue; break; case this.DOCUMENT_FRAGMENT_NODE: object = this._ownerDocument.createDocumentFragment(); break; case this.NOTATION_NODE: object = this._ownerDocument.createNotationNode(this._name, this._publicId, this._systemId); object = attrCopy(this,object); object.nodeValue = this.nodeValue; break; default: throw new core.DOMException(NOT_FOUND_ERR); break; } if (typeof fn === "function") { fn(this, object); } if (deep || this.nodeType === ATTRIBUTE_NODE) { var clone = null; for (var i=0,len=this._childNodes.length;i<len;i++) { clone = this._childNodes[i].cloneNode(true); if (clone.nodeType === ATTRIBUTE_NODE) { object.setAttributeNode(clone); } else { var readonly = object._readonly; object._readonly = false; object.appendChild(clone); object._readonly = readonly; } } } return object; }, /* returns void */ normalize: function() { var prevChild, child, attr,i; if (this._attributes && this._attributes.length) { for (i=0;i<this._attributes.length;i++) { if (this._attributes[i]) { attr = this._attributes[i].normalize(); } } } for (i=0;i<this._childNodes.length;i++) { child = this._childNodes[i]; if (child.normalize) { child.normalize(); } // Level2/core clean off empty nodes if (child.nodeValue === "") { this.removeChild(child); i--; continue; } if (i>0) { prevChild = this._childNodes[i-1]; if (child.nodeType === TEXT_NODE && prevChild.nodeType === TEXT_NODE) { // remove the child and decrement i prevChild.appendData(child.nodeValue); this.removeChild(child); i--; } } } }, toString: function() { var id = ''; if (this.id) { id = '#' + this.id; } if (this.className) { var classes = this.className.split(/\s+/); for (var i = 0, len = classes.length; i < len; i++) { id += '.' + classes[i]; } } return '[ ' + this.tagName + id + ' ]'; }, raise: function(type, message, data) { var text = type + ": " + message; if (data) { if (data.exception) { text = data.exception.stack; } else { text += ' - More:\n' + data; } } if (type === "error") { if (!this.errors) { this.errors = []; } // TODO: consider using actual `Error` objects or `DOMException`s even.. var err = { type : type, message : message || "No message", data : data || null }; this.errors.push(err); if (this._ownerDocument && this._ownerDocument.raise && this !== this._ownerDocument) { this._ownerDocument.raise(type, message, data); } } } }; core.NamedNodeMap = function NamedNodeMap(document) { this._nodes = Object.create(null); this._nsStore = {}; this.length = 0; this._ownerDocument = document; this._readonly = false; }; core.NamedNodeMap.prototype = { get readonly() { return this._readonly;}, get ownerDocument() { this._ownerDocument;}, exists : function(name) { return (this._nodes[name] || this._nodes[name] === null) ? true : false; }, /* returns Node */ getNamedItem: function(/* string */ name) { return this._nodes[name] || null; }, /* returns Node */ setNamedItem: function(/* Node */ arg) { // readonly if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } // arg is from a different document if (arg && arg._ownerDocument !== this._ownerDocument) { throw new core.DOMException(WRONG_DOCUMENT_ERR); } // if this argument is already in use.. if (arg && arg._ownerElement) { throw new core.DOMException(INUSE_ATTRIBUTE_ERR); } var name = arg.name || arg.tagName; var ret = this._nodes[name]; if (!ret) { this.length++; ret = null; } arg._specified = true; this._nodes[name] = arg; // Avoid overwriting prototype methods etc.: if (this.hasOwnProperty(name) || !(name in this)) { this[name] = arg; } return ret; }, // raises: function(DOMException) {}, /* returns Node */ removeNamedItem: function(/* string */ name) { // readonly if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } if (!this._nodes[name]) { throw new core.DOMException(NOT_FOUND_ERR); } var prev = this._nodes[name] || null; delete this._nodes[name]; delete this[name]; this.length--; return prev; }, // raises: function(DOMException) {}, /* returns Node */ item: function(/* int */ index) { var current = 0; for (var member in this._nodes) { if (current === index && this._nodes[member]) { return this._nodes[member]; } current++; } return null; } }; // // For historical reasons, AttributeList objects must allow accessing // attributes as if the object were an associative array. For // instance, if `attributes` is an AttributeList object then // `attributes.x` should evaluate to the attribute named `x` (which is // not in any namespace). The AttributeList class uses the dollar // symbol ($) to reduce the possibility of a clash between its field // names and possible attribute names. For instance, if the method // currently named `$set` were instead named `set` then it would not // be possible to access an attribute named `set` through // `attributes.set`. The dollar symbol is not valid in attribute names // so `$set` cannot clash. // // Some fields do not get the $ because: // // * They are part of the API (e.g. `setNamedItem`, `length`), so they // must be visible under a specific name. // // * Jsdom's code which traverses the DOM tree expects regularly named // fields (e.g. `_parentNode`). // function AttributeList(document, parentNode) { this._ownerDocument = document; this._parentNode = parentNode; this._readonly = false; this._$ns_to_attrs = Object.create(null); this._$name_to_attrs = Object.create(null); this.length = 0; } AttributeList.prototype = { _$reserved: [], // Initialized later // // Code internal to jsdom and which manipulates an AttributeList // object should use the following methods rather than the methods // that provide the NamedNodeMap interface. // // This method *ignores* namespaces. This is *not* the same thing as // requesting an attribute with a null namespace. $getNoNS: function (name) { var attrs = this._$name_to_attrs[name]; if (!attrs) { return null; } return attrs[0] || null; }, $getNode: function (namespace, localName) { var attrs = this._$ns_to_attrs[namespace]; if (!attrs) { return null; } var ret = attrs[localName]; if (!ret) { return null; } return ret; }, // This method *ignores* namespaces. This is *not* the same thing as // requesting an attribute with a null namespace. $setNoNS: function (name, value) { var attr = this.$getNoNS(name); if (!attr) { this.$set(name, value); return; } var prev_val = attr.value; attr.value = value; attr._specified = true; this._parentNode._attrModified(attr.name, attr.value, prev_val); this._parentNode._modified(); }, $set: function (localName, value, name, prefix, namespace) { if (this._readonly) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } if (name === undefined) { name = localName; } if (prefix === undefined) { prefix = null; } if (namespace === undefined) { namespace = null; } var prev_attr = this.$getNode(namespace, localName); var attr; var prev_val = null; if (prev_attr) { prev_val = prev_attr.value; prev_attr._prefix = prefix; prev_attr.value = value; attr = prev_attr; attr._specified = true; this._parentNode._attrModified(attr.name, attr.value, prev_val); this._parentNode._modified(); } else { attr = this._ownerDocument.createAttribute(name); attr._ownerElement = this._parentNode; attr.value = value; attr._namespaceURI = namespace; attr._prefix = prefix; attr._localName = localName; attr._parentNode = this._parentNode; attr._created = true; this.$setNode(attr); // $setNode calls the parent node methods. } }, $setNode: function (attr) { if (this._readonly) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } if (attr.nodeType !== ATTRIBUTE_NODE) { throw new core.DOMException(HIERARCHY_REQUEST_ERR); } if (attr._ownerDocument !== this._ownerDocument) { throw new core.DOMException(WRONG_DOCUMENT_ERR); } if (attr._parentNode && attr._parentNode !== this._parentNode) { throw new core.DOMException(INUSE_ATTRIBUTE_ERR); } var localName = attr._localName; var name = attr.name; var prefix = attr._prefix; var namespace = attr._namespaceURI; if (name === undefined) { name = localName; } if (prefix === undefined) { prefix = null; } if (namespace === undefined) { namespace = null; } var prev_attr = this.$getNode(namespace, localName); var prev_val = null; if (prev_attr) { prev_val = prev_attr.value; // Remove the old attribute this._$onlyRemoveNode(prev_attr); } attr._parentNode = this._parentNode; attr._ownerElement = this._parentNode; attr._specified = true; var attrs = this._$ns_to_attrs[namespace]; if (!attrs) { attrs = this._$ns_to_attrs[namespace] = Object.create(null); } attrs[localName] = attr; attrs = this._$name_to_attrs[name]; if (!attrs) { attrs = this._$name_to_attrs[name] = [attr]; } else { attrs.push(attr); } // Only attributes in the null namespace can be set this way. if (namespace === null) { // Make the node a field on this object but ONLY if it does not // clash with the reserved names. if (this._$reserved.indexOf(name) === -1) { this[name] = attr; } } this[this.length] = attr; this.length++; this._parentNode._attrModified(attr.name, attr.value, prev_val); this._parentNode._modified(); return prev_attr; }, // This method *ignores* namespaces. This is *not* the same thing as // requesting an attribute with a null namespace. $removeNoNS: function (name) { var attr = this.$getNoNS(name); return attr ? this.$removeNode(attr) : null; }, $remove: function (namespace, localName) { var attr = this.$getNode(namespace, localName); return attr ? this.$removeNode(attr) : null; }, /* Only removes the node, and does not add a default value. */ _$onlyRemoveNode: function (attr) { var namespace = attr._namespaceURI; var localName = attr._localName; var attrs = this._$ns_to_attrs[namespace]; if (!attrs) { return null; } var found_attr = attrs[localName]; if (found_attr !== attr) { return null; } if (this._readonly) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } attr._ownerElement = null; attr._parentNode = null; delete attrs[localName]; attrs = this._$name_to_attrs[attr.name]; attrs.splice(attrs.indexOf(attr), 1); var ix = Array.prototype.indexOf.call(this, attr); // Splice also modifies length. Array.prototype.splice.call(this, ix, 1); if (this[attr.name] === attr) { delete this[attr.name]; } this._parentNode._attrModified(attr.name); this._parentNode._modified(); return attr; }, $removeNode: function (attr) { if (!this._$onlyRemoveNode(attr)) { return null; } // set default value if available var doc = this._ownerDocument; if (doc && doc._doctype && doc._doctype.name.toLowerCase() !== "html") { var elem = doc._doctype._attributes.getNamedItem(this._parentNode.nodeName); if (elem) { var defaultValue = elem.attributes.getNamedItemNS(attr._namespaceURI, attr._localName); if (defaultValue) { this.$set(attr._localName, defaultValue.value, attr.name, attr._prefix, attr._namespaceURI); var new_attr = this.$getNode(attr._namespaceURI, attr._localName); new_attr._specified = false; } } } return attr; }, // Although http://dom.spec.whatwg.org/#concept-element-attribute // does not specify that the attributes field on an Element should // support NamedNodeMap, in practice browsers still support this // interface so we should support it for compatibility. getNamedItem: function (name) { return this.getNamedItemNS(null, name); }, removeNamedItem: function (name) { return this.removeNamedItemNS(null, name); }, item: function (i) { return this[i]; }, getNamedItemNS: function (namespaceURI, localName) { if (namespaceURI === "") { namespaceURI = null; } return this.$getNode(namespaceURI, localName); }, removeNamedItemNS: function (namespaceURI, localName) { var ret = this.$remove(namespaceURI, localName); if (ret === null) { throw new core.DOMException(NOT_FOUND_ERR); } return ret; } }; // Alias these methods. AttributeList.prototype.setNamedItem = AttributeList.prototype.$setNode; AttributeList.prototype.setNamedItemNS = AttributeList.prototype.$setNode; (function () { // Construct the list of reserved attribute names from a temporarily // created AttributeList and from the chain of prototypes. We need // this because JavaScript code running an a browser expects to be // able to do el.attributes.x to get the value of the attribute "x" // on an element. Unfortunately, JavaScript *currently* does not // allow us to elegantly provide such functionality without risking // a clash with the fields and methods set on the AttributeList // object. Hence we need a list of reserved field names. var reserved = Object.keys(new AttributeList()); var prototype = AttributeList.prototype; while (prototype) { reserved = reserved.concat(Object.getOwnPropertyNames(prototype)); prototype = Object.getPrototypeOf(prototype); } AttributeList.prototype._$reserved = reserved; })(); core.AttributeList = AttributeList; core.NotationNodeMap = function NotationNodeMap(document) { core.NamedNodeMap.call(this, document); this._readonly = false; for (var i=1;i<arguments.length;i++) { this.setNamedItem(arguments[i]); } this._readonly = true; }; inheritFrom(core.NamedNodeMap, core.NotationNodeMap); core.EntityNodeMap = function EntityNodeMap(document) { core.NamedNodeMap.call(this,document); this._readonly = false; var i = 1, l = arguments.length; for (i=1; i<l; i++) { this.setNamedItem(arguments[i]); } core.markTreeReadonly(this); }; inheritFrom(core.NamedNodeMap, core.EntityNodeMap); core.Element = function Element(document, tagName) { this._ownerDocument = document; core.Node.call(this, document); this._nodeName = tagName; this._tagName = tagName; }; inheritFrom(core.Node, core.Element, { get nodeValue() { return null;}, set nodeValue(value) { /* do nothing */ }, get tagName() { if (this.nodeType === ELEMENT_NODE && this._ownerDocument && this._ownerDocument._doctype && this._ownerDocument._doctype.name.toLowerCase().indexOf("html") !== -1) { return this.nodeName.toUpperCase(); } return this.nodeName; }, nodeType : ELEMENT_NODE, get attributes() { return this._attributes; }, /* returns string */ getAttribute: function(/* string */ name) { var attribute = this._attributes.$getNode(null, name); if (attribute) { return attribute.value; } return null; }, setAttribute: function(/* string */ name, /* string */ value) { if (this._ownerDocument) { var attr = this._ownerDocument.createAttribute(name); attr.value = value; attr._ownerElement = this; attr._created = true; this._attributes.$setNode(attr); } }, //raises: function(DOMException) {}, removeAttribute: function(/* string */ name) { this._attributes.$remove(null, name); }, // raises: function(DOMException) {}, /* returns Attr */ getAttributeNode: function(/* string */ name) { return this._attributes.$getNode(null, name); }, /* returns Attr */ setAttributeNode: function(/* Attr */ newAttr) { var prevNode = this._attributes.$getNode(null, newAttr.name); if (prevNode) { prevNode._ownerElement = null; } newAttr._ownerElement = this; this._attributes.$setNode(newAttr); return (prevNode && prevNode.specified) ? prevNode : null; }, // raises: function(DOMException) {}, /* returns Attr */ removeAttributeNode: function(/* Attr */ oldAttr) { var ret = this._attributes.$removeNode(oldAttr); if (ret !== null) { return ret; } throw new core.DOMException(NOT_FOUND_ERR); }, //raises: function(DOMException) {}, /* returns NodeList */ getElementsByTagName: core.memoizeQuery(function(/* string */ name) { name = name.toLowerCase(); function filterByTagName(child) { child = (child.nodeType === ENTITY_REFERENCE_NODE) ? child._entity : child; if (child.nodeName && child.nodeType === ELEMENT_NODE) { return name === "*" || (child.nodeName.toLowerCase() === name); } return false; } return new core.NodeList(this._ownerDocument || this, core.mapper(this, filterByTagName, true)); }), }); core.DocumentFragment = function DocumentFragment(document) { core.Node.call(this, document); this._nodeName = this._tagName = "#document-fragment"; }; inheritFrom(core.Node, core.DocumentFragment, { nodeType : DOCUMENT_FRAGMENT_NODE, get nodeValue() { return null;}, set nodeValue(unused) { /* do nothing */ }, get attributes() { return null;} }); core.ProcessingInstruction = function ProcessingInstruction(document, target, data) { this._ownerDocument = document; core.Node.call(this, document); this._nodeName = target; this._tagName = target; this._target = target; this._nodeValue = data; } inheritFrom(core.Node, core.ProcessingInstruction, { nodeType : PROCESSING_INSTRUCTION_NODE, get target() { return this._target;}, set target(value) { throw new core.DOMException(1);}, get nodeValue() { return this._nodeValue;}, set nodeValue(value) { this._nodeValue = value}, get data() { return this._nodeValue;}, set data(unused) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR);}, get attributes() { return null;} }); core.Document = function Document(options) { if (!options) { options = {}; } else if (typeof options == 'string') { options = { name: options }; } core.Node.call(this, "#document"); this._nodeName = this._tagName = "#document"; this._contentType = options.contentType || "text/xml"; this._doctype = options._doctype; this._implementation = options.implementation || new (core.DOMImplementation)(); this._documentElement = null; this._ids = Object.create(null); this._attached = true; this._ownerDocument = this; this._readonly = false; }; var tagRegEx = /[^\w:\d_\.-]+/i; var entRegEx = /[^\w\d_\-&;]+/; var invalidAttrRegEx = /[\s"'>/=\u0000-\u001A]/; inheritFrom(core.Node, core.Document, { nodeType : DOCUMENT_NODE, _elementBuilders : { }, _defaultElementBuilder: function(document, tagName) { return new core.Element(document, tagName); }, get contentType() { return this._contentType;}, get doctype() { return this._doctype || null;}, set doctype(doctype) { this._doctype = doctype;}, get documentElement() { if (this._documentElement) { return this._documentElement; } else { var children = this._childNodes, len = this._childNodes.length, i=0; for (i;i<len;i++) { if (children[i].nodeType === ELEMENT_NODE) { this._documentElement = children[i]; return children[i]; } } return null; } }, get implementation() { return this._implementation;}, set implementation(implementation) { this._implementation = implementation;}, get nodeName() { return '#document'; }, get tagName() { return null; }, get nodeValue() { return null; }, set nodeValue(unused) { /* noop */ }, get attributes() { return null;}, get ownerDocument() { return null;}, get readonly() { return this._readonly;}, /* returns Element */ _createElementNoTagNameValidation: function(/*string*/ tagName) { var lower = tagName.toLowerCase(); var element = (this._elementBuilders[lower] || this._defaultElementBuilder)(this, tagName); // Check for and introduce default elements if (this._doctype && this._doctype._attributes && this._doctype.name.toLowerCase() !== "html") { var attrElement = this._doctype._attributes.getNamedItem(tagName); if (attrElement && attrElement._childNodes) { var attrs = attrElement.attributes; var attr, len = attrs.length, defaultAttr; for (var i = 0; i < len; i++) { defaultAttr = attrs[i]; if (defaultAttr) { attr = this.createAttribute(defaultAttr.name); attr.value = defaultAttr.value; element.setAttributeNode(attr); attr._specified = false; attr._created = true; } } } } if (!element.namespaceURI) element._namespaceURI = "http://www.w3.org/1999/xhtml"; element._created = true; return element; }, /* returns Element */ createElement: function(/* string */ tagName) { tagName = String(tagName); var c = []; if (tagName.length === 0 || (c = tagName.match(tagRegEx))) { throw new core.DOMException(INVALID_CHARACTER_ERR, 'Invalid character in tag name: ' + c.pop()); } return this._createElementNoTagNameValidation(tagName); }, //raises: function(DOMException) {}, /* returns DocumentFragment */ createDocumentFragment: function() { return new core.DocumentFragment(this); }, /* returns Text */ createTextNode: function(/* string */ data) { return new core.Text(this,data); }, /* returns Comment */ createComment: function(/* string */ data) { return new core.Comment(this,data); }, /* returns CDATASection */ createCDATASection: function(/* string */ data) { if (this._doctype && this._doctype.name === "html") { throw new core.DOMException(NOT_SUPPORTED_ERR); } return new core.CDATASection(this,data); }, // raises: function(DOMException) {}, /* returns ProcessingInstruction */ createProcessingInstruction: function(/* string */ target,/* string */ data) { if (target.match(tagRegEx) || !target || !target.length) { throw new core.DOMException(INVALID_CHARACTER_ERR); } return new core.ProcessingInstruction(this, target, data); }, // raises: function(DOMException) {}, /* returns Attr */ createAttribute: function(/* string */ name) { if (!name || !name.length || name.match(invalidAttrRegEx) ) { throw new core.DOMException(INVALID_CHARACTER_ERR, "attribute name: " + name); } return new core.Attr(this, name,false); }, // raises: function(DOMException) {}, /* returns EntityReference */ createEntityReference: function(/* string */ name) { if (this._doctype && this._doctype.name === "html") { throw new core.DOMException(NOT_SUPPORTED_ERR); } name = name.replace(/[&;]/g,""); if (!name || !name.length) { throw new core.DOMException(INVALID_CHARACTER_ERR); } if (name.match(tagRegEx)) { throw new core.DOMException(INVALID_CHARACTER_ERR); } var entity; if (this._doctype && this._doctype.entities) { entity = this._doctype.entities.getNamedItem(name); } else { entity = null; } var ref = new core.EntityReference(this, entity); ref._entityName = name; return ref; }, //raises: function(DOMException) {}, /* returns Entity */ createEntityNode : function(/* string */ name) { if (name.match(entRegEx) || !name || !name.length) { throw new core.DOMException(INVALID_CHARACTER_ERR); } var ret = new core.Entity(this, name); ret._readonly = false;// TODO: fix me please. for (var i=1;i<arguments.length;i++) { ret.appendChild(arguments[i]); } core.markTreeReadonly(ret); return ret; }, /* returns Notation */ createNotationNode : function(/* string */ name,/* string */ publicId,/* string */ systemId) { if (name.match(entRegEx) || !name || !name.length) { throw new core.DOMException(INVALID_CHARACTER_ERR); } var ret = new core.Notation(this, name, publicId, systemId); ret._readonly = false;// TODO: fix me please. for (var i=3;i<arguments.length;i++) { ret.appendChild(arguments[i]); } core.markTreeReadonly(ret); return ret; }, appendChild : function(/* Node */ arg) { if (this.documentElement && arg.nodeType == ELEMENT_NODE) { throw new core.DOMException(HIERARCHY_REQUEST_ERR); } return core.Node.prototype.appendChild.call(this, arg); }, removeChild : function(/* Node */ arg) { var ret = core.Node.prototype.removeChild.call(this, arg); if (arg == this._documentElement) { this._documentElement = null;// force a recalculation } return ret; }, /* returns NodeList */ getElementsByTagName: core.memoizeQuery(function(/* string */ name) { function filterByTagName(child) { if (child.nodeType && child.nodeType === ENTITY_REFERENCE_NODE) { child = child._entity; } if (child.nodeName && child.nodeType === ELEMENT_NODE) { if (name === "*") { return true; // case insensitivity for html } else if (child._ownerDocument && child._ownerDocument._doctype && //child._ownerDocument._doctype.name === "html" && child.nodeName.toLowerCase() === name.toLowerCase()) { return true; } else if (child.nodeName.toLowerCase() === name.toLowerCase()) { return true; } } return false; } return new core.NodeList(this.documentElement || this, core.mapper(this, filterByTagName, true)); }) }); core.CharacterData = function CharacterData(document, value) { core.Node.call(this, document); this._nodeValue = String(value); }; inheritFrom(core.Node, core.CharacterData, { get data() { return this._nodeValue;}, set data(data) { // readonly if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } this._nodeValue = String(data); }, /* returns int */ get length() { return this._nodeValue.length || 0;}, /* returns string */ substringData: function(/* int */ offset, /* int */ count) { if (count < 0 || offset < 0 || offset > this._nodeValue.length) { throw new core.DOMException(INDEX_SIZE_ERR); } return (this._nodeValue.length < offset + count) ? this._nodeValue.substring(offset) : this._nodeValue.substring(offset, offset+count); }, // raises: function(DOMException) {}, /* returns string */ appendData: function(/* string */ arg) { // readonly if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } this._nodeValue+=arg; return this._nodeValue; }, // raises: function(DOMException) {}, /* returns string */ insertData: function(/* int */ offset, /* string */ arg) { // readonly if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } if (offset < 0 || offset > this._nodeValue.length) { throw new core.DOMException(INDEX_SIZE_ERR); } var start = this._nodeValue.substring(0,offset); var end = this._nodeValue.substring(offset); this._nodeValue = start + arg + end; }, //raises: function(DOMException) {}, /* returns void */ deleteData: function(/* int */ offset, /* int */ count) { // readonly if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } if (offset < 0 || offset > this._nodeValue.length || count < 0) { throw new core.DOMException(INDEX_SIZE_ERR); } var start = this._nodeValue.substring(0,offset); this._nodeValue = (offset+count<this._nodeValue.length) ? start + this._nodeValue.substring(offset+count) : start; }, // raises: function(DOMException) {}, /* returns void */ replaceData: function(/* int */ offset, /* int */ count, /* string */ arg) { // readonly if (this._readonly === true) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } count = (offset+count > this._nodeValue.length) ? this.nodeValue.length-offset : count; if (offset < 0 || offset > this._nodeValue.length || count < 0 /*|| offset+count > this._nodeValue.length*/) { throw new core.DOMException(INDEX_SIZE_ERR); } var start = this._nodeValue.substring(0,offset); var end = this._nodeValue.substring(offset+count); this._nodeValue = start + arg + end; } // raises: function(DOMException) {}, }); core.Attr = function Attr(document, name, value) { core.Node.call(this, document); this._nodeValue = value; this._name = name; this._specified = (value) ? true : false; this._tagName = name; this._nodeName = name; // Proactively set some level 2 information so that AttributeList // can operate. this._namespaceURI = null; this._nodeName = name; this._localName = name; this._prefix = null; }; inheritFrom(core.Node, core.Attr, { nodeType : ATTRIBUTE_NODE, get nodeValue() { var val = ''; for (var i=0,len=this._childNodes.length;i<len;i++) { var child = this._childNodes[i]; if (child.nodeType === ENTITY_REFERENCE_NODE) { val += Array.prototype.reduce.call(child.childNodes, function(prev, c) { return prev += (c.nodeValue || c); }, ''); } else { val += child.nodeValue; } } return val; }, set nodeValue(value) { // readonly if (this._readonly) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } this._childNodes._resetTo([this._ownerDocument.createTextNode(value)]); this._modified(); this._specified = true; var prev = this._nodeValue; this._nodeValue = value; if (this._ownerElement) { this._ownerElement._attrModified(this._name, value, prev); } }, get name() { return this._name;}, get specified() { return this._specified }, get value() { return this.nodeValue; }, set value(value) { this.nodeValue = value; }, get parentNode() { return null;}, get attributes() { return null;}, insertBefore : function(/* Node */ newChild, /* Node*/ refChild){ if (newChild.nodeType === CDATA_SECTION_NODE || newChild.nodeType === ELEMENT_NODE) { throw new core.DOMException(HIERARCHY_REQUEST_ERR); } return core.Node.prototype.insertBefore.call(this, newChild, refChild); }, appendChild : function(/* Node */ arg) { if (arg.nodeType === CDATA_SECTION_NODE || arg.nodeType === ELEMENT_NODE) { throw new core.DOMException(HIERARCHY_REQUEST_ERR); } return core.Node.prototype.appendChild.call(this, arg); } }); core.Text = function Text(document, text, readonly) { core.CharacterData.call(this, document, text); this._nodeName = "#text"; this._readonly = readonly ? true : false }; inheritFrom(core.CharacterData, core.Text, { nodeType : TEXT_NODE, get attributes() { return null;}, /* returns Text */ splitText: function(offset) { // readonly if (this._readonly) { throw new core.DOMException(NO_MODIFICATION_ALLOWED_ERR); } if (offset < 0 || offset > this._nodeValue.length) { throw new core.DOMException(INDEX_SIZE_ERR); } var newText = this._nodeValue.substring(offset); this._nodeValue = this._nodeValue.substring(0, offset); var newNode = this._ownerDocument.createTextNode(newText); if(this._parentNode.lastChild === this) { this._parentNode.appendChild(newNode); } else { this._parentNode.insertBefore(newNode, this.nextSibling); } return newNode; }, //raises: function(DOMException) {}, toString: function() { return this.nodeName; } }); core.Comment = function Comment(document, text) { core.Text.call(this, document, text); this._nodeName = "#comment"; this._tagName = "#comment"; }; inheritFrom(core.Text, core.Comment, { nodeType : COMMENT_NODE }); core.CDATASection = function CDATASection(document, value) { core.Text.call(this, document, value); this._nodeName = "#cdata-section"; }; inheritFrom(core.Text, core.CDATASection, { nodeType : CDATA_SECTION_NODE }); core.DocumentType = function DocumentType(document, name, entities, notations, attributes) { core.Node.call(this, document); this._name = name; this._tagName = name; this._nodeName = name; this._entities = entities || new core.EntityNodeMap(document); this._notations = notations || new core.NotationNodeMap(document); this._parentNode = document; core.markTreeReadonly(this._notations); this._attributes = attributes || new AttributeList(document); }; inheritFrom(core.Node, core.DocumentType, { nodeType : DOCUMENT_TYPE_NODE, get nodeValue() { return null;}, set nodeValue(unused) { /* do nothing */ }, get name() { return this._name;}, get entities() { return this._entities;}, get notations() { return this._notations;}, get attributes() { return null;} }); core.Notation = function Notation(document, name, publicId, systemId){ core.Node.call(this, document); this._name = name; this._nodeName = name; this._publicId = publicId || null; this._systemId = systemId || null; this._nodeValue = null; }; inheritFrom(core.Node, core.Notation, { nodeType : NOTATION_NODE, get publicId() { return this._publicId;}, get systemId() { return this._systemId;}, get name() { return this._name || this._nodeName;}, get attributes() { /* as per spec */ return null;}, set nodeValue(unused) { /* intentionally left blank */ }, get nodeValue() { return this._nodeValue;}, }); core.Entity = function Entity(document, name) { core.Node.call(this, document); this._name = name; this._nodeName = name; this._tagName = name; this._publicId = null; this._systemId = null; this._notationName = null; this._readonly = true; }; inheritFrom(core.Node, core.Entity, { nodeType : ENTITY_NODE, get nodeValue() { return null;}, set nodeValue(unused) { // readonly if (this._readonly === true) { // TODO: is this needed? // throw new DOMException(NO_MODIFICATION_ALLOWED_ERR); } /* do nothing */ }, get name() { return this._name }, get publicId() { return this._publicId;}, get systemId() { return this._systemId;}, set publicId(publicId) { this._publicId = publicId;}, set systemId(systemId) { this._systemId = systemId;}, set notationName(notationName) { this._notationName = notationName;}, get notationName() { return this._notationName;}, get attributes() { return null;}, }); core.EntityReference = function EntityReference(document, entity) { core.Node.call(this, document); this._entity = entity; this._nodeName = (entity) ? entity.name : null; this._readonly = true; }; inheritFrom(core.Node, core.EntityReference, { nodeType : ENTITY_REFERENCE_NODE, get nodeValue() { return (this._entity) ? this._entity.nodeValue : null;}, set nodeValue(unused) { // readonly if (this._readonly === true) { // TODO: is this needed? //throw new DOMException(NO_MODIFICATION_ALLOWED_ERR); } /* do nothing */ }, get attributes() { return null;}, // Proxy to the entity get nodeName() { return this._entityName;}, get firstChild() { return this._entity.firstChild || null;}, get childNodes() { return this._entity.childNodes;}, get lastChild() { return this._entity.lastChild || null;}, }); exports.dom = { "level1" : { "core" : core }};