UNPKG

datatables.net-searchbuilder

Version:
1,065 lines (1,058 loc) 175 kB
/*! SearchBuilder 1.8.2 * ©SpryMedia Ltd - datatables.net/license/mit */ (function( factory ){ if ( typeof define === 'function' && define.amd ) { // AMD define( ['jquery', 'datatables.net'], function ( $ ) { return factory( $, window, document ); } ); } else if ( typeof exports === 'object' ) { // CommonJS var jq = require('jquery'); var cjsRequires = function (root, $) { if ( ! $.fn.dataTable ) { require('datatables.net')(root, $); } }; if (typeof window === 'undefined') { module.exports = function (root, $) { if ( ! root ) { // CommonJS environments without a window global must pass a // root. This will give an error otherwise root = window; } if ( ! $ ) { $ = jq( root ); } cjsRequires( root, $ ); return factory( $, root, root.document ); }; } else { cjsRequires( window, jq ); module.exports = factory( jq, window, window.document ); } } else { // Browser factory( jQuery, window, document ); } }(function( $, window, document ) { 'use strict'; var DataTable = $.fn.dataTable; (function () { 'use strict'; var $$3; var dataTable$2; /** Get a moment object. Attempt to get from DataTables for module loading first. */ function moment() { var used = DataTable.use('moment'); return used ? used : window.moment; } /** Get a luxon object. Attempt to get from DataTables for module loading first. */ function luxon() { var used = DataTable.use('luxon'); return used ? used : window.luxon; } /** * Sets the value of jQuery for use in the file * * @param jq the instance of jQuery to be set */ function setJQuery$2(jq) { $$3 = jq; dataTable$2 = jq.fn.dataTable; } /** * The Criteria class is used within SearchBuilder to represent a search criteria */ var Criteria = /** @class */ (function () { function Criteria(table, opts, topGroup, index, depth, serverData, liveSearch) { if (index === void 0) { index = 0; } if (depth === void 0) { depth = 1; } if (serverData === void 0) { serverData = undefined; } if (liveSearch === void 0) { liveSearch = false; } var _this = this; this.classes = $$3.extend(true, {}, Criteria.classes); // Get options from user and any extra conditions/column types defined by plug-ins this.c = $$3.extend(true, {}, Criteria.defaults, $$3.fn.dataTable.ext.searchBuilder, opts); var i18n = this.c.i18n; this.s = { condition: undefined, conditions: {}, data: undefined, dataIdx: -1, dataPoints: [], dateFormat: false, depth: depth, dt: table, filled: false, index: index, liveSearch: liveSearch, origData: undefined, preventRedraw: false, serverData: serverData, topGroup: topGroup, type: '', value: [] }; this.dom = { buttons: $$3('<div/>') .addClass(this.classes.buttonContainer), condition: $$3('<select disabled/>') .addClass(this.classes.condition) .addClass(this.classes.dropDown) .addClass(this.classes.italic) .attr('autocomplete', 'hacking'), conditionTitle: $$3('<option value="" disabled selected hidden/>') .html(this.s.dt.i18n('searchBuilder.condition', i18n.condition)), container: $$3('<div/>') .addClass(this.classes.container), data: $$3('<select/>') .addClass(this.classes.data) .addClass(this.classes.dropDown) .addClass(this.classes.italic), dataTitle: $$3('<option value="" disabled selected hidden/>') .html(this.s.dt.i18n('searchBuilder.data', i18n.data)), defaultValue: $$3('<select disabled/>') .addClass(this.classes.value) .addClass(this.classes.dropDown) .addClass(this.classes.select) .addClass(this.classes.italic), "delete": $$3('<button/>') .html(this.s.dt.i18n('searchBuilder.delete', i18n["delete"])) .addClass(this.classes["delete"]) .addClass(this.classes.button) .attr('title', this.s.dt.i18n('searchBuilder.deleteTitle', i18n.deleteTitle)) .attr('type', 'button'), inputCont: $$3('<div/>') .addClass(this.classes.inputCont), // eslint-disable-next-line no-useless-escape left: $$3('<button/>') .html(this.s.dt.i18n('searchBuilder.left', i18n.left)) .addClass(this.classes.left) .addClass(this.classes.button) .attr('title', this.s.dt.i18n('searchBuilder.leftTitle', i18n.leftTitle)) .attr('type', 'button'), // eslint-disable-next-line no-useless-escape right: $$3('<button/>') .html(this.s.dt.i18n('searchBuilder.right', i18n.right)) .addClass(this.classes.right) .addClass(this.classes.button) .attr('title', this.s.dt.i18n('searchBuilder.rightTitle', i18n.rightTitle)) .attr('type', 'button'), value: [ $$3('<select disabled/>') .addClass(this.classes.value) .addClass(this.classes.dropDown) .addClass(this.classes.italic) .addClass(this.classes.select) ], valueTitle: $$3('<option value="--valueTitle--" disabled selected hidden/>') .html(this.s.dt.i18n('searchBuilder.value', i18n.value)) }; // If the greyscale option is selected then add the class to add the grey colour to SearchBuilder if (this.c.greyscale) { this.dom.data.addClass(this.classes.greyscale); this.dom.condition.addClass(this.classes.greyscale); this.dom.defaultValue.addClass(this.classes.greyscale); for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) { var val = _a[_i]; val.addClass(this.classes.greyscale); } } $$3(window).on('resize.dtsb', dataTable$2.util.throttle(function () { _this.s.topGroup.trigger('dtsb-redrawLogic'); })); this._buildCriteria(); return this; } /** * Escape html characters within a string * * @param txt the string to be escaped * @returns the escaped string */ Criteria._escapeHTML = function (txt) { return txt .toString() .replace(/&lt;/g, '<') .replace(/&gt;/g, '>') .replace(/&quot;/g, '"') .replace(/&amp;/g, '&'); }; /** * Redraw the DataTable with the current search parameters */ Criteria.prototype.doSearch = function () { // Only do the search if live search is disabled, otherwise the search // is triggered by the button at the top level group. if (this.c.liveSearch) { this.s.dt.draw(); } }; /** * Parses formatted numbers down to a form where they can be compared. * Note that this does not account for different decimal characters. Use * parseNumber instead on the instance. * * @param val the value to convert * @returns the converted value */ Criteria.parseNumFmt = function (val) { return +val.replace(/(?!^-)[^0-9.]/g, ''); }; /** * Adds the left button to the criteria */ Criteria.prototype.updateArrows = function (hasSiblings) { if (hasSiblings === void 0) { hasSiblings = false; } // Empty the container and append all of the elements in the correct order this.dom.container.children().detach(); this.dom.container .append(this.dom.data) .append(this.dom.condition) .append(this.dom.inputCont); this.setListeners(); // Trigger the inserted events for the value elements as they are inserted if (this.dom.value[0] !== undefined) { $$3(this.dom.value[0]).trigger('dtsb-inserted'); } for (var i = 1; i < this.dom.value.length; i++) { this.dom.inputCont.append(this.dom.value[i]); $$3(this.dom.value[i]).trigger('dtsb-inserted'); } // If this is a top level criteria then don't let it move left if (this.s.depth > 1) { this.dom.buttons.append(this.dom.left); } // If the depthLimit of the query has been hit then don't add the right button if ((this.c.depthLimit === false || this.s.depth < this.c.depthLimit) && hasSiblings) { this.dom.buttons.append(this.dom.right); } else { this.dom.right.remove(); } this.dom.buttons.append(this.dom["delete"]); this.dom.container.append(this.dom.buttons); }; /** * Destroys the criteria, removing listeners and container from the dom */ Criteria.prototype.destroy = function () { // Turn off listeners this.dom.data.off('.dtsb'); this.dom.condition.off('.dtsb'); this.dom["delete"].off('.dtsb'); for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) { var val = _a[_i]; val.off('.dtsb'); } // Remove container from the dom this.dom.container.remove(); }; /** * Passes in the data for the row and compares it against this single criteria * * @param rowData The data for the row to be compared * @returns boolean Whether the criteria has passed */ Criteria.prototype.search = function (rowData, rowIdx) { var settings = this.s.dt.settings()[0]; var condition = this.s.conditions[this.s.condition]; if (this.s.condition !== undefined && condition !== undefined) { var filter = rowData[this.s.dataIdx]; // This check is in place for if a custom decimal character is in place if (this.s.type && this.s.type.includes('num') && (settings.oLanguage.sDecimal !== '' || settings.oLanguage.sThousands !== '')) { var splitRD = [rowData[this.s.dataIdx]]; if (settings.oLanguage.sDecimal !== '') { splitRD = rowData[this.s.dataIdx].split(settings.oLanguage.sDecimal); } if (settings.oLanguage.sThousands !== '') { for (var i = 0; i < splitRD.length; i++) { splitRD[i] = splitRD[i].replace(settings.oLanguage.sThousands, ','); } } filter = splitRD.join('.'); } // If orthogonal data is in place we need to get it's values for searching if (this.c.orthogonal.search !== 'filter') { filter = settings.fastData(rowIdx, this.s.dataIdx, typeof this.c.orthogonal === 'string' ? this.c.orthogonal : this.c.orthogonal.search); } if (this.s.type === 'array') { // Make sure we are working with an array if (!Array.isArray(filter)) { filter = [filter]; } filter.sort(); for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) { var filt = filter_1[_i]; if (filt && typeof filt === 'string') { filt = filt.replace(/[\r\n\u2028]/g, ' '); } } } else if (filter !== null && typeof filter === 'string') { filter = filter.replace(/[\r\n\u2028]/g, ' '); } if (this.s.type.includes('html') && typeof filter === 'string') { filter = filter.replace(/(<([^>]+)>)/ig, ''); } // Not ideal, but jqueries .val() returns an empty string even // when the value set is null, so we shall assume the two are equal if (filter === null) { filter = ''; } return condition.search(filter, this.s.value, this); } }; /** * Gets the details required to rebuild the criteria */ Criteria.prototype.getDetails = function (deFormatDates) { if (deFormatDates === void 0) { deFormatDates = false; } var i; var settings = this.s.dt.settings()[0]; // This check is in place for if a custom decimal character is in place if (this.s.type !== null && ["num", "num-fmt", "html-num", "html-num-fmt"].includes(this.s.type) && (settings.oLanguage.sDecimal !== '' || settings.oLanguage.sThousands !== '')) { for (i = 0; i < this.s.value.length; i++) { var splitRD = [this.s.value[i].toString()]; if (settings.oLanguage.sDecimal !== '') { splitRD = this.s.value[i].split(settings.oLanguage.sDecimal); } if (settings.oLanguage.sThousands !== '') { for (var j = 0; j < splitRD.length; j++) { splitRD[j] = splitRD[j].replace(settings.oLanguage.sThousands, ','); } } this.s.value[i] = splitRD.join('.'); } } else if (this.s.type !== null && deFormatDates) { var momentLib = moment(); var luxonLib = luxon(); if ((this.s.type.includes('date') || this.s.type.includes('time')) && !moment && !luxon) { for (i = 0; i < this.s.value.length; i++) { if (this.s.value[i].match(/^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$/g) === null) { this.s.value[i] = ''; } } } else if (this.s.type.includes('moment') || (this.s.type.includes('datetime') && moment)) { for (i = 0; i < this.s.value.length; i++) { if (this.s.value[i] && this.s.value[i].length > 0 && momentLib(this.s.value[i], this.s.dateFormat, true).isValid()) { this.s.value[i] = momentLib(this.s.value[i], this.s.dateFormat).format('YYYY-MM-DD HH:mm:ss'); } } } else if (this.s.type.includes('luxon') || (this.s.type.includes('datetime') && luxon)) { for (i = 0; i < this.s.value.length; i++) { if (this.s.value[i] && this.s.value[i].length > 0 && luxonLib.DateTime.fromFormat(this.s.value[i], this.s.dateFormat).invalid === null) { this.s.value[i] = luxonLib.DateTime.fromFormat(this.s.value[i], this.s.dateFormat).toFormat('yyyy-MM-dd HH:mm:ss'); } } } } if (this.s.type && this.s.type.includes('num') && this.s.dt.page.info().serverSide) { for (i = 0; i < this.s.value.length; i++) { this.s.value[i] = this.s.value[i].replace(/[^0-9.\-]/g, ''); } } return { condition: this.s.condition, data: this.s.data, origData: this.s.origData, type: this.s.type, value: this.s.value.map(function (a) { return a !== null && a !== undefined ? a.toString() : a; }) }; }; /** * Getter for the node for the container of the criteria * * @returns JQuery<HTMLElement> the node for the container */ Criteria.prototype.getNode = function () { return this.dom.container; }; /** * Parses formatted numbers down to a form where they can be compared * * @param val the value to convert * @returns the converted value */ Criteria.prototype.parseNumber = function (val) { var decimal = this.s.dt.i18n('decimal'); // Remove any periods and then replace the decimal with a period if (decimal && decimal !== '.') { val = val.replace(/\./g, '').replace(decimal, '.'); } return +val.replace(/(?!^-)[^0-9.]/g, ''); }; /** * Populates the criteria data, condition and value(s) as far as has been selected */ Criteria.prototype.populate = function () { this._populateData(); // If the column index has been found attempt to select a condition if (this.s.dataIdx !== -1) { this._populateCondition(); // If the condittion has been found attempt to select the values if (this.s.condition !== undefined) { this._populateValue(); } } }; /** * Rebuilds the criteria based upon the details passed in * * @param loadedCriteria the details required to rebuild the criteria */ Criteria.prototype.rebuild = function (loadedCriteria) { // Check to see if the previously selected data exists, if so select it var foundData = false; var dataIdx, i; this._populateData(); // If a data selection has previously been made attempt to find and select it if (loadedCriteria.data !== undefined) { var italic_1 = this.classes.italic; var data_1 = this.dom.data; this.dom.data.children('option').each(function () { if (!foundData && ($$3(this).text() === loadedCriteria.data || loadedCriteria.origData && $$3(this).prop('origData') === loadedCriteria.origData)) { $$3(this).prop('selected', true); data_1.removeClass(italic_1); foundData = true; dataIdx = parseInt($$3(this).val(), 10); } else { $$3(this).removeProp('selected'); } }); } // If the data has been found and selected then the condition can be populated and searched if (foundData) { this.s.data = loadedCriteria.data; this.s.origData = loadedCriteria.origData; this.s.dataIdx = dataIdx; this.c.orthogonal = this._getOptions().orthogonal; this.dom.dataTitle.remove(); this._populateCondition(); this.dom.conditionTitle.remove(); var condition = void 0; // Check to see if the previously selected condition exists, if so select it var options = this.dom.condition.children('option'); for (i = 0; i < options.length; i++) { var option = $$3(options[i]); if (loadedCriteria.condition !== undefined && option.val() === loadedCriteria.condition && typeof loadedCriteria.condition === 'string') { option.prop('selected', true); condition = option.val(); } else { option.removeProp('selected'); } } this.s.condition = condition; // If the condition has been found and selected then the value can be populated and searched if (this.s.condition !== undefined) { this.dom.conditionTitle.removeProp('selected'); this.dom.conditionTitle.remove(); this.dom.condition.removeClass(this.classes.italic); for (i = 0; i < options.length; i++) { var opt = $$3(options[i]); if (opt.val() !== this.s.condition) { opt.removeProp('selected'); } } this._populateValue(loadedCriteria); } else { this.dom.conditionTitle.prependTo(this.dom.condition).prop('selected', true); } } }; /** * Sets the listeners for the criteria */ Criteria.prototype.setListeners = function () { var _this = this; this.dom.data .unbind('change') .on('change.dtsb', function () { _this.dom.dataTitle.removeProp('selected'); // Need to go over every option to identify the correct selection var options = _this.dom.data.children('option.' + _this.classes.option); for (var i = 0; i < options.length; i++) { var option = $$3(options[i]); if (option.val() === _this.dom.data.val()) { _this.dom.data.removeClass(_this.classes.italic); option.prop('selected', true); _this.s.dataIdx = +option.val(); _this.s.data = option.text(); _this.s.origData = option.prop('origData'); _this.c.orthogonal = _this._getOptions().orthogonal; // When the data is changed, the values in condition and // value may also change so need to renew them _this._clearCondition(); _this._clearValue(); _this._populateCondition(); // If this criteria was previously active in the search then // remove it from the search and trigger a new search if (_this.s.filled) { _this.s.filled = false; _this.doSearch(); _this.setListeners(); } _this.s.dt.state.save(); } else { option.removeProp('selected'); } } }); this.dom.condition .unbind('change') .on('change.dtsb', function () { _this.dom.conditionTitle.removeProp('selected'); // Need to go over every option to identify the correct selection var options = _this.dom.condition.children('option.' + _this.classes.option); for (var i = 0; i < options.length; i++) { var option = $$3(options[i]); if (option.val() === _this.dom.condition.val()) { _this.dom.condition.removeClass(_this.classes.italic); option.prop('selected', true); var condDisp = option.val(); // Find the condition that has been selected and store it internally for (var _i = 0, _a = Object.keys(_this.s.conditions); _i < _a.length; _i++) { var cond = _a[_i]; if (cond === condDisp) { _this.s.condition = condDisp; break; } } // When the condition is changed, the value selector may switch between // a select element and an input element _this._clearValue(); _this._populateValue(); for (var _b = 0, _c = _this.dom.value; _b < _c.length; _b++) { var val = _c[_b]; // If this criteria was previously active in the search then remove // it from the search and trigger a new search if (_this.s.filled && val !== undefined && _this.dom.inputCont.has(val[0]).length !== 0) { _this.s.filled = false; _this.doSearch(); _this.setListeners(); } } if (_this.dom.value.length === 0 || _this.dom.value.length === 1 && _this.dom.value[0] === undefined) { _this.doSearch(); } } else { option.removeProp('selected'); } } }); }; Criteria.prototype.setupButtons = function () { if (window.innerWidth > 550) { this.dom.container.removeClass(this.classes.vertical); this.dom.buttons.css('left', null); this.dom.buttons.css('top', null); return; } this.dom.container.addClass(this.classes.vertical); this.dom.buttons.css('left', this.dom.data.innerWidth()); this.dom.buttons.css('top', this.dom.data.position().top); }; /** * Builds the elements of the dom together */ Criteria.prototype._buildCriteria = function () { // Append Titles for select elements this.dom.data.append(this.dom.dataTitle); this.dom.condition.append(this.dom.conditionTitle); // Add elements to container this.dom.container .append(this.dom.data) .append(this.dom.condition); this.dom.inputCont.empty(); for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) { var val = _a[_i]; val.append(this.dom.valueTitle); this.dom.inputCont.append(val); } // Add buttons to container this.dom.buttons .append(this.dom["delete"]) .append(this.dom.right); this.dom.container.append(this.dom.inputCont).append(this.dom.buttons); this.setListeners(); }; /** * Clears the condition select element */ Criteria.prototype._clearCondition = function () { this.dom.condition.empty(); this.dom.conditionTitle.prop('selected', true).attr('disabled', 'true'); this.dom.condition.prepend(this.dom.conditionTitle).prop('selectedIndex', 0); this.s.conditions = {}; this.s.condition = undefined; }; /** * Clears the value elements */ Criteria.prototype._clearValue = function () { var val; if (this.s.condition !== undefined) { if (this.dom.value.length > 0 && this.dom.value[0] !== undefined) { // Remove all of the value elements for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) { val = _a[_i]; if (val !== undefined) { // Timeout is annoying but because of IOS setTimeout(function () { val.remove(); }, 50); } } } // Call the init function to get the value elements for this condition this.dom.value = [].concat(this.s.conditions[this.s.condition].init(this, Criteria.updateListener)); if (this.dom.value.length > 0 && this.dom.value[0] !== undefined) { this.dom.inputCont .empty() .append(this.dom.value[0]) .insertAfter(this.dom.condition); $$3(this.dom.value[0]).trigger('dtsb-inserted'); // Insert all of the value elements for (var i = 1; i < this.dom.value.length; i++) { this.dom.inputCont.append(this.dom.value[i]); $$3(this.dom.value[i]).trigger('dtsb-inserted'); } } } else { // Remove all of the value elements for (var _b = 0, _c = this.dom.value; _b < _c.length; _b++) { val = _c[_b]; if (val !== undefined) { // Timeout is annoying but because of IOS setTimeout(function () { val.remove(); }, 50); } } // Append the default valueTitle to the default select element this.dom.valueTitle .prop('selected', true); this.dom.defaultValue .append(this.dom.valueTitle) .insertAfter(this.dom.condition); } this.s.value = []; this.dom.value = [ $$3('<select disabled/>') .addClass(this.classes.value) .addClass(this.classes.dropDown) .addClass(this.classes.italic) .addClass(this.classes.select) .append(this.dom.valueTitle.clone()) ]; }; /** * Gets the options for the column * * @returns {object} The options for the column */ Criteria.prototype._getOptions = function () { var table = this.s.dt; return $$3.extend(true, {}, Criteria.defaults, table.settings()[0].aoColumns[this.s.dataIdx].searchBuilder); }; /** * Populates the condition dropdown */ Criteria.prototype._populateCondition = function () { var conditionOpts = []; var conditionsLength = Object.keys(this.s.conditions).length; var dt = this.s.dt; var colInits = dt.settings()[0].aoColumns; var column = +this.dom.data.children('option:selected').val(); var condition, condName; // If there are no conditions stored then we need to get them from the appropriate type if (conditionsLength === 0) { this.s.type = dt.column(column).type(); if (colInits !== undefined) { var colInit = colInits[column]; if (colInit.searchBuilderType !== undefined && colInit.searchBuilderType !== null) { this.s.type = colInit.searchBuilderType; } else if (this.s.type === undefined || this.s.type === null) { this.s.type = colInit.sType; } } // If the column type is still unknown use the internal API to detect type if (this.s.type === null || this.s.type === undefined) { // This can only happen in DT1 - DT2 will do the invalidation of the type itself if ($$3.fn.dataTable.ext.oApi) { $$3.fn.dataTable.ext.oApi._fnColumnTypes(dt.settings()[0]); } this.s.type = dt.column(column).type(); } // Enable the condition element this.dom.condition .removeAttr('disabled') .empty() .append(this.dom.conditionTitle) .addClass(this.classes.italic); this.dom.conditionTitle .prop('selected', true); var decimal = dt.settings()[0].oLanguage.sDecimal; // This check is in place for if a custom decimal character is in place if (decimal !== '' && this.s.type && this.s.type.indexOf(decimal) === this.s.type.length - decimal.length) { if (this.s.type.includes('num-fmt')) { this.s.type = this.s.type.replace(decimal, ''); } else if (this.s.type.includes('num')) { this.s.type = this.s.type.replace(decimal, ''); } } // Select which conditions are going to be used based on the column type var conditionObj = void 0; if (this.c.conditions[this.s.type] !== undefined) { conditionObj = this.c.conditions[this.s.type]; } else if (this.s.type && this.s.type === 'datetime') { // If no format was specified in the DT type, then we need to use // Moment / Luxon's default locale formatting. var moment_1 = DataTable.use('moment'); var luxon_1 = DataTable.use('luxon'); if (moment_1) { conditionObj = this.c.conditions.moment; this.s.dateFormat = moment_1().creationData().locale._longDateFormat.L; } if (luxon_1) { conditionObj = this.c.conditions.luxon; this.s.dateFormat = luxon_1.DateTime.DATE_SHORT; } } else if (this.s.type && this.s.type.includes('datetime-')) { // Date / time data types in DataTables are driven by Luxon or // Moment.js. conditionObj = DataTable.use('moment') ? this.c.conditions.moment : this.c.conditions.luxon; this.s.dateFormat = this.s.type.replace(/datetime-/g, ''); } else if (this.s.type && this.s.type.includes('moment')) { conditionObj = this.c.conditions.moment; this.s.dateFormat = this.s.type.replace(/moment-/g, ''); } else if (this.s.type && this.s.type.includes('luxon')) { conditionObj = this.c.conditions.luxon; this.s.dateFormat = this.s.type.replace(/luxon-/g, ''); } else { conditionObj = this.c.conditions.string; } // Add all of the conditions to the select element for (var _i = 0, _a = Object.keys(conditionObj); _i < _a.length; _i++) { condition = _a[_i]; if (conditionObj[condition] !== null) { // Serverside processing does not supply the options for the select elements // Instead input elements need to be used for these instead if (dt.page.info().serverSide && conditionObj[condition].init === Criteria.initSelect) { var col = colInits[column]; if (this.s.serverData && this.s.serverData[col.data]) { conditionObj[condition].init = Criteria.initSelectSSP; conditionObj[condition].inputValue = Criteria.inputValueSelect; conditionObj[condition].isInputValid = Criteria.isInputValidSelect; } else { conditionObj[condition].init = Criteria.initInput; conditionObj[condition].inputValue = Criteria.inputValueInput; conditionObj[condition].isInputValid = Criteria.isInputValidInput; } } this.s.conditions[condition] = conditionObj[condition]; condName = conditionObj[condition].conditionName; if (typeof condName === 'function') { condName = condName(dt, this.c.i18n); } conditionOpts.push($$3('<option>', { text: condName, value: condition }) .addClass(this.classes.option) .addClass(this.classes.notItalic)); } } } // Otherwise we can just load them in else if (conditionsLength > 0) { this.dom.condition.empty().removeAttr('disabled').addClass(this.classes.italic); for (var _b = 0, _c = Object.keys(this.s.conditions); _b < _c.length; _b++) { condition = _c[_b]; var name_1 = this.s.conditions[condition].conditionName; if (typeof name_1 === 'function') { name_1 = name_1(dt, this.c.i18n); } var newOpt = $$3('<option>', { text: name_1, value: condition }) .addClass(this.classes.option) .addClass(this.classes.notItalic); if (this.s.condition !== undefined && this.s.condition === name_1) { newOpt.prop('selected', true); this.dom.condition.removeClass(this.classes.italic); } conditionOpts.push(newOpt); } } else { this.dom.condition .attr('disabled', 'true') .addClass(this.classes.italic); return; } for (var _d = 0, conditionOpts_1 = conditionOpts; _d < conditionOpts_1.length; _d++) { var opt = conditionOpts_1[_d]; this.dom.condition.append(opt); } // Selecting a default condition if one is set if (colInits[column].searchBuilder && colInits[column].searchBuilder.defaultCondition) { var defaultCondition = colInits[column].searchBuilder.defaultCondition; // If it is a number just use it as an index if (typeof defaultCondition === 'number') { this.dom.condition.prop('selectedIndex', defaultCondition); this.dom.condition.trigger('change'); } // If it is a string then things get slightly more tricly else if (typeof defaultCondition === 'string') { // We need to check each condition option to see if any will match for (var i = 0; i < conditionOpts.length; i++) { // Need to check against the stored conditions so we can match the token "cond" to the option for (var _e = 0, _f = Object.keys(this.s.conditions); _e < _f.length; _e++) { var cond = _f[_e]; condName = this.s.conditions[cond].conditionName; if ( // If the conditionName matches the text of the option (typeof condName === 'string' ? condName : condName(dt, this.c.i18n)) === conditionOpts[i].text() && // and the tokens match cond === defaultCondition) { // Select that option this.dom.condition .prop('selectedIndex', this.dom.condition.children().toArray().indexOf(conditionOpts[i][0])) .removeClass(this.classes.italic); this.dom.condition.trigger('change'); i = conditionOpts.length; break; } } } } } // If not default set then default to 0, the title else { this.dom.condition.prop('selectedIndex', 0); } }; /** * Populates the data / column select element */ Criteria.prototype._populateData = function () { var columns = this.s.dt.settings()[0].aoColumns; var includeColumns = this.s.dt.columns(this.c.columns).indexes().toArray(); this.dom.data.empty().append(this.dom.dataTitle); for (var index = 0; index < columns.length; index++) { // Need to check that the column can be filtered on before adding it if (this.c.columns === true || includeColumns.includes(index)) { var col = columns[index]; var opt = { index: index, origData: col.data, text: (col.searchBuilderTitle || col.sTitle) .replace(/(<([^>]+)>)/ig, '') }; this.dom.data.append($$3('<option>', { text: opt.text, value: opt.index }) .addClass(this.classes.option) .addClass(this.classes.notItalic) .prop('origData', col.data) .prop('selected', this.s.dataIdx === opt.index ? true : false)); if (this.s.dataIdx === opt.index) { this.dom.dataTitle.removeProp('selected'); } } } }; /** * Populates the Value select element * * @param loadedCriteria optional, used to reload criteria from predefined filters */ Criteria.prototype._populateValue = function (loadedCriteria) { var _this = this; var prevFilled = this.s.filled; var i; this.s.filled = false; // Remove any previous value elements // Timeout is annoying but because of IOS setTimeout(function () { _this.dom.defaultValue.remove(); }, 50); var _loop_1 = function (val) { // Timeout is annoying but because of IOS setTimeout(function () { if (val !== undefined) { val.remove(); } }, 50); }; for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) { var val = _a[_i]; _loop_1(val); } var children = this.dom.inputCont.children(); if (children.length > 1) { for (i = 0; i < children.length; i++) { $$3(children[i]).remove(); } } // Find the column with the title matching the data for the criteria and take note of the index if (loadedCriteria !== undefined) { this.s.dt.columns().every(function (index) { if (_this.s.dt.settings()[0].aoColumns[index].sTitle === loadedCriteria.data) { _this.s.dataIdx = index; } }); } // Initialise the value elements based on the condition this.dom.value = [].concat(this.s.conditions[this.s.condition].init(this, Criteria.updateListener, loadedCriteria !== undefined ? loadedCriteria.value : undefined)); if (loadedCriteria !== undefined && loadedCriteria.value !== undefined) { this.s.value = loadedCriteria.value; } this.dom.inputCont.empty(); // Insert value elements and trigger the inserted event if (this.dom.value[0] !== undefined) { $$3(this.dom.value[0]) .appendTo(this.dom.inputCont) .trigger('dtsb-inserted'); } for (i = 1; i < this.dom.value.length; i++) { $$3(this.dom.value[i]) .insertAfter(this.dom.value[i - 1]) .trigger('dtsb-inserted'); } // Check if the criteria can be used in a search this.s.filled = this.s.conditions[this.s.condition].isInputValid(this.dom.value, this); this.setListeners(); // If it can and this is different to before then trigger a draw if (!this.s.preventRedraw && prevFilled !== this.s.filled) { // If using SSP we want to restrict the amount of server calls that take place // and this will already have taken place if (!this.s.dt.page.info().serverSide) { this.doSearch(); } this.setListeners(); } }; /** * Provides throttling capabilities to SearchBuilder without having to use dt's _fnThrottle function * This is because that function is not quite suitable for our needs as it runs initially rather than waiting * * @param args arguments supplied to the throttle function * @returns Function that is to be run that implements the throttling */ Criteria.prototype._throttle = function (fn, frequency) { if (frequency === void 0) { frequency = 200; } var last = null; var timer = null; var that = this; if (frequency === null) { frequency = 200; } return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var now = +new Date(); if (last !== null && now < last + frequency) { clearTimeout(timer); } else { last = now; } timer = setTimeout(function () { last = null; fn.apply(that, args); }, frequency); }; }; Criteria.version = '1.1.0'; Criteria.classes = { button: 'dtsb-button', buttonContainer: 'dtsb-buttonContainer', condition: 'dtsb-condition', container: 'dtsb-criteria', data: 'dtsb-data', "delete": 'dtsb-delete', dropDown: 'dtsb-dropDown', greyscale: 'dtsb-greyscale', input: 'dtsb-input', inputCont: 'dtsb-inputCont', italic: 'dtsb-italic', joiner: 'dtsb-joiner', left: 'dtsb-left', notItalic: 'dtsb-notItalic', option: 'dtsb-option', right: 'dtsb-right', select: 'dtsb-select', value: 'dtsb-value', vertical: 'dtsb-vertical' }; /** * Default initialisation function for select conditions */ Criteria.initSelect = function (that, fn, preDefined, array) { if (preDefined === void 0) { preDefined = null; } if (array === void 0) { array = false; } var column = that.dom.data.children('option:selected').val(); var indexArray = that.s.dt.rows().indexes().toArray(); var fastData = that.s.dt.settings()[0].fastData; that.dom.valueTitle.prop('selected', true); // Declare select element to be used with all of the default classes and listeners. var el = $$3('<select/>') .addClass(Criteria.classes.value) .addClass(Criteria.classes.dropDown) .addClass(Criteria.classes.italic) .addClass(Criteria.classes.select) .append(that.dom.valueTitle) .on('change.dtsb', function () { $$3(this).removeClass