<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>sqlite3 fiddle</title>
+ <!-- script src="jqterm/jqterm-bundle.min.js"></script>
+ <link rel="stylesheet" href="jqterm/jquery.terminal.min.css"/ -->
<style>
/* emcscript-related styling, used during the intialization phase... */
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
}
#main-wrapper {
display: flex;
- flex-direction: column;
+ flex-direction: column-reverse;
+ flex: 20 1 auto;
}
#main-wrapper.side-by-side {
- flex-direction: row;
+ flex-direction: row-reverse;
}
.ta-wrapper{
display: flex;
flex-direction: column;
align-items: stretch;
+ margin: 0 0.25em;
flex: 1 1 auto;
- margin: 0.25em;
}
+ .ta-wrapper.input { flex: 10 1 auto; }
+ .ta-wrapper.output { flex: 20 1 auto; }
.ta-wrapper textarea {
font-size: 110%;
filter: invert(100%);
+ flex: 10 1 auto;
}
+ /*#main-wrapper:not(.side-by-side) .ta-wrapper.input {
+ flex: 5 1 auto;
+ }*/
.button-bar {
display: flex;
justify-content: center;
+ flex: 0 1 auto;
}
.button-bar button {
margin: 0.25em 1em;
margin-top: 0.5em;
}
.center { text-align: center; }
+
+ body.terminal-mode {
+ max-height: calc(100% - 2em);
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ }
+ #jqterminal {
+ }
+ .app-view {
+ flex: 20 1 auto;
+ }
+ #titlebar {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 0.5em;
+ }
+ #view-split {
+ display: flex;
+ flex-direction: column;
+ }
</style>
</head>
<body>
- <header>sqlite3 fiddle</header>
+ <header id='titlebar'><span>sqlite3 fiddle</span></header>
<figure id="spinner">
<div class="spinner"></div>
<div class='center'><strong>Initializing app...</strong></div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden='1'></progress>
</div>
- <fieldset class='options initially-hidden'>
- <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'
- data-config='autoScrollOutput'>
- <label for='opt-cb-autoscroll'>Auto-scroll output</label>
- </span>
- <span class='labeled-input'>
- <input type='checkbox' id='opt-cb-autoclear'
- data-config='autoClearOutput'>
- <label for='opt-cb-autoclear'>Auto-clear output</label>
- </span>
- </div>
- </fieldset>
- <div id='main-wrapper' class='initially-hidden'>
- <div class='ta-wrapper'>
- <textarea id="input" rows="8"
- placeholder="Shell input. Ctrl-enter/shift-enter runs it.">
+
+ <div id='jqterminal' class='app-view hidden initially-hidden'>
+ This is a placeholder for a terminal-like view.
+ </div>
+
+ <div id='view-split' class='app-view initially-hidden'>
+ <fieldset class='options'>
+ <legend>Options</legend>
+ <div class=''>
+ <span class='labeled-input'>
+ <input type='checkbox' id='opt-cb-sbs' checked>
+ <label for='opt-cb-sbs'>Side-by-side</label>
+ </span>
+ <span class='labeled-input'>
+ <input type='checkbox' id='opt-cb-autoscroll'
+ data-config='autoScrollOutput'>
+ <label for='opt-cb-autoscroll'>Auto-scroll output</label>
+ </span>
+ <span class='labeled-input'>
+ <input type='checkbox' id='opt-cb-autoclear'
+ data-config='autoClearOutput'>
+ <label for='opt-cb-autoclear'>Auto-clear output</label>
+ </span>
+ </div>
+ </fieldset>
+ <div id='main-wrapper' class='side-by-side'>
+ <div class='ta-wrapper input'>
+ <textarea id="input"
+ 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'>Run</button>
- <button id='btn-clear'>Clear</button>
- <button data-cmd='.help'>Help</button>
+ <div class='button-bar'>
+ <button id='btn-run'>Run</button>
+ <button id='btn-clear'>Clear</button>
+ <button data-cmd='.help'>Help</button>
+ </div>
</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'>Clear</button>
+ <div class='ta-wrapper output'>
+ <textarea id="output" readonly
+ placeholder="Shell output."></textarea>
+ <div class='button-bar'>
+ <button id='btn-clear-output'>Clear</button>
+ </div>
</div>
</div>
- </div>
- <div id='notes-caveats' class='initially-hidden'>
- <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 -->
+ </div> <!-- .app-view -->
<!-- Maintenance notes:
- emscripten module init goes is in fiddle-pre.js and gets
const btnClearOut = E('#btn-clear-output');
btnClearOut.addEventListener('click',function(){
taOutput.value = '';
+ if(Module.jqTerm) Module.jqTerm.clear();
},false);
/* Sends the given text to the shell. If it's null or empty, this
is a no-op except that the very first call will initialize the
this.checked ? 'add' : 'remove'
]('side-by-side');
}, false);
- E('#btn-notes-caveats')
- .addEventListener('click', function(){
- E('#notes-caveats').remove();
- }, false);
/* For each checkbox with data-config=X, set up a binding to
Module.config[X]. */
e => e.addEventListener('click', cmdClick, false)
);
+
+ /**
+ Given a DOM element, this routine measures its "effective
+ height", which is the bounding top/bottom range of this element
+ and all of its children, recursively. For some DOM structure
+ cases, a parent may have a reported height of 0 even though
+ children have non-0 sizes.
+
+ Returns 0 if !e or if the element really has no height.
+ */
+ const effectiveHeight = function f(e){
+ if(!e) return 0;
+ if(!f.measure){
+ f.measure = function callee(e, depth){
+ if(!e) return;
+ const m = e.getBoundingClientRect();
+ if(0===depth){
+ callee.top = m.top;
+ callee.bottom = m.bottom;
+ }else{
+ callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
+ callee.bottom = Math.max(callee.bottom, m.bottom);
+ }
+ Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
+ if(0===depth){
+ //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
+ f.extra += callee.bottom - callee.top;
+ }
+ return f.extra;
+ };
+ }
+ f.extra = 0;
+ f.measure(e,0);
+ return f.extra;
+ };
+
+ /**
+ Returns a function, that, as long as it continues to be invoked,
+ will not be triggered. The function will be called after it stops
+ being called for N milliseconds. If `immediate` is passed, call
+ the callback immediately and hinder future invocations until at
+ least the given time has passed.
+
+ If passed only 1 argument, or passed a falsy 2nd argument,
+ the default wait time set in this function's $defaultDelay
+ property is used.
+
+ Source: underscore.js, by way of https://davidwalsh.name/javascript-debounce-function
+ */
+ const debounce = function f(func, wait, immediate) {
+ var timeout;
+ if(!wait) wait = f.$defaultDelay;
+ return function() {
+ const context = this, args = Array.prototype.slice.call(arguments);
+ const later = function() {
+ timeout = undefined;
+ if(!immediate) func.apply(context, args);
+ };
+ const callNow = immediate && !timeout;
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ if(callNow) func.apply(context, args);
+ };
+ };
+ debounce.$defaultDelay = 500 /*arbitrary*/;
+
+ const ForceResizeKludge = (function(){
+ /* Workaround for Safari mayhem regarding use of vh CSS units....
+ We cannot use vh units to set the terminal area size because
+ Safari chokes on that, so we calculate that height here. Larger
+ than ~95% is too big for Firefox on Android, causing the input
+ area to move off-screen. */
+ const bcl = document.body.classList;
+ const appViews = EAll('.app-view');
+ const resized = function f(){
+ if(f.$disabled) return;
+ const wh = window.innerHeight;
+ var ht;
+ var extra = 0;
+ const elemsToCount = [
+ E('body > header')
+ ];
+ elemsToCount.forEach((e)=>e ? extra += effectiveHeight(e) : false);
+ ht = wh - extra;
+ appViews.forEach(function(e){
+ e.style.height =
+ e.style.maxHeight = [
+ "calc(", (ht>=100 ? ht : 100), "px",
+ " - 3em"/*fudge value*/,")"
+ /* ^^^^ hypothetically not needed, but both Chrome/FF on
+ Linux will force scrollbars on the body if this value is
+ too small (<0.75em in my tests). */
+ ].join('');
+ });
+ };
+ resized.$disabled = true/*gets deleted when setup is finished*/;
+ window.addEventListener('resize', debounce(resized, 250), false);
+ return resized;
+ })();
+
Module.print(null/*clear any output generated by the init process*/);
- doExec(null)/*sets up the db and outputs the header*/;
+ if(window.jQuery && window.jQuery.terminal){
+ /* Set up the terminal-style view... */
+ const eTerm = window.jQuery('#jqterminal').empty();
+ Module.jqTerm = eTerm.terminal(doExec,{
+ prompt: 'sqlite> ',
+ greetings: false /* note that the docs incorrectly call this 'greeting' */
+ });
+ //Module.jqTerm.clear(/*remove the "greeting"*/);
+ /* Set up a button to toggle the views... */
+ const head = E('header#titlebar');
+ const btnToggleView = jQuery("<button>Toggle View</button>")[0];
+ head.appendChild(btnToggleView);
+ btnToggleView.addEventListener('click',function f(){
+ EAll('.app-view').forEach(e=>e.classList.toggle('hidden'));
+ if(document.body.classList.toggle('terminal-mode')){
+ ForceResizeKludge();
+ }
+ }, false);
+ btnToggleView.click();
+ }
+ doExec(null/*init the db and output the header*/);
+ delete ForceResizeKludge.$disabled;
+ ForceResizeKludge();
};
autoClearOutput: false,
/* If true, Module.print() will echo its output to
the console, in addition to its normal output widget. */
- printToConsole: false,
+ printToConsole: true
},
preRun: [],
postRun: [],
return;
}
if(window.Module.config.printToConsole) console.log(text);
+ if(window.Module.jqTerm) window.Module.jqTerm.echo(text);
outputElem.value += text + "\n";
if(window.Module.config.autoScrollOutput){
outputElem.scrollTop = outputElem.scrollHeight;
-C fiddle\smake\starget\snow\saccepts\sfiddle_cflags=...\sfrom\sthe\sCLI\sto\soverrid\s-Ox\sand\ssuch\sfor\sone-off\sbuilds.
-D 2022-05-19T10:58:59.923
+C Numerous\slayout\stweaks,\sthe\smost\ssignificant\sbeing\sthat\sthe\slayout\snow\sadapts\sto\sthe\swindow\ssize.\sSwapped\spositions\sof\sthe\sinput/output\sareas.\sThis\sversion\ssupports,\sby\suncommenting\sa\sfew\sbits,\sa\sjquery.terminal-based\sview\sbut\salternatives\sto\sthat\s300kb\sdependency\sare\sstill\sunder\sinvestigation.
+D 2022-05-19T15:58:13.139
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaedabd158ea8969b
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
F ext/fiddle/Makefile ea647919e6ac4b50edde1490f60ee87e8ccd75141e4aa650718c6f28eb323bbc
-F ext/fiddle/fiddle.in.html fc5bb8e6c13cac9880dfb41eceed3ff031d51d2a73bf66da51e5cc171e1ee28c
+F ext/fiddle/fiddle.in.html 0a176030dd3643a811007c39b8dde2271652b5a7a8597f0d7db8259a2f043111
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
-F ext/fiddle/module-post.js 5295dfb2bd744cb0ad03d219e8e14123b1bb8ad39054f8b65c3358df4d746cd2
-F ext/fiddle/module-pre.js baff3e5f693db09f693af0bf398c0c89cdef04bdc3ffb6ad4ed02775077fdea4
+F ext/fiddle/module-post.js 0ff724148ea206d69e39241b771006606ec11131536fe677bfaf4d8faf546299
+F ext/fiddle/module-pre.js 318fe73db5b9bf829d6f7a509ed557205ca7e64de59fcbb108d1dc4fa7aa3ac6
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
F ext/fts1/ft_hash.c 3927bd880e65329bdc6f506555b228b28924921b
F ext/fts1/ft_hash.h 06df7bba40dadd19597aa400a875dbc2fed705ea
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 1d8d0593573f9fc8e0990a292a4b3317d8a4c323d60514d0768543dd65c24d1e
-R 95e0243279f406591d2490a8625a64de
+P 4609a4f8626ae3d8179cae27e391bd06ffda18e9ef9e1b78745b36c7e8dd25db
+R e1efe6508b92a0a525711479235be1f2
U stephan
-Z 342f02945f9525abd872463c70c813a2
+Z 187d58546525d991c64f645554e38d64
# Remove this line to create a well-formed Fossil manifest.
-4609a4f8626ae3d8179cae27e391bd06ffda18e9ef9e1b78745b36c7e8dd25db
\ No newline at end of file
+1aad3642c9fc14c25223628a309d84decc8d73a123e42d6efdc36d855b5b0666
\ No newline at end of file