1 // $Id$
  2 
  3 /**
  4  * Represents a Solr parameter.
  5  *
  6  * @param properties A map of fields to set. Refer to the list of public fields.
  7  * @class Parameter
  8  */
  9 AjaxSolr.Parameter = AjaxSolr.Class.extend(
 10   /** @lends AjaxSolr.Parameter.prototype */
 11   {
 12   /**
 13    * The parameter's name.
 14    *
 15    * @field
 16    * @private
 17    * @type String
 18    */
 19   name: null,
 20 
 21   /**
 22    * The parameter's value.
 23    *
 24    * @field
 25    * @private
 26    * @type String
 27    */
 28   value: null,
 29 
 30   /**
 31    * The parameter's local parameters.
 32    *
 33    * @field
 34    * @private
 35    * @type Object
 36    * @default {}
 37    */
 38   locals: {},
 39 
 40   /**
 41    * Returns the value. If called with an argument, sets the value.
 42    *
 43    * @param {String|Number|String[]|Number[]} [value] The value to set.
 44    * @returns The value.
 45    */
 46   val: function (value) {
 47     if (value === undefined) {
 48       return this.value;
 49     }
 50     else {
 51       this.value = value;
 52     }
 53   },
 54 
 55   /**
 56    * Returns the value of a local parameter. If called with a second argument,
 57    * sets the value of a local parameter.
 58    *
 59    * @param {String} name The name of the local parameter.
 60    * @param {String|Number|String[]|Number[]} [value] The value to set.
 61    * @returns The value.
 62    */
 63   local: function (name, value) {
 64     if (value === undefined) {
 65       return this.locals[name];
 66     }
 67     else {
 68       this.locals[name] = value;
 69     }
 70   },
 71 
 72   /**
 73    * Deletes a local parameter.
 74    *
 75    * @param {String} name The name of the local parameter.
 76    */
 77   remove: function (name) {
 78     delete this.locals[name];
 79   },
 80 
 81   /**
 82    * Returns the Solr parameter as a query string key-value pair.
 83    *
 84    * <p>IE6 calls the default toString() if you write <tt>store.toString()
 85    * </tt>. So, we need to choose another name for toString().</p>
 86    */
 87   string: function () {
 88     var pairs = [];
 89 
 90     for (var name in this.locals) {
 91       if (this.locals[name]) {
 92         pairs.push(name + '=' + encodeURIComponent(this.locals[name]));
 93       }
 94     }
 95 
 96     var prefix = pairs.length ? '{!' + pairs.join('%20') + '}' : '';
 97 
 98     if (this.value) {
 99       return this.name + '=' + prefix + this.valueString(this.value);
100     }
101     // For dismax request handlers, if the q parameter has local params, the
102     // q parameter must be set to a non-empty value. In case the q parameter
103     // has local params but is empty, use the q.alt parameter, which accepts
104     // wildcards.
105     else if (this.name == 'q' && prefix) {
106       return 'q.alt=' + prefix + encodeURIComponent('*:*');
107     }
108     else {
109       return '';
110     }
111   },
112 
113   /**
114    * Parses a string formed by calling string().
115    *
116    * @param {String} str The string to parse.
117    */
118   parseString: function (str) {
119     var param = str.match(/^([^=]+)=(?:\{!([^\}]*)\})?(.*)$/);
120     if (param) {
121       var matches;
122 
123       while (matches = /([^\s=]+)=(\S*)/g.exec(decodeURIComponent(param[2]))) {
124         this.locals[matches[1]] = decodeURIComponent(matches[2]);
125         param[2] = param[2].replace(matches[0], ''); // Safari's exec seems not to do this on its own
126       }
127 
128       if (param[1] == 'q.alt') {
129         this.name = 'q';
130         // if q.alt is present, assume it is because q was empty, as above
131       }
132       else {
133         this.name = param[1];
134         this.value = this.parseValueString(param[3]);
135       }
136     }
137   },
138 
139   /**
140    * Returns the value as a URL-encoded string.
141    *
142    * @private
143    * @param {String|Number|String[]|Number[]} value The value.
144    * @returns {String} The URL-encoded string.
145    */
146   valueString: function (value) {
147     value = AjaxSolr.isArray(value) ? value.join(',') : value;
148     return encodeURIComponent(value);
149   },
150 
151   /**
152    * Parses a URL-encoded string to return the value.
153    *
154    * @private
155    * @param {String} str The URL-encoded string.
156    * @returns {Array} The value.
157    */
158   parseValueString: function (str) {
159     str = decodeURIComponent(str);
160     return str.indexOf(',') == -1 ? str : str.split(',');
161   }
162 });
163 
164 /**
165  * Escapes a value, to be used in, for example, an fq parameter. Surrounds
166  * strings containing spaces or colons in double quotes.
167  *
168  * @public
169  * @param {String|Number} value The value.
170  * @returns {String} The escaped value.
171  */
172 AjaxSolr.Parameter.escapeValue = function (value) {
173   // If the field value has a space or a colon in it, wrap it in quotes,
174   // unless it is a range query or it is already wrapped in quotes.
175   if (value.match(/[ :]/) && !value.match(/[\[\{]\S+ TO \S+[\]\}]/) && !value.match(/^["\(].*["\)]$/)) {
176     return '"' + value + '"';
177   }
178   return value;
179 }
180