├── .eslintrc ├── .github ├── issue_template.md └── pull_request_template.md ├── .gitignore ├── GPL-LICENSE ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.MD ├── css ├── jquery.multiselect.css └── jquery.multiselect.filter.css ├── docs ├── _config.yml ├── _includes │ ├── demo-styles.html │ └── jquery-ui.html ├── _layouts │ └── default.html ├── animations.html ├── basic.html ├── callbacks.html ├── css │ ├── jquery.multiselect.css │ ├── jquery.multiselect.filter.css │ ├── prettify.css │ └── style.css ├── enabledisable.html ├── filter.html ├── headers.html ├── index.html ├── js │ ├── prettify.js │ └── widget.min.js ├── maxchecked.html ├── position.html ├── preselected.html ├── refresh.html ├── selectedlist.html └── single.html ├── i18n ├── jquery.multiselect.br.js ├── jquery.multiselect.cs.js ├── jquery.multiselect.de.js ├── jquery.multiselect.es.js ├── jquery.multiselect.filter.br.js ├── jquery.multiselect.filter.cs.js ├── jquery.multiselect.filter.de.js ├── jquery.multiselect.filter.es.js ├── jquery.multiselect.filter.fr.js ├── jquery.multiselect.filter.hu.js ├── jquery.multiselect.filter.it.js ├── jquery.multiselect.filter.ja.js ├── jquery.multiselect.filter.pl.js ├── jquery.multiselect.filter.ru.js ├── jquery.multiselect.filter.tr.js ├── jquery.multiselect.filter.zh-cn.js ├── jquery.multiselect.filter.zh-tw.js ├── jquery.multiselect.fr.js ├── jquery.multiselect.hu.js ├── jquery.multiselect.it.js ├── jquery.multiselect.ja.js ├── jquery.multiselect.pl.js ├── jquery.multiselect.ru.js ├── jquery.multiselect.tr.js ├── jquery.multiselect.zh-cn.js └── jquery.multiselect.zh-tw.js ├── package-lock.json ├── package.json ├── src ├── jquery.multiselect.filter.js └── jquery.multiselect.js └── tests ├── unit ├── core.js ├── events.js ├── filter.js ├── html.js ├── index.htm ├── methods.js ├── options.js └── setup.js └── visual ├── form-reset.htm ├── formsubmission-single.php ├── formsubmission.cfm ├── formsubmission.htm ├── formsubmission.php ├── style.css └── widget-containers.htm /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "google", 3 | "parserOptions": { 4 | "ecmaVersion": 5 5 | }, 6 | "rules": { 7 | "semi": "error", 8 | "no-var": "off", 9 | "max-len": "off", 10 | }, 11 | "env": { 12 | "es6": false 13 | } 14 | } -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### What is the current behavior? 2 | 3 | 4 | 5 | ### What is the expected behavior? 6 | 7 | 8 | 9 | ### If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo 10 | _Common options for demos are [jsfiddle](https://jsfiddle.net/) and [codepen](https://codepen.io)_ 11 | 12 | 13 | 14 | ### If you are requesting a new or changed feature, please provide a rationale 15 | 16 | 17 | ### Will you submit a corresponding Pull Request to fix the bug or implement the feature? 18 | 19 | 20 | ### Please tell us about your setup 21 | 22 | * Version: 23 | * Browser: 24 | * Other frameworks in use: 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### What changes are you proposing? Why are they needed? 2 | 3 | 4 | ### Related Issue numbers 5 | 6 | 7 | 8 | ### Pull Request Approval Checklist: 9 | - [ ] Tests Ran and 0 Failing 10 | - [ ] Tests Added/Updated 11 | - [ ] Impacted Demos Updated 12 | - [ ] Impacted i18n Code Updated 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | docs/public 3 | docs/_site 4 | docs/.sass-cache 5 | .vscode 6 | node_modules 7 | tests/unit/debug.log 8 | -------------------------------------------------------------------------------- /GPL-LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | a 280 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'github-pages', group: :jekyll_plugins 3 | 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (4.2.9) 5 | i18n (~> 0.7) 6 | minitest (~> 5.1) 7 | thread_safe (~> 0.3, >= 0.3.4) 8 | tzinfo (~> 1.1) 9 | addressable (2.5.2) 10 | public_suffix (>= 2.0.2, < 4.0) 11 | coffee-script (2.4.1) 12 | coffee-script-source 13 | execjs 14 | coffee-script-source (1.11.1) 15 | colorator (1.1.0) 16 | commonmarker (0.17.9) 17 | ruby-enum (~> 0.5) 18 | concurrent-ruby (1.0.5) 19 | dnsruby (1.60.2) 20 | em-websocket (0.5.1) 21 | eventmachine (>= 0.12.9) 22 | http_parser.rb (~> 0.6.0) 23 | ethon (0.11.0) 24 | ffi (>= 1.3.0) 25 | eventmachine (1.2.7) 26 | execjs (2.7.0) 27 | faraday (0.15.1) 28 | multipart-post (>= 1.2, < 3) 29 | ffi (1.9.23) 30 | forwardable-extended (2.6.0) 31 | gemoji (3.0.0) 32 | github-pages (185) 33 | activesupport (= 4.2.9) 34 | github-pages-health-check (= 1.8.1) 35 | jekyll (= 3.7.3) 36 | jekyll-avatar (= 0.5.0) 37 | jekyll-coffeescript (= 1.1.1) 38 | jekyll-commonmark-ghpages (= 0.1.5) 39 | jekyll-default-layout (= 0.1.4) 40 | jekyll-feed (= 0.9.3) 41 | jekyll-gist (= 1.5.0) 42 | jekyll-github-metadata (= 2.9.4) 43 | jekyll-mentions (= 1.3.0) 44 | jekyll-optional-front-matter (= 0.3.0) 45 | jekyll-paginate (= 1.1.0) 46 | jekyll-readme-index (= 0.2.0) 47 | jekyll-redirect-from (= 0.13.0) 48 | jekyll-relative-links (= 0.5.3) 49 | jekyll-remote-theme (= 0.3.1) 50 | jekyll-sass-converter (= 1.5.2) 51 | jekyll-seo-tag (= 2.4.0) 52 | jekyll-sitemap (= 1.2.0) 53 | jekyll-swiss (= 0.4.0) 54 | jekyll-theme-architect (= 0.1.1) 55 | jekyll-theme-cayman (= 0.1.1) 56 | jekyll-theme-dinky (= 0.1.1) 57 | jekyll-theme-hacker (= 0.1.1) 58 | jekyll-theme-leap-day (= 0.1.1) 59 | jekyll-theme-merlot (= 0.1.1) 60 | jekyll-theme-midnight (= 0.1.1) 61 | jekyll-theme-minimal (= 0.1.1) 62 | jekyll-theme-modernist (= 0.1.1) 63 | jekyll-theme-primer (= 0.5.3) 64 | jekyll-theme-slate (= 0.1.1) 65 | jekyll-theme-tactile (= 0.1.1) 66 | jekyll-theme-time-machine (= 0.1.1) 67 | jekyll-titles-from-headings (= 0.5.1) 68 | jemoji (= 0.9.0) 69 | kramdown (= 1.16.2) 70 | liquid (= 4.0.0) 71 | listen (= 3.1.5) 72 | mercenary (~> 0.3) 73 | minima (= 2.4.1) 74 | nokogiri (>= 1.8.1, < 2.0) 75 | rouge (= 2.2.1) 76 | terminal-table (~> 1.4) 77 | github-pages-health-check (1.8.1) 78 | addressable (~> 2.3) 79 | dnsruby (~> 1.60) 80 | octokit (~> 4.0) 81 | public_suffix (~> 2.0) 82 | typhoeus (~> 1.3) 83 | html-pipeline (2.8.0) 84 | activesupport (>= 2) 85 | nokogiri (>= 1.4) 86 | http_parser.rb (0.6.0) 87 | i18n (0.9.5) 88 | concurrent-ruby (~> 1.0) 89 | jekyll (3.7.3) 90 | addressable (~> 2.4) 91 | colorator (~> 1.0) 92 | em-websocket (~> 0.5) 93 | i18n (~> 0.7) 94 | jekyll-sass-converter (~> 1.0) 95 | jekyll-watch (~> 2.0) 96 | kramdown (~> 1.14) 97 | liquid (~> 4.0) 98 | mercenary (~> 0.3.3) 99 | pathutil (~> 0.9) 100 | rouge (>= 1.7, < 4) 101 | safe_yaml (~> 1.0) 102 | jekyll-avatar (0.5.0) 103 | jekyll (~> 3.0) 104 | jekyll-coffeescript (1.1.1) 105 | coffee-script (~> 2.2) 106 | coffee-script-source (~> 1.11.1) 107 | jekyll-commonmark (1.2.0) 108 | commonmarker (~> 0.14) 109 | jekyll (>= 3.0, < 4.0) 110 | jekyll-commonmark-ghpages (0.1.5) 111 | commonmarker (~> 0.17.6) 112 | jekyll-commonmark (~> 1) 113 | rouge (~> 2) 114 | jekyll-default-layout (0.1.4) 115 | jekyll (~> 3.0) 116 | jekyll-feed (0.9.3) 117 | jekyll (~> 3.3) 118 | jekyll-gist (1.5.0) 119 | octokit (~> 4.2) 120 | jekyll-github-metadata (2.9.4) 121 | jekyll (~> 3.1) 122 | octokit (~> 4.0, != 4.4.0) 123 | jekyll-mentions (1.3.0) 124 | activesupport (~> 4.0) 125 | html-pipeline (~> 2.3) 126 | jekyll (~> 3.0) 127 | jekyll-optional-front-matter (0.3.0) 128 | jekyll (~> 3.0) 129 | jekyll-paginate (1.1.0) 130 | jekyll-readme-index (0.2.0) 131 | jekyll (~> 3.0) 132 | jekyll-redirect-from (0.13.0) 133 | jekyll (~> 3.3) 134 | jekyll-relative-links (0.5.3) 135 | jekyll (~> 3.3) 136 | jekyll-remote-theme (0.3.1) 137 | jekyll (~> 3.5) 138 | rubyzip (>= 1.2.1, < 3.0) 139 | jekyll-sass-converter (1.5.2) 140 | sass (~> 3.4) 141 | jekyll-seo-tag (2.4.0) 142 | jekyll (~> 3.3) 143 | jekyll-sitemap (1.2.0) 144 | jekyll (~> 3.3) 145 | jekyll-swiss (0.4.0) 146 | jekyll-theme-architect (0.1.1) 147 | jekyll (~> 3.5) 148 | jekyll-seo-tag (~> 2.0) 149 | jekyll-theme-cayman (0.1.1) 150 | jekyll (~> 3.5) 151 | jekyll-seo-tag (~> 2.0) 152 | jekyll-theme-dinky (0.1.1) 153 | jekyll (~> 3.5) 154 | jekyll-seo-tag (~> 2.0) 155 | jekyll-theme-hacker (0.1.1) 156 | jekyll (~> 3.5) 157 | jekyll-seo-tag (~> 2.0) 158 | jekyll-theme-leap-day (0.1.1) 159 | jekyll (~> 3.5) 160 | jekyll-seo-tag (~> 2.0) 161 | jekyll-theme-merlot (0.1.1) 162 | jekyll (~> 3.5) 163 | jekyll-seo-tag (~> 2.0) 164 | jekyll-theme-midnight (0.1.1) 165 | jekyll (~> 3.5) 166 | jekyll-seo-tag (~> 2.0) 167 | jekyll-theme-minimal (0.1.1) 168 | jekyll (~> 3.5) 169 | jekyll-seo-tag (~> 2.0) 170 | jekyll-theme-modernist (0.1.1) 171 | jekyll (~> 3.5) 172 | jekyll-seo-tag (~> 2.0) 173 | jekyll-theme-primer (0.5.3) 174 | jekyll (~> 3.5) 175 | jekyll-github-metadata (~> 2.9) 176 | jekyll-seo-tag (~> 2.0) 177 | jekyll-theme-slate (0.1.1) 178 | jekyll (~> 3.5) 179 | jekyll-seo-tag (~> 2.0) 180 | jekyll-theme-tactile (0.1.1) 181 | jekyll (~> 3.5) 182 | jekyll-seo-tag (~> 2.0) 183 | jekyll-theme-time-machine (0.1.1) 184 | jekyll (~> 3.5) 185 | jekyll-seo-tag (~> 2.0) 186 | jekyll-titles-from-headings (0.5.1) 187 | jekyll (~> 3.3) 188 | jekyll-watch (2.0.0) 189 | listen (~> 3.0) 190 | jemoji (0.9.0) 191 | activesupport (~> 4.0, >= 4.2.9) 192 | gemoji (~> 3.0) 193 | html-pipeline (~> 2.2) 194 | jekyll (~> 3.0) 195 | kramdown (1.16.2) 196 | liquid (4.0.0) 197 | listen (3.1.5) 198 | rb-fsevent (~> 0.9, >= 0.9.4) 199 | rb-inotify (~> 0.9, >= 0.9.7) 200 | ruby_dep (~> 1.2) 201 | mercenary (0.3.6) 202 | mini_portile2 (2.3.0) 203 | minima (2.4.1) 204 | jekyll (~> 3.5) 205 | jekyll-feed (~> 0.9) 206 | jekyll-seo-tag (~> 2.1) 207 | minitest (5.11.3) 208 | multipart-post (2.0.0) 209 | nokogiri (1.8.2) 210 | mini_portile2 (~> 2.3.0) 211 | octokit (4.9.0) 212 | sawyer (~> 0.8.0, >= 0.5.3) 213 | pathutil (0.16.1) 214 | forwardable-extended (~> 2.6) 215 | public_suffix (2.0.5) 216 | rb-fsevent (0.10.3) 217 | rb-inotify (0.9.10) 218 | ffi (>= 0.5.0, < 2) 219 | rouge (2.2.1) 220 | ruby-enum (0.7.2) 221 | i18n 222 | ruby_dep (1.5.0) 223 | rubyzip (1.2.1) 224 | safe_yaml (1.0.4) 225 | sass (3.5.6) 226 | sass-listen (~> 4.0.0) 227 | sass-listen (4.0.0) 228 | rb-fsevent (~> 0.9, >= 0.9.4) 229 | rb-inotify (~> 0.9, >= 0.9.7) 230 | sawyer (0.8.1) 231 | addressable (>= 2.3.5, < 2.6) 232 | faraday (~> 0.8, < 1.0) 233 | terminal-table (1.8.0) 234 | unicode-display_width (~> 1.1, >= 1.1.1) 235 | thread_safe (0.3.6) 236 | typhoeus (1.3.0) 237 | ethon (>= 0.9.0) 238 | tzinfo (1.2.5) 239 | thread_safe (~> 0.1) 240 | unicode-display_width (1.3.2) 241 | 242 | PLATFORMS 243 | ruby 244 | 245 | DEPENDENCIES 246 | github-pages 247 | 248 | BUNDLED WITH 249 | 2.1.2 250 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Eric Hynds 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # jQuery UI MultiSelect Widget 2 | 3 | MultiSelect progessively enhances an ordinary multiple select control into elegant drop down list of checkboxes, stylable with ThemeRoller. 4 | 5 | ## Demo Page 6 | 7 | Check out the demo page [here!](https://ehynds.github.io/jquery-ui-multiselect-widget/) 8 | 9 | ## Version 3 10 | 11 | Version 3 of the widget is available. This introduces a lot of new features and substantial performance imrovements. 12 | 13 | Check out the [release notes](https://github.com/ehynds/jquery-ui-multiselect-widget/releases/tag/3.0.0) for an exact list of changes and see the [wiki](https://github.com/ehynds/jquery-ui-multiselect-widget/wiki/Migrating-to-Version-3) for the information you may need to switch over. The main wiki will be getting updates for the new features and changed options. 14 | 15 | ## Requirements 16 | 17 | The [usage](https://github.com/ehynds/jquery-ui-multiselect-widget/wiki/Usage) section of the Wiki specifies the widget's dependencies. 18 | 19 | ## License 20 | 21 | MultiSelect is dual-licensed under the [GPL 2 license](https://github.com/ehynds/jquery-ui-multiselect-widget/blob/master/GPL-LICENSE) and the [MIT license](https://github.com/ehynds/jquery-ui-multiselect-widget/blob/master/MIT-LICENSE). 22 | 23 | ## Contributing 24 | 25 | When submitting a pull request, please describe the change you are making - preferably with a use case. Unit tests are now (14 March 2016) required for the pull to be merged. 26 | 27 | Please do not submit minified code in your pull request, that tends to cause merge conflicts. 28 | 29 | You can test the demo pages locally by navigating to the `docs` directory and running `bundle install && bundle exec jekyll server`. 30 | -------------------------------------------------------------------------------- /css/jquery.multiselect.css: -------------------------------------------------------------------------------- 1 | .ui-multiselect {box-sizing: border-box; padding:2px 0 2px 4px; text-align:left; width: auto;} 2 | .ui-multiselect .ui-multiselect-open { float:right } 3 | 4 | .ui-multiselect-menu { display:none; box-sizing:border-box; position:absolute; text-align:left; z-index: 101; width:auto; height:auto; padding:3px; } 5 | .ui-multiselect-menu.ui-multiselect-listbox {position:relative; z-index: 0;} 6 | 7 | .ui-multiselect-header { display:block; box-sizing:border-box; position:relative; width:auto; padding:3px 0 3px 4px; margin-bottom:2px;} 8 | .ui-multiselect-header > ul { font-size:0.9em } 9 | .ui-multiselect-header li { float:left; margin:0 10px 0 0;} 10 | .ui-multiselect-header a { text-decoration:none; } 11 | .ui-multiselect-header a:hover { text-decoration:underline; cursor: pointer;} 12 | .ui-multiselect-header .ui-icon { float:left; } 13 | .ui-multiselect-header .ui-multiselect-close { float:right; margin-right:0; text-align:right; } 14 | 15 | .ui-multiselect-checkboxes { display:block; box-sizing:border-box; position:relative; overflow:auto; width: auto; border: 0; padding: 4px 0 8px;} 16 | .ui-multiselect-checkboxes li:not(.ui-multiselect-optgroup) { clear:both; font-size:0.9em; list-style: none; padding-right:3px;} 17 | .ui-multiselect-checkboxes label { border:1px solid transparent; cursor:default; display:block; padding:3px 1px 3px 21px; text-indent: -20px;} 18 | .ui-multiselect-checkboxes input { position:relative; top:1px; cursor: pointer;} 19 | .ui-multiselect-checkboxes img { height: 30px; vertical-align: middle; margin-right: 3px;} 20 | .ui-multiselect-grouplabel { border-bottom:1px solid; display:block; font-weight:bold; margin:1px 0; padding:3px; text-align:center; text-decoration:none; } 21 | .ui-multiselect-selectable { cursor: pointer; } 22 | .ui-multiselect-optgroup > ul { padding: 3px; } 23 | .ui-multiselect-columns { display: inline-block; vertical-align: top; } 24 | .ui-multiselect-collapser { float: left; padding: 0 1px; margin: 0; } 25 | .ui-multiselect-collapsed > ul { display:none } 26 | 27 | .ui-multiselect-single .ui-multiselect-checkboxes input { left:-9999px; position:absolute !important; top: auto !important; } 28 | .ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important; text-indent: 0 !important; } 29 | 30 | .ui-multiselect.ui-multiselect-nowrap { white-space: nowrap } 31 | .ui-multiselect.ui-multiselect-nowrap > span { display: inline-block } 32 | .ui-multiselect-checkboxes.ui-multiselect-nowrap li, 33 | .ui-multiselect-checkboxes.ui-multiselect-nowrap a { white-space: nowrap } 34 | 35 | .ui-multiselect-measure > .ui-multiselect-header, 36 | .ui-multiselect-measure > .ui-multiselect-checkboxes { float: left; } 37 | .ui-multiselect-measure > .ui-multiselect-checkboxes { margin: 4px; overflow-y: scroll; } 38 | 39 | .ui-multiselect-resize { border: 2px dotted #00F } 40 | 41 | @media print{ 42 | .ui-multiselect-menu {display: none;} 43 | } 44 | -------------------------------------------------------------------------------- /css/jquery.multiselect.filter.css: -------------------------------------------------------------------------------- 1 | .ui-multiselect-hasfilter ul { position:relative; top:2px } 2 | .ui-multiselect-filter { float:left; margin-right:10px; font-size:1em; width:100%; } 3 | .ui-multiselect-header .ui-multiselect-filter input { width:100px; font-size:.9em; margin-left:5px; height:15px; padding:2px; border:1px solid #292929; -webkit-appearance:textfield; -webkit-box-sizing:content-box; } 4 | .ui-multiselect-excluded {display: none} 5 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process. 10 | 11 | # Site settings 12 | # These are used to personalize your new site. If you look in the HTML files, 13 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 14 | # You can create any custom variable you would like, and they will be accessible 15 | # in the templates via {{ site.myvariable }}. 16 | title: jQuery Multiselect Widget 17 | email: osiris2918@gmail.com 18 | description: Documentation site for jquery-ui-multiselect-widget. A widget for jQuery UI. 19 | 20 | baseurl: "" # the subpath of your site, e.g. /blog 21 | url: "https://ehynds.github.io/jquery-ui-multiselect-widget" # the base hostname & protocol for your site, e.g. http://example.com 22 | github_username: mlh758 23 | repository: "https://github.com/ehynds/jquery-ui-multiselect-widget/" 24 | # Build settings 25 | markdown: kramdown 26 | theme: minima 27 | # Exclude from processing. 28 | # The following items will not be processed, by default. Create a custom list 29 | # to override the default setting. 30 | exclude: 31 | - Gemfile 32 | - Gemfile.lock 33 | - node_modules 34 | - vendor/bundle/ 35 | - vendor/cache/ 36 | - vendor/gems/ 37 | - vendor/ruby/ 38 | - src/assets/bundle.js 39 | -------------------------------------------------------------------------------- /docs/_includes/demo-styles.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_includes/jquery-ui.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include jquery-ui.html %} 5 | 6 | 7 | 8 | 9 | {{ content }} 10 | 11 | 23 | 24 | -------------------------------------------------------------------------------- /docs/animations.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 19 | 20 | 21 |

Show/hide with Animation

22 |

Using animations with the openEffect and closeEffect parameters. Either pass an array with the effect name and the speed, an object of animation parameters, or just specify the name of an effect. If you don't specify a speed, the default of 400ms will be used.

23 | 24 |

Specifying different show and hide speeds

25 |
26 | $("#test-1").multiselect({
27 |    openEffect: ["bounce", 200],
28 |    closeEffect: ["explode", 1000]
29 | });
30 | 
31 | 38 | 39 |

Only passing the name of an effect

40 |
41 | $("#test-2").multiselect({
42 |    openEffect: "bounce",
43 |    closeEffect: "explode"
44 | });
45 | 
46 | 53 | 54 | -------------------------------------------------------------------------------- /docs/basic.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 9 |

Basic Demos

10 | 11 |

Both multiselects are created with the following one-liner. Optgroup support is built in out of the box:

12 |
13 | $(function(){
14 |    $("select").multiselect(); 
15 | });
16 | 
17 | 18 |

Basic

19 |

20 | 34 |

35 | 36 |

With Optgroups

37 |

Click on an optgroup's heading to toggle the checked state of the entire group.

38 |

39 | 52 |

53 | -------------------------------------------------------------------------------- /docs/callbacks.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 49 |

Callbacks & Events

50 |

Demonstrating beforeopen, open, beforeclose, close, click, checkall, uncheckall, and optgrouptoggle callbacks/events. Note that you can either 51 | pass in an event handler in the options object upon initialization, or bind to the event (including the multiselect prefix). For example, to add an 52 | "open" handler, you can also use $("select").bind("multiselectopen", fn);

53 |

Callback target

54 | 55 | 68 | 69 |
 70 | var $callback = $("#callback");
 71 | 
 72 | $("select").multiselect({
 73 |    click: function(event, ui){
 74 |       $callback.text(ui.value + ' ' + (ui.checked ? 'checked' : 'unchecked') );
 75 |    },
 76 |    beforeopen: function(){
 77 |       $callback.text("Select about to be opened...");
 78 |    },
 79 |    open: function(){
 80 |       $callback.text("Select opened!");
 81 |    },
 82 |    beforeclose: function(){
 83 |       $callback.text("Select about to be closed...");
 84 |    },
 85 |    close: function(){
 86 |       $callback.text("Select closed!");
 87 |    },
 88 |    checkAll: function(){
 89 |       $callback.text("Check all clicked!");
 90 |    },
 91 |    uncheckAll: function(){
 92 |       $callback.text("Uncheck all clicked!");
 93 |    },
 94 |    optgrouptoggle: function(event, ui){
 95 |       var values = $.map(ui.inputs, function(checkbox){
 96 |          return checkbox.value;
 97 |       }).join(", ");
 98 | 
 99 |       $callback.html("Checkboxes " + (ui.checked ? "checked" : "unchecked") + ": " + values);
100 |    },
101 |    groupsCollapsable: true,
102 |    beforecollapsetoggle: function(){
103 |       $callback.text("Option group about to be collapsed/expanded...");
104 |    },
105 |    collapsetoggle: function(){
106 |       $callback.text("Option group collapsed/expanded");
107 |    }
108 | });
109 | 
110 | -------------------------------------------------------------------------------- /docs/css/jquery.multiselect.css: -------------------------------------------------------------------------------- 1 | .ui-multiselect {box-sizing: border-box; padding:2px 0 2px 4px; text-align:left; width: auto;} 2 | .ui-multiselect .ui-multiselect-open { float:right } 3 | 4 | .ui-multiselect-menu { display:none; box-sizing:border-box; position:absolute; text-align:left; z-index: 101; width:auto; height:auto; padding:3px; } 5 | .ui-multiselect-menu.ui-multiselect-listbox {position:relative; z-index: 0;} 6 | 7 | .ui-multiselect-header { display:block; box-sizing:border-box; position:relative; width:auto; padding:3px 0 3px 4px; margin-bottom:2px;} 8 | .ui-multiselect-header > ul { font-size:0.9em } 9 | .ui-multiselect-header li { float:left; margin:0 10px 0 0;} 10 | .ui-multiselect-header a { text-decoration:none; } 11 | .ui-multiselect-header a:hover { text-decoration:underline; cursor: pointer;} 12 | .ui-multiselect-header .ui-icon { float:left; } 13 | .ui-multiselect-header .ui-multiselect-close { float:right; margin-right:0; text-align:right; } 14 | 15 | .ui-multiselect-checkboxes { display:block; box-sizing:border-box; position:relative; overflow:auto; width: auto; border: 0; padding: 4px 0 8px;} 16 | .ui-multiselect-checkboxes li:not(.ui-multiselect-optgroup) { clear:both; font-size:0.9em; list-style: none; padding-right:3px;} 17 | .ui-multiselect-checkboxes label { border:1px solid transparent; cursor:default; display:block; padding:3px 1px 3px 21px; text-indent: -20px;} 18 | .ui-multiselect-checkboxes input { position:relative; top:1px; cursor: pointer;} 19 | .ui-multiselect-checkboxes img { height: 30px; vertical-align: middle; margin-right: 3px;} 20 | .ui-multiselect-grouplabel { border-bottom:1px solid; display:block; font-weight:bold; margin:1px 0; padding:3px; text-align:center; text-decoration:none; } 21 | .ui-multiselect-selectable { cursor: pointer; } 22 | .ui-multiselect-optgroup > ul { padding: 3px; } 23 | .ui-multiselect-columns { display: inline-block; vertical-align: top; } 24 | .ui-multiselect-collapser { float: left; padding: 0 1px; margin: 0; } 25 | .ui-multiselect-collapsed > ul { display:none } 26 | 27 | .ui-multiselect-single .ui-multiselect-checkboxes input { left:-9999px; position:absolute !important; top: auto !important; } 28 | .ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important; text-indent: 0 !important; } 29 | 30 | .ui-multiselect.ui-multiselect-nowrap { white-space: nowrap } 31 | .ui-multiselect.ui-multiselect-nowrap > span { display: inline-block } 32 | .ui-multiselect-checkboxes.ui-multiselect-nowrap li, 33 | .ui-multiselect-checkboxes.ui-multiselect-nowrap a { white-space: nowrap } 34 | 35 | .ui-multiselect-measure > .ui-multiselect-header, 36 | .ui-multiselect-measure > .ui-multiselect-checkboxes { float: left; } 37 | .ui-multiselect-measure > .ui-multiselect-checkboxes { margin: 4px; overflow-y: scroll; } 38 | 39 | .ui-multiselect-resize { border: 2px dotted #00F } 40 | 41 | @media print{ 42 | .ui-multiselect-menu {display: none;} 43 | } 44 | -------------------------------------------------------------------------------- /docs/css/jquery.multiselect.filter.css: -------------------------------------------------------------------------------- 1 | .ui-multiselect-hasfilter ul { position:relative; top:2px } 2 | .ui-multiselect-filter { float:left; margin-right:10px; font-size:1em; width:100%; } 3 | .ui-multiselect-header .ui-multiselect-filter input { width:100px; font-size:.9em; margin-left:5px; height:15px; padding:2px; border:1px solid #292929; -webkit-appearance:textfield; -webkit-box-sizing:content-box; } 4 | .ui-multiselect-excluded {display: none} 5 | -------------------------------------------------------------------------------- /docs/css/prettify.css: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. */ 2 | 3 | .str { color: #080; } 4 | .kwd { color: #008; } 5 | .com { color: #800; } 6 | .typ { color: #606; } 7 | .lit { color: #066; } 8 | .pun { color: #660; } 9 | .pln { color: #000; } 10 | .tag { color: #008; } 11 | .atn { color: #606; } 12 | .atv { color: #080; } 13 | .dec { color: #606; } 14 | pre.prettyprint { padding: 5px; border:1px solid #d2d2d2; background:#f5f5f5 } 15 | 16 | @media print { 17 | .str { color: #060; } 18 | .kwd { color: #006; font-weight: bold; } 19 | .com { color: #600; font-style: italic; } 20 | .typ { color: #404; font-weight: bold; } 21 | .lit { color: #044; } 22 | .pun { color: #440; } 23 | .pln { color: #000; } 24 | .tag { color: #006; font-weight: bold; } 25 | .atn { color: #404; } 26 | .atv { color: #060; } 27 | } -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- 1 | body { font:12px Helvetica, arial, sans-serif } 2 | h1, h2, p { margin:10px 0 } 3 | .hidden { visibility:hidden } 4 | 5 | 6 | .message { padding:10px; margin:15px 0; display:block; text-align:left } 7 | .message-title { font-weight:bold; font-size:1.25em } 8 | .message-body { margin-top:4px } 9 | .error, .notice, .success { padding:.8em; margin-bottom:1em; border:2px solid #ddd } 10 | .error { background:#FBE3E4; color:#8a1f11; border-color:#FBC2C4 } 11 | .notice { background:#FFF6BF; color:#514721; border-color:#FFD324 } 12 | .success { background:#E6EFC2; color:#264409; border-color:#C6D880 } 13 | .error a { color:#8a1f11 } 14 | .notice a { color:#514721 } 15 | .success a { color:#264409 } 16 | 17 | -------------------------------------------------------------------------------- /docs/enabledisable.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 15 | 16 |

Enable/Disable Widget

17 |

Programmatically calling the disable and enable methods.

18 |
19 | var $widget = $("select").multiselect(), 
20 |     state = true;
21 | 
22 | $("#toggle-disabled").click(function(){
23 |    state = !state;
24 |    $widget.multiselect(state ? 'disable' : 'enable');
25 | });
26 | 
27 | 28 | 29 | 36 | 37 |

38 | -------------------------------------------------------------------------------- /docs/filter.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |

Filter Plugin

5 | 6 |

Filtering is available by including the jquery.multiselect.filter.js plugin and the jquery.multiselect.filter.css CSS file. Initialize filtering on any of your multiselects by calling multiselectfilter() on the widget.

7 | 8 |
  9 | $("select").multiselect().multiselectfilter();
 10 | 
11 | 12 |
13 |

14 | 24 |

25 |

26 | 40 |

41 |
42 | 43 |

Options:

44 | 45 |

Pass any of these as a configuration object when you initialize multiselectfilter():

46 | 52 | 53 |

Events:

54 | 92 | 93 |

Methods

94 |

Syntax: $("select").multiselectfilter("method_name");

95 | 100 | 101 | 104 | -------------------------------------------------------------------------------- /docs/headers.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 24 | 25 |

Headers

26 |

The header option can be used in three ways:

27 | 28 |
29 |

header: ['checkAll','uncheckAll'] (default)

30 | 39 | 40 |

header: false

41 | 50 | 51 |

header: "Choose options below"

52 | 61 |
62 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | {% include demo-styles.html %} 5 |
6 |

jQuery UI MultiSelect Widget Demos

7 | 8 |

9 | Download 10 | | 11 | npm 12 |

13 | 14 | 52 | 53 |
54 | 55 | 58 |
59 |
60 | -------------------------------------------------------------------------------- /docs/js/widget.min.js: -------------------------------------------------------------------------------- 1 | (function($,undefined){var multiselectID=0;var linkDefaults={open:{class:"ui-multiselect-open",icon:'',title:"Close"},checkAll:{class:"ui-multiselect-all",icon:'',text:"Check all",title:"Check all"},uncheckAll:{class:"ui-multiselect-none",icon:'',text:"Uncheck all",title:"Uncheck all"},flipAll:{class:"ui-multiselect-flip",icon:'',text:"Flip all",title:"Flip all"},collapse:{icon:'',title:"Collapse"},expand:{icon:'',title:"Expand"},collapseAll:{class:"ui-multiselect-collapseall",icon:'',text:"Collapse all",title:"Collapse all"},expandAll:{class:"ui-multiselect-expandall",icon:'',text:"Expand all",title:"Expand all"}};function insertImage(option,span){var optionImageSrc=option.getAttribute("data-image-src");if(optionImageSrc){var img=document.createElement("img");img.setAttribute("src",optionImageSrc);span.insertBefore(img,span.firstChild)}}function determineFontSize(){if(window.getComputedStyle){return getComputedStyle(document.body).fontSize}return"16px"}function getjQueryFromElement(elem){if(!!elem.jquery){return elem}if(!!elem.nodeType){return $(elem)}return $(elem).eq(0)}function parse2px(dimText,$elem,isHeight){if(typeof dimText!=="string"){return{px:dimText,minimax:0}}var parts=dimText.match(/([<>])?=?\s*([.\d]+)\s*([eimnptx%]*)s?/i);var minimax=parts[1];var value=parseFloat(parts[2]);var unit=parts[3].toLowerCase();var pixels=-1;switch(unit){case"pt":case"in":case"cm":case"mm":pixels={pt:4/3,in:96,cm:96/2.54,mm:96/25.4}[unit]*value;break;case"em":pixels=parseFloat(determineFontSize())*value;break;case"%":if(!!$elem){if(typeof $elem==="string"||!$elem.jquery){$elem=$($elem)}pixels=(!!isHeight?$elem.parent().height():$elem.parent().width())*(value/100)}break;default:pixels=value}return{px:pixels,minimax:minimax==">"?-1:minimax=="<"?1:0}}$.widget("ech.multiselect",{options:{buttonWidth:225,menuWidth:null,menuHeight:200,resizableMenu:false,appendTo:null,position:{},zIndex:null,classes:"",header:["checkAll","uncheckAll"],linkInfo:null,noneSelectedText:"Select options",selectedText:"# of # selected",selectedList:0,selectedListSeparator:", ",maxSelected:null,openEffect:null,closeEffect:null,autoOpen:false,htmlText:[],wrapText:["button","header","options"],listbox:false,addInputNames:true,disableInputsOnToggle:true,groupsSelectable:true,groupsCollapsable:false,groupColumns:false,groupColumnsWidth:false},_getAppendEl:function(){var elem=this.options.appendTo;if(elem){elem=getjQueryFromElement(elem)}if(!elem||!elem[0]){elem=this.element.closest(".ui-front, dialog")}if(!elem.length){elem=$(document.body)}return elem},_buildButton:function(){var wrapText=this.options.wrapText||[];var $button=(this.$button=$(document.createElement("button"))).addClass("ui-multiselect ui-widget ui-state-default ui-corner-all"+(wrapText.indexOf("button")>-1?"":" ui-multiselect-nowrap")+(this.options.classes?" "+this.options.classes:"")).attr({type:"button",title:this.element[0].title,tabIndex:this.element[0].tabIndex,id:this.element[0].id?this.element[0].id+"_ms":null}).prop("aria-haspopup",true).html(this._linkHTML('{{icon}}',"open"));this.$buttonlabel=$(document.createElement("span")).html(this.options.noneSelectedText||this.element[0].placeholder).appendTo($button);return $button},_buildHeaderHtml:function(){if(!this.options.header){return""}if(typeof this.options.header==="string"){return"
  • "+this.options.header+"
  • "}var headerLinksHTML="";if(this.options.header.constructor==Array){for(var x=0;x{{icon}}{{text}}',linkInfoKey)}}}if(this.options.header.constructor==Object){var options=Object.keys(this.options.header);for(var x=0;x{{icon}}'+displayText+"",linkInfoKey)}}}return headerLinksHTML},_create:function(){var $element=this.element;var options=this.options;this.linkInfo=$.extend(true,{},linkDefaults,options.linkInfo||{});this._selectWidth=$element.outerWidth();$element.hide();options.htmlText=options.htmlText||[];var wrapText=options.wrapText=options.wrapText||[];this.speed=$.fx.speeds._default;this._isOpen=false;this._namespaceID=this.eventNamespace;this.multiselectID=multiselectID++;this.$headerLinkContainer=$(document.createElement("ul")).addClass("ui-helper-reset").html(this._buildHeaderHtml()+(!options.listbox?this._linkHTML('
  • {{icon}}
  • ',"close"):""));var $header=(this.$header=$(document.createElement("div"))).addClass("ui-multiselect-header ui-widget-header ui-corner-all ui-helper-clearfix").append(this.$headerLinkContainer);var $checkboxes=(this.$checkboxes=$(document.createElement("ul"))).addClass("ui-multiselect-checkboxes ui-helper-reset"+(wrapText.indexOf("options")>-1?"":" ui-multiselect-nowrap"));var $menu=(this.$menu=$(document.createElement("div"))).addClass("ui-multiselect-menu ui-widget ui-widget-content ui-corner-all"+($element[0].multiple?"":" ui-multiselect-single")+(!options.listbox?"":" ui-multiselect-listbox")+(this.options.classes?" "+this.options.classes:"")).append($header,$checkboxes);if(!options.listbox){var $button=this._buildButton();$button.insertAfter($element);var $appendEl=this._getAppendEl();$appendEl.append($menu);if(!options.zIndex&&!$appendEl.hasClass("ui-front")){var $uiFront=this.element.closest(".ui-front, dialog");options.zIndex=Math.max($uiFront&&parseInt($uiFront.css("z-index"),10)+1||0,$appendEl&&parseInt($appendEl.css("z-index"),10)+1||0)}if(options.zIndex){$menu.css("z-index",options.zIndex)}options.position=$.extend({my:"left top",at:"left bottom",of:$button},options.position||{})}else{$menu.insertAfter($element)}this._bindEvents();this.refresh(true)},_linkHTML:function(linkTemplate,linkID){var self=this;return linkTemplate.replace(/{{(.*?)}}/gi,function(m,p1){return self.linkInfo[linkID][p1]}).replace("","")},_init:function(){var elSelect=this.element[0];if(this.options.header!==false){this.$headerLinkContainer.find(".ui-multiselect-all, .ui-multiselect-none, .ui-multiselect-flip").toggle(!!elSelect.multiple)}else{this.$header.hide()}if(this.options.autoOpen&&!this.options.listbox){this.open()}if(elSelect.disabled){this.disable()}},_makeOption:function(option){var elSelect=this.element.get(0);var id=elSelect.id||this.multiselectID;var inputID="ui-multiselect-"+this.multiselectID+"-"+(option.id||id+"-option-"+this.inputIdCounter++);var isMultiple=elSelect.multiple;var isDisabled=option.disabled;var isSelected=option.selected;var input=document.createElement("input");var inputAttribs={type:isMultiple?"checkbox":"radio",id:inputID,title:option.title||null,value:option.value,name:this.options.addInputNames?"multiselect_"+id:null,checked:isSelected?"checked":null,"aria-selected":isSelected?"true":null,disabled:isDisabled?"disabled":null,"aria-disabled":isDisabled?"true":null};for(var name in inputAttribs){if(inputAttribs[name]!==null){input.setAttribute(name,inputAttribs[name])}}var optionAttribs=option.attributes;var len=optionAttribs.length;for(var x=0;x0&&numChecked<=selectedList){value=$checked.map(function(){return $(this).next().text().replace(/\n$/,"")}).get().join(options.selectedListSeparator)}else{value=selectedText.replace("#",numChecked).replace("#",inputCount)}}else{value=options.noneSelectedText}this._setButtonValue(value,isDefault);if(options.wrapText.indexOf("button")===-1){this._setButtonWidth(true)}if(this._isOpen&&this._savedButtonHeight!=this.$button.outerHeight(false)){this.position()}},_setButtonValue:function(value,isDefault){this.$buttonlabel[this.htmlAllowedFor("button")?"html":"text"](value);if(!!isDefault){this.$button[0].defaultValue=value}},_bindButtonEvents:function(){var self=this;var $button=this.$button;function buttonClickHandler(){self[self._isOpen?"close":"open"]();return false}$button.on({click:buttonClickHandler,keydown:$.proxy(self._handleButtonKeyboardNav,self),mouseenter:function(){if(!this.classList.contains("ui-state-disabled")){this.classList.add("ui-state-hover")}},mouseleave:function(){this.classList.remove("ui-state-hover")},focus:function(){if(!this.classList.contains("ui-state-disabled")){this.classList.add("ui-state-focus")}},blur:function(){this.classList.remove("ui-state-focus")}}).find("span").on("click.multiselect,click",buttonClickHandler)},_handleButtonKeyboardNav:function(e){if(!this._isOpen&&!this.element[0].multiple&&(e.which===38||e.which===40)){var $inputs=this.$inputs;var index=$inputs.index($inputs.filter(":checked"));if(e.which===38&&index){$inputs.eq(index-1).trigger("click")}else if(e.which===40&&index<$inputs.length-1){$inputs.eq(index+1).trigger("click")}return}switch(e.which){case 27:case 37:case 38:this.close();break;case 40:case 39:this.open();break}},_bindCheckboxEvents:function(){var self=this;self.$checkboxes.on("click.multiselect",".ui-multiselect-grouplabel",function(e){e.preventDefault();if(!self.options.groupsSelectable){return false}var $this=$(this);var $inputs=$this.next("ul").children(":not(.ui-multiselect-excluded)").find("input").not(":disabled");var nodes=$inputs.get();var label=this.textContent;if(self._trigger("beforeoptgrouptoggle",e,{inputs:nodes,label:label})===false){return}var maxSelected=self.options.maxSelected;if(maxSelected&&self.$inputs.filter(":checked").length+$inputs.length>maxSelected){return}self._toggleChecked($inputs.filter(":checked").length!==$inputs.length,$inputs);self._trigger("optgrouptoggle",e,{inputs:nodes,label:label,checked:nodes.length?nodes[0].checked:null})}).on("click.multiselect",".ui-multiselect-collapser",function(e){var $this=$(this);var $parent=$this.parent();var optgroupLabel=$parent.find(".ui-multiselect-grouplabel").first().html();var linkInfo=self.linkInfo;var collapsedClass="ui-multiselect-collapsed";var isCollapsed=$parent.hasClass(collapsedClass);if(self._trigger("beforecollapsetoggle",e,{label:optgroupLabel,collapsed:isCollapsed})===false){return}$parent.toggleClass(collapsedClass);$this.attr("title",isCollapsed?linkInfo.collapse.title:linkInfo.expand.title).html(isCollapsed?linkInfo.collapse.icon:linkInfo.expand.icon);if(!self.options.listbox){self._setMenuHeight(true)}self._trigger("collapsetoggle",e,{label:optgroupLabel,collapsed:!isCollapsed})}).on("mouseenter.multiselect",".ui-multiselect-collapser",function(e){this.classList.add("ui-state-hover")}).on("mouseleave.multiselect",".ui-multiselect-collapser",function(e){this.classList.remove("ui-state-hover")}).on("mouseenter.multiselect","label",function(e,param){if(!this.classList.contains("ui-state-disabled")){var checkboxes=self.$checkboxes[0];var scrollLeft=checkboxes.scrollLeft;var scrollTop=checkboxes.scrollTop;var scrollX=window.pageXOffset;var scrollY=window.pageYOffset;self.$labels.removeClass("ui-state-hover");$(this).addClass("ui-state-hover").find("input").focus();if(!param||!param.allowScroll){checkboxes.scrollLeft=scrollLeft;checkboxes.scrollTop=scrollTop;window.scrollTo(scrollX,scrollY)}}}).on("keydown.multiselect","label",function(e){if(e.which===82){return}if(e.which>111&&e.which<124){return}e.preventDefault();switch(e.which){case 9:if(e.shiftKey){self.$menu.find(".ui-state-hover").removeClass("ui-state-hover");self.$header.find("li").last().find("a").focus()}else{self.close()}break;case 27:self.close();break;case 38:case 40:case 37:case 39:self._traverse(e.which,this);break;case 13:case 32:$(this).find("input")[0].click();break;case 65:if(e.altKey){self.checkAll()}break;case 70:if(e.altKey){self.flipAll()}break;case 85:if(e.altKey){self.uncheckAll()}break}}).on("click.multiselect","input",function(e){var input=this;var $input=$(input);var val=input.value;var checked=input.checked;var $element=self.element;var $tags=$element.find("option");var isMultiple=$element[0].multiple;var $allInputs=self.$inputs;var numChecked=$allInputs.filter(":checked").length;var options=self.options;var textFxn=self.htmlAllowedFor("options")?"html":"text";var optionText=$input.parent().find("span")[textFxn]();var maxSelected=options.maxSelected;if(input.disabled||self._trigger("click",e,{value:val,text:optionText,checked:checked})===false){e.preventDefault();return}if(maxSelected&&checked&&numChecked>maxSelected){if(self._trigger("maxselected",e,{labels:self.$labels,inputs:$allInputs})!==false){self.buttonMessage("
    LIMIT OF "+(numChecked-1)+" REACHED!
    ")}input.checked=false;e.preventDefault();return false}input.focus();$input.prop("aria-selected",checked);$tags.each(function(){this.selected=this.value===val?checked:isMultiple&&this.selected});if(!isMultiple){self.$labels.removeClass("ui-state-active");$input.closest("label").toggleClass("ui-state-active",checked);self.close()}$element.trigger("change");setTimeout($.proxy(self.update,self),10)})},_bindHeaderEvents:function(){var self=this;self.$header.on("click.multiselect","a",function(e){var headerLinks={"ui-multiselect-close":"close","ui-multiselect-all":"checkAll","ui-multiselect-none":"uncheckAll","ui-multiselect-flip":"flipAll","ui-multiselect-collapseall":"collapseAll","ui-multiselect-expandall":"expandAll"};for(hdgClass in headerLinks){if(this.classList.contains(hdgClass)){self[headerLinks[hdgClass]]();e.preventDefault();return false}}}).on("keydown.multiselect","a",function(e){switch(e.which){case 27:self.close();break;case 9:var $target=$(e.target);if(e.shiftKey&&!$target.parent().prev().length&&!self.$header.find(".ui-multiselect-filter").length||!$target.parent().next().length&&!self.$labels.length&&!e.shiftKey){self.close();e.preventDefault()}break}})},_setResizable:function(){if(!this.options.resizableMenu||!("resizable"in $.ui)){return}this.$menu.show();this.$menu.resizable({containment:"parent",handles:"s",helper:"ui-multiselect-resize",stop:function(e,ui){ui.size.width=ui.originalSize.width;$(this).outerWidth(ui.originalSize.width);if(this._trigger("resize",e,ui)!==false){this.options.menuHeight=ui.size.height}this._setMenuHeight(true)}});this.$menu.hide()},_bindEvents:function(){if(!this.options.listbox){this._bindButtonEvents()}this._bindHeaderEvents();this._bindCheckboxEvents();this._setResizable();this.document.on("mousedown"+this._namespaceID+" wheel"+this._namespaceID+" mousewheel"+this._namespaceID,function(event){var target=event.target;if(this._isOpen&&(!!this.$button?target!==this.$button[0]&&!$.contains(this.$button[0],target):true)&&target!==this.$menu[0]&&!$.contains(this.$menu[0],target)){this.close()}}.bind(this));$(this.element[0].form).on("reset"+this._namespaceID,function(){setTimeout(this.refresh.bind(this),10)}.bind(this))},_setButtonWidth:function(recalc){if(this._savedButtonWidth&&!recalc){return}var width=this._selectWidth||this._getBCRWidth(this.element);var buttonWidth=this.options.buttonWidth||"";if(/\d/.test(buttonWidth)){var parsed=parse2px(buttonWidth,this.element);var pixels=parsed.px;var minimax=parsed.minimax;width=minimax<0?Math.max(width,pixels):minimax>0?Math.min(width,pixels):pixels}else{buttonWidth=buttonWidth.toLowerCase()}if(buttonWidth!=="auto"){this.$button.outerWidth(width)}this._savedButtonWidth=width},_setMenuWidth:function(recalc){if(this._savedMenuWidth&&!recalc){return}var width=!!this.options.listbox?this._selectWidth:this._savedButtonWidth||this._getBCRWidth(this.$button);var menuWidth=this.options.menuWidth||"";if(/\d/.test(menuWidth)){var parsed=parse2px(menuWidth,this.element);var pixels=parsed.px;var minimax=parsed.minimax;width=minimax<0?Math.max(width,pixels):minimax>0?Math.min(width,pixels):pixels}else{menuWidth=menuWidth.toLowerCase()}if(menuWidth!=="auto"){this.$menu.outerWidth(width);this._savedMenuWidth=width;return}this.$menu.addClass("ui-multiselect-measure");var headerWidth=this.$header.outerWidth(true)+this._jqWidthFix(this.$header);var cbWidth=this.$checkboxes.outerWidth(true)+this._jqWidthFix(this.$checkboxes);this.$menu.removeClass("ui-multiselect-measure");var contentWidth=Math.max(this.options.wrapText.indexOf("header")>-1?0:headerWidth,cbWidth);this.$menu.width(contentWidth);this._savedMenuWidth=this.$menu.outerWidth(false)},_setMenuHeight:function(recalc){var self=this;if(self._savedMenuHeight&&!recalc){return}var maxHeight=$(window).height();var optionHeight=self.options.menuHeight||"";var useSelectSize=false;var elSelectSize=4;if(/\d/.test(optionHeight)){var $header=self.$header.filter(":visible");var headerHeight=$header.outerHeight(true);var menuBorderPaddingHt=this.$menu.outerHeight(false)-this.$menu.height();var cbBorderPaddingHt=this.$checkboxes.outerHeight(false)-this.$checkboxes.height();optionHeight=parse2px(optionHeight,self.element,true).px;maxHeight=Math.min(optionHeight,maxHeight)-headerHeight-menuBorderPaddingHt-cbBorderPaddingHt}else if(optionHeight.toLowerCase()==="size"){useSelectSize=true;elSelectSize=self.element[0].size||elSelectSize}var overflowSetting="hidden";var itemCount=0;var hoverAdjust=4;var ulHeight=hoverAdjust;var ulTop=-1;self.$checkboxes.find("li:not(.ui-multiselect-optgroup),a").filter(":visible").each(function(){if(ulTop<0){ulTop=this.offsetTop}ulHeight=this.offsetTop+this.offsetHeight-ulTop+hoverAdjust;if(useSelectSize&&++itemCount>=elSelectSize||ulHeight>maxHeight){overflowSetting="auto";if(!useSelectSize){ulHeight=maxHeight}return false}});self.$checkboxes.css("overflow",overflowSetting).height(ulHeight);self._savedMenuHeight=this.$menu.outerHeight(false)},_getBCRWidth:function(elem){if(!elem||!!elem.jquery&&!elem[0]){return null}var domRect=!!elem.jquery?elem[0].getBoundingClientRect():elem.getBoundingClientRect();return domRect.right-domRect.left},_jqWidthFix:function(elem){if(!elem||!!elem.jquery&&!elem[0]){return null}return!!elem.jquery?this._getBCRWidth(elem[0])-elem.outerWidth(false):this._getBCRWidth(elem)-$(elem).outerWidth(false)},_traverse:function(which,start){var $start=$(start);var moveToLast=which===38||which===37;var $next=$start.parent()[moveToLast?"prevAll":"nextAll"]("li:not(:disabled, .ui-multiselect-optgroup):visible").first();if(!$next.length){$next=$start.parents(".ui-multiselect-optgroup")[moveToLast?"prev":"next"]()}if(!$next.length){var $container=this.$checkboxes;$container.find("label").filter(":visible")[moveToLast?"last":"first"]().trigger("mouseover",{allowScroll:true});$container.scrollTop(moveToLast?$container.height():0)}else{$next.find("label").filter(":visible")[moveToLast?"last":"first"]().trigger("mouseover",{allowScroll:true})}},_toggleState:function(prop,flag){return function(){var state=flag==="!"?!this[prop]:flag;if(!this.disabled){this[prop]=state}if(state){this.setAttribute("aria-"+prop,true)}else{this.removeAttribute("aria-"+prop)}}},_toggleChecked:function(flag,group,filteredInputs){var self=this;var $element=self.element;var $inputs=group&&group.length?group:self.$inputs;if(filteredInputs){$inputs=self._isOpen?$inputs.closest("li").not(".ui-multiselect-excluded").find("input").not(":disabled"):$inputs.not(":disabled")}$inputs.each(self._toggleState("checked",flag));$inputs.eq(0).focus();self.update();var inputValues={};$inputs.each(function(){inputValues[this.value]=true});$element.find("option").each(function(){if(!this.disabled&&inputValues[this.value]){self._toggleState("selected",flag).call(this)}});if($inputs.length){$element.trigger("change")}},_toggleDisabled:function(flag,groupID){var disabledClass="ui-state-disabled";if(this.$button){this.$button.prop({disabled:flag,"aria-disabled":flag})[flag?"addClass":"removeClass"](disabledClass)}if(this.options.disableInputsOnToggle){var $inputs=typeof groupID==="undefined"?this.$inputs:this._multiselectOptgroupFilter(groupID).find("input");var msDisabledClass="ui-multiselect-disabled";if(flag){var matchedInputs=$inputs.filter(":enabled").get();for(var x=0,len=matchedInputs.length;x=(gotID?checkedCount-gCheckedCount+gInputCount-gCheckedCount:inputCount-checkedCount)){if(gotID){this._toggleChecked("!",$filteredOptgroupInputs)}else{this._toggleChecked("!")}this._trigger("flipAll")}else{this.buttonMessage("
    Flip All Not Permitted.
    ")}},collapseAll:function(groupID){this._trigger("beforeCollapseAll");var $optgroups=typeof groupID==="undefined"?this.$checkboxes.find(".ui-multiselect-optgroup"):this._multiselectOptgroupFilter(groupID);$optgroups.addClass("ui-multiselect-collapsed").children(".ui-multiselect-collapser").attr("title",this.linkInfo.expand.title).html(this.linkInfo.expand.icon);this._trigger("collapseAll")},expandAll:function(groupID){this._trigger("beforeExpandAll");var $optgroups=typeof groupID==="undefined"?this.$checkboxes.find(".ui-multiselect-optgroup"):this._multiselectOptgroupFilter(groupID);$optgroups.removeClass("ui-multiselect-collapsed").children(".ui-multiselect-collapser").attr("title",this.linkInfo.collapse.title).html(this.linkInfo.collapse.icon);this._trigger("expandAll")},buttonMessage:function(message){var self=this;self.$buttonlabel.html(message);setTimeout(function(){self.update()},1e3)},getChecked:function(){return this.$inputs.filter(":checked")},getUnchecked:function(){return this.$inputs.filter(":not(:checked)")},destroy:function(){$.Widget.prototype.destroy.call(this);this.document.off(this._namespaceID);$(this.element[0].form).off(this._namespaceID);if(!this.options.listbox){this.$button.remove()}this.$menu.remove();this.element.show();return this},isOpen:function(){return this._isOpen},widget:function(){return this.$menu},getNamespaceID:function(){return this._namespaceID},getButton:function(){return this.$button},getMenu:function(){return this.$menu},getLabels:function(){return this.$labels},getCollapsed:function(){return this.$checkboxes.find(".ui-multiselect-collapsed")},value:function(newValue){if(typeof newValue!=="undefined"){this.element.val(newValue);this.resync();return this.element}else{return this.element.val()}},htmlAllowedFor:function(element){return this.options.htmlText.indexOf(element)>-1},addOption:function(attributes,text,groupID){var self=this;var textFxn=self.htmlAllowedFor("options")?"html":"text";var $option=$(document.createElement("option")).attr(attributes)[textFxn](text);var optionNode=$option.get(0);if(typeof groupID==="undefined"){self.element.append($option);self.$checkboxes.append(self._makeOption(optionNode))}else{self._nativeOptgroupFilter(groupID).append($option);self._multiselectOptgroupFilter(groupID).append(self._makeOption(optionNode))}self._updateCache()},_nativeOptgroupFilter:function(groupID){return this.element.children("OPTGROUP").filter(function(index){return typeof groupID==="number"?index===groupID:this.getAttribute("label")===groupID})},_multiselectOptgroupFilter:function(groupID){return this.$menu.find(".ui-multiselect-optgroup").filter(function(index){return typeof groupID==="number"?index===groupID:this.getElementsByClassName("ui-multiselect-grouplabel")[0].textContent===groupID})},removeOption:function(value){if(!value){return}this.element.find("option[value="+value+"]").remove();this.$labels.find("input[value="+value+"]").parents("li").remove();this._updateCache()},_setOption:function(key,value){var $header=this.$header;var $menu=this.$menu;switch(key){case"header":if(typeof value==="boolean"){$header.toggle(value)}else if(typeof value==="string"){this.$headerLinkContainer.children("li:not(:last-child)").remove();this.$headerLinkContainer.prepend("
  • "+value+"
  • ")}break;case"checkAllText":case"uncheckAllText":case"flipAllText":case"collapseAllText":case"expandAllText":if(key!=="checkAllText"||!this.options.maxSelected){$header.find("a."+this.linkInfo[key.replace("Text","")]["class"]+" span").eq(-1).html(value)}break;case"checkAllIcon":case"uncheckAllIcon":case"flipAllIcon":case"collapseAllIcon":case"expandAllIcon":if(key!=="checkAllIcon"||!this.options.maxSelected){$header.find("a."+this.linkInfo[key.replace("Icon","")]["class"]+" span").eq(0).replaceWith(value)}break;case"openIcon":$menu.find("span.ui-multiselect-open").html(value);break;case"closeIcon":$menu.find("a.ui-multiselect-close").html(value);break;case"buttonWidth":case"menuWidth":this.options[key]=value;this._setButtonWidth(true);this._setMenuWidth(true);break;case"menuHeight":this.options[key]=value;this._setMenuHeight(true);break;case"selectedText":case"selectedList":case"maxSelected":case"noneSelectedText":case"selectedListSeparator":this.options[key]=value;this.update(true);break;case"classes":$menu.add(this.$button).removeClass(this.options.classes).addClass(value);break;case"multiple":var $element=this.element;if(!!$element[0].multiple!==value){$menu.toggleClass("ui-multiselect-multiple",value).toggleClass("ui-multiselect-single",!value);$element[0].multiple=value;this.uncheckAll();this.refresh()}break;case"position":if(value!==null&&!$.isEmptyObject(value)){this.options.position=value}this.position();break;case"zIndex":this.options.zIndex=value;this.$menu.css("z-index",value);break;default:this.options[key]=value}$.Widget.prototype._setOption.apply(this,arguments)},_parse2px:parse2px});if($.ui&&"dialog"in $.ui){$.widget("ui.dialog",$.ui.dialog,{_allowInteraction:function(event){if(this._super(event)||$(event.target).closest(".ui-multiselect-menu").length){return true}}})}})(jQuery);(function($){var rEscape=/[\-\[\]{}()*+?.,\\\^$|#\s]/g;var filterRules={contains:"{{term}}",beginsWith:"^{{term}}",endsWith:"{{term}}$",exactMatch:"^{{term}}$",containsNumber:"d",isNumeric:"^d+$",isNonNumeric:"^D+$"};var headerSelector=".ui-multiselect-header";var hasFilterClass="ui-multiselect-hasfilter";var filterClass="ui-multiselect-filter";var optgroupClass="ui-multiselect-optgroup";var groupLabelClass="ui-multiselect-grouplabel";var hiddenClass="ui-multiselect-excluded";function debounce(func,wait,immediate){var timeout;return function(){var context=this;var args=arguments;var later=function(){timeout=null;if(!immediate){func.apply(context,args)}};var callNow=immediate&&!timeout;clearTimeout(timeout);timeout=setTimeout(later,wait);if(callNow){func.apply(context,args)}}}$.widget("ech.multiselectfilter",{options:{label:"Filter:",placeholder:"Enter keywords",filterRule:"contains",searchGroups:false,autoReset:false,width:null,debounceMS:250},_create:function(){var opts=this.options;var $element=this.element;this.instance=$element.data("ech-multiselect");this.$header=this.instance.$menu.find(headerSelector).addClass(hasFilterClass);this.$input=$(document.createElement("input")).attr({placeholder:opts.placeholder,type:"search"}).css({width:typeof opts.width==="string"?this.instance._parse2px(opts.width,this.$header).px+"px":/\d/.test(opts.width)?opts.width+"px":null});this._bindInputEvents();if(this.options.autoReset){$element.on("multiselectbeforeclose",$.proxy(this._reset,this))}var $label=$(document.createElement("label")).text(opts.label).append(this.$input).addClass("ui-multiselect-filter-label");this.$wrapper=$(document.createElement("div")).addClass(filterClass).append($label).prependTo(this.$header);if(!!this.instance._isOpen){this.instance._setMenuHeight(true)}this.updateCache();var instance=this.instance;var filter=this.$input[0];instance._oldToggleChecked=instance._toggleChecked;instance._toggleChecked=function(flag,group){instance._oldToggleChecked(flag,group,!!filter.value)}},_bindInputEvents:function(){this.$input.on({keydown:function(e){if(e.which===13){e.preventDefault()}else if(e.which===27){$element.multiselect("close");e.preventDefault()}else if(e.which===9&&e.shiftKey){$element.multiselect("close");e.preventDefault()}else if(e.altKey){switch(e.which){case 82:e.preventDefault();$(this).val("").trigger("input","");break;case 65:$element.multiselect("checkAll");break;case 85:$element.multiselect("uncheckAll");break;case 70:$element.multiselect("flipAll");break;case 76:$element.multiselect("instance").$labels.first().trigger("mouseenter");break}}},input:$.proxy(debounce(this._handler,this.options.debounceMS),this),search:$.proxy(this._handler,this)})},_handler:function(e){var term=this.$input[0].value.toLowerCase().replace(/^\s+|\s+$/g,"");var filterRule=this.options.filterRule||"contains";var regex=new RegExp((filterRules[filterRule]||filterRule).replace("{{term}}",term.replace(rEscape,"\\$&")),"i");var searchGroups=!!this.options.searchGroups;var $checkboxes=this.instance.$checkboxes;var cache=this.cache;this.$rows.toggleClass(hiddenClass,!!term);var filteredInputs=$checkboxes.children().map(function(x){var elem=this;var $groupItems=$(elem);var groupShown=false;if(elem.classList.contains(optgroupClass)){var $groupItems=$groupItems.find("li");if(searchGroups&®ex.test(cache[x])){elem.classList.remove(hiddenClass);$groupItems.removeClass(hiddenClass);return $groupItems.find("input").get()}}return $groupItems.map(function(y){if(regex.test(cache[x+"."+y])){if(!groupShown){elem.classList.remove(hiddenClass);groupShown=true}this.classList.remove(hiddenClass);return this.getElementsByTagName("input")[0]}return null})});if(term){this._trigger("filter",e,filteredInputs)}if(!this.instance.options.listbox&&this.instance._isOpen){this.instance._setMenuHeight(true);this.instance.position()}return},_reset:function(){this.$input.val("");var event=document.createEvent("Event");event.initEvent("reset",true,true);this.$input.get(0).dispatchEvent(event);this._handler(event)},updateCache:function(alsoRefresh){var cache={};this.instance.$checkboxes.children().each(function(x){var $element=$(this);if(this.classList.contains(optgroupClass)){cache[x]=this.getElementsByClassName(groupLabelClass)[0].textContent;$element=$element.find("li")}$element.each(function(y){cache[x+"."+y]=this.textContent})});this.cache=cache;this.$rows=this.instance.$checkboxes.find("li");if(!!alsoRefresh){this._handler()}},widget:function(){return this.$wrapper},destroy:function(){$.Widget.prototype.destroy.call(this);this.$input.val("").trigger("keyup").off("keydown input search");this.instance.$menu.find(headerSelector).removeClass(hasFilterClass);this.$wrapper.remove()}})})(jQuery); 2 | -------------------------------------------------------------------------------- /docs/maxchecked.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 23 | 24 |

    Max Checked Test

    25 |

    Logic to impose a maximum number of checked inputs (two in this demo).

    26 | 27 |

    Check a few boxes.

    28 | 29 |
    30 | 39 |
    40 | 41 |
    42 | var warning = $(".message");
    43 | 
    44 | $("select").multiselect({ 
    45 |    header: "Choose only TWO items!",
    46 |    click: function(e){
    47 |        if( $(this).multiselect("widget").find("input:checked").length > 2 ){
    48 |            warning.addClass("error").removeClass("success").html("You can only check two checkboxes!");
    49 |            return false;
    50 |        } else {
    51 |            warning.addClass("success").removeClass("error").html("Check a few boxes.");
    52 |        }
    53 |    }
    54 | });
    55 | 
    56 | -------------------------------------------------------------------------------- /docs/position.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 25 |

    Position Utility

    26 | 27 |

    Using the jQuery UI position utility to position the menu above the input. This is especially useful when you want collision detection with the bottom of the window, and automatically open the menu above the button.

    28 |

    Please use jQuery 1.4.3 with the positioning option. Bugs in jQuery 1.4.2 prevent the menu from being positioned correctly the first time multiselect is opened in Webkit/IE.

    29 | 30 | 31 | 32 |
    33 |

    Center the menu over the button

    34 |
    35 | $("select").multiselect({
    36 |    position: {
    37 |       my: 'center',
    38 |       at: 'center'
    39 |       
    40 |      // only include the "of" property if you want to position
    41 |      // the menu against an element other than the button.
    42 |      // multiselect automatically sets "of" unless you explictly
    43 |      // pass in a value.
    44 |    }
    45 | });
    46 | 
    47 |

    48 | 53 |

    54 | 55 |

    Open the menu upwards

    56 |
    57 | $("select").multiselect({
    58 |    position: {
    59 |       my: 'left bottom',
    60 |       at: 'left top'
    61 |    }
    62 | });
    63 | 
    64 |

    65 | 70 |

    71 |
    72 | -------------------------------------------------------------------------------- /docs/preselected.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 5 |

    Pre-selected & pre-disabled options

    6 |

    Options one, three, and four have the selected="selected" attribute and are checked by default. Options five and six have the disabled="disabled" attribute. 7 | When the widget is initialized on the select, both attribute types are honored.

    8 | 9 |
    10 |

    11 | 22 |

    23 |
    24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/refresh.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 30 |

    Refresh Method

    31 | 32 |

    Calling refresh allows you to re-build the multiselect menu from your original select. This is useful 33 | when the contents of the select box changes dynamically (through either AJAX, DOM manipulation, etc.) and you want the 34 | multiselect widget to reflect the changes.

    35 | 36 |
    37 | // once the "add" button is clicked below:
    38 | $("select").multiselect('refresh');
    39 | 
    40 | 41 |
    42 |

    Add an item:

    43 |

    Type in the text of a new option tag to add dynamically.

    44 | 45 | 46 |

    47 | 48 | 49 |

    50 |
    51 | 52 |
    53 | 58 |
    59 | -------------------------------------------------------------------------------- /docs/selectedlist.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 13 | 14 |

    Displaying options in a list

    15 | 16 |

    Using the selectedList Parameter

    17 |

    The selectedList parameter is a boolean/numeric value denoting whether or not to display the checked opens in a list, and how many. A number greater than 0 denotes the maximum number of list items to display before switching over to the selectedText parameter.

    18 |
    19 | $("select").multiselect({
    20 |    selectedList: 4 // 0-based index
    21 | });
    22 | 
    23 |
    24 |

    25 | 34 |

    35 |
    36 | 37 |

    Passing a Function to selectedText

    38 |

    Passing a function to the selectedText option gives you low-level control of what the widget displays. The function receives three arguments: 39 | the number of checkboxes checked, the total number of checkboxes, and an array of the checkboxes (DOM elements) that were checked. Example usage:

    40 | 41 |
    42 | $("select").multiselect({
    43 |    selectedText: function(numChecked, numTotal, checkedItems){
    44 |       return numChecked + ' of ' + numTotal + ' checked';
    45 |    }
    46 | });
    47 | 
    48 | 49 |

    The selectedList option is simply a convenience method for the selectedText option.

    50 | -------------------------------------------------------------------------------- /docs/single.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 | 15 | 16 |

    Single Select

    17 | 18 |

    Omitting the multiple attribute from the underlying native select force the widget to use radio buttons instead of checkboxes. It is a good idea to use the header 19 | parameter in order to hide/change the "check all"/"uncheck all" links.

    20 | 21 |
    22 | $("select").multiselect({
    23 |    header: "=Select an option",
    24 |    noneSelectedText: "Select an Option",
    25 |    selectedList: 1
    26 | });
    27 | 
    28 | 29 |
    30 |

    31 | 40 |

    41 |
    42 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.br.js: -------------------------------------------------------------------------------- 1 | /* Brazilian initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Vinícius Fontoura Corrêa (vinusfc@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Marcar todos', title: 'Marcar todos'}, 9 | uncheckAll: {text: 'Desmarcar todos', title: 'Desmarcar todos'} 10 | }, 11 | noneSelectedText: 'Selecione as opções', 12 | selectedText: '# selecionado' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.cs.js: -------------------------------------------------------------------------------- 1 | /* Czech initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Michi (michi.m@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Vybrat vše', title: 'Vybrat vše'}, 9 | uncheckAll: {text: 'Zrušit výběr', title: 'Zrušit výběr'} 10 | }, 11 | noneSelectedText: 'Nic není vybráno', 12 | selectedText: '# vybráno' 13 | }); 14 | 15 | })( jQuery ); 16 | 17 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.de.js: -------------------------------------------------------------------------------- 1 | /* German initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Sven Tatter (sven.tatter@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Alle auswählen', title: 'Alle auswählen'}, 9 | uncheckAll: {text: 'Alle abwählen', title: 'Alle abwählen'} 10 | }, 11 | noneSelectedText: 'Nichts ausgewählt', 12 | selectedText: '# ausgewählt' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.es.js: -------------------------------------------------------------------------------- 1 | /* Spanish initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Vinius Fontoura Correa(vinusfc@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Marca todas', title: 'Marca todas'}, 9 | uncheckAll: {text: 'Desmarque todas', title: 'Desmarque todas'} 10 | }, 11 | noneSelectedText: 'Seleccione las opciones', 12 | selectedText: '# seleccionado' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.br.js: -------------------------------------------------------------------------------- 1 | /* Brazilian initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Vinícius Fontoura Corrêa (vinusfc@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Filtro:", 8 | placeholder: "Entre com a palavra" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.cs.js: -------------------------------------------------------------------------------- 1 | /* Czech initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Michi (michi.m@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Filtrovat:", 8 | placeholder: "Napište výraz" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.de.js: -------------------------------------------------------------------------------- 1 | /* German initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Sven Tatter (sven.tatter@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Suchen:", 8 | placeholder: "Stichwort eingeben" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.es.js: -------------------------------------------------------------------------------- 1 | /* Spanish initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Vinícius Fontoura Corrêa (vinusfc@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Filtro:", 8 | placeholder: "Introduzca una palabra" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.fr.js: -------------------------------------------------------------------------------- 1 | /* French initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Charles SANQUER (charles.sanquer@spyrit.net). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Filtre:", 8 | placeholder: "Entrer un mot clé" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.hu.js: -------------------------------------------------------------------------------- 1 | /* Hungarian initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Adam Fónagy (adam.fonagy@greenformatics.hu). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Keresés:", 8 | placeholder: "kulcsszó megadása" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.it.js: -------------------------------------------------------------------------------- 1 | /* Italian initialization for the jQuery UI multiselect plugin. */ 2 | /* Written by Vincenzo Farruggia(mastropinguino@networky.net). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Filtro:", 8 | placeholder: "Digita una parola chiave" 9 | }); 10 | 11 | })( jQuery ); 12 | 13 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.ja.js: -------------------------------------------------------------------------------- 1 | /* Japanese initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Daisuke (daisuketaniwaki@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: '絞込み:', 8 | placeholder: 'キーワードを入力してください' 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.pl.js: -------------------------------------------------------------------------------- 1 | /* Polish initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Tomasz Mazur (contact@tomaszmazur.eu). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Filtruj:", 8 | placeholder: "Wprowadź słowa kluczowe" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.ru.js: -------------------------------------------------------------------------------- 1 | /* Russian initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Artem Packhomov (gorblnu4@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Фильтр:", 8 | placeholder: "Введите запрос" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.tr.js: -------------------------------------------------------------------------------- 1 | /* Brazilian initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Yusuf Özer (realsby@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: "Filtre:", 8 | placeholder: "Bir kelime yazın" 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.zh-cn.js: -------------------------------------------------------------------------------- 1 | /* Simplified Chinese initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Ben (ben@zfben.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: '过滤:', 8 | placeholder: '输入关键字过滤' 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.filter.zh-tw.js: -------------------------------------------------------------------------------- 1 | /* Simplified Chinese initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Ben (ben@zfben.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselectfilter.prototype.options, { 7 | label: '過濾:', 8 | placeholder: '輸入關鍵字過濾' 9 | }); 10 | 11 | })( jQuery ); 12 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.fr.js: -------------------------------------------------------------------------------- 1 | /* French initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Charles SANQUER (charles.sanquer@spyrit.net). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Tout cocher', title: 'Tout cocher'}, 9 | uncheckAll: {text: 'Tout décocher', title: 'Tout décocher'} 10 | }, 11 | noneSelectedText: 'Sélectionner les options', 12 | selectedText: '# sélectionnés' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.hu.js: -------------------------------------------------------------------------------- 1 | /* Hungarian initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Adam Fónagy (adam.fonagy@greenformatics.hu). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Mind kijelöl', title: 'Mind kijelöl'}, 9 | uncheckAll: {text: 'Mind eltávolít', title: 'Mind eltávolít'} 10 | }, 11 | noneSelectedText: 'Nincs kijelölés', 12 | selectedText: '# kijelölve' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.it.js: -------------------------------------------------------------------------------- 1 | /* Italian initialization for the jQuery UI multiselect plugin. */ 2 | /* Written by Vincenzo Farruggia(mastropinguino@networky.net). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Seleziona tutto', title: 'Seleziona tutto'}, 9 | uncheckAll: {text: 'Deseleziona tutto', title: 'Deseleziona tutto'} 10 | }, 11 | noneSelectedText: 'Seleziona le opzioni', 12 | selectedText: '# selezionati' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.ja.js: -------------------------------------------------------------------------------- 1 | /* Japanese initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Daisuke (daisuketaniwaki@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'すべて選択', title: 'すべて選択'}, 9 | uncheckAll: {text: '選択解除', title: '選択解除'} 10 | }, 11 | noneSelectedText: '選択してください', 12 | selectedText: '#つ選択中' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.pl.js: -------------------------------------------------------------------------------- 1 | /* Polish initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Tomasz Mazur (contact@tomaszmazur.eu). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Zaznacz wszystkie', title: 'Zaznacz wszystkie'}, 9 | uncheckAll: {text: 'Odznacz wszystkie', title: 'Odznacz wszystkie'} 10 | }, 11 | noneSelectedText: 'Wybierz opcje', 12 | selectedText: 'Zaznaczono #' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.ru.js: -------------------------------------------------------------------------------- 1 | /* Russian initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Artem Packhomov (gorblnu4@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Отметить все', title: 'Отметить все'}, 9 | uncheckAll: {text: 'Снять отметку со всех', title: 'Снять отметку со всех'} 10 | }, 11 | noneSelectedText: 'Выберите из списка', 12 | selectedText: 'Выбрано #' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.tr.js: -------------------------------------------------------------------------------- 1 | /* Turkish initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Yusuf Özer (realsby@gmail.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: 'Tümünü seç', title: 'Tümünü seç'}, 9 | uncheckAll: {text: 'Tümünü sil', title: 'Tümünü sil'} 10 | }, 11 | noneSelectedText: 'Seçenekleri belirleyin', 12 | selectedText: '# adet seçildi' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.zh-cn.js: -------------------------------------------------------------------------------- 1 | /* Simplified Chinese initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Ben (ben@zfben.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: '全选', title: '全选'}, 9 | uncheckAll: {text: '清空', title: '清空'} 10 | }, 11 | noneSelectedText: '请选择', 12 | selectedText: '# 已选择' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /i18n/jquery.multiselect.zh-tw.js: -------------------------------------------------------------------------------- 1 | /* Simplified Chinese initialisation for the jQuery UI multiselect plugin. */ 2 | /* Written by Ben (ben@zfben.com). */ 3 | 4 | (function ( $ ) { 5 | 6 | $.extend($.ech.multiselect.prototype.options, { 7 | linkInfo: { 8 | checkAll: {text: '全選', title: '全選'}, 9 | uncheckAll: {text: '清空', title: '清空'} 10 | }, 11 | noneSelectedText: '請選擇', 12 | selectedText: '# 已選擇' 13 | }); 14 | 15 | })( jQuery ); 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-ui-multiselect-widget", 3 | "description": "MultiSelect progessively enhances an ordinary multiple select control into elegant drop down list of checkboxes, stylable with ThemeRoller.", 4 | "version": "3.0.1", 5 | "license": "MIT or GPL-2.0", 6 | "author": "Eric Hynds", 7 | "contributors": [ 8 | { 9 | "name": "Micheal" 10 | }, 11 | { 12 | "name": "AB Zainuddin", 13 | "email": "burhan@codeyellow.nl" 14 | }, 15 | { 16 | "name": "Steve James" 17 | } 18 | ], 19 | "scripts": { 20 | "test": "node-qunit-phantomjs ./tests/unit/index.htm", 21 | "update-docs": "uglifyjs src/jquery.multiselect.js src/jquery.multiselect.filter.js > docs/js/widget.min.js" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/ehynds/jquery-ui-multiselect-widget" 26 | }, 27 | "main": "src/jquery.multiselect.js", 28 | "dependencies": { 29 | "jquery": ">=1.8.0", 30 | "jquery-ui": "^1.11.0" 31 | }, 32 | "devDependencies": { 33 | "eslint": "^6.0.1", 34 | "eslint-config-google": "^0.9.1", 35 | "node-qunit-phantomjs": "^2.0.0", 36 | "uglifyjs": "^2.4.11" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/jquery.multiselect.filter.js: -------------------------------------------------------------------------------- 1 | /* jshint forin:true, noarg:true, noempty:true, eqeqeq:true, boss:true, undef:true, curly:true, browser:true, jquery:true */ 2 | /* 3 | * jQuery MultiSelect UI Widget Filtering Plugin 3.0.0 4 | * Copyright (c) 2012 Eric Hynds 5 | * 6 | * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/ 7 | * 8 | * Depends: 9 | * - jQuery UI MultiSelect widget 10 | * 11 | * Dual licensed under the MIT and GPL licenses: 12 | * http://www.opensource.org/licenses/mit-license.php 13 | * http://www.gnu.org/licenses/gpl.html 14 | * 15 | */ 16 | (function($) { 17 | var rEscape = /[\-\[\]{}()*+?.,\\\^$|#\s]/g; 18 | 19 | // "{{term}}" is a placeholder below for where the search term 20 | // would be inserted in the resulting regular expression. 21 | var filterRules = { 22 | contains: '{{term}}', 23 | beginsWith: '^{{term}}', 24 | endsWith: '{{term}}$', 25 | exactMatch: '^{{term}}$', 26 | containsNumber: '\d', 27 | isNumeric: '^\d+$', 28 | isNonNumeric: '^\D+$', 29 | }; 30 | 31 | var headerSelector = '.ui-multiselect-header'; 32 | var hasFilterClass = 'ui-multiselect-hasfilter'; 33 | var filterClass = 'ui-multiselect-filter'; 34 | var optgroupClass = 'ui-multiselect-optgroup'; 35 | var groupLabelClass = 'ui-multiselect-grouplabel'; 36 | var hiddenClass = 'ui-multiselect-excluded'; 37 | 38 | /** 39 | * This comes courtesy of underscore.js 40 | * @param {function} func to debounce 41 | * @param {number} wait period 42 | * @param {bool} immediate perform once immediately 43 | * @return {function} input function with a debounce period 44 | */ 45 | function debounce(func, wait, immediate) { 46 | var timeout; 47 | return function() { 48 | var context = this; var args = arguments; // eslint-disable-line prefer-rest-params 49 | var later = function() { 50 | timeout = null; 51 | if (!immediate) { 52 | func.apply(context, args); 53 | } 54 | }; 55 | var callNow = immediate && !timeout; 56 | clearTimeout(timeout); 57 | timeout = setTimeout(later, wait); 58 | if (callNow) { 59 | func.apply(context, args); 60 | } 61 | }; 62 | } 63 | 64 | $.widget('ech.multiselectfilter', { 65 | 66 | options: { 67 | label: 'Filter:', // (string) The label to show with the input 68 | placeholder: 'Enter keywords', // (string) The placeholder text to show in the input 69 | filterRule: 'contains', // (string) Either a named filter rule from above or a regular expression containing {{term}} as a placeholder 70 | searchGroups: false, // (true | false) If true, search option group labels and show an entire group on a match. 71 | autoReset: false, // (true | false) If true, clear the filter each time the widget menu is closed. 72 | width: null, // (number) Override default width set in css file (px). null will inherit 73 | debounceMS: 250, // (number) Number of milleseconds to wait between running the search handler. 74 | }, 75 | 76 | /** 77 | * Performs widget creation 78 | * Widget API has already set this.element and this.options for us 79 | * - Find the multiselect widget. 80 | * - Create the filter input 81 | * - Set up event handlers 82 | * - Insert in header 83 | * - Create text cache 84 | * - Override toggleState 85 | */ 86 | _create: function() { 87 | var opts = this.options; 88 | var $element = this.element; 89 | 90 | // get the multiselect instance 91 | this.instance = $element.data('ech-multiselect'); 92 | 93 | // store header; add filter class so the close/check all/uncheck all links can be positioned correctly 94 | this.$header = this.instance.$menu.find(headerSelector).addClass(hasFilterClass); 95 | 96 | // wrapper $element 97 | this.$input = $(document.createElement('input')) 98 | .attr({ 99 | placeholder: opts.placeholder, 100 | type: 'search', 101 | }) 102 | .css({width: (typeof opts.width === 'string') 103 | ? this.instance._parse2px(opts.width, this.$header).px + 'px' 104 | : (/\d/.test(opts.width) ? opts.width + 'px' : null), 105 | }); 106 | this._bindInputEvents(); 107 | // automatically reset the widget on close? 108 | if (this.options.autoReset) { 109 | $element.on('multiselectbeforeclose', $.proxy(this._reset, this)); 110 | } 111 | 112 | var $label = $(document.createElement('label')).text(opts.label).append(this.$input).addClass('ui-multiselect-filter-label'); 113 | this.$wrapper = $(document.createElement('div')) 114 | .addClass(filterClass) 115 | .append($label) 116 | .prependTo(this.$header); 117 | 118 | // If menu already opened, have to reset menu height since 119 | // addition of the filter input changes the header height calc. 120 | if (!!this.instance._isOpen) { 121 | this.instance._setMenuHeight(true); 122 | } 123 | 124 | // cache input values for searching 125 | this.updateCache(); 126 | 127 | // Change the normal _toggleChecked fxn behavior so that when checkAll/uncheckAll 128 | // is fired, only the currently displayed filtered inputs are checked if filter entered. 129 | var instance = this.instance; 130 | var filter = this.$input[0]; 131 | instance._oldToggleChecked = instance._toggleChecked; 132 | instance._toggleChecked = function(flag, group) { 133 | instance._oldToggleChecked(flag, group, !!filter.value); 134 | }; 135 | }, 136 | 137 | /** 138 | * Binds keyboard events to the input 139 | * This is where special behavior like ALT-R for reset is bound 140 | */ 141 | _bindInputEvents: function() { 142 | var $element = this.element; 143 | 144 | this.$input.on({ 145 | keydown: function(e) { 146 | // prevent the enter key from submitting the form / closing the widget 147 | if (e.which === 13) { 148 | e.preventDefault(); 149 | } else if (e.which === 27) { 150 | $element.multiselect('close'); 151 | e.preventDefault(); 152 | } else if (e.which === 9 && e.shiftKey) { 153 | $element.multiselect('close'); 154 | e.preventDefault(); 155 | } else if (e.altKey) { 156 | switch (e.which) { 157 | case 82: 158 | e.preventDefault(); 159 | $(this).val('').trigger('input', ''); 160 | break; 161 | case 65: 162 | $element.multiselect('checkAll'); 163 | break; 164 | case 85: 165 | $element.multiselect('uncheckAll'); 166 | break; 167 | case 70: 168 | $element.multiselect('flipAll'); 169 | break; 170 | case 76: 171 | $element.multiselect('instance').$labels.first().trigger('mouseenter'); 172 | break; 173 | } 174 | } 175 | }, 176 | input: $.proxy(debounce(this._handler, this.options.debounceMS), this), 177 | search: $.proxy(this._handler, this), 178 | }); 179 | }, 180 | 181 | /** 182 | * Handles searches as text is entered in the filter box. 183 | * Uses a text cache to speed up searching. 184 | * Debouncing is done to limit how often this is ran. 185 | * Alternate filter rules can be used. 186 | * Option group labels may be searched, also. 187 | * @param {event} e event object from original event. 188 | */ 189 | _handler: function(e) { 190 | var term = this.$input[0].value.toLowerCase().replace(/^\s+|\s+$/g, ''); 191 | var filterRule = this.options.filterRule || 'contains'; 192 | var regex = new RegExp( ( filterRules[filterRule] || filterRule ).replace('{{term}}', term.replace(rEscape, '\\$&')), 'i'); 193 | var searchGroups = !!this.options.searchGroups; 194 | var $checkboxes = this.instance.$checkboxes; 195 | var cache = this.cache; // Cached text() object 196 | 197 | this.$rows.toggleClass(hiddenClass, !!term); 198 | var filteredInputs = $checkboxes.children().map(function(x) { 199 | var elem = this; 200 | var $groupItems = $(elem); 201 | var groupShown = false; 202 | 203 | // Account for optgroups 204 | // If we are searching in option group labels and we match an optgroup label, 205 | // then show all its children and return all its inputs also. 206 | if (elem.classList.contains(optgroupClass)) { 207 | var $groupItems = $groupItems.find('li'); 208 | if (searchGroups && regex.test( cache[x] ) ) { 209 | elem.classList.remove(hiddenClass); 210 | $groupItems.removeClass(hiddenClass); 211 | return $groupItems.find('input').get(); 212 | } 213 | } 214 | 215 | return $groupItems.map(function(y) { 216 | if ( regex.test( cache[x + '.' + y] ) ) { 217 | // Show the opt group heading if needed 218 | if (!groupShown) { 219 | elem.classList.remove(hiddenClass); 220 | groupShown = true; 221 | } 222 | this.classList.remove(hiddenClass); 223 | return this.getElementsByTagName('input')[0]; 224 | } 225 | return null; 226 | }); 227 | }); 228 | if (term) { 229 | this._trigger('filter', e, filteredInputs); 230 | } 231 | if (!this.instance.options.listbox && this.instance._isOpen) { 232 | this.instance._setMenuHeight(true); 233 | this.instance.position(); 234 | } 235 | return; 236 | }, 237 | 238 | _reset: function() { 239 | this.$input.val(''); 240 | var event = document.createEvent('Event'); 241 | event.initEvent('reset', true, true); 242 | this.$input.get(0).dispatchEvent(event); 243 | this._handler(event); 244 | }, 245 | 246 | /** 247 | * Creates a text cache object from the widget options' text. 248 | * @param {Boolean} alsoRefresh causes the displayed search results to refresh. 249 | */ 250 | updateCache: function(alsoRefresh) { 251 | var cache = {}; // keys are like 0, 0.1, 1, 1.0, 1.1 etc. 252 | this.instance.$checkboxes.children().each(function(x) { 253 | var $element = $(this); 254 | // Account for optgroups 255 | if (this.classList.contains(optgroupClass)) { 256 | // Single number keys are the option labels 257 | cache[x] = this.getElementsByClassName(groupLabelClass)[0].textContent; 258 | $element = $element.find('li'); 259 | } 260 | $element.each(function(y) { 261 | cache[x + '.' + y] = this.textContent; 262 | }); 263 | }); 264 | this.cache = cache; 265 | this.$rows = this.instance.$checkboxes.find('li'); 266 | if (!!alsoRefresh) { 267 | this._handler(); 268 | } 269 | }, 270 | 271 | /** 272 | * @return {object} Returns the input wrapper div 273 | */ 274 | widget: function() { 275 | return this.$wrapper; 276 | }, 277 | 278 | /** 279 | * Destroys this widget 280 | */ 281 | destroy: function() { 282 | $.Widget.prototype.destroy.call(this); 283 | this.$input.val('').trigger('keyup').off('keydown input search'); 284 | this.instance.$menu.find(headerSelector).removeClass(hasFilterClass); 285 | this.$wrapper.remove(); 286 | }, 287 | }); 288 | })(jQuery); 289 | -------------------------------------------------------------------------------- /tests/unit/core.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | var form, data; 3 | 4 | QUnit.module("core", { 5 | beforeEach: function () { 6 | form = $('
    ').appendTo(body); 7 | data = null; 8 | }, 9 | afterEach: function () { 10 | form.remove(); 11 | } 12 | }); 13 | 14 | QUnit.test("init", function (assert) { 15 | el = $("select").multiselect(); 16 | var $header = header(); 17 | assert.ok($header.find('a.ui-multiselect-all').css('display') !== 'none', 'select all is visible'); 18 | assert.ok($header.find('a.ui-multiselect-none').css('display') !== 'none', 'select none is visible'); 19 | assert.ok($header.find('a.ui-multiselect-close').css('display') !== 'none', 'close link is visible'); 20 | assert.ok(menu().is(':hidden'), 'menu is hidden'); 21 | assert.ok(el.is(":hidden"), 'the original select is hidden'); 22 | assert.ok(el.attr('tabIndex') == 2, 'button inherited the correct tab index'); 23 | el.multiselect("destroy"); 24 | }); 25 | 26 | QUnit.test("name space separation", function (assert) { 27 | var el1 = $('') 28 | .appendTo(form) 29 | .multiselect(); 30 | 31 | var el2 = $('') 32 | .appendTo(form) 33 | .multiselect(); 34 | 35 | assert.notEqual(el1.multiselect('widget').find('input').eq(0).attr('id'), el2.multiselect('widget').find('input').eq(0).attr('id'), 'name spaces for multiple widgets are different'); 36 | 37 | el1.multiselect('destroy'); 38 | el2.multiselect('destroy'); 39 | }); 40 | 41 | QUnit.test("form submission", function (assert) { 42 | el = $('') 43 | .appendTo(form) 44 | .multiselect() 45 | .multiselect("checkAll"); 46 | 47 | data = form.serialize(); 48 | assert.equal(data, 'test=foo&test=bar', 'after checking all and serializing the form, the correct keys were serialized'); 49 | 50 | el.multiselect("uncheckAll"); 51 | data = form.serialize(); 52 | assert.equal(data.length, 0, 'after unchecking all and serializing the form, nothing was serialized'); 53 | 54 | // re-check all and destroy, exposing original select 55 | el.multiselect("checkAll").multiselect("destroy"); 56 | data = form.serialize(); 57 | assert.equal(data, 'test=foo&test=bar', 'after checking all, destroying the widget, and serializing the form, the correct keys were serialized'); 58 | }); 59 | 60 | QUnit.test("form submission, optgroups", function (assert) { 61 | el = $('') 62 | .appendTo(form) 63 | .multiselect() 64 | .multiselect("checkAll"); 65 | 66 | data = form.serialize(); 67 | assert.equal(data, 'test=foo&test=bar&test=baz&test=bax', 'after checking all and serializing the form, the correct keys were serialized'); 68 | 69 | el.multiselect("uncheckAll"); 70 | data = form.serialize(); 71 | assert.equal(data.length, 0, 'after unchecking all and serializing the form, nothing was serialized'); 72 | 73 | // re-check all and destroy, exposing original select 74 | el.multiselect("checkAll").multiselect("destroy"); 75 | data = form.serialize(); 76 | assert.equal(data, 'test=foo&test=bar&test=baz&test=bax', 'after checking all, destroying the widget, and serializing the form, the correct keys were serialized'); 77 | 78 | // reset option tags 79 | el.find("option").each(function () { 80 | this.selected = false; 81 | }); 82 | 83 | // test checking one option in both optgroups 84 | el.multiselect(); 85 | 86 | // finds the first input in each optgroup (assumes 2 options per optgroup) 87 | el.multiselect("widget").find('.ui-multiselect-checkboxes li:not(.ui-multiselect-optgroup-label) input:even').each(function (i) { 88 | this.click(); 89 | }); 90 | 91 | data = form.serialize(); 92 | assert.equal(data, 'test=foo&test=baz', 'after manually checking one input in each group, the correct two are serialized'); 93 | 94 | el.multiselect('destroy'); 95 | }); 96 | 97 | QUnit.test("form submission, single select", function (assert) { 98 | // Use an underlying single-select here. 99 | el = $('') 100 | .appendTo(form) 101 | .multiselect(); 102 | 103 | // select multiple radios to ensure that, in the underlying select, only one 104 | // will remain selected 105 | var radios = menu().find(":radio"); 106 | radios[0].click(); 107 | radios[2].click(); 108 | radios[1].click(); 109 | 110 | data = form.serialize(); 111 | assert.equal(data, 'test=bar', 'the form serializes correctly after clicking on multiple radio buttons'); 112 | assert.equal(radios.filter(":checked").length, 1, 'Only one radio button is selected'); 113 | 114 | // uncheckAll method 115 | el.multiselect("uncheckAll"); 116 | data = form.serialize(); 117 | assert.equal(data.length, 0, 'After unchecking all, nothing was serialized'); 118 | assert.equal(radios.filter(":checked").length, 0, 'No radio buttons are selected'); 119 | 120 | // checkAll method 121 | el.multiselect("checkAll"); 122 | data = form.serialize(); 123 | assert.equal(el.multiselect("getChecked").length, 1, 'After checkAll, only one radio is selected'); 124 | assert.equal(radios.filter(":checked").length, 1, 'One radio is selected'); 125 | 126 | // expose original 127 | el.multiselect("destroy"); 128 | data = form.serialize(); 129 | assert.equal(data, 'test=baz', 'after destroying the widget and serializing the form, the correct key was serialized: ' + data); 130 | }); 131 | 132 | QUnit.test("form reset, nothing pre-selected", function (assert) { 133 | var noneSelected = 'Please check something'; 134 | 135 | el = $('') 136 | .appendTo(form) 137 | .multiselect({ noneSelectedText: noneSelected, selectedList: 0 }) 138 | .multiselect("checkAll"); 139 | 140 | // trigger reset 141 | var done = assert.async(); 142 | form.trigger("reset"); 143 | 144 | setTimeout(function () { 145 | assert.equal(menu().find(":checked").length, 0, "no checked checkboxes"); 146 | assert.equal(button().text(), noneSelected, "none selected text"); 147 | el.multiselect('destroy'); 148 | done(); 149 | }, 10); 150 | }); 151 | 152 | QUnit.test("form reset, pre-selected options", function (assert) { 153 | el = $('') 154 | .appendTo(form) 155 | .multiselect({ selectedText: '# of # selected', selectedList: 0 }) 156 | .multiselect("uncheckAll"); 157 | 158 | // trigger reset 159 | var done = assert.async(); 160 | form.trigger("reset"); 161 | 162 | setTimeout(function () { 163 | assert.equal(menu().find(":checked").length, 2, "two checked checkboxes"); 164 | assert.equal(button().text(), "2 of 2 selected", "selected text"); 165 | el.multiselect('destroy'); 166 | done(); 167 | }, 10); 168 | }); 169 | 170 | })(jQuery); 171 | -------------------------------------------------------------------------------- /tests/unit/events.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | 3 | QUnit.module("events"); 4 | 5 | QUnit.test("multiselectopen", function(assert){ 6 | // inject widget 7 | el = $("").appendTo(body); 8 | el.multiselect({ 9 | open: function(e,ui){ 10 | assert.ok( true, 'option: multiselect("open") fires open callback' ); 11 | assert.equal(this, el[0], "option: context of callback"); 12 | assert.equal(e.type, 'multiselectopen', 'option: event type in callback'); 13 | assert.equal(menu().css("display"), 'block', 'menu display css property assert.equal block'); 14 | assert.propEqual(ui, {}, 'option: ui hash in callback'); 15 | } 16 | }) 17 | .on("multiselectopen", function(e,ui){ 18 | assert.ok(true, 'event: multiselect("open") fires multiselectopen event'); 19 | assert.equal(this, el[0], 'event: context of event'); 20 | assert.propEqual(ui, {}, 'event: ui hash'); 21 | }); 22 | 23 | // now try to open it.. 24 | el.multiselect("open"); 25 | 26 | // make sure the width of the menu and button are equivalent 27 | assert.equal( button().outerWidth(), menu().outerWidth(), 'button and menu widths are equivalent'); 28 | 29 | // close 30 | el.multiselect("close"); 31 | 32 | // make sure a click event on the button opens the menu as well. 33 | button().trigger("click"); 34 | el.multiselect("close"); 35 | 36 | // make sure a click event on a span inside the button opens the menu as well. 37 | button().find("span:first").trigger("click"); 38 | 39 | // reset for next test 40 | el.multiselect("destroy").remove(); 41 | 42 | // now try returning false prevent opening 43 | el = $("") 44 | .appendTo(body) 45 | .multiselect() 46 | .on("multiselectbeforeopen", function(){ 47 | assert.ok( true, "event: binding multiselectbeforeopen to return false (prevent from opening)" ); 48 | return false; 49 | }) 50 | .multiselect("open"); 51 | 52 | assert.ok( !el.multiselect("isOpen"), "multiselect is not open after multiselect('open')" ); 53 | el.multiselect("destroy").remove(); 54 | }); 55 | 56 | QUnit.test("multiselectclose", function(assert){ 57 | // inject widget 58 | el = $("").appendTo(body); 59 | el.multiselect({ 60 | close: function(e,ui){ 61 | assert.ok( true, 'option: multiselect("close") fires close callback' ); 62 | assert.equal(this, el[0], "option: context of callback"); 63 | assert.equal(e.type, 'multiselectclose', 'option: event type in callback'); 64 | assert.equal(menu().css("display"), 'none', 'menu display css property assert.equal none'); 65 | assert.propEqual(ui, {}, 'option: ui hash'); 66 | } 67 | }) 68 | .on("multiselectclose", function(e,ui){ 69 | assert.ok(true, 'multiselect("close") fires multiselectclose event'); 70 | assert.equal(this, el[0], 'event: context of event'); 71 | assert.propEqual(ui, {}, 'event: ui hash'); 72 | }) 73 | .multiselect("open") 74 | .multiselect("close") 75 | .multiselect("open"); 76 | 77 | // make sure a click event on the button closes the menu as well. 78 | button().click(); 79 | el.multiselect("open"); 80 | 81 | // make sure a click event on a span inside the button closes the menu as well. 82 | button().find("span:first").click(); 83 | 84 | // make sure that the menu is actually closed. see issue #68 85 | assert.ok( el.multiselect('isOpen') === false, 'menu is indeed closed' ); 86 | 87 | el.multiselect("destroy").remove(); 88 | }); 89 | 90 | QUnit.test("multiselectbeforeclose", function(assert){ 91 | // inject widget 92 | el = $("").appendTo(body); 93 | el.multiselect({ 94 | beforeclose: function(e,ui){ 95 | assert.ok( true, 'option: multiselect("beforeclose") fires close callback' ); 96 | assert.equal(this, el[0], "option: context of callback"); 97 | assert.equal(e.type, 'multiselectbeforeclose', 'option: event type in callback'); 98 | assert.propEqual(ui, {}, 'option: ui hash'); 99 | } 100 | }) 101 | .on("multiselectbeforeclose", function(e,ui){ 102 | assert.ok(true, 'multiselect("beforeclose") fires multiselectclose event'); 103 | assert.equal(this, el[0], 'event: context of event'); 104 | assert.propEqual(ui, {}, 'event: ui hash'); 105 | }) 106 | .multiselect("open") 107 | .multiselect("close"); 108 | 109 | el.multiselect("destroy").remove(); 110 | 111 | // test 'return false' functionality 112 | el = $("").appendTo(body); 113 | el.multiselect({ 114 | beforeclose: function(){ 115 | return false; 116 | } 117 | }); 118 | 119 | el.multiselect('open').multiselect('close'); 120 | assert.ok( menu().is(':visible') && el.multiselect("isOpen"), "returning false inside callback prevents menu from closing" ); 121 | el.multiselect("destroy").remove(); 122 | }); 123 | 124 | QUnit.test("multiselectclick with multiple widgets", function(assert) { 125 | var first = $("").appendTo(body).multiselect(); 126 | var second = $("").appendTo(body).multiselect(); 127 | assert.equal($('.ui-multiselect').length, 2, "two mutliselects are on the page"); 128 | first.multiselect("refresh"); 129 | second.multiselect("refresh"); 130 | var $label = $(second.multiselect("getLabels")[0]); 131 | var $wrongInput = $(first.multiselect("getLabels")[0]).find("input"); 132 | $label.click(); 133 | assert.equal($label.find("input").prop("checked"), true, "the input for that label should be checked"); 134 | assert.equal($wrongInput.prop("checked"), false, "the input for the corresponding label on the first widget should not be checked"); 135 | first.multiselect("destroy").remove(); 136 | second.multiselect("destroy").remove(); 137 | }); 138 | 139 | QUnit.test("multiselectclick", function(assert){ 140 | var times = 0; 141 | 142 | // inject widget 143 | el = $("") 144 | .appendTo(body); 145 | 146 | el.multiselect({ 147 | click: function(e,ui){ 148 | assert.ok(true, 'option: triggering the click event on the second checkbox fires the click callback' ); 149 | assert.equal(this, el[0], "option: context of callback"); 150 | assert.equal(e.type, 'multiselectclick', 'option: event type in callback'); 151 | assert.equal(ui.value, "2", "option: ui.value assert.equal"); 152 | assert.equal(ui.text, "Option 2", "option: ui.text assert.equal"); 153 | 154 | if(times === 0) { 155 | assert.equal(ui.checked, true, "option: ui.checked assert.equal"); 156 | } else if(times === 1) { 157 | assert.equal(ui.checked, false, "option: ui.checked assert.equal"); 158 | } 159 | } 160 | }) 161 | .on("multiselectclick", function(e,ui){ 162 | assert.ok(true, 'event: triggering the click event on the second checkbox triggers multiselectclick'); 163 | assert.equal(this, el[0], 'event: context of event'); 164 | assert.equal(ui.value, "2", "event: ui.value assert.equal"); 165 | assert.equal(ui.text, "Option 2", "event: ui.text assert.equal"); 166 | 167 | if(times === 0) { 168 | assert.equal(ui.checked, true, "option: ui.checked assert.equal"); 169 | } else if(times === 1) { 170 | assert.equal(ui.checked, false, "option: ui.checked assert.equal"); 171 | } 172 | }) 173 | .on("change", function(e){ 174 | if(++times === 1){ 175 | assert.equal(el.val().join(), "2", "event: select element val() within the change event is correct" ); 176 | } else { 177 | assert.equal(el.val(), null, "event: select element val() within the change event is correct" ); 178 | } 179 | 180 | assert.ok(true, "event: the select's change event fires"); 181 | }) 182 | .multiselect("open"); 183 | 184 | // trigger a click event on the input 185 | var lastInput = menu().find("input").last(); 186 | lastInput[0].click(); 187 | 188 | // trigger once more. 189 | lastInput[0].click(); 190 | 191 | // make sure it has focus 192 | assert.equal(true, lastInput[0] == document.activeElement, "The input has focus"); 193 | 194 | // make sure menu isn't closed automatically 195 | assert.equal( true, el.multiselect('isOpen'), 'menu stays open' ); 196 | 197 | el.multiselect("destroy").remove(); 198 | }); 199 | 200 | QUnit.test("multiselectcheckall", function(assert){ 201 | // inject widget 202 | el = $('').appendTo(body); 203 | 204 | el.multiselect({ 205 | checkAll: function(e,ui){ 206 | assert.ok( true, 'option: multiselect("checkAll") fires checkall callback' ); 207 | assert.equal(this, el[0], "option: context of callback"); 208 | assert.equal(e.type, 'multiselectcheckall', 'option: event type in callback'); 209 | assert.propEqual(ui, {}, 'option: ui hash in callback'); 210 | } 211 | }) 212 | .on("multiselectcheckall", function(e,ui){ 213 | assert.ok( true, 'event: multiselect("checkall") fires multiselectcheckall event' ); 214 | assert.equal(this, el[0], 'event: context of event'); 215 | assert.propEqual(ui, {}, 'event: ui hash'); 216 | }) 217 | .on("change", function(){ 218 | assert.ok(true, "event: the select's change event fires"); 219 | assert.equal( el.val().join(), "1,2", "event: select element val() within the change event is correct" ); 220 | }) 221 | .multiselect("open") 222 | .multiselect("checkAll"); 223 | 224 | assert.equal(menu().find("input").first()[0] == document.activeElement, true, "The first input has focus"); 225 | 226 | el.multiselect("destroy").remove(); 227 | }); 228 | 229 | QUnit.test("multiselectuncheckall", function(assert){ 230 | // inject widget 231 | el = $('').appendTo(body); 232 | 233 | el.multiselect({ 234 | uncheckAll: function(e,ui){ 235 | assert.ok( true, 'option: multiselect("uncheckAll") fires uncheckall callback' ); 236 | assert.equal(this, el[0], "option: context of callback"); 237 | assert.equal(e.type, 'multiselectuncheckall', 'option: event type in callback'); 238 | assert.propEqual(ui, {}, 'option: ui hash in callback'); 239 | } 240 | }) 241 | .on("multiselectuncheckall", function(e,ui){ 242 | assert.ok( true, 'event: multiselect("uncheckall") fires multiselectuncheckall event' ); 243 | assert.equal(this, el[0], 'event: context of event'); 244 | assert.propEqual(ui, {}, 'event: ui hash'); 245 | }) 246 | .on("change", function(){ 247 | assert.ok(true, "event: the select's change event fires"); 248 | assert.equal( el.val(), null, "event: select element val() within the change event is correct" ); 249 | }) 250 | .multiselect("open") 251 | .multiselect("uncheckAll"); 252 | 253 | assert.equal(menu().find("input").first()[0] == document.activeElement, true, "The first input has focus"); 254 | 255 | el.multiselect("destroy").remove(); 256 | }); 257 | 258 | 259 | QUnit.test("multiselectbeforeoptgrouptoggle", function(assert){ 260 | // inject widget 261 | el = $('') 262 | .appendTo(body); 263 | 264 | el.on("change", function(){ 265 | assert.ok(true, "the select's change event fires"); 266 | }) 267 | .multiselect({ 268 | groupsSelectable: true, 269 | beforeoptgrouptoggle: function(e,ui){ 270 | assert.equal(this, el[0], "option: context of callback"); 271 | assert.equal(e.type, 'multiselectbeforeoptgrouptoggle', 'option: event type in callback'); 272 | assert.equal(ui.label, "Set One", 'option: ui.label assert.equal'); 273 | assert.equal(ui.inputs.length, 2, 'option: number of inputs in the ui.inputs key'); 274 | } 275 | }) 276 | .on("multiselectbeforeoptgrouptoggle", function(e,ui){ 277 | assert.ok( true, 'option: multiselect("uncheckall") fires multiselectuncheckall event' ); 278 | assert.equal(this, el[0], 'event: context of event'); 279 | assert.equal(ui.label, "Set One", 'event: ui.label assert.equal'); 280 | assert.equal(ui.inputs.length, 2, 'event: number of inputs in the ui.inputs key'); 281 | }) 282 | .multiselect("open"); 283 | 284 | menu().find(".ui-multiselect-optgroup a").click(); 285 | 286 | el.multiselect("destroy").remove(); 287 | el = el.clone(); 288 | 289 | // test return false preventing checkboxes from activating 290 | el.on("change", function(){ 291 | assert.ok( true ); // should not fire 292 | }).multiselect({ 293 | beforeoptgrouptoggle: function(){ 294 | return false; 295 | }, 296 | // if this fires the expected count will be off. just a redundant way of checking that return false worked 297 | optgrouptoggle: function(){ 298 | assert.ok( true ); 299 | } 300 | }).appendTo( body ); 301 | 302 | var label = menu().find("li.ui-multiselect-optgroup-label a").click(); 303 | assert.equal( menu().find(":input:checked").length, 0, "when returning false inside the optgrouptoggle handler, no checkboxes are checked" ); 304 | el.multiselect("destroy").remove(); 305 | }); 306 | 307 | QUnit.test("multiselectoptgrouptoggle", function(assert){ 308 | // inject widget 309 | el = $('').appendTo(body); 310 | 311 | el.multiselect({ 312 | groupsSelectable: true, 313 | optgrouptoggle: function(e,ui){ 314 | assert.equal(this, el[0], "option: context of callback"); 315 | assert.equal(e.type, 'multiselectoptgrouptoggle', 'option: event type in callback'); 316 | assert.equal(ui.label, "Set One", 'option: ui.label assert.equal'); 317 | assert.equal(ui.inputs.length, 2, 'option: number of inputs in the ui.inputs key'); 318 | assert.equal(ui.checked, true, 'option: ui.checked assert.equal true'); 319 | } 320 | }) 321 | .on("multiselectoptgrouptoggle", function(e,ui){ 322 | assert.ok( true, 'option: multiselect("uncheckall") fires multiselectuncheckall event' ); 323 | assert.equal(this, el[0], 'event: context of event'); 324 | assert.equal(ui.label, "Set One", 'event: ui.label assert.equal'); 325 | assert.equal(ui.inputs.length, 2, 'event: number of inputs in the ui.inputs key'); 326 | assert.equal(ui.checked, true, 'event: ui.checked assert.equal true'); 327 | }) 328 | .multiselect("open"); 329 | 330 | // trigger native click event on optgroup 331 | menu().find(".ui-multiselect-optgroup a").click(); 332 | assert.equal(menu().find(":input:checked").length, 2, "both checkboxes are actually checked" ); 333 | 334 | assert.equal(menu().find("input").first()[0] == document.activeElement, true, "The first input has focus"); 335 | 336 | el.multiselect("destroy").remove(); 337 | }); 338 | 339 | })(jQuery); 340 | -------------------------------------------------------------------------------- /tests/unit/filter.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var el; var widget; var input; 3 | 4 | /** 5 | * @return {Object} 6 | */ 7 | function getVisible() { 8 | return widget.find('.ui-multiselect-checkboxes input:visible'); 9 | } 10 | 11 | /** 12 | * @return {Object} 13 | */ 14 | function getChecked() { 15 | return el.multiselect('getChecked'); 16 | } 17 | 18 | /** 19 | * @return {Object} 20 | */ 21 | function getSelected() { 22 | return el.children(':selected'); 23 | } 24 | 25 | /** 26 | * @param {string} term to search for 27 | */ 28 | function searchFor(term) { 29 | input.val(term).trigger('search'); 30 | } 31 | 32 | /** 33 | * Triggers click event 34 | */ 35 | function triggerClick() { 36 | this.click(); 37 | } 38 | 39 | /** 40 | * @param {string} term to search for 41 | * @param {number} expected numberof results 42 | * @param {string} message to display on failure 43 | */ 44 | function searchTest( term, expected, message ) { 45 | message || (message = 'searching for \'#\''); 46 | message = message.replace('#', term); 47 | searchFor(term); 48 | QUnit.assert.equal( getVisible().length, expected, message ); 49 | } 50 | 51 | QUnit.module('filter widget - multiple select', { 52 | beforeEach: function() { 53 | el = $(''); 65 | 66 | el.appendTo(document.body); 67 | el.multiselect(); 68 | el.multiselectfilter(); 69 | el.multiselect('open'); 70 | 71 | widget = el.multiselect('widget'); 72 | input = widget.find('.ui-multiselect-filter input'); 73 | }, 74 | 75 | afterEach: function() { 76 | el.multiselectfilter('destroy'); 77 | el.multiselect('destroy'); 78 | el.remove(); 79 | }, 80 | }); 81 | 82 | QUnit.test('defaults', function(assert) { 83 | assert.ok( input.is(':visible'), 'Filter input box is visible' ); 84 | }); 85 | 86 | QUnit.test('checked and unchecked', function(assert) { 87 | var labelCount = el.multiselect('getLabels').length; 88 | var uncheckedCount = el.multiselect('getUnchecked').length; 89 | var checkedCount = el.multiselect('getChecked').length; 90 | assert.equal(uncheckedCount, labelCount, 'Only the ten options are returned'); 91 | assert.equal(uncheckedCount + checkedCount, labelCount, 'Unchecked + checked should equal total inputs'); 92 | el.multiselect('refresh'); 93 | assert.equal(el.multiselect('getUnchecked').length, uncheckedCount, 'Search input still excluded after refresh'); 94 | }); 95 | 96 | QUnit.test('filtering by node text', function(assert) { 97 | searchTest( 'bbaa', 2); 98 | searchTest( 'bbaarr', 1); 99 | searchTest( ' bbaa ', 2, 'searching for \'#\' with whitespace'); 100 | searchTest( ' ', el.children().length, 'searching for an empty string'); 101 | searchTest( 'test', 5); 102 | searchTest( 'one hundred', 1); 103 | searchTest( 'with wor', 1); 104 | searchTest( ' with wor ', 1); 105 | 106 | $.each('$ ^ / : // { } | -'.split(' '), function( i, char ) { 107 | searchTest( char, 1 ); 108 | }); 109 | }); 110 | 111 | QUnit.test('filtering & checking', function(assert) { 112 | searchFor('ba'); 113 | 114 | getVisible().each(triggerClick); 115 | assert.equal(getChecked().length, 2, 'Two checkboxes are selected'); 116 | assert.equal(getSelected().length, 2, 'Two option tags are selected'); 117 | 118 | getVisible().each(triggerClick); 119 | assert.equal(getChecked().length, 0, 'After clicking again, no checkboxes are selected'); 120 | assert.equal(getSelected().length, 0, 'After clicking again, no tags are selected'); 121 | }); 122 | 123 | QUnit.test('checkAll / uncheckAll', function(assert) { 124 | searchFor('ba'); 125 | 126 | el.multiselect('checkAll'); 127 | assert.equal(getChecked().length, 2, 'checkAll: two checkboxes are selected'); 128 | assert.equal(getSelected().length, 2, 'checkAll: two option tags are selected'); 129 | 130 | el.multiselect('uncheckAll'); 131 | assert.equal(getChecked().length, 0, 'uncheckAll: no checkboxes are selected'); 132 | assert.equal(getSelected().length, 0, 'uncheckAll: no option tags are selected'); 133 | }); 134 | 135 | QUnit.test('combination of filtering/methods/click events', function(assert) { 136 | searchFor('ba'); 137 | 138 | getVisible().first().each(triggerClick); 139 | assert.equal(getChecked().length, 1, 'selecting 1 of multiple results (checked)'); 140 | assert.equal(getSelected().length, 1, 'selecting 1 of multiple results (selected)'); 141 | 142 | searchFor(' '); 143 | assert.equal(getChecked().length, 1, 'clearing search, only 1 is still selected'); 144 | el.multiselect('uncheckAll'); 145 | assert.equal(getChecked().length, 0, 'uncheckAll, nothing is selected (checked)'); 146 | assert.equal(getSelected().length, 0, 'uncheckedAll, nothing is selected (selected)'); 147 | 148 | searchFor('one hundred'); 149 | el.multiselect('checkAll'); 150 | assert.equal(getChecked().length, 1, 'checkAll on one matching result (checked)'); 151 | assert.equal(getSelected().length, 1, 'checkAll on one matching result (selected)'); 152 | 153 | searchFor('foo'); 154 | el.multiselect('checkAll'); 155 | assert.equal(getChecked().length, 2, 'checkAll on one matching result (checked)'); 156 | assert.equal(getSelected().length, 2, 'checkAll on one matching result (selected)'); 157 | }); 158 | 159 | QUnit.test('setting filter width', function(assert) { 160 | el.multiselectfilter('destroy'); 161 | el.multiselect().multiselectfilter({width: '100px'}); 162 | var input = $('.ui-multiselect-filter-label input'); 163 | assert.equal(input.width(), 100, 'width option sets width style on filter input'); 164 | }); 165 | })(jQuery); 166 | -------------------------------------------------------------------------------- /tests/unit/html.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | var el, widget, elems, btn, selectClone; 3 | 4 | QUnit.module("html", { 5 | beforeEach: function() { 6 | el = $("select").multiselect(); 7 | widget = el.multiselect("widget"); 8 | btn = el.multiselect("getButton"); 9 | selectClone = $("select").clone(); 10 | } 11 | }); 12 | 13 | QUnit.test("pull in optgroup's class", function(assert){ 14 | elems = widget.find('.ui-multiselect-optgroup'); 15 | assert.equal( elems.length, 3, 'There are three labels' ); 16 | 17 | elems.filter(":not(:last)").each( function() { 18 | assert.equal($(this).hasClass('ui-multiselect-optgroup'),true,'Default class is present when no extra class is defined'); 19 | }); 20 | elems.filter(":last").each( function() { 21 | assert.equal($(this).hasClass('ui-multiselect-optgroup'),true,'Default class is present when extra class is defined'); 22 | assert.equal($(this).hasClass('optgroupClass'),true,'Extra class is present'); 23 | }); 24 | 25 | }); 26 | 27 | QUnit.test("pull in options's class", function(assert){ 28 | assert.equal(widget.find('input[value="9"]').parents('li:first').hasClass('optionClass'),true,'Extra class is present'); 29 | }); 30 | 31 | QUnit.test("pull in select's ID and adds _ms if it exists", function(assert){ 32 | assert.equal(btn.attr("id"), el.attr("id") + "_ms", "Id is taken from select and _ms is appended"); 33 | }); 34 | 35 | QUnit.test("don't attempt to pull in select's ID and adds _ms if none exists", function(assert){ 36 | selectClone.attr("id", ""); 37 | var clonedEl = selectClone.multiselect(); 38 | var clonedBtn = clonedEl.multiselect("getButton"); 39 | assert.equal(clonedBtn.attr("id"), undefined, "No ID is added"); 40 | }); 41 | 42 | })(jQuery); 43 | -------------------------------------------------------------------------------- /tests/unit/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery UI MultiSelect Widget Unit Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
    14 |
    15 | 16 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /tests/unit/methods.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | 3 | QUnit.module("methods"); 4 | 5 | QUnit.test("open", function(assert){ 6 | el = $("select").multiselect().multiselect("open"); 7 | assert.ok( el.multiselect("isOpen"), "isOpen parameter true" ); 8 | assert.equal( menu().css("display"), "block", "Test display CSS property" ); 9 | el.multiselect("destroy"); 10 | }); 11 | 12 | QUnit.test("close", function(assert){ 13 | el = $("select").multiselect().multiselect("open").multiselect("close"); 14 | assert.ok( !el.multiselect("isOpen"), "isOpen parameter false" ); 15 | assert.equal( menu().css("display"), "none", "Test display CSS property" ); 16 | el.multiselect("destroy"); 17 | }); 18 | 19 | QUnit.test("enable", function(assert){ 20 | el = $("select").multiselect().multiselect("disable").multiselect("enable"); 21 | assert.ok( button().is(":disabled") === false, "Button is enabled" ); 22 | assert.ok( el.is(":disabled") === false, "Original select is enabled" ); 23 | el.multiselect("destroy"); 24 | }); 25 | 26 | QUnit.test("disable", function(assert){ 27 | // clone this one so the original is not affected 28 | el = $("select").clone(true).appendTo(body).multiselect().multiselect("disable"); 29 | assert.ok( button().is(":disabled"), 'Button is disabled'); 30 | assert.ok( el.is(":disabled"), 'Original select is disabled'); 31 | el.multiselect("destroy").remove(); 32 | }); 33 | 34 | QUnit.test("enabling w/ pre-disabled tags (#216)", function(assert){ 35 | el = $(''; 65 | 66 | el = $(html).appendTo("body").multiselect({ 67 | selectedList: 3, 68 | selectedListSeparator: ', ' 69 | }); 70 | 71 | el.multiselect("checkAll"); 72 | assert.equal(button().text(), 'foo "with quotes", bar, baz', '(plain text list separator & button text) after checkAll, button text is a list of all options in the select'); 73 | el.multiselect("destroy").remove(); 74 | 75 | el = $(html).appendTo("body").multiselect({ 76 | selectedList: 3, 77 | selectedListSeparator: ',
    ', 78 | htmlText: ['button'] 79 | }); 80 | 81 | el.multiselect("checkAll"); 82 | assert.equal(button().children('span').not('.ui-multiselect-open').html(),'foo "with quotes",
    bar,
    baz', 83 | '(html list separator & html button text) after checkAll, button html is a list of all options in the select on separate lines'); 84 | el.multiselect("destroy").remove(); 85 | 86 | el = $(html).appendTo("body").multiselect({ 87 | selectedList: 2, 88 | selectedListSeparator: ', ' 89 | }); 90 | 91 | el.multiselect("checkAll"); 92 | assert.equal(button().text(), '3 of 3 selected', 'after checkAll with a limited selectedList value, button value displays number of checked'); 93 | el.multiselect("destroy").remove(); 94 | }); 95 | 96 | QUnit.test("maxSelected", function (assert) { 97 | var html = ''; 98 | 99 | el = $(html).appendTo("body").multiselect({ 100 | maxSelected: 2 101 | }); 102 | 103 | var checkboxes = el.multiselect("widget").find(":checkbox"); 104 | checkboxes.eq(0).trigger('click'); 105 | checkboxes.eq(1).trigger('click'); 106 | checkboxes.eq(2).trigger('click'); 107 | 108 | assert.equal(menu().find("input").filter(":checked").length, 2, 'after clicking each checkbox, count of checked restored to maxSelected of 2'); 109 | 110 | el.multiselect('uncheckAll'); 111 | assert.equal(menu().find("input").filter(":checked").length, 0, 'after uncheckAll() count of checked is 0'); 112 | 113 | el.multiselect('flipAll'); 114 | assert.equal(menu().find("input").filter(":checked").length, 0, 'none checked - after flipAll() count of checked is 0'); 115 | 116 | checkboxes.eq(0).trigger('click'); 117 | checkboxes.eq(1).trigger('click'); 118 | el.multiselect('flipAll'); 119 | assert.equal(menu().find("input").filter(":checked").length, 1, '2 checked - after flipAll() count of checked is 1'); 120 | 121 | el.multiselect('checkAll'); 122 | assert.equal(menu().find("input").filter(":checked").length, 1 , 'after checkAll() count of checked is still 1'); 123 | 124 | el.multiselect('uncheckAll'); 125 | el.multiselect('checkAll'); 126 | assert.equal(menu().find("input").filter(":checked").length, 0 , 'after uncheckAll() + checkAll() count of checked is 0'); 127 | 128 | el.multiselect("destroy").remove(); 129 | }); 130 | 131 | function asyncSelectedList(useTrigger, message, assert) { 132 | var html = '', 133 | checkboxes; 134 | 135 | el = $(html).appendTo(body).multiselect({ 136 | selectedList: 2, 137 | selectedListSeparator: ', ' 138 | }); 139 | 140 | checkboxes = el.multiselect("widget").find(":checkbox"); 141 | var done = assert.async(); 142 | 143 | if (useTrigger) { 144 | checkboxes.eq(0).trigger('click'); 145 | checkboxes.eq(1).trigger('click'); 146 | } else { 147 | checkboxes.eq(0)[0].click(); 148 | checkboxes.eq(1)[0].click(); 149 | } 150 | 151 | setTimeout(function () { 152 | assert.equal(button().text(), 'foo, bar', message); 153 | el.multiselect("destroy").remove(); 154 | done(); 155 | }, 10); 156 | } 157 | 158 | QUnit.test("selectedList - manual trigger - jQuery", function (assert) { 159 | asyncSelectedList(true, 'manually checking items with trigger()', assert); 160 | }); 161 | 162 | QUnit.test("selectedList - manual trigger - native", function (assert) { 163 | asyncSelectedList(false, 'manually checking items with element.click()', assert); 164 | }); 165 | 166 | QUnit.test("selectedList - encoding", function (assert) { 167 | el = $('') 168 | .appendTo("body") 169 | .multiselect({ selectedList: 1 }); 170 | 171 | assert.equal(button().text(), 'A&E'); 172 | el.multiselect("destroy").remove(); 173 | }); 174 | 175 | QUnit.test("menuHeight", function (assert) { 176 | var height = 100; 177 | 178 | el = $("select").multiselect({ menuHeight: height }).multiselect("open"); 179 | assert.equal(height, menu().outerHeight(), 'height after opening property set to ' + height); 180 | 181 | // change height and re-test 182 | height = 300; 183 | el.multiselect("option", "menuHeight", height); 184 | assert.equal(height, menu().outerHeight(), 'changing value through api to ' + height); 185 | 186 | el.multiselect("destroy"); 187 | }); 188 | 189 | QUnit.test("buttonWidth", function (assert) { 190 | var buttonWidth = 321; 191 | 192 | el = $("select").multiselect({ buttonWidth: '>=' + buttonWidth }).multiselect("open"); 193 | assert.equal(button().outerWidth(), buttonWidth, 'outerWidth of button is ' + buttonWidth); 194 | 195 | // change width and re-test 196 | buttonWidth = 351; 197 | el.multiselect("option", "buttonWidth", '>=' + buttonWidth); 198 | assert.equal(button().outerWidth(), buttonWidth, 'changing value through api to ' + buttonWidth); 199 | 200 | // change width to something that should fail. 201 | buttonWidth = 10; 202 | el.multiselect("option", "buttonWidth", '>=' + buttonWidth); 203 | var outerWidth = button().outerWidth(); 204 | assert.ok(buttonWidth !== outerWidth, 'changing value through api to ' + buttonWidth + ' (too small), outerWidth is actually ' + outerWidth); 205 | 206 | // Reference: https://www.wired.com/2010/12/why-percentage-based-designs-dont-work-in-every-browser/ 207 | buttonWidth = "50%"; 208 | el.multiselect("option", "buttonWidth", '>=' + buttonWidth); 209 | var outerWidthX2 = Math.floor(button().outerWidth() * 2); // Double to reduce chance of fractions 210 | var parentWidth = Math.floor(el.parent().outerWidth()); 211 | assert.ok(Math.abs(outerWidthX2 - parentWidth) <= 1, 'changing value to 50%'); // Off by 1 is assert.ok due to floating point rounding discrepancies between browsers. 212 | 213 | buttonWidth = "351px"; 214 | el.multiselect("option", "buttonWidth", '>=' + buttonWidth); 215 | assert.equal(button().outerWidth(), 351, 'buttonWidth supports strings suffixed with px as well as integer px values'); 216 | 217 | buttonWidth = "22em"; 218 | el.multiselect("option", "buttonWidth", '>=' + buttonWidth); 219 | assert.equal(button().outerWidth(), 22 * 16, 'buttonWidth supports strings suffixed with "em" unit as well as integer px values'); 220 | 221 | el.multiselect("destroy"); 222 | }); 223 | 224 | QUnit.test("menuWidth", function (assert) { 225 | var width = 50; 226 | 227 | el = $("select").multiselect({ buttonWidth: 100, menuWidth: width }).multiselect("open"); 228 | 229 | assert.equal(menu().parent().find(".ui-multiselect-menu").outerWidth(), width, 'width after opening, property set to ' + width); 230 | 231 | // change width and re-test 232 | width = 300; 233 | el.multiselect("option", "menuWidth", width).multiselect('refresh'); 234 | assert.equal(menu().parent().find(".ui-multiselect-menu").outerWidth(), width, 'changing value through api to ' + width); 235 | 236 | width = "3in"; 237 | el.multiselect("option", "menuWidth", width).multiselect('refresh'); 238 | assert.equal(menu().parent().find(".ui-multiselect-menu").outerWidth(), 3 * 96.0, 'menuWidth supports strings suffixed with "in" unit as well as integer "px" values'); 239 | 240 | el.multiselect("destroy"); 241 | }); 242 | 243 | QUnit.test("checkAllText", function (assert) { 244 | var text = "foo"; 245 | 246 | el = $("select").multiselect({ header: ['checkAll','uncheckAll','flipAll'], linkInfo: {checkAll: {text: text}} }); 247 | assert.equal(menu().find(".ui-multiselect-all").text(), text, 'check all link reads ' + text); 248 | 249 | // set through option 250 | text = "bar"; 251 | el.multiselect("option", "checkAllText", "bar"); 252 | assert.equal(menu().find(".ui-multiselect-all").text(), text, 'check all link reads ' + text); 253 | 254 | el.multiselect("destroy"); 255 | 256 | // set through dict 257 | text = "foo"; 258 | el = $("select").multiselect({ header: {'foo':'checkAll'} }); 259 | assert.equal(menu().find(".ui-multiselect-all").text(), text, 'check all link reads through obj ' + text); 260 | 261 | el.multiselect("destroy"); 262 | }); 263 | 264 | QUnit.test("uncheckAllText", function (assert) { 265 | var text = "foo"; 266 | 267 | el = $("select").multiselect({ header: ['checkAll','uncheckAll','flipAll'], linkInfo: {uncheckAll: {text: text}} }); 268 | assert.equal(menu().find(".ui-multiselect-none").text(), text, 'check all link reads ' + text); 269 | 270 | // set through option 271 | text = "bar"; 272 | el.multiselect("option", "uncheckAllText", "bar"); 273 | assert.equal(menu().find(".ui-multiselect-none").text(), text, 'changing value through api to ' + text); 274 | 275 | el.multiselect("destroy"); 276 | 277 | // set through dict 278 | text = "foo"; 279 | el = $("select").multiselect({ header: {'foo':'uncheckAll'} }); 280 | assert.equal(menu().find(".ui-multiselect-none").text(), text, 'uncheck all link reads through obj ' + text); 281 | 282 | el.multiselect("destroy"); 283 | }); 284 | 285 | QUnit.test("flipAllText", function (assert) { 286 | var text = "foo"; 287 | 288 | el = $("select").multiselect({ header: ['checkAll','uncheckAll','flipAll'], linkInfo: {flipAll: {text: text}} }); 289 | assert.equal(menu().find(".ui-multiselect-flip").text(), text, 'flip all link reads ' + text); 290 | 291 | // set through option 292 | text = "bar"; 293 | el.multiselect("option", "flipAllText", "bar"); 294 | assert.equal(menu().find(".ui-multiselect-flip").text(), text, 'changing value through api to ' + text); 295 | 296 | el.multiselect("destroy"); 297 | 298 | // set through dict 299 | text = "foo"; 300 | el = $("select").multiselect({ header: {'foo':'flipAll'} }); 301 | assert.equal(menu().find(".ui-multiselect-flip").text(), text, 'flip all link reads through obj ' + text); 302 | 303 | el.multiselect("destroy"); 304 | }); 305 | 306 | QUnit.test("collapseAllText", function (assert) { 307 | var text = "foo"; 308 | 309 | el = $("select").multiselect({ header: ['collapseAll','expandAll'], linkInfo: {collapseAll: {text: text}} }); 310 | assert.equal(menu().find(".ui-multiselect-collapseall").text(), text, 'collapse all link reads ' + text); 311 | 312 | // set through option 313 | text = "bar"; 314 | el.multiselect("option", "collapseAllText", "bar"); 315 | assert.equal(menu().find(".ui-multiselect-collapseall").text(), text, 'changing value through api to ' + text); 316 | 317 | el.multiselect("destroy"); 318 | 319 | // set through dict 320 | text = "foo"; 321 | el = $("select").multiselect({ header: {'foo':'collapseAll'} }); 322 | assert.equal(menu().find(".ui-multiselect-collapseall").text(), text, 'collapse all link reads through obj ' + text); 323 | 324 | el.multiselect("destroy"); 325 | }); 326 | 327 | QUnit.test("expandAllText", function (assert) { 328 | var text = "foo"; 329 | 330 | el = $("select").multiselect({ header: ['collapseAll','expandAll'], linkInfo: {expandAll: {text: text}} }); 331 | assert.equal(menu().find(".ui-multiselect-expandall").text(), text, 'expand all link reads ' + text); 332 | 333 | // set through option 334 | text = "bar"; 335 | el.multiselect("option", "expandAllText", "bar"); 336 | assert.equal(menu().find(".ui-multiselect-expandall").text(), text, 'changing value through api to ' + text); 337 | 338 | el.multiselect("destroy"); 339 | 340 | // set through dict 341 | text = "foo"; 342 | el = $("select").multiselect({ header: {'foo':'expandAll'} }); 343 | assert.equal(menu().find(".ui-multiselect-expandall").text(), text, 'expand all link reads through obj ' + text); 344 | 345 | el.multiselect("destroy"); 346 | }); 347 | 348 | QUnit.test("autoOpen", function (assert) { 349 | el = $("select").multiselect({ autoOpen: false }); 350 | assert.ok(menu().is(":hidden"), 'menu is hidden with autoOpen off'); 351 | el.multiselect("destroy"); 352 | 353 | el = $("select").multiselect({ autoOpen: true }); 354 | assert.ok(menu().is(":visible"), 'menu is visible with autoOpen on'); 355 | el.multiselect("destroy"); 356 | 357 | el = $("select").multiselect({ autoOpen: false, listbox: true }); 358 | assert.ok(menu().is(":visible"), 'menu is visible with autoOpen off and list box option enabled'); 359 | el.multiselect("destroy"); 360 | }); 361 | 362 | QUnit.test("multiple (false - single select)", function (assert) { 363 | $("select").removeAttr("multiple"); 364 | el = $("select").multiselect({ multiple: false, header: ['checkAll', 'uncheckAll', 'flipAll'] }); 365 | 366 | // get some references 367 | var $menu = menu(), $header = header(); 368 | 369 | assert.ok($header.find('a.ui-multiselect-all').is(':hidden'), 'select all link is hidden'); 370 | assert.ok($header.find('a.ui-multiselect-flip').is(':hidden'), 'flip all link is hidden'); 371 | assert.ok($header.find('a.ui-multiselect-none').is(':hidden'), 'select none link is hidden'); 372 | assert.ok($header.find('a.ui-multiselect-close').css('display') !== 'none', 'close link is visible'); 373 | assert.ok(!$menu.find(":checkbox").length, 'no checkboxes are present'); 374 | assert.ok($menu.find(":radio").length > 0, 'but radio boxes are'); 375 | 376 | // simulate click on ALL radios 377 | var radios = $menu.find(":radio").trigger("click"); 378 | 379 | // at the end of that, only one radio should be checked and the menu closed 380 | assert.equal(radios.filter(":checked").length, 1, 'After checking all radios, only one is actually checked'); 381 | assert.equal(false, el.multiselect('isOpen'), 'Menu is closed'); 382 | 383 | // uncheck boxes... should only be one 384 | radios.filter(":checked").trigger("click"); 385 | 386 | // method calls 387 | el.multiselect("checkAll"); 388 | assert.equal($menu.find("input:radio:checked").length, 1, 'After checkAll method call only one is actually checked'); 389 | 390 | el.multiselect("uncheckAll"); 391 | assert.equal($menu.find("input:radio:checked").length, 0, 'After uncheckAll method nothing is checked'); 392 | 393 | // check/uncheck all links 394 | assert.equal($menu.find(".ui-multiselect-all, ui-multiselect-none").filter(":visible").length, 0, "Check/uncheck all links don't exist"); 395 | 396 | el.multiselect("destroy"); 397 | $("select").attr("multiple", "multiple"); 398 | }); 399 | 400 | QUnit.test("multiple (changing dynamically)", function (assert) { 401 | el = $('') 402 | .appendTo("body") 403 | .multiselect(); 404 | 405 | el.multiselect("option", "multiple", false); 406 | assert.equal(el[0].multiple, false, "When changing a multiple select to a single select, the select element no longer has the multiple property"); 407 | assert.equal(menu().hasClass("ui-multiselect-single"), true, "...and the menu now has the single select class"); 408 | assert.equal(menu().find('input[type="radio"]').length, 1, "...and the checkbox is now a radio button"); 409 | 410 | el.multiselect("option", "multiple", true); 411 | assert.equal(el[0].multiple, true, "When changing a single select to a multiple select, the select element has the multiple property"); 412 | assert.equal(menu().hasClass("ui-multiselect-single"), false, "...and the menu doesn't have the single select class"); 413 | assert.equal(menu().find('input[type="checkbox"]').length, 1, "...and the radio button is now a checkbox"); 414 | 415 | el.multiselect("destroy").remove(); 416 | }); 417 | 418 | QUnit.test("classes", function (assert) { 419 | var classname = 'foo'; 420 | 421 | el = $("select").multiselect({ classes: classname }); 422 | var $button = button(), $widget = menu(); 423 | 424 | assert.equal($widget.hasClass(classname), true, 'menu has the class ' + classname); 425 | assert.equal($button.hasClass(classname), true, 'button has the class ' + classname); 426 | 427 | // change it up 428 | var newclass = 'bar'; 429 | el.multiselect("option", "classes", newclass); 430 | assert.equal($widget.hasClass(newclass), true, 'menu has the new class ' + newclass); 431 | assert.equal($button.hasClass(newclass), true, 'button has the new class ' + newclass); 432 | assert.equal($button.hasClass(classname), false, 'menu no longer has the class ' + classname); 433 | assert.equal($button.hasClass(classname), false, 'button no longer has the class ' + classname); 434 | el.multiselect("destroy"); 435 | }); 436 | 437 | QUnit.test("header", function (assert) { 438 | function countLinks() { 439 | return header().find("a").length; 440 | } 441 | 442 | // default 443 | el = $("select").multiselect({ autoOpen: true }); 444 | assert.ok(header().is(':visible'), "default config: header is visible"); 445 | el.multiselect("option", "header", false); 446 | assert.ok(header().is(':hidden'), "after changing header option on default config: header is no longer visible"); 447 | 448 | // test for all links within the default header 449 | assert.equal(countLinks(), 3, "number of links in the default header config"); 450 | 451 | el.multiselect("destroy"); 452 | 453 | // create again, this time header false 454 | el = $("select").multiselect({ header: false, autoOpen: true }); 455 | assert.ok(header().is(':hidden'), "init with header false: header is not visible"); 456 | el.multiselect("option", "header", true); 457 | assert.ok(header().is(':visible'), "after changing header option: header is visible"); 458 | 459 | el.multiselect("destroy"); 460 | 461 | // create again, this time custom header 462 | el = $("select").multiselect({ header: "hai guyz", autoOpen: true }); 463 | assert.equal(header().text(), "hai guyz", "header assert.equal custom text"); 464 | assert.equal(countLinks(), 1, "number of links in the custom header config (should be close button)"); 465 | 466 | el.multiselect("destroy"); 467 | }); 468 | 469 | QUnit.test("selectedListSeparator", function (assert) { 470 | el = $("select").multiselect({ selectedListSeparator: "
    ", selectedList: 15 }); 471 | el.multiselect("checkAll"); 472 | var text = $(button()).text(); 473 | var matched = []; 474 | assert.equal(text.indexOf(","), -1, "There are no commas in the button text"); 475 | matched = text.match(/\/g); 476 | assert.equal(matched.length, 8, "The 9 selected values are joined by
    tags"); 477 | el.multiselect("option", "selectedListSeparator", ", "); 478 | text = $(button()).text(); 479 | matched = text.match(/\,/g); 480 | assert.equal(matched.length, 8, "The 9 selected values are joined by commas again after calling the option method"); 481 | el.multiselect("destroy"); 482 | }); 483 | })(jQuery); 484 | -------------------------------------------------------------------------------- /tests/unit/setup.js: -------------------------------------------------------------------------------- 1 | var el; 2 | var body = document.body; 3 | 4 | function button(){ 5 | return el.next(); 6 | } 7 | 8 | function menu(){ 9 | return el.multiselect("widget"); 10 | } 11 | 12 | function header(){ 13 | return menu().find('.ui-multiselect-header'); 14 | } -------------------------------------------------------------------------------- /tests/visual/form-reset.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery MultiSelect Plugin Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 27 | 28 | 29 | 30 |

    Form Reset Tests

    31 | 32 |

    None selected text

    33 |
    34 | 35 | 41 | 42 |

    Selected text - pre-selected values

    43 | 49 | 50 |

    Single select - none selected text

    51 | 57 | 58 | 59 |

    Single select - pre-selected value

    60 | 66 | 67 |

    68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /tests/visual/formsubmission-single.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery MultiSelect Plugin Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Form Submission Test (Single Select)

    16 |

    Testing to ensure the correct values are actually passed when the form is submitted.

    17 | 18 |
    19 | 
    20 | 
    21 | 22 | 23 | 29 | 30 | 36 | 37 |
    38 |
    39 | 40 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /tests/visual/formsubmission.cfm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | jQuery MultiSelect Plugin Tests 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

    Form Submission Test

    19 |

    Testing to ensure the correct values are actually passed when the form is submitted.

    20 | 21 | 22 | 23 |
    24 | 30 | 31 | 37 | 38 |
    39 |
    40 | 41 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /tests/visual/formsubmission.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery MultiSelect Plugin Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Form Submission Test

    16 |

    Testing to ensure the correct values are actually passed when the form is submitted.

    17 | 18 |
    19 | 25 | 26 | 32 | 33 |
    34 |
    35 | 36 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /tests/visual/formsubmission.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery MultiSelect Plugin Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

    Form Submission Test

    16 |

    Testing to ensure the correct values are actually passed when the form is submitted.

    17 | 18 |
    19 | 
    20 | 
    21 | 22 |
    23 | 29 | 30 | 36 | 37 |
    38 |
    39 | 40 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /tests/visual/style.css: -------------------------------------------------------------------------------- 1 | body { font:12px Helvetica, arial, sans-serif } 2 | h1, h2, p { margin:10px 0 } 3 | .hidden { visibility:hidden } 4 | 5 | 6 | .message { padding:10px; margin:15px 0; display:block; text-align:left } 7 | .message-title { font-weight:bold; font-size:1.25em } 8 | .message-body { margin-top:4px } 9 | .error, .notice, .success { padding:.8em; margin-bottom:1em; border:2px solid #ddd } 10 | .error { background:#FBE3E4; color:#8a1f11; border-color:#FBC2C4 } 11 | .notice { background:#FFF6BF; color:#514721; border-color:#FFD324 } 12 | .success { background:#E6EFC2; color:#264409; border-color:#C6D880 } 13 | .error a { color:#8a1f11 } 14 | .notice a { color:#514721 } 15 | .success a { color:#264409 } 16 | 17 | -------------------------------------------------------------------------------- /tests/visual/widget-containers.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery MultiSelect Plugin Tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 26 | 27 | 28 | 29 |

    Widget Container Tests

    30 |

    Testing the MultiSelect when placed inside other UI widgets.

    31 | 32 |

    Default

    33 |

    Rendered outside a widget (for comparison):

    34 |
    35 | 41 |
    42 | 43 | 44 |

    Widgets

    45 |

    Inside widgets:

    46 | 47 | 48 |
    49 |
    50 | 56 | 57 | 58 |

    Datepicker:

    59 |
    60 |
    61 | 62 | 63 | 64 |
    65 | 69 | 70 |
    71 |
    72 | 78 |
    79 |
    80 | 81 |
    82 | Select the first tab to view the test. 83 |
    84 |
    85 | 86 | 87 |
    88 |

    Inside an accordion

    89 |
    90 |
    91 | 97 |
    98 |
    99 |

    test

    100 |
    Click on the first item to view the test.
    101 |
    102 | 103 | 104 | 105 | 106 | --------------------------------------------------------------------------------