dom-parser.js 8.1 KB

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