352ab2ea26e7033b37d84c3541876f1661078b8b
[motion.git] / public / assets / plugins / angular-elastic-builder-back / dist / angular-elastic-builder.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             var item = scope.item;
145
146             scope.getGroupClassName = function() {
147               var level = depth;
148               if (item.type === 'group') {
149                 level++;
150               }
151
152               return groupClassHelper(level);
153             };
154           });
155         }
156       };
157     }
158
159   ]);
160
161 })(window.angular);
162
163 /**
164  * angular-elastic-builder
165  *
166  * /src/directives/Group.js
167  */
168
169 (function(angular) {
170   'use strict';
171
172   var app = angular.module('angular-elastic-builder');
173
174   app.directive('elasticBuilderGroup', [
175     'RecursionHelper',
176     'groupClassHelper',
177
178     function elasticBuilderGroup(RH, groupClassHelper) {
179
180       return {
181         scope: {
182           elasticFields: '=',
183           group: '=elasticBuilderGroup',
184           onRemove: '&',
185         },
186
187         templateUrl: 'angular-elastic-builder/GroupDirective.html',
188
189         compile: function(element) {
190           return RH.compile(element, function(scope, el, attrs) {
191             var depth = scope.depth = (+attrs.depth);
192             var group = scope.group;
193
194             scope.addRule = function() {
195               group.rules.push({});
196             };
197             scope.addGroup = function() {
198               group.rules.push({
199                 type: 'group',
200                 subType: '$and',
201                 rules: [],
202               });
203             };
204
205             scope.removeChild = function(idx) {
206               group.rules.splice(idx, 1);
207             };
208
209             scope.getGroupClassName = function() {
210               return groupClassHelper(depth + 1);
211             };
212           });
213         }
214       };
215     }
216
217   ]);
218
219 })(window.angular);
220
221 /**
222  * angular-elastic-builder
223  *
224  * /src/directives/Rule.js
225  */
226
227 (function(angular) {
228   'use strict';
229
230   var app = angular.module('angular-elastic-builder');
231
232   app.directive('elasticBuilderRule', [
233
234     function elasticBuilderRule() {
235       return {
236         scope: {
237           elasticFields: '=',
238           rule: '=elasticBuilderRule',
239           onRemove: '&',
240         },
241
242         templateUrl: 'angular-elastic-builder/RuleDirective.html',
243
244         link: function(scope) {
245           scope.getType = function() {
246             var fields = scope.elasticFields;
247             var field = scope.rule.field;
248
249             if (!fields || !field) {
250               return;
251             }
252
253             if (fields[field].subType === 'boolean') {
254               return 'boolean';
255             }
256
257             return fields[field].type;
258           };
259         }
260       };
261     }
262
263   ]);
264
265 })(window.angular);
266
267 /**
268  * angular-elastic-builder
269  *
270  * /src/directives/RuleTypes.js
271  *
272  * Determines which Rule type should be displayed
273  */
274
275 (function(angular) {
276   'use strict';
277
278   var app = angular.module('angular-elastic-builder');
279
280   app.directive('elasticType', [
281
282     function() {
283       return {
284         scope: {
285           type: '=elasticType',
286           rule: '=',
287           guide: '=',
288         },
289
290         template: '<ng-include src="getTemplateUrl()" />',
291
292         link: function(scope) {
293           scope.getTemplateUrl = function() {
294             var type = scope.type;
295             if (!type) {
296               return;
297             }
298
299             type = type.charAt(0).toUpperCase() + type.slice(1);
300
301             return 'angular-elastic-builder/types/' + type + '.html';
302           };
303
304           // This is a weird hack to make sure these are numbers
305           scope.booleans = ['False', 'True'];
306           scope.booleansOrder = ['True', 'False'];
307
308           scope.inputNeeded = function() {
309             var needs = [
310               'equals',
311               'notEquals',
312
313               '$gt',
314               '$gte',
315               '$lt',
316               '$lte',
317             ];
318
319             return ~needs.indexOf(scope.rule.subType);
320           };
321         },
322       };
323     }
324
325   ]);
326
327 })(window.angular);
328
329 /**
330  * angular-elastic-builder
331  *
332  * /src/services/GroupClassHelper.js
333  *
334  * This keeps all of the groups colored correctly
335  */
336
337 (function(angular) {
338   'use strict';
339
340   angular.module('angular-elastic-builder')
341     .factory('groupClassHelper', function groupClassHelper() {
342
343       return function(level) {
344         var levels = [
345           '',
346           // 'list-group-item-info',
347           // 'list-group-item-success',
348           // 'list-group-item-warning',
349           // 'list-group-item-danger',
350         ];
351
352         return levels[level % levels.length];
353       };
354     });
355
356 })(window.angular);
357
358 /**
359  * angular-elastic-builder
360  *
361  * /src/services/QueryService.js
362  *
363  * This file is used to convert filters into queries, and vice versa
364  */
365
366 (function(angular) {
367   'use strict';
368
369   angular.module('angular-elastic-builder')
370     .factory('elasticQueryService', [
371       function() {
372
373         return {
374           toFilters: toFilters,
375           toQuery: toQuery,
376         };
377       }
378     ]);
379
380   function toFilters(query, fieldMap) {
381     var filters = query.map(parseQueryGroup.bind(query, fieldMap));
382     return filters;
383   }
384
385   function toQuery(filters, fieldMap) {
386     var query = filters.map(parseFilterGroup.bind(filters, fieldMap)).filter(function(item) {
387       return !!item;
388     });
389     return query;
390   }
391
392   function parseQueryGroup(fieldMap, group, truthy) {
393     if (truthy !== false) {
394       truthy = true;
395     }
396
397     var key = Object.keys(group)[0];
398     var typeMap = {
399       or: 'group',
400       and: 'group',
401       range: 'number',
402     };
403     var type = typeMap[key] || 'item';
404     var obj = getFilterTemplate(type);
405
406     switch (key) {
407       case '$or':
408       case '$and':
409         obj.rules = group[key].map(parseQueryGroup.bind(group, fieldMap));
410         obj.subType = key;
411         break;
412       case 'missing':
413       case 'exists':
414         obj.field = group[key].field;
415         obj.subType = {
416           exists: 'exists',
417           missing: 'notExists',
418         }[key];
419         delete obj.value;
420         break;
421       case 'term':
422       case 'terms':
423         obj.field = Object.keys(group[key])[0];
424         var fieldData = fieldMap[Object.keys(group[key])[0]];
425
426         if (fieldData.type === 'multi') {
427           var vals = group[key][obj.field];
428           if (typeof vals === 'string') {
429             vals = [vals];
430           }
431           obj.values = fieldData.choices.reduce(function(prev, choice) {
432             prev[choice] = truthy === (group[key][obj.field].indexOf(choice) > -1);
433             return prev;
434           }, {});
435         } else {
436           obj.subType = truthy ? 'equals' : 'notEquals';
437           obj.value = group[key][obj.field];
438
439           if (typeof obj.value === 'number') {
440             obj.subType = 'boolean';
441           }
442         }
443         break;
444       case 'range':
445         obj.field = Object.keys(group[key])[0];
446         obj.subType = Object.keys(group[key][obj.field])[0];
447         obj.value = group[key][obj.field][obj.subType];
448         break;
449       case 'not':
450         obj = parseQueryGroup(fieldMap, group[key].filter, false);
451         break;
452       default:
453         obj.field = Object.keys(group[key])[0];
454         break;
455     }
456
457     return obj;
458   }
459
460   function parseFilterGroup(fieldMap, group) {
461     var obj = {};
462     if (group.type === 'group') {
463       obj[group.subType] = group.rules.map(parseFilterGroup.bind(group, fieldMap)).filter(function(item) {
464         return !!item;
465       });
466       return obj;
467     }
468
469     var fieldName = group.field;
470     var fieldData = fieldMap[fieldName];
471
472     if (!fieldName) {
473       return;
474     }
475
476     switch (fieldData.type) {
477       case 'term':
478         if (fieldData.subType === 'boolean') {
479           group.subType = 'boolean';
480         }
481
482         if (!group.subType) {
483           return;
484         }
485         switch (group.subType) {
486           case 'equals':
487           case 'boolean':
488             if (group.value === undefined) {
489               return;
490             }
491             obj[fieldName] = group.value;
492             break;
493           case 'notEquals':
494             if (group.value === undefined) {
495               return;
496             }
497             obj[fieldName] = {
498               $ne: group.value
499             };
500             break;
501           case 'exists':
502             obj.exists = {
503               field: fieldName
504             };
505             break;
506           case 'notExists':
507             obj.missing = {
508               field: fieldName
509             };
510             break;
511           default:
512             throw new Error('unexpected subtype ' + group.subType);
513         }
514         break;
515
516       case 'select':
517
518       case 'number':
519         obj[fieldName] = {};
520
521         switch (group.subType) {
522           case 'equals':
523             obj[fieldName] = group.value;
524             break;
525           default:
526             obj[fieldName][group.subType] = group.value;
527         }
528
529         break;
530
531       case 'date':
532         if (group.subType === 'exists') {
533           obj.exists = {
534             field: fieldName
535           };
536         } else if (group.subType === 'notExists') {
537           obj.missing = {
538             field: fieldName
539           };
540         } else {
541           throw new Error('unexpected subtype');
542         }
543
544         break;
545
546       case 'multi':
547         obj.terms = {};
548         obj.terms[fieldName] = Object.keys(group.values || {}).reduce(function(prev, key) {
549           if (group.values[key]) {
550             prev.push(key);
551           }
552
553           return prev;
554         }, []);
555         break;
556
557         // case 'multi':
558         //   obj.terms = {};
559         //   obj.terms[fieldName] = Object.keys(group.values || {}).reduce(function(prev, key) {
560         //     if (group.values[key]) {
561         //       prev.push(key);
562         //     }
563         //
564         //     return prev;
565         //   }, []);
566         //   break;
567
568       default:
569         throw new Error('unexpected type');
570     }
571
572     return obj;
573   }
574
575   function getFilterTemplate(type) {
576     var templates = {
577       group: {
578         type: 'group',
579         subType: '',
580         rules: [],
581       },
582       item: {
583         field: '',
584         subType: '',
585         value: '',
586       },
587       number: {
588         field: '',
589         subType: '',
590         value: null,
591       }
592     };
593
594     return angular.copy(templates[type]);
595   }
596
597 })(window.angular);
598
599 (function(angular) {"use strict"; angular.module("angular-elastic-builder").run(["$templateCache", function($templateCache) {$templateCache.put("angular-elastic-builder/BuilderDirective.html","<!-- <div class=\"elastic-builder\">\n  <div class=\"filter-panels\"> -->\n    <div class=\"list-group form-inline list-group-item-custom\">\n      <div\n        data-ng-repeat=\"filter in filters\"\n        data-elastic-builder-chooser=\"filter\"\n        data-elastic-fields=\"data.fields\"\n        data-on-remove=\"removeChild($index)\"\n        data-depth=\"0\"></div>\n      <div class=\"list-group-item actions\">\n        <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addRule()\">\n                            <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_CONDITION\' | translate }}\n                         </a>\n        <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addGroup()\">\n                            <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_GROUP\' | translate }}\n                             </a>\n      </div>\n    </div>\n  <!-- </div>\n</div> -->\n");
600 $templateCache.put("angular-elastic-builder/ChooserDirective.html","<div class=\"list-group-item elastic-builder-chooser list-group-item-custom\" data-ng-class=\"getGroupClassName()\">\n  <div data-ng-if=\"item.type === \'group\'\"\n    data-elastic-builder-group=\"item\"\n    data-depth=\"{{ depth }}\"\n    data-elastic-fields=\"elasticFields\"\n    data-on-remove=\"onRemove()\"></div>\n  <div data-ng-if=\"item.type !== \'group\'\"\n    data-elastic-builder-rule=\"item\"\n    data-elastic-fields=\"elasticFields\"\n    data-on-remove=\"onRemove()\"></div>\n</div>\n");
601 $templateCache.put("angular-elastic-builder/GroupDirective.html","<!-- BEGIN Portlet PORTLET-->\n<div class=\"portlet light bordered\">\n  <div class=\"portlet-title\">\n    <div class=\"caption font-green-sharp\">\n      <!-- <i class=\"icon-people font-green-sharp\"></i> -->\n      <h5><span class=\"caption-subject\">If</span>\n        <select data-ng-model=\"group.subType\" class=\"form-control\">\n          <option value=\"$and\"><strong>all</strong></option>\n          <option value=\"$or\"><strong>any</strong></option>\n        </select>\n        <span class=\"caption-subject\">of these conditions are met</span>\n      </h5>\n    </div>\n  </div>\n  <div class=\"portlet-body\">\n    <div class=\"elastic-builder-group\">\n      <div\n        data-ng-repeat=\"rule in group.rules\"\n        data-elastic-builder-chooser=\"rule\"\n        data-elastic-fields=\"elasticFields\"\n        data-depth=\"{{ +depth + 1 }}\"\n        data-on-remove=\"removeChild($index)\"></div>\n\n      <div class=\"list-group-item actions list-group-item-custom\" data-ng-class=\"getGroupClassName()\">\n        <a href=\"#\" class=\"btn green btn-sm\" data-ng-click=\"addRule()\">\n          <i class=\"icon-plus\"></i> {{ \'APPLICATION_ADD_CONDITION\' | translate }}\n        </a>\n        <a class=\"btn red btn-sm remover\" data-ng-click=\"onRemove()\">\n          <i class=\"icon-trash\"></i> {{ \'APPLICATION_REMOVE_GROUP\' | translate }}\n        </a>\n      </div>\n    </div>\n  </div>\n</div>\n<!-- END Portlet PORTLET-->\n");
602 $templateCache.put("angular-elastic-builder/RuleDirective.html","<!-- <div class=\"elastic-builder-rule\"> -->\n  <select class=\"form-control\" data-ng-model=\"rule.field\" data-ng-options=\"key as key for (key, value) in elasticFields\"></select>\n\n  <span data-elastic-type=\"getType()\" data-rule=\"rule\" data-guide=\"elasticFields[rule.field]\"></span>\n\n  <!-- <a class=\"btn btn-xs btn-danger remover\" data-ng-click=\"onRemove()\">\n    <i class=\"fa fa-minus\"></i>\n  </a> -->\n\n  <a class=\"btn remover\" data-ng-click=\"onRemove()\">\n    <i class=\"icon-trash\"></i>\n  </a>\n\n<!-- </div> -->\n");
603 $templateCache.put("angular-elastic-builder/types/Boolean.html","<span class=\"boolean-rule\">\n  Equals\n\n  <!-- This is a weird hack to make sure these are numbers -->\n  <select\n    data-ng-model=\"rule.value\"\n    class=\"form-control\"\n    data-ng-options=\"booleans.indexOf(choice) as choice for choice in booleansOrder\">\n  </select>\n</span>\n");
604 $templateCache.put("angular-elastic-builder/types/Date.html","<span class=\"date-rule\">\n  <select data-ng-model=\"rule.subType\" class=\"form-control\">\n\n    <optgroup label=\"Generic\">\n      <option value=\"exists\">Exists</option>\n      <option value=\"notExists\">! Exists</option>\n    </optgroup>\n  </select>\n\n</span>\n");
605 $templateCache.put("angular-elastic-builder/types/Multi.html","<span class=\"multi-rule\">\n  <span data-ng-repeat=\"choice in guide.choices\">\n    <label class=\"checkbox\">\n      <input type=\"checkbox\" data-ng-model=\"rule.values[choice]\">\n      {{ choice }}\n    </label>\n  </span>\n</span>\n");
606 $templateCache.put("angular-elastic-builder/types/Number.html","<span class=\"number-rule\">\n  <select data-ng-model=\"rule.subType\" class=\"form-control\">\n    <!-- <optgroup label=\"Numeral\"> -->\n      <option value=\"equals\">=</option>\n      <option value=\"$gt\">&gt;</option>\n      <option value=\"$gte\">&ge;</option>\n      <option value=\"$lt\">&lt;</option>\n      <option value=\"$lte\">&le;</option>\n    <!-- </optgroup> -->\n\n    <!-- <optgroup label=\"Generic\">\n      <option value=\"exists\">Exists</option>\n      <option value=\"notExists\">! Exists</option>\n    </optgroup> -->\n  </select>\n\n  <!-- Range Fields -->\n  <input data-ng-if=\"inputNeeded()\"\n    class=\"form-control\"\n    data-ng-model=\"rule.value\"\n    type=\"number\"\n    min=\"{{ guide.minimum }}\"\n    max=\"{{ guide.maximum }}\">\n</span>\n");
607 $templateCache.put("angular-elastic-builder/types/Select.html","<span class=\"number-rule\">\n  <select data-ng-model=\"rule.subType\" class=\"form-control\">\n    <!-- <optgroup label=\"Numeral\"> -->\n      <option value=\"equals\">=</option>\n      <!-- <option value=\"$gt\">&gt;</option>\n      <option value=\"$gte\">&ge;</option>\n      <option value=\"$lt\">&lt;</option>\n      <option value=\"$lte\">&le;</option> -->\n    <!-- </optgroup> -->\n\n    <!-- <optgroup label=\"Generic\">\n      <option value=\"exists\">Exists</option>\n      <option value=\"notExists\">! Exists</option>\n    </optgroup> -->\n  </select>\n\n  <!-- Range Fields -->\n  <select data-ng-model=\"rule.value\" class=\"form-control\">\n    <!-- <optgroup label=\"Numeral\"> -->\n      <option data-ng-repeat=\"choice in guide.choices\" value=\"{{choice.value}}\">{{choice.key}}</option>\n    <!-- </optgroup> -->\n\n    <!-- <optgroup label=\"Generic\">\n      <option value=\"exists\">Exists</option>\n      <option value=\"notExists\">! Exists</option>\n    </optgroup> -->\n  </select>\n\n  <!-- <input data-ng-if=\"inputNeeded()\"\n    class=\"form-control\"\n    data-ng-model=\"rule.value\"\n    type=\"number\"\n    min=\"{{ guide.minimum }}\"\n    max=\"{{ guide.maximum }}\"> -->\n</span>\n");
608 $templateCache.put("angular-elastic-builder/types/Term.html","<span class=\"elastic-term\">\n  <select data-ng-model=\"rule.subType\" class=\"form-control\">\n    <!-- Term Options -->\n    <!-- <optgroup label=\"Text\"> -->\n      <option value=\"equals\">{{ \'APPLICATION_EQUALS_TO\' | translate }}</option>\n      <option value=\"notEquals\">{{ \'APPLICATION_NOT_EQUALS_TO\' | translate }}</option>\n    <!-- </optgroup> -->\n\n    <!-- Generic Options -->\n    <!-- <optgroup label=\"Generic\">\n      <option value=\"exists\">Exists</option>\n      <option value=\"notExists\">! Exists</option>\n    </optgroup> -->\n\n  </select>\n  <input\n    data-ng-if=\"inputNeeded()\"\n    class=\"form-control\"\n    data-ng-model=\"rule.value\"\n    type=\"text\">\n</span>\n");}]);})(window.angular);