dom-parser.js 8.2 KB

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