Commit 6e6aa9b005ee7636f1e7f02fc4981c1a9791d483

Authored by Tarpit Grover
1 parent 67e94bc0
Exists in master

Basic Setup

Showing 29 changed files with 2333 additions and 0 deletions   Show diff stats
.gitignore 0 → 100644
... ... @@ -0,0 +1,180 @@
  1 +## Ignore Visual Studio temporary files, build results, and
  2 +## files generated by popular Visual Studio add-ons.
  3 +
  4 +# User-specific files
  5 +*.suo
  6 +*.user
  7 +*.sln.docstates
  8 +
  9 +# Build results
  10 +[Dd]ebug/
  11 +[Dd]ebugPublic/
  12 +[Rr]elease/
  13 +x64/
  14 +build/
  15 +bld/
  16 +[Bb]in/
  17 +[Oo]bj/
  18 +
  19 +# Roslyn cache directories
  20 +*.ide
  21 +
  22 +# MSTest test Results
  23 +[Tt]est[Rr]esult*/
  24 +[Bb]uild[Ll]og.*
  25 +
  26 +#NUNIT
  27 +*.VisualState.xml
  28 +TestResult.xml
  29 +
  30 +# Build Results of an ATL Project
  31 +[Dd]ebugPS/
  32 +[Rr]eleasePS/
  33 +dlldata.c
  34 +
  35 +*_i.c
  36 +*_p.c
  37 +*_i.h
  38 +*.ilk
  39 +*.meta
  40 +*.obj
  41 +*.pch
  42 +*.pdb
  43 +*.pgc
  44 +*.pgd
  45 +*.rsp
  46 +*.sbr
  47 +*.tlb
  48 +*.tli
  49 +*.tlh
  50 +*.tmp
  51 +*.tmp_proj
  52 +*.log
  53 +*.vspscc
  54 +*.vssscc
  55 +.builds
  56 +*.pidb
  57 +*.svclog
  58 +*.scc
  59 +
  60 +# Chutzpah Test files
  61 +_Chutzpah*
  62 +
  63 +# Visual C++ cache files
  64 +ipch/
  65 +*.aps
  66 +*.ncb
  67 +*.opensdf
  68 +*.sdf
  69 +*.cachefile
  70 +
  71 +# Visual Studio profiler
  72 +*.psess
  73 +*.vsp
  74 +*.vspx
  75 +
  76 +# TFS 2012 Local Workspace
  77 +$tf/
  78 +
  79 +# Guidance Automation Toolkit
  80 +*.gpState
  81 +
  82 +# ReSharper is a .NET coding add-in
  83 +_ReSharper*/
  84 +*.[Rr]e[Ss]harper
  85 +*.DotSettings.user
  86 +
  87 +# JustCode is a .NET coding addin-in
  88 +.JustCode
  89 +
  90 +# TeamCity is a build add-in
  91 +_TeamCity*
  92 +
  93 +# DotCover is a Code Coverage Tool
  94 +*.dotCover
  95 +
  96 +# NCrunch
  97 +_NCrunch_*
  98 +.*crunch*.local.xml
  99 +
  100 +# MightyMoose
  101 +*.mm.*
  102 +AutoTest.Net/
  103 +
  104 +# Web workbench (sass)
  105 +.sass-cache/
  106 +
  107 +# Installshield output folder
  108 +[Ee]xpress/
  109 +
  110 +# DocProject is a documentation generator add-in
  111 +DocProject/buildhelp/
  112 +DocProject/Help/*.HxT
  113 +DocProject/Help/*.HxC
  114 +DocProject/Help/*.hhc
  115 +DocProject/Help/*.hhk
  116 +DocProject/Help/*.hhp
  117 +DocProject/Help/Html2
  118 +DocProject/Help/html
  119 +
  120 +# Click-Once directory
  121 +publish/
  122 +
  123 +# Publish Web Output
  124 +*.[Pp]ublish.xml
  125 +*.azurePubxml
  126 +## TODO: Comment the next line if you want to checkin your web deploy settings but do note that will include unencrypted passwords
  127 +*.pubxml
  128 +
  129 +# NuGet Packages Directory
  130 +packages/*
  131 +## TODO: If the tool you use requires repositories.config uncomment the next line
  132 +#!packages/repositories.config
  133 +
  134 +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
  135 +# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented)
  136 +!packages/build/
  137 +
  138 +# Windows Azure Build Output
  139 +csx/
  140 +*.build.csdef
  141 +
  142 +# Windows Store app package directory
  143 +AppPackages/
  144 +
  145 +# Others
  146 +sql/
  147 +*.Cache
  148 +ClientBin/
  149 +[Ss]tyle[Cc]op.*
  150 +~$*
  151 +*~
  152 +*.dbmdl
  153 +*.dbproj.schemaview
  154 +*.pfx
  155 +*.publishsettings
  156 +node_modules/
  157 +
  158 +# RIA/Silverlight projects
  159 +Generated_Code/
  160 +
  161 +# Backup & report files from converting an old project file to a newer
  162 +# Visual Studio version. Backup files are not needed, because we have git ;-)
  163 +_UpgradeReport_Files/
  164 +Backup*/
  165 +UpgradeLog*.XML
  166 +UpgradeLog*.htm
  167 +
  168 +# SQL Server files
  169 +*.mdf
  170 +*.ldf
  171 +.DS_Store
  172 +.codekit-cache
  173 +
  174 +# Business Intelligence projects
  175 +*.rdl.data
  176 +*.bim.layout
  177 +*.bim_*.settings
  178 +
  179 +# Microsoft Fakes
  180 +FakesAssemblies/
0 181 \ No newline at end of file
... ...
common_components/directives/BindHtmlCompile.directive.js 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +(function (angular) {
  2 + 'use strict';
  3 +
  4 + var module = angular.module('framework.directives.utils', []);
  5 +
  6 + module.directive('bindHtmlCompile', ['$compile', bindHtmlCompile]);
  7 +
  8 + function bindHtmlCompile($compile) {
  9 + return {
  10 + restrict: 'A',
  11 + link: function (scope, element, attrs) {
  12 + scope.$watch(function () {
  13 + return scope.$eval(attrs.bindHtmlCompile);
  14 + }, function (value) {
  15 + element.html(value);
  16 + $compile(element.contents())(scope);
  17 + });
  18 + }
  19 + };
  20 + }
  21 +}(angular));
0 22 \ No newline at end of file
... ...
common_components/directives/Card.directive.js 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +(function(angular){
  2 + var module = angular.module('framework.directives.UI',[]);
  3 +
  4 + module.directive('card', cardDirective);
  5 + cardDirective.$inject=['$state', '$interval', '$parse'];
  6 + function cardDirective($state, $interval, $parse) {
  7 + return {
  8 + restrict: 'E',
  9 + transclude: true,
  10 + replace: true,
  11 + scope: true,
  12 + template: function(element, attrs) {
  13 + var template='';
  14 + template += '<div class="card"><div class="card-container"';
  15 + if(attrs.allowClick!="false") {
  16 + template += ' ng-click="viewItem(item)"'
  17 + }
  18 + template += '><svg class="logo" style="background-image:url(\'{{::accessors.LogoUrl(item)}}\')" viewBox="0 0 100 100"><circle cx="50" cy="50" r="50" fill="transparent" stroke-width="10" stroke-linecap="round" ng-show="showScore==true" stroke-location="outside"></circle></svg>';
  19 + if(attrs.showTitle!="false") {
  20 + template += '<strong>{{ ::accessors.Title(item) }}</strong>';
  21 + }
  22 + if(attrs.showSubtitle!="false") {
  23 + template += '<small>{{ ::accessors.SubTitle(item) }}</small>';
  24 + }
  25 + template += '</div></div>';
  26 + return template;
  27 + },
  28 + link: function(scope, elem, attrs, ctrls, transcludeFn) {
  29 + if(attrs.item) {
  30 + var getter = $parse(attrs.item);
  31 + scope.item = getter(scope);
  32 + }
  33 + scope.viewItem = viewItem;
  34 + if(scope.accessors.Score) {
  35 + var percentScore = (2 * Math.PI*50)/100*scope.accessors.Score(scope.item);
  36 + var percentColor = (percentScore < 90)? 'red': (percentScore >= 90 && percentScore <180)? 'orange' :(percentScore >= 180 && percentScore <270)? 'rgb(25, 195, 45)' : 'rgb(25, 195, 45);';
  37 + elem.find("circle")
  38 + .attr("stroke", percentColor)
  39 + .attr("stroke-dasharray",percentScore+",10000");
  40 + }
  41 +
  42 + function viewItem(item) {
  43 + $state.go(scope.rNavTo, { Id: item.Id });
  44 + }
  45 + }
  46 + }
  47 + }
  48 +})(angular);
0 49 \ No newline at end of file
... ...
common_components/directives/FieldFor.directive.js 0 → 100644
... ... @@ -0,0 +1,218 @@
  1 +(function (angular) {
  2 + var module = angular.module('framework.directives.UI');
  3 +
  4 + module.directive('fieldFor', fieldForDirective);
  5 + module.directive('formFor', formForDirective);
  6 + module.filter('propSearch', propertySearchFilter);
  7 +
  8 + formForDirective.$inject = ['Model'];
  9 + function formForDirective(Model) {
  10 + return {
  11 + restrict: 'A',
  12 + scope: true,
  13 + transclude: true,
  14 + template: function (element, attrs) {
  15 + return '<form name="formFor.form" class="transclude-target" autocomplete="off" novalidate></form>';
  16 + },
  17 + link: function (scope, element, attrs, ctrls, transcludeFn) {
  18 + scope.formFor = (scope.formFor || {});
  19 + scope.formFor.target = scope.$eval(attrs.formFor);
  20 + scope.formFor.schema = (attrs.schema) ? Model[attrs.schema].$schema : scope.target.$$schema;
  21 + scope.formFor.mode = 'EDIT';
  22 +
  23 + scope.$watch(function () {
  24 + return scope.target;
  25 + }, function (newvalue) {
  26 +
  27 + });
  28 +
  29 + if (attrs.mode) {
  30 + scope.formFor.mode = attrs.mode;
  31 + attrs.$observe('mode', function (newvalue) {
  32 + scope.formFor.mode = newvalue;
  33 + });
  34 + }
  35 +
  36 + transcludeFn(scope, function (clone, scope) {
  37 + element.children('.transclude-target').append(clone);
  38 + });
  39 + }
  40 + }
  41 + };
  42 +
  43 + fieldForDirective.$inject = ['Model'];
  44 + function fieldForDirective(Model) {
  45 + var defaults = {
  46 + inputType: 'text',
  47 + required: false,
  48 + selectTheme: 'selectize',
  49 + selectSelectedTemplate: '{{$select.selected.Name}}',
  50 + selectOptionTemplate: '<div ng-bind-html="option.Name | highlight: $select.search"></div><small class="text-muted">{{option.Description}}</small>'
  51 + };
  52 + return {
  53 + restrict: 'A',
  54 + scope: true,
  55 + template: function (element, attrs) {
  56 + var formParent = element.closest('[form-for]'),
  57 + subformParent = element.closest('[sub-form-for]'),
  58 + schemaName = ((subformParent.length) ? subformParent : formParent).attr('schema'),
  59 + ctor = Model[schemaName],
  60 + schema = ctor && ctor.$schema,
  61 + fieldDef = schema && schema[attrs.fieldFor],
  62 + subForm = subformParent.attr('sub-form-for') || '';
  63 +
  64 + if (!fieldDef) {
  65 + return;
  66 + }
  67 +
  68 + var inputConfig = angular.extend({ display: attrs.fieldFor, inputType: 'text' }, defaults, fieldDef, fieldDef.input, attrs);
  69 +
  70 + //need to fetch these.
  71 + var labelText = attrs.display || attrs.fieldFor,
  72 + fieldName = attrs.fieldFor,
  73 + selectFrom = attrs.selectFrom;
  74 +
  75 + var template = '' +
  76 + '<div class="form-group">' +
  77 + '<label class="control-label" for="' + subForm + fieldName + 'Input">' + inputConfig.display + '</label>' +
  78 + '<div ng-if="formFor.mode === \'VIEW\'">' +
  79 + '<p class="form-control-static" ng-bind="::formFor.target.' + ((subForm) ? (subForm + '.') : '') + (inputConfig.displayField ? inputConfig.displayField : fieldName) + (inputConfig.displayFilters ? inputConfig.displayFilters : '') + '"></p>' +
  80 + '</div>' +
  81 + '<div ng-if="formFor.mode === \'EDIT\'">';
  82 +
  83 + if (selectFrom) {
  84 + template += '' +
  85 + '<ui-select ng-model="formFor.target.' + ((subForm) ? (subForm + '.') : '') + fieldName + '" name="' + subForm + fieldName + 'Input" theme="' + inputConfig.selectTheme + '" ng-disabled="' + (angular.isDefined(inputConfig.editable) && !inputConfig.editable) + '" ng-required="' + inputConfig.required + '">' +
  86 + '<ui-select-match allow-clear="true" placeholder="' + (inputConfig.placeholder || 'Select..') + '">' + inputConfig.selectSelectedTemplate + '</ui-select-match>' +
  87 + '<ui-select-choices repeat="option.Id as option in ' + selectFrom + ' | propSearch: { Name: $select.search }">' +
  88 + inputConfig.selectOptionTemplate +
  89 + '</ui-select-choices>'+
  90 + '</ui-select>';
  91 + } else if (inputConfig.type === moment) {
  92 + template += '' +
  93 + '<div class="input-group">' +
  94 + buildElement('input', {
  95 + class: 'form-control',
  96 + type: 'text',
  97 + name: subForm + fieldName + 'Input',
  98 + ngModel: 'formFor.target.' + ((subForm) ? (subForm + '.') : '') + fieldName,
  99 + ngRequired: inputConfig.required,
  100 + datepickerPopup: 'dd/MM/yyyy',
  101 + isOpen: 'opened',
  102 + placeholder: 'DD/MM/YYYY',
  103 + ngDisabled: angular.isDefined(inputConfig.editable) && !inputConfig.editable
  104 + }) +
  105 + '<span class="input-group-btn">' +
  106 + '<button type="button" class="btn btn-default" ng-click="openCalendar($event)"><i class="glyphicon glyphicon-calendar"></i></button>' +
  107 + '</span>' +
  108 + '</div>';
  109 + } else {
  110 + if (inputConfig.append || inputConfig.prepend) {
  111 + template += '<div class="input-group">';
  112 + }
  113 + if (inputConfig.prepend) {
  114 + template += '<span class="input-group-btn">' +
  115 + '<button type="button" class="btn btn-default" ng-click="' + inputConfig.prepend.click + '"><i class="' + inputConfig.prepend.icon + '"></i>' + inputConfig.prepend.text + '</button>' +
  116 + '</span>'
  117 + }
  118 + template += buildElement('input', {
  119 + class: 'form-control',
  120 + type: inputConfig.inputType,
  121 + name: subForm + fieldName + 'Input',
  122 + ngModel: 'formFor.target.' + ((subForm) ? (subForm + '.') : '') + fieldName,
  123 + ngRequired: inputConfig.required,
  124 + placeholder: inputConfig.placeholder
  125 + });
  126 + if (inputConfig.append) {
  127 + template += '<span class="input-group-btn">' +
  128 + '<button type="button" class="btn btn-default" ng-click="' + inputConfig.append.click + '"><i class="' + inputConfig.append.icon + '"></i>' + inputConfig.append.text + '</button>' +
  129 + '</span>'
  130 + }
  131 + if (inputConfig.append || inputConfig.prepend) {
  132 + template += '</div>';
  133 + }
  134 + }
  135 +
  136 + template += '' +
  137 + '<div ng-messages="formFor.form.' + subForm + fieldName + 'Input.$error" class="help-block">' +
  138 + '<div ng-message="required">' + inputConfig.display + ' is required.</div>' +
  139 + '<div ng-message="parse">' + inputConfig.display + ' is incorrectly formatted.</div>' +
  140 + '<div ng-message="email">' + inputConfig.display + ' is not a valid e-mail.</div>' +
  141 + '</div>' +
  142 + '</div>';
  143 + template += '</div>';
  144 + return template;
  145 + },
  146 + link: function (scope, element, attrs) {
  147 + scope.opened = false;
  148 +
  149 + scope.openCalendar = function (event) {
  150 + event.preventDefault();
  151 + event.stopPropagation();
  152 + scope.opened = true;
  153 + }
  154 + }
  155 + };
  156 + };
  157 +
  158 + function propertySearchFilter() {
  159 + return function (items, props) {
  160 + var out = [];
  161 +
  162 + if (angular.isArray(items)) {
  163 + items.forEach(function (item) {
  164 + var itemMatches = false;
  165 +
  166 + var keys = Object.keys(props);
  167 + for (var i = 0; i < keys.length; i++) {
  168 + var prop = keys[i];
  169 + var text = props[prop].toLowerCase();
  170 + if (item[prop].toString().toLowerCase().indexOf(text) !== -1) {
  171 + itemMatches = true;
  172 + break;
  173 + }
  174 + }
  175 +
  176 + if (itemMatches) {
  177 + out.push(item);
  178 + }
  179 + });
  180 + } else {
  181 + // Let the output be the input untouched
  182 + out = items;
  183 + }
  184 +
  185 + return out;
  186 + };
  187 + };
  188 +
  189 + function buildElement(elementType, attrs) {
  190 + var result = '<' + elementType + ' ';
  191 +
  192 + angular.forEach(attrs, function (value, key) {
  193 + if (angular.isDefined(value) && value !== null)
  194 + result += normalise(key) + '="' + value + '"';
  195 + });
  196 +
  197 + if (elementType === 'input') {
  198 + result += ' />';
  199 + } else {
  200 + result += ' ></' + elementType + '>';
  201 + }
  202 + return result;
  203 + }
  204 +
  205 + function normalise(input) {
  206 + var result = '';
  207 + for (var i = 0; i < input.length; i++) {
  208 + var char = input[i];
  209 + if (char == char.toUpperCase()) {
  210 + result += '-' + char.toLowerCase();
  211 + } else {
  212 + result += char;
  213 + }
  214 + }
  215 + return result;
  216 + }
  217 +
  218 +}(angular));
0 219 \ No newline at end of file
... ...
common_components/directives/Score.directive.js 0 → 100644
... ... @@ -0,0 +1,53 @@
  1 +(function(angular){
  2 + var module = angular.module('framework.directives.UI');
  3 +
  4 + module.directive('score', scoreDirective);
  5 +
  6 + scoreDirective.$inject=['$state', '$interval', '$parse'];
  7 +
  8 + function scoreDirective($state, $interval, $parse) {
  9 + return {
  10 + restrict: 'E',
  11 + transclude: true,
  12 + replace: true,
  13 + scope: true,
  14 + template: function(element, attrs) {
  15 + var template='';
  16 + template += '<div class="score"';
  17 + if(attrs.size) {
  18 + var scoreSize = parseInt(attrs.size);
  19 + template += ' style="position:relative;width:'+ (scoreSize) +'px; height: ' + (scoreSize) + 'px; border-radius:'+ (scoreSize/2) + 'px;padding:5px;">';
  20 + }
  21 + else {
  22 + template += ' style="width:100px;height:100px;border-radius:50px;">';
  23 + }
  24 + if(attrs.showScore) {
  25 + template += '<div class="score-value" style="line-height:' + scoreSize + 'px">'+attrs.showScore+'</div>';
  26 + }
  27 + template += '<svg class="dial" viewBox="0 0 100 100">' +
  28 + '<circle class="score-back-circle" cx="50" cy="50" r="45" fill="transparent" stroke-linecap="round"></circle>' +
  29 + '<circle class="score-front-circle" cx="50" cy="50" r="45" fill="transparent" stroke-linecap="round"></circle>' +
  30 + '</svg>';
  31 + template += '</div>';
  32 + return template;
  33 + },
  34 + link: function(scope, elem, attrs, ctrls, transcludeFn) {
  35 + if(!attrs.value)
  36 + return;
  37 +
  38 + var strokeWidth = (attrs.strokeWidth)? attrs.strokeWidth : 5;
  39 + var percentScore = (2 * Math.PI*50)/100 * attrs.value;
  40 + var percentColor = (percentScore < 90)? 'red': (percentScore >= 90 && percentScore <180)? 'orange' :(percentScore >= 180 && percentScore <270)? 'rgb(25, 195, 45)' : 'rgb(25, 195, 45)';
  41 +
  42 + elem.find("circle.score-back-circle")
  43 + .attr("stroke", "#eee")
  44 + .attr("stroke-width", strokeWidth);
  45 +
  46 + elem.find("circle.score-front-circle")
  47 + .attr("stroke", percentColor)
  48 + .attr("stroke-width", strokeWidth)
  49 + .attr("stroke-dasharray", percentScore+",10000");
  50 + }
  51 + }
  52 + }
  53 +})(angular);
0 54 \ No newline at end of file
... ...
common_components/directives/TableFor.directive.js 0 → 100644
... ... @@ -0,0 +1,592 @@
  1 +(function (angular) {
  2 + var module = angular.module('framework.directives.UI');
  3 +
  4 + module.directive('tableFor', tableForDirective);
  5 + module.directive('tableTitlebar', tableTitlebarDirective);
  6 + module.directive('tableHeading', tableHeadingDirective);
  7 + module.directive('tableDataRows', tableDataRowsDirective);
  8 + module.directive('tableFooter', tableFooterDirective);
  9 + module.filter('publish', publishFilter);
  10 + module.filter('softFilter', softFilter);
  11 + module.filter('pageGroup', pageGroupFilter);
  12 +
  13 +
  14 +
  15 + tableForDirective.$inject = ['$parse', 'Model', '$modal', '$q', '$filter', '$timeout'];
  16 + function tableForDirective($parse, Model, $modal, $q, $filter, $timeout) {
  17 + var defaults = {
  18 + wrapperClass: 'table-responsive',
  19 + tableClass: 'table table-bordered',
  20 + minWidth: '600px'
  21 + }
  22 +
  23 + function linkFn(scope, element, attrs, ctrls, transcludeFn) {
  24 + var externalSearch = $parse(attrs.filtering)(scope);
  25 + var externalSorting = $parse(attrs.sorting)(scope);
  26 + var externalGrouping = $parse(attrs.grouping)(scope);
  27 + var externalPaging = $parse(attrs.paging)(scope);
  28 +
  29 + scope.$schema = Model[attrs.schema].$schema;
  30 + scope.$table = {
  31 + actionColumn: false,
  32 + selectionColumn: false,
  33 + cardTemplate: '{{$row | json}}',
  34 + toggleSelectAll: toggleSelectAll,
  35 + toggleRowSelection: toggleRowSelection,
  36 + toggleGroupSelection: toggleGroupSelection,
  37 + allSelected: false,
  38 + reset: reset,
  39 + configureColumns: configureColumns,
  40 + sort: sort,
  41 + getSortingClass: getSortingClass
  42 + };
  43 + scope.$columns = generateColumns(scope.$schema);
  44 + scope.$filtering = externalSearch || {
  45 + $: ''
  46 + }
  47 + scope.$sorting = externalSorting || [];
  48 + scope.$grouping = externalGrouping || {
  49 + value: undefined
  50 + };
  51 + scope.$paging = externalPaging || {
  52 + currentPage: 1,
  53 + totalItems: 0,
  54 + maxPerPage: 10
  55 + };
  56 + scope.$data = {}; //object as it's grouped
  57 + scope.$datasource = function () {
  58 + return arguments[4]; //returns existing data by default;
  59 + };
  60 +
  61 + var datasource = $parse(attrs.datasource)(scope);
  62 + if (angular.isArray(datasource)) {
  63 + scope.$datasource = generateLocalDatasource(datasource);
  64 + } else if (angular.isFunction(datasource)) {
  65 + scope.$datasource = datasource;
  66 + } else {
  67 + scope.$datasource = generateModelDatasource(scope.$schema.query);
  68 + }
  69 +
  70 + scope.$watchCollection(function () { return scope.$filtering; }, debouncedUpdateData);
  71 + scope.$watchCollection(function () { return scope.$sorting; }, debouncedUpdateData);
  72 + scope.$watchCollection(function () { return scope.$grouping; }, debouncedUpdateData);
  73 + scope.$watch(function () { return scope.$paging.currentPage; }, debouncedUpdateData);
  74 + scope.$watch(function () { return scope.$paging.maxPerPage; }, debouncedUpdateData);
  75 +
  76 + var updateDebounce = null;
  77 +
  78 + function debouncedUpdateData() {
  79 + if (updateDebounce) {
  80 + $timeout.cancel(updateDebounce);
  81 + updateDebounce = null;
  82 + }
  83 + updateDebounce = $timeout(updateData, 500);
  84 + }
  85 +
  86 + function updateData() {
  87 + for (var key in scope.$data) {
  88 + delete scope.$data[key];
  89 + }
  90 + scope.$data.$loading = true;
  91 + scope.$data.$error = null;
  92 + scope.$datasource(scope.$filtering, scope.$sorting, scope.$grouping, scope.$paging, scope.$data).then(dataReceived, dataError, dataNotified);
  93 + }
  94 +
  95 + function dataReceived(data) {
  96 + for (var key in scope.$data) {
  97 + delete scope.$data[key];
  98 + }
  99 + scope.$data.$loading = false;
  100 + angular.extend(scope.$data, data);
  101 + }
  102 +
  103 + function dataError(error) {
  104 + for (var key in scope.$data) {
  105 + delete scope.$data[key];
  106 + }
  107 + scope.$data.$loading = false;
  108 + scope.$data.$error = error;
  109 + }
  110 +
  111 + function dataNotified(dataLength) {
  112 + scope.$paging.totalItems = dataLength;
  113 + }
  114 +
  115 + transcludeFn(scope, function (clone, scope) {
  116 + element.find('table').append(clone);
  117 + });
  118 +
  119 + function generateLocalDatasource(initialData) {
  120 + var data = initialData;
  121 + return function (filtering, sorting, grouping, paging, existing) {
  122 + var deferred = $q.defer();
  123 +
  124 + setTimeout(function () {
  125 + var filter = $filter('filter');
  126 + var orderBy = $filter('orderBy');
  127 + var groupBy = $filter('groupBy');
  128 + var pageGroup = $filter('pageGroup');
  129 +
  130 + deferred.notify(filter(data, filtering).length);
  131 +
  132 + var amendedSorting = [grouping.value];
  133 + [].push.apply(amendedSorting, sorting);
  134 +
  135 + var result = pageGroup(groupBy(orderBy(filter(data, filtering), amendedSorting), grouping.value), paging);
  136 +
  137 + deferred.resolve(result);
  138 + }, 0);
  139 +
  140 + return deferred.promise;
  141 + }
  142 + }
  143 +
  144 + function generateModelDatasource(modelQuery) {
  145 + var queryBase = modelQuery;
  146 + return function (filtering, sorting, grouping, paging, existing) {
  147 + var deferred = $q.defer();
  148 +
  149 + query = queryBase();
  150 + if (grouping.value) {
  151 + query.orderBy(grouping.value);
  152 + }
  153 +
  154 + angular.forEach(ordering, function (o) { query.orderBy(o); });
  155 +
  156 +
  157 + setTimeout(function () {
  158 + var filter = $filter('filter');
  159 + var groupBy = $filter('groupBy');
  160 + var pageGroup = $filter('pageGroup');
  161 +
  162 + deferred.notify(filter(data, filtering).length);
  163 +
  164 + var amendedSorting = [grouping.value];
  165 + [].push.apply(amendedSorting, sorting);
  166 +
  167 + var result = pageGroup(groupBy(orderBy(filter(data, filtering), amendedSorting), grouping.value), paging);
  168 +
  169 + deferred.resolve(result);
  170 + }, 0);
  171 +
  172 + return deferred.promise;
  173 + }
  174 + }
  175 +
  176 + function generateColumns(modelSchema) {
  177 + var columns = [];
  178 + angular.forEach(modelSchema, function (value, key) {
  179 + var config = angular.extend({}, value, value.table || {});
  180 + if (config.hidden) {
  181 + return;
  182 + }
  183 + if (!angular.isDefined(config.visible)) {
  184 + config.visible = true;
  185 + }
  186 + if (!angular.isDefined(config.binding) && config.type === moment) {
  187 + config.binding = key + ".toDate() | date:'dd/MM/yyyy'";
  188 + }
  189 + 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 });
  190 + });
  191 + return columns;
  192 + }
  193 +
  194 + function toggleSelectAll() {
  195 + scope.$table.allSelected = !scope.$table.allSelected;
  196 + angular.forEach(scope.$data, function (group) {
  197 + toggleGroupSelection(group, scope.$table.allSelected);
  198 + });
  199 + }
  200 +
  201 + function toggleRowSelection(group, row, force) {
  202 + row.$selected = angular.isDefined(force) ? force : !row.$selected;
  203 + var selected = group.filter(function (d) { return d.$selected; });
  204 + group.$selected = selected.length === group.length;
  205 +
  206 + var groups = 0;
  207 + var selectedGroups = 0;
  208 + angular.forEach(scope.$data, function (group, title) {
  209 + if (title[0] == '$') {
  210 + return;
  211 + }
  212 + groups++;
  213 + if (group.$selected) selectedGroups++;
  214 + });
  215 + scope.$table.allSelected = groups === selectedGroups;
  216 + }
  217 +
  218 + function toggleGroupSelection(group, force) {
  219 + if (angular.isDefined(force)) {
  220 + group.$selected = force;
  221 + } else {
  222 + group.$selected = !group.$selected;
  223 + }
  224 + angular.forEach(group, function (row) {
  225 + row.$selected = group.$selected;
  226 + });
  227 +
  228 + if (!angular.isDefined(force)) {
  229 + var groups = 0;
  230 + var selectedGroups = 0;
  231 + angular.forEach(scope.$data, function (group, title) {
  232 + if (title[0] == '$') {
  233 + return;
  234 + }
  235 + groups++;
  236 + if (group.$selected) selectedGroups++;
  237 + });
  238 + debugger;
  239 + scope.$table.allSelected = groups === selectedGroups;
  240 + }
  241 + }
  242 +
  243 + function setAreAllSelected() {
  244 + scope.$table.allSelected = false;
  245 + }
  246 +
  247 + function sort(col) {
  248 + //multisort
  249 + var currentSort = scope.$sorting[0];
  250 + if (col == currentSort) {
  251 + scope.$sorting[0] = '-' + col;
  252 + } else if ('-' + col == currentSort) {
  253 + scope.$sorting[0] = col;
  254 + } else {
  255 + scope.$sorting.length = 0;
  256 + scope.$sorting.push(col);
  257 + }
  258 + }
  259 +
  260 + function getSortingClass(col) {
  261 + if (col == scope.$sorting[0]) {
  262 + return 'icon-Arrow-Down2 brand-primary';
  263 + }
  264 + else if ('-' + col == scope.$sorting[0]) {
  265 + return 'icon-Arrow-Up2 brand-primary';
  266 + } else {
  267 + return 'icon-Arrow-Down';
  268 + }
  269 + }
  270 +
  271 + function reset() {
  272 + scope.$filtering = externalSearch || {
  273 + $: ''
  274 + }
  275 + scope.$sorting = [];
  276 +
  277 + //TODO: Deselect ALL
  278 +
  279 + scope.$columns.length = 0;
  280 + [].push.apply(scope.$columns, generateColumns(scope.$schema));
  281 + }
  282 +
  283 + function buildAccessor(fieldName) {
  284 + console.log('return row.' + fieldName + ';');
  285 + window.count = window.count || 0;
  286 + return new Function('row', 'return row.' + fieldName + ';');
  287 + }
  288 +
  289 + function configureColumns() {
  290 + $modal.open({
  291 + template: '' +
  292 + '<div class="modal-header">' +
  293 + '<button class="close" type="button" ng-click="$close()"><i class="icon-Close"></i></button>' +
  294 + '<h3 class="modal-title">Customize Columns</h3>' +
  295 + '</div>' +
  296 + '<div class="modal-body">' +
  297 + '<div ng-repeat="column in columns">' +
  298 + '<a ng-click="moveUp(column)" ng-hide="$first"><i class="icon-Arrow-UpinCircle brand-primary"></i></a>' +
  299 + ' &nbsp; {{column.title}}' +
  300 + ' &nbsp; <a ng-click="moveDown(column)" ng-hide="$last"><i class="icon-Arrow-DowninCircle brand-primary"></i></a>' +
  301 + '<div class="checkbox pull-right m0">' +
  302 + '<input type="checkbox" id="columncustomise{{::$index}}" ng-model="column.visible" ng-checked="column.visible" />' +
  303 + '<label for="columncustomise{{::$index}}"></label>' +
  304 + '</div><br class="clearfix">' +
  305 + '</div>' +
  306 + '</div>',
  307 + controller: function ($scope, columns) {
  308 + $scope.columns = columns;
  309 + $scope.moveUp = function (column) {
  310 + var index = $scope.columns.indexOf(column);
  311 + move(index, index - 1);
  312 + }
  313 + $scope.moveDown = function (column) {
  314 + var index = $scope.columns.indexOf(column);
  315 + move(index, index + 1);
  316 + }
  317 +
  318 + function move(old_index, new_index) {
  319 + $scope.columns.splice(new_index, 0, $scope.columns.splice(old_index, 1)[0]);
  320 + }
  321 + },
  322 + size: 'sm',
  323 + resolve: {
  324 + columns: function () {
  325 + return scope.$columns
  326 + }
  327 + }
  328 + });
  329 + }
  330 + }
  331 +
  332 + function compileFn(element, attrs) {
  333 + return linkFn;
  334 + }
  335 +
  336 + return {
  337 + scope: true,
  338 + transclude: true,
  339 + template: function (element, attrs) {
  340 + return '<div class="' + (attrs.wrapperClass || defaults.wrapperClass) + '"><table class="' + (attrs.tableClass || defaults.tableClass) + ' module-table" style="min-width:' + (attrs.minWidth || defaults.minWidth) + '"></table></div>'
  341 + },
  342 + compile: function (element, attrs) {
  343 + return linkFn;
  344 + }
  345 + };
  346 + }
  347 +
  348 + function tableTitlebarDirective() {
  349 + return {
  350 + restrict: 'E',
  351 + scope: true,
  352 + replace: true,
  353 + template: function (element, attrs) {
  354 + var template = '' +
  355 + '<thead><tr><th colspan="100" class="table-arrange">';
  356 + if (attrs.title) {
  357 + template += '<h3 class="table-title">' + attrs.title + '</h3>';
  358 + }
  359 + if (attrs.showCount) {
  360 + template += '<h3 class="table-title brand-primary">({{$visible.length}} / {{ $data.length }})</h3>';
  361 + }
  362 + if (attrs.columnsOptions) {
  363 + template += '<div class="columns-options">' +
  364 + '<a href="" ng-click="$table.reset()"><i class="icon-Reset"></i>Reset</a>' +
  365 + '<a href="" ng-click="$table.configureColumns()"><i class="icon-Receipt-2"></i>Arrange</a>' +
  366 + '</div>';
  367 + }
  368 + template += '<div class="pull-right">' +
  369 + '<pagination class="pagination-sm" style="margin: 0;" total-items="$paging.totalItems" ng-model="$paging.currentPage" items-per-page="$paging.maxPerPage" boundary-links="true" max-size="5"></pagination>' +
  370 + '</div>';
  371 + if (attrs.search) {
  372 + template += '<input type="text" class="form-control pull-right" style="width:300px" placeholder="Search.." ng-model="$filtering.$" />';
  373 + }
  374 + template += '</th></tr></thead>'
  375 + return template;
  376 + }
  377 + }
  378 + }
  379 +
  380 + function tableHeadingDirective() {
  381 + var defaults = {
  382 + }
  383 + return {
  384 + restrict: 'E',
  385 + scope: true,
  386 + replace: true,
  387 + template: function (element, attrs) {
  388 + var tmpl = '' +
  389 + '<thead><tr>' +
  390 + '<th ng-if="::$table.selectionColumn" style="width:40px;text-align:center;">' +
  391 + '<div class="checkbox"><input type="checkbox" id="table-group-check" ng-click="$table.toggleSelectAll()" ng-checked="$table.allSelected"/><label for="table-group-check"></label></div>' +
  392 +
  393 + '</th>' +
  394 + '<th ng-repeat="$column in $columns | filter: { visible: true }" class="module-table-head">' +
  395 + '<div><i ng-class="$column.icon"></i> {{ ::$column.title }}' +
  396 + '<span class="table-sort"><a href="" ng-click="$table.sort($column.key)"><i ng-class="$table.getSortingClass($column.key)"></i></a></span>' +
  397 + '</div>' + //add filter and search menu
  398 + '</th>' +
  399 + '<th ng-if="::$table.actionColumn" style="text-align:center;">' +
  400 + 'Actions' +
  401 + '</th>' +
  402 + '</tr>';
  403 + tmpl += '</thead>';
  404 + return tmpl;
  405 + }
  406 + };
  407 + }
  408 +
  409 + tableDataRowsDirective.$inject = ['$parse', 'Model'];
  410 + function tableDataRowsDirective($parse, Model) {
  411 + return {
  412 + restrict: 'E',
  413 + scope: true,
  414 + replace: true,
  415 + template: function (element, attrs) {
  416 + var tmp = '' +
  417 + '<tbody ng-repeat="(title, group) in $data track by title">' +
  418 + '<tr ng-hide="title == \'undefined\'">' +
  419 + '<td ng-if="::$table.selectionColumn" style="width:40px;text-align:center;">' +
  420 + '<div class="checkbox"><input id="group-check-{{::title}}" type="checkbox" ng-click="$table.toggleGroupSelection(group)" ng-checked="group.$selected"/><label for="group-check-{{::title}}"></label></div>' +
  421 + '</td>' +
  422 + '<td colspan="1000"><b><u>{{ title }}</u></b></td>' +
  423 + '</tr>' +
  424 + '<tr ng-repeat="$row in group track by $row.Id">' +
  425 + '<td ng-if="::$table.selectionColumn" style="width:40px;text-align:center;">' +
  426 + '<div class="checkbox"><input id="table-check-{{::title}}{{::$index}}" type="checkbox" ng-click="$table.toggleRowSelection(group, $row)" ng-checked="$row.$selected"/><label for="table-check-{{::title}}{{::$index}}"></label></div>' +
  427 + '</td>' +
  428 + '<td ng-repeat="$column in $columns | filter: { visible: true } track by $column.key">' +
  429 + '{{$column.accessor($row)}}' +
  430 + '</td>' +
  431 + '<td ng-if="::$table.actionColumn" style="text-align:center;" bind-html-compile="$table.actionColumn.html">' +
  432 + '</td>' +
  433 + '</tr>' +
  434 + '</tbody>';
  435 + return tmp;
  436 + },
  437 + compile: function (element, attrs) {
  438 + var selectionElement = _findSelectionColumn(element);
  439 + var actionsElement = _findActionColumn(element);
  440 +
  441 + return link;
  442 +
  443 + function link(scope, element, attrs) {
  444 + scope.$table.actionColumn = actionsElement;
  445 + scope.$table.selectionColumn = selectionElement;
  446 + scope.$table.getCellValue = getCellValue;
  447 +
  448 + function getCellValue(row, col) {
  449 + console.log('GetCellValue is depreciated for performance, please use "$column.accessor($row)" instead, functions are now precompilied;');
  450 + if (typeof col.binding === 'string')
  451 + return $parse(col.binding)(row);
  452 + else if (angular.isFunction(col.binding))
  453 + return col.binding(row, col);
  454 + else
  455 + return '';
  456 + }
  457 + }
  458 + }
  459 + }
  460 +
  461 + function _findActionColumn(element) {
  462 + var actionsElement = jQuery(element.context).find('table-actions');
  463 + if (actionsElement.length) {
  464 + return {
  465 + html: actionsElement.html()
  466 + };
  467 + }
  468 + return false;
  469 + }
  470 +
  471 + function _findSelectionColumn(element) {
  472 + var selectionElement = jQuery(element.context).find('table-selection');
  473 + if (selectionElement.length) {
  474 + return {
  475 + html: selectionElement.html()
  476 + };
  477 + }
  478 + return false;
  479 + }
  480 + }
  481 +
  482 + function tableFooterDirective() {
  483 + return {
  484 + restrict: 'E',
  485 + scope: true,
  486 + replace: true,
  487 + template: function (element, attrs) {
  488 + var template = '' +
  489 + '<tfoot><tr><th colspan="100" class="table-arrange">';
  490 + if (attrs.title) {
  491 + template += '<h3 class="table-title">' + attrs.title + '</h3>';
  492 + }
  493 + if (attrs.showCount) {
  494 + template += '<b class="brand-primary">(showing {{$paging.maxPerPage}} from {{ $paging.totalItems }})</b>';
  495 + }
  496 + if (attrs.columnsOptions) {
  497 + template += '<div class="columns-options">' +
  498 + '<a href="" ng-click="$table.reset()"><i class="icon-Reset"></i>Reset</a>' +
  499 + '<a href="" ng-click="$table.configureColumns()"><i class="icon-Receipt-2"></i>Arrange</a>' +
  500 + '</div>';
  501 + }
  502 + template += '<div class="pull-right">' +
  503 + '<pagination class="pagination-sm" style="margin: 0;" total-items="$paging.totalItems" ng-model="$paging.currentPage" items-per-page="$paging.maxPerPage" boundary-links="true" max-size="5"></pagination>' +
  504 + '</div>';
  505 + if (attrs.search) {
  506 + template += '<input type="text" class="form-control pull-right" style="width:300px" placeholder="Search.." ng-model="$filtering.$" />';
  507 + }
  508 + template += '</th></tr></tfoot>'
  509 + return template;
  510 + }
  511 + }
  512 + }
  513 +
  514 + softFilter.$inject = ['$filter'];
  515 + function softFilter($filter) {
  516 + return function (array, filteringConfig) {
  517 + var filterFn = $filter('filter');
  518 + var filtered = filterFn(array, filteringConfig);
  519 + for (var i = 0; i < array.length; i++) {
  520 + var target = array[i];
  521 + if (filtered.indexOf(target) > -1) {
  522 + target.$filtered = true;
  523 + } else {
  524 + target.$filtered = false;
  525 + }
  526 + }
  527 + return array;
  528 + }
  529 + }
  530 +
  531 + function pageGroupFilter() {
  532 + return function (groups, pagingConfig) {
  533 + var newGroups = {};
  534 + var skip = (pagingConfig.currentPage - 1) * pagingConfig.maxPerPage;
  535 + var take = pagingConfig.maxPerPage;
  536 +
  537 + if (angular.isArray(groups)) {
  538 + newGroups.undefined = groups.slice(skip, skip + take);
  539 + } else {
  540 + var groupNames = [];
  541 + for (var key in groups) {
  542 + if (groups.hasOwnProperty(key) && key[0] != '$') {
  543 + groupNames.push(key);
  544 + }
  545 + }
  546 + groupNames.sort();
  547 +
  548 + angular.forEach(groupNames, function (title) {
  549 + var values = groups[title];
  550 + if (take <= 0) {
  551 + return;
  552 + } else if (skip > values.length) {
  553 + skip -= values.length;
  554 + return;
  555 + } else if (skip > 0) {
  556 + values.splice(0, skip);
  557 + skip = 0;
  558 + }
  559 +
  560 + if (take >= values.length) {
  561 + newGroups[title] = values;
  562 + take -= values.length;
  563 + } else {
  564 + newGroups[title] = values.slice(0, take);
  565 + take = 0;
  566 + }
  567 + });
  568 + }
  569 + return newGroups;
  570 + };
  571 + }
  572 +
  573 + function pagingFilter() {
  574 + return function (array, pagingConfig) {
  575 + if (array.length > pagingConfig.minimum) {
  576 +
  577 + } else {
  578 + return array;
  579 + }
  580 + }
  581 + }
  582 +
  583 + function publishFilter() {
  584 + return function (array, target) {
  585 + if (angular.isArray(target)) {
  586 + target.length = 0;
  587 + [].push.apply(target, array);
  588 + }
  589 + return array;
  590 + }
  591 + }
  592 +})(angular);
0 593 \ No newline at end of file
... ...
common_components/filters/utility.filter.js 0 → 100644
... ... @@ -0,0 +1,110 @@
  1 +(function(angular){
  2 + var module = angular.module('framework.filters.utility',[]);
  3 +
  4 + module.filter('RemoveSpaces', removeSpaces);
  5 + module.filter('Truncate', truncate);
  6 + module.filter('AbbrValue', abbrValue);
  7 + module.filter('Countdown', countdown);
  8 +
  9 +
  10 + function removeSpaces() {
  11 + return function (text) {
  12 + return text.replace(/\s/g, '');
  13 + }
  14 + }
  15 +
  16 + function truncate () {
  17 + return function (text, length, end) {
  18 + if (isNaN(length))
  19 + length = 10;
  20 + if (end === undefined)
  21 + end = "...";
  22 + if (text.length <= length || text.length - end.length <= length)
  23 + { return text; }
  24 + else
  25 + { return String(text).substring(0, length - end.length) + end; }
  26 + }
  27 + }
  28 +
  29 + function abbrValue() {
  30 + return function (val) {
  31 +
  32 + return Math.abs(Number(val)) >= 1.0e+9
  33 +
  34 + ? Math.round(Math.abs((Number(val)) / 1.0e+9)*10)/10 + "b"
  35 + // Six Zeroes for Millions
  36 + : Math.abs(Number(val)) >= 1.0e+6
  37 +
  38 + ? Math.round(Math.abs((Number(val)) / 1.0e+6)*10)/10 + "m"
  39 + // Three Zeroes for Thousands
  40 + : Math.abs(Number(val)) >= 1.0e+3
  41 +
  42 + ? Math.round(Math.abs((Number(val)) / 1.0e+3)*10)/10 + "k"
  43 +
  44 + : Math.abs(Number(val));
  45 + }
  46 + }
  47 +
  48 + function countdown() {
  49 + return function (input) {
  50 + var substitute = function (stringOrFunction, number, strings) {
  51 + var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, dateDifference) : stringOrFunction;
  52 + var value = (strings.numbers && strings.numbers[number]) || number;
  53 + return string.replace(/%d/i, value);
  54 + },
  55 + nowTime = (new Date()).getTime(),
  56 + date = (new Date(input)).getTime(),
  57 + allowFuture = true,
  58 + strings = {
  59 + prefixAgo: null,
  60 + prefixFromNow: null,
  61 + suffixAgo: "ago",
  62 + suffixFromNow: "",//"from now"
  63 + seconds: "less than a minute",
  64 + minute: "a minute",
  65 + minutes: "%d minutes",
  66 + hour: "an hour",
  67 + hours: "%d hours",
  68 + day: "a day",
  69 + days: "%d days",
  70 + month: "a month",
  71 + months: "%d months",
  72 + year: "a year",
  73 + years: "%d years"
  74 + },
  75 + dateDifference = nowTime - date,
  76 + words,
  77 + seconds = Math.abs(dateDifference) / 1000,
  78 + minutes = seconds / 60,
  79 + hours = minutes / 60,
  80 + days = hours / 24,
  81 + years = days / 365,
  82 + separator = strings.wordSeparator === undefined ? " " : strings.wordSeparator,
  83 + prefix = strings.prefixAgo,
  84 + suffix = strings.suffixAgo;
  85 +
  86 + if (allowFuture) {
  87 + if (dateDifference < 0) {
  88 + prefix = strings.prefixFromNow;
  89 + suffix = strings.suffixFromNow;
  90 + }
  91 + }
  92 +
  93 + words = seconds < 45 && substitute(strings.seconds, Math.round(seconds), strings) ||
  94 + seconds < 90 && substitute(strings.minute, 1, strings) ||
  95 + minutes < 45 && substitute(strings.minutes, Math.round(minutes), strings) ||
  96 + minutes < 90 && substitute(strings.hour, 1, strings) ||
  97 + hours < 24 && substitute(strings.hours, Math.round(hours), strings) ||
  98 + hours < 42 && substitute(strings.day, 1, strings) ||
  99 + days < 30 && substitute(strings.days, Math.round(days), strings) ||
  100 + days < 45 && substitute(strings.month, 1, strings) ||
  101 + days < 365 && substitute(strings.months, Math.round(days / 30), strings) ||
  102 + years < 1.5 && substitute(strings.year, 1, strings) ||
  103 + substitute(strings.years, Math.round(years), strings);
  104 +
  105 + return $.trim([prefix, words, suffix].join(separator));
  106 + }
  107 + }
  108 +
  109 +
  110 +})(angular);
0 111 \ No newline at end of file
... ...
common_components/layouts/breadcrumb/breadcrumb.controller.js 0 → 100644
... ... @@ -0,0 +1,48 @@
  1 +(function (window, angular) {
  2 + var app = angular.module("framework.UI.breadcrumb", []);
  3 +
  4 + app.controller('BarController', barController);
  5 +
  6 + barController.$inject = ['$scope', '$state', '$modal', '$rootScope'];
  7 + function barController($scope, $state, $modal, $rootScope) {
  8 + $scope.breadcrumbs = [];
  9 + $scope.helpAvailable = null;
  10 + $scope.showHelp = showHelp;
  11 +
  12 + function showHelp() {
  13 + if ($scope.helpAvailable)
  14 + $modal.open({
  15 + templateUrl: $scope.helpAvailable,
  16 + });
  17 + }
  18 +
  19 + $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
  20 + _parseStateName(toState.name);
  21 + $scope.helpAvailable = toState.hasHelp;
  22 + });
  23 +
  24 + $scope.helpAvailable = $state.current.hasHelp;
  25 + _parseStateName($state.current.name);
  26 +
  27 + function _parseStateName(stateName) {
  28 + var parts = stateName.split('.');
  29 + var breadcrumbs = [];
  30 + for (var i = 0; i < parts.length; i++) {
  31 + var p = parts[i];
  32 + if (p === "root")
  33 + continue;
  34 + var full = parts.slice(0, i + 1).join('.');
  35 + breadcrumbs.push({
  36 + name: parts[i],
  37 + $html: _generateBreadcrumbHtml(p, full)
  38 + });
  39 + }
  40 + $scope.breadcrumbs = breadcrumbs;
  41 + }
  42 +
  43 + function _generateBreadcrumbHtml(name, state) {
  44 + var template = '<a ui-sref="' + state + '" >' + name + '</a>';
  45 + return template;
  46 + }
  47 + };
  48 +})(window, angular);
0 49 \ No newline at end of file
... ...
common_components/layouts/breadcrumb/breadcrumb.partial.html 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<div ng-controller="BarController">
  2 + <ol class="breadcrumb">
  3 + <li ng-repeat="item in breadcrumbs" ui-sref-active="active" bind-html-compile="item.$html"></li>
  4 + </ol>
  5 + <span class="module-help" ng-if="helpAvailable">
  6 + <a href="" ng-click="showHelp()"><i class="icon-Assistant"></i>Help</a>
  7 + </span>
  8 +</div>
... ...
common_components/layouts/documentsview/documents-view.controller.js 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +(function (angular) {
  2 + var module = angular.module('framework.documents',[]);
  3 +
  4 + module.controller('DocumentsController', DocumentsController);
  5 +
  6 + DocumentsController.$inject = ['$scope', 'rData', '$state'];
  7 + function DocumentsController($scope, rData, $state) {
  8 + $scope.documents = rData;
  9 + $scope.selectedDocument=rData[0];
  10 + }
  11 +
  12 +})(angular);
0 13 \ No newline at end of file
... ...
common_components/layouts/documentsview/documents-view.partial.html 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +<div class="document-options">
  2 + <a href="" class="btn btn-xs btn-primary"><i class="icon-Add-File"></i> Add New</a>
  3 +</div>
  4 +<div class="row">
  5 + <div class="col-md-4">
  6 + <div class="panel panel-default">
  7 + <div class="panel-heading">
  8 + <h3 class="panel-title">Documents</h3>
  9 + </div>
  10 + <div class="panel-body">
  11 + <div ng-repeat="doc in documents">
  12 + <div class="document" ng-click="$parent.selectedDocument=doc" ng-class="(selectedDocument==doc)?'active':''">
  13 + <small class="pull-right grey">{{doc.ExpiryDate | countdown}}</small>
  14 + <strong>{{doc.Name}}</strong><br />
  15 + {{doc.IdentificationNo}}
  16 + <small class="pull-right grey mt5"><i class="icon-Pencil"></i></small>
  17 + </div>
  18 + </div>
  19 + </div>
  20 + </div>
  21 + </div>
  22 + <div class="col-md-8">
  23 + <div class="panel panel-default">
  24 + <div class="panel-heading">
  25 + <h3 class="panel-title">Viewer</h3>
  26 + </div>
  27 + <div class="panel-body document-viewer">
  28 + <div class="document-viewer-options">
  29 + <a href="" class="btn-icon"><i class="icon-Download"></i>Save</a>
  30 + <a href="" class="btn-icon"><i class="icon-Close-Window"></i>Delete</a>
  31 + </div>
  32 + <img src="{{selectedDocument.DocumentURL}}" width="500px" />
  33 + </div>
  34 + </div>
  35 + </div>
  36 +</div>
0 37 \ No newline at end of file
... ...
common_components/layouts/module/module-base.layout.html 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<div class="module">
  2 + <div ui-view name="options-area"></div>
  3 + <div class="row">
  4 + <div class="col-md-12 col-lg-9 module-primary">
  5 + <div ui-view name="view-area"></div>
  6 + </div>
  7 + <div class="visible-lg col-lg-3 module-secondary">
  8 + <div ui-view name="stats-area"></div>
  9 + </div>
  10 + </div>
  11 +</div>
0 12 \ No newline at end of file
... ...
common_components/layouts/module/module-cards.controller.js 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +(function (angular) {
  2 + var module = angular.module('framework.UI.module',[]);
  3 +
  4 + module.controller('ModuleCardsController', moduleCardsController);
  5 +
  6 + moduleCardsController.$inject = ['$scope', 'rData', 'rNavTo','rAccessors', '$state', '$interval', '$timeout'];
  7 + function moduleCardsController($scope, rData, rNavTo, rAccessors, $state, $interval, $timeout) {
  8 + $scope.rawItems = rData;
  9 + $scope.accessors = rAccessors;
  10 + $scope.rNavTo = rNavTo;
  11 + $scope.showScore = false;
  12 + $scope.chosenGroup = null;
  13 + $scope.cardLimit = 2;
  14 +
  15 + $scope.groups = {};
  16 + rData = rData.sort(function(a,b){
  17 + var valA = rAccessors.Title(a), valB = rAccessors.Title(b);
  18 + if(valA < valB) {
  19 + return -1;
  20 + } else if (valA > valB){
  21 + return 1;
  22 + } else
  23 + return 0;
  24 + });
  25 +
  26 + var i;
  27 +
  28 + for(i = 0; i < rData.length; i++) {
  29 + var title = rAccessors.Group(rData[i]);
  30 + if($scope.groups[title])
  31 + $scope.groups[title].push(rData[i]);
  32 + else
  33 + $scope.groups[title] = [rData[i]];
  34 + }
  35 + }
  36 +
  37 +})(angular);
0 38 \ No newline at end of file
... ...
common_components/layouts/module/module-cards.partial.html 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +<div class="cards-wrapper">
  2 + <div class="cards-view-options">
  3 + <div class="row">
  4 + <div class="col-sm-8 col-xs-8">
  5 + <div class="group-filters">
  6 + <a class="group-options" ng-click="chosenGroup = null" ng-class="(chosenGroup == null)?'active':''">All</a>
  7 + <a class="group-options" ng-click="$parent.chosenGroup = title" ng-class="($parent.chosenGroup == title)?'active':''" ng-repeat="(title, items) in groups">{{title}}({{(items | filter:searchTerm).length }})</a>
  8 + </div>
  9 + </div>
  10 + <div class="col-sm-4 col-xs-4 text-right">
  11 + <a class="btn btn-primary btn-xs" ng-click="showScore=!showScore" ng-bind="(showScore)? 'Hide Scores':'Show Scores'" ng-class="(showScore)?'active':''">Show Scores</a>
  12 + </div>
  13 + </div>
  14 + </div>
  15 + <div ng-repeat="(title, items) in ::groups track by title" ng-if="!chosenGroup || chosenGroup==title">
  16 + <h3 class="section-title" ng-show="results.length > 0">{{title}}</h3>
  17 +
  18 + <card ng-repeat="item in items | filter:searchTerm as results track by item.Id" showscore="{{showScore}}" ></card>
  19 +
  20 + <br class="clearfix" ng-show="results.length > 0" />
  21 + </div>
  22 + <div ng-if="rawItems.length === 0">
  23 + No Items Added Yet.
  24 + </div>
  25 + <br class="clearfix" />
  26 +</div>
0 27 \ No newline at end of file
... ...
common_components/layouts/module/module-item.controller.js 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +(function (angular) {
  2 + var module = angular.module('framework.UI.module');
  3 +
  4 + module.controller('ModuleItemViewController', moduleItemViewController);
  5 +
  6 + moduleItemViewController.$inject = ['$scope', 'rTabs', 'rData', 'rAccessors'];
  7 +
  8 + function moduleItemViewController($scope, rTabs, rData, rAccessors) {
  9 + $scope.item = rData[0];
  10 + $scope.accessors = rAccessors;
  11 + $scope.tabDefs = rTabs;
  12 + }
  13 +
  14 +})(angular);
0 15 \ No newline at end of file
... ...
common_components/layouts/module/module-item.layout.html 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +<div class="module-item">
  2 + <div class="module-item-cover"></div>
  3 + <div class="module-item-nav-bar">
  4 + <card class="module-item-card" allow-click="false" show-score="false" class="single pull-left" show-title="false" show-subtitle="false"></card>
  5 + <div class="module-item-brief">
  6 + <h3>{{accessors.Title(item)}}</h3>
  7 + <h4>{{accessors.SubTitle(item)}}</h4>
  8 + </div>
  9 + <tabs data="tabDefs" type="tabs" justified="false"></tabs>
  10 + </div>
  11 + <div class="module-item-options">
  12 + <div ui-view name="options-area"></div>
  13 + </div>
  14 + <div class="module-item-content">
  15 + <div class="row">
  16 + <div class="col-md-12">
  17 + <ui-view class="tab-content"></ui-view>
  18 + </div>
  19 + </div>
  20 + </div>
  21 +</div>
0 22 \ No newline at end of file
... ...
common_components/layouts/module/module-table.controller.js 0 → 100644
... ... @@ -0,0 +1,22 @@
  1 +(function (angular) {
  2 + var module = angular.module('framework.UI.module');
  3 +
  4 + module.controller('ModuleTableController', moduleCardsController);
  5 +
  6 + moduleCardsController.$inject = ['$scope', 'rData', 'rNavTo', 'rSchema', '$state', '$q'];
  7 + function moduleCardsController($scope, rData, rNavTo, rSchema, $state, $q) {
  8 + $scope.items = rData;
  9 +
  10 + function n() {
  11 + console.log(arguments);
  12 + return $q.when(rData);
  13 + };
  14 +
  15 + $scope.viewItem = viewItem;
  16 + $scope.Schema = rSchema;
  17 + function viewItem(item) {
  18 + $state.go(rNavTo, { Id: 11 });
  19 + }
  20 + }
  21 +
  22 +})(angular);
0 23 \ No newline at end of file
... ...
common_components/layouts/module/module-table.partial.html 0 → 100644
... ... @@ -0,0 +1,17 @@
  1 +<table-for datasource="items" class="table table-condensed table-hover" schema="{{Schema}}" filtering="searchTerm" sorting="" paging="" grouping="grouping" wrapper-class="" table-class="">
  2 + <table-titlebar columns-options="true" paging="true"></table-titlebar>
  3 + <table-heading sorting="true">
  4 + <!--<table-cell column=""></table-cell>--> <!-- for overriding -->
  5 + </table-heading>
  6 + <table-data-rows>
  7 + <table-selection></table-selection>
  8 + <!--<table-cell column=""></table-cell>--> <!-- for overriding -->
  9 + <!--<table-expand-template></table-expand-template>--> <!-- for expanding -->
  10 + <table-actions>
  11 + <a class="btn btn-xs btn-primary" ng-click="viewItem(item)"><i class="icon-D-Eyeglasses2"></i>View</a>
  12 + </table-actions>
  13 + </table-data-rows>
  14 + <table-footer paging="true">
  15 +
  16 + </table-footer>
  17 +</table-for>
0 18 \ No newline at end of file
... ...
common_components/layouts/structure/page.controller.js 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +(function (window, angular) {
  2 + var app = angular.module("framework.UI.structure", []);
  3 +
  4 + app.controller('PageController', pageController);
  5 +
  6 + pageController.$inject = ['$scope', '$interval'];
  7 +
  8 + function pageController($scope, $interval) {
  9 + setupUIElements();
  10 + };
  11 +
  12 +})(window, angular);
0 13 \ No newline at end of file
... ...
common_components/layouts/structure/page.layout.html 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +<div id="application-layout" ng-controller="PageController">
  2 + <div id="sidebar-left" ui-view name="sidebar-left"></div>
  3 + <div id="topbar" ui-view name="topbar"></div>
  4 + <div id="content-wrapper">
  5 + <div id="breadcrumb-bar" ng-include="'/common_components/layouts/breadcrumb/breadcrumb.partial.html'">
  6 +
  7 + </div>
  8 + <div ui-view name="content-wrapper">
  9 +
  10 + </div>
  11 + </div>
  12 + <div id="sidebar-toggle">
  13 + <i class="icon-Align-JustifyAll"></i>
  14 + </div>
  15 + <div id="sidebar-right" ui-view name="sidebar-right"></div>
  16 +</div>
0 17 \ No newline at end of file
... ...
common_components/layouts/table-page/table-page.layout.html 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +<div class="container-fluid tablepage-layout">
  2 + <div class="row">
  3 + <div class="col-md-12">
  4 + <div ui-view name="table-options"></div>
  5 + </div>
  6 + </div>
  7 + <div class="row">
  8 + <div class="col-md-12">
  9 + <div ui-view name="table-content"></div>
  10 + </div>
  11 + </div>
  12 +</div>
... ...
common_components/services/ConsoleLoggingProvider.service.js 0 → 100644
... ... @@ -0,0 +1,67 @@
  1 +(function () {
  2 + var app = angular.module('framework.services.logging');
  3 +
  4 + app.service('ConsoleLoggingProvider', ['$window', consoleLoggingProvider]);
  5 +
  6 + function consoleLoggingProvider($window) {
  7 + this.log = log;
  8 + this.debug = debug;
  9 + this.info = info;
  10 + this.warning = warning;
  11 + this.error = error;
  12 +
  13 + function log(level, catagory, msg, others) {
  14 + switch (level) {
  15 + case 1:
  16 + debug(catagory, msg, others);
  17 + break;
  18 + case 2:
  19 + info(catagory, msg, others);
  20 + break;
  21 + case 3:
  22 + warning(catagory, msg, others);
  23 + break;
  24 + case 4:
  25 + error(catagory, msg, exception, others);
  26 + break;
  27 + default:
  28 + debug(catagory, msg, others);
  29 + break;
  30 + }
  31 + }
  32 +
  33 + function debug(catagory, msg, others) {
  34 + var date = new Date();
  35 + console.debug(date.toISOString() + ' - ' + catagory.toUpperCase() + ' - ' + msg);
  36 + angular.forEach(others, function (val, ind) {
  37 + console.debug('additional info ' + ind + ': ', val);
  38 + });
  39 + }
  40 +
  41 + function info(catagory, msg, others) {
  42 + var date = new Date();
  43 + console.info(date.toISOString() + ' - ' + catagory.toUpperCase() + ' - ' + msg);
  44 + angular.forEach(others, function (val, ind) {
  45 + console.info('additional info ' + ind + ': ', val);
  46 + });
  47 + }
  48 +
  49 + function warning(catagory, msg, others) {
  50 + var date = new Date();
  51 + console.warn(date.toISOString() + ' - ' + catagory.toUpperCase() + ' - ' + msg);
  52 + angular.forEach(others, function (val, ind) {
  53 + console.warn('additional info ' + ind + ': ', val);
  54 + });
  55 + }
  56 +
  57 + function error(catagory, msg, exception, others) {
  58 + var date = new Date();
  59 + console.error(date.toISOString() + ' - ' + catagory.toUpperCase() + ' - ' + msg);
  60 + console.error('original exception: ' + exception);
  61 + angular.forEach(others, function (val, ind) {
  62 + console.error('additional info ' + ind + ': ', val);
  63 + });
  64 + }
  65 + }
  66 +
  67 +})();
0 68 \ No newline at end of file
... ...
common_components/services/Logger.service.js 0 → 100644
... ... @@ -0,0 +1,139 @@
  1 +(function (window, angular) {
  2 + var app = angular.module("framework.services.logging", []);
  3 +
  4 + app.provider('Logger', function () {
  5 + var providers = [];
  6 + var hiddenCatagorys = [];
  7 + var loggingLevel = 1;
  8 +
  9 + this.DEBUG = 1;
  10 + this.INFO = 2;
  11 + this.WARNING = 3;
  12 + this.ERROR = 4;
  13 +
  14 + this.setGlobalLoggingLevel = setGlobalLoggingLevel;
  15 + this.addProvider = addProvider;
  16 + this.hideCatagory = hideCatagory;
  17 +
  18 + function setGlobalLoggingLevel(level) {
  19 + loggingLevel = level;
  20 + }
  21 +
  22 + function addProvider(provider) {
  23 + providers.push(provider);
  24 + }
  25 +
  26 + function hideCatagory(catagory) {
  27 + hiddenCatagorys.push(catagory);
  28 + }
  29 +
  30 + this.$get = buildLoggerService;
  31 +
  32 + buildLoggerService.$inject = ['$injector'];
  33 + function buildLoggerService($injector) {
  34 + var instance = createLogger('Default');
  35 + this.createLogger = createLogger;
  36 + this.log = instance.log;
  37 + this.debug = instance.debug;
  38 + this.info = instance.info;
  39 + this.warning = instance.warning;
  40 + this.error = instance.error;
  41 + this.DEBUG = 1;
  42 + this.INFO = 2;
  43 + this.WARNING = 3;
  44 + this.ERROR = 4;
  45 + var concreteProviders = [];
  46 + angular.forEach(providers, function (value) {
  47 + if (angular.isObject(value)) {
  48 + concreteProviders.push(value);
  49 + } else if (angular.isFunction(value)) {
  50 + var pro = {
  51 + log: value,
  52 + debug: log.bind(null, 1),
  53 + info: log.bind(null, 2),
  54 + warning: log.bind(null, 3),
  55 + error: log.bind(null, 4)
  56 + };
  57 + concreteProviders.push(pro);
  58 + } else if (angular.isArray(value)) {
  59 + var pro = $injector.invoke(value, null, {
  60 + Levels: {
  61 + DEBUG: 1,
  62 + INFO: 2,
  63 + WARNING: 3,
  64 + ERROR: 4
  65 + }
  66 + });
  67 + concreteProviders.push(pro);
  68 + } else if (typeof value === 'string') {
  69 + var pro = $injector.get(value);
  70 + concreteProviders.push(pro);
  71 + }
  72 + });
  73 +
  74 + function createLogger(catagory) {
  75 + return new Logger(catagory, concreteProviders, loggingLevel, hiddenCatagorys);
  76 + }
  77 +
  78 + return this;
  79 + }
  80 + });
  81 +
  82 + function Logger(catagory, providers, loggingLevel, hiddenCatagorys) {
  83 + this.log = log;
  84 + this.debug = debug;
  85 + this.info = info;
  86 + this.warning = warning;
  87 + this.error = error;
  88 +
  89 + function log(level, msg) {
  90 + if (level < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
  91 + return;
  92 +
  93 + var args = Array.prototype.slice.call(arguments, 2);
  94 + angular.forEach(providers, function (provider) {
  95 + provider.log(level, catagory, msg, args);
  96 + });
  97 + }
  98 +
  99 + function debug(msg) {
  100 + if (1 < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
  101 + return;
  102 +
  103 + var args = Array.prototype.slice.call(arguments, 1);
  104 + angular.forEach(providers, function (provider) {
  105 + provider.debug(catagory, msg, args);
  106 + });
  107 + }
  108 +
  109 + function info(msg) {
  110 + if (2 < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
  111 + return;
  112 +
  113 + var args = Array.prototype.slice.call(arguments, 1);
  114 + angular.forEach(providers, function (provider) {
  115 + provider.info(catagory, msg, args);
  116 + });
  117 + }
  118 +
  119 + function warning(msg) {
  120 + if (3 < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
  121 + return;
  122 +
  123 + var args = Array.prototype.slice.call(arguments, 1);
  124 + angular.forEach(providers, function (provider) {
  125 + provider.warning(catagory, msg, args);
  126 + });
  127 + }
  128 +
  129 + function error(msg, exception) {
  130 + if (4 < loggingLevel || hiddenCatagorys.indexOf(catagory) != -1)
  131 + return;
  132 +
  133 + var args = Array.prototype.slice.call(arguments, 2);
  134 + angular.forEach(providers, function (provider) {
  135 + provider.error(catagory, msg, exception, args);
  136 + });
  137 + }
  138 + }
  139 +})(window, angular);
0 140 \ No newline at end of file
... ...
common_components/services/Menu.service.js 0 → 100644
... ... @@ -0,0 +1,45 @@
  1 +(function (window, angular) {
  2 + var app = angular.module("framework.services.menu", []);
  3 +
  4 + app.provider('MenuService', MenuServiceProvider);
  5 +
  6 + function MenuServiceProvider() {
  7 + var menuItems = [];
  8 + var defaultIcon = 'icon-Align-JustifyAll';
  9 + var provider = {
  10 + registerMenuItem: registerMenuItem,
  11 + setDefaultIcon: setDefaultIcon,
  12 + $get: getService
  13 + };
  14 +
  15 + return provider;
  16 +
  17 + function registerMenuItem(menuItem) {
  18 + menuItems.push(menuItem);
  19 + }
  20 +
  21 + function setDefaultIcon(icon) {
  22 + defaultIcon = icon;
  23 + }
  24 +
  25 + function getService() {
  26 + angular.forEach(menuItems, function (mi) {
  27 + mi.$html = _buildTemplate(mi);
  28 + });
  29 + var service = {
  30 + menuItems: menuItems,
  31 + defaultIcon: defaultIcon
  32 + };
  33 + return service;
  34 + }
  35 +
  36 + function _buildTemplate(menuItem) {
  37 + var template = '<a ui-sref="' + menuItem.state + '" style="color:white;">' +
  38 + '<i class="{{item.icon ? item.icon : menu.defaultIcon}}"></i>' +
  39 + '{{item.title}}' +
  40 + '</a>';
  41 + return template;
  42 + }
  43 +
  44 + };
  45 +})(window, angular);
0 46 \ No newline at end of file
... ...
common_components/services/Model.service.js 0 → 100644
... ... @@ -0,0 +1,300 @@
  1 +(function (window, angular) {
  2 + var module = angular.module('framework.services.model', []);
  3 +
  4 + module.provider('Model', modelsProvider);
  5 + //module.service('DataContext', dataService);
  6 +
  7 + function modelsProvider() {
  8 + var schemas = {
  9 +
  10 + };
  11 +
  12 + this.registerSchema = registerSchema;
  13 + this.$get = buildService;
  14 +
  15 + function registerSchema(name, path, schema) {
  16 + schemas[name] = [path, schema];
  17 + return this;
  18 + }
  19 +
  20 + buildService.$inject = ['$config', '$http', '$q'];
  21 + function buildService($config, $http, $q) {
  22 + var models = {};
  23 +
  24 + angular.forEach(schemas, function (value, key) {
  25 + var modelName = key;
  26 + var modelPath = value[0];
  27 + var modelSchema = value[1];
  28 +
  29 + models[modelName] = BuildConstructor(modelPath, modelSchema);
  30 + });
  31 +
  32 + return models;
  33 +
  34 + function Entity(apiEndpoint, schema, values) {
  35 + this.$$apiLocation = apiEndpoint;
  36 + this.$$schema = schema;
  37 + this.$$originalValues = {};
  38 + this.$$changedValues = {};
  39 + this.$$foreignModels = {};
  40 +
  41 + this.$getChanges = getChanges;
  42 + this.$validate = validateEntity;
  43 + this.$save = saveEntity;
  44 + this.$update = updateEntity;
  45 + this.$patch = patchEntity;
  46 + this.$delete = deleteEntity;
  47 + this.$commit = commitEntity;
  48 + this.$rollback = rollbackEntity;
  49 +
  50 + angular.forEach(schema, function (value, key) {
  51 + var fieldName = key,
  52 + fieldDef = value;
  53 +
  54 + if (values[fieldName] !== undefined) {
  55 + if (typeof fieldDef.type === 'string' && values[fieldName] !== null) {
  56 + this.$$originalValues[fieldName] = new models[fieldDef.type](values[fieldName]);
  57 + this.$$foreignModels[fieldName] = fieldDef.type;
  58 + } else {
  59 + this.$$originalValues[fieldName] = values[fieldName];
  60 + }
  61 + } else {
  62 + this.$$originalValues[fieldName] = fieldDef.defaultValue;
  63 + }
  64 +
  65 + Object.defineProperty(this, fieldName, {
  66 + configurable: false,
  67 + enumerable: true,
  68 + get: fieldDef.calculated || function () {
  69 + return this.$$changedValues.hasOwnProperty(fieldName) ?
  70 + this.$$changedValues[fieldName] : this.$$originalValues[fieldName];
  71 + },
  72 + set: function (value) {
  73 + if (angular.isUndefined(fieldDef.editable) || fieldDef.editable) {
  74 +
  75 + this.$$changedValues[fieldName] = value; //CHECK: Cast to correct type??
  76 + }
  77 + }
  78 + });
  79 + }, this);
  80 +
  81 + function getChanges() {
  82 + return this.$$changedValues;
  83 + }
  84 + function validateEntity() {
  85 + } //TODO
  86 + function saveEntity() {
  87 + return $http.post($config.API_ENDPOINT + this.$$apiLocation, this);
  88 + }
  89 + function updateEntity() {
  90 + var config = {
  91 + params: {
  92 + id: this.Id
  93 + }
  94 + };
  95 + return $http.put($config.API_ENDPOINT + this.$$apiLocation, this, config);
  96 + }
  97 + function patchEntity(skipSubObjects) {
  98 + var self = this;
  99 + var delta = angular.extend({ Id: this.Id }, this.$getChanges());
  100 + var config = {
  101 + params: {
  102 + id: this.Id
  103 + }
  104 + };
  105 + var request = $http.patch($config.API_ENDPOINT + this.$$apiLocation, delta, config);
  106 + request.success(function () {
  107 + self.$commit(true);
  108 + });
  109 + return request;
  110 + }
  111 + function deleteEntity() {
  112 + var config = {
  113 + params: {
  114 + id: this.Id
  115 + }
  116 + };
  117 + return $http.delete($config.API_ENDPOINT + this.$$apiLocation, config);
  118 + }
  119 + function commitEntity(skipSubObjects) {
  120 + angular.forEach(this.$$changedValues, function (value, key) {
  121 + var fieldName = key,
  122 + fieldValue = value;
  123 + this.$$originalValues[fieldName] = fieldValue
  124 + }, this);
  125 + this.$$changedValues = {};
  126 +
  127 + if (skipSubObjects) return;
  128 + angular.forEach(this.$$foreignModels, function (value, key) {
  129 + this[key].$commit();
  130 + }, this);
  131 + }
  132 + function rollbackEntity(skipSubObjects) {
  133 + this.$$changedValues = {};
  134 +
  135 + if (skipSubObjects) return;
  136 + angular.forEach(this.$$foreignModels, function (value, key) {
  137 + this[key].$rollback();
  138 + }, this);
  139 + }
  140 + }
  141 +
  142 + function Query(apiEndpoint, ctor) {
  143 + var includes = [];
  144 + var selects = [];
  145 + var filters = [];
  146 + var order = [];
  147 + var modelCtor = ctor;
  148 + this.include = include;
  149 + this.select = select;
  150 + this.filter = filter;
  151 + this.where = filter;
  152 + this.parseAs = parseAs;
  153 + this.execute = execute;
  154 + this.orderBy = orderBy;
  155 +
  156 + function parseAs(parser) {
  157 + modelCtor = parser;
  158 + }
  159 +
  160 + function include() {
  161 + if (arguments.length === 2) {
  162 + if (angular.isObject(arguments[1])) {
  163 + //TODO: Process subquery
  164 + } else if (angular.isFunction(arguments[1])) {
  165 + //TODO: Process subquery
  166 + }
  167 + } else {
  168 + [].push.apply(includes, arguments);
  169 + }
  170 + return this;
  171 + }
  172 +
  173 + function orderBy(prop) {
  174 + this.order.push(prop);
  175 + //todo add dir and aliases
  176 + }
  177 +
  178 + function select() {
  179 + [].push.apply(selects, arguments);
  180 + modelCtor = null;
  181 + return this;
  182 + }
  183 +
  184 + function filter(key, op, value) {
  185 + if (arguments.length === 1) {
  186 + filters.push(key);
  187 + } else if (arguments.length === 3) {
  188 + switch (op) {
  189 + case '&&':
  190 + op = 'and';
  191 + break;
  192 + case '||':
  193 + op = 'or';
  194 + break;
  195 + case '=':
  196 + case '==':
  197 + case '===':
  198 + op = 'eq';
  199 + break;
  200 + case '!=':
  201 + case '!==':
  202 + case '!===':
  203 + op = 'ne';
  204 + break;
  205 + case '>':
  206 + op = 'gt';
  207 + break;
  208 + case '>=':
  209 + op = 'ge';
  210 + break;
  211 + case '<':
  212 + op = 'lt';
  213 + break;
  214 + case '<=':
  215 + op = 'le';
  216 + break;
  217 + default:
  218 + break;
  219 + }
  220 + filters.push(key + ' ' + op + ' ' + value);
  221 + }
  222 + return this;
  223 + }
  224 +
  225 + function execute() {
  226 + var config = {};
  227 + config.params = _buildParams();
  228 + apiCall = $config.API_ENDPOINT + apiEndpoint;
  229 + return $http.get(apiCall, config).then(function (response) {
  230 + if (!modelCtor) {
  231 + return response.data;
  232 + } else {
  233 + if (angular.isArray(response.data)) {
  234 + var results = [];
  235 + angular.forEach(response.data, function (d) {
  236 + results.push(new modelCtor(d));
  237 + });
  238 + return results;
  239 + } else {
  240 + return new modelCtor(response.data);
  241 + }
  242 + }
  243 + });
  244 + }
  245 +
  246 + function _buildParams() {
  247 + var params = {};
  248 + if (includes.length) {
  249 + params.$expand = includes.join(',');
  250 + }
  251 + if (filters.length) {
  252 + params.$filter = filters.join(' and ');
  253 + }
  254 + if (selects.length) {
  255 + params.$select = selects.join(',');
  256 + }
  257 + return params;
  258 + }
  259 + }
  260 +
  261 + function ModelCache() {
  262 +
  263 + }
  264 +
  265 + function BuildConstructor(apiEndpoint, schema) {
  266 + function ModelCtor(values) {
  267 + var values = values || {};
  268 +
  269 + this.base = Entity;
  270 + this.base(apiEndpoint, schema, values);
  271 + }
  272 +
  273 + ModelCtor.prototype = new Entity;
  274 + ModelCtor.$schema = schema;
  275 + ModelCtor.query = query;
  276 + ModelCtor.getById = getById;
  277 +
  278 + var passon = ModelCtor;
  279 +
  280 + function query() {
  281 + return new Query(apiEndpoint, passon);
  282 + }
  283 +
  284 + function getById(id) {
  285 + return new Query(apiEndpoint)
  286 + .filter('Id', 'eq', id);
  287 + }
  288 +
  289 + return ModelCtor;
  290 + }
  291 + }
  292 + }
  293 +
  294 + dataService.$inject = ['$config', '$http', '$q', 'Models'];
  295 + function dataService($config, $http, $q, Models) {
  296 + var entityIdMaps = {};
  297 + var entityArrays = {};
  298 +
  299 + }
  300 +})(window, angular);
0 301 \ No newline at end of file
... ...
common_components/services/Storage.service.js 0 → 100644
... ... @@ -0,0 +1,73 @@
  1 +(function () {
  2 + var module = angular.module('framework.services.storage', [])
  3 +
  4 + module.factory('$localStorage', _storageFactory('localStorage'));
  5 + module.factory('$sessionStorage', _storageFactory('sessionStorage'));
  6 +
  7 + function _storageFactory(storageType) {
  8 + return ['$rootScope', '$window', '$log',
  9 + function ($rootScope, $window, $log) {
  10 + // #9: Assign a placeholder object if Web Storage is unavailable to prevent breaking the entire AngularJS app
  11 + var webStorage = $window[storageType] || ($log.warn('This browser does not support Web Storage!'), {}),
  12 + $storage = {
  13 + $default: function (items) {
  14 + for (var k in items) {
  15 + angular.isDefined($storage[k]) || ($storage[k] = items[k]);
  16 + }
  17 +
  18 + return $storage;
  19 + },
  20 + $reset: function (items) {
  21 + for (var k in $storage) {
  22 + '$' === k[0] || delete $storage[k];
  23 + }
  24 +
  25 + return $storage.$default(items);
  26 + }
  27 + },
  28 + _last$storage,
  29 + _debounce;
  30 +
  31 + for (var i = 0, k; i < webStorage.length; i++) {
  32 + // #8, #10: `webStorage.key(i)` may be an empty string (or throw an exception in IE9 if `webStorage` is empty)
  33 + (k = webStorage.key(i)) && 'ngStorage-' === k.slice(0, 10) && ($storage[k.slice(10)] = angular.fromJson(webStorage.getItem(k)));
  34 + }
  35 +
  36 + _last$storage = angular.copy($storage);
  37 +
  38 + $rootScope.$watch(function () {
  39 + _debounce || (_debounce = setTimeout(function () {
  40 + _debounce = null;
  41 +
  42 + if (!angular.equals($storage, _last$storage)) {
  43 + angular.forEach($storage, function (v, k) {
  44 + angular.isDefined(v) && '$' !== k[0] && webStorage.setItem('ngStorage-' + k, angular.toJson(v));
  45 +
  46 + delete _last$storage[k];
  47 + });
  48 +
  49 + for (var k in _last$storage) {
  50 + webStorage.removeItem('ngStorage-' + k);
  51 + }
  52 +
  53 + _last$storage = angular.copy($storage);
  54 + }
  55 + }, 100));
  56 + });
  57 +
  58 + // #6: Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
  59 + 'localStorage' === storageType && $window.addEventListener && $window.addEventListener('storage', function (event) {
  60 + if ('ngStorage-' === event.key.slice(0, 10)) {
  61 + event.newValue ? $storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete $storage[event.key.slice(10)];
  62 +
  63 + _last$storage = angular.copy($storage);
  64 +
  65 + $rootScope.$apply();
  66 + }
  67 + });
  68 +
  69 + return $storage;
  70 + }
  71 + ];
  72 + }
  73 +})()
0 74 \ No newline at end of file
... ...
common_components/tables/Table.controller.js 0 → 100644
... ... @@ -0,0 +1,157 @@
  1 +(function (angular) {
  2 + var module = angular.module('framework.table', []);
  3 +
  4 + var table_defaults = {
  5 + table: 'table table-bordered'
  6 + };
  7 +
  8 + /*
  9 + * Defines Table defaults and settings
  10 + */
  11 + module.constant("$table_config", table_defaults);
  12 +
  13 + /*
  14 + * Injectable Base Class for Inhertence
  15 + * Functions:
  16 + * Lays out rows / columns
  17 + * Sets heading title
  18 + * Parses cell values
  19 + * Column Sizing
  20 + */
  21 + module.service('BasicTableControllerBase', function () {
  22 + return BasicTableControllerBase;
  23 + });
  24 +
  25 + /*
  26 + * Injectable Base Class for Inhertence
  27 + * Functions:
  28 + * As Basic Table Plus:
  29 + * Sortable Columns
  30 + */
  31 + module.service('SortableTableControllerBase', function () {
  32 + return SortableTableControllerBase;
  33 + });
  34 +
  35 + /*
  36 + * Default Implementations
  37 + */
  38 + module.controller('BasicTableController', basicTableController);
  39 + module.controller('SortableTableController', sortableTableController);
  40 +
  41 + //#region BaseClasses
  42 +
  43 + function BasicTableControllerBase($scope, columns, data) {
  44 + var $injector = angular.element('*[ng-app]').injector();
  45 + var $parse = $injector.get('$parse');
  46 +
  47 + $scope.rows = data.data;
  48 + $scope.columns = columns;
  49 + $scope.getValue = getValue;
  50 +
  51 + var _totalWidths = columns.reduce(function (prev, current) {
  52 + return prev += (current.width || '*').length;
  53 + }, 0);
  54 +
  55 + angular.forEach(columns, function (value) {
  56 + value.$$parser = angular.isFunction(value.mapping) ? value.mapping : $parse(value.mapping || value.name);
  57 + value.$$width = 100.00 / _totalWidths * (value.width || '*').length + '%';
  58 + });
  59 +
  60 + function getValue(row, col) {
  61 + return col.$$parser(row);
  62 + }
  63 + }
  64 +
  65 + function SortableTableControllerBase($scope, columns, data) {
  66 + this.base = BasicTableControllerBase;
  67 + this.base($scope, columns, data);
  68 +
  69 + $scope.currentSort = {
  70 + value: '',
  71 + column: null,
  72 + reverse: false
  73 + };
  74 + $scope.currentSearch = searchFunction;
  75 + $scope.sortBy = sort;
  76 + $scope.searchTerm = null;
  77 +
  78 + function sort(column, $event) {
  79 + var newSort = {
  80 + value: '',
  81 + column: null
  82 + };
  83 +
  84 + if (angular.isObject(column)) {
  85 + newSort.column = column;
  86 + newSort.value = column.$$parser;
  87 + } else if (typeof column === "string") {
  88 + angular.forEach($scope.columns, function (colDef) {
  89 + if ((colDef.mapping || colDef.name) === column) {
  90 + newSort.column = colDef;
  91 + }
  92 + });
  93 + newSort.value = column;
  94 + } else if (angular.isFunction(column)) {
  95 + newSort.value = column;
  96 + newSort.column = null;
  97 + }
  98 +
  99 + if ($scope.currentSort.value === newSort.value) {
  100 + $scope.currentSort.reverse = !$scope.currentSort.reverse;
  101 + } else {
  102 + $scope.currentSort.reverse = false;
  103 + }
  104 +
  105 + $scope.currentSort.value = newSort.value;
  106 + $scope.currentSort.column = newSort.column;
  107 + };
  108 +
  109 + function searchFunction(row) {
  110 + if (!$scope.searchTerm) {
  111 + return true;
  112 + }
  113 + var found = false;
  114 + for (var i = 0; i < columns.length; i++) {
  115 + var column = $scope.columns[i];
  116 + if (angular.isUndefined(column.searchable) || column.searchable) {
  117 + var value = column.$$parser(row);
  118 + if (angular.isFunction(column.comparator)) {
  119 + found = column.comparator(value, $scope.searchTerm);
  120 + } else if (typeof value === 'string') {
  121 + found = value.toLowerCase().indexOf($scope.searchTerm.toLowerCase()) >= 0;
  122 + }
  123 + }
  124 + if (found) {
  125 + break;
  126 + }
  127 + }
  128 + return found;
  129 + };
  130 + }
  131 + SortableTableControllerBase.prototype = BasicTableControllerBase;
  132 +
  133 + //#endregion
  134 +
  135 + //#region Default Implementations
  136 +
  137 + basicTableController.$inject = ['$scope', 'classes', 'columns', 'data'];
  138 + function basicTableController($scope, classes, columns, data) {
  139 + this.base = BasicTableControllerBase;
  140 + this.base($scope, columns, data);
  141 +
  142 + $scope.classes = angular.extend({}, table_defaults, classes);
  143 + }
  144 + basicTableController.prototype = BasicTableControllerBase;
  145 +
  146 +
  147 + sortableTableController.$inject = ['$scope', 'classes', 'columns', 'data'];
  148 + function sortableTableController($scope, classes, columns, data) {
  149 + this.base = SortableTableControllerBase;
  150 + this.base($scope, columns, data);
  151 +
  152 + $scope.classes = angular.extend({}, table_defaults, classes);
  153 + }
  154 + sortableTableController.prototype = SortableTableControllerBase;
  155 +
  156 + //#endregion
  157 +})(angular);
0 158 \ No newline at end of file
... ...
common_components/tables/basic-table.layout.html 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +<div class="basic-table">
  2 + <table ng-class="classes.table">
  3 + <thead ng-class="classes.head">
  4 + <tr>
  5 + <th ng-repeat="col in columns" width="{{col.$$width}}" ng-class="col.classes.heading">
  6 + {{ col.name }}
  7 + </th>
  8 + </tr>
  9 + </thead>
  10 + <tbody ng-class="classes.body">
  11 + <tr ng-repeat="row in rows">
  12 + <td ng-repeat="col in columns">
  13 + {{ ::getValue(row, col) }}
  14 + </td>
  15 + </tr>
  16 + </tbody>
  17 + </table>
  18 +</div>
... ...
common_components/tables/sortable-table.layout.html 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +<div class="basic-table">
  2 + <input type="text" ng-model="searchTerm" />
  3 + <table ng-class="classes.table">
  4 + <thead>
  5 + <tr>
  6 + <th ng-repeat="col in columns" width="{{::col.$$width}}">
  7 + {{ ::col.name }}
  8 + <i class="fa" ng-class="currentSort.column === col ? (currentSort.reverse ? 'fa-sort-amount-desc' : 'fa-sort-amount-asc') : 'fa-sort-amount-asc text-muted' " ng-click="sortBy(col, $event)"></i>
  9 + </th>
  10 + </tr>
  11 + </thead>
  12 + <tbody>
  13 + <tr ng-repeat="row in rows | filter:currentSearch | orderBy:currentSort.value:currentSort.reverse">
  14 + <td ng-repeat="col in columns">
  15 + {{ ::getValue(row, col) }}
  16 + </td>
  17 + </tr>
  18 + </tbody>
  19 + </table>
  20 +</div>
... ...