├── .gitignore ├── examples ├── images │ ├── bg.png │ ├── check@2x.png │ ├── spinner.gif │ ├── repo-forked.png │ └── repo-source.png ├── confirm.html ├── js │ ├── index.js │ └── es5.js ├── dynamic.html ├── required.html ├── performance.html ├── rtl.html ├── lock.html ├── customization.html ├── css │ ├── stylesheet.css │ └── normalize.css ├── api.html ├── events.html ├── plugins.html ├── github.html ├── movies.html ├── cities.html ├── contacts.html └── optgroups.html ├── testem.json ├── .travis.yml ├── dist └── less │ ├── plugins │ ├── optgroup_columns.less │ ├── drag_drop.less │ ├── dropdown_header.less │ └── remove_button.less │ ├── selectize.default.less │ ├── selectize.legacy.less │ ├── selectize.bootstrap3.less │ └── selectize.bootstrap2.less ├── src ├── plugins │ ├── optgroup_columns │ │ ├── plugin.less │ │ └── plugin.js │ ├── drag_drop │ │ ├── plugin.less │ │ └── plugin.js │ ├── dropdown_header │ │ ├── plugin.less │ │ └── plugin.js │ ├── remove_button │ │ ├── plugin.less │ │ └── plugin.js │ └── restore_on_backspace │ │ └── plugin.js ├── constants.js ├── less │ ├── .wrapper.css │ ├── selectize.default.less │ ├── selectize.legacy.less │ ├── selectize.bootstrap3.less │ └── selectize.bootstrap2.less ├── .wrapper.js ├── contrib │ ├── highlight.js │ └── microevent.js ├── defaults.js ├── selectize.jquery.js └── utils.js ├── package.json ├── Makefile ├── bower.json ├── selectize.jquery.json ├── test ├── events_dom.js ├── xss.js ├── index.html ├── vendor │ └── mocha.css ├── interaction.js ├── setup.js └── events.js ├── docs ├── events.md ├── plugins.md └── api.md ├── README.md └── Gruntfile.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DAV 3 | node_modules 4 | bower_components 5 | *.log -------------------------------------------------------------------------------- /examples/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyorg/selectize.js/master/examples/images/bg.png -------------------------------------------------------------------------------- /examples/images/check@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyorg/selectize.js/master/examples/images/check@2x.png -------------------------------------------------------------------------------- /examples/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyorg/selectize.js/master/examples/images/spinner.gif -------------------------------------------------------------------------------- /examples/images/repo-forked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyorg/selectize.js/master/examples/images/repo-forked.png -------------------------------------------------------------------------------- /examples/images/repo-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyorg/selectize.js/master/examples/images/repo-source.png -------------------------------------------------------------------------------- /testem.json: -------------------------------------------------------------------------------- 1 | { 2 | "framework": "mocha", 3 | "test_page": "test/index.html", 4 | "launch_in_ci": ["PhantomJS"], 5 | "launch_in_dev": ["PhantomJS", "Chrome"] 6 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_script: 5 | - npm install -g bower 6 | - npm install -g testem 7 | - npm install -g grunt-cli 8 | - npm install 9 | - make 10 | script: 11 | - testem ci -------------------------------------------------------------------------------- /dist/less/plugins/optgroup_columns.less: -------------------------------------------------------------------------------- 1 | .selectize-dropdown.plugin-optgroup_columns { 2 | .optgroup { 3 | border-right: 1px solid #f2f2f2; 4 | border-top: 0 none; 5 | float: left; 6 | .selectize-box-sizing(border-box); 7 | } 8 | .optgroup:last-child { 9 | border-right: 0 none; 10 | } 11 | .optgroup:before { 12 | display: none; 13 | } 14 | .optgroup-header { 15 | border-top: 0 none; 16 | } 17 | } -------------------------------------------------------------------------------- /src/plugins/optgroup_columns/plugin.less: -------------------------------------------------------------------------------- 1 | .selectize-dropdown.plugin-optgroup_columns { 2 | .optgroup { 3 | border-right: 1px solid #f2f2f2; 4 | border-top: 0 none; 5 | float: left; 6 | .selectize-box-sizing(border-box); 7 | } 8 | .optgroup:last-child { 9 | border-right: 0 none; 10 | } 11 | .optgroup:before { 12 | display: none; 13 | } 14 | .optgroup-header { 15 | border-top: 0 none; 16 | } 17 | } -------------------------------------------------------------------------------- /dist/less/plugins/drag_drop.less: -------------------------------------------------------------------------------- 1 | .selectize-control.plugin-drag_drop { 2 | &.multi > .selectize-input > div.ui-sortable-placeholder { 3 | visibility: visible !important; 4 | background: #f2f2f2 !important; 5 | background: rgba(0,0,0,0.06) !important; 6 | border: 0 none !important; 7 | .selectize-box-shadow(inset 0 0 12px 4px #fff); 8 | } 9 | .ui-sortable-placeholder::after { 10 | content: '!'; 11 | visibility: hidden; 12 | } 13 | .ui-sortable-helper { 14 | .selectize-box-shadow(0 2px 5px rgba(0,0,0,0.2)); 15 | } 16 | } -------------------------------------------------------------------------------- /src/plugins/drag_drop/plugin.less: -------------------------------------------------------------------------------- 1 | .selectize-control.plugin-drag_drop { 2 | &.multi > .selectize-input > div.ui-sortable-placeholder { 3 | visibility: visible !important; 4 | background: #f2f2f2 !important; 5 | background: rgba(0,0,0,0.06) !important; 6 | border: 0 none !important; 7 | .selectize-box-shadow(inset 0 0 12px 4px #fff); 8 | } 9 | .ui-sortable-placeholder::after { 10 | content: '!'; 11 | visibility: hidden; 12 | } 13 | .ui-sortable-helper { 14 | .selectize-box-shadow(0 2px 5px rgba(0,0,0,0.2)); 15 | } 16 | } -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | var IS_MAC = /Mac/.test(navigator.userAgent); 2 | 3 | var KEY_A = 65; 4 | var KEY_COMMA = 188; 5 | var KEY_RETURN = 13; 6 | var KEY_ESC = 27; 7 | var KEY_LEFT = 37; 8 | var KEY_UP = 38; 9 | var KEY_RIGHT = 39; 10 | var KEY_DOWN = 40; 11 | var KEY_BACKSPACE = 8; 12 | var KEY_DELETE = 46; 13 | var KEY_SHIFT = 16; 14 | var KEY_CMD = IS_MAC ? 91 : 17; 15 | var KEY_CTRL = IS_MAC ? 18 : 17; 16 | var KEY_TAB = 9; 17 | 18 | var TAG_SELECT = 1; 19 | var TAG_INPUT = 2; -------------------------------------------------------------------------------- /dist/less/plugins/dropdown_header.less: -------------------------------------------------------------------------------- 1 | .selectize-dropdown-header { 2 | position: relative; 3 | padding: @selectize-padding-dropdown-item-y @selectize-padding-dropdown-item-x; 4 | border-bottom: 1px solid @selectize-color-border; 5 | background: mix(@selectize-color-dropdown, @selectize-color-border, 85%); 6 | .selectize-border-radius(@selectize-border-radius @selectize-border-radius 0 0); 7 | } 8 | .selectize-dropdown-header-close { 9 | position: absolute; 10 | right: @selectize-padding-dropdown-item-x; 11 | top: 50%; 12 | color: @selectize-color-text; 13 | opacity: 0.4; 14 | margin-top: -12px; 15 | line-height: 20px; 16 | font-size: 20px !important; 17 | } 18 | .selectize-dropdown-header-close:hover { 19 | color: darken(@selectize-color-text, 25%); 20 | } -------------------------------------------------------------------------------- /src/less/.wrapper.css: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.css (v@@version) 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @@css -------------------------------------------------------------------------------- /src/plugins/dropdown_header/plugin.less: -------------------------------------------------------------------------------- 1 | .selectize-dropdown-header { 2 | position: relative; 3 | padding: @selectize-padding-dropdown-item-y @selectize-padding-dropdown-item-x; 4 | border-bottom: 1px solid @selectize-color-border; 5 | background: mix(@selectize-color-dropdown, @selectize-color-border, 85%); 6 | .selectize-border-radius(@selectize-border-radius @selectize-border-radius 0 0); 7 | } 8 | .selectize-dropdown-header-close { 9 | position: absolute; 10 | right: @selectize-padding-dropdown-item-x; 11 | top: 50%; 12 | color: @selectize-color-text; 13 | opacity: 0.4; 14 | margin-top: -12px; 15 | line-height: 20px; 16 | font-size: 20px !important; 17 | } 18 | .selectize-dropdown-header-close:hover { 19 | color: darken(@selectize-color-text, 25%); 20 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "selectize", 3 | "keywords": ["select", "ui", "form", "input", "control", "autocomplete", "tagging", "tag"], 4 | "description": "Selectize is a jQuery-based custom UI control. Useful for tagging, contact lists, country selectors, etc.", 5 | "version": "0.8.5", 6 | "license": "Apache License, Version 2.0", 7 | "readmeFilename": "README.md", 8 | "repository": { 9 | "type": "git", 10 | "url": "git://github.com/brianreavis/selectize.js.git" 11 | }, 12 | "main": [ 13 | "dist/css/selectize.css", 14 | "dist/js/selectize.js" 15 | ], 16 | "ignore": [ 17 | "Makefile", 18 | "Gruntfile.js", 19 | "examples", 20 | "node_modules", 21 | "bower_components", 22 | "docs", 23 | "src", 24 | "test", 25 | ".travis.yml", 26 | "testem.json", 27 | "selectize.jquery.json", 28 | "*.sh" 29 | ], 30 | "dependencies": { 31 | "jquery": ">=1.7.0", 32 | "sifter": "0.3.x", 33 | "microplugin": "0.0.x" 34 | }, 35 | "devDependencies": { 36 | "bootstrap2": "bootstrap#2", 37 | "bootstrap3": "bootstrap#3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /selectize.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "selectize", 3 | "version": "0.8.5", 4 | "title": "Selectize.js", 5 | "author": { 6 | "name": "Brian Reavis", 7 | "email": "brian@thirdroute.com", 8 | "url": "http://thirdroute.com" 9 | }, 10 | "keywords": ["select", "ui", "form", "input", "control", "autocomplete", "tagging", "tag"], 11 | "description": "Selectize is a jQuery-based custom 28 | 29 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/plugins/restore_on_backspace/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin: "restore_on_backspace" (selectize.js) 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | Selectize.define('restore_on_backspace', function(options) { 18 | var self = this; 19 | 20 | options.text = options.text || function(option) { 21 | return option[this.settings.labelField]; 22 | }; 23 | 24 | this.onKeyDown = (function(e) { 25 | var original = self.onKeyDown; 26 | return function(e) { 27 | var index, option; 28 | if (e.keyCode === KEY_BACKSPACE && this.$control_input.val() === '' && !this.$activeItems.length) { 29 | index = this.caretPos - 1; 30 | if (index >= 0 && index < this.items.length) { 31 | option = this.options[this.items[index]]; 32 | if (this.deleteSelection(e)) { 33 | this.setTextboxValue(options.text.apply(this, [option])); 34 | this.refreshOptions(true); 35 | } 36 | e.preventDefault(); 37 | return; 38 | } 39 | } 40 | return original.apply(this, arguments); 41 | }; 42 | })(); 43 | }); -------------------------------------------------------------------------------- /src/contrib/microevent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * MicroEvent - to make any js object an event emitter 3 | * 4 | * - pure javascript - server compatible, browser compatible 5 | * - dont rely on the browser doms 6 | * - super simple - you get it immediatly, no mistery, no magic involved 7 | * 8 | * @author Jerome Etienne (https://github.com/jeromeetienne) 9 | */ 10 | 11 | var MicroEvent = function() {}; 12 | MicroEvent.prototype = { 13 | on: function(event, fct){ 14 | this._events = this._events || {}; 15 | this._events[event] = this._events[event] || []; 16 | this._events[event].push(fct); 17 | }, 18 | off: function(event, fct){ 19 | var n = arguments.length; 20 | if (n === 0) return delete this._events; 21 | if (n === 1) return delete this._events[event]; 22 | 23 | this._events = this._events || {}; 24 | if (event in this._events === false) return; 25 | this._events[event].splice(this._events[event].indexOf(fct), 1); 26 | }, 27 | trigger: function(event /* , args... */){ 28 | this._events = this._events || {}; 29 | if (event in this._events === false) return; 30 | for (var i = 0; i < this._events[event].length; i++){ 31 | this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)); 32 | } 33 | } 34 | }; 35 | 36 | /** 37 | * Mixin will delegate all MicroEvent.js function in the destination object. 38 | * 39 | * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent 40 | * 41 | * @param {object} the object which will support MicroEvent 42 | */ 43 | MicroEvent.mixin = function(destObject){ 44 | var props = ['on', 'off', 'trigger']; 45 | for (var i = 0; i < props.length; i++){ 46 | destObject.prototype[props[i]] = MicroEvent.prototype[props[i]]; 47 | } 48 | }; -------------------------------------------------------------------------------- /test/events_dom.js: -------------------------------------------------------------------------------- 1 | describe('DOM Events', function() { 2 | describe('"change"', function() { 3 | it('should be triggered once by addItem()', function(done) { 4 | var test = setup_test('', { 25 | valueField: 'value', 26 | labelField: 'value', 27 | options: [ 28 | {value: 'a'}, 29 | {value: 'b'}, 30 | ], 31 | items: ['a','b'] 32 | }); 33 | 34 | var counter = 0; 35 | test.$select.on('change', function() { counter++; }); 36 | test.selectize.removeItem('b'); 37 | 38 | window.setTimeout(function() { 39 | expect(counter).to.be.equal(1); 40 | done(); 41 | }, 0); 42 | }); 43 | it('should be triggered once by clear()', function(done) { 44 | var test = setup_test(' 28 | 29 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/required.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Selectize.js

22 | 23 |
24 |

Required Element

25 |
26 |
27 | 28 | 35 |
36 | 37 |
38 |
39 |
40 | 49 |
50 | 51 |
52 | 53 | -------------------------------------------------------------------------------- /src/defaults.js: -------------------------------------------------------------------------------- 1 | Selectize.count = 0; 2 | Selectize.defaults = { 3 | plugins: [], 4 | delimiter: ',', 5 | persist: true, 6 | diacritics: true, 7 | create: false, 8 | createOnBlur: false, 9 | highlight: true, 10 | openOnFocus: true, 11 | maxOptions: 1000, 12 | maxItems: null, 13 | hideSelected: null, 14 | addPrecedence: false, 15 | preload: false, 16 | 17 | scrollDuration: 60, 18 | loadThrottle: 300, 19 | 20 | dataAttr: 'data-data', 21 | optgroupField: 'optgroup', 22 | valueField: 'value', 23 | labelField: 'text', 24 | optgroupLabelField: 'label', 25 | optgroupValueField: 'value', 26 | optgroupOrder: null, 27 | 28 | sortField: '$order', 29 | searchField: ['text'], 30 | searchConjunction: 'and', 31 | 32 | mode: null, 33 | wrapperClass: 'selectize-control', 34 | inputClass: 'selectize-input', 35 | dropdownClass: 'selectize-dropdown', 36 | dropdownContentClass: 'selectize-dropdown-content', 37 | 38 | dropdownParent: null, 39 | 40 | /* 41 | load : null, // function(query, callback) { ... } 42 | score : null, // function(search) { ... } 43 | onInitialize : null, // function() { ... } 44 | onChange : null, // function(value) { ... } 45 | onItemAdd : null, // function(value, $item) { ... } 46 | onItemRemove : null, // function(value) { ... } 47 | onClear : null, // function() { ... } 48 | onOptionAdd : null, // function(value, data) { ... } 49 | onOptionRemove : null, // function(value) { ... } 50 | onOptionClear : null, // function() { ... } 51 | onDropdownOpen : null, // function($dropdown) { ... } 52 | onDropdownClose : null, // function($dropdown) { ... } 53 | onType : null, // function(str) { ... } 54 | onDelete : null, // function(values) { ... } 55 | */ 56 | 57 | render: { 58 | /* 59 | item: null, 60 | optgroup: null, 61 | optgroup_header: null, 62 | option: null, 63 | option_create: null 64 | */ 65 | } 66 | }; -------------------------------------------------------------------------------- /test/xss.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | window.setup_xss_test = function(html, options, done) { 4 | window.xss = function() { 5 | window.clearTimeout(timeout); 6 | complete(new Error('Exploit executed')); 7 | }; 8 | 9 | var test = setup_test(html, options); 10 | var complete = function(err) { 11 | window.xss = function() {}; 12 | done(err); 13 | }; 14 | var timeout = window.setTimeout(complete, 75); 15 | return test; 16 | }; 17 | 18 | describe('XSS', function() { 19 | 20 | describe('Raw HTML in original input value', function() { 21 | it('should not trigger exploit', function(done) { 22 | setup_xss_test('', {}, done); 23 | }); 24 | }); 25 | 26 | describe('Raw HTML in optgroup label', function() { 27 | it('should not trigger exploit', function(done) { 28 | var test = setup_xss_test('', {}, done); 29 | test.selectize.refreshOptions(); 30 | test.selectize.open(); 31 | }); 32 | }); 33 | 34 | describe('Raw HTML in option label should not trigger exploit', function() { 35 | it('should not trigger exploit', function(done) { 36 | setup_xss_test('', { 37 | options: [ 38 | {value: '1', label: ''} 39 | ], 40 | items: ['1'], 41 | labelField: 'label', 42 | valueField: 'value' 43 | }, done); 44 | }); 45 | }); 46 | 47 | describe('Raw HTML in option value should not trigger exploit', function() { 48 | it('should not trigger exploit', function(done) { 49 | setup_xss_test('', { 50 | options: [ 51 | {value: '', label: '1'} 52 | ], 53 | items: [''], 54 | labelField: 'label', 55 | valueField: 'value' 56 | }, done); 57 | }); 58 | }); 59 | }); 60 | 61 | })(); -------------------------------------------------------------------------------- /examples/performance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Selectize.js

22 |
23 |

Performance

24 |

This shows how it performs with 25,000 items.

25 |
26 | 27 | 28 |
29 | 56 |
57 |
58 | 59 | -------------------------------------------------------------------------------- /examples/rtl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Selectize.js

22 | 23 |
24 |

Right-to-left Support (RTL)

25 |
26 | 27 | 28 |
29 | 41 |
42 | 43 |
44 |

Right-to-left Support (RTL) – Single

45 |
46 | 47 | 54 |
55 | 58 |
59 | 60 |
61 | 62 | -------------------------------------------------------------------------------- /src/plugins/drag_drop/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin: "drag_drop" (selectize.js) 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | Selectize.define('drag_drop', function(options) { 18 | if (!$.fn.sortable) throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".'); 19 | if (this.settings.mode !== 'multi') return; 20 | var self = this; 21 | 22 | self.lock = (function() { 23 | var original = self.lock; 24 | return function() { 25 | var sortable = self.$control.data('sortable'); 26 | if (sortable) sortable.disable(); 27 | return original.apply(self, arguments); 28 | }; 29 | })(); 30 | 31 | self.unlock = (function() { 32 | var original = self.unlock; 33 | return function() { 34 | var sortable = self.$control.data('sortable'); 35 | if (sortable) sortable.enable(); 36 | return original.apply(self, arguments); 37 | }; 38 | })(); 39 | 40 | self.setup = (function() { 41 | var original = self.setup; 42 | return function() { 43 | original.apply(this, arguments); 44 | 45 | var $control = self.$control.sortable({ 46 | items: '[data-value]', 47 | forcePlaceholderSize: true, 48 | disabled: self.isLocked, 49 | start: function(e, ui) { 50 | ui.placeholder.css('width', ui.helper.css('width')); 51 | $control.css({overflow: 'visible'}); 52 | }, 53 | stop: function() { 54 | $control.css({overflow: 'hidden'}); 55 | var active = self.$activeItems ? self.$activeItems.slice() : null; 56 | var values = []; 57 | $control.children('[data-value]').each(function() { 58 | values.push($(this).attr('data-value')); 59 | }); 60 | self.setValue(values); 61 | self.setActiveItem(active); 62 | } 63 | }); 64 | }; 65 | })(); 66 | 67 | }); -------------------------------------------------------------------------------- /src/plugins/remove_button/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin: "remove_button" (selectize.js) 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | Selectize.define('remove_button', function(options) { 18 | if (this.settings.mode === 'single') return; 19 | 20 | options = $.extend({ 21 | label : '×', 22 | title : 'Remove', 23 | className : 'remove', 24 | append : true 25 | }, options); 26 | 27 | var self = this; 28 | var html = '' + options.label + ''; 29 | 30 | /** 31 | * Appends an element as a child (with raw HTML). 32 | * 33 | * @param {string} html_container 34 | * @param {string} html_element 35 | * @return {string} 36 | */ 37 | var append = function(html_container, html_element) { 38 | var pos = html_container.search(/(<\/[^>]+>\s*)$/); 39 | return html_container.substring(0, pos) + html_element + html_container.substring(pos); 40 | }; 41 | 42 | this.setup = (function() { 43 | var original = self.setup; 44 | return function() { 45 | // override the item rendering method to add the button to each 46 | if (options.append) { 47 | var render_item = self.settings.render.item; 48 | self.settings.render.item = function(data) { 49 | return append(render_item.apply(this, arguments), html); 50 | }; 51 | } 52 | 53 | original.apply(this, arguments); 54 | 55 | // add event listener 56 | this.$control.on('click', '.' + options.className, function(e) { 57 | e.preventDefault(); 58 | if (self.isLocked) return; 59 | 60 | var $item = $(e.target).parent(); 61 | self.setActiveItem($item); 62 | if (self.deleteSelection()) { 63 | self.setCaret(self.items.length); 64 | } 65 | }); 66 | 67 | }; 68 | })(); 69 | 70 | }); -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Selectize.js Tests 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /dist/less/selectize.default.less: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.default.css (v0.8.5) - Default Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @import "selectize"; 18 | 19 | @selectize-color-item: #1da7ee; 20 | @selectize-color-item-text: #fff; 21 | @selectize-color-item-active-text: #fff; 22 | @selectize-color-item-border: #0073bb; 23 | @selectize-color-item-active: #92c836; 24 | @selectize-color-item-active-border: #00578d; 25 | @selectize-width-item-border: 1px; 26 | @selectize-caret-margin: 0 1px; 27 | 28 | .selectize-control { 29 | &.multi { 30 | .selectize-input { 31 | &.has-items { 32 | @padding-x: @selectize-padding-x - 3px; 33 | padding-left: @padding-x; 34 | padding-right: @padding-x; 35 | } 36 | &.disabled [data-value] { 37 | color: #999; 38 | text-shadow: none; 39 | background: none; 40 | .selectize-box-shadow(none); 41 | 42 | &, .remove { 43 | border-color: #e6e6e6; 44 | } 45 | .remove { 46 | background: none; 47 | } 48 | } 49 | [data-value] { 50 | text-shadow: 0 1px 0 rgba(0,51,83,0.3); 51 | .selectize-border-radius(3px); 52 | .selectize-vertical-gradient(#1da7ee, #178ee9); 53 | .selectize-box-shadow(~"0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03)"); 54 | &.active { 55 | .selectize-vertical-gradient(#008fd8, #0075cf); 56 | } 57 | } 58 | } 59 | } 60 | &.single { 61 | .selectize-input { 62 | .selectize-box-shadow(~"0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8)"); 63 | .selectize-vertical-gradient(#fefefe, #f2f2f2); 64 | } 65 | } 66 | } 67 | 68 | .selectize-control.single .selectize-input, .selectize-dropdown.single { 69 | border-color: #b8b8b8; 70 | } 71 | 72 | .selectize-dropdown { 73 | .optgroup-header { 74 | padding-top: @selectize-padding-dropdown-item-y + 2px; 75 | font-weight: bold; 76 | font-size: 0.85em; 77 | } 78 | .optgroup { 79 | border-top: 1px solid @selectize-color-dropdown-border-top; 80 | &:first-child { 81 | border-top: 0 none; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/less/selectize.default.less: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.default.css (v@@version) - Default Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @import "selectize"; 18 | 19 | @selectize-color-item: #1da7ee; 20 | @selectize-color-item-text: #fff; 21 | @selectize-color-item-active-text: #fff; 22 | @selectize-color-item-border: #0073bb; 23 | @selectize-color-item-active: #92c836; 24 | @selectize-color-item-active-border: #00578d; 25 | @selectize-width-item-border: 1px; 26 | @selectize-caret-margin: 0 1px; 27 | 28 | .selectize-control { 29 | &.multi { 30 | .selectize-input { 31 | &.has-items { 32 | @padding-x: @selectize-padding-x - 3px; 33 | padding-left: @padding-x; 34 | padding-right: @padding-x; 35 | } 36 | &.disabled [data-value] { 37 | color: #999; 38 | text-shadow: none; 39 | background: none; 40 | .selectize-box-shadow(none); 41 | 42 | &, .remove { 43 | border-color: #e6e6e6; 44 | } 45 | .remove { 46 | background: none; 47 | } 48 | } 49 | [data-value] { 50 | text-shadow: 0 1px 0 rgba(0,51,83,0.3); 51 | .selectize-border-radius(3px); 52 | .selectize-vertical-gradient(#1da7ee, #178ee9); 53 | .selectize-box-shadow(~"0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03)"); 54 | &.active { 55 | .selectize-vertical-gradient(#008fd8, #0075cf); 56 | } 57 | } 58 | } 59 | } 60 | &.single { 61 | .selectize-input { 62 | .selectize-box-shadow(~"0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8)"); 63 | .selectize-vertical-gradient(#fefefe, #f2f2f2); 64 | } 65 | } 66 | } 67 | 68 | .selectize-control.single .selectize-input, .selectize-dropdown.single { 69 | border-color: #b8b8b8; 70 | } 71 | 72 | .selectize-dropdown { 73 | .optgroup-header { 74 | padding-top: @selectize-padding-dropdown-item-y + 2px; 75 | font-weight: bold; 76 | font-size: 0.85em; 77 | } 78 | .optgroup { 79 | border-top: 1px solid @selectize-color-dropdown-border-top; 80 | &:first-child { 81 | border-top: 0 none; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /dist/less/selectize.legacy.less: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.legacy.css (v0.8.5) - Default Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @import "selectize"; 18 | 19 | @selectize-font-size: 13px; 20 | @selectize-line-height: 20px; 21 | 22 | @selectize-color-input-full: #f2f2f2; 23 | @selectize-color-item: #b8e76f; 24 | @selectize-color-item-text: #3d5d18; 25 | @selectize-color-item-border: #74b21e; 26 | @selectize-color-item-active: #92c836; 27 | @selectize-color-item-active-border: #6f9839; 28 | @selectize-color-highlight: rgba(255,237,40,0.4); 29 | @selectize-color-dropdown-item-active: #fffceb; 30 | @selectize-color-dropdown-item-active-text: @selectize-color-text; 31 | @selectize-color-optgroup: #f8f8f8; 32 | @selectize-color-optgroup-text: @selectize-color-text; 33 | @selectize-width-item-border: 1px; 34 | 35 | @selectize-padding-x: 10px; 36 | @selectize-padding-y: 10px; 37 | @selectize-padding-item-x: 5px; 38 | @selectize-padding-item-y: 1px; 39 | @selectize-padding-dropdown-item-x: 10px; 40 | @selectize-padding-dropdown-item-y: 7px; 41 | @selectize-margin-item-x: 4px; 42 | @selectize-margin-item-y: 4px; 43 | 44 | .selectize-control { 45 | &.multi { 46 | .selectize-input [data-value] { 47 | text-shadow: 0 1px 0 rgba(255,255,255,0.1); 48 | .selectize-border-radius(3px); 49 | .selectize-vertical-gradient(#b8e76f, #a9e25c); 50 | .selectize-box-shadow(0 1px 1px rgba(0,0,0,0.1)); 51 | &.active { 52 | .selectize-vertical-gradient(#92c836, #7abc2c); 53 | } 54 | } 55 | } 56 | &.single { 57 | .selectize-input { 58 | .selectize-box-shadow(~"inset 0 1px 0 rgba(255,255,255,0.8), 0 2px 0 #e0e0e0, 0 3px 0 #c8c8c8, 0 4px 1px rgba(0,0,0,0.1)"); 59 | .selectize-vertical-gradient(#f5f5f5, #efefef); 60 | } 61 | } 62 | } 63 | 64 | .selectize-control.single .selectize-input, .selectize-dropdown.single { 65 | border-color: #b8b8b8; 66 | } 67 | 68 | .selectize-dropdown { 69 | .optgroup-header { 70 | font-weight: bold; 71 | font-size: 0.8em; 72 | border-bottom: 1px solid @selectize-color-dropdown-border-top; 73 | border-top: 1px solid @selectize-color-dropdown-border-top; 74 | } 75 | } -------------------------------------------------------------------------------- /src/less/selectize.legacy.less: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.legacy.css (v@@version) - Default Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @import "selectize"; 18 | 19 | @selectize-font-size: 13px; 20 | @selectize-line-height: 20px; 21 | 22 | @selectize-color-input-full: #f2f2f2; 23 | @selectize-color-item: #b8e76f; 24 | @selectize-color-item-text: #3d5d18; 25 | @selectize-color-item-border: #74b21e; 26 | @selectize-color-item-active: #92c836; 27 | @selectize-color-item-active-border: #6f9839; 28 | @selectize-color-highlight: rgba(255,237,40,0.4); 29 | @selectize-color-dropdown-item-active: #fffceb; 30 | @selectize-color-dropdown-item-active-text: @selectize-color-text; 31 | @selectize-color-optgroup: #f8f8f8; 32 | @selectize-color-optgroup-text: @selectize-color-text; 33 | @selectize-width-item-border: 1px; 34 | 35 | @selectize-padding-x: 10px; 36 | @selectize-padding-y: 10px; 37 | @selectize-padding-item-x: 5px; 38 | @selectize-padding-item-y: 1px; 39 | @selectize-padding-dropdown-item-x: 10px; 40 | @selectize-padding-dropdown-item-y: 7px; 41 | @selectize-margin-item-x: 4px; 42 | @selectize-margin-item-y: 4px; 43 | 44 | .selectize-control { 45 | &.multi { 46 | .selectize-input [data-value] { 47 | text-shadow: 0 1px 0 rgba(255,255,255,0.1); 48 | .selectize-border-radius(3px); 49 | .selectize-vertical-gradient(#b8e76f, #a9e25c); 50 | .selectize-box-shadow(0 1px 1px rgba(0,0,0,0.1)); 51 | &.active { 52 | .selectize-vertical-gradient(#92c836, #7abc2c); 53 | } 54 | } 55 | } 56 | &.single { 57 | .selectize-input { 58 | .selectize-box-shadow(~"inset 0 1px 0 rgba(255,255,255,0.8), 0 2px 0 #e0e0e0, 0 3px 0 #c8c8c8, 0 4px 1px rgba(0,0,0,0.1)"); 59 | .selectize-vertical-gradient(#f5f5f5, #efefef); 60 | } 61 | } 62 | } 63 | 64 | .selectize-control.single .selectize-input, .selectize-dropdown.single { 65 | border-color: #b8b8b8; 66 | } 67 | 68 | .selectize-dropdown { 69 | .optgroup-header { 70 | font-weight: bold; 71 | font-size: 0.8em; 72 | border-bottom: 1px solid @selectize-color-dropdown-border-top; 73 | border-top: 1px solid @selectize-color-dropdown-border-top; 74 | } 75 | } -------------------------------------------------------------------------------- /examples/lock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Selectize.js

22 |
23 |

Locking

24 |

Selectize controls can be locked to prevent user interaction.

25 |
26 | 27 | 32 |
33 |
34 | 35 | 40 |
41 |
42 | 43 | 48 |
49 |
50 | 51 | 56 |
57 | 63 |
64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /examples/customization.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 34 | 35 | 36 |
37 |

Selectize.js

38 |
39 |

Customizing Appearance

40 |

Render items on your own & apply unique CSS styles.

41 |
42 | 43 | 44 |
45 | 78 |

TODO: explain how to bind events.

79 |
80 |
81 | 82 | -------------------------------------------------------------------------------- /examples/css/stylesheet.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 70px 0; 3 | padding: 0; 4 | font-family: Helvetica, arial, sans-serif; 5 | font-size: 15px; 6 | color: #454545; 7 | background: #fff url(../images/bg.png); 8 | text-shadow: 0 1px 0 rgba(0,0,0,0.02); 9 | -webkit-font-smoothing: antialiased; 10 | } 11 | a, a:visited { 12 | color: #3fabff; 13 | text-decoration: none; 14 | } 15 | a:hover { 16 | color: #008af5; 17 | } 18 | h1 { 19 | margin: 0; 20 | font-weight: 300; 21 | font-size: 35px; 22 | letter-spacing: -1px; 23 | } 24 | h2 { 25 | font-size: 15px; 26 | color: #a0a0a0; 27 | margin: 30px 0; 28 | } 29 | label { 30 | display: block; 31 | font-weight: bold; 32 | margin-bottom: 10px; 33 | } 34 | p, .control-group { 35 | margin: 0 0 20px 0; 36 | } 37 | .demo { 38 | border-bottom: 1px solid #e8e8e8; 39 | padding-top: 50px; 40 | padding-bottom: 50px; 41 | -webkit-border-radius: 3px; 42 | -moz-border-radius: 3px; 43 | border-radius: 3px; 44 | } 45 | .demo:last-child { 46 | border-bottom: 0 none; 47 | } 48 | .demo select, .demo input, .demo .selectize-control { 49 | width: 100%; 50 | } 51 | .demo > *:first-child { 52 | margin-top: 0; 53 | } 54 | .demo > *:last-child { 55 | margin-bottom: 0; 56 | } 57 | .demo .value { 58 | margin: 0 0 10px 0; 59 | font-size: 12px; 60 | } 61 | .demo .value span { 62 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 63 | } 64 | .theme-selector { 65 | margin-top: 10px; 66 | font-size: 13px; 67 | } 68 | .theme-selector:before { 69 | content: 'Themes: '; 70 | } 71 | .theme-selector a { 72 | margin: 0 5px; 73 | } 74 | .theme-selector a.active { 75 | color: #202020; 76 | font-weight: bold; 77 | } 78 | #wrapper { 79 | margin: 0; 80 | } 81 | #wrapper > * { 82 | padding-left: 100px; 83 | padding-right: 100px; 84 | } 85 | pre { 86 | background: #f8f8f8; 87 | border: 1px solid #f2f2f2; 88 | padding: 10px; 89 | font-size: 12px; 90 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 91 | -webkit-border-radius: 3px; 92 | -moz-border-radius: 3px; 93 | border-radius: 3px; 94 | } 95 | input[type=button] { 96 | margin: 0 10px 0 0; 97 | padding: 6px 10px; 98 | color: #606060; 99 | background: #e0e0e0; 100 | border: 0 none; 101 | width: auto; 102 | display: inline-block; 103 | -webkit-border-radius: 3px; 104 | -moz-border-radius: 3px; 105 | border-radius: 3px; 106 | -webkit-font-smoothing: antialiased; 107 | } 108 | .buttons { 109 | margin: 0 0 25px 0; 110 | } 111 | input[type=button]:hover { 112 | background: #dadada; 113 | } 114 | 115 | @media only screen and (max-width : 320px) { 116 | body { 117 | margin: 20px 0; 118 | } 119 | #wrapper { 120 | margin: 0; 121 | } 122 | #wrapper > * { 123 | padding-left: 10px; 124 | padding-right: 10px; 125 | } 126 | .demo { 127 | padding: 20px; 128 | -webkit-border-radius: 0; 129 | -moz-border-radius: 0; 130 | border-radius: 0; 131 | } 132 | } -------------------------------------------------------------------------------- /examples/api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Selectize.js

22 |
23 |

API

24 |

Examples of how to interact with the control programmatically.

25 |
26 | 27 | 28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 |
36 | 78 |
79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/events.md: -------------------------------------------------------------------------------- 1 | ## Selectize API – Events 2 | 3 | In the [usage documentation](usage.md), a few callbacks are listed that 4 | allow you to listen for certain events. Callbacks aren't always ideal though; 5 | specifically when you wish to have multiple handlers. 6 | 7 | Selectize instances have a basic event emitter interface that mimics jQuery, Backbone.js, et al: 8 | 9 | ```js 10 | var handler = function() { /* ... */ }; 11 | selectize.on('event_name', handler); 12 | selectize.off('event_name'); 13 | selectize.off('event_name', handler); 14 | ``` 15 | 16 | ### List of Events 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
EventParamsDescription
"initialize"Invoked once the control is completely initialized.
"change"valueInvoked when the value of the control changes.
"item_add"value, $itemInvoked when an item is selected.
"item_remove"valueInvoked when an item is deselected.
"clear"Invoked when the control is manually cleared via the clear() method.
"option_add"value, dataInvoked when a new option is added to the available options list.
"option_remove"valueInvoked when an option is removed from the available options.
"dropdown_open"$dropdownInvoked when the dropdown opens.
"dropdown_close"$dropdownInvoked when the dropdown closes.
"type"strInvoked when the user types while filtering options.
"load"dataInvoked when new options have been loaded and added to the control (via the "load" option or "load" API method).
"destroy"Invoked right before the control is destroyed.
-------------------------------------------------------------------------------- /src/plugins/optgroup_columns/plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin: "optgroup_columns" (selectize.js) 3 | * Copyright (c) 2013 Simon Hewitt & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Simon Hewitt 15 | */ 16 | 17 | Selectize.define('optgroup_columns', function(options) { 18 | var self = this; 19 | 20 | options = $.extend({ 21 | equalizeWidth : true, 22 | equalizeHeight : true 23 | }, options); 24 | 25 | this.getAdjacentOption = function($option, direction) { 26 | var $options = $option.closest('[data-group]').find('[data-selectable]'); 27 | var index = $options.index($option) + direction; 28 | 29 | return index >= 0 && index < $options.length ? $options.eq(index) : $(); 30 | }; 31 | 32 | this.onKeyDown = (function() { 33 | var original = self.onKeyDown; 34 | return function(e) { 35 | var index, $option, $options, $optgroup; 36 | 37 | if (this.isOpen && (e.keyCode === KEY_LEFT || e.keyCode === KEY_RIGHT)) { 38 | self.ignoreHover = true; 39 | $optgroup = this.$activeOption.closest('[data-group]'); 40 | index = $optgroup.find('[data-selectable]').index(this.$activeOption); 41 | 42 | if(e.keyCode === KEY_LEFT) { 43 | $optgroup = $optgroup.prev('[data-group]'); 44 | } else { 45 | $optgroup = $optgroup.next('[data-group]'); 46 | } 47 | 48 | $options = $optgroup.find('[data-selectable]'); 49 | $option = $options.eq(Math.min($options.length - 1, index)); 50 | if ($option.length) { 51 | this.setActiveOption($option); 52 | } 53 | return; 54 | } 55 | 56 | return original.apply(this, arguments); 57 | }; 58 | })(); 59 | 60 | var equalizeSizes = function() { 61 | var i, n, height_max, width, width_last, width_parent, $optgroups; 62 | 63 | $optgroups = $('[data-group]', self.$dropdown_content); 64 | n = $optgroups.length; 65 | if (!n || !self.$dropdown_content.width()) return; 66 | 67 | if (options.equalizeHeight) { 68 | height_max = 0; 69 | for (i = 0; i < n; i++) { 70 | height_max = Math.max(height_max, $optgroups.eq(i).height()); 71 | } 72 | $optgroups.css({height: height_max}); 73 | } 74 | 75 | if (options.equalizeWidth) { 76 | width_parent = self.$dropdown_content.innerWidth(); 77 | width = Math.round(width_parent / n); 78 | $optgroups.css({width: width}); 79 | if (n > 1) { 80 | width_last = width_parent - width * (n - 1); 81 | $optgroups.eq(n - 1).css({width: width_last}); 82 | } 83 | } 84 | }; 85 | 86 | if (options.equalizeHeight || options.equalizeWidth) { 87 | hook.after(this, 'positionDropdown', equalizeSizes); 88 | hook.after(this, 'refreshOptions', equalizeSizes); 89 | } 90 | 91 | 92 | }); -------------------------------------------------------------------------------- /docs/plugins.md: -------------------------------------------------------------------------------- 1 | ## Selectize API – Plugins 2 | 3 | Via the [microplugin](https://github.com/brianreavis/microplugin.js) interface, 4 | features can be added to Selectize without modifying the main library. 5 | This is great because it protects against code bloat, allows for lean builds, 6 | and allows for addons to be sanely isolated. The plugin system isn't meant 7 | to be sexy; it's lean, makes very few assumptions, and gives the developer 8 | complete control. 9 | 10 | [**Example Plugins**](../src/plugins) 11 | 12 | **A few notes:** 13 | - All plugins live in their own folders in ["src/plugins"](../src/plugins). 14 | - Plugin names should be in follow the format: `/[a-z_]+$` 15 | - JS source should live in a "plugin.js" file (required). 16 | - CSS should live in a "plugin.less" file (optional). It will be bundled at build time. 17 | - Plugins are initialized right before the control is setup. 18 | This means that if you want to listen for events on any of the control's 19 | elements, you should override the `setup()` method (see ["DOM Events"](#dom-events)). 20 | 21 | ### Boilerplate 22 | 23 | ```js 24 | Selectize.define('plugin_name', function(options) { 25 | // options: plugin-specific options 26 | // this: selectize instance 27 | }); 28 | ``` 29 | 30 | #### Adding Dependencies 31 | 32 | ```js 33 | Selectize.define('plugin_name', function(options) { 34 | this.require('another_plugin'); 35 | }); 36 | ``` 37 | 38 | ## Overriding Methods 39 | 40 | Methods should be extended by [wrapping them](http://coreymaynard.com/blog/extending-a-javascript-function/): 41 | 42 | ```js 43 | var self = this; 44 | this.someMethod = function() { 45 | var original = self.someMethod; 46 | return function() { 47 | // do your logic 48 | return original.apply(this, arguments); 49 | }; 50 | }); 51 | ``` 52 | 53 | **Important:** If the method you're overriding returns a value, make sure the 54 | overridden function returns a value as well. 55 | 56 | ## DOM Events 57 | 58 | Because all elements for the control are created within the `setup()` method (which is 59 | invoked after the plugin initialized) events should be added by overriding the setup method, 60 | like so: 61 | 62 | ```js 63 | Selectize.define('plugin_name', function(options) { 64 | var self = this; 65 | 66 | // override the setup method to add an extra "click" handler 67 | this.setup = (function() { 68 | var original = self.setup; 69 | return function() { 70 | original.apply(this, arguments); 71 | this.$control.on('click', 'div', function(e) { 72 | alert('A div was clicked!'); 73 | }); 74 | }; 75 | })(); 76 | 77 | }); 78 | ``` 79 | 80 | ## Plugin Usage 81 | 82 | #### List (without options) 83 | 84 | ```js 85 | $('select').selectize({ 86 | plugins: ['plugin_a', 'plugin_b'] 87 | }); 88 | ``` 89 | 90 | #### List (with options) 91 | 92 | ```js 93 | $('select').selectize({ 94 | plugins: { 95 | 'plugin_a': { /* ... */ }, 96 | 'plugin_b': { /* ... */ } 97 | } 98 | }); 99 | ``` 100 | 101 | For a more detailed description of plugin option formats and how the plugin system works, check out the [microplugin](https://github.com/brianreavis/microplugin.js) documentation. -------------------------------------------------------------------------------- /examples/events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

Selectize.js Demos

22 |
23 |

Events

24 |

Check out the console for more details about each event.

25 |
26 | 27 | 81 |
82 |

Event Log

83 |

 84 | 				

Source

85 | 104 |
105 |
106 | 107 | 108 | -------------------------------------------------------------------------------- /src/selectize.jquery.js: -------------------------------------------------------------------------------- 1 | $.fn.selectize = function(settings_user) { 2 | var defaults = $.fn.selectize.defaults; 3 | var settings = $.extend({}, defaults, settings_user); 4 | var attr_data = settings.dataAttr; 5 | var field_label = settings.labelField; 6 | var field_value = settings.valueField; 7 | var field_optgroup = settings.optgroupField; 8 | var field_optgroup_label = settings.optgroupLabelField; 9 | var field_optgroup_value = settings.optgroupValueField; 10 | 11 | /** 12 | * Initializes selectize from a element. 13 | * 14 | * @param {object} $input 15 | * @param {object} settings_element 16 | */ 17 | var init_textbox = function($input, settings_element) { 18 | var i, n, values, option, value = $.trim($input.val() || ''); 19 | if (!value.length) return; 20 | 21 | values = value.split(settings.delimiter); 22 | for (i = 0, n = values.length; i < n; i++) { 23 | option = {}; 24 | option[field_label] = values[i]; 25 | option[field_value] = values[i]; 26 | 27 | settings_element.options[values[i]] = option; 28 | } 29 | 30 | settings_element.items = values; 31 | }; 32 | 33 | /** 34 | * Initializes selectize from a 29 | 30 |
31 | 32 | 33 |
34 | 55 | 56 | 57 |
58 |

Plugin: "restore_on_backspace"

59 |
60 | 61 | 62 |
63 | 76 |
77 | 78 |
79 |

Plugin: "drag_drop"

80 |
81 | 82 | 83 |
84 |
85 | 86 | 87 |
88 | 101 |
102 | 103 |
104 |

Plugin: "dropdown_header"

105 |
106 | 107 | 121 |
122 | 133 |
134 | 135 | 136 | -------------------------------------------------------------------------------- /dist/less/selectize.bootstrap3.less: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.bootstrap3.css (v0.8.5) - Bootstrap 3 Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @import "selectize"; 18 | 19 | @selectize-font-family: @font-family-base; 20 | @selectize-font-size: @font-size-base; 21 | @selectize-line-height: @line-height-computed; 22 | 23 | @selectize-color-text: @text-color; 24 | @selectize-color-highlight: rgba(255,237,40,0.4); 25 | @selectize-color-input: @input-bg; 26 | @selectize-color-input-full: @input-bg; 27 | @selectize-color-disabled: @input-bg; 28 | @selectize-color-item: #efefef; 29 | @selectize-color-item-border: rgba(0,0,0,0); 30 | @selectize-color-item-active: @component-active-bg; 31 | @selectize-color-item-active-text: #fff; 32 | @selectize-color-item-active-border: rgba(0,0,0,0); 33 | @selectize-color-optgroup: @dropdown-bg; 34 | @selectize-color-optgroup-text: @dropdown-header-color; 35 | @selectize-color-optgroup-border: @dropdown-divider-bg; 36 | @selectize-color-dropdown: @dropdown-bg; 37 | @selectize-color-dropdown-border-top: mix(@input-border, @input-bg, 0.8); 38 | @selectize-color-dropdown-item-active: @dropdown-link-hover-bg; 39 | @selectize-color-dropdown-item-active-text: @dropdown-link-hover-color; 40 | @selectize-color-dropdown-item-create-active-text: @dropdown-link-hover-color; 41 | @selectize-opacity-disabled: 0.5; 42 | @selectize-shadow-input: none; 43 | @selectize-shadow-input-focus: inset 0 1px 2px rgba(0,0,0,0.15); 44 | @selectize-border: 1px solid @input-border; 45 | @selectize-border-radius: @input-border-radius; 46 | 47 | @selectize-width-item-border: 0; 48 | @selectize-padding-x: @padding-base-horizontal; 49 | @selectize-padding-y: @padding-base-vertical; 50 | @selectize-padding-dropdown-item-x: @padding-base-horizontal; 51 | @selectize-padding-dropdown-item-y: 3px; 52 | @selectize-padding-item-x: 3px; 53 | @selectize-padding-item-y: 1px; 54 | @selectize-margin-item-x: 3px; 55 | @selectize-margin-item-y: 3px; 56 | @selectize-caret-margin: 0; 57 | 58 | @selectize-arrow-size: 5px; 59 | @selectize-arrow-color: @dropdown-caret-color; 60 | @selectize-arrow-offset: @selectize-padding-x + 5px; 61 | 62 | .selectize-dropdown, .selectize-dropdown.form-control { 63 | height: auto; 64 | padding: 0; 65 | margin: 2px 0 0 0; 66 | z-index: @zindex-dropdown; 67 | background: @selectize-color-dropdown; 68 | border: 1px solid @dropdown-fallback-border; 69 | border: 1px solid @dropdown-border; 70 | .selectize-border-radius(@border-radius-base); 71 | .selectize-box-shadow(0 6px 12px rgba(0,0,0,.175)); 72 | } 73 | 74 | .selectize-dropdown { 75 | .optgroup-header { 76 | font-size: @font-size-small; 77 | line-height: @line-height-base; 78 | } 79 | .optgroup:first-child:before { 80 | display: none; 81 | } 82 | .optgroup:before { 83 | content: ' '; 84 | display: block; 85 | .nav-divider(); 86 | margin-left: @selectize-padding-dropdown-item-x * -1; 87 | margin-right: @selectize-padding-dropdown-item-x * -1; 88 | } 89 | } 90 | 91 | .selectize-dropdown-content { 92 | padding: 5px 0; 93 | } 94 | 95 | .selectize-dropdown-header { 96 | padding: @selectize-padding-dropdown-item-y * 2 @selectize-padding-dropdown-item-x; 97 | } 98 | 99 | .selectize-input { 100 | min-height: @input-height-base; 101 | 102 | &.dropdown-active { 103 | .selectize-border-radius(@selectize-border-radius); 104 | } 105 | &.dropdown-active::before { 106 | display: none; 107 | } 108 | &.focus { 109 | @color: @input-border-focus; 110 | @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); 111 | border-color: @color; 112 | outline: 0; 113 | .selectize-box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); 114 | } 115 | } 116 | 117 | .selectize-control { 118 | &.multi { 119 | .selectize-input.has-items { 120 | padding-left: @selectize-padding-x - @selectize-padding-item-x; 121 | padding-right: @selectize-padding-x - @selectize-padding-item-x; 122 | } 123 | .selectize-input > div { 124 | .selectize-border-radius(@selectize-border-radius - 1px); 125 | } 126 | } 127 | } 128 | 129 | .form-control.selectize-control { 130 | padding: 0; 131 | height: auto; 132 | border: none; 133 | background: none; 134 | .selectize-box-shadow(none); 135 | .selectize-border-radius(0); 136 | } -------------------------------------------------------------------------------- /src/less/selectize.bootstrap3.less: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.bootstrap3.css (v@@version) - Bootstrap 3 Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @import "selectize"; 18 | 19 | @selectize-font-family: @font-family-base; 20 | @selectize-font-size: @font-size-base; 21 | @selectize-line-height: @line-height-computed; 22 | 23 | @selectize-color-text: @text-color; 24 | @selectize-color-highlight: rgba(255,237,40,0.4); 25 | @selectize-color-input: @input-bg; 26 | @selectize-color-input-full: @input-bg; 27 | @selectize-color-disabled: @input-bg; 28 | @selectize-color-item: #efefef; 29 | @selectize-color-item-border: rgba(0,0,0,0); 30 | @selectize-color-item-active: @component-active-bg; 31 | @selectize-color-item-active-text: #fff; 32 | @selectize-color-item-active-border: rgba(0,0,0,0); 33 | @selectize-color-optgroup: @dropdown-bg; 34 | @selectize-color-optgroup-text: @dropdown-header-color; 35 | @selectize-color-optgroup-border: @dropdown-divider-bg; 36 | @selectize-color-dropdown: @dropdown-bg; 37 | @selectize-color-dropdown-border-top: mix(@input-border, @input-bg, 0.8); 38 | @selectize-color-dropdown-item-active: @dropdown-link-hover-bg; 39 | @selectize-color-dropdown-item-active-text: @dropdown-link-hover-color; 40 | @selectize-color-dropdown-item-create-active-text: @dropdown-link-hover-color; 41 | @selectize-opacity-disabled: 0.5; 42 | @selectize-shadow-input: none; 43 | @selectize-shadow-input-focus: inset 0 1px 2px rgba(0,0,0,0.15); 44 | @selectize-border: 1px solid @input-border; 45 | @selectize-border-radius: @input-border-radius; 46 | 47 | @selectize-width-item-border: 0; 48 | @selectize-padding-x: @padding-base-horizontal; 49 | @selectize-padding-y: @padding-base-vertical; 50 | @selectize-padding-dropdown-item-x: @padding-base-horizontal; 51 | @selectize-padding-dropdown-item-y: 3px; 52 | @selectize-padding-item-x: 3px; 53 | @selectize-padding-item-y: 1px; 54 | @selectize-margin-item-x: 3px; 55 | @selectize-margin-item-y: 3px; 56 | @selectize-caret-margin: 0; 57 | 58 | @selectize-arrow-size: 5px; 59 | @selectize-arrow-color: @dropdown-caret-color; 60 | @selectize-arrow-offset: @selectize-padding-x + 5px; 61 | 62 | .selectize-dropdown, .selectize-dropdown.form-control { 63 | height: auto; 64 | padding: 0; 65 | margin: 2px 0 0 0; 66 | z-index: @zindex-dropdown; 67 | background: @selectize-color-dropdown; 68 | border: 1px solid @dropdown-fallback-border; 69 | border: 1px solid @dropdown-border; 70 | .selectize-border-radius(@border-radius-base); 71 | .selectize-box-shadow(0 6px 12px rgba(0,0,0,.175)); 72 | } 73 | 74 | .selectize-dropdown { 75 | .optgroup-header { 76 | font-size: @font-size-small; 77 | line-height: @line-height-base; 78 | } 79 | .optgroup:first-child:before { 80 | display: none; 81 | } 82 | .optgroup:before { 83 | content: ' '; 84 | display: block; 85 | .nav-divider(); 86 | margin-left: @selectize-padding-dropdown-item-x * -1; 87 | margin-right: @selectize-padding-dropdown-item-x * -1; 88 | } 89 | } 90 | 91 | .selectize-dropdown-content { 92 | padding: 5px 0; 93 | } 94 | 95 | .selectize-dropdown-header { 96 | padding: @selectize-padding-dropdown-item-y * 2 @selectize-padding-dropdown-item-x; 97 | } 98 | 99 | .selectize-input { 100 | min-height: @input-height-base; 101 | 102 | &.dropdown-active { 103 | .selectize-border-radius(@selectize-border-radius); 104 | } 105 | &.dropdown-active::before { 106 | display: none; 107 | } 108 | &.focus { 109 | @color: @input-border-focus; 110 | @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); 111 | border-color: @color; 112 | outline: 0; 113 | .selectize-box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); 114 | } 115 | } 116 | 117 | .selectize-control { 118 | &.multi { 119 | .selectize-input.has-items { 120 | padding-left: @selectize-padding-x - @selectize-padding-item-x; 121 | padding-right: @selectize-padding-x - @selectize-padding-item-x; 122 | } 123 | .selectize-input > div { 124 | .selectize-border-radius(@selectize-border-radius - 1px); 125 | } 126 | } 127 | } 128 | 129 | .form-control.selectize-control { 130 | padding: 0; 131 | height: auto; 132 | border: none; 133 | background: none; 134 | .selectize-box-shadow(none); 135 | .selectize-border-radius(0); 136 | } -------------------------------------------------------------------------------- /test/vendor/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, #mocha li { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | #mocha ul { 18 | list-style: none; 19 | } 20 | 21 | #mocha h1, #mocha h2 { 22 | margin: 0; 23 | } 24 | 25 | #mocha h1 { 26 | margin-top: 15px; 27 | font-size: 1em; 28 | font-weight: 200; 29 | } 30 | 31 | #mocha h1 a { 32 | text-decoration: none; 33 | color: inherit; 34 | } 35 | 36 | #mocha h1 a:hover { 37 | text-decoration: underline; 38 | } 39 | 40 | #mocha .suite .suite h1 { 41 | margin-top: 0; 42 | font-size: .8em; 43 | } 44 | 45 | #mocha .hidden { 46 | display: none; 47 | } 48 | 49 | #mocha h2 { 50 | font-size: 12px; 51 | font-weight: normal; 52 | cursor: pointer; 53 | } 54 | 55 | #mocha .suite { 56 | margin-left: 15px; 57 | } 58 | 59 | #mocha .test { 60 | margin-left: 15px; 61 | overflow: hidden; 62 | } 63 | 64 | #mocha .test.pending:hover h2::after { 65 | content: '(pending)'; 66 | font-family: arial, sans-serif; 67 | } 68 | 69 | #mocha .test.pass.medium .duration { 70 | background: #C09853; 71 | } 72 | 73 | #mocha .test.pass.slow .duration { 74 | background: #B94A48; 75 | } 76 | 77 | #mocha .test.pass::before { 78 | content: '✓'; 79 | font-size: 12px; 80 | display: block; 81 | float: left; 82 | margin-right: 5px; 83 | color: #00d6b2; 84 | } 85 | 86 | #mocha .test.pass .duration { 87 | font-size: 9px; 88 | margin-left: 5px; 89 | padding: 2px 5px; 90 | color: white; 91 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 92 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 93 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -webkit-border-radius: 5px; 95 | -moz-border-radius: 5px; 96 | -ms-border-radius: 5px; 97 | -o-border-radius: 5px; 98 | border-radius: 5px; 99 | } 100 | 101 | #mocha .test.pass.fast .duration { 102 | display: none; 103 | } 104 | 105 | #mocha .test.pending { 106 | color: #0b97c4; 107 | } 108 | 109 | #mocha .test.pending::before { 110 | content: '◦'; 111 | color: #0b97c4; 112 | } 113 | 114 | #mocha .test.fail { 115 | color: #c00; 116 | } 117 | 118 | #mocha .test.fail pre { 119 | color: black; 120 | } 121 | 122 | #mocha .test.fail::before { 123 | content: '✖'; 124 | font-size: 12px; 125 | display: block; 126 | float: left; 127 | margin-right: 5px; 128 | color: #c00; 129 | } 130 | 131 | #mocha .test pre.error { 132 | color: #c00; 133 | max-height: 300px; 134 | overflow: auto; 135 | } 136 | 137 | #mocha .test pre { 138 | display: block; 139 | float: left; 140 | clear: left; 141 | font: 12px/1.5 monaco, monospace; 142 | margin: 5px; 143 | padding: 15px; 144 | border: 1px solid #eee; 145 | border-bottom-color: #ddd; 146 | -webkit-border-radius: 3px; 147 | -webkit-box-shadow: 0 1px 3px #eee; 148 | -moz-border-radius: 3px; 149 | -moz-box-shadow: 0 1px 3px #eee; 150 | } 151 | 152 | #mocha .test h2 { 153 | position: relative; 154 | } 155 | 156 | #mocha .test a.replay { 157 | position: absolute; 158 | top: 3px; 159 | right: 0; 160 | text-decoration: none; 161 | vertical-align: middle; 162 | display: block; 163 | width: 15px; 164 | height: 15px; 165 | line-height: 15px; 166 | text-align: center; 167 | background: #eee; 168 | font-size: 15px; 169 | -moz-border-radius: 15px; 170 | border-radius: 15px; 171 | -webkit-transition: opacity 200ms; 172 | -moz-transition: opacity 200ms; 173 | transition: opacity 200ms; 174 | opacity: 0.3; 175 | color: #888; 176 | } 177 | 178 | #mocha .test:hover a.replay { 179 | opacity: 1; 180 | } 181 | 182 | #mocha-report.pass .test.fail { 183 | display: none; 184 | } 185 | 186 | #mocha-report.fail .test.pass { 187 | display: none; 188 | } 189 | 190 | #mocha-error { 191 | color: #c00; 192 | font-size: 1.5em; 193 | font-weight: 100; 194 | letter-spacing: 1px; 195 | } 196 | 197 | #mocha-stats { 198 | position: fixed; 199 | top: 15px; 200 | right: 10px; 201 | font-size: 12px; 202 | margin: 0; 203 | color: #888; 204 | z-index: 1; 205 | } 206 | 207 | #mocha-stats .progress { 208 | float: right; 209 | padding-top: 0; 210 | } 211 | 212 | #mocha-stats em { 213 | color: black; 214 | } 215 | 216 | #mocha-stats a { 217 | text-decoration: none; 218 | color: inherit; 219 | } 220 | 221 | #mocha-stats a:hover { 222 | border-bottom: 1px solid #eee; 223 | } 224 | 225 | #mocha-stats li { 226 | display: inline-block; 227 | margin: 0 5px; 228 | list-style: none; 229 | padding-top: 11px; 230 | } 231 | 232 | #mocha-stats canvas { 233 | width: 40px; 234 | height: 40px; 235 | } 236 | 237 | #mocha code .comment { color: #ddd } 238 | #mocha code .init { color: #2F6FAD } 239 | #mocha code .string { color: #5890AD } 240 | #mocha code .keyword { color: #8A6343 } 241 | #mocha code .number { color: #2F6FAD } 242 | 243 | @media screen and (max-device-width: 480px) { 244 | #mocha { 245 | margin: 60px 0px; 246 | } 247 | 248 | #mocha #stats { 249 | position: absolute; 250 | } 251 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # selectize.js 2 | [![Build Status](https://travis-ci.org/brianreavis/selectize.js.png?branch=master)](https://travis-ci.org/brianreavis/selectize.js) 3 | 4 | Selectize is an extensible jQuery-based custom <select> UI control. It's useful for tagging, contact lists, country selectors, and so on. It clocks in at around ~7kb (gzipped). The goal is to provide a solid & usable experience with a clean and powerful API. 5 | 6 | - [Demos](http://brianreavis.github.io/selectize.js/) 7 | - [Changelog](https://github.com/brianreavis/selectize.js/releases) 8 | - [Examples](examples/) 9 | - [Usage Documentation](docs/usage.md) 10 | - [API Documentation](docs/api.md) 11 | - [Plugin Documentation](docs/plugins.md) 12 | 13 | ### Features 14 | 15 | - **Smart Option Searching / Ranking**
Options are efficiently scored and sorted on-the-fly (using [sifter](https://github.com/brianreavis/sifter.js)). Want to search an item's title *and* description? No problem. 16 | - **Caret between items**
Order matters sometimes. Use the and arrow keys to move between selected items. 17 | - **Select & delete multiple items at once**
Hold down option on Mac or ctrl on Windows to select more than one item to delete. 18 | - **Díåcritîçs supported**
Great for international environments. 19 | - **Item creation**
Allow users to create items on the fly (async saving is supported; the control locks until the callback is fired). 20 | - **Remote data loading**
For when you have thousands of options and want them provided by the server as the user types. 21 | - **Clean API & code**
Interface with it and make modifications easily. Pull requests welcome! 22 | - **Extensible**
[Plugin API](docs/plugins.md) for developing custom features (uses [microplugin](https://github.com/brianreavis/microplugin.js)). 23 | - **Touch Support**
Plays nice with iOS 5+ devices. 24 | 25 | ### Dependencies 26 | 27 | - [jquery](https://github.com/jquery/jquery) (1.7 and greater) 28 | - [sifter](https://github.com/brianreavis/sifter.js) (bundled in ["standalone" build](dist/js/standalone)) 29 | - [microplugin](https://github.com/brianreavis/microplugin.js) (bundled in ["standalone" build](dist/js/standalone)) 30 | 31 | ### Files 32 | 33 | All pre-built files needed to use Selectize can be found in the ["dist"](dist/) folder. 34 | 35 | - [**js/**](dist/js) 36 | - [**standalone/**](dist/js/standalone) 37 | - [selectize.js](dist/js/standalone/selectize.js) — With dependencies, minus jquery 38 | - [selectize.js](dist/js/selectize.js) — Without dependencies 39 | - [**less/**](dist/less) 40 | - [selectize.less](dist/less/selectize.less) — Core styles 41 | - [selectize.default.less](dist/less/selectize.default.less) — Default theme 42 | - [selectize.bootstrap2.less](dist/less/selectize.bootstrap2.less) — Bootstrap 2 theme 43 | - [selectize.bootstrap3.less](dist/less/selectize.bootstrap3.less) — Bootstrap 3 theme 44 | - [**plugins/**](dist/less/plugins) — Individual plugin styles 45 | - [**css/**](dist/css) 46 | - [selectize.css](dist/css/selectize.css) — Core styles 47 | - [selectize.default.css](dist/css/selectize.default.css) — Default theme (with core styles) 48 | 49 | ### Usage 50 | 51 | ```js 52 | $('select').selectize(options); 53 | ``` 54 | 55 | The available options are [documented here](docs/usage.md). 56 | 57 | #### IE8 Support 58 | 59 | To support Internet Explorer 8, [es5-shim](https://github.com/kriskowal/es5-shim/) must be added your page. 60 | 61 | ```html 62 | 63 | ``` 64 | 65 | ### Custom Builds 66 | 67 | By default, all [plugins](src/plugins) are included. To hand-pick what plugins (if any) to include, run [`grunt`](http://gruntjs.com/) with the "--plugins" flag. After this completes, grab the files you need from the ["dist"](dist) folder. 68 | 69 | ```sh 70 | # dependencies 71 | npm install -g grunt-cli 72 | npm install -g bower 73 | npm install 74 | 75 | # build selectize 76 | grunt --plugins= 77 | grunt --plugins=* 78 | grunt --plugins=remove_button,restore_on_backspace 79 | ``` 80 | 81 | ### Contributing 82 | 83 | First build your copy then try out the [bundled examples](examples/). 84 | 85 | To use the automated test runner, either open ["tests/index.html"](tests/index.html) in a browser, or run `make test`. The latter requires [node.js](http://nodejs.org/) and [testem](https://github.com/airportyh/testem) to be installed (`npm install -g testem`). 86 | 87 | When issuing a pull request, please exclude changes in the "dist" folder to avoid merge conflicts. 88 | 89 | ## License 90 | 91 | Copyright © 2013 [Brian Reavis](http://twitter.com/brianreavis) & [Contributors](https://github.com/brianreavis/selectize.js/graphs/contributors) 92 | 93 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 94 | 95 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 96 | -------------------------------------------------------------------------------- /examples/github.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 94 | 95 | 96 |
97 |

Selectize.js

98 |
99 |

Loading + Custom Scoring

100 |

This demo shows how to integrate third-party data and override the scoring method.

101 |
102 | 103 | 104 |
105 | 151 |
152 |
153 | 154 | -------------------------------------------------------------------------------- /examples/movies.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 103 | 104 | 105 |
106 |

Selectize.js

107 |
108 |

Loading from API

109 |

This demo shows how to integrate third-party data, loaded asynchronously.

110 |
111 | 112 | 113 |
114 | 162 |
163 |
164 | 165 | -------------------------------------------------------------------------------- /examples/cities.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 38 | 39 | 40 |
41 |

Selectize.js

42 |
43 |

State / City Selection

44 |

A demonstration showing how to use the API to cascade controls for a classic state / city selector.

45 |

Note: The API for fetching cities is a little spotty, so if it fails to list cities, that's the problem.

46 |
47 | 48 | 102 | 103 | 104 |
105 | 142 |
143 |
144 | 145 | -------------------------------------------------------------------------------- /test/interaction.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // These tests are functional simulations of 4 | // user interaction, using syn.js. For more information: 5 | // 6 | // @see http://v3.javascriptmvc.com/docs.html#&who=Syn 7 | // @see http://bitovi.com/blog/2010/07/syn-a-standalone-synthetic-event-library.html 8 | 9 | describe('Interaction', function() { 10 | 11 | describe('clicking control', function() { 12 | 13 | it('should give it focus', function(done) { 14 | var test = setup_test('', {}); 18 | 19 | Syn 20 | .click(test.selectize.$control) 21 | .delay(0, function() { 22 | expect(test.selectize.isFocused).to.be.equal(true); 23 | done(); 24 | }); 25 | }); 26 | 27 | it('should open dropdown menu', function(done) { 28 | var test = setup_test('', {}); 32 | 33 | Syn 34 | .click(test.selectize.$control) 35 | .delay(0, function() { 36 | expect(test.selectize.isOpen).to.be.equal(true); 37 | expect(test.selectize.$dropdown.is(':visible')).to.be.equal(true); 38 | done(); 39 | }); 40 | }); 41 | 42 | }); 43 | 44 | describe('clicking option', function() { 45 | 46 | it('should select it', function(done) { 47 | var test = setup_test('', {}); 52 | 53 | Syn.click(test.selectize.$control).delay(0, function() { 54 | Syn 55 | .click($('[data-value="b"]', test.selectize.$dropdown)) 56 | .delay(0, function() { 57 | expect(test.selectize.$input.val()).to.be.equal('b'); 58 | done(); 59 | }); 60 | }); 61 | }); 62 | 63 | it('should close dropdown', function(done) { 64 | var test = setup_test('', {}); 69 | 70 | Syn.click(test.selectize.$control).delay(0, function() { 71 | Syn 72 | .click($('[data-value="b"]', test.selectize.$dropdown)) 73 | .delay(0, function() { 74 | expect(test.selectize.isOpen).to.be.equal(false); 75 | expect(test.selectize.$dropdown.is(':visible')).to.be.equal(false); 76 | done(); 77 | }); 78 | }); 79 | }); 80 | 81 | }); 82 | 83 | describe('typing in input', function() { 84 | 85 | it('should filter results', function(done) { 86 | var test = setup_test('', {}); 91 | 92 | Syn 93 | .click(test.selectize.$control) 94 | .type('a', test.selectize.$control_input) 95 | .delay(0, function() { 96 | expect($('[data-value="a"]', test.selectize.$dropdown).length).to.be.equal(1); 97 | expect($('[data-value="b"]', test.selectize.$dropdown).length).to.be.equal(0); 98 | done(); 99 | }); 100 | }); 101 | 102 | it('should hide dropdown if no results present', function(done) { 103 | var test = setup_test('', {}); 108 | 109 | Syn 110 | .click(test.selectize.$control) 111 | .type('awaw', test.selectize.$control_input) 112 | .delay(0, function() { 113 | expect(test.selectize.isOpen).to.be.equal(false); 114 | expect(test.selectize.$dropdown.is(':visible')).to.be.equal(false); 115 | done(); 116 | }); 117 | }); 118 | 119 | it('should not hide dropdown if "create" option enabled and no results present', function(done) { 120 | var test = setup_test('', {create: true}); 125 | 126 | Syn 127 | .click(test.selectize.$control) 128 | .type('awaw', test.selectize.$control_input) 129 | .delay(0, function() { 130 | expect(test.selectize.isOpen).to.be.equal(true); 131 | expect(test.selectize.$dropdown.is(':visible')).to.be.equal(true); 132 | done(); 133 | }); 134 | }); 135 | 136 | it('should restore dropdown visibility when backing out of a query without results (backspace)', function(done) { 137 | var test = setup_test('', {}); 142 | 143 | Syn 144 | .click(test.selectize.$control) 145 | .type('awf', test.selectize.$control_input) 146 | .type('\b\b\b', test.selectize.$control_input) 147 | .delay(0, function() { 148 | expect(test.selectize.isOpen).to.be.equal(true); 149 | expect(test.selectize.$dropdown.is(':visible')).to.be.equal(true); 150 | done(); 151 | }); 152 | }); 153 | 154 | it('should move caret when [left] or [right] pressed', function(done) { 155 | var test = setup_test('', {create: true}); 156 | 157 | Syn 158 | .click(test.selectize.$control) 159 | .type('[left][left]whatt', test.selectize.$control_input) 160 | .delay(0, function() { 161 | expect(test.selectize.caretPos).to.be.equal(2); 162 | done(); 163 | }); 164 | }); 165 | 166 | }); 167 | 168 | }); 169 | 170 | })(); -------------------------------------------------------------------------------- /dist/less/selectize.bootstrap2.less: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.bootstrap2.css (v0.8.5) - Bootstrap 2 Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @import "selectize"; 18 | 19 | @selectize-font-family: @baseFontFamily; 20 | @selectize-font-size: @baseFontSize; 21 | @selectize-line-height: @baseLineHeight; 22 | 23 | @selectize-color-text: @textColor; 24 | @selectize-color-highlight: rgba(255,237,40,0.4); 25 | @selectize-color-input: @inputBackground; 26 | @selectize-color-input-full: @inputBackground; 27 | @selectize-color-disabled: @inputBackground; 28 | @selectize-color-item: @btnBackgroundHighlight; 29 | @selectize-color-item-border: @btnBorder; 30 | @selectize-color-item-active: @dropdownLinkBackgroundHover; 31 | @selectize-color-item-active-text: @dropdownLinkColorHover; 32 | @selectize-color-item-active-border: darken(@selectize-color-item-active, 5%); 33 | @selectize-color-optgroup: @dropdownBackground; 34 | @selectize-color-optgroup-text: @grayLight; 35 | @selectize-color-optgroup-border: @dropdownDividerTop; 36 | @selectize-color-dropdown: @dropdownBackground; 37 | @selectize-color-dropdown-border: @inputBorder; 38 | @selectize-color-dropdown-border-top: @dropdownDividerTop; 39 | @selectize-color-dropdown-item-active: @dropdownLinkBackgroundHover; 40 | @selectize-color-dropdown-item-active-text: @dropdownLinkColorHover; 41 | @selectize-color-dropdown-item-create-active-text: @dropdownLinkColorHover; 42 | @selectize-lighten-disabled-item: 8%; 43 | @selectize-lighten-disabled-item-text: 8%; 44 | @selectize-lighten-disabled-item-border: 8%; 45 | @selectize-opacity-disabled: 0.5; 46 | @selectize-shadow-input: none; 47 | @selectize-shadow-input-focus: inset 0 1px 2px rgba(0,0,0,0.15); 48 | @selectize-border-radius: @inputBorderRadius; 49 | 50 | @selectize-padding-x: 10px; 51 | @selectize-padding-y: 7px; 52 | @selectize-padding-dropdown-item-x: @selectize-padding-x; 53 | @selectize-padding-dropdown-item-y: 3px; 54 | @selectize-padding-item-x: 3px; 55 | @selectize-padding-item-y: 1px; 56 | @selectize-margin-item-x: 3px; 57 | @selectize-margin-item-y: 3px; 58 | @selectize-caret-margin: 0; 59 | 60 | @selectize-arrow-size: 5px; 61 | @selectize-arrow-color: @black; 62 | @selectize-arrow-offset: @selectize-padding-x + 5px; 63 | 64 | @selectize-width-item-border: 1px; 65 | 66 | .selectize-dropdown { 67 | margin: 2px 0 0 0; 68 | z-index: @zindexDropdown; 69 | border: 1px solid @dropdownBorder; 70 | border-radius: @baseBorderRadius; 71 | .box-shadow(0 5px 10px rgba(0,0,0,.2)); 72 | 73 | .optgroup-header { 74 | font-size: 11px; 75 | font-weight: bold; 76 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 77 | text-transform: uppercase; 78 | } 79 | .optgroup:first-child:before { 80 | display: none; 81 | } 82 | .optgroup:before { 83 | content: ' '; 84 | display: block; 85 | .nav-divider(); 86 | margin-left: @selectize-padding-dropdown-item-x * -1; 87 | margin-right: @selectize-padding-dropdown-item-x * -1; 88 | } 89 | 90 | [data-selectable].active { 91 | #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); 92 | } 93 | } 94 | 95 | .selectize-dropdown-content { 96 | padding: 5px 0; 97 | } 98 | 99 | .selectize-dropdown-header { 100 | padding: @selectize-padding-dropdown-item-y * 2 @selectize-padding-dropdown-item-x; 101 | } 102 | 103 | .selectize-input { 104 | .transition(~"border linear .2s, box-shadow linear .2s"); 105 | 106 | &.dropdown-active { 107 | .selectize-border-radius(@selectize-border-radius); 108 | } 109 | &.dropdown-active::before { 110 | display: none; 111 | } 112 | &.input-active, &.input-active:hover, .selectize-control.multi &.focus { 113 | background: @selectize-color-input !important; 114 | border-color: rgba(82,168,236,.8) !important; 115 | outline: 0 !important; 116 | outline: thin dotted \9 !important; 117 | .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)") !important; 118 | } 119 | } 120 | 121 | .selectize-control { 122 | &.single { 123 | .selectize-input { 124 | .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); 125 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); 126 | &:hover { 127 | color: @grayDark; 128 | text-decoration: none; 129 | background-position: 0 -15px; 130 | .transition(background-position .1s linear); 131 | } 132 | &.disabled { 133 | background: @btnBackgroundHighlight !important; 134 | .box-shadow(none); 135 | } 136 | } 137 | } 138 | &.multi { 139 | .selectize-input { 140 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); 141 | &.has-items { 142 | @padding-x: @selectize-padding-x - @selectize-padding-item-x; 143 | padding-left: @padding-x; 144 | padding-right: @padding-x; 145 | } 146 | } 147 | .selectize-input > div { 148 | .gradientBar(@btnBackground, @btnBackgroundHighlight, @selectize-color-item-text, none); 149 | *background-color: @selectize-color-item; 150 | border: @selectize-width-item-border solid @selectize-color-item-border; 151 | .border-radius(@baseBorderRadius); 152 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); 153 | &.active { 154 | .box-shadow(~"0 1px 2px rgba(0,0,0,.05)"); 155 | .gradientBar(@selectize-color-item-active, @selectize-color-item-active-border, @selectize-color-item-active-text, none); 156 | *background-color: @selectize-color-item-active; 157 | border: @selectize-width-item-border solid @dropdownLinkBackgroundHover; 158 | } 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /src/less/selectize.bootstrap2.less: -------------------------------------------------------------------------------- 1 | /** 2 | * selectize.bootstrap2.css (v@@version) - Bootstrap 2 Theme 3 | * Copyright (c) 2013 Brian Reavis & contributors 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 6 | * file except in compliance with the License. You may obtain a copy of the License at: 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under 10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 11 | * ANY KIND, either express or implied. See the License for the specific language 12 | * governing permissions and limitations under the License. 13 | * 14 | * @author Brian Reavis 15 | */ 16 | 17 | @import "selectize"; 18 | 19 | @selectize-font-family: @baseFontFamily; 20 | @selectize-font-size: @baseFontSize; 21 | @selectize-line-height: @baseLineHeight; 22 | 23 | @selectize-color-text: @textColor; 24 | @selectize-color-highlight: rgba(255,237,40,0.4); 25 | @selectize-color-input: @inputBackground; 26 | @selectize-color-input-full: @inputBackground; 27 | @selectize-color-disabled: @inputBackground; 28 | @selectize-color-item: @btnBackgroundHighlight; 29 | @selectize-color-item-border: @btnBorder; 30 | @selectize-color-item-active: @dropdownLinkBackgroundHover; 31 | @selectize-color-item-active-text: @dropdownLinkColorHover; 32 | @selectize-color-item-active-border: darken(@selectize-color-item-active, 5%); 33 | @selectize-color-optgroup: @dropdownBackground; 34 | @selectize-color-optgroup-text: @grayLight; 35 | @selectize-color-optgroup-border: @dropdownDividerTop; 36 | @selectize-color-dropdown: @dropdownBackground; 37 | @selectize-color-dropdown-border: @inputBorder; 38 | @selectize-color-dropdown-border-top: @dropdownDividerTop; 39 | @selectize-color-dropdown-item-active: @dropdownLinkBackgroundHover; 40 | @selectize-color-dropdown-item-active-text: @dropdownLinkColorHover; 41 | @selectize-color-dropdown-item-create-active-text: @dropdownLinkColorHover; 42 | @selectize-lighten-disabled-item: 8%; 43 | @selectize-lighten-disabled-item-text: 8%; 44 | @selectize-lighten-disabled-item-border: 8%; 45 | @selectize-opacity-disabled: 0.5; 46 | @selectize-shadow-input: none; 47 | @selectize-shadow-input-focus: inset 0 1px 2px rgba(0,0,0,0.15); 48 | @selectize-border-radius: @inputBorderRadius; 49 | 50 | @selectize-padding-x: 10px; 51 | @selectize-padding-y: 7px; 52 | @selectize-padding-dropdown-item-x: @selectize-padding-x; 53 | @selectize-padding-dropdown-item-y: 3px; 54 | @selectize-padding-item-x: 3px; 55 | @selectize-padding-item-y: 1px; 56 | @selectize-margin-item-x: 3px; 57 | @selectize-margin-item-y: 3px; 58 | @selectize-caret-margin: 0; 59 | 60 | @selectize-arrow-size: 5px; 61 | @selectize-arrow-color: @black; 62 | @selectize-arrow-offset: @selectize-padding-x + 5px; 63 | 64 | @selectize-width-item-border: 1px; 65 | 66 | .selectize-dropdown { 67 | margin: 2px 0 0 0; 68 | z-index: @zindexDropdown; 69 | border: 1px solid @dropdownBorder; 70 | border-radius: @baseBorderRadius; 71 | .box-shadow(0 5px 10px rgba(0,0,0,.2)); 72 | 73 | .optgroup-header { 74 | font-size: 11px; 75 | font-weight: bold; 76 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 77 | text-transform: uppercase; 78 | } 79 | .optgroup:first-child:before { 80 | display: none; 81 | } 82 | .optgroup:before { 83 | content: ' '; 84 | display: block; 85 | .nav-divider(); 86 | margin-left: @selectize-padding-dropdown-item-x * -1; 87 | margin-right: @selectize-padding-dropdown-item-x * -1; 88 | } 89 | 90 | [data-selectable].active { 91 | #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); 92 | } 93 | } 94 | 95 | .selectize-dropdown-content { 96 | padding: 5px 0; 97 | } 98 | 99 | .selectize-dropdown-header { 100 | padding: @selectize-padding-dropdown-item-y * 2 @selectize-padding-dropdown-item-x; 101 | } 102 | 103 | .selectize-input { 104 | .transition(~"border linear .2s, box-shadow linear .2s"); 105 | 106 | &.dropdown-active { 107 | .selectize-border-radius(@selectize-border-radius); 108 | } 109 | &.dropdown-active::before { 110 | display: none; 111 | } 112 | &.input-active, &.input-active:hover, .selectize-control.multi &.focus { 113 | background: @selectize-color-input !important; 114 | border-color: rgba(82,168,236,.8) !important; 115 | outline: 0 !important; 116 | outline: thin dotted \9 !important; 117 | .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)") !important; 118 | } 119 | } 120 | 121 | .selectize-control { 122 | &.single { 123 | .selectize-input { 124 | .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); 125 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); 126 | &:hover { 127 | color: @grayDark; 128 | text-decoration: none; 129 | background-position: 0 -15px; 130 | .transition(background-position .1s linear); 131 | } 132 | &.disabled { 133 | background: @btnBackgroundHighlight !important; 134 | .box-shadow(none); 135 | } 136 | } 137 | } 138 | &.multi { 139 | .selectize-input { 140 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); 141 | &.has-items { 142 | @padding-x: @selectize-padding-x - @selectize-padding-item-x; 143 | padding-left: @padding-x; 144 | padding-right: @padding-x; 145 | } 146 | } 147 | .selectize-input > div { 148 | .gradientBar(@btnBackground, @btnBackgroundHighlight, @selectize-color-item-text, none); 149 | *background-color: @selectize-color-item; 150 | border: @selectize-width-item-border solid @selectize-color-item-border; 151 | .border-radius(@baseBorderRadius); 152 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); 153 | &.active { 154 | .box-shadow(~"0 1px 2px rgba(0,0,0,.05)"); 155 | .gradientBar(@selectize-color-item-active, @selectize-color-item-active-border, @selectize-color-item-active-text, none); 156 | *background-color: @selectize-color-item-active; 157 | border: @selectize-width-item-border solid @dropdownLinkBackgroundHover; 158 | } 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /examples/contacts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 71 | 72 | 73 |
74 |

Selectize.js

75 |
76 |

Email Contacts

77 |

An example showing how you might go about creating contact selector like those used in Email apps.

78 |
79 | 80 | 81 |
82 | 147 |
148 |
149 | 150 | -------------------------------------------------------------------------------- /examples/optgroups.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Selectize.js Demo 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 31 | 32 | 33 |
34 |

Selectize.js

35 | 36 |
37 |

Optgroups (basic)

38 |
39 | 40 | 56 |
57 | 62 |
63 | 64 |
65 |

Optgroups (repeated options)

66 |
67 | 68 | 79 |
80 | 85 |
86 | 87 |
88 |

Optgroups (programmatic)

89 |
90 | 91 | 92 |
93 | 124 |
125 | 126 |
127 |

Plugin: "optgroup_columns"

128 |
129 | 130 |
131 | 171 |
172 |
173 | 174 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | module.exports = function(grunt) { 4 | grunt.loadNpmTasks('grunt-bower-cli'); 5 | grunt.loadNpmTasks('grunt-contrib-uglify'); 6 | grunt.loadNpmTasks('grunt-contrib-concat'); 7 | grunt.loadNpmTasks('grunt-contrib-clean'); 8 | grunt.loadNpmTasks('grunt-contrib-copy'); 9 | grunt.loadNpmTasks('grunt-replace'); 10 | grunt.loadNpmTasks('grunt-recess'); 11 | 12 | grunt.registerTask('configure', [ 13 | 'clean:pre', 14 | 'bower:install', 15 | ]); 16 | 17 | grunt.registerTask('compile', [ 18 | 'copy:less', 19 | 'copy:less_plugins', 20 | 'concat:less_theme_dependencies', 21 | 'concat:less_plugins', 22 | 'concat:js', 23 | 'recess', 24 | 'clean_bootstrap2_css', 25 | 'replace', 26 | 'build_standalone', 27 | 'uglify', 28 | 'clean:post', 29 | ]); 30 | 31 | grunt.registerTask('default', [ 32 | 'configure', 33 | 'compile' 34 | ]); 35 | 36 | grunt.registerTask('clean_bootstrap2_css', 'Cleans CSS rules ocurring before the header comment.', function() { 37 | var file = 'dist/css/selectize.bootstrap2.css'; 38 | var source = fs.readFileSync(file, 'utf8'); 39 | grunt.file.write(file, source.replace(/^(.|\s)+?\/\*/m, '/*')); 40 | grunt.log.writeln('Cleaned "' + file + '".'); 41 | }); 42 | 43 | grunt.registerTask('build_standalone', '', function() { 44 | var files, i, n, source, name, path, modules = []; 45 | 46 | // amd definitions must be changed to be not anonymous 47 | // @see https://github.com/brianreavis/selectize.js/issues/89 48 | files = []; 49 | for (i = 0, n = files_js_dependencies.length; i < n; i++) { 50 | path = files_js_dependencies[i]; 51 | name = path.match(/([^\/]+?).js$/)[1]; 52 | source = grunt.file.read(path).replace('define(factory);', 'define(\'' + name + '\', factory);'); 53 | modules.push(source); 54 | } 55 | 56 | path = 'dist/js/selectize.js'; 57 | source = grunt.file.read(path).replace(/define\((.*?)factory\);/, 'define(\'selectize\', $1factory);'); 58 | modules.push(source); 59 | 60 | // write output 61 | path = 'dist/js/standalone/selectize.js'; 62 | grunt.file.write(path, modules.join('\n\n')); 63 | grunt.log.writeln('Built "' + path + '".'); 64 | }); 65 | 66 | var files_js = [ 67 | 'src/contrib/*.js', 68 | 'src/*.js', 69 | '!src/.wrapper.js', 70 | '!src/defaults.js', 71 | '!src/selectize.js', 72 | '!src/selectize.jquery.js', 73 | 'src/selectize.js', 74 | 'src/defaults.js', 75 | 'src/selectize.jquery.js', 76 | ]; 77 | 78 | var files_js_dependencies = [ 79 | 'bower_components/sifter/sifter.js', 80 | 'bower_components/microplugin/src/microplugin.js', 81 | ]; 82 | 83 | var less_imports = []; 84 | var less_plugin_files = []; 85 | 86 | // enumerate plugins 87 | (function() { 88 | var selector_plugins = grunt.option('plugins'); 89 | if (!selector_plugins) return; 90 | 91 | if (selector_plugins.indexOf(',') !== -1) { 92 | selector_plugins = '{' + plugins.split(/\s*,\s*/).join(',') + '}'; 93 | } 94 | 95 | // javascript 96 | files_js.push('src/plugins/' + selector_plugins + '/*.js'); 97 | 98 | // less (css) 99 | var matched_files = grunt.file.expand(['src/plugins/' + selector_plugins + '/plugin.less']); 100 | for (var i = 0, n = matched_files.length; i < n; i++) { 101 | var plugin_name = matched_files[i].match(/src\/plugins\/(.+?)\//)[1]; 102 | less_imports.push('@import "plugins/' + plugin_name + '";'); 103 | less_plugin_files.push({src: matched_files[i], dest: 'dist/less/plugins/' + plugin_name + '.less'}); 104 | } 105 | })(); 106 | 107 | grunt.initConfig({ 108 | pkg: grunt.file.readJSON('bower.json'), 109 | bower: { 110 | install: { 111 | options: { 112 | directory: 'bower_components', 113 | action: 'install' 114 | } 115 | } 116 | }, 117 | clean: { 118 | pre: ['dist'], 119 | post: ['**/*.tmp*'] 120 | }, 121 | copy: { 122 | less: { 123 | files: [{expand: true, flatten: true, src: ['src/less/*.less'], dest: 'dist/less'}] 124 | }, 125 | less_plugins: { 126 | files: less_plugin_files 127 | } 128 | }, 129 | replace: { 130 | options: {prefix: '@@'}, 131 | main: { 132 | options: { 133 | variables: { 134 | 'version': '<%= pkg.version %>', 135 | 'js': '<%= grunt.file.read("dist/js/selectize.js").replace(/\\n/g, "\\n\\t") %>', 136 | 'css': '<%= grunt.file.read("dist/css/selectize.css") %>', 137 | }, 138 | }, 139 | files: [ 140 | {src: ['src/.wrapper.js'], dest: 'dist/js/selectize.js'}, 141 | {src: ['src/less/.wrapper.css'], dest: 'dist/css/selectize.css'} 142 | ] 143 | }, 144 | css_post: { 145 | options: { 146 | variables: { 147 | 'version': '<%= pkg.version %>' 148 | }, 149 | }, 150 | files: [ 151 | {expand: true, flatten: false, src: ['dist/css/*.css'], dest: ''}, 152 | {expand: true, flatten: false, src: ['dist/less/*.less'], dest: ''}, 153 | {expand: true, flatten: false, src: ['dist/less/plugins/*.less'], dest: ''}, 154 | ] 155 | } 156 | }, 157 | recess: { 158 | options: { 159 | compile: true 160 | }, 161 | uncompressed: { 162 | files: { 163 | 'dist/css/selectize.css': ['dist/less/selectize.less'], 164 | 'dist/css/selectize.default.css': ['dist/less/selectize.default.less'], 165 | 'dist/css/selectize.legacy.css': ['dist/less/selectize.legacy.less'], 166 | 'dist/css/selectize.bootstrap2.css': ['dist/less/selectize.bootstrap2.tmp.less'], 167 | 'dist/css/selectize.bootstrap3.css': ['dist/less/selectize.bootstrap3.tmp.less'] 168 | } 169 | } 170 | }, 171 | concat: { 172 | options: { 173 | stripBanners: true, 174 | separator: grunt.util.linefeed + grunt.util.linefeed 175 | }, 176 | js: { 177 | files: { 178 | 'dist/js/selectize.js': files_js, 179 | } 180 | }, 181 | less_plugins: { 182 | options: { 183 | banner: less_imports.join('\n') + grunt.util.linefeed + grunt.util.linefeed 184 | }, 185 | files: { 186 | 'dist/less/selectize.less': ['dist/less/selectize.less'] 187 | } 188 | }, 189 | less_theme_dependencies: { 190 | options: {stripBanners: false}, 191 | files: { 192 | 'dist/less/selectize.bootstrap2.tmp.less': [ 193 | 'bower_components/bootstrap2/less/variables.less', 194 | 'bower_components/bootstrap2/less/mixins.less', 195 | 'dist/less/selectize.bootstrap2.less' 196 | ], 197 | 'dist/less/selectize.bootstrap3.tmp.less': [ 198 | 'bower_components/bootstrap3/less/variables.less', 199 | 'bower_components/bootstrap3/less/mixins.less', 200 | 'dist/less/selectize.bootstrap3.less' 201 | ] 202 | } 203 | } 204 | }, 205 | uglify: { 206 | main: { 207 | options: { 208 | 'banner': '/*! selectize.js - v<%= pkg.version %> | https://github.com/brianreavis/selectize.js | Apache License (v2) */\n', 209 | 'report': 'gzip', 210 | 'ascii-only': true 211 | }, 212 | files: { 213 | 'dist/js/selectize.min.js': ['dist/js/selectize.js'], 214 | 'dist/js/standalone/selectize.min.js': ['dist/js/standalone/selectize.js'] 215 | } 216 | } 217 | } 218 | }); 219 | }; -------------------------------------------------------------------------------- /examples/js/es5.js: -------------------------------------------------------------------------------- 1 | (function(o){"function"==typeof define?define(o):"function"==typeof YUI?YUI.add("es5",o):o()})(function(){function o(){}function v(a){a=+a;a!==a?a=0:0!==a&&(a!==1/0&&a!==-(1/0))&&(a=(0>>0;if(h(a)!="[object Function]")throw new TypeError;for(;++e>>0,f=Array(e);if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var g=0;g>>0,f=[],g;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var i=0;i>>0;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var f=0;f>>0;if(h(a)!="[object Function]")throw new TypeError(a+" is not a function");for(var f=0;f>>0;if(h(a)!="[object Function]")throw new TypeError(a+ 7 | " is not a function");if(!c&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var e=0,f;if(arguments.length>=2)f=arguments[1];else{do{if(e in d){f=d[e++];break}if(++e>=c)throw new TypeError("reduce of empty array with no initial value");}while(1)}for(;e>>0;if(h(a)!= 8 | "[object Function]")throw new TypeError(a+" is not a function");if(!c&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var e,c=c-1;if(arguments.length>=2)e=arguments[1];else{do{if(c in d){e=d[c--];break}if(--c<0)throw new TypeError("reduceRight of empty array with no initial value");}while(1)}do c in this&&(e=a.call(void 0,e,d[c],c,b));while(c--);return e});if(!Array.prototype.indexOf||-1!=[0,1].indexOf(1,2))Array.prototype.indexOf=function(a){var b=l&& 9 | h(this)=="[object String]"?this.split(""):n(this),d=b.length>>>0;if(!d)return-1;var c=0;arguments.length>1&&(c=v(arguments[1]));for(c=c>=0?c:Math.max(0,d+c);c>>0;if(!d)return-1;var c=d-1;arguments.length>1&&(c=Math.min(c,v(arguments[1])));for(c=c>=0?c:d-Math.abs(c);c>=0;c--)if(c in b&& 10 | a===b[c])return c;return-1};if(!Object.keys){var w=!0,x="toString toLocaleString valueOf hasOwnProperty isPrototypeOf propertyIsEnumerable constructor".split(" "),A=x.length,r;for(r in{toString:null})w=!1;Object.keys=function(a){if(typeof a!="object"&&typeof a!="function"||a===null)throw new TypeError("Object.keys called on a non-object");var b=[],d;for(d in a)t(a,d)&&b.push(d);if(w)for(d=0;d9999?"+":"")+("00000"+Math.abs(c)).slice(0<=c&&c<=9999?-4:-6);for(b=a.length;b--;){d=a[b];d<10&&(a[b]="0"+d)}return c+"-"+a.slice(0,2).join("-")+"T"+a.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+ 12 | "Z"};r=!1;try{r=Date.prototype.toJSON&&null===(new Date(NaN)).toJSON()&&-1!==(new Date(-621987552E5)).toJSON().indexOf("-000001")&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(H){}r||(Date.prototype.toJSON=function(){var a=Object(this),b;a:if(s(a))b=a;else{b=a.valueOf;if(typeof b==="function"){b=b.call(a);if(s(b))break a}b=a.toString;if(typeof b==="function"){b=b.call(a);if(s(b))break a}throw new TypeError;}if(typeof b==="number"&&!isFinite(b))return null;b=a.toISOString; 13 | if(typeof b!="function")throw new TypeError("toISOString property is not callable");return b.call(a)});var g=Date,m=function(a,b,d,c,e,f,h){var i=arguments.length;if(this instanceof g){i=i==1&&String(a)===a?new g(m.parse(a)):i>=7?new g(a,b,d,c,e,f,h):i>=6?new g(a,b,d,c,e,f):i>=5?new g(a,b,d,c,e):i>=4?new g(a,b,d,c):i>=3?new g(a,b,d):i>=2?new g(a,b):i>=1?new g(a):new g;i.constructor=m;return i}return g.apply(this,arguments)},u=function(a,b){var d=b>1?1:0;return B[b]+Math.floor((a-1969+d)/4)-Math.floor((a- 14 | 1901+d)/100)+Math.floor((a-1601+d)/400)+365*(a-1970)},C=RegExp("^(\\d{4}|[+-]\\d{6})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{3}))?)?(Z|(?:([-+])(\\d{2}):(\\d{2})))?)?)?)?$"),B=[0,31,59,90,120,151,181,212,243,273,304,334,365],j;for(j in g)m[j]=g[j];m.now=g.now;m.UTC=g.UTC;m.prototype=g.prototype;m.prototype.constructor=m;m.parse=function(a){var b=C.exec(a);if(b){var d=Number(b[1]),c=Number(b[2]||1)-1,e=Number(b[3]||1)-1,f=Number(b[4]||0),h=Number(b[5]||0),i=Number(b[6]|| 15 | 0),j=Number(b[7]||0),m=!b[4]||b[8]?0:Number(new g(1970,0)),k=b[9]==="-"?1:-1,l=Number(b[10]||0),b=Number(b[11]||0);if(f<(h>0||i>0||j>0?24:25)&&h<60&&i<60&&j<1E3&&c>-1&&c<12&&l<24&&b<60&&e>-1&&e', {}); 9 | instance_before = test.$select[0].selectize; 10 | test.$select.selectize(); 11 | instance_after = test.$select[0].selectize; 12 | 13 | expect(instance_before).to.be.equal(instance_after); 14 | }); 15 | 16 | describe('', function() { 17 | it('complete without exceptions', function() { 18 | var test = setup_test('', {}); 19 | }); 20 | describe('getValue()', function() { 21 | it('should return value as a string', function() { 22 | var test = setup_test('', {delimiter: ','}); 23 | expect(test.selectize.getValue()).to.be.a('string'); 24 | }); 25 | it('should return "" when empty', function() { 26 | var test = setup_test('', {delimiter: ','}); 27 | expect(test.selectize.getValue()).to.be.equal(''); 28 | }); 29 | it('should proper value when not empty', function() { 30 | var test = setup_test('', {delimiter: ','}); 31 | expect(test.selectize.getValue()).to.be.equal('a,b'); 32 | }); 33 | }); 34 | }); 35 | 36 | describe('', {}); 39 | }); 40 | it('should add options in text form (no html entities)', function() { 41 | var test = setup_test('', {}); 42 | expect(test.selectize.options['a'].text).to.be.equal(''); 43 | }); 44 | it('should keep options in original order if no sort given', function(done) { 45 | var test = setup_test([ 46 | '' 102 | ].join(), {}); 103 | 104 | var order_expected = ['AL','AK','AZ','AR','CO','CT','DE','DC','FL','GA','HI','ID','IL','IN','IA','KS','KY','LA','ME','MD','MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ','NM','NY','NC','ND','OH','OK','OR','PA','RI','SC','SD','TN','TX','UT','VT','VA','WA','WV','WI','01','10']; 105 | var order_actual = []; 106 | 107 | test.selectize.refreshOptions(true); 108 | window.setTimeout(function() { 109 | test.selectize.$dropdown.find('[data-value]').each(function(i, el) { 110 | order_actual.push($(el).attr('data-value')); 111 | }); 112 | 113 | expect(order_actual).to.eql(order_expected); 114 | done(); 115 | }, 0); 116 | }); 117 | describe('getValue()', function() { 118 | it('should return "" when empty', function() { 119 | var test = setup_test('', {}); 124 | expect(test.selectize.getValue()).to.be.equal('a'); 125 | }); 126 | }); 127 | }); 128 | 129 | describe('', {}); 132 | }); 133 | describe('getValue()', function() { 134 | it('should return [] when empty', function() { 135 | var test = setup_test('', {}); 140 | expect(test.selectize.getValue()).to.deep.equal(['a']); 141 | }); 142 | }); 143 | }); 144 | 145 | describe('', {}); 150 | }); 151 | it('should have "disabled" class', function() { 152 | expect(test.selectize.$control.hasClass('disabled')).to.be.equal(true); 153 | }); 154 | it('should have isDisabled property set to true', function() { 155 | expect(test.selectize.isDisabled).to.be.equal(true); 156 | }); 157 | }); 158 | 159 | describe('' + 164 | '' + 165 | '' + 166 | '', {}); 167 | $form = test.$select.parents('form'); 168 | $button = $('