react-native/local-cli/server/util/debugger.html

258 lines
145 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<!--
Copyright (c) 2015-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
-->
<html>
<head>
<meta charset=utf-8>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<title>React Native Debugger</title>
2015-08-21 17:15:04 +00:00
<script>
/* eslint-env browser */
'use strict';
(function() {
const isMacLike = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
const refreshShortcut = isMacLike ? '⌘R' : 'Ctrl R';
window.onload = function() {
if (!isMacLike) {
document.getElementById('shortcut').innerHTML = 'Ctrl⇧J';
}
Page.render();
};
const Assets = {
blueIcon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAABAJJREFUWMPNV0tIlFEU/nsaPSCrhVFZBBFRFkXvKCpCw6BFtagkWoS1CApp0YMKclHRYxNpj0UUQWUavYmeEi2MsFVS/s7oqOPo6DjqzKjpzOjpnDl35s7MP/8/vyUxFy7c/5x7z/nued5fUcwOgLGKBXIUFYqUaijHqeL0iamGaMSjPbR32IYNMpQaKEbhHlQCpibtpTN09q+HBdJQSCEK7DatWDu7QzJI1pBvrULFPyiOt0iFeWtUw2I8YB825RKEXamFLDM31yifqAIcbwO40QmQ3wIwXtUqmFwDcNQJcKeL96apOiB0LUF+0jH7u26AwUEA3wCERmsAYF+z5B9CUF1B5rmDvPeBx8AdCWOCA05zYEEdCz6JtxqB3xsbACp/M+0WWuR+F6+/9ACsqOczV90AQQSRYdEBQbo0pteJ9p1NrCAsnOYYnNc7IDIuocJRUWdyGpm+qcEgO2JcQTmrEzxkahpZdbH0aABX3LG89Q1M3243CErSGalwBkVmi7gN3SpM2+uQLrgrXLCjSfJ3Cautqk9SrEIVk0qnQfrMtrKwtxiIj70A33vZvwGcA2IGxKxE3j0E9MLHZ6ZZkqQm6Ra1XcOcjofPtwO4AtLULbhuD7DSpwjmspvNTwop8tuQ5wnK/U1+gNMYvFNqdK1QpIjGEsPY7eC0IqFvUPgrHys90MKCz7p43xy0TqaV1xSINPaLmHmNZz73yLTdljgeyhXRySLEXDsr/tYrA29tPQtqxhtZ+zkLnnjlTcns41S+sd3PtCU2Prsaz1b1AfgHOTjjAKiKaKcR4kf0dS0qmRBXyYhGg4rOLCtoBvm7oJXXP/u0VZLc98yrAeDTAHiPAGx+LQCLAJCPJp6ZAMBUVFLg5PWvOADpyHMG2GpxALwaF1C6kb+/ogvm1zJtjXABCSEgo5H20COV3+5kF5D5G4ULVooUpAL2AwH1o8x1Oi7QBOEeB0czpVYpoi71cFwcFEF4RgThDCtnC60vtDMvz8F7X/rYmrQ2DkKdNCQ/X+uQDYgE1fSzMLJQiYeVXsRZ5mUaWSg6bem70GVQD0JpmKQQzauVzYZSsrqPlVExCo+g+K7CJvUIgT0XGTLXaqYQJSnF2aIUZ0eV4jxRim+ihYpETyC3hflb7Uzb3GimFCdpRuHCsiiuGRVHNSPqCdG8ZTYtKP1mlKQdhxvLcpuk0bvgnEsCoDgYGXVmg+iGuXaz7djgQbJQPEhOtPE3AfkkSizldYlIx3KkLbXJjKCgzbSafZAkeZJ96ObAc4tG48XMOOKU/MNOmS3UkEh5mXeoTzKDR+kklbsaPTjp4ZmeoLtRxzvWyn3hVBsXpqE9SlPiWZ4SPyYp8WuWEj+n/+n3/A8emj1HwPlGOgAAAABJRU5ErkJggg==',
grayIcon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAA9VJREFUWMPNV+dKJEEQnl93r3Avcfcavpe/zHlXzDmLYlZU1oSrYEDFsGZdVDBvUBTUuv6K6aZ34ioiW1BMT1dPdXWFr3oMI03q7u7+lZOTk5WbmxsQz5DgiOCEyRiHTFkW1hrfRXl5eX+E0qDgmGBKk7E2iG+/vHFZWdlvcaJswclPbJzC+BY6oOvTpxYfhr+6sYMh4bS9kZ+f/098EP2uzTUjoPNvOie3bV5YWEihUIjW1tZobGyMCgoKbBsUFxfT5OQkbWxs8FpxEEcjXD1hxtzR7cfHx/Tx8UGvr68ESiaTNDQ0pOTj4+P08vLCsufnZ167vb3tGg7HnECyOH1QU1PDimdmZvi9ra2Nrq6ueG59fZ22trZ4HI1GqbGxkdcsLy/T+/s7lZeXuxmR7eR6x2zv6+vjDaRysFhPq6urJGlpaQlKlbyrq4vn29vbXasjJRRmnTsuhqtBdXV1KfO6ATixLmttbeX53t5er8QM6gjnCjIdHR2sDKeSc4ODgyoEm5ubPIanpLy/v5/nmpqaPMGKERPQ6VU+wWCQlSERd3d3Of6ILxjJBpbvl5eXnBP7+/v8TWlpqV95ZhkmftuESKDFxUV6enpSrk4kEirLI5EIxx7ux4aYw1pZDaB4PE6zs7NUUlLilgsBw2wsKYKBgQFWBKVHR0d0cHDA45GREVY8NzenvAPGGMaAhoeH+Xl4eEjn5+eqbHt6epyMCBlmJ1OTIi68GdwpE6+lpUWd6P7+nqtgb29PnRRuB/BADtaTtrm5ma6vr+nt7Y2T02JAxDDbqZo8PT2lh4cHRj99HnMgIGEgECArId5TU1M8vrm5saEkwodQWQxI2Aw4OTmhx8dHmwE4OWh0dNTRAMRZGnB7e2szAGGA1yw5ELeFAOWGEFxcXFB1dbVyo4wlDAHoAGoloRwRglgsxqyXIABMhgChdAqBaxKitFB6YBgF9+tJWFFRoeA2HA6zDBiBtUhceBNjzyR0K0O4eWVlRTUgKLq7u2NlGO/s7PCmYGkgZHrZ4n1hYcEVD2QZegJRVVUVK0NJoSSRYBJ8JEkggqthmASiyspKfyDyg+LOzk5WhqcVinE/kD0BYUu3GaVAsV8zksBSW1ubMo/N9STUZQ0NDTajXJuRXzuWjUVvx+D5+XllAPLAqRsC1NJqx14XEpxcv5DAEIAVCHWNmIPOzs6ovr5eVQTyRMK074XE70omywmNCIQSxf1PyicmJlS1oAqw1go6vlcyr0tpUVERdzVcOLExkM26Bkg4PT3NfQHe+vSlNCOu5RnxY5IRv2YZ8XP6U7/n/wFyU6HBPUWxjAAAAABJRU5ErkJggg==',
orangeIcon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAABGZJREFUWMPNV+tvFFUU30/4L/i9K0gQImoiQTQmGtEQQAWCoSGgJiZqojZCYsoHQkNIwAcJppWgHwjqLi1SHrVP2m4tfdAULKUW2NLSx0ILbGsf293ZeR7OOXc72+nM7GwbQjrJze7eufeex+/3O+euz5flAyUrliSL/esTAX9hIugPJYI5YfyMicHfQ/SO1tBa35N68LBnpaC/KB7wT8YDOZDd8E/SHtq7cMMV/mfiwZwCjGo6e8PWwXvxDDpr3lFjSlsXatjmCJ6VdTbkwLJV6HnkSRmflY2IXPL8Su/InYyXrAS54zAoPX9Csi0f4sUr7EbOvAjy1QJQ+87w2vjp5Y5OuGaCcHJLuzZ8GQzDAEOZBnoMaRSSLd+a7+W2fWDIU+JdcpzXqv0X3eFw4gSRxXHD3+/wwfL17/m3VLsD9LEunlPuBEG9W8rftUftIFW9z2uUW7+BoWsQL33VGRK05ZR6R7YnG79gAzOHiwOWgRI+BTOPcvMEzj1nvpfqd4s9tbmu6rBAIXTuTB5KNT2J8vcs81YHfrW8ky5t5/nkP5+5kpJsmhUuU5GR6naKaDAq06nmb3hOJQj6zgpjjZ+n31/+Uuyp/jBjseKKSaUzo3wuvC6MDTeCOlgO2mgXkkxDjFX81MXQNR7a6A3kxFlQI7W8J/7XKxmlSbZ9VL8dF5SuAeW/ImT1mJlqI/EIf//PRtVIDWNP6Vfv1TLzdWkMlTJlrtfjIyB3/oiOvOTGhUIfNRGbZ01fsazoUO1+AxqoZ6PJK98JRXQeTWXnDUicXyc4gc4wFC17Rcbuh0B72GbKVmr41EmSIR91MgvmoU+E4dFOk3hSzbZURA9BnxpgFahDVWaklHYqPBQxDSZtxQaxt3oL6ONhhEhhclodyAlTBmKWojPSAnpsEKvdC5bFNMcRYtGhqOc+hLd87aCoCRN3bFWS4CPY5mQg5uBAExqL2BzQMHLhQD4kzr3m4MDLpgP6ZO8cB1ajA1HO2hwOTNkhQLkR3lq0AxJlb6fSuDVFwihC0I8QLAV1oCwNAcpRQDDMQxSuD8ReLGAMgSYzlE4Q2EnY/DWzmaSmDlbwIF5Q9IKEP4kDzq1ltTAJu38RhpvzRC+4V8fZ5B6SkYQuMiScldsn0w2IZIbRG1KUM6QMlLNRpfs4KEOVPKdjhqyyjYLS9bNrPWAZehaii2+ZzYYkqU/0mgXINESFCYc+3sPQqJFLQgkX3vQuRJ6luH5XqhTvmgVRnugBPQEcvwtyImxpKX8s9tTlepdiz2bUuifVjN61NqOeP6wknJ21yk02p1ybkWc7nmksVZutDtw4lu6GyAOMaFY3/EjswaKWVTvOeCHByJn5HUdSstoM2oMWETnqWkUyMkcetGLkG01FEGlnyrTnhcTzSjbSLIiH1y0mnRLD+9+B9JWsfb/lusYyRGXM60qW+VK6irsaXzjx4kmVzbYGO5787yHuC/L1H+Z/KV0U1/JF8cdkUfw1WxR/Tp/W3/PHSyH/l51SdaoAAAAASUVORK5CYII=',
};
const Page = window.Page = {
state: {
isDark: localStorage.getItem('darkTheme') === 'on',
isPriorityMaintained: localStorage.getItem('maintainPriority') === 'on',
status: {type: 'disconnected'},
visibilityState: document.visibilityState,
},
setState(partialState) {
Page.state = Object.assign({}, Page.state, partialState);
Page.render();
},
render() {
const {
isDark,
isPriorityMaintained,
status,
visibilityState,
} = Page.state;
const statusNode = document.getElementById('status');
switch (status.type) {
case 'connected':
statusNode.innerHTML = 'Debugger session #' + status.id + ' active.';
break;
case 'error':
statusNode.innerHTML = status.error.reason || 'Disconnected from proxy. Attempting reconnection. Is node server running?';
break;
case 'connecting':
case 'disconnected':
// Fall through.
default:
statusNode.innerHTML = 'Waiting, press <span class="shortcut">' + refreshShortcut + '</span> in simulator to reload and connect.';
break;
}
const linkNode = document.querySelector('link[rel=icon]');
if (status.type === 'disconnected' ||
status.type === 'error') {
linkNode.href = Assets.grayIcon;
} else {
if (visibilityState === 'visible' || isPriorityMaintained) {
linkNode.href = Assets.blueIcon;
} else {
linkNode.href = Assets.orangeIcon;
}
}
const darkCheckbox = document.getElementById('dark');
document.body.classList.toggle('dark', isDark);
darkCheckbox.checked = isDark;
localStorage.setItem('darkTheme', isDark ? 'on' : '');
const maintainPriorityCheckbox = document.getElementById('maintain-priority');
const silence = document.getElementById('silence');
silence.volume = 0.001;
if (isPriorityMaintained) {
silence.play();
} else {
silence.pause();
}
maintainPriorityCheckbox.checked = isPriorityMaintained;
localStorage.setItem('maintainPriority', isPriorityMaintained ? 'on' : '');
},
toggleDarkTheme() {
Page.setState({isDark: !Page.state.isDark});
},
togglePriorityMaintenance() {
Page.setState({isPriorityMaintained: !Page.state.isPriorityMaintained});
},
};
function connectToDebuggerProxy() {
const ws = new WebSocket('ws://' + window.location.host + '/debugger-proxy?role=debugger&name=Chrome');
let worker;
function createJSRuntime() {
// This worker will run the application JavaScript code,
// making sure that it's run in an environment without a global
// document, to make it consistent with the JSC executor environment.
worker = new Worker('debuggerWorker.js');
worker.onmessage = function(message) {
ws.send(JSON.stringify(message.data));
};
window.onbeforeunload = function() {
return 'If you reload this page, it is going to break the debugging session. ' +
'You should press' + refreshShortcut + 'in simulator to reload.';
};
updateVisibility();
}
function shutdownJSRuntime() {
if (worker) {
worker.terminate();
worker = null;
window.onbeforeunload = null;
}
}
function updateVisibility() {
if (worker && !Page.state.isPriorityMaintained) {
worker.postMessage({
method: 'setDebuggerVisibility',
visibilityState: document.visibilityState,
});
}
Page.setState({visibilityState: document.visibilityState});
}
ws.onopen = function() {
Page.setState({status: {type: 'connecting'}});
};
ws.onmessage = function(message) {
if (!message.data) {
return;
}
const object = JSON.parse(message.data);
if (object.$event === 'client-disconnected') {
shutdownJSRuntime();
Page.setState({status: {type: 'disconnected'}});
return;
}
if (!object.method) {
return;
}
// Special message that asks for a new JS runtime
if (object.method === 'prepareJSRuntime') {
shutdownJSRuntime();
console.clear();
createJSRuntime();
ws.send(JSON.stringify({replyID: object.id}));
Page.setState({status: {type: 'connected', id: object.id}});
} else if (object.method === '$disconnected') {
shutdownJSRuntime();
Page.setState({status: {type: 'disconnected'}});
} else {
// Otherwise, pass through to the worker.
worker.postMessage(object);
}
};
ws.onclose = function(error) {
shutdownJSRuntime();
Page.setState({status: {type: 'error', error}});
if (error.reason) {
console.warn(error.reason);
}
setTimeout(connectToDebuggerProxy, 500);
};
// Let debuggerWorker.js know when we're not visible so that we can warn about
// poor performance when using remote debugging.
document.addEventListener('visibilitychange', updateVisibility, false);
}
connectToDebuggerProxy();
})();
</script>
<style type="text/css">
html,
body {
font-family: Helvetica, Verdana, sans-serif;
font-size: large;
font-weight: 200;
height: 100%;
2015-08-21 17:15:04 +00:00
margin: 0;
padding: 0;
}
.shortcut {
border-radius: 4px;
color: #eee;
background-color: #333;
font-family: "Monaco", monospace;
font-size: medium;
letter-spacing: 3px;
padding: 4px;
}
.content {
padding: 10px;
}
body.dark {
background-color: #242424;
color: #666;
}
.dark .shortcut {
color: #777;
}
.dark a {
color: #3b99fc;
}
input[type=checkbox] {
vertical-align: middle;
}
</style>
</head>
<body>
<div class="content">
<label for="dark">
<input type="checkbox" id="dark" onclick="Page.toggleDarkTheme()"> Dark Theme
</label>
<label for="maintain-priority">
<input type="checkbox" id="maintain-priority" onclick="Page.togglePriorityMaintenance()"> Maintain Priority
</label>
<p>
React Native JS code runs as a web worker inside this tab.
</p>
<p>Press <kbd id="shortcut" class="shortcut">⌘⌥J</kbd> to open Developer Tools. Enable <a href="https://stackoverflow.com/a/17324511/232122" target="_blank">Pause On Caught Exceptions</a> for a better debugging experience.</p>
<p>You may also install <a href="https://github.com/facebook/react-devtools/tree/master/packages/react-devtools" target="_blank">the standalone version of React Developer Tools</a> to inspect the React component hierarchy, their props, and state.</p>
2015-08-21 17:15:04 +00:00
<p>Status: <span id="status">Loading...</span></p>
</div>
close `<audio>` tag. Some debugger (ex: remote-redux-devtools-on-debu… Summary: [remote-redux-devtools-on-debugger](https://github.com/jhen0409/remote-redux-devtools-on-debugger) is a redux debugger UI for react-native. It inject code to debugger.html to communicate to the [Remote Redux DevTools](https://github.com/zalmoxisus/remote-redux-devtools). But the injecting failed for react-native 0.44 because the injected code will be wrapped in the audio tag. Here's the screenshot: ![2017-05-05 16 12 50](https://cloud.githubusercontent.com/assets/78242/25739567/02d7824c-31b5-11e7-99d3-36b17effaaa3.png) The pull request close the `<audio>` tag to make sure the injecting code not wrap inside `<audio>` tag ![2017-05-05 17 13 29](https://cloud.githubusercontent.com/assets/78242/25739835/317bc698-31b6-11e7-8aab-c2ecabf1eccb.png) `$ npm install --save-dev remote-redux-devtools-on-debugger` `$ ./node_modules/.bin/remotedev-debugger --hostname localhost --port 5678 --injectserver` $ open debugger.html in the browser, open the chrome dev tool, make sure the injected code not wrapped in the audio tag. If you have added code that should be tested, add tests. Sign the [CLA][2], if you haven't already. Small pull requests are much easier to review and more likely to get merged. Make sure the PR does only one thing, otherwise please split it. Make sure all **tests pass** on both [Travis][3] and [Circle CI][4]. PRs that break tests are unlikely to be merged. For more info, see the ["Pull Requests"][5] section of our "Contributing" guidelines. [1]: https://medium.com/martinkonicek/what-is-a-test-plan-8bfc840ec171#.y9lcuqqi9 [2]: https://code.facebook.com/cla [3]: https://travis-ci.org/facebook/react-native [4]: http://circleci.com/gh/facebook/react-native [5]: https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md#pull-requests Closes https://github.com/facebook/react-native/pull/13798 Differential Revision: D5154006 Pulled By: shergin fbshipit-source-id: bd5f1774108649774b33ff2bf38d1aee3a5c50a9
2017-05-31 09:38:14 +00:00
<audio id="silence" loop src="data:audio/wav;base64,UklGRjKQAQBXQVZFZm10IBIAAAABAAEAIlYAAESsAAACABAAAABkYXRhAHsBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</body>
</html>