datatables.net-autofill 2.7.1 → 3.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/js/dataTables.autoFill.js +948 -1244
- package/js/dataTables.autoFill.min.js +3 -3
- package/js/dataTables.autoFill.min.mjs +3 -3
- package/js/dataTables.autoFill.mjs +930 -1226
- package/package.json +3 -15
- package/types/types.d.ts +186 -77
|
@@ -1,1287 +1,991 @@
|
|
|
1
|
-
/*! AutoFill
|
|
2
|
-
*
|
|
1
|
+
/*! AutoFill 3.0.0-beta.1 for DataTables
|
|
2
|
+
* Copyright (c) SpryMedia Ltd - datatables.net/license
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
(function(
|
|
6
|
-
if (
|
|
5
|
+
(function(factory){
|
|
6
|
+
if (typeof define === 'function' && define.amd) {
|
|
7
7
|
// AMD
|
|
8
|
-
define(
|
|
9
|
-
return factory(
|
|
10
|
-
}
|
|
8
|
+
define(['datatables.net'], function (dt) {
|
|
9
|
+
return factory(window, document, dt);
|
|
10
|
+
});
|
|
11
11
|
}
|
|
12
|
-
else if (
|
|
12
|
+
else if (typeof exports === 'object') {
|
|
13
13
|
// CommonJS
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
require('datatables.net')(root, $);
|
|
14
|
+
var cjsRequires = function (root) {
|
|
15
|
+
if (! root.DataTable) {
|
|
16
|
+
require('datatables.net')(root);
|
|
18
17
|
}
|
|
19
18
|
};
|
|
20
19
|
|
|
21
20
|
if (typeof window === 'undefined') {
|
|
22
|
-
module.exports = function (root
|
|
23
|
-
if (
|
|
21
|
+
module.exports = function (root) {
|
|
22
|
+
if (! root) {
|
|
24
23
|
// CommonJS environments without a window global must pass a
|
|
25
24
|
// root. This will give an error otherwise
|
|
26
25
|
root = window;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
cjsRequires( root, $ );
|
|
34
|
-
return factory( $, root, root.document );
|
|
28
|
+
cjsRequires(root);
|
|
29
|
+
return factory(root, root.document, root.DataTable);
|
|
35
30
|
};
|
|
36
31
|
}
|
|
37
32
|
else {
|
|
38
|
-
cjsRequires(
|
|
39
|
-
module.exports = factory(
|
|
33
|
+
cjsRequires(window);
|
|
34
|
+
module.exports = factory(window, window.document, window.DataTable);
|
|
40
35
|
}
|
|
41
36
|
}
|
|
42
37
|
else {
|
|
43
38
|
// Browser
|
|
44
|
-
factory(
|
|
39
|
+
factory(window, document, window.DataTable);
|
|
45
40
|
}
|
|
46
|
-
}(function(
|
|
41
|
+
}(function(window, document, DataTable) {
|
|
47
42
|
'use strict';
|
|
48
|
-
var DataTable = $.fn.dataTable;
|
|
49
|
-
|
|
50
43
|
|
|
44
|
+
var Dom = DataTable.Dom;
|
|
45
|
+
var Api = DataTable.Api;
|
|
46
|
+
var util = DataTable.util;
|
|
51
47
|
|
|
52
|
-
/**
|
|
53
|
-
* @summary AutoFill
|
|
54
|
-
* @description Add Excel like click and drag auto-fill options to DataTables
|
|
55
|
-
* @version 2.7.1
|
|
56
|
-
* @author SpryMedia Ltd (www.sprymedia.co.uk)
|
|
57
|
-
* @copyright SpryMedia Ltd.
|
|
58
|
-
*
|
|
59
|
-
* This source file is free software, available under the following license:
|
|
60
|
-
* MIT license - http://datatables.net/license/mit
|
|
61
|
-
*
|
|
62
|
-
* This source file is distributed in the hope that it will be useful, but
|
|
63
|
-
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
64
|
-
* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
|
|
65
|
-
*
|
|
66
|
-
* For details please refer to: http://www.datatables.net
|
|
67
|
-
*/
|
|
68
48
|
|
|
49
|
+
if (!DataTable || !DataTable.versionCheck || !DataTable.versionCheck('3')) {
|
|
50
|
+
throw 'Warning: AutoFill requires DataTables 3 or greater';
|
|
51
|
+
}
|
|
69
52
|
var _instance = 0;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
53
|
+
class AutoFill {
|
|
54
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
55
|
+
* Public methods (exposed via the DataTables API below)
|
|
56
|
+
*/
|
|
57
|
+
enabled() {
|
|
58
|
+
return this.s.enabled;
|
|
59
|
+
}
|
|
60
|
+
enable(flag = true) {
|
|
61
|
+
let that = this;
|
|
62
|
+
if (flag === false) {
|
|
63
|
+
return this.disable();
|
|
64
|
+
}
|
|
65
|
+
this.s.enabled = true;
|
|
66
|
+
this._focusListener();
|
|
67
|
+
this.dom.handle.on('mousedown touchstart', function (e) {
|
|
68
|
+
that._mousedown(e);
|
|
69
|
+
return false;
|
|
70
|
+
});
|
|
71
|
+
let orientationReset = function () {
|
|
72
|
+
that.s.handle = {
|
|
73
|
+
height: 0,
|
|
74
|
+
width: 0
|
|
75
|
+
};
|
|
76
|
+
that.dom.handle.css({
|
|
77
|
+
height: '',
|
|
78
|
+
width: ''
|
|
79
|
+
});
|
|
80
|
+
if (that.dom.attachedTo) {
|
|
81
|
+
that._attach(that.dom.attachedTo);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
window.addEventListener('resize', function () {
|
|
85
|
+
let handle = Dom.s('div.dtaf-handle');
|
|
86
|
+
if (handle.count() > 0 && that.dom.attachedTo) {
|
|
87
|
+
that._attach(that.dom.attachedTo);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
window.addEventListener('orientationchange', function () {
|
|
91
|
+
setTimeout(function () {
|
|
92
|
+
orientationReset();
|
|
93
|
+
setTimeout(orientationReset, 150);
|
|
94
|
+
}, 50);
|
|
95
|
+
});
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
disable() {
|
|
99
|
+
this.s.enabled = false;
|
|
100
|
+
this._focusListenerRemove();
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
104
|
+
* Constructor
|
|
105
|
+
*/
|
|
106
|
+
constructor(dt, opts) {
|
|
107
|
+
this.c = util.object.assignDeep({}, AutoFill.defaults, opts);
|
|
108
|
+
this.s = {
|
|
109
|
+
dt: new DataTable.Api(dt),
|
|
110
|
+
enabled: false,
|
|
111
|
+
end: { column: 0, row: 0 },
|
|
112
|
+
handle: {
|
|
113
|
+
height: 0,
|
|
114
|
+
width: 0
|
|
115
|
+
},
|
|
116
|
+
namespace: '.autoFill' + _instance++,
|
|
117
|
+
scroll: {
|
|
118
|
+
windowHeight: 0,
|
|
119
|
+
windowWidth: 0,
|
|
120
|
+
dtTop: 0,
|
|
121
|
+
dtLeft: 0,
|
|
122
|
+
dtHeight: 0,
|
|
123
|
+
dtWidth: 0
|
|
124
|
+
},
|
|
125
|
+
scrollInterval: null,
|
|
126
|
+
start: { column: 0, row: 0 }
|
|
127
|
+
};
|
|
128
|
+
this.dom = {
|
|
129
|
+
attachedTo: null,
|
|
130
|
+
container: Dom.c('div').classAdd('dtaf-container'),
|
|
131
|
+
closeButton: Dom
|
|
132
|
+
.c('button')
|
|
133
|
+
.classAdd(AutoFill.classes.close)
|
|
134
|
+
.html('×'),
|
|
135
|
+
dtScroll: null,
|
|
136
|
+
handle: Dom.c('div').classAdd('dtaf-handle'),
|
|
137
|
+
list: Dom
|
|
138
|
+
.c('div')
|
|
139
|
+
.classAdd('dtaf-list')
|
|
140
|
+
.html(this.s.dt.i18n('autoFill.info', ''))
|
|
141
|
+
.attr('aria-modal', true)
|
|
142
|
+
.attr('role', 'dialog')
|
|
143
|
+
.append(Dom.c('div').classAdd('dtaf-list-items')),
|
|
144
|
+
offsetParent: null,
|
|
145
|
+
start: null,
|
|
146
|
+
select: {
|
|
147
|
+
top: Dom.c('div').classAdd('dtaf-select top'),
|
|
148
|
+
right: Dom.c('div').classAdd('dtaf-select right'),
|
|
149
|
+
bottom: Dom.c('div').classAdd('dtaf-select bottom'),
|
|
150
|
+
left: Dom.c('div').classAdd('dtaf-select left')
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
this.dom.list.appendTo(this.dom.container);
|
|
154
|
+
// Go!
|
|
155
|
+
this._init();
|
|
156
|
+
}
|
|
157
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
158
|
+
* Private methods
|
|
159
|
+
*/
|
|
160
|
+
/**
|
|
161
|
+
* Initialise the RowReorder instance
|
|
162
|
+
*/
|
|
163
|
+
_init() {
|
|
164
|
+
let that = this;
|
|
165
|
+
let dt = this.s.dt;
|
|
166
|
+
// Need to account for scrolling as it has a different DOM structure
|
|
167
|
+
let dtScroll = Dom
|
|
168
|
+
.s(this.s.dt.table().container())
|
|
169
|
+
.find('div.dt-scroll-body');
|
|
170
|
+
// Make the instance accessible to the API
|
|
171
|
+
dt.settings()[0].autoFill = this;
|
|
172
|
+
if (dtScroll.count()) {
|
|
173
|
+
this.dom.dtScroll = dtScroll;
|
|
174
|
+
// Need to scroll container to be the offset parent
|
|
175
|
+
if (dtScroll.css('position') === 'static') {
|
|
176
|
+
dtScroll.css('position', 'relative');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (this.c.enable !== false) {
|
|
180
|
+
this.enable();
|
|
181
|
+
}
|
|
182
|
+
dt.on('destroy.autoFill', function () {
|
|
183
|
+
that._focusListenerRemove();
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Display the AutoFill drag handle by appending it to a table cell. This is
|
|
188
|
+
* the opposite of the _detach method.
|
|
189
|
+
*
|
|
190
|
+
* @param node Cell to insert the handle into
|
|
191
|
+
*/
|
|
192
|
+
_attach(node) {
|
|
193
|
+
let dt = this.s.dt;
|
|
194
|
+
let idx = dt.cell(node).index();
|
|
195
|
+
let handle = this.dom.handle;
|
|
196
|
+
let handleDim = this.s.handle;
|
|
197
|
+
if (!idx ||
|
|
198
|
+
dt.columns(this.c.columns).indexes().indexOf(idx.column) === -1) {
|
|
199
|
+
this._detach();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (!this.dom.offsetParent) {
|
|
203
|
+
// We attach to the table's offset parent
|
|
204
|
+
this.dom.offsetParent = dt.table().node()
|
|
205
|
+
.offsetParent;
|
|
206
|
+
}
|
|
207
|
+
if (!handleDim.height || !handleDim.width) {
|
|
208
|
+
// Append to document so we can get its size. Not expecting it to
|
|
209
|
+
// change during the life time of the page
|
|
210
|
+
handle.appendTo('body');
|
|
211
|
+
handleDim.height = handle.height('outer');
|
|
212
|
+
handleDim.width = handle.width('outer');
|
|
213
|
+
}
|
|
214
|
+
// Might need to go through multiple offset parents
|
|
215
|
+
let offset = this._getPosition(node, this.dom.offsetParent);
|
|
216
|
+
this.dom.attachedTo = node;
|
|
217
|
+
handle
|
|
218
|
+
.css({
|
|
219
|
+
top: offset.top + node.offsetHeight - handleDim.height + 'px',
|
|
220
|
+
left: offset.left + node.offsetWidth - handleDim.width + 'px'
|
|
221
|
+
})
|
|
222
|
+
.appendTo(this.dom.offsetParent);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Determine can the fill type should be. This can be automatic, or ask the
|
|
226
|
+
* end user.
|
|
227
|
+
*
|
|
228
|
+
* @param cells Information about the selected cells from the key up
|
|
229
|
+
* function
|
|
230
|
+
*/
|
|
231
|
+
_actionSelector(cells) {
|
|
232
|
+
let that = this;
|
|
233
|
+
let dt = this.s.dt;
|
|
234
|
+
let actions = AutoFill.actions;
|
|
235
|
+
let available = [];
|
|
236
|
+
// "Ask" each plug-in if it wants to handle this data
|
|
237
|
+
util.object.each(actions, function (key, action) {
|
|
238
|
+
if (action.available(dt, cells)) {
|
|
239
|
+
available.push(key);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
if (available.length === 1 && this.c.alwaysAsk === false) {
|
|
243
|
+
// Only one action available - enact it immediately
|
|
244
|
+
let result = actions[available[0]].execute(dt, cells, null);
|
|
245
|
+
this._update(result, cells);
|
|
246
|
+
}
|
|
247
|
+
else if (available.length > 1 || this.c.alwaysAsk) {
|
|
248
|
+
// Multiple actions available - ask the end user what they want to
|
|
249
|
+
// do
|
|
250
|
+
let list = this.dom.list.children('div.dtaf-list-items').empty();
|
|
251
|
+
// Add a cancel option
|
|
252
|
+
available.push('cancel');
|
|
253
|
+
for (let i = 0; i < available.length; i++) {
|
|
254
|
+
let name = available[i];
|
|
255
|
+
list.append(Dom
|
|
256
|
+
.c('button')
|
|
257
|
+
.classAdd(AutoFill.classes.btn)
|
|
258
|
+
.html(actions[name].option(dt, cells))
|
|
259
|
+
.append(Dom
|
|
260
|
+
.c('span')
|
|
261
|
+
.classAdd('dtaf-button')
|
|
262
|
+
.html(dt.i18n('autoFill.button', '')))
|
|
263
|
+
.on('click', function (e) {
|
|
264
|
+
if (e.target.nodeName.toLowerCase() !== 'button') {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
let result = actions[name].execute(dt, cells, Dom.s(this).closest('button'));
|
|
268
|
+
that._update(result, cells);
|
|
269
|
+
that.dom.container.remove();
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
this.dom.container.appendTo('body');
|
|
273
|
+
this.dom.container.on('click', function (e) {
|
|
274
|
+
// Ignore clicks that aren't on the background
|
|
275
|
+
if (e.target !== that.dom.container.get(0)) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
that.dom.container.remove();
|
|
279
|
+
});
|
|
280
|
+
if (this.c.closeButton) {
|
|
281
|
+
this.dom.list
|
|
282
|
+
.prepend(this.dom.closeButton)
|
|
283
|
+
.classAdd(AutoFill.classes.closeable);
|
|
284
|
+
this.dom.closeButton.on('click', function () {
|
|
285
|
+
return that.dom.container.trigger('click');
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Remove the AutoFill handle from the document
|
|
292
|
+
*/
|
|
293
|
+
_detach() {
|
|
294
|
+
this.dom.attachedTo = null;
|
|
295
|
+
this.dom.handle.detach();
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Draw the selection outline by calculating the range between the start
|
|
299
|
+
* and end cells, then placing the highlighting elements to draw a rectangle
|
|
300
|
+
*
|
|
301
|
+
* @param target End cell
|
|
302
|
+
* @param e Originating event
|
|
303
|
+
* @private
|
|
304
|
+
*/
|
|
305
|
+
_drawSelection(target, e) {
|
|
306
|
+
// Calculate boundary for start cell to this one
|
|
307
|
+
var dt = this.s.dt;
|
|
308
|
+
var start = this.s.start;
|
|
309
|
+
var startCell = Dom.s(this.dom.start);
|
|
310
|
+
var end = {
|
|
311
|
+
row: this.c.vertical
|
|
312
|
+
? dt
|
|
313
|
+
.rows({ page: 'current' })
|
|
314
|
+
.nodes()
|
|
315
|
+
.indexOf(target.parentNode)
|
|
316
|
+
: start.row,
|
|
317
|
+
column: this.c.horizontal ? Dom.s(target).index() : start.column
|
|
318
|
+
};
|
|
319
|
+
var colIndx = dt.column.index('toData', end.column);
|
|
320
|
+
var endRow = dt.row(':eq(' + end.row + ')', { page: 'current' }); // Workaround for M581
|
|
321
|
+
var endCell = Dom.s(dt.cell(endRow.index(), colIndx).node());
|
|
322
|
+
// Be sure that is a DataTables controlled cell
|
|
323
|
+
if (!dt.cell(endCell.get(0)).any()) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// if target is not in the columns available - do nothing
|
|
327
|
+
if (dt.columns(this.c.columns).indexes().indexOf(colIndx) === -1 ||
|
|
328
|
+
end.row === -1) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
this.s.end = end;
|
|
332
|
+
var top, bottom, left, right, height, width;
|
|
333
|
+
top = start.row < end.row ? startCell : endCell;
|
|
334
|
+
bottom = start.row < end.row ? endCell : startCell;
|
|
335
|
+
left = start.column < end.column ? startCell : endCell;
|
|
336
|
+
right = start.column < end.column ? endCell : startCell;
|
|
337
|
+
top = this._getPosition(top.get(0)).top;
|
|
338
|
+
left = this._getPosition(left.get(0)).left;
|
|
339
|
+
height =
|
|
340
|
+
this._getPosition(bottom.get(0)).top + bottom.height('outer') - top;
|
|
341
|
+
width =
|
|
342
|
+
this._getPosition(right.get(0)).left + right.width('outer') - left;
|
|
343
|
+
var select = this.dom.select;
|
|
344
|
+
select.top.css({
|
|
345
|
+
top: top + 'px',
|
|
346
|
+
left: left + 'px',
|
|
347
|
+
width: width + 'px'
|
|
348
|
+
});
|
|
349
|
+
select.left.css({
|
|
350
|
+
top: top + 'px',
|
|
351
|
+
left: left + 'px',
|
|
352
|
+
height: height + 'px'
|
|
353
|
+
});
|
|
354
|
+
select.bottom.css({
|
|
355
|
+
top: top + height + 'px',
|
|
356
|
+
left: left + 'px',
|
|
357
|
+
width: width + 'px'
|
|
358
|
+
});
|
|
359
|
+
select.right.css({
|
|
360
|
+
top: top + 'px',
|
|
361
|
+
left: left + width + 'px',
|
|
362
|
+
height: height + 'px'
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Use the Editor API to perform an update based on the new data for the
|
|
367
|
+
* cells
|
|
368
|
+
*
|
|
369
|
+
* @param cells Information about the selected cells from the key up
|
|
370
|
+
* function
|
|
371
|
+
*/
|
|
372
|
+
_editor(cells) {
|
|
373
|
+
let dt = this.s.dt;
|
|
374
|
+
let editor = this.c.editor;
|
|
375
|
+
if (!editor) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
// Build the object structure for Editor's multi-row editing
|
|
379
|
+
let idValues = {};
|
|
380
|
+
let nodes = [];
|
|
381
|
+
let fields = editor.fields();
|
|
382
|
+
for (let i = 0, ien = cells.length; i < ien; i++) {
|
|
383
|
+
for (let j = 0, jen = cells[i].length; j < jen; j++) {
|
|
384
|
+
let cell = cells[i][j];
|
|
385
|
+
// Determine the field name for the cell being edited
|
|
386
|
+
let col = dt.settings()[0].columns[cell.index.column];
|
|
387
|
+
let fieldName = col.editField;
|
|
388
|
+
if (fieldName === undefined) {
|
|
389
|
+
let dataSrc = col.data;
|
|
390
|
+
// dataSrc is the `field.data` property, but we need to set
|
|
391
|
+
// using the field name, so we need to translate from the
|
|
392
|
+
// data to the name
|
|
393
|
+
for (let k = 0, ken = fields.length; k < ken; k++) {
|
|
394
|
+
let field = editor.field(fields[k]);
|
|
395
|
+
if (field.dataSrc() === dataSrc) {
|
|
396
|
+
fieldName = field.name();
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (!fieldName) {
|
|
402
|
+
throw ('Could not automatically determine field data. ' +
|
|
403
|
+
'Please see https://datatables.net/tn/11');
|
|
404
|
+
}
|
|
405
|
+
if (typeof fieldName !== 'string') {
|
|
406
|
+
throw 'AutoFill with multiple edit fields is not supported';
|
|
407
|
+
}
|
|
408
|
+
if (!idValues[fieldName]) {
|
|
409
|
+
idValues[fieldName] = {};
|
|
410
|
+
}
|
|
411
|
+
let id = dt.row(cell.index.row).id();
|
|
412
|
+
idValues[fieldName][id] = cell.set;
|
|
413
|
+
// Keep a list of cells so we can activate the bubble editing
|
|
414
|
+
// with them
|
|
415
|
+
nodes.push(cell.index);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// Perform the edit using bubble editing as it allows us to specify
|
|
419
|
+
// the cells to be edited, rather than using full rows
|
|
420
|
+
editor
|
|
421
|
+
.bubble(nodes, false)
|
|
422
|
+
.multiSet(idValues)
|
|
423
|
+
.submit(null, function () {
|
|
424
|
+
// If an error happens, Editor will show an alert, and then we
|
|
425
|
+
// need to finish the edit since we can't do anything else.
|
|
426
|
+
editor.close();
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Emit an event on the DataTable for listeners
|
|
431
|
+
*
|
|
432
|
+
* @param name Event name
|
|
433
|
+
* @param args Event arguments
|
|
434
|
+
*/
|
|
435
|
+
_emitEvent(name, args) {
|
|
436
|
+
this.s.dt.trigger(name, args);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Attach suitable listeners (based on the configuration) that will attach
|
|
440
|
+
* and detach the AutoFill handle in the document.
|
|
441
|
+
*/
|
|
442
|
+
_focusListener() {
|
|
443
|
+
var that = this;
|
|
444
|
+
var dt = this.s.dt;
|
|
445
|
+
var namespace = this.s.namespace;
|
|
446
|
+
var focus = this.c.focus !== null
|
|
447
|
+
? this.c.focus
|
|
448
|
+
: dt.init().keys || dt.settings()[0].keytable
|
|
449
|
+
? 'focus'
|
|
450
|
+
: 'hover';
|
|
451
|
+
// All event listeners attached here are removed in the `destroy`
|
|
452
|
+
// callback in the constructor
|
|
453
|
+
if (focus === 'focus') {
|
|
454
|
+
dt.on('key-focus.autoFill', function (e, dt, cell) {
|
|
455
|
+
that._attach(cell.node());
|
|
456
|
+
}).on('key-blur.autoFill', function (e, dt, cell) {
|
|
457
|
+
that._detach();
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
else if (focus === 'click') {
|
|
461
|
+
Dom.s(dt.table().body()).on('click' + namespace, 'td, th', function (e) {
|
|
462
|
+
that._attach(this);
|
|
463
|
+
});
|
|
464
|
+
Dom.s(document.body).on('click' + namespace, function (e) {
|
|
465
|
+
if (!Dom.s(e.target).closest(dt.table().body()).count()) {
|
|
466
|
+
that._detach();
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
Dom.s(dt.table().body())
|
|
472
|
+
.on('mouseenter' + namespace + ' touchstart' + namespace, 'td, th', function (e) {
|
|
473
|
+
that._attach(this);
|
|
474
|
+
})
|
|
475
|
+
.on('mouseleave' + namespace + 'touchend' + namespace, function (e) {
|
|
476
|
+
if (Dom.s(e.relatedTarget).classHas('dtaf-handle')) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
that._detach();
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
_focusListenerRemove() {
|
|
484
|
+
var dt = this.s.dt;
|
|
485
|
+
dt.off('.autoFill');
|
|
486
|
+
Dom.s(dt.table().body()).off(this.s.namespace);
|
|
487
|
+
Dom.s(document.body).off(this.s.namespace);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Get the position of a node, relative to another, including any scrolling
|
|
491
|
+
* offsets.
|
|
492
|
+
*
|
|
493
|
+
* @param node Node to get the position of
|
|
494
|
+
* @param targetHost Node to use as the parent
|
|
495
|
+
* @return Offset calculation
|
|
496
|
+
*/
|
|
497
|
+
_getPosition(node, targetHost) {
|
|
498
|
+
let targetParent = Dom.s(this.s.dt.table().node().offsetParent), currNode = node, currOffsetParent, top = 0, left = 0;
|
|
499
|
+
if (targetHost) {
|
|
500
|
+
targetParent = Dom.s(targetHost);
|
|
501
|
+
}
|
|
502
|
+
do {
|
|
503
|
+
let positionTop = currNode.offsetTop;
|
|
504
|
+
let positionLeft = currNode.offsetLeft;
|
|
505
|
+
currOffsetParent = Dom.s(currNode.offsetParent);
|
|
506
|
+
top +=
|
|
507
|
+
positionTop +
|
|
508
|
+
parseInt(currOffsetParent.css('border-top-width') || '0') * 1;
|
|
509
|
+
left +=
|
|
510
|
+
positionLeft +
|
|
511
|
+
parseInt(currOffsetParent.css('border-left-width') || '0') * 1;
|
|
512
|
+
// Emergency fall back. Shouldn't happen, but just in case!
|
|
513
|
+
if (currNode.nodeName.toLowerCase() === 'body') {
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
currNode = currOffsetParent.get(0); // for next loop
|
|
517
|
+
} while (currOffsetParent.get(0) !== targetParent.get(0));
|
|
518
|
+
return {
|
|
519
|
+
top: top,
|
|
520
|
+
left: left
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Start mouse drag - selects the start cell
|
|
525
|
+
*
|
|
526
|
+
* @param e Mouse down event
|
|
527
|
+
*/
|
|
528
|
+
_mousedown(e) {
|
|
529
|
+
let that = this;
|
|
530
|
+
let dt = this.s.dt;
|
|
531
|
+
this.dom.start = this.dom.attachedTo;
|
|
532
|
+
this.s.start = {
|
|
533
|
+
row: dt
|
|
534
|
+
.rows({ page: 'current' })
|
|
535
|
+
.nodes()
|
|
536
|
+
.indexOf(Dom.s(this.dom.start).parent().get(0)),
|
|
537
|
+
column: Dom.s(this.dom.start).index()
|
|
538
|
+
};
|
|
539
|
+
Dom.s(document.body)
|
|
540
|
+
.on('mousemove.autoFill touchmove.autoFill', function (e) {
|
|
541
|
+
that._mousemove(e);
|
|
542
|
+
// If it is a touch event then when the touch ends we need to
|
|
543
|
+
// remove the handle
|
|
544
|
+
if (e.type === 'touchmove') {
|
|
545
|
+
Dom.s(document.body).one('touchend.autoFill', function () {
|
|
546
|
+
that._detach();
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
})
|
|
550
|
+
.on('mouseup.autoFill touchend.autoFill', function (e) {
|
|
551
|
+
that._mouseup(e);
|
|
552
|
+
});
|
|
553
|
+
let select = this.dom.select;
|
|
554
|
+
let offsetParent = dt.table().node().offsetParent;
|
|
555
|
+
select.top.appendTo(offsetParent);
|
|
556
|
+
select.left.appendTo(offsetParent);
|
|
557
|
+
select.right.appendTo(offsetParent);
|
|
558
|
+
select.bottom.appendTo(offsetParent);
|
|
559
|
+
this._drawSelection(this.dom.start, e);
|
|
560
|
+
this.dom.handle.css('display', 'none');
|
|
561
|
+
// Cache scrolling information so mouse move doesn't need to read.
|
|
562
|
+
// This assumes that the window and DT scroller will not change size
|
|
563
|
+
// during an AutoFill drag, which I think is a fair assumption
|
|
564
|
+
let scrollWrapper = this.dom.dtScroll;
|
|
565
|
+
this.s.scroll = {
|
|
566
|
+
windowHeight: window.innerHeight,
|
|
567
|
+
windowWidth: window.innerWidth,
|
|
568
|
+
dtTop: scrollWrapper ? scrollWrapper.offset().top : 0,
|
|
569
|
+
dtLeft: scrollWrapper ? scrollWrapper.offset().left : 0,
|
|
570
|
+
dtHeight: scrollWrapper ? scrollWrapper.height('outer') : 0,
|
|
571
|
+
dtWidth: scrollWrapper ? scrollWrapper.width('outer') : 0
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Mouse drag - selects the end cell and update the selection display for
|
|
576
|
+
* the end user
|
|
577
|
+
*
|
|
578
|
+
* @param e Mouse move event
|
|
579
|
+
*/
|
|
580
|
+
_mousemove(e) {
|
|
581
|
+
let target = e.touches && e.touches.length
|
|
582
|
+
? document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY)
|
|
583
|
+
: e.target;
|
|
584
|
+
let name = target.nodeName.toLowerCase();
|
|
585
|
+
if (name !== 'td' && name !== 'th') {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
this._drawSelection(target, e);
|
|
589
|
+
this._shiftScroll(e);
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* End mouse drag - perform the update actions
|
|
593
|
+
*
|
|
594
|
+
* @param e Mouse up event
|
|
595
|
+
* @private
|
|
596
|
+
*/
|
|
597
|
+
_mouseup(e) {
|
|
598
|
+
Dom.s(document.body).off('.autoFill');
|
|
599
|
+
let that = this;
|
|
600
|
+
let dt = this.s.dt;
|
|
601
|
+
let select = this.dom.select;
|
|
602
|
+
select.top.remove();
|
|
603
|
+
select.left.remove();
|
|
604
|
+
select.right.remove();
|
|
605
|
+
select.bottom.remove();
|
|
606
|
+
this.dom.handle.css('display', 'block');
|
|
607
|
+
// Display complete - now do something useful with the selection!
|
|
608
|
+
let start = this.s.start;
|
|
609
|
+
let end = this.s.end;
|
|
610
|
+
// Haven't selected multiple cells, so nothing to do
|
|
611
|
+
if (start.row === end.row && start.column === end.column) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
let startDt = dt.cell(':eq(' + start.row + ')', start.column + ':visible', { page: 'current' });
|
|
615
|
+
// If Editor is active inside this cell (inline editing) we need to wait
|
|
616
|
+
// for Editor to submit and then we can loop back and trigger the fill.
|
|
617
|
+
if (Dom.s(startDt.node()).find('div.DTE').count()) {
|
|
618
|
+
let editor = dt.editor();
|
|
619
|
+
editor
|
|
620
|
+
.on('submitSuccess.dtaf close.dtaf', function () {
|
|
621
|
+
editor.off('.dtaf');
|
|
622
|
+
setTimeout(function () {
|
|
623
|
+
that._mouseup(e);
|
|
624
|
+
}, 100);
|
|
625
|
+
})
|
|
626
|
+
.on('submitComplete.dtaf preSubmitCancelled.dtaf close.dtaf', function () {
|
|
627
|
+
editor.off('.dtaf');
|
|
628
|
+
});
|
|
629
|
+
// Make the current input submit
|
|
630
|
+
editor.submit();
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
// Build a matrix representation of the selected rows
|
|
634
|
+
let rows = this._range(start.row, end.row);
|
|
635
|
+
let columns = this._range(start.column, end.column);
|
|
636
|
+
let selected = [];
|
|
637
|
+
let dtSettings = dt.settings()[0];
|
|
638
|
+
let dtColumns = dtSettings.columns;
|
|
639
|
+
let enabledColumns = dt.columns(this.c.columns).indexes();
|
|
640
|
+
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
641
|
+
selected.push(columns
|
|
642
|
+
.map(function (column) {
|
|
643
|
+
let row = dt.row(':eq(' + rows[rowIdx] + ')', {
|
|
644
|
+
page: 'current'
|
|
645
|
+
});
|
|
646
|
+
let cell = dt.cell(row.index(), column + ':visible');
|
|
647
|
+
let data = cell.data();
|
|
648
|
+
let cellIndex = cell.index();
|
|
649
|
+
let editField = dtColumns[cellIndex.column].editField;
|
|
650
|
+
if (editField !== undefined) {
|
|
651
|
+
data = DataTable.util.get(editField)(dt.row(cellIndex.row).data());
|
|
652
|
+
}
|
|
653
|
+
if (enabledColumns.indexOf(cellIndex.column) === -1) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
return {
|
|
657
|
+
cell: cell,
|
|
658
|
+
data: data,
|
|
659
|
+
label: cell.data(),
|
|
660
|
+
index: cellIndex
|
|
661
|
+
};
|
|
662
|
+
})
|
|
663
|
+
.filter(d => !!d));
|
|
664
|
+
}
|
|
665
|
+
this._actionSelector(selected);
|
|
666
|
+
// Stop shiftScroll
|
|
667
|
+
if (this.s.scrollInterval) {
|
|
668
|
+
clearInterval(this.s.scrollInterval);
|
|
669
|
+
this.s.scrollInterval = null;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Create an array with a range of numbers defined by the start and end
|
|
674
|
+
* parameters passed in (inclusive!).
|
|
675
|
+
*
|
|
676
|
+
* @param start Start
|
|
677
|
+
* @param end End
|
|
678
|
+
*/
|
|
679
|
+
_range(start, end) {
|
|
680
|
+
var out = [];
|
|
681
|
+
var i;
|
|
682
|
+
if (start <= end) {
|
|
683
|
+
for (i = start; i <= end; i++) {
|
|
684
|
+
out.push(i);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
for (i = start; i >= end; i--) {
|
|
689
|
+
out.push(i);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return out;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Move the window and DataTables scrolling during a drag to scroll new
|
|
696
|
+
* content into view. This is done by proximity to the edge of the scrolling
|
|
697
|
+
* container of the mouse - for example near the top edge of the window
|
|
698
|
+
* should scroll up. This is a little complicated as there are two elements
|
|
699
|
+
* that can be scrolled - the window and the DataTables scrolling view port
|
|
700
|
+
* (if scrollX and / or scrollY is enabled).
|
|
701
|
+
*
|
|
702
|
+
* @param e Mouse move event object
|
|
703
|
+
*/
|
|
704
|
+
_shiftScroll(e) {
|
|
705
|
+
var that = this;
|
|
706
|
+
var scroll = this.s.scroll;
|
|
707
|
+
var runInterval = false;
|
|
708
|
+
var scrollSpeed = 5;
|
|
709
|
+
var buffer = 65;
|
|
710
|
+
// Different values if using a touchscreen
|
|
711
|
+
var pageX = !e.type.includes('touch')
|
|
712
|
+
? e.pageX - window.scrollX
|
|
713
|
+
: e.touches[0].clientX;
|
|
714
|
+
var pageY = !e.type.includes('touch')
|
|
715
|
+
? e.pageY - window.scrollY
|
|
716
|
+
: e.touches[0].clientY;
|
|
717
|
+
var windowY = pageY, windowX = pageX, windowVert, windowHoriz, dtVert, dtHoriz;
|
|
718
|
+
// Window calculations - based on the mouse position in the window,
|
|
719
|
+
// regardless of scrolling
|
|
720
|
+
if (windowY < buffer) {
|
|
721
|
+
windowVert = scrollSpeed * -1;
|
|
722
|
+
}
|
|
723
|
+
else if (windowY > scroll.windowHeight - buffer) {
|
|
724
|
+
windowVert = scrollSpeed;
|
|
725
|
+
}
|
|
726
|
+
if (windowX < buffer) {
|
|
727
|
+
windowHoriz = scrollSpeed * -1;
|
|
728
|
+
}
|
|
729
|
+
else if (windowX > scroll.windowWidth - buffer) {
|
|
730
|
+
windowHoriz = scrollSpeed;
|
|
731
|
+
}
|
|
732
|
+
// DataTables scrolling calculations - based on the table's position in
|
|
733
|
+
// the document and the mouse position on the page
|
|
734
|
+
if (scroll.dtTop !== null && pageY < scroll.dtTop + buffer) {
|
|
735
|
+
dtVert = scrollSpeed * -1;
|
|
736
|
+
}
|
|
737
|
+
else if (scroll.dtTop !== null &&
|
|
738
|
+
pageY > scroll.dtTop + scroll.dtHeight - buffer) {
|
|
739
|
+
dtVert = scrollSpeed;
|
|
740
|
+
}
|
|
741
|
+
if (scroll.dtLeft !== null && pageX < scroll.dtLeft + buffer) {
|
|
742
|
+
dtHoriz = scrollSpeed * -1;
|
|
743
|
+
}
|
|
744
|
+
else if (scroll.dtLeft !== null &&
|
|
745
|
+
pageX > scroll.dtLeft + scroll.dtWidth - buffer) {
|
|
746
|
+
dtHoriz = scrollSpeed;
|
|
747
|
+
}
|
|
748
|
+
// This is where it gets interesting. We want to continue scrolling
|
|
749
|
+
// without requiring a mouse move, so we need an interval to be
|
|
750
|
+
// triggered. The interval should continue until it is no longer needed,
|
|
751
|
+
// but it must also use the latest scroll commands (for example consider
|
|
752
|
+
// that the mouse might move from scrolling up to scrolling left, all
|
|
753
|
+
// with the same interval running. We use the `scroll` object to "pass"
|
|
754
|
+
// this information to the interval. Can't use local variables as they
|
|
755
|
+
// wouldn't be the ones that are used by an already existing interval!
|
|
756
|
+
if (windowVert || windowHoriz || dtVert || dtHoriz) {
|
|
757
|
+
scroll.windowVert = windowVert;
|
|
758
|
+
scroll.windowHoriz = windowHoriz;
|
|
759
|
+
scroll.dtVert = dtVert;
|
|
760
|
+
scroll.dtHoriz = dtHoriz;
|
|
761
|
+
runInterval = true;
|
|
762
|
+
}
|
|
763
|
+
else if (this.s.scrollInterval) {
|
|
764
|
+
// Don't need to scroll - remove any existing timer
|
|
765
|
+
clearInterval(this.s.scrollInterval);
|
|
766
|
+
this.s.scrollInterval = null;
|
|
767
|
+
}
|
|
768
|
+
// If we need to run the interval to scroll and there is no existing
|
|
769
|
+
// interval (if there is an existing one, it will continue to run)
|
|
770
|
+
if (!this.s.scrollInterval && runInterval) {
|
|
771
|
+
this.s.scrollInterval = setInterval(function () {
|
|
772
|
+
var _a;
|
|
773
|
+
// Don't need to worry about setting scroll <0 or beyond the
|
|
774
|
+
// scroll bound as the browser will just reject that.
|
|
775
|
+
window.scrollTo(window.scrollX +
|
|
776
|
+
(scroll.windowHoriz ? scroll.windowHoriz : 0), window.scrollY + (scroll.windowVert ? scroll.windowVert : 0));
|
|
777
|
+
// DataTables scrolling
|
|
778
|
+
if (scroll.dtVert || scroll.dtHoriz) {
|
|
779
|
+
var scroller = (_a = that.dom.dtScroll) === null || _a === void 0 ? void 0 : _a.get(0);
|
|
780
|
+
if (scroller && scroll.dtVert) {
|
|
781
|
+
scroller.scrollTop += scroll.dtVert;
|
|
782
|
+
}
|
|
783
|
+
if (scroller && scroll.dtHoriz) {
|
|
784
|
+
scroller.scrollLeft += scroll.dtHoriz;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}, 20);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Update the DataTable after the user has selected what they want to do
|
|
792
|
+
*
|
|
793
|
+
* @param result Return from the `execute` method - can be false internally
|
|
794
|
+
* to do nothing. This is not documented for plug-ins and is used only by
|
|
795
|
+
* the cancel option.
|
|
796
|
+
* @param cells Information about the selected cells from the key up
|
|
797
|
+
* function, augmented with the set values
|
|
798
|
+
*/
|
|
799
|
+
_update(result, cells) {
|
|
800
|
+
// Do nothing on `false` return from an execute function
|
|
801
|
+
if (result === false) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
let dt = this.s.dt;
|
|
805
|
+
let cell;
|
|
806
|
+
let columns = dt.columns(this.c.columns).indexes();
|
|
807
|
+
// Potentially allow modifications to the cells matrix
|
|
808
|
+
this._emitEvent('preAutoFill', [dt, cells]);
|
|
809
|
+
this._editor(cells);
|
|
810
|
+
// Automatic updates are not performed if `update` is null and the
|
|
811
|
+
// `editor` parameter is passed in - the reason being that Editor will
|
|
812
|
+
// update the data once submitted
|
|
813
|
+
let update = this.c.update !== null
|
|
814
|
+
? this.c.update
|
|
815
|
+
: this.c.editor
|
|
816
|
+
? false
|
|
817
|
+
: true;
|
|
818
|
+
if (update) {
|
|
819
|
+
for (let i = 0, ien = cells.length; i < ien; i++) {
|
|
820
|
+
for (let j = 0, jen = cells[i].length; j < jen; j++) {
|
|
821
|
+
cell = cells[i][j];
|
|
822
|
+
if (columns.indexOf(cell.index.column) !== -1) {
|
|
823
|
+
cell.cell.data(cell.set);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
dt.draw(false);
|
|
828
|
+
}
|
|
829
|
+
this._emitEvent('autoFill', [dt, cells]);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
833
|
+
* Statics
|
|
78
834
|
*/
|
|
79
|
-
var AutoFill = function( dt, opts )
|
|
80
|
-
{
|
|
81
|
-
if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.11' ) ) {
|
|
82
|
-
throw( "Warning: AutoFill requires DataTables 1.11 or greater");
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// User and defaults configuration object
|
|
86
|
-
this.c = $.extend( true, {},
|
|
87
|
-
DataTable.defaults.autoFill,
|
|
88
|
-
AutoFill.defaults,
|
|
89
|
-
opts
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* @namespace Settings object which contains customisable information for AutoFill instance
|
|
94
|
-
*/
|
|
95
|
-
this.s = {
|
|
96
|
-
/** @type {DataTable.Api} DataTables' API instance */
|
|
97
|
-
dt: new DataTable.Api( dt ),
|
|
98
|
-
|
|
99
|
-
/** @type {String} Unique namespace for events attached to the document */
|
|
100
|
-
namespace: '.autoFill'+(_instance++),
|
|
101
|
-
|
|
102
|
-
/** @type {Object} Cached dimension information for use in the mouse move event handler */
|
|
103
|
-
scroll: {},
|
|
104
|
-
|
|
105
|
-
/** @type {integer} Interval object used for smooth scrolling */
|
|
106
|
-
scrollInterval: null,
|
|
107
|
-
|
|
108
|
-
handle: {
|
|
109
|
-
height: 0,
|
|
110
|
-
width: 0
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Enabled setting
|
|
115
|
-
* @type {Boolean}
|
|
116
|
-
*/
|
|
117
|
-
enabled: false
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* @namespace Common and useful DOM elements for the class instance
|
|
123
|
-
*/
|
|
124
|
-
this.dom = {
|
|
125
|
-
closeButton: $('<div class="dtaf-popover-close">×</div>'),
|
|
126
|
-
|
|
127
|
-
/** @type {jQuery} AutoFill handle */
|
|
128
|
-
handle: $('<div class="dt-autofill-handle"/>'),
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* @type {Object} Selected cells outline - Need to use 4 elements,
|
|
132
|
-
* otherwise the mouse over if you back into the selected rectangle
|
|
133
|
-
* will be over that element, rather than the cells!
|
|
134
|
-
*/
|
|
135
|
-
select: {
|
|
136
|
-
top: $('<div class="dt-autofill-select top"/>'),
|
|
137
|
-
right: $('<div class="dt-autofill-select right"/>'),
|
|
138
|
-
bottom: $('<div class="dt-autofill-select bottom"/>'),
|
|
139
|
-
left: $('<div class="dt-autofill-select left"/>')
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
/** @type {jQuery} Fill type chooser background */
|
|
143
|
-
background: $('<div class="dt-autofill-background"/>'),
|
|
144
|
-
|
|
145
|
-
/** @type {jQuery} Fill type chooser */
|
|
146
|
-
list: $('<div class="dt-autofill-list">'+this.s.dt.i18n('autoFill.info', '')+'</div>')
|
|
147
|
-
.attr('aria-modal', true)
|
|
148
|
-
.attr('role', 'dialog')
|
|
149
|
-
.append('<div class="dt-autofill-list-items"></div>'),
|
|
150
|
-
|
|
151
|
-
/** @type {jQuery} DataTables scrolling container */
|
|
152
|
-
dtScroll: null,
|
|
153
|
-
|
|
154
|
-
/** @type {jQuery} Offset parent element */
|
|
155
|
-
offsetParent: null
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
/* Constructor logic */
|
|
160
|
-
this._constructor();
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
$.extend( AutoFill.prototype, {
|
|
166
|
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
167
|
-
* Public methods (exposed via the DataTables API below)
|
|
168
|
-
*/
|
|
169
|
-
enabled: function ()
|
|
170
|
-
{
|
|
171
|
-
return this.s.enabled;
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
enable: function ( flag )
|
|
176
|
-
{
|
|
177
|
-
var that = this;
|
|
178
|
-
|
|
179
|
-
if ( flag === false ) {
|
|
180
|
-
return this.disable();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
this.s.enabled = true;
|
|
184
|
-
|
|
185
|
-
this._focusListener();
|
|
186
|
-
|
|
187
|
-
this.dom.handle.on( 'mousedown touchstart', function (e) {
|
|
188
|
-
that._mousedown( e );
|
|
189
|
-
return false;
|
|
190
|
-
} );
|
|
191
|
-
|
|
192
|
-
$(window).on('resize', function() {
|
|
193
|
-
var handle = $('div.dt-autofill-handle');
|
|
194
|
-
if(handle.length > 0 && that.dom.attachedTo !== undefined) {
|
|
195
|
-
that._attach(that.dom.attachedTo)
|
|
196
|
-
}
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
let orientationReset = function() {
|
|
200
|
-
that.s.handle = {
|
|
201
|
-
height: false,
|
|
202
|
-
width: false
|
|
203
|
-
};
|
|
204
|
-
$(that.dom.handle).css({
|
|
205
|
-
'height': '',
|
|
206
|
-
'width': ''
|
|
207
|
-
})
|
|
208
|
-
if(that.dom.attachedTo !== undefined) {
|
|
209
|
-
that._attach(that.dom.attachedTo)
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
$(window)
|
|
214
|
-
.on('orientationchange', function() {
|
|
215
|
-
setTimeout(function() {
|
|
216
|
-
orientationReset();
|
|
217
|
-
setTimeout(orientationReset, 150);
|
|
218
|
-
}, 50);
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
return this;
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
disable: function ()
|
|
225
|
-
{
|
|
226
|
-
this.s.enabled = false;
|
|
227
|
-
|
|
228
|
-
this._focusListenerRemove();
|
|
229
|
-
|
|
230
|
-
return this;
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
235
|
-
* Constructor
|
|
236
|
-
*/
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Initialise the RowReorder instance
|
|
240
|
-
*
|
|
241
|
-
* @private
|
|
242
|
-
*/
|
|
243
|
-
_constructor: function ()
|
|
244
|
-
{
|
|
245
|
-
var that = this;
|
|
246
|
-
var dt = this.s.dt;
|
|
247
|
-
|
|
248
|
-
// Selectors for DataTables 1 and 2 - only one will be matched
|
|
249
|
-
var dtScroll = $('div.dataTables_scrollBody, div.dt-scroll-body', this.s.dt.table().container());
|
|
250
|
-
|
|
251
|
-
// Make the instance accessible to the API
|
|
252
|
-
dt.settings()[0].autoFill = this;
|
|
253
|
-
|
|
254
|
-
if ( dtScroll.length ) {
|
|
255
|
-
this.dom.dtScroll = dtScroll;
|
|
256
|
-
|
|
257
|
-
// Need to scroll container to be the offset parent
|
|
258
|
-
if ( dtScroll.css('position') === 'static' ) {
|
|
259
|
-
dtScroll.css( 'position', 'relative' );
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if ( this.c.enable !== false ) {
|
|
264
|
-
this.enable();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
dt.on( 'destroy.autoFill', function () {
|
|
268
|
-
that._focusListenerRemove();
|
|
269
|
-
} );
|
|
270
|
-
},
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
274
|
-
* Private methods
|
|
275
|
-
*/
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Display the AutoFill drag handle by appending it to a table cell. This
|
|
279
|
-
* is the opposite of the _detach method.
|
|
280
|
-
*
|
|
281
|
-
* @param {node} node TD/TH cell to insert the handle into
|
|
282
|
-
* @private
|
|
283
|
-
*/
|
|
284
|
-
_attach: function ( node )
|
|
285
|
-
{
|
|
286
|
-
var dt = this.s.dt;
|
|
287
|
-
var idx = dt.cell( node ).index();
|
|
288
|
-
var handle = this.dom.handle;
|
|
289
|
-
var handleDim = this.s.handle;
|
|
290
|
-
|
|
291
|
-
if ( ! idx || dt.columns( this.c.columns ).indexes().indexOf( idx.column ) === -1 ) {
|
|
292
|
-
this._detach();
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if ( ! this.dom.offsetParent ) {
|
|
297
|
-
// We attach to the table's offset parent
|
|
298
|
-
this.dom.offsetParent = $( dt.table().node() ).offsetParent();
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if ( ! handleDim.height || ! handleDim.width ) {
|
|
302
|
-
// Append to document so we can get its size. Not expecting it to
|
|
303
|
-
// change during the life time of the page
|
|
304
|
-
handle.appendTo( 'body' );
|
|
305
|
-
handleDim.height = handle.outerHeight();
|
|
306
|
-
handleDim.width = handle.outerWidth();
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Might need to go through multiple offset parents
|
|
310
|
-
var offset = this._getPosition( node, this.dom.offsetParent );
|
|
311
|
-
|
|
312
|
-
this.dom.attachedTo = node;
|
|
313
|
-
handle
|
|
314
|
-
.css( {
|
|
315
|
-
top: offset.top + node.offsetHeight - handleDim.height,
|
|
316
|
-
left: offset.left + node.offsetWidth - handleDim.width
|
|
317
|
-
} )
|
|
318
|
-
.appendTo( this.dom.offsetParent );
|
|
319
|
-
},
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Determine can the fill type should be. This can be automatic, or ask the
|
|
324
|
-
* end user.
|
|
325
|
-
*
|
|
326
|
-
* @param {array} cells Information about the selected cells from the key
|
|
327
|
-
* up function
|
|
328
|
-
* @private
|
|
329
|
-
*/
|
|
330
|
-
_actionSelector: function ( cells )
|
|
331
|
-
{
|
|
332
|
-
var that = this;
|
|
333
|
-
var dt = this.s.dt;
|
|
334
|
-
var actions = AutoFill.actions;
|
|
335
|
-
var available = [];
|
|
336
|
-
|
|
337
|
-
// "Ask" each plug-in if it wants to handle this data
|
|
338
|
-
$.each( actions, function ( key, action ) {
|
|
339
|
-
if ( action.available( dt, cells ) ) {
|
|
340
|
-
available.push( key );
|
|
341
|
-
}
|
|
342
|
-
} );
|
|
343
|
-
|
|
344
|
-
if ( available.length === 1 && this.c.alwaysAsk === false ) {
|
|
345
|
-
// Only one action available - enact it immediately
|
|
346
|
-
var result = actions[ available[0] ].execute( dt, cells );
|
|
347
|
-
this._update( result, cells );
|
|
348
|
-
}
|
|
349
|
-
else if ( available.length > 1 || this.c.alwaysAsk ) {
|
|
350
|
-
// Multiple actions available - ask the end user what they want to do
|
|
351
|
-
var list = this.dom.list.children('div.dt-autofill-list-items').empty();
|
|
352
|
-
|
|
353
|
-
// Add a cancel option
|
|
354
|
-
available.push( 'cancel' );
|
|
355
|
-
|
|
356
|
-
$.each( available, function ( i, name ) {
|
|
357
|
-
list.append( $('<button/>')
|
|
358
|
-
.html(actions[ name ].option( dt, cells ))
|
|
359
|
-
.append( $('<span class="dt-autofill-button"/>').html(dt.i18n('autoFill.button', '>')))
|
|
360
|
-
.on( 'click', function (e) {
|
|
361
|
-
if (e.target.nodeName.toLowerCase() !== 'button') {
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
var result = actions[ name ].execute(
|
|
366
|
-
dt, cells, $(this).closest('button')
|
|
367
|
-
);
|
|
368
|
-
that._update( result, cells );
|
|
369
|
-
|
|
370
|
-
that.dom.background.remove();
|
|
371
|
-
that.dom.list.remove();
|
|
372
|
-
} )
|
|
373
|
-
);
|
|
374
|
-
} );
|
|
375
|
-
|
|
376
|
-
this.dom.background.appendTo( 'body' );
|
|
377
|
-
this.dom.background.one('click', function() {
|
|
378
|
-
that.dom.background.remove();
|
|
379
|
-
that.dom.list.remove();
|
|
380
|
-
})
|
|
381
|
-
this.dom.list.appendTo( 'body' );
|
|
382
|
-
|
|
383
|
-
if (this.c.closeButton) {
|
|
384
|
-
this.dom.list.prepend(this.dom.closeButton).addClass(AutoFill.classes.closeable)
|
|
385
|
-
this.dom.closeButton.on('click', function() {
|
|
386
|
-
return that.dom.background.click()
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
this.dom.list.css( 'margin-top', this.dom.list.outerHeight()/2 * -1 );
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
/**
|
|
396
|
-
* Remove the AutoFill handle from the document
|
|
397
|
-
*
|
|
398
|
-
* @private
|
|
399
|
-
*/
|
|
400
|
-
_detach: function ()
|
|
401
|
-
{
|
|
402
|
-
this.dom.attachedTo = null;
|
|
403
|
-
this.dom.handle.detach();
|
|
404
|
-
},
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Draw the selection outline by calculating the range between the start
|
|
409
|
-
* and end cells, then placing the highlighting elements to draw a rectangle
|
|
410
|
-
*
|
|
411
|
-
* @param {node} target End cell
|
|
412
|
-
* @param {object} e Originating event
|
|
413
|
-
* @private
|
|
414
|
-
*/
|
|
415
|
-
_drawSelection: function ( target, e )
|
|
416
|
-
{
|
|
417
|
-
// Calculate boundary for start cell to this one
|
|
418
|
-
var dt = this.s.dt;
|
|
419
|
-
var start = this.s.start;
|
|
420
|
-
var startCell = $(this.dom.start);
|
|
421
|
-
var end = {
|
|
422
|
-
row: this.c.vertical ?
|
|
423
|
-
dt.rows( { page: 'current' } ).nodes().indexOf( target.parentNode ) :
|
|
424
|
-
start.row,
|
|
425
|
-
column: this.c.horizontal ?
|
|
426
|
-
$(target).index() :
|
|
427
|
-
start.column
|
|
428
|
-
};
|
|
429
|
-
var colIndx = dt.column.index( 'toData', end.column );
|
|
430
|
-
var endRow = dt.row( ':eq('+end.row+')', { page: 'current' } ); // Workaround for M581
|
|
431
|
-
var endCell = $( dt.cell( endRow.index(), colIndx ).node() );
|
|
432
|
-
|
|
433
|
-
// Be sure that is a DataTables controlled cell
|
|
434
|
-
if ( ! dt.cell( endCell ).any() ) {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// if target is not in the columns available - do nothing
|
|
439
|
-
if ( dt.columns( this.c.columns ).indexes().indexOf( colIndx ) === -1 || end.row === -1) {
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
this.s.end = end;
|
|
444
|
-
|
|
445
|
-
var top, bottom, left, right, height, width;
|
|
446
|
-
|
|
447
|
-
top = start.row < end.row ? startCell : endCell;
|
|
448
|
-
bottom = start.row < end.row ? endCell : startCell;
|
|
449
|
-
left = start.column < end.column ? startCell : endCell;
|
|
450
|
-
right = start.column < end.column ? endCell : startCell;
|
|
451
|
-
|
|
452
|
-
top = this._getPosition( top.get(0) ).top;
|
|
453
|
-
left = this._getPosition( left.get(0) ).left;
|
|
454
|
-
height = this._getPosition( bottom.get(0) ).top + bottom.outerHeight() - top;
|
|
455
|
-
width = this._getPosition( right.get(0) ).left + right.outerWidth() - left;
|
|
456
|
-
|
|
457
|
-
var select = this.dom.select;
|
|
458
|
-
select.top.css( {
|
|
459
|
-
top: top,
|
|
460
|
-
left: left,
|
|
461
|
-
width: width
|
|
462
|
-
} );
|
|
463
|
-
|
|
464
|
-
select.left.css( {
|
|
465
|
-
top: top,
|
|
466
|
-
left: left,
|
|
467
|
-
height: height
|
|
468
|
-
} );
|
|
469
|
-
|
|
470
|
-
select.bottom.css( {
|
|
471
|
-
top: top + height,
|
|
472
|
-
left: left,
|
|
473
|
-
width: width
|
|
474
|
-
} );
|
|
475
|
-
|
|
476
|
-
select.right.css( {
|
|
477
|
-
top: top,
|
|
478
|
-
left: left + width,
|
|
479
|
-
height: height
|
|
480
|
-
} );
|
|
481
|
-
},
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* Use the Editor API to perform an update based on the new data for the
|
|
486
|
-
* cells
|
|
487
|
-
*
|
|
488
|
-
* @param {array} cells Information about the selected cells from the key
|
|
489
|
-
* up function
|
|
490
|
-
* @private
|
|
491
|
-
*/
|
|
492
|
-
_editor: function ( cells )
|
|
493
|
-
{
|
|
494
|
-
var dt = this.s.dt;
|
|
495
|
-
var editor = this.c.editor;
|
|
496
|
-
|
|
497
|
-
if ( ! editor ) {
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// Build the object structure for Editor's multi-row editing
|
|
502
|
-
var idValues = {};
|
|
503
|
-
var nodes = [];
|
|
504
|
-
var fields = editor.fields();
|
|
505
|
-
|
|
506
|
-
for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
|
|
507
|
-
for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
|
|
508
|
-
var cell = cells[i][j];
|
|
509
|
-
|
|
510
|
-
// Determine the field name for the cell being edited
|
|
511
|
-
var col = dt.settings()[0].aoColumns[ cell.index.column ];
|
|
512
|
-
var fieldName = col.editField;
|
|
513
|
-
|
|
514
|
-
if ( fieldName === undefined ) {
|
|
515
|
-
var dataSrc = col.mData;
|
|
516
|
-
|
|
517
|
-
// dataSrc is the `field.data` property, but we need to set
|
|
518
|
-
// using the field name, so we need to translate from the
|
|
519
|
-
// data to the name
|
|
520
|
-
for ( var k=0, ken=fields.length ; k<ken ; k++ ) {
|
|
521
|
-
var field = editor.field( fields[k] );
|
|
522
|
-
|
|
523
|
-
if ( field.dataSrc() === dataSrc ) {
|
|
524
|
-
fieldName = field.name();
|
|
525
|
-
break;
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
if ( ! fieldName ) {
|
|
531
|
-
throw 'Could not automatically determine field data. '+
|
|
532
|
-
'Please see https://datatables.net/tn/11';
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
if ( ! idValues[ fieldName ] ) {
|
|
536
|
-
idValues[ fieldName ] = {};
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
var id = dt.row( cell.index.row ).id();
|
|
540
|
-
idValues[ fieldName ][ id ] = cell.set;
|
|
541
|
-
|
|
542
|
-
// Keep a list of cells so we can activate the bubble editing
|
|
543
|
-
// with them
|
|
544
|
-
nodes.push( cell.index );
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// Perform the edit using bubble editing as it allows us to specify
|
|
549
|
-
// the cells to be edited, rather than using full rows
|
|
550
|
-
editor
|
|
551
|
-
.bubble( nodes, false )
|
|
552
|
-
.multiSet( idValues )
|
|
553
|
-
.submit(null, function () {
|
|
554
|
-
// If an error happens, Editor will show an alert, and then we need
|
|
555
|
-
// to finish the edit since we can't do anything else.
|
|
556
|
-
editor.close();
|
|
557
|
-
});
|
|
558
|
-
},
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* Emit an event on the DataTable for listeners
|
|
563
|
-
*
|
|
564
|
-
* @param {string} name Event name
|
|
565
|
-
* @param {array} args Event arguments
|
|
566
|
-
* @private
|
|
567
|
-
*/
|
|
568
|
-
_emitEvent: function ( name, args )
|
|
569
|
-
{
|
|
570
|
-
this.s.dt.iterator( 'table', function ( ctx, i ) {
|
|
571
|
-
$(ctx.nTable).triggerHandler( name+'.dt', args );
|
|
572
|
-
} );
|
|
573
|
-
},
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Attach suitable listeners (based on the configuration) that will attach
|
|
578
|
-
* and detach the AutoFill handle in the document.
|
|
579
|
-
*
|
|
580
|
-
* @private
|
|
581
|
-
*/
|
|
582
|
-
_focusListener: function ()
|
|
583
|
-
{
|
|
584
|
-
var that = this;
|
|
585
|
-
var dt = this.s.dt;
|
|
586
|
-
var namespace = this.s.namespace;
|
|
587
|
-
var focus = this.c.focus !== null ?
|
|
588
|
-
this.c.focus :
|
|
589
|
-
dt.init().keys || dt.settings()[0].keytable ?
|
|
590
|
-
'focus' :
|
|
591
|
-
'hover';
|
|
592
|
-
|
|
593
|
-
// All event listeners attached here are removed in the `destroy`
|
|
594
|
-
// callback in the constructor
|
|
595
|
-
if ( focus === 'focus' ) {
|
|
596
|
-
dt
|
|
597
|
-
.on( 'key-focus.autoFill', function ( e, dt, cell ) {
|
|
598
|
-
that._attach( cell.node() );
|
|
599
|
-
} )
|
|
600
|
-
.on( 'key-blur.autoFill', function ( e, dt, cell ) {
|
|
601
|
-
that._detach();
|
|
602
|
-
} );
|
|
603
|
-
}
|
|
604
|
-
else if ( focus === 'click' ) {
|
|
605
|
-
$(dt.table().body()).on( 'click'+namespace, 'td, th', function (e) {
|
|
606
|
-
that._attach( this );
|
|
607
|
-
} );
|
|
608
|
-
|
|
609
|
-
$(document.body).on( 'click'+namespace, function (e) {
|
|
610
|
-
if ( ! $(e.target).parents().filter( dt.table().body() ).length ) {
|
|
611
|
-
that._detach();
|
|
612
|
-
}
|
|
613
|
-
} );
|
|
614
|
-
}
|
|
615
|
-
else {
|
|
616
|
-
$(dt.table().body())
|
|
617
|
-
.on( 'mouseenter'+namespace+' touchstart'+namespace, 'td, th', function (e) {
|
|
618
|
-
that._attach( this );
|
|
619
|
-
} )
|
|
620
|
-
.on( 'mouseleave'+namespace+'touchend'+namespace, function (e) {
|
|
621
|
-
if ( $(e.relatedTarget).hasClass('dt-autofill-handle') ) {
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
that._detach();
|
|
626
|
-
} );
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
},
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
_focusListenerRemove: function ()
|
|
634
|
-
{
|
|
635
|
-
var dt = this.s.dt;
|
|
636
|
-
|
|
637
|
-
dt.off( '.autoFill' );
|
|
638
|
-
$(dt.table().body()).off( this.s.namespace );
|
|
639
|
-
$(document.body).off( this.s.namespace );
|
|
640
|
-
},
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
/**
|
|
644
|
-
* Get the position of a node, relative to another, including any scrolling
|
|
645
|
-
* offsets.
|
|
646
|
-
* @param {Node} node Node to get the position of
|
|
647
|
-
* @param {jQuery} targetParent Node to use as the parent
|
|
648
|
-
* @return {object} Offset calculation
|
|
649
|
-
* @private
|
|
650
|
-
*/
|
|
651
|
-
_getPosition: function ( node, targetParent )
|
|
652
|
-
{
|
|
653
|
-
var
|
|
654
|
-
currNode = node,
|
|
655
|
-
currOffsetParent,
|
|
656
|
-
top = 0,
|
|
657
|
-
left = 0;
|
|
658
|
-
|
|
659
|
-
if ( ! targetParent ) {
|
|
660
|
-
targetParent = $( $( this.s.dt.table().node() )[0].offsetParent );
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
do {
|
|
664
|
-
// Don't use jQuery().position() the behaviour changes between 1.x and 3.x for
|
|
665
|
-
// tables
|
|
666
|
-
var positionTop = currNode.offsetTop;
|
|
667
|
-
var positionLeft = currNode.offsetLeft;
|
|
668
|
-
|
|
669
|
-
// jQuery doesn't give a `table` as the offset parent oddly, so use DOM directly
|
|
670
|
-
currOffsetParent = $( currNode.offsetParent );
|
|
671
|
-
|
|
672
|
-
top += positionTop + parseInt( currOffsetParent.css('border-top-width') || 0 ) * 1;
|
|
673
|
-
left += positionLeft + parseInt( currOffsetParent.css('border-left-width') || 0 ) * 1;
|
|
674
|
-
|
|
675
|
-
// Emergency fall back. Shouldn't happen, but just in case!
|
|
676
|
-
if ( currNode.nodeName.toLowerCase() === 'body' ) {
|
|
677
|
-
break;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
currNode = currOffsetParent.get(0); // for next loop
|
|
681
|
-
}
|
|
682
|
-
while ( currOffsetParent.get(0) !== targetParent.get(0) )
|
|
683
|
-
|
|
684
|
-
return {
|
|
685
|
-
top: top,
|
|
686
|
-
left: left
|
|
687
|
-
};
|
|
688
|
-
},
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* Start mouse drag - selects the start cell
|
|
693
|
-
*
|
|
694
|
-
* @param {object} e Mouse down event
|
|
695
|
-
* @private
|
|
696
|
-
*/
|
|
697
|
-
_mousedown: function ( e )
|
|
698
|
-
{
|
|
699
|
-
var that = this;
|
|
700
|
-
var dt = this.s.dt;
|
|
701
|
-
|
|
702
|
-
this.dom.start = this.dom.attachedTo;
|
|
703
|
-
this.s.start = {
|
|
704
|
-
row: dt.rows( { page: 'current' } ).nodes().indexOf( $(this.dom.start).parent()[0] ),
|
|
705
|
-
column: $(this.dom.start).index()
|
|
706
|
-
};
|
|
707
|
-
|
|
708
|
-
$(document.body)
|
|
709
|
-
.on( 'mousemove.autoFill touchmove.autoFill', function (e) {
|
|
710
|
-
that._mousemove( e );
|
|
711
|
-
// If it is a touch event then when the touch ends we need to remove the handle
|
|
712
|
-
if(e.type === 'touchmove') {
|
|
713
|
-
$(document.body).one('touchend.autoFill', function() {
|
|
714
|
-
that._detach();
|
|
715
|
-
})
|
|
716
|
-
}
|
|
717
|
-
} )
|
|
718
|
-
.on( 'mouseup.autoFill touchend.autoFill', function (e) {
|
|
719
|
-
that._mouseup( e );
|
|
720
|
-
} );
|
|
721
|
-
|
|
722
|
-
var select = this.dom.select;
|
|
723
|
-
var offsetParent = $( dt.table().node() ).offsetParent();
|
|
724
|
-
select.top.appendTo( offsetParent );
|
|
725
|
-
select.left.appendTo( offsetParent );
|
|
726
|
-
select.right.appendTo( offsetParent );
|
|
727
|
-
select.bottom.appendTo( offsetParent );
|
|
728
|
-
|
|
729
|
-
this._drawSelection( this.dom.start, e );
|
|
730
|
-
|
|
731
|
-
this.dom.handle.css( 'display', 'none' );
|
|
732
|
-
|
|
733
|
-
// Cache scrolling information so mouse move doesn't need to read.
|
|
734
|
-
// This assumes that the window and DT scroller will not change size
|
|
735
|
-
// during an AutoFill drag, which I think is a fair assumption
|
|
736
|
-
var scrollWrapper = this.dom.dtScroll;
|
|
737
|
-
this.s.scroll = {
|
|
738
|
-
windowHeight: $(window).height(),
|
|
739
|
-
windowWidth: $(window).width(),
|
|
740
|
-
dtTop: scrollWrapper ? scrollWrapper.offset().top : null,
|
|
741
|
-
dtLeft: scrollWrapper ? scrollWrapper.offset().left : null,
|
|
742
|
-
dtHeight: scrollWrapper ? scrollWrapper.outerHeight() : null,
|
|
743
|
-
dtWidth: scrollWrapper ? scrollWrapper.outerWidth() : null
|
|
744
|
-
};
|
|
745
|
-
},
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
/**
|
|
749
|
-
* Mouse drag - selects the end cell and update the selection display for
|
|
750
|
-
* the end user
|
|
751
|
-
*
|
|
752
|
-
* @param {object} e Mouse move event
|
|
753
|
-
* @private
|
|
754
|
-
*/
|
|
755
|
-
_mousemove: function ( e )
|
|
756
|
-
{
|
|
757
|
-
var target = e.touches && e.touches.length
|
|
758
|
-
? document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY)
|
|
759
|
-
: e.target;
|
|
760
|
-
var name = target.nodeName.toLowerCase();
|
|
761
|
-
|
|
762
|
-
if ( name !== 'td' && name !== 'th' ) {
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
this._drawSelection( target, e );
|
|
767
|
-
this._shiftScroll( e );
|
|
768
|
-
},
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
/**
|
|
772
|
-
* End mouse drag - perform the update actions
|
|
773
|
-
*
|
|
774
|
-
* @param {object} e Mouse up event
|
|
775
|
-
* @private
|
|
776
|
-
*/
|
|
777
|
-
_mouseup: function ( e )
|
|
778
|
-
{
|
|
779
|
-
$(document.body).off( '.autoFill' );
|
|
780
|
-
|
|
781
|
-
var that = this;
|
|
782
|
-
var dt = this.s.dt;
|
|
783
|
-
var select = this.dom.select;
|
|
784
|
-
select.top.remove();
|
|
785
|
-
select.left.remove();
|
|
786
|
-
select.right.remove();
|
|
787
|
-
select.bottom.remove();
|
|
788
|
-
|
|
789
|
-
this.dom.handle.css( 'display', 'block' );
|
|
790
|
-
|
|
791
|
-
// Display complete - now do something useful with the selection!
|
|
792
|
-
var start = this.s.start;
|
|
793
|
-
var end = this.s.end;
|
|
794
|
-
|
|
795
|
-
// Haven't selected multiple cells, so nothing to do
|
|
796
|
-
if ( start.row === end.row && start.column === end.column ) {
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
var startDt = dt.cell( ':eq('+start.row+')', start.column+':visible', {page:'current'} );
|
|
801
|
-
|
|
802
|
-
// If Editor is active inside this cell (inline editing) we need to wait for Editor to
|
|
803
|
-
// submit and then we can loop back and trigger the fill.
|
|
804
|
-
if ( $('div.DTE', startDt.node()).length ) {
|
|
805
|
-
var editor = dt.editor();
|
|
806
|
-
|
|
807
|
-
editor
|
|
808
|
-
.on( 'submitSuccess.dtaf close.dtaf', function () {
|
|
809
|
-
editor.off( '.dtaf');
|
|
810
|
-
|
|
811
|
-
setTimeout( function () {
|
|
812
|
-
that._mouseup( e );
|
|
813
|
-
}, 100 );
|
|
814
|
-
} )
|
|
815
|
-
.on( 'submitComplete.dtaf preSubmitCancelled.dtaf close.dtaf', function () {
|
|
816
|
-
editor.off( '.dtaf');
|
|
817
|
-
} );
|
|
818
|
-
|
|
819
|
-
// Make the current input submit
|
|
820
|
-
editor.submit();
|
|
821
|
-
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// Build a matrix representation of the selected rows
|
|
826
|
-
var rows = this._range( start.row, end.row );
|
|
827
|
-
var columns = this._range( start.column, end.column );
|
|
828
|
-
var selected = [];
|
|
829
|
-
var dtSettings = dt.settings()[0];
|
|
830
|
-
var dtColumns = dtSettings.aoColumns;
|
|
831
|
-
var enabledColumns = dt.columns( this.c.columns ).indexes();
|
|
832
|
-
|
|
833
|
-
// Can't use Array.prototype.map as IE8 doesn't support it
|
|
834
|
-
// Can't use $.map as jQuery flattens 2D arrays
|
|
835
|
-
// Need to use a good old fashioned for loop
|
|
836
|
-
for ( var rowIdx=0 ; rowIdx<rows.length ; rowIdx++ ) {
|
|
837
|
-
selected.push(
|
|
838
|
-
$.map( columns, function (column) {
|
|
839
|
-
var row = dt.row( ':eq('+rows[rowIdx]+')', {page:'current'} ); // Workaround for M581
|
|
840
|
-
var cell = dt.cell( row.index(), column+':visible' );
|
|
841
|
-
var data = cell.data();
|
|
842
|
-
var cellIndex = cell.index();
|
|
843
|
-
var editField = dtColumns[ cellIndex.column ].editField;
|
|
844
|
-
|
|
845
|
-
if ( editField !== undefined ) {
|
|
846
|
-
data = DataTable.util.get( editField )( dt.row( cellIndex.row ).data() );
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
if ( enabledColumns.indexOf(cellIndex.column) === -1 ) {
|
|
850
|
-
return;
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
return {
|
|
854
|
-
cell: cell,
|
|
855
|
-
data: data,
|
|
856
|
-
label: cell.data(),
|
|
857
|
-
index: cellIndex
|
|
858
|
-
};
|
|
859
|
-
} )
|
|
860
|
-
);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
this._actionSelector( selected );
|
|
864
|
-
|
|
865
|
-
// Stop shiftScroll
|
|
866
|
-
clearInterval( this.s.scrollInterval );
|
|
867
|
-
this.s.scrollInterval = null;
|
|
868
|
-
},
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Create an array with a range of numbers defined by the start and end
|
|
873
|
-
* parameters passed in (inclusive!).
|
|
874
|
-
*
|
|
875
|
-
* @param {integer} start Start
|
|
876
|
-
* @param {integer} end End
|
|
877
|
-
* @private
|
|
878
|
-
*/
|
|
879
|
-
_range: function ( start, end )
|
|
880
|
-
{
|
|
881
|
-
var out = [];
|
|
882
|
-
var i;
|
|
883
|
-
|
|
884
|
-
if ( start <= end ) {
|
|
885
|
-
for ( i=start ; i<=end ; i++ ) {
|
|
886
|
-
out.push( i );
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
else {
|
|
890
|
-
for ( i=start ; i>=end ; i-- ) {
|
|
891
|
-
out.push( i );
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
return out;
|
|
896
|
-
},
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
/**
|
|
900
|
-
* Move the window and DataTables scrolling during a drag to scroll new
|
|
901
|
-
* content into view. This is done by proximity to the edge of the scrolling
|
|
902
|
-
* container of the mouse - for example near the top edge of the window
|
|
903
|
-
* should scroll up. This is a little complicated as there are two elements
|
|
904
|
-
* that can be scrolled - the window and the DataTables scrolling view port
|
|
905
|
-
* (if scrollX and / or scrollY is enabled).
|
|
906
|
-
*
|
|
907
|
-
* @param {object} e Mouse move event object
|
|
908
|
-
* @private
|
|
909
|
-
*/
|
|
910
|
-
_shiftScroll: function ( e )
|
|
911
|
-
{
|
|
912
|
-
var that = this;
|
|
913
|
-
var scroll = this.s.scroll;
|
|
914
|
-
var runInterval = false;
|
|
915
|
-
var scrollSpeed = 5;
|
|
916
|
-
var buffer = 65;
|
|
917
|
-
|
|
918
|
-
// Different values if using a touchscreen
|
|
919
|
-
var pageX = !e.type.includes('touch') ? e.pageX - window.scrollX :e.touches[0].clientX;
|
|
920
|
-
var pageY = !e.type.includes('touch') ? e.pageY - window.scrollY :e.touches[0].clientY;
|
|
921
|
-
var
|
|
922
|
-
windowY = pageY,
|
|
923
|
-
windowX = pageX,
|
|
924
|
-
windowVert, windowHoriz,
|
|
925
|
-
dtVert, dtHoriz;
|
|
926
|
-
|
|
927
|
-
// Window calculations - based on the mouse position in the window,
|
|
928
|
-
// regardless of scrolling
|
|
929
|
-
if ( windowY < buffer ) {
|
|
930
|
-
windowVert = scrollSpeed * -1;
|
|
931
|
-
}
|
|
932
|
-
else if ( windowY > scroll.windowHeight - buffer ) {
|
|
933
|
-
windowVert = scrollSpeed;
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
if ( windowX < buffer ) {
|
|
937
|
-
windowHoriz = scrollSpeed * -1;
|
|
938
|
-
}
|
|
939
|
-
else if ( windowX > scroll.windowWidth - buffer ) {
|
|
940
|
-
windowHoriz = scrollSpeed;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// DataTables scrolling calculations - based on the table's position in
|
|
944
|
-
// the document and the mouse position on the page
|
|
945
|
-
if ( scroll.dtTop !== null && pageY < scroll.dtTop + buffer ) {
|
|
946
|
-
dtVert = scrollSpeed * -1;
|
|
947
|
-
}
|
|
948
|
-
else if ( scroll.dtTop !== null && pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
|
|
949
|
-
dtVert = scrollSpeed;
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
if ( scroll.dtLeft !== null && pageX < scroll.dtLeft + buffer ) {
|
|
953
|
-
dtHoriz = scrollSpeed * -1;
|
|
954
|
-
}
|
|
955
|
-
else if ( scroll.dtLeft !== null && pageX > scroll.dtLeft + scroll.dtWidth - buffer ) {
|
|
956
|
-
dtHoriz = scrollSpeed;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// This is where it gets interesting. We want to continue scrolling
|
|
960
|
-
// without requiring a mouse move, so we need an interval to be
|
|
961
|
-
// triggered. The interval should continue until it is no longer needed,
|
|
962
|
-
// but it must also use the latest scroll commands (for example consider
|
|
963
|
-
// that the mouse might move from scrolling up to scrolling left, all
|
|
964
|
-
// with the same interval running. We use the `scroll` object to "pass"
|
|
965
|
-
// this information to the interval. Can't use local variables as they
|
|
966
|
-
// wouldn't be the ones that are used by an already existing interval!
|
|
967
|
-
if ( windowVert || windowHoriz || dtVert || dtHoriz ) {
|
|
968
|
-
scroll.windowVert = windowVert;
|
|
969
|
-
scroll.windowHoriz = windowHoriz;
|
|
970
|
-
scroll.dtVert = dtVert;
|
|
971
|
-
scroll.dtHoriz = dtHoriz;
|
|
972
|
-
runInterval = true;
|
|
973
|
-
}
|
|
974
|
-
else if ( this.s.scrollInterval ) {
|
|
975
|
-
// Don't need to scroll - remove any existing timer
|
|
976
|
-
clearInterval( this.s.scrollInterval );
|
|
977
|
-
this.s.scrollInterval = null;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
// If we need to run the interval to scroll and there is no existing
|
|
981
|
-
// interval (if there is an existing one, it will continue to run)
|
|
982
|
-
if ( ! this.s.scrollInterval && runInterval ) {
|
|
983
|
-
this.s.scrollInterval = setInterval( function () {
|
|
984
|
-
// Don't need to worry about setting scroll <0 or beyond the
|
|
985
|
-
// scroll bound as the browser will just reject that.
|
|
986
|
-
window.scrollTo(window.scrollX + (scroll.windowHoriz ? scroll.windowHoriz : 0), window.scrollY + (scroll.windowVert ? scroll.windowVert : 0))
|
|
987
|
-
|
|
988
|
-
// DataTables scrolling
|
|
989
|
-
if ( scroll.dtVert || scroll.dtHoriz ) {
|
|
990
|
-
var scroller = that.dom.dtScroll[0];
|
|
991
|
-
|
|
992
|
-
if ( scroll.dtVert ) {
|
|
993
|
-
scroller.scrollTop += scroll.dtVert;
|
|
994
|
-
}
|
|
995
|
-
if ( scroll.dtHoriz ) {
|
|
996
|
-
scroller.scrollLeft += scroll.dtHoriz;
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
}, 20 );
|
|
1000
|
-
}
|
|
1001
|
-
},
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
/**
|
|
1005
|
-
* Update the DataTable after the user has selected what they want to do
|
|
1006
|
-
*
|
|
1007
|
-
* @param {false|undefined} result Return from the `execute` method - can
|
|
1008
|
-
* be false internally to do nothing. This is not documented for plug-ins
|
|
1009
|
-
* and is used only by the cancel option.
|
|
1010
|
-
* @param {array} cells Information about the selected cells from the key
|
|
1011
|
-
* up function, argumented with the set values
|
|
1012
|
-
* @private
|
|
1013
|
-
*/
|
|
1014
|
-
_update: function ( result, cells )
|
|
1015
|
-
{
|
|
1016
|
-
// Do nothing on `false` return from an execute function
|
|
1017
|
-
if ( result === false ) {
|
|
1018
|
-
return;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
var dt = this.s.dt;
|
|
1022
|
-
var cell;
|
|
1023
|
-
var columns = dt.columns( this.c.columns ).indexes();
|
|
1024
|
-
|
|
1025
|
-
// Potentially allow modifications to the cells matrix
|
|
1026
|
-
this._emitEvent( 'preAutoFill', [ dt, cells ] );
|
|
1027
|
-
|
|
1028
|
-
this._editor( cells );
|
|
1029
|
-
|
|
1030
|
-
// Automatic updates are not performed if `update` is null and the
|
|
1031
|
-
// `editor` parameter is passed in - the reason being that Editor will
|
|
1032
|
-
// update the data once submitted
|
|
1033
|
-
var update = this.c.update !== null ?
|
|
1034
|
-
this.c.update :
|
|
1035
|
-
this.c.editor ?
|
|
1036
|
-
false :
|
|
1037
|
-
true;
|
|
1038
|
-
|
|
1039
|
-
if ( update ) {
|
|
1040
|
-
for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
|
|
1041
|
-
for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
|
|
1042
|
-
cell = cells[i][j];
|
|
1043
|
-
|
|
1044
|
-
if ( columns.indexOf(cell.index.column) !== -1 ) {
|
|
1045
|
-
cell.cell.data( cell.set );
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
dt.draw(false);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
this._emitEvent( 'autoFill', [ dt, cells ] );
|
|
1054
|
-
}
|
|
1055
|
-
} );
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
835
|
/**
|
|
1059
|
-
* AutoFill actions. The options here determine how AutoFill will fill the
|
|
1060
|
-
* in the table when the user has selected a range of cells. Please see
|
|
1061
|
-
* documentation on the DataTables site for full details on how to
|
|
1062
|
-
* ins.
|
|
1063
|
-
*
|
|
1064
|
-
* @type {Object}
|
|
836
|
+
* AutoFill actions. The options here determine how AutoFill will fill the
|
|
837
|
+
* data in the table when the user has selected a range of cells. Please see
|
|
838
|
+
* the documentation on the DataTables site for full details on how to
|
|
839
|
+
* create plug- ins.
|
|
1065
840
|
*/
|
|
1066
841
|
AutoFill.actions = {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
},
|
|
1151
|
-
|
|
1152
|
-
// Special type that does not make itself available, but is added
|
|
1153
|
-
// automatically by AutoFill if a multi-choice list is shown. This allows
|
|
1154
|
-
// sensible code reuse
|
|
1155
|
-
cancel: {
|
|
1156
|
-
available: function () {
|
|
1157
|
-
return false;
|
|
1158
|
-
},
|
|
1159
|
-
|
|
1160
|
-
option: function ( dt ) {
|
|
1161
|
-
return dt.i18n('autoFill.cancel', 'Cancel' );
|
|
1162
|
-
},
|
|
1163
|
-
|
|
1164
|
-
execute: function () {
|
|
1165
|
-
return false;
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
842
|
+
increment: {
|
|
843
|
+
available: function (dt, cells) {
|
|
844
|
+
let d = cells[0][0].label;
|
|
845
|
+
// is numeric test based on jQuery's old `isNumeric` function
|
|
846
|
+
return !isNaN(d - parseFloat(d));
|
|
847
|
+
},
|
|
848
|
+
option: function (dt, cells) {
|
|
849
|
+
return dt.i18n('autoFill.increment', 'Increment / decrement each cell by: <input type="number" value="1">');
|
|
850
|
+
},
|
|
851
|
+
execute: function (dt, cells, node) {
|
|
852
|
+
let value = cells[0][0].data * 1;
|
|
853
|
+
let increment = node ? parseFloat(node.find('input').val()) : 1;
|
|
854
|
+
for (let i = 0, ien = cells.length; i < ien; i++) {
|
|
855
|
+
for (let j = 0, jen = cells[i].length; j < jen; j++) {
|
|
856
|
+
cells[i][j].set = value;
|
|
857
|
+
value += increment;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
fill: {
|
|
863
|
+
available: function (dt, cells) {
|
|
864
|
+
return true;
|
|
865
|
+
},
|
|
866
|
+
option: function (dt, cells) {
|
|
867
|
+
return dt.i18n('autoFill.fill', 'Fill all cells with <i>%d</i>', cells[0][0].label);
|
|
868
|
+
},
|
|
869
|
+
execute: function (dt, cells, node) {
|
|
870
|
+
let value = cells[0][0].data;
|
|
871
|
+
for (let i = 0, ien = cells.length; i < ien; i++) {
|
|
872
|
+
for (let j = 0, jen = cells[i].length; j < jen; j++) {
|
|
873
|
+
cells[i][j].set = value;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
},
|
|
878
|
+
fillHorizontal: {
|
|
879
|
+
available: function (dt, cells) {
|
|
880
|
+
return cells.length > 1 && cells[0].length > 1;
|
|
881
|
+
},
|
|
882
|
+
option: function (dt, cells) {
|
|
883
|
+
return dt.i18n('autoFill.fillHorizontal', 'Fill cells horizontally');
|
|
884
|
+
},
|
|
885
|
+
execute: function (dt, cells, node) {
|
|
886
|
+
for (let i = 0, ien = cells.length; i < ien; i++) {
|
|
887
|
+
for (let j = 0, jen = cells[i].length; j < jen; j++) {
|
|
888
|
+
cells[i][j].set = cells[i][0].data;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
fillVertical: {
|
|
894
|
+
available: function (dt, cells) {
|
|
895
|
+
return cells.length > 1 && cells[0].length > 1;
|
|
896
|
+
},
|
|
897
|
+
option: function (dt, cells) {
|
|
898
|
+
return dt.i18n('autoFill.fillVertical', 'Fill cells vertically');
|
|
899
|
+
},
|
|
900
|
+
execute: function (dt, cells, node) {
|
|
901
|
+
for (let i = 0, ien = cells.length; i < ien; i++) {
|
|
902
|
+
for (let j = 0, jen = cells[i].length; j < jen; j++) {
|
|
903
|
+
cells[i][j].set = cells[0][j].data;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
},
|
|
908
|
+
// Special type that does not make itself available, but is added
|
|
909
|
+
// automatically by AutoFill if a multi-choice list is shown. This
|
|
910
|
+
// allows sensible code reuse
|
|
911
|
+
cancel: {
|
|
912
|
+
available: function () {
|
|
913
|
+
return false;
|
|
914
|
+
},
|
|
915
|
+
option: function (dt) {
|
|
916
|
+
return dt.i18n('autoFill.cancel', 'Cancel');
|
|
917
|
+
},
|
|
918
|
+
execute: function () {
|
|
919
|
+
return false;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
1168
922
|
};
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
/**
|
|
1172
|
-
* AutoFill version
|
|
1173
|
-
*
|
|
1174
|
-
* @static
|
|
1175
|
-
* @type String
|
|
1176
|
-
*/
|
|
1177
|
-
AutoFill.version = '2.7.1';
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
/**
|
|
1181
|
-
* AutoFill defaults
|
|
1182
|
-
*
|
|
1183
|
-
* @namespace
|
|
1184
|
-
*/
|
|
1185
|
-
AutoFill.defaults = {
|
|
1186
|
-
/** @type {Boolean} Ask user what they want to do, even for a single option */
|
|
1187
|
-
alwaysAsk: false,
|
|
1188
|
-
|
|
1189
|
-
closeButton: true,
|
|
1190
|
-
|
|
1191
|
-
/** @type {string|null} What will trigger a focus */
|
|
1192
|
-
focus: null, // focus, click, hover
|
|
1193
|
-
|
|
1194
|
-
/** @type {column-selector} Columns to provide auto fill for */
|
|
1195
|
-
columns: '', // all
|
|
1196
|
-
|
|
1197
|
-
/** @type {Boolean} Enable AutoFill on load */
|
|
1198
|
-
enable: true,
|
|
1199
|
-
|
|
1200
|
-
/** @type {boolean|null} Update the cells after a drag */
|
|
1201
|
-
update: null, // false is editor given, true otherwise
|
|
1202
|
-
|
|
1203
|
-
/** @type {DataTable.Editor} Editor instance for automatic submission */
|
|
1204
|
-
editor: null,
|
|
1205
|
-
|
|
1206
|
-
/** @type {boolean} Enable vertical fill */
|
|
1207
|
-
vertical: true,
|
|
1208
|
-
|
|
1209
|
-
/** @type {boolean} Enable horizontal fill */
|
|
1210
|
-
horizontal: true
|
|
1211
|
-
};
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
/**
|
|
1215
|
-
* Classes used by AutoFill that are configurable
|
|
1216
|
-
*
|
|
1217
|
-
* @namespace
|
|
1218
|
-
*/
|
|
923
|
+
/** Class names used by AutoFill for customisation */
|
|
1219
924
|
AutoFill.classes = {
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
closeable: 'dtaf-popover-closeable'
|
|
925
|
+
btn: 'btn',
|
|
926
|
+
close: 'dtaf-list-close',
|
|
927
|
+
closeable: 'dtaf-list-closeable'
|
|
1224
928
|
};
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
929
|
+
/** Defaults */
|
|
930
|
+
AutoFill.defaults = {
|
|
931
|
+
alwaysAsk: false,
|
|
932
|
+
closeButton: true,
|
|
933
|
+
focus: null,
|
|
934
|
+
columns: '',
|
|
935
|
+
enable: true,
|
|
936
|
+
update: true,
|
|
937
|
+
editor: null,
|
|
938
|
+
vertical: true,
|
|
939
|
+
horizontal: true
|
|
940
|
+
};
|
|
941
|
+
/** AutoFill version */
|
|
942
|
+
AutoFill.version = '3.0.0-beta.1';
|
|
1231
943
|
|
|
1232
944
|
// Doesn't do anything - Not documented
|
|
1233
|
-
Api.register(
|
|
1234
|
-
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
return this.iterator( 'table', function ( ctx ) {
|
|
1255
|
-
if ( ctx.autoFill ) {
|
|
1256
|
-
ctx.autoFill.disable();
|
|
1257
|
-
}
|
|
1258
|
-
} );
|
|
1259
|
-
} );
|
|
1260
|
-
|
|
1261
|
-
|
|
945
|
+
Api.register('autoFill()', function () {
|
|
946
|
+
return this.inst(this.context);
|
|
947
|
+
});
|
|
948
|
+
Api.register('autoFill().enabled()', function () {
|
|
949
|
+
var ctx = this.context[0];
|
|
950
|
+
return ctx.autoFill ? ctx.autoFill.enabled() : false;
|
|
951
|
+
});
|
|
952
|
+
Api.register('autoFill().enable()', function (flag) {
|
|
953
|
+
return this.iterator('table', function (ctx) {
|
|
954
|
+
if (ctx.autoFill) {
|
|
955
|
+
ctx.autoFill.enable(flag);
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
});
|
|
959
|
+
Api.register('autoFill().disable()', function () {
|
|
960
|
+
return this.iterator('table', function (ctx) {
|
|
961
|
+
if (ctx.autoFill) {
|
|
962
|
+
ctx.autoFill.disable();
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
});
|
|
1262
966
|
// Attach a listener to the document which listens for DataTables initialisation
|
|
1263
967
|
// events so we can automatically initialise
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
968
|
+
Dom.s(document).on('preInit.dt.autofill', function (e, settings) {
|
|
969
|
+
if (e.namespace !== 'dt') {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
let init = settings.init.autoFill;
|
|
973
|
+
let defaults = DataTable.defaults.autoFill;
|
|
974
|
+
if (init || defaults) {
|
|
975
|
+
let opts = {};
|
|
976
|
+
if (util.is.plainObject(defaults)) {
|
|
977
|
+
util.object.assign(opts, defaults);
|
|
978
|
+
}
|
|
979
|
+
if (util.is.plainObject(init)) {
|
|
980
|
+
util.object.assign(opts, init);
|
|
981
|
+
}
|
|
982
|
+
if (init !== false) {
|
|
983
|
+
new AutoFill(settings, opts);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
});
|
|
1282
987
|
// Alias for access
|
|
1283
988
|
DataTable.AutoFill = AutoFill;
|
|
1284
|
-
$.fn.DataTable.AutoFill = AutoFill;
|
|
1285
989
|
|
|
1286
990
|
|
|
1287
991
|
return DataTable;
|