sqlui 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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