update floatThead plugin to imporove performance for large tables

This commit is contained in:
Oliver Gorwits
2014-01-02 13:14:18 +00:00
parent 9deb6fe691
commit f716582700
2 changed files with 19 additions and 611 deletions

View File

@@ -22,6 +22,7 @@
* [#49] Allow device port searching with preference for port/name/vlan. * [#49] Allow device port searching with preference for port/name/vlan.
This is to support some devices (HP?) which have plain numbers for port names This is to support some devices (HP?) which have plain numbers for port names
and Netdisco defaults to assuming this is a VLAN number (R. Kerr) and Netdisco defaults to assuming this is a VLAN number (R. Kerr)
* Upgrade floatThead JS plugin to improve performance for large tables
[BUG FIXES] [BUG FIXES]

View File

@@ -1,612 +1,19 @@
/*! /*
* jQuery.floatThead jQuery.floatThead 1.2.0
* Copyright (c) 2012 - 2013 Misha Koryak - https://github.com/mkoryak/floatThead Copyright (c) 2013 Misha Koryak - http://mkoryak.github.io/floatThead/
* Licensed under Creative Commons Attribution-NonCommercial 3.0 Unported - http://creativecommons.org/licenses/by-sa/3.0/ Licensed under http://creativecommons.org/licenses/by-sa/4.0/
* Date: 8/25/13
*
* @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://notetodogself.blogspot.com
* http://programmingdrunk.com/floatThead/
*
* Tested on FF13+, Chrome 21+, IE9, IE8
*
* @author Misha Koryak
* @version 1.0.0
*/ */
// ==ClosureCompiler== (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;
// @compilation_level SIMPLE_OPTIMIZATIONS 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;
// @output_file_name jquery.floatThead.min.js 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"!==
// ==/ClosureCompiler== 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("");
* @preserve jQuery.floatThead 1.0.0 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),
* Copyright (c) 2013 Misha Koryak - https://github.com/mkoryak/floatThead 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"==
* Licensed under Creative Commons Attribution-NonCommercial 3.0 Unported - http://creativecommons.org/licenses/by-sa/3.0/ 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=
(function( $ ) { 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")},
//browser stuff 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"),
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}(); !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);
var ifChrome = function(){ 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);
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;
};
/**
* 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, doesnt have scrollContainer=false
scrollingTop: 0, //String or function($table) - offset from top of window where the header should not pass above
//TODO: this got lost somewhere - needs to be re-implemented
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
},
floatTableClass: 'floatThead-table'
}
};
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);
}
/**
* try to calculate the scrollbar width for your browser/os
* @return {Number}
*/
function scrollbarWidth() {
var $div = $('<div/>')
.css({ width: 100, height: 100, overflow: 'auto', position: 'absolute', top: -1000, left: -1000 })
.prependTo('body').append('<div/>').find('div')
.css({ width: '100%', height: 200 });
var scrollbarWidth = 100 - $div.width();
$div.parent().remove();
return scrollbarWidth;
}
/**
* 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.
}
isChrome = ifChrome(); //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])){
r = obj[command]();
if(typeof r !== 'undefined'){
ret = r;
}
}
});
return ret;
}
var opts = $.extend({}, $.floatThead.defaults, map);
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 $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 && !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("<fthcell 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('fthcell')
}
$sizerRow.html(cells);
$tableColGroup.html(cols);
$tableCells = $tableColGroup.find('col');
$floatColGroup.html(cols);
$headerCells = $floatColGroup.find("col");
}
return count;
}
function refloat(){
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(){
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'
});
}
}
/**
* returns a function that updates the floating header's cell widths.
* @return {Function}
*/
function reflow(){
var numCols = columnNum(); //if the tables columns change dynamically since last time (datatables) we need to rebuild the sizer rows and get new count
var flow = function(){
var badReflow = false;
var $rowCells = isChrome ? $fthCells : $tableCells;
if($rowCells.length == numCols && numCols > 0){
unfloat();
for(var i=0; i < numCols; i++){
var $rowCell = $rowCells.eq(i);
var rowWidth = $rowCell.outerWidth(true);
$headerCells.eq(i).outerWidth(rowWidth);
$tableCells.eq(i).outerWidth(rowWidth);
}
refloat();
for(var i=0; i < numCols; i++){
var hw = $headerCells.eq(i).outerWidth(true);
var tw = $tableCells.eq(i).outerWidth(true);
if(hw != tw){
badReflow = true;
break;
}
}
} else {
$floatTable.append($header);
$table.css(layoutAuto);
$floatTable.css(layoutAuto);
setHeaderHeight();
}
return badReflow;
};
return flow;
}
/**
* 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 floatContainerHeight = $floatContainer.height();
var tableOffset = $table.offset();
if(locked){
var containerOffset = $scrollContainer.offset();
tableContainerGap = tableOffset.top - containerOffset.top + scrollingContainerTop;
} else {
floatEnd = tableOffset.top - scrollingTop - floatContainerHeight + scrollingBottom + scrollbarOffset.horizontal;
}
var windowTop = $window.scrollTop();
var windowLeft = $window.scrollLeft();
var scrollContainerLeft = $scrollContainer.scrollLeft();
scrollingContainerTop = $scrollContainer.scrollTop();
var positionFn = 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(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();
var top, left, tableHeight;
//absolute positioning
if(locked && useAbsolutePositioning){ //inner scrolling
if (tableContainerGap >= scrollingContainerTop) {
var gap = tableContainerGap - scrollingContainerTop;
gap = gap > 0 ? gap : 0;
top = gap;
// unfloat(); //more trouble than its worth
} else {
top = wrappedContainer ? 0 : scrollingContainerTop;
// refloat(); //more trouble than its worth
//headers stop at the top of the viewport
}
left = 0;
} else if(!locked && useAbsolutePositioning) { //window scrolling
tableHeight = $table.outerHeight();
if(windowTop > floatEnd + tableHeight){
top = tableHeight - floatContainerHeight; //scrolled past table
} else if (tableOffset.top > windowTop + scrollingTop) {
top = 0; //scrolling to table
unfloat();
} else {
top = scrollingTop + windowTop - tableOffset.top + tableContainerGap;
refloat(); //scrolling within table. header floated
}
left = 0;
//fixed positioning:
} else if(locked && !useAbsolutePositioning){ //inner scrolling
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
tableHeight = $table.outerHeight();
if(windowTop > floatEnd + tableHeight){
top = tableHeight + scrollingTop - windowTop + floatEnd;
unfloat();
} else if (tableOffset.top > windowTop + scrollingTop) {
top = tableOffset.top - windowTop;
refloat();
} else {
top = scrollingTop;
}
left = tableOffset.left - windowLeft;
}
return {top: top, left: left};
};
return positionFn;
}
/**
* 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 windowResizeEvent = function(){
updateScrollingOffsets();
calculateScrollBarSize();
flow = reflow();
var badReflow = flow();
if(badReflow){
flow();
}
calculateFloatContainerPos = calculateFloatContainerPosFn();
repositionFloatContainer = repositionFloatContainerFn();
repositionFloatContainer(calculateFloatContainerPos('resize'), true, true);
};
var reflowEvent = _.debounce(function(){
calculateScrollBarSize();
updateScrollingOffsets();
flow = reflow();
var badReflow = flow();
if(badReflow){
flow();
}
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();
$fthGrp.remove();
$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);