admin_assistant 0.0.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|