Socket
Socket
Sign inDemoInstall

@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.3.3 to 1.3.4

908

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

@@ -1,222 +0,276 @@

'use strict';
"use strict";
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
angular.module('angular-ui-query-builder', [])
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
// Service: QueryBuilder {{{
angular.module('angular-ui-query-builder', []) // Service: QueryBuilder {{{
.service('QueryBuilder', function () {
var QueryBuilder = this;
var QueryBuilder = this;
/**
* Apply various tidy functions to a raw spec before we process it
* @param {Object} spec The raw spec to clean
* @returns {Object} The output spec post cleaning
*/
/**
* Apply various tidy functions to a raw spec before we process it
* @param {Object} spec The raw spec to clean
* @returns {Object} The output spec post cleaning
*/
QueryBuilder.cleanSpec = function (spec) {
return _(spec).mapValues(function (v, k) {
return {
type: v.type,
enum: _(v.enum).map(function (e) {
return _.isString(e) ? { id: e, title: _.startCase(e) } : e;
}).sortBy('title').value()
};
}).value();
};
QueryBuilder.cleanSpec = function (spec) {
return _(spec).mapValues(function (v, k) {
return {
type: v.type,
enum: _(v.enum).map(function (e) {
return _.isString(e) ? {
id: e,
title: _.startCase(e)
} : e;
}).sortBy('title').value()
};
}).value();
};
/**
* List of additional properties that we support but need special treatment
* @var {Object} Each key is the property name with additional details in the Object value
* @param {string} [type='hidden'] How to handle each property within the UI
* @param {boolean} [canDelete=true] Disable deletion on the field
* @param {*} [...] Other inherited properties (see QueryBuilder.queryToArray) for examples
*/
/**
* List of additional properties that we support but need special treatment
* @var {Object} Each key is the property name with additional details in the Object value
* @param {string} [type='hidden'] How to handle each property within the UI
* @param {boolean} [canDelete=true] Disable deletion on the field
* @param {*} [...] Other inherited properties (see QueryBuilder.queryToArray) for examples
*/
QueryBuilder.metaProperties = {
limit: {
type: 'keyVal',
actions: [{ id: '$eq', title: 'Equals' }],
action: '$eq',
canDelete: true
},
populate: { type: 'hidden' },
skip: {
type: 'keyVal',
actions: [{ id: '$eq', title: 'Equals' }],
action: '$eq',
canDelete: true
},
sort: {
type: 'keyVal',
actions: [{ id: '$eq', title: 'Equals' }],
action: '$eq',
canDelete: false
}
};
/**
* Actions that can apply to all fields by default
* The contents of this array are mutated by QueryBuilder.queryPathPrototype to select the items that are actually applicable
* @var {array}
*/
QueryBuilder.queryPathPrototypeActions = [{ id: '$eq', title: 'Equals' }, { id: '$neq', title: 'Doesnt equal' }, { id: '$lt', title: 'Is less than' }, { id: '$lte', title: 'Is equal to or less than' }, { id: '$gt', title: 'Is greater than' }, { id: '$gte', title: 'Is equal or greater than' }, { id: '$in', title: 'Is one of' }, { id: '$nin', title: 'Is not one of' }, { id: '$exists', title: 'Has a value' }, { id: '$nexists', title: 'Does not have a value' }];
QueryBuilder.metaProperties = {
limit: {
type: 'keyVal',
actions: [{
id: '$eq',
title: 'Equals'
}],
action: '$eq',
canDelete: true
},
populate: {
type: 'hidden'
},
skip: {
type: 'keyVal',
actions: [{
id: '$eq',
title: 'Equals'
}],
action: '$eq',
canDelete: true
},
sort: {
type: 'keyVal',
actions: [{
id: '$eq',
title: 'Equals'
}],
action: '$eq',
canDelete: false
}
};
/**
* Actions that can apply to all fields by default
* The contents of this array are mutated by QueryBuilder.queryPathPrototype to select the items that are actually applicable
* @var {array}
*/
/**
* Utility function for QueryBuilder.queryToArray which returns a prototype of a query element based on its meta properties
* For example if 'foo' has a spec which defines it as a string, the string options are populated (['$eq', '$ne'...]) accordingly
* @param {string} path The Mongo path of the item to prototype
* @param {Object} [operand={}] An existing query infrastructure
* @return {Object} A prototype qbTable collection item representing the spec of the path
*/
QueryBuilder.queryPathPrototype = function (path) {
var operand = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var spec = arguments[2];
QueryBuilder.queryPathPrototypeActions = [{
id: '$eq',
title: 'Equals'
}, {
id: '$neq',
title: 'Doesnt equal'
}, {
id: '$lt',
title: 'Is less than'
}, {
id: '$lte',
title: 'Is equal to or less than'
}, {
id: '$gt',
title: 'Is greater than'
}, {
id: '$gte',
title: 'Is equal or greater than'
}, {
id: '$in',
title: 'Is one of'
}, {
id: '$nin',
title: 'Is not one of'
}, {
id: '$exists',
title: 'Has a value'
}, {
id: '$nexists',
title: 'Does not have a value'
}];
/**
* Utility function for QueryBuilder.queryToArray which returns a prototype of a query element based on its meta properties
* For example if 'foo' has a spec which defines it as a string, the string options are populated (['$eq', '$ne'...]) accordingly
* @param {string} path The Mongo path of the item to prototype
* @param {Object} [operand={}] An existing query infrastructure
* @return {Object} A prototype qbTable collection item representing the spec of the path
*/
var pathSpec = spec[path];
var firstKey = _.isObject(operand) && _(operand).keys().first();
var firstValue = _.isObject(operand) ? _(operand).values().first() : operand;
QueryBuilder.queryPathPrototype = function (path) {
var operand = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var spec = arguments.length > 2 ? arguments[2] : undefined;
var pathSpec = spec[path];
if ( // Looks like a meta 'search' entry?
path == '$or' && operand.every(function (i) {
return _.isObject(i) && _.keys(i).length == 1;
}) && operand.map(function (i) {
return _.chain(i).first().values().first().keys().find(function (i) {
return i == '$regexp';
}).value();
}).length == operand.length // Every key has a $regexp search
) {
return {
path: path,
type: 'search',
title: 'Search',
value: // Horrible expression to find the first regexp value
_.chain(operand).first().values().first().get('$regexp').value(),
fields: _(operand).map(function (i) {
return _.keys(i);
}).flatten().value(),
actions: QueryBuilder.queryPathPrototypeActions
};
} else if (path == '$and' || path == '$or') {
// Meta combinational types
if (!_.isArray(operand)) {
console.warn('query-builder', 'Query path', path, 'is a meta key', operand, 'but is not an array!', 'Given', typeof operand === 'undefined' ? 'undefined' : _typeof(operand));
operand = [];
}
var firstKey = _.isObject(operand) && _(operand).keys().first();
return {
path: path,
type: 'binaryGroup',
title: path == '$and' ? 'AND' : path == '$or' ? 'OR' : 'UNKNOWN',
condition: path.replace(/\$/, ''),
children: operand.map(function (i) {
return QueryBuilder.queryToArray(i, spec);
}),
actions: QueryBuilder.queryPathPrototypeActions
};
} else if (QueryBuilder.metaProperties[path]) {
// Is a meta property
return Object.assign({
path: path,
title: _.startCase(path),
value: operand,
type: 'hidden',
action: '$hidden',
actions: QueryBuilder.queryPathPrototypeActions
}, QueryBuilder.metaProperties[path]);
} else if (firstKey == '$exists') {
return {
path: path,
title: operand.title || _.startCase(path), // Create a title from the key if its omitted
value: !!operand,
type: 'exists',
action: '$exists',
actions: QueryBuilder.queryPathPrototypeActions
};
} else if (pathSpec.type == 'string' && _.isArray(pathSpec.enum) && pathSpec.enum.length) {
return {
path: path,
title: operand.title || _.startCase(path),
type: 'enum',
action: operand.$in ? '$in' : operand.$nin ? '$nin' : pathSpec.enum.length ? '$in' : '$eq',
enum: pathSpec.enum,
value: operand.$in ? operand.$in : operand.$nin ? operand.$nin : pathSpec.enum.length && !_.isArray(operand) ? [operand] : operand,
actions: QueryBuilder.queryPathPrototypeActions
};
} else {
// General fields
return {
path: path,
title: operand.title || _.startCase(path), // Create a title from the key if its omitted
type: pathSpec.type == 'string' ? 'string' : pathSpec.type == 'number' ? 'number' : pathSpec.type == 'date' ? 'date' : 'string',
action: '$eq',
value: pathSpec.type == 'date' ? moment(firstValue).toDate() // Convert date string weirdness into real dates
: firstValue,
actions: QueryBuilder.queryPathPrototypeActions
};
}
};
var firstValue = _.isObject(operand) ? _(operand).values().first() : operand;
/**
* Returns a queryList collection from a query object
* @param {Object} query The raw MongoDB / Sift object to transform from an object into a collection
* @returns {array} An array where each parameter is represented as a object for easier handling
*/
QueryBuilder.queryToArray = function (query, spec) {
return _(query).pickBy(function (v, k) {
var maps = spec[k] // Maps onto a spec path
|| k == '$and' || k == '$or' || QueryBuilder.metaProperties[k]; // is a meta directive
if ( // Looks like a meta 'search' entry?
path == '$or' && operand.every(function (i) {
return _.isObject(i) && _.keys(i).length == 1;
}) && operand.map(function (i) {
return _.chain(i).first().values().first().keys().find(function (i) {
return i == '$regexp';
}).value();
}).length == operand.length // Every key has a $regexp search
) {
return {
path: path,
type: 'search',
title: 'Search',
value: // Horrible expression to find the first regexp value
_.chain(operand).first().values().first().get('$regexp').value(),
fields: _(operand).map(function (i) {
return _.keys(i);
}).flatten().value(),
actions: QueryBuilder.queryPathPrototypeActions
};
} else if (path == '$and' || path == '$or') {
// Meta combinational types
if (!_.isArray(operand)) {
console.warn('query-builder', 'Query path', path, 'is a meta key', operand, 'but is not an array!', 'Given', _typeof(operand));
operand = [];
}
if (!maps) console.warn('query-builder', 'Incomming query path', k, 'Does not map to anything in spec', spec);
return !!maps;
}).map(function (v, k) {
return QueryBuilder.queryPathPrototype(k, v, spec);
}).value();
};
return {
path: path,
type: 'binaryGroup',
title: path == '$and' ? 'AND' : path == '$or' ? 'OR' : 'UNKNOWN',
condition: path.replace(/\$/, ''),
children: operand.map(function (i) {
return QueryBuilder.queryToArray(i, spec);
}),
actions: QueryBuilder.queryPathPrototypeActions
};
} else if (QueryBuilder.metaProperties[path]) {
// Is a meta property
return Object.assign({
path: path,
title: _.startCase(path),
value: operand,
type: 'hidden',
action: '$hidden',
actions: QueryBuilder.queryPathPrototypeActions
}, QueryBuilder.metaProperties[path]);
} else if (firstKey == '$exists') {
return {
path: path,
title: operand.title || _.startCase(path),
// Create a title from the key if its omitted
value: !!operand,
type: 'exists',
action: '$exists',
actions: QueryBuilder.queryPathPrototypeActions
};
} else if (pathSpec.type == 'string' && _.isArray(pathSpec.enum) && pathSpec.enum.length) {
return {
path: path,
title: operand.title || _.startCase(path),
type: 'enum',
action: operand.$in ? '$in' : operand.$nin ? '$nin' : pathSpec.enum.length ? '$in' : '$eq',
enum: pathSpec.enum,
value: operand.$in ? operand.$in : operand.$nin ? operand.$nin : pathSpec.enum.length && !_.isArray(operand) ? [operand] : operand,
actions: QueryBuilder.queryPathPrototypeActions
};
} else {
// General fields
return {
path: path,
title: operand.title || _.startCase(path),
// Create a title from the key if its omitted
type: pathSpec.type == 'string' ? 'string' : pathSpec.type == 'number' ? 'number' : pathSpec.type == 'date' ? 'date' : 'string',
action: '$eq',
value: pathSpec.type == 'date' ? moment(firstValue).toDate() // Convert date string weirdness into real dates
: firstValue,
actions: QueryBuilder.queryPathPrototypeActions
};
}
};
/**
* Returns a queryList collection from a query object
* @param {Object} query The raw MongoDB / Sift object to transform from an object into a collection
* @returns {array} An array where each parameter is represented as a object for easier handling
*/
/**
* Reverse of `queryToArray()`
* @param {array} queryList the internal array composed by queryToArray
* @returns {Object} A Mongo / Sift compatible object
*/
QueryBuilder.arrayToQuery = function (queryList) {
var composer = function composer(ql) {
return _(ql).mapKeys(function (ql) {
return ql.path;
}).mapValues(function (ql) {
switch (ql.type) {
case 'string':
case 'number':
case 'date':
if (ql.action == '$eq') {
return ql.value;
} else {
return _defineProperty({}, ql.action, ql.value);
}
case 'enum':
return _defineProperty({}, ql.action, ql.value);
case 'exists':
return { $exists: ql.action == '$exists' };
case 'search':
return ql.fields.map(function (f) {
return _defineProperty({}, f, {
$regexp: ql.value,
options: 'i'
});
});
case 'keyVal':
case 'hidden':
return ql.value;
default:
console.warn('Unknown type to convert:', ql.type);
}
}).value();
};
return composer(queryList);
};
})
// }}}
QueryBuilder.queryToArray = function (query, spec) {
return _(query).pickBy(function (v, k) {
var maps = spec[k] // Maps onto a spec path
|| k == '$and' || k == '$or' || QueryBuilder.metaProperties[k]; // is a meta directive
if (!maps) console.warn('query-builder', 'Incomming query path', k, 'Does not map to anything in spec', spec);
return !!maps;
}).map(function (v, k) {
return QueryBuilder.queryPathPrototype(k, v, spec);
}).value();
};
/**
* Reverse of `queryToArray()`
* @param {array} queryList the internal array composed by queryToArray
* @returns {Object} A Mongo / Sift compatible object
*/
QueryBuilder.arrayToQuery = function (queryList) {
var composer = function composer(ql) {
return _(ql).mapKeys(function (ql) {
return ql.path;
}).mapValues(function (ql) {
switch (ql.type) {
case 'string':
case 'number':
case 'date':
if (ql.action == '$eq') {
return ql.value;
} else {
return _defineProperty({}, ql.action, ql.value);
}
case 'enum':
return _defineProperty({}, ql.action, ql.value);
case 'exists':
return {
$exists: ql.action == '$exists'
};
case 'search':
return ql.fields.map(function (f) {
return _defineProperty({}, f, {
$regexp: ql.value,
options: 'i'
});
});
case 'keyVal':
case 'hidden':
return ql.value;
default:
console.warn('Unknown type to convert:', ql.type);
}
}).value();
};
return composer(queryList);
};
}) // }}}
// Component: uiQueryBuilder {{{
/**

@@ -229,96 +283,91 @@ * Master query builder component

.component('uiQueryBuilder', {
bindings: {
query: '=',
spec: '<'
},
template: '\n\t\t<div class="ui-query-builder">\n\t\t\t<div class="query-container">\n\t\t\t\t<ui-query-builder-group\n\t\t\t\t\tqb-group="$ctrl.qbQuery"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-group>\n\t\t\t</div>\n\t\t</div>\n\t',
controller: ['$scope', '$timeout', 'QueryBuilder', function controller($scope, $timeout, QueryBuilder) {
var $ctrl = this;
bindings: {
query: '=',
spec: '<'
},
template: "\n\t\t<div class=\"ui-query-builder\">\n\t\t\t<div class=\"query-container\">\n\t\t\t\t<ui-query-builder-group\n\t\t\t\t\tqb-group=\"$ctrl.qbQuery\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t></ui-query-builder-group>\n\t\t\t</div>\n\t\t</div>\n\t",
controller: ["$scope", "$timeout", "QueryBuilder", function controller($scope, $timeout, QueryBuilder) {
var $ctrl = this; // Main loader {{{
// Main loader {{{
$ctrl.qbSpec;
$ctrl.qbQuery;
$ctrl.qbSpec;
$ctrl.qbQuery;
var initUnwatch = $scope.$watchGroup(['$ctrl.query', '$ctrl.spec'], function () {
if (!$ctrl.spec || !$ctrl.query) return; // Not yet got everything we need
var initUnwatch = $scope.$watchGroup(['$ctrl.query', '$ctrl.spec'], function () {
if (!$ctrl.spec || !$ctrl.query) return; // Not yet got everything we need
$ctrl.qbSpec = QueryBuilder.cleanSpec($ctrl.spec);
$ctrl.qbQuery = QueryBuilder.queryToArray($ctrl.query, $ctrl.qbSpec);
initUnwatch(); // Release the watcher so we don't get stuck in a loop
});
// }}}
$ctrl.qbSpec = QueryBuilder.cleanSpec($ctrl.spec);
$ctrl.qbQuery = QueryBuilder.queryToArray($ctrl.query, $ctrl.qbSpec);
initUnwatch(); // Release the watcher so we don't get stuck in a loop
}); // }}}
/**
* Emitted by lower elements to inform the main builder that something has changed
* This will recompute the output query
*/
$scope.$on('queryBuilder.change', function (e, replaceQuery) {
return $timeout(function () {
// Timeout to wait for Angular to catch up with its low level populates
if (replaceQuery) {
// If we're given an entire query to overwrite - recompute it
$ctrl.query = replaceQuery;
$ctrl.qbQuery = QueryBuilder.queryToArray($ctrl.query, $ctrl.qbSpec);
} else {
$ctrl.query = QueryBuilder.arrayToQuery($ctrl.qbQuery);
}
});
});
/**
* Emitted by lower elements to inform the main builder that something has changed
* This will recompute the output query
*/
/**
* Remove an item from the query by path
* @param {Object} event
* @param {string} path The path to remove
*/
$scope.$on('queryBuilder.pathAction.drop', function (e, path) {
$ctrl.qbQuery = $ctrl.qbQuery.filter(function (p) {
return p.path != path;
});
$ctrl.query = QueryBuilder.arrayToQuery($ctrl.qbQuery);
});
$scope.$on('queryBuilder.change', function (e, replaceQuery) {
return $timeout(function () {
// Timeout to wait for Angular to catch up with its low level populates
if (replaceQuery) {
// If we're given an entire query to overwrite - recompute it
$ctrl.query = replaceQuery;
$ctrl.qbQuery = QueryBuilder.queryToArray($ctrl.query, $ctrl.qbSpec);
} else {
$ctrl.query = QueryBuilder.arrayToQuery($ctrl.qbQuery);
}
});
});
/**
* Remove an item from the query by path
* @param {Object} event
* @param {string} path The path to remove
*/
/**
* Swap an item from within query by path
* @param {Object} event
* @param {string} path The path to swap
* @param {string} newPath The new path to use
*/
$scope.$on('queryBuilder.pathAction.swapPath', function (e, path, newPath) {
var existingItemIndex = $ctrl.qbQuery.findIndex(function (q) {
return q.path == path;
});
if (!existingItemIndex) throw new Error('Cannot find path "' + path + '" to swap with new path "' + newPath + '"');
$scope.$on('queryBuilder.pathAction.drop', function (e, path) {
$ctrl.qbQuery = $ctrl.qbQuery.filter(function (p) {
return p.path != path;
});
$ctrl.query = QueryBuilder.arrayToQuery($ctrl.qbQuery);
});
/**
* Swap an item from within query by path
* @param {Object} event
* @param {string} path The path to swap
* @param {string} newPath The new path to use
*/
$ctrl.qbQuery[existingItemIndex] = QueryBuilder.queryPathPrototype(newPath, undefined, $ctrl.qbSpec);
$timeout(function () {
return $scope.$broadcast('queryBuilder.focusOperand', newPath);
}); // Tell the widget to try and focus itself
});
$scope.$on('queryBuilder.pathAction.swapPath', function (e, path, newPath) {
var existingItemIndex = $ctrl.qbQuery.findIndex(function (q) {
return q.path == path;
});
if (!existingItemIndex) throw new Error("Cannot find path \"".concat(path, "\" to swap with new path \"").concat(newPath, "\""));
$ctrl.qbQuery[existingItemIndex] = QueryBuilder.queryPathPrototype(newPath, undefined, $ctrl.qbSpec);
$timeout(function () {
return $scope.$broadcast('queryBuilder.focusOperand', newPath);
}); // Tell the widget to try and focus itself
});
$scope.$on('queryBuilder.pathAction.swapAction', function (e, path, newAction) {
console.log('SWAPACTION', path, newAction);
});
/**
* Add a new item by path
* @param {Object} event
* @param {string} [path] The new path to add, if omitted the new path is added at the root element
*/
$scope.$on('queryBuilder.pathAction.swapAction', function (e, path, newAction) {
console.log('SWAPACTION', path, newAction);
});
$scope.$on('queryBuilder.pathAction.add', function (e, path) {
// Append new path and set to blank
$ctrl.qbQuery.push({
path: '',
type: 'blank',
value: null,
fields: []
});
$timeout(function () {
return $scope.$broadcast('queryBuilder.focusPath', '');
}); // Tell the widget to try and focus itself
});
}]
}) // }}}
// Component: uiQueryBuilderGroup {{{
/**
* Add a new item by path
* @param {Object} event
* @param {string} [path] The new path to add, if omitted the new path is added at the root element
*/
$scope.$on('queryBuilder.pathAction.add', function (e, path) {
// Append new path and set to blank
$ctrl.qbQuery.push({
path: '',
type: 'blank',
value: null,
fields: []
});
$timeout(function () {
return $scope.$broadcast('queryBuilder.focusPath', '');
}); // Tell the widget to try and focus itself
});
}]
})
// }}}
// Component: uiQueryBuilderGroup {{{
/**

@@ -331,22 +380,21 @@ * Query builder element that holds a collection of queries - an array

.component('uiQueryBuilderGroup', {
bindings: {
qbGroup: '=',
qbSpec: '<'
},
template: '\n\t\t<div ng-repeat="row in $ctrl.qbGroup | filter:$ctrl.qbGroupFilter" meta-key="{{row.path}}">\n\t\t\t<ui-query-builder-row\n\t\t\t\tqb-item="row"\n\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t></ui-query-builder-row>\n\t\t</div>\n\t\t<button ng-click="$ctrl.add()" type="button" class="btn-add"></button>\n\t',
controller: ['$scope', 'QueryBuilder', function controller($scope, QueryBuilder) {
var $ctrl = this;
bindings: {
qbGroup: '=',
qbSpec: '<'
},
template: "\n\t\t<div ng-repeat=\"row in $ctrl.qbGroup | filter:$ctrl.qbGroupFilter\" meta-key=\"{{row.path}}\">\n\t\t\t<ui-query-builder-row\n\t\t\t\tqb-item=\"row\"\n\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t></ui-query-builder-row>\n\t\t</div>\n\t\t<button ng-click=\"$ctrl.add()\" type=\"button\" class=\"btn-add\"></button>\n\t",
controller: ["$scope", "QueryBuilder", function controller($scope, QueryBuilder) {
var $ctrl = this;
$ctrl.add = function () {
return $scope.$emit('queryBuilder.pathAction.add');
};
$ctrl.add = function () {
return $scope.$emit('queryBuilder.pathAction.add');
};
$ctrl.qbGroupFilter = function (item) {
return item.type != 'hidden';
};
}]
})
// }}}
$ctrl.qbGroupFilter = function (item) {
return item.type != 'hidden';
};
}]
}) // }}}
// Component: uiQueryBuilderRow {{{
// Component: uiQueryBuilderRow {{{
/**

@@ -360,40 +408,40 @@ * Individual line-item for a query row

.component('uiQueryBuilderRow', {
bindings: {
qbItem: '=',
qbSpec: '<'
},
controller: ['$element', '$scope', 'QueryBuilder', function controller($element, $scope, QueryBuilder) {
var $ctrl = this;
bindings: {
qbItem: '=',
qbSpec: '<'
},
controller: ["$element", "$scope", "QueryBuilder", function controller($element, $scope, QueryBuilder) {
var $ctrl = this;
$ctrl.delete = function (path) {
return $scope.$emit('queryBuilder.pathAction.drop', path);
};
$ctrl.setChanged = function () {
return $scope.$emit('queryBuilder.change');
};
$ctrl.setAction = function (action) {
return $scope.$emit('queryBuilder.pathAction.swapAction', $ctrl.qbItem, action);
};
$ctrl.delete = function (path) {
return $scope.$emit('queryBuilder.pathAction.drop', path);
};
$scope.$on('queryBuilder.focusPath', function (e, path) {
if ($ctrl.qbItem.path != path) return; // We don't control this path - ignore
$ctrl.setChanged = function () {
return $scope.$emit('queryBuilder.change');
};
$element.find('ui-query-builder-path .dropdown-toggle').dropdown('toggle');
});
$ctrl.setAction = function (action) {
return $scope.$emit('queryBuilder.pathAction.swapAction', $ctrl.qbItem, action);
};
$scope.$on('queryBuilder.focusOperand', function (e, path) {
if ($ctrl.qbItem.path != path) return; // We don't control this path - ignore
$scope.$on('queryBuilder.focusPath', function (e, path) {
if ($ctrl.qbItem.path != path) return; // We don't control this path - ignore
// Try finding a single input box {{{
var focusElem = $element.find('input.form-control');
if (focusElem.length == 1) return focusElem.focus();
// }}}
console.warn('Unable to focus any element within DOM', $element[0], 'for type', $ctrl.type, 'on line item', $ctrl.qbItem);
});
}],
template: '\n\t\t<div ng-switch="$ctrl.qbItem.type">\n\t\t\t<!-- $and / $or condition {{{ -->\n\t\t\t<div ng-switch-when="binaryGroup" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-1 btn-block">\n\t\t\t\t\t\t{{$ctrl.qbItem.title}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div ng-repeat="conditional in $ctrl.qbItem.children" class="query-container clearfix">\n\t\t\t\t\t<ui-query-builder-group\n\t\t\t\t\t\tqb-group="conditional"\n\t\t\t\t\t\tqb-spec="$ctrl.spec"\n\t\t\t\t\t></ui-query-builder-group>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- String {{{ -->\n\t\t\t<div ng-switch-when="string" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Enum {{{ -->\n\t\t\t<div ng-switch-when="enum" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<ui-query-builder-block-menu-multiple\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="3"\n\t\t\t\t\tselected="$ctrl.qbItem.value"\n\t\t\t\t\toptions="$ctrl.qbItem.enum"\n\t\t\t\t></ui-query-builder-block-menu-multiple>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Date {{{ -->\n\t\t\t<div ng-switch-when="date" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="date"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Number {{{ -->\n\t\t\t<div ng-switch-when="number" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-value="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-changed="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="number"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Exists {{{ -->\n\t\t\t<div ng-switch-when="exists" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Search {{{ -->\n\t\t\t<div ng-switch-when="search" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- keyVal (Only title + value) {{{ -->\n\t\t\t<div ng-switch-when="keyVal" class="query-row">\n\t\t\t\t<a ng-if="$ctrl.qbItem.canDelete === undefined || $ctrl.qbItem.canDelete" ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-block\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\ttitle="$ctrl.qbItem.title"\n\t\t\t\t></ui-query-builder-block>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Blank (i.e. field not set yet) {{{ -->\n\t\t\t<div ng-switch-when="blank" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Unknown {{{ -->\n\t\t\t<div ng-switch-default class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-warning btn-block">\n\t\t\t\t\t\tUnknown handler: {{$ctrl.qbItem.type}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t</div>\n\t'
})
// }}}
$element.find('ui-query-builder-path .dropdown-toggle').dropdown('toggle');
});
$scope.$on('queryBuilder.focusOperand', function (e, path) {
if ($ctrl.qbItem.path != path) return; // We don't control this path - ignore
// Try finding a single input box {{{
var focusElem = $element.find('input.form-control');
if (focusElem.length == 1) return focusElem.focus(); // }}}
console.warn('Unable to focus any element within DOM', $element[0], 'for type', $ctrl.type, 'on line item', $ctrl.qbItem);
});
}],
template: "\n\t\t<div ng-switch=\"$ctrl.qbItem.type\">\n\t\t\t<!-- $and / $or condition {{{ -->\n\t\t\t<div ng-switch-when=\"binaryGroup\" class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<div class=\"query-block\">\n\t\t\t\t\t<div class=\"btn btn-1 btn-block\">\n\t\t\t\t\t\t{{$ctrl.qbItem.title}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div ng-repeat=\"conditional in $ctrl.qbItem.children\" class=\"query-container clearfix\">\n\t\t\t\t\t<ui-query-builder-group\n\t\t\t\t\t\tqb-group=\"conditional\"\n\t\t\t\t\t\tqb-spec=\"$ctrl.spec\"\n\t\t\t\t\t></ui-query-builder-group>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- String {{{ -->\n\t\t\t<div ng-switch-when=\"string\" class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.path\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"2\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.action\"\n\t\t\t\t\toptions=\"$ctrl.qbItem.actions\"\n\t\t\t\t\ton-change=\"$ctrl.setAction(selected)\"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class=\"query-block\">\n\t\t\t\t\t<div class=\"btn btn-3 btn-block\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model=\"$ctrl.qbItem.value\"\n\t\t\t\t\t\t\tng-change=\"$ctrl.setChanged()\"\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tclass=\"form-control\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Enum {{{ -->\n\t\t\t<div ng-switch-when=\"enum\" class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.path\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"2\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.action\"\n\t\t\t\t\toptions=\"$ctrl.qbItem.actions\"\n\t\t\t\t\ton-change=\"$ctrl.setAction(selected)\"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<ui-query-builder-block-menu-multiple\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"3\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.value\"\n\t\t\t\t\toptions=\"$ctrl.qbItem.enum\"\n\t\t\t\t></ui-query-builder-block-menu-multiple>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Date {{{ -->\n\t\t\t<div ng-switch-when=\"date\" class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.path\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"2\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.action\"\n\t\t\t\t\toptions=\"$ctrl.qbItem.actions\"\n\t\t\t\t\ton-change=\"$ctrl.setAction(selected)\"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class=\"query-block\">\n\t\t\t\t\t<div class=\"btn btn-3 btn-block\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model=\"$ctrl.qbItem.value\"\n\t\t\t\t\t\t\tng-change=\"$ctrl.setChanged()\"\n\t\t\t\t\t\t\ttype=\"date\"\n\t\t\t\t\t\t\tclass=\"form-control\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Number {{{ -->\n\t\t\t<div ng-switch-when=\"number\" class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.path\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"2\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.action\"\n\t\t\t\t\toptions=\"$ctrl.qbItem.actions\"\n\t\t\t\t\ton-change=\"$ctrl.setAction(selected)\"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class=\"query-block\">\n\t\t\t\t\t<div class=\"btn btn-3 btn-block\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-value=\"$ctrl.qbItem.value\"\n\t\t\t\t\t\t\tng-changed=\"$ctrl.setChanged()\"\n\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\tclass=\"form-control\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Exists {{{ -->\n\t\t\t<div ng-switch-when=\"exists\" class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.path\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"2\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.action\"\n\t\t\t\t\toptions=\"$ctrl.qbItem.actions\"\n\t\t\t\t\ton-change=\"$ctrl.setAction(selected)\"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Search {{{ -->\n\t\t\t<div ng-switch-when=\"search\" class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.path\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t\ton-change=\"$ctrl.setAction(selected)\"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class=\"query-block\">\n\t\t\t\t\t<div class=\"btn btn-2 btn-block\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model=\"$ctrl.qbItem.value\"\n\t\t\t\t\t\t\tng-change=\"$ctrl.setChanged()\"\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tclass=\"form-control\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- keyVal (Only title + value) {{{ -->\n\t\t\t<div ng-switch-when=\"keyVal\" class=\"query-row\">\n\t\t\t\t<a ng-if=\"$ctrl.qbItem.canDelete === undefined || $ctrl.qbItem.canDelete\" ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-block\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\ttitle=\"$ctrl.qbItem.title\"\n\t\t\t\t></ui-query-builder-block>\n\t\t\t\t<div class=\"query-block\">\n\t\t\t\t\t<div class=\"btn btn-2 btn-block\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model=\"$ctrl.qbItem.value\"\n\t\t\t\t\t\t\tng-change=\"$ctrl.setChanged()\"\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tclass=\"form-control\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Blank (i.e. field not set yet) {{{ -->\n\t\t\t<div ng-switch-when=\"blank\" class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.path\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t\t<!-- Unknown {{{ -->\n\t\t\t<div ng-switch-default class=\"query-row\">\n\t\t\t\t<a ng-click=\"$ctrl.delete($ctrl.qbItem.path)\" class=\"btn-trash\"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass=\"query-block\"\n\t\t\t\t\tlevel=\"1\"\n\t\t\t\t\tselected=\"$ctrl.qbItem.path\"\n\t\t\t\t\tqb-spec=\"$ctrl.qbSpec\"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class=\"query-block\">\n\t\t\t\t\t<div class=\"btn btn-warning btn-block\">\n\t\t\t\t\t\tUnknown handler: {{$ctrl.qbItem.type}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<!-- }}} -->\n\t\t</div>\n\t"
}) // }}}
// Component: uiQueryBuilderPath {{{
/**

@@ -408,33 +456,32 @@ * Component for drawing a path selection component

.component('uiQueryBuilderPath', {
bindings: {
level: '<',
selected: '<',
qbSpec: '<'
},
controller: ['$scope', function controller($scope) {
var $ctrl = this;
bindings: {
level: '<',
selected: '<',
qbSpec: '<'
},
controller: ["$scope", function controller($scope) {
var $ctrl = this;
$ctrl.setSelected = function (option) {
return $scope.$emit('queryBuilder.pathAction.swapPath', $ctrl.selected, option);
};
$ctrl.setSelected = function (option) {
return $scope.$emit('queryBuilder.pathAction.swapPath', $ctrl.selected, option);
};
$ctrl.options;
$ctrl.$onInit = function () {
$ctrl.options = _.map($ctrl.qbSpec, function (info, path) {
return Object.assign({}, {
path: path,
title: _.startCase(path)
}, info);
});
$ctrl.options;
$ctrl.selectedOption = $ctrl.options.find(function (p) {
return p.path == $ctrl.selected;
});
};
}],
template: '\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="path in $ctrl.options track by path.path"><a ng-click="$ctrl.setSelected(path.path)">{{path.title}}</a></li>\n\t\t</ul>\n\t'
})
// }}}
$ctrl.$onInit = function () {
$ctrl.options = _.map($ctrl.qbSpec, function (info, path) {
return Object.assign({}, {
path: path,
title: _.startCase(path)
}, info);
});
$ctrl.selectedOption = $ctrl.options.find(function (p) {
return p.path == $ctrl.selected;
});
};
}],
template: "\n\t\t<a class=\"btn btn-block btn-{{$ctrl.level}} dropdown-toggle\" data-toggle=\"dropdown\">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class=\"fa fa-caret-down\"></i>\n\t\t</a>\n\t\t<ul class=\"dropdown-menu pull-right\">\n\t\t\t<li ng-repeat=\"path in $ctrl.options track by path.path\"><a ng-click=\"$ctrl.setSelected(path.path)\">{{path.title}}</a></li>\n\t\t</ul>\n\t"
}) // }}}
// Component: uiQueryBuilderBlock {{{
// Component: uiQueryBuilderBlock {{{
/**

@@ -446,14 +493,13 @@ * Component for drawing a Block with no-interactivity

.component('uiQueryBuilderBlock', {
bindings: {
level: '<',
title: '<'
},
controller: ['$scope', function controller($scope) {
var $ctrl = this;
}],
template: '\n\t\t<a class="btn btn-block btn-{{$ctrl.level}}">\n\t\t\t{{$ctrl.title}}\n\t\t</a>\n\t'
})
// }}}
bindings: {
level: '<',
title: '<'
},
controller: ["$scope", function controller($scope) {
var $ctrl = this;
}],
template: "\n\t\t<a class=\"btn btn-block btn-{{$ctrl.level}}\">\n\t\t\t{{$ctrl.title}}\n\t\t</a>\n\t"
}) // }}}
// Component: uiQueryBuilderBlockMenu {{{
// Component: uiQueryBuilderBlockMenu {{{
/**

@@ -467,28 +513,29 @@ * Component for drawing a Block as a dropdown list of options

.component('uiQueryBuilderBlockMenu', {
bindings: {
level: '<',
options: '<',
selected: '=',
onChange: '&?'
},
controller: ['$scope', function controller($scope) {
var $ctrl = this;
bindings: {
level: '<',
options: '<',
selected: '=',
onChange: '&?'
},
controller: ["$scope", function controller($scope) {
var $ctrl = this;
$ctrl.setSelected = function (option) {
$ctrl.selected = option.id;
if (angular.isFunction($ctrl.onChange)) $ctrl.onChange({ selected: $ctrl.selected });
};
$ctrl.setSelected = function (option) {
$ctrl.selected = option.id;
if (angular.isFunction($ctrl.onChange)) $ctrl.onChange({
selected: $ctrl.selected
});
};
$ctrl.selectedOption;
$scope.$watchGroup(['$ctrl.options', '$ctrl.selected'], function () {
$ctrl.selectedOption = $ctrl.options.find(function (i) {
return i.id == $ctrl.selected;
});
});
}],
template: '\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id"><a ng-click="$ctrl.setSelected(option)">{{option.title}}</a></li>\n\t\t</ul>\n\t'
})
// }}}
$ctrl.selectedOption;
$scope.$watchGroup(['$ctrl.options', '$ctrl.selected'], function () {
$ctrl.selectedOption = $ctrl.options.find(function (i) {
return i.id == $ctrl.selected;
});
});
}],
template: "\n\t\t<a class=\"btn btn-block btn-{{$ctrl.level}} dropdown-toggle\" data-toggle=\"dropdown\">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class=\"fa fa-caret-down\"></i>\n\t\t</a>\n\t\t<ul class=\"dropdown-menu pull-right\">\n\t\t\t<li ng-repeat=\"option in $ctrl.options track by option.id\"><a ng-click=\"$ctrl.setSelected(option)\">{{option.title}}</a></li>\n\t\t</ul>\n\t"
}) // }}}
// Component: uiQueryBuilderBlockMenuMultiple {{{
// Component: uiQueryBuilderBlockMenuMultiple {{{
/**

@@ -502,38 +549,37 @@ * Component for drawing a Block as a dropdown list of multiple-select options

.component('uiQueryBuilderBlockMenuMultiple', {
bindings: {
level: '<',
options: '<',
selected: '='
},
controller: ['$scope', function controller($scope) {
var $ctrl = this;
bindings: {
level: '<',
options: '<',
selected: '='
},
controller: ["$scope", function controller($scope) {
var $ctrl = this;
$ctrl.toggle = function (option) {
if (!$ctrl.selected) $ctrl.selected = [];
$ctrl.toggle = function (option) {
if (!$ctrl.selected) $ctrl.selected = [];
if ($ctrl.selected.includes(option.id)) {
$ctrl.selected = $ctrl.selected.filter(function (i) {
return i != option.id;
});
} else {
$ctrl.selected.push(option.id);
}
$scope.$emit('queryBuilder.change');
};
if ($ctrl.selected.includes(option.id)) {
$ctrl.selected = $ctrl.selected.filter(function (i) {
return i != option.id;
});
} else {
$ctrl.selected.push(option.id);
}
$ctrl.selectedOptions;
$scope.$watch('$ctrl.selected', function () {
$ctrl.selectedOptions = $ctrl.options.filter(function (i) {
return ($ctrl.selected || []).includes(i.id);
});
$scope.$emit('queryBuilder.change');
};
$ctrl.options.forEach(function (o) {
return o.selected = $ctrl.selectedOptions.some(function (s) {
return s.id == o.id;
});
});
}, true);
}],
template: '\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t<span ng-repeat="item in $ctrl.selectedOptions track by item.id" class="pill">\n\t\t\t\t{{item.title}}\n\t\t\t</span>\n\t\t\t<i class="fa fa-caret-down"></i></a>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id">\n\t\t\t\t<a ng-click="$ctrl.toggle(option)">\n\t\t\t\t\t<i class="fa fa-fw" ng-class="option.selected ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{option.title}}\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t</ul>\n\t'
});
// }}}
$ctrl.selectedOptions;
$scope.$watch('$ctrl.selected', function () {
$ctrl.selectedOptions = $ctrl.options.filter(function (i) {
return ($ctrl.selected || []).includes(i.id);
});
$ctrl.options.forEach(function (o) {
return o.selected = $ctrl.selectedOptions.some(function (s) {
return s.id == o.id;
});
});
}, true);
}],
template: "\n\t\t<a class=\"btn btn-block btn-{{$ctrl.level}} dropdown-toggle\" data-toggle=\"dropdown\">\n\t\t\t<span ng-repeat=\"item in $ctrl.selectedOptions track by item.id\" class=\"pill\">\n\t\t\t\t{{item.title}}\n\t\t\t</span>\n\t\t\t<i class=\"fa fa-caret-down\"></i></a>\n\t\t</a>\n\t\t<ul class=\"dropdown-menu pull-right\">\n\t\t\t<li ng-repeat=\"option in $ctrl.options track by option.id\">\n\t\t\t\t<a ng-click=\"$ctrl.toggle(option)\">\n\t\t\t\t\t<i class=\"fa fa-fw\" ng-class=\"option.selected ? 'fa-check-square-o' : 'fa-square-o'\"></i>\n\t\t\t\t\t{{option.title}}\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t</ul>\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}var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};angular.module("angular-ui-query-builder",[]).service("QueryBuilder",function(){var t=this;t.cleanSpec=function(t){return _(t).mapValues(function(t,e){return{type:t.type,enum:_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value()}}).value()},t.metaProperties={limit:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!0},populate:{type:"hidden"},skip:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!0},sort:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!1}},t.queryPathPrototypeActions=[{id:"$eq",title:"Equals"},{id:"$neq",title:"Doesnt equal"},{id:"$lt",title:"Is less than"},{id:"$lte",title:"Is equal to or less than"},{id:"$gt",title:"Is greater than"},{id:"$gte",title:"Is equal or greater than"},{id:"$in",title:"Is one of"},{id:"$nin",title:"Is not one of"},{id:"$exists",title:"Has a value"},{id:"$nexists",title:"Does not have a value"}],t.queryPathPrototype=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments[2],l=r[e],i=_.isObject(n)&&_(n).keys().first(),c=_.isObject(n)?_(n).values().first():n;return"$or"==e&&n.every(function(t){return _.isObject(t)&&1==_.keys(t).length})&&n.map(function(t){return _.chain(t).first().values().first().keys().find(function(t){return"$regexp"==t}).value()}).length==n.length?{path:e,type:"search",title:"Search",value:_.chain(n).first().values().first().get("$regexp").value(),fields:_(n).map(function(t){return _.keys(t)}).flatten().value(),actions:t.queryPathPrototypeActions}:"$and"==e||"$or"==e?(_.isArray(n)||(console.warn("query-builder","Query path",e,"is a meta key",n,"but is not an array!","Given",void 0===n?"undefined":_typeof(n)),n=[]),{path:e,type:"binaryGroup",title:"$and"==e?"AND":"$or"==e?"OR":"UNKNOWN",condition:e.replace(/\$/,""),children:n.map(function(e){return t.queryToArray(e,r)}),actions:t.queryPathPrototypeActions}):t.metaProperties[e]?Object.assign({path:e,title:_.startCase(e),value:n,type:"hidden",action:"$hidden",actions:t.queryPathPrototypeActions},t.metaProperties[e]):"$exists"==i?{path:e,title:n.title||_.startCase(e),value:!!n,type:"exists",action:"$exists",actions:t.queryPathPrototypeActions}:"string"==l.type&&_.isArray(l.enum)&&l.enum.length?{path:e,title:n.title||_.startCase(e),type:"enum",action:n.$in?"$in":n.$nin?"$nin":l.enum.length?"$in":"$eq",enum:l.enum,value:n.$in?n.$in:n.$nin?n.$nin:l.enum.length&&!_.isArray(n)?[n]:n,actions:t.queryPathPrototypeActions}:{path:e,title:n.title||_.startCase(e),type:"string"==l.type?"string":"number"==l.type?"number":"date"==l.type?"date":"string",action:"$eq",value:"date"==l.type?moment(c).toDate():c,actions:t.queryPathPrototypeActions}},t.queryToArray=function(e,n){return _(e).pickBy(function(e,r){var l=n[r]||"$and"==r||"$or"==r||t.metaProperties[r];return l||console.warn("query-builder","Incomming query path",r,"Does not map to anything in spec",n),!!l}).map(function(e,r){return t.queryPathPrototype(r,e,n)}).value()},t.arrayToQuery=function(t){return function(t){return _(t).mapKeys(function(t){return t.path}).mapValues(function(t){switch(t.type){case"string":case"number":case"date":return"$eq"==t.action?t.value:_defineProperty({},t.action,t.value);case"enum":return _defineProperty({},t.action,t.value);case"exists":return{$exists:"$exists"==t.action};case"search":return t.fields.map(function(e){return _defineProperty({},e,{$regexp:t.value,options:"i"})});case"keyVal":case"hidden":return t.value;default:console.warn("Unknown type to convert:",t.type)}}).value()}(t)}}).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<div class="query-container">\n\t\t\t\t<ui-query-builder-group\n\t\t\t\t\tqb-group="$ctrl.qbQuery"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-group>\n\t\t\t</div>\n\t\t</div>\n\t',controller:["$scope","$timeout","QueryBuilder",function(t,e,n){var r=this;r.qbSpec,r.qbQuery;var l=t.$watchGroup(["$ctrl.query","$ctrl.spec"],function(){r.spec&&r.query&&(r.qbSpec=n.cleanSpec(r.spec),r.qbQuery=n.queryToArray(r.query,r.qbSpec),l())});t.$on("queryBuilder.change",function(t,l){return e(function(){l?(r.query=l,r.qbQuery=n.queryToArray(r.query,r.qbSpec)):r.query=n.arrayToQuery(r.qbQuery)})}),t.$on("queryBuilder.pathAction.drop",function(t,e){r.qbQuery=r.qbQuery.filter(function(t){return t.path!=e}),r.query=n.arrayToQuery(r.qbQuery)}),t.$on("queryBuilder.pathAction.swapPath",function(l,i,c){var o=r.qbQuery.findIndex(function(t){return t.path==i});if(!o)throw new Error('Cannot find path "'+i+'" to swap with new path "'+c+'"');r.qbQuery[o]=n.queryPathPrototype(c,void 0,r.qbSpec),e(function(){return t.$broadcast("queryBuilder.focusOperand",c)})}),t.$on("queryBuilder.pathAction.swapAction",function(t,e,n){console.log("SWAPACTION",e,n)}),t.$on("queryBuilder.pathAction.add",function(n,l){r.qbQuery.push({path:"",type:"blank",value:null,fields:[]}),e(function(){return t.$broadcast("queryBuilder.focusPath","")})})}]}).component("uiQueryBuilderGroup",{bindings:{qbGroup:"=",qbSpec:"<"},template:'\n\t\t<div ng-repeat="row in $ctrl.qbGroup | filter:$ctrl.qbGroupFilter" meta-key="{{row.path}}">\n\t\t\t<ui-query-builder-row\n\t\t\t\tqb-item="row"\n\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t></ui-query-builder-row>\n\t\t</div>\n\t\t<button ng-click="$ctrl.add()" type="button" class="btn-add"></button>\n\t',controller:["$scope","QueryBuilder",function(t,e){var n=this;n.add=function(){return t.$emit("queryBuilder.pathAction.add")},n.qbGroupFilter=function(t){return"hidden"!=t.type}}]}).component("uiQueryBuilderRow",{bindings:{qbItem:"=",qbSpec:"<"},controller:["$element","$scope","QueryBuilder",function(t,e,n){var r=this;r.delete=function(t){return e.$emit("queryBuilder.pathAction.drop",t)},r.setChanged=function(){return e.$emit("queryBuilder.change")},r.setAction=function(t){return e.$emit("queryBuilder.pathAction.swapAction",r.qbItem,t)},e.$on("queryBuilder.focusPath",function(e,n){r.qbItem.path==n&&t.find("ui-query-builder-path .dropdown-toggle").dropdown("toggle")}),e.$on("queryBuilder.focusOperand",function(e,n){if(r.qbItem.path==n){var l=t.find("input.form-control");if(1==l.length)return l.focus();console.warn("Unable to focus any element within DOM",t[0],"for type",r.type,"on line item",r.qbItem)}})}],template:'\n\t\t<div ng-switch="$ctrl.qbItem.type">\n\t\t\t\x3c!-- $and / $or condition {{{ --\x3e\n\t\t\t<div ng-switch-when="binaryGroup" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-1 btn-block">\n\t\t\t\t\t\t{{$ctrl.qbItem.title}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div ng-repeat="conditional in $ctrl.qbItem.children" class="query-container clearfix">\n\t\t\t\t\t<ui-query-builder-group\n\t\t\t\t\t\tqb-group="conditional"\n\t\t\t\t\t\tqb-spec="$ctrl.spec"\n\t\t\t\t\t></ui-query-builder-group>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- String {{{ --\x3e\n\t\t\t<div ng-switch-when="string" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Enum {{{ --\x3e\n\t\t\t<div ng-switch-when="enum" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<ui-query-builder-block-menu-multiple\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="3"\n\t\t\t\t\tselected="$ctrl.qbItem.value"\n\t\t\t\t\toptions="$ctrl.qbItem.enum"\n\t\t\t\t></ui-query-builder-block-menu-multiple>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Date {{{ --\x3e\n\t\t\t<div ng-switch-when="date" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="date"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Number {{{ --\x3e\n\t\t\t<div ng-switch-when="number" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-value="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-changed="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="number"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Exists {{{ --\x3e\n\t\t\t<div ng-switch-when="exists" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Search {{{ --\x3e\n\t\t\t<div ng-switch-when="search" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- keyVal (Only title + value) {{{ --\x3e\n\t\t\t<div ng-switch-when="keyVal" class="query-row">\n\t\t\t\t<a ng-if="$ctrl.qbItem.canDelete === undefined || $ctrl.qbItem.canDelete" ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-block\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\ttitle="$ctrl.qbItem.title"\n\t\t\t\t></ui-query-builder-block>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Blank (i.e. field not set yet) {{{ --\x3e\n\t\t\t<div ng-switch-when="blank" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Unknown {{{ --\x3e\n\t\t\t<div ng-switch-default class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-warning btn-block">\n\t\t\t\t\t\tUnknown handler: {{$ctrl.qbItem.type}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t'}).component("uiQueryBuilderPath",{bindings:{level:"<",selected:"<",qbSpec:"<"},controller:["$scope",function(t){var e=this;e.setSelected=function(n){return t.$emit("queryBuilder.pathAction.swapPath",e.selected,n)},e.options,e.$onInit=function(){e.options=_.map(e.qbSpec,function(t,e){return Object.assign({},{path:e,title:_.startCase(e)},t)}),e.selectedOption=e.options.find(function(t){return t.path==e.selected})}}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="path in $ctrl.options track by path.path"><a ng-click="$ctrl.setSelected(path.path)">{{path.title}}</a></li>\n\t\t</ul>\n\t'}).component("uiQueryBuilderBlock",{bindings:{level:"<",title:"<"},controller:["$scope",function(t){}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}}">\n\t\t\t{{$ctrl.title}}\n\t\t</a>\n\t'}).component("uiQueryBuilderBlockMenu",{bindings:{level:"<",options:"<",selected:"=",onChange:"&?"},controller:["$scope",function(t){var e=this;e.setSelected=function(t){e.selected=t.id,angular.isFunction(e.onChange)&&e.onChange({selected:e.selected})},e.selectedOption,t.$watchGroup(["$ctrl.options","$ctrl.selected"],function(){e.selectedOption=e.options.find(function(t){return t.id==e.selected})})}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id"><a ng-click="$ctrl.setSelected(option)">{{option.title}}</a></li>\n\t\t</ul>\n\t'}).component("uiQueryBuilderBlockMenuMultiple",{bindings:{level:"<",options:"<",selected:"="},controller:["$scope",function(t){var e=this;e.toggle=function(n){e.selected||(e.selected=[]),e.selected.includes(n.id)?e.selected=e.selected.filter(function(t){return t!=n.id}):e.selected.push(n.id),t.$emit("queryBuilder.change")},e.selectedOptions,t.$watch("$ctrl.selected",function(){e.selectedOptions=e.options.filter(function(t){return(e.selected||[]).includes(t.id)}),e.options.forEach(function(t){return t.selected=e.selectedOptions.some(function(e){return e.id==t.id})})},!0)}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t<span ng-repeat="item in $ctrl.selectedOptions track by item.id" class="pill">\n\t\t\t\t{{item.title}}\n\t\t\t</span>\n\t\t\t<i class="fa fa-caret-down"></i></a>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id">\n\t\t\t\t<a ng-click="$ctrl.toggle(option)">\n\t\t\t\t\t<i class="fa fa-fw" ng-class="option.selected ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{option.title}}\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t</ul>\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}function _typeof(t){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}angular.module("angular-ui-query-builder",[]).service("QueryBuilder",function(){var c=this;c.cleanSpec=function(t){return _(t).mapValues(function(t,e){return{type:t.type,enum:_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value()}}).value()},c.metaProperties={limit:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!0},populate:{type:"hidden"},skip:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!0},sort:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!1}},c.queryPathPrototypeActions=[{id:"$eq",title:"Equals"},{id:"$neq",title:"Doesnt equal"},{id:"$lt",title:"Is less than"},{id:"$lte",title:"Is equal to or less than"},{id:"$gt",title:"Is greater than"},{id:"$gte",title:"Is equal or greater than"},{id:"$in",title:"Is one of"},{id:"$nin",title:"Is not one of"},{id:"$exists",title:"Has a value"},{id:"$nexists",title:"Does not have a value"}],c.queryPathPrototype=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},n=2<arguments.length?arguments[2]:void 0,r=n[t],l=_.isObject(e)&&_(e).keys().first(),i=_.isObject(e)?_(e).values().first():e;return"$or"==t&&e.every(function(t){return _.isObject(t)&&1==_.keys(t).length})&&e.map(function(t){return _.chain(t).first().values().first().keys().find(function(t){return"$regexp"==t}).value()}).length==e.length?{path:t,type:"search",title:"Search",value:_.chain(e).first().values().first().get("$regexp").value(),fields:_(e).map(function(t){return _.keys(t)}).flatten().value(),actions:c.queryPathPrototypeActions}:"$and"==t||"$or"==t?(_.isArray(e)||(console.warn("query-builder","Query path",t,"is a meta key",e,"but is not an array!","Given",_typeof(e)),e=[]),{path:t,type:"binaryGroup",title:"$and"==t?"AND":"$or"==t?"OR":"UNKNOWN",condition:t.replace(/\$/,""),children:e.map(function(t){return c.queryToArray(t,n)}),actions:c.queryPathPrototypeActions}):c.metaProperties[t]?Object.assign({path:t,title:_.startCase(t),value:e,type:"hidden",action:"$hidden",actions:c.queryPathPrototypeActions},c.metaProperties[t]):"$exists"==l?{path:t,title:e.title||_.startCase(t),value:!!e,type:"exists",action:"$exists",actions:c.queryPathPrototypeActions}:"string"==r.type&&_.isArray(r.enum)&&r.enum.length?{path:t,title:e.title||_.startCase(t),type:"enum",action:e.$in?"$in":e.$nin?"$nin":r.enum.length?"$in":"$eq",enum:r.enum,value:e.$in?e.$in:e.$nin?e.$nin:r.enum.length&&!_.isArray(e)?[e]:e,actions:c.queryPathPrototypeActions}:{path:t,title:e.title||_.startCase(t),type:"string"==r.type?"string":"number"==r.type?"number":"date"==r.type?"date":"string",action:"$eq",value:"date"==r.type?moment(i).toDate():i,actions:c.queryPathPrototypeActions}},c.queryToArray=function(t,r){return _(t).pickBy(function(t,e){var n=r[e]||"$and"==e||"$or"==e||c.metaProperties[e];return n||console.warn("query-builder","Incomming query path",e,"Does not map to anything in spec",r),!!n}).map(function(t,e){return c.queryPathPrototype(e,t,r)}).value()},c.arrayToQuery=function(t){return _(t).mapKeys(function(t){return t.path}).mapValues(function(e){switch(e.type){case"string":case"number":case"date":return"$eq"==e.action?e.value:_defineProperty({},e.action,e.value);case"enum":return _defineProperty({},e.action,e.value);case"exists":return{$exists:"$exists"==e.action};case"search":return e.fields.map(function(t){return _defineProperty({},t,{$regexp:e.value,options:"i"})});case"keyVal":case"hidden":return e.value;default:console.warn("Unknown type to convert:",e.type)}}).value()}}).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<div class="query-container">\n\t\t\t\t<ui-query-builder-group\n\t\t\t\t\tqb-group="$ctrl.qbQuery"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-group>\n\t\t\t</div>\n\t\t</div>\n\t',controller:["$scope","$timeout","QueryBuilder",function(l,i,c){var o=this;o.qbSpec,o.qbQuery;var t=l.$watchGroup(["$ctrl.query","$ctrl.spec"],function(){o.spec&&o.query&&(o.qbSpec=c.cleanSpec(o.spec),o.qbQuery=c.queryToArray(o.query,o.qbSpec),t())});l.$on("queryBuilder.change",function(t,e){return i(function(){e?(o.query=e,o.qbQuery=c.queryToArray(o.query,o.qbSpec)):o.query=c.arrayToQuery(o.qbQuery)})}),l.$on("queryBuilder.pathAction.drop",function(t,e){o.qbQuery=o.qbQuery.filter(function(t){return t.path!=e}),o.query=c.arrayToQuery(o.qbQuery)}),l.$on("queryBuilder.pathAction.swapPath",function(t,e,n){var r=o.qbQuery.findIndex(function(t){return t.path==e});if(!r)throw new Error('Cannot find path "'.concat(e,'" to swap with new path "').concat(n,'"'));o.qbQuery[r]=c.queryPathPrototype(n,void 0,o.qbSpec),i(function(){return l.$broadcast("queryBuilder.focusOperand",n)})}),l.$on("queryBuilder.pathAction.swapAction",function(t,e,n){console.log("SWAPACTION",e,n)}),l.$on("queryBuilder.pathAction.add",function(t,e){o.qbQuery.push({path:"",type:"blank",value:null,fields:[]}),i(function(){return l.$broadcast("queryBuilder.focusPath","")})})}]}).component("uiQueryBuilderGroup",{bindings:{qbGroup:"=",qbSpec:"<"},template:'\n\t\t<div ng-repeat="row in $ctrl.qbGroup | filter:$ctrl.qbGroupFilter" meta-key="{{row.path}}">\n\t\t\t<ui-query-builder-row\n\t\t\t\tqb-item="row"\n\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t></ui-query-builder-row>\n\t\t</div>\n\t\t<button ng-click="$ctrl.add()" type="button" class="btn-add"></button>\n\t',controller:["$scope","QueryBuilder",function(t,e){this.add=function(){return t.$emit("queryBuilder.pathAction.add")},this.qbGroupFilter=function(t){return"hidden"!=t.type}}]}).component("uiQueryBuilderRow",{bindings:{qbItem:"=",qbSpec:"<"},controller:["$element","$scope","QueryBuilder",function(r,e,t){var l=this;l.delete=function(t){return e.$emit("queryBuilder.pathAction.drop",t)},l.setChanged=function(){return e.$emit("queryBuilder.change")},l.setAction=function(t){return e.$emit("queryBuilder.pathAction.swapAction",l.qbItem,t)},e.$on("queryBuilder.focusPath",function(t,e){l.qbItem.path==e&&r.find("ui-query-builder-path .dropdown-toggle").dropdown("toggle")}),e.$on("queryBuilder.focusOperand",function(t,e){if(l.qbItem.path==e){var n=r.find("input.form-control");if(1==n.length)return n.focus();console.warn("Unable to focus any element within DOM",r[0],"for type",l.type,"on line item",l.qbItem)}})}],template:'\n\t\t<div ng-switch="$ctrl.qbItem.type">\n\t\t\t\x3c!-- $and / $or condition {{{ --\x3e\n\t\t\t<div ng-switch-when="binaryGroup" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-1 btn-block">\n\t\t\t\t\t\t{{$ctrl.qbItem.title}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div ng-repeat="conditional in $ctrl.qbItem.children" class="query-container clearfix">\n\t\t\t\t\t<ui-query-builder-group\n\t\t\t\t\t\tqb-group="conditional"\n\t\t\t\t\t\tqb-spec="$ctrl.spec"\n\t\t\t\t\t></ui-query-builder-group>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- String {{{ --\x3e\n\t\t\t<div ng-switch-when="string" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Enum {{{ --\x3e\n\t\t\t<div ng-switch-when="enum" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<ui-query-builder-block-menu-multiple\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="3"\n\t\t\t\t\tselected="$ctrl.qbItem.value"\n\t\t\t\t\toptions="$ctrl.qbItem.enum"\n\t\t\t\t></ui-query-builder-block-menu-multiple>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Date {{{ --\x3e\n\t\t\t<div ng-switch-when="date" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="date"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Number {{{ --\x3e\n\t\t\t<div ng-switch-when="number" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-value="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-changed="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="number"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Exists {{{ --\x3e\n\t\t\t<div ng-switch-when="exists" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Search {{{ --\x3e\n\t\t\t<div ng-switch-when="search" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- keyVal (Only title + value) {{{ --\x3e\n\t\t\t<div ng-switch-when="keyVal" class="query-row">\n\t\t\t\t<a ng-if="$ctrl.qbItem.canDelete === undefined || $ctrl.qbItem.canDelete" ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-block\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\ttitle="$ctrl.qbItem.title"\n\t\t\t\t></ui-query-builder-block>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Blank (i.e. field not set yet) {{{ --\x3e\n\t\t\t<div ng-switch-when="blank" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Unknown {{{ --\x3e\n\t\t\t<div ng-switch-default class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-warning btn-block">\n\t\t\t\t\t\tUnknown handler: {{$ctrl.qbItem.type}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t'}).component("uiQueryBuilderPath",{bindings:{level:"<",selected:"<",qbSpec:"<"},controller:["$scope",function(e){var n=this;n.setSelected=function(t){return e.$emit("queryBuilder.pathAction.swapPath",n.selected,t)},n.options,n.$onInit=function(){n.options=_.map(n.qbSpec,function(t,e){return Object.assign({},{path:e,title:_.startCase(e)},t)}),n.selectedOption=n.options.find(function(t){return t.path==n.selected})}}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="path in $ctrl.options track by path.path"><a ng-click="$ctrl.setSelected(path.path)">{{path.title}}</a></li>\n\t\t</ul>\n\t'}).component("uiQueryBuilderBlock",{bindings:{level:"<",title:"<"},controller:["$scope",function(t){}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}}">\n\t\t\t{{$ctrl.title}}\n\t\t</a>\n\t'}).component("uiQueryBuilderBlockMenu",{bindings:{level:"<",options:"<",selected:"=",onChange:"&?"},controller:["$scope",function(t){var e=this;e.setSelected=function(t){e.selected=t.id,angular.isFunction(e.onChange)&&e.onChange({selected:e.selected})},e.selectedOption,t.$watchGroup(["$ctrl.options","$ctrl.selected"],function(){e.selectedOption=e.options.find(function(t){return t.id==e.selected})})}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id"><a ng-click="$ctrl.setSelected(option)">{{option.title}}</a></li>\n\t\t</ul>\n\t'}).component("uiQueryBuilderBlockMenuMultiple",{bindings:{level:"<",options:"<",selected:"="},controller:["$scope",function(t){var n=this;n.toggle=function(e){n.selected||(n.selected=[]),n.selected.includes(e.id)?n.selected=n.selected.filter(function(t){return t!=e.id}):n.selected.push(e.id),t.$emit("queryBuilder.change")},n.selectedOptions,t.$watch("$ctrl.selected",function(){n.selectedOptions=n.options.filter(function(t){return(n.selected||[]).includes(t.id)}),n.options.forEach(function(e){return e.selected=n.selectedOptions.some(function(t){return t.id==e.id})})},!0)}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t<span ng-repeat="item in $ctrl.selectedOptions track by item.id" class="pill">\n\t\t\t\t{{item.title}}\n\t\t\t</span>\n\t\t\t<i class="fa fa-caret-down"></i></a>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id">\n\t\t\t\t<a ng-click="$ctrl.toggle(option)">\n\t\t\t\t\t<i class="fa fa-fw" ng-class="option.selected ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{option.title}}\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t</ul>\n\t'});

@@ -1,133 +0,136 @@

'use strict';
"use strict";
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
angular.module('angular-ui-query-builder')
// qbTableSettings (provider) {{{
angular.module('angular-ui-query-builder') // qbTableSettings (provider) {{{
.provider('qbTableSettings', function () {
var qbTableSettings = this;
var qbTableSettings = this;
qbTableSettings.debug = false;
qbTableSettings.debugPrefix = '[angular-ui-query-builder]';
qbTableSettings.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',
checkMetaChecked: 'fa fa-lg fa-fw fa-check-square-o text-primary',
checkMetaSome: 'fa fa-lg fa-fw fa-minus-square-o',
checkMetaUnchecked: 'fa fa-lg fa-fw fa-square-o',
checkMetaCaret: 'fa fa-caret-down',
checkItemChecked: 'fa fa-lg fa-fw fa-check-square-o',
checkItemUnchecked: 'fa fa-lg fa-fw fa-square-o',
paginationPrev: 'fa fa-arrow-left',
paginationNext: 'fa fa-arrow-right',
modalClose: 'fa fa-times',
modalCollapseClosed: 'fa fa-caret-right pull-right',
search: 'fa fa-search',
searchClear: 'fa fa-times'
};
qbTableSettings.pagination = {
showXOfY: true,
showPages: true,
pageRangeBack: 5,
pageRangeFore: 5
};
qbTableSettings.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)'
}],
questions: [
/*
{
id: String, // Unique ID for each question (will be sent in submitted query)
type: String, // How to render the question. ENUM: 'text'
title: String, // The question to ask
default: String, // Default value of field if any
help: String, // Optional help text,
},
*/
]
};
qbTableSettings.debug = false;
qbTableSettings.debugPrefix = '[angular-ui-query-builder]';
qbTableSettings.$get = function () {
return qbTableSettings;
};
qbTableSettings.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',
checkMetaChecked: 'fa fa-lg fa-fw fa-check-square-o text-primary',
checkMetaSome: 'fa fa-lg fa-fw fa-minus-square-o',
checkMetaUnchecked: 'fa fa-lg fa-fw fa-square-o',
checkMetaCaret: 'fa fa-caret-down',
checkItemChecked: 'fa fa-lg fa-fw fa-check-square-o',
checkItemUnchecked: 'fa fa-lg fa-fw fa-square-o',
paginationPrev: 'fa fa-arrow-left',
paginationNext: 'fa fa-arrow-right',
modalClose: 'fa fa-times',
modalCollapseClosed: 'fa fa-caret-right pull-right',
search: 'fa fa-search',
searchClear: 'fa fa-times'
};
qbTableSettings.pagination = {
showXOfY: true,
showPages: true,
pageRangeBack: 5,
pageRangeFore: 5
};
qbTableSettings.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)' }],
questions: [
/*
{
id: String, // Unique ID for each question (will be sent in submitted query)
type: String, // How to render the question. ENUM: 'text'
title: String, // The question to ask
default: String, // Default value of field if any
help: String, // Optional help text,
},
*/
]
};
qbTableSettings.$get = function () {
return qbTableSettings;
};
return qbTableSettings;
})
// }}}
return qbTableSettings;
}) // }}}
// qbTableUtilities (service) {{{
.service('qbTableUtilities', function () {
return {
/**
* Return a human readable synopsis of a query
* @param {object} query The query to summerise
* @return {string} A short string summerising the query
*/
getSynopsis: function getSynopsis(query) {
var filters = _.keys(query).filter(function (i) {
return !['sort', 'skip', 'limit', 'select'].includes(i);
});
return {
/**
* Return a human readable synopsis of a query
* @param {object} query The query to summerise
* @return {string} A short string summerising the query
*/
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(', ');
},
return [!filters.length ? 'All documents' : filters.length == 1 ? '1 filter' : "".concat(filters.length, " filters"), query.sort ? query.sort.startsWith('-') ? "sorted by ".concat(query.sort.substr(1), " (reverse order)") : "sorted by ".concat(query.sort) : null, query.limit ? "limited to ".concat(query.limit, " rows") : null, query.offset ? "starting at record ".concat(query.skip) : null, query.select ? "selecting only ".concat(query.select.length, " columns") : null].filter(function (i) {
return i;
}).join(', ');
},
/**
* Find the dotted path to a specific query element by a predicate
* @param {object} query The query to search
* @returns {string|false} Either the found path of the item or false
*/
find: function find(query, predicate) {
var searchExpr = _.isFunction(predicate) ? predicate : _.matches(predicate);
var foundPath;
var deepSearcher = function deepSearcher(node, path) {
if (searchExpr(node, path.slice(path.length - 1))) {
foundPath = path;
return true;
} else if (_.isArray(node)) {
return node.some(function (v, k) {
return deepSearcher(v, path.concat(k));
});
} else if (_.isObject(node)) {
return _.some(node, function (v, k) {
return deepSearcher(v, path.concat(k));
});
}
};
/**
* Find the dotted path to a specific query element by a predicate
* @param {object} query The query to search
* @returns {string|false} Either the found path of the item or false
*/
find: function find(query, predicate) {
var searchExpr = _.isFunction(predicate) ? predicate : _.matches(predicate);
var foundPath;
var res = deepSearcher(query, []);
return res ? foundPath : false;
},
var deepSearcher = function deepSearcher(node, path) {
if (searchExpr(node, path.slice(path.length - 1))) {
foundPath = path;
return true;
} else if (_.isArray(node)) {
return node.some(function (v, k) {
return deepSearcher(v, path.concat(k));
});
} else if (_.isObject(node)) {
return _.some(node, function (v, k) {
return deepSearcher(v, path.concat(k));
});
}
};
/**
* Utlility function to return an escaped expression within a RegExp
* @param {string} text The text to escape
* @returns {string} The escaped expression
*/
escapeRegExp: function escapeRegExp(text) {
return String(text).replace(/(\W)/g, '\\$1');
},
var res = deepSearcher(query, []);
return res ? foundPath : false;
},
/**
* Utility to reverse quoting a RegExp
* @param {string} text The escaped regular expression to reverse
* @returns {string} The unescaped expression
*/
unescapeRegExp: function unescapeRegExp(text) {
return String(text).replace(/\\(\W)/g, '$1');
}
/**
* Utlility function to return an escaped expression within a RegExp
* @param {string} text The text to escape
* @returns {string} The escaped expression
*/
escapeRegExp: function escapeRegExp(text) {
return String(text).replace(/(\W)/g, '\\$1');
},
};
})
// }}}
/**
* Utility to reverse quoting a RegExp
* @param {string} text The escaped regular expression to reverse
* @returns {string} The unescaped expression
*/
unescapeRegExp: function unescapeRegExp(text) {
return String(text).replace(/\\(\W)/g, '$1');
}
};
}) // }}}
// qbTable (directive) {{{
// qbTable (directive) {{{
/**

@@ -142,92 +145,96 @@ * Directive applied to a table element to indicate that we should manage that table via angular-ui-query

.directive('qbTable', function () {
return {
scope: {
qbTable: '=?',
count: '<?',
stickyThead: '<?',
stickyTfoot: '<?'
},
restrict: 'AC',
controller: ['$attrs', '$element', '$rootScope', '$scope', '$timeout', 'qbTableSettings', function controller($attrs, $element, $rootScope, $scope, $timeout, qbTableSettings) {
var $ctrl = this;
return {
scope: {
qbTable: '=?',
count: '<?',
stickyThead: '<?',
stickyTfoot: '<?'
},
restrict: 'AC',
controller: ["$attrs", "$element", "$rootScope", "$scope", "$timeout", "qbTableSettings", function controller($attrs, $element, $rootScope, $scope, $timeout, qbTableSettings) {
var $ctrl = this; // Copy into $ctrl so children can access it / $watch it
// Copy into $ctrl so children can access it / $watch it
$ctrl.query = $scope.qbTable;
$ctrl.count = $scope.count;
$scope.$watch('count', function () {
return $ctrl.count = $scope.count;
}); // If our binding changes, also update the qbTable.count reference - no idea why Angular doesn't do this anyway since its using a pointer
$ctrl.query = $scope.qbTable;
$ctrl.count = $scope.count;
$scope.$watch('count', function () {
return $ctrl.count = $scope.count;
}); // If our binding changes, also update the qbTable.count reference - no idea why Angular doesn't do this anyway since its using a pointer
$ctrl.$broadcast = function (msg) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
$ctrl.$broadcast = function (msg) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
return $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.setDirty = function () {
if (qbTableSettings.debug) console.log(qbTableSettings.debugPrefix, 'Declare query dirty', $scope.qbTable);
$rootScope.$broadcast('queryBuilder.change', $scope.qbTable);
};
return $scope.$broadcast.apply($scope, [msg].concat(args));
}; // Rebind broadcast so its accessible from children
/**
* Set the value of a query element to another value
* NOTE: This function does not call $ctrl.setDirty() by default but you can chain this
* @param {string} field The field name to change
* @param {*} value The value to change to, if omitted the field is removed entirely
* @example set the sort criteria and then refresh
* qbTable.setField('sort', 'email').setDirty()
*/
$ctrl.setField = function (field, value) {
if (value == undefined) {
// Remove from query
delete $ctrl.query[field];
return;
}
switch (field) {
case 'sort':
if ($ctrl.query.sort === value) {
// If already sorting by field switch the sort direction
$ctrl.query.sort = '-' + value;
} else if ($ctrl.query.sort === '-' + value) {
// If reverse sorting switch the right way up again
$ctrl.query.sort = value;
} else {
// Just set the sorting
$ctrl.query.sort = value;
}
break;
default:
$scope.qbTable[field] = value;
}
$ctrl.$on = function (event, cb) {
return $scope.$on(event, cb);
};
return $ctrl;
};
$ctrl.setDirty = function () {
if (qbTableSettings.debug) console.log(qbTableSettings.debugPrefix, 'Declare query dirty', $scope.qbTable);
$rootScope.$broadcast('queryBuilder.change', $scope.qbTable);
};
/**
* Set the value of a query element to another value
* NOTE: This function does not call $ctrl.setDirty() by default but you can chain this
* @param {string} field The field name to change
* @param {*} value The value to change to, if omitted the field is removed entirely
* @example set the sort criteria and then refresh
* qbTable.setField('sort', 'email').setDirty()
*/
$element.addClass('qb-table');
$scope.$watch('stickyThead', function () {
return $element.toggleClass('qb-sticky-thead', $scope.stickyThead || $attrs.stickyThead === '');
});
$scope.$watch('stickyTfoot', function () {
return $element.toggleClass('qb-sticky-tfoot', $scope.stickyTfoot || $attrs.stickyTfoot === '');
});
$scope.$watch('count', function () {
return $element.toggleClass('qb-noresults', $scope.count === 0);
});
$scope.$on('queryBuilder.change.replace', function (e, q) {
$ctrl.query = $scope.qbTable = q;
$timeout(function () {
return $ctrl.setDirty();
});
});
}]
};
})
// }}}
$ctrl.setField = function (field, value) {
if (value == undefined) {
// Remove from query
delete $ctrl.query[field];
return;
}
switch (field) {
case 'sort':
if ($ctrl.query.sort === value) {
// If already sorting by field switch the sort direction
$ctrl.query.sort = "-".concat(value);
} else if ($ctrl.query.sort === "-".concat(value)) {
// If reverse sorting switch the right way up again
$ctrl.query.sort = value;
} else {
// Just set the sorting
$ctrl.query.sort = value;
}
break;
default:
$scope.qbTable[field] = value;
}
return $ctrl;
};
$element.addClass('qb-table');
$scope.$watch('stickyThead', function () {
return $element.toggleClass('qb-sticky-thead', $scope.stickyThead || $attrs.stickyThead === '');
});
$scope.$watch('stickyTfoot', function () {
return $element.toggleClass('qb-sticky-tfoot', $scope.stickyTfoot || $attrs.stickyTfoot === '');
});
$scope.$watch('count', function () {
return $element.toggleClass('qb-noresults', $scope.count === 0);
});
$scope.$on('queryBuilder.change.replace', function (e, q) {
$ctrl.query = $scope.qbTable = q;
$timeout(function () {
return $ctrl.setDirty();
});
});
}]
};
}) // }}}
// qbCol (directive) {{{
/**

@@ -247,78 +254,76 @@ * Directive for header elements to add angular-ui-query functionality

.directive('qbCol', function () {
return {
scope: {
qbCol: '@', // The field to operate on
sortable: '@'
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: ['$attrs', '$element', '$scope', '$timeout', 'qbTableSettings', function controller($attrs, $element, $scope, $timeout, qbTableSettings) {
var $ctrl = this;
return {
scope: {
qbCol: '@',
// The field to operate on
sortable: '@'
},
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; // Sanity checks {{{
$scope.qbTableSettings = qbTableSettings;
var unSanityChecks = $scope.$watchGroup(['qbTable', 'sortable'], function () {
if ($attrs.sortable === '' && !$scope.qbTable && qbTableSettings.debug) console.warn(qbTableSettings.debugPrefix, 'Added qb-col + sortable onto element', $element, 'but no qb-table query has been assigned on the table element!');
unSanityChecks();
}); // }}}
// Sort functionality {{{
// Sanity checks {{{
var unSanityChecks = $scope.$watchGroup(['qbTable', 'sortable'], function () {
if ($attrs.sortable === '' && !$scope.qbTable && qbTableSettings.debug) console.warn(qbTableSettings.debugPrefix, 'Added qb-col + sortable onto element', $element, 'but no qb-table query has been assigned on the table element!');
unSanityChecks();
});
// }}}
$scope.canSort = false; // True if either sortable has a specific value or is at least present
// Sort functionality {{{
$scope.canSort = false; // True if either sortable has a specific value or is at least present
$scope.isSorted = false; // False, 'asc', 'desc'
$scope.isSorted = false; // False, 'asc', 'desc'
$ctrl.$onInit = function () {
$scope.canSort = $scope.sortable || $attrs.sortable === '';
$element.toggleClass('sortable', $scope.canSort);
$ctrl.$onInit = function () {
$scope.canSort = $scope.sortable || $attrs.sortable === '';
$element.toggleClass('sortable', $scope.canSort);
if ($scope.canSort) {
// If sortable mode is on - enable clicking anywhere as a sort method
$element.on('click', function () {
return $timeout($scope.toggleSort);
});
}
};
if ($scope.canSort) {
// If sortable mode is on - enable clicking anywhere as a sort method
$element.on('click', function () {
return $timeout($scope.toggleSort);
});
}
};
$scope.$watch('qbTable.query.sort', function (sorter) {
var sortField = $scope.sortable || $scope.qbCol;
$scope.$watch('qbTable.query.sort', function (sorter) {
var sortField = $scope.sortable || $scope.qbCol;
if (!sorter) {
$scope.isSorted = false;
} else if (angular.isArray(sorter) && sorter.some(function (i) {
return i == sortField;
}) || sorter == sortField) {
$scope.isSorted = 'asc';
} else if (angular.isArray(sorter) && sorter.some(function (i) {
return i == '-' + sortField;
}) || sorter == '-' + sortField) {
$scope.isSorted = 'desc';
} else {
$scope.isSorted = false;
}
});
if (!sorter) {
$scope.isSorted = false;
} else if (angular.isArray(sorter) && sorter.some(function (i) {
return i == sortField;
}) || sorter == sortField) {
$scope.isSorted = 'asc';
} else if (angular.isArray(sorter) && sorter.some(function (i) {
return i == '-' + sortField;
}) || sorter == '-' + sortField) {
$scope.isSorted = 'desc';
} else {
$scope.isSorted = false;
}
});
$scope.toggleSort = function () {
if ($scope.sortable) {
// Sort by a specific field
$scope.qbTable.setField('sort', $scope.sortable).setDirty();
} else if ($scope.qbCol && $attrs.sortable === '') {
// Has attribute but no value - assume main key if we have one
$scope.qbTable.setField('sort', $scope.qbCol).setDirty();
}
};
// }}}
$scope.toggleSort = function () {
if ($scope.sortable) {
// Sort by a specific field
$scope.qbTable.setField('sort', $scope.sortable).setDirty();
} else if ($scope.qbCol && $attrs.sortable === '') {
// Has attribute but no value - assume main key if we have one
$scope.qbTable.setField('sort', $scope.qbCol).setDirty();
}
}; // }}}
$element.addClass('qb-col');
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
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'
};
})
// }}}
$element.addClass('qb-col');
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
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"
};
}) // }}}
// qbCell (directive) {{{
/**

@@ -339,102 +344,105 @@ * Directive for cell elements within a table

.directive('qbCell', function () {
return {
scope: {
selector: '=?',
onPreSelect: '&?',
onSelect: '&?'
},
require: '^qbTable',
restrict: 'A',
transclude: true,
controller: ['$attrs', '$element', '$scope', '$timeout', 'qbTableSettings', function controller($attrs, $element, $scope, $timeout, qbTableSettings) {
var $ctrl = this;
return {
scope: {
selector: '=?',
onPreSelect: '&?',
onSelect: '&?'
},
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.qbTableSettings = qbTableSettings;
$scope.isMeta = $element.parents('thead').length > 0;
// 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 {{{
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';
});
});
}
// }}}
$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
// 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.on('click', function (e) {
return $scope.$apply(function () {
if ($scope.onPreSelect) $scope.onPreSelect({
value: $scope.selector
});
$scope.selector = !$scope.selector;
$scope.qbTable.$broadcast('qbTableCellSelect');
if ($scope.onSelect) $timeout(function () {
return $scope.onSelect({
value: $scope.selector
});
});
});
});
} // Handle meta interaction
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 () {
if ($scope.onPreSelect) $scope.onPreSelect({ value: $scope.selector });
$scope.selector = !$scope.selector;
$scope.qbTable.$broadcast('qbTableCellSelect');
if ($scope.onSelect) $timeout(function () {
return $scope.onSelect({ value: $scope.selector });
});
});
});
}
$scope.metaSelect = function (type) {
return $scope.qbTable.$broadcast('qbTableCellSelectMeta', type);
}; // Bind to event listener and respond to selection directives from meta element
// 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 () {
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;
$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
});
case 'invert':
$scope.selector = !$scope.selector;
break;
$scope.qbTable.$on('qbTableCellSelectStatus', function (e, status) {
return status.push($scope.selector);
});
});
}
// }}}
case 'none':
$scope.selector = false;
break;
// 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 ng-class="metaStatus == \'all\' ? qbTableSettings.icons.checkMetaChecked : metaStatus == \'some\' ? qbTableSettings.icons.checkMetaUnchecked : qbTableSettings.icons.checkMetaUnchecked"></i>\n\t\t\t\t<i ng-class="qbTableSettings.icons.checkMetaCaret"></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 ng-class="selector ? qbTableSettings.icons.checkItemChecked : qbTableSettings.icons.checkItemUnchecked"></i>\n\t\t</div>\n\t'
};
})
// }}}
default:
throw new Error("Unknown selection type: ".concat(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 ng-class=\"metaStatus == 'all' ? qbTableSettings.icons.checkMetaChecked : metaStatus == 'some' ? qbTableSettings.icons.checkMetaUnchecked : qbTableSettings.icons.checkMetaUnchecked\"></i>\n\t\t\t\t<i ng-class=\"qbTableSettings.icons.checkMetaCaret\"></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 ng-class=\"selector ? qbTableSettings.icons.checkItemChecked : qbTableSettings.icons.checkItemUnchecked\"></i>\n\t\t</div>\n\t"
};
}) // }}}
// qbPagination (directive) {{{
/**

@@ -447,71 +455,67 @@ * Directive to add table pagination

.directive('qbPagination', function () {
return {
scope: {},
require: '^qbTable',
restrict: 'EA',
transclude: true,
controller: ['$attrs', '$scope', 'qbTableSettings', function controller($attrs, $scope, qbTableSettings) {
var $ctrl = this;
return {
scope: {},
require: '^qbTable',
restrict: 'EA',
transclude: true,
controller: ["$attrs", "$scope", "qbTableSettings", function controller($attrs, $scope, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$scope.canPrev = true;
$scope.canNext = true;
$scope.showRange = {};
$scope.$watchGroup(['qbTable.query.limit', 'qbTable.query.skip', 'qbTable.count'], function (sorter) {
$scope.canPrev = $scope.qbTable.query.skip > 0;
$scope.canNext = !$scope.qbTable.count || $scope.qbTable.query.skip + $scope.qbTable.query.limit < $scope.qbTable.count; // Page X of Y display {{{
$scope.qbTableSettings = qbTableSettings;
if (qbTableSettings.pagination.showXOfY) {
$scope.showRange = {
start: ($scope.qbTable.query.skip || 0) + 1,
end: Math.min(($scope.qbTable.query.skip || 0) + $scope.qbTable.query.limit, $scope.qbTable.count),
total: $scope.qbTable.count
};
} // }}}
// Page view calculation {{{
$scope.canPrev = true;
$scope.canNext = true;
$scope.showRange = {};
$scope.$watchGroup(['qbTable.query.limit', 'qbTable.query.skip', 'qbTable.count'], function (sorter) {
$scope.canPrev = $scope.qbTable.query.skip > 0;
$scope.canNext = !$scope.qbTable.count || $scope.qbTable.query.skip + $scope.qbTable.query.limit < $scope.qbTable.count;
if (qbTableSettings.pagination.showPages) {
$scope.pages = {
current: $scope.qbTable.query.limit ? Math.floor(($scope.qbTable.query.skip || 0) / $scope.qbTable.query.limit) : false
};
$scope.pages.min = Math.max($scope.pages.current - qbTableSettings.pagination.pageRangeBack, 0);
$scope.pages.total = $scope.qbTable.query.limit ? Math.ceil($scope.qbTable.count / $scope.qbTable.query.limit) : 1; // No limit specified therefore there is only one page
// Page X of Y display {{{
if (qbTableSettings.pagination.showXOfY) {
$scope.showRange = {
start: ($scope.qbTable.query.skip || 0) + 1,
end: Math.min(($scope.qbTable.query.skip || 0) + $scope.qbTable.query.limit, $scope.qbTable.count),
total: $scope.qbTable.count
};
}
// }}}
$scope.pages.max = Math.min($scope.pages.total, $scope.pages.current + qbTableSettings.pagination.pageRangeFore + 1);
$scope.pages.range = _.range($scope.pages.min, $scope.pages.max).map(function (i) {
return {
number: i,
mode: i == $scope.pages.current ? 'current' : i == $scope.pages.current - 1 ? 'prev' : i == $scope.pages.current + 1 ? 'next' : 'normal'
};
});
} // }}}
// Page view calculation {{{
if (qbTableSettings.pagination.showPages) {
$scope.pages = {
current: $scope.qbTable.query.limit ? Math.floor(($scope.qbTable.query.skip || 0) / $scope.qbTable.query.limit) : false
};
$scope.pages.min = Math.max($scope.pages.current - qbTableSettings.pagination.pageRangeBack, 0);
$scope.pages.total = $scope.qbTable.query.limit ? Math.ceil($scope.qbTable.count / $scope.qbTable.query.limit) : 1; // No limit specified therefore there is only one page
$scope.pages.max = Math.min($scope.pages.total, $scope.pages.current + qbTableSettings.pagination.pageRangeFore + 1);
$scope.pages.range = _.range($scope.pages.min, $scope.pages.max).map(function (i) {
return {
number: i,
mode: i == $scope.pages.current ? 'current' : i == $scope.pages.current - 1 ? 'prev' : i == $scope.pages.current + 1 ? 'next' : 'normal'
};
});
}
// }}}
});
});
$scope.navPageRelative = function (pageRelative) {
if (pageRelative == -1) {
$scope.qbTable.setField('skip', Math.max(($scope.qbTable.query.skip || 0) - ($scope.qbTable.query.limit || 10), 0)).setDirty();
} else if (pageRelative == 1) {
$scope.qbTable.setField('skip', ($scope.qbTable.query.skip || 0) + ($scope.qbTable.query.limit || 10)).setDirty();
} else {
throw new Error('Unsupported page move: ' + pageRelative);
}
};
$scope.navPageRelative = function (pageRelative) {
if (pageRelative == -1) {
$scope.qbTable.setField('skip', Math.max(($scope.qbTable.query.skip || 0) - ($scope.qbTable.query.limit || 10), 0)).setDirty();
} else if (pageRelative == 1) {
$scope.qbTable.setField('skip', ($scope.qbTable.query.skip || 0) + ($scope.qbTable.query.limit || 10)).setDirty();
} else {
throw new Error('Unsupported page move: ' + pageRelative);
}
};
$scope.navPageNumber = function (number) {
return $scope.qbTable.setField('skip', (number || 0) * ($scope.qbTable.query.limit || 10)).setDirty();
};
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: '\n\t\t<nav>\n\t\t\t<ul class="pager">\n\t\t\t\t<li ng-class="canPrev ? \'\' : \'disabled\'" class="previous"><a ng-click="navPageRelative(-1)"><i ng-class="qbTableSettings.icons.paginationPrev"></i></a></li>\n\t\t\t\t<ng-transclude class="text-center">\n\t\t\t\t\t<span ng-if="qbTableSettings.pagination.showXOfY && showRange.end" class="display-xofy">\n\t\t\t\t\t\tShowing documents {{showRange.start | number}} - {{showRange.end | number}}\n\t\t\t\t\t\t<span ng-if="showRange.total">\n\t\t\t\t\t\t\tof {{showRange.total | number}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul ng-if="qbTableSettings.pagination.showPages && showRange.end && pages.max > 1" class="display-pages pagination">\n\t\t\t\t\t\t<li ng-repeat="page in pages.range track by page.number" ng-class="page.mode == \'current\' ? \'active\' : \'\'">\n\t\t\t\t\t\t\t<a ng-click="navPageNumber(page.number)">\n\t\t\t\t\t\t\t\t{{page.number + 1 | number}}\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</ng-transclude>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i ng-class="qbTableSettings.icons.paginationNext"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t'
};
})
// }}}
$scope.navPageNumber = function (number) {
return $scope.qbTable.setField('skip', (number || 0) * ($scope.qbTable.query.limit || 10)).setDirty();
};
}],
link: function link(scope, element, attrs, parentScope) {
scope.qbTable = parentScope;
},
template: "\n\t\t<nav>\n\t\t\t<ul class=\"pager\">\n\t\t\t\t<li ng-class=\"canPrev ? '' : 'disabled'\" class=\"previous\"><a ng-click=\"navPageRelative(-1)\"><i ng-class=\"qbTableSettings.icons.paginationPrev\"></i></a></li>\n\t\t\t\t<ng-transclude class=\"text-center\">\n\t\t\t\t\t<span ng-if=\"qbTableSettings.pagination.showXOfY && showRange.end\" class=\"display-xofy\">\n\t\t\t\t\t\tShowing documents {{showRange.start | number}} - {{showRange.end | number}}\n\t\t\t\t\t\t<span ng-if=\"showRange.total\">\n\t\t\t\t\t\t\tof {{showRange.total | number}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul ng-if=\"qbTableSettings.pagination.showPages && showRange.end && pages.max > 1\" class=\"display-pages pagination\">\n\t\t\t\t\t\t<li ng-repeat=\"page in pages.range track by page.number\" ng-class=\"page.mode == 'current' ? 'active' : ''\">\n\t\t\t\t\t\t\t<a ng-click=\"navPageNumber(page.number)\">\n\t\t\t\t\t\t\t\t{{page.number + 1 | number}}\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</ng-transclude>\n\t\t\t\t<li ng-class=\"canNext ? '' : 'disabled'\" class=\"next\"><a ng-click=\"navPageRelative(1)\"><i ng-class=\"qbTableSettings.icons.paginationNext\"></i></a></li>\n\t\t\t</ul>\n\t\t</nav>\n\t"
};
}) // }}}
// qbExport (directive) {{{
// qbExport (directive) {{{
/**

@@ -532,96 +536,94 @@ * Directive to export a table via a query

.directive('qbExport', function () {
return {
scope: {
query: '<',
spec: '<',
url: '@'
},
transclude: true,
restrict: 'EA',
controller: ['$element', '$httpParamSerializerJQLike', '$scope', '$timeout', '$window', 'qbTableSettings', 'qbTableUtilities', function controller($element, $httpParamSerializerJQLike, $scope, $timeout, $window, qbTableSettings, qbTableUtilities) {
var $ctrl = this;
return {
scope: {
query: '<',
spec: '<',
url: '@'
},
transclude: true,
restrict: 'EA',
controller: ["$element", "$httpParamSerializerJQLike", "$scope", "$timeout", "$window", "qbTableSettings", "qbTableUtilities", function controller($element, $httpParamSerializerJQLike, $scope, $timeout, $window, qbTableSettings, qbTableUtilities) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$scope.settings = {}; // Set in $scope.exportPrompt()
$scope.qbTableSettings = qbTableSettings;
$scope.isShowing = false;
$scope.settings = {}; // Set in $scope.exportPrompt()
$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: _($scope.spec).pickBy(function (v) {
return v && v.type && ['string', 'number', 'data', 'boolean', 'objectid'].includes(v.type);
}).map(function (v, k) {
v.id = k;
v.title = _.startCase(k);
v.selected = true;
return v;
}).value(),
questions: _(qbTableSettings.export.questions) // Populate questions with defaults
.mapKeys(function (v) {
return v.id;
}).mapValues(function (v) {
return v.default;
}).value()
});
$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.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;
}),
questions: _(qbTableSettings.export.questions) // Populate questions with defaults
.mapKeys(function (v) {
return v.id;
}).mapValues(function (v) {
return v.default;
}).value()
});
$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
}, $scope.settings.questions);
$window.open($scope.url + '?' + $httpParamSerializerJQLike(query));
}; // Generate a readable synopsis of the query {{{
$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
}, $scope.settings.questions);
$scope.querySynopsis;
$scope.$watchGroup(['isShowing', 'settings.query'], function () {
if (!$scope.isShowing) return; // Don't bother if we're not showing anything anyway
$window.open($scope.url + '?' + $httpParamSerializerJQLike(query));
};
$scope.querySynopsis = qbTableUtilities.getSynopsis($scope.settings.query);
}); // }}}
// Generate a readable synopsis of the columns collapse {{{
// 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);
});
// }}}
$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
// 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';
});
// }}}
}],
link: function link($scope, $elem) {
$elem.find('ng-transclude').on('click', function () {
return $scope.$applyAsync(function () {
return $scope.exportPrompt();
});
});
},
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 ng-class="qbTableSettings.icons.modalClose"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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\t<div ng-repeat="question in qbTableSettings.export.questions track by question.id" class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">{{question.title}}</label>\n\t\t\t\t\t\t\t<div ng-switch="question.type" class="col-sm-9">\n\t\t\t\t\t\t\t\t<div ng-switch-when="text">\n\t\t\t\t\t\t\t\t\t<input type="text" ng-model="settings.questions[question.id]" class="form-control"/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div ng-switch-default>\n\t\t\t\t\t\t\t\t\t<div class="alert alert-danger">\n\t\t\t\t\t\t\t\t\t\tUnknown question type: "{{question.type}}"\n\t\t\t\t\t\t\t\t\t\t<pre>{{question | json}}</pre>\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\t<div ng-if="question.help" class="help-block">{{question.help}}</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'
};
})
// }}}
$scope.columnSynopsis = $scope.settings.columns.filter(function (c) {
return c.selected;
}).length + ' columns';
}); // }}}
}],
link: function link($scope, $elem) {
$elem.find('ng-transclude').on('click', function () {
return $scope.$applyAsync(function () {
return $scope.exportPrompt();
});
});
},
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 ng-class=\"qbTableSettings.icons.modalClose\"></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 ng-class=\"qbTableSettings.icons.modalCollapseClosed\"></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 ng-class=\"qbTableSettings.icons.modalCollapseClosed\"></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\t<div ng-repeat=\"question in qbTableSettings.export.questions track by question.id\" class=\"form-group\">\n\t\t\t\t\t\t\t<label class=\"col-sm-3 control-label\">{{question.title}}</label>\n\t\t\t\t\t\t\t<div ng-switch=\"question.type\" class=\"col-sm-9\">\n\t\t\t\t\t\t\t\t<div ng-switch-when=\"text\">\n\t\t\t\t\t\t\t\t\t<input type=\"text\" ng-model=\"settings.questions[question.id]\" class=\"form-control\"/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div ng-switch-default>\n\t\t\t\t\t\t\t\t\t<div class=\"alert alert-danger\">\n\t\t\t\t\t\t\t\t\t\tUnknown question type: \"{{question.type}}\"\n\t\t\t\t\t\t\t\t\t\t<pre>{{question | json}}</pre>\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\t<div ng-if=\"question.help\" class=\"help-block\">{{question.help}}</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"
};
}) // }}}
// qbModal (directive) {{{
// qbModal (directive) {{{
/**

@@ -639,47 +641,47 @@ * Button binding that shows a modal allowing the user to edit a query

.directive('qbModal', function () {
return {
scope: {
query: '=',
spec: '<',
title: '@?',
onRefresh: '&?',
binding: '@?'
},
transclude: true,
restrict: 'A',
controller: ['$element', '$scope', 'qbTableSettings', function controller($element, $scope, qbTableSettings) {
var $ctrl = this;
return {
scope: {
query: '=',
spec: '<',
title: '@?',
onRefresh: '&?',
binding: '@?'
},
transclude: true,
restrict: 'A',
controller: ["$element", "$scope", "qbTableSettings", function controller($element, $scope, qbTableSettings) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$ctrl.isShown = false;
$scope.qbTableSettings = qbTableSettings;
$ctrl.rebind = function () {
$element.one('click', function () {
$element.find('.qb-modal').one('hide.bs.modal', function () {
$ctrl.isShown = false;
}).one('hidden.bs.modal', function () {
$ctrl.rebind();
}).modal('show');
});
};
$ctrl.isShown = false;
$ctrl.rebind = function () {
$element.one('click', function () {
$element.find('.qb-modal').one('hide.bs.modal', function () {
$ctrl.isShown = false;
}).one('hidden.bs.modal', function () {
$ctrl.rebind();
}).modal('show');
});
};
$scope.submit = function () {
if (angular.isFunction($ctrl.onRefresh)) $ctrl.onRefresh({
query: $scope.queryCopy,
spec: $scope.spec
});
if (!$scope.binding || $scope.binding == 'complete') $scope.query = $scope.queryCopy;
$element.find('.qb-modal').modal('hide');
};
$scope.submit = function () {
if (angular.isFunction($ctrl.onRefresh)) $ctrl.onRefresh({ query: $scope.queryCopy, spec: $scope.spec });
if (!$scope.binding || $scope.binding == 'complete') $scope.query = $scope.queryCopy;
$ctrl.$onInit = function () {
$scope.queryCopy = $scope.binding == 'live' ? $scope.query : angular.copy($scope.query);
};
$element.find('.qb-modal').modal('hide');
};
$ctrl.rebind();
}],
template: "\n\t\t<ng-transclude></ng-transclude>\n\t\t<div class=\"qb-modal modal fade\">\n\t\t\t<div class=\"modal-dialog modal-lg\">\n\t\t\t\t<div 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 ng-class=\"qbTableSettings.icons.modalClose\"></i></a>\n\t\t\t\t\t\t<h4 class=\"modal-title\">{{title || 'Edit Filter'}}</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"modal-body\">\n\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\tquery=\"queryCopy\"\n\t\t\t\t\t\t\tspec=\"spec\"\n\t\t\t\t\t\t></ui-query-builder>\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=\"submit()\" class=\"btn btn-success\">Refresh</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"
};
}) // }}}
// qbSearch (directive) {{{
$ctrl.$onInit = function () {
$scope.queryCopy = $scope.binding == 'live' ? $scope.query : angular.copy($scope.query);
};
$ctrl.rebind();
}],
template: '\n\t\t<ng-transclude></ng-transclude>\n\t\t<div class="qb-modal modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div 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 ng-class="qbTableSettings.icons.modalClose"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">{{title || \'Edit Filter\'}}</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body">\n\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\tquery="queryCopy"\n\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t></ui-query-builder>\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="submit()" class="btn btn-success">Refresh</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'
};
})
// }}}
// qbSearch (directive) {{{
/**

@@ -697,173 +699,193 @@ * Directive to automatically populate a generic search into a query via a single textbox

.directive('qbSearch', function () {
return {
scope: {
query: '=',
spec: '<',
onRefresh: '&?',
fields: '<?',
useIndexes: '@?'
},
restrict: 'AE',
transclude: true,
controller: ['$element', '$scope', '$rootScope', '$timeout', 'qbTableSettings', 'qbTableUtilities', function controller($element, $scope, $rootScope, $timeout, qbTableSettings, qbTableUtilities) {
var $ctrl = this;
return {
scope: {
query: '=',
spec: '<',
onRefresh: '&?',
fields: '<?',
useIndexes: '@?'
},
restrict: 'AE',
transclude: true,
controller: ["$element", "$scope", "$rootScope", "$timeout", "qbTableSettings", "qbTableUtilities", function controller($element, $scope, $rootScope, $timeout, qbTableSettings, qbTableUtilities) {
var $ctrl = this;
$scope.qbTableSettings = qbTableSettings;
$scope.search = '';
$scope.isSearching = false;
/**
* Submit a search query - injecting the search terms into the query as needed
* @param {boolean} [clear=true] Clear the existing search before continuing
*/
$scope.qbTableSettings = qbTableSettings;
$scope.submit = function () {
var clear = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
if (!$scope.search && clear) return $scope.clear(false);
var safeRegEx = qbTableUtilities.escapeRegExp(_.trim($scope.search));
var searchQuery = {
$comment: 'search',
$or: _($scope.spec).pickBy(function (v) {
return v.type == 'string';
}).mapValues(function (v, k) {
return [{
$regex: safeRegEx,
$options: 'i'
}];
}).value()
};
var existingQuery = qbTableUtilities.find($scope.query, {
$comment: 'search'
});
var newQuery = angular.copy($scope.query);
$scope.search = '';
$scope.isSearching = false;
if (existingQuery && _.isEqual(existingQuery, ['$comment'])) {
// Existing - found at root level
newQuery = searchQuery;
} else if (existingQuery && existingQuery[0] == '$and') {
// Existing - Found within $and wrapper
_.set(newQuery, existingQuery, searchQuery);
} else if (_.isEqual(_.keys(newQuery), ['$and'])) {
// Non-existing - Query is of form {$and: QUERY} --
newQuery.$and.push(searchQuery);
} else if (_.isObject(newQuery)) {
// Non-existing - Append as a single key $or
var indexMethod = $ctrl.useIndexes || 'auto';
/**
* Submit a search query - injecting the search terms into the query as needed
* @param {boolean} [clear=true] Clear the existing search before continuing
*/
$scope.submit = function () {
var clear = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
if (indexMethod == 'auto') {
// Determine what indexing method to use before we begin
indexMethod = _.keys($scope.spec).some(function (k) {
return k != '_id' && $scope.spec[k].index;
}) ? 'stringIndexed' : 'string';
}
if (!$scope.search && clear) return $scope.clear(false);
if ($scope.fields) {
// User is specifying fields to search
newQuery.$or = _($scope.fields).map(function (k) {
return _defineProperty({}, k, {
$regex: qbTableUtilities.escapeRegExp($scope.search),
$options: 'i'
});
}).value();
} else {
// Auto-compute the fields to use
newQuery.$or = _($scope.spec).pickBy(function (v, k) {
if (k == '_id') return false; // Never search by ID
var safeRegEx = qbTableUtilities.escapeRegExp(_.trim($scope.search));
var searchQuery = {
$comment: 'search',
$or: _($scope.spec).pickBy(function (v) {
return v.type == 'string';
}).mapValues(function (v, k) {
return [{ $regex: safeRegEx, $options: 'i' }];
}).value()
};
if ($scope.fields && $scope.fields.length) return $scope.fields.includes(k);
var existingQuery = qbTableUtilities.find($scope.query, { $comment: 'search' });
var newQuery = angular.copy($scope.query);
if (existingQuery && _.isEqual(existingQuery, ['$comment'])) {
// Existing - found at root level
newQuery = searchQuery;
} else if (existingQuery && existingQuery[0] == '$and') {
// Existing - Found within $and wrapper
_.set(newQuery, existingQuery, searchQuery);
} else if (_.isEqual(_.keys(newQuery), ['$and'])) {
// Non-existing - Query is of form {$and: QUERY} --
newQuery.$and.push(searchQuery);
} else if (_.isObject(newQuery)) {
// Non-existing - Append as a single key $or
var indexMethod = $ctrl.useIndexes || 'auto';
if (indexMethod == 'auto') {
// Determine what indexing method to use before we begin
indexMethod = _.keys($scope.spec).some(function (k) {
return k != '_id' && $scope.spec[k].index;
}) ? 'stringIndexed' : 'string';
}
switch (indexMethod) {
case 'all':
return true;
if ($scope.fields) {
// User is specifying fields to search
newQuery.$or = _($scope.fields).map(function (k) {
return _defineProperty({}, k, { $regex: qbTableUtilities.escapeRegExp($scope.search), $options: 'i' });
}).value();
} else {
// Auto-compute the fields to use
newQuery.$or = _($scope.spec).pickBy(function (v, k) {
if (k == '_id') return false; // Never search by ID
if ($scope.fields && $scope.fields.length) return $scope.fields.includes(k);
switch (indexMethod) {
case 'all':
return true;
case 'string':
return v.type == 'string';
case 'stringIndexed':
return v.type == 'string' && v.index;
default:
throw new Error('Unknown field selection method: "' + indexMethod + '"');
}
}).map(function (v, k) {
return _defineProperty({}, k, { $regex: qbTableUtilities.escapeRegExp($scope.search), $options: 'i' });
}).value();
}
} else {
// Give up
console.warn(qbTableSettings.debugPrefix, 'Unable to inject search term', searchQuery, 'within complex query object', newQuery);
}
case 'string':
return v.type == 'string';
$scope.isSearching = true;
case 'stringIndexed':
return v.type == 'string' && v.index;
// Inform the main query builder that we've changed something
$rootScope.$broadcast('queryBuilder.change.replace', newQuery);
if (angular.isFunction($ctrl.onRefresh)) $ctrl.onRefresh({ query: newQuery });
if ($ctrl.binding == 'complete' || angular.isUndefined($ctrl.binding)) {
$scope.query = newQuery;
}
};
default:
throw new Error('Unknown field selection method: "' + indexMethod + '"');
}
}).map(function (v, k) {
return _defineProperty({}, k, {
$regex: qbTableUtilities.escapeRegExp($scope.search),
$options: 'i'
});
}).value();
}
} else {
// Give up
console.warn(qbTableSettings.debugPrefix, 'Unable to inject search term', searchQuery, 'within complex query object', newQuery);
}
/**
* Attempt to remove a search query from the currently active query block
* @param {boolean} [refocus=true] Attempt to move the user focus to the input element when clearing
*/
$scope.clear = function () {
var refocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
$scope.isSearching = true; // Inform the main query builder that we've changed something
var existingQuery = qbTableUtilities.find($scope.query, { $comment: 'search' });
$scope.isSearching = false;
$scope.search = '';
$rootScope.$broadcast('queryBuilder.change.replace', newQuery);
if (angular.isFunction($ctrl.onRefresh)) $ctrl.onRefresh({
query: newQuery
});
if (refocus) angular.element($element).find('input').focus();
if ($ctrl.binding == 'complete' || angular.isUndefined($ctrl.binding)) {
$scope.query = newQuery;
}
};
/**
* Attempt to remove a search query from the currently active query block
* @param {boolean} [refocus=true] Attempt to move the user focus to the input element when clearing
*/
var newQuery;
if (existingQuery && _.isEqual(existingQuery, ['$comment'])) {
// Existing - found at root level
newQuery = {};
} else if (existingQuery && existingQuery[0] == '$and') {
// Existing - Found within $and wrapper, unwrap and return to simple key/val format
newQuery = angular.copy($scope.query);
newQuery.$and.find(function (v, k) {
return v.$comment != 'search';
});
} else if (existingQuery) {
// Existing - Delete by path
newQuery = angular.copy($scope.query);
_.unset(newQuery, existingQuery);
} else if ($scope.query.$or && $scope.query.$or.every(function (field) {
return _.size(field) == 1 && _.chain(field).first().keys().find(function (k) {
return k == '$regEx';
});
})) {
newQuery = angular.copy($scope.query);
delete newQuery.$or;
} else if (qbTableSettings.debug) {
// Scream if we can't find the query anywhere and debugging mode is enabled
console.warn(qbTableSettings.debugPrefix, 'Unable to clear search query within complex query - or query doesnt contain a search anyway', $scope.query);
return;
} else {
// Give up - this should only happen either when:
// a) there is no search term anyway and we are being asked to clear
// b) we can't find any search term using any of the techniques above
return;
}
// Inform the main query builder that we've changed something
$rootScope.$broadcast('queryBuilder.change.replace', newQuery);
if (angular.isFunction($ctrl.onRefresh)) $ctrl.onRefresh({ query: newQuery });
if ($ctrl.binding == 'complete' || angular.isUndefined($ctrl.binding)) {
$scope.query = newQuery;
}
};
$scope.clear = function () {
var refocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var existingQuery = qbTableUtilities.find($scope.query, {
$comment: 'search'
});
$scope.isSearching = false;
$scope.search = '';
if (refocus) angular.element($element).find('input').focus();
var newQuery;
/**
* Try and populate initial query
* NOTE: This is currently only compatible with query.$or.0.*.$regex level queries
*/
$scope.check = function () {
try {
$scope.search = _.chain($scope.query).get('$or').first().values().first().get('$regex').thru(function (v) {
return qbTableUtilities.unescapeRegExp(v || '');
}).value();
} catch (e) {
$scope.search = '';
}
};
if (existingQuery && _.isEqual(existingQuery, ['$comment'])) {
// Existing - found at root level
newQuery = {};
} else if (existingQuery && existingQuery[0] == '$and') {
// Existing - Found within $and wrapper, unwrap and return to simple key/val format
newQuery = angular.copy($scope.query);
newQuery.$and.find(function (v, k) {
return v.$comment != 'search';
});
} else if (existingQuery) {
// Existing - Delete by path
newQuery = angular.copy($scope.query);
$ctrl.$onInit = function () {
return $scope.check();
};
}],
template: '\n\t\t<ng-transclude>\n\t\t\t<form ng-submit="submit()" class="form-inline">\n\t\t\t\t<div class="form-group">\n\t\t\t\t\t<div class="input-group">\n\t\t\t\t\t\t<input type="text" ng-model="search" class="form-control"/>\n\t\t\t\t\t\t<a ng-click="isSearching ? clear() : submit(false)" class="btn btn-default input-group-addon">\n\t\t\t\t\t\t\t<i ng-class="isSearching ? qbTableSettings.icons.searchClear : qbTableSettings.icons.search"/>\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</div>\n\t\t</ng-transclude>\n\t'
};
});
// }}}
_.unset(newQuery, existingQuery);
} else if ($scope.query.$or && $scope.query.$or.every(function (field) {
return _.size(field) == 1 && _.chain(field).first().keys().find(function (k) {
return k == '$regEx';
});
})) {
newQuery = angular.copy($scope.query);
delete newQuery.$or;
} else if (qbTableSettings.debug) {
// Scream if we can't find the query anywhere and debugging mode is enabled
console.warn(qbTableSettings.debugPrefix, 'Unable to clear search query within complex query - or query doesnt contain a search anyway', $scope.query);
return;
} else {
// Give up - this should only happen either when:
// a) there is no search term anyway and we are being asked to clear
// b) we can't find any search term using any of the techniques above
return;
} // Inform the main query builder that we've changed something
$rootScope.$broadcast('queryBuilder.change.replace', newQuery);
if (angular.isFunction($ctrl.onRefresh)) $ctrl.onRefresh({
query: newQuery
});
if ($ctrl.binding == 'complete' || angular.isUndefined($ctrl.binding)) {
$scope.query = newQuery;
}
};
/**
* Try and populate initial query
* NOTE: This is currently only compatible with query.$or.0.*.$regex level queries
*/
$scope.check = function () {
try {
$scope.search = _.chain($scope.query).get('$or').first().values().first().get('$regex').thru(function (v) {
return qbTableUtilities.unescapeRegExp(v || '');
}).value();
} catch (e) {
$scope.search = '';
}
};
$ctrl.$onInit = function () {
return $scope.check();
};
}],
template: "\n\t\t<ng-transclude>\n\t\t\t<form ng-submit=\"submit()\" class=\"form-inline\">\n\t\t\t\t<div class=\"form-group\">\n\t\t\t\t\t<div class=\"input-group\">\n\t\t\t\t\t\t<input type=\"text\" ng-model=\"search\" class=\"form-control\"/>\n\t\t\t\t\t\t<a ng-click=\"isSearching ? clear() : submit(false)\" class=\"btn btn-default input-group-addon\">\n\t\t\t\t\t\t\t<i ng-class=\"isSearching ? qbTableSettings.icons.searchClear : qbTableSettings.icons.search\"/>\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</div>\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").provider("qbTableSettings",function(){var t=this;return t.debug=!1,t.debugPrefix="[angular-ui-query-builder]",t.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",checkMetaChecked:"fa fa-lg fa-fw fa-check-square-o text-primary",checkMetaSome:"fa fa-lg fa-fw fa-minus-square-o",checkMetaUnchecked:"fa fa-lg fa-fw fa-square-o",checkMetaCaret:"fa fa-caret-down",checkItemChecked:"fa fa-lg fa-fw fa-check-square-o",checkItemUnchecked:"fa fa-lg fa-fw fa-square-o",paginationPrev:"fa fa-arrow-left",paginationNext:"fa fa-arrow-right",modalClose:"fa fa-times",modalCollapseClosed:"fa fa-caret-right pull-right",search:"fa fa-search",searchClear:"fa fa-times"},t.pagination={showXOfY:!0,showPages:!0,pageRangeBack:5,pageRangeFore:5},t.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)"}],questions:[]},t.$get=function(){return t},t}).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(", ")},find:function(t,e){var n,a=_.isFunction(e)?e:_.matches(e);return!!function t(e,s){return a(e,s.slice(s.length-1))?(n=s,!0):_.isArray(e)?e.some(function(e,n){return t(e,s.concat(n))}):_.isObject(e)?_.some(e,function(e,n){return t(e,s.concat(n))}):void 0}(t,[])&&n},escapeRegExp:function(t){return String(t).replace(/(\W)/g,"\\$1")},unescapeRegExp:function(t){return String(t).replace(/\\(\W)/g,"$1")}}}).directive("qbTable",function(){return{scope:{qbTable:"=?",count:"<?",stickyThead:"<?",stickyTfoot:"<?"},restrict:"AC",controller:["$attrs","$element","$rootScope","$scope","$timeout","qbTableSettings",function(t,e,n,a,s,i){var l=this;l.query=a.qbTable,l.count=a.count,a.$watch("count",function(){return l.count=a.count}),l.$broadcast=function(t){for(var e=arguments.length,n=Array(e>1?e-1:0),s=1;s<e;s++)n[s-1]=arguments[s];return a.$broadcast.apply(a,[t].concat(n))},l.$on=function(t,e){return a.$on(t,e)},l.setDirty=function(){i.debug&&console.log(i.debugPrefix,"Declare query dirty",a.qbTable),n.$broadcast("queryBuilder.change",a.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:a.qbTable[t]=e}return l}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)}),a.$watch("count",function(){return e.toggleClass("qb-noresults",0===a.count)}),a.$on("queryBuilder.change.replace",function(t,e){l.query=a.qbTable=e,s(function(){return l.setDirty()})})}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,a,s){var i=this;n.qbTableSettings=s;var l=n.$watchGroup(["qbTable","sortable"],function(){""===t.sortable&&!n.qbTable&&s.debug&&console.warn(s.debugPrefix,"Added qb-col + sortable onto element",e,"but no qb-table query has been assigned on the table element!"),l()});n.canSort=!1,n.isSorted=!1,i.$onInit=function(){n.canSort=n.sortable||""===t.sortable,e.toggleClass("sortable",n.canSort),n.canSort&&e.on("click",function(){return a(n.toggleSort)})},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).setDirty():n.qbCol&&""===t.sortable&&n.qbTable.setField("sort",n.qbCol).setDirty()},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:"=?",onPreSelect:"&?",onSelect:"&?"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,a,s){n.qbTableSettings=s,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.onPreSelect&&n.onPreSelect({value:n.selector}),n.selector=!n.selector,n.qbTable.$broadcast("qbTableCellSelect"),n.onSelect&&a(function(){return n.onSelect({value:n.selector})})})}),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 ng-class="metaStatus == \'all\' ? qbTableSettings.icons.checkMetaChecked : metaStatus == \'some\' ? qbTableSettings.icons.checkMetaUnchecked : qbTableSettings.icons.checkMetaUnchecked"></i>\n\t\t\t\t<i ng-class="qbTableSettings.icons.checkMetaCaret"></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 ng-class="selector ? qbTableSettings.icons.checkItemChecked : qbTableSettings.icons.checkItemUnchecked"></i>\n\t\t</div>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,n){e.qbTableSettings=n,e.canPrev=!0,e.canNext=!0,e.showRange={},e.$watchGroup(["qbTable.query.limit","qbTable.query.skip","qbTable.count"],function(t){e.canPrev=e.qbTable.query.skip>0,e.canNext=!e.qbTable.count||e.qbTable.query.skip+e.qbTable.query.limit<e.qbTable.count,n.pagination.showXOfY&&(e.showRange={start:(e.qbTable.query.skip||0)+1,end:Math.min((e.qbTable.query.skip||0)+e.qbTable.query.limit,e.qbTable.count),total:e.qbTable.count}),n.pagination.showPages&&(e.pages={current:!!e.qbTable.query.limit&&Math.floor((e.qbTable.query.skip||0)/e.qbTable.query.limit)},e.pages.min=Math.max(e.pages.current-n.pagination.pageRangeBack,0),e.pages.total=e.qbTable.query.limit?Math.ceil(e.qbTable.count/e.qbTable.query.limit):1,e.pages.max=Math.min(e.pages.total,e.pages.current+n.pagination.pageRangeFore+1),e.pages.range=_.range(e.pages.min,e.pages.max).map(function(t){return{number:t,mode:t==e.pages.current?"current":t==e.pages.current-1?"prev":t==e.pages.current+1?"next":"normal"}}))}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.max((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0)).setDirty();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)).setDirty()}},e.navPageNumber=function(t){return e.qbTable.setField("skip",(t||0)*(e.qbTable.query.limit||10)).setDirty()}}],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 ng-class="qbTableSettings.icons.paginationPrev"></i></a></li>\n\t\t\t\t<ng-transclude class="text-center">\n\t\t\t\t\t<span ng-if="qbTableSettings.pagination.showXOfY && showRange.end" class="display-xofy">\n\t\t\t\t\t\tShowing documents {{showRange.start | number}} - {{showRange.end | number}}\n\t\t\t\t\t\t<span ng-if="showRange.total">\n\t\t\t\t\t\t\tof {{showRange.total | number}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul ng-if="qbTableSettings.pagination.showPages && showRange.end && pages.max > 1" class="display-pages pagination">\n\t\t\t\t\t\t<li ng-repeat="page in pages.range track by page.number" ng-class="page.mode == \'current\' ? \'active\' : \'\'">\n\t\t\t\t\t\t\t<a ng-click="navPageNumber(page.number)">\n\t\t\t\t\t\t\t\t{{page.number + 1 | number}}\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</ng-transclude>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i ng-class="qbTableSettings.icons.paginationNext"></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","$httpParamSerializerJQLike","$scope","$timeout","$window","qbTableSettings","qbTableUtilities",function(t,e,n,a,s,i,l){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}),questions:_(i.export.questions).mapKeys(function(t){return t.id}).mapValues(function(t){return t.default}).value()}),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},n.settings.questions);s.open(n.url+"?"+e(t))},n.querySynopsis,n.$watchGroup(["isShowing","settings.query"],function(){n.isShowing&&(n.querySynopsis=l.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")})}],link:function(t,e){e.find("ng-transclude").on("click",function(){return t.$applyAsync(function(){return t.exportPrompt()})})},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 ng-class="qbTableSettings.icons.modalClose"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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\t<div ng-repeat="question in qbTableSettings.export.questions track by question.id" class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">{{question.title}}</label>\n\t\t\t\t\t\t\t<div ng-switch="question.type" class="col-sm-9">\n\t\t\t\t\t\t\t\t<div ng-switch-when="text">\n\t\t\t\t\t\t\t\t\t<input type="text" ng-model="settings.questions[question.id]" class="form-control"/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div ng-switch-default>\n\t\t\t\t\t\t\t\t\t<div class="alert alert-danger">\n\t\t\t\t\t\t\t\t\t\tUnknown question type: "{{question.type}}"\n\t\t\t\t\t\t\t\t\t\t<pre>{{question | json}}</pre>\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\t<div ng-if="question.help" class="help-block">{{question.help}}</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'}}).directive("qbModal",function(){return{scope:{query:"=",spec:"<",title:"@?",onRefresh:"&?",binding:"@?"},transclude:!0,restrict:"A",controller:["$element","$scope","qbTableSettings",function(t,e,n){var a=this;e.qbTableSettings=n,a.isShown=!1,a.rebind=function(){t.one("click",function(){t.find(".qb-modal").one("hide.bs.modal",function(){a.isShown=!1}).one("hidden.bs.modal",function(){a.rebind()}).modal("show")})},e.submit=function(){angular.isFunction(a.onRefresh)&&a.onRefresh({query:e.queryCopy,spec:e.spec}),e.binding&&"complete"!=e.binding||(e.query=e.queryCopy),t.find(".qb-modal").modal("hide")},a.$onInit=function(){e.queryCopy="live"==e.binding?e.query:angular.copy(e.query)},a.rebind()}],template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<div class="qb-modal modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div 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 ng-class="qbTableSettings.icons.modalClose"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">{{title || \'Edit Filter\'}}</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body">\n\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\tquery="queryCopy"\n\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t></ui-query-builder>\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="submit()" class="btn btn-success">Refresh</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'}}).directive("qbSearch",function(){return{scope:{query:"=",spec:"<",onRefresh:"&?",fields:"<?",useIndexes:"@?"},restrict:"AE",transclude:!0,controller:["$element","$scope","$rootScope","$timeout","qbTableSettings","qbTableUtilities",function(t,e,n,a,s,i){var l=this;e.qbTableSettings=s,e.search="",e.isSearching=!1,e.submit=function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];if(!e.search&&t)return e.clear(!1);var a=i.escapeRegExp(_.trim(e.search)),r={$comment:"search",$or:_(e.spec).pickBy(function(t){return"string"==t.type}).mapValues(function(t,e){return[{$regex:a,$options:"i"}]}).value()},o=i.find(e.query,{$comment:"search"}),c=angular.copy(e.query);if(o&&_.isEqual(o,["$comment"]))c=r;else if(o&&"$and"==o[0])_.set(c,o,r);else if(_.isEqual(_.keys(c),["$and"]))c.$and.push(r);else if(_.isObject(c)){var u=l.useIndexes||"auto";"auto"==u&&(u=_.keys(e.spec).some(function(t){return"_id"!=t&&e.spec[t].index})?"stringIndexed":"string"),e.fields?c.$or=_(e.fields).map(function(t){return _defineProperty({},t,{$regex:i.escapeRegExp(e.search),$options:"i"})}).value():c.$or=_(e.spec).pickBy(function(t,n){if("_id"==n)return!1;if(e.fields&&e.fields.length)return e.fields.includes(n);switch(u){case"all":return!0;case"string":return"string"==t.type;case"stringIndexed":return"string"==t.type&&t.index;default:throw new Error('Unknown field selection method: "'+u+'"')}}).map(function(t,n){return _defineProperty({},n,{$regex:i.escapeRegExp(e.search),$options:"i"})}).value()}else console.warn(s.debugPrefix,"Unable to inject search term",r,"within complex query object",c);e.isSearching=!0,n.$broadcast("queryBuilder.change.replace",c),angular.isFunction(l.onRefresh)&&l.onRefresh({query:c}),("complete"==l.binding||angular.isUndefined(l.binding))&&(e.query=c)},e.clear=function(){var a=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],r=i.find(e.query,{$comment:"search"});e.isSearching=!1,e.search="",a&&angular.element(t).find("input").focus();var o;if(r&&_.isEqual(r,["$comment"]))o={};else if(r&&"$and"==r[0])(o=angular.copy(e.query)).$and.find(function(t,e){return"search"!=t.$comment});else if(r)o=angular.copy(e.query),_.unset(o,r);else{if(!e.query.$or||!e.query.$or.every(function(t){return 1==_.size(t)&&_.chain(t).first().keys().find(function(t){return"$regEx"==t})}))return s.debug?void console.warn(s.debugPrefix,"Unable to clear search query within complex query - or query doesnt contain a search anyway",e.query):void 0;delete(o=angular.copy(e.query)).$or}n.$broadcast("queryBuilder.change.replace",o),angular.isFunction(l.onRefresh)&&l.onRefresh({query:o}),("complete"==l.binding||angular.isUndefined(l.binding))&&(e.query=o)},e.check=function(){try{e.search=_.chain(e.query).get("$or").first().values().first().get("$regex").thru(function(t){return i.unescapeRegExp(t||"")}).value()}catch(t){e.search=""}},l.$onInit=function(){return e.check()}}],template:'\n\t\t<ng-transclude>\n\t\t\t<form ng-submit="submit()" class="form-inline">\n\t\t\t\t<div class="form-group">\n\t\t\t\t\t<div class="input-group">\n\t\t\t\t\t\t<input type="text" ng-model="search" class="form-control"/>\n\t\t\t\t\t\t<a ng-click="isSearching ? clear() : submit(false)" class="btn btn-default input-group-addon">\n\t\t\t\t\t\t\t<i ng-class="isSearching ? qbTableSettings.icons.searchClear : qbTableSettings.icons.search"/>\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</div>\n\t\t</ng-transclude>\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").provider("qbTableSettings",function(){var t=this;return t.debug=!1,t.debugPrefix="[angular-ui-query-builder]",t.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",checkMetaChecked:"fa fa-lg fa-fw fa-check-square-o text-primary",checkMetaSome:"fa fa-lg fa-fw fa-minus-square-o",checkMetaUnchecked:"fa fa-lg fa-fw fa-square-o",checkMetaCaret:"fa fa-caret-down",checkItemChecked:"fa fa-lg fa-fw fa-check-square-o",checkItemUnchecked:"fa fa-lg fa-fw fa-square-o",paginationPrev:"fa fa-arrow-left",paginationNext:"fa fa-arrow-right",modalClose:"fa fa-times",modalCollapseClosed:"fa fa-caret-right pull-right",search:"fa fa-search",searchClear:"fa fa-times"},t.pagination={showXOfY:!0,showPages:!0,pageRangeBack:5,pageRangeFore:5},t.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)"}],questions:[]},t.$get=function(){return t},t}).service("qbTableUtilities",function(){return{getSynopsis:function(t){var e=_.keys(t).filter(function(t){return!["sort","skip","limit","select"].includes(t)});return[e.length?1==e.length?"1 filter":"".concat(e.length," filters"):"All documents",t.sort?t.sort.startsWith("-")?"sorted by ".concat(t.sort.substr(1)," (reverse order)"):"sorted by ".concat(t.sort):null,t.limit?"limited to ".concat(t.limit," rows"):null,t.offset?"starting at record ".concat(t.skip):null,t.select?"selecting only ".concat(t.select.length," columns"):null].filter(function(t){return t}).join(", ")},find:function(t,e){var s,i=_.isFunction(e)?e:_.matches(e);return!!function n(t,a){return i(t,a.slice(a.length-1))?(s=a,!0):_.isArray(t)?t.some(function(t,e){return n(t,a.concat(e))}):_.isObject(t)?_.some(t,function(t,e){return n(t,a.concat(e))}):void 0}(t,[])&&s},escapeRegExp:function(t){return String(t).replace(/(\W)/g,"\\$1")},unescapeRegExp:function(t){return String(t).replace(/\\(\W)/g,"$1")}}}).directive("qbTable",function(){return{scope:{qbTable:"=?",count:"<?",stickyThead:"<?",stickyTfoot:"<?"},restrict:"AC",controller:["$attrs","$element","$rootScope","$scope","$timeout","qbTableSettings",function(t,e,n,s,a,i){var l=this;l.query=s.qbTable,l.count=s.count,s.$watch("count",function(){return l.count=s.count}),l.$broadcast=function(t){for(var e=arguments.length,n=new Array(1<e?e-1:0),a=1;a<e;a++)n[a-1]=arguments[a];return s.$broadcast.apply(s,[t].concat(n))},l.$on=function(t,e){return s.$on(t,e)},l.setDirty=function(){i.debug&&console.log(i.debugPrefix,"Declare query dirty",s.qbTable),n.$broadcast("queryBuilder.change",s.qbTable)},l.setField=function(t,e){if(null!=e){switch(t){case"sort":l.query.sort===e?l.query.sort="-".concat(e):(l.query.sort,"-".concat(e),l.query.sort=e);break;default:s.qbTable[t]=e}return l}delete l.query[t]},e.addClass("qb-table"),s.$watch("stickyThead",function(){return e.toggleClass("qb-sticky-thead",s.stickyThead||""===t.stickyThead)}),s.$watch("stickyTfoot",function(){return e.toggleClass("qb-sticky-tfoot",s.stickyTfoot||""===t.stickyTfoot)}),s.$watch("count",function(){return e.toggleClass("qb-noresults",0===s.count)}),s.$on("queryBuilder.change.replace",function(t,e){l.query=s.qbTable=e,a(function(){return l.setDirty()})})}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,a,s){n.qbTableSettings=s;var i=n.$watchGroup(["qbTable","sortable"],function(){""===t.sortable&&!n.qbTable&&s.debug&&console.warn(s.debugPrefix,"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,this.$onInit=function(){n.canSort=n.sortable||""===t.sortable,e.toggleClass("sortable",n.canSort),n.canSort&&e.on("click",function(){return a(n.toggleSort)})},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).setDirty():n.qbCol&&""===t.sortable&&n.qbTable.setField("sort",n.qbCol).setDirty()},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:"=?",onPreSelect:"&?",onSelect:"&?"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,a,s){n.qbTableSettings=s,n.isMeta=0<e.parents("thead").length,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.onPreSelect&&n.onPreSelect({value:n.selector}),n.selector=!n.selector,n.qbTable.$broadcast("qbTableCellSelect"),n.onSelect&&a(function(){return n.onSelect({value:n.selector})})})}),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: ".concat(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 ng-class="metaStatus == \'all\' ? qbTableSettings.icons.checkMetaChecked : metaStatus == \'some\' ? qbTableSettings.icons.checkMetaUnchecked : qbTableSettings.icons.checkMetaUnchecked"></i>\n\t\t\t\t<i ng-class="qbTableSettings.icons.checkMetaCaret"></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 ng-class="selector ? qbTableSettings.icons.checkItemChecked : qbTableSettings.icons.checkItemUnchecked"></i>\n\t\t</div>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,n){e.qbTableSettings=n,e.canPrev=!0,e.canNext=!0,e.showRange={},e.$watchGroup(["qbTable.query.limit","qbTable.query.skip","qbTable.count"],function(t){e.canPrev=0<e.qbTable.query.skip,e.canNext=!e.qbTable.count||e.qbTable.query.skip+e.qbTable.query.limit<e.qbTable.count,n.pagination.showXOfY&&(e.showRange={start:(e.qbTable.query.skip||0)+1,end:Math.min((e.qbTable.query.skip||0)+e.qbTable.query.limit,e.qbTable.count),total:e.qbTable.count}),n.pagination.showPages&&(e.pages={current:!!e.qbTable.query.limit&&Math.floor((e.qbTable.query.skip||0)/e.qbTable.query.limit)},e.pages.min=Math.max(e.pages.current-n.pagination.pageRangeBack,0),e.pages.total=e.qbTable.query.limit?Math.ceil(e.qbTable.count/e.qbTable.query.limit):1,e.pages.max=Math.min(e.pages.total,e.pages.current+n.pagination.pageRangeFore+1),e.pages.range=_.range(e.pages.min,e.pages.max).map(function(t){return{number:t,mode:t==e.pages.current?"current":t==e.pages.current-1?"prev":t==e.pages.current+1?"next":"normal"}}))}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.max((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0)).setDirty();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)).setDirty()}},e.navPageNumber=function(t){return e.qbTable.setField("skip",(t||0)*(e.qbTable.query.limit||10)).setDirty()}}],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 ng-class="qbTableSettings.icons.paginationPrev"></i></a></li>\n\t\t\t\t<ng-transclude class="text-center">\n\t\t\t\t\t<span ng-if="qbTableSettings.pagination.showXOfY && showRange.end" class="display-xofy">\n\t\t\t\t\t\tShowing documents {{showRange.start | number}} - {{showRange.end | number}}\n\t\t\t\t\t\t<span ng-if="showRange.total">\n\t\t\t\t\t\t\tof {{showRange.total | number}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul ng-if="qbTableSettings.pagination.showPages && showRange.end && pages.max > 1" class="display-pages pagination">\n\t\t\t\t\t\t<li ng-repeat="page in pages.range track by page.number" ng-class="page.mode == \'current\' ? \'active\' : \'\'">\n\t\t\t\t\t\t\t<a ng-click="navPageNumber(page.number)">\n\t\t\t\t\t\t\t\t{{page.number + 1 | number}}\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</ng-transclude>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i ng-class="qbTableSettings.icons.paginationNext"></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","$httpParamSerializerJQLike","$scope","$timeout","$window","qbTableSettings","qbTableUtilities",function(t,e,n,a,s,i,l){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:_(n.spec).pickBy(function(t){return t&&t.type&&["string","number","data","boolean","objectid"].includes(t.type)}).map(function(t,e){return t.id=e,t.title=_.startCase(e),t.selected=!0,t}).value(),questions:_(i.export.questions).mapKeys(function(t){return t.id}).mapValues(function(t){return t.default}).value()}),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},n.settings.questions);s.open(n.url+"?"+e(t))},n.querySynopsis,n.$watchGroup(["isShowing","settings.query"],function(){n.isShowing&&(n.querySynopsis=l.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")})}],link:function(t,e){e.find("ng-transclude").on("click",function(){return t.$applyAsync(function(){return t.exportPrompt()})})},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 ng-class="qbTableSettings.icons.modalClose"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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\t<div ng-repeat="question in qbTableSettings.export.questions track by question.id" class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">{{question.title}}</label>\n\t\t\t\t\t\t\t<div ng-switch="question.type" class="col-sm-9">\n\t\t\t\t\t\t\t\t<div ng-switch-when="text">\n\t\t\t\t\t\t\t\t\t<input type="text" ng-model="settings.questions[question.id]" class="form-control"/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div ng-switch-default>\n\t\t\t\t\t\t\t\t\t<div class="alert alert-danger">\n\t\t\t\t\t\t\t\t\t\tUnknown question type: "{{question.type}}"\n\t\t\t\t\t\t\t\t\t\t<pre>{{question | json}}</pre>\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\t<div ng-if="question.help" class="help-block">{{question.help}}</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'}}).directive("qbModal",function(){return{scope:{query:"=",spec:"<",title:"@?",onRefresh:"&?",binding:"@?"},transclude:!0,restrict:"A",controller:["$element","$scope","qbTableSettings",function(t,e,n){var a=this;e.qbTableSettings=n,a.isShown=!1,a.rebind=function(){t.one("click",function(){t.find(".qb-modal").one("hide.bs.modal",function(){a.isShown=!1}).one("hidden.bs.modal",function(){a.rebind()}).modal("show")})},e.submit=function(){angular.isFunction(a.onRefresh)&&a.onRefresh({query:e.queryCopy,spec:e.spec}),e.binding&&"complete"!=e.binding||(e.query=e.queryCopy),t.find(".qb-modal").modal("hide")},a.$onInit=function(){e.queryCopy="live"==e.binding?e.query:angular.copy(e.query)},a.rebind()}],template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<div class="qb-modal modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div 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 ng-class="qbTableSettings.icons.modalClose"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">{{title || \'Edit Filter\'}}</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body">\n\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\tquery="queryCopy"\n\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t></ui-query-builder>\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="submit()" class="btn btn-success">Refresh</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'}}).directive("qbSearch",function(){return{scope:{query:"=",spec:"<",onRefresh:"&?",fields:"<?",useIndexes:"@?"},restrict:"AE",transclude:!0,controller:["$element","$scope","$rootScope","$timeout","qbTableSettings","qbTableUtilities",function(a,l,r,t,o,c){var u=this;l.qbTableSettings=o,l.search="",l.isSearching=!1,l.submit=function(){var t=!(0<arguments.length&&void 0!==arguments[0])||arguments[0];if(!l.search&&t)return l.clear(!1);var n=c.escapeRegExp(_.trim(l.search)),e={$comment:"search",$or:_(l.spec).pickBy(function(t){return"string"==t.type}).mapValues(function(t,e){return[{$regex:n,$options:"i"}]}).value()},a=c.find(l.query,{$comment:"search"}),s=angular.copy(l.query);if(a&&_.isEqual(a,["$comment"]))s=e;else if(a&&"$and"==a[0])_.set(s,a,e);else if(_.isEqual(_.keys(s),["$and"]))s.$and.push(e);else if(_.isObject(s)){var i=u.useIndexes||"auto";"auto"==i&&(i=_.keys(l.spec).some(function(t){return"_id"!=t&&l.spec[t].index})?"stringIndexed":"string"),l.fields?s.$or=_(l.fields).map(function(t){return _defineProperty({},t,{$regex:c.escapeRegExp(l.search),$options:"i"})}).value():s.$or=_(l.spec).pickBy(function(t,e){if("_id"==e)return!1;if(l.fields&&l.fields.length)return l.fields.includes(e);switch(i){case"all":return!0;case"string":return"string"==t.type;case"stringIndexed":return"string"==t.type&&t.index;default:throw new Error('Unknown field selection method: "'+i+'"')}}).map(function(t,e){return _defineProperty({},e,{$regex:c.escapeRegExp(l.search),$options:"i"})}).value()}else console.warn(o.debugPrefix,"Unable to inject search term",e,"within complex query object",s);l.isSearching=!0,r.$broadcast("queryBuilder.change.replace",s),angular.isFunction(u.onRefresh)&&u.onRefresh({query:s}),("complete"==u.binding||angular.isUndefined(u.binding))&&(l.query=s)},l.clear=function(){var t,e=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],n=c.find(l.query,{$comment:"search"});if(l.isSearching=!1,l.search="",e&&angular.element(a).find("input").focus(),n&&_.isEqual(n,["$comment"]))t={};else if(n&&"$and"==n[0])(t=angular.copy(l.query)).$and.find(function(t,e){return"search"!=t.$comment});else if(n)t=angular.copy(l.query),_.unset(t,n);else{if(!l.query.$or||!l.query.$or.every(function(t){return 1==_.size(t)&&_.chain(t).first().keys().find(function(t){return"$regEx"==t})}))return o.debug?void console.warn(o.debugPrefix,"Unable to clear search query within complex query - or query doesnt contain a search anyway",l.query):void 0;delete(t=angular.copy(l.query)).$or}r.$broadcast("queryBuilder.change.replace",t),angular.isFunction(u.onRefresh)&&u.onRefresh({query:t}),("complete"==u.binding||angular.isUndefined(u.binding))&&(l.query=t)},l.check=function(){try{l.search=_.chain(l.query).get("$or").first().values().first().get("$regex").thru(function(t){return c.unescapeRegExp(t||"")}).value()}catch(t){l.search=""}},u.$onInit=function(){return l.check()}}],template:'\n\t\t<ng-transclude>\n\t\t\t<form ng-submit="submit()" class="form-inline">\n\t\t\t\t<div class="form-group">\n\t\t\t\t\t<div class="input-group">\n\t\t\t\t\t\t<input type="text" ng-model="search" class="form-control"/>\n\t\t\t\t\t\t<a ng-click="isSearching ? clear() : submit(false)" class="btn btn-default input-group-addon">\n\t\t\t\t\t\t\t<i ng-class="isSearching ? qbTableSettings.icons.searchClear : qbTableSettings.icons.search"/>\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</div>\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}var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};angular.module("angular-ui-query-builder",[]).service("QueryBuilder",function(){var t=this;t.cleanSpec=function(t){return _(t).mapValues(function(t,e){return{type:t.type,enum:_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value()}}).value()},t.metaProperties={limit:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!0},populate:{type:"hidden"},skip:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!0},sort:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!1}},t.queryPathPrototypeActions=[{id:"$eq",title:"Equals"},{id:"$neq",title:"Doesnt equal"},{id:"$lt",title:"Is less than"},{id:"$lte",title:"Is equal to or less than"},{id:"$gt",title:"Is greater than"},{id:"$gte",title:"Is equal or greater than"},{id:"$in",title:"Is one of"},{id:"$nin",title:"Is not one of"},{id:"$exists",title:"Has a value"},{id:"$nexists",title:"Does not have a value"}],t.queryPathPrototype=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=arguments[2],l=i[e],r=_.isObject(n)&&_(n).keys().first(),a=_.isObject(n)?_(n).values().first():n;return"$or"==e&&n.every(function(t){return _.isObject(t)&&1==_.keys(t).length})&&n.map(function(t){return _.chain(t).first().values().first().keys().find(function(t){return"$regexp"==t}).value()}).length==n.length?{path:e,type:"search",title:"Search",value:_.chain(n).first().values().first().get("$regexp").value(),fields:_(n).map(function(t){return _.keys(t)}).flatten().value(),actions:t.queryPathPrototypeActions}:"$and"==e||"$or"==e?(_.isArray(n)||(console.warn("query-builder","Query path",e,"is a meta key",n,"but is not an array!","Given",void 0===n?"undefined":_typeof(n)),n=[]),{path:e,type:"binaryGroup",title:"$and"==e?"AND":"$or"==e?"OR":"UNKNOWN",condition:e.replace(/\$/,""),children:n.map(function(e){return t.queryToArray(e,i)}),actions:t.queryPathPrototypeActions}):t.metaProperties[e]?Object.assign({path:e,title:_.startCase(e),value:n,type:"hidden",action:"$hidden",actions:t.queryPathPrototypeActions},t.metaProperties[e]):"$exists"==r?{path:e,title:n.title||_.startCase(e),value:!!n,type:"exists",action:"$exists",actions:t.queryPathPrototypeActions}:"string"==l.type&&_.isArray(l.enum)&&l.enum.length?{path:e,title:n.title||_.startCase(e),type:"enum",action:n.$in?"$in":n.$nin?"$nin":l.enum.length?"$in":"$eq",enum:l.enum,value:n.$in?n.$in:n.$nin?n.$nin:l.enum.length&&!_.isArray(n)?[n]:n,actions:t.queryPathPrototypeActions}:{path:e,title:n.title||_.startCase(e),type:"string"==l.type?"string":"number"==l.type?"number":"date"==l.type?"date":"string",action:"$eq",value:"date"==l.type?moment(a).toDate():a,actions:t.queryPathPrototypeActions}},t.queryToArray=function(e,n){return _(e).pickBy(function(e,i){var l=n[i]||"$and"==i||"$or"==i||t.metaProperties[i];return l||console.warn("query-builder","Incomming query path",i,"Does not map to anything in spec",n),!!l}).map(function(e,i){return t.queryPathPrototype(i,e,n)}).value()},t.arrayToQuery=function(t){return function(t){return _(t).mapKeys(function(t){return t.path}).mapValues(function(t){switch(t.type){case"string":case"number":case"date":return"$eq"==t.action?t.value:_defineProperty({},t.action,t.value);case"enum":return _defineProperty({},t.action,t.value);case"exists":return{$exists:"$exists"==t.action};case"search":return t.fields.map(function(e){return _defineProperty({},e,{$regexp:t.value,options:"i"})});case"keyVal":case"hidden":return t.value;default:console.warn("Unknown type to convert:",t.type)}}).value()}(t)}}).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<div class="query-container">\n\t\t\t\t<ui-query-builder-group\n\t\t\t\t\tqb-group="$ctrl.qbQuery"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-group>\n\t\t\t</div>\n\t\t</div>\n\t',controller:["$scope","$timeout","QueryBuilder",function(t,e,n){var i=this;i.qbSpec,i.qbQuery;var l=t.$watchGroup(["$ctrl.query","$ctrl.spec"],function(){i.spec&&i.query&&(i.qbSpec=n.cleanSpec(i.spec),i.qbQuery=n.queryToArray(i.query,i.qbSpec),l())});t.$on("queryBuilder.change",function(t,l){return e(function(){l?(i.query=l,i.qbQuery=n.queryToArray(i.query,i.qbSpec)):i.query=n.arrayToQuery(i.qbQuery)})}),t.$on("queryBuilder.pathAction.drop",function(t,e){i.qbQuery=i.qbQuery.filter(function(t){return t.path!=e}),i.query=n.arrayToQuery(i.qbQuery)}),t.$on("queryBuilder.pathAction.swapPath",function(l,r,a){var s=i.qbQuery.findIndex(function(t){return t.path==r});if(!s)throw new Error('Cannot find path "'+r+'" to swap with new path "'+a+'"');i.qbQuery[s]=n.queryPathPrototype(a,void 0,i.qbSpec),e(function(){return t.$broadcast("queryBuilder.focusOperand",a)})}),t.$on("queryBuilder.pathAction.swapAction",function(t,e,n){console.log("SWAPACTION",e,n)}),t.$on("queryBuilder.pathAction.add",function(n,l){i.qbQuery.push({path:"",type:"blank",value:null,fields:[]}),e(function(){return t.$broadcast("queryBuilder.focusPath","")})})}]}).component("uiQueryBuilderGroup",{bindings:{qbGroup:"=",qbSpec:"<"},template:'\n\t\t<div ng-repeat="row in $ctrl.qbGroup | filter:$ctrl.qbGroupFilter" meta-key="{{row.path}}">\n\t\t\t<ui-query-builder-row\n\t\t\t\tqb-item="row"\n\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t></ui-query-builder-row>\n\t\t</div>\n\t\t<button ng-click="$ctrl.add()" type="button" class="btn-add"></button>\n\t',controller:["$scope","QueryBuilder",function(t,e){var n=this;n.add=function(){return t.$emit("queryBuilder.pathAction.add")},n.qbGroupFilter=function(t){return"hidden"!=t.type}}]}).component("uiQueryBuilderRow",{bindings:{qbItem:"=",qbSpec:"<"},controller:["$element","$scope","QueryBuilder",function(t,e,n){var i=this;i.delete=function(t){return e.$emit("queryBuilder.pathAction.drop",t)},i.setChanged=function(){return e.$emit("queryBuilder.change")},i.setAction=function(t){return e.$emit("queryBuilder.pathAction.swapAction",i.qbItem,t)},e.$on("queryBuilder.focusPath",function(e,n){i.qbItem.path==n&&t.find("ui-query-builder-path .dropdown-toggle").dropdown("toggle")}),e.$on("queryBuilder.focusOperand",function(e,n){if(i.qbItem.path==n){var l=t.find("input.form-control");if(1==l.length)return l.focus();console.warn("Unable to focus any element within DOM",t[0],"for type",i.type,"on line item",i.qbItem)}})}],template:'\n\t\t<div ng-switch="$ctrl.qbItem.type">\n\t\t\t\x3c!-- $and / $or condition {{{ --\x3e\n\t\t\t<div ng-switch-when="binaryGroup" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-1 btn-block">\n\t\t\t\t\t\t{{$ctrl.qbItem.title}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div ng-repeat="conditional in $ctrl.qbItem.children" class="query-container clearfix">\n\t\t\t\t\t<ui-query-builder-group\n\t\t\t\t\t\tqb-group="conditional"\n\t\t\t\t\t\tqb-spec="$ctrl.spec"\n\t\t\t\t\t></ui-query-builder-group>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- String {{{ --\x3e\n\t\t\t<div ng-switch-when="string" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Enum {{{ --\x3e\n\t\t\t<div ng-switch-when="enum" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<ui-query-builder-block-menu-multiple\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="3"\n\t\t\t\t\tselected="$ctrl.qbItem.value"\n\t\t\t\t\toptions="$ctrl.qbItem.enum"\n\t\t\t\t></ui-query-builder-block-menu-multiple>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Date {{{ --\x3e\n\t\t\t<div ng-switch-when="date" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="date"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Number {{{ --\x3e\n\t\t\t<div ng-switch-when="number" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-value="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-changed="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="number"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Exists {{{ --\x3e\n\t\t\t<div ng-switch-when="exists" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Search {{{ --\x3e\n\t\t\t<div ng-switch-when="search" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- keyVal (Only title + value) {{{ --\x3e\n\t\t\t<div ng-switch-when="keyVal" class="query-row">\n\t\t\t\t<a ng-if="$ctrl.qbItem.canDelete === undefined || $ctrl.qbItem.canDelete" ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-block\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\ttitle="$ctrl.qbItem.title"\n\t\t\t\t></ui-query-builder-block>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Blank (i.e. field not set yet) {{{ --\x3e\n\t\t\t<div ng-switch-when="blank" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Unknown {{{ --\x3e\n\t\t\t<div ng-switch-default class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-warning btn-block">\n\t\t\t\t\t\tUnknown handler: {{$ctrl.qbItem.type}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t'}).component("uiQueryBuilderPath",{bindings:{level:"<",selected:"<",qbSpec:"<"},controller:["$scope",function(t){var e=this;e.setSelected=function(n){return t.$emit("queryBuilder.pathAction.swapPath",e.selected,n)},e.options,e.$onInit=function(){e.options=_.map(e.qbSpec,function(t,e){return Object.assign({},{path:e,title:_.startCase(e)},t)}),e.selectedOption=e.options.find(function(t){return t.path==e.selected})}}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="path in $ctrl.options track by path.path"><a ng-click="$ctrl.setSelected(path.path)">{{path.title}}</a></li>\n\t\t</ul>\n\t'}).component("uiQueryBuilderBlock",{bindings:{level:"<",title:"<"},controller:["$scope",function(t){}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}}">\n\t\t\t{{$ctrl.title}}\n\t\t</a>\n\t'}).component("uiQueryBuilderBlockMenu",{bindings:{level:"<",options:"<",selected:"=",onChange:"&?"},controller:["$scope",function(t){var e=this;e.setSelected=function(t){e.selected=t.id,angular.isFunction(e.onChange)&&e.onChange({selected:e.selected})},e.selectedOption,t.$watchGroup(["$ctrl.options","$ctrl.selected"],function(){e.selectedOption=e.options.find(function(t){return t.id==e.selected})})}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id"><a ng-click="$ctrl.setSelected(option)">{{option.title}}</a></li>\n\t\t</ul>\n\t'}).component("uiQueryBuilderBlockMenuMultiple",{bindings:{level:"<",options:"<",selected:"="},controller:["$scope",function(t){var e=this;e.toggle=function(n){e.selected||(e.selected=[]),e.selected.includes(n.id)?e.selected=e.selected.filter(function(t){return t!=n.id}):e.selected.push(n.id),t.$emit("queryBuilder.change")},e.selectedOptions,t.$watch("$ctrl.selected",function(){e.selectedOptions=e.options.filter(function(t){return(e.selected||[]).includes(t.id)}),e.options.forEach(function(t){return t.selected=e.selectedOptions.some(function(e){return e.id==t.id})})},!0)}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t<span ng-repeat="item in $ctrl.selectedOptions track by item.id" class="pill">\n\t\t\t\t{{item.title}}\n\t\t\t</span>\n\t\t\t<i class="fa fa-caret-down"></i></a>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id">\n\t\t\t\t<a ng-click="$ctrl.toggle(option)">\n\t\t\t\t\t<i class="fa fa-fw" ng-class="option.selected ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{option.title}}\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t</ul>\n\t'}),angular.module("angular-ui-query-builder").provider("qbTableSettings",function(){var t=this;return t.debug=!1,t.debugPrefix="[angular-ui-query-builder]",t.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",checkMetaChecked:"fa fa-lg fa-fw fa-check-square-o text-primary",checkMetaSome:"fa fa-lg fa-fw fa-minus-square-o",checkMetaUnchecked:"fa fa-lg fa-fw fa-square-o",checkMetaCaret:"fa fa-caret-down",checkItemChecked:"fa fa-lg fa-fw fa-check-square-o",checkItemUnchecked:"fa fa-lg fa-fw fa-square-o",paginationPrev:"fa fa-arrow-left",paginationNext:"fa fa-arrow-right",modalClose:"fa fa-times",modalCollapseClosed:"fa fa-caret-right pull-right",search:"fa fa-search",searchClear:"fa fa-times"},t.pagination={showXOfY:!0,showPages:!0,pageRangeBack:5,pageRangeFore:5},t.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)"}],questions:[]},t.$get=function(){return t},t}).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(", ")},find:function(t,e){var n,i=_.isFunction(e)?e:_.matches(e);return!!function t(e,l){return i(e,l.slice(l.length-1))?(n=l,!0):_.isArray(e)?e.some(function(e,n){return t(e,l.concat(n))}):_.isObject(e)?_.some(e,function(e,n){return t(e,l.concat(n))}):void 0}(t,[])&&n},escapeRegExp:function(t){return String(t).replace(/(\W)/g,"\\$1")},unescapeRegExp:function(t){return String(t).replace(/\\(\W)/g,"$1")}}}).directive("qbTable",function(){return{scope:{qbTable:"=?",count:"<?",stickyThead:"<?",stickyTfoot:"<?"},restrict:"AC",controller:["$attrs","$element","$rootScope","$scope","$timeout","qbTableSettings",function(t,e,n,i,l,r){var a=this;a.query=i.qbTable,a.count=i.count,i.$watch("count",function(){return a.count=i.count}),a.$broadcast=function(t){for(var e=arguments.length,n=Array(e>1?e-1:0),l=1;l<e;l++)n[l-1]=arguments[l];return i.$broadcast.apply(i,[t].concat(n))},a.$on=function(t,e){return i.$on(t,e)},a.setDirty=function(){r.debug&&console.log(r.debugPrefix,"Declare query dirty",i.qbTable),n.$broadcast("queryBuilder.change",i.qbTable)},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:i.qbTable[t]=e}return a}delete a.query[t]},e.addClass("qb-table"),i.$watch("stickyThead",function(){return e.toggleClass("qb-sticky-thead",i.stickyThead||""===t.stickyThead)}),i.$watch("stickyTfoot",function(){return e.toggleClass("qb-sticky-tfoot",i.stickyTfoot||""===t.stickyTfoot)}),i.$watch("count",function(){return e.toggleClass("qb-noresults",0===i.count)}),i.$on("queryBuilder.change.replace",function(t,e){a.query=i.qbTable=e,l(function(){return a.setDirty()})})}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,i,l){var r=this;n.qbTableSettings=l;var a=n.$watchGroup(["qbTable","sortable"],function(){""===t.sortable&&!n.qbTable&&l.debug&&console.warn(l.debugPrefix,"Added qb-col + sortable onto element",e,"but no qb-table query has been assigned on the table element!"),a()});n.canSort=!1,n.isSorted=!1,r.$onInit=function(){n.canSort=n.sortable||""===t.sortable,e.toggleClass("sortable",n.canSort),n.canSort&&e.on("click",function(){return i(n.toggleSort)})},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).setDirty():n.qbCol&&""===t.sortable&&n.qbTable.setField("sort",n.qbCol).setDirty()},e.addClass("qb-col")}],link:function(t,e,n,i){t.qbTable=i},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:"=?",onPreSelect:"&?",onSelect:"&?"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,i,l){n.qbTableSettings=l,n.isMeta=e.parents("thead").length>0,n.isMeta&&i(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.onPreSelect&&n.onPreSelect({value:n.selector}),n.selector=!n.selector,n.qbTable.$broadcast("qbTableCellSelect"),n.onSelect&&i(function(){return n.onSelect({value:n.selector})})})}),n.metaSelect=function(t){return n.qbTable.$broadcast("qbTableCellSelectMeta",t)},n.isSelector&&!n.isMeta&&i(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,i){t.qbTable=i},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 ng-class="metaStatus == \'all\' ? qbTableSettings.icons.checkMetaChecked : metaStatus == \'some\' ? qbTableSettings.icons.checkMetaUnchecked : qbTableSettings.icons.checkMetaUnchecked"></i>\n\t\t\t\t<i ng-class="qbTableSettings.icons.checkMetaCaret"></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 ng-class="selector ? qbTableSettings.icons.checkItemChecked : qbTableSettings.icons.checkItemUnchecked"></i>\n\t\t</div>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,n){e.qbTableSettings=n,e.canPrev=!0,e.canNext=!0,e.showRange={},e.$watchGroup(["qbTable.query.limit","qbTable.query.skip","qbTable.count"],function(t){e.canPrev=e.qbTable.query.skip>0,e.canNext=!e.qbTable.count||e.qbTable.query.skip+e.qbTable.query.limit<e.qbTable.count,n.pagination.showXOfY&&(e.showRange={start:(e.qbTable.query.skip||0)+1,end:Math.min((e.qbTable.query.skip||0)+e.qbTable.query.limit,e.qbTable.count),total:e.qbTable.count}),n.pagination.showPages&&(e.pages={current:!!e.qbTable.query.limit&&Math.floor((e.qbTable.query.skip||0)/e.qbTable.query.limit)},e.pages.min=Math.max(e.pages.current-n.pagination.pageRangeBack,0),e.pages.total=e.qbTable.query.limit?Math.ceil(e.qbTable.count/e.qbTable.query.limit):1,e.pages.max=Math.min(e.pages.total,e.pages.current+n.pagination.pageRangeFore+1),e.pages.range=_.range(e.pages.min,e.pages.max).map(function(t){return{number:t,mode:t==e.pages.current?"current":t==e.pages.current-1?"prev":t==e.pages.current+1?"next":"normal"}}))}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.max((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0)).setDirty();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)).setDirty()}},e.navPageNumber=function(t){return e.qbTable.setField("skip",(t||0)*(e.qbTable.query.limit||10)).setDirty()}}],link:function(t,e,n,i){t.qbTable=i},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 ng-class="qbTableSettings.icons.paginationPrev"></i></a></li>\n\t\t\t\t<ng-transclude class="text-center">\n\t\t\t\t\t<span ng-if="qbTableSettings.pagination.showXOfY && showRange.end" class="display-xofy">\n\t\t\t\t\t\tShowing documents {{showRange.start | number}} - {{showRange.end | number}}\n\t\t\t\t\t\t<span ng-if="showRange.total">\n\t\t\t\t\t\t\tof {{showRange.total | number}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul ng-if="qbTableSettings.pagination.showPages && showRange.end && pages.max > 1" class="display-pages pagination">\n\t\t\t\t\t\t<li ng-repeat="page in pages.range track by page.number" ng-class="page.mode == \'current\' ? \'active\' : \'\'">\n\t\t\t\t\t\t\t<a ng-click="navPageNumber(page.number)">\n\t\t\t\t\t\t\t\t{{page.number + 1 | number}}\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</ng-transclude>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i ng-class="qbTableSettings.icons.paginationNext"></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","$httpParamSerializerJQLike","$scope","$timeout","$window","qbTableSettings","qbTableUtilities",function(t,e,n,i,l,r,a){n.qbTableSettings=r,n.settings={},n.isShowing=!1,n.exportPrompt=function(){n.settings=angular.extend(angular.copy(r.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}),questions:_(r.export.questions).mapKeys(function(t){return t.id}).mapValues(function(t){return t.default}).value()}),t.find(".modal").on("show.bs.modal",function(){return i(function(){return n.isShowing=!0})}).on("hidden.bs.modal",function(){return i(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},n.settings.questions);l.open(n.url+"?"+e(t))},n.querySynopsis,n.$watchGroup(["isShowing","settings.query"],function(){n.isShowing&&(n.querySynopsis=a.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")})}],link:function(t,e){e.find("ng-transclude").on("click",function(){return t.$applyAsync(function(){return t.exportPrompt()})})},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 ng-class="qbTableSettings.icons.modalClose"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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\t<div ng-repeat="question in qbTableSettings.export.questions track by question.id" class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">{{question.title}}</label>\n\t\t\t\t\t\t\t<div ng-switch="question.type" class="col-sm-9">\n\t\t\t\t\t\t\t\t<div ng-switch-when="text">\n\t\t\t\t\t\t\t\t\t<input type="text" ng-model="settings.questions[question.id]" class="form-control"/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div ng-switch-default>\n\t\t\t\t\t\t\t\t\t<div class="alert alert-danger">\n\t\t\t\t\t\t\t\t\t\tUnknown question type: "{{question.type}}"\n\t\t\t\t\t\t\t\t\t\t<pre>{{question | json}}</pre>\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\t<div ng-if="question.help" class="help-block">{{question.help}}</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'}}).directive("qbModal",function(){return{scope:{query:"=",spec:"<",title:"@?",onRefresh:"&?",binding:"@?"},transclude:!0,restrict:"A",controller:["$element","$scope","qbTableSettings",function(t,e,n){var i=this;e.qbTableSettings=n,i.isShown=!1,i.rebind=function(){t.one("click",function(){t.find(".qb-modal").one("hide.bs.modal",function(){i.isShown=!1}).one("hidden.bs.modal",function(){i.rebind()}).modal("show")})},e.submit=function(){angular.isFunction(i.onRefresh)&&i.onRefresh({query:e.queryCopy,spec:e.spec}),e.binding&&"complete"!=e.binding||(e.query=e.queryCopy),t.find(".qb-modal").modal("hide")},i.$onInit=function(){e.queryCopy="live"==e.binding?e.query:angular.copy(e.query)},i.rebind()}],template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<div class="qb-modal modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div 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 ng-class="qbTableSettings.icons.modalClose"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">{{title || \'Edit Filter\'}}</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body">\n\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\tquery="queryCopy"\n\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t></ui-query-builder>\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="submit()" class="btn btn-success">Refresh</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'}}).directive("qbSearch",function(){return{scope:{query:"=",spec:"<",onRefresh:"&?",fields:"<?",useIndexes:"@?"},restrict:"AE",transclude:!0,controller:["$element","$scope","$rootScope","$timeout","qbTableSettings","qbTableUtilities",function(t,e,n,i,l,r){var a=this;e.qbTableSettings=l,e.search="",e.isSearching=!1,e.submit=function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];if(!e.search&&t)return e.clear(!1);var i=r.escapeRegExp(_.trim(e.search)),s={$comment:"search",$or:_(e.spec).pickBy(function(t){return"string"==t.type}).mapValues(function(t,e){return[{$regex:i,$options:"i"}]}).value()},c=r.find(e.query,{$comment:"search"}),o=angular.copy(e.query);if(c&&_.isEqual(c,["$comment"]))o=s;else if(c&&"$and"==c[0])_.set(o,c,s);else if(_.isEqual(_.keys(o),["$and"]))o.$and.push(s);else if(_.isObject(o)){var u=a.useIndexes||"auto";"auto"==u&&(u=_.keys(e.spec).some(function(t){return"_id"!=t&&e.spec[t].index})?"stringIndexed":"string"),e.fields?o.$or=_(e.fields).map(function(t){return _defineProperty({},t,{$regex:r.escapeRegExp(e.search),$options:"i"})}).value():o.$or=_(e.spec).pickBy(function(t,n){if("_id"==n)return!1;if(e.fields&&e.fields.length)return e.fields.includes(n);switch(u){case"all":return!0;case"string":return"string"==t.type;case"stringIndexed":return"string"==t.type&&t.index;default:throw new Error('Unknown field selection method: "'+u+'"')}}).map(function(t,n){return _defineProperty({},n,{$regex:r.escapeRegExp(e.search),$options:"i"})}).value()}else console.warn(l.debugPrefix,"Unable to inject search term",s,"within complex query object",o);e.isSearching=!0,n.$broadcast("queryBuilder.change.replace",o),angular.isFunction(a.onRefresh)&&a.onRefresh({query:o}),("complete"==a.binding||angular.isUndefined(a.binding))&&(e.query=o)},e.clear=function(){var i=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],s=r.find(e.query,{$comment:"search"});e.isSearching=!1,e.search="",i&&angular.element(t).find("input").focus();var c;if(s&&_.isEqual(s,["$comment"]))c={};else if(s&&"$and"==s[0])(c=angular.copy(e.query)).$and.find(function(t,e){return"search"!=t.$comment});else if(s)c=angular.copy(e.query),_.unset(c,s);else{if(!e.query.$or||!e.query.$or.every(function(t){return 1==_.size(t)&&_.chain(t).first().keys().find(function(t){return"$regEx"==t})}))return l.debug?void console.warn(l.debugPrefix,"Unable to clear search query within complex query - or query doesnt contain a search anyway",e.query):void 0;delete(c=angular.copy(e.query)).$or}n.$broadcast("queryBuilder.change.replace",c),angular.isFunction(a.onRefresh)&&a.onRefresh({query:c}),("complete"==a.binding||angular.isUndefined(a.binding))&&(e.query=c)},e.check=function(){try{e.search=_.chain(e.query).get("$or").first().values().first().get("$regex").thru(function(t){return r.unescapeRegExp(t||"")}).value()}catch(t){e.search=""}},a.$onInit=function(){return e.check()}}],template:'\n\t\t<ng-transclude>\n\t\t\t<form ng-submit="submit()" class="form-inline">\n\t\t\t\t<div class="form-group">\n\t\t\t\t\t<div class="input-group">\n\t\t\t\t\t\t<input type="text" ng-model="search" class="form-control"/>\n\t\t\t\t\t\t<a ng-click="isSearching ? clear() : submit(false)" class="btn btn-default input-group-addon">\n\t\t\t\t\t\t\t<i ng-class="isSearching ? qbTableSettings.icons.searchClear : qbTableSettings.icons.search"/>\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</div>\n\t\t</ng-transclude>\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}function _typeof(t){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}angular.module("angular-ui-query-builder",[]).service("QueryBuilder",function(){var a=this;a.cleanSpec=function(t){return _(t).mapValues(function(t,e){return{type:t.type,enum:_(t.enum).map(function(t){return _.isString(t)?{id:t,title:_.startCase(t)}:t}).sortBy("title").value()}}).value()},a.metaProperties={limit:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!0},populate:{type:"hidden"},skip:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!0},sort:{type:"keyVal",actions:[{id:"$eq",title:"Equals"}],action:"$eq",canDelete:!1}},a.queryPathPrototypeActions=[{id:"$eq",title:"Equals"},{id:"$neq",title:"Doesnt equal"},{id:"$lt",title:"Is less than"},{id:"$lte",title:"Is equal to or less than"},{id:"$gt",title:"Is greater than"},{id:"$gte",title:"Is equal or greater than"},{id:"$in",title:"Is one of"},{id:"$nin",title:"Is not one of"},{id:"$exists",title:"Has a value"},{id:"$nexists",title:"Does not have a value"}],a.queryPathPrototype=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},n=2<arguments.length?arguments[2]:void 0,l=n[t],i=_.isObject(e)&&_(e).keys().first(),r=_.isObject(e)?_(e).values().first():e;return"$or"==t&&e.every(function(t){return _.isObject(t)&&1==_.keys(t).length})&&e.map(function(t){return _.chain(t).first().values().first().keys().find(function(t){return"$regexp"==t}).value()}).length==e.length?{path:t,type:"search",title:"Search",value:_.chain(e).first().values().first().get("$regexp").value(),fields:_(e).map(function(t){return _.keys(t)}).flatten().value(),actions:a.queryPathPrototypeActions}:"$and"==t||"$or"==t?(_.isArray(e)||(console.warn("query-builder","Query path",t,"is a meta key",e,"but is not an array!","Given",_typeof(e)),e=[]),{path:t,type:"binaryGroup",title:"$and"==t?"AND":"$or"==t?"OR":"UNKNOWN",condition:t.replace(/\$/,""),children:e.map(function(t){return a.queryToArray(t,n)}),actions:a.queryPathPrototypeActions}):a.metaProperties[t]?Object.assign({path:t,title:_.startCase(t),value:e,type:"hidden",action:"$hidden",actions:a.queryPathPrototypeActions},a.metaProperties[t]):"$exists"==i?{path:t,title:e.title||_.startCase(t),value:!!e,type:"exists",action:"$exists",actions:a.queryPathPrototypeActions}:"string"==l.type&&_.isArray(l.enum)&&l.enum.length?{path:t,title:e.title||_.startCase(t),type:"enum",action:e.$in?"$in":e.$nin?"$nin":l.enum.length?"$in":"$eq",enum:l.enum,value:e.$in?e.$in:e.$nin?e.$nin:l.enum.length&&!_.isArray(e)?[e]:e,actions:a.queryPathPrototypeActions}:{path:t,title:e.title||_.startCase(t),type:"string"==l.type?"string":"number"==l.type?"number":"date"==l.type?"date":"string",action:"$eq",value:"date"==l.type?moment(r).toDate():r,actions:a.queryPathPrototypeActions}},a.queryToArray=function(t,l){return _(t).pickBy(function(t,e){var n=l[e]||"$and"==e||"$or"==e||a.metaProperties[e];return n||console.warn("query-builder","Incomming query path",e,"Does not map to anything in spec",l),!!n}).map(function(t,e){return a.queryPathPrototype(e,t,l)}).value()},a.arrayToQuery=function(t){return _(t).mapKeys(function(t){return t.path}).mapValues(function(e){switch(e.type){case"string":case"number":case"date":return"$eq"==e.action?e.value:_defineProperty({},e.action,e.value);case"enum":return _defineProperty({},e.action,e.value);case"exists":return{$exists:"$exists"==e.action};case"search":return e.fields.map(function(t){return _defineProperty({},t,{$regexp:e.value,options:"i"})});case"keyVal":case"hidden":return e.value;default:console.warn("Unknown type to convert:",e.type)}}).value()}}).component("uiQueryBuilder",{bindings:{query:"=",spec:"<"},template:'\n\t\t<div class="ui-query-builder">\n\t\t\t<div class="query-container">\n\t\t\t\t<ui-query-builder-group\n\t\t\t\t\tqb-group="$ctrl.qbQuery"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-group>\n\t\t\t</div>\n\t\t</div>\n\t',controller:["$scope","$timeout","QueryBuilder",function(i,r,a){var c=this;c.qbSpec,c.qbQuery;var t=i.$watchGroup(["$ctrl.query","$ctrl.spec"],function(){c.spec&&c.query&&(c.qbSpec=a.cleanSpec(c.spec),c.qbQuery=a.queryToArray(c.query,c.qbSpec),t())});i.$on("queryBuilder.change",function(t,e){return r(function(){e?(c.query=e,c.qbQuery=a.queryToArray(c.query,c.qbSpec)):c.query=a.arrayToQuery(c.qbQuery)})}),i.$on("queryBuilder.pathAction.drop",function(t,e){c.qbQuery=c.qbQuery.filter(function(t){return t.path!=e}),c.query=a.arrayToQuery(c.qbQuery)}),i.$on("queryBuilder.pathAction.swapPath",function(t,e,n){var l=c.qbQuery.findIndex(function(t){return t.path==e});if(!l)throw new Error('Cannot find path "'.concat(e,'" to swap with new path "').concat(n,'"'));c.qbQuery[l]=a.queryPathPrototype(n,void 0,c.qbSpec),r(function(){return i.$broadcast("queryBuilder.focusOperand",n)})}),i.$on("queryBuilder.pathAction.swapAction",function(t,e,n){console.log("SWAPACTION",e,n)}),i.$on("queryBuilder.pathAction.add",function(t,e){c.qbQuery.push({path:"",type:"blank",value:null,fields:[]}),r(function(){return i.$broadcast("queryBuilder.focusPath","")})})}]}).component("uiQueryBuilderGroup",{bindings:{qbGroup:"=",qbSpec:"<"},template:'\n\t\t<div ng-repeat="row in $ctrl.qbGroup | filter:$ctrl.qbGroupFilter" meta-key="{{row.path}}">\n\t\t\t<ui-query-builder-row\n\t\t\t\tqb-item="row"\n\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t></ui-query-builder-row>\n\t\t</div>\n\t\t<button ng-click="$ctrl.add()" type="button" class="btn-add"></button>\n\t',controller:["$scope","QueryBuilder",function(t,e){this.add=function(){return t.$emit("queryBuilder.pathAction.add")},this.qbGroupFilter=function(t){return"hidden"!=t.type}}]}).component("uiQueryBuilderRow",{bindings:{qbItem:"=",qbSpec:"<"},controller:["$element","$scope","QueryBuilder",function(l,e,t){var i=this;i.delete=function(t){return e.$emit("queryBuilder.pathAction.drop",t)},i.setChanged=function(){return e.$emit("queryBuilder.change")},i.setAction=function(t){return e.$emit("queryBuilder.pathAction.swapAction",i.qbItem,t)},e.$on("queryBuilder.focusPath",function(t,e){i.qbItem.path==e&&l.find("ui-query-builder-path .dropdown-toggle").dropdown("toggle")}),e.$on("queryBuilder.focusOperand",function(t,e){if(i.qbItem.path==e){var n=l.find("input.form-control");if(1==n.length)return n.focus();console.warn("Unable to focus any element within DOM",l[0],"for type",i.type,"on line item",i.qbItem)}})}],template:'\n\t\t<div ng-switch="$ctrl.qbItem.type">\n\t\t\t\x3c!-- $and / $or condition {{{ --\x3e\n\t\t\t<div ng-switch-when="binaryGroup" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-1 btn-block">\n\t\t\t\t\t\t{{$ctrl.qbItem.title}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div ng-repeat="conditional in $ctrl.qbItem.children" class="query-container clearfix">\n\t\t\t\t\t<ui-query-builder-group\n\t\t\t\t\t\tqb-group="conditional"\n\t\t\t\t\t\tqb-spec="$ctrl.spec"\n\t\t\t\t\t></ui-query-builder-group>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- String {{{ --\x3e\n\t\t\t<div ng-switch-when="string" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Enum {{{ --\x3e\n\t\t\t<div ng-switch-when="enum" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<ui-query-builder-block-menu-multiple\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="3"\n\t\t\t\t\tselected="$ctrl.qbItem.value"\n\t\t\t\t\toptions="$ctrl.qbItem.enum"\n\t\t\t\t></ui-query-builder-block-menu-multiple>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Date {{{ --\x3e\n\t\t\t<div ng-switch-when="date" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="date"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Number {{{ --\x3e\n\t\t\t<div ng-switch-when="number" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-3 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-value="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-changed="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="number"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Exists {{{ --\x3e\n\t\t\t<div ng-switch-when="exists" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<ui-query-builder-block-menu\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="2"\n\t\t\t\t\tselected="$ctrl.qbItem.action"\n\t\t\t\t\toptions="$ctrl.qbItem.actions"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-block-menu>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Search {{{ --\x3e\n\t\t\t<div ng-switch-when="search" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t\ton-change="$ctrl.setAction(selected)"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- keyVal (Only title + value) {{{ --\x3e\n\t\t\t<div ng-switch-when="keyVal" class="query-row">\n\t\t\t\t<a ng-if="$ctrl.qbItem.canDelete === undefined || $ctrl.qbItem.canDelete" ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-block\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\ttitle="$ctrl.qbItem.title"\n\t\t\t\t></ui-query-builder-block>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-2 btn-block">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tng-model="$ctrl.qbItem.value"\n\t\t\t\t\t\t\tng-change="$ctrl.setChanged()"\n\t\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\t\tclass="form-control"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Blank (i.e. field not set yet) {{{ --\x3e\n\t\t\t<div ng-switch-when="blank" class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t\t\x3c!-- Unknown {{{ --\x3e\n\t\t\t<div ng-switch-default class="query-row">\n\t\t\t\t<a ng-click="$ctrl.delete($ctrl.qbItem.path)" class="btn-trash"></a>\n\t\t\t\t<ui-query-builder-path\n\t\t\t\t\tclass="query-block"\n\t\t\t\t\tlevel="1"\n\t\t\t\t\tselected="$ctrl.qbItem.path"\n\t\t\t\t\tqb-spec="$ctrl.qbSpec"\n\t\t\t\t></ui-query-builder-path>\n\t\t\t\t<div class="query-block">\n\t\t\t\t\t<div class="btn btn-warning btn-block">\n\t\t\t\t\t\tUnknown handler: {{$ctrl.qbItem.type}}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t\x3c!-- }}} --\x3e\n\t\t</div>\n\t'}).component("uiQueryBuilderPath",{bindings:{level:"<",selected:"<",qbSpec:"<"},controller:["$scope",function(e){var n=this;n.setSelected=function(t){return e.$emit("queryBuilder.pathAction.swapPath",n.selected,t)},n.options,n.$onInit=function(){n.options=_.map(n.qbSpec,function(t,e){return Object.assign({},{path:e,title:_.startCase(e)},t)}),n.selectedOption=n.options.find(function(t){return t.path==n.selected})}}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="path in $ctrl.options track by path.path"><a ng-click="$ctrl.setSelected(path.path)">{{path.title}}</a></li>\n\t\t</ul>\n\t'}).component("uiQueryBuilderBlock",{bindings:{level:"<",title:"<"},controller:["$scope",function(t){}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}}">\n\t\t\t{{$ctrl.title}}\n\t\t</a>\n\t'}).component("uiQueryBuilderBlockMenu",{bindings:{level:"<",options:"<",selected:"=",onChange:"&?"},controller:["$scope",function(t){var e=this;e.setSelected=function(t){e.selected=t.id,angular.isFunction(e.onChange)&&e.onChange({selected:e.selected})},e.selectedOption,t.$watchGroup(["$ctrl.options","$ctrl.selected"],function(){e.selectedOption=e.options.find(function(t){return t.id==e.selected})})}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t{{$ctrl.selectedOption.title}}\n\t\t\t<i class="fa fa-caret-down"></i>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id"><a ng-click="$ctrl.setSelected(option)">{{option.title}}</a></li>\n\t\t</ul>\n\t'}).component("uiQueryBuilderBlockMenuMultiple",{bindings:{level:"<",options:"<",selected:"="},controller:["$scope",function(t){var n=this;n.toggle=function(e){n.selected||(n.selected=[]),n.selected.includes(e.id)?n.selected=n.selected.filter(function(t){return t!=e.id}):n.selected.push(e.id),t.$emit("queryBuilder.change")},n.selectedOptions,t.$watch("$ctrl.selected",function(){n.selectedOptions=n.options.filter(function(t){return(n.selected||[]).includes(t.id)}),n.options.forEach(function(e){return e.selected=n.selectedOptions.some(function(t){return t.id==e.id})})},!0)}],template:'\n\t\t<a class="btn btn-block btn-{{$ctrl.level}} dropdown-toggle" data-toggle="dropdown">\n\t\t\t<span ng-repeat="item in $ctrl.selectedOptions track by item.id" class="pill">\n\t\t\t\t{{item.title}}\n\t\t\t</span>\n\t\t\t<i class="fa fa-caret-down"></i></a>\n\t\t</a>\n\t\t<ul class="dropdown-menu pull-right">\n\t\t\t<li ng-repeat="option in $ctrl.options track by option.id">\n\t\t\t\t<a ng-click="$ctrl.toggle(option)">\n\t\t\t\t\t<i class="fa fa-fw" ng-class="option.selected ? \'fa-check-square-o\' : \'fa-square-o\'"></i>\n\t\t\t\t\t{{option.title}}\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t</ul>\n\t'}),angular.module("angular-ui-query-builder").provider("qbTableSettings",function(){var t=this;return t.debug=!1,t.debugPrefix="[angular-ui-query-builder]",t.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",checkMetaChecked:"fa fa-lg fa-fw fa-check-square-o text-primary",checkMetaSome:"fa fa-lg fa-fw fa-minus-square-o",checkMetaUnchecked:"fa fa-lg fa-fw fa-square-o",checkMetaCaret:"fa fa-caret-down",checkItemChecked:"fa fa-lg fa-fw fa-check-square-o",checkItemUnchecked:"fa fa-lg fa-fw fa-square-o",paginationPrev:"fa fa-arrow-left",paginationNext:"fa fa-arrow-right",modalClose:"fa fa-times",modalCollapseClosed:"fa fa-caret-right pull-right",search:"fa fa-search",searchClear:"fa fa-times"},t.pagination={showXOfY:!0,showPages:!0,pageRangeBack:5,pageRangeFore:5},t.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)"}],questions:[]},t.$get=function(){return t},t}).service("qbTableUtilities",function(){return{getSynopsis:function(t){var e=_.keys(t).filter(function(t){return!["sort","skip","limit","select"].includes(t)});return[e.length?1==e.length?"1 filter":"".concat(e.length," filters"):"All documents",t.sort?t.sort.startsWith("-")?"sorted by ".concat(t.sort.substr(1)," (reverse order)"):"sorted by ".concat(t.sort):null,t.limit?"limited to ".concat(t.limit," rows"):null,t.offset?"starting at record ".concat(t.skip):null,t.select?"selecting only ".concat(t.select.length," columns"):null].filter(function(t){return t}).join(", ")},find:function(t,e){var i,r=_.isFunction(e)?e:_.matches(e);return!!function n(t,l){return r(t,l.slice(l.length-1))?(i=l,!0):_.isArray(t)?t.some(function(t,e){return n(t,l.concat(e))}):_.isObject(t)?_.some(t,function(t,e){return n(t,l.concat(e))}):void 0}(t,[])&&i},escapeRegExp:function(t){return String(t).replace(/(\W)/g,"\\$1")},unescapeRegExp:function(t){return String(t).replace(/\\(\W)/g,"$1")}}}).directive("qbTable",function(){return{scope:{qbTable:"=?",count:"<?",stickyThead:"<?",stickyTfoot:"<?"},restrict:"AC",controller:["$attrs","$element","$rootScope","$scope","$timeout","qbTableSettings",function(t,e,n,i,l,r){var a=this;a.query=i.qbTable,a.count=i.count,i.$watch("count",function(){return a.count=i.count}),a.$broadcast=function(t){for(var e=arguments.length,n=new Array(1<e?e-1:0),l=1;l<e;l++)n[l-1]=arguments[l];return i.$broadcast.apply(i,[t].concat(n))},a.$on=function(t,e){return i.$on(t,e)},a.setDirty=function(){r.debug&&console.log(r.debugPrefix,"Declare query dirty",i.qbTable),n.$broadcast("queryBuilder.change",i.qbTable)},a.setField=function(t,e){if(null!=e){switch(t){case"sort":a.query.sort===e?a.query.sort="-".concat(e):(a.query.sort,"-".concat(e),a.query.sort=e);break;default:i.qbTable[t]=e}return a}delete a.query[t]},e.addClass("qb-table"),i.$watch("stickyThead",function(){return e.toggleClass("qb-sticky-thead",i.stickyThead||""===t.stickyThead)}),i.$watch("stickyTfoot",function(){return e.toggleClass("qb-sticky-tfoot",i.stickyTfoot||""===t.stickyTfoot)}),i.$watch("count",function(){return e.toggleClass("qb-noresults",0===i.count)}),i.$on("queryBuilder.change.replace",function(t,e){a.query=i.qbTable=e,l(function(){return a.setDirty()})})}]}}).directive("qbCol",function(){return{scope:{qbCol:"@",sortable:"@"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,l,i){n.qbTableSettings=i;var r=n.$watchGroup(["qbTable","sortable"],function(){""===t.sortable&&!n.qbTable&&i.debug&&console.warn(i.debugPrefix,"Added qb-col + sortable onto element",e,"but no qb-table query has been assigned on the table element!"),r()});n.canSort=!1,n.isSorted=!1,this.$onInit=function(){n.canSort=n.sortable||""===t.sortable,e.toggleClass("sortable",n.canSort),n.canSort&&e.on("click",function(){return l(n.toggleSort)})},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).setDirty():n.qbCol&&""===t.sortable&&n.qbTable.setField("sort",n.qbCol).setDirty()},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:"=?",onPreSelect:"&?",onSelect:"&?"},require:"^qbTable",restrict:"A",transclude:!0,controller:["$attrs","$element","$scope","$timeout","qbTableSettings",function(t,e,n,l,i){n.qbTableSettings=i,n.isMeta=0<e.parents("thead").length,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.onPreSelect&&n.onPreSelect({value:n.selector}),n.selector=!n.selector,n.qbTable.$broadcast("qbTableCellSelect"),n.onSelect&&l(function(){return n.onSelect({value:n.selector})})})}),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: ".concat(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 ng-class="metaStatus == \'all\' ? qbTableSettings.icons.checkMetaChecked : metaStatus == \'some\' ? qbTableSettings.icons.checkMetaUnchecked : qbTableSettings.icons.checkMetaUnchecked"></i>\n\t\t\t\t<i ng-class="qbTableSettings.icons.checkMetaCaret"></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 ng-class="selector ? qbTableSettings.icons.checkItemChecked : qbTableSettings.icons.checkItemUnchecked"></i>\n\t\t</div>\n\t'}}).directive("qbPagination",function(){return{scope:{},require:"^qbTable",restrict:"EA",transclude:!0,controller:["$attrs","$scope","qbTableSettings",function(t,e,n){e.qbTableSettings=n,e.canPrev=!0,e.canNext=!0,e.showRange={},e.$watchGroup(["qbTable.query.limit","qbTable.query.skip","qbTable.count"],function(t){e.canPrev=0<e.qbTable.query.skip,e.canNext=!e.qbTable.count||e.qbTable.query.skip+e.qbTable.query.limit<e.qbTable.count,n.pagination.showXOfY&&(e.showRange={start:(e.qbTable.query.skip||0)+1,end:Math.min((e.qbTable.query.skip||0)+e.qbTable.query.limit,e.qbTable.count),total:e.qbTable.count}),n.pagination.showPages&&(e.pages={current:!!e.qbTable.query.limit&&Math.floor((e.qbTable.query.skip||0)/e.qbTable.query.limit)},e.pages.min=Math.max(e.pages.current-n.pagination.pageRangeBack,0),e.pages.total=e.qbTable.query.limit?Math.ceil(e.qbTable.count/e.qbTable.query.limit):1,e.pages.max=Math.min(e.pages.total,e.pages.current+n.pagination.pageRangeFore+1),e.pages.range=_.range(e.pages.min,e.pages.max).map(function(t){return{number:t,mode:t==e.pages.current?"current":t==e.pages.current-1?"prev":t==e.pages.current+1?"next":"normal"}}))}),e.navPageRelative=function(t){if(-1==t)e.qbTable.setField("skip",Math.max((e.qbTable.query.skip||0)-(e.qbTable.query.limit||10),0)).setDirty();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)).setDirty()}},e.navPageNumber=function(t){return e.qbTable.setField("skip",(t||0)*(e.qbTable.query.limit||10)).setDirty()}}],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 ng-class="qbTableSettings.icons.paginationPrev"></i></a></li>\n\t\t\t\t<ng-transclude class="text-center">\n\t\t\t\t\t<span ng-if="qbTableSettings.pagination.showXOfY && showRange.end" class="display-xofy">\n\t\t\t\t\t\tShowing documents {{showRange.start | number}} - {{showRange.end | number}}\n\t\t\t\t\t\t<span ng-if="showRange.total">\n\t\t\t\t\t\t\tof {{showRange.total | number}}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ul ng-if="qbTableSettings.pagination.showPages && showRange.end && pages.max > 1" class="display-pages pagination">\n\t\t\t\t\t\t<li ng-repeat="page in pages.range track by page.number" ng-class="page.mode == \'current\' ? \'active\' : \'\'">\n\t\t\t\t\t\t\t<a ng-click="navPageNumber(page.number)">\n\t\t\t\t\t\t\t\t{{page.number + 1 | number}}\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</ng-transclude>\n\t\t\t\t<li ng-class="canNext ? \'\' : \'disabled\'" class="next"><a ng-click="navPageRelative(1)"><i ng-class="qbTableSettings.icons.paginationNext"></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","$httpParamSerializerJQLike","$scope","$timeout","$window","qbTableSettings","qbTableUtilities",function(t,e,n,l,i,r,a){n.qbTableSettings=r,n.settings={},n.isShowing=!1,n.exportPrompt=function(){n.settings=angular.extend(angular.copy(r.export.defaults),{query:_(n.query).omitBy(function(t,e){return["skip","limit"].includes(e)}).value(),columns:_(n.spec).pickBy(function(t){return t&&t.type&&["string","number","data","boolean","objectid"].includes(t.type)}).map(function(t,e){return t.id=e,t.title=_.startCase(e),t.selected=!0,t}).value(),questions:_(r.export.questions).mapKeys(function(t){return t.id}).mapValues(function(t){return t.default}).value()}),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},n.settings.questions);i.open(n.url+"?"+e(t))},n.querySynopsis,n.$watchGroup(["isShowing","settings.query"],function(){n.isShowing&&(n.querySynopsis=a.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")})}],link:function(t,e){e.find("ng-transclude").on("click",function(){return t.$applyAsync(function(){return t.exportPrompt()})})},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 ng-class="qbTableSettings.icons.modalClose"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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 ng-class="qbTableSettings.icons.modalCollapseClosed"></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\t<div ng-repeat="question in qbTableSettings.export.questions track by question.id" class="form-group">\n\t\t\t\t\t\t\t<label class="col-sm-3 control-label">{{question.title}}</label>\n\t\t\t\t\t\t\t<div ng-switch="question.type" class="col-sm-9">\n\t\t\t\t\t\t\t\t<div ng-switch-when="text">\n\t\t\t\t\t\t\t\t\t<input type="text" ng-model="settings.questions[question.id]" class="form-control"/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div ng-switch-default>\n\t\t\t\t\t\t\t\t\t<div class="alert alert-danger">\n\t\t\t\t\t\t\t\t\t\tUnknown question type: "{{question.type}}"\n\t\t\t\t\t\t\t\t\t\t<pre>{{question | json}}</pre>\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\t<div ng-if="question.help" class="help-block">{{question.help}}</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'}}).directive("qbModal",function(){return{scope:{query:"=",spec:"<",title:"@?",onRefresh:"&?",binding:"@?"},transclude:!0,restrict:"A",controller:["$element","$scope","qbTableSettings",function(t,e,n){var l=this;e.qbTableSettings=n,l.isShown=!1,l.rebind=function(){t.one("click",function(){t.find(".qb-modal").one("hide.bs.modal",function(){l.isShown=!1}).one("hidden.bs.modal",function(){l.rebind()}).modal("show")})},e.submit=function(){angular.isFunction(l.onRefresh)&&l.onRefresh({query:e.queryCopy,spec:e.spec}),e.binding&&"complete"!=e.binding||(e.query=e.queryCopy),t.find(".qb-modal").modal("hide")},l.$onInit=function(){e.queryCopy="live"==e.binding?e.query:angular.copy(e.query)},l.rebind()}],template:'\n\t\t<ng-transclude></ng-transclude>\n\t\t<div class="qb-modal modal fade">\n\t\t\t<div class="modal-dialog modal-lg">\n\t\t\t\t<div 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 ng-class="qbTableSettings.icons.modalClose"></i></a>\n\t\t\t\t\t\t<h4 class="modal-title">{{title || \'Edit Filter\'}}</h4>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class="modal-body">\n\t\t\t\t\t\t<ui-query-builder\n\t\t\t\t\t\t\tquery="queryCopy"\n\t\t\t\t\t\t\tspec="spec"\n\t\t\t\t\t\t></ui-query-builder>\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="submit()" class="btn btn-success">Refresh</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'}}).directive("qbSearch",function(){return{scope:{query:"=",spec:"<",onRefresh:"&?",fields:"<?",useIndexes:"@?"},restrict:"AE",transclude:!0,controller:["$element","$scope","$rootScope","$timeout","qbTableSettings","qbTableUtilities",function(l,a,c,t,s,o){var u=this;a.qbTableSettings=s,a.search="",a.isSearching=!1,a.submit=function(){var t=!(0<arguments.length&&void 0!==arguments[0])||arguments[0];if(!a.search&&t)return a.clear(!1);var n=o.escapeRegExp(_.trim(a.search)),e={$comment:"search",$or:_(a.spec).pickBy(function(t){return"string"==t.type}).mapValues(function(t,e){return[{$regex:n,$options:"i"}]}).value()},l=o.find(a.query,{$comment:"search"}),i=angular.copy(a.query);if(l&&_.isEqual(l,["$comment"]))i=e;else if(l&&"$and"==l[0])_.set(i,l,e);else if(_.isEqual(_.keys(i),["$and"]))i.$and.push(e);else if(_.isObject(i)){var r=u.useIndexes||"auto";"auto"==r&&(r=_.keys(a.spec).some(function(t){return"_id"!=t&&a.spec[t].index})?"stringIndexed":"string"),a.fields?i.$or=_(a.fields).map(function(t){return _defineProperty({},t,{$regex:o.escapeRegExp(a.search),$options:"i"})}).value():i.$or=_(a.spec).pickBy(function(t,e){if("_id"==e)return!1;if(a.fields&&a.fields.length)return a.fields.includes(e);switch(r){case"all":return!0;case"string":return"string"==t.type;case"stringIndexed":return"string"==t.type&&t.index;default:throw new Error('Unknown field selection method: "'+r+'"')}}).map(function(t,e){return _defineProperty({},e,{$regex:o.escapeRegExp(a.search),$options:"i"})}).value()}else console.warn(s.debugPrefix,"Unable to inject search term",e,"within complex query object",i);a.isSearching=!0,c.$broadcast("queryBuilder.change.replace",i),angular.isFunction(u.onRefresh)&&u.onRefresh({query:i}),("complete"==u.binding||angular.isUndefined(u.binding))&&(a.query=i)},a.clear=function(){var t,e=!(0<arguments.length&&void 0!==arguments[0])||arguments[0],n=o.find(a.query,{$comment:"search"});if(a.isSearching=!1,a.search="",e&&angular.element(l).find("input").focus(),n&&_.isEqual(n,["$comment"]))t={};else if(n&&"$and"==n[0])(t=angular.copy(a.query)).$and.find(function(t,e){return"search"!=t.$comment});else if(n)t=angular.copy(a.query),_.unset(t,n);else{if(!a.query.$or||!a.query.$or.every(function(t){return 1==_.size(t)&&_.chain(t).first().keys().find(function(t){return"$regEx"==t})}))return s.debug?void console.warn(s.debugPrefix,"Unable to clear search query within complex query - or query doesnt contain a search anyway",a.query):void 0;delete(t=angular.copy(a.query)).$or}c.$broadcast("queryBuilder.change.replace",t),angular.isFunction(u.onRefresh)&&u.onRefresh({query:t}),("complete"==u.binding||angular.isUndefined(u.binding))&&(a.query=t)},a.check=function(){try{a.search=_.chain(a.query).get("$or").first().values().first().get("$regex").thru(function(t){return o.unescapeRegExp(t||"")}).value()}catch(t){a.search=""}},u.$onInit=function(){return a.check()}}],template:'\n\t\t<ng-transclude>\n\t\t\t<form ng-submit="submit()" class="form-inline">\n\t\t\t\t<div class="form-group">\n\t\t\t\t\t<div class="input-group">\n\t\t\t\t\t\t<input type="text" ng-model="search" class="form-control"/>\n\t\t\t\t\t\t<a ng-click="isSearching ? clear() : submit(false)" class="btn btn-default input-group-addon">\n\t\t\t\t\t\t\t<i ng-class="isSearching ? qbTableSettings.icons.searchClear : qbTableSettings.icons.search"/>\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</div>\n\t\t</ng-transclude>\n\t'}});

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

.pipe(babel({
presets: ['env'],
presets: ['@babel/env'],
plugins: ['angularjs-annotate'],

@@ -89,3 +89,3 @@ }))

.pipe(babel({
presets: ['env'],
presets: ['@babel/env'],
plugins: ['angularjs-annotate'],

@@ -111,3 +111,3 @@ }))

.pipe(babel({
presets: ['env'],
presets: ['@babel/env'],
plugins: ['angularjs-annotate'],

@@ -114,0 +114,0 @@ }))

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

@@ -20,4 +20,5 @@ "main": "src/utilities.js",

"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-plugin-angularjs-annotate": "^0.9.0",
"babel-preset-env": "^1.7.0",
"chai": "^4.1.2",

@@ -28,4 +29,4 @@ "express": "^4.16.3",

"gulp": "^3.9.1",
"gulp-babel": "^6.1.2",
"gulp-clean-css": "^3.9.4",
"gulp-babel": "^8.0.0",
"gulp-clean-css": "^3.10.0",
"gulp-concat": "^2.6.1",

@@ -36,8 +37,8 @@ "gulp-file": "^0.4.0",

"gulp-plumber": "^1.2.0",
"gulp-preprocess": "^3.0.0",
"gulp-rename": "^1.3.0",
"gulp-preprocess": "^3.0.1",
"gulp-rename": "^1.4.0",
"gulp-replace": "^1.0.0",
"gulp-uglify": "^3.0.0",
"gulp-uglify": "^3.0.1",
"gulp-util": "^3.0.8",
"gulp-watch": "^5.0.0",
"gulp-watch": "^5.0.1",
"mocha": "^5.2.0",

@@ -48,8 +49,8 @@ "rimraf": "^2.6.2",

"dependencies": {
"angular": "^1.7.2",
"angular": "^1.7.4",
"bootstrap": "^3.3.7",
"jquery": "^3.3.1",
"lodash": "^4.17.10",
"lodash": "^4.17.11",
"moment": "^2.22.2"
}
}

@@ -504,8 +504,11 @@ angular.module('angular-ui-query-builder')

.value(),
columns: _.map($scope.spec, (v, k) => {
v.id = k;
v.title = _.startCase(k);
v.selected = true;
return v;
}),
columns: _($scope.spec)
.pickBy(v => v && v.type && ['string', 'number', 'data', 'boolean', 'objectid'].includes(v.type))
.map((v, k) => {
v.id = k;
v.title = _.startCase(k);
v.selected = true;
return v;
})
.value(),
questions: _(qbTableSettings.export.questions) // Populate questions with defaults

@@ -512,0 +515,0 @@ .mapKeys(v => v.id)

@@ -15,3 +15,5 @@ // @ifndef ANGULAR

return [
filters.length ? `${filters.length} filters` : 'All records',
!filters.length ? 'All documents'
: filters.length == 1 ? '1 filter'
: `${filters.length} filters`,
query.sort

@@ -18,0 +20,0 @@ ? (

Sorry, the diff of this file is too big to display

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