widgetParser.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.xml.widgetParser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.xml.widgetParser"] = true;
  8. /**
  9. Take some sort of xml block
  10. * like <dojo.button caption="blah"/> and turn
  11. * it into a widget..
  12. */
  13. dojo.provide("dojox.xml.widgetParser");
  14. dojo.require("dojox.xml.parser");
  15. dojo.require("dojo.parser");
  16. /**
  17. * We want to support something like:
  18. * <body>
  19. * <script>
  20. * <dijit.layout.SplitContainer>
  21. * <dijit.button/>
  22. * <div>...</div>
  23. * </dijit.layout.SplitContainer>
  24. * </body>
  25. *
  26. * This is very tricky because if we parse this as XML then the <div> tag
  27. * is actually an XML tag, not an XML tag, which is problematic in at least
  28. * IE.
  29. *
  30. * So the strategy is this, silly as it may be: Convert EVERYTHING to HTML
  31. * nodes, including the dijit.layout.SplitContainer by converting it to a
  32. * div with the dojoType. Then run it through the standard parser.
  33. * The more HTML you have relative to XML the less extra overhead this is.
  34. *
  35. * For something that is all XML we could have a different approach,
  36. * perhaps signified by a different type of script tag. In that case we
  37. * could just instantiate all the elements without a sourceNodeRef and then
  38. * add the top level components to the app.
  39. *
  40. * That is very straightforward but I haven't done it.
  41. *
  42. * Right now there is no mechanism to have an intermediary bridge between
  43. * the XML and the widget, because we are relying on dojo.parser
  44. * to do the instantiation. It isn't clear to me why we would want
  45. * those bridges in this approach and not in that approach.
  46. *
  47. */
  48. dojox.xml.widgetParser = new function(){
  49. var d = dojo;
  50. this.parseNode = function(node){
  51. var toBuild = [];
  52. //TODO figure out the proper type
  53. d.query("script[type='text/xml']", node).forEach(function(script){
  54. toBuild.push.apply(toBuild, this._processScript(script));
  55. }, this).orphan();
  56. //instantiate everything at the end, doing it piecewise can give ID conflicts
  57. return d.parser.instantiate(toBuild);
  58. };
  59. this._processScript = function(script){
  60. //the text is either loaded from a separate file by the src
  61. //attribute or underneath the src tag
  62. var text = script.src ? d._getText(script.src) : script.innerHTML || script.firstChild.nodeValue;
  63. var htmlNode = this.toHTML( dojox.xml.parser.parse(text).firstChild );
  64. //make the list BEFORE we copy things over to keep the query scope as
  65. //small as possible
  66. var ret = d.query('[dojoType]', htmlNode);
  67. //remove the script tag and replace with new HTML block
  68. dojo.query(">", htmlNode).place(script, "before")
  69. script.parentNode.removeChild(script);
  70. return ret;
  71. };
  72. /**
  73. * Given an XML node converts it to HTML where the existing HTML
  74. * is preserved and the dojo widget tags are converted to divs
  75. * with dojoType on them.
  76. */
  77. this.toHTML = function (/*XmlNode*/ node){
  78. var newNode;
  79. var nodeName = node.nodeName;
  80. var dd = dojo.doc;
  81. var type = node.nodeType;
  82. ///node type 3 and 4 are text and cdata
  83. if(type >= 3){
  84. return dd.createTextNode( (type == 3 || type == 4) ? node.nodeValue : "" );
  85. }
  86. var localName = node.localName||nodeName.split(":").pop();
  87. //TODO:
  88. // only check for namespace ONCE ever, instead of each time here,
  89. // by mixing in the right check for each browser?
  90. var namespace = node.namespaceURI || (node.getNamespaceUri ? node.getNamespaceUri() : "");
  91. //TODO check for some real namespace
  92. if(namespace == "html"){
  93. newNode = dd.createElement(localName);
  94. }else{
  95. var dojoType = namespace + "." + localName;
  96. /**
  97. * This is a horrible hack we need because creating a <div>
  98. * with <option> children doesn't work well. Specifically with
  99. * dojo.Declaration at some point the <option> tags get lost
  100. * entirely so we need the parent of <option> tags to be <select>
  101. * tags. (Not a problem outside of dojo.Delcaration)
  102. * There are a couple other ways we could do this:
  103. * 1. Look at the first element child to see if it is an option and
  104. * if so create a <select> here.
  105. * 2. When we add a child to parent fix up the parent then if the
  106. * child is an <option> and the parent isn't a <select>.
  107. * Both of those are a bit messy and slower than this.
  108. *
  109. * This is potentially a problem for other tag combinations as well,
  110. * such as <tr> under a <table> or <li> under a <ul>/<ol>.
  111. * (dojox.widget.SortList for example). Probably need a robust strategy for
  112. * dealing with this. Worst case scenario for now is that user has to use
  113. * html tag with dojoType for misbehaving widget.
  114. */
  115. newNode = newNode || dd.createElement((dojoType == "dijit.form.ComboBox") ? "select" : "div");
  116. newNode.setAttribute("dojoType", dojoType);
  117. }
  118. // TODO:
  119. // we should probably set this up different, mixin a function
  120. // depending on if it is IE rather than checking every time here
  121. // the xmlns problem and the style problem are both IE specific
  122. d.forEach(node.attributes, function(attr){
  123. // NOTE: IE always iterates *all* properties!!!
  124. var name = attr.name || attr.nodeName;
  125. var value = attr.value || attr.nodeValue;
  126. if(name.indexOf("xmlns") != 0){
  127. // style=blah blah blah is a problem, in IE if you use
  128. // setAttribute here you get all sorts of problems. Maybe it
  129. // would be better to just create a giant string of HTML
  130. // instead of an object graph, then set innerHTML on something
  131. // to get the object graph? That might be cleaner... that way
  132. // is uses the browser HTML parsing exactly at is and won't
  133. // cause any sort of issues. We could just special case style
  134. // as well?
  135. if(dojo.isIE && name == "style"){
  136. newNode.style.setAttribute("cssText", value);
  137. }else{
  138. newNode.setAttribute(name, value);
  139. }
  140. }
  141. });
  142. d.forEach(node.childNodes, function(cn){
  143. var childNode = this.toHTML(cn);
  144. // script tags in IE don't like appendChild, innerHTML or innerText
  145. // so if we are creating one programatically set text instead
  146. // could special case this for IE only
  147. if(localName == "script"){
  148. newNode.text += childNode.nodeValue;
  149. }else{
  150. newNode.appendChild(childNode);
  151. }
  152. }, this);
  153. return newNode;
  154. };
  155. }();
  156. }