(function (angular) { var module = angular.module('framework.directives.UI'); module.directive('tableFor', tableForDirective); module.directive('tableTitlebar', tableTitlebarDirective); module.directive('tableHeading', tableHeadingDirective); module.directive('tableDataRows', tableDataRowsDirective); module.directive('tableFooter', tableFooterDirective); module.filter('publish', publishFilter); module.filter('softFilter', softFilter); module.filter('pageGroup', pageGroupFilter); tableForDirective.$inject = ['$parse', 'Model', '$modal', '$q', '$filter', '$timeout']; function tableForDirective($parse, Model, $modal, $q, $filter, $timeout) { var defaults = { wrapperClass: 'table-responsive', tableClass: 'table table-bordered', minWidth: '600px' } function linkFn(scope, element, attrs, ctrls, transcludeFn) { var externalSearch = $parse(attrs.filtering)(scope); var externalSorting = $parse(attrs.sorting)(scope); var externalGrouping = $parse(attrs.grouping)(scope); var externalPaging = $parse(attrs.paging)(scope); scope.$schema = Model[attrs.schema].$schema; scope.$table = { actionColumn: false, selectionColumn: false, cardTemplate: '{{$row | json}}', toggleSelectAll: toggleSelectAll, toggleRowSelection: toggleRowSelection, toggleGroupSelection: toggleGroupSelection, allSelected: false, reset: reset, configureColumns: configureColumns, sort: sort, getSortingClass: getSortingClass }; scope.$columns = generateColumns(scope.$schema); scope.$filtering = externalSearch || { $: '' } scope.$sorting = externalSorting || []; scope.$grouping = externalGrouping || { value: undefined }; scope.$paging = externalPaging || { currentPage: 1, totalItems: 0, maxPerPage: 10 }; scope.$data = {}; //object as it's grouped scope.$datasource = function () { return arguments[4]; //returns existing data by default; }; var datasource = $parse(attrs.datasource)(scope); if (angular.isArray(datasource)) { scope.$datasource = generateLocalDatasource(datasource); } else if (angular.isFunction(datasource)) { scope.$datasource = datasource; } else { scope.$datasource = generateModelDatasource(scope.$schema.query); } scope.$watchCollection(function () { return scope.$filtering; }, debouncedUpdateData); scope.$watchCollection(function () { return scope.$sorting; }, debouncedUpdateData); scope.$watchCollection(function () { return scope.$grouping; }, debouncedUpdateData); scope.$watch(function () { return scope.$paging.currentPage; }, debouncedUpdateData); scope.$watch(function () { return scope.$paging.maxPerPage; }, debouncedUpdateData); var updateDebounce = null; function debouncedUpdateData() { if (updateDebounce) { $timeout.cancel(updateDebounce); updateDebounce = null; } updateDebounce = $timeout(updateData, 500); } function updateData() { for (var key in scope.$data) { delete scope.$data[key]; } scope.$data.$loading = true; scope.$data.$error = null; scope.$datasource(scope.$filtering, scope.$sorting, scope.$grouping, scope.$paging, scope.$data).then(dataReceived, dataError, dataNotified); } function dataReceived(data) { for (var key in scope.$data) { delete scope.$data[key]; } scope.$data.$loading = false; angular.extend(scope.$data, data); } function dataError(error) { for (var key in scope.$data) { delete scope.$data[key]; } scope.$data.$loading = false; scope.$data.$error = error; } function dataNotified(dataLength) { scope.$paging.totalItems = dataLength; } transcludeFn(scope, function (clone, scope) { element.find('table').append(clone); }); function generateLocalDatasource(initialData) { var data = initialData; return function (filtering, sorting, grouping, paging, existing) { var deferred = $q.defer(); setTimeout(function () { var filter = $filter('filter'); var orderBy = $filter('orderBy'); var groupBy = $filter('groupBy'); var pageGroup = $filter('pageGroup'); deferred.notify(filter(data, filtering).length); var amendedSorting = [grouping.value]; [].push.apply(amendedSorting, sorting); var result = pageGroup(groupBy(orderBy(filter(data, filtering), amendedSorting), grouping.value), paging); deferred.resolve(result); }, 0); return deferred.promise; } } function generateModelDatasource(modelQuery) { var queryBase = modelQuery; return function (filtering, sorting, grouping, paging, existing) { var deferred = $q.defer(); query = queryBase(); if (grouping.value) { query.orderBy(grouping.value); } angular.forEach(ordering, function (o) { query.orderBy(o); }); setTimeout(function () { var filter = $filter('filter'); var groupBy = $filter('groupBy'); var pageGroup = $filter('pageGroup'); deferred.notify(filter(data, filtering).length); var amendedSorting = [grouping.value]; [].push.apply(amendedSorting, sorting); var result = pageGroup(groupBy(orderBy(filter(data, filtering), amendedSorting), grouping.value), paging); deferred.resolve(result); }, 0); return deferred.promise; } } function generateColumns(modelSchema) { var columns = []; angular.forEach(modelSchema, function (value, key) { var config = angular.extend({}, value, value.table || {}); if (config.hidden) { return; } if (!angular.isDefined(config.visible)) { config.visible = true; } if (!angular.isDefined(config.binding) && config.type === moment) { config.binding = key + ".toDate() | date:'dd/MM/yyyy'"; } columns.push({ key: key, title: config.display || key, accessor: angular.isFunction(config.binding) ? config.binding : buildAccessor(config.binding || key), binding: config.binding || key, visible: config.visible, filters: config.filters }); }); return columns; } function toggleSelectAll() { scope.$table.allSelected = !scope.$table.allSelected; angular.forEach(scope.$data, function (group) { toggleGroupSelection(group, scope.$table.allSelected); }); } function toggleRowSelection(group, row, force) { row.$selected = angular.isDefined(force) ? force : !row.$selected; var selected = group.filter(function (d) { return d.$selected; }); group.$selected = selected.length === group.length; var groups = 0; var selectedGroups = 0; angular.forEach(scope.$data, function (group, title) { if (title[0] == '$') { return; } groups++; if (group.$selected) selectedGroups++; }); scope.$table.allSelected = groups === selectedGroups; } function toggleGroupSelection(group, force) { if (angular.isDefined(force)) { group.$selected = force; } else { group.$selected = !group.$selected; } angular.forEach(group, function (row) { row.$selected = group.$selected; }); if (!angular.isDefined(force)) { var groups = 0; var selectedGroups = 0; angular.forEach(scope.$data, function (group, title) { if (title[0] == '$') { return; } groups++; if (group.$selected) selectedGroups++; }); debugger; scope.$table.allSelected = groups === selectedGroups; } } function setAreAllSelected() { scope.$table.allSelected = false; } function sort(col) { //multisort var currentSort = scope.$sorting[0]; if (col == currentSort) { scope.$sorting[0] = '-' + col; } else if ('-' + col == currentSort) { scope.$sorting[0] = col; } else { scope.$sorting.length = 0; scope.$sorting.push(col); } } function getSortingClass(col) { if (col == scope.$sorting[0]) { return 'icon-Arrow-Down2 brand-primary'; } else if ('-' + col == scope.$sorting[0]) { return 'icon-Arrow-Up2 brand-primary'; } else { return 'icon-Arrow-Down'; } } function reset() { scope.$filtering = externalSearch || { $: '' } scope.$sorting = []; //TODO: Deselect ALL scope.$columns.length = 0; [].push.apply(scope.$columns, generateColumns(scope.$schema)); } function buildAccessor(fieldName) { console.log('return row.' + fieldName + ';'); window.count = window.count || 0; return new Function('row', 'return row.' + fieldName + ';'); } function configureColumns() { $modal.open({ template: '' + '