@youversion/platform-react-ui 1.20.2 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/verse.d.ts.map +1 -1
- package/dist/index.cjs +302 -236
- package/dist/index.js +301 -236
- package/dist/lib/verse-html-utils.d.ts +0 -28
- package/dist/lib/verse-html-utils.d.ts.map +1 -1
- package/package.json +3 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verse.d.ts","sourceRoot":"","sources":["../../src/components/verse.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAY,MAAM,kCAAkC,CAAC;AAgBxE,OAAO,
|
|
1
|
+
{"version":3,"file":"verse.d.ts","sourceRoot":"","sources":["../../src/components/verse.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAY,MAAM,kCAAkC,CAAC;AAgBxE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAyBzD,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAEnD,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CAC/B,CAAC;AAyNF;;GAEG;AACH,KAAK,UAAU,GAAG;IAChB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,KAAK;IAChB;;;;;;;;OAQG;mCACwC,UAAU,KAAG,KAAK,CAAC,YAAY;;CA6E3E,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC,CAAC;CACnD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,+GA8FzB,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,7 @@ __export(index_exports, {
|
|
|
60
60
|
YouVersionProvider: () => import_platform_react_hooks8.YouVersionProvider,
|
|
61
61
|
YouVersionUserInfo: () => YouVersionUserInfo,
|
|
62
62
|
getAdjacentChapter: () => getAdjacentChapter,
|
|
63
|
+
transformBibleHtml: () => transformBibleHtml,
|
|
63
64
|
useYVAuth: () => import_platform_react_hooks8.useYVAuth
|
|
64
65
|
});
|
|
65
66
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -80,6 +81,247 @@ var import_react = require("react");
|
|
|
80
81
|
var import_react_use_controllable_state = require("@radix-ui/react-use-controllable-state");
|
|
81
82
|
var import_platform_react_hooks = require("@youversion/platform-react-hooks");
|
|
82
83
|
|
|
84
|
+
// ../core/dist/chunk-2Z2S2WY3.js
|
|
85
|
+
var NON_BREAKING_SPACE = "\xA0";
|
|
86
|
+
var FOOTNOTE_KEY_ATTR = "data-footnote-key";
|
|
87
|
+
var NEEDS_SPACE_BEFORE = /^[^\s.,;:!?)}\]'"'»›]/;
|
|
88
|
+
var ALLOWED_TAGS = /* @__PURE__ */ new Set([
|
|
89
|
+
"DIV",
|
|
90
|
+
"P",
|
|
91
|
+
"SPAN",
|
|
92
|
+
"SUP",
|
|
93
|
+
"SUB",
|
|
94
|
+
"EM",
|
|
95
|
+
"STRONG",
|
|
96
|
+
"I",
|
|
97
|
+
"B",
|
|
98
|
+
"SMALL",
|
|
99
|
+
"BR",
|
|
100
|
+
"SECTION",
|
|
101
|
+
"TABLE",
|
|
102
|
+
"THEAD",
|
|
103
|
+
"TBODY",
|
|
104
|
+
"TR",
|
|
105
|
+
"TD",
|
|
106
|
+
"TH"
|
|
107
|
+
]);
|
|
108
|
+
var DROP_ENTIRELY_TAGS = /* @__PURE__ */ new Set([
|
|
109
|
+
"SCRIPT",
|
|
110
|
+
"STYLE",
|
|
111
|
+
"IFRAME",
|
|
112
|
+
"OBJECT",
|
|
113
|
+
"EMBED",
|
|
114
|
+
"SVG",
|
|
115
|
+
"MATH",
|
|
116
|
+
"FORM",
|
|
117
|
+
"INPUT",
|
|
118
|
+
"BUTTON",
|
|
119
|
+
"TEXTAREA",
|
|
120
|
+
"SELECT",
|
|
121
|
+
"TEMPLATE",
|
|
122
|
+
"LINK",
|
|
123
|
+
"META",
|
|
124
|
+
"BASE",
|
|
125
|
+
"NOSCRIPT"
|
|
126
|
+
]);
|
|
127
|
+
var ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class", "v", "colspan", "rowspan", "dir", "usfm"]);
|
|
128
|
+
function sanitizeBibleHtmlDocument(doc) {
|
|
129
|
+
const root = doc.body ?? doc.documentElement;
|
|
130
|
+
for (const el of Array.from(root.querySelectorAll("*"))) {
|
|
131
|
+
const tag = el.tagName;
|
|
132
|
+
if (DROP_ENTIRELY_TAGS.has(tag)) {
|
|
133
|
+
el.remove();
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (!ALLOWED_TAGS.has(tag)) {
|
|
137
|
+
el.replaceWith(...Array.from(el.childNodes));
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
for (const attr of Array.from(el.attributes)) {
|
|
141
|
+
const name = attr.name.toLowerCase();
|
|
142
|
+
if (name.startsWith("on")) {
|
|
143
|
+
el.removeAttribute(attr.name);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (!ALLOWED_ATTRS.has(name) && !name.startsWith("data-")) {
|
|
147
|
+
el.removeAttribute(attr.name);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function wrapVerseContent(doc) {
|
|
153
|
+
function wrapParagraphContent(doc2, paragraph, verseNum) {
|
|
154
|
+
const children = Array.from(paragraph.childNodes);
|
|
155
|
+
if (children.length === 0) return;
|
|
156
|
+
const wrapper = doc2.createElement("span");
|
|
157
|
+
wrapper.className = "yv-v";
|
|
158
|
+
wrapper.setAttribute("v", verseNum);
|
|
159
|
+
const firstChild = children[0];
|
|
160
|
+
if (firstChild) {
|
|
161
|
+
paragraph.insertBefore(wrapper, firstChild);
|
|
162
|
+
}
|
|
163
|
+
children.forEach((child) => {
|
|
164
|
+
wrapper.appendChild(child);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function wrapParagraphsUntilBoundary(doc2, verseNum, startParagraph, endParagraph) {
|
|
168
|
+
if (!startParagraph) return;
|
|
169
|
+
let currentParagraph = startParagraph.nextElementSibling;
|
|
170
|
+
while (currentParagraph && currentParagraph !== endParagraph) {
|
|
171
|
+
const isHeading = currentParagraph.classList.contains("yv-h") || currentParagraph.matches(
|
|
172
|
+
".s1, .s2, .s3, .s4, .ms, .ms1, .ms2, .ms3, .ms4, .mr, .sp, .sr, .qa, .r"
|
|
173
|
+
);
|
|
174
|
+
if (isHeading) {
|
|
175
|
+
currentParagraph = currentParagraph.nextElementSibling;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (currentParagraph.querySelector(".yv-v[v]")) break;
|
|
179
|
+
if (currentParagraph.classList.contains("p") || currentParagraph.tagName === "P") {
|
|
180
|
+
wrapParagraphContent(doc2, currentParagraph, verseNum);
|
|
181
|
+
}
|
|
182
|
+
currentParagraph = currentParagraph.nextElementSibling;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function handleParagraphWrapping(doc2, currentParagraph, nextParagraph, verseNum) {
|
|
186
|
+
if (!currentParagraph) return;
|
|
187
|
+
if (!nextParagraph) {
|
|
188
|
+
wrapParagraphsUntilBoundary(doc2, verseNum, currentParagraph);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (currentParagraph !== nextParagraph) {
|
|
192
|
+
wrapParagraphsUntilBoundary(doc2, verseNum, currentParagraph, nextParagraph);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function processVerseMarker(marker, index, markers) {
|
|
196
|
+
const verseNum = marker.getAttribute("v");
|
|
197
|
+
if (!verseNum) return;
|
|
198
|
+
const nextMarker = markers[index + 1];
|
|
199
|
+
const nodesToWrap = collectNodesBetweenMarkers(marker, nextMarker);
|
|
200
|
+
if (nodesToWrap.length === 0) return;
|
|
201
|
+
const currentParagraph = marker.closest(".p, p, div.p");
|
|
202
|
+
const nextParagraph = nextMarker?.closest(".p, p, div.p") || null;
|
|
203
|
+
const doc2 = marker.ownerDocument;
|
|
204
|
+
wrapNodesInVerse(marker, verseNum, nodesToWrap);
|
|
205
|
+
handleParagraphWrapping(doc2, currentParagraph, nextParagraph, verseNum);
|
|
206
|
+
}
|
|
207
|
+
function wrapNodesInVerse(marker, verseNum, nodes) {
|
|
208
|
+
const wrapper = marker.ownerDocument.createElement("span");
|
|
209
|
+
wrapper.className = "yv-v";
|
|
210
|
+
wrapper.setAttribute("v", verseNum);
|
|
211
|
+
const firstNode = nodes[0];
|
|
212
|
+
if (firstNode) {
|
|
213
|
+
marker.parentNode?.insertBefore(wrapper, firstNode);
|
|
214
|
+
}
|
|
215
|
+
nodes.forEach((node) => {
|
|
216
|
+
wrapper.appendChild(node);
|
|
217
|
+
});
|
|
218
|
+
marker.remove();
|
|
219
|
+
}
|
|
220
|
+
function shouldStopCollecting(node, endMarker) {
|
|
221
|
+
if (node === endMarker) return true;
|
|
222
|
+
if (endMarker && node.nodeType === 1 && node.contains(endMarker)) return true;
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
function shouldSkipNode(node) {
|
|
226
|
+
return node.nodeType === 1 && node.classList.contains("yv-h");
|
|
227
|
+
}
|
|
228
|
+
function collectNodesBetweenMarkers(startMarker, endMarker) {
|
|
229
|
+
const nodes = [];
|
|
230
|
+
let current = startMarker.nextSibling;
|
|
231
|
+
while (current && !shouldStopCollecting(current, endMarker)) {
|
|
232
|
+
if (shouldSkipNode(current)) {
|
|
233
|
+
current = current.nextSibling;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
nodes.push(current);
|
|
237
|
+
current = current.nextSibling;
|
|
238
|
+
}
|
|
239
|
+
return nodes;
|
|
240
|
+
}
|
|
241
|
+
const verseMarkers = Array.from(doc.querySelectorAll(".yv-v[v]"));
|
|
242
|
+
verseMarkers.forEach(processVerseMarker);
|
|
243
|
+
}
|
|
244
|
+
function assignFootnoteKeys(doc) {
|
|
245
|
+
let introIdx = 0;
|
|
246
|
+
doc.querySelectorAll(".yv-n.f").forEach((fn) => {
|
|
247
|
+
const verseNum = fn.closest(".yv-v[v]")?.getAttribute("v");
|
|
248
|
+
fn.setAttribute(FOOTNOTE_KEY_ATTR, verseNum ?? `intro-${introIdx++}`);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function replaceFootnotesWithAnchors(doc, footnotes) {
|
|
252
|
+
for (const fn of footnotes) {
|
|
253
|
+
const key = fn.getAttribute(FOOTNOTE_KEY_ATTR);
|
|
254
|
+
if (!key) continue;
|
|
255
|
+
const prev = fn.previousSibling;
|
|
256
|
+
const next = fn.nextSibling;
|
|
257
|
+
const prevText = prev?.textContent ?? "";
|
|
258
|
+
const nextText = next?.textContent ?? "";
|
|
259
|
+
const prevNeedsSpace = prevText.length > 0 && !/\s$/.test(prevText);
|
|
260
|
+
const nextNeedsSpace = nextText.length > 0 && NEEDS_SPACE_BEFORE.test(nextText);
|
|
261
|
+
if (prevNeedsSpace && nextNeedsSpace && fn.parentNode) {
|
|
262
|
+
fn.parentNode.insertBefore(doc.createTextNode(" "), fn);
|
|
263
|
+
}
|
|
264
|
+
const anchor = doc.createElement("span");
|
|
265
|
+
anchor.setAttribute("data-verse-footnote", key);
|
|
266
|
+
anchor.setAttribute("data-verse-footnote-content", fn.innerHTML);
|
|
267
|
+
fn.replaceWith(anchor);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function addNbspToVerseLabels(doc) {
|
|
271
|
+
doc.querySelectorAll(".yv-vlbl").forEach((label) => {
|
|
272
|
+
const text = label.textContent || "";
|
|
273
|
+
if (!text.endsWith(NON_BREAKING_SPACE)) {
|
|
274
|
+
label.textContent = text + NON_BREAKING_SPACE;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function fixIrregularTables(doc) {
|
|
279
|
+
doc.querySelectorAll("table").forEach((table) => {
|
|
280
|
+
const rows = table.querySelectorAll("tr");
|
|
281
|
+
if (rows.length === 0) return;
|
|
282
|
+
let maxColumns = 0;
|
|
283
|
+
rows.forEach((row) => {
|
|
284
|
+
let count = 0;
|
|
285
|
+
row.querySelectorAll("td, th").forEach((cell) => {
|
|
286
|
+
count += parseInt(cell.getAttribute("colspan") || "1", 10);
|
|
287
|
+
});
|
|
288
|
+
maxColumns = Math.max(maxColumns, count);
|
|
289
|
+
});
|
|
290
|
+
if (maxColumns > 1) {
|
|
291
|
+
rows.forEach((row) => {
|
|
292
|
+
const cells = row.querySelectorAll("td, th");
|
|
293
|
+
if (cells.length === 1) {
|
|
294
|
+
const existing = parseInt(cells[0].getAttribute("colspan") || "1", 10);
|
|
295
|
+
if (existing < maxColumns) {
|
|
296
|
+
cells[0].setAttribute("colspan", maxColumns.toString());
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
function transformBibleHtml(html, options) {
|
|
304
|
+
const doc = options.parseHtml(html);
|
|
305
|
+
sanitizeBibleHtmlDocument(doc);
|
|
306
|
+
wrapVerseContent(doc);
|
|
307
|
+
assignFootnoteKeys(doc);
|
|
308
|
+
const footnotes = Array.from(doc.querySelectorAll(".yv-n.f"));
|
|
309
|
+
replaceFootnotesWithAnchors(doc, footnotes);
|
|
310
|
+
addNbspToVerseLabels(doc);
|
|
311
|
+
fixIrregularTables(doc);
|
|
312
|
+
const transformedHtml = options.serializeHtml(doc);
|
|
313
|
+
return { html: transformedHtml };
|
|
314
|
+
}
|
|
315
|
+
function transformBibleHtmlForBrowser(html) {
|
|
316
|
+
if (typeof globalThis.DOMParser === "undefined") {
|
|
317
|
+
throw new Error("DOMParser is required to transform Bible HTML in browser environments");
|
|
318
|
+
}
|
|
319
|
+
return transformBibleHtml(html, {
|
|
320
|
+
parseHtml: (h) => new DOMParser().parseFromString(h, "text/html"),
|
|
321
|
+
serializeHtml: (doc) => doc.body.innerHTML
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
83
325
|
// ../../node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/external.js
|
|
84
326
|
var external_exports = {};
|
|
85
327
|
__export(external_exports, {
|
|
@@ -13302,12 +13544,18 @@ var BibleClient = (_a = class {
|
|
|
13302
13544
|
/**
|
|
13303
13545
|
* Fetches a passage (range of verses) from the Bible using the passages endpoint.
|
|
13304
13546
|
* This is the new API format that returns HTML-formatted content.
|
|
13547
|
+
*
|
|
13548
|
+
* Note: The HTML returned from the API contains inline footnote content that should
|
|
13549
|
+
* be transformed before rendering. Use `transformBibleHtml()` or
|
|
13550
|
+
* `transformBibleHtmlForBrowser()` to clean up the HTML and extract footnotes.
|
|
13551
|
+
*
|
|
13305
13552
|
* @param versionId The version ID.
|
|
13306
13553
|
* @param usfm The USFM reference (e.g., "JHN.3.1-2", "GEN.1", "JHN.3.16").
|
|
13307
13554
|
* @param format The format to return ("html" or "text", default: "html").
|
|
13308
13555
|
* @param include_headings Whether to include headings in the content.
|
|
13309
13556
|
* @param include_notes Whether to include notes in the content.
|
|
13310
13557
|
* @returns The requested BiblePassage object with HTML content.
|
|
13558
|
+
*
|
|
13311
13559
|
* @example
|
|
13312
13560
|
* ```ts
|
|
13313
13561
|
* // Get a single verse
|
|
@@ -13318,6 +13566,10 @@ var BibleClient = (_a = class {
|
|
|
13318
13566
|
*
|
|
13319
13567
|
* // Get an entire chapter
|
|
13320
13568
|
* const chapter = await bibleClient.getPassage(3034, "GEN.1");
|
|
13569
|
+
*
|
|
13570
|
+
* // Transform HTML before rendering
|
|
13571
|
+
* const passage = await bibleClient.getPassage(3034, "JHN.3.16", "html", true, true);
|
|
13572
|
+
* const transformed = transformBibleHtmlForBrowser(passage.content);
|
|
13321
13573
|
* ```
|
|
13322
13574
|
*/
|
|
13323
13575
|
async getPassage(versionId, usfm, format = "html", include_headings, include_notes) {
|
|
@@ -15732,8 +15984,11 @@ var Footnote = (props) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
|
15732
15984
|
);
|
|
15733
15985
|
|
|
15734
15986
|
// src/lib/verse-html-utils.ts
|
|
15735
|
-
var
|
|
15736
|
-
var
|
|
15987
|
+
var INTER_FONT = '"Inter", sans-serif';
|
|
15988
|
+
var SOURCE_SERIF_FONT = '"Source Serif 4", serif';
|
|
15989
|
+
|
|
15990
|
+
// src/components/verse.tsx
|
|
15991
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
15737
15992
|
var LETTERS = "abcdefghijklmnopqrstuvwxyz";
|
|
15738
15993
|
function getFootnoteMarker(index) {
|
|
15739
15994
|
const base = LETTERS.length;
|
|
@@ -15746,238 +16001,34 @@ function getFootnoteMarker(index) {
|
|
|
15746
16001
|
} while (value >= 0);
|
|
15747
16002
|
return marker;
|
|
15748
16003
|
}
|
|
15749
|
-
|
|
15750
|
-
|
|
15751
|
-
|
|
15752
|
-
function wrapParagraphContent(doc2, paragraph, verseNum) {
|
|
15753
|
-
const children = Array.from(paragraph.childNodes);
|
|
15754
|
-
if (children.length === 0) return;
|
|
15755
|
-
const wrapper = doc2.createElement("span");
|
|
15756
|
-
wrapper.className = "yv-v";
|
|
15757
|
-
wrapper.setAttribute("v", verseNum);
|
|
15758
|
-
const firstChild = children[0];
|
|
15759
|
-
if (firstChild) {
|
|
15760
|
-
paragraph.insertBefore(wrapper, firstChild);
|
|
15761
|
-
}
|
|
15762
|
-
children.forEach((child) => {
|
|
15763
|
-
wrapper.appendChild(child);
|
|
15764
|
-
});
|
|
15765
|
-
}
|
|
15766
|
-
function wrapParagraphsUntilBoundary(doc2, verseNum, startParagraph, endParagraph) {
|
|
15767
|
-
if (!startParagraph) return;
|
|
15768
|
-
let currentP = startParagraph.nextElementSibling;
|
|
15769
|
-
while (currentP && currentP !== endParagraph) {
|
|
15770
|
-
const isHeading = currentP.classList.contains("yv-h") || currentP.matches(".s1, .s2, .s3, .s4, .ms, .ms1, .ms2, .ms3, .ms4, .mr, .sp, .sr, .qa, .r");
|
|
15771
|
-
if (isHeading) {
|
|
15772
|
-
currentP = currentP.nextElementSibling;
|
|
15773
|
-
continue;
|
|
15774
|
-
}
|
|
15775
|
-
if (currentP.querySelector(".yv-v[v]")) break;
|
|
15776
|
-
if (currentP.classList.contains("p") || currentP.tagName === "P") {
|
|
15777
|
-
wrapParagraphContent(doc2, currentP, verseNum);
|
|
15778
|
-
}
|
|
15779
|
-
currentP = currentP.nextElementSibling;
|
|
15780
|
-
}
|
|
15781
|
-
}
|
|
15782
|
-
function handleParagraphWrapping(doc2, currentParagraph, nextParagraph, verseNum) {
|
|
15783
|
-
if (!currentParagraph) return;
|
|
15784
|
-
if (!nextParagraph) {
|
|
15785
|
-
wrapParagraphsUntilBoundary(doc2, verseNum, currentParagraph);
|
|
15786
|
-
return;
|
|
15787
|
-
}
|
|
15788
|
-
if (currentParagraph !== nextParagraph) {
|
|
15789
|
-
wrapParagraphsUntilBoundary(doc2, verseNum, currentParagraph, nextParagraph);
|
|
15790
|
-
}
|
|
15791
|
-
}
|
|
15792
|
-
function processVerseMarker(marker, index, markers) {
|
|
15793
|
-
const verseNum = marker.getAttribute("v");
|
|
15794
|
-
if (!verseNum) return;
|
|
15795
|
-
const nextMarker = markers[index + 1];
|
|
15796
|
-
const nodesToWrap = collectNodesBetweenMarkers(marker, nextMarker);
|
|
15797
|
-
if (nodesToWrap.length === 0) return;
|
|
15798
|
-
const currentParagraph = marker.closest(".p, p, div.p");
|
|
15799
|
-
const nextParagraph = nextMarker?.closest(".p, p, div.p") || null;
|
|
15800
|
-
const doc2 = marker.ownerDocument;
|
|
15801
|
-
wrapNodesInVerse(marker, verseNum, nodesToWrap);
|
|
15802
|
-
handleParagraphWrapping(doc2, currentParagraph, nextParagraph, verseNum);
|
|
15803
|
-
}
|
|
15804
|
-
function wrapNodesInVerse(marker, verseNum, nodes) {
|
|
15805
|
-
const wrapper = marker.ownerDocument.createElement("span");
|
|
15806
|
-
wrapper.className = "yv-v";
|
|
15807
|
-
wrapper.setAttribute("v", verseNum);
|
|
15808
|
-
const firstNode = nodes[0];
|
|
15809
|
-
if (firstNode) {
|
|
15810
|
-
marker.parentNode?.insertBefore(wrapper, firstNode);
|
|
15811
|
-
}
|
|
15812
|
-
nodes.forEach((node) => {
|
|
15813
|
-
wrapper.appendChild(node);
|
|
15814
|
-
});
|
|
15815
|
-
marker.remove();
|
|
15816
|
-
}
|
|
15817
|
-
function shouldStopCollecting(node, endMarker) {
|
|
15818
|
-
if (node === endMarker) return true;
|
|
15819
|
-
if (endMarker && node instanceof Element && node.contains(endMarker)) return true;
|
|
15820
|
-
return false;
|
|
15821
|
-
}
|
|
15822
|
-
function shouldSkipNode(node) {
|
|
15823
|
-
return node instanceof Element && node.classList.contains("yv-h");
|
|
15824
|
-
}
|
|
15825
|
-
function collectNodesBetweenMarkers(startMarker, endMarker) {
|
|
15826
|
-
const nodes = [];
|
|
15827
|
-
let current = startMarker.nextSibling;
|
|
15828
|
-
while (current && !shouldStopCollecting(current, endMarker)) {
|
|
15829
|
-
if (shouldSkipNode(current)) {
|
|
15830
|
-
current = current.nextSibling;
|
|
15831
|
-
continue;
|
|
15832
|
-
}
|
|
15833
|
-
nodes.push(current);
|
|
15834
|
-
current = current.nextSibling;
|
|
15835
|
-
}
|
|
15836
|
-
return nodes;
|
|
15837
|
-
}
|
|
15838
|
-
const verseMarkers = Array.from(doc.querySelectorAll(".yv-v[v]"));
|
|
15839
|
-
verseMarkers.forEach(processVerseMarker);
|
|
15840
|
-
}
|
|
15841
|
-
var NEEDS_SPACE_BEFORE = /^[^\s.,;:!?)}\]'"»›]/;
|
|
15842
|
-
function buildVerseHtml(wrappers) {
|
|
16004
|
+
function getVerseHtmlFromDom(container, verseNum) {
|
|
16005
|
+
const wrappers = container.querySelectorAll(`.yv-v[v="${verseNum}"]`);
|
|
16006
|
+
if (!wrappers.length) return "";
|
|
15843
16007
|
const parts = [];
|
|
15844
16008
|
let noteIdx = 0;
|
|
15845
|
-
|
|
16009
|
+
wrappers.forEach((wrapper, i) => {
|
|
15846
16010
|
if (i > 0) parts.push(" ");
|
|
15847
|
-
const clone2 =
|
|
15848
|
-
const ownerDoc = wrappers[i].ownerDocument;
|
|
16011
|
+
const clone2 = wrapper.cloneNode(true);
|
|
15849
16012
|
clone2.querySelectorAll(".yv-h, .yv-vlbl").forEach((el) => el.remove());
|
|
15850
|
-
clone2.querySelectorAll("
|
|
15851
|
-
const
|
|
15852
|
-
|
|
15853
|
-
|
|
15854
|
-
|
|
16013
|
+
clone2.querySelectorAll("[data-verse-footnote]").forEach((anchor) => {
|
|
16014
|
+
const sup = wrapper.ownerDocument.createElement("sup");
|
|
16015
|
+
sup.className = "yv:text-muted-foreground";
|
|
16016
|
+
sup.textContent = getFootnoteMarker(noteIdx++);
|
|
16017
|
+
anchor.replaceWith(sup);
|
|
15855
16018
|
});
|
|
15856
16019
|
parts.push(clone2.innerHTML);
|
|
15857
|
-
}
|
|
15858
|
-
return parts.join("");
|
|
15859
|
-
}
|
|
15860
|
-
var FOOTNOTE_KEY_ATTR = "data-footnote-key";
|
|
15861
|
-
function assignFootnoteKeys(doc) {
|
|
15862
|
-
let introIdx = 0;
|
|
15863
|
-
doc.querySelectorAll(".yv-n.f").forEach((fn) => {
|
|
15864
|
-
const verseNum = fn.closest(".yv-v[v]")?.getAttribute("v");
|
|
15865
|
-
fn.setAttribute(FOOTNOTE_KEY_ATTR, verseNum ?? `intro-${introIdx++}`);
|
|
15866
16020
|
});
|
|
16021
|
+
return parts.join("");
|
|
15867
16022
|
}
|
|
15868
|
-
function replaceFootnotesWithAnchors(doc, footnotes) {
|
|
15869
|
-
for (const fn of footnotes) {
|
|
15870
|
-
const key = fn.getAttribute(FOOTNOTE_KEY_ATTR);
|
|
15871
|
-
const prev = fn.previousSibling;
|
|
15872
|
-
const next = fn.nextSibling;
|
|
15873
|
-
const prevText = prev?.textContent ?? "";
|
|
15874
|
-
const nextText = next?.textContent ?? "";
|
|
15875
|
-
const prevNeedsSpace = prevText.length > 0 && !/\s$/.test(prevText);
|
|
15876
|
-
const nextNeedsSpace = nextText.length > 0 && NEEDS_SPACE_BEFORE.test(nextText);
|
|
15877
|
-
if (prevNeedsSpace && nextNeedsSpace && fn.parentNode) {
|
|
15878
|
-
fn.parentNode.insertBefore(doc.createTextNode(" "), fn);
|
|
15879
|
-
}
|
|
15880
|
-
const anchor = doc.createElement("span");
|
|
15881
|
-
anchor.setAttribute("data-verse-footnote", key);
|
|
15882
|
-
fn.replaceWith(anchor);
|
|
15883
|
-
}
|
|
15884
|
-
}
|
|
15885
|
-
function extractNotesFromWrappedHtml(doc) {
|
|
15886
|
-
const footnotes = Array.from(doc.querySelectorAll(".yv-n.f"));
|
|
15887
|
-
if (!footnotes.length) return {};
|
|
15888
|
-
const footnotesByKey = /* @__PURE__ */ new Map();
|
|
15889
|
-
for (const fn of footnotes) {
|
|
15890
|
-
const key = fn.getAttribute(FOOTNOTE_KEY_ATTR);
|
|
15891
|
-
let arr = footnotesByKey.get(key);
|
|
15892
|
-
if (!arr) {
|
|
15893
|
-
arr = [];
|
|
15894
|
-
footnotesByKey.set(key, arr);
|
|
15895
|
-
}
|
|
15896
|
-
arr.push(fn);
|
|
15897
|
-
}
|
|
15898
|
-
const wrappersByVerse = /* @__PURE__ */ new Map();
|
|
15899
|
-
doc.querySelectorAll(".yv-v[v]").forEach((el) => {
|
|
15900
|
-
const verseNum = el.getAttribute("v");
|
|
15901
|
-
if (!verseNum) return;
|
|
15902
|
-
const arr = wrappersByVerse.get(verseNum);
|
|
15903
|
-
if (arr) arr.push(el);
|
|
15904
|
-
else wrappersByVerse.set(verseNum, [el]);
|
|
15905
|
-
});
|
|
15906
|
-
const notes = {};
|
|
15907
|
-
for (const [key, fns] of footnotesByKey) {
|
|
15908
|
-
const wrappers = wrappersByVerse.get(key);
|
|
15909
|
-
notes[key] = {
|
|
15910
|
-
verseHtml: wrappers ? buildVerseHtml(wrappers) : "",
|
|
15911
|
-
notes: fns.map((fn) => fn.innerHTML),
|
|
15912
|
-
hasVerseContext: !!wrappers
|
|
15913
|
-
};
|
|
15914
|
-
}
|
|
15915
|
-
replaceFootnotesWithAnchors(doc, footnotes);
|
|
15916
|
-
return notes;
|
|
15917
|
-
}
|
|
15918
|
-
function addNbspToVerseLabels(doc) {
|
|
15919
|
-
doc.querySelectorAll(".yv-vlbl").forEach((label) => {
|
|
15920
|
-
const text = label.textContent || "";
|
|
15921
|
-
if (!text.endsWith(NON_BREAKING_SPACE)) {
|
|
15922
|
-
label.textContent = text + NON_BREAKING_SPACE;
|
|
15923
|
-
}
|
|
15924
|
-
});
|
|
15925
|
-
}
|
|
15926
|
-
function fixIrregularTables(doc) {
|
|
15927
|
-
doc.querySelectorAll("table").forEach((table) => {
|
|
15928
|
-
const rows = table.querySelectorAll("tr");
|
|
15929
|
-
if (rows.length === 0) return;
|
|
15930
|
-
let maxColumns = 0;
|
|
15931
|
-
rows.forEach((row) => {
|
|
15932
|
-
let count = 0;
|
|
15933
|
-
row.querySelectorAll("td, th").forEach((cell) => {
|
|
15934
|
-
count += cell instanceof HTMLTableCellElement ? parseInt(cell.getAttribute("colspan") || "1", 10) : 1;
|
|
15935
|
-
});
|
|
15936
|
-
maxColumns = Math.max(maxColumns, count);
|
|
15937
|
-
});
|
|
15938
|
-
if (maxColumns > 1) {
|
|
15939
|
-
rows.forEach((row) => {
|
|
15940
|
-
const cells = row.querySelectorAll("td, th");
|
|
15941
|
-
if (cells.length === 1 && cells[0] instanceof HTMLTableCellElement) {
|
|
15942
|
-
const existing = parseInt(cells[0].getAttribute("colspan") || "1", 10);
|
|
15943
|
-
if (existing < maxColumns) {
|
|
15944
|
-
cells[0].setAttribute("colspan", maxColumns.toString());
|
|
15945
|
-
}
|
|
15946
|
-
}
|
|
15947
|
-
});
|
|
15948
|
-
}
|
|
15949
|
-
});
|
|
15950
|
-
}
|
|
15951
|
-
var DOMPURIFY_CONFIG = {
|
|
15952
|
-
ALLOWED_ATTR: ["class", "style", "id", "v", "usfm"],
|
|
15953
|
-
ALLOW_DATA_ATTR: true
|
|
15954
|
-
};
|
|
15955
|
-
function transformBibleHtml(html) {
|
|
15956
|
-
if (typeof window === "undefined" || !("DOMParser" in window)) {
|
|
15957
|
-
return { html, notes: {} };
|
|
15958
|
-
}
|
|
15959
|
-
const doc = new DOMParser().parseFromString(
|
|
15960
|
-
import_isomorphic_dompurify.default.sanitize(html, DOMPURIFY_CONFIG),
|
|
15961
|
-
"text/html"
|
|
15962
|
-
);
|
|
15963
|
-
wrapVerseContent(doc);
|
|
15964
|
-
assignFootnoteKeys(doc);
|
|
15965
|
-
const notes = extractNotesFromWrappedHtml(doc);
|
|
15966
|
-
addNbspToVerseLabels(doc);
|
|
15967
|
-
fixIrregularTables(doc);
|
|
15968
|
-
return { html: doc.body.innerHTML, notes };
|
|
15969
|
-
}
|
|
15970
|
-
|
|
15971
|
-
// src/components/verse.tsx
|
|
15972
|
-
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
15973
16023
|
var VerseFootnoteButton = (0, import_react3.memo)(function VerseFootnoteButton2({
|
|
15974
16024
|
verseNum,
|
|
15975
|
-
|
|
16025
|
+
notes,
|
|
16026
|
+
verseHtml,
|
|
16027
|
+
hasVerseContext,
|
|
15976
16028
|
reference,
|
|
15977
16029
|
fontSize,
|
|
15978
16030
|
theme
|
|
15979
16031
|
}) {
|
|
15980
|
-
const { hasVerseContext } = verseNotes;
|
|
15981
16032
|
const verseReference = reference ? `${reference}:${verseNum}` : `Verse ${verseNum}`;
|
|
15982
16033
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Popover, { children: [
|
|
15983
16034
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(PopoverTrigger, { "data-yv-sdk": true, "data-yv-theme": theme, asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
@@ -16002,11 +16053,11 @@ var VerseFootnoteButton = (0, import_react3.memo)(function VerseFootnoteButton2(
|
|
|
16002
16053
|
{
|
|
16003
16054
|
className: "yv:mb-3 yv:font-serif yv:*:font-serif",
|
|
16004
16055
|
style: { fontSize: fontSize ? `${fontSize}px` : "1.25rem" },
|
|
16005
|
-
dangerouslySetInnerHTML: { __html:
|
|
16056
|
+
dangerouslySetInnerHTML: { __html: verseHtml }
|
|
16006
16057
|
}
|
|
16007
16058
|
)
|
|
16008
16059
|
] }),
|
|
16009
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { className: "yv:list-none yv:p-0 yv:m-0 yv:space-y-1", children:
|
|
16060
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { className: "yv:list-none yv:p-0 yv:m-0 yv:space-y-1", children: notes.map((note, index) => {
|
|
16010
16061
|
const marker = getFootnoteMarker(index);
|
|
16011
16062
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
16012
16063
|
"li",
|
|
@@ -16045,7 +16096,6 @@ function VerseUnavailableMessage() {
|
|
|
16045
16096
|
}
|
|
16046
16097
|
function BibleTextHtml({
|
|
16047
16098
|
html,
|
|
16048
|
-
notes,
|
|
16049
16099
|
reference,
|
|
16050
16100
|
fontSize,
|
|
16051
16101
|
theme,
|
|
@@ -16054,19 +16104,32 @@ function BibleTextHtml({
|
|
|
16054
16104
|
highlightedVerses = {}
|
|
16055
16105
|
}) {
|
|
16056
16106
|
const contentRef = (0, import_react3.useRef)(null);
|
|
16057
|
-
const [
|
|
16107
|
+
const [footnoteData, setFootnoteData] = (0, import_react3.useState)([]);
|
|
16058
16108
|
const providerTheme = (0, import_platform_react_hooks3.useTheme)();
|
|
16059
16109
|
const currentTheme = theme || providerTheme;
|
|
16060
16110
|
(0, import_react3.useLayoutEffect)(() => {
|
|
16061
16111
|
if (!contentRef.current) return;
|
|
16062
16112
|
contentRef.current.innerHTML = html;
|
|
16063
16113
|
const anchors = contentRef.current.querySelectorAll("[data-verse-footnote]");
|
|
16114
|
+
const notesByKey = /* @__PURE__ */ new Map();
|
|
16115
|
+
anchors.forEach((el) => {
|
|
16116
|
+
const verseNum = el.getAttribute("data-verse-footnote");
|
|
16117
|
+
if (!verseNum) return;
|
|
16118
|
+
const content = el.getAttribute("data-verse-footnote-content") || "";
|
|
16119
|
+
const existing = notesByKey.get(verseNum);
|
|
16120
|
+
if (existing) existing.push(content);
|
|
16121
|
+
else notesByKey.set(verseNum, [content]);
|
|
16122
|
+
});
|
|
16064
16123
|
const result = [];
|
|
16065
16124
|
anchors.forEach((el) => {
|
|
16066
16125
|
const verseNum = el.getAttribute("data-verse-footnote");
|
|
16067
|
-
if (verseNum)
|
|
16126
|
+
if (!verseNum) return;
|
|
16127
|
+
const allNotes = notesByKey.get(verseNum) || [];
|
|
16128
|
+
const hasVerseContext = el.closest(".yv-v[v]") !== null;
|
|
16129
|
+
const verseHtml = hasVerseContext ? getVerseHtmlFromDom(contentRef.current, verseNum) : "";
|
|
16130
|
+
result.push({ verseNum, el, notes: allNotes, verseHtml, hasVerseContext });
|
|
16068
16131
|
});
|
|
16069
|
-
|
|
16132
|
+
setFootnoteData(result);
|
|
16070
16133
|
}, [html]);
|
|
16071
16134
|
(0, import_react3.useLayoutEffect)(() => {
|
|
16072
16135
|
if (!contentRef.current) return;
|
|
@@ -16086,15 +16149,15 @@ function BibleTextHtml({
|
|
|
16086
16149
|
} : void 0;
|
|
16087
16150
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
16088
16151
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { ref: contentRef, onClick: handleClick }),
|
|
16089
|
-
|
|
16090
|
-
|
|
16091
|
-
if (!verseNotes) return null;
|
|
16092
|
-
return (0, import_react_dom.createPortal)(
|
|
16152
|
+
footnoteData.map(
|
|
16153
|
+
({ verseNum, el, notes, verseHtml, hasVerseContext }, index) => (0, import_react_dom.createPortal)(
|
|
16093
16154
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
16094
16155
|
VerseFootnoteButton,
|
|
16095
16156
|
{
|
|
16096
16157
|
verseNum,
|
|
16097
|
-
|
|
16158
|
+
notes,
|
|
16159
|
+
verseHtml,
|
|
16160
|
+
hasVerseContext,
|
|
16098
16161
|
reference,
|
|
16099
16162
|
fontSize,
|
|
16100
16163
|
theme: currentTheme
|
|
@@ -16102,8 +16165,8 @@ function BibleTextHtml({
|
|
|
16102
16165
|
),
|
|
16103
16166
|
el,
|
|
16104
16167
|
`${verseNum}-${index}`
|
|
16105
|
-
)
|
|
16106
|
-
|
|
16168
|
+
)
|
|
16169
|
+
)
|
|
16107
16170
|
] });
|
|
16108
16171
|
}
|
|
16109
16172
|
var Verse = {
|
|
@@ -16146,7 +16209,10 @@ var Verse = {
|
|
|
16146
16209
|
onVerseSelect,
|
|
16147
16210
|
highlightedVerses
|
|
16148
16211
|
}, ref) => {
|
|
16149
|
-
const
|
|
16212
|
+
const transformedHtml = (0, import_react3.useMemo)(
|
|
16213
|
+
() => typeof window === "undefined" ? html : transformBibleHtmlForBrowser(html).html,
|
|
16214
|
+
[html]
|
|
16215
|
+
);
|
|
16150
16216
|
const providerTheme = (0, import_platform_react_hooks3.useTheme)();
|
|
16151
16217
|
const currentTheme = theme || providerTheme;
|
|
16152
16218
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
@@ -16165,8 +16231,7 @@ var Verse = {
|
|
|
16165
16231
|
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
16166
16232
|
BibleTextHtml,
|
|
16167
16233
|
{
|
|
16168
|
-
html:
|
|
16169
|
-
notes: transformedData.notes,
|
|
16234
|
+
html: transformedHtml,
|
|
16170
16235
|
reference,
|
|
16171
16236
|
fontSize,
|
|
16172
16237
|
theme: currentTheme,
|
|
@@ -17496,5 +17561,6 @@ injectStyles();
|
|
|
17496
17561
|
YouVersionProvider,
|
|
17497
17562
|
YouVersionUserInfo,
|
|
17498
17563
|
getAdjacentChapter,
|
|
17564
|
+
transformBibleHtml,
|
|
17499
17565
|
useYVAuth
|
|
17500
17566
|
});
|