ripple-sql 0.1.0__py3-none-any.whl
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.
- ripple/__init__.py +31 -0
- ripple/answer.py +473 -0
- ripple/answer_page.py +214 -0
- ripple/cache.py +80 -0
- ripple/ci.py +422 -0
- ripple/ci_signature.py +374 -0
- ripple/cli.py +733 -0
- ripple/doctor.py +225 -0
- ripple/engine/__init__.py +111 -0
- ripple/engine/budget.py +86 -0
- ripple/engine/column_lineage.py +112 -0
- ripple/engine/column_ref.py +818 -0
- ripple/engine/cte_tracing.py +1309 -0
- ripple/engine/dependencies.py +466 -0
- ripple/engine/dialect.py +132 -0
- ripple/engine/dispatch.py +12 -0
- ripple/engine/extraction.py +27 -0
- ripple/engine/jinja.py +282 -0
- ripple/engine/json_sources.py +241 -0
- ripple/engine/macro_source.py +127 -0
- ripple/engine/pipeline.py +265 -0
- ripple/engine/preprocess.py +174 -0
- ripple/engine/safe_gen.py +21 -0
- ripple/engine/schema_qualification.py +151 -0
- ripple/engine/scope.py +488 -0
- ripple/engine/select_sources.py +1038 -0
- ripple/engine/sql_script.py +729 -0
- ripple/engine/statement.py +449 -0
- ripple/engine/tech_debt.py +169 -0
- ripple/engine/tsql_catalog.py +83 -0
- ripple/engine/tsql_scalar_vars.py +248 -0
- ripple/engine/tsql_tvf.py +653 -0
- ripple/engine/tsql_xml.py +97 -0
- ripple/engine/types.py +167 -0
- ripple/engine/unused_deps.py +555 -0
- ripple/engine/validation.py +158 -0
- ripple/graph.py +1499 -0
- ripple/home.py +232 -0
- ripple/loaders/__init__.py +7 -0
- ripple/loaders/dbt.py +359 -0
- ripple/loaders/dbt_config.py +339 -0
- ripple/loaders/identity.py +328 -0
- ripple/loaders/sidecar.py +65 -0
- ripple/loaders/sqldir.py +262 -0
- ripple/loaders/types.py +197 -0
- ripple/lookml.py +163 -0
- ripple/mcp_server.py +600 -0
- ripple/names.py +40 -0
- ripple/project.py +167 -0
- ripple/py.typed +0 -0
- ripple/render.py +426 -0
- ripple/render_shims.py +209 -0
- ripple/schemas.py +155 -0
- ripple/semantic.py +232 -0
- ripple/server.py +184 -0
- ripple/sourcefiles.py +64 -0
- ripple/star_resolution.py +100 -0
- ripple/static/answer.css +146 -0
- ripple/static/answer.html +358 -0
- ripple/static/answer_twin.js +299 -0
- ripple/static/explore.js +133 -0
- ripple/usage/__init__.py +18 -0
- ripple/usage/cli.py +78 -0
- ripple/usage/collect.py +315 -0
- ripple/usage/discover.py +190 -0
- ripple/usage/ingest.py +414 -0
- ripple/usage/report.py +131 -0
- ripple_sql-0.1.0.dist-info/METADATA +285 -0
- ripple_sql-0.1.0.dist-info/RECORD +72 -0
- ripple_sql-0.1.0.dist-info/WHEEL +4 -0
- ripple_sql-0.1.0.dist-info/entry_points.txt +3 -0
- ripple_sql-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>__TITLE__</title>
|
|
7
|
+
<style>
|
|
8
|
+
/*CSS*/
|
|
9
|
+
</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div class="wrap">
|
|
13
|
+
<p class="eyebrow"><span class="mark" aria-hidden="true"></span><span id="eyebrow">Ripple</span></p>
|
|
14
|
+
<h1 id="title"></h1>
|
|
15
|
+
<p class="sum" id="sum"></p>
|
|
16
|
+
|
|
17
|
+
<div class="change" id="change" hidden><span class="lab">In this change</span></div>
|
|
18
|
+
|
|
19
|
+
<form class="ask" id="ask" hidden>
|
|
20
|
+
<input id="q" list="cols" placeholder="ask about another column: model.column" autocomplete="off" spellcheck="false">
|
|
21
|
+
<datalist id="cols"></datalist>
|
|
22
|
+
<select id="dir" aria-label="direction"><option value="breaks">what breaks</option><option value="trace">where it comes from</option></select>
|
|
23
|
+
<button type="submit">Ask</button>
|
|
24
|
+
<p class="fine" id="askfine">Answered inside this file. Nothing sent.</p>
|
|
25
|
+
<div class="starters" id="starters" hidden><span class="lab">Start with</span></div>
|
|
26
|
+
</form>
|
|
27
|
+
|
|
28
|
+
<section class="map" id="map" hidden aria-label="every model in this project">
|
|
29
|
+
<div class="board"><div class="board-inner" id="mapboard"><svg class="wires" id="mapwires" aria-hidden="true"></svg></div></div>
|
|
30
|
+
<div class="board-foot">
|
|
31
|
+
<span class="key"><i></i> feeds</span>
|
|
32
|
+
<span class="key"><s class="src"></s> a source table</span>
|
|
33
|
+
<span class="key"><s></s> a dashboard number</span>
|
|
34
|
+
<span class="key"><i class="r"></i> needs review</span>
|
|
35
|
+
<span class="hint" id="maphint">Hover lights the chain. Click a model for its columns, a column to ask.</span>
|
|
36
|
+
</div>
|
|
37
|
+
</section>
|
|
38
|
+
|
|
39
|
+
<div class="demo">
|
|
40
|
+
<div class="nav" id="nav" hidden><button type="button" id="tomap">← All models</button></div>
|
|
41
|
+
<div class="board"><div class="board-inner" id="board"><svg class="wires" id="wires" aria-hidden="true"></svg></div></div>
|
|
42
|
+
<div class="board-foot">
|
|
43
|
+
<span class="key"><i></i> is built from</span>
|
|
44
|
+
<span class="key"><s></s> a dashboard number</span>
|
|
45
|
+
<span class="key"><i class="r"></i> needs review</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="rows" id="rows" hidden></div>
|
|
48
|
+
<div class="term">
|
|
49
|
+
<div class="term-bar"><span class="term-title" id="termtitle">As the command prints it</span><button type="button" id="showtext" aria-expanded="false" aria-controls="out">Show text</button><button type="button" id="copy">Copy text</button></div>
|
|
50
|
+
<pre id="out" hidden></pre>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<section class="ladder" id="ladder" hidden></section>
|
|
54
|
+
<p class="foot" id="foot"></p>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<script id="data" type="application/json">__DATA__</script>
|
|
58
|
+
<script>
|
|
59
|
+
(function(){
|
|
60
|
+
var DATA = JSON.parse(document.getElementById('data').textContent);
|
|
61
|
+
var PREVIEW_MODELS = 8, PREVIEW_COLUMNS = 5;
|
|
62
|
+
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
63
|
+
var board = document.getElementById('board'), wires = document.getElementById('wires');
|
|
64
|
+
var noun = (DATA.project && DATA.project.mode === 'sql-dir') ? 'table' : 'model';
|
|
65
|
+
var current = DATA.answer;
|
|
66
|
+
var demo = document.querySelector('.demo'), fine = document.getElementById('askfine');
|
|
67
|
+
var mapEl = document.getElementById('map'), nav = document.getElementById('nav'), mapApi = null;
|
|
68
|
+
|
|
69
|
+
function esc(s){ return String(s).replace(/&/g,'&').replace(/</g,'<'); }
|
|
70
|
+
|
|
71
|
+
/*TWIN*/
|
|
72
|
+
|
|
73
|
+
/*EXPLORE*/
|
|
74
|
+
|
|
75
|
+
function show(a, text){
|
|
76
|
+
current = a;
|
|
77
|
+
demo.hidden = false;
|
|
78
|
+
mapEl.hidden = true;
|
|
79
|
+
nav.hidden = !mapApi;
|
|
80
|
+
if (mapApi && L.starters.length) document.getElementById('starters').hidden = false;
|
|
81
|
+
document.getElementById('eyebrow').textContent = 'Ripple · ' + (DATA.change ? 'what this change breaks' : (a.kind === 'trace' ? 'where it comes from' : 'what breaks'));
|
|
82
|
+
document.getElementById('title').innerHTML = a.kind === 'trace'
|
|
83
|
+
? 'Where does <code>' + esc(a.target) + '</code> come from?'
|
|
84
|
+
: 'What breaks if <code>' + esc(a.target) + '</code> changes?';
|
|
85
|
+
var s = a.summary, bits = [];
|
|
86
|
+
bits.push('<b>' + plural(s.columns, 'column') + '</b>');
|
|
87
|
+
if (s.models) bits.push(plural(s.models, a.noun));
|
|
88
|
+
if (s.dashboard_numbers) bits.push(plural(s.dashboard_numbers, 'dashboard number'));
|
|
89
|
+
if (s.row_level_models) bits.push(plural(s.row_level_models, a.noun) + ' filtered on it');
|
|
90
|
+
if (s.review) bits.push('<b>' + s.review + ' need review</b>');
|
|
91
|
+
bits.push('deepest ' + plural(s.deepest_hops, 'step'));
|
|
92
|
+
if (!s.complete) bits.push('stopped at depth ' + a.truncated_at_depth);
|
|
93
|
+
document.getElementById('sum').innerHTML = hasHits(a)
|
|
94
|
+
? bits.map(function(b){ return '<span>' + b + '</span>'; }).join('')
|
|
95
|
+
: '<span>' + (a.kind === 'trace' ? 'No upstream lineage.' : 'Nothing downstream reads it.') + '</span>';
|
|
96
|
+
renderBoard(a);
|
|
97
|
+
renderRows(a);
|
|
98
|
+
document.getElementById('out').textContent = text;
|
|
99
|
+
document.title = a.kind === 'trace' ? 'Where does ' + a.target + ' come from?' : 'What breaks if ' + a.target + ' changes?';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function renderBoard(a){
|
|
103
|
+
Array.prototype.slice.call(board.querySelectorAll('.lvl')).forEach(function(el){ el.remove(); });
|
|
104
|
+
wires.innerHTML = '';
|
|
105
|
+
var via = {};
|
|
106
|
+
a.edges.forEach(function(e){ if (!(e.dst in via)) via[e.dst] = e; });
|
|
107
|
+
var byDepth = {}, maxD = 0;
|
|
108
|
+
a.nodes.forEach(function(n){ (byDepth[n.depth] = byDepth[n.depth] || []).push(n); if (n.depth > maxD) maxD = n.depth; });
|
|
109
|
+
var depths = []; for (var d = 0; d <= maxD; d++) if (byDepth[d]) depths.push(d);
|
|
110
|
+
if (a.kind === 'trace') depths.reverse();
|
|
111
|
+
depths.forEach(function(d){
|
|
112
|
+
var lvl = document.createElement('div'); lvl.className = 'lvl' + (d === 0 ? ' asked' : '');
|
|
113
|
+
var head = document.createElement('div'); head.className = 'lhead';
|
|
114
|
+
head.textContent = d === 0 ? (a.kind === 'trace' ? 'You asked about' : 'Your change')
|
|
115
|
+
: plural(d, 'step') + (a.kind === 'trace' ? ' back' : '');
|
|
116
|
+
lvl.appendChild(head);
|
|
117
|
+
var groups = {}, order = [];
|
|
118
|
+
byDepth[d].forEach(function(n){ if (!groups[n.model]){ groups[n.model] = []; order.push(n.model); } groups[n.model].push(n); });
|
|
119
|
+
order.forEach(function(m){
|
|
120
|
+
var g = document.createElement('div'); g.className = 'grp';
|
|
121
|
+
var name = document.createElement('div'); name.className = 'gname';
|
|
122
|
+
var kind = m.split(':')[0];
|
|
123
|
+
name.innerHTML = isDash(m) ? '<span class="bi">' + esc(kind) + '</span> ' + esc(m.slice(kind.length + 1)) : esc(m);
|
|
124
|
+
g.appendChild(name);
|
|
125
|
+
groups[m].forEach(function(n){
|
|
126
|
+
var s = document.createElement('span');
|
|
127
|
+
s.className = 'node' + (n.kind === 'asked' ? ' src' : '') + (n.kind === 'dashboard' ? ' bi' : '') +
|
|
128
|
+
(n.kind === 'unknown' ? ' unk' : '') + (n.trust === 'review_required' && n.kind !== 'asked' ? ' r' : '');
|
|
129
|
+
s.dataset.id = n.id; s.textContent = n.column;
|
|
130
|
+
var e = via[n.id];
|
|
131
|
+
if (e) s.title = n.id + ' ← ' + e.src + ' · ' + (e.path_trust || e.trust).replace('_', ' ') + (e.reason ? ' · ' + e.reason : '');
|
|
132
|
+
s.style.transitionDelay = reduce ? '0ms' : (d * 90) + 'ms';
|
|
133
|
+
g.appendChild(s);
|
|
134
|
+
});
|
|
135
|
+
lvl.appendChild(g);
|
|
136
|
+
});
|
|
137
|
+
board.appendChild(lvl);
|
|
138
|
+
});
|
|
139
|
+
requestAnimationFrame(function(){
|
|
140
|
+
Array.prototype.slice.call(board.querySelectorAll('.node')).forEach(function(el){ el.classList.add('in'); });
|
|
141
|
+
drawWires(a);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function drawWires(a){
|
|
146
|
+
wires.innerHTML = '';
|
|
147
|
+
if (window.innerWidth <= 680) return;
|
|
148
|
+
var base = board.getBoundingClientRect();
|
|
149
|
+
wires.setAttribute('width', board.scrollWidth); wires.setAttribute('height', board.scrollHeight);
|
|
150
|
+
var chips = {};
|
|
151
|
+
Array.prototype.slice.call(board.querySelectorAll('.node')).forEach(function(el){ chips[el.dataset.id] = el; });
|
|
152
|
+
var byId = {}; a.nodes.forEach(function(n){ byId[n.id] = n; });
|
|
153
|
+
a.edges.forEach(function(e){
|
|
154
|
+
var from = chips[e.src], to = chips[e.dst];
|
|
155
|
+
if (!from || !to) return;
|
|
156
|
+
var ra = from.getBoundingClientRect(), rb = to.getBoundingClientRect();
|
|
157
|
+
var x1 = ra.right - base.left, y1 = ra.top + ra.height / 2 - base.top;
|
|
158
|
+
var x2 = rb.left - base.left, y2 = rb.top + rb.height / 2 - base.top;
|
|
159
|
+
if (x2 < x1) { var t = x1; x1 = ra.left - base.left; x2 = rb.right - base.left; }
|
|
160
|
+
var dx = Math.max(18, Math.abs(x2 - x1) / 2);
|
|
161
|
+
var p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
162
|
+
p.setAttribute('d', 'M' + x1 + ' ' + y1 + ' C' + (x1 + dx) + ' ' + y1 + ' ' + (x2 - dx) + ' ' + y2 + ' ' + x2 + ' ' + y2);
|
|
163
|
+
var review = (e.path_trust || e.trust) === 'review_required';
|
|
164
|
+
if (review) p.setAttribute('class', 'r');
|
|
165
|
+
else if (e.src === a.target || e.dst === a.target) p.setAttribute('class', 'o');
|
|
166
|
+
wires.appendChild(p);
|
|
167
|
+
if (!reduce && !review){
|
|
168
|
+
var len = p.getTotalLength();
|
|
169
|
+
p.style.strokeDasharray = len; p.style.strokeDashoffset = len;
|
|
170
|
+
var depth = byId[e.dst] ? byId[e.dst].depth : 1;
|
|
171
|
+
p.style.transitionDelay = (depth * 90 - 30) + 'ms';
|
|
172
|
+
requestAnimationFrame(function(){ p.style.strokeDashoffset = 0; });
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function renderRows(a){
|
|
178
|
+
var el = document.getElementById('rows');
|
|
179
|
+
if (!a.row_level || !a.row_level.length){ el.hidden = true; return; }
|
|
180
|
+
el.hidden = false;
|
|
181
|
+
el.innerHTML = '<b>Filters rows on it:</b> ' + a.row_level.map(function(r){
|
|
182
|
+
return esc(r.model) + (r.trust === 'review_required' ? ' (review)' : '');
|
|
183
|
+
}).join(', ');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ---- the home state: ripple serve opens here, on the whole project ----
|
|
187
|
+
var P = DATA.project || {}, L = DATA.live;
|
|
188
|
+
function home(){
|
|
189
|
+
current = null;
|
|
190
|
+
demo.hidden = true;
|
|
191
|
+
nav.hidden = true;
|
|
192
|
+
document.getElementById('eyebrow').textContent = 'Ripple';
|
|
193
|
+
document.getElementById('title').textContent = P.root || 'this project';
|
|
194
|
+
document.getElementById('q').placeholder = 'model.column';
|
|
195
|
+
fine.textContent = LIVE_FINE;
|
|
196
|
+
var sources = L.map ? L.map.models.filter(function(m){ return m.kind === 'source'; }).length : 0;
|
|
197
|
+
var bits = [plural(P.models || 0, noun)];
|
|
198
|
+
if (sources) bits.push(plural(sources, 'source table'));
|
|
199
|
+
bits.push(plural(L.edges, 'column link'));
|
|
200
|
+
if (L.review) bits.push('<b>' + L.review + ' need review</b>');
|
|
201
|
+
document.getElementById('sum').innerHTML = bits.map(function(b){ return '<span>' + b + '</span>'; }).join('');
|
|
202
|
+
document.title = 'Ripple: ' + (P.root || 'local');
|
|
203
|
+
if (mapApi){
|
|
204
|
+
// the map is the place to start; the starter chips come back with an answer
|
|
205
|
+
document.getElementById('starters').hidden = true;
|
|
206
|
+
mapEl.hidden = false;
|
|
207
|
+
mapApi.filter(document.getElementById('q').value.trim());
|
|
208
|
+
requestAnimationFrame(mapApi.draw);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// SERVE START
|
|
213
|
+
var LIVE_FINE = 'Answered from your files as they are now, on this machine.';
|
|
214
|
+
// ripple serve: the question box asks the local server, which answers from
|
|
215
|
+
// the files as they are now. Relative path only; the file page never runs this.
|
|
216
|
+
if (L){
|
|
217
|
+
var list0 = document.getElementById('cols');
|
|
218
|
+
L.columns.forEach(function(id){ var o = document.createElement('option'); o.value = id; list0.appendChild(o); });
|
|
219
|
+
var form0 = document.getElementById('ask'); form0.hidden = false;
|
|
220
|
+
var strip0 = document.getElementById('starters');
|
|
221
|
+
if (L.starters.length){
|
|
222
|
+
strip0.hidden = false;
|
|
223
|
+
L.starters.forEach(function(st){
|
|
224
|
+
var b = document.createElement('button'); b.type = 'button';
|
|
225
|
+
b.textContent = st.id; b.title = 'read by ' + plural(st.readers, noun);
|
|
226
|
+
b.addEventListener('click', function(){ document.getElementById('q').value = st.id; ask(st.id, document.getElementById('dir').value); });
|
|
227
|
+
strip0.appendChild(b);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (L.map && L.map.models.length){
|
|
231
|
+
mapApi = renderMap(mapEl, L.map, {esc: esc, onAsk: function(id){ document.getElementById('q').value = id; ask(id, document.getElementById('dir').value); }});
|
|
232
|
+
if (mapApi.many) document.getElementById('maphint').textContent = 'Hover a model to see its links and chain. Click it for its columns, a column to ask.';
|
|
233
|
+
document.getElementById('q').addEventListener('input', function(){ if (!mapEl.hidden) mapApi.filter(this.value.trim()); });
|
|
234
|
+
document.getElementById('tomap').addEventListener('click', function(){ home(); history.replaceState(null, '', location.pathname); });
|
|
235
|
+
}
|
|
236
|
+
var ladder = document.getElementById('ladder'); ladder.hidden = false;
|
|
237
|
+
['Coverage', 'Usage'].forEach(function(name, i){
|
|
238
|
+
var row = L.ladder[i], p = document.createElement('p');
|
|
239
|
+
p.innerHTML = '<span class="rung">' + name + '</span><span><b>' + esc(row.lead || row.text) + '</b>' +
|
|
240
|
+
(row.cmd ? ' <code>' + esc(row.cmd) + '</code>' : '') +
|
|
241
|
+
(row.lead ? ' <button type="button" class="more" aria-expanded="false">details</button><span class="detail" hidden>' + esc(row.text) + '</span>' : '') + '</span>';
|
|
242
|
+
var more = p.querySelector('.more');
|
|
243
|
+
if (more) more.addEventListener('click', function(){
|
|
244
|
+
var d = p.querySelector('.detail'), open = d.hidden;
|
|
245
|
+
d.hidden = !open; more.setAttribute('aria-expanded', String(open)); more.textContent = open ? 'less' : 'details';
|
|
246
|
+
});
|
|
247
|
+
ladder.appendChild(p);
|
|
248
|
+
});
|
|
249
|
+
form0.addEventListener('submit', function(ev){
|
|
250
|
+
ev.preventDefault();
|
|
251
|
+
var target = document.getElementById('q').value.trim();
|
|
252
|
+
if (target) ask(target, document.getElementById('dir').value);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function ask(target, dir){
|
|
256
|
+
fine.textContent = 'asking the local server…';
|
|
257
|
+
fetch(L.api + '/' + dir + '?target=' + encodeURIComponent(target), {cache: 'no-store'}).then(function(r){
|
|
258
|
+
return r.json().then(function(j){ return [r.ok, j]; });
|
|
259
|
+
}).then(function(pair){
|
|
260
|
+
var ok = pair[0], j = pair[1];
|
|
261
|
+
if (!ok){
|
|
262
|
+
var msg = j.error || 'no answer';
|
|
263
|
+
if (j.did_you_mean && j.did_you_mean.length) msg += (/[.?!]$/.test(msg) ? '' : '.') + ' Did you mean ' + j.did_you_mean.join(', ') + '?';
|
|
264
|
+
fine.textContent = msg;
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
fine.textContent = LIVE_FINE;
|
|
268
|
+
show(j.answer, j.text);
|
|
269
|
+
history.replaceState(null, '', '?q=' + encodeURIComponent(target) + '&dir=' + dir);
|
|
270
|
+
}).catch(function(){ fine.textContent = 'The local server did not answer. Is ripple serve still running?'; });
|
|
271
|
+
}
|
|
272
|
+
// SERVE END
|
|
273
|
+
|
|
274
|
+
// ---- follow-up questions, answered from the embedded graph ----
|
|
275
|
+
var G = DATA.graph, index = G ? buildIndex(G) : null;
|
|
276
|
+
function offline(target, dir){ return dir === 'trace' ? computeTrace(index, target, noun) : computeBreaks(index, target, noun); }
|
|
277
|
+
if (G){
|
|
278
|
+
var list = document.getElementById('cols');
|
|
279
|
+
Object.keys(index.ids).sort().forEach(function(id){ if (id.indexOf('(') < 0 && id.slice(-2) !== '.*'){ var o = document.createElement('option'); o.value = id; list.appendChild(o); } });
|
|
280
|
+
var form = document.getElementById('ask'); form.hidden = false;
|
|
281
|
+
document.getElementById('q').value = '';
|
|
282
|
+
form.addEventListener('submit', function(ev){
|
|
283
|
+
ev.preventDefault();
|
|
284
|
+
var target = document.getElementById('q').value.trim(), dir = document.getElementById('dir').value;
|
|
285
|
+
if (!target) return;
|
|
286
|
+
var found = resolveTarget(index, target), fine2 = document.getElementById('askfine');
|
|
287
|
+
if (!found.id){
|
|
288
|
+
fine2.textContent = found.error + '.' + (found.candidates && found.candidates.length ? ' Did you mean: ' + found.candidates.join(', ') + '?' : '');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
fine2.textContent = 'Answered inside this file. Nothing sent.';
|
|
292
|
+
var a = offline(found.id, dir);
|
|
293
|
+
show(a, dir === 'trace' ? traceText(a) : breaksText(a));
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
document.getElementById('showtext').addEventListener('click', function(){
|
|
298
|
+
var out = document.getElementById('out'), open = out.hidden;
|
|
299
|
+
out.hidden = !open; this.setAttribute('aria-expanded', String(open)); this.textContent = open ? 'Hide text' : 'Show text';
|
|
300
|
+
});
|
|
301
|
+
document.getElementById('copy').addEventListener('click', function(){
|
|
302
|
+
var text = document.getElementById('out').textContent, btn = this;
|
|
303
|
+
function done(){ btn.textContent = 'Copied'; setTimeout(function(){ btn.textContent = 'Copy text'; }, 1400); }
|
|
304
|
+
if (navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(text).then(done, function(){ fallback(); }); } else fallback();
|
|
305
|
+
function fallback(){ var ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); done(); } catch (e) {} ta.remove(); }
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
var foot = ['Ripple ' + DATA.version, DATA.generated, L ? 'local server' : 'offline'];
|
|
309
|
+
if (P.root) foot.unshift(P.root + (P.models ? ', ' + P.models + ' ' + noun + 's' : '') + (P.dialect ? ', ' + P.dialect : ''));
|
|
310
|
+
if (DATA.note) foot.push(DATA.note);
|
|
311
|
+
document.getElementById('foot').innerHTML = foot.map(function(f){ return '<span>' + esc(f) + '</span>'; }).join('');
|
|
312
|
+
document.getElementById('termtitle').textContent = 'As the command prints it';
|
|
313
|
+
|
|
314
|
+
// a change page carries one view per changed column; chips switch between them
|
|
315
|
+
var params = new URLSearchParams(location.search), asked = params.get('q');
|
|
316
|
+
if (DATA.change && DATA.views.length){
|
|
317
|
+
var strip = document.getElementById('change'); strip.hidden = false;
|
|
318
|
+
DATA.views.forEach(function(v, i){
|
|
319
|
+
var b = document.createElement('button'); b.type = 'button';
|
|
320
|
+
b.textContent = v.answer.target + (v.answer.change ? ' · ' + v.answer.change.replace('_', ' ') : '');
|
|
321
|
+
b.setAttribute('aria-pressed', i === 0 ? 'true' : 'false');
|
|
322
|
+
b.addEventListener('click', function(){
|
|
323
|
+
Array.prototype.slice.call(strip.querySelectorAll('button')).forEach(function(x){ x.setAttribute('aria-pressed', x === b ? 'true' : 'false'); });
|
|
324
|
+
show(v.answer, v.text);
|
|
325
|
+
});
|
|
326
|
+
strip.appendChild(b);
|
|
327
|
+
});
|
|
328
|
+
var c = DATA.change, rb = document.createElement('p'); rb.className = 'rebuild';
|
|
329
|
+
var counts = plural(c.impacted_columns, 'column') + (c.dashboard_numbers ? ' · ' + plural(c.dashboard_numbers, 'dashboard number') : '') + (c.review_required ? ' · ' + c.review_required + ' need review' : '');
|
|
330
|
+
rb.innerHTML = '<b>' + plural(c.changed_models, 'changed ' + noun) + '</b> since ' + esc(c.base) + ' · ' + esc(counts) + (c.rebuild && c.rebuild.length ? '<br><b>Rebuild:</b> ' + esc(c.rebuild.join(' ')) : '');
|
|
331
|
+
strip.appendChild(rb);
|
|
332
|
+
var want = parseInt(params.get('a') || '0', 10);
|
|
333
|
+
if (want > 0 && want < DATA.views.length){ strip.querySelectorAll('button')[want].click(); }
|
|
334
|
+
}
|
|
335
|
+
var dir0 = params.get('dir') === 'trace' ? 'trace' : 'breaks';
|
|
336
|
+
var linked = (G && asked) ? resolveTarget(index, asked) : null;
|
|
337
|
+
if (L){
|
|
338
|
+
home();
|
|
339
|
+
if (asked){ document.getElementById('q').value = asked; document.getElementById('dir').value = dir0; ask(asked, dir0); }
|
|
340
|
+
} else if (linked && linked.id){
|
|
341
|
+
document.getElementById('q').value = asked; document.getElementById('dir').value = dir0;
|
|
342
|
+
var a0 = offline(linked.id, dir0);
|
|
343
|
+
show(a0, dir0 === 'trace' ? traceText(a0) : breaksText(a0));
|
|
344
|
+
} else {
|
|
345
|
+
if (linked && linked.error){
|
|
346
|
+
// the link asked for something this page cannot answer; saying so beats
|
|
347
|
+
// showing the answer it was written for as though it were the reply
|
|
348
|
+
document.getElementById('q').value = asked;
|
|
349
|
+
document.getElementById('askfine').textContent =
|
|
350
|
+
linked.error + (linked.candidates && linked.candidates.length ? '. Did you mean: ' + linked.candidates.join(', ') + '?' : '.');
|
|
351
|
+
}
|
|
352
|
+
show(DATA.answer, DATA.text);
|
|
353
|
+
}
|
|
354
|
+
var t; window.addEventListener('resize', function(){ clearTimeout(t); t = setTimeout(function(){ if (current) drawWires(current); else if (mapApi) mapApi.draw(); }, 120); });
|
|
355
|
+
})();
|
|
356
|
+
</script>
|
|
357
|
+
</body>
|
|
358
|
+
</html>
|