1+ /**
2+ * jQuery Editable Select
3+ * Indri Muska <indrimuska@gmail.com>
4+ *
5+ * Source on GitHub @ https://github.com/indrimuska/jquery-editable-select
6+ */
7+
8+ ( function ( $ ) {
9+ // jQuery Editable Select
10+ EditableSelect = function ( select , options ) {
11+ var that = this ;
12+
13+ this . options = options ;
14+ this . $select = $ ( select ) ;
15+ this . $input = $ ( '<input type="text" autocomplete="off">' ) ;
16+ this . $list = $ ( '<ul class="es-list">' ) ;
17+ this . utility = new EditableSelectUtility ( this ) ;
18+
19+ if ( [ 'focus' , 'manual' ] . indexOf ( this . options . trigger ) < 0 ) this . options . trigger = 'focus' ;
20+ if ( [ 'default' , 'fade' , 'slide' ] . indexOf ( this . options . effects ) < 0 ) this . options . effects = 'default' ;
21+ if ( isNaN ( this . options . duration ) && [ 'fast' , 'slow' ] . indexOf ( this . options . duration ) < 0 ) this . options . duration = 'fast' ;
22+
23+ // create text input
24+ this . $select . replaceWith ( this . $input ) ;
25+ this . $list . appendTo ( this . options . appendTo || this . $input . parent ( ) ) ;
26+
27+ // initalization
28+ this . utility . initialize ( ) ;
29+ this . utility . initializeList ( ) ;
30+ this . utility . initializeInput ( ) ;
31+ this . utility . trigger ( 'created' ) ;
32+ }
33+ EditableSelect . DEFAULTS = { filter : true , effects : 'default' , duration : 'fast' , trigger : 'focus' } ;
34+ EditableSelect . prototype . filter = function ( ) {
35+ var hiddens = 0 ;
36+ var search = this . $input . val ( ) . toLowerCase ( ) . trim ( ) ;
37+
38+ this . $list . find ( 'li' ) . addClass ( 'es-visible' ) . show ( ) ;
39+ if ( this . options . filter ) {
40+ hiddens = this . $list . find ( 'li' ) . filter ( function ( i , li ) { return $ ( li ) . text ( ) . toLowerCase ( ) . indexOf ( search ) < 0 ; } ) . hide ( ) . removeClass ( 'es-visible' ) . length ;
41+ if ( this . $list . find ( 'li' ) . length == hiddens ) this . hide ( ) ;
42+ }
43+ } ;
44+ EditableSelect . prototype . show = function ( ) {
45+ this . $list . css ( {
46+ top : this . $input . position ( ) . top + this . $input . outerHeight ( ) - 1 ,
47+ left : this . $input . position ( ) . left ,
48+ width : this . $input . outerWidth ( )
49+ } ) ;
50+
51+ if ( ! this . $list . is ( ':visible' ) && this . $list . find ( 'li.es-visible' ) . length > 0 ) {
52+ var fns = { default : 'show' , fade : 'fadeIn' , slide : 'slideDown' } ;
53+ var fn = fns [ this . options . effects ] ;
54+
55+ this . utility . trigger ( 'show' ) ;
56+ this . $input . addClass ( 'open' ) ;
57+ this . $list [ fn ] ( this . options . duration , $ . proxy ( this . utility . trigger , this . utility , 'shown' ) ) ;
58+ }
59+ } ;
60+ EditableSelect . prototype . hide = function ( ) {
61+ var fns = { default : 'hide' , fade : 'fadeOut' , slide : 'slideUp' } ;
62+ var fn = fns [ this . options . effects ] ;
63+
64+ this . utility . trigger ( 'hide' ) ;
65+ this . $input . removeClass ( 'open' ) ;
66+ this . $list [ fn ] ( this . options . duration , $ . proxy ( this . utility . trigger , this . utility , 'hidden' ) ) ;
67+ } ;
68+ EditableSelect . prototype . select = function ( $li ) {
69+ if ( ! this . $list . has ( $li ) || ! $li . is ( 'li.es-visible:not([disabled])' ) ) return ;
70+ this . $input . val ( $li . text ( ) ) ;
71+ if ( this . options . filter ) this . hide ( ) ;
72+ this . filter ( ) ;
73+ this . utility . trigger ( 'select' , $li ) ;
74+ } ;
75+ EditableSelect . prototype . add = function ( text , index , attrs , data ) {
76+ var $li = $ ( '<li>' ) . html ( text ) ;
77+ var $option = $ ( '<option>' ) . text ( text ) ;
78+ var last = this . $list . find ( 'li' ) . length ;
79+
80+ if ( isNaN ( index ) ) index = last ;
81+ else index = Math . min ( Math . max ( 0 , index ) , last ) ;
82+ if ( index == 0 ) {
83+ this . $list . prepend ( $li ) ;
84+ this . $select . prepend ( $option ) ;
85+ } else {
86+ this . $list . find ( 'li' ) . eq ( index - 1 ) . after ( $li ) ;
87+ this . $select . find ( 'option' ) . eq ( index - 1 ) . after ( $option ) ;
88+ }
89+ this . utility . setAttributes ( $li , attrs , data ) ;
90+ this . utility . setAttributes ( $option , attrs , data ) ;
91+ this . filter ( ) ;
92+ } ;
93+ EditableSelect . prototype . remove = function ( index ) {
94+ var last = this . $list . find ( 'li' ) . length ;
95+
96+ if ( isNaN ( index ) ) index = last ;
97+ else index = Math . min ( Math . max ( 0 , index ) , last - 1 ) ;
98+ this . $list . find ( 'li' ) . eq ( index ) . remove ( ) ;
99+ this . $select . find ( 'option' ) . eq ( index ) . remove ( ) ;
100+ this . filter ( ) ;
101+ } ;
102+ EditableSelect . prototype . clear = function ( ) {
103+ this . $list . find ( 'li' ) . remove ( ) ;
104+ this . $select . find ( 'option' ) . remove ( ) ;
105+ this . filter ( ) ;
106+ } ;
107+ EditableSelect . prototype . destroy = function ( ) {
108+ this . $list . off ( 'mousemove mousedown mouseup' ) ;
109+ this . $input . off ( 'focus blur input keydown' ) ;
110+ this . $input . replaceWith ( this . $select ) ;
111+ this . $list . remove ( ) ;
112+ this . $select . removeData ( 'editable-select' ) ;
113+ } ;
114+
115+ // Utility
116+ EditableSelectUtility = function ( es ) {
117+ this . es = es ;
118+ }
119+ EditableSelectUtility . prototype . initialize = function ( ) {
120+ var that = this ;
121+ that . setAttributes ( that . es . $input , that . es . $select [ 0 ] . attributes , that . es . $select . data ( ) ) ;
122+ that . es . $input . addClass ( 'es-input' ) . data ( 'editable-select' , that . es ) ;
123+ that . es . $select . find ( 'option' ) . each ( function ( i , option ) {
124+ var $option = $ ( option ) . remove ( ) ;
125+ that . es . add ( $option . text ( ) , i , option . attributes , $option . data ( ) ) ;
126+ if ( $option . attr ( 'selected' ) ) that . es . $input . val ( $option . text ( ) ) ;
127+ } ) ;
128+ that . es . filter ( ) ;
129+ } ;
130+ EditableSelectUtility . prototype . initializeList = function ( ) {
131+ var that = this ;
132+ that . es . $list
133+ . on ( 'mousemove' , 'li:not([disabled])' , function ( ) {
134+ that . es . $list . find ( '.selected' ) . removeClass ( 'selected' ) ;
135+ $ ( this ) . addClass ( 'selected' ) ;
136+ } )
137+ . on ( 'mousedown' , 'li' , function ( e ) {
138+ if ( $ ( this ) . is ( '[disabled]' ) ) e . preventDefault ( ) ;
139+ else that . es . select ( $ ( this ) ) ;
140+ } )
141+ . on ( 'mouseup' , function ( ) {
142+ that . es . $list . find ( 'li.selected' ) . removeClass ( 'selected' ) ;
143+ } ) ;
144+ } ;
145+ EditableSelectUtility . prototype . initializeInput = function ( ) {
146+ var that = this ;
147+ switch ( this . es . options . trigger ) {
148+ default :
149+ case 'focus' :
150+ that . es . $input
151+ . on ( 'focus' , $ . proxy ( that . es . show , that . es ) )
152+ . on ( 'blur' , $ . proxy ( that . es . hide , that . es ) ) ;
153+ break ;
154+ case 'manual' :
155+ break ;
156+ }
157+ that . es . $input . on ( 'input keydown' , function ( e ) {
158+ switch ( e . keyCode ) {
159+ case 38 : // Up
160+ var visibles = that . es . $list . find ( 'li.es-visible:not([disabled])' ) ;
161+ var selectedIndex = visibles . index ( visibles . filter ( 'li.selected' ) ) ;
162+ that . highlight ( selectedIndex - 1 ) ;
163+ e . preventDefault ( ) ;
164+ break ;
165+ case 40 : // Down
166+ var visibles = that . es . $list . find ( 'li.es-visible:not([disabled])' ) ;
167+ var selectedIndex = visibles . index ( visibles . filter ( 'li.selected' ) ) ;
168+ that . highlight ( selectedIndex + 1 ) ;
169+ e . preventDefault ( ) ;
170+ break ;
171+ case 13 : // Enter
172+ if ( that . es . $list . is ( ':visible' ) ) {
173+ that . es . select ( that . es . $list . find ( 'li.selected' ) ) ;
174+ e . preventDefault ( ) ;
175+ }
176+ break ;
177+ case 9 : // Tab
178+ case 27 : // Esc
179+ that . es . hide ( ) ;
180+ break ;
181+ default :
182+ that . es . filter ( ) ;
183+ that . highlight ( 0 ) ;
184+ break ;
185+ }
186+ } ) ;
187+ } ;
188+ EditableSelectUtility . prototype . highlight = function ( index ) {
189+ var that = this ;
190+ that . es . show ( ) ;
191+ setTimeout ( function ( ) {
192+ var visibles = that . es . $list . find ( 'li.es-visible' ) ;
193+ var oldSelected = that . es . $list . find ( 'li.selected' ) . removeClass ( 'selected' ) ;
194+ var oldSelectedIndex = visibles . index ( oldSelected ) ;
195+
196+ if ( visibles . length > 0 ) {
197+ var selectedIndex = ( visibles . length + index ) % visibles . length ;
198+ var selected = visibles . eq ( selectedIndex ) ;
199+ var top = selected . position ( ) . top ;
200+
201+ selected . addClass ( 'selected' ) ;
202+ if ( selectedIndex < oldSelectedIndex && top < 0 )
203+ that . es . $list . scrollTop ( that . es . $list . scrollTop ( ) + top ) ;
204+ if ( selectedIndex > oldSelectedIndex && top + selected . outerHeight ( ) > that . es . $list . outerHeight ( ) )
205+ that . es . $list . scrollTop ( that . es . $list . scrollTop ( ) + selected . outerHeight ( ) + 2 * ( top - that . es . $list . outerHeight ( ) ) ) ;
206+ }
207+ } ) ;
208+ } ;
209+ EditableSelectUtility . prototype . setAttributes = function ( $element , attrs , data ) {
210+ $ . each ( attrs || { } , function ( i , attr ) { $element . attr ( attr . name , attr . value ) ; } ) ;
211+ $element . data ( data ) ;
212+ } ;
213+ EditableSelectUtility . prototype . trigger = function ( event ) {
214+ var params = Array . prototype . slice . call ( arguments , 1 ) ;
215+ var args = [ event + '.editable-select' ] ;
216+ args . push ( params ) ;
217+ this . es . $select . trigger . apply ( this . es . $select , args ) ;
218+ this . es . $input . trigger . apply ( this . es . $input , args ) ;
219+ } ;
220+
221+ // Plugin
222+ Plugin = function ( option ) {
223+ var args = Array . prototype . slice . call ( arguments , 1 ) ;
224+ return this . each ( function ( ) {
225+ var $this = $ ( this ) ;
226+ var data = $this . data ( 'editable-select' ) ;
227+ var options = $ . extend ( { } , EditableSelect . DEFAULTS , $this . data ( ) , typeof option == 'object' && option ) ;
228+
229+ if ( ! data ) data = new EditableSelect ( this , options ) ;
230+ if ( typeof option == 'string' ) data [ option ] . apply ( data , args ) ;
231+ } ) ;
232+ }
233+ $ . fn . editableSelect = Plugin ;
234+ $ . fn . editableSelect . Constructor = EditableSelect ;
235+
236+ } ) ( jQuery ) ;
0 commit comments