index.php
1 year ago
select2-spinner.gif
1 year ago
select2.css
1 year ago
select2.js
1 year ago
select2.min.js
1 year ago
select2.png
1 year ago
select2x2.png
1 year ago
select2.js
5419 lines
| 1 | /* |
| 2 | Copyright 2012 Igor Vaynberg |
| 3 | |
| 4 | Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014 |
| 5 | |
| 6 | This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU |
| 7 | General Public License version 2 (the "GPL License"). You may choose either license to govern your |
| 8 | use of this software only upon the condition that you accept all of the terms of either the Apache |
| 9 | License or the GPL License. |
| 10 | |
| 11 | You may obtain a copy of the Apache License and the GPL License at: |
| 12 | |
| 13 | http://www.apache.org/licenses/LICENSE-2.0 |
| 14 | http://www.gnu.org/licenses/gpl-2.0.html |
| 15 | |
| 16 | Unless required by applicable law or agreed to in writing, software distributed under the |
| 17 | Apache License or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
| 18 | CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for |
| 19 | the specific language governing permissions and limitations under the Apache License and the GPL License. |
| 20 | */ |
| 21 | ( function ( $ ) { |
| 22 | if ( typeof $.fn.each2 == 'undefined' ) { |
| 23 | $.extend( $.fn, { |
| 24 | /* |
| 25 | * 4-10 times faster .each replacement |
| 26 | * use it carefully, as it overrides jQuery context of element on each iteration |
| 27 | */ |
| 28 | each2: function ( c ) { |
| 29 | var j = $( [ 0 ] ), |
| 30 | i = -1, |
| 31 | l = this.length; |
| 32 | while ( |
| 33 | ++i < l && |
| 34 | ( j.context = j[ 0 ] = this[ i ] ) && |
| 35 | c.call( j[ 0 ], i, j ) !== false //"this"=DOM, i=index, j=jQuery object |
| 36 | ); |
| 37 | return this; |
| 38 | }, |
| 39 | } ); |
| 40 | } |
| 41 | } )( jQuery ); |
| 42 | |
| 43 | ( function ( $, undefined ) { |
| 44 | 'use strict'; |
| 45 | /*global document, window, jQuery, console */ |
| 46 | |
| 47 | if ( window.Select2 !== undefined ) { |
| 48 | return; |
| 49 | } |
| 50 | |
| 51 | var AbstractSelect2, |
| 52 | SingleSelect2, |
| 53 | MultiSelect2, |
| 54 | nextUid, |
| 55 | sizer, |
| 56 | lastMousePosition = { x: 0, y: 0 }, |
| 57 | $document, |
| 58 | scrollBarDimensions, |
| 59 | KEY = { |
| 60 | TAB: 9, |
| 61 | ENTER: 13, |
| 62 | ESC: 27, |
| 63 | SPACE: 32, |
| 64 | LEFT: 37, |
| 65 | UP: 38, |
| 66 | RIGHT: 39, |
| 67 | DOWN: 40, |
| 68 | SHIFT: 16, |
| 69 | CTRL: 17, |
| 70 | ALT: 18, |
| 71 | PAGE_UP: 33, |
| 72 | PAGE_DOWN: 34, |
| 73 | HOME: 36, |
| 74 | END: 35, |
| 75 | BACKSPACE: 8, |
| 76 | DELETE: 46, |
| 77 | isArrow: function ( k ) { |
| 78 | k = k.which ? k.which : k; |
| 79 | switch ( k ) { |
| 80 | case KEY.LEFT: |
| 81 | case KEY.RIGHT: |
| 82 | case KEY.UP: |
| 83 | case KEY.DOWN: |
| 84 | return true; |
| 85 | } |
| 86 | return false; |
| 87 | }, |
| 88 | isControl: function ( e ) { |
| 89 | var k = e.which; |
| 90 | switch ( k ) { |
| 91 | case KEY.SHIFT: |
| 92 | case KEY.CTRL: |
| 93 | case KEY.ALT: |
| 94 | return true; |
| 95 | } |
| 96 | |
| 97 | if ( e.metaKey ) return true; |
| 98 | |
| 99 | return false; |
| 100 | }, |
| 101 | isFunctionKey: function ( k ) { |
| 102 | k = k.which ? k.which : k; |
| 103 | return k >= 112 && k <= 123; |
| 104 | }, |
| 105 | }, |
| 106 | MEASURE_SCROLLBAR_TEMPLATE = |
| 107 | "<div class='select2-measure-scrollbar'></div>", |
| 108 | DIACRITICS = { |
| 109 | '\u24B6': 'A', |
| 110 | '\uFF21': 'A', |
| 111 | '\u00C0': 'A', |
| 112 | '\u00C1': 'A', |
| 113 | '\u00C2': 'A', |
| 114 | '\u1EA6': 'A', |
| 115 | '\u1EA4': 'A', |
| 116 | '\u1EAA': 'A', |
| 117 | '\u1EA8': 'A', |
| 118 | '\u00C3': 'A', |
| 119 | '\u0100': 'A', |
| 120 | '\u0102': 'A', |
| 121 | '\u1EB0': 'A', |
| 122 | '\u1EAE': 'A', |
| 123 | '\u1EB4': 'A', |
| 124 | '\u1EB2': 'A', |
| 125 | '\u0226': 'A', |
| 126 | '\u01E0': 'A', |
| 127 | '\u00C4': 'A', |
| 128 | '\u01DE': 'A', |
| 129 | '\u1EA2': 'A', |
| 130 | '\u00C5': 'A', |
| 131 | '\u01FA': 'A', |
| 132 | '\u01CD': 'A', |
| 133 | '\u0200': 'A', |
| 134 | '\u0202': 'A', |
| 135 | '\u1EA0': 'A', |
| 136 | '\u1EAC': 'A', |
| 137 | '\u1EB6': 'A', |
| 138 | '\u1E00': 'A', |
| 139 | '\u0104': 'A', |
| 140 | '\u023A': 'A', |
| 141 | '\u2C6F': 'A', |
| 142 | '\uA732': 'AA', |
| 143 | '\u00C6': 'AE', |
| 144 | '\u01FC': 'AE', |
| 145 | '\u01E2': 'AE', |
| 146 | '\uA734': 'AO', |
| 147 | '\uA736': 'AU', |
| 148 | '\uA738': 'AV', |
| 149 | '\uA73A': 'AV', |
| 150 | '\uA73C': 'AY', |
| 151 | '\u24B7': 'B', |
| 152 | '\uFF22': 'B', |
| 153 | '\u1E02': 'B', |
| 154 | '\u1E04': 'B', |
| 155 | '\u1E06': 'B', |
| 156 | '\u0243': 'B', |
| 157 | '\u0182': 'B', |
| 158 | '\u0181': 'B', |
| 159 | '\u24B8': 'C', |
| 160 | '\uFF23': 'C', |
| 161 | '\u0106': 'C', |
| 162 | '\u0108': 'C', |
| 163 | '\u010A': 'C', |
| 164 | '\u010C': 'C', |
| 165 | '\u00C7': 'C', |
| 166 | '\u1E08': 'C', |
| 167 | '\u0187': 'C', |
| 168 | '\u023B': 'C', |
| 169 | '\uA73E': 'C', |
| 170 | '\u24B9': 'D', |
| 171 | '\uFF24': 'D', |
| 172 | '\u1E0A': 'D', |
| 173 | '\u010E': 'D', |
| 174 | '\u1E0C': 'D', |
| 175 | '\u1E10': 'D', |
| 176 | '\u1E12': 'D', |
| 177 | '\u1E0E': 'D', |
| 178 | '\u0110': 'D', |
| 179 | '\u018B': 'D', |
| 180 | '\u018A': 'D', |
| 181 | '\u0189': 'D', |
| 182 | '\uA779': 'D', |
| 183 | '\u01F1': 'DZ', |
| 184 | '\u01C4': 'DZ', |
| 185 | '\u01F2': 'Dz', |
| 186 | '\u01C5': 'Dz', |
| 187 | '\u24BA': 'E', |
| 188 | '\uFF25': 'E', |
| 189 | '\u00C8': 'E', |
| 190 | '\u00C9': 'E', |
| 191 | '\u00CA': 'E', |
| 192 | '\u1EC0': 'E', |
| 193 | '\u1EBE': 'E', |
| 194 | '\u1EC4': 'E', |
| 195 | '\u1EC2': 'E', |
| 196 | '\u1EBC': 'E', |
| 197 | '\u0112': 'E', |
| 198 | '\u1E14': 'E', |
| 199 | '\u1E16': 'E', |
| 200 | '\u0114': 'E', |
| 201 | '\u0116': 'E', |
| 202 | '\u00CB': 'E', |
| 203 | '\u1EBA': 'E', |
| 204 | '\u011A': 'E', |
| 205 | '\u0204': 'E', |
| 206 | '\u0206': 'E', |
| 207 | '\u1EB8': 'E', |
| 208 | '\u1EC6': 'E', |
| 209 | '\u0228': 'E', |
| 210 | '\u1E1C': 'E', |
| 211 | '\u0118': 'E', |
| 212 | '\u1E18': 'E', |
| 213 | '\u1E1A': 'E', |
| 214 | '\u0190': 'E', |
| 215 | '\u018E': 'E', |
| 216 | '\u24BB': 'F', |
| 217 | '\uFF26': 'F', |
| 218 | '\u1E1E': 'F', |
| 219 | '\u0191': 'F', |
| 220 | '\uA77B': 'F', |
| 221 | '\u24BC': 'G', |
| 222 | '\uFF27': 'G', |
| 223 | '\u01F4': 'G', |
| 224 | '\u011C': 'G', |
| 225 | '\u1E20': 'G', |
| 226 | '\u011E': 'G', |
| 227 | '\u0120': 'G', |
| 228 | '\u01E6': 'G', |
| 229 | '\u0122': 'G', |
| 230 | '\u01E4': 'G', |
| 231 | '\u0193': 'G', |
| 232 | '\uA7A0': 'G', |
| 233 | '\uA77D': 'G', |
| 234 | '\uA77E': 'G', |
| 235 | '\u24BD': 'H', |
| 236 | '\uFF28': 'H', |
| 237 | '\u0124': 'H', |
| 238 | '\u1E22': 'H', |
| 239 | '\u1E26': 'H', |
| 240 | '\u021E': 'H', |
| 241 | '\u1E24': 'H', |
| 242 | '\u1E28': 'H', |
| 243 | '\u1E2A': 'H', |
| 244 | '\u0126': 'H', |
| 245 | '\u2C67': 'H', |
| 246 | '\u2C75': 'H', |
| 247 | '\uA78D': 'H', |
| 248 | '\u24BE': 'I', |
| 249 | '\uFF29': 'I', |
| 250 | '\u00CC': 'I', |
| 251 | '\u00CD': 'I', |
| 252 | '\u00CE': 'I', |
| 253 | '\u0128': 'I', |
| 254 | '\u012A': 'I', |
| 255 | '\u012C': 'I', |
| 256 | '\u0130': 'I', |
| 257 | '\u00CF': 'I', |
| 258 | '\u1E2E': 'I', |
| 259 | '\u1EC8': 'I', |
| 260 | '\u01CF': 'I', |
| 261 | '\u0208': 'I', |
| 262 | '\u020A': 'I', |
| 263 | '\u1ECA': 'I', |
| 264 | '\u012E': 'I', |
| 265 | '\u1E2C': 'I', |
| 266 | '\u0197': 'I', |
| 267 | '\u24BF': 'J', |
| 268 | '\uFF2A': 'J', |
| 269 | '\u0134': 'J', |
| 270 | '\u0248': 'J', |
| 271 | '\u24C0': 'K', |
| 272 | '\uFF2B': 'K', |
| 273 | '\u1E30': 'K', |
| 274 | '\u01E8': 'K', |
| 275 | '\u1E32': 'K', |
| 276 | '\u0136': 'K', |
| 277 | '\u1E34': 'K', |
| 278 | '\u0198': 'K', |
| 279 | '\u2C69': 'K', |
| 280 | '\uA740': 'K', |
| 281 | '\uA742': 'K', |
| 282 | '\uA744': 'K', |
| 283 | '\uA7A2': 'K', |
| 284 | '\u24C1': 'L', |
| 285 | '\uFF2C': 'L', |
| 286 | '\u013F': 'L', |
| 287 | '\u0139': 'L', |
| 288 | '\u013D': 'L', |
| 289 | '\u1E36': 'L', |
| 290 | '\u1E38': 'L', |
| 291 | '\u013B': 'L', |
| 292 | '\u1E3C': 'L', |
| 293 | '\u1E3A': 'L', |
| 294 | '\u0141': 'L', |
| 295 | '\u023D': 'L', |
| 296 | '\u2C62': 'L', |
| 297 | '\u2C60': 'L', |
| 298 | '\uA748': 'L', |
| 299 | '\uA746': 'L', |
| 300 | '\uA780': 'L', |
| 301 | '\u01C7': 'LJ', |
| 302 | '\u01C8': 'Lj', |
| 303 | '\u24C2': 'M', |
| 304 | '\uFF2D': 'M', |
| 305 | '\u1E3E': 'M', |
| 306 | '\u1E40': 'M', |
| 307 | '\u1E42': 'M', |
| 308 | '\u2C6E': 'M', |
| 309 | '\u019C': 'M', |
| 310 | '\u24C3': 'N', |
| 311 | '\uFF2E': 'N', |
| 312 | '\u01F8': 'N', |
| 313 | '\u0143': 'N', |
| 314 | '\u00D1': 'N', |
| 315 | '\u1E44': 'N', |
| 316 | '\u0147': 'N', |
| 317 | '\u1E46': 'N', |
| 318 | '\u0145': 'N', |
| 319 | '\u1E4A': 'N', |
| 320 | '\u1E48': 'N', |
| 321 | '\u0220': 'N', |
| 322 | '\u019D': 'N', |
| 323 | '\uA790': 'N', |
| 324 | '\uA7A4': 'N', |
| 325 | '\u01CA': 'NJ', |
| 326 | '\u01CB': 'Nj', |
| 327 | '\u24C4': 'O', |
| 328 | '\uFF2F': 'O', |
| 329 | '\u00D2': 'O', |
| 330 | '\u00D3': 'O', |
| 331 | '\u00D4': 'O', |
| 332 | '\u1ED2': 'O', |
| 333 | '\u1ED0': 'O', |
| 334 | '\u1ED6': 'O', |
| 335 | '\u1ED4': 'O', |
| 336 | '\u00D5': 'O', |
| 337 | '\u1E4C': 'O', |
| 338 | '\u022C': 'O', |
| 339 | '\u1E4E': 'O', |
| 340 | '\u014C': 'O', |
| 341 | '\u1E50': 'O', |
| 342 | '\u1E52': 'O', |
| 343 | '\u014E': 'O', |
| 344 | '\u022E': 'O', |
| 345 | '\u0230': 'O', |
| 346 | '\u00D6': 'O', |
| 347 | '\u022A': 'O', |
| 348 | '\u1ECE': 'O', |
| 349 | '\u0150': 'O', |
| 350 | '\u01D1': 'O', |
| 351 | '\u020C': 'O', |
| 352 | '\u020E': 'O', |
| 353 | '\u01A0': 'O', |
| 354 | '\u1EDC': 'O', |
| 355 | '\u1EDA': 'O', |
| 356 | '\u1EE0': 'O', |
| 357 | '\u1EDE': 'O', |
| 358 | '\u1EE2': 'O', |
| 359 | '\u1ECC': 'O', |
| 360 | '\u1ED8': 'O', |
| 361 | '\u01EA': 'O', |
| 362 | '\u01EC': 'O', |
| 363 | '\u00D8': 'O', |
| 364 | '\u01FE': 'O', |
| 365 | '\u0186': 'O', |
| 366 | '\u019F': 'O', |
| 367 | '\uA74A': 'O', |
| 368 | '\uA74C': 'O', |
| 369 | '\u01A2': 'OI', |
| 370 | '\uA74E': 'OO', |
| 371 | '\u0222': 'OU', |
| 372 | '\u24C5': 'P', |
| 373 | '\uFF30': 'P', |
| 374 | '\u1E54': 'P', |
| 375 | '\u1E56': 'P', |
| 376 | '\u01A4': 'P', |
| 377 | '\u2C63': 'P', |
| 378 | '\uA750': 'P', |
| 379 | '\uA752': 'P', |
| 380 | '\uA754': 'P', |
| 381 | '\u24C6': 'Q', |
| 382 | '\uFF31': 'Q', |
| 383 | '\uA756': 'Q', |
| 384 | '\uA758': 'Q', |
| 385 | '\u024A': 'Q', |
| 386 | '\u24C7': 'R', |
| 387 | '\uFF32': 'R', |
| 388 | '\u0154': 'R', |
| 389 | '\u1E58': 'R', |
| 390 | '\u0158': 'R', |
| 391 | '\u0210': 'R', |
| 392 | '\u0212': 'R', |
| 393 | '\u1E5A': 'R', |
| 394 | '\u1E5C': 'R', |
| 395 | '\u0156': 'R', |
| 396 | '\u1E5E': 'R', |
| 397 | '\u024C': 'R', |
| 398 | '\u2C64': 'R', |
| 399 | '\uA75A': 'R', |
| 400 | '\uA7A6': 'R', |
| 401 | '\uA782': 'R', |
| 402 | '\u24C8': 'S', |
| 403 | '\uFF33': 'S', |
| 404 | '\u1E9E': 'S', |
| 405 | '\u015A': 'S', |
| 406 | '\u1E64': 'S', |
| 407 | '\u015C': 'S', |
| 408 | '\u1E60': 'S', |
| 409 | '\u0160': 'S', |
| 410 | '\u1E66': 'S', |
| 411 | '\u1E62': 'S', |
| 412 | '\u1E68': 'S', |
| 413 | '\u0218': 'S', |
| 414 | '\u015E': 'S', |
| 415 | '\u2C7E': 'S', |
| 416 | '\uA7A8': 'S', |
| 417 | '\uA784': 'S', |
| 418 | '\u24C9': 'T', |
| 419 | '\uFF34': 'T', |
| 420 | '\u1E6A': 'T', |
| 421 | '\u0164': 'T', |
| 422 | '\u1E6C': 'T', |
| 423 | '\u021A': 'T', |
| 424 | '\u0162': 'T', |
| 425 | '\u1E70': 'T', |
| 426 | '\u1E6E': 'T', |
| 427 | '\u0166': 'T', |
| 428 | '\u01AC': 'T', |
| 429 | '\u01AE': 'T', |
| 430 | '\u023E': 'T', |
| 431 | '\uA786': 'T', |
| 432 | '\uA728': 'TZ', |
| 433 | '\u24CA': 'U', |
| 434 | '\uFF35': 'U', |
| 435 | '\u00D9': 'U', |
| 436 | '\u00DA': 'U', |
| 437 | '\u00DB': 'U', |
| 438 | '\u0168': 'U', |
| 439 | '\u1E78': 'U', |
| 440 | '\u016A': 'U', |
| 441 | '\u1E7A': 'U', |
| 442 | '\u016C': 'U', |
| 443 | '\u00DC': 'U', |
| 444 | '\u01DB': 'U', |
| 445 | '\u01D7': 'U', |
| 446 | '\u01D5': 'U', |
| 447 | '\u01D9': 'U', |
| 448 | '\u1EE6': 'U', |
| 449 | '\u016E': 'U', |
| 450 | '\u0170': 'U', |
| 451 | '\u01D3': 'U', |
| 452 | '\u0214': 'U', |
| 453 | '\u0216': 'U', |
| 454 | '\u01AF': 'U', |
| 455 | '\u1EEA': 'U', |
| 456 | '\u1EE8': 'U', |
| 457 | '\u1EEE': 'U', |
| 458 | '\u1EEC': 'U', |
| 459 | '\u1EF0': 'U', |
| 460 | '\u1EE4': 'U', |
| 461 | '\u1E72': 'U', |
| 462 | '\u0172': 'U', |
| 463 | '\u1E76': 'U', |
| 464 | '\u1E74': 'U', |
| 465 | '\u0244': 'U', |
| 466 | '\u24CB': 'V', |
| 467 | '\uFF36': 'V', |
| 468 | '\u1E7C': 'V', |
| 469 | '\u1E7E': 'V', |
| 470 | '\u01B2': 'V', |
| 471 | '\uA75E': 'V', |
| 472 | '\u0245': 'V', |
| 473 | '\uA760': 'VY', |
| 474 | '\u24CC': 'W', |
| 475 | '\uFF37': 'W', |
| 476 | '\u1E80': 'W', |
| 477 | '\u1E82': 'W', |
| 478 | '\u0174': 'W', |
| 479 | '\u1E86': 'W', |
| 480 | '\u1E84': 'W', |
| 481 | '\u1E88': 'W', |
| 482 | '\u2C72': 'W', |
| 483 | '\u24CD': 'X', |
| 484 | '\uFF38': 'X', |
| 485 | '\u1E8A': 'X', |
| 486 | '\u1E8C': 'X', |
| 487 | '\u24CE': 'Y', |
| 488 | '\uFF39': 'Y', |
| 489 | '\u1EF2': 'Y', |
| 490 | '\u00DD': 'Y', |
| 491 | '\u0176': 'Y', |
| 492 | '\u1EF8': 'Y', |
| 493 | '\u0232': 'Y', |
| 494 | '\u1E8E': 'Y', |
| 495 | '\u0178': 'Y', |
| 496 | '\u1EF6': 'Y', |
| 497 | '\u1EF4': 'Y', |
| 498 | '\u01B3': 'Y', |
| 499 | '\u024E': 'Y', |
| 500 | '\u1EFE': 'Y', |
| 501 | '\u24CF': 'Z', |
| 502 | '\uFF3A': 'Z', |
| 503 | '\u0179': 'Z', |
| 504 | '\u1E90': 'Z', |
| 505 | '\u017B': 'Z', |
| 506 | '\u017D': 'Z', |
| 507 | '\u1E92': 'Z', |
| 508 | '\u1E94': 'Z', |
| 509 | '\u01B5': 'Z', |
| 510 | '\u0224': 'Z', |
| 511 | '\u2C7F': 'Z', |
| 512 | '\u2C6B': 'Z', |
| 513 | '\uA762': 'Z', |
| 514 | '\u24D0': 'a', |
| 515 | '\uFF41': 'a', |
| 516 | '\u1E9A': 'a', |
| 517 | '\u00E0': 'a', |
| 518 | '\u00E1': 'a', |
| 519 | '\u00E2': 'a', |
| 520 | '\u1EA7': 'a', |
| 521 | '\u1EA5': 'a', |
| 522 | '\u1EAB': 'a', |
| 523 | '\u1EA9': 'a', |
| 524 | '\u00E3': 'a', |
| 525 | '\u0101': 'a', |
| 526 | '\u0103': 'a', |
| 527 | '\u1EB1': 'a', |
| 528 | '\u1EAF': 'a', |
| 529 | '\u1EB5': 'a', |
| 530 | '\u1EB3': 'a', |
| 531 | '\u0227': 'a', |
| 532 | '\u01E1': 'a', |
| 533 | '\u00E4': 'a', |
| 534 | '\u01DF': 'a', |
| 535 | '\u1EA3': 'a', |
| 536 | '\u00E5': 'a', |
| 537 | '\u01FB': 'a', |
| 538 | '\u01CE': 'a', |
| 539 | '\u0201': 'a', |
| 540 | '\u0203': 'a', |
| 541 | '\u1EA1': 'a', |
| 542 | '\u1EAD': 'a', |
| 543 | '\u1EB7': 'a', |
| 544 | '\u1E01': 'a', |
| 545 | '\u0105': 'a', |
| 546 | '\u2C65': 'a', |
| 547 | '\u0250': 'a', |
| 548 | '\uA733': 'aa', |
| 549 | '\u00E6': 'ae', |
| 550 | '\u01FD': 'ae', |
| 551 | '\u01E3': 'ae', |
| 552 | '\uA735': 'ao', |
| 553 | '\uA737': 'au', |
| 554 | '\uA739': 'av', |
| 555 | '\uA73B': 'av', |
| 556 | '\uA73D': 'ay', |
| 557 | '\u24D1': 'b', |
| 558 | '\uFF42': 'b', |
| 559 | '\u1E03': 'b', |
| 560 | '\u1E05': 'b', |
| 561 | '\u1E07': 'b', |
| 562 | '\u0180': 'b', |
| 563 | '\u0183': 'b', |
| 564 | '\u0253': 'b', |
| 565 | '\u24D2': 'c', |
| 566 | '\uFF43': 'c', |
| 567 | '\u0107': 'c', |
| 568 | '\u0109': 'c', |
| 569 | '\u010B': 'c', |
| 570 | '\u010D': 'c', |
| 571 | '\u00E7': 'c', |
| 572 | '\u1E09': 'c', |
| 573 | '\u0188': 'c', |
| 574 | '\u023C': 'c', |
| 575 | '\uA73F': 'c', |
| 576 | '\u2184': 'c', |
| 577 | '\u24D3': 'd', |
| 578 | '\uFF44': 'd', |
| 579 | '\u1E0B': 'd', |
| 580 | '\u010F': 'd', |
| 581 | '\u1E0D': 'd', |
| 582 | '\u1E11': 'd', |
| 583 | '\u1E13': 'd', |
| 584 | '\u1E0F': 'd', |
| 585 | '\u0111': 'd', |
| 586 | '\u018C': 'd', |
| 587 | '\u0256': 'd', |
| 588 | '\u0257': 'd', |
| 589 | '\uA77A': 'd', |
| 590 | '\u01F3': 'dz', |
| 591 | '\u01C6': 'dz', |
| 592 | '\u24D4': 'e', |
| 593 | '\uFF45': 'e', |
| 594 | '\u00E8': 'e', |
| 595 | '\u00E9': 'e', |
| 596 | '\u00EA': 'e', |
| 597 | '\u1EC1': 'e', |
| 598 | '\u1EBF': 'e', |
| 599 | '\u1EC5': 'e', |
| 600 | '\u1EC3': 'e', |
| 601 | '\u1EBD': 'e', |
| 602 | '\u0113': 'e', |
| 603 | '\u1E15': 'e', |
| 604 | '\u1E17': 'e', |
| 605 | '\u0115': 'e', |
| 606 | '\u0117': 'e', |
| 607 | '\u00EB': 'e', |
| 608 | '\u1EBB': 'e', |
| 609 | '\u011B': 'e', |
| 610 | '\u0205': 'e', |
| 611 | '\u0207': 'e', |
| 612 | '\u1EB9': 'e', |
| 613 | '\u1EC7': 'e', |
| 614 | '\u0229': 'e', |
| 615 | '\u1E1D': 'e', |
| 616 | '\u0119': 'e', |
| 617 | '\u1E19': 'e', |
| 618 | '\u1E1B': 'e', |
| 619 | '\u0247': 'e', |
| 620 | '\u025B': 'e', |
| 621 | '\u01DD': 'e', |
| 622 | '\u24D5': 'f', |
| 623 | '\uFF46': 'f', |
| 624 | '\u1E1F': 'f', |
| 625 | '\u0192': 'f', |
| 626 | '\uA77C': 'f', |
| 627 | '\u24D6': 'g', |
| 628 | '\uFF47': 'g', |
| 629 | '\u01F5': 'g', |
| 630 | '\u011D': 'g', |
| 631 | '\u1E21': 'g', |
| 632 | '\u011F': 'g', |
| 633 | '\u0121': 'g', |
| 634 | '\u01E7': 'g', |
| 635 | '\u0123': 'g', |
| 636 | '\u01E5': 'g', |
| 637 | '\u0260': 'g', |
| 638 | '\uA7A1': 'g', |
| 639 | '\u1D79': 'g', |
| 640 | '\uA77F': 'g', |
| 641 | '\u24D7': 'h', |
| 642 | '\uFF48': 'h', |
| 643 | '\u0125': 'h', |
| 644 | '\u1E23': 'h', |
| 645 | '\u1E27': 'h', |
| 646 | '\u021F': 'h', |
| 647 | '\u1E25': 'h', |
| 648 | '\u1E29': 'h', |
| 649 | '\u1E2B': 'h', |
| 650 | '\u1E96': 'h', |
| 651 | '\u0127': 'h', |
| 652 | '\u2C68': 'h', |
| 653 | '\u2C76': 'h', |
| 654 | '\u0265': 'h', |
| 655 | '\u0195': 'hv', |
| 656 | '\u24D8': 'i', |
| 657 | '\uFF49': 'i', |
| 658 | '\u00EC': 'i', |
| 659 | '\u00ED': 'i', |
| 660 | '\u00EE': 'i', |
| 661 | '\u0129': 'i', |
| 662 | '\u012B': 'i', |
| 663 | '\u012D': 'i', |
| 664 | '\u00EF': 'i', |
| 665 | '\u1E2F': 'i', |
| 666 | '\u1EC9': 'i', |
| 667 | '\u01D0': 'i', |
| 668 | '\u0209': 'i', |
| 669 | '\u020B': 'i', |
| 670 | '\u1ECB': 'i', |
| 671 | '\u012F': 'i', |
| 672 | '\u1E2D': 'i', |
| 673 | '\u0268': 'i', |
| 674 | '\u0131': 'i', |
| 675 | '\u24D9': 'j', |
| 676 | '\uFF4A': 'j', |
| 677 | '\u0135': 'j', |
| 678 | '\u01F0': 'j', |
| 679 | '\u0249': 'j', |
| 680 | '\u24DA': 'k', |
| 681 | '\uFF4B': 'k', |
| 682 | '\u1E31': 'k', |
| 683 | '\u01E9': 'k', |
| 684 | '\u1E33': 'k', |
| 685 | '\u0137': 'k', |
| 686 | '\u1E35': 'k', |
| 687 | '\u0199': 'k', |
| 688 | '\u2C6A': 'k', |
| 689 | '\uA741': 'k', |
| 690 | '\uA743': 'k', |
| 691 | '\uA745': 'k', |
| 692 | '\uA7A3': 'k', |
| 693 | '\u24DB': 'l', |
| 694 | '\uFF4C': 'l', |
| 695 | '\u0140': 'l', |
| 696 | '\u013A': 'l', |
| 697 | '\u013E': 'l', |
| 698 | '\u1E37': 'l', |
| 699 | '\u1E39': 'l', |
| 700 | '\u013C': 'l', |
| 701 | '\u1E3D': 'l', |
| 702 | '\u1E3B': 'l', |
| 703 | '\u017F': 'l', |
| 704 | '\u0142': 'l', |
| 705 | '\u019A': 'l', |
| 706 | '\u026B': 'l', |
| 707 | '\u2C61': 'l', |
| 708 | '\uA749': 'l', |
| 709 | '\uA781': 'l', |
| 710 | '\uA747': 'l', |
| 711 | '\u01C9': 'lj', |
| 712 | '\u24DC': 'm', |
| 713 | '\uFF4D': 'm', |
| 714 | '\u1E3F': 'm', |
| 715 | '\u1E41': 'm', |
| 716 | '\u1E43': 'm', |
| 717 | '\u0271': 'm', |
| 718 | '\u026F': 'm', |
| 719 | '\u24DD': 'n', |
| 720 | '\uFF4E': 'n', |
| 721 | '\u01F9': 'n', |
| 722 | '\u0144': 'n', |
| 723 | '\u00F1': 'n', |
| 724 | '\u1E45': 'n', |
| 725 | '\u0148': 'n', |
| 726 | '\u1E47': 'n', |
| 727 | '\u0146': 'n', |
| 728 | '\u1E4B': 'n', |
| 729 | '\u1E49': 'n', |
| 730 | '\u019E': 'n', |
| 731 | '\u0272': 'n', |
| 732 | '\u0149': 'n', |
| 733 | '\uA791': 'n', |
| 734 | '\uA7A5': 'n', |
| 735 | '\u01CC': 'nj', |
| 736 | '\u24DE': 'o', |
| 737 | '\uFF4F': 'o', |
| 738 | '\u00F2': 'o', |
| 739 | '\u00F3': 'o', |
| 740 | '\u00F4': 'o', |
| 741 | '\u1ED3': 'o', |
| 742 | '\u1ED1': 'o', |
| 743 | '\u1ED7': 'o', |
| 744 | '\u1ED5': 'o', |
| 745 | '\u00F5': 'o', |
| 746 | '\u1E4D': 'o', |
| 747 | '\u022D': 'o', |
| 748 | '\u1E4F': 'o', |
| 749 | '\u014D': 'o', |
| 750 | '\u1E51': 'o', |
| 751 | '\u1E53': 'o', |
| 752 | '\u014F': 'o', |
| 753 | '\u022F': 'o', |
| 754 | '\u0231': 'o', |
| 755 | '\u00F6': 'o', |
| 756 | '\u022B': 'o', |
| 757 | '\u1ECF': 'o', |
| 758 | '\u0151': 'o', |
| 759 | '\u01D2': 'o', |
| 760 | '\u020D': 'o', |
| 761 | '\u020F': 'o', |
| 762 | '\u01A1': 'o', |
| 763 | '\u1EDD': 'o', |
| 764 | '\u1EDB': 'o', |
| 765 | '\u1EE1': 'o', |
| 766 | '\u1EDF': 'o', |
| 767 | '\u1EE3': 'o', |
| 768 | '\u1ECD': 'o', |
| 769 | '\u1ED9': 'o', |
| 770 | '\u01EB': 'o', |
| 771 | '\u01ED': 'o', |
| 772 | '\u00F8': 'o', |
| 773 | '\u01FF': 'o', |
| 774 | '\u0254': 'o', |
| 775 | '\uA74B': 'o', |
| 776 | '\uA74D': 'o', |
| 777 | '\u0275': 'o', |
| 778 | '\u01A3': 'oi', |
| 779 | '\u0223': 'ou', |
| 780 | '\uA74F': 'oo', |
| 781 | '\u24DF': 'p', |
| 782 | '\uFF50': 'p', |
| 783 | '\u1E55': 'p', |
| 784 | '\u1E57': 'p', |
| 785 | '\u01A5': 'p', |
| 786 | '\u1D7D': 'p', |
| 787 | '\uA751': 'p', |
| 788 | '\uA753': 'p', |
| 789 | '\uA755': 'p', |
| 790 | '\u24E0': 'q', |
| 791 | '\uFF51': 'q', |
| 792 | '\u024B': 'q', |
| 793 | '\uA757': 'q', |
| 794 | '\uA759': 'q', |
| 795 | '\u24E1': 'r', |
| 796 | '\uFF52': 'r', |
| 797 | '\u0155': 'r', |
| 798 | '\u1E59': 'r', |
| 799 | '\u0159': 'r', |
| 800 | '\u0211': 'r', |
| 801 | '\u0213': 'r', |
| 802 | '\u1E5B': 'r', |
| 803 | '\u1E5D': 'r', |
| 804 | '\u0157': 'r', |
| 805 | '\u1E5F': 'r', |
| 806 | '\u024D': 'r', |
| 807 | '\u027D': 'r', |
| 808 | '\uA75B': 'r', |
| 809 | '\uA7A7': 'r', |
| 810 | '\uA783': 'r', |
| 811 | '\u24E2': 's', |
| 812 | '\uFF53': 's', |
| 813 | '\u00DF': 's', |
| 814 | '\u015B': 's', |
| 815 | '\u1E65': 's', |
| 816 | '\u015D': 's', |
| 817 | '\u1E61': 's', |
| 818 | '\u0161': 's', |
| 819 | '\u1E67': 's', |
| 820 | '\u1E63': 's', |
| 821 | '\u1E69': 's', |
| 822 | '\u0219': 's', |
| 823 | '\u015F': 's', |
| 824 | '\u023F': 's', |
| 825 | '\uA7A9': 's', |
| 826 | '\uA785': 's', |
| 827 | '\u1E9B': 's', |
| 828 | '\u24E3': 't', |
| 829 | '\uFF54': 't', |
| 830 | '\u1E6B': 't', |
| 831 | '\u1E97': 't', |
| 832 | '\u0165': 't', |
| 833 | '\u1E6D': 't', |
| 834 | '\u021B': 't', |
| 835 | '\u0163': 't', |
| 836 | '\u1E71': 't', |
| 837 | '\u1E6F': 't', |
| 838 | '\u0167': 't', |
| 839 | '\u01AD': 't', |
| 840 | '\u0288': 't', |
| 841 | '\u2C66': 't', |
| 842 | '\uA787': 't', |
| 843 | '\uA729': 'tz', |
| 844 | '\u24E4': 'u', |
| 845 | '\uFF55': 'u', |
| 846 | '\u00F9': 'u', |
| 847 | '\u00FA': 'u', |
| 848 | '\u00FB': 'u', |
| 849 | '\u0169': 'u', |
| 850 | '\u1E79': 'u', |
| 851 | '\u016B': 'u', |
| 852 | '\u1E7B': 'u', |
| 853 | '\u016D': 'u', |
| 854 | '\u00FC': 'u', |
| 855 | '\u01DC': 'u', |
| 856 | '\u01D8': 'u', |
| 857 | '\u01D6': 'u', |
| 858 | '\u01DA': 'u', |
| 859 | '\u1EE7': 'u', |
| 860 | '\u016F': 'u', |
| 861 | '\u0171': 'u', |
| 862 | '\u01D4': 'u', |
| 863 | '\u0215': 'u', |
| 864 | '\u0217': 'u', |
| 865 | '\u01B0': 'u', |
| 866 | '\u1EEB': 'u', |
| 867 | '\u1EE9': 'u', |
| 868 | '\u1EEF': 'u', |
| 869 | '\u1EED': 'u', |
| 870 | '\u1EF1': 'u', |
| 871 | '\u1EE5': 'u', |
| 872 | '\u1E73': 'u', |
| 873 | '\u0173': 'u', |
| 874 | '\u1E77': 'u', |
| 875 | '\u1E75': 'u', |
| 876 | '\u0289': 'u', |
| 877 | '\u24E5': 'v', |
| 878 | '\uFF56': 'v', |
| 879 | '\u1E7D': 'v', |
| 880 | '\u1E7F': 'v', |
| 881 | '\u028B': 'v', |
| 882 | '\uA75F': 'v', |
| 883 | '\u028C': 'v', |
| 884 | '\uA761': 'vy', |
| 885 | '\u24E6': 'w', |
| 886 | '\uFF57': 'w', |
| 887 | '\u1E81': 'w', |
| 888 | '\u1E83': 'w', |
| 889 | '\u0175': 'w', |
| 890 | '\u1E87': 'w', |
| 891 | '\u1E85': 'w', |
| 892 | '\u1E98': 'w', |
| 893 | '\u1E89': 'w', |
| 894 | '\u2C73': 'w', |
| 895 | '\u24E7': 'x', |
| 896 | '\uFF58': 'x', |
| 897 | '\u1E8B': 'x', |
| 898 | '\u1E8D': 'x', |
| 899 | '\u24E8': 'y', |
| 900 | '\uFF59': 'y', |
| 901 | '\u1EF3': 'y', |
| 902 | '\u00FD': 'y', |
| 903 | '\u0177': 'y', |
| 904 | '\u1EF9': 'y', |
| 905 | '\u0233': 'y', |
| 906 | '\u1E8F': 'y', |
| 907 | '\u00FF': 'y', |
| 908 | '\u1EF7': 'y', |
| 909 | '\u1E99': 'y', |
| 910 | '\u1EF5': 'y', |
| 911 | '\u01B4': 'y', |
| 912 | '\u024F': 'y', |
| 913 | '\u1EFF': 'y', |
| 914 | '\u24E9': 'z', |
| 915 | '\uFF5A': 'z', |
| 916 | '\u017A': 'z', |
| 917 | '\u1E91': 'z', |
| 918 | '\u017C': 'z', |
| 919 | '\u017E': 'z', |
| 920 | '\u1E93': 'z', |
| 921 | '\u1E95': 'z', |
| 922 | '\u01B6': 'z', |
| 923 | '\u0225': 'z', |
| 924 | '\u0240': 'z', |
| 925 | '\u2C6C': 'z', |
| 926 | '\uA763': 'z', |
| 927 | '\u0386': '\u0391', |
| 928 | '\u0388': '\u0395', |
| 929 | '\u0389': '\u0397', |
| 930 | '\u038A': '\u0399', |
| 931 | '\u03AA': '\u0399', |
| 932 | '\u038C': '\u039F', |
| 933 | '\u038E': '\u03A5', |
| 934 | '\u03AB': '\u03A5', |
| 935 | '\u038F': '\u03A9', |
| 936 | '\u03AC': '\u03B1', |
| 937 | '\u03AD': '\u03B5', |
| 938 | '\u03AE': '\u03B7', |
| 939 | '\u03AF': '\u03B9', |
| 940 | '\u03CA': '\u03B9', |
| 941 | '\u0390': '\u03B9', |
| 942 | '\u03CC': '\u03BF', |
| 943 | '\u03CD': '\u03C5', |
| 944 | '\u03CB': '\u03C5', |
| 945 | '\u03B0': '\u03C5', |
| 946 | '\u03C9': '\u03C9', |
| 947 | '\u03C2': '\u03C3', |
| 948 | }; |
| 949 | |
| 950 | $document = $( document ); |
| 951 | |
| 952 | nextUid = ( function () { |
| 953 | var counter = 1; |
| 954 | return function () { |
| 955 | return counter++; |
| 956 | }; |
| 957 | } )(); |
| 958 | |
| 959 | function reinsertElement( element ) { |
| 960 | var placeholder = $( document.createTextNode( '' ) ); |
| 961 | |
| 962 | element.before( placeholder ); |
| 963 | placeholder.before( element ); |
| 964 | placeholder.remove(); |
| 965 | } |
| 966 | |
| 967 | function stripDiacritics( str ) { |
| 968 | // Used 'uni range + named function' from http://jsperf.com/diacritics/18 |
| 969 | function match( a ) { |
| 970 | return DIACRITICS[ a ] || a; |
| 971 | } |
| 972 | |
| 973 | return str.replace( /[^\u0000-\u007E]/g, match ); |
| 974 | } |
| 975 | |
| 976 | function indexOf( value, array ) { |
| 977 | var i = 0, |
| 978 | l = array.length; |
| 979 | for ( ; i < l; i = i + 1 ) { |
| 980 | if ( equal( value, array[ i ] ) ) return i; |
| 981 | } |
| 982 | return -1; |
| 983 | } |
| 984 | |
| 985 | function measureScrollbar() { |
| 986 | var $template = $( MEASURE_SCROLLBAR_TEMPLATE ); |
| 987 | $template.appendTo( document.body ); |
| 988 | |
| 989 | var dim = { |
| 990 | width: $template.width() - $template[ 0 ].clientWidth, |
| 991 | height: $template.height() - $template[ 0 ].clientHeight, |
| 992 | }; |
| 993 | $template.remove(); |
| 994 | |
| 995 | return dim; |
| 996 | } |
| 997 | |
| 998 | /** |
| 999 | * Compares equality of a and b |
| 1000 | * @param a |
| 1001 | * @param b |
| 1002 | */ |
| 1003 | function equal( a, b ) { |
| 1004 | if ( a === b ) return true; |
| 1005 | if ( a === undefined || b === undefined ) return false; |
| 1006 | if ( a === null || b === null ) return false; |
| 1007 | // Check whether 'a' or 'b' is a string (primitive or object). |
| 1008 | // The concatenation of an empty string (+'') converts its argument to a string's primitive. |
| 1009 | if ( a.constructor === String ) return a + '' === b + ''; // a+'' - in case 'a' is a String object |
| 1010 | if ( b.constructor === String ) return b + '' === a + ''; // b+'' - in case 'b' is a String object |
| 1011 | return false; |
| 1012 | } |
| 1013 | |
| 1014 | /** |
| 1015 | * Splits the string into an array of values, transforming each value. An empty array is returned for nulls or empty |
| 1016 | * strings |
| 1017 | * @param string |
| 1018 | * @param separator |
| 1019 | */ |
| 1020 | function splitVal( string, separator, transform ) { |
| 1021 | var val, i, l; |
| 1022 | if ( string === null || string.length < 1 ) return []; |
| 1023 | val = string.split( separator ); |
| 1024 | for ( i = 0, l = val.length; i < l; i = i + 1 ) |
| 1025 | val[ i ] = transform( val[ i ] ); |
| 1026 | return val; |
| 1027 | } |
| 1028 | |
| 1029 | function getSideBorderPadding( element ) { |
| 1030 | return element.outerWidth( false ) - element.width(); |
| 1031 | } |
| 1032 | |
| 1033 | function installKeyUpChangeEvent( element ) { |
| 1034 | var key = 'keyup-change-value'; |
| 1035 | element.on( 'keydown', function () { |
| 1036 | if ( $.data( element, key ) === undefined ) { |
| 1037 | $.data( element, key, element.val() ); |
| 1038 | } |
| 1039 | } ); |
| 1040 | element.on( 'keyup', function () { |
| 1041 | var val = $.data( element, key ); |
| 1042 | if ( val !== undefined && element.val() !== val ) { |
| 1043 | $.removeData( element, key ); |
| 1044 | element.trigger( 'keyup-change' ); |
| 1045 | } |
| 1046 | } ); |
| 1047 | } |
| 1048 | |
| 1049 | /** |
| 1050 | * filters mouse events so an event is fired only if the mouse moved. |
| 1051 | * |
| 1052 | * filters out mouse events that occur when mouse is stationary but |
| 1053 | * the elements under the pointer are scrolled. |
| 1054 | */ |
| 1055 | function installFilteredMouseMove( element ) { |
| 1056 | element.on( 'mousemove', function ( e ) { |
| 1057 | var lastpos = lastMousePosition; |
| 1058 | if ( |
| 1059 | lastpos === undefined || |
| 1060 | lastpos.x !== e.pageX || |
| 1061 | lastpos.y !== e.pageY |
| 1062 | ) { |
| 1063 | $( e.target ).trigger( 'mousemove-filtered', e ); |
| 1064 | } |
| 1065 | } ); |
| 1066 | } |
| 1067 | |
| 1068 | /** |
| 1069 | * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made |
| 1070 | * within the last quietMillis milliseconds. |
| 1071 | * |
| 1072 | * @param quietMillis number of milliseconds to wait before invoking fn |
| 1073 | * @param fn function to be debounced |
| 1074 | * @param ctx object to be used as this reference within fn |
| 1075 | * @return debounced version of fn |
| 1076 | */ |
| 1077 | function debounce( quietMillis, fn, ctx ) { |
| 1078 | ctx = ctx || undefined; |
| 1079 | var timeout; |
| 1080 | return function () { |
| 1081 | var args = arguments; |
| 1082 | window.clearTimeout( timeout ); |
| 1083 | timeout = window.setTimeout( function () { |
| 1084 | fn.apply( ctx, args ); |
| 1085 | }, quietMillis ); |
| 1086 | }; |
| 1087 | } |
| 1088 | |
| 1089 | function installDebouncedScroll( threshold, element ) { |
| 1090 | var notify = debounce( threshold, function ( e ) { |
| 1091 | element.trigger( 'scroll-debounced', e ); |
| 1092 | } ); |
| 1093 | element.on( 'scroll', function ( e ) { |
| 1094 | if ( indexOf( e.target, element.get() ) >= 0 ) notify( e ); |
| 1095 | } ); |
| 1096 | } |
| 1097 | |
| 1098 | function focus( $el ) { |
| 1099 | if ( $el[ 0 ] === document.activeElement ) return; |
| 1100 | |
| 1101 | /* set the focus in a 0 timeout - that way the focus is set after the processing |
| 1102 | of the current event has finished - which seems like the only reliable way |
| 1103 | to set focus */ |
| 1104 | window.setTimeout( function () { |
| 1105 | var el = $el[ 0 ], |
| 1106 | pos = $el.val().length, |
| 1107 | range; |
| 1108 | |
| 1109 | $el.focus(); |
| 1110 | |
| 1111 | /* make sure el received focus so we do not error out when trying to manipulate the caret. |
| 1112 | sometimes modals or others listeners may steal it after its set */ |
| 1113 | var isVisible = el.offsetWidth > 0 || el.offsetHeight > 0; |
| 1114 | if ( isVisible && el === document.activeElement ) { |
| 1115 | /* after the focus is set move the caret to the end, necessary when we val() |
| 1116 | just before setting focus */ |
| 1117 | if ( el.setSelectionRange ) { |
| 1118 | el.setSelectionRange( pos, pos ); |
| 1119 | } else if ( el.createTextRange ) { |
| 1120 | range = el.createTextRange(); |
| 1121 | range.collapse( false ); |
| 1122 | range.select(); |
| 1123 | } |
| 1124 | } |
| 1125 | }, 0 ); |
| 1126 | } |
| 1127 | |
| 1128 | function getCursorInfo( el ) { |
| 1129 | el = $( el )[ 0 ]; |
| 1130 | var offset = 0; |
| 1131 | var length = 0; |
| 1132 | if ( 'selectionStart' in el ) { |
| 1133 | offset = el.selectionStart; |
| 1134 | length = el.selectionEnd - offset; |
| 1135 | } else if ( 'selection' in document ) { |
| 1136 | el.focus(); |
| 1137 | var sel = document.selection.createRange(); |
| 1138 | length = document.selection.createRange().text.length; |
| 1139 | sel.moveStart( 'character', -el.value.length ); |
| 1140 | offset = sel.text.length - length; |
| 1141 | } |
| 1142 | return { offset: offset, length: length }; |
| 1143 | } |
| 1144 | |
| 1145 | function killEvent( event ) { |
| 1146 | event.preventDefault(); |
| 1147 | event.stopPropagation(); |
| 1148 | } |
| 1149 | function killEventImmediately( event ) { |
| 1150 | event.preventDefault(); |
| 1151 | event.stopImmediatePropagation(); |
| 1152 | } |
| 1153 | |
| 1154 | function measureTextWidth( e ) { |
| 1155 | if ( ! sizer ) { |
| 1156 | var style = |
| 1157 | e[ 0 ].currentStyle || window.getComputedStyle( e[ 0 ], null ); |
| 1158 | sizer = $( document.createElement( 'div' ) ).css( { |
| 1159 | position: 'absolute', |
| 1160 | left: '-10000px', |
| 1161 | top: '-10000px', |
| 1162 | display: 'none', |
| 1163 | fontSize: style.fontSize, |
| 1164 | fontFamily: style.fontFamily, |
| 1165 | fontStyle: style.fontStyle, |
| 1166 | fontWeight: style.fontWeight, |
| 1167 | letterSpacing: style.letterSpacing, |
| 1168 | textTransform: style.textTransform, |
| 1169 | whiteSpace: 'nowrap', |
| 1170 | } ); |
| 1171 | sizer.attr( 'class', 'select2-sizer' ); |
| 1172 | $( document.body ).append( sizer ); |
| 1173 | } |
| 1174 | sizer.text( e.val() ); |
| 1175 | return sizer.width(); |
| 1176 | } |
| 1177 | |
| 1178 | function syncCssClasses( dest, src, adapter ) { |
| 1179 | var classes, |
| 1180 | replacements = [], |
| 1181 | adapted; |
| 1182 | |
| 1183 | classes = $.trim( dest.attr( 'class' ) ); |
| 1184 | |
| 1185 | if ( classes ) { |
| 1186 | classes = '' + classes; // for IE which returns object |
| 1187 | |
| 1188 | $( classes.split( /\s+/ ) ).each2( function () { |
| 1189 | if ( this.indexOf( 'select2-' ) === 0 ) { |
| 1190 | replacements.push( this ); |
| 1191 | } |
| 1192 | } ); |
| 1193 | } |
| 1194 | |
| 1195 | classes = $.trim( src.attr( 'class' ) ); |
| 1196 | |
| 1197 | if ( classes ) { |
| 1198 | classes = '' + classes; // for IE which returns object |
| 1199 | |
| 1200 | $( classes.split( /\s+/ ) ).each2( function () { |
| 1201 | if ( this.indexOf( 'select2-' ) !== 0 ) { |
| 1202 | adapted = adapter( this ); |
| 1203 | |
| 1204 | if ( adapted ) { |
| 1205 | replacements.push( adapted ); |
| 1206 | } |
| 1207 | } |
| 1208 | } ); |
| 1209 | } |
| 1210 | |
| 1211 | dest.attr( 'class', replacements.join( ' ' ) ); |
| 1212 | } |
| 1213 | |
| 1214 | function markMatch( text, term, markup, escapeMarkup ) { |
| 1215 | var match = stripDiacritics( text.toUpperCase() ).indexOf( |
| 1216 | stripDiacritics( term.toUpperCase() ) |
| 1217 | ), |
| 1218 | tl = term.length; |
| 1219 | |
| 1220 | if ( match < 0 ) { |
| 1221 | markup.push( escapeMarkup( text ) ); |
| 1222 | return; |
| 1223 | } |
| 1224 | |
| 1225 | markup.push( escapeMarkup( text.substring( 0, match ) ) ); |
| 1226 | markup.push( "<span class='select2-match'>" ); |
| 1227 | markup.push( escapeMarkup( text.substring( match, match + tl ) ) ); |
| 1228 | markup.push( '</span>' ); |
| 1229 | markup.push( |
| 1230 | escapeMarkup( text.substring( match + tl, text.length ) ) |
| 1231 | ); |
| 1232 | } |
| 1233 | |
| 1234 | function defaultEscapeMarkup( markup ) { |
| 1235 | var replace_map = { |
| 1236 | '\\': '\', |
| 1237 | '&': '&', |
| 1238 | '<': '<', |
| 1239 | '>': '>', |
| 1240 | '"': '"', |
| 1241 | "'": ''', |
| 1242 | '/': '/', |
| 1243 | }; |
| 1244 | |
| 1245 | return String( markup ).replace( /[&<>"'\/\\]/g, function ( match ) { |
| 1246 | return replace_map[ match ]; |
| 1247 | } ); |
| 1248 | } |
| 1249 | |
| 1250 | /** |
| 1251 | * Produces an ajax-based query function |
| 1252 | * |
| 1253 | * @param options object containing configuration parameters |
| 1254 | * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax |
| 1255 | * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax |
| 1256 | * @param options.url url for the data |
| 1257 | * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url. |
| 1258 | * @param options.dataType request data type: ajax, jsonp, other datatypes supported by jQuery's $.ajax function or the transport function if specified |
| 1259 | * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often |
| 1260 | * @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2. |
| 1261 | * The expected format is an object containing the following keys: |
| 1262 | * results array of objects that will be used as choices |
| 1263 | * more (optional) boolean indicating whether there are more results available |
| 1264 | * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true} |
| 1265 | */ |
| 1266 | function ajax( options ) { |
| 1267 | var timeout, // current scheduled but not yet executed request |
| 1268 | handler = null, |
| 1269 | quietMillis = options.quietMillis || 100, |
| 1270 | ajaxUrl = options.url, |
| 1271 | self = this; |
| 1272 | |
| 1273 | return function ( query ) { |
| 1274 | window.clearTimeout( timeout ); |
| 1275 | timeout = window.setTimeout( function () { |
| 1276 | var data = options.data, // ajax data function |
| 1277 | url = ajaxUrl, // ajax url string or function |
| 1278 | transport = |
| 1279 | options.transport || |
| 1280 | $.fn.select2.ajaxDefaults.transport, |
| 1281 | // deprecated - to be removed in 4.0 - use params instead |
| 1282 | deprecated = { |
| 1283 | type: options.type || 'GET', // set type of request (GET or POST) |
| 1284 | cache: options.cache || false, |
| 1285 | jsonpCallback: options.jsonpCallback || undefined, |
| 1286 | dataType: options.dataType || 'json', |
| 1287 | }, |
| 1288 | params = $.extend( |
| 1289 | {}, |
| 1290 | $.fn.select2.ajaxDefaults.params, |
| 1291 | deprecated |
| 1292 | ); |
| 1293 | |
| 1294 | data = data |
| 1295 | ? data.call( self, query.term, query.page, query.context ) |
| 1296 | : null; |
| 1297 | url = |
| 1298 | typeof url === 'function' |
| 1299 | ? url.call( |
| 1300 | self, |
| 1301 | query.term, |
| 1302 | query.page, |
| 1303 | query.context |
| 1304 | ) |
| 1305 | : url; |
| 1306 | |
| 1307 | if ( handler && typeof handler.abort === 'function' ) { |
| 1308 | handler.abort(); |
| 1309 | } |
| 1310 | |
| 1311 | if ( options.params ) { |
| 1312 | if ( $.isFunction( options.params ) ) { |
| 1313 | $.extend( params, options.params.call( self ) ); |
| 1314 | } else { |
| 1315 | $.extend( params, options.params ); |
| 1316 | } |
| 1317 | } |
| 1318 | |
| 1319 | $.extend( params, { |
| 1320 | url: url, |
| 1321 | dataType: options.dataType, |
| 1322 | data: data, |
| 1323 | success: function ( data ) { |
| 1324 | // TODO - replace query.page with query so users have access to term, page, etc. |
| 1325 | // added query as third parameter to keep backwards compatibility |
| 1326 | var results = options.results( |
| 1327 | data, |
| 1328 | query.page, |
| 1329 | query |
| 1330 | ); |
| 1331 | query.callback( results ); |
| 1332 | }, |
| 1333 | error: function ( jqXHR, textStatus, errorThrown ) { |
| 1334 | var results = { |
| 1335 | hasError: true, |
| 1336 | jqXHR: jqXHR, |
| 1337 | textStatus: textStatus, |
| 1338 | errorThrown: errorThrown, |
| 1339 | }; |
| 1340 | |
| 1341 | query.callback( results ); |
| 1342 | }, |
| 1343 | } ); |
| 1344 | handler = transport.call( self, params ); |
| 1345 | }, quietMillis ); |
| 1346 | }; |
| 1347 | } |
| 1348 | |
| 1349 | /** |
| 1350 | * Produces a query function that works with a local array |
| 1351 | * |
| 1352 | * @param options object containing configuration parameters. The options parameter can either be an array or an |
| 1353 | * object. |
| 1354 | * |
| 1355 | * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys. |
| 1356 | * |
| 1357 | * If the object form is used it is assumed that it contains 'data' and 'text' keys. The 'data' key should contain |
| 1358 | * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text' |
| 1359 | * key can either be a String in which case it is expected that each element in the 'data' array has a key with the |
| 1360 | * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract |
| 1361 | * the text. |
| 1362 | */ |
| 1363 | function local( options ) { |
| 1364 | var data = options, // data elements |
| 1365 | dataText, |
| 1366 | tmp, |
| 1367 | text = function ( item ) { |
| 1368 | return '' + item.text; |
| 1369 | }; // function used to retrieve the text portion of a data item that is matched against the search |
| 1370 | |
| 1371 | if ( $.isArray( data ) ) { |
| 1372 | tmp = data; |
| 1373 | data = { results: tmp }; |
| 1374 | } |
| 1375 | |
| 1376 | if ( $.isFunction( data ) === false ) { |
| 1377 | tmp = data; |
| 1378 | data = function () { |
| 1379 | return tmp; |
| 1380 | }; |
| 1381 | } |
| 1382 | |
| 1383 | var dataItem = data(); |
| 1384 | if ( dataItem.text ) { |
| 1385 | text = dataItem.text; |
| 1386 | // if text is not a function we assume it to be a key name |
| 1387 | if ( ! $.isFunction( text ) ) { |
| 1388 | dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available |
| 1389 | text = function ( item ) { |
| 1390 | return item[ dataText ]; |
| 1391 | }; |
| 1392 | } |
| 1393 | } |
| 1394 | |
| 1395 | return function ( query ) { |
| 1396 | var t = query.term, |
| 1397 | filtered = { results: [] }, |
| 1398 | process; |
| 1399 | if ( t === '' ) { |
| 1400 | query.callback( data() ); |
| 1401 | return; |
| 1402 | } |
| 1403 | |
| 1404 | process = function ( datum, collection ) { |
| 1405 | var group, attr; |
| 1406 | datum = datum[ 0 ]; |
| 1407 | if ( datum.children ) { |
| 1408 | group = {}; |
| 1409 | for ( attr in datum ) { |
| 1410 | if ( datum.hasOwnProperty( attr ) ) |
| 1411 | group[ attr ] = datum[ attr ]; |
| 1412 | } |
| 1413 | group.children = []; |
| 1414 | $( datum.children ).each2( function ( i, childDatum ) { |
| 1415 | process( childDatum, group.children ); |
| 1416 | } ); |
| 1417 | if ( |
| 1418 | group.children.length || |
| 1419 | query.matcher( t, text( group ), datum ) |
| 1420 | ) { |
| 1421 | collection.push( group ); |
| 1422 | } |
| 1423 | } else { |
| 1424 | if ( query.matcher( t, text( datum ), datum ) ) { |
| 1425 | collection.push( datum ); |
| 1426 | } |
| 1427 | } |
| 1428 | }; |
| 1429 | |
| 1430 | $( data().results ).each2( function ( i, datum ) { |
| 1431 | process( datum, filtered.results ); |
| 1432 | } ); |
| 1433 | query.callback( filtered ); |
| 1434 | }; |
| 1435 | } |
| 1436 | |
| 1437 | // TODO javadoc |
| 1438 | function tags( data ) { |
| 1439 | var isFunc = $.isFunction( data ); |
| 1440 | return function ( query ) { |
| 1441 | var t = query.term, |
| 1442 | filtered = { results: [] }; |
| 1443 | var result = isFunc ? data( query ) : data; |
| 1444 | if ( $.isArray( result ) ) { |
| 1445 | $( result ).each( function () { |
| 1446 | var isObject = this.text !== undefined, |
| 1447 | text = isObject ? this.text : this; |
| 1448 | if ( t === '' || query.matcher( t, text ) ) { |
| 1449 | filtered.results.push( |
| 1450 | isObject ? this : { id: this, text: this } |
| 1451 | ); |
| 1452 | } |
| 1453 | } ); |
| 1454 | query.callback( filtered ); |
| 1455 | } |
| 1456 | }; |
| 1457 | } |
| 1458 | |
| 1459 | /** |
| 1460 | * Checks if the formatter function should be used. |
| 1461 | * |
| 1462 | * Throws an error if it is not a function. Returns true if it should be used, |
| 1463 | * false if no formatting should be performed. |
| 1464 | * |
| 1465 | * @param formatter |
| 1466 | */ |
| 1467 | function checkFormatter( formatter, formatterName ) { |
| 1468 | if ( $.isFunction( formatter ) ) return true; |
| 1469 | if ( ! formatter ) return false; |
| 1470 | if ( typeof formatter === 'string' ) return true; |
| 1471 | throw new Error( |
| 1472 | formatterName + ' must be a string, function, or falsy value' |
| 1473 | ); |
| 1474 | } |
| 1475 | |
| 1476 | /** |
| 1477 | * Returns a given value |
| 1478 | * If given a function, returns its output |
| 1479 | * |
| 1480 | * @param val string|function |
| 1481 | * @param context value of "this" to be passed to function |
| 1482 | * @returns {*} |
| 1483 | */ |
| 1484 | function evaluate( val, context ) { |
| 1485 | if ( $.isFunction( val ) ) { |
| 1486 | var args = Array.prototype.slice.call( arguments, 2 ); |
| 1487 | return val.apply( context, args ); |
| 1488 | } |
| 1489 | return val; |
| 1490 | } |
| 1491 | |
| 1492 | function countResults( results ) { |
| 1493 | var count = 0; |
| 1494 | $.each( results, function ( i, item ) { |
| 1495 | if ( item.children ) { |
| 1496 | count += countResults( item.children ); |
| 1497 | } else { |
| 1498 | count++; |
| 1499 | } |
| 1500 | } ); |
| 1501 | return count; |
| 1502 | } |
| 1503 | |
| 1504 | /** |
| 1505 | * Default tokenizer. This function uses breaks the input on substring match of any string from the |
| 1506 | * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those |
| 1507 | * two options have to be defined in order for the tokenizer to work. |
| 1508 | * |
| 1509 | * @param input text user has typed so far or pasted into the search field |
| 1510 | * @param selection currently selected choices |
| 1511 | * @param selectCallback function(choice) callback tho add the choice to selection |
| 1512 | * @param opts select2's opts |
| 1513 | * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value |
| 1514 | */ |
| 1515 | function defaultTokenizer( input, selection, selectCallback, opts ) { |
| 1516 | var original = input, // store the original so we can compare and know if we need to tell the search to update its text |
| 1517 | dupe = false, // check for whether a token we extracted represents a duplicate selected choice |
| 1518 | token, // token |
| 1519 | index, // position at which the separator was found |
| 1520 | i, |
| 1521 | l, // looping variables |
| 1522 | separator; // the matched separator |
| 1523 | |
| 1524 | if ( |
| 1525 | ! opts.createSearchChoice || |
| 1526 | ! opts.tokenSeparators || |
| 1527 | opts.tokenSeparators.length < 1 |
| 1528 | ) |
| 1529 | return undefined; |
| 1530 | |
| 1531 | while ( true ) { |
| 1532 | index = -1; |
| 1533 | |
| 1534 | for ( i = 0, l = opts.tokenSeparators.length; i < l; i++ ) { |
| 1535 | separator = opts.tokenSeparators[ i ]; |
| 1536 | index = input.indexOf( separator ); |
| 1537 | if ( index >= 0 ) break; |
| 1538 | } |
| 1539 | |
| 1540 | if ( index < 0 ) break; // did not find any token separator in the input string, bail |
| 1541 | |
| 1542 | token = input.substring( 0, index ); |
| 1543 | input = input.substring( index + separator.length ); |
| 1544 | |
| 1545 | if ( token.length > 0 ) { |
| 1546 | token = opts.createSearchChoice.call( this, token, selection ); |
| 1547 | if ( |
| 1548 | token !== undefined && |
| 1549 | token !== null && |
| 1550 | opts.id( token ) !== undefined && |
| 1551 | opts.id( token ) !== null |
| 1552 | ) { |
| 1553 | dupe = false; |
| 1554 | for ( i = 0, l = selection.length; i < l; i++ ) { |
| 1555 | if ( |
| 1556 | equal( opts.id( token ), opts.id( selection[ i ] ) ) |
| 1557 | ) { |
| 1558 | dupe = true; |
| 1559 | break; |
| 1560 | } |
| 1561 | } |
| 1562 | |
| 1563 | if ( ! dupe ) selectCallback( token ); |
| 1564 | } |
| 1565 | } |
| 1566 | } |
| 1567 | |
| 1568 | if ( original !== input ) return input; |
| 1569 | } |
| 1570 | |
| 1571 | function cleanupJQueryElements() { |
| 1572 | var self = this; |
| 1573 | |
| 1574 | $.each( arguments, function ( i, element ) { |
| 1575 | self[ element ].remove(); |
| 1576 | self[ element ] = null; |
| 1577 | } ); |
| 1578 | } |
| 1579 | |
| 1580 | /** |
| 1581 | * Creates a new class |
| 1582 | * |
| 1583 | * @param superClass |
| 1584 | * @param methods |
| 1585 | */ |
| 1586 | function clazz( SuperClass, methods ) { |
| 1587 | var constructor = function () {}; |
| 1588 | constructor.prototype = new SuperClass(); |
| 1589 | constructor.prototype.constructor = constructor; |
| 1590 | constructor.prototype.parent = SuperClass.prototype; |
| 1591 | constructor.prototype = $.extend( constructor.prototype, methods ); |
| 1592 | return constructor; |
| 1593 | } |
| 1594 | |
| 1595 | AbstractSelect2 = clazz( Object, { |
| 1596 | // abstract |
| 1597 | bind: function ( func ) { |
| 1598 | var self = this; |
| 1599 | return function () { |
| 1600 | func.apply( self, arguments ); |
| 1601 | }; |
| 1602 | }, |
| 1603 | |
| 1604 | // abstract |
| 1605 | init: function ( opts ) { |
| 1606 | var results, |
| 1607 | search, |
| 1608 | resultsSelector = '.select2-results'; |
| 1609 | |
| 1610 | // prepare options |
| 1611 | this.opts = opts = this.prepareOpts( opts ); |
| 1612 | |
| 1613 | this.id = opts.id; |
| 1614 | |
| 1615 | // destroy if called on an existing component |
| 1616 | if ( |
| 1617 | opts.element.data( 'select2' ) !== undefined && |
| 1618 | opts.element.data( 'select2' ) !== null |
| 1619 | ) { |
| 1620 | opts.element.data( 'select2' ).destroy(); |
| 1621 | } |
| 1622 | |
| 1623 | this.container = this.createContainer(); |
| 1624 | |
| 1625 | this.liveRegion = $( '.select2-hidden-accessible' ); |
| 1626 | if ( this.liveRegion.length == 0 ) { |
| 1627 | this.liveRegion = $( '<span>', { |
| 1628 | role: 'status', |
| 1629 | 'aria-live': 'polite', |
| 1630 | } ) |
| 1631 | .addClass( 'select2-hidden-accessible' ) |
| 1632 | .appendTo( document.body ); |
| 1633 | } |
| 1634 | |
| 1635 | this.containerId = |
| 1636 | 's2id_' + |
| 1637 | ( opts.element.attr( 'id' ) || 'autogen' + nextUid() ); |
| 1638 | this.containerEventName = this.containerId |
| 1639 | .replace( /([.])/g, '_' ) |
| 1640 | .replace( /([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1' ); |
| 1641 | this.container.attr( 'id', this.containerId ); |
| 1642 | |
| 1643 | this.container.attr( 'title', opts.element.attr( 'title' ) ); |
| 1644 | |
| 1645 | this.body = $( document.body ); |
| 1646 | |
| 1647 | syncCssClasses( |
| 1648 | this.container, |
| 1649 | this.opts.element, |
| 1650 | this.opts.adaptContainerCssClass |
| 1651 | ); |
| 1652 | |
| 1653 | this.container.attr( 'style', opts.element.attr( 'style' ) ); |
| 1654 | this.container.css( |
| 1655 | evaluate( opts.containerCss, this.opts.element ) |
| 1656 | ); |
| 1657 | this.container.addClass( |
| 1658 | evaluate( opts.containerCssClass, this.opts.element ) |
| 1659 | ); |
| 1660 | |
| 1661 | this.elementTabIndex = this.opts.element.attr( 'tabindex' ); |
| 1662 | |
| 1663 | // swap container for the element |
| 1664 | this.opts.element |
| 1665 | .data( 'select2', this ) |
| 1666 | .attr( 'tabindex', '-1' ) |
| 1667 | .before( this.container ) |
| 1668 | .on( 'click.select2', killEvent ); // do not leak click events |
| 1669 | |
| 1670 | this.container.data( 'select2', this ); |
| 1671 | |
| 1672 | this.dropdown = this.container.find( '.select2-drop' ); |
| 1673 | |
| 1674 | syncCssClasses( |
| 1675 | this.dropdown, |
| 1676 | this.opts.element, |
| 1677 | this.opts.adaptDropdownCssClass |
| 1678 | ); |
| 1679 | |
| 1680 | this.dropdown.addClass( |
| 1681 | evaluate( opts.dropdownCssClass, this.opts.element ) |
| 1682 | ); |
| 1683 | this.dropdown.data( 'select2', this ); |
| 1684 | this.dropdown.on( 'click', killEvent ); |
| 1685 | |
| 1686 | this.results = results = this.container.find( resultsSelector ); |
| 1687 | this.search = search = this.container.find( 'input.select2-input' ); |
| 1688 | |
| 1689 | this.queryCount = 0; |
| 1690 | this.resultsPage = 0; |
| 1691 | this.context = null; |
| 1692 | |
| 1693 | // initialize the container |
| 1694 | this.initContainer(); |
| 1695 | |
| 1696 | this.container.on( 'click', killEvent ); |
| 1697 | |
| 1698 | installFilteredMouseMove( this.results ); |
| 1699 | |
| 1700 | this.dropdown.on( |
| 1701 | 'mousemove-filtered', |
| 1702 | resultsSelector, |
| 1703 | this.bind( this.highlightUnderEvent ) |
| 1704 | ); |
| 1705 | this.dropdown.on( |
| 1706 | 'touchstart touchmove touchend', |
| 1707 | resultsSelector, |
| 1708 | this.bind( function ( event ) { |
| 1709 | this._touchEvent = true; |
| 1710 | this.highlightUnderEvent( event ); |
| 1711 | } ) |
| 1712 | ); |
| 1713 | this.dropdown.on( |
| 1714 | 'touchmove', |
| 1715 | resultsSelector, |
| 1716 | this.bind( this.touchMoved ) |
| 1717 | ); |
| 1718 | this.dropdown.on( |
| 1719 | 'touchstart touchend', |
| 1720 | resultsSelector, |
| 1721 | this.bind( this.clearTouchMoved ) |
| 1722 | ); |
| 1723 | |
| 1724 | // Waiting for a click event on touch devices to select option and hide dropdown |
| 1725 | // otherwise click will be triggered on an underlying element |
| 1726 | this.dropdown.on( |
| 1727 | 'click', |
| 1728 | this.bind( function ( event ) { |
| 1729 | if ( this._touchEvent ) { |
| 1730 | this._touchEvent = false; |
| 1731 | this.selectHighlighted(); |
| 1732 | } |
| 1733 | } ) |
| 1734 | ); |
| 1735 | |
| 1736 | installDebouncedScroll( 80, this.results ); |
| 1737 | this.dropdown.on( |
| 1738 | 'scroll-debounced', |
| 1739 | resultsSelector, |
| 1740 | this.bind( this.loadMoreIfNeeded ) |
| 1741 | ); |
| 1742 | |
| 1743 | // do not propagate change event from the search field out of the component |
| 1744 | $( this.container ).on( 'change', '.select2-input', function ( e ) { |
| 1745 | e.stopPropagation(); |
| 1746 | } ); |
| 1747 | $( this.dropdown ).on( 'change', '.select2-input', function ( e ) { |
| 1748 | e.stopPropagation(); |
| 1749 | } ); |
| 1750 | |
| 1751 | // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel |
| 1752 | if ( $.fn.mousewheel ) { |
| 1753 | results.mousewheel( function ( e, delta, deltaX, deltaY ) { |
| 1754 | var top = results.scrollTop(); |
| 1755 | if ( deltaY > 0 && top - deltaY <= 0 ) { |
| 1756 | results.scrollTop( 0 ); |
| 1757 | killEvent( e ); |
| 1758 | } else if ( |
| 1759 | deltaY < 0 && |
| 1760 | results.get( 0 ).scrollHeight - |
| 1761 | results.scrollTop() + |
| 1762 | deltaY <= |
| 1763 | results.height() |
| 1764 | ) { |
| 1765 | results.scrollTop( |
| 1766 | results.get( 0 ).scrollHeight - results.height() |
| 1767 | ); |
| 1768 | killEvent( e ); |
| 1769 | } |
| 1770 | } ); |
| 1771 | } |
| 1772 | |
| 1773 | installKeyUpChangeEvent( search ); |
| 1774 | search.on( |
| 1775 | 'keyup-change input paste', |
| 1776 | this.bind( this.updateResults ) |
| 1777 | ); |
| 1778 | search.on( 'focus', function () { |
| 1779 | search.addClass( 'select2-focused' ); |
| 1780 | } ); |
| 1781 | search.on( 'blur', function () { |
| 1782 | search.removeClass( 'select2-focused' ); |
| 1783 | } ); |
| 1784 | |
| 1785 | this.dropdown.on( |
| 1786 | 'mouseup', |
| 1787 | resultsSelector, |
| 1788 | this.bind( function ( e ) { |
| 1789 | if ( |
| 1790 | $( e.target ).closest( '.select2-result-selectable' ) |
| 1791 | .length > 0 |
| 1792 | ) { |
| 1793 | this.highlightUnderEvent( e ); |
| 1794 | this.selectHighlighted( e ); |
| 1795 | } |
| 1796 | } ) |
| 1797 | ); |
| 1798 | |
| 1799 | // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening |
| 1800 | // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's |
| 1801 | // dom it will trigger the popup close, which is not what we want |
| 1802 | // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal. |
| 1803 | this.dropdown.on( |
| 1804 | 'click mouseup mousedown touchstart touchend focusin', |
| 1805 | function ( e ) { |
| 1806 | e.stopPropagation(); |
| 1807 | } |
| 1808 | ); |
| 1809 | |
| 1810 | this.nextSearchTerm = undefined; |
| 1811 | |
| 1812 | if ( $.isFunction( this.opts.initSelection ) ) { |
| 1813 | // initialize selection based on the current value of the source element |
| 1814 | this.initSelection(); |
| 1815 | |
| 1816 | // if the user has provided a function that can set selection based on the value of the source element |
| 1817 | // we monitor the change event on the element and trigger it, allowing for two way synchronization |
| 1818 | this.monitorSource(); |
| 1819 | } |
| 1820 | |
| 1821 | if ( opts.maximumInputLength !== null ) { |
| 1822 | this.search.attr( 'maxlength', opts.maximumInputLength ); |
| 1823 | } |
| 1824 | |
| 1825 | var disabled = opts.element.prop( 'disabled' ); |
| 1826 | if ( disabled === undefined ) disabled = false; |
| 1827 | this.enable( ! disabled ); |
| 1828 | |
| 1829 | var readonly = opts.element.prop( 'readonly' ); |
| 1830 | if ( readonly === undefined ) readonly = false; |
| 1831 | this.readonly( readonly ); |
| 1832 | |
| 1833 | // Calculate size of scrollbar |
| 1834 | scrollBarDimensions = scrollBarDimensions || measureScrollbar(); |
| 1835 | |
| 1836 | this.autofocus = opts.element.prop( 'autofocus' ); |
| 1837 | opts.element.prop( 'autofocus', false ); |
| 1838 | if ( this.autofocus ) this.focus(); |
| 1839 | |
| 1840 | this.search.attr( 'placeholder', opts.searchInputPlaceholder ); |
| 1841 | }, |
| 1842 | |
| 1843 | // abstract |
| 1844 | destroy: function () { |
| 1845 | var element = this.opts.element, |
| 1846 | select2 = element.data( 'select2' ), |
| 1847 | self = this; |
| 1848 | |
| 1849 | this.close(); |
| 1850 | |
| 1851 | if ( element.length && element[ 0 ].detachEvent && self._sync ) { |
| 1852 | element.each( function () { |
| 1853 | if ( self._sync ) { |
| 1854 | this.detachEvent( 'onpropertychange', self._sync ); |
| 1855 | } |
| 1856 | } ); |
| 1857 | } |
| 1858 | if ( this.propertyObserver ) { |
| 1859 | this.propertyObserver.disconnect(); |
| 1860 | this.propertyObserver = null; |
| 1861 | } |
| 1862 | this._sync = null; |
| 1863 | |
| 1864 | if ( select2 !== undefined ) { |
| 1865 | select2.container.remove(); |
| 1866 | select2.liveRegion.remove(); |
| 1867 | select2.dropdown.remove(); |
| 1868 | element |
| 1869 | .show() |
| 1870 | .removeData( 'select2' ) |
| 1871 | .off( '.select2' ) |
| 1872 | .prop( 'autofocus', this.autofocus || false ); |
| 1873 | if ( this.elementTabIndex ) { |
| 1874 | element.attr( { tabindex: this.elementTabIndex } ); |
| 1875 | } else { |
| 1876 | element.removeAttr( 'tabindex' ); |
| 1877 | } |
| 1878 | element.show(); |
| 1879 | } |
| 1880 | |
| 1881 | cleanupJQueryElements.call( |
| 1882 | this, |
| 1883 | 'container', |
| 1884 | 'liveRegion', |
| 1885 | 'dropdown', |
| 1886 | 'results', |
| 1887 | 'search' |
| 1888 | ); |
| 1889 | }, |
| 1890 | |
| 1891 | // abstract |
| 1892 | optionToData: function ( element ) { |
| 1893 | if ( element.is( 'option' ) ) { |
| 1894 | return { |
| 1895 | id: element.prop( 'value' ), |
| 1896 | text: element.text(), |
| 1897 | element: element.get(), |
| 1898 | css: element.attr( 'class' ), |
| 1899 | disabled: element.prop( 'disabled' ), |
| 1900 | locked: |
| 1901 | equal( element.attr( 'locked' ), 'locked' ) || |
| 1902 | equal( element.data( 'locked' ), true ), |
| 1903 | }; |
| 1904 | } else if ( element.is( 'optgroup' ) ) { |
| 1905 | return { |
| 1906 | text: element.attr( 'label' ), |
| 1907 | children: [], |
| 1908 | element: element.get(), |
| 1909 | css: element.attr( 'class' ), |
| 1910 | }; |
| 1911 | } |
| 1912 | }, |
| 1913 | |
| 1914 | // abstract |
| 1915 | prepareOpts: function ( opts ) { |
| 1916 | var element, |
| 1917 | select, |
| 1918 | idKey, |
| 1919 | ajaxUrl, |
| 1920 | self = this; |
| 1921 | |
| 1922 | element = opts.element; |
| 1923 | |
| 1924 | if ( element.get( 0 ).tagName.toLowerCase() === 'select' ) { |
| 1925 | this.select = select = opts.element; |
| 1926 | } |
| 1927 | |
| 1928 | if ( select ) { |
| 1929 | // these options are not allowed when attached to a select because they are picked up off the element itself |
| 1930 | $.each( |
| 1931 | [ |
| 1932 | 'id', |
| 1933 | 'multiple', |
| 1934 | 'ajax', |
| 1935 | 'query', |
| 1936 | 'createSearchChoice', |
| 1937 | 'initSelection', |
| 1938 | 'data', |
| 1939 | 'tags', |
| 1940 | ], |
| 1941 | function () { |
| 1942 | if ( this in opts ) { |
| 1943 | throw new Error( |
| 1944 | "Option '" + |
| 1945 | this + |
| 1946 | "' is not allowed for Select2 when attached to a <select> element." |
| 1947 | ); |
| 1948 | } |
| 1949 | } |
| 1950 | ); |
| 1951 | } |
| 1952 | |
| 1953 | opts = $.extend( |
| 1954 | {}, |
| 1955 | { |
| 1956 | populateResults: function ( container, results, query ) { |
| 1957 | var populate, |
| 1958 | id = this.opts.id, |
| 1959 | liveRegion = this.liveRegion; |
| 1960 | |
| 1961 | populate = function ( results, container, depth ) { |
| 1962 | var i, |
| 1963 | l, |
| 1964 | result, |
| 1965 | selectable, |
| 1966 | disabled, |
| 1967 | compound, |
| 1968 | node, |
| 1969 | label, |
| 1970 | innerContainer, |
| 1971 | formatted; |
| 1972 | |
| 1973 | results = opts.sortResults( |
| 1974 | results, |
| 1975 | container, |
| 1976 | query |
| 1977 | ); |
| 1978 | |
| 1979 | // collect the created nodes for bulk append |
| 1980 | var nodes = []; |
| 1981 | for ( |
| 1982 | i = 0, l = results.length; |
| 1983 | i < l; |
| 1984 | i = i + 1 |
| 1985 | ) { |
| 1986 | result = results[ i ]; |
| 1987 | |
| 1988 | disabled = result.disabled === true; |
| 1989 | selectable = |
| 1990 | ! disabled && id( result ) !== undefined; |
| 1991 | |
| 1992 | compound = |
| 1993 | result.children && |
| 1994 | result.children.length > 0; |
| 1995 | |
| 1996 | node = $( '<li></li>' ); |
| 1997 | node.addClass( |
| 1998 | 'select2-results-dept-' + depth |
| 1999 | ); |
| 2000 | node.addClass( 'select2-result' ); |
| 2001 | node.addClass( |
| 2002 | selectable |
| 2003 | ? 'select2-result-selectable' |
| 2004 | : 'select2-result-unselectable' |
| 2005 | ); |
| 2006 | if ( disabled ) { |
| 2007 | node.addClass( 'select2-disabled' ); |
| 2008 | } |
| 2009 | if ( compound ) { |
| 2010 | node.addClass( |
| 2011 | 'select2-result-with-children' |
| 2012 | ); |
| 2013 | } |
| 2014 | node.addClass( |
| 2015 | self.opts.formatResultCssClass( result ) |
| 2016 | ); |
| 2017 | node.attr( 'role', 'presentation' ); |
| 2018 | |
| 2019 | label = $( document.createElement( 'div' ) ); |
| 2020 | label.addClass( 'select2-result-label' ); |
| 2021 | label.attr( |
| 2022 | 'id', |
| 2023 | 'select2-result-label-' + nextUid() |
| 2024 | ); |
| 2025 | label.attr( 'role', 'option' ); |
| 2026 | |
| 2027 | formatted = opts.formatResult( |
| 2028 | result, |
| 2029 | label, |
| 2030 | query, |
| 2031 | self.opts.escapeMarkup |
| 2032 | ); |
| 2033 | if ( formatted !== undefined ) { |
| 2034 | label.html( formatted ); |
| 2035 | node.append( label ); |
| 2036 | } |
| 2037 | |
| 2038 | if ( compound ) { |
| 2039 | innerContainer = $( '<ul></ul>' ); |
| 2040 | innerContainer.addClass( |
| 2041 | 'select2-result-sub' |
| 2042 | ); |
| 2043 | populate( |
| 2044 | result.children, |
| 2045 | innerContainer, |
| 2046 | depth + 1 |
| 2047 | ); |
| 2048 | node.append( innerContainer ); |
| 2049 | } |
| 2050 | |
| 2051 | node.data( 'select2-data', result ); |
| 2052 | nodes.push( node[ 0 ] ); |
| 2053 | } |
| 2054 | |
| 2055 | // bulk append the created nodes |
| 2056 | container.append( nodes ); |
| 2057 | liveRegion.text( |
| 2058 | opts.formatMatches( results.length ) |
| 2059 | ); |
| 2060 | }; |
| 2061 | |
| 2062 | populate( results, container, 0 ); |
| 2063 | }, |
| 2064 | }, |
| 2065 | $.fn.select2.defaults, |
| 2066 | opts |
| 2067 | ); |
| 2068 | |
| 2069 | if ( typeof opts.id !== 'function' ) { |
| 2070 | idKey = opts.id; |
| 2071 | opts.id = function ( e ) { |
| 2072 | return e[ idKey ]; |
| 2073 | }; |
| 2074 | } |
| 2075 | |
| 2076 | if ( $.isArray( opts.element.data( 'select2Tags' ) ) ) { |
| 2077 | if ( 'tags' in opts ) { |
| 2078 | throw ( |
| 2079 | "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + |
| 2080 | opts.element.attr( 'id' ) |
| 2081 | ); |
| 2082 | } |
| 2083 | opts.tags = opts.element.data( 'select2Tags' ); |
| 2084 | } |
| 2085 | |
| 2086 | if ( select ) { |
| 2087 | opts.query = this.bind( function ( query ) { |
| 2088 | var data = { results: [], more: false }, |
| 2089 | term = query.term, |
| 2090 | children, |
| 2091 | placeholderOption, |
| 2092 | process; |
| 2093 | |
| 2094 | process = function ( element, collection ) { |
| 2095 | var group; |
| 2096 | if ( element.is( 'option' ) ) { |
| 2097 | if ( |
| 2098 | query.matcher( term, element.text(), element ) |
| 2099 | ) { |
| 2100 | collection.push( self.optionToData( element ) ); |
| 2101 | } |
| 2102 | } else if ( element.is( 'optgroup' ) ) { |
| 2103 | group = self.optionToData( element ); |
| 2104 | element.children().each2( function ( i, elm ) { |
| 2105 | process( elm, group.children ); |
| 2106 | } ); |
| 2107 | if ( group.children.length > 0 ) { |
| 2108 | collection.push( group ); |
| 2109 | } |
| 2110 | } |
| 2111 | }; |
| 2112 | |
| 2113 | children = element.children(); |
| 2114 | |
| 2115 | // ignore the placeholder option if there is one |
| 2116 | if ( |
| 2117 | this.getPlaceholder() !== undefined && |
| 2118 | children.length > 0 |
| 2119 | ) { |
| 2120 | placeholderOption = this.getPlaceholderOption(); |
| 2121 | if ( placeholderOption ) { |
| 2122 | children = children.not( placeholderOption ); |
| 2123 | } |
| 2124 | } |
| 2125 | |
| 2126 | children.each2( function ( i, elm ) { |
| 2127 | process( elm, data.results ); |
| 2128 | } ); |
| 2129 | |
| 2130 | query.callback( data ); |
| 2131 | } ); |
| 2132 | // this is needed because inside val() we construct choices from options and their id is hardcoded |
| 2133 | opts.id = function ( e ) { |
| 2134 | return e.id; |
| 2135 | }; |
| 2136 | } else { |
| 2137 | if ( ! ( 'query' in opts ) ) { |
| 2138 | if ( 'ajax' in opts ) { |
| 2139 | ajaxUrl = opts.element.data( 'ajax-url' ); |
| 2140 | if ( ajaxUrl && ajaxUrl.length > 0 ) { |
| 2141 | opts.ajax.url = ajaxUrl; |
| 2142 | } |
| 2143 | opts.query = ajax.call( opts.element, opts.ajax ); |
| 2144 | } else if ( 'data' in opts ) { |
| 2145 | opts.query = local( opts.data ); |
| 2146 | } else if ( 'tags' in opts ) { |
| 2147 | opts.query = tags( opts.tags ); |
| 2148 | if ( opts.createSearchChoice === undefined ) { |
| 2149 | opts.createSearchChoice = function ( term ) { |
| 2150 | return { |
| 2151 | id: $.trim( term ), |
| 2152 | text: $.trim( term ), |
| 2153 | }; |
| 2154 | }; |
| 2155 | } |
| 2156 | if ( opts.initSelection === undefined ) { |
| 2157 | opts.initSelection = function ( |
| 2158 | element, |
| 2159 | callback |
| 2160 | ) { |
| 2161 | var data = []; |
| 2162 | $( |
| 2163 | splitVal( |
| 2164 | element.val(), |
| 2165 | opts.separator, |
| 2166 | opts.transformVal |
| 2167 | ) |
| 2168 | ).each( function () { |
| 2169 | var obj = { id: this, text: this }, |
| 2170 | tags = opts.tags; |
| 2171 | if ( $.isFunction( tags ) ) tags = tags(); |
| 2172 | $( tags ).each( function () { |
| 2173 | if ( equal( this.id, obj.id ) ) { |
| 2174 | obj = this; |
| 2175 | return false; |
| 2176 | } |
| 2177 | } ); |
| 2178 | data.push( obj ); |
| 2179 | } ); |
| 2180 | |
| 2181 | callback( data ); |
| 2182 | }; |
| 2183 | } |
| 2184 | } |
| 2185 | } |
| 2186 | } |
| 2187 | if ( typeof opts.query !== 'function' ) { |
| 2188 | throw ( |
| 2189 | 'query function not defined for Select2 ' + |
| 2190 | opts.element.attr( 'id' ) |
| 2191 | ); |
| 2192 | } |
| 2193 | |
| 2194 | if ( opts.createSearchChoicePosition === 'top' ) { |
| 2195 | opts.createSearchChoicePosition = function ( list, item ) { |
| 2196 | list.unshift( item ); |
| 2197 | }; |
| 2198 | } else if ( opts.createSearchChoicePosition === 'bottom' ) { |
| 2199 | opts.createSearchChoicePosition = function ( list, item ) { |
| 2200 | list.push( item ); |
| 2201 | }; |
| 2202 | } else if ( |
| 2203 | typeof opts.createSearchChoicePosition !== 'function' |
| 2204 | ) { |
| 2205 | throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function"; |
| 2206 | } |
| 2207 | |
| 2208 | return opts; |
| 2209 | }, |
| 2210 | |
| 2211 | /** |
| 2212 | * Monitor the original element for changes and update select2 accordingly |
| 2213 | */ |
| 2214 | // abstract |
| 2215 | monitorSource: function () { |
| 2216 | var el = this.opts.element, |
| 2217 | observer, |
| 2218 | self = this; |
| 2219 | |
| 2220 | el.on( |
| 2221 | 'change.select2', |
| 2222 | this.bind( function ( e ) { |
| 2223 | if ( |
| 2224 | this.opts.element.data( 'select2-change-triggered' ) !== |
| 2225 | true |
| 2226 | ) { |
| 2227 | this.initSelection(); |
| 2228 | } |
| 2229 | } ) |
| 2230 | ); |
| 2231 | |
| 2232 | this._sync = this.bind( function () { |
| 2233 | // sync enabled state |
| 2234 | var disabled = el.prop( 'disabled' ); |
| 2235 | if ( disabled === undefined ) disabled = false; |
| 2236 | this.enable( ! disabled ); |
| 2237 | |
| 2238 | var readonly = el.prop( 'readonly' ); |
| 2239 | if ( readonly === undefined ) readonly = false; |
| 2240 | this.readonly( readonly ); |
| 2241 | |
| 2242 | if ( this.container ) { |
| 2243 | syncCssClasses( |
| 2244 | this.container, |
| 2245 | this.opts.element, |
| 2246 | this.opts.adaptContainerCssClass |
| 2247 | ); |
| 2248 | this.container.addClass( |
| 2249 | evaluate( |
| 2250 | this.opts.containerCssClass, |
| 2251 | this.opts.element |
| 2252 | ) |
| 2253 | ); |
| 2254 | } |
| 2255 | |
| 2256 | if ( this.dropdown ) { |
| 2257 | syncCssClasses( |
| 2258 | this.dropdown, |
| 2259 | this.opts.element, |
| 2260 | this.opts.adaptDropdownCssClass |
| 2261 | ); |
| 2262 | this.dropdown.addClass( |
| 2263 | evaluate( |
| 2264 | this.opts.dropdownCssClass, |
| 2265 | this.opts.element |
| 2266 | ) |
| 2267 | ); |
| 2268 | } |
| 2269 | } ); |
| 2270 | |
| 2271 | // IE8-10 (IE9/10 won't fire propertyChange via attachEventListener) |
| 2272 | if ( el.length && el[ 0 ].attachEvent ) { |
| 2273 | el.each( function () { |
| 2274 | this.attachEvent( 'onpropertychange', self._sync ); |
| 2275 | } ); |
| 2276 | } |
| 2277 | |
| 2278 | // safari, chrome, firefox, IE11 |
| 2279 | observer = |
| 2280 | window.MutationObserver || |
| 2281 | window.WebKitMutationObserver || |
| 2282 | window.MozMutationObserver; |
| 2283 | if ( observer !== undefined ) { |
| 2284 | if ( this.propertyObserver ) { |
| 2285 | delete this.propertyObserver; |
| 2286 | this.propertyObserver = null; |
| 2287 | } |
| 2288 | this.propertyObserver = new observer( function ( mutations ) { |
| 2289 | $.each( mutations, self._sync ); |
| 2290 | } ); |
| 2291 | this.propertyObserver.observe( el.get( 0 ), { |
| 2292 | attributes: true, |
| 2293 | subtree: false, |
| 2294 | } ); |
| 2295 | } |
| 2296 | }, |
| 2297 | |
| 2298 | // abstract |
| 2299 | triggerSelect: function ( data ) { |
| 2300 | var evt = $.Event( 'select2-selecting', { |
| 2301 | val: this.id( data ), |
| 2302 | object: data, |
| 2303 | choice: data, |
| 2304 | } ); |
| 2305 | this.opts.element.trigger( evt ); |
| 2306 | return ! evt.isDefaultPrevented(); |
| 2307 | }, |
| 2308 | |
| 2309 | /** |
| 2310 | * Triggers the change event on the source element |
| 2311 | */ |
| 2312 | // abstract |
| 2313 | triggerChange: function ( details ) { |
| 2314 | details = details || {}; |
| 2315 | details = $.extend( {}, details, { |
| 2316 | type: 'change', |
| 2317 | val: this.val(), |
| 2318 | } ); |
| 2319 | // prevents recursive triggering |
| 2320 | this.opts.element.data( 'select2-change-triggered', true ); |
| 2321 | this.opts.element.trigger( details ); |
| 2322 | this.opts.element.data( 'select2-change-triggered', false ); |
| 2323 | |
| 2324 | // some validation frameworks ignore the change event and listen instead to keyup, click for selects |
| 2325 | // so here we trigger the click event manually |
| 2326 | this.opts.element.click(); |
| 2327 | |
| 2328 | // ValidationEngine ignores the change event and listens instead to blur |
| 2329 | // so here we trigger the blur event manually if so desired |
| 2330 | if ( this.opts.blurOnChange ) this.opts.element.blur(); |
| 2331 | }, |
| 2332 | |
| 2333 | //abstract |
| 2334 | isInterfaceEnabled: function () { |
| 2335 | return this.enabledInterface === true; |
| 2336 | }, |
| 2337 | |
| 2338 | // abstract |
| 2339 | enableInterface: function () { |
| 2340 | var enabled = this._enabled && ! this._readonly, |
| 2341 | disabled = ! enabled; |
| 2342 | |
| 2343 | if ( enabled === this.enabledInterface ) return false; |
| 2344 | |
| 2345 | this.container.toggleClass( |
| 2346 | 'select2-container-disabled', |
| 2347 | disabled |
| 2348 | ); |
| 2349 | this.close(); |
| 2350 | this.enabledInterface = enabled; |
| 2351 | |
| 2352 | return true; |
| 2353 | }, |
| 2354 | |
| 2355 | // abstract |
| 2356 | enable: function ( enabled ) { |
| 2357 | if ( enabled === undefined ) enabled = true; |
| 2358 | if ( this._enabled === enabled ) return; |
| 2359 | this._enabled = enabled; |
| 2360 | |
| 2361 | this.opts.element.prop( 'disabled', ! enabled ); |
| 2362 | this.enableInterface(); |
| 2363 | }, |
| 2364 | |
| 2365 | // abstract |
| 2366 | disable: function () { |
| 2367 | this.enable( false ); |
| 2368 | }, |
| 2369 | |
| 2370 | // abstract |
| 2371 | readonly: function ( enabled ) { |
| 2372 | if ( enabled === undefined ) enabled = false; |
| 2373 | if ( this._readonly === enabled ) return; |
| 2374 | this._readonly = enabled; |
| 2375 | |
| 2376 | this.opts.element.prop( 'readonly', enabled ); |
| 2377 | this.enableInterface(); |
| 2378 | }, |
| 2379 | |
| 2380 | // abstract |
| 2381 | opened: function () { |
| 2382 | return this.container |
| 2383 | ? this.container.hasClass( 'select2-dropdown-open' ) |
| 2384 | : false; |
| 2385 | }, |
| 2386 | |
| 2387 | // abstract |
| 2388 | positionDropdown: function () { |
| 2389 | var $dropdown = this.dropdown, |
| 2390 | container = this.container, |
| 2391 | offset = container.offset(), |
| 2392 | height = container.outerHeight( false ), |
| 2393 | width = container.outerWidth( false ), |
| 2394 | dropHeight = $dropdown.outerHeight( false ), |
| 2395 | $window = $( window ), |
| 2396 | windowWidth = $window.width(), |
| 2397 | windowHeight = $window.height(), |
| 2398 | viewPortRight = $window.scrollLeft() + windowWidth, |
| 2399 | viewportBottom = $window.scrollTop() + windowHeight, |
| 2400 | dropTop = offset.top + height, |
| 2401 | dropLeft = offset.left, |
| 2402 | enoughRoomBelow = dropTop + dropHeight <= viewportBottom, |
| 2403 | enoughRoomAbove = |
| 2404 | offset.top - dropHeight >= $window.scrollTop(), |
| 2405 | dropWidth = $dropdown.outerWidth( false ), |
| 2406 | enoughRoomOnRight = function () { |
| 2407 | return dropLeft + dropWidth <= viewPortRight; |
| 2408 | }, |
| 2409 | enoughRoomOnLeft = function () { |
| 2410 | return ( |
| 2411 | offset.left + |
| 2412 | viewPortRight + |
| 2413 | container.outerWidth( false ) > |
| 2414 | dropWidth |
| 2415 | ); |
| 2416 | }, |
| 2417 | aboveNow = $dropdown.hasClass( 'select2-drop-above' ), |
| 2418 | bodyOffset, |
| 2419 | above, |
| 2420 | changeDirection, |
| 2421 | css, |
| 2422 | resultsListNode; |
| 2423 | |
| 2424 | // always prefer the current above/below alignment, unless there is not enough room |
| 2425 | if ( aboveNow ) { |
| 2426 | above = true; |
| 2427 | if ( ! enoughRoomAbove && enoughRoomBelow ) { |
| 2428 | changeDirection = true; |
| 2429 | above = false; |
| 2430 | } |
| 2431 | } else { |
| 2432 | above = false; |
| 2433 | if ( ! enoughRoomBelow && enoughRoomAbove ) { |
| 2434 | changeDirection = true; |
| 2435 | above = true; |
| 2436 | } |
| 2437 | } |
| 2438 | |
| 2439 | //if we are changing direction we need to get positions when dropdown is hidden; |
| 2440 | if ( changeDirection ) { |
| 2441 | $dropdown.hide(); |
| 2442 | offset = this.container.offset(); |
| 2443 | height = this.container.outerHeight( false ); |
| 2444 | width = this.container.outerWidth( false ); |
| 2445 | dropHeight = $dropdown.outerHeight( false ); |
| 2446 | viewPortRight = $window.scrollLeft() + windowWidth; |
| 2447 | viewportBottom = $window.scrollTop() + windowHeight; |
| 2448 | dropTop = offset.top + height; |
| 2449 | dropLeft = offset.left; |
| 2450 | dropWidth = $dropdown.outerWidth( false ); |
| 2451 | $dropdown.show(); |
| 2452 | |
| 2453 | // fix so the cursor does not move to the left within the search-textbox in IE |
| 2454 | this.focusSearch(); |
| 2455 | } |
| 2456 | |
| 2457 | if ( this.opts.dropdownAutoWidth ) { |
| 2458 | resultsListNode = $( '.select2-results', $dropdown )[ 0 ]; |
| 2459 | $dropdown.addClass( 'select2-drop-auto-width' ); |
| 2460 | $dropdown.css( 'width', '' ); |
| 2461 | // Add scrollbar width to dropdown if vertical scrollbar is present |
| 2462 | dropWidth = |
| 2463 | $dropdown.outerWidth( false ) + |
| 2464 | ( resultsListNode.scrollHeight === |
| 2465 | resultsListNode.clientHeight |
| 2466 | ? 0 |
| 2467 | : scrollBarDimensions.width ); |
| 2468 | dropWidth > width |
| 2469 | ? ( width = dropWidth ) |
| 2470 | : ( dropWidth = width ); |
| 2471 | dropHeight = $dropdown.outerHeight( false ); |
| 2472 | } else { |
| 2473 | this.container.removeClass( 'select2-drop-auto-width' ); |
| 2474 | } |
| 2475 | |
| 2476 | //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow); |
| 2477 | //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body.scrollTop(), "enough?", enoughRoomAbove); |
| 2478 | |
| 2479 | // fix positioning when body has an offset and is not position: static |
| 2480 | if ( this.body.css( 'position' ) !== 'static' ) { |
| 2481 | bodyOffset = this.body.offset(); |
| 2482 | dropTop -= bodyOffset.top; |
| 2483 | dropLeft -= bodyOffset.left; |
| 2484 | } |
| 2485 | |
| 2486 | if ( ! enoughRoomOnRight() && enoughRoomOnLeft() ) { |
| 2487 | dropLeft = |
| 2488 | offset.left + |
| 2489 | this.container.outerWidth( false ) - |
| 2490 | dropWidth; |
| 2491 | } |
| 2492 | |
| 2493 | css = { |
| 2494 | left: dropLeft, |
| 2495 | width: width, |
| 2496 | }; |
| 2497 | |
| 2498 | if ( above ) { |
| 2499 | css.top = offset.top - dropHeight; |
| 2500 | css.bottom = 'auto'; |
| 2501 | this.container.addClass( 'select2-drop-above' ); |
| 2502 | $dropdown.addClass( 'select2-drop-above' ); |
| 2503 | } else { |
| 2504 | css.top = dropTop; |
| 2505 | css.bottom = 'auto'; |
| 2506 | this.container.removeClass( 'select2-drop-above' ); |
| 2507 | $dropdown.removeClass( 'select2-drop-above' ); |
| 2508 | } |
| 2509 | css = $.extend( |
| 2510 | css, |
| 2511 | evaluate( this.opts.dropdownCss, this.opts.element ) |
| 2512 | ); |
| 2513 | |
| 2514 | $dropdown.css( css ); |
| 2515 | }, |
| 2516 | |
| 2517 | // abstract |
| 2518 | shouldOpen: function () { |
| 2519 | var event; |
| 2520 | |
| 2521 | if ( this.opened() ) return false; |
| 2522 | |
| 2523 | if ( this._enabled === false || this._readonly === true ) |
| 2524 | return false; |
| 2525 | |
| 2526 | event = $.Event( 'select2-opening' ); |
| 2527 | this.opts.element.trigger( event ); |
| 2528 | return ! event.isDefaultPrevented(); |
| 2529 | }, |
| 2530 | |
| 2531 | // abstract |
| 2532 | clearDropdownAlignmentPreference: function () { |
| 2533 | // clear the classes used to figure out the preference of where the dropdown should be opened |
| 2534 | this.container.removeClass( 'select2-drop-above' ); |
| 2535 | this.dropdown.removeClass( 'select2-drop-above' ); |
| 2536 | }, |
| 2537 | |
| 2538 | /** |
| 2539 | * Opens the dropdown |
| 2540 | * |
| 2541 | * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example, |
| 2542 | * the dropdown is already open, or if the 'open' event listener on the element called preventDefault(). |
| 2543 | */ |
| 2544 | // abstract |
| 2545 | open: function () { |
| 2546 | if ( ! this.shouldOpen() ) return false; |
| 2547 | |
| 2548 | this.opening(); |
| 2549 | |
| 2550 | // Only bind the document mousemove when the dropdown is visible |
| 2551 | $document.on( 'mousemove.select2Event', function ( e ) { |
| 2552 | lastMousePosition.x = e.pageX; |
| 2553 | lastMousePosition.y = e.pageY; |
| 2554 | } ); |
| 2555 | |
| 2556 | return true; |
| 2557 | }, |
| 2558 | |
| 2559 | /** |
| 2560 | * Performs the opening of the dropdown |
| 2561 | */ |
| 2562 | // abstract |
| 2563 | opening: function () { |
| 2564 | var cid = this.containerEventName, |
| 2565 | scroll = 'scroll.' + cid, |
| 2566 | resize = 'resize.' + cid, |
| 2567 | orient = 'orientationchange.' + cid, |
| 2568 | mask; |
| 2569 | |
| 2570 | this.container |
| 2571 | .addClass( 'select2-dropdown-open' ) |
| 2572 | .addClass( 'select2-container-active' ); |
| 2573 | |
| 2574 | this.clearDropdownAlignmentPreference(); |
| 2575 | |
| 2576 | if ( this.dropdown[ 0 ] !== this.body.children().last()[ 0 ] ) { |
| 2577 | this.dropdown.detach().appendTo( this.body ); |
| 2578 | } |
| 2579 | |
| 2580 | // create the dropdown mask if doesn't already exist |
| 2581 | mask = $( '#select2-drop-mask' ); |
| 2582 | if ( mask.length === 0 ) { |
| 2583 | mask = $( document.createElement( 'div' ) ); |
| 2584 | mask.attr( 'id', 'select2-drop-mask' ).attr( |
| 2585 | 'class', |
| 2586 | 'select2-drop-mask' |
| 2587 | ); |
| 2588 | mask.hide(); |
| 2589 | mask.appendTo( this.body ); |
| 2590 | mask.on( 'mousedown touchstart click', function ( e ) { |
| 2591 | // Prevent IE from generating a click event on the body |
| 2592 | reinsertElement( mask ); |
| 2593 | |
| 2594 | var dropdown = $( '#select2-drop' ), |
| 2595 | self; |
| 2596 | if ( dropdown.length > 0 ) { |
| 2597 | self = dropdown.data( 'select2' ); |
| 2598 | if ( self.opts.selectOnBlur ) { |
| 2599 | self.selectHighlighted( { noFocus: true } ); |
| 2600 | } |
| 2601 | self.close(); |
| 2602 | e.preventDefault(); |
| 2603 | e.stopPropagation(); |
| 2604 | } |
| 2605 | } ); |
| 2606 | } |
| 2607 | |
| 2608 | // ensure the mask is always right before the dropdown |
| 2609 | if ( this.dropdown.prev()[ 0 ] !== mask[ 0 ] ) { |
| 2610 | this.dropdown.before( mask ); |
| 2611 | } |
| 2612 | |
| 2613 | // move the global id to the correct dropdown |
| 2614 | $( '#select2-drop' ).removeAttr( 'id' ); |
| 2615 | this.dropdown.attr( 'id', 'select2-drop' ); |
| 2616 | |
| 2617 | // show the elements |
| 2618 | mask.show(); |
| 2619 | |
| 2620 | this.positionDropdown(); |
| 2621 | this.dropdown.show(); |
| 2622 | this.positionDropdown(); |
| 2623 | |
| 2624 | this.dropdown.addClass( 'select2-drop-active' ); |
| 2625 | |
| 2626 | // attach listeners to events that can change the position of the container and thus require |
| 2627 | // the position of the dropdown to be updated as well so it does not come unglued from the container |
| 2628 | var that = this; |
| 2629 | this.container |
| 2630 | .parents() |
| 2631 | .add( window ) |
| 2632 | .each( function () { |
| 2633 | $( this ).on( |
| 2634 | resize + ' ' + scroll + ' ' + orient, |
| 2635 | function ( e ) { |
| 2636 | if ( that.opened() ) that.positionDropdown(); |
| 2637 | } |
| 2638 | ); |
| 2639 | } ); |
| 2640 | }, |
| 2641 | |
| 2642 | // abstract |
| 2643 | close: function () { |
| 2644 | if ( ! this.opened() ) return; |
| 2645 | |
| 2646 | var cid = this.containerEventName, |
| 2647 | scroll = 'scroll.' + cid, |
| 2648 | resize = 'resize.' + cid, |
| 2649 | orient = 'orientationchange.' + cid; |
| 2650 | |
| 2651 | // unbind event listeners |
| 2652 | this.container |
| 2653 | .parents() |
| 2654 | .add( window ) |
| 2655 | .each( function () { |
| 2656 | $( this ).off( scroll ).off( resize ).off( orient ); |
| 2657 | } ); |
| 2658 | |
| 2659 | this.clearDropdownAlignmentPreference(); |
| 2660 | |
| 2661 | $( '#select2-drop-mask' ).hide(); |
| 2662 | this.dropdown.removeAttr( 'id' ); // only the active dropdown has the select2-drop id |
| 2663 | this.dropdown.hide(); |
| 2664 | this.container |
| 2665 | .removeClass( 'select2-dropdown-open' ) |
| 2666 | .removeClass( 'select2-container-active' ); |
| 2667 | this.results.empty(); |
| 2668 | |
| 2669 | // Now that the dropdown is closed, unbind the global document mousemove event |
| 2670 | $document.off( 'mousemove.select2Event' ); |
| 2671 | |
| 2672 | this.clearSearch(); |
| 2673 | this.search.removeClass( 'select2-active' ); |
| 2674 | this.opts.element.trigger( $.Event( 'select2-close' ) ); |
| 2675 | }, |
| 2676 | |
| 2677 | /** |
| 2678 | * Opens control, sets input value, and updates results. |
| 2679 | */ |
| 2680 | // abstract |
| 2681 | externalSearch: function ( term ) { |
| 2682 | this.open(); |
| 2683 | this.search.val( term ); |
| 2684 | this.updateResults( false ); |
| 2685 | }, |
| 2686 | |
| 2687 | // abstract |
| 2688 | clearSearch: function () {}, |
| 2689 | |
| 2690 | //abstract |
| 2691 | getMaximumSelectionSize: function () { |
| 2692 | return evaluate( |
| 2693 | this.opts.maximumSelectionSize, |
| 2694 | this.opts.element |
| 2695 | ); |
| 2696 | }, |
| 2697 | |
| 2698 | // abstract |
| 2699 | ensureHighlightVisible: function () { |
| 2700 | var results = this.results, |
| 2701 | children, |
| 2702 | index, |
| 2703 | child, |
| 2704 | hb, |
| 2705 | rb, |
| 2706 | y, |
| 2707 | more, |
| 2708 | topOffset; |
| 2709 | |
| 2710 | index = this.highlight(); |
| 2711 | |
| 2712 | if ( index < 0 ) return; |
| 2713 | |
| 2714 | if ( index == 0 ) { |
| 2715 | // if the first element is highlighted scroll all the way to the top, |
| 2716 | // that way any unselectable headers above it will also be scrolled |
| 2717 | // into view |
| 2718 | |
| 2719 | results.scrollTop( 0 ); |
| 2720 | return; |
| 2721 | } |
| 2722 | |
| 2723 | children = this.findHighlightableChoices().find( |
| 2724 | '.select2-result-label' |
| 2725 | ); |
| 2726 | |
| 2727 | child = $( children[ index ] ); |
| 2728 | |
| 2729 | topOffset = ( child.offset() || {} ).top || 0; |
| 2730 | |
| 2731 | hb = topOffset + child.outerHeight( true ); |
| 2732 | |
| 2733 | // if this is the last child lets also make sure select2-more-results is visible |
| 2734 | if ( index === children.length - 1 ) { |
| 2735 | more = results.find( 'li.select2-more-results' ); |
| 2736 | if ( more.length > 0 ) { |
| 2737 | hb = more.offset().top + more.outerHeight( true ); |
| 2738 | } |
| 2739 | } |
| 2740 | |
| 2741 | rb = results.offset().top + results.outerHeight( false ); |
| 2742 | if ( hb > rb ) { |
| 2743 | results.scrollTop( results.scrollTop() + ( hb - rb ) ); |
| 2744 | } |
| 2745 | y = topOffset - results.offset().top; |
| 2746 | |
| 2747 | // make sure the top of the element is visible |
| 2748 | if ( y < 0 && child.css( 'display' ) != 'none' ) { |
| 2749 | results.scrollTop( results.scrollTop() + y ); // y is negative |
| 2750 | } |
| 2751 | }, |
| 2752 | |
| 2753 | // abstract |
| 2754 | findHighlightableChoices: function () { |
| 2755 | return this.results.find( |
| 2756 | '.select2-result-selectable:not(.select2-disabled):not(.select2-selected)' |
| 2757 | ); |
| 2758 | }, |
| 2759 | |
| 2760 | // abstract |
| 2761 | moveHighlight: function ( delta ) { |
| 2762 | var choices = this.findHighlightableChoices(), |
| 2763 | index = this.highlight(); |
| 2764 | |
| 2765 | while ( index > -1 && index < choices.length ) { |
| 2766 | index += delta; |
| 2767 | var choice = $( choices[ index ] ); |
| 2768 | if ( |
| 2769 | choice.hasClass( 'select2-result-selectable' ) && |
| 2770 | ! choice.hasClass( 'select2-disabled' ) && |
| 2771 | ! choice.hasClass( 'select2-selected' ) |
| 2772 | ) { |
| 2773 | this.highlight( index ); |
| 2774 | break; |
| 2775 | } |
| 2776 | } |
| 2777 | }, |
| 2778 | |
| 2779 | // abstract |
| 2780 | highlight: function ( index ) { |
| 2781 | var choices = this.findHighlightableChoices(), |
| 2782 | choice, |
| 2783 | data; |
| 2784 | |
| 2785 | if ( arguments.length === 0 ) { |
| 2786 | return indexOf( |
| 2787 | choices.filter( '.select2-highlighted' )[ 0 ], |
| 2788 | choices.get() |
| 2789 | ); |
| 2790 | } |
| 2791 | |
| 2792 | if ( index >= choices.length ) index = choices.length - 1; |
| 2793 | if ( index < 0 ) index = 0; |
| 2794 | |
| 2795 | this.removeHighlight(); |
| 2796 | |
| 2797 | choice = $( choices[ index ] ); |
| 2798 | choice.addClass( 'select2-highlighted' ); |
| 2799 | |
| 2800 | // ensure assistive technology can determine the active choice |
| 2801 | this.search.attr( |
| 2802 | 'aria-activedescendant', |
| 2803 | choice.find( '.select2-result-label' ).attr( 'id' ) |
| 2804 | ); |
| 2805 | |
| 2806 | this.ensureHighlightVisible(); |
| 2807 | |
| 2808 | this.liveRegion.text( choice.text() ); |
| 2809 | |
| 2810 | data = choice.data( 'select2-data' ); |
| 2811 | if ( data ) { |
| 2812 | this.opts.element.trigger( { |
| 2813 | type: 'select2-highlight', |
| 2814 | val: this.id( data ), |
| 2815 | choice: data, |
| 2816 | } ); |
| 2817 | } |
| 2818 | }, |
| 2819 | |
| 2820 | removeHighlight: function () { |
| 2821 | this.results |
| 2822 | .find( '.select2-highlighted' ) |
| 2823 | .removeClass( 'select2-highlighted' ); |
| 2824 | }, |
| 2825 | |
| 2826 | touchMoved: function () { |
| 2827 | this._touchMoved = true; |
| 2828 | }, |
| 2829 | |
| 2830 | clearTouchMoved: function () { |
| 2831 | this._touchMoved = false; |
| 2832 | }, |
| 2833 | |
| 2834 | // abstract |
| 2835 | countSelectableResults: function () { |
| 2836 | return this.findHighlightableChoices().length; |
| 2837 | }, |
| 2838 | |
| 2839 | // abstract |
| 2840 | highlightUnderEvent: function ( event ) { |
| 2841 | var el = $( event.target ).closest( '.select2-result-selectable' ); |
| 2842 | if ( el.length > 0 && ! el.is( '.select2-highlighted' ) ) { |
| 2843 | var choices = this.findHighlightableChoices(); |
| 2844 | this.highlight( choices.index( el ) ); |
| 2845 | } else if ( el.length == 0 ) { |
| 2846 | // if we are over an unselectable item remove all highlights |
| 2847 | this.removeHighlight(); |
| 2848 | } |
| 2849 | }, |
| 2850 | |
| 2851 | // abstract |
| 2852 | loadMoreIfNeeded: function () { |
| 2853 | var results = this.results, |
| 2854 | more = results.find( 'li.select2-more-results' ), |
| 2855 | below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible |
| 2856 | page = this.resultsPage + 1, |
| 2857 | self = this, |
| 2858 | term = this.search.val(), |
| 2859 | context = this.context; |
| 2860 | |
| 2861 | if ( more.length === 0 ) return; |
| 2862 | below = more.offset().top - results.offset().top - results.height(); |
| 2863 | |
| 2864 | if ( below <= this.opts.loadMorePadding ) { |
| 2865 | more.addClass( 'select2-active' ); |
| 2866 | this.opts.query( { |
| 2867 | element: this.opts.element, |
| 2868 | term: term, |
| 2869 | page: page, |
| 2870 | context: context, |
| 2871 | matcher: this.opts.matcher, |
| 2872 | callback: this.bind( function ( data ) { |
| 2873 | // ignore a response if the select2 has been closed before it was received |
| 2874 | if ( ! self.opened() ) return; |
| 2875 | |
| 2876 | self.opts.populateResults.call( |
| 2877 | this, |
| 2878 | results, |
| 2879 | data.results, |
| 2880 | { term: term, page: page, context: context } |
| 2881 | ); |
| 2882 | self.postprocessResults( data, false, false ); |
| 2883 | |
| 2884 | if ( data.more === true ) { |
| 2885 | more.detach() |
| 2886 | .appendTo( results ) |
| 2887 | .html( |
| 2888 | self.opts.escapeMarkup( |
| 2889 | evaluate( |
| 2890 | self.opts.formatLoadMore, |
| 2891 | self.opts.element, |
| 2892 | page + 1 |
| 2893 | ) |
| 2894 | ) |
| 2895 | ); |
| 2896 | window.setTimeout( function () { |
| 2897 | self.loadMoreIfNeeded(); |
| 2898 | }, 10 ); |
| 2899 | } else { |
| 2900 | more.remove(); |
| 2901 | } |
| 2902 | self.positionDropdown(); |
| 2903 | self.resultsPage = page; |
| 2904 | self.context = data.context; |
| 2905 | this.opts.element.trigger( { |
| 2906 | type: 'select2-loaded', |
| 2907 | items: data, |
| 2908 | } ); |
| 2909 | } ), |
| 2910 | } ); |
| 2911 | } |
| 2912 | }, |
| 2913 | |
| 2914 | /** |
| 2915 | * Default tokenizer function which does nothing |
| 2916 | */ |
| 2917 | tokenize: function () {}, |
| 2918 | |
| 2919 | /** |
| 2920 | * @param initial whether or not this is the call to this method right after the dropdown has been opened |
| 2921 | */ |
| 2922 | // abstract |
| 2923 | updateResults: function ( initial ) { |
| 2924 | var search = this.search, |
| 2925 | results = this.results, |
| 2926 | opts = this.opts, |
| 2927 | data, |
| 2928 | self = this, |
| 2929 | input, |
| 2930 | term = search.val(), |
| 2931 | lastTerm = $.data( this.container, 'select2-last-term' ), |
| 2932 | // sequence number used to drop out-of-order responses |
| 2933 | queryNumber; |
| 2934 | |
| 2935 | // prevent duplicate queries against the same term |
| 2936 | if ( initial !== true && lastTerm && equal( term, lastTerm ) ) |
| 2937 | return; |
| 2938 | |
| 2939 | $.data( this.container, 'select2-last-term', term ); |
| 2940 | |
| 2941 | // if the search is currently hidden we do not alter the results |
| 2942 | if ( |
| 2943 | initial !== true && |
| 2944 | ( this.showSearchInput === false || ! this.opened() ) |
| 2945 | ) { |
| 2946 | return; |
| 2947 | } |
| 2948 | |
| 2949 | function postRender() { |
| 2950 | search.removeClass( 'select2-active' ); |
| 2951 | self.positionDropdown(); |
| 2952 | if ( |
| 2953 | results.find( |
| 2954 | '.select2-no-results,.select2-selection-limit,.select2-searching' |
| 2955 | ).length |
| 2956 | ) { |
| 2957 | self.liveRegion.text( results.text() ); |
| 2958 | } else { |
| 2959 | self.liveRegion.text( |
| 2960 | self.opts.formatMatches( |
| 2961 | results.find( |
| 2962 | '.select2-result-selectable:not(".select2-selected")' |
| 2963 | ).length |
| 2964 | ) |
| 2965 | ); |
| 2966 | } |
| 2967 | } |
| 2968 | |
| 2969 | function render( html ) { |
| 2970 | results.html( html ); |
| 2971 | postRender(); |
| 2972 | } |
| 2973 | |
| 2974 | queryNumber = ++this.queryCount; |
| 2975 | |
| 2976 | var maxSelSize = this.getMaximumSelectionSize(); |
| 2977 | if ( maxSelSize >= 1 ) { |
| 2978 | data = this.data(); |
| 2979 | if ( |
| 2980 | $.isArray( data ) && |
| 2981 | data.length >= maxSelSize && |
| 2982 | checkFormatter( |
| 2983 | opts.formatSelectionTooBig, |
| 2984 | 'formatSelectionTooBig' |
| 2985 | ) |
| 2986 | ) { |
| 2987 | render( |
| 2988 | "<li class='select2-selection-limit'>" + |
| 2989 | evaluate( |
| 2990 | opts.formatSelectionTooBig, |
| 2991 | opts.element, |
| 2992 | maxSelSize |
| 2993 | ) + |
| 2994 | '</li>' |
| 2995 | ); |
| 2996 | return; |
| 2997 | } |
| 2998 | } |
| 2999 | |
| 3000 | if ( search.val().length < opts.minimumInputLength ) { |
| 3001 | if ( |
| 3002 | checkFormatter( |
| 3003 | opts.formatInputTooShort, |
| 3004 | 'formatInputTooShort' |
| 3005 | ) |
| 3006 | ) { |
| 3007 | render( |
| 3008 | "<li class='select2-no-results'>" + |
| 3009 | evaluate( |
| 3010 | opts.formatInputTooShort, |
| 3011 | opts.element, |
| 3012 | search.val(), |
| 3013 | opts.minimumInputLength |
| 3014 | ) + |
| 3015 | '</li>' |
| 3016 | ); |
| 3017 | } else { |
| 3018 | render( '' ); |
| 3019 | } |
| 3020 | if ( initial && this.showSearch ) this.showSearch( true ); |
| 3021 | return; |
| 3022 | } |
| 3023 | |
| 3024 | if ( |
| 3025 | opts.maximumInputLength && |
| 3026 | search.val().length > opts.maximumInputLength |
| 3027 | ) { |
| 3028 | if ( |
| 3029 | checkFormatter( |
| 3030 | opts.formatInputTooLong, |
| 3031 | 'formatInputTooLong' |
| 3032 | ) |
| 3033 | ) { |
| 3034 | render( |
| 3035 | "<li class='select2-no-results'>" + |
| 3036 | evaluate( |
| 3037 | opts.formatInputTooLong, |
| 3038 | opts.element, |
| 3039 | search.val(), |
| 3040 | opts.maximumInputLength |
| 3041 | ) + |
| 3042 | '</li>' |
| 3043 | ); |
| 3044 | } else { |
| 3045 | render( '' ); |
| 3046 | } |
| 3047 | return; |
| 3048 | } |
| 3049 | |
| 3050 | if ( |
| 3051 | opts.formatSearching && |
| 3052 | this.findHighlightableChoices().length === 0 |
| 3053 | ) { |
| 3054 | render( |
| 3055 | "<li class='select2-searching'>" + |
| 3056 | evaluate( opts.formatSearching, opts.element ) + |
| 3057 | '</li>' |
| 3058 | ); |
| 3059 | } |
| 3060 | |
| 3061 | search.addClass( 'select2-active' ); |
| 3062 | |
| 3063 | this.removeHighlight(); |
| 3064 | |
| 3065 | // give the tokenizer a chance to pre-process the input |
| 3066 | input = this.tokenize(); |
| 3067 | if ( input != undefined && input != null ) { |
| 3068 | search.val( input ); |
| 3069 | } |
| 3070 | |
| 3071 | this.resultsPage = 1; |
| 3072 | |
| 3073 | opts.query( { |
| 3074 | element: opts.element, |
| 3075 | term: search.val(), |
| 3076 | page: this.resultsPage, |
| 3077 | context: null, |
| 3078 | matcher: opts.matcher, |
| 3079 | callback: this.bind( function ( data ) { |
| 3080 | var def; // default choice |
| 3081 | |
| 3082 | // ignore old responses |
| 3083 | if ( queryNumber != this.queryCount ) { |
| 3084 | return; |
| 3085 | } |
| 3086 | |
| 3087 | // ignore a response if the select2 has been closed before it was received |
| 3088 | if ( ! this.opened() ) { |
| 3089 | this.search.removeClass( 'select2-active' ); |
| 3090 | return; |
| 3091 | } |
| 3092 | |
| 3093 | // handle ajax error |
| 3094 | if ( |
| 3095 | data.hasError !== undefined && |
| 3096 | checkFormatter( |
| 3097 | opts.formatAjaxError, |
| 3098 | 'formatAjaxError' |
| 3099 | ) |
| 3100 | ) { |
| 3101 | render( |
| 3102 | "<li class='select2-ajax-error'>" + |
| 3103 | evaluate( |
| 3104 | opts.formatAjaxError, |
| 3105 | opts.element, |
| 3106 | data.jqXHR, |
| 3107 | data.textStatus, |
| 3108 | data.errorThrown |
| 3109 | ) + |
| 3110 | '</li>' |
| 3111 | ); |
| 3112 | return; |
| 3113 | } |
| 3114 | |
| 3115 | // save context, if any |
| 3116 | this.context = |
| 3117 | data.context === undefined ? null : data.context; |
| 3118 | // create a default choice and prepend it to the list |
| 3119 | if ( this.opts.createSearchChoice && search.val() !== '' ) { |
| 3120 | def = this.opts.createSearchChoice.call( |
| 3121 | self, |
| 3122 | search.val(), |
| 3123 | data.results |
| 3124 | ); |
| 3125 | if ( |
| 3126 | def !== undefined && |
| 3127 | def !== null && |
| 3128 | self.id( def ) !== undefined && |
| 3129 | self.id( def ) !== null |
| 3130 | ) { |
| 3131 | if ( |
| 3132 | $( data.results ).filter( function () { |
| 3133 | return equal( |
| 3134 | self.id( this ), |
| 3135 | self.id( def ) |
| 3136 | ); |
| 3137 | } ).length === 0 |
| 3138 | ) { |
| 3139 | this.opts.createSearchChoicePosition( |
| 3140 | data.results, |
| 3141 | def |
| 3142 | ); |
| 3143 | } |
| 3144 | } |
| 3145 | } |
| 3146 | |
| 3147 | if ( |
| 3148 | data.results.length === 0 && |
| 3149 | checkFormatter( |
| 3150 | opts.formatNoMatches, |
| 3151 | 'formatNoMatches' |
| 3152 | ) |
| 3153 | ) { |
| 3154 | render( |
| 3155 | "<li class='select2-no-results'>" + |
| 3156 | evaluate( |
| 3157 | opts.formatNoMatches, |
| 3158 | opts.element, |
| 3159 | search.val() |
| 3160 | ) + |
| 3161 | '</li>' |
| 3162 | ); |
| 3163 | return; |
| 3164 | } |
| 3165 | |
| 3166 | results.empty(); |
| 3167 | self.opts.populateResults.call( |
| 3168 | this, |
| 3169 | results, |
| 3170 | data.results, |
| 3171 | { |
| 3172 | term: search.val(), |
| 3173 | page: this.resultsPage, |
| 3174 | context: null, |
| 3175 | } |
| 3176 | ); |
| 3177 | |
| 3178 | if ( |
| 3179 | data.more === true && |
| 3180 | checkFormatter( opts.formatLoadMore, 'formatLoadMore' ) |
| 3181 | ) { |
| 3182 | results.append( |
| 3183 | "<li class='select2-more-results'>" + |
| 3184 | opts.escapeMarkup( |
| 3185 | evaluate( |
| 3186 | opts.formatLoadMore, |
| 3187 | opts.element, |
| 3188 | this.resultsPage |
| 3189 | ) |
| 3190 | ) + |
| 3191 | '</li>' |
| 3192 | ); |
| 3193 | window.setTimeout( function () { |
| 3194 | self.loadMoreIfNeeded(); |
| 3195 | }, 10 ); |
| 3196 | } |
| 3197 | |
| 3198 | this.postprocessResults( data, initial ); |
| 3199 | |
| 3200 | postRender(); |
| 3201 | |
| 3202 | this.opts.element.trigger( { |
| 3203 | type: 'select2-loaded', |
| 3204 | items: data, |
| 3205 | } ); |
| 3206 | } ), |
| 3207 | } ); |
| 3208 | }, |
| 3209 | |
| 3210 | // abstract |
| 3211 | cancel: function () { |
| 3212 | this.close(); |
| 3213 | }, |
| 3214 | |
| 3215 | // abstract |
| 3216 | blur: function () { |
| 3217 | // if selectOnBlur == true, select the currently highlighted option |
| 3218 | if ( this.opts.selectOnBlur ) |
| 3219 | this.selectHighlighted( { noFocus: true } ); |
| 3220 | |
| 3221 | this.close(); |
| 3222 | this.container.removeClass( 'select2-container-active' ); |
| 3223 | // synonymous to .is(':focus'), which is available in jquery >= 1.6 |
| 3224 | if ( this.search[ 0 ] === document.activeElement ) { |
| 3225 | this.search.blur(); |
| 3226 | } |
| 3227 | this.clearSearch(); |
| 3228 | this.selection |
| 3229 | .find( '.select2-search-choice-focus' ) |
| 3230 | .removeClass( 'select2-search-choice-focus' ); |
| 3231 | }, |
| 3232 | |
| 3233 | // abstract |
| 3234 | focusSearch: function () { |
| 3235 | focus( this.search ); |
| 3236 | }, |
| 3237 | |
| 3238 | // abstract |
| 3239 | selectHighlighted: function ( options ) { |
| 3240 | if ( this._touchMoved ) { |
| 3241 | this.clearTouchMoved(); |
| 3242 | return; |
| 3243 | } |
| 3244 | var index = this.highlight(), |
| 3245 | highlighted = this.results.find( '.select2-highlighted' ), |
| 3246 | data = highlighted |
| 3247 | .closest( '.select2-result' ) |
| 3248 | .data( 'select2-data' ); |
| 3249 | |
| 3250 | if ( data ) { |
| 3251 | this.highlight( index ); |
| 3252 | this.onSelect( data, options ); |
| 3253 | } else if ( options && options.noFocus ) { |
| 3254 | this.close(); |
| 3255 | } |
| 3256 | }, |
| 3257 | |
| 3258 | // abstract |
| 3259 | getPlaceholder: function () { |
| 3260 | var placeholderOption; |
| 3261 | return ( |
| 3262 | this.opts.element.attr( 'placeholder' ) || |
| 3263 | this.opts.element.attr( 'data-placeholder' ) || // jquery 1.4 compat |
| 3264 | this.opts.element.data( 'placeholder' ) || |
| 3265 | this.opts.placeholder || |
| 3266 | ( ( placeholderOption = this.getPlaceholderOption() ) !== |
| 3267 | undefined |
| 3268 | ? placeholderOption.text() |
| 3269 | : undefined ) |
| 3270 | ); |
| 3271 | }, |
| 3272 | |
| 3273 | // abstract |
| 3274 | getPlaceholderOption: function () { |
| 3275 | if ( this.select ) { |
| 3276 | var firstOption = this.select.children( 'option' ).first(); |
| 3277 | if ( this.opts.placeholderOption !== undefined ) { |
| 3278 | //Determine the placeholder option based on the specified placeholderOption setting |
| 3279 | return ( |
| 3280 | ( this.opts.placeholderOption === 'first' && |
| 3281 | firstOption ) || |
| 3282 | ( typeof this.opts.placeholderOption === 'function' && |
| 3283 | this.opts.placeholderOption( this.select ) ) |
| 3284 | ); |
| 3285 | } else if ( |
| 3286 | $.trim( firstOption.text() ) === '' && |
| 3287 | firstOption.val() === '' |
| 3288 | ) { |
| 3289 | //No explicit placeholder option specified, use the first if it's blank |
| 3290 | return firstOption; |
| 3291 | } |
| 3292 | } |
| 3293 | }, |
| 3294 | |
| 3295 | /** |
| 3296 | * Get the desired width for the container element. This is |
| 3297 | * derived first from option `width` passed to select2, then |
| 3298 | * the inline 'style' on the original element, and finally |
| 3299 | * falls back to the jQuery calculated element width. |
| 3300 | */ |
| 3301 | // abstract |
| 3302 | initContainerWidth: function () { |
| 3303 | function resolveContainerWidth() { |
| 3304 | var style, attrs, matches, i, l, attr; |
| 3305 | |
| 3306 | if ( this.opts.width === 'off' ) { |
| 3307 | return null; |
| 3308 | } else if ( this.opts.width === 'element' ) { |
| 3309 | return this.opts.element.outerWidth( false ) === 0 |
| 3310 | ? 'auto' |
| 3311 | : this.opts.element.outerWidth( false ) + 'px'; |
| 3312 | } else if ( |
| 3313 | this.opts.width === 'copy' || |
| 3314 | this.opts.width === 'resolve' |
| 3315 | ) { |
| 3316 | // check if there is inline style on the element that contains width |
| 3317 | style = this.opts.element.attr( 'style' ); |
| 3318 | if ( style !== undefined ) { |
| 3319 | attrs = style.split( ';' ); |
| 3320 | for ( i = 0, l = attrs.length; i < l; i = i + 1 ) { |
| 3321 | attr = attrs[ i ].replace( /\s/g, '' ); |
| 3322 | matches = attr.match( |
| 3323 | /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i |
| 3324 | ); |
| 3325 | if ( matches !== null && matches.length >= 1 ) |
| 3326 | return matches[ 1 ]; |
| 3327 | } |
| 3328 | } |
| 3329 | |
| 3330 | if ( this.opts.width === 'resolve' ) { |
| 3331 | // next check if css('width') can resolve a width that is percent based, this is sometimes possible |
| 3332 | // when attached to input type=hidden or elements hidden via css |
| 3333 | style = this.opts.element.css( 'width' ); |
| 3334 | if ( style.indexOf( '%' ) > 0 ) return style; |
| 3335 | |
| 3336 | // finally, fallback on the calculated width of the element |
| 3337 | return this.opts.element.outerWidth( false ) === 0 |
| 3338 | ? 'auto' |
| 3339 | : this.opts.element.outerWidth( false ) + 'px'; |
| 3340 | } |
| 3341 | |
| 3342 | return null; |
| 3343 | } else if ( $.isFunction( this.opts.width ) ) { |
| 3344 | return this.opts.width(); |
| 3345 | } else { |
| 3346 | return this.opts.width; |
| 3347 | } |
| 3348 | } |
| 3349 | |
| 3350 | var width = resolveContainerWidth.call( this ); |
| 3351 | if ( width !== null ) { |
| 3352 | this.container.css( 'width', width ); |
| 3353 | } |
| 3354 | }, |
| 3355 | } ); |
| 3356 | |
| 3357 | SingleSelect2 = clazz( AbstractSelect2, { |
| 3358 | // single |
| 3359 | |
| 3360 | createContainer: function () { |
| 3361 | var container = $( document.createElement( 'div' ) ) |
| 3362 | .attr( { |
| 3363 | class: 'select2-container', |
| 3364 | } ) |
| 3365 | .html( |
| 3366 | [ |
| 3367 | "<a href='javascript:void(0)' class='select2-choice' tabindex='-1'>", |
| 3368 | " <span class='select2-chosen'> </span><abbr class='select2-search-choice-close'></abbr>", |
| 3369 | " <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>", |
| 3370 | '</a>', |
| 3371 | "<label for='' class='select2-offscreen'></label>", |
| 3372 | "<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />", |
| 3373 | "<div class='select2-drop select2-display-none'>", |
| 3374 | " <div class='select2-search'>", |
| 3375 | " <label for='' class='select2-offscreen'></label>", |
| 3376 | " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'", |
| 3377 | " aria-autocomplete='list' />", |
| 3378 | ' </div>', |
| 3379 | " <ul class='select2-results' role='listbox'>", |
| 3380 | ' </ul>', |
| 3381 | '</div>', |
| 3382 | ].join( '' ) |
| 3383 | ); |
| 3384 | return container; |
| 3385 | }, |
| 3386 | |
| 3387 | // single |
| 3388 | enableInterface: function () { |
| 3389 | if ( this.parent.enableInterface.apply( this, arguments ) ) { |
| 3390 | this.focusser.prop( 'disabled', ! this.isInterfaceEnabled() ); |
| 3391 | } |
| 3392 | }, |
| 3393 | |
| 3394 | // single |
| 3395 | opening: function () { |
| 3396 | var el, range, len; |
| 3397 | |
| 3398 | if ( this.opts.minimumResultsForSearch >= 0 ) { |
| 3399 | this.showSearch( true ); |
| 3400 | } |
| 3401 | |
| 3402 | this.parent.opening.apply( this, arguments ); |
| 3403 | |
| 3404 | if ( this.showSearchInput !== false ) { |
| 3405 | // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range |
| 3406 | // all other browsers handle this just fine |
| 3407 | |
| 3408 | this.search.val( this.focusser.val() ); |
| 3409 | } |
| 3410 | if ( this.opts.shouldFocusInput( this ) ) { |
| 3411 | this.search.focus(); |
| 3412 | // move the cursor to the end after focussing, otherwise it will be at the beginning and |
| 3413 | // new text will appear *before* focusser.val() |
| 3414 | el = this.search.get( 0 ); |
| 3415 | if ( el.createTextRange ) { |
| 3416 | range = el.createTextRange(); |
| 3417 | range.collapse( false ); |
| 3418 | range.select(); |
| 3419 | } else if ( el.setSelectionRange ) { |
| 3420 | len = this.search.val().length; |
| 3421 | el.setSelectionRange( len, len ); |
| 3422 | } |
| 3423 | } |
| 3424 | |
| 3425 | // initializes search's value with nextSearchTerm (if defined by user) |
| 3426 | // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter |
| 3427 | if ( this.search.val() === '' ) { |
| 3428 | if ( this.nextSearchTerm != undefined ) { |
| 3429 | this.search.val( this.nextSearchTerm ); |
| 3430 | this.search.select(); |
| 3431 | } |
| 3432 | } |
| 3433 | |
| 3434 | this.focusser.prop( 'disabled', true ).val( '' ); |
| 3435 | this.updateResults( true ); |
| 3436 | this.opts.element.trigger( $.Event( 'select2-open' ) ); |
| 3437 | }, |
| 3438 | |
| 3439 | // single |
| 3440 | close: function () { |
| 3441 | if ( ! this.opened() ) return; |
| 3442 | this.parent.close.apply( this, arguments ); |
| 3443 | |
| 3444 | this.focusser.prop( 'disabled', false ); |
| 3445 | |
| 3446 | if ( this.opts.shouldFocusInput( this ) ) { |
| 3447 | this.focusser.focus(); |
| 3448 | } |
| 3449 | }, |
| 3450 | |
| 3451 | // single |
| 3452 | focus: function () { |
| 3453 | if ( this.opened() ) { |
| 3454 | this.close(); |
| 3455 | } else { |
| 3456 | this.focusser.prop( 'disabled', false ); |
| 3457 | if ( this.opts.shouldFocusInput( this ) ) { |
| 3458 | this.focusser.focus(); |
| 3459 | } |
| 3460 | } |
| 3461 | }, |
| 3462 | |
| 3463 | // single |
| 3464 | isFocused: function () { |
| 3465 | return this.container.hasClass( 'select2-container-active' ); |
| 3466 | }, |
| 3467 | |
| 3468 | // single |
| 3469 | cancel: function () { |
| 3470 | this.parent.cancel.apply( this, arguments ); |
| 3471 | this.focusser.prop( 'disabled', false ); |
| 3472 | |
| 3473 | if ( this.opts.shouldFocusInput( this ) ) { |
| 3474 | this.focusser.focus(); |
| 3475 | } |
| 3476 | }, |
| 3477 | |
| 3478 | // single |
| 3479 | destroy: function () { |
| 3480 | $( "label[for='" + this.focusser.attr( 'id' ) + "']" ).attr( |
| 3481 | 'for', |
| 3482 | this.opts.element.attr( 'id' ) |
| 3483 | ); |
| 3484 | this.parent.destroy.apply( this, arguments ); |
| 3485 | |
| 3486 | cleanupJQueryElements.call( this, 'selection', 'focusser' ); |
| 3487 | }, |
| 3488 | |
| 3489 | // single |
| 3490 | initContainer: function () { |
| 3491 | var selection, |
| 3492 | container = this.container, |
| 3493 | dropdown = this.dropdown, |
| 3494 | idSuffix = nextUid(), |
| 3495 | elementLabel; |
| 3496 | |
| 3497 | if ( this.opts.minimumResultsForSearch < 0 ) { |
| 3498 | this.showSearch( false ); |
| 3499 | } else { |
| 3500 | this.showSearch( true ); |
| 3501 | } |
| 3502 | |
| 3503 | this.selection = selection = container.find( '.select2-choice' ); |
| 3504 | |
| 3505 | this.focusser = container.find( '.select2-focusser' ); |
| 3506 | |
| 3507 | // add aria associations |
| 3508 | selection |
| 3509 | .find( '.select2-chosen' ) |
| 3510 | .attr( 'id', 'select2-chosen-' + idSuffix ); |
| 3511 | this.focusser.attr( |
| 3512 | 'aria-labelledby', |
| 3513 | 'select2-chosen-' + idSuffix |
| 3514 | ); |
| 3515 | this.results.attr( 'id', 'select2-results-' + idSuffix ); |
| 3516 | this.search.attr( 'aria-owns', 'select2-results-' + idSuffix ); |
| 3517 | |
| 3518 | // rewrite labels from original element to focusser |
| 3519 | this.focusser.attr( 'id', 's2id_autogen' + idSuffix ); |
| 3520 | |
| 3521 | elementLabel = $( |
| 3522 | "label[for='" + this.opts.element.attr( 'id' ) + "']" |
| 3523 | ); |
| 3524 | this.opts.element.focus( |
| 3525 | this.bind( function () { |
| 3526 | this.focus(); |
| 3527 | } ) |
| 3528 | ); |
| 3529 | |
| 3530 | this.focusser |
| 3531 | .prev() |
| 3532 | .text( elementLabel.text() ) |
| 3533 | .attr( 'for', this.focusser.attr( 'id' ) ); |
| 3534 | |
| 3535 | // Ensure the original element retains an accessible name |
| 3536 | var originalTitle = this.opts.element.attr( 'title' ); |
| 3537 | this.opts.element.attr( |
| 3538 | 'title', |
| 3539 | originalTitle || elementLabel.text() |
| 3540 | ); |
| 3541 | |
| 3542 | this.focusser.attr( 'tabindex', this.elementTabIndex ); |
| 3543 | |
| 3544 | // write label for search field using the label from the focusser element |
| 3545 | this.search.attr( 'id', this.focusser.attr( 'id' ) + '_search' ); |
| 3546 | |
| 3547 | this.search |
| 3548 | .prev() |
| 3549 | .text( |
| 3550 | $( |
| 3551 | "label[for='" + this.focusser.attr( 'id' ) + "']" |
| 3552 | ).text() |
| 3553 | ) |
| 3554 | .attr( 'for', this.search.attr( 'id' ) ); |
| 3555 | |
| 3556 | this.search.on( |
| 3557 | 'keydown', |
| 3558 | this.bind( function ( e ) { |
| 3559 | if ( ! this.isInterfaceEnabled() ) return; |
| 3560 | |
| 3561 | // filter 229 keyCodes (input method editor is processing key input) |
| 3562 | if ( 229 == e.keyCode ) return; |
| 3563 | |
| 3564 | if ( |
| 3565 | e.which === KEY.PAGE_UP || |
| 3566 | e.which === KEY.PAGE_DOWN |
| 3567 | ) { |
| 3568 | // prevent the page from scrolling |
| 3569 | killEvent( e ); |
| 3570 | return; |
| 3571 | } |
| 3572 | |
| 3573 | switch ( e.which ) { |
| 3574 | case KEY.UP: |
| 3575 | case KEY.DOWN: |
| 3576 | this.moveHighlight( e.which === KEY.UP ? -1 : 1 ); |
| 3577 | killEvent( e ); |
| 3578 | return; |
| 3579 | case KEY.ENTER: |
| 3580 | this.selectHighlighted(); |
| 3581 | killEvent( e ); |
| 3582 | return; |
| 3583 | case KEY.TAB: |
| 3584 | this.selectHighlighted( { noFocus: true } ); |
| 3585 | return; |
| 3586 | case KEY.ESC: |
| 3587 | this.cancel( e ); |
| 3588 | killEvent( e ); |
| 3589 | return; |
| 3590 | } |
| 3591 | } ) |
| 3592 | ); |
| 3593 | |
| 3594 | this.search.on( |
| 3595 | 'blur', |
| 3596 | this.bind( function ( e ) { |
| 3597 | // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown. |
| 3598 | // without this the search field loses focus which is annoying |
| 3599 | if ( document.activeElement === this.body.get( 0 ) ) { |
| 3600 | window.setTimeout( |
| 3601 | this.bind( function () { |
| 3602 | if ( this.opened() ) { |
| 3603 | this.search.focus(); |
| 3604 | } |
| 3605 | } ), |
| 3606 | 0 |
| 3607 | ); |
| 3608 | } |
| 3609 | } ) |
| 3610 | ); |
| 3611 | |
| 3612 | this.focusser.on( |
| 3613 | 'keydown', |
| 3614 | this.bind( function ( e ) { |
| 3615 | if ( ! this.isInterfaceEnabled() ) return; |
| 3616 | |
| 3617 | if ( |
| 3618 | e.which === KEY.TAB || |
| 3619 | KEY.isControl( e ) || |
| 3620 | KEY.isFunctionKey( e ) || |
| 3621 | e.which === KEY.ESC |
| 3622 | ) { |
| 3623 | return; |
| 3624 | } |
| 3625 | |
| 3626 | if ( |
| 3627 | this.opts.openOnEnter === false && |
| 3628 | e.which === KEY.ENTER |
| 3629 | ) { |
| 3630 | killEvent( e ); |
| 3631 | return; |
| 3632 | } |
| 3633 | |
| 3634 | if ( |
| 3635 | e.which == KEY.DOWN || |
| 3636 | e.which == KEY.UP || |
| 3637 | ( e.which == KEY.ENTER && this.opts.openOnEnter ) |
| 3638 | ) { |
| 3639 | if ( e.altKey || e.ctrlKey || e.shiftKey || e.metaKey ) |
| 3640 | return; |
| 3641 | |
| 3642 | this.open(); |
| 3643 | killEvent( e ); |
| 3644 | return; |
| 3645 | } |
| 3646 | |
| 3647 | if ( e.which == KEY.DELETE || e.which == KEY.BACKSPACE ) { |
| 3648 | if ( this.opts.allowClear ) { |
| 3649 | this.clear(); |
| 3650 | } |
| 3651 | killEvent( e ); |
| 3652 | return; |
| 3653 | } |
| 3654 | } ) |
| 3655 | ); |
| 3656 | |
| 3657 | installKeyUpChangeEvent( this.focusser ); |
| 3658 | this.focusser.on( |
| 3659 | 'keyup-change input', |
| 3660 | this.bind( function ( e ) { |
| 3661 | if ( this.opts.minimumResultsForSearch >= 0 ) { |
| 3662 | e.stopPropagation(); |
| 3663 | if ( this.opened() ) return; |
| 3664 | this.open(); |
| 3665 | } |
| 3666 | } ) |
| 3667 | ); |
| 3668 | |
| 3669 | selection.on( |
| 3670 | 'mousedown touchstart', |
| 3671 | 'abbr', |
| 3672 | this.bind( function ( e ) { |
| 3673 | if ( ! this.isInterfaceEnabled() ) { |
| 3674 | return; |
| 3675 | } |
| 3676 | |
| 3677 | this.clear(); |
| 3678 | killEventImmediately( e ); |
| 3679 | this.close(); |
| 3680 | |
| 3681 | if ( this.selection ) { |
| 3682 | this.selection.focus(); |
| 3683 | } |
| 3684 | } ) |
| 3685 | ); |
| 3686 | |
| 3687 | selection.on( |
| 3688 | 'mousedown touchstart', |
| 3689 | this.bind( function ( e ) { |
| 3690 | // Prevent IE from generating a click event on the body |
| 3691 | reinsertElement( selection ); |
| 3692 | |
| 3693 | if ( |
| 3694 | ! this.container.hasClass( 'select2-container-active' ) |
| 3695 | ) { |
| 3696 | this.opts.element.trigger( $.Event( 'select2-focus' ) ); |
| 3697 | } |
| 3698 | |
| 3699 | if ( this.opened() ) { |
| 3700 | this.close(); |
| 3701 | } else if ( this.isInterfaceEnabled() ) { |
| 3702 | this.open(); |
| 3703 | } |
| 3704 | |
| 3705 | killEvent( e ); |
| 3706 | } ) |
| 3707 | ); |
| 3708 | |
| 3709 | dropdown.on( |
| 3710 | 'mousedown touchstart', |
| 3711 | this.bind( function () { |
| 3712 | if ( this.opts.shouldFocusInput( this ) ) { |
| 3713 | this.search.focus(); |
| 3714 | } |
| 3715 | } ) |
| 3716 | ); |
| 3717 | |
| 3718 | selection.on( |
| 3719 | 'focus', |
| 3720 | this.bind( function ( e ) { |
| 3721 | killEvent( e ); |
| 3722 | } ) |
| 3723 | ); |
| 3724 | |
| 3725 | this.focusser |
| 3726 | .on( |
| 3727 | 'focus', |
| 3728 | this.bind( function () { |
| 3729 | if ( |
| 3730 | ! this.container.hasClass( |
| 3731 | 'select2-container-active' |
| 3732 | ) |
| 3733 | ) { |
| 3734 | this.opts.element.trigger( |
| 3735 | $.Event( 'select2-focus' ) |
| 3736 | ); |
| 3737 | } |
| 3738 | this.container.addClass( 'select2-container-active' ); |
| 3739 | } ) |
| 3740 | ) |
| 3741 | .on( |
| 3742 | 'blur', |
| 3743 | this.bind( function () { |
| 3744 | if ( ! this.opened() ) { |
| 3745 | this.container.removeClass( |
| 3746 | 'select2-container-active' |
| 3747 | ); |
| 3748 | this.opts.element.trigger( |
| 3749 | $.Event( 'select2-blur' ) |
| 3750 | ); |
| 3751 | } |
| 3752 | } ) |
| 3753 | ); |
| 3754 | this.search.on( |
| 3755 | 'focus', |
| 3756 | this.bind( function () { |
| 3757 | if ( |
| 3758 | ! this.container.hasClass( 'select2-container-active' ) |
| 3759 | ) { |
| 3760 | this.opts.element.trigger( $.Event( 'select2-focus' ) ); |
| 3761 | } |
| 3762 | this.container.addClass( 'select2-container-active' ); |
| 3763 | } ) |
| 3764 | ); |
| 3765 | |
| 3766 | this.initContainerWidth(); |
| 3767 | this.opts.element.hide(); |
| 3768 | this.setPlaceholder(); |
| 3769 | }, |
| 3770 | |
| 3771 | // single |
| 3772 | clear: function ( triggerChange ) { |
| 3773 | var data = this.selection.data( 'select2-data' ); |
| 3774 | if ( data ) { |
| 3775 | // guard against queued quick consecutive clicks |
| 3776 | var evt = $.Event( 'select2-clearing' ); |
| 3777 | this.opts.element.trigger( evt ); |
| 3778 | if ( evt.isDefaultPrevented() ) { |
| 3779 | return; |
| 3780 | } |
| 3781 | var placeholderOption = this.getPlaceholderOption(); |
| 3782 | this.opts.element.val( |
| 3783 | placeholderOption ? placeholderOption.val() : '' |
| 3784 | ); |
| 3785 | this.selection.find( '.select2-chosen' ).empty(); |
| 3786 | this.selection.removeData( 'select2-data' ); |
| 3787 | this.setPlaceholder(); |
| 3788 | |
| 3789 | if ( triggerChange !== false ) { |
| 3790 | this.opts.element.trigger( { |
| 3791 | type: 'select2-removed', |
| 3792 | val: this.id( data ), |
| 3793 | choice: data, |
| 3794 | } ); |
| 3795 | this.triggerChange( { removed: data } ); |
| 3796 | } |
| 3797 | } |
| 3798 | }, |
| 3799 | |
| 3800 | /** |
| 3801 | * Sets selection based on source element's value |
| 3802 | */ |
| 3803 | // single |
| 3804 | initSelection: function () { |
| 3805 | var selected; |
| 3806 | if ( this.isPlaceholderOptionSelected() ) { |
| 3807 | this.updateSelection( null ); |
| 3808 | this.close(); |
| 3809 | this.setPlaceholder(); |
| 3810 | } else { |
| 3811 | var self = this; |
| 3812 | this.opts.initSelection.call( |
| 3813 | null, |
| 3814 | this.opts.element, |
| 3815 | function ( selected ) { |
| 3816 | if ( selected !== undefined && selected !== null ) { |
| 3817 | self.updateSelection( selected ); |
| 3818 | self.close(); |
| 3819 | self.setPlaceholder(); |
| 3820 | self.nextSearchTerm = self.opts.nextSearchTerm( |
| 3821 | selected, |
| 3822 | self.search.val() |
| 3823 | ); |
| 3824 | } |
| 3825 | } |
| 3826 | ); |
| 3827 | } |
| 3828 | }, |
| 3829 | |
| 3830 | isPlaceholderOptionSelected: function () { |
| 3831 | var placeholderOption; |
| 3832 | if ( this.getPlaceholder() === undefined ) return false; // no placeholder specified so no option should be considered |
| 3833 | return ( |
| 3834 | ( ( placeholderOption = this.getPlaceholderOption() ) !== |
| 3835 | undefined && |
| 3836 | placeholderOption.prop( 'selected' ) ) || |
| 3837 | this.opts.element.val() === '' || |
| 3838 | this.opts.element.val() === undefined || |
| 3839 | this.opts.element.val() === null |
| 3840 | ); |
| 3841 | }, |
| 3842 | |
| 3843 | // single |
| 3844 | prepareOpts: function () { |
| 3845 | var opts = this.parent.prepareOpts.apply( this, arguments ), |
| 3846 | self = this; |
| 3847 | |
| 3848 | if ( opts.element.get( 0 ).tagName.toLowerCase() === 'select' ) { |
| 3849 | // install the selection initializer |
| 3850 | opts.initSelection = function ( element, callback ) { |
| 3851 | var selected = element |
| 3852 | .find( 'option' ) |
| 3853 | .filter( function () { |
| 3854 | return this.selected && ! this.disabled; |
| 3855 | } ); |
| 3856 | // a single select box always has a value, no need to null check 'selected' |
| 3857 | callback( self.optionToData( selected ) ); |
| 3858 | }; |
| 3859 | } else if ( 'data' in opts ) { |
| 3860 | // install default initSelection when applied to hidden input and data is local |
| 3861 | opts.initSelection = |
| 3862 | opts.initSelection || |
| 3863 | function ( element, callback ) { |
| 3864 | var id = element.val(); |
| 3865 | //search in data by id, storing the actual matching item |
| 3866 | var match = null; |
| 3867 | opts.query( { |
| 3868 | matcher: function ( term, text, el ) { |
| 3869 | var is_match = equal( id, opts.id( el ) ); |
| 3870 | if ( is_match ) { |
| 3871 | match = el; |
| 3872 | } |
| 3873 | return is_match; |
| 3874 | }, |
| 3875 | callback: ! $.isFunction( callback ) |
| 3876 | ? $.noop |
| 3877 | : function () { |
| 3878 | callback( match ); |
| 3879 | }, |
| 3880 | } ); |
| 3881 | }; |
| 3882 | } |
| 3883 | |
| 3884 | return opts; |
| 3885 | }, |
| 3886 | |
| 3887 | // single |
| 3888 | getPlaceholder: function () { |
| 3889 | // if a placeholder is specified on a single select without a valid placeholder option ignore it |
| 3890 | if ( this.select ) { |
| 3891 | if ( this.getPlaceholderOption() === undefined ) { |
| 3892 | return undefined; |
| 3893 | } |
| 3894 | } |
| 3895 | |
| 3896 | return this.parent.getPlaceholder.apply( this, arguments ); |
| 3897 | }, |
| 3898 | |
| 3899 | // single |
| 3900 | setPlaceholder: function () { |
| 3901 | var placeholder = this.getPlaceholder(); |
| 3902 | |
| 3903 | if ( |
| 3904 | this.isPlaceholderOptionSelected() && |
| 3905 | placeholder !== undefined |
| 3906 | ) { |
| 3907 | // check for a placeholder option if attached to a select |
| 3908 | if ( this.select && this.getPlaceholderOption() === undefined ) |
| 3909 | return; |
| 3910 | |
| 3911 | this.selection |
| 3912 | .find( '.select2-chosen' ) |
| 3913 | .html( this.opts.escapeMarkup( placeholder ) ); |
| 3914 | |
| 3915 | this.selection.addClass( 'select2-default' ); |
| 3916 | |
| 3917 | this.container.removeClass( 'select2-allowclear' ); |
| 3918 | } |
| 3919 | }, |
| 3920 | |
| 3921 | // single |
| 3922 | postprocessResults: function ( data, initial, noHighlightUpdate ) { |
| 3923 | var selected = 0, |
| 3924 | self = this, |
| 3925 | showSearchInput = true; |
| 3926 | |
| 3927 | // find the selected element in the result list |
| 3928 | |
| 3929 | this.findHighlightableChoices().each2( function ( i, elm ) { |
| 3930 | if ( |
| 3931 | equal( |
| 3932 | self.id( elm.data( 'select2-data' ) ), |
| 3933 | self.opts.element.val() |
| 3934 | ) |
| 3935 | ) { |
| 3936 | selected = i; |
| 3937 | return false; |
| 3938 | } |
| 3939 | } ); |
| 3940 | |
| 3941 | // and highlight it |
| 3942 | if ( noHighlightUpdate !== false ) { |
| 3943 | if ( initial === true && selected >= 0 ) { |
| 3944 | this.highlight( selected ); |
| 3945 | } else { |
| 3946 | this.highlight( 0 ); |
| 3947 | } |
| 3948 | } |
| 3949 | |
| 3950 | // hide the search box if this is the first we got the results and there are enough of them for search |
| 3951 | |
| 3952 | if ( initial === true ) { |
| 3953 | var min = this.opts.minimumResultsForSearch; |
| 3954 | if ( min >= 0 ) { |
| 3955 | this.showSearch( countResults( data.results ) >= min ); |
| 3956 | } |
| 3957 | } |
| 3958 | }, |
| 3959 | |
| 3960 | // single |
| 3961 | showSearch: function ( showSearchInput ) { |
| 3962 | if ( this.showSearchInput === showSearchInput ) return; |
| 3963 | |
| 3964 | this.showSearchInput = showSearchInput; |
| 3965 | |
| 3966 | this.dropdown |
| 3967 | .find( '.select2-search' ) |
| 3968 | .toggleClass( 'select2-search-hidden', ! showSearchInput ); |
| 3969 | this.dropdown |
| 3970 | .find( '.select2-search' ) |
| 3971 | .toggleClass( 'select2-offscreen', ! showSearchInput ); |
| 3972 | //add "select2-with-searchbox" to the container if search box is shown |
| 3973 | $( this.dropdown, this.container ).toggleClass( |
| 3974 | 'select2-with-searchbox', |
| 3975 | showSearchInput |
| 3976 | ); |
| 3977 | }, |
| 3978 | |
| 3979 | // single |
| 3980 | onSelect: function ( data, options ) { |
| 3981 | if ( ! this.triggerSelect( data ) ) { |
| 3982 | return; |
| 3983 | } |
| 3984 | |
| 3985 | var old = this.opts.element.val(), |
| 3986 | oldData = this.data(); |
| 3987 | |
| 3988 | this.opts.element.val( this.id( data ) ); |
| 3989 | this.updateSelection( data ); |
| 3990 | |
| 3991 | this.opts.element.trigger( { |
| 3992 | type: 'select2-selected', |
| 3993 | val: this.id( data ), |
| 3994 | choice: data, |
| 3995 | } ); |
| 3996 | |
| 3997 | this.nextSearchTerm = this.opts.nextSearchTerm( |
| 3998 | data, |
| 3999 | this.search.val() |
| 4000 | ); |
| 4001 | this.close(); |
| 4002 | |
| 4003 | if ( |
| 4004 | ( ! options || ! options.noFocus ) && |
| 4005 | this.opts.shouldFocusInput( this ) |
| 4006 | ) { |
| 4007 | this.focusser.focus(); |
| 4008 | } |
| 4009 | |
| 4010 | if ( ! equal( old, this.id( data ) ) ) { |
| 4011 | this.triggerChange( { added: data, removed: oldData } ); |
| 4012 | } |
| 4013 | }, |
| 4014 | |
| 4015 | // single |
| 4016 | updateSelection: function ( data ) { |
| 4017 | var container = this.selection.find( '.select2-chosen' ), |
| 4018 | formatted, |
| 4019 | cssClass; |
| 4020 | |
| 4021 | this.selection.data( 'select2-data', data ); |
| 4022 | |
| 4023 | container.empty(); |
| 4024 | if ( data !== null ) { |
| 4025 | formatted = this.opts.formatSelection( |
| 4026 | data, |
| 4027 | container, |
| 4028 | this.opts.escapeMarkup |
| 4029 | ); |
| 4030 | } |
| 4031 | if ( formatted !== undefined ) { |
| 4032 | container.append( formatted ); |
| 4033 | } |
| 4034 | cssClass = this.opts.formatSelectionCssClass( data, container ); |
| 4035 | if ( cssClass !== undefined ) { |
| 4036 | container.addClass( cssClass ); |
| 4037 | } |
| 4038 | |
| 4039 | this.selection.removeClass( 'select2-default' ); |
| 4040 | |
| 4041 | if ( this.opts.allowClear && this.getPlaceholder() !== undefined ) { |
| 4042 | this.container.addClass( 'select2-allowclear' ); |
| 4043 | } |
| 4044 | }, |
| 4045 | |
| 4046 | // single |
| 4047 | val: function () { |
| 4048 | var val, |
| 4049 | triggerChange = false, |
| 4050 | data = null, |
| 4051 | self = this, |
| 4052 | oldData = this.data(); |
| 4053 | |
| 4054 | if ( arguments.length === 0 ) { |
| 4055 | return this.opts.element.val(); |
| 4056 | } |
| 4057 | |
| 4058 | val = arguments[ 0 ]; |
| 4059 | |
| 4060 | if ( arguments.length > 1 ) { |
| 4061 | triggerChange = arguments[ 1 ]; |
| 4062 | } |
| 4063 | |
| 4064 | if ( this.select ) { |
| 4065 | this.select |
| 4066 | .val( val ) |
| 4067 | .find( 'option' ) |
| 4068 | .filter( function () { |
| 4069 | return this.selected; |
| 4070 | } ) |
| 4071 | .each2( function ( i, elm ) { |
| 4072 | data = self.optionToData( elm ); |
| 4073 | return false; |
| 4074 | } ); |
| 4075 | this.updateSelection( data ); |
| 4076 | this.setPlaceholder(); |
| 4077 | if ( triggerChange ) { |
| 4078 | this.triggerChange( { added: data, removed: oldData } ); |
| 4079 | } |
| 4080 | } else { |
| 4081 | // val is an id. !val is true for [undefined,null,'',0] - 0 is legal |
| 4082 | if ( ! val && val !== 0 ) { |
| 4083 | this.clear( triggerChange ); |
| 4084 | return; |
| 4085 | } |
| 4086 | if ( this.opts.initSelection === undefined ) { |
| 4087 | throw new Error( |
| 4088 | 'cannot call val() if initSelection() is not defined' |
| 4089 | ); |
| 4090 | } |
| 4091 | this.opts.element.val( val ); |
| 4092 | this.opts.initSelection( this.opts.element, function ( data ) { |
| 4093 | self.opts.element.val( ! data ? '' : self.id( data ) ); |
| 4094 | self.updateSelection( data ); |
| 4095 | self.setPlaceholder(); |
| 4096 | if ( triggerChange ) { |
| 4097 | self.triggerChange( { added: data, removed: oldData } ); |
| 4098 | } |
| 4099 | } ); |
| 4100 | } |
| 4101 | }, |
| 4102 | |
| 4103 | // single |
| 4104 | clearSearch: function () { |
| 4105 | this.search.val( '' ); |
| 4106 | this.focusser.val( '' ); |
| 4107 | }, |
| 4108 | |
| 4109 | // single |
| 4110 | data: function ( value ) { |
| 4111 | var data, |
| 4112 | triggerChange = false; |
| 4113 | |
| 4114 | if ( arguments.length === 0 ) { |
| 4115 | data = this.selection.data( 'select2-data' ); |
| 4116 | if ( data == undefined ) data = null; |
| 4117 | return data; |
| 4118 | } else { |
| 4119 | if ( arguments.length > 1 ) { |
| 4120 | triggerChange = arguments[ 1 ]; |
| 4121 | } |
| 4122 | if ( ! value ) { |
| 4123 | this.clear( triggerChange ); |
| 4124 | } else { |
| 4125 | data = this.data(); |
| 4126 | this.opts.element.val( ! value ? '' : this.id( value ) ); |
| 4127 | this.updateSelection( value ); |
| 4128 | if ( triggerChange ) { |
| 4129 | this.triggerChange( { added: value, removed: data } ); |
| 4130 | } |
| 4131 | } |
| 4132 | } |
| 4133 | }, |
| 4134 | } ); |
| 4135 | |
| 4136 | MultiSelect2 = clazz( AbstractSelect2, { |
| 4137 | // multi |
| 4138 | createContainer: function () { |
| 4139 | var container = $( document.createElement( 'div' ) ) |
| 4140 | .attr( { |
| 4141 | class: 'select2-container select2-container-multi', |
| 4142 | } ) |
| 4143 | .html( |
| 4144 | [ |
| 4145 | "<ul class='select2-choices'>", |
| 4146 | " <li class='select2-search-field'>", |
| 4147 | " <label for='' class='select2-offscreen'></label>", |
| 4148 | " <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>", |
| 4149 | ' </li>', |
| 4150 | '</ul>', |
| 4151 | "<div class='select2-drop select2-drop-multi select2-display-none'>", |
| 4152 | " <ul class='select2-results'>", |
| 4153 | ' </ul>', |
| 4154 | '</div>', |
| 4155 | ].join( '' ) |
| 4156 | ); |
| 4157 | return container; |
| 4158 | }, |
| 4159 | |
| 4160 | // multi |
| 4161 | prepareOpts: function () { |
| 4162 | var opts = this.parent.prepareOpts.apply( this, arguments ), |
| 4163 | self = this; |
| 4164 | |
| 4165 | // TODO validate placeholder is a string if specified |
| 4166 | if ( opts.element.get( 0 ).tagName.toLowerCase() === 'select' ) { |
| 4167 | // install the selection initializer |
| 4168 | opts.initSelection = function ( element, callback ) { |
| 4169 | var data = []; |
| 4170 | |
| 4171 | element |
| 4172 | .find( 'option' ) |
| 4173 | .filter( function () { |
| 4174 | return this.selected && ! this.disabled; |
| 4175 | } ) |
| 4176 | .each2( function ( i, elm ) { |
| 4177 | data.push( self.optionToData( elm ) ); |
| 4178 | } ); |
| 4179 | callback( data ); |
| 4180 | }; |
| 4181 | } else if ( 'data' in opts ) { |
| 4182 | // install default initSelection when applied to hidden input and data is local |
| 4183 | opts.initSelection = |
| 4184 | opts.initSelection || |
| 4185 | function ( element, callback ) { |
| 4186 | var ids = splitVal( |
| 4187 | element.val(), |
| 4188 | opts.separator, |
| 4189 | opts.transformVal |
| 4190 | ); |
| 4191 | //search in data by array of ids, storing matching items in a list |
| 4192 | var matches = []; |
| 4193 | opts.query( { |
| 4194 | matcher: function ( term, text, el ) { |
| 4195 | var is_match = $.grep( ids, function ( id ) { |
| 4196 | return equal( id, opts.id( el ) ); |
| 4197 | } ).length; |
| 4198 | if ( is_match ) { |
| 4199 | matches.push( el ); |
| 4200 | } |
| 4201 | return is_match; |
| 4202 | }, |
| 4203 | callback: ! $.isFunction( callback ) |
| 4204 | ? $.noop |
| 4205 | : function () { |
| 4206 | // reorder matches based on the order they appear in the ids array because right now |
| 4207 | // they are in the order in which they appear in data array |
| 4208 | var ordered = []; |
| 4209 | for ( var i = 0; i < ids.length; i++ ) { |
| 4210 | var id = ids[ i ]; |
| 4211 | for ( |
| 4212 | var j = 0; |
| 4213 | j < matches.length; |
| 4214 | j++ |
| 4215 | ) { |
| 4216 | var match = matches[ j ]; |
| 4217 | if ( |
| 4218 | equal( |
| 4219 | id, |
| 4220 | opts.id( match ) |
| 4221 | ) |
| 4222 | ) { |
| 4223 | ordered.push( match ); |
| 4224 | matches.splice( j, 1 ); |
| 4225 | break; |
| 4226 | } |
| 4227 | } |
| 4228 | } |
| 4229 | callback( ordered ); |
| 4230 | }, |
| 4231 | } ); |
| 4232 | }; |
| 4233 | } |
| 4234 | |
| 4235 | return opts; |
| 4236 | }, |
| 4237 | |
| 4238 | // multi |
| 4239 | selectChoice: function ( choice ) { |
| 4240 | var selected = this.container.find( |
| 4241 | '.select2-search-choice-focus' |
| 4242 | ); |
| 4243 | if ( selected.length && choice && choice[ 0 ] == selected[ 0 ] ) { |
| 4244 | } else { |
| 4245 | if ( selected.length ) { |
| 4246 | this.opts.element.trigger( 'choice-deselected', selected ); |
| 4247 | } |
| 4248 | selected.removeClass( 'select2-search-choice-focus' ); |
| 4249 | if ( choice && choice.length ) { |
| 4250 | this.close(); |
| 4251 | choice.addClass( 'select2-search-choice-focus' ); |
| 4252 | this.opts.element.trigger( 'choice-selected', choice ); |
| 4253 | } |
| 4254 | } |
| 4255 | }, |
| 4256 | |
| 4257 | // multi |
| 4258 | destroy: function () { |
| 4259 | $( "label[for='" + this.search.attr( 'id' ) + "']" ).attr( |
| 4260 | 'for', |
| 4261 | this.opts.element.attr( 'id' ) |
| 4262 | ); |
| 4263 | this.parent.destroy.apply( this, arguments ); |
| 4264 | |
| 4265 | cleanupJQueryElements.call( this, 'searchContainer', 'selection' ); |
| 4266 | }, |
| 4267 | |
| 4268 | // multi |
| 4269 | initContainer: function () { |
| 4270 | var selector = '.select2-choices', |
| 4271 | selection; |
| 4272 | |
| 4273 | this.searchContainer = this.container.find( |
| 4274 | '.select2-search-field' |
| 4275 | ); |
| 4276 | this.selection = selection = this.container.find( selector ); |
| 4277 | |
| 4278 | var _this = this; |
| 4279 | this.selection.on( |
| 4280 | 'click', |
| 4281 | '.select2-container:not(.select2-container-disabled) .select2-search-choice:not(.select2-locked)', |
| 4282 | function ( e ) { |
| 4283 | _this.search[ 0 ].focus(); |
| 4284 | _this.selectChoice( $( this ) ); |
| 4285 | } |
| 4286 | ); |
| 4287 | |
| 4288 | // rewrite labels from original element to focusser |
| 4289 | this.search.attr( 'id', 's2id_autogen' + nextUid() ); |
| 4290 | |
| 4291 | this.search |
| 4292 | .prev() |
| 4293 | .text( |
| 4294 | $( |
| 4295 | "label[for='" + this.opts.element.attr( 'id' ) + "']" |
| 4296 | ).text() |
| 4297 | ) |
| 4298 | .attr( 'for', this.search.attr( 'id' ) ); |
| 4299 | this.opts.element.focus( |
| 4300 | this.bind( function () { |
| 4301 | this.focus(); |
| 4302 | } ) |
| 4303 | ); |
| 4304 | |
| 4305 | this.search.on( |
| 4306 | 'input paste', |
| 4307 | this.bind( function () { |
| 4308 | if ( |
| 4309 | this.search.attr( 'placeholder' ) && |
| 4310 | this.search.val().length == 0 |
| 4311 | ) |
| 4312 | return; |
| 4313 | if ( ! this.isInterfaceEnabled() ) return; |
| 4314 | if ( ! this.opened() ) { |
| 4315 | this.open(); |
| 4316 | } |
| 4317 | } ) |
| 4318 | ); |
| 4319 | |
| 4320 | this.search.attr( 'tabindex', this.elementTabIndex ); |
| 4321 | |
| 4322 | this.keydowns = 0; |
| 4323 | this.search.on( |
| 4324 | 'keydown', |
| 4325 | this.bind( function ( e ) { |
| 4326 | if ( ! this.isInterfaceEnabled() ) return; |
| 4327 | |
| 4328 | ++this.keydowns; |
| 4329 | var selected = selection.find( |
| 4330 | '.select2-search-choice-focus' |
| 4331 | ); |
| 4332 | var prev = selected.prev( |
| 4333 | '.select2-search-choice:not(.select2-locked)' |
| 4334 | ); |
| 4335 | var next = selected.next( |
| 4336 | '.select2-search-choice:not(.select2-locked)' |
| 4337 | ); |
| 4338 | var pos = getCursorInfo( this.search ); |
| 4339 | |
| 4340 | if ( |
| 4341 | selected.length && |
| 4342 | ( e.which == KEY.LEFT || |
| 4343 | e.which == KEY.RIGHT || |
| 4344 | e.which == KEY.BACKSPACE || |
| 4345 | e.which == KEY.DELETE || |
| 4346 | e.which == KEY.ENTER ) |
| 4347 | ) { |
| 4348 | var selectedChoice = selected; |
| 4349 | if ( e.which == KEY.LEFT && prev.length ) { |
| 4350 | selectedChoice = prev; |
| 4351 | } else if ( e.which == KEY.RIGHT ) { |
| 4352 | selectedChoice = next.length ? next : null; |
| 4353 | } else if ( e.which === KEY.BACKSPACE ) { |
| 4354 | if ( this.unselect( selected.first() ) ) { |
| 4355 | this.search.width( 10 ); |
| 4356 | selectedChoice = prev.length ? prev : next; |
| 4357 | } |
| 4358 | } else if ( e.which == KEY.DELETE ) { |
| 4359 | if ( this.unselect( selected.first() ) ) { |
| 4360 | this.search.width( 10 ); |
| 4361 | selectedChoice = next.length ? next : null; |
| 4362 | } |
| 4363 | } else if ( e.which == KEY.ENTER ) { |
| 4364 | selectedChoice = null; |
| 4365 | } |
| 4366 | |
| 4367 | this.selectChoice( selectedChoice ); |
| 4368 | killEvent( e ); |
| 4369 | if ( ! selectedChoice || ! selectedChoice.length ) { |
| 4370 | this.open(); |
| 4371 | } |
| 4372 | return; |
| 4373 | } else if ( |
| 4374 | ( ( e.which === KEY.BACKSPACE && this.keydowns == 1 ) || |
| 4375 | e.which == KEY.LEFT ) && |
| 4376 | pos.offset == 0 && |
| 4377 | ! pos.length |
| 4378 | ) { |
| 4379 | this.selectChoice( |
| 4380 | selection |
| 4381 | .find( |
| 4382 | '.select2-search-choice:not(.select2-locked)' |
| 4383 | ) |
| 4384 | .last() |
| 4385 | ); |
| 4386 | killEvent( e ); |
| 4387 | return; |
| 4388 | } else { |
| 4389 | this.selectChoice( null ); |
| 4390 | } |
| 4391 | |
| 4392 | if ( this.opened() ) { |
| 4393 | switch ( e.which ) { |
| 4394 | case KEY.UP: |
| 4395 | case KEY.DOWN: |
| 4396 | this.moveHighlight( |
| 4397 | e.which === KEY.UP ? -1 : 1 |
| 4398 | ); |
| 4399 | killEvent( e ); |
| 4400 | return; |
| 4401 | case KEY.ENTER: |
| 4402 | this.selectHighlighted(); |
| 4403 | killEvent( e ); |
| 4404 | return; |
| 4405 | case KEY.TAB: |
| 4406 | this.selectHighlighted( { noFocus: true } ); |
| 4407 | this.close(); |
| 4408 | return; |
| 4409 | case KEY.ESC: |
| 4410 | this.cancel( e ); |
| 4411 | killEvent( e ); |
| 4412 | return; |
| 4413 | } |
| 4414 | } |
| 4415 | |
| 4416 | if ( |
| 4417 | e.which === KEY.TAB || |
| 4418 | KEY.isControl( e ) || |
| 4419 | KEY.isFunctionKey( e ) || |
| 4420 | e.which === KEY.BACKSPACE || |
| 4421 | e.which === KEY.ESC |
| 4422 | ) { |
| 4423 | return; |
| 4424 | } |
| 4425 | |
| 4426 | if ( e.which === KEY.ENTER ) { |
| 4427 | if ( this.opts.openOnEnter === false ) { |
| 4428 | return; |
| 4429 | } else if ( |
| 4430 | e.altKey || |
| 4431 | e.ctrlKey || |
| 4432 | e.shiftKey || |
| 4433 | e.metaKey |
| 4434 | ) { |
| 4435 | return; |
| 4436 | } |
| 4437 | } |
| 4438 | |
| 4439 | this.open(); |
| 4440 | |
| 4441 | if ( |
| 4442 | e.which === KEY.PAGE_UP || |
| 4443 | e.which === KEY.PAGE_DOWN |
| 4444 | ) { |
| 4445 | // prevent the page from scrolling |
| 4446 | killEvent( e ); |
| 4447 | } |
| 4448 | |
| 4449 | if ( e.which === KEY.ENTER ) { |
| 4450 | // prevent form from being submitted |
| 4451 | killEvent( e ); |
| 4452 | } |
| 4453 | } ) |
| 4454 | ); |
| 4455 | |
| 4456 | this.search.on( |
| 4457 | 'keyup', |
| 4458 | this.bind( function ( e ) { |
| 4459 | this.keydowns = 0; |
| 4460 | this.resizeSearch(); |
| 4461 | } ) |
| 4462 | ); |
| 4463 | |
| 4464 | this.search.on( |
| 4465 | 'blur', |
| 4466 | this.bind( function ( e ) { |
| 4467 | this.container.removeClass( 'select2-container-active' ); |
| 4468 | this.search.removeClass( 'select2-focused' ); |
| 4469 | this.selectChoice( null ); |
| 4470 | if ( ! this.opened() ) this.clearSearch(); |
| 4471 | e.stopImmediatePropagation(); |
| 4472 | this.opts.element.trigger( $.Event( 'select2-blur' ) ); |
| 4473 | } ) |
| 4474 | ); |
| 4475 | |
| 4476 | this.container.on( |
| 4477 | 'click', |
| 4478 | selector, |
| 4479 | this.bind( function ( e ) { |
| 4480 | if ( ! this.isInterfaceEnabled() ) return; |
| 4481 | if ( |
| 4482 | $( e.target ).closest( '.select2-search-choice' ) |
| 4483 | .length > 0 |
| 4484 | ) { |
| 4485 | // clicked inside a select2 search choice, do not open |
| 4486 | return; |
| 4487 | } |
| 4488 | this.selectChoice( null ); |
| 4489 | this.clearPlaceholder(); |
| 4490 | if ( |
| 4491 | ! this.container.hasClass( 'select2-container-active' ) |
| 4492 | ) { |
| 4493 | this.opts.element.trigger( $.Event( 'select2-focus' ) ); |
| 4494 | } |
| 4495 | this.open(); |
| 4496 | this.focusSearch(); |
| 4497 | e.preventDefault(); |
| 4498 | } ) |
| 4499 | ); |
| 4500 | |
| 4501 | this.container.on( |
| 4502 | 'focus', |
| 4503 | selector, |
| 4504 | this.bind( function () { |
| 4505 | if ( ! this.isInterfaceEnabled() ) return; |
| 4506 | if ( |
| 4507 | ! this.container.hasClass( 'select2-container-active' ) |
| 4508 | ) { |
| 4509 | this.opts.element.trigger( $.Event( 'select2-focus' ) ); |
| 4510 | } |
| 4511 | this.container.addClass( 'select2-container-active' ); |
| 4512 | this.dropdown.addClass( 'select2-drop-active' ); |
| 4513 | this.clearPlaceholder(); |
| 4514 | } ) |
| 4515 | ); |
| 4516 | |
| 4517 | this.initContainerWidth(); |
| 4518 | this.opts.element.hide(); |
| 4519 | |
| 4520 | // set the placeholder if necessary |
| 4521 | this.clearSearch(); |
| 4522 | }, |
| 4523 | |
| 4524 | // multi |
| 4525 | enableInterface: function () { |
| 4526 | if ( this.parent.enableInterface.apply( this, arguments ) ) { |
| 4527 | this.search.prop( 'disabled', ! this.isInterfaceEnabled() ); |
| 4528 | } |
| 4529 | }, |
| 4530 | |
| 4531 | // multi |
| 4532 | initSelection: function () { |
| 4533 | var data; |
| 4534 | if ( |
| 4535 | this.opts.element.val() === '' && |
| 4536 | this.opts.element.text() === '' |
| 4537 | ) { |
| 4538 | this.updateSelection( [] ); |
| 4539 | this.close(); |
| 4540 | // set the placeholder if necessary |
| 4541 | this.clearSearch(); |
| 4542 | } |
| 4543 | if ( this.select || this.opts.element.val() !== '' ) { |
| 4544 | var self = this; |
| 4545 | this.opts.initSelection.call( |
| 4546 | null, |
| 4547 | this.opts.element, |
| 4548 | function ( data ) { |
| 4549 | if ( data !== undefined && data !== null ) { |
| 4550 | self.updateSelection( data ); |
| 4551 | self.close(); |
| 4552 | // set the placeholder if necessary |
| 4553 | self.clearSearch(); |
| 4554 | } |
| 4555 | } |
| 4556 | ); |
| 4557 | } |
| 4558 | }, |
| 4559 | |
| 4560 | // multi |
| 4561 | clearSearch: function () { |
| 4562 | var placeholder = this.getPlaceholder(), |
| 4563 | maxWidth = this.getMaxSearchWidth(); |
| 4564 | |
| 4565 | if ( |
| 4566 | placeholder !== undefined && |
| 4567 | this.getVal().length === 0 && |
| 4568 | this.search.hasClass( 'select2-focused' ) === false |
| 4569 | ) { |
| 4570 | this.search.val( placeholder ).addClass( 'select2-default' ); |
| 4571 | // stretch the search box to full width of the container so as much of the placeholder is visible as possible |
| 4572 | // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944 |
| 4573 | this.search.width( |
| 4574 | maxWidth > 0 ? maxWidth : this.container.css( 'width' ) |
| 4575 | ); |
| 4576 | } else { |
| 4577 | this.search.val( '' ).width( 10 ); |
| 4578 | } |
| 4579 | }, |
| 4580 | |
| 4581 | // multi |
| 4582 | clearPlaceholder: function () { |
| 4583 | if ( this.search.hasClass( 'select2-default' ) ) { |
| 4584 | this.search.val( '' ).removeClass( 'select2-default' ); |
| 4585 | } |
| 4586 | }, |
| 4587 | |
| 4588 | // multi |
| 4589 | opening: function () { |
| 4590 | this.clearPlaceholder(); // should be done before super so placeholder is not used to search |
| 4591 | this.resizeSearch(); |
| 4592 | |
| 4593 | this.parent.opening.apply( this, arguments ); |
| 4594 | |
| 4595 | this.focusSearch(); |
| 4596 | |
| 4597 | // initializes search's value with nextSearchTerm (if defined by user) |
| 4598 | // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter |
| 4599 | if ( this.search.val() === '' ) { |
| 4600 | if ( this.nextSearchTerm != undefined ) { |
| 4601 | this.search.val( this.nextSearchTerm ); |
| 4602 | this.search.select(); |
| 4603 | } |
| 4604 | } |
| 4605 | |
| 4606 | this.updateResults( true ); |
| 4607 | if ( this.opts.shouldFocusInput( this ) ) { |
| 4608 | this.search.focus(); |
| 4609 | } |
| 4610 | this.opts.element.trigger( $.Event( 'select2-open' ) ); |
| 4611 | }, |
| 4612 | |
| 4613 | // multi |
| 4614 | close: function () { |
| 4615 | if ( ! this.opened() ) return; |
| 4616 | this.parent.close.apply( this, arguments ); |
| 4617 | }, |
| 4618 | |
| 4619 | // multi |
| 4620 | focus: function () { |
| 4621 | this.close(); |
| 4622 | this.search.focus(); |
| 4623 | }, |
| 4624 | |
| 4625 | // multi |
| 4626 | isFocused: function () { |
| 4627 | return this.search.hasClass( 'select2-focused' ); |
| 4628 | }, |
| 4629 | |
| 4630 | // multi |
| 4631 | updateSelection: function ( data ) { |
| 4632 | var ids = [], |
| 4633 | filtered = [], |
| 4634 | self = this; |
| 4635 | |
| 4636 | // filter out duplicates |
| 4637 | $( data ).each( function () { |
| 4638 | if ( indexOf( self.id( this ), ids ) < 0 ) { |
| 4639 | ids.push( self.id( this ) ); |
| 4640 | filtered.push( this ); |
| 4641 | } |
| 4642 | } ); |
| 4643 | data = filtered; |
| 4644 | |
| 4645 | this.selection.find( '.select2-search-choice' ).remove(); |
| 4646 | $( data ).each( function () { |
| 4647 | self.addSelectedChoice( this ); |
| 4648 | } ); |
| 4649 | self.postprocessResults(); |
| 4650 | }, |
| 4651 | |
| 4652 | // multi |
| 4653 | tokenize: function () { |
| 4654 | var input = this.search.val(); |
| 4655 | input = this.opts.tokenizer.call( |
| 4656 | this, |
| 4657 | input, |
| 4658 | this.data(), |
| 4659 | this.bind( this.onSelect ), |
| 4660 | this.opts |
| 4661 | ); |
| 4662 | if ( input != null && input != undefined ) { |
| 4663 | this.search.val( input ); |
| 4664 | if ( input.length > 0 ) { |
| 4665 | this.open(); |
| 4666 | } |
| 4667 | } |
| 4668 | }, |
| 4669 | |
| 4670 | // multi |
| 4671 | onSelect: function ( data, options ) { |
| 4672 | if ( ! this.triggerSelect( data ) || data.text === '' ) { |
| 4673 | return; |
| 4674 | } |
| 4675 | |
| 4676 | this.addSelectedChoice( data ); |
| 4677 | |
| 4678 | this.opts.element.trigger( { |
| 4679 | type: 'selected', |
| 4680 | val: this.id( data ), |
| 4681 | choice: data, |
| 4682 | } ); |
| 4683 | |
| 4684 | // keep track of the search's value before it gets cleared |
| 4685 | this.nextSearchTerm = this.opts.nextSearchTerm( |
| 4686 | data, |
| 4687 | this.search.val() |
| 4688 | ); |
| 4689 | |
| 4690 | this.clearSearch(); |
| 4691 | this.updateResults(); |
| 4692 | |
| 4693 | if ( this.select || ! this.opts.closeOnSelect ) |
| 4694 | this.postprocessResults( |
| 4695 | data, |
| 4696 | false, |
| 4697 | this.opts.closeOnSelect === true |
| 4698 | ); |
| 4699 | |
| 4700 | if ( this.opts.closeOnSelect ) { |
| 4701 | this.close(); |
| 4702 | this.search.width( 10 ); |
| 4703 | } else { |
| 4704 | if ( this.countSelectableResults() > 0 ) { |
| 4705 | this.search.width( 10 ); |
| 4706 | this.resizeSearch(); |
| 4707 | if ( |
| 4708 | this.getMaximumSelectionSize() > 0 && |
| 4709 | this.val().length >= this.getMaximumSelectionSize() |
| 4710 | ) { |
| 4711 | // if we reached max selection size repaint the results so choices |
| 4712 | // are replaced with the max selection reached message |
| 4713 | this.updateResults( true ); |
| 4714 | } else { |
| 4715 | // initializes search's value with nextSearchTerm and update search result |
| 4716 | if ( this.nextSearchTerm != undefined ) { |
| 4717 | this.search.val( this.nextSearchTerm ); |
| 4718 | this.updateResults(); |
| 4719 | this.search.select(); |
| 4720 | } |
| 4721 | } |
| 4722 | this.positionDropdown(); |
| 4723 | } else { |
| 4724 | // if nothing left to select close |
| 4725 | this.close(); |
| 4726 | this.search.width( 10 ); |
| 4727 | } |
| 4728 | } |
| 4729 | |
| 4730 | // since its not possible to select an element that has already been |
| 4731 | // added we do not need to check if this is a new element before firing change |
| 4732 | this.triggerChange( { added: data } ); |
| 4733 | |
| 4734 | if ( ! options || ! options.noFocus ) this.focusSearch(); |
| 4735 | }, |
| 4736 | |
| 4737 | // multi |
| 4738 | cancel: function () { |
| 4739 | this.close(); |
| 4740 | this.focusSearch(); |
| 4741 | }, |
| 4742 | |
| 4743 | addSelectedChoice: function ( data ) { |
| 4744 | var enableChoice = ! data.locked, |
| 4745 | enabledItem = $( |
| 4746 | "<li class='select2-search-choice'>" + |
| 4747 | ' <div></div>' + |
| 4748 | " <a href='#' class='select2-search-choice-close' tabindex='-1'></a>" + |
| 4749 | '</li>' |
| 4750 | ), |
| 4751 | disabledItem = $( |
| 4752 | "<li class='select2-search-choice select2-locked'>" + |
| 4753 | '<div></div>' + |
| 4754 | '</li>' |
| 4755 | ); |
| 4756 | var choice = enableChoice ? enabledItem : disabledItem, |
| 4757 | id = this.id( data ), |
| 4758 | val = this.getVal(), |
| 4759 | formatted, |
| 4760 | cssClass; |
| 4761 | |
| 4762 | formatted = this.opts.formatSelection( |
| 4763 | data, |
| 4764 | choice.find( 'div' ), |
| 4765 | this.opts.escapeMarkup |
| 4766 | ); |
| 4767 | if ( formatted != undefined ) { |
| 4768 | choice |
| 4769 | .find( 'div' ) |
| 4770 | .replaceWith( $( '<div></div>' ).html( formatted ) ); |
| 4771 | } |
| 4772 | cssClass = this.opts.formatSelectionCssClass( |
| 4773 | data, |
| 4774 | choice.find( 'div' ) |
| 4775 | ); |
| 4776 | if ( cssClass != undefined ) { |
| 4777 | choice.addClass( cssClass ); |
| 4778 | } |
| 4779 | |
| 4780 | if ( enableChoice ) { |
| 4781 | choice |
| 4782 | .find( '.select2-search-choice-close' ) |
| 4783 | .on( 'mousedown', killEvent ) |
| 4784 | .on( |
| 4785 | 'click dblclick', |
| 4786 | this.bind( function ( e ) { |
| 4787 | if ( ! this.isInterfaceEnabled() ) return; |
| 4788 | |
| 4789 | this.unselect( $( e.target ) ); |
| 4790 | this.selection |
| 4791 | .find( '.select2-search-choice-focus' ) |
| 4792 | .removeClass( 'select2-search-choice-focus' ); |
| 4793 | killEvent( e ); |
| 4794 | this.close(); |
| 4795 | this.focusSearch(); |
| 4796 | } ) |
| 4797 | ) |
| 4798 | .on( |
| 4799 | 'focus', |
| 4800 | this.bind( function () { |
| 4801 | if ( ! this.isInterfaceEnabled() ) return; |
| 4802 | this.container.addClass( |
| 4803 | 'select2-container-active' |
| 4804 | ); |
| 4805 | this.dropdown.addClass( 'select2-drop-active' ); |
| 4806 | } ) |
| 4807 | ); |
| 4808 | } |
| 4809 | |
| 4810 | choice.data( 'select2-data', data ); |
| 4811 | choice.insertBefore( this.searchContainer ); |
| 4812 | |
| 4813 | val.push( id ); |
| 4814 | this.setVal( val ); |
| 4815 | }, |
| 4816 | |
| 4817 | // multi |
| 4818 | unselect: function ( selected ) { |
| 4819 | var val = this.getVal(), |
| 4820 | data, |
| 4821 | index; |
| 4822 | selected = selected.closest( '.select2-search-choice' ); |
| 4823 | |
| 4824 | if ( selected.length === 0 ) { |
| 4825 | throw ( |
| 4826 | 'Invalid argument: ' + |
| 4827 | selected + |
| 4828 | '. Must be .select2-search-choice' |
| 4829 | ); |
| 4830 | } |
| 4831 | |
| 4832 | data = selected.data( 'select2-data' ); |
| 4833 | |
| 4834 | if ( ! data ) { |
| 4835 | // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued |
| 4836 | // and invoked on an element already removed |
| 4837 | return; |
| 4838 | } |
| 4839 | |
| 4840 | var evt = $.Event( 'select2-removing' ); |
| 4841 | evt.val = this.id( data ); |
| 4842 | evt.choice = data; |
| 4843 | this.opts.element.trigger( evt ); |
| 4844 | |
| 4845 | if ( evt.isDefaultPrevented() ) { |
| 4846 | return false; |
| 4847 | } |
| 4848 | |
| 4849 | while ( ( index = indexOf( this.id( data ), val ) ) >= 0 ) { |
| 4850 | val.splice( index, 1 ); |
| 4851 | this.setVal( val ); |
| 4852 | if ( this.select ) this.postprocessResults(); |
| 4853 | } |
| 4854 | |
| 4855 | selected.remove(); |
| 4856 | |
| 4857 | this.opts.element.trigger( { |
| 4858 | type: 'select2-removed', |
| 4859 | val: this.id( data ), |
| 4860 | choice: data, |
| 4861 | } ); |
| 4862 | this.triggerChange( { removed: data } ); |
| 4863 | |
| 4864 | return true; |
| 4865 | }, |
| 4866 | |
| 4867 | // multi |
| 4868 | postprocessResults: function ( data, initial, noHighlightUpdate ) { |
| 4869 | var val = this.getVal(), |
| 4870 | choices = this.results.find( '.select2-result' ), |
| 4871 | compound = this.results.find( '.select2-result-with-children' ), |
| 4872 | self = this; |
| 4873 | |
| 4874 | choices.each2( function ( i, choice ) { |
| 4875 | var id = self.id( choice.data( 'select2-data' ) ); |
| 4876 | if ( indexOf( id, val ) >= 0 ) { |
| 4877 | choice.addClass( 'select2-selected' ); |
| 4878 | // mark all children of the selected parent as selected |
| 4879 | choice |
| 4880 | .find( '.select2-result-selectable' ) |
| 4881 | .addClass( 'select2-selected' ); |
| 4882 | } |
| 4883 | } ); |
| 4884 | |
| 4885 | compound.each2( function ( i, choice ) { |
| 4886 | // hide an optgroup if it doesn't have any selectable children |
| 4887 | if ( |
| 4888 | ! choice.is( '.select2-result-selectable' ) && |
| 4889 | choice.find( |
| 4890 | '.select2-result-selectable:not(.select2-selected)' |
| 4891 | ).length === 0 |
| 4892 | ) { |
| 4893 | choice.addClass( 'select2-selected' ); |
| 4894 | } |
| 4895 | } ); |
| 4896 | |
| 4897 | if ( |
| 4898 | this.highlight() == -1 && |
| 4899 | noHighlightUpdate !== false && |
| 4900 | this.opts.closeOnSelect === true |
| 4901 | ) { |
| 4902 | self.highlight( 0 ); |
| 4903 | } |
| 4904 | |
| 4905 | //If all results are chosen render formatNoMatches |
| 4906 | if ( |
| 4907 | ! this.opts.createSearchChoice && |
| 4908 | ! choices.filter( '.select2-result:not(.select2-selected)' ) |
| 4909 | .length > 0 |
| 4910 | ) { |
| 4911 | if ( |
| 4912 | ! data || |
| 4913 | ( data && |
| 4914 | ! data.more && |
| 4915 | this.results.find( '.select2-no-results' ).length === |
| 4916 | 0 ) |
| 4917 | ) { |
| 4918 | if ( |
| 4919 | checkFormatter( |
| 4920 | self.opts.formatNoMatches, |
| 4921 | 'formatNoMatches' |
| 4922 | ) |
| 4923 | ) { |
| 4924 | this.results.append( |
| 4925 | "<li class='select2-no-results'>" + |
| 4926 | evaluate( |
| 4927 | self.opts.formatNoMatches, |
| 4928 | self.opts.element, |
| 4929 | self.search.val() |
| 4930 | ) + |
| 4931 | '</li>' |
| 4932 | ); |
| 4933 | } |
| 4934 | } |
| 4935 | } |
| 4936 | }, |
| 4937 | |
| 4938 | // multi |
| 4939 | getMaxSearchWidth: function () { |
| 4940 | return this.selection.width() - getSideBorderPadding( this.search ); |
| 4941 | }, |
| 4942 | |
| 4943 | // multi |
| 4944 | resizeSearch: function () { |
| 4945 | var minimumWidth, |
| 4946 | left, |
| 4947 | maxWidth, |
| 4948 | containerLeft, |
| 4949 | searchWidth, |
| 4950 | sideBorderPadding = getSideBorderPadding( this.search ); |
| 4951 | |
| 4952 | minimumWidth = measureTextWidth( this.search ) + 10; |
| 4953 | |
| 4954 | left = this.search.offset().left; |
| 4955 | |
| 4956 | maxWidth = this.selection.width(); |
| 4957 | containerLeft = this.selection.offset().left; |
| 4958 | |
| 4959 | searchWidth = |
| 4960 | maxWidth - ( left - containerLeft ) - sideBorderPadding; |
| 4961 | |
| 4962 | if ( searchWidth < minimumWidth ) { |
| 4963 | searchWidth = maxWidth - sideBorderPadding; |
| 4964 | } |
| 4965 | |
| 4966 | if ( searchWidth < 40 ) { |
| 4967 | searchWidth = maxWidth - sideBorderPadding; |
| 4968 | } |
| 4969 | |
| 4970 | if ( searchWidth <= 0 ) { |
| 4971 | searchWidth = minimumWidth; |
| 4972 | } |
| 4973 | |
| 4974 | this.search.width( Math.floor( searchWidth ) ); |
| 4975 | }, |
| 4976 | |
| 4977 | // multi |
| 4978 | getVal: function () { |
| 4979 | var val; |
| 4980 | if ( this.select ) { |
| 4981 | val = this.select.val(); |
| 4982 | return val === null ? [] : val; |
| 4983 | } else { |
| 4984 | val = this.opts.element.val(); |
| 4985 | return splitVal( |
| 4986 | val, |
| 4987 | this.opts.separator, |
| 4988 | this.opts.transformVal |
| 4989 | ); |
| 4990 | } |
| 4991 | }, |
| 4992 | |
| 4993 | // multi |
| 4994 | setVal: function ( val ) { |
| 4995 | var unique; |
| 4996 | if ( this.select ) { |
| 4997 | this.select.val( val ); |
| 4998 | } else { |
| 4999 | unique = []; |
| 5000 | // filter out duplicates |
| 5001 | $( val ).each( function () { |
| 5002 | if ( indexOf( this, unique ) < 0 ) unique.push( this ); |
| 5003 | } ); |
| 5004 | this.opts.element.val( |
| 5005 | unique.length === 0 |
| 5006 | ? '' |
| 5007 | : unique.join( this.opts.separator ) |
| 5008 | ); |
| 5009 | } |
| 5010 | }, |
| 5011 | |
| 5012 | // multi |
| 5013 | buildChangeDetails: function ( old, current ) { |
| 5014 | var current = current.slice( 0 ), |
| 5015 | old = old.slice( 0 ); |
| 5016 | |
| 5017 | // remove intersection from each array |
| 5018 | for ( var i = 0; i < current.length; i++ ) { |
| 5019 | for ( var j = 0; j < old.length; j++ ) { |
| 5020 | if ( |
| 5021 | equal( |
| 5022 | this.opts.id( current[ i ] ), |
| 5023 | this.opts.id( old[ j ] ) |
| 5024 | ) |
| 5025 | ) { |
| 5026 | current.splice( i, 1 ); |
| 5027 | if ( i > 0 ) { |
| 5028 | i--; |
| 5029 | } |
| 5030 | old.splice( j, 1 ); |
| 5031 | j--; |
| 5032 | } |
| 5033 | } |
| 5034 | } |
| 5035 | |
| 5036 | return { added: current, removed: old }; |
| 5037 | }, |
| 5038 | |
| 5039 | // multi |
| 5040 | val: function ( val, triggerChange ) { |
| 5041 | var oldData, |
| 5042 | self = this; |
| 5043 | |
| 5044 | if ( arguments.length === 0 ) { |
| 5045 | return this.getVal(); |
| 5046 | } |
| 5047 | |
| 5048 | oldData = this.data(); |
| 5049 | if ( ! oldData.length ) oldData = []; |
| 5050 | |
| 5051 | // val is an id. !val is true for [undefined,null,'',0] - 0 is legal |
| 5052 | if ( ! val && val !== 0 ) { |
| 5053 | this.opts.element.val( '' ); |
| 5054 | this.updateSelection( [] ); |
| 5055 | this.clearSearch(); |
| 5056 | if ( triggerChange ) { |
| 5057 | this.triggerChange( { |
| 5058 | added: this.data(), |
| 5059 | removed: oldData, |
| 5060 | } ); |
| 5061 | } |
| 5062 | return; |
| 5063 | } |
| 5064 | |
| 5065 | // val is a list of ids |
| 5066 | this.setVal( val ); |
| 5067 | |
| 5068 | if ( this.select ) { |
| 5069 | this.opts.initSelection( |
| 5070 | this.select, |
| 5071 | this.bind( this.updateSelection ) |
| 5072 | ); |
| 5073 | if ( triggerChange ) { |
| 5074 | this.triggerChange( |
| 5075 | this.buildChangeDetails( oldData, this.data() ) |
| 5076 | ); |
| 5077 | } |
| 5078 | } else { |
| 5079 | if ( this.opts.initSelection === undefined ) { |
| 5080 | throw new Error( |
| 5081 | 'val() cannot be called if initSelection() is not defined' |
| 5082 | ); |
| 5083 | } |
| 5084 | |
| 5085 | this.opts.initSelection( this.opts.element, function ( data ) { |
| 5086 | var ids = $.map( data, self.id ); |
| 5087 | self.setVal( ids ); |
| 5088 | self.updateSelection( data ); |
| 5089 | self.clearSearch(); |
| 5090 | if ( triggerChange ) { |
| 5091 | self.triggerChange( |
| 5092 | self.buildChangeDetails( oldData, self.data() ) |
| 5093 | ); |
| 5094 | } |
| 5095 | } ); |
| 5096 | } |
| 5097 | this.clearSearch(); |
| 5098 | }, |
| 5099 | |
| 5100 | // multi |
| 5101 | onSortStart: function () { |
| 5102 | if ( this.select ) { |
| 5103 | throw new Error( |
| 5104 | "Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead." |
| 5105 | ); |
| 5106 | } |
| 5107 | |
| 5108 | // collapse search field into 0 width so its container can be collapsed as well |
| 5109 | this.search.width( 0 ); |
| 5110 | // hide the container |
| 5111 | this.searchContainer.hide(); |
| 5112 | }, |
| 5113 | |
| 5114 | // multi |
| 5115 | onSortEnd: function () { |
| 5116 | var val = [], |
| 5117 | self = this; |
| 5118 | |
| 5119 | // show search and move it to the end of the list |
| 5120 | this.searchContainer.show(); |
| 5121 | // make sure the search container is the last item in the list |
| 5122 | this.searchContainer.appendTo( this.searchContainer.parent() ); |
| 5123 | // since we collapsed the width in dragStarted, we resize it here |
| 5124 | this.resizeSearch(); |
| 5125 | |
| 5126 | // update selection |
| 5127 | this.selection.find( '.select2-search-choice' ).each( function () { |
| 5128 | val.push( self.opts.id( $( this ).data( 'select2-data' ) ) ); |
| 5129 | } ); |
| 5130 | this.setVal( val ); |
| 5131 | this.triggerChange(); |
| 5132 | }, |
| 5133 | |
| 5134 | // multi |
| 5135 | data: function ( values, triggerChange ) { |
| 5136 | var self = this, |
| 5137 | ids, |
| 5138 | old; |
| 5139 | if ( arguments.length === 0 ) { |
| 5140 | return this.selection |
| 5141 | .children( '.select2-search-choice' ) |
| 5142 | .map( function () { |
| 5143 | return $( this ).data( 'select2-data' ); |
| 5144 | } ) |
| 5145 | .get(); |
| 5146 | } else { |
| 5147 | old = this.data(); |
| 5148 | if ( ! values ) { |
| 5149 | values = []; |
| 5150 | } |
| 5151 | ids = $.map( values, function ( e ) { |
| 5152 | return self.opts.id( e ); |
| 5153 | } ); |
| 5154 | this.setVal( ids ); |
| 5155 | this.updateSelection( values ); |
| 5156 | this.clearSearch(); |
| 5157 | if ( triggerChange ) { |
| 5158 | this.triggerChange( |
| 5159 | this.buildChangeDetails( old, this.data() ) |
| 5160 | ); |
| 5161 | } |
| 5162 | } |
| 5163 | }, |
| 5164 | } ); |
| 5165 | |
| 5166 | $.fn.select2 = function () { |
| 5167 | var args = Array.prototype.slice.call( arguments, 0 ), |
| 5168 | opts, |
| 5169 | select2, |
| 5170 | method, |
| 5171 | value, |
| 5172 | multiple, |
| 5173 | allowedMethods = [ |
| 5174 | 'val', |
| 5175 | 'destroy', |
| 5176 | 'opened', |
| 5177 | 'open', |
| 5178 | 'close', |
| 5179 | 'focus', |
| 5180 | 'isFocused', |
| 5181 | 'container', |
| 5182 | 'dropdown', |
| 5183 | 'onSortStart', |
| 5184 | 'onSortEnd', |
| 5185 | 'enable', |
| 5186 | 'disable', |
| 5187 | 'readonly', |
| 5188 | 'positionDropdown', |
| 5189 | 'data', |
| 5190 | 'search', |
| 5191 | ], |
| 5192 | valueMethods = [ 'opened', 'isFocused', 'container', 'dropdown' ], |
| 5193 | propertyMethods = [ 'val', 'data' ], |
| 5194 | methodsMap = { search: 'externalSearch' }; |
| 5195 | |
| 5196 | this.each( function () { |
| 5197 | if ( args.length === 0 || typeof args[ 0 ] === 'object' ) { |
| 5198 | opts = args.length === 0 ? {} : $.extend( {}, args[ 0 ] ); |
| 5199 | opts.element = $( this ); |
| 5200 | |
| 5201 | if ( |
| 5202 | opts.element.get( 0 ).tagName.toLowerCase() === 'select' |
| 5203 | ) { |
| 5204 | multiple = opts.element.prop( 'multiple' ); |
| 5205 | } else { |
| 5206 | multiple = opts.multiple || false; |
| 5207 | if ( 'tags' in opts ) { |
| 5208 | opts.multiple = multiple = true; |
| 5209 | } |
| 5210 | } |
| 5211 | |
| 5212 | select2 = multiple |
| 5213 | ? new window.Select2[ 'class' ].multi() |
| 5214 | : new window.Select2[ 'class' ].single(); |
| 5215 | select2.init( opts ); |
| 5216 | } else if ( typeof args[ 0 ] === 'string' ) { |
| 5217 | if ( indexOf( args[ 0 ], allowedMethods ) < 0 ) { |
| 5218 | throw 'Unknown method: ' + args[ 0 ]; |
| 5219 | } |
| 5220 | |
| 5221 | value = undefined; |
| 5222 | select2 = $( this ).data( 'select2' ); |
| 5223 | if ( select2 === undefined ) return; |
| 5224 | |
| 5225 | method = args[ 0 ]; |
| 5226 | |
| 5227 | if ( method === 'container' ) { |
| 5228 | value = select2.container; |
| 5229 | } else if ( method === 'dropdown' ) { |
| 5230 | value = select2.dropdown; |
| 5231 | } else { |
| 5232 | if ( methodsMap[ method ] ) method = methodsMap[ method ]; |
| 5233 | |
| 5234 | value = select2[ method ].apply( select2, args.slice( 1 ) ); |
| 5235 | } |
| 5236 | if ( |
| 5237 | indexOf( args[ 0 ], valueMethods ) >= 0 || |
| 5238 | ( indexOf( args[ 0 ], propertyMethods ) >= 0 && |
| 5239 | args.length == 1 ) |
| 5240 | ) { |
| 5241 | return false; // abort the iteration, ready to return first matched value |
| 5242 | } |
| 5243 | } else { |
| 5244 | throw 'Invalid arguments to select2 plugin: ' + args; |
| 5245 | } |
| 5246 | } ); |
| 5247 | return value === undefined ? this : value; |
| 5248 | }; |
| 5249 | |
| 5250 | // plugin defaults, accessible to users |
| 5251 | $.fn.select2.defaults = { |
| 5252 | width: 'copy', |
| 5253 | loadMorePadding: 0, |
| 5254 | closeOnSelect: true, |
| 5255 | openOnEnter: true, |
| 5256 | containerCss: {}, |
| 5257 | dropdownCss: {}, |
| 5258 | containerCssClass: '', |
| 5259 | dropdownCssClass: '', |
| 5260 | formatResult: function ( result, container, query, escapeMarkup ) { |
| 5261 | var markup = []; |
| 5262 | markMatch( this.text( result ), query.term, markup, escapeMarkup ); |
| 5263 | return markup.join( '' ); |
| 5264 | }, |
| 5265 | transformVal: function ( val ) { |
| 5266 | return $.trim( val ); |
| 5267 | }, |
| 5268 | formatSelection: function ( data, container, escapeMarkup ) { |
| 5269 | return data ? escapeMarkup( this.text( data ) ) : undefined; |
| 5270 | }, |
| 5271 | sortResults: function ( results, container, query ) { |
| 5272 | return results; |
| 5273 | }, |
| 5274 | formatResultCssClass: function ( data ) { |
| 5275 | return data.css; |
| 5276 | }, |
| 5277 | formatSelectionCssClass: function ( data, container ) { |
| 5278 | return undefined; |
| 5279 | }, |
| 5280 | minimumResultsForSearch: 0, |
| 5281 | minimumInputLength: 0, |
| 5282 | maximumInputLength: null, |
| 5283 | maximumSelectionSize: 0, |
| 5284 | id: function ( e ) { |
| 5285 | return e == undefined ? null : e.id; |
| 5286 | }, |
| 5287 | text: function ( e ) { |
| 5288 | if ( e && this.data && this.data.text ) { |
| 5289 | if ( $.isFunction( this.data.text ) ) { |
| 5290 | return this.data.text( e ); |
| 5291 | } else { |
| 5292 | return e[ this.data.text ]; |
| 5293 | } |
| 5294 | } else { |
| 5295 | return e.text; |
| 5296 | } |
| 5297 | }, |
| 5298 | matcher: function ( term, text ) { |
| 5299 | return ( |
| 5300 | stripDiacritics( '' + text ) |
| 5301 | .toUpperCase() |
| 5302 | .indexOf( stripDiacritics( '' + term ).toUpperCase() ) >= 0 |
| 5303 | ); |
| 5304 | }, |
| 5305 | separator: ',', |
| 5306 | tokenSeparators: [], |
| 5307 | tokenizer: defaultTokenizer, |
| 5308 | escapeMarkup: defaultEscapeMarkup, |
| 5309 | blurOnChange: false, |
| 5310 | selectOnBlur: false, |
| 5311 | adaptContainerCssClass: function ( c ) { |
| 5312 | return c; |
| 5313 | }, |
| 5314 | adaptDropdownCssClass: function ( c ) { |
| 5315 | return null; |
| 5316 | }, |
| 5317 | nextSearchTerm: function ( selectedObject, currentSearchTerm ) { |
| 5318 | return undefined; |
| 5319 | }, |
| 5320 | searchInputPlaceholder: '', |
| 5321 | createSearchChoicePosition: 'top', |
| 5322 | shouldFocusInput: function ( instance ) { |
| 5323 | // Attempt to detect touch devices |
| 5324 | var supportsTouchEvents = |
| 5325 | 'ontouchstart' in window || navigator.msMaxTouchPoints > 0; |
| 5326 | |
| 5327 | // Only devices which support touch events should be special cased |
| 5328 | if ( ! supportsTouchEvents ) { |
| 5329 | return true; |
| 5330 | } |
| 5331 | |
| 5332 | // Never focus the input if search is disabled |
| 5333 | if ( instance.opts.minimumResultsForSearch < 0 ) { |
| 5334 | return false; |
| 5335 | } |
| 5336 | |
| 5337 | return true; |
| 5338 | }, |
| 5339 | }; |
| 5340 | |
| 5341 | $.fn.select2.locales = []; |
| 5342 | |
| 5343 | $.fn.select2.locales[ 'en' ] = { |
| 5344 | formatMatches: function ( matches ) { |
| 5345 | if ( matches === 1 ) { |
| 5346 | return 'One result is available, press enter to select it.'; |
| 5347 | } |
| 5348 | return ( |
| 5349 | matches + |
| 5350 | ' results are available, use up and down arrow keys to navigate.' |
| 5351 | ); |
| 5352 | }, |
| 5353 | formatNoMatches: function () { |
| 5354 | return 'No matches found'; |
| 5355 | }, |
| 5356 | formatAjaxError: function ( jqXHR, textStatus, errorThrown ) { |
| 5357 | return 'Loading failed'; |
| 5358 | }, |
| 5359 | formatInputTooShort: function ( input, min ) { |
| 5360 | var n = min - input.length; |
| 5361 | return ( |
| 5362 | 'Please enter ' + |
| 5363 | n + |
| 5364 | ' or more character' + |
| 5365 | ( n == 1 ? '' : 's' ) |
| 5366 | ); |
| 5367 | }, |
| 5368 | formatInputTooLong: function ( input, max ) { |
| 5369 | var n = input.length - max; |
| 5370 | return 'Please delete ' + n + ' character' + ( n == 1 ? '' : 's' ); |
| 5371 | }, |
| 5372 | formatSelectionTooBig: function ( limit ) { |
| 5373 | return ( |
| 5374 | 'You can only select ' + |
| 5375 | limit + |
| 5376 | ' item' + |
| 5377 | ( limit == 1 ? '' : 's' ) |
| 5378 | ); |
| 5379 | }, |
| 5380 | formatLoadMore: function ( pageNumber ) { |
| 5381 | return 'Loading more results…'; |
| 5382 | }, |
| 5383 | formatSearching: function () { |
| 5384 | return 'Searching…'; |
| 5385 | }, |
| 5386 | }; |
| 5387 | |
| 5388 | $.extend( $.fn.select2.defaults, $.fn.select2.locales[ 'en' ] ); |
| 5389 | |
| 5390 | $.fn.select2.ajaxDefaults = { |
| 5391 | transport: $.ajax, |
| 5392 | params: { |
| 5393 | type: 'GET', |
| 5394 | cache: false, |
| 5395 | dataType: 'json', |
| 5396 | }, |
| 5397 | }; |
| 5398 | |
| 5399 | // exports |
| 5400 | window.Select2 = { |
| 5401 | query: { |
| 5402 | ajax: ajax, |
| 5403 | local: local, |
| 5404 | tags: tags, |
| 5405 | }, |
| 5406 | util: { |
| 5407 | debounce: debounce, |
| 5408 | markMatch: markMatch, |
| 5409 | escapeMarkup: defaultEscapeMarkup, |
| 5410 | stripDiacritics: stripDiacritics, |
| 5411 | }, |
| 5412 | class: { |
| 5413 | abstract: AbstractSelect2, |
| 5414 | single: SingleSelect2, |
| 5415 | multi: MultiSelect2, |
| 5416 | }, |
| 5417 | }; |
| 5418 | } )( jQuery ); |
| 5419 |