effective_datatables 4.17.3 → 4.18.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.
@@ -1,15 +1,62 @@
1
- /*! RowReorder 1.2.4
2
- * 2015-2018 SpryMedia Ltd - datatables.net/license
1
+ /*! RowReorder 1.4.1
2
+ * © SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
5
+ (function( factory ){
6
+ if ( typeof define === 'function' && define.amd ) {
7
+ // AMD
8
+ define( ['jquery', 'datatables.net'], function ( $ ) {
9
+ return factory( $, window, document );
10
+ } );
11
+ }
12
+ else if ( typeof exports === 'object' ) {
13
+ // CommonJS
14
+ var jq = require('jquery');
15
+ var cjsRequires = function (root, $) {
16
+ if ( ! $.fn.dataTable ) {
17
+ require('datatables.net')(root, $);
18
+ }
19
+ };
20
+
21
+ if (typeof window === 'undefined') {
22
+ module.exports = function (root, $) {
23
+ if ( ! root ) {
24
+ // CommonJS environments without a window global must pass a
25
+ // root. This will give an error otherwise
26
+ root = window;
27
+ }
28
+
29
+ if ( ! $ ) {
30
+ $ = jq( root );
31
+ }
32
+
33
+ cjsRequires( root, $ );
34
+ return factory( $, root, root.document );
35
+ };
36
+ }
37
+ else {
38
+ cjsRequires( window, jq );
39
+ module.exports = factory( jq, window, window.document );
40
+ }
41
+ }
42
+ else {
43
+ // Browser
44
+ factory( jQuery, window, document );
45
+ }
46
+ }(function( $, window, document, undefined ) {
47
+ 'use strict';
48
+ var DataTable = $.fn.dataTable;
49
+
50
+
51
+
5
52
  /**
6
53
  * @summary RowReorder
7
54
  * @description Row reordering extension for DataTables
8
- * @version 1.2.4
55
+ * @version 1.4.1
9
56
  * @file dataTables.rowReorder.js
10
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
11
- * @contact www.sprymedia.co.uk/contact
12
- * @copyright Copyright 2015-2018 SpryMedia Ltd.
57
+ * @author SpryMedia Ltd
58
+ * @contact datatables.net
59
+ * @copyright Copyright 2015-2023 SpryMedia Ltd.
13
60
  *
14
61
  * This source file is free software, available under the following license:
15
62
  * MIT license - http://datatables.net/license/mit
@@ -21,36 +68,6 @@
21
68
  * For details please refer to: http://www.datatables.net
22
69
  */
23
70
 
24
- (function( factory ){
25
- if ( typeof define === 'function' && define.amd ) {
26
- // AMD
27
- define( ['jquery', 'datatables.net'], function ( $ ) {
28
- return factory( $, window, document );
29
- } );
30
- }
31
- else if ( typeof exports === 'object' ) {
32
- // CommonJS
33
- module.exports = function (root, $) {
34
- if ( ! root ) {
35
- root = window;
36
- }
37
-
38
- if ( ! $ || ! $.fn.dataTable ) {
39
- $ = require('datatables.net')(root, $).$;
40
- }
41
-
42
- return factory( $, root, root.document );
43
- };
44
- }
45
- else {
46
- // Browser
47
- factory( jQuery, window, document );
48
- }
49
- }(function( $, window, document, undefined ) {
50
- 'use strict';
51
- var DataTable = $.fn.dataTable;
52
-
53
-
54
71
  /**
55
72
  * RowReorder provides the ability in DataTables to click and drag rows to
56
73
  * reorder them. When a row is dropped the data for the rows effected will be
@@ -72,613 +89,813 @@ var DataTable = $.fn.dataTable;
72
89
  * @requires jQuery 1.7+
73
90
  * @requires DataTables 1.10.7+
74
91
  */
75
- var RowReorder = function ( dt, opts ) {
76
- // Sanity check that we are using DataTables 1.10 or newer
77
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
78
- throw 'DataTables RowReorder requires DataTables 1.10.8 or newer';
79
- }
80
-
81
- // User and defaults configuration object
82
- this.c = $.extend( true, {},
83
- DataTable.defaults.rowReorder,
84
- RowReorder.defaults,
85
- opts
86
- );
87
-
88
- // Internal settings
89
- this.s = {
90
- /** @type {integer} Scroll body top cache */
91
- bodyTop: null,
92
-
93
- /** @type {DataTable.Api} DataTables' API instance */
94
- dt: new DataTable.Api( dt ),
95
-
96
- /** @type {function} Data fetch function */
97
- getDataFn: DataTable.ext.oApi._fnGetObjectDataFn( this.c.dataSrc ),
98
-
99
- /** @type {array} Pixel positions for row insertion calculation */
100
- middles: null,
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
- /** @type {function} Data set function */
109
- setDataFn: DataTable.ext.oApi._fnSetObjectDataFn( this.c.dataSrc ),
110
-
111
- /** @type {Object} Mouse down information */
112
- start: {
113
- top: 0,
114
- left: 0,
115
- offsetTop: 0,
116
- offsetLeft: 0,
117
- nodes: []
118
- },
119
-
120
- /** @type {integer} Window height cached value */
121
- windowHeight: 0,
122
-
123
- /** @type {integer} Document outer height cached value */
124
- documentOuterHeight: 0,
125
-
126
- /** @type {integer} DOM clone outer height cached value */
127
- domCloneOuterHeight: 0
128
- };
129
-
130
- // DOM items
131
- this.dom = {
132
- /** @type {jQuery} Cloned row being moved around */
133
- clone: null,
134
-
135
- /** @type {jQuery} DataTables scrolling container */
136
- dtScroll: $('div.dataTables_scrollBody', this.s.dt.table().container())
137
- };
138
-
139
- // Check if row reorder has already been initialised on this table
140
- var settings = this.s.dt.settings()[0];
141
- var exisiting = settings.rowreorder;
142
- if ( exisiting ) {
143
- return exisiting;
144
- }
145
-
146
- settings.rowreorder = this;
147
- this._constructor();
148
- };
92
+ var RowReorder = function (dt, opts) {
93
+ // Sanity check that we are using DataTables 1.10 or newer
94
+ if (!DataTable.versionCheck || !DataTable.versionCheck('1.10.8')) {
95
+ throw 'DataTables RowReorder requires DataTables 1.10.8 or newer';
96
+ }
97
+
98
+ // User and defaults configuration object
99
+ this.c = $.extend(true, {}, DataTable.defaults.rowReorder, RowReorder.defaults, opts);
100
+
101
+ // Internal settings
102
+ this.s = {
103
+ /** @type {integer} Scroll body top cache */
104
+ bodyTop: null,
105
+
106
+ /** @type {DataTable.Api} DataTables' API instance */
107
+ dt: new DataTable.Api(dt),
149
108
 
109
+ /** @type {function} Data fetch function */
110
+ getDataFn: DataTable.ext.oApi._fnGetObjectDataFn(this.c.dataSrc),
150
111
 
151
- $.extend( RowReorder.prototype, {
152
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
153
- * Constructor
154
- */
155
-
156
- /**
157
- * Initialise the RowReorder instance
158
- *
159
- * @private
160
- */
161
- _constructor: function ()
162
- {
163
- var that = this;
164
- var dt = this.s.dt;
165
- var table = $( dt.table().node() );
166
-
167
- // Need to be able to calculate the row positions relative to the table
168
- if ( table.css('position') === 'static' ) {
169
- table.css( 'position', 'relative' );
170
- }
171
-
172
- // listen for mouse down on the target column - we have to implement
173
- // this rather than using HTML5 drag and drop as drag and drop doesn't
174
- // appear to work on table rows at this time. Also mobile browsers are
175
- // not supported.
176
- // Use `table().container()` rather than just the table node for IE8 -
177
- // otherwise it only works once...
178
- $(dt.table().container()).on( 'mousedown.rowReorder touchstart.rowReorder', this.c.selector, function (e) {
179
- if ( ! that.c.enable ) {
180
- return;
181
- }
182
-
183
- // Ignore excluded children of the selector
184
- if ( $(e.target).is(that.c.excludedChildren) ) {
185
- return true;
186
- }
187
-
188
- var tr = $(this).closest('tr');
189
- var row = dt.row( tr );
190
-
191
- // Double check that it is a DataTable row
192
- if ( row.any() ) {
193
- that._emitEvent( 'pre-row-reorder', {
194
- node: row.node(),
195
- index: row.index()
196
- } );
197
-
198
- that._mouseDown( e, tr );
199
- return false;
200
- }
201
- } );
202
-
203
- dt.on( 'destroy.rowReorder', function () {
204
- $(dt.table().container()).off( '.rowReorder' );
205
- dt.off( '.rowReorder' );
206
- } );
207
- },
208
-
209
-
210
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
211
- * Private methods
212
- */
213
-
214
- /**
215
- * Cache the measurements that RowReorder needs in the mouse move handler
216
- * to attempt to speed things up, rather than reading from the DOM.
217
- *
218
- * @private
219
- */
220
- _cachePositions: function ()
221
- {
222
- var dt = this.s.dt;
223
-
224
- // Frustratingly, if we add `position:relative` to the tbody, the
225
- // position is still relatively to the parent. So we need to adjust
226
- // for that
227
- var headerHeight = $( dt.table().node() ).find('thead').outerHeight();
228
-
229
- // Need to pass the nodes through jQuery to get them in document order,
230
- // not what DataTables thinks it is, since we have been altering the
231
- // order
232
- var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
233
- var tops = $.map( nodes, function ( node, i ) {
234
- return $(node).position().top - headerHeight;
235
- } );
236
-
237
- var middles = $.map( tops, function ( top, i ) {
238
- return tops.length < i-1 ?
239
- (top + tops[i+1]) / 2 :
240
- (top + top + $( dt.row( ':last-child' ).node() ).outerHeight() ) / 2;
241
- } );
242
-
243
- this.s.middles = middles;
244
- this.s.bodyTop = $( dt.table().body() ).offset().top;
245
- this.s.windowHeight = $(window).height();
246
- this.s.documentOuterHeight = $(document).outerHeight();
247
- },
248
-
249
-
250
- /**
251
- * Clone a row so it can be floated around the screen
252
- *
253
- * @param {jQuery} target Node to be cloned
254
- * @private
255
- */
256
- _clone: function ( target )
257
- {
258
- var dt = this.s.dt;
259
- var clone = $( dt.table().node().cloneNode(false) )
260
- .addClass( 'dt-rowReorder-float' )
261
- .append('<tbody/>')
262
- .append( target.clone( false ) );
263
-
264
- // Match the table and column widths - read all sizes before setting
265
- // to reduce reflows
266
- var tableWidth = target.outerWidth();
267
- var tableHeight = target.outerHeight();
268
- var sizes = target.children().map( function () {
269
- return $(this).width();
270
- } );
271
-
272
- clone
273
- .width( tableWidth )
274
- .height( tableHeight )
275
- .find('tr').children().each( function (i) {
276
- this.style.width = sizes[i]+'px';
277
- } );
278
-
279
- // Insert into the document to have it floating around
280
- clone.appendTo( 'body' );
281
-
282
- this.dom.clone = clone;
283
- this.s.domCloneOuterHeight = clone.outerHeight();
284
- },
285
-
286
-
287
- /**
288
- * Update the cloned item's position in the document
289
- *
290
- * @param {object} e Event giving the mouse's position
291
- * @private
292
- */
293
- _clonePosition: function ( e )
294
- {
295
- var start = this.s.start;
296
- var topDiff = this._eventToPage( e, 'Y' ) - start.top;
297
- var leftDiff = this._eventToPage( e, 'X' ) - start.left;
298
- var snap = this.c.snapX;
299
- var left;
300
- var top = topDiff + start.offsetTop;
301
-
302
- if ( snap === true ) {
303
- left = start.offsetLeft;
304
- }
305
- else if ( typeof snap === 'number' ) {
306
- left = start.offsetLeft + snap;
307
- }
308
- else {
309
- left = leftDiff + start.offsetLeft;
310
- }
311
-
312
- if(top < 0) {
313
- top = 0
314
- }
315
- else if(top + this.s.domCloneOuterHeight > this.s.documentOuterHeight) {
316
- top = this.s.documentOuterHeight - this.s.domCloneOuterHeight;
317
- }
318
-
319
- this.dom.clone.css( {
320
- top: top,
321
- left: left
322
- } );
323
- },
324
-
325
-
326
- /**
327
- * Emit an event on the DataTable for listeners
328
- *
329
- * @param {string} name Event name
330
- * @param {array} args Event arguments
331
- * @private
332
- */
333
- _emitEvent: function ( name, args )
334
- {
335
- this.s.dt.iterator( 'table', function ( ctx, i ) {
336
- $(ctx.nTable).triggerHandler( name+'.dt', args );
337
- } );
338
- },
339
-
340
-
341
- /**
342
- * Get pageX/Y position from an event, regardless of if it is a mouse or
343
- * touch event.
344
- *
345
- * @param {object} e Event
346
- * @param {string} pos X or Y (must be a capital)
347
- * @private
348
- */
349
- _eventToPage: function ( e, pos )
350
- {
351
- if ( e.type.indexOf( 'touch' ) !== -1 ) {
352
- return e.originalEvent.touches[0][ 'page'+pos ];
353
- }
354
-
355
- return e[ 'page'+pos ];
356
- },
357
-
358
-
359
- /**
360
- * Mouse down event handler. Read initial positions and add event handlers
361
- * for the move.
362
- *
363
- * @param {object} e Mouse event
364
- * @param {jQuery} target TR element that is to be moved
365
- * @private
366
- */
367
- _mouseDown: function ( e, target )
368
- {
369
- var that = this;
370
- var dt = this.s.dt;
371
- var start = this.s.start;
372
-
373
- var offset = target.offset();
374
- start.top = this._eventToPage( e, 'Y' );
375
- start.left = this._eventToPage( e, 'X' );
376
- start.offsetTop = offset.top;
377
- start.offsetLeft = offset.left;
378
- start.nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
379
-
380
- this._cachePositions();
381
- this._clone( target );
382
- this._clonePosition( e );
383
-
384
- this.dom.target = target;
385
- target.addClass( 'dt-rowReorder-moving' );
386
-
387
- $( document )
388
- .on( 'mouseup.rowReorder touchend.rowReorder', function (e) {
389
- that._mouseUp(e);
390
- } )
391
- .on( 'mousemove.rowReorder touchmove.rowReorder', function (e) {
392
- that._mouseMove(e);
393
- } );
394
-
395
- // Check if window is x-scrolling - if not, disable it for the duration
396
- // of the drag
397
- if ( $(window).width() === $(document).width() ) {
398
- $(document.body).addClass( 'dt-rowReorder-noOverflow' );
399
- }
400
-
401
- // Cache scrolling information so mouse move doesn't need to read.
402
- // This assumes that the window and DT scroller will not change size
403
- // during an row drag, which I think is a fair assumption
404
- var scrollWrapper = this.dom.dtScroll;
405
- this.s.scroll = {
406
- windowHeight: $(window).height(),
407
- windowWidth: $(window).width(),
408
- dtTop: scrollWrapper.length ? scrollWrapper.offset().top : null,
409
- dtLeft: scrollWrapper.length ? scrollWrapper.offset().left : null,
410
- dtHeight: scrollWrapper.length ? scrollWrapper.outerHeight() : null,
411
- dtWidth: scrollWrapper.length ? scrollWrapper.outerWidth() : null
412
- };
413
- },
414
-
415
-
416
- /**
417
- * Mouse move event handler - move the cloned row and shuffle the table's
418
- * rows if required.
419
- *
420
- * @param {object} e Mouse event
421
- * @private
422
- */
423
- _mouseMove: function ( e )
424
- {
425
- this._clonePosition( e );
426
-
427
- // Transform the mouse position into a position in the table's body
428
- var bodyY = this._eventToPage( e, 'Y' ) - this.s.bodyTop;
429
- var middles = this.s.middles;
430
- var insertPoint = null;
431
- var dt = this.s.dt;
432
- var body = dt.table().body();
433
-
434
- // Determine where the row should be inserted based on the mouse
435
- // position
436
- for ( var i=0, ien=middles.length ; i<ien ; i++ ) {
437
- if ( bodyY < middles[i] ) {
438
- insertPoint = i;
439
- break;
440
- }
441
- }
442
-
443
- if ( insertPoint === null ) {
444
- insertPoint = middles.length;
445
- }
446
-
447
- // Perform the DOM shuffle if it has changed from last time
448
- if ( this.s.lastInsert === null || this.s.lastInsert !== insertPoint ) {
449
- if ( insertPoint === 0 ) {
450
- this.dom.target.prependTo( body );
451
- }
452
- else {
453
- var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
454
-
455
- if ( insertPoint > this.s.lastInsert ) {
456
- this.dom.target.insertAfter( nodes[ insertPoint-1 ] );
457
- }
458
- else {
459
- this.dom.target.insertBefore( nodes[ insertPoint ] );
460
- }
461
- }
462
-
463
- this._cachePositions();
464
-
465
- this.s.lastInsert = insertPoint;
466
- }
467
-
468
- this._shiftScroll( e );
469
- },
470
-
471
-
472
- /**
473
- * Mouse up event handler - release the event handlers and perform the
474
- * table updates
475
- *
476
- * @param {object} e Mouse event
477
- * @private
478
- */
479
- _mouseUp: function ( e )
480
- {
481
- var that = this;
482
- var dt = this.s.dt;
483
- var i, ien;
484
- var dataSrc = this.c.dataSrc;
485
-
486
- this.dom.clone.remove();
487
- this.dom.clone = null;
488
-
489
- this.dom.target.removeClass( 'dt-rowReorder-moving' );
490
- //this.dom.target = null;
491
-
492
- $(document).off( '.rowReorder' );
493
- $(document.body).removeClass( 'dt-rowReorder-noOverflow' );
494
-
495
- clearInterval( this.s.scrollInterval );
496
- this.s.scrollInterval = null;
497
-
498
- // Calculate the difference
499
- var startNodes = this.s.start.nodes;
500
- var endNodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
501
- var idDiff = {};
502
- var fullDiff = [];
503
- var diffNodes = [];
504
- var getDataFn = this.s.getDataFn;
505
- var setDataFn = this.s.setDataFn;
506
-
507
- for ( i=0, ien=startNodes.length ; i<ien ; i++ ) {
508
- if ( startNodes[i] !== endNodes[i] ) {
509
- var id = dt.row( endNodes[i] ).id();
510
- var endRowData = dt.row( endNodes[i] ).data();
511
- var startRowData = dt.row( startNodes[i] ).data();
512
-
513
- if ( id ) {
514
- idDiff[ id ] = getDataFn( startRowData );
515
- }
516
-
517
- fullDiff.push( {
518
- node: endNodes[i],
519
- oldData: getDataFn( endRowData ),
520
- newData: getDataFn( startRowData ),
521
- newPosition: i,
522
- oldPosition: $.inArray( endNodes[i], startNodes )
523
- } );
524
-
525
- diffNodes.push( endNodes[i] );
526
- }
527
- }
528
-
529
- // Create event args
530
- var eventArgs = [ fullDiff, {
531
- dataSrc: dataSrc,
532
- nodes: diffNodes,
533
- values: idDiff,
534
- triggerRow: dt.row( this.dom.target )
535
- } ];
536
-
537
- // Emit event
538
- this._emitEvent( 'row-reorder', eventArgs );
539
-
540
- var update = function () {
541
- if ( that.c.update ) {
542
- for ( i=0, ien=fullDiff.length ; i<ien ; i++ ) {
543
- var row = dt.row( fullDiff[i].node );
544
- var rowData = row.data();
545
-
546
- setDataFn( rowData, fullDiff[i].newData );
547
-
548
- // Invalidate the cell that has the same data source as the dataSrc
549
- dt.columns().every( function () {
550
- if ( this.dataSrc() === dataSrc ) {
551
- dt.cell( fullDiff[i].node, this.index() ).invalidate( 'data' );
552
- }
553
- } );
554
- }
555
-
556
- // Trigger row reordered event
557
- that._emitEvent( 'row-reordered', eventArgs );
558
-
559
- dt.draw( false );
560
- }
561
- };
562
-
563
- // Editor interface
564
- if ( this.c.editor ) {
565
- // Disable user interaction while Editor is submitting
566
- this.c.enable = false;
567
-
568
- this.c.editor
569
- .edit(
570
- diffNodes,
571
- false,
572
- $.extend( {submit: 'changed'}, this.c.formOptions )
573
- )
574
- .multiSet( dataSrc, idDiff )
575
- .one( 'preSubmitCancelled.rowReorder', function () {
576
- that.c.enable = true;
577
- that.c.editor.off( '.rowReorder' );
578
- dt.draw( false );
579
- } )
580
- .one( 'submitUnsuccessful.rowReorder', function () {
581
- dt.draw( false );
582
- } )
583
- .one( 'submitSuccess.rowReorder', function () {
584
- update();
585
- } )
586
- .one( 'submitComplete', function () {
587
- that.c.enable = true;
588
- that.c.editor.off( '.rowReorder' );
589
- } )
590
- .submit();
591
- }
592
- else {
593
- update();
594
- }
595
- },
596
-
597
-
598
- /**
599
- * Move the window and DataTables scrolling during a drag to scroll new
600
- * content into view.
601
- *
602
- * This matches the `_shiftScroll` method used in AutoFill, but only
603
- * horizontal scrolling is considered here.
604
- *
605
- * @param {object} e Mouse move event object
606
- * @private
607
- */
608
- _shiftScroll: function ( e )
609
- {
610
- var that = this;
611
- var dt = this.s.dt;
612
- var scroll = this.s.scroll;
613
- var runInterval = false;
614
- var scrollSpeed = 5;
615
- var buffer = 65;
616
- var
617
- windowY = e.pageY - document.body.scrollTop,
618
- windowVert,
619
- dtVert;
620
-
621
- // Window calculations - based on the mouse position in the window,
622
- // regardless of scrolling
623
- if ( windowY < buffer ) {
624
- windowVert = scrollSpeed * -1;
625
- }
626
- else if ( windowY > scroll.windowHeight - buffer ) {
627
- windowVert = scrollSpeed;
628
- }
629
-
630
- // DataTables scrolling calculations - based on the table's position in
631
- // the document and the mouse position on the page
632
- if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
633
- dtVert = scrollSpeed * -1;
634
- }
635
- else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
636
- dtVert = scrollSpeed;
637
- }
638
-
639
- // This is where it gets interesting. We want to continue scrolling
640
- // without requiring a mouse move, so we need an interval to be
641
- // triggered. The interval should continue until it is no longer needed,
642
- // but it must also use the latest scroll commands (for example consider
643
- // that the mouse might move from scrolling up to scrolling left, all
644
- // with the same interval running. We use the `scroll` object to "pass"
645
- // this information to the interval. Can't use local variables as they
646
- // wouldn't be the ones that are used by an already existing interval!
647
- if ( windowVert || dtVert ) {
648
- scroll.windowVert = windowVert;
649
- scroll.dtVert = dtVert;
650
- runInterval = true;
651
- }
652
- else if ( this.s.scrollInterval ) {
653
- // Don't need to scroll - remove any existing timer
654
- clearInterval( this.s.scrollInterval );
655
- this.s.scrollInterval = null;
656
- }
657
-
658
- // If we need to run the interval to scroll and there is no existing
659
- // interval (if there is an existing one, it will continue to run)
660
- if ( ! this.s.scrollInterval && runInterval ) {
661
- this.s.scrollInterval = setInterval( function () {
662
- // Don't need to worry about setting scroll <0 or beyond the
663
- // scroll bound as the browser will just reject that.
664
- if ( scroll.windowVert ) {
665
- document.body.scrollTop += scroll.windowVert;
666
- }
667
-
668
- // DataTables scrolling
669
- if ( scroll.dtVert ) {
670
- var scroller = that.dom.dtScroll[0];
671
-
672
- if ( scroll.dtVert ) {
673
- scroller.scrollTop += scroll.dtVert;
674
- }
675
- }
676
- }, 20 );
677
- }
678
- }
679
- } );
112
+ /** @type {array} Pixel positions for row insertion calculation */
113
+ middles: null,
680
114
 
115
+ /** @type {Object} Cached dimension information for use in the mouse move event handler */
116
+ scroll: {},
681
117
 
118
+ /** @type {integer} Interval object used for smooth scrolling */
119
+ scrollInterval: null,
120
+
121
+ /** @type {function} Data set function */
122
+ setDataFn: DataTable.ext.oApi._fnSetObjectDataFn(this.c.dataSrc),
123
+
124
+ /** @type {Object} Mouse down information */
125
+ start: {
126
+ top: 0,
127
+ left: 0,
128
+ offsetTop: 0,
129
+ offsetLeft: 0,
130
+ nodes: [],
131
+ rowIndex: 0
132
+ },
133
+
134
+ /** @type {integer} Window height cached value */
135
+ windowHeight: 0,
136
+
137
+ /** @type {integer} Document outer height cached value */
138
+ documentOuterHeight: 0,
139
+
140
+ /** @type {integer} DOM clone outer height cached value */
141
+ domCloneOuterHeight: 0,
142
+
143
+ /** @type {integer} Flag used for signing if the drop is enabled or not */
144
+ dropAllowed: true
145
+ };
146
+
147
+ // DOM items
148
+ this.dom = {
149
+ /** @type {jQuery} Cloned row being moved around */
150
+ clone: null,
151
+ cloneParent: null,
152
+
153
+ /** @type {jQuery} DataTables scrolling container */
154
+ dtScroll: $('div.dataTables_scrollBody', this.s.dt.table().container())
155
+ };
156
+
157
+ // Check if row reorder has already been initialised on this table
158
+ var settings = this.s.dt.settings()[0];
159
+ var exisiting = settings.rowreorder;
160
+
161
+ if (exisiting) {
162
+ return exisiting;
163
+ }
164
+
165
+ if (!this.dom.dtScroll.length) {
166
+ this.dom.dtScroll = $(this.s.dt.table().container(), 'tbody');
167
+ }
168
+
169
+ settings.rowreorder = this;
170
+ this._constructor();
171
+ };
172
+
173
+ $.extend(RowReorder.prototype, {
174
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
175
+ * Constructor
176
+ */
177
+
178
+ /**
179
+ * Initialise the RowReorder instance
180
+ *
181
+ * @private
182
+ */
183
+ _constructor: function () {
184
+ var that = this;
185
+ var dt = this.s.dt;
186
+ var table = $(dt.table().node());
187
+
188
+ // Need to be able to calculate the row positions relative to the table
189
+ if (table.css('position') === 'static') {
190
+ table.css('position', 'relative');
191
+ }
192
+
193
+ // listen for mouse down on the target column - we have to implement
194
+ // this rather than using HTML5 drag and drop as drag and drop doesn't
195
+ // appear to work on table rows at this time. Also mobile browsers are
196
+ // not supported.
197
+ // Use `table().container()` rather than just the table node for IE8 -
198
+ // otherwise it only works once...
199
+ $(dt.table().container()).on(
200
+ 'mousedown.rowReorder touchstart.rowReorder',
201
+ this.c.selector,
202
+ function (e) {
203
+ if (!that.c.enable) {
204
+ return;
205
+ }
206
+
207
+ // Ignore excluded children of the selector
208
+ if ($(e.target).is(that.c.excludedChildren)) {
209
+ return true;
210
+ }
211
+
212
+ var tr = $(this).closest('tr');
213
+ var row = dt.row(tr);
214
+
215
+ // Double check that it is a DataTable row
216
+ if (row.any()) {
217
+ that._emitEvent('pre-row-reorder', {
218
+ node: row.node(),
219
+ index: row.index()
220
+ });
221
+
222
+ that._mouseDown(e, tr);
223
+ return false;
224
+ }
225
+ }
226
+ );
227
+
228
+ dt.on('destroy.rowReorder', function () {
229
+ $(dt.table().container()).off('.rowReorder');
230
+ dt.off('.rowReorder');
231
+ });
232
+
233
+ this._keyup = this._keyup.bind(this);
234
+ },
235
+
236
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
237
+ * Private methods
238
+ */
239
+
240
+ /**
241
+ * Cache the measurements that RowReorder needs in the mouse move handler
242
+ * to attempt to speed things up, rather than reading from the DOM.
243
+ *
244
+ * @private
245
+ */
246
+ _cachePositions: function () {
247
+ var dt = this.s.dt;
248
+
249
+ // Frustratingly, if we add `position:relative` to the tbody, the
250
+ // position is still relatively to the parent. So we need to adjust
251
+ // for that
252
+ var headerHeight = $(dt.table().node()).find('thead').outerHeight();
253
+
254
+ // Need to pass the nodes through jQuery to get them in document order,
255
+ // not what DataTables thinks it is, since we have been altering the
256
+ // order
257
+ var nodes = $.unique(dt.rows({ page: 'current' }).nodes().toArray());
258
+ var middles = $.map(nodes, function (node, i) {
259
+ var top = $(node).position().top - headerHeight;
260
+
261
+ return (top + top + $(node).outerHeight()) / 2;
262
+ });
263
+
264
+ this.s.middles = middles;
265
+ this.s.bodyTop = $(dt.table().body()).offset().top;
266
+ this.s.windowHeight = $(window).height();
267
+ this.s.documentOuterHeight = $(document).outerHeight();
268
+ this.s.bodyArea = this._calcBodyArea();
269
+ },
270
+
271
+ /**
272
+ * Clone a row so it can be floated around the screen
273
+ *
274
+ * @param {jQuery} target Node to be cloned
275
+ * @private
276
+ */
277
+ _clone: function (target) {
278
+ var dt = this.s.dt;
279
+ var clone = $(dt.table().node().cloneNode(false))
280
+ .addClass('dt-rowReorder-float')
281
+ .append('<tbody/>')
282
+ .append(target.clone(false));
283
+
284
+ // Match the table and column widths - read all sizes before setting
285
+ // to reduce reflows
286
+ var tableWidth = target.outerWidth();
287
+ var tableHeight = target.outerHeight();
288
+ var scrollBody = $($(this.s.dt.table().node()).parent());
289
+ var scrollWidth = scrollBody.width();
290
+ var scrollLeft = scrollBody.scrollLeft();
291
+ var sizes = target.children().map(function () {
292
+ return $(this).width();
293
+ });
294
+
295
+ clone
296
+ .width(tableWidth)
297
+ .height(tableHeight)
298
+ .find('tr')
299
+ .children()
300
+ .each(function (i) {
301
+ this.style.width = sizes[i] + 'px';
302
+ });
303
+
304
+ var cloneParent = $('<div>')
305
+ .addClass('dt-rowReorder-float-parent')
306
+ .width(scrollWidth)
307
+ .append(clone)
308
+ .appendTo('body')
309
+ .scrollLeft(scrollLeft);
310
+
311
+ // Insert into the document to have it floating around
312
+
313
+ this.dom.clone = clone;
314
+ this.dom.cloneParent = cloneParent;
315
+ this.s.domCloneOuterHeight = clone.outerHeight();
316
+ },
317
+
318
+ /**
319
+ * Update the cloned item's position in the document
320
+ *
321
+ * @param {object} e Event giving the mouse's position
322
+ * @private
323
+ */
324
+ _clonePosition: function (e) {
325
+ var start = this.s.start;
326
+ var topDiff = this._eventToPage(e, 'Y') - start.top;
327
+ var leftDiff = this._eventToPage(e, 'X') - start.left;
328
+ var snap = this.c.snapX;
329
+ var left;
330
+ var top = topDiff + start.offsetTop;
331
+
332
+ if (snap === true) {
333
+ left = start.offsetLeft;
334
+ }
335
+ else if (typeof snap === 'number') {
336
+ left = start.offsetLeft + snap;
337
+ }
338
+ else {
339
+ left = leftDiff + start.offsetLeft + this.dom.cloneParent.scrollLeft();
340
+ }
341
+
342
+ if (top < 0) {
343
+ top = 0;
344
+ }
345
+ else if (top + this.s.domCloneOuterHeight > this.s.documentOuterHeight) {
346
+ top = this.s.documentOuterHeight - this.s.domCloneOuterHeight;
347
+ }
348
+
349
+ this.dom.cloneParent.css({
350
+ top: top,
351
+ left: left
352
+ });
353
+ },
354
+
355
+ /**
356
+ * Emit an event on the DataTable for listeners
357
+ *
358
+ * @param {string} name Event name
359
+ * @param {array} args Event arguments
360
+ * @private
361
+ */
362
+ _emitEvent: function ( name, args )
363
+ {
364
+ var ret;
365
+
366
+ this.s.dt.iterator( 'table', function ( ctx, i ) {
367
+ var innerRet = $(ctx.nTable).triggerHandler( name+'.dt', args );
368
+
369
+ if (innerRet !== undefined) {
370
+ ret = innerRet;
371
+ }
372
+ } );
373
+
374
+ return ret;
375
+ },
376
+
377
+ /**
378
+ * Get pageX/Y position from an event, regardless of if it is a mouse or
379
+ * touch event.
380
+ *
381
+ * @param {object} e Event
382
+ * @param {string} pos X or Y (must be a capital)
383
+ * @private
384
+ */
385
+ _eventToPage: function (e, pos) {
386
+ if (e.type.indexOf('touch') !== -1) {
387
+ return e.originalEvent.touches[0]['page' + pos];
388
+ }
389
+
390
+ return e['page' + pos];
391
+ },
392
+
393
+ /**
394
+ * Mouse down event handler. Read initial positions and add event handlers
395
+ * for the move.
396
+ *
397
+ * @param {object} e Mouse event
398
+ * @param {jQuery} target TR element that is to be moved
399
+ * @private
400
+ */
401
+ _mouseDown: function (e, target) {
402
+ var that = this;
403
+ var dt = this.s.dt;
404
+ var start = this.s.start;
405
+ var cancelable = this.c.cancelable;
406
+
407
+ var offset = target.offset();
408
+ start.top = this._eventToPage(e, 'Y');
409
+ start.left = this._eventToPage(e, 'X');
410
+ start.offsetTop = offset.top;
411
+ start.offsetLeft = offset.left;
412
+ start.nodes = $.unique(dt.rows({ page: 'current' }).nodes().toArray());
413
+
414
+ this._cachePositions();
415
+ this._clone(target);
416
+ this._clonePosition(e);
417
+
418
+ var bodyY = this._eventToPage(e, 'Y') - this.s.bodyTop;
419
+ start.rowIndex = this._calcRowIndexByPos(bodyY);
420
+
421
+ this.dom.target = target;
422
+ target.addClass('dt-rowReorder-moving');
423
+
424
+ $(document)
425
+ .on('mouseup.rowReorder touchend.rowReorder', function (e) {
426
+ that._mouseUp(e);
427
+ })
428
+ .on('mousemove.rowReorder touchmove.rowReorder', function (e) {
429
+ that._mouseMove(e);
430
+ });
431
+
432
+ // Check if window is x-scrolling - if not, disable it for the duration
433
+ // of the drag
434
+ if ($(window).width() === $(document).width()) {
435
+ $(document.body).addClass('dt-rowReorder-noOverflow');
436
+ }
437
+
438
+ // Cache scrolling information so mouse move doesn't need to read.
439
+ // This assumes that the window and DT scroller will not change size
440
+ // during an row drag, which I think is a fair assumption
441
+ var scrollWrapper = this.dom.dtScroll;
442
+ this.s.scroll = {
443
+ windowHeight: $(window).height(),
444
+ windowWidth: $(window).width(),
445
+ dtTop: scrollWrapper.length ? scrollWrapper.offset().top : null,
446
+ dtLeft: scrollWrapper.length ? scrollWrapper.offset().left : null,
447
+ dtHeight: scrollWrapper.length ? scrollWrapper.outerHeight() : null,
448
+ dtWidth: scrollWrapper.length ? scrollWrapper.outerWidth() : null
449
+ };
450
+
451
+ // Add keyup handler if dragging is cancelable
452
+ if (cancelable) {
453
+ $(document).on('keyup', this._keyup);
454
+ }
455
+ },
456
+
457
+ /**
458
+ * Mouse move event handler - move the cloned row and shuffle the table's
459
+ * rows if required.
460
+ *
461
+ * @param {object} e Mouse event
462
+ * @private
463
+ */
464
+ _mouseMove: function (e) {
465
+ this._clonePosition(e);
466
+
467
+ var start = this.s.start;
468
+ var cancelable = this.c.cancelable;
469
+
470
+ if (cancelable) {
471
+ var bodyArea = this.s.bodyArea;
472
+ var cloneArea = this._calcCloneParentArea();
473
+ this.s.dropAllowed = this._rectanglesIntersect(bodyArea, cloneArea);
474
+
475
+ this.s.dropAllowed
476
+ ? $(this.dom.cloneParent).removeClass('drop-not-allowed')
477
+ : $(this.dom.cloneParent).addClass('drop-not-allowed');
478
+ }
479
+
480
+ // Transform the mouse position into a position in the table's body
481
+ var bodyY = this._eventToPage(e, 'Y') - this.s.bodyTop;
482
+ var middles = this.s.middles;
483
+ var insertPoint = null;
484
+
485
+ // Determine where the row should be inserted based on the mouse
486
+ // position
487
+ for (var i = 0, ien = middles.length; i < ien; i++) {
488
+ if (bodyY < middles[i]) {
489
+ insertPoint = i;
490
+ break;
491
+ }
492
+ }
493
+
494
+ if (insertPoint === null) {
495
+ insertPoint = middles.length;
496
+ }
497
+
498
+ if (cancelable) {
499
+ if (!this.s.dropAllowed) {
500
+ // Move the row back to its original position becasuse the drop is not allowed
501
+ insertPoint =
502
+ start.rowIndex > this.s.lastInsert ? start.rowIndex + 1 : start.rowIndex;
503
+ }
504
+
505
+ this.dom.target.toggleClass('dt-rowReorder-moving', this.s.dropAllowed);
506
+ }
507
+
508
+ this._moveTargetIntoPosition(insertPoint);
509
+
510
+ this._shiftScroll(e);
511
+ },
512
+
513
+ /**
514
+ * Mouse up event handler - release the event handlers and perform the
515
+ * table updates
516
+ *
517
+ * @param {object} e Mouse event
518
+ * @private
519
+ */
520
+ _mouseUp: function (e) {
521
+ var that = this;
522
+ var dt = this.s.dt;
523
+ var i, ien;
524
+ var dataSrc = this.c.dataSrc;
525
+ var dropAllowed = this.s.dropAllowed;
526
+
527
+ if (!dropAllowed) {
528
+ that._cancel();
529
+ return;
530
+ }
531
+
532
+ // Calculate the difference
533
+ var startNodes = this.s.start.nodes;
534
+ var endNodes = $.unique(dt.rows({ page: 'current' }).nodes().toArray());
535
+ var idDiff = {};
536
+ var fullDiff = [];
537
+ var diffNodes = [];
538
+ var getDataFn = this.s.getDataFn;
539
+ var setDataFn = this.s.setDataFn;
540
+
541
+ for (i = 0, ien = startNodes.length; i < ien; i++) {
542
+ if (startNodes[i] !== endNodes[i]) {
543
+ var id = dt.row(endNodes[i]).id();
544
+ var endRowData = dt.row(endNodes[i]).data();
545
+ var startRowData = dt.row(startNodes[i]).data();
546
+
547
+ if (id) {
548
+ idDiff[id] = getDataFn(startRowData);
549
+ }
550
+
551
+ fullDiff.push({
552
+ node: endNodes[i],
553
+ oldData: getDataFn(endRowData),
554
+ newData: getDataFn(startRowData),
555
+ newPosition: i,
556
+ oldPosition: $.inArray(endNodes[i], startNodes)
557
+ });
558
+
559
+ diffNodes.push(endNodes[i]);
560
+ }
561
+ }
562
+
563
+ // Create event args
564
+ var eventArgs = [
565
+ fullDiff,
566
+ {
567
+ dataSrc: dataSrc,
568
+ nodes: diffNodes,
569
+ values: idDiff,
570
+ triggerRow: dt.row(this.dom.target),
571
+ originalEvent: e
572
+ }
573
+ ];
574
+
575
+ // Emit event
576
+ var eventResult = this._emitEvent( 'row-reorder', eventArgs );
577
+
578
+ if (eventResult === false) {
579
+ that._cancel();
580
+ return;
581
+ }
582
+
583
+ // Remove cloned elements, handlers, etc
584
+ this._cleanupDragging();
585
+
586
+ var update = function () {
587
+ if (that.c.update) {
588
+ for (i = 0, ien = fullDiff.length; i < ien; i++) {
589
+ var row = dt.row(fullDiff[i].node);
590
+ var rowData = row.data();
591
+
592
+ setDataFn(rowData, fullDiff[i].newData);
593
+
594
+ // Invalidate the cell that has the same data source as the dataSrc
595
+ dt.columns().every(function () {
596
+ if (this.dataSrc() === dataSrc) {
597
+ dt.cell(fullDiff[i].node, this.index()).invalidate('data');
598
+ }
599
+ });
600
+ }
601
+
602
+ // Trigger row reordered event
603
+ that._emitEvent('row-reordered', eventArgs);
604
+
605
+ dt.draw(false);
606
+ }
607
+ };
608
+
609
+ // Editor interface
610
+ if (this.c.editor) {
611
+ // Disable user interaction while Editor is submitting
612
+ this.c.enable = false;
613
+
614
+ this.c.editor
615
+ .edit(diffNodes, false, $.extend({ submit: 'changed' }, this.c.formOptions))
616
+ .multiSet(dataSrc, idDiff)
617
+ .one('preSubmitCancelled.rowReorder', function () {
618
+ that.c.enable = true;
619
+ that.c.editor.off('.rowReorder');
620
+ dt.draw(false);
621
+ })
622
+ .one('submitUnsuccessful.rowReorder', function () {
623
+ dt.draw(false);
624
+ })
625
+ .one('submitSuccess.rowReorder', function () {
626
+ update();
627
+ })
628
+ .one('submitComplete', function () {
629
+ that.c.enable = true;
630
+ that.c.editor.off('.rowReorder');
631
+ })
632
+ .submit();
633
+ }
634
+ else {
635
+ update();
636
+ }
637
+ },
638
+
639
+ /**
640
+ * Moves the current target into the given position within the table
641
+ * and caches the new positions
642
+ *
643
+ * @param {integer} insertPoint Position
644
+ * @private
645
+ */
646
+ _moveTargetIntoPosition: function (insertPoint) {
647
+ var dt = this.s.dt;
648
+
649
+ // Perform the DOM shuffle if it has changed from last time
650
+ if (this.s.lastInsert === null || this.s.lastInsert !== insertPoint) {
651
+ var nodes = $.unique(dt.rows({ page: 'current' }).nodes().toArray());
652
+ var insertPlacement = '';
653
+
654
+ if (insertPoint > this.s.lastInsert) {
655
+ this.dom.target.insertAfter(nodes[insertPoint - 1]);
656
+ insertPlacement = 'after';
657
+ }
658
+ else {
659
+ this.dom.target.insertBefore(nodes[insertPoint]);
660
+ insertPlacement = 'before';
661
+ }
662
+
663
+ this._cachePositions();
664
+
665
+ this.s.lastInsert = insertPoint;
666
+
667
+ this._emitEvent('row-reorder-changed', {
668
+ insertPlacement,
669
+ insertPoint,
670
+ row: dt.row(this.dom.target)
671
+ });
672
+ }
673
+ },
674
+
675
+ /**
676
+ * Removes the cloned elements, event handlers, scrolling intervals, etc
677
+ *
678
+ * @private
679
+ */
680
+ _cleanupDragging: function () {
681
+ var cancelable = this.c.cancelable;
682
+
683
+ this.dom.clone.remove();
684
+ this.dom.cloneParent.remove();
685
+ this.dom.clone = null;
686
+ this.dom.cloneParent = null;
687
+
688
+ this.dom.target.removeClass('dt-rowReorder-moving');
689
+ //this.dom.target = null;
690
+
691
+ $(document).off('.rowReorder');
692
+ $(document.body).removeClass('dt-rowReorder-noOverflow');
693
+
694
+ clearInterval(this.s.scrollInterval);
695
+ this.s.scrollInterval = null;
696
+
697
+ if (cancelable) {
698
+ $(document).off('keyup', this._keyup);
699
+ }
700
+ },
701
+
702
+ /**
703
+ * Move the window and DataTables scrolling during a drag to scroll new
704
+ * content into view.
705
+ *
706
+ * This matches the `_shiftScroll` method used in AutoFill, but only
707
+ * horizontal scrolling is considered here.
708
+ *
709
+ * @param {object} e Mouse move event object
710
+ * @private
711
+ */
712
+ _shiftScroll: function (e) {
713
+ var that = this;
714
+ var dt = this.s.dt;
715
+ var scroll = this.s.scroll;
716
+ var runInterval = false;
717
+ var scrollSpeed = 5;
718
+ var buffer = 65;
719
+ var windowY = e.pageY - document.body.scrollTop,
720
+ windowVert,
721
+ dtVert;
722
+
723
+ // Window calculations - based on the mouse position in the window,
724
+ // regardless of scrolling
725
+ if (windowY < $(window).scrollTop() + buffer) {
726
+ windowVert = scrollSpeed * -1;
727
+ }
728
+ else if (windowY > scroll.windowHeight + $(window).scrollTop() - buffer) {
729
+ windowVert = scrollSpeed;
730
+ }
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 && e.pageY < scroll.dtTop + buffer) {
735
+ dtVert = scrollSpeed * -1;
736
+ }
737
+ else if (scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer) {
738
+ dtVert = scrollSpeed;
739
+ }
740
+
741
+ // This is where it gets interesting. We want to continue scrolling
742
+ // without requiring a mouse move, so we need an interval to be
743
+ // triggered. The interval should continue until it is no longer needed,
744
+ // but it must also use the latest scroll commands (for example consider
745
+ // that the mouse might move from scrolling up to scrolling left, all
746
+ // with the same interval running. We use the `scroll` object to "pass"
747
+ // this information to the interval. Can't use local variables as they
748
+ // wouldn't be the ones that are used by an already existing interval!
749
+ if (windowVert || dtVert) {
750
+ scroll.windowVert = windowVert;
751
+ scroll.dtVert = dtVert;
752
+ runInterval = true;
753
+ }
754
+ else if (this.s.scrollInterval) {
755
+ // Don't need to scroll - remove any existing timer
756
+ clearInterval(this.s.scrollInterval);
757
+ this.s.scrollInterval = null;
758
+ }
759
+
760
+ // If we need to run the interval to scroll and there is no existing
761
+ // interval (if there is an existing one, it will continue to run)
762
+ if (!this.s.scrollInterval && runInterval) {
763
+ this.s.scrollInterval = setInterval(function () {
764
+ // Don't need to worry about setting scroll <0 or beyond the
765
+ // scroll bound as the browser will just reject that.
766
+ if (scroll.windowVert) {
767
+ var top = $(document).scrollTop();
768
+ $(document).scrollTop(top + scroll.windowVert);
769
+
770
+ if (top !== $(document).scrollTop()) {
771
+ var move = parseFloat(that.dom.cloneParent.css('top'));
772
+ that.dom.cloneParent.css('top', move + scroll.windowVert);
773
+ }
774
+ }
775
+
776
+ // DataTables scrolling
777
+ if (scroll.dtVert) {
778
+ var scroller = that.dom.dtScroll[0];
779
+
780
+ if (scroll.dtVert) {
781
+ scroller.scrollTop += scroll.dtVert;
782
+ }
783
+ }
784
+ }, 20);
785
+ }
786
+ },
787
+
788
+ /**
789
+ * Calculates the current area of the table body and returns it as a rectangle
790
+ *
791
+ * @private
792
+ */
793
+ _calcBodyArea: function (e) {
794
+ var dt = this.s.dt;
795
+ var offset = $(dt.table().body()).offset();
796
+ var area = {
797
+ left: offset.left,
798
+ top: offset.top,
799
+ right: offset.left + $(dt.table().body()).width(),
800
+ bottom: offset.top + $(dt.table().body()).height()
801
+ };
802
+
803
+ return area;
804
+ },
805
+
806
+ /**
807
+ * Calculates the current area of the cloned parent element and returns it as a rectangle
808
+ *
809
+ * @private
810
+ */
811
+ _calcCloneParentArea: function (e) {
812
+ var dt = this.s.dt;
813
+ var offset = $(this.dom.cloneParent).offset();
814
+ var area = {
815
+ left: offset.left,
816
+ top: offset.top,
817
+ right: offset.left + $(this.dom.cloneParent).width(),
818
+ bottom: offset.top + $(this.dom.cloneParent).height()
819
+ };
820
+
821
+ return area;
822
+ },
823
+
824
+ /**
825
+ * Returns whether the given reactangles intersect or not
826
+ *
827
+ * @private
828
+ */
829
+ _rectanglesIntersect: function (a, b) {
830
+ var noOverlap =
831
+ a.left >= b.right || b.left >= a.right || a.top >= b.bottom || b.top >= a.bottom;
832
+
833
+ return !noOverlap;
834
+ },
835
+
836
+ /**
837
+ * Calculates the index of the row which lays under the given Y position or
838
+ * returns -1 if no such row
839
+ *
840
+ * @param {integer} insertPoint Position
841
+ * @private
842
+ */
843
+ _calcRowIndexByPos: function (bodyY) {
844
+ // Determine where the row is located based on the mouse
845
+ // position
846
+
847
+ var dt = this.s.dt;
848
+ var nodes = $.unique(dt.rows({ page: 'current' }).nodes().toArray());
849
+ var rowIndex = -1;
850
+ var headerHeight = $(dt.table().node()).find('thead').outerHeight();
851
+
852
+ $.each(nodes, function (i, node) {
853
+ var top = $(node).position().top - headerHeight;
854
+ var bottom = top + $(node).outerHeight();
855
+
856
+ if (bodyY >= top && bodyY <= bottom) {
857
+ rowIndex = i;
858
+ }
859
+ });
860
+
861
+ return rowIndex;
862
+ },
863
+
864
+ /**
865
+ * Handles key up events and cancels the dragging if ESC key is pressed
866
+ *
867
+ * @param {object} e Mouse move event object
868
+ * @private
869
+ */
870
+ _keyup: function (e) {
871
+ var cancelable = this.c.cancelable;
872
+
873
+ if (cancelable && e.which === 27) {
874
+ // ESC key is up
875
+ e.preventDefault();
876
+ this._cancel();
877
+ }
878
+ },
879
+
880
+ /**
881
+ * Cancels the dragging, moves target back into its original position
882
+ * and cleans up the dragging
883
+ *
884
+ * @param {object} e Mouse move event object
885
+ * @private
886
+ */
887
+ _cancel: function () {
888
+ var start = this.s.start;
889
+ var insertPoint = start.rowIndex > this.s.lastInsert ? start.rowIndex + 1 : start.rowIndex;
890
+
891
+ this._moveTargetIntoPosition(insertPoint);
892
+
893
+ this._cleanupDragging();
894
+
895
+ // Emit event
896
+ this._emitEvent('row-reorder-canceled', [this.s.start.rowIndex]);
897
+ }
898
+ });
682
899
 
683
900
  /**
684
901
  * RowReorder default settings for initialisation
@@ -688,99 +905,104 @@ $.extend( RowReorder.prototype, {
688
905
  * @static
689
906
  */
690
907
  RowReorder.defaults = {
691
- /**
692
- * Data point in the host row's data source object for where to get and set
693
- * the data to reorder. This will normally also be the sorting column.
694
- *
695
- * @type {Number}
696
- */
697
- dataSrc: 0,
698
-
699
- /**
700
- * Editor instance that will be used to perform the update
701
- *
702
- * @type {DataTable.Editor}
703
- */
704
- editor: null,
705
-
706
- /**
707
- * Enable / disable RowReorder's user interaction
708
- * @type {Boolean}
709
- */
710
- enable: true,
711
-
712
- /**
713
- * Form options to pass to Editor when submitting a change in the row order.
714
- * See the Editor `from-options` object for details of the options
715
- * available.
716
- * @type {Object}
717
- */
718
- formOptions: {},
719
-
720
- /**
721
- * Drag handle selector. This defines the element that when dragged will
722
- * reorder a row.
723
- *
724
- * @type {String}
725
- */
726
- selector: 'td:first-child',
727
-
728
- /**
729
- * Optionally lock the dragged row's x-position. This can be `true` to
730
- * fix the position match the host table's, `false` to allow free movement
731
- * of the row, or a number to define an offset from the host table.
732
- *
733
- * @type {Boolean|number}
734
- */
735
- snapX: false,
736
-
737
- /**
738
- * Update the table's data on drop
739
- *
740
- * @type {Boolean}
741
- */
742
- update: true,
743
-
744
- /**
745
- * Selector for children of the drag handle selector that mouseDown events
746
- * will be passed through to and drag will not activate
747
- *
748
- * @type {String}
749
- */
750
- excludedChildren: 'a'
908
+ /**
909
+ * Data point in the host row's data source object for where to get and set
910
+ * the data to reorder. This will normally also be the sorting column.
911
+ *
912
+ * @type {Number}
913
+ */
914
+ dataSrc: 0,
915
+
916
+ /**
917
+ * Editor instance that will be used to perform the update
918
+ *
919
+ * @type {DataTable.Editor}
920
+ */
921
+ editor: null,
922
+
923
+ /**
924
+ * Enable / disable RowReorder's user interaction
925
+ * @type {Boolean}
926
+ */
927
+ enable: true,
928
+
929
+ /**
930
+ * Form options to pass to Editor when submitting a change in the row order.
931
+ * See the Editor `from-options` object for details of the options
932
+ * available.
933
+ * @type {Object}
934
+ */
935
+ formOptions: {},
936
+
937
+ /**
938
+ * Drag handle selector. This defines the element that when dragged will
939
+ * reorder a row.
940
+ *
941
+ * @type {String}
942
+ */
943
+ selector: 'td:first-child',
944
+
945
+ /**
946
+ * Optionally lock the dragged row's x-position. This can be `true` to
947
+ * fix the position match the host table's, `false` to allow free movement
948
+ * of the row, or a number to define an offset from the host table.
949
+ *
950
+ * @type {Boolean|number}
951
+ */
952
+ snapX: false,
953
+
954
+ /**
955
+ * Update the table's data on drop
956
+ *
957
+ * @type {Boolean}
958
+ */
959
+ update: true,
960
+
961
+ /**
962
+ * Selector for children of the drag handle selector that mouseDown events
963
+ * will be passed through to and drag will not activate
964
+ *
965
+ * @type {String}
966
+ */
967
+ excludedChildren: 'a',
968
+
969
+ /**
970
+ * Enable / disable the canceling of the drag & drop interaction
971
+ *
972
+ * @type {Boolean}
973
+ */
974
+ cancelable: false
751
975
  };
752
976
 
753
-
754
977
  /*
755
978
  * API
756
979
  */
757
980
  var Api = $.fn.dataTable.Api;
758
981
 
759
982
  // Doesn't do anything - work around for a bug in DT... Not documented
760
- Api.register( 'rowReorder()', function () {
761
- return this;
762
- } );
763
-
764
- Api.register( 'rowReorder.enable()', function ( toggle ) {
765
- if ( toggle === undefined ) {
766
- toggle = true;
767
- }
768
-
769
- return this.iterator( 'table', function ( ctx ) {
770
- if ( ctx.rowreorder ) {
771
- ctx.rowreorder.c.enable = toggle;
772
- }
773
- } );
774
- } );
775
-
776
- Api.register( 'rowReorder.disable()', function () {
777
- return this.iterator( 'table', function ( ctx ) {
778
- if ( ctx.rowreorder ) {
779
- ctx.rowreorder.c.enable = false;
780
- }
781
- } );
782
- } );
783
-
983
+ Api.register('rowReorder()', function () {
984
+ return this;
985
+ });
986
+
987
+ Api.register('rowReorder.enable()', function (toggle) {
988
+ if (toggle === undefined) {
989
+ toggle = true;
990
+ }
991
+
992
+ return this.iterator('table', function (ctx) {
993
+ if (ctx.rowreorder) {
994
+ ctx.rowreorder.c.enable = toggle;
995
+ }
996
+ });
997
+ });
998
+
999
+ Api.register('rowReorder.disable()', function () {
1000
+ return this.iterator('table', function (ctx) {
1001
+ if (ctx.rowreorder) {
1002
+ ctx.rowreorder.c.enable = false;
1003
+ }
1004
+ });
1005
+ });
784
1006
 
785
1007
  /**
786
1008
  * Version information
@@ -788,31 +1010,30 @@ Api.register( 'rowReorder.disable()', function () {
788
1010
  * @name RowReorder.version
789
1011
  * @static
790
1012
  */
791
- RowReorder.version = '1.2.4';
792
-
1013
+ RowReorder.version = '1.4.1';
793
1014
 
794
1015
  $.fn.dataTable.RowReorder = RowReorder;
795
1016
  $.fn.DataTable.RowReorder = RowReorder;
796
1017
 
797
1018
  // Attach a listener to the document which listens for DataTables initialisation
798
1019
  // events so we can automatically initialise
799
- $(document).on( 'init.dt.dtr', function (e, settings, json) {
800
- if ( e.namespace !== 'dt' ) {
801
- return;
802
- }
1020
+ $(document).on('init.dt.dtr', function (e, settings, json) {
1021
+ if (e.namespace !== 'dt') {
1022
+ return;
1023
+ }
803
1024
 
804
- var init = settings.oInit.rowReorder;
805
- var defaults = DataTable.defaults.rowReorder;
1025
+ var init = settings.oInit.rowReorder;
1026
+ var defaults = DataTable.defaults.rowReorder;
806
1027
 
807
- if ( init || defaults ) {
808
- var opts = $.extend( {}, init, defaults );
1028
+ if (init || defaults) {
1029
+ var opts = $.extend({}, init, defaults);
809
1030
 
810
- if ( init !== false ) {
811
- new RowReorder( settings, opts );
812
- }
813
- }
814
- } );
1031
+ if (init !== false) {
1032
+ new RowReorder(settings, opts);
1033
+ }
1034
+ }
1035
+ });
815
1036
 
816
1037
 
817
- return RowReorder;
1038
+ return DataTable;
818
1039
  }));