@momsfriendlydevco/angular-ui-query-builder
Advanced tools
Comparing version 1.3.3 to 1.3.4
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
345653
5913
24
Updatedangular@^1.7.4
Updatedlodash@^4.17.11