effective_datatables 4.17.4 → 4.18.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }));