source: Dev/branches/rest-dojo-ui/client/dojox/xml/DomParser.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: 10.7 KB
Line 
1define([
2        "dojo/_base/kernel",// dojo.getObject
3        "dojo/_base/array"      // dojo.forEach
4], function(dojo){
5dojo.getObject("xml", true, dojox);
6
7dojox.xml.DomParser=new (function(){
8        /**********************************************************
9         *      The DomParser is a close-to (but not entirely)
10         *      conforming XML parser based on regular
11         *      expressions.  It will take any XML fragment
12         *      and return a lightweight JS structure that is
13         *      similar to (but not exactly) the DOM specification.
14         *
15         *      Getter and setter methods are NOT available; the goal
16         *      was to keep the resulting object model entirely JS-like.
17         *
18         *      All node types but document fragments are supported;
19         *      all nodes support getElementsByTagName and
20         *      getElementsByTagNameNS (with short names byName and
21         *      byNameNS).  The document node supports getElementById
22         *      (byId), and all nodes support a supplimental
23         *      childrenByName/childrenByNameNS method as well.
24         *
25         *      The object model is intended to be a READONLY format;
26         *      mutation events are NOT supported, and though you
27         *      can change properties on a node-by-node basis, certain
28         *      operations are not supported (such as changing the ID
29         *      of an element).
30         **********************************************************/
31
32        //      internal use only.
33        var nodeTypes={ ELEMENT:1, ATTRIBUTE:2, TEXT:3, CDATA_SECTION:4, PROCESSING_INSTRUCTION:7, COMMENT:8, DOCUMENT:9 };
34
35        //      compile the regular expressions once.
36        var reTags=/<([^>\/\s+]*)([^>]*)>([^<]*)/g;
37        var reAttr=/([^=]*)=(("([^"]*)")|('([^']*)'))/g;        //      patch from tdedischew AT gmail, with additional grouping
38        var reEntity=/<!ENTITY\s+([^"]*)\s+"([^"]*)">/g;
39        var reCData=/<!\[CDATA\[([\u0001-\uFFFF]*?)\]\]>/g;
40        var reComments=/<!--([\u0001-\uFFFF]*?)-->/g;
41        var trim=/^\s+|\s+$/g;
42        var normalize=/\s+/g;
43        var egt=/\&gt;/g;
44        var elt=/\&lt;/g;
45        var equot=/\&quot;/g;
46        var eapos=/\&apos;/g;
47        var eamp=/\&amp;/g;
48        var dNs="_def_";
49
50        //      create a root node.
51        function _doc(){
52                return new (function(){
53                        var all={};
54                        this.nodeType=nodeTypes.DOCUMENT;
55                        this.nodeName="#document";
56                        this.namespaces={};
57                        this._nsPaths={};
58                        this.childNodes=[];
59                        this.documentElement=null;
60
61                        //      any element with an ID attribute will be added to the internal hashtable.
62                        this._add=function(obj){
63                                if(typeof(obj.id)!="undefined"){ all[obj.id]=obj; }
64                        };
65                        this._remove=function(id){
66                                if(all[id]){ delete all[id]; }
67                        };
68
69                        this.byId=this.getElementById=function(id){ return all[id]; };
70                        this.byName=this.getElementsByTagName=byName;
71                        this.byNameNS=this.getElementsByTagNameNS=byNameNS;
72                        this.childrenByName=childrenByName;
73                        this.childrenByNameNS=childrenByNameNS;
74                })();
75        }
76
77        //      functions attached to element nodes
78        function byName(name){
79                //      return all descendants with name.  Fully qualified (i.e. svg:svg)
80                function __(node, name, arr){
81                        dojo.forEach(node.childNodes, function(c){
82                                if(c.nodeType==nodeTypes.ELEMENT){
83                                        if(name=="*"){ arr.push(c); }
84                                        else if(c.nodeName==name){ arr.push(c); }
85                                        __(c, name, arr);
86                                }
87                        });
88                }
89                var a=[];
90                __(this, name, a);
91                return a;
92        }
93        function byNameNS(name, ns){
94                //      return all descendants with name by namespace.  If no namespace passed, the default is used.
95                function __(node, name, ns, arr){
96                        dojo.forEach(node.childNodes, function(c){
97                                if(c.nodeType==nodeTypes.ELEMENT){
98                                        if(name=="*"&&c.ownerDocument._nsPaths[ns]==c.namespace){ arr.push(c); }
99                                        else if(c.localName==name&&c.ownerDocument._nsPaths[ns]==c.namespace){ arr.push(c); }
100                                        __(c, name, ns, arr);
101                                }
102                        });
103                }
104                if(!ns){ ns=dNs; }
105                var a=[];
106                __(this, name, ns, a);
107                return a;
108        }
109        //      Only child nodes with name.
110        function childrenByName(name){
111                var a=[];
112                dojo.forEach(this.childNodes, function(c){
113                        if(c.nodeType==nodeTypes.ELEMENT){
114                                if(name=="*"){ a.push(c); }
115                                else if(c.nodeName==name){ a.push(c); }
116                        }
117                });
118                return a;
119        }
120
121        function childrenByNameNS(name, ns){
122                var a=[];
123                dojo.forEach(this.childNodes, function(c){
124                        if(c.nodeType==nodeTypes.ELEMENT){
125                                if(name=="*"&&c.ownerDocument._nsPaths[ns]==c.namespace){ a.push(c); }
126                                else if(c.localName==name&&c.ownerDocument._nsPaths[ns]==c.namespace){ a.push(c); }
127                        }
128                });
129                return a;
130        }
131
132        function _createTextNode(v){
133                return {
134                        nodeType:nodeTypes.TEXT,
135                        nodeName:"#text",
136                        nodeValue:v.replace(normalize," ").replace(egt,">").replace(elt,"<").replace(eapos,"'").replace(equot,'"').replace(eamp,"&")
137                };
138        }
139
140        //      attribute functions
141        function getAttr(name){
142                for(var i=0; i<this.attributes.length; i++){
143                        if(this.attributes[i].nodeName==name){
144                                return this.attributes[i].nodeValue;
145                        }
146                }
147                return null;
148        }
149        function getAttrNS(name, ns){
150                for(var i=0; i<this.attributes.length; i++){
151                        if(this.ownerDocument._nsPaths[ns]==this.attributes[i].namespace
152                                &&this.attributes[i].localName==name
153                        ){
154                                return this.attributes[i].nodeValue;
155                        }
156                }
157                return null;
158        }
159        //      note that you can only swap IDs using setAttribute, NOT with setAttributeNS.
160        function setAttr(name, val){
161                var old=null;
162                for(var i=0; i<this.attributes.length; i++){
163                        if(this.attributes[i].nodeName==name){
164                                old=this.attributes[i].nodeValue;
165                                this.attributes[i].nodeValue=val;
166                                break;
167                        }
168                }
169                if(name=="id"){
170                        if(old!=null){ this.ownerDocument._remove(old); }
171                        this.ownerDocument._add(this);
172                }
173        }
174        function setAttrNS(name, val, ns){
175                for(var i=0; i<this.attributes.length; i++){
176                        if(this.ownerDocument._nsPaths[ns]==this.attributes[i].namespace
177                                &&this.attributes[i].localName==name
178                        ){
179                                this.attributes[i].nodeValue=val;
180                                return;
181                        }
182                }
183        }
184
185        //      navigation
186        function prev(){
187                var p=this.parentNode;
188                if(p){
189                        for(var i=0;i<p.childNodes.length;i++){
190                                if(p.childNodes[i]==this&&i>0){
191                                        return p.childNodes[i-1];
192                                }
193                        }
194                }
195                return null;
196        }
197        function next(){
198                var p=this.parentNode;
199                if(p){
200                        for(var i=0;i<p.childNodes.length;i++){
201                                if(p.childNodes[i]==this&&(i+1)<p.childNodes.length){
202                                        return p.childNodes[i+1];
203                                }
204                        }
205                }
206                return null;
207        }
208
209        //      the main method.
210        this.parse=function(/* String */str){
211                var root=_doc();
212                if(str==null){ return root; }
213                if(str.length==0){ return root; }
214
215                //      preprocess custom entities
216                if(str.indexOf("<!ENTITY")>0){
217                        var entity, eRe=[];
218                        if(reEntity.test(str)){
219                                reEntity.lastIndex=0;
220                                //      match entities
221                                while((entity=reEntity.exec(str))!=null){
222                                        eRe.push({
223                                                entity:"&"+entity[1].replace(trim,"")+";",
224                                                expression:entity[2]
225                                        });
226                                }
227                                //      replace instances in the document.
228                                for(var i=0; i<eRe.length; i++){
229                                        str=str.replace(new RegExp(eRe[i].entity, "g"), eRe[i].expression);
230                                }
231                        }
232                }
233
234                //      pre-parse for CData, and tokenize.
235                var cdSections=[], cdata;
236                while((cdata=reCData.exec(str))!=null){ cdSections.push(cdata[1]); }
237                for(var i=0; i<cdSections.length; i++){ str=str.replace(cdSections[i], i); }
238               
239                //      pre-parse for comments, and tokenize.
240                var comments=[], comment;
241                while((comment=reComments.exec(str))!=null){ comments.push(comment[1]); }
242                for(i=0; i<comments.length; i++){ str=str.replace(comments[i], i); }
243
244                //      parse the document
245                var res, obj=root;
246                while((res=reTags.exec(str))!=null){
247                        //      closing tags.
248                        if(res[2].charAt(0)=="/" && res[2].replace(trim, "").length>1){
249                                if(obj.parentNode){
250                                        obj=obj.parentNode;
251                                }
252                                var text=(res[3]||"").replace(trim, "");
253                                if(text.length>0) {
254                                        obj.childNodes.push(_createTextNode(text));
255                                }
256                        }
257
258                        //      open tags.
259                        else if(res[1].length>0){
260                                //      figure out the type of node.
261                                if(res[1].charAt(0)=="?"){
262                                        //      processing instruction
263                                        var name=res[1].substr(1);
264                                        var target=res[2].substr(0,res[2].length-2);
265                                        obj.childNodes.push({
266                                                nodeType:nodeTypes.PROCESSING_INSTRUCTION,
267                                                nodeName:name,
268                                                nodeValue:target
269                                        });
270                                }
271                                else if(res[1].charAt(0)=="!"){
272                                        //      CDATA; skip over any declaration elements.
273                                        if(res[1].indexOf("![CDATA[")==0){
274                                                var val=parseInt(res[1].replace("![CDATA[","").replace("]]",""));
275                                                obj.childNodes.push({
276                                                        nodeType:nodeTypes.CDATA_SECTION,
277                                                        nodeName:"#cdata-section",
278                                                        nodeValue:cdSections[val]
279                                                });
280                                        }
281                                        //      Comments.
282                                        else if(res[1].substr(0,3)=="!--"){
283                                                var val=parseInt(res[1].replace("!--","").replace("--",""));
284                                                obj.childNodes.push({
285                                                        nodeType:nodeTypes.COMMENT,
286                                                        nodeName:"#comment",
287                                                        nodeValue:comments[val]
288                                                });
289                                        }
290                                }
291                                else {
292                                        //      Elements (with attribute and text)
293                                        var name=res[1].replace(trim,"");
294                                        var o={
295                                                nodeType:nodeTypes.ELEMENT,
296                                                nodeName:name,
297                                                localName:name,
298                                                namespace:dNs,
299                                                ownerDocument:root,
300                                                attributes:[],
301                                                parentNode:null,
302                                                childNodes:[]
303                                        };
304
305                                        //      check to see if it's namespaced.
306                                        if(name.indexOf(":")>-1){
307                                                var t=name.split(":");
308                                                o.namespace=t[0];
309                                                o.localName=t[1];
310                                        }
311
312                                        //      set the function references.
313                                        o.byName=o.getElementsByTagName=byName;
314                                        o.byNameNS=o.getElementsByTagNameNS=byNameNS;
315                                        o.childrenByName=childrenByName;
316                                        o.childrenByNameNS=childrenByNameNS;
317                                        o.getAttribute=getAttr;
318                                        o.getAttributeNS=getAttrNS;
319                                        o.setAttribute=setAttr;
320                                        o.setAttributeNS=setAttrNS;
321                                        o.previous=o.previousSibling=prev;
322                                        o.next=o.nextSibling=next;
323
324                                        //      parse the attribute string.
325                                        var attr;
326                                        while((attr=reAttr.exec(res[2]))!=null){
327                                                if(attr.length>0){
328                                                        var name=attr[1].replace(trim,"");
329                                                        var val=(attr[4]||attr[6]||"").replace(normalize," ")
330                                                                .replace(egt,">")
331                                                                .replace(elt,"<")
332                                                                .replace(eapos,"'")
333                                                                .replace(equot,'"')
334                                                                .replace(eamp,"&");
335                                                        if(name.indexOf("xmlns")==0){
336                                                                if(name.indexOf(":")>0){
337                                                                        var ns=name.split(":");
338                                                                        root.namespaces[ns[1]]=val;
339                                                                        root._nsPaths[val]=ns[1];
340                                                                } else {
341                                                                        root.namespaces[dNs]=val;
342                                                                        root._nsPaths[val]=dNs;
343                                                                }
344                                                        } else {
345                                                                var ln=name;
346                                                                var ns=dNs;
347                                                                if(name.indexOf(":")>0){
348                                                                        var t=name.split(":");
349                                                                        ln=t[1];
350                                                                        ns=t[0];
351                                                                }
352                                                                o.attributes.push({
353                                                                        nodeType:nodeTypes.ATTRIBUTE,
354                                                                        nodeName:name,
355                                                                        localName:ln,
356                                                                        namespace:ns,
357                                                                        nodeValue:val
358                                                                });
359
360                                                                //      only add id as a property.
361                                                                if(ln=="id"){ o.id=val; }
362                                                        }
363                                                }
364                                        }
365                                        root._add(o);
366
367                                        if(obj){
368                                                obj.childNodes.push(o);
369                                                o.parentNode=obj;
370                                                //      if it's not a self-closing node.
371                                                if(res[2].charAt(res[2].length-1)!="/"){
372                                                        obj=o;
373                                                }
374                                        }
375                                        var text=res[3];
376                                        if(text.length>0){
377                                                obj.childNodes.push(_createTextNode(text));
378                                        }
379                                }
380                        }
381                }
382
383                //      set the document element
384                for(var i=0; i<root.childNodes.length; i++){
385                        var e=root.childNodes[i];
386                        if(e.nodeType==nodeTypes.ELEMENT){
387                                root.documentElement=e;
388                                break;
389                        }
390                }
391                return root;
392        };
393})();
394return dojox.xml.DomParser;
395});
Note: See TracBrowser for help on using the repository browser.