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.
- data/README +3 -28
- data/Rakefile +18 -10
- data/init.rb +1 -0
- data/install.rb +5 -1
- data/lib/admin_assistant/active_record_column.rb +211 -0
- data/lib/admin_assistant/association_target.rb +35 -0
- data/lib/admin_assistant/belongs_to_column.rb +186 -0
- data/lib/admin_assistant/builder.rb +222 -35
- data/lib/admin_assistant/column.rb +266 -297
- data/lib/admin_assistant/default_search_column.rb +41 -0
- data/lib/admin_assistant/file_column_column.rb +73 -0
- data/lib/admin_assistant/form_view.rb +7 -68
- data/lib/admin_assistant/helper.rb +4 -2
- data/lib/admin_assistant/index.rb +101 -81
- data/lib/admin_assistant/paperclip_column.rb +45 -0
- data/lib/admin_assistant/polymorphic_belongs_to_column.rb +102 -0
- data/lib/admin_assistant/request/autocomplete.rb +47 -0
- data/lib/admin_assistant/request/base.rb +176 -0
- data/lib/admin_assistant/request/create.rb +27 -0
- data/lib/admin_assistant/request/destroy.rb +15 -0
- data/lib/admin_assistant/request/edit.rb +11 -0
- data/lib/admin_assistant/request/index.rb +26 -0
- data/lib/admin_assistant/request/new.rb +19 -0
- data/lib/admin_assistant/request/show.rb +24 -0
- data/lib/admin_assistant/request/update.rb +44 -0
- data/lib/admin_assistant/search.rb +82 -0
- data/lib/admin_assistant/show_view.rb +20 -0
- data/lib/admin_assistant/virtual_column.rb +61 -0
- data/lib/admin_assistant.rb +190 -85
- data/lib/javascripts/admin_assistant.js +253 -0
- data/lib/stylesheets/activescaffold.css +219 -0
- data/lib/stylesheets/default.css +119 -0
- data/lib/views/_polymorphic_field_search.html.erb +89 -0
- data/lib/views/_restricted_autocompleter.html.erb +53 -0
- data/lib/views/autocomplete.html.erb +11 -0
- data/lib/views/form.html.erb +6 -3
- data/lib/views/index.html.erb +53 -46
- data/lib/views/show.html.erb +19 -0
- data/vendor/ar_query/MIT-LICENSE +20 -0
- data/vendor/ar_query/README +0 -0
- data/vendor/ar_query/init.rb +1 -0
- data/vendor/ar_query/install.rb +1 -0
- data/vendor/ar_query/lib/ar_query.rb +137 -0
- data/vendor/ar_query/spec/ar_query_spec.rb +253 -0
- data/vendor/ar_query/tasks/ar_query_tasks.rake +0 -0
- data/vendor/ar_query/uninstall.rb +1 -0
- metadata +39 -16
- data/lib/admin_assistant/request.rb +0 -183
- data/lib/stylesheets/admin_assistant.css +0 -75
data/lib/admin_assistant.rb
CHANGED
@@ -1,61 +1,39 @@
|
|
1
|
-
|
1
|
+
$: << File.join(File.dirname(__FILE__), '../vendor/ar_query/lib')
|
2
|
+
require 'find'
|
2
3
|
require 'admin_assistant/column'
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
require
|
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
|
11
|
-
|
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
|
-
@
|
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
|
-
@
|
20
|
-
|
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
|
45
|
-
|
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
|
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(
|
112
|
+
def dispatch_to_request_method(request_class, controller)
|
83
113
|
controller.instance_variable_set :@admin_assistant, self
|
84
|
-
|
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
|
-
|
107
|
-
|
108
|
-
dispatch_to_request_method meth, args.first
|
120
|
+
if @model.respond_to?(meth)
|
121
|
+
@model.send meth, *args
|
109
122
|
else
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
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
|
-
|
144
|
+
@model_class_name ||
|
145
|
+
@model_class.name.gsub(/([A-Z])/, ' \1')[1..-1].downcase
|
122
146
|
end
|
123
|
-
|
124
|
-
def
|
125
|
-
|
126
|
-
|
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
|
-
|
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__)}/
|
168
|
-
"#{RAILS_ROOT}/public/
|
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
|
+
|