1 /* 2 * File: ColVis.js 3 * Version: 1.0.1 4 * CVS: $Id$ 5 * Description: Controls for column visiblity in DataTables 6 * Author: Allan Jardine (www.sprymedia.co.uk) 7 * Created: Wed Sep 15 18:23:29 BST 2010 8 * Modified: $Date$ by $Author$ 9 * Language: Javascript 10 * License: LGPL 11 * Project: Just a little bit of fun :-) 12 * Contact: www.sprymedia.co.uk/contact 13 * 14 * Copyright 2010 Allan Jardine, all rights reserved. 15 * 16 */ 17 18 (function($) { 19 20 /** 21 * ColVis provides column visiblity control for DataTables 22 * @class ColVis 23 * @constructor 24 * @param {object} DataTables settings object 25 */ 26 ColVis = function( oDTSettings ) 27 { 28 /* Santiy check that we are a new instance */ 29 if ( !this.CLASS || this.CLASS != "ColVis" ) 30 { 31 alert( "Warning: ColVis must be initialised with the keyword 'new'" ); 32 } 33 34 35 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 36 * Public class variables 37 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 38 39 /** 40 * @namespace Settings object which contains customisable information for ColVis instance 41 */ 42 this.s = { 43 /** 44 * DataTables settings object 45 * @property dt 46 * @type Object 47 * @default null 48 */ 49 dt: null, 50 51 /** 52 * Mode of activation. Can be 'click' or 'mouseover' 53 * @property activate 54 * @type String 55 * @default click 56 */ 57 activate: "click", 58 59 /** 60 * Text used for the button 61 * @property buttonText 62 * @type String 63 * @default Show / hide columns 64 */ 65 buttonText: "Show / hide columns", 66 67 /** 68 * Flag to say if the collection is hidden 69 * @property hidden 70 * @type boolean 71 * @default true 72 */ 73 hidden: true, 74 75 /** 76 * List of columns (integers) which should be excluded from the list 77 * @property aiExclude 78 * @type Array 79 * @default [] 80 */ 81 aiExclude: [] 82 }; 83 84 85 /** 86 * @namespace Common and useful DOM elements for the class instance 87 */ 88 this.dom = { 89 /** 90 * Wrapper for the button - given back to DataTables as the node to insert 91 * @property wrapper 92 * @type Node 93 * @default null 94 */ 95 wrapper: null, 96 97 /** 98 * Activation button 99 * @property button 100 * @type Node 101 * @default null 102 */ 103 button: null, 104 105 /** 106 * Collection list node 107 * @property collection 108 * @type Node 109 * @default null 110 */ 111 collection: null, 112 113 /** 114 * Background node used for shading the display and event capturing 115 * @property background 116 * @type Node 117 * @default null 118 */ 119 background: null, 120 121 /** 122 * Element to position over the activation button to catch mouse events when using mouseover 123 * @property catcher 124 * @type Node 125 * @default null 126 */ 127 catcher: null, 128 129 /** 130 * List of button elements 131 * @property buttons 132 * @type Array 133 * @default [] 134 */ 135 buttons: [] 136 }; 137 138 139 140 141 142 /* Constructor logic */ 143 this.s.dt = oDTSettings; 144 this._fnConstruct(); 145 return this; 146 }; 147 148 149 150 ColVis.prototype = { 151 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 152 * Public methods 153 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 154 155 156 157 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 158 * Private methods (they are of course public in JS, but recommended as private) 159 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 160 161 /** 162 * Constructor logic 163 * @method _fnConstruct 164 * @returns void 165 * @private 166 */ 167 _fnConstruct: function () 168 { 169 this._fnApplyCustomisation(); 170 171 var that = this; 172 this.dom.wrapper = document.createElement('div'); 173 this.dom.wrapper.className = "ColVis TableTools"; 174 175 this.dom.button = this._fnDomBaseButton( this.s.buttonText ); 176 this.dom.wrapper.appendChild( this.dom.button ); 177 178 this.dom.catcher = this._fnDomCatcher(); 179 this.dom.collection = this._fnDomCollection(); 180 this.dom.background = this._fnDomBackground(); 181 182 this._fnAddButtons(); 183 184 this.s.dt.aoDrawCallback.push( { 185 fn: function () { 186 that._fnDrawCallback.call( that ); 187 }, 188 sName: "ColVis" 189 } ); 190 }, 191 192 193 /** 194 * Apply any customisation to the settings from the DataTables initialisation 195 * @method _fnApplyCustomisation 196 * @returns void 197 * @private 198 */ 199 _fnApplyCustomisation: function () 200 { 201 if ( typeof this.s.dt.oInit.oColVis != 'undefined' ) 202 { 203 var oConfig = this.s.dt.oInit.oColVis; 204 205 if ( typeof oConfig.activate != 'undefined' ) 206 { 207 this.s.activate = oConfig.activate; 208 } 209 210 if ( typeof oConfig.buttonText != 'undefined' ) 211 { 212 this.s.buttonText = oConfig.buttonText; 213 } 214 215 if ( typeof oConfig.aiExclude != 'undefined' ) 216 { 217 this.s.aiExclude = oConfig.aiExclude; 218 } 219 } 220 }, 221 222 223 /** 224 * On each table draw, check the visiblity checkboxes as needed. This allows any process to 225 * update the table's column visiblity and ColVis will still be accurate. 226 * @method _fnDrawCallback 227 * @returns void 228 * @private 229 */ 230 _fnDrawCallback: function () 231 { 232 var aoColumns = this.s.dt.aoColumns; 233 234 for ( var i=0, iLen=aoColumns.length ; i<iLen ; i++ ) 235 { 236 if ( this.dom.buttons[i] !== null ) 237 { 238 if ( aoColumns[i].bVisible ) 239 { 240 $('input', this.dom.buttons[i]).attr('checked','checked'); 241 } 242 else 243 { 244 $('input', this.dom.buttons[i]).removeAttr('checked'); 245 } 246 } 247 } 248 }, 249 250 251 /** 252 * Loop through the columns in the table and as a new button for each one. 253 * @method _fnAddButtons 254 * @returns void 255 * @private 256 */ 257 _fnAddButtons: function () 258 { 259 var 260 nButton, 261 sExclude = ","+this.s.aiExclude.join(',')+","; 262 263 for ( var i=0, iLen=this.s.dt.aoColumns.length ; i<iLen ; i++ ) 264 { 265 if ( sExclude.indexOf( ","+i+"," ) == -1 ) 266 { 267 nButton = this._fnDomColumnButton( i ); 268 this.dom.buttons.push( nButton ); 269 this.dom.collection.appendChild( nButton ); 270 } 271 else 272 { 273 this.dom.buttons.push( null ); 274 } 275 } 276 }, 277 278 279 /** 280 * Create the DOM for a show / hide button 281 * @method _fnDomColumnButton 282 * @param {int} i Column in question 283 * @returns {Node} Created button 284 * @private 285 */ 286 _fnDomColumnButton: function ( i ) 287 { 288 var 289 that = this, 290 oColumn = this.s.dt.aoColumns[i], 291 nButton = document.createElement('button'), 292 nSpan = document.createElement('span'); 293 294 nButton.className = !this.s.dt.bJUI ? "ColVis_Button TableTools_Button" : 295 "ColVis_Button TableTools_Button ui-button ui-state-default"; 296 nButton.appendChild( nSpan ); 297 $(nSpan).html( 298 '<span class="ColVis_radio"><input type="checkbox"></span>'+ 299 '<span class="ColVis_title">'+oColumn.sTitle+'</span>' ); 300 301 $(nButton).click( function (e) { 302 var showHide = $('input',this).attr('checked')===true ? false : true; 303 if ( e.target.nodeName.toLowerCase() == "input" ) 304 { 305 showHide = $('input',this).attr('checked'); 306 } 307 that.s.dt.oInstance.fnSetColumnVis( i, showHide ); 308 } ); 309 310 return nButton; 311 }, 312 313 314 /** 315 * Create the DOM needed for the button and apply some base properties. All buttons start here 316 * @method _fnDomBaseButton 317 * @param {String} text Button text 318 * @returns {Node} DIV element for the button 319 * @private 320 */ 321 _fnDomBaseButton: function ( text ) 322 { 323 var 324 that = this, 325 nButton = document.createElement('button'), 326 nSpan = document.createElement('span'), 327 sEvent = this.s.activate=="mouseover" ? "mouseover" : "click"; 328 329 nButton.className = !this.s.dt.bJUI ? "ColVis_Button TableTools_Button" : 330 "ColVis_Button TableTools_Button ui-button ui-state-default"; 331 nButton.appendChild( nSpan ); 332 nSpan.innerHTML = text; 333 334 $(nButton).bind( sEvent, function (e) { 335 that._fnCollectionShow(); 336 e.preventDefault(); 337 } ); 338 339 return nButton; 340 }, 341 342 343 /** 344 * Create the element used to contain list the columns (it is shown and hidden as needed) 345 * @method _fnDomCollection 346 * @returns {Node} div container for the collection 347 * @private 348 */ 349 _fnDomCollection: function () 350 { 351 var that = this; 352 var nHidden = document.createElement('div'); 353 nHidden.style.display = "none"; 354 nHidden.className = !this.s.dt.bJUI ? "ColVis_collection TableTools_collection" : 355 "ColVis_collection TableTools_collection ui-buttonset ui-buttonset-multi"; 356 nHidden.style.position = "absolute"; 357 $(nHidden).css('opacity', 0); 358 359 return nHidden; 360 }, 361 362 363 /** 364 * An element to be placed on top of the activate button to catch events 365 * @method _fnDomCatcher 366 * @returns {Node} div container for the collection 367 * @private 368 */ 369 _fnDomCatcher: function () 370 { 371 var 372 that = this, 373 nCatcher = document.createElement('div'); 374 nCatcher.className = "ColVis_catcher TableTools_catcher"; 375 376 $(nCatcher).click( function () { 377 that._fnCollectionHide.call( that, null, null ); 378 } ); 379 380 return nCatcher; 381 }, 382 383 384 /** 385 * Create the element used to shade the background, and capture hide events (it is shown and 386 * hidden as needed) 387 * @method _fnDomBackground 388 * @returns {Node} div container for the background 389 * @private 390 */ 391 _fnDomBackground: function () 392 { 393 var that = this; 394 395 var nBackground = document.createElement('div'); 396 nBackground.style.position = "absolute"; 397 nBackground.style.left = "0px"; 398 nBackground.style.top = "0px"; 399 nBackground.className = "TableTools_collectionBackground"; 400 $(nBackground).css('opacity', 0); 401 402 $(nBackground).click( function () { 403 that._fnCollectionHide.call( that, null, null ); 404 } ); 405 406 /* When considering a mouse over action for the activation, we also consider a mouse out 407 * which is the same as a mouse over the background - without all the messing around of 408 * bubbling events. Use the catcher element to avoid messing around with bubbling 409 */ 410 if ( this.s.activate == "mouseover" ) 411 { 412 $(nBackground).mouseover( function () { 413 that.s.overcollection = false; 414 that._fnCollectionHide.call( that, null, null ); 415 } ); 416 } 417 418 return nBackground; 419 }, 420 421 422 /** 423 * Show the show / hide list and the background 424 * @method _fnCollectionShow 425 * @returns void 426 * @private 427 */ 428 _fnCollectionShow: function () 429 { 430 var that = this; 431 var oPos = $(this.dom.button).offset(); 432 var nHidden = this.dom.collection; 433 var nBackground = this.dom.background; 434 var iDivX = parseInt(oPos.left, 10); 435 var iDivY = parseInt(oPos.top + $(this.dom.button).outerHeight(), 10); 436 437 nHidden.style.left = iDivX+"px"; 438 nHidden.style.top = iDivY+"px"; 439 nHidden.style.display = "block"; 440 $(nHidden).css('opacity',0); 441 442 var iWinHeight = $(window).height(), iDocHeight = $(document).height(), 443 iWinWidth = $(window).width(), iDocWidth = $(document).width(); 444 445 nBackground.style.height = ((iWinHeight>iDocHeight)? iWinHeight : iDocHeight) +"px"; 446 nBackground.style.width = ((iWinWidth<iDocWidth)? iWinWidth : iDocWidth) +"px"; 447 448 var oStyle = this.dom.catcher.style; 449 oStyle.height = $(this.dom.button).outerHeight()+"px"; 450 oStyle.width = $(this.dom.button).outerWidth()+"px"; 451 oStyle.top = oPos.top+"px"; 452 oStyle.left = iDivX+"px"; 453 454 document.body.appendChild( nBackground ); 455 document.body.appendChild( nHidden ); 456 document.body.appendChild( this.dom.catcher ); 457 458 /* Visual corrections to try and keep the collection visible */ 459 var iDivWidth = $(nHidden).outerWidth(); 460 var iDivHeight = $(nHidden).outerHeight(); 461 462 if ( iDivX + iDivWidth > iDocWidth ) 463 { 464 nHidden.style.left = (iDocWidth-iDivWidth)+"px"; 465 } 466 467 if ( iDivY + iDivHeight > iDocHeight ) 468 { 469 nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px"; 470 } 471 472 473 /* This results in a very small delay for the end user but it allows the animation to be 474 * much smoother. If you don't want the animation, then the setTimeout can be removed 475 */ 476 setTimeout( function () { 477 $(nHidden).animate({opacity: 1}, 500); 478 $(nBackground).animate({opacity: 0.1}, 500); 479 }, 10 ); 480 481 this.s.hidden = false; 482 }, 483 484 485 /** 486 * Hide the show / hide list and the background 487 * @method _fnCollectionHide 488 * @returns void 489 * @private 490 */ 491 _fnCollectionHide: function ( ) 492 { 493 var that = this; 494 495 if ( !this.s.hidden && this.dom.collection !== null ) 496 { 497 this.s.hidden = true; 498 499 $(this.dom.collection).animate({opacity: 0}, 500, function (e) { 500 this.style.display = "none"; 501 } ); 502 503 $(this.dom.background).animate({opacity: 0}, 500, function (e) { 504 document.body.removeChild( that.dom.background ); 505 document.body.removeChild( that.dom.catcher ); 506 } ); 507 } 508 } 509 }; 510 511 512 513 514 515 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 516 * Constants 517 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 518 519 /** 520 * Name of this class 521 * @constant CLASS 522 * @type String 523 * @default ColVis 524 */ 525 ColVis.prototype.CLASS = "ColVis"; 526 527 528 /** 529 * ColVis version 530 * @constant VERSION 531 * @type String 532 * @default 1.0.0 533 */ 534 ColVis.VERSION = "1.0.1"; 535 ColVis.prototype.VERSION = ColVis.VERSION; 536 537 538 539 540 541 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 542 * Initialisation 543 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 544 545 /* 546 * Register a new feature with DataTables 547 */ 548 if ( typeof $.fn.dataTable == "function" && 549 typeof $.fn.dataTableExt.fnVersionCheck == "function" && 550 $.fn.dataTableExt.fnVersionCheck('1.7.0') ) 551 { 552 $.fn.dataTableExt.aoFeatures.push( { 553 fnInit: function( oDTSettings ) { 554 var tt = new ColVis( oDTSettings ); 555 return tt.dom.wrapper; 556 }, 557 cFeature: "C", 558 sFeature: "ColVis" 559 } ); 560 } 561 else 562 { 563 alert( "Warning: ColVis requires DataTables 1.7 or greater - www.datatables.net/download"); 564 } 565 })(jQuery);