arrow-admin
Version:
Arrow Admin Website
434 lines (392 loc) • 14.1 kB
JavaScript
/* jshint undef: true */
/* global History */
define(['jquery', 'loader','numeral', 'history', 'datatables', 'datatables-bootstrap', 'resize-element', 'datatablesColVis'], function($) {
return {
/**
* Creates an appcelerator style dataTable with a variety of new options (found within 'base')
* @param opts
* @param $container
* @returns {*}
*/
createTable: function(opts, $container) {
var base = {
'scrollY': '100%',
//'scrollX': '100%',
//scrollXInner = '100%',
'scrollCollapse': false,
'paging': true,
'noData': { // what to show when the table has no data
'icon': 'icon-chart-bar',
'title': 'No data available for current selection',
'description': ''
},
'noDataFilter': { // what to show when the filter has no results
'icon': 'icon-chart-bar',
'title': 'No data available for current selection',
'description': ''
},
'loadMore': true, // adds a button to the bottom of the table to load more records
//'infiniteScrolling': true, // allows infinite scrolling
'pageLength': 25,
'stripeClasses': ['appc_table_odd', 'appc_table_even'],
//'title': 'Logs', // adds a title to the table
/*'buttons': [ // an array of buttons can be added with actions
{
'text': 'New',
'icon': 'icon-plus',
'action': function(el) {console.log('new');}
'hidden': true // hides the button after creation
},
{
'text': 'Advanced',
'class': '', // any extra classes to add to the button
'id': '' // the id to assign the button. by default it is appc-btn-i, where i is the index of the button
'action': function(el) {console.log('advanced');},
'addTo': 'search' // where to append the button to. by default it is added in the top-right
}
], */
'colVis': {
'buttonText': 'Show / Hide Fields',
'restore': 'reset',
'showAll': 'all',
'showNone': 'none',
'overlayFade': 0
}//,
//selectRow: true, // allow the selection of rows (also takes a function which is instigated on selection)
//'advancedHeader': true // an extra header allowing for better horizontal scrolling and showing/hiding fields
};
opts = overwriteKeys(base, opts);
if (opts.loadMore) {
opts.paging = true;
}
if (opts.infiniteScrolling) {
opts.paging = true;
opts.pageLength = 5;
}
if (opts.advancedHeader) {
opts.scrollX = '100%';
opts.sScrollXInner = '100%';
}
function isObjectAndNotArray(object) {
return (typeof object === 'object' && !Array.isArray(object));
}
function overwriteKeys(baseObject, overrideObject, createNew) {
if (!baseObject) {
baseObject = {};
}
if (createNew) {
baseObject = JSON.parse(JSON.stringify(baseObject));
}
Object.keys(overrideObject).forEach(function(key) {
if (isObjectAndNotArray(baseObject[key]) && isObjectAndNotArray(overrideObject[key])) {
overwriteKeys(baseObject[key], overrideObject[key]);
}
else {
baseObject[key] = overrideObject[key];
}
});
return baseObject;
}
var $table = $('#appc_table');
$table.dataTable(opts);
var dataTableRef = $table.DataTable();
// Do some shuffling
var $tableFilter = $('#appc_table_filter');
$tableFilter
// Add styling.
.addClass('form-group has-feedback mg-top-m')
// Add search icon and clear button.
.append('<i class=\'icon-search-1 form-control-feedback icon-clr-apple icon-md2\'></i>')
// Wrap in the search form (for styling purposes).
.wrap('<form class=\'search\' onsubmit=\'return false\'></form>')
// Remove input-sm class.
.find('input.input-sm').removeClass('input-sm');
var $labelContents = $tableFilter.find('label').contents();
// Remove 'Search: ' text.
$labelContents.filter(function () { return (this.nodeType === 3); }).remove();
//remove the label wrapper
$labelContents.unwrap();
// We need to move some of the elements around a bit.
var search = $tableFilter.parent(),
pageSize = $('#appc_table_length'),
pageInfo = $('#appc_table_info'),
pages = $('#appc_table_paginate');
pageSize.addClass('form-inline').children().addClass('pull-right');
// make sure table background is set
$('#mainContent').removeClass('panel');
$('.dataTables_scroll').addClass('panel');
var newHeader = $('' +
'<div id=\'appc_table_new_header\' class=\'panel-heading panel-heading-multiline table_header\'>' +
'<div class=\'pull-left\'></div>' +
'<div class=\'txt-center\'></div>' +
'<div class=\'pull-right txt-right pd-top-m\'></div>' +
'</div>'
),
newFooter = $('' +
'<div id=\'appc_table_new_footer\'>' +
'<div class=\'pull-left\'></div>' +
'<div class=\'txt-center\'></div>' +
'<div class=\'pull-right txt-right pd-top-m\'></div>' +
'</div>'
);
$('#appc_table_wrapper')
.prepend(newHeader)
.append(newFooter);
/**
* hides the 'load more' button if there is nothing more to load
*/
function hideButtonIfNoMoreData(btn) {
if (dataTableRef.page.info().pages > 1) {
btn.show();
} else {
btn.hide();
}
}
// show loadMore
if (opts.infiniteScrolling) {
pageSize.remove();
pages.remove();
var avgPerRow = 33,//px for each row
canScroll =
$container.innerHeight() <= $container.prop('scrollHeight') ||
$container.innerHeight() <= $container.prop('scrollHeight') + 1 ||
$container.innerHeight() <= $container.prop('scrollHeight') - 1 ;
if (canScroll && (dataTableRef.page.info().pages > 1)) {
// calculate how many more records to add to fill the window
var usedHeight = 125 - 40;//px for the header / footer
usedHeight += avgPerRow * dataTableRef.page.info().length;
var leftoverHeight = ($container.innerHeight() - usedHeight);
var toAdd = (leftoverHeight/avgPerRow);
if (toAdd < 0) {
toAdd = 0;
}
dataTableRef.page.len(dataTableRef.page.len() + toAdd).draw();
}
$container.scroll(function () {
var maxScroll = $container.prop('scrollHeight') - $container.innerHeight();
// if the scroll bar is ~2 records from the bottom then add some more (if there are any left)
if (($container.scrollTop() >= maxScroll - (avgPerRow * 2)) && (dataTableRef.page.info().pages > 1)) {
dataTableRef.page.len(dataTableRef.page.len() + opts.pageLength).draw();
}
});
} else if (opts.loadMore) {
newFooter
.addClass('dataTables_more')
.find('.txt-center')
.append('<button type=\'button\'>Load more</button>');
$('.dataTables_scroll').addClass('can_load_more');
// load more elements on button click
var $loadMore = $('.dataTables_more').find('button');
hideButtonIfNoMoreData($loadMore);
$loadMore.click(function () {
dataTableRef.page.len(dataTableRef.page.len() + opts.pageLength).draw();
hideButtonIfNoMoreData($loadMore);
});
dataTableRef.on('search.dt', function () {
hideButtonIfNoMoreData($loadMore);
});
pageSize.remove();
pages.remove();
} else if (opts.paging){
newFooter.find('> div:nth-child(3)').append(pageSize);
newFooter.find('> div:nth-child(2)').append(pages);
}
newHeader.find('> div:nth-child(1)')
.append(pageInfo)
.addClass('col-md-4 col-sm-8')
.removeClass('pull-left');
newHeader.find('> div:nth-child(2)')
.append(search)
.addClass('col-md-4 col-sm-12');
// Advanced Features
// add title to the table
if (opts.title) {
pageInfo.parent()
.addClass('has-title')
.prepend('<div class=\'table-title\'>' + opts.title + '</div>');
}
var $search = $('#appc_table_new_header form.search');
$search.parent().addClass('search-holder');
// add buttons to the table
if (opts.buttons && opts.buttons.length) {
var addButton = function(btn) {
var wrapper = $('#appc_table_wrapper'),
$btn = '<div class=\'btn btn-default pull-right txt-14 ' + (btn.class || '') + ' \' id=' + btn.id + '>' +
(btn.icon ? '<i class=' + btn.icon + '></i>' : '') +
(btn.text ? btn.text : '') +
'</div>';
if (btn.addTo === 'search') {
var $searchHolder = wrapper.find('.search-holder')
.addClass('has-buttons col-md-6').removeClass('col-md-4');
$searchHolder.siblings('.col-md-4').addClass('col-md-3').removeClass('col-md-4');
$btn = $($btn)
.prependTo($searchHolder)
.addClass('btn-search');
} else {
$btn = $($btn)
.prependTo(wrapper)
.addClass('btn-header');
}
btn.hidden && $btn.hide();
return $btn.click(btn.action);
};
var width = 1; // because some wierd stuff happens if the width is exactly the size of the buttons
for (var i = 0, len = opts.buttons.length, btn = opts.buttons[i]; i < len; ++i, btn = opts.buttons[i]) {
!(btn.id) && (btn.id = 'appc-btn-' + i);
var $btn = addButton(btn);
if (btn.addTo === 'search') {
width += $btn.outerWidth(true);
}
}
$search.css('padding-right', width);
}
var scrollBody = $('.dataTables_scrollBody'),
loadColVis = true;
// add advanced table options. This does the following:
// - allows the user to show/hide fields
// - adds better horizontal field management
if (opts.advancedHeader) {
$('#appc_table_wrapper .row').first().
html(
'<div class=\'col-xs-12\'>' +
'<div class=\'advanced-header\'</div>' +
'</div>');
$('.dataTables_scroll').addClass('has-advanced-header');
var advancedHeader = $('.advanced-header');
// Add 'show/hide fields' area
advancedHeader.append('<div class=\'pull-left\'></div>');
// Add the colVis button
var colVis = new $.fn.dataTable.ColVis(dataTableRef, opts.colVis);
$(colVis.button())
.appendTo('.advanced-header .pull-left')
.find('button')
.addClass('btn btn-default')
.removeClass('ColVis_Button')
.click(function () {
if (loadColVis) {
// Need to push the UI changes to the end of the call stack
setTimeout(function () {
$('.ColVis_collection')
.prepend('<div class=\'ColVis_fields\'></div>');
$('.ColVis_collection li:not(.ColVis_Special)').each(function() {
var self = $(this);
self.find('input')
.after(self.find('span'))
.wrap('<div class=\'pull-right\'></div>')
.after('<div class=\'font-checkbox\'></div>');
$('.ColVis_fields').append(self);
});
loadColVis = false;
}, 0);
}
});
// more fields area
advancedHeader.append(
'<div class=\'pull-right\'>' +
'More Fields' +
'<div class=\'btn-group\'>' +
'<div class=\'btn btn-default btn-left\'><i class=\'icon-left-open\'></i></div>' +
'<div class=\'btn btn-default btn-right\'><i class=\'icon-right-open\'></i></div>' +
'</div>' +
'</div>');
setShiftButtons();
// recalculate scroll on resize
window.addResizeListener(document.getElementById('appc_table_wrapper'), function() {
setShiftButtons();
//dataTableRef.columns.adjust(false).draw(false);
});
advancedHeader.find('.btn-left').click(function(){
scrollTable(scrollBody.scrollLeft() + getScrollAmountLeft());
});
advancedHeader.find('.btn-right').click(function(){
scrollTable(getScrollAmountRight());
});
}
function scrollTable(amount) {
scrollBody.scrollLeft(amount);
setShiftButtons();
}
function getScrollAmountRight() {
var header = $('.dataTables_scrollHeadInner th'),
headerWidths = header.map(function(){
return $(this).outerWidth();
}).get(),
currentWidth = 0,
i = -1;
while (currentWidth < scrollBody.width() + scrollBody.scrollLeft()) {
i++;
currentWidth += headerWidths[i];
}
// the header at i is not visible/partially visible
return (currentWidth - headerWidths[i]);
}
function getScrollAmountLeft() {
return -300; //TODO: sort out left alternative to the scroll right above.
}
function setShiftButtons() {
var advHeader = $('.advanced-header');
var btnLeft = $('.btn-left', advHeader);
// if we can scroll left
if (scrollBody.scrollLeft() !== 0) {
btnLeft.removeAttr('disabled');
} else {
btnLeft.attr('disabled', 'disabled');
}
var btnRight = $('.btn-right', advHeader);
// if we can scroll right
if (scrollBody.width() + scrollBody.scrollLeft() !== scrollBody.prop('scrollWidth')) {
btnRight.removeAttr('disabled');
} else {
btnRight.attr('disabled', 'disabled');
}
}
// add field selection
if (opts.selectRow) {
dataTableRef.on('click', 'tr', function (_event) {
$(this).toggleClass('selected');
typeof opts.selectRow === 'function' && opts.selectRow(_event, this);
_event.isDefaultPrevented() && $(this).toggleClass('selected');
});
}
// add no-data state
$('#appc_table_wrapper').append(
'<div class=\'no-data txt-center\'>' +
'<i class=\'' + opts.noData.icon + ' icon-xxl\'></i><div>' +
'<span>' + opts.noData.title + '</span><br>' +
opts.noData.description + '</div>' +
'</div>');
this._handleNoData();
$table.data('loaded', true);
return dataTableRef;
},
replaceData: function(table, _data) {
this._handleNoData(function () {
table
.clear()
.rows.add(_data)
.search('')
.draw();
});
},
reloadTable: function(table, draw) {
this._handleNoData(function () {
table
.search('')
.draw(draw);
});
},
_handleNoData: function(fn) {
var tableWrapper = $('#appc_table_wrapper');
if (fn) {
// need to do this otherwise the header will mess up
tableWrapper.removeClass('wrapper-no-data');
fn();
}
if ($('.dataTables_empty').length) {
tableWrapper.addClass('wrapper-no-data');
} else {
tableWrapper.removeClass('wrapper-no-data');
}
}
};
});