dom-parser.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. function DOMParser(options){
  2. this.options = options ||{locator:{}};
  3. }
  4. DOMParser.prototype.parseFromString = function(source,mimeType){
  5. var options = this.options;
  6. var sax = new XMLReader();
  7. var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
  8. var errorHandler = options.errorHandler;
  9. var locator = options.locator;
  10. var defaultNSMap = options.xmlns||{};
  11. var isHTML = /\/x?html?$/.test(mimeType);//mimeType.toLowerCase().indexOf('html') > -1;
  12. var entityMap = isHTML?htmlEntity.entityMap:{'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
  13. if(locator){
  14. domBuilder.setDocumentLocator(locator)
  15. }
  16. sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
  17. sax.domBuilder = options.domBuilder || domBuilder;
  18. if(isHTML){
  19. defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
  20. }
  21. defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
  22. if(source && typeof source === 'string'){
  23. sax.parse(source,defaultNSMap,entityMap);
  24. }else{
  25. sax.errorHandler.error("invalid doc source");
  26. }
  27. return domBuilder.doc;
  28. }
  29. function buildErrorHandler(errorImpl,domBuilder,locator){
  30. if(!errorImpl){
  31. if(domBuilder instanceof DOMHandler){
  32. return domBuilder;
  33. }
  34. errorImpl = domBuilder ;
  35. }
  36. var errorHandler = {}
  37. var isCallback = errorImpl instanceof Function;
  38. locator = locator||{}
  39. function build(key){
  40. var fn = errorImpl[key];
  41. if(!fn && isCallback){
  42. fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl;
  43. }
  44. errorHandler[key] = fn && function(msg){
  45. fn('[xmldom '+key+']\t'+msg+_locator(locator));
  46. }||function(){};
  47. }
  48. build('warning');
  49. build('error');
  50. build('fatalError');
  51. return errorHandler;
  52. }
  53. //console.log('#\n\n\n\n\n\n\n####')
  54. /**
  55. * +ContentHandler+ErrorHandler
  56. * +LexicalHandler+EntityResolver2
  57. * -DeclHandler-DTDHandler
  58. *
  59. * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
  60. * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
  61. * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
  62. */
  63. function DOMHandler() {
  64. this.cdata = false;
  65. }
  66. function position(locator,node){
  67. node.lineNumber = locator.lineNumber;
  68. node.columnNumber = locator.columnNumber;
  69. }
  70. /**
  71. * @see org.xml.sax.ContentHandler#startDocument
  72. * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
  73. */
  74. DOMHandler.prototype = {
  75. startDocument : function() {
  76. this.doc = new DOMImplementation().createDocument(null, null, null);
  77. if (this.locator) {
  78. this.doc.documentURI = this.locator.systemId;
  79. }
  80. },
  81. startElement:function(namespaceURI, localName, qName, attrs) {
  82. var doc = this.doc;
  83. var el = doc.createElementNS(namespaceURI, qName||localName);
  84. var len = attrs.length;
  85. appendElement(this, el);
  86. this.currentElement = el;
  87. this.locator && position(this.locator,el)
  88. for (var i = 0 ; i < len; i++) {
  89. var namespaceURI = attrs.getURI(i);
  90. var value = attrs.getValue(i);
  91. var qName = attrs.getQName(i);
  92. var attr = doc.createAttributeNS(namespaceURI, qName);
  93. this.locator &&position(attrs.getLocator(i),attr);
  94. attr.value = attr.nodeValue = value;
  95. el.setAttributeNode(attr)
  96. }
  97. },
  98. endElement:function(namespaceURI, localName, qName) {
  99. var current = this.currentElement
  100. var tagName = current.tagName;
  101. this.currentElement = current.parentNode;
  102. },
  103. startPrefixMapping:function(prefix, uri) {
  104. },
  105. endPrefixMapping:function(prefix) {
  106. },
  107. processingInstruction:function(target, data) {
  108. var ins = this.doc.createProcessingInstruction(target, data);
  109. this.locator && position(this.locator,ins)
  110. appendElement(this, ins);
  111. },
  112. ignorableWhitespace:function(ch, start, length) {
  113. },
  114. characters:function(chars, start, length) {
  115. chars = _toString.apply(this,arguments)
  116. //console.log(chars)
  117. if(chars){
  118. if (this.cdata) {
  119. var charNode = this.doc.createCDATASection(chars);
  120. } else {
  121. var charNode = this.doc.createTextNode(chars);
  122. }
  123. if(this.currentElement){
  124. this.currentElement.appendChild(charNode);
  125. }else if(/^\s*$/.test(chars)){
  126. this.doc.appendChild(charNode);
  127. //process xml
  128. }
  129. this.locator && position(this.locator,charNode)
  130. }
  131. },
  132. skippedEntity:function(name) {
  133. },
  134. endDocument:function() {
  135. this.doc.normalize();
  136. },
  137. setDocumentLocator:function (locator) {
  138. if(this.locator = locator){// && !('lineNumber' in locator)){
  139. locator.lineNumber = 0;
  140. }
  141. },
  142. //LexicalHandler
  143. comment:function(chars, start, length) {
  144. chars = _toString.apply(this,arguments)
  145. var comm = this.doc.createComment(chars);
  146. this.locator && position(this.locator,comm)
  147. appendElement(this, comm);
  148. },
  149. startCDATA:function() {
  150. //used in characters() methods
  151. this.cdata = true;
  152. },
  153. endCDATA:function() {
  154. this.cdata = false;
  155. },
  156. startDTD:function(name, publicId, systemId) {
  157. var impl = this.doc.implementation;
  158. if (impl && impl.createDocumentType) {
  159. var dt = impl.createDocumentType(name, publicId, systemId);
  160. this.locator && position(this.locator,dt)
  161. appendElement(this, dt);
  162. }
  163. },
  164. /**
  165. * @see org.xml.sax.ErrorHandler
  166. * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
  167. */
  168. warning:function(error) {
  169. console.warn('[xmldom warning]\t'+error,_locator(this.locator));
  170. },
  171. error:function(error) {
  172. console.error('[xmldom error]\t'+error,_locator(this.locator));
  173. },
  174. fatalError:function(error) {
  175. throw new ParseError(error, this.locator);
  176. }
  177. }
  178. function _locator(l){
  179. if(l){
  180. return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
  181. }
  182. }
  183. function _toString(chars,start,length){
  184. if(typeof chars == 'string'){
  185. return chars.substr(start,length)
  186. }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
  187. if(chars.length >= start+length || start){
  188. return new java.lang.String(chars,start,length)+'';
  189. }
  190. return chars;
  191. }
  192. }
  193. /*
  194. * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
  195. * used method of org.xml.sax.ext.LexicalHandler:
  196. * #comment(chars, start, length)
  197. * #startCDATA()
  198. * #endCDATA()
  199. * #startDTD(name, publicId, systemId)
  200. *
  201. *
  202. * IGNORED method of org.xml.sax.ext.LexicalHandler:
  203. * #endDTD()
  204. * #startEntity(name)
  205. * #endEntity(name)
  206. *
  207. *
  208. * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
  209. * IGNORED method of org.xml.sax.ext.DeclHandler
  210. * #attributeDecl(eName, aName, type, mode, value)
  211. * #elementDecl(name, model)
  212. * #externalEntityDecl(name, publicId, systemId)
  213. * #internalEntityDecl(name, value)
  214. * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
  215. * IGNORED method of org.xml.sax.EntityResolver2
  216. * #resolveEntity(String name,String publicId,String baseURI,String systemId)
  217. * #resolveEntity(publicId, systemId)
  218. * #getExternalSubset(name, baseURI)
  219. * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
  220. * IGNORED method of org.xml.sax.DTDHandler
  221. * #notationDecl(name, publicId, systemId) {};
  222. * #unparsedEntityDecl(name, publicId, systemId, notationName) {};
  223. */
  224. "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
  225. DOMHandler.prototype[key] = function(){return null}
  226. })
  227. /* 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 */
  228. function appendElement (hander,node) {
  229. if (!hander.currentElement) {
  230. hander.doc.appendChild(node);
  231. } else {
  232. hander.currentElement.appendChild(node);
  233. }
  234. }//appendChild and setAttributeNS are preformance key
  235. //if(typeof require == 'function'){
  236. var htmlEntity = require('./entities');
  237. var sax = require('./sax');
  238. var XMLReader = sax.XMLReader;
  239. var ParseError = sax.ParseError;
  240. var DOMImplementation = exports.DOMImplementation = require('./dom').DOMImplementation;
  241. exports.XMLSerializer = require('./dom').XMLSerializer ;
  242. exports.DOMParser = DOMParser;
  243. exports.__DOMHandler = DOMHandler;
  244. //}