chosen-rails 1.5.2 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +14 -10
- data/Rakefile +1 -1
- data/chosen-rails.gemspec +1 -1
- data/lib/chosen-rails/rspec.rb +7 -6
- data/lib/chosen-rails/source_file.rb +6 -8
- data/lib/chosen-rails/version.rb +2 -2
- data/lib/chosen-rails.rb +1 -1
- data/vendor/assets/javascripts/chosen.jquery.coffee +118 -118
- data/vendor/assets/javascripts/chosen.proto.coffee +113 -97
- data/vendor/assets/javascripts/lib/abstract-chosen.coffee +116 -35
- data/vendor/assets/javascripts/lib/select-parser.coffee +1 -16
- data/vendor/assets/stylesheets/chosen-base.scss +18 -10
- data/vendor/assets/stylesheets/chosen.scss +375 -9
- metadata +9 -11
- data/vendor/assets/stylesheets/chosen-compass.scss +0 -63
@@ -2,15 +2,6 @@ class @Chosen extends AbstractChosen
|
|
2
2
|
|
3
3
|
setup: ->
|
4
4
|
@current_selectedIndex = @form_field.selectedIndex
|
5
|
-
@is_rtl = @form_field.hasClassName "chosen-rtl"
|
6
|
-
|
7
|
-
set_default_values: ->
|
8
|
-
super()
|
9
|
-
|
10
|
-
# HTML Templates
|
11
|
-
@single_temp = new Template('<a class="chosen-single chosen-default"><span>#{default}</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>')
|
12
|
-
@multi_temp = new Template('<ul class="chosen-choices"><li class="search-field"><input type="text" value="#{default}" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>')
|
13
|
-
@no_results_temp = new Template('<li class="no-results">' + @results_none_found + ' "<span>#{terms}</span>"</li>')
|
14
5
|
|
15
6
|
set_up_html: ->
|
16
7
|
container_classes = ["chosen-container"]
|
@@ -20,12 +11,19 @@ class @Chosen extends AbstractChosen
|
|
20
11
|
|
21
12
|
container_props =
|
22
13
|
'class': container_classes.join ' '
|
23
|
-
'style': "width: #{this.container_width()};"
|
24
14
|
'title': @form_field.title
|
25
15
|
|
26
16
|
container_props.id = @form_field.id.replace(/[^\w]/g, '_') + "_chosen" if @form_field.id.length
|
27
17
|
|
28
|
-
@container =
|
18
|
+
@container = new Element('div', container_props)
|
19
|
+
|
20
|
+
# CSP without 'unsafe-inline' doesn't allow setting the style attribute directly
|
21
|
+
@container.setStyle(width: this.container_width())
|
22
|
+
|
23
|
+
if @is_multiple
|
24
|
+
@container.update this.get_multi_html()
|
25
|
+
else
|
26
|
+
@container.update this.get_single_html()
|
29
27
|
|
30
28
|
@form_field.hide().insert({ after: @container })
|
31
29
|
@dropdown = @container.down('div.chosen-drop')
|
@@ -51,8 +49,8 @@ class @Chosen extends AbstractChosen
|
|
51
49
|
@form_field.fire("chosen:ready", {chosen: this})
|
52
50
|
|
53
51
|
register_observers: ->
|
54
|
-
@container.observe "touchstart", (evt) => this.container_mousedown(evt)
|
55
|
-
@container.observe "touchend", (evt) => this.container_mouseup(evt)
|
52
|
+
@container.observe "touchstart", (evt) => this.container_mousedown(evt)
|
53
|
+
@container.observe "touchend", (evt) => this.container_mouseup(evt)
|
56
54
|
|
57
55
|
@container.observe "mousedown", (evt) => this.container_mousedown(evt)
|
58
56
|
@container.observe "mouseup", (evt) => this.container_mouseup(evt)
|
@@ -72,7 +70,7 @@ class @Chosen extends AbstractChosen
|
|
72
70
|
@form_field.observe "chosen:updated", (evt) => this.results_update_field(evt)
|
73
71
|
@form_field.observe "chosen:activate", (evt) => this.activate_field(evt)
|
74
72
|
@form_field.observe "chosen:open", (evt) => this.container_mousedown(evt)
|
75
|
-
@form_field.observe "chosen:close", (evt) => this.
|
73
|
+
@form_field.observe "chosen:close", (evt) => this.close_field(evt)
|
76
74
|
|
77
75
|
@search_field.observe "blur", (evt) => this.input_blur(evt)
|
78
76
|
@search_field.observe "keyup", (evt) => this.keyup_checker(evt)
|
@@ -89,7 +87,9 @@ class @Chosen extends AbstractChosen
|
|
89
87
|
destroy: ->
|
90
88
|
@container.ownerDocument.stopObserving "click", @click_test_action
|
91
89
|
|
92
|
-
|
90
|
+
for event in ['chosen:updated', 'chosen:activate', 'chosen:open', 'chosen:close']
|
91
|
+
@form_field.stopObserving(event)
|
92
|
+
|
93
93
|
@container.stopObserving()
|
94
94
|
@search_results.stopObserving()
|
95
95
|
@search_field.stopObserving()
|
@@ -109,31 +109,38 @@ class @Chosen extends AbstractChosen
|
|
109
109
|
@form_field.show()
|
110
110
|
|
111
111
|
search_field_disabled: ->
|
112
|
-
@is_disabled = @form_field.disabled
|
113
|
-
|
112
|
+
@is_disabled = @form_field.disabled || @form_field.up('fieldset')?.disabled || false
|
113
|
+
|
114
|
+
if @is_disabled
|
114
115
|
@container.addClassName 'chosen-disabled'
|
115
|
-
@search_field.disabled = true
|
116
|
-
@selected_item.stopObserving "focus", @activate_action if !@is_multiple
|
117
|
-
this.close_field()
|
118
116
|
else
|
119
117
|
@container.removeClassName 'chosen-disabled'
|
120
|
-
|
121
|
-
|
118
|
+
|
119
|
+
@search_field.disabled = @is_disabled
|
120
|
+
|
121
|
+
unless @is_multiple
|
122
|
+
@selected_item.stopObserving 'focus', this.activate_field
|
123
|
+
|
124
|
+
if @is_disabled
|
125
|
+
this.close_field()
|
126
|
+
else unless @is_multiple
|
127
|
+
@selected_item.observe 'focus', this.activate_field
|
122
128
|
|
123
129
|
container_mousedown: (evt) ->
|
124
|
-
if
|
125
|
-
if evt and evt.type is "mousedown" and not @results_showing
|
126
|
-
evt.stop()
|
130
|
+
return if @is_disabled
|
127
131
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
132
|
+
if evt and evt.type in ['mousedown', 'touchstart'] and not @results_showing
|
133
|
+
evt.preventDefault()
|
134
|
+
|
135
|
+
if not (evt? and evt.target.hasClassName "search-choice-close")
|
136
|
+
if not @active_field
|
137
|
+
@search_field.clear() if @is_multiple
|
138
|
+
@container.ownerDocument.observe "click", @click_test_action
|
139
|
+
this.results_show()
|
140
|
+
else if not @is_multiple and evt and (evt.target is @selected_item || evt.target.up("a.chosen-single"))
|
141
|
+
this.results_toggle()
|
135
142
|
|
136
|
-
|
143
|
+
this.activate_field()
|
137
144
|
|
138
145
|
container_mouseup: (evt) ->
|
139
146
|
this.results_reset(evt) if evt.target.nodeName is "ABBR" and not @is_disabled
|
@@ -159,12 +166,15 @@ class @Chosen extends AbstractChosen
|
|
159
166
|
|
160
167
|
this.show_search_field_default()
|
161
168
|
this.search_field_scale()
|
169
|
+
@search_field.blur()
|
162
170
|
|
163
171
|
activate_field: ->
|
172
|
+
return if @is_disabled
|
173
|
+
|
164
174
|
@container.addClassName "chosen-container-active"
|
165
175
|
@active_field = true
|
166
176
|
|
167
|
-
@search_field.value =
|
177
|
+
@search_field.value = this.get_search_field_value()
|
168
178
|
@search_field.focus()
|
169
179
|
|
170
180
|
test_active_click: (evt) ->
|
@@ -181,7 +191,7 @@ class @Chosen extends AbstractChosen
|
|
181
191
|
|
182
192
|
if @is_multiple
|
183
193
|
@search_choices.select("li.search-choice").invoke("remove")
|
184
|
-
else
|
194
|
+
else
|
185
195
|
this.single_set_selected_text()
|
186
196
|
if @disable_search or @form_field.options.length <= @disable_search_threshold
|
187
197
|
@search_field.readOnly = true
|
@@ -229,7 +239,7 @@ class @Chosen extends AbstractChosen
|
|
229
239
|
@results_showing = true
|
230
240
|
|
231
241
|
@search_field.focus()
|
232
|
-
@search_field.value =
|
242
|
+
@search_field.value = this.get_search_field_value()
|
233
243
|
|
234
244
|
this.winnow_results()
|
235
245
|
@form_field.fire("chosen:showing_dropdown", {chosen: this})
|
@@ -259,7 +269,7 @@ class @Chosen extends AbstractChosen
|
|
259
269
|
@form_field_label = $$("label[for='#{@form_field.id}']").first() #next check for a for=#{id}
|
260
270
|
|
261
271
|
if @form_field_label?
|
262
|
-
@form_field_label.observe "click",
|
272
|
+
@form_field_label.observe "click", this.label_click_handler
|
263
273
|
|
264
274
|
show_search_field_default: ->
|
265
275
|
if @is_multiple and this.choices_count() < 1 and not @active_field
|
@@ -302,9 +312,12 @@ class @Chosen extends AbstractChosen
|
|
302
312
|
|
303
313
|
choice_destroy: (link) ->
|
304
314
|
if this.result_deselect link.readAttribute("rel")
|
305
|
-
|
315
|
+
if @active_field
|
316
|
+
@search_field.focus()
|
317
|
+
else
|
318
|
+
this.show_search_field_default()
|
306
319
|
|
307
|
-
this.results_hide() if @is_multiple and this.choices_count() > 0 and
|
320
|
+
this.results_hide() if @is_multiple and this.choices_count() > 0 and this.get_search_field_value().length < 1
|
308
321
|
|
309
322
|
link.up('li').remove()
|
310
323
|
|
@@ -316,7 +329,7 @@ class @Chosen extends AbstractChosen
|
|
316
329
|
this.single_set_selected_text()
|
317
330
|
this.show_search_field_default()
|
318
331
|
this.results_reset_cleanup()
|
319
|
-
|
332
|
+
this.trigger_form_field_change()
|
320
333
|
this.results_hide() if @active_field
|
321
334
|
|
322
335
|
results_reset_cleanup: ->
|
@@ -351,10 +364,17 @@ class @Chosen extends AbstractChosen
|
|
351
364
|
else
|
352
365
|
this.single_set_selected_text(this.choice_label(item))
|
353
366
|
|
354
|
-
|
355
|
-
|
367
|
+
if @is_multiple && (!@hide_results_on_select || (evt.metaKey or evt.ctrlKey))
|
368
|
+
if evt.metaKey or evt.ctrlKey
|
369
|
+
this.winnow_results(skip_highlight: true)
|
370
|
+
else
|
371
|
+
@search_field.value = ""
|
372
|
+
this.winnow_results()
|
373
|
+
else
|
374
|
+
this.results_hide()
|
375
|
+
this.show_search_field_default()
|
356
376
|
|
357
|
-
|
377
|
+
this.trigger_form_field_change() if @is_multiple || @form_field.selectedIndex != @current_selectedIndex
|
358
378
|
@current_selectedIndex = @form_field.selectedIndex
|
359
379
|
|
360
380
|
evt.preventDefault()
|
@@ -382,7 +402,7 @@ class @Chosen extends AbstractChosen
|
|
382
402
|
this.result_clear_highlight()
|
383
403
|
this.winnow_results() if @results_showing
|
384
404
|
|
385
|
-
|
405
|
+
this.trigger_form_field_change()
|
386
406
|
this.search_field_scale()
|
387
407
|
return true
|
388
408
|
else
|
@@ -393,8 +413,14 @@ class @Chosen extends AbstractChosen
|
|
393
413
|
@selected_item.down("span").insert { after: "<abbr class=\"search-choice-close\"></abbr>" } unless @selected_item.down("abbr")
|
394
414
|
@selected_item.addClassName("chosen-single-with-deselect")
|
395
415
|
|
416
|
+
get_search_field_value: ->
|
417
|
+
@search_field.value
|
418
|
+
|
396
419
|
get_search_text: ->
|
397
|
-
|
420
|
+
this.get_search_field_value().strip()
|
421
|
+
|
422
|
+
escape_html: (text) ->
|
423
|
+
text.escapeHTML()
|
398
424
|
|
399
425
|
winnow_results_set_highlight: ->
|
400
426
|
if not @is_multiple
|
@@ -406,7 +432,7 @@ class @Chosen extends AbstractChosen
|
|
406
432
|
this.result_do_highlight do_high if do_high?
|
407
433
|
|
408
434
|
no_results: (terms) ->
|
409
|
-
@search_results.insert
|
435
|
+
@search_results.insert this.get_no_results_html(terms)
|
410
436
|
@form_field.fire("chosen:no_results", {chosen: this})
|
411
437
|
|
412
438
|
no_results_clear: ->
|
@@ -453,55 +479,45 @@ class @Chosen extends AbstractChosen
|
|
453
479
|
@pending_backstroke.removeClassName("search-choice-focus") if @pending_backstroke
|
454
480
|
@pending_backstroke = null
|
455
481
|
|
456
|
-
keydown_checker: (evt) ->
|
457
|
-
stroke = evt.which ? evt.keyCode
|
458
|
-
this.search_field_scale()
|
459
|
-
|
460
|
-
this.clear_backstroke() if stroke != 8 and this.pending_backstroke
|
461
|
-
|
462
|
-
switch stroke
|
463
|
-
when 8
|
464
|
-
@backstroke_length = this.search_field.value.length
|
465
|
-
break
|
466
|
-
when 9
|
467
|
-
this.result_select(evt) if this.results_showing and not @is_multiple
|
468
|
-
@mouse_on_container = false
|
469
|
-
break
|
470
|
-
when 13
|
471
|
-
evt.preventDefault() if this.results_showing
|
472
|
-
break
|
473
|
-
when 32
|
474
|
-
evt.preventDefault() if @disable_search
|
475
|
-
break
|
476
|
-
when 38
|
477
|
-
evt.preventDefault()
|
478
|
-
this.keyup_arrow()
|
479
|
-
break
|
480
|
-
when 40
|
481
|
-
evt.preventDefault()
|
482
|
-
this.keydown_arrow()
|
483
|
-
break
|
484
|
-
|
485
482
|
search_field_scale: ->
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
483
|
+
return unless @is_multiple
|
484
|
+
|
485
|
+
style_block =
|
486
|
+
position: 'absolute'
|
487
|
+
left: '-1000px'
|
488
|
+
top: '-1000px'
|
489
|
+
display: 'none'
|
490
|
+
whiteSpace: 'pre'
|
491
|
+
|
492
|
+
styles = ['fontSize', 'fontStyle', 'fontWeight', 'fontFamily', 'lineHeight', 'textTransform', 'letterSpacing']
|
493
|
+
|
494
|
+
for style in styles
|
495
|
+
style_block[style] = @search_field.getStyle(style)
|
496
|
+
|
497
|
+
div = new Element('div').update(this.escape_html(this.get_search_field_value()))
|
498
|
+
# CSP without 'unsafe-inline' doesn't allow setting the style attribute directly
|
499
|
+
div.setStyle(style_block)
|
500
|
+
document.body.appendChild(div)
|
501
|
+
|
502
|
+
width = div.measure('width') + 25
|
503
|
+
div.remove()
|
504
|
+
|
505
|
+
if container_width = @container.getWidth()
|
506
|
+
width = Math.min(container_width - 10, width)
|
507
|
+
|
508
|
+
@search_field.setStyle(width: width + 'px')
|
509
|
+
|
510
|
+
trigger_form_field_change: ->
|
511
|
+
triggerHtmlEvent @form_field, 'input'
|
512
|
+
triggerHtmlEvent @form_field, 'change'
|
513
|
+
|
514
|
+
triggerHtmlEvent = (element, eventType) ->
|
515
|
+
if element.dispatchEvent # Modern way:
|
516
|
+
try
|
517
|
+
evt = new Event(eventType, bubbles: true, cancelable: true)
|
518
|
+
catch
|
519
|
+
evt = document.createEvent('HTMLEvents')
|
520
|
+
evt.initEvent(eventType, true, true);
|
521
|
+
element.dispatchEvent(evt)
|
522
|
+
else # Old IE:
|
523
|
+
element.fireEvent("on#{eventType}", document.createEventObject());
|
@@ -20,6 +20,7 @@ class AbstractChosen
|
|
20
20
|
@mouse_on_container = false
|
21
21
|
@results_showing = false
|
22
22
|
@result_highlighted = null
|
23
|
+
@is_rtl = @options.rtl || /\bchosen-rtl\b/.test(@form_field.className)
|
23
24
|
@allow_single_deselect = if @options.allow_single_deselect? and @form_field.options[0]? and @form_field.options[0].text is "" then @options.allow_single_deselect else false
|
24
25
|
@disable_search_threshold = @options.disable_search_threshold || 0
|
25
26
|
@disable_search = @options.disable_search || false
|
@@ -34,6 +35,7 @@ class AbstractChosen
|
|
34
35
|
@include_group_label_in_selected = @options.include_group_label_in_selected || false
|
35
36
|
@max_shown_results = @options.max_shown_results || Number.POSITIVE_INFINITY
|
36
37
|
@case_sensitive_search = @options.case_sensitive_search || false
|
38
|
+
@hide_results_on_select = if @options.hide_results_on_select? then @options.hide_results_on_select else true
|
37
39
|
|
38
40
|
set_default_text: ->
|
39
41
|
if @form_field.getAttribute("data-placeholder")
|
@@ -43,11 +45,13 @@ class AbstractChosen
|
|
43
45
|
else
|
44
46
|
@default_text = @options.placeholder_text_single || @options.placeholder_text || AbstractChosen.default_single_text
|
45
47
|
|
48
|
+
@default_text = this.escape_html(@default_text)
|
49
|
+
|
46
50
|
@results_none_found = @form_field.getAttribute("data-no_results_text") || @options.no_results_text || AbstractChosen.default_no_result_text
|
47
51
|
|
48
52
|
choice_label: (item) ->
|
49
53
|
if @include_group_label_in_selected and item.group_label?
|
50
|
-
"<b class='group-name'>#{item.group_label}</b>#{item.html}"
|
54
|
+
"<b class='group-name'>#{this.escape_html(item.group_label)}</b>#{item.html}"
|
51
55
|
else
|
52
56
|
item.html
|
53
57
|
|
@@ -65,6 +69,12 @@ class AbstractChosen
|
|
65
69
|
@active_field = false
|
66
70
|
setTimeout (=> this.blur_test()), 100
|
67
71
|
|
72
|
+
label_click_handler: (evt) =>
|
73
|
+
if @is_multiple
|
74
|
+
this.container_mousedown(evt)
|
75
|
+
else
|
76
|
+
this.activate_field()
|
77
|
+
|
68
78
|
results_option_build: (options) ->
|
69
79
|
content = ''
|
70
80
|
shown_results = 0
|
@@ -104,9 +114,9 @@ class AbstractChosen
|
|
104
114
|
|
105
115
|
option_el = document.createElement("li")
|
106
116
|
option_el.className = classes.join(" ")
|
107
|
-
option_el.style.cssText = option.style
|
117
|
+
option_el.style.cssText = option.style if option.style
|
108
118
|
option_el.setAttribute("data-option-array-index", option.array_index)
|
109
|
-
option_el.innerHTML = option.
|
119
|
+
option_el.innerHTML = option.highlighted_html or option.html
|
110
120
|
option_el.title = option.title if option.title
|
111
121
|
|
112
122
|
this.outerHTML(option_el)
|
@@ -121,7 +131,7 @@ class AbstractChosen
|
|
121
131
|
|
122
132
|
group_el = document.createElement("li")
|
123
133
|
group_el.className = classes.join(" ")
|
124
|
-
group_el.innerHTML = group.
|
134
|
+
group_el.innerHTML = group.highlighted_html or this.escape_html(group.label)
|
125
135
|
group_el.title = group.title if group.title
|
126
136
|
|
127
137
|
this.outerHTML(group_el)
|
@@ -149,20 +159,21 @@ class AbstractChosen
|
|
149
159
|
else
|
150
160
|
this.results_show()
|
151
161
|
|
152
|
-
winnow_results: ->
|
162
|
+
winnow_results: (options) ->
|
153
163
|
this.no_results_clear()
|
154
164
|
|
155
165
|
results = 0
|
156
166
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
regex = this.get_search_regex(escapedSearchText)
|
167
|
+
query = this.get_search_text()
|
168
|
+
escapedQuery = query.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
|
169
|
+
regex = this.get_search_regex(escapedQuery)
|
161
170
|
|
162
171
|
for option in @results_data
|
163
172
|
|
164
173
|
option.search_match = false
|
165
174
|
results_group = null
|
175
|
+
search_match = null
|
176
|
+
option.highlighted_html = ''
|
166
177
|
|
167
178
|
if this.include_option_in_results(option)
|
168
179
|
|
@@ -175,17 +186,21 @@ class AbstractChosen
|
|
175
186
|
results += 1 if results_group.active_options is 0 and results_group.search_match
|
176
187
|
results_group.active_options += 1
|
177
188
|
|
178
|
-
|
189
|
+
text = if option.group then option.label else option.text
|
179
190
|
|
180
191
|
unless option.group and not @group_search
|
181
|
-
|
192
|
+
search_match = this.search_string_match(text, regex)
|
193
|
+
option.search_match = search_match?
|
194
|
+
|
182
195
|
results += 1 if option.search_match and not option.group
|
183
196
|
|
184
197
|
if option.search_match
|
185
|
-
if
|
186
|
-
startpos =
|
187
|
-
|
188
|
-
|
198
|
+
if query.length
|
199
|
+
startpos = search_match.index
|
200
|
+
prefix = text.slice(0, startpos)
|
201
|
+
fix = text.slice(startpos, startpos + query.length)
|
202
|
+
suffix = text.slice(startpos + query.length)
|
203
|
+
option.highlighted_html = "#{this.escape_html(prefix)}<em>#{this.escape_html(fix)}</em>#{this.escape_html(suffix)}"
|
189
204
|
|
190
205
|
results_group.group_match = true if results_group?
|
191
206
|
|
@@ -194,28 +209,23 @@ class AbstractChosen
|
|
194
209
|
|
195
210
|
this.result_clear_highlight()
|
196
211
|
|
197
|
-
if results < 1 and
|
212
|
+
if results < 1 and query.length
|
198
213
|
this.update_results_content ""
|
199
|
-
this.no_results
|
214
|
+
this.no_results query
|
200
215
|
else
|
201
216
|
this.update_results_content this.results_option_build()
|
202
|
-
this.winnow_results_set_highlight()
|
217
|
+
this.winnow_results_set_highlight() unless options?.skip_highlight
|
203
218
|
|
204
219
|
get_search_regex: (escaped_search_string) ->
|
205
|
-
|
220
|
+
regex_string = if @search_contains then escaped_search_string else "(^|\\s|\\b)#{escaped_search_string}[^\\s]*"
|
221
|
+
regex_string = "^#{regex_string}" unless @enable_split_word_search or @search_contains
|
206
222
|
regex_flag = if @case_sensitive_search then "" else "i"
|
207
|
-
new RegExp(
|
223
|
+
new RegExp(regex_string, regex_flag)
|
208
224
|
|
209
225
|
search_string_match: (search_string, regex) ->
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
#TODO: replace this substitution of /\[\]/ with a list of characters to skip.
|
214
|
-
parts = search_string.replace(/\[|\]/g, "").split(" ")
|
215
|
-
if parts.length
|
216
|
-
for part in parts
|
217
|
-
if regex.test part
|
218
|
-
return true
|
226
|
+
match = regex.exec(search_string)
|
227
|
+
match.index += 1 if !@search_contains && match?[1] # make up for lack of lookbehind operator in regex
|
228
|
+
match
|
219
229
|
|
220
230
|
choices_count: ->
|
221
231
|
return @selected_option_count if @selected_option_count?
|
@@ -228,30 +238,68 @@ class AbstractChosen
|
|
228
238
|
|
229
239
|
choices_click: (evt) ->
|
230
240
|
evt.preventDefault()
|
241
|
+
this.activate_field()
|
231
242
|
this.results_show() unless @results_showing or @is_disabled
|
232
243
|
|
244
|
+
keydown_checker: (evt) ->
|
245
|
+
stroke = evt.which ? evt.keyCode
|
246
|
+
this.search_field_scale()
|
247
|
+
|
248
|
+
this.clear_backstroke() if stroke != 8 and @pending_backstroke
|
249
|
+
|
250
|
+
switch stroke
|
251
|
+
when 8 # backspace
|
252
|
+
@backstroke_length = this.get_search_field_value().length
|
253
|
+
break
|
254
|
+
when 9 # tab
|
255
|
+
this.result_select(evt) if @results_showing and not @is_multiple
|
256
|
+
@mouse_on_container = false
|
257
|
+
break
|
258
|
+
when 13 # enter
|
259
|
+
evt.preventDefault() if @results_showing
|
260
|
+
break
|
261
|
+
when 27 # escape
|
262
|
+
evt.preventDefault() if @results_showing
|
263
|
+
break
|
264
|
+
when 32 # space
|
265
|
+
evt.preventDefault() if @disable_search
|
266
|
+
break
|
267
|
+
when 38 # up arrow
|
268
|
+
evt.preventDefault()
|
269
|
+
this.keyup_arrow()
|
270
|
+
break
|
271
|
+
when 40 # down arrow
|
272
|
+
evt.preventDefault()
|
273
|
+
this.keydown_arrow()
|
274
|
+
break
|
275
|
+
|
233
276
|
keyup_checker: (evt) ->
|
234
277
|
stroke = evt.which ? evt.keyCode
|
235
278
|
this.search_field_scale()
|
236
279
|
|
237
280
|
switch stroke
|
238
|
-
when 8
|
281
|
+
when 8 # backspace
|
239
282
|
if @is_multiple and @backstroke_length < 1 and this.choices_count() > 0
|
240
283
|
this.keydown_backstroke()
|
241
284
|
else if not @pending_backstroke
|
242
285
|
this.result_clear_highlight()
|
243
286
|
this.results_search()
|
244
|
-
|
287
|
+
break
|
288
|
+
when 13 # enter
|
245
289
|
evt.preventDefault()
|
246
290
|
this.result_select(evt) if this.results_showing
|
247
|
-
|
291
|
+
break
|
292
|
+
when 27 # escape
|
248
293
|
this.results_hide() if @results_showing
|
249
|
-
|
250
|
-
when 9,
|
294
|
+
break
|
295
|
+
when 9, 16, 17, 18, 38, 40, 91
|
251
296
|
# don't do anything on these keys
|
252
|
-
else
|
297
|
+
else
|
298
|
+
this.results_search()
|
299
|
+
break
|
253
300
|
|
254
301
|
clipboard_event_checker: (evt) ->
|
302
|
+
return if @is_disabled
|
255
303
|
setTimeout (=> this.results_search()), 50
|
256
304
|
|
257
305
|
container_width: ->
|
@@ -281,6 +329,39 @@ class AbstractChosen
|
|
281
329
|
tmp.appendChild(element)
|
282
330
|
tmp.innerHTML
|
283
331
|
|
332
|
+
get_single_html: ->
|
333
|
+
"""
|
334
|
+
<a class="chosen-single chosen-default">
|
335
|
+
<span>#{@default_text}</span>
|
336
|
+
<div><b></b></div>
|
337
|
+
</a>
|
338
|
+
<div class="chosen-drop">
|
339
|
+
<div class="chosen-search">
|
340
|
+
<input class="chosen-search-input" type="text" autocomplete="off" />
|
341
|
+
</div>
|
342
|
+
<ul class="chosen-results"></ul>
|
343
|
+
</div>
|
344
|
+
"""
|
345
|
+
|
346
|
+
get_multi_html: ->
|
347
|
+
"""
|
348
|
+
<ul class="chosen-choices">
|
349
|
+
<li class="search-field">
|
350
|
+
<input class="chosen-search-input" type="text" autocomplete="off" value="#{@default_text}" />
|
351
|
+
</li>
|
352
|
+
</ul>
|
353
|
+
<div class="chosen-drop">
|
354
|
+
<ul class="chosen-results"></ul>
|
355
|
+
</div>
|
356
|
+
"""
|
357
|
+
|
358
|
+
get_no_results_html: (terms) ->
|
359
|
+
"""
|
360
|
+
<li class="no-results">
|
361
|
+
#{@results_none_found} <span>#{this.escape_html(terms)}</span>
|
362
|
+
</li>
|
363
|
+
"""
|
364
|
+
|
284
365
|
# class methods and variables ============================================================
|
285
366
|
|
286
367
|
@browser_is_supported: ->
|
@@ -15,7 +15,7 @@ class SelectParser
|
|
15
15
|
@parsed.push
|
16
16
|
array_index: group_position
|
17
17
|
group: true
|
18
|
-
label:
|
18
|
+
label: group.label
|
19
19
|
title: group.title if group.title
|
20
20
|
children: 0
|
21
21
|
disabled: group.disabled,
|
@@ -47,21 +47,6 @@ class SelectParser
|
|
47
47
|
empty: true
|
48
48
|
@options_index += 1
|
49
49
|
|
50
|
-
escapeExpression: (text) ->
|
51
|
-
if not text? or text is false
|
52
|
-
return ""
|
53
|
-
unless /[\&\<\>\"\'\`]/.test(text)
|
54
|
-
return text
|
55
|
-
map =
|
56
|
-
"<": "<"
|
57
|
-
">": ">"
|
58
|
-
'"': """
|
59
|
-
"'": "'"
|
60
|
-
"`": "`"
|
61
|
-
unsafe_chars = /&(?!\w+;)|[\<\>\"\'\`]/g
|
62
|
-
text.replace unsafe_chars, (chr) ->
|
63
|
-
map[chr] || "&"
|
64
|
-
|
65
50
|
SelectParser.select_to_array = (select) ->
|
66
51
|
parser = new SelectParser()
|
67
52
|
parser.add_node( child ) for child in select.childNodes
|