source: public/vendors/dataTable/RowReorder-1.2.6/js/dataTables.rowReorder.js@ 495dd28

develop
Last change on this file since 495dd28 was c6b84df, checked in by beratkjufliju <kufliju@…>, 3 years ago

added fileTypes controller, notifications, excel export, edited views

  • Property mode set to 100644
File size: 20.7 KB
Line 
1/*! RowReorder 1.2.6
2 * 2015-2019 SpryMedia Ltd - datatables.net/license
3 */
4
5/**
6 * @summary RowReorder
7 * @description Row reordering extension for DataTables
8 * @version 1.2.6
9 * @file dataTables.rowReorder.js
10 * @author SpryMedia Ltd (www.sprymedia.co.uk)
11 * @contact www.sprymedia.co.uk/contact
12 * @copyright Copyright 2015-2019 SpryMedia Ltd.
13 *
14 * This source file is free software, available under the following license:
15 * MIT license - http://datatables.net/license/mit
16 *
17 * This source file is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20 *
21 * For details please refer to: http://www.datatables.net
22 */
23
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';
51var DataTable = $.fn.dataTable;
52
53
54/**
55 * RowReorder provides the ability in DataTables to click and drag rows to
56 * reorder them. When a row is dropped the data for the rows effected will be
57 * updated to reflect the change. Normally this data point should also be the
58 * column being sorted upon in the DataTable but this does not need to be the
59 * case. RowReorder implements a "data swap" method - so the rows being
60 * reordered take the value of the data point from the row that used to occupy
61 * the row's new position.
62 *
63 * Initialisation is done by either:
64 *
65 * * `rowReorder` parameter in the DataTable initialisation object
66 * * `new $.fn.dataTable.RowReorder( table, opts )` after DataTables
67 * initialisation.
68 *
69 * @class
70 * @param {object} settings DataTables settings object for the host table
71 * @param {object} [opts] Configuration options
72 * @requires jQuery 1.7+
73 * @requires DataTables 1.10.7+
74 */
75var 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} Folder 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};
149
150
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 originalEvent: e
536 } ];
537
538 // Emit event
539 this._emitEvent( 'row-reorder', eventArgs );
540
541 var update = function () {
542 if ( that.c.update ) {
543 for ( i=0, ien=fullDiff.length ; i<ien ; i++ ) {
544 var row = dt.row( fullDiff[i].node );
545 var rowData = row.data();
546
547 setDataFn( rowData, fullDiff[i].newData );
548
549 // Invalidate the cell that has the same data source as the dataSrc
550 dt.columns().every( function () {
551 if ( this.dataSrc() === dataSrc ) {
552 dt.cell( fullDiff[i].node, this.index() ).invalidate( 'data' );
553 }
554 } );
555 }
556
557 // Trigger row reordered event
558 that._emitEvent( 'row-reordered', eventArgs );
559
560 dt.draw( false );
561 }
562 };
563
564 // Editor interface
565 if ( this.c.editor ) {
566 // Disable user interaction while Editor is submitting
567 this.c.enable = false;
568
569 this.c.editor
570 .edit(
571 diffNodes,
572 false,
573 $.extend( {submit: 'changed'}, this.c.formOptions )
574 )
575 .multiSet( dataSrc, idDiff )
576 .one( 'preSubmitCancelled.rowReorder', function () {
577 that.c.enable = true;
578 that.c.editor.off( '.rowReorder' );
579 dt.draw( false );
580 } )
581 .one( 'submitUnsuccessful.rowReorder', function () {
582 dt.draw( false );
583 } )
584 .one( 'submitSuccess.rowReorder', function () {
585 update();
586 } )
587 .one( 'submitComplete', function () {
588 that.c.enable = true;
589 that.c.editor.off( '.rowReorder' );
590 } )
591 .submit();
592 }
593 else {
594 update();
595 }
596 },
597
598
599 /**
600 * Move the window and DataTables scrolling during a drag to scroll new
601 * content into view.
602 *
603 * This matches the `_shiftScroll` method used in AutoFill, but only
604 * horizontal scrolling is considered here.
605 *
606 * @param {object} e Mouse move event object
607 * @private
608 */
609 _shiftScroll: function ( e )
610 {
611 var that = this;
612 var dt = this.s.dt;
613 var scroll = this.s.scroll;
614 var runInterval = false;
615 var scrollSpeed = 5;
616 var buffer = 65;
617 var
618 windowY = e.pageY - document.body.scrollTop,
619 windowVert,
620 dtVert;
621
622 // Window calculations - based on the mouse position in the window,
623 // regardless of scrolling
624 if ( windowY < buffer ) {
625 windowVert = scrollSpeed * -1;
626 }
627 else if ( windowY > scroll.windowHeight - buffer ) {
628 windowVert = scrollSpeed;
629 }
630
631 // DataTables scrolling calculations - based on the table's position in
632 // the document and the mouse position on the page
633 if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
634 dtVert = scrollSpeed * -1;
635 }
636 else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
637 dtVert = scrollSpeed;
638 }
639
640 // This is where it gets interesting. We want to continue scrolling
641 // without requiring a mouse move, so we need an interval to be
642 // triggered. The interval should continue until it is no longer needed,
643 // but it must also use the latest scroll commands (for example consider
644 // that the mouse might move from scrolling up to scrolling left, all
645 // with the same interval running. We use the `scroll` object to "pass"
646 // this information to the interval. Can't use local variables as they
647 // wouldn't be the ones that are used by an already existing interval!
648 if ( windowVert || dtVert ) {
649 scroll.windowVert = windowVert;
650 scroll.dtVert = dtVert;
651 runInterval = true;
652 }
653 else if ( this.s.scrollInterval ) {
654 // Don't need to scroll - remove any existing timer
655 clearInterval( this.s.scrollInterval );
656 this.s.scrollInterval = null;
657 }
658
659 // If we need to run the interval to scroll and there is no existing
660 // interval (if there is an existing one, it will continue to run)
661 if ( ! this.s.scrollInterval && runInterval ) {
662 this.s.scrollInterval = setInterval( function () {
663 // Don't need to worry about setting scroll <0 or beyond the
664 // scroll bound as the browser will just reject that.
665 if ( scroll.windowVert ) {
666 document.body.scrollTop += scroll.windowVert;
667 }
668
669 // DataTables scrolling
670 if ( scroll.dtVert ) {
671 var scroller = that.dom.dtScroll[0];
672
673 if ( scroll.dtVert ) {
674 scroller.scrollTop += scroll.dtVert;
675 }
676 }
677 }, 20 );
678 }
679 }
680} );
681
682
683
684/**
685 * RowReorder default settings for initialisation
686 *
687 * @namespace
688 * @name RowReorder.defaults
689 * @static
690 */
691RowReorder.defaults = {
692 /**
693 * Data point in the host row's data source object for where to get and set
694 * the data to reorder. This will normally also be the sorting column.
695 *
696 * @type {Number}
697 */
698 dataSrc: 0,
699
700 /**
701 * Editor instance that will be used to perform the update
702 *
703 * @type {DataTable.Editor}
704 */
705 editor: null,
706
707 /**
708 * Enable / disable RowReorder's user interaction
709 * @type {Boolean}
710 */
711 enable: true,
712
713 /**
714 * Form options to pass to Editor when submitting a change in the row order.
715 * See the Editor `from-options` object for details of the options
716 * available.
717 * @type {Object}
718 */
719 formOptions: {},
720
721 /**
722 * Drag handle selector. This defines the element that when dragged will
723 * reorder a row.
724 *
725 * @type {String}
726 */
727 selector: 'td:first-child',
728
729 /**
730 * Optionally lock the dragged row's x-position. This can be `true` to
731 * fix the position match the host table's, `false` to allow free movement
732 * of the row, or a number to define an offset from the host table.
733 *
734 * @type {Boolean|number}
735 */
736 snapX: false,
737
738 /**
739 * Update the table's data on drop
740 *
741 * @type {Boolean}
742 */
743 update: true,
744
745 /**
746 * Selector for children of the drag handle selector that mouseDown events
747 * will be passed through to and drag will not activate
748 *
749 * @type {String}
750 */
751 excludedChildren: 'a'
752};
753
754
755/*
756 * API
757 */
758var Api = $.fn.dataTable.Api;
759
760// Doesn't do anything - work around for a bug in DT... Not documented
761Api.register( 'rowReorder()', function () {
762 return this;
763} );
764
765Api.register( 'rowReorder.enable()', function ( toggle ) {
766 if ( toggle === undefined ) {
767 toggle = true;
768 }
769
770 return this.iterator( 'table', function ( ctx ) {
771 if ( ctx.rowreorder ) {
772 ctx.rowreorder.c.enable = toggle;
773 }
774 } );
775} );
776
777Api.register( 'rowReorder.disable()', function () {
778 return this.iterator( 'table', function ( ctx ) {
779 if ( ctx.rowreorder ) {
780 ctx.rowreorder.c.enable = false;
781 }
782 } );
783} );
784
785
786/**
787 * Version information
788 *
789 * @name RowReorder.version
790 * @static
791 */
792RowReorder.version = '1.2.6';
793
794
795$.fn.dataTable.RowReorder = RowReorder;
796$.fn.DataTable.RowReorder = RowReorder;
797
798// Attach a listener to the document which listens for DataTables initialisation
799// events so we can automatically initialise
800$(document).on( 'init.dt.dtr', function (e, settings, json) {
801 if ( e.namespace !== 'dt' ) {
802 return;
803 }
804
805 var init = settings.oInit.rowReorder;
806 var defaults = DataTable.defaults.rowReorder;
807
808 if ( init || defaults ) {
809 var opts = $.extend( {}, init, defaults );
810
811 if ( init !== false ) {
812 new RowReorder( settings, opts );
813 }
814 }
815} );
816
817
818return RowReorder;
819}));
Note: See TracBrowser for help on using the repository browser.