4444 var $win = $ ( window ) ;
4545
4646 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' ;
4848 var bindSufix = '.sl' ;
4949
5050 var chars = [ 'a' , 'e' , 'i' , 'o' , 'u' , 'n' , 'c' , 'y' ] ;
101101 return / a n d r o i d | i p ( h o n e | o d | a d ) / i. test ( navigator . userAgent ) ;
102102 } ,
103103
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+
104114 /**
105115 * Replace diacritics
106116 *
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
109119 */
110120 replaceDiacritics : function ( str ) {
111121 var k = diacritics . length ;
483493
484494 _this . elements . input
485495 . 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 ( ) ;
489514
490515 // Clear search
491516 clearTimeout ( _this . resetStr ) ;
492517 _this . resetStr = setTimeout ( function ( ) {
493518 _this . elements . input . val ( '' ) ;
494519 } , _this . options . keySearchTimeout ) ;
495520
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 ) {
518522 // Search in select options
519523 $ . 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 ) {
521525 _this . select ( i ) ;
522526 return false ;
523527 }
543547 } ,
544548
545549 /**
546- * Behavior when system keys is pressed
550+ * Behavior when keyboard keys is pressed
547551 *
548552 * @param {object } e - Event object
549553 */
550- handleSystemKeys : function ( e ) {
554+ handleKeys : function ( e ) {
551555 var _this = this ;
552556 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 ) {
555569 e . preventDefault ( ) ;
556570 }
557571
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+
558589 // 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 ( ) ;
562598 }
563599 } ,
564600
845881 * @type {object }
846882 */
847883 $ . 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 : {
861907 prefix : pluginName ,
862908 camelCase : false
863- } ,
864- optionsItemBuilder : '{text}' , // function(itemData, element, index)
865- labelBuilder : '{text}' // function(currItem)
909+ }
866910 } ;
867911} ) ) ;
0 commit comments