admin_assistant 0.0.1 → 1.0.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.
Files changed (49) hide show
  1. data/README +3 -28
  2. data/Rakefile +18 -10
  3. data/init.rb +1 -0
  4. data/install.rb +5 -1
  5. data/lib/admin_assistant/active_record_column.rb +211 -0
  6. data/lib/admin_assistant/association_target.rb +35 -0
  7. data/lib/admin_assistant/belongs_to_column.rb +186 -0
  8. data/lib/admin_assistant/builder.rb +222 -35
  9. data/lib/admin_assistant/column.rb +266 -297
  10. data/lib/admin_assistant/default_search_column.rb +41 -0
  11. data/lib/admin_assistant/file_column_column.rb +73 -0
  12. data/lib/admin_assistant/form_view.rb +7 -68
  13. data/lib/admin_assistant/helper.rb +4 -2
  14. data/lib/admin_assistant/index.rb +101 -81
  15. data/lib/admin_assistant/paperclip_column.rb +45 -0
  16. data/lib/admin_assistant/polymorphic_belongs_to_column.rb +102 -0
  17. data/lib/admin_assistant/request/autocomplete.rb +47 -0
  18. data/lib/admin_assistant/request/base.rb +176 -0
  19. data/lib/admin_assistant/request/create.rb +27 -0
  20. data/lib/admin_assistant/request/destroy.rb +15 -0
  21. data/lib/admin_assistant/request/edit.rb +11 -0
  22. data/lib/admin_assistant/request/index.rb +26 -0
  23. data/lib/admin_assistant/request/new.rb +19 -0
  24. data/lib/admin_assistant/request/show.rb +24 -0
  25. data/lib/admin_assistant/request/update.rb +44 -0
  26. data/lib/admin_assistant/search.rb +82 -0
  27. data/lib/admin_assistant/show_view.rb +20 -0
  28. data/lib/admin_assistant/virtual_column.rb +61 -0
  29. data/lib/admin_assistant.rb +190 -85
  30. data/lib/javascripts/admin_assistant.js +253 -0
  31. data/lib/stylesheets/activescaffold.css +219 -0
  32. data/lib/stylesheets/default.css +119 -0
  33. data/lib/views/_polymorphic_field_search.html.erb +89 -0
  34. data/lib/views/_restricted_autocompleter.html.erb +53 -0
  35. data/lib/views/autocomplete.html.erb +11 -0
  36. data/lib/views/form.html.erb +6 -3
  37. data/lib/views/index.html.erb +53 -46
  38. data/lib/views/show.html.erb +19 -0
  39. data/vendor/ar_query/MIT-LICENSE +20 -0
  40. data/vendor/ar_query/README +0 -0
  41. data/vendor/ar_query/init.rb +1 -0
  42. data/vendor/ar_query/install.rb +1 -0
  43. data/vendor/ar_query/lib/ar_query.rb +137 -0
  44. data/vendor/ar_query/spec/ar_query_spec.rb +253 -0
  45. data/vendor/ar_query/tasks/ar_query_tasks.rake +0 -0
  46. data/vendor/ar_query/uninstall.rb +1 -0
  47. metadata +39 -16
  48. data/lib/admin_assistant/request.rb +0 -183
  49. data/lib/stylesheets/admin_assistant.css +0 -75
@@ -1,61 +1,39 @@
1
- require 'admin_assistant/builder'
1
+ $: << File.join(File.dirname(__FILE__), '../vendor/ar_query/lib')
2
+ require 'find'
2
3
  require 'admin_assistant/column'
3
- require 'admin_assistant/form_view'
4
- require 'admin_assistant/helper'
5
- require 'admin_assistant/index'
6
- require 'admin_assistant/request'
4
+ Find.find(File.dirname(__FILE__)) do |path|
5
+ if path =~ %r|\.rb$| && path !~ %r|admin_assistant\.rb$| &&
6
+ path !~ %r|admin_assistant/column\.rb$|
7
+ require path
8
+ end
9
+ end
7
10
  require 'will_paginate'
8
11
 
9
12
  class AdminAssistant
10
- attr_reader :custom_column_labels, :form_settings, :index_settings,
11
- :model_class
12
- attr_accessor :actions
13
-
13
+ attr_reader :base_settings, :controller_class, :form_settings,
14
+ :index_settings, :model_class, :show_settings
15
+ attr_accessor :actions, :custom_destroy
16
+ attr_writer :model_class_name
17
+
18
+ def self.template_file(template_name)
19
+ "#{File.dirname(__FILE__)}/views/#{template_name}.html.erb"
20
+ end
21
+
14
22
  def initialize(controller_class, model_class)
15
23
  @controller_class, @model_class = controller_class, model_class
16
- @actions = [:index, :create, :update]
24
+ @model = Model.new model_class
25
+ @actions = [:index, :create, :update, :show]
17
26
  @form_settings = FormSettings.new self
18
27
  @index_settings = IndexSettings.new self
19
- @custom_column_labels = {}
20
- end
21
-
22
- def belongs_to_assoc(name)
23
- model_class.reflect_on_all_associations.detect { |assoc|
24
- assoc.macro == :belongs_to && assoc.name.to_s == name.to_s
25
- }
26
- end
27
-
28
- def column(name)
29
- column = if file_columns.include?(name)
30
- FileColumnColumn.new name
31
- elsif (ar_column = model_class.columns_hash[name.to_s])
32
- ActiveRecordColumn.new(ar_column)
33
- elsif belongs_to_assoc = belongs_to_assoc(name)
34
- BelongsToColumn.new(belongs_to_assoc)
35
- else
36
- AdminAssistantColumn.new(name)
37
- end
38
- if column && (custom = custom_column_labels[name.to_s])
39
- column.custom_label = custom
40
- end
41
- column
28
+ @show_settings = ShowSettings.new self
29
+ @base_settings = BaseSettings.new self
42
30
  end
43
31
 
44
- def column_name_or_assoc_name(name)
45
- result = name
46
- ar_column = model_class.columns_hash[name.to_s]
47
- if ar_column
48
- associations = model_class.reflect_on_all_associations
49
- if belongs_to_assoc = associations.detect { |assoc|
50
- assoc.macro == :belongs_to && assoc.association_foreign_key == name
51
- }
52
- result = belongs_to_assoc.name.to_s
53
- end
54
- end
55
- result
32
+ def [](name)
33
+ @base_settings[name]
56
34
  end
57
35
 
58
- def columns(names)
36
+ def accumulate_columns(names)
59
37
  columns = paperclip_attachments.map { |paperclip_attachment|
60
38
  PaperclipColumn.new paperclip_attachment
61
39
  }
@@ -68,10 +46,62 @@ class AdminAssistant
68
46
  columns
69
47
  end
70
48
 
49
+ def accumulate_belongs_to_columns(names)
50
+ accumulate_columns(names).select { |column| column.is_a?(BelongsToColumn) }
51
+ end
52
+
53
+ def autocomplete_actions
54
+ ac_actions = []
55
+ if [:new, :create, :edit, :update].any? { |action|
56
+ actions.include?(action)
57
+ }
58
+ accumulate_belongs_to_columns(default_column_names).each { |column|
59
+ ac_actions << "autocomplete_#{column.name}".to_sym
60
+ }
61
+ end
62
+ if actions.include?(:index)
63
+ base_settings.all_polymorphic_types.each do |p_type|
64
+ ac_actions << "autocomplete_#{p_type.name.underscore.downcase}".to_sym
65
+ end
66
+ end
67
+ ac_actions.uniq
68
+ end
69
+
70
+ def column(name)
71
+ if file_columns.include?(name.to_s)
72
+ FileColumnColumn.new name
73
+ elsif paperclip_attachments.include?(name)
74
+ PaperclipColumn.new name
75
+ elsif (belongs_to_assoc = belongs_to_assoc(name) or
76
+ belongs_to_assoc = belongs_to_assoc_by_foreign_key(name))
77
+ column_based_on_belongs_to_assoc name, belongs_to_assoc
78
+ elsif belongs_to_assoc = belongs_to_assoc_by_polymorphic_type(name)
79
+ # skip it, actually
80
+ elsif (ar_column = @model_class.columns_hash[name.to_s])
81
+ ActiveRecordColumn.new ar_column
82
+ else
83
+ VirtualColumn.new name, @model_class, self
84
+ end
85
+ end
86
+
87
+ def column_based_on_belongs_to_assoc(name, belongs_to_assoc)
88
+ if belongs_to_assoc.options[:polymorphic]
89
+ PolymorphicBelongsToColumn.new belongs_to_assoc
90
+ else
91
+ BelongsToColumn.new(
92
+ belongs_to_assoc,
93
+ :match_text_fields_in_search =>
94
+ search_settings[name].match_text_fields_for_association?,
95
+ :sort_by => index_settings[name].sort_by
96
+ )
97
+ end
98
+ end
99
+
71
100
  def controller_actions
72
101
  c_actions = actions.clone
73
102
  c_actions << :new if c_actions.include?(:create)
74
103
  c_actions << :edit if c_actions.include?(:update)
104
+ c_actions.concat(autocomplete_actions) if autocomplete_actions
75
105
  c_actions
76
106
  end
77
107
 
@@ -79,58 +109,50 @@ class AdminAssistant
79
109
  controller.controller_path.gsub(%r|/|, '_')
80
110
  end
81
111
 
82
- def dispatch_to_request_method(request_method, controller)
112
+ def dispatch_to_request_method(request_class, controller)
83
113
  controller.instance_variable_set :@admin_assistant, self
84
- klass = Request.const_get request_method.to_s.capitalize
85
- @request = klass.new(self, controller)
114
+ @request = request_class.new(self, controller)
86
115
  @request.call
87
116
  @request = nil
88
117
  end
89
118
 
90
- def file_columns
91
- fc = []
92
- if model_class.respond_to?(:file_column)
93
- model_class.columns.each do |column|
94
- suffixes = %w( relative_path dir relative_dir temp )
95
- if suffixes.all? { |suffix|
96
- model_class.method_defined? "#{column.name}_#{suffix}".to_sym
97
- }
98
- fc << column.name
99
- end
100
- end
101
- end
102
- fc
103
- end
104
-
105
119
  def method_missing(meth, *args)
106
- request_methods = [:create, :destroy, :edit, :index, :new, :update]
107
- if request_methods.include?(meth) and args.size == 1
108
- dispatch_to_request_method meth, args.first
120
+ if @model.respond_to?(meth)
121
+ @model.send meth, *args
109
122
  else
110
- if meth.to_s =~ /(.*)\?/ && request_methods.include?($1.to_sym)
111
- @actions.include?($1.to_sym)
112
- elsif @request.respond_to?(meth)
113
- @request.send meth, *args
123
+ request_methods = [
124
+ :create, :destroy, :edit, :index, :new, :update, :show
125
+ ]
126
+ if request_methods.include?(meth) and args.size == 1
127
+ request_class = Request.const_get meth.to_s.capitalize
128
+ dispatch_to_request_method request_class, args.first
129
+ elsif autocomplete_actions && autocomplete_actions.include?(meth)
130
+ dispatch_to_request_method Request::Autocomplete, args.first
114
131
  else
115
- super
132
+ if meth.to_s =~ /(.*)\?/ && request_methods.include?($1.to_sym)
133
+ @controller_class.public_instance_methods.include?($1)
134
+ elsif @request.respond_to?(meth)
135
+ @request.send meth, *args
136
+ else
137
+ super
138
+ end
116
139
  end
117
140
  end
118
141
  end
119
142
 
120
143
  def model_class_name
121
- model_class.name.gsub(/([A-Z])/, ' \1')[1..-1].downcase
144
+ @model_class_name ||
145
+ @model_class.name.gsub(/([A-Z])/, ' \1')[1..-1].downcase
122
146
  end
123
-
124
- def paperclip_attachments
125
- pa = []
126
- if model_class.respond_to?(:attachment_definitions)
127
- if model_class.attachment_definitions
128
- pa = model_class.attachment_definitions.map { |name, definition|
129
- name
130
- }
131
- end
147
+
148
+ def profile(msg)
149
+ if @request_start_time
150
+ puts "#{msg}: #{Time.now - @request_start_time}"
132
151
  end
133
- pa
152
+ end
153
+
154
+ def search_settings
155
+ @index_settings.search_settings
134
156
  end
135
157
 
136
158
  def url_params(a = action)
@@ -159,13 +181,96 @@ class AdminAssistant
159
181
  end
160
182
  end
161
183
  end
184
+
185
+ class Model
186
+ def initialize(ar_model)
187
+ @ar_model = ar_model
188
+ end
189
+
190
+ def belongs_to_associations
191
+ @ar_model.reflect_on_all_associations.select { |assoc|
192
+ assoc.macro == :belongs_to
193
+ }
194
+ end
195
+
196
+ def belongs_to_assoc(association_name)
197
+ belongs_to_associations.detect { |assoc|
198
+ assoc.name.to_s == association_name.to_s
199
+ }
200
+ end
201
+
202
+ def belongs_to_assoc_by_foreign_key(foreign_key)
203
+ belongs_to_associations.detect { |assoc|
204
+ assoc.association_foreign_key == foreign_key
205
+ }
206
+ end
207
+
208
+ def belongs_to_assoc_by_polymorphic_type(name)
209
+ if name =~ /^(.*)_type/
210
+ belongs_to_associations.detect { |assoc|
211
+ assoc.options[:polymorphic] && $1 == assoc.name.to_s
212
+ }
213
+ end
214
+ end
215
+
216
+ def default_column_names
217
+ @ar_model.columns.reject { |ar_column|
218
+ %w(id created_at updated_at).include?(ar_column.name)
219
+ }.map { |ar_column| ar_column.name }
220
+ end
221
+
222
+ def file_columns
223
+ unless @file_columns
224
+ @file_columns = []
225
+ if @ar_model.respond_to?(:file_column)
226
+ names_to_check = @ar_model.columns.map &:name
227
+ names_to_check.concat(
228
+ @ar_model.instance_methods.
229
+ select { |m| m =~ /=$/ }.
230
+ map { |m| m.gsub(/=/, '')}.
231
+ select { |m| @ar_model.instance_methods.include?(m) }
232
+ )
233
+ names_to_check.uniq.each do |name|
234
+ suffixes = %w( relative_path dir relative_dir temp )
235
+ if suffixes.all? { |suffix|
236
+ @ar_model.method_defined? "#{name}_#{suffix}".to_sym
237
+ }
238
+ @file_columns << name
239
+ end
240
+ end
241
+ end
242
+ end
243
+ @file_columns
244
+ end
245
+
246
+ def paperclip_attachments
247
+ pa = []
248
+ if @ar_model.respond_to?(:attachment_definitions)
249
+ if @ar_model.attachment_definitions
250
+ pa = @ar_model.attachment_definitions.map { |name, definition|
251
+ name
252
+ }
253
+ end
254
+ end
255
+ pa
256
+ end
257
+
258
+ def searchable_columns
259
+ @ar_model.columns.select { |column|
260
+ [:string, :text].include?(column.type)
261
+ }
262
+ end
263
+ end
162
264
  end
163
265
 
164
266
  ActionController::Base.send :include, AdminAssistant::ControllerMethods
165
267
 
268
+ css_dir = "#{RAILS_ROOT}/public/stylesheets/admin_assistant"
269
+ FileUtils.mkdir(css_dir) unless File.exist?(css_dir)
270
+ FileUtils.cp_r(Dir.glob("#{File.dirname(__FILE__)}/stylesheets/*"), css_dir)
166
271
  FileUtils.copy(
167
- "#{File.dirname(__FILE__)}/stylesheets/admin_assistant.css",
168
- "#{RAILS_ROOT}/public/stylesheets/admin_assistant.css"
272
+ "#{File.dirname(__FILE__)}/javascripts/admin_assistant.js",
273
+ "#{RAILS_ROOT}/public/javascripts/admin_assistant.js"
169
274
  )
170
275
  images_dir = "#{RAILS_ROOT}/public/images/admin_assistant"
171
276
  FileUtils.mkdir(images_dir) unless File.exist?(images_dir)
@@ -0,0 +1,253 @@
1
+ /******************************************************************************
2
+ Don't edit this file: It gets re-copied every time the server starts.
3
+ ******************************************************************************/
4
+
5
+ var AdminAssistant = {};
6
+ AdminAssistant.clear_datetime_select = function(name) {
7
+ $R(1,5).each(function(index) {
8
+ $(name + '_' + index + 'i').value = '';
9
+ });
10
+ };
11
+
12
+ AdminAssistant.show_search_form = function() {
13
+ $('search_form').show();
14
+ };
15
+
16
+ AdminAssistant.PolymorphicFieldSearch = Class.create();
17
+ AdminAssistant.PolymorphicFieldSearch.prototype = {
18
+ initialize: function(name, types) {
19
+ this.name = name;
20
+ this.hiddenFieldId = 'search_' + name + '_id';
21
+ this.hiddenTypeFieldId = 'search_' + name + '_type';
22
+ this.autocompleteTypes = types.autocompleteTypes;
23
+ this.autocompleters = {};
24
+ this.autocompleteTypes.each(function(autocompleteType) {
25
+ autocompleter = this.newAutocompleter(autocompleteType);
26
+ this.autocompleters[autocompleteType.name] = autocompleter;
27
+ }, this);
28
+ this.idTypes = types.idTypes;
29
+ this.idTypes.each(function(idType) {
30
+ Event.observe(
31
+ this.name + '_' + idType.name + "_id", 'keyup',
32
+ this.setHiddenFieldsFromIdField.bind(this)
33
+ );
34
+ }, this);
35
+ this.selectTypes = types.selectTypes;
36
+ this.selectTypes.each(function(selectType) {
37
+ Event.observe(
38
+ this.name + '_' + selectType.name + "_id", 'change',
39
+ this.setHiddenFieldsFromSelectField.bind(this)
40
+ );
41
+ }, this);
42
+ },
43
+
44
+ clearHiddenFields: function() {
45
+ this.hiddenField().value = '';
46
+ this.hiddenTypeField().value = '';
47
+ },
48
+
49
+ clearInputsExceptFor: function(name_to_not_clear) {
50
+ this.autocompleteTypes.each(function(autocompleteType) {
51
+ if (autocompleteType.name != name_to_not_clear) {
52
+ var autocompleter = this.autocompleters[autocompleteType.name]
53
+ autocompleter.clearTextField();
54
+ autocompleter.setClearLinkVisibility();
55
+ }
56
+ }, this);
57
+ this.idTypes.each(function(idType) {
58
+ if (idType.name != name_to_not_clear) {
59
+ $(this.name + '_' + idType.name + '_id').value = '';
60
+ }
61
+ }, this);
62
+ this.selectTypes.each(function(selectType) {
63
+ if (selectType.name != name_to_not_clear) {
64
+ $(this.name + '_' + selectType.name + '_id').value = '';
65
+ }
66
+ }, this);
67
+ },
68
+
69
+ hiddenField: function() {
70
+ return $(this.hiddenFieldId);
71
+ },
72
+
73
+ hiddenTypeField: function() {
74
+ return $(this.hiddenTypeFieldId);
75
+ },
76
+
77
+ newAutocompleter: function(autocompleteType) {
78
+ options = {
79
+ clearLink: autocompleteType.clearLink,
80
+ includeBlank: true,
81
+ modelName: autocompleteType.name,
82
+ palette: autocompleteType.palette,
83
+ paletteClonesInputWidth: false,
84
+ parameters: autocompleteType.parameters,
85
+ paramName: autocompleteType.name + '_autocomplete_input',
86
+ textField: autocompleteType.textField
87
+ };
88
+ options.afterAutocomplete = function(value) {
89
+ this.hiddenTypeField().value = autocompleteType.type;
90
+ this.clearInputsExceptFor(autocompleteType.name);
91
+ }.bind(this);
92
+ options.afterClearSelected = function(value) {
93
+ this.hiddenTypeField().value = '';
94
+ }.bind(this);
95
+ return new AdminAssistant.RestrictedAutocompleter(
96
+ autocompleteType.name, this.hiddenFieldId, autocompleteType.url,
97
+ options
98
+ );
99
+ },
100
+
101
+ setHiddenFieldsFromIdField: function(event) {
102
+ input = event.findElement();
103
+ idType = this.idTypes.detect(function(idt) {
104
+ return (this.name + '_' + idt.name + '_id' == input.id);
105
+ }, this);
106
+ if (input.value == '') {
107
+ if (this.hiddenTypeField().value == idType.type) {
108
+ this.clearHiddenFields();
109
+ }
110
+ } else {
111
+ this.hiddenField().value = input.value;
112
+ this.hiddenTypeField().value = idType.type;
113
+ this.clearInputsExceptFor(idType.name);
114
+ }
115
+ },
116
+
117
+ setHiddenFieldsFromSelectField: function(event) {
118
+ select = event.findElement();
119
+ selectType = this.selectTypes.detect(function(st) {
120
+ return (this.name + '_' + st.name + '_id' == select.id);
121
+ }, this);
122
+ if (select.value == '') {
123
+ if (this.hiddenTypeField().value == selectType.type) {
124
+ this.clearHiddenFields();
125
+ }
126
+ } else {
127
+ this.hiddenField().value = select.value;
128
+ this.hiddenTypeField().value = selectType.type;
129
+ this.clearInputsExceptFor(selectType.name);
130
+ }
131
+ }
132
+ }
133
+
134
+ /*
135
+ This autocompleter restricts based on a list of names and matching IDs; the IDs
136
+ are set in a hidden field. Arguments
137
+ */
138
+ AdminAssistant.RestrictedAutocompleter = Class.create();
139
+ AdminAssistant.RestrictedAutocompleter.prototype = {
140
+ initialize: function(name, hiddenField, url, options) {
141
+ this.name = name;
142
+ this.textField = options.textField || (this.name + '_autocomplete_input');
143
+ this.palette = options.palette || (this.name + '_autocomplete_palette');
144
+ this.selectedTextFieldValue = $(this.textField).value;
145
+ this.hiddenField = hiddenField;
146
+ this.includeBlank = options.includeBlank;
147
+ this.modelName = options.modelName || this.name;
148
+ this.clearLink = options.clearLink || ('clear_' + this.name + '_link');
149
+ ajaxAutocompleterOptions = {
150
+ afterUpdateElement: this.autocompleteAfterUpdateElement.bind(this),
151
+ fullSearch: true,
152
+ onHide: this.autocompleteOnHide,
153
+ parameters: options.parameters,
154
+ paramName: options.paramName,
155
+ partialChars: 1,
156
+ };
157
+ if (!options.paletteClonesInputWidth) {
158
+ ajaxAutocompleterOptions.onShow = this.onShowWithoutCloningInputWidth;
159
+ }
160
+ var ac = new Ajax.Autocompleter(
161
+ this.textField, this.palette, url, ajaxAutocompleterOptions
162
+ );
163
+ this.changeArrowBehavior(ac);
164
+ this.setClearLinkVisibility();
165
+ Event.observe(this.clearLink, 'click', function(event) {
166
+ this.clearSelected();
167
+ }.bind(this));
168
+ this.afterAutocomplete = options.afterAutocomplete;
169
+ this.afterClearSelected = options.afterClearSelected;
170
+ },
171
+
172
+ /*
173
+ This is the callback that fires after a selection is made from the
174
+ autocompleter results.
175
+
176
+ In addition to setting hidden id fields, it will place the text label of the
177
+ selected item in the search field. It assumes that your selectedElement is
178
+ one with simple text contents or some markup structure with an element of
179
+ class "title". In the latter case, it will use the text of the "title"
180
+ element.
181
+ */
182
+ autocompleteAfterUpdateElement: function(element, selectedElement) {
183
+ var id_name = element.id;
184
+ var input_id = id_name.substr(id_name.length - 1)
185
+ var selected_value = selectedElement.id.substr(this.modelName.length)
186
+
187
+ // If they have more complex markup inside the selection, get the "title" element
188
+ var title_element = selectedElement.down(".title")
189
+ if (title_element) selectedElement = title_element
190
+
191
+ $(this.hiddenField).value = selected_value
192
+ this.selectedTextFieldValue =
193
+ selectedElement.innerHTML.unescapeHTML().strip();
194
+ this.setClearLinkVisibility();
195
+ if (this.afterAutocomplete) { this.afterAutocomplete(selected_value); }
196
+ },
197
+
198
+ /*
199
+ Refresh the text fill-in field to the value reflected in the underlying
200
+ hidden input fields
201
+ */
202
+ autocompleteOnHide: function( element, update ) {
203
+ if (this.selectedTextFieldValue) {
204
+ element.value = this.selectedTextFieldValue;
205
+ }
206
+ new Effect.Fade(update,{duration:0.15});
207
+ },
208
+
209
+ /*
210
+ Overriding totally wierd arrow-up and arrow-down scrolling behavior built
211
+ into the autocompleter in scriptaculous 1.7.0
212
+ */
213
+ changeArrowBehavior: function(ac) {
214
+ ac.markPrevious = function() {
215
+ if(this.index > 0) this.index--
216
+ else this.index = this.entryCount-1;
217
+ };
218
+ ac.markNext = function() {
219
+ if(this.index < this.entryCount-1) this.index++
220
+ else this.index = 0;
221
+ };
222
+ },
223
+
224
+ clearSelected: function() {
225
+ $(this.hiddenField).value = '';
226
+ this.clearTextField();
227
+ this.setClearLinkVisibility();
228
+ if (this.afterClearSelected) { this.afterClearSelected(); }
229
+ },
230
+
231
+ onShowWithoutCloningInputWidth: function(element, update){
232
+ if(!update.style.position || update.style.position=='absolute') {
233
+ update.style.position = 'absolute';
234
+ Position.clone(element, update, {
235
+ setHeight: false, setWidth: false, offsetTop: element.offsetHeight
236
+ });
237
+ }
238
+ Effect.Appear(update,{duration:0.15});
239
+ },
240
+
241
+ clearTextField: function() {
242
+ $(this.textField).value = '';
243
+ },
244
+
245
+ setClearLinkVisibility: function() {
246
+ if ($(this.textField).value == '') {
247
+ $(this.clearLink).hide();
248
+ } else {
249
+ if (this.includeBlank) { $(this.clearLink).show(); }
250
+ }
251
+ }
252
+ }
253
+