1 // $Id$
  2 
  3 /**
  4  * The ParameterStore, as its name suggests, stores Solr parameters. Widgets
  5  * expose some of these parameters to the user. Whenever the user changes the
  6  * values of these parameters, the state of the application changes. In order to
  7  * allow the user to move back and forth between these states with the browser's
  8  * Back and Forward buttons, and to bookmark these states, each state needs to
  9  * be stored. The easiest method is to store the exposed parameters in the URL
 10  * hash (see the <tt>ParameterHashStore</tt> class). However, you may implement
 11  * your own storage method by extending this class.
 12  *
 13  * <p>For a list of possible parameters, please consult the links below.</p>
 14  *
 15  * @see http://wiki.apache.org/solr/CoreQueryParameters
 16  * @see http://wiki.apache.org/solr/CommonQueryParameters
 17  * @see http://wiki.apache.org/solr/SimpleFacetParameters
 18  * @see http://wiki.apache.org/solr/HighlightingParameters
 19  * @see http://wiki.apache.org/solr/MoreLikeThis
 20  * @see http://wiki.apache.org/solr/SpellCheckComponent
 21  * @see http://wiki.apache.org/solr/StatsComponent
 22  * @see http://wiki.apache.org/solr/TermsComponent
 23  * @see http://wiki.apache.org/solr/TermVectorComponent
 24  * @see http://wiki.apache.org/solr/LocalParams
 25  *
 26  * @param properties A map of fields to set. Refer to the list of public fields.
 27  * @class ParameterStore
 28  */
 29 AjaxSolr.ParameterStore = AjaxSolr.Class.extend(
 30   /** @lends AjaxSolr.ParameterStore.prototype */
 31   {
 32   /**
 33    * The names of the exposed parameters. Any parameters that your widgets
 34    * expose to the user, directly or indirectly, should be listed here.
 35    *
 36    * @field
 37    * @public
 38    * @type String[]
 39    * @default []
 40    */
 41   exposed: [],
 42 
 43   /**
 44    * The Solr parameters.
 45    *
 46    * @field
 47    * @private
 48    * @type Object
 49    * @default {}
 50    */
 51   params: {},
 52 
 53   /**
 54    * A reference to the parameter store's manager. For internal use only.
 55    *
 56    * @field
 57    * @private
 58    * @type AjaxSolr.AbstractManager
 59    */
 60   manager: null,
 61 
 62   /**
 63    * An abstract hook for child implementations.
 64    *
 65    * <p>This method should do any necessary one-time initializations.</p>
 66    */
 67   init: function () {},
 68 
 69   /**
 70    * Some Solr parameters may be specified multiple times. It is easiest to
 71    * hard-code a list of such parameters. You may change the list by passing
 72    * <code>{ multiple: /pattern/ }</code> as an argument to the constructor of
 73    * this class or one of its children, e.g.:
 74    *
 75    * <p><code>new ParameterStore({ multiple: /pattern/ })</code>
 76    *
 77    * @param {String} name The name of the parameter.
 78    * @returns {Boolean} Whether the parameter may be specified multiple times.
 79    */
 80   isMultiple: function (name) {
 81     return name.match(/^(?:bf|bq|facet\.date|facet\.date\.other|facet\.field|facet\.query|fq|pf|qf)$/);
 82   },
 83 
 84   /**
 85    * Returns a parameter. If the parameter doesn't exist, creates it.
 86    *
 87    * @param {String} name The name of the parameter.
 88    * @returns {AjaxSolr.Parameter|AjaxSolr.Parameter[]} The parameter.
 89    */
 90   get: function (name) {
 91     if (this.params[name] === undefined) {
 92       var param = new AjaxSolr.Parameter({ name: name });
 93       if (this.isMultiple(name)) {
 94         this.params[name] = [ param ];
 95       }
 96       else {
 97         this.params[name] = param;
 98       }
 99     }
100     return this.params[name];
101   },
102 
103   /**
104    * If the parameter may be specified multiple times, returns the values of
105    * all identically-named parameters. If the parameter may be specified only
106    * once, returns the value of that parameter.
107    *
108    * @param {String} name The name of the parameter.
109    * @returns {String[]|Number[]} The value(s) of the parameter.
110    */
111   values: function (name) {
112     if (this.params[name] !== undefined) {
113       if (this.isMultiple(name)) {
114         var values = [];
115         for (var i = 0, l = this.params[name].length; i < l; i++) {
116           values.push(this.params[name][i].val());
117         }
118         return values;
119       }
120       else {
121         return [ this.params[name].val() ];
122       }
123     }
124     return [];
125   },
126 
127   /**
128    * If the parameter may be specified multiple times, adds the given parameter
129    * to the list of identically-named parameters, unless one already exists with
130    * the same value. If it may be specified only once, replaces the parameter.
131    *
132    * @param {String} name The name of the parameter.
133    * @param {AjaxSolr.Parameter} [param] The parameter.
134    * @returns {AjaxSolr.Parameter|Boolean} The parameter, or false.
135    */
136   add: function (name, param) {
137     if (param === undefined) {
138       param = new AjaxSolr.Parameter({ name: name });
139     }
140     if (this.isMultiple(name)) {
141       if (this.params[name] === undefined) {
142         this.params[name] = [ param ];
143       }
144       else {
145         if (AjaxSolr.inArray(param.val(), this.values(name)) == -1) {
146           this.params[name].push(param);
147         }
148         else {
149           return false;
150         }
151       }
152     }
153     else {
154       this.params[name] = param;
155     }
156     return param;
157   },
158 
159   /**
160    * Deletes a parameter.
161    *
162    * @param {String} name The name of the parameter.
163    * @param {Number} [index] The index of the parameter.
164    */
165   remove: function (name, index) {
166     if (index === undefined) {
167       delete this.params[name];
168     }
169     else {
170       this.params[name].splice(index, 1);
171       if (this.params[name].length == 0) {
172         delete this.params[name];
173       }
174     }
175   },
176 
177   /**
178    * Finds all parameters with matching values.
179    *
180    * @param {String} name The name of the parameter.
181    * @param {String|Number|String[]|Number[]|RegExp} value The value.
182    * @returns {String|Number[]} The indices of the parameters found.
183    */
184   find: function (name, value) {
185     if (this.params[name] !== undefined) {
186       if (this.isMultiple(name)) {
187         var indices = [];
188         for (var i = 0, l = this.params[name].length; i < l; i++) {
189           if (AjaxSolr.equals(this.params[name][i].val(), value)) {
190             indices.push(i);
191           }
192         }
193         return indices.length ? indices : false;
194       }
195       else {
196         if (AjaxSolr.equals(this.params[name].val(), value)) {
197           return name;
198         }
199       }
200     }
201     return false;
202   },
203 
204   /**
205    * If the parameter may be specified multiple times, creates a parameter using
206    * the given name and value, and adds it to the list of identically-named
207    * parameters, unless one already exists with the same value. If it may be
208    * specified only once, replaces the parameter.
209    *
210    * @param {String} name The name of the parameter.
211    * @param {String|Number|String[]|Number[]} value The value.
212    * @returns {AjaxSolr.Parameter|Boolean} The parameter, or false.
213    */
214   addByValue: function (name, value) {
215     if (this.isMultiple(name) && AjaxSolr.isArray(value)) {
216       var ret = [];
217       for (var i = 0, l = value.length; i < l; i++) {
218         ret.push(this.add(name, new AjaxSolr.Parameter({ name: name, value: value[i] })));
219       }
220       return ret;
221     }
222     else {
223       return this.add(name, new AjaxSolr.Parameter({ name: name, value: value }))
224     }
225   },
226 
227   /**
228    * Deletes any parameter with a matching value.
229    *
230    * @param {String} name The name of the parameter.
231    * @param {String|Number|String[]|Number[]|RegExp} value The value.
232    * @returns {String|Number[]} The indices deleted.
233    */
234   removeByValue: function (name, value) {
235     var indices = this.find(name, value);
236     if (indices) {
237       if (AjaxSolr.isArray(indices)) {
238         for (var i = indices.length - 1; i >= 0; i--) {
239           this.remove(name, indices[i]);
240         }
241       }
242       else {
243         this.remove(indices);
244       }
245     }
246     return indices;
247   },
248 
249   /**
250    * Returns the Solr parameters as a query string.
251    *
252    * <p>IE6 calls the default toString() if you write <tt>store.toString()
253    * </tt>. So, we need to choose another name for toString().</p>
254    */
255   string: function () {
256     var params = [];
257     for (var name in this.params) {
258       if (this.isMultiple(name)) {
259         for (var i = 0, l = this.params[name].length; i < l; i++) {
260           params.push(this.params[name][i].string());
261         }
262       }
263       else {
264         params.push(this.params[name].string());
265       }
266     }
267     return AjaxSolr.compact(params).join('&');
268   },
269 
270   /**
271    * Parses a query string into Solr parameters.
272    *
273    * @param {String} str The string to parse.
274    */
275   parseString: function (str) {
276     var pairs = str.split('&');
277     for (var i = 0, l = pairs.length; i < l; i++) {
278       if (pairs[i]) { // ignore leading, trailing, and consecutive &'s
279         var param = new AjaxSolr.Parameter();
280         param.parseString(pairs[i]);
281         this.add(param.name, param);
282       }
283     }
284   },
285 
286   /**
287    * Returns the exposed parameters as a query string.
288    *
289    * @returns {String} A string representation of the exposed parameters.
290    */
291   exposedString: function () {
292     var params = [];
293     for (var i = 0, l = this.exposed.length; i < l; i++) {
294       if (this.params[this.exposed[i]] !== undefined) {
295         if (this.isMultiple(this.exposed[i])) {
296           for (var j = 0, m = this.params[this.exposed[i]].length; j < m; j++) {
297             params.push(this.params[this.exposed[i]][j].string());
298           }
299         }
300         else {
301           params.push(this.params[this.exposed[i]].string());
302         }
303       }
304     }
305     return AjaxSolr.compact(params).join('&');
306   },
307 
308   /**
309    * Resets the values of the exposed parameters.
310    */
311   exposedReset: function () {
312     for (var i = 0, l = this.exposed.length; i < l; i++) {
313       this.remove(this.exposed[i]);
314     }
315   },
316 
317   /**
318    * Loads the values of exposed parameters from persistent storage. It is
319    * necessary, in most cases, to reset the values of exposed parameters before
320    * setting the parameters to the values in storage. This is to ensure that a
321    * parameter whose name is not present in storage is properly reset.
322    *
323    * @param {Boolean} [reset=true] Whether to reset the exposed parameters.
324    *   before loading new values from persistent storage. Default: true.
325    */
326   load: function (reset) {
327     if (reset === undefined) {
328       reset = true;
329     }
330     if (reset) {
331       this.exposedReset();
332     }
333     this.parseString(this.storedString());
334   },
335   
336   /**
337    * An abstract hook for child implementations.
338    *
339    * <p>Stores the values of the exposed parameters in persistent storage. This
340    * method should usually be called before each Solr request.</p>
341    */
342   save: function () {},
343 
344   /**
345    * An abstract hook for child implementations.
346    *
347    * <p>Returns the string to parse from persistent storage.</p>
348    *
349    * @returns {String} The string from persistent storage.
350    */
351   storedString: function () {
352     return '';
353   }
354 });
355