d23464c0 Andy McCall 1 month ago ↶
1
2
( function ( ) {
3
" use strict" ;
4
5
var p = location . pathname , best = null ;
6
document . querySelectorAll ( " .band a" ) . forEach ( function ( a ) {
7
var h = a . getAttribute ( " href" ) ;
8
if ( h !== " /" && ( p === h || p . indexOf ( h + " /" ) === 0 ) && ( ! best || h . length > best . getAttribute ( " href" ) . length ) ) best = a ;
9
} ) ;
10
if ( best ) best . classList . add ( " on" ) ;
11
16ee5d19 Andy McCall 19 days ago ↶
12
13
14
15
var menus = document . querySelectorAll ( " [data-tab-menu]" ) ;
16
function closeMenus ( except ) {
17
menus . forEach ( function ( m ) { if ( m !== except ) m . open = false ; } ) ;
18
}
19
menus . forEach ( function ( m ) {
20
m . addEventListener ( " toggle" , function ( ) { if ( m . open ) closeMenus ( m ) ; } ) ;
21
} ) ;
22
if ( menus . length ) {
23
document . addEventListener ( " click" , function ( ev ) {
24
if ( ! ev . target . closest || ! ev . target . closest ( " [data-tab-menu]" ) ) closeMenus ( null ) ;
25
} ) ;
26
document . addEventListener ( " keydown" , function ( ev ) {
27
if ( ev . key !== " Escape" ) return ;
28
var open = document . querySelector ( " [data-tab-menu][open]" ) ;
29
if ( open ) { open . open = false ; open . querySelector ( " summary" ) . focus ( ) ; }
30
} ) ;
31
}
32
14530604 Andy McCall 1 month ago ↶
33
34
35
function load ( src , css ) {
36
return new Promise ( function ( ok ) {
37
if ( css ) { var l = document . createElement ( " link" ) ; l . rel = " stylesheet" ; l . href = css ; document . head . appendChild ( l ) ; }
38
var s = document . createElement ( " script" ) ; s . src = src ; s . onload = ok ; document . head . appendChild ( s ) ;
39
} ) ;
40
}
41
if ( document . querySelector ( " textarea.code" ) ) load ( " /static/vendor/editor.js" ) ;
42
var mds = document . querySelectorAll ( " textarea.md" ) ;
ffb67a81 Andy McCall 23 days ago
43
44
45
14530604 Andy McCall 1 month ago ↶
46
if ( mds . length ) load ( " /static/vendor/easymde.min.js" , " /static/vendor/easymde.min.css" ) . then ( function ( ) {
47
var E = window . EasyMDE ;
ffb67a81 Andy McCall 23 days ago
48
function svg ( d ) { return ' <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">' + d + ' </svg>' ; }
49
var I = {
50
bold : svg ( ' <path d="M4.5 2.5h4.25a2.75 2.75 0 0 1 0 5.5H4.5zM4.5 8h5a2.75 2.75 0 0 1 0 5.5h-5z"/>' ) ,
51
italic : svg ( ' <path d="M6.5 2.5h6M3.5 13.5h6M10 2.5l-4 11"/>' ) ,
52
strike : svg ( ' <path d="M2.5 8h11M11 4.5c-.5-1.2-1.7-2-3.2-2-1.9 0-3.3 1-3.3 2.6 0 .9.4 1.5 1.2 2M5 11.5c.5 1.2 1.8 2 3.3 2 2 0 3.4-1.1 3.4-2.7"/>' ) ,
53
heading : svg ( ' <path d="M3.5 2.5v11M12.5 2.5v11M3.5 8h9"/>' ) ,
54
quote : svg ( ' <path d="M2.5 3.5h11M5.5 8h8M5.5 12.5h8M2.5 7v6"/>' ) ,
55
code : svg ( ' <path d="M5.5 4.5 2 8l3.5 3.5M10.5 4.5 14 8l-3.5 3.5"/>' ) ,
56
link : svg ( ' <path d="M7 9a3 3 0 0 0 4.2.3l2-2a3 3 0 0 0-4.2-4.2l-1 1M9 7a3 3 0 0 0-4.2-.3l-2 2a3 3 0 0 0 4.2 4.2l1-1"/>' ) ,
57
ul : svg ( ' <path d="M6 4h7.5M6 8h7.5M6 12h7.5"/><circle cx="2.75" cy="4" r=".75" fill="currentColor"/><circle cx="2.75" cy="8" r=".75" fill="currentColor"/><circle cx="2.75" cy="12" r=".75" fill="currentColor"/>' ) ,
58
ol : svg ( ' <path d="M6.5 4h7M6.5 8h7M6.5 12h7M2 2.5h1v3M2 7.5h1.5L2 9.5h1.5M2 10.5h1.5v3H2M2 12h1.5"/>' ) ,
59
task : svg ( ' <rect x="2" y="2.5" width="4" height="4" rx=".75"/><path d="m2.75 11 1 1 1.75-2M8.5 4.5h5M8.5 11.5h5"/>' ) ,
60
table : svg ( ' <rect x="2" y="2.5" width="12" height="11" rx="1"/><path d="M2 6.5h12M2 10h12M6.5 6.5v7"/>' ) ,
61
full : svg ( ' <path d="M2.5 6V2.5H6M10 2.5h3.5V6M13.5 10v3.5H10M6 13.5H2.5V10"/>' )
62
} ;
63
function b ( name , action , title , icon , text ) { return { name : name , action : action , title : title , icon : icon ? ' <span class="mde-ico">' + icon + ' </span>' : undefined , text : text , className : " mde-btn" + ( text ? " mde-text" : " " ) } ; }
64
function task ( ed ) {
65
var cm = ed . codemirror , from = cm . getCursor ( " from" ) , to = cm . getCursor ( " to" ) ;
66
for ( var l = from . line ; l <= to . line ; l ++ ) {
67
var line = cm . getLine ( l ) , m = line . match ( / ^ ( \s * ) [ -*] \[ [ xX] \] / ) ;
68
if ( m ) cm . replaceRange ( m [ 1 ] , { line : l , ch : 0 } , { line : l , ch : m [ 0 ] . length } ) ;
69
else cm . replaceRange ( " - [ ] " , { line : l , ch : ( line . match ( / ^ \s * / ) || [ " " ] ) [ 0 ] . length } ) ;
70
}
71
cm . focus ( ) ;
72
}
73
var toolbar = [ b ( " preview" , E . togglePreview , " Preview" , null , " Preview" ) , " |" ,
74
b ( " bold" , E . toggleBold , " Bold (Ctrl+B)" , I . bold ) , b ( " italic" , E . toggleItalic , " Italic (Ctrl+I)" , I . italic ) , b ( " strikethrough" , E . toggleStrikethrough , " Strikethrough" , I . strike ) , b ( " heading" , E . toggleHeadingSmaller , " Heading" , I . heading ) , " |" ,
75
b ( " quote" , E . toggleBlockquote , " Quote" , I . quote ) , b ( " code" , E . toggleCodeBlock , " Code" , I . code ) , b ( " link" , E . drawLink , " Link (Ctrl+K)" , I . link ) , " |" ,
76
b ( " unordered-list" , E . toggleUnorderedList , " Bullet list" , I . ul ) , b ( " ordered-list" , E . toggleOrderedList , " Numbered list" , I . ol ) , b ( " task-list" , task , " Task list" , I . task ) , b ( " table" , E . drawTable , " Table" , I . table ) , " |" ,
77
b ( " fullscreen" , E . toggleFullScreen , " Fullscreen" , I . full ) ] ;
78
mds . forEach ( function ( t ) {
79
t . _mde = new E ( { element : t , spellChecker : false , status : false , forceSync : true , autoDownloadFontAwesome : false , toolbar : toolbar ,
80
placeholder : t . getAttribute ( " placeholder" ) || " Write in Markdown…" , minHeight : " 120px" , sideBySideFullscreen : false } ) ;
81
var c = t . _mde . element . parentNode . querySelector ( " .EasyMDEContainer" ) || t . nextElementSibling ;
82
if ( c && c . classList . contains ( " EasyMDEContainer" ) ) {
83
var f = document . createElement ( " div" ) ;
84
f . className = " mde-foot small mute" ;
85
f . innerHTML = ' <span>Markdown supported</span><span class="mde-md" title="Markdown" aria-hidden="true">M↓</span>' ;
86
c . appendChild ( f ) ;
87
}
88
} ) ;
4f37afcc Andy McCall 1 month ago ↶
89
90
window . addEventListener ( " hashchange" , function ( ) { mds . forEach ( function ( t ) { if ( t . _mde ) setTimeout ( function ( ) { t . _mde . codemirror . refresh ( ) ; } , 0 ) ; } ) ; } ) ;
14530604 Andy McCall 1 month ago ↶
91
} ) ;
92
84751769 Andy McCall 1 month ago ↶
93
94
95
96
97
var diagrams = document . querySelectorAll ( " pre > code.language-mermaid" ) ;
98
if ( diagrams . length ) load ( " /static/vendor/mermaid.min.js" ) . then ( function ( ) {
99
var m = window . mermaid , dark = document . documentElement . dataset . theme === " dark" ;
100
m . initialize ( { startOnLoad : false , securityLevel : " strict" , suppressErrorRendering : true , theme : dark ? " dark" : " default" } ) ;
101
diagrams . forEach ( function ( code , i ) {
102
var pre = code . parentNode , source = code . textContent ;
103
m . render ( " mermaid-" + i , source ) . then ( function ( r ) {
104
var fig = document . createElement ( " figure" ) ;
105
fig . className = " mermaid-diagram" ;
106
fig . innerHTML = r . svg ;
107
fig . setAttribute ( " aria-label" , " Diagram" ) ;
108
pre . replaceWith ( fig ) ;
109
} ) . catch ( function ( e ) {
110
var note = document . createElement ( " p" ) ;
111
note . className = " small warn" ;
112
note . textContent = " This diagram could not be drawn: " + ( ( e && e . message ) || e ) ;
113
pre . after ( note ) ;
114
} ) ;
115
} ) ;
116
} ) ;
117
f3e5e2bc Andy McCall 26 days ago ↶
118
119
120
121
var frames = document . querySelectorAll ( " iframe.rep-frame" ) ;
122
var themeToFrames = function ( ) {
123
frames . forEach ( function ( f ) { if ( f . contentWindow ) f . contentWindow . postMessage ( { oxygenhubTheme : document . documentElement . dataset . theme || " dark" } , " *" ) ; } ) ;
124
} ;
125
if ( frames . length ) window . addEventListener ( " message" , function ( e ) {
126
frames . forEach ( function ( f ) {
127
if ( e . source !== f . contentWindow ) return ;
128
var r = e . data && e . data . oxygenhubReport ;
129
if ( ! r ) return ;
130
if ( r . ready ) themeToFrames ( ) ;
131
if ( typeof r . height === " number" && r . height > 0 ) {
132
f . style . height = Math . min ( Math . max ( r . height , 120 ) , 40000 ) + " px" ;
133
f . classList . add ( " sized" ) ;
134
}
135
} ) ;
136
} ) ;
137
95b525b0 Andy McCall 25 days ago ↶
138
139
140
141
142
if ( frames . length ) window . addEventListener ( " message" , function ( e ) {
143
frames . forEach ( function ( f ) {
144
if ( e . source !== f . contentWindow ) return ;
145
var ask = e . data && e . data . oxygenhubAsk ;
146
if ( ! ask ) return ;
147
var runs = [ ] ; try { runs = JSON . parse ( f . dataset . reportRuns || " []" ) ; } catch ( _ ) { }
148
var current = Number ( f . dataset . reportCurrent ) ;
149
var reply = function ( body ) { f . contentWindow . postMessage ( { oxygenhubAnswer : { n : ask . n , value : body . value , error : body . error } } , " *" ) ; } ;
150
var meta = function ( r ) { return { run : r . run , bookmark : r . bookmark , created_ms : r . created_ms , summary : r . summary , current : r . id === current } ; } ;
151
if ( ask . what === " run" ) {
152
var r = runs . filter ( function ( x ) { return x . id === current ; } ) [ 0 ] ;
153
reply ( { value : r ? meta ( r ) : null } ) ;
154
} else if ( ask . what === " history" && typeof ask . dataset === " string" && / ^ [ A-Za-z0-9. _+-] + ( \/ [ A-Za-z0-9. _+-] + ) * $ / . test ( ask . dataset ) ) {
155
Promise . all ( runs . map ( function ( r ) {
156
return fetch ( r . files + " /data/" + ask . dataset ) . then ( function ( res ) { return res . ok ? res . text ( ) : null ; } ) . catch ( function ( ) { return null ; } )
157
. then ( function ( text ) { var m = meta ( r ) ; m . text = text ; return m ; } ) ;
158
} ) ) . then ( function ( all ) { reply ( { value : all . filter ( function ( x ) { return x . text !== null ; } ) } ) ; } ) ;
159
} else {
160
reply ( { error : " ask for run, or history with a file name under data/" } ) ;
161
}
162
} ) ;
163
} ) ;
164
165
166
167
var tl = document . querySelector ( " [data-timeline]" ) ;
168
if ( tl ) {
169
var stops = Array . prototype . slice . call ( tl . querySelectorAll ( " [data-tl-stop]" ) ) ;
170
var at = stops . findIndex ( function ( a ) { return a . classList . contains ( " on" ) ; } ) ;
171
var go = function ( i , play ) {
172
if ( i < 0 || i >= stops . length ) return ;
173
var u = new URL ( stops [ i ] . href , location . href ) ;
174
if ( play ) u . searchParams . set ( " play" , play ) ; else u . searchParams . delete ( " play" ) ;
175
location . href = u . toString ( ) ;
176
} ;
177
document . addEventListener ( " keydown" , function ( e ) {
178
if ( e . target . closest ( " input, select, textarea, [contenteditable]" ) ) return ;
179
if ( e . key === " ArrowLeft" ) { e . preventDefault ( ) ; go ( at - 1 , tl . dataset . play ) ; }
180
if ( e . key === " ArrowRight" ) { e . preventDefault ( ) ; go ( at + 1 , tl . dataset . play ) ; }
181
} ) ;
182
var speed = tl . querySelector ( " [data-tl-speed]" ) , button = tl . querySelector ( " [data-tl-play]" ) ;
183
if ( button ) button . addEventListener ( " click" , function ( ) {
184
if ( tl . dataset . play ) { go ( at , null ) ; return ; }
185
go ( at >= stops . length - 1 ? 0 : at + 1 , speed ? speed . value : " 2" ) ;
186
} ) ;
187
if ( speed ) speed . addEventListener ( " change" , function ( ) { if ( tl . dataset . play ) go ( at , speed . value ) ; } ) ;
188
if ( tl . dataset . play ) {
189
var next = at + 1 ;
190
if ( next < stops . length ) setTimeout ( function ( ) { go ( next , tl . dataset . play ) ; } , Number ( tl . dataset . play ) * 1000 ) ;
191
else setTimeout ( function ( ) { go ( at , null ) ; } , Number ( tl . dataset . play ) * 1000 ) ;
192
}
193
}
194
c40c2d3e Andy McCall 1 month ago ↶
195
196
var toggle = document . querySelector ( " [data-theme-toggle]" ) ;
197
if ( toggle ) {
d98a93c1 Andy McCall 1 month ago ↶
198
var root = document . documentElement , order = [ " light" , " dark" , " auto" ] , glyph = { dark : " ◐" , light : " ☀" , auto : " ◑" } ;
c40c2d3e Andy McCall 1 month ago ↶
199
var show = function ( ) {
d98a93c1 Andy McCall 1 month ago ↶
200
var p = root . dataset . themePref || " light" ;
c40c2d3e Andy McCall 1 month ago ↶
201
toggle . title = " Theme: " + p + " (click to change)" ;
202
toggle . setAttribute ( " aria-label" , toggle . title ) ;
203
toggle . firstElementChild . textContent = glyph [ p ] ;
204
} ;
205
show ( ) ;
206
toggle . addEventListener ( " click" , function ( ) {
d98a93c1 Andy McCall 1 month ago ↶
207
var p = order [ ( order . indexOf ( root . dataset . themePref || " light" ) + 1 ) % order . length ] ;
c40c2d3e Andy McCall 1 month ago ↶
208
document . cookie = " theme=" + p + " ; path=/; max-age=31536000; samesite=lax" ;
209
root . dataset . themePref = p ;
210
root . dataset . theme = p === " auto" ? ( window . matchMedia && matchMedia ( " (prefers-color-scheme: light)" ) . matches ? " light" : " dark" ) : p ;
211
show ( ) ;
f3e5e2bc Andy McCall 26 days ago ↶
212
themeToFrames ( ) ;
c40c2d3e Andy McCall 1 month ago ↶
213
} ) ;
214
}
215
9df855f2 Andy McCall 1 month ago ↶
216
217
var diffs = document . querySelectorAll ( " table.code.diff" ) ;
218
if ( diffs . length ) {
219
var pane = function ( tr , col , side ) {
220
var n = document . createElement ( " td" ) , t = document . createElement ( " td" ) ;
221
n . className = " n" ; t . className = " t" + ( side === " right" ? " mid" : " " ) ;
222
if ( ! tr ) { n . className += " none" ; t . className += " none" ; return [ n , t ] ; }
223
var kind = tr . classList . contains ( " add" ) ? " add" : tr . classList . contains ( " del" ) ? " del" : " ctx" ;
224
if ( kind !== " ctx" ) { n . className += " " + kind ; t . className += " " + kind ; }
225
n . textContent = tr . cells [ col ] . textContent ;
226
t . innerHTML = tr . cells [ 3 ] . innerHTML ;
227
228
if ( side === " left" ) t . querySelectorAll ( " .linecomment" ) . forEach ( function ( d ) { d . remove ( ) ; } ) ;
229
return [ n , t ] ;
230
} ;
231
var split = function ( table ) {
232
var s = document . createElement ( " table" ) ;
233
s . className = " code diff split" ;
234
s . innerHTML = ' <colgroup><col class="n"><col><col class="n"><col></colgroup>' ;
235
var rows = [ ] . slice . call ( table . rows ) , i = 0 ;
236
var add = function ( l , r ) {
237
var tr = s . insertRow ( ) ;
238
pane ( l , 0 , " left" ) . concat ( pane ( r , 1 , " right" ) ) . forEach ( function ( c ) { tr . appendChild ( c ) ; } ) ;
239
} ;
240
while ( i < rows . length ) {
241
var r = rows [ i ] ;
242
if ( r . classList . contains ( " del" ) || r . classList . contains ( " add" ) ) {
243
var dels = [ ] , adds = [ ] ;
244
while ( i < rows . length && rows [ i ] . classList . contains ( " del" ) ) dels . push ( rows [ i ++ ] ) ;
245
while ( i < rows . length && rows [ i ] . classList . contains ( " add" ) ) adds . push ( rows [ i ++ ] ) ;
246
for ( var k = 0 ; k < Math . max ( dels . length , adds . length ) ; k ++ ) add ( dels [ k ] , adds [ k ] ) ;
247
continue ;
248
}
249
if ( r . classList . contains ( " gap" ) || r . querySelector ( " td.thread" ) ) {
250
var tr = s . insertRow ( ) , td = tr . insertCell ( ) ;
251
tr . className = r . className ; td . colSpan = 4 ; td . className = r . cells [ r . cells . length - 1 ] . className ;
252
td . innerHTML = r . cells [ r . cells . length - 1 ] . innerHTML ;
253
} else {
254
add ( r , r ) ;
255
}
256
i ++ ;
257
}
258
return s ;
259
} ;
260
var cookieMode = ( document . cookie . match ( / ( ?: ^ | ; ) diff=( split| unified) / ) || [ ] ) [ 1 ] || " unified" ;
261
var apply = function ( mode ) {
262
diffs . forEach ( function ( t ) {
263
if ( mode === " split" && ! t . _split ) { t . _split = split ( t ) ; t . parentNode . insertBefore ( t . _split , t . nextSibling ) ; }
264
t . hidden = mode === " split" ;
265
if ( t . _split ) t . _split . hidden = mode !== " split" ;
266
} ) ;
267
document . querySelectorAll ( " [data-diff-mode]" ) . forEach ( function ( a ) { a . classList . toggle ( " on" , a . getAttribute ( " data-diff-mode" ) === mode ) ; } ) ;
268
} ;
269
document . querySelectorAll ( " [data-diff-modes], [data-diff-collapse]" ) . forEach ( function ( e ) { e . hidden = false ; } ) ;
270
document . querySelectorAll ( " [data-diff-mode]" ) . forEach ( function ( a ) {
271
a . addEventListener ( " click" , function ( ev ) {
272
ev . preventDefault ( ) ;
273
var mode = a . getAttribute ( " data-diff-mode" ) ;
274
document . cookie = " diff=" + mode + " ; path=/; max-age=31536000; samesite=lax" ;
275
apply ( mode ) ;
276
} ) ;
277
} ) ;
278
document . querySelectorAll ( " [data-diff-collapse]" ) . forEach ( function ( a ) {
279
a . addEventListener ( " click" , function ( ev ) {
280
ev . preventDefault ( ) ;
281
var files = document . querySelectorAll ( " details.file-diff" ) , anyOpen = [ ] . some . call ( files , function ( d ) { return d . open ; } ) ;
282
files . forEach ( function ( d ) { d . open = ! anyOpen ; } ) ;
283
a . textContent = anyOpen ? " expand all" : " collapse all" ;
284
} ) ;
285
} ) ;
286
apply ( cookieMode ) ;
287
}
288
64caed18 Andy McCall 1 month ago ↶
289
290
var navLinks = document . querySelectorAll ( " .section-nav a[href^='#']" ) ;
291
if ( navLinks . length && " IntersectionObserver" in window ) {
292
var byId = { } ;
293
navLinks . forEach ( function ( a ) { byId [ a . getAttribute ( " href" ) . slice ( 1 ) ] = a ; } ) ;
294
var io = new IntersectionObserver ( function ( entries ) {
295
entries . forEach ( function ( e ) {
296
if ( e . isIntersecting && byId [ e . target . id ] ) {
297
navLinks . forEach ( function ( a ) { a . classList . remove ( " on" ) ; } ) ;
298
byId [ e . target . id ] . classList . add ( " on" ) ;
299
}
300
} ) ;
301
} , { rootMargin : " 0px 0px -70% 0px" } ) ;
302
Object . keys ( byId ) . forEach ( function ( id ) { var el = document . getElementById ( id ) ; if ( el ) io . observe ( el ) ; } ) ;
303
}
304
84e3c096 Andy McCall 11 days ago ↶
305
306
d23464c0 Andy McCall 1 month ago ↶
307
document . querySelectorAll ( " [data-copy]" ) . forEach ( function ( b ) {
84e3c096 Andy McCall 11 days ago ↶
308
var glyph = b . classList . contains ( " copy-btn" ) , timer ;
309
if ( glyph ) { b . setAttribute ( " data-tip" , " Copy to clipboard" ) ; b . title = " " ; }
d23464c0 Andy McCall 1 month ago ↶
310
b . addEventListener ( " click" , function ( ) {
311
navigator . clipboard . writeText ( b . getAttribute ( " data-copy" ) ) . then ( function ( ) {
84e3c096 Andy McCall 11 days ago ↶
312
clearTimeout ( timer ) ;
313
if ( glyph ) {
314
b . classList . add ( " copied" ) ; b . setAttribute ( " data-tip" , " Copied!" ) ; b . setAttribute ( " aria-label" , " Copied!" ) ;
315
timer = setTimeout ( function ( ) { b . classList . remove ( " copied" ) ; b . setAttribute ( " data-tip" , " Copy to clipboard" ) ; b . setAttribute ( " aria-label" , " Copy to clipboard" ) ; } , 2000 ) ;
316
} else {
317
var t = b . getAttribute ( " data-label" ) || b . textContent ; b . setAttribute ( " data-label" , t ) ;
318
b . textContent = " copied" ; timer = setTimeout ( function ( ) { b . textContent = t ; } , 1200 ) ;
319
}
d23464c0 Andy McCall 1 month ago ↶
320
} ) ;
321
} ) ;
322
} ) ;
323
324
325
var tree = document . querySelector ( " table.tree[data-last]" ) ;
326
if ( tree ) {
327
var paths = [ ] . map . call ( tree . querySelectorAll ( " tr[data-path]" ) , function ( r ) { return r . getAttribute ( " data-path" ) ; } ) ;
328
if ( paths . length ) {
329
fetch ( tree . getAttribute ( " data-last" ) + " ?paths=" + encodeURIComponent ( paths . join ( " ," ) ) , { credentials : " same-origin" } )
330
. then ( function ( r ) { return r . json ( ) ; } )
331
. then ( function ( rows ) {
332
rows . forEach ( function ( c ) {
333
var tr = tree . querySelector ( ' tr[data-path="' + CSS . escape ( c . path ) + ' "]' ) ;
334
if ( ! tr ) return ;
335
var a = tr . querySelector ( " td.last a" ) , t = tr . querySelector ( " td.age" ) ;
336
if ( a ) { a . textContent = c . title ; a . href = tree . getAttribute ( " data-change" ) + c . hash ; }
337
if ( t ) { t . textContent = c . ago ; t . title = c . when ; }
338
} ) ;
339
} ) . catch ( function ( ) { } ) ;
340
}
341
}
342
343
344
function mark ( ) {
345
var m = location . hash . match ( / ^ #L( \d + ) ( ?: -L( \d + ) ) ? $ / ) ;
346
document . querySelectorAll ( " tr.hl" ) . forEach ( function ( r ) { r . classList . remove ( " hl" ) ; } ) ;
6ae41081 Andy McCall 1 month ago ↶
347
if ( ! m ) { showBar ( 0 , 0 ) ; return ; }
d23464c0 Andy McCall 1 month ago ↶
348
var a = + m [ 1 ] , b = + ( m [ 2 ] || m [ 1 ] ) ;
349
for ( var i = Math . min ( a , b ) ; i <= Math . max ( a , b ) ; i ++ ) {
350
var row = document . getElementById ( " L" + i ) ;
351
if ( row ) row . classList . add ( " hl" ) ;
352
}
353
var first = document . getElementById ( " L" + Math . min ( a , b ) ) ;
354
if ( first && ! mark . done ) { first . scrollIntoView ( { block : " center" } ) ; mark . done = true ; }
6ae41081 Andy McCall 1 month ago ↶
355
showBar ( Math . min ( a , b ) , Math . max ( a , b ) ) ;
356
}
357
358
var bar = document . querySelector ( " .linebar" ) ;
359
function showBar ( a , b ) {
360
if ( ! bar ) return ;
361
bar . hidden = ! a ;
362
if ( ! a ) return ;
363
bar . querySelector ( " [data-linebar-label]" ) . textContent = a === b ? " Line " + a : " Lines " + a + " –" + b ;
364
bar . setAttribute ( " data-range" , a + " -" + b ) ;
365
}
366
function flash ( btn ) { var t = btn . textContent ; btn . textContent = " Copied" ; setTimeout ( function ( ) { btn . textContent = t ; } , 1200 ) ; }
367
if ( bar ) {
368
bar . querySelector ( " [data-copy-lines]" ) . addEventListener ( " click" , function ( ev ) {
369
var r = bar . getAttribute ( " data-range" ) . split ( " -" ) , out = [ ] ;
370
for ( var i = + r [ 0 ] ; i <= + r [ 1 ] ; i ++ ) { var row = document . getElementById ( " L" + i ) ; if ( row ) out . push ( row . querySelector ( " td.t" ) . textContent ) ; }
371
navigator . clipboard . writeText ( out . join ( " \n " ) + " \n " ) . then ( function ( ) { flash ( ev . target ) ; } ) ;
372
} ) ;
373
var pl = bar . querySelector ( " [data-copy-permalink]" ) ;
374
if ( pl ) pl . addEventListener ( " click" , function ( ) {
375
navigator . clipboard . writeText ( new URL ( document . body . getAttribute ( " data-permalink" ) , location . href ) . href + location . hash ) . then ( function ( ) { flash ( pl ) ; } ) ;
376
} ) ;
d23464c0 Andy McCall 1 month ago ↶
377
}
6ae41081 Andy McCall 1 month ago ↶
378
function permalink ( ) { var p = document . body . getAttribute ( " data-permalink" ) ; return p ? p + location . hash : null ; }
d23464c0 Andy McCall 1 month ago ↶
379
window . addEventListener ( " hashchange" , mark ) ;
380
mark ( ) ;
381
document . querySelectorAll ( " table.code td.n" ) . forEach ( function ( td ) {
382
td . addEventListener ( " click" , function ( e ) {
383
var n = td . parentNode . id ;
384
if ( e . shiftKey && location . hash . match ( / ^ #L( \d + ) / ) ) location . hash = location . hash . replace ( / ^ #( L\d + ) . * / , " $1-" + n ) ;
385
else location . hash = n ;
386
} ) ;
387
} ) ;
388
42fa9554 Andy McCall 1 month ago ↶
389
390
var liveUrl = document . body . getAttribute ( " data-flow-live" ) ;
391
if ( liveUrl ) {
392
var tick = function ( ) {
393
var after = [ ] . map . call ( document . querySelectorAll ( " details[data-job]" ) , function ( d ) {
394
return d . getAttribute ( " data-job" ) + " :" + d . querySelector ( " [data-log]" ) . getAttribute ( " data-seq" ) ;
395
} ) . join ( " ," ) ;
396
fetch ( liveUrl + " ?after=" + after , { headers : { accept : " application/json" } } )
397
. then ( function ( r ) { return r . json ( ) ; } )
398
. then ( function ( s ) {
399
s . jobs . forEach ( function ( j ) {
400
var cls = " st-" + j . status ;
401
var node = document . querySelector ( " [data-job-node='" + j . id + " ']" ) ;
402
if ( node ) {
403
node . setAttribute ( " class" , " node " + cls ) ;
404
node . querySelector ( " [data-glyph]" ) . textContent = j . glyph ;
405
node . querySelector ( " [data-meta]" ) . textContent = j . status + " · " + j . duration ;
406
}
407
var d = document . querySelector ( " details[data-job='" + j . id + " ']" ) ;
408
if ( ! d ) return ;
409
d . className = " panel flow-job " + cls ;
410
var g = d . querySelector ( " summary [data-glyph]" ) ;
411
g . className = " flow-glyph " + cls ; g . textContent = j . glyph ;
412
d . querySelector ( " summary [data-meta]" ) . textContent = j . status + ( j . exit_code !== null ? " · exit " + j . exit_code : " " ) + " · " + j . duration ;
413
if ( j . status === " running" || j . status === " failed" ) d . open = true ;
858eca0d Andy McCall 16 days ago ↶
414
415
if ( j . status !== " queued" ) [ ] . forEach . call ( d . querySelectorAll ( " [data-queued-note]" ) , function ( p ) { p . remove ( ) ; } ) ;
42fa9554 Andy McCall 1 month ago ↶
416
var log = d . querySelector ( " [data-log]" ) ;
417
var stick = log . scrollTop + log . clientHeight >= log . scrollHeight - 4 ;
418
j . lines . forEach ( function ( l ) {
419
var ln = document . createElement ( " span" ) ; ln . className = " ln s-" + l . stream ;
420
var t = document . createElement ( " span" ) ; t . className = " t" ; t . textContent = ( l . t / 1000 ) . toFixed ( 1 ) + " s" ;
421
ln . appendChild ( t ) ; ln . appendChild ( document . createTextNode ( l . text ) ) ;
422
log . appendChild ( ln ) ; log . appendChild ( document . createTextNode ( " \n " ) ) ;
423
log . setAttribute ( " data-seq" , l . seq ) ;
424
} ) ;
425
if ( stick && j . lines . length ) log . scrollTop = log . scrollHeight ;
426
} ) ;
427
var st = document . querySelector ( " [data-run-status]" ) ; if ( st ) st . textContent = s . status ;
428
if ( s . live ) setTimeout ( tick , 1500 ) ; else setTimeout ( function ( ) { location . reload ( ) ; } , 800 ) ;
429
} )
430
. catch ( function ( ) { setTimeout ( tick , 5000 ) ; } ) ;
431
} ;
432
setTimeout ( tick , 1500 ) ;
433
}
434
d23464c0 Andy McCall 1 month ago ↶
435
436
var pending = null ;
437
document . addEventListener ( " keydown" , function ( e ) {
438
if ( e . target && / ^ ( INPUT| TEXTAREA| SELECT) $ / . test ( e . target . tagName ) ) return ;
439
if ( e . key === " /" ) { var s = document . querySelector ( " input[name=q]" ) ; if ( s ) { e . preventDefault ( ) ; s . focus ( ) ; } return ; }
6ae41081 Andy McCall 1 month ago ↶
440
if ( e . key === " y" && ! e . ctrlKey && ! e . metaKey && ! e . altKey && permalink ( ) ) { history . replaceState ( null , " " , permalink ( ) ) ; pending = null ; return ; }
d23464c0 Andy McCall 1 month ago ↶
441
if ( e . key === " ?" ) { var k = document . getElementById ( " keys" ) ; if ( k ) k . hidden = ! k . hidden ; return ; }
442
var repo = document . body . getAttribute ( " data-repo" ) ;
443
if ( pending === " g" && repo ) {
444
var to = { c : " " , p : " /proposals" , i : " /issues" , h : " /changes/" + ( document . body . getAttribute ( " data-ref" ) || " main" ) , b : " /bookmarks" , t : " /trust" } [ e . key ] ;
445
if ( to !== undefined ) location . href = repo + to ;
446
pending = null ; return ;
447
}
448
pending = e . key === " g" ? " g" : null ;
449
} ) ;
5e6d6484 Andy McCall 1 month ago ↶
450
451
452
453
454
var live = document . querySelector ( " [data-resolve-live]" ) ;
455
if ( live && window . EventSource && window . fetch ) {
456
var url = live . getAttribute ( " data-resolve-live" ) , csrf = live . getAttribute ( " data-csrf" ) ;
457
var client = Math . random ( ) . toString ( 36 ) . slice ( 2 ) + Date . now ( ) . toString ( 36 ) ;
458
var present = document . querySelector ( " [data-live-present]" ) , landed = document . querySelector ( " [data-live-landed]" ) ;
459
var applying = false ;
460
var fieldsOf = function ( region ) {
461
return region === " keep"
462
? { radios : live . querySelectorAll ( ' input[name="keep"]' ) , text : null }
463
: { radios : live . querySelectorAll ( ' input[name="r' + region + ' "]' ) , text : live . querySelector ( ' textarea[name="t' + region + ' "]' ) } ;
464
} ;
465
var apply = function ( region , c ) {
466
var f = fieldsOf ( region ) ;
467
applying = true ;
468
f . radios . forEach ( function ( r ) { r . checked = r . value === c . choice ; } ) ;
469
if ( f . text && c . choice === " text" ) f . text . value = c . text ;
470
applying = false ;
471
var sec = f . radios . length ? f . radios [ 0 ] . closest ( " section" ) : null ;
472
if ( sec ) sec . setAttribute ( " title" , " chosen by " + c . by ) ;
473
} ;
474
var send = function ( region , choice , text ) {
475
fetch ( url , { method : " POST" , headers : { " content-type" : " application/json" } , credentials : " same-origin" ,
476
body : JSON . stringify ( { _csrf : csrf , client : client , region : region , choice : choice , text : text || " " } ) } ) ;
477
} ;
478
var timers = { } ;
479
live . addEventListener ( " change" , function ( ev ) {
480
var t = ev . target ;
481
if ( applying || ! t . name || t . type !== " radio" ) return ;
482
var region = t . name === " keep" ? " keep" : t . name . slice ( 1 ) ;
483
var ta = live . querySelector ( ' textarea[name="t' + region + ' "]' ) ;
484
send ( region , t . value , t . value === " text" && ta ? ta . value : " " ) ;
485
} ) ;
486
live . addEventListener ( " input" , function ( ev ) {
487
var t = ev . target ;
488
if ( applying || t . tagName !== " TEXTAREA" ) return ;
489
var region = t . name . slice ( 1 ) ;
490
live . querySelectorAll ( ' input[name="r' + region + ' "]' ) . forEach ( function ( r ) { r . checked = r . value === " text" ; } ) ;
491
clearTimeout ( timers [ region ] ) ;
492
timers [ region ] = setTimeout ( function ( ) { send ( region , " text" , t . value ) ; } , 400 ) ;
493
} ) ;
494
var es = new EventSource ( url + " ?client=" + encodeURIComponent ( client ) ) ;
495
var showPresent = function ( names ) {
496
present . hidden = names . length < 2 ;
497
present . textContent = " Resolving together: " + names . join ( " , " ) + " . Choices are shared as you make them; the last choice for a conflict wins." ;
498
} ;
499
es . addEventListener ( " snapshot" , function ( e ) {
500
var d = JSON . parse ( e . data ) ;
501
showPresent ( d . present ) ;
502
Object . keys ( d . choices ) . forEach ( function ( r ) { apply ( r , d . choices [ r ] ) ; } ) ;
503
if ( d . keep ) apply ( " keep" , d . keep ) ;
504
} ) ;
505
es . addEventListener ( " presence" , function ( e ) { showPresent ( JSON . parse ( e . data ) . present ) ; } ) ;
506
es . addEventListener ( " choice" , function ( e ) {
507
var d = JSON . parse ( e . data ) ;
508
if ( d . client !== client ) apply ( d . region , d . choice ) ;
509
} ) ;
510
es . addEventListener ( " landed" , function ( e ) {
511
var d = JSON . parse ( e . data ) ;
512
es . close ( ) ;
513
landed . hidden = false ;
514
landed . textContent = " " ;
515
landed . appendChild ( document . createTextNode ( d . by + " committed the resolution: " ) ) ;
516
var a = document . createElement ( " a" ) ;
517
a . href = d . url ;
518
a . textContent = " see the change" ;
519
landed . appendChild ( a ) ;
520
live . querySelectorAll ( " input, textarea, button" ) . forEach ( function ( x ) { x . disabled = true ; } ) ;
521
} ) ;
522
}
2bfe5ef6 Andy McCall 1 month ago ↶
523
524
525
526
527
var plan = document . querySelector ( " [data-plan]" ) ;
528
if ( plan && window . fetch ) {
529
var panel = document . querySelector ( " [data-plan-panel]" ) , from = null , moved = false ;
530
plan . querySelectorAll ( " [data-change]" ) . forEach ( function ( a ) {
531
a . style . cursor = " grab" ;
532
a . addEventListener ( " pointerdown" , function ( ev ) { from = a . getAttribute ( " data-change" ) ; moved = false ; ev . preventDefault ( ) ; } ) ;
533
a . addEventListener ( " pointermove" , function ( ) { if ( from ) moved = true ; } ) ;
534
a . addEventListener ( " click" , function ( ev ) { if ( moved ) ev . preventDefault ( ) ; } ) ;
535
} ) ;
536
document . addEventListener ( " pointerup" , function ( ev ) {
537
var start = from ;
538
from = null ;
539
if ( ! start ) return ;
540
var el = document . elementFromPoint ( ev . clientX , ev . clientY ) , onto = el && el . closest ? el . closest ( " [data-change]" ) : null ;
541
if ( ! onto || onto . getAttribute ( " data-change" ) === start ) return ;
542
var body = new URLSearchParams ( { _csrf : plan . getAttribute ( " data-csrf" ) , change : start , target : onto . getAttribute ( " data-change" ) } ) ;
543
fetch ( plan . getAttribute ( " data-plan" ) , { method : " POST" , credentials : " same-origin" , body : body } )
544
. then ( function ( r ) { return r . json ( ) ; } )
545
. then ( function ( d ) {
546
panel . hidden = false ;
547
panel . textContent = " " ;
548
if ( d . error ) { panel . textContent = d . error ; return ; }
549
d . options . forEach ( function ( o ) {
550
var p = document . createElement ( " p" ) ;
551
p . appendChild ( document . createTextNode ( o . summary + " " ) ) ;
552
var a = document . createElement ( " a" ) ;
553
a . href = o . vscode ;
554
a . textContent = " Apply in VS Code" ;
555
p . appendChild ( a ) ;
556
var code = document . createElement ( " pre" ) ;
557
code . className = " snippet" ;
558
code . textContent = o . command ;
559
panel . appendChild ( p ) ;
560
panel . appendChild ( code ) ;
561
} ) ;
562
} ) ;
563
} ) ;
564
}
cb71e18e Andy McCall 1 month ago ↶
565
566
567
568
document . querySelectorAll ( " [data-menu-filter]" ) . forEach ( function ( input ) {
569
var items = input . closest ( " .menu-pop" ) . querySelectorAll ( " .menu-item" ) ;
570
input . addEventListener ( " input" , function ( ) {
571
var q = input . value . trim ( ) . toLowerCase ( ) ;
572
items . forEach ( function ( it ) { it . hidden = q !== " " && it . textContent . toLowerCase ( ) . indexOf ( q ) < 0 ; } ) ;
573
} ) ;
574
} ) ;
575
document . querySelectorAll ( " details.menu" ) . forEach ( function ( d ) {
576
d . addEventListener ( " toggle" , function ( ) {
577
var f = d . querySelector ( " [data-menu-filter]" ) ;
578
if ( d . open && f ) f . focus ( ) ;
579
} ) ;
580
} ) ;
581
document . addEventListener ( " click" , function ( ev ) {
582
document . querySelectorAll ( " details.menu[open]" ) . forEach ( function ( d ) { if ( ! d . contains ( ev . target ) ) d . open = false ; } ) ;
583
} ) ;
4f37afcc Andy McCall 1 month ago ↶
584
585
586
function copied ( btn , text ) {
587
if ( navigator . clipboard ) navigator . clipboard . writeText ( text ) . then ( function ( ) {
588
var was = btn . textContent ; btn . textContent = " Copied" ; setTimeout ( function ( ) { btn . textContent = was ; } , 1200 ) ;
589
} ) ;
590
var d = btn . closest ( " details" ) ; if ( d ) d . open = false ;
591
}
592
document . querySelectorAll ( " [data-copy-link]" ) . forEach ( function ( b ) {
593
b . addEventListener ( " click" , function ( ) { copied ( b , location . origin + location . pathname + location . search + b . getAttribute ( " data-copy-link" ) ) ; } ) ;
594
} ) ;
595
document . querySelectorAll ( " [data-copy-text]" ) . forEach ( function ( b ) {
596
b . addEventListener ( " click" , function ( ) { copied ( b , b . getAttribute ( " data-copy-text" ) ) ; } ) ;
597
} ) ;
598
document . querySelectorAll ( ' .menu-actions a[href^="#"]' ) . forEach ( function ( a ) {
599
a . addEventListener ( " click" , function ( ) { var d = a . closest ( " details" ) ; if ( d ) d . open = false ; } ) ;
600
} ) ;
ed89fae5 Andy McCall 1 month ago ↶
601
602
603
604
605
document . addEventListener ( " submit" , function ( e ) {
606
var sec = e . target . closest ? e . target . closest ( " section[id], form[id], details[id], div[id]" ) : null ;
607
if ( ! sec || ! sec . id ) return ;
608
try { sessionStorage . setItem ( " oxy-return" , location . pathname + " #" + sec . id ) ; } catch ( err ) { }
609
} ) ;
610
try {
611
var back = sessionStorage . getItem ( " oxy-return" ) ;
612
if ( back && back . indexOf ( location . pathname + " #" ) === 0 ) {
613
sessionStorage . removeItem ( " oxy-return" ) ;
614
var target = document . getElementById ( back . split ( " #" ) [ 1 ] ) ;
615
if ( target ) {
616
var flash = document . querySelector ( " main > .note, main > .error, .sections > .note, .sections > .error" ) ;
617
if ( flash && ! target . contains ( flash ) ) target . parentNode . insertBefore ( flash , target ) ;
618
target . scrollIntoView ( { block : " start" } ) ;
619
}
620
}
621
} catch ( err ) { }
155cab14 Andy McCall 1 month ago ↶
622
16626c0d Andy McCall 25 days ago ↶
623
624
625
626
627
628
var SPIN = [ " ⠋" , " ⠙" , " ⠹" , " ⠸" , " ⠼" , " ⠴" , " ⠦" , " ⠧" , " ⠇" , " ⠏" ] ;
629
var waitTimer = 0 , spinTimer = 0 , pointer = null , mark = null ;
630
document . addEventListener ( " pointermove" , function ( e ) {
631
pointer = e . pointerType === " mouse" ? { x : e . clientX , y : e . clientY } : null ;
632
if ( mark && pointer ) place ( ) ;
633
} , { passive : true } ) ;
634
function place ( ) {
635
if ( pointer ) { mark . style . left = pointer . x + 14 + " px" ; mark . style . top = pointer . y + 14 + " px" ; mark . classList . remove ( " corner" ) ; }
636
else mark . classList . add ( " corner" ) ;
637
}
638
function waiting ( ) {
639
clearTimeout ( waitTimer ) ;
640
waitTimer = setTimeout ( function ( ) {
641
if ( ! mark ) {
642
var logo = document . querySelector ( " header .brand svg" ) ;
643
mark = document . createElement ( " div" ) ;
644
mark . className = " oxy-wait-mark" ;
645
mark . setAttribute ( " role" , " status" ) ;
646
mark . setAttribute ( " aria-label" , " Working" ) ;
647
if ( logo ) mark . appendChild ( logo . cloneNode ( true ) ) ;
648
document . body . appendChild ( mark ) ;
649
}
650
place ( ) ;
651
document . documentElement . classList . add ( " oxy-wait" ) ;
652
} , 300 ) ;
653
}
654
function busy ( button , form ) {
655
if ( ! button ) return ;
656
var label = button . getAttribute ( " data-busy" ) || button . textContent . trim ( ) ;
657
button . dataset . idle = button . innerHTML ;
658
button . style . minWidth = button . offsetWidth + " px" ;
659
button . textContent = label + " " ;
660
var glyph = document . createElement ( " span" ) ;
661
glyph . className = " oxy-spin" ;
662
glyph . setAttribute ( " aria-hidden" , " true" ) ;
663
glyph . textContent = SPIN [ 0 ] ;
664
button . appendChild ( glyph ) ;
665
button . setAttribute ( " aria-busy" , " true" ) ;
666
var i = 0 ;
667
clearInterval ( spinTimer ) ;
668
spinTimer = setInterval ( function ( ) { glyph . textContent = SPIN [ ++ i % SPIN . length ] ; } , 80 ) ;
669
670
671
setTimeout ( function ( ) { button . disabled = true ; } , 0 ) ;
672
form . dataset . sending = " 1" ;
673
}
674
document . addEventListener ( " submit" , function ( e ) {
675
var form = e . target ;
676
if ( form . dataset . sending ) { e . preventDefault ( ) ; return ; }
677
if ( e . defaultPrevented || ( form . target && form . target !== " _self" ) ) return ;
678
if ( ( form . getAttribute ( " method" ) || " get" ) . toLowerCase ( ) === " post" ) {
679
busy ( e . submitter || form . querySelector ( " button[type=submit], button:not([type])" ) , form ) ;
680
}
681
waiting ( ) ;
682
} ) ;
683
document . addEventListener ( " click" , function ( e ) {
684
if ( e . defaultPrevented || e . button !== 0 || e . metaKey || e . ctrlKey || e . shiftKey || e . altKey ) return ;
685
var a = e . target . closest ? e . target . closest ( " a[href]" ) : null ;
686
if ( ! a || a . hasAttribute ( " download" ) || ( a . target && a . target !== " _self" ) ) return ;
687
var url = new URL ( a . href , location . href ) ;
688
if ( url . origin !== location . origin ) return ;
689
if ( url . pathname === location . pathname && url . search === location . search && url . hash ) return ;
690
waiting ( ) ;
691
} ) ;
692
693
window . addEventListener ( " pageshow" , function ( e ) {
694
if ( ! e . persisted ) return ;
695
clearTimeout ( waitTimer ) ;
696
clearInterval ( spinTimer ) ;
697
document . documentElement . classList . remove ( " oxy-wait" ) ;
698
document . querySelectorAll ( " [aria-busy=true][data-idle]" ) . forEach ( function ( b ) {
699
b . innerHTML = b . dataset . idle ;
700
b . disabled = false ;
701
b . removeAttribute ( " aria-busy" ) ;
702
b . style . minWidth = " " ;
703
if ( b . form ) delete b . form . dataset . sending ;
704
} ) ;
705
} ) ;
706
155cab14 Andy McCall 1 month ago ↶
707
708
709
710
document . querySelectorAll ( " [data-preset-select]" ) . forEach ( function ( sel ) {
711
var form = sel . form ;
712
function apply ( ) {
713
var o = sel . options [ sel . selectedIndex ] ;
714
if ( ! o || ! o . value ) return ;
715
var name = form . querySelector ( " [name=name]" ) , ep = form . querySelector ( " [name=endpoint]" ) , api = form . querySelector ( " [name=api]" ) ;
716
name . placeholder = o . dataset . name ; ep . value = o . dataset . endpoint ; api . value = o . dataset . api ;
717
}
718
sel . addEventListener ( " change" , apply ) ;
719
apply ( ) ;
720
} ) ;
721
document . querySelectorAll ( " [data-list-models]" ) . forEach ( function ( b ) {
722
b . addEventListener ( " click" , function ( ) {
723
var form = b . form , input = document . getElementById ( b . dataset . for ) ;
724
var list = document . getElementById ( b . dataset . for + " -list" ) , note = document . querySelector ( " [data-models-note='" + b . dataset . for + " ']" ) ;
725
var body = new URLSearchParams ( ) ;
726
body . set ( " _csrf" , b . dataset . csrf ) ;
727
[ " endpoint" , " api" , " token" , " preset" ] . forEach ( function ( k ) { var el = form . querySelector ( " [name=" + k + " ]" ) ; if ( el ) body . set ( k , el . value ) ; } ) ;
728
if ( b . dataset . id ) body . set ( " id" , b . dataset . id ) ;
729
if ( note ) note . textContent = " Asking the provider…" ;
730
fetch ( b . dataset . listModels , { method : " POST" , body : body , credentials : " same-origin" } )
731
. then ( function ( r ) { return r . json ( ) ; } )
732
. then ( function ( j ) {
733
if ( j . models ) {
734
list . innerHTML = " " ;
735
j . models . forEach ( function ( m ) { var o = document . createElement ( " option" ) ; o . value = m ; list . appendChild ( o ) ; } ) ;
736
if ( note ) note . textContent = j . models . length + " models: pick one in the Model field." ;
737
input . focus ( ) ;
738
} else if ( note ) {
739
note . textContent = " No list: " + ( j . error || " unknown" ) + " . Type the model's name." ;
740
}
741
} )
742
. catch ( function ( ) { if ( note ) note . textContent = " The hub could not ask; type the model's name." ; } ) ;
743
} ) ;
744
} ) ;
d23464c0 Andy McCall 1 month ago ↶
745
} ) ( ) ;
67be8ac0 Andy McCall 21 days ago ↶
746
747
748
749
750
751
752
753
( function ( ) {
754
var marked = document . querySelectorAll ( " [data-live][data-fragment]" ) ;
755
if ( ! marked . length || ! window . EventSource || ! window . fetch ) return ;
756
var topics = { } ;
b5a0a69b Andy McCall 21 days ago ↶
757
758
var topicsOf = function ( el ) { return el . getAttribute ( " data-live" ) . split ( / \s + / ) . filter ( Boolean ) ; } ;
759
Array . prototype . forEach . call ( marked , function ( el ) { topicsOf ( el ) . forEach ( function ( t ) { topics [ t ] = true ; } ) ; } ) ;
67be8ac0 Andy McCall 21 days ago ↶
760
var busy = { } ;
761
var typingIn = function ( el ) {
762
var a = document . activeElement ;
763
return a && el . contains ( a ) && ( a . tagName === " TEXTAREA" || ( a . tagName === " INPUT" && a . type !== " hidden" ) ) && a . value ;
764
} ;
765
var refresh = function ( el ) {
766
var name = el . getAttribute ( " data-fragment" ) ;
767
if ( busy [ name ] ) return ;
768
if ( typingIn ( el ) ) {
769
el . addEventListener ( " focusout" , function again ( ) {
770
el . removeEventListener ( " focusout" , again ) ;
771
setTimeout ( function ( ) { refresh ( el ) ; } , 0 ) ;
772
} ) ;
773
return ;
774
}
775
busy [ name ] = true ;
776
var url = new URL ( location . href ) ;
777
url . searchParams . set ( " fragment" , name ) ;
778
fetch ( url . toString ( ) , { credentials : " same-origin" , headers : { " X-Fragment" : name } } )
779
. then ( function ( r ) { return r . ok ? r . text ( ) : null ; } )
780
. then ( function ( html ) {
781
busy [ name ] = false ;
782
if ( html !== null && ! typingIn ( el ) ) el . innerHTML = html ;
783
} )
784
. catch ( function ( ) { busy [ name ] = false ; } ) ;
785
} ;
786
var es = new EventSource ( " /live?topics=" + encodeURIComponent ( Object . keys ( topics ) . join ( " ," ) ) ) ;
787
es . addEventListener ( " change" , function ( e ) {
788
var d ;
789
try { d = JSON . parse ( e . data ) ; } catch ( err ) { return ; }
790
Array . prototype . forEach . call ( marked , function ( el ) {
b5a0a69b Andy McCall 21 days ago ↶
791
if ( topicsOf ( el ) . indexOf ( d . topic ) >= 0 ) refresh ( el ) ;
67be8ac0 Andy McCall 21 days ago ↶
792
} ) ;
793
} ) ;
794
795
es . addEventListener ( " reload" , function ( ) {
796
Array . prototype . forEach . call ( marked , refresh ) ;
797
} ) ;
798
} ) ( ) ;
119fd17d Andy McCall 20 days ago ↶
799
e97500b7 Andy McCall 19 days ago ↶
800
801
802
803
804
( function ( ) {
805
" use strict" ;
806
var parts = document . querySelectorAll ( " [data-refresh][data-fragment]" ) ;
807
if ( ! parts . length || ! window . fetch || ! window . DOMParser ) return ;
808
var every = Math . max ( 10 , parseInt ( parts [ 0 ] . getAttribute ( " data-refresh" ) , 10 ) || 30 ) * 1000 ;
809
var busy = false ;
810
var inUse = function ( el ) {
811
var a = document . activeElement ;
812
return a && el . contains ( a ) && / ^ ( SELECT| INPUT| TEXTAREA) $ / . test ( a . tagName ) ;
813
} ;
814
var tick = function ( ) {
815
if ( busy || document . hidden ) return ;
816
busy = true ;
817
fetch ( location . href , { credentials : " same-origin" , headers : { " X-Fragment" : " page" } } )
818
. then ( function ( r ) { return r . ok ? r . text ( ) : null ; } )
819
. then ( function ( html ) {
820
busy = false ;
821
if ( html === null ) return ;
822
var doc = new DOMParser ( ) . parseFromString ( html , " text/html" ) ;
823
Array . prototype . forEach . call ( parts , function ( el ) {
824
if ( inUse ( el ) ) return ;
825
var fresh = doc . querySelector ( ' [data-fragment="' + el . getAttribute ( " data-fragment" ) + ' "]' ) ;
826
if ( fresh ) el . innerHTML = fresh . innerHTML ;
827
} ) ;
828
} )
829
. catch ( function ( ) { busy = false ; } ) ;
830
} ;
831
setInterval ( tick , every ) ;
832
document . addEventListener ( " visibilitychange" , function ( ) { if ( ! document . hidden ) tick ( ) ; } ) ;
833
} ) ( ) ;
834
119fd17d Andy McCall 20 days ago ↶
835
836
837
838
839
( function ( ) {
840
" use strict" ;
841
if ( ! window . fetch || ! window . TextDecoder || ! window . URLSearchParams ) return ;
842
document . querySelectorAll ( " button.agent-btn[data-stream]" ) . forEach ( function ( btn ) {
843
btn . addEventListener ( " click" , function ( e ) {
844
var form = btn . form , area = form && form . querySelector ( ' textarea[name="body"]' ) ;
845
if ( ! form || ! area ) return ;
846
e . preventDefault ( ) ;
847
function get ( ) { return area . _mde ? area . _mde . value ( ) : area . value ; }
848
function show ( t ) { if ( area . _mde ) area . _mde . value ( t ) ; else area . value = t ; }
c43e4f63 Andy McCall 11 days ago ↶
849
850
851
var titleField = form . querySelector ( ' input[name="title"]' ) ;
852
function untitled ( t ) { return t . replace ( / ^ \s * title:[ ^ \n ] * ( \n \s * | $ ) / i , " " ) ; }
119fd17d Andy McCall 20 days ago ↶
853
var before = get ( ) , text = " " ;
854
var box = ( area . _mde && area . parentNode . querySelector ( " .EasyMDEContainer" ) ) || area ;
855
var note = form . querySelector ( " .draft-note" ) ;
856
if ( ! note ) { note = document . createElement ( " p" ) ; note . className = " draft-note small mute" ; box . parentNode . insertBefore ( note , box . nextSibling ) ; }
857
note . textContent = btn . value + " is drafting the description…" ;
858
var buttons = form . querySelectorAll ( " button.agent-btn" ) ;
859
buttons . forEach ( function ( b ) { b . disabled = true ; } ) ;
860
var data = new URLSearchParams ( new FormData ( form ) ) ;
861
data . set ( " assistant" , btn . value ) ;
862
var finished = false ;
863
function end ( message , keep ) { finished = true ; if ( ! keep ) show ( before ) ; note . textContent = message ; }
864
fetch ( btn . dataset . stream , { method : " POST" , body : data , credentials : " same-origin" } )
865
. then ( function ( r ) {
866
if ( ! r . ok || ! r . body ) throw new Error ( " the hub answered " + r . status ) ;
867
var reader = r . body . getReader ( ) , dec = new TextDecoder ( ) , buf = " " ;
868
function pump ( ) {
869
return reader . read ( ) . then ( function ( res ) {
870
if ( res . done ) { if ( ! finished ) end ( btn . value + " stopped before finishing." , text !== " " ) ; return ; }
871
buf += dec . decode ( res . value , { stream : true } ) ;
872
var i ;
873
while ( ( i = buf . indexOf ( " \n \n " ) ) >= 0 ) {
874
var block = buf . slice ( 0 , i ) , kind = " message" , payload = " " ;
875
buf = buf . slice ( i + 2 ) ;
876
block . split ( " \n " ) . forEach ( function ( l ) {
877
if ( l . indexOf ( " event:" ) === 0 ) kind = l . slice ( 6 ) . trim ( ) ;
878
else if ( l . indexOf ( " data:" ) === 0 ) payload += l . slice ( 5 ) . trim ( ) ;
879
} ) ;
880
if ( ! payload ) continue ;
881
var v ;
882
try { v = JSON . parse ( payload ) ; } catch ( _ ) { continue ; }
c43e4f63 Andy McCall 11 days ago ↶
883
if ( kind === " part" ) { text += v ; show ( untitled ( text ) ) ; }
884
else if ( kind === " title" ) {
885
if ( titleField && ( ! titleField . value . trim ( ) || titleField . value === titleField . defaultValue ) ) titleField . value = v ;
886
}
119fd17d Andy McCall 20 days ago ↶
887
else if ( kind === " done" ) { show ( v ) ; end ( " Drafted by " + btn . value + " ; nothing is saved until you save the proposal." , true ) ; return ; }
888
else if ( kind === " error" ) { end ( v , false ) ; return ; }
889
}
890
return pump ( ) ;
891
} ) ;
892
}
893
return pump ( ) ;
894
} )
895
. catch ( function ( err ) { end ( btn . value + " did not answer: " + err . message + " ." , false ) ; } )
896
. then ( function ( ) { buttons . forEach ( function ( b ) { b . disabled = false ; } ) ; } ) ;
897
} ) ;
898
} ) ;
a7aa76ac Andy McCall 19 days ago
899
900
901
902
903
904
document . addEventListener ( " click" , function ( e ) {
905
var el = e . target . closest && e . target . closest ( " [data-confirm]" ) ;
906
if ( el && el . tagName !== " FORM" && ! window . confirm ( el . getAttribute ( " data-confirm" ) ) ) {
907
e . preventDefault ( ) ;
908
e . stopImmediatePropagation ( ) ;
909
}
910
} , true ) ;
911
document . addEventListener ( " submit" , function ( e ) {
912
var f = e . target ;
913
if ( f . hasAttribute && f . hasAttribute ( " data-confirm" ) && ! window . confirm ( f . getAttribute ( " data-confirm" ) ) ) e . preventDefault ( ) ;
914
} , true ) ;
915
document . addEventListener ( " change" , function ( e ) {
916
var el = e . target ;
917
if ( el . hasAttribute && el . hasAttribute ( " data-autosubmit" ) && el . form ) el . form . submit ( ) ;
918
} ) ;
919
function selectAll ( e ) {
920
var el = e . target ;
921
if ( el . hasAttribute && el . hasAttribute ( " data-select" ) && el . select ) el . select ( ) ;
922
}
923
document . addEventListener ( " focusin" , selectAll ) ;
924
document . addEventListener ( " click" , selectAll ) ;
925
926
927
( function ( ) {
928
var sel = document . querySelector ( " [data-mail-service]" ) ;
929
if ( ! sel ) return ;
930
var show = function ( ) {
931
document . querySelectorAll ( " .mail-panel [data-for]" ) . forEach ( function ( el ) {
932
el . hidden = el . getAttribute ( " data-for" ) . split ( " " ) . indexOf ( sel . value ) < 0 ;
933
} ) ;
934
} ;
935
sel . addEventListener ( " change" , show ) ;
936
show ( ) ;
937
} ) ( ) ;
d366306d Andy McCall 17 days ago ↶
938
939
940
941
942
( function ( ) {
943
var list = document . querySelector ( " [data-drop-files]" ) ;
944
var add = document . querySelector ( " [data-drop-add]" ) ;
945
if ( ! list || ! add ) return ;
946
function wire ( row ) {
947
var rm = row . querySelector ( " [data-drop-remove]" ) ;
948
if ( ! rm ) return ;
949
rm . hidden = false ;
950
rm . addEventListener ( " click" , function ( ) {
951
if ( list . querySelectorAll ( " [data-drop-file]" ) . length > 1 ) row . remove ( ) ;
952
else row . querySelectorAll ( " input, textarea" ) . forEach ( function ( f ) { f . value = " " ; } ) ;
953
} ) ;
954
}
955
list . querySelectorAll ( " [data-drop-file]" ) . forEach ( wire ) ;
956
add . hidden = false ;
957
add . addEventListener ( " click" , function ( ) {
958
var rows = list . querySelectorAll ( " [data-drop-file]" ) ;
959
var row = rows [ rows . length - 1 ] . cloneNode ( true ) ;
960
row . querySelectorAll ( " input, textarea" ) . forEach ( function ( f ) { f . value = " " ; f . textContent = " " ; } ) ;
961
list . appendChild ( row ) ;
962
wire ( row ) ;
963
row . querySelector ( " input" ) . focus ( ) ;
964
} ) ;
965
} ) ( ) ;
966
967
968
969
970
971
( function ( ) {
972
var nav = document . querySelector ( " [data-settings-anchors]" ) ;
973
var hash = location . hash . slice ( 1 ) ;
974
if ( ! nav || ! / ^ [ a-z-] + $ / . test ( hash ) ) return ;
975
var moved = { " history-scan" : " push-protection" , " flow-priority" : " runs" , " flow-supersede" : " runs" , " agent-gates" : " gates" } ;
976
var link = nav . querySelector ( ' a[data-slug="' + ( moved [ hash ] || hash ) + ' "]' ) ;
977
if ( link && link . getAttribute ( " aria-current" ) !== " page" ) location . replace ( link . getAttribute ( " href" ) + " #" + hash ) ;
978
} ) ( ) ;
a22e4aec Andy McCall 5 days ago ↶
979
980
981
982
983
( function ( ) {
984
var box = document . querySelector ( " [data-approval-poll]" ) ;
985
if ( ! box ) return ;
986
var status = box . querySelector ( " [data-approval-status]" ) ;
987
var form = box . closest ( " form" ) ;
988
var url = box . getAttribute ( " data-approval-poll" ) ;
989
var said = {
990
denied : " Your phone said no, or a different number was picked. Sign in again, or use a code." ,
991
expired : " That took too long. Start again, or use a code." ,
992
unknown : " That approval is gone. Start again."
993
} ;
994
var tries = 0 ;
995
function ask ( ) {
996
var req ;
997
if ( form ) {
998
var body = new URLSearchParams ( ) ;
999
[ " username" , " pending" , " approval" ] . forEach ( function ( n ) {
1000
var el = form . querySelector ( ' input[name="' + n + ' "]' ) ;
1001
if ( el ) body . set ( n , el . value ) ;
1002
} ) ;
1003
req = fetch ( url , { method : " POST" , credentials : " same-origin" , body : body } ) ;
1004
} else {
1005
req = fetch ( url , { credentials : " same-origin" , headers : { accept : " application/json" } } ) ;
1006
}
1007
req . then ( function ( r ) { return r . json ( ) ; } ) . then ( function ( j ) {
1008
var state = j && j . state ;
1009
box . setAttribute ( " data-state" , state || " " ) ;
1010
if ( state === " approved" ) {
1011
if ( status ) status . textContent = " Approved. Carrying on…" ;
1012
if ( form ) form . submit ( ) ; else location . href = box . getAttribute ( " data-approval-next" ) || " /" ;
1013
} else if ( said [ state ] || state === " used" ) {
1014
if ( status ) status . textContent = said [ state ] || said . unknown ;
1015
} else if ( ++ tries < 75 ) {
1016
setTimeout ( ask , 2000 ) ;
1017
}
1018
} ) . catch ( function ( ) { if ( ++ tries < 75 ) setTimeout ( ask , 4000 ) ; } ) ;
1019
}
1020
setTimeout ( ask , 1500 ) ;
1021
} ) ( ) ;
119fd17d Andy McCall 20 days ago ↶
1022
} ) ( ) ;
0bc95135 Andy McCall 2 days ago ↶
1023
1024
1025
1026
1027
( function ( ) {
1028
var filter = document . querySelector ( " [data-board-filter]" ) ;
1029
if ( ! filter ) return ;
1030
function cards ( ) {
1031
return Array . prototype . slice . call ( document . querySelectorAll ( " .card-item[data-card]" ) ) ;
1032
}
1033
function typing ( el ) {
1034
return el && ( el . tagName === " INPUT" || el . tagName === " TEXTAREA" || el . tagName === " SELECT" || el . isContentEditable ) ;
1035
}
1036
document . addEventListener ( " keydown" , function ( e ) {
1037
if ( e . ctrlKey || e . metaKey || e . altKey || typing ( document . activeElement ) ) return ;
1038
var list = cards ( ) ;
1039
var at = list . indexOf ( document . activeElement && document . activeElement . closest ? document . activeElement . closest ( " .card-item[data-card]" ) : null ) ;
1040
if ( e . key === " /" ) {
1041
e . preventDefault ( ) ;
1042
filter . focus ( ) ;
1043
filter . select ( ) ;
1044
} else if ( e . key === " j" || e . key === " k" ) {
1045
if ( ! list . length ) return ;
1046
e . preventDefault ( ) ;
1047
var next = at < 0 ? 0 : Math . max ( 0 , Math . min ( list . length - 1 , at + ( e . key === " j" ? 1 : - 1 ) ) ) ;
1048
list [ next ] . focus ( ) ;
1049
list [ next ] . scrollIntoView ( { block : " nearest" , inline : " nearest" } ) ;
1050
} else if ( e . key === " Enter" && at >= 0 ) {
1051
var href = list [ at ] . getAttribute ( " data-href" ) ;
1052
if ( href ) {
1053
e . preventDefault ( ) ;
1054
window . location . href = href ;
1055
}
1056
} else if ( e . key === " m" && at >= 0 ) {
1057
var sel = list [ at ] . querySelector ( " select[name=column]" ) ;
1058
if ( sel ) {
1059
e . preventDefault ( ) ;
1060
if ( list [ at ] . closest ( " .compact" ) ) list [ at ] . querySelector ( " .card-actions" ) . style . display = " flex" ;
1061
sel . focus ( ) ;
1062
}
1063
}
1064
} ) ;
1065
1066
document . querySelectorAll ( " select[data-autosubmit]" ) . forEach ( function ( sel ) {
1067
sel . addEventListener ( " change" , function ( ) {
1068
var form = sel . form ;
1069
var btn = form && form . querySelector ( " button[name=action][value=move]" ) ;
1070
if ( btn ) btn . click ( ) ;
1071
} ) ;
1072
} ) ;
1073
} ) ( ) ;