telegram-web-app.js 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070
  1. // WebView
  2. (function () {
  3. var eventHandlers = {};
  4. var locationHash = '';
  5. try {
  6. locationHash = location.hash.toString();
  7. } catch (e) {}
  8. var initParams = urlParseHashParams(locationHash);
  9. var storedParams = sessionStorageGet('initParams');
  10. if (storedParams) {
  11. for (var key in storedParams) {
  12. if (typeof initParams[key] === 'undefined') {
  13. initParams[key] = storedParams[key];
  14. }
  15. }
  16. }
  17. sessionStorageSet('initParams', initParams);
  18. var isIframe = false, iFrameStyle;
  19. try {
  20. isIframe = (window.parent != null && window != window.parent);
  21. if (isIframe) {
  22. window.addEventListener('message', function (event) {
  23. if (event.source !== window.parent) return;
  24. try {
  25. var dataParsed = JSON.parse(event.data);
  26. } catch (e) {
  27. return;
  28. }
  29. if (!dataParsed || !dataParsed.eventType) {
  30. return;
  31. }
  32. if (dataParsed.eventType == 'set_custom_style') {
  33. if (event.origin === 'https://web.telegram.org') {
  34. iFrameStyle.innerHTML = dataParsed.eventData;
  35. }
  36. } else if (dataParsed.eventType == 'reload_iframe') {
  37. try {
  38. window.parent.postMessage(JSON.stringify({eventType: 'iframe_will_reload'}), '*');
  39. } catch (e) {}
  40. location.reload();
  41. } else {
  42. receiveEvent(dataParsed.eventType, dataParsed.eventData);
  43. }
  44. });
  45. iFrameStyle = document.createElement('style');
  46. document.head.appendChild(iFrameStyle);
  47. try {
  48. window.parent.postMessage(JSON.stringify({eventType: 'iframe_ready', eventData: {reload_supported: true}}), '*');
  49. } catch (e) {}
  50. }
  51. } catch (e) {}
  52. function urlSafeDecode(urlencoded) {
  53. try {
  54. urlencoded = urlencoded.replace(/\+/g, '%20');
  55. return decodeURIComponent(urlencoded);
  56. } catch (e) {
  57. return urlencoded;
  58. }
  59. }
  60. function urlParseHashParams(locationHash) {
  61. locationHash = locationHash.replace(/^#/, '');
  62. var params = {};
  63. if (!locationHash.length) {
  64. return params;
  65. }
  66. if (locationHash.indexOf('=') < 0 && locationHash.indexOf('?') < 0) {
  67. params._path = urlSafeDecode(locationHash);
  68. return params;
  69. }
  70. var qIndex = locationHash.indexOf('?');
  71. if (qIndex >= 0) {
  72. var pathParam = locationHash.substr(0, qIndex);
  73. params._path = urlSafeDecode(pathParam);
  74. locationHash = locationHash.substr(qIndex + 1);
  75. }
  76. var query_params = urlParseQueryString(locationHash);
  77. for (var k in query_params) {
  78. params[k] = query_params[k];
  79. }
  80. return params;
  81. }
  82. function urlParseQueryString(queryString) {
  83. var params = {};
  84. if (!queryString.length) {
  85. return params;
  86. }
  87. var queryStringParams = queryString.split('&');
  88. var i, param, paramName, paramValue;
  89. for (i = 0; i < queryStringParams.length; i++) {
  90. param = queryStringParams[i].split('=');
  91. paramName = urlSafeDecode(param[0]);
  92. paramValue = param[1] == null ? null : urlSafeDecode(param[1]);
  93. params[paramName] = paramValue;
  94. }
  95. return params;
  96. }
  97. // Telegram apps will implement this logic to add service params (e.g. tgShareScoreUrl) to game URL
  98. function urlAppendHashParams(url, addHash) {
  99. // url looks like 'https://game.com/path?query=1#hash'
  100. // addHash looks like 'tgShareScoreUrl=' + encodeURIComponent('tgb://share_game_score?hash=very_long_hash123')
  101. var ind = url.indexOf('#');
  102. if (ind < 0) {
  103. // https://game.com/path -> https://game.com/path#tgShareScoreUrl=etc
  104. return url + '#' + addHash;
  105. }
  106. var curHash = url.substr(ind + 1);
  107. if (curHash.indexOf('=') >= 0 || curHash.indexOf('?') >= 0) {
  108. // https://game.com/#hash=1 -> https://game.com/#hash=1&tgShareScoreUrl=etc
  109. // https://game.com/#path?query -> https://game.com/#path?query&tgShareScoreUrl=etc
  110. return url + '&' + addHash;
  111. }
  112. // https://game.com/#hash -> https://game.com/#hash?tgShareScoreUrl=etc
  113. if (curHash.length > 0) {
  114. return url + '?' + addHash;
  115. }
  116. // https://game.com/# -> https://game.com/#tgShareScoreUrl=etc
  117. return url + addHash;
  118. }
  119. function postEvent(eventType, callback, eventData) {
  120. if (!callback) {
  121. callback = function () {};
  122. }
  123. if (eventData === undefined) {
  124. eventData = '';
  125. }
  126. console.log('[Telegram.WebView] > postEvent', eventType, eventData);
  127. if (window.TelegramWebviewProxy !== undefined) {
  128. TelegramWebviewProxy.postEvent(eventType, JSON.stringify(eventData));
  129. callback();
  130. }
  131. else if (window.external && 'notify' in window.external) {
  132. window.external.notify(JSON.stringify({eventType: eventType, eventData: eventData}));
  133. callback();
  134. }
  135. else if (isIframe) {
  136. try {
  137. var trustedTarget = 'https://web.telegram.org';
  138. // For now we don't restrict target, for testing purposes
  139. trustedTarget = '*';
  140. window.parent.postMessage(JSON.stringify({eventType: eventType, eventData: eventData}), trustedTarget);
  141. callback();
  142. } catch (e) {
  143. callback(e);
  144. }
  145. }
  146. else {
  147. callback({notAvailable: true});
  148. }
  149. };
  150. function receiveEvent(eventType, eventData) {
  151. console.log('[Telegram.WebView] < receiveEvent', eventType, eventData);
  152. callEventCallbacks(eventType, function(callback) {
  153. callback(eventType, eventData);
  154. });
  155. }
  156. function callEventCallbacks(eventType, func) {
  157. var curEventHandlers = eventHandlers[eventType];
  158. if (curEventHandlers === undefined ||
  159. !curEventHandlers.length) {
  160. return;
  161. }
  162. for (var i = 0; i < curEventHandlers.length; i++) {
  163. try {
  164. func(curEventHandlers[i]);
  165. } catch (e) {}
  166. }
  167. }
  168. function onEvent(eventType, callback) {
  169. if (eventHandlers[eventType] === undefined) {
  170. eventHandlers[eventType] = [];
  171. }
  172. var index = eventHandlers[eventType].indexOf(callback);
  173. if (index === -1) {
  174. eventHandlers[eventType].push(callback);
  175. }
  176. };
  177. function offEvent(eventType, callback) {
  178. if (eventHandlers[eventType] === undefined) {
  179. return;
  180. }
  181. var index = eventHandlers[eventType].indexOf(callback);
  182. if (index === -1) {
  183. return;
  184. }
  185. eventHandlers[eventType].splice(index, 1);
  186. };
  187. function openProtoUrl(url) {
  188. if (!url.match(/^(web\+)?tgb?:\/\/./)) {
  189. return false;
  190. }
  191. var useIframe = navigator.userAgent.match(/iOS|iPhone OS|iPhone|iPod|iPad/i) ? true : false;
  192. if (useIframe) {
  193. var iframeContEl = document.getElementById('tgme_frame_cont') || document.body;
  194. var iframeEl = document.createElement('iframe');
  195. iframeContEl.appendChild(iframeEl);
  196. var pageHidden = false;
  197. var enableHidden = function () {
  198. pageHidden = true;
  199. };
  200. window.addEventListener('pagehide', enableHidden, false);
  201. window.addEventListener('blur', enableHidden, false);
  202. if (iframeEl !== null) {
  203. iframeEl.src = url;
  204. }
  205. setTimeout(function() {
  206. if (!pageHidden) {
  207. window.location = url;
  208. }
  209. window.removeEventListener('pagehide', enableHidden, false);
  210. window.removeEventListener('blur', enableHidden, false);
  211. }, 2000);
  212. }
  213. else {
  214. window.location = url;
  215. }
  216. return true;
  217. }
  218. function sessionStorageSet(key, value) {
  219. try {
  220. window.sessionStorage.setItem('__telegram__' + key, JSON.stringify(value));
  221. return true;
  222. } catch(e) {}
  223. return false;
  224. }
  225. function sessionStorageGet(key) {
  226. try {
  227. return JSON.parse(window.sessionStorage.getItem('__telegram__' + key));
  228. } catch(e) {}
  229. return null;
  230. }
  231. if (!window.Telegram) {
  232. window.Telegram = {};
  233. }
  234. window.Telegram.WebView = {
  235. initParams: initParams,
  236. isIframe: isIframe,
  237. onEvent: onEvent,
  238. offEvent: offEvent,
  239. postEvent: postEvent,
  240. receiveEvent: receiveEvent,
  241. callEventCallbacks: callEventCallbacks
  242. };
  243. window.Telegram.Utils = {
  244. urlSafeDecode: urlSafeDecode,
  245. urlParseQueryString: urlParseQueryString,
  246. urlParseHashParams: urlParseHashParams,
  247. urlAppendHashParams: urlAppendHashParams,
  248. sessionStorageSet: sessionStorageSet,
  249. sessionStorageGet: sessionStorageGet
  250. };
  251. // For Windows Phone app
  252. window.TelegramGameProxy_receiveEvent = receiveEvent;
  253. // App backward compatibility
  254. window.TelegramGameProxy = {
  255. receiveEvent: receiveEvent
  256. };
  257. })();
  258. // WebApp
  259. (function () {
  260. var Utils = window.Telegram.Utils;
  261. var WebView = window.Telegram.WebView;
  262. var initParams = WebView.initParams;
  263. var isIframe = WebView.isIframe;
  264. var WebApp = {};
  265. var webAppInitData = '', webAppInitDataUnsafe = {};
  266. var themeParams = {}, colorScheme = 'light';
  267. var webAppVersion = '6.0';
  268. var webAppPlatform = 'unknown';
  269. if (initParams.tgWebAppData && initParams.tgWebAppData.length) {
  270. webAppInitData = initParams.tgWebAppData;
  271. webAppInitDataUnsafe = Utils.urlParseQueryString(webAppInitData);
  272. for (var key in webAppInitDataUnsafe) {
  273. var val = webAppInitDataUnsafe[key];
  274. try {
  275. if (val.substr(0, 1) == '{' && val.substr(-1) == '}' ||
  276. val.substr(0, 1) == '[' && val.substr(-1) == ']') {
  277. webAppInitDataUnsafe[key] = JSON.parse(val);
  278. }
  279. } catch (e) {}
  280. }
  281. }
  282. if (initParams.tgWebAppThemeParams && initParams.tgWebAppThemeParams.length) {
  283. var themeParamsRaw = initParams.tgWebAppThemeParams;
  284. try {
  285. var theme_params = JSON.parse(themeParamsRaw);
  286. if (theme_params) {
  287. setThemeParams(theme_params);
  288. }
  289. } catch (e) {}
  290. }
  291. var theme_params = Utils.sessionStorageGet('themeParams');
  292. if (theme_params) {
  293. setThemeParams(theme_params);
  294. }
  295. if (initParams.tgWebAppVersion) {
  296. webAppVersion = initParams.tgWebAppVersion;
  297. }
  298. if (initParams.tgWebAppPlatform) {
  299. webAppPlatform = initParams.tgWebAppPlatform;
  300. }
  301. function onThemeChanged(eventType, eventData) {
  302. if (eventData.theme_params) {
  303. setThemeParams(eventData.theme_params);
  304. window.Telegram.WebApp.MainButton.setParams({});
  305. updateBackgroundColor();
  306. receiveWebViewEvent('themeChanged');
  307. }
  308. }
  309. var lastWindowHeight = window.innerHeight;
  310. function onViewportChanged(eventType, eventData) {
  311. if (eventData.height) {
  312. window.removeEventListener('resize', onWindowResize);
  313. setViewportHeight(eventData);
  314. }
  315. }
  316. function onWindowResize(e) {
  317. if (lastWindowHeight != window.innerHeight) {
  318. lastWindowHeight = window.innerHeight;
  319. receiveWebViewEvent('viewportChanged', {
  320. isStateStable: true
  321. });
  322. }
  323. }
  324. function linkHandler(e) {
  325. if (e.metaKey || e.ctrlKey) return;
  326. var el = e.target;
  327. while (el.tagName != 'A' && el.parentNode) {
  328. el = el.parentNode;
  329. }
  330. if (el.tagName == 'A' &&
  331. el.target != '_blank' &&
  332. (el.protocol == 'http:' || el.protocol == 'https:') &&
  333. el.hostname == 't.me') {
  334. WebApp.openTgLink(el.href);
  335. e.preventDefault();
  336. }
  337. }
  338. function strTrim(str) {
  339. return str.toString().replace(/^\s+|\s+$/g, '');
  340. }
  341. function receiveWebViewEvent(eventType) {
  342. var args = Array.prototype.slice.call(arguments);
  343. eventType = args.shift();
  344. WebView.callEventCallbacks('webview:' + eventType, function(callback) {
  345. callback.apply(WebApp, args);
  346. });
  347. }
  348. function onWebViewEvent(eventType, callback) {
  349. WebView.onEvent('webview:' + eventType, callback);
  350. };
  351. function offWebViewEvent(eventType, callback) {
  352. WebView.offEvent('webview:' + eventType, callback);
  353. };
  354. function setCssProperty(name, value) {
  355. var root = document.documentElement;
  356. if (root && root.style && root.style.setProperty) {
  357. root.style.setProperty('--tg-' + name, value);
  358. }
  359. }
  360. function setThemeParams(theme_params) {
  361. // temp iOS fix
  362. if (theme_params.bg_color == '#1c1c1d' &&
  363. theme_params.bg_color == theme_params.secondary_bg_color) {
  364. theme_params.secondary_bg_color = '#2c2c2e';
  365. }
  366. var color;
  367. for (var key in theme_params) {
  368. if (color = parseColorToHex(theme_params[key])) {
  369. themeParams[key] = color;
  370. if (key == 'bg_color') {
  371. colorScheme = isColorDark(color) ? 'dark' : 'light'
  372. setCssProperty('color-scheme', colorScheme);
  373. }
  374. key = 'theme-' + key.split('_').join('-');
  375. setCssProperty(key, color);
  376. }
  377. }
  378. Utils.sessionStorageSet('themeParams', themeParams);
  379. }
  380. var webAppCallbacks = {};
  381. function generateCallbackId(len) {
  382. var tries = 100;
  383. while (--tries) {
  384. var id = '', chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', chars_len = chars.length;
  385. for (var i = 0; i < len; i++) {
  386. id += chars[Math.floor(Math.random() * chars_len)];
  387. }
  388. if (!webAppCallbacks[id]) {
  389. webAppCallbacks[id] = {};
  390. return id;
  391. }
  392. }
  393. throw Error('WebAppCallbackIdGenerateFailed');
  394. }
  395. var viewportHeight = false, viewportStableHeight = false, isExpanded = true;
  396. function setViewportHeight(data) {
  397. if (typeof data !== 'undefined') {
  398. isExpanded = !!data.is_expanded;
  399. viewportHeight = data.height;
  400. if (data.is_state_stable) {
  401. viewportStableHeight = data.height;
  402. }
  403. receiveWebViewEvent('viewportChanged', {
  404. isStateStable: !!data.is_state_stable
  405. });
  406. }
  407. var height, stable_height;
  408. if (viewportHeight !== false) {
  409. height = (viewportHeight - mainButtonHeight) + 'px';
  410. } else {
  411. height = mainButtonHeight ? 'calc(100vh - ' + mainButtonHeight + 'px)' : '100vh';
  412. }
  413. if (viewportStableHeight !== false) {
  414. stable_height = (viewportStableHeight - mainButtonHeight) + 'px';
  415. } else {
  416. stable_height = mainButtonHeight ? 'calc(100vh - ' + mainButtonHeight + 'px)' : '100vh';
  417. }
  418. setCssProperty('viewport-height', height);
  419. setCssProperty('viewport-stable-height', stable_height);
  420. }
  421. var isClosingConfirmationEnabled = false;
  422. function setClosingConfirmation(need_confirmation) {
  423. if (!versionAtLeast('6.2')) {
  424. console.warn('[Telegram.WebApp] Closing confirmation is not supported in version ' + webAppVersion);
  425. return;
  426. }
  427. isClosingConfirmationEnabled = !!need_confirmation;
  428. WebView.postEvent('web_app_setup_closing_behavior', false, {need_confirmation: isClosingConfirmationEnabled});
  429. }
  430. var isVerticalSwipesEnabled = true;
  431. function toggleVerticalSwipes(enable_swipes) {
  432. if (!versionAtLeast('7.7')) {
  433. console.warn('[Telegram.WebApp] Changing swipes behavior is not supported in version ' + webAppVersion);
  434. return;
  435. }
  436. isVerticalSwipesEnabled = !!enable_swipes;
  437. WebView.postEvent('web_app_setup_swipe_behavior', false, {allow_vertical_swipe: isVerticalSwipesEnabled});
  438. }
  439. var headerColorKey = 'bg_color', headerColor = null;
  440. function getHeaderColor() {
  441. if (headerColorKey == 'secondary_bg_color') {
  442. return themeParams.secondary_bg_color;
  443. } else if (headerColorKey == 'bg_color') {
  444. return themeParams.bg_color;
  445. }
  446. return headerColor;
  447. }
  448. function setHeaderColor(color) {
  449. if (!versionAtLeast('6.1')) {
  450. console.warn('[Telegram.WebApp] Header color is not supported in version ' + webAppVersion);
  451. return;
  452. }
  453. if (!versionAtLeast('6.9')) {
  454. if (themeParams.bg_color &&
  455. themeParams.bg_color == color) {
  456. color = 'bg_color';
  457. } else if (themeParams.secondary_bg_color &&
  458. themeParams.secondary_bg_color == color) {
  459. color = 'secondary_bg_color';
  460. }
  461. }
  462. var head_color = null, color_key = null;
  463. if (color == 'bg_color' || color == 'secondary_bg_color') {
  464. color_key = color;
  465. } else if (versionAtLeast('6.9')) {
  466. head_color = parseColorToHex(color);
  467. if (!head_color) {
  468. console.error('[Telegram.WebApp] Header color format is invalid', color);
  469. throw Error('WebAppHeaderColorInvalid');
  470. }
  471. }
  472. if (!versionAtLeast('6.9') &&
  473. color_key != 'bg_color' &&
  474. color_key != 'secondary_bg_color') {
  475. 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);
  476. throw Error('WebAppHeaderColorKeyInvalid');
  477. }
  478. headerColorKey = color_key;
  479. headerColor = head_color;
  480. updateHeaderColor();
  481. }
  482. var appHeaderColorKey = null, appHeaderColor = null;
  483. function updateHeaderColor() {
  484. if (appHeaderColorKey != headerColorKey ||
  485. appHeaderColor != headerColor) {
  486. appHeaderColorKey = headerColorKey;
  487. appHeaderColor = headerColor;
  488. if (appHeaderColor) {
  489. WebView.postEvent('web_app_set_header_color', false, {color: headerColor});
  490. } else {
  491. WebView.postEvent('web_app_set_header_color', false, {color_key: headerColorKey});
  492. }
  493. }
  494. }
  495. var backgroundColor = 'bg_color';
  496. function getBackgroundColor() {
  497. if (backgroundColor == 'secondary_bg_color') {
  498. return themeParams.secondary_bg_color;
  499. } else if (backgroundColor == 'bg_color') {
  500. return themeParams.bg_color;
  501. }
  502. return backgroundColor;
  503. }
  504. function setBackgroundColor(color) {
  505. if (!versionAtLeast('6.1')) {
  506. console.warn('[Telegram.WebApp] Background color is not supported in version ' + webAppVersion);
  507. return;
  508. }
  509. var bg_color;
  510. if (color == 'bg_color' || color == 'secondary_bg_color') {
  511. bg_color = color;
  512. } else {
  513. bg_color = parseColorToHex(color);
  514. if (!bg_color) {
  515. console.error('[Telegram.WebApp] Background color format is invalid', color);
  516. throw Error('WebAppBackgroundColorInvalid');
  517. }
  518. }
  519. backgroundColor = bg_color;
  520. updateBackgroundColor();
  521. }
  522. var appBackgroundColor = null;
  523. function updateBackgroundColor() {
  524. var color = getBackgroundColor();
  525. if (appBackgroundColor != color) {
  526. appBackgroundColor = color;
  527. WebView.postEvent('web_app_set_background_color', false, {color: color});
  528. }
  529. }
  530. function parseColorToHex(color) {
  531. color += '';
  532. var match;
  533. if (match = /^\s*#([0-9a-f]{6})\s*$/i.exec(color)) {
  534. return '#' + match[1].toLowerCase();
  535. }
  536. else if (match = /^\s*#([0-9a-f])([0-9a-f])([0-9a-f])\s*$/i.exec(color)) {
  537. return ('#' + match[1] + match[1] + match[2] + match[2] + match[3] + match[3]).toLowerCase();
  538. }
  539. else if (match = /^\s*rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+\.{0,1}\d*))?\)\s*$/.exec(color)) {
  540. var r = parseInt(match[1]), g = parseInt(match[2]), b = parseInt(match[3]);
  541. r = (r < 16 ? '0' : '') + r.toString(16);
  542. g = (g < 16 ? '0' : '') + g.toString(16);
  543. b = (b < 16 ? '0' : '') + b.toString(16);
  544. return '#' + r + g + b;
  545. }
  546. return false;
  547. }
  548. function isColorDark(rgb) {
  549. rgb = rgb.replace(/[\s#]/g, '');
  550. if (rgb.length == 3) {
  551. rgb = rgb[0] + rgb[0] + rgb[1] + rgb[1] + rgb[2] + rgb[2];
  552. }
  553. var r = parseInt(rgb.substr(0, 2), 16);
  554. var g = parseInt(rgb.substr(2, 2), 16);
  555. var b = parseInt(rgb.substr(4, 2), 16);
  556. var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b));
  557. return hsp < 120;
  558. }
  559. function versionCompare(v1, v2) {
  560. if (typeof v1 !== 'string') v1 = '';
  561. if (typeof v2 !== 'string') v2 = '';
  562. v1 = v1.replace(/^\s+|\s+$/g, '').split('.');
  563. v2 = v2.replace(/^\s+|\s+$/g, '').split('.');
  564. var a = Math.max(v1.length, v2.length), i, p1, p2;
  565. for (i = 0; i < a; i++) {
  566. p1 = parseInt(v1[i]) || 0;
  567. p2 = parseInt(v2[i]) || 0;
  568. if (p1 == p2) continue;
  569. if (p1 > p2) return 1;
  570. return -1;
  571. }
  572. return 0;
  573. }
  574. function versionAtLeast(ver) {
  575. return versionCompare(webAppVersion, ver) >= 0;
  576. }
  577. function byteLength(str) {
  578. if (window.Blob) {
  579. try { return new Blob([str]).size; } catch (e) {}
  580. }
  581. var s = str.length;
  582. for (var i=str.length-1; i>=0; i--) {
  583. var code = str.charCodeAt(i);
  584. if (code > 0x7f && code <= 0x7ff) s++;
  585. else if (code > 0x7ff && code <= 0xffff) s+=2;
  586. if (code >= 0xdc00 && code <= 0xdfff) i--;
  587. }
  588. return s;
  589. }
  590. var BackButton = (function() {
  591. var isVisible = false;
  592. var backButton = {};
  593. Object.defineProperty(backButton, 'isVisible', {
  594. set: function(val){ setParams({is_visible: val}); },
  595. get: function(){ return isVisible; },
  596. enumerable: true
  597. });
  598. var curButtonState = null;
  599. WebView.onEvent('back_button_pressed', onBackButtonPressed);
  600. function onBackButtonPressed() {
  601. receiveWebViewEvent('backButtonClicked');
  602. }
  603. function buttonParams() {
  604. return {is_visible: isVisible};
  605. }
  606. function buttonState(btn_params) {
  607. if (typeof btn_params === 'undefined') {
  608. btn_params = buttonParams();
  609. }
  610. return JSON.stringify(btn_params);
  611. }
  612. function buttonCheckVersion() {
  613. if (!versionAtLeast('6.1')) {
  614. console.warn('[Telegram.WebApp] BackButton is not supported in version ' + webAppVersion);
  615. return false;
  616. }
  617. return true;
  618. }
  619. function updateButton() {
  620. var btn_params = buttonParams();
  621. var btn_state = buttonState(btn_params);
  622. if (curButtonState === btn_state) {
  623. return;
  624. }
  625. curButtonState = btn_state;
  626. WebView.postEvent('web_app_setup_back_button', false, btn_params);
  627. }
  628. function setParams(params) {
  629. if (!buttonCheckVersion()) {
  630. return backButton;
  631. }
  632. if (typeof params.is_visible !== 'undefined') {
  633. isVisible = !!params.is_visible;
  634. }
  635. updateButton();
  636. return backButton;
  637. }
  638. backButton.onClick = function(callback) {
  639. if (buttonCheckVersion()) {
  640. onWebViewEvent('backButtonClicked', callback);
  641. }
  642. return backButton;
  643. };
  644. backButton.offClick = function(callback) {
  645. if (buttonCheckVersion()) {
  646. offWebViewEvent('backButtonClicked', callback);
  647. }
  648. return backButton;
  649. };
  650. backButton.show = function() {
  651. return setParams({is_visible: true});
  652. };
  653. backButton.hide = function() {
  654. return setParams({is_visible: false});
  655. };
  656. return backButton;
  657. })();
  658. var mainButtonHeight = 0;
  659. var MainButton = (function() {
  660. var isVisible = false;
  661. var isActive = true;
  662. var isProgressVisible = false;
  663. var buttonText = 'CONTINUE';
  664. var buttonColor = false;
  665. var buttonTextColor = false;
  666. var mainButton = {};
  667. Object.defineProperty(mainButton, 'text', {
  668. set: function(val){ mainButton.setParams({text: val}); },
  669. get: function(){ return buttonText; },
  670. enumerable: true
  671. });
  672. Object.defineProperty(mainButton, 'color', {
  673. set: function(val){ mainButton.setParams({color: val}); },
  674. get: function(){ return buttonColor || themeParams.button_color || '#2481cc'; },
  675. enumerable: true
  676. });
  677. Object.defineProperty(mainButton, 'textColor', {
  678. set: function(val){ mainButton.setParams({text_color: val}); },
  679. get: function(){ return buttonTextColor || themeParams.button_text_color || '#ffffff'; },
  680. enumerable: true
  681. });
  682. Object.defineProperty(mainButton, 'isVisible', {
  683. set: function(val){ mainButton.setParams({is_visible: val}); },
  684. get: function(){ return isVisible; },
  685. enumerable: true
  686. });
  687. Object.defineProperty(mainButton, 'isProgressVisible', {
  688. get: function(){ return isProgressVisible; },
  689. enumerable: true
  690. });
  691. Object.defineProperty(mainButton, 'isActive', {
  692. set: function(val){ mainButton.setParams({is_active: val}); },
  693. get: function(){ return isActive; },
  694. enumerable: true
  695. });
  696. var curButtonState = null;
  697. WebView.onEvent('main_button_pressed', onMainButtonPressed);
  698. var debugBtn = null, debugBtnStyle = {};
  699. if (initParams.tgWebAppDebug) {
  700. debugBtn = document.createElement('tg-main-button');
  701. debugBtnStyle = {
  702. font: '600 14px/18px sans-serif',
  703. display: 'none',
  704. width: '100%',
  705. height: '48px',
  706. borderRadius: '0',
  707. background: 'no-repeat right center',
  708. position: 'fixed',
  709. left: '0',
  710. right: '0',
  711. bottom: '0',
  712. margin: '0',
  713. padding: '15px 20px',
  714. textAlign: 'center',
  715. boxSizing: 'border-box',
  716. zIndex: '10000'
  717. };
  718. for (var k in debugBtnStyle) {
  719. debugBtn.style[k] = debugBtnStyle[k];
  720. }
  721. document.addEventListener('DOMContentLoaded', function onDomLoaded(event) {
  722. document.removeEventListener('DOMContentLoaded', onDomLoaded);
  723. document.body.appendChild(debugBtn);
  724. debugBtn.addEventListener('click', onMainButtonPressed, false);
  725. });
  726. }
  727. function onMainButtonPressed() {
  728. if (isActive) {
  729. receiveWebViewEvent('mainButtonClicked');
  730. }
  731. }
  732. function buttonParams() {
  733. var color = mainButton.color;
  734. var text_color = mainButton.textColor;
  735. return isVisible ? {
  736. is_visible: true,
  737. is_active: isActive,
  738. is_progress_visible: isProgressVisible,
  739. text: buttonText,
  740. color: color,
  741. text_color: text_color
  742. } : {is_visible: false};
  743. }
  744. function buttonState(btn_params) {
  745. if (typeof btn_params === 'undefined') {
  746. btn_params = buttonParams();
  747. }
  748. return JSON.stringify(btn_params);
  749. }
  750. function updateButton() {
  751. var btn_params = buttonParams();
  752. var btn_state = buttonState(btn_params);
  753. if (curButtonState === btn_state) {
  754. return;
  755. }
  756. curButtonState = btn_state;
  757. WebView.postEvent('web_app_setup_main_button', false, btn_params);
  758. if (initParams.tgWebAppDebug) {
  759. updateDebugButton(btn_params);
  760. }
  761. }
  762. function updateDebugButton(btn_params) {
  763. if (btn_params.is_visible) {
  764. debugBtn.style.display = 'block';
  765. mainButtonHeight = 48;
  766. debugBtn.style.opacity = btn_params.is_active ? '1' : '0.8';
  767. debugBtn.style.cursor = btn_params.is_active ? 'pointer' : 'auto';
  768. debugBtn.disabled = !btn_params.is_active;
  769. debugBtn.innerText = btn_params.text;
  770. debugBtn.style.backgroundImage = btn_params.is_progress_visible ? "url('data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20viewport%3D%220%200%2048%2048%22%20width%3D%2248px%22%20height%3D%2248px%22%3E%3Ccircle%20cx%3D%2250%25%22%20cy%3D%2250%25%22%20stroke%3D%22%23fff%22%20stroke-width%3D%222.25%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%20stroke-dashoffset%3D%22106%22%20r%3D%229%22%20stroke-dasharray%3D%2256.52%22%20rotate%3D%22-90%22%3E%3Canimate%20attributeName%3D%22stroke-dashoffset%22%20attributeType%3D%22XML%22%20dur%3D%22360s%22%20from%3D%220%22%20to%3D%2212500%22%20repeatCount%3D%22indefinite%22%3E%3C%2Fanimate%3E%3CanimateTransform%20attributeName%3D%22transform%22%20attributeType%3D%22XML%22%20type%3D%22rotate%22%20dur%3D%221s%22%20from%3D%22-90%2024%2024%22%20to%3D%22630%2024%2024%22%20repeatCount%3D%22indefinite%22%3E%3C%2FanimateTransform%3E%3C%2Fcircle%3E%3C%2Fsvg%3E')" : 'none';
  771. debugBtn.style.backgroundColor = btn_params.color;
  772. debugBtn.style.color = btn_params.text_color;
  773. } else {
  774. debugBtn.style.display = 'none';
  775. mainButtonHeight = 0;
  776. }
  777. if (document.documentElement) {
  778. document.documentElement.style.boxSizing = 'border-box';
  779. document.documentElement.style.paddingBottom = mainButtonHeight + 'px';
  780. }
  781. setViewportHeight();
  782. }
  783. function setParams(params) {
  784. if (typeof params.text !== 'undefined') {
  785. var text = strTrim(params.text);
  786. if (!text.length) {
  787. console.error('[Telegram.WebApp] Main button text is required', params.text);
  788. throw Error('WebAppMainButtonParamInvalid');
  789. }
  790. if (text.length > 64) {
  791. console.error('[Telegram.WebApp] Main button text is too long', text);
  792. throw Error('WebAppMainButtonParamInvalid');
  793. }
  794. buttonText = text;
  795. }
  796. if (typeof params.color !== 'undefined') {
  797. if (params.color === false ||
  798. params.color === null) {
  799. buttonColor = false;
  800. } else {
  801. var color = parseColorToHex(params.color);
  802. if (!color) {
  803. console.error('[Telegram.WebApp] Main button color format is invalid', params.color);
  804. throw Error('WebAppMainButtonParamInvalid');
  805. }
  806. buttonColor = color;
  807. }
  808. }
  809. if (typeof params.text_color !== 'undefined') {
  810. if (params.text_color === false ||
  811. params.text_color === null) {
  812. buttonTextColor = false;
  813. } else {
  814. var text_color = parseColorToHex(params.text_color);
  815. if (!text_color) {
  816. console.error('[Telegram.WebApp] Main button text color format is invalid', params.text_color);
  817. throw Error('WebAppMainButtonParamInvalid');
  818. }
  819. buttonTextColor = text_color;
  820. }
  821. }
  822. if (typeof params.is_visible !== 'undefined') {
  823. if (params.is_visible &&
  824. !mainButton.text.length) {
  825. console.error('[Telegram.WebApp] Main button text is required');
  826. throw Error('WebAppMainButtonParamInvalid');
  827. }
  828. isVisible = !!params.is_visible;
  829. }
  830. if (typeof params.is_active !== 'undefined') {
  831. isActive = !!params.is_active;
  832. }
  833. updateButton();
  834. return mainButton;
  835. }
  836. mainButton.setText = function(text) {
  837. return mainButton.setParams({text: text});
  838. };
  839. mainButton.onClick = function(callback) {
  840. onWebViewEvent('mainButtonClicked', callback);
  841. return mainButton;
  842. };
  843. mainButton.offClick = function(callback) {
  844. offWebViewEvent('mainButtonClicked', callback);
  845. return mainButton;
  846. };
  847. mainButton.show = function() {
  848. return mainButton.setParams({is_visible: true});
  849. };
  850. mainButton.hide = function() {
  851. return mainButton.setParams({is_visible: false});
  852. };
  853. mainButton.enable = function() {
  854. return mainButton.setParams({is_active: true});
  855. };
  856. mainButton.disable = function() {
  857. return mainButton.setParams({is_active: false});
  858. };
  859. mainButton.showProgress = function(leaveActive) {
  860. isActive = !!leaveActive;
  861. isProgressVisible = true;
  862. updateButton();
  863. return mainButton;
  864. };
  865. mainButton.hideProgress = function() {
  866. if (!mainButton.isActive) {
  867. isActive = true;
  868. }
  869. isProgressVisible = false;
  870. updateButton();
  871. return mainButton;
  872. }
  873. mainButton.setParams = setParams;
  874. return mainButton;
  875. })();
  876. var SettingsButton = (function() {
  877. var isVisible = false;
  878. var settingsButton = {};
  879. Object.defineProperty(settingsButton, 'isVisible', {
  880. set: function(val){ setParams({is_visible: val}); },
  881. get: function(){ return isVisible; },
  882. enumerable: true
  883. });
  884. var curButtonState = null;
  885. WebView.onEvent('settings_button_pressed', onSettingsButtonPressed);
  886. function onSettingsButtonPressed() {
  887. receiveWebViewEvent('settingsButtonClicked');
  888. }
  889. function buttonParams() {
  890. return {is_visible: isVisible};
  891. }
  892. function buttonState(btn_params) {
  893. if (typeof btn_params === 'undefined') {
  894. btn_params = buttonParams();
  895. }
  896. return JSON.stringify(btn_params);
  897. }
  898. function buttonCheckVersion() {
  899. if (!versionAtLeast('6.10')) {
  900. console.warn('[Telegram.WebApp] SettingsButton is not supported in version ' + webAppVersion);
  901. return false;
  902. }
  903. return true;
  904. }
  905. function updateButton() {
  906. var btn_params = buttonParams();
  907. var btn_state = buttonState(btn_params);
  908. if (curButtonState === btn_state) {
  909. return;
  910. }
  911. curButtonState = btn_state;
  912. WebView.postEvent('web_app_setup_settings_button', false, btn_params);
  913. }
  914. function setParams(params) {
  915. if (!buttonCheckVersion()) {
  916. return settingsButton;
  917. }
  918. if (typeof params.is_visible !== 'undefined') {
  919. isVisible = !!params.is_visible;
  920. }
  921. updateButton();
  922. return settingsButton;
  923. }
  924. settingsButton.onClick = function(callback) {
  925. if (buttonCheckVersion()) {
  926. onWebViewEvent('settingsButtonClicked', callback);
  927. }
  928. return settingsButton;
  929. };
  930. settingsButton.offClick = function(callback) {
  931. if (buttonCheckVersion()) {
  932. offWebViewEvent('settingsButtonClicked', callback);
  933. }
  934. return settingsButton;
  935. };
  936. settingsButton.show = function() {
  937. return setParams({is_visible: true});
  938. };
  939. settingsButton.hide = function() {
  940. return setParams({is_visible: false});
  941. };
  942. return settingsButton;
  943. })();
  944. var HapticFeedback = (function() {
  945. var hapticFeedback = {};
  946. function triggerFeedback(params) {
  947. if (!versionAtLeast('6.1')) {
  948. console.warn('[Telegram.WebApp] HapticFeedback is not supported in version ' + webAppVersion);
  949. return hapticFeedback;
  950. }
  951. if (params.type == 'impact') {
  952. if (params.impact_style != 'light' &&
  953. params.impact_style != 'medium' &&
  954. params.impact_style != 'heavy' &&
  955. params.impact_style != 'rigid' &&
  956. params.impact_style != 'soft') {
  957. console.error('[Telegram.WebApp] Haptic impact style is invalid', params.impact_style);
  958. throw Error('WebAppHapticImpactStyleInvalid');
  959. }
  960. } else if (params.type == 'notification') {
  961. if (params.notification_type != 'error' &&
  962. params.notification_type != 'success' &&
  963. params.notification_type != 'warning') {
  964. console.error('[Telegram.WebApp] Haptic notification type is invalid', params.notification_type);
  965. throw Error('WebAppHapticNotificationTypeInvalid');
  966. }
  967. } else if (params.type == 'selection_change') {
  968. // no params needed
  969. } else {
  970. console.error('[Telegram.WebApp] Haptic feedback type is invalid', params.type);
  971. throw Error('WebAppHapticFeedbackTypeInvalid');
  972. }
  973. WebView.postEvent('web_app_trigger_haptic_feedback', false, params);
  974. return hapticFeedback;
  975. }
  976. hapticFeedback.impactOccurred = function(style) {
  977. return triggerFeedback({type: 'impact', impact_style: style});
  978. };
  979. hapticFeedback.notificationOccurred = function(type) {
  980. return triggerFeedback({type: 'notification', notification_type: type});
  981. };
  982. hapticFeedback.selectionChanged = function() {
  983. return triggerFeedback({type: 'selection_change'});
  984. };
  985. return hapticFeedback;
  986. })();
  987. var CloudStorage = (function() {
  988. var cloudStorage = {};
  989. function invokeStorageMethod(method, params, callback) {
  990. if (!versionAtLeast('6.9')) {
  991. console.error('[Telegram.WebApp] CloudStorage is not supported in version ' + webAppVersion);
  992. throw Error('WebAppMethodUnsupported');
  993. }
  994. invokeCustomMethod(method, params, callback);
  995. return cloudStorage;
  996. }
  997. cloudStorage.setItem = function(key, value, callback) {
  998. return invokeStorageMethod('saveStorageValue', {key: key, value: value}, callback);
  999. };
  1000. cloudStorage.getItem = function(key, callback) {
  1001. return cloudStorage.getItems([key], callback ? function(err, res) {
  1002. if (err) callback(err);
  1003. else callback(null, res[key]);
  1004. } : null);
  1005. };
  1006. cloudStorage.getItems = function(keys, callback) {
  1007. return invokeStorageMethod('getStorageValues', {keys: keys}, callback);
  1008. };
  1009. cloudStorage.removeItem = function(key, callback) {
  1010. return cloudStorage.removeItems([key], callback);
  1011. };
  1012. cloudStorage.removeItems = function(keys, callback) {
  1013. return invokeStorageMethod('deleteStorageValues', {keys: keys}, callback);
  1014. };
  1015. cloudStorage.getKeys = function(callback) {
  1016. return invokeStorageMethod('getStorageKeys', {}, callback);
  1017. };
  1018. return cloudStorage;
  1019. })();
  1020. var BiometricManager = (function() {
  1021. var isInited = false;
  1022. var isBiometricAvailable = false;
  1023. var biometricType = 'unknown';
  1024. var isAccessRequested = false;
  1025. var isAccessGranted = false;
  1026. var isBiometricTokenSaved = false;
  1027. var deviceId = '';
  1028. var biometricManager = {};
  1029. Object.defineProperty(biometricManager, 'isInited', {
  1030. get: function(){ return isInited; },
  1031. enumerable: true
  1032. });
  1033. Object.defineProperty(biometricManager, 'isBiometricAvailable', {
  1034. get: function(){ return isInited && isBiometricAvailable; },
  1035. enumerable: true
  1036. });
  1037. Object.defineProperty(biometricManager, 'biometricType', {
  1038. get: function(){ return biometricType || 'unknown'; },
  1039. enumerable: true
  1040. });
  1041. Object.defineProperty(biometricManager, 'isAccessRequested', {
  1042. get: function(){ return isAccessRequested; },
  1043. enumerable: true
  1044. });
  1045. Object.defineProperty(biometricManager, 'isAccessGranted', {
  1046. get: function(){ return isAccessRequested && isAccessGranted; },
  1047. enumerable: true
  1048. });
  1049. Object.defineProperty(biometricManager, 'isBiometricTokenSaved', {
  1050. get: function(){ return isBiometricTokenSaved; },
  1051. enumerable: true
  1052. });
  1053. Object.defineProperty(biometricManager, 'deviceId', {
  1054. get: function(){ return deviceId || ''; },
  1055. enumerable: true
  1056. });
  1057. var initRequestState = {callbacks: []};
  1058. var accessRequestState = false;
  1059. var authRequestState = false;
  1060. var tokenRequestState = false;
  1061. WebView.onEvent('biometry_info_received', onBiometryInfoReceived);
  1062. WebView.onEvent('biometry_auth_requested', onBiometryAuthRequested);
  1063. WebView.onEvent('biometry_token_updated', onBiometryTokenUpdated);
  1064. function onBiometryInfoReceived(eventType, eventData) {
  1065. isInited = true;
  1066. if (eventData.available) {
  1067. isBiometricAvailable = true;
  1068. biometricType = eventData.type || 'unknown';
  1069. if (eventData.access_requested) {
  1070. isAccessRequested = true;
  1071. isAccessGranted = !!eventData.access_granted;
  1072. isBiometricTokenSaved = !!eventData.token_saved;
  1073. } else {
  1074. isAccessRequested = false;
  1075. isAccessGranted = false;
  1076. isBiometricTokenSaved = false;
  1077. }
  1078. } else {
  1079. isBiometricAvailable = false;
  1080. biometricType = 'unknown';
  1081. isAccessRequested = false;
  1082. isAccessGranted = false;
  1083. isBiometricTokenSaved = false;
  1084. }
  1085. deviceId = eventData.device_id || '';
  1086. if (initRequestState.callbacks.length > 0) {
  1087. for (var i = 0; i < initRequestState.callbacks.length; i++) {
  1088. var callback = initRequestState.callbacks[i];
  1089. callback();
  1090. }
  1091. }
  1092. if (accessRequestState) {
  1093. var state = accessRequestState;
  1094. accessRequestState = false;
  1095. if (state.callback) {
  1096. state.callback(isAccessGranted);
  1097. }
  1098. }
  1099. receiveWebViewEvent('biometricManagerUpdated');
  1100. }
  1101. function onBiometryAuthRequested(eventType, eventData) {
  1102. var isAuthenticated = (eventData.status == 'authorized'),
  1103. biometricToken = eventData.token || '';
  1104. if (authRequestState) {
  1105. var state = authRequestState;
  1106. authRequestState = false;
  1107. if (state.callback) {
  1108. state.callback(isAuthenticated, isAuthenticated ? biometricToken : null);
  1109. }
  1110. }
  1111. receiveWebViewEvent('biometricAuthRequested', isAuthenticated ? {
  1112. isAuthenticated: true,
  1113. biometricToken: biometricToken
  1114. } : {
  1115. isAuthenticated: false
  1116. });
  1117. }
  1118. function onBiometryTokenUpdated(eventType, eventData) {
  1119. var applied = false;
  1120. if (isBiometricAvailable &&
  1121. isAccessRequested) {
  1122. if (eventData.status == 'updated') {
  1123. isBiometricTokenSaved = true;
  1124. applied = true;
  1125. }
  1126. else if (eventData.status == 'removed') {
  1127. isBiometricTokenSaved = false;
  1128. applied = true;
  1129. }
  1130. }
  1131. if (tokenRequestState) {
  1132. var state = tokenRequestState;
  1133. tokenRequestState = false;
  1134. if (state.callback) {
  1135. state.callback(applied);
  1136. }
  1137. }
  1138. receiveWebViewEvent('biometricTokenUpdated', {
  1139. isUpdated: applied
  1140. });
  1141. }
  1142. function checkVersion() {
  1143. if (!versionAtLeast('7.2')) {
  1144. console.warn('[Telegram.WebApp] BiometricManager is not supported in version ' + webAppVersion);
  1145. return false;
  1146. }
  1147. return true;
  1148. }
  1149. function checkInit() {
  1150. if (!isInited) {
  1151. console.error('[Telegram.WebApp] BiometricManager should be inited before using.');
  1152. throw Error('WebAppBiometricManagerNotInited');
  1153. }
  1154. return true;
  1155. }
  1156. biometricManager.init = function(callback) {
  1157. if (!checkVersion()) {
  1158. return biometricManager;
  1159. }
  1160. if (isInited) {
  1161. return biometricManager;
  1162. }
  1163. if (callback) {
  1164. initRequestState.callbacks.push(callback);
  1165. }
  1166. WebView.postEvent('web_app_biometry_get_info', false);
  1167. return biometricManager;
  1168. };
  1169. biometricManager.requestAccess = function(params, callback) {
  1170. if (!checkVersion()) {
  1171. return biometricManager;
  1172. }
  1173. checkInit();
  1174. if (!isBiometricAvailable) {
  1175. console.error('[Telegram.WebApp] Biometrics is not available on this device.');
  1176. throw Error('WebAppBiometricManagerBiometricsNotAvailable');
  1177. }
  1178. if (accessRequestState) {
  1179. console.error('[Telegram.WebApp] Access is already requested');
  1180. throw Error('WebAppBiometricManagerAccessRequested');
  1181. }
  1182. var popup_params = {};
  1183. if (typeof params.reason !== 'undefined') {
  1184. var reason = strTrim(params.reason);
  1185. if (reason.length > 128) {
  1186. console.error('[Telegram.WebApp] Biometric reason is too long', reason);
  1187. throw Error('WebAppBiometricRequestAccessParamInvalid');
  1188. }
  1189. if (reason.length > 0) {
  1190. popup_params.reason = reason;
  1191. }
  1192. }
  1193. accessRequestState = {
  1194. callback: callback
  1195. };
  1196. WebView.postEvent('web_app_biometry_request_access', false, popup_params);
  1197. return biometricManager;
  1198. };
  1199. biometricManager.authenticate = function(params, callback) {
  1200. if (!checkVersion()) {
  1201. return biometricManager;
  1202. }
  1203. checkInit();
  1204. if (!isBiometricAvailable) {
  1205. console.error('[Telegram.WebApp] Biometrics is not available on this device.');
  1206. throw Error('WebAppBiometricManagerBiometricsNotAvailable');
  1207. }
  1208. if (!isAccessGranted) {
  1209. console.error('[Telegram.WebApp] Biometric access was not granted by the user.');
  1210. throw Error('WebAppBiometricManagerBiometricAccessNotGranted');
  1211. }
  1212. if (authRequestState) {
  1213. console.error('[Telegram.WebApp] Authentication request is already in progress.');
  1214. throw Error('WebAppBiometricManagerAuthenticationRequested');
  1215. }
  1216. var popup_params = {};
  1217. if (typeof params.reason !== 'undefined') {
  1218. var reason = strTrim(params.reason);
  1219. if (reason.length > 128) {
  1220. console.error('[Telegram.WebApp] Biometric reason is too long', reason);
  1221. throw Error('WebAppBiometricRequestAccessParamInvalid');
  1222. }
  1223. if (reason.length > 0) {
  1224. popup_params.reason = reason;
  1225. }
  1226. }
  1227. authRequestState = {
  1228. callback: callback
  1229. };
  1230. WebView.postEvent('web_app_biometry_request_auth', false, popup_params);
  1231. return biometricManager;
  1232. };
  1233. biometricManager.updateBiometricToken = function(token, callback) {
  1234. if (!checkVersion()) {
  1235. return biometricManager;
  1236. }
  1237. token = token || '';
  1238. if (token.length > 1024) {
  1239. console.error('[Telegram.WebApp] Token is too long', token);
  1240. throw Error('WebAppBiometricManagerTokenInvalid');
  1241. }
  1242. checkInit();
  1243. if (!isBiometricAvailable) {
  1244. console.error('[Telegram.WebApp] Biometrics is not available on this device.');
  1245. throw Error('WebAppBiometricManagerBiometricsNotAvailable');
  1246. }
  1247. if (!isAccessGranted) {
  1248. console.error('[Telegram.WebApp] Biometric access was not granted by the user.');
  1249. throw Error('WebAppBiometricManagerBiometricAccessNotGranted');
  1250. }
  1251. if (tokenRequestState) {
  1252. console.error('[Telegram.WebApp] Token request is already in progress.');
  1253. throw Error('WebAppBiometricManagerTokenUpdateRequested');
  1254. }
  1255. tokenRequestState = {
  1256. callback: callback
  1257. };
  1258. WebView.postEvent('web_app_biometry_update_token', false, {token: token});
  1259. return biometricManager;
  1260. };
  1261. biometricManager.openSettings = function() {
  1262. if (!checkVersion()) {
  1263. return biometricManager;
  1264. }
  1265. checkInit();
  1266. if (!isBiometricAvailable) {
  1267. console.error('[Telegram.WebApp] Biometrics is not available on this device.');
  1268. throw Error('WebAppBiometricManagerBiometricsNotAvailable');
  1269. }
  1270. if (!isAccessRequested) {
  1271. console.error('[Telegram.WebApp] Biometric access was not requested yet.');
  1272. throw Error('WebAppBiometricManagerBiometricsAccessNotRequested');
  1273. }
  1274. if (isAccessGranted) {
  1275. console.warn('[Telegram.WebApp] Biometric access was granted by the user, no need to go to settings.');
  1276. return biometricManager;
  1277. }
  1278. WebView.postEvent('web_app_biometry_open_settings', false);
  1279. return biometricManager;
  1280. };
  1281. return biometricManager;
  1282. })();
  1283. var webAppInvoices = {};
  1284. function onInvoiceClosed(eventType, eventData) {
  1285. if (eventData.slug && webAppInvoices[eventData.slug]) {
  1286. var invoiceData = webAppInvoices[eventData.slug];
  1287. delete webAppInvoices[eventData.slug];
  1288. if (invoiceData.callback) {
  1289. invoiceData.callback(eventData.status);
  1290. }
  1291. receiveWebViewEvent('invoiceClosed', {
  1292. url: invoiceData.url,
  1293. status: eventData.status
  1294. });
  1295. }
  1296. }
  1297. var webAppPopupOpened = false;
  1298. function onPopupClosed(eventType, eventData) {
  1299. if (webAppPopupOpened) {
  1300. var popupData = webAppPopupOpened;
  1301. webAppPopupOpened = false;
  1302. var button_id = null;
  1303. if (typeof eventData.button_id !== 'undefined') {
  1304. button_id = eventData.button_id;
  1305. }
  1306. if (popupData.callback) {
  1307. popupData.callback(button_id);
  1308. }
  1309. receiveWebViewEvent('popupClosed', {
  1310. button_id: button_id
  1311. });
  1312. }
  1313. }
  1314. var webAppScanQrPopupOpened = false;
  1315. function onQrTextReceived(eventType, eventData) {
  1316. if (webAppScanQrPopupOpened) {
  1317. var popupData = webAppScanQrPopupOpened;
  1318. var data = null;
  1319. if (typeof eventData.data !== 'undefined') {
  1320. data = eventData.data;
  1321. }
  1322. if (popupData.callback) {
  1323. if (popupData.callback(data)) {
  1324. webAppScanQrPopupOpened = false;
  1325. WebView.postEvent('web_app_close_scan_qr_popup', false);
  1326. }
  1327. }
  1328. receiveWebViewEvent('qrTextReceived', {
  1329. data: data
  1330. });
  1331. }
  1332. }
  1333. function onScanQrPopupClosed(eventType, eventData) {
  1334. webAppScanQrPopupOpened = false;
  1335. receiveWebViewEvent('scanQrPopupClosed');
  1336. }
  1337. function onClipboardTextReceived(eventType, eventData) {
  1338. if (eventData.req_id && webAppCallbacks[eventData.req_id]) {
  1339. var requestData = webAppCallbacks[eventData.req_id];
  1340. delete webAppCallbacks[eventData.req_id];
  1341. var data = null;
  1342. if (typeof eventData.data !== 'undefined') {
  1343. data = eventData.data;
  1344. }
  1345. if (requestData.callback) {
  1346. requestData.callback(data);
  1347. }
  1348. receiveWebViewEvent('clipboardTextReceived', {
  1349. data: data
  1350. });
  1351. }
  1352. }
  1353. var WebAppWriteAccessRequested = false;
  1354. function onWriteAccessRequested(eventType, eventData) {
  1355. if (WebAppWriteAccessRequested) {
  1356. var requestData = WebAppWriteAccessRequested;
  1357. WebAppWriteAccessRequested = false;
  1358. if (requestData.callback) {
  1359. requestData.callback(eventData.status == 'allowed');
  1360. }
  1361. receiveWebViewEvent('writeAccessRequested', {
  1362. status: eventData.status
  1363. });
  1364. }
  1365. }
  1366. function getRequestedContact(callback, timeout) {
  1367. var reqTo, fallbackTo, reqDelay = 0;
  1368. var reqInvoke = function() {
  1369. invokeCustomMethod('getRequestedContact', {}, function(err, res) {
  1370. if (res && res.length) {
  1371. clearTimeout(fallbackTo);
  1372. callback(res);
  1373. } else {
  1374. reqDelay += 50;
  1375. reqTo = setTimeout(reqInvoke, reqDelay);
  1376. }
  1377. });
  1378. };
  1379. var fallbackInvoke = function() {
  1380. clearTimeout(reqTo);
  1381. callback('');
  1382. };
  1383. fallbackTo = setTimeout(fallbackInvoke, timeout);
  1384. reqInvoke();
  1385. }
  1386. var WebAppContactRequested = false;
  1387. function onPhoneRequested(eventType, eventData) {
  1388. if (WebAppContactRequested) {
  1389. var requestData = WebAppContactRequested;
  1390. WebAppContactRequested = false;
  1391. var requestSent = eventData.status == 'sent';
  1392. var webViewEvent = {
  1393. status: eventData.status
  1394. };
  1395. if (requestSent) {
  1396. getRequestedContact(function(res) {
  1397. if (res && res.length) {
  1398. webViewEvent.response = res;
  1399. webViewEvent.responseUnsafe = Utils.urlParseQueryString(res);
  1400. for (var key in webViewEvent.responseUnsafe) {
  1401. var val = webViewEvent.responseUnsafe[key];
  1402. try {
  1403. if (val.substr(0, 1) == '{' && val.substr(-1) == '}' ||
  1404. val.substr(0, 1) == '[' && val.substr(-1) == ']') {
  1405. webViewEvent.responseUnsafe[key] = JSON.parse(val);
  1406. }
  1407. } catch (e) {}
  1408. }
  1409. }
  1410. if (requestData.callback) {
  1411. requestData.callback(requestSent, webViewEvent);
  1412. }
  1413. receiveWebViewEvent('contactRequested', webViewEvent);
  1414. }, 3000);
  1415. } else {
  1416. if (requestData.callback) {
  1417. requestData.callback(requestSent, webViewEvent);
  1418. }
  1419. receiveWebViewEvent('contactRequested', webViewEvent);
  1420. }
  1421. }
  1422. }
  1423. function onCustomMethodInvoked(eventType, eventData) {
  1424. if (eventData.req_id && webAppCallbacks[eventData.req_id]) {
  1425. var requestData = webAppCallbacks[eventData.req_id];
  1426. delete webAppCallbacks[eventData.req_id];
  1427. var res = null, err = null;
  1428. if (typeof eventData.result !== 'undefined') {
  1429. res = eventData.result;
  1430. }
  1431. if (typeof eventData.error !== 'undefined') {
  1432. err = eventData.error;
  1433. }
  1434. if (requestData.callback) {
  1435. requestData.callback(err, res);
  1436. }
  1437. }
  1438. }
  1439. function invokeCustomMethod(method, params, callback) {
  1440. if (!versionAtLeast('6.9')) {
  1441. console.error('[Telegram.WebApp] Method invokeCustomMethod is not supported in version ' + webAppVersion);
  1442. throw Error('WebAppMethodUnsupported');
  1443. }
  1444. var req_id = generateCallbackId(16);
  1445. var req_params = {req_id: req_id, method: method, params: params || {}};
  1446. webAppCallbacks[req_id] = {
  1447. callback: callback
  1448. };
  1449. WebView.postEvent('web_app_invoke_custom_method', false, req_params);
  1450. };
  1451. if (!window.Telegram) {
  1452. window.Telegram = {};
  1453. }
  1454. Object.defineProperty(WebApp, 'initData', {
  1455. get: function(){ return webAppInitData; },
  1456. enumerable: true
  1457. });
  1458. Object.defineProperty(WebApp, 'initDataUnsafe', {
  1459. get: function(){ return webAppInitDataUnsafe; },
  1460. enumerable: true
  1461. });
  1462. Object.defineProperty(WebApp, 'version', {
  1463. get: function(){ return webAppVersion; },
  1464. enumerable: true
  1465. });
  1466. Object.defineProperty(WebApp, 'platform', {
  1467. get: function(){ return webAppPlatform; },
  1468. enumerable: true
  1469. });
  1470. Object.defineProperty(WebApp, 'colorScheme', {
  1471. get: function(){ return colorScheme; },
  1472. enumerable: true
  1473. });
  1474. Object.defineProperty(WebApp, 'themeParams', {
  1475. get: function(){ return themeParams; },
  1476. enumerable: true
  1477. });
  1478. Object.defineProperty(WebApp, 'isExpanded', {
  1479. get: function(){ return isExpanded; },
  1480. enumerable: true
  1481. });
  1482. Object.defineProperty(WebApp, 'viewportHeight', {
  1483. get: function(){ return (viewportHeight === false ? window.innerHeight : viewportHeight) - mainButtonHeight; },
  1484. enumerable: true
  1485. });
  1486. Object.defineProperty(WebApp, 'viewportStableHeight', {
  1487. get: function(){ return (viewportStableHeight === false ? window.innerHeight : viewportStableHeight) - mainButtonHeight; },
  1488. enumerable: true
  1489. });
  1490. Object.defineProperty(WebApp, 'isClosingConfirmationEnabled', {
  1491. set: function(val){ setClosingConfirmation(val); },
  1492. get: function(){ return isClosingConfirmationEnabled; },
  1493. enumerable: true
  1494. });
  1495. Object.defineProperty(WebApp, 'isVerticalSwipesEnabled', {
  1496. set: function(val){ toggleVerticalSwipes(val); },
  1497. get: function(){ return isVerticalSwipesEnabled; },
  1498. enumerable: true
  1499. });
  1500. Object.defineProperty(WebApp, 'headerColor', {
  1501. set: function(val){ setHeaderColor(val); },
  1502. get: function(){ return getHeaderColor(); },
  1503. enumerable: true
  1504. });
  1505. Object.defineProperty(WebApp, 'backgroundColor', {
  1506. set: function(val){ setBackgroundColor(val); },
  1507. get: function(){ return getBackgroundColor(); },
  1508. enumerable: true
  1509. });
  1510. Object.defineProperty(WebApp, 'BackButton', {
  1511. value: BackButton,
  1512. enumerable: true
  1513. });
  1514. Object.defineProperty(WebApp, 'MainButton', {
  1515. value: MainButton,
  1516. enumerable: true
  1517. });
  1518. Object.defineProperty(WebApp, 'SettingsButton', {
  1519. value: SettingsButton,
  1520. enumerable: true
  1521. });
  1522. Object.defineProperty(WebApp, 'HapticFeedback', {
  1523. value: HapticFeedback,
  1524. enumerable: true
  1525. });
  1526. Object.defineProperty(WebApp, 'CloudStorage', {
  1527. value: CloudStorage,
  1528. enumerable: true
  1529. });
  1530. Object.defineProperty(WebApp, 'BiometricManager', {
  1531. value: BiometricManager,
  1532. enumerable: true
  1533. });
  1534. WebApp.setHeaderColor = function(color_key) {
  1535. WebApp.headerColor = color_key;
  1536. };
  1537. WebApp.setBackgroundColor = function(color) {
  1538. WebApp.backgroundColor = color;
  1539. };
  1540. WebApp.enableClosingConfirmation = function() {
  1541. WebApp.isClosingConfirmationEnabled = true;
  1542. };
  1543. WebApp.disableClosingConfirmation = function() {
  1544. WebApp.isClosingConfirmationEnabled = false;
  1545. };
  1546. WebApp.enableVerticalSwipes = function() {
  1547. WebApp.isVerticalSwipesEnabled = true;
  1548. };
  1549. WebApp.disableVerticalSwipes = function() {
  1550. WebApp.isVerticalSwipesEnabled = false;
  1551. };
  1552. WebApp.isVersionAtLeast = function(ver) {
  1553. return versionAtLeast(ver);
  1554. };
  1555. WebApp.onEvent = function(eventType, callback) {
  1556. onWebViewEvent(eventType, callback);
  1557. };
  1558. WebApp.offEvent = function(eventType, callback) {offWebViewEvent(eventType, callback);
  1559. };
  1560. WebApp.sendData = function (data) {
  1561. if (!data || !data.length) {
  1562. console.error('[Telegram.WebApp] Data is required', data);
  1563. throw Error('WebAppDataInvalid');
  1564. }
  1565. if (byteLength(data) > 4096) {
  1566. console.error('[Telegram.WebApp] Data is too long', data);
  1567. throw Error('WebAppDataInvalid');
  1568. }
  1569. WebView.postEvent('web_app_data_send', false, {data: data});
  1570. };
  1571. WebApp.switchInlineQuery = function (query, choose_chat_types) {
  1572. if (!versionAtLeast('6.6')) {
  1573. console.error('[Telegram.WebApp] Method switchInlineQuery is not supported in version ' + webAppVersion);
  1574. throw Error('WebAppMethodUnsupported');
  1575. }
  1576. if (!initParams.tgWebAppBotInline) {
  1577. console.error('[Telegram.WebApp] Inline mode is disabled for this bot. Read more about inline mode: https://core.telegram.org/bots/inline');
  1578. throw Error('WebAppInlineModeDisabled');
  1579. }
  1580. query = query || '';
  1581. if (query.length > 256) {
  1582. console.error('[Telegram.WebApp] Inline query is too long', query);
  1583. throw Error('WebAppInlineQueryInvalid');
  1584. }
  1585. var chat_types = [];
  1586. if (choose_chat_types) {
  1587. if (!Array.isArray(choose_chat_types)) {
  1588. console.error('[Telegram.WebApp] Choose chat types should be an array', choose_chat_types);
  1589. throw Error('WebAppInlineChooseChatTypesInvalid');
  1590. }
  1591. var good_types = {users: 1, bots: 1, groups: 1, channels: 1};
  1592. for (var i = 0; i < choose_chat_types.length; i++) {
  1593. var chat_type = choose_chat_types[i];
  1594. if (!good_types[chat_type]) {
  1595. console.error('[Telegram.WebApp] Choose chat type is invalid', chat_type);
  1596. throw Error('WebAppInlineChooseChatTypeInvalid');
  1597. }
  1598. if (good_types[chat_type] != 2) {
  1599. good_types[chat_type] = 2;
  1600. chat_types.push(chat_type);
  1601. }
  1602. }
  1603. }
  1604. WebView.postEvent('web_app_switch_inline_query', false, {query: query, chat_types: chat_types});
  1605. };
  1606. WebApp.openLink = function (url, options) {
  1607. var a = document.createElement('A');
  1608. a.href = url;
  1609. if (a.protocol != 'http:' &&
  1610. a.protocol != 'https:') {
  1611. console.error('[Telegram.WebApp] Url protocol is not supported', url);
  1612. throw Error('WebAppTgUrlInvalid');
  1613. }
  1614. var url = a.href;
  1615. options = options || {};
  1616. if (versionAtLeast('6.1')) {
  1617. var req_params = {url: url};
  1618. if (versionAtLeast('6.4') && options.try_instant_view) {
  1619. req_params.try_instant_view = true;
  1620. }
  1621. if (versionAtLeast('7.6') && options.try_browser) {
  1622. req_params.try_browser = options.try_browser;
  1623. }
  1624. WebView.postEvent('web_app_open_link', false, req_params);
  1625. } else {
  1626. window.open(url, '_blank');
  1627. }
  1628. };
  1629. WebApp.openTelegramLink = function (url) {
  1630. var a = document.createElement('A');
  1631. a.href = url;
  1632. if (a.protocol != 'http:' &&
  1633. a.protocol != 'https:') {
  1634. console.error('[Telegram.WebApp] Url protocol is not supported', url);
  1635. throw Error('WebAppTgUrlInvalid');
  1636. }
  1637. if (a.hostname != 't.me') {
  1638. console.error('[Telegram.WebApp] Url host is not supported', url);
  1639. throw Error('WebAppTgUrlInvalid');
  1640. }
  1641. var path_full = a.pathname + a.search;
  1642. if (isIframe || versionAtLeast('6.1')) {
  1643. WebView.postEvent('web_app_open_tg_link', false, {path_full: path_full});
  1644. } else {
  1645. location.href = 'https://t.me' + path_full;
  1646. }
  1647. };
  1648. WebApp.openInvoice = function (url, callback) {
  1649. var a = document.createElement('A'), match, slug;
  1650. a.href = url;
  1651. if (a.protocol != 'http:' &&
  1652. a.protocol != 'https:' ||
  1653. a.hostname != 't.me' ||
  1654. !(match = a.pathname.match(/^\/(\$|invoice\/)([A-Za-z0-9\-_=]+)$/)) ||
  1655. !(slug = match[2])) {
  1656. console.error('[Telegram.WebApp] Invoice url is invalid', url);
  1657. throw Error('WebAppInvoiceUrlInvalid');
  1658. }
  1659. if (!versionAtLeast('6.1')) {
  1660. console.error('[Telegram.WebApp] Method openInvoice is not supported in version ' + webAppVersion);
  1661. throw Error('WebAppMethodUnsupported');
  1662. }
  1663. if (webAppInvoices[slug]) {
  1664. console.error('[Telegram.WebApp] Invoice is already opened');
  1665. throw Error('WebAppInvoiceOpened');
  1666. }
  1667. webAppInvoices[slug] = {
  1668. url: url,
  1669. callback: callback
  1670. };
  1671. WebView.postEvent('web_app_open_invoice', false, {slug: slug});
  1672. };
  1673. WebApp.showPopup = function (params, callback) {
  1674. if (!versionAtLeast('6.2')) {
  1675. console.error('[Telegram.WebApp] Method showPopup is not supported in version ' + webAppVersion);
  1676. throw Error('WebAppMethodUnsupported');
  1677. }
  1678. if (webAppPopupOpened) {
  1679. console.error('[Telegram.WebApp] Popup is already opened');
  1680. throw Error('WebAppPopupOpened');
  1681. }
  1682. var title = '';
  1683. var message = '';
  1684. var buttons = [];
  1685. var popup_buttons = {};
  1686. var popup_params = {};
  1687. if (typeof params.title !== 'undefined') {
  1688. title = strTrim(params.title);
  1689. if (title.length > 64) {
  1690. console.error('[Telegram.WebApp] Popup title is too long', title);
  1691. throw Error('WebAppPopupParamInvalid');
  1692. }
  1693. if (title.length > 0) {
  1694. popup_params.title = title;
  1695. }
  1696. }
  1697. if (typeof params.message !== 'undefined') {
  1698. message = strTrim(params.message);
  1699. }
  1700. if (!message.length) {
  1701. console.error('[Telegram.WebApp] Popup message is required', params.message);
  1702. throw Error('WebAppPopupParamInvalid');
  1703. }
  1704. if (message.length > 256) {
  1705. console.error('[Telegram.WebApp] Popup message is too long', message);
  1706. throw Error('WebAppPopupParamInvalid');
  1707. }
  1708. popup_params.message = message;
  1709. if (typeof params.buttons !== 'undefined') {
  1710. if (!Array.isArray(params.buttons)) {
  1711. console.error('[Telegram.WebApp] Popup buttons should be an array', params.buttons);
  1712. throw Error('WebAppPopupParamInvalid');
  1713. }
  1714. for (var i = 0; i < params.buttons.length; i++) {
  1715. var button = params.buttons[i];
  1716. var btn = {};
  1717. var id = '';
  1718. if (typeof button.id !== 'undefined') {
  1719. id = button.id.toString();
  1720. if (id.length > 64) {
  1721. console.error('[Telegram.WebApp] Popup button id is too long', id);
  1722. throw Error('WebAppPopupParamInvalid');
  1723. }
  1724. }
  1725. btn.id = id;
  1726. var button_type = button.type;
  1727. if (typeof button_type === 'undefined') {
  1728. button_type = 'default';
  1729. }
  1730. btn.type = button_type;
  1731. if (button_type == 'ok' ||
  1732. button_type == 'close' ||
  1733. button_type == 'cancel') {
  1734. // no params needed
  1735. } else if (button_type == 'default' ||
  1736. button_type == 'destructive') {
  1737. var text = '';
  1738. if (typeof button.text !== 'undefined') {
  1739. text = strTrim(button.text);
  1740. }
  1741. if (!text.length) {
  1742. console.error('[Telegram.WebApp] Popup button text is required for type ' + button_type, button.text);
  1743. throw Error('WebAppPopupParamInvalid');
  1744. }
  1745. if (text.length > 64) {
  1746. console.error('[Telegram.WebApp] Popup button text is too long', text);
  1747. throw Error('WebAppPopupParamInvalid');
  1748. }
  1749. btn.text = text;
  1750. } else {
  1751. console.error('[Telegram.WebApp] Popup button type is invalid', button_type);
  1752. throw Error('WebAppPopupParamInvalid');
  1753. }
  1754. buttons.push(btn);
  1755. }
  1756. } else {
  1757. buttons.push({id: '', type: 'close'});
  1758. }
  1759. if (buttons.length < 1) {
  1760. console.error('[Telegram.WebApp] Popup should have at least one button');
  1761. throw Error('WebAppPopupParamInvalid');
  1762. }
  1763. if (buttons.length > 3) {
  1764. console.error('[Telegram.WebApp] Popup should not have more than 3 buttons');
  1765. throw Error('WebAppPopupParamInvalid');
  1766. }
  1767. popup_params.buttons = buttons;
  1768. webAppPopupOpened = {
  1769. callback: callback
  1770. };
  1771. WebView.postEvent('web_app_open_popup', false, popup_params);
  1772. };
  1773. WebApp.showAlert = function (message, callback) {
  1774. WebApp.showPopup({
  1775. message: message
  1776. }, callback ? function(){ callback(); } : null);
  1777. };
  1778. WebApp.showConfirm = function (message, callback) {
  1779. WebApp.showPopup({
  1780. message: message,
  1781. buttons: [
  1782. {type: 'ok', id: 'ok'},
  1783. {type: 'cancel'}
  1784. ]
  1785. }, callback ? function (button_id) {
  1786. callback(button_id == 'ok');
  1787. } : null);
  1788. };
  1789. WebApp.showScanQrPopup = function (params, callback) {
  1790. if (!versionAtLeast('6.4')) {
  1791. console.error('[Telegram.WebApp] Method showScanQrPopup is not supported in version ' + webAppVersion);
  1792. throw Error('WebAppMethodUnsupported');
  1793. }
  1794. if (webAppScanQrPopupOpened) {
  1795. console.error('[Telegram.WebApp] Popup is already opened');
  1796. throw Error('WebAppScanQrPopupOpened');
  1797. }
  1798. var text = '';
  1799. var popup_params = {};
  1800. if (typeof params.text !== 'undefined') {
  1801. text = strTrim(params.text);
  1802. if (text.length > 64) {
  1803. console.error('[Telegram.WebApp] Scan QR popup text is too long', text);
  1804. throw Error('WebAppScanQrPopupParamInvalid');
  1805. }
  1806. if (text.length > 0) {
  1807. popup_params.text = text;
  1808. }
  1809. }
  1810. webAppScanQrPopupOpened = {
  1811. callback: callback
  1812. };
  1813. WebView.postEvent('web_app_open_scan_qr_popup', false, popup_params);
  1814. };
  1815. WebApp.closeScanQrPopup = function () {
  1816. if (!versionAtLeast('6.4')) {
  1817. console.error('[Telegram.WebApp] Method closeScanQrPopup is not supported in version ' + webAppVersion);
  1818. throw Error('WebAppMethodUnsupported');
  1819. }
  1820. webAppScanQrPopupOpened = false;
  1821. WebView.postEvent('web_app_close_scan_qr_popup', false);
  1822. };
  1823. WebApp.readTextFromClipboard = function (callback) {
  1824. if (!versionAtLeast('6.4')) {
  1825. console.error('[Telegram.WebApp] Method readTextFromClipboard is not supported in version ' + webAppVersion);
  1826. throw Error('WebAppMethodUnsupported');
  1827. }
  1828. var req_id = generateCallbackId(16);
  1829. var req_params = {req_id: req_id};
  1830. webAppCallbacks[req_id] = {
  1831. callback: callback
  1832. };
  1833. WebView.postEvent('web_app_read_text_from_clipboard', false, req_params);
  1834. };
  1835. WebApp.requestWriteAccess = function (callback) {
  1836. if (!versionAtLeast('6.9')) {
  1837. console.error('[Telegram.WebApp] Method requestWriteAccess is not supported in version ' + webAppVersion);
  1838. throw Error('WebAppMethodUnsupported');
  1839. }
  1840. if (WebAppWriteAccessRequested) {
  1841. console.error('[Telegram.WebApp] Write access is already requested');
  1842. throw Error('WebAppWriteAccessRequested');
  1843. }
  1844. WebAppWriteAccessRequested = {
  1845. callback: callback
  1846. };
  1847. WebView.postEvent('web_app_request_write_access');
  1848. };
  1849. WebApp.requestContact = function (callback) {
  1850. if (!versionAtLeast('6.9')) {
  1851. console.error('[Telegram.WebApp] Method requestContact is not supported in version ' + webAppVersion);
  1852. throw Error('WebAppMethodUnsupported');
  1853. }
  1854. if (WebAppContactRequested) {
  1855. console.error('[Telegram.WebApp] Contact is already requested');
  1856. throw Error('WebAppContactRequested');
  1857. }
  1858. WebAppContactRequested = {
  1859. callback: callback
  1860. };
  1861. WebView.postEvent('web_app_request_phone');
  1862. };
  1863. WebApp.shareToStory = function (media_url, params) {
  1864. params = params || {};
  1865. if (!versionAtLeast('7.8')) {
  1866. console.error('[Telegram.WebApp] Method shareToStory is not supported in version ' + webAppVersion);
  1867. throw Error('WebAppMethodUnsupported');
  1868. }
  1869. var a = document.createElement('A');
  1870. a.href = media_url;
  1871. if (a.protocol != 'http:' &&
  1872. a.protocol != 'https:') {
  1873. console.error('[Telegram.WebApp] Media url protocol is not supported', url);
  1874. throw Error('WebAppMediaUrlInvalid');
  1875. }
  1876. var share_params = {};
  1877. share_params.media_url = a.href;
  1878. if (typeof params.text !== 'undefined') {
  1879. var text = strTrim(params.text);
  1880. if (text.length > 2048) {
  1881. console.error('[Telegram.WebApp] Text is too long', text);
  1882. throw Error('WebAppShareToStoryParamInvalid');
  1883. }
  1884. if (text.length > 0) {
  1885. share_params.text = text;
  1886. }
  1887. }
  1888. if (typeof params.widget_link !== 'undefined') {
  1889. params.widget_link = params.widget_link || {};
  1890. a.href = params.widget_link.url;
  1891. if (a.protocol != 'http:' &&
  1892. a.protocol != 'https:') {
  1893. console.error('[Telegram.WebApp] Link protocol is not supported', url);
  1894. throw Error('WebAppShareToStoryParamInvalid');
  1895. }
  1896. var widget_link = {
  1897. url: a.href
  1898. };
  1899. if (typeof params.widget_link.name !== 'undefined') {
  1900. var link_name = strTrim(params.widget_link.name);
  1901. if (link_name.length > 48) {
  1902. console.error('[Telegram.WebApp] Link name is too long', link_name);
  1903. throw Error('WebAppShareToStoryParamInvalid');
  1904. }
  1905. if (link_name.length > 0) {
  1906. widget_link.name = link_name;
  1907. }
  1908. }
  1909. share_params.widget_link = widget_link;
  1910. }
  1911. WebView.postEvent('web_app_share_to_story', false, share_params);
  1912. };
  1913. WebApp.invokeCustomMethod = function (method, params, callback) {
  1914. invokeCustomMethod(method, params, callback);
  1915. };
  1916. WebApp.ready = function () {
  1917. WebView.postEvent('web_app_ready');
  1918. };
  1919. WebApp.expand = function () {
  1920. WebView.postEvent('web_app_expand');
  1921. };
  1922. WebApp.close = function (options) {
  1923. options = options || {};
  1924. var req_params = {};
  1925. if (versionAtLeast('7.6') && options.return_back) {
  1926. req_params.return_back = true;
  1927. }
  1928. WebView.postEvent('web_app_close', false, req_params);
  1929. };
  1930. window.Telegram.WebApp = WebApp;
  1931. updateHeaderColor();
  1932. updateBackgroundColor();
  1933. setViewportHeight();
  1934. if (initParams.tgWebAppShowSettings) {
  1935. SettingsButton.show();
  1936. }
  1937. window.addEventListener('resize', onWindowResize);
  1938. if (isIframe) {
  1939. document.addEventListener('click', linkHandler);
  1940. }
  1941. WebView.onEvent('theme_changed', onThemeChanged);
  1942. WebView.onEvent('viewport_changed', onViewportChanged);
  1943. WebView.onEvent('invoice_closed', onInvoiceClosed);
  1944. WebView.onEvent('popup_closed', onPopupClosed);
  1945. WebView.onEvent('qr_text_received', onQrTextReceived);
  1946. WebView.onEvent('scan_qr_popup_closed', onScanQrPopupClosed);
  1947. WebView.onEvent('clipboard_text_received', onClipboardTextReceived);
  1948. WebView.onEvent('write_access_requested', onWriteAccessRequested);
  1949. WebView.onEvent('phone_requested', onPhoneRequested);
  1950. WebView.onEvent('custom_method_invoked', onCustomMethodInvoked);
  1951. WebView.postEvent('web_app_request_theme');
  1952. WebView.postEvent('web_app_request_viewport');
  1953. })();