jqgrid_rails 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +5 -0
- data/LICENSE.rdoc +19 -0
- data/README.rdoc +62 -0
- data/examples/quick_ref.rdoc +83 -0
- data/examples/usage.rdoc +121 -0
- data/files/javascripts/jqgrid/README.md +1 -0
- data/files/javascripts/jqgrid/grid.locale-en.js +128 -0
- data/files/javascripts/jqgrid/jquery.jqGrid.min.js +510 -0
- data/files/stylesheets/jqgrid/ellipsis-xbl.xml +13 -0
- data/files/stylesheets/jqgrid/ui.jqgrid.css +136 -0
- data/init.rb +1 -0
- data/jqgrid_rails.gemspec +17 -0
- data/lib/jqgrid_rails.rb +20 -0
- data/lib/jqgrid_rails/escape_mappings.rb +23 -0
- data/lib/jqgrid_rails/jqgrid.rb +331 -0
- data/lib/jqgrid_rails/jqgrid_rails_controller.rb +286 -0
- data/lib/jqgrid_rails/jqgrid_rails_generators.rb +23 -0
- data/lib/jqgrid_rails/jqgrid_rails_helper.rb +12 -0
- data/lib/jqgrid_rails/jqgrid_rails_helpers.rb +250 -0
- data/lib/jqgrid_rails/jqgrid_rails_view.rb +71 -0
- data/lib/jqgrid_rails/jqgrid_rails_writeexcel.rb +45 -0
- data/lib/jqgrid_rails/jqgrid_url_generator.rb +9 -0
- data/lib/jqgrid_rails/railtie.rb +29 -0
- data/lib/jqgrid_rails/tasks.rb +21 -0
- data/lib/jqgrid_rails/version.rb +17 -0
- data/rails/init.rb +1 -0
- metadata +123 -0
@@ -0,0 +1,286 @@
|
|
1
|
+
module JqGridRails
|
2
|
+
module Controller
|
3
|
+
|
4
|
+
# These are the valid search operators jqgrid uses
|
5
|
+
# and the database translations for them. We use closures
|
6
|
+
# for the value so we can modify the string if we see fit
|
7
|
+
SEARCH_OPERS = {
|
8
|
+
'eq' => ['= ?', lambda{|v|v}],
|
9
|
+
'ne' => ['!= ?', lambda{|v|v}],
|
10
|
+
'lt' => ['< ?', lambda{|v|v}],
|
11
|
+
'le' => ['<= ?', lambda{|v|v}],
|
12
|
+
'gt' => ['> ?', lambda{|v|v}],
|
13
|
+
'ge' => ['>= ?', lambda{|v|v}],
|
14
|
+
'bw' => ['ilike ?', lambda{|v| "#{v}%"}],
|
15
|
+
'bn' => ['not ilike ?', lambda{|v| "#{v}%"}],
|
16
|
+
'in' => ['in ?', lambda{|v| v.split(',').map(&:strip)}],
|
17
|
+
'ni' => ['not in ?', lambda{|v| v.split(',').map(&:strip)}],
|
18
|
+
'ew' => ['ilike ?', lambda{|v| "%#{v}"}],
|
19
|
+
'en' => ['not ilike ?', lambda{|v| "%#{v}"}],
|
20
|
+
'cn' => ['ilike ?', lambda{|v| "%#{v}%"}],
|
21
|
+
'nc' => ['not ilike ?', lambda{|v| "%#{v}%"}]
|
22
|
+
}
|
23
|
+
|
24
|
+
# klass:: ActiveRecord::Base class or ActiveRecord::Relation
|
25
|
+
# params:: Request params
|
26
|
+
# fields:: Fields used within grid. Can be an array of attribute names or a Hash with keys of attributes and Hash values with options
|
27
|
+
# Array: [col1, col2, col3]
|
28
|
+
# Hash: {'col1' => {:fomatter => lambda{|v|v.to_s.upcase}, :order => 'other_table.col1'}}
|
29
|
+
# Provides generic JSON response for jqGrid requests (sorting/searching)
|
30
|
+
def grid_response(klass, params, fields)
|
31
|
+
raw_response(klass, params, fields).to_json
|
32
|
+
end
|
33
|
+
|
34
|
+
# klass:: ActiveRecord::Base class or ActiveRecord::Relation
|
35
|
+
# params:: Request params
|
36
|
+
# fields:: Fields used within grid. Can be an array of attribute names or a Hash with keys of attributes and Hash values with options
|
37
|
+
# Array: [col1, col2, col3]
|
38
|
+
# Hash: {'col1' => {:fomatter => lambda{|v|v.to_s.upcase}, :order => 'other_table.col1'}}
|
39
|
+
# Provides hash result for jqGrid requests (sorting/searching)
|
40
|
+
def raw_response(klass, params, fields)
|
41
|
+
allowed_consts = %w(ActiveRecord::Base ActiveRecord::Relation ActiveRecord::NamedScope::Scope)
|
42
|
+
unless(allowed_consts.detect{|const| klass.ancestors.detect{|c| c.to_s == const}})
|
43
|
+
raise TypeError.new "Unexpected type received. Allowed types are Class or ActiveRecord::Relation. Received: #{klass.class.name}"
|
44
|
+
end
|
45
|
+
clean_fields = scrub_fields(fields)
|
46
|
+
rel = apply_searching(klass, params, clean_fields)
|
47
|
+
unsorted = apply_filtering(rel, params, clean_fields)
|
48
|
+
rel = apply_sorting(unsorted, params, clean_fields)
|
49
|
+
create_result_hash(unsorted, rel, clean_fields)
|
50
|
+
end
|
51
|
+
|
52
|
+
# fields:: Fields used within grid
|
53
|
+
# Scrubs out fields to ensure in proper state
|
54
|
+
def scrub_fields(fields)
|
55
|
+
clean_fields = nil
|
56
|
+
if(fields.is_a?(Hash))
|
57
|
+
clean_fields = ActiveSupport::OrderedHash.new
|
58
|
+
fields.each_pair do |k,v|
|
59
|
+
clean_fields[k.to_s] = v.nil? ? {} : v
|
60
|
+
end
|
61
|
+
else
|
62
|
+
clean_fields = fields.map(&:to_s)
|
63
|
+
end
|
64
|
+
if(clean_fields.is_a?(Hash))
|
65
|
+
raise TypeError.new 'Hash values must be of Hash type or nil' if fields.values.detect{|v| !v.is_a?(Hash)}
|
66
|
+
end
|
67
|
+
clean_fields
|
68
|
+
end
|
69
|
+
|
70
|
+
# given:: Field for searching
|
71
|
+
# fields:: Array or Hash map of fields
|
72
|
+
# Returns proper field if mapped and ensures field is valid
|
73
|
+
def discover_field(given, fields)
|
74
|
+
given = JqGridRails.unescape(given)
|
75
|
+
col = nil
|
76
|
+
case fields
|
77
|
+
when Hash
|
78
|
+
col = fields.keys.detect{|key| key.to_s == given}
|
79
|
+
when Array
|
80
|
+
col = given if fields.map(&:to_s).include?(given)
|
81
|
+
else
|
82
|
+
raise TypeError.new "Expecting fields to be Array or Hash. Received: #{fields.class.name}"
|
83
|
+
end
|
84
|
+
raise NameError.new "Requested field was not found in provided fields list. Given: #{given}" unless col
|
85
|
+
col
|
86
|
+
end
|
87
|
+
|
88
|
+
# klass:: ActiveRecord::Base class or ActiveRecord::Relation
|
89
|
+
# col:: Sort column
|
90
|
+
# fields:: Aray or Hash map of fields
|
91
|
+
# Returns proper sorter based on inference or user defined
|
92
|
+
def discover_sorter(klass, col, fields)
|
93
|
+
col = JqGridRails.unescape(col)
|
94
|
+
if(fields.is_a?(Hash) && fields[col].try(:[], :order).present?)
|
95
|
+
fields[col][:order]
|
96
|
+
else
|
97
|
+
database_name_by_string(col, klass, fields)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
# klass:: ActiveRecord::Base class or ActiveRecord::Relation
|
104
|
+
# params:: Request params
|
105
|
+
# fields:: Fields used within grid
|
106
|
+
# Applies any sorting to result set
|
107
|
+
def apply_sorting(klass, params, fields)
|
108
|
+
sort_col = params[[:sidx, :searchField].find{|sym| !params[sym].blank?}]
|
109
|
+
unless(sort_col)
|
110
|
+
begin
|
111
|
+
sort_col = discover_field(sort_col, fields)
|
112
|
+
rescue NameError
|
113
|
+
# continue on and let the sort_col be set to default below
|
114
|
+
end
|
115
|
+
end
|
116
|
+
unless(sort_col)
|
117
|
+
sort_col = (fields.is_a?(Hash) ? fields.keys : fields).first
|
118
|
+
end
|
119
|
+
sorter = discover_sorter(klass, sort_col, fields)
|
120
|
+
sort_ord = params[:sord] == 'asc' ? 'ASC' : 'DESC'
|
121
|
+
if(sorter.present?)
|
122
|
+
if(defined?(ActiveRecord::Relation) && klass.is_a?(ActiveRecord::Relation))
|
123
|
+
klass.order("#{sorter} #{sort_ord}")
|
124
|
+
else
|
125
|
+
klass.scoped(:order => "#{sorter} #{sort_ord}")
|
126
|
+
end
|
127
|
+
else
|
128
|
+
klass
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# klass:: ActiveRecord::Base class or ActiveRecord::Relation
|
133
|
+
# params:: Request params
|
134
|
+
# fields:: Fields used within grid
|
135
|
+
# Applies any search restrictions to result set
|
136
|
+
# TODO: DRY out #apply_filtering and #apply_searching
|
137
|
+
def apply_searching(klass, params, fields)
|
138
|
+
unless(params[:searchField].blank?)
|
139
|
+
field = discover_field(params[:searchField], fields)
|
140
|
+
search_oper = params[:searchOper]
|
141
|
+
search_string = params[:searchString]
|
142
|
+
raise ArgumentError.new("Invalid search operator received: #{search_oper}") unless SEARCH_OPERS.keys.include?(search_oper)
|
143
|
+
if(defined?(ActiveRecord::Relation) && klass.is_a?(ActiveRecord::Relation))
|
144
|
+
if(fields.is_a?(Hash) && fields[field][:having])
|
145
|
+
rel = rel.having([
|
146
|
+
"#{fields[field][:having]} #{SEARCH_OPERS[oper].first}", SEARCH_OPERS[oper].last.call(data)
|
147
|
+
])
|
148
|
+
end
|
149
|
+
if(!fields.is_a?(Hash) || fields[field][:having].blank? || fields[field][:where])
|
150
|
+
klass.where([
|
151
|
+
"#{database_name_by_string(field, klass, fields)} #{SEARCH_OPERS[search_oper].first}",
|
152
|
+
SEARCH_OPERS[search_oper].last.call(search_string)
|
153
|
+
])
|
154
|
+
end
|
155
|
+
else
|
156
|
+
if(fields.is_a?(Hash) && fields[field][:having])
|
157
|
+
rel = rel.scoped(
|
158
|
+
:conditions => [
|
159
|
+
"#{fields[field][:having]} #{SEARCH_OPERS[oper].first}", SEARCH_OPERS[oper].last.call(data)
|
160
|
+
]
|
161
|
+
)
|
162
|
+
end
|
163
|
+
if(!fields.is_a?(Hash) || fields[field][:having].blank? || fields[field][:where])
|
164
|
+
klass.scoped(
|
165
|
+
:conditions => [
|
166
|
+
"#{database_name_by_string(field, klass, fields)} #{SEARCH_OPERS[search_oper].first}",
|
167
|
+
SEARCH_OPERS[search_oper].last.call(search_string)
|
168
|
+
]
|
169
|
+
)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
else
|
173
|
+
klass
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# klass:: ActiveRecord::Base class or ActiveRecord::Relation
|
178
|
+
# params:: Request params
|
179
|
+
# fields:: Fields used within grid
|
180
|
+
# Applies any filter restrictions to result set
|
181
|
+
#
|
182
|
+
# TODO: Currently this only supports AND'ing the filters. Need
|
183
|
+
# to add support for grabbing groupOp from parameters and using it for
|
184
|
+
# joining query parameters.
|
185
|
+
def apply_filtering(klass, params, fields)
|
186
|
+
rel = klass
|
187
|
+
havings = []
|
188
|
+
unless(params[:filters].blank?)
|
189
|
+
filters = JSON.load(params[:filters])
|
190
|
+
filters['rules'].each do |filter|
|
191
|
+
field = discover_field(filter['field'].gsub('___', '.'), fields)
|
192
|
+
oper = filter['op']
|
193
|
+
raise ArgumentError.new("Invalid search operator received: #{oper}") unless SEARCH_OPERS.keys.include?(oper)
|
194
|
+
data = filter['data']
|
195
|
+
if(fields.is_a?(Hash) && fields[field][:having])
|
196
|
+
havings << ["#{fields[field][:having]} #{SEARCH_OPERS[oper].first}", SEARCH_OPERS[oper].last.call(data)]
|
197
|
+
end
|
198
|
+
if(defined?(ActiveRecord::Relation) && rel.is_a?(ActiveRecord::Relation))
|
199
|
+
if(!fields.is_a?(Hash) || fields[field][:having].blank? || fields[field][:where])
|
200
|
+
rel = rel.where([
|
201
|
+
"#{database_name_by_string(field, klass, fields)} #{SEARCH_OPERS[oper].first}",
|
202
|
+
SEARCH_OPERS[oper].last.call(data)
|
203
|
+
])
|
204
|
+
end
|
205
|
+
else
|
206
|
+
if(!fields.is_a?(Hash) || fields[field][:having].blank? || fields[field][:where])
|
207
|
+
rel = rel.scoped(
|
208
|
+
:conditions => [
|
209
|
+
"#{database_name_by_string(field, klass, fields)} #{SEARCH_OPERS[oper].first}",
|
210
|
+
SEARCH_OPERS[oper].last.call(data)
|
211
|
+
]
|
212
|
+
)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
unless(havings.blank?)
|
218
|
+
ary = ["(#{havings.map(&:first).join(') AND (')})", *havings.map(&:last)]
|
219
|
+
rel = defined?(ActiveRecord::Relation) && rel.is_a?(ActiveRecord::Relation) ? rel.having(ary) : rel.scoped(:having => ary)
|
220
|
+
end
|
221
|
+
rel
|
222
|
+
end
|
223
|
+
|
224
|
+
# klass:: ActiveRecord::Base class or ActiveRecord::Relation
|
225
|
+
# fields:: Fields used within grid
|
226
|
+
# Creates a result Hash in the structure the grid is expecting
|
227
|
+
# TODO: Calling #length is less than ideal on large datasets, but it is needed
|
228
|
+
# for cases where we are grouping items up for results. Perhaps set an optional
|
229
|
+
# flag to enable #length usage and use #count by default
|
230
|
+
def create_result_hash(unsorted, klass, fields)
|
231
|
+
if(defined?(ActiveRecord::Relation) && klass.is_a?(ActiveRecord::Relation))
|
232
|
+
dbres = klass.limit(params[:rows].to_i).offset(params[:rows].to_i * (params[:page].to_i - 1)).all
|
233
|
+
if(unsorted.respond_to?(:group_values) && unsorted.group_values.size > 0)
|
234
|
+
total = klass.connection.execute("SELECT COUNT(*) as count from (#{unsorted.to_sql}) AS countable_query").map.first['count'].to_i
|
235
|
+
end
|
236
|
+
else
|
237
|
+
dbres = klass.find(:all, :limit => params[:rows], :offset => (params[:rows].to_i * (params[:page].to_i - 1)))
|
238
|
+
if(unsorted.respond_to?(:proxy_options) && unsorted.proxy_options[:group].present?)
|
239
|
+
total = klass.connection.execute("SELECT COUNT(*) as count from (#{unsorted.send(:construct_finder_sql, {})}) AS countable_query").map.first['count'].to_i
|
240
|
+
end
|
241
|
+
end
|
242
|
+
total = unsorted.count unless total
|
243
|
+
rows = params[:rows].to_i
|
244
|
+
total_pages = rows > 0 ? (total.to_f / rows).ceil : 0
|
245
|
+
res = {'total' => total_pages, 'page' => params[:page], 'records' => total}
|
246
|
+
calls = fields.is_a?(Array) ? fields : fields.is_a?(Hash) ? fields.keys : nil
|
247
|
+
maps = fields.is_a?(Hash) ? fields : nil
|
248
|
+
res['rows'] = dbres.map do |row|
|
249
|
+
hsh = {}
|
250
|
+
calls.each do |method|
|
251
|
+
value = method.to_s.split('.').inject(row) do |result,meth|
|
252
|
+
if(result.try(:respond_to?, meth))
|
253
|
+
result.send(meth)
|
254
|
+
else
|
255
|
+
nil
|
256
|
+
end
|
257
|
+
end
|
258
|
+
if(fields.is_a?(Hash) && fields[method][:formatter].is_a?(Proc))
|
259
|
+
value = fields[method][:formatter].call(value, row)
|
260
|
+
end
|
261
|
+
hsh[method] = value
|
262
|
+
end
|
263
|
+
hsh
|
264
|
+
end
|
265
|
+
res
|
266
|
+
end
|
267
|
+
|
268
|
+
private
|
269
|
+
|
270
|
+
def database_name_by_string(string, klass, fields)
|
271
|
+
if(fields.is_a?(Hash) && fields[string].try(:[], :where))
|
272
|
+
fields[string][:where]
|
273
|
+
else
|
274
|
+
parts = string.split('.')
|
275
|
+
if(parts.size > 1)
|
276
|
+
parts = parts[-2,2]
|
277
|
+
"#{parts.first.pluralize}.#{parts.last}"
|
278
|
+
else
|
279
|
+
"#{klass.table_name}.#{parts.first}"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
ActionController::Base.send :include, JqGridRails::Controller
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module JqGridRails
|
2
|
+
module Generators
|
3
|
+
|
4
|
+
# grid:: JqGridRails::JqGrid instance
|
5
|
+
# Outputs javascript to build grid
|
6
|
+
def jq_grid(grid)
|
7
|
+
self << grid.build
|
8
|
+
end
|
9
|
+
alias_method :jqgrid, :jq_grid
|
10
|
+
|
11
|
+
# dom_id:: DOM ID of grid
|
12
|
+
# Instructs grid to reload itself
|
13
|
+
def reload_grid(dom_id)
|
14
|
+
dom_id = "##{dom_id}" unless dom_id.start_with?('#')
|
15
|
+
self << "jQuery('#{dom_id}').trigger('reloadGrid');"
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
if(defined?(ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods))
|
22
|
+
ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods.send :include, JqGridRails::Generators
|
23
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
#TODO: :with_row option is currently only applied to remote calls. Needs to be updated
|
2
|
+
# for non-ajax calls by adding dynamic form updates to include inputs for row data
|
3
|
+
require 'rails_javascript_helpers'
|
4
|
+
require 'action_view/helpers/form_tag_helper'
|
5
|
+
|
6
|
+
module JqGridRails
|
7
|
+
module Helpers
|
8
|
+
include RailsJavaScriptHelpers
|
9
|
+
|
10
|
+
# id:: String or RawJS object
|
11
|
+
# Ensures a DOM ID is returned from id. Does simple
|
12
|
+
# string replacement client side to force a # prefix.
|
13
|
+
# Client side conversion allows the id to be a RawJS
|
14
|
+
# instance for dynamic grid IDs
|
15
|
+
def convert_dom_id(id)
|
16
|
+
RawJS.new("#{format_type_to_js(id)}.replace(/^#*/, '#')")
|
17
|
+
end
|
18
|
+
|
19
|
+
# hash:: Argument hash for callback generation
|
20
|
+
# Generates a javascript callback from a Ruby hash. Important hash keys:
|
21
|
+
# :build_callback -> [false|:item|:selection] (false will stop callback from being created and simply return original hash)
|
22
|
+
# :url -> Symbol of route method name or string for actual url
|
23
|
+
# :args -> Arguments to be used when generating URL from :url value
|
24
|
+
# :method -> Request method (defaults to 'get')
|
25
|
+
# :ajax_args -> Arguments for jQuery.ajax options hash
|
26
|
+
# :remote -> [true|false] Request should be made via ajax
|
27
|
+
# :id_replacement -> Value used for dynamic ID replacement (generally not to be altered)
|
28
|
+
# :item_id -> Use for :item type callbacks to set URL generated ID if not the generic 'id' variable
|
29
|
+
def hash_to_callback(hash)
|
30
|
+
if(hash.is_a?(Hash) && hash[:build_callback] != false && hash[:url])
|
31
|
+
case hash[:build_callback]
|
32
|
+
when :item
|
33
|
+
build_single_callback(hash)
|
34
|
+
when :selection
|
35
|
+
build_selection_callback(hash)
|
36
|
+
else
|
37
|
+
build_default_callback(hash)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
hash
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# hash:: Argument hash
|
45
|
+
# Extracts and formats argument hash for callbacks
|
46
|
+
def extract_callback_variables(hash)
|
47
|
+
@url_gen ||= JqGridRails::UrlGenerator.new
|
48
|
+
args = hash.dup
|
49
|
+
args[:ajax_args] = hash.delete(:ajax) || {}
|
50
|
+
args[:method] = args[:ajax_args][:type] || args[:ajax_args].delete(:method) || hash.delete(:method) || 'get'
|
51
|
+
if(hash[:url].is_a?(Symbol))
|
52
|
+
url_args = hash[:args].is_a?(Array) ? hash[:args] : [hash[:args]]
|
53
|
+
args[:url] = @url_gen.send(hash[:url], *(url_args.sort_by{|x,y| if(x.is_a?(Hash) && y.is_a?(Hash)) then 0 elsif(x.is_a?(Hash)) then 1 else -1 end}))
|
54
|
+
else
|
55
|
+
args[:url] = hash[:url]
|
56
|
+
end
|
57
|
+
if(hash[:args_replacements].present?)
|
58
|
+
if(hash[:args_replacements].is_a?(Hash))
|
59
|
+
args[:args_replacements] = hash[:args_replacements].map{|fake_id, js_id| "replace(#{format_type_to_js(fake_id)}, #{format_type_to_js(js_id)})" }.join('.')
|
60
|
+
unless(args[:args_replacements].blank?)
|
61
|
+
args[:args_replacements] = ".#{args[:args_replacements]}"
|
62
|
+
end
|
63
|
+
else
|
64
|
+
args[:args_replacements] = hash[:args_replacements]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
args[:ajax_args][:type] = args[:method] if hash[:remote]
|
68
|
+
args
|
69
|
+
end
|
70
|
+
|
71
|
+
# hash:: Argument hash
|
72
|
+
# Builds callback function for single selection
|
73
|
+
def build_single_callback(hash)
|
74
|
+
hash[:id_replacement] ||= '000'
|
75
|
+
hash[:args] = Array(hash[:args]) unless hash[:args].is_a?(Array)
|
76
|
+
hash[:args].push hash[:id_replacement]
|
77
|
+
args = extract_callback_variables(hash)
|
78
|
+
confirm = args.delete(:confirm)
|
79
|
+
item_id = args[:item_id].present? ? args[:item_id] : RawJS.new('id')
|
80
|
+
if(hash[:remote])
|
81
|
+
if(hash[:with_row])
|
82
|
+
args[:ajax_args] ||= {}
|
83
|
+
args[:ajax_args][:data] = {:row_data => RawJS.new("jQuery(#{convert_dom_id(@table_id)}).jqGrid('getRowData', id)")}
|
84
|
+
end
|
85
|
+
" function(id){
|
86
|
+
#{confirm_if_required(
|
87
|
+
confirm,
|
88
|
+
"jQuery.ajax(#{format_type_to_js(args[:url])}.replace(#{format_type_to_js(args[:id_replacement])}, #{format_type_to_js(item_id)})#{args[:args_replacements]}, #{format_type_to_js(args[:ajax_args])});"
|
89
|
+
)}
|
90
|
+
}
|
91
|
+
"
|
92
|
+
else
|
93
|
+
form_rand = rand(999)
|
94
|
+
" function(id){
|
95
|
+
#{csrf_token_discovery(args[:method])}
|
96
|
+
#{confirm_if_required(
|
97
|
+
confirm,
|
98
|
+
"jQuery('body').append('<form id=\"redirector_#{form_rand}\" action=\"#{args[:url]}\" method=\"#{args[:method]}\">'.replace(#{format_type_to_js(args[:id_replacement])}, #{format_type_to_js(item_id)})#{args[:args_replacements]} + csrf_token +'</form>');
|
99
|
+
jQuery('#redirector_#{form_rand}').submit();"
|
100
|
+
)}
|
101
|
+
}
|
102
|
+
"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns callable function to get current selection in array form
|
107
|
+
def selection_array(error_when_empty=true, table_id=nil)
|
108
|
+
dom_id = convert_dom_id(table_id || @table_id)
|
109
|
+
" function(){
|
110
|
+
ary = jQuery(#{dom_id}).jqGrid('getGridParam', 'selarrrow');
|
111
|
+
if(!ary.length){ ary = []; }
|
112
|
+
ary.push(jQuery(#{dom_id}).jqGrid('getGridParam', 'selrow'));
|
113
|
+
ary = jQuery.grep(ary, function(value,key){ return value != null && value.length && jQuery.inArray(value, ary) === key; });
|
114
|
+
if(!ary.length && #{format_type_to_js(error_when_empty)}){
|
115
|
+
alert('Please select items from the table first.');
|
116
|
+
}
|
117
|
+
return ary;
|
118
|
+
}
|
119
|
+
"
|
120
|
+
end
|
121
|
+
|
122
|
+
# hash:: Argument has
|
123
|
+
# Builds callback function for full selection
|
124
|
+
# NOTE: In general you will want the URL to be auto generated within jqgrid_rails. The route
|
125
|
+
# should accept an ID which will be the first ID of the current selection. An extra parameter named
|
126
|
+
# 'ids' will be provided which will be an array of all selected values, included the ID given
|
127
|
+
# to the route
|
128
|
+
def build_selection_callback(hash, table_id=nil)
|
129
|
+
dom_id = table_id || @table_id
|
130
|
+
hash[:id_replacement] ||= '000'
|
131
|
+
hash[:args] = Array(hash[:args]) unless hash[:args].is_a?(Array)
|
132
|
+
hash[:args].push hash[:id_replacement]
|
133
|
+
args = extract_callback_variables(hash)
|
134
|
+
confirm = args.delete(:confirm)
|
135
|
+
item_id = args[:item_id].present? ? args[:item_id] : RawJS.new('id')
|
136
|
+
function = "function(){
|
137
|
+
rows_func = #{selection_array(true, table_id)}
|
138
|
+
ary = rows_func();
|
139
|
+
if(!ary.length){ return false; }
|
140
|
+
"
|
141
|
+
if(hash[:remote])
|
142
|
+
args[:ajax_args][:data] = {item_id.to_s.pluralize.to_sym => RawJS.new('ary')}
|
143
|
+
if(hash[:with_row])
|
144
|
+
args[:ajax_args][:data][:row_data] = RawJS.new("jQuery(#{convert_dom_id(dom_id)}).jqGrid('getRowData')")
|
145
|
+
end
|
146
|
+
function << confirm_if_required(confirm, "jQuery.ajax(#{format_type_to_js(args[:url])}.replace(#{format_type_to_js(args[:id_replacement])}, ary[0])#{args[:args_replacements]}, #{format_type_to_js(args[:ajax_args])}); }")
|
147
|
+
else
|
148
|
+
randomizer = rand(99999)
|
149
|
+
function << "parts = ary.map(
|
150
|
+
function(item){
|
151
|
+
return '<input type=\"hidden\" name=\"#{item_id.to_s.pluralize}[]\" value=\"'+item+'\"/>';
|
152
|
+
});
|
153
|
+
#{csrf_token_discovery(args[:method])}
|
154
|
+
var target_url = #{format_type_to_js(args[:url])}.replace(#{format_type_to_js(args[:id_replacement])}, ary[0])#{args[:args_replacements]};
|
155
|
+
jQuery('body').append('<form id=\"jqgrid_redirector_#{randomizer}\" action=\"'+ target_url +'\" method=\"#{args[:method]}\">' + parts + csrf_token + '</form>');
|
156
|
+
#{confirm_if_required(confirm, "jQuery(#{format_type_to_js(format_id("jqgrid_redirector_#{randomizer}"))}).submit();")}
|
157
|
+
}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# hash:: Argument hash
|
162
|
+
# Builds a default callback based on argument hash. No interaction with
|
163
|
+
# grid is provided via this method
|
164
|
+
def build_default_callback(hash)
|
165
|
+
args = extract_callback_variables(hash)
|
166
|
+
confirm = args.delete(:confirm)
|
167
|
+
if(hash[:remote])
|
168
|
+
"function(){ #{confirm_if_required(confirm, "jQuery.ajax(#{format_type_to_js(args[:url])}#{args[:args_replacements]}, #{format_type_to_js(args[:ajax_args])});")} }"
|
169
|
+
else
|
170
|
+
randomizer = rand(99999)
|
171
|
+
output = " function(){
|
172
|
+
#{csrf_token_discovery(args[:method])}
|
173
|
+
jQuery('body').append('<form id=\"jqgrid_redirector_#{randomizer}\" action=\"#{args[:url]}#{args[:args_replacements]}\" method=\"#{args[:method]}\">' + csrf_token + '</form>');"
|
174
|
+
if(hash[:ajax_args] && hash[:ajax_args][:data])
|
175
|
+
output << "var args = #{format_type_to_js(hash[:ajax_args][:data])};
|
176
|
+
Object.keys(args).each(function(key){
|
177
|
+
jQuery('#{format_id("jqgrid_redirector_#{randomizer}")}').append(jQuery('<input/>')
|
178
|
+
.attr('type', 'hidden')
|
179
|
+
.attr('name', key)
|
180
|
+
.val(args[key])
|
181
|
+
);
|
182
|
+
});"
|
183
|
+
end
|
184
|
+
output << "#{confirm_if_required(confirm, "jQuery(#{format_type_to_js(format_id("jqgrid_redirector_#{randomizer}"))}).submit();")}
|
185
|
+
}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# key:: ondbl_click_row/on_select_row
|
190
|
+
# Sets up click event functions based on hash values
|
191
|
+
def map_click(key, options)
|
192
|
+
if(options[key].is_a?(Hash))
|
193
|
+
options[key][:build_callback] = :item
|
194
|
+
options[key] = hash_to_callback(options[key])
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# url_hash:: hash for url building
|
199
|
+
# Creates a toolbar button for the grid
|
200
|
+
# TODO: Put confirm in here
|
201
|
+
def build_toolbar_button(url_hash)
|
202
|
+
url_hash[:empty_selection] ||= url_hash[:single]
|
203
|
+
url_hash[:build_callback] ||= :selection unless url_hash[:empty_selection]
|
204
|
+
classes = ['grid_toolbar_item', 'button', 'ui-state-default', 'ui-corner-all']
|
205
|
+
s = <<-EOS
|
206
|
+
jQuery('<div class="#{(classes + Array(url_hash[:class])).compact.join(' ')}" />')
|
207
|
+
.text('#{escape_javascript(url_hash[:name])}')
|
208
|
+
.button()
|
209
|
+
.click(
|
210
|
+
#{hash_to_callback(url_hash)}
|
211
|
+
).appendTo('#t_' + #{format_type_to_js(@table_id)});
|
212
|
+
EOS
|
213
|
+
end
|
214
|
+
|
215
|
+
# options_hash:: Hash of options
|
216
|
+
# Inserts callbacks in any applicable values
|
217
|
+
def scrub_options_hash(options_hash)
|
218
|
+
options_hash.each do |key,value|
|
219
|
+
options_hash[key] = hash_to_callback(value)
|
220
|
+
end
|
221
|
+
options_hash
|
222
|
+
end
|
223
|
+
|
224
|
+
# confirm:: Confirmation string
|
225
|
+
# contents:: JS contents for if block
|
226
|
+
# Wraps contents within if block using confirm() as conditional
|
227
|
+
def confirm_if_required(confirm, contents)
|
228
|
+
string = ''
|
229
|
+
if(confirm)
|
230
|
+
string << "if(confirm(#{format_type_to_js(confirm)})){"
|
231
|
+
string << contents
|
232
|
+
string << "}"
|
233
|
+
else
|
234
|
+
string << contents
|
235
|
+
end
|
236
|
+
string
|
237
|
+
end
|
238
|
+
|
239
|
+
def csrf_token_discovery(method)
|
240
|
+
output = "var csrf_token = '';"
|
241
|
+
if(method.to_s.downcase == 'get')
|
242
|
+
output
|
243
|
+
else
|
244
|
+
output << "if(jQuery('meta[name=\"csrf-param\"]').size() > 0){
|
245
|
+
csrf_token = '<input type=\"hidden\" name=\"'+jQuery('meta[name=\"csrf-param\"]').attr('content')+'\" value=\"'+jQuery('meta[name=\"csrf-token\"]').attr('content')+'\" />';
|
246
|
+
}"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|