/* jQuery Tree Multiselect v2.6.3 | (c) Patrick Tsai | MIT Licensed */ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } var Ast = require('./ast'); var Search = require('./search'); var UiBuilder = require('./ui-builder'); var Util = require('./utility'); var SEARCH_HIT_ATTR = 'searchhit'; function Tree(id, $originalSelect, params) { this.id = id; this.$originalSelect = $originalSelect; this.params = params; this.resetState(); } Tree.prototype.initialize = function () { this.generateSelections(this.$selectionContainer[0]); this.popupDescriptionHover(); if (this.params.allowBatchSelection) { this.handleSectionCheckboxMarkings(); } if (this.params.collapsible) { this.addCollapsibility(); } if (this.params.searchable || this.params.enableSelectAll) { var auxiliaryBox = Util.dom.createNode('div', { "class": 'auxiliary' }); this.$selectionContainer.prepend(auxiliaryBox, this.$selectionContainer.firstChild); if (this.params.searchable) { this.createSearchBar(auxiliaryBox); } if (this.params.enableSelectAll) { this.createSelectAllButtons(auxiliaryBox); } } this.armRemoveSelectedOnClick(); this.updateSelectedAndOnChange(); this.render(true); this.uiBuilder.attach(); }; Tree.prototype.remove = function () { this.uiBuilder.remove(); this.resetState(); }; Tree.prototype.reload = function () { var _this = this; var selectedOptions = {}; this.selectedKeys.forEach(function (key) { var value = _this.astItems[key].value; selectedOptions[value] = true; }); this.remove(); this.$originalSelect.children('option').each(function (idx, element) { var value = element.value; if (selectedOptions[value]) { element.setAttribute('selected', 'selected'); } else { element.removeAttribute('selected'); } }); this.initialize(); this.render(true); }; Tree.prototype.resetState = function () { this.uiBuilder = new UiBuilder(this.$originalSelect, this.params.hideSidePanel); this.$treeContainer = this.uiBuilder.$treeContainer; this.$selectionContainer = this.uiBuilder.$selectionContainer; this.$selectedContainer = this.uiBuilder.$selectedContainer; // data-key is key, provides DOM node this.astItems = {}; this.astSections = {}; this.selectedNodes = {}; this.selectedKeys = []; this.keysToAdd = []; this.keysToRemove = []; }; Tree.prototype.generateSelections = function (parentNode) { var options = this.$originalSelect.children('option'); var ast = this.createAst(options); this.generateHtml(ast, parentNode); }; Tree.prototype.createAst = function (options) { var _this$keysToAdd; var data = []; var lookup = Ast.createLookup(data); var self = this; var itemId = 0; var sectionId = 0; var initialIndexItems = []; var keysToAddAtEnd = []; options.each(function () { var option = this; option.setAttribute('data-key', itemId); var item = Ast.createItem({ treeId: self.id, id: itemId, value: option.value, text: option.text, description: option.getAttribute('data-description'), initialIndex: option.getAttribute('data-index'), section: option.getAttribute('data-section'), disabled: option.hasAttribute('readonly'), selected: option.hasAttribute('selected') }); if (item.initialIndex && item.selected) { initialIndexItems[item.initialIndex] = initialIndexItems[item.initialIndex] || []; initialIndexItems[item.initialIndex].push(itemId); } else if (item.selected) { keysToAddAtEnd.push(itemId); } self.astItems[itemId] = item; ++itemId; var lookupPosition = lookup; var section = item.section; var sectionParts = section && section.length > 0 ? section.split(self.params.sectionDelimiter) : []; for (var ii = 0; ii < sectionParts.length; ++ii) { var sectionPart = sectionParts[ii]; if (lookupPosition.children[sectionPart]) { lookupPosition = lookupPosition.children[sectionPart]; } else { var newSection = Ast.createSection({ treeId: self.id, id: sectionId, name: sectionPart }); ++sectionId; lookupPosition.arr.push(newSection); var newLookupNode = Ast.createLookup(newSection.items); lookupPosition.children[sectionPart] = newLookupNode; lookupPosition = newLookupNode; } } lookupPosition.arr.push(item); }); this.keysToAdd = Util.array.flatten(initialIndexItems); Util.array.removeFalseyExceptZero(this.keysToAdd); (_this$keysToAdd = this.keysToAdd).push.apply(_this$keysToAdd, keysToAddAtEnd); Util.array.uniq(this.keysToAdd); return data; }; Tree.prototype.generateHtml = function (astArr, parentNode) { for (var ii = 0; ii < astArr.length; ++ii) { var astObj = astArr[ii]; if (astObj.isSection()) { this.astSections[astObj.id] = astObj; var createCheckboxes = this.params.allowBatchSelection; var disableCheckboxes = this.params.freeze; var node = astObj.render(createCheckboxes, disableCheckboxes); parentNode.appendChild(node); this.generateHtml(astObj.items, node); } else if (astObj.isItem()) { this.astItems[astObj.id] = astObj; var _createCheckboxes = !this.params.onlyBatchSelection; var _disableCheckboxes = this.params.freeze; var _node = astObj.render(_createCheckboxes, _disableCheckboxes); parentNode.appendChild(_node); } } }; Tree.prototype.popupDescriptionHover = function () { this.$selectionContainer.on('mouseenter', 'div.item > span.description', function () { var $item = jQuery(this).parent(); var description = $item.attr('data-description'); var descriptionDiv = document.createElement('div'); descriptionDiv.className = 'temp-description-popup'; descriptionDiv.innerHTML = description; descriptionDiv.style.position = 'absolute'; $item.append(descriptionDiv); }); this.$selectionContainer.on('mouseleave', 'div.item > span.description', function () { var $item = jQuery(this).parent(); $item.find('div.temp-description-popup').remove(); }); }; Tree.prototype.handleSectionCheckboxMarkings = function () { var self = this; this.$selectionContainer.on('click', 'input.section[type=checkbox]', function () { var $section = jQuery(this).closest('div.section'); var $items = $section.find('div.item'); var keys = $items.map(function (idx, el) { var key = Util.getKey(el); var astItem = self.astItems[key]; if (!astItem.disabled && !astItem.isNotSearchHit()) { return key; } return null; }).get(); if (this.checked) { var _self$keysToAdd; // TODO why does this always take this branch (_self$keysToAdd = self.keysToAdd).push.apply(_self$keysToAdd, _toConsumableArray(keys)); Util.array.uniq(self.keysToAdd); } else { var _self$keysToRemove; (_self$keysToRemove = self.keysToRemove).push.apply(_self$keysToRemove, _toConsumableArray(keys)); Util.array.uniq(self.keysToRemove); } self.render(); }); }; Tree.prototype.redrawSectionCheckboxes = function ($section) { $section = $section || this.$selectionContainer; // returns array; bit 1 is all children are true, bit 0 is all children are false var returnVal = 3; var self = this; var $childSections = $section.find('> div.section'); $childSections.each(function () { var result = self.redrawSectionCheckboxes(jQuery(this)); returnVal &= result; }); if (returnVal) { var $childCheckboxes = $section.find('> div.item > input[type=checkbox]'); for (var ii = 0; ii < $childCheckboxes.length; ++ii) { if ($childCheckboxes[ii].checked) { returnVal &= ~2; } else { returnVal &= ~1; } if (returnVal === 0) { break; } } } var sectionCheckbox = $section.find('> div.title > input[type=checkbox]'); if (sectionCheckbox.length) { sectionCheckbox = sectionCheckbox[0]; if (returnVal & 1) { sectionCheckbox.checked = true; sectionCheckbox.indeterminate = false; } else if (returnVal & 2) { sectionCheckbox.checked = false; sectionCheckbox.indeterminate = false; } else { sectionCheckbox.checked = false; sectionCheckbox.indeterminate = true; } } return returnVal; }; Tree.prototype.addCollapsibility = function () { var titleSelector = 'div.title'; var $titleDivs = this.$selectionContainer.find(titleSelector); var collapseSpan = Util.dom.createNode('span', { "class": 'collapse-section' }); $titleDivs.prepend(collapseSpan); var sectionSelector = 'div.section'; var $sectionDivs = this.$selectionContainer.find(sectionSelector); if (this.params.startCollapsed) { $sectionDivs.addClass('collapsed'); } this.$selectionContainer.on('click', titleSelector, function (event) { if (event.target.nodeName === 'INPUT') { return; } var $section = jQuery(this).parent(); $section.toggleClass('collapsed'); event.stopPropagation(); }); }; Tree.prototype.createSearchBar = function (parentNode) { var searchObj = new Search(SEARCH_HIT_ATTR, this.astItems, this.astSections, this.params.searchParams); var searchNode = Util.dom.createNode('input', { "class": 'search', placeholder: 'Search...' }); parentNode.appendChild(searchNode); this.$selectionContainer.on('input', 'input.search', function () { var searchText = this.value; searchObj.search(searchText); }); }; Tree.prototype.createSelectAllButtons = function (parentNode) { var selectAllNode = Util.dom.createNode('span', { "class": 'select-all', text: this.params.selectAllText }); var unselectAllNode = Util.dom.createNode('span', { "class": 'unselect-all', text: this.params.unselectAllText }); var selectAllContainer = Util.dom.createNode('div', { "class": 'select-all-container' }); selectAllContainer.appendChild(selectAllNode); selectAllContainer.appendChild(unselectAllNode); parentNode.appendChild(selectAllContainer); var self = this; this.$selectionContainer.on('click', 'span.select-all', function () { var _self$keysToAdd2; (_self$keysToAdd2 = self.keysToAdd).push.apply(_self$keysToAdd2, _toConsumableArray(self.unfilteredNodeIds())); self.render(); }); this.$selectionContainer.on('click', 'span.unselect-all', function () { var _self$keysToRemove2; (_self$keysToRemove2 = self.keysToRemove).push.apply(_self$keysToRemove2, _toConsumableArray(self.unfilteredNodeIds())); self.render(); }); }; Tree.prototype.unfilteredNodeIds = function () { var self = this; return Object.keys(self.astItems).filter(function (key) { return !self.astItems[key].node.hasAttribute(SEARCH_HIT_ATTR) || self.astItems[key].node.getAttribute(SEARCH_HIT_ATTR) === 'true'; }); }; Tree.prototype.armRemoveSelectedOnClick = function () { var self = this; this.$selectedContainer.on('click', 'span.remove-selected', function () { var parentNode = this.parentNode; var key = Util.getKey(parentNode); self.keysToRemove.push(key); self.render(); }); }; Tree.prototype.updateSelectedAndOnChange = function () { var self = this; this.$selectionContainer.on('click', 'input.option[type=checkbox]', function () { var checkbox = this; var selection = checkbox.parentNode; var key = Util.getKey(selection); Util.assert(key || key === 0); if (checkbox.checked) { self.keysToAdd.push(key); } else { self.keysToRemove.push(key); } self.render(); }); if (this.params.sortable && !this.params.freeze) { var startIndex = null; var endIndex = null; this.$selectedContainer.sortable({ start: function start(event, ui) { startIndex = ui.item.index(); }, stop: function stop(event, ui) { endIndex = ui.item.index(); if (startIndex === endIndex) { return; } Util.array.moveEl(self.selectedKeys, startIndex, endIndex); self.render(); } }); } }; Tree.prototype.render = function (noCallbacks) { var _this$selectedKeys, _this2 = this; // fix arrays first Util.array.uniq(this.keysToAdd); Util.array.uniq(this.keysToRemove); Util.array.subtract(this.keysToAdd, this.selectedKeys); Util.array.intersect(this.keysToRemove, this.selectedKeys); // check for max number of selections if (Util.isInteger(this.params.maxSelections) && this.params.maxSelections > 0) { var currentLength = this.keysToAdd.length - this.keysToRemove.length + this.selectedKeys.length; if (currentLength > this.params.maxSelections) { var _this$keysToRemove; var lengthToCut = currentLength - this.params.maxSelections; var keysToCut = []; if (lengthToCut > this.selectedKeys.length) { keysToCut.push.apply(keysToCut, _toConsumableArray(this.selectedKeys)); lengthToCut -= this.selectedKeys.length; keysToCut.push.apply(keysToCut, _toConsumableArray(this.keysToAdd.splice(0, lengthToCut))); } else { keysToCut.push.apply(keysToCut, _toConsumableArray(this.selectedKeys.slice(0, lengthToCut))); } (_this$keysToRemove = this.keysToRemove).push.apply(_this$keysToRemove, keysToCut); } } // remove items first for (var ii = 0; ii < this.keysToRemove.length; ++ii) { // remove the selected divs var node = this.selectedNodes[this.keysToRemove[ii]]; if (node) { // slightly more verbose than node.remove(), but more browser support node.parentNode.removeChild(node); this.selectedNodes[this.keysToRemove[ii]] = null; } // uncheck these checkboxes var selectionNode = this.astItems[this.keysToRemove[ii]].node; selectionNode.getElementsByTagName('INPUT')[0].checked = false; } Util.array.subtract(this.selectedKeys, this.keysToRemove); // now add items for (var jj = 0; jj < this.keysToAdd.length; ++jj) { // create selected divs var key = this.keysToAdd[jj]; var astItem = this.astItems[key]; this.selectedKeys.push(key); var selectedNode = Util.dom.createSelected(astItem, this.params.freeze, this.params.showSectionOnSelected); this.selectedNodes[astItem.id] = selectedNode; this.$selectedContainer.append(selectedNode); // check the checkboxes var inputNode = astItem.node.getElementsByTagName('INPUT')[0]; if (inputNode) { inputNode.checked = true; } } (_this$selectedKeys = this.selectedKeys).push.apply(_this$selectedKeys, _toConsumableArray(this.keysToAdd)); Util.array.uniq(this.selectedKeys); // redraw section checkboxes this.redrawSectionCheckboxes(); // now fix original select var originalValsHash = {}; // valHash hashes a value to an index var valHash = {}; for (var kk = 0; kk < this.selectedKeys.length; ++kk) { var value = this.astItems[this.selectedKeys[kk]].value; originalValsHash[this.selectedKeys[kk]] = true; valHash[value] = kk; } // TODO is there a better way to sort the values other than by HTML? // NOTE: the following does not work since jQuery duplicates option values with the same value // this.$originalSelect.val(vals); var options = this.$originalSelect.find('option').toArray(); options.sort(function (a, b) { var aValue = valHash[a.value] || 0; var bValue = valHash[b.value] || 0; return aValue - bValue; }); this.$originalSelect.html(options); this.$originalSelect.find('option').each(function (idx, el) { this.selected = !!originalValsHash[Util.getKey(el)]; }); this.$originalSelect.change(); if (!noCallbacks && this.params.onChange) { var optionsSelected = this.selectedKeys.map(function (key) { return _this2.astItems[key]; }); var optionsAdded = this.keysToAdd.map(function (key) { return _this2.astItems[key]; }); var optionsRemoved = this.keysToRemove.map(function (key) { return _this2.astItems[key]; }); this.params.onChange(optionsSelected, optionsAdded, optionsRemoved); } this.keysToRemove = []; this.keysToAdd = []; }; module.exports = Tree; },{"./ast":3,"./search":7,"./ui-builder":9,"./utility":12}],9:[function(require,module,exports){ "use strict"; function UiBuilder($el, hideSidePanel) { var $tree = jQuery('
'); var $selections = jQuery('
'); if (hideSidePanel) { $selections.addClass('no-border'); } $tree.append($selections); var $selected = jQuery('
'); if (!hideSidePanel) { $tree.append($selected); } this.$el = $el; this.$treeContainer = $tree; this.$selectionContainer = $selections; this.$selectedContainer = $selected; } UiBuilder.prototype.attach = function () { this.$el.after(this.$treeContainer); }; UiBuilder.prototype.remove = function () { this.$treeContainer.remove(); }; module.exports = UiBuilder; },{}],10:[function(require,module,exports){ "use strict"; // keeps if pred is true function filterInPlace(arr, pred) { var idx = 0; for (var ii = 0; ii < arr.length; ++ii) { if (pred(arr[ii])) { arr[idx] = arr[ii]; ++idx; } } arr.length = idx; } exports.flatten = function (arr, r) { if (!Array.isArray(arr)) { return arr; } r = r || []; for (var ii = 0; ii < arr.length; ++ii) { if (Array.isArray(arr[ii])) { r.concat(exports.flatten(arr[ii], r)); } else { r.push(arr[ii]); } } return r; }; exports.uniq = function (arr) { var hash = {}; var pred = function pred(val) { var returnVal = !hash[val]; hash[val] = true; return returnVal; }; filterInPlace(arr, pred); }; exports.removeFalseyExceptZero = function (arr) { var pred = function pred(val) { return val || val === 0; }; filterInPlace(arr, pred); }; exports.moveEl = function (arr, oldPos, newPos) { var el = arr[oldPos]; arr.splice(oldPos, 1); arr.splice(newPos, 0, el); }; exports.subtract = function (arr, arrExcluded) { var hash = {}; for (var ii = 0; ii < arrExcluded.length; ++ii) { hash[arrExcluded[ii]] = true; } var pred = function pred(val) { return !hash[val]; }; filterInPlace(arr, pred); }; exports.intersect = function (arr, arrExcluded) { var hash = {}; for (var ii = 0; ii < arrExcluded.length; ++ii) { hash[arrExcluded[ii]] = true; } var pred = function pred(val) { return hash[val]; }; filterInPlace(arr, pred); }; // takes in array of arrays // arrays are presorted exports.intersectMany = function (arrays) { var indexLocations = []; var maxIndexLocations = []; arrays.forEach(function (array) { indexLocations.push(0); maxIndexLocations.push(array.length - 1); }); var finalOutput = []; for (; indexLocations.length > 0 && indexLocations[0] <= maxIndexLocations[0]; ++indexLocations[0]) { // advance indices to be at least equal to first array element var terminate = false; for (var ii = 1; ii < arrays.length; ++ii) { while (arrays[ii][indexLocations[ii]] < arrays[0][indexLocations[0]] && indexLocations[ii] <= maxIndexLocations[ii]) { ++indexLocations[ii]; } if (indexLocations[ii] > maxIndexLocations[ii]) { terminate = true; break; } } if (terminate) { break; } // check element equality var shouldAdd = true; for (var jj = 1; jj < arrays.length; ++jj) { if (arrays[0][indexLocations[0]] !== arrays[jj][indexLocations[jj]]) { shouldAdd = false; break; } } if (shouldAdd) { finalOutput.push(arrays[0][indexLocations[0]]); } } return finalOutput; }; },{}],11:[function(require,module,exports){ "use strict"; exports.createNode = function (tag, props) { var node = document.createElement(tag); if (props) { for (var key in props) { if (Object.prototype.hasOwnProperty.call(props, key) && key !== 'text') { node.setAttribute(key, props[key]); } } if (props.text) { node.textContent = props.text; } } return node; }; exports.createSelection = function (astItem, createCheckboxes, disableCheckboxes) { var props = { "class": 'item', 'data-key': astItem.id, 'data-value': astItem.value }; var hasDescription = !!astItem.description; if (hasDescription) { props['data-description'] = astItem.description; } if (astItem.initialIndex) { props['data-index'] = astItem.initialIndex; } var selectionNode = exports.createNode('div', props); if (hasDescription) { var popup = exports.createNode('span', { "class": 'description', text: '?' }); selectionNode.appendChild(popup); } if (!createCheckboxes) { selectionNode.innerText = astItem.text || astItem.value; } else { var optionLabelCheckboxId = "treemultiselect-".concat(astItem.treeId, "-").concat(astItem.id); var inputCheckboxProps = { "class": 'option', type: 'checkbox', id: optionLabelCheckboxId }; if (disableCheckboxes || astItem.disabled) { inputCheckboxProps.disabled = true; } var inputCheckbox = exports.createNode('input', inputCheckboxProps); // prepend child selectionNode.insertBefore(inputCheckbox, selectionNode.firstChild); var labelProps = { "class": astItem.disabled ? 'disabled' : '', "for": optionLabelCheckboxId, text: astItem.text || astItem.value }; var label = exports.createNode('label', labelProps); selectionNode.appendChild(label); } return selectionNode; }; exports.createSelected = function (astItem, disableRemoval, showSectionOnSelected) { var node = exports.createNode('div', { "class": 'item', 'data-key': astItem.id, 'data-value': astItem.value, text: astItem.text }); if (!disableRemoval && !astItem.disabled) { var removalSpan = exports.createNode('span', { "class": 'remove-selected', text: '×' }); node.insertBefore(removalSpan, node.firstChild); } if (showSectionOnSelected) { var sectionSpan = exports.createNode('span', { "class": 'section-name', text: astItem.section }); node.appendChild(sectionSpan); } return node; }; exports.createSection = function (astSection, createCheckboxes, disableCheckboxes) { var sectionNode = exports.createNode('div', { "class": 'section', 'data-key': astSection.id }); var titleNode = exports.createNode('div', { "class": 'title', text: astSection.name }); if (createCheckboxes) { var checkboxProps = { "class": 'section', type: 'checkbox' }; if (disableCheckboxes) { checkboxProps.disabled = true; } var checkboxNode = exports.createNode('input', checkboxProps); titleNode.insertBefore(checkboxNode, titleNode.firstChild); } sectionNode.appendChild(titleNode); return sectionNode; }; },{}],12:[function(require,module,exports){ "use strict"; exports.array = require('./array'); exports.assert = function (bool, message) { if (!bool) { throw new Error(message || 'Assertion failed'); } }; exports.dom = require('./dom'); exports.getKey = function (el) { exports.assert(el); return parseInt(el.getAttribute('data-key')); }; exports.isInteger = function (value) { var x; if (isNaN(value)) { return false; } x = parseFloat(value); return (x | 0) === x; }; },{"./array":10,"./dom":11}]},{},[1]);