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

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

Added Dojo 1.9.3 release.

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.