Commit 6e6aa9b005ee7636f1e7f02fc4981c1a9791d483
1 parent
67e94bc0
Exists in
master
Basic Setup
Showing
29 changed files
with
2333 additions
and
0 deletions
Show diff stats
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | + ' {{column.title}}' + | |
300 | + ' <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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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> | ... | ... |