Free cookie consent management tool by TermsFeed Policy Generator

source: branches/WebApplication/MVC2/HeuristicLabWeb.PluginHost/HLWebPluginHost/Content/jQuery/jQueryPlugins/DataTables-1.7.6/extras/FixedColumns/media/js/FixedColumns.js @ 6286

Last change on this file since 6286 was 6286, checked in by dkahn, 13 years ago

#1198 Added jQuery plus plugins

File size: 15.9 KB
RevLine 
[6286]1/*
2 * File:        FixedColumns.js
3 * Version:     1.1.0
4 * Description: "Fix" columns on the left of a scrolling DataTable
5 * Author:      Allan Jardine (www.sprymedia.co.uk)
6 * Created:     Sat Sep 18 09:28:54 BST 2010
7 * Language:    Javascript
8 * License:     GPL v2 or BSD 3 point style
9 * Project:     Just a little bit of fun - enjoy :-)
10 * Contact:     www.sprymedia.co.uk/contact
11 *
12 * Copyright 2010-2011 Allan Jardine, all rights reserved.
13 */
14
15var FixedColumns = function ( oDT, oInit ) {
16  /* Sanity check - you just know it will happen */
17  if ( typeof this._fnConstruct != 'function' )
18  {
19    alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
20    return;
21  }
22 
23  if ( typeof oInit == 'undefined' )
24  {
25    oInit = {};
26  }
27 
28  /**
29   * @namespace Settings object which contains customisable information for FixedColumns instance
30   */
31  this.s = {
32    /**
33     * DataTables settings objects
34     *  @property dt
35     *  @type     object
36     *  @default  null
37     */
38    "dt": oDT.fnSettings(),
39   
40    /**
41     * Number of left hand columns to fix in position
42     *  @property leftColumns
43     *  @type     int
44     *  @default  1
45     */
46    "leftColumns": 1,
47   
48    /**
49     * Number of right hand columns to fix in position
50     *  @property rightColumns
51     *  @type     int
52     *  @default  0
53     */
54    "rightColumns": 0,
55   
56    /**
57     * Store the heights of the rows for a draw. This can significantly speed up a draw where both
58     * left and right columns are fixed
59     *  @property heights
60     *  @type     array int
61     *  @default  0
62     */
63    "heights": []
64  };
65 
66 
67  /**
68   * @namespace Common and useful DOM elements for the class instance
69   */
70  this.dom = {
71    /**
72     * DataTables scrolling element
73     *  @property scroller
74     *  @type     node
75     *  @default  null
76     */
77    "scroller": null,
78       
79    /**
80     * Scroll container that DataTables has added
81     *  @property scrollContainer
82     *  @type     node
83     *  @default  null
84     */
85    "scrollContainer": null,
86   
87    /**
88     * DataTables header table
89     *  @property header
90     *  @type     node
91     *  @default  null
92     */
93    "header": null,
94   
95    /**
96     * DataTables body table
97     *  @property body
98     *  @type     node
99     *  @default  null
100     */
101    "body": null,
102   
103    /**
104     * DataTables footer table
105     *  @property footer
106     *  @type     node
107     *  @default  null
108     */
109    "footer": null,
110   
111    /**
112     * @namespace Cloned table nodes
113     */
114    "clone": {
115      /**
116       * @namespace Left column cloned table nodes
117       */
118      "left": {
119        /**
120         * Cloned header table
121         *  @property header
122         *  @type     node
123         *  @default  null
124         */
125        "header": null,
126       
127        /**
128         * Cloned body table
129         *  @property body
130         *  @type     node
131         *  @default  null
132         */
133        "body": null,
134       
135        /**
136         * Cloned footer table
137         *  @property footer
138         *  @type     node
139         *  @default  null
140         */
141        "footer": null
142      },
143     
144      /**
145       * @namespace Right column cloned table nodes
146       */
147      "right": {
148        /**
149         * Cloned header table
150         *  @property header
151         *  @type     node
152         *  @default  null
153         */
154        "header": null,
155       
156        /**
157         * Cloned body table
158         *  @property body
159         *  @type     node
160         *  @default  null
161         */
162        "body": null,
163       
164        /**
165         * Cloned footer table
166         *  @property footer
167         *  @type     node
168         *  @default  null
169         */
170        "footer": null
171      }
172    }
173  };
174 
175  /* Let's do it */
176  this._fnConstruct( oInit );
177};
178
179
180FixedColumns.prototype = {
181  /**
182   * Update the fixed columns - including headers and footers
183   *  @method  fnUpdate
184   *  @returns void
185   */
186  "fnUpdate": function ()
187  {
188    this._fnDraw( true );
189  },
190 
191 
192  /**
193   * Initialisation for FixedColumns
194   *  @method  _fnConstruct
195   *  @param   {Object} oInit User settings for initialisation
196   *  @returns void
197   */
198  "_fnConstruct": function ( oInit )
199  {
200    var that = this;
201   
202    /* Sanity checking */
203    if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
204         this.s.dt.oInstance.fnVersionCheck( '1.7.0' ) !== true )
205    {
206      alert( "FixedColumns 2 required DataTables 1.7.0 or later. "+
207        "Please upgrade your DataTables installation" );
208      return;
209    }
210   
211    if ( this.s.dt.oScroll.sX === "" )
212    {
213      this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
214        "x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
215        "column fixing when scrolling is not enabled" );
216      return;
217    }
218   
219    if ( typeof oInit.columns != 'undefined' )
220    {
221      /* Support for FixedColumns 1.0.x initialisation parameter */
222      this.s.leftColumns = oInit.columns;
223    }
224   
225    if ( typeof oInit.iColumns != 'undefined' )
226    {
227      this.s.leftColumns = oInit.iColumns;
228    }
229   
230    if ( typeof oInit.iRightColumns != 'undefined' )
231    {
232      this.s.rightColumns = oInit.iRightColumns;
233    }
234   
235    /* Set up the DOM as we need it and cache nodes */
236    this.dom.scrollContainer = $(this.s.dt.nTable).parents('div.dataTables_scroll')[0];
237    this.dom.scrollContainer.style.position = "relative";
238   
239    this.dom.body = this.s.dt.nTable;
240    this.dom.scroller = this.dom.body.parentNode;
241    this.dom.scroller.style.position = "relative";
242   
243    this.dom.header = this.s.dt.nTHead.parentNode;
244    this.dom.header.parentNode.parentNode.style.position = "relative";
245   
246    if ( this.s.dt.nTFoot )
247    {
248      this.dom.footer = this.s.dt.nTFoot.parentNode;
249      this.dom.footer.parentNode.parentNode.style.position = "relative";
250    }
251   
252    this.s.position = this.s.dt.oScroll.sY === "" ? 'absolute' : 'relative';
253   
254    /* Event handlers */
255    if ( this.s.position != "absolute" )
256    {
257      $(this.dom.scroller).scroll( function () {
258        that._fnPosition.call( that );
259      } );
260    }
261   
262    this.s.dt.aoDrawCallback.push( {
263      "fn": function () {
264        that._fnDraw.call( that, false );
265      },
266      "sName": "FixedColumns"
267    } );
268   
269    /* Get things right to start with */
270    this._fnDraw( true );
271  },
272 
273 
274  /**
275   * Clone and position the fixed columns
276   *  @method  _fnDraw
277   *  @returns void
278   *  @param   {Boolean} bAll Indicate if the headre and footer should be updated as well (true)
279   *  @private
280   */
281  "_fnDraw": function ( bAll )
282  {
283    this._fnCloneLeft( bAll );
284    this._fnCloneRight( bAll );
285    this._fnPosition();
286   
287    this.s.heights.splice( 0, this.s.heights.length );
288  },
289 
290 
291  /**
292   * Clone the right columns
293   *  @method  _fnCloneRight
294   *  @returns void
295   *  @param   {Boolean} bAll Indicate if the headre and footer should be updated as well (true)
296   *  @private
297   */
298  "_fnCloneRight": function ( bAll )
299  {
300    if ( this.s.rightColumns <= 0 )
301    {
302      return;
303    }
304   
305    var
306      that = this,
307      iTableWidth = 0,
308      aiCellWidth = [],
309      i, jq,
310      iColumns = $('thead tr:eq(0)', this.dom.header).children().length;
311   
312    /* Grab the widths that we are going to need */
313    for ( i=this.s.rightColumns-1 ; i>=0 ; i-- )
314    {
315      jq = $('thead tr:eq(0)', this.dom.header).children(':eq('+(iColumns-i-1)+')');
316      iTableWidth += jq.outerWidth();
317      aiCellWidth.push( jq.width() );
318    }
319    aiCellWidth.reverse();
320   
321    this._fnClone( this.dom.clone.right, bAll, aiCellWidth, iTableWidth,
322      ':last', ':lt('+(iColumns-this.s.rightColumns)+')' );
323  },
324 
325 
326  /**
327   * Clone the left columns
328   *  @method  _fnCloneLeft
329   *  @returns void
330   *  @param   {Boolean} bAll Indicate if the headre and footer should be updated as well (true)
331   *  @private
332   */
333  "_fnCloneLeft": function ( bAll )
334  {
335    if ( this.s.leftColumns <= 0 )
336    {
337      return;
338    }
339   
340    var
341      that = this,
342      iTableWidth = 0,
343      aiCellWidth = [],
344      i, jq;
345   
346    /* Grab the widths that we are going to need */
347    for ( i=0, iLen=this.s.leftColumns ; i<iLen ; i++ )
348    {
349      jq = $('thead tr:eq(0)', this.dom.header).children(':eq('+i+')');
350      iTableWidth += jq.outerWidth();
351      aiCellWidth.push( jq.width() );
352    }
353   
354    this._fnClone( this.dom.clone.left, bAll, aiCellWidth, iTableWidth,
355      ':first', ':gt('+(this.s.leftColumns-1)+')' );
356  },
357   
358 
359 
360  /**
361   * Clone the DataTable nodes and place them in the DOM (sized correctly)
362   *  @method  _fnClone
363   *  @returns void
364   *  @param   {Object} oClone Object containing the header, footer and body cloned DOM elements
365   *  @param   {Boolean} bAll Indicate if the headre and footer should be updated as well (true)
366   *  @param   {array} aiCellWidth Array of integers with the width's to use for the cloned columns
367   *  @param   {int} iTableWidth Calculated table width
368   *  @param   {string} sBoxHackSelector Selector to pick which TD element to copy styles from
369   *  @param   {string} sRemoveSelector Which elements to remove
370   *  @private
371   */
372  "_fnClone": function ( oClone, bAll, aiCellWidth, iTableWidth, sBoxHackSelector, sRemoveSelector )
373  {
374    var
375      that = this,
376      i, iLen, jq, nTarget;
377   
378    /* Header */
379    if ( bAll )
380    {
381      if ( oClone.header !== null )
382      {
383        oClone.header.parentNode.removeChild( oClone.header );
384      }
385      oClone.header = $(this.dom.header).clone(true)[0];
386      oClone.header.className += " FixedColumns_Cloned";
387     
388      oClone.header.style.position = "absolute";
389      oClone.header.style.top = "0px";
390      oClone.header.style.left = "0px";
391      oClone.header.style.width = iTableWidth+"px";
392     
393      nTarget = this.s.position == "absolute" ? this.dom.scrollContainer :
394        this.dom.header.parentNode;
395      nTarget.appendChild( oClone.header );
396   
397      this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header,
398        sBoxHackSelector, sRemoveSelector );
399   
400      $('thead tr:eq(0)', oClone.header).children().each( function (i) {
401        this.style.width = aiCellWidth[i]+"px";
402      } );
403    }
404    else
405    {
406      this._fnCopyClasses(oClone.header, this.dom.header);
407    }
408   
409    /* Body */
410    /* Remove any heights which have been applied already and let the browser figure it out */
411    $('tbody tr', that.dom.body).css('height', 'auto');
412   
413    if ( oClone.body !== null )
414    {
415      oClone.body.parentNode.removeChild( oClone.body );
416      oClone.body = null;
417    }
418   
419    if ( this.s.dt.aiDisplay.length > 0 )
420    {
421      oClone.body = $(this.dom.body).clone(true)[0];
422      oClone.body.className += " FixedColumns_Cloned";
423      if ( oClone.body.getAttribute('id') !== null )
424      {
425        oClone.body.removeAttribute('id');
426      }
427     
428      $('thead tr:eq(0)', oClone.body).each( function () {
429        $('th'+sRemoveSelector, this).remove();
430      } );
431     
432      $('thead tr:gt(0)', oClone.body).remove();
433     
434      this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body,
435        sBoxHackSelector, sRemoveSelector );
436     
437      $('tfoot tr:eq(0)', oClone.body).each( function () {
438        $('th'+sRemoveSelector, this).remove();
439      } );
440     
441      $('tfoot tr:gt(0)', oClone.body).remove();
442     
443     
444      oClone.body.style.position = "absolute";
445      oClone.body.style.top = "0px";
446      oClone.body.style.left = "0px";
447      oClone.body.style.width = iTableWidth+"px";
448     
449      nTarget = this.s.position == "absolute" ? this.dom.scrollContainer :
450        this.dom.body.parentNode;
451      nTarget.appendChild( oClone.body );
452    }
453   
454    /* Footer */
455    if ( this.s.dt.nTFoot !== null )
456    {
457      if ( bAll )
458      {
459        if ( oClone.footer !== null )
460        {
461          oClone.footer.parentNode.removeChild( oClone.footer );
462        }
463        oClone.footer = $(this.dom.footer).clone(true)[0];
464        oClone.footer.className += " FixedColumns_Cloned";
465       
466        oClone.footer.style.position = "absolute";
467        oClone.footer.style.top = "0px";
468        oClone.footer.style.left = "0px";
469        oClone.footer.style.width = iTableWidth+"px";
470       
471        nTarget = this.s.position == "absolute" ? this.dom.scrollContainer :
472          this.dom.footer.parentNode;
473        nTarget.appendChild( oClone.footer );
474     
475        this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer,
476          sBoxHackSelector, sRemoveSelector );
477       
478        $('tfoot tr:eq(0)', oClone.footer).children().each( function (i) {
479          this.style.width = aiCellWidth[i]+"px";
480        } );
481      }
482    }
483  },
484 
485 
486  /**
487   * Clone classes from one DOM node to another with (IMPORTANT) IDENTICAL structures
488   *  @method  _fnCopyClasses
489   *  @returns void
490   *  @param   {element} clone Node to copy classes to
491   *  @param   {element} original Original node to take the classes from
492   *  @private
493   */
494  "_fnCopyClasses": function ( clone, original )
495  {
496    clone.className = original.className;
497    for ( var i=0, iLen=clone.children.length ; i<iLen ; i++ )
498    {
499      if ( original.children[i].nodeType == 1 )
500      {
501        this._fnCopyClasses( clone.children[i], original.children[i] );
502      }
503    }
504  },
505 
506 
507  /**
508   * Equalise the heights of the rows in a given table node in a cross browser way
509   *  @method  _fnEqualiseHeights
510   *  @returns void
511   *  @param   {string} parent Node type - thead, tbody or tfoot
512   *  @param   {element} original Original node to take the heights from
513   *  @param   {element} clone Copy the heights to
514   *  @param   {string} boxHackSelector Selector to pick which TD element to copy styles from
515   *  @param   {string} removeSelector Which elements to remove
516   *  @private
517   */
518  "_fnEqualiseHeights": function ( parent, original, clone, boxHackSelector, removeSelector )
519  {
520    var that = this,
521      iHeight,
522      iCalculateHeights = (parent == "tbody" && this.s.heights.length > 0) ? false : true,
523      jqBoxHack = $(parent+' tr:eq(0)', original).children(boxHackSelector),
524      iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
525      bRubbishOldIE = ($.browser.msie && ($.browser.version == "6.0" || $.browser.version == "7.0"));
526   
527    if ( $(parent+' tr:eq(0) th', clone).attr('rowspan') > 1 )
528    {
529      $(parent+' tr:gt(0)', clone).remove();
530    }
531   
532    /* Remove cells which are not needed and copy the height from the original table */
533    $(parent+' tr', clone).each( function (k) {
534      $(this).children(removeSelector, this).remove();
535     
536      /* We can store the heights of the rows calculated on the first pass of a draw, to be used
537       * on the second pass (i.e. the right hand column). This significantly speeds up a draw
538       * where both the left and right columns are fixed since we don't need to get the height of
539       * each row twice
540       */
541      if ( iCalculateHeights )
542      {
543        iHeight = $(parent+' tr:eq('+k+')', original).children(':first').height();
544        if ( parent == 'tbody' )
545        {
546          that.s.heights.push( iHeight );
547        }
548      }
549      else
550      {
551        iHeight = that.s.heights[k];
552      }
553     
554      /* Can we use some kind of object detection here?! This is very nasty - damn browsers */
555      if ( $.browser.mozilla || $.browser.opera )
556      {
557        $(this).children().height( iHeight+iBoxHack );
558        $(parent+' tr:eq('+k+')', original).height( iHeight+iBoxHack );
559      }
560      else if ( $.browser.msie && !bRubbishOldIE )
561      {
562        $(this).children().height( iHeight-1 ); /* wtf... */
563      }
564      else
565      {
566        $(this).children().height( iHeight );
567      }
568    } );
569  },
570 
571 
572  /**
573   * Set the absolute position of the fixed column tables when scrolling the DataTable
574   *  @method  _fnPosition
575   *  @returns void
576   *  @private
577   */
578  "_fnPosition": function ()
579  {
580    var
581      iScrollLeft = this.s.position == 'absolute' ? 0 : $(this.dom.scroller).scrollLeft(),
582      oCloneLeft = this.dom.clone.left,
583      oCloneRight = this.dom.clone.right,
584      iTableWidth = $(this.s.dt.nTable.parentNode).width();
585     
586    if ( this.s.position == 'absolute' )
587    {
588      var iBodyTop = $(this.dom.body.parentNode).position().top;
589      if ( this.dom.footer )
590      {
591        var iFooterTop = $(this.dom.footer.parentNode.parentNode).position().top;
592      }
593    }
594   
595    if ( this.s.leftColumns > 0 )
596    {
597      oCloneLeft.header.style.left = iScrollLeft+"px";
598      if ( oCloneLeft.body !== null )
599      {
600        oCloneLeft.body.style.left = iScrollLeft+"px";
601        if (  this.s.position == 'absolute' )
602        {
603          oCloneLeft.body.style.top = iBodyTop+"px";
604        }
605      }
606      if ( this.dom.footer )
607      {
608        oCloneLeft.footer.style.left = iScrollLeft+"px";
609        if (  this.s.position == 'absolute' )
610        {
611          oCloneLeft.footer.style.top = iFooterTop+"px";
612        }
613      }
614    }
615   
616    if ( this.s.rightColumns > 0 )
617    {
618      var iPoint = iTableWidth - $(oCloneRight.body).width() + iScrollLeft;
619     
620      oCloneRight.header.style.left = iPoint+"px";
621      if ( oCloneRight.body !== null )
622      {
623        oCloneRight.body.style.left = iPoint+"px";
624        if (  this.s.position == 'absolute' )
625        {
626          oCloneRight.body.style.top = iBodyTop+"px";
627        }
628      }
629      if ( this.dom.footer )
630      {
631        oCloneRight.footer.style.left = iPoint+"px";
632        if (  this.s.position == 'absolute' )
633        {
634          oCloneRight.footer.style.top = iFooterTop+"px";
635        }
636      }
637    }
638  }
639};
Note: See TracBrowser for help on using the repository browser.