\ No newline at end of file
diff --git a/layouts/module/module-base.layout.html b/layouts/module/module-base.layout.html
new file mode 100644
index 0000000..bbf6a5f
--- /dev/null
+++ b/layouts/module/module-base.layout.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/layouts/module/module-cards.controller.js b/layouts/module/module-cards.controller.js
new file mode 100644
index 0000000..530beea
--- /dev/null
+++ b/layouts/module/module-cards.controller.js
@@ -0,0 +1,37 @@
+(function (angular) {
+ var module = angular.module('framework.UI.module',[]);
+
+ module.controller('ModuleCardsController', moduleCardsController);
+
+ moduleCardsController.$inject = ['$scope', 'rData', 'rNavTo','rAccessors', '$state', '$interval', '$timeout'];
+ function moduleCardsController($scope, rData, rNavTo, rAccessors, $state, $interval, $timeout) {
+ $scope.rawItems = rData;
+ $scope.accessors = rAccessors;
+ $scope.rNavTo = rNavTo;
+ $scope.showScore = false;
+ $scope.chosenGroup = null;
+ $scope.cardLimit = 2;
+
+ $scope.groups = {};
+ rData = rData.sort(function(a,b){
+ var valA = rAccessors.Title(a), valB = rAccessors.Title(b);
+ if(valA < valB) {
+ return -1;
+ } else if (valA > valB){
+ return 1;
+ } else
+ return 0;
+ });
+
+ var i;
+
+ for(i = 0; i < rData.length; i++) {
+ var title = rAccessors.Group(rData[i]);
+ if($scope.groups[title])
+ $scope.groups[title].push(rData[i]);
+ else
+ $scope.groups[title] = [rData[i]];
+ }
+ }
+
+})(angular);
\ No newline at end of file
diff --git a/layouts/module/module-cards.partial.html b/layouts/module/module-cards.partial.html
new file mode 100644
index 0000000..d8d0286
--- /dev/null
+++ b/layouts/module/module-cards.partial.html
@@ -0,0 +1,26 @@
+
\ No newline at end of file
diff --git a/layouts/module/module-item.controller.js b/layouts/module/module-item.controller.js
new file mode 100644
index 0000000..7c15440
--- /dev/null
+++ b/layouts/module/module-item.controller.js
@@ -0,0 +1,14 @@
+(function (angular) {
+ var module = angular.module('framework.UI.module');
+
+ module.controller('ModuleItemViewController', moduleItemViewController);
+
+ moduleItemViewController.$inject = ['$scope', 'rTabs', 'rData', 'rAccessors'];
+
+ function moduleItemViewController($scope, rTabs, rData, rAccessors) {
+ $scope.item = rData[0];
+ $scope.accessors = rAccessors;
+ $scope.tabDefs = rTabs;
+ }
+
+})(angular);
\ No newline at end of file
diff --git a/layouts/module/module-item.layout.html b/layouts/module/module-item.layout.html
new file mode 100644
index 0000000..c981b06
--- /dev/null
+++ b/layouts/module/module-item.layout.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+
{{accessors.Title(item)}}
+
{{accessors.SubTitle(item)}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/layouts/module/module-table.controller.js b/layouts/module/module-table.controller.js
new file mode 100644
index 0000000..739ccb6
--- /dev/null
+++ b/layouts/module/module-table.controller.js
@@ -0,0 +1,22 @@
+(function (angular) {
+ var module = angular.module('framework.UI.module');
+
+ module.controller('ModuleTableController', moduleCardsController);
+
+ moduleCardsController.$inject = ['$scope', 'rData', 'rNavTo', 'rSchema', '$state', '$q'];
+ function moduleCardsController($scope, rData, rNavTo, rSchema, $state, $q) {
+ $scope.items = rData;
+
+ function n() {
+ console.log(arguments);
+ return $q.when(rData);
+ };
+
+ $scope.viewItem = viewItem;
+ $scope.Schema = rSchema;
+ function viewItem(item) {
+ $state.go(rNavTo, { Id: 11 });
+ }
+ }
+
+})(angular);
\ No newline at end of file
diff --git a/layouts/module/module-table.partial.html b/layouts/module/module-table.partial.html
new file mode 100644
index 0000000..01c7136
--- /dev/null
+++ b/layouts/module/module-table.partial.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+ View
+
+
+
+
+
+
\ No newline at end of file
diff --git a/layouts/structure/page.controller.js b/layouts/structure/page.controller.js
new file mode 100644
index 0000000..2f8627a
--- /dev/null
+++ b/layouts/structure/page.controller.js
@@ -0,0 +1,12 @@
+(function (window, angular) {
+ var app = angular.module("framework.UI.structure", []);
+
+ app.controller('PageController', pageController);
+
+ pageController.$inject = ['$scope', '$interval'];
+
+ function pageController($scope, $interval) {
+ setupUIElements();
+ };
+
+})(window, angular);
\ No newline at end of file
diff --git a/layouts/structure/page.layout.html b/layouts/structure/page.layout.html
new file mode 100644
index 0000000..fc095d5
--- /dev/null
+++ b/layouts/structure/page.layout.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/layouts/table-page/table-page.layout.html b/layouts/table-page/table-page.layout.html
new file mode 100644
index 0000000..489f478
--- /dev/null
+++ b/layouts/table-page/table-page.layout.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/services/ConsoleLoggingProvider.service.js b/services/ConsoleLoggingProvider.service.js
new file mode 100644
index 0000000..e44b1ee
--- /dev/null
+++ b/services/ConsoleLoggingProvider.service.js
@@ -0,0 +1,67 @@
+(function () {
+ var app = angular.module('framework.services.logging');
+
+ app.service('ConsoleLoggingProvider', ['$window', consoleLoggingProvider]);
+
+ function consoleLoggingProvider($window) {
+ this.log = log;
+ this.debug = debug;
+ this.info = info;
+ this.warning = warning;
+ this.error = error;
+
+ function log(level, catagory, msg, others) {
+ switch (level) {
+ case 1:
+ debug(catagory, msg, others);
+ break;
+ case 2:
+ info(catagory, msg, others);
+ break;
+ case 3:
+ warning(catagory, msg, others);
+ break;
+ case 4:
+ error(catagory, msg, exception, others);
+ break;
+ default:
+ debug(catagory, msg, others);
+ break;
+ }
+ }
+
+ function debug(catagory, msg, others) {
+ var date = new Date();
+ console.debug(date.toISOString() + ' - ' + catagory.toUpperCase() + ' - ' + msg);
+ angular.forEach(others, function (val, ind) {
+ console.debug('additional info ' + ind + ': ', val);
+ });
+ }
+
+ function info(catagory, msg, others) {
+ var date = new Date();
+ console.info(date.toISOString() + ' - ' + catagory.toUpperCase() + ' - ' + msg);
+ angular.forEach(others, function (val, ind) {
+ console.info('additional info ' + ind + ': ', val);
+ });
+ }
+
+ function warning(catagory, msg, others) {
+ var date = new Date();
+ console.warn(date.toISOString() + ' - ' + catagory.toUpperCase() + ' - ' + msg);
+ angular.forEach(others, function (val, ind) {
+ console.warn('additional info ' + ind + ': ', val);
+ });
+ }
+
+ function error(catagory, msg, exception, others) {
+ var date = new Date();
+ console.error(date.toISOString() + ' - ' + catagory.toUpperCase() + ' - ' + msg);
+ console.error('original exception: ' + exception);
+ angular.forEach(others, function (val, ind) {
+ console.error('additional info ' + ind + ': ', val);
+ });
+ }
+ }
+
+})();
\ No newline at end of file
diff --git a/services/Logger.service.js b/services/Logger.service.js
new file mode 100644
index 0000000..b24f748
--- /dev/null
+++ b/services/Logger.service.js
@@ -0,0 +1,139 @@
+(function (window, angular) {
+ var app = angular.module("framework.services.logging", []);
+
+ app.provider('Logger', function () {
+ var providers = [];
+ var hiddenCatagorys = [];
+ var loggingLevel = 1;
+
+ this.DEBUG = 1;
+ this.INFO = 2;
+ this.WARNING = 3;
+ this.ERROR = 4;
+
+ this.setGlobalLoggingLevel = setGlobalLoggingLevel;
+ this.addProvider = addProvider;
+ this.hideCatagory = hideCatagory;
+
+ function setGlobalLoggingLevel(level) {
+ loggingLevel = level;
+ }
+
+ function addProvider(provider) {
+ providers.push(provider);
+ }
+
+ function hideCatagory(catagory) {
+ hiddenCatagorys.push(catagory);
+ }
+
+ this.$get = buildLoggerService;
+
+ buildLoggerService.$inject = ['$injector'];
+ function buildLoggerService($injector) {
+ var instance = createLogger('Default');
+ this.createLogger = createLogger;
+ this.log = instance.log;
+ this.debug = instance.debug;
+ this.info = instance.info;
+ this.warning = instance.warning;
+ this.error = instance.error;
+ this.DEBUG = 1;
+ this.INFO = 2;
+ this.WARNING = 3;
+ this.ERROR = 4;
+ var concreteProviders = [];
+ angular.forEach(providers, function (value) {
+ if (angular.isObject(value)) {
+ concreteProviders.push(value);
+ } else if (angular.isFunction(value)) {
+ var pro = {
+ log: value,
+ debug: log.bind(null, 1),
+ info: log.bind(null, 2),
+ warning: log.bind(null, 3),
+ error: log.bind(null, 4)
+ };
+ concreteProviders.push(pro);
+ } else if (angular.isArray(value)) {
+ var pro = $injector.invoke(value, null, {
+ Levels: {
+ DEBUG: 1,
+ INFO: 2,
+ WARNING: 3,
+ ERROR: 4
+ }
+ });
+ concreteProviders.push(pro);
+ } else if (typeof value === 'string') {
+ var pro = $injector.get(value);
+ concreteProviders.push(pro);
+ }
+ });
+
+ function createLogger(catagory) {
+ return new Logger(catagory, concreteProviders, loggingLevel, hiddenCatagorys);
+ }
+
+ return this;
+ }
+ });
+
+ function Logger(catagory, providers, loggingLevel, hiddenCatagorys) {
+ this.log = log;
+ this.debug = debug;
+ this.info = info;
+ this.warning = warning;
+ this.error = error;
+
+ function log(level, msg) {
+ if (level < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
+ return;
+
+ var args = Array.prototype.slice.call(arguments, 2);
+ angular.forEach(providers, function (provider) {
+ provider.log(level, catagory, msg, args);
+ });
+ }
+
+ function debug(msg) {
+ if (1 < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
+ return;
+
+ var args = Array.prototype.slice.call(arguments, 1);
+ angular.forEach(providers, function (provider) {
+ provider.debug(catagory, msg, args);
+ });
+ }
+
+ function info(msg) {
+ if (2 < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
+ return;
+
+ var args = Array.prototype.slice.call(arguments, 1);
+ angular.forEach(providers, function (provider) {
+ provider.info(catagory, msg, args);
+ });
+ }
+
+ function warning(msg) {
+ if (3 < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
+ return;
+
+ var args = Array.prototype.slice.call(arguments, 1);
+ angular.forEach(providers, function (provider) {
+ provider.warning(catagory, msg, args);
+ });
+ }
+
+ function error(msg, exception) {
+ if (4 < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
+ return;
+
+ var args = Array.prototype.slice.call(arguments, 2);
+ angular.forEach(providers, function (provider) {
+ provider.error(catagory, msg, exception, args);
+ });
+ }
+ }
+})(window, angular);
\ No newline at end of file
diff --git a/services/Menu.service.js b/services/Menu.service.js
new file mode 100644
index 0000000..d9a52d0
--- /dev/null
+++ b/services/Menu.service.js
@@ -0,0 +1,45 @@
+(function (window, angular) {
+ var app = angular.module("framework.services.menu", []);
+
+ app.provider('MenuService', MenuServiceProvider);
+
+ function MenuServiceProvider() {
+ var menuItems = [];
+ var defaultIcon = 'icon-Align-JustifyAll';
+ var provider = {
+ registerMenuItem: registerMenuItem,
+ setDefaultIcon: setDefaultIcon,
+ $get: getService
+ };
+
+ return provider;
+
+ function registerMenuItem(menuItem) {
+ menuItems.push(menuItem);
+ }
+
+ function setDefaultIcon(icon) {
+ defaultIcon = icon;
+ }
+
+ function getService() {
+ angular.forEach(menuItems, function (mi) {
+ mi.$html = _buildTemplate(mi);
+ });
+ var service = {
+ menuItems: menuItems,
+ defaultIcon: defaultIcon
+ };
+ return service;
+ }
+
+ function _buildTemplate(menuItem) {
+ var template = '' +
+ '' +
+ '{{item.title}}' +
+ '';
+ return template;
+ }
+
+ };
+})(window, angular);
\ No newline at end of file
diff --git a/services/Model.service.js b/services/Model.service.js
new file mode 100644
index 0000000..8ff2ddd
--- /dev/null
+++ b/services/Model.service.js
@@ -0,0 +1,300 @@
+(function (window, angular) {
+ var module = angular.module('framework.services.model', []);
+
+ module.provider('Model', modelsProvider);
+ //module.service('DataContext', dataService);
+
+ function modelsProvider() {
+ var schemas = {
+
+ };
+
+ this.registerSchema = registerSchema;
+ this.$get = buildService;
+
+ function registerSchema(name, path, schema) {
+ schemas[name] = [path, schema];
+ return this;
+ }
+
+ buildService.$inject = ['$config', '$http', '$q'];
+ function buildService($config, $http, $q) {
+ var models = {};
+
+ angular.forEach(schemas, function (value, key) {
+ var modelName = key;
+ var modelPath = value[0];
+ var modelSchema = value[1];
+
+ models[modelName] = BuildConstructor(modelPath, modelSchema);
+ });
+
+ return models;
+
+ function Entity(apiEndpoint, schema, values) {
+ this.$$apiLocation = apiEndpoint;
+ this.$$schema = schema;
+ this.$$originalValues = {};
+ this.$$changedValues = {};
+ this.$$foreignModels = {};
+
+ this.$getChanges = getChanges;
+ this.$validate = validateEntity;
+ this.$save = saveEntity;
+ this.$update = updateEntity;
+ this.$patch = patchEntity;
+ this.$delete = deleteEntity;
+ this.$commit = commitEntity;
+ this.$rollback = rollbackEntity;
+
+ angular.forEach(schema, function (value, key) {
+ var fieldName = key,
+ fieldDef = value;
+
+ if (values[fieldName] !== undefined) {
+ if (typeof fieldDef.type === 'string' && values[fieldName] !== null) {
+ this.$$originalValues[fieldName] = new models[fieldDef.type](values[fieldName]);
+ this.$$foreignModels[fieldName] = fieldDef.type;
+ } else {
+ this.$$originalValues[fieldName] = values[fieldName];
+ }
+ } else {
+ this.$$originalValues[fieldName] = fieldDef.defaultValue;
+ }
+
+ Object.defineProperty(this, fieldName, {
+ configurable: false,
+ enumerable: true,
+ get: fieldDef.calculated || function () {
+ return this.$$changedValues.hasOwnProperty(fieldName) ?
+ this.$$changedValues[fieldName] : this.$$originalValues[fieldName];
+ },
+ set: function (value) {
+ if (angular.isUndefined(fieldDef.editable) || fieldDef.editable) {
+
+ this.$$changedValues[fieldName] = value; //CHECK: Cast to correct type??
+ }
+ }
+ });
+ }, this);
+
+ function getChanges() {
+ return this.$$changedValues;
+ }
+ function validateEntity() {
+ } //TODO
+ function saveEntity() {
+ return $http.post($config.API_ENDPOINT + this.$$apiLocation, this);
+ }
+ function updateEntity() {
+ var config = {
+ params: {
+ id: this.Id
+ }
+ };
+ return $http.put($config.API_ENDPOINT + this.$$apiLocation, this, config);
+ }
+ function patchEntity(skipSubObjects) {
+ var self = this;
+ var delta = angular.extend({ Id: this.Id }, this.$getChanges());
+ var config = {
+ params: {
+ id: this.Id
+ }
+ };
+ var request = $http.patch($config.API_ENDPOINT + this.$$apiLocation, delta, config);
+ request.success(function () {
+ self.$commit(true);
+ });
+ return request;
+ }
+ function deleteEntity() {
+ var config = {
+ params: {
+ id: this.Id
+ }
+ };
+ return $http.delete($config.API_ENDPOINT + this.$$apiLocation, config);
+ }
+ function commitEntity(skipSubObjects) {
+ angular.forEach(this.$$changedValues, function (value, key) {
+ var fieldName = key,
+ fieldValue = value;
+ this.$$originalValues[fieldName] = fieldValue
+ }, this);
+ this.$$changedValues = {};
+
+ if (skipSubObjects) return;
+ angular.forEach(this.$$foreignModels, function (value, key) {
+ this[key].$commit();
+ }, this);
+ }
+ function rollbackEntity(skipSubObjects) {
+ this.$$changedValues = {};
+
+ if (skipSubObjects) return;
+ angular.forEach(this.$$foreignModels, function (value, key) {
+ this[key].$rollback();
+ }, this);
+ }
+ }
+
+ function Query(apiEndpoint, ctor) {
+ var includes = [];
+ var selects = [];
+ var filters = [];
+ var order = [];
+ var modelCtor = ctor;
+ this.include = include;
+ this.select = select;
+ this.filter = filter;
+ this.where = filter;
+ this.parseAs = parseAs;
+ this.execute = execute;
+ this.orderBy = orderBy;
+
+ function parseAs(parser) {
+ modelCtor = parser;
+ }
+
+ function include() {
+ if (arguments.length === 2) {
+ if (angular.isObject(arguments[1])) {
+ //TODO: Process subquery
+ } else if (angular.isFunction(arguments[1])) {
+ //TODO: Process subquery
+ }
+ } else {
+ [].push.apply(includes, arguments);
+ }
+ return this;
+ }
+
+ function orderBy(prop) {
+ this.order.push(prop);
+ //todo add dir and aliases
+ }
+
+ function select() {
+ [].push.apply(selects, arguments);
+ modelCtor = null;
+ return this;
+ }
+
+ function filter(key, op, value) {
+ if (arguments.length === 1) {
+ filters.push(key);
+ } else if (arguments.length === 3) {
+ switch (op) {
+ case '&&':
+ op = 'and';
+ break;
+ case '||':
+ op = 'or';
+ break;
+ case '=':
+ case '==':
+ case '===':
+ op = 'eq';
+ break;
+ case '!=':
+ case '!==':
+ case '!===':
+ op = 'ne';
+ break;
+ case '>':
+ op = 'gt';
+ break;
+ case '>=':
+ op = 'ge';
+ break;
+ case '<':
+ op = 'lt';
+ break;
+ case '<=':
+ op = 'le';
+ break;
+ default:
+ break;
+ }
+ filters.push(key + ' ' + op + ' ' + value);
+ }
+ return this;
+ }
+
+ function execute() {
+ var config = {};
+ config.params = _buildParams();
+ apiCall = $config.API_ENDPOINT + apiEndpoint;
+ return $http.get(apiCall, config).then(function (response) {
+ if (!modelCtor) {
+ return response.data;
+ } else {
+ if (angular.isArray(response.data)) {
+ var results = [];
+ angular.forEach(response.data, function (d) {
+ results.push(new modelCtor(d));
+ });
+ return results;
+ } else {
+ return new modelCtor(response.data);
+ }
+ }
+ });
+ }
+
+ function _buildParams() {
+ var params = {};
+ if (includes.length) {
+ params.$expand = includes.join(',');
+ }
+ if (filters.length) {
+ params.$filter = filters.join(' and ');
+ }
+ if (selects.length) {
+ params.$select = selects.join(',');
+ }
+ return params;
+ }
+ }
+
+ function ModelCache() {
+
+ }
+
+ function BuildConstructor(apiEndpoint, schema) {
+ function ModelCtor(values) {
+ var values = values || {};
+
+ this.base = Entity;
+ this.base(apiEndpoint, schema, values);
+ }
+
+ ModelCtor.prototype = new Entity;
+ ModelCtor.$schema = schema;
+ ModelCtor.query = query;
+ ModelCtor.getById = getById;
+
+ var passon = ModelCtor;
+
+ function query() {
+ return new Query(apiEndpoint, passon);
+ }
+
+ function getById(id) {
+ return new Query(apiEndpoint)
+ .filter('Id', 'eq', id);
+ }
+
+ return ModelCtor;
+ }
+ }
+ }
+
+ dataService.$inject = ['$config', '$http', '$q', 'Models'];
+ function dataService($config, $http, $q, Models) {
+ var entityIdMaps = {};
+ var entityArrays = {};
+
+ }
+})(window, angular);
\ No newline at end of file
diff --git a/services/Storage.service.js b/services/Storage.service.js
new file mode 100644
index 0000000..70c9580
--- /dev/null
+++ b/services/Storage.service.js
@@ -0,0 +1,73 @@
+(function () {
+ var module = angular.module('framework.services.storage', [])
+
+ module.factory('$localStorage', _storageFactory('localStorage'));
+ module.factory('$sessionStorage', _storageFactory('sessionStorage'));
+
+ function _storageFactory(storageType) {
+ return ['$rootScope', '$window', '$log',
+ function ($rootScope, $window, $log) {
+ // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
+ var webStorage = $window[storageType] || ($log.warn('This browser does not support Web Storage!'), {}),
+ $storage = {
+ $default: function (items) {
+ for (var k in items) {
+ angular.isDefined($storage[k]) || ($storage[k] = items[k]);
+ }
+
+ return $storage;
+ },
+ $reset: function (items) {
+ for (var k in $storage) {
+ '$' === k[0] || delete $storage[k];
+ }
+
+ return $storage.$default(items);
+ }
+ },
+ _last$storage,
+ _debounce;
+
+ for (var i = 0, k; i < webStorage.length; i++) {
+ // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty)
+ (k = webStorage.key(i)) && 'ngStorage-' === k.slice(0, 10) && ($storage[k.slice(10)] = angular.fromJson(webStorage.getItem(k)));
+ }
+
+ _last$storage = angular.copy($storage);
+
+ $rootScope.$watch(function () {
+ _debounce || (_debounce = setTimeout(function () {
+ _debounce = null;
+
+ if (!angular.equals($storage, _last$storage)) {
+ angular.forEach($storage, function (v, k) {
+ angular.isDefined(v) && '$' !== k[0] && webStorage.setItem('ngStorage-' + k, angular.toJson(v));
+
+ delete _last$storage[k];
+ });
+
+ for (var k in _last$storage) {
+ webStorage.removeItem('ngStorage-' + k);
+ }
+
+ _last$storage = angular.copy($storage);
+ }
+ }, 100));
+ });
+
+ // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
+ 'localStorage' === storageType && $window.addEventListener && $window.addEventListener('storage', function (event) {
+ if ('ngStorage-' === event.key.slice(0, 10)) {
+ event.newValue ? $storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete $storage[event.key.slice(10)];
+
+ _last$storage = angular.copy($storage);
+
+ $rootScope.$apply();
+ }
+ });
+
+ return $storage;
+ }
+ ];
+ }
+})()
\ No newline at end of file
diff --git a/tables/Table.controller.js b/tables/Table.controller.js
new file mode 100644
index 0000000..51cdb6e
--- /dev/null
+++ b/tables/Table.controller.js
@@ -0,0 +1,157 @@
+(function (angular) {
+ var module = angular.module('framework.table', []);
+
+ var table_defaults = {
+ table: 'table table-bordered'
+ };
+
+ /*
+ * Defines Table defaults and settings
+ */
+ module.constant("$table_config", table_defaults);
+
+ /*
+ * Injectable Base Class for Inhertence
+ * Functions:
+ * Lays out rows / columns
+ * Sets heading title
+ * Parses cell values
+ * Column Sizing
+ */
+ module.service('BasicTableControllerBase', function () {
+ return BasicTableControllerBase;
+ });
+
+ /*
+ * Injectable Base Class for Inhertence
+ * Functions:
+ * As Basic Table Plus:
+ * Sortable Columns
+ */
+ module.service('SortableTableControllerBase', function () {
+ return SortableTableControllerBase;
+ });
+
+ /*
+ * Default Implementations
+ */
+ module.controller('BasicTableController', basicTableController);
+ module.controller('SortableTableController', sortableTableController);
+
+ //#region BaseClasses
+
+ function BasicTableControllerBase($scope, columns, data) {
+ var $injector = angular.element('*[ng-app]').injector();
+ var $parse = $injector.get('$parse');
+
+ $scope.rows = data.data;
+ $scope.columns = columns;
+ $scope.getValue = getValue;
+
+ var _totalWidths = columns.reduce(function (prev, current) {
+ return prev += (current.width || '*').length;
+ }, 0);
+
+ angular.forEach(columns, function (value) {
+ value.$$parser = angular.isFunction(value.mapping) ? value.mapping : $parse(value.mapping || value.name);
+ value.$$width = 100.00 / _totalWidths * (value.width || '*').length + '%';
+ });
+
+ function getValue(row, col) {
+ return col.$$parser(row);
+ }
+ }
+
+ function SortableTableControllerBase($scope, columns, data) {
+ this.base = BasicTableControllerBase;
+ this.base($scope, columns, data);
+
+ $scope.currentSort = {
+ value: '',
+ column: null,
+ reverse: false
+ };
+ $scope.currentSearch = searchFunction;
+ $scope.sortBy = sort;
+ $scope.searchTerm = null;
+
+ function sort(column, $event) {
+ var newSort = {
+ value: '',
+ column: null
+ };
+
+ if (angular.isObject(column)) {
+ newSort.column = column;
+ newSort.value = column.$$parser;
+ } else if (typeof column === "string") {
+ angular.forEach($scope.columns, function (colDef) {
+ if ((colDef.mapping || colDef.name) === column) {
+ newSort.column = colDef;
+ }
+ });
+ newSort.value = column;
+ } else if (angular.isFunction(column)) {
+ newSort.value = column;
+ newSort.column = null;
+ }
+
+ if ($scope.currentSort.value === newSort.value) {
+ $scope.currentSort.reverse = !$scope.currentSort.reverse;
+ } else {
+ $scope.currentSort.reverse = false;
+ }
+
+ $scope.currentSort.value = newSort.value;
+ $scope.currentSort.column = newSort.column;
+ };
+
+ function searchFunction(row) {
+ if (!$scope.searchTerm) {
+ return true;
+ }
+ var found = false;
+ for (var i = 0; i < columns.length; i++) {
+ var column = $scope.columns[i];
+ if (angular.isUndefined(column.searchable) || column.searchable) {
+ var value = column.$$parser(row);
+ if (angular.isFunction(column.comparator)) {
+ found = column.comparator(value, $scope.searchTerm);
+ } else if (typeof value === 'string') {
+ found = value.toLowerCase().indexOf($scope.searchTerm.toLowerCase()) >= 0;
+ }
+ }
+ if (found) {
+ break;
+ }
+ }
+ return found;
+ };
+ }
+ SortableTableControllerBase.prototype = BasicTableControllerBase;
+
+ //#endregion
+
+ //#region Default Implementations
+
+ basicTableController.$inject = ['$scope', 'classes', 'columns', 'data'];
+ function basicTableController($scope, classes, columns, data) {
+ this.base = BasicTableControllerBase;
+ this.base($scope, columns, data);
+
+ $scope.classes = angular.extend({}, table_defaults, classes);
+ }
+ basicTableController.prototype = BasicTableControllerBase;
+
+
+ sortableTableController.$inject = ['$scope', 'classes', 'columns', 'data'];
+ function sortableTableController($scope, classes, columns, data) {
+ this.base = SortableTableControllerBase;
+ this.base($scope, columns, data);
+
+ $scope.classes = angular.extend({}, table_defaults, classes);
+ }
+ sortableTableController.prototype = SortableTableControllerBase;
+
+ //#endregion
+})(angular);
\ No newline at end of file
diff --git a/tables/basic-table.layout.html b/tables/basic-table.layout.html
new file mode 100644
index 0000000..4877fba
--- /dev/null
+++ b/tables/basic-table.layout.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ {{ col.name }}
+
+
+
+
+
+
+ {{ ::getValue(row, col) }}
+
+
+
+
+
diff --git a/tables/sortable-table.layout.html b/tables/sortable-table.layout.html
new file mode 100644
index 0000000..d67ec0a
--- /dev/null
+++ b/tables/sortable-table.layout.html
@@ -0,0 +1,20 @@
+