PageSourceSearch

https://app.eterex.com/vendor/telegram-web-app.js

js eterex.com collected 2026-09-24 13:33:57 UTC 116,510 bytes, 3,397 lines download raw bytes

1// WebView
2(function () {
3  var eventHandlers = {};
4
5  var locationHash = '';
6  try {
7    locationHash = location.hash.toString();
8  } catch (e) {}
9
10  var initParams = urlParseHashParams(locationHash);
11  var storedParams = sessionStorageGet('initParams');
12  if (storedParams) {
13    for (var key in storedParams) {
14      if (typeof initParams[key] === 'undefined') {
15        initParams[key] = storedParams[key];
16      }
17    }
18  }
19  sessionStorageSet('initParams', initParams);
20
21  var isIframe = false, iFrameStyle;
22  try {
23    isIframe = (window.parent != null && window != window.parent);
24    if (isIframe) {
25      window.addEventListener('message', function (event) {
26        if (event.source !== window.parent) return;
27        try {
28          var dataParsed = JSON.parse(event.data);
29        } catch (e) {
30          return;
31        }
32        if (!dataParsed || !dataParsed.eventType) {
33          return;
34        }
35        if (dataParsed.eventType == 'set_custom_style') {
36          if (event.origin === 'https://web.telegram.org') {
37            iFrameStyle.innerHTML = dataParsed.eventData;
38          }
39        } else if (dataParsed.eventType == 'reload_iframe') {
40          try {
41            window.parent.postMessage(JSON.stringify({eventType: 'iframe_will_reload'}), '*');
42          } catch (e) {}
43          location.reload();
44        } else {
45          receiveEvent(dataParsed.eventType, dataParsed.eventData);
46        }
47      });
48      iFrameStyle = document.createElement('style');
49      document.head.appendChild(iFrameStyle);
50      try {
51        window.parent.postMessage(JSON.stringify({eventType: 'iframe_ready', eventData: {reload_supported: true}}), '*');
52      } catch (e) {}
53    }
54  } catch (e) {}
55
56  function urlSafeDecode(urlencoded) {
57    try {
58      urlencoded = urlencoded.replace(/\+/g, '%20');
59      return decodeURIComponent(urlencoded);
60    } catch (e) {
61      return urlencoded;
62    }
63  }
64
65  function urlParseHashParams(locationHash) {
66    locationHash = locationHash.replace(/^#/, '');
67    var params = {};
68    if (!locationHash.length) {
69      return params;
70    }
71    if (locationHash.indexOf('=') < 0 && locationHash.indexOf('?') < 0) {
72      params._path = urlSafeDecode(locationHash);
73      return params;
74    }
75    var qIndex = locationHash.indexOf('?');
76    if (qIndex >= 0) {
77      var pathParam = locationHash.substr(0, qIndex);
78      params._path = urlSafeDecode(pathParam);
79      locationHash = locationHash.substr(qIndex + 1);
80    }
81    var query_params = urlParseQueryString(locationHash);
82    for (var k in query_params) {
83      params[k] = query_params[k];
84    }
85    return params;
86  }
87
88  function urlParseQueryString(queryString) {
89    var params = {};
90    if (!queryString.length) {
91      return params;
92    }
93    var queryStringParams = queryString.split('&');
94    var i, param, paramName, paramValue;
95    for (i = 0; i < queryStringParams.length; i++) {
96      param = queryStringParams[i].split('=');
97      paramName = urlSafeDecode(param[0]);
98      paramValue = param[1] == null ? null : urlSafeDecode(param[1]);
99      params[paramName] = paramValue;
100    }
101    return params;
102  }
103
104  // Telegram apps will implement this logic to add service params (e.g. tgShareScoreUrl) to game URL
105  function urlAppendHashParams(url, addHash) {
106    // url looks like 'https://game.com/path?query=1#hash'
107    // addHash looks like 'tgShareScoreUrl=' + encodeURIComponent('tgb://share_game_score?hash=very_long_hash123')
108
109    var ind = url.indexOf('#');
110    if (ind < 0) {
111      // https://game.com/path -> https://game.com/path#tgShareScoreUrl=etc
112      return url + '#' + addHash;
113    }
114    var curHash = url.substr(ind + 1);
115    if (curHash.indexOf('=') >= 0 || curHash.indexOf('?') >= 0) {
116      // https://game.com/#hash=1 -> https://game.com/#hash=1&tgShareScoreUrl=etc
117      // https://game.com/#path?query -> https://game.com/#path?query&tgShareScoreUrl=etc
118      return url + '&' + addHash;
119    }
120    // https://game.com/#hash -> https://game.com/#hash?tgShareScoreUrl=etc
121    if (curHash.length > 0) {
122      return url + '?' + addHash;
123    }
124    // https://game.com/# -> https://game.com/#tgShareScoreUrl=etc
125    return url + addHash;
126  }
127
128  function postEvent(eventType, callback, eventData) {
129    if (!callback) {
130      callback = function () {};
131    }
132    if (eventData === undefined) {
133      eventData = '';
134    }
135    console.log('[Telegram.WebView] > postEvent', eventType, eventData);
136
137    if (window.TelegramWebviewProxy !== undefined) {
138      TelegramWebviewProxy.postEvent(eventType, JSON.stringify(eventData));
139      callback();
140    }
141    else if (window.external && 'notify' in window.external) {
142      window.external.notify(JSON.stringify({eventType: eventType, eventData: eventData}));
143      callback();
144    }
145    else if (isIframe) {
146      try {
147        var trustedTarget = 'https://web.telegram.org';
148        // For now we don't restrict target, for testing purposes
149        trustedTarget = '*';
150        window.parent.postMessage(JSON.stringify({eventType: eventType, eventData: eventData}), trustedTarget);
151        callback();
152      } catch (e) {
153        callback(e);
154      }
155    }
156    else {
157      callback({notAvailable: true});
158    }
159  };
160
161  function receiveEvent(eventType, eventData) {
162    console.log('[Telegram.WebView] < receiveEvent', eventType, eventData);
163    callEventCallbacks(eventType, function(callback) {
164      callback(eventType, eventData);
165    });
166  }
167
168  function callEventCallbacks(eventType, func) {
169    var curEventHandlers = eventHandlers[eventType];
170    if (curEventHandlers === undefined ||
171        !curEventHandlers.length) {
172      return;
173    }
174    for (var i = 0; i < curEventHandlers.length; i++) {
175      try {
176        func(curEventHandlers[i]);
177      } catch (e) {}
178    }
179  }
180
181  function onEvent(eventType, callback) {
182    if (eventHandlers[eventType] === undefined) {
183      eventHandlers[eventType] = [];
184    }
185    var index = eventHandlers[eventType].indexOf(callback);
186    if (index === -1) {
187      eventHandlers[eventType].push(callback);
188    }
189  };
190
191  function offEvent(eventType, callback) {
192    if (eventHandlers[eventType] === undefined) {
193      return;
194    }
195    var index = eventHandlers[eventType].indexOf(callback);
196    if (index === -1) {
197      return;
198    }
199    eventHandlers[eventType].splice(index, 1);
200  };
201
202  function openProtoUrl(url) {
203    if (!url.match(/^(web\+)?tgb?:\/\/./)) {
204      return false;
205    }
206    var useIframe = navigator.userAgent.match(/iOS|iPhone OS|iPhone|iPod|iPad/i) ? true : false;
207    if (useIframe) {
208      var iframeContEl = document.getElementById('tgme_frame_cont') || document.body;
209      var iframeEl = document.createElement('iframe');
210      iframeContEl.appendChild(iframeEl);
211      var pageHidden = false;
212      var enableHidden = function () {
213        pageHidden = true;
214      };
215      window.addEventListener('pagehide', enableHidden, false);
216      window.addEventListener('blur', enableHidden, false);
217      if (iframeEl !== null) {
218        iframeEl.src = url;
219      }
220      setTimeout(function() {
221        if (!pageHidden) {
222          window.location = url;
223        }
224        window.removeEventListener('pagehide', enableHidden, false);
225        window.removeEventListener('blur', enableHidden, false);
226      }, 2000);
227    }
228    else {
229      window.location = url;
230    }
231    return true;
232  }
233
234  function sessionStorageSet(key, value) {
235    try {
236      window.sessionStorage.setItem('__telegram__' + key, JSON.stringify(value));
237      return true;
238    } catch(e) {}
239    return false;
240  }
241  function sessionStorageGet(key) {
242    try {
243      return JSON.parse(window.sessionStorage.getItem('__telegram__' + key));
244    } catch(e) {}
245    return null;
246  }
247
248  if (!window.Telegram) {
249    window.Telegram = {};
250  }
251  window.Telegram.WebView = {
252    initParams: initParams,
253    isIframe: isIframe,
254    onEvent: onEvent,
255    offEvent: offEvent,
256    postEvent: postEvent,
257    receiveEvent: receiveEvent,
258    callEventCallbacks: callEventCallbacks
259  };
260
261  window.Telegram.Utils = {
262    urlSafeDecode: urlSafeDecode,
263    urlParseQueryString: urlParseQueryString,
264    urlParseHashParams: urlParseHashParams,
265    urlAppendHashParams: urlAppendHashParams,
266    sessionStorageSet: sessionStorageSet,
267    sessionStorageGet: sessionStorageGet
268  };
269
270  // For Windows Phone app
271  window.TelegramGameProxy_receiveEvent = receiveEvent;
272
273  // App backward compatibility
274  window.TelegramGameProxy = {
275    receiveEvent: receiveEvent
276  };
277})();
278
279// WebApp
280(function () {
281  var Utils = window.Telegram.Utils;
282  var WebView = window.Telegram.WebView;
283  var initParams = WebView.initParams;
284  var isIframe = WebView.isIframe;
285
286  var WebApp = {};
287  var webAppInitData = '', webAppInitDataUnsafe = {};
288  var themeParams = {}, colorScheme = 'light';
289  var webAppVersion = '6.0';
290  var webAppPlatform = 'unknown';
291  var webAppIsActive = true;
292  var webAppIsFullscreen = false;
293  var webAppIsOrientationLocked = false;
294  var webAppBackgroundColor = 'bg_color';
295  var webAppHeaderColorKey = 'bg_color';
296  var webAppHeaderColor = null;
297
298  if (initParams.tgWebAppData && initParams.tgWebAppData.length) {
299    webAppInitData = initParams.tgWebAppData;
300    webAppInitDataUnsafe = Utils.urlParseQueryString(webAppInitData);
301    for (var key in webAppInitDataUnsafe) {
302      var val = webAppInitDataUnsafe[key];
303      try {
304        if (val.substr(0, 1) == '{' && val.substr(-1) == '}' ||
305            val.substr(0, 1) == '[' && val.substr(-1) == ']') {
306          webAppInitDataUnsafe[key] = JSON.parse(val);
307        }
308      } catch (e) {}
309    }
310  }
311  var stored_theme_params = Utils.sessionStorageGet('themeParams');
312  if (initParams.tgWebAppThemeParams && initParams.tgWebAppThemeParams.length) {
313    var themeParamsRaw = initParams.tgWebAppThemeParams;
314    try {
315      var theme_params = JSON.parse(themeParamsRaw);
316      if (theme_params) {
317        setThemeParams(theme_params);
318      }
319    } catch (e) {}
320  }
321  if (stored_theme_params) {
322    setThemeParams(stored_theme_params);
323  }
324  var stored_def_colors = Utils.sessionStorageGet('defaultColors');
325  if (initParams.tgWebAppDefaultColors && initParams.tgWebAppDefaultColors.length) {
326    var defColorsRaw = initParams.tgWebAppDefaultColors;
327    try {
328      var def_colors = JSON.parse(defColorsRaw);
329      if (def_colors) {
330        setDefaultColors(def_colors);
331      }
332    } catch (e) {}
333  }
334  if (stored_def_colors) {
335    setDefaultColors(stored_def_colors);
336  }
337  if (initParams.tgWebAppVersion) {
338    webAppVersion = initParams.tgWebAppVersion;
339  }
340  if (initParams.tgWebAppPlatform) {
341    webAppPlatform = initParams.tgWebAppPlatform;
342  }
343
344  var stored_fullscreen = Utils.sessionStorageGet('isFullscreen');
345  if (initParams.tgWebAppFullscreen) {
346    setFullscreen(true);
347  }
348  if (stored_fullscreen) {
349    setFullscreen(stored_fullscreen == 'yes');
350  }
351
352  var stored_orientation_lock = Utils.sessionStorageGet('isOrientationLocked');
353  if (stored_orientation_lock) {
354    setOrientationLock(stored_orientation_lock == 'yes');
355  }
356
357  function onThemeChanged(eventType, eventData) {
358    if (eventData.theme_params) {
359      setThemeParams(eventData.theme_params);
360      window.Telegram.WebApp.MainButton.setParams({});
361      window.Telegram.WebApp.SecondaryButton.setParams({});
362      updateHeaderColor();
363      updateBackgroundColor();
364      updateBottomBarColor();
365      receiveWebViewEvent('themeChanged');
366    }
367  }
368
369  var lastWindowHeight = window.innerHeight;
370  function onViewportChanged(eventType, eventData) {
371    if (eventData.height) {
372      window.removeEventListener('resize', onWindowResize);
373      setViewportHeight(eventData);
374    }
375  }
376
377  function onWindowResize(e) {
378    if (lastWindowHeight != window.innerHeight) {
379      lastWindowHeight = window.innerHeight;
380      receiveWebViewEvent('viewportChanged', {
381        isStateStable: true
382      });
383    }
384  }
385
386  function onSafeAreaChanged(eventType, eventData) {
387    if (eventData) {
388      setSafeAreaInset(eventData);
389    }
390  }
391  function onContentSafeAreaChanged(eventType, eventData) {
392    if (eventData) {
393      setContentSafeAreaInset(eventData);
394    }
395  }
396
397  function onVisibilityChanged(eventType, eventData) {
398    if (eventData.is_visible) {
399      webAppIsActive = true;
400      receiveWebViewEvent('activated');
401    } else {
402      webAppIsActive = false;
403      receiveWebViewEvent('deactivated');
404    }
405  }
406
407  function linkHandler(e) {
408    if (e.metaKey || e.ctrlKey) return;
409    var el = e.target;
410    while (el.tagName != 'A' && el.parentNode) {
411      el = el.parentNode;
412    }
413    if (el.tagName == 'A' &&
414        el.target != '_blank' &&
415        (el.protocol == 'http:' || el.protocol == 'https:') &&
416        isTmeHostname(el.hostname)) {
417      WebApp.openTelegramLink(el.href);
418      e.preventDefault();
419    }
420  }
421
422  function strTrim(str) {
423    return str.toString().replace(/^\s+|\s+$/g, '');
424  }
425
426  function isTmeHostname(hostname) {
427    hostname = hostname.toString().toLowerCase();
428    return hostname == 't.me' || hostname == 'telegram.me';
429  }
430
431  function receiveWebViewEvent(eventType) {
432    var args = Array.prototype.slice.call(arguments);
433    eventType = args.shift();
434    WebView.callEventCallbacks('webview:' + eventType, function(callback) {
435      callback.apply(WebApp, args);
436    });
437  }
438
439  function onWebViewEvent(eventType, callback) {
440    WebView.onEvent('webview:' + eventType, callback);
441  };
442
443  function offWebViewEvent(eventType, callback) {
444    WebView.offEvent('webview:' + eventType, callback);
445  };
446
447  function setCssProperty(name, value) {
448    var root = document.documentElement;
449    if (root && root.style && root.style.setProperty) {
450      root.style.setProperty('--tg-' + name, value);
451    }
452  }
453
454  function setFullscreen(is_fullscreen) {
455    webAppIsFullscreen = !!is_fullscreen;
456    Utils.sessionStorageSet('isFullscreen', webAppIsFullscreen ? 'yes' : 'no');
457  }
458
459  function setOrientationLock(is_locked) {
460    webAppIsOrientationLocked = !!is_locked;
461    Utils.sessionStorageSet('isOrientationLocked', webAppIsOrientationLocked ? 'yes' : 'no');
462  }
463
464  function setThemeParams(theme_params) {
465    // temp iOS fix
466    if (theme_params.bg_color == '#1c1c1d' &&
467        theme_params.bg_color == theme_params.secondary_bg_color) {
468      theme_params.secondary_bg_color = '#2c2c2e';
469    }
470    var color;
471    for (var key in theme_params) {
472      if (color = parseColorToHex(theme_params[key])) {
473        themeParams[key] = color;
474        if (key == 'bg_color') {
475          colorScheme = isColorDark(color) ? 'dark' : 'light'
476          setCssProperty('color-scheme', colorScheme);
477        }
478        key = 'theme-' + key.split('_').join('-');
479        setCssProperty(key, color);
480      }
481    }
482    Utils.sessionStorageSet('themeParams', themeParams);
483  }
484
485  function setDefaultColors(def_colors) {
486    if (colorScheme == 'dark') {
487      if (def_colors.bg_dark_color) {
488        webAppBackgroundColor = def_colors.bg_dark_color;
489      }
490      if (def_colors.header_dark_color) {
491        webAppHeaderColorKey = null;
492        webAppHeaderColor = def_colors.header_dark_color;
493      }
494    } else {
495      if (def_colors.bg_color) {
496        webAppBackgroundColor = def_colors.bg_color;
497      }
498      if (def_colors.header_color) {
499        webAppHeaderColorKey = null;
500        webAppHeaderColor = def_colors.header_color;
501      }
502    }
503    Utils.sessionStorageSet('defaultColors', def_colors);
504  }
505
506  var webAppCallbacks = {};
507  function generateCallbackId(len) {
508    var tries = 100;
509    while (--tries) {
510      var id = '', chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', chars_len = chars.length;
511      for (var i = 0; i < len; i++) {
512        id += chars[Math.floor(Math.random() * chars_len)];
513      }
514      if (!webAppCallbacks[id]) {
515        webAppCallbacks[id] = {};
516        return id;
517      }
518    }
519    throw Error('WebAppCallbackIdGenerateFailed');
520  }
521
522  var viewportHeight = false, viewportStableHeight = false, isExpanded = true;
523  function setViewportHeight(data) {
524    if (typeof data !== 'undefined') {
525      isExpanded = !!data.is_expanded;
526      viewportHeight = data.height;
527      if (data.is_state_stable) {
528        viewportStableHeight = data.height;
529      }
530      receiveWebViewEvent('viewportChanged', {
531        isStateStable: !!data.is_state_stable
532      });
533    }
534    var height, stable_height;
535    if (viewportHeight !== false) {
536      height = (viewportHeight - bottomBarHeight) + 'px';
537    } else {
538      height = bottomBarHeight ? 'calc(100vh - ' + bottomBarHeight + 'px)' : '100vh';
539    }
540    if (viewportStableHeight !== false) {
541      stable_height = (viewportStableHeight - bottomBarHeight) + 'px';
542    } else {
543      stable_height = bottomBarHeight ? 'calc(100vh - ' + bottomBarHeight + 'px)' : '100vh';
544    }
545    setCssProperty('viewport-height', height);
546    setCssProperty('viewport-stable-height', stable_height);
547  }
548
549  var safeAreaInset = {top: 0, bottom: 0, left: 0, right: 0};
550  function setSafeAreaInset(data) {
551    if (typeof data !== 'undefined') {
552      if (typeof data.top !== 'undefined') {
553        safeAreaInset.top = data.top;
554      }
555      if (typeof data.bottom !== 'undefined') {
556        safeAreaInset.bottom = data.bottom;
557      }
558      if (typeof data.left !== 'undefined') {
559        safeAreaInset.left = data.left;
560      }
561      if (typeof data.right !== 'undefined') {
562        safeAreaInset.right = data.right;
563      }
564      receiveWebViewEvent('safeAreaChanged');
565    }
566    setCssProperty('safe-area-inset-top', safeAreaInset.top + 'px');
567    setCssProperty('safe-area-inset-bottom', safeAreaInset.bottom + 'px');
568    setCssProperty('safe-area-inset-left', safeAreaInset.left + 'px');
569    setCssProperty('safe-area-inset-right', safeAreaInset.right + 'px');
570  }
571
572  var contentSafeAreaInset = {top: 0, bottom: 0, left: 0, right: 0};
573  function setContentSafeAreaInset(data) {
574    if (typeof data !== 'undefined') {
575      if (typeof data.top !== 'undefined') {
576        contentSafeAreaInset.top = data.top;
577      }
578      if (typeof data.bottom !== 'undefined') {
579        contentSafeAreaInset.bottom = data.bottom;
580      }
581      if (typeof data.left !== 'undefined') {
582        contentSafeAreaInset.left = data.left;
583      }
584      if (typeof data.right !== 'undefined') {
585        contentSafeAreaInset.right = data.right;
586      }
587      receiveWebViewEvent('contentSafeAreaChanged');
588    }
589    setCssProperty('content-safe-area-inset-top', contentSafeAreaInset.top + 'px');
590    setCssProperty('content-safe-area-inset-bottom', contentSafeAreaInset.bottom + 'px');
591    setCssProperty('content-safe-area-inset-left', contentSafeAreaInset.left + 'px');
592    setCssProperty('content-safe-area-inset-right', contentSafeAreaInset.right + 'px');
593  }
594
595  var isClosingConfirmationEnabled = false;
596  function setClosingConfirmation(need_confirmation) {
597    if (!versionAtLeast('6.2')) {
598      console.warn('[Telegram.WebApp] Closing confirmation is not supported in version ' + webAppVersion);
599      return;
600    }
601    isClosingConfirmationEnabled = !!need_confirmation;
602    WebView.postEvent('web_app_setup_closing_behavior', false, {need_confirmation: isClosingConfirmationEnabled});
603  }
604
605  var isVerticalSwipesEnabled = true;
606  function toggleVerticalSwipes(enable_swipes) {
607    if (!versionAtLeast('7.7')) {
608      console.warn('[Telegram.WebApp] Changing swipes behavior is not supported in version ' + webAppVersion);
609      return;
610    }
611    isVerticalSwipesEnabled = !!enable_swipes;
612    WebView.postEvent('web_app_setup_swipe_behavior', false, {allow_vertical_swipe: isVerticalSwipesEnabled});
613  }
614
615  function onFullscreenChanged(eventType, eventData) {
616    setFullscreen(eventData.is_fullscreen);
617    receiveWebViewEvent('fullscreenChanged');
618  }
619  function onFullscreenFailed(eventType, eventData) {
620    if (eventData.error == 'ALREADY_FULLSCREEN' && !webAppIsFullscreen) {
621      setFullscreen(true);
622    }
623    receiveWebViewEvent('fullscreenFailed', {
624      error: eventData.error
625    });
626  }
627
628  function toggleOrientationLock(locked) {
629    if (!versionAtLeast('8.0')) {
630      console.warn('[Telegram.WebApp] Orientation locking is not supported in version ' + webAppVersion);
631      return;
632    }
633    setOrientationLock(locked);
634    WebView.postEvent('web_app_toggle_orientation_lock', false, {locked: webAppIsOrientationLocked});
635  }
636
637  var homeScreenCallbacks = [];
638  function onHomeScreenAdded(eventType, eventData) {
639    receiveWebViewEvent('homeScreenAdded');
640  }
641  function onHomeScreenChecked(eventType, eventData) {
642    var status = eventData.status || 'unknown';
643    if (homeScreenCallbacks.length > 0) {
644      for (var i = 0; i < homeScreenCallbacks.length; i++) {
645        var callback = homeScreenCallbacks[i];
646        callback(status);
647      }
648      homeScreenCallbacks = [];
649    }
650    receiveWebViewEvent('homeScreenChecked', {
651      status: status
652    });
653  }
654
655  var WebAppShareMessageOpened = false;
656  function onPreparedMessageSent(eventType, eventData) {
657    if (WebAppShareMessageOpened) {
658      var requestData = WebAppShareMessageOpened;
659      WebAppShareMessageOpened = false;
660      if (requestData.callback) {
661        requestData.callback(true);
662      }
663      receiveWebViewEvent('shareMessageSent');
664    }
665  }
666  function onPreparedMessageFailed(eventType, eventData) {
667    if (WebAppShareMessageOpened) {
668      var requestData = WebAppShareMessageOpened;
669      WebAppShareMessageOpened = false;
670      if (requestData.callback) {
671        requestData.callback(false);
672      }
673      receiveWebViewEvent('shareMessageFailed', {
674        error: eventData.error
675      });
676    }
677  }
678
679  var WebAppRequestChatOpened = false;
680  function onRequestedChatSent(eventType, eventData) {
681    if (WebAppRequestChatOpened) {
682      var requestData = WebAppRequestChatOpened;
683      WebAppRequestChatOpened = false;
684      if (requestData.callback) {
685        requestData.callback(true);
686      }
687      receiveWebViewEvent('requestedChatSent');
688    }
689  }
690  function onRequestedChatFailed(eventType, eventData) {
691    if (WebAppRequestChatOpened) {
692      var requestData = WebAppRequestChatOpened;
693      WebAppRequestChatOpened = false;
694      if (requestData.callback) {
695        requestData.callback(false);
696      }
697      receiveWebViewEvent('requestedChatFailed', {
698        error: eventData.error
699      });
700    }
701  }
702
703  var WebAppEmojiStatusRequested = false;
704  function onEmojiStatusSet(eventType, eventData) {
705    if (WebAppEmojiStatusRequested) {
706      var requestData = WebAppEmojiStatusRequested;
707      WebAppEmojiStatusRequested = false;
708      if (requestData.callback) {
709        requestData.callback(true);
710      }
711      receiveWebViewEvent('emojiStatusSet');
712    }
713  }
714  function onEmojiStatusFailed(eventType, eventData) {
715    if (WebAppEmojiStatusRequested) {
716      var requestData = WebAppEmojiStatusRequested;
717      WebAppEmojiStatusRequested = false;
718      if (requestData.callback) {
719        requestData.callback(false);
720      }
721      receiveWebViewEvent('emojiStatusFailed', {
722        error: eventData.error
723      });
724    }
725  }
726  var WebAppEmojiStatusAccessRequested = false;
727  function onEmojiStatusAccessRequested(eventType, eventData) {
728    if (WebAppEmojiStatusAccessRequested) {
729      var requestData = WebAppEmojiStatusAccessRequested;
730      WebAppEmojiStatusAccessRequested = false;
731      if (requestData.callback) {
732        requestData.callback(eventData.status == 'allowed');
733      }
734      receiveWebViewEvent('emojiStatusAccessRequested', {
735        status: eventData.status
736      });
737    }
738  }
739
740  var webAppPopupOpened = false;
741  function onPopupClosed(eventType, eventData) {
742    if (webAppPopupOpened) {
743      var popupData = webAppPopupOpened;
744      webAppPopupOpened = false;
745      var button_id = null;
746      if (typeof eventData.button_id !== 'undefined') {
747        button_id = eventData.button_id;
748      }
749      if (popupData.callback) {
750        popupData.callback(button_id);
751      }
752      receiveWebViewEvent('popupClosed', {
753        button_id: button_id
754      });
755    }
756  }
757
758
759  function getHeaderColor() {
760    if (webAppHeaderColorKey == 'secondary_bg_color') {
761      return themeParams.secondary_bg_color;
762    } else if (webAppHeaderColorKey == 'bg_color') {
763      return themeParams.bg_color;
764    }
765    return webAppHeaderColor;
766  }
767  function setHeaderColor(color) {
768    if (!versionAtLeast('6.1')) {
769      console.warn('[Telegram.WebApp] Header color is not supported in version ' + webAppVersion);
770      return;
771    }
772    if (!versionAtLeast('6.9')) {
773      if (themeParams.bg_color &&
774          themeParams.bg_color == color) {
775        color = 'bg_color';
776      } else if (themeParams.secondary_bg_color &&
777                 themeParams.secondary_bg_color == color) {
778        color = 'secondary_bg_color';
779      }
780    }
781    var head_color = null, color_key = null;
782    if (color == 'bg_color' || color == 'secondary_bg_color') {
783      color_key = color;
784    } else if (versionAtLeast('6.9')) {
785      head_color = parseColorToHex(color);
786      if (!head_color) {
787        console.error('[Telegram.WebApp] Header color format is invali
787d', color);
788        throw Error('WebAppHeaderColorInvalid');
789      }
790    }
791    if (!versionAtLeast('6.9') &&
792        color_key != 'bg_color' &&
793        color_key != 'secondary_bg_color') {
794      console.error('[Telegram.WebApp] Header color key should be one of Telegram.WebApp.themeParams.bg_color, Telegram.WebApp.themeParams.secondary_bg_color, \'bg_color\', \'secondary_bg_color\'', color);
795      throw Error('WebAppHeaderColorKeyInvalid');
796    }
797    webAppHeaderColorKey = color_key;
798    webAppHeaderColor = head_color;
799    updateHeaderColor();
800  }
801  var appHeaderColorKey = null, appHeaderColor = null;
802  function updateHeaderColor() {
803    if (appHeaderColorKey != webAppHeaderColorKey ||
804        appHeaderColor != webAppHeaderColor) {
805      appHeaderColorKey = webAppHeaderColorKey;
806      appHeaderColor = webAppHeaderColor;
807      if (appHeaderColor) {
808        WebView.postEvent('web_app_set_header_color', false, {color: webAppHeaderColor});
809      } else {
810        WebView.postEvent('web_app_set_header_color', false, {color_key: webAppHeaderColorKey});
811      }
812    }
813  }
814
815  function getBackgroundColor() {
816    if (webAppBackgroundColor == 'secondary_bg_color') {
817      return themeParams.secondary_bg_color;
818    } else if (webAppBackgroundColor == 'bg_color') {
819      return themeParams.bg_color;
820    }
821    return webAppBackgroundColor;
822  }
823  function setBackgroundColor(color) {
824    if (!versionAtLeast('6.1')) {
825      console.warn('[Telegram.WebApp] Background color is not supported in version ' + webAppVersion);
826      return;
827    }
828    var bg_color;
829    if (color == 'bg_color' || color == 'secondary_bg_color') {
830      bg_color = color;
831    } else {
832      bg_color = parseColorToHex(color);
833      if (!bg_color) {
834        console.error('[Telegram.WebApp] Background color format is invalid', color);
835        throw Error('WebAppBackgroundColorInvalid');
836      }
837    }
838    webAppBackgroundColor = bg_color;
839    updateBackgroundColor();
840  }
841  var appBackgroundColor = null;
842  function updateBackgroundColor() {
843    var color = getBackgroundColor();
844    if (appBackgroundColor != color) {
845      appBackgroundColor = color;
846      WebView.postEvent('web_app_set_background_color', false, {color: color});
847    }
848  }
849
850  var bottomBarColor = 'bottom_bar_bg_color';
851  function getBottomBarColor() {
852    if (bottomBarColor == 'bottom_bar_bg_color') {
853      return themeParams.bottom_bar_bg_color || themeParams.secondary_bg_color || '#ffffff';
854    } else if (bottomBarColor == 'secondary_bg_color') {
855      return themeParams.secondary_bg_color;
856    } else if (bottomBarColor == 'bg_color') {
857      return themeParams.bg_color;
858    }
859    return bottomBarColor;
860  }
861  function setBottomBarColor(color) {
862    if (!versionAtLeast('7.10')) {
863      console.warn('[Telegram.WebApp] Bottom bar color is not supported in version ' + webAppVersion);
864      return;
865    }
866    var bg_color;
867    if (color == 'bg_color' || color == 'secondary_bg_color' || color == 'bottom_bar_bg_color') {
868      bg_color = color;
869    } else {
870      bg_color = parseColorToHex(color);
871      if (!bg_color) {
872        console.error('[Telegram.WebApp] Bottom bar color format is invalid', color);
873        throw Error('WebAppBottomBarColorInvalid');
874      }
875    }
876    bottomBarColor = bg_color;
877    updateBottomBarColor();
878    window.Telegram.WebApp.SecondaryButton.setParams({});
879  }
880  var appBottomBarColor = null;
881  function updateBottomBarColor() {
882    var color = getBottomBarColor();
883    if (appBottomBarColor != color) {
884      appBottomBarColor = color;
885      WebView.postEvent('web_app_set_bottom_bar_color', false, {color: color});
886    }
887    if (initParams.tgWebAppDebug) {
888      updateDebugBottomBar();
889    }
890  }
891
892
893  function parseColorToHex(color) {
894    color += '';
895    var match;
896    if (match = /^\s*#([0-9a-f]{6})\s*$/i.exec(color)) {
897      return '#' + match[1].toLowerCase();
898    }
899    else if (match = /^\s*#([0-9a-f])([0-9a-f])([0-9a-f])\s*$/i.exec(color)) {
900      return ('#' + match[1] + match[1] + match[2] + match[2] + match[3] + match[3]).toLowerCase();
901    }
902    else if (match = /^\s*rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)\s*$/.exec(color)) {
903      var r = parseInt(match[1]), g = parseInt(match[2]), b = parseInt(match[3]);
904      r = (r < 16 ? '0' : '') + r.toString(16);
905      g = (g < 16 ? '0' : '') + g.toString(16);
906      b = (b < 16 ? '0' : '') + b.toString(16);
907      return '#' + r + g + b;
908    }
909    return false;
910  }
911
912  function isColorDark(rgb) {
913    rgb = rgb.replace(/[\s#]/g, '');
914    if (rgb.length == 3) {
915      rgb = rgb[0] + rgb[0] + rgb[1] + rgb[1] + rgb[2] + rgb[2];
916    }
917    var r = parseInt(rgb.substr(0, 2), 16);
918    var g = parseInt(rgb.substr(2, 2), 16);
919    var b = parseInt(rgb.substr(4, 2), 16);
920    var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b));
921    return hsp < 120;
922  }
923
924  function versionCompare(v1, v2) {
925    if (typeof v1 !== 'string') v1 = '';
926    if (typeof v2 !== 'string') v2 = '';
927    v1 = v1.replace(/^\s+|\s+$/g, '').split('.');
928    v2 = v2.replace(/^\s+|\s+$/g, '').split('.');
929    var a = Math.max(v1.length, v2.length), i, p1, p2;
930    for (i = 0; i < a; i++) {
931      p1 = parseInt(v1[i]) || 0;
932      p2 = parseInt(v2[i]) || 0;
933      if (p1 == p2) continue;
934      if (p1 > p2) return 1;
935      return -1;
936    }
937    return 0;
938  }
939
940  function versionAtLeast(ver) {
941    return versionCompare(webAppVersion, ver) >= 0;
942  }
943
944  function byteLength(str) {
945    if (window.Blob) {
946      try { return new Blob([str]).size; } catch (e) {}
947    }
948    var s = str.length;
949    for (var i=str.length-1; i>=0; i--) {
950      var code = str.charCodeAt(i);
951      if (code > 0x7f && code <= 0x7ff) s++;
952      else if (code > 0x7ff && code <= 0xffff) s+=2;
953      if (code >= 0xdc00 && code <= 0xdfff) i--;
954    }
955    return s;
956  }
957
958  var BackButton = (function() {
959    var isVisible = false;
960
961    var backButton = {};
962    Object.defineProperty(backButton, 'isVisible', {
963      set: function(val){ setParams({is_visible: val}); },
964      get: function(){ return isVisible; },
965      enumerable: true
966    });
967
968    var curButtonState = null;
969
970    WebView.onEvent('back_button_pressed', onBackButtonPressed);
971
972    function onBackButtonPressed() {
973      receiveWebViewEvent('backButtonClicked');
974    }
975
976    function buttonParams() {
977      return {is_visible: isVisible};
978    }
979
980    function buttonState(btn_params) {
981      if (typeof btn_params === 'undefined') {
982        btn_params = buttonParams();
983      }
984      return JSON.stringify(btn_params);
985    }
986
987    function buttonCheckVersion() {
988      if (!versionAtLeast('6.1')) {
989        console.warn('[Telegram.WebApp] BackButton is not supported in version ' + webAppVersion);
990        return false;
991      }
992      return true;
993    }
994
995    function updateButton() {
996      var btn_params = buttonParams();
997      var btn_state = buttonState(btn_params);
998      if (curButtonState === btn_state) {
999        return;
1000      }
1001      curButtonState = btn_state;
1002      WebView.postEvent('web_app_setup_back_button', false, btn_params);
1003    }
1004
1005    function setParams(params) {
1006      if (!buttonCheckVersion()) {
1007        return backButton;
1008      }
1009      if (typeof params.is_visible !== 'undefined') {
1010        isVisible = !!params.is_visible;
1011      }
1012      updateButton();
1013      return backButton;
1014    }
1015
1016    backButton.onClick = function(callback) {
1017      if (buttonCheckVersion()) {
1018        onWebViewEvent('backButtonClicked', callback);
1019      }
1020      return backButton;
1021    };
1022    backButton.offClick = function(callback) {
1023      if (buttonCheckVersion()) {
1024        offWebViewEvent('backButtonClicked', callback);
1025      }
1026      return backButton;
1027    };
1028    backButton.show = function() {
1029      return setParams({is_visible: true});
1030    };
1031    backButton.hide = function() {
1032      return setParams({is_visible: false});
1033    };
1034    return backButton;
1035  })();
1036
1037  var debugBottomBar = null, debugBottomBarBtns = {}, bottomBarHeight = 0;
1038  if (initParams.tgWebAppDebug) {
1039    debugBottomBar = document.createElement('tg-bottom-bar');
1040    var debugBottomBarStyle = {
1041      display: 'flex',
1042      gap: '7px',
1043      font: '600 14px/18px sans-serif',
1044      width: '100%',
1045      background: getBottomBarColor(),
1046      position: 'fixed',
1047      left: '0',
1048      right: '0',
1049      bottom: '0',
1050      margin: '0',
1051      padding: '7px',
1052      textAlign: 'center',
1053      boxSizing: 'border-box',
1054      zIndex: '10000'
1055    };
1056    for (var k in debugBottomBarStyle) {
1057      debugBottomBar.style[k] = debugBottomBarStyle[k];
1058    }
1059    document.addEventListener('DOMContentLoaded', function onDomLoaded(event) {
1060      document.removeEventListener('DOMContentLoaded', onDomLoaded);
1061      document.body.appendChild(debugBottomBar);
1062    });
1063    var animStyle = document.createElement('style');
1064    animStyle.innerHTML = 'tg-bottom-button.shine { position: relative; overflow: hidden; } tg-bottom-button.shine:before { content:""; position: absolute; top: 0; width: 100%; height: 100%; background: linear-gradient(120deg, transparent, rgba(255, 255, 255, .2), transparent); animation: tg-bottom-button-shine 5s ease-in-out infinite; } @-webkit-keyframes tg-bottom-button-shine { 0% {left: -100%;} 12%,100% {left: 100%}} @keyframes tg-bottom-button-shine { 0% {left: -100%;} 12%,100% {left: 100%}}';
1065    debugBottomBar.appendChild(animStyle);
1066  }
1067  function updateDebugBottomBar() {
1068    var mainBtn = debugBottomBarBtns.main._bottomButton;
1069    var secondaryBtn = debugBottomBarBtns.secondary._bottomButton;
1070    if (mainBtn.isVisible || secondaryBtn.isVisible) {
1071      debugBottomBar.style.display = 'flex';
1072      bottomBarHeight = 58;
1073      if (mainBtn.isVisible && secondaryBtn.isVisible) {
1074        if (secondaryBtn.position == 'top') {
1075          debugBottomBar.style.flexDirection = 'column-reverse';
1076          bottomBarHeight += 51;
1077        } else if (secondaryBtn.position == 'bottom') {
1078          debugBottomBar.style.flexDirection = 'column';
1079          bottomBarHeight += 51;
1080        } else if (secondaryBtn.position == 'left') {
1081          debugBottomBar.style.flexDirection = 'row-reverse';
1082        } else if (secondaryBtn.position == 'right') {
1083          debugBottomBar.style.flexDirection = 'row';
1084        }
1085      }
1086    } else {
1087      debugBottomBar.style.display = 'none';
1088      bottomBarHeight = 0;
1089    }
1090    debugBottomBar.style.background = getBottomBarColor();
1091    if (document.documentElement) {
1092      document.documentElement.style.boxSizing = 'border-box';
1093      document.documentElement.style.paddingBottom = bottomBarHeight + 'px';
1094    }
1095    setViewportHeight();
1096  }
1097
1098
1099  var BottomButtonConstructor = function(type) {
1100    var isMainButton = (type == 'main');
1101    if (isMainButton) {
1102      var setupFnName = 'web_app_setup_main_button';
1103      var tgEventName = 'main_button_pressed';
1104      var webViewEventName = 'mainButtonClicked';
1105      var buttonTextDefault = 'Continue';
1106      var buttonColorDefault = function(){ return themeParams.button_color || '#2481cc'; };
1107      var buttonTextColorDefault = function(){ return themeParams.button_text_color || '#ffffff'; };
1108    } else {
1109      var setupFnName = 'web_app_setup_secondary_button';
1110      var tgEventName = 'secondary_button_pressed';
1111      var webViewEventName = 'secondaryButtonClicked';
1112      var buttonTextDefault = 'Cancel';
1113      var buttonColorDefault = function(){ return getBottomBarColor(); };
1114      var buttonTextColorDefault = function(){ return themeParams.button_color || '#2481cc'; };
1115    }
1116
1117    var isVisible = false;
1118    var isActive = true;
1119    var hasShineEffect = false;
1120    var isProgressVisible = false;
1121    var iconCustomEmojiId = false;
1122    var buttonType = type;
1123    var buttonText = buttonTextDefault;
1124    var buttonColor = false;
1125    var buttonTextColor = false;
1126    var buttonPosition = 'left';
1127
1128    var bottomButton = {};
1129    Object.defineProperty(bottomButton, 'type', {
1130      get: function(){ return buttonType; },
1131      enumerable: true
1132    });
1133    Object.defineProperty(bottomButton, 'iconCustomEmojiId', {
1134      set: function(val){ bottomButton.setParams({icon_custom_emoji_id: val}); },
1135      get: function(){ return iconCustomEmojiId; },
1136      enumerable: true
1137    });
1138    Object.defineProperty(bottomButton, 'text', {
1139      set: function(val){ bottomButton.setParams({text: val}); },
1140      get: function(){ return buttonText; },
1141      enumerable: true
1142    });
1143    Object.defineProperty(bottomButton, 'color', {
1144      set: function(val){ bottomButton.setParams({color: val}); },
1145      get: function(){ return buttonColor || buttonColorDefault(); },
1146      enumerable: true
1147    });
1148    Object.defineProperty(bottomButton, 'textColor', {
1149      set: function(val){ bottomButton.setParams({text_color: val}); },
1150      get: function(){ return buttonTextColor || buttonTextColorDefault(); },
1151      enumerable: true
1152    });
1153    Object.defineProperty(bottomButton, 'isVisible', {
1154      set: function(val){ bottomButton.setParams({is_visible: val}); },
1155      get: function(){ return isVisible; },
1156      enumerable: true
1157    });
1158    Object.defineProperty(bottomButton, 'isProgressVisible', {
1159      get: function(){ return isProgressVisible; },
1160      enumerable: true
1161    });
1162    Object.defineProperty(bottomButton, 'isActive', {
1163      set: function(val){ bottomButton.setParams({is_active: val}); },
1164      get: function(){ return isActive; },
1165      enumerable: true
1166    });
1167    Object.defineProperty(bottomButton, 'hasShineEffect', {
1168      set: function(val){ bottomButton.setParams({has_shine_effect: val}); },
1169      get: function(){ return hasShineEffect; },
1170      enumerable: true
1171    });
1172    if (!isMainButton) {
1173      Object.defineProperty(bottomButton, 'position', {
1174        set: function(val){ bottomButton.setParams({position: val}); },
1175        get: function(){ return buttonPosition; },
1176        enumerable: true
1177      });
1178    }
1179
1180    var curButtonState = null;
1181
1182    WebView.onEvent(tgEventName, onBottomButtonPressed);
1183
1184    var debugBtn = null;
1185    if (initParams.tgWebAppDebug) {
1186      debugBtn = document.createElement('tg-bottom-button');
1187      var debugBtnStyle = {
1188        display: 'none',
1189        width: '100%',
1190        height: '44px',
1191        borderRadius: '0',
1192        background: 'no-repeat right center',
1193        padding: '13px 15px',
1194        textAlign: 'center',
1195        boxSizing: 'border-box'
1196      };
1197      for (var k in debugBtnStyle) {
1198        debugBtn.style[k] = debugBtnStyle[k];
1199      }
1200      debugBottomBar.appendChild(debugBtn);
1201      debugBtn.addEventListener('click', onBottomButtonPressed, false);
1202      debugBtn._bottomButton = bottomButton;
1203      debugBottomBarBtns[type] = debugBtn;
1204    }
1205
1206    function onBottomButtonPressed() {
1207      if (isActive) {
1208        receiveWebViewEvent(webViewEventName);
1209      }
1210    }
1211
1212    function buttonParams() {
1213      var color = bottomButton.color;
1214      var text_color = bottomButton.textColor;
1215      if (isVisible) {
1216        var params = {
1217          is_visible: true,
1218          is_active: isActive,
1219          is_progress_visible: isProgressVisible,
1220          icon_custom_emoji_id: iconCustomEmojiId,
1221          text: buttonText,
1222          color: color,
1223          text_color: text_color,
1224          has_shine_effect: hasShineEffect && isActive && !isProgressVisible
1225        };
1226        if (!isMainButton) {
1227          params.position = buttonPosition;
1228        }
1229      } else {
1230        var params = {
1231          is_visible: false
1232        };
1233      }
1234      return params;
1235    }
1236
1237    function buttonState(btn_params) {
1238      if (typeof btn_params === 'undefined') {
1239        btn_params = buttonParams();
1240      }
1241      return JSON.stringify(btn_params);
1242    }
1243
1244    function updateButton() {
1245      var btn_params = buttonParams();
1246      var btn_state = buttonState(btn_params);
1247      if (curButtonState === btn_state) {
1248        return;
1249      }
1250      curButtonState = btn_state;
1251      WebView.postEvent(setupFnName, false, btn_params);
1252      if (initParams.tgWebAppDebug) {
1253        updateDebugButton(btn_params);
1254      }
1255    }
1256
1257    function updateDebugButton(btn_params) {
1258      if (btn_params.is_visible) {
1259        debugBtn.style.display = 'block';
1260
1261        debugBtn.style.opacity = btn_params.is_active ? '1' : '0.8';
1262        debugBtn.style.cursor = btn_params.is_active ? 'pointer' : 'auto';
1263        debugBtn.disabled = !btn_params.is_active;
1264        debugBtn.innerText = btn_params.text;
1265        debugBtn.className = btn_params.has_shine_effect ? 'shine' : '';
1266        debugBtn.style.backgroundImage = btn_params.is_progress_visible ? "url('data:image/svg+xml," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewport="0 0 48 48" width="48px" height="48px"><circle cx="50%" cy="50%" stroke="' + btn_params.text_color + '" stroke-width="2.25" stroke-linecap="round" fill="none" stroke-dashoffset="106" r="9" stroke-dasharray="56.52" rotate="-90"><animate attributeName="stroke-dashoffset" attributeType="XML" dur="360s" from="0" to="12500" repeatCount="indef
1266inite"></animate><animateTransform attributeName="transform" attributeType="XML" type="rotate" dur="1s" from="-90 24 24" to="630 24 24" repeatCount="indefinite"></animateTransform></circle></svg>') + "')" : 'none';
1267        debugBtn.style.backgroundColor = btn_params.color;
1268        debugBtn.style.color = btn_params.text_color;
1269      } else {
1270        debugBtn.style.display = 'none';
1271      }
1272      updateDebugBottomBar();
1273    }
1274
1275    function setParams(params) {
1276      if (typeof params.icon_custom_emoji_id !== 'undefined') {
1277        var emoji_id = params.icon_custom_emoji_id;
1278        if (emoji_id === false || emoji_id === null) {
1279          emoji_id = '';
1280        }
1281        if (emoji_id !== '' && !/^[0-9]{10,20}$/.test(emoji_id)) {
1282          console.error('[Telegram.WebApp] Bottom button icon custom emoji is invalid', params.icon_custom_emoji_id);
1283          throw Error('WebAppBottomButtonParamInvalid');
1284        }
1285        iconCustomEmojiId = emoji_id;
1286      }
1287      if (typeof params.text !== 'undefined') {
1288        var text = strTrim(params.text);
1289        if (!text.length && !iconCustomEmojiId) {
1290          console.error('[Telegram.WebApp] Bottom button text is required', params.text);
1291          throw Error('WebAppBottomButtonParamInvalid');
1292        }
1293        if (text.length > 64) {
1294          console.error('[Telegram.WebApp] Bottom button text is too long', text);
1295          throw Error('WebAppBottomButtonParamInvalid');
1296        }
1297        buttonText = text;
1298      }
1299      if (typeof params.color !== 'undefined') {
1300        if (params.color === false ||
1301            params.color === null) {
1302          buttonColor = false;
1303        } else {
1304          var color = parseColorToHex(params.color);
1305          if (!color) {
1306            console.error('[Telegram.WebApp] Bottom button color format is invalid', params.color);
1307            throw Error('WebAppBottomButtonParamInvalid');
1308          }
1309          buttonColor = color;
1310        }
1311      }
1312      if (typeof params.text_color !== 'undefined') {
1313        if (params.text_color === false ||
1314            params.text_color === null) {
1315          buttonTextColor = false;
1316        } else {
1317          var text_color = parseColorToHex(params.text_color);
1318          if (!text_color) {
1319            console.error('[Telegram.WebApp] Bottom button text color format is invalid', params.text_color);
1320            throw Error('WebAppBottomButtonParamInvalid');
1321          }
1322          buttonTextColor = text_color;
1323        }
1324      }
1325      if (typeof params.is_visible !== 'undefined') {
1326        if (params.is_visible &&
1327            !bottomButton.text.length) {
1328          console.error('[Telegram.WebApp] Bottom button text is required');
1329          throw Error('WebAppBottomButtonParamInvalid');
1330        }
1331        isVisible = !!params.is_visible;
1332      }
1333      if (typeof params.has_shine_effect !== 'undefined') {
1334        hasShineEffect = !!params.has_shine_effect;
1335      }
1336      if (!isMainButton && typeof params.position !== 'undefined') {
1337        if (params.position != 'left' && params.position != 'right' &&
1338            params.position != 'top' && params.position != 'bottom') {
1339          console.error('[Telegram.WebApp] Bottom button posiition is invalid', params.position);
1340          throw Error('WebAppBottomButtonParamInvalid');
1341        }
1342        buttonPosition = params.position;
1343      }
1344      if (typeof params.is_active !== 'undefined') {
1345        isActive = !!params.is_active;
1346      }
1347      updateButton();
1348      return bottomButton;
1349    }
1350
1351    bottomButton.setText = function(text) {
1352      return bottomButton.setParams({text: text});
1353    };
1354    bottomButton.onClick = function(callback) {
1355      onWebViewEvent(webViewEventName, callback);
1356      return bottomButton;
1357    };
1358    bottomButton.offClick = function(callback) {
1359      offWebViewEvent(webViewEventName, callback);
1360      return bottomButton;
1361    };
1362    bottomButton.show = function() {
1363      return bottomButton.setParams({is_visible: true});
1364    };
1365    bottomButton.hide = function() {
1366      return bottomButton.setParams({is_visible: false});
1367    };
1368    bottomButton.enable = function() {
1369      return bottomButton.setParams({is_active: true});
1370    };
1371    bottomButton.disable = function() {
1372      return bottomButton.setParams({is_active: false});
1373    };
1374    bottomButton.showProgress = function(leaveActive) {
1375      isActive = !!leaveActive;
1376      isProgressVisible = true;
1377      updateButton();
1378      return bottomButton;
1379    };
1380    bottomButton.hideProgress = function() {
1381      if (!bottomButton.isActive) {
1382        isActive = true;
1383      }
1384      isProgressVisible = false;
1385      updateButton();
1386      return bottomButton;
1387    }
1388    bottomButton.setParams = setParams;
1389    return bottomButton;
1390  };
1391  var MainButton = BottomButtonConstructor('main');
1392  var SecondaryButton = BottomButtonConstructor('secondary');
1393
1394  var SettingsButton = (function() {
1395    var isVisible = false;
1396
1397    var settingsButton = {};
1398    Object.defineProperty(settingsButton, 'isVisible', {
1399      set: function(val){ setParams({is_visible: val}); },
1400      get: function(){ return isVisible; },
1401      enumerable: true
1402    });
1403
1404    var curButtonState = null;
1405
1406    WebView.onEvent('settings_button_pressed', onSettingsButtonPressed);
1407
1408    function onSettingsButtonPressed() {
1409      receiveWebViewEvent('settingsButtonClicked');
1410    }
1411
1412    function buttonParams() {
1413      return {is_visible: isVisible};
1414    }
1415
1416    function buttonState(btn_params) {
1417      if (typeof btn_params === 'undefined') {
1418        btn_params = buttonParams();
1419      }
1420      return JSON.stringify(btn_params);
1421    }
1422
1423    function buttonCheckVersion() {
1424      if (!versionAtLeast('6.10')) {
1425        console.warn('[Telegram.WebApp] SettingsButton is not supported in version ' + webAppVersion);
1426        return false;
1427      }
1428      return true;
1429    }
1430
1431    function updateButton() {
1432      var btn_params = buttonParams();
1433      var btn_state = buttonState(btn_params);
1434      if (curButtonState === btn_state) {
1435        return;
1436      }
1437      curButtonState = btn_state;
1438      WebView.postEvent('web_app_setup_settings_button', false, btn_params);
1439    }
1440
1441    function setParams(params) {
1442      if (!buttonCheckVersion()) {
1443        return settingsButton;
1444      }
1445      if (typeof params.is_visible !== 'undefined') {
1446        isVisible = !!params.is_visible;
1447      }
1448      updateButton();
1449      return settingsButton;
1450    }
1451
1452    settingsButton.onClick = function(callback) {
1453      if (buttonCheckVersion()) {
1454        onWebViewEvent('settingsButtonClicked', callback);
1455      }
1456      return settingsButton;
1457    };
1458    settingsButton.offClick = function(callback) {
1459      if (buttonCheckVersion()) {
1460        offWebViewEvent('settingsButtonClicked', callback);
1461      }
1462      return settingsButton;
1463    };
1464    settingsButton.show = function() {
1465      return setParams({is_visible: true});
1466    };
1467    settingsButton.hide = function() {
1468      return setParams({is_visible: false});
1469    };
1470    return settingsButton;
1471  })();
1472
1473  var HapticFeedback = (function() {
1474    var hapticFeedback = {};
1475
1476    function triggerFeedback(params) {
1477      if (!versionAtLeast('6.1')) {
1478        console.warn('[Telegram.WebApp] HapticFeedback is not supported in version ' + webAppVersion);
1479        return hapticFeedback;
1480      }
1481      if (params.type == 'impact') {
1482        if (params.impact_style != 'light' &&
1483            params.impact_style != 'medium' &&
1484            params.impact_style != 'heavy' &&
1485            params.impact_style != 'rigid' &&
1486            params.impact_style != 'soft') {
1487          console.error('[Telegram.WebApp] Haptic impact style is invalid', params.impact_style);
1488          throw Error('WebAppHapticImpactStyleInvalid');
1489        }
1490      } else if (params.type == 'notification') {
1491        if (params.notification_type != 'error' &&
1492            params.notification_type != 'success' &&
1493            params.notification_type != 'warning') {
1494          console.error('[Telegram.WebApp] Haptic notification type is invalid', params.notification_type);
1495          throw Error('WebAppHapticNotificationTypeInvalid');
1496        }
1497      } else if (params.type == 'selection_change') {
1498        // no params needed
1499      } else {
1500        console.error('[Telegram.WebApp] Haptic feedback type is invalid', params.type);
1501        throw Error('WebAppHapticFeedbackTypeInvalid');
1502      }
1503      WebView.postEvent('web_app_trigger_haptic_feedback', false, params);
1504      return hapticFeedback;
1505    }
1506
1507    hapticFeedback.impactOccurred = function(style) {
1508      return triggerFeedback({type: 'impact', impact_style: style});
1509    };
1510    hapticFeedback.notificationOccurred = function(type) {
1511      return triggerFeedback({type: 'notification', notification_type: type});
1512    };
1513    hapticFeedback.selectionChanged = function() {
1514      return triggerFeedback({type: 'selection_change'});
1515    };
1516    return hapticFeedback;
1517  })();
1518
1519  var CloudStorage = (function() {
1520    var cloudStorage = {};
1521
1522    function invokeStorageMethod(method, params, callback) {
1523      if (!versionAtLeast('6.9')) {
1524        console.error('[Telegram.WebApp] CloudStorage is not supported in version ' + webAppVersion);
1525        throw Error('WebAppMethodUnsupported');
1526      }
1527      invokeCustomMethod(method, params, callback);
1528      return cloudStorage;
1529    }
1530
1531    cloudStorage.setItem = function(key, value, callback) {
1532      return invokeStorageMethod('saveStorageValue', {key: key, value: value}, callback);
1533    };
1534    cloudStorage.getItem = function(key, callback) {
1535      return cloudStorage.getItems([key], callback ? function(err, res) {
1536        if (err) callback(err);
1537        else callback(null, res[key]);
1538      } : null);
1539    };
1540    cloudStorage.getItems = function(keys, callback) {
1541      return invokeStorageMethod('getStorageValues', {keys: keys}, callback);
1542    };
1543    cloudStorage.removeItem = function(key, callback) {
1544      return cloudStorage.removeItems([key], callback);
1545    };
1546    cloudStorage.removeItems = function(keys, callback) {
1547      return invokeStorageMethod('deleteStorageValues', {keys: keys}, callback);
1548    };
1549    cloudStorage.getKeys = function(callback) {
1550      return invokeStorageMethod('getStorageKeys', {}, callback);
1551    };
1552    return cloudStorage;
1553  })();
1554
1555  var DeviceStorage = (function() {
1556    var deviceStorage = {};
1557
1558    WebView.onEvent('device_storage_key_saved',  onDeviceStorageEvent);
1559    WebView.onEvent('device_storage_key_received', onDeviceStorageEvent);
1560    WebView.onEvent('device_storage_cleared',  onDeviceStorageEvent);
1561    WebView.onEvent('device_storage_failed',  onDeviceStorageEvent);
1562
1563    function onDeviceStorageEvent(eventType, eventData) {
1564      if (eventData.req_id && webAppCallbacks[eventData.req_id]) {
1565        var requestData = webAppCallbacks[eventData.req_id];
1566        delete webAppCallbacks[eventData.req_id];
1567        var res = null, err = null;
1568        if (eventType == 'device_storage_failed') {
1569          err = eventData.error || 'UNKNOWN_ERROR';
1570        } else if (eventType == 'device_storage_key_received') {
1571          res = eventData.value;
1572        } else {
1573          res = true;
1574        }
1575        if (requestData.callback) {
1576          requestData.callback(err, res);
1577        }
1578      }
1579    }
1580
1581    function invokeStorageMethod(method, params, callback) {
1582      if (!versionAtLeast('9.0')) {
1583        console.error('[Telegram.WebApp] DeviceStorage is not supported in version ' + webAppVersion);
1584        throw Error('WebAppMethodUnsupported');
1585      }
1586      var req_id = generateCallbackId(16);
1587      var req_params = {req_id: req_id};
1588      for (var k in params) {
1589        req_params[k] = params[k];
1590      }
1591      webAppCallbacks[req_id] = {
1592        callback: callback
1593      };
1594      WebView.postEvent(method, false, req_params);
1595      return deviceStorage;
1596    }
1597
1598    deviceStorage.setItem = function(key, value, callback) {
1599      return invokeStorageMethod('web_app_device_storage_save_key', {key: key, value: value}, callback);
1600    };
1601    deviceStorage.getItem = function(key, callback) {
1602      return invokeStorageMethod('web_app_device_storage_get_key', {key: key}, callback);
1603    };
1604    deviceStorage.removeItem = function(key, callback) {
1605      return invokeStorageMethod('web_app_device_storage_save_key', {key: key, value: null}, callback);
1606    };
1607    deviceStorage.clear = function(callback) {
1608      return invokeStorageMethod('web_app_device_storage_clear', {}, callback);
1609    };
1610    return deviceStorage;
1611  })();
1612
1613  var SecureStorage = (function() {
1614    var secureStorage = {};
1615
1616    WebView.onEvent('secure_storage_key_saved',  onSecureStorageEvent);
1617    WebView.onEvent('secure_storage_key_received', onSecureStorageEvent);
1618    WebView.onEvent('secure_storage_key_restored', onSecureStorageEvent);
1619    WebView.onEvent('secure_storage_cleared',  onSecureStorageEvent);
1620    WebView.onEvent('secure_storage_failed',  onSecureStorageEvent);
1621
1622    function onSecureStorageEvent(eventType, eventData) {
1623      if (eventData.req_id && webAppCallbacks[eventData.req_id]) {
1624        var requestData = webAppCallbacks[eventData.req_id];
1625        delete webAppCallbacks[eventData.req_id];
1626        var res = null, err = null, can_restore = null;
1627        if (eventType == 'secure_storage_failed') {
1628          err = eventData.error || 'UNKNOWN_ERROR';
1629        } else if (eventType == 'secure_storage_key_received') {
1630          res = eventData.value;
1631          if (eventData.can_restore) {
1632            can_restore = true;
1633          }
1634        } else if (eventType == 'secure_storage_key_restored') {
1635          res = eventData.value;
1636        } else {
1637          res = true;
1638        }
1639        if (requestData.callback) {
1640          requestData.callback(err, res, can_restore);
1641        }
1642      }
1643    }
1644
1645    function invokeStorageMethod(method, params, callback) {
1646      if (!versionAtLeast('9.0')) {
1647        console.error('[Telegram.WebApp] SecureStorage is not supported in version ' + webAppVersion);
1648        throw Error('WebAppMethodUnsupported');
1649      }
1650      var req_id = generateCallbackId(16);
1651      var req_params = {req_id: req_id};
1652      for (var k in params) {
1653        req_params[k] = params[k];
1654      }
1655      webAppCallbacks[req_id] = {
1656        callback: callback
1657      };
1658      WebView.postEvent(method, false, req_params);
1659      return secureStorage;
1660    }
1661
1662    secureStorage.setItem = function(key, value, callback) {
1663      return invokeStorageMethod('web_app_secure_storage_save_key', {key: key, value: value}, callback);
1664    };
1665    secureStorage.getItem = function(key, callback) {
1666      return invokeStorageMethod('web_app_secure_storage_get_key', {key: key}, callback);
1667    };
1668    secureStorage.restoreItem = function(key, callback) {
1669      return invokeStorageMethod('web_app_secure_storage_restore_key', {key: key}, callback);
1670    };
1671    secureStorage.removeItem = function(key, callback) {
1672      return invokeStorageMethod('web_app_secure_storage_save_key', {key: key, value: null}, callback);
1673    };
1674    secureStorage.clear = function(callback) {
1675      return invokeStorageMethod('web_app_secure_storage_clear', {}, callback);
1676    };
1677    return secureStorage;
1678  })();
1679
1680  var BiometricManager = (function() {
1681    var isInited = false;
1682    var isBiometricAvailable = false;
1683    var biometricType = 'unknown';
1684    var isAccessRequested = false;
1685    var isAccessGranted = false;
1686    var isBiometricTokenSaved = false;
1687    var deviceId = '';
1688
1689    var biometricManager = {};
1690    Object.defineProperty(biometricManager, 'isInited', {
1691      get: function(){ return isInited; },
1692      enumerable: true
1693    });
1694    Object.defineProperty(biometricManager, 'isBiometricAvailable', {
1695      get: function(){ return isInited && isBiometricAvailable; },
1696      enumerable: true
1697    });
1698    Object.defineProperty(biometricManager, 'biometricType', {
1699      get: function(){ return biometricType || 'unknown'; },
1700      enumerable: true
1701    });
1702    Object.defineProperty(biometricManager, 'isAccessRequested', {
1703      get: function(){ return isAccessRequested; },
1704      enumerable: true
1705    });
1706    Object.defineProperty(biometricManager, 'isAccessGranted', {
1707      get: function(){ return isAccessRequested && isAccessGranted; },
1708      enumerable: true
1709    });
1710    Object.defineProperty(biometricManager, 'isBiometricTokenSaved', {
1711      get: function(){ return isBiometricTokenSaved; },
1712      enumerable: true
1713    });
1714    Object.defineProperty(biometricManager, 'deviceId', {
1715      get: function(){ return deviceId || ''; },
1716      enumerable: true
1717    });
1718
1719    var initRequestState = {callbacks: []};
1720    var accessRequestState = false;
1721    var authRequestState = false;
1722    var tokenRequestState = false;
1723
1724    WebView.onEvent('biometry_info_received',  onBiometryInfoReceived);
1725    WebView.onEvent('biometry_auth_requested', onBiometryAuthRequested);
1726    WebView.onEvent('biometry_token_updated',  onBiometryTokenUpdated);
1727
1728    function onBiometryInfoReceived(eventType, eventData) {
1729      isInited = true;
1730      if (eventData.available) {
1731        isBiometricAvailable = true;
1732        biometricType = eventData.type || 'unknown';
1733        if (eventData.access_requested) {
1734          isAccessRequested = true;
1735          isAccessGranted = !!eventData.access_granted;
1736          isBiometricTokenSaved = !!eventData.token_saved;
1737        } else {
1738          isAccessRequested = false;
1739          isAccessGranted = false;
1740          isBiometricTokenSaved = false;
1741        }
1742      } else {
1743        isBiometricAvailable = false;
1744        biometricType = 'unknown';
1745        isAccessRequested = false;
1746        isAccessGranted = false;
1747        isBiometricTokenSaved = false;
1748      }
1749      deviceId = eventData.device_id || '';
1750
1751      if (initRequestState.callbacks.length > 0) {
1752        for (var i = 0; i < initRequestState.callbacks.length; i++) {
1753          var callback = initRequestState.callbacks[i];
1754          callback();
1755        }
1756        initRequestState.callbacks = [];
1757      }
1758      if (accessRequestState) {
1759        var state = accessRequestState;
1760        accessRequestState = false;
1761        if (state.callback) {
1762          state.callback(isAccessGranted);
1763        }
1764      }
1765      receiveWebViewEvent('biometricManagerUpdated');
1766    }
1767    function onBiometryAuthRequested(eventType, eventData) {
1768      var isAuthenticated = (eventData.status == 'authorized'),
1769          biometricToken = eventData.token || '';
1770      if (authRequestState) {
1771        var state = authRequestState;
1772        authRequestState = false;
1773        if (state.callback) {
1774          state.callback(isAuthenticated, isAuthenticated ? biometricToken : null);
1775        }
1776      }
1777      receiveWebViewEvent('biometricAuthRequested', isAuthenticated ? {
1778        isAuthenticated: true,
1779        biometricToken: biometricToken
1780      } : {
1781        isAuthenticated: false
1782      });
1783    }
1784    function onBiometryTokenUpdated(eventType, eventData) {
1785      var applied = false;
1786      if (isBiometricAvailable &&
1787          isAccessRequested) {
1788        if (eventData.status == 'updated') {
1789          isBiometricTokenSaved = true;
1790          applied = true;
1791        }
1792        else if (eventData.status == 'removed') {
1793          isBiometricTokenSaved = false;
1794          applied = true;
1795        }
1796      }
1797      if (tokenRequestState) {
1798        var state = tokenRequestState;
1799        tokenRequestState = false;
1800        if (state.callback) {
1801          state.callback(applied);
1802        }
1803      }
1804      receiveWebViewEvent('biometricTokenUpdated', {
1805        isUpdated: applied
1806      });
1807    }
1808
1809    function checkVersion() {
1810      if (!versionAtLeast('7.2')) {
1811        console.warn('[Telegram.WebApp] BiometricManager is not supported in version ' + webAppVersion);
1812        return false;
1813      }
1814      return true;
1815    }
1816
1817    function checkInit() {
1818      if (!isInited) {
1819        console.error('[Telegram.WebApp] BiometricManager should be inited before using.');
1820        throw Error('WebAppBiometricManagerNotInited');
1821      }
1822      return true;
1823    }
1824
1825    biometricManager.init = function(callback) {
1826      if (!checkVersion()) {
1827        return biometricManager;
1828      }
1829      if (isInited) {
1830        return biometricManager;
1831      }
1832      if (callback) {
1833        initRequestState.callbacks.push(callback);
1834      }
1835      WebView.postEvent('web_app_biometry_get_info', false);
1836      return biometricManager;
1837    };
1838    biometricManager.requestAccess = function(params, callback) {
1839      if (!checkVersion()) {
1840        return biometricManager;
1841      }
1842      checkInit();
1843      if (!isBiometricAvailable) {
1844        console.error('[Telegram.WebApp] Biometrics is not available on this device.');
1845        throw Error('WebAppBiometricManagerBiometricsNotAvailable');
1846      }
1847      if (accessRequestState) {
1848        console.error('[Telegram.WebApp] Access is already requested');
1849        throw Error('WebAppBiometricManagerAccessRequested');
1850      }
1851      var popup_params = {};
1852      if (typeof params.reason !== 'undefined') {
1853        var reason = strTrim(params.reason);
1854        if (reason.length > 128) {
1855          console.error('[Telegram.WebApp] Biometric reason is too long', reason);
1856          throw Error('WebAppBiometricRequestAccessParamInvalid');
1857        }
1858        if (reason.length > 0) {
1859          popup_params.reason = reason;
1860        }
1861      }
1862
1863      accessRequestState = {
1864        callback: callback
1865      };
1866      WebView.postEvent('web_app_biometry_request_access', false, popup_params);
1867      return biometricManager;
1868    };
1869    biometricManager.authenticate = function(params, callback) {
1870      if (!checkVersion()) {
1871        return biometricManager;
1872      }
1873      checkInit();
1874      if (!isBiometricAvailable) {
1875        console.error('[Telegram.WebApp] Biometrics is not available on this device.');
1876        throw Error('WebAppBiometricManagerBiometricsNotAvailable');
1877      }
1878      if (!isAccessGranted) {
1879        console.error('[Telegram.WebApp] Biometric access was not granted by the user.');
1880        throw Error('WebAppBiometricManagerBiometricAccessNotGranted');
1881      }
1882      if (authRequestState) {
1883        console.error('[Telegram.WebApp] Authentication request is already in progress.');
1884        throw Error('WebAppBiometricManagerAuthenticationRequested');
1885      }
1886      var popup_params = {};
1887      if (typeof params.reason !== 'undefined') {
1888        var reason = strTrim(params.reason);
1889        if (reason.length > 128) {
1890          console.error('[Telegram.WebApp] Biometric reason is too long', reason);
1891          throw Error('WebAppBiometricRequestAccessParamInvalid');
1892        }
1893        if (reason.length > 0) {
1894          popup_params.reason = reason;
1895        }
1896      }
1897
1898      authRequestState = {
1899        callback: callback
1900      };
1901      WebView.postEvent('web_app_biometry_request_auth', false, popup_params);
1902      return biometricManager;
1903    };
1904    biometricManager.updateBiometricToken = function(token, callback) {
1905      if (!checkVersion()) {
1906        return biometricManager;
1907      }
1908      token = token || '';
1909      if (token.length > 1024) {
1910        console.error('[Telegram.WebApp] Token is too long', token);
1911        throw Error('WebAppBiometricManagerTokenInvalid');
1912      }
1913      checkInit();
1914      if (!isBiometricAvailable) {
1915        console.error('[Telegram.WebApp] Biometrics is not available on this device.');
1916        throw Error('WebAppBiometricManagerBiometricsNotAvailable');
1917      }
1918      if (!isAccessGranted) {
1919        console.error('[Telegram.WebApp] Biometric access was not granted by the user.');
1920        throw Error('WebAppBiometricManagerBiometricAccessNotGranted');
1921      }
1922      if (tokenRequestState) {
1923        console.error('[Telegram.WebApp] Token request is already in progress.');
1924        throw Error('WebAppBiometricManagerTokenUpdateRequested');
1925      }
1926      tokenRequestState = {
1927        callback: callback
1928      };
1929      WebView.postEvent('web_app_biometry_update_token', false, {token: token});
1930      return biometricManager;
1931    };
1932    biometricManager.openSettings = function() {
1933      if (!checkVersion()) {
1934        return biometricManager;
1935      }
1936      checkInit();
1937      if (!isBiometricAvailable) {
1938        console.error('[Telegram.WebApp] Biometrics is not available on this device.');
1939        throw Error('WebAppBiometricManagerBiometricsNotAvailable');
1940      }
1941      if (!isAccessRequested) {
1942        console.error('[Telegram.WebApp] Biometric access was not requested yet.');
1943        throw Error('WebAppBiometricManagerBiometricsAccessNotRequested');
1944      }
1945      if (isAccessGranted) {
1946        console.warn('[Telegram.WebApp] Biometric access was granted by the user, no need to go to settings.');
1947        return biometricManager;
1948      }
1949      WebView.postEvent('web_app_biometry_open_settings', false);
1950      return biometricManager;
1951    };
1952    return biometricManager;
1953  })();
1954
1955  var LocationManager = (function() {
1956    var isInited = false;
1957    var isLocationAvailable = false;
1958    var isAccessRequested = false;
1959    var isAccessGranted = false;
1960
1961    var locationManager = {};
1962    Object.defineProperty(locationManager, 'isInited', {
1963      get: function(){ return isInited; },
1964      enumerable: true
1965    });
1966    Object.defineProperty(locationManager, 'isLocationAvailable', {
1967      get: function(){ return isInited && isLocationAvailable; },
1968      enumerable: true
1969    });
1970    Object.defineProperty(locationManager, 'isAccessRequested', {
1971      get: function(){ return isAccessRequested; },
1972      enumerable: true
1973    });
1974    Object.defineProperty(locationManager, 'isAccessGranted', {
1975      get: function(){ return isAccessRequested && isAccessGranted; },
1976      enumerable: true
1977    });
1978
1979    var initRequestState = {callbacks: []};
1980    var getRequestState = {callbacks: []};
1981
1982    WebView.onEvent('location_checked',  onLocationChecked);
1983    WebView.onEvent('location_requested', onLocationRequested);
1984
1985    function onLocationChecked(eventType, eventData) {
1986      isInited = true;
1987      if (eventData.available) {
1988        isLocationAvailable = true;
1989        if (eventData.access_requested) {
1990          isAccessRequested = true;
1991          isAccessGranted = !!eventData.access_granted;
1992        } else {
1993          isAccessRequested = false;
1994          isAccessGranted = false;
1995        }
1996      } else {
1997        isLocationAvailable = false;
1998        isAccessRequested = false;
1999        isAccessGranted = false;
2000      }
2001
2002      if (initRequestState.callbacks.length > 0) {
2003        for (var i = 0; i < initRequestState.callbacks.length; i++) {
2004          var callback = initRequestState.callbacks[i];
2005          callback();
2006        }
2007        initRequestState.callbacks = [];
2008      }
2009      receiveWebViewEvent('locationManagerUpdated');
2010    }
2011    function onLocationRequested(eventType, eventData) {
2012      if (!eventData.available) {
2013        locationData = null;
2014      } else {
2015        var locationData = {
2016          latitude: eventData.latitude,
2017          longitude: eventData.longitude,
2018          altitude: null,
2019          course: null,
2020          speed: null,
2021          horizontal_accuracy: null,
2022          vertical_accuracy: null,
2023          course_accuracy: null,
2024          speed_accuracy: null,
2025        };
2026        if (typeof eventData.altitude !== 'undefined' && eventData.altitude !== null) {
2027          locationData.altitude = eventData.altitude;
2028        }
2029        if (typeof eventData.course !== 'undefined' && eventData.course !== null) {
2030          locationData.course = eventData.course % 360;
2031        }
2032        if (typeof eventData.speed !== 'undefined' && eventData.speed !== null) {
2033          locationData.speed = eventData.speed;
2034        }
2035        if (typeof eventData.horizontal_accuracy !== 'undefined' && eventData.horizontal_accuracy !== null) {
2036          locationData.horizontal_accuracy = eventData.horizontal_accuracy;
2037        }
2038        if (typeof eventData.vertical_accuracy !== 'undefined' && eventData.vertical_accuracy !== null) {
2039          locationData.vertical_accuracy = eventData.vertical_accuracy;
2040        }
2041        if (typeof eventData.course_accuracy !== 'undefined' && eventData.course_accuracy !== null) {
2042          locationData.course_accuracy = eventData.course_accuracy;
2043        }
2044        if (typeof eventData.speed_accuracy !== 'undefined' && eventData.speed_accuracy !== null) {
2045          locationData.speed_accuracy = eventData.speed_accuracy;
2046        }
2047      }
2048      if (!eventData.available ||
2049          !isLocationAvailable ||
2050          !isAccessRequested ||
2051          !isAccessGranted) {
2052        initRequestState.callbacks.push(function() {
2053          locationResponse(locationData);
2054        });
2055        WebView.postEvent('web_app_check_location', false);
2056      } else {
2057        locationResponse(locationData);
2058      }
2059    }
2060    function locationResponse(response) {
2061      if (getRequestState.callbacks.length > 0) {
2062        for (var i = 0; i < getRequestState.callbacks.length; i++) {
2063          var callback = getRequestState.callbacks[i];
2064          callback(response);
2065        }
2066        getRequestState.callbacks = [];
2067      }
2068      if (response !== null) {
2069        receiveWebViewEvent('locationRequested', {
2070          locationData: response
2071        });
2072      }
2073    }
2074
2075    function checkVersion() {
2076      if (!versionAtLeast('8.0')) {
2077        console.warn('[Telegram.WebApp] LocationManager is not supported in version ' + webAppVersion);
2078        return false;
2079      }
2080      return true;
2081    }
2082
2083    function checkInit() {
2084      if (!isInited) {
2085        console.error('[Telegram.WebApp] LocationManager should be inited before using.');
2086        throw Error('WebAppLocationManagerNotInited');
2087      }
2088      return true;
2089    }
2090
2091    locationManager.init = function(callback) {
2092      if (!checkVersion()) {
2093        return locationManager;
2094      }
2095      if (isInited) {
2096        return locationManager;
2097      }
2098      if (callback) {
2099        initRequestState.callbacks.push(callback);
2100      }
2101      WebView.postEvent('web_app_check_location', false);
2102      return locationManager;
2103    };
2104    locationManager.getLocation = function(callback) {
2105      if (!checkVersion()) {
2106        return locationManager;
2107      }
2108      checkInit();
2109      if (!isLocationAvailable) {
2110        console.error('[Telegram.WebApp] Location is not available on this device.');
2111        throw Error('WebAppLocationManagerLocationNotAvailable');
2112      }
2113
2114      getRequestState.callbacks.push(callback);
2115      WebView.postEvent('web_app_request_location');
2116      return locationManager;
2117    };
2118    locationManager.openSettings = function() {
2119      if (!checkVersion()) {
2120        return locationManager;
2121      }
2122      checkInit();
2123      if (!isLocationAvailable) {
2124        console.error('[Telegram.WebApp] Location is not available on this device.');
2125        throw Error('WebAppLocationManagerLocationNotAvailable');
2126      }
2127      if (!isAccessRequested) {
2128        console.error('[Telegram.WebApp] Location access was not requested yet.');
2129        throw Error('WebAppLocationManagerLocationAccessNotRequested');
2130      }
2131      if (isAccessGranted) {
2132        console.warn('[Telegram.WebApp] Location access was granted by the user, no need to go to settings.');
2133        return locationManager;
2134      }
2135      WebView.postEvent('web_app_open_location_settings', false);
2136      return locationManager;
2137    };
2138    return locationManager;
2139  })();
2140
2141  var Accelerometer = (function() {
2142    var isStarted = false;
2143    var valueX = null, valueY = null, valueZ = null;
2144    var startCallbacks = [], stopCallbacks = [];
2145
2146    var accelerometer = {};
2147    Object.defineProperty(accelerometer, 'isStarted', {
2148      get: function(){ return isStarted; },
2149      enumerable: true
2150    });
2151    Object.defineProperty(accelerometer, 'x', {
2152      get: function(){ return valueX; },
2153      enumerable: true
2154    });
2155    Object.defineProperty(accelerometer, 'y', {
2156      get: function(){ return valueY; },
2157      enumerable: true
2158    });
2159    Object.defineProperty(accelerometer, 'z', {
2160      get: function(){ return valueZ; },
2161      enumerable: true
2162    });
2163
2164    WebView.onEvent('accelerometer_started', onAccelerometerStarted);
2165    WebView.onEvent('accelerometer_stopped', onAccelerometerStopped);
2166    WebView.onEvent('accelerometer_changed', onAccelerometerChanged);
2167    WebView.onEvent('accelerometer_failed',  onAccelerometerFailed);
2168
2169    function onAccelerometerStarted(eventType, eventData) {
2170      isStarted = true;
2171      if (startCallbacks.length > 0) {
2172        for (var i = 0; i < startCallbacks.length; i++) {
2173          var callback = startCallbacks[i];
2174          callback(true);
2175        }
2176        startCallbacks = [];
2177      }
2178      receiveWebViewEvent('accelerometerStarted');
2179    }
2180    function onAccelerometerStopped(eventType, eventData) {
2181      isStarted = false;
2182      if (stopCallbacks.length > 0) {
2183        for (var i = 0; i < stopCallbacks.length; i++) {
2184          var callback = stopCallbacks[i];
2185          callback(true);
2186        }
2187        stopCallbacks = [];
2188      }
2189      receiveWebViewEvent('accelerometerStopped');
2190    }
2191    function onAccelerometerChanged(eventType, eventData) {
2192      valueX = eventData.x;
2193      valueY = eventData.y;
2194      valueZ = eventData.z;
2195      receiveWebViewEvent('accelerometerChanged');
2196    }
2197    function onAccelerometerFailed(eventType, eventData) {
2198      if (startCallbacks.length > 0) {
2199        for (var i = 0; i < startCallbacks.length; i++) {
2200          var callback = startCallbacks[i];
2201          callback(false);
2202        }
2203        startCallbacks = [];
2204      }
2205      receiveWebViewEvent('accelerometerFailed', {
2206        error: eventData.error
2207      });
2208    }
2209
2210    function checkVersion() {
2211      if (!versionAtLeast('8.0')) {
2212        console.warn('[Telegram.WebApp] Accelerometer is not supported in version ' + webAppVersion);
2213        return false;
2214      }
2215      return true;
2216    }
2217
2218    accelerometer.start = function(params, callback) {
2219      params = params || {};
2220      if (!checkVersion()) {
2221        return accelerometer;
2222      }
2223      var req_params = {};
2224      var refresh_rate = parseInt(params.refresh_rate || 1000);
2225      if (isNaN(refresh_rate) || refresh_rate < 20 || refresh_rate > 1000) {
2226        console.warn('[Telegram.WebApp] Accelerometer refresh_rate is invalid', refresh_rate);
2227      } else {
2228        req_params.refresh_rate = refresh_rate;
2229      }
2230
2231      if (callback) {
2232        startCallbacks.push(callback);
2233      }
2234      WebView.postEvent('web_app_start_accelerometer', false, req_params);
2235      return accelerometer;
2236    };
2237    accelerometer.stop = function(callback) {
2238      if (!checkVersion()) {
2239        return accelerometer;
2240      }
2241      if (callback) {
2242        stopCallbacks.push(callback);
2243      }
2244      WebView.postEvent('web_app_stop_accelerometer');
2245      return accelerometer;
2246    };
2247    return accelerometer;
2248  })();
2249
2250  var DeviceOrientation = (function() {
2251    var isStarted = false;
2252    var valueAlpha = null, valueBeta = null, valueGamma = null, valueAbsolute = false;
2253    var startCallbacks = [], stopCallbacks = [];
2254
2255    var deviceOrientation = {};
2256    Object.defineProperty(deviceOrientation, 'isStarted', {
2257      get: function(){ return isStarted; },
2258      enumerable: true
2259    });
2260    Object.defineProperty(deviceOrientation, 'absolute', {
2261      get: function(){ return valueAbsolute; },
2262      enumerable: true
2263    });
2264    Object.defineProperty(deviceOrientation, 'alpha', {
2265      get: function(){ return valueAlpha; },
2266      enumerable: true
2267    });
2268    Object.defineProperty(deviceOrientation, 'beta', {
2269      get: function(){ return valueBeta; },
2270      enumerable: true
2271    });
2272    Object.defineProperty(deviceOrientation, 'gamma', {
2273      get: function(){ return valueGamma; },
2274      enumerable: true
2275    });
2276
2277    WebView.onEvent('device_orientation_started',  onDeviceOrientationStarted);
2278    WebView.onEvent('device_orientation_stopped',  onDeviceOrientationStopped);
2279    WebView.onEvent('device_orientation_changed', onDeviceOrientationChanged);
2280    WebView.onEvent('device_orientation_failed',  onDeviceOrientationFailed);
2281
2282    function onDeviceOrientationStarted(eventType, eventData) {
2283      isStarted = true;
2284      if (startCallbacks.length > 0) {
2285        for (var i = 0; i < startCallbacks.length; i++) {
2286          var callback = startCallbacks[i];
2287          callback(true);
2288        }
2289        startCallbacks = [];
2290      }
2291      receiveWebViewEvent('deviceOrientationStarted');
2292    }
2293    function onDeviceOrientationStopped(eventType, eventData) {
2294      isStarted = false;
2295      if (stopCallbacks.length > 0) {
2296        for (var i = 0; i < stopCallbacks.length; i++) {
2297          var callback = stopCallbacks[i];
2298          callback(true);
2299        }
2300        stopCallbacks = [];
2301      }
2302      receiveWebViewEvent('deviceOrientationStopped');
2303    }
2304    function onDeviceOrientationChanged(eventType, eventData) {
2305      valueAbsolute = !!eventData.absolute;
2306      valueAlpha = eventData.alpha;
2307      valueBeta  = eventData.beta;
2308      valueGamma = eventData.gamma;
2309      receiveWebViewEvent('deviceOrientationChanged');
2310    }
2311    function onDeviceOrientationFailed(eventType, eventData) {
2312      if (startCallbacks.length > 0) {
2313        for (var i = 0; i < startCallbacks.length; i++) {
2314          var callback = startCallbacks[i];
2315          callback(false);
2316        }
2317        startCallbacks = [];
2318      }
2319      receiveWebViewEvent('deviceOrientationFailed', {
2320        error: eventData.error
2321      });
2322    }
2323
2324    function checkVersion() {
2325      if (!versionAtLeast('8.0')) {
2326        console.warn('[Telegram.WebApp] DeviceOrientation is not supported in version ' + webAppVersion);
2327        return false;
2328      }
2329      return true;
2330    }
2331
2332    deviceOrientation.start = function(params, callback) {
2333      params = params || {};
2334      if (!checkVersion()) {
2335        return deviceOrientation;
2336      }
2337      var req_params = {};
2338      var refresh_rate = parseInt(params.refresh_rate || 1000);
2339      if (isNaN(refresh_rate) || refresh_rate < 20 || refresh_rate > 1000) {
2340        console.warn('[Telegram.WebApp] DeviceOrientation refresh_rate is invali
2340d', refresh_rate);
2341      } else {
2342        req_params.refresh_rate = refresh_rate;
2343      }
2344      req_params.need_absolute = !!params.need_absolute;
2345
2346      if (callback) {
2347        startCallbacks.push(callback);
2348      }
2349      WebView.postEvent('web_app_start_device_orientation', false, req_params);
2350      return deviceOrientation;
2351    };
2352    deviceOrientation.stop = function(callback) {
2353      if (!checkVersion()) {
2354        return deviceOrientation;
2355      }
2356      if (callback) {
2357        stopCallbacks.push(callback);
2358      }
2359      WebView.postEvent('web_app_stop_device_orientation');
2360      return deviceOrientation;
2361    };
2362    return deviceOrientation;
2363  })();
2364
2365  var Gyroscope = (function() {
2366    var isStarted = false;
2367    var valueX = null, valueY = null, valueZ = null;
2368    var startCallbacks = [], stopCallbacks = [];
2369
2370    var gyroscope = {};
2371    Object.defineProperty(gyroscope, 'isStarted', {
2372      get: function(){ return isStarted; },
2373      enumerable: true
2374    });
2375    Object.defineProperty(gyroscope, 'x', {
2376      get: function(){ return valueX; },
2377      enumerable: true
2378    });
2379    Object.defineProperty(gyroscope, 'y', {
2380      get: function(){ return valueY; },
2381      enumerable: true
2382    });
2383    Object.defineProperty(gyroscope, 'z', {
2384      get: function(){ return valueZ; },
2385      enumerable: true
2386    });
2387
2388    WebView.onEvent('gyroscope_started',  onGyroscopeStarted);
2389    WebView.onEvent('gyroscope_stopped',  onGyroscopeStopped);
2390    WebView.onEvent('gyroscope_changed', onGyroscopeChanged);
2391    WebView.onEvent('gyroscope_failed',  onGyroscopeFailed);
2392
2393    function onGyroscopeStarted(eventType, eventData) {
2394      isStarted = true;
2395      if (startCallbacks.length > 0) {
2396        for (var i = 0; i < startCallbacks.length; i++) {
2397          var callback = startCallbacks[i];
2398          callback(true);
2399        }
2400        startCallbacks = [];
2401      }
2402      receiveWebViewEvent('gyroscopeStarted');
2403    }
2404    function onGyroscopeStopped(eventType, eventData) {
2405      isStarted = false;
2406      if (stopCallbacks.length > 0) {
2407        for (var i = 0; i < stopCallbacks.length; i++) {
2408          var callback = stopCallbacks[i];
2409          callback(true);
2410        }
2411        stopCallbacks = [];
2412      }
2413      receiveWebViewEvent('gyroscopeStopped');
2414    }
2415    function onGyroscopeChanged(eventType, eventData) {
2416      valueX = eventData.x;
2417      valueY = eventData.y;
2418      valueZ = eventData.z;
2419      receiveWebViewEvent('gyroscopeChanged');
2420    }
2421    function onGyroscopeFailed(eventType, eventData) {
2422      if (startCallbacks.length > 0) {
2423        for (var i = 0; i < startCallbacks.length; i++) {
2424          var callback = startCallbacks[i];
2425          callback(false);
2426        }
2427        startCallbacks = [];
2428      }
2429      receiveWebViewEvent('gyroscopeFailed', {
2430        error: eventData.error
2431      });
2432    }
2433
2434    function checkVersion() {
2435      if (!versionAtLeast('8.0')) {
2436        console.warn('[Telegram.WebApp] Gyroscope is not supported in version ' + webAppVersion);
2437        return false;
2438      }
2439      return true;
2440    }
2441
2442    gyroscope.start = function(params, callback) {
2443      params = params || {};
2444      if (!checkVersion()) {
2445        return gyroscope;
2446      }
2447      var req_params = {};
2448      var refresh_rate = parseInt(params.refresh_rate || 1000);
2449      if (isNaN(refresh_rate) || refresh_rate < 20 || refresh_rate > 1000) {
2450        console.warn('[Telegram.WebApp] Gyroscope refresh_rate is invalid', refresh_rate);
2451      } else {
2452        req_params.refresh_rate = refresh_rate;
2453      }
2454
2455      if (callback) {
2456        startCallbacks.push(callback);
2457      }
2458      WebView.postEvent('web_app_start_gyroscope', false, req_params);
2459      return gyroscope;
2460    };
2461    gyroscope.stop = function(callback) {
2462      if (!checkVersion()) {
2463        return gyroscope;
2464      }
2465      if (callback) {
2466        stopCallbacks.push(callback);
2467      }
2468      WebView.postEvent('web_app_stop_gyroscope');
2469      return gyroscope;
2470    };
2471    return gyroscope;
2472  })();
2473
2474  var webAppInvoices = {};
2475  function onInvoiceClosed(eventType, eventData) {
2476    if (eventData.slug && webAppInvoices[eventData.slug]) {
2477      var invoiceData = webAppInvoices[eventData.slug];
2478      delete webAppInvoices[eventData.slug];
2479      if (invoiceData.callback) {
2480        invoiceData.callback(eventData.status);
2481      }
2482      receiveWebViewEvent('invoiceClosed', {
2483        url: invoiceData.url,
2484        status: eventData.status
2485      });
2486    }
2487  }
2488
2489  var webAppPopupOpened = false;
2490  function onPopupClosed(eventType, eventData) {
2491    if (webAppPopupOpened) {
2492      var popupData = webAppPopupOpened;
2493      webAppPopupOpened = false;
2494      var button_id = null;
2495      if (typeof eventData.button_id !== 'undefined') {
2496        button_id = eventData.button_id;
2497      }
2498      if (popupData.callback) {
2499        popupData.callback(button_id);
2500      }
2501      receiveWebViewEvent('popupClosed', {
2502        button_id: button_id
2503      });
2504    }
2505  }
2506
2507  var webAppScanQrPopupOpened = false;
2508  function onQrTextReceived(eventType, eventData) {
2509    if (webAppScanQrPopupOpened) {
2510      var popupData = webAppScanQrPopupOpened;
2511      var data = null;
2512      if (typeof eventData.data !== 'undefined') {
2513        data = eventData.data;
2514      }
2515      if (popupData.callback) {
2516        if (popupData.callback(data)) {
2517          webAppScanQrPopupOpened = false;
2518          WebView.postEvent('web_app_close_scan_qr_popup', false);
2519        }
2520      }
2521      receiveWebViewEvent('qrTextReceived', {
2522        data: data
2523      });
2524    }
2525  }
2526  function onScanQrPopupClosed(eventType, eventData) {
2527    webAppScanQrPopupOpened = false;
2528    receiveWebViewEvent('scanQrPopupClosed');
2529  }
2530
2531  function onClipboardTextReceived(eventType, eventData) {
2532    if (eventData.req_id && webAppCallbacks[eventData.req_id]) {
2533      var requestData = webAppCallbacks[eventData.req_id];
2534      delete webAppCallbacks[eventData.req_id];
2535      var data = null;
2536      if (typeof eventData.data !== 'undefined') {
2537        data = eventData.data;
2538      }
2539      if (requestData.callback) {
2540        requestData.callback(data);
2541      }
2542      receiveWebViewEvent('clipboardTextReceived', {
2543        data: data
2544      });
2545    }
2546  }
2547
2548  var WebAppWriteAccessRequested = false;
2549  function onWriteAccessRequested(eventType, eventData) {
2550    if (WebAppWriteAccessRequested) {
2551      var requestData = WebAppWriteAccessRequested;
2552      WebAppWriteAccessRequested = false;
2553      if (requestData.callback) {
2554        requestData.callback(eventData.status == 'allowed');
2555      }
2556      receiveWebViewEvent('writeAccessRequested', {
2557        status: eventData.status
2558      });
2559    }
2560  }
2561
2562  function getRequestedContact(callback, timeout) {
2563    var reqTo, fallbackTo, reqDelay = 0;
2564    var reqInvoke = function() {
2565      invokeCustomMethod('getRequestedContact', {}, function(err, res) {
2566        if (res.substr(0, 1) == '"' && res.substr(-1) == '"') { // macos fix
2567          res = JSON.parse(res);
2568        }
2569        if (res && res.length) {
2570          clearTimeout(fallbackTo);
2571          callback(res);
2572        } else {
2573          reqDelay += 50;
2574          reqTo = setTimeout(reqInvoke, reqDelay);
2575        }
2576      });
2577    };
2578    var fallbackInvoke = function() {
2579      clearTimeout(reqTo);
2580      callback('');
2581    };
2582    fallbackTo = setTimeout(fallbackInvoke, timeout);
2583    reqInvoke();
2584  }
2585
2586  var WebAppContactRequested = false;
2587  function onPhoneRequested(eventType, eventData) {
2588    if (WebAppContactRequested) {
2589      var requestData = WebAppContactRequested;
2590      WebAppContactRequested = false;
2591      var requestSent = eventData.status == 'sent';
2592      var webViewEvent = {
2593        status: eventData.status
2594      };
2595      if (requestSent) {
2596        getRequestedContact(function(res) {
2597          if (res && res.length) {
2598            webViewEvent.response = res;
2599            webViewEvent.responseUnsafe = Utils.urlParseQueryString(res);
2600            for (var key in webViewEvent.responseUnsafe) {
2601              var val = webViewEvent.responseUnsafe[key];
2602              try {
2603                if (val.substr(0, 1) == '{' && val.substr(-1) == '}' ||
2604                    val.substr(0, 1) == '[' && val.substr(-1) == ']') {
2605                  webViewEvent.responseUnsafe[key] = JSON.parse(val);
2606                }
2607              } catch (e) {}
2608            }
2609          }
2610          if (requestData.callback) {
2611            requestData.callback(requestSent, webViewEvent);
2612          }
2613          receiveWebViewEvent('contactRequested', webViewEvent);
2614        }, 3000);
2615      } else {
2616        if (requestData.callback) {
2617          requestData.callback(requestSent, webViewEvent);
2618        }
2619        receiveWebViewEvent('contactRequested', webViewEvent);
2620      }
2621    }
2622  }
2623
2624  var webAppDownloadFileRequested = false;
2625  function onFileDownloadRequested(eventType, eventData) {
2626    if (webAppDownloadFileRequested) {
2627      var requestData = webAppDownloadFileRequested;
2628      webAppDownloadFileRequested = false;
2629      var isDownloading = eventData.status == 'downloading';
2630      if (requestData.callback) {
2631        requestData.callback(isDownloading);
2632      }
2633      receiveWebViewEvent('fileDownloadRequested', {
2634        status: isDownloading ? 'downloading' : 'cancelled'
2635      });
2636    }
2637  }
2638
2639  function onCustomMethodInvoked(eventType, eventData) {
2640    if (eventData.req_id && webAppCallbacks[eventData.req_id]) {
2641      var requestData = webAppCallbacks[eventData.req_id];
2642      delete webAppCallbacks[eventData.req_id];
2643      var res = null, err = null;
2644      if (typeof eventData.result !== 'undefined') {
2645        res = eventData.result;
2646      }
2647      if (typeof eventData.error !== 'undefined') {
2648        err = eventData.error;
2649      }
2650      if (requestData.callback) {
2651        requestData.callback(err, res);
2652      }
2653    }
2654  }
2655
2656  function invokeCustomMethod(method, params, callback) {
2657    if (!versionAtLeast('6.9')) {
2658      console.error('[Telegram.WebApp] Method invokeCustomMethod is not supported in version ' + webAppVersion);
2659      throw Error('WebAppMethodUnsupported');
2660    }
2661    var req_id = generateCallbackId(16);
2662    var req_params = {req_id: req_id, method: method, params: params || {}};
2663    webAppCallbacks[req_id] = {
2664      callback: callback
2665    };
2666    WebView.postEvent('web_app_invoke_custom_method', false, req_params);
2667  };
2668
2669  if (!window.Telegram) {
2670    window.Telegram = {};
2671  }
2672
2673  Object.defineProperty(WebApp, 'initData', {
2674    get: function(){ return webAppInitData; },
2675    enumerable: true
2676  });
2677  Object.defineProperty(WebApp, 'initDataUnsafe', {
2678    get: function(){ return webAppInitDataUnsafe; },
2679    enumerable: true
2680  });
2681  Object.defineProperty(WebApp, 'version', {
2682    get: function(){ return webAppVersion; },
2683    enumerable: true
2684  });
2685  Object.defineProperty(WebApp, 'platform', {
2686    get: function(){ return webAppPlatform; },
2687    enumerable: true
2688  });
2689  Object.defineProperty(WebApp, 'colorScheme', {
2690    get: function(){ return colorScheme; },
2691    enumerable: true
2692  });
2693  Object.defineProperty(WebApp, 'themeParams', {
2694    get: function(){ return themeParams; },
2695    enumerable: true
2696  });
2697  Object.defineProperty(WebApp, 'isExpanded', {
2698    get: function(){ return isExpanded; },
2699    enumerable: true
2700  });
2701  Object.defineProperty(WebApp, 'viewportHeight', {
2702    get: function(){ return (viewportHeight === false ? window.innerHeight : viewportHeight) - bottomBarHeight; },
2703    enumerable: true
2704  });
2705  Object.defineProperty(WebApp, 'viewportStableHeight', {
2706    get: function(){ return (viewportStableHeight === false ? window.innerHeight : viewportStableHeight) - bottomBarHeight; },
2707    enumerable: true
2708  });
2709  Object.defineProperty(WebApp, 'safeAreaInset', {
2710    get: function(){ return safeAreaInset; },
2711    enumerable: true
2712  });
2713  Object.defineProperty(WebApp, 'contentSafeAreaInset', {
2714    get: function(){ return contentSafeAreaInset; },
2715    enumerable: true
2716  });
2717  Object.defineProperty(WebApp, 'isClosingConfirmationEnabled', {
2718    set: function(val){ setClosingConfirmation(val); },
2719    get: function(){ return isClosingConfirmationEnabled; },
2720    enumerable: true
2721  });
2722  Object.defineProperty(WebApp, 'isVerticalSwipesEnabled', {
2723    set: function(val){ toggleVerticalSwipes(val); },
2724    get: function(){ return isVerticalSwipesEnabled; },
2725    enumerable: true
2726  });
2727  Object.defineProperty(WebApp, 'isFullscreen', {
2728    get: function(){ return webAppIsFullscreen; },
2729    enumerable: true
2730  });
2731  Object.defineProperty(WebApp, 'isOrientationLocked', {
2732    set: function(val){ toggleOrientationLock(val); },
2733    get: function(){ return webAppIsOrientationLocked; },
2734    enumerable: true
2735  });
2736  Object.defineProperty(WebApp, 'isActive', {
2737    get: function(){ return webAppIsActive; },
2738    enumerable: true
2739  });
2740  Object.defineProperty(WebApp, 'headerColor', {
2741    set: function(val){ setHeaderColor(val); },
2742    get: function(){ return getHeaderColor(); },
2743    enumerable: true
2744  });
2745  Object.defineProperty(WebApp, 'backgroundColor', {
2746    set: function(val){ setBackgroundColor(val); },
2747    get: function(){ return getBackgroundColor(); },
2748    enumerable: true
2749  });
2750  Object.defineProperty(WebApp, 'bottomBarColor', {
2751    set: function(val){ setBottomBarColor(val); },
2752    get: function(){ return getBottomBarColor(); },
2753    enumerable: true
2754  });
2755  Object.defineProperty(WebApp, 'BackButton', {
2756    value: BackButton,
2757    enumerable: true
2758  });
2759  Object.defineProperty(WebApp, 'MainButton', {
2760    value: MainButton,
2761    enumerable: true
2762  });
2763  Object.defineProperty(WebApp, 'SecondaryButton', {
2764    value: SecondaryButton,
2765    enumerable: true
2766  });
2767  Object.defineProperty(WebApp, 'SettingsButton', {
2768    value: SettingsButton,
2769    enumerable: true
2770  });
2771  Object.defineProperty(WebApp, 'HapticFeedback', {
2772    value: HapticFeedback,
2773    enumerable: true
2774  });
2775  Object.defineProperty(WebApp, 'CloudStorage', {
2776    value: CloudStorage,
2777    enumerable: true
2778  });
2779  Object.defineProperty(WebApp, 'DeviceStorage', {
2780    value: DeviceStorage,
2781    enumerable: true
2782  });
2783  Object.defineProperty(WebApp, 'SecureStorage', {
2784    value: SecureStorage,
2785    enumerable: true
2786  });
2787  Object.defineProperty(WebApp, 'BiometricManager', {
2788    value: BiometricManager,
2789    enumerable: true
2790  });
2791  Object.defineProperty(WebApp, 'Accelerometer', {
2792    value: Accelerometer,
2793    enumerable: true
2794  });
2795  Object.defineProperty(WebApp, 'DeviceOrientation', {
2796    value: DeviceOrientation,
2797    enumerable: true
2798  });
2799  Object.defineProperty(WebApp, 'Gyroscope', {
2800    value: Gyroscope,
2801    enumerable: true
2802  });
2803  Object.defineProperty(WebApp, 'LocationManager', {
2804    value: LocationManager,
2805    enumerable: true
2806  });
2807  WebApp.isVersionAtLeast = function(ver) {
2808    return versionAtLeast(ver);
2809  };
2810  WebApp.setHeaderColor = function(color_key) {
2811    WebApp.headerColor = color_key;
2812  };
2813  WebApp.setBackgroundColor = function(color) {
2814    WebApp.backgroundColor = color;
2815  };
2816  WebApp.setBottomBarColor = function(color) {
2817    WebApp.bottomBarColor = color;
2818  };
2819  WebApp.enableClosingConfirmation = function() {
2820    WebApp.isClosingConfirmationEnabled = true;
2821  };
2822  WebApp.disableClosingConfirmation = function() {
2823    WebApp.isClosingConfirmationEnabled = false;
2824  };
2825  WebApp.enableVerticalSwipes = function() {
2826    WebApp.isVerticalSwipesEnabled = true;
2827  };
2828  WebApp.disableVerticalSwipes = function() {
2829    WebApp.isVerticalSwipesEnabled = false;
2830  };
2831  WebApp.lockOrientation = function() {
2832    WebApp.isOrientationLocked = true;
2833  };
2834  WebApp.unlockOrientation = function() {
2835    WebApp.isOrientationLocked = false;
2836  };
2837  WebApp.requestFullscreen = function() {
2838    if (!versionAtLeast('8.0')) {
2839      console.error('[Telegram.WebApp] Method requestFullscreen is not supported in version ' + webAppVersion);
2840      throw Error('WebAppMethodUnsupported');
2841    }
2842    WebView.postEvent('web_app_request_fullscreen');
2843  };
2844  WebApp.exitFullscreen = function() {
2845    if (!versionAtLeast('8.0')) {
2846      console.error('[Telegram.WebApp] Method exitFullscreen is not supported in version ' + webAppVersion);
2847      throw Error('WebAppMethodUnsupported');
2848    }
2849    WebView.postEvent('web_app_exit_fullscreen');
2850  };
2851  WebApp.addToHomeScreen = function() {
2852    if (!versionAtLeast('8.0')) {
2853      console.error('[Telegram.WebApp] Method addToHomeScreen is not supported in version ' + webAppVersion);
2854      throw Error('WebAppMethodUnsupported');
2855    }
2856    WebView.postEvent('web_app_add_to_home_screen');
2857  };
2858  WebApp.checkHomeScreenStatus = function(callback) {
2859    if (!versionAtLeast('8.0')) {
2860      console.error('[Telegram.WebApp] Method checkHomeScreenStatus is not supported in version ' + webAppVersion);
2861      throw Error('WebAppMethodUnsupported');
2862    }
2863    if (callback) {
2864      homeScreenCallbacks.push(callback);
2865    }
2866    WebView.postEvent('web_app_check_home_screen');
2867  };
2868  WebApp.onEvent = function(eventType, callback) {
2869    onWebViewEvent(eventType, callback);
2870  };
2871  WebApp.offEvent = function(eventType, callback) {offWebViewEvent(eventType, callback);
2872  };
2873  WebApp.sendData = function (data) {
2874    if (!data || !data.length) {
2875      console.error('[Telegram.WebApp] Data is required', data);
2876      throw Error('WebAppDataInvalid');
2877    }
2878    if (byteLength(data) > 4096) {
2879      console.error('[Telegram.WebApp] Data is too long', data);
2880      throw Error('WebAppDataInvalid');
2881    }
2882    WebView.postEvent('web_app_data_send', false, {data: data});
2883  };
2884  WebApp.switchInlineQuery = function (query, choose_chat_types) {
2885    if (!versionAtLeast('6.6')) {
2886      console.error('[Telegram.WebApp] Method switchInlineQuery is not supported in version ' + webAppVersion);
2887      throw Error('WebAppMethodUnsupported');
2888    }
2889    if (!initParams.tgWebAppBotInline) {
2890      console.error('[Telegram.WebApp] Inline mode is disabled for this bot. Read more about inline mode: https://core.telegram.org/bots/inline');
2891      throw Error('WebAppInlineModeDisabled');
2892    }
2893    query = query || '';
2894    if (query.length > 256) {
2895      console.error('[Telegram.WebApp] Inline query is too long', query);
2896      throw Error('WebAppInlineQueryInvalid');
2897    }
2898    var chat_types = [];
2899    if (choose_chat_types) {
2900      if (!Array.isArray(choose_chat_types)) {
2901        console.error('[Telegram.WebApp] Choose chat types should be an array', choose_chat_types);
2902        throw Error('WebAppInlineChooseChatTypesInvalid');
2903      }
2904      var good_types = {users: 1, bots: 1, groups: 1, channels: 1};
2905      for (var i = 0; i < choose_chat_types.length; i++) {
2906        var chat_type = choose_chat_types[i];
2907        if (!good_types[chat_type]) {
2908          console.error('[Telegram.WebApp] Choose chat type is invali
2908d', chat_type);
2909          throw Error('WebAppInlineChooseChatTypeInvalid');
2910        }
2911        if (good_types[chat_type] != 2) {
2912          good_types[chat_type] = 2;
2913          chat_types.push(chat_type);
2914        }
2915      }
2916    }
2917    WebView.postEvent('web_app_switch_inline_query', false, {query: query, chat_types: chat_types});
2918  };
2919  WebApp.openLink = function (url, options) {
2920    var a = document.createElement('A');
2921    a.href = url;
2922    if (a.protocol != 'http:' &&
2923        a.protocol != 'https:') {
2924      console.error('[Telegram.WebApp] Url protocol is not supported', url);
2925      throw Error('WebAppTgUrlInvalid');
2926    }
2927    var url = a.href;
2928    options = options || {};
2929    if (versionAtLeast('6.1')) {
2930      var req_params = {url: url};
2931      if (versionAtLeast('6.4') && options.try_instant_view) {
2932        req_params.try_instant_view = true;
2933      }
2934      if (versionAtLeast('7.6') && options.try_browser) {
2935        req_params.try_browser = options.try_browser;
2936      }
2937      WebView.postEvent('web_app_open_link', false, req_params);
2938    } else {
2939      window.open(url, '_blank');
2940    }
2941  };
2942  WebApp.openTelegramLink = function (url, options) {
2943    var a = document.createElement('A');
2944    a.href = url;
2945    if (a.protocol != 'http:' &&
2946        a.protocol != 'https:') {
2947      console.error('[Telegram.WebApp] Url protocol is not supported', url);
2948      throw Error('WebAppTgUrlInvalid');
2949    }
2950    if (!isTmeHostname(a.hostname)) {
2951      console.error('[Telegram.WebApp] Url host is not supported', url);
2952      throw Error('WebAppTgUrlInvalid');
2953    }
2954    var path_full = a.pathname + a.search;
2955    options = options || {};
2956    if (isIframe || versionAtLeast('6.1')) {
2957      var req_params = {path_full: path_full};
2958      if (options.force_request) {
2959        req_params.force_request = true;
2960      }
2961      WebView.postEvent('web_app_open_tg_link', false, req_params);
2962    } else {
2963      location.href = 'https://t.me' + path_full;
2964    }
2965  };
2966  WebApp.openInvoice = function (url, callback) {
2967    var a = document.createElement('A'), match, slug;
2968    a.href = url;
2969    if (a.protocol != 'http:' &&
2970        a.protocol != 'https:' ||
2971        !isTmeHostname(a.hostname) ||
2972        !(match = a.pathname.match(/^\/(\$|invoice\/)([A-Za-z0-9\-_=]+)$/)) ||
2973        !(slug = match[2])) {
2974      console.error('[Telegram.WebApp] Invoice url is invalid', url);
2975      throw Error('WebAppInvoiceUrlInvalid');
2976    }
2977    if (!versionAtLeast('6.1')) {
2978      console.error('[Telegram.WebApp] Method openInvoice is not supported in version ' + webAppVersion);
2979      throw Error('WebAppMethodUnsupported');
2980    }
2981    if (webAppInvoices[slug]) {
2982      console.error('[Telegram.WebApp] Invoice is already opened');
2983      throw Error('WebAppInvoiceOpened');
2984    }
2985    webAppInvoices[slug] = {
2986      url: url,
2987      callback: callback
2988    };
2989    WebView.postEvent('web_app_open_invoice', false, {slug: slug});
2990  };
2991  WebApp.showPopup = function (params, callback) {
2992    if (!versionAtLeast('6.2')) {
2993      console.error('[Telegram.WebApp] Method showPopup is not supported in version ' + webAppVersion);
2994      throw Error('WebAppMethodUnsupported');
2995    }
2996    if (webAppPopupOpened) {
2997      console.error('[Telegram.WebApp] Popup is already opened');
2998      throw Error('WebAppPopupOpened');
2999    }
3000    var title = '';
3001    var message = '';
3002    var buttons = [];
3003    var popup_buttons = {};
3004    var popup_params = {};
3005    if (typeof params.title !== 'undefined') {
3006      title = strTrim(params.title);
3007      if (title.length > 64) {
3008        console.error('[Telegram.WebApp] Popup title is too long', title);
3009        throw Error('WebAppPopupParamInvalid');
3010      }
3011      if (title.length > 0) {
3012        popup_params.title = title;
3013      }
3014    }
3015    if (typeof params.message !== 'undefined') {
3016      message = strTrim(params.message);
3017    }
3018    if (!message.length) {
3019      console.error('[Telegram.WebApp] Popup message is required', params.message);
3020      throw Error('WebAppPopupParamInvalid');
3021    }
3022    if (message.length > 256) {
3023      console.error('[Telegram.WebApp] Popup message is too long', message);
3024      throw Error('WebAppPopupParamInvalid');
3025    }
3026    popup_params.message = message;
3027    if (typeof params.buttons !== 'undefined') {
3028      if (!Array.isArray(params.buttons)) {
3029        console.error('[Telegram.WebApp] Popup buttons should be an array', params.buttons);
3030        throw Error('WebAppPopupParamInvalid');
3031      }
3032      for (var i = 0; i < params.buttons.length; i++) {
3033        var button = params.buttons[i];
3034        var btn = {};
3035        var id = '';
3036        if (typeof button.id !== 'undefined') {
3037          id = button.id.toString();
3038          if (id.length > 64) {
3039            console.error('[Telegram.WebApp] Popup button id is too long', id);
3040            throw Error('WebAppPopupParamInvalid');
3041          }
3042        }
3043        btn.id = id;
3044        var button_type = button.type;
3045        if (typeof button_type === 'undefined') {
3046          button_type = 'default';
3047        }
3048        btn.type = button_type;
3049        if (button_type == 'ok' ||
3050            button_type == 'close' ||
3051            button_type == 'cancel') {
3052          // no params needed
3053        } else if (button_type == 'default' ||
3054                   button_type == 'destructive') {
3055          var text = '';
3056          if (typeof button.text !== 'undefined') {
3057            text = strTrim(button.text);
3058          }
3059          if (!text.length) {
3060            console.error('[Telegram.WebApp] Popup button text is required for type ' + button_type, button.text);
3061            throw Error('WebAppPopupParamInvalid');
3062          }
3063          if (text.length > 64) {
3064            console.error('[Telegram.WebApp] Popup button text is too long', text);
3065            throw Error('WebAppPopupParamInvalid');
3066          }
3067          btn.text = text;
3068        } else {
3069          console.error('[Telegram.WebApp] Popup button type is invali
3069d', button_type);
3070          throw Error('WebAppPopupParamInvalid');
3071        }
3072        buttons.push(btn);
3073      }
3074    } else {
3075      buttons.push({id: '', type: 'close'});
3076    }
3077    if (buttons.length < 1) {
3078      console.error('[Telegram.WebApp] Popup should have at least one button');
3079      throw Error('WebAppPopupParamInvalid');
3080    }
3081    if (buttons.length > 3) {
3082      console.error('[Telegram.WebApp] Popup should not have more than 3 buttons');
3083      throw Error('WebAppPopupParamInvalid');
3084    }
3085    popup_params.buttons = buttons;
3086
3087    webAppPopupOpened = {
3088      callback: callback
3089    };
3090    WebView.postEvent('web_app_open_popup', false, popup_params);
3091  };
3092  WebApp.showAlert = function (message, callback) {
3093    WebApp.showPopup({
3094      message: message
3095    }, callback ? function(){ callback(); } : null);
3096  };
3097  WebApp.showConfirm = function (message, callback) {
3098    WebApp.showPopup({
3099      message: message,
3100      buttons: [
3101        {type: 'ok', id: 'ok'},
3102        {type: 'cancel'}
3103      ]
3104    }, callback ? function (button_id) {
3105      callback(button_id == 'ok');
3106    } : null);
3107  };
3108  WebApp.showScanQrPopup = function (params, callback) {
3109    if (!versionAtLeast('6.4')) {
3110      console.error('[Telegram.WebApp] Method showScanQrPopup is not supported in version ' + webAppVersion);
3111      throw Error('WebAppMethodUnsupported');
3112    }
3113    if (webAppScanQrPopupOpened) {
3114      console.error('[Telegram.WebApp] Popup is already opened');
3115      throw Error('WebAppScanQrPopupOpened');
3116    }
3117    var text = '';
3118    var popup_params = {};
3119    if (typeof params.text !== 'undefined') {
3120      text = strTrim(params.text);
3121      if (text.length > 64) {
3122        console.error('[Telegram.WebApp] Scan QR popup text is too long', text);
3123        throw Error('WebAppScanQrPopupParamInvalid');
3124      }
3125      if (text.length > 0) {
3126        popup_params.text = text;
3127      }
3128    }
3129
3130    webAppScanQrPopupOpened = {
3131      callback: callback
3132    };
3133    WebView.postEvent('web_app_open_scan_qr_popup', false, popup_params);
3134  };
3135  WebApp.closeScanQrPopup = function () {
3136    if (!versionAtLeast('6.4')) {
3137      console.error('[Telegram.WebApp] Method closeScanQrPopup is not supported in version ' + webAppVersion);
3138      throw Error('WebAppMethodUnsupported');
3139    }
3140
3141    webAppScanQrPopupOpened = false;
3142    WebView.postEvent('web_app_close_scan_qr_popup', false);
3143  };
3144  WebApp.readTextFromClipboard = function (callback) {
3145    if (!versionAtLeast('6.4')) {
3146      console.error('[Telegram.WebApp] Method readTextFromClipboard is not supported in version ' + webAppVersion);
3147      throw Error('WebAppMethodUnsupported');
3148    }
3149    var req_id = generateCallbackId(16);
3150    var req_params = {req_id: req_id};
3151    webAppCallbacks[req_id] = {
3152      callback: callback
3153    };
3154    WebView.postEvent('web_app_read_text_from_clipboard', false, req_params);
3155  };
3156  WebApp.requestWriteAccess = function (callback) {
3157    if (!versionAtLeast('6.9')) {
3158      console.error('[Telegram.WebApp] Method requestWriteAccess is not supported in version ' + webAppVersion);
3159      throw Error('WebAppMethodUnsupported');
3160    }
3161    if (WebAppWriteAccessRequested) {
3162      console.error('[Telegram.WebApp] Write access is already requested');
3163      throw Error('WebAppWriteAccessRequested');
3164    }
3165    WebAppWriteAccessRequested = {
3166      callback: callback
3167    };
3168    WebView.postEvent('web_app_request_write_access');
3169  };
3170  WebApp.requestContact = function (callback) {
3171    if (!versionAtLeast('6.9')) {
3172      console.error('[Telegram.WebApp] Method requestContact is not supported in version ' + webAppVersion);
3173      throw Error('WebAppMethodUnsupported');
3174    }
3175    if (WebAppContactRequested) {
3176      console.error('[Telegram.WebApp] Contact is already requested');
3177      throw Error('WebAppContactRequested');
3178    }
3179    WebAppContactRequested = {
3180      callback: callback
3181    };
3182    WebView.postEvent('web_app_request_phone');
3183  };
3184  WebApp.downloadFile = function (params, callback) {
3185    if (!versionAtLeast('8.0')) {
3186      console.error('[Telegram.WebApp] Method downloadFile is not supported in version ' + webAppVersion);
3187      throw Error('WebAppMethodUnsupported');
3188    }
3189    if (webAppDownloadFileRequested) {
3190      console.error('[Telegram.WebApp] Popup is already opened');
3191      throw Error('WebAppDownloadFilePopupOpened');
3192    }
3193    var a = document.createElement('A');
3194
3195    var dl_params = {};
3196    if (!params || !params.url || !params.url.length) {
3197      console.error('[Telegram.WebApp] Url is required');
3198      throw Error('WebAppDownloadFileParamInvalid');
3199    }
3200    a.href = params.url;
3201    if (a.protocol != 'https:') {
3202      console.error('[Telegram.WebApp] Url protocol is not supported', url);
3203      throw Error('WebAppDownloadFileParamInvalid');
3204    }
3205    dl_params.url = a.href;
3206
3207    if (!params || !params.file_name || !params.file_name.length) {
3208      console.error('[Telegram.WebApp] File name is required');
3209      throw Error('WebAppDownloadFileParamInvalid');
3210    }
3211    dl_params.file_name = params.file_name;
3212
3213    webAppDownloadFileRequested = {
3214      callback: callback
3215    };
3216    WebView.postEvent('web_app_request_file_download', false, dl_params);
3217  };
3218  WebApp.shareToStory = function (media_url, params) {
3219    params = params || {};
3220    if (!versionAtLeast('7.8')) {
3221      console.error('[Telegram.WebApp] Method shareToStory is not supported in version ' + webAppVersion);
3222      throw Error('WebAppMethodUnsupported');
3223    }
3224    var a = document.createElement('A');
3225    a.href = media_url;
3226    if (a.protocol != 'http:' &&
3227        a.protocol != 'https:') {
3228      console.error('[Telegram.WebApp] Media url protocol is not supported', url);
3229      throw Error('WebAppMediaUrlInvalid');
3230    }
3231    var share_params = {};
3232    share_params.media_url = a.href;
3233    if (typeof params.text !== 'undefined') {
3234      var text = strTrim(params.text);
3235      if (text.length > 2048) {
3236        console.error('[Telegram.WebApp] Text is too long', text);
3237        throw Error('WebAppShareToStoryParamInvalid');
3238      }
3239      if (text.length > 0) {
3240        share_params.text = text;
3241      }
3242    }
3243    if (typeof params.widget_link !== 'undefined') {
3244      params.widget_link = params.widget_link || {};
3245      a.href = params.widget_link.url;
3246      if (a.protocol != 'http:' &&
3247          a.protocol != 'https:') {
3248        console.error('[Telegram.WebApp] Link protocol is not supported', url);
3249        throw Error('WebAppShareToStoryParamInvalid');
3250      }
3251      var widget_link = {
3252        url: a.href
3253      };
3254      if (typeof params.widget_link.name !== 'undefined') {
3255        var link_name = strTrim(params.widget_link.name);
3256        if (link_name.length > 48) {
3257          console.error('[Telegram.WebApp] Link name is too long', link_name);
3258          throw Error('WebAppShareToStoryParamInvalid');
3259        }
3260        if (link_name.length > 0) {
3261          widget_link.name = link_name;
3262        }
3263      }
3264      share_params.widget_link = widget_link;
3265    }
3266
3267    WebView.postEvent('web_app_share_to_story', false, share_params);
3268  };
3269  WebApp.shareMessage = function (msg_id, callback) {
3270    if (!versionAtLeast('8.0')) {
3271      console.error('[Telegram.WebApp] Method shareMessage is not supported in version ' + webAppVersion);
3272      throw Error('WebAppMethodUnsupported');
3273    }
3274    if (WebAppShareMessageOpened) {
3275      console.error('[Telegram.WebApp] Share message is already opened');
3276      throw Error('WebAppShareMessageOpened');
3277    }
3278    WebAppShareMessageOpened = {
3279      callback: callback
3280    };
3281    WebView.postEvent('web_app_send_prepared_message', false, {id: msg_id});
3282  };
3283  WebApp.requestChat = function (req_id, callback) {
3284    if (!versionAtLeast('9.6')) {
3285      console.error('[Telegram.WebApp] Method requestChat is not supported in version ' + webAppVersion);
3286      throw Error('WebAppMethodUnsupported');
3287    }
3288    if (WebAppRequestChatOpened) {
3289      console.error('[Telegram.WebApp] Request chat is already opened');
3290      throw Error('WebAppRequestChatOpened');
3291    }
3292    WebAppRequestChatOpened = {
3293      callback: callback
3294    };
3295    WebView.postEvent('web_app_request_chat', false, {req_id: req_id});
3296  };
3297  WebApp.setEmojiStatus = function (custom_emoji_id, params, callback) {
3298    params = params || {};
3299    if (!versionAtLeast('8.0')) {
3300      console.error('[Telegram.WebApp] Method setEmojiStatus is not supported in version ' + webAppVersion);
3301      throw Error('WebAppMethodUnsupported');
3302    }
3303    var status_params = {};
3304    status_params.custom_emoji_id = custom_emoji_id;
3305    if (typeof params.duration !== 'undefined') {
3306      status_params.duration = params.duration;
3307    }
3308    if (WebAppEmojiStatusRequested) {
3309      console.error('[Telegram.WebApp] Emoji status is already requested');
3310      throw Error('WebAppEmojiStatusRequested');
3311    }
3312    WebAppEmojiStatusRequested = {
3313      callback: callback
3314    };
3315    WebView.postEvent('web_app_set_emoji_status', false, status_params);
3316  };
3317  WebApp.requestEmojiStatusAccess = function (callback) {
3318    if (!versionAtLeast('8.0')) {
3319      console.error('[Telegram.WebApp] Method requestEmojiStatusAccess is not supported in version ' + webAppVersion);
3320      throw Error('WebAppMethodUnsupported');
3321    }
3322    if (WebAppEmojiStatusAccessRequested) {
3323      console.error('[Telegram.WebApp] Emoji status permission is already requested');
3324      throw Error('WebAppEmojiStatusAccessRequested');
3325    }
3326    WebAppEmojiStatusAccessRequested = {
3327      callback: callback
3328    };
3329    WebView.postEvent('web_app_request_emoji_status_access');
3330  };
3331  WebApp.invokeCustomMethod = function (method, params, callback) {
3332    invokeCustomMethod(method, params, callback);
3333  };
3334  WebApp.hideKeyboard = function () {
3335    WebView.postEvent('web_app_hide_keyboard');
3336  };
3337  WebApp.ready = function () {
3338    WebView.postEvent('web_app_ready');
3339  };
3340  WebApp.expand = function () {
3341    WebView.postEvent('web_app_expand');
3342  };
3343  WebApp.close = function (options) {
3344    options = options || {};
3345    var req_params = {};
3346    if (versionAtLeast('7.6') && options.return_back) {
3347      req_params.return_back = true;
3348    }
3349    WebView.postEvent('web_app_close', false, req_params);
3350  };
3351
3352  window.Telegram.WebApp = WebApp;
3353
3354  updateHeaderColor();
3355  updateBackgroundColor();
3356  updateBottomBarColor();
3357  setViewportHeight();
3358  if (initParams.tgWebAppShowSettings) {
3359    SettingsButton.show();
3360  }
3361
3362  window.addEventListener('resize', onWindowResize);
3363  if (isIframe) {
3364    document.addEventListener('click', linkHandler);
3365  }
3366
3367  WebView.onEvent('theme_changed', onThemeChanged);
3368  WebView.onEvent('viewport_changed', onViewportChanged);
3369  WebView.onEvent('safe_area_changed', onSafeAreaChanged);
3370  WebView.onEvent('content_safe_area_changed', onContentSafeAreaChanged);
3371  WebView.onEvent('visibility_changed', onVisibilityChanged);
3372  WebView.onEvent('invoice_closed', onInvoiceClosed);
3373  WebView.onEvent('popup_closed', onPopupClosed);
3374  WebView.onEvent('qr_text_received', onQrTextReceived);
3375  WebView.onEvent('scan_qr_popup_closed', onScanQrPopupClosed);
3376  WebView.onEvent('clipboard_text_received', onClipboardTextReceived);
3377  WebView.onEvent('write_access_requested', onWriteAccessRequested);
3378  WebView.onEvent('phone_requested', onPhoneRequested);
3379  WebView.onEvent('file_download_requested', onFileDownloadRequested);
3380  WebView.onEvent('custom_method_invoked', onCustomMethodInvoked);
3381  WebView.onEvent('fullscreen_changed', onFullscreenChanged);
3382  WebView.onEvent('fullscreen_failed', onFullscreenFailed);
3383  WebView.onEvent('home_screen_added', onHomeScreenAdded);
3384  WebView.onEvent('home_screen_checked', onHomeScreenChecked);
3385  WebView.onEvent('prepared_message_sent', onPreparedMessageSent);
3386  WebView.onEvent('prepared_message_failed', onPreparedMessageFailed);
3387  WebView.onEvent('requested_chat_sent', onRequestedChatSent);
3388  WebView.onEvent('requested_chat_failed', onRequestedChatFailed);
3389  WebView.onEvent('emoji_status_set', onEmojiStatusSet);
3390  WebView.onEvent('emoji_status_failed', onEmojiStatusFailed);
3391  WebView.onEvent('emoji_status_access_requested', onEmojiStatusAccessRequested);
3392  WebView.postEvent('web_app_request_theme');
3393  WebView.postEvent('web_app_request_viewport');
3394  WebView.postEvent('web_app_request_safe_area');
3395  WebView.postEvent('web_app_request_content_safe_area');
3396
3397})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.