# eBird Mobile Counter

純 HTML / CSS / JavaScript 手機計鳥工具，部署在 GitHub Pages，不需要 Tampermonkey。文字輸出可交給本 repo 的 `EBirdTextInputAssistant.user.js`（請更新至 1.12.5，支援完整鳥名、GPS 距離、中文繁殖簡稱與編輯既有清單）。

## 開啟

- 入口：`counter/index.html`；既有網域的專案網址是 `https://christorng.idv.tw/eBirdScripts/counter/`。
- 選地點、日期時間與 GPS 後即可開始。時間使用手機原生選擇器，禁止未來時間；預設開始時採當下時間，不因分鐘欄位而多算幾十秒。
- 桌面與手機共用最大 360px 的直式版面，方便在電腦上確認手機尺寸。排序頁的鳥名、頻率與移動按鈕各有固定空間。設定內可安裝為獨立網頁 App。
- 功能分支 push 不會更新正式網站；合併到 `main` 後由既有 Pages workflow 發布。沒有修改網域或 CNAME。

## 安裝為網頁 App（PWA）

設定 →「安裝計鳥 App」。支援安裝提示的瀏覽器會開啟安裝流程；否則提示從瀏覽器選單安裝。iPhone / iPad 可用 Safari 分享 → 加入主畫面。Manifest 使用穩定 id、counter/ scope、standalone、192／512 PNG 與 maskable 圖示，另有 Apple touch icon。

安裝後可由主畫面直接開啟，使用獨立視窗；成功快取後可離線計數。首次載入、更新資料與附近熱點仍需網路。這仍是網頁，不保證背景 GPS 持續執行；清除網站資料會清掉本機鳥單與設定，沒有跨裝置自動同步。連網重新整理會優先取得新版，離線時才使用快取。頁首的「版本時間」是網站發布時產生的台灣時間；若 service worker 更新時頁面仍開著，會出現「重新開啟」按鈕，計數會先存入本機。

瀏覽器上一頁會優先關閉繁殖選單、數量編輯或返回計鳥頁；正在計鳥時離開會確認。選單右上 × 固定可見，Esc 也可關閉。沒有永久禁用瀏覽器離開功能。

## 計數

每列為 `+10 +5 +  個體數 鳥名  繁殖  聽到數 聽 +`。點鳥名／個體數展開編輯，下面的 `-10 -5 -`、個體數、聽到數與聽到減號對齊上排；可直接修改數字及輸入描述。

- **聽到 +** 同時增加個體總數，並閃示總數及個體 `+`。**聽到 −** 只減聽到數，適用於稍後確實看見同一隻鳥。直接把聽到欄調高／調低也採相同規則。
- 搜尋欄的 `×` 僅在有輸入時顯示；清除後會離開輸入焦點並收起行動裝置鍵盤。點鳥名的編輯區會插入該列下方，操作其他鳥種或功能時自動收起。
- 輸入搜尋文字時一律捲到列表開頭；搜尋右側 × 或刪除所有文字會恢復未篩選狀態並回到搜尋前的位置。搜尋可輸入完整名或簡稱中的任意字；多字要求每字均有出現。輸入整數（包含負數）時不做鳥名篩選，接著點鳥名／總數便套用增減，最低為 0。
- 支援 `input`、`compositionupdate`、`compositionend`；輸入法尚未提供漢字時，不能從未選字的注音推知鳥名。
- 按加減鈕時，被按的按鈕與實際變動的總數／聽到數會短暫高亮。
- 描述為空不佔一行；非空描述在列表保留顯示，可折行。繁殖選擇顯示 eBird 英文代碼及完整中英文字，其中與簡稱相符的中文字加粗。
- 總數低於聽到数、記一對但不到 2 隻、有描述／繁殖但總數為 0，會顯示紅色提醒。第一次「停止」捲到第一項並閃示；未修改資料而再次停止可繼續。修改資料後重新檢查。
- 耗時在一小時內顯示 `mm:ss`，之後顯示 `h:mm:ss`。停止後可修改文字、複製、下載、繼續計鳥；尚未複製便從頭開始會確認。

## 全部地點共用的頻率與分組

排序來源依序為：**匯入的個人完整鳥單 → 全台公開頻率**，不隨所選地點改變。預設使用所有年份的全年頻率，僅保存全年頻率，不按月份重排。

初次建立或匯入新頻率（尚未自訂排列時），自動尋找接近「常見 15 種、少見再 30 種」的百分比界線；同頻率不拆組，並以組內原始鳥種順序排列，不按每隻鳥的頻率逐項排序。

| 區塊 | 規則 | 初始狀態 |
| --- | --- | --- |
| 常見 | 頻率 ≥ 常見門檻 | 展開 |
| 少見 | 少見門檻 ≤ 頻率 < 常見門檻 | 展開 |
| 罕見 | 0 < 頻率 < 少見門檻 | 收合 |
| 無紀錄 | 0 或無頻率資料 | 收合 |
| 其他分類 | 科、屬、未定類、斜線組合、雜交等非特定物種 | 收合 |

`organization.mjs` 的 `GROUP_TARGETS` 定義 15／30 的目標，無可用頻率時以 `DEFAULT_THRESHOLDS` 的 20%／2% 備援。自動界線先決定數量，百分比在不改變成員時向下取至兩位小數。其他分類依**出現頻率**遞減，0 或同頻率維持原序；現場點按的個體數不會使鳥種自行跳位。

設定中修改門檻會**立即預覽各組項目數**，尚未按「依門檻重建五區塊」不會變動排列。可用「自動抓 15 / 30 種」重新估算。已自訂的排列在更新頻率時保留，明確重建才取代。

點 footer 的 `種 / 隻` 會收合所有區塊；全收合時再點只展開常見／少見，後三組仍收合。各區塊仍可獨立開合。**有數量、繁殖或描述的列即使收合也保留**；只有鳥名搜尋可排除它。

### 自訂排序

- 首頁「鳥種 / 排序」可查看全年頻率，計數中點 `↕` 可直接調整。排序畫面也預設收合後三組。
- 拖動左侧 `↕` 移動鳥種，跨區也可指定實際插入位置；拖動區塊的 handle 移動整區。插入線標示放置處，不反白整列。
- 點鳥種 handle 可選另一區塊，**往下移放目標第一項，往上移放最後一項**，不要求填位置數字。
- 區塊的 `⋯` 可改名、調整順序或刪除；刪除時把鳥種轉移到指定區塊，不丟棄資料。至少保留一區。
- `profiles.global` 保存共享配置；架構保留 profile ID，日後可擴充縣市／環境等多組設定，目前只有一組。

## 個人紀錄 ZIP / CSV

在 [Download My Data](https://ebird.org/downloadMyData) 取得個人資料，在設定匯入官方 ZIP 或解壓縮後的 `MyEBirdData.csv`。**原檔只在瀏覽器處理，不上傳；repo 不包含任何個人原始檔或個人頻率。**

- 只以 `All Obs Reported` 為真之完整鳥單計算，分母為不同 `Submission ID` 的數量；同一鳥種在同一張鳥單最多算一次。0 隻不算出現，X 算出現。
- 全年頻率＝包含該鳥種的完整鳥單數／所有完整鳥單數。每月按鳥單日期月份重新計算；不是把鳥隻數相加或平均各月百分比。
- 相同可辨識物種的歷史名稱以鳥單 ID 聯集合併，保留不同亞種代碼。未能對應全台索引的名稱保留，不丟棄；未知名稱不猜翻譯／物種。
- CSV 支援 UTF-8 BOM、引號、逗號、跨行備註與省略空白尾欄。ZIP 支援標準 stored / deflate 與 CRC 檢查；不支援 ZIP64、加密、多個 CSV。瀏覽器不支援原生解壓縮時，可改匯入 CSV。
- 只存衍生頻率、樣本總數及已登錄地點缺少的座標，不保存原始描述、鳥單 ID 或行程軌跡。全年排序採個人所有地區的完整紀錄；`TW` 為內部共享頻率名稱空間，不是排除台灣以外鳥單的篩選條件。
- 既有地點有座標時不被匯入檔覆寫。「改用全台頻率」可移除本機個人統計，自訂排列仍保留。

### 全台公開資料與延後載入

隨附使用者提供的全台公開 Histogram，含 1,044 個分類項目。啟動只讀取約 88 KB 的全年索引（鳥名、識別碼、全年頻率）；這是分組、搜尋與計算區塊數量所必需。**收合區塊不建立未填資料的鸟種列**，展開／搜尋時才建立；有紀錄者即使收合仍建立並顯示。

只持久化全年頻率，不保存月頻率，也沒有月份預覽。匯入的個人／公開檔案已在本機，不需要展開時再連線。沒有按鳥種下載圖片或其他隱藏資源。

公開頻率可從設定更新全年 Histogram TXT，或嘗試線上更新。`personal=true` 參數**本身不保證回傳的是個人統計**；全台 personal 圖表曾回傳看似公開樣本，因此不以這個參數建立個人排序，個人排序以 Download My Data 內容為準。

公開 TXT 的 48 週比例與樣本數做加權平均：`Σ(週比例 × 週樣本數) / Σ週樣本數`。沒有樣本為 null，有樣本未出現為 0。

## 地點與 GPS

localStorage registry 初次預置 L16381971＝後港新公園、L18412499＝建國二路、L17621411＝市場；後續可新增、改名、刪除與設定座標，刪掉的地點不會自動加回。

接受純 `L123`、`/barchart?r=L123` 或完整 ebird.org barchart 網址，僅接受單一 L ID。新增即背景嘗試取得全年頻率；地點管理的「頻率」保留重新抓取、官方 TXT 匯入、來源／更新時間及全年預覽。此資料**不再左右共用的計數排序**，不會佔據開始畫面。

開啟時若 GPS 精度足夠，預選 500m 內最近的已設定地點；外面不預選。可開啟 eBird 附近地圖，或在設定填入自己的 [eBird API 金鑰](https://ebird.org/api/keygen) 後列出 5km 內熱點，選定並設定簡稱。已設地點顯示自己的簡稱並可編輯；首頁改選地點即更新與目前定位的距離，未設座標會顯示提示。金鑰僅本機保存，不包含在設定備份。附近 API 是否可直接讀取仍受 eBird 的存取與 CORS 政策限制，失敗時可從官方地圖貼回地點網址。

地點線上頻率依序嘗試 personal + `credentials: include`，再移除 personal 以公開模式請求，保留成功來源、fallback 原因及原有快取。**GitHub Pages 與 ebird.org 不同網域**，無法保證存取 eBird 登入 session；不繞過 CORS、不使用第三方代理、不要求帳密或 cookie。個人標記依網站下載連結／手動匯入來源，不能單靠 URL 證明樣本確實為個人；個人全部紀錄排序不使用此標記。

已核對的 eBird 資料來源包括官方 `/barchartData?...&fmt=tsv` TXT 及全年 Line Graphs 的 embedded `lgRaw` JSON（`values`、`values_N`）。不從 b0–b9 視覺級距猜精確頻率，也不執行抓取頁面的程式碼。

GPS 需要 HTTPS 與授權。距離排除精度 >50m、速度 >12m/s、逆序時間與微小漂移；點間隔 >120 秒記為中斷，不猜中間路程。網頁切到背景可能停止定位，**未取得的背景路段不能恢復**，但已累計距離與最後定位会持續保存。

## 文字助手相容性

列表與輸出優先使用簡稱；輸出使用助手可辨識的最短簡稱，沒有簡稱才保留完整鳥名，不略過任何項目。文字助手內建官方 taxonomy 的 17,891 個完整名稱，也接受既有簡稱；`scripts/generate-species-names.mjs` 可從官方 CSV 更新名稱表。全名依確切名稱對應，不把亞種隨意併入其他亞種。野鴿一律以「野鴿」顯示及輸出；原鴿、野鴿(野化)、野鴿(馴化) 仍可輸入，完成頁也支援原種與野化分類的名稱及連結。

新輸出例如：

```text
2026/9/28
後港新公園
08:30 開始 28 分鐘 1.2 km
珠頸斑鳩 6，唱歌，1 聽到；描述 "後來看見，停在樹上。"
```

助手會得到繁殖 S、comments `Heard 1, 後來看見，停在樹上。`。描述以 JSON 字串编码，包含引號或換行也不會被誤判為另一鳥種、個體數或繁殖關鍵字。輸出繁殖中文簡稱如唱歌、求偶、一對、巢雛；助手明確對應代碼，不靠模糊猜測。B 使用「啄鷦築巢」、NB 使用「築巢」以免混淆；舊 `[S]` 等格式仍可讀。

開啟 GPS 時，距離會附在努力量行末（例如 `08:30 開始 28 分鐘 1.2 km`）；助手會依距離判定定點或行進計數。未開啟 GPS 時不輸出距離，助手使用地點預設距離；沒有預設距離才視為附帶紀錄。文字中的距離（含 `0 km`）優先於預設值，也可使用「公里」單位。

## 保存、升級與驗證

每次計數、繁殖、描述、排序與有效定位都同步寫入 localStorage。耗時由絕對開始時間計算，reload 不歸零；停止時間、手動輸出與複製狀態也保存。v1 舊計數會一次性轉換為 `total = seen + heard`，不改動已編輯的文字輸出。

支援 Web Locks 避免多頁互相覆寫；不支援時使用 storage 事件停用有衝突的頁面。儲存失敗有固定警告；無法讀取的備份不覆寫，提供原檔下載。不同裝置、瀏覽器與清除網站資料不会自動同步，可從設定下載／還原 JSON **設定備份**，含地點及座標、簡稱、區塊、分組門檻與區塊內順序；不含鳥單、GPS 軌跡、頻率快取、個人原始資料或 API 金鑰。還原不覆蓋進行中的鳥單與本機頻率；匯入舊完整備份時也只取設定。

service worker 只快取 counter/ 自身的程式與公開索引，個人資料不進網路快取。部署流程每次產生 `build-info.mjs`，連網讀取採網路優先並更新離線快取；service worker 本身有變更時更新 `sw.js` 的 CACHE 版本。

```sh
npm ci
node scripts/generate-counter-aliases.js
node scripts/import-counter-data.mjs TW public /path/to/ebird_TW__1900_2026_1_12_barchart.txt
node --check counter/app.mjs
node --check counter/core.mjs
node --check counter/organization.mjs
node --check counter/personal.mjs
node --check EBirdTextInputAssistant.user.js
npm test
npm run generate:index
```

靜態 server 服務 repo 根目錄，開 `/counter/`；不要用 file://。測試包括 ZIP／CSV、完整鳥單分母、同張鳥單去重、亞種識別、分組數量與同頻率界線、跨區移動、收合／搜尋優先規則、舊資料遷移、聽到加減、GPS、真實助手解析自由描述及繁殖代碼。已做 390px／320px 瀏覽器驗證；實機背景 GPS、各種 IME 與需要個人 API key 的附近熱點仍須現場確認。

舊版個人資料中曾用寬鬆名稱合併的亞種，需要重新匯入個人 ZIP／CSV 才能依正式亞種對照重算；手動排序不會被自動清除。

顯示與輸出簡稱「野鴿」「花嘴」「東方」；「東方黃」專指東方黃鶺鴒(黃眉)，保留東方黃鶺鴒及紅尾伯勞各亞種的獨立分類。無紀錄區塊會移除失效排序項目，標示數與實際列數一致。排序頁採左右四欄，完成頁四個操作鈕在窄手機維持同一列。

個人 CSV 依官方已確認的 `reportAs` 對應，將 13 個亞種／馴化型的鳥單合併計入親種頻率，同一張鳥單只計一次；各亞種仍保留獨立頻率及輸入／輸出名稱。賽氏短趾百靈保持獨立分類。索引外分類也會保留及計算；舊個人快取需要重新匯入原 CSV，才能重算親種頻率，不能直接相加已有比例。
