sqlui 0.1.24 → 0.1.26

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 496f67632ee9516c9569403f9907c96e9efad644cdba583fbc55b2112cfd2eb7
4
- data.tar.gz: d186c82b1fb261ec365275412319c19e61acfe117472f0e370b541be3fb13af1
3
+ metadata.gz: 20b037b445c287b8e9586a2f5820d4be597bb977db8a9412721790a5239a2f87
4
+ data.tar.gz: '08e5d525245186d568bd2deb86f4a5d25adc9a57d3729651ff74d6fe90e22451'
5
5
  SHA512:
6
- metadata.gz: e9d3a25232cbdb23eaec1672da41de0825defc0fb32fb2a3d3f1cc4a639b8d6945c6ff5f8ee8165f815a34ccb61f22443bc6553e840fdd6a9ecfa74b7181754d
7
- data.tar.gz: e165ae26e57800866243a266957b8d9e40da901589c8c0f44efa5efc9ac0ffb293bfd096ecaabca58c972ddfb8ac1b33e9518b9cbef60cd0233d3c97082b36ac
6
+ metadata.gz: 631086186d72bfe55f633e88a1017e911d53c4185b9139da4ebf0d462ec9fb48fbf639154fc014453285835c504b1ce8a18f20e9de9c78239a95566b2cde3177
7
+ data.tar.gz: 24cd3591e88d28785765b6add934a19458c19920cdd77c02f31f7356ca6cefdf4971e45df560c1e451a50ee4192c1ca4d9f8cae227cfe7daa87463b82f7ada6f
data/.version CHANGED
@@ -1 +1 @@
1
- 0.1.24
1
+ 0.1.26
data/app/server.rb CHANGED
@@ -25,20 +25,17 @@ class Server < Sinatra::Base
25
25
  body 'OK'
26
26
  end
27
27
 
28
+ get '/?' do
29
+ redirect config.list_url_path, 301
30
+ end
31
+
28
32
  get "#{config.list_url_path}/?" do
29
33
  erb :databases, locals: { config: config }
30
34
  end
31
35
 
32
36
  config.database_configs.each do |database|
33
- get database.url_path.to_s do
34
- redirect "#{database.url_path}/app", 301
35
- end
36
-
37
- get "#{database.url_path}/app" do
38
- @html ||= File.read(File.join(resources_dir, 'sqlui.html'))
39
- status 200
40
- headers 'Content-Type': 'text/html'
41
- body @html
37
+ get "#{database.url_path}/?" do
38
+ redirect "#{database.url_path}/query", 301
42
39
  end
43
40
 
44
41
  get "#{database.url_path}/sqlui.css" do
@@ -59,16 +56,23 @@ class Server < Sinatra::Base
59
56
  metadata = database.with_client do |client|
60
57
  {
61
58
  server: "#{config.name} - #{database.display_name}",
59
+ list_url_path: config.list_url_path,
62
60
  schemas: DatabaseMetadata.lookup(client, database),
63
- saved: Dir.glob("#{database.saved_path}/*.sql").map do |path|
64
- comment_lines = File.readlines(path).take_while do |l|
61
+ saved: Dir.glob("#{database.saved_path}/*.sql").to_h do |path|
62
+ contents = File.read(path)
63
+ comment_lines = contents.split("\n").take_while do |l|
65
64
  l.start_with?('--')
66
65
  end
66
+ filename = File.basename(path)
67
67
  description = comment_lines.map { |l| l.sub(/^-- */, '') }.join
68
- {
69
- filename: File.basename(path),
70
- description: description
71
- }
68
+ [
69
+ filename,
70
+ {
71
+ filename: filename,
72
+ description: description,
73
+ contents: contents.strip
74
+ }
75
+ ]
72
76
  end
73
77
  }
74
78
  end
@@ -77,36 +81,28 @@ class Server < Sinatra::Base
77
81
  body metadata.to_json
78
82
  end
79
83
 
80
- get "#{database.url_path}/query_file" do
81
- break client_error('missing file param') unless params[:file]
82
-
83
- file = File.join(database.saved_path, params[:file])
84
- break client_error('no such file') unless File.exist?(file)
85
-
86
- sql = File.read(file)
87
- result = database.with_client do |client|
88
- execute_query(client, sql).tap { |r| r[:file] = params[:file] }
89
- end
90
-
91
- status 200
92
- headers 'Content-Type': 'application/json'
93
- body result.to_json
94
- end
95
-
96
84
  post "#{database.url_path}/query" do
97
85
  params.merge!(JSON.parse(request.body.read, symbolize_names: true))
98
86
  break client_error('missing sql') unless params[:sql]
99
87
 
88
+ full_sql = params[:sql]
100
89
  sql = params[:sql]
101
- selection = params[:selection]
102
- if selection
103
- selection = selection.split(':').map { |v| Integer(v) }
90
+ if params[:selection]
91
+ selection = params[:selection]
92
+ if selection.include?('-')
93
+ # sort because the selection could be in either direction
94
+ selection = params[:selection].split('-').map { |v| Integer(v) }.sort
95
+ else
96
+ selection = Integer(selection)
97
+ selection = [selection, selection]
98
+ end
104
99
 
105
100
  sql = if selection[0] == selection[1]
106
101
  SqlParser.find_statement_at_cursor(params[:sql], selection[0])
107
102
  else
108
- params[:sql][selection[0], selection[1]]
103
+ full_sql[selection[0], selection[1]]
109
104
  end
105
+
110
106
  break client_error("can't find query at selection") unless sql
111
107
  end
112
108
 
@@ -115,11 +111,19 @@ class Server < Sinatra::Base
115
111
  end
116
112
 
117
113
  result[:selection] = params[:selection]
114
+ result[:query] = full_sql
118
115
 
119
116
  status 200
120
117
  headers 'Content-Type': 'application/json'
121
118
  body result.to_json
122
119
  end
120
+
121
+ get(%r{#{Regexp.escape(database.url_path)}/(query|graph|structure|saved)}) do
122
+ @html ||= File.read(File.join(resources_dir, 'sqlui.html'))
123
+ status 200
124
+ headers 'Content-Type': 'text/html'
125
+ body @html
126
+ end
123
127
  end
124
128
 
125
129
  error do |e|
@@ -165,7 +169,6 @@ class Server < Sinatra::Base
165
169
  columns = []
166
170
  end
167
171
  {
168
- query: sql,
169
172
  columns: columns,
170
173
  column_types: column_types,
171
174
  total_rows: rows.size,
@@ -1,11 +1,11 @@
1
- <html>
1
+ <html lang="en">
2
2
  <head>
3
- <title>Databases</title>
3
+ <title><%= config.name %> Databases</title>
4
4
 
5
5
  <style>
6
6
  body {
7
- font-family: Helvetica;
8
- margin: 0px;
7
+ font-family: Helvetica, sans-serif;
8
+ margin: 0;
9
9
  }
10
10
 
11
11
  h1 {
@@ -25,7 +25,8 @@
25
25
  flex-direction: row;
26
26
  border-bottom: 1px solid #ddd;
27
27
  height: 36px;
28
- font-family: Helvetica;
28
+ font-family: Helvetica, sans-serif;
29
+ padding: 5px;
29
30
  }
30
31
 
31
32
  .header, .server-name {
@@ -41,40 +42,41 @@
41
42
  font-weight: normal;
42
43
  }
43
44
 
45
+ .name-and-links {
46
+ display: flex;
47
+ flex-direction: row;
48
+ align-items: center;
49
+ }
50
+
44
51
  .header {
45
52
  font-weight: bold;
46
53
  padding-left: 5px;
47
54
  }
48
55
 
49
56
  .database a {
50
- margin-right: 10px;
57
+ margin-right: 5px;
51
58
  color: darkblue;
52
- font-size: 16px
59
+ font-size: 16px;
60
+ text-decoration: none;
53
61
  }
54
62
 
55
63
  .database h2 {
56
- margin: 0px;
64
+ margin: 0 10px 0 0;
57
65
  }
58
66
 
59
67
  .database p {
60
- margin: 0px;
61
- margin-top: 10px;
68
+ margin: 10px 0 0;
62
69
  }
63
70
 
64
71
  .database {
65
- margin: 0px;
72
+ margin: 0;
66
73
  padding: 10px;
67
- cursor: pointer;
68
74
  border-bottom: 1px solid #eeeeee;
69
75
  }
70
76
 
71
77
  .database:last-child {
72
78
  border-bottom: none;
73
79
  }
74
-
75
- .database:hover {
76
- background: #eee;
77
- }
78
80
  </style>
79
81
  </head>
80
82
 
@@ -84,11 +86,13 @@
84
86
  <h1 class="server-name"><%= config.name %> Databases</h1>
85
87
  </div>
86
88
  <% config.database_configs.each do |database_config| %>
87
- <div class="database" onclick="window.location='<%= "#{database_config.url_path}/app" %>'">
88
- <h2 class='name'><%= database_config.display_name %></h2>
89
- <a class='query-link' href="<%= database_config.url_path %>/app">query</a>
90
- <a class='saved-link' href="<%= database_config.url_path %>/app?tab=saved">saved</a>
91
- <a class='structure-link' href="<%= database_config.url_path %>/app?tab=structure">structure</a>
89
+ <div class="database">
90
+ <div class="name-and-links">
91
+ <h2 class='name'><%= database_config.display_name %></h2>
92
+ <a class='query-link' href="<%= database_config.url_path %>/query">query</a>
93
+ <a class='saved-link' href="<%= database_config.url_path %>/saved">saved</a>
94
+ <a class='structure-link' href="<%= database_config.url_path %>/structure">structure</a>
95
+ </div>
92
96
  <p class='description'>
93
97
  <%= database_config.description %>
94
98
  </p>
@@ -71,6 +71,7 @@ p {
71
71
  .tab-button, .selected-tab-button {
72
72
  border: none;
73
73
  outline: none;
74
+ text-decoration: none;
74
75
  cursor: pointer;
75
76
  width: 150px;
76
77
  padding: 2px;
@@ -79,6 +80,7 @@ p {
79
80
  justify-content: center;
80
81
  background-color: #fff;
81
82
  font-size: 18px;
83
+ align-self: center;
82
84
  }
83
85
 
84
86
  .tab-button {
@@ -110,7 +112,12 @@ p {
110
112
  .submit-box {
111
113
  display: flex;
112
114
  border-top: 1px solid #ddd;
113
- justify-content: right;
115
+ border-bottom: 1px solid #ddd;
116
+ }
117
+
118
+ .submit-fill {
119
+ display: flex;
120
+ flex: 1
114
121
  }
115
122
 
116
123
  .submit-button {
@@ -135,15 +142,16 @@ p {
135
142
  color: #333;
136
143
  }
137
144
 
138
- .result-box, .saved-box, .graph-box, .structure-box {
145
+ .result-box, .fetch-sql-box, .saved-box, .graph-box, .structure-box {
139
146
  flex: 1;
140
147
  overflow: auto;
141
148
  display: flex;
142
149
  flex-direction: column;
143
150
  }
144
151
 
145
- .graph-box, .result-box {
146
- border-top: 1px solid #ddd;
152
+ .fetch-sql-box {
153
+ justify-content: center;
154
+ align-items: center;
147
155
  }
148
156
 
149
157
  .graph-box {
@@ -218,6 +226,7 @@ thead {
218
226
 
219
227
  .tabs-box {
220
228
  display: flex;
229
+ padding: 5px;
221
230
  }
222
231
 
223
232
  .saved-box {
@@ -238,18 +247,30 @@ thead {
238
247
  }
239
248
 
240
249
  .saved-list-item {
241
- cursor: pointer;
242
250
  border-bottom: 1px solid #eeeeee;
243
251
  margin: 0;
244
252
  padding: 10px;
245
253
  }
246
254
 
247
255
  .saved-list-item h2 {
248
- margin: 0 0 5px;
256
+ margin: 0 10px 0 0;
249
257
  }
250
258
 
251
- .saved-box div:hover {
252
- background: #eee;
259
+ .saved-list-item p {
260
+ margin: 10px 0 0;
261
+ }
262
+
263
+ .name-and-links {
264
+ display: flex;
265
+ flex-direction: row;
266
+ align-items: center;
267
+ }
268
+
269
+ .name-and-links a {
270
+ margin-right: 5px;
271
+ color: darkblue;
272
+ font-size: 16px;
273
+ text-decoration: none;
253
274
  }
254
275
 
255
276
  .cm-editor.cm-focused {
@@ -287,3 +308,17 @@ select {
287
308
  color: #333;
288
309
  font-size: 18px;
289
310
  }
311
+
312
+ .loader {
313
+ border: 8px solid #eee; /* Light grey */
314
+ border-top: 8px solid #888; /* Blue */
315
+ border-radius: 50%;
316
+ width: 40px;
317
+ height: 40px;
318
+ animation: spin 2s linear infinite;
319
+ }
320
+
321
+ @keyframes spin {
322
+ 0% { transform: rotate(0deg); }
323
+ 100% { transform: rotate(360deg); }
324
+ }
@@ -13,12 +13,12 @@
13
13
 
14
14
  <div id="main-box" class="main-box" style="display:none">
15
15
  <div class="tabs-box">
16
- <h1 class="header"><a href="/sqlui">SQLUI</a></h1>
16
+ <h1 class="header"><a id="header-link">SQLUI</a></h1>
17
17
  <h1 id="server-name" class="server-name"></h1>
18
- <input id="query-tab-button" class="tab-button" type="button" value="Query" onclick="sqlui.selectTab('query')"></input>
19
- <input id="graph-tab-button" class="tab-button" type="button" value="Graph" onclick="sqlui.selectTab('graph')"></input>
20
- <input id="saved-tab-button" class="tab-button" type="button" value="Saved" onclick="sqlui.selectTab('saved')"></input>
21
- <input id="structure-tab-button" class="tab-button" type="button" value="Structure" onclick="sqlui.selectTab('structure')"></input>
18
+ <a id="query-tab-button" class="tab-button">Query</a>
19
+ <a id="graph-tab-button" class="tab-button">Graph</a>
20
+ <a id="saved-tab-button" class="tab-button">Saved</a>
21
+ <a id="structure-tab-button" class="tab-button">Structure</a>
22
22
  </div>
23
23
 
24
24
  <div id="query-box" class="query-box tab-content-element graph-element query-element" style="display: none;">
@@ -26,8 +26,10 @@
26
26
  </div>
27
27
 
28
28
  <div id="submit-box" class="submit-box tab-content-element graph-element query-element" style="display: none;">
29
- <input id="submit-all-button" class="submit-button" type="button" value="execute (ctrl-shift-enter)" onclick="sqlui.submitAll();"></input>
30
- <input id="submit-current-button" class="submit-button" type="button" value="execute selection (ctrl-enter)" onclick="sqlui.submitCurrent();"></input>
29
+ <input id="cancel-button" class="submit-button" type="button" value="cancel"></input>
30
+ <div class="submit-fill"></div>
31
+ <input id="submit-current-button" class="submit-button" type="button" value="run selection (ctrl-enter)"></input>
32
+ <input id="submit-all-button" class="submit-button" type="button" value="run (ctrl-shift-enter)"></input>
31
33
  </div>
32
34
 
33
35
  <div id="result-box" class="result-box tab-content-element query-element" style="display: none;">
@@ -36,6 +38,10 @@
36
38
  <div id="graph-box" class="graph-box tab-content-element graph-element" style="display: none;">
37
39
  </div>
38
40
 
41
+ <div id="fetch-sql-box" class="fetch-sql-box tab-content-element graph-element query-element" style="display: none;">
42
+ <div id="result-loader" class="loader"></div>
43
+ </div>
44
+
39
45
  <div id="saved-box" class="saved-box tab-content-element saved-element" style="display: none;">
40
46
  </div>
41
47
 
@@ -55,7 +61,7 @@
55
61
  </div>
56
62
 
57
63
  <div id="status-box" class="status-box">
58
- <div id="query-status" class="status tab-content-element query-element"></div>
64
+ <div id="result-status" class="status tab-content-element query-element"></div>
59
65
  <div id="graph-status" class="status tab-content-element graph-element"></div>
60
66
  <div id="saved-status" class="status tab-content-element saved-element"></div>
61
67
  </div>
@@ -1,4 +1,4 @@
1
- var sqlui = (function (exports) {
1
+ (function () {
2
2
  'use strict';
3
3
 
4
4
  /**
@@ -23851,6 +23851,28 @@ var sqlui = (function (exports) {
23851
23851
  /* global google */
23852
23852
 
23853
23853
  function init (parent, onSubmit, onShiftSubmit) {
23854
+ document.getElementById('query-tab-button').addEventListener('click', function (event) {
23855
+ selectTab(event, 'query');
23856
+ });
23857
+ document.getElementById('saved-tab-button').addEventListener('click', function (event) {
23858
+ selectTab(event, 'saved');
23859
+ });
23860
+ document.getElementById('structure-tab-button').addEventListener('click', function (event) {
23861
+ selectTab(event, 'structure');
23862
+ });
23863
+ document.getElementById('graph-tab-button').addEventListener('click', function (event) {
23864
+ selectTab(event, 'graph');
23865
+ });
23866
+ document.getElementById('submit-all-button').addEventListener('click', function (event) {
23867
+ submitAll(event.target, event);
23868
+ });
23869
+ document.getElementById('submit-current-button').addEventListener('click', function (event) {
23870
+ submitCurrent(event.target, event);
23871
+ });
23872
+ document.getElementById('cancel-button').addEventListener('click', function (event) {
23873
+ clearResult();
23874
+ });
23875
+
23854
23876
  const fixedHeightEditor = EditorView.theme({
23855
23877
  '.cm-scroller': { height: '200px', overflow: 'auto', resize: 'vertical' }
23856
23878
  });
@@ -23872,18 +23894,27 @@ var sqlui = (function (exports) {
23872
23894
  }
23873
23895
 
23874
23896
  function getSelection () {
23875
- return [window.editorView.state.selection.main.from, window.editorView.state.selection.main.to]
23897
+ const anchor = window.editorView.state.selection.main.anchor;
23898
+ const head = window.editorView.state.selection.main.head;
23899
+ if (anchor === head) {
23900
+ return `${anchor}`
23901
+ } else {
23902
+ return `${anchor}-${head}`
23903
+ }
23876
23904
  }
23877
23905
 
23878
23906
  function setSelection (selection) {
23879
- window.editorView.dispatch(
23880
- {
23881
- selection: {
23882
- anchor: Math.min(selection[0], window.editorView.state.doc.length),
23883
- head: Math.min(selection[1], window.editorView.state.doc.length)
23884
- }
23885
- }
23886
- );
23907
+ let anchor;
23908
+ let head;
23909
+ if (selection.includes('-')) {
23910
+ selection = selection.split('-').map(x => parseInt(x));
23911
+ anchor = Math.min(selection[0], window.editorView.state.doc.length);
23912
+ head = Math.min(selection[1], window.editorView.state.doc.length);
23913
+ } else {
23914
+ anchor = Math.min(parseInt(selection), window.editorView.state.doc.length);
23915
+ head = anchor;
23916
+ }
23917
+ window.editorView.dispatch({ selection: { anchor, head } });
23887
23918
  }
23888
23919
 
23889
23920
  function focus () {
@@ -23904,30 +23935,62 @@ var sqlui = (function (exports) {
23904
23935
  });
23905
23936
  }
23906
23937
 
23907
- function selectTab (tab) {
23908
- window.tab = tab;
23938
+ function setTabInUrl (url, tab) {
23939
+ url.pathname = url.pathname.replace(/\/[^/]+$/, `/${tab}`);
23940
+ }
23941
+
23942
+ function getTabFromUrl (url) {
23943
+ const match = url.pathname.match(/\/([^/]+)$/);
23944
+ if (match && ['query', 'graph', 'structure', 'saved'].includes(match[1])) {
23945
+ return match[1]
23946
+ } else {
23947
+ throw new Error(`invalid tab: ${url.pathname}`)
23948
+ }
23949
+ }
23950
+
23951
+ function updateTabs () {
23909
23952
  const url = new URL(window.location);
23910
- if (url.searchParams.has('tab')) {
23911
- if (url.searchParams.get('tab') !== tab) {
23912
- if (tab === 'query') {
23913
- url.searchParams.delete('tab');
23914
- window.history.pushState({}, '', url);
23915
- return route()
23916
- } else {
23917
- url.searchParams.set('tab', tab);
23918
- window.history.pushState({}, '', url);
23919
- return route()
23953
+ setTabInUrl(url, 'graph');
23954
+ document.getElementById('graph-tab-button').href = url.pathname + url.search;
23955
+ setTabInUrl(url, 'saved');
23956
+ document.getElementById('saved-tab-button').href = url.pathname + url.search;
23957
+ setTabInUrl(url, 'structure');
23958
+ document.getElementById('structure-tab-button').href = url.pathname + url.search;
23959
+ setTabInUrl(url, 'query');
23960
+ document.getElementById('query-tab-button').href = url.pathname + url.search;
23961
+ }
23962
+
23963
+ function selectTab (event, tab) {
23964
+ const url = new URL(window.location);
23965
+ setTabInUrl(url, tab);
23966
+ route(event.target, event, url);
23967
+ }
23968
+
23969
+ function route (target = null, event = null, url = null) {
23970
+ if (url) {
23971
+ if (event) {
23972
+ event.preventDefault();
23973
+ if (!(target instanceof EditorView && event instanceof KeyboardEvent)) {
23974
+ if (event.shiftKey) {
23975
+ window.open(url).focus();
23976
+ return
23977
+ }
23978
+ if (event.metaKey) {
23979
+ window.open(url, '_blank').focus();
23980
+ return
23981
+ }
23920
23982
  }
23921
23983
  }
23922
- } else {
23923
- if (tab !== 'query') {
23924
- url.searchParams.set('tab', tab);
23984
+ if (url.href !== window.location.href) {
23925
23985
  window.history.pushState({}, '', url);
23926
- return route()
23927
23986
  }
23987
+ } else {
23988
+ url = new URL(window.location);
23928
23989
  }
23990
+ updateTabs();
23991
+ window.tab = getTabFromUrl(url);
23929
23992
 
23930
- const tabElement = document.getElementById(`${tab}-tab-button`);
23993
+ const tabElement = document.getElementById(`${window.tab}-tab-button`);
23931
23994
  Array.prototype.forEach.call(document.getElementsByClassName('selected-tab-button'), function (selected) {
23932
23995
  selected.classList.remove('selected-tab-button');
23933
23996
  selected.classList.add('tab-button');
@@ -23939,9 +24002,9 @@ var sqlui = (function (exports) {
23939
24002
  selected.style.display = 'none';
23940
24003
  });
23941
24004
 
23942
- switch (tab) {
24005
+ switch (window.tab) {
23943
24006
  case 'query':
23944
- selectQueryTab();
24007
+ selectResultTab();
23945
24008
  break
23946
24009
  case 'graph':
23947
24010
  selectGraphTab();
@@ -23953,7 +24016,7 @@ var sqlui = (function (exports) {
23953
24016
  selectStructureTab();
23954
24017
  break
23955
24018
  default:
23956
- throw new Error(`Unexpected tab: ${tab}`)
24019
+ throw new Error(`Unexpected tab: ${window.tab}`)
23957
24020
  }
23958
24021
  }
23959
24022
 
@@ -24096,42 +24159,30 @@ var sqlui = (function (exports) {
24096
24159
  }
24097
24160
 
24098
24161
  function selectGraphTab () {
24099
- Array.prototype.forEach.call(document.getElementsByClassName('graph-element'), function (selected) {
24100
- selected.style.display = 'flex';
24101
- });
24102
-
24103
- google.charts.load('current', { packages: ['corechart', 'line'] });
24104
- google.charts.setOnLoadCallback(function () {
24105
- loadQueryOrGraphTab(loadGraphResult, queryErrorCallback('graph-status'));
24106
- });
24162
+ document.getElementById('query-box').style.display = 'flex';
24163
+ document.getElementById('submit-box').style.display = 'flex';
24164
+ document.getElementById('graph-box').style.display = 'flex';
24165
+ document.getElementById('graph-status').style.display = 'flex';
24166
+ document.getElementById('fetch-sql-box').style.display = 'none';
24167
+ document.getElementById('cancel-button').style.display = 'none';
24168
+ maybeFetchResult();
24107
24169
 
24108
24170
  const selection = getSelection();
24109
24171
  focus();
24110
24172
  setSelection(selection);
24111
24173
  }
24112
24174
 
24113
- function selectQueryTab () {
24114
- Array.prototype.forEach.call(document.getElementsByClassName('query-element'), function (selected) {
24115
- selected.style.display = 'flex';
24116
- });
24117
-
24175
+ function selectResultTab () {
24176
+ document.getElementById('query-box').style.display = 'flex';
24177
+ document.getElementById('submit-box').style.display = 'flex';
24178
+ document.getElementById('result-box').style.display = 'flex';
24179
+ document.getElementById('result-status').style.display = 'flex';
24180
+ document.getElementById('fetch-sql-box').style.display = 'none';
24181
+ document.getElementById('cancel-button').style.display = 'none';
24118
24182
  const selection = getSelection();
24119
24183
  focus();
24120
24184
  setSelection(selection);
24121
-
24122
- loadQueryOrGraphTab(loadQueryResult, queryErrorCallback('query-status'));
24123
- }
24124
-
24125
- function queryErrorCallback (statusElementId) {
24126
- const statusElement = document.getElementById(statusElementId);
24127
- return function (message, error) {
24128
- if (error) {
24129
- console.log(`${message}\n${error}`);
24130
- statusElement.innerText = `error: ${message} (check console)`;
24131
- } else {
24132
- statusElement.innerText = `error: ${message}`;
24133
- }
24134
- }
24185
+ maybeFetchResult();
24135
24186
  }
24136
24187
 
24137
24188
  function selectSavedTab () {
@@ -24149,29 +24200,54 @@ var sqlui = (function (exports) {
24149
24200
  }
24150
24201
 
24151
24202
  const saved = window.metadata.saved;
24152
- if (saved && saved.length === 1) {
24203
+ if (Object.keys(saved).length === 1) {
24153
24204
  setSavedStatus('1 file');
24154
24205
  } else {
24155
24206
  setSavedStatus(`${saved.length} files`);
24156
24207
  }
24157
- saved.forEach(file => {
24158
- const divElement = document.createElement('div');
24159
- divElement.classList.add('saved-list-item');
24160
- divElement.addEventListener('click', function (event) {
24208
+ Object.values(saved).forEach(file => {
24209
+ const viewUrl = new URL(window.location.origin + window.location.pathname);
24210
+ setTabInUrl(viewUrl, 'query');
24211
+ viewUrl.searchParams.set('file', file.filename);
24212
+
24213
+ const viewLinkElement = document.createElement('a');
24214
+ viewLinkElement.classList.add('view-link');
24215
+ viewLinkElement.innerText = 'view';
24216
+ viewLinkElement.href = viewUrl.pathname + viewUrl.search;
24217
+ viewLinkElement.addEventListener('click', function (event) {
24161
24218
  clearResult();
24162
- const url = new URL(window.location);
24163
- url.searchParams.delete('sql');
24164
- url.searchParams.delete('tab');
24165
- url.searchParams.set('file', file.filename);
24166
- window.history.pushState({}, '', url);
24167
- route();
24219
+ route(event.target, event, viewUrl);
24220
+ });
24221
+
24222
+ const runUrl = new URL(window.location.origin + window.location.pathname);
24223
+ setTabInUrl(runUrl, 'query');
24224
+ runUrl.searchParams.set('file', file.filename);
24225
+ runUrl.searchParams.set('run', 'true');
24226
+
24227
+ const runLinkElement = document.createElement('a');
24228
+ runLinkElement.classList.add('run-link');
24229
+ runLinkElement.innerText = 'run';
24230
+ runLinkElement.href = runUrl.pathname + runUrl.search;
24231
+ runLinkElement.addEventListener('click', function (event) {
24232
+ clearResult();
24233
+ route(event.target, event, runUrl);
24168
24234
  });
24235
+
24169
24236
  const nameElement = document.createElement('h2');
24170
24237
  nameElement.innerText = file.filename;
24171
- divElement.appendChild(nameElement);
24238
+
24239
+ const nameAndLinksElement = document.createElement('div');
24240
+ nameAndLinksElement.classList.add('name-and-links');
24241
+ nameAndLinksElement.appendChild(nameElement);
24242
+ nameAndLinksElement.appendChild(viewLinkElement);
24243
+ nameAndLinksElement.appendChild(runLinkElement);
24172
24244
 
24173
24245
  const descriptionElement = document.createElement('p');
24174
24246
  descriptionElement.innerText = file.description;
24247
+
24248
+ const divElement = document.createElement('div');
24249
+ divElement.classList.add('saved-list-item');
24250
+ divElement.appendChild(nameAndLinksElement);
24175
24251
  divElement.appendChild(descriptionElement);
24176
24252
 
24177
24253
  savedElement.appendChild(divElement);
@@ -24179,59 +24255,71 @@ var sqlui = (function (exports) {
24179
24255
  window.savedLoaded = true;
24180
24256
  }
24181
24257
 
24182
- function submitAll () {
24183
- submit(null);
24258
+ function submitAll (target, event) {
24259
+ submit(target, event);
24184
24260
  }
24185
24261
 
24186
- function submitCurrent () {
24187
- submit(getSelection());
24262
+ function submitCurrent (target, event) {
24263
+ submit(target, event, getSelection());
24188
24264
  }
24189
24265
 
24190
- function submit (selection) {
24266
+ function submit (target, event, selection = null) {
24267
+ clearResult();
24191
24268
  const url = new URL(window.location);
24192
- if (selection) {
24193
- url.searchParams.set('selection', selection.join(':'));
24194
- } else {
24195
- url.searchParams.delete('selection');
24196
- }
24197
-
24198
24269
  let sql = getValue().trim();
24199
24270
  sql = sql === '' ? null : sql;
24200
24271
 
24272
+ url.searchParams.set('run', 'true');
24273
+
24201
24274
  if (url.searchParams.has('file')) {
24202
- url.searchParams.delete('file');
24203
- url.searchParams.set('sql', sql);
24204
- window.history.pushState({}, '', url);
24275
+ if (window.metadata.saved[url.searchParams.get('file')].contents !== getValue()) {
24276
+ url.searchParams.delete('file');
24277
+ url.searchParams.set('sql', sql);
24278
+ }
24205
24279
  } else {
24206
24280
  let sqlParam = url.searchParams.get('sql')?.trim();
24207
24281
  sqlParam = sqlParam === '' ? null : sqlParam;
24208
24282
 
24209
- if (sqlParam !== sql) {
24210
- if (sql === null) {
24211
- url.searchParams.delete('sql');
24212
- window.history.pushState({}, '', url);
24213
- } else {
24214
- url.searchParams.set('sql', sql);
24215
- window.history.pushState({}, '', url);
24216
- }
24283
+ if (sqlParam !== sql && sql === null) {
24284
+ url.searchParams.delete('sql');
24285
+ } else if (sqlParam !== sql) {
24286
+ url.searchParams.set('sql', sql);
24287
+ }
24288
+ }
24289
+
24290
+ if (sql) {
24291
+ if (selection) {
24292
+ url.searchParams.set('selection', selection);
24217
24293
  } else {
24218
- window.history.replaceState({}, '', url);
24294
+ url.searchParams.delete('selection');
24219
24295
  }
24296
+ } else {
24297
+ url.searchParams.delete('selection');
24298
+ url.searchParams.delete('sql');
24299
+ url.searchParams.delete('file');
24300
+ url.searchParams.delete('run');
24220
24301
  }
24221
- clearResult();
24222
- route();
24302
+
24303
+ route(target, event, url);
24223
24304
  }
24224
24305
 
24225
24306
  function clearResult () {
24226
- clearGraphStatus();
24227
- clearQueryStatus();
24307
+ const existingFetch = window.sqlFetch;
24308
+ if (existingFetch?.state === 'pending') {
24309
+ existingFetch.state = 'aborted';
24310
+ existingFetch.fetchController.abort();
24311
+ }
24312
+ window.sqlFetch = null;
24313
+
24228
24314
  clearGraphBox();
24315
+ clearGraphStatus();
24316
+
24229
24317
  clearResultBox();
24230
- window.result = null;
24318
+ clearResultStatus();
24231
24319
  }
24232
24320
 
24233
- function clearQueryStatus () {
24234
- document.getElementById('query-status').innerText = '';
24321
+ function clearResultStatus () {
24322
+ document.getElementById('result-status').innerText = '';
24235
24323
  }
24236
24324
 
24237
24325
  function clearGraphStatus () {
@@ -24252,7 +24340,7 @@ var sqlui = (function (exports) {
24252
24340
  }
24253
24341
  }
24254
24342
 
24255
- function fetchSql (sql, selection, successCallback, errorCallback) {
24343
+ function fetchSql (sqlFetch, selection, callback) {
24256
24344
  fetch('query', {
24257
24345
  headers: {
24258
24346
  Accept: 'application/json',
@@ -24260,137 +24348,173 @@ var sqlui = (function (exports) {
24260
24348
  },
24261
24349
  method: 'POST',
24262
24350
  body: JSON.stringify({
24263
- sql,
24351
+ sql: sqlFetch.sql,
24264
24352
  selection
24265
- })
24353
+ }),
24354
+ signal: sqlFetch.fetchController.signal
24266
24355
  })
24267
24356
  .then((response) => {
24268
24357
  const contentType = response.headers.get('content-type');
24269
24358
  if (contentType && contentType.indexOf('application/json') !== -1) {
24270
24359
  response.json().then((result) => {
24271
24360
  if (result?.query) {
24272
- successCallback(result);
24273
- } else if (result?.error) {
24274
- errorCallback(result.error, result.stacktrace);
24275
- } else if (result) {
24276
- errorCallback('failed to execute query', result.toString());
24361
+ sqlFetch.state = 'success';
24362
+ sqlFetch.result = result;
24277
24363
  } else {
24278
- errorCallback('failed to execute query');
24364
+ sqlFetch.state = 'error';
24365
+ if (result?.error) {
24366
+ sqlFetch.error_message = result.error;
24367
+ sqlFetch.error_details = result.stacktrace;
24368
+ } else if (result) {
24369
+ sqlFetch.error_message = 'failed to execute query';
24370
+ sqlFetch.error_details = result.toString();
24371
+ } else {
24372
+ sqlFetch.error_message = 'failed to execute query';
24373
+ }
24279
24374
  }
24375
+ callback(sqlFetch);
24280
24376
  });
24281
24377
  } else {
24282
24378
  response.text().then((result) => {
24283
- errorCallback('failed to execute query', result);
24379
+ sqlFetch.state = 'error';
24380
+ sqlFetch.error_message = 'failed to execute query';
24381
+ sqlFetch.error_details = result;
24382
+ callback(sqlFetch);
24284
24383
  });
24285
24384
  }
24286
24385
  })
24287
24386
  .catch(function (error) {
24288
- errorCallback('failed to execute query', error.stack);
24289
- });
24290
- }
24291
-
24292
- function fetchFile (name, successCallback, errorCallback) {
24293
- fetch(`query_file?file=${name}`, {
24294
- headers: {
24295
- Accept: 'application/json'
24296
- },
24297
- method: 'GET'
24298
- })
24299
- .then((response) => {
24300
- const contentType = response.headers.get('content-type');
24301
- if (contentType && contentType.indexOf('application/json') !== -1) {
24302
- response.json().then((result) => {
24303
- if (result?.query) {
24304
- successCallback(result);
24305
- } else if (result?.error) {
24306
- errorCallback(result.error, result.stacktrace);
24307
- } else if (result) {
24308
- errorCallback('failed to load file ', result.toString());
24309
- } else {
24310
- errorCallback('failed to load file');
24311
- }
24312
- });
24313
- } else {
24314
- errorCallback('failed to load file', response.toString());
24387
+ if (sqlFetch.state === 'pending') {
24388
+ sqlFetch.state = 'error';
24389
+ sqlFetch.error_message = 'failed to execute query';
24390
+ sqlFetch.error_details = error;
24315
24391
  }
24316
- })
24317
- .catch(function (error) {
24318
- errorCallback('failed to load file', error.stack);
24392
+ callback(sqlFetch);
24319
24393
  });
24320
24394
  }
24321
24395
 
24322
- function loadQueryOrGraphTab (callback, errorCallback) {
24323
- const params = new URLSearchParams(window.location.search);
24396
+ function maybeFetchResult () {
24397
+ const url = new URL(window.location);
24398
+ const params = url.searchParams;
24324
24399
  const sql = params.get('sql');
24325
24400
  const file = params.get('file');
24326
- const selection = params.has('selection') ? params.get('selection') : null;
24401
+ const selection = params.get('selection');
24402
+ const run = ['1', 'true'].includes(params.get('run')?.toLowerCase());
24327
24403
 
24328
- if (params.has('sql') && window.result && sql === window.result.query) {
24329
- callback();
24330
- return
24331
- } else if (params.has('file') && window.result && file === window.result.file) {
24332
- callback();
24333
- return
24334
- }
24335
-
24336
- if (params.has('file') && params.has('sql') && selection === window.result.selection) {
24404
+ if (params.has('file') && params.has('sql')) {
24337
24405
  // TODO: show an error.
24338
24406
  throw new Error('You can only specify a file or sql, not both.')
24339
24407
  }
24340
24408
 
24409
+ const sqlFetch = {
24410
+ fetchController: new AbortController(),
24411
+ state: 'pending',
24412
+ sql,
24413
+ file,
24414
+ selection
24415
+ };
24416
+
24417
+ if (params.has('file')) {
24418
+ const fileDetails = window.metadata.saved[params.get('file')];
24419
+ if (!fileDetails) {
24420
+ throw new Error(`no such file: ${params.get('file')}`)
24421
+ }
24422
+ sqlFetch.file = file;
24423
+ sqlFetch.sql = fileDetails.contents;
24424
+ } else if (params.has('sql')) {
24425
+ sqlFetch.sql = sql;
24426
+ }
24427
+
24428
+ const existingRequest = window.sqlFetch;
24429
+ if (existingRequest) {
24430
+ const selectionMatches = selection === existingRequest.selection;
24431
+ const sqlMatches = params.has('sql') && sql === existingRequest.sql;
24432
+ const fileMatches = params.has('file') && file === existingRequest.file;
24433
+ const queryMatches = sqlMatches || fileMatches;
24434
+ if (selectionMatches && queryMatches) {
24435
+ displaySqlFetch(existingRequest);
24436
+ if (params.has('selection')) {
24437
+ focus();
24438
+ setSelection(selection);
24439
+ }
24440
+ return
24441
+ }
24442
+ }
24443
+
24341
24444
  clearResult();
24342
24445
 
24343
- if (params.has('sql')) {
24344
- setValue(sql);
24345
- fetchSql(params.get('sql'), selection, function (result) {
24346
- window.result = result;
24347
- callback();
24348
- }, errorCallback);
24349
- } else if (params.has('file')) {
24350
- setValue('');
24351
- fetchFile(file, function (result) {
24352
- window.result = result;
24353
- setValue(result.query);
24354
- callback();
24355
- }, errorCallback);
24446
+ if (params.has('sql') || params.has('file')) {
24447
+ setValue(sqlFetch.sql);
24448
+ if (run) {
24449
+ url.searchParams.delete('run');
24450
+ window.history.replaceState({}, '', url);
24451
+ window.sqlFetch = sqlFetch;
24452
+ displaySqlFetch(sqlFetch);
24453
+ fetchSql(sqlFetch, selection, displaySqlFetch);
24454
+ }
24356
24455
  }
24357
24456
  if (params.has('selection')) {
24358
24457
  focus();
24359
- setSelection(params.get('selection').split(':'));
24458
+ setSelection(selection);
24360
24459
  }
24361
24460
  }
24362
24461
 
24363
- function loadQueryResult () {
24364
- const resultElement = document.getElementById('result-box');
24365
- if (resultElement.children.length > 0) {
24462
+ function displaySqlFetchInResultTab (fetch) {
24463
+ if (fetch.state === 'pending') {
24464
+ clearResultBox();
24465
+ document.getElementById('cancel-button').style.display = 'flex';
24466
+ document.getElementById('result-box').style.display = 'none';
24467
+ document.getElementById('fetch-sql-box').style.display = 'flex';
24468
+ return
24469
+ }
24470
+
24471
+ document.getElementById('cancel-button').style.display = 'none';
24472
+ document.getElementById('fetch-sql-box').style.display = 'none';
24473
+ document.getElementById('result-box').style.display = 'flex';
24474
+
24475
+ if (fetch.state === 'aborted') {
24476
+ clearResultBox();
24366
24477
  return
24367
24478
  }
24368
24479
 
24369
- setQueryStatus(window.result);
24480
+ if (fetch.state === 'error') {
24481
+ clearResultBox();
24482
+ displaySqlFetchError('result-status', fetch.error_message, fetch.error_details);
24483
+ return
24484
+ }
24370
24485
 
24371
- // if (!window.result.rows) {
24372
- // return
24373
- // }
24486
+ if (fetch.state !== 'success') {
24487
+ throw new Error(`unexpected fetch sql request status: ${fetch.status}`)
24488
+ }
24489
+
24490
+ if (document.getElementById('result-table')) {
24491
+ // Results already displayed.
24492
+ return
24493
+ }
24494
+
24495
+ clearResultBox();
24496
+ displaySqlFetchResultStatus('result-status', fetch.result);
24374
24497
 
24375
24498
  const tableElement = document.createElement('table');
24499
+ tableElement.id = 'result-table';
24376
24500
  const theadElement = document.createElement('thead');
24377
24501
  const headerElement = document.createElement('tr');
24378
24502
  const tbodyElement = document.createElement('tbody');
24379
24503
  theadElement.appendChild(headerElement);
24380
24504
  tableElement.appendChild(theadElement);
24381
24505
  tableElement.appendChild(tbodyElement);
24382
- resultElement.appendChild(tableElement);
24506
+ document.getElementById('result-box').appendChild(tableElement);
24383
24507
 
24384
- window.result.columns.forEach(column => {
24508
+ fetch.result.columns.forEach(column => {
24385
24509
  const template = document.createElement('template');
24386
24510
  template.innerHTML = `<th class="cell">${column}</th>`;
24387
24511
  headerElement.appendChild(template.content.firstChild);
24388
24512
  });
24389
- if (window.result.columns.length > 0) {
24513
+ if (fetch.result.columns.length > 0) {
24390
24514
  headerElement.appendChild(document.createElement('th'));
24391
24515
  }
24392
24516
  let highlight = false;
24393
- window.result.rows.forEach(function (row) {
24517
+ fetch.result.rows.forEach(function (row) {
24394
24518
  const rowElement = document.createElement('tr');
24395
24519
  if (highlight) {
24396
24520
  rowElement.classList.add('highlighted-row');
@@ -24404,29 +24528,72 @@ var sqlui = (function (exports) {
24404
24528
  });
24405
24529
  rowElement.appendChild(document.createElement('td'));
24406
24530
  });
24531
+ }
24407
24532
 
24408
- document.getElementById('result-box').style.display = 'flex';
24533
+ function displaySqlFetch (fetch) {
24534
+ if (window.tab === 'query') {
24535
+ displaySqlFetchInResultTab(fetch);
24536
+ } else if (window.tab === 'graph') {
24537
+ displaySqlFetchInGraphTab(fetch);
24538
+ }
24539
+ }
24540
+
24541
+ function displaySqlFetchError (statusElementId, message, details) {
24542
+ const statusElement = document.getElementById(statusElementId);
24543
+ if (details) {
24544
+ console.log(`${message}\n${details}`);
24545
+ statusElement.innerText = `error: ${message} (check console)`;
24546
+ } else {
24547
+ statusElement.innerText = `error: ${message}`;
24548
+ }
24409
24549
  }
24410
24550
 
24411
- function loadGraphResult () {
24412
- setGraphStatus(window.result);
24551
+ function displaySqlFetchInGraphTab (fetch) {
24552
+ if (fetch.state === 'pending') {
24553
+ clearGraphBox();
24554
+ document.getElementById('cancel-button').style.display = 'flex';
24555
+ document.getElementById('graph-box').style.display = 'none';
24556
+ document.getElementById('fetch-sql-box').style.display = 'flex';
24557
+ return
24558
+ }
24559
+
24560
+ document.getElementById('cancel-button').style.display = 'none';
24561
+ document.getElementById('fetch-sql-box').style.display = 'none';
24562
+ document.getElementById('graph-box').style.display = 'flex';
24563
+
24564
+ if (fetch.state === 'aborted') {
24565
+ clearGraphBox();
24566
+ return
24567
+ }
24413
24568
 
24414
- if (!window.result.rows) {
24569
+ if (fetch.state === 'error') {
24570
+ clearGraphBox();
24571
+ displaySqlFetchError('graph-status', fetch.error_message, fetch.error_details);
24415
24572
  return
24416
24573
  }
24417
- if (window.result.rows.length === 0 || window.result.columns.length < 2) {
24574
+
24575
+ if (fetch.state !== 'success') {
24576
+ throw new Error(`unexpected fetch sql request status: ${fetch.status}`)
24577
+ }
24578
+ clearGraphBox();
24579
+ displaySqlFetchResultStatus('graph-status', fetch.result);
24580
+
24581
+ if (!fetch.result.rows) {
24582
+ return
24583
+ }
24584
+ if (fetch.result.rows.length === 0 || fetch.result.columns.length < 2) {
24418
24585
  return
24419
24586
  }
24420
24587
  const dataTable = new google.visualization.DataTable();
24421
- window.result.columns.forEach((column, index) => {
24422
- dataTable.addColumn(window.result.column_types[index], column);
24588
+ fetch.result.columns.forEach((column, index) => {
24589
+ dataTable.addColumn(fetch.result.column_types[index], column);
24423
24590
  });
24424
24591
 
24425
- window.result.rows.forEach((row) => {
24592
+ fetch.result.rows.forEach((row) => {
24426
24593
  const rowValues = row.map((value, index) => {
24427
- if (window.result.column_types[index] === 'date' || window.result.column_types[index] === 'datetime') {
24594
+ if (fetch.result.column_types[index] === 'date' || fetch.result.column_types[index] === 'datetime') {
24428
24595
  return new Date(value)
24429
- } else if (window.result.column_types[index] === 'timeofday') {
24596
+ } else if (fetch.result.column_types[index] === 'timeofday') {
24430
24597
  // TODO: This should be hour, minute, second, milliseconds
24431
24598
  return [0, 0, 0, 0]
24432
24599
  } else {
@@ -24436,36 +24603,20 @@ var sqlui = (function (exports) {
24436
24603
  dataTable.addRow(rowValues);
24437
24604
  });
24438
24605
 
24439
- const graphBoxElement = document.getElementById('graph-box');
24440
-
24441
- const chart = new google.visualization.LineChart(graphBoxElement);
24606
+ const chart = new google.visualization.LineChart(document.getElementById('graph-box'));
24442
24607
  const options = {
24443
24608
  hAxis: {
24444
- title: window.result.columns[0]
24609
+ title: fetch.result.columns[0]
24445
24610
  },
24446
24611
  vAxis: {
24447
- title: window.result.columns[1]
24612
+ title: fetch.result.columns[1]
24448
24613
  }
24449
24614
  };
24450
24615
  chart.draw(dataTable, options);
24451
24616
  }
24452
24617
 
24453
- function setGraphStatus (result) {
24454
- const statusElement = document.getElementById('graph-status');
24455
-
24456
- if (result.total_rows === 1) {
24457
- statusElement.innerText = `${result.total_rows} row`;
24458
- } else {
24459
- statusElement.innerText = `${result.total_rows} rows`;
24460
- }
24461
-
24462
- if (result.total_rows > result.rows.length) {
24463
- statusElement.innerText += ` (truncated to ${result.rows.length})`;
24464
- }
24465
- }
24466
-
24467
- function setQueryStatus (result) {
24468
- const statusElement = document.getElementById('query-status');
24618
+ function displaySqlFetchResultStatus (statusElementId, result) {
24619
+ const statusElement = document.getElementById(statusElementId);
24469
24620
 
24470
24621
  if (result.total_rows === 1) {
24471
24622
  statusElement.innerText = `${result.total_rows} row`;
@@ -24487,24 +24638,24 @@ var sqlui = (function (exports) {
24487
24638
  });
24488
24639
 
24489
24640
  window.addEventListener('resize', function (event) {
24490
- if (window.tab === 'graph' && window.result) {
24641
+ if (window.tab === 'graph' && window.sqlFetch.result) {
24491
24642
  clearGraphBox();
24492
- loadGraphResult();
24643
+ displaySqlFetchInGraphTab(window.sqlFetch);
24493
24644
  }
24494
24645
  });
24495
24646
 
24496
- function route () {
24497
- selectTab(new URLSearchParams(window.location.search).get('tab') || 'query');
24498
- }
24499
-
24500
24647
  window.onload = function () {
24501
- fetch('metadata', {
24502
- headers: {
24503
- Accept: 'application/json'
24504
- },
24505
- method: 'GET'
24506
- })
24507
- .then((response) => {
24648
+ Promise.all([
24649
+ google.charts.load('current', { packages: ['corechart', 'line'] }),
24650
+ fetch('metadata', {
24651
+ headers: {
24652
+ Accept: 'application/json'
24653
+ },
24654
+ method: 'GET'
24655
+ })
24656
+ ])
24657
+ .then((results) => {
24658
+ const response = results[1];
24508
24659
  const contentType = response.headers.get('content-type');
24509
24660
  if (contentType && contentType.indexOf('application/json') !== -1) {
24510
24661
  return response.json().then((result) => {
@@ -24525,7 +24676,9 @@ var sqlui = (function (exports) {
24525
24676
  window.metadata = result;
24526
24677
  document.getElementById('loading-box').style.display = 'none';
24527
24678
  document.getElementById('main-box').style.display = 'flex';
24528
- document.getElementById('server-name').innerText = result.server;
24679
+ document.getElementById('server-name').innerText = window.metadata.server;
24680
+ document.title = `SQLUI ${window.metadata.server}`;
24681
+ document.getElementById('header-link').href = result.list_url_path;
24529
24682
  const queryElement = document.getElementById('query');
24530
24683
 
24531
24684
  init(queryElement, submitCurrent, submitAll);
@@ -24547,10 +24700,4 @@ var sqlui = (function (exports) {
24547
24700
  });
24548
24701
  };
24549
24702
 
24550
- exports.selectTab = selectTab;
24551
- exports.submitAll = submitAll;
24552
- exports.submitCurrent = submitCurrent;
24553
-
24554
- return exports;
24555
-
24556
- })({});
24703
+ })();
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqlui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.24
4
+ version: 0.1.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Dower
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-28 00:00:00.000000000 Z
11
+ date: 2022-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mysql2