February 25, 2020 at 3:17:18 PM UTC
VERIFY
Websites
Websites
Runtime: 3.4s
Share on Facebook or Twitter
On February 25, 2020 at 3:17:18 PM UTC, http://www.eurogrand.com/ was accessible when tested on AS24691 in Togo.

Failures

HTTP Experiment
null
DNS Experiment
null
Control
null

DNS Queries

Resolver:
41.207.188.88
Query:
IN A www.eurogrand.com
Engine:
system
Name
Class
TTL
Type
DATA
Answer IP Info
@
IN
CNAME
e25755.d.akamaiedge.net
Unknown AS
@
IN
A
23.212.109.49
Unknown AS
@
IN
A
23.212.109.56
Unknown AS

TCP Connections

23.212.109.56:80: succeeded
23.212.109.49:80: succeeded

HTTP Requests

URL
GET https://www.eurogrand.com/
Response Headers
Transfer-Encoding:
chunked
X-WPL-DATA:
VEc=,TE9NRQ==,dmhpZ2g=
Server:
Playtech Web Server
Last-Modified:
Tue, 18 Feb 2020 15:11:48 GMT
Connection:
Transfer-Encoding
ETag:
W/"5e4bfeb4-1b48d"
Cache-Control:
private, max-age=300
Date:
Tue, 25 Feb 2020 16:17:21 GMT
Content-Type:
text/html; charset=UTF-8
Response Body
<!doctype html>
<html class="aplication-html">
  <head>
    
<script>
  Playtech = {
    Variables: {
      aliasLocaleMappging: {"en_CA": "ca"},
      allowedCountries: [
        {"code": "AL", "phoneAreaCode": "+355"},
        {"code": "AD", "phoneAreaCode": "+376"},
        {"code": "AO", "phoneAreaCode": "+244"},
        {"code": "AG", "phoneAreaCode": "+1268"},
        {"code": "AR", "phoneAreaCode": "+54"},
        {"code": "AM", "phoneAreaCode": "+374"},
        {"code": "AW", "phoneAreaCode": "+297"},
        {"code": "AT", "phoneAreaCode": "+43"},
        {"code": "AZ", "phoneAreaCode": "+994"},
        {"code": "BS", "phoneAreaCode": "+1242"},
        {"code": "BH", "phoneAreaCode": "+973"},
        {"code": "BB", "phoneAreaCode": "+1246"},
        {"code": "BY", "phoneAreaCode": "+375"},
        {"code": "BM", "phoneAreaCode": "+1441"},
        {"code": "BO", "phoneAreaCode": "+591"},
        {"code": "BA", "phoneAreaCode": "+387"},
        {"code": "BN", "phoneAreaCode": "+673"},
        {"code": "CA", "phoneAreaCode": "+1"},
        {"code": "BQ", "phoneAreaCode": ""},
        {"code": "KY", "phoneAreaCode": "+1345"},
        {"code": "CL", "phoneAreaCode": "+56"},
        {"code": "CO", "phoneAreaCode": "+57"},
        {"code": "CK", "phoneAreaCode": "+682"},
        {"code": "CR", "phoneAreaCode": "+506"},
        {"code": "HR", "phoneAreaCode": "+385"},
        {"code": "CZ", "phoneAreaCode": "+420"},
        {"code": "CD", "phoneAreaCode": "+243"},
        {"code": "DM", "phoneAreaCode": "+1767"},
        {"code": "DO", "phoneAreaCode": "+1809"},
        {"code": "EC", "phoneAreaCode": "+593"},
        {"code": "EG", "phoneAreaCode": "+20"},
        {"code": "SV", "phoneAreaCode": "+503"},
        {"code": "FK", "phoneAreaCode": "+500"},
        {"code": "FO", "phoneAreaCode": "+298"},
        {"code": "FJ", "phoneAreaCode": "+679"},
        {"code": "FI", "phoneAreaCode": "+358"},
        {"code": "GY", "phoneAreaCode": "+592"},
        {"code": "GA", "phoneAreaCode": "+241"},
        {"code": "GM", "phoneAreaCode": "+220"},
        {"code": "GE", "phoneAreaCode": "+995"},
        {"code": "DE", "phoneAreaCode": "+49"},
        {"code": "GH", "phoneAreaCode": "+233"},
        {"code": "GI", "phoneAreaCode": "+350"},
        {"code": "GR", "phoneAreaCode": "+30"},
        {"code": "GL", "phoneAreaCode": "+299"},
        {"code": "GD", "phoneAreaCode": "+1473"},
        {"code": "GT", "phoneAreaCode": "+502"},
        {"code": "GN", "phoneAreaCode": "+224"},
        {"code": "GW", "phoneAreaCode": "+245"},
        {"code": "GG", "phoneAreaCode": "+44"},
        {"code": "GY", "phoneAreaCode": "+592"},
        {"code": "HT", "phoneAreaCode": "+509"},
        {"code": "HN", "phoneAreaCode": "+504"},
        {"code": "HU", "phoneAreaCode": "+36"},
        {"code": "IS", "phoneAreaCode": "+354"},
        {"code": "IN", "phoneAreaCode": "+91"},
        {"code": "ID", "phoneAreaCode": "+62"},
        {"code": "IE", "phoneAreaCode": "+353"},
        {"code": "IM", "phoneAreaCode": "+44"},
        {"code": "CI", "phoneAreaCode": "+225"},
        {"code": "JM", "phoneAreaCode": "+1876"},
        {"code": "JP", "phoneAreaCode": "+81"},
        {"code": "JE", "phoneAreaCode": "+441534"},
        {"code": "JO", "phoneAreaCode": "+962"},
        {"code": "KZ", "phoneAreaCode": "+7"},
        {"code": "KE", "phoneAreaCode": "+254"},
        {"code": "KW", "phoneAreaCode": "+965"},
        {"code": "KG", "phoneAreaCode": "+996"},
        {"code": "LA", "phoneAreaCode": "+856"},
        {"code": "LB", "phoneAreaCode": "+961"},
        {"code": "LS", "phoneAreaCode": "+266"},
        {"code": "LI", "phoneAreaCode": "+423"},
        {"code": "LR", "phoneAreaCode": "+231"},
        {"code": "LT", "phoneAreaCode": "+370"},
        {"code": "LU", "phoneAreaCode": "+352"},
        {"code": "MO", "phoneAreaCode": "+853"},
        {"code": "MK", "phoneAreaCode": "+389"},
        {"code": "MG", "phoneAreaCode": "+261"},
        {"code": "MW", "phoneAreaCode": "+265"},
        {"code": "MY", "phoneAreaCode": "+60"},
        {"code": "MV", "phoneAreaCode": "+960"},
        {"code": "ML", "phoneAreaCode": "+223"},
        {"code": "MT", "phoneAreaCode": "+356"},
        {"code": "MR", "phoneAreaCode": "+222"},
        {"code": "MU", "phoneAreaCode": "+230"},
        {"code": "MX", "phoneAreaCode": "+52"},
        {"code": "MD", "phoneAreaCode": "+373"},
        {"code": "MN", "phoneAreaCode": "+976"},
        {"code": "ME", "phoneAreaCode": "+382"},
        {"code": "MS", "phoneAreaCode": "+1664"},
        {"code": "MC", "phoneAreaCode": "+377"},
        {"code": "MA", "phoneAreaCode": "+212"},
        {"code": "MZ", "phoneAreaCode": "+258"},
        {"code": "NA", "phoneAreaCode": "+264"},
        {"code": "NL", "phoneAreaCode": "+31"},
        {"code": "NC", "phoneAreaCode": "+687"},
        {"code": "NZ", "phoneAreaCode": "+64"},
        {"code": "NI", "phoneAreaCode": "+505"},
        {"code": "NE", "phoneAreaCode": "+227"},
        {"code": "NG", "phoneAreaCode": "+234"},
        {"code": "NF", "phoneAreaCode": "+672"},
        {"code": "NO", "phoneAreaCode": "+47"},
        {"code": "OM", "phoneAreaCode": "+968"},
        {"code": "PK", "phoneAreaCode": "+92"},
        {"code": "PA", "phoneAreaCode": "+507"},
        {"code": "PG", "phoneAreaCode": "+675"},
        {"code": "PY", "phoneAreaCode": "+595"},
        {"code": "PE", "phoneAreaCode": "+51"},
        {"code": "PT", "phoneAreaCode": "+351"},
        {"code": "QA", "phoneAreaCode": "+974"},
        {"code": "RE", "phoneAreaCode": "+262"},
        {"code": "RO", "phoneAreaCode": "+40"},
        {"code": "RS", "phoneAreaCode": "+381"},
        {"code": "RW", "phoneAreaCode": "+250"},
        {"code": "SM", "phoneAreaCode": "+378"},
        {"code": "BL", "phoneAreaCode": ""},
        {"code": "KN", "phoneAreaCode": "+1869"},
        {"code": "LC", "phoneAreaCode": "+1758"},
        {"code": "MF", "phoneAreaCode": ""},
        {"code": "PM", "phoneAreaCode": "+508"},
        {"code": "VC", "phoneAreaCode": "+1784"},
        {"code": "ST", "phoneAreaCode": "+239"},
        {"code": "SN", "phoneAreaCode": "+221"},
        {"code": "SC", "phoneAreaCode": "+248"},
        {"code": "SL", "phoneAreaCode": "+232"},
        {"code": "SG", "phoneAreaCode": "+65"},
        {"code": "SX", "phoneAreaCode": ""},
        {"code": "SK", "phoneAreaCode": "+421"},
        {"code": "SB", "phoneAreaCode": "+677"},
        {"code": "SO", "phoneAreaCode": "+252"},
        {"code": "ZA", "phoneAreaCode": "+27"},
        {"code": "KR", "phoneAreaCode": "+82"},
        {"code": "LK", "phoneAreaCode": "+94"},
        {"code": "SD", "phoneAreaCode": "+249"},
        {"code": "SR", "phoneAreaCode": "+597"},
        {"code": "SJ", "phoneAreaCode": "+47"},
        {"code": "TW", "phoneAreaCode": "+886"},
        {"code": "TJ", "phoneAreaCode": "+992"},
        {"code": "TZ", "phoneAreaCode": "+255"},
        {"code": "TO", "phoneAreaCode": "+676"},
        {"code": "TT", "phoneAreaCode": "+1868"},
        {"code": "TN", "phoneAreaCode": "+216"},
        {"code": "TM", "phoneAreaCode": "+993"},
        {"code": "TC", "phoneAreaCode": "+1649"},
        {"code": "UZ", "phoneAreaCode": "+998"},
        {"code": "VE", "phoneAreaCode": "+58"},
        {"code": "VN", "phoneAreaCode": "+84"},
        {"code": "EH", "phoneAreaCode": "+212"},
        {"code": "ZM", "phoneAreaCode": "+260"},
        {"code": "ZW", "phoneAreaCode": "+263"}
      ],
      allowedCurrencies: ["EUR","GBP","USD","CHF","CAD"],
      allowedLocales: ["de_DE","en_CA","en_GB","ja_JP","eu_EU"],
      animationConfig: {"mobile":{"animationEnabled":true,"gamesinfoComingUp":true,"gamesinfoRearrange":true,"gamesinfoFavorites":false,"gamesinfoSearch":false,"listItemPress":false,"inputPlaceholderTransition":false,"navBarHover":false,"buttonPress":true,"accordionExpandCollapse":false,"navBetweenPageLoader":false},"tablet":{"animationEnabled":true,"gamesinfoComingUp":true,"gamesinfoRearrange":true,"gamesinfoFavorites":false,"gamesinfoSearch":false,"listItemPress":false,"inputPlaceholderTransition":false,"navBarHover":false,"buttonPress":true,"accordionExpandCollapse":false,"navBetweenPageLoader":false},"desktop":{"animationEnabled":true,"gamesinfoComingUp":true,"gamesinfoRearrange":true,"gamesinfoFavorites":false,"gamesinfoSearch":false,"listItemPress":false,"inputPlaceholderTransition":false,"navBarHover":true,"buttonPress":true,"accordionExpandCollapse":false,"navBetweenPageLoader":false}},
      cashierUrlTemplates: [],
      casino: "eurogrand",
      casinoName: "eurogrand",
      casinoSkin: "eurogrand",
      cdnUrl: "",
      chatJsUrl: "",
      chatCssUrl: "",
      clientType: "casino",
      clientVersion: "",
      companyLogo: "\/library\/logo.png",
      cookiesConfiguration: {
        "commonCookieExpiration": 365,
        "commonCookieName": "",
        "cookieSettingsPage": "",
        "cookiesInformationWC": "COOKIE\-CONFIRMATION",
        "enableCookiesAdmission": true,
        "enablePresistentCookieLayer": true,
        "consentCookies": [
{
  "name": "confirmation_cookie",
  "readOnly": false,
  "defaultState": false,
  "expiration": 365
}]
      },
      ezPushConfiguration: {
        "ezPushEnabled": false,
        "ezPushApplicationName": "",
        "ezPushPathToFile": "",
        "ezPushSharePlayerData": false, 
             },
      azureConfiguration: {
        "azureInstrumentationKey": "",
             },
      countryDetectionType: "geoip",
      dataMapperEndpointURL: "",
      defaultBingoRegion: "EU",
      defaultCountry: "DE",
      defaultCurrency: "EUR",
      defaultLocale: "eu_EU",
      detailsRecovery: { isRequiredVerificationEnable: false, useEmailOnly: false },
      directCallDomain: "eurogrand.com",
      dynamicCurrencyTags: {},
      emailVerificationParam: "",
      enableChangeLangPopup: false,
      enableGoogleAnalyticsOAPIEvent: false,
      enableLangDetection: true,
      endpointURL: "https:\/\/pgg.bit.ptecha.io\/echo",
      galaxyClientType: "casino",
      galaxyClientVersion: "",
      GoogleAnalyticsTrackerId: "",
      isAdminNode: false,
      isAutoLoginEnabled: false,
      isEnablePasswordSpy: true,
      isJsLoggingEnabled: true,
      isUsernameInUppercase: false,
      langDetectionType: "browser",
      modifiedDate: "1582038708367",
      mwsAppServerSystemID: "",
      mwsAppServerURL: "",
      openApiDomain: "https:\/\/oapi.digitalarea1000.com:443",
      openApiFallbackUrl: "https:\/\/oapi.digitalarea1000.com:443",
      openApiReconnectAttempts: "3",
      openApiReconnectionDelay: "5000",
      orgTags: [],
      paypalMerchantValue: "",
      pkpassEndpointURL: "",
      qrEndpointURL: "",
      realMode: 1,
      regulation: "",
      rememberMe: "local_storage",
      removeRememberMeOnLogout: false,
      rootPage: "\/home",
      sessionSubscriptionBalances: "withdrawable_balance, pending_withdrawals, real_gaming_balance, casino_gaming_balance, casino_table_real_balance, bonus_balance, bonus_pending_winnings, ringfenced_real_balance, broken_games_real_balance, broken_games_bonus_balance, total_bonus_balance, free_spin_balance",
      sessionSubscriptionTypes: "alert,bonus,bonus:2,rg\-timer,rg:dep\-cool\-off",
      showInfoBtnForPromocodes: true,
      showSessionTicker: false,
      spanishAgeVerification: false,
      ssoPasDomain: "login.eurogrand.com",
      ssoPasJsURLs: "",
      ssoPasExternalJsUrl: "",
      termsMapping: [],
      timeFlyingPeriod: "60",
      useIMSClientApi: false,
      useMwsAppServerAPI: false,
      userSegmentURLType: "User_Segmentation",
      pgg2: {
        endpointUrl: "https:\/\/pgg.bit.ptecha.io\/v2",
        casino: "eurogrand",
        guestsTracking: true
      },
      inboxWidget: {
        enabled: false,
        showIcon: false,
        showLaunchIcon: false,
        counterPage: ""
      },
      chatWidget: {
        enabled: false,
        showIcon: false,
        showLaunchIcon: false,
        counterPage: ""
      },
      themeName: "eurogrand_eurogrand\-desktop\-theme"
    }
  }
</script>
    <script>
	Playtech.Events = {};

	Playtech._API = {
		listeners: [],

		backboneEventsMapping: {
			'COOKIE_REGULATION': 'cookieRegulation',
			'LOGGED_IN': 'auth:login',
			'LOGGED_IN_FAIL': 'user:failedLogin',
			'LOGGED_OUT': 'auth:logout',
			'REGISTRATION_COMPLETED': 'registration:success',
			'REGISTRATION_FAIL': 'registration:error',
			'REGISTRATION_STEP_CHANGED': 'registration:changeStep',
			'DEPOSIT_COMPLETED': 'deposit:success',
			'DEPOSIT_FAIL': 'deposit:fail',
			'WITHDRAWAL_COMPLETED': 'withdraw:success',
			'WITHDRAWAL_FAIL': 'withdraw:fail',
			'USER_PAYMENT_STATISTICS_UPDATED': 'user:updatePaymentStatistics',
			'USER_BALANCE_UPDATED': 'user:balanceUpdated',
			'FORGOT_USERNAME_SUCCESS': 'forgotUsername:success',
			'FORGOT_USERNAME_FAIL': 'forgotUsername:fail',
			'FORGOT_PASSWORD_SUCCESS': 'forgotPassword:success',
			'FORGOT_PASSWORD_FAIL': 'forgotPassword:fail',
			'SAFECHARGE_ANALYTICS': 'safecharge:analytics',
			'SAFECHARGE_PM_REGISTRATION_FAIL': 'safecharge:failedRegistration',
			'SAFECHARGE_PM_REGISTRATION_SUCCESS': 'safecharge:successfulRegistration',
			'SAFECHARGE_CONTACT_SUPPORT': 'safecharge:contactSupport',
			'SAFECHARGE_ALTERNATIVE_VERIFICATION': 'safecharge:alternativeVerification',
			'SAFECHARGE_WITHDRAWAL_SUCCESS': 'safecharge:successfulWithdrawalRequest',
			'SAFECHARGE_WITHDRAWAL_FAIL': 'safecharge:failedWithdrawalRequest',
			'GAME_PORTLET_PERSONALIZED_GRID': 'gamePortletPersonalizedGrid',
			'GAME_PORTLET_DISPLAY': 'gamePortletDisplay',
			'GAME_PORTLET_SEARCH': 'gamePortletSearch',
			'GAME_PORTLET_PAGE': 'gamePortletPage',
			'GAME_PORTLET_VIEW': 'gamePortletView',
			'GAME_PORTLET_CATEGORY_CHANGE': 'gamePortletCategoryChange',
			'GAME_PORTLET_SORT': 'gamePortletSort',
			'GAME_FAVORITES_HANDLER': 'gameFavoritesChange',
			'GAME_DISPLAY': 'gameGameDisplay',
			'GAME_LAUNCH': 'gameGameLaunch'
		},

		reactEventsMapping: {
			'RESPONSIVE_GAME_PORTLET_DISPLAY': 'games:mount',
			'RESPONSIVE_GAME_PORTLET_CATEGORY_SWITCH': 'game:category:switch',
			'RESPONSIVE_GAME_PORTLET_CATEGORY_EXPAND': 'game:category:expand',
			'RESPONSIVE_GAME_PORTLET_CATEGORY_SCROLL': 'game:category:activeSlide:change',
			'RESPONSIVE_GAME_PORTLET_PERSONALIZED_GRID_2': 'games:personalizedGrid:2',
			'RESPONSIVE_GAME_PORTLET_SEARCH': 'game:search:change',
			'GAME_FAVORITES_HANDLER': 'gameFavoritesChange',
			'GAME_LAUNCH': 'gameGameLaunch'
		}
	};

	Playtech.on = function (event, callback) {
		Playtech._API.listeners.push({
			type: 'on',
			event: event,
			callback: callback
		});
	};

	Playtech.once = function (event, callback) {
		Playtech._API.listeners.push({
			type: 'once',
			event: event,
			callback: callback
		});
	};

	(function () {
		for (var event in Playtech._API.backboneEventsMapping) {
			Playtech.Events[event] = event;
		}

		for (var event in Playtech._API.reactEventsMapping) {
			Playtech.Events[event] = event;
		}
	})();
</script>
    <script>
// Do not remove. Close redirect payment methods after execution. 
// https://portal-jira.playtech.corp/browse/WHO-4347
if (~window.location.pathname.indexOf('deposit-redirect-payment-method')) {
window.opener && window.opener.postMessage('deposit-redirect-payment-method' + window.location.search, window.location.origin);
setTimeout(function()
{ window.close(); }
, 0);
}
</script>
<script type="text/javascript">
 window.isNotSuggested = true;
</script>

<meta name="google" content="notranslate" />

<meta content="R6wlAIfi8dNR0bohV_PJWagRUQrPVj2PNX-gFkJwbj0" name="google-site-verification" />
 <!--Marketing Context Tracking mounting START -->
<script src="https://static.staticcache.org/js/marketing-context-tracking-v1.0.0.js" type="text/javascript" onload="MarketingContextTracking.lightweightMount({ env: 'prod' });"></script>
 <!--Marketing Context Tracking mounting END -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta charset="UTF-8">
        <title></title>
            <script>
        if (Playtech.Variables.azureConfiguration && Playtech.Variables.azureConfiguration.azureInstrumentationKey) {
          var sdkInstance="appInsightsSDK";window[sdkInstance]="appInsights";var aiName=window[sdkInstance],aisdk=window[aiName]||function(e){
            function n(e){t[e]=function(){var n=arguments;t.queue.push(function(){t[e].apply(t,n)})}}var t={config:e};t.initialize=!0;var i=document,a=window;setTimeout(function(){var n=i.createElement("script");n.src=e.url||"https://az416426.vo.msecnd.net/next/ai.2.min.js",i.getElementsByTagName("script")[0].parentNode.appendChild(n)});try{t.cookie=i.cookie}catch(e){}t.queue=[],t.version=2;for(var r=["Event","PageView","Exception","Trace","DependencyData","Metric","PageViewPerformance"];r.length;)n("track"+r.pop());n("startTrackPage"),n("stopTrackPage");var s="Track"+r[0];if(n("start"+s),n("stop"+s),n("setAuthenticatedUserContext"),n("clearAuthenticatedUserContext"),n("flush"),!(!0===e.disableExceptionTracking||e.extensionConfig&&e.extensionConfig.ApplicationInsightsAnalytics&&!0===e.extensionConfig.ApplicationInsightsAnalytics.disableExceptionTracking)){n("_"+(r="onerror"));var o=a[r];a[r]=function(e,n,i,a,s){var c=o&&o(e,n,i,a,s);return!0!==c&&t["_"+r]({message:e,url:n,lineNumber:i,columnNumber:a,error:s}),c},e.autoExceptionInstrumented=!0}return t
          }({
            instrumentationKey: Playtech.Variables.azureConfiguration.azureInstrumentationKey,
            namePrefix: "portal",
            isStorageUseDisabled: true
          });

          window[aiName]=aisdk,aisdk.queue&&0===aisdk.queue.length&&aisdk.trackPageView({});
        }
        </script>
        <!-- For IE Mobile -->
<meta name="msapplication-tap-highlight" content="no"/>
<meta name="apple-mobile-web-app-capable" content="no"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
<meta name="apple-touch-fullscreen" content="no"/>
<style>html { font: 80% 'Helvetica Neue', Arial; color: #555555; }</style>
<!--[if lt IE 9]>
<script type="text/javascript">alert('Sorry. This browser is not supported. Please use Google Chrome, Mozilla Firefox or Internet Explorer 9+');</script>
<![endif]-->
<script>if (!window.console) { window.console = {}; window.console.log = window.console.info = window.console.error = function () {}; }</script>
<script>
  window.ScaleManager = {

    _isIE: window.navigator.userAgent.match(/(msie|Edge|trident(?=\/))\/?\s*(\d+)/i),

    _getInitialParameters: function() {
        window._originInnerWidth = window.innerWidth;
        window._originClientWidth = document.documentElement.clientWidth;
        window._originFontSize = parseFloat(window.getComputedStyle(document.querySelector('html')).fontSize);
    },

    _backScale: function() {
        var defaultPixelSize = 16;
        var isClientWidthTheSame = window._originClientWidth === document.documentElement.clientWidth;
        var isInnerWidthTheSame = window._originInnerWidth === window.innerWidth;
        var scale = window.Viewport ? window.Viewport.getViewportValue() : 1;

        // get font size and round it to second digit (139 -> 140)
        var fontSize = Math.round(window._originFontSize / (defaultPixelSize / 10) / scale) * 10;

        if (!isClientWidthTheSame || !isInnerWidthTheSame && scale !== 1) {
            document.querySelector('html').style.fontSize = fontSize + '%';
        }
    }
    
};
</script>
<script>
(function() {
  if (window.ScaleManager._isIE) return;
  window.ScaleManager._getInitialParameters();
  /**
 * This script contains all viewport-related logic. It scales viewport when <head> section is parsed. Also it contains
 * objects and methods for device/browser detections required in context of this script's logic.
 *
 * Script will perform it's logic only when launched in top frame.
 *
 * The main strategy for viewport scaling is to make all devices to scale and report real physical resolution, except
 * those which has hi-resolution screens (e.g. Full HD+, Retina+). For the latest different strategies applies (e.g.
 * for Retina iPad scaling applies to make it report same resolution as non-retina iPad (1024px),
 * for Androids with Full HD - to make them report 1280px resolution). This is business requirement
 * as old NGM Games don't have hi-res layouts for games...
 *
 * See ViewportManager.createViewport() method for strategy details.
 *
 * See also: https://confluence.playtech.corp/display/MOBUA/Universal+Viewport+Solution
 * Presentation: http://prezi.com/b7bukfhy8xyz/universal-viewport-solution/
 * To understand concepts of visual/layout/ideal viewports see: http://www.quirksmode.org/mobile/viewports.html
 *
 * @author Andriy Halyasovskyy
 * @version 17.10.23
 * @email thebit@i.ua
 *
 * =====================================================================================================================
 * Release notes:
 *
 *  17.11.23:
 * - iPhoneX improve Scaling report
 *
 * 17.11.17:
 * - Added magic viewport (scale = 0.5) for iPhone X. For preventing zoom issues
 *
 * 17.10.11
 * - [NGM-149893] - [SGS8][UC browser] Corrupted portal scaling on Samsung Galaxy S8
 *
 * 17.09.08
 * - [NGM-146546] - [UC browser] CP elements are incorrectly scaled
 *
 * 17.07.06:
 * - Added support for Chrome being displayed not in a fullscreen on Samsung S8 device (after recent firmware updates).
 *   [NGM-137864] - [Samsung galaxy S8][Chrome] CP shifted to left
 *
 * 17.06.16:
 * - Fixed accidentally commented-out viewport solution initialization
 * - Added support for Galaxy S8 UC Browser
 *
 * 17.06.07:
 * - NGM-131163 - [CP][DEV] Add support for new devices
 *
 * 17.05.24
 * - NGM-99680 - [ViewportSolution] Support of Oppo R7c UC Browser
 *
 * 17.03.10
 * - NGM-117710 - UC Chinese 11.4 incorrect scaling
 *
 * 16.06.06
 * - Fixed incorrect condition in Device.getDPR() for Nexus 6 and UC Browser, now should be scaled correctly
 *
 * 16.06.01
 * - Fixed Xiaomi Mi4 LTE Default browser (Du) incorrect scaling with disabled T5 engine (NGM-69700)
 *
 * 16.05.26
 * - Fixed UC and UC CN browsers scaling on Nexus 5x, 6, 6P
 *
 * 16.05.25
 * - Added support for next devices and browsers (NGM-55399/NGM-67147):
 *       * Nexus 5x, Nexus 6P Chrome browser
 *       * Xiaomi Mi Note (4G) Chrome, Default, Native Wrapper, Opera, FF, QQ, Baidu, Du, Du HD, Du Mini,
 *           UC, UC CN, 360 browsers
 *       * Oppo R7c Android 4 Default browser and Oppo R7 Android 5 Default browser
 *       * Lenovo A850 UC Browser
 *
 * - Fixed CP not loading if cookies disabled in browser settings by wrapping localStorage call with try-catch
 *
 * - Fixed cross-domain issue, see NGM-57791
 * - Fixed Block screen appear during resizing game window in desktop Edge, see NGM-56342
 * - Fixed other issues, see NGM-53991, NGM-34539, NGM-34494
 * - .header renamed to .debugHeader in debug widget due to Hub on Betfair hides .header in bundle.css
 * - Added URL trigger param "noVP" which will disable all logic of this script leaving HTML page without viewport metatag
 *
 * 16.02.04
 * - Turning off solution for Desktop, see NGM-46827 Wrong page scale on Desktop IE10
 *
 * 15.12.01
 * - Added iPad Pro support, see NGM-39349
 *       Added "iPadProCorrectScale" URL param which will make iPad Pro to scale to more correct 1366 resolution instead of 1024
 *
 * - Minor improvement to workaround for Safari Standalone on iPhone iOS 8.3+ incorrect scale after rotation (and
 *   after reload) - now workaround won't be applied for iOS 9.0.1+ due to Apple fixed the bug:
 *   https://confluence.playtech.corp/pages/viewpage.action?pageId=108748385
 *
 * - Now Debug Widget will update its values with extra delayed run after rotation/resize due to Lumia's slow redraw
 *
 * 15.09.25
 * - Improved workaround for Viewport scale problem after first open on Lumia 635/820/920 in IE and Edge,
 *   see: NGM-23366, NGM-15558. Now "double tap" overlay won't be triggered upon zoom-in to input field in IE 11.
 *
 * 15.09.14
 * - Added support for Samsung Galaxy Tab S 10.5 Native Wrapper and Microsoft Surface Pro 3 Edge
 *
 * - Improved workaround for Safari Standalone on iPhone iOS 8.3+ incorrect scale after rotation (and after reload),
 *       see: NGM-8202, NGM-25292, NGM-23878.
 *       Refactored to detect when scaling issue happens and to reset viewport to initial-scale = 1.0 afterwards.
 *       Plus now debug widget will update viewport content display on resize/orientation-change as well.
 *
 * 15.08.04
 * - Removed debug URL trigger param for Debug Widget to not be easily displayed on Production, also
 *       to not impact autotesters, see NGM-21152 and NGM-20903
 * - Adjusted workaround for Viewport scale problem after first open for Lumia 820/920, WP 8.0 (GDR 3), see: NGM-15558
 *
 * 15.07.29
 * - Added workaround for Viewport scale problem after first open on Lumia 635/820/920 in IE and Edge, see: NGM-15558
 * - Added debug and debugInVP URL trigger params which will now display Debug Widget with useful info
 * - Fixed UC Browser HD incorrect scaling in Galaxy Tab 3 10.1, 4 10.1 and S 10.1, see: NGM-19784
 *
 * 15.07.08
 * - Temporary exposing back all objects to window even if in frame due to NGM Core (turned-out) is using some of them
 * - Added Viewport.getViewportValue() back for backward compatibility (for Hub)
 * - ScalingReport will now work correctly when inside iFrame (it will try to access own mirror instance in top frame)
 * - Added workaroundsOff and workaroundsOffInVP URL trigger params handling which are meant to disable any workarounds
 *
 * 15.06.22
 * - Refactored in context of memory and CPU utilization and organized better namespace management
 *
 * - Added workaround for Safari Standalone on iPhone iOS 8.3+ incorrect scale after rotation,
 *       see: NGM-8202 (iOS 9.0 beta also affected).
 *       Fixed by resetting viewport to initial-scale = 1.0 during first rotation.
 *
 * - iPhone 6+ Zoom Mode support added, see https://support.apple.com/en-us/HT203073 and NGM-13399
 *
 * - Added support for next devices and browsers:
 *       * Nexus 6
 *       * Windows Phone OS (8 and 10 preview) for at least Lumia 820, 920, 925 and
 *           IE (10 [except desktop mode!], 11, Edge) and also UCBrowser on Windows Phone.
 *           Now resolution on these devices will correspond to real one instead of 1024px in width (for both L & P)
 *       * Meizu (MX4, MX4 Pro, M1 Note) Default browser
 *       * Xiaomi Mi4 Default browser
 *
 * - Added ScalingReport object which can be used for automated testing or other purposes
 *       (e.g. ScalingReport.multiplicationRatio())
 *
 * 15.01.15
 * - Added support for Native Wrapper at Samsung Galaxy S4 LTE (GT-I9505)
 * - Added support for old Chrome 25 version
 * - Added support for UCBrowser
 *
 * 14.10.21
 * - Added support for iPhone 6 and 6 Plus
 *
 * 14.07.11
 * - Added support for all Android Native Wrappers
 *
 * 14.07.04
 * - Complete re-architecture to switch from exceptional target-dpi map to exceptional device map
 * - Added support for Webkit browsers at LG G2 and Samsung Note 3 Android 4.3
 *
 * 13.03.22
 * - Initial version
 */

/**
 * @returns {boolean} true if script running inside iFrame (no matter in which level), else otherwise
 */
function inIframe() {
    try {
        return window.self !== window.top;
    } catch (e) {
        return true;
    }
}

/**
 * @returns {boolean} true if script running at least inside 2-nd level iFrame, else otherwise
 */
function inIframeDeeply() {
    try {
        return window.self !== window.top && window.parent !== window.top;
    } catch (e) {
        return true;
    }
}

window.MagicViewportContent = {
    content1 : "target-densitydpi=320, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0",
    content2 : "user-scalable=no, initial-scale=1.0, maximum-scale=0.5, minimum-scale=0.5",
    content3 : "user-scalable=no, initial-scale=1.0, maximum-scale=0.75, minimum-scale=0.75",
    content4 : "user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0",
    content5 : "user-scalable=no, initial-scale=1, maximum-scale=0.5765, minimum-scale=0.5765",
    content6 : "target-densitydpi=152, user-scalable=no",
    content7 : "user-scalable=no, initial-scale=1.0, maximum-scale=0.5457, minimum-scale=0.5457",
    content8 : "target-densitydpi=293, user-scalable=no"
};

/*
 NB: When using RegExp constructor function, the normal string escape rules (preceding special characters with \
 when included in a string) are necessary. For example, the following are equivalent:
 var re = /\w+/;
 var re = new RegExp("\\w+");
 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp

 Also use these resources for developing/debugging regular expressions http://www.regexr.com and https://regex101.com
 */
window.Constants = {
    viewportSolutionVersion : "17.10.23",
    //Source: http://en.wikipedia.org/wiki/Comparison_of_Android_devices + iOS and Lumia devices
    // + 801 for Samsung Galaxy S & S2 as they report 534*1.5=801;
    // + 1281 for Samsung Galaxy Tab 3 10.1 UC Browser HD as it reports screen.width = 1281;
    // read about this constant in Screen.selectClosestResolution()
    mobileDevicesUniqueResolutions : [2960, 2732, 2560, 2436, 2220, 2160, 2048, 1920, 1480, 1440, 1334, 1281, 1280, 1136, 1024, 960, 854, 801, 800, 640, 480, 400, 320],
    highResScreenThreshold : 1920,
    maxSupportedResolution : 1280,
    maxiPhoneXSupportedResolution: 1624, // magic number for calculate scaling = 0.5
    maxIpadSupportedResolution : 1024,
    oldChromeVersionThreshold : 25, //read about this constant in Screen.convertFromDipsToPx()
    iPadProWidenessInDIPs : 1366, //screen width in DIPs
    iPadWidenessInDIPs : 1024, //screen width in DIPs
    iPhone6WidenessInPx : 1334, //physical screen wideness
    iPhone6PlusWidenessInPx : 1920, //physical screen wideness
    iPhone6PlusWidenessInDIPs : 736, //https://en.wikipedia.org/wiki/Device_independent_pixel, aka screen.width, aka ideal viewport width
    lumia920WidenessInPx : 1280, //IE 10 doesn't report window.devicePixelRatio, so this needed for Device.getDPR()
    lumia820WidenessInPx : 800,
    nexus6WidenessInPx : 2560,
    tab3_10_1WidenessInPx : 1280,
    SamsungS8WidenessInPx: 2220,
    tooStretchedAspectRatioThreshold : 4, //Exceeds 21:9 which is 2.33
    tooSquareAspectRatioThreshold : 1.3, //Exceeds 4:3 which is 1.33

    exceptionalDevices : {
        "(?:Windows(?!.+Phone)|WPDesktop|Macintosh|X11)" : "", //Disabling viewport for Desktop
        "Android 4\\.3.+SM-N900.+Version" : MagicViewportContent.content1, //Samsung Galaxy Note 3 Android 4.3 Webkit browser
        "GT-I9505.+Version(?!.+Chrome)" : MagicViewportContent.content1, //Samsung Galaxy S4 LTE Native Wrapper
        "HTC.One.+Version(?!.+Chrome)" : MagicViewportContent.content1, //HTC One Webkit browser
        "HTC.One.+Version.+Chrome" : MagicViewportContent.content2, //HTC One Wrapper
        "LG-D802.+Version" : MagicViewportContent.content2, //LG G2 Webkit browser
        "(?:MX4|m1 note).+Version" : MagicViewportContent.content1, //Meizu (MX4, MX4 Pro, M1 Note) Default browser
        "MI 4.+Version.+T5.+bdbrowser" : MagicViewportContent.content1, //Xiaomi Mi4 Default browser with T5 engine
        "MI NOTE LTE.+XiaoMi\/MiuiBrowser" : MagicViewportContent.content7, //XiaoMi Mi Note LTE Android 6.0.1 Default browser
        "MI NOTE LTE.+MQQBrowser" : MagicViewportContent.content8, //XiaoMi Mi Note LTE Android 6.0.1 QQ browser
        "MI NOTE LTE.+bdbrowser\/1" : MagicViewportContent.content8, //XiaoMi Mi Note LTE Android 6.0.1 Du HD browser
        "MI NOTE LTE.+bdbrowser" : MagicViewportContent.content7, //XiaoMi Mi Note LTE Android 6.0.1 Du browser
        "MI NOTE LTE.+baidubrowser" : MagicViewportContent.content7, //XiaoMi Mi Note LTE Android 6.0.1 Baidu browser
        "MI NOTE LTE.+Aphone browser" : MagicViewportContent.content7, //XiaoMi Mi Note LTE Android 6.0.1 Baidu browser
        "MI NOTE LTE.+Version.+Chrome" : MagicViewportContent.content7, //XiaoMi Mi Note LTE Android 6.0.1 Native wrapper
        "R7.+OppoBrowser" : MagicViewportContent.content2, //Oppo R7c, Android 4.4.4 Default browser
        "R7.+UCBrowser" : MagicViewportContent.content3,
        "R7.+Version.+Chrome" : MagicViewportContent.content1, //Oppo R7, Android 5.1.1 Default browser
        "Nexus 6\\s" : MagicViewportContent.content5, //Nexus 6
        "Lenovo_A850.+UCBrowser" : MagicViewportContent.content1, //Lenovo A850 UC Browser
        "SM-T80.+Version.+Chrome" : MagicViewportContent.content4, //Galaxy Tab S 10.5 Wrapper and Android Webkit
        "Android(?!.+Nexus (?:7|10))(?=.+Version\/4.0.+Chrome)(?!.+UCBrowser)" : MagicViewportContent.content2, //Android Native Wrapper except Nexus 7 and 10
        "Android.+Nexus 7.+Version.+Chrome" : MagicViewportContent.content3, //Android Native Wrapper exact in Nexus 7 2013
        "GT-P52.+Chrome.+UCBrowser" : MagicViewportContent.content6 //Galaxy Tab 3 10.1 UC Browser HD
    },

    DOM : {
        viewport : "viewport", //ID to assign to viewport tag
        viewportStyleTag : "cssViewportStyleTag" //ID to assign to viewport style tag (for IE)
    },

    i18n : {
        doubleTap : {
            "EN" : "Please double tap to continue.",
            "BG" : "Докоснете двукратно, за да продължите.",
            "CH" : "請點選兩下以繼續。",
            "CS" : "Pro pokračování dvakrát klepněte, prosím.",
            "DA" : "Tap to gange for at fortsætte.",
            "DE" : "Doppelklick um fortzufahren.",
            "ES" : "Pulse dos veces para continuar.",
            "ES-MX" : "Toque dos veces para continuar.",
            "FI" : "Napauta kahdesti jatkaaksesi.",
            "IT" : "Effettua un doppio tocco per continuare.",
            "JA" : "続けるにはダブルタップしてください。",
            "KO" : "두 번 탭하여 계속하세요.",
            "MS" : "Sila ketik dua kali untuk meneruskan.",
            "NL" : "Dibbeltik om door te gaan.",
            "RU" : "Пожалуйста, щелкните два раза для продолжения.",
            "SK" : "Pokračujte dvojitým ťuknutím.",
            "SV" : "Dubbeltryck för att fortsätta.",
            "ZH-CN" : "请双击以继续。"
        }
    }
};

/**
 * Minimum memory resources instance to report results of work done by this library and other potentially useful info
 */
window.ScalingReport = (function() {
    var storage = {
        scaleFactor : null,
        actualDPR : null,
        deviceScreenWidth : null,
        deviceScreenHeight : null,
        deviceScreenWideness : null,
        deviceScreenNarrowness : null,
        deviceScreenIsHighResolution : null,
        browserVersionRaw : null,
        browserVersionMajor : null,
        browserVersionMinor : null,
        browserVersionRawBase : null,
        browserVersionMajorBase : null,
        browserVersionMinorBase : null
    };

    return {
        scaleFactor: function (value) {
            if (!arguments.length) return storage["scaleFactor"];
            storage.scaleFactor = value;
        },

        actualDPR: function (value) {
            if (!arguments.length) return storage["actualDPR"];
            storage.actualDPR = value;
        },

        multiplicationRatio: function () {
            return this.actualDPR() * this.scaleFactor();
        },

        deviceScreenWidth: function (value) {
            if (!arguments.length) return storage["deviceScreenWidth"];
            storage.deviceScreenWidth = value;
        },

        deviceScreenHeight: function (value) {
            if (!arguments.length) return storage["deviceScreenHeight"];
            storage.deviceScreenHeight = value;
        },

        deviceScreenWideness: function (value) {
            if (!arguments.length) return storage["deviceScreenWideness"];
            storage.deviceScreenWideness = value;
        },

        deviceScreenNarrowness: function (value) {
            if (!arguments.length) return storage["deviceScreenNarrowness"];
            storage.deviceScreenNarrowness = value;
        },

        deviceScreenIsHighResolution: function (value) {
            if (!arguments.length) return storage["deviceScreenIsHighResolution"];
            storage.deviceScreenIsHighResolution = value;
        },

        browserVersionRaw: function (value) {
            if (!arguments.length) return storage["browserVersionRaw"];
            storage.browserVersionRaw = value;
        },

        browserVersionMajor: function (value) {
            if (!arguments.length) return storage["browserVersionMajor"];
            storage.browserVersionMajor = value;
        },

        browserVersionMinor: function (value) {
            if (!arguments.length) return storage["browserVersionMinor"];
            storage.browserVersionMinor = value;
        },

        browserVersionRawBase: function (value) {
            if (!arguments.length) return storage["browserVersionRawBase"];
            storage.browserVersionRawBase = value;
        },

        browserVersionMajorBase: function (value) {
            if (!arguments.length) return storage["browserVersionMajorBase"];
            storage.browserVersionMajorBase = value;
        },

        browserVersionMinorBase: function (value) {
            if (!arguments.length) return storage["browserVersionMinorBase"];
            storage.browserVersionMinorBase = value;
        }
    }
})();

/**
 * Mini jQuery
 */
var DOM = (function() {
    function getEl(elem) {
        return typeof elem === "string" ? DOM.get(elem) : elem;
    }

    return {
        get: function(elemID) {
            return document.getElementById(elemID);
        },

        /**
         * Warning! Not safe! Sets display always to "block"!
         * @param elemID
         */
        show: function(elemID) {
            getEl(elemID).style.display = "block";
        },

        hide: function(elemID) {
            getEl(elemID).style.display = "none";
        },

        text: function(elemID, value) {
            if (arguments.length == 1) {
                return getEl(elemID).innerText;
            } else {
                getEl(elemID).innerText = value;
            }
        },

        html: function(elemID, value) {
            if (arguments.length == 1) {
                return getEl(elemID).innerHTML;
            } else {
                getEl(elemID).innerHTML = value;
            }
        },

        add: function(elem, elemID, content) {
            var el = document.createElement(elem);
            elemID ? el.id = elemID : null;
            content ? el.innerHTML = content : null;
            document.body.appendChild(el);
            return el;
        },

        addStyle: function(content) {
            var styleTag = document.createElement("style");
            styleTag.type = "text/css";
            styleTag.innerHTML = content;
            document.getElementsByTagName("head")[0].appendChild(styleTag);
            return styleTag;
        },

        data: function(elemID, dataName, value) {
            if (arguments.length == 2) {
                return getEl(elemID).getAttribute("data-" + dataName);
            } else {
                getEl(elemID).setAttribute("data-" + dataName, value);
            }
        },

        css: function(elemID, styleName, value) {
            if (arguments.length == 2) {
                return getEl(elemID).style[styleName];
            } else {
                getEl(elemID).style[styleName] = value;
            }
        }
    };
})();

/**
 * Utilities for working with URL params, local storage, i18n, etc.
 */
var Util = (function() {
    var languageParam = "language";
    var langParam = "lang";
    var localStorageParamPrefix = "_url-param_";
    var localStorageViewportPrefix = "_viewport_";

    return {
        isUrlTriggerParamPresent: function(name) {
            return new RegExp("[?&]" + name + "(?:$|=|&)", "i").test(location.search);
        },

        getUrlParamValue: function(name) {
            var result = new RegExp("[?&]" + name + "=(?:(.+)&|(.+)$(?!&))", "i").exec(location.search);

            if (result) {
                for (var i = 1; i < result.length; i++) {
                    if (result[i]) {
                        return decodeURIComponent(result[i]);
                    }
                }
            }

            return null;
        },

        getLocalized: function(constant) {
            var result = constant[Util.getLang()];
            return result ? result : constant.EN;
        },

        getLang: function() {
            var result;

            if (Util.getUrlParamValue(languageParam)) {
                result = Util.getUrlParamValue(languageParam);
            } else if (Util.getUrlParamValue(langParam)) {
                result = Util.getUrlParamValue(langParam);
            } else if (Util.recallUrlParam(languageParam)) {
                result = Util.recallUrlParam(languageParam);
            } else if (Util.recallUrlParam(langParam)) {
                result = Util.recallUrlParam(langParam);
            } else {
                result = "EN";
            }

            return result.toUpperCase();
        },

        recallUrlParam: function(paramName) {
            try {
                if (localStorage) {
                    return localStorage.getItem(localStorageParamPrefix + paramName);
                }
            } catch (e) {
                Logger.logError("Can't use local storage, most probably cookies disabled in browser settings! " + e);
            }
        },

        memoItem: function(itemName, itemValue) {
            try {
                if (localStorage) {
                    localStorage.setItem(localStorageViewportPrefix + itemName, itemValue);
                }
            } catch (e) {
                Logger.logError("Can't use local storage, most probably cookies disabled in browser settings! " + e);
            }
        },

        recallItem: function(itemName) {
            try {
                if (localStorage) {
                    return localStorage.getItem(localStorageViewportPrefix + itemName);
                }
            } catch (e) {
                Logger.logError("Can't use local storage, most probably cookies disabled in browser settings! " + e);
            }
        },

        /**
         * Run function with small delay so that for example internal browser layout redrawing finishes after rotation.
         * @param func pointer to function to call to after delay
         */
        safeRun: function(func) {
            setTimeout(func, 300);
        },

        /**
         * Run function with longer delay so that for example double tap overlay for Lumia and subsequent fix (zoom
         * back to normal) are finished.
         * @param func pointer to function to call to after delay
         */
        delayedRun: function(func) {
            setTimeout(func, 1000);
        }
    };
})();

/**
 * [Won't be released by GC as contains orientation change handler]
 * Handles workarounds
 */
window.WorkaroundManager = function() {
    /**
     * Bug introduces layout distortion when Safari Standalone in iPhone iOS 8.3+ gets rotated
     * (and/or after rotation web page reload happens). At this moment window dimensions becomes twice larger
     * (and thus it looks twice zoomed-out) and so it can be detected.
     *
     * It also turned-out that setting viewport to 1.0 scale - fixes the problem. Don't ask me, ask Apple...
     */
    var WorkaroundForIncorrectScaleInStandaloneMode = function() {
        function scalingIssueDetected() {
            return Math.max(window.innerWidth, window.innerHeight) > Constants.iPhone6WidenessInPx;
        }

        function fixScalingIssueIfDetected() {
            if (scalingIssueDetected()) {
                DOM.get(Constants.DOM.viewport).setAttribute("content" , MagicViewportContent.content4);
            }
        }

        function init() {
            window.addEventListener("orientationchange", fixScalingIssueIfDetected, false);
            window.addEventListener("load", fixScalingIssueIfDetected, false);
        }

        init();
    };

    /**
     * Bug introduces layout distortion when window viewport (window.innerWidth/innerHeight) falls-down into
     * default device-specific dimension (1024x768 in landscape and 1024x1554 in portrait), meanwhile document
     * viewport stays consistent with what specified in @-ms-viewport CSS rule. This allows to detect the bug itself
     * and display "Please double tap to continue overlay" which workarounds the bug.
     *
     * In IE 11 new behaviour was introduced: zoom to input field, which initially made double tap overlay to trigger
     * also. To eliminate this - smart logic had been added to scalingIssueDetected method: now it checks if aspect
     * ratio stays in "normal" range (not too stretched and not too square), otherwise this is the case with on screen
     * keyboard displayed and zoomed in to input field, so double tap overlay doesn't showing anymore.
     */
    var WorkaroundForIncorrectScaleAfterFirstOpeningInIEAndEdge = function() {
        var styleForBG = null;
        var doubleTapOverlayID = "doubleTapOverlay";
        var doubleTapLabelContainerID = "doubleTapLabelContainerID";

        var normalBGStyle =
            "body {" +
            "-ms-touch-action: none !important;" +
            "touch-action: none !important;" +
            "}";

        var doubleTappableBGStyle =
            "body {" +
            "-ms-touch-action: double-tap-zoom !important;" +
            "touch-action: double-tap-zoom !important;" +
            "}";

        /**
         * Magic Div was found accidentally and it makes the layout distortion automatically to zoom to correct scale.
         * And the "speed" of this zoom depends on left: parameter with -15px is the slowest and -XXXXXpx fast enough
         * to be not noticeable.
         *
         * But there is still bug when user keeps rotating device - after returning from landscape back to landscape -
         * page will be zoomed-in much, and so "Please double tap to continue" overlay will still be required.
         *
         * Don't ask me, ask Microsoft...
         */
        function createDoubleTapOverlay() {
            styleForBG = DOM.addStyle(doubleTappableBGStyle);

            DOM.addStyle(
                "html {" +
                "overflow: inherit !important;" + //to override overflow:hidden in Hub which prevents magicDiv from fixing the bug
                "}" +

                ".magicDiv {" +
                "position: absolute;" + //no matter which element or where placed, just anything in the DOM with position
                // shifted to at least 15px to the left of the screen
                "left: -5000px;" + //this controls the speed of automatic resize/zoom-to-normal, with -15px is the slowest
                "width: 1px;" + //should have some minimal dimensions
                "}" +

                "#" + doubleTapOverlayID + " {" +
                "display: none;" +
                "z-index: 10000;" +
                "position: fixed;" +
                "width: 100%;" +
                "height: 100%;" +
                "background-color: rgba(0, 0, 0, .6);" +
                "color: white;" +
                "font-size: 36px;" +
                "}" +

                "#" + doubleTapOverlayID + " .table {" +
                "display: table;" +
                "width: 100%;" +
                "height: 100%;" +
                "}" +

                "#" + doubleTapOverlayID + " .table-cell {" +
                "display: table-cell;" +
                "vertical-align: middle;" +
                "text-align: center;" +
                "}");

            DOM.add("div").className = "magicDiv";

            DOM.add("div", doubleTapOverlayID, "<div id='" + doubleTapLabelContainerID + "' class='table'>" +
                "<div class='table-cell'>" + Util.getLocalized(Constants.i18n.doubleTap) + "</div></div>");
        }

        /**
         * Checks if window viewport is not of same size as document viewport, taking into account aspect ratio, which
         * should stay in "normal" range (not too stretched and not too square).
         *
         * @returns {boolean} true if scaling issue detected, false otherwise
         */
        function scalingIssueDetected() {
            var widerSide = Math.max(window.innerWidth, window.innerHeight);
            var narrowerSide = Math.min(window.innerWidth, window.innerHeight);
            var aspectRatio = widerSide / narrowerSide;

            return window.innerWidth != document.documentElement.clientWidth &&
                aspectRatio < Constants.tooStretchedAspectRatioThreshold &&
                aspectRatio > Constants.tooSquareAspectRatioThreshold;
        }

        function checkIfToDisplayOverlay() {
            if (scalingIssueDetected()) {
                DOM.css(doubleTapLabelContainerID, "width" , window.innerWidth + "px");
                DOM.css(doubleTapLabelContainerID, "height" , window.innerHeight + "px");

                DOM.show(doubleTapOverlayID);
                toggleGameContainerVisibility(true);
                styleForBG.innerHTML = doubleTappableBGStyle;
            } else {
                DOM.hide(doubleTapOverlayID);
                toggleGameContainerVisibility(false);
                styleForBG.innerHTML = normalBGStyle;
            }
        }

        /**
         * Extra workaround for IE Mobile 10. For some reason Double Tap overlay is not double tapping when game is
         * displayed. So the solution is to hide it when overlay is on the screen.
         *
         * @param isToHide if true - set parent element of a canvas to display: none, display: block otherwise
         */
        function toggleGameContainerVisibility(isToHide) {
            //This whole workaround object is already for IE and Edge so no need to double check browser itself
            if (Device.Browser.Version.getMajor() != 10) return;

            var gameContainer;
            if (searchFirstElInFrame(window, "canvas")) {
                //Platform frame level
                gameContainer = searchFirstElInFrame(window, "canvas").parentElement;
            } else if (searchFirstElInFrame(window.frames[0], "canvas")) {
                //Hub frame level
                gameContainer = searchFirstElInFrame(window.frames[0], "canvas").parentElement;
            }

            if (gameContainer) {
                isToHide ? DOM.hide(gameContainer) : DOM.show(gameContainer);
            }
        }

        function searchFirstElInFrame(windowContext, elem) {
            for (var i = 0; i < windowContext.frames.length; i++) {
                if (windowContext.frames[i].document.getElementsByTagName(elem)[0]) {
                    return windowContext.frames[i].document.getElementsByTagName(elem)[0];
                }
            }
        }

        function init() {
            window.addEventListener("load", function() { createDoubleTapOverlay(); Util.safeRun(checkIfToDisplayOverlay); }, false);
            window.addEventListener("resize", function() { Util.safeRun(checkIfToDisplayOverlay); }, false);

            if(window.PointerEvent) {//For IE11 and Edge
                window.addEventListener("pointerdown", function() { Util.delayedRun(checkIfToDisplayOverlay); });
            } else if (window.MSPointerEvent) {//For IE10
                window.addEventListener("MSPointerDown", function() { Util.delayedRun(checkIfToDisplayOverlay); });
            }
        }

        init();
    };

    function isWorkaroundsDisabled() {
        return Util.isUrlTriggerParamPresent("workaroundsOff") || Util.isUrlTriggerParamPresent("workaroundsOffInVP");
    }

    return {
        /**
         * Workaround for NGM-8202, NGM-23878, NGM-25292 - "Invalid viewport scale on iPhone Safari iOS 8.3+ homescreen"
         */
        workaroundForIncorrectScaleInStandaloneMode: function() {
            if (isWorkaroundsDisabled()) return;
            new WorkaroundForIncorrectScaleInStandaloneMode();
        },

        /**
         * Workaround for NGM-15558 - "Viewport scale problem after first open on Lumia 635/820/920 in IE and Edge"
         */
        workaroundForIncorrectScaleAfterFirstOpeningInIEAndEdge: function() {
            if (isWorkaroundsDisabled()) return;
            new WorkaroundForIncorrectScaleAfterFirstOpeningInIEAndEdge();
        }
    };
};

window.DebugWidget = function() {
    var debugWidgetID = "debugWidget";
    var debugPanelContainerID = "debugPanelContainer";
    var debugPanelID = "debugPanel";
    var debugPanelSwitchSideBtnID = "debugPanelSwitchSideBtn";
    var debugPanelExpandCollapseBtnID = "debugPanelExpandCollapseBtn";
    var debugPanelCloseBtnID = "debugPanelCloseBtn";
    var viewportResolution = "viewportResolution";
    var viewportResolution2 = "viewportResolution2";
    var outerWH = "outerWH";
    var screenWH = "screenWH";
    var screenAvailWH = "screenAvailWH";
    var docOffsetWH = "docOffsetWH";
    var docClientWH = "docClientWH";
    var orientationID = "orientationID";
    var removeWidgetBtnID = "removeWidgetBtn";
    var emptyVPBtnID = "emptyVPBtn";
    var restoreVPBtnID = "restoreVPBtn";
    var setVPBtnID = "setVPBtn";
    var reloadBtnID = "reloadBtn";
    var refreshBtnID = "refreshBtn";
    var arrowLeftSymbol = "<";
    var arrowRightSymbol = ">";
    var arrowTopSymbol = "^";
    var arrowBottomSymbol = "_";
    var widgetRemoved = "widgetRemoved";
    var widgetPosition = "widgetPosition";
    var widgetCollapseState = "widgetCollapseState";
    var vpContentID = "vpContent";

    var widgetPosY = 150; //Optimal position to start right after CP's Main Menu button to not overlap with it

    if (inIframe()) {
        widgetPosY = 185; //Shift CP's widget below to not overlap with Hub's one
    }

    if (inIframeDeeply()) {
        widgetPosY = 220; //Shift Game's widget below to not overlap with CP's one
    }

    function getViewportContent() {
        if (DOM.get(Constants.DOM.viewport)) {
            return DOM.get(Constants.DOM.viewport).getAttribute("content");
        } else if (DOM.get(Constants.DOM.viewportStyleTag) != null) {
            return DOM.html(Constants.DOM.viewportStyleTag);
        } else {
            return "Viewport tag was not created!";
        }
    }

    function getViewportResolution() {
        return window.innerWidth  + " x " + window.innerHeight;
    }

    function getScreenWH() {
        return screen.width + " x " + screen.height;
    }

    function getScreenAvailWH() {
        return screen.availWidth + " x " + screen.availHeight;
    }

    function getDocOffsetWH() {
        return document.documentElement.offsetWidth + " x " + document.documentElement.offsetHeight;
    }

    function getDocClientWH() {
        return document.documentElement.clientWidth + " x " + document.documentElement.clientHeight;
    }

    function getOuterWH() {
        return window.outerWidth + " x " + window.outerHeight;
    }

    /**
     * jQuery you say? Hah!
     */
    function createAndDisplay() {
        DOM.addStyle(
            "#" + debugWidgetID + " {" +
            "display: " + (Util.recallItem(widgetRemoved) && Util.recallItem(widgetRemoved) === "true" ? "none" : "block") + ";" +
            "-moz-box-sizing: border-box;" +
            "-webkit-box-sizing: border-box;" +
            "box-sizing: border-box;" +
            "z-index: 20000;" +
            "position: fixed;" +
            "top: " + widgetPosY + "px;" +
            "width: 120px;" +
            "height: 30px;" +
            "line-height: normal;" +
            "background-color: rgba(0, 0, 0, .6);" +
            "color: white;" +
            "font-size: 20px;" +
            "text-align: center;" +
            "padding: 3px;" +
            "border: 1px solid white;" +
            "}" +

            "#" + debugPanelContainerID + " {" +
            "display: none;" +
            "z-index: 20000;" +
            "position: fixed;" +
            "top: 0px;" +
            "padding-top: " + widgetPosY + "px;" +
            "padding-bottom: 1px;" +
            "width: " + "480px;" +
            "height: 100%;" +
            "line-height: 16px;" +
            "-moz-box-sizing: border-box;" +
            "-webkit-box-sizing: border-box;" +
            "box-sizing: border-box;" +
            "}" +

            "#" + debugPanelContainerID + " * {" +
            "-moz-box-sizing: border-box;" +
            "-webkit-box-sizing: border-box;" +
            "box-sizing: border-box;" +
            "}" +

            "#" + debugPanelID + " {" +
            "height: 100%;" +
            "background-color: rgba(0, 0, 0, .6);" +
            "color: white;" +
            "border: 1px solid white;" +
            "}" +

            "#" + debugPanelID + " .debugHeader {" +
            "width: 100%;" +
            "background-color: rgba(0, 0, 0, .2);" +
            "position: absolute;" +
            "}" +

            "#" + debugPanelID + " .debugScroller {" +
            "overflow: auto;" +
            "height: 100%;" +
            "padding: 5px;" +
            "padding-top: 45px;" +
            "font-size: 16px;" +
            "line-height: 1.5;" +
            "}" +

            "#" + debugPanelID + " button {" +
            "width: 80px;" +
            "height: 40px;" +
            "font-size: 20px;" +
            "line-height: 1;" +
            "background-color: rgba(0, 0, 0, .2);" +
            "border: 1px solid white;" +
            "color: white;" +
            "}" +

            "#" + debugPanelID + " .debugScroller button {" +
            "width: 100px;" +
            "height: 42px;" +
            "font-size: 18px;" +
            "line-height: 1;" +
            "background-color: rgba(0, 0, 0, .2);" +
            "border: 1px solid white;" +
            "color: white;" +
            "margin: 5px;" +
            "}" +

            "#" + debugPanelID + " .debugScroller input {" +
            "width: 100px;" +
            "height: 42px;" +
            "margin: 5px;" +
            "}" +

            "#" + debugPanelID + " span {" +
            "font-size: 16px;" +
            "margin-left: 5px;" +
            "}" +

            "#" + debugPanelID + " p {" +
            "font-size: 20px;" +
            "line-height: 1;" +
            "margin: 0;" +
            "padding: 0;" +
            "}" +

            ".debugScroller pre {" +
            "white-space: pre-wrap;" +
            "margin: 0;" +
            "padding: 0;" +
            "}" +

            ".debugScroller td {" +
            "background-color: transparent;" +
            "}");

        DOM.add("div", debugWidgetID, getViewportResolution());

        DOM.add("div", debugPanelContainerID,
            "<div id='" + debugPanelID + "'>" +
            "<div class='debugHeader'>" +
            "<button id='" + debugPanelSwitchSideBtnID + "' data-state='at-left'>" + arrowRightSymbol + "</button>" +
            "<button id='" + debugPanelExpandCollapseBtnID + "' data-state='collapsed'>" + arrowTopSymbol + "</button>" +
            "<button id='" + debugPanelCloseBtnID + "'>X</button>" +
            "<span>version: " + Constants.viewportSolutionVersion  + "</span>" +
            "<span id='" + viewportResolution + "'>(" + getViewportResolution()  + ")</span>" +
            "</div>" +

            "<div class='debugScroller'>" +
            "<button id='" + removeWidgetBtnID + "'>Remove this widget</button>" +
            "<button id='" + emptyVPBtnID + "'>Clear viewport</button>" +
            "<button id='" + restoreVPBtnID + "'>Restore viewport</button>" +
            "<button id='" + setVPBtnID + "'>Custom viewport</button>" +
            "<button id='" + reloadBtnID + "'>Reload</button>" +
            "<button id='" + refreshBtnID + "'>Refresh</button>" +
            "<input type='text'>" +

            (!inIframe() ? "<p>[Top frame]</p><br>" : inIframeDeeply() ?
                "<p>[2nd+ level frame]</p><br>" : "<p>[1st level frame]</p><br>") +

            "<p>Viewport is set to:</p><pre id='" + vpContentID + "'>" + getViewportContent() + "</pre><br>" +
            "<p>User-Agent is:</p><pre>" + navigator.userAgent + "</pre><br>" +

            "<table>" +
            "<tr><th colspan='2'>Browser properties:</th></tr>" +
            "<tr><td>screen.width x H: </td><td id='" + screenWH + "'>" + getScreenWH() + "</td></tr>" +
            "<tr><td>screen.availWidth x H: </td><td id='" + screenAvailWH + "'>" + getScreenAvailWH() + "</td></tr>" +
            "<tr><td>doc.docEl.offsetWidth x H: </td><td id='" + docOffsetWH + "'>" + getDocOffsetWH() + "</td></tr>" +
            "<tr><td>doc.docEl.clientWidth x H: </td><td id='" + docClientWH + "'>" + getDocClientWH() + "</td></tr>" +
            "<tr><td>window.innerWidth x H: </td><td id='" + viewportResolution2 + "'>" + getViewportResolution() + "</td></tr>" +
            "<tr><td>window.outerWidth x H: </td><td id='" + outerWH + "'>" + getOuterWH() + "</td></tr>" +
            "<tr><td>window.navigator.standalone: </td><td>" + window.navigator.standalone + "</td></tr>" +
            "<tr><td>window.devicePixelRatio: </td><td>" + window.devicePixelRatio + "</td></tr>" +
            "<tr><td>window.orientation: </td><td id='" + orientationID + "'>" + window.orientation + "</td></tr>" +

            "<tr><th colspan='2'>ScalingReport properties:</th></tr>" +
            "<tr><td>Viewport.getViewportValue(): </td><td>" + Viewport.getViewportValue() + "</td></tr>" +
            "<tr><td>ScalingReport.scaleFactor(): </td><td>" + ScalingReport.scaleFactor() + "</td></tr>" +
            "<tr><td>ScalingReport.actualDPR(): </td><td>" + ScalingReport.actualDPR() + "</td></tr>" +
            "<tr><td>ScalingReport.multiplicationRatio(): </td><td>" + ScalingReport.multiplicationRatio() + "</td></tr>" +
            "<tr><td>ScalingReport.deviceScreenWidth(): </td><td>" + ScalingReport.deviceScreenWidth() + "</td></tr>" +
            "<tr><td>ScalingReport.deviceScreenHeight(): </td><td>" + ScalingReport.deviceScreenHeight() + "</td></tr>" +
            "<tr><td>ScalingReport.deviceScreenWideness(): </td><td>" + ScalingReport.deviceScreenWideness() + "</td></tr>" +
            "<tr><td>ScalingReport.deviceScreenNarrowness(): </td><td>" + ScalingReport.deviceScreenNarrowness() + "</td></tr>" +
            "<tr><td>ScalingReport.deviceScreenIsHighResolution(): </td><td>" + ScalingReport.deviceScreenIsHighResolution() + "</td></tr>" +
            "</table>" +
            "</div>" +
            "</div>"
        );
    }

    function updateValues() {
        DOM.text(debugWidgetID, getViewportResolution());
        DOM.text(viewportResolution, "(" + getViewportResolution() + ")");
        DOM.text(viewportResolution2, getViewportResolution());
        DOM.text(screenWH, getScreenWH());
        DOM.text(screenAvailWH, getScreenAvailWH());
        DOM.text(docOffsetWH, getDocOffsetWH());
        DOM.text(docClientWH, getDocClientWH());
        DOM.text(outerWH, getOuterWH());
        DOM.text(orientationID, window.orientation);
        DOM.text(vpContentID, getViewportContent());
    }

    var storedVPContent = ""; //for restore button

    /**
     * Assuming that DOM is ready
     */
    function initAndAssignHandlers() {
        storedVPContent = getViewportContent();

        window.addEventListener("resize", function () { updateValues(); Util.safeRun(updateValues); }, false);
        window.addEventListener("orientationchange", function () { updateValues(); Util.safeRun(updateValues); }, false);

        DOM.get(removeWidgetBtnID).addEventListener("click", function () {
            DOM.hide(debugWidgetID);
            DOM.hide(debugPanelContainerID);
            Util.memoItem(widgetRemoved, true);
        }, false);

        DOM.get(emptyVPBtnID).addEventListener("click", function () {
            if (DOM.get(Constants.DOM.viewport)) {
                DOM.get(Constants.DOM.viewport).setAttribute("content" , "");
            } else if (DOM.get(Constants.DOM.viewportStyleTag) != null) {
                DOM.html(Constants.DOM.viewportStyleTag, "");
            }
        }, false);

        DOM.get(restoreVPBtnID).addEventListener("click", function () {
            if (DOM.get(Constants.DOM.viewport)) {
                DOM.get(Constants.DOM.viewport).setAttribute("content" , storedVPContent);
            } else if (DOM.get(Constants.DOM.viewportStyleTag) != null) {
                DOM.html(Constants.DOM.viewportStyleTag, storedVPContent);
            }
        }, false);

        DOM.get(setVPBtnID).addEventListener("click", function () {
            var customVPContent = prompt("Type custom viewport content", "width=device-width, initial-scale=1.0");
            if (!customVPContent) return;

            if (DOM.get(Constants.DOM.viewport)) {
                DOM.get(Constants.DOM.viewport).setAttribute("content" , customVPContent);
            } else if (DOM.get(Constants.DOM.viewportStyleTag) != null) {
                DOM.html(Constants.DOM.viewportStyleTag, customVPContent);
            }
        }, false);

        DOM.get(reloadBtnID).addEventListener("click", function () {
            window.location.reload();
        }, false);

        DOM.get(refreshBtnID).addEventListener("click", function () {
            updateValues();
        }, false);

        DOM.get(debugWidgetID).addEventListener("click", function () {
            DOM.hide(debugWidgetID);
            DOM.show(debugPanelContainerID);
        }, false);

        DOM.get(debugPanelCloseBtnID).addEventListener("click", function () {
            DOM.hide(debugPanelContainerID);
            DOM.show(debugWidgetID);
        }, false);

        function stickToRight() {
            DOM.html(debugPanelSwitchSideBtnID, arrowLeftSymbol);
            DOM.css(debugPanelContainerID, "right", "0");
            DOM.css(debugWidgetID, "right", "0");
        }

        function stickToLeft() {
            DOM.html(debugPanelSwitchSideBtnID, arrowRightSymbol);
            DOM.css(debugPanelContainerID, "right", "");
            DOM.css(debugWidgetID, "right", "");
        }

        if (Util.recallItem(widgetPosition)) {
            DOM.data(debugPanelSwitchSideBtnID, "state", Util.recallItem(widgetPosition));

            if (Util.recallItem(widgetPosition) == "at-right") {
                stickToRight();
            } else {
                stickToLeft();
            }
        }

        DOM.get(debugPanelSwitchSideBtnID).addEventListener("click", function () {
            if (DOM.data(debugPanelSwitchSideBtnID, "state") == "at-left") {
                DOM.data(debugPanelSwitchSideBtnID, "state", "at-right");
                Util.memoItem(widgetPosition, "at-right");

                stickToRight();
            } else {
                DOM.data(debugPanelSwitchSideBtnID, "state", "at-left");
                Util.memoItem(widgetPosition, "at-left");

                stickToLeft();
            }
        }, false);

        function expand() {
            DOM.html(debugPanelExpandCollapseBtnID, arrowBottomSymbol);
            DOM.css(debugPanelContainerID, "paddingTop", "0");
            DOM.css(debugPanelContainerID, "width", "100%");
        }

        function collapse() {
            DOM.html(debugPanelExpandCollapseBtnID, arrowTopSymbol);
            DOM.css(debugPanelContainerID, "paddingTop", widgetPosY + "px");
            DOM.css(debugPanelContainerID, "width", "480px");
        }

        if (Util.recallItem(widgetCollapseState)) {
            DOM.data(debugPanelExpandCollapseBtnID, "state", Util.recallItem(widgetCollapseState));

            if (Util.recallItem(widgetCollapseState) == "expanded") {
                expand();
            } else {
                collapse();
            }
        }

        DOM.get(debugPanelExpandCollapseBtnID).addEventListener("click", function () {
            if (DOM.data(debugPanelExpandCollapseBtnID, "state") == "collapsed") {
                DOM.data(debugPanelExpandCollapseBtnID, "state", "expanded");
                Util.memoItem(widgetCollapseState, "expanded");

                expand();
            } else {
                DOM.data(debugPanelExpandCollapseBtnID, "state", "collapsed");
                Util.memoItem(widgetCollapseState, "collapsed");

                collapse();
            }
        }, false);
    }

    return {
        init: function() {
            window.addEventListener("load", function() {
                createAndDisplay();
                initAndAssignHandlers();
            }, false);
        }
    };
};

//Create anonymous scope-object and launch its constructor (which will create necessary objects in local scope and
//launch entry point) only if inside top frame. This is needed to simplify memory management.
var Logger = (function() {
    return {
        logDelimiterLine: function() {
            this.log("----------------------------------------------------------------");
        },

        log: function(message) {
            if (console && console.info) {console.info("[INFO] [viewport.js] " + message);}
        },

        logError: function(message) {
            if (console && console.error) {console.error("[ERROR] [viewport.js] " + message);}
        }
    };
})();

/**
 * Creates viewport. Contains private low-level method to create meta tag and add it to DOM and contains public hi-level
 * methods to create viewport with different required content.
 */
var Viewport = (function() {
    /**
     * Need to be called when <head> section is parsed as for iOS devices (at least) it don't take effect right away if
     * called onLoad (e.g. GWT EntryPoint.onModuleLoad())
     */
    function createViewport(content) {
        Logger.logDelimiterLine();
        Logger.log("Creating viewport with next content: " + content);
        Logger.logDelimiterLine();
        var existingMeta = document.getElementById("meta-viewport");
        var viewportMetaTag = existingMeta ? existingMeta : document.createElement("meta");
        viewportMetaTag.setAttribute("id", Constants.DOM.viewport);
        viewportMetaTag.setAttribute("name", Constants.DOM.viewport);
        viewportMetaTag.setAttribute("content", content);

        if(!existingMeta){
            document.getElementsByTagName("head")[0].appendChild(viewportMetaTag);
        }

    }

    /**
     * This particular implementation of dynamic styles creation might not be cross-browser and stable, but it is
     * confirmed to work at least on supported Window Phones.
     *
     * @param widthL width for landscape
     * @param widthP width for portrait
     */
    function createCSSViewport(widthL, widthP) {
        DOM.addStyle(
            "@media screen and (orientation: landscape) {"+
            "@-ms-viewport {"+
            "width: " + widthL + "px;"+
            "}"+
            "}"+

            "@media screen and (orientation: portrait) {"+
            "@-ms-viewport {"+
            "width: " + widthP + "px;"+
            "}"+
            "}")
            .id = Constants.DOM.viewportStyleTag;
    }

    return {
        /**
         * As Web-standards evolve this universal viewport settings should work correctly for major modern mobile OS's
         * and browsers, see: http://dev.w3.org/csswg/css-device-adapt/#viewport-meta
         */
        setViewport: function(scale) {
            this.scale = scale; //for backward compatibility with Hub
            createViewport("user-scalable=no, " +
                "initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale);
        },

        /**
         * This method targeted for Androids with Android WebKit browser in which initial-scale works incorrectly.
         * Setting target-densitydpi=device-dpi makes viewport scaling to real physical resolution of device,
         * see: http://developer.android.com/guide/webapps/targeting.html#ViewportDensity
         * Setting initial, min, max-scale together with target-densitydpi is required to ensure user won't scale the
         * page manually as it was found that user-scalable=no doesn't work correctly on all devices...
         *
         * Support for Android specific target-densitydpi property has being removed from WebKit,
         * see: http://trac.webkit.org/changeset/119527 so this is only for old browsers.
         *
         * Also then NGM Core will rely on window.outerWidth/Height for Android to find-out resolution which is best
         * choice as it ignores browser address bar (as it will be hidden after game loaded with window.scrollTo(0, 1)).
         */
        setViewportAndroidWebkit: function(targetDpi) {
            var content = "target-densitydpi=" + targetDpi + ", user-scalable=no, " +
                "initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0";
            createViewport(content);
        },

        /**
         * Usual viewport meta tag has completely buggy and partial implementation so it can be treated as not supported
         * at all by Microsoft (only some sort of support looks like appearing in upcoming Edge browser):
         * http://blogs.msdn.com/b/iemobile/archive/2011/01/21/managing-the-browser-viewport-in-windows-phone-7.aspx
         * http://blogs.msdn.com/b/iemobile/archive/2010/11/22/the-ie-mobile-viewport-on-windows-phone-7.aspx
         *
         * But it turned-out that IE on Windows Phone is playing nicely with @viewport CSS at-rule:
         * https://msdn.microsoft.com/en-us/library/windows/apps/hh868813.aspx
         * https://developer.mozilla.org/en-US/docs/Web/CSS/@viewport
         *
         * But mainly only Microsoft supports it (partially) ATM:
         * http://caniuse.com/#search=%40viewport
         *
         * -ms-touch-action allows to disable double tap zoom and user manual zoom (but still buggy - can be still
         * zoomed during page scroll):
         * https://msdn.microsoft.com/en-us/library/windows/apps/hh767313.aspx
         *
         * @param widthL width for landscape
         * @param widthP width for portrait
         */
        setViewportIE: function (widthL, widthP) {
            createCSSViewport(widthL, widthP);
        },

        /**
         * This would be as first check before any logic of viewport.js. If device is in exceptional devices map - then
         * just take the viewport content from this map and apply. The end.
         */
        setViewportExceptionalDevice: function(content) {
            createViewport(content);
        },

        /**
         * For backward compatibility
         */
        getViewportValue: function () {
            return ScalingReport.scaleFactor();
        }
    };
})();

// --------------------------------------------------------------------------------------------------------------------

/**
 * Contains detection methods for different devices, os's, browsers, modes, screen characteristics, etc.
 */
var Device = (function() {
    return {
        testUserAgent: function(regExp) {
            return regExp.test(navigator.userAgent);
        },

        execUserAgent: function(regExp) {
            return regExp.exec(navigator.userAgent);
        },

        /**
         * Fail-safe getter for devicePixelRatio (it is not supported by IE).
         * window.devicePixelRatio is the ratio between physical pixels and device-independent pixels (DIPs) on device.
         * window.devicePixelRatio = physical pixels / dips.
         *
         * As iPhone 6 Plus reports incorrect (rounded) DPR = 3, this method workarounds the problem by calculating
         * real DPR for mentioned device, by dividing its real screen height (1920) to its reported screen.height (736)
         * which gives ~2.6 which should have being reported by this device instead of rounded value. All this is
         * required as later logic will try to find-out real device's screen resolution by taking DPR value and
         * multiplying it to reported screen.width or height (max value is used), so 736 * ~2.6 = 1920. Without this
         * workaround it would be 736 * 3 = 2208.
         *
         * @returns {number} window.devicePixelRatio if it exists, null otherwise
         */
        getDPR: function() {
            var idealViewportWidthInDIPs = Math.max(screen.width, screen.height);

            if (window.devicePixelRatio) {
                if (Device.isIphoneX()) {
                    //iPhoneX must be before isIphone6Plus
                    return window.devicePixelRatio;
                } else if (Device.isIphone6Plus()) {
                    if (idealViewportWidthInDIPs == Constants.iPhone6PlusWidenessInDIPs) {
                        return Constants.iPhone6PlusWidenessInPx / idealViewportWidthInDIPs; //2,608695652173913
                    } else {//then its "zoom mode" and Safari reports incorrect (hardcoded) DRP which have to be dynamic
                        return Constants.iPhone6WidenessInPx / idealViewportWidthInDIPs; //2
                    }
                } else if ((Device.isNexus6() || Device.isNexus6P()) && !Device.Browser.isUCBrowser()) {
                    return Constants.nexus6WidenessInPx / idealViewportWidthInDIPs; //3,497267759562842
                } else if (Device.isNexus5X() && !Device.Browser.isUCBrowser()) {
                    return Constants.highResScreenThreshold / idealViewportWidthInDIPs; //3,497267759562842
                } else if (Device.isTab3_10_1() && Device.Browser.isUCBrowser()) {
                    return Math.ceil(Constants.tab3_10_1WidenessInPx/idealViewportWidthInDIPs); //to fix 0.999 dpr
                } else if (Device.isXiaoMiNoteLTE() && !Device.Browser.isUCBrowser()) {
                    return Constants.highResScreenThreshold / idealViewportWidthInDIPs; //2,746781115879828 instead of 2.75
                } else if (Device.isSamsungS8() && Device.Browser.isChrome()) {
                    return Constants.SamsungS8WidenessInPx / idealViewportWidthInDIPs;
                } else {
                    return window.devicePixelRatio;
                }
            } else {
                if (Device.isLumia920() && Device.Browser.isIEMobile() && Device.Browser.Version.getMajor(true) == 10) {
                    return Constants.lumia920WidenessInPx / idealViewportWidthInDIPs; //~2.07
                } if (Device.isLumia820() && Device.Browser.isIEMobile() && Device.Browser.Version.getMajor(true) == 10) {
                    return Constants.lumia820WidenessInPx / idealViewportWidthInDIPs; //1 for IE 10 in GDR2 and ~1.349 in GDR3
                } else {
                    return null;
                }
            }
        },

        isIphone: function() {
            return Device.testUserAgent(/iPhone/i) && !Device.OS.isWindowsPhone();
        },

        /**
         * Warning!
         * As there is no good way to distinguish iPhones, the only possible way was used: to check if
         * window.devicePixelRatio is grater than 2, as all iPhones report it as 2 except iPhone 6 Plus which reports
         * it as 3. If future iPhones will need to have special treatment different from current 6 Plus treatment,
         * and if they will have same User-Agent string and report dpr as 3 - than it will be impossible to distinguish
         * new iPhone from iPhone 6 Plus and thus implement this treatment! God save us all...
         *
         * @returns {boolean} if device is iPhone and its device pixel ratio is higher than 2
         */
        isIphone6Plus: function() {
            return Device.isIphone() && window.devicePixelRatio > 2;
        },

        isIphoneX: function () {
            return Device.isIphone() && (Math.max(window.screen.height, window.screen.width)  === 812);
        },

        isIpod: function() {
            return Device.testUserAgent(/iPod/i);
        },

        isIpad: function() {
            return Device.testUserAgent(/iPad/i) && Math.max(screen.width, screen.height) == Constants.iPadWidenessInDIPs;
        },

        isIpadPro: function() {
            return Device.testUserAgent(/iPad/i) && Math.max(screen.width, screen.height) == Constants.iPadProWidenessInDIPs;
        },

        isLumia820: function() {
            return Device.testUserAgent(/Lumia 820/i);
        },

        isLumia920: function() {
            return Device.testUserAgent(/Lumia 920/i);
        },

        isNexus6: function() {
            return Device.testUserAgent(/Nexus 6\s/i);
        },

        isNexus6P: function() {
            return Device.testUserAgent(/Nexus 6P\s/i);
        },

        isNexus5X: function() {
            return Device.testUserAgent(/Nexus 5X/i);
        },

        isTab3_10_1: function() {
            return Device.testUserAgent(/GT-P52/i);
        },

        isXiaoMiNoteLTE: function() {
            return Device.testUserAgent(/MI NOTE LTE/i);
        },

        isSamsungS8: function() {
            return Device.testUserAgent(/SM-G95\d+/i);
        },

        OS: (function() {
            return {
                isIos: function() {
                    return Device.isIphone() || Device.isIpod() || Device.isIpad();
                },

                isAndroid: function() {
                    return Device.testUserAgent(/Android/i) && !Device.OS.isWindowsPhone();
                },

                isWindowsPhone: function() {
                    return Device.testUserAgent(/Windows\sPhone/i);
                }
            };
        })(),

        Browser: (function() {
            return {
                /**
                 * @returns {boolean} true for Chrome and "new" Android Webkit browser (as there is no clear way how to
                 * distinguish these two), false otherwise.
                 */
                isChrome: function() {
                    return Device.testUserAgent(/(?:Chrome|CriOS|CrMo)(?!.+(?:Edge|UCBrowser))/i);
                },

                /**
                 * Such method is required in context of Device.Screen.isHighResolution()
                 * @returns {boolean} true for Chrome with version less than 25 (not including), false otherwise.
                 */
                isOldChrome: function() {
                    return this.isChrome() && this.Version.getMajor() < Constants.oldChromeVersionThreshold;
                },

                /**
                 * Checks if user agent contains "Android" AND "AppleWebKit" AND NOT "Chrome", ignoring case.
                 *
                 * @returns {boolean} return true only for "old" Android Webkit browser. The "new" one (appeared at
                 * least on Galaxy S4) is now on Chrome engine and so contains word "Chrome" in ua-string.
                 */
                isAndroidWebkit: function() {
                    return Device.testUserAgent(/(?:Android)(?=.+AppleWebKit)(?!.+(?:Chrome|UCBrowser))/i)  && !Device.Browser.isIEMobile();
                },

                /**
                 * UC Browser adds correspondent string everywhere except Windows Phone with IE 11
                 * @returns {boolean} true if UC Browser detected
                 */
                isUCBrowser: function() {
                    return Device.testUserAgent(/UCBrowser/i);
                },

                isUCBrowserChinese: function() {
                    return Device.testUserAgent(/zh-cn/i);
                },

                isIosSafari: function() {
                    return Device.OS.isIos() && !Device.Browser.isUCBrowser() && !Device.Browser.isChrome();
                },

                isIosSafariStandalone: function() {
                    return window.navigator.standalone;
                },

                /**
                 * Will match actually all Internet Explorers excluding desktop versions.
                 * @returns {true} if Internet Explorer detected
                 */
                isIEMobile: function () {
                    return Device.testUserAgent(/Windows\sPhone.+Trident/i);
                },

                /**
                 * Tries to identify Internet Explorer Mobile in "desktop mode"
                 * Settings -> Website preference -> desktop version
                 * @returns {boolean} true if IE Desktop mode detected
                 */
                isIEMobileDesktopMode: function () {
                    return Device.testUserAgent(/Trident.+WPDesktop/i);
                },

                isEdgeMobile: function () {
                    return Device.testUserAgent(/Windows\sPhone.+Edge/i);
                },

                /**
                 * Default browser found in Xiaomi Mi4
                 * @returns {boolean} true if default Xiaomi Mi4 browser detected
                 */
                isBdBrowser: function () {
                    return Device.testUserAgent(/bdbrowser/i);
                },

                Version: (function() {
                    var rawVersion = null;

                    /**
                     * "The returned array has the matched text as the first item, and then one item for each
                     * capturing parenthesis that matched containing the text that was captured."
                     * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
                     *
                     * Also replaces iOS separators "_" to widely adopted "."
                     *
                     * @param array result of regexp is intended to be passed by this param
                     *
                     * @returns {string} captured version
                     */
                    function getVersionSafely(array) {
                        if (array) {
                            return array[1].replace(new RegExp("_", "g"), ".");
                        } else {
                            return null;
                        }
                    }

                    return {
                        /**
                         * Tries to recognize browser version.
                         *
                         * @param baseVersion indicates that caller is interested in version of the "underlying"
                         * browser, e.g. UC Browser for Windows Phone is build on top of IE
                         *
                         * @returns {string} raw version as recognized
                         */
                        getRaw: function(baseVersion) {
                            if (baseVersion === undefined) baseVersion = false;
                            if (rawVersion) return rawVersion;

                            if (Device.Browser.isEdgeMobile()) {
                                rawVersion = getVersionSafely(Device.execUserAgent(/Edge\/(\S+)/i));
                            } else if (!baseVersion && Device.Browser.isUCBrowser()) {
                                rawVersion = getVersionSafely(Device.execUserAgent(/UCBrowser\/(\S+)/i));
                            } else if (!baseVersion && Device.Browser.isBdBrowser()) {
                                rawVersion = getVersionSafely(Device.execUserAgent(/bdbrowser_i18n\/(\S+)/i));
                            } else if (Device.Browser.isChrome()) {
                                rawVersion = getVersionSafely(Device.execUserAgent(/(?:Chrome|CriOS|CrMo)\/(\S+)/i));
                            } else if (Device.Browser.isIEMobile()) {
                                rawVersion = getVersionSafely(Device.execUserAgent(/(?:rv:|IEMobile\/|MSIE\s)(\S[^);]+)/i));
                            } else if (Device.Browser.isAndroidWebkit()) {
                                rawVersion = getVersionSafely(Device.execUserAgent(/Version\/(\S+)/i));
                            } else if (Device.OS.isIos()) {
                                rawVersion = getVersionSafely(Device.execUserAgent(/OS\s(\d\S*)/i));
                            } else {
                                rawVersion = null;
                            }
                            return rawVersion;
                        },

                        getMajor: function(baseVersion) {
                            if (baseVersion === undefined) baseVersion = false;
                            if (this.getRaw(baseVersion)) {
                                return this.getRaw(baseVersion).split(".")[0];
                            } else {
                                return null;
                            }
                        },

                        getMinor: function(baseVersion) {
                            if (baseVersion === undefined) baseVersion = false;
                            if (this.getRaw(baseVersion)) {
                                return this.getRaw(baseVersion).split(".")[1];
                            } else {
                                return null;
                            }
                        },

                        /**
                         * Simply compares two string version values.
                         *
                         * Example:
                         * versionCompare('1.1', '1.2') => -1
                         * versionCompare('1.1', '1.1') =>  0
                         * versionCompare('1.2', '1.1') =>  1
                         * versionCompare('2.23.3', '2.22.3') => 1
                         *
                         * Returns:
                         * -1 = left is LOWER than right
                         *  0 = they are equal
                         *  1 = left is GREATER = right is LOWER
                         *  And FALSE if one of input versions are not valid
                         *
                         * @function
                         * @param {String} left  Version #1
                         * @param {String} right Version #2
                         * @return {Integer|Boolean}
                         * @author Alexey Bass (albass)
                         * @since 2011-07-14
                         */
                        versionCompare: function(left, right) {
                            if (typeof left + typeof right != 'stringstring')
                                return false;

                            var a = left.split('.');
                            var b = right.split('.');
                            var i = 0;
                            var len = Math.max(a.length, b.length);

                            for (; i < len; i++) {
                                if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) {
                                    return 1;
                                } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) {
                                    return -1;
                                }
                            }

                            return 0;
                        },

                        compareTo: function(anotherVersion, baseVersion) {
                            if (baseVersion === undefined) baseVersion = false;
                            return this.versionCompare(this.getRaw(baseVersion), anotherVersion);
                        }
                    };
                })()
            };
        })(),

        Screen: (function() {
            var resolutions = Constants.mobileDevicesUniqueResolutions; //shortcut

            /**
             * Converts value to pixels by multiplying it on DPR (Device Pixel Ratio).
             * Makes decision if multiplication is needed. Some browsers report values like screen.width/height
             * in pixels and some in DIPs (Device Independent Pixels). Latest Chrome and Safari report in DIPS,
             * Default Android browser and UCBrowser in pixels. Old Chrome reported in pixels, but somewhere between
             * 19 and 24 version Chrome began to report in DIPs, but unfortunately info about in which exact version
             * this happened is not available to the author of this file. It is confirmed that Chrome 18 reported in px
             * and Chrome 25 in DIPS.
             *
             * @param size adjusted by multiplying on DPR if its Chrome older than 25 or
             * new Default Android browser (on Chrome engine).
             *
             * @returns {number} size in pixels, previously converted from DIPS if it was needed.
             */
            function convertFromDipsToPx(size) {
                if (Device.Browser.isOldChrome() || Device.Browser.isAndroidWebkit() || Device.Browser.isBdBrowser() ||
                    (Device.OS.isAndroid() && Device.Browser.isUCBrowser() && (Device.Browser.Version.getMajor() <= 11 && Device.Browser.Version.getMinor() < 4)
                    )) {
                    return size;
                } else {
                    return Math.floor(size * Device.getDPR());
                }
            }

            /**
             * Iterates over set of unique resolutions, divides each by input resolution to receive ratio.
             * If ratio less than 1 - returns previous resolution in the list as closest, meaning that iteration reached
             * the resolution which lower than input one. Relies on the facts that array of resolutions is sorted
             * descending and that browser may return resolution LOWER than device has in fact (not higher).
             *
             * WARNING! This method may work incorrectly if resolutions map will start to contain too much resolutions
             * which close to each other by value.
             *
             * @param inputResolution incorrect resolution returned by the browser
             * @returns {number} closest correct resolution
             */
            function selectClosestResolution(inputResolution) {
                for (var i = 0; i < resolutions.length; i++) {
                    if (resolutions[i] / inputResolution < 1) {
                        return resolutions[i - 1];
                    }
                }

                return resolutions[resolutions.length - 1];
            }

            /**
             * Rounds odd value to even grater by 1
             * @param value odd value
             * @returns {number} even value
             */
            function roundToEven(value) {
                return (value % 2 == 0) ? value : value + 1;
            }

            return {
                getWidth: function() {
                    return convertFromDipsToPx(screen.width);
                },

                getHeight: function() {
                    return convertFromDipsToPx(screen.height);
                },

                /**
                 * Chrome in new version (at least in 27) started to return incorrect values for
                 * screen.width/screen.height properties on devices with navigation bar taking space from the screen
                 * itself. Now these properties are reduced by height of this navigation panel (which can be different
                 * in landscape/portrait modes). This method tries to adjust this by using selectClosestResolution()
                 * method but due to its characteristics it used only on high resolution screens.
                 *
                 * @returns {number} size of wider side of the screen adjusted if its high resolution screen
                 */
                getWideness: function() {
                    return selectClosestResolution(Math.max(this.getWidth(), this.getHeight()));
                },

                /**
                 * @returns {number} side of narrower side of the screen rounded to even if odd (by adding +1 extra px)
                 */
                getNarrowness: function() {
                    return roundToEven(Math.min(this.getWidth(), this.getHeight()));
                },

                /**
                 * @returns {boolean} true if wider side size is more than threshold (1920px), false otherwise
                 */
                isHighResolution: function() {
                    return this.getWideness() >= Constants.highResScreenThreshold;
                }
            };
        })()
    };
})();

/**
 * Creates viewport when head section is parsed. Makes decision upon how exactly viewport will be created based on
 * device's screen size and browser. Applies workarounds if needed.
 */
var ViewportManager = (function() {
    var selectedViewport; //stored value between method calls
    var factor;
    var scaleFactor;

    /**
     * Iterates over Constants.exceptionalDevices and tests each key (which is device regexp)
     * upon navigator.userAgent. Stores viewport content selected from the map and returns true if current device is
     * found in the map.
     * Otherwise stores null and returns false.
     *
     * @returns {boolean} true if device found in the list, false otherwise
     */
    function isDeviceInExceptionalList() {
        for (var deviceKey in Constants.exceptionalDevices) {
            if (Device.testUserAgent(new RegExp(deviceKey, "i"))) {
                selectedViewport = Constants.exceptionalDevices[deviceKey];
                return true;
            }
        }

        selectedViewport = null;
        return false;
    }

    /**
     * @returns {String} stored value for already selected viewport content after isDeviceInExceptionalList() run,
     * if stored value undefined - this method will run isDeviceInExceptionalList() first and then retrieve again.
     */
    function getCorrespondentExceptionalViewPort() {
        if (selectedViewport != undefined) {
            return selectedViewport;
        } else {
            isDeviceInExceptionalList();
            return selectedViewport;
        }
    }

    /**
     * @returns {number} maximum supported resolution according to business requirements
     */
    function getMaxSupportedResolution() {
        if (Util.isUrlTriggerParamPresent("iPadProCorrectScale"))
            return Constants.iPadProWidenessInDIPs;

        if (Device.isIpad() || Device.isIpadPro()) {
            return Constants.maxIpadSupportedResolution;
        } else if (Device.isIphoneX()) {
            return Constants.maxiPhoneXSupportedResolution;
        } else {
            return Constants.maxSupportedResolution;
        }
    }

    function calculateScaleFactor() {
        factor = Device.Screen.isHighResolution() ?
            Device.Screen.getWideness() / getMaxSupportedResolution() : 1;

        scaleFactor = factor/Device.getDPR();
    }

    function calculateScaledDimension(dimension) {
        return Math.round(dimension > getMaxSupportedResolution() ?
            dimension / (scaleFactor * Device.getDPR()) : dimension);
    }

    function fillInScalingReport() {
        //report potential scale factor (mainly for QA automation) even though it may not be applied
        ScalingReport.scaleFactor(scaleFactor);
        ScalingReport.actualDPR(Device.getDPR());
        ScalingReport.deviceScreenWidth(Device.Screen.getWidth());
        ScalingReport.deviceScreenHeight(Device.Screen.getHeight());
        ScalingReport.deviceScreenWideness(Device.Screen.getWideness());
        ScalingReport.deviceScreenNarrowness(Device.Screen.getNarrowness());
        ScalingReport.deviceScreenIsHighResolution(Device.Screen.isHighResolution());
        ScalingReport.browserVersionRaw(Device.Browser.Version.getRaw());
        ScalingReport.browserVersionMajor(Device.Browser.Version.getMajor());
        ScalingReport.browserVersionMinor(Device.Browser.Version.getMinor());
        ScalingReport.browserVersionRawBase(Device.Browser.Version.getRaw(true));
        ScalingReport.browserVersionMajorBase(Device.Browser.Version.getMajor(true));
        ScalingReport.browserVersionMinorBase(Device.Browser.Version.getMinor(true));
    }

    function createViewport() {
        if (inIframe() || Util.isUrlTriggerParamPresent("noVP")) return;

        if (isDeviceInExceptionalList()) {
            Viewport.setViewportExceptionalDevice(getCorrespondentExceptionalViewPort());
        } else if (Device.Browser.isAndroidWebkit()) {
            var targetDpi = Device.Screen.isHighResolution() ? "medium-dpi" : "device-dpi";
            Viewport.setViewportAndroidWebkit(targetDpi);
        } else if ((Device.Browser.isIEMobile() || Device.Browser.isEdgeMobile()) && !Device.Browser.isIEMobileDesktopMode()) {
            var scaledWideness = calculateScaledDimension(Device.Screen.getWideness());
            var scaledNarrowness = calculateScaledDimension(Device.Screen.getNarrowness());
            Viewport.setViewportIE(scaledWideness, scaledNarrowness);
        } else {
            Viewport.setViewport(scaleFactor); //will be called also for IE Desktop Mode causing it to
            // "incorrectly" scale to 1024px width which is better than 320px as alternative
        }
    }

    function applyWorkaroundsIfNeeded() {
        if (inIframe()) return;

        if (Device.isIphone() && Device.Browser.isIosSafariStandalone() &&
            Device.Browser.Version.compareTo("8.3") >= 0 && Device.Browser.Version.compareTo("9.0") <= 0) {
            new WorkaroundManager().workaroundForIncorrectScaleInStandaloneMode();
        }

        if (Device.Browser.isIEMobile() || Device.Browser.isEdgeMobile()) {
            new WorkaroundManager().workaroundForIncorrectScaleAfterFirstOpeningInIEAndEdge();
        }
    }

    function displayDebuggerIfNeeded() {
        if (Util.isUrlTriggerParamPresent("debugInVP") || Util.recallItem("debugInVP")) {
            Util.memoItem("debugInVP", "debugInVP"); //no sense in this value, just presence to trigger required behaviour
            new DebugWidget().init();
        }
    }

    return {
        init: function() {
            calculateScaleFactor();
            fillInScalingReport();
            createViewport();
            applyWorkaroundsIfNeeded();
            displayDebuggerIfNeeded();
        }
    };
})();

//Entry point
ViewportManager.init();
  // restore variables from viewport solution
  window.Viewport = Viewport;
  window.ViewportManager = ViewportManager;
  window.ScaleManager._backScale();
}());
</script>
<script async src="https://mpsnare.iesnare.com/snare.js"></script>
    <!-- EzPush Code-->
<!--<link rel="manifest" href="/library/ezpush/manifest.json">-->
<!-- EzPush Code -->

<script>
 window.usabillaButtonsByLang = {
 desktop: {
 en: '9cee0aa1ccb8',
 eu: '9cee0aa1ccb8'
 },
 mobile: {
 en: 'e7743558d009',
 eu: 'e7743558d009'
 }
 };
</script>
<!-- Dynamic Yield Code -->
<script type="text/javascript" src="//cdn.dynamicyield.com/api/8765453/api_dynamic.js?DY_IMS_V3" async="true"></script>
<script type="text/javascript" src="//cdn.dynamicyield.com/api/8765453/api_static.js?DY_IMS_V3" async="true"></script>
<!-- END Dynamic Yield Code -->

<script>(function(w,d,t,r,u){var f,n,i;w[u]=w[u]||[],f=function(){var o={ti:"5036968"};o.q=w[u],w[u]=new UET(o),w[u].push("pageLoad")},n=d.createElement(t),n.src=r,n.async=1,n.onload=n.onreadystatechange=function(){var s=this.readyState;s&&s!=="loaded"&&s!=="complete"||(f(),n.onload=n.onreadystatechange=null)},i=d.getElementsByTagName(t)[0],i.parentNode.insertBefore(n,i)})(window,document,"script","//bat.bing.com/bat.js","uetq");</script>

<!-- dy-top-head -->

<!-- dy-top-head END-->

<!-- google analytics -->
<script async src='https://www.google-analytics.com/analytics.js'></script>
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
</script>
<!-- google analytics END-->


<!-- WHO-5347 START -->
<script>
 if(window.Playtech && window.Playtech.Variables){ window.Playtech.Variables.searchImageUrl = '/library/promo_games_images/'; }
</script>
<!-- WHO-5347 END -->

<!-- WHO-6556 START -->
<script>
    if(window.Playtech && window.Playtech.Variables){
    window.Playtech.Variables.animationGamesConfig = [
              {"pageUrl": "/promotions/popup-fdb", "gameCodes": ["ro_g","wildb"]},
              {"pageUrl": "/promotions/popup-2nd", "gameCodes": ["ro_g"]}
            ];
/**
 * Fix get request /undefined
 */
    window.Playtech.Variables.chatLocation = '#';
/** END */
}
</script>
<!-- WHO-6556 END -->

<!-- 
 To collect end-user usage analytics about your application, 
 insert the following script into each page you want to track.
 Place this code immediately before the closing </head> tag,
 and before any other scripts. Your first data will appear 
 automatically in just a few seconds.
 --> 
 <script type="text/javascript">
 var appInsights=window.appInsights||function(a){
 function b(a){c[a]=function(){var b=arguments;c.queue.push(function(){c[a].apply(c,b)})}}var c={config:a},d=document,e=window;setTimeout(function(){var b=d.createElement("script");b.src=a.url||"https://az416426.vo.msecnd.net/scripts/a/ai.0.js",d.getElementsByTagName("script")[0].parentNode.appendChild(b)});try{c.cookie=d.cookie}catch(a){}c.queue=[];for(var f=["Event","Exception","Metric","PageView","Trace","Dependency"];f.length;)b("track"+f.pop());if(b("setAuthenticatedUserContext"),b("clearAuthenticatedUserContext"),b("startTrackEvent"),b("stopTrackEvent"),b("startTrackPage"),b("stopTrackPage"),b("flush"),!a.disableExceptionTracking){f="onerror",b("_"+f);var g=e[f];e[f]=function(a,b,d,e,h){var i=g&&g(a,b,d,e,h);return!0!==i&&c["_"+f](a,b,d,e,h),i}}return c
 }({
 instrumentationKey:"fa25a2ff-9331-40ee-bb99-446a09c4cbcf"
 });
 
 window.appInsights=appInsights,appInsights.queue&&0===appInsights.queue.length&&appInsights.trackPageView();
 </script>
  </head>
  <body>
    
    <div id="application" class="application-root">
      <div class="preloader">
    <div class="main-site-loader">
        <div class="spinner">
            <div></div>
            <div></div>
            <div></div>
        </div>
    </div>
</div>    </div>
    <script>
    var link = document.createElement('link');
    link.href = '/eurogrand_eurogrand-desktop-theme/css/style.raw.css?t=1578654848367';
    link.type = 'text/css';
    link.rel = 'stylesheet';
    document.querySelector('head').appendChild(link);

    var bundle = document.createElement('link');
    bundle.href = '/eurogrand_eurogrand-desktop-theme/js/app-out/bundle.css?t=1578654848367';
    bundle.type = 'text/css';
    bundle.rel = 'stylesheet';
    bundle.onload = function() {
        // some browsers run this code twice for mysterious reasons
        if (window.isCssBundleCustomPropertiesUrlsHackApplied) {
            return;
        }

        window.isCssBundleCustomPropertiesUrlsHackApplied = true;

        var needApplyUrlsHack = (function() {
            var ua = window.navigator.userAgent.toLowerCase();
            var isTouchDevice = 'ontouchstart' in document.documentElement;
            var isIpadLikeDesktop = isTouchDevice && ua.indexOf('macintosh') !== -1;
            var isIos = isIpadLikeDesktop ||
                ['iphone', 'ipod', 'ipad'].filter(function(i) {
                    return ua.indexOf(i) !== -1;
                }).length > 0;
            var browser = (ua.match(/(chrome|crios|firefox|safari|msie|trident(?=\/))\/?\s*(\d+)/) || [])[1] || null;
            var isSafari = browser === 'safari' || !browser && isIos;
            var isEdge = ua.indexOf('edge') !== -1;

            return isIos || isSafari || isEdge;
        })();

        if (!needApplyUrlsHack) {
            return;
        }

        var root = getComputedStyle(document.documentElement);
        var newRoot = '';
        var bundleStyleSheet;

        for (var i = 0; i < document.styleSheets.length; i++) {
            var styleSheet = document.styleSheets[i];
            if (styleSheet.href && styleSheet.href.indexOf(bundle.href) !== -1) {
                bundleStyleSheet = styleSheet;
                break;
            }
        }

        if (!bundleStyleSheet) {
            return;
        }

        var variables = [];
        var varNameRe = /--[\w\d-]+?(?=\:)/g;
        [].slice.call(bundleStyleSheet.cssRules).forEach(function(rule) {
            if (rule.selectorText !== ':root') {
                return;
            }

            var match = rule.cssText.match(varNameRe);

            if (match) {
                match.forEach(function(varName) {
                    variables.push(varName);
                });
            }
        });

        variables.forEach(function(propName) {
            var value = root.getPropertyValue(propName);

            if (value.indexOf('url(') > -1) {
                newRoot += propName + ': ' +
                    value.replace('url(', 'url(/' + Playtech.Variables.themeName + '/js/app-out/') + ';\n';
            }
        });

        if (newRoot) {
            var sheet = document.createElement('style');
            sheet.type = 'text/css';
            sheet.id = 'ios-custom-property-fix-' + Date.now();
            sheet.textContent = ':root{' + newRoot + '}';
            document.querySelector('head').appendChild(sheet);
        }
    };
    document.querySelector('head').appendChild(bundle);
</script>
<script type="text/javascript">
    var element = document.createElement('script');
    element.src = '/eurogrand_eurogrand-desktop-theme/js/dist/main.min.js?t=1578654848367';
    document.body.appendChild(element);
</script>
<script type="text/javascript">
    // <![CDATA[
    ;
    // ]]>
</script>
    <!--<script src="https://ezpush.techonlinecorp.com/SDK/ezpush-client.js" async></script>-->

<!-- begin usabilla live embed code -->
<script type="text/javascript">/*{literal}<![CDATA[*/window.lightningjs||function(c){function g(b,d){d&&(d+=(/\?/.test(d)?"&":"?")+"lv=1");c[b]||function(){var i=window,h=document,j=b,g=h.location.protocol,l="load",k=0;(function(){function b(){a.P(l);a.w=1;c[j]("_load")}c[j]=function(){function m(){m.id=e;return c[j].apply(m,arguments)}var b,e=++k;b=this&&this!=i?this.id||0:0;(a.s=a.s||[]).push([e,b,arguments]);m.then=function(b,c,h){var d=a.fh[e]=a.fh[e]||[],j=a.eh[e]=a.eh[e]||[],f=a.ph[e]=a.ph[e]||[];b&&d.push(b);c&&j.push(c);h&&f.push(h);return m};return m};var a=c[j]._={};a.fh={};a.eh={};a.ph={};a.l=d?d.replace(/^\/\//,(g=="https:"?g:"http:")+"//"):d;a.p={0:+new Date};a.P=function(b){a.p[b]=new Date-a.p[0]};a.w&&b();i.addEventListener?i.addEventListener(l,b,!1):i.attachEvent("on"+l,b);var q=function(){function b(){return["<head></head><",c,' onload="var d=',n,";d.getElementsByTagName('head')[0].",d,"(d.",g,"('script')).",i,"='",a.l,"'\"></",c,">"].join("")}var c="body",e=h[c];if(!e)return setTimeout(q,100);a.P(1);var d="appendChild",g="createElement",i="src",k=h[g]("div"),l=k[d](h[g]("div")),f=h[g]("iframe"),n="document",p;k.style.display="none";e.insertBefore(k,e.firstChild).id=o+"-"+j;f.frameBorder="0";f.id=o+"-frame-"+j;/MSIE[ ]+6/.test(navigator.userAgent)&&(f[i]="javascript:false");f.allowTransparency="true";l[d](f);try{f.contentWindow[n].open()}catch(s){a.domain=h.domain,p="javascript:var d="+n+".open();d.domain='"+h.domain+"';",f[i]=p+"void(0);"}try{var r=f.contentWindow[n];r.write(b());r.close()}catch(t){f[i]=p+'d.write("'+b().replace(/"/g,String.fromCharCode(92)+'"')+'");d.close();'}a.P(2)};a.l&&setTimeout(q,0)})()}();c[b].lv="1";return c[b]}var o="lightningjs",k=window[o]=g(o);k.require=g;k.modules=c}({});
/*]]>{/literal}*/</script>
<!-- end usabilla live embed code -->
  </body>
</html>
URL
GET http://www.eurogrand.com/
Response Headers
Content-Length:
0
Server:
AkamaiGHost
Connection:
keep-alive
X-WPL-DATA:
VEc=,TE9NRQ==,dmhpZ2g=
Location:
https://www.eurogrand.com/
Date:
Tue, 25 Feb 2020 16:17:20 GMT
Response Body

Empty body

Measurement UID
01202002256223903ca1dd5a70b500adb88906b1
Report ID
20200225T161607Z_AS24691_a3JLxSAB3I97d3nYfNJVMoz110OknVQ7lTubyTYbRVbvyVdPef
Platform
android
Software Name
ooniprobe-android (2.2.0)
Measurement Engine
libmeasurement_kit (0.10.6)

Raw Measurement Data

Loading…