├── .gitignore
├── Gruntfile.js
├── README.md
├── demo
├── index.html
└── ractive-tooltip.js
├── package.json
├── ractive-datatable.js
├── ractive-datatable.min.js
├── src
├── datatable.js
├── partials
│ └── default.html
├── styles.styl
├── template.html
└── util
│ └── sortBy.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | node_modules
3 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /*global module:false*/
2 | module.exports = function(grunt) {
3 |
4 | //handles plugins
5 | require('jit-grunt')(grunt);
6 |
7 | var webpack = require('webpack');
8 |
9 | grunt.initConfig({
10 |
11 | webpack: {
12 | options: require('./webpack.config.js'),
13 | development: {
14 | debug: true,
15 | },
16 | production: {
17 | debug: false,
18 | production: true,
19 | devtool: 'none',
20 | output: {
21 | pathinfo: false,
22 | filename: 'ractive-datatable.min.js',
23 | },
24 | plugins: [
25 | new webpack.DefinePlugin({
26 | DEBUG: false,
27 | PRODUCTION: true
28 | }),
29 | new webpack.optimize.UglifyJsPlugin({
30 | output: {
31 | comments: false,
32 | }
33 | }),
34 | new webpack.optimize.AggressiveMergingPlugin(),
35 | new webpack.optimize.OccurenceOrderPlugin(true),
36 | ]
37 | },
38 | },
39 |
40 | });
41 |
42 | grunt.registerTask('default', ['webpack:development', 'webpack:production']);
43 |
44 | };
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ractive-datatable
2 |
3 |
4 | ## Demo
5 |
6 | [Live Demo](http://jondum.github.com/ractive-datatable/demo/)
7 |
8 | ## Install
9 |
10 | ```
11 | npm install ractive-datatable --save
12 | ```
13 |
14 | ## Features
15 |
16 | * Minimal (3.45kb gzipped **including** styles, 1.71kb without)
17 | * Well designed default styling
18 | * Cell Editing
19 | * Pagination (incl. navigation buttons)
20 | * Filtering (with sexy highlights)
21 | * Sorting
22 | * Per-cell partials (new!)
23 | * Selection (new!) (wip!)
24 |
25 | ## Usage
26 |
27 | Add the datatable to your Ractive instance:
28 |
29 | ```
30 | Ractive.extend({
31 | ...
32 | components: {
33 | datatable: require('ractive-datatable')
34 | },
35 | ...
36 | });
37 | ```
38 |
39 | Use it
40 |
41 | ```
42 |
43 | ```
44 |
45 | Includes minimal styling under the class `.ractive-datatable`. Styles are included in the javascript and added to the page on load. If you don't want these styles in the javascript, `require()` `src/datatable.js` and handle the styles as needed.
46 |
47 | To use a specific partial for a cell, create an inline partial expression inside the tag of the component:
48 |
49 | ```
50 |
51 | {{#partial timestamp}}
52 | {{ moment(this).fromNow() }}
53 | {{/partial}}
54 |
55 | ```
56 |
57 | Will render every row in the "timestamp" column with the passed in partial (in this case formatting the timestamp using moment.js).
58 |
59 | ## API
60 |
61 | ### Properties
62 |
63 | ##### `data`
64 | Array of Objects where each key is a column
65 |
66 | ##### `editable`
67 | globally allow/disallow editing
68 |
69 | ##### `filter`
70 | A string to filter the rows on. Searches through all cells with a case-insensitive RegEx and displays only rows that match. Cells with matches are highlighted.
71 |
72 | ##### `dynamicColumns`
73 | If `true` (default), searches the entire `data` array looking for columns.
74 | If `false`, columns must be explicitly provided through the `columns` property.
75 | If you have a large number of rows this should be turned off for performance, but you will have to explicitly provide columns via the `columns` object.
76 |
77 | ##### `columns`
78 | Determines the ordering of the columns and configuration for specific columns.
79 |
80 | Each key on this object refers to column names. Configurable properties are `edit`,
81 | `display`, `header` & `order`. Key/column names are case-sensitive.
82 |
83 | Example:
84 |
85 | ```
86 | columns: {
87 | 'name': {order: 0}, // `order` "bumps" the column, lowest value is left most.
88 | 'created': {edit: false},
89 | 'someAnnoyinglyNamedThingFromThatCoworkerThatDrivesYouNuts': {header: 'Nicer Name'},
90 | 'id': {edit: false},
91 | 'hiddenField': {display: false},
92 | 'anotherHidden': false, //shorthand for { display: false }
93 | 'someOtherColumn': {order: 3},
94 | }
95 | ```
96 |
97 | If `dynamicColumns` is `false`, only columns configured here will display.
98 |
99 | ##### `selectionMode`
100 | Either `row` or `cell` (WIP). Allows for rows to be selected on click
101 |
102 | ##### `selection`
103 | An array of the currently selected objects from `data`
104 |
105 | ##### `page`
106 | The current page
107 |
108 | ##### `lastPage` (readonly)
109 | The last page or total number of pages
110 |
111 | ##### `sortOn`
112 | Name of column to sort
113 |
114 | ##### `sortMode`
115 | Either 'asc' or 'desc'
116 |
117 | #### Methods
118 |
119 |
120 | ##### `previousPage()`
121 | Go to the previous page
122 |
123 | ##### `nextPage()`
124 | Go to the next page
125 |
126 | ##### `gotoPage(page)`
127 | Go to the specified page
128 |
129 |
130 | ### Events
131 |
132 | `edit`: Dispatched when an edit is made. Sends the entire row and the field that is edited (useful for extracting specific information from the row that changed).
133 |
134 | ```
135 | this.on('dataedited', function(row, field) {
136 |
137 | var change = {};
138 | change.id = row.id;
139 | change[field] = row[field];
140 |
141 | changes.push(change);
142 |
143 | });
144 | ```
145 |
146 | For other events you probably would be better off using Ractive's observers on your datatable instance and the property you want.
147 |
148 | Open to PRs and stuff. I'm around.
149 |
150 |
151 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ractive-Datatable Demo
5 |
6 |
7 |
8 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
93 |
94 |
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/demo/ractive-tooltip.js:
--------------------------------------------------------------------------------
1 | !function(e, t) {
2 | "object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : "object" == typeof exports ?
3 | exports.RactiveTooltip = t() : e.RactiveTooltip = t()
4 | }(this, function() {
5 | return function(e) {
6 | function t(r) {
7 | if (n[r]) return n[r].exports;
8 | var o = n[r] = {
9 | exports: {},
10 | id: r,
11 | loaded: !1
12 | };
13 | return e[r].call(o.exports, o, o.exports, t), o.loaded = !0, o.exports
14 | }
15 | var n = {};
16 | return t.m = e, t.c = n, t.p = "", t(0)
17 | }([function(e, t, n) {
18 | function r(e, t, n) {
19 | var r = {
20 | x: e.pageX,
21 | y: e.pageY
22 | },
23 | o = r.y - n.offsetHeight - 5,
24 | a = r.x + n.offsetWidth - i.innerWidth,
25 | s = e.pageY - n.offsetHeight - 5,
26 | f = e.pageX - 5;
27 | a > 0 && (f -= a - 5), 0 > o && (s += 2 * n.offsetHeight - 5), n.style.left = f + "px", n.style.top = s + "px"
28 | }
29 |
30 | function o(e, t) {
31 | var n, o, i;
32 | o = {
33 | mouseenter: function(o) {
34 | t && 0 !== t.length && (n || (n = a.createElement("div"), n.className = "ractive-tooltip", n.textContent = t), r(o,
35 | e, n), a.body.appendChild(n))
36 | },
37 | mousemove: function(t) {
38 | n && r(t, e, n)
39 | },
40 | mouseleave: function() {
41 | n && n.parentNode && n.parentNode.removeChild(n)
42 | }
43 | };
44 | for (i in o) o.hasOwnProperty(i) && e.addEventListener(i, o[i], !1);
45 | return {
46 | update: function(e) {
47 | t = e, n && (n.textContent = t), t && 0 !== t.length || !n || !n.parentNode || n.parentNode.removeChild(n)
48 | },
49 | teardown: function() {
50 | n && n.parentNode && (n.parentNode.removeChild(n), n = null);
51 | for (i in o) o.hasOwnProperty(i) && e.removeEventListener(i, o[i], !1)
52 | }
53 | }
54 | }
55 | n(1);
56 | var i = window,
57 | a = i.document;
58 | e.exports = o
59 | }, function(e, t, n) {
60 | var r = n(2);
61 | "string" == typeof r && (r = [[e.id, r, ""]]);
62 | n(4)(r, {});
63 | r.placeholders && (e.exports = r.placeholders)
64 | }, function(e, t, n) {
65 | t = e.exports = n(3)(), t.push([e.id,
66 | ".ractive-tooltip{position:absolute;display:table;padding:.5em;color:#fff;background:#000;box-shadow:0 2px 2px rgba(0,0,0,.1);border-radius:5px;white-space:nowrap;z-index:99999;font-style:normal;text-transform:none;pointer-events:none}",
67 | ""])
68 | }, function(e, t) {
69 | e.exports = function() {
70 | var e = [];
71 | return e.toString = function() {
72 | for (var e = [], t = 0; t < this.length; t++) {
73 | var n = this[t];
74 | n[2] ? e.push("@media " + n[2] + "{" + n[1] + "}") : e.push(n[1])
75 | }
76 | return e.join("")
77 | }, e.i = function(t, n) {
78 | "string" == typeof t && (t = [[null, t, ""]]);
79 | for (var r = {}, o = 0; o < this.length; o++) {
80 | var i = this[o][0];
81 | "number" == typeof i && (r[i] = !0)
82 | }
83 | for (var o = 0; o < t.length; o++) {
84 | var a = t[o];
85 | "number" == typeof a[0] && r[a[0]] || (n && !a[2] ? a[2] = n : n && (a[2] = "(" + a[2] + ") and (" + n + ")"), e.push(
86 | a))
87 | }
88 | }, e
89 | }
90 | }, function(e, t, n) {
91 | function r(e, t) {
92 | for (var n = 0; n < e.length; n++) {
93 | var r = e[n],
94 | o = d[r.id];
95 | if (o) {
96 | o.refs++;
97 | for (var i = 0; i < o.parts.length; i++) o.parts[i](r.parts[i]);
98 | for (; i < r.parts.length; i++) o.parts.push(s(r.parts[i], t))
99 | } else {
100 | for (var a = [], i = 0; i < r.parts.length; i++) a.push(s(r.parts[i], t));
101 | d[r.id] = {
102 | id: r.id,
103 | refs: 1,
104 | parts: a
105 | }
106 | }
107 | }
108 | }
109 |
110 | function o(e) {
111 | for (var t = [], n = {}, r = 0; r < e.length; r++) {
112 | var o = e[r],
113 | i = o[0],
114 | a = o[1],
115 | s = o[2],
116 | f = o[3],
117 | p = {
118 | css: a,
119 | media: s,
120 | sourceMap: f
121 | };
122 | n[i] ? n[i].parts.push(p) : t.push(n[i] = {
123 | id: i,
124 | parts: [p]
125 | })
126 | }
127 | return t
128 | }
129 |
130 | function i() {
131 | var e = document.createElement("style"),
132 | t = h();
133 | return e.type = "text/css", t.appendChild(e), e
134 | }
135 |
136 | function a() {
137 | var e = document.createElement("link"),
138 | t = h();
139 | return e.rel = "stylesheet", t.appendChild(e), e
140 | }
141 |
142 | function s(e, t) {
143 | var n, r, o;
144 | if (t.singleton) {
145 | var s = m++;
146 | n = v || (v = i()), r = f.bind(null, n, s, !1), o = f.bind(null, n, s, !0)
147 | } else e.sourceMap && "function" == typeof URL && "function" == typeof URL.createObjectURL && "function" == typeof URL.revokeObjectURL &&
148 | "function" == typeof Blob && "function" == typeof btoa ? (n = a(), r = u.bind(null, n), o = function() {
149 | n.parentNode.removeChild(n), n.href && URL.revokeObjectURL(n.href)
150 | }) : (n = i(), r = p.bind(null, n), o = function() {
151 | n.parentNode.removeChild(n)
152 | });
153 | return r(e),
154 | function(t) {
155 | if (t) {
156 | if (t.css === e.css && t.media === e.media && t.sourceMap === e.sourceMap) return;
157 | r(e = t)
158 | } else o()
159 | }
160 | }
161 |
162 | function f(e, t, n, r) {
163 | var o = n ? "" : r.css;
164 | if (e.styleSheet) e.styleSheet.cssText = g(t, o);
165 | else {
166 | var i = document.createTextNode(o),
167 | a = e.childNodes;
168 | a[t] && e.removeChild(a[t]), a.length ? e.insertBefore(i, a[t]) : e.appendChild(i)
169 | }
170 | }
171 |
172 | function p(e, t) {
173 | var n = t.css,
174 | r = t.media;
175 | t.sourceMap;
176 | if (r && e.setAttribute("media", r), e.styleSheet) e.styleSheet.cssText = n;
177 | else {
178 | for (; e.firstChild;) e.removeChild(e.firstChild);
179 | e.appendChild(document.createTextNode(n))
180 | }
181 | }
182 |
183 | function u(e, t) {
184 | var n = t.css,
185 | r = (t.media, t.sourceMap);
186 | r && (n += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(JSON.stringify(r)) + " */");
187 | var o = new Blob([n], {
188 | type: "text/css"
189 | }),
190 | i = e.href;
191 | e.href = URL.createObjectURL(o), i && URL.revokeObjectURL(i)
192 | }
193 | var d = {},
194 | c = function(e) {
195 | var t;
196 | return function() {
197 | return "undefined" == typeof t && (t = e.apply(this, arguments)), t
198 | }
199 | },
200 | l = c(function() {
201 | return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase())
202 | }),
203 | h = c(function() {
204 | return document.head || document.getElementsByTagName("head")[0]
205 | }),
206 | v = null,
207 | m = 0;
208 | e.exports = function(e, t) {
209 | t = t || {}, "undefined" == typeof t.singleton && (t.singleton = l());
210 | var n = o(e);
211 | return r(n, t),
212 | function(e) {
213 | for (var i = [], a = 0; a < n.length; a++) {
214 | var s = n[a],
215 | f = d[s.id];
216 | f.refs--, i.push(f)
217 | }
218 | if (e) {
219 | var p = o(e);
220 | r(p, t)
221 | }
222 | for (var a = 0; a < i.length; a++) {
223 | var f = i[a];
224 | if (0 === f.refs) {
225 | for (var u = 0; u < f.parts.length; u++) f.parts[u]();
226 | delete d[f.id]
227 | }
228 | }
229 | }
230 | };
231 | var g = function() {
232 | var e = [];
233 | return function(t, n) {
234 | return e[t] = n, e.filter(Boolean).join("\n")
235 | }
236 | }()
237 | }])
238 | });
239 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ractive-datatable",
3 | "version": "1.7.0",
4 | "description": "Interactive datatable for ractivejs",
5 | "main": "src/datatable.js",
6 | "scripts": {
7 | "test": ""
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "http://github.com/jondum/ractive-datatable"
12 | },
13 | "keywords": [
14 | "ractivejs",
15 | "ractive",
16 | "component",
17 | "datatable",
18 | "ractive-datatable"
19 | ],
20 | "author": "Jonathan Dumaine",
21 | "license": "ISC",
22 | "bugs": {
23 | "url": "https://github.com/jondum/ractive-datatable/issues"
24 | },
25 | "homepage": "https://github.com/jondum/ractive-datatable",
26 | "devDependencies": {
27 | "css-loader": "^0.26.1",
28 | "grunt": "^1.0.1",
29 | "grunt-webpack": "^2.0.1",
30 | "jit-grunt": "^0.10.0",
31 | "nib": "^1.1.2",
32 | "ractive": "^0.8.10",
33 | "ractive-loader": "^0.5.6",
34 | "style-loader": "^0.13.1",
35 | "stylus-loader": "^2.4.0",
36 | "webpack": "^2.2.1"
37 | },
38 | "dependencies": {
39 | "css-loader": "^0.26.1",
40 | "lodash": "^4.17.4",
41 | "nib": "^1.1.2",
42 | "ractive": "^0.8.10",
43 | "style-loader": "^0.13.1",
44 | "stylus-loader": "^2.4.0"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ractive-datatable.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory();
4 | else if(typeof define === 'function' && define.amd)
5 | define([], factory);
6 | else if(typeof exports === 'object')
7 | exports["RactiveDatatable"] = factory();
8 | else
9 | root["RactiveDatatable"] = factory();
10 | })(this, function() {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 |
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 |
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId])
20 | /******/ return installedModules[moduleId].exports;
21 |
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ exports: {},
25 | /******/ id: moduleId,
26 | /******/ loaded: false
27 | /******/ };
28 |
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 |
32 | /******/ // Flag the module as loaded
33 | /******/ module.loaded = true;
34 |
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 |
39 |
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 |
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 |
46 | /******/ // __webpack_public_path__
47 | /******/ __webpack_require__.p = "";
48 |
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ([
54 | /* 0 */
55 | /***/ function(module, exports, __webpack_require__) {
56 |
57 | module.exports = __webpack_require__(1);
58 |
59 |
60 | /***/ },
61 | /* 1 */
62 | /***/ function(module, exports, __webpack_require__) {
63 |
64 |
65 | __webpack_require__(2);
66 |
67 | var sortBy = __webpack_require__(6);
68 | var uniq = __webpack_require__(7);
69 | var isUndefined = __webpack_require__(61);
70 | var isObject= __webpack_require__(24);
71 | var isNumber = __webpack_require__(62);
72 |
73 | var DataTable = Ractive.extend({
74 |
75 | template: __webpack_require__(64),
76 |
77 | data: function() {
78 | return {
79 |
80 | filter: '',
81 |
82 | perpage: 30,
83 |
84 | page: 1,
85 |
86 | editable: true,
87 |
88 | sortable: true,
89 |
90 | sortOn: '',
91 |
92 | _selection: [],
93 |
94 | selectionMode: '', // "row" or "cell"
95 |
96 |
97 | /**
98 | * @name dynamicColumns
99 | * @type Boolean
100 | * @default true
101 | * If `true`, searches the entire `data` array looking for columns. If you have a large number of rows this should be turned off.
102 | * If `false`, columns must be explicitly provided through the `columns` property.
103 | */
104 | dynamicColumns: true,
105 |
106 | /**
107 | *
108 | * @name columns
109 | * @type Object
110 | * @default null
111 | *
112 | * Determines the ordering of the columns and configuration for specific columns.
113 | *
114 | * Each key on this object refers to column names. Configurable properties are `edit`,
115 | * `display` & `order`. Keys and column names are case-sensitive.
116 | *
117 | * Example:
118 | *
119 | * ```
120 | * columns: {
121 | * 'name': {order: 0}, // `order` "bumps" the column, lowest value is left most.
122 | * 'created': {edit: false},
123 | * 'id': {edit: false},
124 | * 'hiddenField': {display: false},
125 | * 'anotherHidden': false, //shorthand for { display: false }
126 | * 'someOtherColumn': {order: 3},
127 | * }
128 | * ```
129 | *
130 | * If `dynamicColumns` is `false`, only columns configured here will display.
131 | *
132 | */
133 | columns: undefined,
134 |
135 | can: function(action, field, row) {
136 |
137 | var config = this.get('columns');
138 |
139 | // don't edit fields that don't exit on the row
140 | if(!row.hasOwnProperty(field) && action == 'edit')
141 | return;
142 |
143 | if(!config)
144 | return true;
145 |
146 | if(isUndefined(config[field]))
147 | return true
148 |
149 | if(config[field] && isUndefined(config[field][action]))
150 | return true;
151 |
152 | return config[field][action];
153 | },
154 |
155 | highlight: function(text) {
156 |
157 | var self = this;
158 | var filter = self.get('filter');
159 |
160 | // columns without a corresponding key on the row get passed the whole row
161 | // avoids putting in [object Object] casts
162 | if(typeof text === 'object')
163 | return '';
164 |
165 | if(!filter || !text)
166 | return text;
167 |
168 | text = String(text);
169 |
170 | if(text.indexOf(filter) > -1) {
171 | return text.split(filter).join('' + filter + '');
172 | }
173 |
174 | return text;
175 | },
176 |
177 | cellFor: function(column) {
178 |
179 | if(this.partials[column])
180 | return column;
181 |
182 | return '__default__';
183 |
184 | },
185 |
186 | }
187 | },
188 |
189 | computed: {
190 |
191 |
192 | // `data` set publicly
193 | // `_data` is internal, includes any filters, sorted
194 | _data: function() {
195 |
196 | var self = this;
197 |
198 | var data = self.get('data');
199 |
200 | var filter = self.get('filter');
201 |
202 | var sortOn = self.get('sortOn');
203 | var sortMode = self.get('sortMode');
204 |
205 | if(filter && filter.length > 0) {
206 | var re = new RegExp(filter, 'i');
207 | data = data.filter(function(d) {
208 | for(var p in d)
209 | if(d.hasOwnProperty(p) && re.test(d[p]))
210 | return true;
211 | });
212 | }
213 |
214 | if(sortOn) {
215 | data = data.slice().sort(sortBy(sortOn, (sortMode == 'desc')));
216 | }
217 |
218 | return data
219 | .map(function(v, i) {
220 | return {item: v, index: i};
221 | });
222 | },
223 |
224 | rows: function() {
225 |
226 | var self = this;
227 |
228 | var page = self.get('page') - 1;
229 | var _data = self.get('_data');
230 | var perpage = self.get('perpage');
231 | var total = self.get('total');
232 |
233 | // the original data, unfiltered
234 | var data = self.get('data');
235 |
236 | return _data.slice(page * perpage, Math.min(page * perpage + perpage, total));
237 | },
238 |
239 | cols: function() {
240 |
241 | var self = this;
242 |
243 | var data = self.get('data'); //use data instead of _data
244 | var config = self.get('columns');
245 | var dynamicColumns = self.get('dynamicColumns');
246 |
247 | var _columns = [];
248 |
249 | if(dynamicColumns) {
250 |
251 | data.forEach( function(row) {
252 | Object.keys(row).forEach(function(key) {
253 | if(_columns.indexOf(key) === -1)
254 | _columns.push(key);
255 | });
256 | });
257 |
258 | } else {
259 |
260 | _columns = Object.keys(config);
261 |
262 | }
263 |
264 | if(isObject(config)) {
265 |
266 | var order = [];
267 |
268 | _columns = _columns.filter( function(col) {
269 |
270 | var colConfig = config[col];
271 |
272 | if( isUndefined(colConfig) || colConfig === true )
273 | return true;
274 |
275 | // if display is undefined we still want to show the col
276 | if( colConfig.display === false )
277 | return;
278 |
279 | if( !isUndefined(colConfig.order) && isNumber(colConfig.order) ) {
280 | order.splice(colConfig.order, 0, col);
281 | return;
282 | }
283 |
284 | if( colConfig === false )
285 | return;
286 |
287 | return true;
288 | });
289 |
290 | var length = order.length;
291 |
292 | // push to the beginning of _columns
293 | if(order && length > 0) {
294 | while(length--)
295 | _columns.unshift(order[length]);
296 | }
297 | }
298 |
299 | return _columns;
300 |
301 | },
302 |
303 | total: function() {
304 | var data = this.get('_data');
305 | return data ? this.get('_data').length : 0;
306 | },
307 |
308 | current: function() {
309 | var page = this.get('page');
310 | var perpage = this.get('perpage');
311 | var total = this.get('total');
312 | var ppp = (page - 1) * perpage;
313 | return (page == 1 ? 1 : ppp) + '-' + Math.min(ppp + perpage, total)
314 | },
315 |
316 | pages: function() {
317 |
318 | var total = this.get('total');
319 | var page = this.get('page');
320 | var perpage = this.get('perpage');
321 |
322 | var onFirstPage = this.get('onFirstPage');
323 | var lastPage = this.get('lastPage');
324 |
325 | if(perpage > total)
326 | return null;
327 |
328 | var ret = [];
329 |
330 | var n = Math.min(lastPage, 7);
331 | var p = page > lastPage - 4 ? lastPage - n : Math.max(page - 4, 0);
332 | var c = p + n;
333 | while(p++ < c)
334 | ret.push(p);
335 |
336 | //first page
337 | if(page > n) {
338 | ret[0] = 1;
339 | }
340 |
341 | // last page
342 | if(p < lastPage - 4)
343 | ret[ret.length - 1] = lastPage;
344 |
345 | return ret;
346 | },
347 |
348 | lastPage: function() {
349 | var total = this.get('total');
350 | var perpage = this.get('perpage');
351 |
352 | return Math.ceil(total / perpage);
353 | },
354 |
355 | onFirstPage: function() {
356 | return this.get('page') == 1;
357 | },
358 |
359 |
360 | onLastPage: function() {
361 |
362 | var page = this.get('page');
363 | var lastPage = this.get('lastPage');
364 |
365 | return page == lastPage;
366 | },
367 |
368 |
369 | },
370 |
371 | partials: {
372 | __default__: __webpack_require__(65)
373 | },
374 |
375 | oninit: function() {
376 |
377 | var self = this;
378 | // autofocus editing inputs
379 | self.observe('editing', function(value) {
380 | if(value) {
381 | var node = self.find('td input');
382 | if(node)
383 | node.focus();
384 | }
385 | }, {
386 | defer: true
387 | });
388 |
389 | // reset page when perpage changes
390 | self.observe('perpage filter data', function() {
391 | self.set('page', 1);
392 | });
393 |
394 | self.observe('perpage', function(value) {
395 | if(typeof value !== 'number') {
396 | self.set('perpage', parseInt(value, 10));
397 | }
398 | });
399 |
400 | self.observe('page', function(value) {
401 | if(typeof value !== 'number') {
402 | self.set('perpage', parseInt(value, 10));
403 | }
404 | });
405 |
406 | self.observe('_selection', function(_selection) {
407 |
408 | var data = self.get('data');
409 | if(!_selection)
410 | return;
411 |
412 | self.set('selection', _selection.map(function(v) {
413 | return data[v];
414 | }));
415 |
416 | });
417 |
418 | },
419 |
420 | fieldedited: function(context) {
421 |
422 | var self = this;
423 | var event = context.original;
424 |
425 | if(event.type == 'keyup' && event.keyCode !== 13)
426 | return false;
427 |
428 | var field = context.get();
429 | var index = context.get('index') + (self.get('page') - 1) * self.get('perpage');
430 | var row = self.get('_data.' + index + '.item');
431 |
432 | // don't duplicate
433 | if(context.node.value !== row[field]) {
434 |
435 | // get the real position of index
436 | index = self.get('data').indexOf(row);
437 |
438 | var keypath = 'data.' + index + '.' + field;
439 | var value = context.node.value;
440 |
441 | self.set(keypath, value);
442 |
443 | self.fire('edit', row, field, value, index);
444 |
445 | }
446 |
447 | self.set('editing', null);
448 |
449 | },
450 |
451 | selectRow: function(details) {
452 |
453 | var mode = this.get('selectionMode');
454 | var event = details.original;
455 | var row;
456 |
457 | if(mode == 'cell')
458 | return;
459 |
460 | var _selection = this.get('_selection');
461 | var _lastSelected = this.get('_lastSelected');
462 |
463 | if(details.context)
464 | row = details.context.index;
465 | else
466 | row = details.index.r;
467 |
468 |
469 | // if for some reason the details.context is undef
470 | // and we can't the index through other means then prevent
471 | // an error and do nothing
472 | if(!isNumber(row))
473 | return;
474 |
475 | if(event.shiftKey && _lastSelected) {
476 |
477 | var min = Math.min(_lastSelected, row);
478 | var max = Math.max(_lastSelected, row);
479 |
480 | for(var c = max; c <= max && c >= min; c--) {
481 | _selection.push(c);
482 | }
483 |
484 | } else if(event.ctrlKey || event.metaKey || event.altKey ||
485 | (_selection.length === 1 && _selection[0] === row)) {
486 |
487 | var index = _selection.indexOf(row);
488 |
489 | if(index > -1)
490 | _selection.splice(index, 1);
491 | else
492 | _selection.push(row);
493 |
494 | } else {
495 |
496 | _selection = [row];
497 |
498 | }
499 |
500 | _lastSelected = row;
501 |
502 | this.set('_lastSelected', _lastSelected);
503 | this.set('_selection', _selection);
504 |
505 | clearSelection();
506 | },
507 |
508 | selectCell: function(details) {
509 |
510 | var mode = this.get('selectionMode');
511 |
512 | if(mode == 'row')
513 | return;
514 |
515 | var event = details.original;
516 | event.stopImmediatePropagation();
517 |
518 | clearSelection();
519 |
520 | //TODO
521 | },
522 |
523 | setSort: function(column) {
524 |
525 | var self = this;
526 |
527 | if(!column || !self.get('sortable'))
528 | return
529 |
530 | var sortMode = self.get('sortMode');
531 | var sortOn = self.get('sortOn');
532 |
533 | // toggle sortMode
534 | if(sortOn == column || !sortMode) {
535 |
536 | if(sortMode == 'asc')
537 | self.set('sortMode', 'desc')
538 | else
539 | self.set('sortMode', 'asc');
540 |
541 | }
542 |
543 | self.set('sortOn', column);
544 | },
545 |
546 | previousPage: function() {
547 | this.set('page', Math.max(this.get('page') - 1, 1));
548 | },
549 |
550 | nextPage: function() {
551 | this.set('page', Math.min(this.get('page') + 1, this.get('lastPage')));
552 | },
553 |
554 | gotoPage: function(page)
555 | {
556 | this.set('page', page);
557 | }
558 |
559 |
560 |
561 | });
562 |
563 | function clearSelection() {
564 | if (window.getSelection) {
565 | if (window.getSelection().empty) { // Chrome
566 | window.getSelection().empty();
567 | } else if (window.getSelection().removeAllRanges) { // Firefox
568 | window.getSelection().removeAllRanges();
569 | }
570 | } else if (document.selection) { // IE?
571 | document.selection.empty();
572 | }
573 | }
574 |
575 | module.exports = DataTable;
576 |
577 |
578 | /***/ },
579 | /* 2 */
580 | /***/ function(module, exports, __webpack_require__) {
581 |
582 | // style-loader: Adds some css to the DOM by adding a