Find the Accessories That Fit Your Camera
<!-- GardePro Accessory Compatibility Finder -->
<style>
/* ============================================================
GardePro — Accessory Compatibility Finder
作用域限定在 .cp 内,避免与 Shopify 主题样式冲突
============================================================ */
.cp {
--cp-ink: #11161c;
--cp-body: #3d4653;
--cp-muted: #6b7683;
--cp-line: #e4e8ee;
--cp-line-soft: #eff2f6;
--cp-bg: #ffffff;
--cp-soft: #f6f8fa;
--cp-accent: #e77b1f; /* 品牌主题橙 */
--cp-accent-2: #cf6a0d;
--cp-accent-soft: #fdf1e2;
--cp-green: #0e7a52; /* Compatible 语义色 */
--cp-green-soft: #e7f4ee;
--cp-warn: #8a5a00;
--cp-warn-soft: #fdf4e3;
--cp-radius: 14px;
--cp-shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 12px 32px -12px rgba(16, 24, 40, .12);
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
color: var(--cp-body);
line-height: 1.55;
-webkit-font-smoothing: antialiased;
max-width: 1160px;
margin: 0 auto;
padding: 8px 20px 64px;
box-sizing: border-box;
}
.cp *,
.cp *::before,
.cp *::after { box-sizing: border-box; }
.cp h1, .cp h2, .cp h3, .cp p, .cp dl, .cp dd, .cp dt, .cp ul, .cp li { margin: 0; padding: 0; }
.cp ul { list-style: none; }
.cp button { font: inherit; color: inherit; cursor: pointer; border: 0; background: none; }
/* ---------- Hero ---------- */
.cp-hero { text-align: center; padding: 40px 0 8px; }
.cp-eyebrow {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 12px;
font-weight: 600;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--cp-accent);
background: var(--cp-accent-soft);
padding: 6px 12px;
border-radius: 999px;
}
.cp-hero h1 {
font-size: clamp(26px, 4vw, 40px);
line-height: 1.16;
letter-spacing: -.02em;
color: var(--cp-ink);
margin: 18px auto 12px;
max-width: 720px;
font-weight: 700;
}
.cp-lede {
font-size: 16px;
color: var(--cp-muted);
max-width: 640px;
margin: 0 auto;
text-align: center;
text-wrap: balance; /* 两行均匀换行,视觉上真正居中 */
}
/* ---------- Search ---------- */
.cp-search {
position: relative;
max-width: 620px;
margin: 28px auto 0;
display: flex;
align-items: center;
gap: 8px;
background: var(--cp-bg);
border: 1.5px solid var(--cp-line);
border-radius: 999px;
padding: 6px 6px 6px 18px;
box-shadow: var(--cp-shadow);
transition: border-color .18s, box-shadow .18s;
}
.cp-search:focus-within {
border-color: var(--cp-accent);
box-shadow: 0 0 0 4px rgba(15, 107, 79, .1), var(--cp-shadow);
}
.cp-search > svg { flex: 0 0 auto; color: var(--cp-muted); }
.cp-search input {
flex: 1 1 auto;
min-width: 0;
border: 0;
outline: 0;
background: none;
font-size: 15.5px;
padding: 12px 4px;
color: var(--cp-ink);
}
.cp-search input::placeholder { color: #9aa4b1; }
.cp-search-btn {
flex: 0 0 auto;
background: var(--cp-accent);
color: #fff;
font-weight: 600;
font-size: 14.5px;
padding: 12px 22px;
border-radius: 999px;
transition: background .18s;
}
.cp-search-btn:hover { background: var(--cp-accent-2); }
/* 联想下拉 */
.cp-sugg {
position: absolute;
top: calc(100% + 8px);
left: 0;
right: 0;
background: var(--cp-bg);
border: 1px solid var(--cp-line);
border-radius: 14px;
box-shadow: 0 18px 40px -12px rgba(16, 24, 40, .22);
padding: 6px;
max-height: 340px;
overflow: auto;
z-index: 40;
display: none;
text-align: left;
}
.cp-sugg.is-open { display: block; }
.cp-sugg li { border-radius: 9px; }
.cp-sugg button {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 10px 12px;
border-radius: 9px;
font-size: 15px;
color: var(--cp-ink);
text-align: left;
}
.cp-sugg button:hover,
.cp-sugg button.is-active { background: var(--cp-soft); }
.cp-sugg .cp-sugg-hint { font-size: 12px; color: var(--cp-muted); }
.cp-sugg mark { background: transparent; color: var(--cp-accent); font-weight: 600; }
.cp-quick {
margin: 18px auto 0;
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
align-items: center;
font-size: 13px;
color: var(--cp-muted);
}
.cp-quick button {
border: 1px solid var(--cp-line);
background: var(--cp-bg);
padding: 5px 12px;
border-radius: 999px;
font-size: 13px;
color: var(--cp-body);
transition: .15s;
}
.cp-quick button:hover { border-color: var(--cp-accent); color: var(--cp-accent); }
/* ---------- 结果区 ---------- */
.cp-result { margin-top: 40px; scroll-margin-top: 24px; }
.cp-result[hidden] { display: none; }
.cp-cam-head {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
padding: 18px 20px;
border: 1px solid var(--cp-line);
border-radius: var(--cp-radius);
background: linear-gradient(180deg, #fbfdfc, #f4f9f6);
}
.cp-cam-head .cp-cam-label {
font-size: 12px;
font-weight: 600;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--cp-muted);
width: 100%;
}
.cp-cam-name {
font-size: 24px;
font-weight: 700;
color: var(--cp-ink);
letter-spacing: -.01em;
line-height: 1.2;
}
.cp-cam-sub { font-size: 13.5px; color: var(--cp-muted); }
.cp-cam-head .cp-spacer { flex: 1 1 auto; }
.cp-ghost-btn {
border: 1px solid transparent;
background: var(--cp-accent);
border-radius: 999px;
padding: 8px 16px;
font-size: 13.5px;
font-weight: 600;
color: #fff;
transition: .15s;
}
.cp-ghost-btn:hover { background: var(--cp-accent-2); }
.cp-tag {
display: inline-flex;
align-items: center;
font-size: 11.5px;
font-weight: 600;
letter-spacing: .02em;
padding: 3px 9px;
border-radius: 999px;
background: var(--cp-line-soft);
color: var(--cp-muted);
white-space: nowrap;
}
.cp-tag.is-hw { background: var(--cp-warn-soft); color: var(--cp-warn); }
/* 分类块 */
.cp-cat { margin-top: 34px; }
.cp-cat-title {
display: flex;
align-items: baseline;
gap: 10px;
padding-bottom: 12px;
border-bottom: 1px solid var(--cp-line);
margin-bottom: 18px;
}
.cp-cat-title h2 {
font-size: 18px;
font-weight: 650;
color: var(--cp-ink);
letter-spacing: -.01em;
display: flex;
align-items: center;
gap: 9px;
}
.cp-cat-title h2 svg { color: var(--cp-accent); }
.cp-cat-title .cp-count { font-size: 13px; color: var(--cp-muted); }
.cp-cards {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
}
/* 配件卡 */
.cp-card {
border: 1px solid var(--cp-line);
border-radius: var(--cp-radius);
background: var(--cp-bg);
padding: 18px 18px 14px;
display: flex;
flex-direction: column;
gap: 10px;
transition: border-color .18s, box-shadow .18s, transform .18s;
}
.cp-card:hover {
border-color: #cfd8e3;
box-shadow: 0 10px 26px -14px rgba(16, 24, 40, .28);
transform: translateY(-1px);
}
.cp-card-top { display: flex; align-items: center; gap: 10px; justify-content: space-between; }
.cp-model {
font-size: 19px;
font-weight: 700;
color: var(--cp-ink);
letter-spacing: -.01em;
}
.cp-badge {
font-size: 11.5px;
font-weight: 600;
color: var(--cp-green);
background: var(--cp-green-soft);
padding: 3px 9px;
border-radius: 999px;
white-space: nowrap;
}
.cp-badge.is-rec {
color: var(--cp-accent-2);
background: var(--cp-accent-soft);
}
.cp-conds { display: flex; flex-wrap: wrap; gap: 6px; }
.cp-note {
font-size: 13px;
color: var(--cp-muted);
background: var(--cp-soft);
border-left: 2px solid var(--cp-line);
padding: 7px 10px;
border-radius: 0 8px 8px 0;
}
.cp-card-foot { margin-top: auto; padding-top: 4px; }
.cp-more {
font-size: 13.5px;
font-weight: 600;
color: var(--cp-accent);
display: inline-flex;
align-items: center;
gap: 5px;
padding: 4px 0;
}
.cp-more svg { transition: transform .2s; }
.cp-more[aria-expanded="true"] svg { transform: rotate(180deg); }
.cp-specs {
display: none;
border-top: 1px dashed var(--cp-line);
padding-top: 10px;
margin-top: 4px;
}
.cp-specs.is-open { display: block; }
.cp-specs div {
display: grid;
grid-template-columns: 42% 1fr;
gap: 10px;
padding: 8px 11px;
margin-bottom: 6px;
font-size: 12.5px;
line-height: 1.45;
background: #f8fafc;
border: 1px solid var(--cp-line-soft);
border-radius: 9px;
}
.cp-specs dt { color: var(--cp-muted); }
.cp-specs dd { color: var(--cp-body); font-weight: 500; }
.cp-empty {
font-size: 14px;
color: var(--cp-muted);
padding: 18px 20px;
border: 1px dashed var(--cp-line);
border-radius: var(--cp-radius);
background: var(--cp-soft);
}
/* ---------- 浏览全部配件 ---------- */
.cp-browse { margin-top: 64px; }
.cp-sec-head {
display: flex;
flex-wrap: wrap;
gap: 16px;
align-items: flex-end;
justify-content: space-between;
border-bottom: 1px solid var(--cp-line);
padding-bottom: 14px;
margin-bottom: 20px;
}
.cp-sec-head h2 {
font-size: 22px;
font-weight: 700;
color: var(--cp-ink);
letter-spacing: -.015em;
}
.cp-sec-head p { font-size: 14px; color: var(--cp-muted); margin-top: 4px; }
.cp-tabs { display: flex; flex-wrap: wrap; gap: 6px; }
.cp-tabs button {
padding: 8px 16px;
border-radius: 999px;
font-size: 13.5px;
font-weight: 550;
color: var(--cp-body);
border: 1px solid var(--cp-line);
background: #fff;
transition: .15s;
}
.cp-tabs button:hover { border-color: #c3ccd8; }
.cp-tabs button.is-active {
background: var(--cp-accent);
border-color: var(--cp-accent);
color: #fff;
box-shadow: 0 4px 12px -4px rgba(231, 123, 31, .5);
}
.cp-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
gap: 12px;
}
.cp-tile {
text-align: left;
border: 1px solid var(--cp-line);
border-radius: 12px;
padding: 16px;
background: #fff;
transition: .16s;
display: flex;
flex-direction: column;
gap: 4px;
}
.cp-tile:hover {
border-color: var(--cp-accent);
box-shadow: 0 10px 24px -14px rgba(231, 123, 31, .55);
transform: translateY(-1px);
}
.cp-tile-view {
margin-top: 7px;
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 12.5px;
font-weight: 600;
color: var(--cp-accent);
}
.cp-tile-view svg { transition: transform .18s; }
.cp-tile:hover .cp-tile-view svg { transform: translateX(3px); }
.cp-tile .cp-tile-model { font-size: 16px; font-weight: 700; color: var(--cp-ink); }
.cp-tile .cp-tile-meta { font-size: 12.5px; color: var(--cp-muted); }
/* ---------- 抽屉 / 弹层 ---------- */
.cp-modal {
position: fixed;
inset: 0;
z-index: 9999;
display: none;
align-items: center;
justify-content: center;
padding: 20px;
background: rgba(12, 18, 26, .5);
backdrop-filter: blur(2px);
}
.cp-modal.is-open { display: flex; }
.cp-modal-box {
background: #fff;
border-radius: 18px;
width: 100%;
max-width: 720px;
max-height: 86vh;
overflow: auto;
padding: 26px 26px 30px;
position: relative;
box-shadow: 0 30px 70px -20px rgba(0, 0, 0, .45);
animation: cp-pop .2s ease-out;
}
@keyframes cp-pop { from { opacity: 0; transform: translateY(8px) scale(.99); } }
.cp-modal-close {
position: absolute;
top: 14px;
right: 14px;
width: 34px;
height: 34px;
border-radius: 50%;
display: grid;
place-items: center;
color: var(--cp-muted);
background: var(--cp-soft);
}
.cp-modal-close:hover { color: var(--cp-ink); }
.cp-modal-box h3 {
font-size: 22px;
font-weight: 700;
color: var(--cp-ink);
letter-spacing: -.01em;
margin-bottom: 2px;
}
.cp-modal-cat { font-size: 12px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--cp-accent); }
.cp-modal-note {
margin-top: 12px;
font-size: 13.5px;
color: var(--cp-body);
background: var(--cp-warn-soft);
color: var(--cp-warn);
border-radius: 10px;
padding: 10px 12px;
}
.cp-modal-block { margin-top: 22px; }
.cp-modal-block h4 {
font-size: 13px;
font-weight: 650;
letter-spacing: .06em;
text-transform: uppercase;
color: var(--cp-muted);
margin: 0 0 10px;
}
.cp-typegroup { margin-top: 14px; }
.cp-typegroup-head {
display: flex;
align-items: center;
gap: 8px;
font-size: 12.5px;
font-weight: 600;
color: var(--cp-body);
margin-bottom: 8px;
}
.cp-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.cp-pill {
font-size: 12.5px;
padding: 4px 10px;
border-radius: 7px;
background: var(--cp-soft);
border: 1px solid var(--cp-line-soft);
color: var(--cp-body);
}
.cp-pill:hover { border-color: var(--cp-accent); color: var(--cp-accent); }
/* 弹窗内:机型网格对齐(横竖都对齐,浅色格子) */
.cp-typegroup .cp-pills {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(106px, 1fr));
gap: 8px;
}
.cp-typegroup .cp-pill {
text-align: center;
padding: 7px 6px;
border-radius: 9px;
background: #f8fafc;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.cp-typegroup .cp-pill:hover {
background: var(--cp-accent-soft);
border-color: var(--cp-accent);
}
.cp-spec-table {
width: 100%;
border-collapse: separate;
border-spacing: 0 7px;
font-size: 13px;
margin-top: -2px;
}
.cp-spec-table td {
padding: 8px 14px;
background: #f8fafc;
border-top: 1px solid var(--cp-line-soft);
border-bottom: 1px solid var(--cp-line-soft);
vertical-align: top;
}
.cp-spec-table td:first-child {
color: var(--cp-muted);
width: 42%;
padding-right: 14px;
border-left: 1px solid var(--cp-line-soft);
border-radius: 10px 0 0 10px;
}
.cp-spec-table td:last-child {
color: var(--cp-ink);
font-weight: 500;
border-right: 1px solid var(--cp-line-soft);
border-radius: 0 10px 10px 0;
}
/* ---------- HW "?" 提示 ---------- */
.cp-hw {
display: inline-grid;
place-items: center;
width: 15px;
height: 15px;
margin-left: 6px;
border: 1.2px solid currentColor;
border-radius: 50%;
font-size: 10px;
font-weight: 700;
line-height: 1;
padding: 0;
color: inherit;
background: transparent;
position: relative;
cursor: help;
vertical-align: -2px;
}
/* ---------- HW 提示弹层(body 级,避免被卡片/弹窗裁剪) ---------- */
.cp-hw-pop {
position: fixed;
z-index: 99999;
max-width: min(280px, calc(100vw - 24px));
background: #1a222c;
color: #fff;
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
font-size: 12.5px;
font-weight: 400;
line-height: 1.55;
letter-spacing: 0;
text-transform: none;
text-align: left;
padding: 10px 13px;
border-radius: 10px;
box-shadow: 0 14px 30px -10px rgba(0, 0, 0, .45);
pointer-events: none;
opacity: 0;
transform: translateY(3px);
transition: opacity .15s, transform .15s;
}
.cp-hw-pop.is-show { opacity: 1; transform: translateY(0); }
/* ---------- 特殊徽章 ---------- */
.cp-chip {
display: inline-flex;
align-items: center;
font-size: 11.5px;
font-weight: 600;
padding: 3px 10px;
border-radius: 999px;
background: #f4f6f9;
border: 1px solid var(--cp-line);
color: #4a5563;
white-space: nowrap;
}
/* ---------- 推荐卡 ---------- */
.cp-card.is-rec { border-color: #f5c9ae; background: linear-gradient(180deg, #fffdfb, #fff6f0); }
.cp-note.is-rec-tip {
border-left-color: var(--cp-accent);
background: #fdf0e7;
color: #8a4520;
}
/* ---------- 弹层头部 ---------- */
.cp-modal-head {
padding-bottom: 14px;
border-bottom: 1px solid var(--cp-line);
margin-bottom: 6px;
}
.cp-modal-head h3 { margin-bottom: 0; }
.cp-modal-box h4 { margin-bottom: 10px; }
/* ---------- 页脚 ---------- */
.cp-foot {
margin-top: 56px;
padding-top: 20px;
border-top: 1px solid var(--cp-line);
font-size: 12.5px;
color: var(--cp-muted);
display: flex;
flex-wrap: wrap;
gap: 8px 18px;
justify-content: space-between;
}
.cp-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@media (max-width: 640px) {
.cp { padding: 4px 16px 48px; }
.cp-hero { padding-top: 26px; }
.cp-search { padding-left: 14px; }
.cp-search-btn { padding: 11px 16px; }
.cp-cam-name { font-size: 20px; }
.cp-cards { grid-template-columns: 1fr; }
.cp-specs div { grid-template-columns: 1fr; gap: 2px; }
.cp-modal-box { padding: 22px 18px 26px; }
}
</style>
<div class="cp" id="cp-app">
<header class="cp-hero">
<div class="cp-eyebrow">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>
Compatibility Finder
</div>
<h1>Find the Accessories That Fit Your Camera</h1>
<div class="cp-search" role="search">
<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.6-3.6"/></svg>
<label class="cp-sr" for="cp-input">Camera model</label>
<input id="cp-input" type="text" placeholder="Search your camera model, e.g. X66 Pro" autocomplete="off"
role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="cp-sugg">
<button class="cp-search-btn" id="cp-search-btn" type="button">Search</button>
<ul class="cp-sugg" id="cp-sugg" role="listbox" aria-label="Model suggestions"></ul>
</div>
<div class="cp-quick" id="cp-quick">
<span>Popular:</span>
</div>
</header>
<section class="cp-result" id="cp-result" hidden aria-live="polite"></section>
<section class="cp-browse" id="cp-browse">
<div class="cp-sec-head">
<div>
<h2>Browse All Accessories</h2>
<p id="cp-browse-sub">Select an accessory to see every compatible camera model.</p>
</div>
<div class="cp-tabs" id="cp-tabs" role="tablist"></div>
</div>
<div class="cp-grid" id="cp-grid"></div>
</section>
<footer class="cp-foot">
<span id="cp-foot-stats"></span>
<span>Fitment data is continuously updated. Specifications and compatibility are subject to change without notice.</span>
</footer>
</div>
<div class="cp-modal" id="cp-modal" role="dialog" aria-modal="true" aria-labelledby="cp-modal-title">
<div class="cp-modal-box">
<button class="cp-modal-close" id="cp-modal-close" type="button" aria-label="Close">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
</button>
<div id="cp-modal-body"></div>
</div>
</div>
<script type="application/json" id="cp-data">{"brands":[{"brand":"GardePro","accessories":[{"category":"solar","categoryLabel":"Solar Panel","model":"SP300","note":"Non-cellular camera without lithium battery; neither is supported.","specs":"Solar Panel MAX Output Power: 3.5W\nOutput Voltage: 6.6V\nMAX Output Current: 0.53A\nOutput Plug Size: 5.5 x 2.1mm\nMounting: Tripod Base (1/4-20)\nWaterproof: IP65\nOperation Temperature: -4 ~ 140°F / -20 ~ 60°C\nOperation Humidity: 5% - 95%\nProduct Dimensions: 6.22 x 5.28 x 0.59 inch (15.80 × 13.41 × 1.50 cm)\nProduct Weight: 255g","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["E6P","E6PMB","E6PCF","E6 2.0 Pro","E6 3.0 Pro","E8P","E8PMB","E8PCF","E8PWF","E8 2.0 Pro","E8 3.0 Pro","E9P","E9PMB","E9PCF","E9PWF","E9 2.0 Pro","E9PWF 2.0","E9 3.0 Pro","A80 Pro"]},{"type":"Long Range WiFi","hw":"","cameras":["R3 Pro","R3 2.0 Pro"]},{"type":"Cellular","hw":"","cameras":["X50P","X50PMB","X50PLMB","X60P","X60PMB","X60PLMB","X66 Pro","X66 Pro Max","X66 3.0 Pro","X66 3.0 Ultra","X70 Pro","X70 3.0 Pro","X70 3.0 Ultra","X80 Pro"]}]},{"category":"solar","categoryLabel":"Solar Panel","model":"SP350","note":"All models","specs":"Battery Capacity: 5200 mAh/3.7V,19.24Wh\nSolar Panel MAX\nOutput Power: 12W(12V/1A)\nOutput Voltage: 6V, 9V or 12V\nOutput Current: 1.5A (6V), 1A (9V or 12V)\nOutput Plug Size: 5.5 x 2.1mm, 4.0 x 1.7mm\nPower Adaptor: Input: AC110-220, output: 5V/1A.\nNote: the adaptor is not included.\nMounting: Tripod Base (1/4-20)\nWaterproof: IP65\nOperation Temperature: -4 ~ 140°F / -20 ~ 60°C\nOperation Humidity: 5% - 95%\nProduct Dimensions: 6.89 x 5.79 x 1.09 inch(17.50 × 14.71 × 2.77 cm)\nProduct Weight: 350g","groups":[{"type":"Non-Cellular","hw":"","cameras":["A3","A3S","E5","E5S","E5 2.0","E5WF 2.0","A5","A5CF","A50","A60","T5NG","T5CF","T5WF","T5NG Pro","T5CF Pro","T5WF Pro"]},{"type":"WiFi","hw":"H/W>=V8.2","cameras":["E6","E6P","E6PMB","E6PCF","E6 2.0","E6 2.0 Pro","E6 3.0 Pro","E7","E8","E8P","E8PMB","E8PCF","E8PWF","E8 2.0","E8 2.0 Pro","E8 3.0 Pro","E9","E9P","E9PMB","E9PCF","E9PWF","E9 2.0","E9 2.0 Pro","E9PWF 2.0","E9 3.0 Pro","A80","A80 Pro"]},{"type":"WiFi","hw":"H/W<V8.2","cameras":["E6","E7","E8","E9","A80","T6LRW"]},{"type":"Long Range WiFi","hw":"","cameras":["R3 Pro","R3 2.0 Pro","T6LRW"]},{"type":"Cellular","hw":"H/W>=V8.2","cameras":["X20","X20WF","X20CF","X50","X50S","X50P","X50MB","X50PMB","X50PLMB","X60P","X60PMB","X60PLMB","X66 Pro","X66 Pro Max","X66 3.0 Pro","X66 3.0 Ultra","X70","X70 Pro","X70 3.0 Pro","X70 3.0 Ultra","X80","X80 Pro"]},{"type":"Cellular","hw":"H/W<V8.2","cameras":["X20","X20WF","X20CF","X50","X50S","X60"]}]},{"category":"solar","categoryLabel":"Solar Panel","model":"SP1000","note":"Non-cellular camera without lithium battery; neither is supported.","specs":"Solar Panel MAX Output Power: 10W\nOutput Voltage: 6.6V\nMAX Output Current : 1.52A\nOutput Plug Size: 5.5 x 2.1mm\nWaterproof: IP65\nOperation Temperature: -4 ~ 140°F / -20 ~ 60°C\nOperation Humidity: 5% - 95%\nProduct Dimensions: 10.78 x 9.53 x 4.92 inch (27.38 × 24.21 × 12.50 cm)\nProduct Weight: 962g","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["E6P","E6PMB","E6PCF","E6 2.0 Pro","E6 3.0 Pro","E8P","E8PMB","E8PCF","E8PWF","E8 2.0 Pro","E8 3.0 Pro","E9P","E9PMB","E9PCF","E9PWF","E9 2.0 Pro","E9PWF 2.0","E9 3.0 Pro","A80 Pro","R3 Pro","R3 2.0 Pro"]},{"type":"Long Range WiFi","hw":"","cameras":["R3 Pro","R3 2.0 Pro"]},{"type":"Cellular","hw":"","cameras":["X50P","X50PMB","X50PLMB","X60P","X60PMB","X60PLMB","X66 Pro","X66 Pro Max","X66 3.0 Pro","X66 3.0 Ultra","X70 Pro","X70 3.0 Pro","X70 3.0 Ultra","X80 Pro"]}]},{"category":"solar","categoryLabel":"Solar Panel","model":"SP1100","note":"All models","specs":"Battery Capacity\nTotal Capacity : 10400 mAh / 38.48 Wh\nCell : 2600 mAh x 4pcs\nNominal Voltage : 14.8 V\nSolar Panel MAX\nOutput Power: 27W (12V/2.25A)\nOutput Voltage: 6V, 9V or 12V\nOutput Current: 2.25A (6V), 2.25A (9V ), 2.25A (12V)\nNote: Select the appropriate output voltage according to camera requirements\nOutput Plug Size: 5.5 x 2.1mm, 4.0 x 1.7mm\nPower Adaptor\n(Sold Separately) Type-C INPUT (for Charging Solar Panel): 5-20V(Support PD3.0 fast charging protocol)\nNote: the adaptor is not included.\nMounting: Tripod Base (1/4-20)\nWaterproof: IP65\nOperation Temperature: -4 ~ 140°F / -20 ~ 60°C\nOperation Humidity: 5% - 95%\nProduct Dimensions: 10.78 x 9.53 x 1.50 inch\nProduct Weight: 786g","groups":[{"type":"Non-Cellular","hw":"","cameras":["A3","A3S","E5","E5S","E5 2.0","E5WF 2.0","A5","A5CF","A50","A60","T5NG","T5CF","T5WF","T5NG Pro","T5CF Pro","T5WF Pro"]},{"type":"WiFi","hw":"H/W>=V8.2","cameras":["E6","E6P","E6PMB","E6PCF","E6 2.0","E6 2.0 Pro","E6 3.0 Pro","E7","E8","E8P","E8PMB","E8PCF","E8PWF","E8 2.0","E8 2.0 Pro","E8 3.0 Pro","E9","E9P","E9PMB","E9PCF","E9PWF","E9 2.0","E9 2.0 Pro","E9 3.0 Pro","A80","A80 Pro"]},{"type":"WiFi","hw":"H/W<V8.2","cameras":["E6","E7","E8","E9","A80","T6LRW"]},{"type":"Long Range WiFi","hw":"","cameras":["R3 Pro","R3 2.0 Pro","T6LRW"]},{"type":"Cellular","hw":"H/W>=V8.2","cameras":["X20","X20WF","X20CF","X50","X50S","X50P","X50MB","X50PMB","X50PLMB","X60P","X60PMB","X60PLMB","X66 Pro","X66 Pro Max","X66 3.0 Pro","X66 3.0 Ultra","X70","X70 Pro","X70 3.0 Pro","X70 3.0 Ultra","X80","X80 Pro"]},{"type":"Cellular","hw":"H/W<V8.2","cameras":["X20","X20WF","X20CF","X50","X50S","X60"]}]},{"category":"box","categoryLabel":"Security Box","model":"MB100","note":"Compatible only with large-case models.","specs":"Dimensions: 6.77 × 4.57 × 2.48 inch (17.2 × 11.6 × 6.3 cm)\nLock Hole Diameter: 0.28 inch (0.7 cm)\nLock: Not Included\nMounting Options: Strap Mounting or Screw Installation\nMounting Strap: Not Included (Use the Strap Supplied with the Camera)\nMounting Screws: Not Included","groups":[{"type":"Non-Cellular","hw":"","cameras":["A3","A3S"]}]},{"category":"box","categoryLabel":"Security Box","model":"MB200","note":"","specs":"Dimensions: 6.93 × 4.49 × 2.52 inch (17.6 × 11.4 × 6.4 cm)\nLock Hole Diameter: 0.39 inch (1 cm)\nLock: Not Included\nMounting Options: Strap Mounting or Screw Installation\nMounting Strap: Not Included (Use the Strap Supplied with the Camera)\nMounting Screws: Not Included","groups":[{"type":"Non-Cellular","hw":"","cameras":["E5","E5S","A5","A5CF","A50","T5NG","T5CF","T5WF"]},{"type":"WiFi","hw":"","cameras":["E6","E6P","E6PMB","E6PCF","E7","E8","E8P","E8PMB","E8PCF","E8PWF","E9","E9P","E9PMB","E9PCF","E9PWF"]},{"type":"Cellular","hw":"","cameras":["X20","X20WF","X20CF","X50","X50S","X50P","X50MB","X50PMB","X50PLMB","X60P","X60PMB","X60PLMB"]}]},{"category":"box","categoryLabel":"Security Box","model":"MB300","note":"","specs":"Dimensions: 6.93 × 4.49 × 3.07 inch (17.6 × 11.4 × 7.8 cm)\nLock Hole Diameter: 0.39 inch (1 cm)\nLock: Not Included\nMounting Options: Strap Mounting or Screw Installation\nMounting Strap: Not Included (Use the Strap Supplied with the Camera)\nMounting Screws: Not Included","groups":[{"type":"Non-Cellular","hw":"","cameras":["E5 2.0","E5WF 2.0","A60","T5NG Pro","T5CF Pro","T5WF Pro","T6LRW"]},{"type":"WiFi","hw":"","cameras":["E6+","E6 2.0","E8+","E8 2.0","E9 2.0","A80"]},{"type":"Long Range WiFi","hw":"","cameras":["T6LRW"]},{"type":"Cellular","hw":"","cameras":["X70","X80","X80 Pro"]}]},{"category":"box","categoryLabel":"Security Box","model":"MB350","note":"","specs":"Dimensions: 6.93 × 4.49 × 3.26 inch (17.6 × 11.4 × 8.28 cm)\nLock Hole Diameter: 0.39 inch (1 cm)\nLock: Not Included\nMounting Options: Strap Mounting or Screw Installation\nMounting Strap: Not Included (Use the Strap Supplied with the Camera)\nMounting Screws: Not Included","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["E6 2.0 Pro","E6 3.0 Pro","E8 2.0 Pro","E8 3.0 Pro","E9 2.0 Pro","E9PWF 2.0","E9 3.0 Pro","A80 Pro","R3 Pro","R3 2.0 Pro"]},{"type":"Long Range WiFi","hw":"","cameras":["R3 Pro","R3 2.0 Pro"]},{"type":"Cellular","hw":"","cameras":["X66 Pro","X66 Pro Max","X66 3.0 Pro","X66 3.0 Ultra","X70","X70 Pro","X70 3.0 Pro","X70 3.0 Ultra"]}]},{"category":"battery","categoryLabel":"Lithium Battery","model":"L800D","note":"Non-cellular camera without lithium battery; neither is supported.","specs":"Rated Capacity: 8000 mAh,29.6Wh\nNominal Voltage: 3.7 V\nLimited Charge Voltage: 5.0 V\nStandard Charging Current (Through the Type-C port): 500 mA\nStandard Charging Time: 16 hours\nStandard Discharging Current: 1600 mA\nMax. Discharging Current: 2000 mA\nCut-off Voltage: 2.75 V\nOperating Charge: 0 ~ 45 ℃\nTemperature Range Discharge: -20 ~ 60 ℃\nOperating humidity Range ≤ 90% RH\nSize : 107.8 x 60 x 15.2 mm/4.24 × 2.36 × 0.6inches\nWeight: 168 g\nSpecific Energy:176 Wh/kg","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["E6P","E6PMB","E6PCF","E6 2.0 Pro","E8P","E8PMB","E8PCF","E8PWF","E8 2.0 Pro","E9P","E9PMB","E9PCF","E9PWF","E9 2.0 Pro","A80 Pro"]},{"type":"Long Range WiFi","hw":"","cameras":["R3 Pro","R3 2.0 Pro"]},{"type":"Cellular","hw":"","cameras":["X50P","X50MB","X50PMB","X50PLMB","X60P","X60PMB","X60PLMB","X66 Pro","X66 Pro Max","X66 3.0 Pro","X70","X70 Pro","X70 3.0 Pro","X80 Pro"]}]},{"category":"battery","categoryLabel":"Lithium Battery","model":"L780D","note":"Non-cellular camera without lithium battery; neither is supported.","specs":"Rated Capacity: 7800 mAh,28.86Wh\nNominal Voltage: 3.7 V\nLimited Charge Voltage: 5.0 V\nStandard Charging Current (Through the Type-C port): 500 mA\nStandard Charging Time: 16 hours\nStandard Discharging Current: 1600 mA\nMax. Discharging Current: 2000 mA\nCut-off Voltage: 2.75 V\nOperating Charge: 0 ~ 45 ℃\nTemperature Range Discharge: -20 ~ 60 ℃\nOperating humidity Range ≤ 90% RH\nSize : 4.27 x 2.36 x 0.91 inches\nWeight: 186 g\nSpecific Energy:155.0 Wh/kg","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["E6 2.0 Pro","E8 2.0 Pro","E9 2.0 Pro","A80 Pro"]},{"type":"Long Range WiFi","hw":"","cameras":["R3 Pro","R3 2.0 Pro"]},{"type":"Cellular","hw":"","cameras":["X66 Pro","X66 Pro Max","X66 3.0 Pro","X70","X70 Pro","X70 3.0 Pro"]}]},{"category":"battery","categoryLabel":"Lithium Battery","model":"L1750E","note":"Non-cellular camera without lithium battery; neither is supported.","specs":"Rated Capacity:17500 mAh,64.75Wh\nNominal Voltage:3.7 V\nCharging Input Voltage (Type-C PD Fast Charge):5.0V / 9.0V / 12.0V\nStandard Charging Current (Through the Type-C port):\n5.0V / 2.9A\n9.0V / 1.6A\n12.0V / 1.2A\nMax Charging Power:14.5 W\nStandard Charging Time Approx.:7 hours\nStandard Discharging Current:1600 mA\nMax. Discharging Current:2000 mA\nCut-off Voltage:2.75 V\nOperating Temperature Range:\nCharge:0 ~ 45 ℃\nDischarge:-20 ~ 60 ℃\nOperating humidity Range:≤ 90% RH\nSize:4.24 x 2.82 x 0.85 inches\nweighet:285g\nSpecific Energy:227 Wh/kg","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":[]},{"type":"Cellular","hw":"","cameras":["X66 3.0 Ultra","X70 3.0 Ultra"]}]}],"camera_count":71},{"brand":"Meidase","accessories":[{"category":"solar","categoryLabel":"Solar Panel","model":"SP300","note":"单机无带锂电,均不支持","specs":"","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["P100 Pro","P200 Pro","P300 Pro","P500 Pro"]},{"type":"Cellular","hw":"","cameras":["S950P","S950PMB","S950PLMB","S965 Pro","S970 Pro","SP980 Pro"]}]},{"category":"solar","categoryLabel":"Solar Panel","model":"SP350","note":"所有型号","specs":"","groups":[{"type":"Non-Cellular","hw":"","cameras":["P60","P60 Pro","P70","S5","S3","S3 Pro"]},{"type":"WiFi","hw":"H/W>=V8.2","cameras":["P80","P80 Pro","P90","P90 Pro","P100","P100 Pro","P200","P200 Pro","P300","P300 Pro","P500 Pro"]},{"type":"WiFi","hw":"H/W<V8.2","cameras":["P80","P80 Pro","P90","P90 Pro","P100","P200","P300"]},{"type":"Cellular","hw":"H/W>=V8.2","cameras":["S600","S900","S950","S950P","S950PMB","S950PLMB","S960","S965","S965 Pro","S970","S970 Pro","SP980 Pro"]},{"type":"Cellular","hw":"H/W<V8.2","cameras":["S950P","S950PMB","S950PLMB","S965 Pro","S970 Pro","SP980 Pro"]}]},{"category":"solar","categoryLabel":"Solar Panel","model":"SP1000","note":"单机无带锂电,均不支持","specs":"","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["P100 Pro","P200 Pro","P300 Pro","P500 Pro"]},{"type":"Cellular","hw":"","cameras":["S950P","S950PMB","S950PLMB","S965 Pro","S970 Pro","SP980 Pro"]}]},{"category":"box","categoryLabel":"Security Box","model":"MB100","note":"仅适配大盒子机型","specs":"Dimensions: 6.77 × 4.57 × 2.48 inch (17.2 × 11.6 × 6.3 cm)\nLock Hole Diameter: 0.28 inch (0.7 cm)\nLock: Not Included\nMounting Options: Strap Mounting or Screw Installation\nMounting Strap: Not Included (Use the Strap Supplied with the Camera)\nMounting Screws: Not Included","groups":[{"type":"Non-Cellular","hw":"","cameras":["S3","S3 Pro"]},{"type":"WiFi","hw":"","cameras":[]},{"type":"Cellular","hw":"","cameras":[]}]},{"category":"box","categoryLabel":"Security Box","model":"MB200","note":"","specs":"Dimensions: 6.93 × 4.49 × 2.52 inch (17.6 × 11.4 × 6.4 cm)\nLock Hole Diameter: 0.39 inch (1 cm)\nLock: Not Included\nMounting Options: Strap Mounting or Screw Installation\nMounting Strap: Not Included (Use the Strap Supplied with the Camera)\nMounting Screws: Not Included","groups":[{"type":"Non-Cellular","hw":"","cameras":["P60","P60 Pro","P70","S5"]},{"type":"WiFi","hw":"","cameras":["P80","P80 Pro","P90","P90 Pro","P100","P100 Pro","P200","P200 Pro"]},{"type":"Cellular","hw":"","cameras":["S600","S900","S950","S950P","S950PMB","S950PLMB","S960"]}]},{"category":"box","categoryLabel":"Security Box","model":"MB300","note":"","specs":"Dimensions: 6.93 × 4.49 × 3.07 inch (17.6 × 11.4 × 7.8 cm)\nLock Hole Diameter: 0.39 inch (1 cm)\nLock: Not Included\nMounting Options: Strap Mounting or Screw Installation\nMounting Strap: Not Included (Use the Strap Supplied with the Camera)\nMounting Screws: Not Included","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["P300"]},{"type":"Cellular","hw":"","cameras":["S965","S970"]}]},{"category":"box","categoryLabel":"Security Box","model":"MB350","note":"","specs":"Dimensions: 6.93 × 4.49 × 3.26 inch (17.6 × 11.4 × 8.28 cm)\nLock Hole Diameter: 0.39 inch (1 cm)\nLock: Not Included\nMounting Options: Strap Mounting or Screw Installation\nMounting Strap: Not Included (Use the Strap Supplied with the Camera)\nMounting Screws: Not Included","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["P300 Pro","P500 Pro"]},{"type":"Cellular","hw":"","cameras":["S965 Pro","S970 Pro","S980 Pro"]}]},{"category":"battery","categoryLabel":"Lithium Battery","model":"L800D","note":"单机无带锂电,均不支持","specs":"","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["P300 Pro","P500 Pro"]},{"type":"Cellular","hw":"","cameras":["S950P","S950PMB","S950PLMB","S965 Pro","S970 Pro"]}]},{"category":"battery","categoryLabel":"Lithium Battery","model":"L780D","note":"单机无带锂电,均不支持","specs":"","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":["P300 Pro","P500 Pro"]},{"type":"Cellular","hw":"","cameras":["S965 Pro","S970 Pro"]}]},{"category":"battery","categoryLabel":"Lithium Battery","model":"L1750E","note":"单机无带锂电,均不支持","specs":"","groups":[{"type":"Non-Cellular","hw":"","cameras":[]},{"type":"WiFi","hw":"","cameras":[]},{"type":"Cellular","hw":"","cameras":["S980 Pro"]}]}],"camera_count":30}]}</script>
<script>
/* ============================================================
GardePro — Accessory Compatibility Finder
纯原生 JS,无依赖。数据来自 #cp-data(JSON)
============================================================ */
(function () {
"use strict";
var CFG = window.CP_CONFIG || {};
var DEFAULT_BRAND = CFG.brand || "GardePro";
var CATS = [
{ key: "solar", single: "Solar Panel", plural: "Solar Panels",
icon: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 5h16l2 11H2L4 5Z"/><path d="M9 5 8 16M15 5l1 11M3 10.5h18"/></svg>' },
{ key: "box", single: "Security Box", plural: "Security Boxes",
icon: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2.5"/><path d="M3 9h18M9 9v11"/></svg>' },
{ key: "battery", single: "Lithium Battery", plural: "Lithium Batteries",
icon: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="16" height="10" rx="2.5"/><path d="M21 10.5v3M6 7v10"/></svg>' }
];
var TYPE_ORDER = { "Non-Cellular": 0, "WiFi": 1, "Long Range WiFi": 2, "Cellular": 3 };
// 面向客户的类型名("WiFi" → "WiFi Camera")
function typeDisplay(t) { return t ? t + " Camera" : t; }
// 太阳能板:特殊徽章(显示在相机类型标签下方)与排序(每行两款:无锂电池一行、带锂电池一行)
var SOLAR_TAGS = {
"SP1000": ["Recommended · 10W"],
"SP300": ["Compact · 3.5W"],
"SP350": ["Compact · 3.5W", "Built-in Lithium Battery · 5200 mAh"],
"SP1100": ["Recommended · 10W", "Built-in Lithium Battery · 10400 mAh"]
};
var SOLAR_ORDER = { "SP1000": 0, "SP300": 1, "SP1100": 2, "SP350": 3 };
// 内部备注不展示给客户
function customerNote(note) {
if (!note) return "";
var n = String(note).trim();
if (/neither is supported/i.test(n)) return "";
if (/^(all models|所有型号)/i.test(n)) return "";
if (/must meet the hardware version requirements/i.test(n)) return "";
return n;
}
// 硬件版本条件的通俗解释
function hwTip(hw) {
if (!hw) return "";
if (/>=/.test(hw)) {
return "Fits cameras with hardware version V8.2 or newer. You can check yours in the camera menu: Settings → Version Info.";
}
if (/</.test(hw)) {
return "For cameras with hardware version older than V8.2. If yours is V8.2 or newer, please use the other compatible option listed.";
}
return "";
}
// HW 标签(带 "?" 提示)
function hwTagHtml(hw) {
var tip = hwTip(hw);
return '<span class="cp-tag is-hw">' + esc(hwLabel(hw)) +
(tip ? '<button class="cp-hw" type="button" tabindex="0" aria-label="Hardware version info" data-tip="' + esc(tip) + '">?</button>' : "") +
"</span>";
}
var $ = function (s, r) { return (r || document).querySelector(s); };
function readData() {
var el = document.getElementById("cp-data");
if (!el) return null;
try { return JSON.parse(el.textContent); } catch (e) { return null; }
}
var DATA = readData();
if (!DATA || !DATA.brands || !DATA.brands.length) return;
var brandKey = DEFAULT_BRAND;
try {
var qp = new URLSearchParams(location.search).get("brand");
if (qp && DATA.brands.some(function (b) { return b.brand === qp; })) brandKey = qp;
} catch (e) {}
var BRAND = DATA.brands.filter(function (b) { return b.brand === brandKey; })[0] || DATA.brands[0];
var ACCS = BRAND.accessories.slice();
/* ---------- 索引 ---------- */
function key(s) { return String(s).toLowerCase().replace(/[^a-z0-9]/g, ""); }
var INDEX = Object.create(null); // key -> { name, entries: [{acc, group}], types: {} }
ACCS.forEach(function (acc) {
acc.groups.forEach(function (g) {
g.cameras.forEach(function (cam) {
var k = key(cam);
if (!INDEX[k]) INDEX[k] = { name: cam, entries: [], types: {} };
// 同名型号保持首次出现的写法
INDEX[k].entries.push({ acc: acc, group: g });
INDEX[k].types[g.type] = true;
});
});
});
function typeHint(rec) {
return Object.keys(rec.types)
.sort(function (a, b) { return (TYPE_ORDER[a] || 9) - (TYPE_ORDER[b] || 9); })
.map(typeDisplay)
.join(" · ");
}
function naturalCompare(a, b) {
var re = /(\d+)|(\D+)/g;
var pa = String(a).match(re) || [], pb = String(b).match(re) || [];
for (var i = 0; i < Math.min(pa.length, pb.length); i++) {
var x = pa[i], y = pb[i];
var nx = parseInt(x, 10), ny = parseInt(y, 10);
if (!isNaN(nx) && !isNaN(ny)) { if (nx !== ny) return nx - ny; }
else if (x !== y) return x < y ? -1 : 1;
}
return pa.length - pb.length;
}
function esc(s) {
return String(s).replace(/[&<>"']/g, function (c) {
return ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c];
});
}
function hwLabel(hw) {
if (!hw) return "";
return hw.replace(/H\/W\s*/g, "HW ").replace(/>=/g, "≥").replace(/<=/g, "≤").replace(/=/g, "= ");
}
/* ---------- 规格解析 ----------
原表参数列以换行为主、斜杠为辅分隔;斜杠也常出现在值内(如 12V/1A)。
策略:先按行切,行内只在「斜杠后一段以 Key: 开头」时才切;
再把无冒号的短字母段视为下一行的键前缀(如 Solar Panel MAX + Output Power: 12W)。 */
function colonIdx(s) {
var a = s.indexOf(":"), b = s.indexOf(":");
if (a === -1) return b;
if (b === -1) return a;
return Math.min(a, b);
}
function splitSlash(line) {
var parts = line.split("/");
if (parts.length === 1) return [line];
var out = [parts[0]];
for (var i = 1; i < parts.length; i++) {
var prev = out[out.length - 1];
var seg = parts[i];
var ci = colonIdx(seg);
var prevEndsColon = /[::]\s*$/.test(prev);
if (prevEndsColon || (ci > -1 && ci <= 48)) out.push(seg);
else out[out.length - 1] = prev + "/" + seg;
}
return out;
}
function parseSpecs(text) {
if (!text) return [];
var t = String(text).replace(/\t/g, " ");
var tokens = [];
t.split(/\r?\n/).forEach(function (line) {
if (line.trim()) tokens = tokens.concat(splitSlash(line));
});
var pairs = [], pending = [], cur = null;
tokens.forEach(function (rawTok, i) {
var tok = rawTok.trim();
if (!tok) return;
var ci = colonIdx(tok);
if (ci > -1 && ci <= 48) {
var k = tok.slice(0, ci).replace(/\s+/g, " ").trim();
var v = tok.slice(ci + 1).trim();
if (pending.length) { k = pending.join(" ") + " " + k; pending = []; }
cur = [k, v];
pairs.push(cur);
} else {
var nxt = i + 1 < tokens.length ? tokens[i + 1].trim() : "";
var nci = nxt ? colonIdx(nxt) : -1;
if (cur && !cur[1]) cur[1] = tok; // 前一对值还空着 → 续值
else if (!cur || (nci > -1 && nci <= 48 && tok.length <= 22 && !/\d/.test(tok)))
pending.push(tok); // 短字母段 → 下一对的键前缀
else cur[1] = cur[1] ? cur[1] + " / " + tok : tok; // 普通续值
}
});
return pairs.filter(function (kv) { return kv[0] && kv[1]; });
}
/* ---------- 搜索 ---------- */
var input = $("#cp-input");
var sugg = $("#cp-sugg");
var activeIdx = -1;
var currentList = [];
function searchModels(q) {
var k = key(q);
if (!k) return [];
var all = Object.keys(INDEX);
var exact = [], prefix = [], contains = [];
all.forEach(function (ik) {
if (ik === k) exact.push(ik);
else if (ik.indexOf(k) === 0) prefix.push(ik);
else if (ik.indexOf(k) > -1) contains.push(ik);
});
// 按「词首匹配」优先,其次按包含位置
contains.sort(function (a, b) { return a.indexOf(k) - b.indexOf(k) || naturalCompare(INDEX[a].name, INDEX[b].name); });
prefix.sort(function (a, b) { return naturalCompare(INDEX[a].name, INDEX[b].name); });
return exact.concat(prefix, contains);
}
function renderSugg(q) {
var keys = searchModels(q).slice(0, 8);
currentList = keys;
activeIdx = -1;
if (!keys.length) {
sugg.innerHTML = '<li><button type="button" disabled style="color:#98a2b3;cursor:default">No model matches “' + esc(q) + '”</button></li>';
sugg.classList.add("is-open");
input.setAttribute("aria-expanded", "true");
return;
}
var k = key(q);
sugg.innerHTML = keys.map(function (ik) {
var rec = INDEX[ik];
var name = esc(rec.name);
if (k) {
var pos = rec.name.toLowerCase().indexOf(q.toLowerCase().trim());
if (pos > -1) {
name = esc(rec.name.slice(0, pos)) + "<mark>" + esc(rec.name.slice(pos, pos + q.trim().length)) + "</mark>" + esc(rec.name.slice(pos + q.trim().length));
}
}
return '<li role="option"><button type="button" data-cam="' + esc(rec.name) + '">' +
"<span>" + name + '</span><span class="cp-sugg-hint">' + esc(typeHint(rec)) + "</span></button></li>";
}).join("");
sugg.classList.add("is-open");
input.setAttribute("aria-expanded", "true");
}
function closeSugg() {
sugg.classList.remove("is-open");
input.setAttribute("aria-expanded", "false");
activeIdx = -1;
}
function highlight(i) {
var btns = sugg.querySelectorAll("button[data-cam]");
btns.forEach(function (b, n) { b.classList.toggle("is-active", n === i); });
if (btns[i]) btns[i].scrollIntoView({ block: "nearest" });
}
/* ---------- 结果渲染 ---------- */
var resultEl = $("#cp-result");
function matchesFor(camName) {
var k = key(camName);
var rec = INDEX[k];
if (!rec) return null;
var byAcc = [];
var seen = Object.create(null);
rec.entries.forEach(function (e) {
var id = e.acc.category + "|" + e.acc.model;
if (!seen[id]) { seen[id] = { acc: e.acc, conds: [] }; byAcc.push(seen[id]); }
seen[id].conds.push({ type: e.group.type, hw: e.group.hw });
});
return { camera: rec, items: byAcc };
}
// 类型标签(不含硬件版本条件)
function typeTagsHtml(conds) {
var seen = Object.create(null), out = [];
conds.forEach(function (c) { if (!seen[c.type]) { seen[c.type] = 1; out.push(c.type); } });
return out.map(function (t) {
return '<span class="cp-tag">' + esc(typeDisplay(t)) + "</span>";
}).join("");
}
// 硬件版本条件标签(带 "?")
function hwOnlyTags(conds) {
var seen = Object.create(null), out = [];
conds.forEach(function (c) { if (c.hw && !seen[c.hw]) { seen[c.hw] = 1; out.push(c.hw); } });
return out.map(hwTagHtml).join("");
}
function cardHtml(m, tagsCfg) {
var acc = m.acc;
var specs = parseSpecs(acc.specs);
var specHtml = specs.length
? '<dl class="cp-specs">' + specs.map(function (kv) {
return "<div><dt>" + esc(kv[0]) + "</dt><dd>" + esc(kv[1]) + "</dd></div>";
}).join("") + "</dl>"
: "";
var badges = (tagsCfg && tagsCfg[acc.model]) || [];
var badgesHtml = badges.map(function (b) {
var cls = /Recommended/i.test(b) ? "cp-badge is-rec" : "cp-chip";
return '<span class="' + cls + '">' + esc(b) + "</span>";
}).join("");
var hwHtml = hwOnlyTags(m.conds);
var isRec = badges.some(function (b) { return /Recommended/i.test(b); });
return '<article class="cp-card' + (isRec ? " is-rec" : "") + '">' +
'<div class="cp-card-top"><div class="cp-model">' + esc(acc.model) + '</div>' +
'<span class="cp-badge">Compatible</span></div>' +
'<div class="cp-conds">' + typeTagsHtml(m.conds) + "</div>" +
(badgesHtml ? '<div class="cp-conds">' + badgesHtml + "</div>" : "") +
(hwHtml ? '<div class="cp-conds">' + hwHtml + "</div>" : "") +
(customerNote(acc.note) ? '<p class="cp-note">' + esc(customerNote(acc.note)) + "</p>" : "") +
(specHtml
? '<div class="cp-card-foot"><button class="cp-more" type="button" aria-expanded="false">Specifications' +
'<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg></button>' +
specHtml + "</div>"
: "") +
"</article>";
}
function showResult(camName, opts) {
var m = matchesFor(camName);
if (!m) { showNoMatch(camName); return; }
var rec = m.camera;
var html = '<div class="cp-cam-head">' +
'<span class="cp-cam-label">Compatible accessories for</span>' +
'<div class="cp-cam-name">' + esc(rec.name) + "</div>" +
'<span class="cp-tag">' + esc(typeHint(rec)) + "</span>" +
'<span class="cp-spacer"></span>' +
'<button class="cp-ghost-btn" type="button" data-act="reset">Search Another Model</button>' +
"</div>";
CATS.forEach(function (cat) {
var tagsCfg = cat.key === "solar" ? SOLAR_TAGS : null;
var orderMap = cat.key === "solar" ? SOLAR_ORDER : null;
var items = m.items.filter(function (i) { return i.acc.category === cat.key; })
.sort(function (a, b) {
if (orderMap) {
var oa = orderMap.hasOwnProperty(a.acc.model) ? orderMap[a.acc.model] : 99;
var ob = orderMap.hasOwnProperty(b.acc.model) ? orderMap[b.acc.model] : 99;
if (oa !== ob) return oa - ob;
}
return naturalCompare(a.acc.model, b.acc.model);
});
html += '<section class="cp-cat">' +
'<div class="cp-cat-title"><h2>' + cat.icon + esc(cat.plural) + "</h2>" +
'<span class="cp-count">' + (items.length ? items.length + (items.length > 1 ? " compatible models" : " compatible model") : "not listed") + "</span></div>";
html += items.length
? '<div class="cp-cards">' + items.map(function (i) { return cardHtml(i, tagsCfg); }).join("") + "</div>"
: '<p class="cp-empty">No ' + esc(cat.single.toLowerCase()) + " is listed as compatible with the " + esc(rec.name) +
". Please contact support if you need help choosing.</p>";
html += "</section>";
});
resultEl.innerHTML = html;
resultEl.hidden = false;
bindResultEvents();
if (!opts || !opts.silent) {
resultEl.scrollIntoView({ behavior: "smooth", block: "start" });
}
syncUrl(rec.name);
}
function showNoMatch(q) {
var guess = searchModels(q).slice(0, 6);
var html = '<div class="cp-cam-head"><div class="cp-cam-name">No match for “' + esc(q) + '”</div>' +
'<span class="cp-cam-sub">Check the model name, or pick one of the closest matches below.</span></div>';
if (guess.length) {
html += '<div class="cp-cat"><div class="cp-cat-title"><h2>Did You Mean</h2></div><div class="cp-pills">' +
guess.map(function (k) {
return '<button class="cp-pill" type="button" data-cam="' + esc(INDEX[k].name) + '">' + esc(INDEX[k].name) + "</button>";
}).join("") + "</div></div>";
}
resultEl.innerHTML = html;
resultEl.hidden = false;
bindResultEvents();
resultEl.scrollIntoView({ behavior: "smooth", block: "start" });
}
function bindResultEvents() {
bindHwPop(resultEl);
resultEl.querySelectorAll(".cp-more").forEach(function (btn) {
btn.addEventListener("click", function () {
var open = btn.getAttribute("aria-expanded") === "true";
btn.setAttribute("aria-expanded", String(!open));
var dl = btn.parentNode.querySelector(".cp-specs");
if (dl) dl.classList.toggle("is-open", !open);
});
});
var reset = resultEl.querySelector('[data-act="reset"]');
if (reset) reset.addEventListener("click", function () {
input.value = "";
resultEl.hidden = true;
resultEl.innerHTML = "";
syncUrl("");
input.focus();
window.scrollTo({ top: 0, behavior: "smooth" });
});
resultEl.querySelectorAll("[data-cam]").forEach(function (b) {
b.addEventListener("click", function () {
input.value = b.getAttribute("data-cam");
showResult(b.getAttribute("data-cam"));
});
});
}
function syncUrl(model) {
try {
var u = new URL(location.href);
if (model) u.searchParams.set("model", model); else u.searchParams.delete("model");
history.replaceState(null, "", u.toString());
} catch (e) {}
}
/* ---------- 浏览 ---------- */
var tabsEl = $("#cp-tabs");
var gridEl = $("#cp-grid");
var activeCat = "solar";
function renderTabs() {
tabsEl.innerHTML = CATS.map(function (c) {
var n = ACCS.filter(function (a) { return a.category === c.key; }).length;
if (!n) return "";
return '<button type="button" role="tab" data-cat="' + c.key + '"' +
(c.key === activeCat ? ' class="is-active" aria-selected="true"' : ' aria-selected="false"') + ">" +
esc(c.plural) + " (" + n + ")</button>";
}).join("");
}
function renderGrid() {
var list = ACCS.filter(function (a) { return a.category === activeCat; })
.sort(function (a, b) { return naturalCompare(a.model, b.model); });
gridEl.innerHTML = list.map(function (acc) {
var cams = Object.create(null), n = 0;
acc.groups.forEach(function (g) { g.cameras.forEach(function (c) { if (!cams[c]) { cams[c] = 1; n++; } }); });
return '<button class="cp-tile" type="button" data-acc="' + esc(acc.category + "|" + acc.model) + '">' +
'<span class="cp-tile-model">' + esc(acc.model) + "</span>" +
'<span class="cp-tile-meta">' + n + " compatible camera" + (n === 1 ? "" : "s") + "</span>" +
'<span class="cp-tile-view">View details' +
'<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span></button>';
}).join("");
}
/* ---------- 弹层 ---------- */
var modal = $("#cp-modal");
var modalBody = $("#cp-modal-body");
var lastFocus = null;
function openAcc(categoryKey, model) {
var acc = ACCS.filter(function (a) { return a.category === categoryKey && a.model === model; })[0];
if (!acc) return;
var cat = CATS.filter(function (c) { return c.key === acc.category; })[0];
var specs = parseSpecs(acc.specs);
var groupsHtml = acc.groups.map(function (g) {
var cams = g.cameras.slice();
if (!cams.length) return "";
return '<div class="cp-typegroup">' +
'<div class="cp-typegroup-head">' + esc(typeDisplay(g.type || "All models")) +
(g.hw ? hwTagHtml(g.hw) : "") +
'<span class="cp-count" style="font-weight:400;color:#6b7683">' + cams.length + "</span></div>" +
'<div class="cp-pills">' + cams.map(function (c) {
return '<button class="cp-pill" type="button" data-cam="' + esc(c) + '">' + esc(c) + "</button>";
}).join("") + "</div></div>";
}).join("");
modalBody.innerHTML =
'<div class="cp-modal-head">' +
'<div class="cp-modal-cat">' + esc(cat.single) + "</div>" +
'<h3 id="cp-modal-title">' + esc(acc.model) + "</h3>" +
"</div>" +
(specs.length ? '<div class="cp-modal-block"><h4>Specifications</h4><table class="cp-spec-table"><tbody>' +
specs.map(function (kv) { return "<tr><td>" + esc(kv[0]) + "</td><td>" + esc(kv[1]) + "</td></tr>"; }).join("") +
"</tbody></table></div>" : "") +
'<div class="cp-modal-block"><h4>Compatible Camera Models</h4>' + (groupsHtml || '<p class="cp-empty">No compatible models listed.</p>') + "</div>";
bindHwPop(modalBody);
lastFocus = document.activeElement;
modal.classList.add("is-open");
document.body.style.overflow = "hidden";
$("#cp-modal-close").focus();
modalBody.querySelectorAll("[data-cam]").forEach(function (b) {
b.addEventListener("click", function () {
var name = b.getAttribute("data-cam");
closeModal();
input.value = name;
showResult(name);
});
});
}
function closeModal() {
modal.classList.remove("is-open");
document.body.style.overflow = "";
if (lastFocus && lastFocus.focus) lastFocus.focus();
}
/* ---------- 事件绑定 ---------- */
input.addEventListener("input", function () {
var q = input.value.trim();
if (!q) { closeSugg(); return; }
renderSugg(q);
});
input.addEventListener("keydown", function (e) {
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
if (!sugg.classList.contains("is-open")) { renderSugg(input.value.trim()); return; }
e.preventDefault();
var n = currentList.length;
if (!n) return;
activeIdx = e.key === "ArrowDown" ? (activeIdx + 1) % n : (activeIdx - 1 + n) % n;
highlight(activeIdx);
} else if (e.key === "Enter") {
e.preventDefault();
if (sugg.classList.contains("is-open") && activeIdx > -1 && currentList[activeIdx]) {
var name = INDEX[currentList[activeIdx]].name;
input.value = name;
closeSugg();
showResult(name);
} else {
doSearch();
}
} else if (e.key === "Escape") {
closeSugg();
}
});
sugg.addEventListener("mousedown", function (e) { e.preventDefault(); });
sugg.addEventListener("click", function (e) {
var b = e.target.closest("button[data-cam]");
if (!b) return;
var name = b.getAttribute("data-cam");
input.value = name;
closeSugg();
showResult(name);
});
function doSearch() {
var q = input.value.trim();
if (!q) { input.focus(); return; }
closeSugg();
var keys = searchModels(q);
if (!keys.length) { showNoMatch(q); return; }
var exact = keys.filter(function (k) { return k === key(q); })[0];
showResult(INDEX[exact || keys[0]].name);
}
$("#cp-search-btn").addEventListener("click", doSearch);
tabsEl.addEventListener("click", function (e) {
var b = e.target.closest("button[data-cat]");
if (!b) return;
activeCat = b.getAttribute("data-cat");
renderTabs();
renderGrid();
});
gridEl.addEventListener("click", function (e) {
var b = e.target.closest("[data-acc]");
if (!b) return;
var parts = b.getAttribute("data-acc").split("|");
openAcc(parts[0], parts[1]);
});
$("#cp-modal-close").addEventListener("click", closeModal);
modal.addEventListener("click", function (e) { if (e.target === modal) closeModal(); });
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && modal.classList.contains("is-open")) closeModal();
});
/* ---------- HW "?" 弹层(全局唯一,fixed 定位,避免被卡片/弹窗裁剪) ---------- */
var hwPop = null, hwPopBtn = null;
function hideHwPop() {
if (hwPop) {
var el = hwPop;
el.classList.remove("is-show");
setTimeout(function () { if (el.parentNode) el.parentNode.removeChild(el); }, 160);
hwPop = null; hwPopBtn = null;
}
}
function showHwPop(btn) {
if (hwPopBtn === btn) return;
hideHwPop();
hwPopBtn = btn;
hwPop = document.createElement("div");
hwPop.className = "cp-hw-pop";
hwPop.textContent = btn.getAttribute("data-tip") || "";
document.body.appendChild(hwPop);
var r = btn.getBoundingClientRect();
var vw = document.documentElement.clientWidth;
requestAnimationFrame(function () {
if (!hwPop) return;
var pw = hwPop.offsetWidth, ph = hwPop.offsetHeight;
var left = Math.min(Math.max(8, r.left + r.width / 2 - pw / 2), vw - pw - 8);
var top = r.bottom + 8;
if (top + ph > window.innerHeight - 8) top = r.top - ph - 8;
hwPop.style.left = left + "px";
hwPop.style.top = Math.max(8, top) + "px";
hwPop.classList.add("is-show");
});
}
function bindHwPop(scope) {
(scope || document).querySelectorAll(".cp-hw").forEach(function (btn) {
if (btn.__hwBound) return;
btn.__hwBound = true;
btn.addEventListener("mouseenter", function () { showHwPop(btn); });
btn.addEventListener("mouseleave", hideHwPop);
btn.addEventListener("focus", function () { showHwPop(btn); });
btn.addEventListener("blur", hideHwPop);
btn.addEventListener("click", function (e) {
e.stopPropagation();
if (hwPopBtn === btn) hideHwPop(); else showHwPop(btn);
});
});
}
document.addEventListener("scroll", hideHwPop, true);
window.addEventListener("resize", hideHwPop);
document.addEventListener("click", hideHwPop);
/* ---------- 初始化 ---------- */
function init() {
renderTabs();
renderGrid();
// 常用机型快捷入口
var popular = ["X66 3.0 Ultra", "X66 Pro", "E9 3.0 Pro", "E8 3.0 Pro", "E8 2.0 Pro", "A3S"];
var quick = $("#cp-quick");
popular.filter(function (p) { return INDEX[key(p)]; }).forEach(function (p) {
var b = document.createElement("button");
b.type = "button";
b.textContent = p;
b.addEventListener("click", function () {
input.value = p;
showResult(p);
});
quick.appendChild(b);
});
var totalCams = Object.keys(INDEX).length;
$("#cp-foot-stats").textContent = totalCams + " camera models · " + ACCS.length + " accessories";
// 深链 ?model= 或 ?acc=
try {
var sp = new URLSearchParams(location.search);
var m = sp.get("model");
if (m && INDEX[key(m)]) {
input.value = INDEX[key(m)].name;
showResult(INDEX[key(m)].name, { silent: true });
}
var accQ = sp.get("acc");
if (accQ) {
var hit = ACCS.filter(function (a) { return key(a.model) === key(accQ); })[0];
if (hit) openAcc(hit.category, hit.model);
}
} catch (e) {}
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init);
else init();
})();
</script>