From caacb31a344d4aa779a0774877fba8fdc907af7f Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Thu, 2 Jan 2014 20:28:40 +0000 Subject: [PATCH] need a patched floatThead (https://github.com/mkoryak/floatThead/pull/12) --- .../public/javascripts/jquery.floatThead.js | 659 +++++++++++++++++- 1 file changed, 641 insertions(+), 18 deletions(-) diff --git a/Netdisco/share/public/javascripts/jquery.floatThead.js b/Netdisco/share/public/javascripts/jquery.floatThead.js index 8b91a3bd..e5c764b5 100644 --- a/Netdisco/share/public/javascripts/jquery.floatThead.js +++ b/Netdisco/share/public/javascripts/jquery.floatThead.js @@ -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('
');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;dtd")},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+"]>");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'),s.push(""),f.push("");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&&0r||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=0b+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() 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(''),u=0=D&&!u&&p,h=a(""),P=a(""), - E=a(""),M=a(''),l=a('
'),z=a("
"),L=a(''),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("
").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); \ No newline at end of file +// ==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="",c[0];);return 4
"); + $('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 + '
' + + '
' + ); + $('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 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 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 = $(''); + + 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 = $(""); + var $floatColGroup = $(""); + var $tableColGroup = $(""); + var $fthRow = $(''); //created unstyled elements + var $floatContainer = $('
'); + var $newHeader = $("
"); + var $sizerRow = $(''); + 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("
").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(''); + psuedo.push(""); + } + + 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);