source: Dev/trunk/src/client/dojox/data/AtomReadStore.js @ 529

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

Added Dojo 1.9.3 release.

File size: 15.6 KB
Line 
1define(["dojo", "dojox", "dojo/data/util/filter", "dojo/data/util/simpleFetch", "dojo/date/stamp"], function(dojo, dojox) {
2dojo.experimental("dojox.data.AtomReadStore");
3
4var AtomReadStore = dojo.declare("dojox.data.AtomReadStore", null, {
5        // summary:
6        //              A read only data store for Atom XML based services or documents
7        // description:
8        //              A data store for Atom XML based services or documents.  This store is still under development
9        //              and doesn't support wildcard filtering yet.     Attribute filtering is limited to category or id.
10
11        constructor: function(/* object */ args){
12                // summary:
13                //              Constructor for the AtomRead store.
14                // args:
15                //              An anonymous object to initialize properties.   It expects the following values:
16                //
17                //              - url:                  The url to a service or an XML document that represents the store
18                //              - unescapeHTML: A boolean to specify whether or not to unescape HTML text
19                //              - sendQuery:    A boolean indicate to add a query string to the service URL
20
21                if(args){
22                        this.url = args.url;
23                        this.rewriteUrl = args.rewriteUrl;
24                        this.label = args.label || this.label;
25                        this.sendQuery = (args.sendQuery || args.sendquery || this.sendQuery);
26                        this.unescapeHTML = args.unescapeHTML;
27                        if("urlPreventCache" in args){
28                                this.urlPreventCache = args.urlPreventCache?true:false;
29                                                }
30                }
31                if(!this.url){
32                        throw new Error("AtomReadStore: a URL must be specified when creating the data store");
33                }
34        },
35
36        //Values that may be set by the parser.
37        //Ergo, have to be instantiated to something
38        //So the parser knows how to set them.
39        url: "",
40
41        label: "title",
42
43        sendQuery: false,
44
45        unescapeHTML: false,
46
47        // urlPreventCache: Boolean
48        //              Configurable preventCache option for the URL.
49        urlPreventCache: false,
50
51        /* dojo/data/api/Read */
52
53        getValue: function(/*dojo/data/api/Item*/ item, /*attribute|attribute-name-string*/ attribute, /*anything?*/ defaultValue){
54                // summary:
55                //              Return an attribute value
56                // description:
57                //              'item' must be an instance of an object created by the AtomReadStore instance.
58                //              Accepted attributes are id, subtitle, title, summary, content, author, updated,
59                //              published, category, link and alternate
60                // item:
61                //              An item returned by a call to the 'fetch' method.
62                // attribute:
63                //              A attribute of the Atom Entry
64                // defaultValue:
65                //              A default value
66                // returns:
67                //              An attribute value found, otherwise 'defaultValue'
68                this._assertIsItem(item);
69                this._assertIsAttribute(attribute);
70                this._initItem(item);
71                attribute = attribute.toLowerCase();
72                //If the attribute has previously been retrieved, then return it
73                if(!item._attribs[attribute] && !item._parsed){
74                        this._parseItem(item);
75                        item._parsed = true;
76                }
77                var retVal = item._attribs[attribute];
78
79                if(!retVal && attribute == "summary"){
80                        var content = this.getValue(item, "content");
81                        var regexp = new RegExp("/(<([^>]+)>)/g", "i");
82                        var text = content.text.replace(regexp,"");
83                        retVal = {
84                                text: text.substring(0, Math.min(400, text.length)),
85                                type: "text"
86                        };
87                        item._attribs[attribute] = retVal;
88                }
89
90                if(retVal && this.unescapeHTML){
91                        if((attribute == "content" || attribute == "summary" || attribute == "subtitle") && !item["_"+attribute+"Escaped"]){
92                                retVal.text = this._unescapeHTML(retVal.text);
93                                item["_"+attribute+"Escaped"] = true;
94                        }
95                }
96                return retVal ? dojo.isArray(retVal) ? retVal[0]: retVal : defaultValue;
97        },
98
99        getValues: function(/*dojo/data/api/Item*/ item, /*attribute|attribute-name-string*/ attribute){
100                // summary:
101                //              Return an attribute value
102                // description:
103                //              'item' must be an instance of an object created by the AtomReadStore instance.
104                //              Accepted attributes are id, subtitle, title, summary, content, author, updated,
105                //              published, category, link and alternate
106                // item:
107                //              An item returned by a call to the 'fetch' method.
108                // attribute:
109                //              A attribute of the Atom Entry
110                // returns:
111                //              An array of values for the attribute value found, otherwise 'defaultValue'
112                this._assertIsItem(item);
113                this._assertIsAttribute(attribute);
114                this._initItem(item);
115                attribute = attribute.toLowerCase();
116                //If the attribute has previously been retrieved, then return it
117                if(!item._attribs[attribute]){
118                        this._parseItem(item);
119                }
120                var retVal = item._attribs[attribute];
121                return retVal ? ((retVal.length !== undefined && typeof(retVal) !== "string") ? retVal : [retVal]) : undefined;
122        },
123
124        getAttributes: function(/*dojo/data/api/Item*/ item){
125                // summary:
126                //              Return an array of attribute names
127                // description:
128                //              'item' must be have been created by the AtomReadStore instance.
129                //              tag names of child elements and XML attribute names of attributes
130                //              specified to the element are returned along with special attribute
131                //              names applicable to the element including "tagName", "childNodes"
132                //              if the element has child elements, "text()" if the element has
133                //              child text nodes, and attribute names in '_attributeMap' that match
134                //              the tag name of the element.
135                // item:
136                //              An XML element
137                // returns:
138                //              An array of attributes found
139                this._assertIsItem(item);
140                if(!item._attribs){
141                        this._initItem(item);
142                        this._parseItem(item);
143                }
144                var attrNames = [];
145                for(var x in item._attribs){
146                        attrNames.push(x);
147                }
148                return attrNames; //array
149        },
150
151        hasAttribute: function(/*dojo/data/api/Item*/ item, /*attribute|attribute-name-string*/ attribute){
152                // summary:
153                //              Check whether an element has the attribute
154                // item:
155                //              'item' must be created by the AtomReadStore instance.
156                // attribute:
157                //              An attribute of an Atom Entry item.
158                // returns:
159                //              True if the element has the attribute, otherwise false
160                return (this.getValue(item, attribute) !== undefined); //boolean
161        },
162
163        containsValue: function(/*dojo/data/api/Item*/ item, /*attribute|attribute-name-string*/ attribute, /* anything */ value){
164                // summary:
165                //              Check whether the attribute values contain the value
166                // item:
167                //              'item' must be an instance of a dojox.data.XmlItem from the store instance.
168                // attribute:
169                //              A tag name of a child element, An XML attribute name or one of
170                //              special names
171                // returns:
172                //              True if the attribute values contain the value, otherwise false
173                var values = this.getValues(item, attribute);
174                for(var i = 0; i < values.length; i++){
175                        if((typeof value === "string")){
176                                if(values[i].toString && values[i].toString() === value){
177                                        return true;
178                                }
179                        }else if(values[i] === value){
180                                return true; //boolean
181                        }
182                }
183                return false;//boolean
184        },
185
186        isItem: function(/* anything */ something){
187                // summary:
188                //              Check whether the object is an item (XML element)
189                // item:
190                //              An object to check
191                // returns:
192                //              True if the object is an XML element, otherwise false
193                if(something && something.element && something.store && something.store === this){
194                        return true; //boolean
195                }
196                return false; //boolran
197        },
198
199        isItemLoaded: function(/* anything */ something){
200                // summary:
201                //              Check whether the object is an item (XML element) and loaded
202                // item:
203                //              An object to check
204                // returns:
205                //              True if the object is an XML element, otherwise false
206                return this.isItem(something); //boolean
207        },
208
209        loadItem: function(/* object */ keywordArgs){
210                // summary:
211                //              Load an item (XML element)
212                // keywordArgs:
213                //              object containing the args for loadItem.        See dojo/data/api/Read.loadItem()
214        },
215
216        getFeatures: function(){
217                // summary:
218                //              Return supported data APIs
219                // returns:
220                //              "dojo.data.api.Read" and "dojo.data.api.Write"
221                var features = {
222                        "dojo.data.api.Read": true
223                };
224                return features; //array
225        },
226
227        getLabel: function(/*dojo/data/api/Item*/ item){
228                // summary:
229                //              See dojo/data/api/Read.getLabel()
230                if((this.label !== "") && this.isItem(item)){
231                        var label = this.getValue(item,this.label);
232                        if(label && label.text){
233                                return label.text;
234                        }else if(label){
235                                return label.toString();
236                        }else{
237                                return undefined;
238                        }
239                }
240                return undefined; //undefined
241        },
242
243        getLabelAttributes: function(/*dojo/data/api/Item*/ item){
244                // summary:
245                //              See dojo/data/api/Read.getLabelAttributes()
246                if(this.label !== ""){
247                        return [this.label]; //array
248                }
249                return null; //null
250        },
251
252        getFeedValue: function(attribute, defaultValue){
253                // summary:
254                //              Non-API method for retrieving values regarding the Atom feed,
255                //              rather than the Atom entries.
256                var values = this.getFeedValues(attribute, defaultValue);
257                if(dojo.isArray(values)){
258                        return values[0];
259                }
260                return values;
261        },
262
263        getFeedValues: function(attribute, defaultValue){
264                // summary:
265                //              Non-API method for retrieving values regarding the Atom feed,
266                //              rather than the Atom entries.
267                if(!this.doc){
268                        return defaultValue;
269                }
270                if(!this._feedMetaData){
271                        this._feedMetaData = {
272                                element: this.doc.getElementsByTagName("feed")[0],
273                                store: this,
274                                _attribs: {}
275                        };
276                        this._parseItem(this._feedMetaData);
277                }
278                return this._feedMetaData._attribs[attribute] || defaultValue;
279        },
280
281        _initItem: function(item){
282                // summary:
283                //              Initializes an item before it can be parsed.
284                if(!item._attribs){
285                        item._attribs = {};
286                }
287        },
288
289        _fetchItems: function(request, fetchHandler, errorHandler){
290                // summary:
291                //              Retrieves the items from the Atom XML document.
292                var url = this._getFetchUrl(request);
293                if(!url){
294                        errorHandler(new Error("No URL specified."));
295                        return;
296                }
297                var localRequest = (!this.sendQuery ? request : null); // use request for _getItems()
298
299                var _this = this;
300                var docHandler = function(data){
301                        _this.doc = data;
302                        var items = _this._getItems(data, localRequest);
303                        var query = request.query;
304                        if(query){
305                                if(query.id){
306                                        items = dojo.filter(items, function(item){
307                                                return (_this.getValue(item, "id") == query.id);
308                                        });
309                                }else if(query.category){
310                                        items = dojo.filter(items, function(entry){
311                                                var cats = _this.getValues(entry, "category");
312                                                if(!cats){
313                                                        return false;
314                                                }
315                                                return dojo.some(cats, "return item.term=='"+query.category+"'");
316                                        });
317                                }
318                        }
319
320                        if(items && items.length > 0){
321                                fetchHandler(items, request);
322                        }else{
323                                fetchHandler([], request);
324                        }
325                };
326
327                if(this.doc){
328                        docHandler(this.doc);
329                }else{
330                        var getArgs = {
331                                url: url,
332                                handleAs: "xml",
333                                preventCache: this.urlPreventCache
334                        };
335                        var getHandler = dojo.xhrGet(getArgs);
336                        getHandler.addCallback(docHandler);
337
338                        getHandler.addErrback(function(data){
339                                errorHandler(data, request);
340                        });
341                }
342        },
343
344        _getFetchUrl: function(request){
345                if(!this.sendQuery){
346                        return this.url;
347                }
348                var query = request.query;
349                if(!query){
350                        return this.url;
351                }
352                if(dojo.isString(query)){
353                        return this.url + query;
354                }
355                var queryString = "";
356                for(var name in query){
357                        var value = query[name];
358                        if(value){
359                                if(queryString){
360                                        queryString += "&";
361                                }
362                                queryString += (name + "=" + value);
363                        }
364                }
365                if(!queryString){
366                        return this.url;
367                }
368                //Check to see if the URL already has query params or not.
369                var fullUrl = this.url;
370                if(fullUrl.indexOf("?") < 0){
371                        fullUrl += "?";
372                }else{
373                        fullUrl += "&";
374                }
375                return fullUrl + queryString;
376        },
377
378        _getItems: function(document, request){
379                // summary:
380                //              Parses the document in a first pass
381                if(this._items){
382                        return this._items;
383                }
384                var items = [];
385                var nodes = [];
386
387                if(document.childNodes.length < 1){
388                        this._items = items;
389                        console.log("dojox.data.AtomReadStore: Received an invalid Atom document. Check the content type header");
390                        return items;
391                }
392
393                var feedNodes = dojo.filter(document.childNodes, "return item.tagName && item.tagName.toLowerCase() == 'feed'");
394
395                var query = request.query;
396
397                if(!feedNodes || feedNodes.length != 1){
398                        console.log("dojox.data.AtomReadStore: Received an invalid Atom document, number of feed tags = " + (feedNodes? feedNodes.length : 0));
399                        return items;
400                }
401
402                nodes = dojo.filter(feedNodes[0].childNodes, "return item.tagName && item.tagName.toLowerCase() == 'entry'");
403
404                if(request.onBegin){
405                        request.onBegin(nodes.length, this.sendQuery ? request : {});
406                }
407
408                for(var i = 0; i < nodes.length; i++){
409                        var node = nodes[i];
410                        if(node.nodeType != 1 /*ELEMENT_NODE*/){
411                                continue;
412                        }
413                        items.push(this._getItem(node));
414                }
415                this._items = items;
416                return items;
417        },
418
419        close: function(/*dojo/data/api/Request|Object?*/ request){
420                // summary:
421                //              See dojo/data/api/Read.close()
422        },
423
424/* internal API */
425
426        _getItem: function(element){
427                return {
428                        element: element,
429                        store: this
430                };
431        },
432
433        _parseItem: function(item){
434                var attribs = item._attribs;
435                var _this = this;
436                var text, type;
437
438                function getNodeText(node){
439                        var txt = node.textContent || node.innerHTML || node.innerXML;
440                        if(!txt && node.childNodes[0]){
441                                var child = node.childNodes[0];
442                                if(child && (child.nodeType == 3 || child.nodeType == 4)){
443                                        txt = node.childNodes[0].nodeValue;
444                                }
445                        }
446                        return txt;
447                }
448                function parseTextAndType(node){
449                        return {text: getNodeText(node),type: node.getAttribute("type")};
450                }
451                dojo.forEach(item.element.childNodes, function(node){
452                        var tagName = node.tagName ? node.tagName.toLowerCase() : "";
453                        switch(tagName){
454                                case "title":
455                                        attribs[tagName] = {
456                                                text: getNodeText(node),
457                                                type: node.getAttribute("type")
458                                        }; break;
459                                case "subtitle":
460                                case "summary":
461                                case "content":
462                                        attribs[tagName] = parseTextAndType(node);
463                                        break;
464                                case "author":
465                                        var nameNode ,uriNode;
466                                        dojo.forEach(node.childNodes, function(child){
467                                                if(!child.tagName){
468                                                        return;
469                                                }
470                                                switch(child.tagName.toLowerCase()){
471                                                        case "name":
472                                                                nameNode = child;
473                                                                break;
474                                                        case "uri":
475                                                                uriNode = child;
476                                                                break;
477                                                }
478                                        });
479                                        var author = {};
480                                        if(nameNode && nameNode.length == 1){
481                                                author.name = getNodeText(nameNode[0]);
482                                        }
483                                        if(uriNode && uriNode.length == 1){
484                                                author.uri = getNodeText(uriNode[0]);
485                                        }
486                                        attribs[tagName] = author;
487                                        break;
488                                case "id":
489                                        attribs[tagName] = getNodeText(node);
490                                        break;
491                                case "updated":
492                                        attribs[tagName] = dojo.date.stamp.fromISOString(getNodeText(node) );
493                                        break;
494                                case "published":
495                                        attribs[tagName] = dojo.date.stamp.fromISOString(getNodeText(node));
496                                        break;
497                                case "category":
498                                        if(!attribs[tagName]){
499                                                attribs[tagName] = [];
500                                        }
501                                        attribs[tagName].push({scheme:node.getAttribute("scheme"), term: node.getAttribute("term")});
502                                        break;
503                                case "link":
504                                        if(!attribs[tagName]){
505                                                attribs[tagName] = [];
506                                        }
507                                        var link = {
508                                                rel: node.getAttribute("rel"),
509                                                href: node.getAttribute("href"),
510                                                type: node.getAttribute("type")};
511                                        attribs[tagName].push(link);
512
513                                        if(link.rel == "alternate"){
514                                                attribs["alternate"] = link;
515                                        }
516                                        break;
517                                default:
518                                        break;
519                        }
520                });
521        },
522
523        _unescapeHTML : function(text){
524                //Replace HTML character codes with their unencoded equivalents, e.g. &#8217; with '
525                text = text.replace(/&#8217;/m , "'").replace(/&#8243;/m , "\"").replace(/&#60;/m,">").replace(/&#62;/m,"<").replace(/&#38;/m,"&");
526                return text;
527        },
528
529        _assertIsItem: function(/*dojo/data/api/Item*/ item){
530                // summary:
531                //              This function tests whether the item passed in is indeed an item in the store.
532                // item:
533                //              The item to test for being contained by the store.
534                if(!this.isItem(item)){
535                        throw new Error("dojox.data.AtomReadStore: Invalid item argument.");
536                }
537        },
538
539        _assertIsAttribute: function(/* attribute-name-string */ attribute){
540                // summary:
541                //              This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
542                // attribute:
543                //              The attribute to test for being contained by the store.
544                if(typeof attribute !== "string"){
545                        throw new Error("dojox.data.AtomReadStore: Invalid attribute argument.");
546                }
547        }
548});
549
550dojo.extend(AtomReadStore, dojo.data.util.simpleFetch);
551
552return AtomReadStore;
553});
Note: See TracBrowser for help on using the repository browser.