|
| 1 | +const execBtn = document.getElementById("execute"); |
| 2 | +const outputElm = document.getElementById('output'); |
| 3 | +const errorElm = document.getElementById('error'); |
| 4 | +const commandsElm = document.getElementById('commands'); |
| 5 | +const dbFileElm = document.getElementById('dbfile'); |
| 6 | +const savedbElm = document.getElementById('savedb'); |
| 7 | +const querySection = document.getElementById('query'); |
| 8 | +const uploadSection = document.getElementById('dropzone'); |
| 9 | + |
| 10 | +// Start the worker in which sql.js will run |
| 11 | +const worker = new Worker("worker.sql-wasm.js"); |
| 12 | +worker.onerror = error; |
| 13 | + |
| 14 | +// Open a database |
| 15 | +worker.postMessage({ action: 'open' }); |
| 16 | + |
| 17 | +// Connect to the HTML element we 'print' to |
| 18 | +function print(text) { |
| 19 | + outputElm.innerHTML = text.replace(/\n/g, '<br>'); |
| 20 | +} |
| 21 | + |
| 22 | +function error(e) { |
| 23 | + console.log(e); |
| 24 | + errorElm.classList.remove('visually-hidden'); |
| 25 | + errorElm.classList.add('alert', 'alert-danger'); |
| 26 | + errorElm.textContent = e.message; |
| 27 | +} |
| 28 | + |
| 29 | +function noerror() { |
| 30 | + errorElm.classList.add('visually-hidden'); |
| 31 | + errorElm.classList.remove('alert', 'alert-danger'); |
| 32 | +} |
| 33 | + |
| 34 | +// Run a command in the database |
| 35 | +function execute(commands) { |
| 36 | + tic(); |
| 37 | + worker.onmessage = function (event) { |
| 38 | + var results = event.data.results; |
| 39 | + |
| 40 | + toc("Executing SQL"); |
| 41 | + if (!results) { |
| 42 | + error({message: event.data.error}); |
| 43 | + return; |
| 44 | + } |
| 45 | + |
| 46 | + tic(); |
| 47 | + outputElm.innerHTML = ""; |
| 48 | + for (var i = 0; i < results.length; i++) { |
| 49 | + outputElm.appendChild(tableCreate(results[i].columns, results[i].values)); |
| 50 | + } |
| 51 | + toc("Displaying results"); |
| 52 | + } |
| 53 | + worker.postMessage({ action: 'exec', sql: commands }); |
| 54 | + outputElm.textContent = "Fetching results..."; |
| 55 | +} |
| 56 | + |
| 57 | +// Create an HTML table |
| 58 | +var tableCreate = function () { |
| 59 | + function valconcat(vals, tagName) { |
| 60 | + if (vals.length === 0) return ''; |
| 61 | + var open = '<' + tagName + '>', close = '</' + tagName + '>'; |
| 62 | + return open + vals.join(close + open) + close; |
| 63 | + } |
| 64 | + return function (columns, values) { |
| 65 | + var tbl = document.createElement('table'); |
| 66 | + tbl.classList.add('table', 'table-hover') |
| 67 | + var html = '<thead>' + valconcat(columns, 'th') + '</thead>'; |
| 68 | + var rows = values.map(function (v) { return valconcat(v, 'td'); }); |
| 69 | + html += '<tbody>' + valconcat(rows, 'tr') + '</tbody>'; |
| 70 | + tbl.innerHTML = html; |
| 71 | + return tbl; |
| 72 | + } |
| 73 | +}(); |
| 74 | + |
| 75 | +// Execute the commands when the button is clicked |
| 76 | +function execEditorContents() { |
| 77 | + noerror() |
| 78 | + execute(editor.getValue() + ';'); |
| 79 | +} |
| 80 | +execBtn.addEventListener("click", execEditorContents, true); |
| 81 | + |
| 82 | +// Performance measurement functions |
| 83 | +var tictime; |
| 84 | +if (!window.performance || !performance.now) { window.performance = { now: Date.now } } |
| 85 | +function tic() { tictime = performance.now() } |
| 86 | +function toc(msg) { |
| 87 | + var dt = performance.now() - tictime; |
| 88 | + console.log((msg || 'toc') + ": " + dt + "ms"); |
| 89 | +} |
| 90 | + |
| 91 | +// Add syntax highlihjting to the textarea |
| 92 | +var editor = CodeMirror.fromTextArea(commandsElm, { |
| 93 | + mode: 'text/x-sql', |
| 94 | + viewportMargin: Infinity, |
| 95 | + autofocus: true, |
| 96 | + indentWithTabs: true, |
| 97 | + smartIndent: true, |
| 98 | + lineNumbers: true, |
| 99 | + matchBrackets: true, |
| 100 | + autofocus: true, |
| 101 | + extraKeys: { |
| 102 | + "Ctrl-Enter": execEditorContents, |
| 103 | + "Ctrl-S": savedb, |
| 104 | + } |
| 105 | +}); |
| 106 | + |
| 107 | +// Load a db from a file |
| 108 | +var dbFile = new FileReader(); |
| 109 | +var defaultQuery = "SELECT `name`, `sql`\n FROM `sqlite_master`\n WHERE type='table';" |
| 110 | + |
| 111 | +dbFile.onload = function () { |
| 112 | + worker.onmessage = function () { |
| 113 | + toc("Loading database from file"); |
| 114 | + // Show the schema of the loaded database |
| 115 | + editor.setValue(localStorage.getItem('lastQuery') || defaultQuery); |
| 116 | + execEditorContents(); |
| 117 | + |
| 118 | + uploadSection.classList.add('visually-hidden'); |
| 119 | + querySection.classList.remove('visually-hidden'); |
| 120 | + noerror(); |
| 121 | + }; |
| 122 | + tic(); |
| 123 | + try { |
| 124 | + worker.postMessage({ action: 'open', buffer: dbFile.result }, [dbFile.result]); |
| 125 | + } |
| 126 | + catch (exception) { |
| 127 | + worker.postMessage({ action: 'open', buffer: dbFile.result }); |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | + |
| 132 | +function init() { |
| 133 | + let dropzoneElement = new Dropzone("#demo-upload"); |
| 134 | + |
| 135 | + |
| 136 | + dropzoneElement.uploadFiles = function(files) { |
| 137 | + var self = this, |
| 138 | + minSteps = 6, |
| 139 | + maxSteps = 60, |
| 140 | + timeBetweenSteps = 1, |
| 141 | + bytesPerStep = 1000000; |
| 142 | + |
| 143 | + for (var i = 0; i < files.length; i++) { |
| 144 | + |
| 145 | + var file = files[i]; |
| 146 | + totalSteps = Math.round(Math.min(maxSteps, Math.max(minSteps, file.size / bytesPerStep))); |
| 147 | + |
| 148 | + for (var step = 0; step < totalSteps; step++) { |
| 149 | + var duration = timeBetweenSteps * (step + 1); |
| 150 | + setTimeout(function(file, totalSteps, step) { |
| 151 | + return function() { |
| 152 | + file.upload = { |
| 153 | + progress: 100 * (step + 1) / totalSteps, |
| 154 | + total: file.size, |
| 155 | + bytesSent: (step + 1) * file.size / totalSteps |
| 156 | + }; |
| 157 | + |
| 158 | + self.emit('uploadprogress', file, file.upload.progress, file.upload.bytesSent); |
| 159 | + if (file.upload.progress == 100) { |
| 160 | + file.status = Dropzone.SUCCESS; |
| 161 | + self.emit("success", file, 'success', null); |
| 162 | + self.emit("complete", file); |
| 163 | + self.processQueue(); |
| 164 | + } |
| 165 | + }; |
| 166 | + }(file, totalSteps, step), duration); |
| 167 | + } |
| 168 | + dbFile.readAsArrayBuffer(files[0]); |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + function saveQuery(e) { |
| 173 | + if (e.getValue()) { |
| 174 | + localStorage.setItem('lastQuery', e.getValue()); |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + editor.setValue(localStorage.getItem('lastQuery') || defaultQuery); |
| 179 | + editor.on("changes", saveQuery); |
| 180 | +} |
| 181 | + |
| 182 | +Dropzone.autoDiscover = false; |
| 183 | +document.addEventListener("DOMContentLoaded", init); |
| 184 | + |
| 185 | +// Save the db to a file |
| 186 | +function savedb() { |
| 187 | + worker.onmessage = function (event) { |
| 188 | + toc("Exporting the database"); |
| 189 | + var arraybuff = event.data.buffer; |
| 190 | + var blob = new Blob([arraybuff]); |
| 191 | + var a = document.createElement("a"); |
| 192 | + document.body.appendChild(a); |
| 193 | + a.href = window.URL.createObjectURL(blob); |
| 194 | + a.download = "sql.db"; |
| 195 | + a.onclick = function () { |
| 196 | + setTimeout(function () { |
| 197 | + window.URL.revokeObjectURL(a.href); |
| 198 | + }, 1500); |
| 199 | + }; |
| 200 | + a.click(); |
| 201 | + }; |
| 202 | + tic(); |
| 203 | + worker.postMessage({ action: 'export' }); |
| 204 | +} |
| 205 | + |
| 206 | +savedbElm.addEventListener("click", savedb, true); |
0 commit comments