grapple 0.0.1
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 +7 -0
- data/LICENSE +22 -0
- data/README.md +182 -0
- data/Rakefile +9 -0
- data/app/assets/images/grapple/arrow-down.png +0 -0
- data/app/assets/images/grapple/arrow-up.png +0 -0
- data/app/assets/images/grapple/loading-bar.gif +0 -0
- data/app/assets/images/grapple/minus.png +0 -0
- data/app/assets/images/grapple/plus.png +0 -0
- data/app/assets/javascripts/grapple-history.js +81 -0
- data/app/assets/javascripts/grapple-jquery.js +202 -0
- data/app/assets/javascripts/grapple.js +39 -0
- data/app/assets/stylesheets/grapple.css +252 -0
- data/config/locales/en.yml +2 -0
- data/lib/grapple/ajax_data_grid_builder.rb +38 -0
- data/lib/grapple/base_table_builder.rb +58 -0
- data/lib/grapple/components/actions.rb +24 -0
- data/lib/grapple/components/base_component.rb +91 -0
- data/lib/grapple/components/column_headings.rb +42 -0
- data/lib/grapple/components/html_body.rb +37 -0
- data/lib/grapple/components/html_colgroup.rb +14 -0
- data/lib/grapple/components/html_component.rb +24 -0
- data/lib/grapple/components/html_footer.rb +14 -0
- data/lib/grapple/components/html_header.rb +16 -0
- data/lib/grapple/components/html_row.rb +11 -0
- data/lib/grapple/components/search_form.rb +27 -0
- data/lib/grapple/components/search_query_field.rb +14 -0
- data/lib/grapple/components/search_submit.rb +11 -0
- data/lib/grapple/components/toolbar.rb +15 -0
- data/lib/grapple/components/will_paginate_infobar.rb +22 -0
- data/lib/grapple/components/will_paginate_pagination.rb +30 -0
- data/lib/grapple/components.rb +23 -0
- data/lib/grapple/data_grid_builder.rb +24 -0
- data/lib/grapple/engine.rb +11 -0
- data/lib/grapple/helpers/table_helper.rb +31 -0
- data/lib/grapple/helpers.rb +8 -0
- data/lib/grapple/html_table_builder.rb +31 -0
- data/lib/grapple.rb +14 -0
- data/spec/builders/ajax_data_grid_builder_spec.rb +19 -0
- data/spec/components/actions_spec.rb +33 -0
- data/spec/components/column_headings_spec.rb +33 -0
- data/spec/components/html_body_spec.rb +53 -0
- data/spec/components/html_colgroup_spec.rb +38 -0
- data/spec/components/html_footer_spec.rb +38 -0
- data/spec/components/search_form_spec.rb +29 -0
- data/spec/components/toolbar_spec.rb +38 -0
- data/spec/components/will_paginate_spec.rb +33 -0
- data/spec/fixtures/schema.rb +17 -0
- data/spec/fixtures/users.yml +56 -0
- data/spec/spec_helper.rb +137 -0
- data/spec/support/test_environment.rb +30 -0
- metadata +207 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cbdfba833204a62c126fb91ec6b95e6b17a76654
|
4
|
+
data.tar.gz: ee880107cf1becc73adc3c46d0f908be8b7ff465
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1976ae3090721348125616fdb2703bf5e568849c366c62904a81de9b6359a072522cc2fa8307d13e983961f1e9546ad54dac01ac6fc51e29bed25486eb262865
|
7
|
+
data.tar.gz: 591e70f06fbf5ef355a692fc082a7016a77d993a021b3f6f89c9aaff91c2eb22303825fe4f6d8496e372fffca403116aa3176a7b41938fb444363ecdabcc174e
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Equal Level
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
# grapple
|
2
|
+
Customizable data grid for Rails
|
3
|
+
|
4
|
+
## Features
|
5
|
+
* Modular design
|
6
|
+
* Server side rendering
|
7
|
+
* Usable out of the box
|
8
|
+
* Sorting
|
9
|
+
* Searching/Filtering
|
10
|
+
* Pagination
|
11
|
+
* AJAX
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
``` ruby
|
16
|
+
# Gemfile for Rails 3+
|
17
|
+
gem 'grapple'
|
18
|
+
```
|
19
|
+
|
20
|
+
``` css
|
21
|
+
/* app/assets/stylesheets/application.css */
|
22
|
+
*= require grapple
|
23
|
+
```
|
24
|
+
|
25
|
+
## Dependencies
|
26
|
+
* Rails 3+
|
27
|
+
|
28
|
+
Optional Dependencies:
|
29
|
+
|
30
|
+
* will_paginate - for pagination support
|
31
|
+
* jQuery - for AJAX support
|
32
|
+
* history.js - for back button support when using the AJAX data table
|
33
|
+
|
34
|
+
## Table Builders
|
35
|
+
HtmlTableBuilder - A basic HTML table builder
|
36
|
+
|
37
|
+
DataGridBuilder (default) - An HTML table builder with support for paging, filtering, sorting, and actions.
|
38
|
+
|
39
|
+
AjaxDataGridBuilder - DataGridBuilder that uses AJAX to retrieve results when sorting/filtering the table.
|
40
|
+
|
41
|
+
In an initializer set the default builder:
|
42
|
+
``` ruby
|
43
|
+
Grapple::Helpers::TableHelper.builder = Grapple::AjaxDataGridBuilder
|
44
|
+
```
|
45
|
+
|
46
|
+
## Basic Usage (DataGridBuilder)
|
47
|
+
app/controllers/posts_controller.rb
|
48
|
+
``` ruby
|
49
|
+
class PostsController < ApplicationController
|
50
|
+
def index
|
51
|
+
@posts = Post.all
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
app/views/posts/index.html.erb
|
57
|
+
``` HTML+ERB
|
58
|
+
<%
|
59
|
+
columns = [
|
60
|
+
{ label: 'Name' },
|
61
|
+
{ label: 'Title' },
|
62
|
+
{ label: 'Content' },
|
63
|
+
{ label: '' },
|
64
|
+
{ label: '' },
|
65
|
+
{ label: '' }
|
66
|
+
]
|
67
|
+
|
68
|
+
actions = [
|
69
|
+
{ label: 'New Post', url: new_posts_path }
|
70
|
+
]
|
71
|
+
%>
|
72
|
+
<%= table_for(columns, @posts) do |t| %>
|
73
|
+
<%= t.header do %>
|
74
|
+
<%= t.toolbar do %>
|
75
|
+
<%= t.actions actions %>
|
76
|
+
<% end %>
|
77
|
+
<%= t.column_headings %>
|
78
|
+
<% end %>
|
79
|
+
<%= t.body do |item| %>
|
80
|
+
<td><%= post.name %></td>
|
81
|
+
<td><%= post.title %></td>
|
82
|
+
<td><%= post.content %></td>
|
83
|
+
<td><%= link_to 'Show', post %></td>
|
84
|
+
<td><%= link_to 'Edit', edit_post_path(post) %></td>
|
85
|
+
<td><%= link_to 'Destroy', post, confirm: 'Are you sure?', method: :delete %></td>
|
86
|
+
<% end %>
|
87
|
+
<% end %>
|
88
|
+
```
|
89
|
+
|
90
|
+
## Sorting
|
91
|
+
TODO
|
92
|
+
|
93
|
+
## Pagination (requires will_paginate)
|
94
|
+
app/controllers/posts_controller.rb
|
95
|
+
``` ruby
|
96
|
+
def index
|
97
|
+
@posts = Post.paginate(page: params[:page] || 1, per_page: 10)
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
app/views/posts/index.html.erb
|
102
|
+
``` HTML+ERB
|
103
|
+
<%= table_for(columns, @posts) do |t| %>
|
104
|
+
<%= t.header %>
|
105
|
+
<%= t.footer do %>
|
106
|
+
<%= t.pagination %>
|
107
|
+
<% end %>
|
108
|
+
<% end %>
|
109
|
+
```
|
110
|
+
|
111
|
+
## Filtering/Searching
|
112
|
+
TODO
|
113
|
+
|
114
|
+
## Actions
|
115
|
+
The Actions component can be used to generate buttons/links for actions related to the table. This can be used to provide links to export the data in the table or create new objects.
|
116
|
+
``` HTML+ERB
|
117
|
+
<%= table_for(columns, @posts) do |t| %>
|
118
|
+
<%= t.header do %>
|
119
|
+
<%= t.toolbar do %>
|
120
|
+
<%= t.actions [
|
121
|
+
{ label: :new_post, url: new_posts_path },
|
122
|
+
{ label: :export_posts, url: export_posts_path }
|
123
|
+
] %>
|
124
|
+
<% end %>
|
125
|
+
<%= t.column_headings %>
|
126
|
+
<% end %>
|
127
|
+
<% end %>
|
128
|
+
```
|
129
|
+
|
130
|
+
## AJAX
|
131
|
+
The AjaxDataGridBuilder generates tables that can update their content using AJAX rather than re-loading the page. jQuery is required.
|
132
|
+
``` javascript
|
133
|
+
// app/assets/javascripts/application.js
|
134
|
+
//= require grapple
|
135
|
+
//= require grapple-jquery
|
136
|
+
```
|
137
|
+
|
138
|
+
``` ruby
|
139
|
+
# app/controllers/posts_controller.rb
|
140
|
+
class PostsController < ApplicationController
|
141
|
+
def index
|
142
|
+
@posts = table_results
|
143
|
+
end
|
144
|
+
|
145
|
+
# Method called by AJAX requests - renders the table without a layout
|
146
|
+
def table
|
147
|
+
@posts = table_results
|
148
|
+
render partial: 'table'
|
149
|
+
end
|
150
|
+
|
151
|
+
protected
|
152
|
+
|
153
|
+
def table_results
|
154
|
+
Post.paginate(page: params[:page] || 1, per_page: 10)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
Create a container around the table that can be updated by the JavaScript
|
160
|
+
``` HTML+ERB
|
161
|
+
<%# app/views/posts/index.html.erb %>
|
162
|
+
<%= grapple_container(id: 'posts_table') do %>
|
163
|
+
<%= render :partial => 'table' %>
|
164
|
+
<% end %>
|
165
|
+
```
|
166
|
+
|
167
|
+
Render the table using `table_for` in `app/views/posts/_table.html.erb`
|
168
|
+
|
169
|
+
## History w/AJAX (back button)
|
170
|
+
|
171
|
+
Requires: https://github.com/browserstate/history.js/
|
172
|
+
|
173
|
+
``` javascript
|
174
|
+
// app/assets/javascripts/application.js
|
175
|
+
//= require jquery-history
|
176
|
+
//= require grapple
|
177
|
+
//= require grapple-history
|
178
|
+
//= require grapple-jquery
|
179
|
+
```
|
180
|
+
|
181
|
+
## Customizing
|
182
|
+
TODO
|
data/Rakefile
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,81 @@
|
|
1
|
+
(function(window, Grapple, $) {
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
var urlQuery = Grapple.Util.urlQuery,
|
5
|
+
parseUrlQuery = Grapple.Util.parseUrlQuery;
|
6
|
+
|
7
|
+
var GrappleHistory = function() {
|
8
|
+
if(History.init) {
|
9
|
+
// https://github.com/browserstate/history.js/
|
10
|
+
this.api = History;
|
11
|
+
|
12
|
+
// Initialization of history.js can be delayed
|
13
|
+
// if it was do it now
|
14
|
+
if(this.api.options && this.api.options.delayInit) {
|
15
|
+
this.api.options.delayInit = false;
|
16
|
+
this.api.init();
|
17
|
+
}
|
18
|
+
}
|
19
|
+
else {
|
20
|
+
// TODO: support native history api
|
21
|
+
this.api = window.history;
|
22
|
+
}
|
23
|
+
this.api = History;
|
24
|
+
this.changeCallback = null;
|
25
|
+
};
|
26
|
+
|
27
|
+
// Don't clutter the url with rails form parameters
|
28
|
+
GrappleHistory.IGNORE_PARAMS = { 'utf8': true, 'authenticity_token': true };
|
29
|
+
|
30
|
+
GrappleHistory.prototype = {
|
31
|
+
|
32
|
+
add: function(namespace, params) {
|
33
|
+
var state = this.api.getState();
|
34
|
+
var historyParams = parseUrlQuery(urlQuery(state.url));
|
35
|
+
var newParams = parseUrlQuery(params);
|
36
|
+
|
37
|
+
// Remove any parameters from the current state
|
38
|
+
// that are for this table
|
39
|
+
for(var x in historyParams) {
|
40
|
+
var remove = namespace ?
|
41
|
+
// Remove any parameters in the tables namespace
|
42
|
+
x.indexOf(namespace + '.') === 0 :
|
43
|
+
// Table is in the global namespace, remove any parameters that aren't namespaced
|
44
|
+
x.indexOf('.') === -1;
|
45
|
+
|
46
|
+
if(remove) {
|
47
|
+
delete historyParams[x];
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
// Add the new parameters
|
52
|
+
for(var x in newParams) {
|
53
|
+
if(GrappleHistory.IGNORE_PARAMS[x]) continue;
|
54
|
+
var key = namespace ? namespace + '.' + x : x;
|
55
|
+
historyParams[key] = newParams[key];
|
56
|
+
}
|
57
|
+
|
58
|
+
this.api.pushState(null, document.title, '?' + $.param(historyParams));
|
59
|
+
},
|
60
|
+
|
61
|
+
subscribe: function(callback) {
|
62
|
+
var api = this.api;
|
63
|
+
this.changeCallback = function(event) {
|
64
|
+
var state = api.getState();
|
65
|
+
callback(parseUrlQuery(urlQuery(state.url)));
|
66
|
+
};
|
67
|
+
$(window).bind('statechange', this.changeCallback);
|
68
|
+
},
|
69
|
+
|
70
|
+
unsubscribe: function() {
|
71
|
+
if(this.changeCallback) {
|
72
|
+
$(window).unbind('statechange', this.changeCallback);
|
73
|
+
this.changeCallback = null;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
};
|
78
|
+
|
79
|
+
Grapple.History = GrappleHistory;
|
80
|
+
|
81
|
+
})(window, Grapple, $);
|
@@ -0,0 +1,202 @@
|
|
1
|
+
(function(Grapple, $) {
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
var urlQuery = Grapple.Util.urlQuery,
|
5
|
+
parseUrlQuery = Grapple.Util.parseUrlQuery;
|
6
|
+
|
7
|
+
var overrideLink = function(clickable, anchor, callback) {
|
8
|
+
var href = $(anchor).attr('href');
|
9
|
+
$(anchor).attr('href', 'javascript:void(0)');
|
10
|
+
$(clickable).on('click', function() {
|
11
|
+
callback(href ? href.split('?')[1] : '');
|
12
|
+
});
|
13
|
+
};
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Creates a new instance of the Grapple AJAX widget.
|
17
|
+
*
|
18
|
+
* @param {String} Selector for the table container element.
|
19
|
+
* @param {Object} Hash of options for the table (url, history)
|
20
|
+
*/
|
21
|
+
var GrappleTable = function(element, options) {
|
22
|
+
options = options || {};
|
23
|
+
this.element = $(element);
|
24
|
+
this.url = options.url || this.element.data('grapple-ajax-url');
|
25
|
+
this.namespace = options.namespace || this.element.data('grapple-ajax-namespace') || null;
|
26
|
+
this.currentParams = options.params || '';
|
27
|
+
if(typeof options.history !== 'undefined' && options.history !== true) {
|
28
|
+
this.history = options.history;
|
29
|
+
}
|
30
|
+
else if(this.element.data('grapple-ajax-history') == 1 || options.history === true) {
|
31
|
+
this.history = new Grapple.History();
|
32
|
+
}
|
33
|
+
else {
|
34
|
+
this.history = null;
|
35
|
+
}
|
36
|
+
this.init();
|
37
|
+
};
|
38
|
+
|
39
|
+
GrappleTable.CSS_AJAX_LOADING = 'grapple-ajax-loading';
|
40
|
+
GrappleTable.CSS_LOADING = 'grapple-loading';
|
41
|
+
GrappleTable.CSS_LOADING_OVERLAY = 'loading-overlay';
|
42
|
+
GrappleTable.NON_TABLE_RESPONSE = '<!DOCTYPE html>';
|
43
|
+
|
44
|
+
GrappleTable.prototype = {
|
45
|
+
|
46
|
+
/**
|
47
|
+
*
|
48
|
+
*/
|
49
|
+
init: function() {
|
50
|
+
var self = this;
|
51
|
+
self.table = self.element.children('table');
|
52
|
+
self.header = self.table.children('thead');
|
53
|
+
self.body = self.table.children('tbody');
|
54
|
+
self.footer = self.table.children('tfoot');
|
55
|
+
|
56
|
+
self.initSorting();
|
57
|
+
self.initSearchForm();
|
58
|
+
self.initPagination();
|
59
|
+
self.initHistory();
|
60
|
+
|
61
|
+
self.element.removeClass(GrappleTable.CSS_AJAX_LOADING);
|
62
|
+
},
|
63
|
+
|
64
|
+
initHistory: function() {
|
65
|
+
if(this.history) {
|
66
|
+
var self = this;
|
67
|
+
this.history.unsubscribe();
|
68
|
+
this.history = new Grapple.History();
|
69
|
+
this.history.subscribe(function(params) {
|
70
|
+
self.onHistoryChange(params);
|
71
|
+
});
|
72
|
+
}
|
73
|
+
},
|
74
|
+
|
75
|
+
onHistoryChange: function(params) {
|
76
|
+
console.log("HISTORY CHANGE");
|
77
|
+
this._showLoading();
|
78
|
+
this._updateTable($.param(params));
|
79
|
+
},
|
80
|
+
|
81
|
+
/**
|
82
|
+
*
|
83
|
+
*/
|
84
|
+
loadTable: function(params) {
|
85
|
+
console.log("LOAD TABLE ", params);
|
86
|
+
this._showLoading();
|
87
|
+
|
88
|
+
if(this.history) {
|
89
|
+
console.log("ADD History");
|
90
|
+
this.history.unsubscribe();
|
91
|
+
this.history.add(this.namespace, params);
|
92
|
+
}
|
93
|
+
|
94
|
+
console.log("Update table");
|
95
|
+
this._updateTable(params);
|
96
|
+
},
|
97
|
+
|
98
|
+
_showLoading: function() {
|
99
|
+
// Add loading class to the container
|
100
|
+
this.element.addClass(GrappleTable.CSS_LOADING);
|
101
|
+
|
102
|
+
// Set the position of the loading overlay based on the size of the table
|
103
|
+
var loadingBar = this.element.find('.' + GrappleTable.CSS_LOADING_OVERLAY)
|
104
|
+
loadingBar.width(this.table.width());
|
105
|
+
var barHeight = loadingBar.height() || 20;
|
106
|
+
var top = (this.table.height() / 2) - barHeight;
|
107
|
+
loadingBar.css('top', top + 'px');
|
108
|
+
},
|
109
|
+
|
110
|
+
_hideLoading: function() {
|
111
|
+
// Remove the loading class from the container
|
112
|
+
this.element.removeClass(GrappleTable.CSS_LOADING);
|
113
|
+
},
|
114
|
+
|
115
|
+
_updateTable: function(params) {
|
116
|
+
var self = this;
|
117
|
+
var url = this.url;
|
118
|
+
if(params.length) {
|
119
|
+
url += '?' + params;
|
120
|
+
}
|
121
|
+
|
122
|
+
$.ajax(url, {
|
123
|
+
success: function(data) {
|
124
|
+
// HACK
|
125
|
+
var nonTableKeyIndex = data.indexOf(GrappleTable.NON_TABLE_RESPONSE);
|
126
|
+
if(nonTableKeyIndex > -1 && nonTableKeyIndex < 100) {
|
127
|
+
data = "Failed to load table";
|
128
|
+
}
|
129
|
+
self.element.addClass(GrappleTable.CSS_AJAX_LOADING);
|
130
|
+
self.element.html(data);
|
131
|
+
self.init();
|
132
|
+
self._hideLoading();
|
133
|
+
},
|
134
|
+
error: function(a, b, c) {
|
135
|
+
// TODO: handle loading errors
|
136
|
+
console.log("Failed to load table");
|
137
|
+
console.log(a);
|
138
|
+
console.log(b);
|
139
|
+
console.log(c);
|
140
|
+
}
|
141
|
+
});
|
142
|
+
},
|
143
|
+
|
144
|
+
initSorting: function() {
|
145
|
+
var self = this;
|
146
|
+
this.header.find('th.sortable').each(function(i, elem) {
|
147
|
+
overrideLink(elem, $(elem).find('a'), function(params) {
|
148
|
+
// Return to the first page on sorting
|
149
|
+
params = params.replace(/&?page=[0-9]+/, '');
|
150
|
+
params += '&page=1';
|
151
|
+
self.loadTable(params);
|
152
|
+
});
|
153
|
+
});
|
154
|
+
},
|
155
|
+
|
156
|
+
initSearchForm: function() {
|
157
|
+
var self = this;
|
158
|
+
this.header.find('form.search-form').each(function(i, elem) {
|
159
|
+
$(elem).on('submit', function(event) {
|
160
|
+
// Don't submit the form
|
161
|
+
event.preventDefault();
|
162
|
+
self.loadTable($(elem).serialize());
|
163
|
+
});
|
164
|
+
});
|
165
|
+
},
|
166
|
+
|
167
|
+
initPagination: function() {
|
168
|
+
var self = this;
|
169
|
+
this.footer.find('.pagination a').each(function(i, elem) {
|
170
|
+
overrideLink(elem, elem, function(params) {
|
171
|
+
self.loadTable(params);
|
172
|
+
});
|
173
|
+
});
|
174
|
+
}
|
175
|
+
|
176
|
+
};
|
177
|
+
|
178
|
+
Grapple.Table = GrappleTable;
|
179
|
+
|
180
|
+
function Plugin(option) {
|
181
|
+
return this.each(function() {
|
182
|
+
var $this = $(this);
|
183
|
+
var data = $this.data('grapple');
|
184
|
+
var options = typeof option == 'object' && option;
|
185
|
+
|
186
|
+
if (!data && /destroy|hide/.test(option)) return;
|
187
|
+
if (!data) $this.data('grapple', (data = new GrappleTable(this, options)));
|
188
|
+
if (typeof option == 'string') data[option]();
|
189
|
+
});
|
190
|
+
}
|
191
|
+
|
192
|
+
var old = $.fn.grapple;
|
193
|
+
|
194
|
+
$.fn.grapple = Plugin;
|
195
|
+
$.fn.grapple.Constructor = GrappleTable;
|
196
|
+
|
197
|
+
$.fn.grapple.noConflict = function() {
|
198
|
+
$.fn.grapple = old;
|
199
|
+
return this;
|
200
|
+
}
|
201
|
+
|
202
|
+
})(Grapple, jQuery);
|
@@ -0,0 +1,39 @@
|
|
1
|
+
(function(globals) {
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
// Namespace
|
5
|
+
var Grapple = {};
|
6
|
+
|
7
|
+
var decodeParam = function(str) {
|
8
|
+
return decodeURIComponent(str.replace(/\+/g, " "));
|
9
|
+
};
|
10
|
+
|
11
|
+
var parseUrlQuery = function(query) {
|
12
|
+
var regex = /([^&=]+)=?([^&]*)/g;
|
13
|
+
var params = {}, e;
|
14
|
+
while(e = regex.exec(query)) {
|
15
|
+
var k = decodeParam(e[1]), v = decodeParam(e[2]);
|
16
|
+
if(k.substring(k.length - 2) === '[]') {
|
17
|
+
k = k.substring(0, k.length - 2);
|
18
|
+
(params[k] || (params[k] = [])).push(v);
|
19
|
+
}
|
20
|
+
else {
|
21
|
+
params[k] = v;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
return params;
|
25
|
+
};
|
26
|
+
|
27
|
+
// Get the query string from a url, returns an empty string if there is no query
|
28
|
+
var urlQuery = function(url) {
|
29
|
+
return url.split('?')[1] || '';
|
30
|
+
};
|
31
|
+
|
32
|
+
globals.Grapple = {
|
33
|
+
Util: {
|
34
|
+
urlQuery: urlQuery,
|
35
|
+
parseUrlQuery: parseUrlQuery
|
36
|
+
}
|
37
|
+
};
|
38
|
+
|
39
|
+
})(window);
|