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; | |
exports.XMLSerializer = require('./dom').XMLSerializer ; | |
exports.DOMParser = DOMParser; | |
} |