'use strict';

/*jshint unused: false*/
/*jshint bitwise: false*/
(function() {

  var app = angular.module('upsConsole', [
    'ngResource',
    'ngNewRouter',
    'ngAnimate',
    'ngIdle',
    'ui.bootstrap',
    'patternfly.autofocus',
    'patternfly.notification',
    'patternfly.select',
    'angular-c3',
    'ngClipboard'
  ]);

  app.run(function($rootScope) {
    // allow to retrieve $rootScope in views (for clarification of access scope)
    $rootScope.$rootScope = $rootScope;
  });

  var appConfig = {
    logDebugEnabled: false,
    idleDuration: 300,
    idleWarningDuration : 30,
    keepaliveInterval: 5
  };

  app.provider('appConfig', function () {
    return {
      set: function (settings) {
        // allow to override configuration (e.g. in tests)
        angular.extend(appConfig, settings);
      },
      $get: function () {
        // default configuration
        return appConfig;
      }
    };
  });

  app.config(function ($logProvider, appConfigProvider) {
    var appConfig = appConfigProvider.$get();
    $logProvider.debugEnabled( appConfig.logDebugEnabled );
  });

  app.factory('docsLinks', function( $http, staticResourcePrefix ) {
    var result = {};
    $http.get( staticResourcePrefix + 'docs-links.json' )
      .then(function( response ) {
        angular.extend( result, response.data );
      });
    return result;
  });

  app.config(function(ngClipProvider) {
    ngClipProvider.setPath( 'img/ZeroClipboard.swf' );
  });

  app.value('apiPrefix', '');

  app.value('staticResourcePrefix', '');

  app.constant('allVariantTypes', ['android', 'ios']);

  app.value('allowCreateVariant', function( app, variantType ) {
    return true;
  });

})();

'use strict';

angular.module('upsConsole')
  .config(function( $provide ) {

    /**
     * Decorator for $resource service which helps to maintain conciseness of the code by avoiding a need to call resource.method(...).$promise.then(...),
     * because it returns a promise directly: resource.method(...).then(...).
     */
    $provide.decorator('$resource', function($delegate) {
      return function decorator(url, paramDefaults, actions) {
        var args = Array.prototype.slice.call(arguments);
        var wrappedResource = {};
        var originalActions = {};
        var actionsWithoutFunctions = args[2] = {};
        Object.keys(actions).forEach(function( methodName ) {
          var method = actions[methodName];
          originalActions[methodName] = method;
          if (!angular.isFunction(method)) {
            actionsWithoutFunctions[methodName] = method;
          }
        });
        var originalResource = $delegate.apply($delegate, args);
        Object.keys(originalActions).forEach(function( methodName ) {
          var method = originalActions[methodName];
          if (angular.isFunction(method)) {
            wrappedResource[methodName] = method;
          } else {
            wrappedResource[methodName] = function() {
              return originalResource[methodName].apply(originalResource, arguments).$promise;
            };
          }
        });
        return wrappedResource;
      };
    });
  });


'use strict';

angular.module('upsConsole')
  .controller('RouteController', function($router, $scope, $timeout, $log) {
    $router.config([
      {path: '/',                     component: 'home'},
      {path: '/welcome',              component: 'welcome'},
      {path: '/wizard/create-app',    component: 'wizard01CreateApp'},
      {path: '/wizard/add-variant',   component: 'wizard02AddVariant'},
      {path: '/wizard/register-device',  component: 'wizard03RegisterDevice'},
      {path: '/wizard/send-push-notification',  component: 'wizard04SendPushNotification'},
      {path: '/wizard/setup-sender',  component: 'wizard05SetupSender'},
      {path: '/wizard/done',          component: 'wizard06Done'},
      {path: '/app/:app/:tab/:variant', component: 'appDetail'},
      {path: '/app/:app/:tab',        component: 'appDetail'},
      {path: '/links-check',          component: 'linksCheck'},
    ]);

    /**
     * Listens for 'upsNavigate' event and switches the view given by provided path (defined in $router.config above)
     *
     * @param event - the event passed automatically from $broadcast/$emit
     * @param {string} path - the path of the path that this view should navigate to
     */
    $scope.$on('upsNavigate', function(event, path) {
      $log.debug( 'Navigating to: ' + path );
      $timeout(function() {
        $router.navigate(path).then(
          function(){
            $log.debug( 'Navigation success: ' + path );
          },
          function(){
            $log.warn( 'Navigation failure: ' + path );
          }
        );
      }, 100);
    });
  });

/*
 * JBoss, Home of Professional Open Source
 * Copyright Red Hat, Inc., and individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

angular.module('upsConsole')
  .controller('AppController', function ( $rootScope, $scope, $location, Auth, $http, $interval, $timeout, $log, appConfig, dashboardEndpoint, $$rootRouter ) {

    var self = this;

    this.config = appConfig;
    this.warnings = [];

    //Retrieve the current logged in username
    function getUsername() {
      if (Auth && Auth.keycloak) {
        $rootScope.showUserDropdown = true;
        return Auth.keycloak.idTokenParsed.preferred_username;
      } else {
        $rootScope.showUserDropdown = false;
        return 'dummy';
      }
    }

    this.username = getUsername();
    $scope.$watch(getUsername, function( newValue ) {
      self.username = newValue;
      // After the user is resolved, redirect to the URL passed
      // to the browser
      $$rootRouter.navigate($location.url());
    });

    this.logout = function() {
      Auth.logout();
    };

    this.goToAccountManagement = function() {
      window.location = Auth.keycloak.authServerUrl + '/realms/' + Auth.keycloak.realm + '/account?referrer=unified-push-server-js';
    };

    this.havePendingRequests = function() {
      return $http.pendingRequests.some(function(config) {
        if (config.method !== 'GET') {
          return true;
        }
      });
    };


    this.stats = {};
    function updateStats() {
      dashboardEndpoint.totals()
        .then(function( data ) {
          self.stats = data;
        });
    }
    updateStats();
    $scope.$on('upsUpdateStats', updateStats);
    $scope.$on('upsApplicationCreated', updateStats);
    $scope.$on('upsApplicationDeleted', updateStats);

    // load warnings and update them periodically and when notification is sent
    function updateWarnings() {
      dashboardEndpoint.warnings().then(function( warnings ) {
        self.warnings = warnings;
      });
    }
    updateWarnings();

    // For AGPUSH-1895: temporarrly we disable the polling
    // var updateWarningsInterval = $interval(updateWarnings, 30000);
    // $scope.$on('$destroy', function() {
    //   $log.debug('cancelling updateWarnings interval');
    //   $interval.cancel( updateWarningsInterval );
    // });
    $scope.$on('upsNotificationSent', function() {
      var timer1 = $timeout(updateWarnings, 1500);
      var timer2 = $timeout(updateWarnings, 5000);
      var timer3 = $timeout(updateWarnings, 10000);
      $scope.$on('$destroy', function() {
        $log.debug('cancelling updateWarnings timeouts');
        $timeout.cancel( timer1 );
        $timeout.cancel( timer2 );
        $timeout.cancel( timer3 );
      });
    });
  });

'use strict';

angular.module('upsConsole')
  .controller('IdleController', function (Keepalive, Idle, $rootScope, $scope, $log, appConfig, Auth) {

    var self = this;

    self.config = appConfig;

    // No need for idling when no authentication is used
    if (Auth && Auth.keycloak) {
      Idle.watch();
    }

    /**
     * idle service, keepalive, auth token refresh
     */
    self.idleCountdown = appConfig.idleWarningDuration + 1;
    $rootScope.$on('KeepaliveResponse', function () {
      Auth.keycloak.updateToken(45).success(function (refreshed) {
        if (refreshed) {
          $log.debug('token was successfully refreshed');
        } else {
          $log.debug('token is still valid');
        }
      }).error(function () {
        $log.debug('failed to refresh the token, or the session has expired');
      });
    });

    $rootScope.$on('IdleStart', function () {
      $log.debug('idleStart');

    });
    $rootScope.$on('IdleWarn', function () {
      $log.debug('idleWarn');
      $scope.$apply(function () {
        self.idleCountdown = self.idleCountdown - 1;
      });
    });
    $rootScope.$on('IdleEnd', function () {
      $log.debug('idleEnd');
      $scope.$apply(function () {
        self.idleCountdown = appConfig.idleWarningDuration + 1;
      });

    });
    $rootScope.$on('IdleTimeout', function () {
      $log.debug('idleTimeout');
      Auth.logout();
    });
  })

  .config(function (KeepaliveProvider, IdleProvider, appConfigProvider) {
    var appConfig = appConfigProvider.$get();
    IdleProvider.idle(appConfig.idleDuration);
    IdleProvider.timeout(appConfig.idleWarningDuration);
    KeepaliveProvider.interval(appConfig.keepaliveInterval);
  });

angular.module('upsConsole')
  .controller('HomeController', function( $q, $modal, $router, $rootScope, applicationsEndpoint, dashboardEndpoint, appModal, Notifications ) {

    var self = this;

    this.apps = [];
    this.topNotifications = [];

    this.canActivate = function() {
      this.currentPage = 1;
      return self.fetchNewPage(1)
        .then(function() {
          if (self.totalItems < 1) {
            $rootScope.$broadcast('upsNavigate', '/welcome');
            return false;
          }
        });
    };

    this.activate = function() {
      dashboardEndpoint.latestActiveApps()
        .then(function( data ) {
          self.topNotifications = data;
        });
    };

    this.pageChanged = function(page) {
      self.fetchNewPage(page);
    };

    this.fetchNewPage = function(page) {
      return applicationsEndpoint.fetch(page)
        .then(function( result ) {
          self.apps = result.apps;
          self.totalItems = result.totalItems;
        });
    };

    this.changeName = function(app) {
      var appClone = angular.extend({}, app);
      appModal.editName( appClone )
        .then(function( updatedApp ) {
          angular.extend( app, updatedApp );
          Notifications.success('The name of ' + app.name + ' application was successfully changed');
        })
        .catch(function( e ) {
          if ( e instanceof Error ) {
            Notifications.error('Failed to modify app ' + app.name + ': ' + e);
          }
        });
    };

    this.deleteApp = function(app) {
      $modal.open({
        templateUrl: 'dialogs/remove-app.html',
        controller: function( $modalInstance, $scope ) {
          $scope.app = app;
          $scope.confirm = function() {
            applicationsEndpoint.delete({appId: app.pushApplicationID})
              .then(function () {
                $rootScope.$broadcast('upsApplicationDeleted');
                return self.fetchNewPage(self.currentPage);
              })
              .then(function() {
                $modalInstance.close();
                if (self.totalItems < 1) {
                  $router.parent.navigate('/welcome');
                }
              });
          };
          $scope.dismiss = function() {
            $modalInstance.dismiss('cancel');
          }
        }
      });
    };

  });

angular.module('upsConsole')
  .controller('WelcomeController', function( $rootScope ) {

    this.activate = function() {
      $rootScope.$broadcast('upsUpdateStats');
    };

  });

angular.module('upsConsole')
  .controller('AppDetailController', function ($rootScope, $q, $routeParams, $modal, applicationsEndpoint, messageSenderEndpoint, metricsEndpoint, ContextProvider, Notifications) {

    var self = this;

    this.app = null; // is retrieved in canActivate
    this.notifications = null; // is retrieved in canActivate
    this.tab = $routeParams.tab;
    this.selectedVariant = $routeParams.variant;

    this.contextPath = ContextProvider.contextPath();

    this.canActivate = function () {
      return $q.all([
        applicationsEndpoint.getWithMetrics({appId: $routeParams.app})
          .then(function (app) {

            // Expand the variant if an ID is passed in the URL
            if (self.selectedVariant && app.variants) {
              app.variants.forEach(function (variant) {
                variant.$toggled = self.selectedVariant === variant.variantID;
              });
            }

            self.app = app;
            if (!app.variants.length) {
              self.tab = 'variants';
            }
          }),
        metricsEndpoint.fetchApplicationMetrics($routeParams.app, null, 1)
          .then(function (data) {
            self.notifications = data.pushMetrics;
          })
      ]);
    };

    this.sendNotification = function () {
      $modal.open({
        templateUrl: 'dialogs/send-push-notification.html',
        controller: function ($scope, $modalInstance) {

          $scope.app = self.app;

          // default message
          $scope.pushData = {

            'message': {
              'sound': 'default',
              'alert': '',
              'priority': 'normal',
              'simple-push': 'version=' + new Date().getTime()
            },
            'criteria': {}
          };

          $scope.send = function () {
            if ($scope.selectedVariants && $scope.selectedVariants.length > 0) {
              $scope.pushData.criteria.variants = $scope.selectedVariants;
            }
            if ($scope.aliases) {
              $scope.pushData.criteria.alias = $scope.aliases.split(',');
            }
            if ($scope.deviceTypes) {
              $scope.pushData.criteria.deviceType = $scope.deviceTypes.split(',');
            }
            if ($scope.categories) {
              $scope.pushData.criteria.categories = $scope.categories.split(',');
            }

            messageSenderEndpoint(self.app.pushApplicationID, self.app.masterSecret)
              .send({}, $scope.pushData)
              .then(function () {
                self.app.$messageCount += 1;
                self.notifications.unshift({submitDate: new Date().getTime()});
                $modalInstance.close();
                $rootScope.$broadcast('upsNotificationSent', $scope.pushData, $scope.app);
                Notifications.success('Notification was successfully sent');
              })
              .catch(function () {
                Notifications.error('Failed to sent notification');
              });
          };

          $scope.cancel = function () {
            $modalInstance.dismiss();
          };
        }
      });
    };

  });

angular.module('upsConsole')
  .controller('SenderController', function( $modal, $scope, applicationsEndpoint, ContextProvider ) {

    var self = this;

    this.app = $scope.$parent.$parent.appDetail.app;

    this.contextPath = ContextProvider.contextPath();

    this.renewMasterSecret = function () {
      $modal.open({
        templateUrl: 'inline:renew-master-secret.html',
        controller: function( $scope, $modalInstance ) {
          $scope.app = self.app;
          $scope.confirm = function() {
            applicationsEndpoint.reset({appId: self.app.pushApplicationID})
              .then(function (application) {
                self.app.masterSecret = application.masterSecret;
                $modalInstance.close( application );
              });
          };
          $scope.dismiss = function() {
            $modalInstance.dismiss('cancel');
          }
        }
      });
    };
  });

angular.module('upsConsole')
  .controller('VariantsController', function ( $http, $rootScope, $modal, variantModal, $scope, variantsEndpoint, exporterEndpoint, importerEndpoint, Notifications, ErrorReporter, allowCreateVariant, allVariantTypes ) {

    var self = this;

    this.app = $scope.$parent.$parent.appDetail.app;

    /* split the variant types to the groups so that they can be easily access */
    function splitByType( variants ) {
      return variants
        .sort(function(a, b) {
          return a.type.localeCompare(b.type);
        })
        .reduce(function(variantList, variant) {
          var type = variant.type.match(/^windows/) ? 'windows' : variant.type;
          var variantType = variantList[type] = variantList[type] || [];
          variantType.push(variant);
          variantType.$deviceCount = (variantType.$deviceCount  || 0) + (variant.$deviceCount || 0);
          return variantList;
        }, {});
    }
    this.byType = splitByType( this.app.variants );

    if (Object.keys(this.byType).length == 1) {
      angular.forEach(this.byType, function(variants, type) {
        if (variants.length == 1) {
          variants[0].$toggled = true;
        }
      });
    }

    this.add = function() {
      return variantModal.add( this.app )
        .then(function( variant ) {

          variant.$deviceCount = 0;
          self.app.variants.push( variant );
          variant.$toggled = true;
          self.byType = splitByType( self.app.variants );
          Notifications.success('Variant ' + variant.name + ' successfully created');
        })
        .catch(function(e) {
          ErrorReporter.error(e, 'Failed to create variant');
        });
    };

    this.edit = function( variant ) {
      var variantClone = angular.extend({}, variant);
      return variantModal.edit( this.app, variantClone )
        .then(function( updatedVariant ) {
          angular.extend(variant, updatedVariant);
          Notifications.success('Variant ' + variant.name + ' was successfully modified');
        })
        .catch(function( e ) {
          if ( e != 'cancel' ) {
            ErrorReporter.error(e, 'Failed to modify variant ' + variant.name + ': ' + e);
          }
        });
    };

    this.editName = function( variant ) {
      var variantClone = angular.extend({}, variant);
      return variantModal.editName( self.app, variantClone )
        .then(function( updatedVariant ) {
          angular.extend(variant, updatedVariant);
          Notifications.success('The name of ' + variant.name + ' variant was successfully changed');
        })
        .catch(function(e) {
          if ( e !== 'cancel' ) {
            ErrorReporter.error(e, 'Failed to modify variant ' + variant.name + ': ' + e);
          }
        });
    };

    this.delete = function( variant ) {
      $modal.open({
        templateUrl: 'dialogs/remove-variant.html',
        controller: function( $modalInstance, $scope ) {
          $scope.variant = variant;
          $scope.confirm = function() {
            variantsEndpoint.delete({
                  appId: self.app.pushApplicationID,
                  variantType: variant.type,
                  variantId: variant.variantID })
              .then(function () {
                self.app.variants = self.app.variants.filter(function( v ) {
                  return v != variant;
                });
                self.byType = splitByType( self.app.variants );
                $modalInstance.close();
              });
          };
          $scope.dismiss = function() {
            $modalInstance.dismiss('cancel');
          }
        }
      });
    };

    this.renewVariantSecret = function ( variant ) {
      $modal.open({
        templateUrl: 'dialogs/renew-variant-secret.html',
        controller: function( $scope, $modalInstance ) {
          $scope.variant = variant;
          $scope.confirm = function() {
            variantsEndpoint.reset({
                appId: self.app.pushApplicationID,
                variantType: variant.type,
                variantId: variant.variantID })
              .then(function (receivedVariant) {
                variant.secret  = receivedVariant.secret;
                $modalInstance.close( variant );
                //$rootScope.$digest();
              });
          };
          $scope.dismiss = function() {
            $modalInstance.dismiss('cancel');
          }
        }
      });
    };

    this.exportInstallations = function ( variant ) {
      $modal.open({
        templateUrl: 'dialogs/export-installations.html',
        controller: function( $scope, $modalInstance ) {
          $scope.variant = variant;
          $scope.confirm = function() {
            var params = {
              variantId: variant.variantID
            };
            exporterEndpoint.export(params, function (content) {
              var hiddenElement = document.createElement('a');

              hiddenElement.href = 'data:attachment/json,' + encodeURI(JSON.stringify(content));
              hiddenElement.target = '_blank';
              hiddenElement.download = variant.variantID + '.json';
              hiddenElement.click();

              $modalInstance.close();
              Notifications.success('Successfully exported installations');
            });
          };
          $scope.dismiss = function() {
            $modalInstance.dismiss('cancel');
          }
        }
      });
    };

    this.importInstallations = function (variant) {
      $modal.open({
        templateUrl: 'dialogs/import-installations.html',
        controller: function( $scope, $modalInstance ) {
          $scope.variant = variant;
          $scope.installations = [];
          $scope.confirm = function() {
            var fd = new FormData();
            fd.append('file', $scope.installations[0]);
            $http.defaults.headers.common.Authorization = 'Basic ' + btoa(variant.variantID+
                ':' + variant.secret);
            importerEndpoint.import(null, fd, function(){
              Notifications.success('Import processing has started');
              $modalInstance.close();
            });
          };
          $scope.dismiss = function() {
            $modalInstance.dismiss('cancel');
          };
          $scope.previewImport = function() {
            if (window.File && window.FileList && window.FileReader) {
              var importFiles = $scope.installations[0];
              var fileReader = new FileReader();
              fileReader.readAsText(importFiles);
              fileReader.onload = function(event) {
                $scope.$apply(function() {
                  try {
                    $scope.importPreview = JSON.parse(event.target.result).length;
                    $scope.incorrectFormat = false;
                  }
                  catch(e) {
                    $scope.importPreview = null;
                    $scope.incorrectFormat = true;
                  }
                });
              };
            }
          };
        }
      });
    };

    this.getWarningsForVariant = function( warnings, variant ) {
      return warnings.filter(function( warning ) {
        return warning.variant.variantID == variant.variantID;
      });
    };

    this.isAllowedToAddVariant = function() {
      return allVariantTypes.some(function( variantType ) {
        return allowCreateVariant( self.app, variantType );
      });
    };

  });

angular.module('upsConsole')
  .controller('VariantDetailController', function ( $modal, variantModal, $scope, installationsEndpoint, ContextProvider ) {

    var self = this;

    this.variant = $scope.$parent.variant;
    this.installations = [];
    this.totalCount;
    this.currentPage = 1;
    this.currentStart = 0;
    this.currentEnd = 0;
    this.perPage = 10;
    this.searchString = '';
    this.forceShowSnippets = false;
    this.contextPath = ContextProvider.contextPath();

    function fetchInstallations( page, searchString ) {
      installationsEndpoint.fetchInstallations(self.variant.variantID, searchString, page, self.perPage)
        .then(function( data ) {
          self.installations = data.installations;
          self.totalCount = data.total;
          self.currentStart = self.perPage * (self.currentPage - 1) + 1;
          self.currentEnd = self.perPage * (self.currentPage - 1) + self.installations.length;
        });
    }

    // initial page
    fetchInstallations( 1, null );

    this.onPageChange = function ( page ) {
      fetchInstallations( page, self.searchString );
    };

    $scope.$watch(function() { return self.searchString }, function( searchString ) {
      self.currentPage = 1;
      fetchInstallations( self.currentPage, self.searchString );
    });

    self.enableInstallation = function( variant, installation, enabled ) {
      var clone = angular.extend({}, installation, {enabled: enabled});
      installationsEndpoint.update({variantId: self.variant.variantID, installationId: installation.id}, clone)
        .then(function() {
          installation.enabled = enabled;
        });
    }

  });

angular.module('upsConsole')
  .controller('ActivityController', function ( $log, $timeout, $interval, $modal, variantModal, $scope, metricsEndpoint ) {

    var self = this;

    this.TOOLTIP_TARGETS = "Targeted devices or topics for Android/Firebase";
    this.TOOLTIP_OPENED = "Number of users that launched the mobile app by using the push notification";
    this.TOOLTIP_SUCCESS = "Payload submitted to 3rd party push network for further processing";
    this.TOOLTIP_FAIL = "Could not submit payload to 3rd party";

    this.app = $scope.$parent.$parent.appDetail.app;
    this.metrics = [];
    this.totalCount;
    this.currentPage = 1;
    this.currentStart = 0;
    this.currentEnd = 0;
    this.perPage = 10;
    this.searchString = '';
    this.activeSearch = '';

    var refreshInterval;

    /**
     * Fetches new data, reflecting provided page and searchString
     */
    function fetchMetrics( page, searchString ) {
      return metricsEndpoint.fetchApplicationMetrics(self.app.pushApplicationID, searchString, page, self.perPage)
        .then(function( data ) {
          self.activeSearch = searchString;
          self.metrics.forEach(function( originalMetric ) {
            data.pushMetrics.some(function ( newMetric ) {
              if (originalMetric.id === newMetric.id && originalMetric.$toggled) {
                newMetric.$toggled = true;
                return true;
              }
            });
          });
          self.metrics = data.pushMetrics;
          self.totalCount = data.totalItems;
          self.currentStart = self.perPage * (self.currentPage - 1) + 1;
          self.currentEnd = self.perPage * (self.currentPage - 1) + self.metrics.length;
          self.metrics.forEach(function( metric ) {
            try {
              metric.$message = JSON.parse(metric.rawJsonMessage);
            } catch (err) {
              console.log('failed to parse metric');
              metric.$message = {};
            }
          });
        });
    }

    /**
     * Determines whether search is active - either the user typed search string or the data doesn't reflect the search string yet.
     *
     * @return false if searchString if false and data reflects that searchString; true otherwise
     */
    this.isSearchActive = function() {
      return self.searchString || self.activeSearch;
    };

    /**
     * Fetches new data on page change
     */
    this.onPageChange = function ( page ) {
      fetchMetrics( page, self.searchString );
    };

    function getVariantByID ( variantID ) {
      return self.app.variants.filter(function( variant ) {
        return variant.variantID == variantID;
      })[0];
    }

    function refreshUntilAllServed() {
      fetchMetrics( self.currentPage, self.searchString )
        .then(function() {
          $log.debug('refreshed');
          // is the _last_ push job pending?
          var isPending = self.metrics[0] && (self.metrics[0].servedVariants < self.metrics[0].totalVariants);
          // var isPending = self.metrics.some(function(metric) {
          //   return metric.servedVariants < metric.totalVariants;
          // });
          if (isPending) {
            if (!refreshInterval) {
              $log.debug('scheduling refresh');
              refreshInterval = $interval(refreshUntilAllServed, 1000);
            }
          } else {
            $log.debug('clearing refresh');
            $interval.cancel(refreshInterval);
            refreshInterval = null;
          }
        });
    }

    // initial load
    refreshUntilAllServed();

    $scope.$on('upsNotificationSent', refreshUntilAllServed);
    $scope.$on('$destroy', function () {
      if (refreshInterval) {
        $log.debug('cancelling refreshInterval');
        $interval.cancel(refreshInterval);
      }
    });

    $scope.$watch(function() { 
      return self.searchString 
    }, function (newSearchString, oldSearchString) {
      //ensure that the value has actually changed, to prevent fetchMetrics being called on initial load here
      if (newSearchString !== oldSearchString) {
        self.currentPage = 1;
        fetchMetrics(self.currentPage, self.newSearchString);
      }
    });

  });

angular.module('upsConsole')
  .controller('Wizard01CreateAppController', function ( $rootScope, $router, variantModal, applicationsEndpoint, createAppWizard, Notifications ) {

    var self = this;

    this.application =  {};

    this.createApp = function() {
      applicationsEndpoint.create( self.application )
        .then(function( app ) {
          createAppWizard.app = app;
          Notifications.success('Application ' + self.application.name + ' successfully created');
          $rootScope.$broadcast('upsUpdateStats');
          $rootScope.$broadcast('upsNavigate', '/wizard/add-variant');
        })
        .catch(function() {
          Notifications.error('Failed to create application ' + self.application.name);
        });
    }
  })

  .factory('createAppWizard', function() {
    return {
      app: null,
      variant: null
    };
  });

angular.module('upsConsole')
  .controller('Wizard02AddVariantController', function( $rootScope, $router, variantModal, createAppWizard, Notifications, ErrorReporter, appModal ) {

    this.canActivate = function() {
      if ( createAppWizard.app ) {
        return true;
      } else {
        $rootScope.$broadcast('upsNavigate', '/wizard/create-app');
        return false;
      }
    };

    var self = this;

    this.app = createAppWizard.app;

    this.addVariant = function() {
      return variantModal.add( this.app )
        .then(function( variant ) {
          createAppWizard.variant = variant;
          Notifications.success('Variant ' + variant.name + ' successfully created');
          $rootScope.$broadcast('upsNavigate', '/wizard/register-device');
        })
        .catch(function(e) {
          ErrorReporter.error(e, 'Failed to create variant ');
        });
    };

    this.editAppName = function() {
      var appClone = angular.extend( {}, self.app );
      appModal.editName( appClone )
        .then(function( updatedApp ) {
          angular.extend( self.app, updatedApp );
        });
    }

  });



angular.module('upsConsole')
  .controller('Wizard03RegisterDeviceController', function( $rootScope, $router, variantModal, createAppWizard, appModal, $timeout, $interval, applicationsEndpoint ) {

    this.canActivate = function() {
      if ( !createAppWizard.app ) {
        $rootScope.$broadcast('upsNavigate', '/wizard/create-app');
        return false;
      }
      if ( !createAppWizard.variant ) {
        $rootScope.$broadcast('upsNavigate', '/wizard/add-variant');
        return false;
      }
      return true;
    };

    var self = this;

    this.app = createAppWizard.app;
    this.variant = createAppWizard.variant;

    this.addVariant = function() {
      return variantModal.add()
        .then(function( variant ) {
          console.log('success');
          console.log(variant);
          createAppWizard.variant = variant;
          $rootScope.$broadcast('upsNavigate', '/wizard/register-device');
        })
        .catch(function() {
          console.log('error');
        })
    };

    this.editAppName = function() {
      var appClone = angular.extend( {}, self.app );
      appModal.editName( appClone )
        .then(function( updatedApp ) {
          angular.extend( self.app, updatedApp );
        });
    };

    this.editVariant = function() {
      var variantClone = angular.extend({}, self.variant);
      return variantModal.edit( self.app, variantClone )
        .then(function( updatedVariant ) {
          angular.extend(self.variant, updatedVariant);
        });
    };

    function detectInstallations() {
      return applicationsEndpoint.getWithMetrics({appId: createAppWizard.app.pushApplicationID})
        .then(function( data ) {
          return data.$deviceCount > 0;
        })
        .then(function( installationDetected ) {
          if ( installationDetected ) {
            $rootScope.$broadcast('upsNavigate', '/wizard/send-push-notification');
          }
        });
    }

    var intervalForDetectInstallations;

    this.activate = function() {
      $timeout(function() { // timeout is a workaround for bug in the router - canDeactivate is called right after activate
        intervalForDetectInstallations = $interval(function () {
          detectInstallations().then(function (installationDetected) {
            if (installationDetected) {
              $interval.cancel(intervalForDetectInstallations);
            }
          });
        }, 1500);
      }, 500);
    };

    this.canDeactivate = function() {
      if (intervalForDetectInstallations) {
        $interval.cancel(intervalForDetectInstallations);
      }
      return true;
    };

  });



angular.module('upsConsole')
  .controller('Wizard04SendPushNotificationController', function( $rootScope, $router, $interval, $timeout, createAppWizard, Notifications, messageSenderEndpoint, appModal, applicationsEndpoint) {

    var self = this;

    var intervalForUpdateDeviceCount;

    this.canActivate = function() {
      if ( !createAppWizard.app ) {
        $rootScope.$broadcast('upsNavigate', '/wizard/create-app');
        return false;
      }
      if ( !createAppWizard.variant ) {
        $rootScope.$broadcast('upsNavigate', '/wizard/add-variant');
        return false;
      }
      return true;
    };

    this.app = createAppWizard.app;
    this.variant = createAppWizard.variant;
    this.deviceCount = 0;

    this.pushData = {
      'message': {
        'sound': 'default',
        'alert': 'Hello! This is my first notification to ' + (self.variant ? self.variant.name : null),
        'simple-push': 'version=' + new Date().getTime()
      },
      'criteria' : {}
    };



    this.sendNotification = function() {
      messageSenderEndpoint( self.app.pushApplicationID, self.app.masterSecret ).send({}, self.pushData)
        .then(function() {
          $rootScope.$broadcast('upsNotificationSent', self.pushData, self.app);
          Notifications.success('Notification was successfully sent');
          $rootScope.$broadcast('upsNavigate', '/wizard/setup-sender');
        })
        .catch(function() {
          Notifications.error('Failed to sent notification');
        });
    };

    this.editAppName = function() {
      var appClone = angular.extend( {}, self.app );
      appModal.editName( appClone )
        .then(function( updatedApp ) {
          angular.extend( self.app, updatedApp );
        });
    };

    function updateDeviceCount() {
      applicationsEndpoint.getWithMetrics({appId: createAppWizard.app.pushApplicationID})
        .then(function(data) {
          self.deviceCount = data.$deviceCount;
        });
    }

    this.activate = function() {
      updateDeviceCount();

      $timeout(function() { // timeout is a workaround for bug in the router - canDeactivate is called right after activate
        intervalForUpdateDeviceCount = $interval(function () {
          updateDeviceCount();
          if (self.deviceCount > 0) {
            $interval.cancel(intervalForUpdateDeviceCount);
          }
        }, 1500);
      }, 1500);
    };

    this.canDeactivate = function() {
      if (intervalForUpdateDeviceCount) {
        $interval.cancel(intervalForUpdateDeviceCount);
      }
      return true;
    };

  });



angular.module('upsConsole')
  .controller('Wizard05SetupSenderController', function( $rootScope, $router, variantModal, createAppWizard, ContextProvider, appModal ) {

    var self = this;

    this.canActivate = function() {
      if ( !createAppWizard.app ) {
        $rootScope.$broadcast('upsNavigate', '/wizard/create-app');
        return false;
      }
      if ( !createAppWizard.variant ) {
        $rootScope.$broadcast('upsNavigate', '/wizard/add-variant');
        return false;
      }
      return true;
    };

    this.app = createAppWizard.app;
    this.variant = createAppWizard.variant;
    this.contextPath = ContextProvider.contextPath();

    this.editAppName = function() {
      var appClone = angular.extend( {}, self.app );
      appModal.editName( appClone )
        .then(function( updatedApp ) {
          angular.extend( self.app, updatedApp );
        });
    };

  });



angular.module('upsConsole')
  .controller('LinksCheckController', function( docsLinks ) {

    this.links = docsLinks;

  });

'use strict';

angular.module('upsConsole').factory('appModal', function ($modal, applicationsEndpoint) {
  var service = {

    editName: function (app) {
      return $modal.open({
        templateUrl: 'dialogs/edit-app-name.html',
        controller: function ($scope, $modalInstance) {
          $scope.app = app;

          $scope.confirm = function () {
            var data = { name: app.name, description: app.description };
            applicationsEndpoint.update({ appId: app.pushApplicationID }, data )
              .then(function ( updatedApp ) {
                $modalInstance.close( updatedApp );
              });
          };

          $scope.dismiss = function () {
            $modalInstance.dismiss('cancel');
          };
        }
      }).result;
    }
  };

  return service;

});

'use strict';

angular.module('upsConsole').factory('variantModal', function ($modal, $q, variantsEndpoint, allowCreateVariant) {
  var service = {

    editName: function(app, variant) {
      return $modal.open({
        templateUrl: 'dialogs/edit-variant-name.html',
        controller: function($scope, $modalInstance) {
          $scope.variant = variant;

          $scope.confirm = function() {
            updateVariant( app, $scope.variant )
              .then(function ( updatedVariant ) {
                $modalInstance.close( updatedVariant );
              })
              .catch(function(err) {
                $modalInstance.dismiss(err);
              });
          };

          $scope.dismiss = function() {
            $modalInstance.dismiss('cancel');
          };
        }
      }).result;
    },

    add: function (app) {
      return $modal.open({
        templateUrl: 'dialogs/create-variant.html',
        controller: function ($scope, $modalInstance) {

          $scope.isNew = true;
          $scope.variant = {}; // start with empty variant
          $scope.variant.certificates = []; // initialize file list for upload

          $scope.confirm = function () {
            var variantData = extractValidVariantData($scope.variant);

            var createFunction = (variantData instanceof FormData) ? variantsEndpoint.createWithFormData : variantsEndpoint.create;

            createFunction({
              appId: app.pushApplicationID,
              variantType: extractVariantType($scope.variant)
            }, variantData)
              .then(function (variant) {
                $modalInstance.close(variant);
              })
              .catch(function (err) {
                $modalInstance.dismiss(err);
              });
          };

          $scope.dismiss = function () {
            $modalInstance.dismiss('cancel');
          };

          $scope.validateFileInputs = function () {
            switch ($scope.variant.type) {
            case 'ios':
              return $scope.variant.certificates.length > 0;
            }
            return true;
          };

          $scope.allowCreate = function( variantType ) {
            return allowCreateVariant( app, variantType );
          };
        }
      }).result;
    },

    edit: function (app, variant) {
      return $modal.open({
        templateUrl: 'dialogs/create-variant.html',
        controller: function ($scope, $modalInstance) {

          $scope.isNew = false;
          $scope.variant = variant;
          $scope.variant.certificates = []; // initialize file list for upload

          $scope.confirm = function () {
            updateVariant( app, $scope.variant )
              .then(function ( updatedVariant ) {
                $modalInstance.close( updatedVariant );
              });
          };

          $scope.dismiss = function () {
            $modalInstance.dismiss('cancel');
          };

          $scope.validateFileInputs = function () {
            if ($scope.variant.type === 'ios') {
              if (!$scope.variant.id) {
                // for new variant
                return $scope.variant.certificates.length > 0 && !!variant.passphrase; // we must enter certificate and passphrase
              } else {
                // for existing variant...
                if ($scope.variant.certificates.length > 0) { // if there is a certificate selected
                  return !!variant.passphrase; // we must provide a passphase
                }
              }
            }
            return true;
          };
        }
      }).result;
    }
  };

  function updateVariant ( app, variant ) {
    var endpointParams = {
      appId: app.pushApplicationID,
      variantType: extractVariantType(variant),
      variantId: variant.variantID
    };
    var variantData = extractValidVariantData(variant);
    if (variant.type !== 'ios') {
      return variantsEndpoint.update(endpointParams, variantData);
    } else {
      if (variant.certificates.length > 0) {
        return variantsEndpoint.updateWithFormData(endpointParams, variantData);
      } else {
        return variantsEndpoint.patch(endpointParams, {
          name: variant.name,
          description: variant.description,
          production: variant.production
        });
      }
    }
  }

  function extractValidVariantData(variant) {
    var properties = ['name'], result = {};
    switch (variant.type) {
    case 'android':
      properties = properties.concat(['projectNumber', 'googleKey']);
      break;
    case 'ios':
      if (variant.certificates && variant.certificates.length) {
        variant.certificate = variant.certificates[0];
      }
      properties = properties.concat(['production', 'passphrase', 'certificate']);
      var formData = new FormData();
      properties.forEach(function (property) {
        formData.append(property, variant[property] === undefined ? '' : variant[property]);
      });
      return formData;
    default:
      throw 'Unknown variant type ' + variant.type;
    }
    properties.forEach(function (property) {
      result[property] = variant[property];
    });
    return result;
  }

  function extractVariantType( variant ) {
    switch(variant.type) {
    default:
      return variant.type;
    }
  }

  return service;

});

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('ContextProvider', function( $location, apiPrefix ) {
  return {
    /**
     * getter for the root context of the application
     * (the URL where application can be accessed)
     */
    contextPath: function() {
      var href = $location.absUrl();
      href = href.split('#', 1).join('');
      href = href.split('?', 1).join('');
      return href + apiPrefix;
    }
  };
});

'use strict';

angular.module('upsConsole')
  .config(function($httpProvider) {
    $httpProvider.interceptors.push(function($q, $interpolate, Notifications) {
      return {
        'responseError': function(rejection) {
          var messageExpr, message;
          if (rejection.status === 400 && typeof rejection.data === 'object') {
            Object.keys(rejection.data).forEach( function( key ) {
              message = rejection.data[key];
              Notifications.error( message );
            });
          } else {
            messageExpr = $interpolate('Server returned {{status}}: {{statusText}}');
            message = messageExpr( rejection );
            Notifications.error(messageExpr(rejection));
          }
          return $q.reject(rejection);
        }
      };
    });
  })

  .service('ErrorReporter', function( Notifications ) {
    return {
      error: function (e, message) {
        if (e === 'cancel' || e === 'backdrop click' || e === 'escape key press') {
          return;
        }
        Notifications.error(message);
      }
    };
  });

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('pushConfigGenerator', function ($resource, $q, ContextProvider) {

	function cordovaVariantType(variant) {
			return variant.type;
	}

	function variantSpecificConfiguration(variant, config) {
		switch (variant.type) {
		case 'android':
			config.senderID = variant.projectNumber;
		}
	}

	return {
		generate: function(variants) {
			var pushConfig = {
				'pushServerURL': ContextProvider.contextPath()
			};
      if (variants) {
        variants.forEach(function (variant) {
          var type = cordovaVariantType(variant);
          var config = pushConfig[type] = {};
          variantSpecificConfiguration(variant, config);
          config.variantID = variant.variantID;
          config.variantSecret = variant.secret;
        });
      }
			return JSON.stringify(pushConfig, null, 2);
		}
	};

});

'use strict';

/* Directives */
angular.module('upsConsole')

  .directive('upsFiles', function () {
    return {
      scope: {
        'files': '=upsFiles',
        'onChange': '&onChange'
      },
      restrict: 'A',
      replace: false,
      link: function ($scope, $element) {
        $element.bind('change', function (e) {
          $scope.$apply(function() {
            while ($scope.files.length > 0) {
              $scope.files.pop();
            }
            for (var i in e.target.files) {
              if (typeof e.target.files[i] === 'object') {
                $scope.files.push(e.target.files[i]);
              }
            }
            $scope.onChange();
          });
        });
      }
    };
  })

  .directive('upsDoc', function ( docsLinks, $log ) {
    return {
      scope: {
        'docId': '@',
        'param': '='
      },
      restrict: 'A',
      replace: false,
      link: function ($scope, $element, attributes) {
        $element.attr('target', '_blank');
        attributes.$observe('upsDoc', function( upsDocValue ) {

          function updateHref() {
            var href = docsLinks[ upsDocValue ];
            if (href) {
              $element.attr('href', href);
            } else if (Object.keys(docsLinks).length > 0) {
              $log.warn('ups-doc: cannot resolve a link for id: ' + upsDocValue);
              return true;
            }
            return !!href; // return true if we resolved the href
          }

          if (!updateHref()) {
            var unwatch = $scope.$watch(function () {
              return docsLinks[ upsDocValue ];
            }, function () {
              if (updateHref()) {
                unwatch(); // if we finally resolve the href
              }
            });
          }
        });
      }
    };
  })

  .directive('upsPluralize', function () {
    return {
      scope: {
        'noun': '@',
        'count': '=',
        'zero': '@'
      },
      restrict: 'E',
      template:
      '<span class="ups-pluralize" ng-if="count > 0"><span class="count">{{ count }}</span> {{ noun }}{{ count > 1 ? "s" : "" }}</span>' +
      '<span class="ups-pluralize zero" ng-if="count == 0"><span class="count">{{ zero == null ? "0" : zero }}</span> {{ noun }}s</span>'
    };
  })

  .factory('SnippetRetriever', function($http, $templateCache, $q) {
    return {
      get: function( snippetUrl ) {
        var cacheResult = $templateCache.get( snippetUrl );
        if ( cacheResult ) {
          return $q.when( { data: cacheResult } );
        }
        return $http.get( snippetUrl )
          .then(function( response ) {
            $templateCache.put( snippetUrl, response.data );
            return response;
          });
      }
    };
  })

  .factory('clientSnippets', function() {
    return {
      'android': { url: 'snippets/register-device/android.java' },
      'cordova': { url: 'snippets/register-device/cordova.js' },
      'ios_objc': { url: 'snippets/register-device/ios.objc' },
      'ios_swift': { url: 'snippets/register-device/ios.swift' }
    };
  })

  .factory('ClientSnippetService', function(SnippetRetriever, $q, clientSnippets) {
    var promises = {};
    angular.forEach(clientSnippets, function (value, key) {
      promises[key] = SnippetRetriever.get(value.url)
        .then(function(response){
          clientSnippets[key].template = response.data;
        });
    });
    return {
      populate: function (result) {
        return $q.all(promises)
          .then(function () {
            angular.forEach(clientSnippets, function (value, key) {
              if (!result[key]) {
                result[key] = {};
              }
              if (!result[key].template) {
                result[key].template = value.template;
              }
            });
            return result;
          });
      }
    };
  })

  .directive('upsClientSnippets', function (ClientSnippetService) {
    return {
      templateUrl: 'directives/ups-client-snippets.html',
      scope: {
        variant: '=',
        allVariants: '='
      },
      restrict: 'E',
      controller: function( $scope, ContextProvider, SnippetRetriever, $sce, $interpolate, $timeout, pushConfigGenerator ) {
        $scope.clipText = $sce.trustAsHtml('Copy to clipboard');
        $scope.contextPath = ContextProvider.contextPath();
        $scope.typeEnum = {
          android:      { name: 'Android',    snippets: ['android', 'cordova', 'push_config'] },
          ios:          { name: 'iOS',        snippets: ['ios_objc', 'ios_swift', 'cordova', 'push_config']},
        };
        $scope.state = {
          activeSnippet: $scope.typeEnum[$scope.variant.type].snippets[0]
        };
        $scope.snippets = {};
        function renderSnippets() {
          ClientSnippetService.populate($scope.snippets).then(function() {
            angular.forEach($scope.snippets, function(value, key) {
              $scope.snippets[key].source = $interpolate($scope.snippets[key].template)($scope);
            });
          });
          $scope.pushConfigSource = pushConfigGenerator.generate($scope.allVariants);
        }
        renderSnippets();
        $scope.copySnippet = function() {
          return $scope.snippets[$scope.state.activeSnippet].source;
        };
        $scope.copied = function() {
          $scope.clipText = 'Copied!';
          $timeout(function() {
            $scope.clipText = 'Copy to clipboard';
          }, 1000);
        };
        $scope.cordovaVariantType = (function() {
            return $scope.variant.type;
        })();
        $scope.senderID = $scope.variant.type === 'android' ? $scope.variant.projectNumber : null;
        $scope.$watch('variant.secret', function() {
          renderSnippets();
        });
      }
    };
  })

  .factory('senderSnippets', function() {
    return {
      java: {
        url: 'snippets/senders/sender.java',
        show: true,
        text: {
          before: '<p>First add <code>unifiedpush-java-client.jar</code> as a <a ups-doc="sender-downloads-java">dependency to your Java project</a>.</p>' +
                  '<p>Then use the following snippet in your Java code to enable push notification sending.</p>',
          after:  '<p>Read more on the details of the <a ups-doc="sender-api-java">Java UPS Sender API in documentation</a>.</p>' +
                  '<p>If you have questions about this process, <a ups-doc="sender-step-by-step-java">visit the documentation for full step by step explanation</a>.</p>'
        }
      },
      nodejs: {
        url: 'snippets/senders/sender-nodejs.js',
        show: true,
        text: {
          before: '<p>First download add <code>unifiedpush-node-sender</code> as a <a ups-doc="sender-downloads-nodejs">dependency to your project</a>.</p>' +
                  '<p>Then use the following snippet in your Node.js code to enable push notification sending.</p>',
          after:  '<p>Read more on the details of the <a ups-doc="sender-api-nodejs">Node.js UPS Sender API in documentation</a>.</p>'
        }
      },
      curl: {
        url: 'snippets/senders/sender-curl.sh',
        show: true,
        text: {
          before: '<p>If none of the official client libs suit you or you just want to simply try out the notification sending, you can use the REST API directly.</p>' +
                  '<p>Run the following <code>curl</code> command in a shell to send a notification to UPS server.</p>',
          after:  '<p>Read more about the <a ups-doc="sender-api-rest">UPS REST Sender API in the documentation</a>.</p>'
        }
      }
    };
  })

  .directive('upsSenderSnippets', function () {
    return {
      restrict: 'E',
      templateUrl: 'directives/ups-sender-snippets.html',
      scope: {
        app: '=',
        activeSnippet: '@'
      },
      controller: function( $scope, ContextProvider, SnippetRetriever, $sce, $interpolate, $timeout, senderSnippets ) {

        $scope.clipText = $sce.trustAsHtml('Copy to clipboard');
        $scope.contextPath = ContextProvider.contextPath();
        $scope.snippets = senderSnippets;
        function renderSnippets() {
          angular.forEach($scope.snippets, function(data, senderType) {
            if (data.show) {
              $scope.activeSnippet = $scope.activeSnippet || senderType;
              SnippetRetriever.get(data.url)
                .then(function (response) {
                  $scope.snippets[senderType].source = $interpolate(response.data)($scope);
                });
            }
          });
        }
        renderSnippets();
        $scope.copySnippet = function() {
          return $scope.snippets[$scope.activeSnippet].source;
        };
        $scope.copied = function() {
          $scope.clipText = 'Copied!';
          $timeout(function() {
            $scope.clipText = 'Copy to clipboard';
          }, 1000);
        };
        $scope.$watch('app.masterSecret', function() {
          renderSnippets();
        });
      }
    };
  })

  .directive('searchPf', function() {
    return {
      scope: {
      },
      restrict: 'C',
      replace: false,
      link: function ($scope, $element) {
        $element.find('.has-clear .clear').each(function() {
          if (!$(this).prev('.form-control').val()) {
            $(this).hide();
          }
        });
        // Show the clear button upon entering text in the search input
        $element.find('.has-clear .form-control').keyup(function () {
          var t = $(this);
          t.next('button').toggle(Boolean(t.val()));
        });
        // Upon clicking the clear button, empty the entered text and hide the clear button
        $element.find('.has-clear .clear').click(function () {
          $(this).prev('.form-control').val('').focus();
          $(this).hide();
        });
      }
    };
  })

  .directive('upsWizard', function() {
    return {
      scope: {
      },
      restrict: 'A',
      replace: false,
      link: function ($scope, $element) {
        var highestCol = 0;
        $element.find('.well').each(function () {
          highestCol = Math.max(highestCol, $(this).height());
        });
        $element.find('.well').height(highestCol);
      }
    };
  })

  .directive('sidebarPf', function() {
    return {
      restrict: 'C',
      link: function () {
        sidebar();
      }
    };
  })

  .directive('prettyprint', function() {
    return {
      restrict: 'C',
      link: function ($scope, $element) {
        var unwatch = $scope.$watch(function() {
          return $element.text();
        }, function( text ) {
          if (text) {
            window.requestAnimationFrame(function() {
              $scope.var = $element.html();
              $element.html(prettyPrintOne($element.html(), '', false));
            });
          }
        });
        $scope.$on('$destroy', unwatch);
      }
    };
  })

  .directive('upsBindHtmlCompile', function ($compile) {
    return function (scope, element, attrs) {
      var ensureCompileRunsOnce = scope.$watch(
        function (scope) {
          return scope.$eval(attrs.upsBindHtmlCompile);
        },
        function (value) {
          element.html(value);
          $compile(element.contents())(scope);
          ensureCompileRunsOnce();
        }
      );
    };
  });

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('applicationsEndpoint', function ($resource, $q, apiPrefix) {
  return $resource( apiPrefix + 'rest/applications/:appId/:verb', {
    appId: '@appId'
  }, {
    get: {
      method: 'GET',
      params: {
        includeDeviceCount: true,
        includeActivity: true
      }
    },
    list: {
      method: 'GET',
      isArray: true,
      params: {
        includeDeviceCount: true,
        includeActivity: true
      }
    },
    create: {
      method: 'POST'
    },
    update: {
      method: 'PUT'
    },
    delete: {
      method: 'DELETE'
    },
    count: {
      method: 'GET',
      params: {verb: 'count'}
    },
    reset: {
      method: 'PUT',
      params: {verb: 'reset'}
    },
    fetch: function(pageNo) {
      var deferred = $q.defer();
      this.list({page: pageNo - 1, per_page: 8}, function (apps, responseHeaders) {
        var result = {
          apps: apps,
          totalItems: responseHeaders('total')
        };
        apps.forEach(function(app) {
          app.$messageCount = parseInt(responseHeaders('activity_app_' + app.pushApplicationID), 10);
          app.$deviceCount = parseInt(responseHeaders('deviceCount_app_' + app.pushApplicationID), 10);
        });
        deferred.resolve( result );
      });
      return deferred.promise;
    },
    getWithMetrics: function(params) {
      var deferred = $q.defer();
      this.get(params, function (app, responseHeaders) {
        app.$messageCount = parseInt(responseHeaders('activity_app_' + app.pushApplicationID), 10);
        app.$deviceCount = parseInt(responseHeaders('deviceCount_app_' + app.pushApplicationID), 10);
        app.variants.forEach(function( variant ) {
          variant.$messageCount = parseInt(responseHeaders('activity_variant_' + variant.variantID), 10);
          variant.$deviceCount = parseInt(responseHeaders('deviceCount_variant_' + variant.variantID), 10);
        });
        deferred.resolve( app );
      });
      return deferred.promise;
    }
  });
});

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('variantsEndpoint', function ($resource, apiPrefix) {
  return $resource( apiPrefix + 'rest/applications/:appId/:variantType/:variantId/:verb', {
    appId: '@appId',
    variantType: '@variantType',
    variantId: '@variantId'
  }, {
    get: {
      method: 'GET'
    },
    query: {method: 'GET', isArray: true},
    create: {
      method: 'POST'
    },
    update: {
      method: 'PUT'
    },
    delete: {
      method: 'DELETE'
    },
    patch: {
      method: 'PATCH'
    },
    createWithFormData: {
      method: 'POST',
      headers: {'Content-Type': undefined},
      withCredentials: true,
      transformRequest: angular.identity
    },
    updateWithFormData: {
      method: 'PUT',
      headers: {'Content-Type': undefined},
      withCredentials: true,
      transformRequest: angular.identity
    },
    reset: {
      method: 'PUT',
      params: {verb: 'reset'}
    }
  });
});

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('installationsEndpoint', function ($resource, $q, apiPrefix) {
  var installationsService = $resource( apiPrefix + 'rest/applications/:variantId/installations/:installationId', {
    variantId: '@variantId',
    installationId: '@installationId'
  }, {
    get: {
      method: 'GET',
      isArray: true
    },
    update: {
      method: 'PUT'
    }
  });
  installationsService.fetchInstallations = function(variantId, searchString, pageNo, perPage) {
    var deferred = $q.defer();
    this.get({variantId: variantId, search: searchString, page: pageNo - 1, per_page: perPage}, function (data, responseHeaders) {
      deferred.resolve({
        installations: data,
        total: responseHeaders('total')
      });
    });
    return deferred.promise;
  };
  return installationsService;
});

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('messageSenderEndpoint', function ($resource, apiPrefix) {
  return function ( applicationID, masterSecret ) {
    var url = apiPrefix + 'rest/sender';
    var paramDefaults = {};
    var actions = {
      send: {
        method: 'POST',
        headers: {
          'aerogear-sender': 'AeroGear UnifiedPush Console',
          'Authorization': 'Basic ' + btoa(applicationID + ':' + masterSecret)
        }
      }
    };

    return $resource(url, paramDefaults, actions);
  };
});

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('metricsEndpoint', function ($resource, $q, apiPrefix) {
  return $resource( apiPrefix + 'rest/metrics/messages/:verb/:id', { id: '@id' }, {
    application: {
      method: 'GET',
      isArray: true,
      params: {
        verb: 'application'
      }
    },
    fetchApplicationMetrics: function(applicationId, searchString, pageNo, perPage) {
      perPage = perPage || 10;
      searchString = searchString || null;
      var deferred = $q.defer();
      this.application({id: applicationId, page: pageNo - 1, per_page: perPage, sort:'desc', search: searchString}, function (data, responseHeaders) {
        angular.forEach(data, function (metric) {
          metric.totalVariants = 0;
          metric.servedVariants = 0;
          metric.totalReceivers = 0;
        });
        deferred.resolve({
          totalItems: parseInt(responseHeaders('total'), 10),
          receivers: parseInt(responseHeaders('receivers'), 10),
          appOpenedCounter: parseInt(responseHeaders('appOpenedCounter'), 10),
          pushMetrics: data
        });
      });
      return deferred.promise;
    }
  });
});

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('dashboardEndpoint', function ($resource, apiPrefix) {
  return $resource( apiPrefix + 'rest/metrics/dashboard/:verb', {}, {
    totals: {
      method: 'GET'
    },
    warnings: {
      method: 'GET',
      isArray: true,
      params: {
        verb: 'warnings'
      }
    },
    latestActiveApps: {
      method: 'GET',
      isArray: true,
      params: {
        verb: 'active',
        count: 4
      }
    }
  });
});

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('importerEndpoint', function ($resource, apiPrefix) {
  return $resource( apiPrefix + 'rest/registry/device/importer', {}, {
    import: {
      method: 'POST',
      headers: {'Content-Type': undefined},
      withCredentials: true,
      transformRequest: angular.identity
    }
  });
});

'use strict';

var upsServices = angular.module('upsConsole');

upsServices.factory('exporterEndpoint', function ($resource, apiPrefix) {
  return $resource( apiPrefix + 'rest/export/:variantId/installations', {}, {
    export: {
      method: 'GET',
      isArray: true
    }
  });
});
