44
44
var $win = $ ( window ) ;
45
45
46
46
var pluginName = 'selectric' ;
47
- var classList = 'Input Items Open Disabled TempShow HideSelect Wrapper Hover Responsive Above Scroll Group GroupLabel' ;
47
+ var classList = 'Input Items Open Disabled TempShow HideSelect Wrapper Focus Hover Responsive Above Scroll Group GroupLabel' ;
48
48
var bindSufix = '.sl' ;
49
49
50
50
var chars = [ 'a' , 'e' , 'i' , 'o' , 'u' , 'n' , 'c' , 'y' ] ;
101
101
return / a n d r o i d | i p ( h o n e | o d | a d ) / i. test ( navigator . userAgent ) ;
102
102
} ,
103
103
104
+ /**
105
+ * Escape especial characters in string (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
106
+ *
107
+ * @param {string } str - The string to be escaped
108
+ * @return {string } The string with the special characters escaped
109
+ */
110
+ escapeRegExp : function ( str ) {
111
+ return str . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ; // $& means the whole matched string
112
+ } ,
113
+
104
114
/**
105
115
* Replace diacritics
106
116
*
107
- * @param {string } str - The string to replace the diacritics.
108
- * @return {string } The string with diacritics replaced with ascii characters.
117
+ * @param {string } str - The string to replace the diacritics
118
+ * @return {string } The string with diacritics replaced with ascii characters
109
119
*/
110
120
replaceDiacritics : function ( str ) {
111
121
var k = diacritics . length ;
483
493
484
494
_this . elements . input
485
495
. prop ( { tabindex : _this . originalTabindex , disabled : false } )
486
- . on ( 'keypress' + bindSufix , _this . handleSystemKeys )
487
- . on ( 'keydown' + bindSufix , function ( e ) {
488
- _this . handleSystemKeys ( e ) ;
496
+ . on ( 'keydown' + bindSufix , $ . proxy ( _this . handleKeys , _this ) )
497
+ . on ( 'focusin' + bindSufix , function ( e ) {
498
+ _this . elements . outerWrapper . addClass ( _this . classes . focus ) ;
499
+
500
+ // Prevent the flicker when focusing out and back again in the browser window
501
+ _this . elements . input . one ( 'blur' , function ( ) {
502
+ _this . elements . input . blur ( ) ;
503
+ } ) ;
504
+
505
+ if ( _this . options . openOnFocus && ! _this . state . opened ) {
506
+ _this . open ( e ) ;
507
+ }
508
+ } )
509
+ . on ( 'focusout' + bindSufix , function ( ) {
510
+ _this . elements . outerWrapper . removeClass ( _this . classes . focus ) ;
511
+ } )
512
+ . on ( 'input propertychange' , function ( ) {
513
+ var val = _this . elements . input . val ( ) ;
489
514
490
515
// Clear search
491
516
clearTimeout ( _this . resetStr ) ;
492
517
_this . resetStr = setTimeout ( function ( ) {
493
518
_this . elements . input . val ( '' ) ;
494
519
} , _this . options . keySearchTimeout ) ;
495
520
496
- var key = e . keyCode || e . which ;
497
-
498
- // If it's a directional key
499
- // 37 => Left
500
- // 38 => Up
501
- // 39 => Right
502
- // 40 => Down
503
- if ( key > 36 && key < 41 ) {
504
- if ( ! _this . options . allowWrap ) {
505
- if ( ( key < 39 && _this . state . selectedIdx === 0 ) || ( key > 38 && ( _this . state . selectedIdx + 1 ) === _this . items . length ) ) {
506
- return ;
507
- }
508
- }
509
-
510
- _this . select ( _this . utils [ ( key < 39 ? 'previous' : 'next' ) + 'EnabledItem' ] ( _this . items , _this . state . selectedIdx ) ) ;
511
- }
512
- } )
513
- . on ( 'focusin' + bindSufix , function ( e ) {
514
- _this . state . opened || _this . open ( e ) ;
515
- } )
516
- . on ( 'oninput' in _this . elements . input [ 0 ] ? 'input' : 'keyup' , function ( ) {
517
- if ( _this . elements . input . val ( ) . length ) {
521
+ if ( val . length ) {
518
522
// Search in select options
519
523
$ . each ( _this . items , function ( i , elm ) {
520
- if ( RegExp ( '^' + _this . elements . input . val ( ) , 'i' ) . test ( elm . slug ) && ! elm . disabled ) {
524
+ if ( RegExp ( '^' + _this . utils . escapeRegExp ( val ) , 'i' ) . test ( elm . slug ) && ! elm . disabled ) {
521
525
_this . select ( i ) ;
522
526
return false ;
523
527
}
543
547
} ,
544
548
545
549
/**
546
- * Behavior when system keys is pressed
550
+ * Behavior when keyboard keys is pressed
547
551
*
548
552
* @param {object } e - Event object
549
553
*/
550
- handleSystemKeys : function ( e ) {
554
+ handleKeys : function ( e ) {
551
555
var _this = this ;
552
556
var key = e . keyCode || e . which ;
553
-
554
- if ( key == 13 ) {
557
+ var keys = _this . options . keys ;
558
+
559
+ var isPrev = $ . inArray ( key , keys . previous ) > - 1 ;
560
+ var isNext = $ . inArray ( key , keys . next ) > - 1 ;
561
+ var isSelect = $ . inArray ( key , keys . select ) > - 1 ;
562
+ var isOpen = $ . inArray ( key , keys . open ) > - 1 ;
563
+ var idx = _this . state . selectedIdx ;
564
+ var isFirstOrLastItem = ( isPrev && idx === 0 ) || ( isNext && ( idx + 1 ) === _this . items . length ) ;
565
+ var goToItem = 0 ;
566
+
567
+ // Enter / Space
568
+ if ( key === 13 || key === 32 ) {
555
569
e . preventDefault ( ) ;
556
570
}
557
571
572
+ // If it's a directional key
573
+ if ( isPrev || isNext ) {
574
+ if ( ! _this . options . allowWrap && isFirstOrLastItem ) {
575
+ return ;
576
+ }
577
+
578
+ if ( isPrev ) {
579
+ goToItem = _this . utils . previousEnabledItem ( _this . items , idx ) ;
580
+ }
581
+
582
+ if ( isNext ) {
583
+ goToItem = _this . utils . nextEnabledItem ( _this . items , idx ) ;
584
+ }
585
+
586
+ _this . select ( goToItem ) ;
587
+ }
588
+
558
589
// Tab / Enter / ESC
559
- if ( / ^ ( 9 | 1 3 | 2 7 ) $ / . test ( key ) ) {
560
- e . stopPropagation ( ) ;
561
- _this . select ( _this . state . selectedIdx , true ) ;
590
+ if ( isSelect && _this . state . opened ) {
591
+ _this . select ( idx , true ) ;
592
+ return ;
593
+ }
594
+
595
+ // Space / Enter / Left / Up / Right / Down
596
+ if ( isOpen && ! _this . state . opened ) {
597
+ _this . open ( ) ;
562
598
}
563
599
} ,
564
600
845
881
* @type {object }
846
882
*/
847
883
$ . fn [ pluginName ] . defaults = {
848
- onChange : function ( elm ) { $ ( elm ) . change ( ) ; } ,
849
- maxHeight : 300 ,
850
- keySearchTimeout : 500 ,
851
- arrowButtonMarkup : '<b class="button">▾</b>' ,
852
- disableOnMobile : true ,
853
- openOnHover : false ,
854
- hoverIntentTimeout : 500 ,
855
- expandToItemText : false ,
856
- responsive : false ,
857
- preventWindowScroll : true ,
858
- inheritOriginalWidth : false ,
859
- allowWrap : true ,
860
- customClass : {
884
+ onChange : function ( elm ) { $ ( elm ) . change ( ) ; } ,
885
+ maxHeight : 300 ,
886
+ keySearchTimeout : 500 ,
887
+ arrowButtonMarkup : '<b class="button">▾</b>' ,
888
+ disableOnMobile : true ,
889
+ openOnFocus : true ,
890
+ openOnHover : false ,
891
+ hoverIntentTimeout : 500 ,
892
+ expandToItemText : false ,
893
+ responsive : false ,
894
+ preventWindowScroll : true ,
895
+ inheritOriginalWidth : false ,
896
+ allowWrap : true ,
897
+ optionsItemBuilder : '{text}' , // function(itemData, element, index)
898
+ labelBuilder : '{text}' , // function(currItem)
899
+ keys : {
900
+ previous : [ 37 , 38 ] , // Left / Up
901
+ next : [ 39 , 40 ] , // Right / Down
902
+ select : [ 9 , 13 , 27 ] , // Tab / Enter / Escape
903
+ open : [ 13 , 32 , 37 , 38 , 39 , 40 ] , // Enter / Space / Left / Up / Right / Down
904
+ close : [ 9 , 27 ] // Tab / Escape
905
+ } ,
906
+ customClass : {
861
907
prefix : pluginName ,
862
908
camelCase : false
863
- } ,
864
- optionsItemBuilder : '{text}' , // function(itemData, element, index)
865
- labelBuilder : '{text}' // function(currItem)
909
+ }
866
910
} ;
867
911
} ) ) ;
0 commit comments