$(function()
{
var $tasksTable = $('#tasksTable');
if(!rowsCount) return $tasksTable.removeClass('loading');
var $tableContainer = $('#tableContainer');
var $tableHeader = $('#tableHeader');
var $tableFooter = $('#tableFooter');
var $tableBody = $('#tableBody');
var $cells = $('#cells');
var $scrollbarContainer = $('#scrollbarContainer');
var $timeline = $('#timeline');
var $timeList = $('#timeList');
var $totalDays = $('#totalDays');
var $scrollbar = $('#scrollbar');
var $window = $(window);
var cellWidth = 56;
var dayWidth = cellWidth * 2;
var lastScrollLeft = 0;
var lastScrollTop = 0;
var lastTimelineWidth;
var isHeaderFixed = false;
var isFooterFixed = false;
var winHeight = $window.height();
var headerHeight = $tableHeader.outerHeight();
var footerHeight = $tableFooter.outerHeight();
var pageFooterHeight = $('#footer').outerHeight();
var currentYear = new Date().getFullYear();
var firstVisibleDayIndex = 0;
var rowLayouts = [];
var maxRowHeight = 0;
var lastHoverRow, lastHoverDay, hoverDelayTimer;
/* Init body bounds */
for (var i = 0; i < groupsCount; ++i)
{
var $group = $('#group-' + i);
var height = $group.outerHeight();
$group.find('.group-tasks').css('min-height', height - 1);
}
bodyBounds = $.extend({}, $tableBody[0].getBoundingClientRect());
/* Init layout of rows */
for (var i = 0; i < rowsCount; ++i)
{
var $task = $('#task-' + i);
var bounds = $task[0].getBoundingClientRect();
var layout = {top: bounds.top - bodyBounds.top, height: bounds.height, bottom: bounds.bottom - bodyBounds.top};
rowLayouts.push(layout);
maxRowHeight = Math.max(maxRowHeight, layout.height);
}
var scrollTopOnLoad = $window.scrollTop();
if(scrollTopOnLoad > 0)
{
bodyBounds.y += scrollTopOnLoad;
bodyBounds.top += scrollTopOnLoad;
bodyBounds.bottom += scrollTopOnLoad;
}
/* Init layout of cells */
var cellsWidth = dayWidth * days.length;
$scrollbar.css('width', cellsWidth);
$timeList.css('width', cellsWidth);
$totalDays.css('width', cellsWidth);
/* Layout timeline */
function layoutTimeline(force)
{
var scrollLeft = $scrollbarContainer.scrollLeft();
var timeLineWidth = $timeline.width();
if(!force && lastScrollLeft === scrollLeft && timeLineWidth === lastTimelineWidth) return;
lastScrollLeft = scrollLeft;
lastTimelineWidth = timeLineWidth;
firstVisibleDayIndex = Math.floor(scrollLeft / dayWidth);
var index = firstVisibleDayIndex;
var visibleWidth = timeLineWidth + dayWidth;
var width = 0;
var $days = $timeList.children('.day-cell').addClass('expired');
var $tDays = $totalDays.children('.day-cell').addClass('expired');
while(width < visibleWidth && days[index])
{
var day = days[index];
var $day = $('#day-' + index);
if($day.length) $day.removeClass('expired');
else
{
$day = $(
[
'
',
'
' + (day.indexOf(currentYear + '-') === 0 ? day.substr(5) : day) + '
',
'
' + consumedText + '
',
'
' + leftText + '
',
'
'
].join('')).css({left: index * dayWidth, width: dayWidth}).appendTo($timeList);
}
var $totalDay = $('#day-total-' + index);
if($totalDay.length) $totalDay.removeClass('expired');
else
{
var totalCounts = counts[day];
$totalDay = $(
[
'',
'
' + (totalCounts ? totalCounts.countConsumed.toFixed(1) : '') + '
',
'
' + (totalCounts ? totalCounts.countLeft.toFixed(1) : '') + '
',
'
'
].join('')).css({left: index * dayWidth, width: dayWidth}).appendTo($totalDays);
}
width += dayWidth;
index++;
}
$days.filter('.expired').remove();
$tDays.filter('.expired').remove();
$timeList.css('left', 0 -scrollLeft);
$totalDays.css('left', 0 -scrollLeft);
}
/* Layout table cells */
function layoutCells(scrollTop)
{
if(typeof scrollTop !== 'number') scrollTop = $window.scrollTop();
var firstVisibleRowIndex = 0;
if(scrollTop > (bodyBounds.top - headerHeight))
{
for(var i = 0; i < rowLayouts.length; ++i)
{
var layout = rowLayouts[i];
firstVisibleRowIndex++;
if(scrollTop < layout.bottom + bodyBounds.top) break;
}
}
var $oldCellList = $cells.children('.data-cell').addClass('expired');
var height = 0;
var visibleHeight = winHeight - headerHeight - pageFooterHeight - footerHeight + maxRowHeight;
var rowIndex = firstVisibleRowIndex < 3 ? 0 : firstVisibleRowIndex;
while(height < visibleHeight && rowLayouts[rowIndex])
{
var layout = rowLayouts[rowIndex];
var dayIndex = firstVisibleDayIndex;
var visibleWidth = lastTimelineWidth + dayWidth;
var width = 0;
var task = taskList[rowIndex];
while(width < visibleWidth && days[dayIndex])
{
var day = days[dayIndex];
var $cell = $('#cell-' + rowIndex + '-' + dayIndex);
if($cell.length) $cell.removeClass('expired');
else
{
var data = task ? task[day] : null;
var consumed = (data ? data.consumed : '') || '';
var left = (data ? data.left : '') || '';
$cell = $(
[
'',
'
' + consumed + '
',
'
' + left + '
',
'
'
].join(''));
$cell.css(
{
left: dayIndex * dayWidth,
top: layout.top + (rowIndex ? 1 : 0),
width: dayWidth,
height: layout.height + (rowIndex ? 0 : 1)
}).appendTo($cells);
}
width += dayWidth;
dayIndex++;
}
height += layout.height;
rowIndex++;
}
$oldCellList.filter('.expired').remove();
$cells.css('left', 0 -lastScrollLeft);
}
/* Layout table cells */
function fixedHeaderFooter(scrollTop, force)
{
if(typeof scrollTop !== 'number')
{
force = scrollTop;
scrollTop = $window.scrollTop();
}
var needFixedHeader = scrollTop > (bodyBounds.top - headerHeight);
if(force || needFixedHeader !== isHeaderFixed)
{
isHeaderFixed = needFixedHeader;
$tableHeader.toggleClass('is-fixed', isHeaderFixed);
$tableContainer.css('padding-top', isHeaderFixed ? headerHeight : 0);
$tableHeader.css(isHeaderFixed ? {left: bodyBounds.left, width: bodyBounds.width} : {left: 'auto', width: 'auto'});
}
var needFixedFooter = (scrollTop + winHeight - pageFooterHeight) < bodyBounds.bottom;
if(force || needFixedFooter !== isFooterFixed)
{
isFooterFixed = needFixedFooter;
$tableFooter.toggleClass('is-fixed', isFooterFixed);
$tableContainer.css('padding-bottom', isFooterFixed ? footerHeight : 0);
$tableFooter.css(isFooterFixed ? {left: bodyBounds.left, width: bodyBounds.width, bottom: pageFooterHeight} : {left: 'auto', width: 'auto', bottom: 'auto'});
}
}
/* Listen events to refresh layout */
$scrollbarContainer.on('scroll', function()
{
layoutTimeline(true);
layoutCells(lastScrollTop);
});
$window.on('scroll', function()
{
var scrollTop = $window.scrollTop();
if(scrollTop === lastScrollTop) return;
lastScrollTop = scrollTop;
fixedHeaderFooter(scrollTop);
layoutCells(scrollTop);
clearHoverEffections();
}).on('resize', function()
{
var bounds = $tableBody[0].getBoundingClientRect();
bodyBounds.width = bounds.width;
bodyBounds.left = bounds.left;
bodyBounds.right = bounds.right;
bodyBounds.x = bounds.x;
winHeight = $window.height();
fixedHeaderFooter(true);
layoutTimeline(true);
clearHoverEffections();
});
/* Clear hover effections */
function clearHoverEffections(row, day)
{
if(row !== false && lastHoverRow > -1)
{
$tableContainer.find('.hover-row').removeClass('hover-row');
lastHoverRow = -1;
}
if(day !== false && lastHoverDay > -1)
{
lastHoverDay = -1;
$tableContainer.find('.hover-day').removeClass('hover-day');
}
};
/* Update hover effections */
function updateHoverEffections()
{
var $cell = $(this);
var row = $cell.data('row');
var day = $cell.data('day');
if(row !== lastHoverRow)
{
clearHoverEffections(1, false);
$tableContainer.find('.day-cell,.data-cell,.group-task').filter('[data-row="' + row + '"]').addClass('hover-row');
lastHoverRow = row;
}
if(day !== lastHoverDay)
{
clearHoverEffections(false, 1);
$tableContainer.find('.day-cell,.data-cell,.group-task').filter('[data-day="' + day + '"]').addClass('hover-day');
lastHoverDay = day;
}
hoverDelayTimer = 0;
}
/* Add mouse hover effections */
$tableContainer.on('mouseenter', '.day-cell,.data-cell,.group-task', function()
{
if(hoverDelayTimer) clearTimeout(hoverDelayTimer);
hoverDelayTimer = setTimeout(updateHoverEffections.bind(this), 20);
}).on('mouseleave', '.day-cell,.data-cell,.group-task', clearHoverEffections);
/* Init layouts */
fixedHeaderFooter(true);
layoutTimeline(true);
layoutCells();
/* Remove loading status */
$tasksTable.removeClass('loading');
});