# ]吉他谱
Song Index [ V · 原调 f · 最后更新
font-family: Arial, sans-serif;
/* ADJUST HERE: line spacing — top/bottom padding between song entries */
/* ADJUST HERE: entry font size — controls every song title's size */
/* ADJUST HERE: back-to-top button position, size, and colors */
A – E
F – J
var root = document.getElementById("ch-index-root");
var searchBox = root.querySelector(".ch-search");
var refreshBtn = root.querySelector(".ch-refresh");
var statusEl = root.querySelector(".ch-status");
var alphaNavEl = root.querySelector(".ch-alpha-nav");
var resultsEl = root.querySelector(".ch-results");
var backToTopBtn = root.querySelector(".ch-back-to-top");
// ---- THIS PAGE'S LETTER RANGE ----
// ADJUST HERE if you re-split the pages differently.
var RANGE_START = "V";
var RANGE_END = "Z";
var INCLUDE_HASH = true; // true only on the last page, to catch non-Latin/symbol titles
var PAGE_LABEL = "V – #";
// ADJUST HERE: how far down the page (in px) before the button appears
var BACK_TO_TOP_SHOW_AFTER = 400;
window.addEventListener("scroll", function () {
if (window.scrollY > BACK_TO_TOP_SHOW_AFTER) {
backToTopBtn.classList.add("ch-visible");
backToTopBtn.classList.remove("ch-visible");
backToTopBtn.addEventListener("click", function () {
var CACHE_KEY = "chIndexCacheV3";
var PAGE_SIZE = 150; // Blogger's feed page cap
// ---- EDIT THESE TWO LISTS to keep non-song posts out of the index ----
// Any post whose labels include one of these (case-insensitive) is skipped.
var EXCLUDE_LABELS = ["Not a Song", "Announcement", "Site Update", "blog", "gos", "lessons", "misc"];
// Any post whose TITLE matches one of these patterns is skipped, no label needed.
var EXCLUDE_TITLE_PATTERNS = [
function isExcluded(title, labels) {
var lowerLabels = labels.map(function (l) { return l.toLowerCase(); });
for (var i = 0; i 1 ? parts[0].trim().toLowerCase() : "";
statusEl.textContent = msg || "";
var url = "/feeds/posts/summary?alt=json&max-results=" + PAGE_SIZE + "&start-index=" + startIndex;
if (!res.ok) throw new Error("Feed request failed (" + res.status + ")");
function fetchAllPosts() {
var excludedCount = 0;
entries.forEach(function (entry) {
(entry.link || []).forEach(function (l) {
if (isExcluded(title, labels)) {
excludedCount++;
// Blogger can return a short page even when more posts remain.
console.log("[ch-index] page : raw +" + gotThisPage + " (raw total " + rawSeen + " / reported " + total + "), kept " + allPosts.length + ", excluded " + excludedCount);
return step(1 + PAGE_SIZE, total);
console.log("[ch-index] page @" + startIndex + ": raw +" + gotThisPage + " (raw total " + rawSeen + " / reported " + total + "), kept " + allPosts.length + ", excluded " + excludedCount);
return step(startIndex + PAGE_SIZE, total);
var cached = JSON.parse(sessionStorage.getItem(CACHE_KEY) || "null");
setStatus(rangeCount(allPosts) + " songs \u00B7 " + PAGE_LABEL + " \u00B7 cached this session (" + new Date(cached.time).toLocaleTimeString() + ")");
resultsEl.innerHTML = "";
fetchAllPosts()
var singerA = getSinger(a.title);
var singerB = getSinger(b.title);
if (singerA && singerA === singerB) {
return stripLeadingArticle(a.title).length - stripLeadingArticle(b.title).length;
return stripLeadingArticle(a.title).localeCompare(stripLeadingArticle(b.title));
sessionStorage.setItem(CACHE_KEY, JSON.stringify({ posts: allPosts, time: Date.now() }));
setStatus(rangeCount(allPosts) + " songs \u00B7 " + PAGE_LABEL);
setStatus("Couldn't load the song list (" + err.message + "). Try Refresh list.");
q = q.toLowerCase();
if (post.title.toLowerCase().indexOf(q) !== -1) return true;
return post.labels.some(function (l) { return l.toLowerCase().indexOf(q) !== -1; });
var t = stripLeadingArticle(title).trim();
var c = t.charAt(0);
if (/[A-Za-z]/.test(c)) return c.toUpperCase();
// Chinese character: convert to its pinyin initial (with surname-aware
var letter = (initials && initials[0] || "").charAt(0).toUpperCase();
if (/[A-Z]/.test(letter)) return letter;
// Does this post's group key fall on THIS split page?
if (key === "#") return INCLUDE_HASH;
return key >= RANGE_START && key No songs match \u201C' + q + '\u201D.';
renderAlpha(filtered);
function renderAlpha(posts) {
alphaNavEl.innerHTML = "";
resultsEl.innerHTML = 'No matches in this range (' + PAGE_LABEL + '). Try another page.';
inRange.forEach(function (p) {
return a.localeCompare(b);
alphaNavEl.innerHTML = keys.map(function (k) {
resultsEl.innerHTML = keys.map(function (k) {
searchBox.addEventListener("input", function () { render(); });
refreshBtn.addEventListener("click", function () { loadPosts(true); });