dom-parser.js 7.8 KB

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