mapshaper 0.7.1 → 0.7.3
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.
- package/LICENSE +1 -1
- package/mapshaper.js +1 -1
- package/package.json +5 -2
- package/www/.nojekyll +0 -0
- package/www/index.html +22 -6
- package/www/mapshaper-gui.js +292 -4
- package/www/mapshaper.js +1 -1
- package/www/page.css +188 -10
- package/www/docs/_assets/cmd-search.js +0 -213
- package/www/docs/_assets/docs.css +0 -712
- package/www/docs/_assets/docs.js +0 -75
- package/www/docs/_assets/highlight.css +0 -10
- package/www/docs/essentials/command-line.html +0 -127
- package/www/docs/essentials/command-line.html.md +0 -112
- package/www/docs/essentials/web-app.html +0 -138
- package/www/docs/essentials/web-app.html.md +0 -106
- package/www/docs/examples/basics.html +0 -276
- package/www/docs/examples/basics.html.md +0 -371
- package/www/docs/examples/data/Makefile +0 -31
- package/www/docs/examples/data/globe.msx +0 -0
- package/www/docs/examples/data/globe.svg +0 -616
- package/www/docs/examples/data/globe.txt +0 -21
- package/www/docs/examples/data/globe.zip +0 -0
- package/www/docs/examples/data/ne_50m_admin_0_countries.geojson +0 -1
- package/www/docs/examples/data/ne_50m_admin_1_states_provinces_lakes.geojson +0 -1
- package/www/docs/examples/data/us-states.msx +0 -0
- package/www/docs/examples/data/us-states.svg +0 -56
- package/www/docs/examples/data/us-states.txt +0 -6
- package/www/docs/examples/data/us-states.zip +0 -0
- package/www/docs/examples/globe.html +0 -108
- package/www/docs/examples/globe.html.md +0 -64
- package/www/docs/examples/us-states.html +0 -88
- package/www/docs/examples/us-states.html.md +0 -44
- package/www/docs/formats/csv.html +0 -127
- package/www/docs/formats/csv.html.md +0 -97
- package/www/docs/formats/dbf.html +0 -87
- package/www/docs/formats/dbf.html.md +0 -39
- package/www/docs/formats/flatgeobuf.html +0 -86
- package/www/docs/formats/flatgeobuf.html.md +0 -42
- package/www/docs/formats/geojson.html +0 -107
- package/www/docs/formats/geojson.html.md +0 -65
- package/www/docs/formats/geopackage.html +0 -87
- package/www/docs/formats/geopackage.html.md +0 -42
- package/www/docs/formats/json.html +0 -83
- package/www/docs/formats/json.html.md +0 -35
- package/www/docs/formats/kml.html +0 -82
- package/www/docs/formats/kml.html.md +0 -39
- package/www/docs/formats/overview.html +0 -192
- package/www/docs/formats/overview.html.md +0 -35
- package/www/docs/formats/shapefile.html +0 -123
- package/www/docs/formats/shapefile.html.md +0 -84
- package/www/docs/formats/snapshot.html +0 -87
- package/www/docs/formats/snapshot.html.md +0 -39
- package/www/docs/formats/svg.html +0 -99
- package/www/docs/formats/svg.html.md +0 -51
- package/www/docs/formats/topojson.html +0 -102
- package/www/docs/formats/topojson.html.md +0 -54
- package/www/docs/gallery/index.html +0 -80
- package/www/docs/gallery/index.html.md +0 -29
- package/www/docs/guides/combining-layers.html +0 -105
- package/www/docs/guides/combining-layers.html.md +0 -81
- package/www/docs/guides/expressions.html +0 -600
- package/www/docs/guides/expressions.html.md +0 -376
- package/www/docs/guides/programmatic.html +0 -117
- package/www/docs/guides/programmatic.html.md +0 -91
- package/www/docs/guides/projections.html +0 -158
- package/www/docs/guides/projections.html.md +0 -118
- package/www/docs/guides/simplification.html +0 -110
- package/www/docs/guides/simplification.html.md +0 -94
- package/www/docs/guides/topology.html +0 -90
- package/www/docs/guides/topology.html.md +0 -63
- package/www/docs/images/simplification-detail.png +0 -0
- package/www/docs/images/simplification-dp.png +0 -0
- package/www/docs/images/simplification-mod2.png +0 -0
- package/www/docs/index.html +0 -101
- package/www/docs/index.html.md +0 -59
- package/www/docs/reference.html +0 -1302
- package/www/docs/reference.html.md +0 -1817
- package/www/docs/whats-new.html +0 -76
- package/www/docs/whats-new.html.md +0 -53
- package/www/llms-full.txt +0 -4040
- package/www/llms.txt +0 -55
package/www/page.css
CHANGED
|
@@ -170,7 +170,7 @@ body.map-view {
|
|
|
170
170
|
position: absolute;
|
|
171
171
|
top: 0px;
|
|
172
172
|
right: 0px;
|
|
173
|
-
margin: 0
|
|
173
|
+
margin: 0 3px 3px 0;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
.btn.header-btn {
|
|
@@ -207,6 +207,63 @@ body.map-view {
|
|
|
207
207
|
vertical-align: -1px;
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
/* --- Hamburger menu (post-edit overflow links) --- */
|
|
211
|
+
|
|
212
|
+
#header-menu-btn {
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
padding: 8px 7px 4px 7px;
|
|
215
|
+
user-select: none;
|
|
216
|
+
/* All header buttons are 29px tall, so top-align the hamburger to keep its
|
|
217
|
+
row baseline-neutral. Without this, the SVG-only button (no text) makes
|
|
218
|
+
its inline-block baseline = bottom edge, which drags the surrounding
|
|
219
|
+
text-containing buttons down to match. */
|
|
220
|
+
vertical-align: top;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#header-menu-btn svg {
|
|
224
|
+
display: block;
|
|
225
|
+
fill: currentColor;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#header-menu-dropdown {
|
|
229
|
+
position: absolute;
|
|
230
|
+
top: 30px;
|
|
231
|
+
right: 0;
|
|
232
|
+
min-width: 180px;
|
|
233
|
+
background-color: #fff;
|
|
234
|
+
color: #333;
|
|
235
|
+
border: 1px solid #ccc;
|
|
236
|
+
border-radius: 3px;
|
|
237
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
238
|
+
padding: 4px 0;
|
|
239
|
+
z-index: 50;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.header-menu-item {
|
|
243
|
+
display: block;
|
|
244
|
+
padding: 4px 14px;
|
|
245
|
+
color: #333;
|
|
246
|
+
font-size: 14px;
|
|
247
|
+
line-height: 1.3;
|
|
248
|
+
text-decoration: none;
|
|
249
|
+
white-space: nowrap;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.header-menu-item:hover,
|
|
253
|
+
.header-menu-item:focus {
|
|
254
|
+
background-color: #e6f7ff;
|
|
255
|
+
color: #1A6A96;
|
|
256
|
+
outline: none;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.header-menu-item.header-menu-sponsor svg {
|
|
260
|
+
width: 12px;
|
|
261
|
+
height: 12px;
|
|
262
|
+
fill: #d63384;
|
|
263
|
+
vertical-align: -1px;
|
|
264
|
+
margin-right: 6px;
|
|
265
|
+
}
|
|
266
|
+
|
|
210
267
|
.separator {
|
|
211
268
|
border-left: 1px solid white;
|
|
212
269
|
height: 10px;
|
|
@@ -427,6 +484,14 @@ div.alert-box {
|
|
|
427
484
|
margin: 0 0 5px 0;
|
|
428
485
|
}
|
|
429
486
|
|
|
487
|
+
input[type=text]:focus,
|
|
488
|
+
input[type=number]:focus,
|
|
489
|
+
textarea:focus {
|
|
490
|
+
outline: none;
|
|
491
|
+
border-color: #d3aa00;
|
|
492
|
+
box-shadow: 0 0 0 2px rgba(212, 168, 0, 0.4);
|
|
493
|
+
}
|
|
494
|
+
|
|
430
495
|
::placeholder {
|
|
431
496
|
color: #aaa;
|
|
432
497
|
opacity: 1;
|
|
@@ -495,13 +560,6 @@ div.alert-box {
|
|
|
495
560
|
padding-left: 12px;
|
|
496
561
|
}
|
|
497
562
|
|
|
498
|
-
.info-box,.popup {
|
|
499
|
-
border-radius: 9px;
|
|
500
|
-
border: 1px solid #d5d5d5;
|
|
501
|
-
box-shadow: 0 1px 7px rgba(0,0,0,0.18);
|
|
502
|
-
background-color: #fff;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
563
|
.contextmenu {
|
|
506
564
|
position: absolute;
|
|
507
565
|
border-radius: 6px;
|
|
@@ -518,6 +576,12 @@ div.alert-box {
|
|
|
518
576
|
padding: 6px;
|
|
519
577
|
}
|
|
520
578
|
|
|
579
|
+
.info-box,.popup {
|
|
580
|
+
border-radius: 11px;
|
|
581
|
+
border: 1px solid #d5d5d5;
|
|
582
|
+
box-shadow: 0 1px 7px rgba(0,0,0,0.18);
|
|
583
|
+
background-color: #fff;
|
|
584
|
+
}
|
|
521
585
|
|
|
522
586
|
.info-box {
|
|
523
587
|
min-width: 230px;
|
|
@@ -525,7 +589,8 @@ div.alert-box {
|
|
|
525
589
|
text-align: left;
|
|
526
590
|
margin-top: 12px;
|
|
527
591
|
margin-right: 20px;
|
|
528
|
-
padding: 12px 16px 12px 18px;
|
|
592
|
+
/* padding: 12px 16px 12px 18px; */
|
|
593
|
+
padding: 15px 17px 13px 19px;
|
|
529
594
|
vertical-align: top;
|
|
530
595
|
display: inline-block;
|
|
531
596
|
/* border: 1px solid #aaa; */
|
|
@@ -575,7 +640,7 @@ div.alert-box {
|
|
|
575
640
|
.info-box p {
|
|
576
641
|
white-space: pre-line;
|
|
577
642
|
margin: 0 0 6px 0;
|
|
578
|
-
line-height: 1.
|
|
643
|
+
line-height: 1.25;
|
|
579
644
|
/* font-size: 90%; */
|
|
580
645
|
}
|
|
581
646
|
|
|
@@ -1552,6 +1617,119 @@ body.pan.panning .map-layers:not(.drawing) {
|
|
|
1552
1617
|
cursor: default;
|
|
1553
1618
|
}
|
|
1554
1619
|
|
|
1620
|
+
/* --- FLOATING TOOLBAR --- */
|
|
1621
|
+
|
|
1622
|
+
/*
|
|
1623
|
+
* Anchored at bottom-center of the map area. Multiple toolbars stack
|
|
1624
|
+
* vertically (newest on top). Designed to host buttons and other small
|
|
1625
|
+
* controls; the inner content slot leaves room for a future drag handle.
|
|
1626
|
+
*/
|
|
1627
|
+
|
|
1628
|
+
.floating-toolbar-stack {
|
|
1629
|
+
position: absolute;
|
|
1630
|
+
z-index: 25;
|
|
1631
|
+
bottom: 24px;
|
|
1632
|
+
left: 50%;
|
|
1633
|
+
transform: translateX(-50%);
|
|
1634
|
+
display: flex;
|
|
1635
|
+
flex-direction: column-reverse;
|
|
1636
|
+
align-items: center;
|
|
1637
|
+
gap: 8px;
|
|
1638
|
+
pointer-events: none;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
.floating-toolbar {
|
|
1642
|
+
pointer-events: auto;
|
|
1643
|
+
display: flex;
|
|
1644
|
+
align-items: center;
|
|
1645
|
+
background-color: rgba(255, 255, 255, 0.96);
|
|
1646
|
+
border: 1px solid #ddd;
|
|
1647
|
+
border-radius: 8px;
|
|
1648
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
|
1649
|
+
padding: 4px;
|
|
1650
|
+
opacity: 0;
|
|
1651
|
+
transform: translateY(6px);
|
|
1652
|
+
transition: opacity 150ms ease, transform 150ms ease;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
.floating-toolbar.visible {
|
|
1656
|
+
opacity: 1;
|
|
1657
|
+
transform: translateY(0);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
.floating-toolbar-content {
|
|
1661
|
+
display: flex;
|
|
1662
|
+
align-items: center;
|
|
1663
|
+
gap: 0;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
.floating-toolbar-separator {
|
|
1667
|
+
width: 1px;
|
|
1668
|
+
height: 18px;
|
|
1669
|
+
margin: 0 4px;
|
|
1670
|
+
background-color: #ddd;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
.floating-toolbar-btn {
|
|
1674
|
+
position: relative;
|
|
1675
|
+
display: flex;
|
|
1676
|
+
align-items: center;
|
|
1677
|
+
justify-content: center;
|
|
1678
|
+
width: 28px;
|
|
1679
|
+
height: 28px;
|
|
1680
|
+
border-radius: 5px;
|
|
1681
|
+
cursor: pointer;
|
|
1682
|
+
user-select: none;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
.floating-toolbar-btn svg {
|
|
1686
|
+
width: 24px;
|
|
1687
|
+
height: 24px;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
.floating-toolbar-btn svg * {
|
|
1691
|
+
fill: #1385B7;
|
|
1692
|
+
transition: fill 100ms ease;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
.floating-toolbar-btn:hover:not(.disabled) {
|
|
1696
|
+
background-color: #e6f7ff;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
.floating-toolbar-btn:hover:not(.disabled) svg * {
|
|
1700
|
+
fill: #166689;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
.floating-toolbar-btn:active:not(.disabled) {
|
|
1704
|
+
background-color: #cdebf9;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
.floating-toolbar-btn.disabled {
|
|
1708
|
+
cursor: default;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
.floating-toolbar-btn.disabled svg * {
|
|
1712
|
+
fill: #b8c4ca;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
/* Tooltip shown above the button on hover */
|
|
1716
|
+
.floating-toolbar-btn[data-tooltip]:hover:not(.disabled)::after {
|
|
1717
|
+
content: attr(data-tooltip);
|
|
1718
|
+
position: absolute;
|
|
1719
|
+
bottom: calc(100% + 8px);
|
|
1720
|
+
left: 50%;
|
|
1721
|
+
transform: translateX(-50%);
|
|
1722
|
+
background-color: rgba(40, 40, 40, 0.92);
|
|
1723
|
+
color: #fff;
|
|
1724
|
+
font-size: 11px;
|
|
1725
|
+
line-height: 1;
|
|
1726
|
+
white-space: nowrap;
|
|
1727
|
+
padding: 5px 7px;
|
|
1728
|
+
border-radius: 4px;
|
|
1729
|
+
pointer-events: none;
|
|
1730
|
+
z-index: 1;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1555
1733
|
.zoom-box {
|
|
1556
1734
|
position: absolute;
|
|
1557
1735
|
border: 1px solid #cc6acc;
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
// Jump-to-command dropdown for the command reference page.
|
|
2
|
-
//
|
|
3
|
-
// The build script wraps each command in <section class="cmd-section"
|
|
4
|
-
// data-id="-clip" data-name="-clip" data-options="..."> and tags the H2
|
|
5
|
-
// above each group as <h2 class="cmd-category">. We use that to populate a
|
|
6
|
-
// dropdown of matching commands beneath the search input. Selecting an item
|
|
7
|
-
// jumps to the corresponding section -- the page itself is never filtered.
|
|
8
|
-
|
|
9
|
-
(function () {
|
|
10
|
-
'use strict';
|
|
11
|
-
|
|
12
|
-
var input = document.querySelector('.cmd-search-input');
|
|
13
|
-
var panel = document.querySelector('.cmd-search-results');
|
|
14
|
-
if (!input || !panel) return;
|
|
15
|
-
|
|
16
|
-
var statusEl = panel.querySelector('.cmd-search-status');
|
|
17
|
-
var listEl = panel.querySelector('.cmd-search-list');
|
|
18
|
-
var sections = Array.prototype.slice.call(
|
|
19
|
-
document.querySelectorAll('.cmd-section')
|
|
20
|
-
);
|
|
21
|
-
if (!sections.length) return;
|
|
22
|
-
|
|
23
|
-
// Build a flat command index. We walk the article in document order so we
|
|
24
|
-
// can attach the most recent h2.cmd-category to each section.
|
|
25
|
-
var article = sections[0].closest('.docs-article') || document;
|
|
26
|
-
var commands = (function () {
|
|
27
|
-
var out = [];
|
|
28
|
-
var currentCat = '';
|
|
29
|
-
var nodes = article.querySelectorAll('h2.cmd-category, section.cmd-section');
|
|
30
|
-
for (var i = 0; i < nodes.length; i++) {
|
|
31
|
-
var n = nodes[i];
|
|
32
|
-
if (n.tagName === 'H2') {
|
|
33
|
-
currentCat = (n.textContent || '').trim();
|
|
34
|
-
} else {
|
|
35
|
-
var name = n.dataset.name || '';
|
|
36
|
-
var opts = n.dataset.options || '';
|
|
37
|
-
var id = n.dataset.id || '';
|
|
38
|
-
if (!id) continue;
|
|
39
|
-
out.push({
|
|
40
|
-
id: id,
|
|
41
|
-
name: name,
|
|
42
|
-
options: opts,
|
|
43
|
-
category: currentCat,
|
|
44
|
-
haystack: (name + ' ' + opts).toLowerCase(),
|
|
45
|
-
el: n
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return out;
|
|
50
|
-
})();
|
|
51
|
-
|
|
52
|
-
var MAX_RESULTS = 60;
|
|
53
|
-
var activeIdx = -1;
|
|
54
|
-
var results = [];
|
|
55
|
-
|
|
56
|
-
function render() {
|
|
57
|
-
var raw = input.value.trim();
|
|
58
|
-
var tokens = raw.toLowerCase().split(/\s+/).filter(Boolean);
|
|
59
|
-
|
|
60
|
-
if (!tokens.length) {
|
|
61
|
-
close();
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Score-free matching: every token must appear in the name + options
|
|
66
|
-
// string. We deliberately don't search the full body text -- the dropdown
|
|
67
|
-
// is for jumping to a known command, not for free-text search.
|
|
68
|
-
results = commands.filter(function (c) {
|
|
69
|
-
return tokens.every(function (t) { return c.haystack.indexOf(t) !== -1; });
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
var truncated = results.length > MAX_RESULTS;
|
|
73
|
-
var shown = truncated ? results.slice(0, MAX_RESULTS) : results;
|
|
74
|
-
|
|
75
|
-
listEl.innerHTML = shown.map(function (c, i) {
|
|
76
|
-
return '<li role="option" id="cmd-result-' + i + '" class="cmd-result">' +
|
|
77
|
-
'<a href="#' + escapeAttr(c.id) + '" class="cmd-result-link" tabindex="-1">' +
|
|
78
|
-
'<span class="cmd-result-name">' + escapeHtml(c.name) + '</span>' +
|
|
79
|
-
(c.category
|
|
80
|
-
? '<span class="cmd-result-cat">' + escapeHtml(c.category) + '</span>'
|
|
81
|
-
: '') +
|
|
82
|
-
'</a>' +
|
|
83
|
-
'</li>';
|
|
84
|
-
}).join('');
|
|
85
|
-
|
|
86
|
-
if (!results.length) {
|
|
87
|
-
statusEl.textContent = 'No commands match \u201c' + raw + '\u201d.';
|
|
88
|
-
} else if (truncated) {
|
|
89
|
-
statusEl.textContent = 'Showing first ' + MAX_RESULTS + ' of ' +
|
|
90
|
-
results.length + ' matches.';
|
|
91
|
-
} else if (results.length === 1) {
|
|
92
|
-
statusEl.textContent = '1 match (press Enter to jump).';
|
|
93
|
-
} else {
|
|
94
|
-
statusEl.textContent = results.length + ' matches.';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
open();
|
|
98
|
-
setActive(results.length ? 0 : -1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function open() {
|
|
102
|
-
panel.hidden = false;
|
|
103
|
-
input.setAttribute('aria-expanded', 'true');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function close() {
|
|
107
|
-
panel.hidden = true;
|
|
108
|
-
input.setAttribute('aria-expanded', 'false');
|
|
109
|
-
input.removeAttribute('aria-activedescendant');
|
|
110
|
-
activeIdx = -1;
|
|
111
|
-
results = [];
|
|
112
|
-
listEl.innerHTML = '';
|
|
113
|
-
statusEl.textContent = '';
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function setActive(idx) {
|
|
117
|
-
var items = listEl.querySelectorAll('.cmd-result');
|
|
118
|
-
if (activeIdx >= 0 && items[activeIdx]) {
|
|
119
|
-
items[activeIdx].classList.remove('is-active');
|
|
120
|
-
}
|
|
121
|
-
activeIdx = idx;
|
|
122
|
-
if (idx < 0 || !items[idx]) {
|
|
123
|
-
input.removeAttribute('aria-activedescendant');
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
items[idx].classList.add('is-active');
|
|
127
|
-
input.setAttribute('aria-activedescendant', 'cmd-result-' + idx);
|
|
128
|
-
// Keep the highlighted item visible inside the (scrollable) dropdown.
|
|
129
|
-
var el = items[idx];
|
|
130
|
-
var top = el.offsetTop;
|
|
131
|
-
var bottom = top + el.offsetHeight;
|
|
132
|
-
if (top < listEl.scrollTop) {
|
|
133
|
-
listEl.scrollTop = top;
|
|
134
|
-
} else if (bottom > listEl.scrollTop + listEl.clientHeight) {
|
|
135
|
-
listEl.scrollTop = bottom - listEl.clientHeight;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function jumpTo(idx) {
|
|
140
|
-
if (idx < 0 || idx >= results.length) return;
|
|
141
|
-
var c = results[idx];
|
|
142
|
-
// Use the anchor's href so the browser updates location.hash and triggers
|
|
143
|
-
// its native scroll-to-anchor behaviour (which respects scroll-margin-top).
|
|
144
|
-
var link = listEl.querySelectorAll('.cmd-result-link')[idx];
|
|
145
|
-
if (link) link.click();
|
|
146
|
-
close();
|
|
147
|
-
input.blur();
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function escapeHtml(s) {
|
|
151
|
-
return String(s).replace(/[&<>"']/g, function (c) {
|
|
152
|
-
return ({ '&': '&', '<': '<', '>': '>',
|
|
153
|
-
'"': '"', "'": ''' })[c];
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
function escapeAttr(s) { return escapeHtml(s); }
|
|
157
|
-
|
|
158
|
-
input.addEventListener('input', render);
|
|
159
|
-
input.addEventListener('focus', function () {
|
|
160
|
-
if (input.value.trim()) render();
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
input.addEventListener('keydown', function (e) {
|
|
164
|
-
if (e.key === 'ArrowDown') {
|
|
165
|
-
if (panel.hidden) { render(); return; }
|
|
166
|
-
e.preventDefault();
|
|
167
|
-
if (results.length) setActive((activeIdx + 1) % results.length);
|
|
168
|
-
} else if (e.key === 'ArrowUp') {
|
|
169
|
-
if (panel.hidden) return;
|
|
170
|
-
e.preventDefault();
|
|
171
|
-
if (results.length) {
|
|
172
|
-
setActive(activeIdx <= 0 ? results.length - 1 : activeIdx - 1);
|
|
173
|
-
}
|
|
174
|
-
} else if (e.key === 'Enter') {
|
|
175
|
-
if (panel.hidden || activeIdx < 0) return;
|
|
176
|
-
e.preventDefault();
|
|
177
|
-
jumpTo(activeIdx);
|
|
178
|
-
} else if (e.key === 'Escape') {
|
|
179
|
-
if (!panel.hidden) {
|
|
180
|
-
e.preventDefault();
|
|
181
|
-
close();
|
|
182
|
-
} else if (input.value) {
|
|
183
|
-
e.preventDefault();
|
|
184
|
-
input.value = '';
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
// Mouse interaction with the dropdown.
|
|
190
|
-
listEl.addEventListener('mousemove', function (e) {
|
|
191
|
-
var li = e.target.closest('.cmd-result');
|
|
192
|
-
if (!li) return;
|
|
193
|
-
var idx = Array.prototype.indexOf.call(listEl.children, li);
|
|
194
|
-
if (idx !== activeIdx) setActive(idx);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
listEl.addEventListener('click', function (e) {
|
|
198
|
-
var link = e.target.closest('.cmd-result-link');
|
|
199
|
-
if (!link) return;
|
|
200
|
-
// Let cmd/ctrl/middle-click open in new tab as normal; otherwise
|
|
201
|
-
// intercept so we can close the dropdown after navigation.
|
|
202
|
-
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button === 1) return;
|
|
203
|
-
// Default <a> click handles the scroll; just tidy up the UI.
|
|
204
|
-
setTimeout(close, 0);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Click outside the wrap closes the dropdown.
|
|
208
|
-
document.addEventListener('click', function (e) {
|
|
209
|
-
if (panel.hidden) return;
|
|
210
|
-
if (e.target.closest('.cmd-search-wrap')) return;
|
|
211
|
-
close();
|
|
212
|
-
});
|
|
213
|
-
})();
|