grapple 0.2.0 → 0.3.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 670fc0f6b9406162b5de3113dc4869e181f64b73
4
- data.tar.gz: da7f27323caa88f1847c1a58f753ca94a96d1888
2
+ SHA256:
3
+ metadata.gz: 9a76259d2dd289df0f09d714b30fa6ead30003ac4b63554c8232027f0f729317
4
+ data.tar.gz: 47fad8fb8ea7b8e3015d4598301149f0a3fb7c2a761a464be85081d0865ea339
5
5
  SHA512:
6
- metadata.gz: a6bd235b2e4ad38bb45cf7755f5b84b82e0461d2f7124e3131d11a85f64f8f57d69619d8135531c475682559e2570b4946c807796413b2c9121873ba53c37412
7
- data.tar.gz: bc324f20226c679b46f848032a5eabdf00502a395afd12ff2e61962c244304fe56a09f0da3717d22cf418cf5d64f9d5a39583e28b9b6dd40f0cf4101bfdad633
6
+ metadata.gz: de415d06be64cd90a834600fe9964f3d5df2a65e361e8175789a9a5e4d240d4d4b12ec0c061c4cbddc450708422f2da1c164c269b484ecd3b6844cb10f5d3729
7
+ data.tar.gz: 995d9c603e2f9a09ef9ede25a26af02a51aa112ae3bdb4756542d234dd2fdb07af4772a99634c4a7241e70a9831483ba6fe704205c77f4542d2e1fc49dbda986
data/README.md CHANGED
@@ -189,7 +189,7 @@ Render the table using `table_for` in `app/views/posts/_table.html.erb`
189
189
 
190
190
  ## History w/AJAX (back button)
191
191
 
192
- Requires: https://github.com/browserstate/history.js/
192
+ Uses the native `window.history` API by default. If History.js is present it will use that instead for compatibility with older browsers (https://github.com/browserstate/history.js/).
193
193
 
194
194
  ``` javascript
195
195
  // app/assets/javascripts/application.js
@@ -4,25 +4,62 @@
4
4
  var urlQuery = Grapple.Util.urlQuery,
5
5
  parseUrlQuery = Grapple.Util.parseUrlQuery;
6
6
 
7
- var GrappleHistory = function(namespace) {
8
- this.namespace = namespace;
9
- if(History.init) {
10
- // https://github.com/browserstate/history.js/
11
- this.api = History;
7
+ // History.js for backwards compatibility with older browsers
8
+ // https://github.com/browserstate/history.js/
9
+ var LegacyHistory = function() {
10
+ var api = History;
12
11
 
13
- // Initialization of history.js can be delayed
14
- // if it was do it now
15
- if(this.api.options && this.api.options.delayInit) {
16
- this.api.options.delayInit = false;
17
- this.api.init();
18
- }
12
+ // Initialization of history.js can be delayed
13
+ // if it was do it now
14
+ if(api.options && api.options.delayInit) {
15
+ api.options.delayInit = false;
16
+ api.init();
19
17
  }
20
- else {
21
- // TODO: support native history api
22
- this.api = window.history;
18
+ this.api = api;
19
+ };
20
+
21
+ LegacyHistory.prototype = {
22
+ push: function(params) {
23
+ return this.api.pushState(null, document.title, '?' + params);
24
+ },
25
+ get: function() {
26
+ return urlQuery(this.api.getState().url);
27
+ },
28
+ bind: function(callback) {
29
+ $(window).bind('statechange', callback);
30
+ },
31
+ unbind: function(callback) {
32
+ $(window).unbind('statechange', callback);
23
33
  }
24
- this.api = History;
25
- this.changeCallback = null;
34
+ };
35
+
36
+ // Native Browser History API
37
+ var ModernHistory = function() {
38
+
39
+ };
40
+
41
+ ModernHistory.prototype = {
42
+ push: function(params) {
43
+ if(window.history && window.history.pushState) {
44
+ return window.history.pushState(params, document.title, '?' + params);
45
+ }
46
+ },
47
+ get: function() {
48
+ if(window.history) {
49
+ return window.history.state || urlQuery(document.location.toString());
50
+ }
51
+ },
52
+ bind: function(callback) {
53
+ $(window).bind('popstate', callback);
54
+ },
55
+ unbind: function(callback) {
56
+ $(window).unbind('popstate', callback);
57
+ }
58
+ };
59
+
60
+ var GrappleHistory = function(grappleTable) {
61
+ this.grappleTable = grappleTable;
62
+ this._historyChangeCallback = null;
26
63
  };
27
64
 
28
65
  // Don't clutter the url with rails form parameters
@@ -30,10 +67,20 @@ GrappleHistory.IGNORE_PARAMS = { 'utf8': true, 'authenticity_token': true };
30
67
 
31
68
  GrappleHistory.prototype = {
32
69
 
33
- add: function(params) {
34
- var namespace = this.namespace;
35
- var state = this.api.getState();
36
- var historyParams = parseUrlQuery(urlQuery(state.url));
70
+ init: function() {
71
+ // Use the History.js wrapper if History.js has been loaded
72
+ this.api = typeof History !== 'undefined' && History.init ? new LegacyHistory() : new ModernHistory();
73
+ this._subscribeToTableChange();
74
+ this._subscribeToHistoryChange();
75
+ },
76
+
77
+ /**
78
+ * Add an entry to the history
79
+ * @param {String} params
80
+ */
81
+ _addHistoryEntry: function(params) {
82
+ var namespace = this.grappleTable.namespace;
83
+ var historyParams = parseUrlQuery(this.api.get());
37
84
  var newParams = parseUrlQuery(params);
38
85
 
39
86
  // Remove any parameters from the current state
@@ -57,14 +104,45 @@ GrappleHistory.prototype = {
57
104
  historyParams[x] = newParams[x];
58
105
  }
59
106
 
60
- this.api.pushState(null, document.title, '?' + $.param(historyParams));
107
+ this.api.push($.param(historyParams));
61
108
  },
62
109
 
63
- subscribe: function(callback) {
64
- var api = this.api, namespace = this.namespace;
65
- this.changeCallback = function(event) {
66
- var state = api.getState();
67
- var params = parseUrlQuery(urlQuery(state.url));
110
+ /**
111
+ * Listen for changes to the table to add them to the history
112
+ */
113
+ _subscribeToTableChange: function() {
114
+ var self = this;
115
+ this._beforeLoadCallback = function(e, params) {
116
+ self._unsubscribeFromTableChange();
117
+ self._unsubscribeFromHistoryChange();
118
+ // Don't add params to history if this was triggered by history change
119
+ if(self._ignoreNextLoad) {
120
+ self._ignoreNextLoad = false;
121
+ }
122
+ else {
123
+ self._addHistoryEntry(params);
124
+ }
125
+ };
126
+ this.grappleTable.element.on('grapple:before_load', this._beforeLoadCallback);
127
+ },
128
+
129
+ /**
130
+ * Stop listening for changes to the table
131
+ */
132
+ _unsubscribeFromTableChange: function() {
133
+ if(this._beforeLoadCallback) {
134
+ this.grappleTable.element.unbind('grapple:before_load', this._beforeLoadCallback);
135
+ this._beforeLoadCallback = null;
136
+ }
137
+ },
138
+
139
+ /**
140
+ * Listen for changes to the history (back button clicks)
141
+ */
142
+ _subscribeToHistoryChange: function() {
143
+ var api = this.api, namespace = this.grappleTable.namespace, self = this;
144
+ this._historyChangeCallback = function(event) {
145
+ var params = parseUrlQuery(api.get());
68
146
  // Only include the parameters for this namespace
69
147
  if(namespace) {
70
148
  var r = new RegExp('^' + namespace + '\\[([^\\]]+)\\]$')
@@ -74,15 +152,19 @@ GrappleHistory.prototype = {
74
152
  }
75
153
  }
76
154
  }
77
- callback(params);
155
+ self._ignoreNextLoad = true;
156
+ self.grappleTable.loadTable($.param(params));
78
157
  };
79
- $(window).bind('statechange', this.changeCallback);
158
+ this.api.bind(this._historyChangeCallback);
80
159
  },
81
160
 
82
- unsubscribe: function() {
83
- if(this.changeCallback) {
84
- $(window).unbind('statechange', this.changeCallback);
85
- this.changeCallback = null;
161
+ /**
162
+ * Stop listening for history changes
163
+ */
164
+ _unsubscribeFromHistoryChange: function() {
165
+ if(this._historyChangeCallback) {
166
+ this.api.unbind(this._historyChangeCallback);
167
+ this._historyChangeCallback = null;
86
168
  }
87
169
  }
88
170
 
@@ -1,8 +1,5 @@
1
1
  (function(Grapple, $) {
2
2
  'use strict';
3
-
4
- var urlQuery = Grapple.Util.urlQuery,
5
- parseUrlQuery = Grapple.Util.parseUrlQuery;
6
3
 
7
4
  var overrideLink = function(clickable, anchor, callback) {
8
5
  var href = $(anchor).attr('href');
@@ -15,8 +12,8 @@ var overrideLink = function(clickable, anchor, callback) {
15
12
  /**
16
13
  * Creates a new instance of the Grapple AJAX widget.
17
14
  *
18
- * @param {String} Selector for the table container element.
19
- * @param {Object} Hash of options for the table (url, namespace, history)
15
+ * @param {String} element - Selector for the table container element.
16
+ * @param {Object} options - Hash of options for the table (url, namespace, history)
20
17
  */
21
18
  var GrappleTable = function(element, options) {
22
19
  options = options || {};
@@ -24,16 +21,19 @@ var GrappleTable = function(element, options) {
24
21
  this.url = options.url || this.element.data('grapple-ajax-url');
25
22
  this.namespace = options.namespace || this.element.data('grapple-ajax-namespace') || null;
26
23
  this.currentParams = options.params || '';
24
+ this.plugins = {};
27
25
  if(typeof options.history !== 'undefined' && options.history !== true) {
28
- this.history = options.history;
26
+ this.plugins.history = options.history;
29
27
  }
30
28
  else if(this.element.data('grapple-ajax-history') == 1 || options.history === true) {
31
- this.history = new Grapple.History(this.namespace);
32
- }
33
- else {
34
- this.history = null;
29
+ this.plugins.history = new Grapple.History(this);
35
30
  }
31
+
36
32
  this.init();
33
+
34
+ if (this.element.data('grapple-ajax-init-params')) {
35
+ this.loadTable(this.element.data('grapple-ajax-init-params'))
36
+ }
37
37
  };
38
38
 
39
39
  GrappleTable.CSS_AJAX_LOADING = 'grapple-ajax-loading';
@@ -44,7 +44,7 @@ GrappleTable.NON_TABLE_RESPONSE = '<!DOCTYPE html>';
44
44
  GrappleTable.prototype = {
45
45
 
46
46
  /**
47
- *
47
+ * Initialize the grapple table
48
48
  */
49
49
  init: function() {
50
50
  var self = this;
@@ -56,39 +56,29 @@ GrappleTable.prototype = {
56
56
  self.initSorting();
57
57
  self.initSearchForm();
58
58
  self.initPagination();
59
- self.initHistory();
59
+ self.initPlugins();
60
60
 
61
61
  self.element.removeClass(GrappleTable.CSS_AJAX_LOADING);
62
62
  },
63
63
 
64
- initHistory: function() {
65
- if(this.history) {
66
- var self = this;
67
- this.history.unsubscribe();
68
- this.history = new Grapple.History(this.namespace);
69
- this.history.subscribe(function(params) {
70
- self.onHistoryChange(params);
71
- });
64
+ /**
65
+ * Initialize plugins
66
+ */
67
+ initPlugins: function() {
68
+ for(var name in this.plugins) {
69
+ this.plugins[name].init();
72
70
  }
73
71
  },
74
72
 
75
- onHistoryChange: function(params) {
76
- this._showLoading();
77
- this._updateTable($.param(params));
78
- },
79
-
80
73
  /**
81
- *
74
+ * Load the table contents
75
+ * @param {String} params - Query string of parameters to load the table with
76
+ * @fires Grapple#grapple:before_load
77
+ * @fires Grapple#grapple:after_load
82
78
  */
83
79
  loadTable: function(params) {
84
- this.element.trigger('grapple:before_load');
80
+ this.element.trigger('grapple:before_load', params);
85
81
  this._showLoading();
86
-
87
- if(this.history) {
88
- this.history.unsubscribe();
89
- this.history.add(params);
90
- }
91
-
92
82
  this._updateTable(params);
93
83
  },
94
84
 
@@ -117,7 +107,7 @@ GrappleTable.prototype = {
117
107
  }
118
108
  $.ajax(url, {
119
109
  success: function(data) {
120
- // HACK
110
+ // HACK: handle full page responses
121
111
  var nonTableKeyIndex = data.indexOf(GrappleTable.NON_TABLE_RESPONSE);
122
112
  if(nonTableKeyIndex > -1 && nonTableKeyIndex < 100) {
123
113
  data = "Failed to load table";
@@ -1,9 +1,6 @@
1
1
  (function(globals) {
2
2
  'use strict';
3
3
 
4
- // Namespace
5
- var Grapple = {};
6
-
7
4
  var decodeParam = function(str) {
8
5
  return decodeURIComponent(str.replace(/\+/g, " "));
9
6
  };
@@ -1,2 +1,5 @@
1
1
  en:
2
- no_search_results: "We're sorry. No results were found."
2
+ displaying_x_y_of_z_results: "Displaying %{x} - %{y} of %{z} results"
3
+ no_search_results: "We're sorry. No results were found."
4
+ search: "Search"
5
+ zero_results: "0 results"
@@ -9,7 +9,7 @@ module Grapple
9
9
  template.text_field_tag(
10
10
  search_query_param.to_s,
11
11
  params[search_query_param.to_sym],
12
- { :class => search_query_field_class, placeholder: "Search" }
12
+ { class: search_query_field_class, placeholder: I18n.translate(:search) }
13
13
  )
14
14
  end
15
15
 
@@ -2,9 +2,6 @@ module Grapple
2
2
  module Components
3
3
  class WillPaginateInfobar < HtmlComponent
4
4
 
5
- setting :message, "Displaying %s - %s of %s results"
6
- setting :no_results_message, "0 results"
7
-
8
5
  def render
9
6
  if records.total_entries > 0
10
7
  start_range = records.offset + 1
@@ -14,12 +11,12 @@ module Grapple
14
11
  end_range = ActiveSupport::NumberHelper.number_to_delimited(end_range)
15
12
  total = ActiveSupport::NumberHelper.number_to_delimited(records.total_entries)
16
13
 
17
- html = sprintf(message, start_range, end_range, total)
14
+ html = I18n.translate(:displaying_x_y_of_z_results, x: start_range, y: end_range, z: total)
18
15
  else
19
- html = no_results_message
16
+ html = I18n.translate(:zero_results)
20
17
  end
21
18
 
22
- builder.row "<th colspan=\"#{num_columns}\">#{html}</th>", :class => 'infobar'
19
+ builder.row "<th colspan=\"#{num_columns}\">#{html}</th>", class: 'infobar'
23
20
  end
24
21
 
25
22
  end
@@ -19,9 +19,9 @@ module Grapple
19
19
 
20
20
  if records.instance_of?(Array)
21
21
  html = '&nbsp;'
22
- elsif params.has_key?(:query) && records.empty?
22
+ elsif records.empty?
23
23
  html = h(t(no_results_message))
24
- td_class = "class='text-left'"
24
+ td_class = "class='no-results-message text-left'"
25
25
  else
26
26
  paginate_parameters[:param_name] = url_parameter(:page) if builder.namespace
27
27
  options = { renderer: renderer }.select { |_, value| !value.nil? }.merge(paginate_parameters)
File without changes
File without changes
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grapple
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edward Potocko
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2019-11-18 00:00:00.000000000 Z
13
+ date: 2020-12-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: actionpack
@@ -206,8 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
206
  - !ruby/object:Gem::Version
207
207
  version: '0'
208
208
  requirements: []
209
- rubyforge_project:
210
- rubygems_version: 2.6.14.1
209
+ rubygems_version: 3.1.2
211
210
  signing_key:
212
211
  specification_version: 4
213
212
  summary: Customizable data grid for Rails