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.1 to 1.2.3

271

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

@@ -12,2 +12,8 @@ 'use strict';

sortDesc: 'fa fa-fw fa-sort-alpha-desc text-primary'
},
export: {
defaults: {
format: 'xlsx'
},
formats: [{ id: 'xlsx', title: 'Excel (XLSX)' }, { id: 'csv', title: 'CSV' }, { id: 'json', title: 'JSON' }, { id: 'html', title: 'HTML (display in browser)' }]
}

@@ -18,2 +24,18 @@ };

// qbTableUtilities (service) {{{
.service('qbTableUtilities', function () {
return {
getSynopsis: function getSynopsis(query) {
var filters = _.keys(query).filter(function (i) {
return !['sort', 'skip', 'limit', 'select'].includes(i);
});
return [filters.length ? filters.length + ' filters' : 'All records', query.sort ? query.sort.startsWith('-') ? 'sorted by ' + query.sort.substr(1) + ' (reverse order)' : 'sorted by ' + query.sort : null, query.limit ? 'limited to ' + query.limit + ' rows' : null, query.offset ? 'starting at record ' + query.skip : null, query.select ? 'selecting only ' + query.select.length + ' columns' : null].filter(function (i) {
return i;
}).join(', ');
}
};
})
// }}}
// qbTable (directive) {{{

@@ -30,5 +52,5 @@ /**

scope: {
qbTable: '=',
stickyThead: '<',
stickyTfoot: '<'
qbTable: '=?',
stickyThead: '<?',
stickyTfoot: '<?'
},

@@ -40,2 +62,16 @@ restrict: 'AC',

$ctrl.$broadcast = function (msg) {
var _console;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
(_console = console).log.apply(_console, ['BROADCAST DOWN', msg].concat(args));
$scope.$broadcast.apply($scope, [msg].concat(args)); // Rebind broadcast so its accessible from children
};
$ctrl.$on = function (event, cb) {
return $scope.$on(event, cb);
};
$ctrl.setField = function (field, value) {

@@ -84,2 +120,9 @@ if (value == undefined) {

* @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
*
* @example
* <thead>
* <tr>
* <td qb-col="name" sortable>Name</td>
* </tr>
* </thead>
*/

@@ -95,3 +138,3 @@ .directive('qbCol', function () {

transclude: true,
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
controller: ['$attrs', '$element', '$scope', 'qbTableSettings', function controller($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;

@@ -101,2 +144,9 @@

// Sanity checks {{{
var unSanityChecks = $scope.$watchGroup(['qbTable', 'sortable'], function () {
if ($attrs.sortable === '' && !$scope.qbTable) console.warn('Added qb-col + sortable onto element', $element, 'but no qb-table query has been assigned on the table element!');
unSanityChecks();
});
// }}}
// Sort functionality {{{

@@ -107,7 +157,9 @@ $scope.canSort = false; // True if either sortable has a specific value or is at least present

$ctrl.$onInit = function () {
return $scope.canSort = $scope.sortable || $attrs.sortable === '';
$scope.canSort = $scope.sortable || $attrs.sortable === '';
$element.toggleClass('sortable', $scope.canSort);
};
$scope.$watch('qbTable.query.sort', function (sorter) {
var sortField = $scope.sortable || $scope.q;
var sortField = $scope.sortable || $scope.qbCol;
if (!sorter) {

@@ -138,2 +190,4 @@ $scope.isSorted = false;

// }}}
$element.addClass('qb-col');
}],

@@ -143,3 +197,3 @@ link: function link(scope, element, attrs, 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'
template: '\n\t\t<div class="qb-col-wrapper">\n\t\t\t<ng-transclude></ng-transclude>\n\t\t\t<a ng-if="canSort" ng-click="toggleSort()" class="qb-col-right">\n\t\t\t\t<i class="{{\n\t\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t\t}}"></i>\n\t\t\t</a>\n\t\t</div>\n\t'
};

@@ -149,2 +203,109 @@ })

// qbCell (directive) {{{
/**
* Directive for cell elements within a table
* @param {boolean} selector Whether the cell should act as a select / unselect prompt, if any value bind to this as the selection variable
* @param {Object} ^qbTable.qbTable The query Object to mutate
*
* @emits qbTableCellSelectMeta Issued by the meta-selector element to peer selection elements that the selection criteria has changed. Called as (arg) where arg is 'all', 'none', 'invert'
* @emits qbTableCellSelect Issued by a regular selector element to broadcast its state has changed
* @emits qbTableCellSelectStatus Sent to one or more child elements as (array) to enquire their status, used to figure out if everything / partial / no items are selected. Each item is expected to add its status to `status` as a boolean
*
* @example
* <td qb-cell selector="row.selected"></td>
*/
.directive('qbCell', function () {
return {
scope: {
selector: '=?'
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: ['$attrs', '$element', '$scope', '$timeout', 'qbTableSettings', function controller($attrs, $element, $scope, $timeout, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
// Meta selection support {{{
// A cell `isMeta` if it detects its located in the `thead` section of a table
$scope.isMeta = $element.parents('thead').length > 0;
if ($scope.isMeta) {
$timeout(function () {
return $scope.qbTable.$on('qbTableCellSelect', function () {
// Ask all children what their status is
var status = [];
$scope.qbTable.$broadcast('qbTableCellSelectStatus', status);
$scope.metaStatus = status.every(function (i) {
return i;
}) ? 'all' : status.some(function (i) {
return i;
}) ? 'some' : 'none';
});
});
}
// }}}
// Selection support {{{
$scope.isSelector = 'selector' in $attrs;
$scope.$watch('selector', function () {
if ($scope.isSelector) $element.toggleClass('selector', $scope.isSelector);
if ($scope.isSelector && !$scope.isMeta) $element.parents('tr').toggleClass('selected', !!$scope.selector);
});
// Respond to clicking anywhere in the 'TD' tag
if ($scope.isSelector && !$scope.isMeta) {
$element.on('click', function (e) {
return $scope.$apply(function () {
$scope.selector = !$scope.selector;
$scope.qbTable.$broadcast('qbTableCellSelect');
});
});
}
// Handle meta interaction
$scope.metaSelect = function (type) {
return $scope.qbTable.$broadcast('qbTableCellSelectMeta', type);
};
// Bind to event listener and respond to selection directives from meta element
if ($scope.isSelector && !$scope.isMeta) {
// If we're a standard per-row minion respond to certain events
$timeout(function () {
$scope.qbTable.$on('qbTableCellSelectMeta', function (e, type) {
switch (type) {
case 'all':
$scope.selector = true;break;
case 'invert':
$scope.selector = !$scope.selector;break;
case 'none':
$scope.selector = false;break;
default:
throw new Error('Unknown selection type: ' + type);
}
$scope.qbTable.$broadcast('qbTableCellSelect'); // Trigger a recount of what is/isn't selected
});
$scope.qbTable.$on('qbTableCellSelectStatus', function (e, status) {
return status.push($scope.selector);
});
});
}
// }}}
// Style up the selector
$element.addClass('qb-cell');
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<ng-transclude></ng-transclude>\n\t\t<div ng-if="isSelector && isMeta" class="btn-group">\n\t\t\t<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t<i class="fa fa-lg fa-fw" ng-class="metaStatus == \'all\' ? \'fa-check-square-o text-primary\' : metaStatus == \'some\' ? \'fa-minus-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t</a>\n\t\t\t<ul class="dropdown-menu">\n\t\t\t\t<li><a ng-click="metaSelect(\'all\')">All</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'invert\')">Invert</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'none\')">None</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t\t<div ng-if="isSelector && !isMeta">\n\t\t\t<i class="fa fa-lg fa-fw" ng-class="selector ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t</div>\n\t'
};
})
// }}}
// qbPagination {{{

@@ -188,3 +349,99 @@ /**

};
})
// }}}
// qbExport {{{
/**
* Directive to export a table via a query
* NOTE: This element draws a simple 'Export...' button by default but can be replaced by any valid transcluded HTML. Simply call `exportPrompt()` to action
* @param {Object} query The query Object to use when exporting
* @param {Object} spec The specification object of the collection
* @param {string} url The URL endpoint to redirect to for the query to be executed (typically something like `/api/widgets`)
*
* @example Simple export button
* <qb-export query="myQuery" spec="mySpec"></qb-export>
* @example Custom button
* <qb-export query="myQuery" spec="mySpec">
* <a class="btn btn-primary" ng-click="exportPrompt()">Export this list</a>
* </qb-export>
*/
.directive('qbExport', function () {
return {
scope: {
query: '<',
spec: '<',
url: '@'
},
transclude: true,
restrict: 'EA',
controller: ['$element', '$httpParamSerializer', '$scope', '$timeout', '$window', 'qbTableSettings', 'qbTableUtilities', function controller($element, $httpParamSerializer, $scope, $timeout, $window, qbTableSettings, qbTableUtilities) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$scope.settings = {};
$scope.isShowing = false;
$scope.exportPrompt = function () {
$scope.settings = angular.extend(angular.copy(qbTableSettings.export.defaults), {
query: _($scope.query).omitBy(function (v, k) {
return ['skip', 'limit'].includes(k);
}).value(),
columns: _.map($scope.spec, function (v, k) {
v.id = k;
v.title = _.startCase(k);
v.selected = true;
return v;
})
});
$element.find('.modal').on('show.bs.modal', function () {
return $timeout(function () {
return $scope.isShowing = true;
});
}).on('hidden.bs.modal', function () {
return $timeout(function () {
return $scope.isShowing = false;
});
}).modal('show');
};
$scope.exportExecute = function () {
var query = angular.extend($scope.settings.query, {
select: $scope.settings.columns.filter(function (c) {
return c.selected;
}).map(function (c) {
return c.id;
}),
format: $scope.settings.format
});
$window.open($scope.url + '?' + $httpParamSerializer(query));
};
// Generate a readable synopsis of the query {{{
$scope.querySynopsis;
$scope.$watchGroup(['isShowing', 'settings.query'], function () {
if (!$scope.isShowing) return; // Don't bother if we're not showing anything anyway
$scope.querySynopsis = qbTableUtilities.getSynopsis($scope.settings.query);
});
// }}}
// Generate a readable synopsis of the columns collapse {{{
$scope.columnSynopsis;
$scope.$watchGroup(['isShowing', function () {
return _.get($scope.settings, 'columns', []).map(function (c) {
return c.id + '=' + c.selected;
}).join('&');
}], function () {
if (!$scope.isShowing) return; // Don't bother if we're not showing anything anyway
$scope.columnSynopsis = $scope.settings.columns.filter(function (c) {
return c.selected;
}).length + ' columns';
});
// }}}
}],
template: '\n\t\t<div class="modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div ng-if="isShowing" class="modal-content">\n\t\t\t\t\t<div class="modal-header">\n\t\t\t\t\t\t<a class="close" data-dismiss="modal"><i class="fa fa-times"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">Export</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body form-horizontal">\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Output format</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<select ng-model="settings.format" class="form-control">\n\t\t\t\t\t\t\t\t\t<option ng-repeat="format in qbTableSettings.export.formats track by format.id" value="{{format.id}}">{{format.title}}</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Criteria</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<div class="panel-group" id="qb-export-criteria-{{$id}}">\n\t\t\t\t\t\t\t\t\t<div class="panel panel-default">\n\t\t\t\t\t\t\t\t\t\t<div class="panel-heading">\n\t\t\t\t\t\t\t\t\t\t\t<h4 class="panel-title">\n\t\t\t\t\t\t\t\t\t\t\t\t<a data-toggle="collapse" data-target="#qb-export-criteria-{{$id}}-query" data-parent="#qb-export-criteria-{{$id}}" class="btn-block collapsed">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{{querySynopsis}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<i class="fa fa-caret-right pull-right"></i>\n\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div id="qb-export-criteria-{{$id}}-query" class="panel-collapse collapse container">\n\t\t\t\t\t\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\t\t\t\t\t\tquery="settings.query"\n\t\t\t\t\t\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t\t\t\t\t\t></ui-query-builder>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Columns</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<div class="panel-group" id="qb-export-columns-{{$id}}">\n\t\t\t\t\t\t\t\t\t<div class="panel panel-default">\n\t\t\t\t\t\t\t\t\t\t<div class="panel-heading">\n\t\t\t\t\t\t\t\t\t\t\t<h4 class="panel-title">\n\t\t\t\t\t\t\t\t\t\t\t\t<a data-toggle="collapse" data-target="#qb-export-columns-{{$id}}-columns" data-parent="#qb-export-columns-{{$id}}" class="btn-block collapsed">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{{columnSynopsis}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<i class="fa fa-caret-right pull-right"></i>\n\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div id="qb-export-columns-{{$id}}-columns" class="panel-collapse collapse row">\n\t\t\t\t\t\t\t\t\t\t\t<div class="col-xs-12">\n\t\t\t\t\t\t\t\t\t\t\t\t<table qb-table class="table table-hover">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<th qb-cell selector></th>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<tr ng-repeat="col in settings.columns track by col.id">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td qb-cell selector="col.selected"></td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td>{{col.title}}</td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-footer">\n\t\t\t\t\t\t<div class="pull-left">\n\t\t\t\t\t\t\t<a class="btn btn-danger" data-dismiss="modal">Cancel</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="pull-right">\n\t\t\t\t\t\t\t<a ng-click="exportExecute()" class="btn btn-primary" data-dismiss="modal">Export</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<ng-transclude>\n\t\t\t<a ng-click="exportPrompt()" class="btn btn-default">Export...</a>\n\t\t</ng-transclude>\n\t'
};
});
// }}}

2

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

@@ -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:"=",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'}});
"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"},export:{defaults:{format:"xlsx"},formats:[{id:"xlsx",title:"Excel (XLSX)"},{id:"csv",title:"CSV"},{id:"json",title:"JSON"},{id:"html",title:"HTML (display in browser)"}]}}}).service("qbTableUtilities",function(){return{getSynopsis:function(t){var e=_.keys(t).filter(function(t){return!["sort","skip","limit","select"].includes(t)});return[e.length?e.length+" filters":"All records",t.sort?t.sort.startsWith("-")?"sorted by "+t.sort.substr(1)+" (reverse order)":"sorted by "+t.sort:null,t.limit?"limited to "+t.limit+" rows":null,t.offset?"starting at record "+t.skip:null,t.select?"selecting only "+t.select.length+" columns":null].filter(function(t){return t}).join(", ")}}}).directive("qbTable",function(){return{scope:{qbTable:"=?",stickyThead:"<?",stickyTfoot:"<?"},restrict:"AC",controller:["$attrs","$element","$scope","qbTableSettings",function(t,e,n,l){var a=this;a.query=n.qbTable,a.$broadcast=function(t){for(var e,l=arguments.length,a=Array(l>1?l-1:0),s=1;s<l;s++)a[s-1]=arguments[s];(e=console).log.apply(e,["BROADCAST DOWN",t].concat(a)),n.$broadcast.apply(n,[t].concat(a))},a.$on=function(t,e){return n.$on(t,e)},a.setField=function(t,e){if(void 0!=e)switch(t){case"sort":a.query.sort===e?a.query.sort="-"+e:(a.query.sort,a.query.sort=e);break;default:n.qbTable[t]=e}else delete a.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","$element","$scope","qbTableSettings",function(t,e,n,l){var a=this;n.qbTableSettings=l;var s=n.$watchGroup(["qbTable","sortable"],function(){""!==t.sortable||n.qbTable||console.warn("Added qb-col + sortable onto element",e,"but no qb-table query has been assigned on the table element!"),s()});n.canSort=!1,n.isSorted=!1,a.$onInit=function(){n.canSort=n.sortable||""===t.sortable,e.toggleClass("sortable",n.canSort)},n.$watch("qbTable.query.sort",function(t){var e=n.sortable||n.qbCol;t?angular.isArray(t)&&t.some(function(t){return t==e})||t==e?n.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+e})||t=="-"+e?n.isSorted="desc":n.isSorted=!1:n.isSorted=!1}),n.toggleSort=function(){n.sortable?n.qbTable.setField("sort",n.sortable):n.qbCol&&""===t.sortable&&n.qbTable.setField("sort",n.qbCol)},e.addClass("qb-col")}],link:function(t,e,n,l){t.qbTable=l},template:'\n\t\t<div class="qb-col-wrapper">\n\t\t\t<ng-transclude></ng-transclude>\n\t\t\t<a ng-if="canSort" ng-click="toggleSort()" class="qb-col-right">\n\t\t\t\t<i class="{{\n\t\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t\t}}"></i>\n\t\t\t</a>\n\t\t</div>\n\t'}}).directive("qbCell",function(){return{scope:{selector:"=?"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,l,a){n.qbTableSettings=a,n.isMeta=e.parents("thead").length>0,n.isMeta&&l(function(){return n.qbTable.$on("qbTableCellSelect",function(){var t=[];n.qbTable.$broadcast("qbTableCellSelectStatus",t),n.metaStatus=t.every(function(t){return t})?"all":t.some(function(t){return t})?"some":"none"})}),n.isSelector="selector"in t,n.$watch("selector",function(){n.isSelector&&e.toggleClass("selector",n.isSelector),n.isSelector&&!n.isMeta&&e.parents("tr").toggleClass("selected",!!n.selector)}),n.isSelector&&!n.isMeta&&e.on("click",function(t){return n.$apply(function(){n.selector=!n.selector,n.qbTable.$broadcast("qbTableCellSelect")})}),n.metaSelect=function(t){return n.qbTable.$broadcast("qbTableCellSelectMeta",t)},n.isSelector&&!n.isMeta&&l(function(){n.qbTable.$on("qbTableCellSelectMeta",function(t,e){switch(e){case"all":n.selector=!0;break;case"invert":n.selector=!n.selector;break;case"none":n.selector=!1;break;default:throw new Error("Unknown selection type: "+e)}n.qbTable.$broadcast("qbTableCellSelect")}),n.qbTable.$on("qbTableCellSelectStatus",function(t,e){return e.push(n.selector)})}),e.addClass("qb-cell")}],link:function(t,e,n,l){t.qbTable=l},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<div ng-if="isSelector && isMeta" class="btn-group">\n\t\t\t<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t<i class="fa fa-lg fa-fw" ng-class="metaStatus == \'all\' ? \'fa-check-square-o text-primary\' : metaStatus == \'some\' ? \'fa-minus-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t</a>\n\t\t\t<ul class="dropdown-menu">\n\t\t\t\t<li><a ng-click="metaSelect(\'all\')">All</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'invert\')">Invert</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'none\')">None</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t\t<div ng-if="isSelector && !isMeta">\n\t\t\t<i class="fa fa-lg fa-fw" ng-class="selector ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t</div>\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,l){t.qbTable=l},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'}}).directive("qbExport",function(){return{scope:{query:"<",spec:"<",url:"@"},transclude:!0,restrict:"EA",controller:["$element","$httpParamSerializer","$scope","$timeout","$window","qbTableSettings","qbTableUtilities",function(t,e,n,l,a,s,i){n.qbTableSettings=s,n.settings={},n.isShowing=!1,n.exportPrompt=function(){n.settings=angular.extend(angular.copy(s.export.defaults),{query:_(n.query).omitBy(function(t,e){return["skip","limit"].includes(e)}).value(),columns:_.map(n.spec,function(t,e){return t.id=e,t.title=_.startCase(e),t.selected=!0,t})}),t.find(".modal").on("show.bs.modal",function(){return l(function(){return n.isShowing=!0})}).on("hidden.bs.modal",function(){return l(function(){return n.isShowing=!1})}).modal("show")},n.exportExecute=function(){var t=angular.extend(n.settings.query,{select:n.settings.columns.filter(function(t){return t.selected}).map(function(t){return t.id}),format:n.settings.format});a.open(n.url+"?"+e(t))},n.querySynopsis,n.$watchGroup(["isShowing","settings.query"],function(){n.isShowing&&(n.querySynopsis=i.getSynopsis(n.settings.query))}),n.columnSynopsis,n.$watchGroup(["isShowing",function(){return _.get(n.settings,"columns",[]).map(function(t){return t.id+"="+t.selected}).join("&")}],function(){n.isShowing&&(n.columnSynopsis=n.settings.columns.filter(function(t){return t.selected}).length+" columns")})}],template:'\n\t\t<div class="modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div ng-if="isShowing" class="modal-content">\n\t\t\t\t\t<div class="modal-header">\n\t\t\t\t\t\t<a class="close" data-dismiss="modal"><i class="fa fa-times"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">Export</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body form-horizontal">\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Output format</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<select ng-model="settings.format" class="form-control">\n\t\t\t\t\t\t\t\t\t<option ng-repeat="format in qbTableSettings.export.formats track by format.id" value="{{format.id}}">{{format.title}}</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Criteria</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<div class="panel-group" id="qb-export-criteria-{{$id}}">\n\t\t\t\t\t\t\t\t\t<div class="panel panel-default">\n\t\t\t\t\t\t\t\t\t\t<div class="panel-heading">\n\t\t\t\t\t\t\t\t\t\t\t<h4 class="panel-title">\n\t\t\t\t\t\t\t\t\t\t\t\t<a data-toggle="collapse" data-target="#qb-export-criteria-{{$id}}-query" data-parent="#qb-export-criteria-{{$id}}" class="btn-block collapsed">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{{querySynopsis}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<i class="fa fa-caret-right pull-right"></i>\n\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div id="qb-export-criteria-{{$id}}-query" class="panel-collapse collapse container">\n\t\t\t\t\t\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\t\t\t\t\t\tquery="settings.query"\n\t\t\t\t\t\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t\t\t\t\t\t></ui-query-builder>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Columns</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<div class="panel-group" id="qb-export-columns-{{$id}}">\n\t\t\t\t\t\t\t\t\t<div class="panel panel-default">\n\t\t\t\t\t\t\t\t\t\t<div class="panel-heading">\n\t\t\t\t\t\t\t\t\t\t\t<h4 class="panel-title">\n\t\t\t\t\t\t\t\t\t\t\t\t<a data-toggle="collapse" data-target="#qb-export-columns-{{$id}}-columns" data-parent="#qb-export-columns-{{$id}}" class="btn-block collapsed">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{{columnSynopsis}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<i class="fa fa-caret-right pull-right"></i>\n\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div id="qb-export-columns-{{$id}}-columns" class="panel-collapse collapse row">\n\t\t\t\t\t\t\t\t\t\t\t<div class="col-xs-12">\n\t\t\t\t\t\t\t\t\t\t\t\t<table qb-table class="table table-hover">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<th qb-cell selector></th>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<tr ng-repeat="col in settings.columns track by col.id">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td qb-cell selector="col.selected"></td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td>{{col.title}}</td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-footer">\n\t\t\t\t\t\t<div class="pull-left">\n\t\t\t\t\t\t\t<a class="btn btn-danger" data-dismiss="modal">Cancel</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="pull-right">\n\t\t\t\t\t\t\t<a ng-click="exportExecute()" class="btn btn-primary" data-dismiss="modal">Export</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<ng-transclude>\n\t\t\t<a ng-click="exportPrompt()" class="btn btn-default">Export...</a>\n\t\t</ng-transclude>\n\t'}});

@@ -490,2 +490,8 @@ 'use strict';

sortDesc: 'fa fa-fw fa-sort-alpha-desc text-primary'
},
export: {
defaults: {
format: 'xlsx'
},
formats: [{ id: 'xlsx', title: 'Excel (XLSX)' }, { id: 'csv', title: 'CSV' }, { id: 'json', title: 'JSON' }, { id: 'html', title: 'HTML (display in browser)' }]
}

@@ -496,2 +502,18 @@ };

// qbTableUtilities (service) {{{
.service('qbTableUtilities', function () {
return {
getSynopsis: function getSynopsis(query) {
var filters = _.keys(query).filter(function (i) {
return !['sort', 'skip', 'limit', 'select'].includes(i);
});
return [filters.length ? filters.length + ' filters' : 'All records', query.sort ? query.sort.startsWith('-') ? 'sorted by ' + query.sort.substr(1) + ' (reverse order)' : 'sorted by ' + query.sort : null, query.limit ? 'limited to ' + query.limit + ' rows' : null, query.offset ? 'starting at record ' + query.skip : null, query.select ? 'selecting only ' + query.select.length + ' columns' : null].filter(function (i) {
return i;
}).join(', ');
}
};
})
// }}}
// qbTable (directive) {{{

@@ -508,5 +530,5 @@ /**

scope: {
qbTable: '=',
stickyThead: '<',
stickyTfoot: '<'
qbTable: '=?',
stickyThead: '<?',
stickyTfoot: '<?'
},

@@ -518,2 +540,16 @@ restrict: 'AC',

$ctrl.$broadcast = function (msg) {
var _console;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
(_console = console).log.apply(_console, ['BROADCAST DOWN', msg].concat(args));
$scope.$broadcast.apply($scope, [msg].concat(args)); // Rebind broadcast so its accessible from children
};
$ctrl.$on = function (event, cb) {
return $scope.$on(event, cb);
};
$ctrl.setField = function (field, value) {

@@ -562,2 +598,9 @@ if (value == undefined) {

* @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
*
* @example
* <thead>
* <tr>
* <td qb-col="name" sortable>Name</td>
* </tr>
* </thead>
*/

@@ -573,3 +616,3 @@ .directive('qbCol', function () {

transclude: true,
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
controller: ['$attrs', '$element', '$scope', 'qbTableSettings', function controller($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;

@@ -579,2 +622,9 @@

// Sanity checks {{{
var unSanityChecks = $scope.$watchGroup(['qbTable', 'sortable'], function () {
if ($attrs.sortable === '' && !$scope.qbTable) console.warn('Added qb-col + sortable onto element', $element, 'but no qb-table query has been assigned on the table element!');
unSanityChecks();
});
// }}}
// Sort functionality {{{

@@ -585,7 +635,9 @@ $scope.canSort = false; // True if either sortable has a specific value or is at least present

$ctrl.$onInit = function () {
return $scope.canSort = $scope.sortable || $attrs.sortable === '';
$scope.canSort = $scope.sortable || $attrs.sortable === '';
$element.toggleClass('sortable', $scope.canSort);
};
$scope.$watch('qbTable.query.sort', function (sorter) {
var sortField = $scope.sortable || $scope.q;
var sortField = $scope.sortable || $scope.qbCol;
if (!sorter) {

@@ -616,2 +668,4 @@ $scope.isSorted = false;

// }}}
$element.addClass('qb-col');
}],

@@ -621,3 +675,3 @@ link: function link(scope, element, attrs, 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'
template: '\n\t\t<div class="qb-col-wrapper">\n\t\t\t<ng-transclude></ng-transclude>\n\t\t\t<a ng-if="canSort" ng-click="toggleSort()" class="qb-col-right">\n\t\t\t\t<i class="{{\n\t\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t\t}}"></i>\n\t\t\t</a>\n\t\t</div>\n\t'
};

@@ -627,2 +681,109 @@ })

// qbCell (directive) {{{
/**
* Directive for cell elements within a table
* @param {boolean} selector Whether the cell should act as a select / unselect prompt, if any value bind to this as the selection variable
* @param {Object} ^qbTable.qbTable The query Object to mutate
*
* @emits qbTableCellSelectMeta Issued by the meta-selector element to peer selection elements that the selection criteria has changed. Called as (arg) where arg is 'all', 'none', 'invert'
* @emits qbTableCellSelect Issued by a regular selector element to broadcast its state has changed
* @emits qbTableCellSelectStatus Sent to one or more child elements as (array) to enquire their status, used to figure out if everything / partial / no items are selected. Each item is expected to add its status to `status` as a boolean
*
* @example
* <td qb-cell selector="row.selected"></td>
*/
.directive('qbCell', function () {
return {
scope: {
selector: '=?'
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: ['$attrs', '$element', '$scope', '$timeout', 'qbTableSettings', function controller($attrs, $element, $scope, $timeout, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
// Meta selection support {{{
// A cell `isMeta` if it detects its located in the `thead` section of a table
$scope.isMeta = $element.parents('thead').length > 0;
if ($scope.isMeta) {
$timeout(function () {
return $scope.qbTable.$on('qbTableCellSelect', function () {
// Ask all children what their status is
var status = [];
$scope.qbTable.$broadcast('qbTableCellSelectStatus', status);
$scope.metaStatus = status.every(function (i) {
return i;
}) ? 'all' : status.some(function (i) {
return i;
}) ? 'some' : 'none';
});
});
}
// }}}
// Selection support {{{
$scope.isSelector = 'selector' in $attrs;
$scope.$watch('selector', function () {
if ($scope.isSelector) $element.toggleClass('selector', $scope.isSelector);
if ($scope.isSelector && !$scope.isMeta) $element.parents('tr').toggleClass('selected', !!$scope.selector);
});
// Respond to clicking anywhere in the 'TD' tag
if ($scope.isSelector && !$scope.isMeta) {
$element.on('click', function (e) {
return $scope.$apply(function () {
$scope.selector = !$scope.selector;
$scope.qbTable.$broadcast('qbTableCellSelect');
});
});
}
// Handle meta interaction
$scope.metaSelect = function (type) {
return $scope.qbTable.$broadcast('qbTableCellSelectMeta', type);
};
// Bind to event listener and respond to selection directives from meta element
if ($scope.isSelector && !$scope.isMeta) {
// If we're a standard per-row minion respond to certain events
$timeout(function () {
$scope.qbTable.$on('qbTableCellSelectMeta', function (e, type) {
switch (type) {
case 'all':
$scope.selector = true;break;
case 'invert':
$scope.selector = !$scope.selector;break;
case 'none':
$scope.selector = false;break;
default:
throw new Error('Unknown selection type: ' + type);
}
$scope.qbTable.$broadcast('qbTableCellSelect'); // Trigger a recount of what is/isn't selected
});
$scope.qbTable.$on('qbTableCellSelectStatus', function (e, status) {
return status.push($scope.selector);
});
});
}
// }}}
// Style up the selector
$element.addClass('qb-cell');
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<ng-transclude></ng-transclude>\n\t\t<div ng-if="isSelector && isMeta" class="btn-group">\n\t\t\t<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t<i class="fa fa-lg fa-fw" ng-class="metaStatus == \'all\' ? \'fa-check-square-o text-primary\' : metaStatus == \'some\' ? \'fa-minus-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t</a>\n\t\t\t<ul class="dropdown-menu">\n\t\t\t\t<li><a ng-click="metaSelect(\'all\')">All</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'invert\')">Invert</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'none\')">None</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t\t<div ng-if="isSelector && !isMeta">\n\t\t\t<i class="fa fa-lg fa-fw" ng-class="selector ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t</div>\n\t'
};
})
// }}}
// qbPagination {{{

@@ -666,3 +827,99 @@ /**

};
})
// }}}
// qbExport {{{
/**
* Directive to export a table via a query
* NOTE: This element draws a simple 'Export...' button by default but can be replaced by any valid transcluded HTML. Simply call `exportPrompt()` to action
* @param {Object} query The query Object to use when exporting
* @param {Object} spec The specification object of the collection
* @param {string} url The URL endpoint to redirect to for the query to be executed (typically something like `/api/widgets`)
*
* @example Simple export button
* <qb-export query="myQuery" spec="mySpec"></qb-export>
* @example Custom button
* <qb-export query="myQuery" spec="mySpec">
* <a class="btn btn-primary" ng-click="exportPrompt()">Export this list</a>
* </qb-export>
*/
.directive('qbExport', function () {
return {
scope: {
query: '<',
spec: '<',
url: '@'
},
transclude: true,
restrict: 'EA',
controller: ['$element', '$httpParamSerializer', '$scope', '$timeout', '$window', 'qbTableSettings', 'qbTableUtilities', function controller($element, $httpParamSerializer, $scope, $timeout, $window, qbTableSettings, qbTableUtilities) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$scope.settings = {};
$scope.isShowing = false;
$scope.exportPrompt = function () {
$scope.settings = angular.extend(angular.copy(qbTableSettings.export.defaults), {
query: _($scope.query).omitBy(function (v, k) {
return ['skip', 'limit'].includes(k);
}).value(),
columns: _.map($scope.spec, function (v, k) {
v.id = k;
v.title = _.startCase(k);
v.selected = true;
return v;
})
});
$element.find('.modal').on('show.bs.modal', function () {
return $timeout(function () {
return $scope.isShowing = true;
});
}).on('hidden.bs.modal', function () {
return $timeout(function () {
return $scope.isShowing = false;
});
}).modal('show');
};
$scope.exportExecute = function () {
var query = angular.extend($scope.settings.query, {
select: $scope.settings.columns.filter(function (c) {
return c.selected;
}).map(function (c) {
return c.id;
}),
format: $scope.settings.format
});
$window.open($scope.url + '?' + $httpParamSerializer(query));
};
// Generate a readable synopsis of the query {{{
$scope.querySynopsis;
$scope.$watchGroup(['isShowing', 'settings.query'], function () {
if (!$scope.isShowing) return; // Don't bother if we're not showing anything anyway
$scope.querySynopsis = qbTableUtilities.getSynopsis($scope.settings.query);
});
// }}}
// Generate a readable synopsis of the columns collapse {{{
$scope.columnSynopsis;
$scope.$watchGroup(['isShowing', function () {
return _.get($scope.settings, 'columns', []).map(function (c) {
return c.id + '=' + c.selected;
}).join('&');
}], function () {
if (!$scope.isShowing) return; // Don't bother if we're not showing anything anyway
$scope.columnSynopsis = $scope.settings.columns.filter(function (c) {
return c.selected;
}).length + ' columns';
});
// }}}
}],
template: '\n\t\t<div class="modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div ng-if="isShowing" class="modal-content">\n\t\t\t\t\t<div class="modal-header">\n\t\t\t\t\t\t<a class="close" data-dismiss="modal"><i class="fa fa-times"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">Export</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body form-horizontal">\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Output format</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<select ng-model="settings.format" class="form-control">\n\t\t\t\t\t\t\t\t\t<option ng-repeat="format in qbTableSettings.export.formats track by format.id" value="{{format.id}}">{{format.title}}</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Criteria</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<div class="panel-group" id="qb-export-criteria-{{$id}}">\n\t\t\t\t\t\t\t\t\t<div class="panel panel-default">\n\t\t\t\t\t\t\t\t\t\t<div class="panel-heading">\n\t\t\t\t\t\t\t\t\t\t\t<h4 class="panel-title">\n\t\t\t\t\t\t\t\t\t\t\t\t<a data-toggle="collapse" data-target="#qb-export-criteria-{{$id}}-query" data-parent="#qb-export-criteria-{{$id}}" class="btn-block collapsed">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{{querySynopsis}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<i class="fa fa-caret-right pull-right"></i>\n\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div id="qb-export-criteria-{{$id}}-query" class="panel-collapse collapse container">\n\t\t\t\t\t\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\t\t\t\t\t\tquery="settings.query"\n\t\t\t\t\t\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t\t\t\t\t\t></ui-query-builder>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Columns</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<div class="panel-group" id="qb-export-columns-{{$id}}">\n\t\t\t\t\t\t\t\t\t<div class="panel panel-default">\n\t\t\t\t\t\t\t\t\t\t<div class="panel-heading">\n\t\t\t\t\t\t\t\t\t\t\t<h4 class="panel-title">\n\t\t\t\t\t\t\t\t\t\t\t\t<a data-toggle="collapse" data-target="#qb-export-columns-{{$id}}-columns" data-parent="#qb-export-columns-{{$id}}" class="btn-block collapsed">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{{columnSynopsis}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<i class="fa fa-caret-right pull-right"></i>\n\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div id="qb-export-columns-{{$id}}-columns" class="panel-collapse collapse row">\n\t\t\t\t\t\t\t\t\t\t\t<div class="col-xs-12">\n\t\t\t\t\t\t\t\t\t\t\t\t<table qb-table class="table table-hover">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<th qb-cell selector></th>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<tr ng-repeat="col in settings.columns track by col.id">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td qb-cell selector="col.selected"></td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td>{{col.title}}</td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-footer">\n\t\t\t\t\t\t<div class="pull-left">\n\t\t\t\t\t\t\t<a class="btn btn-danger" data-dismiss="modal">Cancel</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="pull-right">\n\t\t\t\t\t\t\t<a ng-click="exportExecute()" class="btn btn-primary" data-dismiss="modal">Export</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<ng-transclude>\n\t\t\t<a ng-click="exportPrompt()" class="btn btn-default">Export...</a>\n\t\t</ng-transclude>\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\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'}});
"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"},export:{defaults:{format:"xlsx"},formats:[{id:"xlsx",title:"Excel (XLSX)"},{id:"csv",title:"CSV"},{id:"json",title:"JSON"},{id:"html",title:"HTML (display in browser)"}]}}}).service("qbTableUtilities",function(){return{getSynopsis:function(t){var e=_.keys(t).filter(function(t){return!["sort","skip","limit","select"].includes(t)});return[e.length?e.length+" filters":"All records",t.sort?t.sort.startsWith("-")?"sorted by "+t.sort.substr(1)+" (reverse order)":"sorted by "+t.sort:null,t.limit?"limited to "+t.limit+" rows":null,t.offset?"starting at record "+t.skip:null,t.select?"selecting only "+t.select.length+" columns":null].filter(function(t){return t}).join(", ")}}}).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.$broadcast=function(t){for(var e,a=arguments.length,l=Array(a>1?a-1:0),i=1;i<a;i++)l[i-1]=arguments[i];(e=console).log.apply(e,["BROADCAST DOWN",t].concat(l)),n.$broadcast.apply(n,[t].concat(l))},l.$on=function(t,e){return n.$on(t,e)},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","$element","$scope","qbTableSettings",function(t,e,n,a){var l=this;n.qbTableSettings=a;var i=n.$watchGroup(["qbTable","sortable"],function(){""!==t.sortable||n.qbTable||console.warn("Added qb-col + sortable onto element",e,"but no qb-table query has been assigned on the table element!"),i()});n.canSort=!1,n.isSorted=!1,l.$onInit=function(){n.canSort=n.sortable||""===t.sortable,e.toggleClass("sortable",n.canSort)},n.$watch("qbTable.query.sort",function(t){var e=n.sortable||n.qbCol;t?angular.isArray(t)&&t.some(function(t){return t==e})||t==e?n.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+e})||t=="-"+e?n.isSorted="desc":n.isSorted=!1:n.isSorted=!1}),n.toggleSort=function(){n.sortable?n.qbTable.setField("sort",n.sortable):n.qbCol&&""===t.sortable&&n.qbTable.setField("sort",n.qbCol)},e.addClass("qb-col")}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<div class="qb-col-wrapper">\n\t\t\t<ng-transclude></ng-transclude>\n\t\t\t<a ng-if="canSort" ng-click="toggleSort()" class="qb-col-right">\n\t\t\t\t<i class="{{\n\t\t\t\t\tisSorted == \'asc\' ? qbTableSettings.icons.sortAsc\n\t\t\t\t\t: isSorted == \'desc\' ? qbTableSettings.icons.sortDesc\n\t\t\t\t\t: qbTableSettings.icons.sortNone\n\t\t\t\t}}"></i>\n\t\t\t</a>\n\t\t</div>\n\t'}}).directive("qbCell",function(){return{scope:{selector:"=?"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,a,l){n.qbTableSettings=l,n.isMeta=e.parents("thead").length>0,n.isMeta&&a(function(){return n.qbTable.$on("qbTableCellSelect",function(){var t=[];n.qbTable.$broadcast("qbTableCellSelectStatus",t),n.metaStatus=t.every(function(t){return t})?"all":t.some(function(t){return t})?"some":"none"})}),n.isSelector="selector"in t,n.$watch("selector",function(){n.isSelector&&e.toggleClass("selector",n.isSelector),n.isSelector&&!n.isMeta&&e.parents("tr").toggleClass("selected",!!n.selector)}),n.isSelector&&!n.isMeta&&e.on("click",function(t){return n.$apply(function(){n.selector=!n.selector,n.qbTable.$broadcast("qbTableCellSelect")})}),n.metaSelect=function(t){return n.qbTable.$broadcast("qbTableCellSelectMeta",t)},n.isSelector&&!n.isMeta&&a(function(){n.qbTable.$on("qbTableCellSelectMeta",function(t,e){switch(e){case"all":n.selector=!0;break;case"invert":n.selector=!n.selector;break;case"none":n.selector=!1;break;default:throw new Error("Unknown selection type: "+e)}n.qbTable.$broadcast("qbTableCellSelect")}),n.qbTable.$on("qbTableCellSelectStatus",function(t,e){return e.push(n.selector)})}),e.addClass("qb-cell")}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<div ng-if="isSelector && isMeta" class="btn-group">\n\t\t\t<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t<i class="fa fa-lg fa-fw" ng-class="metaStatus == \'all\' ? \'fa-check-square-o text-primary\' : metaStatus == \'some\' ? \'fa-minus-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t</a>\n\t\t\t<ul class="dropdown-menu">\n\t\t\t\t<li><a ng-click="metaSelect(\'all\')">All</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'invert\')">Invert</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'none\')">None</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t\t<div ng-if="isSelector && !isMeta">\n\t\t\t<i class="fa fa-lg fa-fw" ng-class="selector ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t</div>\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'}}).directive("qbExport",function(){return{scope:{query:"<",spec:"<",url:"@"},transclude:!0,restrict:"EA",controller:["$element","$httpParamSerializer","$scope","$timeout","$window","qbTableSettings","qbTableUtilities",function(t,e,n,a,l,i,r){n.qbTableSettings=i,n.settings={},n.isShowing=!1,n.exportPrompt=function(){n.settings=angular.extend(angular.copy(i.export.defaults),{query:_(n.query).omitBy(function(t,e){return["skip","limit"].includes(e)}).value(),columns:_.map(n.spec,function(t,e){return t.id=e,t.title=_.startCase(e),t.selected=!0,t})}),t.find(".modal").on("show.bs.modal",function(){return a(function(){return n.isShowing=!0})}).on("hidden.bs.modal",function(){return a(function(){return n.isShowing=!1})}).modal("show")},n.exportExecute=function(){var t=angular.extend(n.settings.query,{select:n.settings.columns.filter(function(t){return t.selected}).map(function(t){return t.id}),format:n.settings.format});l.open(n.url+"?"+e(t))},n.querySynopsis,n.$watchGroup(["isShowing","settings.query"],function(){n.isShowing&&(n.querySynopsis=r.getSynopsis(n.settings.query))}),n.columnSynopsis,n.$watchGroup(["isShowing",function(){return _.get(n.settings,"columns",[]).map(function(t){return t.id+"="+t.selected}).join("&")}],function(){n.isShowing&&(n.columnSynopsis=n.settings.columns.filter(function(t){return t.selected}).length+" columns")})}],template:'\n\t\t<div class="modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div ng-if="isShowing" class="modal-content">\n\t\t\t\t\t<div class="modal-header">\n\t\t\t\t\t\t<a class="close" data-dismiss="modal"><i class="fa fa-times"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">Export</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body form-horizontal">\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Output format</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<select ng-model="settings.format" class="form-control">\n\t\t\t\t\t\t\t\t\t<option ng-repeat="format in qbTableSettings.export.formats track by format.id" value="{{format.id}}">{{format.title}}</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Criteria</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<div class="panel-group" id="qb-export-criteria-{{$id}}">\n\t\t\t\t\t\t\t\t\t<div class="panel panel-default">\n\t\t\t\t\t\t\t\t\t\t<div class="panel-heading">\n\t\t\t\t\t\t\t\t\t\t\t<h4 class="panel-title">\n\t\t\t\t\t\t\t\t\t\t\t\t<a data-toggle="collapse" data-target="#qb-export-criteria-{{$id}}-query" data-parent="#qb-export-criteria-{{$id}}" class="btn-block collapsed">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{{querySynopsis}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<i class="fa fa-caret-right pull-right"></i>\n\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div id="qb-export-criteria-{{$id}}-query" class="panel-collapse collapse container">\n\t\t\t\t\t\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\t\t\t\t\t\tquery="settings.query"\n\t\t\t\t\t\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t\t\t\t\t\t></ui-query-builder>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">Columns</label>\n\t\t\t\t\t\t\t<div class="col-sm-9">\n\t\t\t\t\t\t\t\t<div class="panel-group" id="qb-export-columns-{{$id}}">\n\t\t\t\t\t\t\t\t\t<div class="panel panel-default">\n\t\t\t\t\t\t\t\t\t\t<div class="panel-heading">\n\t\t\t\t\t\t\t\t\t\t\t<h4 class="panel-title">\n\t\t\t\t\t\t\t\t\t\t\t\t<a data-toggle="collapse" data-target="#qb-export-columns-{{$id}}-columns" data-parent="#qb-export-columns-{{$id}}" class="btn-block collapsed">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{{columnSynopsis}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t<i class="fa fa-caret-right pull-right"></i>\n\t\t\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div id="qb-export-columns-{{$id}}-columns" class="panel-collapse collapse row">\n\t\t\t\t\t\t\t\t\t\t\t<div class="col-xs-12">\n\t\t\t\t\t\t\t\t\t\t\t\t<table qb-table class="table table-hover">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<th qb-cell selector></th>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<tr ng-repeat="col in settings.columns track by col.id">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td qb-cell selector="col.selected"></td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<td>{{col.title}}</td>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-footer">\n\t\t\t\t\t\t<div class="pull-left">\n\t\t\t\t\t\t\t<a class="btn btn-danger" data-dismiss="modal">Cancel</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div class="pull-right">\n\t\t\t\t\t\t\t<a ng-click="exportExecute()" class="btn btn-primary" data-dismiss="modal">Export</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t\t<ng-transclude>\n\t\t\t<a ng-click="exportPrompt()" class="btn btn-default">Export...</a>\n\t\t</ng-transclude>\n\t'}});

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

[{"id":"user0","name":"Lonnie Rau","username":"Mariano_Schiller","address":{"street":"686 O'Connell Union","city":"East Simeon","zip":"04167","state":"Massachusetts","country":"Pitcairn Islands"},"phone":"907.268.7482","role":"admin","status":"active","lastLogin":"2018-01-31T05:23:56.766Z"},{"id":"user1","name":"Marta Stehr","username":"Alfonso87","address":{"street":"03354 Giovanny Lodge","city":"Bechtelarside","zip":"89777-5813","state":"Alabama","country":"Peru"},"role":"user","status":"active","lastLogin":"2017-08-27T02:08:22.872Z"},{"id":"user2","name":"Claud Braun","username":"Miguel_Corkery","email":"Lesley.Crooks66@yahoo.com","address":{"street":"4879 Smitham Haven","city":"South Rozellaborough","zip":"36549-1922","state":"Massachusetts","country":"Macedonia"},"role":"root","status":"active","lastLogin":"2017-03-26T11:11:47.645Z"},{"id":"user3","name":"Darian Hamill","username":"Jamir41","email":"Van.Kohler97@hotmail.com","address":{"street":"11504 Aliza Neck","city":"Damianburgh","zip":"13001-7057","state":"Michigan","country":"Seychelles"},"phone":"(508) 176-0567","website":"https://boris.biz","role":"admin","status":"active","lastLogin":"2017-12-21T13:53:29.350Z"},{"id":"user4","name":"Ila Murphy","username":"Santos93","address":{"street":"01437 Daniel Cape","city":"Toyview","zip":"33819","state":"Alabama","country":"Bosnia and Herzegovina"},"website":"http://kari.info","role":"root","status":"active","lastLogin":"2017-06-26T00:15:08.819Z"},{"id":"user5","name":"Leopoldo Cummerata","username":"Barrett.Klein75","email":"Ricardo20@yahoo.com","address":{"street":"3214 Orn Brooks","city":"Linnieshire","zip":"20967","state":"Massachusetts","country":"Chad"},"company":{"name":"Ullrich and Sons"},"role":"user","status":"pending","lastLogin":"2018-01-31T10:25:40.788Z"},{"id":"user6","name":"Samanta Becker","username":"Remington_Waelchi24","email":"Brown58@hotmail.com","address":{"street":"01320 Rafaela Ridge","city":"Wavafort","zip":"22244","state":"California","country":"Mauritania"},"phone":"1-324-134-8336","role":"user","status":"active","lastLogin":"2018-01-13T03:17:57.768Z"},{"id":"user7","name":"Lorine Kassulke","username":"Jordan.Huels","email":"Josephine_Hegmann65@yahoo.com","address":{"street":"272 Juliet Plaza","city":"Port Valentina","zip":"21057-2256","state":"Oregon","country":"Puerto Rico"},"phone":"415.613.2864 x849","company":{"name":"Waelchi, Wolf and Blick"},"role":"user","status":"active","lastLogin":"2017-09-16T08:53:45.991Z"},{"id":"user8","name":"Jesus Koch","username":"Lowell58","email":"Vada_Luettgen@gmail.com","address":{"street":"483 Kassulke Divide","city":"Moenburgh","zip":"93877-2276","state":"Missouri","country":"Marshall Islands"},"company":{"name":"Nikolaus - Kerluke"},"role":"user","status":"pending","lastLogin":"2018-01-31T06:17:09.312Z"},{"id":"user9","name":"Aidan Schultz","username":"Alvah_Walker","email":"Providenci.Harvey@gmail.com","address":{"street":"66605 McGlynn Skyway","city":"Donniestad","zip":"34853-3402","state":"Illinois","country":"Mexico"},"phone":"831-630-2349 x108","website":"https://garrison.com","role":"user","status":"active","lastLogin":"2018-01-31T20:37:18.638Z"},{"id":"user10","name":"Catherine Wisozk","username":"Alanna_Heidenreich49","email":"Charlie27@yahoo.com","address":{"street":"07929 Nicolas Tunnel","city":"Batzstad","zip":"27523-6514","state":"Pennsylvania","country":"Burundi"},"phone":"(282) 087-9192","website":"https://cassidy.info","company":{"name":"Langosh, Effertz and Dare"},"role":"user","status":"active","lastLogin":"2017-03-29T02:44:48.515Z"},{"id":"user11","name":"Milan Wehner","username":"Morris.Powlowski69","email":"Jammie_Aufderhar@yahoo.com","address":{"street":"245 Feest Brooks","city":"Gabrielfort","zip":"08653-2687","state":"Maryland","country":"Lebanon"},"phone":"637-135-9287","website":"http://webster.org","company":{"name":"Hermann - Larkin"},"role":"user","status":"active","lastLogin":"2018-02-01T01:32:35.543Z"},{"id":"user12","name":"Juliet Schuppe","username":"Laurie77","email":"General96@yahoo.com","address":{"street":"55297 Marisa Ports","city":"Heloisefurt","zip":"93618-6739","state":"California","country":"Maldives"},"phone":"(607) 695-4493 x114","role":"user","status":"pending","lastLogin":"2018-01-31T19:40:42.521Z"},{"id":"user13","name":"Jarret Runte","username":"Lucy.Klein49","email":"Rhett52@hotmail.com","address":{"street":"72290 Grimes Extension","city":"Nicolasside","zip":"26239","state":"North Dakota","country":"Mongolia"},"role":"admin","status":"active","lastLogin":"2017-06-14T00:57:38.649Z"},{"id":"user14","name":"Amie Vandervort","username":"Salvatore.Hickle","email":"Keara.Durgan@yahoo.com","address":{"street":"86578 Farrell Station","city":"New Alycetown","zip":"11149-5608","state":"Louisiana","country":"Lesotho"},"website":"https://halie.net","role":"user","status":"pending","lastLogin":"2018-01-31T18:35:32.967Z"},{"id":"user15","name":"Mabelle Hayes","username":"Carlee90","address":{"street":"3513 Alessandro Pines","city":"Maxwellborough","zip":"94552","state":"Iowa","country":"Botswana"},"website":"http://blanche.name","role":"user","status":"pending","lastLogin":"2018-01-31T19:05:14.440Z"},{"id":"user16","name":"Lessie McLaughlin","username":"Iva27","address":{"street":"452 Flavio Lakes","city":"East Alize","zip":"16667","state":"Alaska","country":"Namibia"},"phone":"1-711-427-1255","role":"user","status":"active","lastLogin":"2018-01-31T07:52:29.981Z"},{"id":"user17","name":"Dashawn Simonis","username":"Joseph_Schaden","email":"Vincenza27@hotmail.com","address":{"street":"1524 Braulio Plain","city":"South Oren","zip":"66244","state":"Oregon","country":"Latvia"},"phone":"034-019-0387 x1378","role":"user","status":"pending","lastLogin":"2018-01-31T07:34:44.090Z"},{"id":"user18","name":"Bryana Raynor","username":"Rahsaan.Doyle","address":{"street":"5046 Johns Brook","city":"New Mabelle","zip":"45684","state":"Maryland","country":"Mexico"},"website":"http://jacky.net","role":"user","status":"active","lastLogin":"2017-04-21T15:39:02.604Z"},{"id":"user19","name":"Amie Muller","username":"Jude.Bosco99","address":{"street":"2301 Kenya Brook","city":"South Lonnie","zip":"58099","state":"Kentucky","country":"Brazil"},"role":"admin","status":"deleted","lastLogin":"2018-01-31T23:56:30.039Z"},{"id":"user20","name":"Gladys Macejkovic","username":"Newell_Lowe79","email":"Rasheed72@yahoo.com","address":{"street":"69059 Lois Oval","city":"Lake Babyside","zip":"70329-6012","state":"Utah","country":"Reunion"},"phone":"(379) 962-3764","website":"http://brain.biz","company":{"name":"Konopelski LLC"},"role":"root","status":"active","lastLogin":"2017-12-10T05:33:17.782Z"},{"id":"user21","name":"Camren Fadel","username":"Derek48","email":"Maya43@yahoo.com","address":{"street":"12158 Sheila Road","city":"Icieburgh","zip":"84474-5860","state":"Louisiana","country":"Bhutan"},"phone":"100-112-0542","website":"https://logan.org","role":"root","status":"active","lastLogin":"2017-09-13T22:50:33.381Z"},{"id":"user22","name":"Alvina Bosco","username":"Monte62","address":{"street":"23400 Skiles Squares","city":"New Tessieshire","zip":"78181","state":"Connecticut","country":"Niue"},"website":"https://stefan.info","role":"admin","status":"active","lastLogin":"2018-01-31T22:42:26.835Z"},{"id":"user23","name":"Sophia Ziemann","username":"Marty.Sipes","address":{"street":"8317 Wyman Stream","city":"West Mylenemouth","zip":"18366-2279","state":"Kansas","country":"Oman"},"phone":"1-382-039-6206","role":"user","status":"active","lastLogin":"2018-01-31T12:03:01.087Z"},{"id":"user24","name":"Cyrus Osinski","username":"Montana26","address":{"street":"593 Murray Drives","city":"Kohlerberg","zip":"41308","state":"Delaware","country":"Japan"},"company":{"name":"Wolf, Kuhic and Hills"},"role":"user","status":"active","lastLogin":"2018-02-01T00:35:33.678Z"},{"id":"user25","name":"Wayne Kling","username":"Brad_Kris","email":"Cornelius_McKenzie@hotmail.com","address":{"street":"580 Lorenz Station","city":"North Geovannyside","zip":"00304","state":"West Virginia","country":"Mexico"},"website":"https://nasir.name","role":"user","status":"active","lastLogin":"2018-01-31T23:38:40.233Z"},{"id":"user26","name":"Sibyl Kerluke","username":"Annamae69","email":"Morton18@hotmail.com","address":{"street":"5107 Kuhlman Center","city":"Toneyview","zip":"95224-8937","state":"Florida","country":"Germany"},"phone":"1-578-281-6056","role":"user","status":"active","lastLogin":"2018-01-31T23:37:49.870Z"},{"id":"user27","name":"Ward McGlynn","username":"Jade_Mante","email":"Krista41@gmail.com","address":{"street":"491 Zelda Fall","city":"Jonatantown","zip":"31062","state":"Florida","country":"Singapore"},"role":"root","status":"deleted","lastLogin":"2018-02-01T02:30:04.963Z"},{"id":"user28","name":"Pete Ryan","username":"Matt_Lueilwitz0","address":{"street":"886 Price Ridge","city":"North Tobinside","zip":"90000-7381","state":"California","country":"Greece"},"phone":"188.058.0376","website":"https://kelton.name","role":"user","status":"active","lastLogin":"2017-10-31T01:56:58.681Z"},{"id":"user29","name":"Sharon Morar","username":"Luella_McLaughlin42","address":{"street":"123 Effertz Drive","city":"O'Keefeview","zip":"08292","state":"Rhode Island","country":"Liechtenstein"},"role":"root","status":"active","lastLogin":"2017-02-01T05:55:33.508Z"},{"id":"user30","name":"Jadon Ferry","username":"Laurence.Rath77","address":{"street":"84954 Mann Pine","city":"North Stoneview","zip":"43834-0514","state":"Louisiana","country":"Fiji"},"company":{"name":"Grady - Shields"},"role":"user","status":"active","lastLogin":"2017-07-02T00:17:57.785Z"},{"id":"user31","name":"Kenna Okuneva","username":"Billy_Volkman","email":"Filiberto.Nikolaus16@hotmail.com","address":{"street":"176 Aliya Fall","city":"New Aglae","zip":"24890","state":"Illinois","country":"Comoros"},"website":"http://ayden.info","role":"user","status":"active","lastLogin":"2017-06-29T09:56:08.919Z"},{"id":"user32","name":"Conner Rempel","username":"Hillard.Feil","address":{"street":"4574 Jimmy Turnpike","city":"East Alanna","zip":"84645","state":"California","country":"Micronesia"},"phone":"851-972-3963 x43910","role":"user","status":"active","lastLogin":"2017-12-11T17:09:47.763Z"},{"id":"user33","name":"Demetris Raynor","username":"Zachariah.Weimann","address":{"street":"087 Jast Shores","city":"West Elise","zip":"88540","state":"Alaska","country":"Spain"},"role":"admin","status":"active","lastLogin":"2018-02-01T02:08:07.204Z"},{"id":"user34","name":"Elsie Lowe","username":"Maia.Wiegand64","address":{"street":"96660 Colten Vista","city":"Lucieburgh","zip":"35366","state":"Michigan","country":"Dominican Republic"},"role":"user","status":"pending","lastLogin":"2018-01-31T09:16:28.651Z"},{"id":"user35","name":"Georgianna O'Keefe","username":"Jeanie_Braun","email":"Arden_Bosco64@yahoo.com","address":{"street":"600 Michel Meadows","city":"Dickensstad","zip":"81532-0243","state":"Mississippi","country":"India"},"phone":"971-905-8333 x553","website":"https://carol.info","company":{"name":"Pacocha, Zulauf and Wolf"},"role":"admin","status":"active","lastLogin":"2018-01-31T08:01:35.427Z"},{"id":"user36","name":"Alan Baumbach","username":"Maritza55","address":{"street":"1746 Brisa Estate","city":"North Taylorstad","zip":"11468","state":"Florida","country":"Zambia"},"phone":"1-960-977-8202 x50400","website":"https://cassandra.com","company":{"name":"Schaden Inc"},"role":"user","status":"active","lastLogin":"2017-07-15T21:32:30.956Z"},{"id":"user37","name":"Jack Baumbach","username":"Rex_OReilly44","email":"Gust45@yahoo.com","address":{"street":"2501 Bertha Summit","city":"Germaineshire","zip":"05647","state":"Ohio","country":"Italy"},"website":"http://jacky.biz","role":"user","status":"pending","lastLogin":"2018-01-31T11:35:09.099Z"},{"id":"user38","name":"Flavio Feeney","username":"Aliyah_Dickens90","email":"Rowena.Kunze@gmail.com","address":{"street":"7719 Adela Crossing","city":"East Tyrese","zip":"98040","state":"West Virginia","country":"Maldives"},"phone":"1-597-933-4711 x01736","role":"admin","status":"active","lastLogin":"2018-01-31T20:00:48.666Z"},{"id":"user39","name":"Buford Ernser","username":"Robb.Lowe","email":"Kelli12@yahoo.com","address":{"street":"3698 Hessel Plaza","city":"East Beverlyhaven","zip":"90198-5610","state":"Wyoming","country":"Uganda"},"website":"http://stefanie.org","company":{"name":"Ferry, Breitenberg and Schultz"},"role":"admin","status":"active","lastLogin":"2018-01-31T10:33:59.613Z"},{"id":"user40","name":"Lauryn Weissnat","username":"Jackeline.Hammes","email":"Annetta73@yahoo.com","address":{"street":"11541 Wunsch Park","city":"Vivianefort","zip":"91187-8502","state":"Alaska","country":"Bahrain"},"phone":"627.332.4685 x11179","website":"http://micah.com","company":{"name":"Willms Inc"},"role":"user","status":"active","lastLogin":"2018-02-01T01:21:55.682Z"},{"id":"user41","name":"Dylan Leuschke","username":"Adeline_Weimann","email":"Izabella_Bosco@hotmail.com","address":{"street":"334 Alphonso Drive","city":"Port Elvisborough","zip":"51647","state":"Oklahoma","country":"Tonga"},"website":"http://delaney.net","role":"admin","status":"active","lastLogin":"2017-02-04T11:00:20.599Z"},{"id":"user42","name":"Madilyn Hegmann","username":"Constantin.Spinka","email":"Vivien_Halvorson@gmail.com","address":{"street":"377 Liza Fall","city":"Lueilwitzfort","zip":"74482","state":"New Jersey","country":"Kyrgyz Republic"},"phone":"(270) 154-0354 x0035","role":"admin","status":"active","lastLogin":"2017-08-01T06:06:32.849Z"},{"id":"user43","name":"Abraham Bayer","username":"Richie89","email":"Retta66@yahoo.com","address":{"street":"60877 Mosciski Hill","city":"North Terrell","zip":"99949","state":"Delaware","country":"Greece"},"company":{"name":"Glover and Sons"},"role":"user","status":"pending","lastLogin":"2017-04-24T02:27:50.895Z"},{"id":"user44","name":"Wava Haley","username":"Fae_Walsh46","address":{"street":"318 Laurine Coves","city":"Kulasside","zip":"62621","state":"Vermont","country":"Solomon Islands"},"website":"http://kirk.net","company":{"name":"Bergnaum, Oberbrunner and Schiller"},"role":"user","status":"deleted","lastLogin":"2018-01-31T07:52:33.041Z"},{"id":"user45","name":"Bernie Nicolas","username":"Terrence.Crona","email":"Christine_Hilpert@yahoo.com","address":{"street":"38952 Weldon Rapids","city":"Krajcikshire","zip":"60785-3079","state":"Maine","country":"Serbia"},"company":{"name":"VonRueden - Weimann"},"role":"user","status":"deleted","lastLogin":"2018-01-31T21:13:35.177Z"},{"id":"user46","name":"Marlon Bins","username":"Carolina46","email":"Jerad.Roob@hotmail.com","address":{"street":"3653 Wiegand Lane","city":"East Veronicahaven","zip":"29402-0253","state":"Delaware","country":"Afghanistan"},"phone":"1-604-287-1750 x38457","website":"https://mariah.biz","role":"admin","status":"active","lastLogin":"2017-05-21T08:39:16.394Z"},{"id":"user47","name":"Nasir Torphy","username":"Quinton.Windler","email":"Alexanne.Brakus@yahoo.com","address":{"street":"48428 Daugherty Hill","city":"Erlingstad","zip":"44628-0425","state":"Delaware","country":"Jamaica"},"phone":"726-373-1887","website":"http://rose.net","role":"admin","status":"active","lastLogin":"2018-01-31T13:19:02.419Z"},{"id":"user48","name":"Gussie Langosh","username":"Kelli.Kerluke","address":{"street":"44124 Pierce Loop","city":"Sydneeborough","zip":"72197-3268","state":"Kansas","country":"Seychelles"},"role":"user","status":"active","lastLogin":"2017-03-23T09:25:14.348Z"},{"id":"user49","name":"Jaylen Fadel","username":"Hailey48","address":{"street":"054 Hermann Bypass","city":"Zulachester","zip":"00045-0245","state":"Oregon","country":"Svalbard & Jan Mayen Islands"},"phone":"948-185-1928","role":"user","status":"active","lastLogin":"2017-07-18T02:41:12.062Z"},{"id":"user50","name":"Laverne McClure","username":"Kelley_Schuster10","address":{"street":"12150 Aiden Bridge","city":"McCulloughstad","zip":"74537-5765","state":"Oklahoma","country":"Wallis and Futuna"},"website":"https://willard.info","role":"user","status":"active","lastLogin":"2018-01-31T18:43:35.281Z"},{"id":"user51","name":"Jazmyne Green","username":"Ebba28","address":{"street":"086 Towne Club","city":"Hildaport","zip":"51900-7204","state":"Alaska","country":"Antarctica (the territory South of 60 deg S)"},"phone":"(354) 753-8258","role":"user","status":"active","lastLogin":"2018-02-01T03:42:18.826Z"},{"id":"user52","name":"Malika Labadie","username":"Jackson.Kerluke","address":{"street":"5702 Gottlieb Rue","city":"Maurinemouth","zip":"87571","state":"Georgia","country":"Comoros"},"phone":"514-214-2694 x68294","role":"user","status":"active","lastLogin":"2017-11-11T16:33:25.309Z"},{"id":"user53","name":"Melyna Fay","username":"Beulah.Kerluke","email":"Jonathon.Kuhlman96@yahoo.com","address":{"street":"22279 Kautzer Land","city":"Altaton","zip":"45733","state":"Mississippi","country":"Malaysia"},"phone":"(911) 576-3518 x4337","website":"http://sanford.org","company":{"name":"Bartell, Schoen and Hahn"},"role":"root","status":"pending","lastLogin":"2018-01-31T16:42:28.897Z"},{"id":"user54","name":"Audra Daniel","username":"Twila64","email":"Alexane_Jones31@hotmail.com","address":{"street":"8993 Boyle Neck","city":"New Tessmouth","zip":"33485","state":"Hawaii","country":"Puerto Rico"},"role":"user","status":"active","lastLogin":"2017-08-01T21:04:39.295Z"},{"id":"user55","name":"Cameron Mitchell","username":"Bessie.Lubowitz","email":"Dayana_Parker@yahoo.com","address":{"street":"56800 Kuhic Throughway","city":"Aufderharbury","zip":"06880-2323","state":"New Hampshire","country":"Luxembourg"},"phone":"1-325-014-2769","website":"https://ines.net","role":"user","status":"active","lastLogin":"2018-01-31T14:04:37.215Z"},{"id":"user56","name":"Bernice Reinger","username":"Marilie.Rowe33","address":{"street":"765 Josephine Point","city":"West Haydenbury","zip":"05900-9515","state":"Utah","country":"Israel"},"phone":"(674) 778-9594","role":"user","status":"active","lastLogin":"2017-07-23T16:11:36.161Z"},{"id":"user57","name":"Geovanni Jones","username":"Loyce29","address":{"street":"5566 Ziemann Gateway","city":"Lake Janiya","zip":"04045-1853","state":"New Hampshire","country":"Zambia"},"company":{"name":"Halvorson - Lockman"},"role":"user","status":"active","lastLogin":"2017-09-15T23:52:30.027Z"},{"id":"user58","name":"Lauren Price","username":"Dino_Nader45","email":"Louvenia_Johnston51@yahoo.com","address":{"street":"373 Bosco Cliffs","city":"Octaviaside","zip":"30291","state":"West Virginia","country":"Croatia"},"role":"user","status":"active","lastLogin":"2018-02-01T03:01:02.791Z"},{"id":"user59","name":"Magdalen Senger","username":"Clark.Zulauf","email":"Alice.Ledner@hotmail.com","address":{"street":"12045 Alda Skyway","city":"Port Makennamouth","zip":"72529-2448","state":"Oregon","country":"Seychelles"},"phone":"(309) 869-1194","role":"user","status":"pending","lastLogin":"2017-12-06T14:23:45.871Z"},{"id":"user60","name":"Mallory Haley","username":"Keyon18","email":"Eduardo.Grimes@yahoo.com","address":{"street":"659 Lakin Shoals","city":"East Sedrick","zip":"70263-4672","state":"Arizona","country":"Northern Mariana Islands"},"role":"user","status":"active","lastLogin":"2018-02-01T03:07:09.988Z"},{"id":"user61","name":"Tristin Dare","username":"Maude.Stokes","email":"Theo85@yahoo.com","address":{"street":"0896 Kohler Circles","city":"East Jacklyn","zip":"13628","state":"Idaho","country":"Pakistan"},"website":"https://elroy.name","role":"admin","status":"active","lastLogin":"2018-01-31T21:16:50.786Z"},{"id":"user62","name":"Ava Koelpin","username":"Name_Gibson9","address":{"street":"00400 Zulauf Squares","city":"New Elinore","zip":"32368-7599","state":"Oregon","country":"Tokelau"},"phone":"1-984-061-7677","website":"https://irma.biz","role":"user","status":"deleted","lastLogin":"2017-07-05T18:20:30.067Z"},{"id":"user63","name":"Teagan Greenfelder","username":"Kadin55","email":"Roma_Will31@hotmail.com","address":{"street":"5826 Feeney Skyway","city":"North Krystina","zip":"55260-7288","state":"Maine","country":"Venezuela"},"phone":"302.801.4815","role":"user","status":"deleted","lastLogin":"2017-07-11T01:52:33.183Z"},{"id":"user64","name":"Simeon Ernser","username":"Katrina25","address":{"street":"966 Judy Burg","city":"Brigittemouth","zip":"20978-5239","state":"Massachusetts","country":"Yemen"},"phone":"636-291-1719 x5253","website":"https://montana.biz","company":{"name":"Denesik, Mitchell and Doyle"},"role":"user","status":"pending","lastLogin":"2018-01-31T20:30:46.706Z"},{"id":"user65","name":"Murl Abshire","username":"Stacey_MacGyver","email":"Elinore_Bauch55@yahoo.com","address":{"street":"3083 Kshlerin Forge","city":"Wintheiserton","zip":"85475-8920","state":"Wisconsin","country":"Canada"},"website":"http://weldon.info","role":"user","status":"pending","lastLogin":"2017-09-28T12:47:11.729Z"},{"id":"user66","name":"Greta Jones","username":"Fidel_Willms","address":{"street":"00169 Hammes Circle","city":"Bartellburgh","zip":"99498","state":"Mississippi","country":"Netherlands"},"website":"https://rossie.org","company":{"name":"Abshire, Bernhard and Rodriguez"},"role":"admin","status":"active","lastLogin":"2018-02-01T02:54:26.218Z"},{"id":"user67","name":"Viviane Wiegand","username":"Esperanza31","address":{"street":"729 Nyasia Shores","city":"New Mariam","zip":"83662","state":"Hawaii","country":"Algeria"},"company":{"name":"Carroll, O'Conner and Bogisich"},"role":"root","status":"active","lastLogin":"2017-06-06T05:53:40.601Z"},{"id":"user68","name":"Brianne Bayer","username":"Don.Cummerata","address":{"street":"887 Hermiston Skyway","city":"North Tia","zip":"22943-7283","state":"Pennsylvania","country":"Rwanda"},"company":{"name":"Bernier - Waters"},"role":"admin","status":"active","lastLogin":"2018-01-31T10:30:42.149Z"},{"id":"user69","name":"Eliza Hammes","username":"Tyrel.Gerlach98","address":{"street":"02082 Gleichner Shoal","city":"New Thaddeushaven","zip":"42063-1213","state":"Virginia","country":"Bulgaria"},"phone":"123-068-2678 x534","website":"https://julius.biz","role":"user","status":"active","lastLogin":"2017-07-27T20:21:54.378Z"},{"id":"user70","name":"Elton Bauch","username":"Arielle3","email":"Rubye86@gmail.com","address":{"street":"37237 Cortez Manor","city":"North Alannamouth","zip":"60727-2207","state":"Missouri","country":"United Arab Emirates"},"phone":"390-656-2266","role":"admin","status":"deleted","lastLogin":"2017-06-18T17:40:49.716Z"},{"id":"user71","name":"Chris Hegmann","username":"Ena.Dicki","address":{"street":"98955 Dario Creek","city":"Prosaccoside","zip":"32816","state":"Delaware","country":"Portugal"},"phone":"1-328-959-5122 x428","website":"https://gerald.name","company":{"name":"Dooley - Lockman"},"role":"user","status":"active","lastLogin":"2018-01-31T22:14:43.998Z"},{"id":"user72","name":"Maximo Hirthe","username":"Aryanna44","email":"Ward_Huels@gmail.com","address":{"street":"92345 Davin Point","city":"Schaefermouth","zip":"89033","state":"Maine","country":"Dominica"},"phone":"(922) 123-7222","role":"admin","status":"deleted","lastLogin":"2017-04-08T14:43:33.284Z"},{"id":"user73","name":"Eleanora Kunze","username":"Tobin96","email":"Rebeka70@hotmail.com","address":{"street":"17014 VonRueden Alley","city":"Lake Lucas","zip":"07601-2703","state":"Ohio","country":"Palestinian Territory"},"website":"https://alexys.info","role":"root","status":"active","lastLogin":"2018-01-31T06:10:38.575Z"},{"id":"user74","name":"Dejuan Hilpert","username":"Norwood.Harber69","email":"Rashawn_Little58@gmail.com","address":{"street":"0889 Odie Loaf","city":"New Edd","zip":"70965","state":"Minnesota","country":"Cuba"},"website":"https://green.biz","role":"user","status":"active","lastLogin":"2018-01-31T09:18:10.523Z"},{"id":"user75","name":"Antwan Wilkinson","username":"Darwin84","address":{"street":"340 Rene Road","city":"Port Elisemouth","zip":"70318-3587","state":"Wisconsin","country":"Greenland"},"phone":"016.025.2607","role":"user","status":"active","lastLogin":"2017-10-23T03:57:13.800Z"},{"id":"user76","name":"Baby Cummerata","username":"Stacy.Roberts8","email":"Virginia.Padberg75@gmail.com","address":{"street":"3817 Jennie Lane","city":"East Jessicaside","zip":"72753","state":"Nebraska","country":"Philippines"},"role":"user","status":"active","lastLogin":"2018-01-31T17:32:59.122Z"},{"id":"user77","name":"Danika Wunsch","username":"Cloyd.Zieme","email":"Deja_Nitzsche@gmail.com","address":{"street":"276 Jammie Lodge","city":"Satterfieldstad","zip":"05508-4199","state":"Maryland","country":"Kiribati"},"phone":"1-457-834-5899 x99987","company":{"name":"Reynolds, Hermann and Fritsch"},"role":"root","status":"pending","lastLogin":"2018-01-31T15:18:22.601Z"},{"id":"user78","name":"Vanessa Spinka","username":"Herbert_Heidenreich64","address":{"street":"67957 Jones Wall","city":"Generalside","zip":"30897-5555","state":"Nebraska","country":"Samoa"},"role":"user","status":"active","lastLogin":"2017-04-28T13:45:53.130Z"},{"id":"user79","name":"Keenan Powlowski","username":"Mario_McLaughlin89","email":"Novella.Durgan88@hotmail.com","address":{"street":"266 Kutch Pike","city":"Christiansenland","zip":"37566-8595","state":"Wyoming","country":"Tokelau"},"phone":"(890) 659-1155","role":"user","status":"active","lastLogin":"2018-01-31T10:00:22.961Z"},{"id":"user80","name":"Wayne Feeney","username":"Katelin78","address":{"street":"9843 Baron Stravenue","city":"North Jacquelynfurt","zip":"68220","state":"Connecticut","country":"Benin"},"company":{"name":"Gibson - Braun"},"role":"user","status":"active","lastLogin":"2017-07-30T22:10:46.418Z"},{"id":"user81","name":"Tom Terry","username":"Jarvis.Daniel","address":{"street":"3887 Mann Lodge","city":"Raynorhaven","zip":"16317-0082","state":"West Virginia","country":"Armenia"},"phone":"208-528-7798","company":{"name":"Pfeffer, Feil and Crooks"},"role":"user","status":"active","lastLogin":"2017-08-02T06:05:36.028Z"},{"id":"user82","name":"Josefina Kuphal","username":"Santina_Satterfield","email":"Augustine61@gmail.com","address":{"street":"3125 Aileen Harbors","city":"Juneton","zip":"34462-5967","state":"Idaho","country":"Pitcairn Islands"},"role":"user","status":"active","lastLogin":"2017-12-31T03:44:04.966Z"},{"id":"user83","name":"Lafayette Bailey","username":"Valentin.Goodwin8","address":{"street":"2805 Santa Mills","city":"New Americabury","zip":"04017","state":"Utah","country":"Saint Barthelemy"},"role":"admin","status":"active","lastLogin":"2017-07-26T20:11:12.979Z"},{"id":"user84","name":"Aida Quigley","username":"Camila_Lakin","email":"Janick.Hermiston24@hotmail.com","address":{"street":"4763 Block Turnpike","city":"Hilperttown","zip":"70697","state":"Nevada","country":"Micronesia"},"phone":"447-911-5499","role":"user","status":"active","lastLogin":"2017-06-15T20:57:21.101Z"},{"id":"user85","name":"Curt Auer","username":"Gayle_Schneider","email":"Jolie_Hintz@hotmail.com","address":{"street":"87094 Ortiz Inlet","city":"Albinview","zip":"18367","state":"New York","country":"Australia"},"phone":"(629) 316-6528","website":"http://cali.com","role":"user","status":"active","lastLogin":"2018-01-31T23:05:08.267Z"},{"id":"user86","name":"Brannon Wehner","username":"Tyson.Olson63","email":"Liliane48@yahoo.com","address":{"street":"19678 Osinski Glens","city":"South Reagan","zip":"41561","state":"Missouri","country":"Swaziland"},"role":"admin","status":"pending","lastLogin":"2018-01-31T08:11:49.812Z"},{"id":"user87","name":"Meggie Casper","username":"Tess39","email":"Myrtle_Smitham97@gmail.com","address":{"street":"05634 Albin Knoll","city":"Tyreeville","zip":"83498","state":"South Dakota","country":"Finland"},"company":{"name":"Cruickshank Inc"},"role":"admin","status":"active","lastLogin":"2018-01-31T13:10:02.660Z"},{"id":"user88","name":"Mack Runolfsdottir","username":"Trinity_Ruecker85","email":"Myrtie.Macejkovic69@yahoo.com","address":{"street":"2166 Foster Manors","city":"McClurechester","zip":"19277-0780","state":"Kentucky","country":"Latvia"},"role":"user","status":"active","lastLogin":"2017-04-02T08:02:32.819Z"},{"id":"user89","name":"Cristobal Hills","username":"Jonathan.Little96","email":"Jolie48@yahoo.com","address":{"street":"01635 Paucek Pines","city":"West Ora","zip":"89614-1128","state":"New York","country":"Guam"},"website":"http://brice.name","role":"user","status":"pending","lastLogin":"2018-01-31T22:07:10.257Z"},{"id":"user90","name":"Barbara Macejkovic","username":"Olen.Heidenreich","email":"Aimee.Swift@yahoo.com","address":{"street":"933 Hartmann Courts","city":"Rosieshire","zip":"65986-5931","state":"Tennessee","country":"Guyana"},"website":"https://tobin.biz","role":"user","status":"active","lastLogin":"2018-01-23T22:50:07.902Z"},{"id":"user91","name":"Elaina Stamm","username":"Sabrina_Hermiston","address":{"street":"11757 Effertz Keys","city":"Port Kirstinside","zip":"15882","state":"Illinois","country":"Albania"},"company":{"name":"Leuschke, Ortiz and Cartwright"},"role":"admin","status":"pending","lastLogin":"2018-01-31T19:19:35.735Z"},{"id":"user92","name":"Edwin Crist","username":"Alexandrine.Ullrich98","address":{"street":"1718 Rodriguez Forks","city":"Kreigerburgh","zip":"49099","state":"Wyoming","country":"Panama"},"role":"user","status":"pending","lastLogin":"2017-10-08T16:31:29.786Z"},{"id":"user93","name":"Mavis Hane","username":"Gracie.Johnston91","email":"Romaine.Homenick79@yahoo.com","address":{"street":"554 Bahringer Islands","city":"East Aurelio","zip":"69438","state":"North Dakota","country":"Guadeloupe"},"phone":"1-973-678-1117","website":"http://chelsea.net","company":{"name":"Skiles - Mosciski"},"role":"user","status":"active","lastLogin":"2017-02-25T23:11:59.634Z"},{"id":"user94","name":"Yoshiko Spinka","username":"Myah_Feil34","address":{"street":"833 Wisozk Village","city":"New Rebecca","zip":"36339","state":"Florida","country":"United Kingdom"},"phone":"1-467-847-3677 x98522","role":"admin","status":"active","lastLogin":"2017-02-23T13:02:34.661Z"},{"id":"user95","name":"Lyla Jenkins","username":"Carolyne82","email":"Adela_Schumm29@gmail.com","address":{"street":"12762 Jones Tunnel","city":"South Candido","zip":"22531-8876","state":"Pennsylvania","country":"Suriname"},"company":{"name":"King LLC"},"role":"user","status":"pending","lastLogin":"2017-05-05T22:42:20.225Z"},{"id":"user96","name":"Bertrand Connelly","username":"Myrna.McLaughlin","address":{"street":"158 Vena Mission","city":"New Kaileyshire","zip":"71272-5339","state":"Iowa","country":"Guatemala"},"phone":"606.762.5153 x5407","role":"user","status":"pending","lastLogin":"2018-01-31T23:15:32.723Z"},{"id":"user97","name":"Bobbie Zulauf","username":"Jeffrey_Beer","email":"Tianna.Hintz53@hotmail.com","address":{"street":"6670 Johnson Circles","city":"Margarettamouth","zip":"99053","state":"Colorado","country":"Samoa"},"phone":"(215) 173-5719","website":"https://koby.org","role":"user","status":"active","lastLogin":"2017-11-13T22:46:52.923Z"},{"id":"user98","name":"Oleta Strosin","username":"Al.Schuppe","address":{"street":"675 Evelyn Lake","city":"Port Wallaceside","zip":"23801-1565","state":"Alaska","country":"United States of America"},"role":"user","status":"active","lastLogin":"2017-05-07T04:03:48.053Z"},{"id":"user99","name":"Candace O'Reilly","username":"Colten_VonRueden90","email":"Miller.Wolf58@gmail.com","address":{"street":"02927 Bashirian Fords","city":"Hammesmouth","zip":"80027","state":"Vermont","country":"Macedonia"},"phone":"(835) 043-3272","role":"user","status":"deleted","lastLogin":"2018-01-31T04:45:55.455Z"}]
[{"id":"user0","name":"Leanna Hagenes","username":"Aurore.Wunsch50","email":"Walker.Vandervort48@gmail.com","address":{"street":"65111 Smitham Street","city":"Vandervorttown","zip":"82417-1527","state":"Georgia","country":"Saint Lucia"},"phone":"649.191.0198 x40747","website":"http://seth.info","role":"user","status":"active","lastLogin":"2018-02-04T21:39:23.089Z"},{"id":"user1","name":"Pasquale Stiedemann","username":"Lemuel.Leuschke9","email":"Finn_Christiansen96@gmail.com","address":{"street":"01684 Buckridge Plaza","city":"Lake Haileytown","zip":"61428","state":"Arkansas","country":"Bahrain"},"phone":"(975) 084-6678 x04828","role":"user","status":"active","lastLogin":"2018-02-04T14:16:36.750Z"},{"id":"user2","name":"Demario Durgan","username":"Lizeth_Veum49","email":"Nelson92@yahoo.com","address":{"street":"4456 Hammes Road","city":"South Ali","zip":"13871-4730","state":"New Jersey","country":"Gibraltar"},"phone":"1-303-120-7836 x166","company":{"name":"McGlynn, VonRueden and Kris"},"role":"root","status":"pending","lastLogin":"2018-01-27T17:24:14.451Z"},{"id":"user3","name":"Tessie Deckow","username":"Guadalupe.Johns","address":{"street":"393 Rau Run","city":"North Sallyfort","zip":"47629","state":"Alabama","country":"French Southern Territories"},"website":"http://amya.com","company":{"name":"Krajcik - Goyette"},"role":"admin","status":"active","lastLogin":"2018-02-04T10:08:59.627Z"},{"id":"user4","name":"Scottie Carter","username":"Wilber_Dickens31","email":"Miracle99@yahoo.com","address":{"street":"840 Corwin Green","city":"Boscobury","zip":"62027-8400","state":"Colorado","country":"Myanmar"},"company":{"name":"Rath, Reichel and Hickle"},"role":"user","status":"active","lastLogin":"2017-11-22T11:49:35.898Z"},{"id":"user5","name":"Adrien Cassin","username":"Efrain10","email":"Crystal.Wilkinson@yahoo.com","address":{"street":"667 Turner Island","city":"West Normaborough","zip":"43962-1733","state":"New Mexico","country":"Zambia"},"company":{"name":"Legros - Cremin"},"role":"admin","status":"active","lastLogin":"2018-02-04T05:26:20.447Z"},{"id":"user6","name":"Bradley Labadie","username":"Humberto_Leffler","email":"Santa8@yahoo.com","address":{"street":"539 Turner Lock","city":"West Mark","zip":"10197-2868","state":"Wisconsin","country":"Qatar"},"role":"user","status":"active","lastLogin":"2018-02-04T21:32:07.069Z"},{"id":"user7","name":"Brannon Cummerata","username":"Mandy18","email":"Christian_Mayert87@yahoo.com","address":{"street":"220 Feest Island","city":"East Dulceburgh","zip":"55915","state":"Arkansas","country":"Chad"},"phone":"130.629.8790","website":"http://yessenia.info","role":"user","status":"active","lastLogin":"2018-02-05T01:38:07.479Z"},{"id":"user8","name":"Cara Johns","username":"Rashawn84","address":{"street":"9227 Angeline Villages","city":"Port Sallyhaven","zip":"94297-0397","state":"Wisconsin","country":"Tunisia"},"website":"http://halie.info","role":"user","status":"pending","lastLogin":"2017-03-22T00:44:16.737Z"},{"id":"user9","name":"Evan Mayert","username":"Freeda_Weimann18","address":{"street":"68129 Jocelyn Stravenue","city":"East Lynnchester","zip":"98429-8454","state":"New York","country":"Bosnia and Herzegovina"},"phone":"378-409-3199 x3290","website":"http://rebekah.name","role":"user","status":"active","lastLogin":"2018-02-05T02:16:23.271Z"},{"id":"user10","name":"Jazlyn Gutkowski","username":"Ismael.Bruen15","email":"Pauline_Schiller77@yahoo.com","address":{"street":"3354 Gregory Parkway","city":"Aureliomouth","zip":"81752","state":"Nevada","country":"Hong Kong"},"phone":"421-451-4565","website":"https://jayne.info","company":{"name":"Nienow Group"},"role":"user","status":"deleted","lastLogin":"2018-02-04T14:44:09.033Z"},{"id":"user11","name":"Abel Dietrich","username":"Ramiro93","address":{"street":"2223 Barrows Mews","city":"New Howell","zip":"71017","state":"New Hampshire","country":"Peru"},"role":"admin","status":"pending","lastLogin":"2018-02-04T10:17:14.831Z"},{"id":"user12","name":"Eloisa Herzog","username":"Lucinda27","address":{"street":"5410 Wisoky Lock","city":"New Damian","zip":"35662","state":"Indiana","country":"Lesotho"},"role":"user","status":"active","lastLogin":"2017-04-22T23:56:19.061Z"},{"id":"user13","name":"Arvel Schulist","username":"Jarret.Gerhold","email":"Berry.Lebsack84@yahoo.com","address":{"street":"634 Harris Valley","city":"Dagmarburgh","zip":"07155","state":"Louisiana","country":"Honduras"},"phone":"1-371-256-5842","website":"https://berta.info","company":{"name":"Hamill - Stanton"},"role":"admin","status":"deleted","lastLogin":"2018-02-04T18:54:14.988Z"},{"id":"user14","name":"Kenyatta Mohr","username":"Chloe.Kautzer","email":"Marilie.Larson8@gmail.com","address":{"street":"438 Maverick Prairie","city":"Urbanton","zip":"59321-3940","state":"Montana","country":"Lithuania"},"phone":"961.447.6942","website":"https://geraldine.name","role":"admin","status":"pending","lastLogin":"2017-07-27T19:24:24.327Z"},{"id":"user15","name":"Phoebe Crona","username":"Ona29","address":{"street":"187 Kaitlin Fork","city":"West Yolandaport","zip":"17616","state":"Pennsylvania","country":"Cote d'Ivoire"},"company":{"name":"Runolfsdottir - Krajcik"},"role":"admin","status":"active","lastLogin":"2017-10-22T03:29:23.691Z"},{"id":"user16","name":"Herminia Nienow","username":"Edyth_Zieme21","email":"Reymundo30@gmail.com","address":{"street":"70768 Hoppe Pine","city":"South Jenningsland","zip":"49192","state":"Wisconsin","country":"Haiti"},"website":"http://ottilie.com","role":"user","status":"active","lastLogin":"2018-01-21T01:41:34.369Z"},{"id":"user17","name":"Esteban Marvin","username":"Mekhi14","address":{"street":"0793 Lemke View","city":"Brauntown","zip":"06562","state":"Ohio","country":"Iceland"},"role":"user","status":"active","lastLogin":"2018-02-04T09:04:48.114Z"},{"id":"user18","name":"Eldred Hudson","username":"Lorine_Dietrich","email":"Savannah.Dare45@yahoo.com","address":{"street":"77418 Rath Drive","city":"South Kaylee","zip":"30550-4466","state":"Oregon","country":"Tanzania"},"phone":"300-031-6323 x475","role":"user","status":"active","lastLogin":"2018-02-04T21:32:28.961Z"},{"id":"user19","name":"Vivian Prohaska","username":"Shayna.Schuppe","address":{"street":"72390 Larry Vista","city":"Cronaville","zip":"84179-8942","state":"Mississippi","country":"Slovenia"},"phone":"865.488.1579 x13375","role":"root","status":"active","lastLogin":"2018-02-04T11:45:18.162Z"},{"id":"user20","name":"Dustin Turner","username":"Kennith52","address":{"street":"860 Jerde Mountains","city":"East Edwardo","zip":"15993","state":"Wyoming","country":"Antigua and Barbuda"},"company":{"name":"Hills and Sons"},"role":"user","status":"pending","lastLogin":"2017-06-07T19:35:19.379Z"},{"id":"user21","name":"Rod Wintheiser","username":"Reed_Stokes99","address":{"street":"77842 Rosamond Row","city":"Port Tianaton","zip":"21998-4215","state":"Ohio","country":"Taiwan"},"company":{"name":"Schiller - Vandervort"},"role":"admin","status":"active","lastLogin":"2018-02-05T02:38:08.048Z"},{"id":"user22","name":"Ellen Feest","username":"Odessa11","email":"Claud_OKon61@hotmail.com","address":{"street":"688 Upton Shoal","city":"Arloport","zip":"53765-4485","state":"Arizona","country":"Northern Mariana Islands"},"phone":"506.651.6658 x581","website":"https://adriel.biz","company":{"name":"Bernier, Kirlin and Johnston"},"role":"root","status":"deleted","lastLogin":"2017-08-24T09:33:48.685Z"},{"id":"user23","name":"Garry Cormier","username":"Dock31","email":"Robert_Sanford58@hotmail.com","address":{"street":"6143 Guillermo Tunnel","city":"New Delfina","zip":"67974","state":"Alaska","country":"Latvia"},"phone":"1-360-940-9156 x94358","role":"admin","status":"active","lastLogin":"2018-02-04T18:27:23.677Z"},{"id":"user24","name":"Audreanne Wehner","username":"Kayley.Cummerata56","email":"Darrell.Breitenberg@yahoo.com","address":{"street":"520 Kiana Fork","city":"Lake Coy","zip":"82014-9132","state":"Missouri","country":"Poland"},"phone":"1-976-439-3572","website":"https://icie.info","role":"user","status":"active","lastLogin":"2018-02-04T09:46:25.176Z"},{"id":"user25","name":"Friedrich Kozey","username":"Julien.Larson6","address":{"street":"7499 Crooks Throughway","city":"Adelemouth","zip":"56311","state":"Missouri","country":"Malta"},"phone":"744.715.2889","website":"http://makenzie.biz","role":"user","status":"active","lastLogin":"2017-12-27T14:32:00.919Z"},{"id":"user26","name":"Mathew Torp","username":"Virginie.Orn95","email":"Maximillia.Kohler98@hotmail.com","address":{"street":"8418 Esther Cliff","city":"Bernierfurt","zip":"70803","state":"Missouri","country":"Argentina"},"company":{"name":"Gerlach - Mayert"},"role":"user","status":"active","lastLogin":"2017-09-16T08:36:44.691Z"},{"id":"user27","name":"Alexandrea Price","username":"Flossie.VonRueden","email":"Jadyn_Kohler@hotmail.com","address":{"street":"88873 Quitzon Squares","city":"Beahanmouth","zip":"55686","state":"South Dakota","country":"Sierra Leone"},"website":"http://torrey.net","company":{"name":"Dare - Lowe"},"role":"user","status":"active","lastLogin":"2017-10-28T08:44:58.987Z"},{"id":"user28","name":"Adolph Botsford","username":"Myrtice.Gottlieb66","email":"Raleigh56@yahoo.com","address":{"street":"0956 Eloisa Port","city":"Ashtynbury","zip":"87846","state":"Louisiana","country":"Gibraltar"},"website":"http://anais.net","role":"user","status":"pending","lastLogin":"2018-02-04T11:04:54.788Z"},{"id":"user29","name":"Kyla O'Hara","username":"Gennaro54","address":{"street":"59790 Rozella Overpass","city":"East Brooksstad","zip":"54814-6220","state":"Texas","country":"Liberia"},"role":"user","status":"deleted","lastLogin":"2018-01-09T13:35:31.293Z"},{"id":"user30","name":"Aric Pollich","username":"Matilde.Rice52","address":{"street":"685 Blaise Place","city":"South Christina","zip":"12151-0320","state":"Iowa","country":"Northern Mariana Islands"},"role":"user","status":"active","lastLogin":"2017-08-02T06:04:23.138Z"},{"id":"user31","name":"Alison Schroeder","username":"Julian.Jacobson","email":"Xavier.Rowe94@gmail.com","address":{"street":"21410 Tromp Island","city":"Ryanside","zip":"91714-5297","state":"Hawaii","country":"Malta"},"role":"user","status":"active","lastLogin":"2018-02-04T13:02:45.774Z"},{"id":"user32","name":"Greta Lowe","username":"Stanley.Witting","email":"Carolyn.Pfannerstill39@gmail.com","address":{"street":"316 Laila Land","city":"Bergefurt","zip":"80949","state":"Oregon","country":"Solomon Islands"},"phone":"1-071-031-5085 x2548","company":{"name":"Murray, Ortiz and Rice"},"role":"user","status":"deleted","lastLogin":"2018-02-04T06:13:13.588Z"},{"id":"user33","name":"Arjun Kuhn","username":"Eulalia25","email":"Brian.Waters33@hotmail.com","address":{"street":"717 Karolann Crescent","city":"Smithville","zip":"56560","state":"Nevada","country":"Saint Lucia"},"phone":"(005) 267-9027 x502","role":"user","status":"active","lastLogin":"2018-02-05T01:06:20.085Z"},{"id":"user34","name":"Freeman Windler","username":"Emmy87","email":"Carmela_Barton43@gmail.com","address":{"street":"69684 Little Squares","city":"Gagetown","zip":"82784-8574","state":"Georgia","country":"Cook Islands"},"phone":"(498) 179-8528 x80795","role":"user","status":"deleted","lastLogin":"2017-07-03T04:40:28.323Z"},{"id":"user35","name":"Royce Lynch","username":"Bertram_Rath","email":"Genesis.Effertz30@gmail.com","address":{"street":"5972 Leuschke Valley","city":"South Pabloview","zip":"51071-6994","state":"Wyoming","country":"Singapore"},"phone":"665.451.8619 x7384","website":"https://milo.org","company":{"name":"Batz Inc"},"role":"user","status":"pending","lastLogin":"2018-02-04T10:42:10.699Z"},{"id":"user36","name":"Carey Klocko","username":"Branson_Moore17","address":{"street":"29971 Abshire Street","city":"West Maggiemouth","zip":"07530-2386","state":"Maryland","country":"Turkey"},"website":"http://maurine.name","role":"user","status":"active","lastLogin":"2018-02-04T09:59:22.449Z"},{"id":"user37","name":"Jared Feest","username":"Mathew51","email":"Ari3@yahoo.com","address":{"street":"9477 Narciso Plain","city":"Francomouth","zip":"22517","state":"Tennessee","country":"Honduras"},"website":"https://ayden.com","role":"user","status":"active","lastLogin":"2018-02-04T05:23:06.767Z"},{"id":"user38","name":"Tracey Kohler","username":"Tanya20","email":"Elenor.OConner42@yahoo.com","address":{"street":"814 Spencer Rest","city":"Mosciskiton","zip":"07159-2456","state":"Idaho","country":"Martinique"},"company":{"name":"Block Inc"},"role":"admin","status":"pending","lastLogin":"2017-09-28T09:41:59.521Z"},{"id":"user39","name":"Annamarie Roberts","username":"Garrick.Grimes","email":"Dariana63@hotmail.com","address":{"street":"695 Darlene Plains","city":"Cummingsfort","zip":"82908","state":"Nevada","country":"Bahrain"},"role":"admin","status":"pending","lastLogin":"2018-02-04T09:44:54.832Z"},{"id":"user40","name":"Lonny Witting","username":"Avery_Cormier","address":{"street":"9478 Flavio Prairie","city":"Nicklausville","zip":"62721-5291","state":"Maine","country":"Andorra"},"phone":"435-223-6083","website":"http://brooks.net","company":{"name":"Krajcik - Heaney"},"role":"user","status":"pending","lastLogin":"2018-02-04T23:45:10.099Z"},{"id":"user41","name":"Adah Cummerata","username":"Liliana.Beer15","email":"Antonette.Koepp93@yahoo.com","address":{"street":"5903 Rocky Port","city":"North Shawn","zip":"57753","state":"Montana","country":"Guernsey"},"website":"https://maritza.name","company":{"name":"Osinski - Hane"},"role":"user","status":"active","lastLogin":"2018-02-04T10:33:27.787Z"},{"id":"user42","name":"Madelynn Funk","username":"Deonte_Bartoletti","email":"Jackeline.Flatley11@hotmail.com","address":{"street":"0705 Schroeder Centers","city":"Shanonview","zip":"37819-8071","state":"Ohio","country":"Mongolia"},"phone":"(183) 783-0585 x134","website":"http://haylie.net","role":"root","status":"active","lastLogin":"2017-02-16T17:15:41.533Z"},{"id":"user43","name":"Lavada Monahan","username":"Mohammed_Cartwright73","address":{"street":"630 McGlynn Creek","city":"Lake Myrnastad","zip":"38749","state":"Utah","country":"Guadeloupe"},"phone":"687.393.6767 x13305","role":"user","status":"active","lastLogin":"2017-05-06T01:22:44.446Z"},{"id":"user44","name":"Saul Kassulke","username":"Nelda.Beer","email":"Jerrold_Maggio84@gmail.com","address":{"street":"528 Beier Stream","city":"Streichfort","zip":"32049-8557","state":"North Carolina","country":"Antarctica (the territory South of 60 deg S)"},"phone":"485.074.0564 x23233","website":"https://leonora.name","role":"admin","status":"active","lastLogin":"2017-05-07T16:59:15.349Z"},{"id":"user45","name":"Jerrod Hirthe","username":"Urban.Oberbrunner","address":{"street":"30910 Elenora Branch","city":"Mayertton","zip":"80043","state":"Maine","country":"Niue"},"phone":"791.175.8309 x83835","role":"user","status":"active","lastLogin":"2018-02-04T16:23:44.525Z"},{"id":"user46","name":"Maymie Leffler","username":"Ethelyn_Hyatt","email":"Alisha_Abbott91@gmail.com","address":{"street":"1882 Marquise Key","city":"South Heathbury","zip":"90434-0043","state":"Rhode Island","country":"Slovakia (Slovak Republic)"},"phone":"687.328.0729","role":"user","status":"active","lastLogin":"2018-02-04T20:17:10.690Z"},{"id":"user47","name":"Maximillia Wolf","username":"Minnie_Parisian","email":"Timmy4@hotmail.com","address":{"street":"22788 Katelyn Spurs","city":"East Watsonfurt","zip":"16290-8159","state":"Montana","country":"Namibia"},"website":"http://toni.com","company":{"name":"Homenick - Oberbrunner"},"role":"admin","status":"pending","lastLogin":"2017-05-02T18:52:20.953Z"},{"id":"user48","name":"Rosella Ferry","username":"Cortez_Yundt8","email":"Cyril_Leffler89@hotmail.com","address":{"street":"55813 Euna Creek","city":"Destanyberg","zip":"51720-8174","state":"Vermont","country":"Tajikistan"},"role":"admin","status":"active","lastLogin":"2018-01-27T14:01:39.993Z"},{"id":"user49","name":"Ashleigh Stokes","username":"Ebony_Zemlak80","address":{"street":"0587 Rogahn Trail","city":"Edwinland","zip":"06169-7211","state":"Virginia","country":"Slovenia"},"role":"user","status":"active","lastLogin":"2018-02-05T01:20:57.047Z"},{"id":"user50","name":"Stephan Turcotte","username":"Hester48","email":"Walton64@hotmail.com","address":{"street":"2963 Williamson Port","city":"Ludwigview","zip":"41508","state":"Alaska","country":"Mali"},"phone":"164.764.7555","role":"user","status":"active","lastLogin":"2017-07-25T01:31:54.559Z"},{"id":"user51","name":"Viviane Metz","username":"Serenity.Abshire","address":{"street":"99439 Kevon Parkway","city":"East Quintonburgh","zip":"17991-0063","state":"Delaware","country":"Albania"},"role":"user","status":"active","lastLogin":"2017-05-26T01:09:26.700Z"},{"id":"user52","name":"Khalid Bartoletti","username":"Kelley_Rau39","email":"Clarabelle86@hotmail.com","address":{"street":"85250 Britney Land","city":"Port Normaview","zip":"00686-6720","state":"South Carolina","country":"Venezuela"},"phone":"(576) 203-8764 x966","company":{"name":"Parisian, Wyman and Cassin"},"role":"user","status":"active","lastLogin":"2017-08-27T16:50:53.423Z"},{"id":"user53","name":"Sibyl Gusikowski","username":"Beryl_Barrows","address":{"street":"3495 Abshire Lodge","city":"South Noah","zip":"33213-4394","state":"Massachusetts","country":"Pitcairn Islands"},"phone":"602-689-8833","website":"https://kolby.org","role":"admin","status":"active","lastLogin":"2017-06-21T10:25:27.697Z"},{"id":"user54","name":"Ashly Skiles","username":"Pauline.Tremblay","address":{"street":"840 Jaskolski Union","city":"Monicahaven","zip":"34596","state":"Arkansas","country":"Myanmar"},"role":"user","status":"deleted","lastLogin":"2017-07-08T21:57:48.620Z"},{"id":"user55","name":"Pearl Haley","username":"Leola45","email":"Kristian_Ledner34@hotmail.com","address":{"street":"885 Greg Island","city":"Veldafurt","zip":"40186-1321","state":"Utah","country":"Nicaragua"},"role":"user","status":"active","lastLogin":"2017-10-11T02:17:33.545Z"},{"id":"user56","name":"Matilde Schamberger","username":"Cathrine46","email":"Ferne_Howe17@yahoo.com","address":{"street":"048 Rudy Rapids","city":"South Rafaelafort","zip":"14561-8128","state":"Alaska","country":"Brunei Darussalam"},"phone":"(809) 516-4232 x29566","role":"user","status":"active","lastLogin":"2017-06-29T16:01:25.255Z"},{"id":"user57","name":"Waylon Armstrong","username":"Hilda_Toy69","address":{"street":"495 Casper Loop","city":"Lake Reubenchester","zip":"94817-7309","state":"Virginia","country":"Mozambique"},"phone":"668-288-8027 x4154","company":{"name":"D'Amore and Sons"},"role":"root","status":"pending","lastLogin":"2018-02-04T22:32:45.479Z"},{"id":"user58","name":"Mose Krajcik","username":"Frank.Grant","email":"Yasmine.Runolfsson23@hotmail.com","address":{"street":"7153 Satterfield Pike","city":"South Miracle","zip":"32425-8956","state":"Colorado","country":"Botswana"},"phone":"880-817-1498 x790","website":"http://jennings.net","company":{"name":"Terry and Sons"},"role":"user","status":"deleted","lastLogin":"2018-02-04T16:48:29.442Z"},{"id":"user59","name":"Floy Schneider","username":"Otha19","email":"Rosalyn56@hotmail.com","address":{"street":"7776 Maegan Cliffs","city":"Ryleeside","zip":"94041-1084","state":"Delaware","country":"Mongolia"},"phone":"(844) 557-5000 x371","role":"user","status":"active","lastLogin":"2017-02-21T09:14:38.296Z"},{"id":"user60","name":"Elouise Grady","username":"Alfredo.Beer","email":"Theodore_Hansen@gmail.com","address":{"street":"3007 Mueller Pine","city":"East Shawnhaven","zip":"57588-4970","state":"Minnesota","country":"Libyan Arab Jamahiriya"},"phone":"(923) 808-4957","role":"user","status":"active","lastLogin":"2018-01-23T21:50:47.892Z"},{"id":"user61","name":"Elna Auer","username":"Frieda.Flatley38","address":{"street":"970 Zelda Ports","city":"Port Maxineshire","zip":"32207","state":"Delaware","country":"Kazakhstan"},"role":"user","status":"active","lastLogin":"2017-03-26T23:29:44.475Z"},{"id":"user62","name":"Sylvan Berge","username":"Kassandra_Wolf8","address":{"street":"4542 Bartell Freeway","city":"Amosberg","zip":"39230-9587","state":"Delaware","country":"Seychelles"},"phone":"183-414-5578 x008","website":"https://skylar.org","role":"user","status":"active","lastLogin":"2018-02-04T05:17:37.036Z"},{"id":"user63","name":"Carolina Schroeder","username":"Jacklyn.Carroll0","address":{"street":"461 Bert Place","city":"Dillanport","zip":"04694","state":"Florida","country":"Turks and Caicos Islands"},"website":"https://gianni.biz","role":"root","status":"pending","lastLogin":"2018-02-04T11:52:00.572Z"},{"id":"user64","name":"Wilford Wuckert","username":"Merle.Murazik68","email":"Eda.Brown22@hotmail.com","address":{"street":"8495 Becker Square","city":"Corkerymouth","zip":"74082-2966","state":"South Carolina","country":"Ireland"},"role":"user","status":"active","lastLogin":"2018-02-05T03:12:36.030Z"},{"id":"user65","name":"Ludie Schuster","username":"Grace4","email":"Newell.Greenfelder26@hotmail.com","address":{"street":"2947 Waters Corner","city":"Breannaborough","zip":"25759-5585","state":"South Carolina","country":"Chile"},"website":"http://marianne.com","role":"user","status":"active","lastLogin":"2017-05-20T11:08:28.964Z"},{"id":"user66","name":"Erick Dickinson","username":"Noah.Cassin46","email":"Lula.Senger@yahoo.com","address":{"street":"70370 Harvey Prairie","city":"South Vadachester","zip":"05757-0872","state":"New Mexico","country":"Moldova"},"phone":"1-613-689-1455","website":"http://larue.net","role":"admin","status":"active","lastLogin":"2018-01-02T05:06:43.641Z"},{"id":"user67","name":"Granville Lowe","username":"Ciara.Hettinger","email":"Dale54@gmail.com","address":{"street":"050 Marks Parkways","city":"Revaton","zip":"13586-5724","state":"North Carolina","country":"Burundi"},"phone":"399-556-4406","role":"root","status":"active","lastLogin":"2018-02-04T18:21:49.661Z"},{"id":"user68","name":"Jacquelyn Schiller","username":"Nathen_Kihn78","email":"Cristal98@yahoo.com","address":{"street":"3418 Marilou Isle","city":"Lake Ardith","zip":"61383","state":"South Carolina","country":"Kyrgyz Republic"},"website":"http://marta.biz","role":"user","status":"pending","lastLogin":"2017-04-29T23:27:06.090Z"},{"id":"user69","name":"Soledad Vandervort","username":"Francisco.Hand","email":"Laurianne77@hotmail.com","address":{"street":"7549 Dach Fork","city":"Eichmannton","zip":"06693","state":"New Jersey","country":"Saint Kitts and Nevis"},"phone":"1-822-824-3217","website":"http://oran.name","company":{"name":"Ortiz - Wunsch"},"role":"admin","status":"active","lastLogin":"2017-10-08T09:57:20.180Z"},{"id":"user70","name":"Aliyah Gulgowski","username":"Tristian86","address":{"street":"89253 Eveline Trafficway","city":"South Providenci","zip":"04457-6583","state":"Ohio","country":"South Georgia and the South Sandwich Islands"},"role":"user","status":"active","lastLogin":"2018-02-04T15:15:27.363Z"},{"id":"user71","name":"Leonora Macejkovic","username":"Dee16","email":"Fabiola19@gmail.com","address":{"street":"041 Emmerich Port","city":"North Abraham","zip":"49874-1339","state":"Nebraska","country":"Dominican Republic"},"phone":"(241) 130-5981","website":"http://pamela.info","company":{"name":"Hickle, Hane and Witting"},"role":"user","status":"active","lastLogin":"2017-06-08T14:16:33.592Z"},{"id":"user72","name":"Kari Kuhn","username":"Melyna_Cremin96","email":"Bart.McKenzie@gmail.com","address":{"street":"149 Nova Trail","city":"Christyport","zip":"09297","state":"South Dakota","country":"San Marino"},"role":"admin","status":"active","lastLogin":"2018-02-04T11:47:09.818Z"},{"id":"user73","name":"Schuyler Feil","username":"Andre.Huels","address":{"street":"53685 Nico Squares","city":"East Freemanhaven","zip":"57804-2965","state":"Mississippi","country":"Bouvet Island (Bouvetoya)"},"phone":"922.116.4940 x749","website":"http://dagmar.biz","role":"user","status":"active","lastLogin":"2018-02-05T03:23:25.197Z"},{"id":"user74","name":"Dario Reichert","username":"Blaze50","email":"Alyson_Runte67@yahoo.com","address":{"street":"7160 Shaniya Loaf","city":"Kianaville","zip":"94009","state":"Alabama","country":"Equatorial Guinea"},"phone":"424.179.9756 x3423","website":"http://jakayla.org","company":{"name":"O'Conner - Powlowski"},"role":"user","status":"active","lastLogin":"2017-08-22T15:40:25.450Z"},{"id":"user75","name":"Emerson Botsford","username":"Kaycee73","email":"Bella91@gmail.com","address":{"street":"705 Brown Motorway","city":"Gleasonport","zip":"92431-5464","state":"Tennessee","country":"Ukraine"},"website":"https://kiana.net","role":"user","status":"active","lastLogin":"2017-07-20T06:04:57.916Z"},{"id":"user76","name":"Sally Murazik","username":"Jaylin66","email":"Viva_OReilly93@gmail.com","address":{"street":"67322 Candice Greens","city":"East Keelymouth","zip":"19137-8315","state":"Missouri","country":"Zambia"},"website":"https://raina.net","role":"user","status":"active","lastLogin":"2018-01-10T16:25:26.065Z"},{"id":"user77","name":"Ivory Hegmann","username":"Dayne_Herzog58","email":"Verda70@yahoo.com","address":{"street":"2998 Goldner Crescent","city":"Weberside","zip":"60594-4952","state":"Montana","country":"Russian Federation"},"phone":"(354) 752-5165 x301","role":"user","status":"active","lastLogin":"2017-10-25T11:23:09.994Z"},{"id":"user78","name":"Nyah Wolff","username":"Kody.Monahan31","email":"Angeline26@hotmail.com","address":{"street":"75891 Mario Fields","city":"Port Emmanuelmouth","zip":"08769","state":"Kansas","country":"Jamaica"},"website":"http://colin.net","role":"root","status":"active","lastLogin":"2018-02-05T00:41:43.901Z"},{"id":"user79","name":"Jamir Schinner","username":"Emely92","email":"Kaleigh11@hotmail.com","address":{"street":"7306 Reinger Greens","city":"East Aronville","zip":"00763","state":"Michigan","country":"Christmas Island"},"phone":"(403) 748-7273 x83021","company":{"name":"Turner, Moen and Mohr"},"role":"admin","status":"active","lastLogin":"2018-02-04T17:03:34.471Z"},{"id":"user80","name":"Darby Okuneva","username":"Amely.Kassulke86","address":{"street":"5241 Carter Mission","city":"Port Careytown","zip":"55607-8689","state":"Oklahoma","country":"Uganda"},"phone":"(592) 325-7324 x3285","website":"https://tamia.info","role":"user","status":"active","lastLogin":"2017-11-02T11:31:58.125Z"},{"id":"user81","name":"Rebekah Pfeffer","username":"Adelia.Rice","email":"Angelina_Sipes@yahoo.com","address":{"street":"29795 Goldner Throughway","city":"Shyannechester","zip":"44968-0220","state":"South Dakota","country":"Singapore"},"company":{"name":"Kozey and Sons"},"role":"user","status":"active","lastLogin":"2018-02-04T09:28:08.893Z"},{"id":"user82","name":"Lucius Padberg","username":"Jordon.Vandervort47","email":"Hadley.Cormier@hotmail.com","address":{"street":"8061 Eula Brook","city":"New Madonnatown","zip":"66161-2442","state":"Alabama","country":"Liberia"},"website":"http://cathryn.info","company":{"name":"Gutkowski - MacGyver"},"role":"admin","status":"pending","lastLogin":"2018-02-04T21:46:56.142Z"},{"id":"user83","name":"Jailyn Douglas","username":"Constance.Little","address":{"street":"453 Ayla Neck","city":"Reubenhaven","zip":"96236-1587","state":"Virginia","country":"El Salvador"},"phone":"639-336-1337 x603","role":"user","status":"pending","lastLogin":"2017-08-20T02:39:11.939Z"},{"id":"user84","name":"Oswaldo Price","username":"Phoebe13","email":"Keon28@yahoo.com","address":{"street":"37386 Littel Ville","city":"Carolyneport","zip":"56445","state":"Kentucky","country":"Mauritius"},"phone":"457.036.3825","website":"http://sandrine.org","role":"user","status":"pending","lastLogin":"2017-04-04T15:55:34.653Z"},{"id":"user85","name":"Rosetta Kreiger","username":"Alexandrine.Roob54","email":"Adah79@yahoo.com","address":{"street":"033 Briana Meadows","city":"Whitemouth","zip":"00837","state":"Michigan","country":"Lao People's Democratic Republic"},"phone":"(522) 282-4001 x14201","role":"user","status":"pending","lastLogin":"2017-07-15T17:07:05.173Z"},{"id":"user86","name":"Oswald Erdman","username":"Harmony.Schoen72","email":"Brandy73@hotmail.com","address":{"street":"45829 Aubree Points","city":"Elinorebury","zip":"01369","state":"Ohio","country":"Vanuatu"},"phone":"698.162.1878","website":"https://raphael.net","company":{"name":"Crist - Reichel"},"role":"user","status":"active","lastLogin":"2018-02-04T07:39:09.955Z"},{"id":"user87","name":"Melissa Wuckert","username":"Reva69","email":"Reinhold30@yahoo.com","address":{"street":"4661 Robel Meadow","city":"Port Abigayleshire","zip":"87845-1459","state":"Utah","country":"Afghanistan"},"phone":"(791) 066-7233 x128","role":"user","status":"active","lastLogin":"2018-02-04T06:04:04.590Z"},{"id":"user88","name":"Darron Skiles","username":"Moshe_Fritsch83","email":"Alexzander_Glover40@gmail.com","address":{"street":"4051 Dina Way","city":"Cullenburgh","zip":"99044","state":"Texas","country":"Guernsey"},"phone":"089.279.2687 x59192","role":"user","status":"pending","lastLogin":"2017-03-03T16:11:40.800Z"},{"id":"user89","name":"Emmett Cartwright","username":"Marcelino62","email":"Clay_Fahey73@hotmail.com","address":{"street":"81952 Noble Ranch","city":"Spinkaton","zip":"38337","state":"Iowa","country":"Cook Islands"},"website":"http://merlin.net","role":"admin","status":"deleted","lastLogin":"2018-02-04T16:03:45.078Z"},{"id":"user90","name":"Arnulfo Parisian","username":"Winfield14","address":{"street":"114 Maude Plaza","city":"Madieshire","zip":"90803-6685","state":"Oklahoma","country":"Bahamas"},"phone":"044.169.3648 x31581","website":"http://dusty.name","role":"root","status":"active","lastLogin":"2017-11-03T02:23:11.414Z"},{"id":"user91","name":"Lorenz Schaefer","username":"Zula.Romaguera","address":{"street":"18788 Davin Green","city":"Riceland","zip":"65348","state":"Oklahoma","country":"Montserrat"},"phone":"031.534.9927","website":"https://jaleel.name","company":{"name":"Ritchie - Nikolaus"},"role":"user","status":"pending","lastLogin":"2018-02-04T14:32:26.680Z"},{"id":"user92","name":"Damon O'Keefe","username":"Zachery_Herman93","email":"Carli.Cummerata18@yahoo.com","address":{"street":"28331 Batz Ranch","city":"Conroyhaven","zip":"27459","state":"Georgia","country":"Guatemala"},"phone":"793-369-9803","website":"http://khalid.net","role":"user","status":"active","lastLogin":"2018-02-04T07:52:43.067Z"},{"id":"user93","name":"Raymond Gibson","username":"Mack.Dibbert22","address":{"street":"294 Rey Landing","city":"Gorczanyport","zip":"05450-8602","state":"Massachusetts","country":"Ecuador"},"phone":"091.165.7214 x301","role":"user","status":"active","lastLogin":"2018-02-04T22:23:05.069Z"},{"id":"user94","name":"Jayce Mraz","username":"Riley_Kris18","email":"Oren.Jacobi@yahoo.com","address":{"street":"65864 Kling Centers","city":"Sydneyfort","zip":"45300","state":"Wisconsin","country":"Niue"},"role":"user","status":"active","lastLogin":"2018-02-04T23:21:30.427Z"},{"id":"user95","name":"Lucinda Lindgren","username":"Moshe.Carroll45","address":{"street":"680 Metz Crossing","city":"McClureberg","zip":"77275-5634","state":"Minnesota","country":"Philippines"},"company":{"name":"O'Connell - Ritchie"},"role":"admin","status":"pending","lastLogin":"2018-02-04T11:28:15.626Z"},{"id":"user96","name":"Cleveland Hickle","username":"Maeve.OKeefe","address":{"street":"9061 Jast Ridge","city":"Dinofort","zip":"38037","state":"Georgia","country":"Solomon Islands"},"role":"user","status":"pending","lastLogin":"2017-08-09T11:42:30.063Z"},{"id":"user97","name":"Juana Crona","username":"Bartholome91","address":{"street":"853 Mante Falls","city":"Wandastad","zip":"10477","state":"Florida","country":"Tanzania"},"phone":"1-036-630-6422","website":"http://noemi.org","role":"user","status":"active","lastLogin":"2018-02-04T23:36:20.205Z"},{"id":"user98","name":"Demarcus Kertzmann","username":"Devin76","email":"Chadrick27@yahoo.com","address":{"street":"885 Lambert Crest","city":"West Maximo","zip":"00464-6428","state":"Iowa","country":"Norfolk Island"},"phone":"(488) 273-3199","role":"root","status":"active","lastLogin":"2018-02-05T01:40:23.135Z"},{"id":"user99","name":"Neoma Will","username":"Kaylah_Grady","address":{"street":"4081 Nona Square","city":"New Adalbertomouth","zip":"05793-0184","state":"Ohio","country":"Guyana"},"phone":"719.048.5688 x85024","website":"https://ewell.biz","role":"root","status":"active","lastLogin":"2018-02-04T05:28:02.754Z"}]

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

scope: {
qbTable: '=',
stickyThead: '<',
stickyTfoot: '<'
qbTable: '=?',
stickyThead: '<?',
stickyTfoot: '<?'
},

@@ -38,2 +38,16 @@ restrict: 'AC',

$ctrl.$broadcast = function (msg) {
var _console;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
(_console = console).log.apply(_console, ['BROADCAST DOWN', msg].concat(args));
$scope.$broadcast.apply($scope, [msg].concat(args)); // Rebind broadcast so its accessible from children
};
$ctrl.$on = function (event, cb) {
return $scope.$on(event, cb);
};
$ctrl.setField = function (field, value) {

@@ -82,2 +96,9 @@ if (value == undefined) {

* @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
*
* @example
* <thead>
* <tr>
* <td qb-col="name" sortable>Name</td>
* </tr>
* </thead>
*/

@@ -93,3 +114,3 @@ .directive('qbCol', function () {

transclude: true,
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
controller: ['$attrs', '$element', '$scope', 'qbTableSettings', function controller($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;

@@ -99,2 +120,9 @@

// Sanity checks {{{
var unSanityChecks = $scope.$watchGroup(['qbTable', 'sortable'], function () {
if ($attrs.sortable === '' && !$scope.qbTable) console.warn('Added qb-col + sortable onto element', $element, 'but no qb-table query has been assigned on the table element!');
unSanityChecks();
});
// }}}
// Sort functionality {{{

@@ -144,2 +172,84 @@ $scope.canSort = false; // True if either sortable has a specific value or is at least present

// qbCell (directive) {{{
/**
* Directive for cell elements within a table
* @param {boolean} selector Whether the cell should act as a select / unselect prompt, if any value bind to this as the selection variable
* @param {Object} ^qbTable.qbTable The query Object to mutate
* @example
* <td qb-cell selector="row.selected"></td>
*/
.directive('qbCell', function () {
return {
scope: {
selector: '=?'
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: ['$attrs', '$element', '$scope', '$timeout', 'qbTableSettings', function controller($attrs, $element, $scope, $timeout, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
// Meta selection support {{{
// A cell `isMeta` if it detects its located in the `thead` section of a table
$scope.isMeta = $element.parents('thead').length > 0;
// }}}
// Selection support {{{
$scope.isSelector = 'selector' in $attrs;
$scope.$watch('selector', function () {
if ($scope.isSelector) {
$element.toggleClass('selector', $scope.isSelector);
}
if ($scope.isSelector && !$scope.isMeta) {
$element.parents('tr').toggleClass('selected', !!$scope.selector);
$element.find('input[type=checkbox]').prop('checked', !!$scope.selector);
}
});
// Also respond to clicking anywhere in the 'TD' tag
$element.on('click', function (e) {
if (e.target.tagName != 'INPUT') e.preventDefault(); // Clicking on the background should also disable bubbling
$scope.$apply(function () {
return $scope.selector = !$scope.selector;
});
});
// Handle meta interaction
$scope.metaSelect = function (type) {
return $scope.qbTable.$broadcast('qbTableCellSelect', type);
};
// Bind to event listener and respond to selection directives from meta element
if ($scope.isSelector) {
$timeout(function () {
return $scope.qbTable.$on('qbTableCellSelect', function (e, type) {
switch (type) {
case 'all':
$scope.selector = true;break;
case 'invert':
$scope.selector = !$scope.selector;break;
case 'none':
$scope.selector = false;break;
default:
throw new Error('Unknown selection type: ' + type);
}
});
});
}
// }}}
// Style up the selector
$element.addClass('qb-cell');
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<ng-transclude></ng-transclude>\n\t\t<div ng-if="isSelector && isMeta" class="btn-group">\n\t\t\t<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t<input type="checkbox"/>\n\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t</a>\n\t\t\t<ul class="dropdown-menu">\n\t\t\t\t<li><a ng-click="metaSelect(\'all\')">All</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'invert\')">Invert</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'none\')">None</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t\t<div ng-if="isSelector && !isMeta" class="checkbox">\n\t\t\t<label>\n\t\t\t\t<input type="checkbox"/>\n\t\t\t</label>\n\t\t</div>\n\t'
};
})
// }}}
// qbPagination {{{

@@ -146,0 +256,0 @@ /**

@@ -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:"=",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'}});
"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,n){var l=this;l.query=a.qbTable,l.$broadcast=function(t){for(var e,n=arguments.length,l=Array(n>1?n-1:0),r=1;r<n;r++)l[r-1]=arguments[r];(e=console).log.apply(e,["BROADCAST DOWN",t].concat(l)),a.$broadcast.apply(a,[t].concat(l))},l.$on=function(t,e){return a.$on(t,e)},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:a.qbTable[t]=e}else delete l.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","$element","$scope","qbTableSettings",function(t,e,a,n){var l=this;a.qbTableSettings=n;var r=a.$watchGroup(["qbTable","sortable"],function(){""!==t.sortable||a.qbTable||console.warn("Added qb-col + sortable onto element",e,"but no qb-table query has been assigned on the table element!"),r()});a.canSort=!1,a.isSorted=!1,l.$onInit=function(){return a.canSort=a.sortable||""===t.sortable},a.$watch("qbTable.query.sort",function(t){var e=a.sortable||a.q;t?angular.isArray(t)&&t.some(function(t){return t==e})||t==e?a.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+e})||t=="-"+e?a.isSorted="desc":a.isSorted=!1:a.isSorted=!1}),a.toggleSort=function(){a.sortable?a.qbTable.setField("sort",a.sortable):a.qbCol&&""===t.sortable&&a.qbTable.setField("sort",a.qbCol)}}],link:function(t,e,a,n){t.qbTable=n},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("qbCell",function(){return{scope:{selector:"=?"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,a,n,l){a.qbTableSettings=l,a.isMeta=e.parents("thead").length>0,a.isSelector="selector"in t,a.$watch("selector",function(){a.isSelector&&e.toggleClass("selector",a.isSelector),a.isSelector&&!a.isMeta&&(e.parents("tr").toggleClass("selected",!!a.selector),e.find("input[type=checkbox]").prop("checked",!!a.selector))}),e.on("click",function(t){"INPUT"!=t.target.tagName&&t.preventDefault(),a.$apply(function(){return a.selector=!a.selector})}),a.metaSelect=function(t){return a.qbTable.$broadcast("qbTableCellSelect",t)},a.isSelector&&n(function(){return a.qbTable.$on("qbTableCellSelect",function(t,e){switch(e){case"all":a.selector=!0;break;case"invert":a.selector=!a.selector;break;case"none":a.selector=!1;break;default:throw new Error("Unknown selection type: "+e)}})}),e.addClass("qb-cell")}],link:function(t,e,a,n){t.qbTable=n},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<div ng-if="isSelector && isMeta" class="btn-group">\n\t\t\t<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t<input type="checkbox"/>\n\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t</a>\n\t\t\t<ul class="dropdown-menu">\n\t\t\t\t<li><a ng-click="metaSelect(\'all\')">All</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'invert\')">Invert</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'none\')">None</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t\t<div ng-if="isSelector && !isMeta" class="checkbox">\n\t\t\t<label>\n\t\t\t\t<input type="checkbox"/>\n\t\t\t</label>\n\t\t</div>\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,n){t.qbTable=n},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'}});

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

scope: {
qbTable: '=',
stickyThead: '<',
stickyTfoot: '<'
qbTable: '=?',
stickyThead: '<?',
stickyTfoot: '<?'
},

@@ -516,2 +516,16 @@ restrict: 'AC',

$ctrl.$broadcast = function (msg) {
var _console;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
(_console = console).log.apply(_console, ['BROADCAST DOWN', msg].concat(args));
$scope.$broadcast.apply($scope, [msg].concat(args)); // Rebind broadcast so its accessible from children
};
$ctrl.$on = function (event, cb) {
return $scope.$on(event, cb);
};
$ctrl.setField = function (field, value) {

@@ -560,2 +574,9 @@ if (value == undefined) {

* @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
*
* @example
* <thead>
* <tr>
* <td qb-col="name" sortable>Name</td>
* </tr>
* </thead>
*/

@@ -571,3 +592,3 @@ .directive('qbCol', function () {

transclude: true,
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
controller: ['$attrs', '$element', '$scope', 'qbTableSettings', function controller($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;

@@ -577,2 +598,9 @@

// Sanity checks {{{
var unSanityChecks = $scope.$watchGroup(['qbTable', 'sortable'], function () {
if ($attrs.sortable === '' && !$scope.qbTable) console.warn('Added qb-col + sortable onto element', $element, 'but no qb-table query has been assigned on the table element!');
unSanityChecks();
});
// }}}
// Sort functionality {{{

@@ -622,2 +650,84 @@ $scope.canSort = false; // True if either sortable has a specific value or is at least present

// qbCell (directive) {{{
/**
* Directive for cell elements within a table
* @param {boolean} selector Whether the cell should act as a select / unselect prompt, if any value bind to this as the selection variable
* @param {Object} ^qbTable.qbTable The query Object to mutate
* @example
* <td qb-cell selector="row.selected"></td>
*/
.directive('qbCell', function () {
return {
scope: {
selector: '=?'
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: ['$attrs', '$element', '$scope', '$timeout', 'qbTableSettings', function controller($attrs, $element, $scope, $timeout, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
// Meta selection support {{{
// A cell `isMeta` if it detects its located in the `thead` section of a table
$scope.isMeta = $element.parents('thead').length > 0;
// }}}
// Selection support {{{
$scope.isSelector = 'selector' in $attrs;
$scope.$watch('selector', function () {
if ($scope.isSelector) {
$element.toggleClass('selector', $scope.isSelector);
}
if ($scope.isSelector && !$scope.isMeta) {
$element.parents('tr').toggleClass('selected', !!$scope.selector);
$element.find('input[type=checkbox]').prop('checked', !!$scope.selector);
}
});
// Also respond to clicking anywhere in the 'TD' tag
$element.on('click', function (e) {
if (e.target.tagName != 'INPUT') e.preventDefault(); // Clicking on the background should also disable bubbling
$scope.$apply(function () {
return $scope.selector = !$scope.selector;
});
});
// Handle meta interaction
$scope.metaSelect = function (type) {
return $scope.qbTable.$broadcast('qbTableCellSelect', type);
};
// Bind to event listener and respond to selection directives from meta element
if ($scope.isSelector) {
$timeout(function () {
return $scope.qbTable.$on('qbTableCellSelect', function (e, type) {
switch (type) {
case 'all':
$scope.selector = true;break;
case 'invert':
$scope.selector = !$scope.selector;break;
case 'none':
$scope.selector = false;break;
default:
throw new Error('Unknown selection type: ' + type);
}
});
});
}
// }}}
// Style up the selector
$element.addClass('qb-cell');
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<ng-transclude></ng-transclude>\n\t\t<div ng-if="isSelector && isMeta" class="btn-group">\n\t\t\t<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t<input type="checkbox"/>\n\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t</a>\n\t\t\t<ul class="dropdown-menu">\n\t\t\t\t<li><a ng-click="metaSelect(\'all\')">All</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'invert\')">Invert</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'none\')">None</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t\t<div ng-if="isSelector && !isMeta" class="checkbox">\n\t\t\t<label>\n\t\t\t\t<input type="checkbox"/>\n\t\t\t</label>\n\t\t</div>\n\t'
};
})
// }}}
// qbPagination {{{

@@ -624,0 +734,0 @@ /**

@@ -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:"=",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'}});
"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.$broadcast=function(t){for(var e,a=arguments.length,l=Array(a>1?a-1:0),i=1;i<a;i++)l[i-1]=arguments[i];(e=console).log.apply(e,["BROADCAST DOWN",t].concat(l)),n.$broadcast.apply(n,[t].concat(l))},l.$on=function(t,e){return n.$on(t,e)},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","$element","$scope","qbTableSettings",function(t,e,n,a){var l=this;n.qbTableSettings=a;var i=n.$watchGroup(["qbTable","sortable"],function(){""!==t.sortable||n.qbTable||console.warn("Added qb-col + sortable onto element",e,"but no qb-table query has been assigned on the table element!"),i()});n.canSort=!1,n.isSorted=!1,l.$onInit=function(){return n.canSort=n.sortable||""===t.sortable},n.$watch("qbTable.query.sort",function(t){var e=n.sortable||n.q;t?angular.isArray(t)&&t.some(function(t){return t==e})||t==e?n.isSorted="asc":angular.isArray(t)&&t.some(function(t){return t=="-"+e})||t=="-"+e?n.isSorted="desc":n.isSorted=!1:n.isSorted=!1}),n.toggleSort=function(){n.sortable?n.qbTable.setField("sort",n.sortable):n.qbCol&&""===t.sortable&&n.qbTable.setField("sort",n.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("qbCell",function(){return{scope:{selector:"=?"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,a,l){n.qbTableSettings=l,n.isMeta=e.parents("thead").length>0,n.isSelector="selector"in t,n.$watch("selector",function(){n.isSelector&&e.toggleClass("selector",n.isSelector),n.isSelector&&!n.isMeta&&(e.parents("tr").toggleClass("selected",!!n.selector),e.find("input[type=checkbox]").prop("checked",!!n.selector))}),e.on("click",function(t){"INPUT"!=t.target.tagName&&t.preventDefault(),n.$apply(function(){return n.selector=!n.selector})}),n.metaSelect=function(t){return n.qbTable.$broadcast("qbTableCellSelect",t)},n.isSelector&&a(function(){return n.qbTable.$on("qbTableCellSelect",function(t,e){switch(e){case"all":n.selector=!0;break;case"invert":n.selector=!n.selector;break;case"none":n.selector=!1;break;default:throw new Error("Unknown selection type: "+e)}})}),e.addClass("qb-cell")}],link:function(t,e,n,a){t.qbTable=a},template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<div ng-if="isSelector && isMeta" class="btn-group">\n\t\t\t<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">\n\t\t\t\t<input type="checkbox"/>\n\t\t\t\t<i class="fa fa-caret-down"></i>\n\t\t\t</a>\n\t\t\t<ul class="dropdown-menu">\n\t\t\t\t<li><a ng-click="metaSelect(\'all\')">All</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'invert\')">Invert</a></li>\n\t\t\t\t<li><a ng-click="metaSelect(\'none\')">None</a></li>\n\t\t\t</ul>\n\t\t</div>\n\t\t<div ng-if="isSelector && !isMeta" class="checkbox">\n\t\t\t<label>\n\t\t\t\t<input type="checkbox"/>\n\t\t\t</label>\n\t\t</div>\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'}});
{
"name": "@momsfriendlydevco/angular-ui-query-builder",
"version": "1.2.1",
"version": "1.2.3",
"description": "MongoDB format query-builder UI component for Angular",

@@ -22,3 +22,2 @@ "main": "dist/angular-ui-query-builder.js",

"babel-preset-es2015": "^6.24.1",
"body-parser": "^1.18.2",
"express": "^4.16.1",

@@ -25,0 +24,0 @@ "faker": "^4.1.0",

@@ -5,2 +5,9 @@ angular-ui-query-builder

This component comes in two parts:
1. An interactive UI element to edit a MongoDB query
2. Functionality to extend basic HTML tables with additional features (column sorting, filtering etc.) by changing the MongoDB query
[Demo](https://momsfriendlydevco.github.io/angular-ui-query-builder).

@@ -80,4 +87,4 @@

qb-table, qb-col, qb-pagination (directives)
--------------------------------------------
qb-table & qb-* (directives)
----------------------------
If using either the full JS release (`angular-ui-query-builder.js`) or the table add-on (`angular-ui-query-builder-tables.js`) additional functionality is provided for Tables including column setup, pagination and other functionality.

@@ -135,3 +142,82 @@

qb-col (directive)
------------------
Use on ` <td>` / `<th>` element within a `<thead>` to provide column level interaction.
```html
<table class="table table-bordered table-striped table-hover" qb-table="query">
<thead>
<tr>
<th qb-col="name" sortable>Name</th>
<th qb-col="username" sortable>Username</th>
<th qb-col="email" sortable>Email</th>
</tr>
</thead>
...
</table>
```
Valid attributes are:
| Attribute | Type | Description |
|----------------|-----------------------|-------------------------------------------------------------------------------------------|
| `qb-col` | `string` | The database field to link against in dotted notation |
| `sortable` | `boolean` or `string` | Indicates that this column can be sorted, if blank or boolean true this simply uses the `qb-col` value as the field to sort by, if its a string it uses that field instead |
qb-cell (directive)
-------------------
Use on ` <td>` / `<th>` element within a `<thead>` / `<thead>` to provide column level interaction.
If this element is within `<thead>` it adds table level meta functionality (such as controlling which rows are selected if `selector` is truthy). If its within a `<tbody>` it adds per-row functionality.
```html
<table class="table table-bordered table-striped table-hover" qb-table="query">
<thead>
...
</thead>
<tbody>
<tr ng-repeat="row in data track by row.id">
<td qb-cell selector="row.selected"></td>
<td>{{row.name}}</td>
</tr>
</tbody>
</table>
```
Valid attributes are:
| Attribute | Type | Description |
|----------------|----------|-------------------------------------------------------------------------------------------|
| `selector` | `Object` | Indicates the field to bind against + mutate when selection is toggled. This usually resembles something like `row.selected` |
qb-pagination
-------------
An element, usually found in the `<tfoot>` section of a table which provides general pagination functionality.
```html
<table class="table table-bordered table-striped table-hover" qb-table="query">
<thead>
...
</thead>
<tbody>
...
</tbody>
<tfoot>
<tr>
<td colspan="10">
<qb-pagination></qb-pagination>
</td>
</tr>
</tfoot>
</table>
```
This directive has no attributes.
TODO

@@ -159,5 +245,6 @@ ====

* [x] Sticky headers on scroll
* [x] Query-Builder intergration
* [x] Query-Builder integration
* [x] Sorting per-column
* [ ] Export to excel functionality
* [x] Sorting per-column
* [ ] Row selection
* [ ] Simple searching

@@ -164,0 +251,0 @@ * [ ] Freezing columns (low priority)

@@ -11,2 +11,13 @@ angular.module('angular-ui-query-builder')

},
export: {
defaults: {
format: 'xlsx',
},
formats: [
{id: 'xlsx', title: 'Excel (XLSX)'},
{id: 'csv', title: 'CSV'},
{id: 'json', title: 'JSON'},
{id: 'html', title: 'HTML (display in browser)'},
],
},
};

@@ -16,2 +27,30 @@ })

// qbTableUtilities (service) {{{
.service('qbTableUtilities', function() { return {
getSynopsis: query => {
var filters = _.keys(query).filter(i => !['sort', 'skip', 'limit', 'select'].includes(i));
return [
filters.length ? `${filters.length} filters` : 'All records',
query.sort
? (
query.sort.startsWith('-')
? `sorted by ${query.sort.substr(1)} (reverse order)`
: `sorted by ${query.sort}`
)
: null,
query.limit
? `limited to ${query.limit} rows`
: null,
query.offset
? `starting at record ${query.skip}`
: null,
query.select
? `selecting only ${query.select.length} columns`
: null,
].filter(i => i).join(', ');
},
}})
// }}}
// qbTable (directive) {{{

@@ -27,5 +66,5 @@ /**

scope: {
qbTable: '=',
stickyThead: '<',
stickyTfoot: '<',
qbTable: '=?',
stickyThead: '<?',
stickyTfoot: '<?',
},

@@ -37,2 +76,8 @@ restrict: 'AC',

$ctrl.$broadcast = (msg, ...args) => {
console.log('BROADCAST DOWN', msg, ...args);
$scope.$broadcast(msg, ...args); // Rebind broadcast so its accessible from children
};
$ctrl.$on = (event, cb) => $scope.$on(event, cb);
$ctrl.setField = (field, value) => {

@@ -72,2 +117,9 @@ if (value == undefined) { // Remove from query

* @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
*
* @example
* <thead>
* <tr>
* <td qb-col="name" sortable>Name</td>
* </tr>
* </thead>
*/

@@ -82,3 +134,3 @@ .directive('qbCol', function() { return {

transclude: true,
controller: function($attrs, $scope, qbTableSettings) {
controller: function($attrs, $element, $scope, qbTableSettings) {
var $ctrl = this;

@@ -88,2 +140,9 @@

// Sanity checks {{{
var unSanityChecks = $scope.$watchGroup(['qbTable', 'sortable'], ()=> {
if ($attrs.sortable === '' && !$scope.qbTable) console.warn('Added qb-col + sortable onto element', $element, 'but no qb-table query has been assigned on the table element!');
unSanityChecks();
});
// }}}
// Sort functionality {{{

@@ -93,6 +152,10 @@ $scope.canSort = false; // True if either sortable has a specific value or is at least present

$ctrl.$onInit = ()=> $scope.canSort = $scope.sortable || $attrs.sortable === '';
$ctrl.$onInit = ()=> {
$scope.canSort = $scope.sortable || $attrs.sortable === '';
$element.toggleClass('sortable', $scope.canSort);
};
$scope.$watch('qbTable.query.sort', sorter => {
var sortField = $scope.sortable || $scope.q;
var sortField = $scope.sortable || $scope.qbCol;
if (!sorter) {

@@ -123,2 +186,4 @@ $scope.isSorted = false;

// }}}
$element.addClass('qb-col');
},

@@ -129,10 +194,121 @@ link: function(scope, element, attrs, parentScope) {

template: `
<div class="qb-col-wrapper">
<ng-transclude></ng-transclude>
<a ng-if="canSort" ng-click="toggleSort()" class="qb-col-right">
<i class="{{
isSorted == 'asc' ? qbTableSettings.icons.sortAsc
: isSorted == 'desc' ? qbTableSettings.icons.sortDesc
: qbTableSettings.icons.sortNone
}}"></i>
</a>
</div>
`,
}})
// }}}
// qbCell (directive) {{{
/**
* Directive for cell elements within a table
* @param {boolean} selector Whether the cell should act as a select / unselect prompt, if any value bind to this as the selection variable
* @param {Object} ^qbTable.qbTable The query Object to mutate
*
* @emits qbTableCellSelectMeta Issued by the meta-selector element to peer selection elements that the selection criteria has changed. Called as (arg) where arg is 'all', 'none', 'invert'
* @emits qbTableCellSelect Issued by a regular selector element to broadcast its state has changed
* @emits qbTableCellSelectStatus Sent to one or more child elements as (array) to enquire their status, used to figure out if everything / partial / no items are selected. Each item is expected to add its status to `status` as a boolean
*
* @example
* <td qb-cell selector="row.selected"></td>
*/
.directive('qbCell', function() { return {
scope: {
selector: '=?',
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: function($attrs, $element, $scope, $timeout, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
// Meta selection support {{{
// A cell `isMeta` if it detects its located in the `thead` section of a table
$scope.isMeta = $element.parents('thead').length > 0;
if ($scope.isMeta) {
$timeout(()=> $scope.qbTable.$on('qbTableCellSelect', ()=> {
// Ask all children what their status is
var status = [];
$scope.qbTable.$broadcast('qbTableCellSelectStatus', status);
$scope.metaStatus =
status.every(i => i) ? 'all'
: status.some(i => i) ? 'some'
: 'none';
}));
}
// }}}
// Selection support {{{
$scope.isSelector = 'selector' in $attrs;
$scope.$watch('selector', ()=> {
if ($scope.isSelector) $element.toggleClass('selector', $scope.isSelector);
if ($scope.isSelector && !$scope.isMeta) $element.parents('tr').toggleClass('selected', !! $scope.selector);
});
// Respond to clicking anywhere in the 'TD' tag
if ($scope.isSelector && !$scope.isMeta) {
$element.on('click', e => $scope.$apply(()=> {
$scope.selector = !$scope.selector;
$scope.qbTable.$broadcast('qbTableCellSelect');
}));
}
// Handle meta interaction
$scope.metaSelect = type => $scope.qbTable.$broadcast('qbTableCellSelectMeta', type);
// Bind to event listener and respond to selection directives from meta element
if ($scope.isSelector && !$scope.isMeta) {
// If we're a standard per-row minion respond to certain events
$timeout(()=> {
$scope.qbTable.$on('qbTableCellSelectMeta', (e, type) => {
switch (type) {
case 'all': $scope.selector = true; break;
case 'invert': $scope.selector = !$scope.selector; break;
case 'none': $scope.selector = false; break;
default: throw new Error (`Unknown selection type: ${type}`);
}
$scope.qbTable.$broadcast('qbTableCellSelect'); // Trigger a recount of what is/isn't selected
});
$scope.qbTable.$on('qbTableCellSelectStatus', (e, status) => status.push($scope.selector));
});
}
// }}}
// Style up the selector
$element.addClass('qb-cell')
},
link: function(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: `
<ng-transclude></ng-transclude>
<a ng-if="canSort" ng-click="toggleSort()" class="pull-right">
<i class="{{
isSorted == 'asc' ? qbTableSettings.icons.sortAsc
: isSorted == 'desc' ? qbTableSettings.icons.sortDesc
: qbTableSettings.icons.sortNone
}}"></i>
</a>
<div ng-if="isSelector && isMeta" class="btn-group">
<a class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-lg fa-fw" ng-class="metaStatus == 'all' ? 'fa-check-square-o text-primary' : metaStatus == 'some' ? 'fa-minus-square-o' : 'fa-square-o'"></i>
<i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu">
<li><a ng-click="metaSelect('all')">All</a></li>
<li><a ng-click="metaSelect('invert')">Invert</a></li>
<li><a ng-click="metaSelect('none')">None</a></li>
</ul>
</div>
<div ng-if="isSelector && !isMeta">
<i class="fa fa-lg fa-fw" ng-class="selector ? 'fa-check-square-o' : 'fa-square-o'"></i>
</div>
`,

@@ -187,1 +363,178 @@ }})

// }}}
// qbExport {{{
/**
* Directive to export a table via a query
* NOTE: This element draws a simple 'Export...' button by default but can be replaced by any valid transcluded HTML. Simply call `exportPrompt()` to action
* @param {Object} query The query Object to use when exporting
* @param {Object} spec The specification object of the collection
* @param {string} url The URL endpoint to redirect to for the query to be executed (typically something like `/api/widgets`)
*
* @example Simple export button
* <qb-export query="myQuery" spec="mySpec"></qb-export>
* @example Custom button
* <qb-export query="myQuery" spec="mySpec">
* <a class="btn btn-primary" ng-click="exportPrompt()">Export this list</a>
* </qb-export>
*/
.directive('qbExport', function() { return {
scope: {
query: '<',
spec: '<',
url: '@',
},
transclude: true,
restrict: 'EA',
controller: function($element, $httpParamSerializer, $scope, $timeout, $window, qbTableSettings, qbTableUtilities) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$scope.settings = {};
$scope.isShowing = false;
$scope.exportPrompt = ()=> {
$scope.settings = angular.extend(
angular.copy(qbTableSettings.export.defaults),
{
query: _($scope.query)
.omitBy((v, k) => ['skip', 'limit'].includes(k))
.value(),
columns: _.map($scope.spec, (v, k) => {
v.id = k;
v.title = _.startCase(k);
v.selected = true;
return v;
}),
}
);
$element.find('.modal')
.on('show.bs.modal', ()=> $timeout(()=> $scope.isShowing = true))
.on('hidden.bs.modal', ()=> $timeout(()=> $scope.isShowing = false))
.modal('show');
};
$scope.exportExecute = ()=> {
var query = angular.extend($scope.settings.query, {
select: $scope.settings.columns
.filter(c => c.selected)
.map(c => c.id),
format: $scope.settings.format,
});
$window.open(`${$scope.url}?${$httpParamSerializer(query)}`);
};
// Generate a readable synopsis of the query {{{
$scope.querySynopsis;
$scope.$watchGroup(['isShowing', 'settings.query'], ()=> {
if (!$scope.isShowing) return; // Don't bother if we're not showing anything anyway
$scope.querySynopsis = qbTableUtilities.getSynopsis($scope.settings.query);
});
// }}}
// Generate a readable synopsis of the columns collapse {{{
$scope.columnSynopsis;
$scope.$watchGroup([
'isShowing',
()=> _.get($scope.settings, 'columns', []).map(c => c.id + '=' + c.selected).join('&'), // Create a digest of what columns are selected
], ()=> {
if (!$scope.isShowing) return; // Don't bother if we're not showing anything anyway
$scope.columnSynopsis = $scope.settings.columns.filter(c => c.selected).length + ' columns';
});
// }}}
},
template: `
<div class="modal fade">
<div class="modal-dialog modal-lg">
<div ng-if="isShowing" class="modal-content">
<div class="modal-header">
<a class="close" data-dismiss="modal"><i class="fa fa-times"></i></a>
<h4 class="modal-title">Export</h4>
</div>
<div class="modal-body form-horizontal">
<div class="form-group">
<label class="col-sm-3 control-label">Output format</label>
<div class="col-sm-9">
<select ng-model="settings.format" class="form-control">
<option ng-repeat="format in qbTableSettings.export.formats track by format.id" value="{{format.id}}">{{format.title}}</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Criteria</label>
<div class="col-sm-9">
<div class="panel-group" id="qb-export-criteria-{{$id}}">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-target="#qb-export-criteria-{{$id}}-query" data-parent="#qb-export-criteria-{{$id}}" class="btn-block collapsed">
{{querySynopsis}}
<i class="fa fa-caret-right pull-right"></i>
</a>
</h4>
</div>
<div id="qb-export-criteria-{{$id}}-query" class="panel-collapse collapse container">
<ui-query-builder
query="settings.query"
spec="spec"
></ui-query-builder>
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Columns</label>
<div class="col-sm-9">
<div class="panel-group" id="qb-export-columns-{{$id}}">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-target="#qb-export-columns-{{$id}}-columns" data-parent="#qb-export-columns-{{$id}}" class="btn-block collapsed">
{{columnSynopsis}}
<i class="fa fa-caret-right pull-right"></i>
</a>
</h4>
</div>
<div id="qb-export-columns-{{$id}}-columns" class="panel-collapse collapse row">
<div class="col-xs-12">
<table qb-table class="table table-hover">
<thead>
<tr>
<th qb-cell selector></th>
<th>Column</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="col in settings.columns track by col.id">
<td qb-cell selector="col.selected"></td>
<td>{{col.title}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="pull-left">
<a class="btn btn-danger" data-dismiss="modal">Cancel</a>
</div>
<div class="pull-right">
<a ng-click="exportExecute()" class="btn btn-primary" data-dismiss="modal">Export</a>
</div>
</div>
</div>
</div>
</div>
<ng-transclude>
<a ng-click="exportPrompt()" class="btn btn-default">Export...</a>
</ng-transclude>
`,
}})
// }}}

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

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