├── .editorconfig
├── .eslintrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE.md
├── README.md
├── _config.yml
├── bootstrap-tokenfield.jquery.json
├── bower.json
├── dist
├── bootstrap-tokenfield.js
├── bootstrap-tokenfield.min.js
└── css
│ ├── bootstrap-tokenfield.css
│ ├── bootstrap-tokenfield.min.css
│ ├── tokenfield-typeahead.css
│ └── tokenfield-typeahead.min.css
├── docs-assets
├── css
│ ├── docs.css
│ └── pygments-manni.css
└── js
│ ├── affix.js
│ ├── docs.js
│ ├── docs.min.js
│ ├── scrollspy.js
│ ├── typeahead.bundle.js
│ └── typeahead.bundle.min.js
├── index.html
├── js
└── bootstrap-tokenfield.js
├── less
├── bootstrap-tokenfield.less
└── tokenfield-typeahead.less
├── package.json
├── tasks
├── copy.js
├── eslint.js
├── exec.js
├── less.js
├── uglify.js
└── watch.js
└── test
├── README.md
├── setup.js
├── test.tokenfield.1.unit.js
└── test.tokenfield.2.integration.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 | indent_style = space
12 | indent_size = 4
13 |
14 | [{package.json,bower.json}]
15 | indent_style = space
16 | indent_size = 2
17 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | 'rules': {
3 | // BEST PRACTICES
4 |
5 | // Enforces getter/setter pairs in objects
6 | 'accessor-pairs': 0,
7 | // treat var statements as if they were block scoped
8 | 'block-scoped-var': 0,
9 | // specify the maximum cyclomatic complexity allowed in a program
10 | 'complexity': [0, 11],
11 | // require return statements to either always or never specify values
12 | 'consistent-return': 0,
13 | // specify curly brace conventions for all control statements
14 | 'curly': [2, 'multi-line'],
15 | // require default case in switch statements
16 | 'default-case': 2,
17 | // encourages use of dot notation whenever possible
18 | 'dot-notation': [2, { 'allowKeywords': false }],
19 | // enforces consistent newlines before or after dots
20 | 'dot-location': 0,
21 | // require the use of === and !==
22 | 'eqeqeq': 2,
23 | // make sure for-in loops have an if statement
24 | 'guard-for-in': 0,
25 | // disallow the use of alert, confirm, and prompt
26 | 'no-alert': 1,
27 | // disallow use of arguments.caller or arguments.callee
28 | 'no-caller': 2,
29 | // disallow division operators explicitly at beginning of regular expression
30 | 'no-div-regex': 0,
31 | // disallow else after a return in an if
32 | 'no-else-return': 0,
33 | // disallow use of labels for anything other then loops and switches
34 | 'no-empty-label': 2,
35 | // disallow comparisons to null without a type-checking operator
36 | 'no-eq-null': 0,
37 | // disallow use of eval()
38 | 'no-eval': 2,
39 | // disallow adding to native types
40 | 'no-extend-native': 2,
41 | // disallow unnecessary function binding
42 | 'no-extra-bind': 2,
43 | // disallow fallthrough of case statements
44 | 'no-fallthrough': 2,
45 | // disallow the use of leading or trailing decimal points in numeric literals
46 | 'no-floating-decimal': 2,
47 | // disallow the type conversions with shorter notations
48 | 'no-implicit-coercion': 0,
49 | // disallow use of eval()-like methods
50 | 'no-implied-eval': 2,
51 | // disallow this keywords outside of classes or class-like objects
52 | 'no-invalid-this': 0,
53 | // disallow usage of __iterator__ property
54 | 'no-iterator': 2,
55 | // disallow use of labeled statements
56 | 'no-labels': 2,
57 | // disallow unnecessary nested blocks
58 | 'no-lone-blocks': 2,
59 | // disallow creation of functions within loops
60 | 'no-loop-func': 2,
61 | // disallow use of multiple spaces
62 | 'no-multi-spaces': 2,
63 | // disallow use of multiline strings
64 | 'no-multi-str': 2,
65 | // disallow reassignments of native objects
66 | 'no-native-reassign': 2,
67 | // disallow use of new operator when not part of the assignment or comparison
68 | 'no-new': 0,
69 | // disallow use of new operator for Function object
70 | 'no-new-func': 2,
71 | // disallows creating new instances of String,Number, and Boolean
72 | 'no-new-wrappers': 2,
73 | // disallow use of (old style) octal literals
74 | 'no-octal': 2,
75 | // disallow use of octal escape sequences in string literals, such as
76 | // var foo = 'Copyright \251';
77 | 'no-octal-escape': 2,
78 | // disallow reassignment of function parameters
79 | 'no-param-reassign': 0,
80 | // disallow use of process.env
81 | 'no-process-env': 0,
82 | // disallow usage of __proto__ property
83 | 'no-proto': 2,
84 | // disallow declaring the same variable more then once
85 | 'no-redeclare': 2,
86 | // disallow use of assignment in return statement
87 | 'no-return-assign': 2,
88 | // disallow use of `javascript:` urls.
89 | 'no-script-url': 2,
90 | // disallow comparisons where both sides are exactly the same
91 | 'no-self-compare': 2,
92 | // disallow use of comma operator
93 | 'no-sequences': 2,
94 | // restrict what can be thrown as an exception
95 | 'no-throw-literal': 0,
96 | // disallow usage of expressions in statement position
97 | 'no-unused-expressions': 2,
98 | // disallow unnecessary .call() and .apply()
99 | 'no-useless-call': 0,
100 | // disallow use of void operator
101 | 'no-void': 0,
102 | // disallow usage of configurable warning terms in comments: e.g. todo
103 | 'no-warning-comments': [0, { 'terms': ['todo', 'fixme', 'xxx'], 'location': 'start' }],
104 | // disallow use of the with statement
105 | 'no-with': 2,
106 | // require use of the second argument for parseInt()
107 | 'radix': 2,
108 | // requires to declare all vars on top of their containing scope
109 | 'vars-on-top': 0,
110 | // require immediate function invocation to be wrapped in parentheses
111 | 'wrap-iife': [2, 'any'],
112 | // require or disallow Yoda conditions
113 | 'yoda': 2,
114 |
115 | // STYLE
116 |
117 | // enforce spacing inside array brackets
118 | 'array-bracket-spacing': 0,
119 | // enforce one true brace style
120 | 'brace-style': [2, '1tbs', {'allowSingleLine': true }],
121 | // require camel case names
122 | 'camelcase': 0,
123 | // enforce spacing before and after comma
124 | 'comma-spacing': [2, {'before': false, 'after': true}],
125 | // enforce one true comma style
126 | 'comma-style': [2, 'last'],
127 | // require or disallow padding inside computed properties
128 | 'computed-property-spacing': 0,
129 | // enforces consistent naming when capturing the current execution context
130 | 'consistent-this': 0,
131 | // enforce newline at the end of file, with no multiple empty lines
132 | 'eol-last': 2,
133 | // require function expressions to have a name
134 | 'func-names': 0,
135 | // enforces use of function declarations or expressions
136 | 'func-style': 0,
137 | // this option enforces minimum and maximum identifier lengths (variable names, property names etc.)
138 | 'id-length': 0,
139 | // this option sets a specific tab width for your code
140 | 'indent': [2, 4, {"SwitchCase": 1}],
141 | // specify whether double or single quotes should be used in JSX attributes
142 | 'jsx-quotes': 2,
143 | // enforces spacing between keys and values in object literal properties
144 | 'key-spacing': [2, {"beforeColon": false, "afterColon": true}],
145 | // enforces empty lines around comments
146 | 'lines-around-comment': 0,
147 | // disallow mixed 'LF' and 'CRLF' as linebreaks
148 | 'linebreak-style': 2,
149 | // specify the maximum depth callbacks can be nested
150 | 'max-nested-callbacks': 0,
151 | // require a capital letter for constructors
152 | 'new-cap': [2, {'capIsNew': false, newIsCap: false}],
153 | // disallow the omission of parentheses when invoking a constructor with no arguments
154 | 'new-parens': 0,
155 | // allow/disallow an empty newline after var statement
156 | 'newline-after-var': 0,
157 | // disallow use of the Array constructor
158 | 'no-array-constructor': 0,
159 | // disallow use of the continue statement
160 | 'no-continue': 0,
161 | // disallow comments inline after code
162 | 'no-inline-comments': 0,
163 | // disallow if as the only statement in an else block
164 | 'no-lonely-if': 2,
165 | // disallow mixed spaces and tabs for indentation
166 | 'no-mixed-spaces-and-tabs': 2,
167 | // disallow multiple empty lines
168 | 'no-multiple-empty-lines': [2, {'max': 2}],
169 | // disallow nested ternary expressions
170 | 'no-nested-ternary': 2,
171 | // disallow use of the Object constructor
172 | 'no-new-object': 2,
173 | // disallow space between function identifier and application
174 | 'no-spaced-func': 2,
175 | // disallow the use of ternary operators
176 | 'no-ternary': 0,
177 | // disallow trailing whitespace at the end of lines
178 | 'no-trailing-spaces': 2,
179 | // disallow dangling underscores in identifiers
180 | 'no-underscore-dangle': 0,
181 | // disallow the use of Boolean literals in conditional expressions
182 | 'no-unneeded-ternary': 2,
183 | // require or disallow padding inside curly braces
184 | 'object-curly-spacing': [2, "always", {
185 | "objectsInObjects": false,
186 | "arraysInObjects": false
187 | }],
188 | // allow just one var statement per function
189 | 'one-var': 0,
190 | // require assignment operator shorthand where possible or prohibit it entirely
191 | 'operator-assignment': 0,
192 | // enforce operators to be placed before or after line breaks
193 | 'operator-linebreak': 0,
194 | // enforce padding within blocks
195 | 'padded-blocks': 0,
196 | // require quotes around object literal property names
197 | 'quote-props': 0,
198 | // specify whether double or single quotes should be used
199 | 'quotes': [2, 'single', 'avoid-escape'],
200 | // require identifiers to match the provided regular expression
201 | 'id-match': 0,
202 | // enforce spacing before and after semicolons
203 | 'semi-spacing': [2, {'before': false, 'after': true}],
204 | // require or disallow use of semicolons instead of ASI
205 | 'semi': [2, 'always'],
206 | // sort variables within the same declaration block
207 | 'sort-vars': 0,
208 | // require a space after certain keywords
209 | 'space-after-keywords': 2,
210 | // require or disallow space before blocks
211 | 'space-before-blocks': 2,
212 | // require or disallow space before function opening parenthesis
213 | 'space-before-function-paren': [2, {"anonymous": "always", "named": "never"}],
214 | // require or disallow spaces inside parentheses
215 | 'space-in-parens': 2,
216 | // require spaces around operators
217 | 'space-infix-ops': 2,
218 | // require a space after return, throw, and case
219 | 'space-return-throw-case': 2,
220 | // Require or disallow spaces before/after unary operators
221 | 'space-unary-ops': 0,
222 | // require or disallow a space immediately following the // or /* in a comment
223 | 'spaced-comment': [2, 'always', {'exceptions': ['-', '+'], 'markers': ['=', '!', '#.']}],
224 |
225 | // require regex literals to be wrapped in parentheses
226 | 'wrap-regex': 0
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /_site/
2 | /bower_components/
3 | /node_modules/
4 | /tmp/
5 | *.tgz
6 | *.sublime-project
7 | *.sublime-workspace
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /_site/
2 | /bower_components/
3 | /*.tgz
4 | /tmp/
5 | /.travis.yml
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4.2"
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to this project
2 |
3 | Please take a moment to review this document in order to make the contribution
4 | process easy and effective for everyone involved.
5 |
6 | Following these guidelines helps to communicate that you respect the time of
7 | the developers managing and developing this open source project. In return,
8 | they should reciprocate that respect in addressing your issue or assessing
9 | patches and features.
10 |
11 |
12 | ## Using the issue tracker
13 |
14 | The issue tracker is the preferred channel for [bug reports](#bugs),
15 | [features requests](#features) and [submitting pull
16 | requests](#pull-requests), but please respect the following restrictions:
17 |
18 | * Please **do not** use the issue tracker for personal support requests (use
19 | [Stack Overflow](http://stackoverflow.com)).
20 |
21 | * Please **do not** derail or troll issues. Keep the discussion on topic and
22 | respect the opinions of others.
23 |
24 |
25 |
26 | ## Bug reports
27 |
28 | A bug is a _demonstrable problem_ that is caused by the code in the repository.
29 | Good bug reports are extremely helpful - thank you!
30 |
31 | Guidelines for bug reports:
32 |
33 | 1. **Use the GitHub issue search** — check if the issue has already been
34 | reported.
35 |
36 | 2. **Check if the issue has been fixed** — try to reproduce it using the
37 | latest `master` or development branch in the repository.
38 |
39 | 3. **Isolate the problem** — create a [reduced test
40 | case](http://css-tricks.com/6263-reduced-test-cases/) and **a live example** (preferrably using jsfiddle). Please understand that it takes a lot of time to re-create your issue without a live example. **Issues without live examples will be ignored**.
41 |
42 | A good bug report shouldn't leave others needing to chase you up for more
43 | information. Please try to be as detailed as possible in your report. What is
44 | your environment? What steps will reproduce the issue? What browser(s) and OS
45 | experience the problem? What would you expect to be the outcome? All these
46 | details will help people to fix any potential bugs.
47 |
48 | Example:
49 |
50 | > Short and descriptive example bug report title
51 | >
52 | > A summary of the issue and the browser/OS environment in which it occurs. If
53 | > suitable, include the steps required to reproduce the bug.
54 | >
55 | > 1. This is the first step
56 | > 2. This is the second step
57 | > 3. Further steps, etc.
58 | >
59 | > `` - a link to the reduced test case
60 | >
61 | > Any other information you want to share that is relevant to the issue being
62 | > reported. This might include the lines of code that you have identified as
63 | > causing the bug, and potential solutions (and your opinions on their
64 | > merits).
65 |
66 |
67 |
68 | ## Feature requests
69 |
70 | Feature requests are welcome. But take a moment to find out whether your idea
71 | fits with the scope and aims of the project. It's up to *you* to make a strong
72 | case to convince the project's developers of the merits of this feature. Please
73 | provide as much detail and context as possible.
74 |
75 |
76 |
77 | ## Pull requests
78 |
79 | Good pull requests - patches, improvements, new features - are a fantastic
80 | help. They should remain focused in scope and avoid containing unrelated
81 | commits.
82 |
83 | **Please ask first** before embarking on any significant pull request (e.g.
84 | implementing features, refactoring code, porting to a different language),
85 | otherwise you risk spending a lot of time working on something that the
86 | project's developers might not want to merge into the project.
87 |
88 | Please adhere to the coding conventions used throughout a project (indentation,
89 | accurate comments, etc.) and any other requirements (such as test coverage).
90 |
91 | Follow this process if you'd like your work considered for inclusion in the
92 | project:
93 |
94 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
95 | and configure the remotes:
96 |
97 | ```bash
98 | # Clone your fork of the repo into the current directory
99 | git clone https://github.com//
100 | # Navigate to the newly cloned directory
101 | cd
102 | # Assign the original repo to a remote called "upstream"
103 | git remote add upstream https://github.com//
104 | ```
105 |
106 | 2. If you cloned a while ago, get the latest changes from upstream:
107 |
108 | ```bash
109 | git checkout
110 | git pull upstream
111 | ```
112 |
113 | 3. Create a new topic branch (off the main project development branch) to
114 | contain your feature, change, or fix:
115 |
116 | ```bash
117 | git checkout -b
118 | ```
119 |
120 | 4. Commit your changes in logical chunks. Please adhere to these [git commit
121 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
122 | or your code is unlikely be merged into the main project. Use Git's
123 | [interactive rebase](https://help.github.com/articles/interactive-rebase)
124 | feature to tidy up your commits before making them public.
125 |
126 | 5. Locally merge (or rebase) the upstream development branch into your topic branch:
127 |
128 | ```bash
129 | git pull [--rebase] upstream
130 | ```
131 |
132 | 6. Push your topic branch up to your fork:
133 |
134 | ```bash
135 | git push origin
136 | ```
137 |
138 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
139 | with a clear title and description.
140 |
141 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to
142 | license your work under the same license as that used by the project.
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 |
3 | require('jit-grunt')(grunt);
4 |
5 | var semver = require('semver'),
6 | f = require('util').format;
7 |
8 | grunt.initConfig({
9 | pkg: grunt.file.readJSON('package.json'),
10 | version: '<%= pkg.version %>',
11 |
12 | banner: [
13 | '/*!',
14 | ' * bootstrap-tokenfield <%= version %>',
15 | ' * https://github.com/sliptree/bootstrap-tokenfield',
16 | ' * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT',
17 | ' */\n\n'
18 | ].join('\n'),
19 |
20 | jekyll: {
21 | docs: {}
22 | }
23 | });
24 |
25 | grunt.registerTask('manifests', 'Update manifests.', function (version) {
26 | var _ = grunt.util._,
27 | pkg = grunt.file.readJSON('package.json'),
28 | bower = grunt.file.readJSON('bower.json'),
29 | jqueryPlugin = grunt.file.readJSON('bootstrap-tokenfield.jquery.json');
30 |
31 | bower = JSON.stringify(_.extend(bower, {
32 | name: pkg.name,
33 | version: version
34 | }), null, 2);
35 |
36 | jqueryPlugin = JSON.stringify(_.extend(jqueryPlugin, {
37 | name: pkg.name,
38 | title: pkg.name,
39 | version: version,
40 | author: pkg.author,
41 | description: pkg.description,
42 | keywords: pkg.keywords,
43 | homepage: pkg.homepage,
44 | bugs: pkg.bugs,
45 | maintainers: pkg.contributors
46 | }), null, 2);
47 |
48 | pkg = JSON.stringify(_.extend(pkg, {
49 | version: version
50 | }), null, 2);
51 |
52 | grunt.file.write('package.json', pkg);
53 | grunt.file.write('bower.json', bower);
54 | grunt.file.write('bootstrap-tokenfield.jquery.json', jqueryPlugin);
55 | });
56 |
57 | grunt.registerTask('release', 'Ship it.', function (version) {
58 | var curVersion = grunt.config.get('version');
59 |
60 | version = semver.inc(curVersion, version) || version;
61 |
62 | if (!semver.valid(version) || semver.lte(version, curVersion)) {
63 | grunt.fatal('invalid version dummy');
64 | }
65 |
66 | grunt.config.set('version', version);
67 |
68 | grunt.task.run([
69 | 'exec:git_on_master',
70 | 'exec:git_is_clean',
71 | 'manifests:' + version,
72 | 'build',
73 | 'exec:git_add',
74 | 'exec:git_commit:' + version,
75 | 'exec:git_tag:' + version,
76 | 'exec:update_docs'
77 | // 'exec:git_push',
78 | // 'exec:npm_publish',
79 | ]);
80 | });
81 |
82 | grunt.loadTasks('tasks/');
83 |
84 | // Build task
85 | grunt.registerTask('default', ['eslint', 'copy', 'uglify', 'less']);
86 | };
87 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | #### Sliptree
2 | - by Illimar Tambek for [Sliptree](http://sliptree.com)
3 | - Copyright (c) 2013 by Sliptree
4 |
5 | Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License)
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in
15 | all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Bootstrap Tokenfield
2 | ====================
3 | [![NPM version][npm-badge]](http://badge.fury.io/js/bootstrap-tokenfield)
4 | [![Build status][travis-badge]](https://travis-ci.org/Open-Xchange-Frontend/bootstrap-tokenfield)
5 | [npm-badge]: https://badge.fury.io/js/bootstrap-tokenfield.png
6 | [travis-badge]: https://travis-ci.org/Open-Xchange-Frontend/bootstrap-tokenfield.png?branch=master
7 |
8 | A jQuery tagging / tokenizer input plugin for Twitter's Bootstrap, by the guys from [Sliptree](https://sliptree.com)
9 |
10 | Check out the [demo and docs](http://sliptree.github.io/bootstrap-tokenfield/)
11 |
12 | ### Installation
13 |
14 | Requirements: jQuery 1.9+, Bootstrap 3+ (only CSS)
15 |
16 | 1. Install via npm or bower (recommended) or manually download the package
17 | 2. Include `dist/bootstrap-tokenfield.js` or `dist/bootstrap-tokenfield.min.js` in your HTML
18 | 3. Include `dist/css/bootstrap-tokenfield.css` in your HTML
19 |
20 | ### Usage
21 |
22 | ```js
23 | $('input').tokenfield()
24 | ```
25 |
26 | ### Features
27 |
28 | * Copy & paste tokens with Ctrl+C and Ctrl+V
29 | * Keyboard navigation, delete tokens with keyboard (arrow keys, Shift + arrow keys)
30 | * Select specific tokens with Ctrl + click and Shift + click
31 | * Twitter Typeahead and jQuery UI Autocomplete support
32 |
33 | ### FAQ
34 |
35 | #### How can I prevent duplicate tokens from being entered?
36 |
37 | You can use the `tokenfield:createtoken` event for that. Check the `event.attrs` property for token value and label,
38 | and the run your duplicate detection logic. If it's a duplicate token, simply do `event.preventDefault()`.
39 |
40 | Here's a simple example that checks if token's value is equal to any of the existing tokens' values.
41 |
42 | ```js
43 | $('#my-tokenfield').on('tokenfield:createtoken', function (event) {
44 | var existingTokens = $(this).tokenfield('getTokens');
45 | $.each(existingTokens, function(index, token) {
46 | if (token.value === event.attrs.value)
47 | event.preventDefault();
48 | });
49 | });
50 | ```
51 |
52 | #### And how about limiting tokens to my typeahead/autocomplete data?
53 |
54 | Similarly, using `tokenfield:createtoken`, you can check to see if a token exists in your autocomplete/typeahead
55 | data. This example checks if the given token already exists and stops its entry if it doesn't.
56 |
57 | ```js
58 | $('#my-tokenfield').on('tokenfield:createtoken', function (event) {
59 | var available_tokens = bloodhound_tokens.index.datums
60 | var exists = true;
61 | $.each(available_tokens, function(index, token) {
62 | if (token.value === event.attrs.value)
63 | exists = false;
64 | });
65 | if(exists === true)
66 | event.preventDefault();
67 | })
68 | ```
69 |
70 |
71 |
72 | ### Changelog
73 |
74 | See [release notes](https://github.com/sliptree/bootstrap-tokenfield/releases)
75 |
76 | Previous releases:
77 |
78 | 0.10.0
79 |
80 | * Fixed: Entering a duplicate token does not submit the underlying form anymore
81 | * Fixed: Selecting a duplicate token from autocomplete or typeahead suggestions no longer clears the input
82 | * Improved: Trying to enter a duplicate tag now animates the existing tag for a little while
83 | * Improved: Tokenfield input has now `autocomplete="off"` to prevent browser-specific autocomplete suggestions
84 | * Changed: `triggerKeys` has been renamed to `delimiter` and accepts a single or an array of characters instead of character codes.
85 |
86 | 0.9.9-1
87 |
88 | * Fixed: setTokens now respects `triggerKeys` option
89 |
90 | 0.9.8
91 |
92 | * New: `triggerKeys` option
93 | * Fixed: Long placeholders are not being cut off anymore when initializing tokenfield with no tokens #37
94 | * Fixed: createTokensOnBlur no more breaks token editing #35
95 |
96 | 0.9.7 Valuable
97 |
98 | * Fixed: Twitter Typeahead valueKey support #18
99 | * Fixed: Removing multiple tokens returned wrong data #30
100 | * Fixed: If token is removed in beforeEdit event, no longer falls over #27, #28
101 | * Fixed: Change event was triggered on initialization #22
102 | * Fixed: When token is removed in tokenfield:preparetoken event, no longer tries to create a token
103 | * Fixed: Pressing comma key was not handled reliably
104 | * New: `prevetDuplicateToken` event
105 | * Improved: Typeahead integration
106 | * Improved: styling
107 | * Minor tweaks, fixes, improvements
108 |
109 | 0.9.5 Typeable
110 |
111 | * New: Twitter Typeahead support
112 | * New: When triggering 'change' event on original input, setTokens is now called. This allows you to update tokens externally.
113 | * Fixed: Nnput labels did not work with tokenfield
114 | * Fixed: Set correct input width on fixed-width inputs
115 |
116 | 0.9.2 Maintenance release
117 |
118 | * Many small fixes and improvements
119 |
120 | 0.9.0 Bootstrappable
121 |
122 | * New: Bootstrap 3 support
123 | * New: Input group support
124 | * New: Disable/enable tokenfield
125 | * New: Tokenfield is now responsive
126 | * Deprecated: Bootstrap 2 support
127 |
128 | 0.7.1
129 |
130 | * Fixed: pressing comma did not create a token in Firefox
131 | * Fixed: tokenfield('getTokensList') returned array instead of string
132 |
133 | 0.7.0 Autocompleted
134 |
135 | * New feature: jQuery UI Autocomplete support
136 |
137 | 0.6.7 Crossable
138 |
139 | * Fixed: Firefox close icon was misplaced
140 | * New: IE 8-10 support (both CSS and Javascript)
141 |
142 | 0.6.5 Shiftable
143 |
144 | * New feature: select specific tokens with Ctrl/Shift + click
145 | * New feature: select specific tokens with Shift + arrow keys
146 | * Internal API improvements
147 |
148 | 0.6 Editable
149 |
150 | * New feature: Edit existing tokens by double-clicking or pressing enter
151 | * A lot of improvements and bugfixes
152 |
153 | 0.5 Initial release
154 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | safe: true
2 | lsi: false
3 | pygments: true
4 | exclude: [README.md, LICENSE.md, CONTRIBUTING.md, .gitignore, .npmignore, .travis.yml, bower.json, package.json, bootstrap-tokenfield.jquery.json, Gruntfile.js, less, test, bower_components, node_modules, Tokenfield.sublime-project]
--------------------------------------------------------------------------------
/bootstrap-tokenfield.jquery.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bootstrap-tokenfield",
3 | "version": "0.12.1",
4 | "title": "bootstrap-tokenfield",
5 | "author": {
6 | "name": "ragulka",
7 | "url": "https://github.com/ragulka"
8 | },
9 | "licenses": [
10 | {
11 | "type": "MIT",
12 | "url": "http://opensource.org/licenses/MIT"
13 | }
14 | ],
15 | "dependencies": {
16 | "jquery": "*"
17 | },
18 | "description": "Advanced tagging/tokenizing plugin for input fields with a focus on keyboard and copy-paste support.",
19 | "keywords": [
20 | "jquery",
21 | "javascript",
22 | "bootstrap",
23 | "tokenfield",
24 | "tags",
25 | "token",
26 | "input",
27 | "select",
28 | "form"
29 | ],
30 | "homepage": "http://sliptree.github.io/bootstrap-tokenfield/",
31 | "docs": "http://sliptree.github.io/bootstrap-tokenfield/",
32 | "bugs": {
33 | "url": "https://github.com/sliptree/bootstrap-tokenfield/issues"
34 | }
35 | }
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bootstrap-tokenfield",
3 | "description": "Advanced tagging/tokenizing plugin for input fields with a focus on keyboard and copy-paste support.",
4 | "version": "0.12.1",
5 | "authors": [
6 | {
7 | "name": "ragulka",
8 | "homepage": "https://github.com/ragulka"
9 | },
10 | {
11 | "name": "sliptree",
12 | "homepage": "https://github.com/sliptree"
13 | }
14 | ],
15 | "ignore": [
16 | "**/.*",
17 | "node_modules",
18 | "bower_components",
19 | "doc-assets",
20 | "test",
21 | "tests"
22 | ],
23 | "homepage": "http://sliptree.github.io/bootstrap-tokenfield/",
24 | "main": [
25 | "dist/bootstrap-tokenfield.js",
26 | "dist/css/bootstrap-tokenfield.css"
27 | ],
28 | "keywords": [
29 | "jquery",
30 | "javascript",
31 | "bootstrap",
32 | "tokenfield",
33 | "tags",
34 | "token",
35 | "input",
36 | "select",
37 | "form"
38 | ],
39 | "dependencies": {
40 | "jquery": "~2.1.4",
41 | "bootstrap": "~3.3.5"
42 | },
43 | "devDependecies": {
44 | "mocha": "~1.12.*",
45 | "chai": "~1.7.*",
46 | "typeahead.js": "~0.10.1"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/dist/bootstrap-tokenfield.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * bootstrap-tokenfield
3 | * https://github.com/sliptree/bootstrap-tokenfield
4 | * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
5 | */
6 |
7 | (function (factory) {
8 | if (typeof define === 'function' && define.amd) {
9 | // AMD. Register as an anonymous module.
10 | define(['jquery'], factory);
11 | } else if (typeof exports === 'object') {
12 | // For CommonJS and CommonJS-like environments where a window with jQuery
13 | // is present, execute the factory with the jQuery instance from the window object
14 | // For environments that do not inherently posses a window with a document
15 | // (such as Node.js), expose a Tokenfield-making factory as module.exports
16 | // This accentuates the need for the creation of a real window or passing in a jQuery instance
17 | // e.g. require("bootstrap-tokenfield")(window); or require("bootstrap-tokenfield")($);
18 | module.exports = global.window && global.window.$ ?
19 | factory(global.window.$) :
20 | function (input) {
21 | if (!input.$ && !input.fn) {
22 | throw new Error('Tokenfield requires a window object with jQuery or a jQuery instance');
23 | }
24 | return factory(input.$ || input);
25 | };
26 | } else {
27 | // Browser globals
28 | factory(jQuery, window);
29 | }
30 | }(function ($, window) {
31 |
32 | 'use strict';
33 |
34 | /* TOKENFIELD PUBLIC CLASS DEFINITION */
35 |
36 | var Tokenfield = function (element, options) {
37 | var _self = this;
38 |
39 | this.$element = $(element);
40 | this.textDirection = this.$element.css('direction');
41 |
42 | // Extend options
43 | this.options = $.extend(true, {}, $.fn.tokenfield.defaults, { tokens: this.$element.val() }, this.$element.data(), options);
44 |
45 | // Setup delimiters and trigger keys
46 | this._delimiters = (typeof this.options.delimiter === 'string') ? [this.options.delimiter] : this.options.delimiter;
47 | this._triggerKeys = $.map(this._delimiters, function (delimiter) {
48 | return delimiter.charCodeAt(0);
49 | });
50 | this._firstDelimiter = this._delimiters[0];
51 |
52 | // Check for whitespace, dash and special characters
53 | var whitespace = $.inArray(' ', this._delimiters),
54 | dash = $.inArray('-', this._delimiters);
55 |
56 | if (whitespace >= 0) {
57 | this._delimiters[whitespace] = '\\s';
58 | }
59 |
60 | if (dash >= 0) {
61 | delete this._delimiters[dash];
62 | this._delimiters.unshift('-');
63 | }
64 |
65 | var specialCharacters = ['\\', '$', '[', '{', '^', '.', '|', '?', '*', '+', '(', ')'];
66 | $.each(this._delimiters, function (index, character) {
67 | var pos = $.inArray(character, specialCharacters);
68 | if (pos >= 0) _self._delimiters[index] = '\\' + character;
69 | });
70 |
71 | // Store original input width
72 | var elStyleWidth = element.style.width,
73 | elWidth = this.$element.width();
74 |
75 | // Move original input out of the way
76 | var hidingPosition = $('body').css('direction') === 'rtl' ? 'right' : 'left',
77 | originalStyles = { position: this.$element.css('position') };
78 |
79 | originalStyles[hidingPosition] = this.$element.css(hidingPosition);
80 |
81 | this.$element
82 | .data('original-styles', originalStyles)
83 | .data('original-tabindex', this.$element.prop('tabindex'))
84 | .css('position', 'absolute')
85 | .css(hidingPosition, '-10000px')
86 | .prop('tabindex', -1);
87 |
88 | // Create a wrapper
89 | this.$wrapper = $('
');
90 |
91 | if (this.$element.hasClass('input-lg')) this.$wrapper.addClass('input-lg');
92 | if (this.$element.hasClass('input-sm')) this.$wrapper.addClass('input-sm');
93 | if (this.textDirection === 'rtl') this.$wrapper.addClass('rtl');
94 |
95 | // Create a new input
96 | var id = this.$element.prop('id') || new Date().getTime() + '' + Math.floor((1 + Math.random()) * 100);
97 | this.$input = $(' ')
98 | .appendTo(this.$wrapper)
99 | .prop('placeholder', this.$element.prop('placeholder'))
100 | .prop('id', id + '-tokenfield')
101 | .prop('tabindex', this.$element.data('original-tabindex'));
102 |
103 | // Re-route original input label to new input
104 | var $label = $('label[for="' + this.$element.prop('id') + '"]');
105 | if ($label.length) {
106 | $label.prop('for', this.$input.prop('id'));
107 | }
108 |
109 | // Set up a copy helper to handle copy & paste
110 | this.$copyHelper = $(' ').css('position', 'absolute').css(hidingPosition, '-10000px').prop('tabindex', -1).prependTo(this.$wrapper);
111 |
112 | // Set wrapper width
113 | if (elStyleWidth) {
114 | this.$wrapper.css('width', elStyleWidth);
115 | } else if (this.$element.parents('.form-inline').length) {
116 | // If input is inside inline-form with no width set, set fixed width
117 | this.$wrapper.width(elWidth);
118 | }
119 |
120 | // Set tokenfield disabled, if original or fieldset input is disabled
121 | if (this.$element.prop('disabled') || this.$element.parents('fieldset[disabled]').length) {
122 | this.disable();
123 | }
124 |
125 | // Set tokenfield readonly, if original input is readonly
126 | if (this.$element.prop('readonly')) {
127 | this.readonly();
128 | }
129 |
130 | // Set up mirror for input auto-sizing
131 | this.$mirror = $(' ');
132 | this.$input.css('min-width', this.options.minWidth + 'px');
133 |
134 | // Insert tokenfield to HTML
135 | this.$wrapper.insertBefore(this.$element);
136 | this.$element.prependTo(this.$wrapper);
137 |
138 | // Append mirror to tokenfield wrapper
139 | this.$mirror.appendTo(this.$wrapper);
140 |
141 | // Calculate inner input width
142 | this.update();
143 |
144 | // Create initial tokens, if any
145 | this.setTokens(this.options.tokens, false, ! this.$element.val() && this.options.tokens);
146 |
147 | // Start listening to events
148 | this.listen();
149 |
150 | // Initialize autocomplete, if necessary
151 | if (!$.isEmptyObject(this.options.autocomplete)) {
152 | var side = this.textDirection === 'rtl' ? 'right' : 'left',
153 | autocompleteOptions = $.extend({
154 | minLength: this.options.showAutocompleteOnFocus ? 0 : null,
155 | position: { my: side + ' top', at: side + ' bottom', of: this.$wrapper }
156 | }, this.options.autocomplete);
157 |
158 | this.$input.autocomplete(autocompleteOptions);
159 | }
160 |
161 | // Initialize typeahead, if necessary
162 | if (!$.isEmptyObject(this.options.typeahead)) {
163 |
164 | var typeaheadOptions = this.options.typeahead,
165 | defaults = {
166 | minLength: this.options.showAutocompleteOnFocus ? 0 : null
167 | },
168 | args = $.isArray(typeaheadOptions) ? typeaheadOptions : [typeaheadOptions, typeaheadOptions];
169 |
170 | args[0] = $.extend({}, defaults, args[0]);
171 |
172 | this.$input.typeahead.apply(this.$input, args);
173 | this.typeahead = true;
174 | }
175 | };
176 |
177 | Tokenfield.prototype = {
178 |
179 | constructor: Tokenfield,
180 |
181 | createToken: function (attrs, triggerChange) {
182 | var _self = this;
183 |
184 | if (typeof attrs === 'string') {
185 | attrs = { value: attrs, label: attrs };
186 | } else {
187 | // Copy objects to prevent contamination of data sources.
188 | attrs = $.extend({}, attrs);
189 | }
190 |
191 | if (typeof triggerChange === 'undefined') {
192 | triggerChange = true;
193 | }
194 |
195 | // Normalize label and value
196 | attrs.value = $.trim(attrs.value.toString());
197 | attrs.label = attrs.label && attrs.label.length ? $.trim(attrs.label) : attrs.value;
198 |
199 | // Bail out if has no value or label, or label is too short
200 | if (!attrs.value.length || !attrs.label.length || attrs.label.length < this.options.minLength) return;
201 |
202 | // Bail out if maximum number of tokens is reached
203 | if (this.options.limit && this.getTokens().length >= this.options.limit) return;
204 |
205 | // Allow changing token data before creating it
206 | var createEvent = $.Event('tokenfield:createtoken', { attrs: attrs });
207 | this.$element.trigger(createEvent);
208 |
209 | // Bail out if there if attributes are empty or event was defaultPrevented
210 | if (!createEvent.attrs || createEvent.isDefaultPrevented()) return;
211 |
212 | var $token = $('
')
213 | .append(' ')
214 | .append('× ')
215 | .data('attrs', attrs);
216 |
217 | // Insert token into HTML
218 | if (this.$input.hasClass('tt-input')) {
219 | // If the input has typeahead enabled, insert token before it's parent
220 | this.$input.parent().before($token);
221 | } else {
222 | this.$input.before($token);
223 | }
224 |
225 | // Temporarily set input width to minimum
226 | this.$input.css('width', this.options.minWidth + 'px');
227 |
228 | var $tokenLabel = $token.find('.token-label'),
229 | $closeButton = $token.find('.close');
230 |
231 | // Todo: Determine maximum possible token label width
232 | // Previous method does not work in FF
233 | // Refactor this!
234 | if (!this.maxTokenWidth) {
235 | this.maxTokenWidth = this.$wrapper.width() - $closeButton.width() - 10;
236 | }
237 |
238 | $tokenLabel.css('max-width', this.maxTokenWidth);
239 |
240 | if (this.options.html) {
241 | $tokenLabel.html(attrs.label);
242 | } else {
243 | $tokenLabel.text(attrs.label);
244 | }
245 |
246 | // Listen to events on token
247 | $token
248 | .on('mousedown', function (e) {
249 | if (_self._disabled || _self._readonly) return false;
250 | _self.preventDeactivation = true;
251 | })
252 | .on('click', function (e) {
253 | if (_self._disabled || _self._readonly) return false;
254 | _self.preventDeactivation = false;
255 |
256 | if (e.ctrlKey || e.metaKey) {
257 | e.preventDefault();
258 | return _self.toggle($token);
259 | }
260 |
261 | _self.activate($token, e.shiftKey, e.shiftKey);
262 | })
263 | .on('dblclick', function (e) {
264 | if (_self._disabled || _self._readonly || !_self.options.allowEditing) return false;
265 | _self.edit($token);
266 | });
267 |
268 | $closeButton
269 | .on('click', $.proxy(this.remove, this));
270 |
271 | // Trigger createdtoken event on the original field
272 | // indicating that the token is now in the DOM
273 | this.$element.trigger($.Event('tokenfield:createdtoken', {
274 | attrs: attrs,
275 | relatedTarget: $token.get(0)
276 | }));
277 |
278 | // Trigger change event on the original field
279 | if (triggerChange) {
280 | this.$element.val(this.getTokensList()).trigger($.Event('change', { initiator: 'tokenfield' }));
281 | }
282 |
283 | // Update tokenfield dimensions
284 | setTimeout(function () {
285 | _self.update();
286 | }, 0);
287 |
288 | // Return original element
289 | return this.$element.get(0);
290 | },
291 |
292 | setTokens: function (tokens, add, triggerChange) {
293 | if (!add) this.$wrapper.find('.token').remove();
294 |
295 | if (!tokens) return;
296 |
297 | if (typeof triggerChange === 'undefined') {
298 | triggerChange = true;
299 | }
300 |
301 | if (typeof tokens === 'string') {
302 | if (this._delimiters.length) {
303 | // Split based on delimiters
304 | tokens = tokens.split(new RegExp('[' + this._delimiters.join('') + ']'));
305 | } else {
306 | tokens = [tokens];
307 | }
308 | }
309 |
310 | var _self = this;
311 | $.each(tokens, function (i, attrs) {
312 | _self.createToken(attrs, triggerChange);
313 | });
314 |
315 | return this.$element.get(0);
316 | },
317 |
318 | getTokenData: function ($token) {
319 | var data = $token.map(function () {
320 | var $token = $(this);
321 | return $token.data('attrs');
322 | }).get();
323 |
324 | if (data.length === 1) {
325 | data = data[0];
326 | }
327 |
328 | return data;
329 | },
330 |
331 | getTokens: function (active) {
332 | var self = this,
333 | tokens = [],
334 | activeClass = active ? '.active' : ''; // get active tokens only
335 |
336 | this.$wrapper.find('.token' + activeClass).each(function () {
337 | tokens.push(self.getTokenData($(this)));
338 | });
339 | return tokens;
340 | },
341 |
342 | getTokensList: function (delimiter, beautify, active) {
343 | delimiter = delimiter || this._firstDelimiter;
344 | beautify = (typeof beautify !== 'undefined' && beautify !== null) ? beautify : this.options.beautify;
345 |
346 | var separator = delimiter + (beautify && delimiter !== ' ' ? ' ' : '');
347 | return $.map(this.getTokens(active), function (token) {
348 | return token.value;
349 | }).join(separator);
350 | },
351 |
352 | getInput: function () {
353 | return this.$input.val();
354 | },
355 |
356 | setInput: function (val) {
357 | if (this.$input.hasClass('tt-input')) {
358 | // Typeahead acts weird when simply setting input value to empty,
359 | // so we set the query to empty instead
360 | this.$input.typeahead('val', val);
361 | } else {
362 | this.$input.val(val);
363 | }
364 | },
365 |
366 | listen: function () {
367 | var _self = this;
368 | this.$element
369 | .on('change.tokenfield', $.proxy(this.change, this));
370 |
371 | this.$wrapper
372 | .on('mousedown', $.proxy(this.focusInput, this));
373 |
374 | this.$input
375 | .on('focus', $.proxy(this.focus, this))
376 | .on('blur', $.proxy(this.blur, this))
377 | .on('paste', $.proxy(this.paste, this))
378 | .on('keydown', $.proxy(this.keydown, this))
379 | .on('keypress', $.proxy(this.keypress, this))
380 | .on('keyup', $.proxy(this.keyup, this));
381 |
382 | this.$copyHelper
383 | .on('focus', $.proxy(this.focus, this))
384 | .on('blur', $.proxy(this.blur, this))
385 | .on('keydown', $.proxy(this.keydown, this))
386 | .on('keyup', $.proxy(this.keyup, this));
387 |
388 | // Secondary listeners for input width calculation
389 | this.$input
390 | .on('keypress', $.proxy(this.update, this))
391 | .on('keyup', $.proxy(this.update, this));
392 |
393 | this.$input
394 | .on('autocompletecreate', function () {
395 | // Set minimum autocomplete menu width
396 | var $_menuElement = $(this).data('ui-autocomplete').menu.element,
397 | minWidth = _self.$wrapper.outerWidth() -
398 | parseInt($_menuElement.css('border-left-width'), 10) -
399 | parseInt($_menuElement.css('border-right-width'), 10);
400 |
401 | $_menuElement.css('min-width', minWidth + 'px');
402 | })
403 | .on('autocompleteselect', function (e, ui) {
404 | if (_self.createToken(ui.item)) {
405 | _self.$input.val('');
406 | if (_self.$input.data('edit')) {
407 | _self.unedit(true);
408 | }
409 | }
410 | return false;
411 | })
412 | .on('typeahead:selected typeahead:autocompleted', function (e, datum, dataset) {
413 | // Create token
414 | if (_self.createToken(datum)) {
415 | _self.$input.typeahead('val', '');
416 | if (_self.$input.data('edit')) {
417 | _self.unedit(true);
418 | }
419 | }
420 | });
421 |
422 | // Listen to window resize
423 | $(window).on('resize', $.proxy(this.update, this));
424 | },
425 |
426 | keydown: function (e) {
427 |
428 | if (!this.focused) return;
429 |
430 | var _self = this;
431 |
432 | switch (e.keyCode) {
433 | case 8: // backspace
434 | if (!this.$input.is(document.activeElement)) break;
435 | this.lastInputValue = this.$input.val();
436 | break;
437 |
438 | case 37: // left arrow
439 | leftRight(this.textDirection === 'rtl' ? 'next' : 'prev');
440 | break;
441 |
442 | case 38: // up arrow
443 | upDown('prev');
444 | break;
445 |
446 | case 39: // right arrow
447 | leftRight(this.textDirection === 'rtl' ? 'prev' : 'next');
448 | break;
449 |
450 | case 40: // down arrow
451 | upDown('next');
452 | break;
453 |
454 | case 65: // a (to handle ctrl + a)
455 | if (this.$input.val().length > 0 || !(e.ctrlKey || e.metaKey)) break;
456 | this.activateAll();
457 | e.preventDefault();
458 | break;
459 |
460 | case 9: // tab
461 | case 13: // enter
462 |
463 | // We will handle creating tokens from autocomplete in autocomplete events
464 | if (this.$input.data('ui-autocomplete') && this.$input.data('ui-autocomplete').menu.element.find('li:has(a.ui-state-focus), li.ui-state-focus').length) break;
465 |
466 | // We will handle creating tokens from typeahead in typeahead events
467 | if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-cursor').length) break;
468 | if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-hint').val() && this.$wrapper.find('.tt-hint').val().length) break;
469 |
470 | // Create token
471 | if (this.$input.is(document.activeElement) && this.$input.val().length || this.$input.data('edit')) {
472 | return this.createTokensFromInput(e, this.$input.data('edit'));
473 | } else if (this.$input.is(document.activeElement) && (e.keyCode === 13)) {
474 | e.preventDefault();
475 | this.$element.trigger('tokenfield:next');
476 | }
477 |
478 | // Edit token
479 | if (e.keyCode === 13) {
480 | if (!this.$copyHelper.is(document.activeElement) || this.$wrapper.find('.token.active').length !== 1) break;
481 | if (!_self.options.allowEditing) break;
482 | this.edit(this.$wrapper.find('.token.active'));
483 | }
484 |
485 | // no default
486 | }
487 |
488 | function leftRight(direction) {
489 | if (_self.$input.is(document.activeElement)) {
490 | if (_self.$input.val().length > 0) return;
491 |
492 | direction += 'All';
493 | var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction]('.token:first') : _self.$input[direction]('.token:first');
494 | if (!$token.length) return;
495 |
496 | _self.preventInputFocus = true;
497 | _self.preventDeactivation = true;
498 |
499 | _self.activate($token);
500 | e.preventDefault();
501 |
502 | } else {
503 | _self[direction](e.shiftKey);
504 | e.preventDefault();
505 | }
506 | }
507 |
508 | function upDown(direction) {
509 | if (!e.shiftKey) return;
510 |
511 | if (_self.$input.is(document.activeElement)) {
512 | if (_self.$input.val().length > 0) return;
513 |
514 | var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction + 'All']('.token:first') : _self.$input[direction + 'All']('.token:first');
515 | if (!$token.length) return;
516 |
517 | _self.activate($token);
518 | }
519 |
520 | var opposite = direction === 'prev' ? 'next' : 'prev',
521 | position = direction === 'prev' ? 'first' : 'last';
522 |
523 | _self.$firstActiveToken[opposite + 'All']('.token').each(function () {
524 | _self.deactivate($(this));
525 | });
526 |
527 | _self.activate(_self.$wrapper.find('.token:' + position), true, true);
528 | e.preventDefault();
529 | }
530 |
531 | this.lastKeyDown = e.keyCode;
532 | },
533 |
534 | keypress: function (e) {
535 |
536 | // Comma
537 | if ($.inArray(e.which, this._triggerKeys) !== -1 && this.$input.is(document.activeElement)) {
538 | var val = this.$input.val(),
539 | quoting = /^"[^"]*$/.test(val);
540 | if (quoting) return;
541 | if (val) this.createTokensFromInput(e);
542 | return false;
543 | }
544 | },
545 |
546 | keyup: function (e) {
547 | this.preventInputFocus = false;
548 |
549 | if (!this.focused) return;
550 |
551 | switch (e.keyCode) {
552 | case 8: // backspace
553 | if (this.$input.is(document.activeElement)) {
554 | if (this.$input.val().length || this.lastInputValue.length && this.lastKeyDown === 8) break;
555 |
556 | this.preventDeactivation = true;
557 | var $prevToken = this.$input.hasClass('tt-input') ? this.$input.parent().prevAll('.token:first') : this.$input.prevAll('.token:first');
558 |
559 | if (!$prevToken.length) break;
560 |
561 | this.activate($prevToken);
562 | } else {
563 | this.remove(e);
564 | }
565 | break;
566 |
567 | case 46: // delete
568 | this.remove(e, 'next');
569 | break;
570 |
571 | // no default
572 | }
573 | this.lastKeyUp = e.keyCode;
574 | },
575 |
576 | focus: function (e) {
577 | this.focused = true;
578 | this.$wrapper.addClass('focus');
579 |
580 | if (this.$input.is(document.activeElement)) {
581 | this.$wrapper.find('.active').removeClass('active');
582 | this.$firstActiveToken = null;
583 |
584 | if (this.options.showAutocompleteOnFocus) {
585 | this.search();
586 | }
587 | }
588 | },
589 |
590 | blur: function (e) {
591 | this.focused = false;
592 | this.$wrapper.removeClass('focus');
593 |
594 | if (!this.preventDeactivation && !this.$element.is(document.activeElement)) {
595 | this.$wrapper.find('.active').removeClass('active');
596 | this.$firstActiveToken = null;
597 | }
598 |
599 | if (!this.preventCreateTokens && (this.$input.data('edit') && !this.$input.is(document.activeElement) || this.options.createTokensOnBlur)) {
600 | this.createTokensFromInput(e);
601 | }
602 |
603 | this.preventDeactivation = false;
604 | this.preventCreateTokens = false;
605 | },
606 |
607 | paste: function (e) {
608 | var _self = this;
609 |
610 | // Add tokens to existing ones
611 | if (_self.options.allowPasting) {
612 | setTimeout(function () {
613 | _self.createTokensFromInput(e);
614 | }, 1);
615 | }
616 | },
617 |
618 | change: function (e) {
619 | if (e.initiator === 'tokenfield') return; // Prevent loops
620 | this.setTokens(this.$element.val());
621 | },
622 |
623 | createTokensFromInput: function (e, focus) {
624 | if (this.$input.val().length < this.options.minLength) return; // No input, simply return
625 |
626 | var tokensBefore = this.getTokensList();
627 | this.setTokens(this.$input.val(), true);
628 |
629 | if (tokensBefore === this.getTokensList() && this.$input.val().length) return false; // No tokens were added, do nothing (prevent form submit)
630 |
631 | this.setInput('');
632 |
633 | if (this.$input.data('edit')) this.unedit(focus);
634 |
635 | return false; // Prevent form being submitted
636 | },
637 |
638 | next: function (add) {
639 | if (add) {
640 | var $firstActiveToken = this.$wrapper.find('.active:first'),
641 | deactivate = $firstActiveToken && this.$firstActiveToken ? $firstActiveToken.index() < this.$firstActiveToken.index() : false;
642 |
643 | if (deactivate) return this.deactivate($firstActiveToken);
644 | }
645 |
646 | var $lastActiveToken = this.$wrapper.find('.active:last'),
647 | $nextToken = $lastActiveToken.nextAll('.token:first');
648 |
649 | if (!$nextToken.length) {
650 | this.$input.focus();
651 | return;
652 | }
653 |
654 | this.activate($nextToken, add);
655 | },
656 |
657 | prev: function (add) {
658 |
659 | if (add) {
660 | var $lastActiveToken = this.$wrapper.find('.active:last'),
661 | deactivate = $lastActiveToken && this.$firstActiveToken ? $lastActiveToken.index() > this.$firstActiveToken.index() : false;
662 |
663 | if (deactivate) return this.deactivate($lastActiveToken);
664 | }
665 |
666 | var $firstActiveToken = this.$wrapper.find('.active:first'),
667 | $prevToken = $firstActiveToken.prevAll('.token:first');
668 |
669 | if (!$prevToken.length) {
670 | $prevToken = this.$wrapper.find('.token:first');
671 | }
672 |
673 | if (!$prevToken.length && !add) {
674 | this.$input.focus();
675 | return;
676 | }
677 |
678 | this.activate($prevToken, add);
679 | },
680 |
681 | activate: function ($token, add, multi, remember) {
682 |
683 | if (!$token) return;
684 |
685 | if (typeof remember === 'undefined') remember = true;
686 |
687 | if (multi) add = true;
688 |
689 | this.$copyHelper.focus();
690 |
691 | if (!add) {
692 | this.$wrapper.find('.active').removeClass('active');
693 | if (remember) {
694 | this.$firstActiveToken = $token;
695 | } else {
696 | delete this.$firstActiveToken;
697 | }
698 | }
699 |
700 | if (multi && this.$firstActiveToken) {
701 | // Determine first active token and the current tokens indicies
702 | // Account for the 1 hidden textarea by subtracting 1 from both
703 | var i = this.$firstActiveToken.index() - 2,
704 | a = $token.index() - 2,
705 | _self = this;
706 |
707 | this.$wrapper.find('.token').slice(Math.min(i, a) + 1, Math.max(i, a)).each(function () {
708 | _self.activate($(this), true);
709 | });
710 | }
711 |
712 | $token.addClass('active');
713 | this.$copyHelper.val(this.getTokensList(null, null, true)).select();
714 | },
715 |
716 | activateAll: function () {
717 | var _self = this;
718 |
719 | this.$wrapper.find('.token').each(function (i) {
720 | _self.activate($(this), i !== 0, false, false);
721 | });
722 | },
723 |
724 | deactivate: function ($token) {
725 | if (!$token) return;
726 |
727 | $token.removeClass('active');
728 | this.$copyHelper.val(this.getTokensList(null, null, true)).select();
729 | },
730 |
731 | toggle: function ($token) {
732 | if (!$token) return;
733 |
734 | $token.toggleClass('active');
735 | this.$copyHelper.val(this.getTokensList(null, null, true)).select();
736 | },
737 |
738 | edit: function ($token) {
739 | if (!$token) return;
740 |
741 | var attrs = $token.data('attrs');
742 |
743 | // Allow changing input value before editing
744 | var options = { attrs: attrs, relatedTarget: $token.get(0) };
745 | var editEvent = $.Event('tokenfield:edittoken', options);
746 | this.$element.trigger(editEvent);
747 |
748 | // Edit event can be cancelled if default is prevented
749 | if (editEvent.isDefaultPrevented()) return;
750 |
751 | $token.find('.token-label').text(attrs.value);
752 |
753 | var tokenWidth = $token.outerWidth(),
754 | $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input;
755 |
756 | $token.replaceWith($_input);
757 |
758 | this.preventCreateTokens = true;
759 |
760 | this.$input.val(attrs.value)
761 | .select()
762 | .data('edit', true)
763 | .width(tokenWidth);
764 |
765 | this.update();
766 |
767 | // Indicate that token is now being edited, and is replaced with an input field in the DOM
768 | this.$element.trigger($.Event('tokenfield:editedtoken', options));
769 | },
770 |
771 | unedit: function (focus) {
772 | var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input;
773 | $_input.appendTo(this.$wrapper);
774 |
775 | this.$input.data('edit', false);
776 | this.$mirror.text('');
777 |
778 | this.update();
779 |
780 | // Because moving the input element around in DOM
781 | // will cause it to lose focus, we provide an option
782 | // to re-focus the input after appending it to the wrapper
783 | if (focus) {
784 | var _self = this;
785 | setTimeout(function () {
786 | _self.$input.focus();
787 | }, 1);
788 | }
789 | },
790 |
791 | remove: function (e, direction) {
792 | if (this.$input.is(document.activeElement) || this._disabled || this._readonly) return;
793 | var firstToken,
794 | $token = (e.type === 'click') ? $(e.target).closest('.token') : this.$wrapper.find('.token.active');
795 |
796 | if (e.type !== 'click') {
797 | if (!direction) direction = 'prev';
798 | this[direction]();
799 |
800 | // Was it the first token?
801 | if (direction === 'prev') firstToken = $token.first().prevAll('.token:first').length === 0;
802 | }
803 |
804 | // Prepare events and their options
805 | var options = { attrs: this.getTokenData($token), relatedTarget: $token.get(0) },
806 | removeEvent = $.Event('tokenfield:removetoken', options);
807 |
808 | this.$element.trigger(removeEvent);
809 |
810 | // Remove event can be intercepted and cancelled
811 | if (removeEvent.isDefaultPrevented()) return;
812 |
813 | var removedEvent = $.Event('tokenfield:removedtoken', options),
814 | changeEvent = $.Event('change', { initiator: 'tokenfield' });
815 |
816 | // Remove token from DOM
817 | $token.remove();
818 |
819 | // Trigger events
820 | this.$element.val(this.getTokensList()).trigger(removedEvent).trigger(changeEvent);
821 |
822 | // Focus, when necessary:
823 | // When there are no more tokens, or if this was the first token
824 | // and it was removed with backspace or it was clicked on
825 | if (!this.$wrapper.find('.token').length || e.type === 'click' || firstToken) this.$input.focus();
826 |
827 | // Adjust input width
828 | this.$input.css('width', this.options.minWidth + 'px');
829 | this.update();
830 |
831 | // Cancel original event handlers
832 | e.preventDefault();
833 | e.stopPropagation();
834 | },
835 |
836 | /**
837 | * Update tokenfield dimensions
838 | */
839 | update: function (e) {
840 | var value = this.$input.val(),
841 | inputPaddingLeft = parseInt(this.$input.css('padding-left'), 10),
842 | inputPaddingRight = parseInt(this.$input.css('padding-right'), 10),
843 | inputPadding = inputPaddingLeft + inputPaddingRight;
844 |
845 | if (this.$input.data('edit')) {
846 |
847 | if (!value) {
848 | value = this.$input.prop('placeholder');
849 | }
850 | if (value === this.$mirror.text()) return;
851 |
852 | this.$mirror.text(value);
853 |
854 | var mirrorWidth = this.$mirror.width() + 10;
855 | if (mirrorWidth > this.$wrapper.width()) {
856 | return this.$input.width(this.$wrapper.width());
857 | }
858 |
859 | this.$input.width(mirrorWidth);
860 | } else {
861 | // temporary reset width to minimal value to get proper results
862 | this.$input.width(this.options.minWidth);
863 |
864 | var w = (this.textDirection === 'rtl')
865 | ? this.$input.offset().left + this.$input.outerWidth() - this.$wrapper.offset().left - parseInt(this.$wrapper.css('padding-left'), 10) - inputPadding - 1
866 | : this.$wrapper.offset().left + this.$wrapper.width() + parseInt(this.$wrapper.css('padding-left'), 10) - this.$input.offset().left - inputPadding;
867 | //
868 | // some usecases pre-render widget before attaching to DOM,
869 | // dimensions returned by jquery will be NaN -> we default to 100%
870 | // so placeholder won't be cut off.
871 | if (isNaN(w)) {
872 | this.$input.width('100%');
873 | } else {
874 | this.$input.width(w);
875 | }
876 | }
877 | },
878 | focusInput: function (e) {
879 | if ($(e.target).closest('.token').length || $(e.target).closest('.token-input').length || $(e.target).closest('.tt-dropdown-menu').length) return;
880 | // Focus only after the current call stack has cleared,
881 | // otherwise has no effect.
882 | // Reason: mousedown is too early - input will lose focus
883 | // after mousedown. However, since the input may be moved
884 | // in DOM, there may be no click or mouseup event triggered.
885 | var _self = this;
886 | setTimeout(function () {
887 | _self.$input.focus();
888 | }, 0);
889 | },
890 | search: function () {
891 | if (this.$input.data('ui-autocomplete')) {
892 | this.$input.autocomplete('search');
893 | }
894 | },
895 | disable: function () {
896 | this.setProperty('disabled', true);
897 | },
898 | enable: function () {
899 | this.setProperty('disabled', false);
900 | },
901 |
902 | readonly: function () {
903 | this.setProperty('readonly', true);
904 | },
905 |
906 | writeable: function () {
907 | this.setProperty('readonly', false);
908 | },
909 |
910 | setProperty: function (property, value) {
911 | this['_' + property] = value;
912 | this.$input.prop(property, value);
913 | this.$element.prop(property, value);
914 | this.$wrapper[ value ? 'addClass' : 'removeClass' ](property);
915 | },
916 |
917 | destroy: function () {
918 | // Set field value
919 | this.$element.val(this.getTokensList());
920 | // Restore styles and properties
921 | this.$element.css(this.$element.data('original-styles'));
922 | this.$element.prop('tabindex', this.$element.data('original-tabindex'));
923 |
924 | // Re-route tokenfield label to original input
925 | var $label = $('label[for="' + this.$input.prop('id') + '"]');
926 |
927 | if ($label.length) $label.prop('for', this.$element.prop('id'));
928 |
929 | // Move original element outside of tokenfield wrapper
930 | this.$element.insertBefore(this.$wrapper);
931 |
932 | // Remove tokenfield-related events
933 | this.$element.off('.tokenfield');
934 |
935 | // Remove tokenfield-related data
936 | this.$element.removeData('original-styles')
937 | .removeData('original-tabindex')
938 | .removeData('bs.tokenfield');
939 |
940 | // Remove tokenfield from DOM
941 | this.$wrapper.remove();
942 | this.$mirror.remove();
943 |
944 | var $_element = this.$element;
945 |
946 | return $_element;
947 | }
948 |
949 | };
950 |
951 |
952 | /* TOKENFIELD PLUGIN DEFINITION
953 | * ======================== */
954 |
955 | var old = $.fn.tokenfield;
956 |
957 | $.fn.tokenfield = function (option, param) {
958 | var value,
959 | args = [];
960 |
961 | Array.prototype.push.apply(args, arguments);
962 |
963 | var elements = this.each(function () {
964 | var $this = $(this),
965 | data = $this.data('bs.tokenfield'),
966 | options = typeof option === 'object' && option;
967 |
968 | if (typeof option === 'string' && data && data[option]) {
969 | args.shift();
970 | value = data[option].apply(data, args);
971 | } else if (!data && typeof option !== 'string' && !param) {
972 | $this.data('bs.tokenfield', (data = new Tokenfield(this, options)));
973 | $this.trigger('tokenfield:initialize');
974 | }
975 | });
976 |
977 | return typeof value !== 'undefined' ? value : elements;
978 | };
979 |
980 | $.fn.tokenfield.defaults = {
981 | minWidth: 60,
982 | minLength: 0,
983 | html: true,
984 | allowEditing: true,
985 | allowPasting: true,
986 | limit: 0,
987 | autocomplete: {},
988 | typeahead: {},
989 | showAutocompleteOnFocus: false,
990 | createTokensOnBlur: false,
991 | delimiter: ',',
992 | beautify: true,
993 | inputType: 'text'
994 | };
995 |
996 | $.fn.tokenfield.Constructor = Tokenfield;
997 |
998 |
999 | /* TOKENFIELD NO CONFLICT
1000 | * ================== */
1001 |
1002 | $.fn.tokenfield.noConflict = function () {
1003 | $.fn.tokenfield = old;
1004 | return this;
1005 | };
1006 |
1007 | return Tokenfield;
1008 |
1009 | }));
1010 |
--------------------------------------------------------------------------------
/dist/bootstrap-tokenfield.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * bootstrap-tokenfield 0.12.1
3 | * https://github.com/sliptree/bootstrap-tokenfield
4 | * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
5 | */
6 |
7 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=global.window&&global.window.$?a(global.window.$):function(b){if(!b.$&&!b.fn)throw new Error("Tokenfield requires a window object with jQuery or a jQuery instance");return a(b.$||b)}:a(jQuery,window)}(function(a,b){"use strict";var c=function(b,c){var d=this;this.$element=a(b),this.textDirection=this.$element.css("direction"),this.options=a.extend(!0,{},a.fn.tokenfield.defaults,{tokens:this.$element.val()},this.$element.data(),c),this._delimiters="string"==typeof this.options.delimiter?[this.options.delimiter]:this.options.delimiter,this._triggerKeys=a.map(this._delimiters,function(a){return a.charCodeAt(0)}),this._firstDelimiter=this._delimiters[0];var e=a.inArray(" ",this._delimiters),f=a.inArray("-",this._delimiters);e>=0&&(this._delimiters[e]="\\s"),f>=0&&(delete this._delimiters[f],this._delimiters.unshift("-"));var g=["\\","$","[","{","^",".","|","?","*","+","(",")"];a.each(this._delimiters,function(b,c){var e=a.inArray(c,g);e>=0&&(d._delimiters[b]="\\"+c)});var h=b.style.width,i=this.$element.width(),j="rtl"===a("body").css("direction")?"right":"left",k={position:this.$element.css("position")};k[j]=this.$element.css(j),this.$element.data("original-styles",k).data("original-tabindex",this.$element.prop("tabindex")).css("position","absolute").css(j,"-10000px").prop("tabindex",-1),this.$wrapper=a('
'),this.$element.hasClass("input-lg")&&this.$wrapper.addClass("input-lg"),this.$element.hasClass("input-sm")&&this.$wrapper.addClass("input-sm"),"rtl"===this.textDirection&&this.$wrapper.addClass("rtl");var l=this.$element.prop("id")||(new Date).getTime()+""+Math.floor(100*(1+Math.random()));this.$input=a(' ').appendTo(this.$wrapper).prop("placeholder",this.$element.prop("placeholder")).prop("id",l+"-tokenfield").prop("tabindex",this.$element.data("original-tabindex"));var m=a('label[for="'+this.$element.prop("id")+'"]');if(m.length&&m.prop("for",this.$input.prop("id")),this.$copyHelper=a(' ').css("position","absolute").css(j,"-10000px").prop("tabindex",-1).prependTo(this.$wrapper),h?this.$wrapper.css("width",h):this.$element.parents(".form-inline").length&&this.$wrapper.width(i),(this.$element.prop("disabled")||this.$element.parents("fieldset[disabled]").length)&&this.disable(),this.$element.prop("readonly")&&this.readonly(),this.$mirror=a(' '),this.$input.css("min-width",this.options.minWidth+"px"),this.$wrapper.insertBefore(this.$element),this.$element.prependTo(this.$wrapper),this.$mirror.appendTo(this.$wrapper),this.update(),this.setTokens(this.options.tokens,!1,!this.$element.val()&&this.options.tokens),this.listen(),!a.isEmptyObject(this.options.autocomplete)){var n="rtl"===this.textDirection?"right":"left",o=a.extend({minLength:this.options.showAutocompleteOnFocus?0:null,position:{my:n+" top",at:n+" bottom",of:this.$wrapper}},this.options.autocomplete);this.$input.autocomplete(o)}if(!a.isEmptyObject(this.options.typeahead)){var p=this.options.typeahead,q={minLength:this.options.showAutocompleteOnFocus?0:null},r=a.isArray(p)?p:[p,p];r[0]=a.extend({},q,r[0]),this.$input.typeahead.apply(this.$input,r),this.typeahead=!0}};c.prototype={constructor:c,createToken:function(b,c){var d=this;if(b="string"==typeof b?{value:b,label:b}:a.extend({},b),"undefined"==typeof c&&(c=!0),b.value=a.trim(b.value.toString()),b.label=b.label&&b.label.length?a.trim(b.label):b.value,b.value.length&&b.label.length&&!(b.label.length=this.options.limit)){var e=a.Event("tokenfield:createtoken",{attrs:b});if(this.$element.trigger(e),e.attrs&&!e.isDefaultPrevented()){var f=a('
').append(' ').append('× ').data("attrs",b);this.$input.hasClass("tt-input")?this.$input.parent().before(f):this.$input.before(f),this.$input.css("width",this.options.minWidth+"px");var g=f.find(".token-label"),h=f.find(".close");return this.maxTokenWidth||(this.maxTokenWidth=this.$wrapper.width()-h.width()-10),g.css("max-width",this.maxTokenWidth),this.options.html?g.html(b.label):g.text(b.label),f.on("mousedown",function(a){return d._disabled||d._readonly?!1:void(d.preventDeactivation=!0)}).on("click",function(a){return d._disabled||d._readonly?!1:(d.preventDeactivation=!1,a.ctrlKey||a.metaKey?(a.preventDefault(),d.toggle(f)):void d.activate(f,a.shiftKey,a.shiftKey))}).on("dblclick",function(a){return d._disabled||d._readonly||!d.options.allowEditing?!1:void d.edit(f)}),h.on("click",a.proxy(this.remove,this)),this.$element.trigger(a.Event("tokenfield:createdtoken",{attrs:b,relatedTarget:f.get(0)})),c&&this.$element.val(this.getTokensList()).trigger(a.Event("change",{initiator:"tokenfield"})),setTimeout(function(){d.update()},0),this.$element.get(0)}}},setTokens:function(b,c,d){if(c||this.$wrapper.find(".token").remove(),b){"undefined"==typeof d&&(d=!0),"string"==typeof b&&(b=this._delimiters.length?b.split(new RegExp("["+this._delimiters.join("")+"]")):[b]);var e=this;return a.each(b,function(a,b){e.createToken(b,d)}),this.$element.get(0)}},getTokenData:function(b){var c=b.map(function(){var b=a(this);return b.data("attrs")}).get();return 1===c.length&&(c=c[0]),c},getTokens:function(b){var c=this,d=[],e=b?".active":"";return this.$wrapper.find(".token"+e).each(function(){d.push(c.getTokenData(a(this)))}),d},getTokensList:function(b,c,d){b=b||this._firstDelimiter,c="undefined"!=typeof c&&null!==c?c:this.options.beautify;var e=b+(c&&" "!==b?" ":"");return a.map(this.getTokens(d),function(a){return a.value}).join(e)},getInput:function(){return this.$input.val()},setInput:function(a){this.$input.hasClass("tt-input")?this.$input.typeahead("val",a):this.$input.val(a)},listen:function(){var c=this;this.$element.on("change.tokenfield",a.proxy(this.change,this)),this.$wrapper.on("mousedown",a.proxy(this.focusInput,this)),this.$input.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("paste",a.proxy(this.paste,this)).on("keydown",a.proxy(this.keydown,this)).on("keypress",a.proxy(this.keypress,this)).on("keyup",a.proxy(this.keyup,this)),this.$copyHelper.on("focus",a.proxy(this.focus,this)).on("blur",a.proxy(this.blur,this)).on("keydown",a.proxy(this.keydown,this)).on("keyup",a.proxy(this.keyup,this)),this.$input.on("keypress",a.proxy(this.update,this)).on("keyup",a.proxy(this.update,this)),this.$input.on("autocompletecreate",function(){var b=a(this).data("ui-autocomplete").menu.element,d=c.$wrapper.outerWidth()-parseInt(b.css("border-left-width"),10)-parseInt(b.css("border-right-width"),10);b.css("min-width",d+"px")}).on("autocompleteselect",function(a,b){return c.createToken(b.item)&&(c.$input.val(""),c.$input.data("edit")&&c.unedit(!0)),!1}).on("typeahead:selected typeahead:autocompleted",function(a,b,d){c.createToken(b)&&(c.$input.typeahead("val",""),c.$input.data("edit")&&c.unedit(!0))}),a(b).on("resize",a.proxy(this.update,this))},keydown:function(b){function c(a){if(e.$input.is(document.activeElement)){if(e.$input.val().length>0)return;a+="All";var c=e.$input.hasClass("tt-input")?e.$input.parent()[a](".token:first"):e.$input[a](".token:first");if(!c.length)return;e.preventInputFocus=!0,e.preventDeactivation=!0,e.activate(c),b.preventDefault()}else e[a](b.shiftKey),b.preventDefault()}function d(c){if(b.shiftKey){if(e.$input.is(document.activeElement)){if(e.$input.val().length>0)return;var d=e.$input.hasClass("tt-input")?e.$input.parent()[c+"All"](".token:first"):e.$input[c+"All"](".token:first");if(!d.length)return;e.activate(d)}var f="prev"===c?"next":"prev",g="prev"===c?"first":"last";e.$firstActiveToken[f+"All"](".token").each(function(){e.deactivate(a(this))}),e.activate(e.$wrapper.find(".token:"+g),!0,!0),b.preventDefault()}}if(this.focused){var e=this;switch(b.keyCode){case 8:if(!this.$input.is(document.activeElement))break;this.lastInputValue=this.$input.val();break;case 37:c("rtl"===this.textDirection?"next":"prev");break;case 38:d("prev");break;case 39:c("rtl"===this.textDirection?"prev":"next");break;case 40:d("next");break;case 65:if(this.$input.val().length>0||!b.ctrlKey&&!b.metaKey)break;this.activateAll(),b.preventDefault();break;case 9:case 13:if(this.$input.data("ui-autocomplete")&&this.$input.data("ui-autocomplete").menu.element.find("li:has(a.ui-state-focus), li.ui-state-focus").length)break;if(this.$input.hasClass("tt-input")&&this.$wrapper.find(".tt-cursor").length)break;if(this.$input.hasClass("tt-input")&&this.$wrapper.find(".tt-hint").val()&&this.$wrapper.find(".tt-hint").val().length)break;if(this.$input.is(document.activeElement)&&this.$input.val().length||this.$input.data("edit"))return this.createTokensFromInput(b,this.$input.data("edit"));if(this.$input.is(document.activeElement)&&13===b.keyCode&&(b.preventDefault(),this.$element.trigger("tokenfield:next")),13===b.keyCode){if(!this.$copyHelper.is(document.activeElement)||1!==this.$wrapper.find(".token.active").length)break;if(!e.options.allowEditing)break;this.edit(this.$wrapper.find(".token.active"))}}this.lastKeyDown=b.keyCode}},keypress:function(b){if(-1!==a.inArray(b.which,this._triggerKeys)&&this.$input.is(document.activeElement)){var c=this.$input.val(),d=/^"[^"]*$/.test(c);if(d)return;return c&&this.createTokensFromInput(b),!1}},keyup:function(a){if(this.preventInputFocus=!1,this.focused){switch(a.keyCode){case 8:if(this.$input.is(document.activeElement)){if(this.$input.val().length||this.lastInputValue.length&&8===this.lastKeyDown)break;this.preventDeactivation=!0;var b=this.$input.hasClass("tt-input")?this.$input.parent().prevAll(".token:first"):this.$input.prevAll(".token:first");if(!b.length)break;this.activate(b)}else this.remove(a);break;case 46:this.remove(a,"next")}this.lastKeyUp=a.keyCode}},focus:function(a){this.focused=!0,this.$wrapper.addClass("focus"),this.$input.is(document.activeElement)&&(this.$wrapper.find(".active").removeClass("active"),this.$firstActiveToken=null,this.options.showAutocompleteOnFocus&&this.search())},blur:function(a){this.focused=!1,this.$wrapper.removeClass("focus"),this.preventDeactivation||this.$element.is(document.activeElement)||(this.$wrapper.find(".active").removeClass("active"),this.$firstActiveToken=null),!this.preventCreateTokens&&(this.$input.data("edit")&&!this.$input.is(document.activeElement)||this.options.createTokensOnBlur)&&this.createTokensFromInput(a),this.preventDeactivation=!1,this.preventCreateTokens=!1},paste:function(a){var b=this;b.options.allowPasting&&setTimeout(function(){b.createTokensFromInput(a)},1)},change:function(a){"tokenfield"!==a.initiator&&this.setTokens(this.$element.val())},createTokensFromInput:function(a,b){if(!(this.$input.val().lengththis.$firstActiveToken.index():!1;if(c)return this.deactivate(b)}var d=this.$wrapper.find(".active:first"),e=d.prevAll(".token:first");return e.length||(e=this.$wrapper.find(".token:first")),e.length||a?void this.activate(e,a):void this.$input.focus()},activate:function(b,c,d,e){if(b){if("undefined"==typeof e&&(e=!0),d&&(c=!0),this.$copyHelper.focus(),c||(this.$wrapper.find(".active").removeClass("active"),e?this.$firstActiveToken=b:delete this.$firstActiveToken),d&&this.$firstActiveToken){var f=this.$firstActiveToken.index()-2,g=b.index()-2,h=this;this.$wrapper.find(".token").slice(Math.min(f,g)+1,Math.max(f,g)).each(function(){h.activate(a(this),!0)})}b.addClass("active"),this.$copyHelper.val(this.getTokensList(null,null,!0)).select()}},activateAll:function(){var b=this;this.$wrapper.find(".token").each(function(c){b.activate(a(this),0!==c,!1,!1)})},deactivate:function(a){a&&(a.removeClass("active"),this.$copyHelper.val(this.getTokensList(null,null,!0)).select())},toggle:function(a){a&&(a.toggleClass("active"),this.$copyHelper.val(this.getTokensList(null,null,!0)).select())},edit:function(b){if(b){var c=b.data("attrs"),d={attrs:c,relatedTarget:b.get(0)},e=a.Event("tokenfield:edittoken",d);if(this.$element.trigger(e),!e.isDefaultPrevented()){b.find(".token-label").text(c.value);var f=b.outerWidth(),g=this.$input.hasClass("tt-input")?this.$input.parent():this.$input;b.replaceWith(g),this.preventCreateTokens=!0,this.$input.val(c.value).select().data("edit",!0).width(f),this.update(),this.$element.trigger(a.Event("tokenfield:editedtoken",d))}}},unedit:function(a){var b=this.$input.hasClass("tt-input")?this.$input.parent():this.$input;if(b.appendTo(this.$wrapper),this.$input.data("edit",!1),this.$mirror.text(""),this.update(),a){var c=this;setTimeout(function(){c.$input.focus()},1)}},remove:function(b,c){if(!(this.$input.is(document.activeElement)||this._disabled||this._readonly)){var d,e="click"===b.type?a(b.target).closest(".token"):this.$wrapper.find(".token.active");"click"!==b.type&&(c||(c="prev"),this[c](),"prev"===c&&(d=0===e.first().prevAll(".token:first").length));var f={attrs:this.getTokenData(e),relatedTarget:e.get(0)},g=a.Event("tokenfield:removetoken",f);if(this.$element.trigger(g),!g.isDefaultPrevented()){var h=a.Event("tokenfield:removedtoken",f),i=a.Event("change",{initiator:"tokenfield"});e.remove(),this.$element.val(this.getTokensList()).trigger(h).trigger(i),(!this.$wrapper.find(".token").length||"click"===b.type||d)&&this.$input.focus(),this.$input.css("width",this.options.minWidth+"px"),this.update(),b.preventDefault(),b.stopPropagation()}}},update:function(a){var b=this.$input.val(),c=parseInt(this.$input.css("padding-left"),10),d=parseInt(this.$input.css("padding-right"),10),e=c+d;if(this.$input.data("edit")){if(b||(b=this.$input.prop("placeholder")),b===this.$mirror.text())return;this.$mirror.text(b);var f=this.$mirror.width()+10;if(f>this.$wrapper.width())return this.$input.width(this.$wrapper.width());this.$input.width(f)}else{this.$input.width(this.options.minWidth);var g="rtl"===this.textDirection?this.$input.offset().left+this.$input.outerWidth()-this.$wrapper.offset().left-parseInt(this.$wrapper.css("padding-left"),10)-e-1:this.$wrapper.offset().left+this.$wrapper.width()+parseInt(this.$wrapper.css("padding-left"),10)-this.$input.offset().left-e;isNaN(g)?this.$input.width("100%"):this.$input.width(g)}},focusInput:function(b){if(!(a(b.target).closest(".token").length||a(b.target).closest(".token-input").length||a(b.target).closest(".tt-dropdown-menu").length)){var c=this;setTimeout(function(){c.$input.focus()},0)}},search:function(){this.$input.data("ui-autocomplete")&&this.$input.autocomplete("search")},disable:function(){this.setProperty("disabled",!0)},enable:function(){this.setProperty("disabled",!1)},readonly:function(){this.setProperty("readonly",!0)},writeable:function(){this.setProperty("readonly",!1)},setProperty:function(a,b){this["_"+a]=b,this.$input.prop(a,b),this.$element.prop(a,b),this.$wrapper[b?"addClass":"removeClass"](a)},destroy:function(){this.$element.val(this.getTokensList()),this.$element.css(this.$element.data("original-styles")),this.$element.prop("tabindex",this.$element.data("original-tabindex"));var b=a('label[for="'+this.$input.prop("id")+'"]');b.length&&b.prop("for",this.$element.prop("id")),this.$element.insertBefore(this.$wrapper),this.$element.off(".tokenfield"),this.$element.removeData("original-styles").removeData("original-tabindex").removeData("bs.tokenfield"),this.$wrapper.remove(),this.$mirror.remove();var c=this.$element;return c}};var d=a.fn.tokenfield;return a.fn.tokenfield=function(b,d){var e,f=[];Array.prototype.push.apply(f,arguments);var g=this.each(function(){var g=a(this),h=g.data("bs.tokenfield"),i="object"==typeof b&&b;"string"==typeof b&&h&&h[b]?(f.shift(),e=h[b].apply(h,f)):h||"string"==typeof b||d||(g.data("bs.tokenfield",h=new c(this,i)),g.trigger("tokenfield:initialize"))});return"undefined"!=typeof e?e:g},a.fn.tokenfield.defaults={minWidth:60,minLength:0,html:!0,allowEditing:!0,allowPasting:!0,limit:0,autocomplete:{},typeahead:{},showAutocompleteOnFocus:!1,createTokensOnBlur:!1,delimiter:",",beautify:!0,inputType:"text"},a.fn.tokenfield.Constructor=c,a.fn.tokenfield.noConflict=function(){return a.fn.tokenfield=d,this},c});
--------------------------------------------------------------------------------
/dist/css/bootstrap-tokenfield.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * bootstrap-tokenfield
3 | * https://github.com/sliptree/bootstrap-tokenfield
4 | * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
5 | */
6 | @-webkit-keyframes blink {
7 | 0% {
8 | border-color: #ededed;
9 | }
10 | 100% {
11 | border-color: #b94a48;
12 | }
13 | }
14 | @-moz-keyframes blink {
15 | 0% {
16 | border-color: #ededed;
17 | }
18 | 100% {
19 | border-color: #b94a48;
20 | }
21 | }
22 | @keyframes blink {
23 | 0% {
24 | border-color: #ededed;
25 | }
26 | 100% {
27 | border-color: #b94a48;
28 | }
29 | }
30 | .tokenfield {
31 | height: auto;
32 | min-height: 34px;
33 | padding-bottom: 0px;
34 | }
35 | .tokenfield.focus {
36 | border-color: #66afe9;
37 | outline: 0;
38 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
39 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
40 | }
41 | .tokenfield .token {
42 | -webkit-box-sizing: border-box;
43 | -moz-box-sizing: border-box;
44 | box-sizing: border-box;
45 | -webkit-border-radius: 3px;
46 | -moz-border-radius: 3px;
47 | border-radius: 3px;
48 | display: inline-block;
49 | border: 1px solid #d9d9d9;
50 | background-color: #ededed;
51 | white-space: nowrap;
52 | margin: -1px 5px 5px 0;
53 | height: 22px;
54 | vertical-align: top;
55 | cursor: default;
56 | }
57 | .tokenfield .token:hover {
58 | border-color: #b9b9b9;
59 | }
60 | .tokenfield .token.active {
61 | border-color: #52a8ec;
62 | border-color: rgba(82, 168, 236, 0.8);
63 | }
64 | .tokenfield .token.duplicate {
65 | border-color: #ebccd1;
66 | -webkit-animation-name: blink;
67 | animation-name: blink;
68 | -webkit-animation-duration: 0.1s;
69 | animation-duration: 0.1s;
70 | -webkit-animation-direction: normal;
71 | animation-direction: normal;
72 | -webkit-animation-timing-function: ease;
73 | animation-timing-function: ease;
74 | -webkit-animation-iteration-count: infinite;
75 | animation-iteration-count: infinite;
76 | }
77 | .tokenfield .token.invalid {
78 | background: none;
79 | border: 1px solid transparent;
80 | -webkit-border-radius: 0;
81 | -moz-border-radius: 0;
82 | border-radius: 0;
83 | border-bottom: 1px dotted #d9534f;
84 | }
85 | .tokenfield .token.invalid.active {
86 | background: #ededed;
87 | border: 1px solid #ededed;
88 | -webkit-border-radius: 3px;
89 | -moz-border-radius: 3px;
90 | border-radius: 3px;
91 | }
92 | .tokenfield .token .token-label {
93 | display: inline-block;
94 | overflow: hidden;
95 | text-overflow: ellipsis;
96 | padding-left: 4px;
97 | vertical-align: top;
98 | }
99 | .tokenfield .token .close {
100 | font-family: Arial;
101 | display: inline-block;
102 | line-height: 100%;
103 | font-size: 1.1em;
104 | line-height: 1.49em;
105 | margin-left: 5px;
106 | float: none;
107 | height: 100%;
108 | vertical-align: top;
109 | padding-right: 4px;
110 | }
111 | .tokenfield .token-input {
112 | background: none;
113 | width: 60px;
114 | min-width: 60px;
115 | border: 0;
116 | height: 20px;
117 | padding: 0;
118 | margin-bottom: 6px;
119 | -webkit-box-shadow: none;
120 | box-shadow: none;
121 | }
122 | .tokenfield .token-input:focus {
123 | border-color: transparent;
124 | outline: 0;
125 | /* IE6-9 */
126 | -webkit-box-shadow: none;
127 | box-shadow: none;
128 | }
129 | .tokenfield.disabled {
130 | cursor: not-allowed;
131 | background-color: #eeeeee;
132 | }
133 | .tokenfield.disabled .token-input {
134 | cursor: not-allowed;
135 | }
136 | .tokenfield.disabled .token:hover {
137 | cursor: not-allowed;
138 | border-color: #d9d9d9;
139 | }
140 | .tokenfield.disabled .token:hover .close {
141 | cursor: not-allowed;
142 | opacity: 0.2;
143 | filter: alpha(opacity=20);
144 | }
145 | .has-warning .tokenfield.focus {
146 | border-color: #66512c;
147 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
148 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
149 | }
150 | .has-error .tokenfield.focus {
151 | border-color: #843534;
152 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
153 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
154 | }
155 | .has-success .tokenfield.focus {
156 | border-color: #2b542c;
157 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
158 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
159 | }
160 | .tokenfield.input-sm,
161 | .input-group-sm .tokenfield {
162 | min-height: 30px;
163 | padding-bottom: 0px;
164 | }
165 | .input-group-sm .token,
166 | .tokenfield.input-sm .token {
167 | height: 20px;
168 | margin-bottom: 4px;
169 | }
170 | .input-group-sm .token-input,
171 | .tokenfield.input-sm .token-input {
172 | height: 18px;
173 | margin-bottom: 5px;
174 | }
175 | .tokenfield.input-lg,
176 | .input-group-lg .tokenfield {
177 | height: auto;
178 | min-height: 45px;
179 | padding-bottom: 4px;
180 | }
181 | .input-group-lg .token,
182 | .tokenfield.input-lg .token {
183 | height: 25px;
184 | }
185 | .input-group-lg .token .close,
186 | .tokenfield.input-lg .token .close {
187 | line-height: 1.3em;
188 | }
189 | .input-group-lg .token-label,
190 | .tokenfield.input-lg .token-label {
191 | line-height: 23px;
192 | }
193 | .input-group-lg .token-input,
194 | .tokenfield.input-lg .token-input {
195 | height: 23px;
196 | line-height: 23px;
197 | margin-bottom: 6px;
198 | vertical-align: top;
199 | }
200 | .tokenfield.rtl {
201 | direction: rtl;
202 | text-align: right;
203 | }
204 | .tokenfield.rtl .token {
205 | margin: -1px 0 5px 5px;
206 | }
207 | .tokenfield.rtl .token .token-label {
208 | padding-left: 0px;
209 | padding-right: 4px;
210 | }
211 |
--------------------------------------------------------------------------------
/dist/css/bootstrap-tokenfield.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * bootstrap-tokenfield
3 | * https://github.com/sliptree/bootstrap-tokenfield
4 | * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
5 | */@-webkit-keyframes blink{0%{border-color:#ededed}100%{border-color:#b94a48}}@-moz-keyframes blink{0%{border-color:#ededed}100%{border-color:#b94a48}}@keyframes blink{0%{border-color:#ededed}100%{border-color:#b94a48}}.tokenfield{height:auto;min-height:34px;padding-bottom:0}.tokenfield.focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.tokenfield .token{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;display:inline-block;border:1px solid #d9d9d9;background-color:#ededed;white-space:nowrap;margin:-1px 5px 5px 0;height:22px;vertical-align:top;cursor:default}.tokenfield .token:hover{border-color:#b9b9b9}.tokenfield .token.active{border-color:#52a8ec;border-color:rgba(82,168,236,0.8)}.tokenfield .token.duplicate{border-color:#ebccd1;-webkit-animation-name:blink;animation-name:blink;-webkit-animation-duration:.1s;animation-duration:.1s;-webkit-animation-direction:normal;animation-direction:normal;-webkit-animation-timing-function:ease;animation-timing-function:ease;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.tokenfield .token.invalid{background:none;border:1px solid transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border-bottom:1px dotted #d9534f}.tokenfield .token.invalid.active{background:#ededed;border:1px solid #ededed;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.tokenfield .token .token-label{display:inline-block;overflow:hidden;text-overflow:ellipsis;padding-left:4px;vertical-align:top}.tokenfield .token .close{font-family:Arial;display:inline-block;line-height:100%;font-size:1.1em;line-height:1.49em;margin-left:5px;float:none;height:100%;vertical-align:top;padding-right:4px}.tokenfield .token-input{background:none;width:60px;min-width:60px;border:0;height:20px;padding:0;margin-bottom:6px;-webkit-box-shadow:none;box-shadow:none}.tokenfield .token-input:focus{border-color:transparent;outline:0;-webkit-box-shadow:none;box-shadow:none}.tokenfield.disabled{cursor:not-allowed;background-color:#eee}.tokenfield.disabled .token-input{cursor:not-allowed}.tokenfield.disabled .token:hover{cursor:not-allowed;border-color:#d9d9d9}.tokenfield.disabled .token:hover .close{cursor:not-allowed;opacity:.2;filter:alpha(opacity=20)}.has-warning .tokenfield.focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-error .tokenfield.focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-success .tokenfield.focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.tokenfield.input-sm,.input-group-sm .tokenfield{min-height:30px;padding-bottom:0}.input-group-sm .token,.tokenfield.input-sm .token{height:20px;margin-bottom:4px}.input-group-sm .token-input,.tokenfield.input-sm .token-input{height:18px;margin-bottom:5px}.tokenfield.input-lg,.input-group-lg .tokenfield{height:auto;min-height:45px;padding-bottom:4px}.input-group-lg .token,.tokenfield.input-lg .token{height:25px}.input-group-lg .token .close,.tokenfield.input-lg .token .close{line-height:1.3em}.input-group-lg .token-label,.tokenfield.input-lg .token-label{line-height:23px}.input-group-lg .token-input,.tokenfield.input-lg .token-input{height:23px;line-height:23px;margin-bottom:6px;vertical-align:top}.tokenfield.rtl{direction:rtl;text-align:right}.tokenfield.rtl .token{margin:-1px 0 5px 5px}.tokenfield.rtl .token .token-label{padding-left:0;padding-right:4px}
--------------------------------------------------------------------------------
/dist/css/tokenfield-typeahead.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * bootstrap-tokenfield
3 | * https://github.com/sliptree/bootstrap-tokenfield
4 | * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
5 | */
6 | /* General Typeahead styling, from http://jsfiddle.net/ragulka/Dy9au/1/ */
7 | .twitter-typeahead {
8 | width: 100%;
9 | position: relative;
10 | vertical-align: top;
11 | }
12 | .twitter-typeahead .tt-input,
13 | .twitter-typeahead .tt-hint {
14 | margin: 0;
15 | width: 100%;
16 | vertical-align: middle;
17 | background-color: #ffffff;
18 | height: 34px;
19 | padding: 6px 12px;
20 | font-size: 14px;
21 | line-height: 1.428571429;
22 | }
23 | .twitter-typeahead .tt-hint {
24 | color: #999999;
25 | z-index: 1;
26 | border: 1px solid transparent;
27 | }
28 | .twitter-typeahead .tt-input {
29 | color: #555555;
30 | z-index: 2;
31 | }
32 | .twitter-typeahead .input-sm.tt-input,
33 | .twitter-typeahead .hint-sm.tt-hint {
34 | border-radius: 3px;
35 | }
36 | .twitter-typeahead .input-lg.tt-input,
37 | .twitter-typeahead .hint-lg.tt-hint {
38 | border-radius: 6px;
39 | }
40 | .input-group .twitter-typeahead:first-child .tt-input,
41 | .input-group .twitter-typeahead:first-child .tt-hint {
42 | border-radius: 4px 0 0 4px !important;
43 | }
44 | .input-group .twitter-typeahead:last-child .tt-input,
45 | .input-group .twitter-typeahead:last-child .tt-hint {
46 | border-radius: 0 4px 4px 0 !important;
47 | }
48 | .input-group.input-group-sm .twitter-typeahead:first-child .tt-input,
49 | .input-group.input-group-sm .twitter-typeahead:first-child .tt-hint {
50 | border-radius: 3px 0 0 3px !important;
51 | }
52 | .input-group.input-group-sm .twitter-typeahead:last-child .tt-input,
53 | .input-group.input-group-sm .twitter-typeahead:last-child .tt-hint {
54 | border-radius: 0 3px 3px 0 !important;
55 | }
56 | .input-group.input-group-lg .twitter-typeahead:first-child .tt-input,
57 | .input-group.input-group-lg .twitter-typeahead:first-child .tt-hint {
58 | border-radius: 6px 0 0 6px !important;
59 | }
60 | .input-group.input-group-lg .twitter-typeahead:last-child .tt-input,
61 | .input-group.input-group-lg .twitter-typeahead:last-child .tt-hint {
62 | border-radius: 0 6px 6px 0 !important;
63 | }
64 | .input-sm.tt-input,
65 | .hint-sm.tt-hint,
66 | .input-group.input-group-sm .tt-input,
67 | .input-group.input-group-sm .tt-hint {
68 | height: 30px;
69 | padding: 5px 10px;
70 | font-size: 12px;
71 | line-height: 1.5;
72 | }
73 | .input-lg.tt-input,
74 | .hint-lg.tt-hint,
75 | .input-group.input-group-lg .tt-input,
76 | .input-group.input-group-lg .tt-hint {
77 | height: 45px;
78 | padding: 10px 16px;
79 | font-size: 18px;
80 | line-height: 1.33;
81 | }
82 | .tt-dropdown-menu {
83 | width: 100%;
84 | min-width: 160px;
85 | margin-top: 2px;
86 | padding: 5px 0;
87 | background-color: #ffffff;
88 | border: 1px solid #ccc;
89 | border: 1px solid rgba(0, 0, 0, 0.15);
90 | border-radius: 6px;
91 | -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
92 | box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
93 | -webkit-background-clip: padding-box;
94 | -moz-background-clip: padding;
95 | background-clip: padding-box;
96 | }
97 | .tt-suggestion {
98 | display: block;
99 | padding: 3px 20px;
100 | }
101 | .tt-suggestion.tt-cursor {
102 | color: #262626;
103 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
104 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
105 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
106 | background-repeat: repeat-x;
107 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
108 | }
109 | .tt-suggestion.tt-cursor a {
110 | color: #ffffff;
111 | }
112 | .tt-suggestion p {
113 | margin: 0;
114 | }
115 | /* Tokenfield-specific Typeahead styling */
116 | .tokenfield .twitter-typeahead {
117 | width: auto;
118 | }
119 | .tokenfield .twitter-typeahead .tt-hint {
120 | padding: 0;
121 | height: 20px;
122 | }
123 | .tokenfield .twitter-typeahead .tt-suggestions {
124 | font-size: 14px;
125 | }
126 | .tokenfield.input-sm .twitter-typeahead .tt-input,
127 | .tokenfield.input-sm .twitter-typeahead .tt-hint {
128 | height: 18px;
129 | font-size: 12px;
130 | line-height: 1.5;
131 | }
132 | .tokenfield.input-lg .twitter-typeahead .tt-input,
133 | .tokenfield.input-lg .twitter-typeahead .tt-hint {
134 | height: 23px;
135 | font-size: 18px;
136 | line-height: 1.33;
137 | }
138 |
--------------------------------------------------------------------------------
/dist/css/tokenfield-typeahead.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * bootstrap-tokenfield
3 | * https://github.com/sliptree/bootstrap-tokenfield
4 | * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
5 | */.twitter-typeahead{width:100%;position:relative;vertical-align:top}.twitter-typeahead .tt-input,.twitter-typeahead .tt-hint{margin:0;width:100%;vertical-align:middle;background-color:#fff;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143}.twitter-typeahead .tt-hint{color:#999;z-index:1;border:1px solid transparent}.twitter-typeahead .tt-input{color:#555;z-index:2}.twitter-typeahead .input-sm.tt-input,.twitter-typeahead .hint-sm.tt-hint{border-radius:3px}.twitter-typeahead .input-lg.tt-input,.twitter-typeahead .hint-lg.tt-hint{border-radius:6px}.input-group .twitter-typeahead:first-child .tt-input,.input-group .twitter-typeahead:first-child .tt-hint{border-radius:4px 0 0 4px !important}.input-group .twitter-typeahead:last-child .tt-input,.input-group .twitter-typeahead:last-child .tt-hint{border-radius:0 4px 4px 0 !important}.input-group.input-group-sm .twitter-typeahead:first-child .tt-input,.input-group.input-group-sm .twitter-typeahead:first-child .tt-hint{border-radius:3px 0 0 3px !important}.input-group.input-group-sm .twitter-typeahead:last-child .tt-input,.input-group.input-group-sm .twitter-typeahead:last-child .tt-hint{border-radius:0 3px 3px 0 !important}.input-group.input-group-lg .twitter-typeahead:first-child .tt-input,.input-group.input-group-lg .twitter-typeahead:first-child .tt-hint{border-radius:6px 0 0 6px !important}.input-group.input-group-lg .twitter-typeahead:last-child .tt-input,.input-group.input-group-lg .twitter-typeahead:last-child .tt-hint{border-radius:0 6px 6px 0 !important}.input-sm.tt-input,.hint-sm.tt-hint,.input-group.input-group-sm .tt-input,.input-group.input-group-sm .tt-hint{height:30px;padding:5px 10px;font-size:12px;line-height:1.5}.input-lg.tt-input,.hint-lg.tt-hint,.input-group.input-group-lg .tt-input,.input-group.input-group-lg .tt-hint{height:45px;padding:10px 16px;font-size:18px;line-height:1.33}.tt-dropdown-menu{width:100%;min-width:160px;margin-top:2px;padding:5px 0;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.tt-suggestion{display:block;padding:3px 20px}.tt-suggestion.tt-cursor{color:#262626;background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.tt-suggestion.tt-cursor a{color:#fff}.tt-suggestion p{margin:0}.tokenfield .twitter-typeahead{width:auto}.tokenfield .twitter-typeahead .tt-hint{padding:0;height:20px}.tokenfield .twitter-typeahead .tt-suggestions{font-size:14px}.tokenfield.input-sm .twitter-typeahead .tt-input,.tokenfield.input-sm .twitter-typeahead .tt-hint{height:18px;font-size:12px;line-height:1.5}.tokenfield.input-lg .twitter-typeahead .tt-input,.tokenfield.input-lg .twitter-typeahead .tt-hint{height:23px;font-size:18px;line-height:1.33}
--------------------------------------------------------------------------------
/docs-assets/css/pygments-manni.css:
--------------------------------------------------------------------------------
1 | .hll { background-color: #ffffcc }
2 | /*{ background: #f0f3f3; }*/
3 | .c { color: #999; } /* Comment */
4 | .err { color: #AA0000; background-color: #FFAAAA } /* Error */
5 | .k { color: #006699; } /* Keyword */
6 | .o { color: #555555 } /* Operator */
7 | .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */
8 | .cp { color: #009999 } /* Comment.Preproc */
9 | .c1 { color: #999; } /* Comment.Single */
10 | .cs { color: #999; } /* Comment.Special */
11 | .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */
12 | .ge { font-style: italic } /* Generic.Emph */
13 | .gr { color: #FF0000 } /* Generic.Error */
14 | .gh { color: #003300; } /* Generic.Heading */
15 | .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */
16 | .go { color: #AAAAAA } /* Generic.Output */
17 | .gp { color: #000099; } /* Generic.Prompt */
18 | .gs { } /* Generic.Strong */
19 | .gu { color: #003300; } /* Generic.Subheading */
20 | .gt { color: #99CC66 } /* Generic.Traceback */
21 | .kc { color: #006699; } /* Keyword.Constant */
22 | .kd { color: #006699; } /* Keyword.Declaration */
23 | .kn { color: #006699; } /* Keyword.Namespace */
24 | .kp { color: #006699 } /* Keyword.Pseudo */
25 | .kr { color: #006699; } /* Keyword.Reserved */
26 | .kt { color: #007788; } /* Keyword.Type */
27 | .m { color: #FF6600 } /* Literal.Number */
28 | .s { color: #d44950 } /* Literal.String */
29 | .na { color: #4f9fcf } /* Name.Attribute */
30 | .nb { color: #336666 } /* Name.Builtin */
31 | .nc { color: #00AA88; } /* Name.Class */
32 | .no { color: #336600 } /* Name.Constant */
33 | .nd { color: #9999FF } /* Name.Decorator */
34 | .ni { color: #999999; } /* Name.Entity */
35 | .ne { color: #CC0000; } /* Name.Exception */
36 | .nf { color: #CC00FF } /* Name.Function */
37 | .nl { color: #9999FF } /* Name.Label */
38 | .nn { color: #00CCFF; } /* Name.Namespace */
39 | .nt { color: #2f6f9f; } /* Name.Tag */
40 | .nv { color: #003333 } /* Name.Variable */
41 | .ow { color: #000000; } /* Operator.Word */
42 | .w { color: #bbbbbb } /* Text.Whitespace */
43 | .mf { color: #FF6600 } /* Literal.Number.Float */
44 | .mh { color: #FF6600 } /* Literal.Number.Hex */
45 | .mi { color: #FF6600 } /* Literal.Number.Integer */
46 | .mo { color: #FF6600 } /* Literal.Number.Oct */
47 | .sb { color: #CC3300 } /* Literal.String.Backtick */
48 | .sc { color: #CC3300 } /* Literal.String.Char */
49 | .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */
50 | .s2 { color: #CC3300 } /* Literal.String.Double */
51 | .se { color: #CC3300; } /* Literal.String.Escape */
52 | .sh { color: #CC3300 } /* Literal.String.Heredoc */
53 | .si { color: #AA0000 } /* Literal.String.Interpol */
54 | .sx { color: #CC3300 } /* Literal.String.Other */
55 | .sr { color: #33AAAA } /* Literal.String.Regex */
56 | .s1 { color: #CC3300 } /* Literal.String.Single */
57 | .ss { color: #FFCC33 } /* Literal.String.Symbol */
58 | .bp { color: #336666 } /* Name.Builtin.Pseudo */
59 | .vc { color: #003333 } /* Name.Variable.Class */
60 | .vg { color: #003333 } /* Name.Variable.Global */
61 | .vi { color: #003333 } /* Name.Variable.Instance */
62 | .il { color: #FF6600 } /* Literal.Number.Integer.Long */
63 |
64 | .css .o,
65 | .css .o + .nt,
66 | .css .nt + .nt { color: #999; }
--------------------------------------------------------------------------------
/docs-assets/js/affix.js:
--------------------------------------------------------------------------------
1 | /* ========================================================================
2 | * Bootstrap: affix.js v3.3.6
3 | * http://getbootstrap.com/javascript/#affix
4 | * ========================================================================
5 | * Copyright 2011-2015 Twitter, Inc.
6 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7 | * ======================================================================== */
8 |
9 |
10 | +function ($) {
11 | 'use strict';
12 |
13 | // AFFIX CLASS DEFINITION
14 | // ======================
15 |
16 | var Affix = function (element, options) {
17 | this.options = $.extend({}, Affix.DEFAULTS, options)
18 |
19 | this.$target = $(this.options.target)
20 | .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
21 | .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
22 |
23 | this.$element = $(element)
24 | this.affixed = null
25 | this.unpin = null
26 | this.pinnedOffset = null
27 |
28 | this.checkPosition()
29 | }
30 |
31 | Affix.VERSION = '3.3.6'
32 |
33 | Affix.RESET = 'affix affix-top affix-bottom'
34 |
35 | Affix.DEFAULTS = {
36 | offset: 0,
37 | target: window
38 | }
39 |
40 | Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
41 | var scrollTop = this.$target.scrollTop()
42 | var position = this.$element.offset()
43 | var targetHeight = this.$target.height()
44 |
45 | if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false
46 |
47 | if (this.affixed == 'bottom') {
48 | if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
49 | return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
50 | }
51 |
52 | var initializing = this.affixed == null
53 | var colliderTop = initializing ? scrollTop : position.top
54 | var colliderHeight = initializing ? targetHeight : height
55 |
56 | if (offsetTop != null && scrollTop <= offsetTop) return 'top'
57 | if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'
58 |
59 | return false
60 | }
61 |
62 | Affix.prototype.getPinnedOffset = function () {
63 | if (this.pinnedOffset) return this.pinnedOffset
64 | this.$element.removeClass(Affix.RESET).addClass('affix')
65 | var scrollTop = this.$target.scrollTop()
66 | var position = this.$element.offset()
67 | return (this.pinnedOffset = position.top - scrollTop)
68 | }
69 |
70 | Affix.prototype.checkPositionWithEventLoop = function () {
71 | setTimeout($.proxy(this.checkPosition, this), 1)
72 | }
73 |
74 | Affix.prototype.checkPosition = function () {
75 | if (!this.$element.is(':visible')) return
76 |
77 | var height = this.$element.height()
78 | var offset = this.options.offset
79 | var offsetTop = offset.top
80 | var offsetBottom = offset.bottom
81 | var scrollHeight = Math.max($(document).height(), $(document.body).height())
82 |
83 | if (typeof offset != 'object') offsetBottom = offsetTop = offset
84 | if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
85 | if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
86 |
87 | var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)
88 |
89 | if (this.affixed != affix) {
90 | if (this.unpin != null) this.$element.css('top', '')
91 |
92 | var affixType = 'affix' + (affix ? '-' + affix : '')
93 | var e = $.Event(affixType + '.bs.affix')
94 |
95 | this.$element.trigger(e)
96 |
97 | if (e.isDefaultPrevented()) return
98 |
99 | this.affixed = affix
100 | this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
101 |
102 | this.$element
103 | .removeClass(Affix.RESET)
104 | .addClass(affixType)
105 | .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
106 | }
107 |
108 | if (affix == 'bottom') {
109 | this.$element.offset({
110 | top: scrollHeight - height - offsetBottom
111 | })
112 | }
113 | }
114 |
115 |
116 | // AFFIX PLUGIN DEFINITION
117 | // =======================
118 |
119 | function Plugin(option) {
120 | return this.each(function () {
121 | var $this = $(this)
122 | var data = $this.data('bs.affix')
123 | var options = typeof option == 'object' && option
124 |
125 | if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
126 | if (typeof option == 'string') data[option]()
127 | })
128 | }
129 |
130 | var old = $.fn.affix
131 |
132 | $.fn.affix = Plugin
133 | $.fn.affix.Constructor = Affix
134 |
135 |
136 | // AFFIX NO CONFLICT
137 | // =================
138 |
139 | $.fn.affix.noConflict = function () {
140 | $.fn.affix = old
141 | return this
142 | }
143 |
144 |
145 | // AFFIX DATA-API
146 | // ==============
147 |
148 | $(window).on('load', function () {
149 | $('[data-spy="affix"]').each(function () {
150 | var $spy = $(this)
151 | var data = $spy.data()
152 |
153 | data.offset = data.offset || {}
154 |
155 | if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom
156 | if (data.offsetTop != null) data.offset.top = data.offsetTop
157 |
158 | Plugin.call($spy, data)
159 | })
160 | })
161 |
162 | }(jQuery);
163 |
--------------------------------------------------------------------------------
/docs-assets/js/docs.js:
--------------------------------------------------------------------------------
1 | jQuery(function(){
2 | // Track downloads
3 | $('#download-master').click(function(){
4 | _trackEvent('Downloads', 'master');
5 | });
6 | });
7 |
8 | jQuery(document).ready(function($) {
9 |
10 | /* Docs scrollspy */
11 | $('body').scrollspy({
12 | target: '.bs-sidebar',
13 | offset: 0
14 | })
15 |
16 | $(window).on('load', function () {
17 | $('body').scrollspy('refresh')
18 | })
19 |
20 | // back to top
21 | setTimeout(function () {
22 | var $sideBar = $('.bs-sidebar')
23 |
24 | $sideBar.affix({
25 | offset: {
26 | top: function () {
27 | var offsetTop = $sideBar.offset().top
28 | var sideBarMargin = parseInt($sideBar.children(0).css('margin-top'), 10)
29 |
30 | return (this.top = offsetTop - sideBarMargin)
31 | }
32 | , bottom: function () {
33 | return (this.bottom = $('.bs-footer').outerHeight(true))
34 | }
35 | }
36 | })
37 | }, 100)
38 |
39 | /* Run examples */
40 | $('.token-example-field').tokenfield();
41 |
42 | $('#tokenfield-1').tokenfield({
43 | autocomplete: {
44 | source: ['red','blue','green','yellow','violet','brown','purple','black','white'],
45 | delay: 100
46 | },
47 | showAutocompleteOnFocus: true,
48 | delimiter: [',',' ', '-', '_']
49 | });
50 |
51 | var engine = new Bloodhound({
52 | local: [{value: 'red'}, {value: 'blue'}, {value: 'green'} , {value: 'yellow'}, {value: 'violet'}, {value: 'brown'}, {value: 'purple'}, {value: 'black'}, {value: 'white'}],
53 | datumTokenizer: function(d) {
54 | return Bloodhound.tokenizers.whitespace(d.value);
55 | },
56 | queryTokenizer: Bloodhound.tokenizers.whitespace
57 | });
58 | engine.initialize();
59 |
60 | $('#tokenfield-typeahead').tokenfield({
61 | typeahead: [null, { source: engine.ttAdapter() }]
62 | });
63 |
64 | $('#tokenfield-2')
65 | .on('tokenfield:createtoken', function (e) {
66 | var data = e.attrs.value.split('|')
67 | e.attrs.value = data[1] || data[0]
68 | e.attrs.label = data[1] ? data[0] + ' (' + data[1] + ')' : data[0]
69 | })
70 | .on('tokenfield:createdtoken', function (e) {
71 | // Über-simplistic e-mail validation
72 | var re = /\S+@\S+\.\S+/
73 | var valid = re.test(e.attrs.value)
74 | if (!valid) {
75 | $(e.relatedTarget).addClass('invalid')
76 | }
77 | })
78 | .on('tokenfield:edittoken', function (e) {
79 | if (e.attrs.label !== e.attrs.value) {
80 | var label = e.attrs.label.split(' (')
81 | e.attrs.value = label[0] + '|' + e.attrs.value
82 | }
83 | })
84 | .on('tokenfield:removedtoken', function (e) {
85 | if (e.attrs.length > 1) {
86 | var values = $.map(e.attrs, function (attrs) { return attrs.value });
87 | alert(e.attrs.length + ' tokens removed! Token values were: ' + values.join(', '))
88 | } else {
89 | alert('Token removed! Token value was: ' + e.attrs.value)
90 | }
91 | })
92 | .tokenfield()
93 |
94 | });
--------------------------------------------------------------------------------
/docs-assets/js/docs.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * bootstrap-tokenfield 0.12.1
3 | * https://github.com/sliptree/bootstrap-tokenfield
4 | * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
5 | */
6 |
7 | jQuery(function(){$("#download-master").click(function(){_trackEvent("Downloads","master")})}),jQuery(document).ready(function(a){a("body").scrollspy({target:".bs-sidebar",offset:0}),a(window).on("load",function(){a("body").scrollspy("refresh")}),setTimeout(function(){var b=a(".bs-sidebar");b.affix({offset:{top:function(){var a=b.offset().top,c=parseInt(b.children(0).css("margin-top"),10);return this.top=a-c},bottom:function(){return this.bottom=a(".bs-footer").outerHeight(!0)}}})},100),a(".token-example-field").tokenfield(),a("#tokenfield-1").tokenfield({autocomplete:{source:["red","blue","green","yellow","violet","brown","purple","black","white"],delay:100},showAutocompleteOnFocus:!0,delimiter:[","," ","-","_"]});var b=new Bloodhound({local:[{value:"red"},{value:"blue"},{value:"green"},{value:"yellow"},{value:"violet"},{value:"brown"},{value:"purple"},{value:"black"},{value:"white"}],datumTokenizer:function(a){return Bloodhound.tokenizers.whitespace(a.value)},queryTokenizer:Bloodhound.tokenizers.whitespace});b.initialize(),a("#tokenfield-typeahead").tokenfield({typeahead:[null,{source:b.ttAdapter()}]}),a("#tokenfield-2").on("tokenfield:createtoken",function(a){var b=a.attrs.value.split("|");a.attrs.value=b[1]||b[0],a.attrs.label=b[1]?b[0]+" ("+b[1]+")":b[0]}).on("tokenfield:createdtoken",function(b){var c=/\S+@\S+\.\S+/,d=c.test(b.attrs.value);d||a(b.relatedTarget).addClass("invalid")}).on("tokenfield:edittoken",function(a){if(a.attrs.label!==a.attrs.value){var b=a.attrs.label.split(" (");a.attrs.value=b[0]+"|"+a.attrs.value}}).on("tokenfield:removedtoken",function(b){if(b.attrs.length>1){var c=a.map(b.attrs,function(a){return a.value});alert(b.attrs.length+" tokens removed! Token values were: "+c.join(", "))}else alert("Token removed! Token value was: "+b.attrs.value)}).tokenfield()});
--------------------------------------------------------------------------------
/docs-assets/js/scrollspy.js:
--------------------------------------------------------------------------------
1 | /* ========================================================================
2 | * Bootstrap: scrollspy.js v3.3.6
3 | * http://getbootstrap.com/javascript/#scrollspy
4 | * ========================================================================
5 | * Copyright 2011-2015 Twitter, Inc.
6 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
7 | * ======================================================================== */
8 |
9 |
10 | +function ($) {
11 | 'use strict';
12 |
13 | // SCROLLSPY CLASS DEFINITION
14 | // ==========================
15 |
16 | function ScrollSpy(element, options) {
17 | this.$body = $(document.body)
18 | this.$scrollElement = $(element).is(document.body) ? $(window) : $(element)
19 | this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
20 | this.selector = (this.options.target || '') + ' .nav li > a'
21 | this.offsets = []
22 | this.targets = []
23 | this.activeTarget = null
24 | this.scrollHeight = 0
25 |
26 | this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this))
27 | this.refresh()
28 | this.process()
29 | }
30 |
31 | ScrollSpy.VERSION = '3.3.6'
32 |
33 | ScrollSpy.DEFAULTS = {
34 | offset: 10
35 | }
36 |
37 | ScrollSpy.prototype.getScrollHeight = function () {
38 | return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight)
39 | }
40 |
41 | ScrollSpy.prototype.refresh = function () {
42 | var that = this
43 | var offsetMethod = 'offset'
44 | var offsetBase = 0
45 |
46 | this.offsets = []
47 | this.targets = []
48 | this.scrollHeight = this.getScrollHeight()
49 |
50 | if (!$.isWindow(this.$scrollElement[0])) {
51 | offsetMethod = 'position'
52 | offsetBase = this.$scrollElement.scrollTop()
53 | }
54 |
55 | this.$body
56 | .find(this.selector)
57 | .map(function () {
58 | var $el = $(this)
59 | var href = $el.data('target') || $el.attr('href')
60 | var $href = /^#./.test(href) && $(href)
61 |
62 | return ($href
63 | && $href.length
64 | && $href.is(':visible')
65 | && [[$href[offsetMethod]().top + offsetBase, href]]) || null
66 | })
67 | .sort(function (a, b) { return a[0] - b[0] })
68 | .each(function () {
69 | that.offsets.push(this[0])
70 | that.targets.push(this[1])
71 | })
72 | }
73 |
74 | ScrollSpy.prototype.process = function () {
75 | var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
76 | var scrollHeight = this.getScrollHeight()
77 | var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height()
78 | var offsets = this.offsets
79 | var targets = this.targets
80 | var activeTarget = this.activeTarget
81 | var i
82 |
83 | if (this.scrollHeight != scrollHeight) {
84 | this.refresh()
85 | }
86 |
87 | if (scrollTop >= maxScroll) {
88 | return activeTarget != (i = targets[targets.length - 1]) && this.activate(i)
89 | }
90 |
91 | if (activeTarget && scrollTop < offsets[0]) {
92 | this.activeTarget = null
93 | return this.clear()
94 | }
95 |
96 | for (i = offsets.length; i--;) {
97 | activeTarget != targets[i]
98 | && scrollTop >= offsets[i]
99 | && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1])
100 | && this.activate(targets[i])
101 | }
102 | }
103 |
104 | ScrollSpy.prototype.activate = function (target) {
105 | this.activeTarget = target
106 |
107 | this.clear()
108 |
109 | var selector = this.selector +
110 | '[data-target="' + target + '"],' +
111 | this.selector + '[href="' + target + '"]'
112 |
113 | var active = $(selector)
114 | .parents('li')
115 | .addClass('active')
116 |
117 | if (active.parent('.dropdown-menu').length) {
118 | active = active
119 | .closest('li.dropdown')
120 | .addClass('active')
121 | }
122 |
123 | active.trigger('activate.bs.scrollspy')
124 | }
125 |
126 | ScrollSpy.prototype.clear = function () {
127 | $(this.selector)
128 | .parentsUntil(this.options.target, '.active')
129 | .removeClass('active')
130 | }
131 |
132 |
133 | // SCROLLSPY PLUGIN DEFINITION
134 | // ===========================
135 |
136 | function Plugin(option) {
137 | return this.each(function () {
138 | var $this = $(this)
139 | var data = $this.data('bs.scrollspy')
140 | var options = typeof option == 'object' && option
141 |
142 | if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
143 | if (typeof option == 'string') data[option]()
144 | })
145 | }
146 |
147 | var old = $.fn.scrollspy
148 |
149 | $.fn.scrollspy = Plugin
150 | $.fn.scrollspy.Constructor = ScrollSpy
151 |
152 |
153 | // SCROLLSPY NO CONFLICT
154 | // =====================
155 |
156 | $.fn.scrollspy.noConflict = function () {
157 | $.fn.scrollspy = old
158 | return this
159 | }
160 |
161 |
162 | // SCROLLSPY DATA-API
163 | // ==================
164 |
165 | $(window).on('load.bs.scrollspy.data-api', function () {
166 | $('[data-spy="scroll"]').each(function () {
167 | var $spy = $(this)
168 | Plugin.call($spy, $spy.data())
169 | })
170 | })
171 |
172 | }(jQuery);
173 |
--------------------------------------------------------------------------------
/docs-assets/js/typeahead.bundle.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * typeahead.js 0.10.1
3 | * https://github.com/twitter/typeahead.js
4 | * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
5 | */
6 |
7 | !function(a){var b={isMsie:function(){return/(msie|trident)/i.test(navigator.userAgent)?navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2]:!1},isBlankString:function(a){return!a||/^\s*$/.test(a)},escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isString:function(a){return"string"==typeof a},isNumber:function(a){return"number"==typeof a},isArray:a.isArray,isFunction:a.isFunction,isObject:a.isPlainObject,isUndefined:function(a){return"undefined"==typeof a},bind:a.proxy,each:function(b,c){function d(a,b){return c(b,a)}a.each(b,d)},map:a.map,filter:a.grep,every:function(b,c){var d=!0;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?void 0:!1}),!!d):d},some:function(b,c){var d=!1;return b?(a.each(b,function(a,e){return(d=c.call(null,e,a,b))?!1:void 0}),!!d):d},mixin:a.extend,getUniqueId:function(){var a=0;return function(){return a++}}(),templatify:function(b){function c(){return String(b)}return a.isFunction(b)?b:c},defer:function(a){setTimeout(a,0)},debounce:function(a,b,c){var d,e;return function(){var f,g,h=this,i=arguments;return f=function(){d=null,c||(e=a.apply(h,i))},g=c&&!d,clearTimeout(d),d=setTimeout(f,b),g&&(e=a.apply(h,i)),e}},throttle:function(a,b){var c,d,e,f,g,h;return g=0,h=function(){g=new Date,e=null,f=a.apply(c,d)},function(){var i=new Date,j=b-(i-g);return c=this,d=arguments,0>=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},noop:function(){}},c="0.10.1",d=function(){function a(a){this.maxSize=a||100,this.size=0,this.hash={},this.list=new c}function c(){this.head=this.tail=null}function d(a,b){this.key=a,this.val=b,this.prev=this.next=null}return b.mixin(a.prototype,{set:function(a,b){var c,e=this.list.tail;this.size>=this.maxSize&&(this.list.remove(e),delete this.hash[e.key]),(c=this.hash[a])?(c.val=b,this.list.moveToFront(c)):(c=new d(a,b),this.list.add(c),this.hash[a]=c,this.size++)},get:function(a){var b=this.hash[a];return b?(this.list.moveToFront(b),b.val):void 0}}),b.mixin(c.prototype,{add:function(a){this.head&&(a.next=this.head,this.head.prev=a),this.head=a,this.tail=this.tail||a},remove:function(a){a.prev?a.prev.next=a.next:this.head=a.next,a.next?a.next.prev=a.prev:this.tail=a.prev},moveToFront:function(a){this.remove(a),this.add(a)}}),a}(this),e=function(){function a(a){this.prefix=["__",a,"__"].join(""),this.ttlKey="__ttl__",this.keyMatcher=new RegExp("^"+this.prefix)}function c(){return(new Date).getTime()}function d(a){return JSON.stringify(b.isUndefined(a)?null:a)}function e(a){return JSON.parse(a)}var f,g;try{f=window.localStorage,f.setItem("~~~","!"),f.removeItem("~~~")}catch(h){f=null}return g=f&&window.JSON?{_prefix:function(a){return this.prefix+a},_ttlKey:function(a){return this._prefix(a)+this.ttlKey},get:function(a){return this.isExpired(a)&&this.remove(a),e(f.getItem(this._prefix(a)))},set:function(a,e,g){return b.isNumber(g)?f.setItem(this._ttlKey(a),d(c()+g)):f.removeItem(this._ttlKey(a)),f.setItem(this._prefix(a),d(e))},remove:function(a){return f.removeItem(this._ttlKey(a)),f.removeItem(this._prefix(a)),this},clear:function(){var a,b,c=[],d=f.length;for(a=0;d>a;a++)(b=f.key(a)).match(this.keyMatcher)&&c.push(b.replace(this.keyMatcher,""));for(a=c.length;a--;)this.remove(c[a]);return this},isExpired:function(a){var d=e(f.getItem(this._ttlKey(a)));return b.isNumber(d)&&c()>d?!0:!1}}:{get:b.noop,set:b.noop,remove:b.noop,clear:b.noop,isExpired:b.noop},b.mixin(a.prototype,g),a}(),f=function(){function c(b){b=b||{},this._send=b.transport?e(b.transport):a.ajax,this._get=b.rateLimiter?b.rateLimiter(this._get):this._get}function e(c){return function(d,e){function f(a){b.defer(function(){h.resolve(a)})}function g(a){b.defer(function(){h.reject(a)})}var h=a.Deferred();return c(d,e,f,g),h}}var f=0,g={},h=6,i=new d(10);return c.setMaxPendingRequests=function(a){h=a},c.resetCache=function(){i=new d(10)},b.mixin(c.prototype,{_get:function(a,b,c){function d(b){c&&c(b),i.set(a,b)}function e(){f--,delete g[a],k.onDeckRequestArgs&&(k._get.apply(k,k.onDeckRequestArgs),k.onDeckRequestArgs=null)}var j,k=this;(j=g[a])?j.done(d):h>f?(f++,g[a]=this._send(a,b).done(d).always(e)):this.onDeckRequestArgs=[].slice.call(arguments,0)},get:function(a,c,d){var e;return b.isFunction(c)&&(d=c,c={}),(e=i.get(a))?b.defer(function(){d&&d(e)}):this._get(a,c,d),!!e}}),c}(),g=function(){function c(b){b=b||{},b.datumTokenizer&&b.queryTokenizer||a.error("datumTokenizer and queryTokenizer are both required"),this.datumTokenizer=b.datumTokenizer,this.queryTokenizer=b.queryTokenizer,this.datums=[],this.trie=e()}function d(a){return a=b.filter(a,function(a){return!!a}),a=b.map(a,function(a){return a.toLowerCase()})}function e(){return{ids:[],children:{}}}function f(a){for(var b={},c=[],d=0;db[e]?e++:(f.push(a[d]),d++,e++);return f}return b.mixin(c.prototype,{bootstrap:function(a){this.datums=a.datums,this.trie=a.trie},add:function(a){var c=this;a=b.isArray(a)?a:[a],b.each(a,function(a){var f,g;f=c.datums.push(a)-1,g=d(c.datumTokenizer(a)),b.each(g,function(a){var b,d,g;for(b=c.trie,d=a.split("");g=d.shift();)b=b.children[g]||(b.children[g]=e()),b.ids.push(f)})})},get:function(a){var c,e,h=this;return c=d(this.queryTokenizer(a)),b.each(c,function(a){var b,c,d,f;if(e&&0===e.length)return!1;for(b=h.trie,c=a.split("");b&&(d=c.shift());)b=b.children[d];return b&&0===c.length?(f=b.ids.slice(0),void(e=e?g(e,f):f)):(e=[],!1)}),e?b.map(f(e),function(a){return h.datums[a]}):[]},serialize:function(){return{datums:this.datums,trie:this.trie}}}),c}(),h=function(){function d(a){var c=a.local||null;return b.isFunction(c)&&(c=c.call(null)),c}function e(d){var e,f;return f={url:null,thumbprint:"",ttl:864e5,filter:null,ajax:{}},(e=d.prefetch||null)&&(e=b.isString(e)?{url:e}:e,e=b.mixin(f,e),e.thumbprint=c+e.thumbprint,e.ajax.type=e.ajax.type||"GET",e.ajax.dataType=e.ajax.dataType||"json",!e.url&&a.error("prefetch requires url to be set")),e}function f(c){function d(a){return function(c){return b.debounce(c,a)}}function e(a){return function(c){return b.throttle(c,a)}}var f,g;return g={url:null,wildcard:"%QUERY",replace:null,rateLimitBy:"debounce",rateLimitWait:300,send:null,filter:null,ajax:{}},(f=c.remote||null)&&(f=b.isString(f)?{url:f}:f,f=b.mixin(g,f),f.rateLimiter=/^throttle$/i.test(f.rateLimitBy)?e(f.rateLimitWait):d(f.rateLimitWait),f.ajax.type=f.ajax.type||"GET",f.ajax.dataType=f.ajax.dataType||"json",delete f.rateLimitBy,delete f.rateLimitWait,!f.url&&a.error("remote requires url to be set")),f}return{local:d,prefetch:e,remote:f}}(),i=(window.Bloodhound=function(){function c(b){b&&(b.local||b.prefetch||b.remote)||a.error("one of local, prefetch, or remote is required"),this.limit=b.limit||5,this.sorter=d(b.sorter),this.dupDetector=b.dupDetector||i,this.local=h.local(b),this.prefetch=h.prefetch(b),this.remote=h.remote(b),this.cacheKey=this.prefetch?this.prefetch.cacheKey||this.prefetch.url:null,this.index=new g({datumTokenizer:b.datumTokenizer,queryTokenizer:b.queryTokenizer}),this.storage=this.cacheKey?new e(this.cacheKey):null}function d(a){function c(b){return b.sort(a)}function d(a){return a}return b.isFunction(a)?c:d}function i(){return!1}var j;return j={data:"data",protocol:"protocol",thumbprint:"thumbprint"},c.tokenizers={whitespace:function(a){return a.split(/\s+/)},nonword:function(a){return a.split(/\W+/)}},b.mixin(c.prototype,{_loadPrefetch:function(b){function c(a){var c;c=b.filter?b.filter(a):a,f.add(c),f._saveToStorage(f.index.serialize(),b.thumbprint,b.ttl)}var d,e,f=this;return(d=this._readFromStorage(b.thumbprint))?(this.index.bootstrap(d),e=a.Deferred().resolve()):e=a.ajax(b.url,b.ajax).done(c),e},_getFromRemote:function(a,b){function c(a){var c=f.remote.filter?f.remote.filter(a):a;b(c)}var d,e,f=this;return a=a||"",e=encodeURIComponent(a),d=this.remote.replace?this.remote.replace(this.remote.url,a):this.remote.url.replace(this.remote.wildcard,e),this.transport.get(d,this.remote.ajax,c)},_saveToStorage:function(a,b,c){this.storage&&(this.storage.set(j.data,a,c),this.storage.set(j.protocol,location.protocol,c),this.storage.set(j.thumbprint,b,c))},_readFromStorage:function(a){var b,c={};return this.storage&&(c.data=this.storage.get(j.data),c.protocol=this.storage.get(j.protocol),c.thumbprint=this.storage.get(j.thumbprint)),b=c.thumbprint!==a||c.protocol!==location.protocol,c.data&&!b?c.data:null},initialize:function(){function b(){d.add(d.local)}var c,d=this;return c=this.prefetch?this._loadPrefetch(this.prefetch):a.Deferred().resolve(),this.local&&c.done(b),this.transport=this.remote?new f(this.remote):null,this.initialize=function(){return c.promise()},c.promise()},add:function(a){this.index.add(a)},get:function(a,c){function d(a){var d=e.slice(0);b.each(a,function(a){var c;return c=b.some(d,function(b){return f.dupDetector(a,b)}),!c&&d.push(a),d.length