{"version":3,"file":"launch.min.js","sources":["launch.js"],"sourcesContent":["'use strict';\n\n/* Build Timestamp: (2022-2-3 13:21:58) */\nangular.module('kappGlobal', [\n// GENERAL\n'ui.router', 'ngMessages', 'ngSanitize', 'ngLocale', 'angularUtils.directives.dirPagination', 'uiRouterStyles', 'angular.filter', 'angularLoad',\n// 'tandibar/ng-rollbar',\n'kappGlobal.errorHandler', 'seo', 'duScroll', 'ngDialog', 'kappGlobal.env', 'kappGlobal.version', 'kappGlobal.branch', 'kappGlobal.helpers', 'kappGlobal.softRedirect', 'kappGlobal.partnersConfig', 'kappGlobal.ajaxSpinner', 'kappGlobal.dynamicModelBinding', 'kappGlobal.directiveData', 'kappGlobal.productData', 'kappGlobal.priceData', 'kappGlobal.filters', 'kappGlobal.localization', 'kappGlobal.downloadLinksFactory', 'kappGlobal.pageInfoModule', 'kappGlobal.imageManager', 'kappGlobal.cookieService', 'kappGlobal.errorService', 'kappGlobal.pdcProxyService', 'kappGlobal.httpInterceptor', 'kappGlobal.templateVersion', 'kappGlobal.seoConfig', 'kappGlobal.notificationBarGeo', 'kappGlobal.newsLetterPopup', 'kappGlobal.ngRenderAnchorScroll', 'kappGlobal.customAnchorScroll', 'kappGlobal.marketoFormService', 'kappGlobal.injectScriptOnceService', 'kappGlobal.canonicalHref', 'kappGlobal.heightSync', 'kappGlobal.renewalSteps', 'kappGlobal.statsWidgetNumbers',\n// LAYOUT\n'kappGlobal.siteTop', 'kappGlobal.footerGlobal', 'kappGlobal.footerGlobalWrapper', 'kappGlobal.siteHeader', 'kappGlobal.currencySelector', 'kappGlobal.mobileNav', 'kappGlobal.pageHeader', 'kappGlobal.nanoBanner', 'kappGlobal.socialSharing', 'kappGlobal.homepageBanner', 'kappGlobal.videoMarkup', 'kappGlobal.b2c', 'kappGlobal.marketoForm', 'kappGlobal.premiumServices', 'kappGlobal.b2b', 'kappGlobal.vsb', 'kappGlobal.smb', 'kappGlobal.eula', 'kappGlobal.enterprise', 'kappGlobal.partners', 'kappGlobal.about', 'kappGlobal.awardsSection', 'kappGlobal.rating', 'kappGlobal.buyblocks', 'kappGlobal.compareTable', 'kappGlobal.promoComparisonChart', 'kappGlobal.customHtml', 'kappGlobal.downloadBlockFeatured', 'kappGlobal.downloadBlock', 'kappGlobal.featureSection', 'kappGlobal.featuredProducts', 'kappGlobal.hmc', 'kappGlobal.hmcUniversal', 'kappGlobal.articlesSection', 'kappGlobal.kssVerdictHeader', 'kappGlobal.lrcSteps', 'kappGlobal.lrcStepsSec', 'kappGlobal.malwareStats', 'kappGlobal.premiumServices', 'kappGlobal.productUpdates', 'kappGlobal.productUpdatesPack', 'kappGlobal.promoSection', 'kappGlobal.renewAndAbout', 'kappGlobal.resourceDocuments', 'kappGlobal.tabbedSectionBuyblock', 'kappGlobal.contentRepo', 'kappGlobal.search', 'kappGlobal.lrcService', 'kappGlobal.osDetect', 'kappGlobal.tracking', 'kappGlobal.maxymiser', 'kappGlobal.lrcVerdictPage', 'kappGlobal.lrcSerial', 'kappGlobal.arPopup', 'kappGlobal.popup', 'kappGlobal.ngSrcLocaleHack', 'kappGlobal.runOnLast', 'kappGlobal.rss', 'kappGlobal.cookiesRequirement', 'kappGlobal.siteBar', 'kappGlobal.vpnProhibitedMessage', 'kappGlobal.errorAnimation', 'kappGlobal.statsWidget', 'kappGlobal.bottomBanner', 'kappGlobal.productPromo', 'kappGlobal.featuresGrid', 'kappGlobal.productScriptsInjector', 'kappGlobal.newsLetterOptIn', 'kappGlobal.doubleBanner', 'kappGlobal.lrcDisclaimers', 'kappGlobal.trustpilotReviews', 'kappGlobal.callbackForm', 'kappGlobal.gdprReady', 'kappGlobal.customCarousel', 'kappGlobal.awardsCarousel', 'kappGlobal.accordion', 'kappGlobal.landingPage', 'kappGlobal.dl2Connector', 'kappGlobal.lrcLong']);\n'use strict';\n\n(function () {\n 'use strict';\n\n rootScopeSetup.$inject = [\"$rootScope\", \"$window\", \"$location\", \"sessionStorageService\", \"currencyManager\", \"appHelperService\", \"PARTNERS\", \"ENV\", \"windowHelperService\", \"VER\", \"BRANCH\", \"localizationService\"];\n angular.module('kappGlobal').run(rootScopeSetup);\n\n function rootScopeSetup($rootScope, $window, $location, sessionStorageService, currencyManager, appHelperService, PARTNERS, ENV, windowHelperService, VER, BRANCH, localizationService) {\n $rootScope.stateChangeInProgress = false;\n $rootScope.getCacheNames = getCacheNames();\n $rootScope.currencyLocale = getCurrencyLocale();\n $rootScope.currency = getCurrency();\n $rootScope.dir = appHelperService.getPageDirection();\n $rootScope.products = {}; // variable that holds products information\n $rootScope.search = { 'query': '', 'count': '' };\n $rootScope.currentBreadcrumbs = $rootScope.currentBreadcrumbs || '[NULL]';\n $rootScope.kaspersky = $window.kaspersky = {\n 'trackPageViewOnLoad': 0,\n 'platformName': 'NextGen',\n 'siteType': 'Default',\n 'siteLocale': appHelperService.getLocale(),\n 'pageCharset': 'UTF-8',\n 'pageName': $rootScope.currentBreadcrumbs,\n 'eCommProvider': PARTNERS[ENV.locale] || 'Digital River',\n 'verdictSite': $rootScope.verdictSite,\n 'verdictRegion': $rootScope.verdictRegion,\n 'verdictChannel': $rootScope.verdictChannel,\n 'verdictPartner': $rootScope.verdictPartner,\n 'verdictProduct': $rootScope.verdictProduct,\n 'verdictDevices': $rootScope.verdictDevices,\n 'verdictTerm': $rootScope.verdictTerm,\n 'verdictLicenseStatus': $rootScope.verdictLicenseStatus,\n 'serialNumber': '',\n 'verdictPartNumber': '',\n // verdictChannelInfo: '',\n 'verdictLicenseType': '',\n 'searchQuery': $rootScope.search.query,\n 'searchTotalResults': $rootScope.search.count,\n 'currency': '',\n 'isStaging': windowHelperService.isStaging(),\n 'version': {\n 'rev': VER.version,\n 'branch': BRANCH.name,\n 'date': BRANCH.date,\n 'sha': BRANCH.sha,\n 'api': ENV.apiServer\n },\n 'hmcTool': ''\n };\n $rootScope.prevPageName = '';\n $rootScope.buyBlockReady = buyBlockReady;\n $rootScope.populateCurrency = populateCurrency;\n $rootScope.isFavDownloadPage = (ENV.locale === 'en-in' || ENV.locale === 'en-us') && $location.path() === '/downloads/thank-you/free-antivirus-download';\n $rootScope.isResourceCenterPage = [$location.path() === '/resource-center/threats', $location.path() === '/resource-center/preemptive-safety', $location.path() === '/resource-center/infographics', $location.path() === '/resource-center/definitions'].some(function (condition) {\n return condition;\n });\n $rootScope.directStateVisit = true;\n\n initialise();\n\n function initialise() {\n populateCurrency();\n hrefLinkTags();\n }\n\n function buyBlockReady() {\n $rootScope.$broadcast('buyBlockReady');\n if (performance && 'mark' in performance) performance.mark('buyblockReady');\n }\n\n function getCurrencyLocale() {\n return sessionStorageService.get($rootScope.getCacheNames.currencyLocaleCache) && sessionStorageService.get($rootScope.getCacheNames.currencyLocaleCache).locale || appHelperService.getLocale();\n }\n\n function getCurrency() {\n return sessionStorageService.get($rootScope.getCacheNames.currencyLocaleCache) && sessionStorageService.get($rootScope.getCacheNames.currencyLocaleCache).currency;\n }\n\n function getCacheNames() {\n var hostUrl = appHelperService.getHostName(),\n currenciesCacheName = hostUrl + '-globalCurrencies',\n currencyLocaleCacheName = hostUrl + '-currencyLocale';\n return {\n 'currenciesCache': currenciesCacheName,\n 'currencyLocaleCache': currencyLocaleCacheName\n };\n }\n\n function populateCurrency(currencyItem) {\n var cachedCurrency = sessionStorageService.get($rootScope.getCacheNames.currencyLocaleCache);\n currencyItem = currencyItem || cachedCurrency || currencyManager.getDefaultCurrencyFromConfig(appHelperService.getLocale());\n localizationService.setCurrencyLocale(currencyItem);\n currencyManager.setCurrentCurrency(currencyItem);\n if (angular.isUndefined(currencyItem) || currencyItem === null) return;\n $rootScope.currencyLocale = currencyItem.locale;\n $rootScope.currency = currencyItem.currency;\n }\n\n function hrefLinkTags() {\n return $rootScope.langLinkTags = [{ 'href': 'https://usa.kaspersky.com/', 'hreflang': 'en-us' }, { 'href': 'https://www.kaspersky.com/', 'hreflang': 'x-default' }, { 'href': 'https://www.kaspersky.co.uk/', 'hreflang': 'en-gb' }, { 'href': 'https://www.kaspersky.fr/', 'hreflang': 'fr-fr' }, { 'href': 'https://www.kaspersky.de/', 'hreflang': 'de-de' }, { 'href': 'https://www.kaspersky.it/', 'hreflang': 'it-it' }, { 'href': 'https://www.kaspersky.es/', 'hreflang': 'es-es' }, { 'href': 'https://www.kaspersky.nl/', 'hreflang': 'nl-nl' }, { 'href': 'https://www.kaspersky.se/', 'hreflang': 'sv-se' }, { 'href': 'https://www.kaspersky.no/', 'hreflang': 'no-no' }, { 'href': 'https://www.kaspersky.dk/', 'hreflang': 'da-dk' }, { 'href': 'https://www.kaspersky.com.tr/', 'hreflang': 'tr-tr' }, { 'href': 'https://www.kaspersky.com.br/', 'hreflang': 'pt-br' }, { 'href': 'https://www.kaspersky.com.au/', 'hreflang': 'en-au' }, { 'href': 'https://www.kaspersky.co.in/', 'hreflang': 'en-in' }, { 'href': 'https://www.kaspersky.co.za/', 'hreflang': 'en-za' }, { 'href': 'https://www.kaspersky.com.cn/', 'hreflang': 'zh-cn' }, { 'href': 'https://www.kaspersky.co.jp/', 'hreflang': 'ja-jp' }, { 'href': 'https://www.kaspersky.ru/', 'hreflang': 'ru-ru' }, { 'href': 'https://latam.kaspersky.com/', 'hreflang': 'es-mx' }, { 'href': 'https://latam.kaspersky.com/', 'hreflang': 'es-co' }, { 'href': 'https://latam.kaspersky.com/', 'hreflang': 'es-ar' }, { 'href': 'https://latam.kaspersky.com/', 'hreflang': 'es-cl' }, { 'href': 'https://latam.kaspersky.com/', 'hreflang': 'es-pe' }, { 'href': 'https://algerie.kaspersky.com', 'hreflang': 'fr-dz' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-ae' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-eg' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-sa' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-qa' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-kw' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-bh' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-om' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-lb' }, { 'href': 'https://me-en.kaspersky.com/', 'hreflang': 'en-jo' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-ae' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-eg' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-sa' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-qa' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-kw' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-bh' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-om' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-lb' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar-jo' }, { 'href': 'https://me.kaspersky.com/', 'hreflang': 'ar' }, { 'href': 'https://www.kaspersky.fi/', 'hreflang': 'fi-fi' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-ug' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-zm' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-zw' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-rw' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-bw' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-gh' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-mw' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-ng' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-ao' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-bi' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-km' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-et' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-ls' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-mg' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-mu' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-mz' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-na' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-re' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-sc' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-sz' }, { 'href': 'https://africa.kaspersky.com/', 'hreflang': 'en-tz' }];\n }\n }\n})();\n'use strict';\n\n/*eslint angular/on-watch: 0*/\n(function () {\n 'use strict';\n\n siteSetup.$inject = [\"$rootScope\", \"$state\", \"$stateParams\", \"$window\", \"cookieService\", \"ENV\", \"windowHelperService\", \"seoHelperService\", \"appHelperService\", \"$log\", \"$location\", \"tracking\", \"directiveData\", \"$templateCache\", \"maxymiser\", \"$rootElement\", \"sessionStorageService\", \"softRedirectService\"];\n angular.module('kappGlobal').run(siteSetup);\n function siteSetup($rootScope, $state, $stateParams, $window, cookieService, ENV, windowHelperService, seoHelperService, appHelperService, $log, $location, tracking, directiveData, $templateCache, maxymiser, $rootElement, sessionStorageService, softRedirectService) {\n\n var contentLocation = void 0,\n hrefLangTagAdded = false,\n dataLayer = $window.dataLayer = $window.dataLayer || [];\n\n initialise();\n\n function initialise() {\n //Turn off SPA for France\n $rootElement.off();\n skipRedirect();\n addLocaleClass();\n softRedirectService.initialize();\n if ($window.templates) {\n for (var item in $window.templates) {\n $templateCache.put(decodeURIComponent(item), $window.templates[item]);\n }\n }\n runTracking();\n }\n\n $rootScope.$on('$locationChangeStart', handleLocationChangeStart);\n $rootScope.$on('$stateChangeStart', handleStateChangeStart);\n $rootScope.$on('$stateChangeSuccess', handleStateChangeSuccess);\n\n function runTracking() {\n tracking.setTrackingCookie();\n tracking.setAdobeCampaignValues();\n tracking.setAdmitadRefererParams();\n }\n\n function handleLocationChangeStart(event, newUrl, oldUrl) {\n $rootScope.oldUrl = oldUrl;\n $rootScope.newUrl = newUrl;\n\n if (performance && 'mark' in performance) performance.mark('stateChangeStart');\n // Checking that spa navigation has occurred\n if (oldUrl !== newUrl) {\n $rootScope.directStateVisit = false;\n }\n }\n\n function handleStateChangeStart(ev, to, toParams) {\n checkPageSetting(ev, to);\n if (ENV.locale === 'ja-jp') handleProxiesJP(ev, to, toParams);\n\n // A requirement to clean up window.kaspersky on every state change\n $rootScope.stateData = false;\n }\n\n /**\n * JP still has some proxies that are incompatible with SPA flow at the moment.\n * Also the whole B2C section is blocked, so we need to intercept it.\n */\n function handleProxiesJP(ev, to, toParams) {\n var url = void 0,\n allowedUrls = ['product-kfa', 'kfa-thank-you', 'product-kss', 'b2c-resource-center'];\n\n if (to.name === 'press-center.details' && toParams.path === 'whitepaper') {\n url = 'https://www.kaspersky.co.jp/about/press-center/whitepaper';\n }\n\n if (to.parent === 'home-security-section' && allowedUrls.indexOf(to.name) === -1 && to.name.indexOf('b2c-resource-center') === -1) {\n url = 'https://home.kaspersky.co.jp/store/kasperjp/DisplayHomePage/?icid=jp_ng-tophb_pro_ona_oth__onl_b2c__banner_______';\n }\n\n if (url) {\n ev.preventDefault();\n windowHelperService.navigateUserTo(url);\n }\n }\n\n function handleStateChangeSuccess(ev, to, toParams, from) {\n $window.scrollTo(0, 0);\n setContentLocation();\n\n $rootScope.stateData = undefined;\n $rootScope.kaspersky.regKey = undefined;\n $rootScope.kaspersky.pageName = undefined;\n $rootScope.kaspersky.verdictSite = undefined;\n $rootScope.kaspersky.verdictRegion = undefined;\n $rootScope.kaspersky.verdictChannel = undefined;\n $rootScope.kaspersky.verdictPartner = undefined;\n $rootScope.kaspersky.verdictProduct = undefined;\n $rootScope.kaspersky.verdictDevices = undefined;\n $rootScope.kaspersky.verdictTerm = undefined;\n $rootScope.kaspersky.verdictLicenseProduct = undefined;\n $rootScope.kaspersky.verdictLicenseStatus = undefined;\n $rootScope.kaspersky.serialNumber = undefined;\n $rootScope.kaspersky.verdictPartNumber = undefined;\n $rootScope.kaspersky.verdictLicenseType = undefined;\n $rootScope.kaspersky.providerToken = undefined;\n $rootScope.kaspersky.productName = undefined;\n $rootScope.buyblocksOnPage = false;\n\n detectBuyBlockPresence();\n\n var referrerValue = $window.document.referrer;\n /**\n * This is needed to persist referrer on LRC.\n * Due to GWP-34912 we need to reload LRC page when accessed from another SPA state,\n * losing information about the referrer. For this reason, it's being saved in\n * sessionStorage, and removed once used. This function runs twice on LRC,\n * so it needs to be removed only the 2nd time.\n */\n var sessionReferrer = sessionStorageService.get('referrer');\n if (sessionReferrer) {\n if (referrerValue.indexOf('renewal-center/home') > -1) {\n sessionStorageService.remove('referrer');\n }\n referrerValue = sessionReferrer;\n }\n $rootScope.kaspersky.referrer = from.params && from.params.canonical ? '' + windowHelperService.getDomainName() + from.params.canonical : referrerValue;\n\n directiveData.get('/content/' + ENV.locale + contentLocation + '/meta.json').then(handleMetaSuccess).catch(handleMetaError);\n\n if ($state.current.name === 'homepage') addHrefLangTags();\n\n var viewContentListener = $rootScope.$on('$viewContentLoaded', function () {\n viewContentListener();\n\n angular.getTestability(document).whenStable(function () {\n setPerformanceMarkers();\n $rootScope.$emit('ngRender');\n maxymiser.fireMMEvent();\n\n var isLRC = appHelperService.assertStateByName(['lrc-verdict', 'lrc-serial']);\n\n if ($rootScope.pageDataReady && $rootScope.kaspersky.prevPageName !== $rootScope.kaspersky.pageName && !isLRC) {\n $rootScope.kaspersky.prevPageName = angular.copy($rootScope.kaspersky.pageName);\n tracking.trackPageView($rootScope.kaspersky.pageName);\n }\n\n sendEventToGTM();\n\n if ($state.current.name !== 'homepage' && hrefLangTagAdded) removeHrefLangTags();\n\n $log.log('data_complete');\n });\n });\n }\n\n /**\n * Detects presence of buyblocks for currency switcher, as buyblocks can appear first\n */\n function detectBuyBlockPresence() {\n var buyblockListener = $rootScope.$on('buyBlockReady', function () {\n $rootScope.buyblocksOnPage = true;\n buyblockListener();\n });\n }\n\n function setPerformanceMarkers() {\n var RProfiler = $window.RProfiler,\n timingObj = $window.timingObj;\n if (performance && 'mark' in performance) {\n performance.mark('ngRender');\n\n var frontendNGReadyTimeVal = performance.getEntriesByName('ngRender')[0].startTime,\n firstPaintTimeVal = timingObj && timingObj.firstPaintTime,\n totalLoadTimeVal = performance.timing.loadEventEnd - performance.timing.fetchStart;\n if (RProfiler) {\n RProfiler.addInfo('indicator', 'i15631', frontendNGReadyTimeVal);\n RProfiler.addInfo('indicator', 'i55228', firstPaintTimeVal);\n RProfiler.addInfo('indicator', 'i65010', totalLoadTimeVal);\n }\n }\n }\n\n function sendEventToGTM() {\n dataLayer.push({\n 'event': 'dataReady',\n 'page': {\n 'path': $window.location.href,\n 'title': $rootScope.kaspersky.pageName\n }\n });\n }\n\n function setupMeta(description, title, keywords, internalSearch) {\n if ($state.current.name !== '404' && $state.current.name !== 'b2c-resource-center.category-details' && !$window.customFlag) windowHelperService.setWindowTitle(title || $state.current.data && $state.current.data.title);\n\n // Reduce an array of breadcrumbs into a string\n $rootScope.currentBreadcrumbs = seoHelperService.parseBreadcrumbs($rootScope.breadcrumbs);\n\n if ($rootScope.currentBreadcrumbs.indexOf('Resource Center') > -1) setResourceTitle();\n\n kaspersky.pageName = $location.search().pageName || $rootScope.currentBreadcrumbs || $window.kaspersky.pageName;\n kaspersky.productName = $stateParams.productName;\n /*Metadata settings*/\n if (!isLRCPage()) {\n seoHelperService.setMeta({ 'description': description || 'Kaspersky' });\n if (!description) kaspersky.pageName = $rootScope.currentBreadcrumbs;\n if (keywords) seoHelperService.setMeta({ 'keywords': keywords });\n } else {\n seoHelperService.removeMeta('description');\n seoHelperService.removeMeta('keywords');\n }\n $rootScope.kaspersky.prevPageName = '';\n $rootScope.$emit('pageDataReady'); // either way fire an event that page data is in place\n $rootScope.pageDataReady = true;\n $rootScope.internalSearch = internalSearch || 'index';\n setMetaTags();\n }\n\n function setResourceTitle() {\n var resourceTitle = _.startCase(windowHelperService.getPathName().split('/').pop().replace(/-/g, ' '));\n $rootScope.currentBreadcrumbs = $rootScope.currentBreadcrumbs + ' > ' + resourceTitle;\n }\n\n function handleMetaSuccess(response) {\n var res = response && response.data ? response.data.fields : {};\n if (res.linkedComponents && res.linkedComponents.length) $rootScope.stateData = res.linkedComponents; //empty linkedComponents array check, after - value assignment\n //angular.extend($state.params, {linkedComponents: res.linkedComponents})\n\n if (!res) {\n $rootScope.breadcrumbs = seoHelperService.getDefaultBreadcrumbs(null, contentLocation);\n } else {\n $rootScope.breadcrumbs = res.breadcrumbs;\n }\n setupMeta(res.description, res.title, res.keywords, res.internalSearch);\n }\n\n function handleMetaError() {\n $rootScope.breadcrumbs = seoHelperService.getDefaultBreadcrumbs(true, contentLocation);\n setupMeta();\n }\n\n /* Section meta for search */\n function setMetaTags() {\n var searchAreaContent = 'other';\n if ($state.includes('home-security-section')) {\n searchAreaContent = 'home-security';\n } else if ($state.includes('about-section')) {\n searchAreaContent = 'about-section';\n } else if ($state.includes('enterprise')) {\n searchAreaContent = 'enterprise';\n } else if ($state.includes('vsb')) {\n searchAreaContent = 'vsb';\n } else if ($state.includes('smb')) {\n searchAreaContent = 'smb';\n }\n\n var metaObj = {\n 'DocId': appHelperService.getLocationPath(),\n 'DocType': 'Marketing-B2B',\n 'PublishDate': new Date().toISOString().slice(0, 10),\n 'PubLang': ENV.locale.match(/\\w+/g)[0],\n 'PubCountry': ENV.locale.match(/\\w+/g)[1],\n 'Breadcrumb': $rootScope.breadcrumbs.reduce(function (prev, curr, index) {\n return !index ? prev + curr.title : prev + '/' + curr.title;\n }, ''),\n 'SearchAreaNavigator': searchAreaContent,\n 'internalsearch': $rootScope.internalSearch\n };\n\n seoHelperService.setMeta(metaObj);\n }\n\n function setContentLocation() {\n return contentLocation = seoHelperService.getContentLocation();\n }\n\n function addLocaleClass() {\n angular.element(document).find('body').addClass('locale--' + ENV.locale);\n }\n\n function isLRCPage() {\n var url = windowHelperService.getPathName().toLowerCase();\n return url.indexOf('/lrc') !== -1;\n }\n\n // Removed as part of performance optimization\n // function activateRandomImagesCaching() {\n // return imageManagerService.activateRandomImagesCache();\n // }\n\n function skipRedirect() {\n if (appHelperService.searchParam('ignoreredirects')) cookieService.set('ignoreredirects', true, 7);\n }\n\n function checkPageSetting(ev, to) {\n var loc = ['it-it'];\n\n if (loc.indexOf(ENV.locale) > -1 && $window.pageSettings && $window.pageSettings.match(/error404/g)) {\n $window.pageSettings = '/content/';\n $state.go('404', {}, { 'location': false });\n ev.preventDefault();\n }\n\n if ($window.pageSettings && $window.pageSettings.match(/error500/g) && to.name !== '500') {\n $window.pageSettings = '/content/';\n $state.go('500', {}, { 'location': false });\n ev.preventDefault();\n }\n }\n\n function addHrefLangTags() {\n angular.forEach($rootScope.langLinkTags, function (tag) {\n var hrefLangTag = $window.document.createElement('link');\n hrefLangTag.rel = 'alternate';\n hrefLangTag.href = tag.href;\n hrefLangTag.hreflang = tag.hreflang;\n $window.document.head.appendChild(hrefLangTag);\n });\n\n hrefLangTagAdded = true;\n }\n\n function removeHrefLangTags() {\n var tags = $window.document.querySelectorAll('link[rel=\"alternate\"]');\n\n for (var i = 0; i < tags.length; i++) {\n tags[i].parentNode.removeChild(tags[i]);\n }\n\n hrefLangTagAdded = false;\n }\n }\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n angular.module('kappGlobal').config([\"$urlMatcherFactoryProvider\", \"$locationProvider\", \"$urlRouterProvider\", \"$stateProvider\", function ($urlMatcherFactoryProvider, $locationProvider, $urlRouterProvider, $stateProvider) {\n $urlMatcherFactoryProvider.caseInsensitive(true);\n $urlMatcherFactoryProvider.strictMode(false);\n $locationProvider.html5Mode({ 'enabled': true, 'requireBase': true }).hashPrefix('!');\n $urlRouterProvider.otherwise(function ($injector) {\n var $window = $injector.get('$window'),\n $rootElement = $injector.get('$rootElement'),\n $state = $injector.get('$state');\n if ($window.error404) {\n $state.go('404', {}, { 'location': false });\n return;\n }\n if ($window.error500) {\n $state.go('500', {}, { 'location': false });\n return;\n }\n if (!$window.customFlag) {\n $window.location.href = $window.location;\n } else {\n // In case if page is custom turn off SPA mode\n $rootElement.off();\n }\n });\n $stateProvider.decorator('views', function (state) {\n var views = {};\n angular.forEach(angular.isDefined(state.views) ? state.views : { '': state }, function (view, name) {\n if (name.indexOf('@') < 0) name += '@' + state.parent.name;\n view.resolveAs = view.resolveAs || state.resolveAs || '$resolve';\n views[name] = view;\n });\n return views;\n });\n $stateProvider.decorator('data', function (state) {\n return state.data;\n });\n }]);\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n angular.module('kappGlobal').config([\"$stateProvider\", function ($stateProvider) {\n\n /** Callback to be called on scroll for handling the sticky header */\n function handleStickyHeaderOnScroll($window) {\n var scroll = $($window).scrollTop(),\n $body = $('body'),\n siteHeaderAvail = $('.site-header').length,\n totalHeight = $('site-header').attr('site-top-height'),\n stickyClass = 'sticky-header-on';\n\n var clearFocus = function clearFocus() {\n if ($window.document.activeElement && $window.document.activeElement.blur) {\n $window.document.activeElement.blur();\n }\n };\n\n if (!totalHeight && $('page-header .nano-banner').length) {\n totalHeight = $('page-header .nano-banner').outerHeight(true);\n stickyClass = 'sticky-lrc-nano';\n }\n\n if (scroll >= totalHeight && siteHeaderAvail) {\n $body.addClass(stickyClass);\n } else {\n if ($body.hasClass(stickyClass)) {\n clearFocus();\n }\n\n $body.removeClass(stickyClass);\n }\n }\n\n $stateProvider.state('master', {\n 'abstract': true,\n 'templateUrl': '/resources/template/html/pages/master/index.html'\n }).state('master-header', {\n 'abstract': true,\n 'template': '
This product is currently unavailable in your region. Please contact us for further details.
';\n }\n\n /**\n * Handler for when an option is selected in another buyblock with the same product.\n * @param {Object} event\n * @param {Object} optionObj The object of the active option\n */\n function handleProductSelected(event, optionObj) {\n if (optionObj.scope === ctrl.$id) return;\n ctrl.autorenew = optionObj.autoRenewal;\n ctrl.hasBundle = optionObj.hasBundle;\n ctrl.isBundleActive = optionObj.isBundleActive;\n ctrl.priceList = optionObj.priceList;\n setSelectedOption(optionObj.optionID);\n if (ctrl.selectedID !== optionObj.optionID) updateSelectedID(optionObj.optionID);\n }\n\n /**\n * Updates the radio checkbox with the active option\n * @param {Number} id The ID of the active option\n */\n function updateSelectedID(id) {\n if (angular.isUndefined(id)) return;\n ctrl.selectedID = id;\n }\n\n ctrl.$onInit = activate;\n\n function activate() {\n ctrl.defaults = {\n 'term': ctrl.selectedTerm,\n 'pack': ctrl.selectedPack\n };\n ctrl.locale = ctrl.locale || ENV.locale;\n var productData = getProductData(),\n buyBlockSetup = buyblockSetup(),\n promises = [productData, buyBlockSetup];\n $q.all(promises).then(function () {\n var isB2B = $state.current.parent ? Boolean($state.current.parent.match(/(smb|vsb)/ig)) : false;\n checkDisclaimer(isB2B);\n setUnavailableText();\n });\n $scope.$on('$destroy', handleScopeDestroyed);\n }\n\n function hasTrialButton() {\n return buyblockService.hasTrialButton(ctrl.product) && !appHelperService.isTrue(ctrl.hideTrialButton);\n }\n\n function addSalesTrackingParameters(event) {\n if (event) event.preventDefault();\n ctrl.priceList.forEach(function (item) {\n if (item.id === ctrl.selectedID) {\n ctrl.masterPartner = item.master_partner;\n }\n });\n ctrl.buyLink = tracking.onBuyButtonClick(ctrl.buyLink, ctrl.masterPartner);\n sendToBuyUrl();\n }\n\n function getPriceList(skipEstore) {\n if (!ctrl.productName || ctrl.stateName === 'home-downloads') return;\n if (!skipEstore) setEStore();\n\n return priceData.getPrice(ctrl.productName, ctrl.purchaseType, ctrl.newEstore || '').then(handlePriceSuccess, handlePriceError);\n }\n\n function registerOnce(checked) {\n buyblockService.registerOnce(checked, ctrl.isChecked);\n }\n\n function radioFilter(input) {\n if (!input || input.autorenew !== ctrl.autorenew) {\n return;\n }\n if (ctrl.itemOffersGroupLimit && input.outOfGroupVisibleLimit) {\n return;\n }\n if (ctrl.priceList.length <= ctrl.limitTo || ctrl.showEvenDevices === 'Yes') {\n return true;\n }\n\n var isUSLocale = ENV.locale === 'en-us';\n\n if (isUSLocale && $state.includes('lrc-verdict') && ctrl.defaults.pack) {\n if (parseInt(input.pack) % ctrl.defaults.pack === 0) {\n return true;\n }\n } else if (isUSLocale && ctrl.productShortName === 'ktsmd') {\n if (parseInt(input.pack) % 5 === 0) {\n return true;\n }\n } else {\n if (parseInt(input.pack) % 2 !== 0) {\n return true;\n }\n }\n }\n\n function getPromoline() {\n // GWP-31338 home-security page has a specific promoline\n var isHomeSec = ctrl.stateName === 'home-security';\n return ctrl.buyblockData ? ctrl.buyblockData[isHomeSec ? 'promolineHomeSecurity' : 'promoline'] : ctrl.promoline;\n }\n\n function showARPopup() {\n buyblockService.showARPopup(ctrl.showAR);\n }\n\n function getBuyButtonText() {\n var isExtendedButtonText = angular.isString(ctrl.updateButtonText) ? ctrl.updateButtonText !== 'false' : ctrl.updateButtonText;\n if (ENV.locale === 'ru-ru' && isExtendedButtonText && $rootScope.kaspersky.verdictSite !== 'ATT') {\n ctrl.updatedBuyButtonText = ctrl.buyblockData && ctrl.buyblockData.extendButtonText;\n } else if (ENV.locale === 'en-us' && ctrl.upgradeButton) {\n ctrl.updatedBuyButtonText = ctrl.buyblockData && ctrl.buyblockData.upgradeText;\n } else if (ENV.locale === 'en-us' && !ctrl.upgradeButton && ctrl.purchaseType !== 'Purchase') {\n ctrl.updatedBuyButtonText = ctrl.buyblockData && ctrl.buyblockData.renewText;\n } else {\n ctrl.updatedBuyButtonText = ctrl.buyButtonText || ctrl.buyblockData ? ctrl.buyblockData.buyText : '';\n }\n }\n\n function sendToBuyUrl() {\n buyblockService.sendToBuyUrl(ctrl.buyLink, ctrl.purchaseType);\n }\n\n function hidePack() {\n if (ctrl.locale === 'ru-ru' && (ctrl.productShortName === 'ksc-family' || ctrl.product && ctrl.product.shortName === 'ksc-family')) {\n ctrl.hidePack = true;\n }\n }\n\n // Private methods\n function buyblockSetup() {\n return directiveData.getLocal('buyblock/buyblock-texts').then(function (response) {\n ctrl.buyblockData = response.data.fields;\n getBuyButtonText();\n });\n }\n\n function doScreenshotsSeo() {\n var newScreenshotsArr = [],\n seoAltTexts = SEO[ENV.locale] || SEO['default'],\n screenshots = ctrl.productInfo ? ctrl.productInfo.screenshots : [];\n for (var i = 0, len = screenshots.length; i < len; i++) {\n var obj = {},\n ss = screenshots[i];\n if (angular.isString(ss)) {\n var name = ss.split('/').pop().split('.')[0];\n obj.path = ss;\n obj.name = name;\n obj.altText = seoAltTexts && seoAltTexts[name] ? seoAltTexts[name] : name;\n newScreenshotsArr.push(obj);\n }\n }\n ctrl.productInfo.screenshots = newScreenshotsArr; // replace array of string, with array of object\n }\n\n function checkDisclaimer(isB2B) {\n var b2bDisclaimer = ctrl.buyblockData.disclaimer || ctrl.buyblockData.smbDisclaimer,\n hideDisclaimer = appHelperService.isTrue(ctrl.product && ctrl.product.hideDisclaimer) || ctrl.hidePriceDisclaimer === 'true';\n ctrl.disclaimerObj = hideDisclaimer && $rootScope.kaspersky.verdictSite !== 'ATT' ? false : ctrl.product && ctrl.product.bbDisclaimer ? { 'disclaimer': ctrl.product.bbDisclaimer, 'asterisk': '*' } : buyblockService.checkDisclaimer(isB2B, ctrl.buyblockData.b2cDisclaimer, b2bDisclaimer);\n }\n\n function resetPricingObjectsToDefault() {\n ctrl.priceList = [];\n ctrl.selectedOption = {\n 'price': ''\n };\n ctrl.selectedPack = ctrl.defaults.pack;\n ctrl.selectedTerm = ctrl.defaults.term;\n ctrl.buyLink = 'no data received yet';\n ctrl.autorenew = ctrl.isChecked === 'true';\n ctrl.productPricesExist = true;\n ctrl.isBBReady = false;\n }\n\n function handleCurrencyChange() {\n if (ctrl.currency === $rootScope.currency) return;\n ctrl.showAR = SETUP.showAR;\n ctrl.isBBReady = false;\n ctrl.currency = $rootScope.currency;\n if (ctrl.product.bbSettings.purchaseType !== 'Free' && !ctrl.isProductBlocked) getPriceList();\n }\n\n function handleBundleChange() {\n if (!ctrl.isBundleActive) ctrl.productNameBackup = ctrl.productName;\n\n ctrl.isBundleActive = !ctrl.isBundleActive;\n ctrl.productName = ctrl.isBundleActive ? ctrl.product.bundleProductName : ctrl.productNameBackup;\n ctrl.previousBundleOption = ctrl.selectedOption;\n $scope.$emit('buyblockBundleChange', {\n 'isActive': ctrl.isBundleActive,\n 'productName': ctrl.productShortName\n });\n\n getPriceList();\n applyOption();\n }\n\n function handleScopeDestroyed() {\n if (angular.isFunction(productSelected)) productSelected();\n currencyChange();\n arPopupCheck();\n $rootScope.mainProductAlreadySelected = false;\n otaButtonAvailable();\n }\n\n /**\n * ctrl.product.bbSettings.eStore pulls data from Tridion component, ctrl.estore takes it from parameter passed in\n * this conditional is probably used to target buyblocks outside of LRC, added lrc state detection to make it work for ATT verdicts - AFedotov\n */\n\n function setEStore() {\n if (ctrl.estore || isLRC) {\n ctrl.newEstore = ctrl.estore;\n if (!currencyManager.pl2Enabled()) setOverrideEstoreID();\n return;\n }\n\n ctrl.newEstore = ctrl.product && ctrl.product.bbSettings ? ctrl.product.bbSettings.eStore : '';\n }\n\n function setOverrideEstoreID() {\n priceData.info(ctrl.productName, ctrl.purchaseType, ctrl.newEstore, isLRC).then(function (response) {\n ctrl.overrideEstoreID = response.data && response.data.id;\n if (!ctrl.overrideEstoreID) handleEstoreOverrideError();\n }).catch(function (rejection) {\n errorService.warn(rejection);\n handleEstoreOverrideError();\n });\n }\n\n function handleEstoreOverrideError() {\n directiveData.getPartnerByName(ctrl.newEstore).then(function (response) {\n ctrl.partnerObj = response;\n if (ctrl.partnerObj) ctrl.buyblockData.lrcPartnerUnavailable = ctrl.buyblockData.lrcPartnerUnavailable && ctrl.buyblockData.lrcPartnerUnavailable.replace('#', ctrl.partnerObj.href);\n });\n }\n\n /**\n * Get the default option either from Product settings or template override\n */\n function getDefaultOption() {\n var option = void 0,\n hasTemplateOverride = void 0,\n hasProductOverride = void 0;\n hasTemplateOverride = ctrl.defaults.term || ctrl.defaults.pack;\n hasProductOverride = ctrl.product.bbSettings && ctrl.product.bbSettings.selectedTerm && ctrl.product.bbSettings.selectedPack;\n\n if (hasTemplateOverride) option = findOptionByKeys(ctrl.defaults.term, ctrl.defaults.pack);\n if (ctrl.previousBundleOption) option = findOptionByKeys(ctrl.previousBundleOption.term, ctrl.previousBundleOption.pack);\n if (!option && hasProductOverride) option = findOptionByKeys(ctrl.product.bbSettings.selectedTerm, ctrl.product.bbSettings.selectedPack);\n if (!option) option = findFirstAvailableOption();\n return option;\n }\n\n function findFirstAvailableOption() {\n var filteredPriceList = ctrl.priceList.filter(radioFilter),\n options = ctrl.showTerm && ctrl.monthlyAvailable ? ['subscription', ctrl.showTerm] : ['autorenew', ctrl.autorenew];\n return _.find(filteredPriceList, options);\n }\n\n /**\n * Get the default options either from Product settings or template override\n */\n function findOptionByKeys(term, pack) {\n var isSelectedTerm = void 0,\n filteredPriceList = ctrl.priceList.filter(radioFilter);\n\n if (!filteredPriceList.length) filteredPriceList = ctrl.priceList;\n\n term = term || ctrl.selectedOption.term || filteredPriceList[0].term;\n pack = pack || ctrl.selectedOption.pack || filteredPriceList[0].pack;\n return _.find(filteredPriceList, function (obj) {\n isSelectedTerm = !term || term.length > 2 ? obj.term === term : parseInt(obj.term) === parseInt(term);\n return isSelectedTerm && parseInt(obj.pack) === parseInt(pack) && obj.autorenew === ctrl.autorenew;\n });\n }\n\n /**\n * Get the default options either from Product settings or template override\n */\n function findOptionById() {\n var id = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ctrl.selectedID;\n\n return _.find(ctrl.priceList, ['id', parseInt(id)]);\n }\n\n function setupPriceList(listOrderBy) {\n var productHasAutoRenewal = ctrl.priceList.some(function (item) {\n return ctrl.priceList[0].autorenew !== item.autorenew;\n });\n\n // if we have only one autorenew and it doesn't match the default one\n\n if (ctrl.disableAutoRenew === 'true') {\n ctrl.showAR = false;\n ctrl.autorenew = false;\n } else {\n if (!productHasAutoRenewal) {\n ctrl.showAR = false;\n ctrl.autorenew = ctrl.priceList[0].autorenew;\n }\n }\n\n // apply default for RU if not overridden\n if (ENV.locale === 'ru-ru') listOrderBy = !listOrderBy ? orderBy.TERM_PACK : listOrderBy;\n\n var orderByFunc = getOrderByFunction(listOrderBy) || getOrderByFunction(orderBy.PACK_TERM);\n ctrl.priceList.sort(orderByFunc);\n\n if (ctrl.itemOffersGroupLimit) {\n _.values(_.groupBy(ctrl.priceList, 'autorenew')).forEach(function (arGroup) {\n var itemsByTerm = _.values(_.groupBy(arGroup, 'term'));\n\n if (itemsByTerm.length > 1) {\n itemsByTerm.forEach(function (groupedList) {\n groupedList.slice(ctrl.itemOffersGroupLimit).forEach(function (item) {\n return item.outOfGroupVisibleLimit = true;\n });\n });\n }\n });\n }\n\n applyOption(true);\n\n if (isProductKSC()) checkMonthlyOptions();\n\n // Emit Buyblock ready event to currency selector\n $rootScope.buyBlockReady();\n getFirstProductMaxAndMinPrices(ctrl.priceList);\n ctrl.showMoreDevices = hasMoreDevices();\n }\n\n /**\n * Returns the matching sort function or undefined if not matched to one of orderBy constants\n * @param key\n * @return {function|undefined}\n */\n function getOrderByFunction(key) {\n switch (key) {\n case orderBy.TERM:\n return buildOrderByFunction('term');\n case orderBy.TERM_PACK:\n return buildOrderByFunction('term', buildOrderByFunction('pack'));\n case orderBy.PACK:\n return buildOrderByFunction('pack');\n case orderBy.PACK_TERM:\n return buildOrderByFunction('pack', buildOrderByFunction('term'));\n }\n }\n\n /**\n * Builds and returns an int based ordering function for prop\n * @param {String} prop name of prop to sort on\n * @param {function?} equalSort equal case callback\n * @return {function(a, b): number}\n */\n function buildOrderByFunction(prop, equalSort) {\n return function (a, b) {\n var propA = parseInt(a[prop]),\n propB = parseInt(b[prop]);\n return equalSort && propA === propB ? equalSort(a, b) : propA - propB;\n };\n }\n\n function checkMonthlyOptions() {\n // Conditional for non-string term with split values, as the backend is being switched\n var splitPackageDataEnabled = ctrl.priceList.some(function (item) {\n return !!(item.termDuration && item.termType && item.termName);\n });\n\n ctrl.monthlyAvailable = false;\n\n if (ctrl.priceList && !splitPackageDataEnabled) {\n ctrl.priceList.forEach(function (item) {\n if (parseFloat(item.term) % 1 !== 0) {\n ctrl.monthlyAvailable = true;\n var dec = Math.round(item.term.match(/\\d+(.|,)\\d+/g) * 12);\n item.term = item.term.replace(/\\d+(.|,)\\d+/g, dec);\n item.subscription = 'Month';\n } else item.subscription = 'Year';\n });\n } else if (ctrl.priceList && splitPackageDataEnabled) {\n ctrl.priceList.forEach(function (item) {\n if (item.termType === 'M') {\n ctrl.monthlyAvailable = true;\n item.term = item.termDuration + ' ' + item.termName;\n item.subscription = 'Month';\n } else if (item.termType === 'Y') {\n item.term = item.termDuration + ' ' + item.termName;\n item.subscription = 'Year';\n }\n });\n }\n // Set default monthly toggle value\n if (ctrl.monthlyAvailable) {\n ctrl.showTerm = ctrl.product.bbSettings.selectedTab || ctrl.selectedOption.subscription;\n }\n switchArCheckbox();\n }\n\n function isActive(name) {\n return ctrl.showTerm === name;\n }\n\n function switchTerm(showTerm) {\n ctrl.showTerm = showTerm;\n switchArCheckbox();\n }\n\n function updateOptions() {\n ctrl.selectedOption = findFirstAvailableOption();\n updateSelectedID(ctrl.selectedOption.id);\n $scope.$emit('productSelected-' + ctrl.productShortName, {\n 'scope': ctrl.$id,\n 'optionID': ctrl.selectedOption.id,\n 'autoRenewal': ctrl.autorenew,\n 'priceList': ctrl.priceList,\n 'hasBundle': ctrl.hasBundle,\n 'isBundleActive': ctrl.isBundleActive\n });\n getBuyLink();\n }\n\n function switchArCheckbox() {\n ctrl.priceList.some(function (item) {\n if (item.subscription && ctrl.showTerm) {\n if (ctrl.showTerm === 'Month') {\n showHideTabs('true', false);\n } else {\n showHideTabs('true', false);\n }\n ctrl.autorenew = item.autorenew;\n updateOptions();\n return true;\n } else {\n showHideTabs('true', false);\n return true;\n }\n });\n }\n\n function showHideTabs(showAR, showOnlyAR) {\n if (ctrl.monthlyAvailable) {\n ctrl.showAR = showAR;\n ctrl.showOnlyAR = showOnlyAR;\n } else ctrl.showTerm = 'Year';\n }\n\n /**\n * Override buyblock settings with ones from Tridion. Needs to run even if they're set to false.\n */\n function overrideBuyblockSettings() {\n // if we have overridden values come from Tridion - needs to run even if false\n var bbSettings = ctrl.product.bbSettings;\n\n ['isChecked', 'showAR', 'itemOffersOrderBy', 'itemOffersGroupLimit'].filter(function (key) {\n return angular.isDefined(bbSettings[key]);\n }).forEach(function (key) {\n return ctrl[key] = bbSettings[key];\n });\n\n if (angular.isDefined(bbSettings.hideArCheckbox) && bbSettings.hideArCheckbox[0]) ctrl.hideArCheckbox = bbSettings.hideArCheckbox[0];\n }\n\n /**\n * Preparation for pricelist.\n * @param {Object} response The pricelist object\n */\n function handlePriceSuccess(response) {\n overrideBuyblockSettings();\n resetPricingObjectsToDefault();\n\n ctrl.priceList = response.data;\n\n if (!angular.isArray(ctrl.priceList) || !ctrl.priceList.length) {\n if (ctrl.newEstore) {\n errorService.warn('No matches found on ' + ctrl.newEstore + ' for ' + ctrl.productName + '; retrying with default estore');\n delete ctrl.newEstore;\n return getPriceList(true);\n } else {\n errorService.warn('API not available');\n ctrl.productPricesExist = false;\n return false;\n }\n }\n\n if (ctrl.getBuyblockPriceList) {\n ctrl.getBuyblockPriceList({ 'priceList': ctrl.priceList });\n }\n\n setupPriceList(ctrl.product.bbSettings.itemOffersOrderBy);\n }\n\n /**\n * Handles product not found, prices or service not available\n * @param {String} response\n */\n function handlePriceError(response) {\n ctrl.productPricesExist = false;\n ctrl.isBBReady = true;\n errorService.warn(response);\n return false;\n }\n\n function setProductPageLink() {\n ctrl.product.prodPageLink = ctrl.hideProductLinks ? '' : ctrl.product.prodPageLink;\n }\n\n /**\n * Sets the number of options to show in radio list to 6.\n * For US LRC verdicts this is meant to be 3 (except KSC)\n */\n function setOptionsLimit() {\n ctrl.limitTo = ctrl.overrideLimitTo || (ctrl.locale === 'en-us' && $state.includes('lrc-verdict') && !isProductKSC() ? 3 : 6);\n }\n\n /**\n * Get the product data when we already have the product short name available\n */\n function getProductByName() {\n return productData.getb2c(ctrl.productShortName).then(function (response) {\n handleProductData(response.data.fields);\n });\n }\n\n /**\n * Checks whether we need to listen for the product data to come from another component\n */\n function getProductData() {\n if (ctrl.productShortName) return getProductByName();\n\n var productInfo = $scope.$watch('$ctrl.productInfo', function (newV) {\n if (!newV) return;\n handleProductData(ctrl.productInfo);\n productInfo();\n });\n }\n\n function isProductKSC() {\n return ctrl.productShortName.indexOf('ksc') > -1 || ctrl.productShortName.indexOf('ksec') > -1;\n }\n /**\n * Set up product data bindings\n * @param {Object} product Product data\n */\n function handleProductData(product) {\n ctrl.product = ctrl.productInfo = product;\n if (!ctrl.productShortName) ctrl.productShortName = product.shortName;\n ctrl.productName = product.title;\n ctrl.showEvenDevices = product.bbSettings.showEvenDevices;\n ctrl.overrideLimitTo = ctrl.setLimitTo || product.bbSettings.limitTo;\n ctrl.hasBundle = $state.params.bundle && appHelperService.isTrue(ctrl.product.enableBundle) && ctrl.product.bundleProductName;\n\n if (!ctrl.disablePriceSync) {\n productSelected = $rootScope.$on('productSelected-' + ctrl.productShortName, handleProductSelected);\n }\n\n if (ctrl.locale === 'en\\-global' && isProductKSC()) blockProhibitedCountries();\n if (ctrl.product.bbSettings.purchaseType !== 'Free' && !ctrl.isProductBlocked) getPriceList();\n doScreenshotsSeo();\n setProductPageLink();\n setOptionsLimit();\n hidePack();\n }\n\n function blockProhibitedCountries() {\n if (geoLocationManager.isKscProhibitedCountry()) {\n ctrl.isProductBlocked = true;\n handlePriceError('Product not available in this country');\n }\n }\n\n /**\n * Whether to show the button 'Show more devices' or not\n * @return {Boolean}\n */\n function hasMoreDevices() {\n var checkAutoRenewal = _.countBy(ctrl.priceList, 'autorenew'),\n limit = ctrl.itemOffersGroupLimit || ctrl.limitTo;\n // return checkAutoRenewal.false ? checkAutoRenewal.false > limit : checkAutoRenewal.true > limit;\n return checkAutoRenewal[ctrl.autorenew] > limit;\n }\n\n /**\n * Set an option from the radio checkbox or from another buyblock. If ID is present, it's internal.\n * @param {String|Number} id The ID of the option to select\n */\n function setSelectedOption(id) {\n if (angular.isUndefined(id)) {\n $scope.$emit('productSelected-' + ctrl.productShortName, {\n 'scope': ctrl.$id,\n 'optionID': ctrl.selectedID,\n 'autoRenewal': ctrl.autorenew,\n 'priceList': ctrl.priceList,\n 'hasBundle': ctrl.hasBundle,\n 'isBundleActive': ctrl.isBundleActive\n });\n }\n\n if (angular.isArray(ctrl.priceList)) ctrl.selectedOption = findOptionById(id) || findFirstAvailableOption();\n ctrl.getBuyblockSavings({ 'selectedOption': ctrl.selectedOption, 'product': ctrl.product.bbSettings, 'component': ctrl.component });\n getBuyLink();\n }\n\n /**\n * Reapply an existing option (e.g. if switching from AR to non-AR)\n */\n function applyOption(isInit) {\n ctrl.selectedOption = isInit ? getDefaultOption() : findOptionByKeys() || findFirstAvailableOption();\n\n updateSelectedID(ctrl.selectedOption.id);\n $scope.$emit('productSelected-' + ctrl.productShortName, {\n 'scope': ctrl.$id,\n 'optionID': ctrl.selectedOption.id,\n 'autoRenewal': ctrl.autorenew,\n 'priceList': ctrl.priceList,\n 'hasBundle': ctrl.hasBundle,\n 'isBundleActive': ctrl.isBundleActive\n });\n ctrl.getBuyblockSavings({ 'selectedOption': ctrl.selectedOption, 'product': ctrl.product.bbSettings, 'component': ctrl.component });\n getBuyLink();\n ctrl.showMoreDevices = hasMoreDevices();\n }\n\n /**\n * Get the buy link for the current selected option\n */\n function getBuyLink() {\n ctrl.isBBReady = false;\n if (angular.isUndefined(ctrl.selectedOption)) return false;\n return priceData.getCartLink(ctrl.overrideEstoreID || ctrl.selectedOption.product_id, ctrl.selectedOption.id, ctrl.purchaseType, ctrl.productShortName, ctrl.resellerOverride, ctrl.selectedOption['buy_link']).then(handleBuyLinkSuccess, handleBuyLinkError).then(function () {\n ctrl.isBBReady = true;\n });\n }\n\n /**\n * Prepare checkout link for tracking\n * @param {Object} response The checkout link object\n */\n function handleBuyLinkSuccess(response) {\n ctrl.buyLink = response.data;\n }\n\n /**\n * Handle link not found\n * @param {Object|String} response\n */\n function handleBuyLinkError(response) {\n errorService.warn(response);\n ctrl.buyLink = 'Oops! Something went wrong';\n }\n\n /**\n * Handle more devices popup, set more devices popup. Without localTemplate a default design will be used\n * @param { boolean } localTemplate - if true the value will be taken from buyblock template,\n * based on specific popup class\n */\n function moreDevicesPopup(localTemplate) {\n var template = '/resources/template/html/modules/_shared/components/buyblock/templates/buyblock-more-devices-popup.html';\n if (localTemplate) {\n var moreDevicesPopupTemplateUrl = 'buyblock-popup.html';\n var moreDevicesPopupTemplateCache = $templateCache.get(moreDevicesPopupTemplateUrl);\n if (moreDevicesPopupTemplateCache) {\n template = moreDevicesPopupTemplateUrl;\n }\n }\n\n ngDialog.open(buyblockService.ngDialogSettings(ctrl, $scope, {\n 'className': 'ngdialog-no-bg',\n 'template': template\n }));\n }\n\n function accountDescPopup() {\n ngDialog.open(buyblockService.ngDialogSettings(ctrl, $scope, {\n 'className': 'ngdialog-plain ngdialog-narrow',\n 'template': '/resources/template/html/modules/b2c/components/kscloud-disclaimers/templates/account-description.html'\n }));\n }\n\n function getFirstProductMaxAndMinPrices(priceList) {\n if (!angular.isArray(priceList)) return;\n if (!$rootScope.mainProductAlreadySelected) {\n var filteredPriceList = priceList.filter(function (x) {\n return ctrl.radioFilter(x);\n });\n ctrl.minValue = filteredPriceList[0].price;\n ctrl.maxValue = filteredPriceList[filteredPriceList.length - 1].price;\n $rootScope.mainProductAlreadySelected = true;\n }\n }\n\n /**\n * Get savings messaging and value (either custom or default)\n */\n function getSavings(messageType, savingsPriceType, ignoreOverrideText) {\n if (angular.isUndefined(ctrl.product) || angular.isUndefined(ctrl.buyblockData) || angular.isUndefined(ctrl.selectedOption)) return;\n\n var overrideText = ctrl.hasBundle && ctrl.isBundleActive ? ctrl.product.bundleBuyblockPromo : null;\n\n if (ignoreOverrideText) {\n overrideText = null;\n }\n\n return buyblockService.getSavings(messageType, ctrl.selectedOption, ctrl.product.bbSettings, ctrl.buyblockData, overrideText || ctrl.saveRateText, savingsPriceType || ctrl.savingsPriceType, ctrl.component);\n }\n\n function checkHighlighted() {\n if (ctrl.highlighted === 'true') return 'red';else if (ctrl.highlighted === 'grey') return 'grey';else return 'green';\n }\n\n function isInteger(value) {\n return angular.isNumber(value) && isFinite(value) && Math.floor(value) === value;\n }\n }\n})();\n'use strict';\n\n(function () {\n angular.module('kappGlobal.buyblocks').component('buyblockStep', {\n 'bindings': {\n 'productShortName': '@productName', //for cases when we only know the name of the product\n 'productInfo': '<', //for cases when we have all product data available\n 'purchaseType': '@',\n 'selectedPack': '@',\n 'selectedTerm': '@',\n 'positionOnPage': '@',\n 'selectedAutorenew': '@',\n 'estore': '@',\n 'symbol': '@',\n 'locale': '@',\n 'buyButtonText': '@',\n 'productBox': '@',\n 'template': '@',\n 'noBoxshot': '@', // does nothing? LT\n 'setBuyblockValueByProduct': '&', // does nothing? LT\n 'valueToSet': '<',\n 'upgradeButton': '@',\n 'highlighted': '@',\n 'type': '@',\n 'kscDisclaimer': '@',\n 'hideProductLinks': '@',\n 'promoline': '@',\n 'countMonthly': '@',\n 'description': '@',\n 'free': '@',\n 'onFormUpdated': '&',\n 'hideTrialButton': '@',\n 'showBbTrialButton': '@',\n 'saveRateText': '@',\n 'savingsPriceType': '@',\n 'disableAutoRenew': '@',\n 'hideArCheckbox': '@',\n 'hideTagline': '@',\n 'hideBv': '@',\n 'buttonText': '@',\n 'showDisclaimer': '@',\n 'getBuyblockSavings': '&',\n 'buyButtonHtmlTop': '<',\n 'buyButtonHtmlBottom': '<'\n },\n 'controller': 'buyblockStepController',\n 'templateUrl': ['$element', '$attrs', function ($element, $attrs) {\n var template = 'step';\n\n if ($attrs.template) template = $attrs.template;\n\n return '/resources/template/html/modules/_shared/components/buyblock/templates/buyblock-' + template + '.html';\n }]\n });\n})();\n'use strict';\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\n(function () {\n 'use strict';\n\n buyblockStepController.$inject = [\"$rootScope\", \"$scope\", \"$state\", \"$timeout\", \"$element\", \"ENV\", \"SETUP\", \"appHelperService\", \"directiveData\", \"priceData\", \"productData\", \"windowHelperService\", \"buyblockService\", \"ngDialog\", \"errorService\", \"tracking\", \"currencyManager\"];\n angular.module('kappGlobal.buyblocks').controller('buyblockStepController', buyblockStepController);\n\n function buyblockStepController($rootScope, $scope, $state, $timeout, $element, ENV, SETUP, appHelperService, directiveData, priceData, productData, windowHelperService, buyblockService, ngDialog, errorService, tracking, currencyManager) {\n var ctrl = this;\n ctrl.error = ctrl.error || null;\n ctrl.hidePack = false;\n ctrl.isChecked = '' + SETUP.isChecked;\n ctrl.showAR = '' + SETUP.showAR;\n ctrl.curState = $state.current.parent;\n ctrl.stateName = $state.current.name;\n ctrl.currency = buyblockService.getCurrency(ctrl);\n ctrl.currentValue = {\n 'price': 'no data',\n 'eStore': '/store'\n };\n ctrl.productPricesExist = true;\n ctrl.navigateUserTo = windowHelperService.navigateUserTo;\n ctrl.product = {};\n ctrl.productName = ctrl.productName || '';\n ctrl.productShortName = ctrl.productShortName || '';\n ctrl.asterisk = '';\n ctrl.translations = {};\n ctrl.buyblockData = {\n 'unavailableMsgType': '',\n 'buyblock_not_available_msg': '',\n 'buyblock_not_available_b2b_msg': '', 'b2cDisclaimer': '', 'disclaimer': ''\n };\n ctrl.priceList = [];\n ctrl.packArr = [];\n ctrl.termArr = [];\n ctrl.buyLink = 'no data received yet';\n ctrl.omnitureText = 'Buy';\n ctrl.defaultAutorenew = ctrl.isChecked === 'true';\n ctrl.priceListGroupedBy = { 'term': {}, 'pack': {} };\n ctrl.notAvailableItemsFor = { 'term': {}, 'pack': {} };\n ctrl.isChangeTriggeredByAnotherBuyblock = false;\n\n ctrl.addSalesTrackingParameters = addSalesTrackingParameters;\n ctrl.registerOnce = registerOnce;\n ctrl.setCurrentValue = setCurrentValue;\n ctrl.setCurrentPack = setCurrentPack;\n ctrl.setCurrentTerm = setCurrentTerm;\n ctrl.showARPopup = showARPopup;\n ctrl.sendToBuyUrl = sendToBuyUrl;\n ctrl.getPromoline = getPromoline;\n ctrl.accountDescPopup = accountDescPopup;\n ctrl.getSavings = getSavings;\n ctrl.isPriceSavingDisplayed = isPriceSavingDisplayed;\n ctrl.isPackDisabled = isPackDisabled;\n ctrl.isTermDisabled = isTermDisabled;\n ctrl.hideDisclaimer = hideDisclaimer;\n ctrl.arCheckboxClick = arCheckboxClick;\n ctrl.changePack = changePack;\n ctrl.changeTerm = changeTerm;\n var isLRC = appHelperService.stateIncludes('lrc-verdict');\n ctrl.isLrc = isLRC;\n ctrl.fixedDashTitle = fixedDashTitle;\n\n // $rootScope destroy events begin\n var currencyChange = $rootScope.$on('currencyChange', handleCurrencyChange),\n arPopupCheck = $rootScope.$on('arPopupCheck', function (event, value) {\n ctrl.autorenew = value;\n setCurrentValue();\n });\n // $rootScope destroy events end\n\n ctrl.$onInit = activate;\n ctrl.$onChanges = handleChanges;\n ctrl.hasTrialButton = hasTrialButton;\n\n function fixedDashTitle(title) {\n return (title || '').replace('-', '‑');\n }\n\n function activate() {\n ctrl.locale = ctrl.locale || ENV.locale;\n\n getProductData(ctrl.productShortName);\n getTranslations();\n\n $scope.$on('$destroy', handleScopeDestroyed);\n }\n\n function handleChanges(changesObj) {\n if (changesObj['valueToSet']) {\n handleUpdateCurrentValue(changesObj['valueToSet'].currentValue);\n }\n }\n\n function getPromoline() {\n return ctrl.promoline ? ctrl.promoline : ctrl.buyblockData ? ctrl.buyblockData.promoline : undefined;\n }\n\n function addSalesTrackingParameters(event) {\n if (event) event.preventDefault();\n if (ctrl.error) return;\n\n if (isLRC && ctrl.positionOnPage) {\n var price = ctrl.currentValue.price;\n var price_striked = ctrl.currentValue.price_striked;\n var delta = 0;\n\n if (price_striked) {\n delta = Math.round(100 * (price_striked - price) / price_striked);\n }\n\n buyblockService.trackBBEvent('addToCart', 'ecommerce', 'addToCart', ctrl.positionOnPage + '_' + ctrl.product.shortName + '_' + parseInt(ctrl.packValue, 10) + '_' + parseInt(ctrl.termValue, 10) + '_' + (ctrl.autorenew ? 'AR' : 'MR') + '_' + delta + '%');\n }\n ctrl.priceList.forEach(function (item) {\n if (item.id === ctrl.currentValue.id) {\n ctrl.masterPartner = item.master_partner;\n }\n });\n ctrl.buyLink = tracking.onBuyButtonClick(ctrl.buyLink, ctrl.masterPartner);\n sendToBuyUrl();\n }\n\n function registerOnce(checked) {\n buyblockService.registerOnce(checked, ctrl.defaultAutorenew);\n }\n\n function updateARPopupProduct() {\n if (isLRC) {\n $rootScope.$emit('shoARPopupProduct', {\n 'name': ctrl.product.shortName,\n 'position': ctrl.positionOnPage\n });\n }\n }\n\n function changePack($event) {\n ctrl.setCurrentValue('pack', $event);\n\n if (isLRC && ctrl.positionOnPage) {\n buyblockService.trackBBEvent('autoEvent', 'LRC', 'selector_devices', ctrl.packValue + '_' + ctrl.positionOnPage + '_' + ctrl.product.shortName);\n }\n }\n\n function changeTerm() {\n ctrl.setCurrentValue('term');\n\n if (isLRC && ctrl.positionOnPage) {\n buyblockService.trackBBEvent('autoEvent', 'LRC', 'selector_years', ctrl.termValue + '_' + ctrl.positionOnPage + '_' + ctrl.product.shortName);\n }\n }\n\n function showARPopup($event) {\n updateARPopupProduct();\n\n if (isLRC && ctrl.positionOnPage) {\n buyblockService.trackBBEvent('autoEvent', 'LRC', 'click_infoAutoRenewal', ctrl.positionOnPage + '_' + ctrl.product.shortName);\n }\n\n buyblockService.showARPopup();\n\n if ($event) {\n $event.preventDefault();\n return false;\n }\n }\n\n function arCheckboxClick() {\n updateARPopupProduct();\n\n if (isLRC && ctrl.positionOnPage) {\n buyblockService.trackBBEvent('autoEvent', 'LRC', 'checkbox_autoRenewal', (ctrl.autorenew ? 'on' : 'off') + '_' + ctrl.positionOnPage + '_' + ctrl.product.shortName);\n }\n\n registerOnce(true);\n }\n\n function handleUpdateCurrentValue(valueToSet) {\n var valueIsDifferent = !_.isEqual(valueToSet, {\n 'pack': ctrl.packValue,\n 'term': ctrl.termValue,\n 'autorenew': ctrl.autorenew\n });\n\n if (valueToSet && valueIsDifferent) {\n ctrl.packValue = valueToSet.pack;\n ctrl.termValue = valueToSet.term;\n ctrl.autorenew = valueToSet.autorenew;\n if (ctrl.priceList && ctrl.priceList.length > 0) setCurrentValue();\n }\n }\n\n function setCurrentPack(direction) {\n var position = ctrl.packArr.indexOf(ctrl.packValue);\n var length = ctrl.packArr.length;\n var newPosition = direction === 'up' ? position + 1 : position - 1;\n\n if (newPosition < 0) {\n newPosition = position;\n } else if (newPosition >= length) {\n newPosition = position;\n }\n\n ctrl.packValue = ctrl.packArr[newPosition];\n setCurrentValue('pack');\n }\n\n function setCurrentTerm(direction) {\n var position = ctrl.termArr.indexOf(ctrl.termValue);\n var length = ctrl.termArr.length;\n var newPosition = direction === 'up' ? position + 1 : position - 1;\n\n if (newPosition < 0) {\n newPosition = position;\n } else if (newPosition >= length) {\n newPosition = position;\n }\n\n ctrl.termValue = ctrl.termArr[newPosition];\n setCurrentValue('pack');\n }\n\n function setCurrentValue(changedProp) {\n findMatch(changedProp);\n ctrl.renewalAvailable = buyblockService.checkRenewal(ctrl.priceList, ctrl.packValue, ctrl.termValue, false);\n ctrl.getBuyblockSavings({ 'selectedOption': ctrl.currentValue, 'product': ctrl.product.bbSettings, 'component': ctrl.component });\n\n var promise = priceData.getCartLink(ctrl.overrideEstoreID || ctrl.currentValue.product_id, ctrl.currentValue.id, ctrl.purchaseType, ctrl.product.shortName, '', ctrl.currentValue['buy_link']).then(function (response) {\n ctrl.buyLink = response.data;\n }, function (rejection) {\n errorService.warn(rejection);\n ctrl.buyLink = 'Oops! Something went wrong';\n });\n // Prevents cyclic buyblock switching of one another and $onChanges() error\n if (!ctrl.isChangeTriggeredByAnotherBuyblock) {\n ctrl.onFormUpdated({\n 'section': 'top',\n 'product': ctrl.product,\n 'values': {\n 'term': ctrl.termValue,\n 'pack': ctrl.packValue,\n 'autorenew': ctrl.autorenew\n }\n });\n } else {\n ctrl.isChangeTriggeredByAnotherBuyblock = false;\n }\n\n setAvailableOptionList(changedProp);\n\n return promise;\n }\n\n function setAvailableOptionList(prop) {\n if (!prop) return;\n\n // this is to avoid repeating the same logic for term and pack\n // prop is pack|term\n // otherProp is the other value\n var otherProp = prop === 'term' ? 'pack' : 'term',\n propValue = ctrl[prop + 'Value'],\n otherPropValueArr = ctrl[otherProp + 'Arr'],\n availableListForOtherProp = _.uniq(ctrl.priceListGroupedBy[prop][propValue].map(function (i) {\n return i[otherProp];\n }));\n\n ctrl.notAvailableItemsFor[otherProp] = _.difference(otherPropValueArr, availableListForOtherProp).reduce(function (res, pack) {\n return angular.extend(res, _defineProperty({}, pack, true));\n }, {});\n }\n\n function isPackDisabled(value) {\n return ctrl.notAvailableItemsFor.pack[value] === true;\n }\n\n function isTermDisabled(value) {\n return ctrl.notAvailableItemsFor.term[value] === true;\n }\n\n function sendToBuyUrl() {\n return buyblockService.sendToBuyUrl(ctrl.buyLink, ctrl.purchaseType);\n }\n\n function hidePack() {\n if (ctrl.locale === 'ru-ru' && (ctrl.productShortName === 'ksc-family' || ctrl.product && ctrl.product.shortName === 'ksc-family')) {\n ctrl.hidePack = true;\n }\n }\n\n // Private helper Methods\n function buyblockSetup() {\n return directiveData.getLocal('buyblock/buyblock-texts').then(function (response) {\n var bbSettings = ctrl.productInfo ? ctrl.productInfo.bbSettings : {},\n products = ['kesb-select', 'kesb-advanced', 'ksos', 'cloud', 'cloud-old', 'cloud-plus', 'office-365'];\n\n ctrl.isB2B = _.some(products, function (product) {\n return product === ctrl.product.shortName;\n });\n\n ctrl.buyblockData = angular.extend({}, response.data.fields, bbSettings);\n\n ctrl.disablePackSelect = ctrl.buyblockData.packSelectDisabled === 'True';\n ctrl.disableTermSelect = ctrl.buyblockData.termSelectDisabled === 'True';\n\n ctrl.buyblockData.unavailableMsgType = ''; // this.buyblockData.unavailableMsgType used in ng-switch directive to pick b2b_fallback text if it exists and b2c_fallback if not\n if (ctrl.buyblockData.buyblock_not_available_msg && !ctrl.isB2B) ctrl.buyblockData.unavailableMsgType = 'b2c';\n if (ctrl.buyblockData.buyblock_not_available_b2b_msg && ctrl.isB2B) ctrl.buyblockData.unavailableMsgType = 'b2b';\n checkDisclaimer();\n hidePack();\n });\n }\n\n function checkDisclaimer() {\n var b2bDisclaimer = ctrl.buyblockData.disclaimer || ctrl.buyblockData.smbDisclaimer,\n hideAsteriskOnB2B = ctrl.product && appHelperService.isTrue(ctrl.product.hideAsterisk) && ctrl.isB2B;\n var alwaysShowDisclaimer = ctrl.buyblockData.b2cDisclaimerLogicType && !!ctrl.buyblockData.b2cDisclaimerLogicType.length && ctrl.buyblockData.b2cDisclaimerLogicType[0] === 'always show b2c disclaimer';\n if ($rootScope.kaspersky.verdictSite === 'LRC' && !alwaysShowDisclaimer && (!ctrl.isB2B || ctrl.locale === 'en-us')) return ctrl.disclaimerObj = {};\n\n ctrl.disclaimerObj = ctrl.product && ctrl.product.bbDisclaimer ? { 'disclaimer': ctrl.product.bbDisclaimer, 'asterisk': hideAsteriskOnB2B ? '' : '*' } : buyblockService.checkDisclaimer(ctrl.isB2B, ctrl.buyblockData.b2cDisclaimer, b2bDisclaimer, hideAsteriskOnB2B);\n }\n\n function findMatch(changedProp) {\n var otherProp = changedProp === 'pack' ? 'term' : 'pack';\n var otherPropValueArr = ctrl[otherProp + 'Arr'];\n var matchConfig = {\n 'pack': ctrl.packValue,\n 'term': ctrl.termValue,\n 'autorenew': ctrl.autorenew\n },\n matchIndex = buyblockService.findPriceMatch(ctrl.priceList, matchConfig, false);\n var initialMatchConfig = angular.copy(matchConfig);\n\n // First look if opposite autorenew exists, matchIndex will be set above 0 if success\n if (matchIndex < 0) {\n matchConfig.autorenew = !matchConfig.autorenew;\n matchIndex = buyblockService.findPriceMatch(ctrl.priceList, matchConfig, false);\n }\n\n // Second if no opposite autorenew look if an option with higher value of other prop exists\n if (matchIndex < 0) {\n matchConfig.autorenew = !matchConfig.autorenew;\n\n for (var i = 0; i < otherPropValueArr.length; i++) {\n matchConfig[otherProp] = otherPropValueArr[i];\n matchIndex = buyblockService.findPriceMatch(ctrl.priceList, matchConfig, false);\n if (matchIndex >= 0 && !angular.equals(initialMatchConfig, matchConfig)) {\n ctrl[otherProp + 'Value'] = ctrl.priceList[matchIndex][otherProp];\n setAvailableOptionList(otherProp);\n highlightForcedValueChange(otherProp);\n break;\n }\n }\n }\n\n if (matchIndex > -1) {\n ctrl.currentValue = ctrl.priceList[matchIndex];\n }\n }\n\n function highlightForcedValueChange(selectTypeAttr) {\n $element.find('input[' + selectTypeAttr + ']').addClass('highlight');\n $timeout(function () {\n $element.find('input[' + selectTypeAttr + ']').removeClass('highlight');\n }, 1000);\n }\n\n function getTranslations() {\n return directiveData.getLocal('general-translations').then(function (response) {\n ctrl.translations = response.data.fields;\n });\n }\n\n function setProductPageLink() {\n ctrl.product.prodPageLink = ctrl.hideProductLinks ? '' : ctrl.product.prodPageLink;\n }\n\n function getProductData(productShortName) {\n if (productShortName) {\n return getCustomProduct(productShortName);\n }\n var productInfo = $scope.$watch('$ctrl.productInfo', function (newV) {\n if (newV) {\n ctrl.product = ctrl.productInfo;\n ctrl.productName = ctrl.productInfo.title;\n setProductPageLink();\n getPriceList();\n productInfo();\n buyblockSetup();\n }\n });\n }\n\n function getCustomProduct(productShortName) {\n return productData.getb2c(productShortName).then(function (response) {\n ctrl.product = response.data.fields;\n ctrl.productName = response.data.fields.title;\n setProductPageLink();\n buyblockSetup();\n }).then(function () {\n getPriceList();\n });\n }\n\n function getPriceList(skipEstore) {\n if (!ctrl.productName) return;\n if (ctrl.purchaseType === 'Free') {\n ctrl.productPricesExist = false;\n return;\n }\n if (!skipEstore) setEStore();\n\n return priceData.getPrice(ctrl.productName, ctrl.purchaseType, ctrl.newEstore || '').then(handlePriceSuccess, handlePriceError);\n }\n\n function handleCurrencyChange() {\n ctrl.currency = $rootScope.currency;\n ctrl.showAR = SETUP.showAR;\n ctrl.isChangeTriggeredByAnotherBuyblock = true;\n getPriceList();\n }\n\n function handlePriceError(rejection) {\n ctrl.productPricesExist = false;\n errorService.warn(rejection);\n }\n\n function handleScopeDestroyed() {\n arPopupCheck();\n currencyChange();\n }\n\n function resetPricingObjectsToDefault() {\n ctrl.packArr = [];\n ctrl.termArr = [];\n ctrl.buyLink = 'no data received yet';\n ctrl.defaultAutorenew = ctrl.isChecked === 'true';\n ctrl.productPricesExist = true;\n }\n\n function handlePriceSuccess(response) {\n setupTracking();\n var bbSettings = ctrl.product.bbSettings;\n\n if (bbSettings.isChecked) ctrl.isChecked = bbSettings.isChecked;\n if (bbSettings.showAR) ctrl.showAR = bbSettings.showAR;\n if (bbSettings.hideArCheckbox[0]) {\n ctrl.showAR = bbSettings.hideArCheckbox[0] && bbSettings.hideArCheckbox[0].toLowerCase() !== 'true';\n }\n\n resetPricingObjectsToDefault();\n\n function sortByPropName(prop, a, b) {\n return parseInt(a[prop]) - parseInt(b[prop]);\n }\n\n ctrl.priceList = response.data;\n ctrl.priceList.sort(function (a, b) {\n var res = sortByPropName('pack', a, b);\n return res !== 0 ? res : sortByPropName('term', a, b);\n });\n\n if (!angular.isArray(ctrl.priceList) || !ctrl.priceList.length) {\n if (ctrl.newEstore) {\n errorService.warn('No matches found on ' + ctrl.newEstore + ' for ' + ctrl.productName + '; retrying with default estore');\n delete ctrl.newEstore;\n return getPriceList(true);\n } else {\n errorService.warn('API not available');\n ctrl.productPricesExist = false;\n return false;\n }\n }\n setupPriceList();\n }\n\n function isPriceSavingDisplayed() {\n var hasCurVal = ctrl.currentValue && ctrl.currentValue.price_striked;\n\n if (isLRC && ctrl.buyblockData.saveRateLRC) ctrl.buyblockData.saveRate = ctrl.buyblockData.saveRateLRC;\n\n return hasCurVal && (ctrl.buyblockData.saveRate || ctrl.buyblockData.saveRateText);\n }\n\n function setEStore() {\n if (ctrl.estore || isLRC) {\n ctrl.newEstore = ctrl.estore;\n if (!currencyManager.pl2Enabled()) setOverrideEstoreID();\n return;\n }\n ctrl.newEstore = ctrl.product.bbSettings.eStore;\n }\n\n function setOverrideEstoreID() {\n priceData.info(ctrl.productName, ctrl.purchaseType, ctrl.newEstore, isLRC).then(function (response) {\n ctrl.overrideEstoreID = response.data && response.data.id;\n if (!ctrl.overrideEstoreID) handleEstoreOverrideError();\n }).catch(function (rejection) {\n errorService.warn(rejection);\n handleEstoreOverrideError();\n });\n }\n\n function handleEstoreOverrideError() {\n directiveData.getPartnerByName(ctrl.newEstore).then(function (response) {\n ctrl.partnerObj = response;\n if (ctrl.partnerObj) ctrl.buyblockData.lrcPartnerUnavailable = ctrl.buyblockData.lrcPartnerUnavailable && ctrl.buyblockData.lrcPartnerUnavailable.replace('#', ctrl.partnerObj.href);\n });\n }\n\n function getCNPackValue() {\n if (ctrl.locale === 'zh-cn' && (ctrl.productShortName === 'kav' || $state.params.productName === 'kav')) {\n return ctrl.packArr[0];\n }\n }\n\n function getUSPackValue() {\n if (ctrl.locale === 'en-us' && (ctrl.productShortName === 'ktsmd' || $state.params.productName === 'ktsmd')) {\n return _.find(ctrl.packArr, function (i) {\n return i === '5 Devices';\n });\n }\n }\n\n function setupPriceList() {\n var hasAutoRenewal = ctrl.priceList.some(function (item) {\n return ctrl.priceList[0].autorenew !== item.autorenew;\n });\n ctrl.packArr = _.uniq(ctrl.priceList.map(function (i) {\n return i.pack;\n }));\n ctrl.termArr = _.uniq(ctrl.priceList.map(function (i) {\n return i.term;\n }));\n var packMonthlyArr = [];\n var packYearlyArr = [];\n\n ctrl.priceList.forEach(function (item) {\n if (item.term_type === 'M') {\n ctrl.monthlyAvailable = true;\n packMonthlyArr.push(item.term);\n } else if (item.term_type === 'Y') {\n packYearlyArr.push(item.term);\n }\n });\n\n ctrl.packMonthlyArr = _.uniq(packMonthlyArr);\n ctrl.packYearlyArr = _.uniq(packYearlyArr);\n\n ctrl.priceListGroupedBy = {\n 'term': _.groupBy(ctrl.priceList, 'term'),\n 'pack': _.groupBy(ctrl.priceList, 'pack')\n };\n\n // if we have only one autorenew and it doesn't match the default one\n if (ctrl.disableAutoRenew === 'true') {\n ctrl.showAR = false;\n ctrl.autorenew = false;\n } else {\n if (!hasAutoRenewal) {\n ctrl.showAR = false;\n ctrl.defaultAutorenew = ctrl.priceList[0].autorenew;\n }\n }\n // when we provided with a number for licenses convert it to string like '1 device' for LRC\n if (ctrl.selectedPack) {\n setCustomSelectedPack(ctrl);\n }\n // when we provided with a number for terms convert it to string like '2 years' for LRC\n if (ctrl.selectedTerm && ctrl.productShortName === 'ksec') {\n ctrl.selectedTermString = ctrl.selectedTerm;\n } else if (ctrl.selectedTerm) {\n setCustomSelectedTerm(ctrl);\n }\n\n ctrl.packValue = getCNPackValue() || ctrl.selectedPackString || getUSPackValue() || ctrl.packArr[0];\n ctrl.termValue = ctrl.selectedTermString || ctrl.termArr[0];\n ctrl.autorenew = ctrl.selectedAutorenew === 'true' || ctrl.defaultAutorenew;\n\n // Emit Buyblock ready event to currency selector\n $rootScope.buyBlockReady();\n\n setCurrentValue();\n setAvailableOptionList('pack');\n setAvailableOptionList('term');\n\n updatePriceFormat();\n }\n\n function setCustomSelectedPack(ctrl) {\n var selectedPack = parseInt(ctrl.selectedPack);\n ctrl.selectedPackString = _.find(ctrl.packArr, function (value) {\n return parseInt(value) === selectedPack;\n });\n }\n\n function setCustomSelectedTerm(ctrl) {\n if (ctrl.selectedTerm === '0') ctrl.selectedTerm = 1;\n\n if (ctrl.selectedTerm === '0.5' && ctrl.priceListGroupedBy.term['6 Months']) {\n ctrl.selectedTerm = ctrl.selectedTermString = '6 Months';\n return;\n }\n\n var selectedTermFloat = parseFloat(ctrl.selectedTerm),\n rounded = selectedTermFloat < 1 ? Math.ceil(selectedTermFloat) : Math.floor(selectedTermFloat);\n\n if (ctrl.selectedTerm < 1) {\n ctrl.packMonthlyArr.forEach(function (value) {\n var valueInt = parseInt(value);\n // this is needed for cases where product is in months, and the term would come as 0.08 (30/365)\n // use ceil for numbers < 1 and floor for numbers > 1\n if (valueInt === rounded || valueInt === rounded - 1) {\n ctrl.selectedTermString = value;\n }\n });\n } else {\n ctrl.packYearlyArr.forEach(function (value) {\n var valueInt = parseInt(value);\n if (valueInt === rounded || valueInt === rounded - 1) {\n ctrl.selectedTermString = value;\n }\n });\n }\n }\n\n function setupTracking() {\n if (ctrl.purchaseType === 'Renewal') ctrl.omnitureText = 'Renew';\n if (ctrl.upgradeButton) ctrl.omnitureText = 'Upgrade';\n }\n\n function accountDescPopup() {\n ngDialog.open(buyblockService.ngDialogSettings(ctrl, $scope, {\n 'className': 'ngdialog-plain ngdialog-narrow',\n 'template': '/resources/template/html/modules/b2c/components/kscloud-disclaimers/templates/account-description.html'\n }));\n }\n\n function hasTrialButton() {\n return buyblockService.hasTrialButton(ctrl.product) && !appHelperService.isTrue(ctrl.hideTrialButton);\n }\n\n /**\n * Get savings messaging and value (either custom or default)\n */\n function getSavings(messageType, savingsPriceType, saveRateText) {\n if (ctrl.product) return buyblockService.getSavings(messageType, ctrl.currentValue, ctrl.product.bbSettings, ctrl.buyblockData, saveRateText || ctrl.saveRateText, savingsPriceType || ctrl.savingsPriceType, ctrl.component);\n }\n\n function updatePriceFormat() {\n ctrl.showLabelFR = appHelperService.stateIncludes('lrc-verdict') && ENV.locale === 'fr-fr';\n }\n\n function hideDisclaimer() {\n var isLrcPage = appHelperService.assertStateByName(['lrc-verdict', 'lrc-serial', 'lrc-b2b', 'lrc-b2b-att', 'lrc-b2c', 'lrc-b2c-att', 'lrc-fallback', 'vsb-lrc-closed']),\n isProduct = ctrl.product && ['cloud', 'cloud-old', 'cloud-plus', 'office-365'].indexOf(ctrl.product.shortName) > -1,\n hasDisclaimer = ctrl.disclaimerObj && ctrl.disclaimerObj.disclaimer,\n hideDisclaimer = appHelperService.isTrue(ctrl.product.hideDisclaimerOnLrc);\n\n return isLrcPage && isProduct && hasDisclaimer && hideDisclaimer;\n }\n }\n})();\n'use strict';\n\n(function () {\n angular.module('kappGlobal.buyblocks').component('buyblockVsbDropdown', {\n 'bindings': {\n 'productShortName': '@productName', //for cases when we only know the name of the product\n 'productInfo': '<', //for cases when we have all product data available\n 'purchaseType': '@',\n 'selectedPack': '@',\n 'selectedTerm': '@',\n 'selectedAutorenew': '@',\n 'estore': '@',\n 'template': '@',\n 'overrideTrialButtonText': '@',\n 'overrideTrialButtonLink': '@',\n 'hideProductLinks': '@',\n 'promoline': '@',\n 'hideTrialButton': '@',\n 'saveRateText': '@',\n 'savingsPriceType': '@',\n 'disableAutoRenew': '@',\n 'setCustomLocale': '@',\n 'component': '@',\n 'redesign': '@',\n 'showDisclaimer': '@',\n 'getBuyblockSavings': '&'\n },\n 'controller': 'buyblockVsbDropdownController',\n 'templateUrl': ['$element', '$attrs', function ($element, $attrs) {\n var template = 'vsb-dropdown';\n\n if ($attrs.template) template = $attrs.template;\n\n return '/resources/template/html/modules/_shared/components/buyblock/templates/buyblock-' + template + '.html';\n }]\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n buyblockVsbDropdownController.$inject = [\"$rootScope\", \"$scope\", \"$state\", \"ENV\", \"SETUP\", \"buyBlockHelperService\", \"directiveData\", \"priceData\", \"productData\", \"tracking\", \"windowHelperService\", \"buyblockService\", \"appHelperService\", \"errorService\"];\n angular.module('kappGlobal.buyblocks').controller('buyblockVsbDropdownController', buyblockVsbDropdownController);\n\n function buyblockVsbDropdownController($rootScope, $scope, $state, ENV, SETUP, buyBlockHelperService, directiveData, priceData, productData, tracking, windowHelperService, buyblockService, appHelperService, errorService) {\n var ctrl = this;\n ctrl.isChecked = '' + SETUP.isChecked;\n ctrl.showAR = '' + SETUP.showAR;\n ctrl.productPricesExist = true;\n ctrl.currency = buyblockService.getCurrency(ctrl);\n ctrl.currentValue = {\n 'price': 'no data',\n 'eStore': '/store'\n };\n if (!ctrl.isCustomLocale) ctrl.curState = $state.current.parent;\n ctrl.termArr = [];\n ctrl.buyLink = 'no data received yet';\n ctrl.defaultAutorenew = ctrl.isChecked === 'true';\n ctrl.selectedPackString = '';\n ctrl.selectedTermString = '';\n ctrl.showARPopup = showARPopup;\n ctrl.registerOnce = registerOnce;\n ctrl.getPriceList = getPriceList;\n ctrl.setCurrentValue = setCurrentValue;\n ctrl.sendToBuyUrl = sendToBuyUrl;\n ctrl.addSalesTrackingParameters = addSalesTrackingParameters;\n ctrl.getPromoline = getPromoline;\n ctrl.getSavings = getSavings;\n ctrl.hideRenewLink = hideRenewLink;\n ctrl.isTotal = appHelperService.assertStateByName('smb-product-total');\n ctrl.isUniversal = appHelperService.assertStateByName('smb-product-universal');\n\n // $rootScope destroy events begin\n var arPopupCheck = $rootScope.$on('arPopupCheck', function (event, value) {\n ctrl.autorenew = value;\n setCurrentValue();\n }),\n disableAR = $rootScope.$on('disableAR', function () {\n ctrl.autorenew = false;\n setCurrentValue();\n }),\n currencyChange = $rootScope.$on('currencyChange', handleCurrencyChange);\n // $rootScope destroy events end\n ctrl.$onInit = activate;\n ctrl.hasTrialButton = hasTrialButton;\n\n function activate() {\n ctrl.locale = ctrl.locale || ENV.locale;\n ctrl.isCustomLocale = checkCustomLocale();\n ctrl.formattedPromoSavings = '';\n\n getProductData();\n\n $scope.$on('$destroy', handleScopeDestroyed);\n }\n\n function addSalesTrackingParameters(event) {\n if (event) event.preventDefault();\n ctrl.priceList.forEach(function (item) {\n if (item.id === ctrl.currentValue.id) {\n ctrl.masterPartner = item.master_partner;\n }\n });\n ctrl.buyLink = tracking.onBuyButtonClick(ctrl.buyLink, ctrl.masterPartner);\n if (!ctrl.error) ctrl.sendToBuyUrl();\n }\n\n function hideRenewLink() {\n var isLrcPage = appHelperService.assertStateByName(['lrc-verdict', 'lrc-serial', 'lrc-b2b', 'lrc-b2b-att', 'lrc-b2c', 'lrc-b2c-att', 'lrc-fallback', 'vsb-lrc-closed']);\n if (ctrl.product && ctrl.product.shortName) return ctrl.product.shortName === 'ksos' && ctrl.locale === 'en-us' && isLrcPage && !!ctrl.product.renewLink;\n }\n\n function getPromoline() {\n return ctrl.promoline ? ctrl.promoline : ctrl.buyblockData ? ctrl.buyblockData.promoline : undefined;\n }\n\n function getPriceList() {\n if (angular.isUndefined(ctrl.productName)) return;\n if (ctrl.purchaseType === 'Free') {\n ctrl.productPricesExist = false;\n return;\n }\n setEStore();\n\n return priceData.getPrice(ctrl.productName, ctrl.purchaseType, ctrl.newEstore || '').then(handlePriceSuccess, handlePriceError);\n }\n\n function registerOnce(checked) {\n if (ctrl.modalDisplayedOnce) return;\n\n ctrl.modalDisplayedOnce = true;\n buyblockService.registerOnce(checked, ctrl.defaultAutorenew, true);\n }\n\n function sendToBuyUrl() {\n windowHelperService.goToUrl(tracking.formatQueryString(ctrl.buyLink, buyBlockHelperService.hasPurchaseAsPurchaseType(ctrl.purchaseType)));\n }\n\n function setPackArr() {\n var packListMatchingSelectedTerm = ctrl.priceList.filter(function (i) {\n return i.term === ctrl.termValue;\n }).map(function (i) {\n return i.pack;\n });\n\n ctrl.packArr = _.uniq(packListMatchingSelectedTerm).sort(function (a, b) {\n return parseInt(a) - parseInt(b);\n });\n }\n\n function setCurrentValue() {\n setPackArr();\n findMinMax();\n\n // A check for proper text input begin\n var packNumber = parseInt(ctrl.packValue, 10);\n ctrl.error = false;\n\n if (ctrl.isCustomLocale) {\n if (isNaN(packNumber)) ctrl.packValue = ctrl.minValue; // for IE which allows symbols in the number input\n\n if (angular.isUndefined(ctrl.packValue) || packNumber > 0 && packNumber < ctrl.minValue) {\n ctrl.error = true; // the value is incorrect but user could continue his input to get a valid number, triggers red border\n return false;\n }\n\n ctrl.error = false;\n\n if (packNumber <= 0) ctrl.packValue = ctrl.minValue;\n }\n\n if (packNumber > ctrl.maxValue) ctrl.packValue = ctrl.maxValue;\n if (packNumber < ctrl.minValue) ctrl.packValue = ctrl.minValue;\n\n if (ctrl.packValue === undefined) {\n ctrl.error = true;\n return;\n }\n findMatch();\n ctrl.renewalAvailable = buyblockService.checkRenewal(ctrl.priceList, ctrl.packValue, ctrl.termValue, mustParsePackValue());\n ctrl.getBuyblockSavings({ 'selectedOption': ctrl.currentValue, 'product': ctrl.product.bbSettings, 'component': ctrl.component });\n ctrl.formattedPromoSavings = ctrl.product.savingsPromoType !== 'none' ? getSavings('text', ctrl.product.buyblockPromo) : '';\n\n return priceData.getCartLink(ctrl.currentValue.product_id, ctrl.currentValue.id, ctrl.purchaseType, '', '', ctrl.currentValue['buy_link']).then(function (response) {\n ctrl.buyLink = response.data;\n }, function (rejection) {\n errorService.warn(rejection);\n ctrl.buyLink = 'Oops! Something went wrong';\n });\n }\n\n function showARPopup() {\n buyblockService.showARPopup();\n }\n\n // Private methods\n function buyblockSetup() {\n return directiveData.getLocal('buyblock/buyblock-texts').then(function (response) {\n var products = ['kesb-select', 'kesb-advanced', 'ksos', 'cloud', 'cloud-old', 'cloud-plus', 'office-365', 'kesb-universal'];\n ctrl.isB2B = _.some(products, function (product) {\n return product === ctrl.product.shortName;\n });\n ctrl.buyblockData = response.data.fields;\n ctrl.buyblockData.unavailableMsgType = '';\n if (ctrl.buyblockData.buyblock_not_available_msg) ctrl.buyblockData.unavailableMsgType = 'b2c';\n if (ctrl.buyblockData.buyblock_not_available_b2b_msg) ctrl.buyblockData.unavailableMsgType = 'b2b';\n checkDisclaimer();\n checkMetaCountries();\n });\n }\n\n function checkCustomLocale() {\n var customLocales = ['fi-fi', 'da-dk', 'en-us', 'es-es', 'fr-fr', 'fr-be', 'it-it', 'ja-jp', 'nb-no', 'nl-nl', 'ru-ru', 'sv-se'];\n return ctrl.setCustomLocale || customLocales.indexOf(ctrl.locale) > -1;\n }\n\n function checkMetaCountries() {\n var metaCountries = ['en-za', 'en-ae', 'ar-ae', 'tr-tr'];\n if (metaCountries.indexOf(ctrl.locale) > -1) ctrl.buyblockData.needMore = ctrl.buyblockData.or;\n }\n\n function checkDisclaimer() {\n var b2bDisclaimer = ctrl.buyblockData.disclaimer || ctrl.buyblockData.smbDisclaimer,\n hideAsteriskOnB2B = ctrl.product && appHelperService.isTrue(ctrl.product.hideAsterisk) && ctrl.isB2B;\n\n if (ctrl.locale === 'en-us' && $rootScope.kaspersky.verdictSite === 'LRC' || $state.current.name.indexOf('smb-product') > -1 && ctrl.locale !== 'it-it') return ctrl.disclaimerObj = {};\n\n ctrl.disclaimerObj = ctrl.product && ctrl.product.bbDisclaimer ? { 'disclaimer': ctrl.product.bbDisclaimer, 'asterisk': hideAsteriskOnB2B ? '' : '*' } : buyblockService.checkDisclaimer(ctrl.isB2B, ctrl.buyblockData.b2cDisclaimer, b2bDisclaimer, hideAsteriskOnB2B);\n }\n\n function setProductPageLink() {\n ctrl.product.prodPageLink = ctrl.hideProductLinks ? '' : ctrl.product.prodPageLink;\n }\n\n function getProductData() {\n if (ctrl.productShortName) {\n // for cases when we only know the name of the product\n return getCustomProduct();\n } else {\n // for cases when we have all product data available\n $scope.$watch('$ctrl.productInfo', function (newV) {\n if (newV && angular.isObject(newV)) {\n ctrl.product = ctrl.productInfo;\n ctrl.productName = ctrl.productInfo.title;\n ctrl.hasExtendedFallback = ctrl.product.extendedFallback === 'true';\n ctrl.getPriceList();\n setProductPageLink();\n buyblockSetup();\n }\n });\n }\n }\n\n function handleCurrencyChange() {\n ctrl.currency = $rootScope.currency || '';\n ctrl.getPriceList();\n ctrl.packValue = ctrl.minValue;\n }\n\n function handleScopeDestroyed() {\n arPopupCheck();\n disableAR();\n currencyChange();\n }\n\n /**\n * ctrl.product.bbSettings.eStore pulls data from Tridion component, ctrl.estore takes it from parameter passed in\n * this conditional is probably used to target buyblocks outside of LRC, added lrc state detection to make it work for ATT verdicts - AFedotov\n */\n function setEStore() {\n if (ctrl.estore || $state.includes('lrc-verdict')) {\n ctrl.newEstore = ctrl.estore;\n } else {\n ctrl.newEstore = ctrl.product && ctrl.product.bbSettings ? ctrl.product.bbSettings.eStore : '';\n }\n }\n\n function setupPriceList() {\n var hasAutoRenewal = ctrl.priceList.some(function (item) {\n return ctrl.priceList[0].autorenew !== item.autorenew;\n });\n\n ctrl.termArr = _.uniq(ctrl.priceList.map(function (i) {\n return i.term;\n }));\n ctrl.termTypeArr = _.uniq(ctrl.priceList.map(function (i) {\n return i['term_name'];\n }));\n\n if (ctrl.disableAutoRenew === 'true') {\n ctrl.showAR = false;\n ctrl.autorenew = false;\n } else {\n if (!hasAutoRenewal) {\n ctrl.showAR = false;\n if (!ctrl.isCustomLocale) ctrl.defaultAutorenew = ctrl.priceList[0].autorenew;\n } else {\n ctrl.showAR = '' + SETUP.showAR;\n }\n }\n\n function setCustomSelectedTerm() {\n _.forEach(ctrl.termArr, function (value, index) {\n if (parseInt(value) === parseInt(ctrl.selectedTerm || ctrl.product.bbSettings.selectedTerm)) {\n if (ctrl.selectedTerm && ctrl.selectedTerm.indexOf(ctrl.termTypeArr[index]) === -1) return;\n ctrl.selectedTermString = value;\n }\n });\n }\n\n if (ctrl.product.bbSettings && (ctrl.selectedTerm || ctrl.product.bbSettings.selectedTerm)) setCustomSelectedTerm();\n ctrl.termValue = ctrl.selectedTermString || ctrl.termArr[0];\n ctrl.autorenew = ctrl.defaultAutorenew;\n\n setPackArr();\n\n function setCustomSelectedPack() {\n var x = parseInt(ctrl.selectedPack || ctrl.product.bbSettings.selectedPack);\n ctrl.selectedPackString = ctrl.packArr.reduce(function (prev, curr) {\n return Math.abs(parseInt(curr) - x) < Math.abs(parseInt(prev) - x) ? curr : prev;\n });\n ctrl.showMinMaxDisclaimer = x > parseInt(ctrl.selectedPackString) || x < parseInt(ctrl.selectedPackString);\n }\n\n // let packValueInt = (ctrl.template === 'smb-prod' && !ctrl.isCustomLocale) ? parseInt(ctrl.packArr[0]) : ctrl.packArr[0];\n if (ctrl.product.bbSettings && (ctrl.selectedPack || ctrl.product.bbSettings.selectedPack)) setCustomSelectedPack();\n if (!ctrl.isCustomLocale) ctrl.packValue = parseInt(ctrl.selectedPackString) || parseInt(ctrl.packArr[0]);\n if (ctrl.isCustomLocale) ctrl.packValue = ctrl.selectedPackString || ctrl.packArr[0];\n // ctrl.packValue = ctrl.selectedPackString || packValueInt;\n\n // Emit Buyblock ready event to currency selector\n $rootScope.buyBlockReady();\n setCurrentValue();\n }\n\n function findMinMax() {\n var mappedPackArr = _.map(ctrl.packArr, function (obj) {\n return parseInt(obj);\n });\n ctrl.maxValue = Math.max.apply(Math, mappedPackArr);\n ctrl.minValue = Math.min.apply(Math, mappedPackArr);\n }\n\n function handlePriceError(response) {\n ctrl.productPricesExist = false;\n errorService.warn(response);\n }\n\n function handlePriceSuccess(response) {\n var bbSettings = ctrl.product.bbSettings;\n\n if (bbSettings.isChecked) {\n ctrl.isChecked = bbSettings.isChecked;\n ctrl.defaultAutorenew = ctrl.isChecked === 'true';\n }\n\n if (bbSettings.showAR) {\n ctrl.showAR = bbSettings.showAR;\n }\n\n if (angular.isDefined(bbSettings.hideArCheckbox) && bbSettings.hideArCheckbox[0]) {\n ctrl.hideArCheckbox = bbSettings.hideArCheckbox[0];\n }\n\n ctrl.priceList = response.data;\n if (!angular.isObject(ctrl.priceList) || !ctrl.priceList.length) {\n errorService.warn('API not available');\n ctrl.productPricesExist = false;\n return false;\n }\n setupPriceList();\n ctrl.productPricesExist = true;\n }\n\n function getCustomProduct() {\n var promiseFn = productData.getsmb;\n if (ctrl.productShortName === 'ksos') promiseFn = productData.getvsb;\n return promiseFn(ctrl.productShortName).then(function (response) {\n ctrl.product = ctrl.productInfo = response.data.fields;\n ctrl.productName = response.data.fields.title;\n setProductPageLink();\n buyblockSetup();\n }).then(function () {\n ctrl.getPriceList();\n });\n }\n\n function findMatch() {\n var matchConfig = {\n 'pack': ctrl.packValue,\n 'term': ctrl.termValue,\n 'autorenew': ctrl.autorenew\n },\n matchIndex = buyblockService.findPriceMatch(ctrl.priceList, matchConfig, mustParsePackValue());\n\n if (matchIndex < 0) {\n matchConfig.autorenew = !matchConfig.autorenew;\n\n matchIndex = buyblockService.findPriceMatch(ctrl.priceList, matchConfig, mustParsePackValue());\n }\n\n if (matchIndex > -1) {\n ctrl.currentValue = ctrl.priceList[matchIndex];\n ctrl.currentValuePack = ctrl.currentValue.pack.split(' + ');\n ctrl.packValue = ctrl.isCustomLocale ? ctrl.currentValue.pack : parseInt(ctrl.currentValuePack[0]);\n }\n }\n\n function mustParsePackValue() {\n return ctrl.locale !== 'ru-ru' && ctrl.locale !== 'ja-jp';\n }\n\n function hasTrialButton() {\n return buyblockService.hasTrialButton(ctrl.product) && !appHelperService.isTrue(ctrl.hideTrialButton);\n }\n\n /**\n * Get savings messaging and value (either custom or default)\n */\n function getSavings(messageType, messageText) {\n if (ctrl.product) return buyblockService.getSavings(messageType, ctrl.currentValue, ctrl.product.bbSettings, ctrl.buyblockData, messageText || ctrl.saveRateText, ctrl.savingsPriceType);\n }\n }\n})();\n'use strict';\n\n(function () {\n buyblockService.$inject = [\"$rootScope\", \"currencyManager\", \"windowHelperService\", \"sessionStorageService\", \"appHelperService\", \"$filter\", \"SETUP\", \"$window\"];\n angular.module('kappGlobal.buyblocks').factory('buyblockService', buyblockService);\n\n function buyblockService($rootScope, currencyManager, windowHelperService, sessionStorageService, appHelperService, $filter, SETUP, $window) {\n return {\n 'sendToBuyUrl': sendToBuyUrl,\n 'hasPurchaseAsPurchaseType': hasPurchaseAsPurchaseType,\n 'getCurrency': getCurrency,\n 'checkDisclaimer': checkDisclaimer,\n 'registerOnce': registerOnce,\n 'showARPopup': showARPopup,\n 'checkRenewal': checkRenewal,\n 'ngDialogSettings': ngDialogSettings,\n 'findPriceMatch': findPriceMatch,\n 'hasTrialButton': hasTrialButton,\n 'getSavings': getSavings,\n 'trackBBEvent': trackBBEvent\n };\n\n function trackBBEvent(name, eventCategory, eventAction, eventLabel) {\n if (angular.isFunction($window.trackEvent)) {\n $window.trackEvent(name, {\n 'eventCategory': eventCategory,\n 'eventAction': eventAction,\n 'eventLabel': eventLabel\n });\n }\n }\n\n function sendToBuyUrl(buyLink) {\n return windowHelperService.goToUrl(buyLink);\n }\n\n function hasPurchaseAsPurchaseType(purchaseType) {\n return purchaseType && purchaseType === 'Purchase';\n }\n\n function ngDialogSettings(ctrl, scope, settings) {\n var defaultSettings = {\n 'data': ctrl,\n 'scope': scope\n };\n\n if (settings) for (var attr in settings) {\n defaultSettings[attr] = settings[attr];\n }return defaultSettings;\n }\n\n function getCurrency(value) {\n currencyManager.getCurrencies();\n\n var requiredArray = ['currency', 'symbol', 'locale'];\n var hasAllRequirements = requiredArray.every(function (item) {\n return value.hasOwnProperty(item);\n });\n\n if (!hasAllRequirements) return $rootScope.currency || '';\n $rootScope.populateCurrency({\n 'currency': value.currency,\n 'symbol': value.symbol,\n 'locale': value.locale\n });\n return value.currency;\n }\n\n function checkDisclaimer(isB2B, b2cDisclaimer, b2bDisclaimer, hideAsteriskOnB2B) {\n var disclaimerObj = {};\n\n if (!isB2B && b2cDisclaimer) {\n disclaimerObj.asterisk = '*';\n disclaimerObj.disclaimer = b2cDisclaimer;\n }\n\n if (isB2B && b2bDisclaimer) {\n disclaimerObj.asterisk = hideAsteriskOnB2B ? '' : '*';\n disclaimerObj.disclaimer = b2bDisclaimer;\n }\n\n return disclaimerObj;\n }\n\n function registerOnce(checked, defaultAutoRenew) {\n var canClickTwice = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;\n\n if (!defaultAutoRenew) return;\n var isRegistered = sessionStorageService.get('unchecked');\n if (isRegistered && canClickTwice) {\n $rootScope.$emit('clickedTwice');\n return;\n }\n if (isRegistered) return;\n sessionStorageService.set('unchecked', 'true');\n $rootScope.$emit('onceUnchecked');\n if (checked) {\n $rootScope.$emit('checkboxClicked', true);\n }\n }\n\n function showARPopup() {\n var arPopupButtons = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'true';\n\n $rootScope.$emit('toggleARPopup', arPopupButtons);\n $rootScope.$emit('onceUnchecked');\n }\n\n /**\n * Given a list of price configurations, check if there are two different\n * configurations for the same pack and term values, one with autorenew enabled\n * and another one with no autorenew.\n * @param {object[]} priceList - List containing all the price configurations\n * @param {string | number} packValue - Provided pack value\n * @param {string} termValue - Provided term value\n * @param {boolean} parsePack - If provided and true, the value of the field\n * 'pack' will be parsed to int before comparing\n * @returns {boolean} True if both configurations are found; false otherwise\n */\n function checkRenewal(priceList, packValue, termValue, parsePack) {\n var manualConfig = {\n 'pack': packValue,\n 'term': termValue,\n 'autorenew': false\n },\n autoConfig = {\n 'pack': packValue,\n 'term': termValue,\n 'autorenew': true\n };\n\n return findPriceMatch(priceList, manualConfig, parsePack) > -1 && findPriceMatch(priceList, autoConfig, parsePack) > -1;\n }\n\n /**\n * Find a matching price config on a provided price list.\n * @param {object[]} priceList - List containing all the price configurations\n * @param {object} matchConfig - Target config to be found in the price list\n * @param {boolean} parsePack - If provided and true, the value of the field\n * 'pack' will be parsed to int before comparing\n * @returns {number} The index of the first appearance of a matching config, if any; -1 otherwise\n */\n function findPriceMatch(priceList, matchConfig, parsePack) {\n var comparator = parsePack ? function (config) {\n return parseInt(config.pack) === parseInt(matchConfig.pack) && config.term === matchConfig.term && config.autorenew === matchConfig.autorenew;\n } : matchConfig;\n\n return _.findIndex(priceList, comparator);\n }\n\n /**\n * Check if the trial button must be shown or hidden\n * @param {object} productData - Product data from Tridion\n * @returns {boolean} True if trial button must be shown, false otherwise\n */\n function hasTrialButton(productData) {\n if (!productData) return true;\n if (!productData.freeTrialLink) return false;\n\n var hideInB2c = _.first(productData.hideTrialFromB2C) === 'True';\n var isB2cPage = appHelperService.assertStateByName('home-security');\n if (isB2cPage && hideInB2c) return false;\n\n var hideInSmb = _.first(productData.hideTrialFromSMB) === 'True';\n var isSmbPage = appHelperService.assertStateByName('vsb-home');\n if (isSmbPage && hideInSmb) return false;\n\n var hideInVsb = _.first(productData.hideTrialFromVSB) === 'True';\n var isVsbPage = appHelperService.assertStateByName('vsb-home');\n if (isVsbPage && hideInVsb) return false;\n\n var hideInLrc = SETUP.trialButtonsInsideLrc === 'false' || _.first(productData.hideTrialFromLRC) === 'True';\n var isLrcPage = appHelperService.assertStateByName(['lrc-verdict', 'lrc-serial', 'lrc-b2b', 'lrc-b2b-att', 'lrc-b2c', 'lrc-b2c-att', 'lrc-fallback', 'vsb-lrc-closed']);\n if (isLrcPage && hideInLrc) return false;\n\n var hideInProductPage = _.first(productData.hideTrialFromProductPage) === 'True';\n var isProductPage = false;\n switch (productData.shortName) {\n case 'kac':\n isProductPage = appHelperService.assertStateByName('product-adcleaner');\n break;\n case 'kbls':\n isProductPage = appHelperService.assertStateByName('product-battery-life-saver');\n break;\n case 'kcp':\n isProductPage = appHelperService.assertStateByName('product-cleaner');\n break;\n case 'kfa':\n isProductPage = appHelperService.assertStateByName('product-kfa');\n break;\n case 'kisa-free':\n isProductPage = appHelperService.assertStateByName('product-kisa-free');\n break;\n case 'kpm':\n isProductPage = appHelperService.assertStateByName('home-product-kpm');\n break;\n case 'kqs':\n isProductPage = appHelperService.assertStateByName('product-qr-scanner');\n break;\n case 'ksc':\n isProductPage = appHelperService.assertStateByName('product-system-checker');\n break;\n case 'kscloud':\n isProductPage = appHelperService.assertStateByName('home-product-kscloud');\n break;\n case 'ksec':\n isProductPage = appHelperService.assertStateByName('product-ksec');\n break;\n case 'ksk':\n isProductPage = appHelperService.assertStateByName('product-ksk');\n break;\n case 'kss':\n isProductPage = appHelperService.assertStateByName('product-kss');\n break;\n case 'kwc': // Fall-through\n case 'ksu':\n isProductPage = appHelperService.assertStateByName('product-ksu');\n break;\n case 'kvs':\n isProductPage = appHelperService.assertStateByName('product-kvs');\n break;\n case 'kvspro':\n isProductPage = appHelperService.assertStateByName('product-kvspro');\n break;\n case 'kav': // Fall-through\n case 'kis': // Fall-through\n case 'kisa': // Fall-through\n case 'kismac': // Fall-through\n case 'ktsmd':\n isProductPage = appHelperService.assertStateByName('home-product');\n break;\n case 'kcpc': // Fall-through\n case 'kcpcs': // Fall-through\n case 'kpctu': // Fall-through\n case 'kpis': // Fall-through\n case 'kwbsc':\n isProductPage = appHelperService.assertStateByName('premium-service');\n break;\n case 'ksc-family':\n case 'ksc-personal':\n isProductPage = appHelperService.assertStateByName('home-product-kscloud');\n break;\n case 'kps': // Fall-through\n case 'ksos':\n isProductPage = appHelperService.assertStateByName('vsb-product');\n break;\n case 'kseb-core': // Fall-through\n case 'cloud':\n isProductPage = appHelperService.assertStateByName('smb-product-cloud');\n break;\n case 'kseb-advanced':\n isProductPage = appHelperService.assertStateByName('smb-product-advanced');\n break;\n case 'kseb-select':\n isProductPage = appHelperService.assertStateByName('smb-product-select');\n break;\n case 'total':\n isProductPage = appHelperService.assertStateByName('smb-product-total');\n break;\n }\n return !(isProductPage && hideInProductPage);\n }\n\n function roundPricingPercentage(num) {\n return Math.floor(num / 5) * 5;\n }\n\n /**\n * Generates a text to display for savings based on comparison of original and promo prices\n * @param {string} messageType\n * @param {object} selectedOption\n * @param {object=} bbSettings\n * @param {object=} buyblockData\n * @param {string=} customSaveText\n * @param {string=}customSavingsPriceType\n * @param {object=} component\n * @returns {string|boolean}\n */\n function getSavings(messageType, selectedOption) {\n var bbSettings = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n var buyblockData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n var customSaveText = arguments[4];\n var customSavingsPriceType = arguments[5];\n var component = arguments[6];\n\n var savingsPrice = '',\n currentPrice = selectedOption.price,\n priceStriked = selectedOption.price_striked,\n savingsPriceType = customSavingsPriceType || bbSettings && bbSettings.savingsPriceType,\n savingsText = customSaveText || bbSettings && bbSettings.saveRateText || (messageType === 'rate' ? buyblockData.saveRate : buyblockData.saveText);\n\n if (angular.isDefined(component) && angular.isDefined(bbSettings.saveMsgOverride)) {\n for (var i = 0; i < bbSettings.saveMsgOverride.length; i++) {\n if (bbSettings.saveMsgOverride[i].component === component) {\n savingsText = bbSettings.saveMsgOverride[i].savingsMsgOverride;\n savingsPriceType = bbSettings.saveMsgOverride[i].savingsPriceType;\n }\n }\n }\n\n if (!savingsPriceType) {\n return savingsText;\n }\n\n if (savingsPriceType && savingsText) {\n if (!priceStriked || savingsPriceType === 'none') return;\n\n if (savingsPriceType === 'difference') {\n savingsPrice = $filter('customCurrency')(priceStriked - currentPrice);\n }\n\n if (savingsPriceType === 'percentage') {\n savingsPrice = Math.round(roundPricingPercentage((priceStriked - currentPrice) / priceStriked * 100)) + '%';\n }\n\n if (savingsPriceType === 'exact') {\n savingsPrice = Math.round((priceStriked - currentPrice) / priceStriked * 100) + '%';\n }\n }\n\n savingsText = _.replace(savingsText, /\\(\\(savings\\)\\)/g, savingsPrice) || false;\n\n return savingsText;\n }\n }\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n angular.module('kappGlobal.buyblocks').component('buyblockUserAccount', {\n 'bindings': {\n 'label': '@',\n 'count': '<',\n 'accountInfoOverride': '@',\n 'descriptionPopup': '@',\n 'productName': '@'\n },\n 'controller': 'buyblockUserAccountController',\n 'templateUrl': '/resources/template/html/modules/_shared/components/buyblock-user-account/templates/buyblock-user-accounts.html'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n buyblockUserAccountController.$inject = [\"$scope\", \"ngDialog\", \"buyblockService\", \"messageFormatService\"];\n angular.module('kappGlobal.buyblocks').controller('buyblockUserAccountController', buyblockUserAccountController);\n\n function buyblockUserAccountController($scope, ngDialog, buyblockService, messageFormatService) {\n var ctrl = this;\n ctrl.$id = $scope.$id || Math.random().toString(36).substring(7);\n ctrl.internalDescriptionPopup = '';\n\n ctrl.$onInit = function () {\n ctrl.internalDescriptionPopup = ctrl.internalDescriptionPopup || ctrl.descriptionPopup;\n // For cases when changes occur before $onInit\n replaceUserAccountString();\n };\n\n ctrl.$onChanges = function () {\n ctrl.setShowFlags();\n\n if (ctrl.showAccountLabel && ctrl.label) {\n ctrl.setPluralizedLabel();\n // For cases when changes occur after $onInit\n replaceUserAccountString();\n }\n };\n\n ctrl.openDescriptionPopup = function () {\n ngDialog.open(buyblockService.ngDialogSettings(ctrl, $scope, {\n 'className': 'ngdialog-plain ngdialog-narrow',\n 'template': '/resources/template/html/modules/b2c/components/kscloud-disclaimers/templates/account-description.html'\n }));\n };\n\n ctrl.setShowFlags = function () {\n ctrl.showAccountOverride = ctrl.hasAccountOverride();\n ctrl.showAccountLabel = !ctrl.hasAccountOverride() && ctrl.hasLabel() && ctrl.hasCount();\n ctrl.showPopupIcon = ctrl.hasDescriptionPopup() && (ctrl.showAccountOverride || ctrl.showAccountLabel);\n };\n\n ctrl.setPluralizedLabel = function () {\n if (!ctrl._mfFormatter || ctrl._mfFormatterLabel !== ctrl.label) {\n ctrl._mfFormatterLabel = ctrl.label;\n // userAccounts: name of the resulting formatter\n ctrl._mfFormatter = messageFormatService.compile({ 'userAccounts': ctrl.label });\n }\n\n ctrl._mfFormatter.then(function (formatter) {\n // USER_ACCOUNTS: name of the param passed\n ctrl.pluralizedLabel = formatter.userAccounts({ 'USER_ACCOUNTS': ctrl.count });\n });\n };\n\n ctrl.hasAccountOverride = function () {\n return isStringNonEmpty(ctrl.accountInfoOverride);\n };\n\n ctrl.hasLabel = function () {\n return isStringNonEmpty(ctrl.label);\n };\n\n ctrl.hasDescriptionPopup = function () {\n return isStringNonEmpty(ctrl.descriptionPopup);\n };\n\n ctrl.hasCount = function () {\n return angular.isDefined(ctrl.count) && ctrl.count > 0;\n };\n\n function isStringNonEmpty(str) {\n return angular.isDefined(str) && str.trim() !== '';\n }\n\n function replaceUserAccountString() {\n var userAccountsRegex = new RegExp('\\\\(\\\\(user_accounts\\\\)\\\\)', 'ig');\n var userAccountsMinusRegex = new RegExp('\\\\(\\\\(user_accounts_minus_1\\\\)\\\\)', 'ig');\n ctrl.internalDescriptionPopup = ctrl.descriptionPopup;\n ctrl.internalDescriptionPopup = ctrl.internalDescriptionPopup.replace(userAccountsRegex, ctrl.count);\n ctrl.internalDescriptionPopup = ctrl.internalDescriptionPopup.replace(userAccountsMinusRegex, ctrl.count - 1);\n }\n }\n})();\n'use strict';\n\n(function () {\n angular.module('kappGlobal.callbackForm').component('callbackForm', {\n 'bindings': {\n 'resource': '@'\n },\n 'templateUrl': '/resources/template/html/modules/_shared/components/callback-form/templates/callback-form-button.html',\n 'controller': 'callbackFormController'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n callbackFormController.$inject = [\"directiveData\", \"$scope\", \"errorService\", \"ENV\", \"ngDialog\", \"$http\", \"$timeout\", \"$document\"];\n angular.module('kappGlobal.callbackForm').controller('callbackFormController', callbackFormController);\n\n function callbackFormController(directiveData, $scope, errorService, ENV, ngDialog, $http, $timeout, $document) {\n var ctrl = this;\n ctrl.env = ENV.locale;\n ctrl.$onInit = activate;\n ctrl.requestCallbackForm = requestCallbackForm;\n ctrl.setCallbackBtnWidth = {};\n ctrl.callbackIsEnabled = false;\n\n function activate() {\n getData();\n }\n\n function handleRejection(rejection) {\n errorService.warn(rejection);\n }\n\n function getData() {\n return directiveData.getByUrl('page-header').then(function (response) {\n ctrl.data = response.data.fields;\n ctrl.callbackIsEnabled = ctrl.data.enableCallbackWidget && ctrl.data.enableCallbackWidget[0] === 'true';\n\n if (ctrl.callbackIsEnabled) {\n return $http.get('/content/' + ctrl.env + '/site-header/callback-widget.json', { 'cache': true }).then(function (response) {\n ctrl.callbackPopup = response.data.fields;\n\n $timeout(function () {\n ctrl.setCallbackBtnWidth = {\n 'width': $document.find('#callback-button').outerWidth(true) + 1\n };\n });\n }).catch(handleRejection);\n }\n }).catch(handleRejection);\n }\n\n function requestCallbackForm(munchkinId, formId) {\n ngDialog.open({\n 'template': '/resources/template/html/modules/_shared/components/callback-form/templates/callback-form.html',\n 'className': 'ngdialog-flyout ngdialog-callback-form',\n 'data': ctrl,\n 'showClose': false,\n 'controller': ['marketoHelperService', function (marketoHelperService) {\n marketoHelperService.loadMarketo(munchkinId, formId, $scope);\n }]\n });\n }\n }\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n angular.module('kappGlobal.compareTable').component('compareTable', {\n 'bindings': {\n 'resource': '@',\n 'hideProductLinks': '@',\n 'component': '@'\n },\n 'templateUrl': '/resources/template/html/modules/_shared/components/compare-table/templates/compare-table.html',\n 'controller': 'compareTableController'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n compareTableController.$inject = [\"$http\", \"$element\", \"$scope\", \"directiveData\", \"osDetectService\", \"appHelperService\", \"$rootScope\", \"errorService\", \"ENV\", \"rootScopeHelper\"];\n angular.module('kappGlobal.compareTable').controller('compareTableController', compareTableController);\n\n function compareTableController($http, $element, $scope, directiveData, osDetectService, appHelperService, $rootScope, errorService, ENV, rootScopeHelper) {\n var ctrl = this;\n ctrl.bbData = {};\n ctrl.locale = ENV.locale;\n ctrl.setBuyblockValueByProduct = setBuyblockValueByProduct;\n ctrl.$onInit = activate;\n ctrl.checkForHomeSecurityState = checkForHomeSecurityState;\n ctrl.term = ctrl.locale === 'en-us' && appHelperService.stateIncludes('lrc-b2c') ? 2 : '';\n\n var ngRender = $rootScope.$on('ngRender', function () {\n ctrl.data ? runScripts() : ngRender();\n });\n\n rootScopeHelper.on($scope, 'buyblockBundleChange', handleBundleChange);\n\n function handleBundleChange(event, data) {\n ctrl.isBundleActive = data.isActive;\n ctrl.bundleProductName = data.productName;\n }\n\n function activate() {\n getData();\n setPromoline();\n }\n\n function setBuyblockValueByProduct(product, term, pack, autorenew) {\n ctrl.bbData[product] = { 'term': term, 'pack': pack, 'autorenew': autorenew };\n }\n\n function checkForHomeSecurityState() {\n return appHelperService.assertStateByName(['home-security', 'home-product']);\n }\n\n function setPromoline() {\n directiveData.getLocal('buyblock/buyblock-texts').then(function (response) {\n ctrl.buyblockData = response.data.fields;\n }, function (rejection) {\n errorService.warn(rejection);\n });\n }\n\n // Helper Methods\n function getData() {\n var featuredProducts = getFeaturedProducts();\n return directiveData.getByUrl(featuredProducts).then(function (response) {\n ctrl.featureSet = []; //Contains objects of every unique feature\n if (!response.data.fields) return false;\n ctrl.header = response.data.fields;\n ctrl.data = response.data.fields.products;\n ctrl.featuresResponse = response.data.fields.featuresOverride;\n var uniqueFeaturedArray = [];\n var counter = 0;\n ctrl.data.forEach(function (item, index) {\n if (item.match(/\\.json/)) {\n return $http.get(item, { 'cache': true }).then(function (response) {\n var featuresArray = Object.keys(response.data.fields.productFeatures).map(function (key) {\n return response.data.fields.productFeatures[key];\n });\n ctrl.data[index] = response.data.fields;\n for (var i in featuresArray) {\n var featured = featuresArray[i];\n if (angular.isDefined(featured.name) && featured.name !== '' && uniqueFeaturedArray.indexOf(featured.name) === -1) {\n uniqueFeaturedArray.push(featured.name);\n ctrl.featureSet.push(featured);\n }\n }\n\n if (ctrl.featuresResponse && counter === 0) {\n counter++;\n return $http.get(ctrl.featuresResponse, { 'cache': true }).then(function (response) {\n ctrl.data.features = response.data.fields.Body;\n }, function (rejection) {\n errorService.warn(rejection);\n });\n }\n }, function (rejection) {\n errorService.warn(rejection);\n });\n }\n });\n }, function (rejection) {\n errorService.warn(rejection);\n $element.hide();\n });\n }\n\n function getFeaturedProducts() {\n var osList = getOsList(),\n featuredProducts = osList[0].data,\n hash = appHelperService.getLocationHash(),\n osDetected = osDetectService.getCurrentOs(hash),\n osProducts = [];\n if (appHelperService.assertStateByName('home-security')) {\n if (osDetected.device !== 'undefined') {\n osProducts = osList.filter(function (obj) {\n return JSON.stringify(obj.os) === JSON.stringify(osDetected.device.type);\n })[0];\n }\n if (osProducts) {\n featuredProducts = osProducts.data;\n return featuredProducts;\n }\n }\n return featuredProducts;\n }\n\n function getOsList() {\n return [{ 'os': 'pc', 'data': ctrl.resource || 'comparison-table' }, { 'os': 'mac', 'data': 'comparison-table-mac' }, {\n 'os': 'mobile', 'data': 'comparison-table-mobile'\n }];\n }\n\n function runScripts() {\n ngRender();\n angular.element(document).ready(function () {\n var mobileDefault = void 0;\n // Check if current page has comparison charts on it\n if ($element.find('.comparison-chart').length) {\n // Loop through each comparison chart and set the default column\n mobileDefault = $element.find('.comparison-chart').data('comparison-default');\n $element.find('.comparison-chart .' + mobileDefault).addClass('currently-active');\n }\n $element.find('.comparison-nav li a').on('click', function (e) {\n e.preventDefault();\n var $this = $(this),\n\n // Get navigation direction\n direction = $this.data('comparison-nav-direction'),\n\n // Get chart ID from the data attribute\n chartID = $this.closest('.comparison-nav').data('comparison-chart'),\n\n // Get comparison chart using that ID\n $comparisonChart = $('.comparison-chart[data-comparison-chart=\"' + chartID + '\"]'),\n\n // Get comparison chart mobile nav\n $comparisonChartNav = $('.comparison-nav[data-comparison-chart=\"' + chartID + '\"]'),\n\n // Get current column\n $currentColumn = $comparisonChart.find('.row-header .column-product.currently-active'),\n currentTitle = $currentColumn.find('h2.product-title a').clone(),\n currentID = parseInt($currentColumn.data('column')),\n nextID = parseInt(currentID + 1),\n prevID = parseInt(currentID - 1),\n nextNavID = parseInt(currentID + 2),\n prevNavID = parseInt(currentID - 2);\n\n // Remove featured style from the comparison chart mobile nav\n $comparisonChartNav.find('li a.feat').removeClass('feat');\n $comparisonChart.find('.row-header .column-product.column-' + ($scope.$ctrl.data.length + 1) + ' h2.product-title a').addClass('feat');\n\n // Check direction and if prev/next column exists\n if (direction === 'prev' && $comparisonChart.find('.column-product.column-' + prevID).length) {\n $comparisonChart.find('.column-' + currentID).removeClass('currently-active');\n $comparisonChart.find('.column-' + prevID).addClass('currently-active');\n var newPrevTitle = $comparisonChart.find('.row-header .column-product.column-' + prevNavID + ' h2.product-title a').clone();\n\n if ($comparisonChart.find('.row-header .column-product.column-' + prevNavID + ' h2.product-title a').hasClass('feat')) {\n $comparisonChartNav.find('li.nav-prev a').addClass('feat');\n }\n\n if ($comparisonChart.find('.row-header .column-product.column-' + currentID + ' h2.product-title a').hasClass('feat')) {\n $comparisonChartNav.find('li.nav-next a').addClass('feat');\n }\n\n $comparisonChartNav.find('li.nav-prev a').html(newPrevTitle.children());\n $comparisonChartNav.find('li.nav-next a').html(currentTitle.children());\n } else if (direction === 'next' && $comparisonChart.find('.column-product.column-' + nextID).length) {\n $comparisonChart.find('.column-' + currentID).removeClass('currently-active');\n $comparisonChart.find('.column-' + nextID).addClass('currently-active');\n var newNextTitle = $comparisonChart.find('.row-header .column-product.column-' + nextNavID + ' h2.product-title a').clone();\n\n if ($comparisonChart.find('.row-header .column-product.column-' + nextNavID + ' h2.product-title a').hasClass('feat')) {\n $comparisonChartNav.find('li.nav-next a').addClass('feat');\n }\n\n if ($comparisonChart.find('.row-header .column-product.column-' + currentID + ' h2.product-title a').hasClass('feat')) {\n $comparisonChartNav.find('li.nav-prev a').addClass('feat');\n }\n $comparisonChartNav.find('li.nav-next a').html(newNextTitle.children());\n $comparisonChartNav.find('li.nav-prev a').html(currentTitle.children());\n }\n });\n });\n }\n }\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n contentRepoService.$inject = [\"$window\", \"$rootScope\", \"$sce\", \"$http\", \"$stateParams\", \"$location\", \"repoMapperService\", \"imageManagerService\", \"SUBCATCONFIG\", \"CATEGORYCONFIG\", \"CERTIFICATESCONFIG\", \"AWARDSCONFIG\", \"ENV\", \"appHelperService\", \"windowHelperService\", \"tracking\"];\n angular.module('kappGlobal.contentRepo').factory('contentRepoService', contentRepoService);\n\n function contentRepoService($window, $rootScope, $sce, $http, $stateParams, $location, repoMapperService, imageManagerService, SUBCATCONFIG, CATEGORYCONFIG, CERTIFICATESCONFIG, AWARDSCONFIG, ENV, appHelperService, windowHelperService, tracking) {\n var apiServer = 'https://api-router.kaspersky-labs.com';\n var config = {\n 'serverUrl': ENV.apiServer,\n 'searchUrl': apiServer + '/contentrepository/docs/query',\n 'headerUrl': 'content/' + ENV.locale + '/repository/headers/',\n 'blogUrl': 'content/' + ENV.locale + '/repository/blogs/',\n 'apiContentRepoPath': 'content/' + ENV.locale + '/repository/',\n 'resourcesB2BUrl': ENV.apiServer + '/content/' + ENV.locale + '/repository/resources/b2b-resources.json',\n 'resourcesB2CUrl': ENV.apiServer + '/content/' + ENV.locale + '/repository/resources/b2c-resources.json',\n 'resourcesAboutUrl': ENV.apiServer + '/content/' + ENV.locale + '/repository/resources/about-resources.json',\n 'defaultOrderBy': 'pub_start desc',\n 'defaultYears': [1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018]\n };\n\n return {\n 'buildCategoryName': buildCategoryName,\n 'buildSubCategoryName': buildSubCategoryName,\n 'getCategories': getCategories,\n 'getCategoryParams': getCategoryParams,\n 'getLinkMediaByPath': getLinkMediaByPath,\n 'getHeaderByName': getHeaderByName,\n 'getPathFromUrl': getPathFromUrl,\n 'getUserFriendlyUrl': getUserFriendlyUrl,\n 'getStrippedString': getStrippedString,\n 'getTrustAsHtml': getTrustAsHtml,\n 'getRandomizedImage': getRandomizedImage,\n 'getResources': getResources,\n 'getCategoriesFromObject': getCategoriesFromObject,\n 'getBlogByName': getBlogByName,\n 'populateLinkFields': populateLinkFields,\n 'populateRepositoryHeaderFields': populateRepositoryHeaderFields,\n 'populateBlogFields': populateBlogFields,\n 'populateBlogResourceFields': populateBlogResourceFields,\n 'populateResourcesFields': populateResourcesFields,\n 'setUrlToCorrectCategory': setUrlToCorrectCategory,\n 'search': search,\n 'getPageNumberFromUrl': getPageNumberFromUrl,\n 'getSelectorFromUrl': getSelectorFromUrl,\n 'getDateFromUrl': getDateFromUrl,\n 'setPaginationUrlValue': setPaginationUrlValue,\n 'setSelectorUrlValue': setSelectorUrlValue,\n 'setDateUrlValue': setDateUrlValue,\n 'formatSelectorForApp': formatSelectorForApp,\n 'getDefaultYears': getDefaultYears,\n 'setTrackPageView': setTrackPageView\n };\n\n function buildSubCategoryName(name, subCategories, repoType) {\n var config = void 0;\n switch (repoType) {\n case 'awards':\n config = AWARDSCONFIG;\n break;\n case 'certificates':\n config = CERTIFICATESCONFIG;\n break;\n default:\n config = SUBCATCONFIG;\n }\n\n return buildConfigCategoryName(name, subCategories, config);\n }\n\n function buildCategoryName(name, categories) {\n return buildConfigCategoryName(name, categories, CATEGORYCONFIG);\n }\n\n function getCategories() {\n return $http.get(config.serverUrl + '/content/' + ENV.locale + '/resources/Categories.json', { 'cache': true });\n }\n\n function getCategoryParams() {\n return $stateParams.category;\n }\n\n function getLinkMediaByPath(path) {\n path = decodeURIComponent('' + config.serverUrl + path);\n return $http.get(path, { 'cache': true });\n }\n\n function getHeaderByName(headerName) {\n var path = decodeURIComponent('' + config.headerUrl + headerName + '.json');\n return $http.get(path, { 'cache': true });\n }\n\n function getPathFromUrl(fileName) {\n fileName = fileName || $stateParams.path.replace(/_/g, '/') + '.json';\n var path = '';\n if (isIscSection()) {\n path = '/' + config.apiContentRepoPath + 'isc/';\n }\n if (isSmbSection()) {\n path = '/' + config.apiContentRepoPath + 'smb/';\n }\n if (isPrSection()) {\n path = '/' + config.apiContentRepoPath + 'pr/';\n fileName = fileName.replace(/_/g, '/');\n }\n if (isAboutSection()) {\n path = '/' + config.apiContentRepoPath + 'about/';\n }\n return path + fileName;\n }\n\n function getUserFriendlyUrl(url) {\n var fileName = url.substr(url.lastIndexOf('/') + 1).replace(/.json/g, '').toLowerCase();\n if (isPrSection()) {\n var values = url.split('/'),\n yearFolder = values[values.length - 2];\n return yearFolder + '_' + fileName;\n }\n return fileName;\n }\n\n function getStrippedString(str, length) {\n return str.substring(0, length);\n }\n\n function getTrustAsHtml(strHtml) {\n return $sce.trustAsHtml(strHtml);\n }\n\n function getRandomizedImage(image) {\n return imageManagerService.getRandomizedImage(image);\n }\n\n function getResources(repoType) {\n var path = '';\n if (repoType === 'smb' || repoType === 'vsb') {\n path = decodeURIComponent('' + config.resourcesB2BUrl);\n }\n if (repoType === 'about') {\n path = decodeURIComponent('' + config.resourcesAboutUrl);\n } else {\n path = decodeURIComponent('' + config.resourcesB2CUrl);\n }\n return $http.get(path, { 'cache': true });\n }\n\n function getCategoriesFromObject(obj, array, repoType) {\n var tempArray = [],\n filteredCategories = void 0;\n array = array || [];\n var categoriesWithSubCat = {};\n for (var key in obj) {\n if (obj.hasOwnProperty(key)) {\n categoriesWithSubCat[key] = obj[key];\n tempArray.push(key);\n }\n }\n\n filteredCategories = removedUnusedCategories(tempArray, repoType);\n\n if (ENV.locale === 'ja-jp' && repoType === 'pr') filteredCategories = reorderJPCategories(filteredCategories);\n\n array = array.concat(filteredCategories ? filteredCategories : tempArray);\n\n return { 'categoriesList': array, 'categoriesWithSubCat': categoriesWithSubCat };\n }\n\n /**\n * Temporary function to reorder JP categories until they're done in Tridion\n * @param {[Array]} array The categories to sort\n * @return {[Array]} The sorted categories\n */\n function reorderJPCategories(array) {\n var order = ['製品ニュース', 'ウイルスニュース', 'スパムニュース', '比較テスト', 'ビジネスニュース', 'イベントカレンダー', 'お知らせ'];\n\n return array.sort(function (a, b) {\n return order.indexOf(a) - order.indexOf(b);\n });\n }\n\n function getBlogByName(blogName) {\n var path = decodeURIComponent('' + config.blogUrl + blogName + '.json');\n return $http.get(path, { 'cache': true });\n }\n\n function populateLinkFields(linkResponse, url) {\n return repoMapperService.mapRepositoryLinkFields(linkResponse, url);\n }\n\n function populateRepositoryHeaderFields(linkResponse) {\n return repoMapperService.mapRepositoryHeaderFields(linkResponse);\n }\n\n function populateBlogFields(linkResponse) {\n return repoMapperService.mapBlogFields(linkResponse);\n }\n\n function populateBlogResourceFields(resource) {\n return repoMapperService.mapBlogResourceFields(resource);\n }\n\n function populateResourcesFields(resource) {\n return repoMapperService.mapResourcesFields(resource);\n }\n\n function setUrlToCorrectCategory(category) {\n var url = $window.location.href;\n var lm = url.split('/').reverse()[1];\n var path = $window.location.pathname;\n if (lm !== category) {\n if (lm === '') {\n lm = '//';\n return $window.location.href = windowHelperService.getDomainName() + path.replace(lm, '/' + category + '/');\n }\n return $window.location.href = windowHelperService.getDomainName() + path.replace(lm, category);\n }\n }\n\n function search() {\n var pageNumber = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;\n var pageSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10;\n var repoType = arguments[2];\n var contentTypes = arguments[3];\n var category = arguments[4];\n var subcategory = arguments[5];\n var startDate = arguments[6];\n var rawSelect = arguments[7];\n\n var params = buildSearchParams(pageNumber, pageSize, repoType, contentTypes, category, subcategory, startDate, rawSelect);\n return $http.post(config.searchUrl, params, { 'withCredentials': true });\n }\n\n function getPageNumberFromUrl() {\n return $stateParams.rel || 1;\n }\n\n function getSelectorFromUrl() {\n return formatSelectorForApp($stateParams.sel || '');\n }\n\n function getDateFromUrl() {\n return formatSelectorForApp($stateParams.date || '');\n }\n\n function setPaginationUrlValue(value) {\n $location.search('rel', value);\n }\n\n function setSelectorUrlValue(selector) {\n selector = selector.toLowerCase().indexOf('sort-by') === -1 ? selector.toLowerCase() : '';\n $location.search('sel', selector);\n }\n\n function setDateUrlValue(date) {\n $location.search('date', date);\n }\n\n function formatSelectorForApp(value) {\n return value ? value.replace(/-_/g, '**').replace(/-/g, ' ').replace('**', '-') : '';\n }\n\n // Local helper functions\n function buildSearchParams(pageNumber, pageSize) {\n var repoType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';\n var contentTypes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];\n var category = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '';\n var subcategory = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : '';\n var startDate = arguments[6];\n var rawSelect = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : [];\n\n if (contentTypes === '') contentTypes = [];\n contentTypes = angular.isArray(contentTypes) ? contentTypes : contentTypes.split(',');\n\n var optionsObj = {\n 'locale': '' + ENV.locale,\n 'page_num': parseInt(pageNumber),\n 'page_size': parseInt(pageSize),\n 'repository_type': repoType,\n 'order_by': config.defaultOrderBy,\n 'raw_select': rawSelect\n };\n\n var categoryValues = splitAndFormatWithConfigNumbers(category, buildCategoryName);\n var subCategoryValues = splitAndFormatWithConfigNumbers(subcategory, buildSubCategoryName, repoType);\n\n if (contentTypes && contentTypes.length) {\n angular.extend(optionsObj, { 'contenttype': { 'select': 0, 'value': contentTypes } });\n }\n\n if (categoryValues && categoryValues.length) {\n angular.extend(optionsObj, { 'category': { 'select': 0, 'value': categoryValues } });\n }\n\n if (subCategoryValues && subCategoryValues.length) {\n angular.extend(optionsObj, { 'subcategory': { 'select': 0, 'value': subCategoryValues } });\n }\n\n if (rawSelect && rawSelect.length) {\n angular.extend(optionsObj, { 'raw_select': rawSelect });\n }\n\n if (startDate) {\n var dateFrom = new Date('01/01/' + startDate).getTime(),\n dateTo = new Date('12/31/' + startDate).getTime();\n optionsObj.publication_start = '/Date(' + dateFrom + '+0300)/';\n optionsObj.publication_end = '/Date(' + dateTo + '+0300)/';\n }\n\n return optionsObj;\n }\n\n function buildConfigCategoryName(name, array, config) {\n array = array || config[ENV.locale.split('-')[1]] || config['global'];\n if (array.length < 1) return name;\n for (var i = 0, length = array.length; i < length; i++) {\n var subCat = array[i],\n splitByDoubleColon = subCat.split('::');\n if (splitByDoubleColon.length === 1) throw new Error('Array item is not in correct format');\n if (splitByDoubleColon[1] !== name) continue;\n name = subCat;\n break;\n }\n return name;\n }\n\n function removedUnusedCategories(categoriesArray, repoType) {\n var translatedCategories = [],\n locale = ENV.locale.split('-')[1];\n\n if (repoType === 'pr') translatedCategories = SUBCATCONFIG;\n\n if (translatedCategories[locale]) {\n var categoriesWithoutIds = _.map(translatedCategories[locale], function (category) {\n return category.substr(4);\n });\n return _.intersection(categoriesArray, categoriesWithoutIds);\n }\n\n return false;\n }\n\n function splitAndFormatWithConfigNumbers(obj, callback, repoType) {\n obj = obj === '' ? obj : obj.split(',');\n var result = [];\n angular.forEach(obj, function (value) {\n result.push(callback(value, null, repoType));\n });\n return result;\n }\n\n function isPrSection() {\n var states = ['press-releases', 'press-releases.details', 'in-the-news', 'in-the-news.details', 'press-center', 'press-center.details', 'rss-feeds', 'rss-feeds.details', 'homepage'];\n return appHelperService.assertStateByName(states);\n }\n\n function isSmbSection() {\n var states = ['resources-category.details', 'resources-category', 'smb-resources-category.details', 'smb-resources-category'];\n return appHelperService.assertStateByName(states);\n }\n\n function isIscSection() {\n var states = ['b2c-resource-center.category', 'b2c-resource-center.category-details'];\n return appHelperService.assertStateByName(states);\n }\n\n function isAboutSection() {\n var states = ['policy-blog', 'policy-blog-category', 'policy-blog-category.details'];\n return appHelperService.assertStateByName(states);\n }\n\n function getDefaultYears() {\n return config.defaultYears;\n }\n\n function setTrackPageView() {\n tracking.trackPageView($rootScope.kaspersky.pageName); // search tracking requirement\n }\n }\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n angular.module('kappGlobal.cookiesRequirement').component('cookiesRequirement', {\n 'bindings': {\n 'show': '@'\n },\n 'templateUrl': '/resources/template/html/modules/_shared/components/cookies-requirement/templates/cookies-requirement.html',\n 'controller': 'cookiesRequirementController'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n cookiesRequirementController.$inject = [\"$window\", \"cookieService\", \"cookiesRequirementService\"];\n angular.module('kappGlobal.cookiesRequirement').controller('cookiesRequirementController', cookiesRequirementController);\n\n function cookiesRequirementController($window, cookieService, cookiesRequirementService) {\n var ctrl = this;\n ctrl.show = false;\n ctrl.$onInit = activate;\n ctrl.accept = acceptRequirements;\n\n function activate() {\n getData();\n }\n\n function getData() {\n ctrl.show = !cookieService.get('kl.c.p');\n if (!ctrl.show) return false;\n\n return cookiesRequirementService.getContent().then(function (response) {\n ctrl.data = response;\n if (!ctrl.data.cookieDesc && !ctrl.data.cookieBtn) ctrl.show = false;\n });\n }\n\n function acceptRequirements() {\n var domain = $window.location.host.replace(/^(https?:\\/\\/)?(www)?/, '');\n ctrl.show = false;\n cookieService.set('kl.c.p', false, 30, domain);\n }\n }\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n cookiesRequirementService.$inject = [\"$q\", \"sessionStorageService\", \"errorService\", \"directiveData\"];\n angular.module('kappGlobal.cookiesRequirement').factory('cookiesRequirementService', cookiesRequirementService);\n\n function cookiesRequirementService($q, sessionStorageService, errorService, directiveData) {\n return {\n 'getContent': getContent\n };\n\n function handleRejection(rejection) {\n errorService.warn(rejection);\n }\n\n function getContent() {\n var cachedData = sessionStorageService.get('local[general-translations]'),\n deferred = $q.defer();\n\n if (cachedData) {\n deferred.resolve(cachedData);\n return deferred.promise.then(function (cachedData) {\n return cachedData.fields;\n }, function (error) {\n return deferred.reject(error);\n });\n }\n\n return directiveData.getLocal('general-translations').then(function (response) {\n return response.data.fields;\n }, handleRejection);\n }\n }\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n angular.module('kappGlobal.currencySelector').component('currencySelector', {\n 'bindings': {\n 'resource': '@'\n },\n 'templateUrl': '/resources/template/html/modules/_shared/components/currency-selector/templates/currency-selector.html',\n 'controller': 'currencySelectorController'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n currencySelectorController.$inject = [\"$rootScope\", \"$scope\", \"currencyManager\", \"appHelperService\", \"geoLocationManager\", \"ENV\"];\n angular.module('kappGlobal.currencySelector').controller('currencySelectorController', currencySelectorController);\n\n function currencySelectorController($rootScope, $scope, currencyManager, appHelperService, geoLocationManager, ENV) {\n var ctrl = this;\n ctrl.openCurrencyMenu = false;\n ctrl.showCurrencyMenu = ctrl.showCurrencyMenu || true;\n ctrl.showCurrencySelector = ctrl.showCurrencySelector || false;\n ctrl.currencies = getCurrencies();\n ctrl.setCurrency = setCurrency;\n ctrl.onMenuBlur = onMenuBlur;\n ctrl.$onInit = activate;\n ctrl.locale = ENV.locale;\n ctrl.geoIP = geoLocationManager.getUserGeoIp();\n\n var geoIpGlobalRule = ctrl.geoIP && ctrl.currencies.some(function (item) {\n return item.geoIP.toLowerCase() === ctrl.geoIP;\n }) && ctrl.geoIP !== 'fi';\n ctrl.hideCurrencySelector = ENV.locale === 'ru-ru' || geoIpGlobalRule && ctrl.locale === 'en-au';\n\n function activate() {\n\n ctrl.currenciesCount = 1;\n if (appHelperService.assertLocaleByName('en-in')) {\n ctrl.currenciesCount = 6;\n } else if (appHelperService.assertLocaleByName('ru-ru')) {\n ctrl.currenciesCount = 11;\n }\n ctrl.showCurrencySelectorCondition = ctrl.currencies.length > ctrl.currenciesCount;\n /**\n * Make sure we know of any buyblocks that appeared before currency-switcher by using $rootScope.buyblocksOnPage\n */\n handleCurrencySwitcherAppearance(false, $rootScope.buyblocksOnPage);\n }\n\n /**\n * The function checks whether the currency switcher should be shown or not\n * It's hidden by default every time you enter the state or the component activates and appears if buyblocks are present\n * premium-services state is the exception where it should be shown anyway (without buyblocks)\n * The function is invoked at component activation, state change or at buyblock mount (buyblockReady)\n * @param calledByBuyblock { boolean= } Truthy if we are at the state change, falsy if invoked by buyblock mount\n * @param forceBuyblocksOnPageFlag { boolean= } Forces true if true\n */\n function handleCurrencySwitcherAppearance(calledByBuyblock, forceBuyblocksOnPageFlag) {\n if (!forceBuyblocksOnPageFlag) {\n // At state change we should clear the buyblockOnPage flag, but if called by buyBlockReady it means buyblock is there\n if (calledByBuyblock) ctrl.buyblocksOnPage = true;else ctrl.buyblocksOnPage = false;\n } else {\n // forceBuyblocksOnPageFlag is truthy meaning buyblocks have appeared before then currency-selector appeared then\n ctrl.buyblocksOnPage = true;\n }\n\n if (!ctrl.buyblocksOnPage) {\n ctrl.showCurrencySelector = appHelperService.assertStateByName(['premium-service', 'premium-services']) && ctrl.showCurrencySelectorCondition;\n } else {\n ctrl.showCurrencySelector = ctrl.showCurrencySelectorCondition && !ctrl.hideCurrencySelector;\n }\n }\n\n function getCurrencies() {\n ctrl.currencies = currencyManager.getCurrencies();\n ctrl.currentCurrency = currencyManager.getCurrentCurrencyFromCache() || ctrl.currencies[0];\n return ctrl.currencies;\n }\n\n function setCurrency(item) {\n ctrl.currentCurrency = item;\n ctrl.openCurrencyMenu = false;\n return currencyManager.setCurrency(item);\n }\n\n function onMenuBlur() {\n ctrl.openCurrencyMenu = false;\n }\n\n var buyBlockReady = $rootScope.$on('buyBlockReady', function () {\n return handleCurrencySwitcherAppearance(true);\n });\n var stateChange = $rootScope.$on('$stateChangeSuccess', function () {\n return handleCurrencySwitcherAppearance(false);\n });\n\n $scope.$on('$destroy', handleScopeDestroyed);\n\n function handleScopeDestroyed() {\n buyBlockReady();\n stateChange();\n }\n }\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n angular.module('kappGlobal.customHtml').component('customHtml', {\n 'bindings': {\n 'resource': '@'\n },\n 'templateUrl': '/resources/template/html/modules/_shared/components/custom-html/templates/custom-html.html',\n 'controller': 'customHtmlController'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n customHtmlController.$inject = [\"directiveData\", \"ENV\", \"errorService\", \"$timeout\", \"$element\"];\n angular.module('kappGlobal.customHtml').controller('customHtmlController', customHtmlController);\n\n function customHtmlController(directiveData, ENV, errorService, $timeout, $element) {\n var ctrl = this;\n ctrl.data = {};\n ctrl.env = ENV.locale;\n ctrl.$onInit = activate;\n\n function activate() {\n getData();\n }\n\n function handleRejection(rejection) {\n errorService.warn(rejection);\n }\n\n function getData() {\n if (ctrl.resource && ctrl.resource.match(/\\//)) {\n ctrl.resource = ctrl.resource.replace(/cur-locale/, ctrl.env);\n return directiveData.get(ctrl.resource).then(function (response) {\n ctrl.data = response.data.fields;\n }, handleRejection);\n } else {\n return directiveData.getByUrl(ctrl.resource || 'custom-html').then(function (response) {\n ctrl.data = response.data.fields;\n $timeout(function () {\n $element.find('.accordion-section > li > h4, .ent-accordion .accordion-section .accordion-title').click(function () {\n $(this).parent().toggleClass('open');\n $(this).siblings().slideToggle();\n });\n });\n }, handleRejection);\n }\n }\n }\n})();\n'use strict';\n\n(function () {\n\n 'use strict';\n\n angular.module('kappGlobal.doubleBanner').component('doubleBanner', {\n 'bindings': {\n 'resource': '@',\n 'template': '@'\n },\n 'templateUrl': '/resources/template/html/modules/_shared/components/double-banner/templates/double-banner.html',\n 'controller': 'doubleBannerController'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n doubleBannerController.$inject = [\"directiveData\", \"$element\", \"$rootScope\", \"errorService\"];\n angular.module('kappGlobal.doubleBanner').controller('doubleBannerController', doubleBannerController);\n\n function doubleBannerController(directiveData, $element, $rootScope, errorService) {\n var ctrl = this,\n ngRender = $rootScope.$on('ngRender', runScripts);\n ctrl.$onInit = activate;\n\n function activate() {\n getData();\n }\n\n function getData() {\n return directiveData.getByUrl(ctrl.resource || 'double-banner').then(function (response) {\n ctrl.data = response.data.fields;\n }, handleRejection);\n }\n\n function handleRejection(rejection) {\n errorService.warn(rejection);\n }\n\n function runScripts() {\n var redHeight = $('.quick-menu .red-item').outerWidth(true);\n $element.find('.narrow-link a').css({ 'width': redHeight + 'px' });\n ngRender();\n }\n }\n})();\n'use strict';\n\n(function () {\n angular.module('kappGlobal.customCarousel').component('customCarousel', {\n 'bindings': {\n 'resource': '@'\n },\n 'templateUrl': '/resources/template/html/modules/_shared/components/custom-carousel/templates/custom-carousel.html',\n 'controller': 'customCarouselController'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n customCarouselController.$inject = [\"directiveData\", \"errorService\", \"$scope\", \"$element\", \"$rootScope\"];\n angular.module('kappGlobal.customCarousel').controller('customCarouselController', customCarouselController);\n\n function customCarouselController(directiveData, errorService, $scope, $element, $rootScope) {\n var ctrl = this;\n ctrl.data = {};\n ctrl.$onInit = activate;\n var slickConfig = {\n 'infinite': true,\n 'slidesToShow': 1,\n 'slidesToScroll': 1,\n 'dots': true,\n 'accessibility': false,\n 'draggable': false,\n 'nextArrow': '',\n 'prevArrow': '',\n 'responsive': [{\n 'breakpoint': 1023,\n 'settings': {\n 'slidesToShow': 3,\n 'slidesToScroll': 3,\n 'infinite': true,\n 'dots': true,\n 'arrows': false\n }\n }, {\n 'breakpoint': 769,\n 'settings': {\n 'slidesToShow': 2,\n 'slidesToScroll': 2,\n 'dots': true,\n 'arrows': false\n }\n }, {\n 'breakpoint': 480,\n 'settings': {\n 'slidesToShow': 1,\n 'slidesToScroll': 1,\n 'dots': true,\n 'arrows': false\n }\n }]\n };\n\n function activate() {\n getData();\n }\n\n function getData() {\n return directiveData.getByUrl(ctrl.resource || 'custom-carousel').then(function (response) {\n ctrl.data = response.data.fields;\n }, function (rejection) {\n errorService.warn(rejection);\n });\n }\n\n var ngRender = $rootScope.$on('ngRender', function () {\n startSlick();\n ngRender();\n });\n\n $scope.$on('$destroy', function () {\n stopSlick();\n });\n\n function startSlick() {\n $element.find('.carousel-body').slick(slickConfig);\n }\n\n function stopSlick() {\n $element.find('.slick-initialized').slick('unslick');\n }\n }\n})();\n'use strict';\n\n(function () {\n angular.module('kappGlobal.downloadBlockFeatured').component('downloadBlockFeatured', {\n 'templateUrl': '/resources/template/html/modules/_shared/components/download-block-featured/templates/download-block-featured.html',\n 'controller': 'sharedGetByNameController'\n });\n})();\n'use strict';\n\n(function () {\n 'use strict';\n\n angular.module('kappGlobal.downloadBlock').component('downloadBlock', {\n 'bindings': {\n 'productName': '@',\n 'productInfo': '<',\n 'productData': '<'\n },\n // 'templateUrl': '/resources/template/html/modules/_shared/components/downloads/templates/downloads.html',\n // The change is intentionally made to enable proper children (download-block) appearence detection\n 'template': '{{::$ctrl.productInfo.summary}}