source: Dev/branches/rest-dojo-ui/client/dojox/data/OpenSearchStore.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.0 KB
Line 
1define([
2        "dojo/_base/kernel", // dojo.experimental
3        "dojo/_base/lang", // dojo.extend
4        "dojo/_base/declare", // dojo.declare
5        "dojo/_base/xhr", // dojo.xhrGet
6        "dojo/_base/array", // dojo.forEach
7        "dojo/_base/window", // dojo.doc
8        "dojo/query",
9        "dojo/data/util/simpleFetch",
10        "dojox/xml/parser"], function (kernel, lang, declare, dxhr, array, window, query, simpleFetch, parser) {
11kernel.experimental("dojox.data.OpenSearchStore");
12
13var OpenSearchStore = declare("dojox.data.OpenSearchStore", null, {
14        constructor: function(/*Object*/args){
15                //      summary:
16                //              Initializer for the OpenSearchStore store.
17                //      description:
18                //              The OpenSearchStore is a Datastore interface to any search
19                //              engine that implements the open search specifications.
20                if(args){
21                        this.label = args.label;
22                        this.url = args.url;
23                        this.itemPath = args.itemPath;
24                        if("urlPreventCache" in args){
25                                this.urlPreventCache = args.urlPreventCache?true:false;
26                        }
27                }
28                var def = dxhr.get({
29                        url: this.url,
30                        handleAs: "xml",
31                        sync: true,
32                        preventCache: this.urlPreventCache
33                });
34                def.addCallback(this, "_processOsdd");
35                def.addErrback(function(){
36                        throw new Error("Unable to load OpenSearch Description document from " . args.url);
37                });
38        },
39       
40        // URL to the open search description document
41        url: "",
42        itemPath: "",
43        _storeRef: "_S",
44        urlElement: null,
45        iframeElement: null,
46
47        //urlPreventCache: boolean
48        //Flag denoting if xhrGet calls should use the preventCache option.
49        urlPreventCache: true,
50       
51        ATOM_CONTENT_TYPE: 3,
52        ATOM_CONTENT_TYPE_STRING: "atom",
53        RSS_CONTENT_TYPE: 2,
54        RSS_CONTENT_TYPE_STRING: "rss",
55        XML_CONTENT_TYPE: 1,
56        XML_CONTENT_TYPE_STRING: "xml",
57
58        _assertIsItem: function(/* item */ item){
59                //      summary:
60                //      This function tests whether the item passed in is indeed an item in the store.
61                //      item:
62                //              The item to test for being contained by the store.
63                if(!this.isItem(item)){
64                        throw new Error("dojox.data.OpenSearchStore: a function was passed an item argument that was not an item");
65                }
66        },
67
68        _assertIsAttribute: function(/* attribute-name-string */ attribute){
69                //      summary:
70                //              This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
71                //      attribute:
72                //              The attribute to test for being contained by the store.
73                if(typeof attribute !== "string"){
74                        throw new Error("dojox.data.OpenSearchStore: a function was passed an attribute argument that was not an attribute name string");
75                }
76        },
77
78        getFeatures: function(){
79                //      summary:
80                //      See dojo.data.api.Read.getFeatures()
81                return {
82                        'dojo.data.api.Read': true
83                };
84        },
85
86        getValue: function(item, attribute, defaultValue){
87                //      summary:
88                //      See dojo.data.api.Read.getValue()
89                var values = this.getValues(item, attribute);
90                if(values){
91                        return values[0];
92                }
93                return defaultValue;
94        },
95
96        getAttributes: function(item){
97                //      summary:
98                //      See dojo.data.api.Read.getAttributes()
99                return ["content"];
100        },
101
102        hasAttribute: function(item, attribute){
103                //      summary:
104                //      See dojo.data.api.Read.hasAttributes()
105                if(this.getValue(item,attribute)){
106                        return true;
107                }
108                return false;
109        },
110
111        isItemLoaded: function(item){
112                 //     summary:
113                 //      See dojo.data.api.Read.isItemLoaded()
114                 return this.isItem(item);
115        },
116
117        loadItem: function(keywordArgs){
118                //      summary:
119                //      See dojo.data.api.Read.loadItem()
120        },
121
122        getLabel: function(item){
123                //      summary:
124                //      See dojo.data.api.Read.getLabel()
125                return undefined;
126        },
127       
128        getLabelAttributes: function(item){
129                //      summary:
130                //      See dojo.data.api.Read.getLabelAttributes()
131                return null;
132        },
133
134        containsValue: function(item, attribute, value){
135                //      summary:
136                //      See dojo.data.api.Read.containsValue()
137                var values = this.getValues(item,attribute);
138                for(var i = 0; i < values.length; i++){
139                        if(values[i] === value){
140                                return true;
141                        }
142                }
143                return false;
144        },
145
146        getValues: function(item, attribute){
147                //      summary:
148                //      See dojo.data.api.Read.getValue()
149
150                this._assertIsItem(item);
151                this._assertIsAttribute(attribute);
152                var value = this.processItem(item, attribute);
153                if(value){
154                        return [value];
155                }
156                return undefined;
157        },
158
159        isItem: function(item){
160                //      summary:
161                //      See dojo.data.api.Read.isItem()
162                if(item && item[this._storeRef] === this){
163                        return true;
164                }
165                return false;
166        },
167       
168        close: function(request){
169                //      summary:
170                //      See dojo.data.api.Read.close()
171        },
172       
173        process: function(data){
174                // This should return an array of items.  This would be the function to override if the
175                // developer wanted to customize the processing/parsing of the entire batch of search
176                // results.
177                return this["_processOSD"+this.contentType](data);
178        },
179       
180        processItem: function(item, attribute){
181                // This returns the text that represents the item.  If a developer wanted to customize
182                // how an individual item is rendered/parsed, they'd override this function.
183                return this["_processItem"+this.contentType](item.node, attribute);
184        },
185       
186        _createSearchUrl: function(request){
187                var template = this.urlElement.attributes.getNamedItem("template").nodeValue;
188                var attrs = this.urlElement.attributes;
189                var index = template.indexOf("{searchTerms}");
190                template = template.substring(0, index) + request.query.searchTerms + template.substring(index+13);
191               
192                array.forEach([ {'name': 'count', 'test': request.count, 'def': '10'},
193                                                {'name': 'startIndex', 'test': request.start, 'def': this.urlElement.attributes.getNamedItem("indexOffset")?this.urlElement.attributes.getNamedItem("indexOffset").nodeValue:0},
194                                                {'name': 'startPage', 'test': request.startPage, 'def': this.urlElement.attributes.getNamedItem("pageOffset")?this.urlElement.attributes.getNamedItem("pageOffset").nodeValue:0},
195                                                {'name': 'language', 'test': request.language, 'def': "*"},
196                                                {'name': 'inputEncoding', 'test': request.inputEncoding, 'def': 'UTF-8'},
197                                                {'name': 'outputEncoding', 'test': request.outputEncoding, 'def': 'UTF-8'}
198                                        ], function(item){
199                        template = template.replace('{'+item.name+'}', item.test || item.def);
200                        template = template.replace('{'+item.name+'?}', item.test || item.def);
201                });
202                return template;
203        },
204
205        _fetchItems: function(request, fetchHandler, errorHandler){
206                //      summary:
207                //              Fetch OpenSearch items that match to a query
208                //      request:
209                //              A request object
210                //      fetchHandler:
211                //              A function to call for fetched items
212                //      errorHandler:
213                //              A function to call on error
214
215                if(!request.query){
216                        request.query={};
217                }
218
219                //Build up the content using information from the request
220                var self = this;
221                var url = this._createSearchUrl(request);
222                var getArgs = {
223                        url: url,
224                        preventCache: this.urlPreventCache
225                };
226
227                // Change to fetch the query results.
228                var xhr = dxhr.get(getArgs);
229
230                xhr.addErrback(function(error){
231                        errorHandler(error, request);
232                });
233
234                xhr.addCallback(function(data){
235                        var items = [];
236                        if(data){
237                                //Process the items...
238                                items = self.process(data);
239                                for(var i=0; i < items.length; i++){
240                                        items[i] = {node: items[i]};
241                                        items[i][self._storeRef] = self;
242                                }
243                        }
244                        fetchHandler(items, request);
245                });
246        },
247       
248        _processOSDxml: function(data){
249                var div = window.doc.createElement("div");
250                div.innerHTML = data;
251                return query(this.itemPath, div);
252        },
253       
254        _processItemxml: function(item, attribute){
255                if(attribute === "content"){
256                        return item.innerHTML;
257                }
258                return undefined;
259        },
260       
261        _processOSDatom: function(data){
262                return this._processOSDfeed(data, "entry");
263        },
264
265        _processItematom: function(item, attribute){
266                return this._processItemfeed(item, attribute, "content");
267        },
268
269        _processOSDrss: function(data){
270                return this._processOSDfeed(data, "item");
271        },
272
273        _processItemrss: function(item, attribute){
274                return this._processItemfeed(item, attribute, "description");
275        },
276
277        _processOSDfeed: function(data, type){
278                data = dojox.xml.parser.parse(data);
279                var items = [];
280                var nodeList = data.getElementsByTagName(type);
281                for(var i=0; i<nodeList.length; i++){
282                        items.push(nodeList.item(i));
283                }
284                return items;
285        },
286
287        _processItemfeed: function(item, attribute, type){
288                if(attribute === "content"){
289                        var content = item.getElementsByTagName(type).item(0);
290                        return this._getNodeXml(content, true);
291                }
292                return undefined;
293        },
294       
295        _getNodeXml: function(node, skipFirst){
296                var i;
297                switch(node.nodeType){
298                        case 1:
299                                var xml = [];
300                                if(!skipFirst){
301                                        xml.push("<"+node.tagName);
302                                        var attr;
303                                        for(i=0; i<node.attributes.length; i++){
304                                                attr = node.attributes.item(i);
305                                                xml.push(" "+attr.nodeName+"=\""+attr.nodeValue+"\"");
306                                        }
307                                        xml.push(">");
308                                }
309                                for(i=0; i<node.childNodes.length; i++){
310                                        xml.push(this._getNodeXml(node.childNodes.item(i)));
311                                }
312                                if(!skipFirst){
313                                        xml.push("</"+node.tagName+">\n");
314                                }
315                                return xml.join("");
316                        case 3:
317                        case 4:
318                                return node.nodeValue;
319                }
320                return undefined;
321        },
322
323        _processOsdd: function(doc){
324                var urlnodes = doc.getElementsByTagName("Url");
325                //TODO: Check all the urlnodes and determine what our best one is...
326                var types = [];
327                var contentType;
328                var i;
329                for(i=0; i<urlnodes.length; i++){
330                        contentType = urlnodes[i].attributes.getNamedItem("type").nodeValue;
331                        switch(contentType){
332                                case "application/rss+xml":
333                                        types[i] = this.RSS_CONTENT_TYPE;
334                                        break;
335                                case "application/atom+xml":
336                                        types[i] = this.ATOM_CONTENT_TYPE;
337                                        break;
338                                default:
339                                        types[i] = this.XML_CONTENT_TYPE;
340                                        break;
341                        }
342                }
343                var index = 0;
344                var currentType = types[0];
345                for(i=1; i<urlnodes.length; i++){
346                        if(types[i]>currentType){
347                                index = i;
348                                currentType = types[i];
349                        }
350                }
351
352                // We'll be using urlnodes[index] as it's the best option (ATOM > RSS > XML)
353                var label = urlnodes[index].nodeName.toLowerCase();
354                if(label == 'url'){
355                        var urlattrs = urlnodes[index].attributes;
356                        this.urlElement = urlnodes[index];
357                        switch(types[index]){
358                                case this.ATOM_CONTENT_TYPE:
359                                        this.contentType = this.ATOM_CONTENT_TYPE_STRING;
360                                        break;
361                                case this.RSS_CONTENT_TYPE:
362                                        this.contentType = this.RSS_CONTENT_TYPE_STRING;
363                                        break;
364                                case this.XML_CONTENT_TYPE:
365                                        this.contentType = this.XML_CONTENT_TYPE_STRING;
366                                        break;
367                        }
368                }
369        }
370});
371return lang.extend(OpenSearchStore,simpleFetch);
372});
Note: See TracBrowser for help on using the repository browser.