telegram-crawler/data/web/blogfork.telegram.org/api/bots/webapps.html

244 lines
24 KiB
HTML
Raw Normal View History

2022-11-15 01:03:58 +01:00
<!DOCTYPE html>
<html class="">
<head>
<meta charset="utf-8">
<title>Bot web apps</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="description" content="Bots can offer users interactive HTML5 web apps to completely replace any website.">
<meta property="og:title" content="Bot web apps">
<meta property="og:image" content="">
<meta property="og:description" content="Bots can offer users interactive HTML5 web apps to completely replace any website.">
<link rel="icon" type="image/svg+xml" href="/img/website_icon.svg?4">
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png">
<link rel="alternate icon" href="/img/favicon.ico" type="image/x-icon" />
<link href="/css/bootstrap.min.css?3" rel="stylesheet">
2022-11-21 13:10:26 +01:00
<link href="/css/telegram.css?233" rel="stylesheet" media="screen">
2022-11-15 01:03:58 +01:00
<style>
</style>
</head>
<body class="preload">
<div class="dev_page_wrap">
<div class="dev_page_head navbar navbar-static-top navbar-tg">
<div class="navbar-inner">
<div class="container clearfix">
<ul class="nav navbar-nav navbar-right hidden-xs"><li class="navbar-twitter"><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)"><i class="icon icon-twitter"></i><span> Twitter</span></a></li></ul>
<ul class="nav navbar-nav">
<li><a href="//telegram.org/">Home</a></li>
<li class="hidden-xs"><a href="//telegram.org/faq">FAQ</a></li>
<li class="hidden-xs"><a href="//telegram.org/apps">Apps</a></li>
<li class="active"><a href="/api">API</a></li>
<li class=""><a href="/mtproto">Protocol</a></li>
<li class=""><a href="/schema">Schema</a></li>
</ul>
</div>
</div>
</div>
<div class="container clearfix">
<div class="dev_page">
<div id="dev_page_content_wrap" class=" ">
<div class="dev_page_bread_crumbs"><ul class="breadcrumb clearfix"><li><a href="/api" >API</a></li><i class="icon icon-breadcrumb-divider"></i><li><a href="/api/bots%2Fwebapps" >Bot web apps</a></li></ul></div>
<h1 id="dev_page_title">Bot web apps</h1>
<div id="dev_page_content"><!-- scroll_nav -->
<p>Bots can offer users interactive <a href="/bots/webapps">HTML5 web apps</a> to completely replace <strong>any website</strong>. </p>
<p>They support <a href="https://telegram.org/blog/privacy-discussions-web-bots#meet-seamless-web-bots">seamless authorization</a>, <a href="https://core.telegram.org/bots/payments">integrated payments</a> via multiple payment providers (with <em>Google Pay</em> and <em>Apple Pay</em> out of the box), delivering tailored push notifications to users, and <a href="https://core.telegram.org/bots">much more</a>.</p>
<p>This article offers a client-side overview of the implementation of bot web apps using the MTProto API: see <a href="/bots/webapps">here for an overview of the web-app side JS API »</a>. </p>
<h3><a class="anchor" href="#outgoing-events-web-app-to-client" id="outgoing-events-web-app-to-client" name="outgoing-events-web-app-to-client"><i class="anchor-icon"></i></a>Outgoing events: Web app to client</h3>
<p>Both <a href="#simple-web-apps">simple</a> and <a href="#normal-web-apps">normal</a> web apps can <em>send</em> web events starting with <code>web_app_</code>; see the <a href="/api/web-events">web event documentation for the full list of events that can be <em>sent</em> by the web app to the client »</a>. </p>
<h3><a class="anchor" href="#incoming-events-client-to-web-app" id="incoming-events-client-to-web-app" name="incoming-events-client-to-web-app"><i class="anchor-icon"></i></a>Incoming events: Client to web app</h3>
<p>Web apps can also <em>receive</em> events, by exposing a <code>window.Telegram.WebView.receiveEvent("event_name", params)</code> method. </p>
<p>Here's the full list of events that can be <em>received</em> by a web app from the client, by calling the <code>receiveEvent</code> method. </p>
<h4><a class="anchor" href="#main-button-pressed" id="main-button-pressed" name="main-button-pressed"><i class="anchor-icon"></i></a><code>main_button_pressed</code></h4>
<p>Params: <code>null</code></p>
<p>Sent by the client when the user presses the main button located at the bottom of the webview, handle this event only if the main button was <a href="/api/web-events#web-app-setup-main-button">previously configured by a <code>web_app_setup_main_button</code> event »</a>.</p>
<h4><a class="anchor" href="#settings-button-pressed" id="settings-button-pressed" name="settings-button-pressed"><i class="anchor-icon"></i></a><code>settings_button_pressed</code></h4>
<p>Params: <code>null</code></p>
<p>Sent by the client when the user presses the settings button, if it was previously enabled in <a href="https://t.me/BotFather">@BotFather</a>, as specified by the <code>has_settings</code> flag of <a href="/constructor/attachMenuBot">attachMenuBot »</a>. </p>
<h4><a class="anchor" href="#back-button-pressed" id="back-button-pressed" name="back-button-pressed"><i class="anchor-icon"></i></a><code>back_button_pressed</code></h4>
<p>Params: <code>null</code></p>
<p>Sent by the client when the user presses the (OS or UI) back button, if it was <a href="/api/web-events#web-app-setup-back-button">previously enabled by a <code>web_app_setup_back_button</code> event »</a>.</p>
<h4><a class="anchor" href="#invoice-closed" id="invoice-closed" name="invoice-closed"><i class="anchor-icon"></i></a><code>invoice_closed</code></h4>
<p>Params: JSON object with the following fields:</p>
<ul>
<li><code>slug</code> - Invoice identifier (string)</li>
<li><code>status</code> - One of the following values (string):<ul>
<li><code>cancelled</code> The user closed the invoice popup without paying, before the call to <a href="/method/payments.sendPaymentForm">payments.sendPaymentForm</a>.</li>
<li><code>failed</code> The user tried to pay, but the payment failed: the call to <a href="/method/payments.sendPaymentForm">payments.sendPaymentForm</a> returned an RPC error and the popup was closed.</li>
<li><code>pending</code> The payment is still processing: the bot will receive a further service message about a successful payment. <a href="/method/payments.sendPaymentForm">payments.sendPaymentForm</a> was successfully invoked returning <a href="/constructor/payments.paymentVerificationNeeded">payments.paymentVerificationNeeded</a>, the user completed all additional verification forms returned by the method and the invoice popup was closed, but the client hasn't received a <a href="/constructor/messageActionPaymentSent">messageActionPaymentSent</a> service message yet.<br>
Note that eventual errors will not be sent as a <code>failed</code> event if the user fails additional validation (ie 3-D Secure) returned by <a href="/constructor/payments.paymentVerificationNeeded">payments.paymentVerificationNeeded</a>: the state will remaing <code>pending</code>. </li>
<li><code>paid</code> The invoice was paid successfully: the client completed the <a href="/api/payments">payment flow »</a>, the invoice popup was closed and a <a href="/constructor/messageActionPaymentSent">messageActionPaymentSent</a> service message was received by the client.</li>
</ul>
</li>
</ul>
<p>Sent by the client to report the <a href="/api/payments">payment status</a> of an invoice obtained from a <a href="/api/web-events#web-app-open-invoice"><code>web_app_open_invoice</code> event »</a>. </p>
<h4><a class="anchor" href="#viewport-changed" id="viewport-changed" name="viewport-changed"><i class="anchor-icon"></i></a><code>viewport_changed</code></h4>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>height</code> - The current height of the visible area of the Web App (excluding the bottom <a href="#main-button-pressed">main button</a>, if visible) (integer)</li>
<li><code>is_state_stable</code> - If true, the viewport is currently being resized (animation in progress), more events of this type may be emitted. (boolean)</li>
<li><code>is_expanded</code> - Whether the Web App is expanded to its maximum height after the user swiped up or after the web app emitted a <a href="/api/web-events#web-app-expand">web_app_expand</a> event (boolean)</li>
</ul>
<p>Emitted when the viewport is changed. </p>
<h4><a class="anchor" href="#theme-changed" id="theme-changed" name="theme-changed"><i class="anchor-icon"></i></a><code>theme_changed</code></h4>
<p>Params: a JSON object with the following fields:</p>
<ul>
<li><code>theme_params</code>: A <a href="#theme-parameters">theme parameters object »</a> (object)</li>
</ul>
<p>Emitted when requested by the web app using a <a href="/api/web-events#web-app-request-theme"><code>web_app_request_theme</code> event »</a>, or when the app theme changes. </p>
<h4><a class="anchor" href="#popup-closed" id="popup-closed" name="popup-closed"><i class="anchor-icon"></i></a><code>popup_closed</code></h4>
<p>Params: a JSON object with an optional <code>button_id</code> string field.</p>
<p>Emitted when the user presses a button or cancels a popup brought up by a previous <a href="/api/web-events#web-app-open-popup"><code>web_app_open_popup</code> event »</a>. </p>
<h3><a class="anchor" href="#simple-web-apps" id="simple-web-apps" name="simple-web-apps"><i class="anchor-icon"></i></a>Simple web apps</h3>
<p>Schema:</p>
<pre><code><a href='/constructor/replyKeyboardMarkup'>replyKeyboardMarkup</a>#85dd99d1 flags:<a href='/type/%23'>#</a> resize:flags.0?<a href='/constructor/true'>true</a> single_use:flags.1?<a href='/constructor/true'>true</a> selective:flags.2?<a href='/constructor/true'>true</a> rows:<a href='/type/Vector%20t'>Vector</a>&lt;<a href='/type/KeyboardButtonRow'>KeyboardButtonRow</a>&gt; placeholder:flags.3?<a href='/type/string'>string</a> = <a href='/type/ReplyMarkup'>ReplyMarkup</a>;
<a href='/constructor/keyboardButtonSimpleWebView'>keyboardButtonSimpleWebView</a>#a0c0505c text:<a href='/type/string'>string</a> url:<a href='/type/string'>string</a> = <a href='/type/KeyboardButton'>KeyboardButton</a>;
<a href='/constructor/messageActionWebViewDataSentMe'>messageActionWebViewDataSentMe</a>#47dd8079 text:<a href='/type/string'>string</a> data:<a href='/type/string'>string</a> = <a href='/type/MessageAction'>MessageAction</a>;
<a href='/constructor/messageActionWebViewDataSent'>messageActionWebViewDataSent</a>#b4c38cb5 text:<a href='/type/string'>string</a> = <a href='/type/MessageAction'>MessageAction</a>;
<a href='/constructor/simpleWebViewResultUrl'>simpleWebViewResultUrl</a>#882f76bb url:<a href='/type/string'>string</a> = <a href='/type/SimpleWebViewResult'>SimpleWebViewResult</a>;
---functions---
<a href='/method/messages.requestSimpleWebView'>messages.requestSimpleWebView</a>#299bec8e flags:<a href='/type/%23'>#</a> bot:<a href='/type/InputUser'>InputUser</a> url:<a href='/type/string'>string</a> theme_params:flags.0?<a href='/type/DataJSON'>DataJSON</a> platform:<a href='/type/string'>string</a> = <a href='/type/SimpleWebViewResult'>SimpleWebViewResult</a>;
<a href='/method/messages.sendWebViewData'>messages.sendWebViewData</a>#dc0242c8 bot:<a href='/type/InputUser'>InputUser</a> random_id:<a href='/type/long'>long</a> button_text:<a href='/type/string'>string</a> data:<a href='/type/string'>string</a> = <a href='/type/Updates'>Updates</a>;</code></pre>
<p>Simple webapps can only send back data to the bot through the MTProto API via a <a href="/api/web-events#web-app-data-send"><code>web_app_data_send</code> JS event »</a>. </p>
<p>Simple webapps can only be opened from a <a href="/constructor/keyboardButtonSimpleWebView">keyboardButtonSimpleWebView</a> button contained in a reply keyboard identified by a <a href="/constructor/replyKeyboardMarkup">replyKeyboardMarkup</a> constructor. </p>
<p>To open them, users should call <a href="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a>, and then open a webview using the <code>url</code> contained in the returned <a href="/constructor/simpleWebViewResultUrl">simpleWebViewResultUrl</a>. </p>
<p>Upon receiving a <a href="/api/web-events#web-app-data-send"><code>web_app_data_send</code> JS event »</a> from the web app, clients should invoke <a href="/method/messages.sendWebViewData">messages.sendWebViewData</a>, passing the following arguments:</p>
<ul>
<li><code>bot</code> - Bot ID</li>
<li><code>random_id</code> - Unique random ID to avoid resending the same event multiple times</li>
<li><code>button_text</code> - Text of the <a href="/constructor/keyboardButtonSimpleWebView">keyboardButtonSimpleWebView</a> that was pressed to open the simple web app</li>
<li><code>data</code> - Contents of the <code>data</code> field of the JS event. </li>
</ul>
<p>Make sure to ignore all <code>web_app_data_send</code> events sent after the first one, <a href="/method/messages.sendWebViewData">messages.sendWebViewData</a> must be called only once.<br>
The webview must be closed after invoking the <a href="/method/messages.sendWebViewData">messages.sendWebViewData</a> method. </p>
<p>This will generate a <a href="/constructor/messageActionWebViewDataSent">messageActionWebViewDataSent</a> update for the user, and a <a href="/constructor/messageActionWebViewDataSentMe">messageActionWebViewDataSentMe</a> update for the bot, containing the event data. </p>
<h3><a class="anchor" href="#normal-web-apps" id="normal-web-apps" name="normal-web-apps"><i class="anchor-icon"></i></a>Normal web apps</h3>
<p>Schema:</p>
<pre><code><a href='/constructor/keyboardButtonWebView'>keyboardButtonWebView</a>#13767230 text:<a href='/type/string'>string</a> url:<a href='/type/string'>string</a> = <a href='/type/KeyboardButton'>KeyboardButton</a>;
<a href='/constructor/botMenuButton'>botMenuButton</a>#c7b57ce6 text:<a href='/type/string'>string</a> url:<a href='/type/string'>string</a> = <a href='/type/BotMenuButton'>BotMenuButton</a>;
<a href='/constructor/webViewResultUrl'>webViewResultUrl</a>#c14557c query_id:<a href='/type/long'>long</a> url:<a href='/type/string'>string</a> = <a href='/type/WebViewResult'>WebViewResult</a>;
<a href='/constructor/inputBotInlineResult'>inputBotInlineResult</a>#88bf9319 flags:<a href='/type/%23'>#</a> id:<a href='/type/string'>string</a> type:<a href='/type/string'>string</a> title:flags.1?<a href='/type/string'>string</a> description:flags.2?<a href='/type/string'>string</a> url:flags.3?<a href='/type/string'>string</a> thumb:flags.4?<a href='/type/InputWebDocument'>InputWebDocument</a> content:flags.5?<a href='/type/InputWebDocument'>InputWebDocument</a> send_message:<a href='/type/InputBotInlineMessage'>InputBotInlineMessage</a> = <a href='/type/InputBotInlineResult'>InputBotInlineResult</a>;
<a href='/constructor/inputBotInlineResultPhoto'>inputBotInlineResultPhoto</a>#a8d864a7 id:<a href='/type/string'>string</a> type:<a href='/type/string'>string</a> photo:<a href='/type/InputPhoto'>InputPhoto</a> send_message:<a href='/type/InputBotInlineMessage'>InputBotInlineMessage</a> = <a href='/type/InputBotInlineResult'>InputBotInlineResult</a>;
<a href='/constructor/inputBotInlineResultDocument'>inputBotInlineResultDocument</a>#fff8fdc4 flags:<a href='/type/%23'>#</a> id:<a href='/type/string'>string</a> type:<a href='/type/string'>string</a> title:flags.1?<a href='/type/string'>string</a> description:flags.2?<a href='/type/string'>string</a> document:<a href='/type/InputDocument'>InputDocument</a> send_message:<a href='/type/InputBotInlineMessage'>InputBotInlineMessage</a> = <a href='/type/InputBotInlineResult'>InputBotInlineResult</a>;
<a href='/constructor/inputBotInlineResultGame'>inputBotInlineResultGame</a>#4fa417f2 id:<a href='/type/string'>string</a> short_name:<a href='/type/string'>string</a> send_message:<a href='/type/InputBotInlineMessage'>InputBotInlineMessage</a> = <a href='/type/InputBotInlineResult'>InputBotInlineResult</a>;
<a href='/constructor/updateWebViewResultSent'>updateWebViewResultSent</a>#1592b79d query_id:<a href='/type/long'>long</a> = <a href='/type/Update'>Update</a>;
<a href='/constructor/webViewMessageSent'>webViewMessageSent</a>#c94511c flags:<a href='/type/%23'>#</a> msg_id:flags.0?<a href='/type/InputBotInlineMessageID'>InputBotInlineMessageID</a> = <a href='/type/WebViewMessageSent'>WebViewMessageSent</a>;
---functions---
<a href='/method/messages.requestWebView'>messages.requestWebView</a>#fc87a53c flags:<a href='/type/%23'>#</a> from_bot_menu:flags.4?<a href='/constructor/true'>true</a> silent:flags.5?<a href='/constructor/true'>true</a> peer:<a href='/type/InputPeer'>InputPeer</a> bot:<a href='/type/InputUser'>InputUser</a> url:flags.1?<a href='/type/string'>string</a> start_param:flags.3?<a href='/type/string'>string</a> theme_params:flags.2?<a href='/type/DataJSON'>DataJSON</a> platform:<a href='/type/string'>string</a> reply_to_msg_id:flags.0?<a href='/type/int'>int</a> send_as:flags.13?<a href='/type/InputPeer'>InputPeer</a> = <a href='/type/WebViewResult'>WebViewResult</a>;
<a href='/method/messages.prolongWebView'>messages.prolongWebView</a>#ea5fbcce flags:<a href='/type/%23'>#</a> silent:flags.5?<a href='/constructor/true'>true</a> peer:<a href='/type/InputPeer'>InputPeer</a> bot:<a href='/type/InputUser'>InputUser</a> query_id:<a href='/type/long'>long</a> reply_to_msg_id:flags.0?<a href='/type/int'>int</a> send_as:flags.13?<a href='/type/InputPeer'>InputPeer</a> = <a href='/type/Bool'>Bool</a>;
<a href='/method/messages.sendWebViewResultMessage'>messages.sendWebViewResultMessage</a>#a4314f5 bot_query_id:<a href='/type/string'>string</a> result:<a href='/type/InputBotInlineResult'>InputBotInlineResult</a> = <a href='/type/WebViewMessageSent'>WebViewMessageSent</a>;</code></pre>
<p>Normal webapps work similarly to <a href="/api/bots/inline">inline bots »</a>: they send messages on behalf of the user to the chat from which the query originated.</p>
<p>Normal webapps can be opened from:</p>
<ul>
<li>A <a href="/constructor/keyboardButtonWebView">keyboardButtonWebView</a> button contained in an inline keyboard identified by a <a href="/constructor/replyInlineMarkup">replyInlineMarkup</a> constructor: in this case, <a href="/constructor/keyboardButtonWebView">keyboardButtonWebView</a>.<code>url</code> must be passed to <a href="/method/messages.requestWebView">messages.requestWebView</a>.<code>url</code>.</li>
<li>A <a href="/constructor/botMenuButton">botMenuButton</a> <a href="/api/bots/menu">menu button »</a>: in this case, the <a href="/method/messages.requestWebView">messages.requestWebView</a>.<code>from_bot_menu</code> flag should be set.</li>
<li>An <a href="/api/bots/attach">attachment menu »</a>: in this case, no special flag should be set, unless the attachment menu is opened via a with a <a href="/api/links#bot-attachment-menu-links">bot attachment menu deep link</a>, in which case the <code>start_parameter</code> should be provided to <a href="/method/messages.requestWebView">messages.requestWebView</a>.<code>start_param</code>, if present.</li>
</ul>
<p>To open them, clients should call <a href="/method/messages.requestWebView">messages.requestWebView</a>, and then open a webview using the <code>url</code> contained in the returned <a href="/constructor/webViewResultUrl">webViewResultUrl</a>. </p>
<p>After loading the webview, until it is closed by a <a href="/api/web-events#web-app-close">web_app_close event</a>, the user client must invoke <a href="/method/messages.prolongWebView">messages.prolongWebView</a> every 60 seconds: if the method call returns <code>QUERY_ID_INVALID</code>, the webview must be closed. </p>
<p>The opened URL's fragment parameters already contain basic information about the user and a <code>query_id</code> parameter, that is exposed by the <a href="/bots/webapps">bot web apps JS library</a>: this <code>query_id</code> can then be used <strong>by the bot</strong> to invoke <a href="/method/messages.sendWebViewResultMessage">messages.sendWebViewResultMessage</a>, passing an <a href="/type/InputBotInlineResult">InputBotInlineResult</a> constructor that will automatically send a message with optionally attached media, and even inline buttons on behalf of the user. </p>
<h3><a class="anchor" href="#theme-parameters" id="theme-parameters" name="theme-parameters"><i class="anchor-icon"></i></a>Theme parameters</h3>
<p>Bot web apps can be themed according to the following theme parameters, passed as a JSON object to the <code>theme_params</code> parameter of the <a href="/method/messages.requestSimpleWebView">messages.requestSimpleWebView</a> and <a href="/method/messages.requestWebView">messages.requestWebView</a> methods. </p>
<p>This JSON object has the the following keys, containing color theme information (hex string, RGB, no alpha) to pass to the web app:</p>
<ul>
<li><code>bg_color</code> - Background color</li>
<li><code>secondary_bg_color</code> - Secondary background color</li>
<li><code>text_color</code> - Text color</li>
<li><code>hint_color</code> - Hint text color</li>
<li><code>link_color</code> - Link color</li>
<li><code>button_color</code> - Button color</li>
<li><code>button_text_color</code> - Button text color</li>
</ul></div>
</div>
</div>
</div>
<div class="footer_wrap">
<div class="footer_columns_wrap footer_desktop">
<div class="footer_column footer_column_telegram">
<h5>Telegram</h5>
<div class="footer_telegram_description"></div>
Telegram is a cloud-based mobile and desktop messaging app with a focus on security and speed.
</div>
<div class="footer_column">
<h5><a href="//telegram.org/faq">About</a></h5>
<ul>
<li><a href="//telegram.org/faq">FAQ</a></li>
<li><a href="//telegram.org/privacy">Privacy</a></li>
<li><a href="//telegram.org/press">Press</a></li>
</ul>
</div>
<div class="footer_column">
<h5><a href="//telegram.org/apps#mobile-apps">Mobile Apps</a></h5>
<ul>
<li><a href="//telegram.org/dl/ios">iPhone/iPad</a></li>
<li><a href="//telegram.org/android">Android</a></li>
<li><a href="//telegram.org/dl/web">Mobile Web</a></li>
</ul>
</div>
<div class="footer_column">
<h5><a href="//telegram.org/apps#desktop-apps">Desktop Apps</a></h5>
<ul>
<li><a href="//desktop.telegram.org/">PC/Mac/Linux</a></li>
<li><a href="//macos.telegram.org/">macOS</a></li>
<li><a href="//telegram.org/dl/web">Web-browser</a></li>
</ul>
</div>
<div class="footer_column footer_column_platform">
<h5><a href="/">Platform</a></h5>
<ul>
<li><a href="/api">API</a></li>
<li><a href="//translations.telegram.org/">Translations</a></li>
<li><a href="//instantview.telegram.org/">Instant View</a></li>
</ul>
</div>
</div>
<div class="footer_columns_wrap footer_mobile">
<div class="footer_column">
<h5><a href="//telegram.org/faq">About</a></h5>
</div>
<div class="footer_column">
<h5><a href="//telegram.org/blog">Blog</a></h5>
</div>
<div class="footer_column">
<h5><a href="//telegram.org/apps">Apps</a></h5>
</div>
<div class="footer_column">
<h5><a href="/">Platform</a></h5>
</div>
<div class="footer_column">
<h5><a href="https://twitter.com/telegram" target="_blank" data-track="Follow/Twitter" onclick="trackDlClick(this, event)">Twitter</a></h5>
</div>
</div>
</div>
</div>
<script src="/js/main.js?46"></script>
<script src="/js/jquery.min.js?1"></script>
<script src="/js/bootstrap.min.js?1"></script>
<script>window.initDevPageNav&&initDevPageNav();
backToTopInit("Go up");
removePreloadInit();
</script>
</body>
</html>