From 1c2a4998bbdf236857d8e275e61d9fb296dc18fa Mon Sep 17 00:00:00 2001 From: Manuel van Rijn Date: Mon, 14 Oct 2013 09:25:33 +0200 Subject: [PATCH] version bump to v0.8.0 --- README.md | 1 + lib/selectize-rails/version.rb | 2 +- vendor/assets/javascripts/selectize.js | 618 ++++++++++++------ .../stylesheets/selectize.bootstrap2.css | 30 +- .../stylesheets/selectize.bootstrap3.css | 25 +- vendor/assets/stylesheets/selectize.css | 25 +- .../assets/stylesheets/selectize.default.css | 35 +- .../assets/stylesheets/selectize.legacy.css | 25 +- 8 files changed, 554 insertions(+), 207 deletions(-) diff --git a/README.md b/README.md index 6b4b11e..1d98ff3 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ See the [demo page of Brian Reavis](http://brianreavis.github.io/selectize.js/) | Version | Notes | | -------:| ----------------------------------------------------------- | +| 0.8.0 | Update to v0.8.0 of selectize.js | | 0.7.7 | Update to v0.7.7 of selectize.js | | 0.7.6 | Update to v0.7.6 of selectize.js | | 0.7.5 | Update to v0.7.5 of selectize.js | diff --git a/lib/selectize-rails/version.rb b/lib/selectize-rails/version.rb index 4d55eae..9fd9dc6 100644 --- a/lib/selectize-rails/version.rb +++ b/lib/selectize-rails/version.rb @@ -1,5 +1,5 @@ module Selectize module Rails - VERSION = "0.7.7" + VERSION = "0.8.0" end end diff --git a/vendor/assets/javascripts/selectize.js b/vendor/assets/javascripts/selectize.js index a982e71..92acae1 100644 --- a/vendor/assets/javascripts/selectize.js +++ b/vendor/assets/javascripts/selectize.js @@ -176,12 +176,120 @@ return scoreObject(tokens[0], data); }; } - return function(data) { - for (var i = 0, sum = 0; i < token_count; i++) { - sum += scoreObject(tokens[i], data); - } - return sum / token_count; + + if (search.options.conjunction === 'and') { + return function(data) { + var score; + for (var i = 0, sum = 0; i < token_count; i++) { + score = scoreObject(tokens[i], data); + if (score <= 0) return 0; + sum += score; + } + return sum / token_count; + }; + } else { + return function(data) { + for (var i = 0, sum = 0; i < token_count; i++) { + sum += scoreObject(tokens[i], data); + } + return sum / token_count; + }; + } + }; + + /** + * Returns a function that can be used to compare two + * results, for sorting purposes. If no sorting should + * be performed, `null` will be returned. + * + * @param {string|object} search + * @param {object} options + * @return function(a,b) + */ + Sifter.prototype.getSortFunction = function(search, options) { + var i, n, self, field, fields, fields_count, multiplier, multipliers, get_field, implicit_score, sort; + + self = this; + search = self.prepareSearch(search, options); + sort = (!search.query && options.sort_empty) || options.sort; + + /** + * Fetches the specified sort field value + * from a search result item. + * + * @param {string} name + * @param {object} result + * @return {mixed} + */ + get_field = function(name, result) { + if (name === '$score') return result.score; + return self.items[result.id][name]; }; + + // parse options + fields = []; + if (sort) { + for (i = 0, n = sort.length; i < n; i++) { + if (search.query || sort[i].field !== '$score') { + fields.push(sort[i]); + } + } + } + + // the "$score" field is implied to be the primary + // sort field, unless it's manually specified + if (search.query) { + implicit_score = true; + for (i = 0, n = fields.length; i < n; i++) { + if (fields[i].field === '$score') { + implicit_score = false; + break; + } + } + if (implicit_score) { + fields.unshift({field: '$score', direction: 'desc'}); + } + } else { + for (i = 0, n = fields.length; i < n; i++) { + if (fields[i].field === '$score') { + fields.splice(i, 1); + break; + } + } + } + + multipliers = []; + for (i = 0, n = fields.length; i < n; i++) { + multipliers.push(fields[i].direction === 'desc' ? -1 : 1); + } + + // build function + fields_count = fields.length; + if (!fields_count) { + return null; + } else if (fields_count === 1) { + field = fields[0].field; + multiplier = multipliers[0]; + return function(a, b) { + return multiplier * cmp( + get_field(field, a), + get_field(field, b) + ); + }; + } else { + return function(a, b) { + var i, result, a_value, b_value, field; + for (i = 0; i < fields_count; i++) { + field = fields[i].field; + result = multipliers[i] * cmp( + get_field(field, a), + get_field(field, b) + ); + if (result) return result; + } + return 0; + }; + } }; /** @@ -195,8 +303,19 @@ */ Sifter.prototype.prepareSearch = function(query, options) { if (typeof query === 'object') return query; + + options = extend({}, options); + + var option_fields = options.fields; + var option_sort = options.sort; + var option_sort_empty = options.sort_empty; + + if (option_fields && !is_array(option_fields)) options.fields = [option_fields]; + if (option_sort && !is_array(option_sort)) options.sort = [option_sort]; + if (option_sort_empty && !is_array(option_sort_empty)) options.sort_empty = [option_sort_empty]; + return { - options : extend({}, options), + options : options, query : String(query || '').toLowerCase(), tokens : this.tokenize(query), total : 0, @@ -210,9 +329,9 @@ * The `options` parameter can contain: * * - fields {string|array} - * - sort {string} - * - direction {string} + * - sort {array} * - score {function} + * - filter {bool} * - limit {integer} * * Returns an object containing: @@ -229,41 +348,33 @@ */ Sifter.prototype.search = function(query, options) { var self = this, value, score, search, calculateScore; + var fn_sort; + var fn_score; search = this.prepareSearch(query, options); options = search.options; query = search.query; // generate result scoring function - if (!is_array(options.fields)) options.fields = [options.fields]; - calculateScore = options.score || self.getScoreFunction(search); + fn_score = options.score || self.getScoreFunction(search); // perform search and sort if (query.length) { self.iterator(self.items, function(item, id) { - score = calculateScore(item); - if (score > 0) { + score = fn_score(item); + if (options.filter === false || score > 0) { search.items.push({'score': score, 'id': id}); } }); - search.items.sort(function(a, b) { - return b.score - a.score; - }); } else { self.iterator(self.items, function(item, id) { search.items.push({'score': 1, 'id': id}); }); - if (options.sort) { - search.items.sort((function() { - var field = options.sort; - var multiplier = options.direction === 'desc' ? -1 : 1; - return function(a, b) { - return cmp(self.items[a.id][field], self.items[b.id][field]) * multiplier; - }; - })()); - } } + fn_sort = self.getSortFunction(search, options); + if (fn_sort) search.items.sort(fn_sort); + // apply limits search.total = search.items.length; if (typeof options.limit === 'number') { @@ -315,7 +426,8 @@ var DIACRITICS = { 'a': '[aÀÁÂÃÄÅàáâãäå]', - 'c': '[cÇç]', + 'c': '[cÇçćĆčČ]', + 'd': '[dđĐ]', 'e': '[eÈÉÊËèéêë]', 'i': '[iÌÍÎÏìíîï]', 'n': '[nÑñ]', @@ -330,9 +442,10 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - return Sifter; - })); + + /** * microplugin.js * Copyright (c) 2013 Brian Reavis & contributors @@ -470,7 +583,7 @@ })); /** - * selectize.js (v0.7.7) + * selectize.js (v0.8.0) * Copyright (c) 2013 Brian Reavis & contributors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this @@ -830,7 +943,7 @@ left: -99999, width: 'auto', padding: 0, - whiteSpace: 'nowrap' + whiteSpace: 'pre' }).text(str).appendTo('body'); transferStyles($parent, $test, [ @@ -910,22 +1023,29 @@ }; var Selectize = function($input, settings) { - var key, i, n, self = this; - $input[0].selectize = self; + var key, i, n, dir, input, self = this; + input = $input[0]; + input.selectize = self; + + // detect rtl environment + dir = window.getComputedStyle ? window.getComputedStyle(input, null).getPropertyValue('direction') : input.currentStyle && input.currentStyle.direction; + dir = dir || $input.parents('[dir]:first').attr('dir') || ''; // setup default state $.extend(self, { settings : settings, $input : $input, - tagType : $input[0].tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT, + tagType : input.tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT, + rtl : /rtl/i.test(dir), eventNS : '.selectize' + (++Selectize.count), highlightedValue : null, isOpen : false, isDisabled : false, + isRequired : $input.is(':required'), + isInvalid : false, isLocked : false, isFocused : false, - isInputFocused : false, isInputHidden : false, isSetup : false, isShiftDown : false, @@ -1014,7 +1134,7 @@ $wrapper = $('
').addClass(settings.wrapperClass).addClass(classes).addClass(inputMode); $control = $('
').addClass(settings.inputClass).addClass('items').appendTo($wrapper); - $control_input = $('').appendTo($control).attr('tabindex', tab_index); + $control_input = $('').appendTo($control).attr('tabindex', tab_index); $dropdown_parent = $(settings.dropdownParent || $wrapper); $dropdown = $('
').addClass(settings.dropdownClass).addClass(classes).addClass(inputMode).hide().appendTo($dropdown_parent); $dropdown_content = $('
').addClass(settings.dropdownContentClass).appendTo($dropdown); @@ -1043,27 +1163,16 @@ self.$dropdown = $dropdown; self.$dropdown_content = $dropdown_content; - $control.on('mousedown', function(e) { - if (!e.isDefaultPrevented()) { - window.setTimeout(function() { - self.focus(true); - }, 0); - } - }); - - // necessary for mobile webkit devices (manual focus triggering - // is ignored unless invoked within a click event) - $control.on('click', function(e) { - if (!self.isInputFocused) { - self.focus(true); - } - }); - $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); }); $dropdown.on('mousedown', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); }); watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); }); autoGrow($control_input); + $control.on({ + mousedown : function() { return self.onMouseDown.apply(self, arguments); }, + click : function() { return self.onClick.apply(self, arguments); } + }); + $control_input.on({ mousedown : function(e) { e.stopPropagation(); }, keydown : function() { return self.onKeyDown.apply(self, arguments); }, @@ -1090,13 +1199,7 @@ if (self.isFocused) { // prevent events on the dropdown scrollbar from causing the control to blur if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) { - var ignoreFocus = self.ignoreFocus; - self.ignoreFocus = true; - window.setTimeout(function() { - self.ignoreFocus = ignoreFocus; - self.focus(false); - }, 0); - return; + return false; } // blur on click outside if (!self.$control.has(e.target).length && e.target !== self.$control[0]) { @@ -1114,16 +1217,25 @@ self.ignoreHover = false; }); - self.$input.attr('tabindex',-1).hide().after(self.$wrapper); + self.$input.attr('tabindex', -1).hide().after(self.$wrapper); if ($.isArray(settings.items)) { self.setValue(settings.items); delete settings.items; } + // feature detect for the validation API + if (self.$input[0].validity) { + self.$input.on('invalid' + eventNS, function(e) { + e.preventDefault(); + self.isInvalid = true; + self.refreshState(); + }); + } + self.updateOriginalInput(); self.refreshItems(); - self.refreshClasses(); + self.refreshState(); self.updatePlaceholder(); self.isSetup = true; @@ -1163,7 +1275,7 @@ }, 'option_create': function(data, escape) { return '
Add ' + escape(data.input) + '
'; - }, + } }; self.settings.render = $.extend({}, templates, self.settings.render); @@ -1196,6 +1308,59 @@ } }, + /** + * Triggered when the main control element + * has a click event. + * + * @param {object} e + * @return {boolean} + */ + onClick: function(e) { + var self = this; + + // necessary for mobile webkit devices (manual focus triggering + // is ignored unless invoked within a click event) + if (!self.isFocused) { + self.focus(); + e.preventDefault(); + } + }, + + /** + * Triggered when the main control element + * has a mouse down event. + * + * @param {object} e + * @return {boolean} + */ + onMouseDown: function(e) { + var self = this; + var defaultPrevented = e.isDefaultPrevented(); + var $target = $(e.target); + + if (self.isFocused) { + // retain focus by preventing native handling. if the + // event target is the input it should not be modified. + // otherwise, text selection within the input won't work. + if (e.target !== self.$control_input[0]) { + if (self.settings.mode === 'single') { + // toggle dropdown + self.isOpen ? self.close() : self.open(); + } else if (!defaultPrevented) { + self.setActiveItem(null); + } + return false; + } + } else { + // give control focus + if (!defaultPrevented) { + window.setTimeout(function() { + self.focus(); + }, 0); + } + } + }, + /** * Triggered when the value of the control has been changed. * This should propagate the event to the original DOM @@ -1267,7 +1432,7 @@ e.preventDefault(); return; case KEY_RETURN: - if (self.$activeOption) { + if (self.isOpen && self.$activeOption) { self.onOptionSelect({currentTarget: self.$activeOption}); } e.preventDefault(); @@ -1342,7 +1507,6 @@ onFocus: function(e) { var self = this; - self.isInputFocused = true; self.isFocused = true; if (self.isDisabled) { self.blur(); @@ -1353,10 +1517,13 @@ if (self.ignoreFocus) return; if (self.settings.preload === 'focus') self.onSearchChange(''); - self.showInput(); - self.setActiveItem(null); - self.refreshOptions(!!self.settings.openOnFocus); - self.refreshClasses(); + if (!self.$activeItems.length) { + self.showInput(); + self.setActiveItem(null); + self.refreshOptions(!!self.settings.openOnFocus); + } + + self.refreshState(); }, /** @@ -1367,7 +1534,7 @@ */ onBlur: function(e) { var self = this; - self.isInputFocused = false; + self.isFocused = false; if (self.ignoreFocus) return; self.close(); @@ -1375,8 +1542,7 @@ self.setActiveItem(null); self.setActiveOption(null); self.setCaret(self.items.length); - self.isFocused = false; - self.refreshClasses(); + self.refreshState(); }, /** @@ -1401,9 +1567,10 @@ onOptionSelect: function(e) { var value, $target, $option, self = this; - e.preventDefault && e.preventDefault(); - e.stopPropagation && e.stopPropagation(); - self.focus(false); + if (e.preventDefault) { + e.preventDefault(); + e.stopPropagation(); + } $target = $(e.currentTarget); if ($target.hasClass('create')) { @@ -1430,11 +1597,10 @@ onItemSelect: function(e) { var self = this; + if (self.isLocked) return; if (self.settings.mode === 'multi') { e.preventDefault(); self.setActiveItem(e.currentTarget, e); - self.focus(false); - self.hideInput(); } }, @@ -1454,8 +1620,7 @@ self.loading = Math.max(self.loading - 1, 0); if (results && results.length) { self.addOption(results); - self.refreshOptions(false); - if (self.isInputFocused) self.open(); + self.refreshOptions(self.isFocused && !self.isInputHidden); } if (!self.loading) { $wrapper.removeClass('loading'); @@ -1517,13 +1682,16 @@ var i, idx, begin, end, item, swap; var $last; + if (self.settings.mode === 'single') return; $item = $($item); // clear the active selection if (!$item.length) { $(self.$activeItems).removeClass('active'); self.$activeItems = []; - self.isFocused = self.isInputFocused; + if (self.isFocused) { + self.showInput(); + } return; } @@ -1560,7 +1728,11 @@ self.$activeItems = [$item.addClass('active')[0]]; } - self.isFocused = !!self.$activeItems.length || self.isInputFocused; + // ensure control has focus + self.hideInput(); + if (!this.isFocused) { + self.focus(); + } }, /** @@ -1607,8 +1779,11 @@ */ selectAll: function() { this.$activeItems = Array.prototype.slice.apply(this.$control.children(':not(input)').addClass('active')); - this.isFocused = true; - if (this.$activeItems.length) this.hideInput(); + if (this.$activeItems.length) { + this.hideInput(); + this.close(); + } + this.focus(); }, /** @@ -1618,9 +1793,8 @@ hideInput: function() { var self = this; - self.close(); self.setTextboxValue(''); - self.$control_input.css({opacity: 0, position: 'absolute', left: -10000}); + self.$control_input.css({opacity: 0, position: 'absolute', left: self.rtl ? 10000 : -10000}); self.isInputHidden = true; }, @@ -1639,16 +1813,15 @@ * * @param {boolean} trigger */ - focus: function(trigger) { + focus: function() { var self = this; - if (self.isDisabled) return; + self.ignoreFocus = true; self.$control_input[0].focus(); - self.isInputFocused = true; window.setTimeout(function() { self.ignoreFocus = false; - if (trigger) self.onFocus(); + self.onFocus(); }, 0); }, @@ -1681,12 +1854,15 @@ */ getSearchOptions: function() { var settings = this.settings; - var fields = settings.searchField; + var sort = settings.sortField; + if (typeof sort === 'string') { + sort = {field: sort}; + } return { - fields : $.isArray(fields) ? fields : [fields], - sort : settings.sortField, - direction : settings.sortDirection, + fields : settings.searchField, + conjunction : settings.searchConjunction, + sort : sort }; }, @@ -1746,17 +1922,18 @@ * @param {boolean} triggerDropdown */ refreshOptions: function(triggerDropdown) { + var i, j, k, n, groups, groups_order, option, option_html, optgroup, optgroups, html, html_children, has_create_option; + var $active, $active_before, $create; + if (typeof triggerDropdown === 'undefined') { triggerDropdown = true; } - var self = this; - var i, n, groups, groups_order, option, optgroup, html, html_children; - var hasCreateOption; - var query = self.$control_input.val(); - var results = self.search(query); - var $active, $create; + var self = this; + var query = self.$control_input.val(); + var results = self.search(query); var $dropdown_content = self.$dropdown_content; + var active_before = self.$activeOption && hash_key(self.$activeOption.attr('data-value')); // build markup n = results.items.length; @@ -1777,16 +1954,22 @@ } for (i = 0; i < n; i++) { - option = self.options[results.items[i].id]; - optgroup = option[self.settings.optgroupField] || ''; - if (!self.optgroups.hasOwnProperty(optgroup)) { - optgroup = ''; - } - if (!groups.hasOwnProperty(optgroup)) { - groups[optgroup] = []; - groups_order.push(optgroup); + option = self.options[results.items[i].id]; + option_html = self.render('option', option); + optgroup = option[self.settings.optgroupField] || ''; + optgroups = $.isArray(optgroup) ? optgroup : [optgroup]; + + for (j = 0, k = optgroups && optgroups.length; j < k; j++) { + optgroup = optgroups[j]; + if (!self.optgroups.hasOwnProperty(optgroup)) { + optgroup = ''; + } + if (!groups.hasOwnProperty(optgroup)) { + groups[optgroup] = []; + groups_order.push(optgroup); + } + groups[optgroup].push(option_html); } - groups[optgroup].push(self.render('option', option)); } // render optgroup headers & join groups @@ -1823,20 +2006,28 @@ } // add create option - hasCreateOption = self.settings.create && results.query.length; - if (hasCreateOption) { + has_create_option = self.settings.create && results.query.length; + if (has_create_option) { $dropdown_content.prepend(self.render('option_create', {input: query})); $create = $($dropdown_content[0].childNodes[0]); } // activate - self.hasOptions = results.items.length > 0 || hasCreateOption; + self.hasOptions = results.items.length > 0 || has_create_option; if (self.hasOptions) { if (results.items.length > 0) { - if ($create) { - $active = self.getAdjacentOption($create, 1); - } else { - $active = $dropdown_content.find("[data-selectable]").first(); + $active_before = active_before && self.getOption(active_before); + if ($active_before && $active_before.length) { + $active = $active_before; + } else if (self.settings.mode === 'single' && self.items.length) { + $active = self.getOption(self.items[0]); + } + if (!$active || !$active.length) { + if ($create && !self.settings.addPrecedence) { + $active = self.getAdjacentOption($create, 1); + } else { + $active = $dropdown_content.find('[data-selectable]:first'); + } } } else { $active = $create; @@ -2060,7 +2251,7 @@ $item = $(self.render('item', self.options[value])); self.items.splice(self.caretPos, 0, value); self.insertAtCaret($item); - self.refreshClasses(); + self.refreshState(); if (self.isSetup) { options = self.$dropdown_content.find('[data-selectable]'); @@ -2080,19 +2271,6 @@ self.positionDropdown(); } - // restore focus to input - if (self.isFocused) { - window.setTimeout(function() { - if (inputMode === 'single') { - self.blur(); - self.focus(false); - self.hideInput(); - } else { - self.focus(false); - } - }, 0); - } - self.updatePlaceholder(); self.trigger('item_add', value, $item); self.updateOriginalInput(); @@ -2131,7 +2309,7 @@ self.setCaret(self.caretPos - 1); } - self.refreshClasses(); + self.refreshState(); self.updatePlaceholder(); self.updateOriginalInput(); self.positionDropdown(); @@ -2163,7 +2341,6 @@ var create = once(function(data) { self.unlock(); - self.focus(false); if (!data || typeof data !== 'object') return; var value = hash_key(data[self.settings.valueField]); @@ -2174,7 +2351,6 @@ self.setCaret(caret); self.addItem(value); self.refreshOptions(self.settings.mode !== 'single'); - self.focus(false); }); var output = setup.apply(this, [input, create]); @@ -2195,25 +2371,45 @@ } } - this.refreshClasses(); + this.refreshState(); this.updateOriginalInput(); }, + /** + * Updates all state-dependent attributes + * and CSS classes. + */ + refreshState: function() { + var self = this; + var invalid = self.isRequired && !self.items.length; + if (!invalid) self.isInvalid = false; + self.$control_input.prop('required', invalid); + self.refreshClasses(); + }, + /** * Updates all state-dependent CSS classes. */ refreshClasses: function() { - var self = this; - var isFull = self.isFull(); + var self = this; + var isFull = self.isFull(); var isLocked = self.isLocked; + + this.$wrapper + .toggleClass('rtl', self.rtl); + this.$control .toggleClass('focus', self.isFocused) .toggleClass('disabled', self.isDisabled) + .toggleClass('required', self.isRequired) + .toggleClass('invalid', self.isInvalid) .toggleClass('locked', isLocked) .toggleClass('full', isFull).toggleClass('not-full', !isFull) + .toggleClass('input-active', self.isFocused && !self.isInputHidden) .toggleClass('dropdown-active', self.isOpen) .toggleClass('has-options', !$.isEmptyObject(self.options)) .toggleClass('has-items', self.items.length > 0); + this.$control_input.data('grow', !isFull && !isLocked); }, @@ -2276,9 +2472,9 @@ var self = this; if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return; - self.focus(true); + self.focus(); self.isOpen = true; - self.refreshClasses(); + self.refreshState(); self.$dropdown.css({visibility: 'hidden', display: 'block'}); self.positionDropdown(); self.$dropdown.css({visibility: 'visible'}); @@ -2290,13 +2486,18 @@ */ close: function() { var self = this; + var trigger = self.isOpen; - if (!self.isOpen) return; + if (self.settings.mode === 'single' && this.items.length) { + self.hideInput(); + } + + self.isOpen = false; self.$dropdown.hide(); self.setActiveOption(null); - self.isOpen = false; - self.refreshClasses(); - self.trigger('dropdown_close', self.$dropdown); + self.refreshState(); + + if (trigger) self.trigger('dropdown_close', self.$dropdown); }, /** @@ -2328,7 +2529,7 @@ self.setCaret(0); self.updatePlaceholder(); self.updateOriginalInput(); - self.refreshClasses(); + self.refreshState(); self.showInput(); self.trigger('clear'); }, @@ -2431,11 +2632,12 @@ var self = this; if (direction === 0) return; + if (self.rtl) direction *= -1; tail = direction > 0 ? 'last' : 'first'; selection = getSelection(self.$control_input[0]); - if (self.isInputFocused && !self.isInputHidden) { + if (self.isFocused && !self.isInputHidden) { valueLength = self.$control_input.val().length; cursorAtEdge = direction < 0 ? selection.start === 0 && selection.length === 0 @@ -2450,7 +2652,6 @@ idx = self.$control.children(':not(input)').index($tail); self.setActiveItem(null); self.setCaret(direction > 0 ? idx + 1 : idx); - self.showInput(); } } }, @@ -2462,11 +2663,13 @@ * @param {object} e (optional) */ advanceCaret: function(direction, e) { + var self = this, fn, $adj; + if (direction === 0) return; - var self = this; - var fn = direction > 0 ? 'next' : 'prev'; + + fn = direction > 0 ? 'next' : 'prev'; if (self.isShiftDown) { - var $adj = self.$control_input[fn](); + $adj = self.$control_input[fn](); if ($adj.length) { self.hideInput(); self.setActiveItem($adj); @@ -2515,7 +2718,7 @@ lock: function() { this.close(); this.isLocked = true; - this.refreshClasses(); + this.refreshState(); }, /** @@ -2523,7 +2726,7 @@ */ unlock: function() { this.isLocked = false; - this.refreshClasses(); + this.refreshState(); }, /** @@ -2637,6 +2840,7 @@ maxOptions: 1000, maxItems: null, hideSelected: null, + addPrecedence: false, preload: false, scrollDuration: 60, @@ -2644,14 +2848,15 @@ dataAttr: 'data-data', optgroupField: 'optgroup', - sortField: '$order', - sortDirection: 'asc', valueField: 'value', labelField: 'text', optgroupLabelField: 'label', optgroupValueField: 'value', optgroupOrder: null, + + sortField: '$order', searchField: ['text'], + searchConjunction: 'and', mode: null, wrapperClass: 'selectize-control', @@ -2689,28 +2894,33 @@ } }; - $.fn.selectize = function(settings) { - settings = settings || {}; - - var defaults = $.fn.selectize.defaults; - var dataAttr = settings.dataAttr || defaults.dataAttr; + $.fn.selectize = function(settings_user) { + var defaults = $.fn.selectize.defaults; + var settings = $.extend({}, defaults, settings_user); + var attr_data = settings.dataAttr; + var field_label = settings.labelField; + var field_value = settings.valueField; + var field_optgroup = settings.optgroupField; + var field_optgroup_label = settings.optgroupLabelField; + var field_optgroup_value = settings.optgroupValueField; /** * Initializes selectize from a element. * * @param {object} $input - * @param {object} settings + * @param {object} settings_element */ var init_textbox = function($input, settings_element) { - var i, n, values, value = $.trim($input.val() || ''); + var i, n, values, option, value = $.trim($input.val() || ''); if (!value.length) return; - values = value.split(settings.delimiter || defaults.delimiter); + values = value.split(settings.delimiter); for (i = 0, n = values.length; i < n; i++) { - settings_element.options[values[i]] = { - 'text' : values[i], - 'value' : values[i] - }; + option = {}; + option[field_label] = values[i]; + option[field_value] = values[i]; + + settings_element.options[values[i]] = option; } settings_element.items = values; @@ -2720,16 +2930,14 @@ * Initializes selectize from a