Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@momsfriendlydevco/angular-ui-query-builder

Package Overview
Dependencies
Maintainers
3
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@momsfriendlydevco/angular-ui-query-builder - npm Package Compare versions

Comparing version 1.2.0 to 1.2.1

gh-pages/app.css

32

dist/angular-ui-query-builder-tables.js

@@ -5,3 +5,3 @@ 'use strict';

// Table decorator {{{
// qbTableSettings (service) {{{
.service('qbTableSettings', function () {

@@ -16,6 +16,10 @@ return {

})
// }}}
// qbTable (directive) {{{
/**
* Directive applied to a table element to indicate that we should manage that table via angular-ui-query
* @param {Object} qbTable The query object to modify
* @param {boolean} stickyThead Anything within the `thead` section of the table should remain on the screen while scrolling
* @param {boolean} stickyTfoot Anything within the `tfoot` section of the table should remain on the screen while scrolling
* @emits qbTableQueryChange Emitted to child elements as (e, query) when the query object changes

@@ -26,6 +30,8 @@ */

scope: {
qbTable: '='
qbTable: '=',
stickyThead: '<',
stickyTfoot: '<'
},
restrict: 'AC',
controller: ['$scope', 'qbTableSettings', function controller($scope, qbTableSettings) {
controller: ['$attrs', '$element', '$scope', 'qbTableSettings', function controller($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;

@@ -58,6 +64,16 @@ $ctrl.query = $scope.qbTable; // Copy into $ctrl so children can access it / $watch it

};
$element.addClass('qb-table');
$scope.$watch('stickyThead', function () {
return $element.toggleClass('qb-sticky-thead', $scope.stickyThead || $attrs.stickyThead === '');
});
$scope.$watch('stickyTfoot', function () {
return $element.toggleClass('qb-sticky-tfoot', $scope.stickyTfoot || $attrs.stickyTfoot === '');
});
}]
};
})
// }}}
// qbCol (directive) {{{
/**

@@ -88,3 +104,3 @@ * Directive for header elements to add angular-ui-query functionality

$ctrl.$onInit = function () {
$scope.canSort = $scope.sortable || $attrs.sortable === '';
return $scope.canSort = $scope.sortable || $attrs.sortable === '';
};

@@ -111,6 +127,7 @@

if ($scope.sortable) {
// Sort by a specific field
$scope.qbTable.setField('sort', $scope.sortable);
} else if ($scope.q && $attrs.sortable === '') {
} else if ($scope.qbCol && $attrs.sortable === '') {
// Has attribute but no value - assume main key if we have one
$scope.qbTable.setField('sort', $scope.q);
$scope.qbTable.setField('sort', $scope.qbCol);
}

@@ -126,3 +143,5 @@ };

})
// }}}
// qbPagination {{{
/**

@@ -166,3 +185,2 @@ * Directive to add table pagination

});
// }}}

@@ -1,1 +0,1 @@

"use strict";angular.module("angular-ui-query-builder").service("qbTableSettings",function(){return{icons:{sortNone:"fa fa-fw fa-sort text-muted",sortAsc:"fa fa-fw fa-sort-alpha-asc text-primary",sortDesc:"fa fa-fw fa-sort-alpha-desc text-primary"}}}).directive("qbTable",function(){return{scope:{qbTable:"="},restrict:"AC",controller:["$scope","qbTableSettings",function(t,e){var a=this;a.query=t.qbTable,a.setField=function(e,r){if(void 0!=r)switch(e){case"sort":a.query.sort===r?a.query.sort="-"+r:(a.query.sort,a.query.sort=r);break;default:t.qbTable[e]=r}else delete a.query[e]}}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,a){var r=this;e.qbTableSettings=a,e.canSort=!1,e.isSorted=!1,r.$onInit=function(){e.canSort=e.sortable||""===t.sortable},e.$watch("qbTable.query.sort",function(t){var a=e.sortable||e.q;t?angular.isArray(t)&&t.some(function(t){return t==a})||t==a?e.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+a})||t=="-"+a?e.isSorted="desc":e.isSorted=!1:e.isSorted=!1}),e.toggleSort=function(){e.sortable?e.qbTable.setField("sort",e.sortable):e.q&&""===t.sortable&&e.qbTable.setField("sort",e.q)}}],link:function(t,e,a,r){t.qbTable=r},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">\n\t\t\t<i class="{{\n\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t}}"></i>\n\t\t</a>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",controller:["$attrs","$scope","qbTableSettings",function(t,e,a){e.qbTableSettings=a,e.canPrev=!0,e.canNext=!0,e.$watchGroup(["qbTable.query.limit","qbTable.query.skip"],function(t){e.canPrev=e.qbTable.query.skip>0,e.canNext=!e.total||e.qbTable.query.skip+e.qbTable.query.limit<e.total}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.min((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0));else{if(1!=t)throw new Error("Unsupported page move: "+t);e.qbTable.setField("skip",(e.qbTable.query.skip||0)+(e.qbTable.query.limit||10),0)}}}],link:function(t,e,a,r){t.qbTable=r},template:'\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i class="fa fa-arrow-left"></i></a></li>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i class="fa fa-arrow-right"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'}});
"use strict";angular.module("angular-ui-query-builder").service("qbTableSettings",function(){return{icons:{sortNone:"fa fa-fw fa-sort text-muted",sortAsc:"fa fa-fw fa-sort-alpha-asc text-primary",sortDesc:"fa fa-fw fa-sort-alpha-desc text-primary"}}}).directive("qbTable",function(){return{scope:{qbTable:"=",stickyThead:"<",stickyTfoot:"<"},restrict:"AC",controller:["$attrs","$element","$scope","qbTableSettings",function(t,e,a,r){var s=this;s.query=a.qbTable,s.setField=function(t,e){if(void 0!=e)switch(t){case"sort":s.query.sort===e?s.query.sort="-"+e:(s.query.sort,s.query.sort=e);break;default:a.qbTable[t]=e}else delete s.query[t]},e.addClass("qb-table"),a.$watch("stickyThead",function(){return e.toggleClass("qb-sticky-thead",a.stickyThead||""===t.stickyThead)}),a.$watch("stickyTfoot",function(){return e.toggleClass("qb-sticky-tfoot",a.stickyTfoot||""===t.stickyTfoot)})}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,a){var r=this;e.qbTableSettings=a,e.canSort=!1,e.isSorted=!1,r.$onInit=function(){return e.canSort=e.sortable||""===t.sortable},e.$watch("qbTable.query.sort",function(t){var a=e.sortable||e.q;t?angular.isArray(t)&&t.some(function(t){return t==a})||t==a?e.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+a})||t=="-"+a?e.isSorted="desc":e.isSorted=!1:e.isSorted=!1}),e.toggleSort=function(){e.sortable?e.qbTable.setField("sort",e.sortable):e.qbCol&&""===t.sortable&&e.qbTable.setField("sort",e.qbCol)}}],link:function(t,e,a,r){t.qbTable=r},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">\n\t\t\t<i class="{{\n\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t}}"></i>\n\t\t</a>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",controller:["$attrs","$scope","qbTableSettings",function(t,e,a){e.qbTableSettings=a,e.canPrev=!0,e.canNext=!0,e.$watchGroup(["qbTable.query.limit","qbTable.query.skip"],function(t){e.canPrev=e.qbTable.query.skip>0,e.canNext=!e.total||e.qbTable.query.skip+e.qbTable.query.limit<e.total}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.min((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0));else{if(1!=t)throw new Error("Unsupported page move: "+t);e.qbTable.setField("skip",(e.qbTable.query.skip||0)+(e.qbTable.query.limit||10),0)}}}],link:function(t,e,a,r){t.qbTable=r},template:'\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i class="fa fa-arrow-left"></i></a></li>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i class="fa fa-arrow-right"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'}});

@@ -483,3 +483,3 @@ 'use strict';

// Table decorator {{{
// qbTableSettings (service) {{{
.service('qbTableSettings', function () {

@@ -494,6 +494,10 @@ return {

})
// }}}
// qbTable (directive) {{{
/**
* Directive applied to a table element to indicate that we should manage that table via angular-ui-query
* @param {Object} qbTable The query object to modify
* @param {boolean} stickyThead Anything within the `thead` section of the table should remain on the screen while scrolling
* @param {boolean} stickyTfoot Anything within the `tfoot` section of the table should remain on the screen while scrolling
* @emits qbTableQueryChange Emitted to child elements as (e, query) when the query object changes

@@ -504,6 +508,8 @@ */

scope: {
qbTable: '='
qbTable: '=',
stickyThead: '<',
stickyTfoot: '<'
},
restrict: 'AC',
controller: ['$scope', 'qbTableSettings', function controller($scope, qbTableSettings) {
controller: ['$attrs', '$element', '$scope', 'qbTableSettings', function controller($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;

@@ -536,6 +542,16 @@ $ctrl.query = $scope.qbTable; // Copy into $ctrl so children can access it / $watch it

};
$element.addClass('qb-table');
$scope.$watch('stickyThead', function () {
return $element.toggleClass('qb-sticky-thead', $scope.stickyThead || $attrs.stickyThead === '');
});
$scope.$watch('stickyTfoot', function () {
return $element.toggleClass('qb-sticky-tfoot', $scope.stickyTfoot || $attrs.stickyTfoot === '');
});
}]
};
})
// }}}
// qbCol (directive) {{{
/**

@@ -566,3 +582,3 @@ * Directive for header elements to add angular-ui-query functionality

$ctrl.$onInit = function () {
$scope.canSort = $scope.sortable || $attrs.sortable === '';
return $scope.canSort = $scope.sortable || $attrs.sortable === '';
};

@@ -589,6 +605,7 @@

if ($scope.sortable) {
// Sort by a specific field
$scope.qbTable.setField('sort', $scope.sortable);
} else if ($scope.q && $attrs.sortable === '') {
} else if ($scope.qbCol && $attrs.sortable === '') {
// Has attribute but no value - assume main key if we have one
$scope.qbTable.setField('sort', $scope.q);
$scope.qbTable.setField('sort', $scope.qbCol);
}

@@ -604,3 +621,5 @@ };

})
// }}}
// qbPagination {{{
/**

@@ -644,3 +663,2 @@ * Directive to add table pagination

});
// }}}

@@ -1,1 +0,1 @@

"use strict";function _defineProperty(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}angular.module("angular-ui-query-builder",[]).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\n\t\t\t\x3c!-- Meta field: sort {{{ --\x3e\n\t\t\t<div class="query-container">\n\t\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tSort by\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.sort" type="text" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Meta field: limit {{{ --\x3e\n\t\t\t<div class="query-container">\n\t\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tLimited to\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.limit" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-1">\n\t\t\t\t\t\tSkipping\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.skip" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\n\t\t</div>\n\t',controller:["$scope",function(t){var e=this;t.$watch("$ctrl.spec",function(){_.forEach(e.spec,function(t,e){t.title||(t.title=_.startCase(e)),t.enum&&_.isArray(t.enum)&&(t.enum=_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value())})})}]}).component("uiQueryBuilderBranch",{bindings:{branch:"=",spec:"<"},template:'\n\t\t<div ng-repeat="leaf in $ctrl.properties | filter:{isMeta:false} track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query type component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Is after</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gte\')">Is at least</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Is before</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lte\')">Is at most</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="date" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="date" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t\t\x3c!-- Add button {{{ --\x3e\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t\x3c!-- }}} --\x3e\n\t',controller:["$element","$scope",function(t,e){var n=this;n.operands=[{id:"$eq",setter:function(t){return{$eq:t}},export:function(t){return t.valueEdit},base:{title:"Is",type:"string"},boolean:{title:"Is",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"},date:{title:"Is exactly",type:"date"}},{id:"$ne",setter:function(t){return{$ne:t}},export:function(t){return{$ne:t.valueEdit}},base:{title:"Is not",type:"string"},boolean:{title:"Is not",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"},date:{title:"Is not exactly",type:"date"}},{id:"$in",setter:function(t){return{$in:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$in:t.value.$in}},base:{title:"One of",type:"array"}},{id:"$nin",setter:function(t){return{$nin:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$nin:t.value.$nin}},base:{title:"Not one of",type:"array"}},{id:"$gt",setter:function(t){return{$gt:t}},export:function(t){return{$gt:t.value.$gt}},base:{title:"Above",type:"number"},date:{title:"Is after",type:"date"}},{id:"$gte",setter:function(t){return{$gte:t}},export:function(t){return{$gte:t.value.$gte}},base:{title:"Above or equals",type:"number"},date:{title:"Is at least",type:"date"}},{id:"$lt",setter:function(t){return{$lt:t}},export:function(t){return{$lt:t.value.$lt}},base:{title:"Below",type:"number"},date:{title:"Is before",type:"date"}},{id:"$lte",setter:function(t){return{$lt:t}},export:function(t){return{$lte:t.value.$lte}},base:{title:"Below or equals",type:"number"},date:{title:"Is at most",type:"date"}},{id:"$exists",setter:function(t){return{$exists:!!t}},export:function(t){return{$exists:t.value.$exists}},base:{title:"Has a value",type:"boolean",textTrue:"Has a value",textFalse:"Has a value"}},{id:"$regexp",setter:function(t){return{$regexp:t}},export:function(t){return{$regexp:t.value.$regexp}},base:{title:"Matches",type:"string"}}],n.operandsByID=_.mapKeys(n.operands,"id"),n.getSpec=function(t,e,a){return n.spec[a]?n.spec[a]:"$and"==t||"$or"==t?_defineProperty({type:"group"},"type",t):_.isString(e)?{type:"string"}:_.isNumber(e)?{type:"number"}:{type:"string"}},n.translateBranch=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return _(n.branch).map(function(t,a){return{id:a,value:t,valueEdit:n.getFlatValue(t),valueOperand:_.isObject(t)?_(t).keys().first():"$eq",isMeta:a.startsWith("$")||["sort","skip","limit"].includes(a),spec:n.getSpec(a,t,a),path:e.concat([a])}}).sortBy(function(t){return t.isMeta?"Z"+t.id:"A"+t.id}).value()},n.exportBranch=function(){n.branch=_(n.properties).mapKeys(function(t){return t.id}).mapValues(function(t){return n.operandsByID[t.valueOperand].export(t)}).value()},n.properties,e.$watchGroup(["$ctrl.branch","$ctrl.spec"],function(){n.branch&&n.spec&&(n.properties=n.translateBranch(n.branch))}),n.setField=function(t,e){t.id=e,t.path=[e],t.value=void 0,t.valueEdit=void 0,t.valueOperand="$eq",t.spec=n.spec[e],n.setValue(t)},n.setWrapper=function(t,e){if("$eq"==t.valueOperand&&"$ne"==e)t.valueOperand="$ne",t.valueEdit=n.getFlatValue(t.value),t.value={$ne:t.valueEdit};else if("$ne"==t.valueOperand&&"$eq"==e)t.valueOperand="$eq",t.valueEdit=n.getFlatValue(t.value),t.value={$eq:t.valueEdit};else if("$in"==t.valueOperand&&"$eq"==e)t.valueOperand="$eq",t.value=t.valueEdit=n.getFlatValue(t.value);else if("$eq"!=t.valueOperand&&void 0!==t.valueOperand||"$in"!=e)if("$exists"==e)t.valueOperand="$exists",t.valueEdit=!0,t.value={$exists:t.valueEdit};else{console.log("UNHANDLED TYPE CONVERT:",t.type,"=>",e);var a=n.getFlatValue(t.value);t.valueOperand=e,t.valueEdit=a,t.value=_defineProperty({},t.valueOperand,t.valueEdit)}else t.valueOperand="$in",t.valueEdit=n.getFlatValue(t.value),t.value={$in:[t.valueEdit]};n.exportBranch()},n.setValue=function(t,e){var a=_.isUndefined(e)?t.valueEdit:e;t.value=n.operandsByID[t.valueOperand].setter(a),t.valueEdit=n.getFlatValue(t.value),n.exportBranch()},n.setValueIncluded=function(t,e,n){var a=_(t.value).keys().first();if(!a)throw new Error("Tried to set array inclusion on non wrapped key: "+t.value);var l=t.value[a].includes(e);n&&!l?t.value[a].push(e):!n&&l&&(t.value[a]=t.value[a].filter(function(t){return t!=e})),t.value[a].sort(),t.valueEdit=_.isObject(t.value)&&_.size(t.value)?_(t.value).map().first():t.value},n.getFlatValue=function(t){return _.isString(t)||_.isNumber(t)||_.isBoolean(t)||_.isDate(t)?t:_.isObject(t)&&1==_.size(t)?_(t).values().first():(console.warn("Given up trying to flatten input value",t),t)},n.add=function(){if(!n.properties.some(function(t){return!t.id})){n.properties.push({});e.$on("uiQueryQueryRepaint",function(){t.find(".query-block > .new").addClass("open")})}},n.remove=function(t){n.properties=n.properties.filter(function(e){return e.id!=t}),n.exportBranch()}}]}).filter("uiQueryBuilderFilterSelected",function(){return function(t,e,n){if(t)return t.filter(function(t){var a=e.valueEdit.includes(t.id);return n?!a:a})}}).directive("ngRepeatEmit",["$rootScope","$timeout",function(t,e){return{restrict:"A",link:function(t,n,a){!0===t.$last&&e(function(){return t.$emit(a.ngRepeatEmit)})}}}]),angular.module("angular-ui-query-builder").service("qbTableSettings",function(){return{icons:{sortNone:"fa fa-fw fa-sort text-muted",sortAsc:"fa fa-fw fa-sort-alpha-asc text-primary",sortDesc:"fa fa-fw fa-sort-alpha-desc text-primary"}}}).directive("qbTable",function(){return{scope:{qbTable:"="},restrict:"AC",controller:["$scope","qbTableSettings",function(t,e){var n=this;n.query=t.qbTable,n.setField=function(e,a){if(void 0!=a)switch(e){case"sort":n.query.sort===a?n.query.sort="-"+a:(n.query.sort,n.query.sort=a);break;default:t.qbTable[e]=a}else delete n.query[e]}}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,n){var a=this;e.qbTableSettings=n,e.canSort=!1,e.isSorted=!1,a.$onInit=function(){e.canSort=e.sortable||""===t.sortable},e.$watch("qbTable.query.sort",function(t){var n=e.sortable||e.q;t?angular.isArray(t)&&t.some(function(t){return t==n})||t==n?e.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+n})||t=="-"+n?e.isSorted="desc":e.isSorted=!1:e.isSorted=!1}),e.toggleSort=function(){e.sortable?e.qbTable.setField("sort",e.sortable):e.q&&""===t.sortable&&e.qbTable.setField("sort",e.q)}}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">\n\t\t\t<i class="{{\n\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t}}"></i>\n\t\t</a>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",controller:["$attrs","$scope","qbTableSettings",function(t,e,n){e.qbTableSettings=n,e.canPrev=!0,e.canNext=!0,e.$watchGroup(["qbTable.query.limit","qbTable.query.skip"],function(t){e.canPrev=e.qbTable.query.skip>0,e.canNext=!e.total||e.qbTable.query.skip+e.qbTable.query.limit<e.total}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.min((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0));else{if(1!=t)throw new Error("Unsupported page move: "+t);e.qbTable.setField("skip",(e.qbTable.query.skip||0)+(e.qbTable.query.limit||10),0)}}}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i class="fa fa-arrow-left"></i></a></li>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i class="fa fa-arrow-right"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'}});
"use strict";function _defineProperty(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}angular.module("angular-ui-query-builder",[]).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\n\t\t\t\x3c!-- Meta field: sort {{{ --\x3e\n\t\t\t<div class="query-container">\n\t\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tSort by\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.sort" type="text" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Meta field: limit {{{ --\x3e\n\t\t\t<div class="query-container">\n\t\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tLimited to\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.limit" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-1">\n\t\t\t\t\t\tSkipping\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.skip" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\n\t\t</div>\n\t',controller:["$scope",function(t){var e=this;t.$watch("$ctrl.spec",function(){_.forEach(e.spec,function(t,e){t.title||(t.title=_.startCase(e)),t.enum&&_.isArray(t.enum)&&(t.enum=_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value())})})}]}).component("uiQueryBuilderBranch",{bindings:{branch:"=",spec:"<"},template:'\n\t\t<div ng-repeat="leaf in $ctrl.properties | filter:{isMeta:false} track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query type component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Is after</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gte\')">Is at least</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Is before</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lte\')">Is at most</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="date" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="date" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t\t\x3c!-- Add button {{{ --\x3e\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t\x3c!-- }}} --\x3e\n\t',controller:["$element","$scope",function(t,e){var n=this;n.operands=[{id:"$eq",setter:function(t){return{$eq:t}},export:function(t){return t.valueEdit},base:{title:"Is",type:"string"},boolean:{title:"Is",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"},date:{title:"Is exactly",type:"date"}},{id:"$ne",setter:function(t){return{$ne:t}},export:function(t){return{$ne:t.valueEdit}},base:{title:"Is not",type:"string"},boolean:{title:"Is not",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"},date:{title:"Is not exactly",type:"date"}},{id:"$in",setter:function(t){return{$in:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$in:t.value.$in}},base:{title:"One of",type:"array"}},{id:"$nin",setter:function(t){return{$nin:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$nin:t.value.$nin}},base:{title:"Not one of",type:"array"}},{id:"$gt",setter:function(t){return{$gt:t}},export:function(t){return{$gt:t.value.$gt}},base:{title:"Above",type:"number"},date:{title:"Is after",type:"date"}},{id:"$gte",setter:function(t){return{$gte:t}},export:function(t){return{$gte:t.value.$gte}},base:{title:"Above or equals",type:"number"},date:{title:"Is at least",type:"date"}},{id:"$lt",setter:function(t){return{$lt:t}},export:function(t){return{$lt:t.value.$lt}},base:{title:"Below",type:"number"},date:{title:"Is before",type:"date"}},{id:"$lte",setter:function(t){return{$lt:t}},export:function(t){return{$lte:t.value.$lte}},base:{title:"Below or equals",type:"number"},date:{title:"Is at most",type:"date"}},{id:"$exists",setter:function(t){return{$exists:!!t}},export:function(t){return{$exists:t.value.$exists}},base:{title:"Has a value",type:"boolean",textTrue:"Has a value",textFalse:"Has a value"}},{id:"$regexp",setter:function(t){return{$regexp:t}},export:function(t){return{$regexp:t.value.$regexp}},base:{title:"Matches",type:"string"}}],n.operandsByID=_.mapKeys(n.operands,"id"),n.getSpec=function(t,e,a){return n.spec[a]?n.spec[a]:"$and"==t||"$or"==t?_defineProperty({type:"group"},"type",t):_.isString(e)?{type:"string"}:_.isNumber(e)?{type:"number"}:{type:"string"}},n.translateBranch=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return _(n.branch).map(function(t,a){return{id:a,value:t,valueEdit:n.getFlatValue(t),valueOperand:_.isObject(t)?_(t).keys().first():"$eq",isMeta:a.startsWith("$")||["sort","skip","limit"].includes(a),spec:n.getSpec(a,t,a),path:e.concat([a])}}).sortBy(function(t){return t.isMeta?"Z"+t.id:"A"+t.id}).value()},n.exportBranch=function(){n.branch=_(n.properties).mapKeys(function(t){return t.id}).mapValues(function(t){return n.operandsByID[t.valueOperand].export(t)}).value()},n.properties,e.$watchGroup(["$ctrl.branch","$ctrl.spec"],function(){n.branch&&n.spec&&(n.properties=n.translateBranch(n.branch))}),n.setField=function(t,e){t.id=e,t.path=[e],t.value=void 0,t.valueEdit=void 0,t.valueOperand="$eq",t.spec=n.spec[e],n.setValue(t)},n.setWrapper=function(t,e){if("$eq"==t.valueOperand&&"$ne"==e)t.valueOperand="$ne",t.valueEdit=n.getFlatValue(t.value),t.value={$ne:t.valueEdit};else if("$ne"==t.valueOperand&&"$eq"==e)t.valueOperand="$eq",t.valueEdit=n.getFlatValue(t.value),t.value={$eq:t.valueEdit};else if("$in"==t.valueOperand&&"$eq"==e)t.valueOperand="$eq",t.value=t.valueEdit=n.getFlatValue(t.value);else if("$eq"!=t.valueOperand&&void 0!==t.valueOperand||"$in"!=e)if("$exists"==e)t.valueOperand="$exists",t.valueEdit=!0,t.value={$exists:t.valueEdit};else{console.log("UNHANDLED TYPE CONVERT:",t.type,"=>",e);var a=n.getFlatValue(t.value);t.valueOperand=e,t.valueEdit=a,t.value=_defineProperty({},t.valueOperand,t.valueEdit)}else t.valueOperand="$in",t.valueEdit=n.getFlatValue(t.value),t.value={$in:[t.valueEdit]};n.exportBranch()},n.setValue=function(t,e){var a=_.isUndefined(e)?t.valueEdit:e;t.value=n.operandsByID[t.valueOperand].setter(a),t.valueEdit=n.getFlatValue(t.value),n.exportBranch()},n.setValueIncluded=function(t,e,n){var a=_(t.value).keys().first();if(!a)throw new Error("Tried to set array inclusion on non wrapped key: "+t.value);var l=t.value[a].includes(e);n&&!l?t.value[a].push(e):!n&&l&&(t.value[a]=t.value[a].filter(function(t){return t!=e})),t.value[a].sort(),t.valueEdit=_.isObject(t.value)&&_.size(t.value)?_(t.value).map().first():t.value},n.getFlatValue=function(t){return _.isString(t)||_.isNumber(t)||_.isBoolean(t)||_.isDate(t)?t:_.isObject(t)&&1==_.size(t)?_(t).values().first():(console.warn("Given up trying to flatten input value",t),t)},n.add=function(){if(!n.properties.some(function(t){return!t.id})){n.properties.push({});e.$on("uiQueryQueryRepaint",function(){t.find(".query-block > .new").addClass("open")})}},n.remove=function(t){n.properties=n.properties.filter(function(e){return e.id!=t}),n.exportBranch()}}]}).filter("uiQueryBuilderFilterSelected",function(){return function(t,e,n){if(t)return t.filter(function(t){var a=e.valueEdit.includes(t.id);return n?!a:a})}}).directive("ngRepeatEmit",["$rootScope","$timeout",function(t,e){return{restrict:"A",link:function(t,n,a){!0===t.$last&&e(function(){return t.$emit(a.ngRepeatEmit)})}}}]),angular.module("angular-ui-query-builder").service("qbTableSettings",function(){return{icons:{sortNone:"fa fa-fw fa-sort text-muted",sortAsc:"fa fa-fw fa-sort-alpha-asc text-primary",sortDesc:"fa fa-fw fa-sort-alpha-desc text-primary"}}}).directive("qbTable",function(){return{scope:{qbTable:"=",stickyThead:"<",stickyTfoot:"<"},restrict:"AC",controller:["$attrs","$element","$scope","qbTableSettings",function(t,e,n,a){var l=this;l.query=n.qbTable,l.setField=function(t,e){if(void 0!=e)switch(t){case"sort":l.query.sort===e?l.query.sort="-"+e:(l.query.sort,l.query.sort=e);break;default:n.qbTable[t]=e}else delete l.query[t]},e.addClass("qb-table"),n.$watch("stickyThead",function(){return e.toggleClass("qb-sticky-thead",n.stickyThead||""===t.stickyThead)}),n.$watch("stickyTfoot",function(){return e.toggleClass("qb-sticky-tfoot",n.stickyTfoot||""===t.stickyTfoot)})}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,n){var a=this;e.qbTableSettings=n,e.canSort=!1,e.isSorted=!1,a.$onInit=function(){return e.canSort=e.sortable||""===t.sortable},e.$watch("qbTable.query.sort",function(t){var n=e.sortable||e.q;t?angular.isArray(t)&&t.some(function(t){return t==n})||t==n?e.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+n})||t=="-"+n?e.isSorted="desc":e.isSorted=!1:e.isSorted=!1}),e.toggleSort=function(){e.sortable?e.qbTable.setField("sort",e.sortable):e.qbCol&&""===t.sortable&&e.qbTable.setField("sort",e.qbCol)}}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">\n\t\t\t<i class="{{\n\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t}}"></i>\n\t\t</a>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",controller:["$attrs","$scope","qbTableSettings",function(t,e,n){e.qbTableSettings=n,e.canPrev=!0,e.canNext=!0,e.$watchGroup(["qbTable.query.limit","qbTable.query.skip"],function(t){e.canPrev=e.qbTable.query.skip>0,e.canNext=!e.total||e.qbTable.query.skip+e.qbTable.query.limit<e.total}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.min((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0));else{if(1!=t)throw new Error("Unsupported page move: "+t);e.qbTable.setField("skip",(e.qbTable.query.skip||0)+(e.qbTable.query.limit||10),0)}}}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i class="fa fa-arrow-left"></i></a></li>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i class="fa fa-arrow-right"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'}});

@@ -5,10 +5,22 @@ var app = angular.module("app", [

app.controller("queryBuilderExampleCtrl", function($scope) {
// Force URL encoding to use jQuery syntax so we can pass JSON to the backend using URL query objects
app.config($httpProvider => $httpProvider.defaults.paramSerializer = '$httpParamSerializerJQLike')
app.controller('queryBuilderExampleCtrl', function($http, $scope) {
$scope.spec = {
_id: {type: 'objectId'},
lastLogin: {type: 'date'},
status: {type: 'string', enum: ['pending', 'active', 'approved', 'deleted']},
role: {type: 'string', enum: ['user', 'admin', 'root']},
name: {type: 'string'},
username: {type: 'string'},
email: {type: 'string'},
'address.street': {type: 'string'},
'address.city': {type: 'string'},
'address.zip': {type: 'string'},
'address.state': {type: 'string'},
'address.country': {type: 'string'},
phone: {type: 'string'},
website: {type: 'string'},
'company.name': {type: 'string'},
role: {type: 'string', enum: ['user', 'admin', 'root']},
status: {type: 'string', enum: ['pending', 'active', 'deleted']},
lastLogin: {type: 'date'},
};

@@ -18,441 +30,17 @@

email: {$exists: true},
role: 'admin',
status: {$in: ['active', 'approved']},
/* FIXME: Not yet supported
$and: [
{role: 'admin'},
{role: 'user', $exists: {email: true}},
],
*/
role: 'user',
status: {$in: ['pending', 'active']},
lastLogin: {$lt: moment().subtract(2, 'd').toDate()},
sort: 'username',
limit: 10,
};
$scope.data;
$scope.$watch('query', ()=> {
// console.log('REFRESH', $scope.query);
$http.get('data.json', {params: $scope.query})
.then(res => $scope.data = res.data)
}, true);
// FIXME: Rockjaw scenario
$scope.query =
{
"cancelled": false,
"status": {
"$in": [
"draft",
"assignedLotNumber",
"opened",
"pendingClosureOnTestResults",
"lotCompletionNotification"
]
},
"workPack": "1234",
};
$scope.spec =
{
"_id": {
"type": "objectid",
"title": "Id"
},
"area": {
"type": "string",
"title": "Area"
},
"areaDescription": {
"type": "string",
"title": "Area Description"
},
"assets": {
"type": "array",
"default": "[DYNAMIC]",
"title": "Assets"
},
"cancelReason": {
"type": "string",
"title": "Cancel Reason"
},
"cancelled": {
"type": "boolean",
"title": "Cancelled"
},
"code": {
"type": "string",
"title": "Code"
},
"constructionAreaPlan": {
"type": "string",
"title": "Construction Area Plan"
},
"created": {
"type": "date",
"default": "[DYNAMIC]",
"title": "Created"
},
"createdBy": {
"type": "objectid",
"ref": "users",
"title": "Created By"
},
"description": {
"type": "string",
"title": "Description"
},
"documentationConfirm.asBuiltSurvey": {
"type": "string",
"enum": [
{
"id": "absent",
"title": "Absent"
},
{
"id": "confirmed",
"title": "Confirmed"
},
{
"id": "notApplicable",
"title": "Not Applicable"
}
],
"default": "absent",
"title": "Documentation Confirm As Built Survey"
},
"documentationConfirm.completedInspectionAndTestPlans": {
"type": "string",
"enum": [
{
"id": "absent",
"title": "Absent"
},
{
"id": "confirmed",
"title": "Confirmed"
},
{
"id": "notApplicable",
"title": "Not Applicable"
}
],
"default": "absent",
"title": "Documentation Confirm Completed Inspection And Test Plans"
},
"documentationConfirm.completedVerificationChecklist": {
"type": "string",
"enum": [
{
"id": "absent",
"title": "Absent"
},
{
"id": "confirmed",
"title": "Confirmed"
},
{
"id": "notApplicable",
"title": "Not Applicable"
}
],
"default": "absent",
"title": "Documentation Confirm Completed Verification Checklist"
},
"documentationConfirm.inspectionReports": {
"type": "string",
"enum": [
{
"id": "absent",
"title": "Absent"
},
{
"id": "confirmed",
"title": "Confirmed"
},
{
"id": "notApplicable",
"title": "Not Applicable"
}
],
"default": "absent",
"title": "Documentation Confirm Inspection Reports"
},
"documentationConfirm.materialTestReports": {
"type": "string",
"enum": [
{
"id": "absent",
"title": "Absent"
},
{
"id": "confirmed",
"title": "Confirmed"
},
{
"id": "notApplicable",
"title": "Not Applicable"
}
],
"default": "absent",
"title": "Documentation Confirm Material Test Reports"
},
"documentationConfirm.other": {
"type": "string",
"enum": [
{
"id": "absent",
"title": "Absent"
},
{
"id": "confirmed",
"title": "Confirmed"
},
{
"id": "notApplicable",
"title": "Not Applicable"
}
],
"default": "absent",
"title": "Documentation Confirm Other"
},
"documentationConfirm.subContractorRecords": {
"type": "string",
"enum": [
{
"id": "absent",
"title": "Absent"
},
{
"id": "confirmed",
"title": "Confirmed"
},
{
"id": "notApplicable",
"title": "Not Applicable"
}
],
"default": "absent",
"title": "Documentation Confirm Sub Contractor Records"
},
"documentationConfirm.supplierRecords": {
"type": "string",
"enum": [
{
"id": "absent",
"title": "Absent"
},
{
"id": "confirmed",
"title": "Confirmed"
},
{
"id": "notApplicable",
"title": "Not Applicable"
}
],
"default": "absent",
"title": "Documentation Confirm Supplier Records"
},
"documents": {
"type": "array",
"default": "[DYNAMIC]",
"title": "Documents"
},
"edited": {
"type": "date",
"title": "Edited"
},
"editedBy": {
"type": "objectid",
"ref": "users",
"title": "Edited By"
},
"holdWitnessPoints": {
"type": "array",
"default": "[DYNAMIC]",
"title": "Hold Witness Points"
},
"lot.cableType": {
"type": "string",
"enum": [
{
"id": "commsCabinet",
"title": "Comms Cabinet"
},
{
"id": "none",
"title": "None"
},
{
"id": "sourceLocation",
"title": "Source Location"
}
],
"default": "sourceLocation",
"title": "Lot Cable Type"
},
"lot.chainageFrom": {
"type": "number",
"title": "Lot Chainage From"
},
"lot.chainageTo": {
"type": "number",
"title": "Lot Chainage To"
},
"lot.controlLine": {
"title": "Lot Control Line"
},
"lot.designLot": {
"type": "string",
"title": "Lot Design Lot"
},
"lot.location": {
"title": "Lot Location"
},
"lot.type": {
"type": "string",
"enum": [
{
"id": "linear",
"title": "Linear"
},
{
"id": "location",
"title": "Location"
},
{
"id": "none",
"title": "None"
}
],
"default": "linear",
"title": "Lot Type"
},
"ncr": {
"type": "objectid",
"ref": "ncrs",
"title": "Ncr"
},
"process": {
"type": "string",
"title": "Process"
},
"processDescription": {
"type": "string",
"title": "Process Description"
},
"responsibleEngineer": {
"type": "objectid",
"ref": "users",
"title": "Responsible Engineer"
},
"reviewedStatus": {
"type": "string",
"enum": [
{
"id": "ICAccepted",
"title": "IC Accepted"
},
{
"id": "ICIssued",
"title": "IC Issued"
},
{
"id": "ICReviewed",
"title": "IC Reviewed"
},
{
"id": "notReviewed",
"title": "Not Reviewed"
}
],
"default": "notReviewed",
"title": "Reviewed Status"
},
"status": {
"type": "string",
"enum": [
{
"id": "assignedLotNumber",
"title": "Assigned Lot Number"
},
{
"id": "closed",
"title": "Closed"
},
{
"id": "draft",
"title": "Draft"
},
{
"id": "lotCompletionNotification",
"title": "Lot Completion Notification"
},
{
"id": "opened",
"title": "Opened"
},
{
"id": "pendingClosureOnTestResults",
"title": "Pending Closure On Test Results"
}
],
"default": "draft",
"title": "Status"
},
"subProcess": {
"type": "string",
"title": "Sub Process"
},
"subProcessDescription": {
"type": "string",
"title": "Sub Process Description"
},
"type": {
"type": "string",
"enum": [
{
"id": "assets",
"title": "Assets"
},
{
"id": "cables",
"title": "Cables"
}
],
"default": "assets",
"title": "Type"
},
"workPack": {
"type": "string",
"title": "Work Pack"
},
"workType": {
"type": "string",
"enum": [
{
"id": "permanent",
"title": "Permanent"
},
{
"id": "temporary",
"title": "Temporary"
}
],
"default": "permanent",
"title": "Work Type"
},
"$prototype": {
"documentationConfirm": {
"asBuiltSurvey": "absent",
"completedInspectionAndTestPlans": "absent",
"completedVerificationChecklist": "absent",
"inspectionReports": "absent",
"materialTestReports": "absent",
"other": "absent",
"subContractorRecords": "absent",
"supplierRecords": "absent"
},
"lot": {
"cableType": "sourceLocation",
"type": "linear"
},
"reviewedStatus": "notReviewed",
"status": "draft",
"type": "assets",
"workType": "permanent",
"title": "Prototype"
}
};
$scope.isGitHub = /\.github.io$/.test(document.location.hostname);
});

@@ -13,3 +13,3 @@ 'use strict';

},
template: '\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\t\t</div>\n\t',
template: '\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\n\t\t\t<!-- Meta field: sort {{{ -->\n\t\t\t<div class="query-container">\n\t\t\t\t<!-- Root branch display {{{ -->\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<!-- }}} -->\n\t\t\t\t<!-- Path component {{{ -->\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tSort by\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- }}} -->\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<!-- Query operand component {{{ -->\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.sort" type="text" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- }}} -->\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Meta field: limit {{{ -->\n\t\t\t<div class="query-container">\n\t\t\t\t<!-- Root branch display {{{ -->\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<!-- }}} -->\n\t\t\t\t<!-- Path component {{{ -->\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tLimited to\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- }}} -->\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<!-- Query operand component {{{ -->\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.limit" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-1">\n\t\t\t\t\t\tSkipping\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.skip" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<!-- }}} -->\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\n\t\t</div>\n\t',
controller: ['$scope', function controller($scope) {

@@ -47,3 +47,3 @@ var $ctrl = this;

},
template: '\n\t\t<div ng-repeat="leaf in $ctrl.properties track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t<!-- Root branch display {{{ -->\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Path component {{{ -->\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t<!-- Query type component {{{ -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">After</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Before</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t<!-- Query operand component {{{ -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t</div>\n\t\t<!-- Add button {{{ -->\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t<!-- }}} -->\n\t',
template: '\n\t\t<div ng-repeat="leaf in $ctrl.properties | filter:{isMeta:false} track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<!-- Path component {{{ -->\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t<!-- Query type component {{{ -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Is after</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gte\')">Is at least</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Is before</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lte\')">Is at most</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t<!-- Query operand component {{{ -->\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="date" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="date" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t</div>\n\t\t<!-- Add button {{{ -->\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t<!-- }}} -->\n\t',
controller: ['$element', '$scope', function controller($element, $scope) {

@@ -92,2 +92,6 @@ var $ctrl = this;

textFalse: 'Disabled'
},
date: {
title: 'Is exactly',
type: 'date'
}

@@ -100,3 +104,3 @@ }, {

export: function _export(leaf) {
return leaf.valueEdit;
return { $ne: leaf.valueEdit };
},

@@ -112,2 +116,6 @@ base: {

textFalse: 'Disabled'
},
date: {
title: 'Is not exactly',
type: 'date'
}

@@ -149,2 +157,6 @@ }, {

type: 'number'
},
date: {
title: 'Is after',
type: 'date'
}

@@ -162,2 +174,6 @@ }, {

type: 'number'
},
date: {
title: 'Is at least',
type: 'date'
}

@@ -175,2 +191,6 @@ }, {

type: 'number'
},
date: {
title: 'Is before',
type: 'date'
}

@@ -188,2 +208,6 @@ }, {

type: 'number'
},
date: {
title: 'Is at most',
type: 'date'
}

@@ -202,3 +226,3 @@ }, {

textTrue: 'Has a value',
textFalse: 'Has no value'
textFalse: 'Has a value' // This isn't technically right but its right next to a disabled checkbox so it makes sense in context
}

@@ -249,16 +273,11 @@ }, {

return _($ctrl.branch).map(function (v, k) {
var wrappingKey = _.isObject(v) ? _(v).keys().first() : '$eq';
var firstKeyVal = _.isObject(v) && _.size(v) > 0 ? _(v).map().first() : undefined;
var newBranch = {
return {
id: k,
value: v,
valueEdit: firstKeyVal || v,
valueOperand: wrappingKey,
isMeta: k.startsWith('$'),
valueEdit: $ctrl.getFlatValue(v),
valueOperand: _.isObject(v) ? _(v).keys().first() : '$eq',
isMeta: k.startsWith('$') || ['sort', 'skip', 'limit'].includes(k),
spec: $ctrl.getSpec(k, v, k),
path: pathSegments.concat([k])
};
return newBranch;
}).sortBy(function (p) {

@@ -305,15 +324,37 @@ return p.isMeta ? 'Z' + p.id : 'A' + p.id;

$ctrl.setWrapper = function (leaf, type) {
var newValue = {};
if (_.isObject(leaf.value) && _.size(leaf.value) == 1) {
// Unwrap object value
newValue[type] = _(leaf.value).values().first();
if (leaf.valueOperand == '$eq' && type == '$ne') {
// Negate
leaf.valueOperand = '$ne';
leaf.valueEdit = $ctrl.getFlatValue(leaf.value);
leaf.value = { $ne: leaf.valueEdit };
} else if (leaf.valueOperand == '$ne' && type == '$eq') {
leaf.valueOperand = '$eq';
leaf.valueEdit = $ctrl.getFlatValue(leaf.value);
leaf.value = { $eq: leaf.valueEdit };
} else if (leaf.valueOperand == '$in' && type == '$eq') {
// Flatten array into scalar
leaf.valueOperand = '$eq';
leaf.value = leaf.valueEdit = $ctrl.getFlatValue(leaf.value);
} else if ((leaf.valueOperand == '$eq' || leaf.valueOperand === undefined) && type == '$in') {
// Roll scalar into array
leaf.valueOperand = '$in';
leaf.valueEdit = $ctrl.getFlatValue(leaf.value);
leaf.value = { $in: [leaf.valueEdit] };
} else if (type == '$exists') {
// Convert anything to exists - force it to be a boolean
leaf.valueOperand = '$exists';
leaf.valueEdit = true;
leaf.value = { $exists: leaf.valueEdit };
} else {
// Preseve value
newValue[type] = leaf.valueEdit;
// Unknown swapping - convert to an object with one key
console.log('UNHANDLED TYPE CONVERT:', leaf.type, '=>', type);
var newValue = $ctrl.getFlatValue(leaf.value);
leaf.valueOperand = type;
leaf.valueEdit = newValue;
leaf.value = _defineProperty({}, leaf.valueOperand, leaf.valueEdit);
}
leaf.valueOperand = type;
leaf.value = newValue;
leaf.valueEdit = _.isObject(newValue[type]) && _.size(newValue[type]) ? newValue[type] : newValue;
$ctrl.setValue(leaf);
// Set the upstream model value
$ctrl.exportBranch();
};

@@ -331,3 +372,3 @@

leaf.value = $ctrl.operandsByID[leaf.valueOperand].setter(newValue);
leaf.valueEdit = _.isObject(leaf.value) && _.size(leaf.value) ? _(leaf.value).map().first() : leaf.value;
leaf.valueEdit = $ctrl.getFlatValue(leaf.value);

@@ -363,2 +404,29 @@ // Set the upstream model value

};
/**
* Return the 'flat' value of a Mongo expression
* This will always return the closest thing we have to a scalar primative
* @param {Object|string} input The input expression to flatten
* @returns {string|number} The nearest thing we can evaluate to a primative (or an empty string)
*
* @example
* $ctrl.getFlatValue('foo') //= 'foo'
* @example
* $ctrl.getFlatValue({$eq: 'bar'}) //= 'bar'
* @example
* $ctrl.getFlatValue({$in: ['quz', 'qux']}) //= 'quz'
*/
$ctrl.getFlatValue = function (input) {
if (_.isString(input) || _.isNumber(input) || _.isBoolean(input) || _.isDate(input)) {
// Already a primative
return input;
} else if (_.isObject(input) && _.size(input) == 1) {
// Unwrap object value from object
return _(input).values().first();
} else {
// No idea how to convert - just return an empty string
console.warn('Given up trying to flatten input value', input);
return input;
}
};
// }}}

@@ -425,2 +493,179 @@

}]);
// }}}
angular.module('angular-ui-query-builder')
// qbTableSettings (service) {{{
.service('qbTableSettings', function () {
return {
icons: {
sortNone: 'fa fa-fw fa-sort text-muted',
sortAsc: 'fa fa-fw fa-sort-alpha-asc text-primary',
sortDesc: 'fa fa-fw fa-sort-alpha-desc text-primary'
}
};
})
// }}}
// qbTable (directive) {{{
/**
* Directive applied to a table element to indicate that we should manage that table via angular-ui-query
* @param {Object} qbTable The query object to modify
* @param {boolean} stickyThead Anything within the `thead` section of the table should remain on the screen while scrolling
* @param {boolean} stickyTfoot Anything within the `tfoot` section of the table should remain on the screen while scrolling
* @emits qbTableQueryChange Emitted to child elements as (e, query) when the query object changes
*/
.directive('qbTable', function () {
return {
scope: {
qbTable: '=',
stickyThead: '<',
stickyTfoot: '<'
},
restrict: 'AC',
controller: ['$attrs', '$element', '$scope', 'qbTableSettings', function controller($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;
$ctrl.query = $scope.qbTable; // Copy into $ctrl so children can access it / $watch it
$ctrl.setField = function (field, value) {
if (value == undefined) {
// Remove from query
delete $ctrl.query[field];
return;
}
switch (field) {
case 'sort':
if ($ctrl.query.sort === value) {
// If already sorting by field switch the sort direction
$ctrl.query.sort = '-' + value;
} else if ($ctrl.query.sort === '-' + value) {
// If reverse sorting switch the right way up again
$ctrl.query.sort = value;
} else {
// Just set the sorting
$ctrl.query.sort = value;
}
break;
default:
$scope.qbTable[field] = value;
}
};
$element.addClass('qb-table');
$scope.$watch('stickyThead', function () {
return $element.toggleClass('qb-sticky-thead', $scope.stickyThead || $attrs.stickyThead === '');
});
$scope.$watch('stickyTfoot', function () {
return $element.toggleClass('qb-sticky-tfoot', $scope.stickyTfoot || $attrs.stickyTfoot === '');
});
}]
};
})
// }}}
// qbCol (directive) {{{
/**
* Directive for header elements to add angular-ui-query functionality
* @param {Object} ^qbTable.qbTable The query Object to mutate
* @param {string} qbCol The field to operate on
* @param {string} [sortable=q] Indicates that the column should switch to being sorted if the user clicks on it, if a value is specified that is used instead of `q` as the sort field
*/
.directive('qbCol', function () {
return {
scope: {
qbCol: '@', // The field to operate on
sortable: '@'
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
// Sort functionality {{{
$scope.canSort = false; // True if either sortable has a specific value or is at least present
$scope.isSorted = false; // False, 'asc', 'desc'
$ctrl.$onInit = function () {
return $scope.canSort = $scope.sortable || $attrs.sortable === '';
};
$scope.$watch('qbTable.query.sort', function (sorter) {
var sortField = $scope.sortable || $scope.q;
if (!sorter) {
$scope.isSorted = false;
} else if (angular.isArray(sorter) && sorter.some(function (i) {
return i == sortField;
}) || sorter == sortField) {
$scope.isSorted = 'asc';
} else if (angular.isArray(sorter) && sorter.some(function (i) {
return i == '-' + sortField;
}) || sorter == '-' + sortField) {
$scope.isSorted = 'desc';
} else {
$scope.isSorted = false;
}
});
$scope.toggleSort = function () {
if ($scope.sortable) {
// Sort by a specific field
$scope.qbTable.setField('sort', $scope.sortable);
} else if ($scope.qbCol && $attrs.sortable === '') {
// Has attribute but no value - assume main key if we have one
$scope.qbTable.setField('sort', $scope.qbCol);
}
};
// }}}
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<ng-transclude></ng-transclude>\n\t\t<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">\n\t\t\t<i class="{{\n\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t}}"></i>\n\t\t</a>\n\t'
};
})
// }}}
// qbPagination {{{
/**
* Directive to add table pagination
* @param {Object} ^qbTable.qbTable The query Object to mutate
*/
.directive('qbPagination', function () {
return {
scope: {},
require: '^qbTable',
restrict: 'EA',
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$scope.canPrev = true;
$scope.canNext = true;
$scope.$watchGroup(['qbTable.query.limit', 'qbTable.query.skip'], function (sorter) {
$scope.canPrev = $scope.qbTable.query.skip > 0;
$scope.canNext = !$scope.total || $scope.qbTable.query.skip + $scope.qbTable.query.limit < $scope.total;
});
$scope.navPageRelative = function (pageRelative) {
if (pageRelative == -1) {
$scope.qbTable.setField('skip', Math.min(($scope.qbTable.query.skip || 0) - ($scope.qbTable.query.limit || 10), 0));
} else if (pageRelative == 1) {
$scope.qbTable.setField('skip', ($scope.qbTable.query.skip || 0) + ($scope.qbTable.query.limit || 10), 0);
} else {
throw new Error('Unsupported page move: ' + pageRelative);
}
};
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i class="fa fa-arrow-left"></i></a></li>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i class="fa fa-arrow-right"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'
};
});
// }}}

@@ -1,1 +0,1 @@

"use strict";function _defineProperty(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}angular.module("angular-ui-query-builder",[]).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\t\t</div>\n\t',controller:["$scope",function(t){var e=this;t.$watch("$ctrl.spec",function(){_.forEach(e.spec,function(t,e){t.title||(t.title=_.startCase(e)),t.enum&&_.isArray(t.enum)&&(t.enum=_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value())})})}]}).component("uiQueryBuilderBranch",{bindings:{branch:"=",spec:"<"},template:'\n\t\t<div ng-repeat="leaf in $ctrl.properties track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query type component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">After</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Before</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t\t\x3c!-- Add button {{{ --\x3e\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t\x3c!-- }}} --\x3e\n\t',controller:["$element","$scope",function(t,e){var n=this;n.operands=[{id:"$eq",setter:function(t){return{$eq:t}},export:function(t){return t.valueEdit},base:{title:"Is",type:"string"},boolean:{title:"Is",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"}},{id:"$ne",setter:function(t){return{$ne:t}},export:function(t){return t.valueEdit},base:{title:"Is not",type:"string"},boolean:{title:"Is not",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"}},{id:"$in",setter:function(t){return{$in:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$in:t.value.$in}},base:{title:"One of",type:"array"}},{id:"$nin",setter:function(t){return{$nin:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$nin:t.value.$nin}},base:{title:"Not one of",type:"array"}},{id:"$gt",setter:function(t){return{$gt:t}},export:function(t){return{$gt:t.value.$gt}},base:{title:"Above",type:"number"}},{id:"$gte",setter:function(t){return{$gte:t}},export:function(t){return{$gte:t.value.$gte}},base:{title:"Above or equals",type:"number"}},{id:"$lt",setter:function(t){return{$lt:t}},export:function(t){return{$lt:t.value.$lt}},base:{title:"Below",type:"number"}},{id:"$lte",setter:function(t){return{$lt:t}},export:function(t){return{$lte:t.value.$lte}},base:{title:"Below or equals",type:"number"}},{id:"$exists",setter:function(t){return{$exists:!!t}},export:function(t){return{$exists:t.value.$exists}},base:{title:"Has a value",type:"boolean",textTrue:"Has a value",textFalse:"Has no value"}},{id:"$regexp",setter:function(t){return{$regexp:t}},export:function(t){return{$regexp:t.value.$regexp}},base:{title:"Matches",type:"string"}}],n.operandsByID=_.mapKeys(n.operands,"id"),n.getSpec=function(t,e,a){return n.spec[a]?n.spec[a]:"$and"==t||"$or"==t?_defineProperty({type:"group"},"type",t):_.isString(e)?{type:"string"}:_.isNumber(e)?{type:"number"}:{type:"string"}},n.translateBranch=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return _(n.branch).map(function(t,a){var r=_.isObject(t)?_(t).keys().first():"$eq";return{id:a,value:t,valueEdit:(_.isObject(t)&&_.size(t)>0?_(t).map().first():void 0)||t,valueOperand:r,isMeta:a.startsWith("$"),spec:n.getSpec(a,t,a),path:e.concat([a])}}).sortBy(function(t){return t.isMeta?"Z"+t.id:"A"+t.id}).value()},n.exportBranch=function(){n.branch=_(n.properties).mapKeys(function(t){return t.id}).mapValues(function(t){return n.operandsByID[t.valueOperand].export(t)}).value()},n.properties,e.$watchGroup(["$ctrl.branch","$ctrl.spec"],function(){n.branch&&n.spec&&(n.properties=n.translateBranch(n.branch))}),n.setField=function(t,e){t.id=e,t.path=[e],t.value=void 0,t.valueEdit=void 0,t.valueOperand="$eq",t.spec=n.spec[e],n.setValue(t)},n.setWrapper=function(t,e){var a={};_.isObject(t.value)&&1==_.size(t.value)?a[e]=_(t.value).values().first():a[e]=t.valueEdit,t.valueOperand=e,t.value=a,t.valueEdit=_.isObject(a[e])&&_.size(a[e])?a[e]:a,n.setValue(t)},n.setValue=function(t,e){var a=_.isUndefined(e)?t.valueEdit:e;t.value=n.operandsByID[t.valueOperand].setter(a),t.valueEdit=_.isObject(t.value)&&_.size(t.value)?_(t.value).map().first():t.value,n.exportBranch()},n.setValueIncluded=function(t,e,n){var a=_(t.value).keys().first();if(!a)throw new Error("Tried to set array inclusion on non wrapped key: "+t.value);var r=t.value[a].includes(e);n&&!r?t.value[a].push(e):!n&&r&&(t.value[a]=t.value[a].filter(function(t){return t!=e})),t.value[a].sort(),t.valueEdit=_.isObject(t.value)&&_.size(t.value)?_(t.value).map().first():t.value},n.add=function(){if(!n.properties.some(function(t){return!t.id})){n.properties.push({});e.$on("uiQueryQueryRepaint",function(){t.find(".query-block > .new").addClass("open")})}},n.remove=function(t){n.properties=n.properties.filter(function(e){return e.id!=t}),n.exportBranch()}}]}).filter("uiQueryBuilderFilterSelected",function(){return function(t,e,n){if(t)return t.filter(function(t){var a=e.valueEdit.includes(t.id);return n?!a:a})}}).directive("ngRepeatEmit",["$rootScope","$timeout",function(t,e){return{restrict:"A",link:function(t,n,a){!0===t.$last&&e(function(){return t.$emit(a.ngRepeatEmit)})}}}]);
"use strict";function _defineProperty(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}angular.module("angular-ui-query-builder",[]).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<ui-query-builder-branch\n\t\t\t\tbranch="$ctrl.query"\n\t\t\t\tspec="$ctrl.spec"\n\t\t\t></ui-query-builder-branch>\n\n\t\t\t\x3c!-- Meta field: sort {{{ --\x3e\n\t\t\t<div class="query-container">\n\t\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tSort by\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.sort" type="text" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Meta field: limit {{{ --\x3e\n\t\t\t<div class="query-container">\n\t\t\t\t\x3c!-- Root branch display {{{ --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t\t<a class="btn btn-1 btn-block">\n\t\t\t\t\t\t\tLimited to\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.limit" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-1">\n\t\t\t\t\t\tSkipping\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\t<div class="query-block btn-group">\n\t\t\t\t\t<div class="btn btn-block btn-2">\n\t\t\t\t\t\t<input ng-model="$ctrl.query.skip" type="number" class="form-control"/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t\x3c!-- }}} --\x3e\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\n\t\t</div>\n\t',controller:["$scope",function(t){var e=this;t.$watch("$ctrl.spec",function(){_.forEach(e.spec,function(t,e){t.title||(t.title=_.startCase(e)),t.enum&&_.isArray(t.enum)&&(t.enum=_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value())})})}]}).component("uiQueryBuilderBranch",{bindings:{branch:"=",spec:"<"},template:'\n\t\t<div ng-repeat="leaf in $ctrl.properties | filter:{isMeta:false} track by leaf.id" ng-switch="leaf.spec.type" ng-repeat-emit="uiQueryQueryRepaint" class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Path component {{{ --\x3e\n\t\t\t<div class="query-block">\n\t\t\t\t<div class="btn-group btn-block" ng-class="{new: !leaf.id}">\n\t\t\t\t\t<a class="btn btn-1 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<div ng-click="$ctrl.remove(leaf.id); $event.stopPropagation()" class="btn btn-trash btn-danger btn-xs pull-left">\n\t\t\t\t\t\t\t<i class="fa fa-times"></i>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{{$ctrl.spec[leaf.id].title || \'Select...\'}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="(key, val) in $ctrl.spec track by key" ng-class="key == leaf.id && \'active\'">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setField(leaf, key)">\n\t\t\t\t\t\t\t\t{{$ctrl.spec[key].title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query type component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block">\n\t\t\t\t<div class="btn-group btn-block">\n\t\t\t\t\t<a class="btn btn-2 btn-block dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t{{($ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).title}}\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</a>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$eq\')">Is</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$ne\')">Is not</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$in\')">One of</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$nin\')">Not one of</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Above</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'number\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Below</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gt\')">Is after</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$gte\')">Is at least</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lt\')">Is before</a></li>\n\t\t\t\t\t\t<li ng-if="leaf.spec.type == \'date\'"><a ng-click="$ctrl.setWrapper(leaf, \'$lte\')">Is at most</a></li>\n\t\t\t\t\t\t<li><a ng-click="$ctrl.setWrapper(leaf, \'$exists\')">Has a value</a></li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-stem"><div></div></div>\n\t\t\t\x3c!-- Query operand component {{{ --\x3e\n\t\t\t<div ng-show="leaf.valueOperand" class="query-block btn-group" ng-switch="(operandConfig = $ctrl.operandsByID[leaf.valueOperand][leaf.spec.type] || $ctrl.operandsByID[leaf.valueOperand].base).type">\n\t\t\t\t<div ng-switch-when="string" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="text" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="array" class="btn btn-block btn-3 btn-group">\n\t\t\t\t\t<div class="btn-fill text-left dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t\t\t<span class="pill" ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf track by item.id">\n\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span ng-if="!leaf.valueEdit.length">...</span>\n\t\t\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t\t\t</div>\n\t\t\t\t\t<ul class="dropdown-menu pull-right">\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:false track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, false)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-check-square text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li ng-repeat="item in $ctrl.spec[leaf.id].enum | uiQueryBuilderFilterSelected:leaf:true track by item.id">\n\t\t\t\t\t\t\t<a ng-click="$ctrl.setValueIncluded(leaf, item.id, true)">\n\t\t\t\t\t\t\t\t<i class="fa fa-fw fa-square-o text-primary"></i>\n\t\t\t\t\t\t\t\t{{item.title}}\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="boolean" class="btn btn-block btn-3" ng-click="$ctrl.setValue(leaf, !leaf.valueEdit)">\n\t\t\t\t\t<i class="fa" ng-class="leaf.valueEdit ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{leaf.valueEdit ? operandConfig.textTrue : operandConfig.textFalse}}\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-when="date" class="btn btn-block btn-3">\n\t\t\t\t\t<input ng-model="leaf.valueEdit" ng-change="$ctrl.setValue(leaf)" type="date" class="form-control"/>\n\t\t\t\t</div>\n\t\t\t\t<div ng-switch-default class="btn btn-block btn-3">\n\t\t\t\t\tUnknown operand: <code>{{leaf.valueOperand}}</code>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t\t\x3c!-- Add button {{{ --\x3e\n\t\t<div class="query-container">\n\t\t\t<div class="query-stem"><div></div></div>\n\t\t\t<div class="query-block btn-group">\n\t\t\t\t<a ng-click="$ctrl.add()" class="btn btn-lg btn-add btn-default">\n\t\t\t\t\t<i class="fa fa-fw fa-plus fa-lg"></i>\n\t\t\t\t</a>\n\t\t\t</div>\n\t\t</div>\n\t\t\x3c!-- }}} --\x3e\n\t',controller:["$element","$scope",function(t,e){var n=this;n.operands=[{id:"$eq",setter:function(t){return{$eq:t}},export:function(t){return t.valueEdit},base:{title:"Is",type:"string"},boolean:{title:"Is",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"},date:{title:"Is exactly",type:"date"}},{id:"$ne",setter:function(t){return{$ne:t}},export:function(t){return{$ne:t.valueEdit}},base:{title:"Is not",type:"string"},boolean:{title:"Is not",type:"boolean",textTrue:"Enabled",textFalse:"Disabled"},date:{title:"Is not exactly",type:"date"}},{id:"$in",setter:function(t){return{$in:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$in:t.value.$in}},base:{title:"One of",type:"array"}},{id:"$nin",setter:function(t){return{$nin:_.isArray(t)?t.split(/\s*,\s*/):[t]}},export:function(t){return{$nin:t.value.$nin}},base:{title:"Not one of",type:"array"}},{id:"$gt",setter:function(t){return{$gt:t}},export:function(t){return{$gt:t.value.$gt}},base:{title:"Above",type:"number"},date:{title:"Is after",type:"date"}},{id:"$gte",setter:function(t){return{$gte:t}},export:function(t){return{$gte:t.value.$gte}},base:{title:"Above or equals",type:"number"},date:{title:"Is at least",type:"date"}},{id:"$lt",setter:function(t){return{$lt:t}},export:function(t){return{$lt:t.value.$lt}},base:{title:"Below",type:"number"},date:{title:"Is before",type:"date"}},{id:"$lte",setter:function(t){return{$lt:t}},export:function(t){return{$lte:t.value.$lte}},base:{title:"Below or equals",type:"number"},date:{title:"Is at most",type:"date"}},{id:"$exists",setter:function(t){return{$exists:!!t}},export:function(t){return{$exists:t.value.$exists}},base:{title:"Has a value",type:"boolean",textTrue:"Has a value",textFalse:"Has a value"}},{id:"$regexp",setter:function(t){return{$regexp:t}},export:function(t){return{$regexp:t.value.$regexp}},base:{title:"Matches",type:"string"}}],n.operandsByID=_.mapKeys(n.operands,"id"),n.getSpec=function(t,e,a){return n.spec[a]?n.spec[a]:"$and"==t||"$or"==t?_defineProperty({type:"group"},"type",t):_.isString(e)?{type:"string"}:_.isNumber(e)?{type:"number"}:{type:"string"}},n.translateBranch=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return _(n.branch).map(function(t,a){return{id:a,value:t,valueEdit:n.getFlatValue(t),valueOperand:_.isObject(t)?_(t).keys().first():"$eq",isMeta:a.startsWith("$")||["sort","skip","limit"].includes(a),spec:n.getSpec(a,t,a),path:e.concat([a])}}).sortBy(function(t){return t.isMeta?"Z"+t.id:"A"+t.id}).value()},n.exportBranch=function(){n.branch=_(n.properties).mapKeys(function(t){return t.id}).mapValues(function(t){return n.operandsByID[t.valueOperand].export(t)}).value()},n.properties,e.$watchGroup(["$ctrl.branch","$ctrl.spec"],function(){n.branch&&n.spec&&(n.properties=n.translateBranch(n.branch))}),n.setField=function(t,e){t.id=e,t.path=[e],t.value=void 0,t.valueEdit=void 0,t.valueOperand="$eq",t.spec=n.spec[e],n.setValue(t)},n.setWrapper=function(t,e){if("$eq"==t.valueOperand&&"$ne"==e)t.valueOperand="$ne",t.valueEdit=n.getFlatValue(t.value),t.value={$ne:t.valueEdit};else if("$ne"==t.valueOperand&&"$eq"==e)t.valueOperand="$eq",t.valueEdit=n.getFlatValue(t.value),t.value={$eq:t.valueEdit};else if("$in"==t.valueOperand&&"$eq"==e)t.valueOperand="$eq",t.value=t.valueEdit=n.getFlatValue(t.value);else if("$eq"!=t.valueOperand&&void 0!==t.valueOperand||"$in"!=e)if("$exists"==e)t.valueOperand="$exists",t.valueEdit=!0,t.value={$exists:t.valueEdit};else{console.log("UNHANDLED TYPE CONVERT:",t.type,"=>",e);var a=n.getFlatValue(t.value);t.valueOperand=e,t.valueEdit=a,t.value=_defineProperty({},t.valueOperand,t.valueEdit)}else t.valueOperand="$in",t.valueEdit=n.getFlatValue(t.value),t.value={$in:[t.valueEdit]};n.exportBranch()},n.setValue=function(t,e){var a=_.isUndefined(e)?t.valueEdit:e;t.value=n.operandsByID[t.valueOperand].setter(a),t.valueEdit=n.getFlatValue(t.value),n.exportBranch()},n.setValueIncluded=function(t,e,n){var a=_(t.value).keys().first();if(!a)throw new Error("Tried to set array inclusion on non wrapped key: "+t.value);var l=t.value[a].includes(e);n&&!l?t.value[a].push(e):!n&&l&&(t.value[a]=t.value[a].filter(function(t){return t!=e})),t.value[a].sort(),t.valueEdit=_.isObject(t.value)&&_.size(t.value)?_(t.value).map().first():t.value},n.getFlatValue=function(t){return _.isString(t)||_.isNumber(t)||_.isBoolean(t)||_.isDate(t)?t:_.isObject(t)&&1==_.size(t)?_(t).values().first():(console.warn("Given up trying to flatten input value",t),t)},n.add=function(){if(!n.properties.some(function(t){return!t.id})){n.properties.push({});e.$on("uiQueryQueryRepaint",function(){t.find(".query-block > .new").addClass("open")})}},n.remove=function(t){n.properties=n.properties.filter(function(e){return e.id!=t}),n.exportBranch()}}]}).filter("uiQueryBuilderFilterSelected",function(){return function(t,e,n){if(t)return t.filter(function(t){var a=e.valueEdit.includes(t.id);return n?!a:a})}}).directive("ngRepeatEmit",["$rootScope","$timeout",function(t,e){return{restrict:"A",link:function(t,n,a){!0===t.$last&&e(function(){return t.$emit(a.ngRepeatEmit)})}}}]),angular.module("angular-ui-query-builder").service("qbTableSettings",function(){return{icons:{sortNone:"fa fa-fw fa-sort text-muted",sortAsc:"fa fa-fw fa-sort-alpha-asc text-primary",sortDesc:"fa fa-fw fa-sort-alpha-desc text-primary"}}}).directive("qbTable",function(){return{scope:{qbTable:"=",stickyThead:"<",stickyTfoot:"<"},restrict:"AC",controller:["$attrs","$element","$scope","qbTableSettings",function(t,e,n,a){var l=this;l.query=n.qbTable,l.setField=function(t,e){if(void 0!=e)switch(t){case"sort":l.query.sort===e?l.query.sort="-"+e:(l.query.sort,l.query.sort=e);break;default:n.qbTable[t]=e}else delete l.query[t]},e.addClass("qb-table"),n.$watch("stickyThead",function(){return e.toggleClass("qb-sticky-thead",n.stickyThead||""===t.stickyThead)}),n.$watch("stickyTfoot",function(){return e.toggleClass("qb-sticky-tfoot",n.stickyTfoot||""===t.stickyTfoot)})}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,n){var a=this;e.qbTableSettings=n,e.canSort=!1,e.isSorted=!1,a.$onInit=function(){return e.canSort=e.sortable||""===t.sortable},e.$watch("qbTable.query.sort",function(t){var n=e.sortable||e.q;t?angular.isArray(t)&&t.some(function(t){return t==n})||t==n?e.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+n})||t=="-"+n?e.isSorted="desc":e.isSorted=!1:e.isSorted=!1}),e.toggleSort=function(){e.sortable?e.qbTable.setField("sort",e.sortable):e.qbCol&&""===t.sortable&&e.qbTable.setField("sort",e.qbCol)}}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">\n\t\t\t<i class="{{\n\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t}}"></i>\n\t\t</a>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",controller:["$attrs","$scope","qbTableSettings",function(t,e,n){e.qbTableSettings=n,e.canPrev=!0,e.canNext=!0,e.$watchGroup(["qbTable.query.limit","qbTable.query.skip"],function(t){e.canPrev=e.qbTable.query.skip>0,e.canNext=!e.total||e.qbTable.query.skip+e.qbTable.query.limit<e.total}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.min((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0));else{if(1!=t)throw new Error("Unsupported page move: "+t);e.qbTable.setField("skip",(e.qbTable.query.skip||0)+(e.qbTable.query.limit||10),0)}}}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i class="fa fa-arrow-left"></i></a></li>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i class="fa fa-arrow-right"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'}});

@@ -5,2 +5,3 @@ var _ = require('lodash');

var cleanCSS = require('gulp-clean-css');
var file = require('gulp-file');
var ghPages = require('gulp-gh-pages');

@@ -11,2 +12,4 @@ var gulp = require('gulp');

var nodemon = require('gulp-nodemon');
var rename = require('gulp-rename');
var replace = require('gulp-replace');
var rimraf = require('rimraf');

@@ -152,4 +155,4 @@ var uglify = require('gulp-uglify');

'./demo/app.js',
'./demo/app.css',
'./demo/index.html',
'./demo/style.css',
'./dist/**/*',

@@ -165,4 +168,5 @@ './node_modules/angular/angular.min.js',

'./node_modules/font-awesome/fonts/fontawesome-webfont.woff2',
'./node_modules/moment/min/moment.min.js',
], {base: __dirname})
.pipe(concat(function(path) {
.pipe(rename(function(path) {
if (path.dirname == 'demo') { // Move all demo files into root

@@ -173,2 +177,4 @@ path.dirname = '.';

}))
.pipe(file('data.json', JSON.stringify(require('./demo/testData.js'))))
.pipe(replace(/api\/data/, 'data.json', {skipBinary: true}))
.pipe(ghPages({

@@ -175,0 +181,0 @@ cacheDir: 'gh-pages',

{
"name": "@momsfriendlydevco/angular-ui-query-builder",
"version": "1.2.0",
"version": "1.2.1",
"description": "MongoDB format query-builder UI component for Angular",

@@ -30,5 +30,8 @@ "main": "dist/angular-ui-query-builder.js",

"gulp-concat": "^2.6.1",
"gulp-file": "^0.4.0",
"gulp-gh-pages": "^0.5.4",
"gulp-nodemon": "^2.2.1",
"gulp-plumber": "^1.1.0",
"gulp-rename": "^1.2.2",
"gulp-replace": "^0.6.1",
"gulp-uglify": "^3.0.0",

@@ -35,0 +38,0 @@ "gulp-util": "^3.0.8",

@@ -120,7 +120,22 @@ angular-ui-query-builder

qb-table (directive)
--------------------
Use on a `<table/>` element to designate that it should be managed by this module.
Valid attributes are:
| Attribute | Type | Description |
|----------------|-----------|-------------------------------------------------------------------------------------------|
| `qb-table` | `Object` | The main query object to mutate when the table is interacted with |
| `sticky-thead` | `boolean` | Indicates that the `<thead/>` portion of the table should remain on screen when scrolling |
| `sticky-tfoot` | `boolean` | Indicates that the `<tfoot/>` portion of the table should remain on screen when scrolling |
TODO
====
ui-query-builder
----------------
* [x] Basic field filtering

@@ -136,1 +151,15 @@ * [ ] CSS tidyup

* [ ] Nicer multi level path support
qb-tables
---------
* [x] Pagination
* [x] Sticky headers on scroll
* [x] Query-Builder intergration
* [ ] Export to excel functionality
* [x] Sorting per-column
* [ ] Simple searching
* [ ] Freezing columns (low priority)
* [ ] Responsive layout compatible for mobiles (low priority)
* [ ] Animated on load or transitions (low priority)
* [ ] Column reorder capability (low priority)
angular.module('angular-ui-query-builder')
// Table decorator {{{
// qbTableSettings (service) {{{
.service('qbTableSettings', function() {

@@ -13,7 +13,10 @@ return {

})
// }}}
// qbTable (directive) {{{
/**
* Directive applied to a table element to indicate that we should manage that table via angular-ui-query
* @param {Object} qbTable The query object to modify
* @param {boolean} stickyThead Anything within the `thead` section of the table should remain on the screen while scrolling
* @param {boolean} stickyTfoot Anything within the `tfoot` section of the table should remain on the screen while scrolling
* @emits qbTableQueryChange Emitted to child elements as (e, query) when the query object changes

@@ -24,5 +27,7 @@ */

qbTable: '=',
stickyThead: '<',
stickyTfoot: '<',
},
restrict: 'AC',
controller: function($scope, qbTableSettings) {
controller: function($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;

@@ -51,6 +56,11 @@ $ctrl.query = $scope.qbTable; // Copy into $ctrl so children can access it / $watch it

};
$element.addClass('qb-table');
$scope.$watch('stickyThead', ()=> $element.toggleClass('qb-sticky-thead', $scope.stickyThead || $attrs.stickyThead === ''));
$scope.$watch('stickyTfoot', ()=> $element.toggleClass('qb-sticky-tfoot', $scope.stickyTfoot || $attrs.stickyTfoot === ''));
},
}})
// }}}
// qbCol (directive) {{{
/**

@@ -79,5 +89,3 @@ * Directive for header elements to add angular-ui-query functionality

$ctrl.$onInit = ()=> {
$scope.canSort = $scope.sortable || $attrs.sortable === '';
};
$ctrl.$onInit = ()=> $scope.canSort = $scope.sortable || $attrs.sortable === '';

@@ -104,6 +112,6 @@ $scope.$watch('qbTable.query.sort', sorter => {

$scope.toggleSort = ()=> {
if ($scope.sortable) {
if ($scope.sortable) { // Sort by a specific field
$scope.qbTable.setField('sort', $scope.sortable);
} else if ($scope.q && $attrs.sortable === '') { // Has attribute but no value - assume main key if we have one
$scope.qbTable.setField('sort', $scope.q);
} else if ($scope.qbCol && $attrs.sortable === '') { // Has attribute but no value - assume main key if we have one
$scope.qbTable.setField('sort', $scope.qbCol);
}

@@ -127,4 +135,5 @@ };

}})
// }}}
// qbPagination {{{
/**

@@ -173,3 +182,2 @@ * Directive to add table pagination

}})
// }}}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc