text: var app = angular.module('AffilinetToolbar', [
      'ui.bootstrap',
      'ui.bootstrap.popover',
      'ui.router',
      'angular-momentjs',
      'xml',
      'template',
      'angular-flot',
      'angular-clipboard',
      'pascalprecht.translate',
      'oi.select',
      'mgcrea.bootstrap.affix',
      'xeditable',
      'ngDragDrop',
      'slickCarousel',
      'rzModule',
      'ngSanitize'
  ], function ($compileProvider) {
      "use strict";
      $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|resource|chrome-extension|moz-extension):/);
  
  }).config(function ($httpProvider, $sceDelegateProvider, $translateProvider) {
  
      $httpProvider.interceptors.push('xmlHttpInterceptor');
  
      $sceDelegateProvider.resourceUrlWhitelist([
          // Allow same origin resource loads.
          'self'
      ]);
  
      $translateProvider.useSanitizeValueStrategy(null);
  
      $translateProvider.fallbackLanguage('en');
  
      switch (navigator.language) {
          case 'de-DE':
          case 'de-de':
          case 'de-AT':
          case 'de-at':
          case 'de-CH':
          case 'de-ch':
          case 'de-li':
          case 'de-LI':
          case 'de-LU':
          case 'de-lu':
          case 'de':
          case 'at':
          case 'ch':
              $translateProvider.preferredLanguage('de');
              break;
  
          case 'es-ES' :
          case 'es-es' :
          case 'es-AR' :
          case 'es-BO' :
          case 'es-CL' :
          case 'es-CO' :
          case 'es-CR' :
          case 'es-DO' :
          case 'es-EC' :
          case 'es-GT' :
          case 'es-HN' :
          case 'es-MX' :
          case 'es-NI' :
          case 'es-PA' :
          case 'es-PE' :
          case 'es-PR' :
          case 'es-PY' :
          case 'es-SV' :
          case 'es-UY' :
          case 'es-VE' :
          case 'es' :
              $translateProvider.preferredLanguage('es');
              break;
  
          case 'fr-BE' :
          case 'fr-CA' :
          case 'fr-ca' :
          case 'fr-FR' :
          case 'fr-fr' :
          case 'fr-CH' :
          case 'fr-ch' :
          case 'fr-LU' :
          case 'fr-lu' :
          case 'fr-MC' :
          case 'fr-mc' :
          case 'fr' :
              $translateProvider.preferredLanguage('fr');
              break;
  
          case 'nl-NL' :
          case 'nl-nl' :
          case 'nl-BE' :
          case 'nl-be' :
          case 'nl' :
          case 'be' :
              $translateProvider.preferredLanguage('nl');
              break;
  
          default:
              $translateProvider.preferredLanguage('en');
              break;
  
  
      }
  
  
  }).run(function(editableOptions, editableThemes) {
      // set `default` theme
      editableOptions.theme = 'bs3';
  
      editableThemes['bs3'].submitTpl = '<button class="btn btn-primary" type="submit"><i class="fa fa-check"</button>';
      editableThemes['bs3'].cancelTpl = '<button class="btn btn-default"><i class="fa fa-times-circle"></i></button>';
  
  })
  
  
  
  'use strict';
  
  /**
   * Route configuration for the AffilinetToolbar module.
   */
  angular.module('AffilinetToolbar').config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
      function ($stateProvider, $urlRouterProvider, $locationProvider) {
          // For unmatched routes
          $urlRouterProvider.otherwise(function ($injector, $location) {
  
              if ($location.$$hash == '') {
                  return $location.$$path;
              }
              return $location.$$hash;
  
          });
  
          $locationProvider.html5Mode(false);
  
          // Application routes
          $stateProvider
              .state('settings', {
                  url: '/settings',
                  templateUrl: 'templates/settings.html',
                  controller: 'SettingsController'
              })
              .state('orders', {
                  url: '/orders',
                  templateUrl: 'templates/orders.html',
                  controller: 'OrdersController'
              })
  
              .state('news', {
                  url: '/news',
                  templateUrl: 'templates/news.html',
                  controller: 'NewsController'
              })
              .state('applynow', {
                  url: '/applynow/:programId',
                  templateUrl: 'templates/applyNow.html',
                  controller: 'applyNowController'
  
              })
              .state('getVouchers', {
                  url: '/getVouchers/:programId',
                  templateUrl: 'templates/getVouchers.html',
                  controller: 'getVouchersController'
              })
              .state('getCreatives', {
                  url: '/getCreatives/:programId',
                  templateUrl: 'templates/getCreatives.html',
                  controller: 'getCreativesController'
              })
              .state('searchDiscover', {
                  url: '/searchDiscover',
                  templateUrl: 'templates/searchDiscover.html',
                  controller: 'SearchDiscoverController'
              })
              .state('likeList', {
                  url: '/likeList',
                  templateUrl: 'templates/likeList.html',
                  controller: 'LikeListController'
              })
              .state('widget', {
                  url: '/widget',
                  params: {
                      productIds: [],
                      widgetId: null
                  },
                  templateUrl: 'templates/widget.html',
                  controller: 'WidgetController'
              })
              .state('allwidgets', {
                  url: '/allwidgets',
                  params: {
                  },
                  templateUrl: 'templates/allwidgets.html',
                  controller: 'AllWidgetsController'
              })
          ;
  
  
      }
  ]);
  angular.module('AffilinetToolbar')
      .controller('AllWidgetsController', ['$scope', '$sce', '$translate', 'LogonService', AllWidgetsController]);
  
  function AllWidgetsController($scope,  $sce, $translate,   LogonService) {
  
  
  
      $scope.loadingFinished = false;
      
      $translate('ALLWIDGETS_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
  
      $scope.messages = [];
  
      $translate('ALLWIDGETS_OldProducts').then(function (text) {
          $scope.messages.ALL_WIDGETS_CLEAR_OLD_PRODUCS = text;
      });
  
      $scope.allWidgets = [];
  
  
      $scope.copySuccess = false;
  
  
      $scope.trustAsUrl = function(url) {
          "use strict";
          return $sce.trustAsUrl(url);
      }
  
  
      $scope.deletedProductCount = function(widget) {
          "use strict";
          let count = 0;
          widget.products.forEach(
              function(prod) {
                  if (prod.deleted === true) {
                      count++;
                  }
              }
          );
          return count;
      }
  
      $scope.clearOldProduct = function(widget) {
          "use strict";
          let newProds = [];
          widget.products.forEach(
              function(prod) {
                  if (prod.deleted === false) {
                      newProds.push(prod);
                  }
              }
          );
          widget.products =  newProds;
          updateWidget(widget)
  
          return count;
      }
  
  
      let updateWidget = function(widget) {
          LogonService.WidgetUpdate(widget.id, widget).then(function (result) {
              "use strict";
              let changedWidget = result.data;
              let index = $scioe.allWidgets.findIndex(function(widg) { return widg.id === changedWidget.id});
              $scope.allWidgets[index] = changedWidget;
              $scope.$parent.sendAlert($scope.messages.ALLWIDGETS_OldProductsRemoved, 'success')
  
  
          }, function (error) {
              "use strict";
              console.error(error);
              $scope.$parent.sendAlert( 'Could not update widget', 'danger')
          });
  
  
      };
  
  
  
      $scope.deleteWidget = function() {
  
          /**
           * delete it serverside, then remove from allWidgets
           */
  
          if (!confirm($scope.messages.ALL_WIDGETS_SureYouWantToDeleteWidget)) {
              return;
          }
  
          LogonService.WidgetDelete($scope.widget.id).then(
              function(result) {
                  const index = $scope.allWidgets.findIndex((widget) => {return widget.id === $scope.widget.id});
                  $scope.selectedWidget = null;
                  $scope.widget = angular.copy($scope.defaultWidget);
                  $scope.addWatchToWidget();
                  $scope.allWidgets.splice(index, 1);
              },
              function(error) {
                  $scope.$parent.sendAlert('Could not delete widget. Please check internet connection', 'danger')
              }
          )
      };
  
  
  
  
      /**
       * Load the widgets
       */
  
      let init = () => {
          LogonService.WidgetIndex().then(function(response){
              $scope.allWidgets = response.data;
              $scope.loadingFinished = true;
  
          }, function (error) {
              $scope.$parent.sendAlert('Could not load widgets, please check internet connection', 'danger')
              $scope.loadingFinished = true;
          })
      };
  
      init()
  
  
  
  
  };
  
  angular.module('AffilinetToolbar')
      .controller('applyNowController', ['$scope', '$rootScope', 'LogonService', '$sce', '$location', '$moment', '$translate', '$stateParams', applyNowController]);
  
  function applyNowController($scope, $rootScope, LogonService, $sce, $location, $moment, $translate, $stateParams) {
      console.log($stateParams.programId);
  
      $scope.programDescription = 'No program description yet';
      $scope.limitationsComment = '';
      $scope.LaunchDate = '';
  
      $scope.loadingFinished = false;
      $translate('APPLY_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
  
      String.prototype.trunc = String.prototype.trunc ||
          function (n) {
              return (this.length > n) ? this.substr(0, n - 1) + '...' : this;
          };
  
  
      LogonService.GetProgram($stateParams.programId).then(function (response) {
          $scope.program = response.data.Envelope.Body.GetProgramsResponse.ProgramCollection.Program;
  
          console.log(response.data);
  
          if ($scope.program && $scope.program.ProgramDescription) {
              $scope.programDescription = $scope.program.ProgramDescription.toString().replace(/<(?:.|\n)*?>/gm, '').trunc(100);
          }
  
          if ($scope.program && $scope.program.LimitationsComment) {
              $scope.limitationsComment = $scope.program.LimitationsComment.toString().replace(/<(?:.|\n)*?>/gm, '').trunc(50);
          }
  
          if ($scope.program && $scope.program.LaunchDate) {
              $scope.LaunchDate = $moment($scope.program.LaunchDate.toString(), 'YYYY-MM-DDTHH:mm:ss').format('DD.MM.YYYY');
          }
  
  
  
          $scope.loadingFinished = true;
      }, function error(response) {
          console.log(response.data.Envelope.Body.Fault);
      })
  }
  
  
  
  angular.module('AffilinetToolbar')
      .controller('getCreativesController', ['$scope', '$rootScope', 'LogonService', '$translate', '$stateParams', getCreativesController]);
  
  function getCreativesController($scope, $rootScope, LogonService, $translate, $stateParams) {
      $scope.loadingFinished = false;
      $translate('GETCREATIVES_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
  
  
      $scope.sizes = {
          'Leaderbord/Super Banner': '728x90',
          'Standard Banner/Full Banner': '468x60',
          'Medium Rectangle': '300x250',
          'Square Button': '250x250',
          'Wide Skyscraper': '160x600',
          'Skyscraper': '120x600'
      };
  
      $scope.size = '728x90';
  
      LogonService.GetCreatives($stateParams.programId).then(function (response) {
          $scope.creatives = [];
  
          angular.forEach(response.data.Envelope.Body.SearchCreativesResponse.CreativeCollection.Creative, function (creative) {
              $scope.creatives.push(creative);
          });
  
          $scope.loadingFinished = true;
  
      }, function error(response) {
          console.log(response.data.Envelope.Body.Fault.faultstring.toString());
      });
  
      $scope.show = function (creative) {
          var shown;
          if ((typeof creative.BannerStub != 'undefined') && (typeof creative.BannerStub.Width != 'undefined')) {
              shown = (creative.BannerStub.Width.toString() + 'x' + creative.BannerStub.Height.toString() == $scope.size);
              return shown;
          }
          if ((typeof creative.HTMLStub != 'undefined') && (typeof creative.HTMLStub.Width != 'undefined')) {
              shown = (creative.HTMLStub.Width.toString() + 'x' + creative.HTMLStub.Height.toString() == $scope.size);
              return shown;
          }
      };
  
  
      $scope.getPreviewURL = function (creative) {
          var type, platform, previewUrl, integrationCode;
          integrationCode = creative.IntegrationCode.toString();
  
          console.log($rootScope.credentials.publisherId );
  
  
          // detect the platform depending on integration code
          if (integrationCode.search('/banners.webmasterplan.com/')) {
              platform = 'de';
          } else if (integrationCode.search('/become.successfultogether.co.uk/')) {
              platform = 'uk';
          } else if (integrationCode.search('/banniere.reussissonsensemble.fr/')) {
              platform = 'fr';
          } else if (integrationCode.search('/worden.samenresultaat.nl/')) {
              platform = 'nl';
          } else if (integrationCode.search('/program.epartner.es/')) {
              platform = 'es';
          }
  
          // set banner or html
          if (creative.CreativeTypeEnum.toString() == 'Banner') {
              type = 'b';
          } else {
              type = 'h';
          }
  
          // return the preview URL
          return 'http://www.affilinet-toolbar.com/creativepreview.php?platform='
              + platform
              + '&ref='
              + $rootScope.credentials.publisherId
              + '&site='
              + parseInt(creative.ProgramId.toString())
              + '&ad_type='
              + type
              + '&ad_number='
              + parseInt(creative.CreativeNumber.toString());
      };
  
  }
  
  angular.module('AffilinetToolbar')
      .controller('getVouchersController', ['$scope', '$rootScope', 'LogonService', '$stateParams', '$translate', getVouchersController]);
  
  function getVouchersController($scope, $rootScope, LogonService, $stateParams, $translate) {
      $scope.loadingFinished = false;
  
      $translate('GETVOUCHERS_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
  
      LogonService.GetProgram($stateParams.programId).then(function (response) {
          $scope.programInfo = response.data.Envelope.Body.GetProgramsResponse.ProgramCollection.Program;
  
  
      });
  
      LogonService.GetVouchers($stateParams.programId).then(function (response) {
          $scope.vouchersCount = parseInt(response.data.Envelope.Body.SearchVoucherCodesResponse.TotalResults);
  
  
          if ($scope.vouchersCount == 0) {
              $scope.noVouchers = true;
          }
          else if ($scope.vouchersCount === 1) {
              console.log('one voucher');
              $scope.noVouchers = false;
              console.log(response.data.Envelope.Body.SearchVoucherCodesResponse);
              $scope.vouchers = [];
              $scope.vouchers[0] = response.data.Envelope.Body.SearchVoucherCodesResponse.VoucherCodeCollection.VoucherCodeItem;
          }
          else {
              console.log('else voucher');
              $scope.noVouchers = false;
              console.log(response.data.Envelope.Body.SearchVoucherCodesResponse);
              $scope.vouchers = response.data.Envelope.Body.SearchVoucherCodesResponse.VoucherCodeCollection.VoucherCodeItem;
          }
          $scope.contentLoaded = true;
          $scope.loadingFinished = true;
      }, function error(response) {
          console.log(response.data.Envelope.Body.Fault.faultstring.toString());
      })
  }
  
  angular.module('AffilinetToolbar')
      .controller('LikeListController', ['$scope', '$translate', 'BrowserExtensionService', 'productWebservice', '$location',   LikeListController]);
  
  function LikeListController($scope,  $translate, BrowserExtensionService, productWebservice,  $location) {
      $scope.loadingFinished = false;
  
      let searchParams = $location.search();
      if (searchParams.tab) {
          $scope.activeTabIndex = +searchParams.tab
      } else {
          $scope.activeTabIndex = 0;
      }
  
      $translate('LIKELIST_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
      $scope.messages = [];
      $translate('LIKELIST_ListNamesMustBeUnique').then(function (text) {
          $scope.messages.LIKELIST_ListNamesMustBeUnique = text;
      });
      $translate('LIKELIST_SureYouWantToDeleteProduct').then(function (text) {
          $scope.messages.LIKELIST_SureYouWantToDeleteProduct = text;
      });
  
      $translate('LIKELIST_SureYouWantToDeleteLike').then(function (text) {
          $scope.messages.LIKELIST_SureYouWantToDeleteLike = text;
      });
      $translate('LIKELIST_SureYouWantToDeleteProductList').then(function (text) {
          $scope.messages.LIKELIST_SureYouWantToDeleteProductList = text;
      });
  
      $scope.likeList = [];
      $scope.storedProductLists = [];
      $scope.productDetails = [];
      $scope.selectedProducts = [];
      $scope.selectedProductIds = [];
  
      $scope.programUrlsRequested = [];
      $scope.programUrls = [];
  
      $scope.newProductListKey = null;
  
      $scope.likeImageCount = 0;
      $scope.likeWebsiteCount = 0;
      $scope.productMenuPopoverTemplate = 'productMenuPopoverTemplate.html';
      $scope.pinMenuPopoverTemplate = 'pinMenuPopoverTemplate.html';
  
  
      $scope.shareOn = function(socialNetworkName, productId) {
          "use strict";
          BrowserExtensionService.runtime.sendMessage({
              action: "share-on-" + socialNetworkName,
              data : getShareDetails(productId)
          });
      };
  
      $scope.shareLikeOn = function(socialNetworkName, like) {
          "use strict";
          BrowserExtensionService.runtime.sendMessage({
              action: "share-on-" + socialNetworkName,
              data : like
          });
      };
      $scope.copyDeeplink = function(like) {
  
          $scope.$parent.sendAlert('Deeplink copied', 'success');
          BrowserExtensionService.runtime.sendMessage({
              action: "copyDeeplink",
              data : like
          }, function(resposne) {
          });
      };
      $scope.copyImageCode = function(like) {
          $scope.$parent.sendAlert('Image Code  copied', 'success');
          BrowserExtensionService.runtime.sendMessage({
              action: "copyImageCode",
              data : like
          }, function(resposne) {
  
          });
      };
      $scope.copyDeeplinkForProduct = function(productId) {
          $scope.$parent.sendAlert('Deeplink copied', 'success');
          BrowserExtensionService.runtime.sendMessage({
              action: "copyDeeplink",
              data : getShareDetails(productId)
          }, function(resposne) {
          });
      };
  
      $scope.copyImageCodeForProduct = function(productId){
          "use strict";
          $scope.$parent.sendAlert('Image Code  copied', 'success');
          BrowserExtensionService.runtime.sendMessage({
              action: "copyImageCode",
              data : getShareDetails(productId)
          }, function(resposne) {
  
          });
      };
      function getShareDetails(productId) {
          const productDetails = $scope.productDetails[productId];
          return {
              image : {
                  src  : productDetails.Images[0][0].URL,
                  width : productDetails.Images[0][0].Width,
                  height : productDetails.Images[0][0].Height,
                  alt : productDetails.ProductName,
                  title : productDetails.ProductName,
              },
              hasDeeplink: false,
              uri: productDetails.Deeplink1,
              pageTitle: productDetails.ProductName
          };
      }
  
      $scope.selectedNewListForProduct = function(productId, fromList, toList, productKey) {
          "use strict";
          console.log('selected new list for product', productId, fromList,  toList);
          $scope.storedProductLists[toList].products.push(productId);
          $scope.storedProductLists[fromList].products.splice(productKey, 1);
          $scope.newProductListKey = null;
      };
  
      $scope.checkProductListName = function (name, list) {
          console.log(name, list);
          if (name === list.name) {
              return true;
          }
          const foundIndex = $scope.storedProductLists.findIndex((productList) => {
              return productList.name === name;
          });
          if (foundIndex >= 0) {
              alert($scope.messages.LIKELIST_ListNamesMustBeUnique);
              return false
          }
          return true;
      };
  
      $scope.createProductList = function (name) {
  
          console.log('create product list', name);
          if (name === undefined) {
              return false;
          }
          const foundIndex = $scope.storedProductLists.findIndex((productList) => {
              return productList.name === name;
          });
          if (foundIndex >= 0) {
              alert($scope.messages.LIKELIST_ListNamesMustBeUnique);
              return false
          }
          let id = new Date().getTime() + '-' + Math.random();
          let newItem = {name: name, products : [], id: id};
          $scope.storedProductLists.unshift(newItem);
          return false;
      };
  
  
      attachWatchHandlers = function() {
          console.log('attach watch handler');
          $scope.$watch('storedProductLists', function(newVal, oldVal) {
              console.log('changed storedProductLists', newVal, oldVal);
              BrowserExtensionService.storage.local.set({storedProductLists: newVal });
          }, true)
          $scope.$watch('likeList', function(newVal, oldVal) {
              console.log('changed likeList', newVal, oldVal);
              $scope.likeImageCount = 0;
              $scope.likeWebsiteCount = 0;
              angular.forEach(newVal, (like) => {
                  if(like.type === 'image') {
                      $scope.likeImageCount++
                  } else {
                      $scope.likeWebsiteCount++;
                  }
              });
              BrowserExtensionService.storage.local.set({likeList: newVal });
          }, true)
      };
  
  
      $scope.deleteProduct = function(event, productKey, listKey) {
          event.preventDefault();
          event.stopPropagation();
          if (confirm($scope.messages.LIKELIST_SureYouWantToDeleteProduct)) {
              $scope.storedProductLists[listKey].products.splice(productKey, 1);
          }
      };
  
  
      $scope.deleteProductList = function(listKey) {
          if (confirm($scope.messages.LIKELIST_SureYouWantToDeleteProductList)) {
              $scope.storedProductLists.splice(listKey, 1);
          }
  
      };
  
      $scope.deleteLike = function(likeKey) {
          if (confirm($scope.messages.LIKELIST_SureYouWantToDeleteLike)) {
              $scope.likeList.splice(likeKey, 1);
          }
      }
  
      $scope.toggleSelectProduct = function(event, productId) {
          console.log(productId);
          event.preventDefault();
          event.stopPropagation();
          console.log(event);
          if ($scope.selectedProductIds.includes(productId)) {
              $scope.selectedProducts = $scope.selectedProducts
                  .filter( (prod) => prod.ProductId !== productId);
  
              $scope.selectedProductIds = $scope.selectedProductIds
                  .filter( (prodId) => prodId !== productId);
  
          } else {
              if ($scope.productDetails[productId]) {
                  $scope.selectedProducts.push($scope.productDetails[productId]);
                  $scope.selectedProductIds.push(productId);
              }
  
          }
      };
  
      let getProgramUrlForProgramId = function(ProgramId) {
          if (!$scope.programUrlsRequested[ProgramId]) {
              $scope.programUrlsRequested[ProgramId] = true;
              BrowserExtensionService.runtime.sendMessage({action : 'get-programDetailsForProgramId', data : { programId : ProgramId}},
                  function(programDetails){
                      if (programDetails !== false) {
                          $scope.$apply(function(){
                              $scope.programUrls[ProgramId] = programDetails.programUrl;
                          });
                      }
                  })
          }
      }
  
      BrowserExtensionService.storage.local.get(['likeList', 'storedProductLists'], function (res) {
          console.log('loading likelist');
          "use strict";
          if (res.likeList) {
              $scope.likeList = res.likeList;
          }
          if (res.storedProductLists) {
              $scope.storedProductLists = res.storedProductLists;
          }
          attachWatchHandlers();
  
          let allProductIds = [];
          angular.forEach($scope.storedProductLists, function (productList) {
              productList.products.forEach(function (prodId) {
                  allProductIds.push(prodId)
              })
          });
  
          let i = 0;
          let batch = 0;
          let productBatches = [];
          angular.forEach(allProductIds, (productId) => {
  
              if (!productBatches[batch]) {
                  productBatches[batch] = [productId];
              } else {
                  productBatches[batch].push(productId);
              }
              if (productBatches[batch].length === 50) {
                  batch++;
              }
              i++;
          });
  
          // load the product details from webservice
  
          angular.forEach(productBatches, function (productIds) {
              productWebservice.GetProducts(productIds).then(
                  (response) => {
                      angular.forEach(response.data.Products, (product) => {
                          $scope.productDetails[product.ProductId] = product
                          getProgramUrlForProgramId(product.ProgramId)
                      });
                  }
              )
          });
          console.log($scope.productDetails);
          $scope.loadingFinished = true;
      });
  
  };
  
  /**
   * Master Controller
   */
  
  angular.module('AffilinetToolbar')
      .controller('MasterCtrl', ['$scope', '$rootScope', 'BrowserExtensionService', MasterCtrl]);
  
  function MasterCtrl($scope, $rootScope, BrowserExtensionService) {
  
      $rootScope.credentials = {};
      $rootScope.credentialsLoaded = false;
      $rootScope.validCredentials = false;
  
  
      var checkIfCredentialObjectIsSet = function () {
          BrowserExtensionService.storage.local.get(['publisherId', 'webservicePassword', 'countryPlatform', 'disableImageContextMenu', 'productWebservicePassword'], function(result) {
              "use strict";
              if (result.webservicePassword && result.publisherId && result.countryPlatform && result.productWebservicePassword) {
                  $rootScope.credentials = result;
                  clearInterval(refreshIntervalId);
                  $rootScope.$broadcast('updateCredentials');
                  $rootScope.credentialsLoaded = true;
                  $rootScope.validCredentials = true;
              } else {
                  clearInterval(refreshIntervalId);
                  $rootScope.$broadcast('updateCredentials');
                  $rootScope.credentialsLoaded = true;
                  $rootScope.validCredentials = false;
              }
          })
  
      };
      var refreshIntervalId = null;
      refreshIntervalId = setInterval(checkIfCredentialObjectIsSet, 50);
  
  
      $scope.sendAlert = function (message, type) {
          $scope.alerts.push({
              msg: message,
              type: type
          });
          setTimeout(function () {
              $scope.closeAlert($scope.alerts.length - 1);
          }, 2500);
      };
      $scope.alerts = [];
  
      $scope.closeAlert = function (index) {
          $scope.alerts.splice(index, 1);
      };
  
      /**
       * Sidebar Toggle & Cookie Control
       */
      var mobileView = 992;
  
      $scope.pageName = 'Dashboard';
      $scope.toggle = true;
  
      $scope.getWidth = function () {
          return window.innerWidth;
      };
  
      $scope.$watch($scope.getWidth, function (newValue, oldValue) {
          $scope.toggle = newValue >= mobileView;
  
      });
  
  
      $scope.toggleSidebar = function () {
          $scope.toggle = !$scope.toggle;
      };
  
      window.onresize = function () {
          $scope.$apply();
      };
  
  
  }
  
  angular.module('AffilinetToolbar').filter('currentyear', ['$filter', function ($filter) {
      return function () {
          return $filter('date')(new Date(), 'yyyy');
      };
  }]);
  angular.module('AffilinetToolbar')
      .controller('NewsController', ['$scope', '$rootScope', 'LogonService', '$sce', '$translate', NewsController]);
  
  function NewsController($scope, $rootScope, LogonService, $sce, $translate) {
      $scope.loadingFinished = false;
      $translate('NEWS_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
  
  
      //$scope.$parent.sendAlert('test', 'danger');
  
  
      LogonService.GetNewPrograms().then(function (response) {
          $scope.newPrograms = response.data.Envelope.Body.GetProgramsResponse.ProgramCollection.Program;
      }, function error(response) {
          console.log(response.data.Envelope.Body.Fault.faultstring.toString());
      });
  
      LogonService.GetNewVouchers().then(function (response) {
          $scope.newVouchers = response.data.Envelope.Body.SearchVoucherCodesResponse.VoucherCodeCollection.VoucherCodeItem;
          var programIds = [];
  
          angular.forEach($scope.newVouchers, function (key, val) {
              programIds.push(key.ProgramId.toString());
  
          });
          // now fetch the programinfo for each program
          LogonService.GetProgramInfoForIds(programIds).then(function (response) {
  
                  var voucherProgramResponse = response.data.Envelope.Body.GetProgramsResponse.ProgramCollection.Program;
                  $scope.voucherProgramInfo = [];
                  angular.forEach(voucherProgramResponse, function (val, key) {
                      $scope.voucherProgramInfo[val.ProgramId] = val;
                  });
  
              }
          );
          $scope.loadingFinished = true;
  
      }, function error(response) {
          console.log(response.data.Envelope.Body.Fault.faultstring.toString());
      });
  
  
  };
  
  angular.module('AffilinetToolbar')
      .controller('OrdersController', ['$scope', 'LogonService', '$sce', '$location', '$moment', '$translate', '$rootScope', '$window', OrdersController]);
  
  function OrdersController($scope, LogonService, $sce, $location, $moment, $translate, $rootScope, $window) {
      $scope.loadedByDay = false;
      $scope.loadedByProgram = false;
  
      $translate('ORDERS_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
  
      $translate('ORDERS_ChartLabelConfCommission').then(function (text) {
          $scope.labelConfirmedCommission = text;
      });
      $translate('ORDERS_ChartLabelTotalSales').then(function (text) {
          $scope.labelTotalSales = text;
      });
  
      $translate('LOCALE_DATEFORMAT').then(function (text) {
          $scope.dateFormat = text;
      });
  
      $scope.month = new $moment().format('MM');
      $scope.months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
  
      $scope.year = new $moment().format('YYYY');
  
      var years = [];
  
      for (var i = ($scope.year - 3); i <= $scope.year; i++) {
          years.push(i);
      }
      $scope.years = years;
      $scope.chartWidth = 1000 ;
  
      $scope.ApiLocale = $window.ApiLocale;
  
      function moneyFormater(v, axis) {
          if ($scope.ApiLocale == 'en-gb' || $scope.ApiLocale == 'en-GB') {
              return v.toLocaleString('en-gb', {localeMatcher: 'best fit', style: 'currency', currency: "GBP"});
          }
          if ($scope.ApiLocale == 'de-ch' || $scope.ApiLocale == 'de-CH') {
              return v.toLocaleString('de-ch', {localeMatcher: 'best fit', style: 'currency', currency: "CHF"});
          }
          return v.toLocaleString('de-DE', {localeMatcher: 'best fit', style: 'currency', currency: "EUR"});
  
      }
  
      $scope.chartOptionsByDay = {
          yaxes: [
              {
                  position: "left",
                  min: 0,
                  tickDecimals: 0
              },
              {
                  position: "right",
                  min: 0,
                  tickDecimals: 2,
                  tickFormatter: moneyFormater
  
              }
          ],
          grid: {
              hoverable: true,
              clickable: false,
              borderWidth: 0
          },
          xaxis: {
              mode: "time",
              tickLength: 0, // hide gridlines
              minTickSize: [1, "day"],
              timeformat: "%e.%b.%Y"
          },
          series: {
              lines: {show: true},
              points: {show: true}
          }
      };
  
      $scope.chartOptionsByProgram = {
          xaxis: {
              mode: "categories",
              tickLength: 0 // hide gridlines
          },
          yaxes: [
              {
                  position: "left",
                  min: 0,
                  tickDecimals: 0
              },
              {
                  position: "right",
                  min: 0,
                  tickDecimals: 2,
                  tickFormatter: moneyFormater
              }
          ],
          grid: {
              hoverable: true,
              clickable: false,
              borderWidth: 0
          },
          series: {
              shadowSize: 1
          },
          legend: {
              show: true,
              position: "ne",
              container: null
          }
      };
  
  
      $scope.chartDataByDay = [];
      $scope.chartDataByProgram = [];
      $scope.chartDataByProgramValues = [];
  
      var formatstring = 'YYYY-MM-DDTHH:mm:ss';
  
      $scope.loadDataByDay = function (month, year) {
  
          $scope.loadedByDay = false;
          $scope.chartDataByDay = [];
  
          var totalCommission = [];
          var totalSales = [];
          $scope.tableByDay = [];
          $scope.tableByDaySum = [];
  
          $scope.start = new $moment(month + ' ' + year, 'MM YYYY').startOf('month').format(formatstring);
          $scope.end = new $moment(month + ' ' + year, 'MM YYYY').endOf('month').format(formatstring);
  
  
          LogonService.GetDailyStatistics($scope.start, $scope.end).then(function (response) {
  
              var Total = response.data.Envelope.Body.GetDailyStatisticsResponse.DailyStatisticsRecords.Total;
              var DailyStatisticsRecords = response.data.Envelope.Body.GetDailyStatisticsResponse.DailyStatisticsRecords.DailyStatisticRecords.DailyStatisticsRecord;
  
              // update the total Sum for each Column
              $scope.tableByDaySum['views'] = parseInt(Total.PayPerClick.Views.toString()) + parseInt(Total.CombinedPrograms.Views.toString()) + parseInt(Total.PayPerSaleLead.Views.toString());
              $scope.tableByDaySum['clicks'] = parseInt(Total.PayPerClick.Clicks.toString()) + parseInt(Total.CombinedPrograms.Clicks.toString()) + parseInt(Total.PayPerSaleLead.Clicks.toString());
              $scope.tableByDaySum['openSaleLeads'] = parseInt(Total.CombinedPrograms.OpenSales.toString()) + parseInt(Total.PayPerSaleLead.OpenSales.toString());
              $scope.tableByDaySum['confirmedSaleLeads'] = parseInt(Total.CombinedPrograms.ConfirmedSales.toString()) + parseInt(Total.PayPerSaleLead.ConfirmedSales.toString());
              $scope.tableByDaySum['cancelledSaleLeads'] = parseInt(Total.CombinedPrograms.CancelledSales.toString()) + parseInt(Total.PayPerSaleLead.CancelledSales.toString());
              $scope.tableByDaySum['openCommission'] = moneyFormater(parseFloat(Total.TotalOpenCommission.toString()), false);
              $scope.tableByDaySum['commission'] = moneyFormater(parseFloat(Total.TotalCommission.toString()), false);
  
  
              for (var i = 0; i < DailyStatisticsRecords.length; i++) {
  
                  var timestamp = $moment(DailyStatisticsRecords[i].Date.toString(), formatstring).format('x');
                  var day = $moment(DailyStatisticsRecords[i].Date.toString(), formatstring).format('D') - 1;
  
                  /**
                   * for display in chart total Commission
                   */
                  var commission = +(DailyStatisticsRecords[i].TotalCommission.toString());
  
                  if (totalCommission[day] == undefined) {
                      totalCommission[day] = [timestamp, commission];
                  }
  
                  /**
                   * for display in chart total Orders
                   */
                  var sales = +DailyStatisticsRecords[i].CombinedPrograms.ConfirmedSales.toString() + +DailyStatisticsRecords[i].PayPerSaleLead.ConfirmedSales.toString() + parseInt(DailyStatisticsRecords[i].CombinedPrograms.OpenSales.toString()) + parseInt(DailyStatisticsRecords[i].PayPerSaleLead.OpenSales.toString());
                  if (totalSales[day] == undefined) {
                      totalSales[day] = [timestamp, sales];
                  }
  
                  /**
                   * populate the table for byDay data
                   */
  
                  var views = parseInt(DailyStatisticsRecords[i].PayPerClick.Views.toString()) + parseInt(DailyStatisticsRecords[i].CombinedPrograms.Views.toString()) + parseInt(DailyStatisticsRecords[i].PayPerSaleLead.Views.toString());
                  var clicks = parseInt(DailyStatisticsRecords[i].PayPerClick.Clicks.toString()) + parseInt(DailyStatisticsRecords[i].CombinedPrograms.Clicks.toString()) + parseInt(DailyStatisticsRecords[i].PayPerSaleLead.Clicks.toString());
                  var openSaleLeads = parseInt(DailyStatisticsRecords[i].CombinedPrograms.OpenSales.toString()) + parseInt(DailyStatisticsRecords[i].PayPerSaleLead.OpenSales.toString());
                  var confirmedSaleLeads = parseInt(DailyStatisticsRecords[i].CombinedPrograms.ConfirmedSales.toString()) + parseInt(DailyStatisticsRecords[i].PayPerSaleLead.ConfirmedSales.toString());
                  var cancelledSaleLeads = parseInt(DailyStatisticsRecords[i].CombinedPrograms.CancelledSales.toString()) + parseInt(DailyStatisticsRecords[i].PayPerSaleLead.CancelledSales.toString());
                  var openCommission = parseFloat(DailyStatisticsRecords[i].TotalOpenCommission.toString());
  
  
                  $scope.tableByDay[day] = [];
                  $scope.tableByDay[day]['date'] = new $moment(DailyStatisticsRecords[i].Date.toString(), formatstring).format($scope.dateFormat);
  
  
                  $scope.tableByDay[day]['views'] = views;
                  $scope.tableByDay[day]['clicks'] = clicks;
                  $scope.tableByDay[day]['openSaleLeads'] = openSaleLeads;
                  $scope.tableByDay[day]['confirmedSaleLeads'] = confirmedSaleLeads;
                  $scope.tableByDay[day]['cancelledSaleLeads'] = cancelledSaleLeads;
                  $scope.tableByDay[day]['openCommission'] = moneyFormater(openCommission, false);
                  $scope.tableByDay[day]['commission'] = moneyFormater(commission, false);
  
  
              }
  
              $scope.chartDataByDay = [
                  {label: $scope.labelConfirmedCommission, color: '#3B302D', data: totalCommission, yaxis: 2},
                  {label: $scope.labelTotalSales, color: '#a8a199', data: totalSales, yaxis: 1}
              ];
  
              $scope.refreshWidth()
              $scope.loadedByDay = true;
          });
  
      };
  
      $scope.pushToByProgramChart = function (title, sales, commission) {
  
          if ($scope.chartDataByProgramOrder.length < 10) {
              $scope.chartDataByProgramOrder.push([title, sales]);
              $scope.chartDataByProgramCommission.push([title, commission]);
          }
          else {
              // find shortProgramName with lowest totalCommission
  
              var lowest = Infinity;
              var lowestProgramName = false;
  
              // find the name of the lowest program in chart
              angular.forEach($scope.chartDataByProgramCommission, function (elem) {
                  "use strict";
                  if (elem[1] < lowest) {
                      lowestProgramName = elem[0];
                      lowest = elem[1]
                  }
              });
  
              // get the index of this lowest program
              var index = $scope.chartDataByProgramCommission.findIndex(function (elem) {
                  return elem[0] == lowestProgramName && elem[1] == lowest;
              });
              // splice out the lowest program
              $scope.chartDataByProgramOrder.splice(index, 1);
              $scope.chartDataByProgramCommission.splice(index, 1);
  
              // add the new element which has a higher commission
              $scope.chartDataByProgramOrder.push([title, sales]);
              $scope.chartDataByProgramCommission.push([title, commission]);
          }
  
      };
  
  
      $scope.loadDataByProgram = function (month, year) {
  
          $scope.loadedByProgram = false;
  
          $scope.chartDataByProgram = [];
          $scope.chartDataByProgramOrder = [];
          $scope.chartDataByProgramCommission = [];
          $scope.tableByProgram = [];
  
          $scope.start = new $moment(month + ' ' + year, 'MM YYYY').startOf('month').format(formatstring);
          $scope.end = new $moment(month + ' ' + year, 'MM YYYY').endOf('month').format(formatstring);
  
  
          LogonService.GetAllProgramStatistics($scope.start, $scope.end).then(function (response) {
  
  
              var programs = [];
  
              // these can be array or single result.. thx soap!
              var PayPerSaleLeadStatistics = response.data.Envelope.Body.GetProgramStatisticsResponse.ProgramStatisticsRecords.PayPerSaleLeadStatistics.StatisticsRecords;
  
              angular.forEach(PayPerSaleLeadStatistics, function (statisticsRecords) {
                  if (angular.isUndefined(statisticsRecords.ProgramTitle)) {
                      angular.forEach(statisticsRecords, function (statisticsRecord) {
                          if (!angular.isUndefined(statisticsRecord.ProgramTitle)) {
                              programs.push(statisticsRecord);
                          }
                      });
                  } else {
                      programs.push(statisticsRecords);
                  }
              });
  
  
              var CombinedProgramStatistics = response.data.Envelope.Body.GetProgramStatisticsResponse.ProgramStatisticsRecords.CombinedProgramStatistics.StatisticsRecords;
  
              angular.forEach(CombinedProgramStatistics, function (statisticsRecords) {
                  if (angular.isUndefined(statisticsRecords.ProgramTitle)) {
                      angular.forEach(statisticsRecords, function (statisticsRecord) {
                          if (!angular.isUndefined(statisticsRecord.ProgramTitle)) {
                              programs.push(statisticsRecord);
                          }
                      });
                  } else {
                      programs.push(statisticsRecords);
                  }
              });
  
  
              var PayPerClickStatistics = response.data.Envelope.Body.GetProgramStatisticsResponse.ProgramStatisticsRecords.PayPerClickStatistics.StatisticsRecords;
              angular.forEach(PayPerClickStatistics, function (statisticsRecords) {
                  if (angular.isUndefined(statisticsRecords.ProgramTitle)) {
                      angular.forEach(statisticsRecords, function (statisticsRecord) {
                          if (!angular.isUndefined(statisticsRecord.ProgramTitle)) {
                              programs.push(statisticsRecord);
                          }
                      });
                  } else {
                      programs.push(statisticsRecords);
                  }
              });
  
  
              angular.forEach(programs, function (statisticsRecord) {
                  if (!angular.isUndefined(statisticsRecord.ProgramTitle)) {
  
                      var openCommission = statisticsRecord.OpenCommission.toString();
                      var commission = statisticsRecord.Commission.toString();
  
                      $scope.tableByProgram.push({
                          name: statisticsRecord.ProgramTitle.toString(),
                          programId: statisticsRecord.ProgramId.toString(),
                          views: statisticsRecord.Views.toString(),
                          clicks: statisticsRecord.Clicks.toString(),
                          openSaleLeads: statisticsRecord.OpenSales.toString(),
                          confirmedSaleLeads: statisticsRecord.Sales.toString(),
                          cancelledSaleLeads: statisticsRecord.CancelledSales.toString(),
                          openCommission: moneyFormater(parseFloat(openCommission), false),
                          commission: moneyFormater(parseFloat(commission), false)
                      });
                      var shortProgramName = statisticsRecord.ProgramTitle.toString();
                      if (shortProgramName.length > 10) {
                          shortProgramName = shortProgramName.substring(0, 10) + '...';
                      }
                      $scope.pushToByProgramChart(shortProgramName, +statisticsRecord.Sales.toString(), +statisticsRecord.Commission.toString());
                  }
              });
              $scope.refreshWidth()
              $scope.loadedByProgram = true;
          });
  
  
          $scope.chartDataByProgram.push({
              data: $scope.chartDataByProgramOrder,
              label: $scope.labelTotalSales,
              color: '#a8a199',
              yaxis: 1,
              bars: {
                  show: true,
                  fill: true,
                  order: 1,
                  barWidth: 0.25,
                  align: 'right',
                  fillColor: '#a8a199'
              }
  
          }, {
              data: $scope.chartDataByProgramCommission,
              label: $scope.labelConfirmedCommission,
              color: '#3B302D',
              yaxis: 2,
              bars: {
                  show: true,
                  fill: true,
                  order: 2,
                  barWidth: 0.25,
                  align: 'left',
                  fillColor: '#3B302D'
  
              }
          });
  
  
      };
  
  
      $scope.showData = function (month, year) {
  
          $scope.loadDataByDay(month, year);
          $scope.loadDataByProgram(month, year);
  
      };
  
  
      var checkApiLocale = function () {
          if (typeof $window.ApiLocale !== 'undefined' && $window.ApiLocale) {
              clearInterval(refreshIntervalId);
              $scope.ApiLocale = $window.ApiLocale;
              $scope.showData($scope.month, $scope.year);
          }
      };
      var refreshIntervalId = null;
      refreshIntervalId = setInterval(checkApiLocale, 50);
  
  
      $scope.refreshPlot = function (that, month, year) {
          $scope.loadDataByDay(month, year);
          $scope.loadDataByProgram(month, year);
          $('#' + that).css('display', 'block');
          $('#' + that).css('clear', 'both');
  
  
      };
  
      $scope.refreshWidth = function() {
          var elem = document.getElementById('chartWidthSelector');
  
          if (elem !== null) {
              $scope.chartWidth = elem.clientWidth -80;
  
          }
      }
  
      window.addEventListener('resize', function () {
          $scope.refreshWidth();
      })
  
  }
  
  angular.module('AffilinetToolbar')
      .controller('SearchDiscoverController', ['$scope', '$rootScope', 'LogonService', '$timeout', '$translate', 'productWebservice' ,  'BrowserExtensionService',  SearchDiscoverController]);
  
  function SearchDiscoverController($scope, $rootScope, LogonService, $timeout,  $translate, productWebservice,  BrowserExtensionService) {
      $scope.loadingFinished = false;
      $translate('SEARCHDISCOVER_PageName').then(function (text) {
          $scope.$parent.pageName = text;
  
      });
  
  
      $scope.allShopsLoading = true;
      $scope.searchFinsihed = false;
      $scope.searching = false;
      $scope.myPrograms = [];
      $scope.allMyProgramIds = [];
      $scope.selectedProgramsIds = [];
      $scope.allShops = [];
      $scope.productDetails = [];
      $scope.allShopsLoading = false;
      $scope.showSuccessMessage = false;
      $scope.shopsFilteredToSelectedPrograms = [];
      $scope.programUrls = [];
      $scope.programUrlsRequested = [];
  
  
  
      $scope.searchDiscoverShowDetails = {};
  
      $scope.searchKeyword = '';
      $scope.selectedPrograms = [];
      $scope.selectedShops = [];
  
      $scope.shopCategories = [];
      $scope.shopCategoriesLoading = false;
  
      $scope.selectedAffilinetCategories = [];
      $scope.selectedShopCategories = [];
  
      $scope.productResult = [];
      $scope.facetsResult = [];
      $scope.productPage = 1;
  
  
      $scope.selectedBrand = '';
      $scope.selectedDistributor = '';
      $scope.selectedManufacturer = '';
      $scope.selectedAffilinetCategoryPath = '';
  
  
  
  
      $scope.selectedProductIds = [];
      $scope.totalRecords = 0;
      $scope.totalPages = 0;
      let searchChangedTimeoutPromise;
  
      $scope.selectedProductList = null;
  
      let loadProducts = function (productIds){
  
          let i = 0;
          let batch = 0;
          let productBatches = [];
          angular.forEach(productIds, (productId) => {
  
              if (!productBatches[batch]) {
                  productBatches[batch] = [productId];
              } else {
                  productBatches[batch].push(productId);
              }
              if (productBatches[batch].length === 50) {
                  batch++;
              }
              i++;
          });
  
          // load the product details from webservice
  
          angular.forEach(productBatches, function (productIds) {
              productWebservice.GetProducts(productIds).then(
                  (response) => {
                      response.data.Products.forEach((product) => {
                          $scope.productDetails[product.ProductId] = product;
                          getProgramUrlForProgramId(product.ProductId)
                      })
                  }
              )
          });
      };
  
  
      $scope.setBrand = function(value) {
          "use strict";
          if ($scope.selectedBrand === value) {
              $scope.selectedBrand = '';
          } else {
              $scope.selectedBrand =  value;
          }
          searchIfValid()
      };
      $scope.setManufacturer = function(value) {
          if ($scope.selectedManufacturer === value) {
              $scope.selectedManufacturer = '';
          } else {
              $scope.selectedManufacturer =  value;
          }
          searchIfValid()
      };
      $scope.setDistributor = function(value) {
          if ($scope.selectedDistributor === value) {
              $scope.selectedDistributor = '';
          } else {
              $scope.selectedDistributor =  value;
          }
          searchIfValid()
      };
      $scope.setAffilinetCategoryPath = function(value) {
          if ($scope.selectedAffilinetCategoryPath === value) {
              $scope.selectedAffilinetCategoryPath = '';
          } else {
              $scope.selectedAffilinetCategoryPath =  value;
          }
          searchIfValid()
      };
  
      $scope.toggleFilters = function () {
          $scope.showFilters= !$scope.showFilters
      };
  
      let loadInitialValues = function  () {
          BrowserExtensionService.storage.local.get(
              ['storedProductLists',
                  'searchDiscoverShowDetails',
                  'searchDiscoverLastKeyword',
                  'searchDiscoverSelectedShopCategories',
                  'searchDiscoverSelectedShops',
                  'searchDiscoverSelectedPrograms',
                  'searchDiscoverSelectedProgramIds',
                  'searchDiscoverSelectedProductIds',
                  'searchDiscoverSelectedBrand',
                  'searchDiscoverSelectedDistributor',
                  'searchDiscoverSelectedManufacturer',
                  'searchDiscoverSelectedAffilinetCategoryPath',
                  'searchDiscoverShowFilters',
                  'searchDiscoverShopsFilteredToSelectedPrograms',
                  'searchDiscoverShopsMinPrice',
                  'searchDiscoverShopsMaxPrice',
              ]
              , function(result) {
                  console.log('result from storage', result);
                  if (result.storedProductLists) {
                      $scope.storedProductLists = result.storedProductLists;
                  } else {
                      $scope.storedProductLists = [];
                  }
                  if (result.searchDiscoverShowDetails) {
                      $scope.searchDiscoverShowDetails = result.searchDiscoverShowDetails;
                  }
                  if (result.searchDiscoverLastKeyword) {
                      $scope.searchKeyword = result.searchDiscoverLastKeyword;
                  }
                  if (result.searchDiscoverSelectedShopCategories) {
                      $scope.selectedShopCategories = result.searchDiscoverSelectedShopCategories;
                  }
                  if (result.searchDiscoverSelectedShops) {
                      $scope.selectedShops = result.searchDiscoverSelectedShops;
                  }
  
                  if (result.searchDiscoverShopsFilteredToSelectedPrograms) {
                      $scope.shopsFilteredToSelectedPrograms = result.searchDiscoverShopsFilteredToSelectedPrograms;
                  }
  
                  if (result.searchDiscoverSelectedProgramIds) {
                      $scope.selectedProgramsIds = result.searchDiscoverSelectedProgramIds;
                  }
                  if (result.searchDiscoverSelectedBrand) {
                      $scope.selectedBrand = result.searchDiscoverSelectedBrand;
                  }
  
                  if (result.searchDiscoverSelectedDistributor) {
                      $scope.selectedDistributor = result.searchDiscoverSelectedDistributor;
                  }
  
                  if (result.searchDiscoverSelectedManufacturer) {
                      $scope.selectedManufacturer = result.searchDiscoverSelectedManufacturer;
                  }
  
                  if (result.searchDiscoverSelectedAffilinetCategoryPath) {
                      $scope.selectedAffilinetCategoryPath = result.searchDiscoverSelectedAffilinetCategoryPath;
                  }
                  if (result.searchDiscoverShowFilters) {
                      $scope.showFilters = result.searchDiscoverShowFilters;
                  }
  
                  if (result.searchDiscoverSelectedPrograms) {
                      $scope.selectedPrograms = result.searchDiscoverSelectedPrograms;
                  }
                  if (result.searchDiscoverSelectedProductIds) {
                      $scope.selectedProductIds = result.searchDiscoverSelectedProductIds;
                      console.log('selected product ids', $scope.selectedProductIds);
  
                      if ($scope.selectedProductIds.length > 0 ) {
                          loadProducts($scope.selectedProductIds)
  
                      }
  
  
                  }
  
  
                  if (result.searchDiscoverShopsMinPrice) {
                      $scope.minPrice = result.searchDiscoverShopsMinPrice;
                  } else {
                      $scope.minPrice = 0
                  }
                  if (result.searchDiscoverShopsMaxPrice) {
                      $scope.maxPrice = result.searchDiscoverShopsMaxPrice;
                  } else {
                      $scope.maxPrice = 2000;
                  }
                  $scope.priceSlider = {
                      minValue: $scope.minPrice,
                      maxValue: $scope.maxPrice,
                      options: {
                          floor: 0,
                          ceil: 2000,
                          pushRange: true,
                          onEnd: function(modelValue,min, max) {
                              console.log(modelValue,min, max)
                              searchIfValid ();
                              BrowserExtensionService.storage.local.set({
                                  searchDiscoverShopsMinPrice : min,
                                  searchDiscoverShopsMaxPrice : max
                              })
                          }
                      }
                  };
                  searchIfValid ();
                  bindWatch();
                  $scope.$apply(function(){
                      $scope.loadingFinished = true;
                  })
  
  
  
              });
      }
  
  
      function bindWatch() {
          $scope.$watch( 'searchDiscoverShowDetails', (searchDiscoverShowDetails)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverShowDetails : searchDiscoverShowDetails})
              }, true
          );
  
          $scope.$watch( 'selectedProgramIds', (selectedProgramIds)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverSelectedProgramIds : selectedProgramIds})
                  resetFilters()
              }, true
          );
  
          $scope.$watch( 'selectedProductIds', (selectedProductIss)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverSelectedProductIds : selectedProductIss})
              }, true
          );
  
  
          $scope.$watch( 'selectedBrand', (selectedBrand)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverSelectedBrand : selectedBrand})
                  searchIfValid (true);
              }, true
          );
  
          $scope.$watch( 'selectedManufacturer', (selectedManufacturer)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverSelectedManufacturer : selectedManufacturer})
                  searchIfValid (true);
              }, true
          );
          $scope.$watch( 'selectedDistributor', (selectedDistributor)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverSelectedDistributor : selectedDistributor})
                  searchIfValid (true);
              }, true
          );
          $scope.$watch( 'selectedAffilinetCategoryPath', (selectedAffilinetCategoryPath)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverSelectedAffilinetCategoryPath : selectedAffilinetCategoryPath})
                  searchIfValid (true);
              }, true
          );
  
          $scope.$watch( 'showFilters', (showFilters)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverShowFilters : showFilters});
              }
          );
  
          $scope.$watch( 'selectedPrograms',
              (selectedPrograms)  => {
                  BrowserExtensionService.storage.local.set({searchDiscoverSelectedPrograms : selectedPrograms})
                  resetFilters()
                  if (selectedPrograms.length === 0) {
                      $scope.shopsFilteredToSelectedPrograms = $scope.allShops;
                      BrowserExtensionService.storage.local.set({searchDiscoverShopsFilteredToSelectedPrograms : $scope.shopsFilteredToSelectedPrograms})
                      $scope.selectedProgramsIds = $scope.allMyProgramIds;
                      $scope.selectedShops = [];
                  } else {
                      $scope.selectedProgramsIds = [];
                      selectedPrograms.forEach((program) => {
                          "use strict";
                          $scope.selectedProgramsIds.push(program.programId)
                      });
                      $scope.shopsFilteredToSelectedPrograms = [];
                      $scope.allShopsLoading = true;
                      $scope.allShops.forEach((shop) => {
                          "use strict";
  
                          if ($scope.selectedProgramsIds.indexOf(shop.ProgramId.toString()) >= 0) {
                              $scope.shopsFilteredToSelectedPrograms.push(shop);
                          }
                      })
                      BrowserExtensionService.storage.local.set({searchDiscoverShopsFilteredToSelectedPrograms : $scope.shopsFilteredToSelectedPrograms})
                      $scope.allShopsLoading = false;
                      searchIfValid ();
                  }
              }
          );
  
  
  
  
          $scope.$watch('selectedShops', (selectedShops) => {
              resetFilters()
              BrowserExtensionService.storage.local.set({searchDiscoverSelectedShops : selectedShops});
              if (selectedShops.length !== 1 ) {
                  $scope.selectedShopCategories = [];
              }
  
              if (selectedShops.length === 1) {
                  $scope.shopCategoriesLoading = true;
                  productWebservice.GetCategoryList(selectedShops[0].ShopId).then(
                      (result) => {
                          console.log('GetCategoryList list fetched', result);
                          $scope.shopCategories = result.data.Categories;
                          $scope.shopCategoriesLoading = false;
                      }
                  )
              }
              searchIfValid ();
  
          });
          $scope.$watch('selectedShopCategories', function(shopCategories){
              resetFilters()
              BrowserExtensionService.storage.local.set({searchDiscoverSelectedShopCategories : shopCategories});
              searchIfValid ();
          });
  
          $scope.$watch('searchKeyword', (searchKeyword)=> {
              resetFilters()
              console.log('Search Keyword changed', searchKeyword);
              BrowserExtensionService.storage.local.set({searchDiscoverLastKeyword : searchKeyword});
              searchIfValid ();
          });
  
  
      }
  
      
  
      let searchIfValid  = function(immidiately = false) {
          let delay;
          if (!immidiately) {
              delay = 500
          } else {
              delay = 0;
          }
          if ($scope.loadingFinished === false) {
              return;
          }
  
  
          $timeout.cancel(searchChangedTimeoutPromise);  //does nothing, if timeout already done
  
          if ($scope.searchKeyword === '' && $scope.selectedShopCategories.length === 0) {
              return
          }
  
  
          searchChangedTimeoutPromise = $timeout(function(){   //Set timeout
              $scope.searching = true;
              console.debug('search if valid runs with', $scope.searchKeyword , $scope.selectedShopCategories, $scope.selectedPrograms, $scope.shopsFilteredToSelectedPrograms);
              $scope.searchProducts();
          },delay);
  
  
      }
  
      
      let showError  = function(error) {
          "use strict";
          let message = 'Error getting result from Product Webservice.';
          if (error.data && error.data.ErrorMessages && error.data.ErrorMessages.length > 0) {
              message += ' ' +error.data.ErrorMessages[0]['Value'];
          }
          $scope.$parent.sendAlert(message, 'danger');
      }
  
  
  
  
  
      let init = function() {
  
          BrowserExtensionService.storage.local.get('myPrograms', function(result) {
              "use strict";
              if (result.myPrograms ) {
                  console.log('myPrograms', result.myPrograms);
                  $scope.myPrograms = result.myPrograms;
                  $scope.selectedProgramsIds = [];
                  result.myPrograms.forEach((program) => {
                      $scope.selectedProgramsIds.push(program.programId);
                      $scope.allMyProgramIds.push(program.programId);
                  });
              } else {
                  $scope.myPrograms = [];
              }
          });
          productWebservice.GetShopList().then(
              (shopListResult)  => {
                  $scope.allShops = shopListResult.data.Shops;
                  $scope.allShopsLoading = false;
                  console.log('INIT');
                  loadInitialValues()
  
              },
              (error)  => {
                  showError (error);
              }
          );
  
      }
  
  
  
  
  
  
      $scope.searchProducts = function() {
          $scope.productPage = 1;
          $scope.searching = true;
          let params = _buildSearchParams();
          productWebservice.SearchProducts(params).then((result) => {
              "use strict";
              console.log(result);
              $scope.searching = false;
              $scope.searchFinished = true;
              $scope.totalRecords = result.data.ProductsSummary.TotalRecords;
              $scope.totalPages = result.data.ProductsSummary.TotalPages;
              $scope.productResult = result.data.Products;
  
              if (result.data.Facets) {
                  $scope.facetsResult = _buildFacets(result.data.Facets)
              }
  
              result.data.Products.forEach((product) => {
                   $scope.productDetails[product.ProductId] = product;
                   getProgramUrlForProgramId(product.ProgramId);
              });
          }, (error)  => {
              showError (error);
              $scope.searching = false;
              $scope.searchFinished = true;
  
          })
      };
  
      let getProgramUrlForProgramId = function(ProgramId) {
          if (!$scope.programUrlsRequested[ProgramId]) {
              $scope.programUrlsRequested[ProgramId] = true;
              BrowserExtensionService.runtime.sendMessage({action : 'get-programDetailsForProgramId', data : { programId : ProgramId}},
                  function(programDetails){
                      if (programDetails !== false) {
                          $scope.$apply(function(){
                              $scope.programUrls[ProgramId] = programDetails.programUrl;
                          });
                      }
                  })
          }
      }
  
      $scope.loadMore = function() {
          "use strict";
          $scope.productPage++;
          let params = _buildSearchParams();
          productWebservice.SearchProducts(params).then((result) => {
              "use strict";
              console.log(result);
              $scope.recordsOnPage = result.data.ProductsSummary.Records;
              result.data.Products.forEach((product) => {
                  $scope.productResult.push(product)
              });
          }, (error)  => {
              showError (error);
          })
      }
  
  
  
      $scope.toggleProduct = function(productId) {
          if ($scope.selectedProductIds.includes(productId)) {
              $scope.selectedProductIds = $scope.selectedProductIds
                  .filter( (prod) => prod !== productId);
  
          } else {
              $scope.selectedProductIds.unshift(productId);
          }
          console.log($scope.selectedProductIds);
      }
      $scope.addAllProducts = function(){
          "use strict";
          $scope.productResult.forEach((product) => {
              if (!$scope.selectedProductIds.includes(product.ProductId)) {
                  $scope.selectedProductIds.unshift(product.ProductId);
              }
          })
      }
  
      $scope.createProductList = function (query) {
          let newItem = {name: query, products : [], id: new Date().getTime() + '-' + Math.random()};
  
          // name should be unique
          let exists = false;
          $scope.storedProductLists.forEach((list) => {
              if (list.name === query) {
                  $scope.selectedProductList = list;
                  exists = true;
              }
          });
  
          if (exists === true) {
              return;
          }
  
          $scope.storedProductLists.unshift(newItem);
          BrowserExtensionService.storage.local.set({
              storedProductLists : $scope.storedProductLists
          });
          $scope.selectedProductList = newItem;
          $scope.addProductsToProductList();
      };
  
  
      $scope.addProductsToProductList = function () {
  
          angular.forEach($scope.selectedProductIds, (productId) => {
              // do now allow dupes
              if (! $scope.selectedProductList.products.includes(productId)) {
                  $scope.selectedProductList.products.unshift(productId);
              }
          });
          if ($scope.selectedProductIds.length > 0) {
              $scope.showSuccessMessage = true;
              $timeout(function(){   //Set timeout
                  $scope.showSuccessMessage = false;
              },3000);
          }
  
          $scope.selectedProductIds = [];
          BrowserExtensionService.storage.local.set({
              storedProductLists : $scope.storedProductLists
          });
  
  
  
      };
  
  
      _shopsToShopIdCsv = function(shops) {
          let ids = [];
          angular.forEach(shops, (shop)=> {
              "use strict";
              ids.push(shop.ShopId)
          })
          return ids.join(',');
  
      }
  
      _categoriesToCSV = function(categories) {
          "use strict";
          console.log('categories: ', categories);
          let ids = [];
          angular.forEach(categories, (category)=> {
              "use strict";
              ids.push(category.Id)
          })
          return ids.join(',');
      }
  
  
  
      _buildSearchParams = () => {
          "use strict";
  
          let params = {
              CurrentPage : $scope.productPage,
              PageSize : 100,
              SortBy : 'Score',
              SortOrder : 'descending',
              ImageScales : 'Image180',
              WithImageOnly : 'true',
              FacetFields : 'Brand,AffilinetCategoryPathFacet,Manufacturer,Distributor',
              FacetValueLimit : 10,
          };
  
          if ( $scope.priceSlider.minValue !== $scope.priceSlider.options.floor) {
              params.MinimumPrice =  $scope.priceSlider.minValue
          }
          if ( $scope.priceSlider.maxValue !== $scope.priceSlider.options.ceil) {
              params.MaximumPrice =  $scope.priceSlider.maxValue
          }
  
          // Filter Query
  
          let fq = '';
          if ($scope.selectedBrand !== '') {
              fq += ',Brand:' + ($scope.selectedBrand);
          }
          if ($scope.selectedDistributor !== '') {
              fq += ',Distributor:' + ($scope.selectedDistributor);
          }
          if ( $scope.selectedManufacturer !== '') {
              fq += ',Manufacturer:' + ($scope.selectedManufacturer);
          }
          if ( $scope.selectedAffilinetCategoryPath !== '') {
              fq += ',AffilinetCategoryPathFacet:' + $scope.selectedAffilinetCategoryPath;
          }
          if (fq !== '' ) {
              params.FQ = fq.slice(1);
          }
  
  
          // wenn shops ausgewählt nur diese shops
          if ($scope.selectedShops.length > 0) {
              params.ShopIds  = _shopsToShopIdCsv($scope.selectedShops);
              params.ShopIdMode = 'Include';
          }
          // wenn keine shops aber programme ausgewählt filter nur nach shops der programme
          else if($scope.selectedPrograms.length > 0) {
              if ($scope.shopsFilteredToSelectedPrograms.length === 0) {
                  console.error('NOT YET LOADED $scope.shopsFilteredToSelectedPrograms', $scope.shopsFilteredToSelectedPrograms)
              }
              params.ShopIds = _shopsToShopIdCsv($scope.shopsFilteredToSelectedPrograms)
              params.ShopIdMode = 'Include';
          }
  
  
          // genau ein shop ausgewählt entweder affilinetcategories <=> ShopCategories
          if ($scope.selectedShops.length === 1) {
  
              if($scope.selectedShopCategories.length > 0) {
                  params.UseAffilinetCategories  = 'false';
                  params.CategoryIds = _categoriesToCSV($scope.selectedShopCategories);
              }
          }
  
          if ($scope.searchKeyword.length > 0) {
              params.Query  =  $scope.searchKeyword;
          }
  
          return params;
      };
  
  
      _buildFacets = function (facetResult) {
          "use strict";
          let facets = [];
          facetResult.forEach(function (facet) {
              let res = { facetField : facet.FacetField, values : []};
              facet.FacetValues.forEach(function(facetValue) {
                  let displayName;
  
                  if (facet.FacetField === 'AffilinetCategoryPathFacet') {
                      displayName = $scope.affilinetCategoryPathToDisplayValue(facetValue.FacetValueName)
                  } else {
                      displayName = $scope.ucfirst(facetValue.FacetValueName) ;
                  }
                  let val = {
                      displayName :  displayName + ' (' +  facetValue.FacetValueCount  +')',
                      FacetValueName : facetValue.FacetValueName ,
                      FacetValueCount: facetValue.FacetValueCount
                  };
                  res.values.push(val)
              });
              facets.push(res);
          });
          return facets;
      };
  
      $scope.ucfirst = function(string) {
          "use strict";
  
          if (typeof string !== 'undefined'){
              return string.charAt(0).toUpperCase() + string.slice(1)
          }
          return string
  
      };
  
      $scope.affilinetCategoryPathToDisplayValue = function(name) {
          "use strict";
          let displayName;
          let $values = name.split('^');
          if ($values[3] === $values[1]) return $values[1];
          return $values[3];
      };
  
  
      let resetFilters = function () {
          $scope.selectedBrand = '';
          $scope.selectedManufacturer = '';
          $scope.selectedDistributor = '';
          $scope.selectedAffilinetCategoryPath = '';
      };
      $scope.reset = function () {
          $scope.priceSlider.minValue = 0;
          $scope.priceSlider.maxValue = $scope.priceSlider.options.ceil;
          $scope.searchKeyword = '';
          $scope.selectedShops = [];
          resetFilters()
          $scope.selectedProgramsIds= [];
          $scope.selectedPrograms= [];
          $scope.selectedProductIds= [];
          $scope.selectedProducts= [];
          $scope.selectedShopCategories= [];
          BrowserExtensionService.storage.local.set({
              searchDiscoverShopsMinPrice : 0,
              searchDiscoverShopsMaxPrice : $scope.priceSlider.options.ceil
          })
      }
  
  
  
      $timeout(function () {
          $scope.$broadcast('rzSliderForceRender');
      }, 1000);
  
  
      init();
  }
  
  angular.module('AffilinetToolbar')
      .controller('SettingsController', ['$scope', '$rootScope', '$window', '$translate', 'LogonService', 'productWebservice', 'BrowserExtensionService', SettingsController]);
  
  
  function SettingsController($scope, $rootScope, $window, $translate, LogonService, productWebservice, BrowserExtensionService) {
  
      $translate('SETTINGS_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
  
      $scope.$on("updateCredentials", function () {
          "use strict";
          $scope.$apply();
      });
  
      LogonService.LoadCreadentialsFromRootScope();
  
      $scope.updateProgramData = function () {
          BrowserExtensionService.runtime.sendMessage({action : 'update-programData'});
          $scope.$parent.sendAlert('Downloading Program Data', 'success');
      };
  
      $scope.submitLoginData = function () {
          $rootScope.validCredentials = false;
  
          if (angular.isUndefined($rootScope.credentials.countryPlatform)) {
              $scope.$parent.sendAlert('Please select a country platform', 'danger');
              return
          }
          LogonService.RemoveToken();
  
  
  
          LogonService.CheckCredentials($rootScope.credentials).then(function (response) {
  
              if (angular.isDefined(response.data.Envelope.Body.Fault)) {
                  // wrong publisher webservice credentials
                  $scope.$parent.sendAlert('Incorrect Publisher Webservice Password', 'danger');
                  $rootScope.$broadcast('updateCredentials');
                  $rootScope.credentialsLoaded = true;
                  $rootScope.validCredentials = false;
                  $rootScope.credentials.webservicePassword = '';
  
                  BrowserExtensionService.runtime.sendMessage({
                      action: 'clear-cache',
                      from: 'settings-ctrl'
                  }, function(response){
                      console.log(response)
                  });
  
              } else {
  
  
  
                  $rootScope.credentialsLoaded = true;
                  $rootScope.validCredentials = true;
                  BrowserExtensionService.runtime.sendMessage({
                      action: 'clear-cache',
                      from: 'settings-ctrl'
                  }, function(response){
                      console.log(response)
                  });
                  $rootScope.credentials.publisherId = $rootScope.credentials.publisherId.trim();
                  $rootScope.credentials.webservicePassword = $rootScope.credentials.webservicePassword.trim();
                  $rootScope.credentials.productWebservicePassword = $rootScope.credentials.productWebservicePassword.trim();
                  BrowserExtensionService.runtime.sendMessage({
                      action: 'save-credentials',
                      from: 'settings-ctrl',
                      data: $rootScope.credentials
                  }, function(response){
                      console.log(response)
                      $rootScope.$broadcast('updateCredentials');
                  });
                  $window.credentials = angular.copy($rootScope.credentials);
  
  
                  // check product data webservice password
                  productWebservice.CheckLoginData($rootScope.credentials.publisherId, $rootScope.credentials.productWebservicePassword)
                      .then(
                          (result) => {
                              // success
                          },
                          (error) => {
                              $scope.$parent.sendAlert('Please check your  Product Webservice Password', 'danger');
                              $rootScope.credentials.productWebservicePassword = '';
                          })
  
  
                  $scope.$parent.sendAlert('Login Data saved', 'success');
  
              }
  
  
          }, function error(response) {
              BrowserExtensionService.runtime.sendMessage({
                  action: 'clear-cache',
                  from: 'settings-ctrl'
              });
              $scope.$parent.sendAlert('Incorrect Login Data', 'danger');
              $rootScope.credentials.webservicePassword = '';
          });
  
  
      };
  
  
  }
  
  angular.module('AffilinetToolbar')
      .controller('WidgetController', ['$scope', '$rootScope', '$sce' , '$translate', '$timeout', 'BrowserExtensionService', 'productWebservice', '$stateParams', 'LogonService', '$location', WidgetController]);
  
  function WidgetController($scope, $rootScope, $sce, $translate, $timeout, BrowserExtensionService, productWebservice, $stateParams, LogonService, $location) {
  
  
      $scope.loadingFinished = false;
      $translate('WIDGET_PageName').then(function (text) {
          $scope.$parent.pageName = text;
      });
  
      $scope.messages = [];
      $translate('WIDGET_SureYouWantToDeleteWidget').then(function (text) {
          $scope.messages.WIDGET_SureYouWantToDeleteWidget = text;
      });
  
      $translate('WIDGET_SureYouWantToClearProductsFromWidget').then(function (text) {
          $scope.messages.WIDGET_SureYouWantToClearProductsFromWidget = text;
      });
      $translate('WIDGET_WarningChangesNotSaved').then(function (text) {
          $scope.messages.WIDGET_WarningChangesNotSaved = text;
      });
      $translate('WIDGET_WidgetCreated').then(function (text) {
          $scope.messages.WIDGET_WidgetCreated = text;
      });
      $translate('WIDGET_WidgetSaved').then(function (text) {
          $scope.messages.WIDGET_WidgetSaved = text;
      });
  
  
      $scope.storedProductLists = [];
      /**
       * @deprecated
       * @type {Array}
       */
      $scope.storedWidgets = [];
      $scope.migrationStarted = false;
  
      /**
       * widgets from server
       * @type {Array}
       */
      $scope.allWidgets = [];
  
  
      $scope.productDetails = [];
  
      $scope.sliderOffset = 0;
      $scope.widgetCode = '';
  
      $scope.currentlyDraggedItem = {};
  
      let programUrlsRequested = [];
      let programUrls = [];
  
      $scope.selectedWidget = {};
  
  
      $scope.sliderIsReady = false;
  
      $scope.defaultWidget = {
          id: null,
          widgetName: '',
          autoRotate: true,
          price: false,
          name: true,
          shop: false,
          brand: false,
          manufacturer: false,
          type: 'carousel',
          productInfoOnHover: true,
          imageSize: '180',
          products: []
      };
      $scope.widget = angular.copy($scope.defaultWidget);
      $scope.widget.products = [];
  
  
      $scope.copySuccess = false;
  
  
      $scope.currentSlideIndex = 1;
      $scope.slickConfig = {
          enabled: true,
          autoPlay: $scope.autoRotate,
          autoPlaySpeed: 100,
          dots: false,
          arrows: true,
          swipe: true,
          infinite: true,
          method: {},
          respondTo: 'window',
          slidesToShow: 4,
          slidesToScroll: 4,
  
          event: {
              afterChange: function (event, slick, currentSlide, nextSlide) {
                  $scope.currentSlideIndex = currentSlide; // save current index each time
              },
              init: function (event, slick) {
                  slick.slickGoTo($scope.currentSlideIndex); // slide to correct index when init
              }
          }
      };
  
      $scope.addWatchToWidget = function () {
  
          $scope.createWidgetCode();
          $scope.$watch('widget', function (newVal, old) {
              $scope.createWidgetCode();
              if (
                  old.products.length !== newVal.products.length
                  || old.imageSize !== newVal.imageSize
  
              ) {
                  console.log('products, imageSize is not equal: reload slider');
                  $scope.refreshSlider();
              }
          }, true);
      };
  
  
      $scope.activateDragOnSlick = function () {
          "use strict";
          $(function () {
              $('*[draggable!=true]', '.slick-track').unbind('dragstart');
              $(".affilinet-product-widget-product").draggable({
                  placeholder: true,
                  animate: true,
                  onStart: 'startDragProduct',
                  revert: true,
                  revertDuration: 0,
                  appendTo: 'body',
                  containment: 'window',
                  scroll: false,
                  helper: function () {
                      return $(this).clone().appendTo("#widgetContainer").removeClass('slick-slide').show();
  
                  },
              });
          });
  
          $(".affilinet-product-widget-product").on("draggable mouseenter mousedown", function (event) {
              event.stopPropagation();
          });
      };
  
      $scope.refreshSlider = function () {
          console.log('deactivate slider');
  
          $scope.sliderIsReady = false; // disable slick
          $timeout(() => {
              $scope.sliderIsReady = true;
              $scope.activateDragOnSlick();
          }, 100);
      };
      $scope.refreshSlider();
  
      $scope.saveWidget = function () {
  
          $scope.widget.platform =  function(){
              if ($rootScope.credentials  && $rootScope.credentials.countryPlatform ) {
                  switch ($rootScope.credentials.countryPlatform) {
                      case 'DE': return 'de';
                      case 'AT': return 'at';
                      case 'CH': return 'ch';
                      case 'EN': return 'uk';
                      case 'FR': return 'fr';
                      case 'NL': return 'nl';
                  }
              }
          }();
  
          if ($scope.widget.id !== null) {
              const index = $scope.allWidgets.findIndex((widget) => {
                  return widget.id === $scope.widget.id
              });
              LogonService.WidgetUpdate($scope.widget.id, $scope.widget).then(function (result) {
                  "use strict";
                  let changedWidget = result.data;
                  $scope.allWidgets[index] = changedWidget;
                  $scope.selectedWidget = changedWidget;
                  $scope.$parent.sendAlert($scope.messages.WIDGET_WidgetSaved, 'success')
  
  
              }, function (error) {
                  "use strict";
                  console.error(error);
                  $scope.$parent.sendAlert('Could not save widget', 'danger')
              });
  
  
          } else {
  
              LogonService.WidgetCreate($scope.widget).then(function (result) {
                  "use strict";
                  let newWidget = result.data;
                  // put it to the end
                  $scope.allWidgets.unshift(newWidget);
                  $scope.widget.id = newWidget.id;
                  $scope.selectedWidget = newWidget;
                  $scope.$parent.sendAlert($scope.messages.WIDGET_WidgetCreated, 'success')
  
              }, function (error) {
                  "use strict";
                  console.error(error);
                  $scope.$parent.sendAlert('Could not save widget', 'danger')
              });
  
          }
  
  
      };
  
  
      $scope.trustAsUrl = function (url) {
          "use strict";
          return $sce.trustAsUrl(url);
      }
      $scope.createNewWidget = function () {
  
          // not saved
          if ($scope.widget.id === null && $scope.widget.products.length > 0) {
              if (!confirm($scope.messages.WIDGET_WarningChangesNotSaved)) {
                  return;
              }
          } else {
              // save changes!
              if ($scope.widget.widgetName !== '') {
                  $scope.saveWidget();
              }
          }
  
          $scope.widget = angular.copy($scope.defaultWidget);
          $scope.widget.id = null;
          $scope.selectedWidget = null;
          $scope.addWatchToWidget();
          $scope.refreshSlider();
      };
  
      $scope.copyWidget = function () {
          "use strict";
          $scope.widget.widgetName += ' Copy';
          $scope.widget.id = null
      };
  
      $scope.deleteWidget = function () {
  
          /**
           * delete it serverside, then remove from allWidgets
           */
  
          if (!confirm($scope.messages.WIDGET_SureYouWantToDeleteWidget)) {
              return;
          }
  
          LogonService.WidgetDelete($scope.widget.id).then(
              function (result) {
                  const index = $scope.allWidgets.findIndex((widget) => {
                      return widget.id === $scope.widget.id
                  });
                  $scope.selectedWidget = null;
                  $scope.widget = angular.copy($scope.defaultWidget);
                  $scope.addWatchToWidget();
                  $scope.allWidgets.splice(index, 1);
              },
              function (error) {
                  $scope.$parent.sendAlert('Could not delete widget. Please check internet connection', 'danger')
              }
          )
  
  
      };
  
      $scope.clearProductsFromWidget = function () {
          "use strict";
          if (confirm($scope.messages.WIDGET_SureYouWantToClearProductsFromWidget)) {
              $scope.widget.products = [];
          }
      }
  
      $scope.openWidget = function () {
          "use strict";
          console.log('open widget', $scope.selectedWidget);
  
          if ($scope.selectedWidget && $scope.selectedWidget.id) {
              const index = $scope.allWidgets.findIndex((widget) => {
                  return widget.id === $scope.selectedWidget.id
              });
              $scope.widget = angular.copy($scope.allWidgets[index]);
              $scope.addWatchToWidget();
              $scope.refreshSlider();
          }
  
      };
  
  
      $scope.getWidgetSizeAsArray = function () {
  
          // more than one empty product at the end? remove them
          let lenght = $scope.widget.products.length;
          for (let i = lenght; i >= 0; i--) {
              if (i !== lenght && $scope.widget.products[i] === null) {
                  $scope.widget.products.splice(i, 1);
                  break;
              }
          }
          let temp = [];
          let size = parseInt($scope.widget.products.length) + 1;
          for (var j = 0; j < size; j++) {
              temp.push(j)
          }
          return temp;
      }
  
  
      $scope.dropProductOnWidget = function (event, draggable) {
          let targetIndex = +event.target.getAttribute('data-index');
  
          let draggedItemInfo = {
              from: draggable.helper[0].getAttribute('data-from'),
              productId: draggable.helper[0].getAttribute('data-product-id'),
              index: draggable.helper[0].getAttribute('data-index'),
              targetIndex: targetIndex
          };
          console.log('DROP ON WIDGET draggedItemInfo', draggedItemInfo);
  
          if (draggedItemInfo.productId === null || typeof draggedItemInfo.productId === 'undefined') {
              return false;
          }
  
          const tartgetProduct = $scope.widget.products[targetIndex];
          console.log('tartgetProduct product')
  
          if (draggedItemInfo.from === 'widget') {
              // ziel ist leer?
              if (tartgetProduct === undefined || tartgetProduct === null) {
                  // überschreibe die null
                  $scope.widget.products[targetIndex] = $scope.getProductDetail(draggedItemInfo.productId);
                  // entferne die alte karte
                  $scope.widget.products.splice(draggedItemInfo.index, 1);
  
              } else {
                  // tausche die karten
                  let temp = $scope.widget.products[targetIndex];
                  $scope.widget.products[targetIndex] = $scope.getProductDetail(draggedItemInfo.productId);
                  $scope.widget.products[draggedItemInfo.index] = temp;
              }
          } else {
              if (tartgetProduct === undefined || tartgetProduct === null) {
                  // überschreibe leere karten
                  $scope.widget.products[targetIndex] = $scope.getProductDetail(draggedItemInfo.productId);
              } else {
                  // füge die karte ein
                  $scope.widget.products.splice(targetIndex, 0, $scope.getProductDetail(draggedItemInfo.productId));
              }
          }
          $scope.currentlyDraggedItem = {};
          $scope.refreshSlider();
          return true;
      };
  
  
      let storeProductDetail = function (ApiProduct, addToWidget = false) {
  
          if (programUrlsRequested[ApiProduct.ProgramId] !== true) {
              programUrlsRequested[ApiProduct.ProgramId] = true;
              BrowserExtensionService.runtime.sendMessage({
                      action: 'get-programDetailsForProgramId',
                      data: {programId: ApiProduct.ProgramId}
                  },
                  function (programDetails) {
                      if (programDetails !== false) {
                          programUrls[ApiProduct.ProgramId] = programDetails.programUrl;
                          $scope.productDetails[ApiProduct.ProductId] = {
                              "id": ApiProduct.ProductId,
                              "url": ApiProduct.Deeplink1,
                              "img": ApiProduct.Images[0][0].URL,
                              "price": ApiProduct.PriceInformation.DisplayPrice,
                              "brand": ApiProduct.Brand,
                              "name": ApiProduct.ProductName,
                              "manufacturer": ApiProduct.Manufacturer,
                              "shop": programDetails.programUrl,
                              "articleNumber": ApiProduct.ArticleNumber,
                              "shopId": ApiProduct.ShopId,
                              "deleted": false
                          }
  
                          if (addToWidget) {
                              $scope.widget.products.push($scope.productDetails[ApiProduct.ProductId])
                          }
                      }
                  })
          }
          else {
              $scope.productDetails[ApiProduct.ProductId] = {
                  "id": ApiProduct.ProductId,
                  "url": ApiProduct.Deeplink1,
                  "img": ApiProduct.Images[0][0].URL,
                  "price": ApiProduct.PriceInformation.DisplayPrice,
                  "brand": ApiProduct.Brand,
                  "name": ApiProduct.ProductName,
                  "manufacturer": ApiProduct.Manufacturer,
                  "shop": programUrls[ApiProduct.ProgramId],
                  "articleNumber": ApiProduct.ArticleNumber,
                  "shopId": ApiProduct.ShopId,
                  "deleted": false
              }
              if (addToWidget) {
                  $scope.widget.products.push($scope.productDetails[ApiProduct.ProductId])
              }
          }
  
      }
  
      $scope.getProductDetail = function (productId) {
          let productDetails = $scope.productDetails[productId];
  
  
          if (typeof productDetails === 'undefined') {
              console.log('getProductDetail is null ', productId)
              return null;
          }
          return productDetails
  
      }
  
      $scope.dropProductOnTrash = function ($event, draggable) {
          // remove the product from widget
          let draggedItemInfo = {
              from: draggable.helper[0].getAttribute('data-from'),
              productId: draggable.helper[0].getAttribute('data-product-id'),
              index: draggable.helper[0].getAttribute('data-index')
          };
  
          if (draggedItemInfo.from === 'widget') {
              $scope.widget.products.splice(draggedItemInfo.index, 1);
              return true;
          }
          return false;
      }
  
      $scope.deleteProductFromWidget = function (index) {
          console.log('deleteProductFromWidget', index);
          $scope.widget.products.splice(index, 1);
      }
  
      $scope.dropProductOnProductList = function ($event, data) {
          // remove the product from widget
          if (data.from === 'widget') {
              $scope.widget.products[data.index] = null;
              return true;
          }
          return false;
      };
  
  
      $scope.loadProductData = function (productIdArray, addToWidget = false) {
          let i = 0;
          let batch = 0;
          let productBatches = [];
          angular.forEach(productIdArray, (productId) => {
  
              if (!productBatches[batch]) {
                  productBatches[batch] = [productId];
              } else {
                  productBatches[batch].push(productId);
              }
              if (productBatches[batch].length === 50) {
                  batch++;
              }
              i++;
          });
  
          // load the product details from webservice
  
          angular.forEach(productBatches, function (productIds) {
              productWebservice.GetProducts(productIds).then(
                  (response) => {
                      angular.forEach(response.data.Products, (ApiProduct) => {
                          storeProductDetail(ApiProduct, addToWidget)
  
                      });
                      $scope.createWidgetCode();
                  }
              )
  
          });
  
      };
  
  
      $scope.createWidgetCode = function () {
          "use strict";
          if ($scope.widget.id === null) {
              $scope.widgetCode = null;
              return
          }
  
          let code = '<div id="affilinet-product-widget-' + $scope.widget.id + '"' +
              ' class="affilinet-product-widget"' +
              ' data-affilinet-widget-id="' + $scope.widget.id + '">' +
              '<link rel="stylesheet" type="text/css" href="https://productwidget.com/style-1.0.0.css">' +
              '<scr' + 'ipt type="text/javascript">' +
              '!function(d){var e,i = \'affilinet-product-widget-script\';if(!d.getElementById(i)){' +
              'e = d.createElement(\'script\');' +
              'e.id = i;' +
              'e.src = \'https://productwidget.com/affilinet-product-widget-1.0.0-min.js\';' +
              'd.body.appendChild(e);}' +
              'if (typeof window.__affilinetWidget===\'object\')if (d.readyState===\'complete\'){' +
              'window.__affilinetWidget.init();}}(document);</scr' + 'ipt></div>'
  
          $scope.widgetCode = code;
  
      }
  
  
      $scope.copiedCode = function () {
          $scope.copySuccess = true;
          $timeout(function () {
              $scope.copySuccess = false;
          }, 2000);
      }
  
  
      /**
       * convert old stored widgets to server side saved widgets.
       * This ugly code is to be removed in the next versions when all old widgets are converted
       */
      migrateOldWidgets = function () {
  
          BrowserExtensionService.storage.local.get(['storedWidgets'], function (res) {
  
              if (res.storedWidgets && $scope.migrationStarted === false) {
                  $scope.migrationStarted = true;
                  console.log('LEGACY widgets existing', res.storedWidgets);
  
                  let legacyProductIds = [];
                  res.storedWidgets.forEach(function (legacyWidget) {
                      legacyWidget.products.forEach(function (productId) {
                          if (typeof productId === 'string' || typeof productId === 'number') {
                              legacyProductIds.push(productId);
                          }
                      });
                  });
                  console.log(legacyProductIds);
  
  
                  let batch = 0;
                  let productBatches = [];
                  angular.forEach(legacyProductIds, (productId) => {
  
                      if (!productBatches[batch]) {
                          productBatches[batch] = [productId];
                      } else {
                          productBatches[batch].push(productId);
                      }
                      if (productBatches[batch].length === 50) {
                          batch++;
                      }
                  });
  
                  // load the product details from webservice
                  let fetchedBatches = 0;
  
  
                  angular.forEach(productBatches, (productIds) => {
                      console.log('handling batch', fetchedBatches)
  
                      productWebservice.GetProducts(productIds).then(
                          (response) => {
                              angular.forEach(response.data.Products, (product) => {
                                  storeProductDetail(product);
                              });
  
  
                              if (fetchedBatches === productBatches.length) {
                                  console.log('finished loading legacy widget products, batches: ', fetchedBatches)
  
                                  window.setTimeout((res) => {
                                      res.storedWidgets.forEach((legacyWidget) => {
  
                                          let convertedWidget = legacyWidget;
  
                                          // geth das????
                                          let products = [];
                                          convertedWidget.products.forEach((productId) => {
                                              let product = $scope.getProductDetail(productId);
                                              if (product !== null) {
                                                  products.push(product)
                                              } else {
                                                  console.error('product not found', productId)
  
                                              }
                                          });
                                          convertedWidget.products = products;
                                          convertedWidget.platform =  function(){
                                              if ($rootScope.credentials  && $rootScope.credentials.countryPlatform ) {
                                                  switch ($rootScope.credentials.countryPlatform) {
                                                      case 'DE': return 'de';
                                                      case 'AT': return 'at';
                                                      case 'CH': return 'ch';
                                                      case 'EN': return 'uk';
                                                      case 'FR': return 'fr';
                                                      case 'NL': return 'nl';
                                                  }
                                              }
                                          }();
  
  
  
                                          console.log('converting widget', convertedWidget);
  
                                          LogonService.WidgetCreate(convertedWidget).then(function (result) {
                                              "use strict";
                                              let newWidget = result.data;
                                              // put it to the end of allwidgets
                                              $scope.allWidgets.unshift(newWidget);
                                              $scope.$parent.sendAlert('Migrated widget', 'success');
  
                                              // remove it from storedWidgets
                                              const index = $scope.storedWidgets.findIndex((widget) => {
                                                  return widget.id === legacyWidget.id
                                              });
                                              $scope.storedWidgets.splice(index, 1);
  
                                              // remove it from browserextension storage
                                              BrowserExtensionService.storage.local.set({storedWidgets: $scope.storedWidgets});
  
                                          }, function (error) {
  
  
                                              // remove it from storedWidgets
                                              const index = $scope.storedWidgets.findIndex((widget) => {
                                                  return widget.id === legacyWidget.id
                                              });
                                              $scope.storedWidgets.splice(index, 1);
  
                                              // remove it from browserextension storage
                                              BrowserExtensionService.storage.local.set({storedWidgets: $scope.storedWidgets});
                                              console.error(error)
                                          });
  
  
                                      });
                                  }, 2000, res)
  
                              }
                          }
                      );
                      fetchedBatches++;
  
                  })
  
  
              }
          });
      }
  
  
      /***
       * Inititalizing functions
       */
  
  
      if ($stateParams.productIds.length > 0) {
          $scope.loadProductData($stateParams.productIds, true);
      }
  
  
      $scope.addWatchToWidget();
  
  
      /**
       * Load the widgets
       */
  
      BrowserExtensionService.storage.local.get(['storedProductLists'], function (res) {
  
  
          if (res.storedProductLists) {
              $scope.storedProductLists = res.storedProductLists;
              if ($scope.storedProductLists[0]) {
                  $scope.selectedProductList = $scope.storedProductLists[0];
              }
          }
  
  
          let allProductIds = [];
          angular.forEach($scope.storedProductLists, function (productList) {
              productList.products.forEach(function (prodId) {
                  allProductIds.push(prodId)
              })
          });
  
          if ($stateParams.productIds.length === 0) {
  
              $stateParams.productIds.forEach(function (prodId) {
                  allProductIds.push(prodId)
              })
          }
  
  
          $scope.loadProductData(allProductIds);
  
  
          LogonService.WidgetIndex().then(function (response) {
              $scope.allWidgets = response.data;
              if ($scope.allWidgets.length > 0) {
                  $scope.selectedWidget = $scope.allWidgets[0]
                  console.log('loaded widget', $scope.selectedWidget)
  
                  if ($stateParams.widgetId !== null) {
                      // user wants to load a widget
                      const widgetIndex = $scope.allWidgets.findIndex(function (widg) {
                          return widg.id === $stateParams.widgetId
                      });
                      console.log('open widget from state param');
                      $scope.selectedWidget = $scope.allWidgets[widgetIndex];
                      $scope.openWidget()
                  }
                  // for initial page view loading of widget (the search parameter widgetId is set in the background script)
                  else if ($location.search().widgetId) {
                      const widgetIndex = $scope.allWidgets.findIndex(function (widg) {
                          return widg.id == $location.search().widgetId;
                      });
  
                      $scope.selectedWidget = $scope.allWidgets[widgetIndex];
                      $scope.openWidget()
                  }
                  else if ($stateParams.productIds.length === 0) {
                      console.log('open first widget');
                      $scope.selectedWidget = $scope.allWidgets[0];
                      $scope.openWidget()
                  }
  
              }
  
          }, function (error) {
              $scope.$parent.sendAlert('Could not load widgets, please check internet connection', 'danger')
          })
  
          $scope.loadingFinished = true;
  
          // migrateOldWidgets()
      });
  
      // when the serarch param changes (done by background script)
      $rootScope.$on('$locationChangeSuccess', function(){
  
          if ($scope.selectedWidget && $location.search().widgetId && $location.search().widgetId != $scope.selectedWidget.id) {
              const widgetIndex = $scope.allWidgets.findIndex(function (widg) {
                  return widg.id == $location.search().widgetId;
              });
              $scope.selectedWidget = $scope.allWidgets[widgetIndex];
              $scope.openWidget()
  
          }
  
      })
  };
  
  /**
   * Loading Directive
   * @see http://tobiasahlin.com/spinkit/
   */
  
  angular
      .module('AffilinetToolbar')
      .directive('rdLoading', rdLoading);
  
  function rdLoading() {
      var directive = {
          restrict: 'AE',
          template: '<div class="loading"><div class="double-bounce1"></div><div class="double-bounce2"></div></div>'
      };
      return directive;
  };
  angular.module('AffilinetToolbar').directive('selectOnClick', ['$window', function ($window) {
      return {
          restrict: 'A',
          link: function (scope, element, attrs) {
              element.on('click', function () {
                  if (!$window.getSelection().toString()) {
                      // Required for mobile Safari
                      this.setSelectionRange(0, this.value.length)
                  }
              });
          }
      };
  }]);
  angular.module('AffilinetToolbar')
  
      .factory('LogonService', ['$http', '$moment', '$timeout', '$q', '$rootScope', '$window', 'BrowserExtensionService', function ($http, $moment, $timeout, $q, $rootScope, $window, BrowserExtensionService) {
  
          let endpointPublisherWebservice = 'https://api.affili.net/V2.0/';
          let endpointWidgetWebservice = 'https://productwidget.com/api/v1.0/';
  
  
          let token = false;
          let validUntil = false;
          let credentials = false;
  
          let _sendRequest = function (requestBody, method, soap_action, successCallback, errorCallback) {
              $http({
                  method: 'POST',
                  data: requestBody,
                  url: endpointPublisherWebservice + method,
                  headers: {
                      "SOAPAction": soap_action,
                      "Content-Type": 'text/xml; charset=utf-8'
                  }
              }).then(successCallback, errorCallback);
  
          };
  
          let _tokenMustBeRefreshed =  () => {
              // Refresh token 2 minutes before it is invalid
              const now =  new $moment().add(2, 'minutes');
              if (token === false) {
                  return true;
              }
              console.log('token will be valid for',  $moment(validUntil) - now, token)
              return validUntil === false || validUntil < now;
          };
  
  
          let _removeToken = function () {
              "use strict";
              token = false;
              validUntil = false;
          };
  
          let _getToken = function () {
              let deferred = $q.defer();
  
  
              if (_tokenMustBeRefreshed()) {
                  Logon().then(
                      function success(response) {
                          let localToken = response.data.Envelope.Body.CredentialToken.toString();
                          token = localToken;
                          _getTokenExpiration(token).then(
                              (tokenExpResult) => {
                                  let expirationDate = tokenExpResult.data.Envelope.Body.ExpirationDate.toString();
                                  validUntil = new Date(expirationDate);
                                  console.log('created a token, it is valid until', validUntil);
                                  deferred.resolve(localToken);
  
                              },
                              (error) => {
                                  console.log('error in get token expiration ', error);
                                  validUntil = 0;
                                  deferred.reject(error);
                              }
                          );
                      },
                      function error(response) {
                          deferred.reject(response);
                      }
                  )
              } else {
                  deferred.resolve(token);
              }
              return deferred.promise
          };
  
  
          let _getWidgetApiCredentials = function() {
              let deferred = $q.defer();
              _getToken().then(function(){
                  "use strict";
                  let httpConfig = {
                      params : {
                          publisherId :  $rootScope.credentials.publisherId,
                          credentialToken :  token,
                      }
                  };
                  deferred.resolve(httpConfig)
  
              }, function(error) {
                  "use strict";
                  deferred.reject(error)
              })
              return deferred.promise;
          }
  
          let _loadCreadentialsFromRootScope = function () {
              let deferred = $q.defer();
  
              let checkIfCredentialObjectIsSet = function () {
                  BrowserExtensionService.storage.local.get(['publisherId', 'webservicePassword', 'countryPlatform'], function(result) {
                      "use strict";
                      if (result.webservicePassword && result.publisherId && result.countryPlatform) {
                          clearInterval(refreshIntervalId);
                          deferred.resolve(result);
                          $rootScope.$broadcast('updateCredentials');
                          $rootScope.credentialsLoaded = true;
                          $rootScope.validCredentials = true;
                      } else {
                          deferred.reject('No Data found');
                          $rootScope.$broadcast('updateCredentials');
                          $rootScope.credentialsLoaded = true;
                          $rootScope.validCredentials = false;
                          clearInterval(refreshIntervalId);
                      }
                  })
              };
              let refreshIntervalId = null;
              refreshIntervalId = setInterval(checkIfCredentialObjectIsSet, 50);
              return deferred.promise;
  
          };
  
          let Logon = function () {
              let deferred = $q.defer();
              _loadCreadentialsFromRootScope().then(
                  function success(credentials) {
                      let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:typ="http://affilinet.framework.webservices/types">' +
                          '<soapenv:Header/>' +
                          '<soapenv:Body>' +
                          '<svc:LogonRequestMsg>' +
                          '<typ:Username>' + credentials.publisherId.trim() + '</typ:Username>' +
                          '<typ:Password>' + credentials.webservicePassword.trim() + '</typ:Password>' +
                          '<typ:WebServiceType>Publisher</typ:WebServiceType>' +
                          '</svc:LogonRequestMsg>' +
                          '</soapenv:Body>' +
                          '</soapenv:Envelope>';
                      _sendRequest(requestBody, 'Logon.svc', 'http://affilinet.framework.webservices/Svc/ServiceContract1/Logon', deferred.resolve, deferred.reject);
                  },
                  function error(response) {
                      deferred.reject(response);
                  });
  
              return deferred.promise
          };
  
  
          let _getTokenExpiration = function (token) {
              let deferred = $q.defer();
              let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc">' +
                          '   <soapenv:Header/>' +
                          '   <soapenv:Body>' +
                          '      <svc:CredentialToken>' + token  + '</svc:CredentialToken>' +
                          '   </soapenv:Body>' +
                          '</soapenv:Envelope>';
              _sendRequest(requestBody, 'Logon.svc', 'http://affilinet.framework.webservices/Svc/AuthenticationContract/GetIdentifierExpiration',deferred.resolve, deferred.reject);
              return deferred.promise;
          };
  
  
          let _checkCredentials = function (credentials) {
              let deferred = $q.defer();
              let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:typ="http://affilinet.framework.webservices/types">' +
                  '<soapenv:Header/>' +
                  '<soapenv:Body>' +
                  '<svc:LogonRequestMsg>' +
                  '<typ:Username>' + credentials.publisherId.trim() + '</typ:Username>' +
                  '<typ:Password>' + credentials.webservicePassword.trim() + '</typ:Password>' +
                  '<typ:WebServiceType>Publisher</typ:WebServiceType>' +
                  '</svc:LogonRequestMsg>' +
                  '</soapenv:Body>' +
                  '</soapenv:Envelope>';
              _sendRequest(requestBody, 'Logon.svc', 'http://affilinet.framework.webservices/Svc/ServiceContract1/Logon', deferred.resolve, deferred.reject);
  
              return deferred.promise;
  
          };
  
  
          return {
              LoadCreadentialsFromRootScope: function () {
                  "use strict";
                  return _loadCreadentialsFromRootScope();
              },
  
              GetToken: function () {
                  "use strict";
                  return _getToken();
              },
  
              RemoveToken: function () {
                  "use strict";
                  _removeToken();
              },
  
              CheckCredentials: function (credentials) {
                  "use strict";
                  let deferred = $q.defer();
                  _checkCredentials(credentials).then(
                      function success(response) {
                          deferred.resolve(response);
  
                      },
                      function error(response) {
                          deferred.reject(response);
                      }
                  );
                  return deferred.promise;
  
              },
  
              GetProgram: function (programId) {
                  let deferred = $q.defer();
  
                  _getToken().then(
                      function success(response) {
                          let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:pub="http://affilinet.framework.webservices/types/PublisherProgram" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">' +
                              '<soapenv:Header/>' +
                              '<soapenv:Body>' +
                              '<svc:GetProgramsRequest>' +
                              '<svc:CredentialToken>' + response + '</svc:CredentialToken>' +
                              '<svc:DisplaySettings><pub:CurrentPage>1</pub:CurrentPage><pub:PageSize>1</pub:PageSize></svc:DisplaySettings>' +
                              '<svc:GetProgramsQuery>' +
                              '<pub:ProgramIds><arr:int>' + programId + '</arr:int></pub:ProgramIds>' +
                              '<pub:PartnershipStatus>' +
                              '<pub:ProgramPartnershipStatusEnum>Active</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>NoPartnership</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Paused</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Waiting</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Refused</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Cancelled</pub:ProgramPartnershipStatusEnum>' +
                              '</pub:PartnershipStatus>' +
                              '</svc:GetProgramsQuery>' +
                              '</svc:GetProgramsRequest>' +
                              '</soapenv:Body>' +
                              '</soapenv:Envelope>';
  
                          _sendRequest(requestBody, 'PublisherProgram.svc', 'http://affilinet.framework.webservices/Svc/PublisherProgramContract/SearchPrograms', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
  
                      }
                  );
                  return deferred.promise;
              },
  
  
              GetProgramInfoForIds: function (programIds) {
                  let deferred = $q.defer();
  
                  _getToken().then(
                      function success(response) {
                          let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:pub="http://affilinet.framework.webservices/types/PublisherProgram" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">' +
                              '<soapenv:Header/>' +
                              '<soapenv:Body>' +
                              '<svc:GetProgramsRequest>' +
                              '<svc:CredentialToken>' + response + '</svc:CredentialToken>' +
                              '<svc:DisplaySettings><pub:CurrentPage>1</pub:CurrentPage><pub:PageSize>100</pub:PageSize></svc:DisplaySettings>' +
                              '<svc:GetProgramsQuery>' +
                              '<pub:ProgramIds>';
  
                          angular.forEach(programIds, function (val, key) {
                              requestBody = requestBody + '<arr:int>' + val + '</arr:int>';
                          });
                          requestBody = requestBody +
  
                              '</pub:ProgramIds>' +
                              '<pub:PartnershipStatus>' +
                              '<pub:ProgramPartnershipStatusEnum>Active</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Paused</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Waiting</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Cancelled</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Refused</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>NoPartnership</pub:ProgramPartnershipStatusEnum>' +
                              '</pub:PartnershipStatus>' +
                              '</svc:GetProgramsQuery>' +
                              '</svc:GetProgramsRequest>' +
                              '</soapenv:Body>' +
                              '</soapenv:Envelope>';
  
                          _sendRequest(requestBody, 'PublisherProgram.svc', 'http://affilinet.framework.webservices/Svc/PublisherProgramContract/SearchPrograms', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
  
                      }
                  );
                  return deferred.promise;
              },
  
              GetVouchers: function (programId) {
                  let deferred = $q.defer();
  
                  _getToken().then(
                      function success(response) {
                          let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:pub="http://affilinet.framework.webservices/types/PublisherInbox">' +
                              '<soapenv:Header/>' +
                              '<soapenv:Body>' +
                              '<svc:SearchVoucherCodesRequest>' +
                              '<svc:CredentialToken>' + response + '</svc:CredentialToken>' +
                              '<svc:DisplaySettings><pub:CurrentPage>1</pub:CurrentPage><pub:PageSize>100</pub:PageSize></svc:DisplaySettings>' +
                              '<svc:SearchVoucherCodesRequestMessage>' +
                              '<pub:ProgramId>' + programId + '</pub:ProgramId>' +
                              '</svc:SearchVoucherCodesRequestMessage>' +
                              '</svc:SearchVoucherCodesRequest>' +
                              '</soapenv:Body>' +
                              '</soapenv:Envelope>';
  
                          _sendRequest(requestBody, 'PublisherInbox.svc', 'http://affilinet.framework.webservices/Svc/PublisherInboxContract/SearchVoucherCodes', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
  
                      }
                  );
                  return deferred.promise;
              },
  
              GetNewVouchers: function () {
                  let deferred = $q.defer();
  
                  _getToken().then(
                      function success(response) {
                          let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:pub="http://affilinet.framework.webservices/types/PublisherInbox">' +
                              '<soapenv:Header/>' +
                              '<soapenv:Body>' +
                              '<svc:SearchVoucherCodesRequest>' +
                              '<svc:CredentialToken>' + response + '</svc:CredentialToken>' +
                              '<svc:DisplaySettings><pub:CurrentPage>1</pub:CurrentPage><pub:PageSize>10</pub:PageSize><pub:SortBy>StartDate</pub:SortBy><pub:SortOrder>Descending</pub:SortOrder></svc:DisplaySettings>' +
                              '<svc:SearchVoucherCodesRequestMessage>' +
                              '</svc:SearchVoucherCodesRequestMessage>' +
                              '</svc:SearchVoucherCodesRequest>' +
                              '</soapenv:Body>' +
                              '</soapenv:Envelope>';
  
                          _sendRequest(requestBody, 'PublisherInbox.svc', 'http://affilinet.framework.webservices/Svc/PublisherInboxContract/SearchVoucherCodes', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
  
                      }
                  );
                  return deferred.promise;
              },
  
  
              GetCreatives: function (programId) {
                  let deferred = $q.defer();
  
                  _getToken().then(
                      function success(response) {
                          let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:pub="http://affilinet.framework.webservices/types/PublisherCreative" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">' +
                              '<soapenv:Header/>' +
                              '<soapenv:Body>' +
                              '<svc:SearchCreativesRequest>' +
  
                              '<svc:CredentialToken>' + response + '</svc:CredentialToken>' +
                              '<svc:DisplaySettings><pub:CurrentPage>1</pub:CurrentPage><pub:PageSize>100</pub:PageSize></svc:DisplaySettings>' +
  
                              '<svc:SearchCreativesQuery>' +
                              '<pub:ProgramIds><arr:int>' + programId + '</arr:int></pub:ProgramIds>' +
                              '<pub:CreativeTypes>' +
                              '<pub:CreativeTypeEnum>Banner</pub:CreativeTypeEnum>' +
                              '<pub:CreativeTypeEnum>HTML</pub:CreativeTypeEnum>' +
                              '</pub:CreativeTypes>' +
                              '</svc:SearchCreativesQuery>' +
                              '</svc:SearchCreativesRequest>' +
                              '</soapenv:Body>' +
                              '</soapenv:Envelope>';
  
                          _sendRequest(requestBody, 'PublisherCreative.svc', 'http://affilinet.framework.webservices/Svc/PublisherCreativeServiceContract/SearchCreatives', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
  
                      }
                  );
                  return deferred.promise;
              },
  
              GetNewPrograms: function () {
                  let deferred = $q.defer();
  
                  _getToken().then(
                      function success(response) {
                          let requestBody = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:pub="http://affilinet.framework.webservices/types/PublisherProgram" xmlns:arr="http://schemas.microsoft.com/2003/10/Serialization/Arrays">' +
                              '<soapenv:Header/>' +
                              '<soapenv:Body>' +
                              '<svc:GetProgramsRequest>' +
                              '<svc:CredentialToken>' + response + '</svc:CredentialToken>' +
                              '<svc:DisplaySettings><pub:CurrentPage>1</pub:CurrentPage><pub:PageSize>10</pub:PageSize><pub:SortByEnum>ProgramLifetime</pub:SortByEnum><pub:SortOrderEnum>Descending</pub:SortOrderEnum></svc:DisplaySettings>' +
                              '<svc:GetProgramsQuery>' +
  
                              '<pub:PartnershipStatus>' +
                              '<pub:ProgramPartnershipStatusEnum>Active</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>NoPartnership</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Paused</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Waiting</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Refused</pub:ProgramPartnershipStatusEnum>' +
                              '<pub:ProgramPartnershipStatusEnum>Cancelled</pub:ProgramPartnershipStatusEnum>' +
                              '</pub:PartnershipStatus>' +
                              '</svc:GetProgramsQuery>' +
                              '</svc:GetProgramsRequest>' +
                              '</soapenv:Body>' +
                              '</soapenv:Envelope>';
  
                          _sendRequest(requestBody, 'PublisherProgram.svc', 'http://affilinet.framework.webservices/Svc/PublisherProgramContract/SearchPrograms', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
  
                      }
                  );
                  return deferred.promise;
              },
  
              GetDailyStatistics: function (start, end) {
                  let deferred = $q.defer();
  
                  _getToken().then(
                      function success(response) {
                          let requestBody =
                              '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:pub="http://affilinet.framework.webservices/types/PublisherStatistics">' +
                              '<soapenv:Header/>' +
                              '<soapenv:Body>' +
                              '<svc:GetDailyStatisticsRequest>' +
  
                              '<svc:CredentialToken>' + response + '</svc:CredentialToken>' +
                              '<svc:GetDailyStatisticsRequestMessage>' +
                              '<pub:StartDate>' + start + '</pub:StartDate>' +
                              '<pub:EndDate>' + end + '</pub:EndDate>' +
                              '<pub:SubId></pub:SubId>' +
                              '<pub:ProgramTypes>All</pub:ProgramTypes>' +
                              '<pub:ValuationType>DateOfRegistration</pub:ValuationType>' +
  
                              '<pub:ProgramId>0</pub:ProgramId>' +
                              '</svc:GetDailyStatisticsRequestMessage>' +
                              '</svc:GetDailyStatisticsRequest>' +
                              '</soapenv:Body>' +
                              '</soapenv:Envelope>';
  
                          _sendRequest(requestBody, 'PublisherStatistics.svc', 'http://affilinet.framework.webservices/Svc/PublisherStatisticsContract/GetDailyStatistics', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
  
                      }
                  );
                  return deferred.promise;
              },
  
  
              GetAllProgramStatistics: function (start, end) {
                  let deferred = $q.defer();
  
                  _getToken().then(
                      function success(response) {
                          let requestBody =
                              '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:svc="http://affilinet.framework.webservices/Svc" xmlns:pub="http://affilinet.framework.webservices/types/PublisherStatistics">' +
                              '<soapenv:Header/>' +
                              '<soapenv:Body>' +
                              '<svc:GetProgramStatisticsRequest>' +
  
                              '<svc:CredentialToken>' + response + '</svc:CredentialToken>' +
                              '<svc:GetProgramStatisticsRequestMessage>' +
                              '<pub:StartDate>' + start + '</pub:StartDate>' +
                              '<pub:EndDate>' + end + '</pub:EndDate>' +
                              '<pub:SubId></pub:SubId>' +
                              '<pub:ProgramTypes>All</pub:ProgramTypes>' +
                              '<pub:ValuationType>DateOfRegistration</pub:ValuationType>' +
  
                              '<pub:ProgramIds></pub:ProgramIds>' +
                              '<pub:ProgramStatus>All</pub:ProgramStatus>' +
                              '</svc:GetProgramStatisticsRequestMessage>' +
                              '</svc:GetProgramStatisticsRequest>' +
                              '</soapenv:Body>' +
                              '</soapenv:Envelope>';
  
                          _sendRequest(requestBody, 'PublisherStatistics.svc', 'http://affilinet.framework.webservices/Svc/PublisherStatisticsContract/GetProgramStatistics', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
  
                      }
                  );
                  return deferred.promise;
              },
  
  
              WidgetCreate : function(data) {
                  let deferred = $q.defer();
                  _getWidgetApiCredentials().then(
                      function(credentials) {
                          $http.post(endpointWidgetWebservice + 'widgets', data, credentials).then(deferred.resolve, deferred.reject)
                      }
                  )
                  return deferred.promise;
              },
  
              WidgetUpdate : function(id, data){
                  let deferred = $q.defer();
                  _getWidgetApiCredentials().then(
                      function(credentials) {
                          $http.put(endpointWidgetWebservice + 'widgets/' + id, data, credentials).then(deferred.resolve, deferred.reject)
                      }
                  )
                  return deferred.promise;
              },
  
              WidgetDelete : function(id, data){
                  let deferred = $q.defer();
                  _getWidgetApiCredentials().then(
                      function(credentials) {
                          $http.delete(endpointWidgetWebservice + 'widgets/' + id, credentials).then(deferred.resolve, deferred.reject)
                      }
                  )
                  return deferred.promise;
              },
  
              WidgetIndex : function(){
                  let deferred = $q.defer();
                  _getWidgetApiCredentials().then(
                      function(credentials) {
                          $http.get(endpointWidgetWebservice + 'widgets', credentials).then(deferred.resolve, deferred.reject)
                      }
                  )
                  return deferred.promise;
              },
  
  
          }
      }])
  
  
  ;
  
  
  angular.module('AffilinetToolbar')
  
      .factory('productWebservice', ['$http', '$window', '$timeout', '$q', '$rootScope', 'BrowserExtensionService', function ($http, $window, $timeout, $q, $rootScope, BrowserExtensionService) {
  
          let endpointPublisherWebservice = 'https://product-api.affili.net/V3/productservice.svc/JSON/';
          let credentials = false;
  
          let _sendRequest = function (params, method, successCallback, errorCallback) {
              $http({
                  method: 'GET',
                  params: params,
                  url: endpointPublisherWebservice + method,
              }).then(successCallback, errorCallback);
  
          };
  
          let _loadCreadentialsFromRootScope = function () {
              let deferred = $q.defer();
  
              let checkIfCredentialObjectIsSet = function () {
                  BrowserExtensionService.storage.local.get(['publisherId', 'productWebservicePassword'], function(result) {
                      "use strict";
                      if (result.productWebservicePassword && result.publisherId) {
                          clearInterval(refreshIntervalId);
                          deferred.resolve(result);
                          $rootScope.$broadcast('updateCredentials');
                          $rootScope.credentialsLoaded = true;
                          $rootScope.validCredentials = true;
                      } else {
                          deferred.reject('No Data found');
                          $rootScope.$broadcast('updateCredentials');
                          $rootScope.credentialsLoaded = true;
                          $rootScope.validCredentials = false;
                          clearInterval(refreshIntervalId);
                      }
                  })
              };
              let refreshIntervalId = null;
              refreshIntervalId = setInterval(checkIfCredentialObjectIsSet, 50);
              return deferred.promise;
          };
  
  
  
  
          return {
              LoadCreadentialsFromRootScope: function () {
                  "use strict";
                  return _loadCreadentialsFromRootScope();
              },
  
              CheckLoginData : function(publisherId, productWebservicePassword) {
                  let deferred = $q.defer();
  
                  let params = {
                      PublisherId : publisherId,
                      Password : productWebservicePassword,
                      PageSize : 1
                  };
                  _sendRequest(params, 'GetShopList', deferred.resolve, deferred.reject);
  
                  return deferred.promise;
              },
  
              GetShopList: function () {
                  let deferred = $q.defer();
  
                  _loadCreadentialsFromRootScope().then(
                      function success(credentials) {
                          let params = {
                              PublisherId : credentials.publisherId,
                              Password : credentials.productWebservicePassword,
                              PageSize : 5000
                          };
                          _sendRequest(params, 'GetShopList', deferred.resolve, deferred.reject);
  
                      },
                      function error($rootScope) {
                          console.error('Error retrieving shoplist');
                          $rootScope.sendAlert('Error retrieving shoplist', 'danger')
                      }
                  );
                  return deferred.promise;
              },
  
  
              GetCategoryList: function (shopId = 0) {
                  let deferred = $q.defer();
  
                  _loadCreadentialsFromRootScope().then(
                      function success(credentials) {
                          let params = {
                              PublisherId : credentials.publisherId,
                              Password : credentials.productWebservicePassword,
                              PageSize : 5000,
                              ShopId : shopId
                          };
                          _sendRequest(params, 'GetCategoryList', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
                          $rootScope.sendAlert('Error retrieving category list', 'danger')
                      }
                  );
                  return deferred.promise;
              },
  
              SearchProducts : function (params) {
  
                  let deferred = $q.defer();
  
                  _loadCreadentialsFromRootScope().then(
                      function success(credentials) {
  
                          let data = {
                              PublisherId : credentials.publisherId,
                              Password : credentials.productWebservicePassword,
                          };
                          const requestParams = Object.assign({}, data, params);
                          _sendRequest(requestParams, 'SearchProducts', deferred.resolve, deferred.reject);
  
                      },
                      function error(error) {
                          console.error(error);
                          $rootScope.sendAlert('Error retrieving result', 'danger')
                      }
                  );
                  return deferred.promise;
                  
              },
  
              GetProducts : function (productIds) {
  
                  let deferred = $q.defer();
  
                  _loadCreadentialsFromRootScope().then(
                      function success(credentials) {
  
                          let data = {
                              PublisherId : credentials.publisherId,
                              Password : credentials.productWebservicePassword,
                              ProductIds: productIds.join(','),
                              ImageScales: 'Image180'
                          };
                          _sendRequest(data, 'GetProducts', deferred.resolve, deferred.reject);
  
                      },
                      function error() {
                          "use strict";
                          $rootScope.sendAlert('Error retrieving result', 'danger')
                      }
                  );
                  return deferred.promise;
  
              }
  
  
  
  
  
          }
      }])
  
  
  ;
  
  
  angular.module('AffilinetToolbar')
      .service('BrowserExtensionService', function(){
  
          const apis = [
              'alarms',
              'bookmarks',
              'browserAction',
              'commands',
              'contextMenus',
              'cookies',
              'downloads',
              'events',
              'extension',
              'extensionTypes',
              'history',
              'i18n',
              'idle',
              'notifications',
              'pageAction',
              'runtime',
              'storage',
              'tabs',
              'webNavigation',
              'webRequest',
              'windows',
          ];
  
  
          function Extension () {
              const _this = this;
  
              apis.forEach(function (api) {
  
                  _this[api] = null;
  
  
                  try {
                      if (chrome[api]) {
                          _this[api] = chrome[api]
                      }
                  } catch (e) {}
  
                  try {
                      if (window[api]) {
                          _this[api] = window[api]
                      }
                  } catch (e) {}
  
                  try {
                      if (browser[api]) {
                          _this[api] = browser[api]
                      }
                  } catch (e) {}
                  try {
                      _this.api = browser.extension[api]
                  } catch (e) {}
              });
  
              try {
                  if (browser && browser.runtime) {
                      this.runtime = browser.runtime
                  }
              } catch (e) {}
  
              try {
                  if (browser && browser.browserAction) {
                      this.browserAction = browser.browserAction
                  }
              } catch (e) {}
  
          }
  
  
          /**
           * @typedef {Object} FooEngine
           * @property {function(string, boolean)} start Starts the [Foo/Bar] Engine
           */
  
  
          /**
           * The Browser Extensions
           * @typedef {Object} Extension
           * @proptery {function} alarms
           * @proptery {function} bookmarks
           * @proptery {function} browserAction
           * @proptery {function} commands
           * @proptery {function} contextMenus
           * @proptery {function} cookies
           * @proptery {function} downloads
           * @proptery {function} events
           * @proptery {function} extension
           * @proptery {function} extensionTypes
           * @proptery {function} history
           * @proptery {function} i18n
           * @proptery {function} idle
           * @proptery {function} notifications
           * @proptery {function} pageAction
           * @proptery {function} runtime
           * @proptery {function} storage
           * @proptery {function} tabs
           * @proptery {function} webNavigation
           * @proptery {function} webRequest
           * @proptery {function} windows
           */
  
          return new Extension();
  
      });