tabulator-tables 5.1.8 → 5.2.0

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 (100) hide show
  1. package/bower.json +1 -1
  2. package/dist/css/tabulator.css +59 -51
  3. package/dist/css/tabulator.css.map +1 -1
  4. package/dist/css/tabulator.min.css +1 -1
  5. package/dist/css/tabulator.min.css.map +1 -1
  6. package/dist/css/tabulator_bootstrap3.css +59 -51
  7. package/dist/css/tabulator_bootstrap3.css.map +1 -1
  8. package/dist/css/tabulator_bootstrap3.min.css +1 -1
  9. package/dist/css/tabulator_bootstrap3.min.css.map +1 -1
  10. package/dist/css/tabulator_bootstrap4.css +59 -51
  11. package/dist/css/tabulator_bootstrap4.css.map +1 -1
  12. package/dist/css/tabulator_bootstrap4.min.css +1 -1
  13. package/dist/css/tabulator_bootstrap4.min.css.map +1 -1
  14. package/dist/css/tabulator_bootstrap5.css +1575 -0
  15. package/dist/css/tabulator_bootstrap5.css.map +1 -0
  16. package/dist/css/tabulator_bootstrap5.min.css +2 -0
  17. package/dist/css/tabulator_bootstrap5.min.css.map +1 -0
  18. package/dist/css/tabulator_bulma.css +59 -51
  19. package/dist/css/tabulator_bulma.css.map +1 -1
  20. package/dist/css/tabulator_bulma.min.css +1 -1
  21. package/dist/css/tabulator_bulma.min.css.map +1 -1
  22. package/dist/css/tabulator_materialize.css +59 -51
  23. package/dist/css/tabulator_materialize.css.map +1 -1
  24. package/dist/css/tabulator_materialize.min.css +1 -1
  25. package/dist/css/tabulator_materialize.min.css.map +1 -1
  26. package/dist/css/tabulator_midnight.css +59 -51
  27. package/dist/css/tabulator_midnight.css.map +1 -1
  28. package/dist/css/tabulator_midnight.min.css +1 -1
  29. package/dist/css/tabulator_midnight.min.css.map +1 -1
  30. package/dist/css/tabulator_modern.css +59 -51
  31. package/dist/css/tabulator_modern.css.map +1 -1
  32. package/dist/css/tabulator_modern.min.css +1 -1
  33. package/dist/css/tabulator_modern.min.css.map +1 -1
  34. package/dist/css/tabulator_semanticui.css +59 -51
  35. package/dist/css/tabulator_semanticui.css.map +1 -1
  36. package/dist/css/tabulator_semanticui.min.css +1 -1
  37. package/dist/css/tabulator_semanticui.min.css.map +1 -1
  38. package/dist/css/tabulator_simple.css +59 -51
  39. package/dist/css/tabulator_simple.css.map +1 -1
  40. package/dist/css/tabulator_simple.min.css +1 -1
  41. package/dist/css/tabulator_simple.min.css.map +1 -1
  42. package/dist/css/tabulator_site.css +59 -51
  43. package/dist/css/tabulator_site.css.map +1 -1
  44. package/dist/css/tabulator_site.min.css +1 -1
  45. package/dist/css/tabulator_site.min.css.map +1 -1
  46. package/dist/js/tabulator.js +2539 -1658
  47. package/dist/js/tabulator.js.map +1 -1
  48. package/dist/js/tabulator.min.js +2 -2
  49. package/dist/js/tabulator.min.js.map +1 -1
  50. package/dist/js/tabulator_esm.js +2742 -2066
  51. package/dist/js/tabulator_esm.js.map +1 -1
  52. package/dist/js/tabulator_esm.min.js +2 -2
  53. package/dist/js/tabulator_esm.min.js.map +1 -1
  54. package/package.json +1 -1
  55. package/src/js/core/FooterManager.js +6 -13
  56. package/src/js/core/Module.js +44 -2
  57. package/src/js/core/Tabulator.js +341 -289
  58. package/src/js/core/cell/Cell.js +9 -38
  59. package/src/js/core/column/Column.js +8 -44
  60. package/src/js/core/column/defaults/options.js +0 -2
  61. package/src/js/core/defaults/options.js +5 -2
  62. package/src/js/core/modules/optional.js +2 -0
  63. package/src/js/core/row/Row.js +127 -118
  64. package/src/js/core/tools/Alert.js +63 -0
  65. package/src/js/core/tools/DataLoader.js +12 -84
  66. package/src/js/core/tools/InteractionMonitor.js +1 -1
  67. package/src/js/core/tools/ModuleBinder.js +30 -23
  68. package/src/js/core/tools/Popup.js +251 -0
  69. package/src/js/modules/ColumnCalcs/ColumnCalcs.js +2 -2
  70. package/src/js/modules/Edit/Edit.js +5 -25
  71. package/src/js/modules/Edit/List.js +997 -0
  72. package/src/js/modules/Edit/defaults/editors/autocomplete.js +6 -446
  73. package/src/js/modules/Edit/defaults/editors/input.js +4 -0
  74. package/src/js/modules/Edit/defaults/editors/list.js +7 -0
  75. package/src/js/modules/Edit/defaults/editors/number.js +4 -0
  76. package/src/js/modules/Edit/defaults/editors/select.js +5 -585
  77. package/src/js/modules/Edit/defaults/editors/textarea.js +4 -0
  78. package/src/js/modules/Edit/defaults/editors/tickCross.js +17 -5
  79. package/src/js/modules/Edit/defaults/editors.js +2 -0
  80. package/src/js/modules/Filter/Filter.js +7 -4
  81. package/src/js/modules/Format/defaults/formatters/datetime.js +9 -1
  82. package/src/js/modules/Format/defaults/formatters/datetimediff.js +10 -3
  83. package/src/js/modules/Format/defaults/formatters/link.js +12 -1
  84. package/src/js/modules/Format/defaults/formatters/money.js +5 -3
  85. package/src/js/modules/Format/defaults/formatters/tickCross.js +2 -1
  86. package/src/js/modules/FrozenColumns/FrozenColumns.js +30 -8
  87. package/src/js/modules/HtmlTableImport/HtmlTableImport.js +5 -1
  88. package/src/js/modules/Menu/Menu.js +58 -158
  89. package/src/js/modules/Mutator/Mutator.js +20 -0
  90. package/src/js/modules/Page/Page.js +7 -3
  91. package/src/js/modules/Persistence/Persistence.js +1 -1
  92. package/src/js/modules/Popup/Popup.js +194 -0
  93. package/src/js/modules/ResizeColumns/ResizeColumns.js +138 -81
  94. package/src/js/modules/Sort/defaults/sorters/datetime.js +15 -2
  95. package/src/js/modules/Tooltip/Tooltip.js +132 -0
  96. package/src/js/modules/Validate/Validate.js +107 -71
  97. package/src/scss/tabulator.scss +349 -349
  98. package/src/scss/themes/bootstrap/functions5.scss +302 -0
  99. package/src/scss/themes/bootstrap/tabulator_bootstrap5.scss +546 -0
  100. package/src/scss/themes/bootstrap/variables5.scss +1641 -0
@@ -4,55 +4,11 @@ export default class DataLoader extends CoreFeature{
4
4
  constructor(table){
5
5
  super(table);
6
6
 
7
- this.loaderElement = this.createLoaderElement(); //loader message div
8
- this.msgElement = this.createMsgElement(); //message element
9
- this.loadingElement = null;
10
- this.errorElement = null;
11
-
12
- this.requestOrder = 0; //prevent requests comming out of sequence if overridden by another load request
7
+ this.requestOrder = 0; //prevent requests coming out of sequence if overridden by another load request
13
8
  this.loading = false;
14
9
  }
15
10
 
16
- initialize(){
17
- var template;
18
-
19
- this.loaderElement.appendChild(this.msgElement);
20
-
21
- if(this.table.options.dataLoaderLoading){
22
- if(typeof this.table.options.dataLoaderLoading == "string"){
23
- template = document.createElement('template');
24
- template.innerHTML = this.table.options.dataLoaderLoading.trim();
25
- this.loadingElement = template.firstElementChild;
26
- }else{
27
- this.loadingElement = this.table.options.dataLoaderLoading;
28
- }
29
- }
30
-
31
- if(this.table.options.dataLoaderError){
32
- if(typeof this.table.options.dataLoaderError == "string"){
33
- template = document.createElement('template');
34
- template.innerHTML = this.table.options.dataLoaderError.trim();
35
- this.errorElement = template.firstElementChild;
36
- }else{
37
- this.errorElement = this.table.options.dataLoaderError;
38
- }
39
- }
40
- }
41
-
42
- createLoaderElement(){
43
- var el = document.createElement("div");
44
- el.classList.add("tabulator-loader");
45
- return el;
46
- }
47
-
48
- createMsgElement(){
49
- var el = document.createElement("div");
50
-
51
- el.classList.add("tabulator-loader-msg");
52
- el.setAttribute("role", "alert");
53
-
54
- return el;
55
- }
11
+ initialize(){}
56
12
 
57
13
  load(data, params, config, replace, silent){
58
14
  var requestNo = ++this.requestOrder;
@@ -68,7 +24,7 @@ export default class DataLoader extends CoreFeature{
68
24
  this.loading = true;
69
25
 
70
26
  if(!silent){
71
- this.showLoader();
27
+ this.alertLoader();
72
28
  }
73
29
 
74
30
  //get params for request
@@ -86,7 +42,7 @@ export default class DataLoader extends CoreFeature{
86
42
  var rowData = this.chain("data-loaded", response, null, response);
87
43
 
88
44
  if(requestNo == this.requestOrder){
89
- this.hideLoader();
45
+ this.clearAlert();
90
46
 
91
47
  if(rowData !== false){
92
48
  this.dispatchExternal("dataLoaded", rowData);
@@ -100,11 +56,11 @@ export default class DataLoader extends CoreFeature{
100
56
  this.dispatchExternal("dataLoadError", error);
101
57
 
102
58
  if(!silent){
103
- this.showError();
59
+ this.alertError();
104
60
  }
105
61
 
106
62
  setTimeout(() => {
107
- this.hideLoader();
63
+ this.clearAlert();
108
64
  }, this.table.options.dataLoaderErrorTimeout);
109
65
  })
110
66
  .finally(() => {
@@ -146,47 +102,19 @@ export default class DataLoader extends CoreFeature{
146
102
  this.requestOrder++;
147
103
  }
148
104
 
149
- showLoader(){
105
+ alertLoader(){
150
106
  var shouldLoad = typeof this.table.options.dataLoader === "function" ? this.table.options.dataLoader() : this.table.options.dataLoader;
151
107
 
152
108
  if(shouldLoad){
153
- this.hideLoader();
154
-
155
- while(this.msgElement.firstChild) this.msgElement.removeChild(this.msgElement.firstChild);
156
-
157
- this.msgElement.classList.remove("tabulator-error");
158
- this.msgElement.classList.add("tabulator-loading");
159
-
160
- if(this.loadingElement){
161
- this.msgElement.appendChild(this.loadingElement);
162
- }else{
163
- this.msgElement.innerHTML = this.langText("data|loading");
164
- }
165
-
166
- this.table.element.appendChild(this.loaderElement);
109
+ this.table.alertManager.alert(this.table.options.dataLoaderLoading || this.langText("data|loading"));
167
110
  }
168
111
  }
169
112
 
170
- showError(){
171
- this.hideLoader();
172
-
173
- while(this.msgElement.firstChild) this.msgElement.removeChild(this.msgElement.firstChild);
174
- this.msgElement.classList.remove("tabulator-loading");
175
- this.msgElement.classList.add("tabulator-error");
176
-
177
- if(this.errorElement){
178
- this.msgElement.appendChild(this.errorElement);
179
- }else{
180
- this.msgElement.innerHTML = this.langText("data|error");
181
- }
182
-
183
- this.table.element.appendChild(this.loaderElement);
113
+ alertError(){
114
+ this.table.alertManager.alert(this.table.options.dataLoaderError || this.langText("data|error"), "error");
184
115
  }
185
116
 
186
-
187
- hideLoader(){
188
- if(this.loaderElement.parentNode){
189
- this.loaderElement.parentNode.removeChild(this.loaderElement);
190
- }
117
+ clearAlert(){
118
+ this.table.alertManager.clear();
191
119
  }
192
120
  }
@@ -88,7 +88,7 @@ export default class InteractionManager extends CoreFeature {
88
88
  if(this.pseudoTrackers[key].target !== target){
89
89
 
90
90
  if(this.pseudoTrackers[key].target){
91
- this.dispatch(key + "-mouseleave", e, target);
91
+ this.dispatch(key + "-mouseleave", e, this.pseudoTrackers[key].target);
92
92
  }
93
93
 
94
94
  this.pseudoMouseLeave(key, e);
@@ -2,23 +2,23 @@ import * as coreModules from '../modules/core.js';
2
2
  import TableRegistry from './TableRegistry.js';
3
3
 
4
4
  export default class ModuleBinder {
5
-
5
+
6
6
  constructor(tabulator, modules){
7
7
  this.bindStaticFuctionality(tabulator);
8
8
  this.bindModules(tabulator, coreModules, true);
9
-
9
+
10
10
  if(modules){
11
11
  this.bindModules(tabulator, modules);
12
12
  }
13
13
  }
14
-
14
+
15
15
  bindStaticFuctionality(tabulator){
16
16
  tabulator.moduleBindings = {};
17
-
17
+
18
18
  tabulator.extendModule = function(name, property, values){
19
19
  if(tabulator.moduleBindings[name]){
20
20
  var source = tabulator.moduleBindings[name][property];
21
-
21
+
22
22
  if(source){
23
23
  if(typeof values == "object"){
24
24
  for(let key in values){
@@ -34,65 +34,72 @@ export default class ModuleBinder {
34
34
  console.warn("Module Error - module does not exist:", name);
35
35
  }
36
36
  };
37
-
37
+
38
38
  tabulator.registerModule = function(modules){
39
39
  if(!Array.isArray(modules)){
40
40
  modules = [modules];
41
41
  }
42
-
42
+
43
43
  modules.forEach((mod) => {
44
44
  tabulator.registerModuleBinding(mod)
45
45
  });
46
46
  }
47
-
47
+
48
48
  tabulator.registerModuleBinding = function(mod){
49
49
  tabulator.moduleBindings[mod.moduleName] = mod;
50
50
  };
51
-
51
+
52
52
  tabulator.findTable = function(query){
53
53
  var results = TableRegistry.lookupTable(query, true);
54
54
  return Array.isArray(results) && !results.length ? false : results;
55
55
  }
56
-
56
+
57
57
  //ensure that module are bound to instantiated function
58
58
  tabulator.prototype.bindModules = function(){
59
- var orderedMods = [],
59
+ var orderedStartMods = [],
60
+ orderedEndMods = [],
60
61
  unOrderedMods = [];
61
-
62
+
62
63
  this.modules = {};
63
-
64
+
64
65
  for(var name in tabulator.moduleBindings){
65
66
  let mod = tabulator.moduleBindings[name];
66
67
  let module = new mod(this);
67
-
68
+
68
69
  this.modules[name] = module;
69
-
70
+
70
71
  if(mod.prototype.moduleCore){
71
72
  this.modulesCore.push(module);
72
73
  }else{
73
74
  if(mod.moduleInitOrder){
74
- orderedMods.push(module);
75
+ if(mod.moduleInitOrder < 0){
76
+ orderedStartMods.push(module);
77
+ }else{
78
+ orderedEndMods.push(module);
79
+ }
80
+
75
81
  }else{
76
82
  unOrderedMods.push(module);
77
83
  }
78
84
  }
79
85
  }
80
-
81
- orderedMods.sort((a, b) => a.moduleInitOrder > b.moduleInitOrder ? 1 : -1);
82
-
83
- this.modulesRegular = orderedMods.concat(unOrderedMods);
86
+
87
+ orderedStartMods.sort((a, b) => a.moduleInitOrder > b.moduleInitOrder ? 1 : -1);
88
+ orderedEndMods.sort((a, b) => a.moduleInitOrder > b.moduleInitOrder ? 1 : -1);
89
+
90
+ this.modulesRegular = orderedStartMods.concat(unOrderedMods.concat(orderedEndMods));
84
91
  }
85
92
  }
86
-
93
+
87
94
  bindModules(tabulator, modules, core){
88
95
  var mods = Object.values(modules);
89
-
96
+
90
97
  if(core){
91
98
  mods.forEach((mod) => {
92
99
  mod.prototype.moduleCore = true;
93
100
  });
94
101
  }
95
-
102
+
96
103
  tabulator.registerModule(mods);
97
104
  }
98
105
  }
@@ -0,0 +1,251 @@
1
+ import CoreFeature from '../CoreFeature.js';
2
+ import Helpers from './Helpers.js';
3
+
4
+ export default class Popup extends CoreFeature{
5
+ constructor(table, element, parent){
6
+ super(table);
7
+
8
+ this.element = element;
9
+ this.container = this._lookupContainer();
10
+
11
+ this.parent = parent;
12
+
13
+ this.reversedX = false;
14
+ this.childPopup = null;
15
+ this.blurable = false;
16
+ this.blurCallback = null;
17
+ this.renderedCallback = null;
18
+
19
+ this.visible = false;
20
+
21
+ this.element.classList.add("tabulator-popup-container");
22
+
23
+ this.blurEvent = this.hide.bind(this, false);
24
+ this.escEvent = this._escapeCheck.bind(this);
25
+ }
26
+
27
+ _lookupContainer(){
28
+ var container = this.table.options.popupContainer;
29
+
30
+ if(typeof container === "string"){
31
+ container = document.querySelector(container);
32
+
33
+ if(!container){
34
+ console.warn("Menu Error - no container element found matching selector:", this.table.options.popupContainer , "(defaulting to document body)");
35
+ }
36
+ }else if (container === true){
37
+ container = this.table.element;
38
+ }
39
+
40
+ if(container && !this._checkContainerIsParent(container)){
41
+ container = false;
42
+ console.warn("Menu Error - container element does not contain this table:", this.table.options.popupContainer , "(defaulting to document body)");
43
+ }
44
+
45
+ if(!container){
46
+ container = document.body;
47
+ }
48
+
49
+ return container;
50
+ }
51
+
52
+ _checkContainerIsParent(container, element = this.table.element){
53
+ if(container === element){
54
+ return true;
55
+ }else{
56
+ return element.parentNode ? this._checkContainerIsParent(container, element.parentNode) : false;
57
+ }
58
+ }
59
+
60
+ renderCallback(callback){
61
+ this.renderedCallback = callback;
62
+ }
63
+
64
+ containerEventCoords(e){
65
+ var touch = !(e instanceof MouseEvent);
66
+
67
+ var x = touch ? e.touches[0].pageX : e.pageX;
68
+ var y = touch ? e.touches[0].pageY : e.pageY;
69
+
70
+ if(this.container !== document.body){
71
+ let parentOffset = Helpers.elOffset(this.container);
72
+
73
+ x -= parentOffset.left;
74
+ y -= parentOffset.top;
75
+ }
76
+
77
+ return {x, y};
78
+ }
79
+
80
+ elementPositionCoords(element, position = "right"){
81
+ var offset = Helpers.elOffset(element),
82
+ containerOffset, x, y;
83
+
84
+ if(this.container !== document.body){
85
+ containerOffset = Helpers.elOffset(this.container);
86
+
87
+ offset.left -= containerOffset.left;
88
+ offset.top -= containerOffset.top;
89
+ }
90
+
91
+ switch(position){
92
+ case "right":
93
+ x = offset.left + element.offsetWidth;
94
+ y = offset.top - 1;
95
+ break;
96
+
97
+ case "bottom":
98
+ x = offset.left;
99
+ y = offset.top + element.offsetHeight;
100
+ break;
101
+ }
102
+
103
+ return {x, y, offset};
104
+ }
105
+
106
+ show(origin, position){
107
+ var x, y, parentEl, parentOffset, containerOffset, coords;
108
+
109
+ if(origin instanceof HTMLElement){
110
+ parentEl = origin;
111
+ coords = this.elementPositionCoords(origin, position);
112
+
113
+ parentOffset = coords.offset;
114
+ x = coords.x;
115
+ y = coords.y;
116
+
117
+ }else if(typeof origin === "number"){
118
+ parentOffset = {top:0, left:0};
119
+ x = origin;
120
+ y = position;
121
+ }else{
122
+ coords = this.containerEventCoords(origin);
123
+
124
+ x = coords.x;
125
+ y = coords.y;
126
+
127
+ this.reversedX = false;
128
+ }
129
+
130
+ this.element.style.top = y + "px";
131
+ this.element.style.left = x + "px";
132
+
133
+ this.container.appendChild(this.element);
134
+
135
+ if(typeof this.renderedCallback === "function"){
136
+ this.renderedCallback();
137
+ }
138
+
139
+ this._fitToScreen(x, y, parentEl, parentOffset, position);
140
+
141
+ this.visible = true;
142
+
143
+ return this;
144
+ }
145
+
146
+ _fitToScreen(x, y, parentEl, parentOffset, position){
147
+ var scrollTop = this.container === document.body ? document.documentElement.scrollTop : this.container.scrollTop;
148
+
149
+ //move menu to start on right edge if it is too close to the edge of the screen
150
+ if((x + this.element.offsetWidth) >= this.container.offsetWidth || this.reversedX){
151
+ this.element.style.left = "";
152
+
153
+ if(parentEl){
154
+ this.element.style.right = (this.container.offsetWidth - parentOffset.left) + "px";
155
+ }else{
156
+ this.element.style.right = (this.container.offsetWidth - x) + "px";
157
+ }
158
+
159
+ this.reversedX = true;
160
+ }
161
+
162
+ //move menu to start on bottom edge if it is too close to the edge of the screen
163
+ if((y + this.element.offsetHeight) > Math.max(this.container.offsetHeight, scrollTop ? this.container.scrollHeight : 0)) {
164
+ if(parentEl){
165
+ switch(position){
166
+ case "bottom":
167
+ this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight - parentEl.offsetHeight - 1) + "px";
168
+ break;
169
+
170
+ default:
171
+ this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight + parentEl.offsetHeight + 1) + "px";
172
+ }
173
+
174
+ }else{
175
+ this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight) + "px";
176
+ }
177
+ }
178
+ }
179
+
180
+ isVisible(){
181
+ return this.visible;
182
+ }
183
+
184
+ hideOnBlur(callback){
185
+ this.blurable = true;
186
+
187
+ if(this.visible){
188
+ setTimeout(() => {
189
+ this.table.rowManager.element.addEventListener("scroll", this.blurEvent);
190
+ this.subscribe("cell-editing", this.blurEvent);
191
+ document.body.addEventListener("click", this.blurEvent);
192
+ document.body.addEventListener("contextmenu", this.blurEvent);
193
+ window.addEventListener("resize", this.blurEvent);
194
+ document.body.addEventListener("keydown", this.escEvent);
195
+ }, 100);
196
+
197
+ this.blurCallback = callback;
198
+ }
199
+
200
+ return this;
201
+ }
202
+
203
+ _escapeCheck(e){
204
+ if(e.keyCode == 27){
205
+ this.hide();
206
+ }
207
+ }
208
+
209
+ hide(silent = false){
210
+ if(this.visible){
211
+ if(this.blurable){
212
+ document.body.removeEventListener("keydown", this.escEvent);
213
+ document.body.removeEventListener("click", this.blurEvent);
214
+ document.body.removeEventListener("contextmenu", this.blurEvent);
215
+ window.removeEventListener("resize", this.blurEvent);
216
+ this.table.rowManager.element.removeEventListener("scroll", this.blurEvent);
217
+ this.unsubscribe("cell-editing", this.blurEvent);
218
+ }
219
+
220
+ if(this.childPopup){
221
+ this.childPopup.hide();
222
+ }
223
+
224
+ if(this.parent){
225
+ this.parent.childPopup = null;
226
+ }
227
+
228
+ if(this.element.parentNode){
229
+ this.element.parentNode.removeChild(this.element);
230
+ }
231
+
232
+ this.visible = false;
233
+
234
+ if(this.blurCallback && !silent){
235
+ this.blurCallback();
236
+ }
237
+ }
238
+
239
+ return this;
240
+ }
241
+
242
+ child(element){
243
+ if(this.childPopup){
244
+ this.childPopup.hide();
245
+ }
246
+
247
+ this.childPopup = new Popup(this.table, element, this);
248
+
249
+ return this.childPopup;
250
+ }
251
+ }
@@ -212,7 +212,7 @@ class ColumnCalcs extends Module{
212
212
 
213
213
  if(this.botInitialized){
214
214
  this.botInitialized = false;
215
- this.table.footerManager.remove(this.botElement);
215
+ this.footerRemove(this.botElement);
216
216
  changed = true;
217
217
  }
218
218
 
@@ -230,7 +230,7 @@ class ColumnCalcs extends Module{
230
230
 
231
231
  initializeBottomRow(){
232
232
  if(!this.botInitialized){
233
- this.table.footerManager.prepend(this.botElement);
233
+ this.footerPrepend(this.botElement);
234
234
  this.botInitialized = true;
235
235
  }
236
236
  }
@@ -404,13 +404,7 @@ class Edit extends Module{
404
404
 
405
405
  cellEl = cell.getElement();
406
406
 
407
- if(cancel){
408
- if(cell.column.modules.validate && this.table.modExists("validate")){
409
- this.table.modules.validate.cellValidate(cell);
410
- }
411
- }else{
412
- cellEl.classList.remove("tabulator-validation-fail");
413
- }
407
+ this.dispatch("edit-editor-clear", cell, cancel);
414
408
 
415
409
  cellEl.classList.remove("tabulator-editing");
416
410
 
@@ -543,14 +537,10 @@ class Edit extends Module{
543
537
  return;
544
538
  }
545
539
 
546
- //handle successfull value change
540
+ //handle successful value change
547
541
  function success(value){
548
542
  if(self.currentCell === cell){
549
- var valid = true;
550
-
551
- if(cell.column.modules.validate && self.table.modExists("validate") && self.table.options.validationMode != "manual"){
552
- valid = self.table.modules.validate.validate(cell.column.modules.validate, cell, value);
553
- }
543
+ var valid = self.chain("edit-success", [cell, value], true, true);
554
544
 
555
545
  if(valid === true || self.table.options.validationMode === "highlight"){
556
546
  self.clearEditor();
@@ -568,19 +558,11 @@ class Edit extends Module{
568
558
 
569
559
  cell.setValue(value, true);
570
560
 
571
- if(valid !== true){
572
- element.classList.add("tabulator-validation-fail");
573
- self.table.externalEvents.dispatch("validationFailed", cell.getComponent(), value, valid);
574
- return false;
575
- }
576
-
577
- return true;
561
+ return valid === true;
578
562
  }else{
579
563
  self.invalidEdit = true;
580
- element.classList.add("tabulator-validation-fail");
581
564
  self.focusCellNoEvent(cell, true);
582
565
  rendered();
583
- self.table.externalEvents.dispatch("validationFailed", cell.getComponent(), value, valid);
584
566
  return false;
585
567
  }
586
568
  }else{
@@ -705,9 +687,7 @@ class Edit extends Module{
705
687
  if(cell.modules.edit && cell.modules.edit.edited){
706
688
  cell.modules.edit.edited = false;
707
689
 
708
- if(cell.modules.validate){
709
- cell.modules.validate.invalid = false;
710
- }
690
+ this.dispatch("edit-edited-clear", cell);
711
691
  }
712
692
 
713
693
  editIndex = this.editedCells.indexOf(cell);