PluginProbe
Adminify – White Label, Admin Menu Editor, Login Customizer / 3.2.4
Adminify – White Label, Admin Menu Editor, Login Customizer v3.2.4
4.3.2 4.3.1 4.3.0 4.2.26 4.2.25 4.2.24 4.2.23 4.2.22 4.2.21 4.2.20 4.2.19 4.2.18 4.2.17 4.2.16 4.2.15 4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 All 165 releases
adminify / assets / vendors / tokenize / tokenize2.js

tokenize2.js in Adminify – White Label, Admin Menu Editor, Login Customizer 3.2.4, at assets/vendors/tokenize/tokenize2.js

2,925 lines 68.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*!
2 * Tokenize2 v1.3.6 (https://github.com/dragonofmercy/Tokenize2)
3 * Copyright 2016-2017 DragonOfMercy.
4 * Licensed under the new BSD license
5 */
6 (function(factory){
7 if (typeof define === 'function' && define.amd) {
8 // AMD. Register as an anonymous module.
9 define( ['jquery'], factory );
10 } else if (typeof module === 'object' && module.exports) {
11 // Node/CommonJS
12 module.exports = function(root, jQuery){
13 if (jQuery === undefined) {
14 // require('jQuery') returns a factory that requires window to
15 // build a jQuery instance, we normalize how we use modules
16 // that require this pattern but the window provided is a noop
17 // if it's defined (how jquery works)
18 if (typeof window !== 'undefined') {
19 jQuery = require( 'jquery' );
20 } else {
21 jQuery = require( 'jquery' )( root );
22 }
23 }
24 factory( jQuery );
25 return jQuery;
26 };
27 } else {
28 // Browser globals
29 factory( jQuery );
30 }
31 }(function($){
32 /**
33 * Tokenize2 constructor.
34 *
35 * @param {object} element
36 * @param {object} options
37 * @constructor
38 */
39 var Tokenize2 = function(element, options){
40
41 this.control = false;
42 this.element = $( element );
43 this.options = $.extend( {}, Tokenize2.DEFAULTS, options );
44
45 this.options.tabIndex = this.options.tabIndex === -1 ? 0 : this.options.tabIndex;
46 this.options.sortable = this.options.tokensMaxItems === 1 ? false : this.options.sortable;
47
48 this.bind();
49 this.trigger( 'tokenize:load' );
50
51 };
52
53 /**
54 * Keycodes constants
55 *
56 * @type {object}
57 */
58 var KEYS = {
59 BACKSPACE: 8,
60 TAB: 9,
61 ENTER: 13,
62 ESCAPE: 27,
63 ARROW_UP: 38,
64 ARROW_DOWN: 40,
65 CTRL: 17,
66 MAJ: 16,
67 A: 65
68 };
69
70 Tokenize2.VERSION = '1.3.6';
71 Tokenize2.DEBOUNCE = null;
72 Tokenize2.DEFAULTS = {
73 tokensMaxItems: 0,
74 tokensAllowCustom: false,
75 dropdownMaxItems: 10,
76 dropdownSelectFirstItem: true,
77 searchMinLength: 0,
78 searchMaxLength: 0,
79 searchFromStart: true,
80 searchHighlight: true,
81 displayNoResultsMessage: false,
82 noResultsMessageText: 'No results mached "%s"',
83 delimiter: ',',
84 dataSource: 'select',
85 debounce: 0,
86 placeholder: false,
87 sortable: false,
88 allowEmptyValues: false,
89 zIndexMargin: 500,
90 tabIndex: 0
91 };
92
93 /**
94 * Trigger an event
95 *
96 * @see $.trigger
97 */
98 Tokenize2.prototype.trigger = function(event, data, elem, onlyHandlers){
99
100 this.element.trigger( event, data, elem, onlyHandlers );
101
102 };
103
104 /**
105 * Bind events
106 */
107 Tokenize2.prototype.bind = function(){
108
109 this.element.on( 'tokenize:load', {}, $.proxy( function(){ this.init() }, this ) )
110 .on( 'tokenize:clear', {}, $.proxy( function(){ this.clear() }, this ) )
111 .on( 'tokenize:remap', {}, $.proxy( function(){ this.remap() }, this ) )
112 .on( 'tokenize:select', {}, $.proxy( function(e, c){ this.focus( c ) }, this ) )
113 .on( 'tokenize:deselect', {}, $.proxy( function(){ this.blur() }, this ) )
114 .on( 'tokenize:search', {}, $.proxy( function(e, v){ this.find( v ) }, this ) )
115 .on( 'tokenize:paste', {}, $.proxy( function(){ this.paste() }, this ) )
116 .on( 'tokenize:dropdown:up', {}, $.proxy( function(){ this.dropdownSelectionMove( -1 ) }, this ) )
117 .on( 'tokenize:dropdown:down', {}, $.proxy( function(){ this.dropdownSelectionMove( 1 ) }, this ) )
118 .on( 'tokenize:dropdown:clear', {}, $.proxy( function(){ this.dropdownClear() }, this ) )
119 .on( 'tokenize:dropdown:show', {}, $.proxy( function(){ this.dropdownShow() }, this ) )
120 .on( 'tokenize:dropdown:hide', {}, $.proxy( function(){ this.dropdownHide() }, this ) )
121 .on( 'tokenize:dropdown:fill', {}, $.proxy( function(e, i){ this.dropdownFill( i ) }, this ) )
122 .on( 'tokenize:dropdown:itemAdd', {}, $.proxy( function(e, i){ this.dropdownAddItem( i ) }, this ) )
123 .on( 'tokenize:keypress', {}, $.proxy( function(e, routedEvent){ this.keypress( routedEvent ) }, this ) )
124 .on( 'tokenize:keydown', {}, $.proxy( function(e, routedEvent){ this.keydown( routedEvent ) }, this ) )
125 .on( 'tokenize:keyup', {}, $.proxy( function(e, routedEvent){ this.keyup( routedEvent ) }, this ) )
126 .on( 'tokenize:tokens:reorder', {}, $.proxy( function(){ this.reorder() }, this ) )
127 .on( 'tokenize:tokens:add', {}, $.proxy( function(e, v, t, c){ this.tokenAdd( v, t, c ) }, this ) )
128 .on( 'tokenize:tokens:remove', {}, $.proxy( function(e, v){ this.tokenRemove( v ) }, this ) );
129
130 };
131
132 /**
133 * Init function
134 */
135 Tokenize2.prototype.init = function(){
136
137 this.id = this.guid();
138 this.element.hide();
139
140 if ( ! this.element.attr( 'multiple' )) {
141 console.error( 'Attribute multiple is missing, tokenize2 can be buggy !' )
142 }
143
144 this.dropdown = undefined;
145 this.searchContainer = $( '<li class="token-search" />' );
146 this.input = $( '<input autocomplete="off" />' )
147 .on( 'keydown', {}, $.proxy( function(e){ this.trigger( 'tokenize:keydown', [e] ) }, this ) )
148 .on( 'keypress', {}, $.proxy( function(e){ this.trigger( 'tokenize:keypress', [e] ) }, this ) )
149 .on( 'keyup', {}, $.proxy( function(e){ this.trigger( 'tokenize:keyup', [e] ) }, this ) )
150 .on(
151 'focus',
152 {},
153 $.proxy(
154 function(){
155 if (this.input.val().length >= this.options.searchMinLength && this.input.val().length > 0) {
156 this.trigger( 'tokenize:search', [this.input.val()] );
157 }
158 },
159 this
160 )
161 )
162 .on(
163 'paste',
164 {},
165 $.proxy(
166 function(){
167 if (this.options.tokensAllowCustom) {
168 setTimeout(
169 $.proxy(
170 function(){
171 this.trigger( 'tokenize:paste' );
172 },
173 this
174 ),
175 10
176 );
177 }
178 },
179 this
180 )
181 );
182
183 if (this.options.searchMaxLength > 0) {
184 this.input.attr( 'maxlength', this.options.searchMaxLength );
185 }
186
187 this.tokensContainer = $( '<ul class="tokens-container form-control" />' )
188 .addClass( this.element.attr( 'data-class' ) )
189 .attr( 'tabindex', this.options.tabIndex )
190 .append( this.searchContainer.append( this.input ) );
191
192 if (this.options.placeholder !== false) {
193 this.placeholder = $( '<li class="placeholder" />' ).html( this.options.placeholder );
194 this.tokensContainer.prepend( this.placeholder );
195 this.element.on(
196 'tokenize:tokens:add tokenize:remap tokenize:select tokenize:deselect tokenize:tokens:remove',
197 $.proxy(
198 function(){
199 if (this.container.hasClass( 'focus' ) || $( 'li.token', this.tokensContainer ).length > 0 || this.input.val().length > 0) {
200 this.placeholder.hide();
201 } else {
202 this.placeholder.show();
203 }
204 },
205 this
206 )
207 );
208 }
209
210 this.container = $( '<div class="tokenize" />' ).attr( 'id', this.id );
211 this.container.append( this.tokensContainer ).insertAfter( this.element );
212 this.container.focusin(
213 $.proxy(
214 function(e){
215 this.trigger( 'tokenize:select', [($( e.target )[0] === this.tokensContainer[0])] );
216 },
217 this
218 )
219 )
220 .focusout(
221 $.proxy(
222 function(){
223 if (this.container.hasClass( 'focus' )) {
224 this.trigger( 'tokenize:deselect' )
225 }
226 },
227 this
228 )
229 );
230
231 if (this.options.tokensMaxItems === 1) {
232 this.container.addClass( 'single' );
233 }
234
235 if (this.options.sortable) {
236 this.container.addClass( 'sortable' );
237 this.tokensContainer.tokenize2sortable(
238 {
239 itemSelector: 'li.token',
240 cursor: 'move',
241 placeholder: '<li class="token shadow"><br /></li>',
242 placeholderClass: 'token shadow',
243 onDragStart: $.proxy(
244 function($item, container, _super){
245 this.searchContainer.hide();
246 _super( $item, container );
247 } ,
248 this
249 ),
250 onDrop: $.proxy(
251 function($item, container, _super){
252 this.searchContainer.show();
253 this.trigger( 'tokenize:tokens:reorder' );
254 _super( $item, container );
255 },
256 this
257 )
258 }
259 );
260 }
261
262 this.element
263 .on(
264 'tokenize:tokens:add tokenize:tokens:remove',
265 $.proxy(
266 function(){
267 if (this.options.tokensMaxItems > 0 && $( 'li.token', this.tokensContainer ).length >= this.options.tokensMaxItems) {
268 this.searchContainer.hide();
269 } else {
270 this.searchContainer.show();
271 }
272 },
273 this
274 )
275 )
276 .on(
277 'tokenize:keydown tokenize:keyup tokenize:loaded',
278 $.proxy(
279 function(){
280 this.scaleInput();
281 },
282 this
283 )
284 );
285
286 this.trigger( 'tokenize:remap' );
287 this.trigger( 'tokenize:tokens:reorder' );
288 this.trigger( 'tokenize:loaded' );
289
290 if (this.element.is( ':disabled' )) {
291 this.disable();
292 }
293
294 };
295
296 /**
297 * Reorder tokens in the select
298 */
299 Tokenize2.prototype.reorder = function(){
300
301 if (this.options.sortable) {
302 var previous, current, data = this.tokensContainer.tokenize2sortable( 'serialize' ).get( 0 );
303 $.each(
304 data,
305 $.proxy(
306 function(k, v){
307 current = $( 'option[value="' + this.escapeQuery( v.value ) + '"]', this.element );
308 if (previous === undefined) {
309 current.prependTo( this.element );
310 } else {
311 previous.after( current );
312 }
313 previous = current;
314 },
315 this
316 )
317 );
318 }
319 };
320
321 /**
322 * Transform clipboard item to tokens
323 */
324 Tokenize2.prototype.paste = function(){
325
326 var $pattern = new RegExp( this.escapeRegex( Array.isArray( this.options.delimiter ) ? this.options.delimiter.join( '|' ) : this.options.delimiter ), 'ig' );
327
328 if ($pattern.test( this.input.val() )) {
329 $.each(
330 this.input.val().split( $pattern ),
331 $.proxy(
332 function(_, value){
333 this.trigger( 'tokenize:tokens:add', [value, null, true] );
334 },
335 this
336 )
337 )
338 }
339
340 };
341
342 /**
343 * Add token
344 *
345 * If text is empty text = value
346 *
347 * @param {string} value
348 * @param {string} text
349 * @param {boolean} force
350 * @returns {Tokenize2}
351 */
352 Tokenize2.prototype.tokenAdd = function(value, text, force){
353
354 value = this.escape( value );
355 text = text === undefined ? value : this.escape( text );
356 force = force || false;
357 this.resetInput();
358
359 // Check if token is empty
360 if (value === undefined || ( ! this.options.allowEmptyValues && value === '')) {
361 this.trigger( 'tokenize:tokens:error:empty' );
362 return this;
363 }
364
365 // Check if max number of token is reached
366 if (this.options.tokensMaxItems > 0 && $( 'li.token', this.tokensContainer ).length >= this.options.tokensMaxItems) {
367 this.trigger( 'tokenize:tokens:error:max' );
368 return this;
369 }
370
371 // Check duplicate token
372 if ($( 'li.token[data-value="' + this.escapeQuery( value ) + '"]', this.tokensContainer ).length > 0) {
373 this.trigger( 'tokenize:tokens:error:duplicate', [value, text] );
374 return this;
375 }
376
377 if ($( 'option[value="' + this.escapeQuery( value ) + '"]', this.element ).length) {
378 $( 'option[value="' + this.escapeQuery( value ) + '"]', this.element ).attr( 'selected', 'selected' ).prop( 'selected', true );
379 } else if (force) {
380 this.element.append( $( '<option selected />' ).val( value ).html( text ) );
381 } else if (this.options.tokensAllowCustom) {
382 this.element.append( $( '<option selected data-type="custom" />' ).val( value ).html( text ) );
383 } else {
384 this.trigger( 'tokenize:tokens:error:notokensAllowCustom' );
385 return this;
386 }
387
388 $( '<li class="token" />' )
389 .attr( 'data-value', value )
390 .append( '<span>' + text + '</span>' )
391 .prepend(
392 $( '<a class="dismiss" />' ).on(
393 'mousedown touchstart',
394 {},
395 $.proxy(
396 function(e){
397 e.preventDefault();
398 if (e.which == 1) {
399 this.trigger( 'tokenize:tokens:remove', [value] );
400 }
401 },
402 this
403 )
404 )
405 )
406 .insertBefore( this.searchContainer );
407
408 this.trigger( 'tokenize:dropdown:hide' );
409 this.trigger( 'tokenize:tokens:added', [value, text] );
410
411 return this;
412
413 };
414
415 /**
416 * Remove token
417 *
418 * @param {string} v
419 * @returns {Tokenize2}
420 */
421 Tokenize2.prototype.tokenRemove = function(v){
422
423 var $item = $( 'option[value="' + this.escapeQuery( v ) + '"]', this.element );
424
425 if ($item.attr( 'data-type' ) === 'custom') {
426 $item.remove();
427 } else {
428 $item.removeAttr( 'selected' ).prop( 'selected', false );
429 }
430
431 $( 'li.token[data-value="' + this.escapeQuery( v ) + '"]', this.tokensContainer ).remove();
432
433 this.trigger( 'tokenize:tokens:reorder' );
434 this.input.focus();
435 return this;
436
437 };
438
439 /**
440 * Refresh tokens reflecting selected options
441 *
442 * @returns {Tokenize2}
443 */
444 Tokenize2.prototype.remap = function(){
445
446 var $selected = $( 'option:selected', this.element );
447 $selected.each(
448 $.proxy(
449 function(v, t) {
450 this.trigger( 'tokenize:tokens:add', [$( t ).val(), $( t ).html(), false] );
451 },
452 this
453 )
454 );
455
456 return this;
457
458 };
459
460 /**
461 * Disable tokenize
462 *
463 * @returns {Tokenize2}
464 */
465 Tokenize2.prototype.disable = function(){
466
467 this.tokensContainer.addClass( 'disabled' );
468 this.searchContainer.hide();
469 return this;
470
471 };
472
473 /**
474 * Enable tokenize
475 *
476 * @returns {Tokenize2}
477 */
478 Tokenize2.prototype.enable = function(){
479
480 this.tokensContainer.removeClass( 'disabled' );
481 this.searchContainer.show();
482 return this;
483
484 };
485
486 /**
487 * Focus
488 *
489 * @param {boolean} container
490 */
491 Tokenize2.prototype.focus = function(container){
492
493 if (this.element.is( ':disabled' )) {
494 this.tokensContainer.blur();
495 return;
496 }
497
498 if (container) {
499 this.input.focus();
500 }
501
502 if ( ! this.container.hasClass( 'focus' )) {
503 this.container.addClass( 'focus' );
504 this.trigger( 'tokenize:focus' );
505 }
506
507 };
508
509 /**
510 * Blur
511 */
512 Tokenize2.prototype.blur = function(){
513
514 if (this.isDropdownOpen()) {
515 this.trigger( 'tokenize:dropdown:hide' );
516 }
517 this.container.removeClass( 'focus' );
518 this.resetPending();
519 if ( ! this.tokensContainer.attr( 'tabindex' )) {
520 this.tokensContainer.attr( 'tabindex', this.options.tabIndex );
521 }
522
523 };
524
525 /**
526 * Keydown
527 *
528 * @param {Event} e
529 */
530 Tokenize2.prototype.keydown = function(e){
531
532 if (e.type === 'keydown') {
533 switch (e.keyCode) {
534 case KEYS.BACKSPACE:
535 if (this.input.val().length < 1) {
536 e.preventDefault();
537 if ($( 'li.token.pending-delete', this.tokensContainer ).length > 0) {
538 $( 'li.token.pending-delete', this.tokensContainer ).each(
539 $.proxy(
540 function(v, t){
541 this.trigger( 'tokenize:tokens:remove', $( t ).attr( 'data-value' ) );
542 },
543 this
544 )
545 );
546 } else {
547 var $token = $( 'li.token:last', this.tokensContainer );
548 if ($token.length > 0) {
549 this.trigger( 'tokenize:tokens:markForDelete', [$token.attr( 'data-value' )] );
550 $token.addClass( 'pending-delete' );
551 }
552 }
553 this.trigger( 'tokenize:dropdown:hide' );
554 }
555 break;
556
557 case KEYS.TAB:
558 if ( ! e.shiftKey) {
559 this.pressedDelimiter( e );
560 } else {
561 this.tokensContainer.removeAttr( 'tabindex' );
562 }
563 break;
564
565 case KEYS.ENTER:
566 this.pressedDelimiter( e );
567 break;
568
569 case KEYS.ESCAPE:
570 this.resetPending();
571 break;
572
573 case KEYS.ARROW_UP:
574 e.preventDefault();
575 this.trigger( 'tokenize:dropdown:up' );
576 break;
577
578 case KEYS.ARROW_DOWN:
579 e.preventDefault();
580 this.trigger( 'tokenize:dropdown:down' );
581 break;
582
583 case KEYS.CTRL:
584 this.control = true;
585 break;
586
587 default:
588 if (e.keyCode === KEYS.A && this.control && this.input.val().length < 1) {
589 e.preventDefault();
590 $( 'li.token', this.tokensContainer ).each(
591 $.proxy(
592 function(v, t){
593 this.trigger( 'tokenize:tokens:markForDelete', [$( t ).attr( 'data-value' )] );
594 $( t ).addClass( 'pending-delete' );
595 },
596 this
597 )
598 );
599 } else {
600 this.resetPending();
601 }
602 break;
603
604 }
605 } else {
606 e.preventDefault();
607 }
608
609 };
610
611 /**
612 * Keyup
613 *
614 * @param {Event} e
615 */
616 Tokenize2.prototype.keyup = function(e){
617
618 if (e.type === 'keyup') {
619 switch (e.keyCode) {
620 case KEYS.TAB:
621 case KEYS.ENTER:
622 case KEYS.ESCAPE:
623 case KEYS.ARROW_UP:
624 case KEYS.ARROW_DOWN:
625 case KEYS.MAJ:
626 break;
627 case KEYS.CTRL:
628 this.control = false;
629 break;
630 case KEYS.BACKSPACE:
631 default:
632 if (this.input.val().length >= this.options.searchMinLength && this.input.val().length > 0) {
633 this.trigger( 'tokenize:search', [this.input.val()] );
634 } else {
635 this.trigger( 'tokenize:dropdown:hide' );
636 }
637 break;
638 }
639 } else {
640 e.preventDefault();
641 }
642
643 };
644
645 /**
646 * Keypress
647 *
648 * @param {Event} e
649 */
650 Tokenize2.prototype.keypress = function(e){
651
652 if (e.type === 'keypress' && ! this.element.is( ':disabled' )) {
653 var $delimiter = false;
654
655 if (Array.isArray( this.options.delimiter )) {
656 if (this.options.delimiter.indexOf( String.fromCharCode( e.which ) ) >= 0) {
657 $delimiter = true;
658 }
659 } else {
660 if (String.fromCharCode( e.which ) === this.options.delimiter) {
661 $delimiter = true;
662 }
663 }
664
665 if ($delimiter) {
666 this.pressedDelimiter( e );
667 }
668 } else {
669 e.preventDefault();
670 }
671
672 };
673
674 /**
675 * When a delimiter is pressed
676 *
677 * @param {Event} e
678 */
679 Tokenize2.prototype.pressedDelimiter = function(e){
680
681 this.resetPending();
682 if (this.isDropdownOpen() && $( 'li.active', this.dropdown ).length > 0 && this.control === false) {
683 e.preventDefault();
684 $( 'li.active a', this.dropdown ).trigger( 'mousedown' );
685 } else {
686 if (this.input.val().length > 0) {
687 e.preventDefault();
688 this.trigger( 'tokenize:tokens:add', [this.input.val()] );
689 }
690 }
691
692 };
693
694 /**
695 * Search value
696 *
697 * @param {string} v
698 */
699 Tokenize2.prototype.find = function(v){
700
701 if (v.length < this.options.searchMinLength) {
702 this.trigger( 'tokenize:dropdown:hide' );
703 return false;
704 }
705
706 this.lastSearchTerms = v;
707
708 if (this.options.dataSource === 'select') {
709 this.dataSourceLocal( v );
710 } else if (typeof this.options.dataSource === 'function') {
711 this.options.dataSource( v, this );
712 } else {
713 this.dataSourceRemote( v );
714 }
715
716 };
717
718 /**
719 * Gets data from ajax
720 *
721 * @param {string} search
722 */
723 Tokenize2.prototype.dataSourceRemote = function(search){
724
725 this.debounce(
726 $.proxy(
727 function(){
728 if (this.xhr !== undefined) {
729 this.xhr.abort();
730 }
731 this.xhr = $.ajax(
732 this.options.dataSource,
733 {
734 data: { search: search },
735 dataType: 'text',
736 success: $.proxy(
737 function(data){
738 var $items = [];
739 if (data != '') {
740 data = JSON.parse( data );
741 $.each(
742 data,
743 function(k, v){
744 $items.push( v );
745 }
746 );
747 }
748 this.trigger( 'tokenize:dropdown:fill', [$items] );
749 },
750 this
751 )
752 }
753 );
754 },
755 this
756 ),
757 this.options.debounce
758 );
759
760 };
761
762 /**
763 * Gets data from select
764 *
765 * @param {string} search
766 */
767 Tokenize2.prototype.dataSourceLocal = function(search){
768
769 var $searchString = this.transliteration( search );
770 var $items = [];
771 var $pattern = (this.options.searchFromStart ? '^' : '') + this.escapeRegex( $searchString );
772 var $regexp = new RegExp( $pattern, 'i' );
773 var $this = this;
774
775 $( 'option', this.element )
776 .not( ':selected, :disabled' )
777 .each(
778 function(){
779 var text = $this.trim( $( this ).html() );
780 var value = $this.trim( $( this ).attr( 'value' ) );
781 if ($regexp.test( $this.transliteration( text ) )) {
782 $items.push( { value: value, text: text } );
783 }
784 }
785 );
786
787 this.trigger( 'tokenize:dropdown:fill', [$items] );
788
789 };
790
791 /**
792 * Debounce method for ajax request
793 *
794 * @param {function} func
795 * @param {number} threshold
796 */
797 Tokenize2.prototype.debounce = function(func, threshold){
798
799 var $args = arguments;
800 var $delayed = $.proxy(
801 function(){
802 func.apply( this, $args );
803 this.debounceTimeout = undefined;
804 },
805 this
806 );
807
808 if (this.debounceTimeout !== undefined) {
809 clearTimeout( this.debounceTimeout );
810 }
811
812 this.debounceTimeout = setTimeout( $delayed, threshold || 0 );
813
814 };
815
816 /**
817 * Show dropdown
818 */
819 Tokenize2.prototype.dropdownShow = function(){
820
821 if ( ! this.isDropdownOpen()) {
822 $( '.tokenize-dropdown' ).remove();
823 this.dropdown = $( '<div class="tokenize-dropdown dropdown"><ul class="dropdown-menu" /></div>' ).attr( 'data-related', this.id );
824 $( 'body' ).append( this.dropdown );
825 this.dropdown.show();
826 this.dropdown.css( 'z-index', this.calculatezindex() + this.options.zIndexMargin );
827 $( window ).on( 'resize scroll', {}, $.proxy( function(){ this.dropdownMove() }, this ) ).trigger( 'resize' );
828 this.trigger( 'tokenize:dropdown:shown' );
829 }
830
831 };
832
833 /**
834 * Calculate z-index
835 *
836 * @returns {int}
837 */
838 Tokenize2.prototype.calculatezindex = function(){
839
840 var el = this.container;
841 var zindex = 0;
842
843 if ( ! isNaN( parseInt( el.css( 'z-index' ) ) ) && parseInt( el.css( 'z-index' ) ) > 0) {
844 zindex = parseInt( el.css( 'z-index' ) );
845 }
846
847 if (zindex < 1) {
848 while (el.length) {
849 el = el.parent();
850 if (el.length > 0) {
851 if ( ! isNaN( parseInt( el.css( 'z-index' ) ) ) && parseInt( el.css( 'z-index' ) ) > 0) {
852 return parseInt( el.css( 'z-index' ) );
853 }
854 if (el.is( 'html' )) {
855 break;
856 }
857 }
858 }
859 }
860
861 return zindex;
862
863 };
864
865 /**
866 * Hide dropdown
867 */
868 Tokenize2.prototype.dropdownHide = function(){
869
870 if (this.isDropdownOpen()) {
871 $( window ).off( 'resize scroll' );
872 this.dropdown.remove();
873 this.dropdown = undefined;
874 this.trigger( 'tokenize:dropdown:hidden' );
875 }
876
877 };
878
879 /**
880 * Clear dropdown
881 */
882 Tokenize2.prototype.dropdownClear = function(){
883
884 if (this.dropdown) {
885 this.dropdown.find( '.dropdown-menu li' ).remove();
886 }
887
888 };
889
890 /**
891 * Fill dropdown with options
892 *
893 * @param {object} items
894 */
895 Tokenize2.prototype.dropdownFill = function(items){
896
897 if (items && items.length > 0) {
898 this.trigger( 'tokenize:dropdown:show' );
899 this.trigger( 'tokenize:dropdown:clear' );
900
901 $.each(
902 items,
903 $.proxy(
904 function(k, v) {
905 if ($( 'li.dropdown-item', this.dropdown ).length <= this.options.dropdownMaxItems) {
906 this.trigger( 'tokenize:dropdown:itemAdd', [v] );
907 }
908 },
909 this
910 )
911 );
912
913 if ($( 'li.active', this.dropdown ).length < 1 && this.options.dropdownSelectFirstItem) {
914 $( 'li:first', this.dropdown ).addClass( 'active' );
915 }
916
917 if ($( 'li.dropdown-item', this.dropdown ).length < 1) {
918 this.trigger( 'tokenize:dropdown:hide' );
919 } else {
920 this.trigger( 'tokenize:dropdown:filled' );
921 }
922 } else {
923 if (this.options.displayNoResultsMessage) {
924 this.trigger( 'tokenize:dropdown:show' );
925 this.trigger( 'tokenize:dropdown:clear' );
926 this.dropdown.find( '.dropdown-menu' ).append(
927 $( '<li class="dropdown-item locked" />' ).html( this.options.noResultsMessageText.replace( '%s', this.input.val() ) )
928 );
929 } else {
930 this.trigger( 'tokenize:dropdown:hide' );
931 }
932 }
933
934 // Fix the dropdown position when page start scroll
935 $( window ).trigger( 'resize' );
936
937 };
938
939 /**
940 * Move selection through dropdown items
941 *
942 * @param {int} d
943 */
944 Tokenize2.prototype.dropdownSelectionMove = function(d){
945
946 if ($( 'li.active', this.dropdown ).length > 0) {
947 if ( ! $( 'li.active', this.dropdown ).is( 'li:' + (d > 0 ? 'last-child' : 'first-child') )) {
948 var $active = $( 'li.active', this.dropdown );
949 $active.removeClass( 'active' );
950 if (d > 0) {
951 $active.next().addClass( 'active' );
952 } else {
953 $active.prev().addClass( 'active' );
954 }
955 } else {
956 $( 'li.active', this.dropdown ).removeClass( 'active' );
957 if (this.options.dropdownSelectFirstItem) {
958 $( 'li:' + (d > 0 ? 'first-child' : 'last-child'), this.dropdown ).addClass( 'active' );
959 }
960 }
961 } else {
962 $( 'li:first', this.dropdown ).addClass( 'active' );
963 }
964
965 };
966
967 /**
968 * Add dropdown item
969 *
970 * @param {object} item
971 */
972 Tokenize2.prototype.dropdownAddItem = function(item){
973
974 if (this.isDropdownOpen()) {
975
976 if (item.hasOwnProperty( 'text' )) {
977 item.text = this.escape( item.text );
978 }
979 item.value = this.escape( item.value );
980
981 var $li = $( '<li class="dropdown-item" />' ).html( this.dropdownItemFormat( item ) )
982 .on(
983 'mouseover',
984 $.proxy(
985 function(e){
986 e.preventDefault();
987 e.target = this.fixTarget( e.target );
988 $( 'li', this.dropdown ).removeClass( 'active' );
989 $( e.target ).parent().addClass( 'active' );
990 },
991 this
992 )
993 ).on(
994 'mouseout',
995 $.proxy(
996 function(){
997 $( 'li', this.dropdown ).removeClass( 'active' );
998 },
999 this
1000 )
1001 ).on(
1002 'mousedown touchstart',
1003 $.proxy(
1004 function(e){
1005 e.preventDefault();
1006 e.target = this.fixTarget( e.target );
1007 this.trigger( 'tokenize:tokens:add', [$( e.target ).attr( 'data-value' ), $( e.target ).attr( 'data-text' ), true] );
1008 },
1009 this
1010 )
1011 );
1012 if ($( 'li.token[data-value="' + this.escapeQuery( $li.find( 'a' ).attr( 'data-value' ) ) + '"]', this.tokensContainer ).length < 1) {
1013 this.dropdown.find( '.dropdown-menu' ).append( $li );
1014 this.trigger( 'tokenize:dropdown:itemAdded', [item] );
1015 }
1016 }
1017
1018 };
1019
1020 /**
1021 * Fix target for hover and click event on dropdown items
1022 *
1023 * @param {object} target
1024 * @returns {object}
1025 */
1026 Tokenize2.prototype.fixTarget = function(target){
1027
1028 var $target = $( target );
1029
1030 if ( ! $target.data( 'value' )) {
1031 var $link = $target.find( 'a' );
1032 if ($link.length) {
1033 return $link.get( 0 );
1034 }
1035 var $parent = $target.parents( '[data-value]' );
1036 if ($parent.length) {
1037 return $parent.get( 0 );
1038 }
1039 }
1040
1041 return $target.get( 0 );
1042
1043 };
1044
1045 /**
1046 * Format dropdown item
1047 *
1048 * @param {object} item
1049 * @returns {object|jQuery}
1050 */
1051 Tokenize2.prototype.dropdownItemFormat = function(item){
1052 if (item.hasOwnProperty( 'text' )) {
1053 var $display = '';
1054 if (this.options.searchHighlight) {
1055 var $regex = new RegExp( (this.options.searchFromStart ? '^' : '') + '(' + this.escapeRegex( this.transliteration( this.lastSearchTerms ) ) + ')', 'gi' );
1056 $display = item.text.replace( $regex, '<span class="tokenize-highlight">$1</span>' );
1057 } else {
1058 $display = item.text;
1059 }
1060
1061 return $( '<a />' ).html( $display ).attr(
1062 {
1063 'data-value': item.value,
1064 'data-text': item.text
1065 }
1066 );
1067 }
1068
1069 };
1070
1071 /**
1072 * Move dropdown according tokens container
1073 */
1074 Tokenize2.prototype.dropdownMove = function(){
1075
1076 var $position = this.tokensContainer.offset();
1077 var $height = this.tokensContainer.outerHeight();
1078 var $width = this.tokensContainer.outerWidth();
1079
1080 $position.top += $height;
1081 this.dropdown.css(
1082 {
1083 width: $width
1084 }
1085 ).offset( $position );
1086
1087 };
1088
1089 /**
1090 * Returns the current status of the dropdown
1091 *
1092 * @returns {boolean}
1093 */
1094 Tokenize2.prototype.isDropdownOpen = function(){
1095
1096 return (this.dropdown !== undefined);
1097
1098 };
1099
1100 /**
1101 * Clear control
1102 *
1103 * @returns {Tokenize2}
1104 */
1105 Tokenize2.prototype.clear = function(){
1106
1107 $.each(
1108 $( 'li.token', this.tokensContainer ),
1109 $.proxy(
1110 function(e, item){
1111 this.trigger( 'tokenize:tokens:remove', [$( item ).attr( 'data-value' )] );
1112 },
1113 this
1114 )
1115 );
1116
1117 this.trigger( 'tokenize:dropdown:hide' );
1118
1119 return this;
1120
1121 };
1122
1123 /**
1124 * Reset pending delete tokens
1125 */
1126 Tokenize2.prototype.resetPending = function(){
1127
1128 var $token = $( 'li.pending-delete', this.tokensContainer );
1129
1130 if ($token.length > 0) {
1131 this.trigger( 'tokenize:tokens:cancelDelete', [$token.attr( 'data-value' )] );
1132 $token.removeClass( 'pending-delete' );
1133 }
1134
1135 };
1136
1137 /**
1138 * Scale input
1139 */
1140 Tokenize2.prototype.scaleInput = function(){
1141
1142 if ( ! this.ctx) {
1143 this.ctx = document.createElement( 'canvas' ).getContext( '2d' );
1144 }
1145
1146 var $width, $tokensContainerWidth;
1147
1148 this.ctx.font = this.input.css( 'font-style' ) + ' ' +
1149 this.input.css( 'font-variant' ) + ' ' +
1150 this.input.css( 'font-weight' ) + ' ' +
1151 Math.ceil( parseFloat( this.input.css( 'font-size' ) ) ) + 'px ' +
1152 this.input.css( 'font-family' );
1153
1154 $width = Math.round( this.ctx.measureText( this.input.val() + 'M' ).width ) + Math.ceil( parseFloat( this.searchContainer.css( 'margin-left' ) ) ) + Math.ceil( parseFloat( this.searchContainer.css( 'margin-right' ) ) );
1155 $tokensContainerWidth = this.tokensContainer.width() -
1156 (
1157 Math.ceil( parseFloat( this.tokensContainer.css( 'border-left-width' ) ) ) + Math.ceil(
1158 parseFloat( this.tokensContainer.css( 'border-right-width' ) ) +
1159 Math.ceil( parseFloat( this.tokensContainer.css( 'padding-left' ) ) ) + Math.ceil( parseFloat( this.tokensContainer.css( 'padding-right' ) ) )
1160 )
1161 );
1162
1163 if ($width >= $tokensContainerWidth) {
1164 $width = $tokensContainerWidth;
1165 }
1166
1167 this.searchContainer.width( $width );
1168 this.ctx.restore();
1169
1170 };
1171
1172 /**
1173 * Reset input
1174 */
1175 Tokenize2.prototype.resetInput = function(){
1176
1177 this.input.val( '' );
1178 this.scaleInput();
1179
1180 };
1181
1182 Tokenize2.prototype.trim = function(string){
1183 return string.trim();
1184 };
1185
1186 /**
1187 * Escape string
1188 *
1189 * @param {string} string
1190 * @returns {string}
1191 */
1192 Tokenize2.prototype.escape = function(string){
1193
1194 string = String( string );
1195
1196 if (string) {
1197 string = this.replaceAll( string, /["]/, '&quot;' );
1198 string = this.replaceAll( string, /[<]/, '&lt;' );
1199 string = this.replaceAll( string, /[>]/, '&gt;' );
1200 }
1201
1202 return string;
1203
1204 };
1205
1206 /**
1207 * Escape string for query
1208 *
1209 * @param {string} string
1210 * @returns {string}
1211 */
1212 Tokenize2.prototype.escapeQuery = function(string){
1213
1214 string = String( string );
1215
1216 if (string) {
1217 string = this.replaceAll( string, /\\/, '\\\\' );
1218 }
1219
1220 return string
1221 }
1222
1223 /**
1224 * Replace all function for strings
1225 *
1226 * @param {string} needle
1227 * @param {string|RegExp} search
1228 * @param {string} replacement
1229 * @returns {string}
1230 */
1231 Tokenize2.prototype.replaceAll = function(needle, search, replacement) {
1232 return needle.replace( new RegExp( search, 'g' ), replacement );
1233 };
1234
1235 /**
1236 * Escape regex
1237 *
1238 * @param {string} value
1239 * @returns {string}
1240 */
1241 Tokenize2.prototype.escapeRegex = function(value){
1242
1243 return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
1244
1245 };
1246
1247 /**
1248 * Generates guid
1249 *
1250 * @returns {string}
1251 */
1252 Tokenize2.prototype.guid = function(){
1253
1254 function s4(){
1255 return Math.floor( (1 + Math.random()) * 0x10000 )
1256 .toString( 16 )
1257 .substring( 1 );
1258 }
1259 return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
1260 s4() + '-' + s4() + s4() + s4();
1261
1262 };
1263
1264 /**
1265 * Retrieve tokens value to an array
1266 *
1267 * @returns {Array}
1268 */
1269 Tokenize2.prototype.toArray = function(){
1270
1271 var $output = [];
1272 $( "option:selected", this.element ).each(
1273 function(){
1274 $output.push( $( this ).val() );
1275 }
1276 );
1277 return $output;
1278
1279 };
1280
1281 Tokenize2.prototype.transliteration = function(text){
1282 var diacritics = {
1283 '\u24B6': 'A',
1284 '\uFF21': 'A',
1285 '\u00C0': 'A',
1286 '\u00C1': 'A',
1287 '\u00C2': 'A',
1288 '\u1EA6': 'A',
1289 '\u1EA4': 'A',
1290 '\u1EAA': 'A',
1291 '\u1EA8': 'A',
1292 '\u00C3': 'A',
1293 '\u0100': 'A',
1294 '\u0102': 'A',
1295 '\u1EB0': 'A',
1296 '\u1EAE': 'A',
1297 '\u1EB4': 'A',
1298 '\u1EB2': 'A',
1299 '\u0226': 'A',
1300 '\u01E0': 'A',
1301 '\u00C4': 'A',
1302 '\u01DE': 'A',
1303 '\u1EA2': 'A',
1304 '\u00C5': 'A',
1305 '\u01FA': 'A',
1306 '\u01CD': 'A',
1307 '\u0200': 'A',
1308 '\u0202': 'A',
1309 '\u1EA0': 'A',
1310 '\u1EAC': 'A',
1311 '\u1EB6': 'A',
1312 '\u1E00': 'A',
1313 '\u0104': 'A',
1314 '\u023A': 'A',
1315 '\u2C6F': 'A',
1316 '\uA732': 'AA',
1317 '\u00C6': 'AE',
1318 '\u01FC': 'AE',
1319 '\u01E2': 'AE',
1320 '\uA734': 'AO',
1321 '\uA736': 'AU',
1322 '\uA738': 'AV',
1323 '\uA73A': 'AV',
1324 '\uA73C': 'AY',
1325 '\u24B7': 'B',
1326 '\uFF22': 'B',
1327 '\u1E02': 'B',
1328 '\u1E04': 'B',
1329 '\u1E06': 'B',
1330 '\u0243': 'B',
1331 '\u0182': 'B',
1332 '\u0181': 'B',
1333 '\u24B8': 'C',
1334 '\uFF23': 'C',
1335 '\u0106': 'C',
1336 '\u0108': 'C',
1337 '\u010A': 'C',
1338 '\u010C': 'C',
1339 '\u00C7': 'C',
1340 '\u1E08': 'C',
1341 '\u0187': 'C',
1342 '\u023B': 'C',
1343 '\uA73E': 'C',
1344 '\u24B9': 'D',
1345 '\uFF24': 'D',
1346 '\u1E0A': 'D',
1347 '\u010E': 'D',
1348 '\u1E0C': 'D',
1349 '\u1E10': 'D',
1350 '\u1E12': 'D',
1351 '\u1E0E': 'D',
1352 '\u0110': 'D',
1353 '\u018B': 'D',
1354 '\u018A': 'D',
1355 '\u0189': 'D',
1356 '\uA779': 'D',
1357 '\u01F1': 'DZ',
1358 '\u01C4': 'DZ',
1359 '\u01F2': 'Dz',
1360 '\u01C5': 'Dz',
1361 '\u24BA': 'E',
1362 '\uFF25': 'E',
1363 '\u00C8': 'E',
1364 '\u00C9': 'E',
1365 '\u00CA': 'E',
1366 '\u1EC0': 'E',
1367 '\u1EBE': 'E',
1368 '\u1EC4': 'E',
1369 '\u1EC2': 'E',
1370 '\u1EBC': 'E',
1371 '\u0112': 'E',
1372 '\u1E14': 'E',
1373 '\u1E16': 'E',
1374 '\u0114': 'E',
1375 '\u0116': 'E',
1376 '\u00CB': 'E',
1377 '\u1EBA': 'E',
1378 '\u011A': 'E',
1379 '\u0204': 'E',
1380 '\u0206': 'E',
1381 '\u1EB8': 'E',
1382 '\u1EC6': 'E',
1383 '\u0228': 'E',
1384 '\u1E1C': 'E',
1385 '\u0118': 'E',
1386 '\u1E18': 'E',
1387 '\u1E1A': 'E',
1388 '\u0190': 'E',
1389 '\u018E': 'E',
1390 '\u24BB': 'F',
1391 '\uFF26': 'F',
1392 '\u1E1E': 'F',
1393 '\u0191': 'F',
1394 '\uA77B': 'F',
1395 '\u24BC': 'G',
1396 '\uFF27': 'G',
1397 '\u01F4': 'G',
1398 '\u011C': 'G',
1399 '\u1E20': 'G',
1400 '\u011E': 'G',
1401 '\u0120': 'G',
1402 '\u01E6': 'G',
1403 '\u0122': 'G',
1404 '\u01E4': 'G',
1405 '\u0193': 'G',
1406 '\uA7A0': 'G',
1407 '\uA77D': 'G',
1408 '\uA77E': 'G',
1409 '\u24BD': 'H',
1410 '\uFF28': 'H',
1411 '\u0124': 'H',
1412 '\u1E22': 'H',
1413 '\u1E26': 'H',
1414 '\u021E': 'H',
1415 '\u1E24': 'H',
1416 '\u1E28': 'H',
1417 '\u1E2A': 'H',
1418 '\u0126': 'H',
1419 '\u2C67': 'H',
1420 '\u2C75': 'H',
1421 '\uA78D': 'H',
1422 '\u24BE': 'I',
1423 '\uFF29': 'I',
1424 '\u00CC': 'I',
1425 '\u00CD': 'I',
1426 '\u00CE': 'I',
1427 '\u0128': 'I',
1428 '\u012A': 'I',
1429 '\u012C': 'I',
1430 '\u0130': 'I',
1431 '\u00CF': 'I',
1432 '\u1E2E': 'I',
1433 '\u1EC8': 'I',
1434 '\u01CF': 'I',
1435 '\u0208': 'I',
1436 '\u020A': 'I',
1437 '\u1ECA': 'I',
1438 '\u012E': 'I',
1439 '\u1E2C': 'I',
1440 '\u0197': 'I',
1441 '\u24BF': 'J',
1442 '\uFF2A': 'J',
1443 '\u0134': 'J',
1444 '\u0248': 'J',
1445 '\u24C0': 'K',
1446 '\uFF2B': 'K',
1447 '\u1E30': 'K',
1448 '\u01E8': 'K',
1449 '\u1E32': 'K',
1450 '\u0136': 'K',
1451 '\u1E34': 'K',
1452 '\u0198': 'K',
1453 '\u2C69': 'K',
1454 '\uA740': 'K',
1455 '\uA742': 'K',
1456 '\uA744': 'K',
1457 '\uA7A2': 'K',
1458 '\u24C1': 'L',
1459 '\uFF2C': 'L',
1460 '\u013F': 'L',
1461 '\u0139': 'L',
1462 '\u013D': 'L',
1463 '\u1E36': 'L',
1464 '\u1E38': 'L',
1465 '\u013B': 'L',
1466 '\u1E3C': 'L',
1467 '\u1E3A': 'L',
1468 '\u0141': 'L',
1469 '\u023D': 'L',
1470 '\u2C62': 'L',
1471 '\u2C60': 'L',
1472 '\uA748': 'L',
1473 '\uA746': 'L',
1474 '\uA780': 'L',
1475 '\u01C7': 'LJ',
1476 '\u01C8': 'Lj',
1477 '\u24C2': 'M',
1478 '\uFF2D': 'M',
1479 '\u1E3E': 'M',
1480 '\u1E40': 'M',
1481 '\u1E42': 'M',
1482 '\u2C6E': 'M',
1483 '\u019C': 'M',
1484 '\u24C3': 'N',
1485 '\uFF2E': 'N',
1486 '\u01F8': 'N',
1487 '\u0143': 'N',
1488 '\u00D1': 'N',
1489 '\u1E44': 'N',
1490 '\u0147': 'N',
1491 '\u1E46': 'N',
1492 '\u0145': 'N',
1493 '\u1E4A': 'N',
1494 '\u1E48': 'N',
1495 '\u0220': 'N',
1496 '\u019D': 'N',
1497 '\uA790': 'N',
1498 '\uA7A4': 'N',
1499 '\u01CA': 'NJ',
1500 '\u01CB': 'Nj',
1501 '\u24C4': 'O',
1502 '\uFF2F': 'O',
1503 '\u00D2': 'O',
1504 '\u00D3': 'O',
1505 '\u00D4': 'O',
1506 '\u1ED2': 'O',
1507 '\u1ED0': 'O',
1508 '\u1ED6': 'O',
1509 '\u1ED4': 'O',
1510 '\u00D5': 'O',
1511 '\u1E4C': 'O',
1512 '\u022C': 'O',
1513 '\u1E4E': 'O',
1514 '\u014C': 'O',
1515 '\u1E50': 'O',
1516 '\u1E52': 'O',
1517 '\u014E': 'O',
1518 '\u022E': 'O',
1519 '\u0230': 'O',
1520 '\u00D6': 'O',
1521 '\u022A': 'O',
1522 '\u1ECE': 'O',
1523 '\u0150': 'O',
1524 '\u01D1': 'O',
1525 '\u020C': 'O',
1526 '\u020E': 'O',
1527 '\u01A0': 'O',
1528 '\u1EDC': 'O',
1529 '\u1EDA': 'O',
1530 '\u1EE0': 'O',
1531 '\u1EDE': 'O',
1532 '\u1EE2': 'O',
1533 '\u1ECC': 'O',
1534 '\u1ED8': 'O',
1535 '\u01EA': 'O',
1536 '\u01EC': 'O',
1537 '\u00D8': 'O',
1538 '\u01FE': 'O',
1539 '\u0186': 'O',
1540 '\u019F': 'O',
1541 '\uA74A': 'O',
1542 '\uA74C': 'O',
1543 '\u01A2': 'OI',
1544 '\uA74E': 'OO',
1545 '\u0222': 'OU',
1546 '\u24C5': 'P',
1547 '\uFF30': 'P',
1548 '\u1E54': 'P',
1549 '\u1E56': 'P',
1550 '\u01A4': 'P',
1551 '\u2C63': 'P',
1552 '\uA750': 'P',
1553 '\uA752': 'P',
1554 '\uA754': 'P',
1555 '\u24C6': 'Q',
1556 '\uFF31': 'Q',
1557 '\uA756': 'Q',
1558 '\uA758': 'Q',
1559 '\u024A': 'Q',
1560 '\u24C7': 'R',
1561 '\uFF32': 'R',
1562 '\u0154': 'R',
1563 '\u1E58': 'R',
1564 '\u0158': 'R',
1565 '\u0210': 'R',
1566 '\u0212': 'R',
1567 '\u1E5A': 'R',
1568 '\u1E5C': 'R',
1569 '\u0156': 'R',
1570 '\u1E5E': 'R',
1571 '\u024C': 'R',
1572 '\u2C64': 'R',
1573 '\uA75A': 'R',
1574 '\uA7A6': 'R',
1575 '\uA782': 'R',
1576 '\u24C8': 'S',
1577 '\uFF33': 'S',
1578 '\u1E9E': 'S',
1579 '\u015A': 'S',
1580 '\u1E64': 'S',
1581 '\u015C': 'S',
1582 '\u1E60': 'S',
1583 '\u0160': 'S',
1584 '\u1E66': 'S',
1585 '\u1E62': 'S',
1586 '\u1E68': 'S',
1587 '\u0218': 'S',
1588 '\u015E': 'S',
1589 '\u2C7E': 'S',
1590 '\uA7A8': 'S',
1591 '\uA784': 'S',
1592 '\u24C9': 'T',
1593 '\uFF34': 'T',
1594 '\u1E6A': 'T',
1595 '\u0164': 'T',
1596 '\u1E6C': 'T',
1597 '\u021A': 'T',
1598 '\u0162': 'T',
1599 '\u1E70': 'T',
1600 '\u1E6E': 'T',
1601 '\u0166': 'T',
1602 '\u01AC': 'T',
1603 '\u01AE': 'T',
1604 '\u023E': 'T',
1605 '\uA786': 'T',
1606 '\uA728': 'TZ',
1607 '\u24CA': 'U',
1608 '\uFF35': 'U',
1609 '\u00D9': 'U',
1610 '\u00DA': 'U',
1611 '\u00DB': 'U',
1612 '\u0168': 'U',
1613 '\u1E78': 'U',
1614 '\u016A': 'U',
1615 '\u1E7A': 'U',
1616 '\u016C': 'U',
1617 '\u00DC': 'U',
1618 '\u01DB': 'U',
1619 '\u01D7': 'U',
1620 '\u01D5': 'U',
1621 '\u01D9': 'U',
1622 '\u1EE6': 'U',
1623 '\u016E': 'U',
1624 '\u0170': 'U',
1625 '\u01D3': 'U',
1626 '\u0214': 'U',
1627 '\u0216': 'U',
1628 '\u01AF': 'U',
1629 '\u1EEA': 'U',
1630 '\u1EE8': 'U',
1631 '\u1EEE': 'U',
1632 '\u1EEC': 'U',
1633 '\u1EF0': 'U',
1634 '\u1EE4': 'U',
1635 '\u1E72': 'U',
1636 '\u0172': 'U',
1637 '\u1E76': 'U',
1638 '\u1E74': 'U',
1639 '\u0244': 'U',
1640 '\u24CB': 'V',
1641 '\uFF36': 'V',
1642 '\u1E7C': 'V',
1643 '\u1E7E': 'V',
1644 '\u01B2': 'V',
1645 '\uA75E': 'V',
1646 '\u0245': 'V',
1647 '\uA760': 'VY',
1648 '\u24CC': 'W',
1649 '\uFF37': 'W',
1650 '\u1E80': 'W',
1651 '\u1E82': 'W',
1652 '\u0174': 'W',
1653 '\u1E86': 'W',
1654 '\u1E84': 'W',
1655 '\u1E88': 'W',
1656 '\u2C72': 'W',
1657 '\u24CD': 'X',
1658 '\uFF38': 'X',
1659 '\u1E8A': 'X',
1660 '\u1E8C': 'X',
1661 '\u24CE': 'Y',
1662 '\uFF39': 'Y',
1663 '\u1EF2': 'Y',
1664 '\u00DD': 'Y',
1665 '\u0176': 'Y',
1666 '\u1EF8': 'Y',
1667 '\u0232': 'Y',
1668 '\u1E8E': 'Y',
1669 '\u0178': 'Y',
1670 '\u1EF6': 'Y',
1671 '\u1EF4': 'Y',
1672 '\u01B3': 'Y',
1673 '\u024E': 'Y',
1674 '\u1EFE': 'Y',
1675 '\u24CF': 'Z',
1676 '\uFF3A': 'Z',
1677 '\u0179': 'Z',
1678 '\u1E90': 'Z',
1679 '\u017B': 'Z',
1680 '\u017D': 'Z',
1681 '\u1E92': 'Z',
1682 '\u1E94': 'Z',
1683 '\u01B5': 'Z',
1684 '\u0224': 'Z',
1685 '\u2C7F': 'Z',
1686 '\u2C6B': 'Z',
1687 '\uA762': 'Z',
1688 '\u24D0': 'a',
1689 '\uFF41': 'a',
1690 '\u1E9A': 'a',
1691 '\u00E0': 'a',
1692 '\u00E1': 'a',
1693 '\u00E2': 'a',
1694 '\u1EA7': 'a',
1695 '\u1EA5': 'a',
1696 '\u1EAB': 'a',
1697 '\u1EA9': 'a',
1698 '\u00E3': 'a',
1699 '\u0101': 'a',
1700 '\u0103': 'a',
1701 '\u1EB1': 'a',
1702 '\u1EAF': 'a',
1703 '\u1EB5': 'a',
1704 '\u1EB3': 'a',
1705 '\u0227': 'a',
1706 '\u01E1': 'a',
1707 '\u00E4': 'a',
1708 '\u01DF': 'a',
1709 '\u1EA3': 'a',
1710 '\u00E5': 'a',
1711 '\u01FB': 'a',
1712 '\u01CE': 'a',
1713 '\u0201': 'a',
1714 '\u0203': 'a',
1715 '\u1EA1': 'a',
1716 '\u1EAD': 'a',
1717 '\u1EB7': 'a',
1718 '\u1E01': 'a',
1719 '\u0105': 'a',
1720 '\u2C65': 'a',
1721 '\u0250': 'a',
1722 '\uA733': 'aa',
1723 '\u00E6': 'ae',
1724 '\u01FD': 'ae',
1725 '\u01E3': 'ae',
1726 '\uA735': 'ao',
1727 '\uA737': 'au',
1728 '\uA739': 'av',
1729 '\uA73B': 'av',
1730 '\uA73D': 'ay',
1731 '\u24D1': 'b',
1732 '\uFF42': 'b',
1733 '\u1E03': 'b',
1734 '\u1E05': 'b',
1735 '\u1E07': 'b',
1736 '\u0180': 'b',
1737 '\u0183': 'b',
1738 '\u0253': 'b',
1739 '\u24D2': 'c',
1740 '\uFF43': 'c',
1741 '\u0107': 'c',
1742 '\u0109': 'c',
1743 '\u010B': 'c',
1744 '\u010D': 'c',
1745 '\u00E7': 'c',
1746 '\u1E09': 'c',
1747 '\u0188': 'c',
1748 '\u023C': 'c',
1749 '\uA73F': 'c',
1750 '\u2184': 'c',
1751 '\u24D3': 'd',
1752 '\uFF44': 'd',
1753 '\u1E0B': 'd',
1754 '\u010F': 'd',
1755 '\u1E0D': 'd',
1756 '\u1E11': 'd',
1757 '\u1E13': 'd',
1758 '\u1E0F': 'd',
1759 '\u0111': 'd',
1760 '\u018C': 'd',
1761 '\u0256': 'd',
1762 '\u0257': 'd',
1763 '\uA77A': 'd',
1764 '\u01F3': 'dz',
1765 '\u01C6': 'dz',
1766 '\u24D4': 'e',
1767 '\uFF45': 'e',
1768 '\u00E8': 'e',
1769 '\u00E9': 'e',
1770 '\u00EA': 'e',
1771 '\u1EC1': 'e',
1772 '\u1EBF': 'e',
1773 '\u1EC5': 'e',
1774 '\u1EC3': 'e',
1775 '\u1EBD': 'e',
1776 '\u0113': 'e',
1777 '\u1E15': 'e',
1778 '\u1E17': 'e',
1779 '\u0115': 'e',
1780 '\u0117': 'e',
1781 '\u00EB': 'e',
1782 '\u1EBB': 'e',
1783 '\u011B': 'e',
1784 '\u0205': 'e',
1785 '\u0207': 'e',
1786 '\u1EB9': 'e',
1787 '\u1EC7': 'e',
1788 '\u0229': 'e',
1789 '\u1E1D': 'e',
1790 '\u0119': 'e',
1791 '\u1E19': 'e',
1792 '\u1E1B': 'e',
1793 '\u0247': 'e',
1794 '\u025B': 'e',
1795 '\u01DD': 'e',
1796 '\u24D5': 'f',
1797 '\uFF46': 'f',
1798 '\u1E1F': 'f',
1799 '\u0192': 'f',
1800 '\uA77C': 'f',
1801 '\u24D6': 'g',
1802 '\uFF47': 'g',
1803 '\u01F5': 'g',
1804 '\u011D': 'g',
1805 '\u1E21': 'g',
1806 '\u011F': 'g',
1807 '\u0121': 'g',
1808 '\u01E7': 'g',
1809 '\u0123': 'g',
1810 '\u01E5': 'g',
1811 '\u0260': 'g',
1812 '\uA7A1': 'g',
1813 '\u1D79': 'g',
1814 '\uA77F': 'g',
1815 '\u24D7': 'h',
1816 '\uFF48': 'h',
1817 '\u0125': 'h',
1818 '\u1E23': 'h',
1819 '\u1E27': 'h',
1820 '\u021F': 'h',
1821 '\u1E25': 'h',
1822 '\u1E29': 'h',
1823 '\u1E2B': 'h',
1824 '\u1E96': 'h',
1825 '\u0127': 'h',
1826 '\u2C68': 'h',
1827 '\u2C76': 'h',
1828 '\u0265': 'h',
1829 '\u0195': 'hv',
1830 '\u24D8': 'i',
1831 '\uFF49': 'i',
1832 '\u00EC': 'i',
1833 '\u00ED': 'i',
1834 '\u00EE': 'i',
1835 '\u0129': 'i',
1836 '\u012B': 'i',
1837 '\u012D': 'i',
1838 '\u00EF': 'i',
1839 '\u1E2F': 'i',
1840 '\u1EC9': 'i',
1841 '\u01D0': 'i',
1842 '\u0209': 'i',
1843 '\u020B': 'i',
1844 '\u1ECB': 'i',
1845 '\u012F': 'i',
1846 '\u1E2D': 'i',
1847 '\u0268': 'i',
1848 '\u0131': 'i',
1849 '\u24D9': 'j',
1850 '\uFF4A': 'j',
1851 '\u0135': 'j',
1852 '\u01F0': 'j',
1853 '\u0249': 'j',
1854 '\u24DA': 'k',
1855 '\uFF4B': 'k',
1856 '\u1E31': 'k',
1857 '\u01E9': 'k',
1858 '\u1E33': 'k',
1859 '\u0137': 'k',
1860 '\u1E35': 'k',
1861 '\u0199': 'k',
1862 '\u2C6A': 'k',
1863 '\uA741': 'k',
1864 '\uA743': 'k',
1865 '\uA745': 'k',
1866 '\uA7A3': 'k',
1867 '\u24DB': 'l',
1868 '\uFF4C': 'l',
1869 '\u0140': 'l',
1870 '\u013A': 'l',
1871 '\u013E': 'l',
1872 '\u1E37': 'l',
1873 '\u1E39': 'l',
1874 '\u013C': 'l',
1875 '\u1E3D': 'l',
1876 '\u1E3B': 'l',
1877 '\u017F': 'l',
1878 '\u0142': 'l',
1879 '\u019A': 'l',
1880 '\u026B': 'l',
1881 '\u2C61': 'l',
1882 '\uA749': 'l',
1883 '\uA781': 'l',
1884 '\uA747': 'l',
1885 '\u01C9': 'lj',
1886 '\u24DC': 'm',
1887 '\uFF4D': 'm',
1888 '\u1E3F': 'm',
1889 '\u1E41': 'm',
1890 '\u1E43': 'm',
1891 '\u0271': 'm',
1892 '\u026F': 'm',
1893 '\u24DD': 'n',
1894 '\uFF4E': 'n',
1895 '\u01F9': 'n',
1896 '\u0144': 'n',
1897 '\u00F1': 'n',
1898 '\u1E45': 'n',
1899 '\u0148': 'n',
1900 '\u1E47': 'n',
1901 '\u0146': 'n',
1902 '\u1E4B': 'n',
1903 '\u1E49': 'n',
1904 '\u019E': 'n',
1905 '\u0272': 'n',
1906 '\u0149': 'n',
1907 '\uA791': 'n',
1908 '\uA7A5': 'n',
1909 '\u01CC': 'nj',
1910 '\u24DE': 'o',
1911 '\uFF4F': 'o',
1912 '\u00F2': 'o',
1913 '\u00F3': 'o',
1914 '\u00F4': 'o',
1915 '\u1ED3': 'o',
1916 '\u1ED1': 'o',
1917 '\u1ED7': 'o',
1918 '\u1ED5': 'o',
1919 '\u00F5': 'o',
1920 '\u1E4D': 'o',
1921 '\u022D': 'o',
1922 '\u1E4F': 'o',
1923 '\u014D': 'o',
1924 '\u1E51': 'o',
1925 '\u1E53': 'o',
1926 '\u014F': 'o',
1927 '\u022F': 'o',
1928 '\u0231': 'o',
1929 '\u00F6': 'o',
1930 '\u022B': 'o',
1931 '\u1ECF': 'o',
1932 '\u0151': 'o',
1933 '\u01D2': 'o',
1934 '\u020D': 'o',
1935 '\u020F': 'o',
1936 '\u01A1': 'o',
1937 '\u1EDD': 'o',
1938 '\u1EDB': 'o',
1939 '\u1EE1': 'o',
1940 '\u1EDF': 'o',
1941 '\u1EE3': 'o',
1942 '\u1ECD': 'o',
1943 '\u1ED9': 'o',
1944 '\u01EB': 'o',
1945 '\u01ED': 'o',
1946 '\u00F8': 'o',
1947 '\u01FF': 'o',
1948 '\u0254': 'o',
1949 '\uA74B': 'o',
1950 '\uA74D': 'o',
1951 '\u0275': 'o',
1952 '\u01A3': 'oi',
1953 '\u0223': 'ou',
1954 '\uA74F': 'oo',
1955 '\u24DF': 'p',
1956 '\uFF50': 'p',
1957 '\u1E55': 'p',
1958 '\u1E57': 'p',
1959 '\u01A5': 'p',
1960 '\u1D7D': 'p',
1961 '\uA751': 'p',
1962 '\uA753': 'p',
1963 '\uA755': 'p',
1964 '\u24E0': 'q',
1965 '\uFF51': 'q',
1966 '\u024B': 'q',
1967 '\uA757': 'q',
1968 '\uA759': 'q',
1969 '\u24E1': 'r',
1970 '\uFF52': 'r',
1971 '\u0155': 'r',
1972 '\u1E59': 'r',
1973 '\u0159': 'r',
1974 '\u0211': 'r',
1975 '\u0213': 'r',
1976 '\u1E5B': 'r',
1977 '\u1E5D': 'r',
1978 '\u0157': 'r',
1979 '\u1E5F': 'r',
1980 '\u024D': 'r',
1981 '\u027D': 'r',
1982 '\uA75B': 'r',
1983 '\uA7A7': 'r',
1984 '\uA783': 'r',
1985 '\u24E2': 's',
1986 '\uFF53': 's',
1987 '\u00DF': 's',
1988 '\u015B': 's',
1989 '\u1E65': 's',
1990 '\u015D': 's',
1991 '\u1E61': 's',
1992 '\u0161': 's',
1993 '\u1E67': 's',
1994 '\u1E63': 's',
1995 '\u1E69': 's',
1996 '\u0219': 's',
1997 '\u015F': 's',
1998 '\u023F': 's',
1999 '\uA7A9': 's',
2000 '\uA785': 's',
2001 '\u1E9B': 's',
2002 '\u24E3': 't',
2003 '\uFF54': 't',
2004 '\u1E6B': 't',
2005 '\u1E97': 't',
2006 '\u0165': 't',
2007 '\u1E6D': 't',
2008 '\u021B': 't',
2009 '\u0163': 't',
2010 '\u1E71': 't',
2011 '\u1E6F': 't',
2012 '\u0167': 't',
2013 '\u01AD': 't',
2014 '\u0288': 't',
2015 '\u2C66': 't',
2016 '\uA787': 't',
2017 '\uA729': 'tz',
2018 '\u24E4': 'u',
2019 '\uFF55': 'u',
2020 '\u00F9': 'u',
2021 '\u00FA': 'u',
2022 '\u00FB': 'u',
2023 '\u0169': 'u',
2024 '\u1E79': 'u',
2025 '\u016B': 'u',
2026 '\u1E7B': 'u',
2027 '\u016D': 'u',
2028 '\u00FC': 'u',
2029 '\u01DC': 'u',
2030 '\u01D8': 'u',
2031 '\u01D6': 'u',
2032 '\u01DA': 'u',
2033 '\u1EE7': 'u',
2034 '\u016F': 'u',
2035 '\u0171': 'u',
2036 '\u01D4': 'u',
2037 '\u0215': 'u',
2038 '\u0217': 'u',
2039 '\u01B0': 'u',
2040 '\u1EEB': 'u',
2041 '\u1EE9': 'u',
2042 '\u1EEF': 'u',
2043 '\u1EED': 'u',
2044 '\u1EF1': 'u',
2045 '\u1EE5': 'u',
2046 '\u1E73': 'u',
2047 '\u0173': 'u',
2048 '\u1E77': 'u',
2049 '\u1E75': 'u',
2050 '\u0289': 'u',
2051 '\u24E5': 'v',
2052 '\uFF56': 'v',
2053 '\u1E7D': 'v',
2054 '\u1E7F': 'v',
2055 '\u028B': 'v',
2056 '\uA75F': 'v',
2057 '\u028C': 'v',
2058 '\uA761': 'vy',
2059 '\u24E6': 'w',
2060 '\uFF57': 'w',
2061 '\u1E81': 'w',
2062 '\u1E83': 'w',
2063 '\u0175': 'w',
2064 '\u1E87': 'w',
2065 '\u1E85': 'w',
2066 '\u1E98': 'w',
2067 '\u1E89': 'w',
2068 '\u2C73': 'w',
2069 '\u24E7': 'x',
2070 '\uFF58': 'x',
2071 '\u1E8B': 'x',
2072 '\u1E8D': 'x',
2073 '\u24E8': 'y',
2074 '\uFF59': 'y',
2075 '\u1EF3': 'y',
2076 '\u00FD': 'y',
2077 '\u0177': 'y',
2078 '\u1EF9': 'y',
2079 '\u0233': 'y',
2080 '\u1E8F': 'y',
2081 '\u00FF': 'y',
2082 '\u1EF7': 'y',
2083 '\u1E99': 'y',
2084 '\u1EF5': 'y',
2085 '\u01B4': 'y',
2086 '\u024F': 'y',
2087 '\u1EFF': 'y',
2088 '\u24E9': 'z',
2089 '\uFF5A': 'z',
2090 '\u017A': 'z',
2091 '\u1E91': 'z',
2092 '\u017C': 'z',
2093 '\u017E': 'z',
2094 '\u1E93': 'z',
2095 '\u1E95': 'z',
2096 '\u01B6': 'z',
2097 '\u0225': 'z',
2098 '\u0240': 'z',
2099 '\u2C6C': 'z',
2100 '\uA763': 'z',
2101 '\u0386': '\u0391',
2102 '\u0388': '\u0395',
2103 '\u0389': '\u0397',
2104 '\u038A': '\u0399',
2105 '\u03AA': '\u0399',
2106 '\u038C': '\u039F',
2107 '\u038E': '\u03A5',
2108 '\u03AB': '\u03A5',
2109 '\u038F': '\u03A9',
2110 '\u03AC': '\u03B1',
2111 '\u03AD': '\u03B5',
2112 '\u03AE': '\u03B7',
2113 '\u03AF': '\u03B9',
2114 '\u03CA': '\u03B9',
2115 '\u0390': '\u03B9',
2116 '\u03CC': '\u03BF',
2117 '\u03CD': '\u03C5',
2118 '\u03CB': '\u03C5',
2119 '\u03B0': '\u03C5',
2120 '\u03C9': '\u03C9',
2121 '\u03C2': '\u03C3'
2122 };
2123
2124 var $match = function(a){
2125 return diacritics[a] || a;
2126 };
2127
2128 return text.replace( /[^\u0000-\u007E]/g, $match );
2129 };
2130
2131 /**
2132 * Tokenize plugin main function
2133 *
2134 * @param {object} options
2135 * @returns {object|Tokenize2|Array}
2136 */
2137 function Plugin(options){
2138
2139 var $items = [];
2140 this.filter( 'select' ).each(
2141 function(){
2142 var $this = $( this );
2143 var $data = $this.data( 'tokenize2' );
2144 var $options = typeof options === 'object' && options;
2145 if ( ! $data) {
2146 $this.data( 'tokenize2', new Tokenize2( this, $options ) );
2147 }
2148 $items.push( $this.data( 'tokenize2' ) );
2149 }
2150 );
2151
2152 if ($items.length > 1) {
2153 return $items;
2154 } else {
2155 return $items[0];
2156 }
2157 }
2158
2159 var old = $.fn.tokenize2;
2160
2161 /**
2162 * jQuery plugin entry
2163 */
2164 $.fn.tokenize2 = Plugin;
2165 $.fn.tokenize2.Constructor = Tokenize2;
2166 $.fn.tokenize2.noConflict = function(){
2167 $.fn.tokenize2 = old;
2168 return this;
2169 }
2170
2171 }));
2172
2173 /* ===================================================
2174 * jquery-sortable.js v0.9.13
2175 * http://johnny.github.com/jquery-sortable/
2176 * ===================================================
2177 * Copyright (c) 2012 Jonas von Andrian
2178 * All rights reserved.
2179 *
2180 * Redistribution and use in source and binary forms, with or without
2181 * modification, are permitted provided that the following conditions are met:
2182 * * Redistributions of source code must retain the above copyright
2183 * notice, this list of conditions and the following disclaimer.
2184 * * Redistributions in binary form must reproduce the above copyright
2185 * notice, this list of conditions and the following disclaimer in the
2186 * documentation and/or other materials provided with the distribution.
2187 * * The name of the author may not be used to endorse or promote products
2188 * derived from this software without specific prior written permission.
2189 *
2190 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
2191 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2192 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2193 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
2194 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2195 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2196 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2197 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2198 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2199 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2200 * ========================================================== */
2201
2202 ! function ( $, window, pluginName, undefined){
2203 var containerDefaults = {
2204 // If true, items can be dragged from this container
2205 drag: true,
2206 // If true, items can be droped onto this container
2207 drop: true,
2208 // Exclude items from being draggable, if the
2209 // selector matches the item
2210 exclude: "",
2211 // If true, search for nested containers within an item.If you nest containers,
2212 // either the original selector with which you call the plugin must only match the top containers,
2213 // or you need to specify a group (see the bootstrap nav example)
2214 nested: true,
2215 // If true, the items are assumed to be arranged vertically
2216 vertical: true
2217 }, // end container defaults
2218 groupDefaults = {
2219 // This is executed after the placeholder has been moved.
2220 // $closestItemOrContainer contains the closest item, the placeholder
2221 // has been put at or the closest empty Container, the placeholder has
2222 // been appended to.
2223 afterMove: function ($placeholder, container, $closestItemOrContainer) {
2224 },
2225 // The exact css path between the container and its items, e.g. "> tbody"
2226 containerPath: "",
2227 // The css selector of the containers
2228 containerSelector: "ol, ul",
2229 // Distance the mouse has to travel to start dragging
2230 distance: 0,
2231 // Time in milliseconds after mousedown until dragging should start.
2232 // This option can be used to prevent unwanted drags when clicking on an element.
2233 delay: 0,
2234 // The css selector of the drag handle
2235 handle: "",
2236 // The exact css path between the item and its subcontainers.
2237 // It should only match the immediate items of a container.
2238 // No item of a subcontainer should be matched. E.g. for ol>div>li the itemPath is "> div"
2239 itemPath: "",
2240 // The css selector of the items
2241 itemSelector: "li",
2242 // The class given to "body" while an item is being dragged
2243 bodyClass: "dragging",
2244 // The class giving to an item while being dragged
2245 draggedClass: "dragged",
2246 // Check if the dragged item may be inside the container.
2247 // Use with care, since the search for a valid container entails a depth first search
2248 // and may be quite expensive.
2249 isValidTarget: function ($item, container) {
2250 return true
2251 },
2252 // Executed before onDrop if placeholder is detached.
2253 // This happens if pullPlaceholder is set to false and the drop occurs outside a container.
2254 onCancel: function ($item, container, _super, event) {
2255 },
2256 // Executed at the beginning of a mouse move event.
2257 // The Placeholder has not been moved yet.
2258 onDrag: function ($item, position, _super, event) {
2259 $item.css( position )
2260 },
2261 // Called after the drag has been started,
2262 // that is the mouse button is being held down and
2263 // the mouse is moving.
2264 // The container is the closest initialized container.
2265 // Therefore it might not be the container, that actually contains the item.
2266 onDragStart: function ($item, container, _super, event) {
2267 $item.css(
2268 {
2269 height: $item.outerHeight(),
2270 width: $item.outerWidth()
2271 }
2272 )
2273 $item.addClass( container.group.options.draggedClass )
2274 $( "body" ).addClass( container.group.options.bodyClass )
2275 },
2276 // Called when the mouse button is being released
2277 onDrop: function ($item, container, _super, event) {
2278 $item.removeClass( container.group.options.draggedClass ).removeAttr( "style" )
2279 $( "body" ).removeClass( container.group.options.bodyClass )
2280 },
2281 // Called on mousedown. If falsy value is returned, the dragging will not start.
2282 // Ignore if element clicked is input, select or textarea
2283 onMousedown: function ($item, _super, event) {
2284 if ( ! event.target.nodeName.match( /^(input|select|textarea)$/i )) {
2285 event.preventDefault()
2286 return true
2287 }
2288 },
2289 // The class of the placeholder (must match placeholder option markup)
2290 placeholderClass: "placeholder",
2291 // Template for the placeholder. Can be any valid jQuery input
2292 // e.g. a string, a DOM element.
2293 // The placeholder must have the class "placeholder"
2294 placeholder: '<li class="placeholder"></li>',
2295 // If true, the position of the placeholder is calculated on every mousemove.
2296 // If false, it is only calculated when the mouse is above a container.
2297 pullPlaceholder: true,
2298 // Specifies serialization of the container group.
2299 // The pair $parent/$children is either container/items or item/subcontainers.
2300 serialize: function ($parent, $children, parentIsContainer) {
2301 var result = $.extend( {}, $parent.data() )
2302
2303 if (parentIsContainer)
2304 return [$children]
2305 else if ($children[0]) {
2306 result.children = $children
2307 }
2308
2309 delete result.subContainers
2310 delete result.sortable
2311
2312 return result
2313 },
2314 // Set tolerance while dragging. Positive values decrease sensitivity,
2315 // negative values increase it.
2316 tolerance: 0
2317 }, // end group defaults
2318 containerGroups = {},
2319 groupCounter = 0,
2320 emptyBox = {
2321 left: 0,
2322 top: 0,
2323 bottom: 0,
2324 right:0
2325 },
2326 eventNames = {
2327 start: "touchstart.sortable mousedown.sortable",
2328 drop: "touchend.sortable touchcancel.sortable mouseup.sortable",
2329 drag: "touchmove.sortable mousemove.sortable",
2330 scroll: "scroll.sortable"
2331 },
2332 subContainerKey = "subContainers"
2333
2334 /*
2335 * a is Array [left, right, top, bottom]
2336 * b is array [left, top]
2337 */
2338 function d(a,b) {
2339 var x = Math.max( 0, a[0] - b[0], b[0] - a[1] ),
2340 y = Math.max( 0, a[2] - b[1], b[1] - a[3] )
2341 return x + y;
2342 }
2343
2344 function setDimensions(array, dimensions, tolerance, useOffset) {
2345 var i = array.length,
2346 offsetMethod = useOffset ? "offset" : "position"
2347 tolerance = tolerance || 0
2348
2349 while (i--) {
2350 var el = array[i].el ? array[i].el : $( array[i] ),
2351 // use fitting method
2352 pos = el[offsetMethod]()
2353 pos.left += parseInt( el.css( 'margin-left' ), 10 )
2354 pos.top += parseInt( el.css( 'margin-top' ),10 )
2355 dimensions[i] = [
2356 pos.left - tolerance,
2357 pos.left + el.outerWidth() + tolerance,
2358 pos.top - tolerance,
2359 pos.top + el.outerHeight() + tolerance
2360 ]
2361 }
2362 }
2363
2364 function getRelativePosition(pointer, element) {
2365 var offset = element.offset()
2366 return {
2367 left: pointer.left - offset.left,
2368 top: pointer.top - offset.top
2369 }
2370 }
2371
2372 function sortByDistanceDesc(dimensions, pointer, lastPointer) {
2373 pointer = [pointer.left, pointer.top]
2374 lastPointer = lastPointer && [lastPointer.left, lastPointer.top]
2375
2376 var dim,
2377 i = dimensions.length,
2378 distances = []
2379
2380 while (i--) {
2381 dim = dimensions[i]
2382 distances[i] = [i,d( dim,pointer ), lastPointer && d( dim, lastPointer )]
2383 }
2384 distances = distances.sort(
2385 function (a,b) {
2386 return b[1] - a[1] || b[2] - a[2] || b[0] - a[0]
2387 }
2388 )
2389
2390 // last entry is the closest
2391 return distances
2392 }
2393
2394 function ContainerGroup(options) {
2395 this.options = $.extend( {}, groupDefaults, options )
2396 this.containers = []
2397
2398 if ( ! this.options.rootGroup) {
2399 this.scrollProxy = $.proxy( this.scroll, this )
2400 this.dragProxy = $.proxy( this.drag, this )
2401 this.dropProxy = $.proxy( this.drop, this )
2402 this.placeholder = $( this.options.placeholder )
2403
2404 if ( ! options.isValidTarget) {
2405 this.options.isValidTarget = undefined
2406 }
2407 }
2408 }
2409
2410 ContainerGroup.get = function (options) {
2411 if ( ! containerGroups[options.group]) {
2412 if (options.group === undefined) {
2413 options.group = groupCounter ++
2414
2415 containerGroups[options.group] = new ContainerGroup( options )
2416 }
2417 }
2418
2419 return containerGroups[options.group]
2420 }
2421
2422 ContainerGroup.prototype = {
2423 dragInit: function (e, itemContainer) {
2424 this.$document = $( itemContainer.el[0].ownerDocument )
2425
2426 // get item to drag
2427 var closestItem = $( e.target ).closest( this.options.itemSelector );
2428 // using the length of this item, prevents the plugin from being started if there is no handle being clicked on.
2429 // this may also be helpful in instantiating multidrag.
2430 if (closestItem.length) {
2431 this.item = closestItem;
2432 this.itemContainer = itemContainer;
2433 if (this.item.is( this.options.exclude ) || ! this.options.onMousedown( this.item, groupDefaults.onMousedown, e )) {
2434 return;
2435 }
2436 this.setPointer( e );
2437 this.toggleListeners( 'on' );
2438 this.setupDelayTimer();
2439 this.dragInitDone = true;
2440 }
2441 },
2442 drag: function (e) {
2443 if ( ! this.dragging) {
2444 if ( ! this.distanceMet( e ) || ! this.delayMet) {
2445 return
2446
2447 this.options.onDragStart( this.item, this.itemContainer, groupDefaults.onDragStart, e )
2448 this.item.before( this.placeholder )
2449 this.dragging = true
2450 }
2451 }
2452
2453 this.setPointer( e )
2454 // place item under the cursor
2455 this.options.onDrag(
2456 this.item,
2457 getRelativePosition( this.pointer, this.item.offsetParent() ),
2458 groupDefaults.onDrag,
2459 e
2460 )
2461
2462 var p = this.getPointer( e ),
2463 box = this.sameResultBox,
2464 t = this.options.tolerance
2465
2466 if ( ! box || box.top - t > p.top || box.bottom + t < p.top || box.left - t > p.left || box.right + t < p.left) {
2467 if ( ! this.searchValidTarget()) {
2468 this.placeholder.detach()
2469 this.lastAppendedItem = undefined
2470 }
2471 }
2472 },
2473 drop: function (e) {
2474 this.toggleListeners( 'off' )
2475
2476 this.dragInitDone = false
2477
2478 if (this.dragging) {
2479 // processing Drop, check if placeholder is detached
2480 if (this.placeholder.closest( "html" )[0]) {
2481 this.placeholder.before( this.item ).detach()
2482 } else {
2483 this.options.onCancel( this.item, this.itemContainer, groupDefaults.onCancel, e )
2484 }
2485 this.options.onDrop( this.item, this.getContainer( this.item ), groupDefaults.onDrop, e )
2486
2487 // cleanup
2488 this.clearDimensions()
2489 this.clearOffsetParent()
2490 this.lastAppendedItem = this.sameResultBox = undefined
2491 this.dragging = false
2492 }
2493 },
2494 searchValidTarget: function (pointer, lastPointer) {
2495 if ( ! pointer) {
2496 pointer = this.relativePointer || this.pointer
2497 lastPointer = this.lastRelativePointer || this.lastPointer
2498 }
2499
2500 var distances = sortByDistanceDesc(
2501 this.getContainerDimensions(),
2502 pointer,
2503 lastPointer
2504 ),
2505 i = distances.length
2506
2507 while (i--) {
2508 var index = distances[i][0],
2509 distance = distances[i][1]
2510
2511 if ( ! distance || this.options.pullPlaceholder) {
2512 var container = this.containers[index]
2513 if ( ! container.disabled) {
2514 if ( ! this.$getOffsetParent()) {
2515 var offsetParent = container.getItemOffsetParent()
2516 pointer = getRelativePosition( pointer, offsetParent )
2517 lastPointer = getRelativePosition( lastPointer, offsetParent )
2518 }
2519 if (container.searchValidTarget( pointer, lastPointer )) {
2520 return true
2521 }
2522 }
2523 }
2524 }
2525 if (this.sameResultBox) {
2526 this.sameResultBox = undefined
2527 }
2528 },
2529 movePlaceholder: function (container, item, method, sameResultBox) {
2530 var lastAppendedItem = this.lastAppendedItem
2531 if ( ! sameResultBox && lastAppendedItem && lastAppendedItem[0] === item[0]) {
2532 return;
2533 }
2534
2535 item[method]( this.placeholder )
2536 this.lastAppendedItem = item
2537 this.sameResultBox = sameResultBox
2538 this.options.afterMove( this.placeholder, container, item )
2539 },
2540 getContainerDimensions: function () {
2541 if ( ! this.containerDimensions) {
2542 setDimensions( this.containers, this.containerDimensions = [], this.options.tolerance, ! this.$getOffsetParent() )
2543 return this.containerDimensions
2544 }
2545 },
2546 getContainer: function (element) {
2547 return element.closest( this.options.containerSelector ).data( pluginName )
2548 },
2549 $getOffsetParent: function () {
2550 if (this.offsetParent === undefined) {
2551 var i = this.containers.length - 1,
2552 offsetParent = this.containers[i].getItemOffsetParent()
2553
2554 if ( ! this.options.rootGroup) {
2555 while (i--) {
2556 if (offsetParent[0] != this.containers[i].getItemOffsetParent()[0]) {
2557 // If every container has the same offset parent,
2558 // use position() which is relative to this parent,
2559 // otherwise use offset()
2560 // compare #setDimensions
2561 offsetParent = false
2562 break;
2563 }
2564 }
2565 }
2566
2567 this.offsetParent = offsetParent
2568 }
2569 return this.offsetParent
2570 },
2571 setPointer: function (e) {
2572 var pointer = this.getPointer( e )
2573
2574 if (this.$getOffsetParent()) {
2575 var relativePointer = getRelativePosition( pointer, this.$getOffsetParent() )
2576 this.lastRelativePointer = this.relativePointer
2577 this.relativePointer = relativePointer
2578 }
2579
2580 this.lastPointer = this.pointer
2581 this.pointer = pointer
2582 },
2583 distanceMet: function (e) {
2584 var currentPointer = this.getPointer( e )
2585 return (Math.max(
2586 Math.abs( this.pointer.left - currentPointer.left ),
2587 Math.abs( this.pointer.top - currentPointer.top )
2588 ) >= this.options.distance)
2589 },
2590 getPointer: function(e) {
2591 var o = e.originalEvent || e.originalEvent.touches && e.originalEvent.touches[0]
2592 return {
2593 left: e.pageX || o.pageX,
2594 top: e.pageY || o.pageY
2595 }
2596 },
2597 setupDelayTimer: function () {
2598 var that = this
2599 this.delayMet = ! this.options.delay
2600
2601 // init delay timer if needed
2602 if ( ! this.delayMet) {
2603 clearTimeout( this._mouseDelayTimer );
2604 this._mouseDelayTimer = setTimeout(
2605 function() {
2606 that.delayMet = true
2607 },
2608 this.options.delay
2609 )
2610 }
2611 },
2612 scroll: function (e) {
2613 this.clearDimensions()
2614 this.clearOffsetParent() // TODO is this needed?
2615 },
2616 toggleListeners: function (method) {
2617 var that = this,
2618 events = ['drag','drop','scroll']
2619
2620 $.each(
2621 events,
2622 function (i,event) {
2623 that.$document[method]( eventNames[event], that[event + 'Proxy'] )
2624 }
2625 )
2626 },
2627 clearOffsetParent: function () {
2628 this.offsetParent = undefined
2629 },
2630 // Recursively clear container and item dimensions
2631 clearDimensions: function () {
2632 this.traverse(
2633 function(object){
2634 object._clearDimensions()
2635 }
2636 )
2637 },
2638 traverse: function(callback) {
2639 callback( this )
2640 var i = this.containers.length
2641 while (i--) {
2642 this.containers[i].traverse( callback )
2643 }
2644 },
2645 _clearDimensions: function(){
2646 this.containerDimensions = undefined
2647 },
2648 _destroy: function () {
2649 containerGroups[this.options.group] = undefined
2650 }
2651 }
2652
2653 function Container(element, options) {
2654 this.el = element
2655 this.options = $.extend( {}, containerDefaults, options )
2656
2657 this.group = ContainerGroup.get( this.options )
2658 this.rootGroup = this.options.rootGroup || this.group
2659 this.handle = this.rootGroup.options.handle || this.rootGroup.options.itemSelector
2660
2661 var itemPath = this.rootGroup.options.itemPath
2662 this.target = itemPath ? this.el.find( itemPath ) : this.el
2663
2664 this.target.on( eventNames.start, this.handle, $.proxy( this.dragInit, this ) )
2665
2666 if (this.options.drop) {
2667 this.group.containers.push( this )
2668 }
2669 }
2670
2671 Container.prototype = {
2672 dragInit: function (e) {
2673 var rootGroup = this.rootGroup
2674
2675 if ( ! this.disabled &&
2676 ! rootGroup.dragInitDone &&
2677 this.options.drag &&
2678 this.isValidDrag( e )) {
2679 rootGroup.dragInit( e, this )
2680 }
2681 },
2682 isValidDrag: function(e) {
2683 return e.which == 1 ||
2684 e.type == "touchstart" && e.originalEvent.touches.length == 1
2685 },
2686 searchValidTarget: function (pointer, lastPointer) {
2687 var distances = sortByDistanceDesc(
2688 this.getItemDimensions(),
2689 pointer,
2690 lastPointer
2691 ),
2692 i = distances.length,
2693 rootGroup = this.rootGroup,
2694 validTarget = ! rootGroup.options.isValidTarget ||
2695 rootGroup.options.isValidTarget( rootGroup.item, this )
2696
2697 if ( ! i && validTarget) {
2698 rootGroup.movePlaceholder( this, this.target, "append" )
2699 return true
2700 } else {
2701 while (i--) {
2702 var index = distances[i][0],
2703 distance = distances[i][1]
2704 if ( ! distance && this.hasChildGroup( index )) {
2705 var found = this.getContainerGroup( index ).searchValidTarget( pointer, lastPointer )
2706 if (found) {
2707 return true
2708 } } else if (validTarget) {
2709 this.movePlaceholder( index, pointer )
2710 return true
2711 }
2712 }
2713 }
2714 },
2715 movePlaceholder: function (index, pointer) {
2716 var item = $( this.items[index] ),
2717 dim = this.itemDimensions[index],
2718 method = "after",
2719 width = item.outerWidth(),
2720 height = item.outerHeight(),
2721 offset = item.offset(),
2722 sameResultBox = {
2723 left: offset.left,
2724 right: offset.left + width,
2725 top: offset.top,
2726 bottom: offset.top + height
2727 }
2728 if (this.options.vertical) {
2729 var yCenter = (dim[2] + dim[3]) / 2,
2730 inUpperHalf = pointer.top <= yCenter
2731 if (inUpperHalf) {
2732 method = "before"
2733 sameResultBox.bottom -= height / 2
2734 } else {
2735 sameResultBox.top += height / 2
2736 } } else {
2737 var xCenter = (dim[0] + dim[1]) / 2,
2738 inLeftHalf = pointer.left <= xCenter
2739 if (inLeftHalf) {
2740 method = "before"
2741 sameResultBox.right -= width / 2
2742 } else {
2743 sameResultBox.left += width / 2
2744 }
2745 }
2746 if (this.hasChildGroup( index )) {
2747 sameResultBox = emptyBox
2748 this.rootGroup.movePlaceholder( this, item, method, sameResultBox )
2749 }
2750 },
2751 getItemDimensions: function () {
2752 if ( ! this.itemDimensions) {
2753 this.items = this.$getChildren( this.el, "item" ).filter(
2754 ":not(." + this.group.options.placeholderClass + ", ." + this.group.options.draggedClass + ")"
2755 ).get()
2756 setDimensions( this.items, this.itemDimensions = [], this.options.tolerance )
2757 }
2758 return this.itemDimensions
2759 },
2760 getItemOffsetParent: function () {
2761 var offsetParent,
2762 el = this.el
2763 // Since el might be empty we have to check el itself and
2764 // can not do something like el.children().first().offsetParent()
2765 if (el.css( "position" ) === "relative" || el.css( "position" ) === "absolute" || el.css( "position" ) === "fixed") {
2766 offsetParent = el
2767 else {
2768 offsetParent = el.offsetParent()
2769 return offsetParent
2770 }
2771 }
2772 },
2773 hasChildGroup: function (index) {
2774 return this.options.nested && this.getContainerGroup( index )
2775 },
2776 getContainerGroup: function (index) {
2777 var childGroup = $.data( this.items[index], subContainerKey )
2778 if ( childGroup === undefined) {
2779 var childContainers = this.$getChildren( this.items[index], "container" )
2780 childGroup = false
2781
2782 if (childContainers[0]) {
2783 var options = $.extend(
2784 {},
2785 this.options,
2786 {
2787 rootGroup: this.rootGroup,
2788 group: groupCounter ++
2789 }
2790 )
2791 childGroup = childContainers[pluginName]( options ).data( pluginName ).group
2792 }
2793 $.data( this.items[index], subContainerKey, childGroup )
2794 }
2795 return childGroup
2796 },
2797 $getChildren: function (parent, type) {
2798 var options = this.rootGroup.options,
2799 path = options[type + "Path"],
2800 selector = options[type + "Selector"]
2801
2802 parent = $( parent )
2803 if (path) {
2804 parent = parent.find( path )
2805
2806 return parent.children( selector )
2807 }
2808 },
2809 _serialize: function (parent, isContainer) {
2810 var that = this,
2811 childType = isContainer ? "item" : "container",
2812
2813 children = this.$getChildren( parent, childType ).not( this.options.exclude ).map(
2814 function () {
2815 return that._serialize( $( this ), ! isContainer )
2816 }
2817 ).get()
2818
2819 return this.rootGroup.options.serialize( parent, children, isContainer )
2820 },
2821 traverse: function(callback) {
2822 $.each(
2823 this.items || [],
2824 function(item){
2825 var group = $.data( this, subContainerKey )
2826 if (group) {
2827 group.traverse( callback )
2828 }
2829 }
2830 );
2831
2832 callback( this )
2833 },
2834 _clearDimensions: function () {
2835 this.itemDimensions = undefined
2836 },
2837 _destroy: function() {
2838 var that = this;
2839
2840 this.target.off( eventNames.start, this.handle );
2841 this.el.removeData( pluginName )
2842
2843 if (this.options.drop) {
2844 this.group.containers = $.grep(
2845 this.group.containers,
2846 function(val){
2847 return val != that
2848 }
2849 )
2850
2851 $.each(
2852 this.items || [],
2853 function(){
2854 $.removeData( this, subContainerKey )
2855 }
2856 )
2857 }
2858 }
2859 }
2860
2861 var API = {
2862 enable: function() {
2863 this.traverse(
2864 function(object){
2865 object.disabled = false
2866 }
2867 )
2868 },
2869 disable: function (){
2870 this.traverse(
2871 function(object){
2872 object.disabled = true
2873 }
2874 )
2875 },
2876 serialize: function () {
2877 return this._serialize( this.el, true )
2878 },
2879 refresh: function() {
2880 this.traverse(
2881 function(object){
2882 object._clearDimensions()
2883 }
2884 )
2885 },
2886 destroy: function () {
2887 this.traverse(
2888 function(object){
2889 object._destroy();
2890 }
2891 )
2892 }
2893 }
2894
2895 $.extend( Container.prototype, API )
2896
2897 /**
2898 * jQuery API
2899 *
2900 * Parameters are
2901 * either options on init
2902 * or a method name followed by arguments to pass to the method
2903 */
2904 $.fn[pluginName] = function(methodOrOptions) {
2905 var args = Array.prototype.slice.call( arguments, 1 )
2906
2907 return this.map(
2908 function(){
2909 var $t = $( this ),
2910 object = $t.data( pluginName )
2911
2912 if (object && API[methodOrOptions])
2913 return API[methodOrOptions].apply( object, args ) || this
2914 else if ( ! object && (methodOrOptions === undefined ||
2915 typeof methodOrOptions === "object")) {
2916 $t.data( pluginName, new Container( $t, methodOrOptions ) )
2917
2918 return this
2919 }
2920 }
2921 );
2922 };
2923
2924 }(jQuery, window, 'tokenize2sortable');
2925