selectize.js-0.12.4/000077500000000000000000000000001277751564300142215ustar00rootroot00000000000000selectize.js-0.12.4/.github/000077500000000000000000000000001277751564300155615ustar00rootroot00000000000000selectize.js-0.12.4/.github/ISSUE_TEMPLATE.md000066400000000000000000000027341277751564300202740ustar00rootroot00000000000000Welcome to the Selectize bug tracker. Thank you for taking the time to make Selectize a better library! Please keep in mind this repository has an important issue backlog and that maintainers have limited time to fix issues, triage, and understand them. If you've never done so, please read the [guide to reporting issues](https://github.com/selectize/selectize.js/wiki/Reporting-issues-guide) in the Wiki to help us better understand your issue. Place an `x` in the checklist steps (`[ ]` becomes `[x]`) to demonstrate you have done/verified all the steps you needed to do. Thank you for reading this! You can now erase everything up to the following dashes, and then complete what's after. ISSUES THAT EITHER: * IGNORE THE ISSUE GUIDELINES * ERASE THE FOLLOWING TEMPLATE * DON'T FOLLOW THE PROPER NUMBERED FORMAT FOR STEPS TO REPRODUCE COULD BE CLOSED. --- I did: * [ ] Search for if my issue has already been submitted * [ ] Make sure I'm reporting something precise that needs to be fixed * [ ] Give my issue a descriptive and concise title * [ ] Create a *minimal* working example on JsFiddle or Codepen (or gave a link to a demo on the Selectize docs) * [ ] Indicate *precise* steps to reproduce in *numbers* and the result, like below [replace me with a short description of issue] Steps to reproduce: 1. 2. 3. Expected result: Actual result: [you can add explanations here of the context/what you were trying to do, and if you have findings about what the cause might be] selectize.js-0.12.4/.gitignore000066400000000000000000000000621277751564300162070ustar00rootroot00000000000000.DS_Store .DAV node_modules bower_components *.logselectize.js-0.12.4/.travis.yml000066400000000000000000000022401277751564300163300ustar00rootroot00000000000000language: node_js env: matrix: - TARGET=phantomjs - TARGET=saucelabs global: - secure: df9eTd6JkMOgdF3J3UInb0ouaDEbF4Y0sklmvwgBUs38yRvGQ3mucrzH13dCU0GYW8mM/H9euF7yG4/leWQHoUYdRJTDRy/sWXGvs6qg7DTkUXsmGyzv/5XxS86WTD5B9LbzB1hvRs5nzWeA1sFP6rpzGG6HwogM/+37ijRjKQQ= - secure: FI6ATM3Pngx0bowuYi5WHIDCe0R5ORlJWNsFxJyZLEusie+wZrRy9QbZsVvJGVYZJKAR9MFT3Ks494ou85lyfUnfvEnn+lX2DQ3Hsyz0xyBNoSvNkQbhZEsUr0TV+UGpCvSwYr6lYqXsBy6VxqyAkXDkqdVTAzMydq5ttGxszPU= - secure: "ctyTk+LdMsch1jTRLLTboJW2TsJCIRtXktEQ86JdGteg0YSvsqDj/LndUaaGZ2ZSR3sSJ1Q/YV1X0M0VizrzX5I7/3WnKViyeKygJ5sSbTuqkjwFa2sAAmrnmBv9JWofIc2XuRG3tfZv2fX6QMDy5PA72KGPBmGAw4S8WTxGkHQ=" matrix: allow_failures: - env: TARGET=saucelabs node_js: - '0.10' before_script: - npm install -g bower - npm install -g grunt-cli - npm install - make script: - export COMMIT_MESSAGE=$(git show -s --format=%B $TRAVIS_COMMIT | tr -d '\n') - export TRAVIS_CI=1 - if [[ "${TARGET}" == "phantomjs" ]]; then npm test || exit 1; fi - if [[ "${TARGET}" == "saucelabs" ]]; then (for x in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do export SAUCELABS_BATCH=$x; echo "Starting batch $x of 15"; npm test || true; done); fi selectize.js-0.12.4/CHANGELOG.md000066400000000000000000000026141277751564300160350ustar00rootroot00000000000000 ## v0.12.3 · 24 August 2016 * Make `label[for]` work after applying Selectize (#755) *Barrett Sonntag* (@barretts) * Output friendly error message when Microplguin is missing (#1137). Special thanks to @styxxx for proposing the improvement. * Add local server command `grunt server`. * Stop creating items automatically when text is pasted, only create them when pasted text contains delimiter. * Fix regression 'Required fields can not be focusable' in Chrome (#733) * Fix detection of Validity API, we had false negatives before. *Jonathan Allard* (@joallard) * Fix open keyboard bug under iOS after closing selection (#1127) *@zeitiger* * Fix highlighting more than one character (#1099, #1098) *@skimi* ## v0.12.2 · 23 June 2016 * Fix issue preventing build ("Cannot assign to read only property 'subarray'") because of bug in uglifyjs. (#1072) *@jaridmargolin* * Fix tabbing issue (#877) on IE11. (#997) *@bwilson-ux* * Fix jQuery initialization for jQuery >= 1.9 (#1045) *@mpokrywka* * Make `remove_button` work for single-option usage (#848) *@ChoppyThing* * Fixed bug that made `allowEmptyOption: true` useless (#739) *@mcavalletto* * Functions in option `render` can now return a DOM node in addition to text. (#617) *@topaxi* selectize.js-0.12.4/Gruntfile.js000066400000000000000000000144321277751564300165220ustar00rootroot00000000000000var fs = require('fs'); module.exports = function(grunt) { grunt.loadNpmTasks('grunt-bower-task'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-less'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-replace'); grunt.registerTask('configure', [ 'clean:pre', 'bower:install', ]); grunt.registerTask('compile', [ 'copy:less', 'copy:less_plugins', 'concat:less_theme_dependencies', 'concat:less_plugins', 'concat:js', 'less:uncompressed', 'clean_bootstrap2_css', 'replace', 'build_standalone', 'uglify', 'clean:post', ]); grunt.registerTask('default', [ 'configure', 'compile' ]); grunt.registerTask('serve', [ 'connect', 'watch' ]) grunt.registerTask('clean_bootstrap2_css', 'Cleans CSS rules ocurring before the header comment.', function() { var file = 'dist/css/selectize.bootstrap2.css'; var source = fs.readFileSync(file, 'utf8'); grunt.file.write(file, source.replace(/^(.|\s)+?\/\*/m, '/*')); grunt.log.writeln('Cleaned "' + file + '".'); }); grunt.registerTask('build_standalone', '', function() { var files, i, n, source, name, path, modules = []; // amd definitions must be changed to be not anonymous // @see https://github.com/brianreavis/selectize.js/issues/89 files = []; for (i = 0, n = files_js_dependencies.length; i < n; i++) { path = files_js_dependencies[i]; name = path.match(/([^\/]+?).js$/)[1]; source = grunt.file.read(path).replace('define(factory);', 'define(\'' + name + '\', factory);'); modules.push(source); } path = 'dist/js/selectize.js'; source = grunt.file.read(path).replace(/define\((.*?)factory\);/, 'define(\'selectize\', $1factory);'); modules.push(source); // write output path = 'dist/js/standalone/selectize.js'; grunt.file.write(path, modules.join('\n\n')); grunt.log.writeln('Built "' + path + '".'); }); var files_js = [ 'src/contrib/*.js', 'src/*.js', '!src/.wrapper.js', '!src/defaults.js', '!src/selectize.js', '!src/selectize.jquery.js', 'src/selectize.js', 'src/defaults.js', 'src/selectize.jquery.js', ]; var files_js_dependencies = [ 'bower_components/sifter/sifter.js', 'bower_components/microplugin/src/microplugin.js', ]; var less_imports = []; var less_plugin_files = []; // enumerate plugins (function() { var selector_plugins = grunt.option('plugins'); if (!selector_plugins) return; if (selector_plugins.indexOf(',') !== -1) { selector_plugins = '{' + selector_plugins.split(/\s*,\s*/).join(',') + '}'; } // javascript files_js.push('src/plugins/' + selector_plugins + '/*.js'); // less (css) var matched_files = grunt.file.expand(['src/plugins/' + selector_plugins + '/plugin.less']); for (var i = 0, n = matched_files.length; i < n; i++) { var plugin_name = matched_files[i].match(/src\/plugins\/(.+?)\//)[1]; less_imports.push('@import "plugins/' + plugin_name + '";'); less_plugin_files.push({src: matched_files[i], dest: 'dist/less/plugins/' + plugin_name + '.less'}); } })(); grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), bower: { install: { options: { copy: false, clean: false, layout: 'byComponent', action: 'install' } } }, clean: { pre: ['dist'], post: ['**/*.tmp*'] }, copy: { less: { files: [{expand: true, flatten: true, src: ['src/less/*.less'], dest: 'dist/less'}] }, less_plugins: { files: less_plugin_files } }, replace: { options: {prefix: '@@'}, main: { options: { variables: { 'version': '<%= pkg.version %>', 'js': '<%= grunt.file.read("dist/js/selectize.js").replace(/\\n/g, "\\n\\t") %>', 'css': '<%= grunt.file.read("dist/css/selectize.css") %>', }, }, files: [ {src: ['src/.wrapper.js'], dest: 'dist/js/selectize.js'}, {src: ['src/less/.wrapper.css'], dest: 'dist/css/selectize.css'} ] }, css_post: { options: { variables: { 'version': '<%= pkg.version %>' }, }, files: [ {expand: true, flatten: false, src: ['dist/css/*.css'], dest: ''}, {expand: true, flatten: false, src: ['dist/less/*.less'], dest: ''}, {expand: true, flatten: false, src: ['dist/less/plugins/*.less'], dest: ''}, ] } }, less: { options: {}, uncompressed: { files: { 'dist/css/selectize.css': ['dist/less/selectize.less'], 'dist/css/selectize.default.css': ['dist/less/selectize.default.less'], 'dist/css/selectize.legacy.css': ['dist/less/selectize.legacy.less'], 'dist/css/selectize.bootstrap2.css': ['dist/less/selectize.bootstrap2.tmp.less'], 'dist/css/selectize.bootstrap3.css': ['dist/less/selectize.bootstrap3.tmp.less'] } } }, concat: { options: { stripBanners: true, separator: grunt.util.linefeed + grunt.util.linefeed }, js: { files: { 'dist/js/selectize.js': files_js, } }, less_plugins: { options: { banner: less_imports.join('\n') + grunt.util.linefeed + grunt.util.linefeed }, files: { 'dist/less/selectize.less': ['dist/less/selectize.less'] } }, less_theme_dependencies: { options: {stripBanners: false}, files: { 'dist/less/selectize.bootstrap2.tmp.less': [ 'bower_components/bootstrap2/less/variables.less', 'bower_components/bootstrap2/less/mixins.less', 'dist/less/selectize.bootstrap2.less' ], 'dist/less/selectize.bootstrap3.tmp.less': [ 'bower_components/bootstrap3/less/variables.less', 'bower_components/bootstrap3/less/mixins/nav-divider.less', 'dist/less/selectize.bootstrap3.less' ] } } }, connect: { keepalive: true }, uglify: { main: { options: { 'banner': '/*! selectize.js - v<%= pkg.version %> | https://github.com/selectize/selectize.js | Apache License (v2) */\n', 'report': 'gzip', 'ascii-only': true }, files: { 'dist/js/selectize.min.js': ['dist/js/selectize.js'], 'dist/js/standalone/selectize.min.js': ['dist/js/standalone/selectize.js'] } } }, watch: { files: [ 'src/**/*.js' ], tasks: [ 'concat:js', 'build_standalone' ] } }); }; selectize.js-0.12.4/LICENSE000066400000000000000000000261261277751564300152350ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2013–2015 Brian Reavis 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 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. selectize.js-0.12.4/Makefile000066400000000000000000000016061277751564300156640ustar00rootroot00000000000000.PHONY: compile release test plugins=* GRUNT=node_modules/.bin/grunt all: compile test: npm test compile: $(GRUNT) --plugins=$(plugins) release: ifeq ($(strip $(version)),) @echo "\033[31mERROR:\033[0;39m No version provided." @echo "\033[1;30mmake release version=1.0.0\033[0;39m" else sed -i.bak 's/"version": "[^"]*"/"version": "$(version)"/' selectize.jquery.json sed -i.bak 's/"version": "[^"]*"/"version": "$(version)"/' package.json rm *.bak make compile npm test || exit 1 cp dist/js/standalone/selectize.js ../.selectize.js git add . git commit -a -m "Released $(version)." git tag v$(version) git push origin master git push origin --tags npm publish git checkout gh-pages mv -f ../.selectize.js js/selectize.js git commit -a -m "Updated selectize.js to latest version." git push origin gh-pages git checkout master @echo "\033[32mv${version} released\033[0;39m" endif selectize.js-0.12.4/README.md000066400000000000000000000154751277751564300155140ustar00rootroot00000000000000# selectize.js [![NPM version](http://img.shields.io/npm/v/selectize.svg?style=flat)](https://www.npmjs.org/package/selectize) [![Build Status](http://img.shields.io/travis/selectize/selectize.js/master.svg?style=flat)](https://travis-ci.org/selectize/selectize.js) [![Coverage Status](http://img.shields.io/coveralls/selectize/selectize.js/master.svg?style=flat)](https://coveralls.io/r/selectize/selectize.js) Selectize is an extensible [jQuery](http://jquery.com/)-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. - [Demos](http://selectize.github.io/selectize.js/) - [Changelog](https://github.com/selectize/selectize.js/releases) - [Examples](examples/) - [Usage Documentation](docs/usage.md) - [API Documentation](docs/api.md) - [Plugin Documentation](docs/plugins.md) - [Browser Test Matrix](https://saucelabs.com/u/selectize) ### Features - **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. - **Caret between items**
Order matters sometimes. Use the and arrow keys to move between selected items. - **Select & delete multiple items at once**
Hold down option on Mac or ctrl on Windows to select more than one item to delete. - **Díåcritîçs supported**
Great for international environments. - **Item creation**
Allow users to create items on the fly (async saving is supported; the control locks until the callback is fired). - **Remote data loading**
For when you have thousands of options and want them provided by the server as the user types. - **Clean API & code**
Interface with it and make modifications easily. Pull requests welcome! - **Extensible**
[Plugin API](docs/plugins.md) for developing custom features (uses [microplugin](https://github.com/brianreavis/microplugin.js)). - **Touch Support**
Plays nice with iOS 5+ devices. ### Dependencies - [jquery](https://github.com/jquery/jquery) (1.7 and greater) - [sifter](https://github.com/brianreavis/sifter.js) (bundled in ["standalone" build](dist/js/standalone)) - [microplugin](https://github.com/brianreavis/microplugin.js) (bundled in ["standalone" build](dist/js/standalone)) ### Installation and files All pre-built files needed to use Selectize can be found in the ["dist"](dist/) folder. If you're looking to get started with minimal fuss, include `standalone/selectize.min.js` (bundles Sifter and Microplugin dependencies – also available un-minifed for debugging, just remove the `.min` part) and `css/selectize.default.css`. Selectize is available at [cdnjs](https://cdnjs.com/libraries/selectize.js). - [**js/**](dist/js) - [**standalone/**](dist/js/standalone) - [selectize.js](dist/js/standalone/selectize.js) — With dependencies, minus jquery - [selectize.js](dist/js/selectize.js) — Without dependencies - [**less/**](dist/less) - [selectize.less](dist/less/selectize.less) — Core styles - [selectize.default.less](dist/less/selectize.default.less) — Default theme - [selectize.bootstrap2.less](dist/less/selectize.bootstrap2.less) — Bootstrap 2 theme - [selectize.bootstrap3.less](dist/less/selectize.bootstrap3.less) — Bootstrap 3 theme - [**plugins/**](dist/less/plugins) — Individual plugin styles - [**css/**](dist/css) - [selectize.css](dist/css/selectize.css) — Core styles - [selectize.default.css](dist/css/selectize.default.css) — Default theme (with core styles) - [selectize.bootstrap2.css](dist/css/selectize.bootstrap2.css) - Bootstrap 2 theme - [selectize.bootstrap3.css](dist/css/selectize.bootstrap3.css) - Bootstrap 3 theme ### Usage ```js $('select').selectize(options); ``` The available options are [documented here](docs/usage.md). #### IE8 Support To support Internet Explorer 8, [es5-shim](https://github.com/kriskowal/es5-shim/) must be added your page. ```html ``` ### Custom Builds 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. ```sh # dependencies npm install # build selectize grunt --plugins= grunt --plugins=* grunt --plugins=remove_button,restore_on_backspace ``` ### Contributing When issuing a pull request: * please **do not include/commit changes in the `dist/` folder** to avoid merge conflicts. A good way to include the right files is to use `git gui` or `git add` when committing to select the files you want to add to your commit. * please **include tests** with your feature so that we're not tempted to break it in the future! Add an entry to the top of the CHANGELOG, and update the documentation in `docs/` as needed. (Refactors and documentation changes don't need a changelog entry.) Squash your commits together in one or a few complete, logical commits, with a concise and descriptive message. One commit means one feature/bugfix/thing that has changed, or a diff bringing the code one step forward to a better, working state. Once your commit is nice and clean, and you want to *discard the other changes*, you can use `git checkout .` (that will erase changes to tracked files) and `git clean [-i/--interactive]` (to erase untracked files). **However, be careful with those commands, as their function is to erase things/changes.** #### Tests Please ensure all the tests pass: ```sh $ npm test # phantomjs $ BROWSERS=Firefox npm test $ BROWSERS=Firefox,Chrome npm test $ BROWSERS=Firefox,Chrome,Safari npm test ``` #### Local environment To run Selectize locally: ```sh $ grunt serve ``` You can then run the examples in `http://localhost:8000/examples/`. However, be careful not to add the `dist/` files in your commit, as Grunt automatically regenerates the files in `dist/` as the source is changed. ## License Copyright © 2013–2016 [Brian Reavis](http://twitter.com/brianreavis) & [Contributors](https://github.com/selectize/selectize.js/graphs/contributors) 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 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. selectize.js-0.12.4/bower.json000066400000000000000000000015711277751564300162360ustar00rootroot00000000000000{ "name": "selectize", "keywords": ["select", "ui", "form", "input", "control", "autocomplete", "tagging", "tag"], "description": "Selectize is a jQuery-based custom ').appendTo($control).attr('tabindex', $input.is(':disabled') ? '-1' : self.tabIndex); $dropdown_parent = $(settings.dropdownParent || $wrapper); $dropdown = $('
').addClass(settings.dropdownClass).addClass(inputMode).hide().appendTo($dropdown_parent); $dropdown_content = $('
').addClass(settings.dropdownContentClass).appendTo($dropdown); if(inputId = $input.attr('id')) { $control_input.attr('id', inputId + '-selectized'); $("label[for='"+inputId+"']").attr('for', inputId + '-selectized'); } if(self.settings.copyClassesToDropdown) { $dropdown.addClass(classes); } $wrapper.css({ width: $input[0].style.width }); if (self.plugins.names.length) { classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-'); $wrapper.addClass(classes_plugins); $dropdown.addClass(classes_plugins); } if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) { $input.attr('multiple', 'multiple'); } if (self.settings.placeholder) { $control_input.attr('placeholder', settings.placeholder); } // if splitOn was not passed in, construct it from the delimiter to allow pasting universally if (!self.settings.splitOn && self.settings.delimiter) { var delimiterEscaped = self.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); self.settings.splitOn = new RegExp('\\s*' + delimiterEscaped + '+\\s*'); } if ($input.attr('autocorrect')) { $control_input.attr('autocorrect', $input.attr('autocorrect')); } if ($input.attr('autocapitalize')) { $control_input.attr('autocapitalize', $input.attr('autocapitalize')); } self.$wrapper = $wrapper; self.$control = $control; self.$control_input = $control_input; self.$dropdown = $dropdown; self.$dropdown_content = $dropdown_content; $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); }); $dropdown.on('mousedown click', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); }); watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); }); autoGrow($control_input); $control.on({ mousedown : function() { return self.onMouseDown.apply(self, arguments); }, click : function() { return self.onClick.apply(self, arguments); } }); $control_input.on({ mousedown : function(e) { e.stopPropagation(); }, keydown : function() { return self.onKeyDown.apply(self, arguments); }, keyup : function() { return self.onKeyUp.apply(self, arguments); }, keypress : function() { return self.onKeyPress.apply(self, arguments); }, resize : function() { self.positionDropdown.apply(self, []); }, blur : function() { return self.onBlur.apply(self, arguments); }, focus : function() { self.ignoreBlur = false; return self.onFocus.apply(self, arguments); }, paste : function() { return self.onPaste.apply(self, arguments); } }); $document.on('keydown' + eventNS, function(e) { self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey']; self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey']; self.isShiftDown = e.shiftKey; }); $document.on('keyup' + eventNS, function(e) { if (e.keyCode === KEY_CTRL) self.isCtrlDown = false; if (e.keyCode === KEY_SHIFT) self.isShiftDown = false; if (e.keyCode === KEY_CMD) self.isCmdDown = false; }); $document.on('mousedown' + eventNS, function(e) { if (self.isFocused) { // prevent events on the dropdown scrollbar from causing the control to blur if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) { return false; } // blur on click outside if (!self.$control.has(e.target).length && e.target !== self.$control[0]) { self.blur(e.target); } } }); $window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() { if (self.isOpen) { self.positionDropdown.apply(self, arguments); } }); $window.on('mousemove' + eventNS, function() { self.ignoreHover = false; }); // store original children and tab index so that they can be // restored when the destroy() method is called. this.revertSettings = { $children : $input.children().detach(), tabindex : $input.attr('tabindex') }; $input.attr('tabindex', -1).hide().after(self.$wrapper); if ($.isArray(settings.items)) { self.setValue(settings.items); delete settings.items; } // feature detect for the validation API if (SUPPORTS_VALIDITY_API) { $input.on('invalid' + eventNS, function(e) { e.preventDefault(); self.isInvalid = true; self.refreshState(); }); } self.updateOriginalInput(); self.refreshItems(); self.refreshState(); self.updatePlaceholder(); self.isSetup = true; if ($input.is(':disabled')) { self.disable(); } self.on('change', this.onChange); $input.data('selectize', self); $input.addClass('selectized'); self.trigger('initialize'); // preload options if (settings.preload === true) { self.onSearchChange(''); } }, /** * Sets up default rendering functions. */ setupTemplates: function() { var self = this; var field_label = self.settings.labelField; var field_optgroup = self.settings.optgroupLabelField; var templates = { 'optgroup': function(data) { return '
' + data.html + '
'; }, 'optgroup_header': function(data, escape) { return '
' + escape(data[field_optgroup]) + '
'; }, 'option': function(data, escape) { return '
' + escape(data[field_label]) + '
'; }, 'item': function(data, escape) { return '
' + escape(data[field_label]) + '
'; }, 'option_create': function(data, escape) { return '
Add ' + escape(data.input) + '
'; } }; self.settings.render = $.extend({}, templates, self.settings.render); }, /** * Maps fired events to callbacks provided * in the settings used when creating the control. */ setupCallbacks: function() { var key, fn, callbacks = { 'initialize' : 'onInitialize', 'change' : 'onChange', 'item_add' : 'onItemAdd', 'item_remove' : 'onItemRemove', 'clear' : 'onClear', 'option_add' : 'onOptionAdd', 'option_remove' : 'onOptionRemove', 'option_clear' : 'onOptionClear', 'optgroup_add' : 'onOptionGroupAdd', 'optgroup_remove' : 'onOptionGroupRemove', 'optgroup_clear' : 'onOptionGroupClear', 'dropdown_open' : 'onDropdownOpen', 'dropdown_close' : 'onDropdownClose', 'type' : 'onType', 'load' : 'onLoad', 'focus' : 'onFocus', 'blur' : 'onBlur' }; for (key in callbacks) { if (callbacks.hasOwnProperty(key)) { fn = this.settings[callbacks[key]]; if (fn) this.on(key, fn); } } }, /** * Triggered when the main control element * has a click event. * * @param {object} e * @return {boolean} */ onClick: function(e) { var self = this; // necessary for mobile webkit devices (manual focus triggering // is ignored unless invoked within a click event) if (!self.isFocused) { self.focus(); e.preventDefault(); } }, /** * Triggered when the main control element * has a mouse down event. * * @param {object} e * @return {boolean} */ onMouseDown: function(e) { var self = this; var defaultPrevented = e.isDefaultPrevented(); var $target = $(e.target); if (self.isFocused) { // retain focus by preventing native handling. if the // event target is the input it should not be modified. // otherwise, text selection within the input won't work. if (e.target !== self.$control_input[0]) { if (self.settings.mode === 'single') { // toggle dropdown self.isOpen ? self.close() : self.open(); } else if (!defaultPrevented) { self.setActiveItem(null); } return false; } } else { // give control focus if (!defaultPrevented) { window.setTimeout(function() { self.focus(); }, 0); } } }, /** * Triggered when the value of the control has been changed. * This should propagate the event to the original DOM * input / select element. */ onChange: function() { this.$input.trigger('change'); }, /** * Triggered on paste. * * @param {object} e * @returns {boolean} */ onPaste: function(e) { var self = this; if (self.isFull() || self.isInputHidden || self.isLocked) { e.preventDefault(); return; } // If a regex or string is included, this will split the pasted // input and create Items for each separate value if (self.settings.splitOn) { // Wait for pasted text to be recognized in value setTimeout(function() { var pastedText = self.$control_input.val(); if(!pastedText.match(self.settings.splitOn)){ return } var splitInput = $.trim(pastedText).split(self.settings.splitOn); for (var i = 0, n = splitInput.length; i < n; i++) { self.createItem(splitInput[i]); } }, 0); } }, /** * Triggered on keypress. * * @param {object} e * @returns {boolean} */ onKeyPress: function(e) { if (this.isLocked) return e && e.preventDefault(); var character = String.fromCharCode(e.keyCode || e.which); if (this.settings.create && this.settings.mode === 'multi' && character === this.settings.delimiter) { this.createItem(); e.preventDefault(); return false; } }, /** * Triggered on keydown. * * @param {object} e * @returns {boolean} */ onKeyDown: function(e) { var isInput = e.target === this.$control_input[0]; var self = this; if (self.isLocked) { if (e.keyCode !== KEY_TAB) { e.preventDefault(); } return; } switch (e.keyCode) { case KEY_A: if (self.isCmdDown) { self.selectAll(); return; } break; case KEY_ESC: if (self.isOpen) { e.preventDefault(); e.stopPropagation(); self.close(); } return; case KEY_N: if (!e.ctrlKey || e.altKey) break; case KEY_DOWN: if (!self.isOpen && self.hasOptions) { self.open(); } else if (self.$activeOption) { self.ignoreHover = true; var $next = self.getAdjacentOption(self.$activeOption, 1); if ($next.length) self.setActiveOption($next, true, true); } e.preventDefault(); return; case KEY_P: if (!e.ctrlKey || e.altKey) break; case KEY_UP: if (self.$activeOption) { self.ignoreHover = true; var $prev = self.getAdjacentOption(self.$activeOption, -1); if ($prev.length) self.setActiveOption($prev, true, true); } e.preventDefault(); return; case KEY_RETURN: if (self.isOpen && self.$activeOption) { self.onOptionSelect({currentTarget: self.$activeOption}); e.preventDefault(); } return; case KEY_LEFT: self.advanceSelection(-1, e); return; case KEY_RIGHT: self.advanceSelection(1, e); return; case KEY_TAB: if (self.settings.selectOnTab && self.isOpen && self.$activeOption) { self.onOptionSelect({currentTarget: self.$activeOption}); // Default behaviour is to jump to the next field, we only want this // if the current field doesn't accept any more entries if (!self.isFull()) { e.preventDefault(); } } if (self.settings.create && self.createItem()) { e.preventDefault(); } return; case KEY_BACKSPACE: case KEY_DELETE: self.deleteSelection(e); return; } if ((self.isFull() || self.isInputHidden) && !(IS_MAC ? e.metaKey : e.ctrlKey)) { e.preventDefault(); return; } }, /** * Triggered on keyup. * * @param {object} e * @returns {boolean} */ onKeyUp: function(e) { var self = this; if (self.isLocked) return e && e.preventDefault(); var value = self.$control_input.val() || ''; if (self.lastValue !== value) { self.lastValue = value; self.onSearchChange(value); self.refreshOptions(); self.trigger('type', value); } }, /** * Invokes the user-provide option provider / loader. * * Note: this function is debounced in the Selectize * constructor (by `settings.loadThrottle` milliseconds) * * @param {string} value */ onSearchChange: function(value) { var self = this; var fn = self.settings.load; if (!fn) return; if (self.loadedSearches.hasOwnProperty(value)) return; self.loadedSearches[value] = true; self.load(function(callback) { fn.apply(self, [value, callback]); }); }, /** * Triggered on focus. * * @param {object} e (optional) * @returns {boolean} */ onFocus: function(e) { var self = this; var wasFocused = self.isFocused; if (self.isDisabled) { self.blur(); e && e.preventDefault(); return false; } if (self.ignoreFocus) return; self.isFocused = true; if (self.settings.preload === 'focus') self.onSearchChange(''); if (!wasFocused) self.trigger('focus'); if (!self.$activeItems.length) { self.showInput(); self.setActiveItem(null); self.refreshOptions(!!self.settings.openOnFocus); } self.refreshState(); }, /** * Triggered on blur. * * @param {object} e * @param {Element} dest */ onBlur: function(e, dest) { var self = this; if (!self.isFocused) return; self.isFocused = false; if (self.ignoreFocus) { return; } else if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) { // necessary to prevent IE closing the dropdown when the scrollbar is clicked self.ignoreBlur = true; self.onFocus(e); return; } var deactivate = function() { self.close(); self.setTextboxValue(''); self.setActiveItem(null); self.setActiveOption(null); self.setCaret(self.items.length); self.refreshState(); // IE11 bug: element still marked as active dest && dest.focus && dest.focus(); self.ignoreFocus = false; self.trigger('blur'); }; self.ignoreFocus = true; if (self.settings.create && self.settings.createOnBlur) { self.createItem(null, false, deactivate); } else { deactivate(); } }, /** * Triggered when the user rolls over * an option in the autocomplete dropdown menu. * * @param {object} e * @returns {boolean} */ onOptionHover: function(e) { if (this.ignoreHover) return; this.setActiveOption(e.currentTarget, false); }, /** * Triggered when the user clicks on an option * in the autocomplete dropdown menu. * * @param {object} e * @returns {boolean} */ onOptionSelect: function(e) { var value, $target, $option, self = this; if (e.preventDefault) { e.preventDefault(); e.stopPropagation(); } $target = $(e.currentTarget); if ($target.hasClass('create')) { self.createItem(null, function() { if (self.settings.closeAfterSelect) { self.close(); } }); } else { value = $target.attr('data-value'); if (typeof value !== 'undefined') { self.lastQuery = null; self.setTextboxValue(''); self.addItem(value); if (self.settings.closeAfterSelect) { self.close(); } else if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) { self.setActiveOption(self.getOption(value)); } } } }, /** * Triggered when the user clicks on an item * that has been selected. * * @param {object} e * @returns {boolean} */ onItemSelect: function(e) { var self = this; if (self.isLocked) return; if (self.settings.mode === 'multi') { e.preventDefault(); self.setActiveItem(e.currentTarget, e); } }, /** * Invokes the provided method that provides * results to a callback---which are then added * as options to the control. * * @param {function} fn */ load: function(fn) { var self = this; var $wrapper = self.$wrapper.addClass(self.settings.loadingClass); self.loading++; fn.apply(self, [function(results) { self.loading = Math.max(self.loading - 1, 0); if (results && results.length) { self.addOption(results); self.refreshOptions(self.isFocused && !self.isInputHidden); } if (!self.loading) { $wrapper.removeClass(self.settings.loadingClass); } self.trigger('load', results); }]); }, /** * Sets the input field of the control to the specified value. * * @param {string} value */ setTextboxValue: function(value) { var $input = this.$control_input; var changed = $input.val() !== value; if (changed) { $input.val(value).triggerHandler('update'); this.lastValue = value; } }, /** * Returns the value of the control. If multiple items * can be selected (e.g. or * element to reflect the current state. */ updateOriginalInput: function(opts) { var i, n, options, label, self = this; opts = opts || {}; if (self.tagType === TAG_SELECT) { options = []; for (i = 0, n = self.items.length; i < n; i++) { label = self.options[self.items[i]][self.settings.labelField] || ''; options.push(''); } if (!options.length && !this.$input.attr('multiple')) { options.push(''); } self.$input.html(options.join('')); } else { self.$input.val(self.getValue()); self.$input.attr('value',self.$input.val()); } if (self.isSetup) { if (!opts.silent) { self.trigger('change', self.$input.val()); } } }, /** * Shows/hide the input placeholder depending * on if there items in the list already. */ updatePlaceholder: function() { if (!this.settings.placeholder) return; var $input = this.$control_input; if (this.items.length) { $input.removeAttr('placeholder'); } else { $input.attr('placeholder', this.settings.placeholder); } $input.triggerHandler('update', {force: true}); }, /** * Shows the autocomplete dropdown containing * the available options. */ open: function() { var self = this; if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return; self.focus(); self.isOpen = true; self.refreshState(); self.$dropdown.css({visibility: 'hidden', display: 'block'}); self.positionDropdown(); self.$dropdown.css({visibility: 'visible'}); self.trigger('dropdown_open', self.$dropdown); }, /** * Closes the autocomplete dropdown menu. */ close: function() { var self = this; var trigger = self.isOpen; if (self.settings.mode === 'single' && self.items.length) { self.hideInput(); self.$control_input.blur(); // close keyboard on iOS } self.isOpen = false; self.$dropdown.hide(); self.setActiveOption(null); self.refreshState(); if (trigger) self.trigger('dropdown_close', self.$dropdown); }, /** * Calculates and applies the appropriate * position of the dropdown. */ positionDropdown: function() { var $control = this.$control; var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position(); offset.top += $control.outerHeight(true); this.$dropdown.css({ width : $control.outerWidth(), top : offset.top, left : offset.left }); }, /** * Resets / clears all selected items * from the control. * * @param {boolean} silent */ clear: function(silent) { var self = this; if (!self.items.length) return; self.$control.children(':not(input)').remove(); self.items = []; self.lastQuery = null; self.setCaret(0); self.setActiveItem(null); self.updatePlaceholder(); self.updateOriginalInput({silent: silent}); self.refreshState(); self.showInput(); self.trigger('clear'); }, /** * A helper method for inserting an element * at the current caret position. * * @param {object} $el */ insertAtCaret: function($el) { var caret = Math.min(this.caretPos, this.items.length); if (caret === 0) { this.$control.prepend($el); } else { $(this.$control[0].childNodes[caret]).before($el); } this.setCaret(caret + 1); }, /** * Removes the current selected item(s). * * @param {object} e (optional) * @returns {boolean} */ deleteSelection: function(e) { var i, n, direction, selection, values, caret, option_select, $option_select, $tail; var self = this; direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1; selection = getSelection(self.$control_input[0]); if (self.$activeOption && !self.settings.hideSelected) { option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value'); } // determine items that will be removed values = []; if (self.$activeItems.length) { $tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first')); caret = self.$control.children(':not(input)').index($tail); if (direction > 0) { caret++; } for (i = 0, n = self.$activeItems.length; i < n; i++) { values.push($(self.$activeItems[i]).attr('data-value')); } if (e) { e.preventDefault(); e.stopPropagation(); } } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) { if (direction < 0 && selection.start === 0 && selection.length === 0) { values.push(self.items[self.caretPos - 1]); } else if (direction > 0 && selection.start === self.$control_input.val().length) { values.push(self.items[self.caretPos]); } } // allow the callback to abort if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete.apply(self, [values]) === false)) { return false; } // perform removal if (typeof caret !== 'undefined') { self.setCaret(caret); } while (values.length) { self.removeItem(values.pop()); } self.showInput(); self.positionDropdown(); self.refreshOptions(true); // select previous option if (option_select) { $option_select = self.getOption(option_select); if ($option_select.length) { self.setActiveOption($option_select); } } return true; }, /** * Selects the previous / next item (depending * on the `direction` argument). * * > 0 - right * < 0 - left * * @param {int} direction * @param {object} e (optional) */ advanceSelection: function(direction, e) { var tail, selection, idx, valueLength, cursorAtEdge, $tail; var self = this; if (direction === 0) return; if (self.rtl) direction *= -1; tail = direction > 0 ? 'last' : 'first'; selection = getSelection(self.$control_input[0]); if (self.isFocused && !self.isInputHidden) { valueLength = self.$control_input.val().length; cursorAtEdge = direction < 0 ? selection.start === 0 && selection.length === 0 : selection.start === valueLength; if (cursorAtEdge && !valueLength) { self.advanceCaret(direction, e); } } else { $tail = self.$control.children('.active:' + tail); if ($tail.length) { idx = self.$control.children(':not(input)').index($tail); self.setActiveItem(null); self.setCaret(direction > 0 ? idx + 1 : idx); } } }, /** * Moves the caret left / right. * * @param {int} direction * @param {object} e (optional) */ advanceCaret: function(direction, e) { var self = this, fn, $adj; if (direction === 0) return; fn = direction > 0 ? 'next' : 'prev'; if (self.isShiftDown) { $adj = self.$control_input[fn](); if ($adj.length) { self.hideInput(); self.setActiveItem($adj); e && e.preventDefault(); } } else { self.setCaret(self.caretPos + direction); } }, /** * Moves the caret to the specified index. * * @param {int} i */ setCaret: function(i) { var self = this; if (self.settings.mode === 'single') { i = self.items.length; } else { i = Math.max(0, Math.min(self.items.length, i)); } if(!self.isPending) { // the input must be moved by leaving it in place and moving the // siblings, due to the fact that focus cannot be restored once lost // on mobile webkit devices var j, n, fn, $children, $child; $children = self.$control.children(':not(input)'); for (j = 0, n = $children.length; j < n; j++) { $child = $($children[j]).detach(); if (j < i) { self.$control_input.before($child); } else { self.$control.append($child); } } } self.caretPos = i; }, /** * Disables user input on the control. Used while * items are being asynchronously created. */ lock: function() { this.close(); this.isLocked = true; this.refreshState(); }, /** * Re-enables user input on the control. */ unlock: function() { this.isLocked = false; this.refreshState(); }, /** * Disables user input on the control completely. * While disabled, it cannot receive focus. */ disable: function() { var self = this; self.$input.prop('disabled', true); self.$control_input.prop('disabled', true).prop('tabindex', -1); self.isDisabled = true; self.lock(); }, /** * Enables the control so that it can respond * to focus and user input. */ enable: function() { var self = this; self.$input.prop('disabled', false); self.$control_input.prop('disabled', false).prop('tabindex', self.tabIndex); self.isDisabled = false; self.unlock(); }, /** * Completely destroys the control and * unbinds all event listeners so that it can * be garbage collected. */ destroy: function() { var self = this; var eventNS = self.eventNS; var revertSettings = self.revertSettings; self.trigger('destroy'); self.off(); self.$wrapper.remove(); self.$dropdown.remove(); self.$input .html('') .append(revertSettings.$children) .removeAttr('tabindex') .removeClass('selectized') .attr({tabindex: revertSettings.tabindex}) .show(); self.$control_input.removeData('grow'); self.$input.removeData('selectize'); $(window).off(eventNS); $(document).off(eventNS); $(document.body).off(eventNS); delete self.$input[0].selectize; }, /** * A helper method for rendering "item" and * "option" templates, given the data. * * @param {string} templateName * @param {object} data * @returns {string} */ render: function(templateName, data) { var value, id, label; var html = ''; var cache = false; var self = this; var regex_tag = /^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i; if (templateName === 'option' || templateName === 'item') { value = hash_key(data[self.settings.valueField]); cache = !!value; } // pull markup from cache if it exists if (cache) { if (!isset(self.renderCache[templateName])) { self.renderCache[templateName] = {}; } if (self.renderCache[templateName].hasOwnProperty(value)) { return self.renderCache[templateName][value]; } } // render markup html = $(self.settings.render[templateName].apply(this, [data, escape_html])); // add mandatory attributes if (templateName === 'option' || templateName === 'option_create') { html.attr('data-selectable', ''); } else if (templateName === 'optgroup') { id = data[self.settings.optgroupValueField] || ''; html.attr('data-group', id); } if (templateName === 'option' || templateName === 'item') { html.attr('data-value', value || ''); } // update cache if (cache) { self.renderCache[templateName][value] = html[0]; } return html[0]; }, /** * Clears the render cache for a template. If * no template is given, clears all render * caches. * * @param {string} templateName */ clearCache: function(templateName) { var self = this; if (typeof templateName === 'undefined') { self.renderCache = {}; } else { delete self.renderCache[templateName]; } }, /** * Determines whether or not to display the * create item prompt, given a user input. * * @param {string} input * @return {boolean} */ canCreate: function(input) { var self = this; if (!self.settings.create) return false; var filter = self.settings.createFilter; return input.length && (typeof filter !== 'function' || filter.apply(self, [input])) && (typeof filter !== 'string' || new RegExp(filter).test(input)) && (!(filter instanceof RegExp) || filter.test(input)); } }); Selectize.count = 0; Selectize.defaults = { options: [], optgroups: [], plugins: [], delimiter: ',', splitOn: null, // regexp or string for splitting up values from a paste command persist: true, diacritics: true, create: false, createOnBlur: false, createFilter: null, highlight: true, openOnFocus: true, maxOptions: 1000, maxItems: null, hideSelected: null, addPrecedence: false, selectOnTab: false, preload: false, allowEmptyOption: false, closeAfterSelect: false, scrollDuration: 60, loadThrottle: 300, loadingClass: 'loading', dataAttr: 'data-data', optgroupField: 'optgroup', valueField: 'value', labelField: 'text', optgroupLabelField: 'label', optgroupValueField: 'value', lockOptgroupOrder: false, sortField: '$order', searchField: ['text'], searchConjunction: 'and', mode: null, wrapperClass: 'selectize-control', inputClass: 'selectize-input', dropdownClass: 'selectize-dropdown', dropdownContentClass: 'selectize-dropdown-content', dropdownParent: null, copyClassesToDropdown: true, /* load : null, // function(query, callback) { ... } score : null, // function(search) { ... } onInitialize : null, // function() { ... } onChange : null, // function(value) { ... } onItemAdd : null, // function(value, $item) { ... } onItemRemove : null, // function(value) { ... } onClear : null, // function() { ... } onOptionAdd : null, // function(value, data) { ... } onOptionRemove : null, // function(value) { ... } onOptionClear : null, // function() { ... } onOptionGroupAdd : null, // function(id, data) { ... } onOptionGroupRemove : null, // function(id) { ... } onOptionGroupClear : null, // function() { ... } onDropdownOpen : null, // function($dropdown) { ... } onDropdownClose : null, // function($dropdown) { ... } onType : null, // function(str) { ... } onDelete : null, // function(values) { ... } */ render: { /* item: null, optgroup: null, optgroup_header: null, option: null, option_create: null */ } }; $.fn.selectize = function(settings_user) { var defaults = $.fn.selectize.defaults; var settings = $.extend({}, defaults, settings_user); var attr_data = settings.dataAttr; var field_label = settings.labelField; var field_value = settings.valueField; var field_optgroup = settings.optgroupField; var field_optgroup_label = settings.optgroupLabelField; var field_optgroup_value = settings.optgroupValueField; /** * Initializes selectize from a element. * * @param {object} $input * @param {object} settings_element */ var init_textbox = function($input, settings_element) { var i, n, values, option; var data_raw = $input.attr(attr_data); if (!data_raw) { var value = $.trim($input.val() || ''); if (!settings.allowEmptyOption && !value.length) return; values = value.split(settings.delimiter); for (i = 0, n = values.length; i < n; i++) { option = {}; option[field_label] = values[i]; option[field_value] = values[i]; settings_element.options.push(option); } settings_element.items = values; } else { settings_element.options = JSON.parse(data_raw); for (i = 0, n = settings_element.options.length; i < n; i++) { settings_element.items.push(settings_element.options[i][field_value]); } } }; /** * Initializes selectize from a ').appendTo($control).attr('tabindex', $input.is(':disabled') ? '-1' : self.tabIndex); $dropdown_parent = $(settings.dropdownParent || $wrapper); $dropdown = $('
').addClass(settings.dropdownClass).addClass(inputMode).hide().appendTo($dropdown_parent); $dropdown_content = $('
').addClass(settings.dropdownContentClass).appendTo($dropdown); if(inputId = $input.attr('id')) { $control_input.attr('id', inputId + '-selectized'); $("label[for='"+inputId+"']").attr('for', inputId + '-selectized'); } if(self.settings.copyClassesToDropdown) { $dropdown.addClass(classes); } $wrapper.css({ width: $input[0].style.width }); if (self.plugins.names.length) { classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-'); $wrapper.addClass(classes_plugins); $dropdown.addClass(classes_plugins); } if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) { $input.attr('multiple', 'multiple'); } if (self.settings.placeholder) { $control_input.attr('placeholder', settings.placeholder); } // if splitOn was not passed in, construct it from the delimiter to allow pasting universally if (!self.settings.splitOn && self.settings.delimiter) { var delimiterEscaped = self.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); self.settings.splitOn = new RegExp('\\s*' + delimiterEscaped + '+\\s*'); } if ($input.attr('autocorrect')) { $control_input.attr('autocorrect', $input.attr('autocorrect')); } if ($input.attr('autocapitalize')) { $control_input.attr('autocapitalize', $input.attr('autocapitalize')); } self.$wrapper = $wrapper; self.$control = $control; self.$control_input = $control_input; self.$dropdown = $dropdown; self.$dropdown_content = $dropdown_content; $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); }); $dropdown.on('mousedown click', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); }); watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); }); autoGrow($control_input); $control.on({ mousedown : function() { return self.onMouseDown.apply(self, arguments); }, click : function() { return self.onClick.apply(self, arguments); } }); $control_input.on({ mousedown : function(e) { e.stopPropagation(); }, keydown : function() { return self.onKeyDown.apply(self, arguments); }, keyup : function() { return self.onKeyUp.apply(self, arguments); }, keypress : function() { return self.onKeyPress.apply(self, arguments); }, resize : function() { self.positionDropdown.apply(self, []); }, blur : function() { return self.onBlur.apply(self, arguments); }, focus : function() { self.ignoreBlur = false; return self.onFocus.apply(self, arguments); }, paste : function() { return self.onPaste.apply(self, arguments); } }); $document.on('keydown' + eventNS, function(e) { self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey']; self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey']; self.isShiftDown = e.shiftKey; }); $document.on('keyup' + eventNS, function(e) { if (e.keyCode === KEY_CTRL) self.isCtrlDown = false; if (e.keyCode === KEY_SHIFT) self.isShiftDown = false; if (e.keyCode === KEY_CMD) self.isCmdDown = false; }); $document.on('mousedown' + eventNS, function(e) { if (self.isFocused) { // prevent events on the dropdown scrollbar from causing the control to blur if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) { return false; } // blur on click outside if (!self.$control.has(e.target).length && e.target !== self.$control[0]) { self.blur(e.target); } } }); $window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() { if (self.isOpen) { self.positionDropdown.apply(self, arguments); } }); $window.on('mousemove' + eventNS, function() { self.ignoreHover = false; }); // store original children and tab index so that they can be // restored when the destroy() method is called. this.revertSettings = { $children : $input.children().detach(), tabindex : $input.attr('tabindex') }; $input.attr('tabindex', -1).hide().after(self.$wrapper); if ($.isArray(settings.items)) { self.setValue(settings.items); delete settings.items; } // feature detect for the validation API if (SUPPORTS_VALIDITY_API) { $input.on('invalid' + eventNS, function(e) { e.preventDefault(); self.isInvalid = true; self.refreshState(); }); } self.updateOriginalInput(); self.refreshItems(); self.refreshState(); self.updatePlaceholder(); self.isSetup = true; if ($input.is(':disabled')) { self.disable(); } self.on('change', this.onChange); $input.data('selectize', self); $input.addClass('selectized'); self.trigger('initialize'); // preload options if (settings.preload === true) { self.onSearchChange(''); } }, /** * Sets up default rendering functions. */ setupTemplates: function() { var self = this; var field_label = self.settings.labelField; var field_optgroup = self.settings.optgroupLabelField; var templates = { 'optgroup': function(data) { return '
' + data.html + '
'; }, 'optgroup_header': function(data, escape) { return '
' + escape(data[field_optgroup]) + '
'; }, 'option': function(data, escape) { return '
' + escape(data[field_label]) + '
'; }, 'item': function(data, escape) { return '
' + escape(data[field_label]) + '
'; }, 'option_create': function(data, escape) { return '
Add ' + escape(data.input) + '
'; } }; self.settings.render = $.extend({}, templates, self.settings.render); }, /** * Maps fired events to callbacks provided * in the settings used when creating the control. */ setupCallbacks: function() { var key, fn, callbacks = { 'initialize' : 'onInitialize', 'change' : 'onChange', 'item_add' : 'onItemAdd', 'item_remove' : 'onItemRemove', 'clear' : 'onClear', 'option_add' : 'onOptionAdd', 'option_remove' : 'onOptionRemove', 'option_clear' : 'onOptionClear', 'optgroup_add' : 'onOptionGroupAdd', 'optgroup_remove' : 'onOptionGroupRemove', 'optgroup_clear' : 'onOptionGroupClear', 'dropdown_open' : 'onDropdownOpen', 'dropdown_close' : 'onDropdownClose', 'type' : 'onType', 'load' : 'onLoad', 'focus' : 'onFocus', 'blur' : 'onBlur' }; for (key in callbacks) { if (callbacks.hasOwnProperty(key)) { fn = this.settings[callbacks[key]]; if (fn) this.on(key, fn); } } }, /** * Triggered when the main control element * has a click event. * * @param {object} e * @return {boolean} */ onClick: function(e) { var self = this; // necessary for mobile webkit devices (manual focus triggering // is ignored unless invoked within a click event) if (!self.isFocused) { self.focus(); e.preventDefault(); } }, /** * Triggered when the main control element * has a mouse down event. * * @param {object} e * @return {boolean} */ onMouseDown: function(e) { var self = this; var defaultPrevented = e.isDefaultPrevented(); var $target = $(e.target); if (self.isFocused) { // retain focus by preventing native handling. if the // event target is the input it should not be modified. // otherwise, text selection within the input won't work. if (e.target !== self.$control_input[0]) { if (self.settings.mode === 'single') { // toggle dropdown self.isOpen ? self.close() : self.open(); } else if (!defaultPrevented) { self.setActiveItem(null); } return false; } } else { // give control focus if (!defaultPrevented) { window.setTimeout(function() { self.focus(); }, 0); } } }, /** * Triggered when the value of the control has been changed. * This should propagate the event to the original DOM * input / select element. */ onChange: function() { this.$input.trigger('change'); }, /** * Triggered on paste. * * @param {object} e * @returns {boolean} */ onPaste: function(e) { var self = this; if (self.isFull() || self.isInputHidden || self.isLocked) { e.preventDefault(); return; } // If a regex or string is included, this will split the pasted // input and create Items for each separate value if (self.settings.splitOn) { // Wait for pasted text to be recognized in value setTimeout(function() { var pastedText = self.$control_input.val(); if(!pastedText.match(self.settings.splitOn)){ return } var splitInput = $.trim(pastedText).split(self.settings.splitOn); for (var i = 0, n = splitInput.length; i < n; i++) { self.createItem(splitInput[i]); } }, 0); } }, /** * Triggered on keypress. * * @param {object} e * @returns {boolean} */ onKeyPress: function(e) { if (this.isLocked) return e && e.preventDefault(); var character = String.fromCharCode(e.keyCode || e.which); if (this.settings.create && this.settings.mode === 'multi' && character === this.settings.delimiter) { this.createItem(); e.preventDefault(); return false; } }, /** * Triggered on keydown. * * @param {object} e * @returns {boolean} */ onKeyDown: function(e) { var isInput = e.target === this.$control_input[0]; var self = this; if (self.isLocked) { if (e.keyCode !== KEY_TAB) { e.preventDefault(); } return; } switch (e.keyCode) { case KEY_A: if (self.isCmdDown) { self.selectAll(); return; } break; case KEY_ESC: if (self.isOpen) { e.preventDefault(); e.stopPropagation(); self.close(); } return; case KEY_N: if (!e.ctrlKey || e.altKey) break; case KEY_DOWN: if (!self.isOpen && self.hasOptions) { self.open(); } else if (self.$activeOption) { self.ignoreHover = true; var $next = self.getAdjacentOption(self.$activeOption, 1); if ($next.length) self.setActiveOption($next, true, true); } e.preventDefault(); return; case KEY_P: if (!e.ctrlKey || e.altKey) break; case KEY_UP: if (self.$activeOption) { self.ignoreHover = true; var $prev = self.getAdjacentOption(self.$activeOption, -1); if ($prev.length) self.setActiveOption($prev, true, true); } e.preventDefault(); return; case KEY_RETURN: if (self.isOpen && self.$activeOption) { self.onOptionSelect({currentTarget: self.$activeOption}); e.preventDefault(); } return; case KEY_LEFT: self.advanceSelection(-1, e); return; case KEY_RIGHT: self.advanceSelection(1, e); return; case KEY_TAB: if (self.settings.selectOnTab && self.isOpen && self.$activeOption) { self.onOptionSelect({currentTarget: self.$activeOption}); // Default behaviour is to jump to the next field, we only want this // if the current field doesn't accept any more entries if (!self.isFull()) { e.preventDefault(); } } if (self.settings.create && self.createItem()) { e.preventDefault(); } return; case KEY_BACKSPACE: case KEY_DELETE: self.deleteSelection(e); return; } if ((self.isFull() || self.isInputHidden) && !(IS_MAC ? e.metaKey : e.ctrlKey)) { e.preventDefault(); return; } }, /** * Triggered on keyup. * * @param {object} e * @returns {boolean} */ onKeyUp: function(e) { var self = this; if (self.isLocked) return e && e.preventDefault(); var value = self.$control_input.val() || ''; if (self.lastValue !== value) { self.lastValue = value; self.onSearchChange(value); self.refreshOptions(); self.trigger('type', value); } }, /** * Invokes the user-provide option provider / loader. * * Note: this function is debounced in the Selectize * constructor (by `settings.loadThrottle` milliseconds) * * @param {string} value */ onSearchChange: function(value) { var self = this; var fn = self.settings.load; if (!fn) return; if (self.loadedSearches.hasOwnProperty(value)) return; self.loadedSearches[value] = true; self.load(function(callback) { fn.apply(self, [value, callback]); }); }, /** * Triggered on focus. * * @param {object} e (optional) * @returns {boolean} */ onFocus: function(e) { var self = this; var wasFocused = self.isFocused; if (self.isDisabled) { self.blur(); e && e.preventDefault(); return false; } if (self.ignoreFocus) return; self.isFocused = true; if (self.settings.preload === 'focus') self.onSearchChange(''); if (!wasFocused) self.trigger('focus'); if (!self.$activeItems.length) { self.showInput(); self.setActiveItem(null); self.refreshOptions(!!self.settings.openOnFocus); } self.refreshState(); }, /** * Triggered on blur. * * @param {object} e * @param {Element} dest */ onBlur: function(e, dest) { var self = this; if (!self.isFocused) return; self.isFocused = false; if (self.ignoreFocus) { return; } else if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) { // necessary to prevent IE closing the dropdown when the scrollbar is clicked self.ignoreBlur = true; self.onFocus(e); return; } var deactivate = function() { self.close(); self.setTextboxValue(''); self.setActiveItem(null); self.setActiveOption(null); self.setCaret(self.items.length); self.refreshState(); // IE11 bug: element still marked as active dest && dest.focus && dest.focus(); self.ignoreFocus = false; self.trigger('blur'); }; self.ignoreFocus = true; if (self.settings.create && self.settings.createOnBlur) { self.createItem(null, false, deactivate); } else { deactivate(); } }, /** * Triggered when the user rolls over * an option in the autocomplete dropdown menu. * * @param {object} e * @returns {boolean} */ onOptionHover: function(e) { if (this.ignoreHover) return; this.setActiveOption(e.currentTarget, false); }, /** * Triggered when the user clicks on an option * in the autocomplete dropdown menu. * * @param {object} e * @returns {boolean} */ onOptionSelect: function(e) { var value, $target, $option, self = this; if (e.preventDefault) { e.preventDefault(); e.stopPropagation(); } $target = $(e.currentTarget); if ($target.hasClass('create')) { self.createItem(null, function() { if (self.settings.closeAfterSelect) { self.close(); } }); } else { value = $target.attr('data-value'); if (typeof value !== 'undefined') { self.lastQuery = null; self.setTextboxValue(''); self.addItem(value); if (self.settings.closeAfterSelect) { self.close(); } else if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) { self.setActiveOption(self.getOption(value)); } } } }, /** * Triggered when the user clicks on an item * that has been selected. * * @param {object} e * @returns {boolean} */ onItemSelect: function(e) { var self = this; if (self.isLocked) return; if (self.settings.mode === 'multi') { e.preventDefault(); self.setActiveItem(e.currentTarget, e); } }, /** * Invokes the provided method that provides * results to a callback---which are then added * as options to the control. * * @param {function} fn */ load: function(fn) { var self = this; var $wrapper = self.$wrapper.addClass(self.settings.loadingClass); self.loading++; fn.apply(self, [function(results) { self.loading = Math.max(self.loading - 1, 0); if (results && results.length) { self.addOption(results); self.refreshOptions(self.isFocused && !self.isInputHidden); } if (!self.loading) { $wrapper.removeClass(self.settings.loadingClass); } self.trigger('load', results); }]); }, /** * Sets the input field of the control to the specified value. * * @param {string} value */ setTextboxValue: function(value) { var $input = this.$control_input; var changed = $input.val() !== value; if (changed) { $input.val(value).triggerHandler('update'); this.lastValue = value; } }, /** * Returns the value of the control. If multiple items * can be selected (e.g. or * element to reflect the current state. */ updateOriginalInput: function(opts) { var i, n, options, label, self = this; opts = opts || {}; if (self.tagType === TAG_SELECT) { options = []; for (i = 0, n = self.items.length; i < n; i++) { label = self.options[self.items[i]][self.settings.labelField] || ''; options.push(''); } if (!options.length && !this.$input.attr('multiple')) { options.push(''); } self.$input.html(options.join('')); } else { self.$input.val(self.getValue()); self.$input.attr('value',self.$input.val()); } if (self.isSetup) { if (!opts.silent) { self.trigger('change', self.$input.val()); } } }, /** * Shows/hide the input placeholder depending * on if there items in the list already. */ updatePlaceholder: function() { if (!this.settings.placeholder) return; var $input = this.$control_input; if (this.items.length) { $input.removeAttr('placeholder'); } else { $input.attr('placeholder', this.settings.placeholder); } $input.triggerHandler('update', {force: true}); }, /** * Shows the autocomplete dropdown containing * the available options. */ open: function() { var self = this; if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return; self.focus(); self.isOpen = true; self.refreshState(); self.$dropdown.css({visibility: 'hidden', display: 'block'}); self.positionDropdown(); self.$dropdown.css({visibility: 'visible'}); self.trigger('dropdown_open', self.$dropdown); }, /** * Closes the autocomplete dropdown menu. */ close: function() { var self = this; var trigger = self.isOpen; if (self.settings.mode === 'single' && self.items.length) { self.hideInput(); self.$control_input.blur(); // close keyboard on iOS } self.isOpen = false; self.$dropdown.hide(); self.setActiveOption(null); self.refreshState(); if (trigger) self.trigger('dropdown_close', self.$dropdown); }, /** * Calculates and applies the appropriate * position of the dropdown. */ positionDropdown: function() { var $control = this.$control; var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position(); offset.top += $control.outerHeight(true); this.$dropdown.css({ width : $control.outerWidth(), top : offset.top, left : offset.left }); }, /** * Resets / clears all selected items * from the control. * * @param {boolean} silent */ clear: function(silent) { var self = this; if (!self.items.length) return; self.$control.children(':not(input)').remove(); self.items = []; self.lastQuery = null; self.setCaret(0); self.setActiveItem(null); self.updatePlaceholder(); self.updateOriginalInput({silent: silent}); self.refreshState(); self.showInput(); self.trigger('clear'); }, /** * A helper method for inserting an element * at the current caret position. * * @param {object} $el */ insertAtCaret: function($el) { var caret = Math.min(this.caretPos, this.items.length); if (caret === 0) { this.$control.prepend($el); } else { $(this.$control[0].childNodes[caret]).before($el); } this.setCaret(caret + 1); }, /** * Removes the current selected item(s). * * @param {object} e (optional) * @returns {boolean} */ deleteSelection: function(e) { var i, n, direction, selection, values, caret, option_select, $option_select, $tail; var self = this; direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1; selection = getSelection(self.$control_input[0]); if (self.$activeOption && !self.settings.hideSelected) { option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value'); } // determine items that will be removed values = []; if (self.$activeItems.length) { $tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first')); caret = self.$control.children(':not(input)').index($tail); if (direction > 0) { caret++; } for (i = 0, n = self.$activeItems.length; i < n; i++) { values.push($(self.$activeItems[i]).attr('data-value')); } if (e) { e.preventDefault(); e.stopPropagation(); } } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) { if (direction < 0 && selection.start === 0 && selection.length === 0) { values.push(self.items[self.caretPos - 1]); } else if (direction > 0 && selection.start === self.$control_input.val().length) { values.push(self.items[self.caretPos]); } } // allow the callback to abort if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete.apply(self, [values]) === false)) { return false; } // perform removal if (typeof caret !== 'undefined') { self.setCaret(caret); } while (values.length) { self.removeItem(values.pop()); } self.showInput(); self.positionDropdown(); self.refreshOptions(true); // select previous option if (option_select) { $option_select = self.getOption(option_select); if ($option_select.length) { self.setActiveOption($option_select); } } return true; }, /** * Selects the previous / next item (depending * on the `direction` argument). * * > 0 - right * < 0 - left * * @param {int} direction * @param {object} e (optional) */ advanceSelection: function(direction, e) { var tail, selection, idx, valueLength, cursorAtEdge, $tail; var self = this; if (direction === 0) return; if (self.rtl) direction *= -1; tail = direction > 0 ? 'last' : 'first'; selection = getSelection(self.$control_input[0]); if (self.isFocused && !self.isInputHidden) { valueLength = self.$control_input.val().length; cursorAtEdge = direction < 0 ? selection.start === 0 && selection.length === 0 : selection.start === valueLength; if (cursorAtEdge && !valueLength) { self.advanceCaret(direction, e); } } else { $tail = self.$control.children('.active:' + tail); if ($tail.length) { idx = self.$control.children(':not(input)').index($tail); self.setActiveItem(null); self.setCaret(direction > 0 ? idx + 1 : idx); } } }, /** * Moves the caret left / right. * * @param {int} direction * @param {object} e (optional) */ advanceCaret: function(direction, e) { var self = this, fn, $adj; if (direction === 0) return; fn = direction > 0 ? 'next' : 'prev'; if (self.isShiftDown) { $adj = self.$control_input[fn](); if ($adj.length) { self.hideInput(); self.setActiveItem($adj); e && e.preventDefault(); } } else { self.setCaret(self.caretPos + direction); } }, /** * Moves the caret to the specified index. * * @param {int} i */ setCaret: function(i) { var self = this; if (self.settings.mode === 'single') { i = self.items.length; } else { i = Math.max(0, Math.min(self.items.length, i)); } if(!self.isPending) { // the input must be moved by leaving it in place and moving the // siblings, due to the fact that focus cannot be restored once lost // on mobile webkit devices var j, n, fn, $children, $child; $children = self.$control.children(':not(input)'); for (j = 0, n = $children.length; j < n; j++) { $child = $($children[j]).detach(); if (j < i) { self.$control_input.before($child); } else { self.$control.append($child); } } } self.caretPos = i; }, /** * Disables user input on the control. Used while * items are being asynchronously created. */ lock: function() { this.close(); this.isLocked = true; this.refreshState(); }, /** * Re-enables user input on the control. */ unlock: function() { this.isLocked = false; this.refreshState(); }, /** * Disables user input on the control completely. * While disabled, it cannot receive focus. */ disable: function() { var self = this; self.$input.prop('disabled', true); self.$control_input.prop('disabled', true).prop('tabindex', -1); self.isDisabled = true; self.lock(); }, /** * Enables the control so that it can respond * to focus and user input. */ enable: function() { var self = this; self.$input.prop('disabled', false); self.$control_input.prop('disabled', false).prop('tabindex', self.tabIndex); self.isDisabled = false; self.unlock(); }, /** * Completely destroys the control and * unbinds all event listeners so that it can * be garbage collected. */ destroy: function() { var self = this; var eventNS = self.eventNS; var revertSettings = self.revertSettings; self.trigger('destroy'); self.off(); self.$wrapper.remove(); self.$dropdown.remove(); self.$input .html('') .append(revertSettings.$children) .removeAttr('tabindex') .removeClass('selectized') .attr({tabindex: revertSettings.tabindex}) .show(); self.$control_input.removeData('grow'); self.$input.removeData('selectize'); $(window).off(eventNS); $(document).off(eventNS); $(document.body).off(eventNS); delete self.$input[0].selectize; }, /** * A helper method for rendering "item" and * "option" templates, given the data. * * @param {string} templateName * @param {object} data * @returns {string} */ render: function(templateName, data) { var value, id, label; var html = ''; var cache = false; var self = this; var regex_tag = /^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i; if (templateName === 'option' || templateName === 'item') { value = hash_key(data[self.settings.valueField]); cache = !!value; } // pull markup from cache if it exists if (cache) { if (!isset(self.renderCache[templateName])) { self.renderCache[templateName] = {}; } if (self.renderCache[templateName].hasOwnProperty(value)) { return self.renderCache[templateName][value]; } } // render markup html = $(self.settings.render[templateName].apply(this, [data, escape_html])); // add mandatory attributes if (templateName === 'option' || templateName === 'option_create') { html.attr('data-selectable', ''); } else if (templateName === 'optgroup') { id = data[self.settings.optgroupValueField] || ''; html.attr('data-group', id); } if (templateName === 'option' || templateName === 'item') { html.attr('data-value', value || ''); } // update cache if (cache) { self.renderCache[templateName][value] = html[0]; } return html[0]; }, /** * Clears the render cache for a template. If * no template is given, clears all render * caches. * * @param {string} templateName */ clearCache: function(templateName) { var self = this; if (typeof templateName === 'undefined') { self.renderCache = {}; } else { delete self.renderCache[templateName]; } }, /** * Determines whether or not to display the * create item prompt, given a user input. * * @param {string} input * @return {boolean} */ canCreate: function(input) { var self = this; if (!self.settings.create) return false; var filter = self.settings.createFilter; return input.length && (typeof filter !== 'function' || filter.apply(self, [input])) && (typeof filter !== 'string' || new RegExp(filter).test(input)) && (!(filter instanceof RegExp) || filter.test(input)); } }); Selectize.count = 0; Selectize.defaults = { options: [], optgroups: [], plugins: [], delimiter: ',', splitOn: null, // regexp or string for splitting up values from a paste command persist: true, diacritics: true, create: false, createOnBlur: false, createFilter: null, highlight: true, openOnFocus: true, maxOptions: 1000, maxItems: null, hideSelected: null, addPrecedence: false, selectOnTab: false, preload: false, allowEmptyOption: false, closeAfterSelect: false, scrollDuration: 60, loadThrottle: 300, loadingClass: 'loading', dataAttr: 'data-data', optgroupField: 'optgroup', valueField: 'value', labelField: 'text', optgroupLabelField: 'label', optgroupValueField: 'value', lockOptgroupOrder: false, sortField: '$order', searchField: ['text'], searchConjunction: 'and', mode: null, wrapperClass: 'selectize-control', inputClass: 'selectize-input', dropdownClass: 'selectize-dropdown', dropdownContentClass: 'selectize-dropdown-content', dropdownParent: null, copyClassesToDropdown: true, /* load : null, // function(query, callback) { ... } score : null, // function(search) { ... } onInitialize : null, // function() { ... } onChange : null, // function(value) { ... } onItemAdd : null, // function(value, $item) { ... } onItemRemove : null, // function(value) { ... } onClear : null, // function() { ... } onOptionAdd : null, // function(value, data) { ... } onOptionRemove : null, // function(value) { ... } onOptionClear : null, // function() { ... } onOptionGroupAdd : null, // function(id, data) { ... } onOptionGroupRemove : null, // function(id) { ... } onOptionGroupClear : null, // function() { ... } onDropdownOpen : null, // function($dropdown) { ... } onDropdownClose : null, // function($dropdown) { ... } onType : null, // function(str) { ... } onDelete : null, // function(values) { ... } */ render: { /* item: null, optgroup: null, optgroup_header: null, option: null, option_create: null */ } }; $.fn.selectize = function(settings_user) { var defaults = $.fn.selectize.defaults; var settings = $.extend({}, defaults, settings_user); var attr_data = settings.dataAttr; var field_label = settings.labelField; var field_value = settings.valueField; var field_optgroup = settings.optgroupField; var field_optgroup_label = settings.optgroupLabelField; var field_optgroup_value = settings.optgroupValueField; /** * Initializes selectize from a element. * * @param {object} $input * @param {object} settings_element */ var init_textbox = function($input, settings_element) { var i, n, values, option; var data_raw = $input.attr(attr_data); if (!data_raw) { var value = $.trim($input.val() || ''); if (!settings.allowEmptyOption && !value.length) return; values = value.split(settings.delimiter); for (i = 0, n = values.length; i < n; i++) { option = {}; option[field_label] = values[i]; option[field_value] = values[i]; settings_element.options.push(option); } settings_element.items = values; } else { settings_element.options = JSON.parse(data_raw); for (i = 0, n = settings_element.options.length; i < n; i++) { settings_element.items.push(settings_element.options[i][field_value]); } } }; /** * Initializes selectize from a
selectize.js-0.12.4/examples/basic.html000066400000000000000000000512331277751564300200120ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

<input type="text">

<select>

<select> (allow empty)

<select> (disabled)

<select multiple>

<select multiple> (disabled)

selectize.js-0.12.4/examples/cities.html000066400000000000000000000120501277751564300202030ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

State / City Selection

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

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

selectize.js-0.12.4/examples/confirm.html000066400000000000000000000027011277751564300203620ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Confirm Delete

selectize.js-0.12.4/examples/contacts.html000066400000000000000000000135201277751564300205440ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Email Contacts

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

selectize.js-0.12.4/examples/create-filter.html000066400000000000000000000047221277751564300214600ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Create Filter

Examples of how to filter created results.





selectize.js-0.12.4/examples/css/000077500000000000000000000000001277751564300166275ustar00rootroot00000000000000selectize.js-0.12.4/examples/css/normalize.css000066400000000000000000000153331277751564300213460ustar00rootroot00000000000000/*! normalize.css v2.0.1 | MIT License | git.io/normalize */ /* ========================================================================== HTML5 display definitions ========================================================================== */ /* * Corrects `block` display not defined in IE 8/9. */ article, aside, details, figcaption, figure, footer, header, hgroup, nav, section, summary { display: block; } /* * Corrects `inline-block` display not defined in IE 8/9. */ audio, canvas, video { display: inline-block; } /* * Prevents modern browsers from displaying `audio` without controls. * Remove excess height in iOS 5 devices. */ audio:not([controls]) { display: none; height: 0; } /* * Addresses styling for `hidden` attribute not present in IE 8/9. */ [hidden] { display: none; } /* ========================================================================== Base ========================================================================== */ /* * 1. Sets default font family to sans-serif. * 2. Prevents iOS text size adjust after orientation change, without disabling * user zoom. */ html { font-family: sans-serif; /* 1 */ -webkit-text-size-adjust: 100%; /* 2 */ -ms-text-size-adjust: 100%; /* 2 */ } /* * Removes default margin. */ body { margin: 0; } /* ========================================================================== Links ========================================================================== */ /* * Addresses `outline` inconsistency between Chrome and other browsers. */ a:focus { outline: thin dotted; } /* * Improves readability when focused and also mouse hovered in all browsers. */ a:active, a:hover { outline: 0; } /* ========================================================================== Typography ========================================================================== */ /* * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, * Safari 5, and Chrome. */ h1 { font-size: 2em; } /* * Addresses styling not present in IE 8/9, Safari 5, and Chrome. */ abbr[title] { border-bottom: 1px dotted; } /* * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */ b, strong { font-weight: bold; } /* * Addresses styling not present in Safari 5 and Chrome. */ dfn { font-style: italic; } /* * Addresses styling not present in IE 8/9. */ mark { background: #ff0; color: #000; } /* * Corrects font family set oddly in Safari 5 and Chrome. */ code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } /* * Improves readability of pre-formatted text in all browsers. */ pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } /* * Sets consistent quote types. */ q { quotes: "\201C" "\201D" "\2018" "\2019"; } /* * Addresses inconsistent and variable font size in all browsers. */ small { font-size: 80%; } /* * Prevents `sub` and `sup` affecting `line-height` in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } /* ========================================================================== Embedded content ========================================================================== */ /* * Removes border when inside `a` element in IE 8/9. */ img { border: 0; } /* * Corrects overflow displayed oddly in IE 9. */ svg:not(:root) { overflow: hidden; } /* ========================================================================== Figures ========================================================================== */ /* * Addresses margin not present in IE 8/9 and Safari 5. */ figure { margin: 0; } /* ========================================================================== Forms ========================================================================== */ /* * Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } /* * 1. Corrects color not being inherited in IE 8/9. * 2. Remove padding so people aren't caught out if they zero out fieldsets. */ legend { border: 0; /* 1 */ padding: 0; /* 2 */ } /* * 1. Corrects font family not being inherited in all browsers. * 2. Corrects font size not being inherited in all browsers. * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome */ button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ } /* * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in * the UA stylesheet. */ button, input { line-height: normal; } /* * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` * and `video` controls. * 2. Corrects inability to style clickable `input` types in iOS. * 3. Improves usability and consistency of cursor style between image-type * `input` and others. */ button, html input[type="button"], /* 1 */ input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } /* * Re-set default cursor for disabled elements. */ button[disabled], input[disabled] { cursor: default; } /* * 1. Addresses box sizing set to `content-box` in IE 8/9. * 2. Removes excess padding in IE 8/9. */ input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /* * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome * (include `-moz` to future-proof). */ input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } /* * Removes inner padding and search cancel button in Safari 5 and Chrome * on OS X. */ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /* * Removes inner padding and border in Firefox 4+. */ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } /* * 1. Removes default vertical scrollbar in IE 8/9. * 2. Improves readability and alignment in all browsers. */ textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ } /* ========================================================================== Tables ========================================================================== */ /* * Remove most spacing between table cells. */ table { border-collapse: collapse; border-spacing: 0; } selectize.js-0.12.4/examples/css/stylesheet.css000066400000000000000000000043571277751564300215430ustar00rootroot00000000000000body { margin: 70px 0; padding: 0; font-family: Helvetica, arial, sans-serif; font-size: 15px; color: #454545; background: #fff url(../images/bg.png); text-shadow: 0 1px 0 rgba(0,0,0,0.02); -webkit-font-smoothing: antialiased; } a, a:visited { color: #3fabff; text-decoration: none; } a:hover { color: #008af5; } h1 { margin: 0; font-weight: 300; font-size: 35px; letter-spacing: -1px; } h2 { font-size: 15px; color: #a0a0a0; margin: 30px 0; } label { display: block; font-weight: bold; margin-bottom: 10px; } p, .control-group { margin: 0 0 20px 0; } .demo { border-bottom: 1px solid #e8e8e8; padding-top: 50px; padding-bottom: 50px; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } .demo:last-child { border-bottom: 0 none; } .demo select, .demo input, .demo .selectize-control { width: 100%; } .demo > *:first-child { margin-top: 0; } .demo > *:last-child { margin-bottom: 0; } .demo .value { margin: 0 0 10px 0; font-size: 12px; } .demo .value span { font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; } .theme-selector { margin-top: 10px; font-size: 13px; } .theme-selector:before { content: 'Themes: '; } .theme-selector a { margin: 0 5px; } .theme-selector a.active { color: #202020; font-weight: bold; } #wrapper { margin: 0; } #wrapper > * { padding-left: 100px; padding-right: 100px; } pre { background: #f8f8f8; border: 1px solid #f2f2f2; padding: 10px; font-size: 12px; font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } input[type=button] { margin: 0 10px 0 0; padding: 6px 10px; color: #606060; background: #e0e0e0; border: 0 none; width: auto; display: inline-block; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; -webkit-font-smoothing: antialiased; } .buttons { margin: 0 0 25px 0; } input[type=button]:hover { background: #dadada; } @media only screen and (max-width : 320px) { body { margin: 20px 0; } #wrapper { margin: 0; } #wrapper > * { padding-left: 10px; padding-right: 10px; } .demo { padding: 20px; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; } }selectize.js-0.12.4/examples/customization.html000066400000000000000000000046741277751564300216500ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Customizing Appearance

Render items on your own & apply unique CSS styles.

TODO: explain how to bind events.

selectize.js-0.12.4/examples/dynamic.html000066400000000000000000000032651277751564300203570ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Dynamic Options

The options are created straight from an array.

selectize.js-0.12.4/examples/events.html000066400000000000000000000105061277751564300202330ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js Demos

Events

Check out the console for more details about each event.

Event Log


				

Source

selectize.js-0.12.4/examples/github.html000066400000000000000000000110771277751564300202150ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Loading + Custom Scoring

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

selectize.js-0.12.4/examples/images/000077500000000000000000000000001277751564300173045ustar00rootroot00000000000000selectize.js-0.12.4/examples/images/bg.png000066400000000000000000000164631277751564300204140ustar00rootroot00000000000000PNG  IHDRddpTIDATx^uݍQڱuU!Aɾo?߿uϟ?9O_u?1x~Ϙsnq9?oϟgcθs߼:>cX^QW5 $ S!AǡY*1WZq$_ j2ZmE2}Y;VW9+mB߂nXTE8(ꓢZdy5YOQ!RCkMf)q %6ӆ[ڗlo*zD]B#!4ϒTwIjC8 ^A-Zu>@pmE8u1P@ iO:.jI gTRR㌅dW^Bq +q-P\j$|| eD7;:P·ÐvCѧ$G6b)5pC;Xj>Ot{m5.KIbSM&Pj- /i[j5u ybhs0 TQ0B*'pI-BjZk 8mX$lnvآl!&ukҢW #-Jե]s)g.Ǔ/ 鎹 dȻm~lVaZ3@;61V[K>볷!]njVT#NmJ)boJ30AĴhU["TJ{,kGe˶͗u}HdEԉlm`|<}.jasNIuwna[n 2nE mms\7 3DυNb: ȖEP =s Te6;h2&RQ]g7(.a=~YvnԎ:tQ-Qq-h-Pc@,w7.~FDsZUtoA"_@A4XtіTھ:RQIH.mAus^Qf:#αz4qjD7CkWՍHo$vx'Gg>7ZT̫T-0 _f_ir%v̯u &t1ϲ+ Pm0!~/:_u~I*OKi6o _5^_e毬p:č9Z}uE&uUۅ'h[נ#gϷr#W?YХvtoLOh.b܍cѦ= s?$ڧtr7I:c~-\A}2uXm`1ư%6B,F7tLΤ ry(S \O ^qAMs-}3v7-}K:7|}hbb7Y:i &TScP 5 X (g;9N+׵ g糖هBdf,ż^S>j(+K~s&˻[gu^OwBU] eiq( vR%fRMӧ T 48EW(]=kw-A~tqUxqt]ihdL'Zr$WpQI/?mC+%Le1-~`vmwWu5wWC_jjPES&:gHja2.Ԏxj<۲*uu .v/ =Ŕ$ e$inCGM%[|zt/Z|5N]ls_Yukd4MEIݓm"^XJ9v1Q8mjpȪ0%XV8uHws(0 l tR6랠;VQ:{7D(;JA*es(L;2 ]~A熢ЂME75" 1O,BUJQ E#(O 9E͓=tzxg@{'c AY-ﶱ};aӊn8Ki:Fn]APA5!ӢU϶'űm7ExĨ%UgS)57)EJ1.ϫA e?nA--nqw,`R0zG\ |\6I 6"0}Rs&! \~hh]P tD" W L3{'NnhGT;}DgVi&-ޢ+݉>]IvsP.hgT+b\`! G 3$Y@x}"]&ڼ0P Aa(Yx<+KnJ+Ӊj-+<%NnW?V"ֲ"ύYpqI~uEUT7갂3K{~@?ZMPrs7?_m[RB@Nb/4Ah-ՏHwo&vC;J,ZA=ջmq밺U A wN s4DPv]4-4wR('cƯt'eTW jW? [m,c[n?_saubPE )У,TQb^w=qTݔEJVы]k VE5BӦ]@)HHbHgw\$4b,~',Zv"Z*A[sO 9hh&A< ZI:ntCLO^78:!߼ <{R}mSѶ~]#ZITzݴs7m7bQ2*(D-TR4hu\=h60J1 QZ%u6)%t@n~(> 8i Xw@m'݀mmk&Yr)O7HSZY3~Gs7zJx@ [St4nKX*_Vm]I."*M֞ƴ>cvxim9jLzL\s >) nR:]n+9%eoJ Dd(c4m@{%HQ$^Zy⯈\9ք ׳YZЉuuv1KoZpnS mSi\<G~Ww_c~bXDl[ r<څt^@ ƪ+J51OF(KhZ>|w]m46K(/W7z^i7As,igu)~BkG[~= x$VY|h3dP *]{$"pV`ӹ,(AEzE,V~hBPr2cAI MVǑJ*_ӑ;]rRlC gdA&6e[@;:u3tc X>2XwB]ۍg]N@׽ԥ׷'*d^0X6UBrl?-Eoä{4eˢOJ=BL4ՑYzK}r Ųw9Cbp,Ypo|"4'C<(RCuK/tks"PcE]הYictp ""Z-Nf-[!5QOWb*-)*8kxVO?r]Mp)`8V@_b%k+k %J ϥ<\:A= .ZǠq&nwOڑɮ(8PKi/Vi" (CtW6>Rlq!Z<Vkzl^KmCXs u`V&P rl49v`tPE'vV[0Nu4Or>өOʪ)~oފqʫ`(Aꨊ1 clZL@(uQ?T=cjNGq*]2hUޜL۱cgS_s \@ -,\*ud2&Om.ؽkbPN;`InR@h֑RNMB7-9ɶptCh\'nT:@XTow5'XauގQHg=\>n #! ^"frgE:PB"&qiZM Ä')= TT]g]Xiz Q ʚȽiҨ.[0Pk%pCE]WX [q6^C-m^Y5H$ h-G1tb]ߌ-ϵͺЎn8:]*zfޤt;&͵}[D9ux^іEIuM-Eh1|kek@\E F'%MUs\!%qTL+;զG4p%njNĻAimL +n¨6Uw/?ιvEn!8󊃉ꌍ-\~huMHAXtKbH%??]͍?ᵨC f博9l'(e) pNAQAל"ќD)LvvL;MۮvVV r{?)Zц)Hr}# eSguk>}i*"AkCseZl`7ڼf:5¾;cDOZY MUXEgu4]ꢃrd|4vE7\}˼S'SDN@u]vG.ѐV;UBU|֞Z@_ڕ5E6ZRj0חNzг=Vԑ:44FqtTAf$B".{=yY)@zک]vu@̹G&ˡˆVX9qѸ !κ~sMhZ%qbrQDB/)ᮌSs=+58I̊us}r_&ڤ!w{lIfRK(t-P@(uw^ۆҭB{:-oa nƭ+/ە_ İCۙRl͒kk_ԡ]IxHw3` ": r@ }գP"@$5-7w;ҫ%t5l19(H(]qZ8:,Ңv`x[ȠVB]5Ehz4yQ7]xiR$-nP h`x'LP`E쓈N{k5Mľ }$.?%3+NP>K*2ϲѹO|3W$c~ځZᆭVg>0'VT]?zE8H3ջC׌+ *Z,8* J 8l? $Tk5bCsoA:CYrZJ!6w$l.g>o=g!)Փ]pZJF]_N6vgcE9rv&T"힠Z@)n#0y(fW+*۠ >^'6~iOpc}v-Z\d D Pk nJk8 Xߢѵkj-taRjtWFW⵽_ϏL(=g!k;VG!sIv !1hc^-Jz ʹ{?c-)q[t`^]a=N?t|^0ٝD!VbJA:$sIX7U4VhN*mcD:չuu tU2ggꞽ%p O}uYdC!1C eksed *п5Jϛlהnt\(bHV*4{0 jAg5[2 $o:Q 4E(Puo0EǦ&XM9&eIENDB`selectize.js-0.12.4/examples/images/check@2x.png000066400000000000000000000140411277751564300214410ustar00rootroot00000000000000PNG  IHDR( :U pHYs%%IR$xiCCPPhotoshop ICC profilexڭWPT߲{I0̐sr93s$I@ ( " "(QT9}U~շVU{W/rjll$dn$%H<`!_Z9ej<_4wgۮ%8l[O.ﲭ 3ܮ7Hm$sv l-؀) CDCD b!`(h$#h '# ^ڷ %hi'Q:eX/Q HO \ j ` V`. >adC.@ Ti825=p0$L<|e2‚p!("(!b N'⇄ Hُ"EHR#WHA#3"@1( eEP1T@ Qk݉qh:zCZڎ1tbCaP0 1 cbr0ZL#ӇLc0?x, V+Z`]8^Ql{;.c8^4N gRpٸRy\n 7xu߅??owGsUE& TB"!ppp0OXQ1񢉦ɢ)Hs &-#(=m mm>YNǴD&8QB '#/+tttBtttattttf~IR$c7)G#uVdـEN$wȯ,rO?12220322qT&NH3C-5DڲՙҥN UMS  % /~2R07j3Z32cm11712e6u50}e&db`lj˼gamQh1ng`Yoln5ٺzF&ަ=nN.=[rwsr;:8V:uRt|Kˤkk[ۚ{Gܞa^/7^;LwV~S|g·>>>7}|~8?w~?Z꪿rq@Y@ ݠ`w!!CCCKCŒ*¾[_؊tl}7?&5f$V:6;v:N+$n9:|3#516q I"`L^rez[Jk*Sjt@Tڑtsv޷{fរ^233/#7Um|24?ؐM=~Ha(9qwN`NBniϣG)+?7_]PBSQzqł9J|K.#%M۔w9QpgEhXQeSoՑ'28xtZ33gޞu;wN\yEM_pp^"!a&;ek؛r9+Z[z[5Z^jciiGڗ^nuSKFMں~{;{'g׷wǝwݳ;}}>P~k  ?V1993rQO->ylb{|z"p_^$؜|{38UUkMj7gLffg'>Ixs[wJn,-~CͥL>I|/[_p}Sֻ{͵u ?4~mo,l$,%[Q[[x*` Z@` (!r$+# 4igk$Yޚ!iU-cKS'*Dj~"I `B~H#ߦpV@ir:OyU:u d ؍MhM̗-YN[MXܳajcE.MWZ[=z^j}egOo_ y۠o[taBґQі1Aq)'NHJ&nwԽ9ejN/?0;{סGrr g.+-;XYB).S)=aZa[ZdӉՙ55g{}zދ F\/46n.Rzvk;tuMܘ֛W{1wJӹ aNգWG6|>ljw^xdjՃ>z8#-r ge/J_9~]vb5/?{~Rq+E{Y/tD(ٔ>!iE՗SN2-AnG)W eHrJIK vʝ?d¡KZzFf.3FY)&fnzVl6uv/G8:rrpmskvo8YUC>Y~,UAu!aGEzHI4JHNٟZv)'Y@3Hvp1{{G:rrO-8URR~<8dinUm'o=Y͛ճst.^mȼTpfs׭.ou௓;Yo%s[[ǪN݌{u>|8u;>$XuhS1g>~^P'/SWӡ3rT7Zx7%GOb忈2g5u?on6<+ٖC,DV&\R?|֩h MC0{o(%L,\*B>R)J5Z3F?<17!6130&Jjw]vE3}YHַo<=؛t#I9\ۣDŽ~,-|Y4q|xyDDgO+GFN>>5xzzYԙgma/.7]Ҩդl|ŤŴQA5ʝ]7n +rG=}F'?8zu>Vw4v:juaH."W7*^sj~[ :AW ..L~!R"$"6&^!').QU:MF_/XR>TACNRrJ:&6EW^O_ Qqɜ\2uMݚSsKw^{,;w5P GBY<«#bLI)oR]vn߫ѼO1+d-?ƒWXTX|Tm\U)5Üx%FLӋ+W[ 9^W袻1{;}oa^#F cJ]'˧6gzGoZ_{dk_50(yP p&a! :D FT5C#" }aa01o,{Se|]/!0JDSL֏N"$$3l9deY:󔂔 TxU6U7ihVhUj4kecemϬ|fh\|\r_Q=C5K^(\1L%|oDWtDLwW|rxNT=vSLd8ff<ӑ\y}ECŮ%"7*VQNv]pGq?VJg)$aƕv7іI'AWN!Ⱦ6z301S?egfSb#Ss+'/_X`[J0#\Q[1&QR82ar }7T^7h\lЙg7P50k\g2j1W,괞Sr8xiхݭ}ؓyG*!HCXmZ}TC 16*n,(%Y<:7r7 ̶,Of<\Cm>RrN[S+pٹ:zы4nNkqӁ^_s'=>p@ȏgcMFO K?к8}f[[ J^HFW[[+b"_[[[[`^tGP-~]qm Paɜ cHRMmspZ0~v#IDATxڴۑ0 E/$U ( Yl8eiᕜچ8,,x.Sᜳ.En }DGYV~ͱb5pj3pl?SC7-y )t\ F%]ttsiK2/ %06)8l\+> 9cA19E@<_ l7Py A2Hl") rA [Z  l?T,as$<;$&%' @XWz9p58agv⟞)dy=( L}0 GpO H$X8yBqq8.WZpq7pw<{9D__?J%$2KfB0H%Dk1%ʉ%mă3!'d@r$I*R%4:iF6%{#|ry=y||`־Y^F^\=^l>>\Z'ƾ|,2,# ,  zl, n  qY zq8#aa·c«ßDXE"gfopɜHDn|e\ܨ5sF;D/a,9!?v}88E\gj  3%^MI%%' e|%o/0_BⅧ..:OIH9ɭ厦rRx[y/aB,+"KI8^J ؕ132.s\ n"edhH2%iGΖYl. MΔK <:OWϗwX-xV\ޒΥKW.Xlrdy+W կ\*UޯNX^W\Tg/NR_]hq56if5Ê/~I#Gݎ63=8xi R2Қߖw"Dgg_~;ix)O)<3!xuVxvsQsn{BK/:sO\qzjKsߜ;rZ[{o{߬}xMͫww>'~EK=|wߛ]O t?y`7???|fý/z)}9O?x}/߿GGތ-Q?d}XISg]_<[gK2.`'0z(*L9\|":vv: P''rӜ'h2wzvo`:r&7A œemeѿg7oa: cHRMmusdph0>'IDATxڜ1r@ ?9;Cj(( `r )bHh8EUJ8(IVvW/ɿdKsxt9-{vV.߭Gñ`4NE93raEoRu2HADW$vGQS@&Tx:9(Xݳȷs@.螏`ZӎK蓑Yu "yN)i*&0Qs6Űly oJ"=ORC3t̲̉#r)R7%1֡<*  x%^ؚ/=QӓVt>cҐGڎBt\6͜4iVThdpI+5(S'Q>}dЂ~%V|6!$7Y]L+sqt*)";W4#?%c#x.߁{$;'iP7=m&'"6ޮ]<}~,f~@X *֡{IENDB`selectize.js-0.12.4/examples/images/repo-source.png000066400000000000000000000065221277751564300222620ustar00rootroot00000000000000PNG  IHDR szz pHYs%%IR$ iCCPPhotoshop ICC profilexڭwP -!RBH.P6BB l,ZP(@ւX .l?X½wwgss+Q5,\NLJfZr~QQ p\T [<$ R,'X0Y,|`r` 'x70S'0e%˕ 7^Ih&Kh|6E?3U <1 ER1ߕ%VL̘p0@yܠINp"&Y*d(B&Y7əJ$uN?7 y c&/ dYvR43ܰI&Y :s,i`F;5<=6TɲX&MQ+ G)=eh=$qJO>7Py A2Hl") rA [Z  l?T,as$<;$&%' @XWz9p58agv⟞)dy=( L}0 GpO H$X8yBqq8.WZpq7pw<{9D__?J%$2KfB0H%Dk1%ʉ%mă3!'d@r$I*R%4:iF6%{#|ry=y||`־Y^F^\=^l>>\Z'ƾ|,2,# ,  zl, n  qY zq8#aa·c«ßDXE"gfopɜHDn|e\ܨ5sF;D/a,9!?v}88E\gj  3%^MI%%' e|%o/0_BⅧ..:OIH9ɭ厦rRx[y/aB,+"KI8^J ؕ132.s\ n"edhH2%iGΖYl. MΔK <:OWϗwX-xV\ޒΥKW.Xlrdy+W կ\*UޯNX^W\Tg/NR_]hq56if5Ê/~I#Gݎ63=8xi R2Қߖw"Dgg_~;ix)O)<3!xuVxvsQsn{BK/:sO\qzjKsߜ;rZ[{o{߬}xMͫww>'~EK=|wߛ]O t?y`7???|fý/z)}9O?x}/߿GGތ-Q?d}XISg]_<[gK2.`'0z(*L9\|":vv: P''rӜ'h2wzvo`:r&7A œemeѿg7oa: cHRMmusdph0>IDATxڬ?nA ެDBH nB."A\cF 'Lc{9ue,z%x<@vQRv# <(: SqG 9b"ABZ)F%^D+JȠTVfN&Ce~rE<8o[h<9TJ׎~dd7T,.f;@<ɷf\9ˀ2PFӠde*x ڴ.׎^ u\ee4~|ΰ :st`E |'0ћ-V[&+FF^%\znKA6w𪨄|orD]x< rFIENDB`selectize.js-0.12.4/examples/images/spinner.gif000066400000000000000000000062101277751564300214500ustar00rootroot00000000000000GIF89a Ƅ666VVV似! NETSCAPE2.0!Created with ajaxload.info! , IiabK$F RAT,2S*05//mp!z0;$0C.I*!HC(A@o!39T5\8) `dwxG=Y gwHbvA=0 V\\; ;H0t%HsrY'e$"\#E1CnĎ~J,,AaUw^4I%Pu Q33{0i1TGgwy}%%'R  = 3G%p0 JRo5Ȇ0IĦmykxT_}(^yKs>i_%n=q4e-M¤D! , I)*')Ed]PR A:!zrbw %6"G(d$["JFhaQP`p%†/BFP\cU ?TtW/pG&OtDa_sylD'M q tc b2DM : d% 4%s) uE3 YUtږD$JiM^%o/rvl9'L;99% i9 C "B BDs ^Xf}$P {L?P O4 E咛V$dJ#)pV$! , IiRͧ"Jd] RZN*P*;$P{*N\EА!1UO2D _r6Ib H8 B; "'ZtbK#C'Kw}?Kiz6:xKAC&}9tz\ \D5;x Qd( KW  MBIڈM=ˤs⸽8DaJ`@LG! , IiRͧ"Jd] RZN*P*;$P{*N\EА!1UO2D _r6Ib H8 B; "'ZtbK#C'KGziz68}z~%XK9:0}% tz\Blc LbQ   lj ųKň x(țPX ,ւ|/"! , IiRͧ"Jd] RZN*P*;$P{*N\EА!1UO2D _r6Ib H8 B; "'ZtbK#C'KGziz68}z~%:A/ C} u\ h}b D]=  V)  ڊ9CDK Ku *00StD! , IiRͧ"Jd] RZN*P*;$P{*N\EА!1UO2D _r6Ib H8 B; "'ZtbK#C'KGz z5 C: A/ C}u\ Eh}b6[=Wx&)I9Ԭ@oCT?Kd]B76ЫD! , IiRͧ"Jd] RZN*P*;$P{*N\EА!1UO2D _r6I ƀH03hոaj U {CIkmbK#cK8 {a8nV:/q:M Cu~Ehk6 [_6P.]6!)V! , IiRͧ"Jd]U RZN JjN2sK6 dI)  LHWG 6 KX젱.6d~zhuur/6 X5I;_t O#E {O9V94;VC/ 6Ø~*'MonbX:~]+V*mK_OrKN@.d~qЦDB֋ 5D;selectize.js-0.12.4/examples/js/000077500000000000000000000000001277751564300164535ustar00rootroot00000000000000selectize.js-0.12.4/examples/js/index.js000066400000000000000000000031451277751564300201230ustar00rootroot00000000000000$(function() { var $wrapper = $('#wrapper'); // theme switcher var theme_match = String(window.location).match(/[?&]theme=([a-z0-9]+)/); var theme = (theme_match && theme_match[1]) || 'default'; var themes = ['default','legacy','bootstrap2','bootstrap3']; $('head').append(''); var $themes = $('
').addClass('theme-selector').insertAfter('h1'); for (var i = 0; i < themes.length; i++) { $themes.append('' + themes[i] + ''); } // display scripts on the page $('script', $wrapper).each(function() { var code = this.text; if (code && code.length) { var lines = code.split('\n'); var indent = null; for (var i = 0; i < lines.length; i++) { if (/^[ ]*$/.test(lines[i])) continue; if (!indent) { var lineindent = lines[i].match(/^([ ]+)/); if (!lineindent) break; indent = lineindent[1]; } lines[i] = lines[i].replace(new RegExp('^' + indent), ''); } var code = $.trim(lines.join('\n')).replace(/ /g, ' '); var $pre = $('
').addClass('js').text(code);
			$pre.insertAfter(this);
		}
	});

	// show current input values
	$('select.selectized,input.selectized', $wrapper).each(function() {
		var $container = $('
').addClass('value').html('Current Value: '); var $value = $('').appendTo($container); var $input = $(this); var update = function(e) { $value.text(JSON.stringify($input.val())); } $(this).on('change', update); update(); $container.insertAfter($input); }); });selectize.js-0.12.4/examples/lock.html000066400000000000000000000047761277751564300176730ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Locking

Selectize controls can be locked to prevent user interaction.

selectize.js-0.12.4/examples/movies.html000066400000000000000000000114641277751564300202350ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Loading from API

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

selectize.js-0.12.4/examples/optgroups.html000066400000000000000000000141071277751564300207720ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Optgroups (basic)

Optgroups (repeated options)

Optgroups (programmatic)

Plugin: "optgroup_columns"

selectize.js-0.12.4/examples/performance.html000066400000000000000000000034701277751564300212320ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Performance

This shows how it performs with 25,000 items.

selectize.js-0.12.4/examples/plugins.html000066400000000000000000000074621277751564300204170ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Plugin: "remove_button"

Plugin: "restore_on_backspace"

Plugin: "drag_drop"

Plugin: "dropdown_header"

selectize.js-0.12.4/examples/required.html000066400000000000000000000032751277751564300205540ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Required Element

selectize.js-0.12.4/examples/rtl.html000066400000000000000000000035141277751564300175310ustar00rootroot00000000000000 Selectize.js Demo

Selectize.js

Right-to-left Support (RTL)

Right-to-left Support (RTL) – Single

selectize.js-0.12.4/karma.conf.js000066400000000000000000000073131277751564300166020ustar00rootroot00000000000000module.exports = function(config) { // workaround for https://github.com/karma-runner/karma-sauce-launcher/issues/40 var saucelabsBatchID = Number(process.env.SAUCELABS_BATCH) - 1; var saucelabsConcurrency = 4; var saucelabsBrowsers = [ // mobile {platform: 'OS X 10.10', browserName: 'iPhone', version: '8.1'}, //{platform: 'OS X 10.10 ', browserName: 'iPhone', version: '6.0'}, {platform: 'OS X 10.10', browserName: 'iPad', version: '8.1'}, //{platform: 'OS X 10.10', browserName: 'iPad', version: '6.0'}, {platform: 'Linux', browserName: 'android', version: '4.4'}, {platform: 'Linux', browserName: 'android', version: '4.3'}, // desktop (safari) {platform: 'OS X 10.8', browserName: 'safari', version: 6}, {platform: 'OS X 10.9', browserName: 'safari', version: 7}, {platform: 'OS X 10.10', browserName: 'safari', version: 8}, // desktop (chrome) {platform: 'OS X 10.10', browserName: 'chrome', version: 39}, {platform: 'OS X 10.10', browserName: 'chrome', version: 38}, {platform: 'OS X 10.10', browserName: 'chrome', version: 37}, {platform: 'Windows 7', browserName: 'chrome', version: 39}, {platform: 'Windows 7', browserName: 'chrome', version: 38}, {platform: 'Windows 7', browserName: 'chrome', version: 37}, // desktop (firefox) {platform: 'Windows 7', browserName: 'firefox', version: 35}, {platform: 'Windows 8', browserName: 'firefox', version: 35}, {platform: 'OS X 10.10', browserName: 'firefox', version: 34}, {platform: 'OS X 10.10', browserName: 'firefox', version: 33}, {platform: 'OS X 10.10', browserName: 'firefox', version: 32}, // desktop (internet explorer) {platform: 'Windows 8', browserName: 'iexplore', version: 10}, {platform: 'Windows 8.1', browserName: 'iexplore', version: 11}, {platform: 'Windows 7', browserName: 'iexplore', version: 9} ]; if (process.env.TARGET === 'saucelabs') { saucelabsBrowsers = saucelabsBrowsers.slice(saucelabsBatchID * saucelabsConcurrency, saucelabsBatchID * saucelabsConcurrency + saucelabsConcurrency); if (!saucelabsBrowsers.length) process.exit(0); } var customLaunchers = {}; saucelabsBrowsers.forEach(function(browser, i) { browser.base = 'SauceLabs'; customLaunchers['SL_' + i] = browser; }); var targets = { 'saucelabs': Object.keys(customLaunchers), 'phantomjs': ['PhantomJS'] }; var reporters = ['mocha']; if (process.env.TRAVIS_CI) { reporters = process.env.TARGET === 'saucelabs' ? ['saucelabs', 'mocha'] : ['mocha', 'coverage', 'coveralls'] } var browsers = targets[process.env.TARGET || 'phantomjs']; if (process.env.BROWSERS) { browsers = process.env.BROWSERS.split(','); } config.set({ frameworks: ['mocha', 'chai'], files: [ 'dist/css/selectize.default.css', 'bower_components/jquery/jquery.js', 'bower_components/microplugin/src/microplugin.js', 'bower_components/sifter/sifter.js', 'test/support/*.js', 'src/contrib/*.js', 'src/constants.js', 'src/utils.js', 'src/selectize.js', 'src/defaults.js', 'src/selectize.jquery.js', 'test/*.js' ], preprocessors: { 'src/*.js': ['coverage'] }, coverageReporter: { type: process.env.TRAVIS_CI && process.env.TARGET === 'phantomjs' ? 'lcov' : 'text-summary', dir: 'coverage/' }, sauceLabs: { recordVideo: false, startConnect: true, tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER, build: process.env.TRAVIS_BUILD_NUMBER, testName: process.env.COMMIT_MESSAGE, tags: ['selectize', 'test'] }, customLaunchers: customLaunchers, reporters: reporters, port: 8888, colors: true, captureTimeout: 0, logLevel: config.LOG_INFO, browsers: browsers, browserDisconnectTolerance: 2, browserDisconnectTimeout: 10000, browserNoActivityTimeout: 120000, singleRun: true }); };selectize.js-0.12.4/package.json000066400000000000000000000030301277751564300165030ustar00rootroot00000000000000{ "name": "selectize", "keywords": [ "select", "ui", "form", "input", "control", "autocomplete", "tagging", "tag" ], "main": "dist/js/selectize.js", "description": "Selectize is a jQuery-based custom UI control. Useful for tagging, contact lists, country selectors, etc.", "homepage": "http://brianreavis.github.io/selectize.js/", "demo": "http://brianreavis.github.io/selectize.js/", "docs": "https://github.com/brianreavis/selectize.js", "bugs": "https://github.com/brianreavis/selectize.js/issues", "licenses": [ { "type": "Apache License, Version 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html" } ], "maintainers": [ { "name": "Brian Reavis", "email": "brian@thirdroute.com", "url": "http://thirdroute.com" } ], "dependencies": { "jquery": ">=1.6" } } selectize.js-0.12.4/src/000077500000000000000000000000001277751564300150105ustar00rootroot00000000000000selectize.js-0.12.4/src/.wrapper.js000066400000000000000000000022341277751564300171050ustar00rootroot00000000000000/** * selectize.js (v@@version) * Copyright (c) 2013–2015 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ /*jshint curly:false */ /*jshint browser:true */ (function(root, factory) { if (typeof define === 'function' && define.amd) { define(['jquery','sifter','microplugin'], factory); } else if (typeof exports === 'object') { module.exports = factory(require('jquery'), require('sifter'), require('microplugin')); } else { root.Selectize = factory(root.jQuery, root.Sifter, root.MicroPlugin); } }(this, function($, Sifter, MicroPlugin) { 'use strict'; @@js return Selectize; }));selectize.js-0.12.4/src/constants.js000066400000000000000000000013021277751564300173560ustar00rootroot00000000000000var IS_MAC = /Mac/.test(navigator.userAgent); var KEY_A = 65; var KEY_COMMA = 188; var KEY_RETURN = 13; var KEY_ESC = 27; var KEY_LEFT = 37; var KEY_UP = 38; var KEY_P = 80; var KEY_RIGHT = 39; var KEY_DOWN = 40; var KEY_N = 78; var KEY_BACKSPACE = 8; var KEY_DELETE = 46; var KEY_SHIFT = 16; var KEY_CMD = IS_MAC ? 91 : 17; var KEY_CTRL = IS_MAC ? 18 : 17; var KEY_TAB = 9; var TAG_SELECT = 1; var TAG_INPUT = 2; // for now, android support in general is too spotty to support validity var SUPPORTS_VALIDITY_API = !/android/i.test(window.navigator.userAgent) && !!document.createElement('input').validity; selectize.js-0.12.4/src/contrib/000077500000000000000000000000001277751564300164505ustar00rootroot00000000000000selectize.js-0.12.4/src/contrib/highlight.js000066400000000000000000000031421277751564300207550ustar00rootroot00000000000000/** * highlight v3 | MIT license | Johann Burkard * Highlights arbitrary terms in a node. * * - Modified by Marshal 2011-6-24 (added regex) * - Modified by Brian Reavis 2012-8-27 (cleanup) */ var highlight = function($element, pattern) { if (typeof pattern === 'string' && !pattern.length) return; var regex = (typeof pattern === 'string') ? new RegExp(pattern, 'i') : pattern; var highlight = function(node) { var skip = 0; if (node.nodeType === 3) { var pos = node.data.search(regex); if (pos >= 0 && node.data.length > 0) { var match = node.data.match(regex); var spannode = document.createElement('span'); spannode.className = 'highlight'; var middlebit = node.splitText(pos); var endbit = middlebit.splitText(match[0].length); var middleclone = middlebit.cloneNode(true); spannode.appendChild(middleclone); middlebit.parentNode.replaceChild(spannode, middlebit); skip = 1; } } else if (node.nodeType === 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) { for (var i = 0; i < node.childNodes.length; ++i) { i += highlight(node.childNodes[i]); } } return skip; }; return $element.each(function() { highlight(this); }); }; /** * removeHighlight fn copied from highlight v5 and * edited to remove with() and pass js strict mode */ $.fn.removeHighlight = function() { return this.find("span.highlight").each(function() { this.parentNode.firstChild.nodeName; var parent = this.parentNode; parent.replaceChild(this.firstChild, this); parent.normalize(); }).end(); }; selectize.js-0.12.4/src/contrib/microevent.js000066400000000000000000000027371277751564300211720ustar00rootroot00000000000000/** * MicroEvent - to make any js object an event emitter * * - pure javascript - server compatible, browser compatible * - dont rely on the browser doms * - super simple - you get it immediatly, no mistery, no magic involved * * @author Jerome Etienne (https://github.com/jeromeetienne) */ var MicroEvent = function() {}; MicroEvent.prototype = { on: function(event, fct){ this._events = this._events || {}; this._events[event] = this._events[event] || []; this._events[event].push(fct); }, off: function(event, fct){ var n = arguments.length; if (n === 0) return delete this._events; if (n === 1) return delete this._events[event]; this._events = this._events || {}; if (event in this._events === false) return; this._events[event].splice(this._events[event].indexOf(fct), 1); }, trigger: function(event /* , args... */){ this._events = this._events || {}; if (event in this._events === false) return; for (var i = 0; i < this._events[event].length; i++){ this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)); } } }; /** * Mixin will delegate all MicroEvent.js function in the destination object. * * - MicroEvent.mixin(Foobar) will make Foobar able to use MicroEvent * * @param {object} the object which will support MicroEvent */ MicroEvent.mixin = function(destObject){ var props = ['on', 'off', 'trigger']; for (var i = 0; i < props.length; i++){ destObject.prototype[props[i]] = MicroEvent.prototype[props[i]]; } };selectize.js-0.12.4/src/defaults.js000066400000000000000000000041521277751564300171570ustar00rootroot00000000000000Selectize.count = 0; Selectize.defaults = { options: [], optgroups: [], plugins: [], delimiter: ',', splitOn: null, // regexp or string for splitting up values from a paste command persist: true, diacritics: true, create: false, createOnBlur: false, createFilter: null, highlight: true, openOnFocus: true, maxOptions: 1000, maxItems: null, hideSelected: null, addPrecedence: false, selectOnTab: false, preload: false, allowEmptyOption: false, closeAfterSelect: false, scrollDuration: 60, loadThrottle: 300, loadingClass: 'loading', dataAttr: 'data-data', optgroupField: 'optgroup', valueField: 'value', labelField: 'text', optgroupLabelField: 'label', optgroupValueField: 'value', lockOptgroupOrder: false, sortField: '$order', searchField: ['text'], searchConjunction: 'and', mode: null, wrapperClass: 'selectize-control', inputClass: 'selectize-input', dropdownClass: 'selectize-dropdown', dropdownContentClass: 'selectize-dropdown-content', dropdownParent: null, copyClassesToDropdown: true, /* load : null, // function(query, callback) { ... } score : null, // function(search) { ... } onInitialize : null, // function() { ... } onChange : null, // function(value) { ... } onItemAdd : null, // function(value, $item) { ... } onItemRemove : null, // function(value) { ... } onClear : null, // function() { ... } onOptionAdd : null, // function(value, data) { ... } onOptionRemove : null, // function(value) { ... } onOptionClear : null, // function() { ... } onOptionGroupAdd : null, // function(id, data) { ... } onOptionGroupRemove : null, // function(id) { ... } onOptionGroupClear : null, // function() { ... } onDropdownOpen : null, // function($dropdown) { ... } onDropdownClose : null, // function($dropdown) { ... } onType : null, // function(str) { ... } onDelete : null, // function(values) { ... } */ render: { /* item: null, optgroup: null, optgroup_header: null, option: null, option_create: null */ } }; selectize.js-0.12.4/src/less/000077500000000000000000000000001277751564300157565ustar00rootroot00000000000000selectize.js-0.12.4/src/less/.wrapper.css000066400000000000000000000012711277751564300202270ustar00rootroot00000000000000/** * selectize.css (v@@version) * Copyright (c) 2013–2015 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ @@cssselectize.js-0.12.4/src/less/selectize.bootstrap2.less000066400000000000000000000125441277751564300227410ustar00rootroot00000000000000/** * selectize.bootstrap2.css (v@@version) - Bootstrap 2 Theme * Copyright (c) 2013–2015 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ @import "selectize"; @selectize-font-family: @baseFontFamily; @selectize-font-size: @baseFontSize; @selectize-line-height: @baseLineHeight; @selectize-color-text: @textColor; @selectize-color-highlight: rgba(255,237,40,0.4); @selectize-color-input: @inputBackground; @selectize-color-input-full: @inputBackground; @selectize-color-disabled: @inputBackground; @selectize-color-item: @btnBackgroundHighlight; @selectize-color-item-border: @btnBorder; @selectize-color-item-active: @dropdownLinkBackgroundHover; @selectize-color-item-active-text: @dropdownLinkColorHover; @selectize-color-item-active-border: darken(@selectize-color-item-active, 5%); @selectize-color-optgroup: @dropdownBackground; @selectize-color-optgroup-text: @grayLight; @selectize-color-optgroup-border: @dropdownDividerTop; @selectize-color-dropdown: @dropdownBackground; @selectize-color-dropdown-border: @inputBorder; @selectize-color-dropdown-border-top: @dropdownDividerTop; @selectize-color-dropdown-item-active: @dropdownLinkBackgroundHover; @selectize-color-dropdown-item-active-text: @dropdownLinkColorHover; @selectize-color-dropdown-item-create-active-text: @dropdownLinkColorHover; @selectize-lighten-disabled-item: 8%; @selectize-lighten-disabled-item-text: 8%; @selectize-lighten-disabled-item-border: 8%; @selectize-opacity-disabled: 0.5; @selectize-shadow-input: none; @selectize-shadow-input-focus: inset 0 1px 2px rgba(0,0,0,0.15); @selectize-border-radius: @inputBorderRadius; @selectize-padding-x: 10px; @selectize-padding-y: 7px; @selectize-padding-dropdown-item-x: @selectize-padding-x; @selectize-padding-dropdown-item-y: 3px; @selectize-padding-item-x: 3px; @selectize-padding-item-y: 1px; @selectize-margin-item-x: 3px; @selectize-margin-item-y: 3px; @selectize-caret-margin: 0; @selectize-arrow-size: 5px; @selectize-arrow-color: @black; @selectize-arrow-offset: @selectize-padding-x + 5px; @selectize-width-item-border: 1px; .selectize-dropdown { margin: 2px 0 0 0; z-index: @zindexDropdown; border: 1px solid @dropdownBorder; border-radius: @baseBorderRadius; .box-shadow(0 5px 10px rgba(0,0,0,.2)); .optgroup-header { font-size: 11px; font-weight: bold; text-shadow: 0 1px 0 rgba(255,255,255,.5); text-transform: uppercase; } .optgroup:first-child:before { display: none; } .optgroup:before { content: ' '; display: block; .nav-divider(); margin-left: @selectize-padding-dropdown-item-x * -1; margin-right: @selectize-padding-dropdown-item-x * -1; } [data-selectable].active { #gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%)); } } .selectize-dropdown-content { padding: 5px 0; } .selectize-dropdown-header { padding: @selectize-padding-dropdown-item-y * 2 @selectize-padding-dropdown-item-x; } .selectize-input { .transition(~"border linear .2s, box-shadow linear .2s"); &.dropdown-active { .selectize-border-radius(@selectize-border-radius); } &.dropdown-active::before { display: none; } &.input-active, &.input-active:hover, .selectize-control.multi &.focus { background: @selectize-color-input !important; border-color: rgba(82,168,236,.8) !important; outline: 0 !important; outline: thin dotted \9 !important; .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6)") !important; } } .selectize-control { &.single { .selectize-input { .buttonBackground(@btnBackground, @btnBackgroundHighlight, @grayDark, 0 1px 1px rgba(255,255,255,.75)); .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); &:hover { color: @grayDark; text-decoration: none; background-position: 0 -15px; .transition(background-position .1s linear); } &.disabled { background: @btnBackgroundHighlight !important; .box-shadow(none); } } } &.multi { .selectize-input { .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); &.has-items { @padding-x: @selectize-padding-x - @selectize-padding-item-x; padding-left: @padding-x; padding-right: @padding-x; } } .selectize-input > div { .gradientBar(@btnBackground, @btnBackgroundHighlight, @selectize-color-item-text, none); *background-color: @selectize-color-item; border: @selectize-width-item-border solid @selectize-color-item-border; .border-radius(@baseBorderRadius); .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05)"); &.active { .box-shadow(~"0 1px 2px rgba(0,0,0,.05)"); .gradientBar(@selectize-color-item-active, @selectize-color-item-active-border, @selectize-color-item-active-text, none); *background-color: @selectize-color-item-active; border: @selectize-width-item-border solid @dropdownLinkBackgroundHover; } } } }selectize.js-0.12.4/src/less/selectize.bootstrap3.less000066400000000000000000000113761277751564300227440ustar00rootroot00000000000000/** * selectize.bootstrap3.css (v@@version) - Bootstrap 3 Theme * Copyright (c) 2013–2015 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ @import "selectize"; @selectize-font-family: inherit; @selectize-font-size: inherit; @selectize-line-height: @line-height-computed; @selectize-color-text: @text-color; @selectize-color-highlight: rgba(255,237,40,0.4); @selectize-color-input: @input-bg; @selectize-color-input-full: @input-bg; @selectize-color-input-error: @state-danger-text; @selectize-color-input-error-focus: darken(@selectize-color-input-error, 10%); @selectize-color-disabled: @input-bg; @selectize-color-item: #efefef; @selectize-color-item-border: rgba(0,0,0,0); @selectize-color-item-active: @component-active-bg; @selectize-color-item-active-text: #fff; @selectize-color-item-active-border: rgba(0,0,0,0); @selectize-color-optgroup: @dropdown-bg; @selectize-color-optgroup-text: @dropdown-header-color; @selectize-color-optgroup-border: @dropdown-divider-bg; @selectize-color-dropdown: @dropdown-bg; @selectize-color-dropdown-border-top: mix(@input-border, @input-bg, 0.8); @selectize-color-dropdown-item-active: @dropdown-link-hover-bg; @selectize-color-dropdown-item-active-text: @dropdown-link-hover-color; @selectize-color-dropdown-item-create-active-text: @dropdown-link-hover-color; @selectize-opacity-disabled: 0.5; @selectize-shadow-input: none; @selectize-shadow-input-focus: inset 0 1px 2px rgba(0,0,0,0.15); @selectize-shadow-input-error: inset 0 1px 1px rgba(0, 0, 0, .075); @selectize-shadow-input-error-focus: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px lighten(@selectize-color-input-error, 20%); @selectize-border: 1px solid @input-border; @selectize-border-radius: @input-border-radius; @selectize-width-item-border: 0; @selectize-padding-x: @padding-base-horizontal; @selectize-padding-y: @padding-base-vertical; @selectize-padding-dropdown-item-x: @padding-base-horizontal; @selectize-padding-dropdown-item-y: 3px; @selectize-padding-item-x: 3px; @selectize-padding-item-y: 1px; @selectize-margin-item-x: 3px; @selectize-margin-item-y: 3px; @selectize-caret-margin: 0; @selectize-arrow-size: 5px; @selectize-arrow-color: @selectize-color-text; @selectize-arrow-offset: @selectize-padding-x + 5px; .selectize-dropdown, .selectize-dropdown.form-control { height: auto; padding: 0; margin: 2px 0 0 0; z-index: @zindex-dropdown; background: @selectize-color-dropdown; border: 1px solid @dropdown-fallback-border; border: 1px solid @dropdown-border; .selectize-border-radius(@border-radius-base); .selectize-box-shadow(0 6px 12px rgba(0,0,0,.175)); } .selectize-dropdown { .optgroup-header { font-size: @font-size-small; line-height: @line-height-base; } .optgroup:first-child:before { display: none; } .optgroup:before { content: ' '; display: block; .nav-divider(); margin-left: @selectize-padding-dropdown-item-x * -1; margin-right: @selectize-padding-dropdown-item-x * -1; } } .selectize-dropdown-content { padding: 5px 0; } .selectize-dropdown-header { padding: @selectize-padding-dropdown-item-y * 2 @selectize-padding-dropdown-item-x; } .selectize-input { min-height: @input-height-base; &.dropdown-active { .selectize-border-radius(@selectize-border-radius); } &.dropdown-active::before { display: none; } &.focus { @color: @input-border-focus; @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); border-color: @color; outline: 0; .selectize-box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); } } .has-error .selectize-input { border-color: @selectize-color-input-error; .selectize-box-shadow(@selectize-shadow-input-error); &:focus { border-color: @selectize-color-input-error-focus; .selectize-box-shadow(@selectize-shadow-input-error-focus); } } .selectize-control { &.multi { .selectize-input.has-items { padding-left: @selectize-padding-x - @selectize-padding-item-x; padding-right: @selectize-padding-x - @selectize-padding-item-x; } .selectize-input > div { .selectize-border-radius(@selectize-border-radius - 1px); } } } .form-control.selectize-control { padding: 0; height: auto; border: none; background: none; .selectize-box-shadow(none); .selectize-border-radius(0); } selectize.js-0.12.4/src/less/selectize.default.less000066400000000000000000000044201277751564300222600ustar00rootroot00000000000000/** * selectize.default.css (v@@version) - Default Theme * Copyright (c) 2013–2015 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ @import "selectize"; @selectize-color-item: #1da7ee; @selectize-color-item-text: #fff; @selectize-color-item-active-text: #fff; @selectize-color-item-border: #0073bb; @selectize-color-item-active: #92c836; @selectize-color-item-active-border: #00578d; @selectize-width-item-border: 1px; @selectize-caret-margin: 0 1px; .selectize-control { &.multi { .selectize-input { &.has-items { @padding-x: @selectize-padding-x - 3px; padding-left: @padding-x; padding-right: @padding-x; } &.disabled [data-value] { color: #999; text-shadow: none; background: none; .selectize-box-shadow(none); &, .remove { border-color: #e6e6e6; } .remove { background: none; } } [data-value] { text-shadow: 0 1px 0 rgba(0,51,83,0.3); .selectize-border-radius(3px); .selectize-vertical-gradient(#1da7ee, #178ee9); .selectize-box-shadow(~"0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03)"); &.active { .selectize-vertical-gradient(#008fd8, #0075cf); } } } } &.single { .selectize-input { .selectize-box-shadow(~"0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8)"); .selectize-vertical-gradient(#fefefe, #f2f2f2); } } } .selectize-control.single .selectize-input, .selectize-dropdown.single { border-color: #b8b8b8; } .selectize-dropdown { .optgroup-header { padding-top: @selectize-padding-dropdown-item-y + 2px; font-weight: bold; font-size: 0.85em; } .optgroup { border-top: 1px solid @selectize-color-dropdown-border-top; &:first-child { border-top: 0 none; } } }selectize.js-0.12.4/src/less/selectize.legacy.less000066400000000000000000000046211277751564300221030ustar00rootroot00000000000000/** * selectize.legacy.css (v@@version) - Default Theme * Copyright (c) 2013–2015 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ @import "selectize"; @selectize-font-size: 13px; @selectize-line-height: 20px; @selectize-color-input-full: #f2f2f2; @selectize-color-item: #b8e76f; @selectize-color-item-text: #3d5d18; @selectize-color-item-border: #74b21e; @selectize-color-item-active: #92c836; @selectize-color-item-active-border: #6f9839; @selectize-color-highlight: rgba(255,237,40,0.4); @selectize-color-dropdown-item-active: #fffceb; @selectize-color-dropdown-item-active-text: @selectize-color-text; @selectize-color-optgroup: #f8f8f8; @selectize-color-optgroup-text: @selectize-color-text; @selectize-width-item-border: 1px; @selectize-padding-x: 10px; @selectize-padding-y: 10px; @selectize-padding-item-x: 5px; @selectize-padding-item-y: 1px; @selectize-padding-dropdown-item-x: 10px; @selectize-padding-dropdown-item-y: 7px; @selectize-margin-item-x: 4px; @selectize-margin-item-y: 4px; .selectize-control { &.multi { .selectize-input [data-value] { text-shadow: 0 1px 0 rgba(255,255,255,0.1); .selectize-border-radius(3px); .selectize-vertical-gradient(#b8e76f, #a9e25c); .selectize-box-shadow(0 1px 1px rgba(0,0,0,0.1)); &.active { .selectize-vertical-gradient(#92c836, #7abc2c); } } } &.single { .selectize-input { .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)"); .selectize-vertical-gradient(#f5f5f5, #efefef); } } } .selectize-control.single .selectize-input, .selectize-dropdown.single { border-color: #b8b8b8; } .selectize-dropdown { .optgroup-header { font-weight: bold; font-size: 0.8em; border-bottom: 1px solid @selectize-color-dropdown-border-top; border-top: 1px solid @selectize-color-dropdown-border-top; } }selectize.js-0.12.4/src/less/selectize.less000066400000000000000000000216071277751564300206430ustar00rootroot00000000000000// base styles @selectize-font-family: inherit; @selectize-font-smoothing: inherit; @selectize-font-size: 13px; @selectize-line-height: 18px; @selectize-color-text: #303030; @selectize-color-border: #d0d0d0; @selectize-color-highlight: rgba(125,168,208,0.2); @selectize-color-input: #fff; @selectize-color-input-full: @selectize-color-input; @selectize-color-disabled: #fafafa; @selectize-color-item: #f2f2f2; @selectize-color-item-text: @selectize-color-text; @selectize-color-item-border: #d0d0d0; @selectize-color-item-active: #e8e8e8; @selectize-color-item-active-text: @selectize-color-text; @selectize-color-item-active-border: #cacaca; @selectize-color-dropdown: #fff; @selectize-color-dropdown-border: @selectize-color-border; @selectize-color-dropdown-border-top: #f0f0f0; @selectize-color-dropdown-item-active: #f5fafd; @selectize-color-dropdown-item-active-text: #495c68; @selectize-color-dropdown-item-create-text: rgba(red(@selectize-color-text), green(@selectize-color-text), blue(@selectize-color-text), 0.5); @selectize-color-dropdown-item-create-active-text: @selectize-color-dropdown-item-active-text; @selectize-color-optgroup: @selectize-color-dropdown; @selectize-color-optgroup-text: @selectize-color-text; @selectize-lighten-disabled-item: 30%; @selectize-lighten-disabled-item-text: 30%; @selectize-lighten-disabled-item-border: 30%; @selectize-opacity-disabled: 0.5; @selectize-shadow-input: inset 0 1px 1px rgba(0,0,0,0.1); @selectize-shadow-input-focus: inset 0 1px 2px rgba(0,0,0,0.15); @selectize-border: 1px solid @selectize-color-border; @selectize-dropdown-border: 1px solid @selectize-color-dropdown-border; @selectize-border-radius: 3px; @selectize-width-item-border: 0; @selectize-max-height-dropdown: 200px; @selectize-padding-x: 8px; @selectize-padding-y: 8px; @selectize-padding-item-x: 6px; @selectize-padding-item-y: 2px; @selectize-padding-dropdown-item-x: @selectize-padding-x; @selectize-padding-dropdown-item-y: 5px; @selectize-margin-item-x: 3px; @selectize-margin-item-y: 3px; @selectize-arrow-size: 5px; @selectize-arrow-color: #808080; @selectize-arrow-offset: 15px; @selectize-caret-margin: 0 2px 0 0; @selectize-caret-margin-rtl: 0 4px 0 -2px; .selectize-border-radius (@radii) { -webkit-border-radius: @radii; -moz-border-radius: @radii; border-radius: @radii; } .selectize-unselectable () { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .selectize-box-shadow (@shadow) { -webkit-box-shadow: @shadow; box-shadow: @shadow; } .selectize-box-sizing (@type: border-box) { -webkit-box-sizing: @type; -moz-box-sizing: @type; box-sizing: @type; } .selectize-vertical-gradient (@color-top, @color-bottom) { background-color: mix(@color-top, @color-bottom, 60%); background-image: -moz-linear-gradient(top, @color-top, @color-bottom); // FF 3.6+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@color-top), to(@color-bottom)); // Safari 4+, Chrome 2+ background-image: -webkit-linear-gradient(top, @color-top, @color-bottom); // Safari 5.1+, Chrome 10+ background-image: -o-linear-gradient(top, @color-top, @color-bottom); // Opera 11.10 background-image: linear-gradient(to bottom, @color-top, @color-bottom); // Standard, IE10 background-repeat: repeat-x; filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@color-top),argb(@color-bottom))); // IE9 and down } .selectize-control { position: relative; } .selectize-dropdown, .selectize-input, .selectize-input input { color: @selectize-color-text; font-family: @selectize-font-family; font-size: @selectize-font-size; line-height: @selectize-line-height; -webkit-font-smoothing: @selectize-font-smoothing; } .selectize-input, .selectize-control.single .selectize-input.input-active { background: @selectize-color-input; cursor: text; display: inline-block; } .selectize-input { border: @selectize-border; padding: @selectize-padding-y @selectize-padding-x; display: inline-block; width: 100%; overflow: hidden; position: relative; z-index: 1; .selectize-box-sizing(border-box); .selectize-box-shadow(@selectize-shadow-input); .selectize-border-radius(@selectize-border-radius); .selectize-control.multi &.has-items { @padding-x: @selectize-padding-x; @padding-top: @selectize-padding-y - @selectize-padding-item-y - @selectize-width-item-border; @padding-bottom: @selectize-padding-y - @selectize-padding-item-y - @selectize-margin-item-y - @selectize-width-item-border; padding: @padding-top @padding-x @padding-bottom; } &.full { background-color: @selectize-color-input-full; } &.disabled, &.disabled * { cursor: default !important; } &.focus { .selectize-box-shadow(@selectize-shadow-input-focus); } &.dropdown-active { .selectize-border-radius(@selectize-border-radius @selectize-border-radius 0 0); } > * { vertical-align: baseline; display: -moz-inline-stack; display: inline-block; zoom: 1; *display: inline; } .selectize-control.multi & > div { cursor: pointer; margin: 0 @selectize-margin-item-x @selectize-margin-item-y 0; padding: @selectize-padding-item-y @selectize-padding-item-x; background: @selectize-color-item; color: @selectize-color-item-text; border: @selectize-width-item-border solid @selectize-color-item-border; &.active { background: @selectize-color-item-active; color: @selectize-color-item-active-text; border: @selectize-width-item-border solid @selectize-color-item-active-border; } } .selectize-control.multi &.disabled > div { &, &.active { color: lighten(desaturate(@selectize-color-item-text, 100%), @selectize-lighten-disabled-item-text); background: lighten(desaturate(@selectize-color-item, 100%), @selectize-lighten-disabled-item); border: @selectize-width-item-border solid lighten(desaturate(@selectize-color-item-border, 100%), @selectize-lighten-disabled-item-border); } } > input { &::-ms-clear { display: none; } display: inline-block !important; padding: 0 !important; min-height: 0 !important; max-height: none !important; max-width: 100% !important; margin: @selectize-caret-margin !important; text-indent: 0 !important; border: 0 none !important; background: none !important; line-height: inherit !important; -webkit-user-select: auto !important; .selectize-box-shadow(none) !important; &:focus { outline: none !important; } } } .selectize-input::after { content: ' '; display: block; clear: left; } .selectize-input.dropdown-active::before { content: ' '; display: block; position: absolute; background: @selectize-color-dropdown-border-top; height: 1px; bottom: 0; left: 0; right: 0; } .selectize-dropdown { position: absolute; z-index: 10; border: @selectize-dropdown-border; background: @selectize-color-dropdown; margin: -1px 0 0 0; border-top: 0 none; .selectize-box-sizing(border-box); .selectize-box-shadow(0 1px 3px rgba(0,0,0,0.1)); .selectize-border-radius(0 0 @selectize-border-radius @selectize-border-radius); [data-selectable] { cursor: pointer; overflow: hidden; .highlight { background: @selectize-color-highlight; .selectize-border-radius(1px); } } [data-selectable], .optgroup-header { padding: @selectize-padding-dropdown-item-y @selectize-padding-dropdown-item-x; } .optgroup:first-child .optgroup-header { border-top: 0 none; } .optgroup-header { color: @selectize-color-optgroup-text; background: @selectize-color-optgroup; cursor: default; } .active { background-color: @selectize-color-dropdown-item-active; color: @selectize-color-dropdown-item-active-text; &.create { color: @selectize-color-dropdown-item-create-active-text; } } .create { color: @selectize-color-dropdown-item-create-text; } } .selectize-dropdown-content { overflow-y: auto; overflow-x: hidden; max-height: @selectize-max-height-dropdown; -webkit-overflow-scrolling: touch; } .selectize-control.single .selectize-input { &, input { cursor: pointer; } &.input-active, &.input-active input { cursor: text; } &:after { content: ' '; display: block; position: absolute; top: 50%; right: @selectize-arrow-offset; margin-top: round((-1 * @selectize-arrow-size / 2)); width: 0; height: 0; border-style: solid; border-width: @selectize-arrow-size @selectize-arrow-size 0 @selectize-arrow-size; border-color: @selectize-arrow-color transparent transparent transparent; } &.dropdown-active:after { margin-top: @selectize-arrow-size * -0.8; border-width: 0 @selectize-arrow-size @selectize-arrow-size @selectize-arrow-size; border-color: transparent transparent @selectize-arrow-color transparent; } } .selectize-control.rtl { &.single .selectize-input:after { left: @selectize-arrow-offset; right: auto; } .selectize-input > input { margin: @selectize-caret-margin-rtl !important; } } .selectize-control .selectize-input.disabled { opacity: @selectize-opacity-disabled; background-color: @selectize-color-disabled; } selectize.js-0.12.4/src/plugins/000077500000000000000000000000001277751564300164715ustar00rootroot00000000000000selectize.js-0.12.4/src/plugins/drag_drop/000077500000000000000000000000001277751564300204325ustar00rootroot00000000000000selectize.js-0.12.4/src/plugins/drag_drop/plugin.js000066400000000000000000000040511277751564300222660ustar00rootroot00000000000000/** * Plugin: "drag_drop" (selectize.js) * Copyright (c) 2013 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ Selectize.define('drag_drop', function(options) { if (!$.fn.sortable) throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".'); if (this.settings.mode !== 'multi') return; var self = this; self.lock = (function() { var original = self.lock; return function() { var sortable = self.$control.data('sortable'); if (sortable) sortable.disable(); return original.apply(self, arguments); }; })(); self.unlock = (function() { var original = self.unlock; return function() { var sortable = self.$control.data('sortable'); if (sortable) sortable.enable(); return original.apply(self, arguments); }; })(); self.setup = (function() { var original = self.setup; return function() { original.apply(this, arguments); var $control = self.$control.sortable({ items: '[data-value]', forcePlaceholderSize: true, disabled: self.isLocked, start: function(e, ui) { ui.placeholder.css('width', ui.helper.css('width')); $control.css({overflow: 'visible'}); }, stop: function() { $control.css({overflow: 'hidden'}); var active = self.$activeItems ? self.$activeItems.slice() : null; var values = []; $control.children('[data-value]').each(function() { values.push($(this).attr('data-value')); }); self.setValue(values); self.setActiveItem(active); } }); }; })(); });selectize.js-0.12.4/src/plugins/drag_drop/plugin.less000066400000000000000000000006761277751564300226310ustar00rootroot00000000000000.selectize-control.plugin-drag_drop { &.multi > .selectize-input > div.ui-sortable-placeholder { visibility: visible !important; background: #f2f2f2 !important; background: rgba(0,0,0,0.06) !important; border: 0 none !important; .selectize-box-shadow(inset 0 0 12px 4px #fff); } .ui-sortable-placeholder::after { content: '!'; visibility: hidden; } .ui-sortable-helper { .selectize-box-shadow(0 2px 5px rgba(0,0,0,0.2)); } }selectize.js-0.12.4/src/plugins/dropdown_header/000077500000000000000000000000001277751564300216355ustar00rootroot00000000000000selectize.js-0.12.4/src/plugins/dropdown_header/plugin.js000066400000000000000000000031141277751564300234700ustar00rootroot00000000000000/** * Plugin: "dropdown_header" (selectize.js) * Copyright (c) 2013 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ Selectize.define('dropdown_header', function(options) { var self = this; options = $.extend({ title : 'Untitled', headerClass : 'selectize-dropdown-header', titleRowClass : 'selectize-dropdown-header-title', labelClass : 'selectize-dropdown-header-label', closeClass : 'selectize-dropdown-header-close', html: function(data) { return ( '
' + '
' + '' + data.title + '' + '×' + '
' + '
' ); } }, options); self.setup = (function() { var original = self.setup; return function() { original.apply(self, arguments); self.$dropdown_header = $(options.html(options)); self.$dropdown.prepend(self.$dropdown_header); }; })(); });selectize.js-0.12.4/src/plugins/dropdown_header/plugin.less000066400000000000000000000012171277751564300240240ustar00rootroot00000000000000.selectize-dropdown-header { position: relative; padding: @selectize-padding-dropdown-item-y @selectize-padding-dropdown-item-x; border-bottom: 1px solid @selectize-color-border; background: mix(@selectize-color-dropdown, @selectize-color-border, 85%); .selectize-border-radius(@selectize-border-radius @selectize-border-radius 0 0); } .selectize-dropdown-header-close { position: absolute; right: @selectize-padding-dropdown-item-x; top: 50%; color: @selectize-color-text; opacity: 0.4; margin-top: -12px; line-height: 20px; font-size: 20px !important; } .selectize-dropdown-header-close:hover { color: darken(@selectize-color-text, 25%); }selectize.js-0.12.4/src/plugins/optgroup_columns/000077500000000000000000000000001277751564300221105ustar00rootroot00000000000000selectize.js-0.12.4/src/plugins/optgroup_columns/plugin.js000066400000000000000000000064451277751564300237550ustar00rootroot00000000000000/** * Plugin: "optgroup_columns" (selectize.js) * Copyright (c) 2013 Simon Hewitt & contributors * * 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 * * 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. * * @author Simon Hewitt */ Selectize.define('optgroup_columns', function(options) { var self = this; options = $.extend({ equalizeWidth : true, equalizeHeight : true }, options); this.getAdjacentOption = function($option, direction) { var $options = $option.closest('[data-group]').find('[data-selectable]'); var index = $options.index($option) + direction; return index >= 0 && index < $options.length ? $options.eq(index) : $(); }; this.onKeyDown = (function() { var original = self.onKeyDown; return function(e) { var index, $option, $options, $optgroup; if (this.isOpen && (e.keyCode === KEY_LEFT || e.keyCode === KEY_RIGHT)) { self.ignoreHover = true; $optgroup = this.$activeOption.closest('[data-group]'); index = $optgroup.find('[data-selectable]').index(this.$activeOption); if(e.keyCode === KEY_LEFT) { $optgroup = $optgroup.prev('[data-group]'); } else { $optgroup = $optgroup.next('[data-group]'); } $options = $optgroup.find('[data-selectable]'); $option = $options.eq(Math.min($options.length - 1, index)); if ($option.length) { this.setActiveOption($option); } return; } return original.apply(this, arguments); }; })(); var getScrollbarWidth = function() { var div; var width = getScrollbarWidth.width; var doc = document; if (typeof width === 'undefined') { div = doc.createElement('div'); div.innerHTML = '
'; div = div.firstChild; doc.body.appendChild(div); width = getScrollbarWidth.width = div.offsetWidth - div.clientWidth; doc.body.removeChild(div); } return width; }; var equalizeSizes = function() { var i, n, height_max, width, width_last, width_parent, $optgroups; $optgroups = $('[data-group]', self.$dropdown_content); n = $optgroups.length; if (!n || !self.$dropdown_content.width()) return; if (options.equalizeHeight) { height_max = 0; for (i = 0; i < n; i++) { height_max = Math.max(height_max, $optgroups.eq(i).height()); } $optgroups.css({height: height_max}); } if (options.equalizeWidth) { width_parent = self.$dropdown_content.innerWidth() - getScrollbarWidth(); width = Math.round(width_parent / n); $optgroups.css({width: width}); if (n > 1) { width_last = width_parent - width * (n - 1); $optgroups.eq(n - 1).css({width: width_last}); } } }; if (options.equalizeHeight || options.equalizeWidth) { hook.after(this, 'positionDropdown', equalizeSizes); hook.after(this, 'refreshOptions', equalizeSizes); } });selectize.js-0.12.4/src/plugins/optgroup_columns/plugin.less000066400000000000000000000004641277751564300243020ustar00rootroot00000000000000.selectize-dropdown.plugin-optgroup_columns { .optgroup { border-right: 1px solid #f2f2f2; border-top: 0 none; float: left; .selectize-box-sizing(border-box); } .optgroup:last-child { border-right: 0 none; } .optgroup:before { display: none; } .optgroup-header { border-top: 0 none; } }selectize.js-0.12.4/src/plugins/remove_button/000077500000000000000000000000001277751564300213615ustar00rootroot00000000000000selectize.js-0.12.4/src/plugins/remove_button/plugin.js000066400000000000000000000071171277751564300232230ustar00rootroot00000000000000/** * Plugin: "remove_button" (selectize.js) * Copyright (c) 2013 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ Selectize.define('remove_button', function(options) { options = $.extend({ label : '×', title : 'Remove', className : 'remove', append : true }, options); var singleClose = function(thisRef, options) { options.className = 'remove-single'; var self = thisRef; var html = '' + options.label + ''; /** * Appends an element as a child (with raw HTML). * * @param {string} html_container * @param {string} html_element * @return {string} */ var append = function(html_container, html_element) { return html_container + html_element; }; thisRef.setup = (function() { var original = self.setup; return function() { // override the item rendering method to add the button to each if (options.append) { var id = $(self.$input.context).attr('id'); var selectizer = $('#'+id); var render_item = self.settings.render.item; self.settings.render.item = function(data) { return append(render_item.apply(thisRef, arguments), html); }; } original.apply(thisRef, arguments); // add event listener thisRef.$control.on('click', '.' + options.className, function(e) { e.preventDefault(); if (self.isLocked) return; self.clear(); }); }; })(); }; var multiClose = function(thisRef, options) { var self = thisRef; var html = '' + options.label + ''; /** * Appends an element as a child (with raw HTML). * * @param {string} html_container * @param {string} html_element * @return {string} */ var append = function(html_container, html_element) { var pos = html_container.search(/(<\/[^>]+>\s*)$/); return html_container.substring(0, pos) + html_element + html_container.substring(pos); }; thisRef.setup = (function() { var original = self.setup; return function() { // override the item rendering method to add the button to each if (options.append) { var render_item = self.settings.render.item; self.settings.render.item = function(data) { return append(render_item.apply(thisRef, arguments), html); }; } original.apply(thisRef, arguments); // add event listener thisRef.$control.on('click', '.' + options.className, function(e) { e.preventDefault(); if (self.isLocked) return; var $item = $(e.currentTarget).parent(); self.setActiveItem($item); if (self.deleteSelection()) { self.setCaret(self.items.length); } }); }; })(); }; if (this.settings.mode === 'single') { singleClose(this, options); return; } else { multiClose(this, options); } }); selectize.js-0.12.4/src/plugins/remove_button/plugin.less000066400000000000000000000020441277751564300235470ustar00rootroot00000000000000.selectize-control.plugin-remove_button { [data-value] { position: relative; padding-right: 24px !important; } [data-value] .remove { z-index: 1; /* fixes ie bug (see #392) */ position: absolute; top: 0; right: 0; bottom: 0; width: 17px; text-align: center; font-weight: bold; font-size: 12px; color: inherit; text-decoration: none; vertical-align: middle; display: inline-block; padding: @selectize-padding-item-y 0 0 0; border-left: 1px solid @selectize-color-item-border; .selectize-border-radius(0 2px 2px 0); .selectize-box-sizing(border-box); } [data-value] .remove:hover { background: rgba(0,0,0,0.05); } [data-value].active .remove { border-left-color: @selectize-color-item-active-border; } .disabled [data-value] .remove:hover { background: none; } .disabled [data-value] .remove { border-left-color: lighten(desaturate(@selectize-color-item-border, 100%), @selectize-lighten-disabled-item-border); } .remove-single { position: absolute; right: 28px; top: 6px; font-size: 23px; } } selectize.js-0.12.4/src/plugins/restore_on_backspace/000077500000000000000000000000001277751564300226445ustar00rootroot00000000000000selectize.js-0.12.4/src/plugins/restore_on_backspace/plugin.js000066400000000000000000000027011277751564300245000ustar00rootroot00000000000000/** * Plugin: "restore_on_backspace" (selectize.js) * Copyright (c) 2013 Brian Reavis & contributors * * 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 * * 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. * * @author Brian Reavis */ Selectize.define('restore_on_backspace', function(options) { var self = this; options.text = options.text || function(option) { return option[this.settings.labelField]; }; this.onKeyDown = (function() { var original = self.onKeyDown; return function(e) { var index, option; if (e.keyCode === KEY_BACKSPACE && this.$control_input.val() === '' && !this.$activeItems.length) { index = this.caretPos - 1; if (index >= 0 && index < this.items.length) { option = this.options[this.items[index]]; if (this.deleteSelection(e)) { this.setTextboxValue(options.text.apply(this, [option])); this.refreshOptions(true); } e.preventDefault(); return; } } return original.apply(this, arguments); }; })(); }); selectize.js-0.12.4/src/selectize.jquery.js000066400000000000000000000107401277751564300206550ustar00rootroot00000000000000$.fn.selectize = function(settings_user) { var defaults = $.fn.selectize.defaults; var settings = $.extend({}, defaults, settings_user); var attr_data = settings.dataAttr; var field_label = settings.labelField; var field_value = settings.valueField; var field_optgroup = settings.optgroupField; var field_optgroup_label = settings.optgroupLabelField; var field_optgroup_value = settings.optgroupValueField; /** * Initializes selectize from a element. * * @param {object} $input * @param {object} settings_element */ var init_textbox = function($input, settings_element) { var i, n, values, option; var data_raw = $input.attr(attr_data); if (!data_raw) { var value = $.trim($input.val() || ''); if (!settings.allowEmptyOption && !value.length) return; values = value.split(settings.delimiter); for (i = 0, n = values.length; i < n; i++) { option = {}; option[field_label] = values[i]; option[field_value] = values[i]; settings_element.options.push(option); } settings_element.items = values; } else { settings_element.options = JSON.parse(data_raw); for (i = 0, n = settings_element.options.length; i < n; i++) { settings_element.items.push(settings_element.options[i][field_value]); } } }; /** * Initializes selectize from a ').appendTo($control).attr('tabindex', $input.is(':disabled') ? '-1' : self.tabIndex); $dropdown_parent = $(settings.dropdownParent || $wrapper); $dropdown = $('
').addClass(settings.dropdownClass).addClass(inputMode).hide().appendTo($dropdown_parent); $dropdown_content = $('
').addClass(settings.dropdownContentClass).appendTo($dropdown); if(inputId = $input.attr('id')) { $control_input.attr('id', inputId + '-selectized'); $("label[for='"+inputId+"']").attr('for', inputId + '-selectized'); } if(self.settings.copyClassesToDropdown) { $dropdown.addClass(classes); } $wrapper.css({ width: $input[0].style.width }); if (self.plugins.names.length) { classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-'); $wrapper.addClass(classes_plugins); $dropdown.addClass(classes_plugins); } if ((settings.maxItems === null || settings.maxItems > 1) && self.tagType === TAG_SELECT) { $input.attr('multiple', 'multiple'); } if (self.settings.placeholder) { $control_input.attr('placeholder', settings.placeholder); } // if splitOn was not passed in, construct it from the delimiter to allow pasting universally if (!self.settings.splitOn && self.settings.delimiter) { var delimiterEscaped = self.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); self.settings.splitOn = new RegExp('\\s*' + delimiterEscaped + '+\\s*'); } if ($input.attr('autocorrect')) { $control_input.attr('autocorrect', $input.attr('autocorrect')); } if ($input.attr('autocapitalize')) { $control_input.attr('autocapitalize', $input.attr('autocapitalize')); } self.$wrapper = $wrapper; self.$control = $control; self.$control_input = $control_input; self.$dropdown = $dropdown; self.$dropdown_content = $dropdown_content; $dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); }); $dropdown.on('mousedown click', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); }); watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); }); autoGrow($control_input); $control.on({ mousedown : function() { return self.onMouseDown.apply(self, arguments); }, click : function() { return self.onClick.apply(self, arguments); } }); $control_input.on({ mousedown : function(e) { e.stopPropagation(); }, keydown : function() { return self.onKeyDown.apply(self, arguments); }, keyup : function() { return self.onKeyUp.apply(self, arguments); }, keypress : function() { return self.onKeyPress.apply(self, arguments); }, resize : function() { self.positionDropdown.apply(self, []); }, blur : function() { return self.onBlur.apply(self, arguments); }, focus : function() { self.ignoreBlur = false; return self.onFocus.apply(self, arguments); }, paste : function() { return self.onPaste.apply(self, arguments); } }); $document.on('keydown' + eventNS, function(e) { self.isCmdDown = e[IS_MAC ? 'metaKey' : 'ctrlKey']; self.isCtrlDown = e[IS_MAC ? 'altKey' : 'ctrlKey']; self.isShiftDown = e.shiftKey; }); $document.on('keyup' + eventNS, function(e) { if (e.keyCode === KEY_CTRL) self.isCtrlDown = false; if (e.keyCode === KEY_SHIFT) self.isShiftDown = false; if (e.keyCode === KEY_CMD) self.isCmdDown = false; }); $document.on('mousedown' + eventNS, function(e) { if (self.isFocused) { // prevent events on the dropdown scrollbar from causing the control to blur if (e.target === self.$dropdown[0] || e.target.parentNode === self.$dropdown[0]) { return false; } // blur on click outside if (!self.$control.has(e.target).length && e.target !== self.$control[0]) { self.blur(e.target); } } }); $window.on(['scroll' + eventNS, 'resize' + eventNS].join(' '), function() { if (self.isOpen) { self.positionDropdown.apply(self, arguments); } }); $window.on('mousemove' + eventNS, function() { self.ignoreHover = false; }); // store original children and tab index so that they can be // restored when the destroy() method is called. this.revertSettings = { $children : $input.children().detach(), tabindex : $input.attr('tabindex') }; $input.attr('tabindex', -1).hide().after(self.$wrapper); if ($.isArray(settings.items)) { self.setValue(settings.items); delete settings.items; } // feature detect for the validation API if (SUPPORTS_VALIDITY_API) { $input.on('invalid' + eventNS, function(e) { e.preventDefault(); self.isInvalid = true; self.refreshState(); }); } self.updateOriginalInput(); self.refreshItems(); self.refreshState(); self.updatePlaceholder(); self.isSetup = true; if ($input.is(':disabled')) { self.disable(); } self.on('change', this.onChange); $input.data('selectize', self); $input.addClass('selectized'); self.trigger('initialize'); // preload options if (settings.preload === true) { self.onSearchChange(''); } }, /** * Sets up default rendering functions. */ setupTemplates: function() { var self = this; var field_label = self.settings.labelField; var field_optgroup = self.settings.optgroupLabelField; var templates = { 'optgroup': function(data) { return '
' + data.html + '
'; }, 'optgroup_header': function(data, escape) { return '
' + escape(data[field_optgroup]) + '
'; }, 'option': function(data, escape) { return '
' + escape(data[field_label]) + '
'; }, 'item': function(data, escape) { return '
' + escape(data[field_label]) + '
'; }, 'option_create': function(data, escape) { return '
Add ' + escape(data.input) + '
'; } }; self.settings.render = $.extend({}, templates, self.settings.render); }, /** * Maps fired events to callbacks provided * in the settings used when creating the control. */ setupCallbacks: function() { var key, fn, callbacks = { 'initialize' : 'onInitialize', 'change' : 'onChange', 'item_add' : 'onItemAdd', 'item_remove' : 'onItemRemove', 'clear' : 'onClear', 'option_add' : 'onOptionAdd', 'option_remove' : 'onOptionRemove', 'option_clear' : 'onOptionClear', 'optgroup_add' : 'onOptionGroupAdd', 'optgroup_remove' : 'onOptionGroupRemove', 'optgroup_clear' : 'onOptionGroupClear', 'dropdown_open' : 'onDropdownOpen', 'dropdown_close' : 'onDropdownClose', 'type' : 'onType', 'load' : 'onLoad', 'focus' : 'onFocus', 'blur' : 'onBlur' }; for (key in callbacks) { if (callbacks.hasOwnProperty(key)) { fn = this.settings[callbacks[key]]; if (fn) this.on(key, fn); } } }, /** * Triggered when the main control element * has a click event. * * @param {object} e * @return {boolean} */ onClick: function(e) { var self = this; // necessary for mobile webkit devices (manual focus triggering // is ignored unless invoked within a click event) if (!self.isFocused) { self.focus(); e.preventDefault(); } }, /** * Triggered when the main control element * has a mouse down event. * * @param {object} e * @return {boolean} */ onMouseDown: function(e) { var self = this; var defaultPrevented = e.isDefaultPrevented(); var $target = $(e.target); if (self.isFocused) { // retain focus by preventing native handling. if the // event target is the input it should not be modified. // otherwise, text selection within the input won't work. if (e.target !== self.$control_input[0]) { if (self.settings.mode === 'single') { // toggle dropdown self.isOpen ? self.close() : self.open(); } else if (!defaultPrevented) { self.setActiveItem(null); } return false; } } else { // give control focus if (!defaultPrevented) { window.setTimeout(function() { self.focus(); }, 0); } } }, /** * Triggered when the value of the control has been changed. * This should propagate the event to the original DOM * input / select element. */ onChange: function() { this.$input.trigger('change'); }, /** * Triggered on paste. * * @param {object} e * @returns {boolean} */ onPaste: function(e) { var self = this; if (self.isFull() || self.isInputHidden || self.isLocked) { e.preventDefault(); return; } // If a regex or string is included, this will split the pasted // input and create Items for each separate value if (self.settings.splitOn) { // Wait for pasted text to be recognized in value setTimeout(function() { var pastedText = self.$control_input.val(); if(!pastedText.match(self.settings.splitOn)){ return } var splitInput = $.trim(pastedText).split(self.settings.splitOn); for (var i = 0, n = splitInput.length; i < n; i++) { self.createItem(splitInput[i]); } }, 0); } }, /** * Triggered on keypress. * * @param {object} e * @returns {boolean} */ onKeyPress: function(e) { if (this.isLocked) return e && e.preventDefault(); var character = String.fromCharCode(e.keyCode || e.which); if (this.settings.create && this.settings.mode === 'multi' && character === this.settings.delimiter) { this.createItem(); e.preventDefault(); return false; } }, /** * Triggered on keydown. * * @param {object} e * @returns {boolean} */ onKeyDown: function(e) { var isInput = e.target === this.$control_input[0]; var self = this; if (self.isLocked) { if (e.keyCode !== KEY_TAB) { e.preventDefault(); } return; } switch (e.keyCode) { case KEY_A: if (self.isCmdDown) { self.selectAll(); return; } break; case KEY_ESC: if (self.isOpen) { e.preventDefault(); e.stopPropagation(); self.close(); } return; case KEY_N: if (!e.ctrlKey || e.altKey) break; case KEY_DOWN: if (!self.isOpen && self.hasOptions) { self.open(); } else if (self.$activeOption) { self.ignoreHover = true; var $next = self.getAdjacentOption(self.$activeOption, 1); if ($next.length) self.setActiveOption($next, true, true); } e.preventDefault(); return; case KEY_P: if (!e.ctrlKey || e.altKey) break; case KEY_UP: if (self.$activeOption) { self.ignoreHover = true; var $prev = self.getAdjacentOption(self.$activeOption, -1); if ($prev.length) self.setActiveOption($prev, true, true); } e.preventDefault(); return; case KEY_RETURN: if (self.isOpen && self.$activeOption) { self.onOptionSelect({currentTarget: self.$activeOption}); e.preventDefault(); } return; case KEY_LEFT: self.advanceSelection(-1, e); return; case KEY_RIGHT: self.advanceSelection(1, e); return; case KEY_TAB: if (self.settings.selectOnTab && self.isOpen && self.$activeOption) { self.onOptionSelect({currentTarget: self.$activeOption}); // Default behaviour is to jump to the next field, we only want this // if the current field doesn't accept any more entries if (!self.isFull()) { e.preventDefault(); } } if (self.settings.create && self.createItem()) { e.preventDefault(); } return; case KEY_BACKSPACE: case KEY_DELETE: self.deleteSelection(e); return; } if ((self.isFull() || self.isInputHidden) && !(IS_MAC ? e.metaKey : e.ctrlKey)) { e.preventDefault(); return; } }, /** * Triggered on keyup. * * @param {object} e * @returns {boolean} */ onKeyUp: function(e) { var self = this; if (self.isLocked) return e && e.preventDefault(); var value = self.$control_input.val() || ''; if (self.lastValue !== value) { self.lastValue = value; self.onSearchChange(value); self.refreshOptions(); self.trigger('type', value); } }, /** * Invokes the user-provide option provider / loader. * * Note: this function is debounced in the Selectize * constructor (by `settings.loadThrottle` milliseconds) * * @param {string} value */ onSearchChange: function(value) { var self = this; var fn = self.settings.load; if (!fn) return; if (self.loadedSearches.hasOwnProperty(value)) return; self.loadedSearches[value] = true; self.load(function(callback) { fn.apply(self, [value, callback]); }); }, /** * Triggered on focus. * * @param {object} e (optional) * @returns {boolean} */ onFocus: function(e) { var self = this; var wasFocused = self.isFocused; if (self.isDisabled) { self.blur(); e && e.preventDefault(); return false; } if (self.ignoreFocus) return; self.isFocused = true; if (self.settings.preload === 'focus') self.onSearchChange(''); if (!wasFocused) self.trigger('focus'); if (!self.$activeItems.length) { self.showInput(); self.setActiveItem(null); self.refreshOptions(!!self.settings.openOnFocus); } self.refreshState(); }, /** * Triggered on blur. * * @param {object} e * @param {Element} dest */ onBlur: function(e, dest) { var self = this; if (!self.isFocused) return; self.isFocused = false; if (self.ignoreFocus) { return; } else if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) { // necessary to prevent IE closing the dropdown when the scrollbar is clicked self.ignoreBlur = true; self.onFocus(e); return; } var deactivate = function() { self.close(); self.setTextboxValue(''); self.setActiveItem(null); self.setActiveOption(null); self.setCaret(self.items.length); self.refreshState(); // IE11 bug: element still marked as active dest && dest.focus && dest.focus(); self.ignoreFocus = false; self.trigger('blur'); }; self.ignoreFocus = true; if (self.settings.create && self.settings.createOnBlur) { self.createItem(null, false, deactivate); } else { deactivate(); } }, /** * Triggered when the user rolls over * an option in the autocomplete dropdown menu. * * @param {object} e * @returns {boolean} */ onOptionHover: function(e) { if (this.ignoreHover) return; this.setActiveOption(e.currentTarget, false); }, /** * Triggered when the user clicks on an option * in the autocomplete dropdown menu. * * @param {object} e * @returns {boolean} */ onOptionSelect: function(e) { var value, $target, $option, self = this; if (e.preventDefault) { e.preventDefault(); e.stopPropagation(); } $target = $(e.currentTarget); if ($target.hasClass('create')) { self.createItem(null, function() { if (self.settings.closeAfterSelect) { self.close(); } }); } else { value = $target.attr('data-value'); if (typeof value !== 'undefined') { self.lastQuery = null; self.setTextboxValue(''); self.addItem(value); if (self.settings.closeAfterSelect) { self.close(); } else if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) { self.setActiveOption(self.getOption(value)); } } } }, /** * Triggered when the user clicks on an item * that has been selected. * * @param {object} e * @returns {boolean} */ onItemSelect: function(e) { var self = this; if (self.isLocked) return; if (self.settings.mode === 'multi') { e.preventDefault(); self.setActiveItem(e.currentTarget, e); } }, /** * Invokes the provided method that provides * results to a callback---which are then added * as options to the control. * * @param {function} fn */ load: function(fn) { var self = this; var $wrapper = self.$wrapper.addClass(self.settings.loadingClass); self.loading++; fn.apply(self, [function(results) { self.loading = Math.max(self.loading - 1, 0); if (results && results.length) { self.addOption(results); self.refreshOptions(self.isFocused && !self.isInputHidden); } if (!self.loading) { $wrapper.removeClass(self.settings.loadingClass); } self.trigger('load', results); }]); }, /** * Sets the input field of the control to the specified value. * * @param {string} value */ setTextboxValue: function(value) { var $input = this.$control_input; var changed = $input.val() !== value; if (changed) { $input.val(value).triggerHandler('update'); this.lastValue = value; } }, /** * Returns the value of the control. If multiple items * can be selected (e.g. or * element to reflect the current state. */ updateOriginalInput: function(opts) { var i, n, options, label, self = this; opts = opts || {}; if (self.tagType === TAG_SELECT) { options = []; for (i = 0, n = self.items.length; i < n; i++) { label = self.options[self.items[i]][self.settings.labelField] || ''; options.push(''); } if (!options.length && !this.$input.attr('multiple')) { options.push(''); } self.$input.html(options.join('')); } else { self.$input.val(self.getValue()); self.$input.attr('value',self.$input.val()); } if (self.isSetup) { if (!opts.silent) { self.trigger('change', self.$input.val()); } } }, /** * Shows/hide the input placeholder depending * on if there items in the list already. */ updatePlaceholder: function() { if (!this.settings.placeholder) return; var $input = this.$control_input; if (this.items.length) { $input.removeAttr('placeholder'); } else { $input.attr('placeholder', this.settings.placeholder); } $input.triggerHandler('update', {force: true}); }, /** * Shows the autocomplete dropdown containing * the available options. */ open: function() { var self = this; if (self.isLocked || self.isOpen || (self.settings.mode === 'multi' && self.isFull())) return; self.focus(); self.isOpen = true; self.refreshState(); self.$dropdown.css({visibility: 'hidden', display: 'block'}); self.positionDropdown(); self.$dropdown.css({visibility: 'visible'}); self.trigger('dropdown_open', self.$dropdown); }, /** * Closes the autocomplete dropdown menu. */ close: function() { var self = this; var trigger = self.isOpen; if (self.settings.mode === 'single' && self.items.length) { self.hideInput(); self.$control_input.blur(); // close keyboard on iOS } self.isOpen = false; self.$dropdown.hide(); self.setActiveOption(null); self.refreshState(); if (trigger) self.trigger('dropdown_close', self.$dropdown); }, /** * Calculates and applies the appropriate * position of the dropdown. */ positionDropdown: function() { var $control = this.$control; var offset = this.settings.dropdownParent === 'body' ? $control.offset() : $control.position(); offset.top += $control.outerHeight(true); this.$dropdown.css({ width : $control.outerWidth(), top : offset.top, left : offset.left }); }, /** * Resets / clears all selected items * from the control. * * @param {boolean} silent */ clear: function(silent) { var self = this; if (!self.items.length) return; self.$control.children(':not(input)').remove(); self.items = []; self.lastQuery = null; self.setCaret(0); self.setActiveItem(null); self.updatePlaceholder(); self.updateOriginalInput({silent: silent}); self.refreshState(); self.showInput(); self.trigger('clear'); }, /** * A helper method for inserting an element * at the current caret position. * * @param {object} $el */ insertAtCaret: function($el) { var caret = Math.min(this.caretPos, this.items.length); if (caret === 0) { this.$control.prepend($el); } else { $(this.$control[0].childNodes[caret]).before($el); } this.setCaret(caret + 1); }, /** * Removes the current selected item(s). * * @param {object} e (optional) * @returns {boolean} */ deleteSelection: function(e) { var i, n, direction, selection, values, caret, option_select, $option_select, $tail; var self = this; direction = (e && e.keyCode === KEY_BACKSPACE) ? -1 : 1; selection = getSelection(self.$control_input[0]); if (self.$activeOption && !self.settings.hideSelected) { option_select = self.getAdjacentOption(self.$activeOption, -1).attr('data-value'); } // determine items that will be removed values = []; if (self.$activeItems.length) { $tail = self.$control.children('.active:' + (direction > 0 ? 'last' : 'first')); caret = self.$control.children(':not(input)').index($tail); if (direction > 0) { caret++; } for (i = 0, n = self.$activeItems.length; i < n; i++) { values.push($(self.$activeItems[i]).attr('data-value')); } if (e) { e.preventDefault(); e.stopPropagation(); } } else if ((self.isFocused || self.settings.mode === 'single') && self.items.length) { if (direction < 0 && selection.start === 0 && selection.length === 0) { values.push(self.items[self.caretPos - 1]); } else if (direction > 0 && selection.start === self.$control_input.val().length) { values.push(self.items[self.caretPos]); } } // allow the callback to abort if (!values.length || (typeof self.settings.onDelete === 'function' && self.settings.onDelete.apply(self, [values]) === false)) { return false; } // perform removal if (typeof caret !== 'undefined') { self.setCaret(caret); } while (values.length) { self.removeItem(values.pop()); } self.showInput(); self.positionDropdown(); self.refreshOptions(true); // select previous option if (option_select) { $option_select = self.getOption(option_select); if ($option_select.length) { self.setActiveOption($option_select); } } return true; }, /** * Selects the previous / next item (depending * on the `direction` argument). * * > 0 - right * < 0 - left * * @param {int} direction * @param {object} e (optional) */ advanceSelection: function(direction, e) { var tail, selection, idx, valueLength, cursorAtEdge, $tail; var self = this; if (direction === 0) return; if (self.rtl) direction *= -1; tail = direction > 0 ? 'last' : 'first'; selection = getSelection(self.$control_input[0]); if (self.isFocused && !self.isInputHidden) { valueLength = self.$control_input.val().length; cursorAtEdge = direction < 0 ? selection.start === 0 && selection.length === 0 : selection.start === valueLength; if (cursorAtEdge && !valueLength) { self.advanceCaret(direction, e); } } else { $tail = self.$control.children('.active:' + tail); if ($tail.length) { idx = self.$control.children(':not(input)').index($tail); self.setActiveItem(null); self.setCaret(direction > 0 ? idx + 1 : idx); } } }, /** * Moves the caret left / right. * * @param {int} direction * @param {object} e (optional) */ advanceCaret: function(direction, e) { var self = this, fn, $adj; if (direction === 0) return; fn = direction > 0 ? 'next' : 'prev'; if (self.isShiftDown) { $adj = self.$control_input[fn](); if ($adj.length) { self.hideInput(); self.setActiveItem($adj); e && e.preventDefault(); } } else { self.setCaret(self.caretPos + direction); } }, /** * Moves the caret to the specified index. * * @param {int} i */ setCaret: function(i) { var self = this; if (self.settings.mode === 'single') { i = self.items.length; } else { i = Math.max(0, Math.min(self.items.length, i)); } if(!self.isPending) { // the input must be moved by leaving it in place and moving the // siblings, due to the fact that focus cannot be restored once lost // on mobile webkit devices var j, n, fn, $children, $child; $children = self.$control.children(':not(input)'); for (j = 0, n = $children.length; j < n; j++) { $child = $($children[j]).detach(); if (j < i) { self.$control_input.before($child); } else { self.$control.append($child); } } } self.caretPos = i; }, /** * Disables user input on the control. Used while * items are being asynchronously created. */ lock: function() { this.close(); this.isLocked = true; this.refreshState(); }, /** * Re-enables user input on the control. */ unlock: function() { this.isLocked = false; this.refreshState(); }, /** * Disables user input on the control completely. * While disabled, it cannot receive focus. */ disable: function() { var self = this; self.$input.prop('disabled', true); self.$control_input.prop('disabled', true).prop('tabindex', -1); self.isDisabled = true; self.lock(); }, /** * Enables the control so that it can respond * to focus and user input. */ enable: function() { var self = this; self.$input.prop('disabled', false); self.$control_input.prop('disabled', false).prop('tabindex', self.tabIndex); self.isDisabled = false; self.unlock(); }, /** * Completely destroys the control and * unbinds all event listeners so that it can * be garbage collected. */ destroy: function() { var self = this; var eventNS = self.eventNS; var revertSettings = self.revertSettings; self.trigger('destroy'); self.off(); self.$wrapper.remove(); self.$dropdown.remove(); self.$input .html('') .append(revertSettings.$children) .removeAttr('tabindex') .removeClass('selectized') .attr({tabindex: revertSettings.tabindex}) .show(); self.$control_input.removeData('grow'); self.$input.removeData('selectize'); $(window).off(eventNS); $(document).off(eventNS); $(document.body).off(eventNS); delete self.$input[0].selectize; }, /** * A helper method for rendering "item" and * "option" templates, given the data. * * @param {string} templateName * @param {object} data * @returns {string} */ render: function(templateName, data) { var value, id, label; var html = ''; var cache = false; var self = this; var regex_tag = /^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i; if (templateName === 'option' || templateName === 'item') { value = hash_key(data[self.settings.valueField]); cache = !!value; } // pull markup from cache if it exists if (cache) { if (!isset(self.renderCache[templateName])) { self.renderCache[templateName] = {}; } if (self.renderCache[templateName].hasOwnProperty(value)) { return self.renderCache[templateName][value]; } } // render markup html = $(self.settings.render[templateName].apply(this, [data, escape_html])); // add mandatory attributes if (templateName === 'option' || templateName === 'option_create') { html.attr('data-selectable', ''); } else if (templateName === 'optgroup') { id = data[self.settings.optgroupValueField] || ''; html.attr('data-group', id); } if (templateName === 'option' || templateName === 'item') { html.attr('data-value', value || ''); } // update cache if (cache) { self.renderCache[templateName][value] = html[0]; } return html[0]; }, /** * Clears the render cache for a template. If * no template is given, clears all render * caches. * * @param {string} templateName */ clearCache: function(templateName) { var self = this; if (typeof templateName === 'undefined') { self.renderCache = {}; } else { delete self.renderCache[templateName]; } }, /** * Determines whether or not to display the * create item prompt, given a user input. * * @param {string} input * @return {boolean} */ canCreate: function(input) { var self = this; if (!self.settings.create) return false; var filter = self.settings.createFilter; return input.length && (typeof filter !== 'function' || filter.apply(self, [input])) && (typeof filter !== 'string' || new RegExp(filter).test(input)) && (!(filter instanceof RegExp) || filter.test(input)); } }); selectize.js-0.12.4/src/utils.js000066400000000000000000000174431277751564300165170ustar00rootroot00000000000000/** * Determines if the provided value has been defined. * * @param {mixed} object * @returns {boolean} */ var isset = function(object) { return typeof object !== 'undefined'; }; /** * Converts a scalar to its best string representation * for hash keys and HTML attribute values. * * Transformations: * 'str' -> 'str' * null -> '' * undefined -> '' * true -> '1' * false -> '0' * 0 -> '0' * 1 -> '1' * * @param {string} value * @returns {string|null} */ var hash_key = function(value) { if (typeof value === 'undefined' || value === null) return null; if (typeof value === 'boolean') return value ? '1' : '0'; return value + ''; }; /** * Escapes a string for use within HTML. * * @param {string} str * @returns {string} */ var escape_html = function(str) { return (str + '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); }; /** * Escapes "$" characters in replacement strings. * * @param {string} str * @returns {string} */ var escape_replace = function(str) { return (str + '').replace(/\$/g, '$$$$'); }; var hook = {}; /** * Wraps `method` on `self` so that `fn` * is invoked before the original method. * * @param {object} self * @param {string} method * @param {function} fn */ hook.before = function(self, method, fn) { var original = self[method]; self[method] = function() { fn.apply(self, arguments); return original.apply(self, arguments); }; }; /** * Wraps `method` on `self` so that `fn` * is invoked after the original method. * * @param {object} self * @param {string} method * @param {function} fn */ hook.after = function(self, method, fn) { var original = self[method]; self[method] = function() { var result = original.apply(self, arguments); fn.apply(self, arguments); return result; }; }; /** * Wraps `fn` so that it can only be invoked once. * * @param {function} fn * @returns {function} */ var once = function(fn) { var called = false; return function() { if (called) return; called = true; fn.apply(this, arguments); }; }; /** * Wraps `fn` so that it can only be called once * every `delay` milliseconds (invoked on the falling edge). * * @param {function} fn * @param {int} delay * @returns {function} */ var debounce = function(fn, delay) { var timeout; return function() { var self = this; var args = arguments; window.clearTimeout(timeout); timeout = window.setTimeout(function() { fn.apply(self, args); }, delay); }; }; /** * Debounce all fired events types listed in `types` * while executing the provided `fn`. * * @param {object} self * @param {array} types * @param {function} fn */ var debounce_events = function(self, types, fn) { var type; var trigger = self.trigger; var event_args = {}; // override trigger method self.trigger = function() { var type = arguments[0]; if (types.indexOf(type) !== -1) { event_args[type] = arguments; } else { return trigger.apply(self, arguments); } }; // invoke provided function fn.apply(self, []); self.trigger = trigger; // trigger queued events for (type in event_args) { if (event_args.hasOwnProperty(type)) { trigger.apply(self, event_args[type]); } } }; /** * A workaround for http://bugs.jquery.com/ticket/6696 * * @param {object} $parent - Parent element to listen on. * @param {string} event - Event name. * @param {string} selector - Descendant selector to filter by. * @param {function} fn - Event handler. */ var watchChildEvent = function($parent, event, selector, fn) { $parent.on(event, selector, function(e) { var child = e.target; while (child && child.parentNode !== $parent[0]) { child = child.parentNode; } e.currentTarget = child; return fn.apply(this, [e]); }); }; /** * Determines the current selection within a text input control. * Returns an object containing: * - start * - length * * @param {object} input * @returns {object} */ var getSelection = function(input) { var result = {}; if ('selectionStart' in input) { result.start = input.selectionStart; result.length = input.selectionEnd - result.start; } else if (document.selection) { input.focus(); var sel = document.selection.createRange(); var selLen = document.selection.createRange().text.length; sel.moveStart('character', -input.value.length); result.start = sel.text.length - selLen; result.length = selLen; } return result; }; /** * Copies CSS properties from one element to another. * * @param {object} $from * @param {object} $to * @param {array} properties */ var transferStyles = function($from, $to, properties) { var i, n, styles = {}; if (properties) { for (i = 0, n = properties.length; i < n; i++) { styles[properties[i]] = $from.css(properties[i]); } } else { styles = $from.css(); } $to.css(styles); }; /** * Measures the width of a string within a * parent element (in pixels). * * @param {string} str * @param {object} $parent * @returns {int} */ var measureString = function(str, $parent) { if (!str) { return 0; } var $test = $('').css({ position: 'absolute', top: -99999, left: -99999, width: 'auto', padding: 0, whiteSpace: 'pre' }).text(str).appendTo('body'); transferStyles($parent, $test, [ 'letterSpacing', 'fontSize', 'fontFamily', 'fontWeight', 'textTransform' ]); var width = $test.width(); $test.remove(); return width; }; /** * Sets up an input to grow horizontally as the user * types. If the value is changed manually, you can * trigger the "update" handler to resize: * * $input.trigger('update'); * * @param {object} $input */ var autoGrow = function($input) { var currentWidth = null; var update = function(e, options) { var value, keyCode, printable, placeholder, width; var shift, character, selection; e = e || window.event || {}; options = options || {}; if (e.metaKey || e.altKey) return; if (!options.force && $input.data('grow') === false) return; value = $input.val(); if (e.type && e.type.toLowerCase() === 'keydown') { keyCode = e.keyCode; printable = ( (keyCode >= 97 && keyCode <= 122) || // a-z (keyCode >= 65 && keyCode <= 90) || // A-Z (keyCode >= 48 && keyCode <= 57) || // 0-9 keyCode === 32 // space ); if (keyCode === KEY_DELETE || keyCode === KEY_BACKSPACE) { selection = getSelection($input[0]); if (selection.length) { value = value.substring(0, selection.start) + value.substring(selection.start + selection.length); } else if (keyCode === KEY_BACKSPACE && selection.start) { value = value.substring(0, selection.start - 1) + value.substring(selection.start + 1); } else if (keyCode === KEY_DELETE && typeof selection.start !== 'undefined') { value = value.substring(0, selection.start) + value.substring(selection.start + 1); } } else if (printable) { shift = e.shiftKey; character = String.fromCharCode(e.keyCode); if (shift) character = character.toUpperCase(); else character = character.toLowerCase(); value += character; } } placeholder = $input.attr('placeholder'); if (!value && placeholder) { value = placeholder; } width = measureString(value, $input) + 4; if (width !== currentWidth) { currentWidth = width; $input.width(width); $input.triggerHandler('resize'); } }; $input.on('keydown keyup update blur', update); update(); }; var domToString = function(d) { var tmp = document.createElement('div'); tmp.appendChild(d.cloneNode(true)); return tmp.innerHTML; }; var logError = function(message, options){ if(!options) options = {}; var component = "Selectize"; console.error(component + ": " + message) if(options.explanation){ // console.group is undefined in ', {}); expect(String(test.selectize.$control_input.attr('tabindex'))).to.be.equal('4'); test.selectize.disable(); }); it('should set "tabindex" prop to -1', function() { expect(String(test.selectize.$control_input.attr('tabindex'))).to.be.equal('-1'); }); it('should set "disabled" class', function() { expect(test.selectize.$control.hasClass('disabled')).to.be.equal(true); }); it('should set isDisabled property to true', function() { expect(test.selectize.isDisabled).to.be.equal(true); }); it('should add "disabled" attribute on inputs', function() { expect(test.selectize.$input.is(':disabled')).to.be.equal(true); expect(test.selectize.$control_input.is(':disabled')).to.be.equal(true); }); }); describe('enable()', function() { var test; before(function() { test = setup_test('', {}); test.selectize.focus(); window.setTimeout(function() { done(); }, 5); }); it('should set isFocused property to true', function() { expect(test.selectize.isFocused).to.be.equal(true); }); it('should give the control focus', function() { expect(has_focus(test.selectize.$control_input[0])).to.be.equal(true); }); }); describe('blur()', function() { var test; before(function(done) { test = setup_test('', { valueField: 'value', labelField: 'value', create: function(input) { return false; } }); test.selectize.$control_input.val('test'); test.selectize.createItem(); expect(test.selectize.items.length).to.be.equal(0); test = setup_test('', { valueField: 'value', labelField: 'value', create: function(input) { return {value: input}; } }); test.selectize.$control_input.val('test'); test.selectize.createItem(); expect(test.selectize.options).to.have.property('test'); }); it('should add option upon completion (asynchronous)', function(done) { var test = setup_test('', {valueField: 'value', labelField: 'value', optgroupValueField: 'grpval'}); }); it('should register group', function() { var data = {label: 'Group Label'}; test.selectize.addOptionGroup('group_id', data); expect(test.selectize.optgroups).to.have.property('group_id'); }); it('should add implicit $order property', function() { test.selectize.addOptionGroup('group1', {}); test.selectize.addOptionGroup('group2', {}); assert.equal(test.selectize.optgroups['group1'].$order, 2); assert.equal(test.selectize.optgroups['group2'].$order, 3); }); }); describe('removeOptionGroup()', function() { var test; before(function() { test = setup_test('', {valueField: 'value', labelField: 'value'}); }); it('should clear all groups', function() { var data = {label: 'Group Label'}; test.selectize.addOptionGroup('group_id', data); test.selectize.addOptionGroup('group_id2', data); test.selectize.clearOptionGroups(); expect(test.selectize.optgroups).to.deep.equal({}); }); }); describe('addOption()', function() { var test; before(function() { test = setup_test('', { valueField: 'value', labelField: 'value', options: [ {value: 0}, {value: 1}, {value: 'undefined'}, {value: 'null'}, {value: 'a'}, {value: 'b'}, {value: 'c'}, {value: 'x'}, {value: '$1'}, {value: '\''}, {value: '"'}, {value: '\\\''}, {value: '\\"'}, ] }); }); it('should update "items" array', function() { test.selectize.addItem('b'); expect(test.selectize.items.indexOf('b')).to.be.equal(0); }); it('should not give control focus', function(done) { test.selectize.addItem(0); window.setTimeout(function() { expect(test.selectize.isFocused).to.be.equal(false); done(); }, 0); }); it('should not allow duplicate entries', function() { test.selectize.addItem('a'); test.selectize.addItem('a'); expect(test.selectize.items.indexOf('a')).to.be.equal(test.selectize.items.lastIndexOf('a')); }); it('should not allow undefined / null values', function() { test.selectize.addItem(undefined); test.selectize.addItem(null); expect(test.selectize.items.indexOf('undefined')).to.be.equal(-1); expect(test.selectize.items.indexOf('null')).to.be.equal(-1); }); it('should allow integer values', function() { test.selectize.addItem(0); expect(test.selectize.items.indexOf('0')).to.not.be.equal(-1); }); it('should not fire "change" if silent is truthy', function(done) { var watcher = function(e) { throw new Error('Change fired'); }; test.$select.on('change', watcher); test.selectize.addItem('x', true); expect(test.selectize.items.indexOf('x')).to.not.be.equal(-1); window.setTimeout(function() { test.$select.off('change', watcher); done(); }, 0); }); it('should update DOM', function() { test.selectize.addItem('c'); expect(test.selectize.$control.find('[data-value=c]').length).to.be.equal(1); test.selectize.addItem('$1'); var found = false; test.selectize.$control.children().each(function() { if (this.getAttribute('data-value') === '$1') { found = true; return false; } }); expect(found).to.be.equal(true); }); }); describe('updateOption()', function() { var test; before(function() { test = setup_test('', { valueField: 'value', labelField: 'value', options: [ {value: 0}, {value: 1}, {value: 'a'}, {value: 'b'}, {value: '\''}, {value: '\\'}, {value: '"'}, {value: '\\\''}, {value: '\\"'}, ] }); test.selectize.refreshOptions(true); }); it('should allow string values', function() { expect(test.selectize.getOption('a')).to.be.ok; expect(test.selectize.getOption('a').length).to.be.equal(1); expect(test.selectize.getOption('b')).to.be.ok; expect(test.selectize.getOption('b').length).to.be.equal(1); }); it('should allow integer values', function() { expect(test.selectize.getOption(0)).to.be.ok; expect(test.selectize.getOption(0).length).to.be.equal(1); expect(test.selectize.getOption(1)).to.be.ok; expect(test.selectize.getOption(1).length).to.be.equal(1); }); it('should allow values with quotation marks', function() { expect(test.selectize.getOption('\'')).to.be.ok; expect(test.selectize.getOption('\'').length).to.be.equal(1); expect(test.selectize.getOption('"')).to.be.ok; expect(test.selectize.getOption('"').length).to.be.equal(1); }); it('should allow values with backslashes', function() { expect(test.selectize.getOption('\\')).to.be.ok; expect(test.selectize.getOption('\\').length).to.be.equal(1); expect(test.selectize.getOption('\\\'')).to.be.ok; expect(test.selectize.getOption('\\\'').length).to.be.equal(1); expect(test.selectize.getOption('\\"')).to.be.ok; expect(test.selectize.getOption('\\"').length).to.be.equal(1); }); it('should not allow undefined / null values', function() { expect(test.selectize.getOption(null)).to.be.ok; expect(test.selectize.getOption(null).length).to.be.equal(0); expect(test.selectize.getOption(undefined)).to.be.ok; expect(test.selectize.getOption(undefined).length).to.be.equal(0); }); }); describe('getItem()', function() { var test; before(function() { test = setup_test('', { valueField: 'value', labelField: 'value', options: [ {value: 0}, {value: 1}, {value: 2}, {value: 3}, ], items: ['1','2','3'] }); }); it('should empty "$activeItems" array', function() { test.selectize.setActiveItem(test.selectize.getItem('1')); expect(test.selectize.$activeItems.length).to.be.equal(1); test.selectize.clear(); expect(test.selectize.$activeItems.length).to.be.equal(0); }); it('should refresh option list (dropdown)', function(done) { // test = setup_test('', { valueField: 'value', labelField: 'value', options: [ {value: 0}, {value: 1} ], score: function() { } }); test.selectize.search('hello'); }).to.throw(Error); }); it('should not throw error if "score" setting does return a function', function() { var test; expect(function() { test = setup_test('', { valueField: 'value', labelField: 'value', searchField: 'value', options: [] }); var fn = test.selectize.getScoreFunction('test'); expect(typeof fn).to.be.equal('function'); expect(typeof fn({value: 'test'})).to.be.equal('number'); expect(fn({value: 'test'})).to.be.above(0); }); }); describe('destroy()', function() { var has_namespaced_event = function($el, ns) { var i, n, key; var data = ($._data || $.data).apply($, [$(window)[0], 'events']); ns = ns.replace(/^./, ''); for (key in data) { if (data.hasOwnProperty(key)) { for (i = 0, n = data[key].length; i < n; i++) { if (data[key][i].namespace.indexOf(ns) !== -1) { return true; } } } } return false; }; it('should remove control from DOM', function() { var test = setup_test('', {}); test.selectize.destroy(); expect(test.selectize.$input[0].selectize).to.be.equal(undefined); }); it('should unbind events on window', function() { var test = setup_test('', {}); test.selectize.destroy(); expect(has_namespaced_event($(document), test.selectize.eventNS)).to.be.equal(false); }); it('should unbind events on ', function() { var test = setup_test('' + children + '', {}); test.selectize.destroy(); expect(test.$select.html()).to.be.equal(children); expect(test.$select.attr('tabindex')).to.be.equal('9999'); }); it('should remove tabindex if it was originally undefined', function() { var test = setup_test('', { valueField: 'value', labelField: 'value', options: [ {value: 0}, {value: 1}, {value: 2}, {value: 3}, ], items: ['1','2','3'] }); test.selectize.advanceSelection(1); test.selectize.refreshOptions(true); test.selectize.refreshItems(); }); it('should clear the whole renderCache', function () { expect($.isEmptyObject(test.selectize.renderCache)).to.be.equal(false); test.selectize.clearCache(); expect($.isEmptyObject(test.selectize.renderCache)).to.be.equal(true); }); it('should allow clearing just one template type from the renderCache', function () { test.selectize.render('item', test.selectize.options[0]); test.selectize.refreshOptions(); expect($.isEmptyObject(test.selectize.renderCache['option'])).to.be.equal(false); expect($.isEmptyObject(test.selectize.renderCache['item'])).to.be.equal(false); test.selectize.clearCache('option'); expect($.isEmptyObject(test.selectize.renderCache['option'])).to.be.equal(true); expect($.isEmptyObject(test.selectize.renderCache['item'])).to.be.equal(false); }); }); }); })();selectize.js-0.12.4/test/events.js000066400000000000000000000234371277751564300170530ustar00rootroot00000000000000describe('Events', function() { describe('focus', function() { it('should work as expected', function(done) { var test = setup_test('', {}); var counter = 0; test.selectize.on('focus', function() { counter++; }); test.selectize.focus(); syn.click(test.selectize.$control).delay(0, function() { window.setTimeout(function() { expect(counter).to.be.equal(1); done(); }, 0); }); }); }); describe('blur', function() { it('should work as expected', function(done) { var test = setup_test('', {}); var counter = 0; test.selectize.on('blur', function() { counter++; }); test.selectize.focus(); syn.click(test.selectize.$control).delay(0, function() { syn.click($('body')).delay(0, function() { window.setTimeout(function() { expect(counter).to.be.equal(1); done(); }, 0); }); }); }); }); describe('change', function() { it('should be triggered once', function(done) { var test = setup_test('', {}); var counter = 0; test.selectize.on('change', function() { counter++; }); test.selectize.setValue('b'); window.setTimeout(function() { expect(counter).to.be.equal(1); done(); }, 0); }); it('should contain current value', function(done) { var test = setup_test('', {}); test.selectize.on('change', function(value) { expect(value).to.be.equal('c'); done(); }); test.selectize.setValue('c'); }); it('should not be triggered when the selected item has not changed', function(done) { var test = setup_test(''); var counter = 0; test.$select.on('change', function() { counter++; }); syn.click(test.selectize.$control).delay(0, function() { syn .click($('[data-value="a"]', test.selectize.$dropdown)) .delay(0, function() { expect(counter).to.be.equal(0); done(); }); }); }); }); describe('item_add', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('item_add', function() { done(); }); test.selectize.addItem('b'); }); it('should contain item\'s value and element', function(done) { var test = setup_test('', {}); test.selectize.on('item_add', function(value, $item) { expect(value).to.be.equal('b'); assert.equal($item.length, 1); done(); }); test.selectize.addItem('b'); }); }); describe('item_remove', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('item_remove', function() { done(); }); test.selectize.removeItem('a'); }); it('should contain item\'s value and element', function(done) { var test = setup_test('', {}); test.selectize.on('item_remove', function(value, $item) { expect(value).to.be.equal('b'); assert.equal($item.length, 1); done(); }); test.selectize.removeItem('b'); }); }); describe('clear', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('clear', function() { done(); }); test.selectize.clear(); }); }); describe('optgroup_add', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('optgroup_add', function() { done(); }); test.selectize.addOptionGroup('id', {label: 'Group'}); }); it('should contain optgroup id', function(done) { var test = setup_test('', {}); test.selectize.on('optgroup_add', function(id, data) { expect(id).to.be.equal('id'); done(); }); test.selectize.addOptionGroup('id', {label: 'Group'}); }); it('should contain outgroup data', function(done) { var test = setup_test('', {}); var optgroup = {label: 'Group'}; test.selectize.on('optgroup_add', function(id, data) { expect(data).to.eql(optgroup); done(); }); test.selectize.addOptionGroup('id', optgroup); }); }); describe('optgroup_remove', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('optgroup_remove', function(id) { expect(id).to.be.equal('id'); done(); }); test.selectize.addOptionGroup('id', {label: 'Group'}); test.selectize.removeOptionGroup('id'); }); }); describe('optgroup_clear', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('optgroup_clear', function() { done(); }); test.selectize.addOptionGroup('id', {label: 'Group'}); test.selectize.clearOptionGroups(); }); }); describe('option_add', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('option_add', function() { done(); }); test.selectize.addOption({value: 'e'}); }); it('should contain option value', function(done) { var test = setup_test('', {}); test.selectize.on('option_add', function(value, data) { expect(value).to.be.equal('e'); done(); }); test.selectize.addOption({value: 'e'}); }); it('should contain option data', function(done) { var test = setup_test('', {}); var option = {value: 'e'}; test.selectize.on('option_add', function(value, data) { expect(option).to.eql(data); done(); }); test.selectize.addOption(option); }); }); describe('option_remove', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('option_remove', function() { done(); }); test.selectize.removeOption('a'); }); it('should contain option value', function(done) { var test = setup_test('', {}); test.selectize.on('option_remove', function(value) { expect(value).to.be.equal('a'); done(); }); test.selectize.removeOption('a'); }); }); describe('option_clear', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('option_clear', function() { done(); }); test.selectize.clearOptions(); }); }); describe('dropdown_open', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('dropdown_open', function() { done(); }); test.selectize.open(); }); }); describe('dropdown_close', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('dropdown_close', function() { done(); }); test.selectize.open(); test.selectize.close(); }); }); describe('destroy', function() { it('should be triggered', function(done) { var test = setup_test('', {}); test.selectize.on('destroy', function() { done(); }); test.selectize.destroy(); }); }); describe('type', function() { it('should be triggered', function(done) { var test = setup_test('', {create: true}); test.selectize.on('type', function() { done(); }); syn.click(test.selectize.$control).type('a', test.selectize.$control_input); }); it('should contain current value', function(done) { var test = setup_test('', {create: true}); test.selectize.on('type', function(value) { expect(value).to.be.equal('a'); done(); }); syn.click(test.selectize.$control).type('a', test.selectize.$control_input); }); }); });selectize.js-0.12.4/test/events_dom.js000066400000000000000000000026371277751564300177110ustar00rootroot00000000000000describe('DOM Events', function() { describe('"change"', function() { it('should be triggered once by addItem()', function(done) { var test = setup_test('', { valueField: 'value', labelField: 'value', options: [ {value: 'a'}, {value: 'b'}, ], items: ['a','b'] }); var counter = 0; test.$select.on('change', function() { counter++; }); test.selectize.removeItem('b'); window.setTimeout(function() { expect(counter).to.be.equal(1); done(); }, 0); }); it('should be triggered once by clear()', function(done) { var test = setup_test('' + '' + '' + '', {}); click(test.selectize.$control, function() { click($('[data-value=a]', test.selectize.$dropdown_content), function() { expect(test.selectize.isOpen).to.be.equal(true); expect(test.selectize.isFocused).to.be.equal(true); done(); }); }); }); it('should close dropdown after selection made if closeAfterSelect: true', function(done) { var test = setup_test('', {closeAfterSelect: true}); click(test.selectize.$control, function() { click($('[data-value=a]', test.selectize.$dropdown_content), function() { expect(test.selectize.isOpen).to.be.equal(false); expect(test.selectize.isFocused).to.be.equal(true); done(); }); }); }); it('should close and blur dropdown after selection made if closeAfterSelect: true and in single mode' , function(done) { var test = setup_test('', {closeAfterSelect: true}); click(test.selectize.$control, function() { expect(test.selectize.isOpen).to.be.equal(true); expect(test.selectize.isFocused).to.be.equal(true); click($('[data-value=a]', test.selectize.$dropdown_content), function() { expect(test.selectize.isOpen).to.be.equal(false); expect(test.selectize.isFocused).to.be.equal(false); done(); }); }); }); describe('clicking control', function() { it('should give it focus', function(done) { var test = setup_test('', {}); click(test.selectize.$control, function() { expect(test.selectize.isFocused).to.be.equal(true); done(); }); }); it('should start loading results if preload:"focus"', function(done) { var calls_focus = 0; var calls_load = 0; var test = setup_test('', { preload: 'focus', load: function(query, done) { calls_load++; assert.equal(query, ''); setTimeout(function() { done([{value: 'c', text: 'C'}]); }); } }); test.selectize.on('focus', function() { calls_focus++; }); click(test.selectize.$control, function() { setTimeout(function() { assert.equal(calls_focus, 1); assert.equal(calls_load, 1); done(); }, 300); }); }); it('should open dropdown menu', function(done) { var test = setup_test('', {}); click(test.selectize.$control, function() { expect(test.selectize.isOpen).to.be.equal(true); expect(test.selectize.$dropdown.is(':visible')).to.be.equal(true); done(); }); }); }); describe('clicking label', function() { it('should give it focus to select', function(done) { var inputId = "labeledSelect"; var label = $('').appendTo('form'); var test = setup_test('', {}); syn.click(label) .delay(0, function() { label.remove(); expect(test.selectize.isFocused).to.be.equal(true); done(); }); }); it('should give it focus to input', function(done) { var inputId = "labeledInput"; var label = $('').appendTo('form'); var test = setup_test('', {}); syn.click(label) .delay(0, function() { label.remove(); expect(test.selectize.isFocused).to.be.equal(true); done(); }); }); }); describe('clicking option', function() { it('should select it', function(done) { var test = setup_test('', {}); click(test.selectize.$control, function() { click($('[data-value="b"]', test.selectize.$dropdown), function() { expect(test.selectize.$input.val()).to.be.equal('b'); expect(test.selectize.$input.text()).to.be.equal('B'); done(); }); }); }); it('should close dropdown', function(done) { var test = setup_test('', {}); click(test.selectize.$control, function() { click($('[data-value="b"]', test.selectize.$dropdown), function() { expect(test.selectize.isOpen).to.be.equal(false); expect(test.selectize.$dropdown.is(':visible')).to.be.equal(false); done(); }); }); }); }); describe('typing in input', function() { it('should filter results', function(done) { var test = setup_test('', {}); click(test.selectize.$control, function() { syn.type('a', test.selectize.$control_input) .delay(0, function() { expect($('[data-value="a"]', test.selectize.$dropdown).length).to.be.equal(1); expect($('[data-value="b"]', test.selectize.$dropdown).length).to.be.equal(0); done(); }); }); }); it('should hide dropdown if no results present', function(done) { var test = setup_test('', {}); click(test.selectize.$control, function() { syn.type('awaw', test.selectize.$control_input) .delay(0, function() { expect(test.selectize.isOpen).to.be.equal(false); expect(test.selectize.$dropdown.is(':visible')).to.be.equal(false); done(); }); }); }); it('should not hide dropdown if "create" option enabled and no results present', function(done) { var test = setup_test('', {create: true}); click(test.selectize.$control, function() { syn.type('awaw', test.selectize.$control_input) .delay(0, function() { expect(test.selectize.isOpen).to.be.equal(true); expect(test.selectize.$dropdown.is(':visible')).to.be.equal(true); done(); }); }); }); it('should restore dropdown visibility when backing out of a query without results (backspace)', function(done) { var test = setup_test('', {}); click(test.selectize.$control, function() { syn.type('awf', test.selectize.$control_input) .type('\b\b\b', test.selectize.$control_input) .delay(0, function() { expect(test.selectize.isOpen).to.be.equal(true); expect(test.selectize.$dropdown.is(':visible')).to.be.equal(true); done(); }); }); }); it('should move caret when [left] or [right] pressed', function(done) { var test = setup_test('', {create: true}); click(test.selectize.$control, function() { syn.type('[left][left]whatt', test.selectize.$control_input) .delay(0, function() { expect(test.selectize.caretPos).to.be.equal(2); done(); }); }); }); it('should not create input if comma entered in single select mode', function(done) { var test = setup_test('', {create: true}); click(test.selectize.$control, function() { syn.type('asdf,asdf', test.selectize.$control_input) .delay(0, function() { expect(test.selectize.isOpen).to.be.equal(true); expect(test.selectize.options).to.not.have.property('asdf'); done(); }); }); }); }); describe('blurring the input', function() { it('should close dropdown when createOnBlur is true', function(done) { var test = setup_test('', { createOnBlur: true, create: function(value){ return { value: value, text: value }; } }); click(test.selectize.$control, function() { syn .type('fooo', test.selectize.$control_input) .delay(0, function() { expect(test.selectize.isOpen).to.be.equal(true); expect(test.selectize.$dropdown.is(':visible')).to.be.equal(true); syn .click($("body")) .delay(5, function() { expect(test.selectize.isOpen).to.be.equal(false); expect(test.selectize.$dropdown.is(':visible')).to.be.equal(false); done(); }); }); }); }); }); describe('filtering created items', function() { function createFilterTest(createFilter) { return setup_test('', {create: true, createFilter: createFilter}); } var text = 'abc'; function execFilterTest(test, done, expectation) { var selectize = test.selectize; click(selectize.$control, function() { syn .type(text, selectize.$control_input) .type(selectize.settings.delimiter, selectize.$control_input) .delay(0, function() { expectation(selectize); done(); }) }); } function execFilterTests(heading, filters, expectation) { for (var i = 0; i < filters.length; i++) { (function(filter) { it(heading, function(done) { execFilterTest(createFilterTest(filter), done, expectation); }); })(filters[i]); } } execFilterTests('should add an item normally if there is no createFilter', [undefined, null, ''], function(selectize) { expect(selectize.getItem(text).length).to.be.equal(1); }); execFilterTests('should add an item if the input matches the createFilter', ['a', /a/, function() { return true; }], function(selectize) { expect(selectize.getItem(text).length).to.be.equal(1); }); execFilterTests('should not add an item or display the create label if the input does not match the createFilter', ['foo', /foo/, function() { return false; }], function(selectize) { expect(selectize.getItem(text).length).to.be.equal(0); expect($(selectize.$dropdown_content).filter('.create').length).to.be.equal(0); }); }); }); })(); selectize.js-0.12.4/test/setup.js000066400000000000000000000305061277751564300167020ustar00rootroot00000000000000(function() { describe('Setup', function() { it('should not allow duplicate initialization', function() { var instance_before, instance_after, test; test = setup_test('', {}); instance_before = test.$select[0].selectize; test.$select.selectize(); instance_after = test.$select[0].selectize; expect(instance_before).to.be.equal(instance_after); }); describe('', function() { it('should complete without exceptions', function() { var test = setup_test('', {}); }); it('should populate items,options from "dataAttr" if available', function() { var data = [{val: 'a', lbl: 'Hello'}, {val: 'b', lbl: 'World'}]; var test = setup_test('', { dataAttr: 'data-hydrate', valueField: 'val', labelField: 'lbl' }); expect(test.selectize.getValue()).to.be.equal('a,b'); assert.deepEqual(test.selectize.items, ['a','b']); assert.deepEqual(test.selectize.options, { 'a': {val: 'a', lbl: 'Hello', $order: 1}, 'b': {val: 'b', lbl: 'World', $order: 2} }); }); describe('getValue()', function() { it('should return value as a string', function() { var test = setup_test('', {delimiter: ','}); expect(test.selectize.getValue()).to.be.a('string'); }); it('should return "" when empty', function() { var test = setup_test('', {delimiter: ','}); expect(test.selectize.getValue()).to.be.equal(''); }); it('should return proper value when not empty', function() { var test = setup_test('', {delimiter: ','}); expect(test.selectize.getValue()).to.be.equal('a,b'); }); }); describe('', function() { it('should propagate original input attributes to the generated input', function() { var test = setup_test('', {}); expect(test.selectize.$control_input.attr('autocorrect')).to.be.equal('off'); expect(test.selectize.$control_input.attr('autocapitalize')).to.be.equal('none'); }); it('should not add attributes if not present in the original', function() { var test = setup_test('', {}); expect(test.selectize.$control_input.attr('autocorrect')).to.be.equal(undefined); expect(test.selectize.$control_input.attr('autocapitalize')).to.be.equal(undefined); }); }); }); describe('', {}); }); it('should allow for values optgroups with duplicated options', function() { var test = setup_test([''].join(''), { optgroupValueField: 'val', optgroupField: 'grp' }); assert.deepEqual(test.selectize.options, { 'a': {text: 'Item A', value: 'a', grp: ['Group 1', 'Group 2'], $order: 1}, 'b': {text: 'Item B', value: 'b', grp: ['Group 1', 'Group 2'], $order: 2} }); assert.deepEqual(test.selectize.optgroups, { 'Group 1': {label: 'Group 1', val: 'Group 1', $order: 3}, 'Group 2': {label: 'Group 2', val: 'Group 2', $order: 4} }); }); it('should add options in text form (no html entities)', function() { var test = setup_test('', {}); expect(test.selectize.options['a'].text).to.be.equal(''); }); it('should keep options in original order if no sort given', function(done) { var test = setup_test([ '' ].join(), {}); 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']; var order_actual = []; test.selectize.refreshOptions(true); window.setTimeout(function() { test.selectize.$dropdown.find('[data-value]').each(function(i, el) { order_actual.push($(el).attr('data-value')); }); expect(order_actual).to.eql(order_expected); done(); }, 0); }); describe('getValue()', function() { it('should return "" when empty', function() { var test = setup_test('', {}); expect(test.selectize.getValue()).to.be.equal('a'); }); }); }); describe('', {}); }); describe('getValue()', function() { it('should return [] when empty', function() { var test = setup_test('', {}); expect(test.selectize.getValue()).to.deep.equal(['a']); }); }); }); describe('', {}); }); it('should have "disabled" class', function() { expect(test.selectize.$control.hasClass('disabled')).to.be.equal(true); }); it('should have isDisabled property set to true', function() { expect(test.selectize.isDisabled).to.be.equal(true); }); }); describe('' + '' + '' + '', {}); $form = test.$select.parents('form'); $button = $('