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