aboutsummaryrefslogtreecommitdiff
path: root/ext/wasm/fiddle/fiddle.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/wasm/fiddle/fiddle.js')
-rw-r--r--ext/wasm/fiddle/fiddle.js298
1 files changed, 180 insertions, 118 deletions
diff --git a/ext/wasm/fiddle/fiddle.js b/ext/wasm/fiddle/fiddle.js
index f0a89f25d..877a87772 100644
--- a/ext/wasm/fiddle/fiddle.js
+++ b/ext/wasm/fiddle/fiddle.js
@@ -329,6 +329,21 @@
SF.worker = new Worker('fiddle-worker.js'+self.location.search);
SF.worker.onmessage = (ev)=>SF.runMsgHandlers(ev.data);
SF.addMsgHandler(['stdout', 'stderr'], (ev)=>SF.echo(ev.data));
+ SF.addMsgHandler('sqlite-version', (ev)=>{
+ const v = ev.data;
+ const a = E('#sqlite-version-link');
+ const li = v.srcId.split(' ')/*DATE TIME HASH*/;
+ a.setAttribute('href',
+ //'https://sqlite.org/src/timeline/?c='+li[2].substr(0,20)
+ 'https://sqlite.org/src/info/'+li[2].substr(0,20)
+ );
+ a.setAttribute('target', '_blank');
+ a.innerText = [
+ v.lib,
+ v.srcId.substr(0,34)
+ ].join(' ');
+ SF.echo("SQLite version",a.innerText);
+ });
/* querySelectorAll() proxy */
const EAll = function(/*[element=document,] cssSelector*/){
@@ -391,6 +406,143 @@
self.onSFLoaded();
});
+ SF.e ={
+ about: E('#view-about'),
+ split: E('#view-split'),
+ terminal: E('#view-terminal'),
+ hideInTerminal: EAll('.hide-in-terminal'
+ /* Elements to hide when in terminal mode */)
+ };
+ SF.eViews = EAll('.app-view');
+ SF.setMainView = function(eMain){
+ if( SF.e.main === eMain ) return;
+ SF.eViews.forEach((e)=>{
+ if( e===eMain ) e.classList.remove('hidden');
+ else e.classList.add('hidden');
+ });
+ SF.e.hideInTerminal.forEach(e=>{
+ if( eMain === SF.e.terminal ) e.classList.add('hidden');
+ else e.classList.remove('hidden');
+ });
+ SF.e.main = eMain;
+ SF.ForceResizeKludge();
+ };
+
+ /** Toggle the "About" view on and off. */
+ SF.toggleAbout = function(){
+ if( SF.e.about.classList.contains('hidden') ){
+ SF.e.about.$returnTo = SF.e.main;
+ SF.setMainView( SF.e.about );
+ }else{
+ const e = SF.e.about.$returnTo;
+ delete SF.e.about.$returnTo;
+ SF.setMainView( e );
+ }
+ };
+
+ /**
+ 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*/;
+
+ SF.ForceResizeKludge = (function(){
+ /* Workaround for Safari mayhem regarding use of vh CSS
+ units.... We cannot use vh units to set the main view
+ 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 eVisibles = EAll('.app-view');
+ const elemsToCount = [
+ /* Elements which we need to always count in the
+ visible body size. */
+ E('body > header'),
+ E('body > footer'),
+ E('body > fieldset.options')
+ ];
+ const resized = function f(){
+ if(f.$disabled) return;
+ const wh = window.innerHeight;
+ var ht;
+ var extra = 0;
+ elemsToCount.forEach((e)=>e ? extra += effectiveHeight(e) : false);
+ ht = wh - extra;
+ eVisibles.forEach(function(e){
+ e.style.height =
+ e.style.maxHeight = [
+ "calc(", (ht>=100 ? ht : 100), "px",
+ " - 2em"/*fudge value*/,")"
+ /* ^^^^ hypothetically not needed, but both
+ Chrome/FF on Linux will force scrollbars on the
+ body if this value is too small. */
+ ].join('');
+ });
+ };
+ resized.$disabled = true/*gets deleted when setup is finished*/;
+ window.addEventListener('resize', debounce(resized, 250), false);
+ return resized;
+ })();
+
/**
Performs all app initialization which must wait until after the
worker module is loaded. This function removes itself when it's
@@ -398,9 +550,19 @@
*/
self.onSFLoaded = function(){
delete this.onSFLoaded;
+
// Unhide all elements which start out hidden
EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden'));
+ SF.e.main = EAll('.app-view:not(.hidden)')[0]
+ /** The main view widget. Initially the first non-hidden
+ .app-view element. */;
+ if( (new URL(self.location.href).searchParams).has('about') ){
+ SF.toggleAbout() /* for use while editing the About page */;
+ }
E('#btn-reset').addEventListener('click',()=>SF.resetDb());
+ EAll('#btn-about, #btn-about-close').forEach((e)=>{
+ e.addEventListener('click',()=>SF.toggleAbout())
+ });
const taInput = E('#input');
const btnClearIn = E('#btn-clear');
const selectExamples = E('#select-examples');
@@ -618,108 +780,6 @@
}, 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 main view
- 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 appViews = EAll('.app-view');
- const elemsToCount = [
- /* Elements which we need to always count in the
- visible body size. */
- E('body > header'),
- E('body > footer')
- ];
- const resized = function f(){
- if(f.$disabled) return;
- const wh = window.innerHeight;
- var ht;
- var extra = 0;
- 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",
- " - 2em"/*fudge value*/,")"
- /* ^^^^ hypothetically not needed, but both
- Chrome/FF on Linux will force scrollbars on the
- body if this value is too small. */
- ].join('');
- });
- };
- resized.$disabled = true/*gets deleted when setup is finished*/;
- window.addEventListener('resize', debounce(resized, 250), false);
- return resized;
- })();
-
/** Set up a selection list of examples */
(function(){
const xElem = E('#select-examples');
@@ -790,33 +850,35 @@
})()/* example queries */;
//SF.echo(null/*clear any output generated by the init process*/);
- if(window.jQuery && window.jQuery.terminal){
+ if(window.jQuery && window.jQuery.terminal && SF.e.terminal){
/* Set up the terminal-style view... */
- const eTerm = window.jQuery('#view-terminal').empty();
- SF.jqTerm = eTerm.terminal(SF.dbExec.bind(SF),{
+ const jqeTerm = window.jQuery(SF.e.terminal).empty();
+ SF.jqTerm = jqeTerm.terminal(SF.dbExec.bind(SF),{
prompt: 'sqlite> ',
greetings: false /* note that the docs incorrectly call this 'greeting' */
});
+ EAll('.unhide-if-terminal-available').forEach(e=>{
+ e.classList.remove('hidden');
+ });
+ EAll('.remove-if-terminal-available').forEach(e=>{
+ e.parentElement.removeChild(e);
+ });
/* Set up a button to toggle the views... */
- const head = E('header#titlebar');
+ const ePlaceholder = E('#terminal-button-placeholder');
+ ePlaceholder.classList.add('labeled-input');
+ ePlaceholder.classList.remove('hidden');
const btnToggleView = document.createElement('button');
- btnToggleView.appendChild(document.createTextNode("Toggle View"));
- head.appendChild(btnToggleView);
+ btnToggleView.innerText = "Toggle view";
+ ePlaceholder.appendChild( btnToggleView );
btnToggleView.addEventListener('click',function f(){
- EAll('.app-view').forEach(e=>e.classList.toggle('hidden'));
- if(document.body.classList.toggle('terminal-mode')){
- ForceResizeKludge();
- }
+ const terminalOn = document.body.classList.toggle('terminal-mode');
+ SF.setMainView( terminalOn ? SF.e.terminal : SF.e.split );
}, false);
btnToggleView.click()/*default to terminal view*/;
}
- SF.echo('This experimental app is provided in the hope that it',
- 'may prove interesting or useful but is not an officially',
- 'supported deliverable of the sqlite project. It is subject to',
- 'any number of changes or outright removal at any time.\n');
const urlParams = new URL(self.location.href).searchParams;
SF.dbExec(urlParams.get('sql') || null);
- delete ForceResizeKludge.$disabled;
- ForceResizeKludge();
+ delete SF.ForceResizeKludge.$disabled;
+ SF.ForceResizeKludge();
}/*onSFLoaded()*/;
})();