Guru Granth Sahib

/* SECTION 2/3: CSS */ #sggs-reader-widget.sggs { –bg: #ffffff; –panel: #f6f7f9; –panel2: #fafbfc; –text: #111827; –muted: #6b7280; –border: #e5e7eb; –shadow: 0 8px 24px rgba(0,0,0,0.10); –btn: #111827; –btnText: #ffffff; –focus: 0 0 0 3px rgba(59,130,246,0.35); –danger: #b91c1c; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, “Noto Sans”, sans-serif; } #sggs-reader-widget.sggs[data-theme=”dark”] { –bg: #0b1220; –panel: #0f1a2e; –panel2: #13213a; –text: #e5e7eb; –muted: #9ca3af; –border: #24324b; –shadow: 0 10px 28px rgba(0,0,0,0.35); –btn: #e5e7eb; –btnText: #0b1220; –danger: #fca5a5; } @media (prefers-color-scheme: dark) { #sggs-reader-widget.sggs[data-theme=”auto”] { –bg: #0b1220; –panel: #0f1a2e; –panel2: #13213a; –text: #e5e7eb; –muted: #9ca3af; –border: #24324b; –shadow: 0 10px 28px rgba(0,0,0,0.35); –btn: #e5e7eb; –btnText: #0b1220; –danger: #fca5a5; } } #sggs-reader-widget .sggs-shell { background: var(–bg); color: var(–text); border: 1px solid var(–border); border-radius: 16px; box-shadow: var(–shadow); overflow: hidden; max-width: 1120px; margin: 14px auto; } #sggs-reader-widget .sggs-header { display: flex; gap: 12px; justify-content: space-between; align-items: center; padding: 16px 16px 12px; border-bottom: 1px solid var(–border); background: linear-gradient(0deg, transparent, rgba(0,0,0,0.02)); } #sggs-reader-widget .sggs-h1 { font-weight: 700; font-size: 19px; line-height: 1.2; } #sggs-reader-widget .sggs-sub { font-size: 12px; color: var(–muted); margin-top: 3px; } #sggs-reader-widget .sggs-actions { display: flex; gap: 8px; flex-wrap: wrap; } #sggs-reader-widget .sggs-btn, #sggs-reader-widget .sggs-iconbtn, #sggs-reader-widget .sggs-result { border: 1px solid var(–border); background: var(–panel); color: var(–text); border-radius: 10px; padding: 10px 12px; font-size: 14px; cursor: pointer; min-height: 44px; } #sggs-reader-widget .sggs-btn-primary { background: var(–btn); color: var(–btnText); border-color: transparent; } #sggs-reader-widget .sggs-btn:focus, #sggs-reader-widget .sggs-iconbtn:focus, #sggs-reader-widget .sggs-input:focus, #sggs-reader-widget .sggs-result:focus, #sggs-reader-widget select:focus { outline: none; box-shadow: var(–focus); } #sggs-reader-widget .sggs-iconbtn { width: 44px; height: 44px; font-size: 22px; display: grid; place-items: center; padding: 0; } #sggs-reader-widget .sggs-label { font-size: 12px; color: var(–muted); display: block; margin-bottom: 6px; } #sggs-reader-widget .sggs-input { width: 100%; min-height: 44px; border: 1px solid var(–border); background: var(–bg); color: var(–text); border-radius: 10px; padding: 10px 11px; font-size: 14px; box-sizing: border-box; } #sggs-reader-widget .sggs-navrow { display: grid; grid-template-columns: 1fr; gap: 10px; padding: 12px; background: var(–panel); border-bottom: 1px solid var(–border); } @media (min-width: 760px) { #sggs-reader-widget .sggs-navrow { grid-template-columns: 1fr 1fr; } } #sggs-reader-widget .sggs-navitem { background: var(–bg); border: 1px solid var(–border); border-radius: 12px; padding: 10px; } #sggs-reader-widget .sggs-panel { display: none; padding: 12px; border-bottom: 1px solid var(–border); background: var(–panel2); } #sggs-reader-widget .sggs-panel.is-active { display: block; } #sggs-reader-widget .sggs-reader { padding: 12px; } #sggs-reader-widget .sggs-readerbar { display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px; gap: 10px; align-items: center; margin-bottom: 10px; } #sggs-reader-widget .sggs-loc { display: grid; grid-template-columns: auto 1fr auto; gap: 8px; align-items: center; } #sggs-reader-widget .sggs-meta { grid-column: 1 / -1; font-size: 12px; color: var(–muted); padding-left: 2px; margin-top: 6px; } #sggs-reader-widget .sggs-toggles { display: flex; gap: 12px; flex-wrap: wrap; padding: 10px; background: var(–panel); border: 1px solid var(–border); border-radius: 12px; margin-bottom: 10px; } #sggs-reader-widget .sggs-check { font-size: 13px; color: var(–text); display: inline-flex; gap: 8px; align-items: center; } #sggs-reader-widget .sggs-status { font-size: 13px; color: var(–muted); min-height: 18px; margin: 6px 2px 10px; } #sggs-reader-widget .sggs-status.is-error { color: var(–danger); } #sggs-reader-widget .sggs-content { border: 1px solid var(–border); background: var(–bg); border-radius: 14px; padding: 12px; min-height: 300px; max-height: 68vh; overflow: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; } #sggs-reader-widget .sggs-readerfooter { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; } #sggs-reader-widget .sggs-reading-header { position: sticky; top: -12px; z-index: 2; background: var(–bg); padding: 4px 2px 12px; border-bottom: 1px solid var(–border); margin-bottom: 10px; } #sggs-reader-widget .sggs-reading-title { font-size: 18px; font-weight: 700; } #sggs-reader-widget .sggs-reading-meta { font-size: 12px; color: var(–muted); margin-top: 4px; line-height: 1.45; } #sggs-reader-widget .sggs-line { padding: 12px 10px 14px; border-bottom: 1px solid var(–border); } #sggs-reader-widget .sggs-line:last-child { border-bottom: none; } #sggs-reader-widget .sggs-linehead { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; } #sggs-reader-widget .sggs-line-grid { display: grid; grid-template-columns: 1fr; gap: 12px; } @media (min-width: 880px) { #sggs-reader-widget .sggs-line-grid.dual { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } } #sggs-reader-widget .sggs-col { min-width: 0; } #sggs-reader-widget .sggs-col-label { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(–muted); margin-bottom: 6px; } #sggs-reader-widget .sggs-gur, #sggs-reader-widget .sggs-eng, #sggs-reader-widget .sggs-trn, #sggs-reader-widget .sggs-result-gur, #sggs-reader-widget .sggs-result-eng { white-space: pre-line; word-break: break-word; } #sggs-reader-widget .sggs-gur { font-size: 21px; line-height: 1.7; } #sggs-reader-widget .sggs-eng { font-size: 15px; line-height: 1.6; } #sggs-reader-widget .sggs-trn { font-size: 13px; color: var(–muted); line-height: 1.55; margin-top: 10px; } #sggs-reader-widget .sggs-minirow { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 8px; } #sggs-reader-widget .sggs-pill { display: inline-flex; gap: 8px; align-items: center; background: var(–panel); border: 1px solid var(–border); border-radius: 999px; padding: 6px 10px; font-size: 12px; color: var(–muted); } #sggs-reader-widget .sggs-grid { display: grid; grid-template-columns: 1fr; gap: 10px; } @media (min-width: 760px) { #sggs-reader-widget .sggs-grid.cols-2 { grid-template-columns: 1fr 1fr; } } #sggs-reader-widget .sggs-card { border: 1px solid var(–border); background: var(–bg); border-radius: 12px; padding: 12px; } #sggs-reader-widget .sggs-card h3 { margin: 0 0 6px 0; font-size: 14px; } #sggs-reader-widget .sggs-card p { margin: 0; font-size: 12px; color: var(–muted); line-height: 1.45; } #sggs-reader-widget .sggs-note { margin-top: 10px; padding: 10px 12px; border: 1px solid var(–border); border-radius: 12px; background: var(–panel); color: var(–muted); font-size: 12px; line-height: 1.5; } #sggs-reader-widget .sggs-results { display: grid; gap: 8px; } #sggs-reader-widget .sggs-result { text-align: left; width: 100%; background: var(–bg); } #sggs-reader-widget .sggs-result-title { font-size: 12px; color: var(–muted); margin-bottom: 6px; } #sggs-reader-widget .sggs-result-gur { font-size: 18px; line-height: 1.55; margin-bottom: 5px; } #sggs-reader-widget .sggs-result-eng { font-size: 14px; line-height: 1.45; } #sggs-reader-widget .sggs-bookmark { display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; padding: 10px 0; border-bottom: 1px solid var(–border); } #sggs-reader-widget .sggs-bookmark:last-child { border-bottom: none; } #sggs-reader-widget .sggs-bookmark-title { font-weight: 700; font-size: 14px; } #sggs-reader-widget .sggs-bookmark-meta { font-size: 12px; color: var(–muted); margin-top: 4px; } #sggs-reader-widget .sggs-empty { border: 1px dashed var(–border); border-radius: 12px; padding: 14px; color: var(–muted); font-size: 13px; background: var(–panel); } #sggs-reader-widget .sggs-footer { padding: 12px; border-top: 1px solid var(–border); font-size: 12px; color: var(–muted); background: var(–panel); text-align: center; } @media (max-width: 640px) { #sggs-reader-widget .sggs-header { align-items: flex-start; flex-direction: column; } #sggs-reader-widget .sggs-readerbar { grid-template-columns: 40px 1fr 40px; } #sggs-reader-widget .sggs-loc { grid-template-columns: 1fr; } #sggs-reader-widget .sggs-reading-header { position: static; top: auto; } #sggs-reader-widget .sggs-content { min-height: 260px; max-height: 60vh; padding: 10px; } #sggs-reader-widget .sggs-gur { font-size: 19px; line-height: 1.65; } #sggs-reader-widget .sggs-eng { font-size: 14px; line-height: 1.55; } #sggs-reader-widget .sggs-line { padding: 10px 8px 12px; } } /* SECTION 3/3: JAVASCRIPT */ (function () { ‘use strict’; var API_BASE = ‘https://api.sikher.com’; var DEFAULT_TRANSLATION = 13; var DEFAULT_TRANSLITERATION = 69; var MAX_PAGE = 1430; var IS_MOBILE = window.matchMedia(‘(max-width: 767px)’).matches; var SECTION_BATCH_SIZE = IS_MOBILE ? 3 : 8; var INITIAL_RENDER_COUNT = IS_MOBILE ? 35 : 140; var RENDER_INCREMENT = IS_MOBILE ? 25 : 120; var STORAGE_KEYS = { theme: ‘sggs_theme_v7’, bookmarks: ‘sggs_bookmarks_v7’, lastState: ‘sggs_last_state_v7’ }; var NAMED_READINGS = [ { key: ‘japji-sahib’, label: ‘Japji Sahib’, startPage: 1, endPage: 8, note: ‘Opening bani.’, patterns: [‘Jap Ji Sahib’, ‘Japji’] }, { key: ‘so-dar’, label: ‘So Dar’, startPage: 8, endPage: 12, note: ‘Evening bani reading.’, patterns: [‘So Dar’] }, { key: ‘so-purakh’, label: ‘So Purakh’, startPage: 10, endPage: 12, note: ‘Evening bani reading.’, patterns: [‘So Purakh’, ‘So Purkh’] }, { key: ‘kirtan-sohila’, label: ‘Kirtan Sohila’, startPage: 12, endPage: 13, note: ‘Night prayer reading.’, patterns: [‘Sohila’, ‘Kirtan Sohila’] } ]; var COMPLETE_SECTIONS = [ { key: ‘pre-raga-section’, label: ‘Pre-Raag Section’, startPage: 1, endPage: 13, note: ‘Opening section before the main raag arrangement.’ } ]; var RAAG_SECTIONS = [ { key: ‘sri-raga’, label: ‘Sri Raag’, startPage: 14, endPage: 93 }, { key: ‘majh’, label: ‘Majh’, startPage: 94, endPage: 150 }, { key: ‘gauri’, label: ‘Gauri’, startPage: 151, endPage: 346 }, { key: ‘asa’, label: ‘Asa’, startPage: 347, endPage: 488 }, { key: ‘gujari’, label: ‘Gujari’, startPage: 489, endPage: 526 }, { key: ‘devgandhari’, label: ‘Devgandhari’, startPage: 527, endPage: 536 }, { key: ‘bihagara’, label: ‘Bihagara’, startPage: 537, endPage: 556 }, { key: ‘wadhans’, label: ‘Wadhans’, startPage: 557, endPage: 594 }, { key: ‘sorath’, label: ‘Sorath’, startPage: 595, endPage: 659 }, { key: ‘dhanasari’, label: ‘Dhanasari’, startPage: 660, endPage: 695 }, { key: ‘jaitsari’, label: ‘Jaitsari’, startPage: 696, endPage: 710 }, { key: ‘todi’, label: ‘Todi’, startPage: 711, endPage: 718 }, { key: ‘bairari’, label: ‘Bairari’, startPage: 719, endPage: 720 }, { key: ’tilang’, label: ‘Tilang’, startPage: 721, endPage: 727 }, { key: ‘suhi’, label: ‘Suhi’, startPage: 728, endPage: 794 }, { key: ‘bilaval’, label: ‘Bilaval’, startPage: 795, endPage: 858 }, { key: ‘gaund’, label: ‘Gaund’, startPage: 859, endPage: 875 }, { key: ‘ramkali’, label: ‘Ramkali’, startPage: 876, endPage: 974 }, { key: ‘nat-narayan’, label: ‘Nat Narayan’, startPage: 975, endPage: 983 }, { key: ‘mali-gaura’, label: ‘Mali Gaura’, startPage: 984, endPage: 988 }, { key: ‘maru’, label: ‘Maru’, startPage: 989, endPage: 1106 }, { key: ‘tukhari’, label: ‘Tukhari’, startPage: 1107, endPage: 1117 }, { key: ‘kedara’, label: ‘Kedara’, startPage: 1118, endPage: 1124 }, { key: ‘bhairon’, label: ‘Bhairon’, startPage: 1125, endPage: 1167 }, { key: ‘basant’, label: ‘Basant’, startPage: 1168, endPage: 1196 }, { key: ‘sarang’, label: ‘Sarang’, startPage: 1197, endPage: 1253 }, { key: ‘malar’, label: ‘Malar’, startPage: 1254, endPage: 1293 }, { key: ‘kanara’, label: ‘Kanara’, startPage: 1294, endPage: 1318 }, { key: ‘kalyan’, label: ‘Kalyan’, startPage: 1319, endPage: 1326 }, { key: ‘prabhati’, label: ‘Prabhati’, startPage: 1327, endPage: 1351 }, { key: ‘jaijaiwanti’, label: ‘Jaijaiwanti’, startPage: 1352, endPage: 1353, patterns: [‘Jaijaiwanti’, ‘Jaijavanti’] } ]; var POST_RAAG_SECTIONS = [ { key: ‘saloks-in-sahaskrit’, label: ‘Saloks in Sahaskrit’, startPage: 1353, endPage: 1360, patterns: [‘Sahaskrit’, ‘Sahaskriti’] }, { key: ‘gatha’, label: ‘Gatha’, startPage: 1360, endPage: 1361, patterns: [‘Gatha’] }, { key: ‘phunahe’, label: ‘Phunahe’, startPage: 1361, endPage: 1363, patterns: [‘Phunahe’, ‘Funhe’] }, { key: ‘chaubole’, label: ‘Chaubole’, startPage: 1363, endPage: 1364, patterns: [‘Chaubole’] }, { key: ‘saloks-of-kabir’, label: ‘Saloks of Kabir’, startPage: 1364, endPage: 1377, patterns: [‘Kabir’] }, { key: ‘saloks-of-farid’, label: ‘Saloks of Sheikh Farid’, startPage: 1377, endPage: 1384, patterns: [‘Farid’] }, { key: ‘swayyas’, label: ‘Swayyas’, startPage: 1385, endPage: 1409, patterns: [‘Swayiye’, ‘Swayyas’, ‘Swaiyyas’] }, { key: ‘salok-varaan-te-wadhik’, label: ‘Salok Vaaran te Wadhik’, startPage: 1410, endPage: 1429, patterns: [‘Vaaran’, ‘Wadhik’] }, { key: ‘mundavani’, label: ‘Mundavani’, startPage: 1429, endPage: 1429, patterns: [‘Mundavani’] }, { key: ‘rag-mala’, label: ‘Rag Mala’, startPage: 1429, endPage: 1430, patterns: [‘Rag Mala’, ‘Ragmala’] } ]; var ALL_SECTION_ITEMS = NAMED_READINGS.concat(COMPLETE_SECTIONS, RAAG_SECTIONS, POST_RAAG_SECTIONS); var SECTION_MAP = {}; ALL_SECTION_ITEMS.forEach(function (item) { SECTION_MAP[item.key] = item; }); function start() { var widget = document.getElementById(‘sggs-reader-widget’); if (!widget || widget.dataset.bound === ‘1’) return; widget.dataset.bound = ‘1’; var state = { theme: widget.getAttribute(‘data-theme’) || ‘auto’, currentType: ‘page’, currentId: 1, currentLabel: ‘Ang 1’, currentMeta: ”, currentMode: ‘direct’, currentSectionKey: ”, allLines: [], renderCount: 0, sectionProgress: null, bookmarks: [] }; var els = { navSelect: byId(‘sggsNavSelect’), jumpSelect: byId(‘sggsJumpSelect’), themeBtn: byId(‘sggsThemeBtn’), addBookmarkBtn: byId(‘sggsAddBookmarkBtn’), fullSectionSelect: byId(‘sggsFullSectionSelect’), sectionNote: byId(‘sggsSectionNote’), readType: byId(‘sggsReadType’), readId: byId(‘sggsReadId’), loadReadingBtn: byId(‘sggsLoadReadingBtn’), randomBtn: byId(‘sggsRandomBtn’), prevBtn: byId(‘sggsPrevBtn’), nextBtn: byId(‘sggsNextBtn’), idInput: byId(‘sggsIdInput’), goBtn: byId(‘sggsGoBtn’), searchInput: byId(‘sggsSearchInput’), searchMode: byId(‘sggsSearchMode’), searchBtn: byId(‘sggsSearchBtn’), searchResults: byId(‘sggsSearchResults’), bookmarksList: byId(‘sggsBookmarksList’), showEnglish: byId(‘sggsShowEnglish’), showGurmukhi: byId(‘sggsShowGurmukhi’), showTranslit: byId(‘sggsShowTranslit’), status: byId(‘sggsStatus’), meta: byId(‘sggsMeta’), content: byId(‘sggsContent’), readerFooter: byId(‘sggsReaderFooter’) }; loadStorage(); applyTheme(state.theme); fillQuickJump(); fillSectionMenu(); bindEvents(); renderBookmarks(); syncInputs(); if (state.currentMode === ‘section’ && state.currentSectionKey && SECTION_MAP[state.currentSectionKey]) { beginSectionLoad(SECTION_MAP[state.currentSectionKey]); } else { loadReading(state.currentType, state.currentId); } function byId(id) { return document.getElementById(id); } function bindEvents() { if (els.navSelect) { els.navSelect.addEventListener(‘change’, function () { showPanel(els.navSelect.value); }); } if (els.jumpSelect) { els.jumpSelect.addEventListener(‘change’, function () { var raw = els.jumpSelect.value || ”; if (!raw) return; try { var parsed = JSON.parse(raw); if (parsed.mode === ‘section’ && parsed.sectionKey) { beginSectionLoad(SECTION_MAP[parsed.sectionKey]); } else if (parsed.action === ‘random’) { loadRandom(parsed.type); } else { loadReading(parsed.type || ‘page’, parsed.id || 1, parsed.label || null); } els.jumpSelect.value = ”; } catch (err) { setStatus(‘Quick jump option could not be read.’, true); } }); } if (els.themeBtn) { els.themeBtn.addEventListener(‘click’, function () { var order = [‘auto’, ‘dark’, ‘light’]; var next = order[(order.indexOf(state.theme) + 1) % order.length]; applyTheme(next); saveStorage(); }); } if (els.addBookmarkBtn) { els.addBookmarkBtn.addEventListener(‘click’, addBookmark); } if (els.fullSectionSelect) { els.fullSectionSelect.addEventListener(‘change’, function () { var key = els.fullSectionSelect.value || ”; if (!key || !SECTION_MAP[key]) return; beginSectionLoad(SECTION_MAP[key]); showPanel(‘browse’); }); } if (els.readType) { els.readType.addEventListener(‘change’, function () { syncInputsFromBrowse(); }); } if (els.loadReadingBtn) { els.loadReadingBtn.addEventListener(‘click’, function () { loadReading(els.readType.value, els.readId.value); }); } if (els.randomBtn) { els.randomBtn.addEventListener(‘click’, function () { var t = els.readType.value === ‘hymn’ ? 2 : 1; loadRandom(t); }); } if (els.searchBtn) { els.searchBtn.addEventListener(‘click’, performSearch); } if (els.searchInput) { els.searchInput.addEventListener(‘keydown’, function (ev) { if (ev.key === ‘Enter’) performSearch(); }); } if (els.prevBtn) { els.prevBtn.addEventListener(‘click’, function () { loadReading(‘page’, Math.max(1, Number(state.currentId || 1) – 1)); }); } if (els.nextBtn) { els.nextBtn.addEventListener(‘click’, function () { loadReading(‘page’, Math.min(MAX_PAGE, Number(state.currentId || 1) + 1)); }); } if (els.goBtn) { els.goBtn.addEventListener(‘click’, function () { loadReading(‘page’, els.idInput.value); }); } if (els.readerFooter) { els.readerFooter.addEventListener(‘click’, function (ev) { var btn = ev.target.closest(‘button’); if (!btn) return; if (btn.id === ‘sggsShowMoreBtn’) { state.renderCount = Math.min(state.allLines.length, state.renderCount + RENDER_INCREMENT); renderReading(); } if (btn.id === ‘sggsLoadNextSectionBtn’) { loadNextSectionChunk(); } if (btn.id === ‘sggsBackToTopBtn’) { els.content.scrollTop = 0; } }); } [els.showEnglish, els.showGurmukhi, els.showTranslit].forEach(function (el) { if (!el) return; el.addEventListener(‘change’, renderReading); }); } function showPanel(name) { [‘start’, ‘browse’, ‘search’, ‘bookmarks’].forEach(function (key) { var panel = byId(‘sggs-tab-‘ + key); if (panel) panel.classList.toggle(‘is-active’, key === name); }); } function fillQuickJump() { if (!els.jumpSelect) return; els.jumpSelect.innerHTML = [ ‘Choose…’, optionValue({ mode: ‘section’, sectionKey: ‘japji-sahib’ }, ‘Japji Sahib — Full Reading’), optionValue({ mode: ‘section’, sectionKey: ‘so-dar’ }, ‘So Dar — Full Reading’), optionValue({ mode: ‘section’, sectionKey: ‘so-purakh’ }, ‘So Purakh — Full Reading’), optionValue({ mode: ‘section’, sectionKey: ‘kirtan-sohila’ }, ‘Kirtan Sohila — Full Reading’), optionValue({ mode: ‘section’, sectionKey: ‘pre-raga-section’ }, ‘Pre-Raag Section — Full Reading’), optionValue({ mode: ‘section’, sectionKey: ‘sri-raga’ }, ‘Sri Raag — Full Reading’), optionValue({ mode: ‘section’, sectionKey: ‘prabhati’ }, ‘Prabhati — Full Reading’), optionValue({ mode: ‘section’, sectionKey: ‘rag-mala’ }, ‘Rag Mala — Full Reading’), optionValue({ action: ‘random’, type: 1 }, ‘Random Ang’), optionValue({ action: ‘random’, type: 2 }, ‘Random Hymn’) ].join(”); } function optionValue(data, label) { return ” + escapeHtml(label) + ”; } function fillSectionMenu() { if (!els.fullSectionSelect) return; var html = [‘Choose a named section…’]; html.push(”); NAMED_READINGS.forEach(function (item) { html.push(” + escapeHtml(item.label + ‘ — Angs ‘ + item.startPage + ‘–’ + item.endPage) + ”); }); html.push(”); html.push(”); COMPLETE_SECTIONS.forEach(function (item) { html.push(” + escapeHtml(item.label + ‘ — Angs ‘ + item.startPage + ‘–’ + item.endPage) + ”); }); html.push(”); html.push(”); RAAG_SECTIONS.forEach(function (item) { html.push(” + escapeHtml(item.label + ‘ — Angs ‘ + item.startPage + ‘–’ + item.endPage) + ”); }); html.push(”); html.push(”); POST_RAAG_SECTIONS.forEach(function (item) { html.push(” + escapeHtml(item.label + ‘ — Angs ‘ + item.startPage + ‘–’ + item.endPage) + ”); }); html.push(”); els.fullSectionSelect.innerHTML = html.join(”); } function setSectionNote(text) { if (els.sectionNote) els.sectionNote.textContent = text || ”; } function syncInputs() { if (els.readType) els.readType.value = state.currentType; if (els.readId) els.readId.value = state.currentId; if (els.idInput) els.idInput.value = state.currentId; updateReaderLabel(); } function syncInputsFromBrowse() { var type = els.readType ? els.readType.value : ‘page’; var rawId = els.readId ? els.readId.value : 1; rawId = type === ‘page’ ? clampInt(rawId, 1, MAX_PAGE) : Math.max(1, parseInt(rawId, 10) || 1); if (els.readId) els.readId.value = rawId; if (els.idInput) els.idInput.value = rawId; setMeta(formatTypeLabel(type) + ‘ ‘ + rawId); } function updateReaderLabel() { var labelEl = widget.querySelector(‘label[for=”sggsIdInput”]’); if (labelEl) labelEl.textContent = ‘Ang / Page ID’; } function formatTypeLabel(type) { if (type === ‘page’) return ‘Ang / Page’; if (type === ‘hymn’) return ‘Hymn’; return ‘Line’; } function formatReadingTitle(type, id) { if (type === ‘page’) return ‘Ang ‘ + id; if (type === ‘hymn’) return ‘Hymn ‘ + id; return ‘Line ‘ + id; } function applyTheme(theme) { state.theme = theme || ‘auto’; widget.setAttribute(‘data-theme’, state.theme); if (els.themeBtn) els.themeBtn.textContent = ‘Theme: ‘ + capitalize(state.theme); } function loadStorage() { try { var savedTheme = localStorage.getItem(STORAGE_KEYS.theme); var savedBookmarks = JSON.parse(localStorage.getItem(STORAGE_KEYS.bookmarks) || ‘[]’); var savedState = JSON.parse(localStorage.getItem(STORAGE_KEYS.lastState) || ‘{}’); if (savedTheme) state.theme = savedTheme; if (Array.isArray(savedBookmarks)) state.bookmarks = savedBookmarks; if (savedState && typeof savedState === ‘object’) { state.currentType = savedState.currentType || state.currentType; state.currentId = clampForType(savedState.currentType || state.currentType, savedState.currentId || state.currentId); state.currentMode = savedState.currentMode || state.currentMode; state.currentSectionKey = savedState.currentSectionKey || ”; } } catch (err) {} } function saveStorage() { try { localStorage.setItem(STORAGE_KEYS.theme, state.theme); localStorage.setItem(STORAGE_KEYS.bookmarks, JSON.stringify(state.bookmarks)); localStorage.setItem(STORAGE_KEYS.lastState, JSON.stringify({ currentType: state.currentType, currentId: state.currentId, currentMode: state.currentMode, currentSectionKey: state.currentSectionKey })); } catch (err) {} } function setStatus(text, isError) { if (!els.status) return; els.status.textContent = text || ”; els.status.classList.toggle(‘is-error’, !!isError); } function setMeta(text) { if (els.meta) els.meta.textContent = text || ”; } function fetchJson(path) { return fetch(API_BASE + path, { method: ‘GET’, headers: { ‘Accept’: ‘application/json’ }, credentials: ‘omit’ }).then(function (res) { if (!res.ok) throw new Error(‘HTTP ‘ + res.status); return res.json(); }); } function normalizeReadType(type) { return type === ‘hymn’ || type === ‘line’ ? type : ‘page’; } function clampForType(type, value) { var n = parseInt(value, 10); if (Number.isNaN(n) || n MAX_PAGE) n = MAX_PAGE; return n; } function clampInt(value, min, max) { var n = parseInt(value, 10); if (Number.isNaN(n)) n = min; if (n max) n = max; return n; } function normalizeName(str) { return String(str || ”).toLowerCase().replace(/raag/g, ”).replace(/[^a-z0-9]+/g, ”); } function capitalize(str) { str = String(str || ”); return str.charAt(0).toUpperCase() + str.slice(1); } function escapeHtml(str) { return String(str == null ? ” : str) .replace(/&/g, ‘&’) .replace(//g, ‘>’) .replace(/”/g, ‘"’) .replace(/’/g, ‘'’); } function normalizePayload(payload) { var rows = Array.isArray(payload) ? payload : (payload && typeof payload === ‘object’ ? [payload] : []); return rows.map(normalizeLine).filter(function (item) { return item.gurmukhi || item.english || item.translit; }); } function normalizeLine(item) { item = item || {}; return { id: item.id || ”, gurmukhi: item.text || ”, english: item.translation && item.translation.text ? item.translation.text : ”, translit: item.transliteration && item.transliteration.text ? item.transliteration.text : ”, page: item.page || ”, line: item.line || ”, hymn: item.hymn || ”, section: item.section || ”, melodyName: item.melody && item.melody.melody ? item.melody.melody : ”, authorName: item.author && item.author.author ? item.author.author : ” }; } function dedupeLines(items) { var seen = Object.create(null); return items.filter(function (item) { var key = [item.id || ”, item.page || ”, item.line || ”, item.hymn || ”, item.gurmukhi || ”].join(‘|’); if (seen[key]) return false; seen[key] = true; return true; }); } function filterBySection(items, patterns) { if (!patterns || !patterns.length) return items; var normalized = patterns.map(normalizeName); var filtered = items.filter(function (item) { var sectionName = normalizeName(item.section || ”); return normalized.some(function (pattern) { return sectionName.indexOf(pattern) !== -1; }); }); return filtered.length ? filtered : items; } function buildMeta() { var bits = []; var first = state.allLines[0] || {}; var last = state.allLines[state.allLines.length – 1] || {}; if (state.currentMode === ‘section’ && state.sectionProgress) { bits.push(‘Angs ‘ + state.sectionProgress.startPage + ‘–’ + state.sectionProgress.endPage); bits.push(‘Loaded through Ang ‘ + state.sectionProgress.loadedThrough); if (state.sectionProgress.done) bits.push(‘Complete section’); } else { bits.push(formatReadingTitle(state.currentType, state.currentId)); } bits.push(state.allLines.length + ‘ line’ + (state.allLines.length === 1 ? ” : ‘s’)); if (first.section) bits.push(first.section); if (first.melodyName) bits.push(first.melodyName); if (first.authorName) bits.push(first.authorName); if (first.page && last.page && first.page !== last.page) bits.push(‘Visible range spans Angs ‘ + first.page + ‘–’ + last.page); return bits.join(‘ • ‘); } function renderReaderFooter() { if (!els.readerFooter) return; var html = []; if (state.allLines.length > state.renderCount) { html.push(‘‘); } if (state.currentMode === ‘section’ && state.sectionProgress && !state.sectionProgress.done) { html.push(‘‘); } if (state.allLines.length) { html.push(‘‘); } els.readerFooter.innerHTML = html.join(”); } function renderReading() { state.currentMeta = buildMeta(); setMeta(state.currentMeta || ”); if (!els.content) return; if (!state.allLines.length) { if (state.currentMode === ‘section’ && state.sectionProgress) { els.content.innerHTML = ‘
Loading section…
‘; } else { els.content.innerHTML = ‘
No content returned yet.
‘; } renderReaderFooter(); return; } var visibleLines = state.allLines.slice(0, state.renderCount); var showEnglish = !!(els.showEnglish && els.showEnglish.checked); var showGurmukhi = !!(els.showGurmukhi && els.showGurmukhi.checked); var showTranslit = !!(els.showTranslit && els.showTranslit.checked); var html = [ ‘
‘, ‘
‘ + escapeHtml(state.currentLabel) + ‘
‘, state.currentMeta ? ‘
‘ + escapeHtml(state.currentMeta) + ‘
‘ : ”, ‘
‘ ]; visibleLines.forEach(function (line, idx) { var dual = showGurmukhi && showEnglish ? ‘ dual’ : ”; html.push(‘
‘); html.push(‘
‘); html.push(‘Ang ‘ + escapeHtml(String(line.page || ‘?’)) + ‘‘); if (line.hymn) html.push(‘Hymn ‘ + escapeHtml(String(line.hymn)) + ‘‘); if (line.line) html.push(‘Line ‘ + escapeHtml(String(line.line)) + ‘‘); if (line.section) html.push(‘‘ + escapeHtml(line.section) + ‘‘); if (line.melodyName) html.push(‘‘ + escapeHtml(line.melodyName) + ‘‘); if (line.authorName) html.push(‘‘ + escapeHtml(line.authorName) + ‘‘); html.push(‘
‘); if (!showGurmukhi && !showEnglish && !showTranslit) { if (idx === 0) html.push(‘
Turn on at least one display option above.
‘); html.push(‘
‘); return; } html.push(‘
‘); if (showGurmukhi) { html.push( ‘
‘ + ‘
Gurmukhi
‘ + ‘
‘ + escapeHtml(line.gurmukhi) + ‘
‘ + ‘
‘ ); } if (showEnglish) { html.push( ‘
‘ + ‘
English
‘ + ‘
‘ + escapeHtml(line.english || ‘—’) + ‘
‘ + ‘
‘ ); } html.push(‘
‘); if (showTranslit && line.translit) { html.push( ‘
‘ + ‘
Transliteration
‘ + ‘
‘ + escapeHtml(line.translit) + ‘
‘ + ‘
‘ ); } html.push(‘
‘); }); els.content.innerHTML = html.join(”); renderReaderFooter(); } function applyDirectReading(type, id, items, label) { state.currentType = type; state.currentId = id; state.currentLabel = label; state.currentMode = ‘direct’; state.currentSectionKey = ”; state.sectionProgress = null; state.allLines = dedupeLines(items); state.renderCount = state.allLines.length; if (els.fullSectionSelect) els.fullSectionSelect.value = ”; setSectionNote(‘Choose a section to load its full text into the reader window.’); syncInputs(); saveStorage(); renderReading(); setStatus(‘Loaded ‘ + label + ‘.’); } function buildReadingPath(type, id) { if (type === ‘page’) return ‘/page/’ + id + ‘/’ + DEFAULT_TRANSLATION + ‘/’ + DEFAULT_TRANSLITERATION; if (type === ‘hymn’) return ‘/hymn/’ + id + ‘/’ + DEFAULT_TRANSLATION + ‘/’ + DEFAULT_TRANSLITERATION; return ‘/’ + id + ‘/’ + DEFAULT_TRANSLATION + ‘/’ + DEFAULT_TRANSLITERATION; } function loadReading(type, id, customLabel) { type = normalizeReadType(type); id = clampForType(type, id); setStatus(‘Loading ‘ + (customLabel || formatReadingTitle(type, id)) + ‘…’); return fetchJson(buildReadingPath(type, id)).then(function (payload) { var items = dedupeLines(normalizePayload(payload)); if (!items.length) throw new Error(‘Empty reading’); applyDirectReading(type, id, items, customLabel || formatReadingTitle(type, id)); }).catch(function () { state.allLines = []; state.currentType = type; state.currentId = id; state.currentMode = ‘direct’; state.currentSectionKey = ”; state.sectionProgress = null; syncInputs(); renderReading(); setStatus(‘Could not load ‘ + formatReadingTitle(type, id) + ‘.’, true); }); } function beginSectionLoad(section) { if (!section) return; state.currentType = ‘page’; state.currentId = section.startPage; state.currentLabel = section.label; state.currentMode = ‘section’; state.currentSectionKey = section.key; state.allLines = []; state.renderCount = 0; state.sectionProgress = { key: section.key, startPage: section.startPage, endPage: section.endPage, nextPage: section.startPage, loadedThrough: section.startPage – 1, done: false, patterns: section.patterns || [] }; if (els.fullSectionSelect) els.fullSectionSelect.value = section.key; setSectionNote(section.note || (‘Loading ‘ + section.label + ‘ as a section reading.’)); syncInputs(); renderReading(); saveStorage(); loadNextSectionChunk(); } function loadNextSectionChunk() { if (!state.sectionProgress || state.sectionProgress.done) return Promise.resolve(); var startPage = state.sectionProgress.nextPage; var endPage = Math.min(state.sectionProgress.endPage, startPage + SECTION_BATCH_SIZE – 1); var requests = []; setStatus(‘Loading ‘ + state.currentLabel + ‘… Angs ‘ + startPage + ‘–’ + endPage); for (var p = startPage; p state.sectionProgress.endPage; if (oldRender === 0) { state.renderCount = Math.min(state.allLines.length, INITIAL_RENDER_COUNT); } else if (oldRender >= oldLength) { state.renderCount = Math.min(state.allLines.length, oldRender + newItems.length); } else { state.renderCount = oldRender; } saveStorage(); renderReading(); if (state.sectionProgress.done) { setStatus(‘Loaded complete section: ‘ + state.currentLabel + ‘.’); } else { setStatus(‘Loaded through Ang ‘ + endPage + ‘.’); } }).catch(function () { setStatus(‘Could not continue loading this section.’, true); }); } function performSearch() { var query = (els.searchInput && els.searchInput.value || ”).trim(); var mode = els.searchMode ? els.searchMode.value : ‘2’; if (!query) { if (els.searchResults) els.searchResults.innerHTML = ‘
Enter a search term.
‘; return; } setStatus(‘Searching GurbaniDB…’); fetchJson(buildSearchPath(query, mode)).then(function (payload) { var items = dedupeLines(normalizePayload(payload)); renderSearchResults(items, query); setStatus(‘Search complete.’); showPanel(‘search’); }).catch(function () { if (els.searchResults) els.searchResults.innerHTML = ‘
Search failed for this query.
‘; setStatus(‘Search failed.’, true); }); } function buildSearchPath(query, mode) { var q = encodeURIComponent(query); if (mode === ‘0’) return ‘/search/’ + q + ‘/’ + DEFAULT_TRANSLATION + ‘/’ + DEFAULT_TRANSLITERATION + ‘/0’; if (mode === ‘1’) return ‘/search/1/’ + q + ‘/’ + DEFAULT_TRANSLATION + ‘/’ + DEFAULT_TRANSLITERATION + ‘/0’; if (mode === ‘2’) return ‘/search/2/’ + q + ‘/’ + DEFAULT_TRANSLATION + ‘/’ + DEFAULT_TRANSLITERATION + ‘/0’; if (mode === ‘3’) return ‘/search/3/’ + q + ‘/’ + DEFAULT_TRANSLATION + ‘/0’; if (mode === ‘4’) return ‘/search/4/’ + q + ‘/’ + DEFAULT_TRANSLITERATION + ‘/0’; return ‘/search/2/’ + q + ‘/’ + DEFAULT_TRANSLATION + ‘/’ + DEFAULT_TRANSLITERATION + ‘/0’; } function renderSearchResults(items, query) { if (!els.searchResults) return; if (!items.length) { els.searchResults.innerHTML = ‘
No results found for “’ + escapeHtml(query) + ‘”.
‘; return; } els.searchResults.innerHTML = items.slice(0, 100).map(function (line, index) { return [ ‘‘ ].join(”); }).join(”); Array.prototype.forEach.call(els.searchResults.querySelectorAll(‘[data-result-index]’), function (btn) { btn.addEventListener(‘click’, function () { var line = items[Number(btn.getAttribute(‘data-result-index’))]; if (!line) return; if (line.hymn) loadReading(‘hymn’, line.hymn); else if (line.page) loadReading(‘page’, line.page); else if (line.id) loadReading(‘line’, line.id); }); }); } function loadRandom(type) { setStatus(type === 2 ? ‘Loading random hymn…’ : ‘Loading random ang…’); return fetchJson(‘/random/’ + type + ‘/’ + DEFAULT_TRANSLATION + ‘/’ + DEFAULT_TRANSLITERATION).then(function (payload) { var items = dedupeLines(normalizePayload(payload)); if (!items.length) throw new Error(‘No random reading returned’); if (type === 2) { var hymnId = items[0].hymn || 1; applyDirectReading(‘hymn’, hymnId, items, ‘Random Hymn ‘ + hymnId); } else { var pageId = items[0].page || 1; applyDirectReading(‘page’, pageId, items, ‘Random Ang ‘ + pageId); } }).catch(function () { setStatus(‘Random reading failed.’, true); }); } function addBookmark() { if (!state.allLines.length) { setStatus(‘Load a reading before adding a bookmark.’, true); return; } var bookmark = { id: Date.now(), mode: state.currentMode, type: state.currentType, readingId: state.currentId, label: state.currentLabel, meta: state.currentMeta, sectionKey: state.currentSectionKey }; state.bookmarks.unshift(bookmark); state.bookmarks = state.bookmarks.slice(0, 50); saveStorage(); renderBookmarks(); setStatus(‘Bookmark added.’); } function renderBookmarks() { if (!els.bookmarksList) return; if (!state.bookmarks.length) { els.bookmarksList.innerHTML = ‘
No bookmarks yet.
‘; return; } els.bookmarksList.innerHTML = state.bookmarks.map(function (item) { return [ ‘
‘, ‘
‘, ‘
‘ + escapeHtml(item.label || ‘Bookmark’) + ‘
‘, item.meta ? ‘
‘ + escapeHtml(item.meta) + ‘
‘ : ”, ‘
‘, ‘
‘, ‘‘, ‘‘, ‘
‘, ‘
‘ ].join(”); }).join(”); Array.prototype.forEach.call(els.bookmarksList.querySelectorAll(‘[data-open-bookmark]’), function (btn) { btn.addEventListener(‘click’, function () { var id = Number(btn.getAttribute(‘data-open-bookmark’)); var bookmark = state.bookmarks.find(function (b) { return b.id === id; }); if (!bookmark) return; if (bookmark.mode === ‘section’ && bookmark.sectionKey && SECTION_MAP[bookmark.sectionKey]) { beginSectionLoad(SECTION_MAP[bookmark.sectionKey]); } else { loadReading(bookmark.type, bookmark.readingId, bookmark.label); } showPanel(‘bookmarks’); }); }); Array.prototype.forEach.call(els.bookmarksList.querySelectorAll(‘[data-remove-bookmark]’), function (btn) { btn.addEventListener(‘click’, function () { var id = Number(btn.getAttribute(‘data-remove-bookmark’)); state.bookmarks = state.bookmarks.filter(function (b) { return b.id !== id; }); saveStorage(); renderBookmarks(); }); }); } } if (document.readyState === ‘loading’) { document.addEventListener(‘DOMContentLoaded’, start); } else { start(); } })();
Guru Granth Sahib Reader
Mobile-friendly full-text reader • named sections • no PHP
Menu Start Here Browse Sections Search Bookmarks
Quick Jump Choose…

Best mobile use

Open a named section, then use Load more lines and Load next pages as you read. This keeps the reader smooth on phones.

Named Banis Raag Sections Post-Raag Sections

What changed

The reader now uses smaller render batches, touch-friendly controls, and progressive section loading for better mobile performance.

Full scripture sections

Choose a named section Loading sections…
Choose a section to load its full text into the reader window.

Read directly

Reading type Ang / Page Hymn Line Number

Bookmarks

Ang / Page ID
English Gurmukhi Transliteration
Powered by GurbaniDB / The Sikher Project