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.
@@ -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 = if @is_multiple then new Element('div', container_props).update( @multi_temp.evaluate({ "default": @default_text}) ) else new Element('div', container_props).update( @single_temp.evaluate({ "default":@default_text }) )
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); evt.preventDefault()
55
- @container.observe "touchend", (evt) => this.container_mouseup(evt); evt.preventDefault()
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.input_blur(evt)
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
- @form_field.stopObserving()
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
- if(@is_disabled)
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
- @search_field.disabled = false
121
- @selected_item.observe "focus", @activate_action if !@is_multiple
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 !@is_disabled
125
- if evt and evt.type is "mousedown" and not @results_showing
126
- evt.stop()
130
+ return if @is_disabled
127
131
 
128
- if not (evt? and evt.target.hasClassName "search-choice-close")
129
- if not @active_field
130
- @search_field.clear() if @is_multiple
131
- @container.ownerDocument.observe "click", @click_test_action
132
- this.results_show()
133
- else if not @is_multiple and evt and (evt.target is @selected_item || evt.target.up("a.chosen-single"))
134
- this.results_toggle()
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
- this.activate_field()
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 = @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 if not @is_multiple
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 = @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", (evt) => if @is_multiple then this.container_mousedown(evt) else this.activate_field()
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
- this.show_search_field_default()
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 @search_field.value.length < 1
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
- @form_field.simulate("change") if typeof Event.simulate is 'function'
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
- this.results_hide() unless (evt.metaKey or evt.ctrlKey) and @is_multiple
355
- this.show_search_field_default()
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
- @form_field.simulate("change") if typeof Event.simulate is 'function' && (@is_multiple || @form_field.selectedIndex != @current_selectedIndex)
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
- @form_field.simulate("change") if typeof Event.simulate is 'function'
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
- @search_field.value.strip().escapeHTML()
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 @no_results_temp.evaluate( terms: terms )
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
- if @is_multiple
487
- h = 0
488
- w = 0
489
-
490
- style_block = "position:absolute; left: -1000px; top: -1000px; display:none;"
491
- styles = ['font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing']
492
-
493
- for style in styles
494
- style_block += style + ":" + @search_field.getStyle(style) + ";"
495
-
496
- div = new Element('div', { 'style' : style_block }).update(@search_field.value.escapeHTML())
497
- document.body.appendChild(div)
498
-
499
- w = Element.measure(div, 'width') + 25
500
- div.remove()
501
-
502
- f_width = @container.getWidth()
503
-
504
- if( w > f_width-10 )
505
- w = f_width - 10
506
-
507
- @search_field.setStyle({'width': w + 'px'})
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.search_text
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.search_text
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
- searchText = this.get_search_text()
158
- escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")
159
- zregex = new RegExp(escapedSearchText, 'i')
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
- option.search_text = if option.group then option.label else option.html
189
+ text = if option.group then option.label else option.text
179
190
 
180
191
  unless option.group and not @group_search
181
- option.search_match = this.search_string_match(option.search_text, regex)
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 searchText.length
186
- startpos = option.search_text.search zregex
187
- text = option.search_text.substr(0, startpos + searchText.length) + '</em>' + option.search_text.substr(startpos + searchText.length)
188
- option.search_text = text.substr(0, startpos) + '<em>' + text.substr(startpos)
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 searchText.length
212
+ if results < 1 and query.length
198
213
  this.update_results_content ""
199
- this.no_results searchText
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
- regex_anchor = if @search_contains then "" else "^"
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(regex_anchor + escaped_search_string, regex_flag)
223
+ new RegExp(regex_string, regex_flag)
208
224
 
209
225
  search_string_match: (search_string, regex) ->
210
- if regex.test search_string
211
- return true
212
- else if @enable_split_word_search and (search_string.indexOf(" ") >= 0 or search_string.indexOf("[") == 0)
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
- when 13
287
+ break
288
+ when 13 # enter
245
289
  evt.preventDefault()
246
290
  this.result_select(evt) if this.results_showing
247
- when 27
291
+ break
292
+ when 27 # escape
248
293
  this.results_hide() if @results_showing
249
- return true
250
- when 9, 38, 40, 16, 91, 17, 18
294
+ break
295
+ when 9, 16, 17, 18, 38, 40, 91
251
296
  # don't do anything on these keys
252
- else this.results_search()
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: this.escapeExpression(group.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
- "<": "&lt;"
57
- ">": "&gt;"
58
- '"': "&quot;"
59
- "'": "&#x27;"
60
- "`": "&#x60;"
61
- unsafe_chars = /&(?!\w+;)|[\<\>\"\'\`]/g
62
- text.replace unsafe_chars, (chr) ->
63
- map[chr] || "&amp;"
64
-
65
50
  SelectParser.select_to_array = (select) ->
66
51
  parser = new SelectParser()
67
52
  parser.add_node( child ) for child in select.childNodes