tabulator-tables 5.0.9 → 5.1.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.
Files changed (110) hide show
  1. package/.editorconfig +6 -0
  2. package/LICENSE +1 -1
  3. package/bower.json +1 -1
  4. package/build/rollup.js +2 -1
  5. package/dist/css/tabulator.css +26 -10
  6. package/dist/css/tabulator.css.map +1 -1
  7. package/dist/css/tabulator.min.css +1 -1
  8. package/dist/css/tabulator.min.css.map +1 -1
  9. package/dist/css/tabulator_bootstrap3.css +26 -10
  10. package/dist/css/tabulator_bootstrap3.css.map +1 -1
  11. package/dist/css/tabulator_bootstrap3.min.css +1 -1
  12. package/dist/css/tabulator_bootstrap3.min.css.map +1 -1
  13. package/dist/css/tabulator_bootstrap4.css +26 -10
  14. package/dist/css/tabulator_bootstrap4.css.map +1 -1
  15. package/dist/css/tabulator_bootstrap4.min.css +1 -1
  16. package/dist/css/tabulator_bootstrap4.min.css.map +1 -1
  17. package/dist/css/tabulator_bulma.css +26 -10
  18. package/dist/css/tabulator_bulma.css.map +1 -1
  19. package/dist/css/tabulator_bulma.min.css +1 -1
  20. package/dist/css/tabulator_bulma.min.css.map +1 -1
  21. package/dist/css/tabulator_materialize.css +33 -14
  22. package/dist/css/tabulator_materialize.css.map +1 -1
  23. package/dist/css/tabulator_materialize.min.css +1 -1
  24. package/dist/css/tabulator_materialize.min.css.map +1 -1
  25. package/dist/css/tabulator_midnight.css +30 -10
  26. package/dist/css/tabulator_midnight.css.map +1 -1
  27. package/dist/css/tabulator_midnight.min.css +1 -1
  28. package/dist/css/tabulator_midnight.min.css.map +1 -1
  29. package/dist/css/tabulator_modern.css +26 -10
  30. package/dist/css/tabulator_modern.css.map +1 -1
  31. package/dist/css/tabulator_modern.min.css +1 -1
  32. package/dist/css/tabulator_modern.min.css.map +1 -1
  33. package/dist/css/tabulator_semanticui.css +26 -10
  34. package/dist/css/tabulator_semanticui.css.map +1 -1
  35. package/dist/css/tabulator_semanticui.min.css +1 -1
  36. package/dist/css/tabulator_semanticui.min.css.map +1 -1
  37. package/dist/css/tabulator_simple.css +26 -10
  38. package/dist/css/tabulator_simple.css.map +1 -1
  39. package/dist/css/tabulator_simple.min.css +1 -1
  40. package/dist/css/tabulator_simple.min.css.map +1 -1
  41. package/dist/css/tabulator_site.css +30 -10
  42. package/dist/css/tabulator_site.css.map +1 -1
  43. package/dist/css/tabulator_site.min.css +1 -1
  44. package/dist/css/tabulator_site.min.css.map +1 -1
  45. package/dist/js/tabulator.js +1077 -530
  46. package/dist/js/tabulator.js.map +1 -1
  47. package/dist/js/tabulator.min.js +2 -2
  48. package/dist/js/tabulator.min.js.map +1 -1
  49. package/dist/js/tabulator_esm.js +1696 -1171
  50. package/dist/js/tabulator_esm.js.map +1 -1
  51. package/dist/js/tabulator_esm.min.js +2 -2
  52. package/dist/js/tabulator_esm.min.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/js/core/FooterManager.js +14 -2
  55. package/src/js/core/RowManager.js +318 -291
  56. package/src/js/core/Tabulator.js +8 -68
  57. package/src/js/core/cell/Cell.js +5 -1
  58. package/src/js/core/column/Column.js +20 -5
  59. package/src/js/core/column/ColumnComponent.js +6 -0
  60. package/src/js/core/column/defaults/options.js +1 -0
  61. package/src/js/core/defaults/options.js +2 -2
  62. package/src/js/core/modules/optional.js +1 -0
  63. package/src/js/core/rendering/Renderer.js +1 -1
  64. package/src/js/core/rendering/renderers/BasicVertical.js +2 -1
  65. package/src/js/core/rendering/renderers/VirtualDomHorizontal.js +165 -151
  66. package/src/js/core/row/Row.js +2 -2
  67. package/src/js/core/tools/DataLoader.js +5 -5
  68. package/src/js/core/tools/InteractionMonitor.js +63 -32
  69. package/src/js/core/tools/ModuleBinder.js +15 -3
  70. package/src/js/modules/Ajax/Ajax.js +62 -82
  71. package/src/js/modules/ColumnCalcs/ColumnCalcs.js +3 -3
  72. package/src/js/modules/DataTree/DataTree.js +5 -1
  73. package/src/js/modules/Download/defaults/downloaders/json.js +1 -1
  74. package/src/js/modules/Download/defaults/downloaders/jsonLines.js +32 -0
  75. package/src/js/modules/Download/defaults/downloaders.js +2 -0
  76. package/src/js/modules/Edit/Edit.js +26 -31
  77. package/src/js/modules/Export/Export.js +2 -1
  78. package/src/js/modules/Filter/Filter.js +2 -3
  79. package/src/js/modules/Format/defaults/formatters/datetime.js +1 -1
  80. package/src/js/modules/Format/defaults/formatters/datetimediff.js +2 -1
  81. package/src/js/modules/Format/defaults/formatters/rownum.js +1 -1
  82. package/src/js/modules/FrozenColumns/FrozenColumns.js +1 -1
  83. package/src/js/modules/FrozenRows/FrozenRows.js +9 -0
  84. package/src/js/modules/GroupRows/Group.js +2 -61
  85. package/src/js/modules/GroupRows/GroupComponent.js +1 -1
  86. package/src/js/modules/GroupRows/GroupRows.js +2 -2
  87. package/src/js/modules/Import/Import.js +195 -0
  88. package/src/js/modules/Import/defaults/importers/csv.js +63 -0
  89. package/src/js/modules/Import/defaults/importers/json.js +8 -0
  90. package/src/js/modules/Import/defaults/importers.js +7 -0
  91. package/src/js/modules/Interaction/Interaction.js +5 -0
  92. package/src/js/modules/Keybindings/Keybindings.js +9 -14
  93. package/src/js/modules/Keybindings/defaults/bindings.js +3 -3
  94. package/src/js/modules/Localize/defaults/langs.js +6 -0
  95. package/src/js/modules/Menu/Menu.js +200 -155
  96. package/src/js/modules/MoveRows/MoveRows.js +4 -0
  97. package/src/js/modules/Page/Page.js +279 -176
  98. package/src/js/modules/Page/defaults/pageCounters/pages.js +33 -0
  99. package/src/js/modules/Page/defaults/pageCounters/rows.js +32 -0
  100. package/src/js/modules/Page/defaults/pageCounters.js +8 -0
  101. package/src/js/modules/Persistence/Persistence.js +6 -4
  102. package/src/js/modules/ResizeRows/ResizeRows.js +1 -1
  103. package/src/js/modules/ResponsiveLayout/ResponsiveLayout.js +8 -10
  104. package/src/js/modules/SelectRow/SelectRow.js +1 -1
  105. package/src/js/modules/Sort/defaults/sorters/datetime.js +2 -2
  106. package/src/js/modules/Validate/Validate.js +7 -5
  107. package/src/scss/tabulator.scss +31 -12
  108. package/src/scss/themes/materialize/tabulator_materialize.scss +5 -4
  109. package/src/scss/themes/tabulator_midnight.scss +4 -0
  110. package/src/scss/themes/tabulator_site.scss +4 -0
@@ -208,7 +208,8 @@ class Export extends Module{
208
208
 
209
209
  header.forEach((col) => {
210
210
  if(col){
211
- columns.push(new ExportColumn(col.title, col.column.getComponent(), col.width, col.height, col.depth));
211
+ let title = typeof col.title === "undefined" ? "" : col.title;
212
+ columns.push(new ExportColumn(title, col.column.getComponent(), col.width, col.height, col.depth));
212
213
  }else{
213
214
  columns.push(null);
214
215
  }
@@ -382,7 +382,7 @@ class Filter extends Module{
382
382
 
383
383
  params = column.definition.headerFilterParams || {};
384
384
 
385
- params = typeof params === "function" ? params.call(self.table) : params;
385
+ params = typeof params === "function" ? params.call(self.table, cellWrapper) : params;
386
386
 
387
387
  editorElement = editor.call(this.table.modules.edit, cellWrapper, function(){}, success, cancel, params);
388
388
 
@@ -595,12 +595,11 @@ class Filter extends Module{
595
595
  }
596
596
 
597
597
  field.forEach((filter) => {
598
-
599
598
  filter = this.findFilter(filter);
600
599
 
601
600
  if(filter){
602
601
  this.filterList.push(filter);
603
- this.changed = true;
602
+ changed = true;
604
603
  }
605
604
  });
606
605
 
@@ -6,7 +6,7 @@ export default function(cell, formatterParams, onRendered){
6
6
  var value = cell.getValue();
7
7
 
8
8
  if(typeof DT != "undefined"){
9
- var newDatetime = DT.fromFormat(String(value), inputFormat);
9
+ var newDatetime = inputFormat === "iso" ? DT.fromISO(String(value)) : DT.fromFormat(String(value), inputFormat);
10
10
 
11
11
  if(newDatetime.isValid){
12
12
 
@@ -9,7 +9,8 @@ export default function (cell, formatterParams, onRendered) {
9
9
  var value = cell.getValue();
10
10
 
11
11
  if(typeof DT != "undefined"){
12
- var newDatetime = DT.fromFormat(String(value), inputFormat);
12
+
13
+ var newDatetime = inputFormat === "iso" ? DT.fromISO(String(value)) : DT.fromFormat(String(value), inputFormat);
13
14
 
14
15
  if (newDatetime.isValid){
15
16
  if(humanize){
@@ -1,3 +1,3 @@
1
1
  export default function(cell, formatterParams, onRendered){
2
- return this.table.rowManager.activeRows.indexOf(cell.getRow()._getSelf()) + 1;
2
+ return this.table.rowManager.activeRows.indexOf(cell.getRow()._getSelf()) + 1 || "";
3
3
  };
@@ -35,7 +35,7 @@ class FrozenColumns extends Module{
35
35
  this.subscribe("cell-layout", this.layoutCell.bind(this));
36
36
  this.subscribe("column-init", this.initializeColumn.bind(this));
37
37
  this.subscribe("column-width", this.layout.bind(this));
38
- this.subscribe("row-layout-before", this.layoutRow.bind(this));
38
+ this.subscribe("row-layout-after", this.layoutRow.bind(this));
39
39
  this.subscribe("table-layout", this.layout.bind(this));
40
40
  this.subscribe("scroll-horizontal", this.scrollHorizontal.bind(this));
41
41
  this.subscribe("columns-loading", this.reset.bind(this));
@@ -23,6 +23,7 @@ class FrozenRows extends Module{
23
23
  this.table.columnManager.getElement().insertBefore(this.topElement, this.table.columnManager.headersElement.nextSibling);
24
24
 
25
25
  this.subscribe("row-deleting", this.detachRow.bind(this));
26
+ this.subscribe("rows-visible", this.visibleRows.bind(this));
26
27
 
27
28
  this.registerDisplayHandler(this.getRows.bind(this), 10);
28
29
  }
@@ -36,6 +37,14 @@ class FrozenRows extends Module{
36
37
  return !!this.rows.length;
37
38
  }
38
39
 
40
+ visibleRows(viewable, rows){
41
+ this.rows.forEach((row) => {
42
+ rows.push(row);
43
+ });
44
+
45
+ return rows;
46
+ }
47
+
39
48
  //filter frozen rows out of display data
40
49
  getRows(rows){
41
50
  var self = this,
@@ -18,6 +18,7 @@ class Group{
18
18
  this.groups = [];
19
19
  this.groupList = [];
20
20
  this.generator = generator;
21
+ this.element = false;
21
22
  this.elementContents = false;
22
23
  this.height = 0;
23
24
  this.outerHeight = 0;
@@ -85,67 +86,7 @@ class Group{
85
86
  }
86
87
 
87
88
  addBindings(){
88
- var dblTap, tapHold, tap, toggleElement;
89
-
90
- if ((this.groupManager.table.options.groupContextMenu || this.groupManager.table.options.groupClickMenu) && this.groupManager.table.modExists("menu")){
91
- this.groupManager.table.modules.menu.initializeGroup.call(this.groupManager.table.modules.menu, this);
92
- }
93
-
94
- if (this.groupManager.table.options.groupTap){
95
- tap = false;
96
-
97
- this.element.addEventListener("touchstart", (e) => {
98
- tap = true;
99
- }, {passive: true});
100
-
101
- this.element.addEventListener("touchend", (e) => {
102
- if(tap){
103
- this.groupManager.table.options.groupTap(e, this.getComponent());
104
- }
105
-
106
- tap = false;
107
- });
108
- }
109
-
110
- if (this.groupManager.table.options.groupDblTap){
111
- dblTap = null;
112
-
113
- this.element.addEventListener("touchend", (e) => {
114
- if(dblTap){
115
- clearTimeout(dblTap);
116
- dblTap = null;
117
-
118
- this.groupManager.table.options.groupDblTap(e, this.getComponent());
119
- }else{
120
-
121
- dblTap = setTimeout(() => {
122
- clearTimeout(dblTap);
123
- dblTap = null;
124
- }, 300);
125
- }
126
- });
127
- }
128
-
129
- if (this.groupManager.table.options.groupTapHold){
130
- tapHold = null;
131
-
132
- this.element.addEventListener("touchstart", (e) => {
133
- clearTimeout(tapHold);
134
-
135
- tapHold = setTimeout(() => {
136
- clearTimeout(tapHold);
137
- tapHold = null;
138
- tap = false;
139
- this.groupManager.table.options.groupTapHold(e, this.getComponent());
140
- }, 1000);
141
-
142
- }, {passive: true});
143
-
144
- this.element.addEventListener("touchend", (e) => {
145
- clearTimeout(tapHold);
146
- tapHold = null;
147
- });
148
- }
89
+ var toggleElement;
149
90
 
150
91
  if(this.groupManager.table.options.groupToggleElement){
151
92
  toggleElement = this.groupManager.table.options.groupToggleElement == "arrow" ? this.arrowElement : this.element;
@@ -9,7 +9,7 @@ class GroupComponent {
9
9
  if (typeof target[name] !== "undefined") {
10
10
  return target[name];
11
11
  }else{
12
- return target._group.table.componentFunctionBinder.handle("row", target._group, name)
12
+ return target._group.groupManager.table.componentFunctionBinder.handle("row", target._group, name)
13
13
  }
14
14
  }
15
15
  })
@@ -47,13 +47,13 @@ class GroupRows extends Module{
47
47
  initialize(){
48
48
  if(this.table.options.groupBy){
49
49
 
50
- this.configureGroupSetup();
51
-
52
50
  if(this.table.options.groupUpdateOnCellEdit){
53
51
  this.subscribe("cell-value-updated", this.cellUpdated.bind(this));
54
52
  this.subscribe("row-data-changed", this.reassignRowToGroup.bind(this), 0);
55
53
  }
56
54
 
55
+ this.subscribe("table-built", this.configureGroupSetup.bind(this));
56
+
57
57
  this.subscribe("row-deleting", this.rowDeleting.bind(this));
58
58
  this.subscribe("row-deleted", this.rowsUpdated.bind(this));
59
59
  this.subscribe("scroll-horizontal", this.scrollHeaders.bind(this));
@@ -0,0 +1,195 @@
1
+ import Module from '../../core/Module.js';
2
+
3
+ import defaultImporters from './defaults/importers.js';
4
+
5
+ class Import extends Module{
6
+
7
+ constructor(table){
8
+ super(table);
9
+
10
+ this.registerTableOption("importFormat");
11
+ this.registerTableOption("importReader", "text");
12
+ }
13
+
14
+ initialize(){
15
+ this.registerTableFunction("import", this.importFromFile.bind(this));
16
+
17
+ if(this.table.options.importFormat){
18
+ this.subscribe("data-loading", this.loadDataCheck.bind(this), 10);
19
+ this.subscribe("data-load", this.loadData.bind(this), 10);
20
+ }
21
+ }
22
+
23
+ loadDataCheck(data){
24
+ return typeof data === "string";
25
+ }
26
+
27
+ loadData(data, params, config, silent, previousData){
28
+ return this.importData(this.lookupImporter(), data)
29
+ .then(this.structureData.bind(this))
30
+ .catch((err) => {
31
+ console.error("Import Error:", err || "Unable to import data")
32
+ return Promise.reject(err);
33
+ })
34
+ }
35
+
36
+ lookupImporter(importFormat){
37
+ var importer;
38
+
39
+ if(!importFormat){
40
+ importFormat = this.table.options.importFormat;
41
+ }
42
+
43
+ if(typeof importFormat === "string"){
44
+ importer = Import.importers[importFormat];
45
+ }else{
46
+ importer = importFormat;
47
+ }
48
+
49
+ if(!importer){
50
+ console.error("Import Error - Importer not found:", importFormat);
51
+ }
52
+
53
+ return importer;
54
+ }
55
+
56
+ importFromFile(importFormat, extension){
57
+ var importer = this.lookupImporter(importFormat);
58
+
59
+ if(importer){
60
+ return this.pickFile(extension)
61
+ .then(this.importData.bind(this, importer))
62
+ .then(this.structureData.bind(this))
63
+ .then(this.setData.bind(this))
64
+ .catch((err) => {
65
+ console.error("Import Error:", err || "Unable to import file")
66
+ return Promise.reject(err);
67
+ })
68
+ }
69
+ }
70
+
71
+ pickFile(extensions){
72
+ return new Promise((resolve, reject) => {
73
+ var input = document.createElement("input");
74
+ input.type = "file";
75
+ input.accept = extensions;
76
+
77
+ input.addEventListener("change", (e) => {
78
+ var file = input.files[0],
79
+ reader = new FileReader(),
80
+ data;
81
+
82
+ switch(this.table.options.importReader){
83
+ case "buffer":
84
+ reader.readAsArrayBuffer(file);
85
+ break;
86
+
87
+ case "binary":
88
+ reader.readAsBinaryString(file);
89
+ break;
90
+
91
+ case "url":
92
+ reader.readAsDataURL(file);
93
+ break;
94
+
95
+ case "text":
96
+ default:
97
+ reader.readAsText(file);
98
+ }
99
+
100
+ reader.onload = (e) => {
101
+ resolve(reader.result)
102
+ };
103
+
104
+ reader.onerror = (e) => {
105
+ console.warn("File Load Error - Unable to read file");
106
+ reject();
107
+ };
108
+ });
109
+
110
+ input.click();
111
+ });
112
+ }
113
+
114
+ importData(importer, fileContents){
115
+ var data = importer.call(this.table, fileContents);
116
+
117
+ if(data instanceof Promise){
118
+ return data;
119
+ }else{
120
+ return data ? Promise.resolve(data) : Promise.reject();
121
+ }
122
+ }
123
+
124
+ structureData(parsedData){
125
+ var data = [];
126
+
127
+ if(Array.isArray(parsedData) && parsedData.length && Array.isArray(parsedData[0])){
128
+ if(this.table.options.autoColumns){
129
+ data = this.structureArrayToObject(parsedData);
130
+ }else{
131
+ data = this.structureArrayToColumns(parsedData);
132
+ }
133
+
134
+ return data;
135
+ }else{
136
+ return parsedData;
137
+ }
138
+ }
139
+
140
+ structureArrayToObject(parsedData){
141
+ var columns = parsedData.shift();
142
+
143
+ var data = parsedData.map((values) => {
144
+ var row = {};
145
+
146
+ columns.forEach((key, i) => {
147
+ row[key] = values[i];
148
+ })
149
+
150
+ return row;
151
+ })
152
+
153
+ return data;
154
+ }
155
+
156
+ structureArrayToColumns(parsedData){
157
+ var data = [],
158
+ columns = this.table.getColumns();
159
+
160
+ //remove first row if it is the column names
161
+ if(columns[0] && parsedData[0][0]){
162
+ if(columns[0].getDefinition().title === parsedData[0][0]){
163
+ parsedData.shift();
164
+ }
165
+ }
166
+
167
+ //convert row arrays to objects
168
+ parsedData.forEach((rowData) => {
169
+ var row = {};
170
+
171
+ rowData.forEach((value, index) => {
172
+ var column = columns[index];
173
+
174
+ if(column){
175
+ row[column.getField()] = value;
176
+ }
177
+ })
178
+
179
+ data.push(row);
180
+ });
181
+
182
+ return data;
183
+ }
184
+
185
+ setData(data){
186
+ return this.table.setData(data);
187
+ }
188
+ }
189
+
190
+ Import.moduleName = "import";
191
+
192
+ //load defaults
193
+ Import.importers = defaultImporters;
194
+
195
+ export default Import;
@@ -0,0 +1,63 @@
1
+ function csvImporter(input){
2
+ var data = [],
3
+ row = 0,
4
+ col = 0,
5
+ inQuote = false;
6
+
7
+ //Iterate over each character
8
+ for (let index = 0; index < input.length; index++) {
9
+ let char = input[index],
10
+ nextChar = input[index+1];
11
+
12
+ //Initialize empty row
13
+ if(!data[row]){
14
+ data[row] = [];
15
+ }
16
+
17
+ //Initialize empty column
18
+ if(!data[row][col]){
19
+ data[row][col] = "";
20
+ }
21
+
22
+ //Handle quotation mark inside string
23
+ if (char == '"' && inQuote && nextChar == '"') {
24
+ data[row][col] += char;
25
+ index++;
26
+ continue;
27
+ }
28
+
29
+ //Begin / End Quote
30
+ if (char == '"') {
31
+ inQuote = !inQuote;
32
+ continue;
33
+ }
34
+
35
+ //Next column (if not in quote)
36
+ if (char == ',' && !inQuote) {
37
+ col++;
38
+ continue;
39
+ }
40
+
41
+ //New row if new line and not in quote (CRLF)
42
+ if (char == '\r' && nextChar == '\n' && !inQuote) {
43
+ col = 0;
44
+ row++;
45
+ index++;
46
+ continue;
47
+ }
48
+
49
+ //New row if new line and not in quote (CR or LF)
50
+ if ((char == '\r' || char == '\n') && !inQuote) {
51
+ col = 0;
52
+ row++;
53
+ continue;
54
+ }
55
+
56
+ //Normal Character, append to column
57
+ data[row][col] += char;
58
+ }
59
+
60
+ return data;
61
+ }
62
+
63
+ export default csvImporter;
@@ -0,0 +1,8 @@
1
+ export default function(input){
2
+ try {
3
+ return JSON.parse(input);
4
+ } catch(e) {
5
+ console.warn("JSON Import Error - File contents is invalid JSON", e);
6
+ return Promise.reject();
7
+ }
8
+ }
@@ -0,0 +1,7 @@
1
+ import csv from './importers/csv.js';
2
+ import json from './importers/json.js';
3
+
4
+ export default {
5
+ csv:csv,
6
+ json:json,
7
+ };
@@ -84,6 +84,11 @@ class Interaction extends Module{
84
84
  tap:null,
85
85
  tapDbl:null,
86
86
  tapHold:null,
87
+ },
88
+ group:{
89
+ tap:null,
90
+ tapDbl:null,
91
+ tapHold:null,
87
92
  }
88
93
  }
89
94
 
@@ -13,7 +13,7 @@ class Keybindings extends Module{
13
13
  this.keyupBinding = false;
14
14
  this.keydownBinding = false;
15
15
 
16
- this.registerTableOption("keybindings", []); //array for keybindings
16
+ this.registerTableOption("keybindings", {}); //array for keybindings
17
17
  this.registerTableOption("tabEndNewRow", false); //create new row when tab to end of table
18
18
  }
19
19
 
@@ -25,17 +25,8 @@ class Keybindings extends Module{
25
25
  this.pressedKeys = [];
26
26
 
27
27
  if(bindings !== false){
28
-
29
- for(let key in Keybindings.bindings){
30
- mergedBindings[key] = Keybindings.bindings[key];
31
- }
32
-
33
- if(Object.keys(bindings).length){
34
-
35
- for(let key in bindings){
36
- mergedBindings[key] = bindings[key];
37
- }
38
- }
28
+ Object.assign(mergedBindings, Keybindings.bindings)
29
+ Object.assign(mergedBindings, bindings)
39
30
 
40
31
  this.mapBindings(mergedBindings);
41
32
  this.bindEvents();
@@ -53,7 +44,11 @@ class Keybindings extends Module{
53
44
  }
54
45
 
55
46
  bindings[key].forEach((binding) => {
56
- this.mapBinding(key, binding);
47
+ var bindingList = Array.isArray(binding) ? binding : [binding];
48
+
49
+ bindingList.forEach((item) => {
50
+ this.mapBinding(key, item);
51
+ });
57
52
  });
58
53
  }
59
54
  }else{
@@ -88,7 +83,7 @@ class Keybindings extends Module{
88
83
  break;
89
84
 
90
85
  default:
91
- symbol = parseInt(symbol);
86
+ symbol = isNaN(symbol) ? symbol.toUpperCase().charCodeAt(0) : parseInt(symbol);
92
87
  binding.keys.push(symbol);
93
88
 
94
89
  if(!this.watchKeys[symbol]){
@@ -7,7 +7,7 @@ export default {
7
7
  scrollPageDown:34,
8
8
  scrollToStart:36,
9
9
  scrollToEnd:35,
10
- undo:"ctrl + 90",
11
- redo:"ctrl + 89",
12
- copyToClipboard:"ctrl + 67",
10
+ undo:["ctrl + 90", "meta + 90"],
11
+ redo:["ctrl + 89", "meta + 89"],
12
+ copyToClipboard:["ctrl + 67", "meta + 89"],
13
13
  };
@@ -22,6 +22,12 @@ export default {
22
22
  "next":"Next",
23
23
  "next_title":"Next Page",
24
24
  "all":"All",
25
+ "counter":{
26
+ "showing": "Showing",
27
+ "of": "of",
28
+ "rows": "rows",
29
+ "pages": "pages",
30
+ }
25
31
  },
26
32
  "headerFilters":{
27
33
  "default":"filter column...",