function DOMParser(options) { this.options = options || {locator: {}}; } DOMParser.prototype.parseFromString = function (source, mimeType) { var options = this.options; var sax = new XMLReader(); var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler var errorHandler = options.errorHandler; var locator = options.locator; var defaultNSMap = options.xmlns || {}; var entityMap = {'lt': '<', 'gt': '>', 'amp': '&', 'quot': '"', 'apos': "'"} if (locator) { domBuilder.setDocumentLocator(locator) } sax.errorHandler = buildErrorHandler(errorHandler, domBuilder, locator); sax.domBuilder = options.domBuilder || domBuilder; if (/\/x?html?$/.test(mimeType)) { entityMap.nbsp = '\xa0'; entityMap.copy = '\xa9'; defaultNSMap[''] = 'http://www.w3.org/1999/xhtml'; } defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace'; if (source) { sax.parse(source, defaultNSMap, entityMap); } else { sax.errorHandler.error("invalid document source"); } return domBuilder.document; } function buildErrorHandler(errorImpl, domBuilder, locator) { if (!errorImpl) { if (domBuilder instanceof DOMHandler) { return domBuilder; } errorImpl = domBuilder; } var errorHandler = {} var isCallback = errorImpl instanceof Function; locator = locator || {} function build(key) { var fn = errorImpl[key]; if (!fn && isCallback) { fn = errorImpl.length == 2 ? function (msg) { errorImpl(key, msg) } : errorImpl; } errorHandler[key] = fn && function (msg) { fn('[xmldom ' + key + ']\t' + msg + _locator(locator)); } || function () { }; } build('warning'); build('error'); build('fatalError'); return errorHandler; } //console.log('#\n\n\n\n\n\n\n####') /** * +ContentHandler+ErrorHandler * +LexicalHandler+EntityResolver2 * -DeclHandler-DTDHandler * * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2 * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html */ function DOMHandler() { this.cdata = false; } function position(locator, node) { node.lineNumber = locator.lineNumber; node.columnNumber = locator.columnNumber; } /** * @see org.xml.sax.ContentHandler#startDocument * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html */ DOMHandler.prototype = { startDocument: function () { this.document = new DOMImplementation().createDocument(null, null, null); if (this.locator) { this.document.documentURI = this.locator.systemId; } }, startElement: function (namespaceURI, localName, qName, attrs) { var doc = this.document; var el = doc.createElementNS(namespaceURI, qName || localName); var len = attrs.length; appendElement(this, el); this.currentElement = el; this.locator && position(this.locator, el) for (var i = 0; i < len; i++) { var namespaceURI = attrs.getURI(i); var value = attrs.getValue(i); var qName = attrs.getQName(i); var attr = doc.createAttributeNS(namespaceURI, qName); if (attr.getOffset) { position(attr.getOffset(1), attr) } attr.value = attr.nodeValue = value; el.setAttributeNode(attr) } }, endElement: function (namespaceURI, localName, qName) { var current = this.currentElement; var tagName = current.tagName; this.currentElement = current.parentNode; }, startPrefixMapping: function (prefix, uri) { }, endPrefixMapping: function (prefix) { }, processingInstruction: function (target, data) { var ins = this.document.createProcessingInstruction(target, data); this.locator && position(this.locator, ins) appendElement(this, ins); }, ignorableWhitespace: function (ch, start, length) { }, characters: function (chars, start, length) { chars = _toString.apply(this, arguments) //console.log(chars) if (this.currentElement && chars) { if (this.cdata) { var charNode = this.document.createCDATASection(chars); this.currentElement.appendChild(charNode); } else { var charNode = this.document.createTextNode(chars); this.currentElement.appendChild(charNode); } this.locator && position(this.locator, charNode) } }, skippedEntity: function (name) { }, endDocument: function () { this.document.normalize(); }, setDocumentLocator: function (locator) { if (this.locator = locator) {// && !('lineNumber' in locator)){ locator.lineNumber = 0; } }, //LexicalHandler comment: function (chars, start, length) { chars = _toString.apply(this, arguments) var comm = this.document.createComment(chars); this.locator && position(this.locator, comm) appendElement(this, comm); }, startCDATA: function () { //used in characters() methods this.cdata = true; }, endCDATA: function () { this.cdata = false; }, startDTD: function (name, publicId, systemId) { var impl = this.document.implementation; if (impl && impl.createDocumentType) { var dt = impl.createDocumentType(name, publicId, systemId); this.locator && position(this.locator, dt) appendElement(this, dt); } }, /** * @see org.xml.sax.ErrorHandler * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html */ warning: function (error) { //console.warn('[xmldom warning]\t' + error, _locator(this.locator)); }, error: function (error) { //console.error('[xmldom error]\t' + error, _locator(this.locator)); }, fatalError: function (error) { //console.error('[xmldom fatalError]\t' + error, _locator(this.locator)); throw error; } } function _locator(l) { if (l) { return '\n@' + (l.systemId || '') + '#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']' } } function _toString(chars, start, length) { if (typeof chars == 'string') { return chars.substr(start, length) } else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") if (chars.length >= start + length || start) { return new java.lang.String(chars, start, length) + ''; } return chars; } } /* * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html * used method of org.xml.sax.ext.LexicalHandler: * #comment(chars, start, length) * #startCDATA() * #endCDATA() * #startDTD(name, publicId, systemId) * * * IGNORED method of org.xml.sax.ext.LexicalHandler: * #endDTD() * #startEntity(name) * #endEntity(name) * * * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html * IGNORED method of org.xml.sax.ext.DeclHandler * #attributeDecl(eName, aName, type, mode, value) * #elementDecl(name, model) * #externalEntityDecl(name, publicId, systemId) * #internalEntityDecl(name, value) * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html * IGNORED method of org.xml.sax.EntityResolver2 * #resolveEntity(String name,String publicId,String baseURI,String systemId) * #resolveEntity(publicId, systemId) * #getExternalSubset(name, baseURI) * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html * IGNORED method of org.xml.sax.DTDHandler * #notationDecl(name, publicId, systemId) {}; * #unparsedEntityDecl(name, publicId, systemId, notationName) {}; */ "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g, function (key) { DOMHandler.prototype[key] = function () { return null } }) /* 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 */ function appendElement(hander, node) { if (!hander.currentElement) { hander.document.appendChild(node); } else { hander.currentElement.appendChild(node); } }//appendChild and setAttributeNS are preformance key if (typeof require == 'function') { var XMLReader = require('./sax').XMLReader; var DOMImplementation = exports.DOMImplementation = require('./dom').DOMImplementation; var XMLSerializer = exports.XMLSerializer = require('./dom').XMLSerializer; exports.DOMParser = DOMParser; var DOMParser = { DOMImplementation: DOMImplementation, XMLSerializer: XMLSerializer, DOMParser: DOMParser } module.exports = DOMParser }