need a patched floatThead (https://github.com/mkoryak/floatThead/pull/12)
This commit is contained in:
		| @@ -1,19 +1,642 @@ | ||||
| /* | ||||
|  jQuery.floatThead 1.2.0 | ||||
|  Copyright (c) 2013 Misha Koryak - http://mkoryak.github.io/floatThead/ | ||||
|  Licensed under http://creativecommons.org/licenses/by-sa/4.0/ | ||||
| /*! | ||||
|  * jQuery.floatThead | ||||
|  * Copyright (c) 2012 - 2013 Misha Koryak | ||||
|  * Licensed under Attribution-ShareAlike 4.0 International - http://creativecommons.org/licenses/by-sa/4.0/ | ||||
|  * Date: 12/12/13 | ||||
|  * | ||||
|  * @author Misha Koryak | ||||
|  * @version 1.2.0 | ||||
|  * @projectDescription lock a table header in place while scrolling - without breaking styles or events bound to the header | ||||
|  * | ||||
|  * Dependencies: | ||||
|  * jquery 1.9.0 + [required] OR jquery 1.7.0 + jquery UI core | ||||
|  * underscore.js 1.3.0 + [required] | ||||
|  * | ||||
|  * http://mkoryak.github.io/floatThead/ | ||||
|  * | ||||
|  * Tested on FF13+, Chrome 21+, IE8, IE9, IE10 | ||||
|  * | ||||
|  */ | ||||
| (function(a){function ha(a,d){var b=e.width(),ia=_.debounce(function(){var a=e.width();b!=a&&(b=a,d())},a);e.bind("resize.floatTHead",ia)}function ja(){var g=a('<div style="width:50px;height:50px;overflow-y:scroll;position:absolute;top:-200px;left:-200px;"><div style="height:100px;width:100%"></div>');a("body").append(g);var d=g.innerWidth(),b=a("div",g).innerWidth();g.remove();return d-b}function ka(a){if(a.dataTableSettings)for(var d=0;d<a.dataTableSettings.length;d++)if(a[0]==a.dataTableSettings[d].nTable)return!0; | ||||
|   return!1}a.floatThead={defaults:{cellTag:"th",zIndex:1001,debounceResizeMs:1,useAbsolutePositioning:!0,scrollingTop:0,scrollingBottom:0,scrollContainer:function(g){return a([])},getSizingRow:function(a,d,b){return a.find("tbody tr:visible:first>td")},floatTableClass:"floatThead-table",debug:!1}};var D=function(){for(var a=3,d=document.createElement("b"),b=d.all||[];d.innerHTML="\x3c!--[if gt IE "+ ++a+"]><i><![endif]--\x3e",b[0];);return 4<a?a:document.documentMode}(),v=null,J=function(){if(D)return!1; | ||||
|   var e=a("<table><colgroup><col></colgroup><tbody><tr><td style='width:10px'></td></tbody></table>");a("body").append(e);var d=e.find("col").width();e.remove();return 0==d},e=a(window),K=0;a.fn.floatThead=function(g){if(8>D)return this;null==v&&(v=J())&&(document.createElement("fthtr"),document.createElement("fthtd"),document.createElement("fthfoot"));if(_.isString(g)){var d=this;this.filter("table").each(function(){var b=a(this).data("floatThead-attached");b&&_.isFunction(b[g])&&(b=b[g](),"undefined"!== | ||||
|   typeof b&&(d=b))});return d}var b=a.extend({},a.floatThead.defaults,g);_.each(g,function(e,d){if(!(d in a.floatThead.defaults)&&b.debug){var g="jQuery.floatThead: used ["+d+"] key to init plugin, but that param is not an option for the plugin. Valid options are: "+_.keys(a.floatThead.defaults).join(", ");window.console&&window.console&&window.console.log&&window.console.log(g)}});this.filter(":not(."+b.floatTableClass+")").each(function(){function d(){var a=n.outerHeight(!0);L.outerHeight(a);la.outerHeight(a)} | ||||
|   function g(){w=(_.isFunction(b.scrollingTop)?b.scrollingTop(c):b.scrollingTop)||0;X=(_.isFunction(b.scrollingBottom)?b.scrollingBottom(c):b.scrollingBottom)||0}function J(){var c=n.find("tr:first>"+b.cellTag),c=_.reduce(c,function(c,b){var d=parseInt(a(b).attr("colspan")||1,10);return c+d},0);if(c!=Y){Y=c;for(var d=[],s=[],f=[],e=0;e<c;e++)d.push("<"+b.cellTag+' class="floatThead-col-'+e+'"/>'),s.push("<col/>"),f.push("<fthtd style='display:table-cell;height:0;width:auto;'/>");s=s.join("");d=d.join(""); | ||||
|     v&&(f=f.join(""),M.html(f),N=M.find("fthtd"));L.html(d);E.html(s);O=E.find("col");P.html(s);Z=P.find("col")}return c}function F(){G||(G=!0,c.css($),h.css($),h.append(n),ma.before(z),d())}function Q(){G&&(G=!1,z.detach(),c.prepend(n),c.css(A),h.css(A))}function aa(a){p!=a&&(p=a,l.css({position:p?"absolute":"fixed"}))}function R(){var a,e=J();return function(){var s;s=O;s=v?N:D?b.getSizingRow(c,s,N):s;if(s.length==e&&0<e){Q();for(a=0;a<e;a++){var f=s.get(a).offsetWidth;Z.eq(a).width(f);O.eq(a).width(f)}F()}else h.append(n), | ||||
|     c.css(A),h.css(A),d()}}function S(){var a=m.scrollTop(),b,d=0,f=H?I.outerHeight(!0):0,g=l.height(),k=c.offset();if(u){var h=m.offset(),d=k.top-h.top+a;H&&ba&&(d+=f)}else b=k.top-w-g+X+B.horizontal;var r=e.scrollTop(),n=e.scrollLeft(),t=m.scrollLeft(),a=m.scrollTop();return function(q){"windowScroll"==q?(r=e.scrollTop(),n=e.scrollLeft()):"containerScroll"==q?(a=m.scrollTop(),t=m.scrollLeft()):"init"!=q&&(r=e.scrollTop(),n=e.scrollLeft(),a=m.scrollTop(),t=m.scrollLeft());if(!v||!(0>r||0>n)){if(na)"windowScrollDone"== | ||||
|     q?aa(!0):aa(!1);else if("windowScrollDone"==q)return null;k=c.offset();H&&ba&&(k.top+=f);var h,l;u&&p?(d>=a?(q=d-a,h=0<q?q:0):h=ca?0:a,l=0):!u&&p?(q=c.outerHeight(),r>b+q+f?h=q-g+f:k.top>r+w?(h=0,Q()):(h=w+r-k.top+d+f,F()),l=0):u&&!p?(d>a?(h=k.top-r,Q()):(h=k.top+a-r-d,F()),l=k.left+t-n):u||p||(q=c.outerHeight(),r>b+q+f?h=q+w-r+b+f:k.top>r+w?(h=k.top-r,F()):h=w,l=k.left-n);return{top:h,left:l}}}}function da(){var a=null,b=null,e=null;return function(f,g,k){null==f||a==f.top&&b==f.left||(l.css({top:f.top, | ||||
|     left:f.left}),a=f.top,b=f.left);g&&(f=c.outerWidth(),g=m.width()||f,l.width(g-B.vertical),u?h.css("width",100*f/(g-B.vertical)+"%"):h.outerWidth(f));k&&d();k=m.scrollLeft();e!=k&&(l.scrollLeft(k),e=k)}}function T(){m.length&&(B.horizontal=m.width()<c.width()?ea:0,B.vertical=m.height()<c.height()?ea:0)}var c=a(this);if(c.data("floatThead-attached"))return!0;if(!c.is("table"))throw Error('jQuery.floatThead must be run on a table element. ex: $("table").floatThead();');var n=c.find("thead:first"),ma= | ||||
|     c.find("tbody:first");if(0==n.length)throw Error("jQuery.floatThead must be run on a table that contains a <thead> element");var G=!0,w,X,B={vertical:0,horizontal:0},ea=ja(),Y=0,m=b.scrollContainer(c)||a([]),p=b.useAbsolutePositioning;null==p&&(p=b.scrollContainer(c).length);var I=c.find("caption"),H=1==I.length;if(H)var ba="top"===(I.css("caption-side")||I.attr("align")||"top");var U=a('<fthfoot style="display:table-footer-group;"/>'),u=0<m.length,ca=!1,na=9>=D&&!u&&p,h=a("<table/>"),P=a("<colgroup/>"), | ||||
|     E=a("<colgroup/>"),M=a('<fthrow style="display:table-row;height:0;"/>'),l=a('<div style="overflow: hidden;"></div>'),z=a("<thead/>"),L=a('<tr class="size-row"/>'),la=a([]),O=a([]),Z=a([]),N=a([]);z.append(L);n.detach();c.prepend(z);c.prepend(E);v&&(U.append(M),c.append(U));h.append(P);l.append(h);h.attr("class",c.attr("class"));h.addClass(b.floatTableClass).css("margin",0);if(p){var fa=function(a,c){var b=a.css("position");if("relative"!=b&&"absolute"!=b||c)b={paddingLeft:a.css("paddingLeft"),paddingRight:a.css("paddingRight")}, | ||||
|     l.css(b),a=a.wrap("<div style='position: relative; clear:both;'></div>").parent(),ca=!0;return a};u?fa(m,!0).append(l):(fa(c),c.after(l))}else c.after(l);l.css({position:p?"absolute":"fixed",marginTop:0,top:p?0:"auto",zIndex:b.zIndex});g();var $={"table-layout":"fixed"},A={"table-layout":c.css("tableLayout")||"auto"};T();var C=R();C();var t=S(),x=da();x(t("init"),!0);var oa=_.debounce(function(){x(t("windowScrollDone"),!1)},300),V=function(){x(t("windowScroll"),!1);oa()},W=function(){x(t("containerScroll"), | ||||
|     !1)},ga=function(){g();T();C=R();C();t=S();x=da();x(t("resize"),!0,!0)},y=_.debounce(function(){T();g();C=R();C();t=S();x(t("reflow"),!0)},1);u?p?m.bind("scroll.floatTHead",W):(m.bind("scroll.floatTHead",W),e.bind("scroll.floatTHead",V)):e.bind("scroll.floatTHead",V);e.bind("load.floatTHead",y);ha(b.debounceResizeMs,ga);c.bind("reflow",y);ka(c)&&c.bind("filter",y).bind("sort",y).bind("page",y);c.data("floatThead-attached",{destroy:function(){c.css(A);E.remove();v&&U.remove();z.parent().length&&z.replaceWith(n); | ||||
|     c.unbind("reflow");y=ga=W=V=function(){};m.unbind("scroll.floatTHead");l.remove();c.data("floatThead-attached",!1);K--;0==K&&(e.unbind("scroll.floatTHead"),e.unbind("resize.floatTHead"),e.unbind("load.floatTHead"))},reflow:function(){y()},setHeaderHeight:function(){d()},getFloatContainer:function(){return l}});K++});return this}})(jQuery); | ||||
| // ==ClosureCompiler== | ||||
| // @compilation_level SIMPLE_OPTIMIZATIONS | ||||
| // @output_file_name jquery.floatThead.min.js | ||||
| // ==/ClosureCompiler== | ||||
| /** | ||||
|  * @preserve jQuery.floatThead 1.2.0 | ||||
|  * Copyright (c) 2013 Misha Koryak - http://mkoryak.github.io/floatThead/ | ||||
|  * Licensed under http://creativecommons.org/licenses/by-sa/4.0/ | ||||
|  */ | ||||
| (function( $ ) { | ||||
|   /** | ||||
|    * provides a default config object. You can modify this after including this script if you want to change the init defaults | ||||
|    * @type {Object} | ||||
|    */ | ||||
|   $.floatThead = { | ||||
|     defaults: { | ||||
|       cellTag: 'th', | ||||
|       zIndex: 1001, //zindex of the floating thead (actually a container div) | ||||
|       debounceResizeMs: 1, | ||||
|       useAbsolutePositioning: true, //if set to NULL - defaults: has scrollContainer=true, doesn't have scrollContainer=false | ||||
|       scrollingTop: 0, //String or function($table) - offset from top of window where the header should not pass above | ||||
|       scrollingBottom: 0, //String or function($table) - offset from the bottom of the table where the header should stop scrolling | ||||
|       scrollContainer: function($table){ | ||||
|         return $([]); //if the table has horizontal scroll bars then this is the container that has overflow:auto and causes those scroll bars | ||||
|       }, | ||||
|       getSizingRow: function($table, $cols, $fthCells){ // this is only called when using IE8, | ||||
|         // override it if the first row of the table is going to contain colgroups (any cell spans greater then one col) | ||||
|         // it should return a jquery object containing a wrapped set of table cells comprising a row that contains no col spans and is visible | ||||
|         return $table.find('tbody tr:visible:first>td'); | ||||
|       }, | ||||
|       floatTableClass: 'floatThead-table', | ||||
|       debug: false //print possible issues (that don't prevent script loading) to console, if console exists. | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|  | ||||
|   //browser stuff | ||||
|   var ieVersion = function(){for(var a=3,b=document.createElement("b"),c=b.all||[];b.innerHTML="<!--[if gt IE "+ ++a+"]><i><![endif]-->",c[0];);return 4<a?a:document.documentMode}(); | ||||
|   var isChrome = null; | ||||
|   var isChromeCheck = function(){ | ||||
|     if(ieVersion){ | ||||
|       return false; | ||||
|     } | ||||
|     var $table = $("<table><colgroup><col></colgroup><tbody><tr><td style='width:10px'></td></tbody></table>"); | ||||
|     $('body').append($table); | ||||
|     var width = $table.find('col').width(); | ||||
|     $table.remove(); | ||||
|     return width == 0; | ||||
|   }; | ||||
|  | ||||
|   var $window = $(window); | ||||
|   var floatTheadCreated = 0; | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * debounce and fix window resize event for ie7. ie7 is evil and will fire window resize event when ANY dom element is resized. | ||||
|    * @param debounceMs | ||||
|    * @param cb | ||||
|    */ | ||||
|  | ||||
|   function windowResize(debounceMs, cb){ | ||||
|     var winWidth = $window.width(); | ||||
|     var debouncedCb = _.debounce(function(){ | ||||
|       var winWidthNew = $window.width(); | ||||
|       if(winWidth != winWidthNew){ | ||||
|         winWidth = winWidthNew; | ||||
|         cb(); | ||||
|       } | ||||
|     }, debounceMs); | ||||
|     $window.bind('resize.floatTHead', debouncedCb); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   function debug(str){ | ||||
|     window.console && window.console && window.console.log && window.console.log(str); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * try to calculate the scrollbar width for your browser/os | ||||
|    * @return {Number} | ||||
|    */ | ||||
|   function scrollbarWidth() { | ||||
|     var $div = $( //borrowed from anti-scroll | ||||
|       '<div style="width:50px;height:50px;overflow-y:scroll;' | ||||
|         + 'position:absolute;top:-200px;left:-200px;"><div style="height:100px;width:100%">' | ||||
|         + '</div>' | ||||
|     ); | ||||
|     $('body').append($div); | ||||
|     var w1 = $div.innerWidth(); | ||||
|     var w2 = $('div', $div).innerWidth(); | ||||
|     $div.remove(); | ||||
|     return w1 - w2; | ||||
|   } | ||||
|   /** | ||||
|    * Check if a given table has been datatableized (http://datatables.net) | ||||
|    * @param $table | ||||
|    * @return {Boolean} | ||||
|    */ | ||||
|   function isDatatable($table){ | ||||
|     if($table.dataTableSettings){ | ||||
|       for(var i = 0; i < $table.dataTableSettings.length; i++){ | ||||
|         var table = $table.dataTableSettings[i].nTable; | ||||
|         if($table[0] == table){ | ||||
|           return true; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|   $.fn.floatThead = function(map){ | ||||
|     if(ieVersion < 8){ | ||||
|       return this; //no more crappy browser support. | ||||
|     } | ||||
|  | ||||
|     if(isChrome == null){ //make sure this is done only once no matter how many times you call the plugin fn | ||||
|       isChrome = isChromeCheck(); //need to call this after dom ready, and now it is. | ||||
|       if(isChrome){ | ||||
|         //because chrome cant read <col> width, these elements are used for sizing the table. Need to create new elements because they must be unstyled by user's css. | ||||
|         document.createElement('fthtr'); //tr | ||||
|         document.createElement('fthtd'); //td | ||||
|         document.createElement('fthfoot'); //tfoot | ||||
|       } | ||||
|     } | ||||
|     if(_.isString(map)){ | ||||
|       var command = map; | ||||
|       var ret = this; | ||||
|       this.filter('table').each(function(){ | ||||
|         var obj = $(this).data('floatThead-attached'); | ||||
|         if(obj && _.isFunction(obj[command])){ | ||||
|           var r = obj[command](); | ||||
|           if(typeof r !== 'undefined'){ | ||||
|             ret = r; | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|       return ret; | ||||
|     } | ||||
|     var opts = $.extend({}, $.floatThead.defaults, map); | ||||
|  | ||||
|     _.each(map, function(val, key){ | ||||
|       if((!(key in $.floatThead.defaults)) && opts.debug){ | ||||
|         debug("jQuery.floatThead: used ["+key+"] key to init plugin, but that param is not an option for the plugin. Valid options are: "+ (_.keys($.floatThead.defaults)).join(', ')); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     this.filter(':not(.'+opts.floatTableClass+')').each(function(){ | ||||
|       var $table = $(this); | ||||
|       if($table.data('floatThead-attached')){ | ||||
|         return true; //continue the each loop | ||||
|       } | ||||
|       if(!$table.is('table')){ | ||||
|         throw new Error('jQuery.floatThead must be run on a table element. ex: $("table").floatThead();'); | ||||
|       } | ||||
|       var $header = $table.find('thead:first'); | ||||
|       var $tbody = $table.find('tbody:first'); | ||||
|       if($header.length == 0){ | ||||
|         throw new Error('jQuery.floatThead must be run on a table that contains a <thead> element'); | ||||
|       } | ||||
|       var headerFloated = true; | ||||
|       var scrollingTop, scrollingBottom; | ||||
|       var scrollbarOffset = {vertical: 0, horizontal: 0}; | ||||
|       var scWidth = scrollbarWidth(); | ||||
|       var lastColumnCount = 0; //used by columnNum() | ||||
|       var $scrollContainer = opts.scrollContainer($table) || $([]); //guard against returned nulls | ||||
|  | ||||
|       var useAbsolutePositioning = opts.useAbsolutePositioning; | ||||
|       if(useAbsolutePositioning == null){ //defaults: locked=true, !locked=false | ||||
|         useAbsolutePositioning = opts.scrollContainer($table).length; | ||||
|       } | ||||
|       var $caption = $table.find("caption"); | ||||
|       var haveCaption = $caption.length == 1; | ||||
|       if(haveCaption){ | ||||
|         var captionAlignTop = ($caption.css("caption-side") || $caption.attr("align") || "top") === "top"; | ||||
|       } | ||||
|  | ||||
|       var $fthGrp = $('<fthfoot style="display:table-footer-group;"/>'); | ||||
|  | ||||
|       var locked = $scrollContainer.length > 0; | ||||
|       var wrappedContainer = false; //used with absolute positioning enabled. did we need to wrap the scrollContainer/table with a relative div? | ||||
|       var absoluteToFixedOnScroll = ieVersion <= 9 && !locked && useAbsolutePositioning; //on ie using absolute positioning doesnt look good with window scrolling, so we change positon to fixed on scroll, and then change it back to absolute when done. | ||||
|       var $floatTable = $("<table/>"); | ||||
|       var $floatColGroup = $("<colgroup/>"); | ||||
|       var $tableColGroup = $("<colgroup/>"); | ||||
|       var $fthRow = $('<fthrow style="display:table-row;height:0;"/>'); //created unstyled elements | ||||
|       var $floatContainer = $('<div style="overflow: hidden;"></div>'); | ||||
|       var $newHeader = $("<thead/>"); | ||||
|       var $sizerRow = $('<tr class="size-row"/>'); | ||||
|       var $sizerCells = $([]); | ||||
|       var $tableCells = $([]); //used for sizing - either $sizerCells or $tableColGroup cols. $tableColGroup cols are only created in chrome for borderCollapse:collapse because of a chrome bug. | ||||
|       var $headerCells = $([]); | ||||
|       var $fthCells = $([]); //created elements | ||||
|  | ||||
|       $newHeader.append($sizerRow); | ||||
|       $header.detach(); | ||||
|  | ||||
|       $table.prepend($newHeader); | ||||
|       $table.prepend($tableColGroup); | ||||
|       if(isChrome){ | ||||
|         $fthGrp.append($fthRow); | ||||
|         $table.append($fthGrp); | ||||
|       } | ||||
|  | ||||
|       $floatTable.append($floatColGroup); | ||||
|       $floatContainer.append($floatTable); | ||||
|       $floatTable.attr('class', $table.attr('class')); | ||||
|       $floatTable.addClass(opts.floatTableClass).css('margin', 0); //must have no margins or you wont be able to click on things under floating table | ||||
|  | ||||
|       if(useAbsolutePositioning){ | ||||
|         var makeRelative = function($container, alwaysWrap){ | ||||
|           var positionCss = $container.css('position'); | ||||
|           var relativeToScrollContainer = (positionCss == "relative" || positionCss == "absolute"); | ||||
|           if(!relativeToScrollContainer || alwaysWrap){ | ||||
|             var css = {"paddingLeft": $container.css('paddingLeft'), "paddingRight": $container.css('paddingRight')}; | ||||
|             $floatContainer.css(css); | ||||
|             $container = $container.wrap("<div style='position: relative; clear:both;'></div>").parent(); | ||||
|             wrappedContainer = true; | ||||
|           } | ||||
|           return $container; | ||||
|         }; | ||||
|         if(locked){ | ||||
|           var $relative = makeRelative($scrollContainer, true); | ||||
|           $relative.append($floatContainer); | ||||
|         } else { | ||||
|           makeRelative($table); | ||||
|           $table.after($floatContainer); | ||||
|         } | ||||
|       } else { | ||||
|         $table.after($floatContainer); | ||||
|       } | ||||
|  | ||||
|  | ||||
|       $floatContainer.css({ | ||||
|         position: useAbsolutePositioning ? 'absolute' : 'fixed', | ||||
|         marginTop: 0, | ||||
|         top:  useAbsolutePositioning ? 0 : 'auto', | ||||
|         zIndex: opts.zIndex | ||||
|       }); | ||||
|       updateScrollingOffsets(); | ||||
|  | ||||
|       var layoutFixed = {'table-layout': 'fixed'}; | ||||
|       var layoutAuto = {'table-layout': $table.css('tableLayout') || 'auto'}; | ||||
|  | ||||
|       function setHeaderHeight(){ | ||||
|         var headerHeight = $header.outerHeight(true); | ||||
|         $sizerRow.outerHeight(headerHeight); | ||||
|         $sizerCells.outerHeight(headerHeight); | ||||
|       } | ||||
|  | ||||
|  | ||||
|       function setFloatWidth(){ | ||||
|         var tableWidth = $table.outerWidth(); | ||||
|         var width = $scrollContainer.width() || tableWidth; | ||||
|         $floatContainer.width(width - scrollbarOffset.vertical); | ||||
|         if(locked){ | ||||
|           var percent = 100 * tableWidth / (width - scrollbarOffset.vertical); | ||||
|           $floatTable.css('width', percent+'%'); | ||||
|         } else { | ||||
|           $floatTable.outerWidth(tableWidth); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       function updateScrollingOffsets(){ | ||||
|         scrollingTop = (_.isFunction(opts.scrollingTop) ? opts.scrollingTop($table) : opts.scrollingTop) || 0; | ||||
|         scrollingBottom = (_.isFunction(opts.scrollingBottom) ? opts.scrollingBottom($table) : opts.scrollingBottom) || 0; | ||||
|       } | ||||
|  | ||||
|       /** | ||||
|        * get the number of columns and also rebuild resizer rows if the count is different then the last count | ||||
|        */ | ||||
|       function columnNum(){ | ||||
|         var $headerColumns = $header.find('tr:first>'+opts.cellTag); | ||||
|  | ||||
|         var count =  _.reduce($headerColumns, function(sum, cell){ | ||||
|           var colspan = parseInt(($(cell).attr('colspan') || 1), 10); | ||||
|           return sum + colspan; | ||||
|         }, 0); | ||||
|         if(count != lastColumnCount){ | ||||
|           lastColumnCount = count; | ||||
|           var cells = [], cols = [], psuedo = []; | ||||
|           for(var x = 0; x < count; x++){ | ||||
|             cells.push('<'+opts.cellTag+' class="floatThead-col-'+x+'"/>'); | ||||
|             cols.push('<col/>'); | ||||
|             psuedo.push("<fthtd style='display:table-cell;height:0;width:auto;'/>"); | ||||
|           } | ||||
|  | ||||
|           cols = cols.join(''); | ||||
|           cells = cells.join(''); | ||||
|  | ||||
|           if(isChrome){ | ||||
|             psuedo = psuedo.join(''); | ||||
|             $fthRow.html(psuedo); | ||||
|             $fthCells = $fthRow.find('fthtd'); | ||||
|           } | ||||
|  | ||||
|           $sizerRow.html(cells); | ||||
|           $tableColGroup.html(cols); | ||||
|           $tableCells = $tableColGroup.find('col'); | ||||
|           $floatColGroup.html(cols); | ||||
|           $headerCells = $floatColGroup.find("col"); | ||||
|  | ||||
|         } | ||||
|         return count; | ||||
|       } | ||||
|  | ||||
|       function refloat(){ //make the thing float | ||||
|         if(!headerFloated){ | ||||
|           headerFloated = true; | ||||
|           $table.css(layoutFixed); | ||||
|           $floatTable.css(layoutFixed); | ||||
|           $floatTable.append($header); //append because colgroup must go first in chrome | ||||
|           $tbody.before($newHeader); | ||||
|           setHeaderHeight(); | ||||
|         } | ||||
|       } | ||||
|       function unfloat(){ //put the header back into the table | ||||
|         if(headerFloated){ | ||||
|           headerFloated = false; | ||||
|           $newHeader.detach(); | ||||
|           $table.prepend($header); | ||||
|           $table.css(layoutAuto); | ||||
|           $floatTable.css(layoutAuto); | ||||
|         } | ||||
|       } | ||||
|       function changePositioning(isAbsolute){ | ||||
|         if(useAbsolutePositioning != isAbsolute){ | ||||
|           useAbsolutePositioning = isAbsolute; | ||||
|           $floatContainer.css({ | ||||
|             position: useAbsolutePositioning ? 'absolute' : 'fixed' | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|       function getSizingRow($table, $cols, $fthCells, ieVersion){ | ||||
|         if(isChrome){ | ||||
|           return $fthCells; | ||||
|         } else if(ieVersion) { | ||||
|           return opts.getSizingRow($table, $cols, $fthCells); | ||||
|         } else { | ||||
|           return $cols; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       /** | ||||
|        * returns a function that updates the floating header's cell widths. | ||||
|        * @return {Function} | ||||
|        */ | ||||
|       function reflow(){ | ||||
|         var i; | ||||
|         var numCols = columnNum(); //if the tables columns change dynamically since last time (datatables) we need to rebuild the sizer rows and get new count | ||||
|         return function(){ | ||||
|           var $rowCells = getSizingRow($table, $tableCells, $fthCells, ieVersion); | ||||
|           if($rowCells.length == numCols && numCols > 0){ | ||||
|             unfloat(); | ||||
|             for(i=0; i < numCols; i++){ | ||||
|               var _rowcell = $rowCells.get(i); | ||||
|               var rowWidth = _rowcell.offsetWidth; | ||||
|               $headerCells.eq(i).width(rowWidth); | ||||
|               $tableCells.eq(i).width(rowWidth); | ||||
|             } | ||||
|             refloat(); | ||||
|           } else { | ||||
|             $floatTable.append($header); | ||||
|             $table.css(layoutAuto); | ||||
|             $floatTable.css(layoutAuto); | ||||
|             setHeaderHeight(); | ||||
|           } | ||||
|         }; | ||||
|       } | ||||
|  | ||||
|       /** | ||||
|        * first performs initial calculations that we expect to not change when the table, window, or scrolling container are scrolled. | ||||
|        * returns a function that calculates the floating container's top and left coords. takes into account if we are using page scrolling or inner scrolling | ||||
|        * @return {Function} | ||||
|        */ | ||||
|       function calculateFloatContainerPosFn(){ | ||||
|         var scrollingContainerTop = $scrollContainer.scrollTop(); | ||||
|  | ||||
|         //this floatEnd calc was moved out of the returned function because we assume the table height doesnt change (otherwise we must reinit by calling calculateFloatContainerPosFn) | ||||
|         var floatEnd; | ||||
|         var tableContainerGap = 0; | ||||
|         var captionHeight = haveCaption ? $caption.outerHeight(true) : 0; | ||||
|  | ||||
|         var floatContainerHeight = $floatContainer.height(); | ||||
|         var tableOffset = $table.offset(); | ||||
|         if(locked){ | ||||
|           var containerOffset = $scrollContainer.offset(); | ||||
|           tableContainerGap = tableOffset.top - containerOffset.top + scrollingContainerTop; | ||||
|           if(haveCaption && captionAlignTop){ | ||||
|             tableContainerGap += captionHeight; | ||||
|           } | ||||
|         } else { | ||||
|           floatEnd = tableOffset.top - scrollingTop - floatContainerHeight + scrollingBottom + scrollbarOffset.horizontal; | ||||
|         } | ||||
|         var windowTop = $window.scrollTop(); | ||||
|         var windowLeft = $window.scrollLeft(); | ||||
|         var scrollContainerLeft =  $scrollContainer.scrollLeft(); | ||||
|         scrollingContainerTop = $scrollContainer.scrollTop(); | ||||
|  | ||||
|  | ||||
|  | ||||
|         return function(eventType){ | ||||
|           if(eventType == 'windowScroll'){ | ||||
|             windowTop = $window.scrollTop(); | ||||
|             windowLeft = $window.scrollLeft(); | ||||
|           } else if(eventType == 'containerScroll'){ | ||||
|             scrollingContainerTop = $scrollContainer.scrollTop(); | ||||
|             scrollContainerLeft =  $scrollContainer.scrollLeft(); | ||||
|           } else if(eventType != 'init') { | ||||
|             windowTop = $window.scrollTop(); | ||||
|             windowLeft = $window.scrollLeft(); | ||||
|             scrollingContainerTop = $scrollContainer.scrollTop(); | ||||
|             scrollContainerLeft =  $scrollContainer.scrollLeft(); | ||||
|           } | ||||
|           if(isChrome && (windowTop < 0 || windowLeft < 0)){ //chrome overscroll effect at the top of the page - breaks fixed positioned floated headers | ||||
|             return; | ||||
|           } | ||||
|  | ||||
|           if(absoluteToFixedOnScroll){ | ||||
|             if(eventType == 'windowScrollDone'){ | ||||
|               changePositioning(true); //change to absolute | ||||
|             } else { | ||||
|               changePositioning(false); //change to fixed | ||||
|             } | ||||
|           } else if(eventType == 'windowScrollDone'){ | ||||
|             return null; //event is fired when they stop scrolling. ignore it if not 'absoluteToFixedOnScroll' | ||||
|           } | ||||
|  | ||||
|           tableOffset = $table.offset(); | ||||
|           if(haveCaption && captionAlignTop){ | ||||
|             tableOffset.top += captionHeight; | ||||
|           } | ||||
|           var top, left, tableHeight; | ||||
|  | ||||
|           if(locked && useAbsolutePositioning){ //inner scrolling, absolute positioning | ||||
|             if (tableContainerGap >= scrollingContainerTop) { | ||||
|               var gap = tableContainerGap - scrollingContainerTop; | ||||
|               gap = gap > 0 ? gap : 0; | ||||
|               top = gap; | ||||
|             } else { | ||||
|               top = wrappedContainer ? 0 : scrollingContainerTop; | ||||
|               //headers stop at the top of the viewport | ||||
|             } | ||||
|             left = 0; | ||||
|           } else if(!locked && useAbsolutePositioning) { //window scrolling, absolute positioning | ||||
|             tableHeight = $table.outerHeight(); | ||||
|             if(windowTop > floatEnd + tableHeight + captionHeight){ | ||||
|               top = tableHeight - floatContainerHeight + captionHeight; //scrolled past table | ||||
|             } else if (tableOffset.top > windowTop + scrollingTop) { | ||||
|               top = 0; //scrolling to table | ||||
|               unfloat(); | ||||
|             } else { | ||||
|               top = scrollingTop + windowTop - tableOffset.top + tableContainerGap + captionHeight; | ||||
|               refloat(); //scrolling within table. header floated | ||||
|             } | ||||
|             left =  0; | ||||
|           } else if(locked && !useAbsolutePositioning){ //inner scrolling, fixed positioning | ||||
|             if (tableContainerGap > scrollingContainerTop) { | ||||
|               top = tableOffset.top - windowTop; | ||||
|               unfloat(); | ||||
|             } else { | ||||
|               top = tableOffset.top + scrollingContainerTop  - windowTop - tableContainerGap; | ||||
|               refloat(); | ||||
|               //headers stop at the top of the viewport | ||||
|             } | ||||
|             left = tableOffset.left + scrollContainerLeft - windowLeft; | ||||
|           } else if(!locked && !useAbsolutePositioning) { //window scrolling, fixed positioning | ||||
|             tableHeight = $table.outerHeight(); | ||||
|             if(windowTop > floatEnd + tableHeight + captionHeight){ | ||||
|               top = tableHeight + scrollingTop - windowTop + floatEnd + captionHeight; | ||||
|               //scrolled past the bottom of the table | ||||
|             } else if (tableOffset.top > windowTop + scrollingTop) { | ||||
|               top = tableOffset.top - windowTop; | ||||
|               refloat(); | ||||
|               //scrolled past the top of the table | ||||
|             } else { | ||||
|               //scrolling within the table | ||||
|               top = scrollingTop; | ||||
|             } | ||||
|             left = tableOffset.left - windowLeft; | ||||
|           } | ||||
|           return {top: top, left: left}; | ||||
|         }; | ||||
|       } | ||||
|       /** | ||||
|        * returns a function that caches old floating container position and only updates css when the position changes | ||||
|        * @return {Function} | ||||
|        */ | ||||
|       function repositionFloatContainerFn(){ | ||||
|         var oldTop = null; | ||||
|         var oldLeft = null; | ||||
|         var oldScrollLeft = null; | ||||
|         return function(pos, setWidth, setHeight){ | ||||
|           if(pos != null && (oldTop != pos.top || oldLeft != pos.left)){ | ||||
|             $floatContainer.css({ | ||||
|               top: pos.top, | ||||
|               left: pos.left | ||||
|             }); | ||||
|             oldTop = pos.top; | ||||
|             oldLeft = pos.left; | ||||
|           } | ||||
|           if(setWidth){ | ||||
|             setFloatWidth(); | ||||
|           } | ||||
|           if(setHeight){ | ||||
|             setHeaderHeight(); | ||||
|           } | ||||
|           var scrollLeft = $scrollContainer.scrollLeft(); | ||||
|           if(oldScrollLeft != scrollLeft){ | ||||
|             $floatContainer.scrollLeft(scrollLeft); | ||||
|             oldScrollLeft = scrollLeft; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       /** | ||||
|        * checks if THIS table has scrollbars, and finds their widths | ||||
|        */ | ||||
|       function calculateScrollBarSize(){ //this should happen after the floating table has been positioned | ||||
|         if($scrollContainer.length){ | ||||
|           scrollbarOffset.horizontal = $scrollContainer.width() < $table.width() ? scWidth : 0; | ||||
|           scrollbarOffset.vertical =  $scrollContainer.height() < $table.height() ? scWidth: 0; | ||||
|         } | ||||
|       } | ||||
|       //finish up. create all calculation functions and bind them to events | ||||
|       calculateScrollBarSize(); | ||||
|  | ||||
|       var flow = reflow(); | ||||
|       flow(); | ||||
|       var calculateFloatContainerPos = calculateFloatContainerPosFn(); | ||||
|       var repositionFloatContainer = repositionFloatContainerFn(); | ||||
|  | ||||
|       repositionFloatContainer(calculateFloatContainerPos('init'), true, true); | ||||
|       //this must come after reflow because reflow changes scrollLeft back to 0 when it rips out the thead | ||||
|  | ||||
|       var windowScrollDoneEvent = _.debounce(function(){ | ||||
|         repositionFloatContainer(calculateFloatContainerPos('windowScrollDone'), false); | ||||
|       }, 300); | ||||
|  | ||||
|       var windowScrollEvent = function(){ | ||||
|         repositionFloatContainer(calculateFloatContainerPos('windowScroll'), false); | ||||
|         windowScrollDoneEvent(); | ||||
|       }; | ||||
|       var containerScrollEvent = function(){ | ||||
|         repositionFloatContainer(calculateFloatContainerPos('containerScroll'), false); | ||||
|       }; | ||||
|  | ||||
|       var ensureReflow = function(){ | ||||
|         flow = reflow(); | ||||
|         flow(); | ||||
|       }; | ||||
|  | ||||
|       var windowResizeEvent = function(){ | ||||
|         updateScrollingOffsets(); | ||||
|         calculateScrollBarSize(); | ||||
|         ensureReflow(); | ||||
|         calculateFloatContainerPos = calculateFloatContainerPosFn(); | ||||
|         repositionFloatContainer = repositionFloatContainerFn(); | ||||
|         repositionFloatContainer(calculateFloatContainerPos('resize'), true, true); | ||||
|       }; | ||||
|       var reflowEvent = _.debounce(function(){ | ||||
|         calculateScrollBarSize(); | ||||
|         updateScrollingOffsets(); | ||||
|         ensureReflow(); | ||||
|         calculateFloatContainerPos = calculateFloatContainerPosFn(); | ||||
|         repositionFloatContainer(calculateFloatContainerPos('reflow'), true); | ||||
|       }, 1); | ||||
|       if(locked){ //internal scrolling | ||||
|         if(useAbsolutePositioning){ | ||||
|           $scrollContainer.bind('scroll.floatTHead', containerScrollEvent); | ||||
|         } else { | ||||
|           $scrollContainer.bind('scroll.floatTHead', containerScrollEvent); | ||||
|           $window.bind('scroll.floatTHead', windowScrollEvent); | ||||
|         } | ||||
|       } else { //window scrolling | ||||
|         $window.bind('scroll.floatTHead', windowScrollEvent); | ||||
|       } | ||||
|  | ||||
|       $window.bind('load.floatTHead', reflowEvent); //for tables with images | ||||
|  | ||||
|       windowResize(opts.debounceResizeMs, windowResizeEvent); | ||||
|       $table.bind('reflow', reflowEvent); | ||||
|       if(isDatatable($table)){ | ||||
|         $table | ||||
|           .bind('filter', reflowEvent) | ||||
|           .bind('sort',   reflowEvent) | ||||
|           .bind('page',   reflowEvent); | ||||
|       } | ||||
|  | ||||
|       //attach some useful functions to the table. | ||||
|       $table.data('floatThead-attached', { | ||||
|         destroy: function(){ | ||||
|           $table.css(layoutAuto); | ||||
|           $tableColGroup.remove(); | ||||
|           isChrome && $fthGrp.remove(); | ||||
|           if($newHeader.parent().length){ //only if its in the dom | ||||
|             $newHeader.replaceWith($header); | ||||
|           } | ||||
|           $table.unbind('reflow'); | ||||
|           reflowEvent = windowResizeEvent = containerScrollEvent = windowScrollEvent = function() {}; | ||||
|           $scrollContainer.unbind('scroll.floatTHead'); | ||||
|           $floatContainer.remove(); | ||||
|           $table.data('floatThead-attached', false); | ||||
|           floatTheadCreated--; | ||||
|           if(floatTheadCreated == 0){ | ||||
|             $window.unbind('scroll.floatTHead'); | ||||
|             $window.unbind('resize.floatTHead'); | ||||
|             $window.unbind('load.floatTHead'); | ||||
|           } | ||||
|         }, | ||||
|         reflow: function(){ | ||||
|           reflowEvent(); | ||||
|         }, | ||||
|         setHeaderHeight: function(){ | ||||
|           setHeaderHeight(); | ||||
|         }, | ||||
|         getFloatContainer: function(){ | ||||
|           return $floatContainer; | ||||
|         } | ||||
|       }); | ||||
|       floatTheadCreated++; | ||||
|     }); | ||||
|     return this; | ||||
|   }; | ||||
| })(jQuery); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user