8c29ba8213ea3cfd98a5a5b4fd9f849e7cb3ce35
[motion.git] / public / assets / plugins / angular-elastic-builder / dist / angular-elastic-builder_old.js
1 /**
2  * # angular-elastic-builder
3  * ## Angular Module for building an Elasticsearch Query
4  *
5  * @version v1.4.0
6  * @link https://github.com/dncrews/angular-elastic-builder.git
7  * @license MIT
8  * @author Dan Crews <crewsd@gmail.com>
9  */
10
11 /**
12  * angular-elastic-builder
13  *
14  * /src/module.js
15  *
16  * Angular Module for building an Elasticsearch query
17  */
18
19 (function(angular) {
20   'use strict';
21
22   angular.module('angular-elastic-builder', [
23     'RecursionHelper',
24   ]);
25
26 })(window.angular);
27
28 /**
29  * angular-elastic-builder
30  *
31  * /src/directives/BuilderDirective.js
32  *
33  * Angular Directive for injecting a query builder form.
34  */
35
36 (function(angular) {
37   'use strict';
38
39   angular.module('angular-elastic-builder')
40     .directive('elasticBuilder', [
41       'elasticQueryService',
42
43       function EB(elasticQueryService) {
44
45         return {
46           scope: {
47             data: '=elasticBuilder',
48           },
49
50           templateUrl: 'angular-elastic-builder/BuilderDirective.html',
51
52           link: function(scope) {
53             var data = scope.data;
54
55             scope.filters = [];
56
57             /**
58              * Removes either Group or Rule
59              */
60             scope.removeChild = function(idx) {
61               scope.filters.splice(idx, 1);
62             };
63
64             /**
65              * Adds a Single Rule
66              */
67             scope.addRule = function() {
68               scope.filters.push({});
69             };
70
71             /**
72              * Adds a Group of Rules
73              */
74             scope.addGroup = function() {
75               scope.filters.push({
76                 type: 'group',
77                 subType: 'and',
78                 rules: [],
79               });
80             };
81
82             /**
83              * Any time "outside forces" change the query, they should tell us so via
84              * `data.needsUpdate`
85              */
86             scope.$watch('data.needsUpdate', function(curr) {
87               if (!curr) {
88                 return;
89               }
90
91               scope.filters = elasticQueryService.toFilters(data.query, scope.data.fields);
92               scope.data.needsUpdate = false;
93             });
94
95             /**
96              * Changes on the page update the Query
97              */
98             scope.$watch('filters', function(curr) {
99               if (!curr) {
100                 return;
101               }
102
103               data.query = elasticQueryService.toQuery(scope.filters, scope.data.fields);
104             }, true);
105           }
106         };
107       }
108
109     ]);
110
111 })(window.angular);
112
113 /**
114  * angular-elastic-builder
115  *
116  * /src/directives/Chooser.js
117  *
118  * This file is to help recursively, to decide whether to show a group or rule
119  */
120
121 (function(angular) {
122   'use strict';
123
124   var app = angular.module('angular-elastic-builder');
125
126   app.directive('elasticBuilderChooser', [
127     'RecursionHelper',
128     'groupClassHelper',
129
130     function elasticBuilderChooser(RH, groupClassHelper) {
131
132       return {
133         scope: {
134           elasticFields: '=',
135           item: '=elasticBuilderChooser',
136           onRemove: '&',
137         },
138
139         templateUrl: 'angular-elastic-builder/ChooserDirective.html',
140
141         compile: function (element) {
142           return RH.compile(element, function(scope, el, attrs) {
143             var depth = scope.depth = (+ attrs.depth)
144               , item = scope.item;
145
146             scope.getGroupClassName = function() {
147               var level = depth;
148               if (item.type === 'group') level++;
149
150               return groupClassHelper(level);
151             };
152           });
153         }
154       };
155     }
156
157   ]);
158
159 })(window.angular);
160
161 /**
162  * angular-elastic-builder
163  *
164  * /src/directives/Group.js
165  */
166
167 (function(angular) {
168   'use strict';
169
170   var app = angular.module('angular-elastic-builder');
171
172   app.directive('elasticBuilderGroup', [
173     'RecursionHelper',
174     'groupClassHelper',
175
176     function elasticBuilderGroup(RH, groupClassHelper) {
177
178       return {
179         scope: {
180           elasticFields: '=',
181           group: '=elasticBuilderGroup',
182           onRemove: '&',
183         },
184
185         templateUrl: 'angular-elastic-builder/GroupDirective.html',
186
187         compile: function(element) {
188           return RH.compile(element, function(scope, el, attrs) {
189             var depth = scope.depth = (+ attrs.depth);
190             var group = scope.group;
191
192             scope.addRule = function() {
193               group.rules.push({});
194             };
195             scope.addGroup = function() {
196               group.rules.push({
197                 type: 'group',
198                 subType: 'and',
199                 rules: [],
200               });
201             };
202
203             scope.removeChild = function(idx) {
204               group.rules.splice(idx, 1);
205             };
206
207             scope.getGroupClassName = function() {
208               return groupClassHelper(depth + 1);
209             };
210           });
211         }
212       };
213     }
214
215   ]);
216
217 })(window.angular);
218
219 /**
220  * angular-elastic-builder
221  *
222  * /src/directives/Rule.js
223  */
224
225 (function(angular) {
226   'use strict';
227
228   var app = angular.module('angular-elastic-builder');
229
230   app.directive('elasticBuilderRule', [
231
232     function elasticBuilderRule() {
233       return {
234         scope: {
235           elasticFields: '=',
236           rule: '=elasticBuilderRule',
237           onRemove: '&',
238         },
239
240         templateUrl: 'angular-elastic-builder/RuleDirective.html',
241
242         link: function(scope) {
243           scope.getType = function() {
244             var fields = scope.elasticFields;
245             var field = scope.rule.field;
246
247             if (!fields || !field) {
248               return;
249             }
250
251             if (fields[field].subType === 'boolean') {
252               return 'boolean';
253             }
254
255             return fields[field].type;
256           };
257         }
258       };
259     }
260
261   ]);
262
263 })(window.angular);
264
265 /**
266  * angular-elastic-builder
267  *
268  * /src/directives/RuleTypes.js
269  *
270  * Determines which Rule type should be displayed
271  */
272
273 (function(angular) {
274   'use strict';
275
276   var app = angular.module('angular-elastic-builder');
277
278   app.directive('elasticType', [
279
280     function() {
281       return {
282         scope: {
283           type: '=elasticType',
284           rule: '=',
285           guide: '=',
286         },
287
288         template: '<ng-include src="getTemplateUrl()" />',
289
290         link: function(scope) {
291           scope.getTemplateUrl = function() {
292             var type = scope.type;
293             if (! type) return;
294
295             type = type.charAt(0).toUpperCase() + type.slice(1);
296
297             return 'angular-elastic-builder/types/' + type + '.html';
298           };
299
300           // This is a weird hack to make sure these are numbers
301           scope.booleans = [ 'False', 'True' ];
302           scope.booleansOrder = [ 'True', 'False' ];
303
304           scope.inputNeeded = function() {
305             var needs = [
306               'equals',
307               'notEquals',
308
309               'gt',
310               'gte',
311               'lt',
312               'lte',
313             ];
314
315             return ~needs.indexOf(scope.rule.subType);
316           };
317         },
318       };
319     }
320
321   ]);
322
323 })(window.angular);
324
325 (function(angular) {"use strict"; angular.module("angular-elastic-builder").run(["$templateCache", function($templateCache) {$templateCache.put("angular-elastic-builder/BuilderDirective.html","<!-- <div class=\"elastic-builder\">\r\n  <div class=\"filter-panels\">\r\n    <div class=\"list-group form-inline\">\r\n      <div\r\n        data-ng-repeat=\"filter in filters\"\r\n        data-elastic-builder-chooser=\"filter\"\r\n        data-elastic-fields=\"data.fields\"\r\n        data-on-remove=\"removeChild($index)\"\r\n        data-depth=\"0\"></div>\r\n      <div class=\"list-group-item actions\">\r\n        <a class=\"btn btn-xs btn-primary\" title=\"Add Rule\" data-ng-click=\"addRule()\">\r\n          <i class=\"fa fa-plus\"></i>\r\n        </a>\r\n        <a class=\"btn btn-xs btn-primary\" title=\"Add Group\" data-ng-click=\"addGroup()\">\r\n          <i class=\"fa fa-list\"></i>\r\n        </a>\r\n      </div>\r\n    </div>\r\n  </div>\r\n</div> -->\r\n\r\n<!-- <div class=\"elastic-builder\">\r\n  <div class=\"filter-panels\"> -->\r\n    <div class=\"list-group form-inline list-group-item-custom\" ng-switch=\"data.type\">\r\n      <div\r\n        data-ng-repeat=\"filter in filters\"\r\n        data-elastic-builder-chooser=\"filter\"\r\n        data-elastic-fields=\"data.fields\"\r\n        data-on-remove=\"removeChild($index)\"\r\n        data-depth=\"0\">\r\n      </div>\r\n      <div class=\"list-group-item actions list-group-item-custom\" ng-switch-when=\"condition\">\r\n        <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addGroup()\">\r\n                                <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_GROUP\' | translate }}\r\n                           </a>\r\n      </div>\r\n      <div class=\"list-group-item actions list-group-item-custom\" ng-switch-when=\"action\">\r\n        <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addRule()\">\r\n                               <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_ACTION\' | translate }}\r\n                          </a>\r\n      </div>\r\n      <div class=\"list-group-item actions list-group-item-custom\" ng-switch-default>\r\n        <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addRule()\">\r\n                               <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_CONDITION\' | translate }}\r\n                               </a>\r\n        <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addGroup()\">\r\n                                <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_GROUP\' | translate }}\r\n                           </a>\r\n      </div>\r\n    </div>\r\n  <!-- </div>\r\n</div> -->\r\n");
326 $templateCache.put("angular-elastic-builder/ChooserDirective.html","<!-- <div\r\n  class=\"list-group-item elastic-builder-chooser\"\r\n  data-ng-class=\"getGroupClassName()\">\r\n\r\n  <div data-ng-if=\"item.type === \'group\'\"\r\n    data-elastic-builder-group=\"item\"\r\n    data-depth=\"{{ depth }}\"\r\n    data-elastic-fields=\"elasticFields\"\r\n    data-on-remove=\"onRemove()\"></div>\r\n\r\n  <div data-ng-if=\"item.type !== \'group\'\"\r\n    data-elastic-builder-rule=\"item\"\r\n    data-elastic-fields=\"elasticFields\"\r\n    data-on-remove=\"onRemove()\"></div>\r\n\r\n</div> -->\r\n\r\n\r\n<div class=\"list-group-item elastic-builder-chooser list-group-item-custom\" data-ng-class=\"getGroupClassName()\">\r\n  <div data-ng-if=\"item.type === \'group\'\"\r\n    data-elastic-builder-group=\"item\"\r\n    data-depth=\"{{ depth }}\"\r\n    data-elastic-fields=\"elasticFields\"\r\n    data-on-remove=\"onRemove()\"></div>\r\n  <div data-ng-if=\"item.type !== \'group\'\"\r\n    data-elastic-builder-rule=\"item\"\r\n    data-elastic-fields=\"elasticFields\"\r\n    data-on-remove=\"onRemove()\"></div>\r\n</div>\r\n");
327 $templateCache.put("angular-elastic-builder/GroupDirective.html","<!-- <div class=\"elastic-builder-group\">\r\n  <h5>If\r\n    <select data-ng-model=\"group.subType\" class=\"form-control\">\r\n      <option value=\"and\">all</option>\r\n      <option value=\"or\">any</option>\r\n    </select>\r\n    of these conditions are met\r\n  </h5>\r\n  <div\r\n    data-ng-repeat=\"rule in group.rules\"\r\n    data-elastic-builder-chooser=\"rule\"\r\n    data-elastic-fields=\"elasticFields\"\r\n    data-depth=\"{{ +depth + 1 }}\"\r\n    data-on-remove=\"removeChild($index)\"></div>\r\n\r\n  <div class=\"list-group-item actions\" data-ng-class=\"getGroupClassName()\">\r\n    <a class=\"btn btn-xs btn-primary\" title=\"Add Sub-Rule\" data-ng-click=\"addRule()\">\r\n      <i class=\"fa fa-plus\"></i>\r\n    </a>\r\n    <a class=\"btn btn-xs btn-primary\" title=\"Add Sub-Group\" data-ng-click=\"addGroup()\">\r\n      <i class=\"fa fa-list\"></i>\r\n    </a>\r\n  </div>\r\n\r\n  <a class=\"btn btn-xs btn-danger remover\" data-ng-click=\"onRemove()\">\r\n    <i class=\"fa fa-minus\"></i>\r\n  </a>\r\n</div> -->\r\n\r\n\r\n<!-- BEGIN Portlet PORTLET-->\r\n<div class=\"portlet light\">\r\n  <div class=\"portlet-title\">\r\n    <div class=\"caption\" data-ng-class=\"{\'font-blue-hoki\': data.type === \'condition\', \'font-red-flamingo\': data.type === \'action\'}\">\r\n      <!-- <i class=\"icon-people font-green-sharp\"></i> -->\r\n      <h5><span class=\"caption-subject\">{{ \'APPLICATION_IF\' | translate }}</span>\r\n        <select data-ng-model=\"group.subType\" class=\"form-control\">\r\n          <option value=\"and\"><strong>{{ \'APPLICATION_ALL\' | translate | lowercase }}</strong></option>\r\n          <option value=\"or\"><strong>{{ \'APPLICATION_ANY\' | translate | lowercase }}</strong></option>\r\n        </select>\r\n        <span class=\"caption-subject\">{{group.subType == \'and\' ? \'MESSAGE_CODITIONS_MET_ALL\' : \'MESSAGE_CODITIONS_MET_ANY\' | translate}}</span>\r\n      </h5>\r\n    </div>\r\n  </div>\r\n  <div class=\"portlet-body\">\r\n    <div class=\"elastic-builder-group\">\r\n      <div\r\n        data-ng-repeat=\"rule in group.rules\"\r\n        data-elastic-builder-chooser=\"rule\"\r\n        data-elastic-fields=\"elasticFields\"\r\n        data-depth=\"{{ +depth + 1 }}\"\r\n        data-on-remove=\"removeChild($index)\"></div>\r\n\r\n      <div class=\"list-group-item actions list-group-item-custom\" data-ng-class=\"getGroupClassName()\">\r\n        <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addRule()\">\r\n          <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_CONDITION\' | translate }}\r\n        </a>\r\n        <a class=\"btn red btn-sm remover\" data-ng-click=\"onRemove()\">\r\n          <i class=\"icon-trash\"></i> {{ \'APPLICATION_REMOVE_GROUP\' | translate }}\r\n        </a>\r\n      </div>\r\n    </div>\r\n  </div>\r\n</div>\r\n<!-- END Portlet PORTLET-->\r\n");
328 $templateCache.put("angular-elastic-builder/RuleDirective.html","<!-- <div class=\"elastic-builder-rule\">\r\n  <select class=\"form-control\" data-ng-model=\"rule.field\" data-ng-options=\"key as key for (key, value) in elasticFields\"></select>\r\n\r\n  <span data-elastic-type=\"getType()\" data-rule=\"rule\" data-guide=\"elasticFields[rule.field]\"></span>\r\n\r\n  <a class=\"btn btn-xs btn-danger remover\" data-ng-click=\"onRemove()\">\r\n    <i class=\"fa fa-minus\"></i>\r\n  </a>\r\n\r\n</div> -->\r\n<!-- <div class=\"elastic-builder-rule\"> -->\r\n  <!-- <select class=\"form-control\" data-ng-model=\"rule.field\" data-ng-options=\"key as key for (key, value) in elasticFields\"></select> -->\n  {{elasticFields[rule.field].action.label | translate}}\n  <select class=\"form-control\" data-ng-model=\"rule.field\" >\n  <option ng-repeat=\"option in elasticFields\" value=\"{{option.value}}\">{{ option.label | translate }}</option>\n  </select>\n  <!-- <ui-select class=\"form-control\" data-ng-model=\"rule.field\" theme=\"bootstrap\">\r\n    <ui-select-match placeholder=\"{{\'APPLICATION_CONDITION\' | translate}}\">{{$translate.instant($select.selected.name)}}</ui-select-match>\r\n    <ui-select-choices repeat=\"value.value as key for (key, value) in elasticFields | filter: $select.search\">\r\n      {{$translate.instant(key.name)}}\r\n    </ui-select-choices>\r\n  </ui-select> -->\r\n\r\n  <span data-elastic-type=\"getType()\" data-rule=\"rule\" data-guide=\"elasticFields[rule.field]\"></span>\r\n\r\n  <!-- <a class=\"btn btn-xs btn-danger remover\" data-ng-click=\"onRemove()\">\r\n    <i class=\"fa fa-minus\"></i>\r\n  </a> -->\r\n\r\n  <a class=\"btn remover\" data-ng-click=\"onRemove()\">\r\n    <i class=\"icon-trash\"></i>\r\n  </a>\r\n\r\n<!-- </div> -->\r\n");
329 $templateCache.put("angular-elastic-builder/types/Boolean.html","<span class=\"boolean-rule\">\r\n  Equals\r\n\r\n  <!-- This is a weird hack to make sure these are numbers -->\r\n  <select\r\n    data-ng-model=\"rule.value\"\r\n    class=\"form-control\"\r\n    data-ng-options=\"booleans.indexOf(choice) as choice for choice in booleansOrder\">\r\n  </select>\r\n</span>\r\n");
330 $templateCache.put("angular-elastic-builder/types/Date.html","<span class=\"date-rule\">\r\n  <select data-ng-model=\"rule.subType\" class=\"form-control\">\r\n\r\n    <optgroup label=\"Generic\">\r\n      <option value=\"exists\">Exists</option>\r\n      <option value=\"notExists\">! Exists</option>\r\n    </optgroup>\r\n  </select>\r\n\r\n</span>\r\n");
331 $templateCache.put("angular-elastic-builder/types/Multi.html","<!-- <span class=\"multi-rule\">\r\n  <span data-ng-repeat=\"choice in guide.choices\">\r\n    <label class=\"checkbox\">\r\n      <input type=\"checkbox\" data-ng-model=\"rule.values[choice]\">\r\n      {{ choice }}\r\n    </label>\r\n  </span>\r\n</span> -->\r\n\r\n<span class=\"multi-rule\">\r\n  {{guide.action.midterm | translate}}\r\n  <!-- Range Fields -->\r\n  <!-- <select data-ng-model=\"rule.values\" class=\"form-control\"> -->\r\n  <select data-ng-model=\"rule.value\" class=\"form-control\">\r\n    <!-- <optgroup label=\"Numeral\"> -->\r\n      <option data-ng-repeat=\"(key, value) in guide.choices\" value=\"{{value}}\">{{key}}</option>\r\n    <!-- </optgroup> -->\r\n\r\n    <!-- <optgroup label=\"Generic\">\r\n      <option value=\"exists\">Exists</option>\r\n      <option value=\"notExists\">! Exists</option>\r\n    </optgroup> -->\r\n  </select>\r\n\r\n  <!-- <input data-ng-if=\"inputNeeded()\"\r\n    class=\"form-control\"\r\n    data-ng-model=\"rule.value\"\r\n    type=\"number\"\r\n    min=\"{{ guide.minimum }}\"\r\n    max=\"{{ guide.maximum }}\"> -->\r\n</span>\r\n");
332 $templateCache.put("angular-elastic-builder/types/Multiterm.html","<span class=\"elastic-multiterm\">\r\n  <select data-ng-model=\"rule.subType\" class=\"form-control\">\r\n      <option value=\"equals\">{{ \'APPLICATION_EQUALS_TO\' | translate }}</option>\r\n      <option value=\"notEquals\">{{ \'APPLICATION_NOT_EQUALS_TO\' | translate }}</option>\r\n  </select>\r\n  <select data-ng-model=\"rule.value\" class=\"form-control\">\r\n      <option data-ng-repeat=\"(key, value) in guide.choices\" value=\"{{value}}\">{{value | capitalize}}</option>\r\n  </select>\r\n</span>\r\n");
333 $templateCache.put("angular-elastic-builder/types/Number.html","<span class=\"number-rule\">\r\n  <select data-ng-model=\"rule.subType\" class=\"form-control\">\r\n    <!-- <optgroup label=\"Numeral\"> -->\r\n      <option value=\"equals\">=</option>\r\n      <option value=\"gt\">&gt;</option>\r\n      <option value=\"gte\">&ge;</option>\r\n      <option value=\"lt\">&lt;</option>\r\n      <option value=\"lte\">&le;</option>\r\n    <!-- </optgroup> -->\r\n\r\n    <!-- <optgroup label=\"Generic\"> -->\r\n      <!-- <option value=\"exists\">Exists</option>\r\n      <option value=\"notExists\">! Exists</option> -->\r\n    <!-- </optgroup> -->\r\n  </select>\r\n\r\n  <!-- Range Fields -->\r\n  <input data-ng-if=\"inputNeeded()\"\r\n    class=\"form-control\"\r\n    data-ng-model=\"rule.value\"\r\n    type=\"number\"\r\n    min=\"{{ guide.minimum }}\"\r\n    max=\"{{ guide.maximum }}\">\r\n\r\n    <!-- <span class=\"help-block\">{{rule.help}}</span> -->\r\n\r\n</span>\r\n");
334 $templateCache.put("angular-elastic-builder/types/Term.html","<!-- <span class=\"elastic-term\">\r\n  <select data-ng-model=\"rule.subType\" class=\"form-control\"> -->\r\n    <!-- Term Options -->\r\n    <!-- <optgroup label=\"Text\">\r\n      <option value=\"equals\">Equals</option>\r\n      <option value=\"notEquals\">! Equals</option>\r\n    </optgroup> -->\r\n\r\n    <!-- Generic Options -->\r\n    <!-- <optgroup label=\"Generic\">\r\n      <option value=\"exists\">Exists</option>\r\n      <option value=\"notExists\">! Exists</option>\r\n    </optgroup>\r\n\r\n  </select>\r\n  <input\r\n    data-ng-if=\"inputNeeded()\"\r\n    class=\"form-control\"\r\n    data-ng-model=\"rule.value\"\r\n    type=\"text\">\r\n</span> -->\r\n\r\n\r\n<span class=\"elastic-term\">\r\n  <select data-ng-model=\"rule.subType\" class=\"form-control\">\r\n    <!-- Term Options -->\r\n    <!-- <optgroup label=\"Text\"> -->\r\n      <option value=\"equals\">{{ \'APPLICATION_EQUALS_TO\' | translate }}</option>\r\n      <option value=\"notEquals\">{{ \'APPLICATION_NOT_EQUALS_TO\' | translate }}</option>\r\n    <!-- </optgroup> -->\r\n\r\n    <!-- Generic Options -->\r\n    <!-- <optgroup label=\"Generic\">\r\n      <option value=\"exists\">Exists</option>\r\n      <option value=\"notExists\">! Exists</option>\r\n    </optgroup> -->\r\n\r\n  </select>\r\n  <input\r\n    data-ng-if=\"inputNeeded()\"\r\n    class=\"form-control input-medium\"\r\n    data-ng-model=\"rule.value\"\r\n    type=\"text\">\r\n</span>\r\n");}]);})(window.angular);
335 /**
336  * angular-elastic-builder
337  *
338  * /src/services/GroupClassHelper.js
339  *
340  * This keeps all of the groups colored correctly
341  */
342
343 (function(angular) {
344   'use strict';
345
346   angular.module('angular-elastic-builder')
347     .factory('groupClassHelper', function groupClassHelper() {
348
349       return function(level) {
350         var levels = [
351           '',
352           // 'list-group-item-info',
353           // 'list-group-item-success',
354           // 'list-group-item-warning',
355           // 'list-group-item-danger',
356         ];
357
358         return levels[level % levels.length];
359       };
360     });
361
362 })(window.angular);
363
364 /**
365  * angular-elastic-builder
366  *
367  * /src/services/QueryService.js
368  *
369  * This file is used to convert filters into queries, and vice versa
370  */
371
372 (function(angular) {
373   'use strict';
374
375   angular.module('angular-elastic-builder')
376     .factory('elasticQueryService', [
377       function() {
378
379         return {
380           toFilters: toFilters,
381           toQuery: toQuery,
382         };
383       }
384     ]);
385
386   function toFilters(query, fieldMap) {
387     var filters = query.map(parseQueryGroup.bind(query, fieldMap));
388     return filters;
389   }
390
391   function toQuery(filters, fieldMap) {
392     var query = filters.map(parseFilterGroup.bind(filters, fieldMap)).filter(function(item) {
393       return !!item;
394     });
395     return query;
396   }
397
398   function parseQueryGroup(fieldMap, group, truthy) {
399     if (truthy !== false) {
400       truthy = true;
401     }
402
403     var key = Object.keys(group)[0],
404       typeMap = {
405         or: 'group',
406         and: 'group',
407         range: 'number',
408       },
409       type = typeMap[key] || 'item',
410       obj = getFilterTemplate(type);
411
412     switch (key) {
413       case 'or':
414       case 'and':
415         obj.rules = group[key].map(parseQueryGroup.bind(group, fieldMap));
416         obj.subType = key;
417         break;
418       case 'missing':
419       case 'exists':
420         obj.field = group[key].field;
421         obj.subType = {
422           exists: 'exists',
423           missing: 'notExists',
424         }[key];
425         delete obj.value;
426         break;
427       case 'term':
428       case 'terms':
429         obj.field = Object.keys(group[key])[0];
430         var fieldData = fieldMap[Object.keys(group[key])[0]];
431
432         if (fieldData.type === 'multi') {
433           var vals = group[key][obj.field];
434           if (typeof vals === 'string') {
435             vals = [vals];
436           }
437           // MY CUSTOM MODIFICATION
438           obj.value = group[key][obj.field];
439           // obj.values = fieldData.choices.reduce(function(prev, choice) {
440           //   prev[choice] = truthy === (group[key][obj.field].indexOf(choice) > -1);
441           //   return prev;
442           // }, {});
443         } else {
444           obj.subType = truthy ? 'equals' : 'notEquals';
445           obj.value = group[key][obj.field];
446
447           if (typeof obj.value === 'number') {
448             obj.subType = 'boolean';
449           }
450         }
451         break;
452       case 'range':
453         obj.field = Object.keys(group[key])[0];
454         obj.subType = Object.keys(group[key][obj.field])[0];
455         obj.value = group[key][obj.field][obj.subType];
456         break;
457       case 'not':
458         obj = parseQueryGroup(fieldMap, group[key].filter, false);
459         break;
460       default:
461         obj.field = Object.keys(group[key])[0];
462         break;
463     }
464
465     return obj;
466   }
467
468   function parseFilterGroup(fieldMap, group) {
469     var obj = {};
470     if (group.type === 'group') {
471       obj[group.subType] = group.rules.map(parseFilterGroup.bind(group, fieldMap)).filter(function(item) {
472         return !!item;
473       });
474       return obj;
475     }
476
477     var fieldName = group.field;
478     var fieldData = fieldMap[fieldName];
479
480     if (!fieldName) {
481       return;
482     }
483
484     switch (fieldData.type) {
485       case 'term':
486       case 'multiterm':
487         if (fieldData.subType === 'boolean') {
488           group.subType = 'boolean';
489         }
490
491         if (!group.subType) {
492           return;
493         }
494         switch (group.subType) {
495           case 'equals':
496           case 'boolean':
497             if (group.value === undefined) {
498               return;
499             }
500             obj.term = {};
501             obj.term[fieldName] = group.value;
502             break;
503           case 'notEquals':
504             if (group.value === undefined) {
505               return;
506             }
507             obj.not = {
508               filter: {
509                 term: {}
510               }
511             };
512             obj.not.filter.term[fieldName] = group.value;
513             break;
514           case 'exists':
515             obj.exists = {
516               field: fieldName
517             };
518             break;
519           case 'notExists':
520             obj.missing = {
521               field: fieldName
522             };
523             break;
524           default:
525             throw new Error('unexpected subtype ' + group.subType);
526         }
527         break;
528
529       case 'number':
530         obj.range = {};
531         obj.range[fieldName] = {};
532         obj.range[fieldName][group.subType] = group.value;
533         break;
534
535       case 'date':
536         if (group.subType === 'exists') {
537           obj.exists = {
538             field: fieldName
539           };
540         } else if (group.subType === 'notExists') {
541           obj.missing = {
542             field: fieldName
543           };
544         } else {
545           throw new Error('unexpected subtype');
546         }
547
548         break;
549
550       case 'multi':
551         obj.terms = {};
552         obj.terms[fieldName] = group.value;
553         // obj.terms[fieldName] = Object.keys(group.values || {}).reduce(function(prev, key) {
554         //   if (group.values[key]) prev.push(key);
555         //
556         //   return prev;
557         // }, []);
558         break;
559
560       default:
561         throw new Error('unexpected type');
562     }
563
564     return obj;
565   }
566
567   function getFilterTemplate(type) {
568     var templates = {
569       group: {
570         type: 'group',
571         subType: '',
572         rules: [],
573       },
574       item: {
575         field: '',
576         subType: '',
577         value: '',
578       },
579       number: {
580         field: '',
581         subType: '',
582         value: null,
583       }
584     };
585
586     return angular.copy(templates[type]);
587   }
588
589 })(window.angular);