effective_datatables 4.3.4 → 4.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -1
  3. data/app/assets/javascripts/dataTables/buttons/buttons.bootstrap4.js +1 -0
  4. data/app/assets/javascripts/dataTables/buttons/buttons.colVis.js +12 -2
  5. data/app/assets/javascripts/dataTables/buttons/buttons.html5.js +58 -6
  6. data/app/assets/javascripts/dataTables/buttons/buttons.print.js +16 -5
  7. data/app/assets/javascripts/dataTables/buttons/dataTables.buttons.js +26 -10
  8. data/app/assets/javascripts/dataTables/rowReorder/dataTables.rowReorder.js +818 -0
  9. data/app/assets/javascripts/dataTables/rowReorder/rowReorder.bootstrap4.js +38 -0
  10. data/app/assets/javascripts/effective_datatables.js +3 -10
  11. data/app/assets/javascripts/effective_datatables/flash.js.coffee +20 -0
  12. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +12 -1
  13. data/app/assets/javascripts/effective_datatables/overrides.js.coffee +0 -24
  14. data/app/assets/javascripts/effective_datatables/reorder.js.coffee +37 -0
  15. data/app/assets/stylesheets/dataTables/buttons/buttons.bootstrap4.css +6 -0
  16. data/app/assets/stylesheets/dataTables/rowReorder/rowReorder.bootstrap4.css +22 -0
  17. data/app/assets/stylesheets/effective_datatables.scss +1 -0
  18. data/app/assets/stylesheets/effective_datatables/_overrides.scss +15 -2
  19. data/app/controllers/effective/datatables_controller.rb +25 -0
  20. data/app/helpers/effective_datatables_helper.rb +3 -1
  21. data/app/helpers/effective_datatables_private_helper.rb +28 -4
  22. data/app/models/effective/datatable.rb +23 -3
  23. data/app/models/effective/effective_datatable/dsl.rb +1 -1
  24. data/app/models/effective/effective_datatable/dsl/datatable.rb +65 -26
  25. data/app/models/effective/effective_datatable/format.rb +7 -4
  26. data/app/models/effective/effective_datatable/resource.rb +0 -2
  27. data/app/models/effective/effective_datatable/state.rb +15 -8
  28. data/app/views/effective/datatables/_reorder_column.html.haml +5 -0
  29. data/config/routes.rb +1 -0
  30. data/lib/effective_datatables/version.rb +1 -1
  31. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4dabd6230608916ad4260f443412c2b81bb4ec80
4
- data.tar.gz: 26a82f35207f348157706794fcb818a1b58e77db
3
+ metadata.gz: 7ce4be5421f36169833da3142e5f171f63f794a6
4
+ data.tar.gz: ca64b139e8056c55d46723abfca5ead307b8667b
5
5
  SHA512:
6
- metadata.gz: 17fd09f4caa9cfd14798fe9744a627e355f4f90504cb40a02943b35155fe25d15a4e7708bfab3d4a167b1acd3a9260ba2d8fa95117566cb085b21679099258de
7
- data.tar.gz: b31833cadf5f81ba9e5e7cb69260891fb2498647b872243afd9cec216070ff8c9d40be2da3137b78b06ff60b21f766d2ea081cbc2f6c2dd77e3a10b68868dbaf
6
+ metadata.gz: 9036c5d175ae16eb22ea83892b49e72c5916dd4d53bfda0863fe7d7f6f6c26abf68715cdb29b2de15822b4486353b18042b473d8f8ff1569c0cca7ded5c943fb
7
+ data.tar.gz: b7519014581a8e3e07f496bf17dba5d8a847ad129c07ed0604ae9bdb273d7968743b3e7fe7aece5adb35ad471a1844be0d4cfc4512df3557fd203da68aa53e59
data/README.md CHANGED
@@ -571,6 +571,24 @@ When not specified, effective_datatables will sort by the first defined column.
571
571
  order :created_at, :asc|:desc
572
572
  ```
573
573
 
574
+ ### reorder
575
+
576
+ Enables drag-and-drop row re-ordering.
577
+
578
+ Only works with ActiveRecord collections.
579
+
580
+ The underlying field must be an Integer, and it's assumed to be a sequential list of unique numbers.
581
+
582
+ When a drag and drop is completed, a POST request is made to the datatables#reorder action that will update the indexes.
583
+
584
+ Both zero and one based lists will work.
585
+
586
+ ```ruby
587
+ reorder :position
588
+ ```
589
+
590
+ Using `reorder` will sort the collection by this field and disable all other column sorting.
591
+
574
592
  ### aggregate
575
593
 
576
594
  The `aggregate` command inserts a row in the table's `tfoot`.
@@ -658,7 +676,7 @@ filters do
658
676
  filter :end_date, nil, parse: -> { |term| Time.zone.local(term).end_of_day }
659
677
  filter :user, current_user, as: :select, collection: User.all
660
678
  filter :year, 2018, as: :select, collection: [2018, 2017], label: false, include_blank: false
661
- filter :reports, '2018', as: :grouped_select, collection: [['Years', [['2017', '2017'], ['2018', '2018']]], ['Months', [['January', '1'], ['February', '2']]]], group_method: :last
679
+ filter :year_group, '2018', as: :select, grouped: true, collection: { 'Years' => [['2017', 2017], ['2018', 2018]], 'Months' => [['January', 1], ['February', 2]] }
662
680
  end
663
681
  ```
664
682
 
@@ -57,6 +57,7 @@ $.extend( true, DataTable.Buttons.defaults, {
57
57
  } );
58
58
 
59
59
  DataTable.ext.buttons.collection.className += ' dropdown-toggle';
60
+ DataTable.ext.buttons.collection.rightAlignClassName = 'dropdown-menu-right';
60
61
 
61
62
  return DataTable.Buttons;
62
63
  }));
@@ -108,6 +108,7 @@ $.extend( DataTable.ext.buttons, {
108
108
  },
109
109
  init: function ( dt, button, conf ) {
110
110
  var that = this;
111
+ button.attr( 'data-cv-idx', conf.columns );
111
112
 
112
113
  dt
113
114
  .on( 'column-visibility.dt'+conf.namespace, function (e, settings) {
@@ -122,8 +123,17 @@ $.extend( DataTable.ext.buttons, {
122
123
  return;
123
124
  }
124
125
 
125
- that.text( conf._columnText( dt, conf ) );
126
- that.active( dt.column( conf.columns ).visible() );
126
+ conf.columns = $.inArray( conf.columns, details.mapping );
127
+ button.attr( 'data-cv-idx', conf.columns );
128
+
129
+ // Reorder buttons for new table order
130
+ button
131
+ .parent()
132
+ .children('[data-cv-idx]')
133
+ .sort( function (a, b) {
134
+ return (a.getAttribute('data-cv-idx')*1) - (b.getAttribute('data-cv-idx')*1);
135
+ } )
136
+ .appendTo(button.parent());
127
137
  } );
128
138
 
129
139
  this.active( dt.column( conf.columns ).visible() );
@@ -48,6 +48,20 @@ function _pdfMake () {
48
48
  return pdfmake || window.pdfMake;
49
49
  }
50
50
 
51
+ DataTable.Buttons.pdfMake = function (_) {
52
+ if ( ! _ ) {
53
+ return _pdfMake();
54
+ }
55
+ pdfmake = m_ake;
56
+ }
57
+
58
+ DataTable.Buttons.jszip = function (_) {
59
+ if ( ! _ ) {
60
+ return _jsZip();
61
+ }
62
+ jszip = _;
63
+ }
64
+
51
65
 
52
66
  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
53
67
  * FileSaver.js dependency
@@ -422,6 +436,9 @@ function _addToZip( zip, obj ) {
422
436
 
423
437
  // Return namespace attributes to being as such
424
438
  str = str.replace( /_dt_b_namespace_token_/g, ':' );
439
+
440
+ // Remove testing name space that IE puts into the space preserve attr
441
+ str = str.replace( /xmlns:NS[\d]+="" NS[\d]+:/g, '' );
425
442
  }
426
443
 
427
444
  // Safari, IE and Edge will put empty name space attributes onto
@@ -505,11 +522,11 @@ function _excelColWidth( data, col ) {
505
522
 
506
523
  // Max width rather than having potentially massive column widths
507
524
  if ( max > 40 ) {
508
- return 52; // 40 * 1.3
525
+ return 54; // 40 * 1.35
509
526
  }
510
527
  }
511
528
 
512
- max *= 1.3;
529
+ max *= 1.35;
513
530
 
514
531
  // And a min width
515
532
  return max > 6 ? max : 6;
@@ -550,8 +567,9 @@ var excelStrings = {
550
567
  '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>'+
551
568
  '</bookViews>'+
552
569
  '<sheets>'+
553
- '<sheet name="" sheetId="1" r:id="rId1"/>'+
570
+ '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>'+
554
571
  '</sheets>'+
572
+ '<definedNames/>'+
555
573
  '</workbook>',
556
574
 
557
575
  "xl/worksheets/sheet1.xml":
@@ -992,6 +1010,7 @@ DataTable.ext.buttons.excelHtml5 = {
992
1010
 
993
1011
  var that = this;
994
1012
  var rowPos = 0;
1013
+ var dataStartRow, dataEndRow;
995
1014
  var getXml = function ( type ) {
996
1015
  var str = excelStrings[ type ];
997
1016
 
@@ -1124,8 +1143,6 @@ DataTable.ext.buttons.excelHtml5 = {
1124
1143
  rowPos++;
1125
1144
  };
1126
1145
 
1127
- $( 'sheets sheet', xlsx.xl['workbook.xml'] ).attr( 'name', _sheetname( config ) );
1128
-
1129
1146
  if ( config.customizeData ) {
1130
1147
  config.customizeData( data );
1131
1148
  }
@@ -1154,16 +1171,21 @@ DataTable.ext.buttons.excelHtml5 = {
1154
1171
  mergeCells( rowPos, data.header.length-1 );
1155
1172
  }
1156
1173
 
1174
+
1157
1175
  // Table itself
1158
1176
  if ( config.header ) {
1159
1177
  addRow( data.header, rowPos );
1160
1178
  $('row:last c', rels).attr( 's', '2' ); // bold
1161
1179
  }
1162
1180
 
1181
+ dataStartRow = rowPos;
1182
+
1163
1183
  for ( var n=0, ie=data.body.length ; n<ie ; n++ ) {
1164
1184
  addRow( data.body[n], rowPos );
1165
1185
  }
1166
1186
 
1187
+ dataEndRow = rowPos;
1188
+
1167
1189
  if ( config.footer && data.footer ) {
1168
1190
  addRow( data.footer, rowPos);
1169
1191
  $('row:last c', rels).attr( 's', '2' ); // bold
@@ -1190,6 +1212,29 @@ DataTable.ext.buttons.excelHtml5 = {
1190
1212
  } ) );
1191
1213
  }
1192
1214
 
1215
+ // Auto filter for columns
1216
+ $('mergeCells', rels).before( _createNode( rels, 'autoFilter', {
1217
+ attr: {
1218
+ ref: 'A'+dataStartRow+':'+createCellPos(data.header.length-1)+dataEndRow
1219
+ }
1220
+ } ) );
1221
+
1222
+ // Workbook modifications
1223
+ var workbook = xlsx.xl['workbook.xml'];
1224
+
1225
+ $( 'sheets sheet', workbook ).attr( 'name', _sheetname( config ) );
1226
+
1227
+ if ( config.autoFilter ) {
1228
+ $('definedNames', workbook).append( _createNode( workbook, 'definedName', {
1229
+ attr: {
1230
+ name: '_xlnm._FilterDatabase',
1231
+ localSheetId: '0',
1232
+ hidden: 1
1233
+ },
1234
+ text: _sheetname(config)+'!$A$'+dataStartRow+':'+createCellPos(data.header.length-1)+dataEndRow
1235
+ } ) );
1236
+ }
1237
+
1193
1238
  // Let the developer customise the document if they want to
1194
1239
  if ( config.customize ) {
1195
1240
  config.customize( xlsx, config, dt );
@@ -1244,7 +1289,11 @@ DataTable.ext.buttons.excelHtml5 = {
1244
1289
 
1245
1290
  messageBottom: '*',
1246
1291
 
1247
- createEmptyCells: false
1292
+ createEmptyCells: false,
1293
+
1294
+ autoFilter: false,
1295
+
1296
+ sheetName: ''
1248
1297
  };
1249
1298
 
1250
1299
  //
@@ -1280,6 +1329,9 @@ DataTable.ext.buttons.pdfHtml5 = {
1280
1329
 
1281
1330
  for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
1282
1331
  rows.push( $.map( data.body[i], function ( d ) {
1332
+ if ( d === null || d === undefined ) {
1333
+ d = '';
1334
+ }
1283
1335
  return {
1284
1336
  text: typeof d === 'string' ? d : d+'',
1285
1337
  style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
@@ -91,9 +91,13 @@ DataTable.ext.buttons.print = {
91
91
  $.extend( {decodeEntities: false}, config.exportOptions ) // XSS protection
92
92
  );
93
93
  var exportInfo = dt.buttons.exportInfo( config );
94
- var columnClasses = $.map( dt.settings()[0].aoColumns, function (col, key) {
95
- return col.sClass;
96
- } );
94
+ var columnClasses = dt
95
+ .columns( config.exportOptions.columns )
96
+ .flatten()
97
+ .map( function (idx) {
98
+ return dt.settings()[0].aoColumns[dt.column(idx).index()].sClass;
99
+ } )
100
+ .toArray();
97
101
 
98
102
  var addRow = function ( d, tag ) {
99
103
  var str = '<tr>';
@@ -169,12 +173,19 @@ DataTable.ext.buttons.print = {
169
173
  }
170
174
 
171
175
  // Allow stylesheets time to load
172
- win.setTimeout( function () {
176
+ var autoPrint = function () {
173
177
  if ( config.autoPrint ) {
174
178
  win.print(); // blocking - so close will not
175
179
  win.close(); // execute until this is done
176
180
  }
177
- }, 1000 );
181
+ };
182
+
183
+ if ( navigator.userAgent.match(/Trident\/\d.\d/) ) { // IE needs to call this without a setTimeout
184
+ autoPrint();
185
+ }
186
+ else {
187
+ win.setTimeout( autoPrint, 1000 );
188
+ }
178
189
  },
179
190
 
180
191
  title: '*',
@@ -1,4 +1,4 @@
1
- /*! Buttons for DataTables 1.5.2
1
+ /*! Buttons for DataTables 1.5.4
2
2
  * ©2016-2018 SpryMedia Ltd - datatables.net/license
3
3
  */
4
4
 
@@ -489,7 +489,7 @@ $.extend( Buttons.prototype, {
489
489
  var collectionDom = this.c.dom.collection;
490
490
  built.collection = $('<'+collectionDom.tag+'/>')
491
491
  .addClass( collectionDom.className )
492
- .attr( 'role', 'menu') ;
492
+ .attr( 'role', 'menu' ) ;
493
493
  built.conf._collection = built.collection;
494
494
 
495
495
  this._expandButton( built.buttons, built.conf.buttons, true, attachPoint );
@@ -908,20 +908,23 @@ $.extend( Buttons.prototype, {
908
908
  * @param {string} Class to assign to the background
909
909
  * @static
910
910
  */
911
- Buttons.background = function ( show, className, fade ) {
911
+ Buttons.background = function ( show, className, fade, insertPoint ) {
912
912
  if ( fade === undefined ) {
913
913
  fade = 400;
914
914
  }
915
+ if ( ! insertPoint ) {
916
+ insertPoint = document.body;
917
+ }
915
918
 
916
919
  if ( show ) {
917
920
  $('<div/>')
918
921
  .addClass( className )
919
922
  .css( 'display', 'none' )
920
- .appendTo( 'body' )
923
+ .insertAfter( insertPoint )
921
924
  .fadeIn( fade );
922
925
  }
923
926
  else {
924
- $('body > div.'+className)
927
+ $('div.'+className)
925
928
  .fadeOut( fade, function () {
926
929
  $(this)
927
930
  .removeClass( className )
@@ -1145,7 +1148,10 @@ Buttons.defaults = {
1145
1148
  className: 'dt-button-collection'
1146
1149
  },
1147
1150
  button: {
1148
- tag: 'button',
1151
+ // Flash buttons will not work with `<button>` in IE - it has to be `<a>`
1152
+ tag: 'ActiveXObject' in window ?
1153
+ 'a' :
1154
+ 'button',
1149
1155
  className: 'dt-button',
1150
1156
  active: 'active',
1151
1157
  disabled: 'disabled'
@@ -1162,7 +1168,7 @@ Buttons.defaults = {
1162
1168
  * @type {string}
1163
1169
  * @static
1164
1170
  */
1165
- Buttons.version = '1.5.2';
1171
+ Buttons.version = '1.5.4';
1166
1172
 
1167
1173
 
1168
1174
  $.extend( _dtButtons, {
@@ -1190,13 +1196,15 @@ $.extend( _dtButtons, {
1190
1196
  insertPoint = document.body.lastChild;
1191
1197
  }
1192
1198
 
1199
+ config._collection.find('.dt-button-collection-title').remove();
1200
+ config._collection.prepend('<div class="dt-button-collection-title">'+config.collectionTitle+'</div>');
1201
+
1193
1202
  config._collection
1194
1203
  .addClass( config.collectionLayout )
1195
1204
  .css( 'display', 'none' )
1196
1205
  .insertAfter( insertPoint )
1197
1206
  .fadeIn( config.fade );
1198
1207
 
1199
-
1200
1208
  var position = config._collection.css( 'position' );
1201
1209
 
1202
1210
  if ( multiLevel && position === 'absolute' ) {
@@ -1226,6 +1234,12 @@ $.extend( _dtButtons, {
1226
1234
  config._collection.css( 'top', hostPosition.top - config._collection.outerHeight() - 5);
1227
1235
  }
1228
1236
 
1237
+ // Right alignment is enabled on a class, e.g. bootstrap:
1238
+ // $.fn.dataTable.Buttons.defaults.dom.collection.className += " dropdown-menu-right";
1239
+ if ( config._collection.hasClass( config.rightAlignClassName ) ) {
1240
+ config._collection.css( 'left', hostPosition.left + host.outerWidth() - config._collection.outerWidth() );
1241
+ }
1242
+
1229
1243
  // Right alignment in table container
1230
1244
  var listRight = hostPosition.left + config._collection.outerWidth();
1231
1245
  var tableRight = tableContainer.offset().left + tableContainer.width();
@@ -1250,7 +1264,7 @@ $.extend( _dtButtons, {
1250
1264
  }
1251
1265
 
1252
1266
  if ( config.background ) {
1253
- Buttons.background( true, config.backgroundClassName, config.fade );
1267
+ Buttons.background( true, config.backgroundClassName, config.fade, insertPoint );
1254
1268
  }
1255
1269
 
1256
1270
  var close = function () {
@@ -1260,7 +1274,7 @@ $.extend( _dtButtons, {
1260
1274
  } );
1261
1275
 
1262
1276
  $('div.dt-button-background').off( 'click.dtb-collection' );
1263
- Buttons.background( false, config.backgroundClassName, config.fade );
1277
+ Buttons.background( false, config.backgroundClassName, config.fade, insertPoint );
1264
1278
 
1265
1279
  $('body').off( '.dtb-collection' );
1266
1280
  dt.off( 'buttons-action.b-internal' );
@@ -1299,7 +1313,9 @@ $.extend( _dtButtons, {
1299
1313
  },
1300
1314
  background: true,
1301
1315
  collectionLayout: '',
1316
+ collectionTitle: '',
1302
1317
  backgroundClassName: 'dt-button-background',
1318
+ rightAlignClassName: 'dt-button-right',
1303
1319
  autoClose: false,
1304
1320
  fade: 400,
1305
1321
  attr: {
@@ -0,0 +1,818 @@
1
+ /*! RowReorder 1.2.4
2
+ * 2015-2018 SpryMedia Ltd - datatables.net/license
3
+ */
4
+
5
+ /**
6
+ * @summary RowReorder
7
+ * @description Row reordering extension for DataTables
8
+ * @version 1.2.4
9
+ * @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.
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';
51
+ var 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
+ */
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
+ };
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
+ } ];
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
+ } );
680
+
681
+
682
+
683
+ /**
684
+ * RowReorder default settings for initialisation
685
+ *
686
+ * @namespace
687
+ * @name RowReorder.defaults
688
+ * @static
689
+ */
690
+ 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'
751
+ };
752
+
753
+
754
+ /*
755
+ * API
756
+ */
757
+ var Api = $.fn.dataTable.Api;
758
+
759
+ // 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
+
784
+
785
+ /**
786
+ * Version information
787
+ *
788
+ * @name RowReorder.version
789
+ * @static
790
+ */
791
+ RowReorder.version = '1.2.4';
792
+
793
+
794
+ $.fn.dataTable.RowReorder = RowReorder;
795
+ $.fn.DataTable.RowReorder = RowReorder;
796
+
797
+ // Attach a listener to the document which listens for DataTables initialisation
798
+ // events so we can automatically initialise
799
+ $(document).on( 'init.dt.dtr', function (e, settings, json) {
800
+ if ( e.namespace !== 'dt' ) {
801
+ return;
802
+ }
803
+
804
+ var init = settings.oInit.rowReorder;
805
+ var defaults = DataTable.defaults.rowReorder;
806
+
807
+ if ( init || defaults ) {
808
+ var opts = $.extend( {}, init, defaults );
809
+
810
+ if ( init !== false ) {
811
+ new RowReorder( settings, opts );
812
+ }
813
+ }
814
+ } );
815
+
816
+
817
+ return RowReorder;
818
+ }));