source: Dev/branches/rest-dojo-ui/client/dojox/xml/widgetParser.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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.