2 * ©2010-2015 SpryMedia Ltd - datatables.net/license
7 * @description Controls for column visibility in DataTables
9 * @file dataTables.colReorder.js
10 * @author SpryMedia Ltd (www.sprymedia.co.uk)
11 * @contact www.sprymedia.co.uk/contact
12 * @copyright Copyright 2010-2015 SpryMedia Ltd.
14 * This source file is free software, available under the following license:
15 * MIT license - http://datatables.net/license/mit
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.
21 * For details please refer to: http://www.datatables.net
24 (function(window, document, undefined) {
27 var factory = function( $, DataTable ) {
31 * ColVis provides column visibility control for DataTables
35 * @param {object} DataTables settings object. With DataTables 1.10 this can
36 * also be and API instance, table node, jQuery collection or jQuery selector.
37 * @param {object} ColVis configuration options
39 var ColVis = function( oDTSettings, oInit )
41 /* Santiy check that we are a new instance */
42 if ( !this.CLASS || this.CLASS != "ColVis" )
44 alert( "Warning: ColVis must be initialised with the keyword 'new'" );
47 if ( typeof oInit == 'undefined' )
52 var camelToHungarian = $.fn.dataTable.camelToHungarian;
53 if ( camelToHungarian ) {
54 camelToHungarian( ColVis.defaults, ColVis.defaults, true );
55 camelToHungarian( ColVis.defaults, oInit );
59 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
60 * Public class variables
61 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
64 * @namespace Settings object which contains customisable information for
65 * ColVis instance. Augmented by ColVis.defaults
69 * DataTables settings object
77 * Customisation object
85 * Flag to say if the collection is hidden
93 * Store the original visibility settings so they could be restored
94 * @property abOriginal
103 * @namespace Common and useful DOM elements for the class instance
107 * Wrapper for the button - given back to DataTables as the node to insert
123 * Collection list node
124 * @property collection
131 * Background node used for shading the display and event capturing
132 * @property background
139 * Element to position over the activation button to catch mouse events when using mouseover
147 * List of button elements
155 * List of group button elements
156 * @property groupButtons
171 /* Store global reference */
172 ColVis.aInstances.push( this );
174 /* Constructor logic */
175 this.s.dt = $.fn.dataTable.Api ?
176 new $.fn.dataTable.Api( oDTSettings ).settings()[0] :
179 this._fnConstruct( oInit );
186 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
188 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
191 * Get the ColVis instance's control button so it can be injected into the
194 * @returns {node} ColVis button
198 return this.dom.wrapper;
202 * Alias of `rebuild` for backwards compatibility
205 "fnRebuild": function ()
211 * Rebuild the list of buttons for this instance (i.e. if there is a column
217 /* Remove the old buttons */
218 for ( var i=this.dom.buttons.length-1 ; i>=0 ; i-- ) {
219 this.dom.collection.removeChild( this.dom.buttons[i] );
221 this.dom.buttons.splice( 0, this.dom.buttons.length );
222 this.dom.groupButtons.splice(0, this.dom.groupButtons.length);
224 if ( this.dom.restore ) {
225 this.dom.restore.parentNode( this.dom.restore );
228 /* Re-add them (this is not the optimal way of doing this, it is fast and effective) */
230 this._fnAddButtons();
232 /* Update the checkboxes */
233 this._fnDrawCallback();
237 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
238 * Private methods (they are of course public in JS, but recommended as private)
239 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
243 * @method _fnConstruct
247 "_fnConstruct": function ( init )
249 this._fnApplyCustomisation( init );
253 this.dom.wrapper = document.createElement('div');
254 this.dom.wrapper.className = "ColVis";
256 this.dom.button = $( '<button />', {
257 'class': !this.s.dt.bJUI ?
258 "ColVis_Button ColVis_MasterButton" :
259 "ColVis_Button ColVis_MasterButton ui-button ui-state-default"
261 .append( '<span>'+this.s.buttonText+'</span>' )
262 .bind( this.s.activate=="mouseover" ? "mouseover" : "click", function (e) {
264 that._fnCollectionShow();
266 .appendTo( this.dom.wrapper )[0];
268 this.dom.catcher = this._fnDomCatcher();
269 this.dom.collection = this._fnDomCollection();
270 this.dom.background = this._fnDomBackground();
273 this._fnAddButtons();
275 /* Store the original visibility information */
276 for ( i=0, iLen=this.s.dt.aoColumns.length ; i<iLen ; i++ )
278 this.s.abOriginal.push( this.s.dt.aoColumns[i].bVisible );
281 /* Update on each draw */
282 this.s.dt.aoDrawCallback.push( {
284 that._fnDrawCallback.call( that );
289 /* If columns are reordered, then we need to update our exclude list and
290 * rebuild the displayed list
292 $(this.s.dt.oInstance).bind( 'column-reorder.dt', function ( e, oSettings, oReorder ) {
293 for ( i=0, iLen=that.s.aiExclude.length ; i<iLen ; i++ ) {
294 that.s.aiExclude[i] = oReorder.aiInvertMapping[ that.s.aiExclude[i] ];
297 var mStore = that.s.abOriginal.splice( oReorder.iFrom, 1 )[0];
298 that.s.abOriginal.splice( oReorder.iTo, 0, mStore );
303 $(this.s.dt.oInstance).bind( 'destroy.dt', function () {
304 $(that.dom.wrapper).remove();
307 // Set the initial state
308 this._fnDrawCallback();
313 * Apply any customisation to the settings from the DataTables initialisation
314 * @method _fnApplyCustomisation
318 "_fnApplyCustomisation": function ( init )
320 $.extend( true, this.s, ColVis.defaults, init );
322 // Slightly messy overlap for the camelCase notation
323 if ( ! this.s.showAll && this.s.bShowAll ) {
324 this.s.showAll = this.s.sShowAll;
327 if ( ! this.s.restore && this.s.bRestore ) {
328 this.s.restore = this.s.sRestore;
331 // CamelCase to Hungarian for the column groups
332 var groups = this.s.groups;
333 var hungarianGroups = this.s.aoGroups;
335 for ( var i=0, ien=groups.length ; i<ien ; i++ ) {
336 if ( groups[i].title ) {
337 hungarianGroups[i].sTitle = groups[i].title;
339 if ( groups[i].columns ) {
340 hungarianGroups[i].aiColumns = groups[i].columns;
348 * On each table draw, check the visibility checkboxes as needed. This allows any process to
349 * update the table's column visibility and ColVis will still be accurate.
350 * @method _fnDrawCallback
354 "_fnDrawCallback": function ()
356 var columns = this.s.dt.aoColumns;
357 var buttons = this.dom.buttons;
358 var groups = this.s.aoGroups;
361 for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
364 if ( button.__columnIdx !== undefined ) {
365 $('input', button).prop( 'checked', columns[ button.__columnIdx ].bVisible );
369 var allVisible = function ( columnIndeces ) {
370 for ( var k=0, kLen=columnIndeces.length ; k<kLen ; k++ )
372 if ( columns[columnIndeces[k]].bVisible === false ) { return false; }
376 var allHidden = function ( columnIndeces ) {
377 for ( var m=0 , mLen=columnIndeces.length ; m<mLen ; m++ )
379 if ( columns[columnIndeces[m]].bVisible === true ) { return false; }
384 for ( var j=0, jLen=groups.length ; j<jLen ; j++ )
386 if ( allVisible(groups[j].aiColumns) )
388 $('input', this.dom.groupButtons[j]).prop('checked', true);
389 $('input', this.dom.groupButtons[j]).prop('indeterminate', false);
391 else if ( allHidden(groups[j].aiColumns) )
393 $('input', this.dom.groupButtons[j]).prop('checked', false);
394 $('input', this.dom.groupButtons[j]).prop('indeterminate', false);
398 $('input', this.dom.groupButtons[j]).prop('indeterminate', true);
405 * Loop through the groups (provided in the settings) and create a button for each.
406 * @method _fnAddgroups
410 "_fnAddGroups": function ()
414 if ( typeof this.s.aoGroups != 'undefined' )
416 for ( var i=0, iLen=this.s.aoGroups.length ; i<iLen ; i++ )
418 nButton = this._fnDomGroupButton( i );
419 this.dom.groupButtons.push( nButton );
420 this.dom.buttons.push( nButton );
421 this.dom.collection.appendChild( nButton );
428 * Loop through the columns in the table and as a new button for each one.
429 * @method _fnAddButtons
433 "_fnAddButtons": function ()
437 columns = this.s.dt.aoColumns;
439 if ( $.inArray( 'all', this.s.aiExclude ) === -1 ) {
440 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
442 if ( $.inArray( i, this.s.aiExclude ) === -1 )
444 nButton = this._fnDomColumnButton( i );
445 nButton.__columnIdx = i;
446 this.dom.buttons.push( nButton );
451 if ( this.s.order === 'alpha' ) {
452 this.dom.buttons.sort( function ( a, b ) {
453 var titleA = columns[ a.__columnIdx ].sTitle;
454 var titleB = columns[ b.__columnIdx ].sTitle;
456 return titleA === titleB ?
464 if ( this.s.restore )
466 nButton = this._fnDomRestoreButton();
467 nButton.className += " ColVis_Restore";
468 this.dom.buttons.push( nButton );
471 if ( this.s.showAll )
473 nButton = this._fnDomShowXButton( this.s.showAll, true );
474 nButton.className += " ColVis_ShowAll";
475 this.dom.buttons.push( nButton );
478 if ( this.s.showNone )
480 nButton = this._fnDomShowXButton( this.s.showNone, false );
481 nButton.className += " ColVis_ShowNone";
482 this.dom.buttons.push( nButton );
485 $(this.dom.collection).append( this.dom.buttons );
490 * Create a button which allows a "restore" action
491 * @method _fnDomRestoreButton
492 * @returns {Node} Created button
495 "_fnDomRestoreButton": function ()
502 '<li class="ColVis_Special '+(dt.bJUI ? 'ui-button ui-state-default' : '')+'">'+
506 .click( function (e) {
507 for ( var i=0, iLen=that.s.abOriginal.length ; i<iLen ; i++ )
509 that.s.dt.oInstance.fnSetColumnVis( i, that.s.abOriginal[i], false );
511 that._fnAdjustOpenRows();
512 that.s.dt.oInstance.fnAdjustColumnSizing( false );
513 that.s.dt.oInstance.fnDraw( false );
519 * Create a button which allows show all and show node actions
520 * @method _fnDomShowXButton
521 * @returns {Node} Created button
524 "_fnDomShowXButton": function ( str, action )
531 '<li class="ColVis_Special '+(dt.bJUI ? 'ui-button ui-state-default' : '')+'">'+
535 .click( function (e) {
536 for ( var i=0, iLen=that.s.abOriginal.length ; i<iLen ; i++ )
538 if (that.s.aiExclude.indexOf(i) === -1)
540 that.s.dt.oInstance.fnSetColumnVis( i, action, false );
543 that._fnAdjustOpenRows();
544 that.s.dt.oInstance.fnAdjustColumnSizing( false );
545 that.s.dt.oInstance.fnDraw( false );
551 * Create the DOM for a show / hide group button
552 * @method _fnDomGroupButton
553 * @param {int} i Group in question, order based on that provided in settings
554 * @returns {Node} Created button
557 "_fnDomGroupButton": function ( i )
562 oGroup = this.s.aoGroups[i];
565 '<li class="ColVis_Special '+(dt.bJUI ? 'ui-button ui-state-default' : '')+'">'+
567 '<input type="checkbox" />'+
568 '<span>'+oGroup.sTitle+'</span>'+
572 .click( function (e) {
573 var showHide = !$('input', this).is(":checked");
574 if ( e.target.nodeName.toLowerCase() !== "li" )
576 showHide = ! showHide;
579 for ( var j=0 ; j < oGroup.aiColumns.length ; j++ )
581 that.s.dt.oInstance.fnSetColumnVis( oGroup.aiColumns[j], showHide );
588 * Create the DOM for a show / hide button
589 * @method _fnDomColumnButton
590 * @param {int} i Column in question
591 * @returns {Node} Created button
594 "_fnDomColumnButton": function ( i )
598 column = this.s.dt.aoColumns[i],
601 var title = this.s.fnLabel===null ?
603 this.s.fnLabel( i, column.sTitle, column.nTh );
606 '<li '+(dt.bJUI ? 'class="ui-button ui-state-default"' : '')+'>'+
608 '<input type="checkbox" />'+
609 '<span>'+title+'</span>'+
613 .click( function (e) {
614 var showHide = !$('input', this).is(":checked");
615 if ( e.target.nodeName.toLowerCase() !== "li" )
617 if ( e.target.nodeName.toLowerCase() == "input" || that.s.fnStateChange === null )
619 showHide = ! showHide;
623 /* Need to consider the case where the initialiser created more than one table - change the
624 * API index that DataTables is using
626 var oldIndex = $.fn.dataTableExt.iApiIndex;
627 $.fn.dataTableExt.iApiIndex = that._fnDataTablesApiIndex.call(that);
629 // Optimisation for server-side processing when scrolling - don't do a full redraw
630 if ( dt.oFeatures.bServerSide )
632 that.s.dt.oInstance.fnSetColumnVis( i, showHide, false );
633 that.s.dt.oInstance.fnAdjustColumnSizing( false );
634 if (dt.oScroll.sX !== "" || dt.oScroll.sY !== "" )
636 that.s.dt.oInstance.oApi._fnScrollDraw( that.s.dt );
638 that._fnDrawCallback();
642 that.s.dt.oInstance.fnSetColumnVis( i, showHide );
645 $.fn.dataTableExt.iApiIndex = oldIndex; /* Restore */
647 if ( that.s.fnStateChange !== null )
649 if ( e.target.nodeName.toLowerCase() == "span" )
653 that.s.fnStateChange.call( that, i, showHide );
660 * Get the position in the DataTables instance array of the table for this
662 * @method _fnDataTablesApiIndex
663 * @returns {int} Index
666 "_fnDataTablesApiIndex": function ()
668 for ( var i=0, iLen=this.s.dt.oInstance.length ; i<iLen ; i++ )
670 if ( this.s.dt.oInstance[i] == this.s.dt.nTable )
680 * Create the element used to contain list the columns (it is shown and
682 * @method _fnDomCollection
683 * @returns {Node} div container for the collection
686 "_fnDomCollection": function ()
689 'class': !this.s.dt.bJUI ?
690 "ColVis_collection" :
691 "ColVis_collection ui-buttonset ui-buttonset-multi"
696 'position': ! this.s.bCssPosition ?
704 * An element to be placed on top of the activate button to catch events
705 * @method _fnDomCatcher
706 * @returns {Node} div container for the collection
709 "_fnDomCatcher": function ()
713 nCatcher = document.createElement('div');
714 nCatcher.className = "ColVis_catcher";
716 $(nCatcher).click( function () {
717 that._fnCollectionHide.call( that, null, null );
725 * Create the element used to shade the background, and capture hide events (it is shown and
727 * @method _fnDomBackground
728 * @returns {Node} div container for the background
731 "_fnDomBackground": function ()
735 var background = $('<div></div>')
736 .addClass( 'ColVis_collectionBackground' )
738 .click( function () {
739 that._fnCollectionHide.call( that, null, null );
742 /* When considering a mouse over action for the activation, we also consider a mouse out
743 * which is the same as a mouse over the background - without all the messing around of
744 * bubbling events. Use the catcher element to avoid messing around with bubbling
746 if ( this.s.activate == "mouseover" )
748 background.mouseover( function () {
749 that.s.overcollection = false;
750 that._fnCollectionHide.call( that, null, null );
754 return background[0];
759 * Show the show / hide list and the background
760 * @method _fnCollectionShow
764 "_fnCollectionShow": function ()
766 var that = this, i, iLen, iLeft;
767 var oPos = $(this.dom.button).offset();
768 var nHidden = this.dom.collection;
769 var nBackground = this.dom.background;
770 var iDivX = parseInt(oPos.left, 10);
771 var iDivY = parseInt(oPos.top + $(this.dom.button).outerHeight(), 10);
773 if ( ! this.s.bCssPosition )
775 nHidden.style.top = iDivY+"px";
776 nHidden.style.left = iDivX+"px";
784 nBackground.style.bottom ='0px';
785 nBackground.style.right = '0px';
787 var oStyle = this.dom.catcher.style;
788 oStyle.height = $(this.dom.button).outerHeight()+"px";
789 oStyle.width = $(this.dom.button).outerWidth()+"px";
790 oStyle.top = oPos.top+"px";
791 oStyle.left = iDivX+"px";
793 document.body.appendChild( nBackground );
794 document.body.appendChild( nHidden );
795 document.body.appendChild( this.dom.catcher );
797 /* This results in a very small delay for the end user but it allows the animation to be
798 * much smoother. If you don't want the animation, then the setTimeout can be removed
800 $(nHidden).animate({"opacity": 1}, that.s.iOverlayFade);
801 $(nBackground).animate({"opacity": 0.1}, that.s.iOverlayFade, 'linear', function () {
802 /* In IE6 if you set the checked attribute of a hidden checkbox, then this is not visually
803 * reflected. As such, we need to do it here, once it is visible. Unbelievable.
805 if ( $.browser && $.browser.msie && $.browser.version == "6.0" )
807 that._fnDrawCallback();
811 /* Visual corrections to try and keep the collection visible */
812 if ( !this.s.bCssPosition )
814 iLeft = ( this.s.sAlign=="left" ) ?
816 iDivX - $(nHidden).outerWidth() + $(this.dom.button).outerWidth();
818 nHidden.style.left = iLeft+"px";
820 var iDivWidth = $(nHidden).outerWidth();
821 var iDivHeight = $(nHidden).outerHeight();
822 var iDocWidth = $(document).width();
824 if ( iLeft + iDivWidth > iDocWidth )
826 nHidden.style.left = (iDocWidth-iDivWidth)+"px";
830 this.s.hidden = false;
835 * Hide the show / hide list and the background
836 * @method _fnCollectionHide
840 "_fnCollectionHide": function ( )
844 if ( !this.s.hidden && this.dom.collection !== null )
846 this.s.hidden = true;
848 $(this.dom.collection).animate({"opacity": 0}, that.s.iOverlayFade, function (e) {
849 this.style.display = "none";
852 $(this.dom.background).animate({"opacity": 0}, that.s.iOverlayFade, function (e) {
853 document.body.removeChild( that.dom.background );
854 document.body.removeChild( that.dom.catcher );
861 * Alter the colspan on any fnOpen rows
863 "_fnAdjustOpenRows": function ()
865 var aoOpen = this.s.dt.aoOpenRows;
866 var iVisible = this.s.dt.oApi._fnVisbleColumns( this.s.dt );
868 for ( var i=0, iLen=aoOpen.length ; i<iLen ; i++ ) {
869 aoOpen[i].nTr.getElementsByTagName('td')[0].colSpan = iVisible;
878 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
879 * Static object methods
880 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
883 * Rebuild the collection for a given table, or all tables if no parameter given
884 * @method ColVis.fnRebuild
886 * @param object oTable DataTable instance to consider - optional
889 ColVis.fnRebuild = function ( oTable )
892 if ( typeof oTable != 'undefined' )
894 nTable = $.fn.dataTable.Api ?
895 new $.fn.dataTable.Api( oTable ).table().node() :
896 oTable.fnSettings().nTable;
899 for ( var i=0, iLen=ColVis.aInstances.length ; i<iLen ; i++ )
901 if ( typeof oTable == 'undefined' || nTable == ColVis.aInstances[i].s.dt.nTable )
903 ColVis.aInstances[i].fnRebuild();
911 * Mode of activation. Can be 'click' or 'mouseover'
919 * Text used for the button
920 * @property buttonText
922 * @default Show / hide columns
924 buttonText: 'Show / hide columns',
927 * List of columns (integers) which should be excluded from the list
928 * @property aiExclude
935 * Show restore button
943 * Restore button text
946 * @default Restore original
948 sRestore: 'Restore original',
951 * Show Show-All button
959 * Show All button text
962 * @default Restore original
964 sShowAll: 'Show All',
967 * Position of the collection menu when shown - align "left" or "right"
975 * Callback function to tell the user when the state has changed
976 * @property fnStateChange
983 * Overlay animation duration in mS
984 * @property iOverlayFade
985 * @type integer|false
991 * Label callback for column names. Takes three parameters: 1. the
992 * column index, 2. the column title detected by DataTables and 3. the
993 * TH node for the column
1001 * Indicate if the column list should be positioned by Javascript,
1002 * visually below the button or allow CSS to do the positioning
1003 * @property bCssPosition
1007 bCssPosition: false,
1011 * @property aoGroups
1018 * Button ordering - 'alpha' (alphabetical) or 'column' (table column
1029 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1030 * Static object properties
1031 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1034 * Collection of all ColVis instances
1035 * @property ColVis.aInstances
1040 ColVis.aInstances = [];
1046 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1048 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1051 * Name of this class
1056 ColVis.prototype.CLASS = "ColVis";
1065 ColVis.VERSION = "1.1.2";
1066 ColVis.prototype.VERSION = ColVis.VERSION;
1072 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1074 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1077 * Register a new feature with DataTables
1079 if ( typeof $.fn.dataTable == "function" &&
1080 typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
1081 $.fn.dataTableExt.fnVersionCheck('1.7.0') )
1083 $.fn.dataTableExt.aoFeatures.push( {
1084 "fnInit": function( oDTSettings ) {
1085 var init = oDTSettings.oInit;
1086 var colvis = new ColVis( oDTSettings, init.colVis || init.oColVis || {} );
1087 return colvis.button();
1090 "sFeature": "ColVis"
1095 alert( "Warning: ColVis requires DataTables 1.7 or greater - www.datatables.net/download");
1099 // Make ColVis accessible from the DataTables instance
1100 $.fn.dataTable.ColVis = ColVis;
1101 $.fn.DataTable.ColVis = ColVis;
1108 // Define as an AMD module if possible
1109 if ( typeof define === 'function' && define.amd ) {
1110 define( ['jquery', 'datatables'], factory );
1112 else if ( typeof exports === 'object' ) {
1114 factory( require('jquery'), require('datatables') );
1116 else if ( jQuery && !jQuery.fn.dataTable.ColVis ) {
1117 // Otherwise simply initialise as normal, stopping multiple evaluation
1118 factory( jQuery, jQuery.fn.dataTable );
1122 })(window, document);