jqgrid_rails 1.2.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/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
|