sqlite/ext/fiddle/fiddle.in.html

326 lines
12 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>sqlite3 fiddle</title>
<style>
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
textarea {
font-family: monospace;
flex: 1 1 auto;
}
div.emscripten { text-align: center; }
div.emscripten_border { border: 1px solid black; }
.spinner {
height: 50px;
width: 50px;
margin: 0px auto;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
header {
font-size: 130%;
font-weight: bold;
}
#main-wrapper {
display: flex;
flex-direction: column;
}
#main-wrapper.side-by-side {
flex-direction: row;
}
.ta-wrapper{
display: flex;
flex-direction: column;
align-items: stretch;
flex: 1 1 auto;
margin: 0.25em;
}
.button-bar {
display: flex;
justify-content: center;
}
.button-bar button {
margin: 0.25em 1em;
}
label[for] {
cursor: pointer;
}
fieldset {
border-radius: 0.5em;
}
.error {
color: red;
background-color: yellow;
}
.hidden {
position: absolute !important;
opacity: 0 !important;
pointer-events: none !important;
display: none !important;
}
fieldset.options {
font-size: 80%;
}
fieldset > legend {
padding: 0 0.5em;
}
span.labeled-input {
padding: 0.25em;
margin: 0.25em 0.5em;
border: 1px inset;
border-radius: 0.25em;
white-space: nowrap;
}
#notes-caveats {
border-top: 1px dotted;
padding-top: 0.25em;
margin-top: 0.5em;
}
</style>
</head>
<body>
<header>sqlite3 fiddle</header>
<figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden='1'></progress>
</div>
<fieldset class='options'>
<legend>Options</legend>
<div class=''>
<span class='labeled-input'>
<input type='checkbox' id='opt-cb-sbs'>
<label for='opt-cb-sbs'>Side-by-side</label>
</span>
<span class='labeled-input'>
<input type='checkbox' id='opt-cb-autoscroll' checked>
<label for='opt-cb-autoscroll'>Auto-scroll output</label>
</span>
</div>
</fieldset>
<div id='main-wrapper'>
<div class='ta-wrapper'>
<textarea id="input" rows="8"
placeholder="Shell input. Ctrl-enter/shift-enter runs it.">
-- Use ctrl-enter or shift-enter to execute SQL
.nullvalue NULL
.mode box
CREATE TABLE t(a,b);
INSERT INTO t(a,b) VALUES('abc',123),('def',456),(NULL,789),('ghi',012);
SELECT * FROM t;</textarea>
<div class='button-bar'>
<button id='btn-run' disabled>Run</button>
<button id='btn-clear' disabled>Clear</button>
<button data-cmd='.help' disabled>Help</button>
</div>
</div>
<div class='ta-wrapper'>
<textarea id="output" rows="18" readonly
placeholder="Shell output."></textarea>
<div class='button-bar'>
<button id='btn-clear-output' disabled>Clear</button>
</div>
</div>
</div>
<div id='notes-caveats'>
<header>
Notes and Caveats
<button id='btn-notes-caveats'>Remove</button>
</header>
<p>
This JavaScript application runs a C application which has been
compiled into WASM (Web Assembly). As such, it has certain
limitations. Those include, but are not limited to:
</p>
<ul>
<li>It <strong>cannot recover after a call to
<code>exit()</code></strong>. If the native code triggers
an exit, reloading the page is the only way to restart
the application. (Making the app restartable without reloading
the page would require significant surgery in the C code.)
</li>
<li>It <strong>cannot perform any file I/O</strong>, and running
any command which attempts to do so might trigger an
<code>exit()</code>.
</li>
<li>A number of dot-commands available in the CLI shell are
explicitly removed from this version of the shell.
</li>
</ul>
</div><!-- #notes-caveats -->
<script type='text/javascript'>
(function(){
/* An object for propagating certain config state between our
JS code and the WASM module. */
const config = {
/* If true, the Module.print() impl will auto-scroll
the output widget to the bottom when it receives output,
else it won't. */
autoScrollOutput: true
};
/**
Callback for the emscripten module init process, gets
passed the module object after all parts of the module
have been loaded and initialized.
*/
const doAppSetup = function(Module) {
const taInput = document.querySelector('#input');
const btnClearIn = document.querySelector('#btn-clear');
document.querySelectorAll('button').forEach(function(e){
e.removeAttribute('disabled');
});
btnClearIn.addEventListener('click',function(){
taInput.value = '';
},false);
// Ctrl-enter and shift-enter both run the current SQL.
taInput.addEventListener('keydown',function(ev){
if((ev.ctrlKey || ev.shiftKey) && 13 === ev.keyCode){
ev.preventDefault();
ev.stopPropagation();
btnRun.click();
}
}, false);
const taOutput = document.querySelector('#output');
const btnClearOut = document.querySelector('#btn-clear-output');
btnClearOut.addEventListener('click',function(){
taOutput.value = '';
},false);
const doExec = function f(sql){
if(!f._) f._ = Module.cwrap('fiddle_exec', null, ['string']);
if(Module._isDead){
Module.printErr("shell module has exit()ed. Cannot run SQL.");
}else{
f._(sql);
}
};
const btnRun = document.querySelector('#btn-run');
btnRun.addEventListener('click',function(){
const sql = taInput.value.trim();
if(sql){
doExec(sql);
}
},false);
doExec(null)/*sets up the db and outputs the header*/;
document.querySelector('#opt-cb-sbs')
.addEventListener('change', function(){
document.querySelector('#main-wrapper').classList[
this.checked ? 'add' : 'remove'
]('side-by-side');
}, false);
document.querySelector('#opt-cb-autoscroll')
.addEventListener('change', function(){
config.autoScrollOutput = this.checked;
}, false);
document.querySelector('#btn-notes-caveats')
.addEventListener('click', function(){
document.querySelector('#notes-caveats').remove();
}, false);
/* For all buttons with data-cmd=X, map a click handler which
calls doExec(X). */
const cmdClick = function(){doExec(this.dataset.cmd);};
document.querySelectorAll('button[data-cmd]').forEach(
e => e.addEventListener('click', cmdClick, false)
);
};
/**
What follows is part of the emscripten core setup. Do not
modify without understanding what it's doing...
*/
const statusElement = document.getElementById('status');
const progressElement = document.getElementById('progress');
const spinnerElement = document.getElementById('spinner');
const Module = window.Module = {
preRun: [],
postRun: [],
onRuntimeInitialized: function(){
doAppSetup(this);
},
print: (function f() {
if(!f._){
f._ = document.getElementById('output');
}
f._.value = ''; // clear browser cache
return function(text) {
if(arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
//text = text.replace(/>/g, "&gt;");
//text = text.replace('\n', '<br>', 'g');
//console.log("arguments",arguments);
console.log(text);
f._.value += text + "\n";
if(config.autoScrollOutput){
f._.scrollTop = f._.scrollHeight;
}
};
})(),
setStatus: function f(text) {
if(!f.last) f.last = { time: Date.now(), text: '' };
if(text === f.last.text) return;
const m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
const now = Date.now();
if(m && now - f.last.time < 30) return; // if this is a progress update, skip it if too soon
f.last.time = now;
f.last.text = text;
if(m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
if(!text) spinnerElement.hidden = true;
}
statusElement.innerHTML = text;
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
this.setStatus(left
? ('Preparing... (' + (this.totalDependencies-left)
+ '/' + this.totalDependencies + ')')
: 'All downloads complete.');
}
};
Module.printErr = Module.print/*capture stderr output*/;
Module.setStatus('Downloading...');
window.onerror = function(/*message, source, lineno, colno, error*/) {
const err = arguments[4];
if(err && 'ExitStatus'==err.name){
Module._isDead = true;
Module.printErr("FATAL ERROR:", err.message);
Module.printErr("Restarting the app requires reloading the page.");
const taOutput = document.querySelector('#output');
if(taOutput) taOutput.classList.add('error');
}
Module.setStatus('Exception thrown, see JavaScript console');
spinnerElement.style.display = 'none';
Module.setStatus = function(text) {
if(text) console.error('[post-exception status] ' + text);
};
};
})();
</script>
{{{ SCRIPT }}}
</body>
</html>