/*
* Fuel UX Datagrid
* https://github.com/ExactTarget/fuelux
*
* Copyright (c) 2012 ExactTarget
* Licensed under the MIT license.
*/
define(['require','jquery'],function(require) {
var $ = require('jquery');
// Relates to thead .sorted styles in datagrid.less
var SORTED_HEADER_OFFSET = 22;
// DATAGRID CONSTRUCTOR AND PROTOTYPE
var Datagrid = function (element, options) {
this.$element = $(element);
this.$thead = this.$element.find('thead');
this.$tfoot = this.$element.find('tfoot');
this.$footer = this.$element.find('tfoot th');
this.$footerchildren = this.$footer.children().show().css('visibility', 'hidden');
this.$topheader = this.$element.find('thead th');
this.$searchcontrol = this.$element.find('.datagrid-search');
this.$filtercontrol = this.$element.find('.filter');
this.$pagesize = this.$element.find('.grid-pagesize');
this.$pageinput = this.$element.find('.grid-pager input');
this.$pagedropdown = this.$element.find('.grid-pager .dropdown-menu');
this.$prevpagebtn = this.$element.find('.grid-prevpage');
this.$nextpagebtn = this.$element.find('.grid-nextpage');
this.$pageslabel = this.$element.find('.grid-pages');
this.$countlabel = this.$element.find('.grid-count');
this.$startlabel = this.$element.find('.grid-start');
this.$endlabel = this.$element.find('.grid-end');
this.$tbody = $('
').insertAfter(this.$thead);
this.$colheader = $('').appendTo(this.$thead);
this.options = $.extend(true, {}, $.fn.datagrid.defaults, options);
// Shim until v3 -- account for FuelUX select or native select for page size:
if (this.$pagesize.hasClass('select')) {
this.options.dataOptions.pageSize = parseInt(this.$pagesize.select('selectedItem').value, 10);
} else {
this.options.dataOptions.pageSize = parseInt(this.$pagesize.val(), 10);
}
// Shim until v3 -- account for older search class:
if (this.$searchcontrol.length <= 0) {
this.$searchcontrol = this.$element.find('.search');
}
this.columns = this.options.dataSource.columns();
this.$nextpagebtn.on('click', $.proxy(this.next, this));
this.$prevpagebtn.on('click', $.proxy(this.previous, this));
this.$searchcontrol.on('searched cleared', $.proxy(this.searchChanged, this));
this.$filtercontrol.on('changed', $.proxy(this.filterChanged, this));
this.$colheader.on('click', 'th', $.proxy(this.headerClicked, this));
if(this.$pagesize.hasClass('select')) {
this.$pagesize.on('changed', $.proxy(this.pagesizeChanged, this));
} else {
this.$pagesize.on('change', $.proxy(this.pagesizeChanged, this));
}
this.$pageinput.on('change', $.proxy(this.pageChanged, this));
this.renderColumns();
if (this.options.stretchHeight) this.initStretchHeight();
this.renderData();
};
Datagrid.prototype = {
constructor: Datagrid,
renderColumns: function () {
var self = this;
this.$footer.attr('colspan', this.columns.length);
this.$topheader.attr('colspan', this.columns.length);
var colHTML = '';
$.each(this.columns, function (index, column) {
colHTML += '' + column.label + ' | ';
});
self.$colheader.append(colHTML);
},
updateColumns: function ($target, direction) {
this._updateColumns(this.$colheader, $target, direction);
if (this.$sizingHeader) {
this._updateColumns(this.$sizingHeader, this.$sizingHeader.find('th').eq($target.index()), direction);
}
},
_updateColumns: function ($header, $target, direction) {
var className = (direction === 'asc') ? 'icon-chevron-up' : 'icon-chevron-down';
$header.find('i.datagrid-sort').remove();
$header.find('th').removeClass('sorted');
$('').addClass(className + ' datagrid-sort').appendTo($target);
$target.addClass('sorted');
},
updatePageDropdown: function (data) {
var pageHTML = '';
for (var i = 1; i <= data.pages; i++) {
pageHTML += '' + i + '';
}
this.$pagedropdown.html(pageHTML);
},
updatePageButtons: function (data) {
if (data.page === 1) {
this.$prevpagebtn.attr('disabled', 'disabled');
} else {
this.$prevpagebtn.removeAttr('disabled');
}
if (data.page === data.pages) {
this.$nextpagebtn.attr('disabled', 'disabled');
} else {
this.$nextpagebtn.removeAttr('disabled');
}
},
renderData: function () {
var self = this;
this.$tbody.html(this.placeholderRowHTML(this.options.loadingHTML));
this.options.dataSource.data(this.options.dataOptions, function (data) {
var itemdesc = (data.count === 1) ? self.options.itemText : self.options.itemsText;
var rowHTML = '';
self.$footerchildren.css('visibility', function () {
return (data.count > 0) ? 'visible' : 'hidden';
});
self.$pageinput.val(data.page);
self.$pageslabel.text(data.pages);
self.$countlabel.text(data.count + ' ' + itemdesc);
self.$startlabel.text(data.start);
self.$endlabel.text(data.end);
self.updatePageDropdown(data);
self.updatePageButtons(data);
$.each(data.data, function (index, row) {
rowHTML += '';
$.each(self.columns, function (index, column) {
rowHTML += '' + row[column.property] + ' | ';
});
rowHTML += '
';
});
if (!rowHTML) rowHTML = self.placeholderRowHTML('0 ' + self.options.itemsText);
self.$tbody.html(rowHTML);
self.stretchHeight();
self.$element.trigger('loaded');
});
},
placeholderRowHTML: function (content) {
return '' + content + ' |
';
},
headerClicked: function (e) {
var $target = $(e.target);
if (!$target.hasClass('sortable')) return;
var direction = this.options.dataOptions.sortDirection;
var sort = this.options.dataOptions.sortProperty;
var property = $target.data('property');
if (sort === property) {
this.options.dataOptions.sortDirection = (direction === 'asc') ? 'desc' : 'asc';
} else {
this.options.dataOptions.sortDirection = 'asc';
this.options.dataOptions.sortProperty = property;
}
this.options.dataOptions.pageIndex = 0;
this.updateColumns($target, this.options.dataOptions.sortDirection);
this.renderData();
},
pagesizeChanged: function (e, pageSize) {
if(pageSize) {
this.options.dataOptions.pageSize = parseInt(pageSize.value, 10);
} else {
this.options.dataOptions.pageSize = parseInt($(e.target).val(), 10);
}
this.options.dataOptions.pageIndex = 0;
this.renderData();
},
pageChanged: function (e) {
var pageRequested = parseInt($(e.target).val(), 10);
pageRequested = (isNaN(pageRequested)) ? 1 : pageRequested;
var maxPages = this.$pageslabel.text();
this.options.dataOptions.pageIndex =
(pageRequested > maxPages) ? maxPages - 1 : pageRequested - 1;
this.renderData();
},
searchChanged: function (e, search) {
this.options.dataOptions.search = search;
this.options.dataOptions.pageIndex = 0;
this.renderData();
},
filterChanged: function (e, filter) {
this.options.dataOptions.filter = filter;
this.options.dataOptions.pageIndex = 0;
this.renderData();
},
previous: function () {
this.$nextpagebtn.attr('disabled', 'disabled');
this.$prevpagebtn.attr('disabled', 'disabled');
this.options.dataOptions.pageIndex--;
this.renderData();
},
next: function () {
this.$nextpagebtn.attr('disabled', 'disabled');
this.$prevpagebtn.attr('disabled', 'disabled');
this.options.dataOptions.pageIndex++;
this.renderData();
},
reload: function () {
this.options.dataOptions.pageIndex = 0;
this.renderData();
},
initStretchHeight: function () {
this.$gridContainer = this.$element.parent();
this.$element.wrap('');
this.$stretchWrapper = this.$element.parent();
this.$headerTable = $('
').attr('class', this.$element.attr('class'));
this.$footerTable = this.$headerTable.clone();
this.$headerTable.prependTo(this.$gridContainer).addClass('datagrid-stretch-header');
this.$thead.detach().appendTo(this.$headerTable);
this.$sizingHeader = this.$thead.clone();
this.$sizingHeader.find('tr:first').remove();
this.$footerTable.appendTo(this.$gridContainer).addClass('datagrid-stretch-footer');
this.$tfoot.detach().appendTo(this.$footerTable);
},
stretchHeight: function () {
if (!this.$gridContainer) return;
this.setColumnWidths();
var targetHeight = this.$gridContainer.height();
var headerHeight = this.$headerTable.outerHeight();
var footerHeight = this.$footerTable.outerHeight();
var overhead = headerHeight + footerHeight;
this.$stretchWrapper.height(targetHeight - overhead);
},
setColumnWidths: function () {
if (!this.$sizingHeader) return;
this.$element.prepend(this.$sizingHeader);
var $sizingCells = this.$sizingHeader.find('th');
var columnCount = $sizingCells.length;
function matchSizingCellWidth(i, el) {
if (i === columnCount - 1) return;
var $el = $(el);
var $sourceCell = $sizingCells.eq(i);
var width = $sourceCell.width();
// TD needs extra width to match sorted column header
if ($sourceCell.hasClass('sorted') && $el.prop('tagName') === 'TD') width = width + SORTED_HEADER_OFFSET;
$el.width(width);
}
this.$colheader.find('th').each(matchSizingCellWidth);
this.$tbody.find('tr:first > td').each(matchSizingCellWidth);
this.$sizingHeader.detach();
}
};
// DATAGRID PLUGIN DEFINITION
$.fn.datagrid = function (option) {
return this.each(function () {
var $this = $(this);
var data = $this.data('datagrid');
var options = typeof option === 'object' && option;
if (!data) $this.data('datagrid', (data = new Datagrid(this, options)));
if (typeof option === 'string') data[option]();
});
};
$.fn.datagrid.defaults = {
dataOptions: { pageIndex: 0, pageSize: 10 },
loadingHTML: '',
itemsText: 'items',
itemText: 'item'
};
$.fn.datagrid.Constructor = Datagrid;
});