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