grid_table 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +124 -0
- data/Rakefile +2 -0
- data/grid_table.gemspec +23 -0
- data/lib/grid_table/control.rb +131 -0
- data/lib/grid_table/controller.rb +35 -0
- data/lib/grid_table/model.rb +14 -0
- data/lib/grid_table/table.rb +72 -0
- data/lib/grid_table/version.rb +3 -0
- data/lib/grid_table.rb +10 -0
- data/vendor/assets/javascripts/gridtable.js +482 -0
- metadata +87 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3822148a7134b0523bad25bba7d31440a68497e9
|
4
|
+
data.tar.gz: 9e3b2dbc9d8dc3d316b3995060f4865c26b6b620
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b8afb20b6c8d00007a29b256659e4ae15c80c0f3e41f8e29f4daaa7fb450e9a3357583625b4b11220d508d829c9ee979173c06380fd962e6ee053b66acd07b21
|
7
|
+
data.tar.gz: b8b6495fb43c7d7aade3d6c2ff881af9af47f0c8de603dcb36e7c738fce7b9e438e6a028574539833d242e7a5b94f7e2d378c9b107a7da2cdb2f3298ae2564da
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Jon Principe
|
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,124 @@
|
|
1
|
+
# GridTable
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'grid_table'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install grid_table
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
1. Mark up your table in the index.html.haml with the GridTable HTML 5 attributes
|
22
|
+
```
|
23
|
+
%table.grid-table{data: {url: users_path}}
|
24
|
+
%thead
|
25
|
+
%tr
|
26
|
+
%th.small-5{data: { field: 'email', sort: 'true', filter: 'true', default_sort: 'asc' }} Email
|
27
|
+
%th{data: { field: 'first_name', sort: 'true', filter: 'true' }} First Name
|
28
|
+
%th{data: { field: 'last_name', sort: 'true', filter: 'true' }} Last Name
|
29
|
+
%th{data: { field: 'customer_name', sort: 'true', filter: 'true' }} Customer
|
30
|
+
%th{data: { field: 'created_at', sort: 'true', filter: 'false' }} Created
|
31
|
+
%th
|
32
|
+
%tr.filters
|
33
|
+
%th
|
34
|
+
%input{data: {field: 'email'}}
|
35
|
+
%th
|
36
|
+
%input{data: {field: 'first_name'}}
|
37
|
+
%th
|
38
|
+
%input{data: {field: 'last_name'}}
|
39
|
+
%th
|
40
|
+
%input{data: {field: 'customer_name'}}
|
41
|
+
%th
|
42
|
+
%th
|
43
|
+
%tbody
|
44
|
+
%tr.no-results.hide
|
45
|
+
%td
|
46
|
+
%h4 No Results Found
|
47
|
+
%td
|
48
|
+
%td
|
49
|
+
%td
|
50
|
+
%td
|
51
|
+
%td
|
52
|
+
%tcaption
|
53
|
+
.grid-pager
|
54
|
+
%ul.pagination
|
55
|
+
%li
|
56
|
+
%button#first
|
57
|
+
First
|
58
|
+
%li
|
59
|
+
%button#previous
|
60
|
+
Back
|
61
|
+
%li#pagedisplay
|
62
|
+
%li
|
63
|
+
%button#next
|
64
|
+
Next
|
65
|
+
%li
|
66
|
+
%button#last
|
67
|
+
Last
|
68
|
+
.pagination
|
69
|
+
Results per page:
|
70
|
+
#pagesize
|
71
|
+
```
|
72
|
+
2. Create a partial for each row of the table. By default the partial name is ```_row.html.haml```
|
73
|
+
```html
|
74
|
+
%tr
|
75
|
+
%td= user.email
|
76
|
+
%td= user.first_name
|
77
|
+
%td= user.last_name
|
78
|
+
%td= user.customer.name
|
79
|
+
%td= l(user.created_at.to_date)
|
80
|
+
%td
|
81
|
+
```
|
82
|
+
3. Define the fields and how they are accessed in the ActiveRecord Model
|
83
|
+
```ruby
|
84
|
+
class User < ActiveRecord::Base
|
85
|
+
extend GridTable::Model
|
86
|
+
|
87
|
+
belongs_to :customer
|
88
|
+
|
89
|
+
grid_table_control :email
|
90
|
+
grid_table_control :first_name
|
91
|
+
grid_table_control :last_name
|
92
|
+
grid_table_control :created_at
|
93
|
+
grid_table_control :customer_name,
|
94
|
+
source: :customer,
|
95
|
+
source_column: 'name'
|
96
|
+
end
|
97
|
+
```
|
98
|
+
4. Setup the controller handle the HTML response and the JS response that does the lookup of the data
|
99
|
+
```ruby
|
100
|
+
class UsersController < ApplicationController
|
101
|
+
def index
|
102
|
+
respond_to do |format|
|
103
|
+
format.html {}
|
104
|
+
format.js { grid_table_for(Users.all, index_params) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def index_params
|
111
|
+
params.permit [].concat(User.grid_table_strong_params)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
TODO: Fill out details about options available other than defaults
|
117
|
+
|
118
|
+
## Contributing
|
119
|
+
|
120
|
+
1. Fork it ( https://github.com/[my-github-username]/gridtable/fork )
|
121
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
122
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
123
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
124
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/grid_table.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'grid_table/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "grid_table"
|
8
|
+
spec.version = GridTable::VERSION
|
9
|
+
spec.authors = ["Jon Principe", "Mike Raimondi"]
|
10
|
+
spec.email = ["jp@adharmonics.com"]
|
11
|
+
spec.summary = %q{Utility for powerful HTML Tables}
|
12
|
+
spec.description = %q{Library to help manage powerful HTML Tables through the Model, View and Controller.}
|
13
|
+
spec.homepage = "http://github.com/jprincipe/grid_table"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
class GridTable::Control
|
2
|
+
include ActiveModel::Model
|
3
|
+
|
4
|
+
attr_writer :model, :attribute, :source, :source_class, :source_column, :filter, :polymorphic
|
5
|
+
|
6
|
+
def filter(param_filter_value, records)
|
7
|
+
unless @filter == false
|
8
|
+
arel_query = nil
|
9
|
+
strategy_map = {
|
10
|
+
exact_match: ->(col) { col.eq(param_filter_value) },
|
11
|
+
prefix: ->(col) { col.matches("#{param_filter_value}%") },
|
12
|
+
suffix: ->(col) { col.matches("%#{param_filter_value}") },
|
13
|
+
fuzzy: ->(col) { col.matches("%#{param_filter_value}%") },
|
14
|
+
array: ->(col) { "#{column.to_s} @> ARRAY[#{[param_filter_value].flatten.join(',')}]" }
|
15
|
+
}
|
16
|
+
|
17
|
+
polymorphic_models.each_with_index do |klass, i|
|
18
|
+
# TODO implement array filtering
|
19
|
+
if i == 0
|
20
|
+
arel_query = strategy_map[strategy].call(klass.arel_table[column])
|
21
|
+
else
|
22
|
+
arel_query = arel_query.or(strategy_map[strategy].call(klass.arel_table[column]))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
arel_query ||= strategy_map[strategy].call(source_table[column])
|
27
|
+
prepared_records(records).where(arel_query)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def sort(param_sort_order, records)
|
32
|
+
sort_order = %w[asc, desc].include?(param_sort_order) ? param_sort_order : 'asc'
|
33
|
+
|
34
|
+
if @polymorphic
|
35
|
+
models = polymorphic_models
|
36
|
+
if models.present?
|
37
|
+
prepared_records(records).select("\"#{@model.to_s.tableize}\".*, #{polymorphic_select_sql(models)} AS #{active_source}")
|
38
|
+
.order("#{active_source} #{sort_order}")
|
39
|
+
else
|
40
|
+
records
|
41
|
+
end
|
42
|
+
else
|
43
|
+
prepared_records(records).order("#{table_with_column} #{sort_order}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def url_param
|
48
|
+
@attribute
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def prepared_records(records)
|
54
|
+
if @polymorphic
|
55
|
+
polymorphic_models.each do |klass|
|
56
|
+
records = records.joins("LEFT OUTER JOIN #{klass.table_name} ON #{model_fk}_id = #{klass.table_name}.id AND #{model_fk}_type = '#{klass}'")
|
57
|
+
end
|
58
|
+
records
|
59
|
+
else
|
60
|
+
joined_control? ? records.includes(active_source).references(active_source) : records
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def column
|
65
|
+
@source_column || @attribute
|
66
|
+
end
|
67
|
+
|
68
|
+
def active_source
|
69
|
+
@source || @model
|
70
|
+
end
|
71
|
+
|
72
|
+
def joined_control?
|
73
|
+
@model != active_source
|
74
|
+
end
|
75
|
+
|
76
|
+
def strategy
|
77
|
+
@filter || :fuzzy
|
78
|
+
end
|
79
|
+
|
80
|
+
def source_table
|
81
|
+
klass = Object.const_get(@source_class || active_source.to_s.classify)
|
82
|
+
klass.arel_table
|
83
|
+
end
|
84
|
+
|
85
|
+
def table_with_column
|
86
|
+
"#{source_table.name}.#{column}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def model_fk
|
90
|
+
"#{@model.to_s.tableize}.#{@source}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def polymorphic_select_sql(models)
|
94
|
+
sql = ''
|
95
|
+
|
96
|
+
models.each_with_index do |klass, i|
|
97
|
+
if models.length == 1
|
98
|
+
sql = "#{klass.table_name}.#{column}"
|
99
|
+
elsif i == 0
|
100
|
+
sql << "(CASE WHEN #{klass.table_name}.#{column} IS NOT NULL THEN #{klass.table_name}.#{column}"
|
101
|
+
elsif i == models.length - 1
|
102
|
+
sql << " ELSE #{klass.table_name}.#{column} END)"
|
103
|
+
else
|
104
|
+
sql << " ELSIF #{klass.table_name}.#{column} IS NOT NULL THEN #{klass.table_name}.#{column}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
sql
|
109
|
+
end
|
110
|
+
|
111
|
+
def polymorphic_models
|
112
|
+
return [] unless @polymorphic
|
113
|
+
|
114
|
+
if @polymorphic_models.blank?
|
115
|
+
col = (active_source.to_s + '_type').to_sym
|
116
|
+
@polymorphic_models = Object.const_get(@model.to_s.classify)
|
117
|
+
.select(col)
|
118
|
+
.uniq
|
119
|
+
.where.not(col => nil)
|
120
|
+
.pluck(col).map { |klass| Object.const_get(klass) }
|
121
|
+
end
|
122
|
+
|
123
|
+
@polymorphic_models
|
124
|
+
end
|
125
|
+
|
126
|
+
class << self
|
127
|
+
def find_by_param(param, controls)
|
128
|
+
controls.detect { |control| control.url_param == param.try(:to_sym) }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module GridTable
|
2
|
+
module Controller
|
3
|
+
def grid_table_for(resource, params, options = {})
|
4
|
+
grid_table = resource.grid_table
|
5
|
+
grid_table.populate!(resource, params)
|
6
|
+
|
7
|
+
if block_given?
|
8
|
+
yield grid_table.records, grid_table.total_rows
|
9
|
+
else
|
10
|
+
rows = []
|
11
|
+
|
12
|
+
local = options[:local].try(:to_sym) || grid_table.records.klass.name.demodulize.underscore.to_sym
|
13
|
+
grid_table.records.each do |record|
|
14
|
+
rows << (render_to_string partial: (options[:partial] || 'row'), locals: { local => record })
|
15
|
+
end
|
16
|
+
|
17
|
+
render json: { total_rows: grid_table.total_rows, rows: rows }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def grid_table_export_for(resource, params, options = {})
|
22
|
+
grid_table = resource.grid_table
|
23
|
+
params[:skip_paging] ||= true
|
24
|
+
grid_table.populate!(resource, params)
|
25
|
+
|
26
|
+
if block_given?
|
27
|
+
yield grid_table.records
|
28
|
+
else
|
29
|
+
csv = grid_table.records.klass.to_csv(grid_table.records)
|
30
|
+
|
31
|
+
send_data csv, filename: "#{grid_table.records.klass.name.demodulize.underscore}.csv"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module GridTable
|
2
|
+
module Model
|
3
|
+
attr_accessor :grid_table
|
4
|
+
|
5
|
+
def grid_table_control(attribute, options = {})
|
6
|
+
self.grid_table ||= GridTable::Table.new
|
7
|
+
self.grid_table.add_control(self, attribute, options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def grid_table_strong_params
|
11
|
+
self.grid_table.strong_params
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class GridTable::Table
|
2
|
+
attr_reader :records, :total_rows
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@controls = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_control(model, attribute, options)
|
9
|
+
@controls << GridTable::Control.new(
|
10
|
+
{
|
11
|
+
model: model.name.underscore.to_sym,
|
12
|
+
attribute: attribute,
|
13
|
+
source: options[:source],
|
14
|
+
source_class: options[:source_class],
|
15
|
+
source_column: options[:source_column],
|
16
|
+
filter: options[:filter],
|
17
|
+
polymorphic: options[:polymorphic]
|
18
|
+
})
|
19
|
+
end
|
20
|
+
|
21
|
+
def populate!(resource, params)
|
22
|
+
@params = params
|
23
|
+
@records = resource
|
24
|
+
|
25
|
+
filter!
|
26
|
+
sort!
|
27
|
+
@total_rows = @records.size
|
28
|
+
page! unless params[:skip_paging]
|
29
|
+
|
30
|
+
@records
|
31
|
+
end
|
32
|
+
|
33
|
+
def strong_params
|
34
|
+
@controls.inject(common_strong_params) do |all_params, control|
|
35
|
+
all_params << control.url_param
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def common_strong_params
|
42
|
+
[:page, :page_size, :sort, :sort_order]
|
43
|
+
end
|
44
|
+
|
45
|
+
def page
|
46
|
+
(@params[:page] || 0).to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
def page_size
|
50
|
+
(@params[:page_size] || 10).to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def filter!
|
54
|
+
@params.each do |attribute, attribute_value|
|
55
|
+
control = GridTable::Control.find_by_param(attribute, @controls)
|
56
|
+
@records = control.filter(attribute_value, @records) if control.present?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def sort!
|
61
|
+
control = GridTable::Control.find_by_param(@params[:sort], @controls)
|
62
|
+
|
63
|
+
if control.present?
|
64
|
+
@records = control.sort(@params[:sort_order], records)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def page!
|
69
|
+
@records = @records.offset(page * page_size).limit(page_size)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
data/lib/grid_table.rb
ADDED
@@ -0,0 +1,482 @@
|
|
1
|
+
var GridTable;
|
2
|
+
|
3
|
+
$(function() {
|
4
|
+
$('[data-grid-table-sort]').on('change', function() {
|
5
|
+
var gridtable;
|
6
|
+
gridtable = GridTableFactory.get($(this));
|
7
|
+
return gridtable.refresh((function(_this) {
|
8
|
+
return function() {
|
9
|
+
var $checked, n, url;
|
10
|
+
n = _this.name;
|
11
|
+
gridtable = GridTableFactory.get(_this);
|
12
|
+
url = gridtable.getUrl();
|
13
|
+
gridtable.setFilter(n, null, {
|
14
|
+
skip_load: true
|
15
|
+
});
|
16
|
+
if (_this.type === 'checkbox') {
|
17
|
+
$checked = $(':checkbox[name="' + n + '"]:checked');
|
18
|
+
if ($checked.length > 0) {
|
19
|
+
url += '?' + ($checked.map(function() {
|
20
|
+
return "" + n + "=" + this.value;
|
21
|
+
}).toArray().join('&'));
|
22
|
+
}
|
23
|
+
return gridtable.setUrl(url);
|
24
|
+
} else {
|
25
|
+
return gridtable.setUrl(updateQueryString(n, _this.value, url));
|
26
|
+
}
|
27
|
+
};
|
28
|
+
})(this));
|
29
|
+
});
|
30
|
+
return $('.grid-table').each(function() {
|
31
|
+
var $table;
|
32
|
+
$table = $(this);
|
33
|
+
return GridTableFactory.createGridTable($table).loadGridTable($table);
|
34
|
+
});
|
35
|
+
});
|
36
|
+
|
37
|
+
window.GridTableFactory = (function() {
|
38
|
+
function GridTableFactory() {}
|
39
|
+
|
40
|
+
GridTableFactory.gridTableList = [];
|
41
|
+
|
42
|
+
GridTableFactory.defaultGridTableId = 'default-grid-table';
|
43
|
+
|
44
|
+
GridTableFactory.get = function(obj) {
|
45
|
+
var $gt, id;
|
46
|
+
if (typeof obj === 'object') {
|
47
|
+
id = $(obj).data('grid-table-id');
|
48
|
+
} else {
|
49
|
+
id = obj;
|
50
|
+
}
|
51
|
+
return $gt = this.gridTableList[id || this.defaultGridTableId];
|
52
|
+
};
|
53
|
+
|
54
|
+
GridTableFactory.createGridTable = function(table, params) {
|
55
|
+
var $table, existing_table, gridTable;
|
56
|
+
if ((existing_table = this.get(table)) != null) {
|
57
|
+
throw new Error(existing_table);
|
58
|
+
}
|
59
|
+
$table = $(table);
|
60
|
+
gridTable = new GridTable(params);
|
61
|
+
$table.on('ajax:success', 'a, button', function() {
|
62
|
+
gridTable = GridTableFactory.get($table);
|
63
|
+
return gridTable.refresh();
|
64
|
+
});
|
65
|
+
this.gridTableList[$table.data('grid-table-id') || this.defaultGridTableId] = gridTable;
|
66
|
+
return gridTable;
|
67
|
+
};
|
68
|
+
|
69
|
+
return GridTableFactory;
|
70
|
+
|
71
|
+
})();
|
72
|
+
|
73
|
+
GridTable = (function() {
|
74
|
+
var GridTableParams, sortIcons;
|
75
|
+
|
76
|
+
sortIcons = {
|
77
|
+
"default": 'fi-sort',
|
78
|
+
asc: 'fi-sort-down',
|
79
|
+
desc: 'fi-sort-up'
|
80
|
+
};
|
81
|
+
|
82
|
+
GridTable.prototype.gridTableParams = null;
|
83
|
+
|
84
|
+
GridTable.prototype.gridTableDOM = null;
|
85
|
+
|
86
|
+
GridTable.prototype.loadDataCompleteCallback = null;
|
87
|
+
|
88
|
+
GridTable.prototype.loadDataStartCallback = null;
|
89
|
+
|
90
|
+
GridTable.prototype.loadDataErrorCallback = null;
|
91
|
+
|
92
|
+
function GridTable(params) {
|
93
|
+
this.gridTableParams = new GridTableParams(params);
|
94
|
+
}
|
95
|
+
|
96
|
+
GridTable.prototype.loadGridTable = function(table, params) {
|
97
|
+
if (params == null) {
|
98
|
+
params = {};
|
99
|
+
}
|
100
|
+
this.gridTableDOM = $(table);
|
101
|
+
this.gridTableParams.setId(this.gridTableDOM.data('grid-table-id'));
|
102
|
+
if (this.gridTableDOM.attr('data-page-size')) {
|
103
|
+
this.gridTableParams.setPageSize(this.gridTableDOM.data('page-size'));
|
104
|
+
}
|
105
|
+
this.gridTableParams.setUrl(this.gridTableDOM.data('url'));
|
106
|
+
this.gridTableDOM.find('thead th[data-sort="true"], .thead [data-sort="true"]').each((function(_this) {
|
107
|
+
return function(index, column) {
|
108
|
+
var $column;
|
109
|
+
$column = $(column);
|
110
|
+
$column.append(" <i class='" + sortIcons['default'] + "'></i>");
|
111
|
+
if ($column.data('default-sort')) {
|
112
|
+
_this.setSort($column.data('field'), $column.data('default-sort'));
|
113
|
+
}
|
114
|
+
return $column.on('click', function(event) {
|
115
|
+
_this.gridTableParams.setSort($(event.currentTarget).data('field'), null);
|
116
|
+
return _this.loadData();
|
117
|
+
});
|
118
|
+
};
|
119
|
+
})(this));
|
120
|
+
this.gridTableDOM.find('select.row-filter').each((function(_this) {
|
121
|
+
return function(index, filter) {
|
122
|
+
return $(filter).on("change", function(event) {
|
123
|
+
_this.gridTableParams.setFilter($(filter).data('field'), $(filter).val());
|
124
|
+
return _this.loadData();
|
125
|
+
});
|
126
|
+
};
|
127
|
+
})(this));
|
128
|
+
this.gridTableDOM.find('input.row-filter').each((function(_this) {
|
129
|
+
return function(index, filter) {
|
130
|
+
var timeout;
|
131
|
+
_this.gridTableParams.setFilter($(filter).data('field'), $(filter).val());
|
132
|
+
timeout = null;
|
133
|
+
return $(filter).on("propertychange keyup input paste", function(event) {
|
134
|
+
clearTimeout(timeout);
|
135
|
+
return timeout = setTimeout((function() {
|
136
|
+
_this.gridTableParams.setFilter($(filter).data('field'), $(filter).val());
|
137
|
+
return _this.loadData();
|
138
|
+
}), 500);
|
139
|
+
});
|
140
|
+
};
|
141
|
+
})(this));
|
142
|
+
this.gridTableDOM.find('.grid-pager #pagesize').each((function(_this) {
|
143
|
+
return function(index, elem) {
|
144
|
+
var pageSizeSelect, size, _i, _len, _ref;
|
145
|
+
pageSizeSelect = '<select id="page-size-select">';
|
146
|
+
_ref = _this.gridTableParams.pageSizeOptions;
|
147
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
148
|
+
size = _ref[_i];
|
149
|
+
if (size === _this.gridTableParams.pageSize) {
|
150
|
+
pageSizeSelect += "<option selected value=\"" + size + "\">" + size + "</option>";
|
151
|
+
} else {
|
152
|
+
pageSizeSelect += "<option value=\"" + size + "\">" + size + "</option>";
|
153
|
+
}
|
154
|
+
}
|
155
|
+
pageSizeSelect += '</select>';
|
156
|
+
$(elem).append(pageSizeSelect);
|
157
|
+
return $(elem).find('#page-size-select').on("change", function(event) {
|
158
|
+
return _this.setPageSize($(event.currentTarget).val());
|
159
|
+
});
|
160
|
+
};
|
161
|
+
})(this));
|
162
|
+
this.loadData(params);
|
163
|
+
return $('.export[data-grid-table-id="' + this.gridTableParams.id + '"]').on('click', (function(_this) {
|
164
|
+
return function(event) {
|
165
|
+
return _this.exportData();
|
166
|
+
};
|
167
|
+
})(this));
|
168
|
+
};
|
169
|
+
|
170
|
+
GridTable.prototype.refresh = function(callback) {
|
171
|
+
if (typeof callback === "function") {
|
172
|
+
callback(callback);
|
173
|
+
}
|
174
|
+
return this.loadData();
|
175
|
+
};
|
176
|
+
|
177
|
+
GridTable.prototype.setId = function(id) {
|
178
|
+
return this.gridTableParams.setId(id);
|
179
|
+
};
|
180
|
+
|
181
|
+
GridTable.prototype.setUrl = function(url) {
|
182
|
+
return this.gridTableParams.setUrl(url);
|
183
|
+
};
|
184
|
+
|
185
|
+
GridTable.prototype.getUrl = function() {
|
186
|
+
return this.gridTableParams.url;
|
187
|
+
};
|
188
|
+
|
189
|
+
GridTable.prototype.setFilter = function(key, value, options) {
|
190
|
+
if (options == null) {
|
191
|
+
options = {};
|
192
|
+
}
|
193
|
+
this.gridTableParams.setFilter(key, value);
|
194
|
+
if (this.gridTableDOM !== null && !options.skip_load) {
|
195
|
+
return this.loadData();
|
196
|
+
}
|
197
|
+
};
|
198
|
+
|
199
|
+
GridTable.prototype.setSort = function(column, direction) {
|
200
|
+
this.gridTableParams.setSort(column, direction);
|
201
|
+
if (this.gridTableDOM !== null) {
|
202
|
+
return this.loadData();
|
203
|
+
}
|
204
|
+
};
|
205
|
+
|
206
|
+
GridTable.prototype.loadDataStart = function(callback) {
|
207
|
+
return this.loadDataStartCallback = callback;
|
208
|
+
};
|
209
|
+
|
210
|
+
GridTable.prototype.loadDataComplete = function(callback) {
|
211
|
+
return this.loadDataCompleteCallback = callback;
|
212
|
+
};
|
213
|
+
|
214
|
+
GridTable.prototype.loadDataError = function(callback) {
|
215
|
+
return this.loadDataErrorCallback = callback;
|
216
|
+
};
|
217
|
+
|
218
|
+
GridTable.prototype.buildUrl = function(baseUrl) {
|
219
|
+
return this.gridTableParams.buildUrl(baseUrl, true);
|
220
|
+
};
|
221
|
+
|
222
|
+
GridTable.prototype.setPage = function(page) {
|
223
|
+
this.gridTableParams.page = page;
|
224
|
+
return this.loadData();
|
225
|
+
};
|
226
|
+
|
227
|
+
GridTable.prototype.setPageSize = function(size) {
|
228
|
+
this.gridTableParams.pageSize = size;
|
229
|
+
this.gridTableParams.page = 0;
|
230
|
+
return this.loadData();
|
231
|
+
};
|
232
|
+
|
233
|
+
GridTable.prototype.loadData = function(params) {
|
234
|
+
if (params == null) {
|
235
|
+
params = {};
|
236
|
+
}
|
237
|
+
if (params.globalAjax == null) {
|
238
|
+
params.globalAjax = true;
|
239
|
+
}
|
240
|
+
if (typeof this.loadDataStartCallback === "function") {
|
241
|
+
this.loadDataStartCallback();
|
242
|
+
}
|
243
|
+
return $.ajax(this.gridTableParams.buildUrl(this.gridTableParams.url), {
|
244
|
+
type: 'GET',
|
245
|
+
global: params.globalAjax,
|
246
|
+
dataType: 'json',
|
247
|
+
error: (function(_this) {
|
248
|
+
return function(jqXHR, textStatus, errorThrown) {
|
249
|
+
if (typeof _this.loadDataErrorCallback === "function") {
|
250
|
+
return _this.loadDataErrorCallback();
|
251
|
+
}
|
252
|
+
};
|
253
|
+
})(this),
|
254
|
+
success: (function(_this) {
|
255
|
+
return function(data, textStatus, jqXHR) {
|
256
|
+
var row, _i, _len, _ref;
|
257
|
+
_this.gridTableDOM.find('tbody, .tbody').children().not('.no-results').remove();
|
258
|
+
_this.gridTableDOM.find('.no-results').addClass('hide');
|
259
|
+
if (data.totals) {
|
260
|
+
_this.gridTableDOM.find('thead tr.totals, .thead tr.totals, .thead .tr.totals').html(data.totals);
|
261
|
+
}
|
262
|
+
if (data.rows.length === 0) {
|
263
|
+
_this.gridTableDOM.find('.no-results').removeClass('hide');
|
264
|
+
} else {
|
265
|
+
_ref = data.rows;
|
266
|
+
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
267
|
+
row = _ref[_i];
|
268
|
+
_this.gridTableDOM.find('tbody, .tbody').append(row);
|
269
|
+
}
|
270
|
+
}
|
271
|
+
_this.updateSortDisplay();
|
272
|
+
_this.updatePagerDisplay(data.total_rows);
|
273
|
+
if (typeof _this.loadDataCompleteCallback === "function") {
|
274
|
+
return _this.loadDataCompleteCallback();
|
275
|
+
}
|
276
|
+
};
|
277
|
+
})(this)
|
278
|
+
});
|
279
|
+
};
|
280
|
+
|
281
|
+
GridTable.prototype.exportData = function() {
|
282
|
+
var baseUrl, url;
|
283
|
+
url = this.gridTableParams.url.split('?');
|
284
|
+
if (url.length === 1) {
|
285
|
+
baseUrl = url[0] + '.csv';
|
286
|
+
} else {
|
287
|
+
baseUrl = url[0] + '.csv?' + url[1];
|
288
|
+
}
|
289
|
+
return window.open(this.gridTableParams.buildUrl(baseUrl));
|
290
|
+
};
|
291
|
+
|
292
|
+
GridTable.prototype.updatePagerDisplay = function(total_rows) {
|
293
|
+
var back_enabled, display, first, forward_enabled, last, last_page, next, pager, previous;
|
294
|
+
pager = this.gridTableDOM.find('.grid-pager');
|
295
|
+
first = $(pager).find('#first');
|
296
|
+
previous = $(pager).find('#previous');
|
297
|
+
next = $(pager).find('#next');
|
298
|
+
last = $(pager).find('#last');
|
299
|
+
display = $(pager).find('#pagedisplay');
|
300
|
+
$(first).off('click');
|
301
|
+
$(previous).off('click');
|
302
|
+
$(next).off('click');
|
303
|
+
$(last).off('click');
|
304
|
+
last_page = Math.floor(total_rows / this.gridTableParams.pageSize);
|
305
|
+
if (total_rows % this.gridTableParams.pageSize === 0) {
|
306
|
+
last_page -= 1;
|
307
|
+
}
|
308
|
+
back_enabled = this.gridTableParams.page > 0;
|
309
|
+
forward_enabled = this.gridTableParams.page < last_page;
|
310
|
+
if (back_enabled) {
|
311
|
+
$(first).removeClass('disabled');
|
312
|
+
$(first).on('click', (function(_this) {
|
313
|
+
return function(event) {
|
314
|
+
event.preventDefault();
|
315
|
+
return _this.setPage(0);
|
316
|
+
};
|
317
|
+
})(this));
|
318
|
+
$(previous).removeClass('disabled');
|
319
|
+
$(previous).on('click', (function(_this) {
|
320
|
+
return function(event) {
|
321
|
+
event.preventDefault();
|
322
|
+
return _this.setPage(_this.gridTableParams.page - 1);
|
323
|
+
};
|
324
|
+
})(this));
|
325
|
+
} else {
|
326
|
+
$(first).addClass('disabled');
|
327
|
+
$(previous).addClass('disabled');
|
328
|
+
}
|
329
|
+
if (forward_enabled) {
|
330
|
+
$(next).removeClass('disabled');
|
331
|
+
$(next).on('click', (function(_this) {
|
332
|
+
return function(event) {
|
333
|
+
event.preventDefault();
|
334
|
+
return _this.setPage(_this.gridTableParams.page + 1);
|
335
|
+
};
|
336
|
+
})(this));
|
337
|
+
$(last).removeClass('disabled');
|
338
|
+
$(last).on('click', (function(_this) {
|
339
|
+
return function(event) {
|
340
|
+
event.preventDefault();
|
341
|
+
return _this.setPage(last_page);
|
342
|
+
};
|
343
|
+
})(this));
|
344
|
+
} else {
|
345
|
+
$(next).addClass('disabled');
|
346
|
+
$(last).addClass('disabled');
|
347
|
+
}
|
348
|
+
return display.text("" + (this.gridTableParams.page + 1) + " of " + (last_page + 1) + " (" + total_rows + ")");
|
349
|
+
};
|
350
|
+
|
351
|
+
GridTable.prototype.updateSortDisplay = function() {
|
352
|
+
var field, sortOrder;
|
353
|
+
field = this.gridTableParams.sortCol;
|
354
|
+
sortOrder = this.gridTableParams.sortOrder;
|
355
|
+
return this.gridTableDOM.find('thead th[data-sort="true"], .thead [data-sort="true"]').each((function(_this) {
|
356
|
+
return function(i, c) {
|
357
|
+
var value;
|
358
|
+
value = $(c).data('field');
|
359
|
+
if (value === field) {
|
360
|
+
switch (sortOrder) {
|
361
|
+
case 'asc':
|
362
|
+
$(c).addClass('sorting');
|
363
|
+
return $(c).find('i').attr('class', sortIcons['asc']);
|
364
|
+
case 'desc':
|
365
|
+
$(c).addClass('sorting');
|
366
|
+
return $(c).find('i').attr('class', sortIcons['desc']);
|
367
|
+
default:
|
368
|
+
$(c).removeClass('sorting');
|
369
|
+
return $(c).find('i').attr('class', sortIcons['default']);
|
370
|
+
}
|
371
|
+
} else {
|
372
|
+
$(c).removeClass('sorting');
|
373
|
+
return $(c).find('i').attr('class', sortIcons['default']);
|
374
|
+
}
|
375
|
+
};
|
376
|
+
})(this));
|
377
|
+
};
|
378
|
+
|
379
|
+
GridTableParams = (function() {
|
380
|
+
GridTableParams.prototype.id = null;
|
381
|
+
|
382
|
+
GridTableParams.prototype.url = null;
|
383
|
+
|
384
|
+
GridTableParams.prototype.sortCol = '';
|
385
|
+
|
386
|
+
GridTableParams.prototype.sortOrder = '';
|
387
|
+
|
388
|
+
GridTableParams.prototype.filter = {};
|
389
|
+
|
390
|
+
GridTableParams.prototype.page = 0;
|
391
|
+
|
392
|
+
GridTableParams.prototype.pageSize = 10;
|
393
|
+
|
394
|
+
GridTableParams.prototype.pageSizeOptions = [5, 10, 25, 50, 100, 200];
|
395
|
+
|
396
|
+
function GridTableParams(params) {
|
397
|
+
if ((params != null)) {
|
398
|
+
if ('sortCol' in params) {
|
399
|
+
this.sortCol = params['sortCol'];
|
400
|
+
}
|
401
|
+
if ('sortOrder' in params) {
|
402
|
+
this.sortOrder = params['sortOrder'];
|
403
|
+
}
|
404
|
+
if ('filter' in params) {
|
405
|
+
this.filter = params['filter'];
|
406
|
+
}
|
407
|
+
if ('page' in params) {
|
408
|
+
this.page = params['page'];
|
409
|
+
}
|
410
|
+
if ('pageSize' in params) {
|
411
|
+
this.pageSize = params['pageSize'];
|
412
|
+
}
|
413
|
+
if ('id' in params) {
|
414
|
+
this.id = params['id'];
|
415
|
+
}
|
416
|
+
if ('url' in params) {
|
417
|
+
this.url = params['url'];
|
418
|
+
}
|
419
|
+
}
|
420
|
+
}
|
421
|
+
|
422
|
+
GridTableParams.prototype.setId = function(id) {
|
423
|
+
return this.id = id;
|
424
|
+
};
|
425
|
+
|
426
|
+
GridTableParams.prototype.setUrl = function(url) {
|
427
|
+
return this.url = url;
|
428
|
+
};
|
429
|
+
|
430
|
+
GridTableParams.prototype.setPageSize = function(pageSize) {
|
431
|
+
return this.pageSize = pageSize;
|
432
|
+
};
|
433
|
+
|
434
|
+
GridTableParams.prototype.setSort = function(column, direction) {
|
435
|
+
var order;
|
436
|
+
this.page = 0;
|
437
|
+
if (this.sortCol === column) {
|
438
|
+
order = this.sortOrder === 'asc' ? 'desc' : 'asc';
|
439
|
+
} else {
|
440
|
+
order = 'asc';
|
441
|
+
}
|
442
|
+
this.sortOrder = direction || order;
|
443
|
+
return this.sortCol = column;
|
444
|
+
};
|
445
|
+
|
446
|
+
GridTableParams.prototype.setFilter = function(column, value) {
|
447
|
+
this.page = 0;
|
448
|
+
if (value === null || value.trim().length === 0) {
|
449
|
+
return delete this.filter[column];
|
450
|
+
} else {
|
451
|
+
return this.filter[column] = value;
|
452
|
+
}
|
453
|
+
};
|
454
|
+
|
455
|
+
GridTableParams.prototype.buildUrl = function(baseUrl, skip_paging) {
|
456
|
+
var k, url, v, _ref;
|
457
|
+
if (skip_paging == null) {
|
458
|
+
skip_paging = false;
|
459
|
+
}
|
460
|
+
url = baseUrl;
|
461
|
+
url += /\?/.test(url) ? '&' : '?';
|
462
|
+
if (!skip_paging) {
|
463
|
+
url += 'page=' + this.page;
|
464
|
+
url += "&page_size=" + this.pageSize;
|
465
|
+
url += "&sort=" + this.sortCol;
|
466
|
+
url += "&sort_order=" + this.sortOrder;
|
467
|
+
}
|
468
|
+
_ref = this.filter;
|
469
|
+
for (k in _ref) {
|
470
|
+
v = _ref[k];
|
471
|
+
url += '&' + k + '=' + v;
|
472
|
+
}
|
473
|
+
return url;
|
474
|
+
};
|
475
|
+
|
476
|
+
return GridTableParams;
|
477
|
+
|
478
|
+
})();
|
479
|
+
|
480
|
+
return GridTable;
|
481
|
+
|
482
|
+
})();
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: grid_table
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jon Principe
|
8
|
+
- Mike Raimondi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-11-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '1.6'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '1.6'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
description: Library to help manage powerful HTML Tables through the Model, View and
|
43
|
+
Controller.
|
44
|
+
email:
|
45
|
+
- jp@adharmonics.com
|
46
|
+
executables: []
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- .gitignore
|
51
|
+
- Gemfile
|
52
|
+
- LICENSE.txt
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- grid_table.gemspec
|
56
|
+
- lib/grid_table.rb
|
57
|
+
- lib/grid_table/control.rb
|
58
|
+
- lib/grid_table/controller.rb
|
59
|
+
- lib/grid_table/model.rb
|
60
|
+
- lib/grid_table/table.rb
|
61
|
+
- lib/grid_table/version.rb
|
62
|
+
- vendor/assets/javascripts/gridtable.js
|
63
|
+
homepage: http://github.com/jprincipe/grid_table
|
64
|
+
licenses:
|
65
|
+
- MIT
|
66
|
+
metadata: {}
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
requirements: []
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 2.0.14
|
84
|
+
signing_key:
|
85
|
+
specification_version: 4
|
86
|
+
summary: Utility for powerful HTML Tables
|
87
|
+
test_files: []
|