import './selectize.html'; /* Meteor need globals */ /* eslint strict: 0 */ /* jshint strict: false */ UniSelectize = function (options, template) { this.items = new ReactiveVar([]); this.itemsSelected = new ReactiveVar([]); this.itemsUnselected = new ReactiveVar([]); this.open = new ReactiveVar(false); this.loading = new ReactiveVar(false); this.searchText = new ReactiveVar(); this.activeOption = new ReactiveVar(-1); this.inputPosition = new ReactiveVar(-1); this.optionsMethodParams = new ReactiveVar(); this.create = options.create; this.template = template; this.multiple = options.multiple; this.sortMethod = _.isUndefined(options.sortMethod) ? 'label' : options.sortMethod; this.placeholder = options.placeholder; this.removeButton = options.removeButton !== false; this.createMethod = options.createMethod; this.optionsMethod = options.optionsMethod; this.optionsPlaceholder = options.optionsPlaceholder; }; UniSelectize.prototype.triggerChangeEvent = function() { var self = this; Meteor.defer(function () { $(self.template.find('select')).change(); }); }; UniSelectize.prototype.setItems = function (items, value) { if (!_.isArray(items)) { console.warn('invalid options format'); } var values = value && (_.isArray(value) ? value : [value]); items = _.filter(items, function (item) { if (!item.value || !item.label) { console.info('invalid option', item); return false; } return true; }); var itemValues = items.map(function (item) { return item.value; }); _.each(values, function (val) { if (!_.contains(itemValues, val) && val) { items.push({ value: val, label: val }); } }); _.each(items, function (item) { if (_.contains(values, item.value)) { item.selected = true; } }); this.items.set(items); }; UniSelectize.prototype.addItems = function (newItems, value) { if (!_.isArray(newItems)) { console.warn('invalid options format'); } var values = value && (_.isArray(value) ? value : [value]); var items = this.items.get(); var itemsValues = items.map(function (item) { return item.value; }); _.each(newItems, function (newItem) { if (!newItem.value || !newItem.label) { console.info('invalid option', newItem); return; } if (!_.contains(itemsValues, newItem.value)) { var item = { value: newItem.value, label: newItem.label, selected: newItem.selected }; if (_.contains(values, newItem.value)) { item.selected = true; } items.push(item); } else if (typeof newItem.selected !== 'undefined') { var item = _.find(items, function (item) { return item.value === newItem.value; }); item.selected = newItem.selected; } }); this.items.set(items); }; UniSelectize.prototype.removeUnusedItems = function (newItems) { if (!_.isArray(newItems)) { console.warn('invalid options format'); } var items = this.items.get(); var newItemsValues = newItems.map(function (item) { return item.value; }); items = _.filter(items, function (item) { return _.contains(newItemsValues, item.value) || item.selected; }); this.items.set(items); }; UniSelectize.prototype.itemsAutorun = function () { var items = this.items.get(); var itemsSelected = []; var itemsUnselected = []; _.each(items, function (item) { if (item.selected) { itemsSelected.push(item); } else { itemsUnselected.push(item); } }); if (this.sortMethod) { itemsSelected = _.sortBy(itemsSelected, this.sortMethod); itemsUnselected = _.sortBy(itemsUnselected, this.sortMethod); } var itemsSelectedPrev = this.itemsSelected.get(); if (!_.isEqual(itemsSelectedPrev, itemsSelected)) { this.itemsSelected.set(itemsSelected); } if (this.placeholder && this.optionsPlaceholder) { itemsUnselected.unshift({ value: '', label: _.isString(this.optionsPlaceholder) ? this.optionsPlaceholder: this.placeholder }); } this.itemsUnselected.set(itemsUnselected); }; UniSelectize.prototype.itemsSelectedAutorun = function () { var itemsSelected = this.template.uniSelectize.itemsSelected.get(); this.template.uniSelectize.inputPosition.set(itemsSelected.length - 1); }; UniSelectize.prototype.inputFocus = function () { var self = this; Meteor.defer(function () { var $input = $(self.template.find('input')); $input.focus(); }); }; UniSelectize.prototype.selectItem = function (value) { var items = this.items.get(); var multiple = this.multiple; _.each(items, function (item) { if (value === '') { item.selected = false; } else if (item.value === value) { item.selected = true; } else if (!multiple) { item.selected = false; } }); this.setItems(items); this.triggerChangeEvent(); }; UniSelectize.prototype.unselectItem = function (value, reset) { var items = this.items.get(); _.each(items, function (item) { if (item.value === value || reset) { item.selected = false; } }); this.setItems(items); this.triggerChangeEvent() }; UniSelectize.prototype.removeItemBeforeInput = function () { var items = this.itemsSelected.get(); var inputPosition = this.inputPosition.get(); var itemToRemove; _.each(items, function (item, index) { if (index === inputPosition) { itemToRemove = item; } }); if (itemToRemove) { this.unselectItem(itemToRemove.value, false); } }; UniSelectize.prototype.removeItemAfterInput = function () { var items = this.itemsSelected.get(); var inputPosition = this.inputPosition.get(); var itemToRemove; _.each(items, function (item, index) { if (index === inputPosition + 1) { itemToRemove = item; } }); if (itemToRemove) { this.unselectItem(itemToRemove.value, false); } }; UniSelectize.prototype.selectActiveItem = function () { var itemsUnselected = this.getItemsUnselectedFiltered(); var activeOption = this.activeOption.get(); var itemToSelect = itemsUnselected && itemsUnselected[activeOption]; if (activeOption === itemsUnselected.length && this.create) { this.createItem(); return; } itemToSelect && this.selectItem(itemToSelect.value); if (this.multiple) { this.open.set(true); this.inputFocus(); } else { this.open.set(false); } }; UniSelectize.prototype.insertItem = function (item) { var items = this.items.get(); if (!_.find(items, function (obj) { if (obj.value === item.value) { obj.selected = item.selected; return true; } return false; })) { items.push(item); } this.setItems(items); this.triggerChangeEvent(); }; UniSelectize.prototype.createItem = function () { var self = this; var template = this.template; var searchText = this.searchText.get(); if (!searchText) { return false; } var item = { label: searchText, value: searchText, selected: true }; if (template.uniSelectize.createMethod) { Meteor.call(template.uniSelectize.createMethod, searchText, searchText, function (error, value) { if (error) { console.error('universe selectize create method error:', error); return; } Meteor.defer(function () { item.value = value || item.value; self.insertItem(item); }); }); } else { this.insertItem(item); } if (this.multiple) { this.inputFocus(); } else { this.open.set(false); } }; UniSelectize.prototype.getItemsUnselectedFiltered = function () { var items = this.itemsUnselected.get(); var searchText = this.searchText.get(); return _.filter(items, function (item) { if (item.label && item.label.search(new RegExp(searchText, 'i')) !== -1) { return true; } return false; }); }; UniSelectize.prototype.checkDisabled = function () { if (this.template.data.disabled) { throw new Meteor.Error('This field is disabled'); } }; UniSelectize.prototype.measureString = function (str, $parent) { if (!str) { return 0; } var $test = $('').css({ position: 'absolute', top: -99999, left: -99999, width: 'auto', padding: 0, whiteSpace: 'pre' }).text(str).appendTo('body'); this.transferStyles($parent, $test, [ 'letterSpacing', 'fontSize', 'fontFamily', 'fontWeight', 'textTransform' ]); var width = $test.width(); $test.remove(); return width; }; UniSelectize.prototype.transferStyles = function ($from, $to, properties) { var i, n, styles = {}; if (properties) { for (i = 0, n = properties.length; i < n; i++) { styles[properties[i]] = $from.css(properties[i]); } } else { styles = $from.css(); } $to.css(styles); }; UniSelectize.prototype.getOptionsFromMethod = function (values) { var self = this; var methodName = this.optionsMethod; var searchText = this.searchText.get(); var params = this.optionsMethodParams.get(); if (!methodName) { return false; } var searchVal = { searchText: searchText, values: values || [], params: params || null }; self.loading.set(true); Meteor.call(methodName, searchVal, function (err, options) { self.loading.set(false); if (params) { self.removeUnusedItems(options); } self.addItems(options, values); }); }; Template.Selectize.onCreated(function () { var template = this; template.uniSelectize = new UniSelectize(template.data, template); }); Template.Selectize.onRendered(function () { var template = this; template.autorun(function () { var data = Template.currentData(); var value = data.value; if (template.uniSelectize.optionsMethod) { template.uniSelectize.getOptionsFromMethod(value); } else { var options = data.options; template.uniSelectize.setItems(options, value); } }); template.autorun(function () { template.uniSelectize.itemsAutorun(); }); template.autorun(function () { template.uniSelectize.itemsSelectedAutorun(); }); template.autorun(function () { var data = Template.currentData(); var methodParams = data.optionsMethodParams; var params = _.isFunction(methodParams) ? methodParams() : methodParams; template.uniSelectize.optionsMethodParams.set(params); }); this.form = $(template.find('select')).parents('form'); this.form.bind('reset', function () { template.uniSelectize.unselectItem(null, true); }); }); Template.Selectize.onDestroyed(function () { if (this.form) { this.form.unbind('reset'); } }); Template.Selectize.helpers({ multiple: function () { var template = Template.instance(); return template.uniSelectize.multiple; }, removeButton: function () { var template = Template.instance(); return template.uniSelectize.multiple && template.uniSelectize.removeButton; }, getItems: function () { var template = Template.instance(); return template.uniSelectize.items.get(); }, getItemsSelected: function () { var template = Template.instance(); return template.uniSelectize.itemsSelected.get(); }, getItemsUnselected: function () { var template = Template.instance(); return template.uniSelectize.getItemsUnselectedFiltered(); }, getSearchText: function () { var template = Template.instance(); return template.uniSelectize.searchText.get(); }, open: function () { var template = Template.instance(); return template.uniSelectize.open.get(); }, loading: function () { var template = Template.instance(); return template.uniSelectize.loading.get(); }, inputPosition: function (position) { var template = Template.instance(); var inputPosition = template.uniSelectize.inputPosition.get(); return position === inputPosition; }, activeOption: function (position) { var template = Template.instance(); var activeOption = template.uniSelectize.activeOption.get(); var itemsUnselected = template.uniSelectize.getItemsUnselectedFiltered(); var createOption = template.uniSelectize.create; if (activeOption === itemsUnselected.length && createOption) { return position === 'create'; } return position === activeOption; }, getPlaceholder: function () { var template = Template.instance(); var itemsSelected = template.uniSelectize.itemsSelected.get(); if (itemsSelected.length) { return false; } return template.uniSelectize.placeholder; }, isPlaceholder: function () { return this.value === '' ? 'uniPlaceholder' : ''; } }); Template.Selectize.events({ 'click .selectize-input': function (e, template) { template.uniSelectize.checkDisabled(); template.uniSelectize.inputFocus(template); template.uniSelectize.getOptionsFromMethod(); }, 'keydown input.js-selectizeInput': function (e, template) { var uniSelectize = template.uniSelectize; var itemsSelected = uniSelectize.itemsSelected.get(); var itemsUnselected = uniSelectize.getItemsUnselectedFiltered(); var inputPosition = uniSelectize.inputPosition.get(); var activeOption = uniSelectize.activeOption.get(); template.uniSelectize.checkDisabled(); var $input = $(e.target); var width = template.uniSelectize.measureString($input.val(), $input) + 10; $input.width(width); switch (e.keyCode) { case 8: // backspace if ($input.val() === '') { e.preventDefault(); uniSelectize.removeItemBeforeInput(); } uniSelectize.open.set(true); uniSelectize.inputFocus(); break; case 46: // delete if ($input.val() === '') { uniSelectize.removeItemAfterInput(); } uniSelectize.open.set(true); uniSelectize.inputFocus(); break; case 27: // escape $input.blur(); break; case 13: // enter e.preventDefault(); if (activeOption === -1 && $input.val() === '') { break; } if (itemsUnselected && itemsUnselected.length > 0) { uniSelectize.selectActiveItem(template); uniSelectize.searchText.set(''); $input.val(''); } else if (uniSelectize.create /*&& createOnBlur*/) { uniSelectize.createItem(); uniSelectize.searchText.set(''); $input.val(''); } break; case 37: // left if (!uniSelectize.multiple) { break; } if (inputPosition > -1) { uniSelectize.inputPosition.set(inputPosition - 1); uniSelectize.inputFocus(); } break; case 39: // right if (!uniSelectize.multiple) { break; } if (inputPosition < itemsSelected.length - 1) { uniSelectize.inputPosition.set(inputPosition + 1); uniSelectize.inputFocus(); } break; case 38: // up if (activeOption > -1) { uniSelectize.activeOption.set(activeOption - 1); } break; case 40: // down if (activeOption < itemsUnselected.length - 1 || (activeOption < itemsUnselected.length && uniSelectize.create)) { uniSelectize.activeOption.set(activeOption + 1); } break; } if (!template.uniSelectize.multiple && itemsSelected.length) { return false; } }, 'keyup input.js-selectizeInput': function (e, template) { template.uniSelectize.checkDisabled(); var $el = $(e.target); var value = $el.val(); template.uniSelectize.searchText.set(value); template.uniSelectize.getOptionsFromMethod(); }, 'focus input.js-selectizeInput': function (e, template) { template.uniSelectize.checkDisabled(); template.uniSelectize.open.set(true); Meteor.clearTimeout(template.uniSelectize.timeoutId); }, 'change input.js-selectizeInput': function(e, template) { template.uniSelectize.checkDisabled(); // prevent non-autoform fields changes from submitting the form when autosave is enabled e.preventDefault(); e.stopPropagation(); }, 'blur input.js-selectizeInput': function (e, template) { template.uniSelectize.checkDisabled(); template.uniSelectize.timeoutId = Meteor.setTimeout(function () { template.uniSelectize.open.set(false); }, 500); }, 'scroll .selectize-dropdown-content': function (e, template) { Meteor.clearTimeout(template.uniSelectize.timeoutId); template.uniSelectize.timeoutId = Meteor.setTimeout(function () { template.uniSelectize.open.set(false); }, 5000); }, 'click .selectize-dropdown-content > div:not(.create)': function (e, template) { e.preventDefault(); template.uniSelectize.checkDisabled(); var $input = $(template.find('input')); var itemsUnselected = template.uniSelectize.getItemsUnselectedFiltered(); var itemsUnselectedLength = itemsUnselected && itemsUnselected.length; template.uniSelectize.selectItem(this.value); template.uniSelectize.searchText.set(''); $input.val(''); if (template.uniSelectize.multiple && itemsUnselectedLength && this.value) { template.uniSelectize.inputFocus(); } else { template.uniSelectize.open.set(false); } }, 'mouseenter .selectize-dropdown-content > div': function (e, template) { var $el = $(e.target); var elIndex = $el.attr('data-index'); var itemsUnselected = template.uniSelectize.getItemsUnselectedFiltered(); if (elIndex === 'create') { elIndex = itemsUnselected.length; } else { elIndex = parseInt(elIndex); } template.uniSelectize.activeOption.set(elIndex); }, 'click .create': function (e, template) { e.preventDefault(); template.uniSelectize.checkDisabled(); var $input = $(template.find('input')); template.uniSelectize.createItem(); template.uniSelectize.searchText.set(''); $input.val(''); }, 'click .remove': function (e, template) { e.preventDefault(); template.uniSelectize.checkDisabled(); template.uniSelectize.unselectItem(this.value, false); } });