chosen-rails 1.5.2 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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