From 6e6aa9b005ee7636f1e7f02fc4981c1a9791d483 Mon Sep 17 00:00:00 2001 From: Tarpit Grover Date: Mon, 23 Feb 2015 17:06:12 +0000 Subject: [PATCH] Basic Setup --- .gitignore | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/directives/BindHtmlCompile.directive.js | 21 +++++++++++++++++++++ common_components/directives/Card.directive.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ common_components/directives/FieldFor.directive.js | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/directives/Score.directive.js | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/directives/TableFor.directive.js | 592 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/filters/utility.filter.js | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/layouts/breadcrumb/breadcrumb.controller.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ common_components/layouts/breadcrumb/breadcrumb.partial.html | 8 ++++++++ common_components/layouts/documentsview/documents-view.controller.js | 12 ++++++++++++ common_components/layouts/documentsview/documents-view.partial.html | 36 ++++++++++++++++++++++++++++++++++++ common_components/layouts/module/module-base.layout.html | 11 +++++++++++ common_components/layouts/module/module-cards.controller.js | 37 +++++++++++++++++++++++++++++++++++++ common_components/layouts/module/module-cards.partial.html | 26 ++++++++++++++++++++++++++ common_components/layouts/module/module-item.controller.js | 14 ++++++++++++++ common_components/layouts/module/module-item.layout.html | 21 +++++++++++++++++++++ common_components/layouts/module/module-table.controller.js | 22 ++++++++++++++++++++++ common_components/layouts/module/module-table.partial.html | 17 +++++++++++++++++ common_components/layouts/structure/page.controller.js | 12 ++++++++++++ common_components/layouts/structure/page.layout.html | 16 ++++++++++++++++ common_components/layouts/table-page/table-page.layout.html | 12 ++++++++++++ common_components/services/ConsoleLoggingProvider.service.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/services/Logger.service.js | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/services/Menu.service.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ common_components/services/Model.service.js | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/services/Storage.service.js | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/tables/Table.controller.js | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common_components/tables/basic-table.layout.html | 18 ++++++++++++++++++ common_components/tables/sortable-table.layout.html | 20 ++++++++++++++++++++ 29 files changed, 2333 insertions(+), 0 deletions(-) create mode 100644 .gitignore create mode 100644 common_components/directives/BindHtmlCompile.directive.js create mode 100644 common_components/directives/Card.directive.js create mode 100644 common_components/directives/FieldFor.directive.js create mode 100644 common_components/directives/Score.directive.js create mode 100644 common_components/directives/TableFor.directive.js create mode 100644 common_components/filters/utility.filter.js create mode 100644 common_components/layouts/breadcrumb/breadcrumb.controller.js create mode 100644 common_components/layouts/breadcrumb/breadcrumb.partial.html create mode 100644 common_components/layouts/documentsview/documents-view.controller.js create mode 100644 common_components/layouts/documentsview/documents-view.partial.html create mode 100644 common_components/layouts/module/module-base.layout.html create mode 100644 common_components/layouts/module/module-cards.controller.js create mode 100644 common_components/layouts/module/module-cards.partial.html create mode 100644 common_components/layouts/module/module-item.controller.js create mode 100644 common_components/layouts/module/module-item.layout.html create mode 100644 common_components/layouts/module/module-table.controller.js create mode 100644 common_components/layouts/module/module-table.partial.html create mode 100644 common_components/layouts/structure/page.controller.js create mode 100644 common_components/layouts/structure/page.layout.html create mode 100644 common_components/layouts/table-page/table-page.layout.html create mode 100644 common_components/services/ConsoleLoggingProvider.service.js create mode 100644 common_components/services/Logger.service.js create mode 100644 common_components/services/Menu.service.js create mode 100644 common_components/services/Model.service.js create mode 100644 common_components/services/Storage.service.js create mode 100644 common_components/tables/Table.controller.js create mode 100644 common_components/tables/basic-table.layout.html create mode 100644 common_components/tables/sortable-table.layout.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..acdd9c1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,180 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +x64/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Roslyn cache directories +*.ide + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +## TODO: Comment the next line if you want to checkin your web deploy settings but do note that will include unencrypted passwords +*.pubxml + +# NuGet Packages Directory +packages/* +## TODO: If the tool you use requires repositories.config uncomment the next line +#!packages/repositories.config + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) +!packages/build/ + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +.DS_Store +.codekit-cache + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ \ No newline at end of file diff --git a/common_components/directives/BindHtmlCompile.directive.js b/common_components/directives/BindHtmlCompile.directive.js new file mode 100644 index 0000000..195de96 --- /dev/null +++ b/common_components/directives/BindHtmlCompile.directive.js @@ -0,0 +1,21 @@ +(function (angular) { + 'use strict'; + + var module = angular.module('framework.directives.utils', []); + + module.directive('bindHtmlCompile', ['$compile', bindHtmlCompile]); + + function bindHtmlCompile($compile) { + return { + restrict: 'A', + link: function (scope, element, attrs) { + scope.$watch(function () { + return scope.$eval(attrs.bindHtmlCompile); + }, function (value) { + element.html(value); + $compile(element.contents())(scope); + }); + } + }; + } +}(angular)); \ No newline at end of file diff --git a/common_components/directives/Card.directive.js b/common_components/directives/Card.directive.js new file mode 100644 index 0000000..6cd9578 --- /dev/null +++ b/common_components/directives/Card.directive.js @@ -0,0 +1,48 @@ +(function(angular){ + var module = angular.module('framework.directives.UI',[]); + + module.directive('card', cardDirective); + cardDirective.$inject=['$state', '$interval', '$parse']; + function cardDirective($state, $interval, $parse) { + return { + restrict: 'E', + transclude: true, + replace: true, + scope: true, + template: function(element, attrs) { + var template=''; + template += '
'; + if(attrs.showTitle!="false") { + template += '{{ ::accessors.Title(item) }}'; + } + if(attrs.showSubtitle!="false") { + template += '{{ ::accessors.SubTitle(item) }}'; + } + template += '
'; + return template; + }, + link: function(scope, elem, attrs, ctrls, transcludeFn) { + if(attrs.item) { + var getter = $parse(attrs.item); + scope.item = getter(scope); + } + scope.viewItem = viewItem; + if(scope.accessors.Score) { + var percentScore = (2 * Math.PI*50)/100*scope.accessors.Score(scope.item); + var percentColor = (percentScore < 90)? 'red': (percentScore >= 90 && percentScore <180)? 'orange' :(percentScore >= 180 && percentScore <270)? 'rgb(25, 195, 45)' : 'rgb(25, 195, 45);'; + elem.find("circle") + .attr("stroke", percentColor) + .attr("stroke-dasharray",percentScore+",10000"); + } + + function viewItem(item) { + $state.go(scope.rNavTo, { Id: item.Id }); + } + } + } + } +})(angular); \ No newline at end of file diff --git a/common_components/directives/FieldFor.directive.js b/common_components/directives/FieldFor.directive.js new file mode 100644 index 0000000..a352549 --- /dev/null +++ b/common_components/directives/FieldFor.directive.js @@ -0,0 +1,218 @@ +(function (angular) { + var module = angular.module('framework.directives.UI'); + + module.directive('fieldFor', fieldForDirective); + module.directive('formFor', formForDirective); + module.filter('propSearch', propertySearchFilter); + + formForDirective.$inject = ['Model']; + function formForDirective(Model) { + return { + restrict: 'A', + scope: true, + transclude: true, + template: function (element, attrs) { + return '
'; + }, + link: function (scope, element, attrs, ctrls, transcludeFn) { + scope.formFor = (scope.formFor || {}); + scope.formFor.target = scope.$eval(attrs.formFor); + scope.formFor.schema = (attrs.schema) ? Model[attrs.schema].$schema : scope.target.$$schema; + scope.formFor.mode = 'EDIT'; + + scope.$watch(function () { + return scope.target; + }, function (newvalue) { + + }); + + if (attrs.mode) { + scope.formFor.mode = attrs.mode; + attrs.$observe('mode', function (newvalue) { + scope.formFor.mode = newvalue; + }); + } + + transcludeFn(scope, function (clone, scope) { + element.children('.transclude-target').append(clone); + }); + } + } + }; + + fieldForDirective.$inject = ['Model']; + function fieldForDirective(Model) { + var defaults = { + inputType: 'text', + required: false, + selectTheme: 'selectize', + selectSelectedTemplate: '{{$select.selected.Name}}', + selectOptionTemplate: '
{{option.Description}}' + }; + return { + restrict: 'A', + scope: true, + template: function (element, attrs) { + var formParent = element.closest('[form-for]'), + subformParent = element.closest('[sub-form-for]'), + schemaName = ((subformParent.length) ? subformParent : formParent).attr('schema'), + ctor = Model[schemaName], + schema = ctor && ctor.$schema, + fieldDef = schema && schema[attrs.fieldFor], + subForm = subformParent.attr('sub-form-for') || ''; + + if (!fieldDef) { + return; + } + + var inputConfig = angular.extend({ display: attrs.fieldFor, inputType: 'text' }, defaults, fieldDef, fieldDef.input, attrs); + + //need to fetch these. + var labelText = attrs.display || attrs.fieldFor, + fieldName = attrs.fieldFor, + selectFrom = attrs.selectFrom; + + var template = '' + + '
' + + '' + + '
' + + '

' + + '
' + + '
'; + + if (selectFrom) { + template += '' + + '' + + '' + inputConfig.selectSelectedTemplate + '' + + '' + + inputConfig.selectOptionTemplate + + ''+ + ''; + } else if (inputConfig.type === moment) { + template += '' + + '
' + + buildElement('input', { + class: 'form-control', + type: 'text', + name: subForm + fieldName + 'Input', + ngModel: 'formFor.target.' + ((subForm) ? (subForm + '.') : '') + fieldName, + ngRequired: inputConfig.required, + datepickerPopup: 'dd/MM/yyyy', + isOpen: 'opened', + placeholder: 'DD/MM/YYYY', + ngDisabled: angular.isDefined(inputConfig.editable) && !inputConfig.editable + }) + + '' + + '' + + '' + + '
'; + } else { + if (inputConfig.append || inputConfig.prepend) { + template += '
'; + } + if (inputConfig.prepend) { + template += '' + + '' + + '' + } + template += buildElement('input', { + class: 'form-control', + type: inputConfig.inputType, + name: subForm + fieldName + 'Input', + ngModel: 'formFor.target.' + ((subForm) ? (subForm + '.') : '') + fieldName, + ngRequired: inputConfig.required, + placeholder: inputConfig.placeholder + }); + if (inputConfig.append) { + template += '' + + '' + + '' + } + if (inputConfig.append || inputConfig.prepend) { + template += '
'; + } + } + + template += '' + + '
' + + '
' + inputConfig.display + ' is required.
' + + '
' + inputConfig.display + ' is incorrectly formatted.
' + + '
' + inputConfig.display + ' is not a valid e-mail.
' + + '
' + + '
'; + template += '
'; + return template; + }, + link: function (scope, element, attrs) { + scope.opened = false; + + scope.openCalendar = function (event) { + event.preventDefault(); + event.stopPropagation(); + scope.opened = true; + } + } + }; + }; + + function propertySearchFilter() { + return function (items, props) { + var out = []; + + if (angular.isArray(items)) { + items.forEach(function (item) { + var itemMatches = false; + + var keys = Object.keys(props); + for (var i = 0; i < keys.length; i++) { + var prop = keys[i]; + var text = props[prop].toLowerCase(); + if (item[prop].toString().toLowerCase().indexOf(text) !== -1) { + itemMatches = true; + break; + } + } + + if (itemMatches) { + out.push(item); + } + }); + } else { + // Let the output be the input untouched + out = items; + } + + return out; + }; + }; + + function buildElement(elementType, attrs) { + var result = '<' + elementType + ' '; + + angular.forEach(attrs, function (value, key) { + if (angular.isDefined(value) && value !== null) + result += normalise(key) + '="' + value + '"'; + }); + + if (elementType === 'input') { + result += ' />'; + } else { + result += ' >'; + } + return result; + } + + function normalise(input) { + var result = ''; + for (var i = 0; i < input.length; i++) { + var char = input[i]; + if (char == char.toUpperCase()) { + result += '-' + char.toLowerCase(); + } else { + result += char; + } + } + return result; + } + +}(angular)); \ No newline at end of file diff --git a/common_components/directives/Score.directive.js b/common_components/directives/Score.directive.js new file mode 100644 index 0000000..25e2bf6 --- /dev/null +++ b/common_components/directives/Score.directive.js @@ -0,0 +1,53 @@ +(function(angular){ + var module = angular.module('framework.directives.UI'); + + module.directive('score', scoreDirective); + + scoreDirective.$inject=['$state', '$interval', '$parse']; + + function scoreDirective($state, $interval, $parse) { + return { + restrict: 'E', + transclude: true, + replace: true, + scope: true, + template: function(element, attrs) { + var template=''; + template += '
'; + } + else { + template += ' style="width:100px;height:100px;border-radius:50px;">'; + } + if(attrs.showScore) { + template += '
'+attrs.showScore+'
'; + } + template += '' + + '' + + '' + + ''; + template += '
'; + return template; + }, + link: function(scope, elem, attrs, ctrls, transcludeFn) { + if(!attrs.value) + return; + + var strokeWidth = (attrs.strokeWidth)? attrs.strokeWidth : 5; + var percentScore = (2 * Math.PI*50)/100 * attrs.value; + var percentColor = (percentScore < 90)? 'red': (percentScore >= 90 && percentScore <180)? 'orange' :(percentScore >= 180 && percentScore <270)? 'rgb(25, 195, 45)' : 'rgb(25, 195, 45)'; + + elem.find("circle.score-back-circle") + .attr("stroke", "#eee") + .attr("stroke-width", strokeWidth); + + elem.find("circle.score-front-circle") + .attr("stroke", percentColor) + .attr("stroke-width", strokeWidth) + .attr("stroke-dasharray", percentScore+",10000"); + } + } + } +})(angular); \ No newline at end of file diff --git a/common_components/directives/TableFor.directive.js b/common_components/directives/TableFor.directive.js new file mode 100644 index 0000000..ca224e8 --- /dev/null +++ b/common_components/directives/TableFor.directive.js @@ -0,0 +1,592 @@ +(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: '' + + '' + + '', + controller: function ($scope, columns) { + $scope.columns = columns; + $scope.moveUp = function (column) { + var index = $scope.columns.indexOf(column); + move(index, index - 1); + } + $scope.moveDown = function (column) { + var index = $scope.columns.indexOf(column); + move(index, index + 1); + } + + function move(old_index, new_index) { + $scope.columns.splice(new_index, 0, $scope.columns.splice(old_index, 1)[0]); + } + }, + size: 'sm', + resolve: { + columns: function () { + return scope.$columns + } + } + }); + } + } + + function compileFn(element, attrs) { + return linkFn; + } + + return { + scope: true, + transclude: true, + template: function (element, attrs) { + return '
' + }, + compile: function (element, attrs) { + return linkFn; + } + }; + } + + function tableTitlebarDirective() { + return { + restrict: 'E', + scope: true, + replace: true, + template: function (element, attrs) { + var template = '' + + ''; + if (attrs.title) { + template += '

' + attrs.title + '

'; + } + if (attrs.showCount) { + template += '

({{$visible.length}} / {{ $data.length }})

'; + } + if (attrs.columnsOptions) { + template += '
' + + 'Reset' + + 'Arrange' + + '
'; + } + template += '
' + + '' + + '
'; + if (attrs.search) { + template += ''; + } + template += '' + return template; + } + } + } + + function tableHeadingDirective() { + var defaults = { + } + return { + restrict: 'E', + scope: true, + replace: true, + template: function (element, attrs) { + var tmpl = '' + + '' + + '' + + '
' + + + '' + + '' + + '
{{ ::$column.title }}' + + '' + + '
' + //add filter and search menu + '' + + '' + + 'Actions' + + '' + + ''; + tmpl += ''; + return tmpl; + } + }; + } + + tableDataRowsDirective.$inject = ['$parse', 'Model']; + function tableDataRowsDirective($parse, Model) { + return { + restrict: 'E', + scope: true, + replace: true, + template: function (element, attrs) { + var tmp = '' + + '' + + '' + + '' + + '
' + + '' + + '{{ title }}' + + '' + + '' + + '' + + '
' + + '' + + '' + + '{{$column.accessor($row)}}' + + '' + + '' + + '' + + '' + + ''; + return tmp; + }, + compile: function (element, attrs) { + var selectionElement = _findSelectionColumn(element); + var actionsElement = _findActionColumn(element); + + return link; + + function link(scope, element, attrs) { + scope.$table.actionColumn = actionsElement; + scope.$table.selectionColumn = selectionElement; + scope.$table.getCellValue = getCellValue; + + function getCellValue(row, col) { + console.log('GetCellValue is depreciated for performance, please use "$column.accessor($row)" instead, functions are now precompilied;'); + if (typeof col.binding === 'string') + return $parse(col.binding)(row); + else if (angular.isFunction(col.binding)) + return col.binding(row, col); + else + return ''; + } + } + } + } + + function _findActionColumn(element) { + var actionsElement = jQuery(element.context).find('table-actions'); + if (actionsElement.length) { + return { + html: actionsElement.html() + }; + } + return false; + } + + function _findSelectionColumn(element) { + var selectionElement = jQuery(element.context).find('table-selection'); + if (selectionElement.length) { + return { + html: selectionElement.html() + }; + } + return false; + } + } + + function tableFooterDirective() { + return { + restrict: 'E', + scope: true, + replace: true, + template: function (element, attrs) { + var template = '' + + ''; + if (attrs.title) { + template += '

' + attrs.title + '

'; + } + if (attrs.showCount) { + template += '(showing {{$paging.maxPerPage}} from {{ $paging.totalItems }})'; + } + if (attrs.columnsOptions) { + template += '
' + + 'Reset' + + 'Arrange' + + '
'; + } + template += '
' + + '' + + '
'; + if (attrs.search) { + template += ''; + } + template += '' + return template; + } + } + } + + softFilter.$inject = ['$filter']; + function softFilter($filter) { + return function (array, filteringConfig) { + var filterFn = $filter('filter'); + var filtered = filterFn(array, filteringConfig); + for (var i = 0; i < array.length; i++) { + var target = array[i]; + if (filtered.indexOf(target) > -1) { + target.$filtered = true; + } else { + target.$filtered = false; + } + } + return array; + } + } + + function pageGroupFilter() { + return function (groups, pagingConfig) { + var newGroups = {}; + var skip = (pagingConfig.currentPage - 1) * pagingConfig.maxPerPage; + var take = pagingConfig.maxPerPage; + + if (angular.isArray(groups)) { + newGroups.undefined = groups.slice(skip, skip + take); + } else { + var groupNames = []; + for (var key in groups) { + if (groups.hasOwnProperty(key) && key[0] != '$') { + groupNames.push(key); + } + } + groupNames.sort(); + + angular.forEach(groupNames, function (title) { + var values = groups[title]; + if (take <= 0) { + return; + } else if (skip > values.length) { + skip -= values.length; + return; + } else if (skip > 0) { + values.splice(0, skip); + skip = 0; + } + + if (take >= values.length) { + newGroups[title] = values; + take -= values.length; + } else { + newGroups[title] = values.slice(0, take); + take = 0; + } + }); + } + return newGroups; + }; + } + + function pagingFilter() { + return function (array, pagingConfig) { + if (array.length > pagingConfig.minimum) { + + } else { + return array; + } + } + } + + function publishFilter() { + return function (array, target) { + if (angular.isArray(target)) { + target.length = 0; + [].push.apply(target, array); + } + return array; + } + } +})(angular); \ No newline at end of file diff --git a/common_components/filters/utility.filter.js b/common_components/filters/utility.filter.js new file mode 100644 index 0000000..649ec75 --- /dev/null +++ b/common_components/filters/utility.filter.js @@ -0,0 +1,110 @@ +(function(angular){ + var module = angular.module('framework.filters.utility',[]); + + module.filter('RemoveSpaces', removeSpaces); + module.filter('Truncate', truncate); + module.filter('AbbrValue', abbrValue); + module.filter('Countdown', countdown); + + + function removeSpaces() { + return function (text) { + return text.replace(/\s/g, ''); + } + } + + function truncate () { + return function (text, length, end) { + if (isNaN(length)) + length = 10; + if (end === undefined) + end = "..."; + if (text.length <= length || text.length - end.length <= length) + { return text; } + else + { return String(text).substring(0, length - end.length) + end; } + } + } + + function abbrValue() { + return function (val) { + + return Math.abs(Number(val)) >= 1.0e+9 + + ? Math.round(Math.abs((Number(val)) / 1.0e+9)*10)/10 + "b" + // Six Zeroes for Millions + : Math.abs(Number(val)) >= 1.0e+6 + + ? Math.round(Math.abs((Number(val)) / 1.0e+6)*10)/10 + "m" + // Three Zeroes for Thousands + : Math.abs(Number(val)) >= 1.0e+3 + + ? Math.round(Math.abs((Number(val)) / 1.0e+3)*10)/10 + "k" + + : Math.abs(Number(val)); + } + } + + function countdown() { + return function (input) { + var substitute = function (stringOrFunction, number, strings) { + var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, dateDifference) : stringOrFunction; + var value = (strings.numbers && strings.numbers[number]) || number; + return string.replace(/%d/i, value); + }, + nowTime = (new Date()).getTime(), + date = (new Date(input)).getTime(), + allowFuture = true, + strings = { + prefixAgo: null, + prefixFromNow: null, + suffixAgo: "ago", + suffixFromNow: "",//"from now" + seconds: "less than a minute", + minute: "a minute", + minutes: "%d minutes", + hour: "an hour", + hours: "%d hours", + day: "a day", + days: "%d days", + month: "a month", + months: "%d months", + year: "a year", + years: "%d years" + }, + dateDifference = nowTime - date, + words, + seconds = Math.abs(dateDifference) / 1000, + minutes = seconds / 60, + hours = minutes / 60, + days = hours / 24, + years = days / 365, + separator = strings.wordSeparator === undefined ? " " : strings.wordSeparator, + prefix = strings.prefixAgo, + suffix = strings.suffixAgo; + + if (allowFuture) { + if (dateDifference < 0) { + prefix = strings.prefixFromNow; + suffix = strings.suffixFromNow; + } + } + + words = seconds < 45 && substitute(strings.seconds, Math.round(seconds), strings) || + seconds < 90 && substitute(strings.minute, 1, strings) || + minutes < 45 && substitute(strings.minutes, Math.round(minutes), strings) || + minutes < 90 && substitute(strings.hour, 1, strings) || + hours < 24 && substitute(strings.hours, Math.round(hours), strings) || + hours < 42 && substitute(strings.day, 1, strings) || + days < 30 && substitute(strings.days, Math.round(days), strings) || + days < 45 && substitute(strings.month, 1, strings) || + days < 365 && substitute(strings.months, Math.round(days / 30), strings) || + years < 1.5 && substitute(strings.year, 1, strings) || + substitute(strings.years, Math.round(years), strings); + + return $.trim([prefix, words, suffix].join(separator)); + } + } + + +})(angular); \ No newline at end of file diff --git a/common_components/layouts/breadcrumb/breadcrumb.controller.js b/common_components/layouts/breadcrumb/breadcrumb.controller.js new file mode 100644 index 0000000..2c57daf --- /dev/null +++ b/common_components/layouts/breadcrumb/breadcrumb.controller.js @@ -0,0 +1,48 @@ +(function (window, angular) { + var app = angular.module("framework.UI.breadcrumb", []); + + app.controller('BarController', barController); + + barController.$inject = ['$scope', '$state', '$modal', '$rootScope']; + function barController($scope, $state, $modal, $rootScope) { + $scope.breadcrumbs = []; + $scope.helpAvailable = null; + $scope.showHelp = showHelp; + + function showHelp() { + if ($scope.helpAvailable) + $modal.open({ + templateUrl: $scope.helpAvailable, + }); + } + + $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) { + _parseStateName(toState.name); + $scope.helpAvailable = toState.hasHelp; + }); + + $scope.helpAvailable = $state.current.hasHelp; + _parseStateName($state.current.name); + + function _parseStateName(stateName) { + var parts = stateName.split('.'); + var breadcrumbs = []; + for (var i = 0; i < parts.length; i++) { + var p = parts[i]; + if (p === "root") + continue; + var full = parts.slice(0, i + 1).join('.'); + breadcrumbs.push({ + name: parts[i], + $html: _generateBreadcrumbHtml(p, full) + }); + } + $scope.breadcrumbs = breadcrumbs; + } + + function _generateBreadcrumbHtml(name, state) { + var template = '' + name + ''; + return template; + } + }; +})(window, angular); \ No newline at end of file diff --git a/common_components/layouts/breadcrumb/breadcrumb.partial.html b/common_components/layouts/breadcrumb/breadcrumb.partial.html new file mode 100644 index 0000000..9a60a4d --- /dev/null +++ b/common_components/layouts/breadcrumb/breadcrumb.partial.html @@ -0,0 +1,8 @@ +
+ + + Help + +
diff --git a/common_components/layouts/documentsview/documents-view.controller.js b/common_components/layouts/documentsview/documents-view.controller.js new file mode 100644 index 0000000..6ef8e67 --- /dev/null +++ b/common_components/layouts/documentsview/documents-view.controller.js @@ -0,0 +1,12 @@ +(function (angular) { + var module = angular.module('framework.documents',[]); + + module.controller('DocumentsController', DocumentsController); + + DocumentsController.$inject = ['$scope', 'rData', '$state']; + function DocumentsController($scope, rData, $state) { + $scope.documents = rData; + $scope.selectedDocument=rData[0]; + } + +})(angular); \ No newline at end of file diff --git a/common_components/layouts/documentsview/documents-view.partial.html b/common_components/layouts/documentsview/documents-view.partial.html new file mode 100644 index 0000000..b74c3da --- /dev/null +++ b/common_components/layouts/documentsview/documents-view.partial.html @@ -0,0 +1,36 @@ +
+ Add New +
+
+
+
+
+

Documents

+
+
+
+
+ {{doc.ExpiryDate | countdown}} + {{doc.Name}}
+ {{doc.IdentificationNo}} + +
+
+
+
+
+
+
+
+

Viewer

+
+
+
+ Save + Delete +
+ +
+
+
+
\ No newline at end of file diff --git a/common_components/layouts/module/module-base.layout.html b/common_components/layouts/module/module-base.layout.html new file mode 100644 index 0000000..bbf6a5f --- /dev/null +++ b/common_components/layouts/module/module-base.layout.html @@ -0,0 +1,11 @@ +
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/common_components/layouts/module/module-cards.controller.js b/common_components/layouts/module/module-cards.controller.js new file mode 100644 index 0000000..530beea --- /dev/null +++ b/common_components/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/common_components/layouts/module/module-cards.partial.html b/common_components/layouts/module/module-cards.partial.html new file mode 100644 index 0000000..d8d0286 --- /dev/null +++ b/common_components/layouts/module/module-cards.partial.html @@ -0,0 +1,26 @@ +
+ +
+

{{title}}

+ + + +
+
+
+ No Items Added Yet. +
+
+
\ No newline at end of file diff --git a/common_components/layouts/module/module-item.controller.js b/common_components/layouts/module/module-item.controller.js new file mode 100644 index 0000000..7c15440 --- /dev/null +++ b/common_components/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/common_components/layouts/module/module-item.layout.html b/common_components/layouts/module/module-item.layout.html new file mode 100644 index 0000000..c981b06 --- /dev/null +++ b/common_components/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/common_components/layouts/module/module-table.controller.js b/common_components/layouts/module/module-table.controller.js new file mode 100644 index 0000000..739ccb6 --- /dev/null +++ b/common_components/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/common_components/layouts/module/module-table.partial.html b/common_components/layouts/module/module-table.partial.html new file mode 100644 index 0000000..01c7136 --- /dev/null +++ b/common_components/layouts/module/module-table.partial.html @@ -0,0 +1,17 @@ + + + + + + + + + + + View + + + + + + \ No newline at end of file diff --git a/common_components/layouts/structure/page.controller.js b/common_components/layouts/structure/page.controller.js new file mode 100644 index 0000000..2f8627a --- /dev/null +++ b/common_components/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/common_components/layouts/structure/page.layout.html b/common_components/layouts/structure/page.layout.html new file mode 100644 index 0000000..fc095d5 --- /dev/null +++ b/common_components/layouts/structure/page.layout.html @@ -0,0 +1,16 @@ +
+ +
+
+ +
+ +
+
+ + +
\ No newline at end of file diff --git a/common_components/layouts/table-page/table-page.layout.html b/common_components/layouts/table-page/table-page.layout.html new file mode 100644 index 0000000..489f478 --- /dev/null +++ b/common_components/layouts/table-page/table-page.layout.html @@ -0,0 +1,12 @@ +
+
+
+
+
+
+
+
+
+
+
+
diff --git a/common_components/services/ConsoleLoggingProvider.service.js b/common_components/services/ConsoleLoggingProvider.service.js new file mode 100644 index 0000000..e44b1ee --- /dev/null +++ b/common_components/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/common_components/services/Logger.service.js b/common_components/services/Logger.service.js new file mode 100644 index 0000000..b24f748 --- /dev/null +++ b/common_components/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/common_components/services/Menu.service.js b/common_components/services/Menu.service.js new file mode 100644 index 0000000..d9a52d0 --- /dev/null +++ b/common_components/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/common_components/services/Model.service.js b/common_components/services/Model.service.js new file mode 100644 index 0000000..8ff2ddd --- /dev/null +++ b/common_components/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/common_components/services/Storage.service.js b/common_components/services/Storage.service.js new file mode 100644 index 0000000..70c9580 --- /dev/null +++ b/common_components/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/common_components/tables/Table.controller.js b/common_components/tables/Table.controller.js new file mode 100644 index 0000000..51cdb6e --- /dev/null +++ b/common_components/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/common_components/tables/basic-table.layout.html b/common_components/tables/basic-table.layout.html new file mode 100644 index 0000000..4877fba --- /dev/null +++ b/common_components/tables/basic-table.layout.html @@ -0,0 +1,18 @@ +
+ + + + + + + + + + + +
+ {{ col.name }} +
+ {{ ::getValue(row, col) }} +
+
diff --git a/common_components/tables/sortable-table.layout.html b/common_components/tables/sortable-table.layout.html new file mode 100644 index 0000000..d67ec0a --- /dev/null +++ b/common_components/tables/sortable-table.layout.html @@ -0,0 +1,20 @@ +
+ + + + + + + + + + + + +
+ {{ ::col.name }} + +
+ {{ ::getValue(row, col) }} +
+
-- libgit2 0.21.0