source: Dev/trunk/src/client/dojox/data/OpenSearchStore.js @ 532

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

Added Dojo 1.9.3 release.

File size: 9.9 KB
Line 
1define([
2        "dojo/_base/kernel", // dojo.experimental
3        "dojo/_base/lang", // dojo.extend
4        "dojo/_base/declare", // 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.