├── .gitignore ├── .npmignore ├── .project ├── .travis.yml ├── LICENSE ├── __package__.js ├── changelog ├── component.json ├── dom-parser.js ├── dom.js ├── entities.js ├── package.json ├── readme.md ├── sax.js ├── t ├── cover ├── dom │ └── require.t.js └── test └── test ├── 3rd-cases ├── index.js ├── mock.js └── o3xml.js ├── dom ├── attr.js ├── clone.js ├── element.js ├── fragment.js ├── index.js ├── level3.js ├── ns-test.js └── serializer.js ├── error ├── error.js ├── index.js └── xml-error.js ├── html └── normalize.js ├── index.js ├── parse ├── big-file-performance.js ├── file-test.js ├── file-test1.result.xml ├── file-test1.xml ├── index.js ├── locator.js ├── namespace.js ├── node.js ├── parse-element.js ├── simple.js ├── test-define.js ├── test-doc-witespace.js └── unclosedcomment.js ├── test.js └── xss-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.proof.out 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | t 3 | travis.yml 4 | .project 5 | changelog 6 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | xmldom 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '0.10' 5 | 6 | branches: 7 | only: 8 | - master 9 | - proof 10 | - travis-ci 11 | 12 | # Not using `npm install --dev` because it is recursive. It will pull in the all 13 | # development dependencies for CoffeeScript. Way too much spew in the Travis CI 14 | # build output. 15 | 16 | before_install: 17 | - npm install 18 | - npm install istanbul coveralls 19 | 20 | env: 21 | global: 22 | - secure: "BxUHTsa1WVANLQoimilbZwa1MCWSdM9hOmPWBE/rsYb7uT/iiqkRXXwnWhKtN5CLvTvIQbiAzq4iyPID0S8UHrnxClYQrOuA6QkrtwgIEuDAmijao/bgxobPOremvkwXcpMGIwzYKyYQQtSEaEIQbqf6gSSKW9dBh/GZ/vfTsqo=" 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | You can choose any one of those: 2 | 3 | The MIT License (MIT): 4 | 5 | link:http://opensource.org/licenses/MIT 6 | 7 | LGPL: 8 | http://www.gnu.org/licenses/lgpl.html 9 | -------------------------------------------------------------------------------- /__package__.js: -------------------------------------------------------------------------------- 1 | this.addScript('dom.js',['DOMImplementation','XMLSerializer']); 2 | this.addScript('dom-parser.js',['DOMHandler','DOMParser'], 3 | ['DOMImplementation','XMLReader']); 4 | this.addScript('sax.js','XMLReader'); -------------------------------------------------------------------------------- /changelog: -------------------------------------------------------------------------------- 1 | ### Version 0.1.16 2 | 3 | Sat May 4 14:58:03 UTC 2013 4 | 5 | * Correctly handle multibyte Unicode greater than two byts. #57. #56. 6 | * Initial unit testing and test coverage. #53. #46. #19. 7 | * Create Bower `component.json` #52. 8 | 9 | ### Version 0.1.8 10 | 11 | * Add: some test case from node-o3-xml(excludes xpath support) 12 | * Fix: remove existed attribute before setting (bug introduced in v0.1.5) 13 | * Fix: index direct access for childNodes and any NodeList collection(not w3c standard) 14 | * Fix: remove last child bug 15 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xmldom", 3 | "version": "0.1.15", 4 | "main": "dom-parser.js", 5 | "ignore": [ 6 | "**/.*", 7 | "node_modules", 8 | "components" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /dom-parser.js: -------------------------------------------------------------------------------- 1 | function DOMParser(options){ 2 | this.options = options ||{locator:{}}; 3 | 4 | } 5 | 6 | DOMParser.prototype.parseFromString = function(source,mimeType){ 7 | var options = this.options; 8 | var sax = new XMLReader(); 9 | var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler 10 | var errorHandler = options.errorHandler; 11 | var locator = options.locator; 12 | var defaultNSMap = options.xmlns||{}; 13 | var isHTML = /\/x?html?$/.test(mimeType);//mimeType.toLowerCase().indexOf('html') > -1; 14 | var entityMap = isHTML?htmlEntity.entityMap:{'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"}; 15 | if(locator){ 16 | domBuilder.setDocumentLocator(locator) 17 | } 18 | 19 | sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator); 20 | sax.domBuilder = options.domBuilder || domBuilder; 21 | if(isHTML){ 22 | defaultNSMap['']= 'http://www.w3.org/1999/xhtml'; 23 | } 24 | defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace'; 25 | if(source){ 26 | sax.parse(source,defaultNSMap,entityMap); 27 | }else{ 28 | sax.errorHandler.error("invalid doc source"); 29 | } 30 | return domBuilder.doc; 31 | } 32 | function buildErrorHandler(errorImpl,domBuilder,locator){ 33 | if(!errorImpl){ 34 | if(domBuilder instanceof DOMHandler){ 35 | return domBuilder; 36 | } 37 | errorImpl = domBuilder ; 38 | } 39 | var errorHandler = {} 40 | var isCallback = errorImpl instanceof Function; 41 | locator = locator||{} 42 | function build(key){ 43 | var fn = errorImpl[key]; 44 | if(!fn && isCallback){ 45 | fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl; 46 | } 47 | errorHandler[key] = fn && function(msg){ 48 | fn('[xmldom '+key+']\t'+msg+_locator(locator)); 49 | }||function(){}; 50 | } 51 | build('warning'); 52 | build('error'); 53 | build('fatalError'); 54 | return errorHandler; 55 | } 56 | 57 | //console.log('#\n\n\n\n\n\n\n####') 58 | /** 59 | * +ContentHandler+ErrorHandler 60 | * +LexicalHandler+EntityResolver2 61 | * -DeclHandler-DTDHandler 62 | * 63 | * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler 64 | * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2 65 | * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html 66 | */ 67 | function DOMHandler() { 68 | this.cdata = false; 69 | } 70 | function position(locator,node){ 71 | node.lineNumber = locator.lineNumber; 72 | node.columnNumber = locator.columnNumber; 73 | } 74 | /** 75 | * @see org.xml.sax.ContentHandler#startDocument 76 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html 77 | */ 78 | DOMHandler.prototype = { 79 | startDocument : function() { 80 | this.doc = new DOMImplementation().createDocument(null, null, null); 81 | if (this.locator) { 82 | this.doc.documentURI = this.locator.systemId; 83 | } 84 | }, 85 | startElement:function(namespaceURI, localName, qName, attrs) { 86 | var doc = this.doc; 87 | var el = doc.createElementNS(namespaceURI, qName||localName); 88 | var len = attrs.length; 89 | appendElement(this, el); 90 | this.currentElement = el; 91 | 92 | this.locator && position(this.locator,el) 93 | for (var i = 0 ; i < len; i++) { 94 | var namespaceURI = attrs.getURI(i); 95 | var value = attrs.getValue(i); 96 | var qName = attrs.getQName(i); 97 | var attr = doc.createAttributeNS(namespaceURI, qName); 98 | this.locator &&position(attrs.getLocator(i),attr); 99 | attr.value = attr.nodeValue = value; 100 | el.setAttributeNode(attr) 101 | } 102 | }, 103 | endElement:function(namespaceURI, localName, qName) { 104 | var current = this.currentElement 105 | var tagName = current.tagName; 106 | this.currentElement = current.parentNode; 107 | }, 108 | startPrefixMapping:function(prefix, uri) { 109 | }, 110 | endPrefixMapping:function(prefix) { 111 | }, 112 | processingInstruction:function(target, data) { 113 | var ins = this.doc.createProcessingInstruction(target, data); 114 | this.locator && position(this.locator,ins) 115 | appendElement(this, ins); 116 | }, 117 | ignorableWhitespace:function(ch, start, length) { 118 | }, 119 | characters:function(chars, start, length) { 120 | chars = _toString.apply(this,arguments) 121 | //console.log(chars) 122 | if(chars){ 123 | if (this.cdata) { 124 | var charNode = this.doc.createCDATASection(chars); 125 | } else { 126 | var charNode = this.doc.createTextNode(chars); 127 | } 128 | if(this.currentElement){ 129 | this.currentElement.appendChild(charNode); 130 | }else if(/^\s*$/.test(chars)){ 131 | this.doc.appendChild(charNode); 132 | //process xml 133 | } 134 | this.locator && position(this.locator,charNode) 135 | } 136 | }, 137 | skippedEntity:function(name) { 138 | }, 139 | endDocument:function() { 140 | this.doc.normalize(); 141 | }, 142 | setDocumentLocator:function (locator) { 143 | if(this.locator = locator){// && !('lineNumber' in locator)){ 144 | locator.lineNumber = 0; 145 | } 146 | }, 147 | //LexicalHandler 148 | comment:function(chars, start, length) { 149 | chars = _toString.apply(this,arguments) 150 | var comm = this.doc.createComment(chars); 151 | this.locator && position(this.locator,comm) 152 | appendElement(this, comm); 153 | }, 154 | 155 | startCDATA:function() { 156 | //used in characters() methods 157 | this.cdata = true; 158 | }, 159 | endCDATA:function() { 160 | this.cdata = false; 161 | }, 162 | 163 | startDTD:function(name, publicId, systemId) { 164 | var impl = this.doc.implementation; 165 | if (impl && impl.createDocumentType) { 166 | var dt = impl.createDocumentType(name, publicId, systemId); 167 | this.locator && position(this.locator,dt) 168 | appendElement(this, dt); 169 | } 170 | }, 171 | /** 172 | * @see org.xml.sax.ErrorHandler 173 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html 174 | */ 175 | warning:function(error) { 176 | console.warn('[xmldom warning]\t'+error,_locator(this.locator)); 177 | }, 178 | error:function(error) { 179 | console.error('[xmldom error]\t'+error,_locator(this.locator)); 180 | }, 181 | fatalError:function(error) { 182 | console.error('[xmldom fatalError]\t'+error,_locator(this.locator)); 183 | throw error; 184 | } 185 | } 186 | function _locator(l){ 187 | if(l){ 188 | return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']' 189 | } 190 | } 191 | function _toString(chars,start,length){ 192 | if(typeof chars == 'string'){ 193 | return chars.substr(start,length) 194 | }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") 195 | if(chars.length >= start+length || start){ 196 | return new java.lang.String(chars,start,length)+''; 197 | } 198 | return chars; 199 | } 200 | } 201 | 202 | /* 203 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html 204 | * used method of org.xml.sax.ext.LexicalHandler: 205 | * #comment(chars, start, length) 206 | * #startCDATA() 207 | * #endCDATA() 208 | * #startDTD(name, publicId, systemId) 209 | * 210 | * 211 | * IGNORED method of org.xml.sax.ext.LexicalHandler: 212 | * #endDTD() 213 | * #startEntity(name) 214 | * #endEntity(name) 215 | * 216 | * 217 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html 218 | * IGNORED method of org.xml.sax.ext.DeclHandler 219 | * #attributeDecl(eName, aName, type, mode, value) 220 | * #elementDecl(name, model) 221 | * #externalEntityDecl(name, publicId, systemId) 222 | * #internalEntityDecl(name, value) 223 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html 224 | * IGNORED method of org.xml.sax.EntityResolver2 225 | * #resolveEntity(String name,String publicId,String baseURI,String systemId) 226 | * #resolveEntity(publicId, systemId) 227 | * #getExternalSubset(name, baseURI) 228 | * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html 229 | * IGNORED method of org.xml.sax.DTDHandler 230 | * #notationDecl(name, publicId, systemId) {}; 231 | * #unparsedEntityDecl(name, publicId, systemId, notationName) {}; 232 | */ 233 | "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){ 234 | DOMHandler.prototype[key] = function(){return null} 235 | }) 236 | 237 | /* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */ 238 | function appendElement (hander,node) { 239 | if (!hander.currentElement) { 240 | hander.doc.appendChild(node); 241 | } else { 242 | hander.currentElement.appendChild(node); 243 | } 244 | }//appendChild and setAttributeNS are preformance key 245 | 246 | //if(typeof require == 'function'){ 247 | var htmlEntity = require('./entities'); 248 | var XMLReader = require('./sax').XMLReader; 249 | var DOMImplementation = exports.DOMImplementation = require('./dom').DOMImplementation; 250 | exports.XMLSerializer = require('./dom').XMLSerializer ; 251 | exports.DOMParser = DOMParser; 252 | //} 253 | -------------------------------------------------------------------------------- /dom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * DOM Level 2 3 | * Object DOMException 4 | * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html 5 | * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html 6 | */ 7 | 8 | function copy(src,dest){ 9 | for(var p in src){ 10 | dest[p] = src[p]; 11 | } 12 | } 13 | /** 14 | ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));? 15 | ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));? 16 | */ 17 | function _extends(Class,Super){ 18 | var pt = Class.prototype; 19 | if(!(pt instanceof Super)){ 20 | function t(){}; 21 | t.prototype = Super.prototype; 22 | t = new t(); 23 | copy(pt,t); 24 | Class.prototype = pt = t; 25 | } 26 | if(pt.constructor != Class){ 27 | if(typeof Class != 'function'){ 28 | console.error("unknow Class:"+Class) 29 | } 30 | pt.constructor = Class 31 | } 32 | } 33 | var htmlns = 'http://www.w3.org/1999/xhtml' ; 34 | // Node Types 35 | var NodeType = {} 36 | var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1; 37 | var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2; 38 | var TEXT_NODE = NodeType.TEXT_NODE = 3; 39 | var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4; 40 | var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5; 41 | var ENTITY_NODE = NodeType.ENTITY_NODE = 6; 42 | var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7; 43 | var COMMENT_NODE = NodeType.COMMENT_NODE = 8; 44 | var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9; 45 | var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10; 46 | var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11; 47 | var NOTATION_NODE = NodeType.NOTATION_NODE = 12; 48 | 49 | // ExceptionCode 50 | var ExceptionCode = {} 51 | var ExceptionMessage = {}; 52 | var INDEX_SIZE_ERR = ExceptionCode.INDEX_SIZE_ERR = ((ExceptionMessage[1]="Index size error"),1); 53 | var DOMSTRING_SIZE_ERR = ExceptionCode.DOMSTRING_SIZE_ERR = ((ExceptionMessage[2]="DOMString size error"),2); 54 | var HIERARCHY_REQUEST_ERR = ExceptionCode.HIERARCHY_REQUEST_ERR = ((ExceptionMessage[3]="Hierarchy request error"),3); 55 | var WRONG_DOCUMENT_ERR = ExceptionCode.WRONG_DOCUMENT_ERR = ((ExceptionMessage[4]="Wrong document"),4); 56 | var INVALID_CHARACTER_ERR = ExceptionCode.INVALID_CHARACTER_ERR = ((ExceptionMessage[5]="Invalid character"),5); 57 | var NO_DATA_ALLOWED_ERR = ExceptionCode.NO_DATA_ALLOWED_ERR = ((ExceptionMessage[6]="No data allowed"),6); 58 | var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7); 59 | var NOT_FOUND_ERR = ExceptionCode.NOT_FOUND_ERR = ((ExceptionMessage[8]="Not found"),8); 60 | var NOT_SUPPORTED_ERR = ExceptionCode.NOT_SUPPORTED_ERR = ((ExceptionMessage[9]="Not supported"),9); 61 | var INUSE_ATTRIBUTE_ERR = ExceptionCode.INUSE_ATTRIBUTE_ERR = ((ExceptionMessage[10]="Attribute in use"),10); 62 | //level2 63 | var INVALID_STATE_ERR = ExceptionCode.INVALID_STATE_ERR = ((ExceptionMessage[11]="Invalid state"),11); 64 | var SYNTAX_ERR = ExceptionCode.SYNTAX_ERR = ((ExceptionMessage[12]="Syntax error"),12); 65 | var INVALID_MODIFICATION_ERR = ExceptionCode.INVALID_MODIFICATION_ERR = ((ExceptionMessage[13]="Invalid modification"),13); 66 | var NAMESPACE_ERR = ExceptionCode.NAMESPACE_ERR = ((ExceptionMessage[14]="Invalid namespace"),14); 67 | var INVALID_ACCESS_ERR = ExceptionCode.INVALID_ACCESS_ERR = ((ExceptionMessage[15]="Invalid access"),15); 68 | 69 | 70 | function DOMException(code, message) { 71 | if(message instanceof Error){ 72 | var error = message; 73 | }else{ 74 | error = this; 75 | Error.call(this, ExceptionMessage[code]); 76 | this.message = ExceptionMessage[code]; 77 | if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException); 78 | } 79 | error.code = code; 80 | if(message) this.message = this.message + ": " + message; 81 | return error; 82 | }; 83 | DOMException.prototype = Error.prototype; 84 | copy(ExceptionCode,DOMException) 85 | /** 86 | * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177 87 | * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live. 88 | * The items in the NodeList are accessible via an integral index, starting from 0. 89 | */ 90 | function NodeList() { 91 | }; 92 | NodeList.prototype = { 93 | /** 94 | * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive. 95 | * @standard level1 96 | */ 97 | length:0, 98 | /** 99 | * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null. 100 | * @standard level1 101 | * @param index unsigned long 102 | * Index into the collection. 103 | * @return Node 104 | * The node at the indexth position in the NodeList, or null if that is not a valid index. 105 | */ 106 | item: function(index) { 107 | return this[index] || null; 108 | }, 109 | toString:function(isHTML,nodeFilter){ 110 | for(var buf = [], i = 0;i=0){ 172 | var lastIndex = list.length-1 173 | while(i0 || key == 'xmlns'){ 193 | // return null; 194 | // } 195 | //console.log() 196 | var i = this.length; 197 | while(i--){ 198 | var attr = this[i]; 199 | //console.log(attr.nodeName,key) 200 | if(attr.nodeName == key){ 201 | return attr; 202 | } 203 | } 204 | }, 205 | setNamedItem: function(attr) { 206 | var el = attr.ownerElement; 207 | if(el && el!=this._ownerElement){ 208 | throw new DOMException(INUSE_ATTRIBUTE_ERR); 209 | } 210 | var oldAttr = this.getNamedItem(attr.nodeName); 211 | _addNamedNode(this._ownerElement,this,attr,oldAttr); 212 | return oldAttr; 213 | }, 214 | /* returns Node */ 215 | setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR 216 | var el = attr.ownerElement, oldAttr; 217 | if(el && el!=this._ownerElement){ 218 | throw new DOMException(INUSE_ATTRIBUTE_ERR); 219 | } 220 | oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName); 221 | _addNamedNode(this._ownerElement,this,attr,oldAttr); 222 | return oldAttr; 223 | }, 224 | 225 | /* returns Node */ 226 | removeNamedItem: function(key) { 227 | var attr = this.getNamedItem(key); 228 | _removeNamedNode(this._ownerElement,this,attr); 229 | return attr; 230 | 231 | 232 | },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR 233 | 234 | //for level2 235 | removeNamedItemNS:function(namespaceURI,localName){ 236 | var attr = this.getNamedItemNS(namespaceURI,localName); 237 | _removeNamedNode(this._ownerElement,this,attr); 238 | return attr; 239 | }, 240 | getNamedItemNS: function(namespaceURI, localName) { 241 | var i = this.length; 242 | while(i--){ 243 | var node = this[i]; 244 | if(node.localName == localName && node.namespaceURI == namespaceURI){ 245 | return node; 246 | } 247 | } 248 | return null; 249 | } 250 | }; 251 | /** 252 | * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 253 | */ 254 | function DOMImplementation(/* Object */ features) { 255 | this._features = {}; 256 | if (features) { 257 | for (var feature in features) { 258 | this._features = features[feature]; 259 | } 260 | } 261 | }; 262 | 263 | DOMImplementation.prototype = { 264 | hasFeature: function(/* string */ feature, /* string */ version) { 265 | var versions = this._features[feature.toLowerCase()]; 266 | if (versions && (!version || version in versions)) { 267 | return true; 268 | } else { 269 | return false; 270 | } 271 | }, 272 | // Introduced in DOM Level 2: 273 | createDocument:function(namespaceURI, qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR 274 | var doc = new Document(); 275 | doc.implementation = this; 276 | doc.childNodes = new NodeList(); 277 | doc.doctype = doctype; 278 | if(doctype){ 279 | doc.appendChild(doctype); 280 | } 281 | if(qualifiedName){ 282 | var root = doc.createElementNS(namespaceURI,qualifiedName); 283 | doc.appendChild(root); 284 | } 285 | return doc; 286 | }, 287 | // Introduced in DOM Level 2: 288 | createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR 289 | var node = new DocumentType(); 290 | node.name = qualifiedName; 291 | node.nodeName = qualifiedName; 292 | node.publicId = publicId; 293 | node.systemId = systemId; 294 | // Introduced in DOM Level 2: 295 | //readonly attribute DOMString internalSubset; 296 | 297 | //TODO:.. 298 | // readonly attribute NamedNodeMap entities; 299 | // readonly attribute NamedNodeMap notations; 300 | return node; 301 | } 302 | }; 303 | 304 | 305 | /** 306 | * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247 307 | */ 308 | 309 | function Node() { 310 | }; 311 | 312 | Node.prototype = { 313 | firstChild : null, 314 | lastChild : null, 315 | previousSibling : null, 316 | nextSibling : null, 317 | attributes : null, 318 | parentNode : null, 319 | childNodes : null, 320 | ownerDocument : null, 321 | nodeValue : null, 322 | namespaceURI : null, 323 | prefix : null, 324 | localName : null, 325 | // Modified in DOM Level 2: 326 | insertBefore:function(newChild, refChild){//raises 327 | return _insertBefore(this,newChild,refChild); 328 | }, 329 | replaceChild:function(newChild, oldChild){//raises 330 | this.insertBefore(newChild,oldChild); 331 | if(oldChild){ 332 | this.removeChild(oldChild); 333 | } 334 | }, 335 | removeChild:function(oldChild){ 336 | return _removeChild(this,oldChild); 337 | }, 338 | appendChild:function(newChild){ 339 | return this.insertBefore(newChild,null); 340 | }, 341 | hasChildNodes:function(){ 342 | return this.firstChild != null; 343 | }, 344 | cloneNode:function(deep){ 345 | return cloneNode(this.ownerDocument||this,this,deep); 346 | }, 347 | // Modified in DOM Level 2: 348 | normalize:function(){ 349 | var child = this.firstChild; 350 | while(child){ 351 | var next = child.nextSibling; 352 | if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){ 353 | this.removeChild(next); 354 | child.appendData(next.data); 355 | }else{ 356 | child.normalize(); 357 | child = next; 358 | } 359 | } 360 | }, 361 | // Introduced in DOM Level 2: 362 | isSupported:function(feature, version){ 363 | return this.ownerDocument.implementation.hasFeature(feature,version); 364 | }, 365 | // Introduced in DOM Level 2: 366 | hasAttributes:function(){ 367 | return this.attributes.length>0; 368 | }, 369 | lookupPrefix:function(namespaceURI){ 370 | var el = this; 371 | while(el){ 372 | var map = el._nsMap; 373 | //console.dir(map) 374 | if(map){ 375 | for(var n in map){ 376 | if(map[n] == namespaceURI){ 377 | return n; 378 | } 379 | } 380 | } 381 | el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; 382 | } 383 | return null; 384 | }, 385 | // Introduced in DOM Level 3: 386 | lookupNamespaceURI:function(prefix){ 387 | var el = this; 388 | while(el){ 389 | var map = el._nsMap; 390 | //console.dir(map) 391 | if(map){ 392 | if(prefix in map){ 393 | return map[prefix] ; 394 | } 395 | } 396 | el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode; 397 | } 398 | return null; 399 | }, 400 | // Introduced in DOM Level 3: 401 | isDefaultNamespace:function(namespaceURI){ 402 | var prefix = this.lookupPrefix(namespaceURI); 403 | return prefix == null; 404 | } 405 | }; 406 | 407 | 408 | function _xmlEncoder(c){ 409 | return c == '<' && '<' || 410 | c == '>' && '>' || 411 | c == '&' && '&' || 412 | c == '"' && '"' || 413 | '&#'+c.charCodeAt()+';' 414 | } 415 | 416 | 417 | copy(NodeType,Node); 418 | copy(NodeType,Node.prototype); 419 | 420 | /** 421 | * @param callback return true for continue,false for break 422 | * @return boolean true: break visit; 423 | */ 424 | function _visitNode(node,callback){ 425 | if(callback(node)){ 426 | return true; 427 | } 428 | if(node = node.firstChild){ 429 | do{ 430 | if(_visitNode(node,callback)){return true} 431 | }while(node=node.nextSibling) 432 | } 433 | } 434 | 435 | 436 | 437 | function Document(){ 438 | } 439 | function _onAddAttribute(doc,el,newAttr){ 440 | doc && doc._inc++; 441 | var ns = newAttr.namespaceURI ; 442 | if(ns == 'http://www.w3.org/2000/xmlns/'){ 443 | //update namespace 444 | el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value 445 | } 446 | } 447 | function _onRemoveAttribute(doc,el,newAttr,remove){ 448 | doc && doc._inc++; 449 | var ns = newAttr.namespaceURI ; 450 | if(ns == 'http://www.w3.org/2000/xmlns/'){ 451 | //update namespace 452 | delete el._nsMap[newAttr.prefix?newAttr.localName:''] 453 | } 454 | } 455 | function _onUpdateChild(doc,el,newChild){ 456 | if(doc && doc._inc){ 457 | doc._inc++; 458 | //update childNodes 459 | var cs = el.childNodes; 460 | if(newChild){ 461 | cs[cs.length++] = newChild; 462 | }else{ 463 | //console.log(1) 464 | var child = el.firstChild; 465 | var i = 0; 466 | while(child){ 467 | cs[i++] = child; 468 | child =child.nextSibling; 469 | } 470 | cs.length = i; 471 | } 472 | } 473 | } 474 | 475 | /** 476 | * attributes; 477 | * children; 478 | * 479 | * writeable properties: 480 | * nodeValue,Attr:value,CharacterData:data 481 | * prefix 482 | */ 483 | function _removeChild(parentNode,child){ 484 | var previous = child.previousSibling; 485 | var next = child.nextSibling; 486 | if(previous){ 487 | previous.nextSibling = next; 488 | }else{ 489 | parentNode.firstChild = next 490 | } 491 | if(next){ 492 | next.previousSibling = previous; 493 | }else{ 494 | parentNode.lastChild = previous; 495 | } 496 | _onUpdateChild(parentNode.ownerDocument,parentNode); 497 | return child; 498 | } 499 | /** 500 | * preformance key(refChild == null) 501 | */ 502 | function _insertBefore(parentNode,newChild,nextChild){ 503 | var cp = newChild.parentNode; 504 | if(cp){ 505 | cp.removeChild(newChild);//remove and update 506 | } 507 | if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ 508 | var newFirst = newChild.firstChild; 509 | if (newFirst == null) { 510 | return newChild; 511 | } 512 | var newLast = newChild.lastChild; 513 | }else{ 514 | newFirst = newLast = newChild; 515 | } 516 | var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild; 517 | 518 | newFirst.previousSibling = pre; 519 | newLast.nextSibling = nextChild; 520 | 521 | 522 | if(pre){ 523 | pre.nextSibling = newFirst; 524 | }else{ 525 | parentNode.firstChild = newFirst; 526 | } 527 | if(nextChild == null){ 528 | parentNode.lastChild = newLast; 529 | }else{ 530 | nextChild.previousSibling = newLast; 531 | } 532 | do{ 533 | newFirst.parentNode = parentNode; 534 | }while(newFirst !== newLast && (newFirst= newFirst.nextSibling)) 535 | _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode); 536 | //console.log(parentNode.lastChild.nextSibling == null) 537 | if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) { 538 | newChild.firstChild = newChild.lastChild = null; 539 | } 540 | return newChild; 541 | } 542 | function _appendSingleChild(parentNode,newChild){ 543 | var cp = newChild.parentNode; 544 | if(cp){ 545 | var pre = parentNode.lastChild; 546 | cp.removeChild(newChild);//remove and update 547 | var pre = parentNode.lastChild; 548 | } 549 | var pre = parentNode.lastChild; 550 | newChild.parentNode = parentNode; 551 | newChild.previousSibling = pre; 552 | newChild.nextSibling = null; 553 | if(pre){ 554 | pre.nextSibling = newChild; 555 | }else{ 556 | parentNode.firstChild = newChild; 557 | } 558 | parentNode.lastChild = newChild; 559 | _onUpdateChild(parentNode.ownerDocument,parentNode,newChild); 560 | return newChild; 561 | //console.log("__aa",parentNode.lastChild.nextSibling == null) 562 | } 563 | Document.prototype = { 564 | //implementation : null, 565 | nodeName : '#document', 566 | nodeType : DOCUMENT_NODE, 567 | doctype : null, 568 | documentElement : null, 569 | _inc : 1, 570 | 571 | insertBefore : function(newChild, refChild){//raises 572 | if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){ 573 | var child = newChild.firstChild; 574 | while(child){ 575 | var next = child.nextSibling; 576 | this.insertBefore(child,refChild); 577 | child = next; 578 | } 579 | return newChild; 580 | } 581 | if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){ 582 | this.documentElement = newChild; 583 | } 584 | 585 | return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild; 586 | }, 587 | removeChild : function(oldChild){ 588 | if(this.documentElement == oldChild){ 589 | this.documentElement = null; 590 | } 591 | return _removeChild(this,oldChild); 592 | }, 593 | // Introduced in DOM Level 2: 594 | importNode : function(importedNode,deep){ 595 | return importNode(this,importedNode,deep); 596 | }, 597 | // Introduced in DOM Level 2: 598 | getElementById : function(id){ 599 | var rtv = null; 600 | _visitNode(this.documentElement,function(node){ 601 | if(node.nodeType == ELEMENT_NODE){ 602 | if(node.getAttribute('id') == id){ 603 | rtv = node; 604 | return true; 605 | } 606 | } 607 | }) 608 | return rtv; 609 | }, 610 | 611 | //document factory method: 612 | createElement : function(tagName){ 613 | var node = new Element(); 614 | node.ownerDocument = this; 615 | node.nodeName = tagName; 616 | node.tagName = tagName; 617 | node.childNodes = new NodeList(); 618 | var attrs = node.attributes = new NamedNodeMap(); 619 | attrs._ownerElement = node; 620 | return node; 621 | }, 622 | createDocumentFragment : function(){ 623 | var node = new DocumentFragment(); 624 | node.ownerDocument = this; 625 | node.childNodes = new NodeList(); 626 | return node; 627 | }, 628 | createTextNode : function(data){ 629 | var node = new Text(); 630 | node.ownerDocument = this; 631 | node.appendData(data) 632 | return node; 633 | }, 634 | createComment : function(data){ 635 | var node = new Comment(); 636 | node.ownerDocument = this; 637 | node.appendData(data) 638 | return node; 639 | }, 640 | createCDATASection : function(data){ 641 | var node = new CDATASection(); 642 | node.ownerDocument = this; 643 | node.appendData(data) 644 | return node; 645 | }, 646 | createProcessingInstruction : function(target,data){ 647 | var node = new ProcessingInstruction(); 648 | node.ownerDocument = this; 649 | node.tagName = node.target = target; 650 | node.nodeValue= node.data = data; 651 | return node; 652 | }, 653 | createAttribute : function(name){ 654 | var node = new Attr(); 655 | node.ownerDocument = this; 656 | node.name = name; 657 | node.nodeName = name; 658 | node.localName = name; 659 | node.specified = true; 660 | return node; 661 | }, 662 | createEntityReference : function(name){ 663 | var node = new EntityReference(); 664 | node.ownerDocument = this; 665 | node.nodeName = name; 666 | return node; 667 | }, 668 | // Introduced in DOM Level 2: 669 | createElementNS : function(namespaceURI,qualifiedName){ 670 | var node = new Element(); 671 | var pl = qualifiedName.split(':'); 672 | var attrs = node.attributes = new NamedNodeMap(); 673 | node.childNodes = new NodeList(); 674 | node.ownerDocument = this; 675 | node.nodeName = qualifiedName; 676 | node.tagName = qualifiedName; 677 | node.namespaceURI = namespaceURI; 678 | if(pl.length == 2){ 679 | node.prefix = pl[0]; 680 | node.localName = pl[1]; 681 | }else{ 682 | //el.prefix = null; 683 | node.localName = qualifiedName; 684 | } 685 | attrs._ownerElement = node; 686 | return node; 687 | }, 688 | // Introduced in DOM Level 2: 689 | createAttributeNS : function(namespaceURI,qualifiedName){ 690 | var node = new Attr(); 691 | var pl = qualifiedName.split(':'); 692 | node.ownerDocument = this; 693 | node.nodeName = qualifiedName; 694 | node.name = qualifiedName; 695 | node.namespaceURI = namespaceURI; 696 | node.specified = true; 697 | if(pl.length == 2){ 698 | node.prefix = pl[0]; 699 | node.localName = pl[1]; 700 | }else{ 701 | //el.prefix = null; 702 | node.localName = qualifiedName; 703 | } 704 | return node; 705 | } 706 | }; 707 | _extends(Document,Node); 708 | 709 | 710 | function Element() { 711 | this._nsMap = {}; 712 | }; 713 | Element.prototype = { 714 | nodeType : ELEMENT_NODE, 715 | hasAttribute : function(name){ 716 | return this.getAttributeNode(name)!=null; 717 | }, 718 | getAttribute : function(name){ 719 | var attr = this.getAttributeNode(name); 720 | return attr && attr.value || ''; 721 | }, 722 | getAttributeNode : function(name){ 723 | return this.attributes.getNamedItem(name); 724 | }, 725 | setAttribute : function(name, value){ 726 | var attr = this.ownerDocument.createAttribute(name); 727 | attr.value = attr.nodeValue = "" + value; 728 | this.setAttributeNode(attr) 729 | }, 730 | removeAttribute : function(name){ 731 | var attr = this.getAttributeNode(name) 732 | attr && this.removeAttributeNode(attr); 733 | }, 734 | 735 | //four real opeartion method 736 | appendChild:function(newChild){ 737 | if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){ 738 | return this.insertBefore(newChild,null); 739 | }else{ 740 | return _appendSingleChild(this,newChild); 741 | } 742 | }, 743 | setAttributeNode : function(newAttr){ 744 | return this.attributes.setNamedItem(newAttr); 745 | }, 746 | setAttributeNodeNS : function(newAttr){ 747 | return this.attributes.setNamedItemNS(newAttr); 748 | }, 749 | removeAttributeNode : function(oldAttr){ 750 | //console.log(this == oldAttr.ownerElement) 751 | return this.attributes.removeNamedItem(oldAttr.nodeName); 752 | }, 753 | //get real attribute name,and remove it by removeAttributeNode 754 | removeAttributeNS : function(namespaceURI, localName){ 755 | var old = this.getAttributeNodeNS(namespaceURI, localName); 756 | old && this.removeAttributeNode(old); 757 | }, 758 | 759 | hasAttributeNS : function(namespaceURI, localName){ 760 | return this.getAttributeNodeNS(namespaceURI, localName)!=null; 761 | }, 762 | getAttributeNS : function(namespaceURI, localName){ 763 | var attr = this.getAttributeNodeNS(namespaceURI, localName); 764 | return attr && attr.value || ''; 765 | }, 766 | setAttributeNS : function(namespaceURI, qualifiedName, value){ 767 | var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName); 768 | attr.value = attr.nodeValue = "" + value; 769 | this.setAttributeNode(attr) 770 | }, 771 | getAttributeNodeNS : function(namespaceURI, localName){ 772 | return this.attributes.getNamedItemNS(namespaceURI, localName); 773 | }, 774 | 775 | getElementsByTagName : function(tagName){ 776 | return new LiveNodeList(this,function(base){ 777 | var ls = []; 778 | _visitNode(base,function(node){ 779 | if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){ 780 | ls.push(node); 781 | } 782 | }); 783 | return ls; 784 | }); 785 | }, 786 | getElementsByTagNameNS : function(namespaceURI, localName){ 787 | return new LiveNodeList(this,function(base){ 788 | var ls = []; 789 | _visitNode(base,function(node){ 790 | if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){ 791 | ls.push(node); 792 | } 793 | }); 794 | return ls; 795 | 796 | }); 797 | } 798 | }; 799 | Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName; 800 | Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS; 801 | 802 | 803 | _extends(Element,Node); 804 | function Attr() { 805 | }; 806 | Attr.prototype.nodeType = ATTRIBUTE_NODE; 807 | _extends(Attr,Node); 808 | 809 | 810 | function CharacterData() { 811 | }; 812 | CharacterData.prototype = { 813 | data : '', 814 | substringData : function(offset, count) { 815 | return this.data.substring(offset, offset+count); 816 | }, 817 | appendData: function(text) { 818 | text = this.data+text; 819 | this.nodeValue = this.data = text; 820 | this.length = text.length; 821 | }, 822 | insertData: function(offset,text) { 823 | this.replaceData(offset,0,text); 824 | 825 | }, 826 | appendChild:function(newChild){ 827 | throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR]) 828 | }, 829 | deleteData: function(offset, count) { 830 | this.replaceData(offset,count,""); 831 | }, 832 | replaceData: function(offset, count, text) { 833 | var start = this.data.substring(0,offset); 834 | var end = this.data.substring(offset+count); 835 | text = start + text + end; 836 | this.nodeValue = this.data = text; 837 | this.length = text.length; 838 | } 839 | } 840 | _extends(CharacterData,Node); 841 | function Text() { 842 | }; 843 | Text.prototype = { 844 | nodeName : "#text", 845 | nodeType : TEXT_NODE, 846 | splitText : function(offset) { 847 | var text = this.data; 848 | var newText = text.substring(offset); 849 | text = text.substring(0, offset); 850 | this.data = this.nodeValue = text; 851 | this.length = text.length; 852 | var newNode = this.ownerDocument.createTextNode(newText); 853 | if(this.parentNode){ 854 | this.parentNode.insertBefore(newNode, this.nextSibling); 855 | } 856 | return newNode; 857 | } 858 | } 859 | _extends(Text,CharacterData); 860 | function Comment() { 861 | }; 862 | Comment.prototype = { 863 | nodeName : "#comment", 864 | nodeType : COMMENT_NODE 865 | } 866 | _extends(Comment,CharacterData); 867 | 868 | function CDATASection() { 869 | }; 870 | CDATASection.prototype = { 871 | nodeName : "#cdata-section", 872 | nodeType : CDATA_SECTION_NODE 873 | } 874 | _extends(CDATASection,CharacterData); 875 | 876 | 877 | function DocumentType() { 878 | }; 879 | DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE; 880 | _extends(DocumentType,Node); 881 | 882 | function Notation() { 883 | }; 884 | Notation.prototype.nodeType = NOTATION_NODE; 885 | _extends(Notation,Node); 886 | 887 | function Entity() { 888 | }; 889 | Entity.prototype.nodeType = ENTITY_NODE; 890 | _extends(Entity,Node); 891 | 892 | function EntityReference() { 893 | }; 894 | EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE; 895 | _extends(EntityReference,Node); 896 | 897 | function DocumentFragment() { 898 | }; 899 | DocumentFragment.prototype.nodeName = "#document-fragment"; 900 | DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE; 901 | _extends(DocumentFragment,Node); 902 | 903 | 904 | function ProcessingInstruction() { 905 | } 906 | ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE; 907 | _extends(ProcessingInstruction,Node); 908 | function XMLSerializer(){} 909 | XMLSerializer.prototype.serializeToString = function(node,isHtml,nodeFilter){ 910 | return nodeSerializeToString.call(node,isHtml,nodeFilter); 911 | } 912 | Node.prototype.toString = nodeSerializeToString; 913 | function nodeSerializeToString(isHtml,nodeFilter){ 914 | var buf = []; 915 | var refNode = this.nodeType == 9 && this.documentElement || this; 916 | var prefix = refNode.prefix; 917 | var uri = refNode.namespaceURI; 918 | 919 | if(uri && prefix == null){ 920 | //console.log(prefix) 921 | var prefix = refNode.lookupPrefix(uri); 922 | if(prefix == null){ 923 | //isHTML = true; 924 | var visibleNamespaces=[ 925 | {namespace:uri,prefix:null} 926 | //{namespace:uri,prefix:''} 927 | ] 928 | } 929 | } 930 | serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces); 931 | //console.log('###',this.nodeType,uri,prefix,buf.join('')) 932 | return buf.join(''); 933 | } 934 | function needNamespaceDefine(node,isHTML, visibleNamespaces) { 935 | var prefix = node.prefix||''; 936 | var uri = node.namespaceURI; 937 | if (!prefix && !uri){ 938 | return false; 939 | } 940 | if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 941 | || uri == 'http://www.w3.org/2000/xmlns/'){ 942 | return false; 943 | } 944 | 945 | var i = visibleNamespaces.length 946 | //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces) 947 | while (i--) { 948 | var ns = visibleNamespaces[i]; 949 | // get namespace prefix 950 | //console.log(node.nodeType,node.tagName,ns.prefix,prefix) 951 | if (ns.prefix == prefix){ 952 | return ns.namespace != uri; 953 | } 954 | } 955 | //console.log(isHTML,uri,prefix=='') 956 | //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){ 957 | // return false; 958 | //} 959 | //node.flag = '11111' 960 | //console.error(3,true,node.flag,node.prefix,node.namespaceURI) 961 | return true; 962 | } 963 | function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){ 964 | if(nodeFilter){ 965 | node = nodeFilter(node); 966 | if(node){ 967 | if(typeof node == 'string'){ 968 | buf.push(node); 969 | return; 970 | } 971 | }else{ 972 | return; 973 | } 974 | //buf.sort.apply(attrs, attributeSorter); 975 | } 976 | switch(node.nodeType){ 977 | case ELEMENT_NODE: 978 | if (!visibleNamespaces) visibleNamespaces = []; 979 | var startVisibleNamespaces = visibleNamespaces.length; 980 | var attrs = node.attributes; 981 | var len = attrs.length; 982 | var child = node.firstChild; 983 | var nodeName = node.tagName; 984 | 985 | isHTML = (htmlns === node.namespaceURI) ||isHTML 986 | buf.push('<',nodeName); 987 | 988 | 989 | 990 | for(var i=0;i'); 1021 | //if is cdata child node 1022 | if(isHTML && /^script$/i.test(nodeName)){ 1023 | while(child){ 1024 | if(child.data){ 1025 | buf.push(child.data); 1026 | }else{ 1027 | serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); 1028 | } 1029 | child = child.nextSibling; 1030 | } 1031 | }else 1032 | { 1033 | while(child){ 1034 | serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); 1035 | child = child.nextSibling; 1036 | } 1037 | } 1038 | buf.push(''); 1039 | }else{ 1040 | buf.push('/>'); 1041 | } 1042 | // remove added visible namespaces 1043 | //visibleNamespaces.length = startVisibleNamespaces; 1044 | return; 1045 | case DOCUMENT_NODE: 1046 | case DOCUMENT_FRAGMENT_NODE: 1047 | var child = node.firstChild; 1048 | while(child){ 1049 | serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces); 1050 | child = child.nextSibling; 1051 | } 1052 | return; 1053 | case ATTRIBUTE_NODE: 1054 | return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"'); 1055 | case TEXT_NODE: 1056 | return buf.push(node.data.replace(/[<&]/g,_xmlEncoder)); 1057 | case CDATA_SECTION_NODE: 1058 | return buf.push( ''); 1059 | case COMMENT_NODE: 1060 | return buf.push( ""); 1061 | case DOCUMENT_TYPE_NODE: 1062 | var pubid = node.publicId; 1063 | var sysid = node.systemId; 1064 | buf.push(''); 1071 | }else if(sysid && sysid!='.'){ 1072 | buf.push(' SYSTEM "',sysid,'">'); 1073 | }else{ 1074 | var sub = node.internalSubset; 1075 | if(sub){ 1076 | buf.push(" [",sub,"]"); 1077 | } 1078 | buf.push(">"); 1079 | } 1080 | return; 1081 | case PROCESSING_INSTRUCTION_NODE: 1082 | return buf.push( ""); 1083 | case ENTITY_REFERENCE_NODE: 1084 | return buf.push( '&',node.nodeName,';'); 1085 | //case ENTITY_NODE: 1086 | //case NOTATION_NODE: 1087 | default: 1088 | buf.push('??',node.nodeName); 1089 | } 1090 | } 1091 | function importNode(doc,node,deep){ 1092 | var node2; 1093 | switch (node.nodeType) { 1094 | case ELEMENT_NODE: 1095 | node2 = node.cloneNode(false); 1096 | node2.ownerDocument = doc; 1097 | //var attrs = node2.attributes; 1098 | //var len = attrs.length; 1099 | //for(var i=0;i', 4 | amp: '&', 5 | quot: '"', 6 | apos: "'", 7 | Agrave: "À", 8 | Aacute: "Á", 9 | Acirc: "Â", 10 | Atilde: "Ã", 11 | Auml: "Ä", 12 | Aring: "Å", 13 | AElig: "Æ", 14 | Ccedil: "Ç", 15 | Egrave: "È", 16 | Eacute: "É", 17 | Ecirc: "Ê", 18 | Euml: "Ë", 19 | Igrave: "Ì", 20 | Iacute: "Í", 21 | Icirc: "Î", 22 | Iuml: "Ï", 23 | ETH: "Ð", 24 | Ntilde: "Ñ", 25 | Ograve: "Ò", 26 | Oacute: "Ó", 27 | Ocirc: "Ô", 28 | Otilde: "Õ", 29 | Ouml: "Ö", 30 | Oslash: "Ø", 31 | Ugrave: "Ù", 32 | Uacute: "Ú", 33 | Ucirc: "Û", 34 | Uuml: "Ü", 35 | Yacute: "Ý", 36 | THORN: "Þ", 37 | szlig: "ß", 38 | agrave: "à", 39 | aacute: "á", 40 | acirc: "â", 41 | atilde: "ã", 42 | auml: "ä", 43 | aring: "å", 44 | aelig: "æ", 45 | ccedil: "ç", 46 | egrave: "è", 47 | eacute: "é", 48 | ecirc: "ê", 49 | euml: "ë", 50 | igrave: "ì", 51 | iacute: "í", 52 | icirc: "î", 53 | iuml: "ï", 54 | eth: "ð", 55 | ntilde: "ñ", 56 | ograve: "ò", 57 | oacute: "ó", 58 | ocirc: "ô", 59 | otilde: "õ", 60 | ouml: "ö", 61 | oslash: "ø", 62 | ugrave: "ù", 63 | uacute: "ú", 64 | ucirc: "û", 65 | uuml: "ü", 66 | yacute: "ý", 67 | thorn: "þ", 68 | yuml: "ÿ", 69 | nbsp: " ", 70 | iexcl: "¡", 71 | cent: "¢", 72 | pound: "£", 73 | curren: "¤", 74 | yen: "¥", 75 | brvbar: "¦", 76 | sect: "§", 77 | uml: "¨", 78 | copy: "©", 79 | ordf: "ª", 80 | laquo: "«", 81 | not: "¬", 82 | shy: "­­", 83 | reg: "®", 84 | macr: "¯", 85 | deg: "°", 86 | plusmn: "±", 87 | sup2: "²", 88 | sup3: "³", 89 | acute: "´", 90 | micro: "µ", 91 | para: "¶", 92 | middot: "·", 93 | cedil: "¸", 94 | sup1: "¹", 95 | ordm: "º", 96 | raquo: "»", 97 | frac14: "¼", 98 | frac12: "½", 99 | frac34: "¾", 100 | iquest: "¿", 101 | times: "×", 102 | divide: "÷", 103 | forall: "∀", 104 | part: "∂", 105 | exist: "∃", 106 | empty: "∅", 107 | nabla: "∇", 108 | isin: "∈", 109 | notin: "∉", 110 | ni: "∋", 111 | prod: "∏", 112 | sum: "∑", 113 | minus: "−", 114 | lowast: "∗", 115 | radic: "√", 116 | prop: "∝", 117 | infin: "∞", 118 | ang: "∠", 119 | and: "∧", 120 | or: "∨", 121 | cap: "∩", 122 | cup: "∪", 123 | 'int': "∫", 124 | there4: "∴", 125 | sim: "∼", 126 | cong: "≅", 127 | asymp: "≈", 128 | ne: "≠", 129 | equiv: "≡", 130 | le: "≤", 131 | ge: "≥", 132 | sub: "⊂", 133 | sup: "⊃", 134 | nsub: "⊄", 135 | sube: "⊆", 136 | supe: "⊇", 137 | oplus: "⊕", 138 | otimes: "⊗", 139 | perp: "⊥", 140 | sdot: "⋅", 141 | Alpha: "Α", 142 | Beta: "Β", 143 | Gamma: "Γ", 144 | Delta: "Δ", 145 | Epsilon: "Ε", 146 | Zeta: "Ζ", 147 | Eta: "Η", 148 | Theta: "Θ", 149 | Iota: "Ι", 150 | Kappa: "Κ", 151 | Lambda: "Λ", 152 | Mu: "Μ", 153 | Nu: "Ν", 154 | Xi: "Ξ", 155 | Omicron: "Ο", 156 | Pi: "Π", 157 | Rho: "Ρ", 158 | Sigma: "Σ", 159 | Tau: "Τ", 160 | Upsilon: "Υ", 161 | Phi: "Φ", 162 | Chi: "Χ", 163 | Psi: "Ψ", 164 | Omega: "Ω", 165 | alpha: "α", 166 | beta: "β", 167 | gamma: "γ", 168 | delta: "δ", 169 | epsilon: "ε", 170 | zeta: "ζ", 171 | eta: "η", 172 | theta: "θ", 173 | iota: "ι", 174 | kappa: "κ", 175 | lambda: "λ", 176 | mu: "μ", 177 | nu: "ν", 178 | xi: "ξ", 179 | omicron: "ο", 180 | pi: "π", 181 | rho: "ρ", 182 | sigmaf: "ς", 183 | sigma: "σ", 184 | tau: "τ", 185 | upsilon: "υ", 186 | phi: "φ", 187 | chi: "χ", 188 | psi: "ψ", 189 | omega: "ω", 190 | thetasym: "ϑ", 191 | upsih: "ϒ", 192 | piv: "ϖ", 193 | OElig: "Œ", 194 | oelig: "œ", 195 | Scaron: "Š", 196 | scaron: "š", 197 | Yuml: "Ÿ", 198 | fnof: "ƒ", 199 | circ: "ˆ", 200 | tilde: "˜", 201 | ensp: " ", 202 | emsp: " ", 203 | thinsp: " ", 204 | zwnj: "‌", 205 | zwj: "‍", 206 | lrm: "‎", 207 | rlm: "‏", 208 | ndash: "–", 209 | mdash: "—", 210 | lsquo: "‘", 211 | rsquo: "’", 212 | sbquo: "‚", 213 | ldquo: "“", 214 | rdquo: "”", 215 | bdquo: "„", 216 | dagger: "†", 217 | Dagger: "‡", 218 | bull: "•", 219 | hellip: "…", 220 | permil: "‰", 221 | prime: "′", 222 | Prime: "″", 223 | lsaquo: "‹", 224 | rsaquo: "›", 225 | oline: "‾", 226 | euro: "€", 227 | trade: "™", 228 | larr: "←", 229 | uarr: "↑", 230 | rarr: "→", 231 | darr: "↓", 232 | harr: "↔", 233 | crarr: "↵", 234 | lceil: "⌈", 235 | rceil: "⌉", 236 | lfloor: "⌊", 237 | rfloor: "⌋", 238 | loz: "◊", 239 | spades: "♠", 240 | clubs: "♣", 241 | hearts: "♥", 242 | diams: "♦" 243 | }; 244 | //for(var n in exports.entityMap){console.log(exports.entityMap[n].charCodeAt())} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xmldom-alpha", 3 | "version": "0.1.28", 4 | "description": "A W3C Standard XML DOM(Level2 CORE) implementation and parser(DOMParser/XMLSerializer).", 5 | "keywords": ["w3c","dom","xml","parser","javascript","DOMParser","XMLSerializer"], 6 | "author": "jindw (http://www.xidea.org)", 7 | "homepage": "https://github.com/jindw/xmldom", 8 | "repository": {"type": "git","url": "git://github.com/jindw/xmldom.git"}, 9 | "main": "./dom-parser.js", 10 | "scripts" : { "test": "proof platform win32 && proof test */*/*.t.js || t/test" }, 11 | "engines": {"node": ">=0.1"}, 12 | "dependencies": {}, 13 | "devDependencies": { "proof": "0.0.28" }, 14 | "maintainers": [{"name": "jindw","email": "jindw@xidea.org","url": "http://www.xidea.org"}], 15 | "contributors": [ 16 | {"name" : "Yaron Naveh","email" : "yaronn01@gmail.com","web" : "http://webservices20.blogspot.com/"}, 17 | {"name" : "Harutyun Amirjanyan","email" : "amirjanyan@gmail.com","web" : "https://github.com/nightwing"}, 18 | {"name" : "Alan Gutierrez","email" : "alan@prettyrobots.com","web" : "http://www.prettyrobots.com/"} 19 | ], 20 | "bugs": {"email": "jindw@xidea.org","url": "http://github.com/jindw/xmldom/issues"}, 21 | "license": "(LGPL-2.0 or MIT)" 22 | } 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # XMLDOM [![Build Status](https://secure.travis-ci.org/bigeasy/xmldom.png?branch=master)](http://travis-ci.org/bigeasy/xmldom) [![Coverage Status](https://coveralls.io/repos/bigeasy/xmldom/badge.png?branch=master)](https://coveralls.io/r/bigeasy/xmldom) [![NPM version](https://badge.fury.io/js/xmldom.png)](http://badge.fury.io/js/xmldom) 2 | 3 | A JavaScript implementation of W3C DOM for Node.js, Rhino and the browser. Fully 4 | compatible with `W3C DOM level2`; and some compatible with `level3`. Supports 5 | `DOMParser` and `XMLSerializer` interface such as in browser. 6 | 7 | Install: 8 | ------- 9 | >npm install xmldom 10 | 11 | Example: 12 | ==== 13 | ```javascript 14 | var DOMParser = require('xmldom').DOMParser; 15 | var doc = new DOMParser().parseFromString( 16 | '\n'+ 17 | '\ttest\n'+ 18 | '\t\n'+ 19 | '\t\n'+ 20 | '' 21 | ,'text/xml'); 22 | doc.documentElement.setAttribute('x','y'); 23 | doc.documentElement.setAttributeNS('./lite','c:x','y2'); 24 | var nsAttr = doc.documentElement.getAttributeNS('./lite','x') 25 | console.info(nsAttr) 26 | console.info(doc) 27 | ``` 28 | API Reference 29 | ===== 30 | 31 | * [DOMParser](https://developer.mozilla.org/en/DOMParser): 32 | 33 | ```javascript 34 | parseFromString(xmlsource,mimeType) 35 | ``` 36 | * **options extension** _by xmldom_(not BOM standard!!) 37 | 38 | ```javascript 39 | //added the options argument 40 | new DOMParser(options) 41 | 42 | //errorHandler is supported 43 | new DOMParser({ 44 | /** 45 | * locator is always need for error position info 46 | */ 47 | locator:{}, 48 | /** 49 | * you can override the errorHandler for xml parser 50 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html 51 | */ 52 | errorHandler:{warning:function(w){console.warn(w)},error:callback,fatalError:callback} 53 | //only callback model 54 | //errorHandler:function(level,msg){console.log(level,msg)} 55 | }) 56 | 57 | ``` 58 | 59 | * [XMLSerializer](https://developer.mozilla.org/en/XMLSerializer) 60 | 61 | ```javascript 62 | serializeToString(node) 63 | ``` 64 | DOM level2 method and attribute: 65 | ------ 66 | 67 | * [Node](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247) 68 | 69 | attribute: 70 | nodeValue|prefix 71 | readonly attribute: 72 | nodeName|nodeType|parentNode|childNodes|firstChild|lastChild|previousSibling|nextSibling|attributes|ownerDocument|namespaceURI|localName 73 | method: 74 | insertBefore(newChild, refChild) 75 | replaceChild(newChild, oldChild) 76 | removeChild(oldChild) 77 | appendChild(newChild) 78 | hasChildNodes() 79 | cloneNode(deep) 80 | normalize() 81 | isSupported(feature, version) 82 | hasAttributes() 83 | 84 | * [DOMImplementation](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-102161490) 85 | 86 | method: 87 | hasFeature(feature, version) 88 | createDocumentType(qualifiedName, publicId, systemId) 89 | createDocument(namespaceURI, qualifiedName, doctype) 90 | 91 | * [Document](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#i-Document) : Node 92 | 93 | readonly attribute: 94 | doctype|implementation|documentElement 95 | method: 96 | createElement(tagName) 97 | createDocumentFragment() 98 | createTextNode(data) 99 | createComment(data) 100 | createCDATASection(data) 101 | createProcessingInstruction(target, data) 102 | createAttribute(name) 103 | createEntityReference(name) 104 | getElementsByTagName(tagname) 105 | importNode(importedNode, deep) 106 | createElementNS(namespaceURI, qualifiedName) 107 | createAttributeNS(namespaceURI, qualifiedName) 108 | getElementsByTagNameNS(namespaceURI, localName) 109 | getElementById(elementId) 110 | 111 | * [DocumentFragment](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-B63ED1A3) : Node 112 | * [Element](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-745549614) : Node 113 | 114 | readonly attribute: 115 | tagName 116 | method: 117 | getAttribute(name) 118 | setAttribute(name, value) 119 | removeAttribute(name) 120 | getAttributeNode(name) 121 | setAttributeNode(newAttr) 122 | removeAttributeNode(oldAttr) 123 | getElementsByTagName(name) 124 | getAttributeNS(namespaceURI, localName) 125 | setAttributeNS(namespaceURI, qualifiedName, value) 126 | removeAttributeNS(namespaceURI, localName) 127 | getAttributeNodeNS(namespaceURI, localName) 128 | setAttributeNodeNS(newAttr) 129 | getElementsByTagNameNS(namespaceURI, localName) 130 | hasAttribute(name) 131 | hasAttributeNS(namespaceURI, localName) 132 | 133 | * [Attr](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-637646024) : Node 134 | 135 | attribute: 136 | value 137 | readonly attribute: 138 | name|specified|ownerElement 139 | 140 | * [NodeList](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177) 141 | 142 | readonly attribute: 143 | length 144 | method: 145 | item(index) 146 | 147 | * [NamedNodeMap](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1780488922) 148 | 149 | readonly attribute: 150 | length 151 | method: 152 | getNamedItem(name) 153 | setNamedItem(arg) 154 | removeNamedItem(name) 155 | item(index) 156 | getNamedItemNS(namespaceURI, localName) 157 | setNamedItemNS(arg) 158 | removeNamedItemNS(namespaceURI, localName) 159 | 160 | * [CharacterData](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-FF21A306) : Node 161 | 162 | method: 163 | substringData(offset, count) 164 | appendData(arg) 165 | insertData(offset, arg) 166 | deleteData(offset, count) 167 | replaceData(offset, count, arg) 168 | 169 | * [Text](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1312295772) : CharacterData 170 | 171 | method: 172 | splitText(offset) 173 | 174 | * [CDATASection](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-667469212) 175 | * [Comment](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1728279322) : CharacterData 176 | 177 | * [DocumentType](http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-412266927) 178 | 179 | readonly attribute: 180 | name|entities|notations|publicId|systemId|internalSubset 181 | 182 | * Notation : Node 183 | 184 | readonly attribute: 185 | publicId|systemId 186 | 187 | * Entity : Node 188 | 189 | readonly attribute: 190 | publicId|systemId|notationName 191 | 192 | * EntityReference : Node 193 | * ProcessingInstruction : Node 194 | 195 | attribute: 196 | data 197 | readonly attribute: 198 | target 199 | 200 | DOM level 3 support: 201 | ----- 202 | 203 | * [Node](http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-textContent) 204 | 205 | attribute: 206 | textContent 207 | method: 208 | isDefaultNamespace(namespaceURI){ 209 | lookupNamespaceURI(prefix) 210 | 211 | DOM extension by xmldom 212 | --- 213 | * [Node] Source position extension; 214 | 215 | attribute: 216 | //Numbered starting from '1' 217 | lineNumber 218 | //Numbered starting from '1' 219 | columnNumber 220 | -------------------------------------------------------------------------------- /sax.js: -------------------------------------------------------------------------------- 1 | //[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF] 2 | //[4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] 3 | //[5] Name ::= NameStartChar (NameChar)* 4 | var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF 5 | var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]"); 6 | var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$'); 7 | //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/ 8 | //var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',') 9 | 10 | //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE 11 | //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE 12 | var S_TAG = 0;//tag name offerring 13 | var S_ATTR = 1;//attr name offerring 14 | var S_ATTR_SPACE=2;//attr name end and space offer 15 | var S_EQ = 3;//=space? 16 | var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only) 17 | var S_ATTR_END = 5;//attr value end and no space(quot end) 18 | var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer) 19 | var S_TAG_CLOSE = 7;//closed el 20 | 21 | function XMLReader(){ 22 | 23 | } 24 | 25 | XMLReader.prototype = { 26 | parse:function(source,defaultNSMap,entityMap){ 27 | var domBuilder = this.domBuilder; 28 | domBuilder.startDocument(); 29 | _copy(defaultNSMap ,defaultNSMap = {}) 30 | parse(source,defaultNSMap,entityMap, 31 | domBuilder,this.errorHandler); 32 | domBuilder.endDocument(); 33 | } 34 | } 35 | function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){ 36 | function fixedFromCharCode(code) { 37 | // String.prototype.fromCharCode does not supports 38 | // > 2 bytes unicode chars directly 39 | if (code > 0xffff) { 40 | code -= 0x10000; 41 | var surrogate1 = 0xd800 + (code >> 10) 42 | , surrogate2 = 0xdc00 + (code & 0x3ff); 43 | 44 | return String.fromCharCode(surrogate1, surrogate2); 45 | } else { 46 | return String.fromCharCode(code); 47 | } 48 | } 49 | function entityReplacer(a){ 50 | var k = a.slice(1,-1); 51 | if(k in entityMap){ 52 | return entityMap[k]; 53 | }else if(k.charAt(0) === '#'){ 54 | return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x'))) 55 | }else{ 56 | errorHandler.error('entity not found:'+a); 57 | return a; 58 | } 59 | } 60 | function appendText(end){//has some bugs 61 | if(end>start){ 62 | var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer); 63 | locator&&position(start); 64 | domBuilder.characters(xt,0,end-start); 65 | start = end 66 | } 67 | } 68 | function position(p,m){ 69 | while(p>=lineEnd && (m = linePattern.exec(source))){ 70 | lineStart = m.index; 71 | lineEnd = lineStart + m[0].length; 72 | locator.lineNumber++; 73 | //console.log('line++:',locator,startPos,endPos) 74 | } 75 | locator.columnNumber = p-lineStart+1; 76 | } 77 | var lineStart = 0; 78 | var lineEnd = 0; 79 | var linePattern = /.*(?:\r\n?|\n)|.*$/g 80 | var locator = domBuilder.locator; 81 | 82 | var parseStack = [{currentNSMap:defaultNSMapCopy}] 83 | var closeMap = {}; 84 | var start = 0; 85 | while(true){ 86 | try{ 87 | var tagStart = source.indexOf('<',start); 88 | if(tagStart<0){ 89 | if(!source.substr(start).match(/^\s*$/)){ 90 | var doc = domBuilder.doc; 91 | var text = doc.createTextNode(source.substr(start)); 92 | doc.appendChild(text); 93 | domBuilder.currentElement = text; 94 | } 95 | return; 96 | } 97 | if(tagStart>start){ 98 | appendText(tagStart); 99 | } 100 | switch(source.charAt(tagStart+1)){ 101 | case '/': 102 | var end = source.indexOf('>',tagStart+3); 103 | var tagName = source.substring(tagStart+2,end); 104 | var config = parseStack.pop(); 105 | if(end<0){ 106 | 107 | tagName = source.substring(tagStart+2).replace(/[\s<].*/,''); 108 | //console.error('#@@@@@@'+tagName) 109 | errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName); 110 | end = tagStart+1+tagName.length; 111 | }else if(tagName.match(/\s 139 | locator&&position(tagStart); 140 | end = parseInstruction(source,tagStart,domBuilder); 141 | break; 142 | case '!':// start){ 196 | start = end; 197 | }else{ 198 | //TODO: 这里有可能sax回退,有位置错误风险 199 | appendText(Math.max(tagStart,start)+1); 200 | } 201 | } 202 | } 203 | function copyLocator(f,t){ 204 | t.lineNumber = f.lineNumber; 205 | t.columnNumber = f.columnNumber; 206 | return t; 207 | } 208 | 209 | /** 210 | * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack); 211 | * @return end of the elementStartPart(end of elementEndPart for selfClosed el) 212 | */ 213 | function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){ 214 | var attrName; 215 | var value; 216 | var p = ++start; 217 | var s = S_TAG;//status 218 | while(true){ 219 | var c = source.charAt(p); 220 | switch(c){ 221 | case '=': 222 | if(s === S_ATTR){//attrName 223 | attrName = source.slice(start,p); 224 | s = S_EQ; 225 | }else if(s === S_ATTR_SPACE){ 226 | s = S_EQ; 227 | }else{ 228 | //fatalError: equal must after attrName or space after attrName 229 | throw new Error('attribute equal must after attrName'); 230 | } 231 | break; 232 | case '\'': 233 | case '"': 234 | if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE 235 | ){//equal 236 | if(s === S_ATTR){ 237 | errorHandler.warning('attribute value must after "="') 238 | attrName = source.slice(start,p) 239 | } 240 | start = p+1; 241 | p = source.indexOf(c,start) 242 | if(p>0){ 243 | value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); 244 | el.add(attrName,value,start-1); 245 | s = S_ATTR_END; 246 | }else{ 247 | //fatalError: no end quot match 248 | throw new Error('attribute value no end \''+c+'\' match'); 249 | } 250 | }else if(s == S_ATTR_NOQUOT_VALUE){ 251 | value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); 252 | //console.log(attrName,value,start,p) 253 | el.add(attrName,value,start); 254 | //console.dir(el) 255 | errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!'); 256 | start = p+1; 257 | s = S_ATTR_END 258 | }else{ 259 | //fatalError: no equal before 260 | throw new Error('attribute value must after "="'); 261 | } 262 | break; 263 | case '/': 264 | switch(s){ 265 | case S_TAG: 266 | el.setTagName(source.slice(start,p)); 267 | case S_ATTR_END: 268 | case S_TAG_SPACE: 269 | case S_TAG_CLOSE: 270 | s =S_TAG_CLOSE; 271 | el.closed = true; 272 | case S_ATTR_NOQUOT_VALUE: 273 | case S_ATTR: 274 | case S_ATTR_SPACE: 275 | break; 276 | //case S_EQ: 277 | default: 278 | throw new Error("attribute invalid close char('/')") 279 | } 280 | break; 281 | case ''://end document 282 | //throw new Error('unexpected end of input') 283 | errorHandler.error('unexpected end of input'); 284 | if(s == S_TAG){ 285 | el.setTagName(source.slice(start,p)); 286 | } 287 | return p; 288 | case '>': 289 | switch(s){ 290 | case S_TAG: 291 | el.setTagName(source.slice(start,p)); 292 | case S_ATTR_END: 293 | case S_TAG_SPACE: 294 | case S_TAG_CLOSE: 295 | break;//normal 296 | case S_ATTR_NOQUOT_VALUE://Compatible state 297 | case S_ATTR: 298 | value = source.slice(start,p); 299 | if(value.slice(-1) === '/'){ 300 | el.closed = true; 301 | value = value.slice(0,-1) 302 | } 303 | case S_ATTR_SPACE: 304 | if(s === S_ATTR_SPACE){ 305 | value = attrName; 306 | } 307 | if(s == S_ATTR_NOQUOT_VALUE){ 308 | errorHandler.warning('attribute "'+value+'" missed quot(")!!'); 309 | el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start) 310 | }else{ 311 | if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){ 312 | errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!') 313 | } 314 | el.add(value,value,start) 315 | } 316 | break; 317 | case S_EQ: 318 | throw new Error('attribute value missed!!'); 319 | } 320 | // console.log(tagName,tagNamePattern,tagNamePattern.test(tagName)) 321 | return p; 322 | /*xml space '\x20' | #x9 | #xD | #xA; */ 323 | case '\u0080': 324 | c = ' '; 325 | default: 326 | if(c<= ' '){//space 327 | switch(s){ 328 | case S_TAG: 329 | el.setTagName(source.slice(start,p));//tagName 330 | s = S_TAG_SPACE; 331 | break; 332 | case S_ATTR: 333 | attrName = source.slice(start,p) 334 | s = S_ATTR_SPACE; 335 | break; 336 | case S_ATTR_NOQUOT_VALUE: 337 | var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer); 338 | errorHandler.warning('attribute "'+value+'" missed quot(")!!'); 339 | el.add(attrName,value,start) 340 | case S_ATTR_END: 341 | s = S_TAG_SPACE; 342 | break; 343 | //case S_TAG_SPACE: 344 | //case S_EQ: 345 | //case S_ATTR_SPACE: 346 | // void();break; 347 | //case S_TAG_CLOSE: 348 | //ignore warning 349 | } 350 | }else{//not space 351 | //S_TAG, S_ATTR, S_EQ, S_ATTR_NOQUOT_VALUE 352 | //S_ATTR_SPACE, S_ATTR_END, S_TAG_SPACE, S_TAG_CLOSE 353 | switch(s){ 354 | //case S_TAG:void();break; 355 | //case S_ATTR:void();break; 356 | //case S_ATTR_NOQUOT_VALUE:void();break; 357 | case S_ATTR_SPACE: 358 | var tagName = el.tagName; 359 | if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){ 360 | errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!') 361 | } 362 | el.add(attrName,attrName,start); 363 | start = p; 364 | s = S_ATTR; 365 | break; 366 | case S_ATTR_END: 367 | errorHandler.warning('attribute space is required"'+attrName+'"!!') 368 | case S_TAG_SPACE: 369 | s = S_ATTR; 370 | start = p; 371 | break; 372 | case S_EQ: 373 | s = S_ATTR_NOQUOT_VALUE; 374 | start = p; 375 | break; 376 | case S_TAG_CLOSE: 377 | throw new Error("elements closed character '/' and '>' must be connected to"); 378 | } 379 | } 380 | }//end outer switch 381 | //console.log('p++',p) 382 | p++; 383 | } 384 | } 385 | /** 386 | * @return true if has new namespace define 387 | */ 388 | function appendElement(el,domBuilder,currentNSMap){ 389 | var tagName = el.tagName; 390 | var localNSMap = null; 391 | //var currentNSMap = parseStack[parseStack.length-1].currentNSMap; 392 | var i = el.length; 393 | while(i--){ 394 | var a = el[i]; 395 | var qName = a.qName; 396 | var value = a.value; 397 | var nsp = qName.indexOf(':'); 398 | if(nsp>0){ 399 | var prefix = a.prefix = qName.slice(0,nsp); 400 | var localName = qName.slice(nsp+1); 401 | var nsPrefix = prefix === 'xmlns' && localName 402 | }else{ 403 | localName = qName; 404 | prefix = null 405 | nsPrefix = qName === 'xmlns' && '' 406 | } 407 | //can not set prefix,because prefix !== '' 408 | a.localName = localName ; 409 | //prefix == null for no ns prefix attribute 410 | if(nsPrefix !== false){//hack!! 411 | if(localNSMap == null){ 412 | localNSMap = {} 413 | //console.log(currentNSMap,0) 414 | _copy(currentNSMap,currentNSMap={}) 415 | //console.log(currentNSMap,1) 416 | } 417 | currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value; 418 | a.uri = 'http://www.w3.org/2000/xmlns/' 419 | domBuilder.startPrefixMapping(nsPrefix, value) 420 | } 421 | } 422 | var i = el.length; 423 | while(i--){ 424 | a = el[i]; 425 | var prefix = a.prefix; 426 | if(prefix){//no prefix attribute has no namespace 427 | if(prefix === 'xml'){ 428 | a.uri = 'http://www.w3.org/XML/1998/namespace'; 429 | }if(prefix !== 'xmlns'){ 430 | a.uri = currentNSMap[prefix || ''] 431 | 432 | //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)} 433 | } 434 | } 435 | } 436 | var nsp = tagName.indexOf(':'); 437 | if(nsp>0){ 438 | prefix = el.prefix = tagName.slice(0,nsp); 439 | localName = el.localName = tagName.slice(nsp+1); 440 | }else{ 441 | prefix = null;//important!! 442 | localName = el.localName = tagName; 443 | } 444 | //no prefix element has default namespace 445 | var ns = el.uri = currentNSMap[prefix || '']; 446 | domBuilder.startElement(ns,localName,tagName,el); 447 | //endPrefixMapping and startPrefixMapping have not any help for dom builder 448 | //localNSMap = null 449 | if(el.closed){ 450 | domBuilder.endElement(ns,localName,tagName); 451 | if(localNSMap){ 452 | for(prefix in localNSMap){ 453 | domBuilder.endPrefixMapping(prefix) 454 | } 455 | } 456 | }else{ 457 | el.currentNSMap = currentNSMap; 458 | el.localNSMap = localNSMap; 459 | //parseStack.push(el); 460 | return true; 461 | } 462 | } 463 | function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){ 464 | if(/^(?:script|textarea)$/i.test(tagName)){ 465 | var elEndStart = source.indexOf('',elStartEnd); 466 | var text = source.substring(elStartEnd+1,elEndStart); 467 | if(/[&<]/.test(text)){ 468 | if(/^script$/i.test(tagName)){ 469 | //if(!/\]\]>/.test(text)){ 470 | //lexHandler.startCDATA(); 471 | domBuilder.characters(text,0,text.length); 472 | //lexHandler.endCDATA(); 473 | return elEndStart; 474 | //} 475 | }//}else{//text area 476 | text = text.replace(/&#?\w+;/g,entityReplacer); 477 | domBuilder.characters(text,0,text.length); 478 | return elEndStart; 479 | //} 480 | 481 | } 482 | } 483 | return elStartEnd+1; 484 | } 485 | function fixSelfClosed(source,elStartEnd,tagName,closeMap){ 486 | //if(tagName in closeMap){ 487 | var pos = closeMap[tagName]; 488 | if(pos == null){ 489 | //console.log(tagName) 490 | pos = source.lastIndexOf('') 491 | if(pos',start+4); 508 | //append comment source.substring(4,end)//\n\ 14 | \n\ 15 | \n\ 16 | \n\ 17 | \n\ 18 | \n\ 19 | '; 20 | //console.log(xml) 21 | var error = [] 22 | var parser = new DOMParser({ 23 | locator:{}, 24 | errorHandler:{ 25 | error:function(msg){ 26 | error.push(msg); 27 | //throw new Error(msg) 28 | } 29 | } 30 | }); 31 | var doc = parser.parseFromString(xml, 'text/html'); 32 | //console.log(doc.toString()) 33 | var doc = parser.parseFromString('<;test<1 1="2"/>', 'text/xml'); 44 | 45 | error.map(function(e){error[e.replace(/\:[\s\S]*/,'')]=e}) 46 | console.log(error) 47 | console.assert(error.warning!=null ,'error.error:'+error.warning); 48 | console.assert(error.error!=null ,'error.error:'+error.error); 49 | console.assert(error.fatalError!=null ,'error.error:'+error.fatalError); 50 | //console.log(doc+'') 51 | }catch(e){ 52 | } 53 | }, 54 | 'only function1': function() { 55 | var error = [] 56 | var parser = new DOMParser({ 57 | errorHandler:function(msg){error.push(msg)} 58 | }); 59 | try{ 60 | var doc = parser.parseFromString('<1 1="2"/>', 'text/xml'); 61 | error.map(function(e){error[e.replace(/\:[\s\S]*/,'')]=e}) 62 | console.assert(error.warning!=null ,'error.error:'+error.warning); 63 | console.assert(error.error!=null ,'error.error:'+error.error); 64 | console.assert(error.fatalError!=null ,'error.error:'+error.fatalError); 65 | //console.log(doc+'') 66 | }catch(e){ 67 | } 68 | }, 69 | 'only function2': function() { 70 | var error = [] 71 | var errorMap = [] 72 | new DOMParser({ 73 | errorHandler:function(msg){error.push(msg)} 74 | }).parseFromString('test', 'text/xml'); 75 | 'warn,warning,error,fatalError'.replace(/\w+/g,function(k){ 76 | var errorHandler = {}; 77 | errorMap[k] = []; 78 | errorHandler[k] = function(msg){errorMap[k] .push(msg)} 79 | new DOMParser({errorHandler:errorHandler}).parseFromString('test', 'text/xml'); 80 | }); 81 | var error2 = []; 82 | for(var n in errorMap){ 83 | error2 = error2.concat(errorMap[n]) 84 | //console.assert(error.length == errorMap[n].length) 85 | } 86 | 87 | console.assert( error2.sort().join(',')==error.sort().join(',')) 88 | }, 89 | 'error function': function() { 90 | var error = [] 91 | var parser = new DOMParser({ 92 | locator:{}, 93 | errorHandler:{ 94 | error:function(msg){ 95 | error.push(msg); 96 | throw new Error(msg) 97 | } 98 | } 99 | }); 100 | var doc = parser.parseFromString('
<;test', 'text/html'); 101 | try{ 102 | var doc = parser.parseFromString('
<;test', 'text/html'); 103 | }catch(e){ 104 | //console.log(e,doc+''); 105 | console.assert(/\n@#\[line\:\d+,col\:\d+\]/.test(error.join(' ')),'line,col must record:'+error) 106 | return; 107 | } 108 | //console.assert(false,doc+' should be null'); 109 | } 110 | }).run(); -------------------------------------------------------------------------------- /test/error/index.js: -------------------------------------------------------------------------------- 1 | require('./error') 2 | require('./xml-error') 3 | -------------------------------------------------------------------------------- /test/error/xml-error.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'); 2 | var DOMParser = require('xmldom').DOMParser; 3 | var assert = require('assert'); 4 | 5 | 6 | vows.describe('errorHandle').addBatch({ 7 | 'empty document': function() { 8 | var errors = []; 9 | var p = new DOMParser({ 10 | errorHandler: function(key,msg){ 11 | //console.log(key,msg) 12 | errors.push(key, msg) 13 | } 14 | }); 15 | var dom = p.parseFromString('', 'text/xml'); 16 | console.assert(errors.length,"empty document error unreported!!") 17 | }, 18 | 'unclosed document': function() { 19 | var errors = []; 20 | var p = new DOMParser({ 21 | errorHandler: function(key,msg){ 22 | errors.push(key, msg) 23 | } 24 | }); 25 | var dom = p.parseFromString('', 'text/xml'); 26 | console.assert(errors.length,"unclosed tag error unreported!!") 27 | }, 28 | 'unclosed xml': function() { 29 | var errors = []; 30 | var p = new DOMParser({ 31 | errorHandler: function(key,msg){ 32 | errors.push(key, msg) 33 | } 34 | }); 35 | var dom = p.parseFromString('', 'text/html'); 36 | //console.log(errors) 37 | console.assert(errors.length==0,"unclosed html tag not need report!!") 38 | }, 39 | "invalid xml node":function(){ 40 | var errors = []; 41 | var p = new DOMParser({ 42 | errorHandler: function(key,msg){ 43 | //console.log(key,msg) 44 | errors.push(key, msg) 45 | } 46 | }); 47 | //console.log('loop'); 48 | var dom = new DOMParser().parseFromString('', 26 | '', 27 | ' ', 28 | ' ', 29 | ' ', 30 | '', 31 | ' ' 32 | ].join('\n') 33 | var parser = new DOMParser({locator:{}}); 34 | var doc = parser.parseFromString(xml, 'text/xml'); 35 | var trans = doc.getElementsByTagName('transition')[0]; 36 | //console.error(doc+'') 37 | assert.equal(trans.lineNumber , 10)//,''+trans+trans.lineNumber+'/'+trans.parentNode.previousSibling.previousSibling.lineNumber) 38 | 39 | }, 40 | 'node positions': function() { 41 | var parser = new DOMParser({locator:{}}); 42 | var doc = parser.parseFromString('\n' + 43 | '\n' + 44 | ' something\nx', 'text/xml'); 45 | var test = doc.documentElement; 46 | var a = test.firstChild.nextSibling; 47 | assertPosition(doc.firstChild, 1, 1,'first child'); 48 | assertPosition(doc.firstChild.nextSibling, 1, 1+''.length,'first child nextSibling'); 49 | assertPosition(test, 2, 1,'document element'+test); 50 | //assertPosition(test.firstChild, 1, 7); 51 | assertPosition(a, 3, 3,'documentElement firstchild nextSibling'+a); 52 | assertPosition(a.firstChild, 3, 19,'a.firstchild'); 53 | assertPosition(a.firstChild.nextSibling, 3, 19+''.length,'a.firstchild.nextsibling'); 54 | assertPosition(test.lastChild, 4, 5,'test.lastChild'); 55 | }, 56 | 'error positions':function(){ 57 | var error = [] 58 | var parser = new DOMParser({ 59 | locator:{systemId:'c:/test/1.xml'}, 60 | errorHandler:function(msg){ 61 | console.error('#######'+msg); 62 | error.push(msg); 63 | } 64 | }); 65 | var xml = '
<;test'; 66 | var doc = parser.parseFromString(xml, 'text/html'); 67 | var attr = doc.documentElement.firstChild.attributes.item(0); 68 | //assert.equal() 69 | assertPosition(attr, 1, 19,'title="1<2 ') 70 | //console.assert(/\n@c\:\/test\/1\.xml#\[line\:\d+,col\:\d+\]/.test(error.join(' ')),'line,col must record:'+error) 71 | }, 72 | 'error positions p':function(){ 73 | var error = [] 74 | var parser = new DOMParser({ 75 | locator:{}, 76 | errorHandler:function(msg){ 77 | error.push('@@'+msg); 78 | } 79 | }); 80 | var doc = parser.parseFromString('\n\t', 'text/html'); 81 | var root = doc.documentElement; 82 | var textNode = root.firstChild; 83 | //console.log('!!!!!'+root+'/'+error) 84 | console.assert(/\n@#\[line\:2,col\:2\]/.test(error.join(' ')),'line,col must record:'+error); 85 | //console.log(textNode.lineNumber+'/'+textNode.columnNumber) 86 | } 87 | }).run(); -------------------------------------------------------------------------------- /test/parse/namespace.js: -------------------------------------------------------------------------------- 1 | var wows = require('vows'); 2 | var DOMParser = require('xmldom').DOMParser; 3 | 4 | // Create a Test Suite 5 | wows.describe('XML Namespace Parse').addBatch({ 6 | 'default namespace': function () { 7 | var dom = new DOMParser().parseFromString('','text/xml'); 8 | var root = dom.documentElement; 9 | console.assert(root.namespaceURI=='http://test.com') 10 | console.assert(root.lookupNamespaceURI('') == 'http://test.com') 11 | console.assert(root.firstChild.namespaceURI=='http://test.com') 12 | console.assert(root.firstChild.lookupNamespaceURI('') == 'http://test.com') 13 | console.assert(root.firstChild.getAttributeNode('attr').namespaceURI==null) 14 | }, 15 | 'prefix namespace': function () { 16 | var dom = new DOMParser().parseFromString('','text/xml'); 17 | var root = dom.documentElement; 18 | console.assert(root.firstChild.namespaceURI == 'http://p1.com') 19 | console.assert(root.lookupNamespaceURI('p1') == 'http://p1.com') 20 | console.assert(root.firstChild.getAttributeNode('attr') == null) 21 | console.assert(root.firstChild.getAttributeNode('p1:attr').namespaceURI == 'http://p1.com') 22 | console.assert(root.firstChild.nextSibling.namespaceURI == 'http://p2.com') 23 | console.assert(root.firstChild.nextSibling.lookupNamespaceURI('p2') == 'http://p2.com') 24 | }, 25 | 'after prefix namespace': function () { 26 | var dom = new DOMParser().parseFromString('','text/xml'); 27 | var root = dom.documentElement; 28 | console.assert(root.firstChild.namespaceURI=='http://p.com') 29 | console.assert(root.lastChild.namespaceURI=='http://test.com') 30 | console.assert(root.firstChild.nextSibling.lookupNamespaceURI('p') == 'http://test.com') 31 | } 32 | }).run(); // Run it -------------------------------------------------------------------------------- /test/parse/node.js: -------------------------------------------------------------------------------- 1 | var wows = require('vows'); 2 | var assert = require('assert'); 3 | var DOMParser = require('xmldom').DOMParser; 4 | var XMLSerializer = require('xmldom').XMLSerializer; 5 | var parser = new DOMParser(); 6 | // Create a Test Suite 7 | wows.describe('XML Node Parse').addBatch({ 8 | 'element': function () { 9 | var dom = new DOMParser().parseFromString(''); 10 | console.assert (dom.childNodes.length== 1,dom.childNodes.length, 1); 11 | console.assert (dom.documentElement.childNodes.length== 1); 12 | console.assert (dom.documentElement.tagName== 'xml'); 13 | console.assert (dom.documentElement.firstChild.tagName== 'child'); 14 | }, 15 | 'text':function(){ 16 | var dom = new DOMParser().parseFromString('start center end'); 17 | var root = dom.documentElement; 18 | console.assert( root.firstChild.data =='start center end'); 19 | console.assert( root.firstChild.nextSibling ==null); 20 | }, 21 | 'cdata': function () { 22 | var dom = new DOMParser().parseFromString('start ]]> end'); 23 | var root = dom.documentElement; 24 | console.assert ( root.firstChild.data =='start '); 25 | console.assert ( root.firstChild.nextSibling.data ==''); 26 | console.assert ( root.firstChild.nextSibling.nextSibling.nextSibling.data =='[[[[[[[[]]]]]]]]'); 27 | }, 28 | 'cdata empty': function () { 29 | var dom = new DOMParser().parseFromString('start end'); 30 | var root = dom.documentElement; 31 | console.assert ( root.textContent =='start end'); 32 | }, 33 | 'comment': function(){ 34 | var dom = new DOMParser().parseFromString(''); 35 | var root = dom.documentElement; 36 | console.assert ( root.firstChild.nodeValue ==' comment&>< '); 37 | }, 38 | 'cdata comment': function(){ 39 | var dom = new DOMParser().parseFromString('start ]]> end'); 40 | var root = dom.documentElement; 41 | console.assert ( root.firstChild.nodeValue =='start '); 42 | console.assert ( root.firstChild.nextSibling.nodeValue ==''); 43 | console.assert ( root.firstChild.nextSibling.nextSibling.nextSibling.nodeValue ==' comment '); 44 | console.assert ( root.firstChild.nextSibling.nextSibling.nextSibling.nextSibling.nodeValue =='end'); 45 | }, 46 | 'append node': function () { 47 | var dom = new DOMParser().parseFromString(''); 48 | var child = dom.createElement("child"); 49 | console.assert ( child == dom.documentElement.appendChild(child)); 50 | console.assert ( child == dom.documentElement.firstChild); 51 | var fragment = new dom.createDocumentFragment(); 52 | console.assert ( child == fragment.appendChild(child)); 53 | }, 54 | 'insert node': function () { 55 | var dom = new DOMParser().parseFromString(''); 56 | var node = dom.createElement("sibling"); 57 | var child = dom.documentElement.firstChild; 58 | child.parentNode.insertBefore(node, child); 59 | console.assert ( node == child.previousSibling); 60 | console.assert ( node.nextSibling == child); 61 | console.assert ( node.parentNode == child.parentNode); 62 | }, 63 | 'insert fragment': function () { 64 | var dom = new DOMParser().parseFromString(''); 65 | var fragment = dom.createDocumentFragment(); 66 | assert(fragment.nodeType === 11); 67 | var first = fragment.appendChild(dom.createElement("first")); 68 | var last = fragment.appendChild(dom.createElement("last")); 69 | console.assert ( fragment.firstChild == first); 70 | console.assert ( fragment.lastChild == last); 71 | console.assert ( last.previousSibling == first); 72 | console.assert ( first.nextSibling == last); 73 | var child = dom.documentElement.firstChild; 74 | child.parentNode.insertBefore(fragment, child); 75 | console.assert ( last.previousSibling == first); 76 | console.assert ( first.nextSibling == last); 77 | console.assert ( child.parentNode.firstChild == first); 78 | console.assert ( last == child.previousSibling); 79 | console.assert ( last.nextSibling == child); 80 | console.assert ( first.parentNode == child.parentNode); 81 | console.assert ( last.parentNode == child.parentNode); 82 | } 83 | }).addBatch({ 84 | "instruction":function(){ 85 | var source = '&'; 86 | var doc = new DOMParser().parseFromString(source,"text/xml"); 87 | var source2 = new XMLSerializer().serializeToString(doc); 88 | console.assert(source == source2,source2); 89 | }, 90 | 'public id && sysid':function(){ 91 | var error = [] 92 | var parser = new DOMParser({ 93 | locator:{}, 94 | errorHandler:function(msg){ 95 | error.push(msg); 96 | } 97 | }); 98 | var doc = parser.parseFromString('', 'text/html'); 99 | console.log(doc+'') 100 | 101 | } 102 | }).run(); // Run it 103 | //var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1; 104 | //var ATTRIBUTE_NODE = NodeType.ATTRIBUTE_NODE = 2; 105 | //var TEXT_NODE = NodeType.TEXT_NODE = 3; 106 | //var CDATA_SECTION_NODE = NodeType.CDATA_SECTION_NODE = 4; 107 | //var ENTITY_REFERENCE_NODE = NodeType.ENTITY_REFERENCE_NODE = 5; 108 | //var ENTITY_NODE = NodeType.ENTITY_NODE = 6; 109 | //var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7; 110 | //var COMMENT_NODE = NodeType.COMMENT_NODE = 8; 111 | //var DOCUMENT_NODE = NodeType.DOCUMENT_NODE = 9; 112 | //var DOCUMENT_TYPE_NODE = NodeType.DOCUMENT_TYPE_NODE = 10; 113 | //var DOCUMENT_FRAGMENT_NODE = NodeType.DOCUMENT_FRAGMENT_NODE = 11; 114 | //var NOTATION_NODE = NodeType.NOTATION_NODE = 12; 115 | -------------------------------------------------------------------------------- /test/parse/parse-element.js: -------------------------------------------------------------------------------- 1 | var wows = require('vows'); 2 | var assert = require('assert'); 3 | var DOMParser = require('xmldom').DOMParser; 4 | var XMLSerializer = require('xmldom').XMLSerializer; 5 | var parser = new DOMParser(); 6 | // Create a Test Suite 7 | wows.describe('XML Node Parse').addBatch({ 8 | 'noAttribute': function () { 9 | var dom = new DOMParser().parseFromString('','text/xml'); 10 | var dom = new DOMParser().parseFromString('','text/xml'); 11 | var dom = new DOMParser().parseFromString('','text/xml'); 12 | var dom = new DOMParser().parseFromString('','text/xml'); 13 | var dom = new DOMParser().parseFromString('','text/xml'); 14 | }, 15 | 'simpleAttribute': function () { 16 | var dom = new DOMParser().parseFromString('','text/xml'); 17 | var dom = new DOMParser().parseFromString('','text/xml'); 18 | var dom = new DOMParser().parseFromString('','text/xml'); 19 | var dom = new DOMParser().parseFromString('','text/xml'); 20 | var dom = new DOMParser().parseFromString('','text/xml'); 21 | var dom = new DOMParser().parseFromString('','text/xml'); 22 | var dom = new DOMParser().parseFromString('','text/xml'); 23 | var dom = new DOMParser().parseFromString('','text/xml'); 24 | }, 25 | 'nsAttribute': function () { 26 | var dom = new DOMParser().parseFromString('','text/xml'); 27 | var dom = new DOMParser().parseFromString('','text/xml'); 28 | var dom = new DOMParser().parseFromString('','text/xml'); 29 | var dom = new DOMParser().parseFromString('','text/xml'); 30 | } 31 | }).run(); -------------------------------------------------------------------------------- /test/parse/simple.js: -------------------------------------------------------------------------------- 1 | var wows = require('vows'); 2 | var DOMParser = require('xmldom').DOMParser; 3 | var assert = require('assert') 4 | 5 | 6 | 7 | 8 | var inc = 0; 9 | var domErrorHandler = function(level, str) { 10 | //console.log("x",level,str); 11 | if(inc++>120)throw new Error() 12 | }; 13 | 14 | 15 | wows.describe('errorHandle').addBatch({ 16 | 'simple': function() { 17 | var parser = new DOMParser(); 18 | var s = ''; 19 | var doc = parser.parseFromString(s, 'text/html'); 20 | console.assert(doc==s.replace('<2','<2'),doc+''); 21 | }, 22 | unclosedFix:function(){ 23 | var parser = new DOMParser(); 24 | var fileContents = ''; 25 | var expected = ' ' 26 | var inc = 0; 27 | var dom = parser.parseFromString(fileContents, "text/xml"); 28 | //console.log(dom+''); 29 | //console.log(expected) 30 | assert.equal(expected,dom+''); 31 | }, 32 | 'test':function(){ 33 | var parser = new DOMParser(); 34 | var fileContents = ' 1$' , dom+'') 39 | //console.log('dom-result:'+dom) 40 | }, 41 | 'svg test':function(){ 42 | var svgCase = [ 43 | '', 44 | ' ...', 45 | ' ', 46 | ' ', 47 | ' ', 48 | '' 49 | ].join('\n') 50 | var parser = new DOMParser({ locator:{}, errorHandler: domErrorHandler }); 51 | var dom = parser.parseFromString(svgCase, "text/xml"); 52 | console.assert(String(dom)==svgCase.replace(/ \/>/g,'/>'),svgCase+'\n!=\n'+dom) 53 | }, 54 | 'line error':function(){ 55 | var xmlLineError=[ 56 | '', 62 | ' ', 63 | ''].join('\r\n'); 64 | 65 | var parser = new DOMParser({ locator:{}, errorHandler: domErrorHandler }); 66 | var dom = parser.parseFromString(xmlLineError, "text/xml"); 67 | //console.log('xmlLineError-result:'+dom) 68 | var node = dom.documentElement.firstChild.nextSibling 69 | //console.log(Object.keys(node),node.lineNumber) 70 | console.assert(node.lineNumber == 7,node.lineNumber ); 71 | } 72 | }).run(); 73 | 74 | 75 | -------------------------------------------------------------------------------- /test/parse/test-define.js: -------------------------------------------------------------------------------- 1 | console.log(__dirname) 2 | exports.XMLSerializer = require('../../dom').XMLSerializer ; 3 | exports.DOMParser = require('../../dom-parser').DOMParser; -------------------------------------------------------------------------------- /test/parse/test-doc-witespace.js: -------------------------------------------------------------------------------- 1 | var wows = require('vows'), 2 | assert = require('assert'); 3 | var DOMParser = require('xmldom').DOMParser; 4 | var XMLSerializer = require('xmldom').XMLSerializer 5 | 6 | 7 | wows.describe('errorHandle').addBatch({ 8 | 'unclosed tag':function(){ 9 | console.log(new DOMParser().parseFromString('' + 8 | '' + 9 | '','text/xml'); 10 | 11 | var doc1 = doc; 12 | var str1=new XMLSerializer().serializeToString(doc); 13 | var doc2 = doc1.cloneNode(true); 14 | var doc3 = doc1.cloneNode(true); 15 | var doc4 = doc1.cloneNode(true); 16 | 17 | doc3.documentElement.appendChild(doc3.documentElement.lastChild); 18 | //doc4.documentElement.appendChild(doc4.documentElement.firstChild); 19 | 20 | var str2=new XMLSerializer().serializeToString(doc2); 21 | var str3=new XMLSerializer().serializeToString(doc3); 22 | var str4=new XMLSerializer().serializeToString(doc4); 23 | console.assert(str1 == str3,str3,str1); 24 | //console.assert(str3 != str4 && str3.length == str4.length,str3); 25 | 26 | 27 | var xmlString = '\n\n'; 28 | var xml = new DOMParser().parseFromString(xmlString, "application/xml"); 29 | var selectedXml = xml.documentElement;//select(xml, "//*[local-name() = 'SignatureValue']")[0]; 30 | xmlString = new XMLSerializer().serializeToString(selectedXml);//.firstChild.nextSibling); 31 | console.log(xmlString); -------------------------------------------------------------------------------- /test/xss-test.js: -------------------------------------------------------------------------------- 1 | var XMLSerializer = require('xmldom').XMLSerializer 2 | var DOMParser = require('xmldom').DOMParser; 3 | var domParser = new DOMParser({xmlns:{'':'http://www.w3.org/1999/xhtml'}}); 4 | 5 | var excludeTags = new RegExp('^(?:'+['javascript', 'vbscript', 'expression', 6 | 'meta', 'xml', 'blink', 'link', 7 | 'script', 'applet','embed', 'object', 8 | 'iframe', 'frame', 'frameset','ilayer', 'layer', 'bgsound', 'base', 9 | ].join('|') 10 | +')$','i'); 11 | var excludeAttrs = /^on|style/i 12 | var urlAttrs = /(?:href|src)/i 13 | var invalidURL = /^(data|javascript|vbscript|ftp)\:/ 14 | 15 | function xss(html){ 16 | var dom = domParser.parseFromString(html,'text/html') 17 | return dom.documentElement.toString(true,function(node){ 18 | switch(node.nodeType){ 19 | case 1://element 20 | var tagName = node.tagName; 21 | if(excludeTags.test(tagName)){ 22 | return ''; 23 | } 24 | return node; 25 | case 2: 26 | var attrName = node.name 27 | if(excludeAttrs.test(attrName)){ 28 | return null; 29 | } 30 | if(urlAttrs.test(attrName)){ 31 | var value = node.value; 32 | if(invalidURL.test(value)){ 33 | return null; 34 | } 35 | } 36 | return node; 37 | case 3: 38 | return node; 39 | } 40 | }) 41 | } 42 | 43 | var html = '
'; 44 | var result = xss(html); 45 | console.log(result) --------------------------------------------------------------------------------