wice_grid 3.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG +412 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +1179 -0
  5. data/Rakefile +42 -0
  6. data/SAVED_QUERIES_HOWTO.rdoc +123 -0
  7. data/VERSION +1 -0
  8. data/lib/generators/wice_grid/templates/calendarview.css +107 -0
  9. data/lib/generators/wice_grid/templates/calendarview.js +1168 -0
  10. data/lib/generators/wice_grid/templates/icons/arrow_down.gif +0 -0
  11. data/lib/generators/wice_grid/templates/icons/arrow_up.gif +0 -0
  12. data/lib/generators/wice_grid/templates/icons/calendar_view_month.png +0 -0
  13. data/lib/generators/wice_grid/templates/icons/delete.png +0 -0
  14. data/lib/generators/wice_grid/templates/icons/expand.png +0 -0
  15. data/lib/generators/wice_grid/templates/icons/page_white_excel.png +0 -0
  16. data/lib/generators/wice_grid/templates/icons/page_white_find.png +0 -0
  17. data/lib/generators/wice_grid/templates/icons/table.png +0 -0
  18. data/lib/generators/wice_grid/templates/icons/table_refresh.png +0 -0
  19. data/lib/generators/wice_grid/templates/icons/tick_all.png +0 -0
  20. data/lib/generators/wice_grid/templates/icons/untick_all.png +0 -0
  21. data/lib/generators/wice_grid/templates/wice_grid.css +173 -0
  22. data/lib/generators/wice_grid/templates/wice_grid.yml +279 -0
  23. data/lib/generators/wice_grid/templates/wice_grid_config.rb +154 -0
  24. data/lib/generators/wice_grid/templates/wice_grid_jquery.js +161 -0
  25. data/lib/generators/wice_grid/templates/wice_grid_prototype.js +153 -0
  26. data/lib/generators/wice_grid/wice_grid_assets_jquery_generator.rb +32 -0
  27. data/lib/generators/wice_grid/wice_grid_assets_prototype_generator.rb +34 -0
  28. data/lib/grid_output_buffer.rb +52 -0
  29. data/lib/grid_renderer.rb +535 -0
  30. data/lib/helpers/js_calendar_helpers.rb +183 -0
  31. data/lib/helpers/wice_grid_misc_view_helpers.rb +113 -0
  32. data/lib/helpers/wice_grid_serialized_queries_view_helpers.rb +91 -0
  33. data/lib/helpers/wice_grid_view_helpers.rb +781 -0
  34. data/lib/js_adaptors/jquery_adaptor.rb +145 -0
  35. data/lib/js_adaptors/js_adaptor.rb +12 -0
  36. data/lib/js_adaptors/prototype_adaptor.rb +168 -0
  37. data/lib/table_column_matrix.rb +51 -0
  38. data/lib/tasks/wice_grid_tasks.rake +28 -0
  39. data/lib/view_columns.rb +486 -0
  40. data/lib/views/create.rjs +13 -0
  41. data/lib/views/create_jq.rjs +31 -0
  42. data/lib/views/delete.rjs +12 -0
  43. data/lib/views/delete_jq.rjs +26 -0
  44. data/lib/wice_grid.rb +827 -0
  45. data/lib/wice_grid_controller.rb +165 -0
  46. data/lib/wice_grid_core_ext.rb +179 -0
  47. data/lib/wice_grid_misc.rb +98 -0
  48. data/lib/wice_grid_serialized_queries_controller.rb +86 -0
  49. data/lib/wice_grid_serialized_query.rb +15 -0
  50. data/lib/wice_grid_spreadsheet.rb +33 -0
  51. data/test/.gitignore +2 -0
  52. data/test/database.yml +21 -0
  53. data/test/schema.rb +33 -0
  54. data/test/test_helper.rb +89 -0
  55. data/test/views/projects_and_people_grid.html.erb +12 -0
  56. data/test/views/projects_and_people_grid_invalid.html.erb +12 -0
  57. data/test/views/simple_projects_grid.html.erb +9 -0
  58. data/test/wice_grid_core_ext_test.rb +183 -0
  59. data/test/wice_grid_functional_test.rb +68 -0
  60. data/test/wice_grid_misc_test.rb +41 -0
  61. data/test/wice_grid_test.rb +42 -0
  62. data/test/wice_grid_view_helper_test.rb +12 -0
  63. data/wice_grid.gemspec +111 -0
  64. metadata +153 -0
@@ -0,0 +1,183 @@
1
+ module Wice #:nodoc:
2
+ module JsCalendarHelpers #:nodoc:
3
+
4
+ # Jquery
5
+
6
+ def date_calendar_jquery(initial_date, view, opts = {}, html_opts = {}) #:nodoc:
7
+ date_format = Wice::Defaults::DATE_FORMAT
8
+
9
+ options, name, date_string, dom_id, datepicker_placeholder_id, date_span_id =
10
+ prepare_data_for_calendar(opts, date_format, initial_date)
11
+
12
+ remove_date_function = %! $('##{date_span_id}').html(''); $('##{dom_id}')[0].value = ''; !
13
+
14
+ date_picker =
15
+
16
+ hidden_field_tag(name, date_string, :id => dom_id) + ' ' +
17
+
18
+ link_to_function(
19
+ content_tag(:span, date_string, :id => date_span_id),
20
+ remove_date_function,
21
+ :class => 'date_label',
22
+ :title => ::Wice::WiceGridNlMessageProvider.get_message(:DATE_STRING_TOOLTIP))
23
+
24
+ html = "<span id=\"#{datepicker_placeholder_id}\">#{date_picker}</span>"
25
+
26
+ javascript = calendar_constructor_jquery(dom_id, view, Wice::Defaults::DATE_FORMAT_JQUERY,
27
+ date_span_id, opts[:fire_event], html_opts[:title], datepicker_placeholder_id)
28
+
29
+ [html, javascript]
30
+ end
31
+
32
+ # Prototype
33
+ def date_calendar_prototype(initial_date, view, opts = {}, html_opts = {}) #:nodoc:
34
+ select_date_datetime_common_prototype(initial_date, view, opts, html_opts, false, Wice::Defaults::DATE_FORMAT)
35
+ end
36
+
37
+ def datetime_calendar_prototype(initial_date, view, opts = {}, html_opts = {}) #:nodoc:
38
+ select_date_datetime_common_prototype(initial_date, view, opts, html_opts, true, Wice::Defaults::DATETIME_FORMAT)
39
+ end
40
+
41
+ protected
42
+
43
+ # common
44
+
45
+ def prepare_data_for_calendar(opts, date_format, initial_date) #:nodoc:
46
+ options = {:prefix => 'date'}
47
+ options.merge!(opts)
48
+ name = options[:prefix]
49
+ date_string = initial_date.nil? ? '' : initial_date.strftime(date_format)
50
+ dom_id = options[:id] || name.gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '').gsub(/\./, '_').gsub(/_+/, '_')
51
+ datepicker_placeholder_id = dom_id + '_date_placeholder'
52
+ date_span_id = dom_id + '_date_view'
53
+
54
+ return options, name, date_string, dom_id, datepicker_placeholder_id, date_span_id
55
+ end
56
+
57
+ # jquery
58
+
59
+ def calendar_constructor_jquery(dom_id, view, date_format, date_span_id, fireEvent, title, datepicker_placeholder_id) #:nodoc:
60
+
61
+ javascript = %| $( "##{dom_id}" ).datepicker({\n|
62
+ javascript << %| firstDay: 1,\n|
63
+ javascript << %| showOn: "button",\n|
64
+ javascript << %| dateFormat: "#{date_format}",\n|
65
+ javascript << %| buttonImage: "#{::Wice::Defaults::CALENDAR_ICON}",\n|
66
+ javascript << %| buttonImageOnly: true,\n|
67
+ javascript << %| buttonText: "#{title}",\n|
68
+ javascript << %| changeMonth: true,\n|
69
+ javascript << %| changeYear: true,\n|
70
+ javascript << %| onSelect: function(dateText, inst) {\n|
71
+ javascript << %| $("##{date_span_id}").html(dateText);\n|
72
+ if fireEvent
73
+ javascript << %| $("##{dom_id}").trigger('wg:calendarChanged');\n|
74
+ end
75
+ javascript << %| }\n|
76
+ javascript << %| });\n|
77
+
78
+ lang = Object.const_defined?(:I18n) ? I18n.locale : nil
79
+
80
+ if Rails.env == 'development'
81
+ unless view.respond_to? :datepicker_check_done
82
+
83
+ javascript << %| if (! $.datepicker ){\n|
84
+ javascript << %| alert("Seems like you do not have jQuery datepicker (http://jqueryui.com/demos/datepicker/)|
85
+ javascript << %| installed. Either install it or set Wice::Defaults::HELPER_STYLE to :standard in |
86
+ javascript << %| wice_grid_config.rb in order to use standard Rails date helpers")\n|
87
+ javascript << %| }\n|
88
+
89
+ def view.datepicker_check_done
90
+ end
91
+ end
92
+ end
93
+
94
+ if lang
95
+ unless view.respond_to? :wg_calendar_lang_set
96
+
97
+ javascript << %| wgCalendarLangRegionalOptions = $.datepicker.regional['#{lang}'];\n|
98
+ javascript << %| if (wgCalendarLangRegionalOptions){\n|
99
+ javascript << %| delete wgCalendarLangRegionalOptions.dateFormat ;\n|
100
+ javascript << %| delete wgCalendarLangRegionalOptions.firstDate ;\n|
101
+ javascript << %| }\n|
102
+
103
+ def view.wg_calendar_lang_set
104
+ end
105
+ end
106
+ javascript << %| if (wgCalendarLangRegionalOptions){\n|
107
+ javascript << %| $( "##{dom_id}" ).datepicker("option", wgCalendarLangRegionalOptions);\n|
108
+ javascript << %| }\n|
109
+ end
110
+
111
+ javascript += %| $('##{datepicker_placeholder_id} .ui-datepicker-trigger').addClass('clickable');\n|
112
+
113
+ javascript
114
+ end
115
+
116
+ # prortotype
117
+
118
+ def calendar_constructor_prototype(popup_trigger_icon_id, view, dom_id, date_format,
119
+ date_span_id, with_time, fireEvent) #:nodoc:
120
+ javascript = ''
121
+
122
+ unless view.respond_to? :wg_calendar_lang_set
123
+ lang = Object.const_defined?(:I18n) ? I18n.locale : nil
124
+ javascript << %| Calendar.language = '#{lang}';\n| unless lang.blank?
125
+
126
+ def view.wg_calendar_lang_set
127
+ end
128
+ end
129
+
130
+
131
+ javascript << %| new Calendar({\n |
132
+ javascript << %| popupTriggerElement : "#{popup_trigger_icon_id}",\n |
133
+ javascript << %| initialDate : $('#{dom_id}').value,\n |
134
+ if fireEvent
135
+ javascript << %| onHideCallback : function(){Event.fire($(#{dom_id}), 'wg:calendarChanged')},\n |
136
+ end
137
+ javascript << %| dateFormat : "#{date_format}",\n|
138
+ unless Wice::Defaults::POPUP_PLACEMENT_STRATEGY == :trigger
139
+ javascript << %| popupPositioningStrategy : "#{Wice::Defaults::POPUP_PLACEMENT_STRATEGY}",\n|
140
+ end
141
+ if with_time
142
+ javascript << %| withTime : true,\n|
143
+ end
144
+ javascript << %| outputFields : $A(['#{date_span_id}', '#{dom_id}'])\n |
145
+ javascript << %| });\n|
146
+
147
+ javascript
148
+ end
149
+
150
+ def select_date_datetime_common_prototype(initial_date, view, opts, html_opts, with_time, date_format) #:nodoc:
151
+
152
+ options, name, date_string, dom_id, datepicker_placeholder_id, date_span_id =
153
+ prepare_data_for_calendar(opts, date_format, initial_date)
154
+
155
+ popup_trigger_icon_id = dom_id + '_trigger'
156
+
157
+ function = %! $('#{date_span_id}').innerHTML = ''; $('#{dom_id}').value = ''; !
158
+ if opts[:fire_event]
159
+ function += "Event.fire($(#{dom_id}), 'wg:calendarChanged')"
160
+ end
161
+
162
+ date_picker = image_tag(Defaults::CALENDAR_ICON,
163
+ :id => popup_trigger_icon_id,
164
+ :class => 'clickable',
165
+ :title => html_opts[:title]) +
166
+
167
+ link_to_function(
168
+ content_tag(:span, date_string, :id => date_span_id),
169
+ function,
170
+ :class => 'date_label',
171
+ :title => WiceGridNlMessageProvider.get_message(:DATE_STRING_TOOLTIP)) + ' ' +
172
+
173
+ hidden_field_tag(name, date_string, :class => 'text-input', :id => dom_id)
174
+
175
+ html = "<span id=\"#{datepicker_placeholder_id}\">#{date_picker}</span>"
176
+
177
+ javascript = calendar_constructor_prototype(popup_trigger_icon_id, view, dom_id, date_format, date_span_id, with_time, opts[:fire_event])
178
+
179
+ [html, javascript]
180
+ end
181
+
182
+ end
183
+ end
@@ -0,0 +1,113 @@
1
+ # encoding: UTF-8
2
+ module Wice
3
+ module GridViewHelper
4
+
5
+
6
+ # This method dumps all HTTP parameters related to filtering and ordering of a certain grid as hidden form fields.
7
+ # This might be required if you want to keep the state of a grid while reloading the page using other forms.
8
+ #
9
+ # The only parameter is a grid object returned by +initialize_grid+ in the controller.
10
+ def dump_filter_parameters_as_hidden_fields(grid)
11
+ unless grid.kind_of? WiceGrid
12
+ raise WiceGridArgumentError.new("dump_filter_parameters_as_hidden_fields: the parameter must be a WiceGrid instance.")
13
+ end
14
+
15
+ grid.get_state_as_parameter_value_pairs(true).collect{|param_name, value|
16
+ hidden_field_tag(param_name, value)
17
+ }.join("\n").html_safe_if_necessary
18
+ end
19
+
20
+ def dump_state(grid) #:nodoc:
21
+ debug(grid.get_state_as_parameter_value_pairs)
22
+ end
23
+
24
+
25
+ # secret but stupid weapon - takes an ActiveRecord and using reflection tries to build all the column clauses by itself.
26
+ # WiceGrid is not a scaffolding solution, I hate scaffolding and how certain idiots associate scaffolding with Rails,
27
+ # so I do not document this method to avoid contributing to this misunderstanding.
28
+ def scaffolded_grid(grid_obj, opts = {}) #:nodoc:
29
+ unless grid_obj.kind_of? WiceGrid
30
+ raise WiceGridArgumentError.new("scaffolded_grid: the parameter must be a WiceGrid instance.")
31
+ end
32
+
33
+ # debug grid.klass.column_names
34
+ columns = grid_obj.klass.column_names
35
+ if opts[:reject_attributes].is_a? Proc
36
+ columns = columns.reject{|c| opts[:reject_attributes].call(c)}
37
+ opts.delete(:reject_attributes)
38
+ elsif
39
+ columns = columns.reject{|c| c =~ opts[:reject_attributes]}
40
+ opts.delete(:reject_attributes)
41
+ end
42
+ grid(grid_obj, opts) do |g|
43
+ columns.each do |column_name|
44
+ g.column :column_name => column_name.humanize, :attribute_name => column_name do |ar|
45
+ ar.send(column_name)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ # View helper to include all stylesheets and javascript files needed for WiceGrid to function. Add it to template/layout in the
52
+ # page header section.
53
+ #
54
+ # By default the helper returns WiceGrid javascripts and stylesheets include statements only on demand, that is, only if at least one initialize_grid has been
55
+ # called in the controller. Otherwise the helper returns an empty string. However, you can force the old unconditioned behavior if you need by submitting
56
+ # parameter <tt>:load_on_demand</tt> set to +false+:
57
+ # <%= include_wice_grid_assets(:load_on_demand => false) %>
58
+ #
59
+ # By default +include_wice_grid_assets+ adds include statements for the javascript calendar widget used for date/datetime filters,
60
+ # but it's possible not to include them setting parameter +include_calendar+ to false:
61
+ # <%= include_wice_grid_assets(:include_calendar => false) %>
62
+ def include_wice_grid_assets(options = {})
63
+ opts = {:include_calendar => true, :load_on_demand => true}
64
+ options.assert_valid_keys(opts.keys)
65
+ opts.merge!(options)
66
+
67
+ if @__wice_grid_on_page || ! opts[:load_on_demand]
68
+ javascript_include_tag('wice_grid') +
69
+ stylesheet_link_tag('wice_grid') +
70
+ if opts[:include_calendar]
71
+ JsAdaptor.js_framework_specific_calendar_assets(self)
72
+ else
73
+ ''
74
+ end
75
+ end
76
+ end
77
+
78
+ # Returns a list of names of javascript files for WiceGrid thus providing compatibility with Rails asset caching.
79
+ # Mind the parameters, and the fact that Prototype has to be loaded before WiceGrid javascripts:
80
+ #
81
+ # <%= javascript_include_tag *([:defaults] + names_of_wice_grid_javascripts + [ 'ui', 'swfobject', {:cache => true}]) %>
82
+ #
83
+ # By default +names_of_wice_grid_javascripts+ returns all javascripts, but it's possible not to include calendar widget javascripts by
84
+ # setting parameter <tt>:include_calendar</tt> to +false+.
85
+ def names_of_wice_grid_javascripts(options = {})
86
+ opts = {:include_calendar => true}
87
+ options.assert_valid_keys(opts.keys)
88
+ opts.merge!(options)
89
+ res = ['wice_grid']
90
+ n = js_framework_specific_calendar_js_name
91
+ res << n if n && opts[:include_calendar]
92
+ res
93
+ end
94
+
95
+ # Returns a list of names of stylesheet files for WiceGrid thus providing compatibility with Rails asset caching.
96
+ # Mind the parameters, for example:
97
+ #
98
+ # <%= stylesheet_link_tag *(['core', 'modalbox'] + names_of_wice_grid_stylesheets + [ {:cache => true}]) %>
99
+ #
100
+ # By default +names_of_wice_grid_stylesheets+ returns all javascripts, but it's possible not to include calendar widget javascripts by
101
+ # setting parameter <tt>:include_calendar</tt> to +false+.
102
+ def names_of_wice_grid_stylesheets(options = {})
103
+ opts = {:include_calendar => true}
104
+ options.assert_valid_keys(opts.keys)
105
+ opts.merge!(options)
106
+ res = ['wice_grid']
107
+ n = js_framework_specific_calendar_css_name
108
+ res << n if n && opts[:include_calendar]
109
+ res
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: UTF-8
2
+ module Wice
3
+ module GridViewHelper
4
+
5
+ # View helper to render the list of saved queries and the form to create a new query.
6
+ # Parameters:
7
+ # * <tt>:extra_parameters</tt> - a hash of additional parameters to use when creating a new query object.
8
+ # Read section "Adding Application Specific Logic to Saving/Restoring Queries" in README for more details.
9
+ def saved_queries_panel(grid, opts = {})
10
+ unless grid.kind_of? WiceGrid
11
+ raise WiceGridArgumentError.new("saved_queries_panel: the parameter must be a WiceGrid instance.")
12
+ end
13
+
14
+ options = {:extra_parameters => {}}
15
+ opts.assert_valid_keys(options.keys)
16
+ options.merge!(opts)
17
+
18
+ grid_name = grid.name
19
+ id_and_name = "#{grid_name}_saved_query_name"
20
+ base_path_to_query_controller = create_serialized_query_url(:grid_name => grid_name)
21
+
22
+ parameters = grid.get_state_as_parameter_value_pairs
23
+
24
+ options[:extra_parameters].each do |k, v|
25
+ parameters << [ CGI.unescape({:extra => {k => ''}}.to_query.sub(/=$/,'')) , v.to_s ]
26
+ end
27
+ parameters << ['authenticity_token', form_authenticity_token]
28
+ notification_messages_id = "#{grid_name}_notification_messages"
29
+ (%! <div class="wice_grid_query_panel"><h3>#{WiceGridNlMessageProvider.get_message(:SAVED_QUERY_PANEL_TITLE)}</h3>! +
30
+ saved_queries_list(grid_name, grid.saved_query, options[:extra_parameters]) +
31
+ %!<div id="#{notification_messages_id}" onmouseover="#{Wice::JsAdaptor.fade_this(notification_messages_id)}"></div>! +
32
+ if block_given?
33
+ view, ids = yield
34
+ view
35
+ else
36
+ ''
37
+ end +
38
+ text_field_tag(id_and_name, '', :size => 20, :onkeydown=>'', :id => id_and_name) +
39
+ button_to_function(WiceGridNlMessageProvider.get_message(:SAVE_QUERY_BUTTON_LABEL), "#{grid_name}_save_query()" ) +
40
+ '</div>' +
41
+ javascript_tag do
42
+ JsAdaptor.call_to_save_query_and_key_event_initialization_for_saving_queries(
43
+ id_and_name, grid_name, base_path_to_query_controller, parameters.to_json, ids.to_json
44
+ ).html_safe
45
+ end
46
+ ).html_safe
47
+ end
48
+
49
+ def saved_queries_list(grid_name, saved_query = nil, extra_parameters = {}) #:nodoc:
50
+
51
+ link_title = WiceGridNlMessageProvider.get_message(:SAVED_QUERY_LINK_TITLE)
52
+ deletion_confirmation = WiceGridNlMessageProvider.get_message(:SAVED_QUERY_DELETION_CONFIRMATION)
53
+ deletion_link_title = WiceGridNlMessageProvider.get_message(:SAVED_QUERY_DELETION_LINK_TITLE)
54
+
55
+ query_store_model = ::Wice::get_query_store_model
56
+ currently_loaded_query_id = saved_query ? saved_query.id : nil
57
+ with = extra_parameters.nil? ? nil : "'" + {:extra => extra_parameters}.to_query + "'"
58
+
59
+ %!<ul id="#{grid_name}_query_list" class="query_list"> ! +
60
+ query_store_model.list(grid_name, controller).collect do |sq|
61
+ link_opts = {:class => 'query_load_link', :title => "#{link_title} #{sq.name}"}
62
+ link_opts[:class] += ' current' if saved_query == sq
63
+ "<li>"+
64
+ link_to(
65
+ image_tag(Defaults::DELETE_QUERY_ICON),
66
+ delete_serialized_query_path(
67
+ :grid_name => grid_name,
68
+ :id => sq.id,
69
+ :current => currently_loaded_query_id,
70
+ :extra => extra_parameters
71
+ ),
72
+ :remote => true,
73
+ :confirm => deletion_confirmation,
74
+ :title => "#{deletion_link_title} #{sq.name}",
75
+ :with => 'with'
76
+ ) + ' &nbsp; ' +
77
+ link_to_function(h(sq.name),
78
+ %/ if (typeof(#{grid_name}) != "undefined") #{grid_name}.load_query(#{sq.id}) /,
79
+ link_opts) +
80
+ if sq.respond_to? :description
81
+ desc = sq.description
82
+ desc.blank? ? '' : " <i>#{desc}</i>"
83
+ else
84
+ ''
85
+ end +
86
+ '</li>'
87
+ end.join('') + '</ul>'
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,781 @@
1
+ # encoding: UTF-8
2
+ module Wice
3
+ module GridViewHelper
4
+
5
+ # View helper for rendering the grid.
6
+ #
7
+ # The first parameter is a grid object returned by +initialize_grid+ in the controller.
8
+ #
9
+ # The second parameter is a hash of options:
10
+ # * <tt>:table_html_attrs</tt> - a hash of HTML attributes to be included into the <tt>table</tt> tag.
11
+ # * <tt>:class</tt> - a shortcut for <tt>:table_html_attrs => {:class => 'css_class'}</tt>
12
+ # * <tt>:header_tr_html_attrs</tt> - a hash of HTML attributes to be included into the first <tt>tr</tt> tag
13
+ # (or two first <tt>tr</tt>'s if the filter row is present).
14
+ # * <tt>:show_filters</tt> - defines when the filter is shown. Possible values are:
15
+ # * <tt>:when_filtered</tt> - the filter is shown when the current table is the result of filtering
16
+ # * <tt>:always</tt> or <tt>true</tt> - show the filter always
17
+ # * <tt>:no</tt> or <tt>false</tt> - never show the filter
18
+ # * <tt>:upper_pagination_panel</tt> - a boolean value which defines whether there is an additional pagination
19
+ # panel on top of the table. By default it is false.
20
+ # * <tt>:extra_request_parameters</tt> - a hash which will be added as additional HTTP request parameters to all
21
+ # links generated by the grid, be it sorting links, filters, or the 'Reset Filter' icon.
22
+ # Please note that WiceGrid respects and retains all request parameters already present in the URL which
23
+ # formed the page, so there is no need to enumerate them in <tt>:extra_request_parameters</tt>. A typical
24
+ # usage of <tt>:extra_request_parameters</tt> is a page with javascript tabs - changing the active tab
25
+ # does not reload the page, but if one such tab contains a WiceGrid, it could be required that if the user
26
+ # orders or filters the grid, the result page should have the tab with the grid activated. For this we
27
+ # need to send an additional parameter specifying from which tab the request was generated.
28
+ # * <tt>:sorting_dependant_row_cycling</tt> - When set to true (by default it is false) the row styles +odd+
29
+ # and +even+ will be changed only when the content of the cell belonging to the sorted column changes.
30
+ # In other words, rows with identical values in the ordered column will have the same style (color).
31
+ # * <tt>:erb_mode</tt> - can be <tt>true</tt> or <tt>false</tt>. Defines the style of the helper method
32
+ # in the view. The default is <tt>false</tt>.
33
+ # * <tt>:allow_showing_all_records</tt> - allow or prohibit the "All Records" mode.
34
+ # * <tt>:hide_reset_button</tt> - Do not show the default Filter Reset button.
35
+ # Useful when using a custom reset button (helper +reset_grid_javascript+).
36
+ # By default it is false.
37
+ # * <tt>:hide_submit_button</tt> - Do not show the default Filter Submit button.
38
+ # Useful when using a custom submit button (helper +submit_grid_javascript+).
39
+ # By default it is false.
40
+ # * <tt>:hide_csv_button</tt> - a boolean value which defines whether the default Export To CSV button
41
+ # should be rendered. Useful when using a custom Export To CSV button (helper +export_to_csv_javascript+).
42
+ # By default it is false.
43
+ # Please read README for more insights.
44
+ #
45
+ # The block contains definitions of grid columns using the +column+ method sent to the object yielded into
46
+ # the block. In other words, the value returned by each of the blocks defines the content of a cell, the
47
+ # first block is called for cells of the first column for each row (each ActiveRecord instance), the
48
+ # second block is called for cells of the second column, and so on. See the example:
49
+ #
50
+ # <%= grid(@accounts_grid, :table_html_attrs => {:class => 'grid_style', :id => 'accounts_grid'}, :header_tr_html_attrs => {:class => 'grid_headers'}) do |g|
51
+ #
52
+ # g.column :column_name => 'Username', :attribute_name => 'username' do |account|
53
+ # account.username
54
+ # end
55
+ #
56
+ # g.column :column_name => 'application_account.field.identity_id'._, :attribute_name => 'firstname', :model_class => Person do |account|
57
+ # link_to(account.identity.name, identity_path(account.identity))
58
+ # end
59
+ #
60
+ # g.column do |account|
61
+ # link_to('Edit', edit_account_path(account))
62
+ # end
63
+ #
64
+ # end -%>
65
+ #
66
+ # The helper may have two styles defined by the +erb_mode+ parameter to the +initialize_grid+ in the contoller.
67
+ # By default (<tt>erb_mode = false</tt>) this is a simple helper surrounded by <tt><%=</tt> and <tt>%></tt>:
68
+ #
69
+ # <%= grid(@countries_grid) do |g|
70
+ #
71
+ # g.column :column_name => 'Name', :attribute_name => 'name' do |country|
72
+ # link_to(country.name, country_path(country))
73
+ # end
74
+ #
75
+ # g.column :column_name => 'Numeric Code', :attribute_name => 'numeric_code' do |country|
76
+ # country.numeric_code
77
+ # end
78
+ #
79
+ # end -%>
80
+ #
81
+ #
82
+ #
83
+ # The second style (<tt>erb_mode = true</tt>) is called <em>ERB mode</em> and it allows to embed any ERB content inside blocks,
84
+ # which is basically the style of the
85
+ # <tt>form_for</tt> helper, only <tt>form_for</tt> takes one block, while inside the <tt>grid</tt> block there are other method calls taking
86
+ # blocks as parameters:
87
+ #
88
+ # <% grid(@countries_grid) do |g|
89
+ #
90
+ # <% g.column :column_name => 'Name', :attribute_name => 'name' do |country| %>
91
+ # <b>Name: <%= link_to(country.name, country_path(country)) %></b>
92
+ # <% end %>
93
+ #
94
+ # <% g.column :column_name => 'Numeric Code', :attribute_name => 'numeric_code' do |country| %>
95
+ # <i>Numeric Code: <%= country.numeric_code %></i>
96
+ # <% end %>
97
+ #
98
+ # <% end -%>
99
+ #
100
+ # This mode can be usable if you like to have much HTML code inside cells.
101
+ #
102
+ # Please remember that in this mode the helper opens with <tt><%</tt> instead of <tt><%=</tt>, similar to <tt>form_for</tt>.
103
+ #
104
+ # Defaults for parameters <tt>:show_filters</tt> and <tt>:upper_pagination_panel</tt>
105
+ # can be changed in <tt>lib/wice_grid_config.rb</tt> using constants <tt>Wice::Defaults::SHOW_FILTER</tt> and
106
+ # <tt>WiceGrid::Defaults::SHOW_UPPER_PAGINATION_PANEL</tt>, this is convenient if you want to set a project wide setting
107
+ # without having to repeat it for every grid instance.
108
+ #
109
+ # Pease read documentation about the +column+ method to achieve the enlightenment.
110
+
111
+ def grid(grid, opts = {}, &block)
112
+ unless grid.class == WiceGrid
113
+ raise WiceGridArgumentError.new("The first argument for the grid helper must be an instance of the WiceGrid class")
114
+ end
115
+
116
+ if grid.output_buffer
117
+ if grid.output_buffer == true
118
+ raise WiceGridException.new("Second occurence of grid helper with the same grid object. " +
119
+ "Did you intend to use detached filters and forget to define them?")
120
+ else
121
+ return grid.output_buffer
122
+ end
123
+ end
124
+
125
+ options = {
126
+ :allow_showing_all_records => Defaults::ALLOW_SHOWING_ALL_QUERIES,
127
+ :class => nil,
128
+ :erb_mode => Defaults::ERB_MODE,
129
+ :extra_request_parameters => {},
130
+ :header_tr_html_attrs => {},
131
+ :hide_reset_button => false,
132
+ :hide_submit_button => false,
133
+ :hide_csv_button => false,
134
+ :show_filters => Defaults::SHOW_FILTER,
135
+ :sorting_dependant_row_cycling => false,
136
+ :table_html_attrs => {},
137
+ :upper_pagination_panel => Defaults::SHOW_UPPER_PAGINATION_PANEL
138
+ }
139
+
140
+ opts.assert_valid_keys(options.keys)
141
+
142
+ options.merge!(opts)
143
+
144
+ options[:show_filters] = :no if options[:show_filters] == false
145
+ options[:show_filters] = :always if options[:show_filters] == true
146
+
147
+ options[:table_html_attrs].add_or_append_class_value!('wice_grid', true)
148
+
149
+ if options[:class]
150
+ options[:table_html_attrs].add_or_append_class_value!(options[:class])
151
+ options.delete(:class)
152
+ end
153
+
154
+ rendering = GridRenderer.new(grid, self)
155
+ rendering.erb_mode = options[:erb_mode]
156
+
157
+ block.call(rendering) # calling block containing column() calls
158
+
159
+ reuse_last_column_for_filter_buttons =
160
+ Defaults::REUSE_LAST_COLUMN_FOR_FILTER_ICONS && rendering.last_column_for_html.capable_of_hosting_filter_related_icons?
161
+
162
+ if grid.output_csv?
163
+ content = grid_csv(grid, rendering)
164
+ else
165
+ # If blank_slate is defined we don't show any grid at all
166
+ if rendering.blank_slate_handler && grid.resultset.size == 0 && ! grid.filtering_on?
167
+ content = generate_blank_slate(grid, rendering)
168
+ return prepare_result(rendering, grid, content, block)
169
+ end
170
+
171
+ content = grid_html(grid, options, rendering, reuse_last_column_for_filter_buttons)
172
+ end
173
+
174
+ grid.view_helper_finished = true
175
+ prepare_result(rendering, grid, content, block)
176
+ end
177
+
178
+ def prepare_result(rendering, grid, content, block) #:nodoc:
179
+ if rendering.erb_mode
180
+ # true in this case is a sign that grid_html has run in a normal mode, i.e. without detached filters
181
+ if grid.output_buffer.nil? || grid.output_buffer == true
182
+ content = content.to_s
183
+ if Rails.respond_to?(:version) && Rails.version.to_f >= 2.2
184
+ return concat(content)
185
+ else
186
+ return concat(content, block.binding)
187
+ end
188
+ else
189
+ # this way we're sending an empty string and setting flag stubborn_output_mode of GridOutputBuffer to false
190
+ return grid.output_buffer.to_s
191
+ end
192
+ else
193
+ return content
194
+ end
195
+ end
196
+
197
+
198
+ def generate_blank_slate(grid, rendering) #:nodoc:
199
+ buff = GridOutputBuffer.new
200
+
201
+ buff << if rendering.blank_slate_handler.is_a?(Proc)
202
+ call_block_as_erb_or_ruby(rendering, rendering.blank_slate_handler, nil)
203
+ elsif rendering.blank_slate_handler.is_a?(Hash)
204
+ render(rendering.blank_slate_handler)
205
+ else
206
+ rendering.blank_slate_handler
207
+ end
208
+
209
+ if rendering.find_one_for(:in_html){|column| column.detach_with_id}
210
+ buff.stubborn_output_mode = true
211
+ buff.return_empty_strings_for_nonexistent_filters = true
212
+ grid.output_buffer = buff
213
+ end
214
+ buff
215
+ end
216
+
217
+ def call_block_as_erb_or_ruby(rendering, block, ar) #:nodoc:
218
+ if rendering.erb_mode
219
+ capture(ar, &block)
220
+ else
221
+ block.call(ar)
222
+ end
223
+ end
224
+
225
+ # the longest method? :(
226
+ def grid_html(grid, options, rendering, reuse_last_column_for_filter_buttons) #:nodoc:
227
+
228
+ table_html_attrs, header_tr_html_attrs = options[:table_html_attrs], options[:header_tr_html_attrs]
229
+
230
+ cycle_class = nil
231
+ sorting_dependant_row_cycling = options[:sorting_dependant_row_cycling]
232
+
233
+ content = GridOutputBuffer.new
234
+ # Ruby 1.9.1
235
+ content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
236
+
237
+ content << %!<div class="wice_grid_container" id="#{grid.name}"><div id="#{grid.name}_title">!
238
+ content << content_tag(:h3, grid.saved_query.name) if grid.saved_query
239
+ content << "</div><table #{tag_options(table_html_attrs, true)}>"
240
+ content << "<thead>"
241
+
242
+ no_filters_at_all = (options[:show_filters] == :no || rendering.no_filter_needed?) ? true: false
243
+
244
+ if no_filters_at_all
245
+ no_rightmost_column = no_filter_row = no_filters_at_all
246
+ else
247
+ no_rightmost_column = no_filter_row = (options[:show_filters] == :no || rendering.no_filter_needed_in_main_table?) ? true: false
248
+ end
249
+
250
+ no_rightmost_column = true if reuse_last_column_for_filter_buttons
251
+
252
+ pagination_panel_content_html, pagination_panel_content_js = nil, nil
253
+ if options[:upper_pagination_panel]
254
+ content << rendering.pagination_panel(no_rightmost_column, options[:hide_csv_button]) do
255
+ pagination_panel_content_html, pagination_panel_content_js =
256
+ pagination_panel_content(grid, options[:extra_request_parameters], options[:allow_showing_all_records])
257
+ pagination_panel_content_html
258
+ end
259
+ end
260
+
261
+ title_row_attrs = header_tr_html_attrs.clone
262
+ title_row_attrs.add_or_append_class_value!('wice_grid_title_row', true)
263
+
264
+ content << %!<tr #{tag_options(title_row_attrs, true)}>!
265
+
266
+ filter_row_id = grid.name + '_filter_row'
267
+
268
+ # first row of column labels with sorting links
269
+
270
+ filter_shown = if options[:show_filters] == :when_filtered
271
+ grid.filtering_on?
272
+ elsif options[:show_filters] == :always
273
+ true
274
+ end
275
+
276
+ cached_javascript = []
277
+
278
+ rendering.each_column_aware_of_one_last_one(:in_html) do |column, last|
279
+
280
+ column_name = column.column_name
281
+ if column_name.is_a? Array
282
+ column_name, js = column_name
283
+ cached_javascript << js
284
+ end
285
+
286
+ if column.attribute_name && column.allow_ordering
287
+
288
+ css_class = grid.filtered_by?(column) ? 'active_filter' : nil
289
+
290
+ direction = 'asc'
291
+ link_style = nil
292
+ if grid.ordered_by?(column)
293
+ css_class = css_class.nil? ? 'sorted' : css_class + ' sorted'
294
+ link_style = grid.order_direction
295
+ direction = 'desc' if grid.order_direction == 'asc'
296
+ end
297
+
298
+ col_link = link_to(
299
+ column_name,
300
+ rendering.column_link(column, direction, params, options[:extra_request_parameters]),
301
+ :class => link_style)
302
+ content << content_tag(:th, col_link, Hash.make_hash(:class, css_class))
303
+ column.css_class = css_class
304
+ else
305
+ if reuse_last_column_for_filter_buttons && last
306
+ content << content_tag(:th,
307
+ hide_show_icon(filter_row_id, grid, filter_shown, no_filter_row, options[:show_filters], rendering),
308
+ :class => 'hide_show_icon'
309
+ )
310
+ else
311
+ content << content_tag(:th, column_name)
312
+ end
313
+ end
314
+ end
315
+
316
+ content << content_tag(:th,
317
+ hide_show_icon(filter_row_id, grid, filter_shown, no_filter_row, options[:show_filters], rendering),
318
+ :class => 'hide_show_icon'
319
+ ) unless no_rightmost_column
320
+
321
+ content << '</tr>'
322
+ # rendering first row end
323
+
324
+
325
+ unless no_filters_at_all # there are filters, we don't know where, in the table or detached
326
+ if no_filter_row # they are all detached
327
+ content.stubborn_output_mode = true
328
+ rendering.each_column(:in_html) do |column|
329
+ if column.filter_shown?
330
+ filter_html_code, filter_js_code = column.render_filter
331
+ filter_html_code = filter_html_code.html_safe_if_necessary
332
+ cached_javascript << filter_js_code
333
+ content.add_filter(column.detach_with_id, filter_html_code)
334
+ end
335
+ end
336
+
337
+ else # some filters are present in the table
338
+
339
+ filter_row_attrs = header_tr_html_attrs.clone
340
+ filter_row_attrs.add_or_append_class_value!('wice_grid_filter_row', true)
341
+ filter_row_attrs['id'] = filter_row_id
342
+
343
+ content << %!<tr #{tag_options(filter_row_attrs, true)} !
344
+ content << 'style="display:none"' unless filter_shown
345
+ content << '>'
346
+
347
+ rendering.each_column_aware_of_one_last_one(:in_html) do |column, last|
348
+ if column.filter_shown?
349
+
350
+ filter_html_code, filter_js_code = column.render_filter
351
+ filter_html_code = filter_html_code.html_safe_if_necessary
352
+ cached_javascript << filter_js_code
353
+ if column.detach_with_id
354
+ content.stubborn_output_mode = true
355
+ content << content_tag(:th, '', Hash.make_hash(:class, column.css_class))
356
+ content.add_filter(column.detach_with_id, filter_html_code)
357
+ else
358
+ content << content_tag(:th, filter_html_code, Hash.make_hash(:class, column.css_class))
359
+ end
360
+ else
361
+ if reuse_last_column_for_filter_buttons && last
362
+ content << content_tag(:th,
363
+ reset_submit_buttons(options, grid, rendering),
364
+ Hash.make_hash(:class, column.css_class).add_or_append_class_value!('filter_icons')
365
+ )
366
+ else
367
+ content << content_tag(:th, '', Hash.make_hash(:class, column.css_class))
368
+ end
369
+ end
370
+ end
371
+ unless no_rightmost_column
372
+ content << content_tag(:th, reset_submit_buttons(options, grid, rendering), :class => 'filter_icons' )
373
+ end
374
+ content << '</tr>'
375
+ end
376
+ end
377
+
378
+ rendering.each_column(:in_html) do |column|
379
+ unless column.css_class.blank?
380
+ column.td_html_attrs.add_or_append_class_value!(column.css_class)
381
+ end
382
+ end
383
+
384
+ content << '</thead><tfoot>'
385
+ content << rendering.pagination_panel(no_rightmost_column, options[:hide_csv_button]) do
386
+ if pagination_panel_content_html
387
+ pagination_panel_content_html
388
+ else
389
+ pagination_panel_content_html, pagination_panel_content_js =
390
+ pagination_panel_content(grid, options[:extra_request_parameters], options[:allow_showing_all_records])
391
+ pagination_panel_content_html
392
+ end
393
+ end
394
+
395
+ content << '</tfoot><tbody>'
396
+ cached_javascript << pagination_panel_content_js
397
+
398
+ # rendering rows
399
+ cell_value_of_the_ordered_column = nil
400
+ previous_cell_value_of_the_ordered_column = nil
401
+
402
+ grid.each do |ar| # rows
403
+
404
+ before_row_output = if rendering.before_row_handler
405
+ call_block_as_erb_or_ruby(rendering, rendering.before_row_handler, ar)
406
+ else
407
+ nil
408
+ end
409
+
410
+ after_row_output = if rendering.after_row_handler
411
+ call_block_as_erb_or_ruby(rendering, rendering.after_row_handler, ar)
412
+ else
413
+ nil
414
+ end
415
+
416
+ row_content = ''
417
+ rendering.each_column(:in_html) do |column|
418
+ cell_block = column.cell_rendering_block
419
+
420
+ opts = column.td_html_attrs.clone
421
+
422
+ column_block_output = if column.class == Wice::ActionViewColumn
423
+ cell_block.call(ar, params)
424
+ else
425
+ call_block_as_erb_or_ruby(rendering, cell_block, ar)
426
+ end
427
+
428
+ if column_block_output.kind_of?(Array)
429
+
430
+ unless column_block_output.size == 2
431
+ raise WiceGridArgumentError.new('When WiceGrid column block returns an array it is expected to contain 2 elements only - '+
432
+ 'the first is the contents of the table cell and the second is a hash containing HTML attributes for the <td> tag.')
433
+ end
434
+
435
+ column_block_output, additional_opts = column_block_output
436
+
437
+ unless additional_opts.is_a?(Hash)
438
+ raise WiceGridArgumentError.new('When WiceGrid column block returns an array its second element is expected to be a ' +
439
+ "hash containing HTML attributes for the <td> tag. The returned value is #{additional_opts.inspect}. Read documentation.")
440
+ end
441
+
442
+ additional_css_class = nil
443
+ if additional_opts.has_key?(:class)
444
+ additional_css_class = additional_opts[:class]
445
+ additional_opts.delete(:class)
446
+ elsif additional_opts.has_key?('class')
447
+ additional_css_class = additional_opts['class']
448
+ additional_opts.delete('class')
449
+ end
450
+ opts.merge!(additional_opts)
451
+ opts.add_or_append_class_value!(additional_css_class) unless additional_css_class.blank?
452
+ end
453
+
454
+ if sorting_dependant_row_cycling && column.attribute_name && grid.ordered_by?(column)
455
+ cell_value_of_the_ordered_column = column_block_output
456
+ end
457
+ row_content += content_tag(:td, column_block_output, opts)
458
+ end
459
+
460
+ row_attributes = rendering.get_row_attributes(ar)
461
+
462
+ if sorting_dependant_row_cycling
463
+ cycle_class = cycle('odd', 'even', :name => grid.name) if cell_value_of_the_ordered_column != previous_cell_value_of_the_ordered_column
464
+ previous_cell_value_of_the_ordered_column = cell_value_of_the_ordered_column
465
+ else
466
+ cycle_class = cycle('odd', 'even', :name => grid.name)
467
+ end
468
+
469
+ row_attributes.add_or_append_class_value!(cycle_class)
470
+
471
+ content << before_row_output if before_row_output
472
+ content << "<tr #{tag_options(row_attributes)}>#{row_content}"
473
+ content << content_tag(:td, '') unless no_rightmost_column
474
+ content << '</tr>'
475
+ content << after_row_output if after_row_output
476
+ end
477
+
478
+ content << '</tbody></table></div>'
479
+
480
+ base_link_for_filter, base_link_for_show_all_records = rendering.base_link_for_filter(controller, options[:extra_request_parameters])
481
+
482
+ link_for_export = rendering.link_for_export(controller, 'csv', options[:extra_request_parameters])
483
+
484
+ parameter_name_for_query_loading = {grid.name => {:q => ''}}.to_query
485
+ parameter_name_for_focus = {grid.name => {:foc => ''}}.to_query
486
+
487
+ prototype_and_js_version_check = if Rails.env == 'development'
488
+ %$ if (typeof(WiceGridProcessor) == "undefined"){\n$ +
489
+ %$ alert('wice_grid.js not loaded, WiceGrid cannot proceed! ' +\n$ +
490
+ %$ 'Please make sure that you include WiceGrid javascript in your page. ' +\n$ +
491
+ %$ 'Use <%= include_wice_grid_assets %> or <%= include_wice_grid_assets(:include_calendar => true) %> ' +\n$ +
492
+ %$ 'for WiceGrid javascripts and assets.')\n$ +
493
+ %$ } else if ((typeof(WiceGridProcessor._version) == "undefined") || ( WiceGridProcessor._version != "0.4.3")) {\n$ +
494
+ %$ alert("wice_grid.js in your /public is outdated, please run\\n ./script/generate wice_grid_assets_jquery\\n$ +
495
+ %$ or\\n ./script/generate wice_grid_assets_prototype\\nto update it.");\n$ +
496
+ %$ }\n$
497
+ else
498
+ ''
499
+ end
500
+
501
+ if rendering.show_hide_button_present
502
+ cached_javascript << JsAdaptor.show_hide_button_initialization(grid.name, filter_row_id)
503
+ end
504
+
505
+ if rendering.reset_button_present
506
+ cached_javascript << JsAdaptor.reset_button_initialization(grid.name, reset_grid_javascript(grid))
507
+ end
508
+
509
+ if rendering.submit_button_present
510
+ cached_javascript << JsAdaptor.submit_button_initialization(grid.name, submit_grid_javascript(grid))
511
+ end
512
+
513
+ if rendering.contains_a_text_input?
514
+ cached_javascript << JsAdaptor.enter_key_event_registration(grid.name)
515
+ end
516
+
517
+ if rendering.csv_export_icon_present
518
+ cached_javascript << JsAdaptor.csv_export_icon_initialization(grid.name)
519
+ end
520
+
521
+ if rendering.contains_auto_reloading_selects
522
+ cached_javascript << JsAdaptor.auto_reloading_selects_event_initialization(grid.name)
523
+ end
524
+
525
+ if rendering.contains_auto_reloading_inputs
526
+ cached_javascript << JsAdaptor.auto_reloading_inputs_event_initialization(grid.name)
527
+ end
528
+
529
+ if rendering.contains_auto_reloading_inputs_with_negation_checkboxes
530
+ cached_javascript << JsAdaptor.auto_reloading_inputs_with_negation_checkboxes_event_initialization(grid.name)
531
+ end
532
+
533
+ if rendering.contains_auto_reloading_calendars
534
+ cached_javascript << JsAdaptor.auto_reloading_calendar_event_initialization(grid.name)
535
+ end
536
+
537
+ if rendering.element_to_focus
538
+ cached_javascript << JsAdaptor.focus_element(rendering.element_to_focus)
539
+ end
540
+
541
+ content << javascript_tag(
542
+ JsAdaptor.dom_loaded +
543
+ %/ #{prototype_and_js_version_check}\n/ +
544
+ %/ window['#{grid.name}'] = new WiceGridProcessor('#{grid.name}', '#{base_link_for_filter}',\n/ +
545
+ %/ '#{base_link_for_show_all_records}', '#{link_for_export}', '#{parameter_name_for_query_loading}',\n/ +
546
+ %/ '#{parameter_name_for_focus}', '#{Rails.env}');\n/ +
547
+ if no_filters_at_all
548
+ ''
549
+ else
550
+ rendering.select_for(:in_html) do |vc|
551
+ vc.attribute_name and not vc.no_filter
552
+ end.collect{|column| column.yield_javascript}.join("\n")
553
+ end +
554
+ "\n" + cached_javascript.compact.join('') +
555
+ '})'
556
+ )
557
+
558
+ if content.stubborn_output_mode
559
+ grid.output_buffer = content
560
+ else
561
+ # this will serve as a flag that the grid helper has already processed the grid but in a normal mode,
562
+ # not in the mode with detached filters.
563
+ grid.output_buffer = true
564
+ end
565
+ return content
566
+ end
567
+
568
+ def hide_show_icon(filter_row_id, grid, filter_shown, no_filter_row, show_filters, rendering) #:nodoc:
569
+ grid_name = grid.name
570
+ no_filter_opening_closing_icon = (show_filters == :always) || no_filter_row
571
+
572
+ styles = ["display: block;", "display: none;"]
573
+ styles.reverse! unless filter_shown
574
+
575
+
576
+ if no_filter_opening_closing_icon
577
+ hide_icon = show_icon = ''
578
+ else
579
+
580
+
581
+ rendering.show_hide_button_present = true
582
+ filter_tooltip = WiceGridNlMessageProvider.get_message(:HIDE_FILTER_TOOLTIP)
583
+
584
+ hide_icon = content_tag(:span,
585
+ image_tag(Defaults::SHOW_HIDE_FILTER_ICON,
586
+ :title => filter_tooltip,
587
+ :alt => filter_tooltip),
588
+ :id => grid_name + '_hide_icon',
589
+ :style => styles[0],
590
+ :class => 'clickable'
591
+ )
592
+
593
+
594
+ filter_tooltip = WiceGridNlMessageProvider.get_message(:SHOW_FILTER_TOOLTIP)
595
+
596
+ show_icon = content_tag(:span,
597
+ image_tag(Defaults::SHOW_HIDE_FILTER_ICON,
598
+ :title => filter_tooltip,
599
+ :alt => filter_tooltip),
600
+ :id => grid_name + '_show_icon',
601
+ :style => styles[1],
602
+ :class => 'clickable'
603
+ )
604
+
605
+ hide_icon + show_icon
606
+ end
607
+ end
608
+
609
+ def reset_submit_buttons(options, grid, rendering) #:nodoc:
610
+ (if options[:hide_submit_button]
611
+ ''
612
+ else
613
+ rendering.submit_button_present = true
614
+ filter_tooltip = WiceGridNlMessageProvider.get_message(:FILTER_TOOLTIP)
615
+ image_tag(Defaults::FILTER_ICON, :title => filter_tooltip, :alt => filter_tooltip, :class => 'submit clickable')
616
+ end + ' ' +
617
+ if options[:hide_reset_button]
618
+ ''
619
+ else
620
+ rendering.reset_button_present = true
621
+ filter_tooltip = WiceGridNlMessageProvider.get_message(:RESET_FILTER_TOOLTIP)
622
+ image_tag(Defaults::RESET_ICON, :title => filter_tooltip, :alt => filter_tooltip, :class => 'reset clickable')
623
+ end).html_safe_if_necessary
624
+ end
625
+
626
+ # Renders a detached filter. The parameters are:
627
+ # * +grid+ the WiceGrid object
628
+ # * +filter_key+ an identifier of the filter specified in the column declaration by parameter +:detach_with_id+
629
+ def grid_filter(grid, filter_key)
630
+ unless grid.kind_of? WiceGrid
631
+ raise WiceGridArgumentError.new("submit_grid_javascript: the parameter must be a WiceGrid instance.")
632
+ end
633
+ if grid.output_buffer.nil?
634
+ raise WiceGridArgumentError.new("grid_filter: You have attempted to run 'grid_filter' before 'grid'. Read about detached filters in the documentation.")
635
+ end
636
+ if grid.output_buffer == true
637
+ raise WiceGridArgumentError.new("grid_filter: You have defined no detached filters, or you try use detached filters with" +
638
+ ":show_filters => :no (set :show_filters to :always in this case). Read about detached filters in the documentation.")
639
+ end
640
+
641
+ content_tag :span, grid.output_buffer.filter_for(filter_key), :class => "#{grid.name}_detached_filter"
642
+ end
643
+
644
+ # Returns javascript which applies current filters. The parameter is a WiceGrid instance. Use it with +button_to_function+ to create
645
+ # your Submit button.
646
+ def submit_grid_javascript(grid)
647
+ unless grid.kind_of? WiceGrid
648
+ raise WiceGridArgumentError.new("submit_grid_javascript: the parameter must be a WiceGrid instance.")
649
+ end
650
+ "#{grid.name}.process()"
651
+ end
652
+
653
+ # Returns javascript which resets the grid, clearing the state of filters.
654
+ # The parameter is a WiceGrid instance. Use it with +button_to_function+ to create
655
+ # your Reset button.
656
+ def reset_grid_javascript(grid)
657
+ unless grid.kind_of? WiceGrid
658
+ raise WiceGridArgumentError.new("reset_grid_javascript: the parameter must be a WiceGrid instance.")
659
+ end
660
+ "#{grid.name}.reset()"
661
+ end
662
+
663
+ # Returns javascript which triggers export to CSV.
664
+ # The parameter is a WiceGrid instance. Use it with +button_to_function+ to create
665
+ # your own Export To CSV button.
666
+ def export_to_csv_javascript(grid)
667
+ unless grid.kind_of? WiceGrid
668
+ raise WiceGridArgumentError.new("export_csv_javascript: the parameter must be a WiceGrid instance.")
669
+ end
670
+ "#{grid.name}.export_to_csv()"
671
+ end
672
+
673
+
674
+ def grid_csv(grid, rendering) #:nodoc:
675
+
676
+
677
+ field_separator = (grid.export_to_csv_enabled && grid.export_to_csv_enabled.is_a?(String)) ? grid.export_to_csv_enabled : ','
678
+ spreadsheet = ::Wice::Spreadsheet.new(grid.name, field_separator)
679
+
680
+ # columns
681
+ spreadsheet << rendering.column_labels(:in_csv)
682
+
683
+ # rendering rows
684
+ grid.each do |ar| # rows
685
+ row = []
686
+
687
+ rendering.each_column(:in_csv) do |column|
688
+ cell_block = column.cell_rendering_block
689
+
690
+ column_block_output = call_block_as_erb_or_ruby(rendering, cell_block, ar)
691
+
692
+ if column_block_output.kind_of?(Array)
693
+ column_block_output, additional_opts = column_block_output
694
+ end
695
+
696
+ row << column_block_output
697
+ end
698
+ spreadsheet << row
699
+ end
700
+ grid.csv_tempfile = spreadsheet.tempfile
701
+ return grid.csv_tempfile.path
702
+ end
703
+
704
+ def pagination_panel_content(grid, extra_request_parameters, allow_showing_all_records) #:nodoc:
705
+ extra_request_parameters = extra_request_parameters.clone
706
+ if grid.saved_query
707
+ extra_request_parameters["#{grid.name}[q]"] = grid.saved_query.id
708
+ end
709
+
710
+ html, js = pagination_info(grid, allow_showing_all_records)
711
+
712
+ [will_paginate(grid.resultset,
713
+ :previous_label => WiceGridNlMessageProvider.get_message(:PREVIOUS_LABEL),
714
+ :next_label => WiceGridNlMessageProvider.get_message(:NEXT_LABEL),
715
+ :param_name => "#{grid.name}[page]",
716
+ :params => extra_request_parameters).to_s +
717
+ (' <div class="pagination_status">' + html + '</div>').html_safe_if_necessary, js]
718
+ end
719
+
720
+
721
+ def show_all_link(collection_total_entries, parameters, grid_name) #:nodoc:
722
+
723
+ message = WiceGridNlMessageProvider.get_message(:ALL_QUERIES_WARNING)
724
+ confirmation = collection_total_entries > Defaults::START_SHOWING_WARNING_FROM ? "if (confirm('#{message}'))" : ''
725
+
726
+ js = JsAdaptor.show_all_link_initialization(grid_name, confirmation, parameters.to_json)
727
+
728
+ tooltip = WiceGridNlMessageProvider.get_message(:SHOW_ALL_RECORDS_TOOLTIP)
729
+ html = %/<span class="show_all_link"><a href="#" title="#{tooltip}">/ +
730
+ WiceGridNlMessageProvider.get_message(:SHOW_ALL_RECORDS_LABEL) +
731
+ '</a></span>'
732
+
733
+ [html, js]
734
+ end
735
+
736
+ def back_to_pagination_link(parameters, grid_name) #:nodoc:
737
+ pagination_override_parameter_name = "#{grid_name}[pp]"
738
+ parameters = parameters.reject{|k, v| k == pagination_override_parameter_name}
739
+
740
+ js = JsAdaptor.back_to_pagination_link_initialization(grid_name, parameters.to_json)
741
+
742
+ tooltip = WiceGridNlMessageProvider.get_message(:SWITCH_BACK_TO_PAGINATED_MODE_TOOLTIP)
743
+ html = %/ <span class="show_all_link"><a href="#" title="#{tooltip}">/ +
744
+ WiceGridNlMessageProvider.get_message(:SWITCH_BACK_TO_PAGINATED_MODE_LABEL) +
745
+ '</a></span>'
746
+ [html, js]
747
+ end
748
+
749
+ def pagination_info(grid, allow_showing_all_records) #:nodoc:
750
+ collection = grid.resultset
751
+
752
+ collection_total_entries = collection.total_entries
753
+ collection_total_entries_str = collection_total_entries.to_s
754
+ parameters = grid.get_state_as_parameter_value_pairs
755
+
756
+ js = ''
757
+ html = if (collection.total_pages < 2 && collection.length == 0)
758
+ '0'
759
+ else
760
+ parameters << ["#{grid.name}[pp]", collection_total_entries_str]
761
+
762
+ "#{collection.offset + 1}-#{collection.offset + collection.length} / #{collection_total_entries_str} " +
763
+ if (! allow_showing_all_records) || collection_total_entries <= collection.length
764
+ ''
765
+ else
766
+ res, js = show_all_link(collection_total_entries, parameters, grid.name)
767
+ res
768
+ end
769
+ end +
770
+ if grid.all_record_mode?
771
+ res, js = back_to_pagination_link(parameters, grid.name)
772
+ res
773
+ else
774
+ ''
775
+ end
776
+
777
+ [html, js]
778
+ end
779
+
780
+ end
781
+ end