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