haveapi 0.31.2 → 0.32.0
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 +4 -4
- data/.gitignore +2 -0
- data/benchmarks/documentation.rb +29 -6
- data/benchmarks/endpoints.rb +153 -24
- data/benchmarks/spec/client_examples_spec.rb +109 -37
- data/doc/client-examples.md +218 -45
- data/doc/documentation-oauth.md +157 -0
- data/doc/documentation-ui.md +192 -0
- data/doc/index.md +3 -6
- data/doc/performance.md +39 -12
- data/haveapi.gemspec +1 -1
- data/lib/haveapi/authentication/chain.rb +19 -0
- data/lib/haveapi/authentication/oauth2/provider.rb +7 -1
- data/lib/haveapi/client_example.rb +143 -15
- data/lib/haveapi/client_examples/curl.rb +30 -20
- data/lib/haveapi/client_examples/dynamic_helpers.rb +1 -1
- data/lib/haveapi/client_examples/go_client.rb +9 -2
- data/lib/haveapi/client_examples/http.rb +9 -4
- data/lib/haveapi/client_examples/js_async_client.rb +7 -0
- data/lib/haveapi/client_examples/js_client.rb +47 -36
- data/lib/haveapi/client_examples/php_client.rb +161 -75
- data/lib/haveapi/client_examples/python_async_client.rb +3 -0
- data/lib/haveapi/client_examples/python_client.rb +61 -47
- data/lib/haveapi/client_examples/ruby_cli.rb +27 -18
- data/lib/haveapi/client_examples/ruby_client.rb +80 -31
- data/lib/haveapi/client_examples/rust_client.rb +20 -9
- data/lib/haveapi/client_examples/ts_async_client.rb +7 -0
- data/lib/haveapi/client_examples/ts_client.rb +3 -0
- data/lib/haveapi/client_examples/ts_generated_client.rb +10 -2
- data/lib/haveapi/documentation_context.rb +7 -2
- data/lib/haveapi/documentation_helpers.rb +223 -0
- data/lib/haveapi/documentation_oauth.rb +399 -0
- data/lib/haveapi/documentation_search.rb +225 -0
- data/lib/haveapi/documentation_search_index.rb +79 -0
- data/lib/haveapi/documentation_validators.rb +179 -0
- data/lib/haveapi/public/css/documentation.css +343 -0
- data/lib/haveapi/public/js/documentation.js +992 -0
- data/lib/haveapi/server.rb +144 -86
- data/lib/haveapi/shared_documentation_helpers.rb +54 -0
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi/views/doc_layout.erb +16 -0
- data/lib/haveapi/views/doc_sidebars/create-client.erb +3 -3
- data/lib/haveapi/views/doc_sidebars/json-schema.erb +1 -1
- data/lib/haveapi/views/doc_sidebars/protocol.erb +5 -5
- data/lib/haveapi/views/documentation/search_form.erb +10 -0
- data/lib/haveapi/views/documentation/search_index.erb +1 -0
- data/lib/haveapi/views/documentation/search_results.erb +20 -0
- data/lib/haveapi/views/index.erb +2 -4
- data/lib/haveapi/views/main_layout.erb +12 -6
- data/lib/haveapi/views/shared_documentation.erb +12 -0
- data/lib/haveapi/views/shared_sidebar.erb +25 -0
- data/lib/haveapi/views/version_page/auth_body.erb +12 -33
- data/lib/haveapi/views/version_page/client_overview.erb +40 -0
- data/lib/haveapi/views/version_page/example_panels.erb +18 -0
- data/lib/haveapi/views/version_page/resource_body.erb +91 -132
- data/lib/haveapi/views/version_page/resource_directory.erb +14 -0
- data/lib/haveapi/views/version_page/usage.erb +7 -0
- data/lib/haveapi/views/version_page.erb +36 -117
- data/lib/haveapi/views/version_sidebar/auth_nav.erb +12 -16
- data/lib/haveapi/views/version_sidebar/auth_resource_nav.erb +19 -0
- data/lib/haveapi/views/version_sidebar/authentication_control.erb +26 -0
- data/lib/haveapi/views/version_sidebar/authentication_status.erb +3 -0
- data/lib/haveapi/views/version_sidebar/brand.erb +31 -0
- data/lib/haveapi/views/version_sidebar/resource_nav.erb +11 -20
- data/lib/haveapi/views/version_sidebar.erb +59 -48
- data/spec/authentication/basic_spec.rb +10 -10
- data/spec/authentication/oauth2_chain_spec.rb +72 -0
- data/spec/authentication/oauth2_token_validation_spec.rb +38 -0
- data/spec/client_examples/assets/documentation_test.cjs +817 -0
- data/spec/client_examples/context_performance_spec.rb +5 -4
- data/spec/client_examples/context_spec.rb +23 -2
- data/spec/client_examples/detail_spec.rb +175 -0
- data/spec/client_examples/dynamic_acceptance.md +59 -26
- data/spec/client_examples/dynamic_acceptance.rb +164 -44
- data/spec/client_examples/dynamic_acceptance_spec.rb +140 -23
- data/spec/client_examples/dynamic_fixture.rb +4 -3
- data/spec/client_examples/dynamic_js_spec.rb +20 -11
- data/spec/client_examples/dynamic_metadata_spec.rb +69 -0
- data/spec/client_examples/dynamic_mounted_paths_spec.rb +9 -7
- data/spec/client_examples/dynamic_python_spec.rb +62 -11
- data/spec/client_examples/go_spec.rb +14 -1
- data/spec/client_examples/presentation_spec.rb +247 -0
- data/spec/client_examples/profile_mount_spec.rb +40 -13
- data/spec/client_examples/rust_client_spec.rb +35 -1
- data/spec/client_examples/shell_spec.rb +268 -0
- data/spec/client_examples/syntax_spec.rb +192 -0
- data/spec/client_examples/ts_generated_client_spec.rb +15 -2
- data/spec/client_examples/typescript_profiles_spec.rb +1 -1
- data/spec/documentation/auth_filtering_spec.rb +9 -4
- data/spec/documentation/canonical_routes_spec.rb +89 -0
- data/spec/documentation/client_examples_spec.rb +39 -5
- data/spec/documentation/client_overview_spec.rb +145 -0
- data/spec/documentation/current_user_html_escaping_spec.rb +7 -2
- data/spec/documentation/example_variants_spec.rb +93 -0
- data/spec/documentation/fixtures/search_index.json +476 -0
- data/spec/documentation/identity_and_guides_spec.rb +130 -0
- data/spec/documentation/identity_context_spec.rb +180 -0
- data/spec/documentation/navigation_spec.rb +296 -0
- data/spec/documentation/oauth_fixture_spec.rb +39 -0
- data/spec/documentation/oauth_spec.rb +912 -0
- data/spec/documentation/oauth_transport_spec.rb +45 -0
- data/spec/documentation/performance_spec.rb +47 -6
- data/spec/documentation/reference_headings_spec.rb +190 -0
- data/spec/documentation/search_index_routes_spec.rb +202 -0
- data/spec/documentation/search_index_spec.rb +85 -0
- data/spec/documentation/search_routes_spec.rb +199 -0
- data/spec/documentation/search_spec.rb +134 -0
- data/spec/documentation/shared_pages_spec.rb +240 -0
- data/spec/documentation/validators_spec.rb +123 -0
- data/spec/documentation_spec.rb +3 -3
- data/spec/packaging_spec.rb +21 -2
- data/spec/performance/documentation_identity_fixture_spec.rb +47 -0
- data/spec/performance/documentation_shapes_spec.rb +97 -0
- data/spec/performance/json_activation_spec.rb +78 -1
- data/spec/performance/release_lineage_spec.rb +4 -3
- data/spec/performance/runner_spec.rb +1 -0
- data/test_support/all_client_performance_fixture.rb +6 -10
- data/test_support/documentation_oauth_fixture.rb +54 -0
- data/test_support/documentation_performance_fixture.rb +19 -1
- data/test_support/dynamic_documentation_api.rb +35 -10
- data/test_support/dynamic_python_login_spec.rb +3 -11
- metadata +52 -3
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var storageKey = 'haveapi.docs.preferences.v1';
|
|
5
|
+
|
|
6
|
+
function preferences(value) {
|
|
7
|
+
var source = value && typeof value === 'object' ? value : {};
|
|
8
|
+
var clients = Object.create(null);
|
|
9
|
+
if (source.clients && typeof source.clients === 'object') {
|
|
10
|
+
Object.keys(source.clients).forEach(function (language) {
|
|
11
|
+
if (typeof source.clients[language] === 'string') clients[language] = source.clients[language];
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
theme: ['auto', 'light', 'dark'].indexOf(source.theme) >= 0 ? source.theme : 'auto',
|
|
16
|
+
language: typeof source.language === 'string' ? source.language : '',
|
|
17
|
+
clients: clients,
|
|
18
|
+
detail: source.detail === 'thorough' ? 'thorough' : 'simple'
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function readPreferences(storage) {
|
|
23
|
+
try { return preferences(JSON.parse(storage.getItem(storageKey))); }
|
|
24
|
+
catch (error) { return preferences(null); }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function clientChoice(state, clients) {
|
|
28
|
+
if (!clients.length) return null;
|
|
29
|
+
var group = clients.filter(function (client) { return client.language === state.language; });
|
|
30
|
+
if (!group.length) group = clients.filter(function (client) { return client.language === clients[0].language; });
|
|
31
|
+
return group.find(function (client) { return client.id === state.clients[group[0].language]; }) || group[0];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function detailLevels(client) {
|
|
35
|
+
if (client && client.details === undefined) return ['simple', 'thorough'];
|
|
36
|
+
var levels = client && client.details;
|
|
37
|
+
return Array.isArray(levels) && levels.length === 2 && levels[0] === 'simple' && levels[1] === 'thorough'
|
|
38
|
+
? ['simple', 'thorough'] : ['simple'];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function effectiveDetail(state, client) {
|
|
42
|
+
return detailLevels(client).includes(state.detail) ? state.detail : 'simple';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function exampleDetail(detail, client, declaration) {
|
|
46
|
+
// Old/custom pages without group metadata keep their page-level contract.
|
|
47
|
+
if (declaration === null || declaration === undefined) return detail;
|
|
48
|
+
var levels = ['simple'];
|
|
49
|
+
try {
|
|
50
|
+
var groups = JSON.parse(declaration);
|
|
51
|
+
if (groups && typeof groups === 'object' && !Array.isArray(groups)) {
|
|
52
|
+
if (!client || !Object.prototype.hasOwnProperty.call(groups, client.id)) return null;
|
|
53
|
+
levels = detailLevels({ details: groups[client.id] });
|
|
54
|
+
}
|
|
55
|
+
} catch (error) { /* Malformed group data keeps the renderer's Simple panel. */ }
|
|
56
|
+
return levels.includes(detail) ? detail : 'simple';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function nativeCopy(text, win, doc) {
|
|
60
|
+
if (typeof doc.execCommand !== 'function') return false;
|
|
61
|
+
var active = doc.activeElement;
|
|
62
|
+
var selection = win.getSelection ? win.getSelection() : null;
|
|
63
|
+
var ranges = [];
|
|
64
|
+
var scroll = [];
|
|
65
|
+
var inputSelection = active && typeof active.selectionStart === 'number'
|
|
66
|
+
? [active.selectionStart, active.selectionEnd, active.selectionDirection] : null;
|
|
67
|
+
var x = win.scrollX;
|
|
68
|
+
var y = win.scrollY;
|
|
69
|
+
if (selection) for (var i = 0; i < selection.rangeCount; i += 1) ranges.push(selection.getRangeAt(i).cloneRange());
|
|
70
|
+
for (var ancestor = active; ancestor; ancestor = ancestor.parentElement) {
|
|
71
|
+
scroll.push({ element: ancestor, left: ancestor.scrollLeft, top: ancestor.scrollTop });
|
|
72
|
+
}
|
|
73
|
+
var field = doc.createElement('textarea');
|
|
74
|
+
field.value = text;
|
|
75
|
+
field.tabIndex = -1;
|
|
76
|
+
field.setAttribute('readonly', '');
|
|
77
|
+
field.style.cssText = 'position:fixed;top:0;left:0;width:1px;height:1px;padding:0;border:0;opacity:0;pointer-events:none';
|
|
78
|
+
try {
|
|
79
|
+
doc.body.appendChild(field);
|
|
80
|
+
field.focus({ preventScroll: true });
|
|
81
|
+
field.select();
|
|
82
|
+
return doc.execCommand('copy') === true;
|
|
83
|
+
} catch (error) { return false; }
|
|
84
|
+
finally {
|
|
85
|
+
field.remove();
|
|
86
|
+
if (active && active.isConnected) {
|
|
87
|
+
try {
|
|
88
|
+
active.focus({ preventScroll: true });
|
|
89
|
+
if (inputSelection) active.setSelectionRange.apply(active, inputSelection);
|
|
90
|
+
} catch (error) { /* A removed or disabled control cannot regain focus. */ }
|
|
91
|
+
}
|
|
92
|
+
if (selection) {
|
|
93
|
+
try { selection.removeAllRanges(); ranges.forEach(function (range) { selection.addRange(range); }); }
|
|
94
|
+
catch (error) { /* A selection may refer to content removed by the page. */ }
|
|
95
|
+
}
|
|
96
|
+
scroll.forEach(function (position) { position.element.scrollLeft = position.left; position.element.scrollTop = position.top; });
|
|
97
|
+
win.scrollTo(x, y);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function copyText(text, win, doc, navigator) {
|
|
102
|
+
try {
|
|
103
|
+
if (win.isSecureContext && navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
104
|
+
return Promise.resolve(navigator.clipboard.writeText(text)).then(function () { return true; }, function () { return false; });
|
|
105
|
+
}
|
|
106
|
+
return Promise.resolve(!win.isSecureContext && win.location.protocol === 'http:' && nativeCopy(text, win, doc));
|
|
107
|
+
} catch (error) { return Promise.resolve(false); }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function visibleExampleCode(panel) {
|
|
111
|
+
if (panel.closest('[hidden]') || (panel.tagName === 'DETAILS' && !panel.open) || !panel.getClientRects().length) return null;
|
|
112
|
+
return panel.querySelector(':scope > [data-example-code-block] > pre > code');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function exampleCopyControls(root, win, doc, navigator) {
|
|
116
|
+
var resets = [];
|
|
117
|
+
Array.from(root.querySelectorAll('[data-example] > [data-example-panel] > pre > code, '
|
|
118
|
+
+ '[data-example] > [data-example-panel] > [data-example-code-block] > pre > code')).forEach(function (source) {
|
|
119
|
+
var pre = source.parentElement;
|
|
120
|
+
var block = pre.parentElement;
|
|
121
|
+
if (block.hasAttribute('data-example-code-block') && block.documentationCopyReset) {
|
|
122
|
+
resets.push(block.documentationCopyReset);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
var panel = block;
|
|
126
|
+
block = doc.createElement('div');
|
|
127
|
+
block.className = 'docs-code-block';
|
|
128
|
+
block.setAttribute('data-example-code-block', '');
|
|
129
|
+
panel.insertBefore(block, pre);
|
|
130
|
+
block.appendChild(pre);
|
|
131
|
+
var button = doc.createElement('button');
|
|
132
|
+
button.type = 'button';
|
|
133
|
+
button.setAttribute('data-copy-example', '');
|
|
134
|
+
button.setAttribute('aria-label', 'Copy example');
|
|
135
|
+
button.title = 'Copy example';
|
|
136
|
+
var icon = doc.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
137
|
+
icon.setAttribute('viewBox', '0 0 24 24');
|
|
138
|
+
icon.setAttribute('aria-hidden', 'true');
|
|
139
|
+
icon.setAttribute('focusable', 'false');
|
|
140
|
+
icon.setAttribute('fill', 'none');
|
|
141
|
+
icon.setAttribute('stroke', 'currentColor');
|
|
142
|
+
icon.setAttribute('stroke-width', '2');
|
|
143
|
+
var outline = doc.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
144
|
+
outline.setAttribute('d', 'M8 8h12v13H8zM16 8V3H3v13h5');
|
|
145
|
+
icon.appendChild(outline);
|
|
146
|
+
button.appendChild(icon);
|
|
147
|
+
var status = doc.createElement('span');
|
|
148
|
+
status.className = 'docs-copy-status';
|
|
149
|
+
status.setAttribute('role', 'status');
|
|
150
|
+
status.setAttribute('aria-live', 'polite');
|
|
151
|
+
block.insertBefore(button, pre);
|
|
152
|
+
block.insertBefore(status, pre);
|
|
153
|
+
var generation = 0;
|
|
154
|
+
block.documentationCopyReset = function () { generation += 1; status.textContent = ''; };
|
|
155
|
+
resets.push(block.documentationCopyReset);
|
|
156
|
+
button.addEventListener('click', function () {
|
|
157
|
+
var code = visibleExampleCode(panel);
|
|
158
|
+
if (!code) return;
|
|
159
|
+
resets.forEach(function (reset) { reset(); });
|
|
160
|
+
var current = generation;
|
|
161
|
+
copyText(code.textContent, win, doc, navigator).then(function (success) {
|
|
162
|
+
if (current !== generation || visibleExampleCode(panel) !== code) return;
|
|
163
|
+
status.textContent = success ? 'Copied' : 'Select the code and copy it.';
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
return function () { resets.forEach(function (reset) { reset(); }); };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function disclosureMenus(root, sidebar, win, doc) {
|
|
171
|
+
var menus = Array.from(root.querySelectorAll('[data-documentation-menu]'));
|
|
172
|
+
function position(menu) {
|
|
173
|
+
if (!menu.open) return;
|
|
174
|
+
var content = menu.querySelector('[data-documentation-menu-content]');
|
|
175
|
+
if (!content) return;
|
|
176
|
+
var bounds = sidebar ? sidebar.getBoundingClientRect() : { left: 0, right: win.innerWidth };
|
|
177
|
+
var left = menu.getBoundingClientRect().left;
|
|
178
|
+
var desired = Math.max(bounds.left + 8, Math.min(left, bounds.right - content.getBoundingClientRect().width - 8));
|
|
179
|
+
content.style.left = (desired - left) + 'px';
|
|
180
|
+
}
|
|
181
|
+
menus.forEach(function (menu) {
|
|
182
|
+
menu.addEventListener('toggle', function () {
|
|
183
|
+
position(menu);
|
|
184
|
+
});
|
|
185
|
+
menu.addEventListener('keydown', function (event) {
|
|
186
|
+
if (event.key !== 'Escape' || !menu.open) return;
|
|
187
|
+
event.preventDefault();
|
|
188
|
+
event.stopPropagation();
|
|
189
|
+
menu.open = false;
|
|
190
|
+
menu.querySelector('summary').focus({ preventScroll: true });
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
doc.addEventListener('pointerdown', function (event) {
|
|
194
|
+
menus.forEach(function (menu) { if (!menu.contains(event.target)) menu.open = false; });
|
|
195
|
+
});
|
|
196
|
+
win.addEventListener('resize', function () { menus.forEach(position); });
|
|
197
|
+
return function () { menus.forEach(function (menu) { menu.open = false; }); };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function legacyTarget(fragment, anchors) {
|
|
201
|
+
var anchor;
|
|
202
|
+
try { anchor = decodeURIComponent(fragment.replace(/^#/, '')); }
|
|
203
|
+
catch (error) { return null; }
|
|
204
|
+
var keys = Object.keys(anchors).sort(function (left, right) { return right.length - left.length; });
|
|
205
|
+
var key = keys.find(function (entry) {
|
|
206
|
+
return anchor === entry || (entry.indexOf('example-') === 0 && anchor.indexOf(entry) === 0);
|
|
207
|
+
});
|
|
208
|
+
return key && typeof anchors[key] === 'string' ? anchors[key] : null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function loginTarget(href, currentHref, fragment) {
|
|
212
|
+
var url;
|
|
213
|
+
var current;
|
|
214
|
+
try { current = new URL(currentHref); url = new URL(href, current); }
|
|
215
|
+
catch (error) { return null; }
|
|
216
|
+
if (url.origin !== current.origin || url.username || url.password) return null;
|
|
217
|
+
var anchor = fragment.replace(/^#/, '');
|
|
218
|
+
url.searchParams.delete('fragment');
|
|
219
|
+
if (/^[A-Za-z0-9_.-]{1,256}$/.test(anchor)) url.searchParams.set('fragment', anchor);
|
|
220
|
+
return url.href;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function highlighterTarget(scriptSrc, currentHref, suppliedURL) {
|
|
224
|
+
try {
|
|
225
|
+
if (!scriptSrc && !suppliedURL) return null;
|
|
226
|
+
var current = new URL(currentHref);
|
|
227
|
+
var url = suppliedURL ? new URL(suppliedURL, current) : new URL('highlighter.js', scriptSrc);
|
|
228
|
+
return url.origin === current.origin && !url.username && !url.password ? url.href : null;
|
|
229
|
+
} catch (error) { return null; }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function activeSection(sections, scrollPadding) {
|
|
233
|
+
var active = null;
|
|
234
|
+
var padding = parseFloat(scrollPadding) || 0;
|
|
235
|
+
sections.forEach(function (section) {
|
|
236
|
+
// Native fragment scrolling combines root padding and target margin.
|
|
237
|
+
// Allow one pixel for fractional layout rounding at the landing point.
|
|
238
|
+
if (section.top <= padding + (parseFloat(section.margin) || 0) + 1) active = section;
|
|
239
|
+
});
|
|
240
|
+
return active || sections[0] || null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function navigationTargets(fragments, lookup) {
|
|
244
|
+
var seen = new Set();
|
|
245
|
+
return fragments.map(function (fragment) {
|
|
246
|
+
if (!fragment || fragment.charAt(0) !== '#') return null;
|
|
247
|
+
try { return lookup(decodeURIComponent(fragment.slice(1))); }
|
|
248
|
+
catch (error) { return null; }
|
|
249
|
+
}).filter(function (target) {
|
|
250
|
+
if (!target || seen.has(target)) return false;
|
|
251
|
+
seen.add(target);
|
|
252
|
+
return true;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function referenceNavigation(root, win, doc) {
|
|
257
|
+
var primaryLinks = Array.from(root.querySelectorAll('[data-action-nav] [data-doc-primary]'));
|
|
258
|
+
// Shared technical pages retain their ordinary flat table of contents.
|
|
259
|
+
if (!primaryLinks.length) primaryLinks = Array.from(root.querySelectorAll('[data-action-nav] a[href^="#"]'));
|
|
260
|
+
function targets(links) {
|
|
261
|
+
return navigationTargets(links.map(function (link) { return link.getAttribute('href'); }),
|
|
262
|
+
function (id) { return doc.getElementById(id); }).sort(function (left, right) {
|
|
263
|
+
return left.compareDocumentPosition(right) & 2 ? 1 : -1;
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
var primaryTargets = targets(primaryLinks);
|
|
267
|
+
var groups = Array.from(root.querySelectorAll('[data-doc-nav-owner]')).map(function (owner) {
|
|
268
|
+
var id = owner.getAttribute('data-doc-nav-owner');
|
|
269
|
+
var subnav = Array.from(owner.querySelectorAll('[data-doc-subnav]')).find(function (list) {
|
|
270
|
+
return list.getAttribute('data-doc-subnav') === id;
|
|
271
|
+
});
|
|
272
|
+
var links = subnav ? Array.from(subnav.querySelectorAll('[data-doc-subsection]')) : [];
|
|
273
|
+
return { id: id, subnav: subnav, links: links, targets: targets(links) };
|
|
274
|
+
});
|
|
275
|
+
function active(targets) {
|
|
276
|
+
return activeSection(targets.map(function (target) {
|
|
277
|
+
return { id: target.id, top: target.getBoundingClientRect().top, margin: win.getComputedStyle(target).scrollMarginTop };
|
|
278
|
+
}), win.getComputedStyle(doc.documentElement).scrollPaddingTop);
|
|
279
|
+
}
|
|
280
|
+
function mark(links, selected) {
|
|
281
|
+
links.forEach(function (link) {
|
|
282
|
+
var target = navigationTargets([link.getAttribute('href')], function (id) { return doc.getElementById(id); })[0];
|
|
283
|
+
if (selected && target && target.id === selected.id) link.setAttribute('aria-current', 'location');
|
|
284
|
+
else link.removeAttribute('aria-current');
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function update() {
|
|
288
|
+
var primary = active(primaryTargets);
|
|
289
|
+
mark(primaryLinks, primary);
|
|
290
|
+
groups.forEach(function (group) {
|
|
291
|
+
var current = primary && primary.id === group.id;
|
|
292
|
+
mark(group.links, current ? active(group.targets) : null);
|
|
293
|
+
if (group.subnav) group.subnav.hidden = !current && !group.subnav.contains(doc.activeElement);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
doc.addEventListener('focusout', function () { win.requestAnimationFrame(update); });
|
|
297
|
+
return { update: update, targets: primaryTargets.concat.apply(primaryTargets, groups.map(function (group) { return group.targets; })) };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function searchShortcut(doc, inputForSearch, openNavigation, focusInput) {
|
|
301
|
+
doc.addEventListener('keydown', function (event) {
|
|
302
|
+
if (event.key !== '/' || event.defaultPrevented || event.isComposing || event.ctrlKey || event.metaKey || event.altKey) return;
|
|
303
|
+
var path = event.composedPath ? event.composedPath() : [event.target];
|
|
304
|
+
if (path.some(function (target) {
|
|
305
|
+
for (var element = target; element; element = element.parentElement) {
|
|
306
|
+
if (/^(INPUT|TEXTAREA|SELECT)$/.test(element.tagName) || element.isContentEditable) return true;
|
|
307
|
+
}
|
|
308
|
+
return false;
|
|
309
|
+
})) return;
|
|
310
|
+
var input = inputForSearch();
|
|
311
|
+
if (!input) return;
|
|
312
|
+
openNavigation();
|
|
313
|
+
if (!input.getClientRects().length) return;
|
|
314
|
+
event.preventDefault();
|
|
315
|
+
focusInput(input);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function searchConfiguration(action, indexURL, key, version, currentHref, bootstrap) {
|
|
320
|
+
try {
|
|
321
|
+
var current = new URL(currentHref);
|
|
322
|
+
var base = new URL(action, current);
|
|
323
|
+
var url = new URL(indexURL, current);
|
|
324
|
+
if (!/^https?:$/.test(base.protocol) || base.origin !== current.origin || base.username || base.password
|
|
325
|
+
|| base.search || base.hash || !base.pathname.endsWith('/') || url.origin !== base.origin
|
|
326
|
+
|| url.pathname !== base.pathname || url.username || url.password || url.hash
|
|
327
|
+
|| Array.from(url.searchParams).length !== 2 || url.searchParams.getAll('page').length !== 1
|
|
328
|
+
|| url.searchParams.get('page') !== 'resources' || url.searchParams.getAll('search_index').length !== 1
|
|
329
|
+
|| typeof version !== 'string' || !version || (key !== '' && !/^[a-f0-9]{64}$/.test(key))) return null;
|
|
330
|
+
if (url.searchParams.get('search_index') !== (key || 'current')) return null;
|
|
331
|
+
if (bootstrap) { url.searchParams.set('search_index', 'current'); key = ''; }
|
|
332
|
+
return { url: url.href, origin: base.origin, base_path: base.pathname, version: version, key: key };
|
|
333
|
+
} catch (error) { return null; }
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function validateSearchIndex(envelope, expected) {
|
|
337
|
+
function fail() { throw new Error('Invalid documentation search index'); }
|
|
338
|
+
function shape(object, fields) {
|
|
339
|
+
if (!object || typeof object !== 'object' || Array.isArray(object)
|
|
340
|
+
|| Object.keys(object).length !== fields.length
|
|
341
|
+
|| !fields.every(function (field) { return Object.prototype.hasOwnProperty.call(object, field); })) fail();
|
|
342
|
+
}
|
|
343
|
+
function string(value) {
|
|
344
|
+
if (typeof value !== 'string') fail();
|
|
345
|
+
Array.from(value).forEach(function (character) {
|
|
346
|
+
var code = character.codePointAt(0);
|
|
347
|
+
if (code >= 0xd800 && code <= 0xdfff) fail();
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function strings(values) { if (!Array.isArray(values)) fail(); values.forEach(string); }
|
|
351
|
+
function parent(value, length) { if (!Number.isInteger(value) || value < 0 || value >= length) fail(); }
|
|
352
|
+
shape(envelope, ['key', 'index']);
|
|
353
|
+
if (typeof envelope.key !== 'string' || !/^[a-f0-9]{64}$/.test(envelope.key)
|
|
354
|
+
|| (expected.key && envelope.key !== expected.key)) fail();
|
|
355
|
+
var index = envelope.index;
|
|
356
|
+
shape(index, ['format', 'version', 'base_path', 'locale', 'visibility', 'resources', 'actions', 'parameters']);
|
|
357
|
+
if (index.format !== 1 || index.version !== expected.version || index.base_path !== expected.base_path
|
|
358
|
+
|| ['anonymous', 'authenticated'].indexOf(index.visibility) < 0) fail();
|
|
359
|
+
['version', 'base_path', 'locale', 'visibility'].forEach(function (field) { string(index[field]); });
|
|
360
|
+
if (!Array.isArray(index.resources) || !Array.isArray(index.actions) || !Array.isArray(index.parameters)) fail();
|
|
361
|
+
index.resources.forEach(function (resource) {
|
|
362
|
+
shape(resource, ['path', 'label', 'authentication', 'search']);
|
|
363
|
+
strings(resource.path);
|
|
364
|
+
if (!resource.path.length) fail();
|
|
365
|
+
string(resource.label);
|
|
366
|
+
if (resource.authentication !== null) string(resource.authentication);
|
|
367
|
+
strings(resource.search);
|
|
368
|
+
});
|
|
369
|
+
index.actions.forEach(function (action) {
|
|
370
|
+
shape(action, ['resource', 'name', 'label', 'identifiers', 'search']);
|
|
371
|
+
parent(action.resource, index.resources.length);
|
|
372
|
+
string(action.name); string(action.label); strings(action.identifiers); strings(action.search);
|
|
373
|
+
});
|
|
374
|
+
index.parameters.forEach(function (parameter) {
|
|
375
|
+
shape(parameter, ['action', 'name', 'label', 'type', 'direction', 'humanized_name']);
|
|
376
|
+
parent(parameter.action, index.actions.length);
|
|
377
|
+
['name', 'label', 'type', 'direction', 'humanized_name'].forEach(function (field) { string(parameter[field]); });
|
|
378
|
+
if (['input', 'output'].indexOf(parameter.direction) < 0) fail();
|
|
379
|
+
});
|
|
380
|
+
return index;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function searchMatcher(index) {
|
|
384
|
+
var labels = new Map(index.resources.map(function (resource) {
|
|
385
|
+
return [JSON.stringify([resource.authentication, resource.path]), resource.label];
|
|
386
|
+
}));
|
|
387
|
+
var resourceLabels = new Map(index.resources.map(function (resource) {
|
|
388
|
+
return [resource, resource.path.map(function (name, position) {
|
|
389
|
+
var key = JSON.stringify([resource.authentication, resource.path.slice(0, position + 1)]);
|
|
390
|
+
return labels.has(key) ? labels.get(key) : name;
|
|
391
|
+
}).join(' · ')];
|
|
392
|
+
}));
|
|
393
|
+
var resourceText = index.resources.map(function (resource) { return resource.search.join(' ').toLowerCase(); });
|
|
394
|
+
var actionText = index.actions.map(function (action) { return action.identifiers.concat(action.search).join(' ').toLowerCase(); });
|
|
395
|
+
var parameterText = index.parameters.map(function (parameter) {
|
|
396
|
+
return [parameter.name, parameter.label, parameter.type, parameter.direction, parameter.humanized_name].join(' ').toLowerCase();
|
|
397
|
+
});
|
|
398
|
+
function compare(left, right) {
|
|
399
|
+
for (var field = 0; field < left.length; field += 1) {
|
|
400
|
+
if (typeof left[field] === 'number') { if (left[field] !== right[field]) return left[field] - right[field]; }
|
|
401
|
+
else {
|
|
402
|
+
var a = Array.from(left[field]); var b = Array.from(right[field]);
|
|
403
|
+
for (var part = 0; part < Math.min(a.length, b.length); part += 1) {
|
|
404
|
+
if (a[part] !== b[part]) return a[part].codePointAt(0) - b[part].codePointAt(0);
|
|
405
|
+
}
|
|
406
|
+
if (a.length !== b.length) return a.length - b.length;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return 0;
|
|
410
|
+
}
|
|
411
|
+
return function (query) {
|
|
412
|
+
var needle = query.trim().toLowerCase();
|
|
413
|
+
if (!needle || Array.from(needle).length > 256) return { results: [], more: false };
|
|
414
|
+
var tokens = needle.split(/[\s\u0085]+/u);
|
|
415
|
+
var matches = [];
|
|
416
|
+
function match(kind, resource, action, parameter, texts, identifiers) {
|
|
417
|
+
if (!tokens.every(function (token) { return texts.some(function (text) { return text.includes(token); }); })) return;
|
|
418
|
+
var names = identifiers.map(function (name) { return name.toLowerCase(); });
|
|
419
|
+
var rank = names.includes(needle) ? 0 : names.some(function (name) { return name.startsWith(needle); }) ? 1 : 2;
|
|
420
|
+
var name = parameter ? parameter.name : action ? action.name : resource.path[resource.path.length - 1];
|
|
421
|
+
var record = { kind: ['resource', 'action', 'parameter'][kind], resource_path: resource.path,
|
|
422
|
+
authentication: resource.authentication, action_name: action ? action.name : null,
|
|
423
|
+
name: name, label: parameter ? parameter.label : action ? action.label : resource.label,
|
|
424
|
+
resource_label: resourceLabels.get(resource),
|
|
425
|
+
direction: parameter ? parameter.direction : null, type: parameter ? parameter.type : null };
|
|
426
|
+
matches.push({ record: record, order: [kind, rank, resource.authentication || '', resource.path.join('.'),
|
|
427
|
+
action ? action.name : '', name, parameter ? parameter.direction : ''] });
|
|
428
|
+
}
|
|
429
|
+
index.resources.forEach(function (resource, position) {
|
|
430
|
+
match(0, resource, null, null, [resourceText[position]], [resource.path[resource.path.length - 1], resource.path.join('.')]);
|
|
431
|
+
});
|
|
432
|
+
index.actions.forEach(function (action, position) {
|
|
433
|
+
match(1, index.resources[action.resource], action, null, [resourceText[action.resource], actionText[position]], action.identifiers);
|
|
434
|
+
});
|
|
435
|
+
index.parameters.forEach(function (parameter, position) {
|
|
436
|
+
var action = index.actions[parameter.action];
|
|
437
|
+
match(2, index.resources[action.resource], action, parameter,
|
|
438
|
+
[resourceText[action.resource], actionText[parameter.action], parameterText[position]], [parameter.name]);
|
|
439
|
+
});
|
|
440
|
+
matches.sort(function (left, right) { return compare(left.order, right.order); });
|
|
441
|
+
return { results: matches.slice(0, 20).map(function (entry) { return entry.record; }), more: matches.length > 20 };
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function searchResultURL(index, result, origin) {
|
|
446
|
+
var url = new URL(index.base_path, origin);
|
|
447
|
+
if (url.origin !== new URL(origin).origin || url.pathname !== index.base_path || url.search || url.hash) throw new Error('Invalid search base path');
|
|
448
|
+
var anchor = 'root-' + result.resource_path.join('-') + (result.action_name === null ? '' : '-' + result.action_name);
|
|
449
|
+
if (result.kind === 'parameter') {
|
|
450
|
+
var tuple = [result.authentication, result.resource_path, result.action_name, result.direction, result.name];
|
|
451
|
+
anchor = 'parameter-' + Array.from(new TextEncoder().encode(JSON.stringify(tuple)))
|
|
452
|
+
.map(function (byte) { return byte.toString(16).padStart(2, '0'); }).join('');
|
|
453
|
+
}
|
|
454
|
+
if (result.authentication !== null) url.searchParams.set('page', 'authentication');
|
|
455
|
+
else url.searchParams.set('resource', result.resource_path.join('.'));
|
|
456
|
+
if (result.kind !== 'resource' || result.authentication !== null) url.hash = anchor;
|
|
457
|
+
return url.href;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function renderSearchResult(doc, index, result, origin) {
|
|
461
|
+
var item = doc.createElement('li');
|
|
462
|
+
var link = doc.createElement('a');
|
|
463
|
+
var name = result.kind === 'resource' ? result.resource_path.join('.') : result.name;
|
|
464
|
+
link.href = searchResultURL(index, result, origin);
|
|
465
|
+
link.textContent = result.kind.charAt(0).toUpperCase() + result.kind.slice(1) + ': ';
|
|
466
|
+
var code = doc.createElement('code');
|
|
467
|
+
code.textContent = name;
|
|
468
|
+
link.appendChild(code);
|
|
469
|
+
item.appendChild(link);
|
|
470
|
+
var context = [];
|
|
471
|
+
if (result.kind === 'resource') {
|
|
472
|
+
if (typeof result.resource_label === 'string' && result.resource_label !== name) context.push(result.resource_label);
|
|
473
|
+
} else {
|
|
474
|
+
context.push(result.resource_path.join('.') + (result.action_name !== null ? ' · ' + result.action_name : ''));
|
|
475
|
+
}
|
|
476
|
+
if (result.kind === 'parameter') context.push(result.direction.charAt(0).toUpperCase() + result.direction.slice(1) + ' · ' + result.type);
|
|
477
|
+
if (result.authentication !== null) context.push('Authentication: ' + result.authentication);
|
|
478
|
+
if (result.kind !== 'resource' && typeof result.label === 'string' && result.label !== result.name) context.push(result.label);
|
|
479
|
+
context.forEach(function (text) {
|
|
480
|
+
var detail = doc.createElement('span');
|
|
481
|
+
detail.textContent = text;
|
|
482
|
+
item.appendChild(detail);
|
|
483
|
+
});
|
|
484
|
+
return item;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function searchIndexLoader(fetcher, Controller, parse) {
|
|
488
|
+
var entries = new Map();
|
|
489
|
+
var epoch = 0;
|
|
490
|
+
function entryKey(config) { return JSON.stringify([config.url, config.version, config.key]); }
|
|
491
|
+
return {
|
|
492
|
+
peek: function (config) { var entry = entries.get(entryKey(config)); return entry ? entry.snapshot || null : null; },
|
|
493
|
+
reset: function () {
|
|
494
|
+
epoch += 1;
|
|
495
|
+
entries.forEach(function (entry) { entry.controller.abort(); });
|
|
496
|
+
entries.clear();
|
|
497
|
+
},
|
|
498
|
+
load: function (config) {
|
|
499
|
+
var key = entryKey(config);
|
|
500
|
+
if (entries.has(key)) return entries.get(key).promise;
|
|
501
|
+
var current = epoch;
|
|
502
|
+
var entry = { controller: new Controller(), promise: null };
|
|
503
|
+
entry.promise = Promise.resolve().then(function () {
|
|
504
|
+
return fetcher(config.url, { method: 'GET', credentials: 'same-origin', cache: 'default', redirect: 'error',
|
|
505
|
+
headers: { Accept: 'text/html' }, signal: entry.controller.signal });
|
|
506
|
+
}).then(function (response) {
|
|
507
|
+
if (!response.ok) throw new Error('Search index unavailable');
|
|
508
|
+
return response.text();
|
|
509
|
+
}).then(function (html) {
|
|
510
|
+
if (current !== epoch) throw new Error('Obsolete search index');
|
|
511
|
+
var index = parse(html, config);
|
|
512
|
+
entry.snapshot = { index: index, match: searchMatcher(index) };
|
|
513
|
+
return entry.snapshot;
|
|
514
|
+
}).catch(function (error) {
|
|
515
|
+
if (entries.get(key) === entry) entries.delete(key);
|
|
516
|
+
throw error;
|
|
517
|
+
});
|
|
518
|
+
entries.set(key, entry);
|
|
519
|
+
return entry.promise;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Pure helpers keep saved-state and legacy-link contracts independently testable.
|
|
525
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
526
|
+
module.exports = { preferences: preferences, readPreferences: readPreferences, clientChoice: clientChoice,
|
|
527
|
+
detailLevels: detailLevels, effectiveDetail: effectiveDetail, exampleDetail: exampleDetail, copyText: copyText,
|
|
528
|
+
exampleCopyControls: exampleCopyControls, disclosureMenus: disclosureMenus,
|
|
529
|
+
legacyTarget: legacyTarget, loginTarget: loginTarget,
|
|
530
|
+
highlighterTarget: highlighterTarget,
|
|
531
|
+
activeSection: activeSection, navigationTargets: navigationTargets, referenceNavigation: referenceNavigation, searchShortcut: searchShortcut,
|
|
532
|
+
searchConfiguration: searchConfiguration, validateSearchIndex: validateSearchIndex, searchMatcher: searchMatcher,
|
|
533
|
+
searchResultURL: searchResultURL, renderSearchResult: renderSearchResult, searchIndexLoader: searchIndexLoader };
|
|
534
|
+
}
|
|
535
|
+
if (typeof document === 'undefined') return;
|
|
536
|
+
|
|
537
|
+
// Capture the script URL while this synchronous head script is executing;
|
|
538
|
+
// document.currentScript is no longer available at DOMContentLoaded.
|
|
539
|
+
var script = document.currentScript;
|
|
540
|
+
var workerURL = highlighterTarget(script && script.src, window.location.href,
|
|
541
|
+
script && script.getAttribute('data-highlighter-url'));
|
|
542
|
+
|
|
543
|
+
var state;
|
|
544
|
+
try { state = readPreferences(window.localStorage); }
|
|
545
|
+
catch (error) { state = preferences(null); }
|
|
546
|
+
var colorScheme = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
|
547
|
+
|
|
548
|
+
function applyTheme() {
|
|
549
|
+
document.documentElement.setAttribute('data-docs-theme', state.theme === 'auto'
|
|
550
|
+
? (colorScheme && colorScheme.matches ? 'dark' : 'light') : state.theme);
|
|
551
|
+
}
|
|
552
|
+
applyTheme();
|
|
553
|
+
if (colorScheme && colorScheme.addEventListener) colorScheme.addEventListener('change', applyTheme);
|
|
554
|
+
|
|
555
|
+
function ready() {
|
|
556
|
+
var root = document.querySelector('[data-docs]');
|
|
557
|
+
if (!root) return;
|
|
558
|
+
var all = function (selector) { return Array.from(root.querySelectorAll(selector)); };
|
|
559
|
+
var json = function (selector, fallback) {
|
|
560
|
+
var element = root.querySelector(selector);
|
|
561
|
+
try { return element ? JSON.parse(element.textContent) : fallback; }
|
|
562
|
+
catch (error) { return fallback; }
|
|
563
|
+
};
|
|
564
|
+
var metadata = json('[data-documentation-clients]', []);
|
|
565
|
+
var clients = Array.isArray(metadata) ? metadata.filter(function (client, index, list) {
|
|
566
|
+
return client && typeof client.id === 'string' && typeof client.language === 'string'
|
|
567
|
+
&& typeof client.language_label === 'string' && client.id && client.language
|
|
568
|
+
&& list.findIndex(function (entry) { return entry && entry.id === client.id; }) === index;
|
|
569
|
+
}) : [];
|
|
570
|
+
var sidebar = root.querySelector('[data-docs-sidebar]');
|
|
571
|
+
var examples = all('[data-example]');
|
|
572
|
+
var sections = all('[data-doc-section]');
|
|
573
|
+
var referenceNav = referenceNavigation(root, window, document);
|
|
574
|
+
var status = root.querySelector('[data-preference-status]');
|
|
575
|
+
var clearCopyStatus = exampleCopyControls(root, window, document, window.navigator || {});
|
|
576
|
+
var closeMenus = disclosureMenus(root, sidebar, window, document);
|
|
577
|
+
|
|
578
|
+
function persist() {
|
|
579
|
+
try { window.localStorage.setItem(storageKey, JSON.stringify(state)); }
|
|
580
|
+
catch (error) { /* Preferences still work for this page when storage is denied. */ }
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function redirectLegacy() {
|
|
584
|
+
if (root.getAttribute('data-page') !== 'overview' || !window.location.hash) return;
|
|
585
|
+
var anchors = json('[data-legacy-anchors]', {});
|
|
586
|
+
if (!anchors || typeof anchors !== 'object' || Array.isArray(anchors)) return;
|
|
587
|
+
var target = legacyTarget(window.location.hash, anchors);
|
|
588
|
+
if (!target) return;
|
|
589
|
+
var url = new URL(target, window.location.href);
|
|
590
|
+
// Only page URLs supplied by the authorized server map can redirect locally.
|
|
591
|
+
if (url.origin !== window.location.origin) return;
|
|
592
|
+
url.hash = window.location.hash;
|
|
593
|
+
if (url.href !== window.location.href) window.location.replace(url.href);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// One queued worker highlights only visible sources. A failed worker leaves
|
|
597
|
+
// the original text in place; no synchronous fallback blocks a large page.
|
|
598
|
+
var worker = null;
|
|
599
|
+
var queue = [];
|
|
600
|
+
var pending = new WeakSet();
|
|
601
|
+
var current = null;
|
|
602
|
+
var codeObserver = null;
|
|
603
|
+
try { if (window.Worker && workerURL) worker = new Worker(workerURL); }
|
|
604
|
+
catch (error) { worker = null; }
|
|
605
|
+
|
|
606
|
+
function visible(code) {
|
|
607
|
+
if (code.closest('[hidden]')) return false;
|
|
608
|
+
var panel = code.closest('details');
|
|
609
|
+
if (panel && !panel.open) return false;
|
|
610
|
+
var rect = code.getBoundingClientRect();
|
|
611
|
+
return rect.width > 0 && rect.height > 0 && rect.bottom >= 0 && rect.top < window.innerHeight;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function nextHighlight() {
|
|
615
|
+
if (!worker || current) return;
|
|
616
|
+
while (queue.length) {
|
|
617
|
+
var code = queue.shift();
|
|
618
|
+
pending.delete(code);
|
|
619
|
+
if (!visible(code) || code.classList.contains('hljs')) continue;
|
|
620
|
+
current = code;
|
|
621
|
+
try { worker.postMessage({ language: code.getAttribute('data-language') || code.classList.item(0), code: code.textContent }); }
|
|
622
|
+
catch (error) { stopWorker(); }
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function stopWorker() {
|
|
627
|
+
if (worker) worker.terminate();
|
|
628
|
+
worker = null;
|
|
629
|
+
queue = [];
|
|
630
|
+
current = null;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function highlight(code) {
|
|
634
|
+
if (!worker || pending.has(code) || current === code || code.classList.contains('hljs') || !visible(code)) return;
|
|
635
|
+
pending.add(code);
|
|
636
|
+
queue.push(code);
|
|
637
|
+
nextHighlight();
|
|
638
|
+
}
|
|
639
|
+
if (worker) {
|
|
640
|
+
worker.onmessage = function (event) {
|
|
641
|
+
if (current && typeof event.data === 'string') {
|
|
642
|
+
current.innerHTML = event.data;
|
|
643
|
+
current.classList.add('hljs');
|
|
644
|
+
}
|
|
645
|
+
current = null;
|
|
646
|
+
nextHighlight();
|
|
647
|
+
};
|
|
648
|
+
worker.onerror = stopWorker;
|
|
649
|
+
if (window.IntersectionObserver) {
|
|
650
|
+
codeObserver = new IntersectionObserver(function (entries) {
|
|
651
|
+
entries.forEach(function (entry) { if (entry.isIntersecting) highlight(entry.target); });
|
|
652
|
+
});
|
|
653
|
+
all('pre code').forEach(function (code) { codeObserver.observe(code); });
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function highlightVisible() { all('pre code').forEach(highlight); }
|
|
658
|
+
|
|
659
|
+
function option(select, value, label) {
|
|
660
|
+
var element = document.createElement('option');
|
|
661
|
+
element.value = value;
|
|
662
|
+
element.textContent = label;
|
|
663
|
+
select.appendChild(element);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function updateControls(choice) {
|
|
667
|
+
var languages = clients.filter(function (client, index, list) {
|
|
668
|
+
return list.findIndex(function (entry) { return entry.language === client.language; }) === index;
|
|
669
|
+
});
|
|
670
|
+
all('[data-language-select]').forEach(function (select) {
|
|
671
|
+
select.replaceChildren();
|
|
672
|
+
languages.forEach(function (client) { option(select, client.language, client.language_label); });
|
|
673
|
+
select.disabled = languages.length < 2;
|
|
674
|
+
if (choice) select.value = choice.language;
|
|
675
|
+
});
|
|
676
|
+
all('[data-variant-select]').forEach(function (select) {
|
|
677
|
+
select.replaceChildren();
|
|
678
|
+
var variants = clients.filter(function (client) { return choice && client.language === choice.language; });
|
|
679
|
+
variants.forEach(function (client) { option(select, client.id, client.variant || client.language_label); });
|
|
680
|
+
if (choice) select.value = choice.id;
|
|
681
|
+
var control = select.closest('[data-variant-control]') || select;
|
|
682
|
+
control.hidden = variants.length < 2;
|
|
683
|
+
select.disabled = variants.length < 2;
|
|
684
|
+
});
|
|
685
|
+
all('[data-detail-select]').forEach(function (select) {
|
|
686
|
+
var levels = choice ? detailLevels(choice) : [];
|
|
687
|
+
select.replaceChildren();
|
|
688
|
+
levels.forEach(function (detail) { option(select, detail, detail === 'thorough' ? 'With error handling' : 'Simple'); });
|
|
689
|
+
(select.closest('[data-detail-control]') || select).hidden = false;
|
|
690
|
+
select.disabled = levels.length < 2;
|
|
691
|
+
select.value = effectiveDetail(state, choice);
|
|
692
|
+
});
|
|
693
|
+
all('[data-theme-choice]').forEach(function (button) {
|
|
694
|
+
button.setAttribute('aria-pressed', String(button.getAttribute('data-theme-choice') === state.theme));
|
|
695
|
+
});
|
|
696
|
+
if (status) {
|
|
697
|
+
var fallback = choice && ((state.language && state.language !== choice.language)
|
|
698
|
+
|| (state.clients[choice.language] && state.clients[choice.language] !== choice.id));
|
|
699
|
+
status.textContent = fallback ? 'Your saved example is unavailable. Showing ' + choice.language_label
|
|
700
|
+
+ (choice.variant ? ' (' + choice.variant + ')' : '') + '.' : '';
|
|
701
|
+
status.hidden = !fallback;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function updateExamples() {
|
|
706
|
+
var choice = clientChoice(state, clients);
|
|
707
|
+
var detail = effectiveDetail(state, choice);
|
|
708
|
+
clearCopyStatus();
|
|
709
|
+
updateControls(choice);
|
|
710
|
+
examples.forEach(function (example) {
|
|
711
|
+
var groupDetail = exampleDetail(detail, choice, example.getAttribute('data-example-details'));
|
|
712
|
+
var panels = Array.from(example.querySelectorAll('[data-example-panel]'));
|
|
713
|
+
var chosen = choice && panels.find(function (panel) {
|
|
714
|
+
return panel.getAttribute('data-client') === choice.id && panel.getAttribute('data-detail') === groupDetail;
|
|
715
|
+
});
|
|
716
|
+
// Missing renderers remain visible as explicit limitations from the
|
|
717
|
+
// server; never silently replace the selected client with another one.
|
|
718
|
+
panels.forEach(function (panel) {
|
|
719
|
+
panel.hidden = panel !== chosen;
|
|
720
|
+
if (panel.tagName === 'DETAILS') panel.open = panel === chosen;
|
|
721
|
+
});
|
|
722
|
+
var unavailable = example.querySelector('[data-example-unavailable]');
|
|
723
|
+
if (!unavailable) {
|
|
724
|
+
unavailable = document.createElement('p');
|
|
725
|
+
unavailable.setAttribute('data-example-unavailable', '');
|
|
726
|
+
unavailable.className = 'docs-notice';
|
|
727
|
+
unavailable.textContent = 'No example is available for this client.';
|
|
728
|
+
example.appendChild(unavailable);
|
|
729
|
+
}
|
|
730
|
+
unavailable.hidden = !!chosen;
|
|
731
|
+
});
|
|
732
|
+
highlightVisible();
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function capturePosition(control) {
|
|
736
|
+
var anchor = control && control.closest('[data-example]');
|
|
737
|
+
if (!anchor) {
|
|
738
|
+
var top = parseFloat(window.getComputedStyle(document.documentElement).scrollPaddingTop) || 0;
|
|
739
|
+
var candidates = examples.concat(sections).filter(function (element) {
|
|
740
|
+
var rect = element.getBoundingClientRect();
|
|
741
|
+
return rect.bottom > top && rect.top < window.innerHeight;
|
|
742
|
+
});
|
|
743
|
+
candidates.sort(function (left, right) {
|
|
744
|
+
return Math.abs(left.getBoundingClientRect().top - top) - Math.abs(right.getBoundingClientRect().top - top);
|
|
745
|
+
});
|
|
746
|
+
anchor = candidates[0];
|
|
747
|
+
}
|
|
748
|
+
return { anchor: anchor, top: anchor ? anchor.getBoundingClientRect().top : 0,
|
|
749
|
+
sidebarTop: sidebar ? sidebar.scrollTop : 0 };
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function updatePreservingPosition(control) {
|
|
753
|
+
var position = capturePosition(control);
|
|
754
|
+
updateExamples();
|
|
755
|
+
window.requestAnimationFrame(function () {
|
|
756
|
+
window.requestAnimationFrame(function () {
|
|
757
|
+
if (position.anchor && position.anchor.isConnected) window.scrollBy(0, position.anchor.getBoundingClientRect().top - position.top);
|
|
758
|
+
if (sidebar) sidebar.scrollTop = position.sidebarTop;
|
|
759
|
+
updateActiveAction();
|
|
760
|
+
highlightVisible();
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
all('[data-language-select]').forEach(function (select) {
|
|
766
|
+
select.addEventListener('change', function () { state.language = select.value; persist(); updatePreservingPosition(select); });
|
|
767
|
+
});
|
|
768
|
+
all('[data-variant-select]').forEach(function (select) {
|
|
769
|
+
select.addEventListener('change', function () {
|
|
770
|
+
var client = clients.find(function (entry) { return entry.id === select.value; });
|
|
771
|
+
if (!client) return;
|
|
772
|
+
state.language = client.language;
|
|
773
|
+
state.clients[client.language] = client.id;
|
|
774
|
+
persist();
|
|
775
|
+
updatePreservingPosition(select);
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
all('[data-detail-select]').forEach(function (select) {
|
|
779
|
+
select.addEventListener('change', function () {
|
|
780
|
+
var choice = clientChoice(state, clients);
|
|
781
|
+
if (detailLevels(choice).length < 2) return;
|
|
782
|
+
state.detail = select.value === 'thorough' ? 'thorough' : 'simple';
|
|
783
|
+
persist();
|
|
784
|
+
updatePreservingPosition(select);
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
all('[data-theme-choice]').forEach(function (button) {
|
|
788
|
+
button.addEventListener('click', function () {
|
|
789
|
+
var theme = button.getAttribute('data-theme-choice');
|
|
790
|
+
if (['auto', 'light', 'dark'].indexOf(theme) < 0) return;
|
|
791
|
+
state.theme = theme;
|
|
792
|
+
persist();
|
|
793
|
+
applyTheme();
|
|
794
|
+
updateControls(clientChoice(state, clients));
|
|
795
|
+
var menu = button.closest('[data-documentation-menu]');
|
|
796
|
+
if (menu) { menu.open = false; menu.querySelector('summary').focus({ preventScroll: true }); }
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
all('a[data-documentation-login]').forEach(function (link) {
|
|
800
|
+
var prepareLogin = function () {
|
|
801
|
+
var target = loginTarget(link.href, window.location.href, window.location.hash);
|
|
802
|
+
if (target) link.href = target;
|
|
803
|
+
};
|
|
804
|
+
link.addEventListener('click', prepareLogin);
|
|
805
|
+
link.addEventListener('auxclick', prepareLogin);
|
|
806
|
+
});
|
|
807
|
+
var searches = [];
|
|
808
|
+
var shortcutFocus = false;
|
|
809
|
+
function clearSearches() { searches.forEach(function (search) { search.clear(); }); }
|
|
810
|
+
var bootstrapSearch = false;
|
|
811
|
+
var indexLoader = null;
|
|
812
|
+
if (window.fetch && window.AbortController && window.TextEncoder && 'content' in document.createElement('template')) {
|
|
813
|
+
indexLoader = searchIndexLoader(window.fetch.bind(window), window.AbortController, function (html, expected) {
|
|
814
|
+
var parsed = document.createElement('template');
|
|
815
|
+
parsed.innerHTML = html;
|
|
816
|
+
var scripts = parsed.content.querySelectorAll('script');
|
|
817
|
+
var data = parsed.content.querySelectorAll('script[type="application/json"][data-documentation-search-index]');
|
|
818
|
+
if (scripts.length !== 1 || data.length !== 1) throw new Error('Search index representation unavailable');
|
|
819
|
+
return validateSearchIndex(JSON.parse(data[0].textContent), expected);
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
if (indexLoader) all('[data-documentation-search]').forEach(function (form) {
|
|
823
|
+
var input = form.querySelector('[data-search-query]');
|
|
824
|
+
var suggestions = form.querySelector('[data-search-suggestions]');
|
|
825
|
+
var preview = form.querySelector('[data-search-preview]');
|
|
826
|
+
var searchStatus = form.querySelector('[data-search-status]');
|
|
827
|
+
if (!input || !suggestions || !preview || !searchStatus || form.method.toLowerCase() !== 'get') return;
|
|
828
|
+
var generation = 0;
|
|
829
|
+
var suppressFocus = false;
|
|
830
|
+
function clear() {
|
|
831
|
+
generation += 1;
|
|
832
|
+
preview.replaceChildren();
|
|
833
|
+
searchStatus.textContent = '';
|
|
834
|
+
suggestions.hidden = true;
|
|
835
|
+
}
|
|
836
|
+
searches.push({ clear: clear });
|
|
837
|
+
function render(snapshot, query) {
|
|
838
|
+
var results = snapshot.match(query);
|
|
839
|
+
var list = document.createElement('ol');
|
|
840
|
+
results.results.forEach(function (result) {
|
|
841
|
+
list.appendChild(renderSearchResult(document, snapshot.index, result, window.location.origin));
|
|
842
|
+
});
|
|
843
|
+
preview.replaceChildren(list);
|
|
844
|
+
searchStatus.textContent = !results.results.length ? 'No matches. Try another resource, action or parameter name.'
|
|
845
|
+
: results.more ? 'Showing ' + results.results.length + ' results. More matches are available; refine your search.'
|
|
846
|
+
: results.results.length + (results.results.length === 1 ? ' result.' : ' results.');
|
|
847
|
+
suggestions.hidden = false;
|
|
848
|
+
}
|
|
849
|
+
function intent() {
|
|
850
|
+
clearSearches();
|
|
851
|
+
var current = generation;
|
|
852
|
+
var query = input.value.trim();
|
|
853
|
+
var config = searchConfiguration(form.action, form.getAttribute('data-search-index-url'),
|
|
854
|
+
form.getAttribute('data-search-view-key'), form.getAttribute('data-search-version'), window.location.href, bootstrapSearch);
|
|
855
|
+
if (!config) {
|
|
856
|
+
if (query) { suggestions.hidden = false; searchStatus.textContent = 'Search could not be loaded. Submit to open the results page.'; }
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
var loaded = indexLoader.peek(config);
|
|
860
|
+
if (loaded) { if (query && Array.from(query).length <= 256) render(loaded, query); return; }
|
|
861
|
+
if (query) { suggestions.hidden = false; searchStatus.textContent = 'Loading search…'; }
|
|
862
|
+
indexLoader.load(config).then(function (snapshot) {
|
|
863
|
+
if (current !== generation || input.value.trim() !== query || !query || Array.from(query).length > 256) return;
|
|
864
|
+
render(snapshot, query);
|
|
865
|
+
}).catch(function () {
|
|
866
|
+
if (current === generation && query) {
|
|
867
|
+
searchStatus.textContent = 'Search could not be loaded. Submit to open the results page.';
|
|
868
|
+
suggestions.hidden = false;
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
input.addEventListener('input', function () { if (input.value.trim()) intent(); else clearSearches(); });
|
|
873
|
+
input.addEventListener('pointerdown', intent);
|
|
874
|
+
input.addEventListener('focus', function () { if (!suppressFocus && !shortcutFocus) intent(); });
|
|
875
|
+
form.addEventListener('submit', clearSearches);
|
|
876
|
+
form.addEventListener('keydown', function (event) {
|
|
877
|
+
if (event.key !== 'Escape') return;
|
|
878
|
+
event.preventDefault();
|
|
879
|
+
var focusWasInside = suggestions.contains(document.activeElement);
|
|
880
|
+
clear();
|
|
881
|
+
if (focusWasInside) {
|
|
882
|
+
suppressFocus = true;
|
|
883
|
+
input.focus({ preventScroll: true });
|
|
884
|
+
suppressFocus = false;
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
window.addEventListener('pageshow', function (event) {
|
|
889
|
+
clearSearches();
|
|
890
|
+
if (event.persisted) { bootstrapSearch = true; if (indexLoader) indexLoader.reset(); }
|
|
891
|
+
});
|
|
892
|
+
window.addEventListener('pagehide', function () { clearSearches(); clearCopyStatus(); });
|
|
893
|
+
window.addEventListener('focus', clearSearches);
|
|
894
|
+
|
|
895
|
+
function updateActiveAction() {
|
|
896
|
+
referenceNav.update();
|
|
897
|
+
}
|
|
898
|
+
if (window.IntersectionObserver) {
|
|
899
|
+
var actionObserver = new IntersectionObserver(updateActiveAction);
|
|
900
|
+
referenceNav.targets.forEach(function (section) { actionObserver.observe(section); });
|
|
901
|
+
}
|
|
902
|
+
var scrollPending = false;
|
|
903
|
+
window.addEventListener('scroll', function () {
|
|
904
|
+
if (scrollPending) return;
|
|
905
|
+
scrollPending = true;
|
|
906
|
+
window.requestAnimationFrame(function () { scrollPending = false; updateActiveAction(); if (!codeObserver) highlightVisible(); });
|
|
907
|
+
}, { passive: true });
|
|
908
|
+
window.addEventListener('resize', function () { updateActiveAction(); highlightVisible(); });
|
|
909
|
+
|
|
910
|
+
var toggle = root.querySelector('[data-nav-toggle]');
|
|
911
|
+
var main = root.querySelector('.docs-main');
|
|
912
|
+
var navigationMedia = window.matchMedia('(max-width: 900px)');
|
|
913
|
+
var previousOverflow = null;
|
|
914
|
+
function closeNavigation() {
|
|
915
|
+
closeMenus();
|
|
916
|
+
root.removeAttribute('data-nav-open');
|
|
917
|
+
if (toggle) toggle.setAttribute('aria-expanded', 'false');
|
|
918
|
+
if (main) main.inert = false;
|
|
919
|
+
if (previousOverflow !== null) { document.body.style.overflow = previousOverflow; previousOverflow = null; }
|
|
920
|
+
}
|
|
921
|
+
function openNavigation() {
|
|
922
|
+
if (!toggle || !navigationMedia.matches || root.hasAttribute('data-nav-open')) return;
|
|
923
|
+
root.setAttribute('data-nav-open', '');
|
|
924
|
+
toggle.setAttribute('aria-expanded', 'true');
|
|
925
|
+
if (navigationMedia.matches) {
|
|
926
|
+
if (main) main.inert = true;
|
|
927
|
+
previousOverflow = document.body.style.overflow;
|
|
928
|
+
document.body.style.overflow = 'hidden';
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (toggle) toggle.addEventListener('click', function () {
|
|
932
|
+
if (root.hasAttribute('data-nav-open')) closeNavigation();
|
|
933
|
+
else openNavigation();
|
|
934
|
+
});
|
|
935
|
+
searchShortcut(document, function () {
|
|
936
|
+
return all('[data-documentation-search] [data-search-query]').find(function (input) {
|
|
937
|
+
return !input.disabled && !input.readOnly && !input.closest('[hidden], [inert]');
|
|
938
|
+
});
|
|
939
|
+
}, openNavigation, function (input) {
|
|
940
|
+
shortcutFocus = true;
|
|
941
|
+
try { input.focus({ preventScroll: true }); }
|
|
942
|
+
finally { shortcutFocus = false; }
|
|
943
|
+
});
|
|
944
|
+
root.addEventListener('keydown', function (event) {
|
|
945
|
+
if (!root.hasAttribute('data-nav-open')) return;
|
|
946
|
+
if (event.key === 'Escape') { closeNavigation(); toggle.focus({ preventScroll: true }); }
|
|
947
|
+
if (event.key === 'Tab' && sidebar && navigationMedia.matches) {
|
|
948
|
+
var focusable = [toggle].concat(Array.from(sidebar.querySelectorAll('a[href],button,input,select,textarea,summary,[tabindex]')))
|
|
949
|
+
.filter(function (element) { return !element.disabled && element.tabIndex >= 0 && element.getClientRects().length; });
|
|
950
|
+
var first = focusable[0];
|
|
951
|
+
var last = focusable[focusable.length - 1];
|
|
952
|
+
if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
|
|
953
|
+
else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
if (navigationMedia.addEventListener) navigationMedia.addEventListener('change', function () { if (!navigationMedia.matches) closeNavigation(); });
|
|
957
|
+
if (sidebar) sidebar.addEventListener('click', function (event) {
|
|
958
|
+
var link = event.target.closest('a[href]');
|
|
959
|
+
if (link && navigationMedia.matches) {
|
|
960
|
+
closeNavigation();
|
|
961
|
+
if (link.hash && new URL(link.href).pathname === window.location.pathname && new URL(link.href).search === window.location.search) {
|
|
962
|
+
var target = document.getElementById(decodeURIComponent(link.hash.slice(1)));
|
|
963
|
+
if (target) { target.setAttribute('tabindex', '-1'); target.focus({ preventScroll: true }); }
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
window.addEventListener('hashchange', function () { redirectLegacy(); updateActiveAction(); });
|
|
968
|
+
window.addEventListener('pageshow', updateActiveAction);
|
|
969
|
+
window.addEventListener('storage', function (event) {
|
|
970
|
+
if (event.key !== storageKey) return;
|
|
971
|
+
try { state = preferences(JSON.parse(event.newValue)); } catch (error) { state = preferences(null); }
|
|
972
|
+
applyTheme();
|
|
973
|
+
updatePreservingPosition(null);
|
|
974
|
+
});
|
|
975
|
+
root.setAttribute('data-docs-enhanced', '');
|
|
976
|
+
updateExamples();
|
|
977
|
+
redirectLegacy();
|
|
978
|
+
updateActiveAction();
|
|
979
|
+
// Fragment targets may start inside a panel whose visibility just changed.
|
|
980
|
+
if (window.location.hash) {
|
|
981
|
+
var target;
|
|
982
|
+
try { target = document.getElementById(decodeURIComponent(window.location.hash.slice(1))); }
|
|
983
|
+
catch (error) { target = null; }
|
|
984
|
+
if (target && target.closest('[hidden]')) {
|
|
985
|
+
var example = target.closest('[data-example]');
|
|
986
|
+
if (example) example.scrollIntoView();
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ready);
|
|
991
|
+
else ready();
|
|
992
|
+
})();
|