1angular.module('DataUI', ["ApiService", "MessagesUI","rzModule"]); 2 3/*Templates*/ 4angular.module("DataUI").run(["$templateCache",function ($templateCache) { 5 /*common*/ 6 $templateCache.put('uiApiToolbar.html', '<div class="clearfix"> <div class="pull-right"><div ui-include="uiApiToolbarCommands.html"></div></div> <div class="pull-right" style="margin-right:15px;" ng-show="inProgress">{{inProgressOperation}}</div></div> <div class="progress-bar progress-bar-info progress-bar-striped progress-bar-auto-advance" style="width: 100%; height:3px; margin-top:-3px;" ng-show="inProgress"></div>'); 7 $templateCache.put('uiApiToolbarCommands.html', '<div ui-include="uiApiCommand_Add.html"></div>'); 8 9 /*Common commands*/ 10 $templateCache.put('uiApiCommand_Add.html', '<button type="button" class="btn btn-default btn-sm" ng-click="new()"><i class="fas fa-plus"></i> Add</button>'); 11 $templateCache.put('uiApiCommand_Edit.html', '<button type="button" class="btn btn-default btn-sm" ng-click="sendCommand(\'EditRow\', row)"><i class="fas fa-pencil"></i> Edit</button>'); 12 13 /*List Templates*/ 14 $templateCache.put('uiApiListFull.html', '<div class="list-group"> <div class="list-group-item clearfix" ui-include="uiApiToolbar.html"></div> <a href="#" class="list-group-item" ng-repeat="row in $data" ng-click="select(row)">{{row[titleField]}}</a> </div>'); 15 $templateCache.put('uiApiList.html', '<div class="list-group"> <a href="#" class="list-group-item" ng-repeat="row in $data" ng-click="select(row)">{{row[titleField]}}</a> </div>'); 16 17 18 /*Side Nav Lists*/ 19 $templateCache.put('uiApiNavListFull.html', '<div class="clearfix container-fluid" ui-include="uiApiToolbar.html"></div><ul><li ng-repeat="row in $data" ng-click="select(row)" ng-class="{\'active\': row==selectedRow}" data-toggle="collapse" data-target=".side-nav"><a href="#">{{row[titleField]}}</a></li></ul>'); 20 $templateCache.put('uiApiNavList.html', '<ul><li ng-repeat="row in $data" ng-click="select(row)"><a href="#" ng-class="{\'active\': row==selectedRow}">{{row[titleField]}}</a></li></ul>'); 21 22 /*Table base templates*/ 23 $templateCache.put('uiApiTableFull.html', '<div> <div ui-include="uiApiTableHeader.html"></div> <div ui-include="uiApiToolbar.html"></div> <div ui-include="uiApiTable.html"></div> <div ui-include="uiApiTableFooter.html"></div> </div>'); 24 $templateCache.put('uiApiTableHeader.html', ''); 25 $templateCache.put('uiApiTableFooter.html', ''); 26 $templateCache.put('uiApiTable.html', '<table class="table table-hover" ng-table-dynamic="tableParams with columns" show-filter="!listSettings.hideFilters"><tr ng-repeat="row in $data track by row.Id" ng-click="select(row)" ui-include="uiApiTableCell.html"></tr></table>'); 27 $templateCache.put('uiApiTableRow.html', '<tr ng-repeat="row in $data track by row.Id" ng-click="select(row)" ui-include="uiApiTableCell.html"></tr>'); 28 $templateCache.put('uiApiTableCell.html', '<td ng-repeat="column in $columns" class="{{column.class()}}" ui-include="uiApiTableCellValue.html"></td>'); 29 $templateCache.put('uiApiTableCellValue.html', '<span ui-display-field="column" data="row"></span>'); 30 31 $templateCache.put('uiApiTableTools.html', '<div class="dropdown"><button class="btn btn-default btn-sm dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"><i class="fas fa-elipsis-v"></i></button><ul class="dropdown-menu dropdown-menu-right "><li><a href="#" ng-click="toggleFilters()"><i class="fas fa-filter"></i> Show / hide filters</a></li></ul></div>'); 32 33 /*pagination*/ 34 $templateCache.put('uiTablePagination.html', ['' + 35 '<div class="btn-group btn-group-sm" role="group" aria-label="Page"> ' + 36 '<button type="button" class="btn btn-default" aria-label="Previous" ng-if="pagination.showPrev" ng-click="ChangePage(pagination.page - 1)"><span aria-hidden="true">«</span></button>'+ 37 '<button type="button" class="btn btn-default" aria-label="First" ng-if="pagination.showFirst" ng-click="ChangePage(pagination.firstPage)">{{(pagination.firstPage + 1)}}</li>'+ 38 '<button type="button" class="btn btn-default" ng-repeat="page in pagination.middlePages" ng-click="ChangePage(page)" ng-class="{\'btn-primary\': pagination.page == page}">{{(page + 1)}}</button>'+ 39 '<button type="button" class="btn btn-default" aria-label="Last" ng-if="pagination.showLast" ng-click="ChangePage(pagination.lastPage)">{{(pagination.lastPage + 1)}}</button>'+ 40 '<button type="button" class="btn btn-default" aria-label="Next" ng-if="pagination.showNext" ng-click="ChangePage(pagination.page + 1)"><span aria-hidden="true">»</span></button>'+ 41 '</div >'+ 42 ''].join(''));
43 44 $templateCache.put('uiTablePaginationSize.html', ['' + 45 '<div class="btn-group btn-group-sm right" role="group" aria-label="Page navigation size"> ' + 46 '<button type="button" class="btn btn-default" ng-repeat="size in pagination.pagesizes" ng-class="{\'btn-primary\': pagination.pagesize == size}" ng-click="Changepagesize(size)">{{size}}</button>' + 47 '</div >'].join('')); 48 49 /*Pivot Templates*/ 50 $templateCache.put('uiPivot.html', [ 51 '<div>', 52 ' <div class="row" style="border-bottom: 1px solid #CCC;">', 53 ' <div class="col-xs-12 col-sm-2"></div>', 54 ' <div ng-repeat="(colKey, col) in pivotColumns track by $index" class="{{::colCssClass}} text-center">', 55 ' <h4>{{colKey}}</h4>', 56 ' </div>', 57 ' </div>', 58 ' <div ng-repeat="(rowKey, d1) in pivotData track by $index" class="row" style="border-bottom: 1px solid #CCC;">', 59 ' <div class="col-xs-12 col-sm-2 text-center"><h4>{{rowKey}}</h4></div>', 60 ' <div ng-repeat="(colKey, row) in d1 track by $index" class="{{::colCssClass}} pivotwidgetPlaceholder">', 61 ' <div ng-transclude ng-show="row.hasValue"></div>', 62 ' </div>', 63 ' </div>', 64 '</div>' 65 ].join('')); 66 67 $templateCache.put('uiNestedList.html', [ 68 '<div class="list-group-item" ng-click="parentScope.toggle(row)" ng-class="{\'active\':parentScope.isSelected(row)}">', 69 ' {{row.Label || row.Name || row.Title }}', 70 '</div>', 71 '<ul ng-if="row.children.length>0" class="children collapse" id="x-{{row._UniqueId || row.Id}}">', 72 ' <li ng-repeat="row in row.children" ng-include="\'uiNestedList.html\'" ></li>', 73 '</ul>', 74 '<span ng-if="row.children.length>0" data-toggle="collapse" data-target="#x-{{row._UniqueId || row.Id}}" class="expand-collapse"></span>' 75 ].join('')); 76 77 $templateCache.put('uiFileDropZone.html', [ 78 '<div class="drop-text"><span translate="algoapps.DropFileHere">Drag and drop files here to upload</span></div>' 79 ].join('')); 80 81}]); 82 83/*Controller for API List Data*/ 84angular.module("DataUI").controller('UIApiList', ["$scope", 'CommonApi', '$rootScope', "$attrs", "$q", "MessagesService", "$timeout", '$interval', '$window', '$translate', 'jqDialogService', 'SendCommandService', 'AlgoDataUtils', function ($scope, CommonApi, $rootScope, $attrs, $q, MessagesService, $timeout, $interval, $window, $translate, jqDialogService, SendCommandService, AlgoDataUtils) { 85 $scope.$data = new Array(); 86 $scope.AlgoDataUtils = AlgoDataUtils; 87 //Default settings 88 $scope.listSettings = { hideFilters: false, pagesize: 20, filter: {}, filterOperators: {} }; 89 $scope.querySettings = {}; 90 $scope.inProgress = false; 91 $scope.dataLoadSeq = 0; 92 93 //If settings sould inherit from parent scope 94 $scope.InheritSettings = ($attrs.uiInheritSettings != "false" && !!$scope.$parent); 95 $scope.SettingsVersion = 0; 96 97 /*Load data - based on listSettings*/ 98 var abort; 99 $scope._loadData = function () { 100 var query = buildQUery(); 101 102 if (abort) abort.resolve("Canceled");//Abort previos http request if exists 103 abort = $q.defer();//Create a new abort defer object 104 var deferred = $q.defer();//Create the data completed defer object 105 $scope.inProgress = true; $scope.inProgressOperation = "Loading..."; $scope.dataLoadSeq++; 106 deferred.dataLoadSeq = $scope.dataLoadSeq; 107 var api = CommonApi.all($scope.getApiName()).withHttpConfig({ timeout: abort.promise }); 108 $scope.listSettings.listUrl = api.buildUrl((api.action || 'List'), query); 109 //We can disable querying through attribute - usefull when we only need to produce a listUrl. 110 if ($attrs.uiDisableQuerying || $scope.listSettings.disableQuerying) { 111 deferred.resolve(); 112 } 113 else { 114 115 var dataLoaded = function (data) { 116 $scope.dataIsLoaded = true; 117 if (data && data.paging) $scope.listSettings.total = data.paging.totalRecordCount; 118 119 var _data = data; 120 if ($attrs.transformResult) { 121 transformResult = $attrs.transformResult; 122 _data = _.chain(data).keyBy('Id').value(); 123 } 124 $scope.$data = _data; 125 if ($attrs.uiDataVariable) $rootScope[$attrs.uiDataVariable] = _data; 126 deferred.resolve($scope.$data); 127 if ($scope.sendCommand) $scope.sendCommand("DataLoaded", $scope.queryParams); 128 else SendCommandService.sendCommand($scope.apiName, "DataLoaded", $scope.queryParams); 129 }; 130 var dataFailed = function (response) { 131 $scope.dataHasError = true; 132 deferred.reject(response); 133 if (response.status >= 0) MessagesService.showError(response); 134 }; 135 136 if ($scope.querySettings.postQuery) { 137 api.customPost((api.action || 'List'), query, $scope.querySettings.postQuery) 138 .then(dataLoaded, dataFailed); 139 } 140 else { 141 api.getList(query) 142 .then(dataLoaded, dataFailed); 143 } 144 } 145 146 deferred.promise.finally(function () { 147 //Check if this finallyty is for te current request. 148 if (deferred.dataLoadSeq == $scope.dataLoadSeq) { 149 $scope.inProgress = false; 150 $scope.inProgressOperation = ""; 151 } 152 else 153 { 154 //When finallyty is not for te current request, fill progress in 30 sec so it does not lock. 155 $timeout(function () {$scope.inProgress = false;$scope.inProgressOperation = "";}, 30000); 156 } 157 $scope.$applyAsync(); 158 }); 159 160 return deferred.promise; 161 }; 162 $scope.loadData = _.debounce($scope._loadData, 200); 163
164 $scope.export = function (format, params) { 165 var query = buildQUery(); 166 query.format = format; 167 //call export API 168 CommonApi 169 .all($scope.querySettings.exportApiName || $scope.getApiName()) 170 .download('Export', _.merge({}, query, params), null, 'GET').then( 171 function (data) { 172 scope.inProgress = false; 173 }, function (response) { 174 scope.inProgress = false; 175 MessagesService.showError(response); 176 } 177 ); 178 // var downloadUrl = CommonApi.all($scope.querySettings.exportApiName || $scope.getApiName()).buildUrl('Export', _.merge({}, query, params) ); 179 // $window.open(downloadUrl); 180 } 181 182 //Advanced export 183 $scope.exportAdvanced = function (apiName, apiAction, method, filename, sheetname) { 184 var query = buildQUery(); 185 if (sheetname) query.sheetName = sheetname; 186 if (filename) query.fileName = filename; 187 //call export API 188 CommonApi.all(apiName || $scope.querySettings.exportApiName || $scope.apiName).withHttpConfig({ method: (method || 'POST') }).download(apiAction || 'Export', query).then( 189 function (data) { 190 $scope.inProgress = false; 191 }, function (response) { 192 $scope.inProgress = false; 193 MessagesService.showError(response); 194 } 195 ); 196 } 197 198 /*Gets list settings merging with defaults and parent settings*/ 199 $scope.getListSettings = function (queryOverrides) { 200 //default 201 var r = { pagesize: 20, page: 0, order: new Array(), filter: {}, filterOperators: {}, select: new Array(), groupby: '' }; 202 203 //Inherit from parent 204 var p = {}, ps = $scope.$parent, psi = 0; 205 if ($scope.InheritSettings) { 206 while (ps && psi < 90) { 207 if (ps.getListSettings) { 208 _.mergeWith(p, ps.getListSettings(), function (a, b) { return (_.isArray(b) ? b : undefined); }); 209 break; 210 } 211 else if (ps.listSettings) { 212 _.mergeWith(p, ps.listSettings, function (a, b) { return (_.isArray(b) ? b : undefined); }); 213 } 214 ps = ps.$parent; psi++; 215 } 216 } 217 218 _.mergeWith(r, p, 219 $scope.listSettings,//from list settings (default overrides) 220 $scope.overrideSettings, //from list settings (forced values) 221 queryOverrides, //local overrides 222 function (a, b) { return (_.isArray(b) ? b : undefined); } 223 ); 224 return r; 225 } 226 227 $scope.getApiName = function () { 228 if ($scope.apiName) return $scope.apiName; 229 230 if ($scope.InheritSettings) { 231 var p = {}, ps = $scope.$parent, psi = 0; 232 while (ps && psi < 90) { 233 if ($scope.$parent.apiName) return $scope.$parent.apiName; 234 if ($scope.$parent.getApiName) return $scope.$parent.getApiName; 235 ps = ps.$parent; psi++; 236 } 237 } 238 239 return ''; 240 } 241 242 var buildQUery = function () { 243 $scope.SettingsVersion++; 244 var prm = $scope.getListSettings(); 245 $scope.querySettings = prm; 246 247 var query = AlgoDataUtils.getQueryObject(prm); 248 $scope.query = query; 249 return query; 250 } 251 252 $scope.buildQuery = buildQUery; 253 $scope.$watch('listSettings.filter', $scope.loadData, true); 254 $scope.$watch('listSettings.postQuery', $scope.loadData, true); 255 $scope.$watch('listSettings.order', $scope.loadData, true); 256 $scope.$watch('listSettings.groupby', $scope.loadData, true); 257 $scope.$watch('listSettings.select', $scope.loadData, true); 258 $scope.$watch('listSettings.page', $scope.loadData, true); 259 $scope.$watch('listSettings.pagesize', $scope.loadData, true); 260 $scope.$watch('apiName', $scope.loadData, true); 261 262 //Watch parent filters 263 if ($scope.InheritSettings) { 264 $scope.$parent.$watch('querySettings.filter', $scope.loadData, true); 265 $scope.$parent.$watch('listSettings.filter', $scope.loadData, true); 266 if (!$scope.apiName) $scope.$parent.$watch('apiName', $scope.loadData); 267 $scope.$parent.$watch('listSettings.postQuery', $scope.loadData, true); 268 } 269 270 /*Auto update data on intervals*/ 271 if ($attrs.updateInterval) {
272 var delay = parseInt($attrs.updateInterval); 273 if (delay > 0) $interval($scope.loadData, delay*1000); 274 } 275 276 $scope.new = function () { 277 var data = {Id:0}; 278 if ($scope.defaultRow) angular.extend(data, $scope.defaultRow); 279 if ($scope.sendCommand) $scope.sendCommand("NewRow", data); 280 else SendCommandService.sendCommand($scope.apiName, "NewRow", data); 281 } 282 283 $scope.select = function (data) { 284 $scope.selectedRow = data; 285 if ($scope.sendCommand) $scope.sendCommand("SelectRow", data); 286 else SendCommandService.sendCommand($scope.apiName, "SelectRow", data); 287 } 288 289 $scope.delete = function (data, what) { 290 MessagesService.confirm($translate.instant('algoapps.ConfirmDelete', { what: what }), $translate.instant('algoapps.Delete')).then(function () { 291 $scope.inProgress = true; 292 $scope.inProgressOperation = "Deleting"; 293 CommonApi.all($scope.apiName).customPost('delete', null, data).then( 294 function (data) { 295 $scope.inProgress = false; 296 if ($scope.sendCommand) $scope.sendCommand("RowDeleted", $scope.row); 297 else SendCommandService.sendCommand($scope.apiName, "RowDeleted", $scope.row); 298 $scope.loadData(); 299 MessagesService.showMessage($translate.instant('algoapps.DeleteSuccess', { what: what }), 'success', 3000); 300 }, function (response) { 301 $scope.inProgress = false; 302 MessagesService.showError(response); 303 }); 304 }, function () { });
305 } 306 307 $scope.setStatus = function (row, newStatus) { 308 $scope.inProgress = true; 309 var deferred = $q.defer(); 310 CommonApi.all($scope.apiName).customPost('UpdateStatus', null, { Id: row.Id, NewStatus: newStatus }).then( 311 function (data) { 312 $scope.inProgress = false; 313 MessagesService.showMessage($translate.instant('algoapps.UpdateSuccess', { what: $translate.instant('algoapps.Status') }), 'success', 3000); 314 row.Status = newStatus; 315 deferred.resolve(); 316 }, 317 function (error) { 318 $scope.inProgress = false; 319 MessagesService.showError(error); 320 deferred.reject(); 321 }); 322 return deferred.promise; 323 } 324 325 $timeout(function(){ 326 if ($scope.sendCommand) $scope.sendCommand("Ready"); 327 else SendCommandService.sendCommand($scope.apiName, "Ready"); 328 }, 0); 329 330 var redirectUrl='/'; 331 var redirectFn = function (data) { 332 window.location = redirectUrl + data.Id; 333 }; 334 $scope.createRedirectWithId = function (url) { 335 redirectUrl = url; 336 return redirectFn; 337 } 338 339 $scope.openDetail = function (template, queryOverrides) { 340 var model = {}, scope = $scope.$new(); 341 scope.listSettings = $scope.getListSettings(queryOverrides); 342 scope.getListSettings = function () { return scope.listSettings; } 343 344 jqDialogService.open(jqDialogService.generateId(), template, model, 345 { title: '', modal: true, resizable: false, width: 'auto', dialogClass: "modal-auto-height modal-full", parentScope: scope } 346 ).then(function (result) {},function (result) {}); 347 348 } 349}]); 350 351 352/*Service for data utilities*/ 353angular.module("DataUI").service('AlgoDataUtils', ['$rootScope', '$timeout', function ($rootScope, $timeout) { 354 var svc = {}; 355 356 svc.getFiltersString = function (prm) { 357 if (!prm) return ''; 358 359 var filtersStr = $.map(prm.filter, function (v, k) { 360 if (!v) return null; 361 if (prm.ignoreFilters && (k == prm.ignoreFilters || (_.isArray(prm.ignoreFilters) && _.find(prm.ignoreFilters, function (f) { return (f == k); })))) return null;//Filter set to be ignored in this context 362 if (v.range) //Compose range filter 363 { 364 if (v.range.from || v.range.to) { 365 var f = new Array(); 366 if (v.range.from) f.push(k + ':(ge)\'' + v.range.from + '\''); 367 if (v.range.to) f.push(k + ':(le)\'' + v.range.to + '\''); 368 return f.join(','); 369 } 370 else { 371 return null; //range with no values => null 372 } 373 } 374 else if (_.isArray(v)) { 375 //use In clause 376 if (v.length == 0) return null; 377 return k + ':(in)\'' + _.map(v, function (i) { return (_.isUndefined(i.Id) ? i : i.Id); }).join(';') + '\''; 378 } 379 else if (_.startsWith(v, '(')) { 380 //Forced operator 381 var i = v.indexOf(')'); 382 if (i !== -1) { 383 var op = v.slice(0, i); 384 const rest = v.slice(i + 1); 385 return k + ':' + op + ')\'' + rest +'\''; 386 } else { 387 return k + ':\'' + v + '\''; 388 } 389 390 391 } 392 else { 393 v = _.trim(v, '\''); 394 if (prm.filterOperators && prm.filterOperators[k]) { 395 return v == '' ? null : k + ':' + (prm.filterOperators[k] == '(lk)' ? '' : prm.filterOperators[k]) + '\'' + v + '\''; 396 } 397 else if (!isNaN(v)) { 398 //Number uses equal 399 return k + ':(eq)\'' + v + '\''; 400 } 401 else { 402 //Default Contains filter 403 return v == '' ? null : k + ':\'' + v + '\''; 404 } 405 } 406 }).join(','); 407 408 return filtersStr; 409 }; 410 411 svc.getQueryObject = function (prm) { 412 var query = { 413 pagesize: prm.pagesize, 414 page: prm.page, 415 filters: svc.getFiltersString(prm), 416 groupby: prm.groupby, 417 select: (_.isArray(prm.select) ? prm.select.join(',') : prm.select), 418 orderby: prm.order.join(',') 419 }; 420 421 if (prm.groupby && query.select) { 422 //Group by queries (select list except group by column) 423 query.select = _.filter(query.select.split(','), function (o) { return o.toLowerCase() != prm.groupby.toLowerCase(); }).join(','); 424 } 425 426 if (prm.timeout) query.timeout = prm.timeout; 427 query.page = query.page + 1; //Fix zero base pagination: 428 429 return query; 430 }; 431 432 433 return svc; 434}]); 435 436 437 438/*Controller for API Row Data*/ 439angular.module("DataUI").controller('UIApiRow', ['$scope', 'CommonApi', '$rootScope', '$attrs', 'MessagesService', '$translate', 'SendCommandService', function ($scope, CommonApi, $rootScope, $attrs, MessagesService, $translate, SendCommandService) { 440 $scope.row = {}; 441 442 /*Data management actions*/ 443 //Load data (data.Id) and use default values if (data.Id==0)
444 var _loadData = function (row) { 445 if (!$scope.apiName) { $scope.loadData(); $scope.inProgress = false; return; }; 446 $scope.inProgress = true; 447 $scope.inProgressOperation = 'Loading'; 448 var controllerAPI = CommonApi.all($scope.apiName); 449 if (controllerAPI.action == 'List') controllerAPI.action = 'Get'; 450 var id = 0; 451 //id can come from various places: 452 if ($scope.row && $scope.row.Id) id = $scope.row.Id; 453 if (row && row.Id) id = row.Id; 454 //If Id is specified in Api params, then it is more important 455 if (/*id == 0 &&*/ controllerAPI.params && controllerAPI.params.Id) id = controllerAPI.params.Id; 456 var scopeVariable = $attrs.uiScopeVar; 457 $scope.row = { Id: id }; 458 if (scopeVariable) $scope[scopeVariable] = $scope.row; 459 460 controllerAPI.customGet(controllerAPI.action || 'Get', { Id: id }).then( 461 function (data) { 462 $scope.inProgress = false; 463 //404 redirect 464 if (!data && $attrs.ui404Redirect) window.location = $attrs.ui404Redirect; 465 if (!data) data = { Id: 0 }; 466 467 $scope.setRow(data, row); 468 }, 469 function (response) { 470 if (response.status == '404' && $attrs.ui404Redirect) { 471 window.location = $attrs.ui404Redirect; 472 } 473 $scope.inProgress = false; 474 MessagesService.showError(response); 475 } 476 ); 477 }; 478 $scope.loadData = _.debounce(_loadData, 50); 479 //Load data when DataAPI Changes 480 $scope.$watch('apiName', function (newVal, oldVal) { 481 if (newVal != oldVal) $scope.loadData(); 482 }); 483 484 $scope.setRow = function (data, defaultData) { 485 $scope.row = data; 486 if (data.Content) { 487 $scope.row = angular.extend({}, data.Content, data); 488 $scope.row.Id = data.Id; 489 $scope.row.Content = null; 490 } 491 if (data.Id == 0) angular.extend($scope.row, defaultData); 492 493 var scopeVariable = $attrs.uiScopeVar; 494 if (scopeVariable) $scope[scopeVariable] = $scope.row; 495 496 if ($scope.sendCommand) $scope.sendCommand('DataLoaded', data); 497 else SendCommandService.sendCommand($scope.apiName, "DataLoaded", data); 498 499 $scope.$applyAsync(); 500 } 501 502 $scope.edit = function () { 503 $scope.viewMode = 'edit'; 504 } 505 506 $scope.delete = function () { 507 MessagesService.confirm($translate.instant('algoapps.ConfirmDelete', { what: '' }), $translate.instant('algoapps.Delete')).then(function () { 508 $scope.inProgress = true; 509 $scope.inProgressOperation = 'Deleting'; 510 $scope.row.remove().then( 511 function (data) { 512 $scope.inProgress = false; 513 if ($scope.sendCommand) $scope.sendCommand('RowDeleted', $scope.row); 514 else SendCommandService.sendCommand($scope.apiName, "RowDeleted", $scope.row); 515 MessagesService.showMessage($translate.instant('algoapps.DeleteSuccess', { what: '' }), 'success', 3000); 516 }, function (response) { 517 $scope.inProgress = false; 518 MessagesService.showError(response); 519 }); 520 }, function () { });
521 522 } 523 524}]); 525 526/*Controller for posting multipart form data*/ 527angular.module('DataUI').controller('UIMultipartFormData', ['$scope', '$attrs', '$element', 'CommonApi', 'MessagesService', function ($scope, $attrs, $element, CommonApi, MessagesService) { 528 $scope.FormData = {}; 529 $scope.inProgress = false; 530 $scope.validation = $scope.validation || {}; 531 $scope.isDownload = ($attrs.uiIsDownload == 'true'); 532 533 var _post = function (isDownload) { 534 if ($scope.inProgress || $scope.validation.$invalid) return; 535 $scope.inProgress = true; 536 537 var _api = CommonApi.createApi($attrs.uiApiName); 538 var formData = new FormData(); 539 //Prepare files 540 $($element).find('input[type="file"]').each(function(i, el){ 541 _.forEach(el.files, function (file) { 542 var filename = file.name; 543 formData.append('file', file, filename); 544 }); 545 }); 546 547 //Prepare model 548 formData.append('model', JSON.stringify($scope.FormData)); 549 550 //Post the data 551 if ($scope.isDownload) { 552 _api.withHttpConfig({ transformRequest: angular.identity, headers: { 'Content-Type': undefined } }) 553 .download(_api.action, null, formData) 554 .then(successCallback, errorCallback); 555 } 556 else { 557 _api.withHttpConfig({ transformRequest: angular.identity, headers: { 'Content-Type': undefined } }) 558 .customPost(_api.action, null, formData) 559 .then(successCallback, errorCallback); 560 } 561 562 } 563 var successCallback = function (data) { 564 $scope.inProgress = false; 565 MessagesService.showMessage('Sucesso.', 'success', 5000); 566 }; 567 var errorCallback = function (response) { 568 $scope.inProgress = false; 569 MessagesService.showError(response); 570 }; 571 572 var post = _.debounce(_post, 50); 573 $scope.post = function () { post(); } 574 575}]); 576 577/*Directive to get row.id from URL*/ 578angular.module("DataUI").directive('uiUrlPattern', ['$timeout', '$location', function ($timeout, $location) { 579 return { 580 restrict: 'EA', 581 priority: 1000, 582 scope: { 583 ngModel: '=ngModel', 584 urlPattern: '@uiUrlPattern', 585 loadDataCallback: '&uiLoadData' 586 }, 587 link: function (scope, element, attrs) { 588 589 var _evalPattern = function () { 590 if (scope.urlPattern) { 591 ///*Find procid in url*/ 592 //var regexp = /\/procId\/(\d+)/g; 593 var regexp = new RegExp(scope.urlPattern + '(?:/)?', "gi"); 594 var loc = $location.absUrl(), match; 595 while ((match = regexp.exec(loc)) != null) { 596 if (match && match.length > 1) scope.ngModel = match[1]; 597 } 598 } 599 else { 600 loadData(); 601 } 602 }; 603 604 var _loadData = function () { 605 if (scope.urlPattern && scope.loadDataCallback) scope.loadDataCallback()(); 606 }; 607 var loadData = _.debounce(_loadData, 100); 608 scope.$watch("ngModel", function (newVal, oldVal) { 609 if (newVal != oldVal) loadData(); 610 }); 611 612 //Eval on init 613 $timeout(_evalPattern, 0); 614 } 615 }; 616}]); 617 618 619 620/*Save button with API connection:*/ 621angular.module("DataUI").directive('uiGetItemApi', ['CommonApi', 'MessagesService', function (CommonApi, MessagesService) { 622 return { 623 restrict: 'EA', 624 priority: 1000, 625 scope: { 626 apiName: '@uiGetItemApi', 627 data: '=uiData', 628 contentMap:'@?uiDataContent' 629 }, 630 link: function (scope, element, attrs) { 631 632 var _loadData = function () { 633 scope.inProgress = true; 634 scope.inProgressOperation = "Loading"; 635 CommonApi.all(scope.apiName).get(scope.data.Id).then( 636 function (data) { 637 scope.inProgress = false; 638 _.extend(scope.data, data); 639 }, 640 function (response) { 641 scope.inProgress = false; 642 MessagesService.showError(response); 643 } 644 ); 645 } 646 scope.loadData = _.debounce(_loadData, 100); 647 scope.$watch('data.Id', scope.loadData);
648 scope.$on('loadData', scope.loadData); 649 } 650 }; 651}]); 652 653/*Save button with API connection:*/ 654angular.module("DataUI").directive('uiSaveApi', ['CommonApi', 'MessagesService', '$translate', function (CommonApi, MessagesService, $translate) { 655 return { 656 restrict: 'EA', 657 priority: 1000, 658 scope: { 659 apiName: '@uiSaveApi', 660 apiAction: '@uiSaveApiAction', 661 data: '=data', 662 onSuccess: "=?onSuccess", 663 validation: "=?uiValidationState", 664 contentMap: '@?uiDataContent' 665 }, 666 transclude:true, 667 replace:true, 668 template: '<a href ng-click="save()" ng-disabled="validation.$invalid || inProgress" ng-transclude=""><span translate="algoapps.Save">Save</span></a>', 669 link: function (scope, element, attrs) { 670 671 var _save = function () { 672 if (scope.inProgress || scope.validation.$invalid) return; 673 scope.inProgress = true; 674 var api = CommonApi.all(attrs.uiSaveApi).customPost(scope.apiAction || 'createOrUpdate', null, scope.data).then( 675 function (data) { 676 scope.inProgress = false; 677 if (data) angular.extend(scope.data, data); 678 MessagesService.showMessage(attrs.successMessage || $translate.instant('algoapps.UpdateSuccess', { what: '' }), 'success', 3000); 679 if (scope.onSuccess) scope.onSuccess(scope.data); 680 }, function (response) { 681 scope.inProgress = false; 682 MessagesService.showError(response); 683 }); 684 } 685 var save = _.debounce(_save, 50); 686 scope.save = function () { save(); } 687 688 } 689 }; 690}]); 691 692/*Save button with API connection:*/ 693angular.module("DataUI").directive('uiCallApi', ['CommonApi', 'MessagesService', '$translate', 'SendCommandService', function (CommonApi, MessagesService, $translate, SendCommandService) { 694 return { 695 restrict: 'EA', 696 priority: 1000, 697 scope: { 698 apiName: '@uiCallApi', 699 apiAction: '@uiApiAction', 700 isPost:'@uiIsPost', 701 data: '=uiData', 702 urlData: '=uiUrlData', 703 isDownload: '@uiIsDownload', 704 onSuccess: '&?onSuccess', 705 validation: '=?uiValidationState', 706 isDisabled: '=?uiDisabled', 707 confirmMessage: '@uiConfirmMessage' 708 }, 709 transclude: true, 710 replace: true, 711 template: '<a href ng-click="callApi(); $event.stopPropagation();" ng-disabled="isDisabled || validation.$invalid || inProgress" ng-class="{\'action-in-progress\':inProgress}" ng-transclude="">Do Something...</a>', 712 link: function (scope, element, attrs) { 713 714 var _callApi = function () { 715 if (scope.inProgress) return; 716 scope.inProgress = true; 717 718 if (!scope.isDownload) { 719 if (!scope.isPost) { 720 CommonApi.all(scope.apiName).customGet(scope.apiAction, _.merge({}, scope.data, scope.urlData)).then( 721 function (data) { 722 scope.inProgress = false; 723 if (!data) data = {}; 724 MessagesService.showMessage(data.message || $translate.instant('algoapps.Success'), 'success', 5000); 725 if (scope.onSuccess) scope.onSuccess({ result: data }); 726 if (scope.$parent.sendCommand) scope.$parent.sendCommand(scope.apiAction); 727 else SendCommandService.sendCommand(scope.apiName, scope.apiAction); 728 729 }, function (response) { 730 scope.inProgress = false; 731 MessagesService.showError(response); 732 }); 733 } 734 else { 735 CommonApi.all(scope.apiName).customPost(scope.apiAction, scope.urlData, scope.data).then( 736 function (data) { 737 scope.inProgress = false; 738 if (!data) data = {}; 739 MessagesService.showMessage(data.message || $translate.instant('algoapps.Success'), 'success', 5000); 740 if (scope.onSuccess) scope.onSuccess({ result: data }); 741 if (scope.$parent.sendCommand) scope.$parent.sendCommand(scope.apiAction); 742 else SendCommandService.sendCommand(scope.apiName, scope.apiAction); 743 744 }, function (response) { 745 scope.inProgress = false; 746 MessagesService.showError(response); 747 }); 748 } 749 } else { 750 if (!scope.isPost) { 751 CommonApi.all(scope.apiName).download(scope.apiAction, _.merge({}, scope.data, scope.urlData), null, 'GET').then( 752 function (data) { 753 scope.inProgress = false; 754 }, function (response) { 755 scope.inProgress = false; 756 MessagesService.showError(response); 757 } 758 ); 759 } 760 else { 761 CommonApi.all(scope.apiName).download(scope.apiAction, scope.urlData, scope.data, 'POST').then( 762 function (data) { 763 scope.inProgress = false; 764 }, function (response) { 765 scope.inProgress = false; 766 MessagesService.showError(response); 767 } 768 ); 769 } 770 } 771 772 } 773 774 var _execute = function () { 775 if (scope.confirmMessage) { 776 MessagesService.confirm(scope.confirmMessage, '').then( 777 function () { _callApi(); },function () { } 778 ); 779 } 780 else { 781 _callApi(); 782 } 783 } 784 var callApi = _.debounce(_execute, 50); 785 scope.callApi = function () { callApi(); } 786 787 } 788 }; 789}]); 790 791 792/*Default list settings:*/ 793angular.module("DataUI").directive('uiListSettings', [function () { 794 return { 795 restrict: 'A', 796 priority: 1000, 797 scope: false, 798 link: { 799 pre: function (scope, element, attrs) { 800 if (!scope.listSettings) scope.listSettings = {}; 801 802 attrs.$observe("uiListSettings", function (newVal) { 803 angular.merge(scope.listSettings, angular.fromJson(newVal)); 804 }); 805 806 angular.merge(scope.listSettings, angular.fromJson(attrs.uiListSettings)); 807 808 } 809 } 810 }; 811}]); 812 813 814/*Set filters from object:*/ 815angular.module("DataUI").directive('uiFilterFromObject', [function () { 816 return { 817 restrict: 'A', 818 priority: 1000, 819 scope: { 820 filterObj: '=uiData' 821 }, 822 link: function(scope, element, attrs){ 823 if (!scope.$root.listSettings) scope.$root.listSettings = {}; 824 825 var _updateListSettings = function () { 826 if (!scope.$root.listSettings.filter) scope.$root.listSettings.filter = {}; 827 _.merge(scope.$root.listSettings.filter, scope.filterObj); 828 } 829 var updateListSettings = _.debounce(_updateListSettings, 100); 830 scope.$watch("filterObj", updateListSettings); 831 scope.$watch("filterObj", updateListSettings, true); 832 833 angular.merge(scope.listSettings, angular.fromJson(attrs.uiListSettings)); 834 835 } 836 }; 837}]); 838/*Directive to specify wich Api to use*/ 839angular.module("DataUI").directive('uiLookup', ['CommonApi', function (CommonApi) { 840 return { 841 restrict: 'A', 842 priority: 1000, 843 scope: { 844 lookupId: '=uiLookup', 845 apiName: '@uiApiName' 846 }, 847 replace: true, 848 template: '<span class="lookup-label"></span>', 849 link: function (scope, element, attrs) { 850 851 var _reload = function () { 852 CommonApi.getLookup(scope.apiName, scope.lookupId).then( 853 function (data) { $(element).text(data.Label || data.Name || data.Title || data.Id || scope.lookupId); }, 854 function () { $(element).text('');}); 855 }; 856 var reload = _.debounce(_reload, 50); 857 scope.$watch('lookupId', reload); 858 scope.$watch('apiName', reload); 859 } 860 }; 861}]); 862 863 864/*Directive to specify wich Api to use*/ 865angular.module("DataUI").directive('uiApiName', ['CommonApi', '$rootScope', '$compile', function (CommonApi, $rootScope, $compile) { 866 return { 867 restrict: 'A', 868 priority: 1000, 869 scope: false, 870 link: function (scope, element, attrs) { 871 scope.fields = new Array(); 872 873 attrs.$observe("uiApiName", function (newVal) { 874 scope.apiName = newVal; 875 }); 876 877 scope.findField = function (fieldId) { 878 return _.chain(scope.fields).find(function (r) { return r.Id == fieldId }
878).value(); 879 }; 880 } 881 }; 882}]); 883 884/*Directive to specify wich Api to for exporting data*/ 885angular.module("DataUI").directive('uiExportApiName', [function () { 886 return { 887 restrict: 'A', 888 priority: 1000, 889 scope: false, 890 link: function (scope, element, attrs) { 891 attrs.$observe("uiExportApiName", function (newVal) { 892 if (scope.listSettings) scope.listSettings.exportApiName = newVal; 893 }); 894 } 895 }; 896}]); 897 898/*Directive to default values for new objects*/ 899angular.module("DataUI").directive('uiDataDefault', [function () { 900 return { 901 restrict: 'EA', 902 priority: 1000, 903 scope: false, 904 link: function (scope, element, attrs) { 905 906 attrs.$observe("uiDataDefault", function (newVal) { 907 if (!scope.defaultRow) scope.defaultRow = {}; 908 angular.extend(scope.defaultRow, angular.fromJson(newVal)); 909 }); 910 911 } 912 }; 913}]); 914 915/*Directive to default values for new objects*/ 916angular.module("DataUI").directive('uiDataOverride', [function () { 917 return { 918 restrict: 'EA', 919 priority: 1000, 920 scope: false, 921 link: function (scope, element, attrs) { 922 923 attrs.$observe("uiDataOverride", function (newVal) { 924 if (!scope.dataOverride) scope.dataOverride = {}; 925 angular.extend(scope.dataOverride, angular.fromJson(newVal)); 926 }); 927 928 } 929 }; 930}]); 931 932/*Directive to extend a object*/ 933angular.module("DataUI").directive('uiDataExtend', [function () { 934 return { 935 restrict: 'EA', 936 priority: 1000, 937 scope: { 938 data: '=?uiDataExtend', 939 extendData:'=?uiDataExtendWith' 940 }, 941 link: function (scope, element, attrs) { 942 943 var doExtend = function () { 944 if (!scope.data) scope.data = {}; 945 if (!scope.extendData) scope.extendData = {}; 946 angular.merge(scope.data, scope.extendData); 947 } 948 scope.$watch("data", doExtend); 949 scope.$watch("extendData", doExtend); 950 951 } 952 }; 953}]); 954 955 956/*Directive to show data as a list ul > li with commands*/ 957angular.module("DataUI").directive('uiDataList', ['$timeout', function ($timeout) { 958 return { 959 restrict: 'A', 960 priority:100, 961 scope: false, 962 templateUrl: function (element, attrs) { return attrs.uiTemplate || 'uiApiListFull.html'; }, 963 link: function (scope, element, attrs) { 964 if (scope.loadData) $timeout(scope.loadData, 100); 965 966 scope.refresh = function () { 967 if(scope.loadData) scope.loadData(); 968 } 969 } 970 }; 971}]); 972 973/*Directive to show data as masonry list*/ 974angular.module("DataUI").directive('uiMasonry', ['$window', function ($window) { 975 return { 976 restrict: 'A', 977 priority: 100, 978 scope: false, 979 link: function (scope, element, attrs) { 980 var msnry, imgLoader; 981 982 var _update = function () { 983 if (!msnry) { 984 msnry = $(element).masonry({ itemSelector: '.masonry-item', columnWidth: '.masonry-item', percentPosition: true }); 985 } 986 msnry.masonry('reloadItems').masonry('layout'); 987 //Images have loaded 988 msnry.imagesLoaded() 989 .progress(function (imgLoad, image) { 990 var s = image.img.src; 991 if (!window.allimages) window.allimages = {}; 992 if (s) { 993 if (!window.allimages[s]) allimages[s] = false; 994 if (window.allimages[s] != image.isLoaded) update(); 995 window.allimages[s] = image.isLoaded; 996 } 997 }); 998 } 999 var update = _.debounce(_update, 50); 1000 //Watch for dom change and other events that must update the masonry 1001 scope.$watch(function () { return element[0].childNodes.length; }, update); 1002 //Init 1003 update(); 1004 } 1005 }; 1006}]); 1007 1008/*Directive to show data as grouped by columns and row fields*/ 1009angular.module("DataUI").directive('uiPivot', [function () { 1010 return { 1011 restrict: 'A', 1012 priority: 100, 1013 scope: false, 1014 templateUrl: 'uiPivot.html', 1015 transclude:true, 1016 link: { 1017 pre: function (scope, element, attrs, ctrl, transcludeFn) { 1018 scope.pivotData = {}; 1019 1020 var _buildPivot = function () { 1021 scope.pivotData = {}; scope.pivotColumns = {}; 1022 scope.colCssClass = attrs.uiColClass; 1023 //Prepare pivot with all row and col values; 1024 _.forEach(scope.$data, function (r) { 1025 var rv = r[attrs.uiRowField] || 'ND'; 1026 scope.pivotData[rv] = {}; 1027 _.forEach(scope.$data, function (c) { 1028 var cv = c[attrs.uiColField] || 'ND'; 1029 scope.pivotData[rv][cv] = {}; 1030 scope.pivotColumns[cv] = {}; 1031 }); 1032 }); 1033 //Set object values 1034 _.forEach(scope.$data, function (o) { 1035 var rv = o[attrs.uiRowField] || 'ND'; 1036 var cv = o[attrs.uiColField] || 'ND'; 1037 scope.pivotData[rv][cv] = o; 1038 scope.pivotData[rv][cv].hasValue = true; 1039 }); 1040 1041 scope.$applyAsync(); 1042 }; 1043 1044 var buildPivot = _.debounce(_buildPivot, 200);
1045 scope.$watchCollection('$data', buildPivot); 1046 attrs.$observe('rowField', buildPivot); 1047 attrs.$observe('colField', buildPivot); 1048 } 1049 } 1050 }; 1051}]); 1052 1053/*Directive to show data as grouped by columns and row fields*/ 1054angular.module("DataUI").directive('uiPivotData', [function () { 1055 return { 1056 restrict: 'A', 1057 priority: 100, 1058 scope: { 1059 data: '=uiData', 1060 pivotData: '=uiPivotData', 1061 rowField: '@uiRowField', 1062 colField: '@uiColField', 1063 }, 1064 link: { 1065 pre: function (scope, element, attrs, ctrl, transcludeFn) { 1066 1067 var _buildPivot = function () { 1068 scope.pivotData.pivotColumns = []; 1069 scope.pivotData.data = {}; 1070 var cols = {}; 1071 1072 //Prepare pivot with all row and col values; 1073 _.forEach(scope.data, function (r) { 1074 var rv = r[scope.rowField] || 'ND'; 1075 scope.pivotData.data[rv] = {}; 1076 scope.pivotData.data[rv].data = r; 1077 _.forEach(scope.data, function (c) { 1078 var cv = c[scope.colField] || 'ND'; 1079 scope.pivotData.data[rv][cv] = {}; 1080 cols[cv] = {}; 1081 }); 1082 }); 1083 1084 //Set object values 1085 _.forEach(scope.data, function (o) { 1086 var rv = o[scope.rowField] || 'ND'; 1087 var cv = o[scope.colField] || 'ND'; 1088 scope.pivotData.data[rv][cv] = o; 1089 scope.pivotData.data[rv][cv].hasValue = true; 1090 }); 1091 1092 if (cols.ND) delete cols.ND; 1093 scope.pivotData.pivotColumns = _(cols).keys().sortBy().value() 1094 scope.$applyAsync(); 1095 }; 1096 1097 var buildPivot = _.debounce(_buildPivot, 200); 1098 scope.$watch('data', buildPivot); 1099 scope.$watchCollection('data', buildPivot); 1100 attrs.$observe('rowField', buildPivot); 1101 attrs.$observe('colField', buildPivot); 1102 } 1103 } 1104 }; 1105}]); 1106 1107 1108/* 1109Directive to show data as grouped by columns and row fields 1110see: https://www.webdatarocks.com/doc/download/ 1111*/ 1112angular.module("DataUI").directive('uiPivotTable', ['$ocLazyLoad', function ($ocLazyLoad) { 1113 return { 1114 restrict: 'A', 1115 priority: 100, 1116 scope: { 1117 data: '=uiData', 1118 dataUrl: '@uiDataUrl', 1119 cols: '=uiColumnsBy', 1120 rows: '=uiRowsBy', 1121 vals: '=uiValuesBy', 1122 filters: '=uiFilters', 1123 fields: '=uiFieldLabels', 1124 opts: '=uiOptions', 1125 viewMode: "@uiViewMode" 1126 }, 1127 template: '<div id="ui-pivot-{{::$id}}" class="ui-pivot"></div>', 1128 transclude: true, 1129 link: function (scope, element, attrs) { 1130 1131 scope.pivotData = {}; 1132 scope.pivotObj = {}; 1133 1134 var getFieldObject = function (o) { 1135 var r = (_.isObject(o) ? o : { uniqueName: o }); 1136 if (!r.caption && r.uniqueName && scope.fields && scope.fields[r.uniqueName]) r.caption = scope.fields[r.uniqueName]; 1137 return r; 1138 } 1139 1140 var _buildPivot = function () { 1141 if (!$.fn.webdatarocks) { 1142 buildPivot(); return; 1143 } 1144 1145 var el = $(element).find('.ui-pivot'); 1146 1147 1148 //prepare datasources object 1149 var ds = {}; 1150 if (scope.data) { 1151 ds.data = scope.data; 1152 } 1153 else if (scope.dataUrl) { 1154 ds.filename = scope.dataUrl; 1155 } 1156 1157 1158 var isEditMode = (scope.viewMode == 'edit'); 1159 var opts = _.merge({ 1160 toolbar: isEditMode, 1161 report: { 1162 dataSource: ds, 1163 slice: { 1164 reportFilters: _.map(scope.filters, getFieldObject), 1165 rows: _.map(scope.rows, getFieldObject), 1166 columns: _.map(scope.cols, getFieldObject), 1167 measures: _.map(scope.vals, getFieldObject), 1168 }, 1169 options: { 1170 grid: { 1171 type: 'compact', 1172 showHeaders:false 1173 },
1174 configuratorButton: false 1175 } 1176 }, 1177 global: { 1178 localization: "/DesktopModules/MVC/AlgoritmiaAngular/js/lib/webdatarocks/loc/pt_PT.json" 1179 } 1180 }, scope.opts); 1181 1182 scope.pivotObj = $(el).webdatarocks(opts); 1183 1184 }; 1185 1186 var buildPivot = _.debounce(_buildPivot, 200); 1187 scope.$watchCollection('data', buildPivot); 1188 scope.$watch('dataUrl', buildPivot); 1189 scope.$watch('cols', buildPivot); 1190 scope.$watch('rows', buildPivot); 1191 scope.$watch('vals', buildPivot); 1192 scope.$watch('aggregatorName', buildPivot); 1193 scope.$watch('rendererName', buildPivot); 1194 scope.$watch('viewMode', buildPivot); 1195 1196 //Load dependencies 1197 $ocLazyLoad.load({ 1198 serie: true, 1199 files: [ 1200 '/DesktopModules/MVC/AlgoritmiaAngular/js/lib/webdatarocks/webdatarocks.toolbar.min.js', 1201 '/DesktopModules/MVC/AlgoritmiaAngular/js/lib/webdatarocks/webdatarocks.min.css', 1202 '/DesktopModules/MVC/AlgoritmiaAngular/js/lib/webdatarocks/webdatarocks.js' 1203 ] 1204 }); 1205 } 1206 }; 1207}]); 1208 1209 1210/* 1211Directive to show data as grouped by columns and row fields 1212see: https://pivottable.js.org/examples/index.html 1213*/ 1214angular.module("DataUI").directive('uiPivotTable2', ['$ocLazyLoad', function ($ocLazyLoad) { 1215 return { 1216 restrict: 'A', 1217 priority: 100, 1218 scope: { 1219 data: '=uiData', 1220 cols: '=uiColumnsBy', 1221 rows: '=uiRowsBy', 1222 vals: '=uiValuesBy', 1223 opts: '=uiOptions', 1224 aggregatorName:"@uiAggregation", 1225 rendererName:"@uiRenderer", 1226 viewMode: "@uiViewMode", 1227 pivotVersion: "@uiPivotVersion" 1228 }, 1229 template: '<div class="ui-pivot"></div>', 1230 transclude: true, 1231 link: function (scope, element, attrs) { 1232 1233 scope.pivotData = {}; 1234 1235 var _buildPivot = function () { 1236 var opts = _.merge({}, scope.opts); 1237 if (scope.cols) opts.cols = scope.cols; 1238 if (scope.rows) opts.rows = scope.rows; 1239 if (scope.aggregatorName) opts.aggregatorName = scope.aggregatorName; 1240 1241 if (scope.aggregatorName == 'multi' || scope.aggregatorName == 'Multifact Aggregators') { 1242 opts.aggregatorName = 'Multifact Aggregators'; 1243 opts.vals = []; 1244 var aggMap = {}; 1245 _.forEach(scope.vals, function (o,i) { 1246 var aggName = 'agg' + i; 1247 var d = { 1248 aggType: 'Sum', 1249 arguments: [], 1250 name: aggName, 1251 varName: aggName, 1252 hidden: false, 1253 renderEnhancement: 'none' 1254 }; 1255 1256 if (_.isString(o)) { 1257 d.arguments = [o]; 1258 d.name = o; 1259 } 1260 else { 1261 _.extend(d, o); 1262 } 1263 aggMap[aggName] = d; 1264 }); 1265 opts.rendererOptions = { aggregations: { defaultAggregations: aggMap } }; 1266 var customAggs = {}; 1267 //Lame dependency... If we need this module, wait until it's loaded... 1268 if (!$.pivotUtilities || !$.pivotUtilities.multifactAggregatorGenerator) { 1269 buildPivot(); 1270 return; 1271 } 1272 customAggs['Multifact Aggregators'] = $.pivotUtilities.multifactAggregatorGenerator(aggMap, []); 1273 $.pivotUtilities.customAggs = customAggs; 1274 opts.aggregators = $.extend($.pivotUtilities.aggregators, $.pivotUtilities.customAggs); 1275 opts.renderers = $.extend($.pivotUtilities.renderers, $.pivotUtilities.gtRenderers); 1276 1277 } else { 1278 if (scope.vals) opts.vals = scope.vals; 1279 }
1280 if (scope.rendererName) opts.rendererName = scope.rendererName; 1281 1282 opts.grouping= { 1283 colGroupBefore: false, // this is defualt value 1284 }; 1285 opts.rendererOptions= { 1286 table: { 1287 compactLayout: true, // this is default value 1288 } 1289 }; 1290 1291 if (scope.viewMode != 'edit') 1292 opts.showUI = false; 1293 1294 $(element).pivotUI(scope.data, opts, true); 1295 1296 1297 }; 1298 1299 var buildPivot = _.debounce(_buildPivot, 200); 1300 scope.$watchCollection('data', buildPivot); 1301 scope.$watch('cols', buildPivot); 1302 scope.$watch('rows', buildPivot); 1303 scope.$watch('vals', buildPivot); 1304 scope.$watch('aggregatorName', buildPivot); 1305 scope.$watch('rendererName', buildPivot); 1306 scope.$watch('viewMode', buildPivot); 1307 1308 var srcFolder = 'Grouping'; 1309 if (scope.pivotVersion == 'grouping') srcFolder = 'Grouping'; 1310 //Load dependencies 1311 $ocLazyLoad.load({ 1312 serie: true, 1313 files: [ 1314 '/DesktopModules/MVC/AlgoritmiaAngular/js/lib/pivottable/' + srcFolder + '/pivot.min.js', 1315 '/DesktopModules/MVC/AlgoritmiaAngular/js/lib/pivottable/' + srcFolder + '/pivot.min.css', 1316 '/DesktopModules/MVC/AlgoritmiaAngular/js/lib/pivottable/' + srcFolder + '/Grouping.css', 1317 '/DesktopModules/MVC/AlgoritmiaAngular/js/lib/pivottable/' + srcFolder + '/pivot.pt.min.js', 1318 '/DesktopModules/MVC/AlgoritmiaAngular/js/lib/pivottable/Plugins/multifact-pivottable.js' 1319 ] 1320 }); 1321 } 1322 }; 1323}]); 1324 1325 1326 1327/*Directive to specify wich one is the title field*/ 1328angular.module("DataUI").directive('uiTitleField', ['$rootScope',function ($rootScope) { 1329 return { 1330 restrict: 'A', 1331 priority:1000, 1332 scope: false, 1333 link:{ 1334 pre: function (scope, element, attrs) { 1335 attrs.$observe('uiTitleField', function(newVal){scope.titleField = newVal;}); 1336 } 1337 } 1338 }; 1339}]); 1340 1341 1342/*Table directive*/ 1343angular.module("DataUI").directive('uiDataTable', ['CommonApi', 'MessagesService', 'ContentItemsService', '$translate', function (CommonApi, MessagesService, ContentItemsService, $translate) { 1344 return { 1345 restrict: 'EA', 1346 priority:100, 1347 scope: { 1348 allowUpload: '=uiAllowUpload', 1349 apiName: '@uiUploadApiName', 1350 formName: '@uiFormName', 1351 defaultData: '=uiData' 1352 }, 1353 link: function (scope, element, attrs) { 1354 1355 1356 /*Drag n drop Files*/ 1357 if (scope.allowUpload) 1358 { 1359 var showDrop = function (e) { 1360 e.preventDefault(); 1361 if (hideDropTimer) window.clearTimeout(hideDropTimer); 1362 $(element).addClass('file-drag-hover'); 1363 } 1364 var hideDropTimer; 1365 var hideDrop = function (e) { 1366 if (e) e.preventDefault(); 1367 hideDropTimer = window.setTimeout(function () { $(element).removeClass('file-drag-hover') }, 100); 1368 } 1369 $(element).on('dragenter', showDrop); 1370 $(element).on('dragover', showDrop); 1371 $(element).on('dragleave', hideDrop); 1372 1373 $(element).find('.file-drop-zone').on('drop', function (e) { 1374 hideDrop(); e.preventDefault(); e.stopPropagation(); 1375 if (e.originalEvent.dataTransfer) { 1376 /*Data to go with the file*/ 1377 var model = _.merge({ FormName: scope.formName }, scope.defaultData); 1378 model.Content = _.cloneDeep(model); 1379 /*UPLOAD FILES HERE*/ 1380 scope.doUpload(e.originalEvent.dataTransfer.files, model); 1381 } 1382 }); 1383 } 1384 1385 1386 /*Upload the file specified in an file input element and an optionl data object*/ 1387 scope.doUpload = function (files, uploadData) { 1388 var _api = CommonApi.all(scope.apiName); 1389 if (files && files.length) { 1390 //scope.FileName = file.name; 1391 var formData = new FormData(); 1392 _.forEach(files, function (file) { formData.append('file', file); }); 1393 formData.append('model', JSON.stringify(uploadData || {})); 1394 1395 _api 1396 .withHttpConfig({ transformRequest: angular.identity, headers: { 'Content-Type': undefined } }) 1397 .customPost('UploadDocument', null, formData) 1398 .then(function (data) { 1399 MessagesService.showMessage($translate.instant('algoapps.UploadFileSuccess', { }), 'success', 5000); 1400 1401 //Then data is an object or a list with only one object, open it for editing. 1402 if (data && (data.Id || data.length == 1)) { 1403 var row = data; 1404 if (_.isArray(data)) row = data[0]; 1405 if (row.Id) { 1406 ContentItemsService.OpenModal(scope.$parent, { 1407 apiName: scope.apiName, 1408 formName: scope.formName, 1409 viewMode: 'edit', 1410 dataId: row.Id, 1411 onComplete: function () { 1412 scope.$parent.loadData(); 1413 scope.$applyAsync(); 1414} 1415 }); 1416 } 1417 1418 } 1419 1420 scope.$parent.loadData(); 1421 scope.$applyAsync(); 1422 }, function (response) { 1423 /*error*/ 1424 MessagesService.showError(response); 1425 scope.$applyAsync(); 1426 }); 1427 } 1428 }; 1429 1430 } 1431 }; 1432}]); 1433 1434/*Comunication to receive eventes for load / reload Data*/ 1435angular.module("DataUI").directive('uiLoadDataOn', [function () { 1436 return { 1437 restrict: 'A', 1438 priority: 200, 1439 scope: false, 1440 link: function (scope, element, attrs) { 1441 1442 if (attrs.uiLoadDataOn) { 1443 scope.$on('uiCommand', function (ev, data) { 1444 var triggers = attrs.uiLoadDataOn ? attrs.uiLoadDataOn.split(',') : new Array(); 1445 if (triggers.indexOf(data.sendGroup + '.' + data.commandName) == -1) return; 1446 if (scope.refresh) scope.refresh();
1447 else if (scope.loadData) scope.loadData(); 1448 1449 if (data.commandName == 'SelectRow') scope.viewMode = 'display'; 1450 if (data.commandName == 'EditRow' || data.commandName == 'NewRow') scope.viewMode = 'edit'; 1451 }); 1452 } 1453 } 1454 }; 1455}]); 1456 1457/*Comunication to receive eventes for Data filter change*/ 1458angular.module("DataUI").directive('uiFilterOn', [function () { 1459 return { 1460 restrict: 'A', 1461 priority: 200, 1462 scope: false, 1463 link: function (scope, element, attrs) { 1464 1465 if (attrs.uiFilterOn) { 1466 scope.$on('uiCommand', function (ev, data) { 1467 var triggers = attrs.uiFilterOn ? attrs.uiFilterOn.split(',') : new Array(); 1468 if (triggers.indexOf(data.sendGroup + '.' + data.commandName) == -1) return; 1469 if (scope.listSettings) _.merge(scope.listSettings.filter, data.row); 1470 }); 1471 } 1472 } 1473 }; 1474}]); 1475 1476/*Comunication infrastructure for sending broadcast commands on the specified group*/ 1477angular.module("DataUI").directive('uiSendGroup', ['SendCommandService', function (SendCommandService) { 1478 return { 1479 restrict: 'A', 1480 priority: 500, 1481 scope: false, 1482 link: function (scope, element, attrs) { 1483 attrs.$observe('uiSendGroup', function (newVal) { scope.sendGroup = newVal || scope.apiName; }) 1484 scope.sendCommand = function (commandName, data) { 1485 SendCommandService.sendCommand(scope.sendGroup, commandName, data); 1486 } 1487 } 1488 }; 1489}]); 1490 1491/*Service for inter component comunication*/ 1492angular.module("DataUI").service('SendCommandService', ['$rootScope', '$timeout', function ($rootScope, $timeout) { 1493 var svc = { 1494 dataCommands: { 1495 ready: 'Ready', 1496 loaded: 'DataLoaded', 1497 selected: 'RowSelected', 1498 added: 'RowAdded', 1499 deleted: 'RowDeleted', 1500 updated: 'RowUpdated', 1501 }, 1502 sendCommand: function (sendGroup, commandName, data) { 1503 $timeout(function () { 1504 console.log(sendGroup + ' send a ' + commandName + ' command.'); 1505 $rootScope.$broadcast('uiCommand', { sendGroup: sendGroup, commandName: commandName, row: data }); 1506 }, 50); 1507 } 1508 } 1509 return svc; 1510}]); 1511 1512/*Directive for file href downloads*/ 1513angular.module("DataUI").directive('uiFileDownloadHref', ['CommonApi', function (CommonApi) { 1514 return { 1515 restrict: 'A', 1516 scope: { 1517 file: '=uiFileDownloadHref', 1518 apiName: '@uiFileApiName' 1519 }, 1520 link: function (scope, element, attrs) { 1521 var _api = CommonApi.createApi(scope.apiName || CommonApi.defaultApis.file.apiName); 1522 scope.$watch('file', function (newVal) { 1523 if (newVal) { 1524 var parts = newVal.toString().split("|"); 1525 var _id = parts[0] || 0; 1526 var _Location= (parts.length >= 2 && parts[2] && parts[2] != 'undefined' ? parts[2] : '') 1527 if (_Location) { 1528 $(element).attr('href', _Location); 1529 } 1530 else if (_id) { 1531 var downloadUrl = _api.getUrl('Download', { Id: _id }); 1532 $(element).attr('href', downloadUrl).attr('target', '_blank'); 1533 } 1534 } 1535 }) 1536 } 1537 } 1538}]); 1539 1540/*Directive for file src downloads*/ 1541angular.module("DataUI").directive('uiFileDownloadSrc', ['CommonApi', function (CommonApi) { 1542 return { 1543 restrict: 'A', 1544 scope: { 1545 file: '=uiFileDownloadSrc', 1546 apiName: '@uiFileApiName' 1547 }, 1548 link: function (scope, element, attrs) { 1549 var _api = CommonApi.createApi(scope.apiName || CommonApi.defaultApis.file.apiName); 1550 scope.$watch('file', function (newVal) { 1551 if (newVal) { 1552 var parts = newVal.toString().split("|"); 1553 var _id = parts[0] || 0; 1554 var _filename = parts[1] || ''; 1555 var _location = (parts.length >= 2 && parts[2] && parts[2] != 'undefined' ? parts[2] : ''); 1556 1557 if (_location) { 1558 $(element).attr('src', _location); 1559 } 1560 else if (_id) { 1561 var downloadUrl = _api.getUrl('Download', { Id: _id }); 1562 $(element).attr('src', downloadUrl); 1563 } 1564 } 1565 }) 1566 } 1567 } 1568}]); 1569 1570/*Directive for file src downloads*/
1571angular.module("DataUI").directive('uiFileThumbnailSrc', ['CommonApi', function (CommonApi) { 1572 return { 1573 restrict: 'A', 1574 scope: { 1575 file: '=uiFileThumbnailSrc', 1576 maxWidth: '@?maxWidth', 1577 maxHeight: '@?maxHeight', 1578 apiName: '@uiFileApiName' 1579 }, 1580 link: function (scope, element, attrs) { 1581 var _api = CommonApi.createApi(scope.apiName || attrs.uiApiName || CommonApi.defaultApis.file.apiName); 1582 scope.$watch('file', function (newVal) { 1583 if (newVal) { 1584 var parts = newVal.toString().split("|"); 1585 _id = parts[0] || 0; 1586 if (_id) { 1587 var p = { Id: _id }; 1588 if (scope.maxHeight) p.maxHeight = scope.maxHeight; 1589 if (scope.maxWidth) p.maxWidth = scope.maxWidth; 1590 var downloadUrl = _api.getUrl('Thumbnail', p); 1591 $(element).attr('src', downloadUrl); 1592 } 1593 } 1594 }) 1595 } 1596 } 1597}]); 1598 1599 1600/*Filter to convert fileRefs in URL*/ 1601angular.module("DataUI").filter('fileRefUrl', ['CommonApi', function (CommonApi) { 1602 var _api = CommonApi.createApi(CommonApi.defaultApis.file.apiName); 1603 var f = function (val) { 1604 if (val) { 1605 var parts = val.toString().split("|"); 1606 var _id = parts[0] || 0; 1607 var _location = (parts.length >= 2 && parts[2] && parts[2] != 'undefined' ? parts[2] : ''); 1608 1609 if (_location) return _location; 1610 if (_id) return _api.getUrl('Download', { Id: _id }); 1611 } 1612 1613 return ''; 1614 }; 1615 1616 return f; 1617}]); 1618 1619angular.module("DataUI").directive('uiDataTableTools', ['CommonApi', '$rootScope', '$compile', function (CommonApi, $rootScope, $compile) { 1620 return { 1621 restrict: 'EA', 1622 scope: false, 1623 priority: 100, 1624 templateUrl: function (element, attrs) { return attrs.uiTemplate || 'uiApiTableTools.html'; }, 1625 link: function (scope, element, attrs) { 1626 scope.toggleFilters = function () { 1627 scope.listSettings.hideFilters = !scope.listSettings.hideFilters; 1628 } 1629 } 1630 }; 1631}]); 1632 1633 1634/*Filters*/ 1635 1636/*Default filter attribute: - proxy for templated field from FormsUI*/ 1637angular.module("DataUI").directive('uiDataFieldFilter', ['ParseContextUtils', '$window', '$location', function (ParseContextUtils, $window, $location) { 1638 return { 1639 restrict: 'A', 1640 priority: 1000, 1641 scope: { 1642 settings: '=uiDataFieldFilter', 1643 data: '=data', 1644 urlSync: '@?uiUrlSync', 1645 filterOperator: '=?uiDataFieldFilterOperator', 1646 }, 1647 template:'<div ui-field="field" data="data" ui-view-mode="edit" ui-split-complex-id="false" ui-form-data="formData"></div>', 1648 link: function (scope, element, attrs) { 1649 scope.formData = scope.$parent; 1650 //Create field definition 1651 scope.field = angular.copy(scope.settings); 1652 scope.field.IgnoreAditionalFields = true; 1653 scope.field.ClearIfInvalid = true; 1654 var _lastLocation = $location.url(); 1655 1656 var updateOperator = function () { 1657 if (scope.filterOperator) { 1658 if (scope.settings && scope.settings.ForceOperator) { 1659 scope.filterOperator[scope.field.Id] = scope.settings.ForceOperator; 1660 } 1661 else { 1662 if (scope.field.FieldType == 'number') 1663 scope.filterOperator[scope.field.Id] = '(eq)'; 1664 else if (scope.field.FieldType == 'text') 1665 scope.filterOperator[scope.field.Id] = '(lk)'; 1666 else 1667 scope.filterOperator[scope.field.Id] = ''; 1668 } 1669 } 1670 } 1671 1672 //apply default values to fields 1673 if (scope.field.defaultValue && (!angular.isDefined(scope.data[scope.field.Id]) || scope.data[scope.field.Id] == null)) { 1674 var defaultValue = scope.field.defaultValue; 1675 if (defaultValue) { 1676 defaultValue = ParseContextUtils.evalInContext(defaultValue, scope.data); 1677 } 1678 scope.data[scope.field.Id] = defaultValue; 1679 } 1680 1681 1682 var getUrlVars = function (){ 1683 var vars = [], hash; 1684 var hashes = $window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); 1685 for (var i = 0; i < hashes.length; i++) { 1686 hash = hashes[i].split('='); 1687 vars[hash[0]] = decodeURIComponent(hash[1]); 1688 } 1689 return vars; 1690 } 1691 1692 //Sync filter from URL 1693 if (!scope.urlSync || scope.urlSync != 'none') { 1694 //First load 1695 var newVal = getUrlVars()[scope.field.Id]; 1696 if (newVal) { 1697 scope.data[scope.field.Id] = newVal; 1698 updateOperator(); 1699 } 1700 1701 scope.$on('$locationChangeSuccess', function (event) { 1702 if (_lastLocation == $location.url()) return; 1703 _lastLocation = $location.url(); 1704 //var newVal = $location.search()[scope.field.Id]; 1705 var newVal = getUrlVars()[scope.field.Id]; 1706 if (newVal != scope.data[scope.field.Id]) 1707 scope.data[scope.field.Id] = newVal; 1708 updateOperator(); 1709 }); 1710 } 1711 //Sync filter to URL 1712 if (scope.urlSync == 'full') { 1713 scope.$watch(function () { return scope.data[scope.field.Id] }, function (newVal) { 1714 if (newVal) 1715 $location.search(scope.field.Id, newVal); 1716 else 1717 $location.search(scope.field.Id, null); 1718 }); 1719 } 1720 1721 //Init operator if necessary 1722 updateOperator(); 1723 } 1724 }; 1725}]); 1726 1727/*Default filter attribute: - proxy for templated field from FormsUI*/ 1728angular.module("DataUI").directive('uiDataFieldRangeFilter', ['$location', '$window', '$translate', function ($location, $window, $translate) { 1729 return { 1730 restrict: 'A', 1731 scope: { 1732 settings: '=uiDataFieldRangeFilter', 1733 data: '=data', 1734 urlSync: '@?uiUrlSync' 1735 }, 1736 template: [ 1737 '<div>', 1738 ' <div class="row">', 1739 ' <div class="col-xs-6"><div ui-field="fieldMin" data="data[settings.Id].range" ui-view-mode="edit"></div></div>', 1740 ' <div class="col-xs-6"><div ui-field="fieldMax" data="data[settings.Id].range" ui-view-mode="edit"></div></div>', 1741 ' </div>', 1742 '</div>'].join(''), 1743 link: function (scope, element, attrs) { 1744 //Create field definition 1745 1746 scope.fieldMin = angular.copy(scope.settings); 1747 scope.fieldMin.Label = scope.settings.Label + ' ' + $translate.instant('algoapps.From'); 1748 scope.fieldMin.Id = 'from'; 1749 scope.fieldMin.IgnoreAditionalFields = true; 1750 1751 scope.fieldMax = angular.copy(scope.settings); 1752 scope.fieldMax.Label = '' + $translate.instant('algoapps.To'); 1753 scope.fieldMax.Id = 'to'; 1754 scope.fieldMax.IgnoreAditionalFields = true; 1755 1756 //Commented because field directive does nor support changes to field label (during init phase the field info is copyed and then it is never used again). 1757 //$translate('algoapps.From').then(function (t) { scope.fieldMin.Label = scope.settings.Label + ' ' + t.toString(); }, function (t) { scope.fieldMin.Label = scope.settings.Label + ' ' + t.toString(); }); 1758 //$translate('algoapps.To').then(function (t) { scope.fieldMax.Label = t.toString(); }, function (t) { scope.fieldMax.Label = t.toString(); }); 1759 1760 scope.$watch('data', function (newVal) { 1761 if (newVal) { 1762 if (!scope.data[scope.settings.Id]) scope.data[scope.settings.Id] = {}; 1763 if (!scope.data[scope.settings.Id].range) scope.data[scope.settings.Id].range = {}; 1764 1765 if (scope.settings.FieldType == 'date' && scope.settings.IncludeLastDay) { 1766 var range = scope.data[scope.settings.Id].range; 1767 if (range.to) { 1768 scope.data[scope.settings.Id].range.to = moment(range.to).endOf('Day').format('YYYY-MM-DDTHH:mm:ssZ'); 1769 } 1770 } 1771 } 1772 }, true); 1773 1774 var getUrlVars = function () { 1775 var vars = [], hash; 1776 var hashes = $window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); 1777 for (var i = 0; i < hashes.length; i++) { 1778 hash = hashes[i].split('='); 1779 vars[hash[0]] = hash[1]; 1780 } 1781 return vars; 1782 } 1783
1784 var _locationChangedHandler = function (event) { 1785 if (!scope.data) return; 1786 var kMin = scope.settings.Id + '.' + scope.fieldMin.Id; 1787 var kMax = scope.settings.Id + '.' + scope.fieldMax.Id; 1788 var search = getUrlVars(); 1789 var newValMin = search[kMin]; 1790 var newValMax = search[kMax]; 1791 var d = scope.data[scope.settings.Id] || {}; 1792 if (!d.range) d.range = {}; 1793 if (!_.isUndefined(search[kMin]) && newValMin != d.range[scope.fieldMin.Id]) { 1794 if (!scope.data[scope.settings.Id]) scope.data[scope.settings.Id] = {}; 1795 if (!scope.data[scope.settings.Id].range) scope.data[scope.settings.Id].range = {}; 1796 scope.data[scope.settings.Id].range[scope.fieldMin.Id] = newValMin 1797 }; 1798 if (!_.isUndefined(search[kMax]) && newValMax != d.range[scope.fieldMax.Id]) { 1799 if (!scope.data[scope.settings.Id]) scope.data[scope.settings.Id] = {}; 1800 if (!scope.data[scope.settings.Id].range) scope.data[scope.settings.Id].range = {}; 1801 scope.data[scope.settings.Id].range[scope.fieldMax.Id] = newValMax 1802 }; 1803 } 1804 1805 //Sync filter with URL 1806 if (!scope.urlSync || scope.urlSync != 'none') { 1807 1808 //First load
1809 _locationChangedHandler(); 1810 1811 scope.$on('$locationChangeSuccess', _locationChangedHandler); 1812 } 1813 if (scope.urlSync == 'full') { 1814 scope.$watch(function () { return scope.data[scope.settings.Id] }, function (newVal) { 1815 var d = scope.data[scope.settings.Id]; 1816 if (!d.range) d.range = {}; 1817 var newValMin = d.range[scope.fieldMin.Id]; 1818 var newValMax = d.range[scope.fieldMax.Id]; 1819 $location.search(scope.settings.Id + '.' + scope.fieldMin.Id, newValMin ? newValMin : null); 1820 $location.search(scope.settings.Id + '.' + scope.fieldMax.Id, newValMax ? newValMax : null); 1821 }, true); 1822 } 1823 1824 1825 } 1826 }; 1827}]); 1828/*Default filter attribute:*/ 1829angular.module("DataUI").directive('uiDataFilters', [function () { 1830 return { 1831 restrict: 'A', 1832 priority: 1000, 1833 scope: false, 1834 link: { 1835 pre: function (scope, element, attrs) { 1836 attrs.$observe("uiDataFilters", function (newVal) { 1837 if (!scope.queryParams) scope.queryParams = {}; 1838 if (!scope.queryParams.filter) scope.queryParams.filter = {}; 1839 angular.extend(scope.queryParams.filter, angular.fromJson(newVal)); 1840 1841 }); 1842 } 1843 } 1844 }; 1845}]); 1846 1847 1848/*Select filter*/ 1849angular.module("DataUI").directive('uiSelectFilter', ['CommonApi', function (CommonApi) { 1850 return { 1851 restrict: 'A', 1852 priority: 1000, 1853 scope: { 1854 ngModel: '=ngModel', 1855 options: '@uiOptions', 1856 apiOptions: '@uiApiOptions', 1857 defaultOption: '@uiDefaultOption', 1858 emptyLabel: '@uiEmptyOption' 1859 }, 1860 template: [ 1861 '<div class="btn-group">', 1862 ' <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">', 1863 ' {{selectedOption.Label}} <span class="caret"></span>', 1864 ' </button>', 1865 ' <ul class="dropdown-menu">', 1866 ' <li ng-repeat="option in optionsArray" ng-click="select(option)" ng-class="{\'active\':selectedOption.Id==option.Id}"><a href="#">{{option.Label}}</a></li>', 1867 ' </ul>', 1868 '</div>' 1869 ].join(''), 1870 link: function (scope, element, attrs) { 1871 scope.selectedOption = { Id: '', Label: '' }; 1872 scope.emptyOption = { Id: '', Label: '' }; 1873 scope.optionsArray = new Array(); 1874 1875 /*Build filter*/ 1876 var _buildFilter = function () { 1877 if (scope.options) { 1878 scope.optionsArray = new Array(); 1879 _.forEach(scope.options.split(','), function (o) { 1880 var p = o.split('|'); 1881 scope.optionsArray.push({ Id: p[0], Label: p[1] || p[0] }); 1882 }); 1883 }; 1884 //Load from API 1885 if (scope.apiOptions) { 1886 CommonApi.all(scope.apiOptions).getList().then(function (data) { 1887 scope.optionsArray = data; 1888 scope.optionsArray.push(scope.emptyOption); 1889 }); 1890 }; 1891 1892 if (!scope.selectedOption.Id && scope.defaultOption) 1893 { 1894 var p = scope.defaultOption.split('|'); 1895 scope.selectedOption = { Id: p[0], Label: p[1] || p[0] }; 1896 } 1897 1898 if (scope.emptyLabel) scope.emptyOption.Label = scope.emptyLabel; 1899 } 1900 var buildFilter = _.debounce(_buildFilter, 100); 1901 scope.$watch('uiOptions', buildFilter); 1902 scope.$watch('uiDefaultOption', buildFilter); 1903 scope.$watch('uiEmptyOption', buildFilter); 1904 1905 /*Change Filter*/ 1906 scope.select = function (newVal) { 1907 scope.selectedOption = newVal; 1908 } 1909 scope.$watch('selectedOption.Id', function (newVal) { 1910 scope.ngModel = newVal; 1911 }); 1912 scope.$watch('ngModel', function (newVal) { 1913 if (newVal == scope.selectedOption.Id) return; 1914 scope.selectedOption = _.find(scope.optionsArray, function (o) { return o.Id == newVal; }); 1915 }); 1916 } 1917 }; 1918}]); 1919 1920/*Select filter*/ 1921angular.module("DataUI").directive('uiCustomListFilter', ['CommonApi', '$compile', function (CommonApi, $compile) { 1922 return { 1923 restrict: 'A', 1924 priority: 100, 1925 scope: { 1926 ngModel: '=ngModel',
1927 _apiName: '@uiApiName', //<--Obsolete because it conflicts with the parent scope API 1928 apiName: '@uiListApiName' 1929 }, 1930 template: [ 1931 '<div></div>' 1932 ].join(''), 1933 transclude:true, 1934 link: function (scope, element, attrs, ctrl, transcludeFn) { 1935 scope.selectedOption = { Id: '', Label: '' }; 1936 scope.$data = new Array(); 1937 if (scope.ngModel) scope.selectedOption.Id = scope.ngModel; 1938 1939 scope.innerTemplate = '<div>{{$data | json}}</div>'; 1940 transcludeFn(scope, function (content, innerScope) { 1941 element.append(content); 1942 }, null); 1943 1944 /*Build filter*/ 1945 var _buildFilter = function () { 1946 //Load from API 1947 var _apiName = scope.apiName || scope._apiName; 1948 if (_apiName) { 1949 CommonApi.all(_apiName).getList().then(function (data) { 1950 scope.$data = data; 1951 }); 1952 }; 1953 } 1954 1955 var buildFilter = _.debounce(_buildFilter, 100); 1956 scope.$watch('_apiName', buildFilter); 1957 scope.$watch('apiName', buildFilter); 1958 1959 /*Change Filter*/ 1960 scope.select = function (newVal) { 1961 scope.selectedOption = newVal; 1962 } 1963 scope.toggle = function (newVal) { 1964 if (scope.isSelected(newVal)) { 1965 scope.select(null); 1966 } 1967 else { 1968 scope.select(newVal); 1969 } 1970 } 1971 scope.isSelected = function (v) { 1972 return (v && scope.selectedOption && v.Id == scope.selectedOption.Id); 1973 } 1974 scope.$watch('selectedOption.Id', function (newVal) { 1975 scope.ngModel = newVal; 1976 }); 1977 scope.$watch('ngModel', function (newVal) { 1978 if (scope.selectedOption && newVal == scope.selectedOption.Id) return; 1979 scope.selectedOption = _.find(scope.$data, function (o) { return o.Id == newVal; }); 1980 }); 1981 1982 } 1983 }; 1984}]); 1985 1986 1987/*Date range filters*/ 1988angular.module("DataUI").directive('uiDateRangeFilter', [function () { 1989 return { 1990 restrict: 'A', 1991 priority: 1000, 1992 scope: { 1993 filter: '=?uiDateRangeFilter', 1994 from: '=?uiModelMin', 1995 to: '=?uiModelMax', 1996 options: '@uiOptions', 1997 defaultOption: '@uiDefaultOption' 1998 }, 1999 template: '<div class="btn-group"><button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><span class="button-text">{{filterValue.selectedOption.label}}</span> <span class="caret"></span></button><ul class="dropdown-menu"><li ng-repeat="option in optionsArray" ng-click="filterValue.selectedOption.id=option.id" ng-class="{\'active\':filterValue.selectedOption.id==option.id}"><a href="#">{{option.label}}</a></li></ul></div>', 2000 link: function (scope, element, attrs) { 2001 scope.filterValue = { selectedOption: {} }; 2002 scope.optionsArray = new Array(); 2003 2004 2005 /*Utility to parse text to date intervals 2006 To Do: Create text to date interval service 2007 To do: Add rules for Up To and From To enable setting only start or end dates or both. 2008 */ 2009 var parseHumanDateString = function (newVal) { 2010 //Will retur this structure: with start, end and isValid 2011 2012 /*Regular expressions for formats*/ 2013 var start, end, isValid = false; 2014 //Today 2015 if (/today/gi.test(newVal)) { 2016 start = moment().startOf('day'); 2017 end = start.clone().endOf('day'); 2018 isValid = true; 2019 } 2020 //Yesterday 2021 else if (/yesterday/gi.test(newVal)) { 2022 start = moment().subtract(1, 'day').startOf('day'); 2023 end = start.clone().endOf('day'); 2024 isValid = true; 2025 } 2026 //tomorow 2027 else if (/tomor/gi.test(newVal)) { 2028 start = moment().add(1, 'day').startOf('day'); 2029 end = start.clone().endOf('day'); 2030 isValid = true; 2031 } 2032 //xx days ago 2033 else if (/(\d{1,3}) (year|month|week|day|hour|minute|second)(s)? ago/gi.test(newVal)) { 2034 var r = /(\d{1,3}) (year|month|week|day|hour|minute|second)(s)? ago/gi; 2035 var m = r.exec(newVal); 2036 start = moment().subtract(m[1], m[2]).startOf(m[2]); 2037 end = start.clone().endOf(m[2]); 2038 isValid = true; 2039 } 2040 //Last xx Days, Months, ... 2041 else if (/last (\d{1,3})?.*(year|month|week|day|hour|minute|second)(s)?/gi.test(newVal)) { 2042 var r = /last (\d{1,3})?.*(year|month|week|day|hour|minute|second)(s)?/gi; 2043 var m = r.exec(newVal); 2044 start = moment().subtract(m[1] || 1, m[2] || 'day').startOf(m[2] || 'day'); 2045 end = start.clone().add((m[1] || 1) , m[2] || 'day').endOf(m[2] || 'day'); 2046 isValid = true; 2047 } 2048 //Next xx Days, Months, ... 2049 else if (/next (\d{1,3})?.*(year|month|week|day|hour|minute|second)(s)?/gi.test(newVal)) { 2050 var r = /next (\d{1,3})?.*(year|month|week|day|hour|minute|second)(s)?/gi; 2051 var m = r.exec(newVal); 2052 start = moment().add(0, m[2] || 'day').startOf(m[2] || 'day'); 2053 end = start.clone().add(m[1] || 0, m[2] || 'day').endOf(m[2] || 'day'); 2054 isValid = true; 2055 } 2056 //Day, Month, year before 2057 else if (/(year|month|week|day|hour|minute|second) before/gi.test(newVal)) { 2058 var r = /(year|month|week|day|hour|minute|second) before/gi; 2059 var m = r.exec(newVal); 2060 start = moment().add(-1, m[1]).startOf(m[1] || 'day'); 2061 end = start.clone().endOf(m[1] || 'day'); 2062 isValid = true; 2063 } 2064 //This Day, Month, ... 2065 else if (/This (year|month|week|day|hour|minute|second)(s)?/gi.test(newVal)) { 2066 var r = /This (year|month|week|day|hour|minute|second)(s)?/gi; 2067 var m = r.exec(newVal); 2068 start = moment().startOf(m[1] || 'day'); 2069 end = start.clone().endOf(m[1] || 'day'); 2070 isValid = true; 2071 } 2072 else { 2073 } 2074 2075 return { start: start, end: end, isValid: isValid }; 2076 } 2077 2078 2079 /*Change Filter*/ 2080 var setFilterFromText = function (newVal) { 2081 2082 if (!newVal || newVal == 'ALL') { 2083 //Null clears filters 2084 scope.filterValue.selectedLabel = scope.filterValue.selectedText; 2085 scope.filterValue.from = null; 2086 scope.filterValue.to = null; 2087 if (attrs.uiDateRangeFilter) scope.filter = { range: { from: null, to: null } };
2088 if (attrs.uiModelMin) scope.from = null; 2089 if (attrs.uiModelMax) scope.to = null; 2090 return; 2091 } 2092 2093 var _interval = parseHumanDateString(newVal); 2094 2095 if (_interval.isValid) { 2096 scope.filterValue.selectedLabel = scope.filterValue.selectedText; 2097 scope.filterValue.from = _interval.start; 2098 scope.filterValue.to = _interval.end; 2099 if (attrs.uiDateRangeFilter) scope.filter = { range: { from: _interval.start.toISOString(), to: _interval.end.toISOString() } }; 2100 if (attrs.uiModelMin) scope.from = _interval.start.toISOString(); 2101 if (attrs.uiModelMax) scope.to = _interval.end.toISOString(); 2102 } 2103 2104 }; 2105 scope.$watch('filterValue.selectedOption.id', setFilterFromText); 2106 2107 /*Build filter*/ 2108 scope.buildFilter = function () { 2109 /*Options*/ 2110 if (scope.options) { 2111 scope.optionsArray = new Array(); 2112 _.forEach(scope.options.split(','), function (o) { 2113 var p = o.split('|'); 2114 var id = p[0]; var label = p[1] || p[0]; 2115 scope.optionsArray.push({id:id,label:label}); 2116 }); 2117 } 2118 /*Default selection*/ 2119 if (!scope.filterValue.selectedOption.id && scope.defaultOption) { 2120 var p = scope.defaultOption.split('|'); 2121 var id = p[0]; var label = p[1] || p[0]; 2122 scope.filterValue.selectedOption = { id: id, label: label }; 2123 }; 2124 setFilterFromText(scope.filterValue.selectedOption.id); 2125 } 2126 2127 scope.$watch('uiOptions', scope.buildFilter); 2128 scope.$watch('uiDefaultOption', scope.buildFilter); 2129 //init 2130 scope.buildFilter(); 2131 2132 /*Filter changed => reflect changes in selected label*/ 2133 var filterChanged = function () { 2134 var _start = moment.invalid(); 2135 var _end = moment.invalid(); 2136 2137 if (attrs.uiDateRangeFilter) _start = moment(scope.filter.range.from); 2138 if (attrs.uiDateRangeFilter) _end = moment(scope.filter.range.to); 2139 if (attrs.uiModelMin) _start = moment(scope.from); 2140 if (attrs.uiModelMax) _end = moment(scope.to); 2141 2142 2143 //TODO: Get range label from Options. 2144 2145 if (_start.isValid() && _end.isValid()) { 2146 scope.filterValue.selectedOption.label = _start.format("MMM D") + " - " + _end.format("MMM D"); 2147 } 2148 else if (_start.isValid()) 2149 { 2150 scope.filterValue.selectedOption.label = _start.format("MMM D"); 2151 } 2152 else if (_end.isValid()) 2153 { 2154 scope.filterValue.selectedOption.label = _end.format("MMM D"); 2155 } 2156 else 2157 { 2158 scope.filterValue.selectedOption.label = ""; 2159 } 2160 }; 2161 scope.$watch('filter.range.from', filterChanged); 2162 scope.$watch('filter.range.to', filterChanged); 2163 scope.$watch('from', filterChanged); 2164 scope.$watch('to', filterChanged); 2165 2166 2167 2168 } 2169 }; 2170}]); 2171 2172 2173/*Date range slider*/ 2174angular.module("DataUI").directive('uiDateRangeSlider', ['$timeout', function ($timeout) { 2175 return { 2176 restrict: 'A', 2177 priority: 1000, 2178 scope: { 2179 from: '=uiModelMin', 2180 to: '=uiModelMax', 2181 rangeMonths: '@uiRangeMonths' 2182 }, 2183 template: '<rzslider class="ui-slider" rz-slider-model="slider.min" rz-slider-high="slider.max" rz-slider-options="slider.options"></rzslider>', 2184 link: function (scope, element, attrs) { 2185 scope.slider = { 2186 min: moment(scope.from).unix(), 2187 max: moment(scope.to).unix(), 2188 options: { 2189 floor: moment().subtract(scope.rangeMonths || 3, 'month').startOf('day').unix(),/*To Do: Pass as attribute*/ 2190 ceil: moment().add(scope.rangeMonths || 3, 'month').startOf('day').unix(),/*To Do: Pass as attribute*/ 2191 step: 24 * 60 * 60, 2192 draggableRange: true, 2193 translate: function (value) { 2194 return moment.unix(value).format('MMM D'); 2195 }, 2196 onEnd: function (sliderId, modelValue, highValue, pointerType) { 2197 scope.from = moment.unix(modelValue).startOf('day').toISOString(); 2198 scope.to = moment.unix(highValue).endOf('day').toISOString(); 2199 } 2200 } 2201 }; 2202 2203 /*Filter changed => reflect changes on scope To slider*/ 2204 var filterChanged = function () { 2205 scope.slider.min = moment(scope.from).startOf('day').unix(); 2206 scope.slider.max = moment(scope.to).startOf('day').unix(); 2207 2208 if (scope.slider.min < scope.slider.options.floor) scope.slider.options.floor = scope.slider.min; 2209 if (scope.slider.max > scope.slider.options.ceil) scope.slider.options.ceil = scope.slider.max; 2210 2211 $timeout(function () { scope.$broadcast('rzSliderForceRender'); }); 2212 }; 2213 scope.$watch('from', filterChanged); 2214 scope.$watch('to', filterChanged); 2215 2216 } 2217 }; 2218}]); 2219 2220/*Pagination*/ 2221angular.module("DataUI").directive('uiPagination', [function () { 2222 return { 2223 restrict: 'A', 2224 priority: 1000, 2225 scope: { 2226 listSettings: '=uiPagination' 2227 }, 2228 templateUrl: 'uiTablePagination.html',
2229 link: function (scope, element, attrs) { 2230 scope.pagination = { 2231 showPrev: false, 2232 showNext: false, 2233 firstPage: 0, 2234 middlePages: new Array(), 2235 middleSize: 3, 2236 lastPage: 0, 2237 pagesize: 20, 2238 total:0 2239 } 2240 2241 scope.buildPages = function () { 2242 scope.pagination.total = (scope.listSettings || {}).total || 0; 2243 scope.pagination.page = (scope.listSettings || {}).page || 0; 2244 scope.pagination.pagesize = (scope.listSettings || {}).pagesize || 20; 2245 2246 scope.pagination.showFirst = true; 2247 scope.pagination.showLast = true; 2248 scope.pagination.lastPage = Math.max(0, Math.floor((scope.pagination.total-1) / scope.pagination.pagesize)); 2249 scope.pagination.showPrev = (scope.pagination.page > 0) 2250 scope.pagination.showNext = (scope.pagination.page < scope.pagination.lastPage) 2251 2252 var middleStart = Math.max(0, scope.pagination.page - Math.floor((scope.pagination.middleSize-1)/2)); 2253 var middleEnd = Math.min(scope.pagination.lastPage ,middleStart + scope.pagination.middleSize); 2254 scope.pagination.middlePages = new Array(); 2255 for (var p = middleStart; p <= middleEnd; p++) 2256 { 2257 scope.pagination.middlePages.push(p); 2258 if (p == scope.pagination.firstPage) scope.pagination.showFirst = false; 2259 if (p == scope.pagination.lastPage) scope.pagination.showLast = false; 2260 } 2261 if (scope.pagination.lastPage < scope.pagination.page) 2262 { 2263 scope.ChangePage(scope.pagination.lastPage); 2264 } 2265 2266 if (scope.pagination.total <= scope.pagination.pagesize) { 2267 scope.pagination.showFirst = false; 2268 scope.pagination.showLast = false; 2269 scope.pagination.middlePages = new Array(); 2270 } 2271 } 2272 scope.$watch('listSettings.total', scope.buildPages); 2273 scope.$watch('listSettings.page', scope.buildPages); 2274 scope.$watch('listSettings.pagesize', scope.buildPages); 2275 2276 scope.ChangePage = function (newPage) { 2277 scope.pagination.page = newPage; 2278 if (scope.pagination.page < 0) scope.pagination.page = 0; 2279 if (scope.pagination.page > scope.pagination.lastPage) scope.pagination.page = scope.pagination.lastPage; 2280 scope.listSettings.page = scope.pagination.page; 2281 } 2282 } 2283 }; 2284}]); 2285 2286angular.module("DataUI").directive('uiPaginationSize', [function () { 2287 return { 2288 restrict: 'A', 2289 priority: 1000, 2290 scope: { 2291 listSettings: '=uiPaginationSize' 2292 }, 2293 templateUrl: 'uiTablePaginationSize.html', 2294 link: function (scope, element, attrs) { 2295 scope.pagination = { 2296 pagesize: 20, 2297 pagesizes: new Array() 2298 } 2299 2300 scope.buildPages = function () { 2301 scope.pagination.pagesize = scope.listSettings.pagesize || 20; 2302 var pageSizes = scope.listSettings.pagesizes; 2303 if (!pageSizes || pageSizes == 0) pageSizes = [5, 10, 20, 50, 100, 200]; 2304 /*Todo: Only allow page sizes that are smaller that the actual record count*/ 2305 scope.pagination.pagesizes = pageSizes; 2306 } 2307 scope.$watch('listSettings.pagesize', scope.buildPages); 2308 scope.$watch('listSettings.total', scope.buildPages); 2309 2310 scope.Changepagesize = function (newpagesize) { 2311 scope.pagination.pagesize = newpagesize; 2312 scope.listSettings.pagesize = scope.pagination.pagesize; 2313 } 2314 } 2315 }; 2316}]); 2317 2318angular.module("DataUI").directive('uiOrderBy', [function () { 2319 return { 2320 restrict: 'A', 2321 priority: 1000, 2322 scope: false, /*will depend on parent scope listSettings*/ 2323 link: function (scope, element, attrs) { 2324 $(element).addClass('orderBy'); 2325 2326 $(element).click(function () { 2327 scope.$applyAsync(function () { 2328 var thisOrder = attrs.uiOrderBy; 2329 if (!thisOrder) return; 2330 var current = ""; 2331 if (scope.listSettings.order) current = scope.listSettings.order.join(','); 2332 var newOrder = thisOrder; 2333 if (newOrder == current || '+' + newOrder == current) 2334 { newOrder = '-' + newOrder;} 2335 scope.listSettings.order = [newOrder]; 2336 }); 2337 }); 2338 2339 var setIcon = function () { 2340 $(element).removeClass('orderByAsc').removeClass('orderByDesc'); 2341 var thisOrder = attrs.uiOrderBy; 2342 if (!thisOrder || !scope.listSettings) return; 2343 _.forEach(scope.listSettings.order, function (o) { 2344 if (thisOrder == o || '+' + thisOrder == o) { 2345 $(element).addClass('orderByAsc'); 2346 } 2347 else if ('-' + thisOrder == o) { 2348 $(element).addClass('orderByDesc'); 2349 } 2350 }); 2351 } 2352 2353 scope.$watch('listSettings.order', setIcon, true); 2354 2355 } 2356 }; 2357}]); 2358 2359/*Preforms hierarchical parent child data transformation. Result will be stored in an array $data where each item has a Children array*/ 2360angular.module("DataUI").directive('uiHierarchyData', ['$timeout', function ($timeout) { 2361 return { 2362 restrict: 'A', 2363 scope: { 2364 data: '=uiData', 2365 idField: '@?uiIdField', 2366 parrentIdField: '@?uiParentIdField' 2367 }, 2368 transclude: true, 2369 link: function (scope, element, attrs, ctrl, transcludeFn) { 2370 scope.parentScope = scope.$parent; 2371 scope.$data = new Array(); 2372 scope.root = {}; 2373 2374 transcludeFn(scope, function (clone, scope) { 2375 element.append(clone); 2376 }); 2377 2378 var _doTransform = function () { 2379 scope.root.children = []; 2380 2381 //Clear previous child calculations: 2382 _.forEach(scope.data, function (o) { if (o && o.children) o.children = []; }); 2383 //Assign each child to its parent. When no parent found assign to root. 2384 _.forEach(scope.data, function (o) { 2385 var pID = (o && (o[(scope.parrentIdField || 'ParentId')] || 0)); 2386 var p = _.find(scope.data, function (i) { return ((pID) == (i[(scope.idField || 'Id')] || 0)); }); 2387 if (!p) p = scope.root; 2388 if (!p.children) p.children = []; 2389 p.children.push(o); 2390 }); 2391 2392 scope.$data = scope.root.children; 2393 scope.$applyAsync(); 2394 } 2395 2396 var doTransform = _.debounce(_doTransform, 50); 2397 scope.$watchCollection('data', doTransform); 2398 } 2399 } 2400}]); 2401 2402 2403/*Converts list data into an object where each key and value will be mapped to list data fields. Result will be stored in an array $data as an object*/ 2404angular.module("DataUI").directive('uiDataKeyBy', ['$timeout', function ($timeout) { 2405 return { 2406 restrict: 'A', 2407 scope: { 2408 data: '=uiDataKeyBy', 2409 aggBy: '@uiDataKeyField', 2410 //valuesBy: '@uiDataValuesField', 2411 }, 2412 transclude: true, 2413 link: function (scope, element, attrs, ctrl, transcludeFn) { 2414 scope.$data = {}; 2415 2416 transcludeFn(scope, function (clone, scope) {element.append(clone);}); 2417 2418 var _doAgg = function () { 2419 2420 var aggFn = function (i) { return i[scope.aggBy]; }; 2421 //var mapFn = function (i) { return (!scope.valuesBy ? i : i[scope.valuesBy]); }; 2422 2423 scope.$data = _.chain(scope.data).keyBy(aggFn).value(
2423); 2424 2425 scope.$applyAsync(); 2426 } 2427 var doAgg = _.debounce(_doAgg, 50); 2428 scope.$watchCollection('data', doAgg); 2429 2430 } 2431 } 2432}]); 2433 2434/*Preforms a agg function like group by and allows calculation of one member. Result will be stored in an array $data*/ 2435angular.module("DataUI").directive('uiAggregateData', ['$timeout', function ($timeout) { 2436 return { 2437 restrict: 'A', 2438 scope: { 2439 data: '=uiAggregateData', 2440 aggBy: '@uiAggregateBy', 2441 aggFunc: '@uiAggregateFunction', 2442 aggMetric: '@uiAggregateMetric' 2443 }, 2444 transclude: true, 2445 link: function (scope, element, attrs, ctrl, transcludeFn) { 2446 scope.$data = new Array(); 2447 2448 transcludeFn(scope, function (clone, scope) { 2449 element.append(clone); 2450 }); 2451 2452 var _doAgg = function () { 2453 var aggFn = function (i) { return i[scope.aggBy]; }; 2454 var mapFn = function (i) { var r = {}; r[(scope.aggBy || 'Key')] = (i[0][scope.aggBy] || 'N.D.'); return r; }; 2455 //Multimple? 2456 var nultipleAggBy = scope.aggBy.split(','); 2457 if (nultipleAggBy.length > 1) { 2458 aggFn = function (i) { return (_.map(nultipleAggBy, function (a) { return i[a] || ''; }).join('-')); }; 2459 var mapFn = function (i) { var r = {}; _.forEach(nultipleAggBy, function (a) { r[a] = (i[0][a] || 'N.D.'); }); return r; }; 2460 } 2461 //aggBy is the group by function. 2462 scope.$data = _(scope.data) 2463 .groupBy(aggFn) 2464 .map(function (objs, key) { 2465 var r = mapFn(objs); 2466 r[(scope.aggMetric || 'Value')] = (scope.aggFunc == 'sum' ? _.sumBy(objs, scope.aggMetric) : objs.length); 2467 return r; 2468 }) 2469 .value(); 2470 scope.$applyAsync(); 2471 } 2472 var doAgg = _.debounce(_doAgg, 50); 2473 scope.$watchCollection('data', doAgg); 2474 2475 } 2476 } 2477}]); 2478 2479/*Preforms a agg function like group by and allows calculation of one member. Result will be stored in an array $data*/ 2480angular.module("DataUI").directive('uiDataAggregate', ['$timeout', function ($timeout) { 2481 return { 2482 restrict: 'A', 2483 scope: { 2484 data: '=uiData', 2485 destVar: '=?uiDataOutputTo', 2486 aggFn: '@?uiGroupByFn', 2487 aggBy: '@?uiGroupBy', 2488 calcFn: '@?uiAggFn', 2489 sortBy: '@?uiSortBy', 2490 alsoWatch:'@?uiWatch', 2491 filterFn: '@?uiFilterFn', 2492 }, 2493 transclude: true, 2494 link: function (scope, element, attrs, ctrl, transcludeFn) { 2495 scope.$data = new Array(); 2496 2497 transcludeFn(scope, function (clone, scope) { 2498 element.append(clone); 2499 }); 2500 2501 var aggFn = function (i) { return i.Id; }; //Default to id 2502 if (scope.aggBy) aggFn = (new Function("with(this) { return function(i){ return _.get(i,'" + scope.aggBy + "');} }")).call(); 2503 if (scope.aggFn) aggFn = (new Function("with(this) { return " + scope.aggFn + "}")).call(); 2504 var calcFn = function (i) { return i.length; }; //Default to count 2505 if (scope.calcFn) calcFn = (new Function("with(this) { return " + scope.calcFn + "}")).call(); 2506 var filterFn = function (i) { return true;}; 2507 if (scope.filterFn) filterFn = (new Function("with(this) { return " + scope.filterFn + "}")).call(scope.$parent); 2508 2509 var _doAgg = function () { 2510 var d = _(scope.data) 2511 .filter(filterFn) 2512 .groupBy(aggFn) 2513 .map(function (objs, key) { 2514 var r = calcFn(objs); 2515 _.set(r, (scope.aggBy || 'Key'), (key || 'N.D.')); 2516 return r; 2517 }) 2518 .sortBy(scope.aggBy || 'Key') 2519 .value(); 2520 if (scope.sortBy) { 2521 var sortOrder = scope.sortBy, sortOrderDesc = false; 2522 if (sortOrder.substring(0, 1) == '-') { sortOrderDesc = true; sortOrder = sortOrder.substring(1);} 2523 d = _.sortBy(d, sortOrder); 2524 if (sortOrderDesc) d = d.reverse(); 2525 } 2526 scope.$data = d; 2527 if (attrs.uiDataOutputTo) scope.destVar = d; 2528 scope.$applyAsync(); 2529 } 2530 var doAgg = _.debounce(_doAgg, 100); 2531 scope.$watch('data', doAgg); 2532 scope.$watchCollection('data', doAgg); 2533 scope.$watch('alsoWatch', doAgg); 2534 2535 } 2536 } 2537}]); 2538 2539/*Preforms a filter function. Result will be stored in an array $data*/ 2540angular.module("DataUI").directive('uiDataFilter', ['$timeout', function ($timeout) { 2541 return { 2542 restrict: 'A', 2543 scope: { 2544 data: '=uiData', 2545 destVar: '=?uiDataOutputTo', 2546 filterFn: '@?uiFilterFn' 2547 }, 2548 transclude: true, 2549 link: function (scope, element, attrs, ctrl, transcludeFn) { 2550 scope.$data = new Array(); 2551 2552 transcludeFn(scope, function (clone, scope) { 2553 element.append(clone); 2554 }); 2555 2556 var filterFn = function (i) { return true; }; 2557 if (scope.filterFn) filterFn = (new Function("with(this) { return " + scope.filterFn + "}")).call(scope.$parent); 2558 2559 var _doAgg = function () { 2560 var d = _(scope.data) 2561 .filter(filterFn) 2562 .value(); 2563 scope.$data = d; 2564 if (attrs.uiDataOutputTo) scope.destVar = d; 2565 scope.$applyAsync(); 2566 } 2567 var doAgg = _.debounce(_doAgg, 50); 2568 scope.$watch('data', doAgg); 2569 scope.$watchCollection('data', doAgg); 2570 scope.$watch('alsoWatch', doAgg); 2571 scope.$watch('filterFn', doAgg); 2572 2573 } 2574 } 2575}]); 2576/*Stores ui-data attribute value in the root scope's variable specified by the attribute ui-set-in-root-scope*/ 2577angular.module("DataUI").directive('uiSetInRootScope', ['$rootScope', function ($rootScope) { 2578 return { 2579 restrict: 'A', 2580 scope: { 2581 data: '=uiData', 2582 destVar: '@uiSetInRootScope' 2583 }, 2584 link: function (scope, element, attrs) { 2585 scope.$watch('data', function (newVal) { 2586 if (scope.destVar) $rootScope[scope.destVar]= newVal; 2587 }); 2588 } 2589 } 2590}]); 2591 2592angular.module("DataUI").directive('uiMockLoad', ['ParseContextUtils','$timeout', function (ParseContextUtils,$timeout) { 2593 return { 2594 restrict: 'A', 2595 transclude: { 2596 uiMockData: "?uiMockData" 2597 }, 2598 scope: false, 2599 template: [ 2600 '<div>', 2601 ' <div ng-transclude="uiMockData" style="display:none;"></div>', 2602 '</div>' 2603 ].join(''), 2604 link: function (scope, element, attrs, ctrl, transcludeFn) { 2605 2606 //Get the onclick js 2607 scope.mockData = ""; 2608 transcludeFn(function (content) { 2609 scope.mockData = content.html(); 2610 }, null, 'uiMockData'); 2611 2612 2613 scope.setMockData = function () { 2614 scope.inProgress = false; 2615 scope.$data = scope.$eval(scope.mockData); 2616 } 2617 scope.loadData = function () { 2618 scope.inProgress = true; 2619 $timeout(scope.setMockData,2000); 2620 } 2621 2622 //init 2623 scope.loadData(); 2624 } 2625 } 2626}]); 2627 2628angular.module("DataUI").controller('UISelectable', ["$scope", function ($scope) { 2629 2630 $scope.isAllSelected = false; 2631 2632 $scope.toggleAllSelection = function (filter) { 2633 if (!$scope.$data) return; 2634 2635 var data = $scope.$data; 2636 //Todo: change allSelected and apply to all rows. 2637 $scope.isAllSelected = !$scope.isAllSelected; 2638 2639 // Apply filtering in rows 2640 if (filter) data = _.filter(data, filter); 2641 2642 _.forEach(data, function (i) { i.isSelected = $scope.isAllSelected; }); 2643 2644 rebuildSelected(); 2645 } 2646 2647 var _selectedRows = []; 2648 var _selectedRowsId = []; 2649 var _rebuildSelected = function () { 2650 _selectedRows.length = 0; 2651 _selectedRowsId.length = 0; 2652 if (!$scope.$data) return; 2653 2654 _($scope.$data) 2655 .filter(function (i) { return i.isSelected === true; }) 2656 .forEach(function (i) { _selectedRows.push(i); _selectedRowsId.push(i.Id); }); 2657 2658 $scope.$applyAsync(); 2659 } 2660 var rebuildSelected = _.throttle(_rebuildSelected, 500, { 'trailing': true, 'leading': true });
2661 2662 $scope.getSelectedRows = function () { 2663 rebuildSelected(); 2664 return _selectedRows; 2665 } 2666 2667 $scope.getSelectedRowsId = function () { 2668 rebuildSelected(); 2669 return _selectedRowsId; 2670 } 2671 2672 $scope.getSelectedRowsCustomField = function (fieldName) { 2673 if (!$scope.$data) return []; 2674 return _($scope.$data) 2675 .filter(function (i) { return i.isSelected === true; }) 2676 .map(function (i) { return i[fieldName]; }) 2677 .value(); 2678 } 2679 2680}]); 2681 2682 2683angular.module("DataUI").directive('uiDataMap', ['$timeout', function ($timeout) { 2684 return { 2685 restrict: 'A', 2686 scope: { 2687 data: '=uiData', 2688 calcFn: '@?uiMapFn', 2689 filterFn: '@?uiFilterFn' 2690 }, 2691 transclude: true, 2692 link: function (scope, element, attrs, ctrl, transcludeFn) { 2693 scope.$data = new Array(); 2694 2695 transcludeFn(scope, function (clone, scope) { 2696 element.append(clone); 2697 }); 2698 2699 var calcFn = function (i) { return i.length; }; //Default to count 2700 var filterFn = function (i) { return true; }; 2701 if (scope.calcFn) calcFn = (new Function("with(this) { return " + scope.calcFn + "}")).call(); 2702 if (scope.filterFn) filterFn = (new Function("with(this) { return " + scope.filterFn + "}")).call(); 2703 2704 var _doMap = function () { 2705 2706 var d = _(scope.data).map(calcFn).filter(filterFn).value(); 2707 2708 scope.$data = d; 2709 2710 scope.$applyAsync(); 2711 } 2712 2713 var doMap = _.debounce(_doMap, 100); 2714 scope.$watch('data', doMap); 2715 2716 } 2717 } 2718}]); 2719 2720/*Simple directive to set $data isolated scope*/ 2721angular.module("DataUI").directive('uiDataScope', ['$timeout', function ($timeout) { 2722 return { 2723 restrict: 'A', 2724 scope: { 2725 data: '=uiData', 2726 onChange: '&?uiOnChange' 2727 }, 2728 transclude: true, 2729 link: function (scope, element, attrs, ctrl, transcludeFn) { 2730 scope.$data = new Array(); 2731 2732 transcludeFn(scope, function (clone, scope) { 2733 element.append(clone); 2734 }); 2735 2736 var _doMap = function () { 2737 scope.$data = scope.data; 2738 if (scope.onChange) scope.onChange(); 2739 scope.$applyAsync(); 2740 } 2741 2742 var doMap = _.debounce(_doMap, 100); 2743 scope.$watch('data', doMap); 2744 } 2745 } 2746}]); 2747 2748 2749/*Simple directive to parse items content from string to object*/ 2750angular.module("DataUI").directive('uiParseItemsContent', ['$timeout', function ($timeout) { 2751 return { 2752 restrict: 'A', 2753 scope: false, 2754 link: function (scope, element, attrs, ctrl, transcludeFn) { 2755 var _doMap = function () { 2756 if (scope.$data) { 2757 _.forEach(scope.$data, function (o) { 2758 if (o.Content) { 2759 try { o.Content = JSON.parse(o.Content); } catch(ex) { } 2760 } 2761 }); 2762 scope.$applyAsync(); 2763 } 2764 } 2765 scope.$watch('$data', _doMap); 2766 } 2767 } 2768}]); 2769 2770/*Simple directive to join 2 arrays by one key field*/ 2771angular.module("DataUI").directive('uiDataJoin', ['$timeout', function ($timeout) { 2772 return { 2773 restrict: 'A', 2774 scope: { 2775 data1: '=uiData1', 2776 data2: '=uiData2', 2777 dataKey: '@uiDataKey' 2778 }, 2779 transclude: true, 2780 link: function (scope, element, attrs, ctrl, transcludeFn) { 2781 scope.$data = new Array(); 2782 2783 transcludeFn(scope, function (clone, scope) { 2784 element.append(clone); 2785 }); 2786 2787 var _doMap = function () { 2788 var merged = _.merge(_.keyBy(scope.data1, scope.dataKey), _.keyBy(scope.data2, scope.dataKey)); 2789 scope.$data = _.values(merged); 2790 scope.$applyAsync(); 2791 } 2792 2793 var doMap = _.debounce(_doMap, 100); 2794 scope.$watch('data1', doMap); 2795 scope.$watch('data2', doMap); 2796 } 2797 } 2798}]); 2799 2800
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.