chosen-rails_ffcrm 0.9.5

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.
@@ -0,0 +1,580 @@
1
+ ###
2
+ Chosen source: generate output using 'cake build'
3
+ Copyright (c) 2011 by Harvest
4
+ ###
5
+ root = this
6
+
7
+ class Chosen extends AbstractChosen
8
+
9
+ setup: ->
10
+ @is_rtl = @form_field.hasClassName "chzn-rtl"
11
+
12
+ finish_setup: ->
13
+ @form_field.addClassName "chzn-done"
14
+
15
+ set_default_values: ->
16
+ super()
17
+
18
+ # HTML Templates
19
+ @single_temp = new Template('<a href="javascript:void(0)" class="chzn-single"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>')
20
+ @multi_temp = new Template('<ul class="chzn-choices"><li class="search-field"><input type="text" value="#{default}" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>')
21
+ @choice_temp = new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>')
22
+ @no_results_temp = new Template('<li class="no-results">' + @results_none_found + ' "<span>#{terms}</span>"</li>')
23
+
24
+ set_up_html: ->
25
+ @container_id = @form_field.identify().replace(/(:|\.)/g, '_') + "_chzn"
26
+
27
+ @f_width = if @form_field.getStyle("width") then parseInt @form_field.getStyle("width"), 10 else @form_field.getWidth()
28
+
29
+ container_props =
30
+ 'id': @container_id
31
+ 'class': "chzn-container#{ if @is_rtl then ' chzn-rtl' else '' }"
32
+ 'style': 'width: ' + (@f_width) + 'px' #use parens around @f_width so coffeescript doesn't think + ' px' is a function parameter
33
+
34
+ @default_text = if @form_field.readAttribute 'data-placeholder' then @form_field.readAttribute 'data-placeholder' else @default_text_default
35
+
36
+ base_template = 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 }) )
37
+
38
+ @form_field.hide().insert({ after: base_template })
39
+ @container = $(@container_id)
40
+ @container.addClassName( "chzn-container-" + (if @is_multiple then "multi" else "single") )
41
+ @container.addClassName "chzn-container-single-nosearch" if not @is_multiple and @form_field.options.length <= @disable_search_threshold
42
+ @dropdown = @container.down('div.chzn-drop')
43
+
44
+ dd_top = @container.getHeight()
45
+ dd_width = (@f_width - get_side_border_padding(@dropdown))
46
+
47
+ @dropdown.setStyle({"width": dd_width + "px", "top": dd_top + "px"})
48
+
49
+ @search_field = @container.down('input')
50
+ @search_results = @container.down('ul.chzn-results')
51
+ this.search_field_scale()
52
+
53
+ @search_no_results = @container.down('li.no-results')
54
+
55
+ if @is_multiple
56
+ @search_choices = @container.down('ul.chzn-choices')
57
+ @search_container = @container.down('li.search-field')
58
+ else
59
+ @search_container = @container.down('div.chzn-search')
60
+ @selected_item = @container.down('.chzn-single')
61
+ sf_width = dd_width - get_side_border_padding(@search_container) - get_side_border_padding(@search_field)
62
+ @search_field.setStyle( {"width" : sf_width + "px"} )
63
+
64
+ this.results_build()
65
+ this.set_tab_index()
66
+ @form_field.fire("liszt:ready", {chosen: this})
67
+
68
+ register_observers: ->
69
+ @container.observe "mousedown", (evt) => this.container_mousedown(evt)
70
+ @container.observe "mouseup", (evt) => this.container_mouseup(evt)
71
+ @container.observe "mouseenter", (evt) => this.mouse_enter(evt)
72
+ @container.observe "mouseleave", (evt) => this.mouse_leave(evt)
73
+
74
+ @search_results.observe "mouseup", (evt) => this.search_results_mouseup(evt)
75
+ @search_results.observe "mouseover", (evt) => this.search_results_mouseover(evt)
76
+ @search_results.observe "mouseout", (evt) => this.search_results_mouseout(evt)
77
+
78
+ @form_field.observe "liszt:updated", (evt) => this.results_update_field(evt)
79
+
80
+ @search_field.observe "blur", (evt) => this.input_blur(evt)
81
+ @search_field.observe "keyup", (evt) => this.keyup_checker(evt)
82
+ @search_field.observe "keydown", (evt) => this.keydown_checker(evt)
83
+
84
+ if @is_multiple
85
+ @search_choices.observe "click", (evt) => this.choices_click(evt)
86
+ @search_field.observe "focus", (evt) => this.input_focus(evt)
87
+
88
+ search_field_disabled: ->
89
+ @is_disabled = @form_field.disabled
90
+ if(@is_disabled)
91
+ @container.addClassName 'chzn-disabled'
92
+ @search_field.disabled = true
93
+ @selected_item.stopObserving "focus", @activate_action if !@is_multiple
94
+ this.close_field()
95
+ else
96
+ @container.removeClassName 'chzn-disabled'
97
+ @search_field.disabled = false
98
+ @selected_item.observe "focus", @activate_action if !@is_multiple
99
+
100
+ container_mousedown: (evt) ->
101
+ if !@is_disabled
102
+ target_closelink = if evt? then evt.target.hasClassName "search-choice-close" else false
103
+ if evt and evt.type is "mousedown" and not @results_showing
104
+ evt.stop()
105
+ if not @pending_destroy_click and not target_closelink
106
+ if not @active_field
107
+ @search_field.clear() if @is_multiple
108
+ document.observe "click", @click_test_action
109
+ this.results_show()
110
+ else if not @is_multiple and evt and (evt.target is @selected_item || evt.target.up("a.chzn-single"))
111
+ this.results_toggle()
112
+
113
+ this.activate_field()
114
+ else
115
+ @pending_destroy_click = false
116
+
117
+ container_mouseup: (evt) ->
118
+ this.results_reset(evt) if evt.target.nodeName is "ABBR"
119
+
120
+ blur_test: (evt) ->
121
+ this.close_field() if not @active_field and @container.hasClassName("chzn-container-active")
122
+
123
+ close_field: ->
124
+ document.stopObserving "click", @click_test_action
125
+
126
+ if not @is_multiple
127
+ @selected_item.tabIndex = @search_field.tabIndex
128
+ @search_field.tabIndex = -1
129
+
130
+ @active_field = false
131
+ this.results_hide()
132
+
133
+ @container.removeClassName "chzn-container-active"
134
+ this.winnow_results_clear()
135
+ this.clear_backstroke()
136
+
137
+ this.show_search_field_default()
138
+ this.search_field_scale()
139
+
140
+ activate_field: ->
141
+ if not @is_multiple and not @active_field
142
+ @search_field.tabIndex = @selected_item.tabIndex
143
+ @selected_item.tabIndex = -1
144
+
145
+ @container.addClassName "chzn-container-active"
146
+ @active_field = true
147
+
148
+ @search_field.value = @search_field.value
149
+ @search_field.focus()
150
+
151
+
152
+ test_active_click: (evt) ->
153
+ if evt.target.up('#' + @container_id)
154
+ @active_field = true
155
+ else
156
+ this.close_field()
157
+
158
+ results_build: ->
159
+ startTime = new Date()
160
+ @parsing = true
161
+ @results_data = root.SelectParser.select_to_array @form_field
162
+
163
+ if @is_multiple and @choices > 0
164
+ @search_choices.select("li.search-choice").invoke("remove")
165
+ @choices = 0
166
+ else if not @is_multiple
167
+ @selected_item.down("span").update(@default_text)
168
+
169
+ content = ''
170
+ for data in @results_data
171
+ if data.group
172
+ content += this.result_add_group data
173
+ else if !data.empty
174
+ content += this.result_add_option data
175
+ if data.selected and @is_multiple
176
+ this.choice_build data
177
+ else if data.selected and not @is_multiple
178
+ @selected_item.down("span").update( data.html )
179
+ this.single_deselect_control_build() if @allow_single_deselect
180
+
181
+ this.search_field_disabled()
182
+ this.show_search_field_default()
183
+ this.search_field_scale()
184
+
185
+ @search_results.update content
186
+ @parsing = false
187
+
188
+
189
+ result_add_group: (group) ->
190
+ if not group.disabled
191
+ group.dom_id = @container_id + "_g_" + group.array_index
192
+ '<li id="' + group.dom_id + '" class="group-result">' + group.label.escapeHTML() + '</li>'
193
+ else
194
+ ""
195
+
196
+ result_do_highlight: (el) ->
197
+ this.result_clear_highlight()
198
+
199
+ @result_highlight = el
200
+ @result_highlight.addClassName "highlighted"
201
+
202
+ maxHeight = parseInt @search_results.getStyle('maxHeight'), 10
203
+ visible_top = @search_results.scrollTop
204
+ visible_bottom = maxHeight + visible_top
205
+
206
+ high_top = @result_highlight.positionedOffset().top
207
+ high_bottom = high_top + @result_highlight.getHeight()
208
+
209
+ if high_bottom >= visible_bottom
210
+ @search_results.scrollTop = if (high_bottom - maxHeight) > 0 then (high_bottom - maxHeight) else 0
211
+ else if high_top < visible_top
212
+ @search_results.scrollTop = high_top
213
+
214
+ result_clear_highlight: ->
215
+ @result_highlight.removeClassName('highlighted') if @result_highlight
216
+ @result_highlight = null
217
+
218
+ results_show: ->
219
+ if not @is_multiple
220
+ @selected_item.addClassName('chzn-single-with-drop')
221
+ if @result_single_selected
222
+ this.result_do_highlight( @result_single_selected )
223
+
224
+ dd_top = if @is_multiple then @container.getHeight() else (@container.getHeight() - 1)
225
+ @dropdown.setStyle {"top": dd_top + "px", "left":0}
226
+ @results_showing = true
227
+
228
+ @search_field.focus()
229
+ @search_field.value = @search_field.value
230
+
231
+ this.winnow_results()
232
+
233
+ results_hide: ->
234
+ @selected_item.removeClassName('chzn-single-with-drop') unless @is_multiple
235
+ this.result_clear_highlight()
236
+ @dropdown.setStyle({"left":"-9000px"})
237
+ @results_showing = false
238
+
239
+
240
+ set_tab_index: (el) ->
241
+ if @form_field.tabIndex
242
+ ti = @form_field.tabIndex
243
+ @form_field.tabIndex = -1
244
+
245
+ if @is_multiple
246
+ @search_field.tabIndex = ti
247
+ else
248
+ @selected_item.tabIndex = ti
249
+ @search_field.tabIndex = -1
250
+
251
+ show_search_field_default: ->
252
+ if @is_multiple and @choices < 1 and not @active_field
253
+ @search_field.value = @default_text
254
+ @search_field.addClassName "default"
255
+ else
256
+ @search_field.value = ""
257
+ @search_field.removeClassName "default"
258
+
259
+ search_results_mouseup: (evt) ->
260
+ target = if evt.target.hasClassName("active-result") then evt.target else evt.target.up(".active-result")
261
+ if target
262
+ @result_highlight = target
263
+ this.result_select(evt)
264
+
265
+ search_results_mouseover: (evt) ->
266
+ target = if evt.target.hasClassName("active-result") then evt.target else evt.target.up(".active-result")
267
+ this.result_do_highlight( target ) if target
268
+
269
+ search_results_mouseout: (evt) ->
270
+ this.result_clear_highlight() if evt.target.hasClassName('active-result') or evt.target.up('.active-result')
271
+
272
+
273
+ choices_click: (evt) ->
274
+ evt.preventDefault()
275
+ if( @active_field and not(evt.target.hasClassName('search-choice') or evt.target.up('.search-choice')) and not @results_showing )
276
+ this.results_show()
277
+
278
+ choice_build: (item) ->
279
+ choice_id = @container_id + "_c_" + item.array_index
280
+ @choices += 1
281
+ @search_container.insert
282
+ before: @choice_temp.evaluate
283
+ id: choice_id
284
+ choice: item.html
285
+ position: item.array_index
286
+ link = $(choice_id).down('a')
287
+ link.observe "click", (evt) => this.choice_destroy_link_click(evt)
288
+
289
+ choice_destroy_link_click: (evt) ->
290
+ evt.preventDefault()
291
+ if not @is_disabled
292
+ @pending_destroy_click = true
293
+ this.choice_destroy evt.target
294
+
295
+ choice_destroy: (link) ->
296
+ @choices -= 1
297
+ this.show_search_field_default()
298
+
299
+ this.results_hide() if @is_multiple and @choices > 0 and @search_field.value.length < 1
300
+
301
+ this.result_deselect link.readAttribute("rel")
302
+ link.up('li').remove()
303
+
304
+ results_reset: (evt) ->
305
+ @form_field.options[0].selected = true
306
+ @selected_item.down("span").update(@default_text)
307
+ this.show_search_field_default()
308
+ evt.target.remove()
309
+ @form_field.simulate("change") if typeof Event.simulate is 'function'
310
+ this.results_hide() if @active_field
311
+
312
+ result_select: (evt) ->
313
+ if @result_highlight
314
+ high = @result_highlight
315
+ this.result_clear_highlight()
316
+
317
+ if @is_multiple
318
+ this.result_deactivate high
319
+ else
320
+ @search_results.descendants(".result-selected").invoke "removeClassName", "result-selected"
321
+ @result_single_selected = high
322
+
323
+ high.addClassName("result-selected")
324
+
325
+ position = high.id.substr(high.id.lastIndexOf("_") + 1 )
326
+ item = @results_data[position]
327
+ item.selected = true
328
+
329
+ @form_field.options[item.options_index].selected = true
330
+
331
+ if @is_multiple
332
+ this.choice_build item
333
+ else
334
+ @selected_item.down("span").update(item.html)
335
+ this.single_deselect_control_build() if @allow_single_deselect
336
+
337
+ @on_option_add(item.value) if @on_option_add
338
+
339
+ this.results_hide() unless evt.metaKey and @is_multiple
340
+
341
+ @search_field.value = ""
342
+
343
+ @form_field.simulate("change") if typeof Event.simulate is 'function'
344
+ this.search_field_scale()
345
+ else if @options.allow_option_creation
346
+ new_option = @search_field.value
347
+ return unless new_option
348
+ if @allow_creation(new_option)
349
+ @form_field.insert(Element('option', {selected: true, value: new_option}).update(new_option))
350
+ @results_update_field(evt)
351
+ @on_option_add(new_option) if @on_option_add
352
+ @form_field.simulate("change") if typeof Event.simulate is 'function'
353
+ @search_field.value = ""
354
+ @results_hide()
355
+
356
+ allow_creation: (new_option) ->
357
+ if @is_multiple
358
+ matches = @search_choices.getElementsBySelector("li.search-choice span").select (el) ->
359
+ console.debug(el)
360
+ el.innerHTML.toLowerCase() == new_option.toLowerCase()
361
+ console.debug(matches)
362
+ !matches.length
363
+ else
364
+ @selected_item.getElementsBySelector('span').innerHTML.toLowerCase() != new_option.toLowerCase()
365
+
366
+ result_activate: (el) ->
367
+ el.addClassName("active-result")
368
+
369
+ result_deactivate: (el) ->
370
+ el.removeClassName("active-result")
371
+
372
+ result_deselect: (pos) ->
373
+ result_data = @results_data[pos]
374
+ result_data.selected = false
375
+
376
+ option = @form_field.options[result_data.options_index]
377
+ option.selected = false
378
+ @on_option_remove(option.value) if @on_option_remove
379
+
380
+ result = $(@container_id + "_o_" + pos)
381
+ result.removeClassName("result-selected").addClassName("active-result").show()
382
+
383
+ this.result_clear_highlight()
384
+ this.winnow_results()
385
+
386
+ @form_field.simulate("change") if typeof Event.simulate is 'function'
387
+ this.search_field_scale()
388
+
389
+ single_deselect_control_build: ->
390
+ @selected_item.down("span").insert { after: "<abbr class=\"search-choice-close\"></abbr>" } if @allow_single_deselect and not @selected_item.down("abbr")
391
+
392
+ winnow_results: ->
393
+ startTime = new Date()
394
+ this.no_results_clear()
395
+
396
+ results = 0
397
+
398
+ searchText = if @search_field.value is @default_text then "" else @search_field.value.strip().escapeHTML()
399
+ textToSearch = searchText.replace(/[\-\[\]\{\}\(\)\*\+\?\.,\\\^\$\|\#\s]/g, "\\$&")
400
+ regex = new RegExp('^' + textToSearch, 'i')
401
+ zregex = new RegExp(textToSearch, 'i')
402
+ fregex = new RegExp("^" + textToSearch + "$", 'i')
403
+
404
+ for option in @results_data
405
+ if not option.disabled and not option.empty
406
+ if option.group
407
+ $(option.dom_id).hide()
408
+ else if not (@is_multiple and option.selected)
409
+ found = false
410
+ result_id = option.dom_id
411
+
412
+ if @options.allow_option_creation && searchText && fregex.test(option.html)
413
+ found = true
414
+ results += 1
415
+ @result_do_highlight($(option.dom_id))
416
+ else if regex.test option.html
417
+ found = true
418
+ results += 1
419
+ else if option.html.indexOf(" ") >= 0 or option.html.indexOf("[") == 0
420
+ #TODO: replace this substitution of /\[\]/ with a list of characters to skip.
421
+ parts = option.html.replace(/\[|\]/g, "").split(" ")
422
+ if parts.length
423
+ for part in parts
424
+ if regex.test part
425
+ found = true
426
+ results += 1
427
+
428
+ if found
429
+ if searchText.length
430
+ startpos = option.html.search zregex
431
+ text = option.html.substr(0, startpos + searchText.length) + '</em>' + option.html.substr(startpos + searchText.length)
432
+ text = text.substr(0, startpos) + '<em>' + text.substr(startpos)
433
+ else
434
+ text = option.html
435
+
436
+ $(result_id).update text if $(result_id).innerHTML != text
437
+
438
+ this.result_activate $(result_id)
439
+
440
+ $(@results_data[option.group_array_index].dom_id).show() if option.group_array_index?
441
+ else
442
+ this.result_clear_highlight() if $(result_id) is @result_highlight
443
+ this.result_deactivate $(result_id)
444
+
445
+ if results < 1 and searchText.length
446
+ this.no_results(searchText)
447
+ @results_hide() if @options.allow_option_creation && @is_multiple
448
+ else if not @options.allow_option_creation
449
+ this.winnow_results_set_highlight()
450
+
451
+ winnow_results_clear: ->
452
+ @search_field.clear()
453
+ lis = @search_results.select("li")
454
+
455
+ for li in lis
456
+ if li.hasClassName("group-result")
457
+ li.show()
458
+ else if not @is_multiple or not li.hasClassName("result-selected")
459
+ this.result_activate li
460
+
461
+ winnow_results_set_highlight: ->
462
+ if not @result_highlight
463
+
464
+ if not @is_multiple
465
+ do_high = @search_results.down(".result-selected.active-result")
466
+
467
+ if not do_high?
468
+ do_high = @search_results.down(".active-result")
469
+
470
+ this.result_do_highlight do_high if do_high?
471
+
472
+ no_results: (terms) ->
473
+ return if !@is_multiple && @options.allow_option_creation
474
+ @search_results.insert @no_results_temp.evaluate( terms: terms )
475
+
476
+ no_results_clear: ->
477
+ nr = null
478
+ nr.remove() while nr = @search_results.down(".no-results")
479
+
480
+
481
+ keydown_arrow: ->
482
+ actives = @search_results.select("li.active-result")
483
+ if actives.length
484
+ if not @result_highlight
485
+ this.result_do_highlight actives.first()
486
+ else if @results_showing
487
+ sibs = @result_highlight.nextSiblings()
488
+ nexts = sibs.intersect(actives)
489
+ this.result_do_highlight nexts.first() if nexts.length
490
+ this.results_show() if not @results_showing
491
+
492
+ keyup_arrow: ->
493
+ if not @results_showing and not @is_multiple
494
+ this.results_show()
495
+ else if @result_highlight
496
+ sibs = @result_highlight.previousSiblings()
497
+ actives = @search_results.select("li.active-result")
498
+ prevs = sibs.intersect(actives)
499
+
500
+ if prevs.length
501
+ this.result_do_highlight prevs.first()
502
+ else
503
+ this.results_hide() if @choices > 0
504
+ this.result_clear_highlight()
505
+
506
+ keydown_backstroke: ->
507
+ if @pending_backstroke
508
+ this.choice_destroy @pending_backstroke.down("a")
509
+ this.clear_backstroke()
510
+ else
511
+ @pending_backstroke = @search_container.siblings("li.search-choice").last()
512
+ @pending_backstroke.addClassName("search-choice-focus")
513
+
514
+ clear_backstroke: ->
515
+ @pending_backstroke.removeClassName("search-choice-focus") if @pending_backstroke
516
+ @pending_backstroke = null
517
+
518
+ keydown_checker: (evt) ->
519
+ stroke = evt.which ? evt.keyCode
520
+ this.search_field_scale()
521
+
522
+ this.clear_backstroke() if stroke != 8 and this.pending_backstroke
523
+
524
+ switch stroke
525
+ when 8
526
+ @backstroke_length = this.search_field.value.length
527
+ break
528
+ when 9
529
+ this.result_select(evt) if this.results_showing and not @is_multiple
530
+ @mouse_on_container = false
531
+ break
532
+ when 13
533
+ evt.preventDefault()
534
+ break
535
+ when 38
536
+ evt.preventDefault()
537
+ this.keyup_arrow()
538
+ break
539
+ when 40
540
+ this.keydown_arrow()
541
+ break
542
+
543
+ search_field_scale: ->
544
+ if @is_multiple
545
+ h = 0
546
+ w = 0
547
+
548
+ style_block = "position:absolute; left: -1000px; top: -1000px; display:none;"
549
+ styles = ['font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing']
550
+
551
+ for style in styles
552
+ style_block += style + ":" + @search_field.getStyle(style) + ";"
553
+
554
+ div = new Element('div', { 'style' : style_block }).update(@search_field.value.escapeHTML())
555
+ document.body.appendChild(div)
556
+
557
+ w = Element.measure(div, 'width') + 25
558
+ div.remove()
559
+
560
+ if( w > @f_width-10 )
561
+ w = @f_width - 10
562
+
563
+ @search_field.setStyle({'width': w + 'px'})
564
+
565
+ dd_top = @container.getHeight()
566
+ @dropdown.setStyle({"top": dd_top + "px"})
567
+
568
+ root.Chosen = Chosen
569
+
570
+ # Prototype does not support version numbers so we add it ourselves
571
+ if Prototype.Browser.IE
572
+ if /MSIE (\d+\.\d+);/.test(navigator.userAgent)
573
+ Prototype.BrowserFeatures['Version'] = new Number(RegExp.$1);
574
+
575
+
576
+ get_side_border_padding = (elmt) ->
577
+ layout = new Element.Layout(elmt)
578
+ side_border_padding = layout.get("border-left") + layout.get("border-right") + layout.get("padding-left") + layout.get("padding-right")
579
+
580
+ root.get_side_border_padding = get_side_border_padding