source: Dev/trunk/src/client/dojox/xml/widgetParser.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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