OpenSencillo  2016.106
Long live the simplicity of PHP
 All Data Structures Namespaces Files Functions Pages
TableTools.js
1 /* TableTools
2  * 2009-2015 SpryMedia Ltd - datatables.net/license
3  */
4 
5 /*globals TableTools,ZeroClipboard_TableTools*/
6 
7 
8 (function($, window, document) {
9 
25 TableTools = function( oDT, oOpts )
26 {
27  /* Santiy check that we are a new instance */
28  if ( ! this instanceof TableTools )
29  {
30  alert( "Warning: TableTools must be initialised with the keyword 'new'" );
31  }
32 
33  // In 1.10 we can use the API to get the settings object from a number of
34  // sources
35  var dtSettings = $.fn.dataTable.Api ?
36  new $.fn.dataTable.Api( oDT ).settings()[0] :
37  oDT.fnSettings();
38 
39 
40  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
41  * Public class variables
42  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
43 
47  this.s = {
54  "that": this,
55 
62  "dt": dtSettings,
63 
67  "print": {
74  "saveStart": -1,
75 
82  "saveLength": -1,
83 
90  "saveScroll": -1,
91 
98  "funcEnd": function () {}
99  },
100 
107  "buttonCounter": 0,
108 
112  "select": {
119  "type": "",
120 
127  "selected": [],
128 
136  "preRowSelect": null,
137 
144  "postSelected": null,
145 
152  "postDeselected": null,
153 
160  "all": false,
161 
168  "selectedClass": ""
169  },
170 
177  "custom": {},
178 
185  "swfPath": "",
186 
193  "buttonSet": [],
194 
202  "master": false,
203 
208  "tags": {}
209  };
210 
211 
215  this.dom = {
222  "container": null,
223 
230  "table": null,
231 
235  "print": {
242  "hidden": [],
243 
250  "message": null
251  },
252 
256  "collection": {
263  "collection": null,
264 
271  "background": null
272  }
273  };
274 
279  this.classes = $.extend( true, {}, TableTools.classes );
280  if ( this.s.dt.bJUI )
281  {
282  $.extend( true, this.classes, TableTools.classes_themeroller );
283  }
284 
285 
286  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
287  * Public class methods
288  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
289 
295  this.fnSettings = function () {
296  return this.s;
297  };
298 
299 
300  /* Constructor logic */
301  if ( typeof oOpts == 'undefined' )
302  {
303  oOpts = {};
304  }
305 
306 
307  TableTools._aInstances.push( this );
308  this._fnConstruct( oOpts );
309 
310  return this;
311 };
312 
313 
314 
315 TableTools.prototype = {
316  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
317  * Public methods
318  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
319 
328  "fnGetSelected": function ( filtered )
329  {
330  var
331  out = [],
332  data = this.s.dt.aoData,
333  displayed = this.s.dt.aiDisplay,
334  i, iLen;
335 
336  if ( filtered )
337  {
338  // Only consider filtered rows
339  for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
340  {
341  if ( data[ displayed[i] ]._DTTT_selected )
342  {
343  out.push( data[ displayed[i] ].nTr );
344  }
345  }
346  }
347  else
348  {
349  // Use all rows
350  for ( i=0, iLen=data.length ; i<iLen ; i++ )
351  {
352  if ( data[i]._DTTT_selected )
353  {
354  out.push( data[i].nTr );
355  }
356  }
357  }
358 
359  return out;
360  },
361 
362 
368  "fnGetSelectedData": function ()
369  {
370  var out = [];
371  var data=this.s.dt.aoData;
372  var i, iLen;
373 
374  for ( i=0, iLen=data.length ; i<iLen ; i++ )
375  {
376  if ( data[i]._DTTT_selected )
377  {
378  out.push( this.s.dt.oInstance.fnGetData(i) );
379  }
380  }
381 
382  return out;
383  },
384 
385 
394  "fnGetSelectedIndexes": function ( filtered )
395  {
396  var
397  out = [],
398  data = this.s.dt.aoData,
399  displayed = this.s.dt.aiDisplay,
400  i, iLen;
401 
402  if ( filtered )
403  {
404  // Only consider filtered rows
405  for ( i=0, iLen=displayed.length ; i<iLen ; i++ )
406  {
407  if ( data[ displayed[i] ]._DTTT_selected )
408  {
409  out.push( displayed[i] );
410  }
411  }
412  }
413  else
414  {
415  // Use all rows
416  for ( i=0, iLen=data.length ; i<iLen ; i++ )
417  {
418  if ( data[i]._DTTT_selected )
419  {
420  out.push( i );
421  }
422  }
423  }
424 
425  return out;
426  },
427 
428 
434  "fnIsSelected": function ( n )
435  {
436  var pos = this.s.dt.oInstance.fnGetPosition( n );
437  return (this.s.dt.aoData[pos]._DTTT_selected===true) ? true : false;
438  },
439 
440 
447  "fnSelectAll": function ( filtered )
448  {
449  this._fnRowSelect( filtered ?
450  this.s.dt.aiDisplay :
451  this.s.dt.aoData
452  );
453  },
454 
455 
462  "fnSelectNone": function ( filtered )
463  {
464  this._fnRowDeselect( this.fnGetSelectedIndexes(filtered) );
465  },
466 
467 
473  "fnSelect": function ( n )
474  {
475  if ( this.s.select.type == "single" )
476  {
477  this.fnSelectNone();
478  this._fnRowSelect( n );
479  }
480  else
481  {
482  this._fnRowSelect( n );
483  }
484  },
485 
486 
492  "fnDeselect": function ( n )
493  {
494  this._fnRowDeselect( n );
495  },
496 
497 
504  "fnGetTitle": function( oConfig )
505  {
506  var sTitle = "";
507  if ( typeof oConfig.sTitle != 'undefined' && oConfig.sTitle !== "" ) {
508  sTitle = oConfig.sTitle;
509  } else {
510  var anTitle = document.getElementsByTagName('title');
511  if ( anTitle.length > 0 )
512  {
513  sTitle = anTitle[0].innerHTML;
514  }
515  }
516 
517  /* Strip characters which the OS will object to - checking for UTF8 support in the scripting
518  * engine
519  */
520  if ( "\u00A1".toString().length < 4 ) {
521  return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
522  } else {
523  return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, "");
524  }
525  },
526 
527 
535  "fnCalcColRatios": function ( oConfig )
536  {
537  var
538  aoCols = this.s.dt.aoColumns,
539  aColumnsInc = this._fnColumnTargets( oConfig.mColumns ),
540  aColWidths = [],
541  iWidth = 0, iTotal = 0, i, iLen;
542 
543  for ( i=0, iLen=aColumnsInc.length ; i<iLen ; i++ )
544  {
545  if ( aColumnsInc[i] )
546  {
547  iWidth = aoCols[i].nTh.offsetWidth;
548  iTotal += iWidth;
549  aColWidths.push( iWidth );
550  }
551  }
552 
553 
554  for ( i=0, iLen=aColWidths.length ; i<iLen ; i++ )
555  {
556  aColWidths[i] = aColWidths[i] / iTotal;
557  }
558 
559  return aColWidths.join('\t');
560  },
561 
562 
568  "fnGetTableData": function ( oConfig )
569  {
570  /* In future this could be used to get data from a plain HTML source as well as DataTables */
571  if ( this.s.dt )
572  {
573  return this._fnGetDataTablesData( oConfig );
574  }
575  },
576 
577 
583  "fnSetText": function ( clip, text )
584  {
585  this._fnFlashSetText( clip, text );
586  },
587 
588 
594  "fnResizeButtons": function ()
595  {
596  for ( var cli in ZeroClipboard_TableTools.clients )
597  {
598  if ( cli )
599  {
600  var client = ZeroClipboard_TableTools.clients[cli];
601  if ( typeof client.domElement != 'undefined' &&
602  client.domElement.parentNode )
603  {
604  client.positionElement();
605  }
606  }
607  }
608  },
609 
610 
614  "fnResizeRequired": function ()
615  {
616  for ( var cli in ZeroClipboard_TableTools.clients )
617  {
618  if ( cli )
619  {
620  var client = ZeroClipboard_TableTools.clients[cli];
621  if ( typeof client.domElement != 'undefined' &&
622  client.domElement.parentNode == this.dom.container &&
623  client.sized === false )
624  {
625  return true;
626  }
627  }
628  }
629  return false;
630  },
631 
632 
644  "fnPrint": function ( bView, oConfig )
645  {
646  if ( oConfig === undefined )
647  {
648  oConfig = {};
649  }
650 
651  if ( bView === undefined || bView )
652  {
653  this._fnPrintStart( oConfig );
654  }
655  else
656  {
657  this._fnPrintEnd();
658  }
659  },
660 
661 
667  "fnInfo": function ( message, time ) {
668  var info = $('<div/>')
669  .addClass( this.classes.print.info )
670  .html( message )
671  .appendTo( 'body' );
672 
673  setTimeout( function() {
674  info.fadeOut( "normal", function() {
675  info.remove();
676  } );
677  }, time );
678  },
679 
680 
681 
686  "fnContainer": function () {
687  return this.dom.container;
688  },
689 
690 
691 
692  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
693  * Private methods (they are of course public in JS, but recommended as private)
694  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
695 
703  "_fnConstruct": function ( oOpts )
704  {
705  var that = this;
706 
707  this._fnCustomiseSettings( oOpts );
708 
709  /* Container element */
710  this.dom.container = document.createElement( this.s.tags.container );
711  this.dom.container.className = this.classes.container;
712 
713  /* Row selection config */
714  if ( this.s.select.type != 'none' )
715  {
716  this._fnRowSelectConfig();
717  }
718 
719  /* Buttons */
720  this._fnButtonDefinations( this.s.buttonSet, this.dom.container );
721 
722  /* Destructor */
723  this.s.dt.aoDestroyCallback.push( {
724  "sName": "TableTools",
725  "fn": function () {
726  $(that.s.dt.nTBody)
727  .off( 'click.DTTT_Select', that.s.custom.sRowSelector )
728  .off( 'mousedown.DTTT_Select', 'tr' )
729  .off( 'mouseup.DTTT_Select', 'tr' );
730 
731  $.each( ZeroClipboard_TableTools.clients, function ( id, client ) {
732  if ( $(client.domElement).parents( that.dom.container ).length ) {
733  delete ZeroClipboard_TableTools.clients[ id ];
734  }
735  } );
736 
737  $(that.dom.container).empty();
738 
739  // Remove the instance
740  var idx = $.inArray( that, TableTools._aInstances );
741  if ( idx !== -1 ) {
742  TableTools._aInstances.splice( idx, 1 );
743  }
744  }
745  } );
746  },
747 
748 
756  "_fnCustomiseSettings": function ( oOpts )
757  {
758  /* Is this the master control instance or not? */
759  if ( typeof this.s.dt._TableToolsInit == 'undefined' )
760  {
761  this.s.master = true;
762  this.s.dt._TableToolsInit = true;
763  }
764 
765  /* We can use the table node from comparisons to group controls */
766  this.dom.table = this.s.dt.nTable;
767 
768  /* Clone the defaults and then the user options */
769  this.s.custom = $.extend( {}, TableTools.DEFAULTS, oOpts );
770 
771  /* Flash file location */
772  this.s.swfPath = this.s.custom.sSwfPath;
773  if ( typeof ZeroClipboard_TableTools != 'undefined' )
774  {
775  ZeroClipboard_TableTools.moviePath = this.s.swfPath;
776  }
777 
778  /* Table row selecting */
779  this.s.select.type = this.s.custom.sRowSelect;
780  this.s.select.preRowSelect = this.s.custom.fnPreRowSelect;
781  this.s.select.postSelected = this.s.custom.fnRowSelected;
782  this.s.select.postDeselected = this.s.custom.fnRowDeselected;
783 
784  // Backwards compatibility - allow the user to specify a custom class in the initialiser
785  if ( this.s.custom.sSelectedClass )
786  {
787  this.classes.select.row = this.s.custom.sSelectedClass;
788  }
789 
790  this.s.tags = this.s.custom.oTags;
791 
792  /* Button set */
793  this.s.buttonSet = this.s.custom.aButtons;
794  },
795 
796 
806  "_fnButtonDefinations": function ( buttonSet, wrapper )
807  {
808  var buttonDef;
809 
810  for ( var i=0, iLen=buttonSet.length ; i<iLen ; i++ )
811  {
812  if ( typeof buttonSet[i] == "string" )
813  {
814  if ( typeof TableTools.BUTTONS[ buttonSet[i] ] == 'undefined' )
815  {
816  alert( "TableTools: Warning - unknown button type: "+buttonSet[i] );
817  continue;
818  }
819  buttonDef = $.extend( {}, TableTools.BUTTONS[ buttonSet[i] ], true );
820  }
821  else
822  {
823  if ( typeof TableTools.BUTTONS[ buttonSet[i].sExtends ] == 'undefined' )
824  {
825  alert( "TableTools: Warning - unknown button type: "+buttonSet[i].sExtends );
826  continue;
827  }
828  var o = $.extend( {}, TableTools.BUTTONS[ buttonSet[i].sExtends ], true );
829  buttonDef = $.extend( o, buttonSet[i], true );
830  }
831 
832  var button = this._fnCreateButton(
833  buttonDef,
834  $(wrapper).hasClass(this.classes.collection.container)
835  );
836 
837  if ( button ) {
838  wrapper.appendChild( button );
839  }
840  }
841  },
842 
843 
851  "_fnCreateButton": function ( oConfig, bCollectionButton )
852  {
853  var nButton = this._fnButtonBase( oConfig, bCollectionButton );
854 
855  if ( oConfig.sAction.match(/flash/) )
856  {
857  if ( ! this._fnHasFlash() ) {
858  return false;
859  }
860 
861  this._fnFlashConfig( nButton, oConfig );
862  }
863  else if ( oConfig.sAction == "text" )
864  {
865  this._fnTextConfig( nButton, oConfig );
866  }
867  else if ( oConfig.sAction == "div" )
868  {
869  this._fnTextConfig( nButton, oConfig );
870  }
871  else if ( oConfig.sAction == "collection" )
872  {
873  this._fnTextConfig( nButton, oConfig );
874  this._fnCollectionConfig( nButton, oConfig );
875  }
876 
877  if ( this.s.dt.iTabIndex !== -1 ) {
878  $(nButton)
879  .attr( 'tabindex', this.s.dt.iTabIndex )
880  .attr( 'aria-controls', this.s.dt.sTableId )
881  .on( 'keyup.DTTT', function (e) {
882  // Trigger the click event on return key when focused.
883  // Note that for Flash buttons this has no effect since we
884  // can't programmatically trigger the Flash export
885  if ( e.keyCode === 13 ) {
886  e.stopPropagation();
887 
888  $(this).trigger( 'click' );
889  }
890  } )
891  .on( 'mousedown.DTTT', function (e) {
892  // On mousedown we want to stop the focus occurring on the
893  // button, focus is used only for the keyboard navigation.
894  // But using preventDefault for the flash buttons stops the
895  // flash action. However, it is not the button that gets
896  // focused but the flash element for flash buttons, so this
897  // works
898  if ( ! oConfig.sAction.match(/flash/) ) {
899  e.preventDefault();
900  }
901  } );
902  }
903 
904  return nButton;
905  },
906 
907 
915  "_fnButtonBase": function ( o, bCollectionButton )
916  {
917  var sTag, sLiner, sClass;
918 
919  if ( bCollectionButton )
920  {
921  sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.collection.button;
922  sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.collection.liner;
923  sClass = this.classes.collection.buttons.normal;
924  }
925  else
926  {
927  sTag = o.sTag && o.sTag !== "default" ? o.sTag : this.s.tags.button;
928  sLiner = o.sLinerTag && o.sLinerTag !== "default" ? o.sLiner : this.s.tags.liner;
929  sClass = this.classes.buttons.normal;
930  }
931 
932  var
933  nButton = document.createElement( sTag ),
934  nSpan = document.createElement( sLiner ),
935  masterS = this._fnGetMasterSettings();
936 
937  nButton.className = sClass+" "+o.sButtonClass;
938  nButton.setAttribute('id', "ToolTables_"+this.s.dt.sInstance+"_"+masterS.buttonCounter );
939  nButton.appendChild( nSpan );
940  nSpan.innerHTML = o.sButtonText;
941 
942  masterS.buttonCounter++;
943 
944  return nButton;
945  },
946 
947 
956  "_fnGetMasterSettings": function ()
957  {
958  if ( this.s.master )
959  {
960  return this.s;
961  }
962  else
963  {
964  /* Look for the master which has the same DT as this one */
965  var instances = TableTools._aInstances;
966  for ( var i=0, iLen=instances.length ; i<iLen ; i++ )
967  {
968  if ( this.dom.table == instances[i].s.dt.nTable )
969  {
970  return instances[i].s;
971  }
972  }
973  }
974  },
975 
976 
977 
978  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
979  * Button collection functions
980  */
981 
989  "_fnCollectionConfig": function ( nButton, oConfig )
990  {
991  var nHidden = document.createElement( this.s.tags.collection.container );
992  nHidden.style.display = "none";
993  nHidden.className = this.classes.collection.container;
994  oConfig._collection = nHidden;
995  document.body.appendChild( nHidden );
996 
997  this._fnButtonDefinations( oConfig.aButtons, nHidden );
998  },
999 
1000 
1008  "_fnCollectionShow": function ( nButton, oConfig )
1009  {
1010  var
1011  that = this,
1012  oPos = $(nButton).offset(),
1013  nHidden = oConfig._collection,
1014  iDivX = oPos.left,
1015  iDivY = oPos.top + $(nButton).outerHeight(),
1016  iWinHeight = $(window).height(), iDocHeight = $(document).height(),
1017  iWinWidth = $(window).width(), iDocWidth = $(document).width();
1018 
1019  nHidden.style.position = "absolute";
1020  nHidden.style.left = iDivX+"px";
1021  nHidden.style.top = iDivY+"px";
1022  nHidden.style.display = "block";
1023  $(nHidden).css('opacity',0);
1024 
1025  var nBackground = document.createElement('div');
1026  nBackground.style.position = "absolute";
1027  nBackground.style.left = "0px";
1028  nBackground.style.top = "0px";
1029  nBackground.style.height = ((iWinHeight>iDocHeight)? iWinHeight : iDocHeight) +"px";
1030  nBackground.style.width = ((iWinWidth>iDocWidth)? iWinWidth : iDocWidth) +"px";
1031  nBackground.className = this.classes.collection.background;
1032  $(nBackground).css('opacity',0);
1033 
1034  document.body.appendChild( nBackground );
1035  document.body.appendChild( nHidden );
1036 
1037  /* Visual corrections to try and keep the collection visible */
1038  var iDivWidth = $(nHidden).outerWidth();
1039  var iDivHeight = $(nHidden).outerHeight();
1040 
1041  if ( iDivX + iDivWidth > iDocWidth )
1042  {
1043  nHidden.style.left = (iDocWidth-iDivWidth)+"px";
1044  }
1045 
1046  if ( iDivY + iDivHeight > iDocHeight )
1047  {
1048  nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px";
1049  }
1050 
1051  this.dom.collection.collection = nHidden;
1052  this.dom.collection.background = nBackground;
1053 
1054  /* This results in a very small delay for the end user but it allows the animation to be
1055  * much smoother. If you don't want the animation, then the setTimeout can be removed
1056  */
1057  setTimeout( function () {
1058  $(nHidden).animate({"opacity": 1}, 500);
1059  $(nBackground).animate({"opacity": 0.25}, 500);
1060  }, 10 );
1061 
1062  /* Resize the buttons to the Flash contents fit */
1063  this.fnResizeButtons();
1064 
1065  /* Event handler to remove the collection display */
1066  $(nBackground).click( function () {
1067  that._fnCollectionHide.call( that, null, null );
1068  } );
1069  },
1070 
1071 
1079  "_fnCollectionHide": function ( nButton, oConfig )
1080  {
1081  if ( oConfig !== null && oConfig.sExtends == 'collection' )
1082  {
1083  return;
1084  }
1085 
1086  if ( this.dom.collection.collection !== null )
1087  {
1088  $(this.dom.collection.collection).animate({"opacity": 0}, 500, function (e) {
1089  this.style.display = "none";
1090  } );
1091 
1092  $(this.dom.collection.background).animate({"opacity": 0}, 500, function (e) {
1093  this.parentNode.removeChild( this );
1094  } );
1095 
1096  this.dom.collection.collection = null;
1097  this.dom.collection.background = null;
1098  }
1099  },
1100 
1101 
1102 
1103  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1104  * Row selection functions
1105  */
1106 
1113  "_fnRowSelectConfig": function ()
1114  {
1115  if ( this.s.master )
1116  {
1117  var
1118  that = this,
1119  i, iLen,
1120  dt = this.s.dt,
1121  aoOpenRows = this.s.dt.aoOpenRows;
1122 
1123  $(dt.nTable).addClass( this.classes.select.table );
1124 
1125  // When using OS style selection, we want to cancel the shift text
1126  // selection, but only when the shift key is used (so you can
1127  // actually still select text in the table)
1128  if ( this.s.select.type === 'os' ) {
1129  $(dt.nTBody).on( 'mousedown.DTTT_Select', 'tr', function(e) {
1130  if ( e.shiftKey ) {
1131 
1132  $(dt.nTBody)
1133  .css( '-moz-user-select', 'none' )
1134  .one('selectstart.DTTT_Select', 'tr', function () {
1135  return false;
1136  } );
1137  }
1138  } );
1139 
1140  $(dt.nTBody).on( 'mouseup.DTTT_Select', 'tr', function(e) {
1141  $(dt.nTBody).css( '-moz-user-select', '' );
1142  } );
1143  }
1144 
1145  // Row selection
1146  $(dt.nTBody).on( 'click.DTTT_Select', this.s.custom.sRowSelector, function(e) {
1147  var row = this.nodeName.toLowerCase() === 'tr' ?
1148  this :
1149  $(this).parents('tr')[0];
1150 
1151  var select = that.s.select;
1152  var pos = that.s.dt.oInstance.fnGetPosition( row );
1153 
1154  /* Sub-table must be ignored (odd that the selector won't do this with >) */
1155  if ( row.parentNode != dt.nTBody ) {
1156  return;
1157  }
1158 
1159  /* Check that we are actually working with a DataTables controlled row */
1160  if ( dt.oInstance.fnGetData(row) === null ) {
1161  return;
1162  }
1163 
1164  // Shift click, ctrl click and simple click handling to make
1165  // row selection a lot like a file system in desktop OSs
1166  if ( select.type == 'os' ) {
1167  if ( e.ctrlKey || e.metaKey ) {
1168  // Add or remove from the selection
1169  if ( that.fnIsSelected( row ) ) {
1170  that._fnRowDeselect( row, e );
1171  }
1172  else {
1173  that._fnRowSelect( row, e );
1174  }
1175  }
1176  else if ( e.shiftKey ) {
1177  // Add a range of rows, from the last selected row to
1178  // this one
1179  var rowIdxs = that.s.dt.aiDisplay.slice(); // visible rows
1180  var idx1 = $.inArray( select.lastRow, rowIdxs );
1181  var idx2 = $.inArray( pos, rowIdxs );
1182 
1183  if ( that.fnGetSelected().length === 0 || idx1 === -1 ) {
1184  // select from top to here - slightly odd, but both
1185  // Windows and Mac OS do this
1186  rowIdxs.splice( $.inArray( pos, rowIdxs )+1, rowIdxs.length );
1187  }
1188  else {
1189  // reverse so we can shift click 'up' as well as down
1190  if ( idx1 > idx2 ) {
1191  var tmp = idx2;
1192  idx2 = idx1;
1193  idx1 = tmp;
1194  }
1195 
1196  rowIdxs.splice( idx2+1, rowIdxs.length );
1197  rowIdxs.splice( 0, idx1 );
1198  }
1199 
1200  if ( ! that.fnIsSelected( row ) ) {
1201  // Select range
1202  that._fnRowSelect( rowIdxs, e );
1203  }
1204  else {
1205  // Deselect range - need to keep the clicked on row selected
1206  rowIdxs.splice( $.inArray( pos, rowIdxs ), 1 );
1207  that._fnRowDeselect( rowIdxs, e );
1208  }
1209  }
1210  else {
1211  // No cmd or shift click. Deselect current if selected,
1212  // or select this row only
1213  if ( that.fnIsSelected( row ) && that.fnGetSelected().length === 1 ) {
1214  that._fnRowDeselect( row, e );
1215  }
1216  else {
1217  that.fnSelectNone();
1218  that._fnRowSelect( row, e );
1219  }
1220  }
1221  }
1222  else if ( that.fnIsSelected( row ) ) {
1223  that._fnRowDeselect( row, e );
1224  }
1225  else if ( select.type == "single" ) {
1226  that.fnSelectNone();
1227  that._fnRowSelect( row, e );
1228  }
1229  else if ( select.type == "multi" ) {
1230  that._fnRowSelect( row, e );
1231  }
1232 
1233  select.lastRow = pos;
1234  } );//.on('selectstart', function () { return false; } );
1235 
1236  // Bind a listener to the DataTable for when new rows are created.
1237  // This allows rows to be visually selected when they should be and
1238  // deferred rendering is used.
1239  dt.oApi._fnCallbackReg( dt, 'aoRowCreatedCallback', function (tr, data, index) {
1240  if ( dt.aoData[index]._DTTT_selected ) {
1241  $(tr).addClass( that.classes.select.row );
1242  }
1243  }, 'TableTools-SelectAll' );
1244  }
1245  },
1246 
1252  "_fnRowSelect": function ( src, e )
1253  {
1254  var
1255  that = this,
1256  data = this._fnSelectData( src ),
1257  firstTr = data.length===0 ? null : data[0].nTr,
1258  anSelected = [],
1259  i, len;
1260 
1261  // Get all the rows that will be selected
1262  for ( i=0, len=data.length ; i<len ; i++ )
1263  {
1264  if ( data[i].nTr )
1265  {
1266  anSelected.push( data[i].nTr );
1267  }
1268  }
1269 
1270  // User defined pre-selection function
1271  if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anSelected, true) )
1272  {
1273  return;
1274  }
1275 
1276  // Mark them as selected
1277  for ( i=0, len=data.length ; i<len ; i++ )
1278  {
1279  data[i]._DTTT_selected = true;
1280 
1281  if ( data[i].nTr )
1282  {
1283  $(data[i].nTr).addClass( that.classes.select.row );
1284  }
1285  }
1286 
1287  // Post-selection function
1288  if ( this.s.select.postSelected !== null )
1289  {
1290  this.s.select.postSelected.call( this, anSelected );
1291  }
1292 
1293  TableTools._fnEventDispatch( this, 'select', anSelected, true );
1294  },
1295 
1301  "_fnRowDeselect": function ( src, e )
1302  {
1303  var
1304  that = this,
1305  data = this._fnSelectData( src ),
1306  firstTr = data.length===0 ? null : data[0].nTr,
1307  anDeselectedTrs = [],
1308  i, len;
1309 
1310  // Get all the rows that will be deselected
1311  for ( i=0, len=data.length ; i<len ; i++ )
1312  {
1313  if ( data[i].nTr )
1314  {
1315  anDeselectedTrs.push( data[i].nTr );
1316  }
1317  }
1318 
1319  // User defined pre-selection function
1320  if ( this.s.select.preRowSelect !== null && !this.s.select.preRowSelect.call(this, e, anDeselectedTrs, false) )
1321  {
1322  return;
1323  }
1324 
1325  // Mark them as deselected
1326  for ( i=0, len=data.length ; i<len ; i++ )
1327  {
1328  data[i]._DTTT_selected = false;
1329 
1330  if ( data[i].nTr )
1331  {
1332  $(data[i].nTr).removeClass( that.classes.select.row );
1333  }
1334  }
1335 
1336  // Post-deselection function
1337  if ( this.s.select.postDeselected !== null )
1338  {
1339  this.s.select.postDeselected.call( this, anDeselectedTrs );
1340  }
1341 
1342  TableTools._fnEventDispatch( this, 'select', anDeselectedTrs, false );
1343  },
1344 
1352  "_fnSelectData": function ( src )
1353  {
1354  var out = [], pos, i, iLen;
1355 
1356  if ( src.nodeName )
1357  {
1358  // Single node
1359  pos = this.s.dt.oInstance.fnGetPosition( src );
1360  out.push( this.s.dt.aoData[pos] );
1361  }
1362  else if ( typeof src.length !== 'undefined' )
1363  {
1364  // jQuery object or an array of nodes, or aoData points
1365  for ( i=0, iLen=src.length ; i<iLen ; i++ )
1366  {
1367  if ( src[i].nodeName )
1368  {
1369  pos = this.s.dt.oInstance.fnGetPosition( src[i] );
1370  out.push( this.s.dt.aoData[pos] );
1371  }
1372  else if ( typeof src[i] === 'number' )
1373  {
1374  out.push( this.s.dt.aoData[ src[i] ] );
1375  }
1376  else
1377  {
1378  out.push( src[i] );
1379  }
1380  }
1381 
1382  return out;
1383  }
1384  else if ( typeof src === 'number' )
1385  {
1386  out.push(this.s.dt.aoData[src]);
1387  }
1388  else
1389  {
1390  // A single aoData point
1391  out.push( src );
1392  }
1393 
1394  return out;
1395  },
1396 
1397 
1398  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1399  * Text button functions
1400  */
1401 
1410  "_fnTextConfig": function ( nButton, oConfig )
1411  {
1412  var that = this;
1413 
1414  if ( oConfig.fnInit !== null )
1415  {
1416  oConfig.fnInit.call( this, nButton, oConfig );
1417  }
1418 
1419  if ( oConfig.sToolTip !== "" )
1420  {
1421  nButton.title = oConfig.sToolTip;
1422  }
1423 
1424  $(nButton).hover( function () {
1425  if ( oConfig.fnMouseover !== null )
1426  {
1427  oConfig.fnMouseover.call( this, nButton, oConfig, null );
1428  }
1429  }, function () {
1430  if ( oConfig.fnMouseout !== null )
1431  {
1432  oConfig.fnMouseout.call( this, nButton, oConfig, null );
1433  }
1434  } );
1435 
1436  if ( oConfig.fnSelect !== null )
1437  {
1438  TableTools._fnEventListen( this, 'select', function (n) {
1439  oConfig.fnSelect.call( that, nButton, oConfig, n );
1440  } );
1441  }
1442 
1443  $(nButton).click( function (e) {
1444  //e.preventDefault();
1445 
1446  if ( oConfig.fnClick !== null )
1447  {
1448  oConfig.fnClick.call( that, nButton, oConfig, null, e );
1449  }
1450 
1451  /* Provide a complete function to match the behaviour of the flash elements */
1452  if ( oConfig.fnComplete !== null )
1453  {
1454  oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1455  }
1456 
1457  that._fnCollectionHide( nButton, oConfig );
1458  } );
1459  },
1460 
1461 
1462 
1463  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1464  * Flash button functions
1465  */
1466 
1473  "_fnHasFlash": function ()
1474  {
1475  try {
1476  var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
1477  if (fo) {
1478  return true;
1479  }
1480  }
1481  catch (e) {
1482  if (
1483  navigator.mimeTypes &&
1484  navigator.mimeTypes['application/x-shockwave-flash'] !== undefined &&
1485  navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin
1486  ) {
1487  return true;
1488  }
1489  }
1490 
1491  return false;
1492  },
1493 
1494 
1503  "_fnFlashConfig": function ( nButton, oConfig )
1504  {
1505  var that = this;
1506  var flash = new ZeroClipboard_TableTools.Client();
1507 
1508  if ( oConfig.fnInit !== null )
1509  {
1510  oConfig.fnInit.call( this, nButton, oConfig );
1511  }
1512 
1513  flash.setHandCursor( true );
1514 
1515  if ( oConfig.sAction == "flash_save" )
1516  {
1517  flash.setAction( 'save' );
1518  flash.setCharSet( (oConfig.sCharSet=="utf16le") ? 'UTF16LE' : 'UTF8' );
1519  flash.setBomInc( oConfig.bBomInc );
1520  flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1521  }
1522  else if ( oConfig.sAction == "flash_pdf" )
1523  {
1524  flash.setAction( 'pdf' );
1525  flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1526  }
1527  else
1528  {
1529  flash.setAction( 'copy' );
1530  }
1531 
1532  flash.addEventListener('mouseOver', function(client) {
1533  if ( oConfig.fnMouseover !== null )
1534  {
1535  oConfig.fnMouseover.call( that, nButton, oConfig, flash );
1536  }
1537  } );
1538 
1539  flash.addEventListener('mouseOut', function(client) {
1540  if ( oConfig.fnMouseout !== null )
1541  {
1542  oConfig.fnMouseout.call( that, nButton, oConfig, flash );
1543  }
1544  } );
1545 
1546  flash.addEventListener('mouseDown', function(client) {
1547  if ( oConfig.fnClick !== null )
1548  {
1549  oConfig.fnClick.call( that, nButton, oConfig, flash );
1550  }
1551  } );
1552 
1553  flash.addEventListener('complete', function (client, text) {
1554  if ( oConfig.fnComplete !== null )
1555  {
1556  oConfig.fnComplete.call( that, nButton, oConfig, flash, text );
1557  }
1558  that._fnCollectionHide( nButton, oConfig );
1559  } );
1560 
1561  if ( oConfig.fnSelect !== null )
1562  {
1563  TableTools._fnEventListen( this, 'select', function (n) {
1564  oConfig.fnSelect.call( that, nButton, oConfig, n );
1565  } );
1566  }
1567 
1568  this._fnFlashGlue( flash, nButton, oConfig.sToolTip );
1569  },
1570 
1571 
1582  "_fnFlashGlue": function ( flash, node, text )
1583  {
1584  var that = this;
1585  var id = node.getAttribute('id');
1586 
1587  if ( document.getElementById(id) )
1588  {
1589  flash.glue( node, text );
1590  }
1591  else
1592  {
1593  setTimeout( function () {
1594  that._fnFlashGlue( flash, node, text );
1595  }, 100 );
1596  }
1597  },
1598 
1599 
1614  "_fnFlashSetText": function ( clip, sData )
1615  {
1616  var asData = this._fnChunkData( sData, 8192 );
1617 
1618  clip.clearText();
1619  for ( var i=0, iLen=asData.length ; i<iLen ; i++ )
1620  {
1621  clip.appendText( asData[i] );
1622  }
1623  },
1624 
1625 
1626 
1627  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1628  * Data retrieval functions
1629  */
1630 
1642  "_fnColumnTargets": function ( mColumns )
1643  {
1644  var aColumns = [];
1645  var dt = this.s.dt;
1646  var i, iLen;
1647  var columns = dt.aoColumns;
1648  var columnCount = columns.length;
1649 
1650  if ( typeof mColumns == "function" )
1651  {
1652  var a = mColumns.call( this, dt );
1653 
1654  for ( i=0, iLen=columnCount ; i<iLen ; i++ )
1655  {
1656  aColumns.push( $.inArray( i, a ) !== -1 ? true : false );
1657  }
1658  }
1659  else if ( typeof mColumns == "object" )
1660  {
1661  for ( i=0, iLen=columnCount ; i<iLen ; i++ )
1662  {
1663  aColumns.push( false );
1664  }
1665 
1666  for ( i=0, iLen=mColumns.length ; i<iLen ; i++ )
1667  {
1668  aColumns[ mColumns[i] ] = true;
1669  }
1670  }
1671  else if ( mColumns == "visible" )
1672  {
1673  for ( i=0, iLen=columnCount ; i<iLen ; i++ )
1674  {
1675  aColumns.push( columns[i].bVisible ? true : false );
1676  }
1677  }
1678  else if ( mColumns == "hidden" )
1679  {
1680  for ( i=0, iLen=columnCount ; i<iLen ; i++ )
1681  {
1682  aColumns.push( columns[i].bVisible ? false : true );
1683  }
1684  }
1685  else if ( mColumns == "sortable" )
1686  {
1687  for ( i=0, iLen=columnCount ; i<iLen ; i++ )
1688  {
1689  aColumns.push( columns[i].bSortable ? true : false );
1690  }
1691  }
1692  else /* all */
1693  {
1694  for ( i=0, iLen=columnCount ; i<iLen ; i++ )
1695  {
1696  aColumns.push( true );
1697  }
1698  }
1699 
1700  return aColumns;
1701  },
1702 
1703 
1710  "_fnNewline": function ( oConfig )
1711  {
1712  if ( oConfig.sNewLine == "auto" )
1713  {
1714  return navigator.userAgent.match(/Windows/) ? "\r\n" : "\n";
1715  }
1716  else
1717  {
1718  return oConfig.sNewLine;
1719  }
1720  },
1721 
1722 
1737  "_fnGetDataTablesData": function ( oConfig )
1738  {
1739  var i, iLen, j, jLen;
1740  var aRow, aData=[], sLoopData='', arr;
1741  var dt = this.s.dt, tr, child;
1742  var regex = new RegExp(oConfig.sFieldBoundary, "g"); /* Do it here for speed */
1743  var aColumnsInc = this._fnColumnTargets( oConfig.mColumns );
1744  var bSelectedOnly = (typeof oConfig.bSelectedOnly != 'undefined') ? oConfig.bSelectedOnly : false;
1745 
1746  /*
1747  * Header
1748  */
1749  if ( oConfig.bHeader )
1750  {
1751  aRow = [];
1752 
1753  for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1754  {
1755  if ( aColumnsInc[i] )
1756  {
1757  sLoopData = dt.aoColumns[i].sTitle.replace(/\n/g," ").replace( /<.*?>/g, "" ).replace(/^\s+|\s+$/g,"");
1758  sLoopData = this._fnHtmlDecode( sLoopData );
1759 
1760  aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
1761  }
1762  }
1763 
1764  aData.push( aRow.join(oConfig.sFieldSeperator) );
1765  }
1766 
1767  bSelectedOnly = true;
1768 
1769  /*
1770  * Body
1771  */
1772  var aDataIndex;
1773  var aSelected = this.fnGetSelectedIndexes();
1774  bSelectedOnly = this.s.select.type !== "none" && bSelectedOnly && aSelected.length !== 0;
1775 
1776  if ( bSelectedOnly ) {
1777  // Use the selected indexes
1778  aDataIndex = aSelected;
1779  }
1780  else if ( DataTable.Api ) {
1781  // 1.10+ style
1782  aDataIndex = new DataTable.Api( dt )
1783  .rows( oConfig.oSelectorOpts )
1784  .indexes()
1785  .flatten()
1786  .toArray();
1787  }
1788  else {
1789  // 1.9- style
1790  aDataIndex = dt.oInstance
1791  .$('tr', oConfig.oSelectorOpts)
1792  .map( function (id, row) {
1793  return dt.oInstance.fnGetPosition( row );
1794  } )
1795  .get();
1796  }
1797 
1798  for ( j=0, jLen=aDataIndex.length ; j<jLen ; j++ )
1799  {
1800  tr = dt.aoData[ aDataIndex[j] ].nTr;
1801  aRow = [];
1802 
1803  /* Columns */
1804  for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1805  {
1806  if ( aColumnsInc[i] )
1807  {
1808  /* Convert to strings (with small optimisation) */
1809  var mTypeData = dt.oApi._fnGetCellData( dt, aDataIndex[j], i, 'display' );
1810  if ( oConfig.fnCellRender )
1811  {
1812  sLoopData = oConfig.fnCellRender( mTypeData, i, tr, aDataIndex[j] )+"";
1813  }
1814  else if ( typeof mTypeData == "string" )
1815  {
1816  /* Strip newlines, replace img tags with alt attr. and finally strip html... */
1817  sLoopData = mTypeData.replace(/\n/g," ");
1818  sLoopData =
1819  sLoopData.replace(/<img.*?\s+alt\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+)).*?>/gi,
1820  '$1$2$3');
1821  sLoopData = sLoopData.replace( /<.*?>/g, "" );
1822  }
1823  else
1824  {
1825  sLoopData = mTypeData+"";
1826  }
1827 
1828  /* Trim and clean the data */
1829  sLoopData = sLoopData.replace(/^\s+/, '').replace(/\s+$/, '');
1830  sLoopData = this._fnHtmlDecode( sLoopData );
1831 
1832  /* Bound it and add it to the total data */
1833  aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
1834  }
1835  }
1836 
1837  aData.push( aRow.join(oConfig.sFieldSeperator) );
1838 
1839  /* Details rows from fnOpen */
1840  if ( oConfig.bOpenRows )
1841  {
1842  arr = $.grep(dt.aoOpenRows, function(o) { return o.nParent === tr; });
1843 
1844  if ( arr.length === 1 )
1845  {
1846  sLoopData = this._fnBoundData( $('td', arr[0].nTr).html(), oConfig.sFieldBoundary, regex );
1847  aData.push( sLoopData );
1848  }
1849  }
1850  }
1851 
1852  /*
1853  * Footer
1854  */
1855  if ( oConfig.bFooter && dt.nTFoot !== null )
1856  {
1857  aRow = [];
1858 
1859  for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1860  {
1861  if ( aColumnsInc[i] && dt.aoColumns[i].nTf !== null )
1862  {
1863  sLoopData = dt.aoColumns[i].nTf.innerHTML.replace(/\n/g," ").replace( /<.*?>/g, "" );
1864  sLoopData = this._fnHtmlDecode( sLoopData );
1865 
1866  aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
1867  }
1868  }
1869 
1870  aData.push( aRow.join(oConfig.sFieldSeperator) );
1871  }
1872 
1873  var _sLastData = aData.join( this._fnNewline(oConfig) );
1874  return _sLastData;
1875  },
1876 
1877 
1888  "_fnBoundData": function ( sData, sBoundary, regex )
1889  {
1890  if ( sBoundary === "" )
1891  {
1892  return sData;
1893  }
1894  else
1895  {
1896  return sBoundary + sData.replace(regex, sBoundary+sBoundary) + sBoundary;
1897  }
1898  },
1899 
1900 
1909  "_fnChunkData": function ( sData, iSize )
1910  {
1911  var asReturn = [];
1912  var iStrlen = sData.length;
1913 
1914  for ( var i=0 ; i<iStrlen ; i+=iSize )
1915  {
1916  if ( i+iSize < iStrlen )
1917  {
1918  asReturn.push( sData.substring( i, i+iSize ) );
1919  }
1920  else
1921  {
1922  asReturn.push( sData.substring( i, iStrlen ) );
1923  }
1924  }
1925 
1926  return asReturn;
1927  },
1928 
1929 
1937  "_fnHtmlDecode": function ( sData )
1938  {
1939  if ( sData.indexOf('&') === -1 )
1940  {
1941  return sData;
1942  }
1943 
1944  var n = document.createElement('div');
1945 
1946  return sData.replace( /&([^\s]*?);/g, function( match, match2 ) {
1947  if ( match.substr(1, 1) === '#' )
1948  {
1949  return String.fromCharCode( Number(match2.substr(1)) );
1950  }
1951  else
1952  {
1953  n.innerHTML = match;
1954  return n.childNodes[0].nodeValue;
1955  }
1956  } );
1957  },
1958 
1959 
1960 
1961  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1962  * Printing functions
1963  */
1964 
1973  "_fnPrintStart": function ( oConfig )
1974  {
1975  var that = this;
1976  var oSetDT = this.s.dt;
1977 
1978  /* Parse through the DOM hiding everything that isn't needed for the table */
1979  this._fnPrintHideNodes( oSetDT.nTable );
1980 
1981  /* Show the whole table */
1982  this.s.print.saveStart = oSetDT._iDisplayStart;
1983  this.s.print.saveLength = oSetDT._iDisplayLength;
1984 
1985  if ( oConfig.bShowAll )
1986  {
1987  oSetDT._iDisplayStart = 0;
1988  oSetDT._iDisplayLength = -1;
1989  if ( oSetDT.oApi._fnCalculateEnd ) {
1990  oSetDT.oApi._fnCalculateEnd( oSetDT );
1991  }
1992  oSetDT.oApi._fnDraw( oSetDT );
1993  }
1994 
1995  /* Adjust the display for scrolling which might be done by DataTables */
1996  if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
1997  {
1998  this._fnPrintScrollStart( oSetDT );
1999 
2000  // If the table redraws while in print view, the DataTables scrolling
2001  // setup would hide the header, so we need to readd it on draw
2002  $(this.s.dt.nTable).bind('draw.DTTT_Print', function () {
2003  that._fnPrintScrollStart( oSetDT );
2004  } );
2005  }
2006 
2007  /* Remove the other DataTables feature nodes - but leave the table! and info div */
2008  var anFeature = oSetDT.aanFeatures;
2009  for ( var cFeature in anFeature )
2010  {
2011  if ( cFeature != 'i' && cFeature != 't' && cFeature.length == 1 )
2012  {
2013  for ( var i=0, iLen=anFeature[cFeature].length ; i<iLen ; i++ )
2014  {
2015  this.dom.print.hidden.push( {
2016  "node": anFeature[cFeature][i],
2017  "display": "block"
2018  } );
2019  anFeature[cFeature][i].style.display = "none";
2020  }
2021  }
2022  }
2023 
2024  /* Print class can be used for styling */
2025  $(document.body).addClass( this.classes.print.body );
2026 
2027  /* Show information message to let the user know what is happening */
2028  if ( oConfig.sInfo !== "" )
2029  {
2030  this.fnInfo( oConfig.sInfo, 3000 );
2031  }
2032 
2033  /* Add a message at the top of the page */
2034  if ( oConfig.sMessage )
2035  {
2036  $('<div/>')
2037  .addClass( this.classes.print.message )
2038  .html( oConfig.sMessage )
2039  .prependTo( 'body' );
2040  }
2041 
2042  /* Cache the scrolling and the jump to the top of the page */
2043  this.s.print.saveScroll = $(window).scrollTop();
2044  window.scrollTo( 0, 0 );
2045 
2046  /* Bind a key event listener to the document for the escape key -
2047  * it is removed in the callback
2048  */
2049  $(document).bind( "keydown.DTTT", function(e) {
2050  /* Only interested in the escape key */
2051  if ( e.keyCode == 27 )
2052  {
2053  e.preventDefault();
2054  that._fnPrintEnd.call( that, e );
2055  }
2056  } );
2057  },
2058 
2059 
2067  "_fnPrintEnd": function ( e )
2068  {
2069  var that = this;
2070  var oSetDT = this.s.dt;
2071  var oSetPrint = this.s.print;
2072  var oDomPrint = this.dom.print;
2073 
2074  /* Show all hidden nodes */
2075  this._fnPrintShowNodes();
2076 
2077  /* Restore DataTables' scrolling */
2078  if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
2079  {
2080  $(this.s.dt.nTable).unbind('draw.DTTT_Print');
2081 
2082  this._fnPrintScrollEnd();
2083  }
2084 
2085  /* Restore the scroll */
2086  window.scrollTo( 0, oSetPrint.saveScroll );
2087 
2088  /* Drop the print message */
2089  $('div.'+this.classes.print.message).remove();
2090 
2091  /* Styling class */
2092  $(document.body).removeClass( this.classes.print.body );
2093 
2094  /* Restore the table length */
2095  oSetDT._iDisplayStart = oSetPrint.saveStart;
2096  oSetDT._iDisplayLength = oSetPrint.saveLength;
2097  if ( oSetDT.oApi._fnCalculateEnd ) {
2098  oSetDT.oApi._fnCalculateEnd( oSetDT );
2099  }
2100  oSetDT.oApi._fnDraw( oSetDT );
2101 
2102  $(document).unbind( "keydown.DTTT" );
2103  },
2104 
2105 
2111  "_fnPrintScrollStart": function ()
2112  {
2113  var
2114  oSetDT = this.s.dt,
2115  nScrollHeadInner = oSetDT.nScrollHead.getElementsByTagName('div')[0],
2116  nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
2117  nScrollBody = oSetDT.nTable.parentNode,
2118  nTheadSize, nTfootSize;
2119 
2120  /* Copy the header in the thead in the body table, this way we show one single table when
2121  * in print view. Note that this section of code is more or less verbatim from DT 1.7.0
2122  */
2123  nTheadSize = oSetDT.nTable.getElementsByTagName('thead');
2124  if ( nTheadSize.length > 0 )
2125  {
2126  oSetDT.nTable.removeChild( nTheadSize[0] );
2127  }
2128 
2129  if ( oSetDT.nTFoot !== null )
2130  {
2131  nTfootSize = oSetDT.nTable.getElementsByTagName('tfoot');
2132  if ( nTfootSize.length > 0 )
2133  {
2134  oSetDT.nTable.removeChild( nTfootSize[0] );
2135  }
2136  }
2137 
2138  nTheadSize = oSetDT.nTHead.cloneNode(true);
2139  oSetDT.nTable.insertBefore( nTheadSize, oSetDT.nTable.childNodes[0] );
2140 
2141  if ( oSetDT.nTFoot !== null )
2142  {
2143  nTfootSize = oSetDT.nTFoot.cloneNode(true);
2144  oSetDT.nTable.insertBefore( nTfootSize, oSetDT.nTable.childNodes[1] );
2145  }
2146 
2147  /* Now adjust the table's viewport so we can actually see it */
2148  if ( oSetDT.oScroll.sX !== "" )
2149  {
2150  oSetDT.nTable.style.width = $(oSetDT.nTable).outerWidth()+"px";
2151  nScrollBody.style.width = $(oSetDT.nTable).outerWidth()+"px";
2152  nScrollBody.style.overflow = "visible";
2153  }
2154 
2155  if ( oSetDT.oScroll.sY !== "" )
2156  {
2157  nScrollBody.style.height = $(oSetDT.nTable).outerHeight()+"px";
2158  nScrollBody.style.overflow = "visible";
2159  }
2160  },
2161 
2162 
2169  "_fnPrintScrollEnd": function ()
2170  {
2171  var
2172  oSetDT = this.s.dt,
2173  nScrollBody = oSetDT.nTable.parentNode;
2174 
2175  if ( oSetDT.oScroll.sX !== "" )
2176  {
2177  nScrollBody.style.width = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sX );
2178  nScrollBody.style.overflow = "auto";
2179  }
2180 
2181  if ( oSetDT.oScroll.sY !== "" )
2182  {
2183  nScrollBody.style.height = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sY );
2184  nScrollBody.style.overflow = "auto";
2185  }
2186  },
2187 
2188 
2195  "_fnPrintShowNodes": function ( )
2196  {
2197  var anHidden = this.dom.print.hidden;
2198 
2199  for ( var i=0, iLen=anHidden.length ; i<iLen ; i++ )
2200  {
2201  anHidden[i].node.style.display = anHidden[i].display;
2202  }
2203  anHidden.splice( 0, anHidden.length );
2204  },
2205 
2206 
2215  "_fnPrintHideNodes": function ( nNode )
2216  {
2217  var anHidden = this.dom.print.hidden;
2218 
2219  var nParent = nNode.parentNode;
2220  var nChildren = nParent.childNodes;
2221  for ( var i=0, iLen=nChildren.length ; i<iLen ; i++ )
2222  {
2223  if ( nChildren[i] != nNode && nChildren[i].nodeType == 1 )
2224  {
2225  /* If our node is shown (don't want to show nodes which were previously hidden) */
2226  var sDisplay = $(nChildren[i]).css("display");
2227  if ( sDisplay != "none" )
2228  {
2229  /* Cache the node and it's previous state so we can restore it */
2230  anHidden.push( {
2231  "node": nChildren[i],
2232  "display": sDisplay
2233  } );
2234  nChildren[i].style.display = "none";
2235  }
2236  }
2237  }
2238 
2239  if ( nParent.nodeName.toUpperCase() != "BODY" )
2240  {
2241  this._fnPrintHideNodes( nParent );
2242  }
2243  }
2244 };
2245 
2246 
2247 
2248 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2249  * Static variables
2250  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2251 
2260 TableTools._aInstances = [];
2261 
2262 
2269 TableTools._aListeners = [];
2270 
2271 
2272 
2273 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2274  * Static methods
2275  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2276 
2283 TableTools.fnGetMasters = function ()
2284 {
2285  var a = [];
2286  for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2287  {
2288  if ( TableTools._aInstances[i].s.master )
2289  {
2290  a.push( TableTools._aInstances[i] );
2291  }
2292  }
2293  return a;
2294 };
2295 
2302 TableTools.fnGetInstance = function ( node )
2303 {
2304  if ( typeof node != 'object' )
2305  {
2306  node = document.getElementById(node);
2307  }
2308 
2309  for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2310  {
2311  if ( TableTools._aInstances[i].s.master && TableTools._aInstances[i].dom.table == node )
2312  {
2313  return TableTools._aInstances[i];
2314  }
2315  }
2316  return null;
2317 };
2318 
2319 
2330 TableTools._fnEventListen = function ( that, type, fn )
2331 {
2332  TableTools._aListeners.push( {
2333  "that": that,
2334  "type": type,
2335  "fn": fn
2336  } );
2337 };
2338 
2339 
2352 TableTools._fnEventDispatch = function ( that, type, node, selected )
2353 {
2354  var listeners = TableTools._aListeners;
2355  for ( var i=0, iLen=listeners.length ; i<iLen ; i++ )
2356  {
2357  if ( that.dom.table == listeners[i].that.dom.table && listeners[i].type == type )
2358  {
2359  listeners[i].fn( node, selected );
2360  }
2361  }
2362 };
2363 
2364 
2365 
2366 
2367 
2368 
2369 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2370  * Constants
2371  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2372 
2373 
2374 
2375 TableTools.buttonBase = {
2376  // Button base
2377  "sAction": "text",
2378  "sTag": "default",
2379  "sLinerTag": "default",
2380  "sButtonClass": "DTTT_button_text",
2381  "sButtonText": "Button text",
2382  "sTitle": "",
2383  "sToolTip": "",
2384 
2385  // Common button specific options
2386  "sCharSet": "utf8",
2387  "bBomInc": false,
2388  "sFileName": "*.csv",
2389  "sFieldBoundary": "",
2390  "sFieldSeperator": "\t",
2391  "sNewLine": "auto",
2392  "mColumns": "all", /* "all", "visible", "hidden" or array of column integers */
2393  "bHeader": true,
2394  "bFooter": true,
2395  "bOpenRows": false,
2396  "bSelectedOnly": false,
2397  "oSelectorOpts": undefined, // See http://datatables.net/docs/DataTables/1.9.4/#$ for full options
2398 
2399  // Callbacks
2400  "fnMouseover": null,
2401  "fnMouseout": null,
2402  "fnClick": null,
2403  "fnSelect": null,
2404  "fnComplete": null,
2405  "fnInit": null,
2406  "fnCellRender": null
2407 };
2408 
2409 
2413 TableTools.BUTTONS = {
2414  "csv": $.extend( {}, TableTools.buttonBase, {
2415  "sAction": "flash_save",
2416  "sButtonClass": "DTTT_button_csv",
2417  "sButtonText": "CSV",
2418  "sFieldBoundary": '"',
2419  "sFieldSeperator": ",",
2420  "fnClick": function( nButton, oConfig, flash ) {
2421  this.fnSetText( flash, this.fnGetTableData(oConfig) );
2422  }
2423  } ),
2424 
2425  "xls": $.extend( {}, TableTools.buttonBase, {
2426  "sAction": "flash_save",
2427  "sCharSet": "utf16le",
2428  "bBomInc": true,
2429  "sButtonClass": "DTTT_button_xls",
2430  "sButtonText": "Excel",
2431  "fnClick": function( nButton, oConfig, flash ) {
2432  this.fnSetText( flash, this.fnGetTableData(oConfig) );
2433  }
2434  } ),
2435 
2436  "copy": $.extend( {}, TableTools.buttonBase, {
2437  "sAction": "flash_copy",
2438  "sButtonClass": "DTTT_button_copy",
2439  "sButtonText": "Copy",
2440  "fnClick": function( nButton, oConfig, flash ) {
2441  this.fnSetText( flash, this.fnGetTableData(oConfig) );
2442  },
2443  "fnComplete": function(nButton, oConfig, flash, text) {
2444  var lines = text.split('\n').length;
2445  if (oConfig.bHeader) lines--;
2446  if (this.s.dt.nTFoot !== null && oConfig.bFooter) lines--;
2447  var plural = (lines==1) ? "" : "s";
2448  this.fnInfo( '<h6>Table copied</h6>'+
2449  '<p>Copied '+lines+' row'+plural+' to the clipboard.</p>',
2450  1500
2451  );
2452  }
2453  } ),
2454 
2455  "pdf": $.extend( {}, TableTools.buttonBase, {
2456  "sAction": "flash_pdf",
2457  "sNewLine": "\n",
2458  "sFileName": "*.pdf",
2459  "sButtonClass": "DTTT_button_pdf",
2460  "sButtonText": "PDF",
2461  "sPdfOrientation": "portrait",
2462  "sPdfSize": "A4",
2463  "sPdfMessage": "",
2464  "fnClick": function( nButton, oConfig, flash ) {
2465  this.fnSetText( flash,
2466  "title:"+ this.fnGetTitle(oConfig) +"\n"+
2467  "message:"+ oConfig.sPdfMessage +"\n"+
2468  "colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+
2469  "orientation:"+ oConfig.sPdfOrientation +"\n"+
2470  "size:"+ oConfig.sPdfSize +"\n"+
2471  "--/TableToolsOpts--\n" +
2472  this.fnGetTableData(oConfig)
2473  );
2474  }
2475  } ),
2476 
2477  "print": $.extend( {}, TableTools.buttonBase, {
2478  "sInfo": "<h6>Print view</h6><p>Please use your browser's print function to "+
2479  "print this table. Press escape when finished.</p>",
2480  "sMessage": null,
2481  "bShowAll": true,
2482  "sToolTip": "View print view",
2483  "sButtonClass": "DTTT_button_print",
2484  "sButtonText": "Print",
2485  "fnClick": function ( nButton, oConfig ) {
2486  this.fnPrint( true, oConfig );
2487  }
2488  } ),
2489 
2490  "text": $.extend( {}, TableTools.buttonBase ),
2491 
2492  "select": $.extend( {}, TableTools.buttonBase, {
2493  "sButtonText": "Select button",
2494  "fnSelect": function( nButton, oConfig ) {
2495  if ( this.fnGetSelected().length !== 0 ) {
2496  $(nButton).removeClass( this.classes.buttons.disabled );
2497  } else {
2498  $(nButton).addClass( this.classes.buttons.disabled );
2499  }
2500  },
2501  "fnInit": function( nButton, oConfig ) {
2502  $(nButton).addClass( this.classes.buttons.disabled );
2503  }
2504  } ),
2505 
2506  "select_single": $.extend( {}, TableTools.buttonBase, {
2507  "sButtonText": "Select button",
2508  "fnSelect": function( nButton, oConfig ) {
2509  var iSelected = this.fnGetSelected().length;
2510  if ( iSelected == 1 ) {
2511  $(nButton).removeClass( this.classes.buttons.disabled );
2512  } else {
2513  $(nButton).addClass( this.classes.buttons.disabled );
2514  }
2515  },
2516  "fnInit": function( nButton, oConfig ) {
2517  $(nButton).addClass( this.classes.buttons.disabled );
2518  }
2519  } ),
2520 
2521  "select_all": $.extend( {}, TableTools.buttonBase, {
2522  "sButtonText": "Select all",
2523  "fnClick": function( nButton, oConfig ) {
2524  this.fnSelectAll();
2525  },
2526  "fnSelect": function( nButton, oConfig ) {
2527  if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) {
2528  $(nButton).addClass( this.classes.buttons.disabled );
2529  } else {
2530  $(nButton).removeClass( this.classes.buttons.disabled );
2531  }
2532  }
2533  } ),
2534 
2535  "select_none": $.extend( {}, TableTools.buttonBase, {
2536  "sButtonText": "Deselect all",
2537  "fnClick": function( nButton, oConfig ) {
2538  this.fnSelectNone();
2539  },
2540  "fnSelect": function( nButton, oConfig ) {
2541  if ( this.fnGetSelected().length !== 0 ) {
2542  $(nButton).removeClass( this.classes.buttons.disabled );
2543  } else {
2544  $(nButton).addClass( this.classes.buttons.disabled );
2545  }
2546  },
2547  "fnInit": function( nButton, oConfig ) {
2548  $(nButton).addClass( this.classes.buttons.disabled );
2549  }
2550  } ),
2551 
2552  "ajax": $.extend( {}, TableTools.buttonBase, {
2553  "sAjaxUrl": "/xhr.php",
2554  "sButtonText": "Ajax button",
2555  "fnClick": function( nButton, oConfig ) {
2556  var sData = this.fnGetTableData(oConfig);
2557  $.ajax( {
2558  "url": oConfig.sAjaxUrl,
2559  "data": [
2560  { "name": "tableData", "value": sData }
2561  ],
2562  "success": oConfig.fnAjaxComplete,
2563  "dataType": "json",
2564  "type": "POST",
2565  "cache": false,
2566  "error": function () {
2567  alert( "Error detected when sending table data to server" );
2568  }
2569  } );
2570  },
2571  "fnAjaxComplete": function( json ) {
2572  alert( 'Ajax complete' );
2573  }
2574  } ),
2575 
2576  "div": $.extend( {}, TableTools.buttonBase, {
2577  "sAction": "div",
2578  "sTag": "div",
2579  "sButtonClass": "DTTT_nonbutton",
2580  "sButtonText": "Text button"
2581  } ),
2582 
2583  "collection": $.extend( {}, TableTools.buttonBase, {
2584  "sAction": "collection",
2585  "sButtonClass": "DTTT_button_collection",
2586  "sButtonText": "Collection",
2587  "fnClick": function( nButton, oConfig ) {
2588  this._fnCollectionShow(nButton, oConfig);
2589  }
2590  } )
2591 };
2592 /*
2593  * on* callback parameters:
2594  * 1. node - button element
2595  * 2. object - configuration object for this button
2596  * 3. object - ZeroClipboard reference (flash button only)
2597  * 4. string - Returned string from Flash (flash button only - and only on 'complete')
2598  */
2599 
2600 // Alias to match the other plug-ins styling
2601 TableTools.buttons = TableTools.BUTTONS;
2602 
2603 
2609 TableTools.classes = {
2610  "container": "DTTT_container",
2611  "buttons": {
2612  "normal": "DTTT_button",
2613  "disabled": "DTTT_disabled"
2614  },
2615  "collection": {
2616  "container": "DTTT_collection",
2617  "background": "DTTT_collection_background",
2618  "buttons": {
2619  "normal": "DTTT_button",
2620  "disabled": "DTTT_disabled"
2621  }
2622  },
2623  "select": {
2624  "table": "DTTT_selectable",
2625  "row": "DTTT_selected selected"
2626  },
2627  "print": {
2628  "body": "DTTT_Print",
2629  "info": "DTTT_print_info",
2630  "message": "DTTT_PrintMessage"
2631  }
2632 };
2633 
2634 
2639 TableTools.classes_themeroller = {
2640  "container": "DTTT_container ui-buttonset ui-buttonset-multi",
2641  "buttons": {
2642  "normal": "DTTT_button ui-button ui-state-default"
2643  },
2644  "collection": {
2645  "container": "DTTT_collection ui-buttonset ui-buttonset-multi"
2646  }
2647 };
2648 
2649 
2653 TableTools.DEFAULTS = {
2654  "sSwfPath": "../swf/copy_csv_xls_pdf.swf",
2655  "sRowSelect": "none",
2656  "sRowSelector": "tr",
2657  "sSelectedClass": null,
2658  "fnPreRowSelect": null,
2659  "fnRowSelected": null,
2660  "fnRowDeselected": null,
2661  "aButtons": [ "copy", "csv", "xls", "pdf", "print" ],
2662  "oTags": {
2663  "container": "div",
2664  "button": "a", // We really want to use buttons here, but Firefox and IE ignore the
2665  // click on the Flash element in the button (but not mouse[in|out]).
2666  "liner": "span",
2667  "collection": {
2668  "container": "div",
2669  "button": "a",
2670  "liner": "span"
2671  }
2672  }
2673 };
2674 
2675 // Alias to match the other plug-ins
2676 TableTools.defaults = TableTools.DEFAULTS;
2677 
2678 
2685 TableTools.prototype.CLASS = "TableTools";
2686 
2687 
2694 TableTools.version = "2.2.4";
2695 
2696 
2697 
2698 // DataTables 1.10 API
2699 //
2700 // This will be extended in a big way in in TableTools 3 to provide API methods
2701 // such as rows().select() and rows.selected() etc, but for the moment the
2702 // tabletools() method simply returns the instance.
2703 
2704 if ( $.fn.dataTable.Api ) {
2705  $.fn.dataTable.Api.register( 'tabletools()', function () {
2706  var tt = null;
2707 
2708  if ( this.context.length > 0 ) {
2709  tt = TableTools.fnGetInstance( this.context[0].nTable );
2710  }
2711 
2712  return tt;
2713  } );
2714 }
2715 
2716 
2717 
2718 
2719 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2720  * Initialisation
2721  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2722 
2723 /*
2724  * Register a new feature with DataTables
2725  */
2726 if ( typeof $.fn.dataTable == "function" &&
2727  typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
2728  $.fn.dataTableExt.fnVersionCheck('1.9.0') )
2729 {
2730  $.fn.dataTableExt.aoFeatures.push( {
2731  "fnInit": function( oDTSettings ) {
2732  var init = oDTSettings.oInit;
2733  var opts = init ?
2734  init.tableTools || init.oTableTools || {} :
2735  {};
2736 
2737  return new TableTools( oDTSettings.oInstance, opts ).dom.container;
2738  },
2739  "cFeature": "T",
2740  "sFeature": "TableTools"
2741  } );
2742 }
2743 else
2744 {
2745  alert( "Warning: TableTools requires DataTables 1.9.0 or newer - www.datatables.net/download");
2746 }
2747 
2748 $.fn.DataTable.TableTools = TableTools;
2749 
2750 })(jQuery, window, document);