1/** 2 * API utilities service 3 * 4 * @module app/common/services/api-utils 5 * @exports APIUtils 6 * @name APIUtils 7 * @version 0.0.1 8 */ 9 10window.angular && (function (angular) { 11 'use strict'; 12 angular 13 .module('app.common.services') 14 .factory('APIUtils', ['$http', 'Constants', '$q', function($http, Constants, $q){ 15 var SERVICE = { 16 LOGIN_CREDENTIALS: Constants.LOGIN_CREDENTIALS, 17 API_CREDENTIALS: Constants.API_CREDENTIALS, 18 API_RESPONSE: Constants.API_RESPONSE, 19 CHASSIS_POWER_STATE: Constants.CHASSIS_POWER_STATE, 20 HOST_STATE_TEXT: Constants.HOST_STATE, 21 HOST_STATE: Constants.HOST_STATE, 22 LED_STATE: Constants.LED_STATE, 23 LED_STATE_TEXT: Constants.LED_STATE_TEXT, 24 getChassisState: function(callback){ 25 $http({ 26 method: 'GET', 27 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/chassis0", 28 headers: { 29 'Accept': 'application/json', 30 'Content-Type': 'application/json' 31 }, 32 withCredentials: true 33 }).success(function(response){ 34 var json = JSON.stringify(response); 35 var content = JSON.parse(json); 36 callback(content.data.CurrentPowerState); 37 }).error(function(error){ 38 console.log(error); 39 }); 40 }, 41 getHostState: function(callback){ 42 $http({ 43 method: 'GET', 44 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/host0", 45 headers: { 46 'Accept': 'application/json', 47 'Content-Type': 'application/json' 48 }, 49 withCredentials: true 50 }).success(function(response){ 51 var json = JSON.stringify(response); 52 var content = JSON.parse(json); 53 callback(content.data.CurrentHostState); 54 }).error(function(error){ 55 console.log(error); 56 }); 57 }, 58 getLEDState: function(){ 59 var deferred = $q.defer(); 60 $http({ 61 method: 'GET', 62 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/led/groups/enclosure_identify", 63 headers: { 64 'Accept': 'application/json', 65 'Content-Type': 'application/json' 66 }, 67 withCredentials: true 68 }).success(function(response){ 69 var json = JSON.stringify(response); 70 var content = JSON.parse(json); 71 deferred.resolve(content.data.Asserted); 72 }).error(function(error){ 73 console.log(error); 74 deferred.reject(error); 75 }); 76 return deferred.promise; 77 }, 78 login: function(username, password, callback){ 79 $http({ 80 method: 'POST', 81 url: SERVICE.API_CREDENTIALS.host + "/login", 82 headers: { 83 'Accept': 'application/json', 84 'Content-Type': 'application/json' 85 }, 86 withCredentials: true, 87 data: JSON.stringify({"data": [username, password]}) 88 }).success(function(response){ 89 if(callback){ 90 callback(response); 91 } 92 }).error(function(error){ 93 if(callback){ 94 if(error && error.status && error.status == 'error'){ 95 callback(error); 96 }else{ 97 callback(error, true); 98 } 99 } 100 console.log(error); 101 }); 102 }, 103 logout: function(callback){ 104 $http({ 105 method: 'POST', 106 url: SERVICE.API_CREDENTIALS.host + "/logout", 107 headers: { 108 'Accept': 'application/json', 109 'Content-Type': 'application/json' 110 }, 111 withCredentials: true, 112 data: JSON.stringify({"data": []}) 113 }).success(function(response){ 114 if(callback){ 115 callback(response); 116 } 117 }).error(function(error){ 118 if(callback){ 119 callback(null, error); 120 } 121 console.log(error); 122 }); 123 }, 124 chassisPowerOn: function(callback){ 125 $http({ 126 method: 'POST', 127 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/host0", 128 headers: { 129 'Accept': 'application/json', 130 'Content-Type': 'application/json' 131 }, 132 withCredentials: true, 133 data: JSON.stringify({"data": []}) 134 }).success(function(response){ 135 var json = JSON.stringify(response); 136 var content = JSON.parse(json); 137 if(callback){ 138 return callback(content.data.CurrentPowerState); 139 } 140 }).error(function(error){ 141 if(callback){ 142 callback(error); 143 }else{ 144 console.log(error); 145 } 146 }); 147 }, 148 chassisPowerOff: function(callback){ 149 $http({ 150 method: 'POST', 151 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/host0", 152 headers: { 153 'Accept': 'application/json', 154 'Content-Type': 'application/json' 155 }, 156 withCredentials: true, 157 data: JSON.stringify({"data": []}) 158 }).success(function(response){ 159 var json = JSON.stringify(response); 160 var content = JSON.parse(json); 161 if(callback){ 162 return callback(content.data.CurrentPowerState); 163 } 164 }).error(function(error){ 165 if(callback){ 166 callback(error); 167 }else{ 168 console.log(error); 169 } 170 }); 171 }, 172 setLEDState: function(state, callback){ 173 $http({ 174 method: 'PUT', 175 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/led/groups/enclosure_identify/attr/Asserted", 176 headers: { 177 'Accept': 'application/json', 178 'Content-Type': 'application/json' 179 }, 180 withCredentials: true, 181 data: JSON.stringify({"data": state}) 182 }).success(function(response){ 183 var json = JSON.stringify(response); 184 var content = JSON.parse(json); 185 if(callback){ 186 return callback(content.status); 187 } 188 }).error(function(error){ 189 if(callback){ 190 callback(error); 191 }else{ 192 console.log(error); 193 } 194 }); 195 }, 196 bmcReboot: function(callback){ 197 $http({ 198 method: 'PUT', 199 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/bmc0/attr/RequestedBmcTransition", 200 headers: { 201 'Accept': 'application/json', 202 'Content-Type': 'application/json' 203 }, 204 withCredentials: true, 205 data: JSON.stringify({"data": "xyz.openbmc_project.State.BMC.Transition.Reboot"}) 206 }).success(function(response){ 207 var json = JSON.stringify(response); 208 var content = JSON.parse(json); 209 if(callback){ 210 return callback(content.status); 211 } 212 }).error(function(error){ 213 if(callback){ 214 callback(error); 215 }else{ 216 console.log(error); 217 } 218 }); 219 }, 220 hostPowerOn: function(callback){ 221 $http({ 222 method: 'PUT', 223 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/host0/attr/RequestedHostTransition", 224 headers: { 225 'Accept': 'application/json', 226 'Content-Type': 'application/json' 227 }, 228 withCredentials: true, 229 data: JSON.stringify({"data": "xyz.openbmc_project.State.Host.Transition.On"}) 230 }).success(function(response){ 231 var json = JSON.stringify(response); 232 var content = JSON.parse(json); 233 if(callback){ 234 return callback(content.status); 235 } 236 }).error(function(error){ 237 if(callback){ 238 callback(error); 239 }else{ 240 console.log(error); 241 } 242 }); 243 }, 244 hostPowerOff: function(callback){ 245 $http({ 246 method: 'PUT', 247 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/host0/attr/RequestedHostTransition", 248 headers: { 249 'Accept': 'application/json', 250 'Content-Type': 'application/json' 251 }, 252 withCredentials: true, 253 data: JSON.stringify({"data": "xyz.openbmc_project.State.Host.Transition.Off"}) 254 }).success(function(response){ 255 var json = JSON.stringify(response); 256 var content = JSON.parse(json); 257 if(callback){ 258 return callback(content.status); 259 } 260 }).error(function(error){ 261 if(callback){ 262 callback(error); 263 }else{ 264 console.log(error); 265 } 266 }); 267 }, 268 hostReboot: function(callback){ 269 $http({ 270 method: 'POST', 271 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/host0", 272 headers: { 273 'Accept': 'application/json', 274 'Content-Type': 'application/json' 275 }, 276 withCredentials: true, 277 data: JSON.stringify({"data": []}), 278 }).success(function(response){ 279 var json = JSON.stringify(response); 280 var content = JSON.parse(json); 281 if(callback){ 282 return callback(content); 283 } 284 }).error(function(error){ 285 if(callback){ 286 callback(error); 287 }else{ 288 console.log(error); 289 } 290 }); 291 }, 292 hostShutdown: function(callback){ 293 $http({ 294 method: 'POST', 295 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/state/host0", 296 headers: { 297 'Accept': 'application/json', 298 'Content-Type': 'application/json' 299 }, 300 withCredentials: true, 301 data: JSON.stringify({"data": []}) 302 }).success(function(response){ 303 var json = JSON.stringify(response); 304 var content = JSON.parse(json); 305 if(callback){ 306 return callback(content); 307 } 308 }).error(function(error){ 309 if(callback){ 310 callback(error); 311 }else{ 312 console.log(error); 313 } 314 }); 315 }, 316 getLogs: function(){ 317 var deferred = $q.defer(); 318 $http({ 319 method: 'GET', 320 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/logging/enumerate", 321 headers: { 322 'Accept': 'application/json', 323 'Content-Type': 'application/json' 324 }, 325 withCredentials: true 326 }).success(function(response){ 327 var json = JSON.stringify(response); 328 var content = JSON.parse(json); 329 var dataClone = JSON.parse(JSON.stringify(content.data)); 330 var data = []; 331 var severityCode = ''; 332 var priority = ''; 333 var relatedItems = []; 334 335 for(var key in content.data){ 336 if(content.data.hasOwnProperty(key) && content.data[key].hasOwnProperty('Id')){ 337 var severityFlags = {low: false, medium: false, high: false}; 338 severityCode = content.data[key].Severity.split(".").pop(); 339 priority = Constants.SEVERITY_TO_PRIORITY_MAP[severityCode]; 340 severityFlags[priority.toLowerCase()] = true; 341 relatedItems = []; 342 content.data[key].associations.forEach(function(item){ 343 relatedItems.push(item[2]); 344 }); 345 346 data.push(Object.assign({ 347 path: key, 348 copied: false, 349 priority: priority, 350 severity_code: severityCode, 351 severity_flags: severityFlags, 352 additional_data: content.data[key].AdditionalData.join("\n"), 353 selected: false, 354 search_text: ("#" + content.data[key].Id + " " + severityCode + " " + content.data[key].Severity + " " + content.data[key].AdditionalData.join(" ")).toLowerCase(), 355 meta: false, 356 confirm: false, 357 related_items: relatedItems, 358 data: {key: key, value: content.data[key]} 359 }, content.data[key])); 360 } 361 } 362 deferred.resolve({data: data, original: dataClone}); 363 }).error(function(error){ 364 console.log(error); 365 deferred.reject(error); 366 }); 367 368 return deferred.promise; 369 }, 370 getAllSensorStatus: function(callback){ 371 $http({ 372 method: 'GET', 373 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/sensors/enumerate", 374 headers: { 375 'Accept': 'application/json', 376 'Content-Type': 'application/json' 377 }, 378 withCredentials: true 379 }).success(function(response){ 380 var json = JSON.stringify(response); 381 var content = JSON.parse(json); 382 var dataClone = JSON.parse(JSON.stringify(content.data)); 383 var sensorData = []; 384 var severity = {}; 385 var title = ""; 386 var tempKeyParts = []; 387 var order = 0; 388 389 function getSensorStatus(reading){ 390 var severityFlags = {critical: false, warning: false, normal: false}, severityText = '', order = 0; 391 392 if(reading.hasOwnProperty('CriticalLow') && 393 reading.Value < reading.CriticalLow 394 ){ 395 severityFlags.critical = true; 396 severityText = 'critical'; 397 order = 2; 398 }else if(reading.hasOwnProperty('CriticalHigh') && 399 reading.Value > reading.CriticalHigh 400 ){ 401 severityFlags.critical = true; 402 severityText = 'critical'; 403 order = 2; 404 }else if(reading.hasOwnProperty('CriticalLow') && 405 reading.hasOwnProperty('WarningLow') && 406 reading.Value >= reading.CriticalLow && reading.Value <= reading.WarningLow){ 407 severityFlags.warning = true; 408 severityText = 'warning'; 409 order = 1; 410 }else if(reading.hasOwnProperty('WarningHigh') && 411 reading.hasOwnProperty('CriticalHigh') && 412 reading.Value >= reading.WarningHigh && reading.Value <= reading.CriticalHigh){ 413 severityFlags.warning = true; 414 severityText = 'warning'; 415 order = 1; 416 }else{ 417 severityFlags.normal = true; 418 severityText = 'normal'; 419 } 420 return { flags: severityFlags, severityText: severityText, order: order}; 421 } 422 423 for(var key in content.data){ 424 if(content.data.hasOwnProperty(key) && content.data[key].hasOwnProperty('Unit')){ 425 426 severity = getSensorStatus(content.data[key]); 427 428 if(!content.data[key].hasOwnProperty('CriticalLow')){ 429 content.data[key].CriticalLow = "--"; 430 content.data[key].CriticalHigh = "--"; 431 } 432 433 if(!content.data[key].hasOwnProperty('WarningLow')){ 434 content.data[key].WarningLow = "--"; 435 content.data[key].WarningHigh = "--"; 436 } 437 438 tempKeyParts = key.split("/"); 439 title = tempKeyParts.pop(); 440 title = tempKeyParts.pop() + '_' + title; 441 title = title.split("_").map(function(item){ 442 return item.toLowerCase().charAt(0).toUpperCase() + item.slice(1); 443 }).reduce(function(prev, el){ 444 return prev + " " + el; 445 }); 446 447 sensorData.push(Object.assign({ 448 path: key, 449 selected: false, 450 confirm: false, 451 copied: false, 452 title: title, 453 unit: Constants.SENSOR_UNIT_MAP[content.data[key].Unit], 454 severity_flags: severity.flags, 455 status: severity.severityText, 456 order: severity.order, 457 search_text: (title + " " + content.data[key].Value + " " + 458 Constants.SENSOR_UNIT_MAP[content.data[key].Unit] + " " + 459 severity.severityText + " " + 460 content.data[key].CriticalLow + " " + 461 content.data[key].CriticalHigh + " " + 462 content.data[key].WarningLow + " " + 463 content.data[key].WarningHigh + " " 464 ).toLowerCase(), 465 original_data: {key: key, value: content.data[key]} 466 }, content.data[key])); 467 } 468 } 469 470 callback(sensorData, dataClone); 471 }).error(function(error){ 472 console.log(error); 473 }); 474 }, 475 getFirmwares: function(){ 476 var deferred = $q.defer(); 477 $http({ 478 method: 'GET', 479 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/software/enumerate", 480 headers: { 481 'Accept': 'application/json', 482 'Content-Type': 'application/json' 483 }, 484 withCredentials: true 485 }).success(function(response){ 486 var json = JSON.stringify(response); 487 var content = JSON.parse(json); 488 var data = []; 489 var active = false; 490 var isExtended = false; 491 var bmcActiveVersion = ""; 492 var hostActiveVersion = ""; 493 var imageType = ""; 494 var extendedVersions = []; 495 496 function getFormatedExtendedVersions(extendedVersion){ 497 var versions = []; 498 extendedVersion = extendedVersion.split(","); 499 500 extendedVersion.forEach(function(item){ 501 var parts = item.split("-"); 502 var numberIndex = 0; 503 for(var i = 0; i < parts.length; i++){ 504 if(/[0-9]/.test(parts[i])){ 505 numberIndex = i; 506 break; 507 } 508 } 509 var titlePart = parts.splice(0, numberIndex); 510 titlePart = titlePart.join(""); 511 titlePart = titlePart[0].toUpperCase() + titlePart.substr(1, titlePart.length); 512 var versionPart = parts.join("-"); 513 versions.push({ 514 title: titlePart, 515 version: versionPart 516 }); 517 }); 518 519 return versions; 520 } 521 522 for(var key in content.data){ 523 if(content.data.hasOwnProperty(key) && content.data[key].hasOwnProperty('Version')){ 524 active = (/\.Active$/).test(content.data[key].Activation); 525 imageType = content.data[key].Purpose.split(".").pop(); 526 isExtended = content.data[key].hasOwnProperty('ExtendedVersion') && content.data[key].ExtendedVersion != ""; 527 if(isExtended){ 528 extendedVersions = getFormatedExtendedVersions(content.data[key].ExtendedVersion); 529 } 530 data.push(Object.assign({ 531 path: key, 532 active: active, 533 imageId: key.split("/").pop(), 534 imageType: imageType, 535 isExtended: isExtended, 536 extended: { 537 show: false, 538 versions: extendedVersions 539 }, 540 data: {key: key, value: content.data[key]} 541 }, content.data[key])); 542 543 if(active && imageType == 'BMC'){ 544 bmcActiveVersion = content.data[key].Version; 545 } 546 547 if(active && imageType == 'Host'){ 548 hostActiveVersion = content.data[key].Version; 549 } 550 } 551 } 552 553 deferred.resolve({ 554 data: data, 555 bmcActiveVersion: bmcActiveVersion, 556 hostActiveVersion: hostActiveVersion 557 }); 558 }).error(function(error){ 559 console.log(error); 560 deferred.reject(error); 561 }); 562 563 return deferred.promise; 564 }, 565 uploadImage: function(file, callback){ 566 $http({ 567 method: 'PUT', 568 timeout: 5 * 60 * 1000, 569 //url: 'http://localhost:3002/upload', 570 url: SERVICE.API_CREDENTIALS.host + "/upload/image/", 571 headers: { 572 'Accept': 'application/octet-stream', 573 'Content-Type': 'application/octet-stream' 574 }, 575 withCredentials: true, 576 data: file 577 }).success(function(response){ 578 var json = JSON.stringify(response); 579 var content = JSON.parse(json); 580 if(callback){ 581 return callback(content); 582 } 583 }).error(function(error){ 584 if(callback){ 585 callback(error); 586 }else{ 587 console.log(error); 588 } 589 }); 590 }, 591 getBMCEthernetInfo: function(){ 592 var deferred = $q.defer(); 593 $http({ 594 method: 'GET', 595 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/inventory/system/chassis/motherboard/boxelder/bmc/ethernet", 596 headers: { 597 'Accept': 'application/json', 598 'Content-Type': 'application/json' 599 }, 600 withCredentials: true 601 }).success(function(response){ 602 var json = JSON.stringify(response); 603 var content = JSON.parse(json); 604 deferred.resolve(content.data); 605 }).error(function(error){ 606 console.log(error); 607 deferred.reject(error); 608 }); 609 610 return deferred.promise; 611 }, 612 getBMCInfo: function(callback){ 613 var deferred = $q.defer(); 614 $http({ 615 method: 'GET', 616 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/inventory/system/chassis/motherboard/boxelder/bmc", 617 headers: { 618 'Accept': 'application/json', 619 'Content-Type': 'application/json' 620 }, 621 withCredentials: true 622 }).success(function(response){ 623 var json = JSON.stringify(response); 624 var content = JSON.parse(json); 625 deferred.resolve(content.data); 626 }).error(function(error){ 627 console.log(error); 628 deferred.reject(error); 629 }); 630 return deferred.promise; 631 }, 632 getHardwares: function(callback){ 633 $http({ 634 method: 'GET', 635 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/inventory/enumerate", 636 headers: { 637 'Accept': 'application/json', 638 'Content-Type': 'application/json' 639 }, 640 withCredentials: true 641 }).success(function(response){ 642 var json = JSON.stringify(response); 643 var content = JSON.parse(json); 644 var hardwareData = []; 645 var keyIndexMap = {}; 646 var title = ""; 647 var data = []; 648 var searchText = ""; 649 var componentIndex = -1; 650 var tempParts = []; 651 652 653 function isSubComponent(key){ 654 655 for(var i = 0; i < Constants.HARDWARE.parent_components.length; i++){ 656 if(key.split(Constants.HARDWARE.parent_components[i]).length == 2) return true; 657 } 658 659 return false; 660 } 661 662 function titlelize(title){ 663 title = title.replace(/([A-Z0-9]+)/g, " $1").replace(/^\s+/, ""); 664 for(var i = 0; i < Constants.HARDWARE.uppercase_titles.length; i++){ 665 if(title.toLowerCase().indexOf((Constants.HARDWARE.uppercase_titles[i] + " ")) > -1){ 666 return title.toUpperCase(); 667 } 668 } 669 670 return title; 671 } 672 673 function camelcaseToLabel(obj){ 674 var transformed = [], label = "", value = ""; 675 for(var key in obj){ 676 label = key.replace(/([A-Z0-9]+)/g, " $1").replace(/^\s+/, ""); 677 if(obj[key] !== ""){ 678 value = obj[key]; 679 if(value == 1 || value == 0){ 680 value = (value == 1) ? 'Yes' : 'No'; 681 } 682 transformed.push({key:label, value: value}); 683 } 684 } 685 686 return transformed; 687 } 688 689 function getSearchText(data){ 690 var searchText = ""; 691 for(var i = 0; i < data.length; i++){ 692 searchText += " " + data[i].key + " " + data[i].value; 693 } 694 695 return searchText; 696 } 697 698 for(var key in content.data){ 699 if(content.data.hasOwnProperty(key) && 700 key.indexOf(Constants.HARDWARE.component_key_filter) == 0){ 701 702 data = camelcaseToLabel(content.data[key]); 703 searchText = getSearchText(data); 704 title = key.split("/").pop(); 705 706 title = titlelize(title); 707 708 if(!isSubComponent(key)){ 709 hardwareData.push(Object.assign({ 710 path: key, 711 title: title, 712 selected: false, 713 expanded: false, 714 search_text: title.toLowerCase() + " " + searchText.toLowerCase(), 715 sub_components: [], 716 original_data: {key: key, value: content.data[key]} 717 }, {items: data})); 718 719 keyIndexMap[key] = hardwareData.length - 1; 720 }else{ 721 var tempParts = key.split("/"); 722 tempParts.pop(); 723 tempParts = tempParts.join("/"); 724 componentIndex = keyIndexMap[tempParts]; 725 data = content.data[key]; 726 data.title = title; 727 hardwareData[componentIndex].sub_components.push(data); 728 hardwareData[componentIndex].search_text += " " + title.toLowerCase(); 729 } 730 } 731 } 732 733 if(callback){ 734 callback(hardwareData, content.data); 735 }else{ 736 return { data: hardwareData, original_data: content.data}; 737 } 738 }); 739 }, 740 deleteLogs: function(logs) { 741 var defer = $q.defer(); 742 var promises = []; 743 744 function finished(){ 745 defer.resolve(); 746 } 747 748 logs.forEach(function(item){ 749 promises.push($http({ 750 method: 'POST', 751 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/logging/entry/"+item.Id+"/action/Delete", 752 headers: { 753 'Accept': 'application/json', 754 'Content-Type': 'application/json' 755 }, 756 withCredentials: true, 757 data: JSON.stringify({"data": []}) 758 })); 759 }); 760 761 $q.all(promises).then(finished); 762 763 return defer.promise; 764 }, 765 resolveLogs: function(logs) { 766 var defer = $q.defer(); 767 var promises = []; 768 769 function finished(){ 770 defer.resolve(); 771 } 772 773 logs.forEach(function(item){ 774 promises.push($http({ 775 method: 'PUT', 776 url: SERVICE.API_CREDENTIALS.host + "/xyz/openbmc_project/logging/entry/"+item.Id+"/attr/Resolved", 777 headers: { 778 'Accept': 'application/json', 779 'Content-Type': 'application/json' 780 }, 781 withCredentials: true, 782 data: JSON.stringify({"data": "1"}) 783 })); 784 }); 785 786 $q.all(promises).then(finished); 787 788 return defer.promise; 789 }, 790 }; 791 return SERVICE; 792 }]); 793 794 })(window.angular); 795