1 (function (callback) {
  2   if (typeof define === 'function' && define.amd) {
  3     define(['core/AbstractWidget', 'core/Parameter'], callback);
  4   }
  5   else {
  6     callback();
  7   }
  8 }(function () {
  9 
 10 /**
 11  * Baseclass for all facet widgets.
 12  *
 13  * @class AbstractFacetWidget
 14  * @augments AjaxSolr.AbstractWidget
 15  */
 16 AjaxSolr.AbstractFacetWidget = AjaxSolr.AbstractWidget.extend(
 17   /** @lends AjaxSolr.AbstractFacetWidget.prototype */
 18   {
 19   /**
 20    * @param {Object} attributes
 21    * @param {String} attributes.field The field to facet on.
 22    * @param {Number} [attributes.start] This widget will by default set the
 23    *   offset parameter to 0 on each request.
 24    * @param {Boolean} [attributes.multivalue] Set to <tt>false</tt> to force a
 25    *   single "fq" parameter for this widget. Defaults to <tt>true</tt>.
 26    */
 27   constructor: function (attributes) {
 28     AjaxSolr.extend(this, {
 29       start: 0,
 30       field: null,
 31       multivalue: true
 32     }, attributes);
 33   },
 34 
 35   init: function () {
 36     this.initStore();
 37   },
 38 
 39   /**
 40    * Add facet parameters to the parameter store.
 41    */
 42   initStore: function () {
 43     /* http://wiki.apache.org/solr/SimpleFacetParameters */
 44     var parameters = [
 45       'facet.prefix',
 46       'facet.sort',
 47       'facet.limit',
 48       'facet.offset',
 49       'facet.mincount',
 50       'facet.missing',
 51       'facet.method',
 52       'facet.enum.cache.minDf'
 53     ];
 54 
 55     this.manager.store.addByValue('facet', true);
 56 
 57     // Set facet.field, facet.date or facet.range to truthy values to add
 58     // related per-field parameters to the parameter store.
 59     if (this['facet.field'] !== undefined) {
 60       this.manager.store.addByValue('facet.field', this.field);
 61     }
 62     else if (this['facet.date'] !== undefined) {
 63       this.manager.store.addByValue('facet.date', this.field);
 64       parameters = parameters.concat([
 65         'facet.date.start',
 66         'facet.date.end',
 67         'facet.date.gap',
 68         'facet.date.hardend',
 69         'facet.date.other',
 70         'facet.date.include'
 71       ]);
 72     }
 73     else if (this['facet.range'] !== undefined) {
 74       this.manager.store.addByValue('facet.range', this.field);
 75       parameters = parameters.concat([
 76         'facet.range.start',
 77         'facet.range.end',
 78         'facet.range.gap',
 79         'facet.range.hardend',
 80         'facet.range.other',
 81         'facet.range.include'
 82       ]);
 83     }
 84 
 85     for (var i = 0, l = parameters.length; i < l; i++) {
 86       if (this[parameters[i]] !== undefined) {
 87         this.manager.store.addByValue('f.' + this.field + '.' + parameters[i], this[parameters[i]]);
 88       }
 89     }
 90   },
 91 
 92   /**
 93    * @returns {Boolean} Whether any filter queries have been set using this
 94    *   widget's facet field.
 95    */
 96   isEmpty: function () {
 97     return !this.manager.store.find('fq', new RegExp('^-?' + this.field + ':'));
 98   },
 99 
100   /**
101    * Sets the filter query.
102    *
103    * @returns {Boolean} Whether the selection changed.
104    */
105   set: function (value) {
106     return this.changeSelection(function () {
107       var a = this.manager.store.removeByValue('fq', new RegExp('^-?' + this.field + ':')),
108           b = this.manager.store.addByValue('fq', this.fq(value));
109       return a || b;
110     });
111   },
112 
113   /**
114    * Adds a filter query.
115    *
116    * @returns {Boolean} Whether a filter query was added.
117    */
118   add: function (value) {
119     return this.changeSelection(function () {
120       return this.manager.store.addByValue('fq', this.fq(value));
121     });
122   },
123 
124   /**
125    * Removes a filter query.
126    *
127    * @returns {Boolean} Whether a filter query was removed.
128    */
129   remove: function (value) {
130     return this.changeSelection(function () {
131       return this.manager.store.removeByValue('fq', this.fq(value));
132     });
133   },
134 
135   /**
136    * Removes all filter queries using the widget's facet field.
137    *
138    * @returns {Boolean} Whether a filter query was removed.
139    */
140   clear: function () {
141     return this.changeSelection(function () {
142       return this.manager.store.removeByValue('fq', new RegExp('^-?' + this.field + ':'));
143     });
144   },
145 
146   /**
147    * Helper for selection functions.
148    *
149    * @param {Function} Selection function to call.
150    * @returns {Boolean} Whether the selection changed.
151    */
152   changeSelection: function (func) {
153     changed = func.apply(this);
154     if (changed) {
155       this.afterChangeSelection();
156     }
157     return changed;
158   },
159 
160   /**
161    * An abstract hook for child implementations.
162    *
163    * <p>This method is executed after the filter queries change.</p>
164    */
165   afterChangeSelection: function () {},
166 
167   /**
168    * One of "facet.field", "facet.date" or "facet.range" must be set on the
169    * widget in order to determine where the facet counts are stored.
170    *
171    * @returns {Array} An array of objects with the properties <tt>facet</tt> and
172    * <tt>count</tt>, e.g <tt>{ facet: 'facet', count: 1 }</tt>.
173    */
174   getFacetCounts: function () {
175     var property;
176     if (this['facet.field'] !== undefined) {
177       property = 'facet_fields';
178     }
179     else if (this['facet.date'] !== undefined) {
180       property = 'facet_dates';
181     }
182     else if (this['facet.range'] !== undefined) {
183       property = 'facet_ranges';
184     }
185     if (property !== undefined) {
186       switch (this.manager.store.get('json.nl').val()) {
187         case 'map':
188           return this.getFacetCountsMap(property);
189         case 'arrarr':
190           return this.getFacetCountsArrarr(property);
191         default:
192           return this.getFacetCountsFlat(property);
193       }
194     }
195     throw 'Cannot get facet counts unless one of the following properties is set to "true" on widget "' + this.id + '": "facet.field", "facet.date", or "facet.range".';
196   },
197 
198   /**
199    * Used if the facet counts are represented as a JSON object.
200    *
201    * @param {String} property "facet_fields", "facet_dates", or "facet_ranges".
202    * @returns {Array} An array of objects with the properties <tt>facet</tt> and
203    * <tt>count</tt>, e.g <tt>{ facet: 'facet', count: 1 }</tt>.
204    */
205   getFacetCountsMap: function (property) {
206     var counts = [];
207     for (var facet in this.manager.response.facet_counts[property][this.field]) {
208       counts.push({
209         facet: facet,
210         count: parseInt(this.manager.response.facet_counts[property][this.field][facet])
211       });
212     }
213     return counts;
214   },
215 
216   /**
217    * Used if the facet counts are represented as an array of two-element arrays.
218    *
219    * @param {String} property "facet_fields", "facet_dates", or "facet_ranges".
220    * @returns {Array} An array of objects with the properties <tt>facet</tt> and
221    * <tt>count</tt>, e.g <tt>{ facet: 'facet', count: 1 }</tt>.
222    */
223   getFacetCountsArrarr: function (property) {
224     var counts = [];
225     for (var i = 0, l = this.manager.response.facet_counts[property][this.field].length; i < l; i++) {
226       counts.push({
227         facet: this.manager.response.facet_counts[property][this.field][i][0],
228         count: parseInt(this.manager.response.facet_counts[property][this.field][i][1])
229       });
230     }
231     return counts;
232   },
233 
234   /**
235    * Used if the facet counts are represented as a flat array.
236    *
237    * @param {String} property "facet_fields", "facet_dates", or "facet_ranges".
238    * @returns {Array} An array of objects with the properties <tt>facet</tt> and
239    * <tt>count</tt>, e.g <tt>{ facet: 'facet', count: 1 }</tt>.
240    */
241   getFacetCountsFlat: function (property) {
242     var counts = [];
243     for (var i = 0, l = this.manager.response.facet_counts[property][this.field].length; i < l; i += 2) {
244       counts.push({
245         facet: this.manager.response.facet_counts[property][this.field][i],
246         count: parseInt(this.manager.response.facet_counts[property][this.field][i+1])
247       });
248     }
249     return counts;
250   },
251 
252   /**
253    * @param {String} value The value.
254    * @returns {Function} Sends a request to Solr if it successfully adds a
255    *   filter query with the given value.
256    */
257   clickHandler: function (value) {
258     var self = this, meth = this.multivalue ? 'add' : 'set';
259     return function () {
260       if (self[meth].call(self, value)) {
261         self.doRequest();
262       }
263       return false;
264     }
265   },
266 
267   /**
268    * @param {String} value The value.
269    * @returns {Function} Sends a request to Solr if it successfully removes a
270    *   filter query with the given value.
271    */
272   unclickHandler: function (value) {
273     var self = this;
274     return function () {
275       if (self.remove(value)) {
276         self.doRequest();
277       }
278       return false;
279     }
280   },
281 
282   /**
283    * @param {String} value The facet value.
284    * @param {Boolean} exclude Whether to exclude this fq parameter value.
285    * @returns {String} An fq parameter value.
286    */
287   fq: function (value, exclude) {
288     return (exclude ? '-' : '') + this.field + ':' + AjaxSolr.Parameter.escapeValue(value);
289   }
290 });
291 
292 }));
293