widebible-embed 1.0.1 → 1.2.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/README.md CHANGED
@@ -7,32 +7,8 @@
7
7
 
8
8
  Embed WideBible scripture widgets on any website. **Zero dependencies**, Shadow DOM style isolation, 3 built-in themes (light, dark, sepia), and automatic daily verse updates. Each widget includes a "Powered by WideBible" backlink.
9
9
 
10
- WideBible covers **66 books** (39 Old Testament, 27 New Testament), **31,102 verses**, 4 English translations (KJV, ASV, BBE, YLT), **1,833 biblical people**, **942 biblical places**, and **432,000+ cross-references** — all accessible via free public API with CORS enabled for any origin.
11
-
12
10
  > **Try the interactive widget builder at [widget.widebible.com](https://widget.widebible.com)**
13
11
 
14
- <p align="center">
15
- <img src="demo.gif" alt="widebible-embed demo — Bible verse widget with KJV, ASV, BBE translations and light/dark/sepia themes" width="800">
16
- </p>
17
-
18
- ## Table of Contents
19
-
20
- - [Quick Start](#quick-start)
21
- - [What You Can Do](#what-you-can-do)
22
- - [Verse Card — Single Bible Verse Display](#verse-card--single-bible-verse-display)
23
- - [Chapter Card — Full Chapter with Navigation](#chapter-card--full-chapter-with-navigation)
24
- - [Person Card — Biblical Figure Profile](#person-card--biblical-figure-profile)
25
- - [Comparison Card — Side-by-Side Verse Comparison](#comparison-card--side-by-side-verse-comparison)
26
- - [Verse of the Day — Auto-Updating Daily Widget](#verse-of-the-day--auto-updating-daily-widget)
27
- - [Search Box — Full-Text Bible Search](#search-box--full-text-bible-search)
28
- - [Widget Options](#widget-options)
29
- - [Themes](#themes)
30
- - [CDN Options](#cdn-options)
31
- - [Technical Details](#technical-details)
32
- - [Learn More About the Bible](#learn-more-about-the-bible)
33
- - [WideHoly Scripture Family](#wideholy-scripture-family)
34
- - [License](#license)
35
-
36
12
  ## Quick Start
37
13
 
38
14
  ```html
@@ -43,301 +19,122 @@ WideBible covers **66 books** (39 Old Testament, 27 New Testament), **31,102 ver
43
19
  <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
44
20
  ```
45
21
 
46
- That's it. The widget loads, fetches the verse from the WideBible API, and renders it with full style isolation. No configuration files, no build step.
22
+ That's it. The widget will load, fetch the verse from the WideBible API, and render it with full style isolation.
47
23
 
48
- ## What You Can Do
24
+ ## Widget Types
49
25
 
50
- The Bible contains 66 canonical books written across roughly 1,500 years in three languages — Hebrew, Aramaic, and Greek. WideBible makes every verse, chapter, person, and place queryable through a structured API, enabling developers to embed rich scripture content with a single HTML attribute.
26
+ | Type | Usage |
27
+ |------|-------|
28
+ | Verse Card | `<div data-widebible="verse" data-ref="..." data-theme="light"></div>` |
29
+ | Chapter Card | `<div data-widebible="chapter" data-ref="..."></div>` |
30
+ | Person Card | `<div data-widebible="person" data-slug="moses"></div>` |
31
+ | Comparison Card | `<div data-widebible="compare" data-a="..." data-b="..."></div>` |
32
+ | Verse of the Day | `<div data-widebible="votd" data-theme="light"></div>` |
33
+ | Search Box | `<div data-widebible="search" data-placeholder="..."></div>` |
51
34
 
52
- ### Verse Card — Single Bible Verse Display
35
+ ## Widget Options
53
36
 
54
- A Verse Card renders a single verse reference with translation name, book context, and optional original-language text. The default translation is KJV (King James Version, 1611), the most widely cited English Bible. Other supported translations: ASV (American Standard Version, 1901), BBE (Bible in Basic English, 1949), YLT (Young's Literal Translation, 1862).
37
+ | Attribute | Values | Default | Description |
38
+ |-----------|--------|---------|-------------|
39
+ | `data-widebible` | verse, chapter, person, compare, votd, search | required | Widget type |
40
+ | `data-ref` | "John 3:16" | — | Verse/chapter reference |
41
+ | `data-slug` | "moses", "paul" | — | Person slug (person widget only) |
42
+ | `data-a` | verse ref | — | First verse for comparison |
43
+ | `data-b` | verse ref | — | Second verse for comparison |
44
+ | `data-theme` | light, dark, sepia | light | Visual theme |
45
+ | `data-size` | default, compact | default | Widget size |
46
+ | `data-translation` | kjv, etc. | kjv | Translation code |
47
+ | `data-show-original` | true, false | false | Show original language text |
48
+ | `data-placeholder` | any string | "Search Bible…" | Search box placeholder |
55
49
 
56
- | Translation | Code | Style | Year |
57
- |-------------|------|-------|------|
58
- | King James Version | `kjv` | Traditional, formal | 1611 |
59
- | American Standard Version | `asv` | Literal, scholarly | 1901 |
60
- | Bible in Basic English | `bbe` | Simplified vocabulary | 1949 |
61
- | Young's Literal Translation | `ylt` | Woodenly literal | 1862 |
50
+ ## Themes
62
51
 
63
52
  ```html
64
- <!-- John 3:16 in KJV (default) -->
65
- <div data-widebible="verse"
66
- data-ref="John 3:16"
67
- data-theme="light">
68
- </div>
69
-
70
- <!-- Psalm 23:1 with Hebrew original text -->
71
- <div data-widebible="verse"
72
- data-ref="Psalm 23:1"
73
- data-translation="kjv"
74
- data-show-original="true"
75
- data-theme="sepia">
76
- </div>
53
+ <!-- Light (default) -->
54
+ <div data-widebible="votd" data-theme="light"></div>
77
55
 
78
- <!-- Romans 8:28 in BBE, compact size for sidebar -->
79
- <div data-widebible="verse"
80
- data-ref="Romans 8:28"
81
- data-translation="bbe"
82
- data-size="compact"
83
- data-theme="dark">
84
- </div>
56
+ <!-- Dark -->
57
+ <div data-widebible="votd" data-theme="dark"></div>
85
58
 
86
- <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
59
+ <!-- Sepia (warm, book-like) -->
60
+ <div data-widebible="votd" data-theme="sepia"></div>
87
61
  ```
88
62
 
89
- **Available options for Verse Card:**
90
-
91
- | Attribute | Values | Default |
92
- |-----------|--------|---------|
93
- | `data-ref` | "Book Chapter:Verse" e.g. `John 3:16` | required |
94
- | `data-translation` | `kjv`, `asv`, `bbe`, `ylt` | `kjv` |
95
- | `data-show-original` | `true`, `false` | `false` |
96
- | `data-theme` | `light`, `dark`, `sepia` | `light` |
97
- | `data-size` | `default`, `compact` | `default` |
98
-
99
- Learn more: [Bible Verse Lookup — widebible.com](https://widebible.com) · [KJV Translation Guide](https://widebible.com/translations/kjv/) · [Bible Books Reference](https://widebible.com/books/)
100
-
101
- ### Chapter Card — Full Chapter with Navigation
102
-
103
- A Chapter Card renders all verses in a chapter with previous/next navigation. Ideal for study pages, devotional sites, or any context where readers want to read extended passages.
63
+ ## CDN Options
104
64
 
105
- The Bible's 1,189 chapters range from a single verse (Psalm 117) to 176 verses (Psalm 119). Chapter Cards adapt their layout automatically compact books like Obadiah (21 verses) display inline, while longer chapters like Numbers 7 (89 verses) render in a scrollable container.
65
+ ### jsDelivr (recommendedglobal CDN, auto-updates with npm)
106
66
 
107
67
  ```html
108
- <!-- Genesis Chapter 1 — The Creation account -->
109
- <div data-widebible="chapter"
110
- data-ref="Genesis 1"
111
- data-theme="light">
112
- </div>
113
-
114
- <!-- Psalm 23 — The Lord is my shepherd (6 verses) -->
115
- <div data-widebible="chapter"
116
- data-ref="Psalm 23"
117
- data-translation="kjv"
118
- data-theme="sepia">
119
- </div>
120
-
121
- <!-- John Chapter 3 — Nicodemus and the new birth (36 verses) -->
122
- <div data-widebible="chapter"
123
- data-ref="John 3"
124
- data-show-original="true"
125
- data-theme="dark">
126
- </div>
127
-
128
68
  <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
129
69
  ```
130
70
 
131
- Learn more: [Browse Bible Chapters — widebible.com/chapters/](https://widebible.com) · [Old Testament Books](https://widebible.com/books/) · [New Testament Books](https://widebible.com/books/)
132
-
133
- ### Person Card — Biblical Figure Profile
134
-
135
- WideBible indexes **1,833 biblical people** — from major figures like Moses, David, and Paul to minor characters who appear in a single genealogy. Each person profile includes first mention, total verse appearances, associated places, and related figures.
71
+ ### R2 CDN (widebible.com hosted)
136
72
 
137
73
  ```html
138
- <!-- Moses — The Lawgiver (appeared in ~848 verses) -->
139
- <div data-widebible="person"
140
- data-slug="moses"
141
- data-theme="light">
142
- </div>
143
-
144
- <!-- King David — Shepherd, warrior, psalmist (appeared in ~1,118 verses) -->
145
- <div data-widebible="person"
146
- data-slug="david"
147
- data-theme="sepia">
148
- </div>
149
-
150
- <!-- Apostle Paul — Author of 13 epistles -->
151
- <div data-widebible="person"
152
- data-slug="paul"
153
- data-theme="dark">
154
- </div>
155
-
156
- <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
74
+ <script src="https://cdn.widebible.com/embed.min.js"></script>
157
75
  ```
158
76
 
159
- **Available options for Person Card:**
160
-
161
- | Attribute | Values | Notes |
162
- |-----------|--------|-------|
163
- | `data-slug` | lowercase person name | e.g. `moses`, `david`, `paul`, `mary` |
164
- | `data-theme` | `light`, `dark`, `sepia` | `light` |
165
-
166
- Learn more: [Biblical People Directory — widebible.com/people/](https://widebible.com/people/) · [Patriarchs and Prophets](https://widebible.com/people/) · [Apostles of Jesus](https://widebible.com/people/)
77
+ ### npm (for bundlers)
167
78
 
168
- ### Comparison Card — Side-by-Side Verse Comparison
169
-
170
- Compare any two Bible verses side by side — useful for parallel Gospel comparisons, Old Testament prophecy vs. New Testament fulfillment, or showing how different translations render the same passage.
171
-
172
- With 31,102 verses, there are over 482 million possible verse pairs. Popular comparisons include Synoptic Gospel parallels (Matthew/Mark/Luke covering the same events), Messianic prophecy fulfillments, and wisdom literature parallels between Proverbs and Ecclesiastes.
173
-
174
- ```html
175
- <!-- Compare two creation accounts: Genesis 1:1 vs John 1:1 -->
176
- <div data-widebible="compare"
177
- data-a="Genesis 1:1"
178
- data-b="John 1:1"
179
- data-theme="light">
180
- </div>
181
-
182
- <!-- Messianic prophecy: Isaiah 7:14 foretold vs Matthew 1:23 fulfilled -->
183
- <div data-widebible="compare"
184
- data-a="Isaiah 7:14"
185
- data-b="Matthew 1:23"
186
- data-translation="kjv"
187
- data-theme="sepia">
188
- </div>
189
-
190
- <!-- Synoptic parallel: Matthew 5:3 vs Luke 6:20 (Beatitudes) -->
191
- <div data-widebible="compare"
192
- data-a="Matthew 5:3"
193
- data-b="Luke 6:20"
194
- data-show-original="true"
195
- data-theme="dark">
196
- </div>
197
-
198
- <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
79
+ ```bash
80
+ npm install widebible-embed
199
81
  ```
200
82
 
201
- Learn more: [Synoptic Gospel Parallels — widebible.com](https://widebible.com) · [Messianic Prophecies](https://widebible.com) · [Cross-Reference Explorer](https://widebible.com)
202
-
203
- ### Verse of the Day — Auto-Updating Daily Widget
83
+ ```javascript
84
+ import 'widebible-embed';
85
+ ```
204
86
 
205
- The Verse of the Day widget displays a curated verse that changes automatically each day. Results are cached in localStorage and refresh at midnight UTC — no server calls on repeat visits within the same day. This makes it ideal for homepages, blog sidebars, and any page where you want fresh scripture without manual updates.
87
+ ## Examples
206
88
 
207
- The daily verse selection cycles through 365 curated passages covering wisdom, promises, encouragement, and the Gospels — suitable for general Christian audiences.
89
+ ### Verse of the Day
208
90
 
209
91
  ```html
210
- <!-- Verse of the Day — full size, light theme -->
211
92
  <div data-widebible="votd" data-theme="light"></div>
212
-
213
- <!-- Verse of the Day — dark theme for dark websites -->
214
- <div data-widebible="votd" data-theme="dark"></div>
215
-
216
- <!-- Verse of the Day — sepia, warm book-like tone -->
217
- <div data-widebible="votd" data-theme="sepia"></div>
218
-
219
- <!-- Compact for narrow sidebars (shows verse + reference only) -->
220
- <div data-widebible="votd" data-theme="light" data-size="compact"></div>
221
-
222
93
  <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
223
94
  ```
224
95
 
225
- Learn more: [Daily Bible Verse widebible.com](https://widebible.com) · [Bible Reading Plans](https://widebible.com) · [Verse Collections](https://widebible.com)
226
-
227
- ### Search Box — Full-Text Bible Search
228
-
229
- Embed a full-text search box that queries all 31,102 verses in real time. Results appear in a dropdown as the user types, with verse reference, book, and a highlighted snippet. Clicking a result opens the full verse on widebible.com.
230
-
231
- The search index covers all 4 translations simultaneously. Searching "love one another" returns verses from John, Romans, 1 John, and more — across all supported translations.
96
+ ### Verse with Original Language
232
97
 
233
98
  ```html
234
- <!-- Default search box -->
235
- <div data-widebible="search"
236
- data-placeholder="Search Bible verses…"
237
- data-theme="light">
238
- </div>
239
-
240
- <!-- Thematic search — pre-scoped to a topic -->
241
- <div data-widebible="search"
242
- data-placeholder="Search Psalms…"
99
+ <div data-widebible="verse"
100
+ data-ref="John 3:16"
101
+ data-show-original="true"
243
102
  data-theme="sepia">
244
103
  </div>
245
-
246
- <!-- Dark theme search for dark-mode websites -->
247
- <div data-widebible="search"
248
- data-placeholder="Find a Bible verse…"
249
- data-theme="dark">
250
- </div>
251
-
252
104
  <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
253
105
  ```
254
106
 
255
- Learn more: [Bible Verse Search widebible.com](https://widebible.com) · [Concordance Search](https://widebible.com) · [API Search Endpoint](https://widebible.com/developers/)
256
-
257
- ## Widget Options
258
-
259
- All widget types share these common attributes:
260
-
261
- | Attribute | Values | Default | Description |
262
- |-----------|--------|---------|-------------|
263
- | `data-widebible` | `verse`, `chapter`, `person`, `compare`, `votd`, `search` | required | Widget type |
264
- | `data-ref` | `"Book Chapter:Verse"` | — | Verse or chapter reference (verse/chapter widgets) |
265
- | `data-slug` | `"moses"`, `"paul"` | — | Person identifier (person widget only) |
266
- | `data-a` | verse reference | — | First verse for comparison |
267
- | `data-b` | verse reference | — | Second verse for comparison |
268
- | `data-theme` | `light`, `dark`, `sepia` | `light` | Visual theme — light for white backgrounds, dark for dark UIs, sepia for warm book-like feel |
269
- | `data-size` | `default`, `compact` | `default` | Compact omits metadata, suitable for sidebars under 300px |
270
- | `data-translation` | `kjv`, `asv`, `bbe`, `ylt` | `kjv` | Bible translation |
271
- | `data-show-original` | `true`, `false` | `false` | Show Hebrew (OT) or Greek (NT) source text below translation |
272
- | `data-placeholder` | any string | `"Search Bible…"` | Placeholder text for search widget |
273
-
274
- ## Themes
275
-
276
- WideBible widgets support 3 themes that respect your site's visual design:
277
-
278
- ```html
279
- <!-- Light — white background, dark text, blue accent -->
280
- <div data-widebible="votd" data-theme="light"></div>
281
-
282
- <!-- Dark — dark background, light text, blue accent -->
283
- <div data-widebible="votd" data-theme="dark"></div>
284
-
285
- <!-- Sepia — warm parchment background, brown text — evokes printed Bible -->
286
- <div data-widebible="votd" data-theme="sepia"></div>
287
- ```
288
-
289
- All themes are self-contained inside Shadow DOM — they never affect or inherit from your site's CSS. You can place widgets on pages with any existing stylesheet without conflicts.
290
-
291
- ## CDN Options
292
-
293
- ### jsDelivr (recommended — global CDN, auto-updates with npm)
107
+ ### Compact Verse of the Day (sidebar use)
294
108
 
295
109
  ```html
110
+ <div data-widebible="votd" data-theme="light" data-size="compact"></div>
296
111
  <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
297
112
  ```
298
113
 
299
- Pin to a specific version for production stability:
114
+ ### Search Box
300
115
 
301
116
  ```html
302
- <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1.0.1/dist/embed.min.js"></script>
303
- ```
304
-
305
- ### R2 CDN (widebible.com hosted)
306
-
307
- ```html
308
- <script src="https://cdn.widebible.com/embed.min.js"></script>
309
- ```
310
-
311
- ### npm (for bundlers — Webpack, Vite, Rollup)
312
-
313
- ```bash
314
- npm install widebible-embed
315
- ```
316
-
317
- ```javascript
318
- // Import the embed script — registers the custom behavior on data-widebible elements
319
- import 'widebible-embed';
117
+ <div data-widebible="search" data-placeholder="Search Bible…"></div>
118
+ <script src="https://cdn.jsdelivr.net/npm/widebible-embed@1/dist/embed.min.js"></script>
320
119
  ```
321
120
 
322
121
  ## Technical Details
323
122
 
324
- - **Shadow DOM**: Complete style isolation — no CSS conflicts with your site. Widgets are fully encapsulated.
325
- - **Zero dependencies**: No jQuery, React, Vue, or any external library. Pure browser APIs only.
326
- - **System fonts**: No Google Fonts request — widgets use the system font stack and load instantly.
327
- - **CORS**: WideBible API has CORS enabled for all origins — no proxy needed.
328
- - **Caching**: Verse of the Day is cached in localStorage and refreshes daily at midnight UTC.
329
- - **MutationObserver**: Works with dynamically added elements (React, Vue, Angular SPAs) — widgets initialize when their container is added to the DOM.
330
- - **Bundle size**: ~5KB gzipped.
331
- - **Accent color**: Blue (`#3B82F6`) — matches the WideBible brand.
332
- - **API base**: `https://widebible.com/api/v1/` — free, no authentication required.
123
+ - **Shadow DOM**: Complete style isolation — no CSS conflicts with your site
124
+ - **Zero dependencies**: No jQuery, React, or any external library
125
+ - **System fonts**: No Google Fonts request — loads instantly
126
+ - **CORS**: WideBible API has CORS enabled for all origins
127
+ - **Caching**: Verse of the Day cached in localStorage (refreshes daily)
128
+ - **MutationObserver**: Works with dynamically added elements (SPAs)
129
+ - **Bundle size**: ~5KB gzipped
130
+
131
+ ## Learn More About Bible
333
132
 
334
- ## Learn More About the Bible
133
+ Visit [widebible.com](https://widebible.com) WideBible is a comprehensive Bible reference with full text, translations, and study tools.
335
134
 
336
- - **Browse**: [All 66 Bible Books](https://widebible.com/books/) · [Old Testament](https://widebible.com/books/) · [New Testament](https://widebible.com/books/) · [Psalms — 150 Chapters](https://widebible.com/books/)
337
- - **People**: [1,833 Biblical People](https://widebible.com/people/) · [Prophets and Patriarchs](https://widebible.com/people/) · [Apostles and Disciples](https://widebible.com/people/)
338
- - **Places**: [942 Biblical Places](https://widebible.com/places/) · [Jerusalem](https://widebible.com/places/) · [Ancient Israel Geography](https://widebible.com/places/)
339
- - **Tools**: [Bible Verse Lookup](https://widebible.com) · [Cross-Reference Explorer — 432K+ links](https://widebible.com) · [Widget Builder](https://widget.widebible.com)
340
- - **API**: [REST API Docs](https://widebible.com/developers/) · [OpenAPI Spec](https://widebible.com/api/openapi.json)
135
+ - **API docs**: [widebible.com/developers/](https://widebible.com/developers/)
136
+ - **Widget builder**: [widget.widebible.com](https://widget.widebible.com)
137
+ - **npm package**: [npmjs.com/package/widebible-embed](https://www.npmjs.com/package/widebible-embed)
341
138
 
342
139
  ## WideHoly Scripture Family
343
140
 
@@ -345,15 +142,15 @@ Part of [WideHoly](https://wideholy.com) — Scripture for everyone.
345
142
 
346
143
  | Site | Domain | Scripture |
347
144
  |------|--------|-----------|
348
- | **WideBible** | [widebible.com](https://widebible.com) | **66 books, 31,102 verses, KJV/ASV/BBE/YLT, 1,833 people, 942 places** |
349
- | WideQuran | [widequran.com](https://widequran.com) | 114 surahs, 6,236 ayahs, Arabic Uthmani + 5 translations |
350
- | WideTorah | [widetorah.com](https://widetorah.com) | 24 books, 23,145 verses, Hebrew Masoretic + English, 54 parashot |
351
- | WideGita | [widegita.com](https://widegita.com) | 18 chapters, 700 verses, Sanskrit Devanagari + 9 commentators |
352
- | WideSutra | [widesutra.com](https://widesutra.com) | 5 collections, 5,326 texts, Pali + English |
353
- | WideHoly | [wideholy.com](https://wideholy.com) | 5 religions, 236,000+ scripture records, cross-religion comparison |
145
+ | **WideBible** | [widebible.com](https://widebible.com) | Bible verses, chapters, people |
146
+ | **WideQuran** | [widequran.com](https://widequran.com) | Quran verses, surahs |
147
+ | **WideTorah** | [widetorah.com](https://widetorah.com) | Torah verses, portions |
148
+ | **WideGita** | [widegita.com](https://widegita.com) | Bhagavad Gita verses |
149
+ | **WideSutra** | [widesutra.com](https://widesutra.com) | Buddhist sutras, teachings |
150
+ | **WideHoly** | [wideholy.com](https://wideholy.com) | Multi-religion scripture hub |
354
151
 
355
152
  ## License
356
153
 
357
154
  MIT — see [LICENSE](./LICENSE).
358
155
 
359
- Built with care by [WideHoly](https://wideholy.com).
156
+ Built with ❤️ by [WideHoly](https://wideholy.com).
package/dist/embed.esm.js CHANGED
@@ -518,6 +518,31 @@ function bindCopyButton(btn, text) {
518
518
  });
519
519
  }
520
520
 
521
+ // src/rich-snippets.ts
522
+ function injectQuotation(data, domain, siteName) {
523
+ if (document.querySelector("script[data-wide-snippet]")) return;
524
+ const jsonLd = {
525
+ "@context": "https://schema.org",
526
+ "@type": "Quotation",
527
+ text: data.text,
528
+ spokenByCharacter: data.reference,
529
+ isPartOf: {
530
+ "@type": "Book",
531
+ name: data.book
532
+ },
533
+ contributor: {
534
+ "@type": "Organization",
535
+ name: siteName,
536
+ url: `https://${domain}`
537
+ }
538
+ };
539
+ const script = document.createElement("script");
540
+ script.type = "application/ld+json";
541
+ script.setAttribute("data-wide-snippet", "true");
542
+ script.textContent = JSON.stringify(jsonLd);
543
+ document.head.appendChild(script);
544
+ }
545
+
521
546
  // src/widgets/verse.ts
522
547
  function parseRef(ref) {
523
548
  const withBook = ref.match(/^(.+?)\s+(\d+):(\d+)$/);
@@ -595,6 +620,20 @@ function initVerseWidget(el, config) {
595
620
  const verse = "results" in data && Array.isArray(data.results) ? data.results[0] : data;
596
621
  if (!verse || !verse.text) throw new Error("No verse found");
597
622
  renderVerseWidget(container, verse, el, config);
623
+ if (el.dataset.noSnippet !== "true") {
624
+ const parsed = parseRef(verse.reference || ref);
625
+ const bookName = (parsed == null ? void 0 : parsed.book) || verse.book || config.scriptureLabel;
626
+ injectQuotation(
627
+ {
628
+ text: verse.text,
629
+ reference: verse.reference || ref,
630
+ translation: verse.translation || config.defaultTranslation,
631
+ book: bookName
632
+ },
633
+ config.domain,
634
+ config.name
635
+ );
636
+ }
598
637
  }).catch(() => {
599
638
  renderError(
600
639
  container,
@@ -898,6 +937,66 @@ function initSearchWidget(el, config) {
898
937
  `;
899
938
  }
900
939
 
940
+ // src/web-components.ts
941
+ function makeWidgetElement(widgetType, initFn, domainAttrs) {
942
+ const observed = [...domainAttrs, "theme", "style-variant", "size", "show-original"];
943
+ return class extends HTMLElement {
944
+ static get observedAttributes() {
945
+ return observed;
946
+ }
947
+ connectedCallback() {
948
+ if (this.shadowRoot) return;
949
+ this._syncDataAttrs();
950
+ initFn(this, define_SITE_CONFIG_default);
951
+ }
952
+ attributeChangedCallback(_name, oldVal, newVal) {
953
+ if (oldVal === newVal) return;
954
+ if (!this.shadowRoot) return;
955
+ const shadow = this.shadowRoot;
956
+ while (shadow.firstChild) shadow.firstChild.remove();
957
+ this._syncDataAttrs();
958
+ initFn(this, define_SITE_CONFIG_default);
959
+ }
960
+ /**
961
+ * Bridge element attributes → data-* attributes so the existing widget
962
+ * init functions (which read from dataset) work unchanged.
963
+ */
964
+ _syncDataAttrs() {
965
+ const attrKey = define_SITE_CONFIG_default.attribute.replace("data-", "");
966
+ this.dataset[attrKey] = widgetType;
967
+ for (const a of domainAttrs) {
968
+ const val = this.getAttribute(a);
969
+ if (val !== null) this.dataset[a] = val;
970
+ }
971
+ const theme = this.getAttribute("theme");
972
+ if (theme !== null) this.dataset.theme = theme;
973
+ const styleVariant = this.getAttribute("style-variant");
974
+ if (styleVariant !== null) this.dataset.style = styleVariant;
975
+ const size = this.getAttribute("size");
976
+ if (size !== null) this.dataset.size = size;
977
+ const showOriginal = this.getAttribute("show-original");
978
+ if (showOriginal !== null) this.dataset.showOriginal = showOriginal;
979
+ }
980
+ };
981
+ }
982
+ function registerWideElements() {
983
+ if (typeof customElements === "undefined") return;
984
+ const definitions = [
985
+ ["wide-verse", "verse", initVerseWidget, ["ref", "translation", "show-original"]],
986
+ ["wide-chapter", "chapter", initChapterWidget, ["ref"]],
987
+ ["wide-person", "person", initPersonWidget, ["slug"]],
988
+ ["wide-compare", "compare", initCompareWidget, ["a", "b"]],
989
+ ["wide-votd", "votd", initVotdWidget, []],
990
+ ["wide-search", "search", initSearchWidget, ["placeholder"]]
991
+ ];
992
+ for (const [tagName, widgetType, initFn, attrs] of definitions) {
993
+ if (!customElements.get(tagName)) {
994
+ customElements.define(tagName, makeWidgetElement(widgetType, initFn, attrs));
995
+ }
996
+ }
997
+ }
998
+ registerWideElements();
999
+
901
1000
  // src/core.ts
902
1001
  function initWidget(el, type, config) {
903
1002
  switch (type) {
package/dist/embed.min.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* widebible-embed v1.0.0 | MIT | https://widget.widebible.com */
2
- "use strict";(()=>{var h={site:"widebible",name:"WideBible",domain:"widebible.com",apiBase:"https://widebible.com/api/v1/bible",votdEndpoint:"https://widebible.com/api/v1/verse-of-the-day/",searchPath:"/search/",accent:"#4F46E5",attribute:"data-widebible",religion:"christianity",scriptureLabel:"Bible",defaultTranslation:"kjv"};function C(t){return`
2
+ "use strict";(()=>{var u={site:"widebible",name:"WideBible",domain:"widebible.com",apiBase:"https://widebible.com/api/v1/bible",votdEndpoint:"https://widebible.com/api/v1/verse-of-the-day/",searchPath:"/search/",accent:"#4F46E5",attribute:"data-widebible",religion:"christianity",scriptureLabel:"Bible",defaultTranslation:"kjv"};function _(t){return`
3
3
  :host {
4
4
  display: block;
5
5
  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
@@ -441,30 +441,30 @@
441
441
  color: var(--muted);
442
442
  margin: 2px 0 0 0;
443
443
  }
444
- `}function l(t,e){let r=t.attachShadow({mode:"open"}),n=document.createElement("style");return n.textContent=C(e.accent),r.appendChild(n),r}function u(t,e,r){let n=e.dataset.theme||"light",i=e.dataset.size||"default",s=document.createElement("div");return s.className=["wide-widget",r].filter(Boolean).join(" "),s.setAttribute("data-theme",n),s.setAttribute("data-size",i),t.appendChild(s),s}function f(t){t.innerHTML=`
444
+ `}function m(t,e){let r=t.attachShadow({mode:"open"}),n=document.createElement("style");return n.textContent=_(e.accent),r.appendChild(n),r}function g(t,e,r){let n=e.dataset.theme||"light",o=e.dataset.size||"default",s=document.createElement("div");return s.className=["wide-widget",r].filter(Boolean).join(" "),s.setAttribute("data-theme",n),s.setAttribute("data-size",o),t.appendChild(s),s}function f(t){t.innerHTML=`
445
445
  <div class="wide-loading">
446
446
  <span class="wide-spinner"></span>
447
447
  Loading\u2026
448
448
  </div>
449
- `}function c(t,e,r){t.innerHTML=`
449
+ `}function l(t,e,r){t.innerHTML=`
450
450
  <div class="wide-error">
451
451
  <p>${e}</p>
452
452
  <a href="https://${r.domain}" target="_blank" rel="noopener">
453
453
  Visit ${r.name}
454
454
  </a>
455
455
  </div>
456
- `}var w='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>',b='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',E='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>',L='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>';function g(t){return`<span class="wide-powered">Powered by <a href="https://${t.domain}" target="_blank" rel="noopener">${t.name}</a></span>`}function k(t,e){t.addEventListener("click",()=>{var r;(r=navigator.clipboard)==null||r.writeText(e).then(()=>{t.innerHTML=`${E} Copied!`,setTimeout(()=>{t.innerHTML=`${b} Copy`},2e3)}).catch(()=>{let n=document.createElement("textarea");n.value=e,n.style.position="fixed",n.style.opacity="0",document.body.appendChild(n),n.select(),document.execCommand("copy"),document.body.removeChild(n),t.innerHTML=`${E} Copied!`,setTimeout(()=>{t.innerHTML=`${b} Copy`},2e3)})})}function P(t){let e=t.match(/^(.+?)\s+(\d+):(\d+)$/);if(e)return{book:e[1],chapter:e[2],verse:e[3]};let r=t.match(/^(\d+):(\d+)$/);return r?{book:"",chapter:r[1],verse:r[2]}:null}function U(t,e,r){let n=P(e);if(!n)return"";let i=new URLSearchParams;return n.book&&i.set("book",n.book),i.set("chapter",n.chapter),i.set("verse",n.verse),r&&i.set("translation",r),`${t.apiBase}/verses/?${i.toString()}`}function j(t,e,r,n){let i=r.dataset.size==="compact",s=r.dataset.showOriginal==="true",a=e.reference||r.dataset.ref||"",o=e.translation||n.defaultTranslation,d=e.url||`https://${n.domain}`,p=s&&e.original_text?`<p class="wide-original" lang="${e.original_language||""}">${e.original_text}</p>`:"";t.innerHTML=`
457
- <div class="wide-ribbon${i?" compact":""}">
458
- <p class="wide-verse-text${i?" compact":""}">${e.text}</p>
459
- ${p}
456
+ `}var w='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>',b='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',W='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>',I='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>';function h(t){return`<span class="wide-powered">Powered by <a href="https://${t.domain}" target="_blank" rel="noopener">${t.name}</a></span>`}function k(t,e){t.addEventListener("click",()=>{var r;(r=navigator.clipboard)==null||r.writeText(e).then(()=>{t.innerHTML=`${W} Copied!`,setTimeout(()=>{t.innerHTML=`${b} Copy`},2e3)}).catch(()=>{let n=document.createElement("textarea");n.value=e,n.style.position="fixed",n.style.opacity="0",document.body.appendChild(n),n.select(),document.execCommand("copy"),document.body.removeChild(n),t.innerHTML=`${W} Copied!`,setTimeout(()=>{t.innerHTML=`${b} Copy`},2e3)})})}function V(t,e,r){if(document.querySelector("script[data-wide-snippet]"))return;let n={"@context":"https://schema.org","@type":"Quotation",text:t.text,spokenByCharacter:t.reference,isPartOf:{"@type":"Book",name:t.book},contributor:{"@type":"Organization",name:r,url:`https://${e}`}},o=document.createElement("script");o.type="application/ld+json",o.setAttribute("data-wide-snippet","true"),o.textContent=JSON.stringify(n),document.head.appendChild(o)}function B(t){let e=t.match(/^(.+?)\s+(\d+):(\d+)$/);if(e)return{book:e[1],chapter:e[2],verse:e[3]};let r=t.match(/^(\d+):(\d+)$/);return r?{book:"",chapter:r[1],verse:r[2]}:null}function O(t,e,r){let n=B(e);if(!n)return"";let o=new URLSearchParams;return n.book&&o.set("book",n.book),o.set("chapter",n.chapter),o.set("verse",n.verse),r&&o.set("translation",r),`${t.apiBase}/verses/?${o.toString()}`}function N(t,e,r,n){let o=r.dataset.size==="compact",s=r.dataset.showOriginal==="true",a=e.reference||r.dataset.ref||"",i=e.translation||n.defaultTranslation,d=e.url||`https://${n.domain}`,c=s&&e.original_text?`<p class="wide-original" lang="${e.original_language||""}">${e.original_text}</p>`:"";t.innerHTML=`
457
+ <div class="wide-ribbon${o?" compact":""}">
458
+ <p class="wide-verse-text${o?" compact":""}">${e.text}</p>
459
+ ${c}
460
460
  </div>
461
- <div class="wide-body${i?" compact":""}">
461
+ <div class="wide-body${o?" compact":""}">
462
462
  <div class="wide-meta">
463
463
  <span class="wide-ref">${a}</span>
464
- <span class="wide-badge">${o.toUpperCase()}</span>
464
+ <span class="wide-badge">${i.toUpperCase()}</span>
465
465
  </div>
466
466
  </div>
467
- <div class="wide-actions${i?" compact":""}">
467
+ <div class="wide-actions${o?" compact":""}">
468
468
  <a class="wide-link" href="${d}" target="_blank" rel="noopener">
469
469
  Read on ${n.name} ${w}
470
470
  </a>
@@ -472,11 +472,11 @@
472
472
  ${b} Copy
473
473
  </button>
474
474
  </div>
475
- ${g(n)}
476
- `;let m=t.querySelector(".wide-copy-btn");m&&k(m,`${e.text} \u2014 ${a}`)}function S(t,e){let r=l(t,e),n=u(r,t);f(n);let i=t.dataset.ref||"",s=t.dataset.translation||e.defaultTranslation;if(!i){c(n,"Missing data-ref attribute.",e);return}let a=U(e,i,s);if(!a){c(n,`Could not parse reference: "${i}"`,e);return}fetch(a).then(o=>{if(!o.ok)throw new Error(`HTTP ${o.status}`);return o.json()}).then(o=>{let d="results"in o&&Array.isArray(o.results)?o.results[0]:o;if(!d||!d.text)throw new Error("No verse found");j(n,d,t,e)}).catch(()=>{c(n,`Could not load "${i}". Visit ${e.name} to read scripture.`,e)})}function N(t){let e=t.match(/^(.+?)\s+(\d+)$/);if(e)return{book:e[1],chapter:e[2]};let r=t.match(/^(\d+)$/);return r?{book:"",chapter:r[1]}:null}function A(t,e){let r=t.split(/\s+/);return r.length<=e?t:r.slice(0,e).join(" ")+"\u2026"}function M(t,e){let r=l(t,e),n=u(r,t);f(n);let i=t.dataset.ref||"";if(!i){c(n,"Missing data-ref attribute.",e);return}let s=N(i);if(!s){c(n,`Could not parse chapter reference: "${i}"`,e);return}let a=s.book?`${e.apiBase}/chapters/${encodeURIComponent(s.book)}/${s.chapter}/`:`${e.apiBase}/chapters/${s.chapter}/`;fetch(a).then(o=>{if(!o.ok)throw new Error(`HTTP ${o.status}`);return o.json()}).then(o=>{var v;let d=o.title||o.name||i,p=o.book_name||o.book||"",m=o.summary||o.description||"",y=(v=o.verse_count)!=null?v:Array.isArray(o.verses)?o.verses.length:null,$=o.url||`https://${e.domain}`,x=m?A(m,100):"";n.innerHTML=`
475
+ ${h(n)}
476
+ `;let p=t.querySelector(".wide-copy-btn");p&&k(p,`${e.text} \u2014 ${a}`)}function T(t,e){let r=m(t,e),n=g(r,t);f(n);let o=t.dataset.ref||"",s=t.dataset.translation||e.defaultTranslation;if(!o){l(n,"Missing data-ref attribute.",e);return}let a=O(e,o,s);if(!a){l(n,`Could not parse reference: "${o}"`,e);return}fetch(a).then(i=>{if(!i.ok)throw new Error(`HTTP ${i.status}`);return i.json()}).then(i=>{let d="results"in i&&Array.isArray(i.results)?i.results[0]:i;if(!d||!d.text)throw new Error("No verse found");if(N(n,d,t,e),t.dataset.noSnippet!=="true"){let c=B(d.reference||o),p=(c==null?void 0:c.book)||d.book||e.scriptureLabel;V({text:d.text,reference:d.reference||o,translation:d.translation||e.defaultTranslation,book:p},e.domain,e.name)}}).catch(()=>{l(n,`Could not load "${o}". Visit ${e.name} to read scripture.`,e)})}function j(t){let e=t.match(/^(.+?)\s+(\d+)$/);if(e)return{book:e[1],chapter:e[2]};let r=t.match(/^(\d+)$/);return r?{book:"",chapter:r[1]}:null}function U(t,e){let r=t.split(/\s+/);return r.length<=e?t:r.slice(0,e).join(" ")+"\u2026"}function C(t,e){let r=m(t,e),n=g(r,t);f(n);let o=t.dataset.ref||"";if(!o){l(n,"Missing data-ref attribute.",e);return}let s=j(o);if(!s){l(n,`Could not parse chapter reference: "${o}"`,e);return}let a=s.book?`${e.apiBase}/chapters/${encodeURIComponent(s.book)}/${s.chapter}/`:`${e.apiBase}/chapters/${s.chapter}/`;fetch(a).then(i=>{if(!i.ok)throw new Error(`HTTP ${i.status}`);return i.json()}).then(i=>{var v;let d=i.title||i.name||o,c=i.book_name||i.book||"",p=i.summary||i.description||"",y=(v=i.verse_count)!=null?v:Array.isArray(i.verses)?i.verses.length:null,$=i.url||`https://${e.domain}`,x=p?U(p,100):"";n.innerHTML=`
477
477
  <div class="wide-body">
478
478
  <div class="wide-meta">
479
- ${p?`<span class="wide-badge">${p}</span>`:""}
479
+ ${c?`<span class="wide-badge">${c}</span>`:""}
480
480
  ${y!=null?`<span class="wide-stat">${y} verses</span>`:""}
481
481
  </div>
482
482
  <h3 class="wide-title">${d}</h3>
@@ -487,18 +487,18 @@
487
487
  Read full chapter on ${e.name} ${w}
488
488
  </a>
489
489
  </div>
490
- ${g(e)}
491
- `}).catch(()=>{c(n,`Could not load chapter "${i}". Visit ${e.name} to read scripture.`,e)})}function O(t,e){let r=t.split(/\s+/);return r.length<=e?t:r.slice(0,e).join(" ")+"\u2026"}function H(t,e){let r=l(t,e),n=u(r,t);f(n);let i=t.dataset.slug||"";if(!i){c(n,"Missing data-slug attribute.",e);return}let s=`${e.apiBase}/people/${encodeURIComponent(i)}/`;fetch(s).then(a=>{if(!a.ok)throw new Error(`HTTP ${a.status}`);return a.json()}).then(a=>{var x,v;let o=a.name||a.full_name||i,d=a.era||a.period||"",p=a.bio||a.description||"",m=(v=(x=a.verse_count)!=null?x:a.mentioned_verses)!=null?v:null,y=a.url||`https://${e.domain}/people/${i}/`,$=p?O(p,150):"";n.innerHTML=`
490
+ ${h(e)}
491
+ `}).catch(()=>{l(n,`Could not load chapter "${o}". Visit ${e.name} to read scripture.`,e)})}function F(t,e){let r=t.split(/\s+/);return r.length<=e?t:r.slice(0,e).join(" ")+"\u2026"}function E(t,e){let r=m(t,e),n=g(r,t);f(n);let o=t.dataset.slug||"";if(!o){l(n,"Missing data-slug attribute.",e);return}let s=`${e.apiBase}/people/${encodeURIComponent(o)}/`;fetch(s).then(a=>{if(!a.ok)throw new Error(`HTTP ${a.status}`);return a.json()}).then(a=>{var x,v;let i=a.name||a.full_name||o,d=a.era||a.period||"",c=a.bio||a.description||"",p=(v=(x=a.verse_count)!=null?x:a.mentioned_verses)!=null?v:null,y=a.url||`https://${e.domain}/people/${o}/`,$=c?F(c,150):"";n.innerHTML=`
492
492
  <div class="wide-person-header">
493
- <div class="wide-person-icon">${L}</div>
493
+ <div class="wide-person-icon">${I}</div>
494
494
  <div>
495
- <p class="wide-person-name">${o}</p>
495
+ <p class="wide-person-name">${i}</p>
496
496
  ${d?`<p class="wide-person-era">${d}</p>`:""}
497
497
  </div>
498
498
  </div>
499
499
  <div class="wide-body">
500
500
  <div class="wide-meta">
501
- ${m!=null?`<span class="wide-stat">${m} verses</span>`:""}
501
+ ${p!=null?`<span class="wide-stat">${p} verses</span>`:""}
502
502
  </div>
503
503
  ${$?`<p class="wide-summary">${$}</p>`:""}
504
504
  </div>
@@ -507,16 +507,16 @@
507
507
  Learn more on ${e.name} ${w}
508
508
  </a>
509
509
  </div>
510
- ${g(e)}
511
- `}).catch(()=>{c(n,`Could not load "${i}". Visit ${e.name} for biblical figures.`,e)})}function q(t){let e=new URLSearchParams,r=t.match(/^(.+?)\s+(\d+):(\d+)$/);if(r)return e.set("book",r[1]),e.set("chapter",r[2]),e.set("verse",r[3]),e;let n=t.match(/^(\d+):(\d+)$/);return n&&(e.set("chapter",n[1]),e.set("verse",n[2])),e}async function B(t,e){var o,d;let r=q(e),n=`${t.apiBase}/verses/?${r.toString()}`,i=await fetch(n);if(!i.ok)throw new Error(`HTTP ${i.status}`);let s=await i.json(),a=(d=(o=s.results)==null?void 0:o[0])!=null?d:s;if(!(a!=null&&a.text))throw new Error("No verse data");return{text:a.text,reference:a.reference||e,url:a.url}}function V(t,e){let r=l(t,e),n=u(r,t);f(n);let i=t.dataset.a||"",s=t.dataset.b||"";if(!i||!s){c(n,"Missing data-a or data-b attributes.",e);return}Promise.all([B(e,i),B(e,s)]).then(([a,o])=>{let d=`https://${e.domain}/compare/`;n.innerHTML=`
510
+ ${h(e)}
511
+ `}).catch(()=>{l(n,`Could not load "${o}". Visit ${e.name} for biblical figures.`,e)})}function G(t){let e=new URLSearchParams,r=t.match(/^(.+?)\s+(\d+):(\d+)$/);if(r)return e.set("book",r[1]),e.set("chapter",r[2]),e.set("verse",r[3]),e;let n=t.match(/^(\d+):(\d+)$/);return n&&(e.set("chapter",n[1]),e.set("verse",n[2])),e}async function z(t,e){var i,d;let r=G(e),n=`${t.apiBase}/verses/?${r.toString()}`,o=await fetch(n);if(!o.ok)throw new Error(`HTTP ${o.status}`);let s=await o.json(),a=(d=(i=s.results)==null?void 0:i[0])!=null?d:s;if(!(a!=null&&a.text))throw new Error("No verse data");return{text:a.text,reference:a.reference||e,url:a.url}}function L(t,e){let r=m(t,e),n=g(r,t);f(n);let o=t.dataset.a||"",s=t.dataset.b||"";if(!o||!s){l(n,"Missing data-a or data-b attributes.",e);return}Promise.all([z(e,o),z(e,s)]).then(([a,i])=>{let d=`https://${e.domain}/compare/`;n.innerHTML=`
512
512
  <div class="wide-compare-grid">
513
513
  <div class="wide-compare-col">
514
514
  <p class="wide-compare-label">${a.reference}</p>
515
515
  <p class="wide-compare-text">${a.text}</p>
516
516
  </div>
517
517
  <div class="wide-compare-col">
518
- <p class="wide-compare-label">${o.reference}</p>
519
- <p class="wide-compare-text">${o.text}</p>
518
+ <p class="wide-compare-label">${i.reference}</p>
519
+ <p class="wide-compare-text">${i.text}</p>
520
520
  </div>
521
521
  </div>
522
522
  <div class="wide-actions">
@@ -524,41 +524,41 @@
524
524
  Compare on ${e.name} ${w}
525
525
  </a>
526
526
  </div>
527
- ${g(e)}
528
- `}).catch(()=>{c(n,`Could not load comparison. Visit ${e.name} to compare scripture.`,e)})}var _="wide_votd_";function z(){return new Date().toISOString().slice(0,10)}function G(t){try{let e=`${_}${t}`,r=localStorage.getItem(e);if(!r)return null;let n=JSON.parse(r);return n.date!==z()?(localStorage.removeItem(e),null):n.data}catch(e){return null}}function F(t,e){try{let r=`${_}${t}`;localStorage.setItem(r,JSON.stringify({date:z(),data:e}))}catch(r){}}function D(t,e){let r=l(t,e),n=u(r,t);f(n);let i=t.dataset.size==="compact";function s(o){let d=o.translation||e.defaultTranslation,p=o.url||`https://${e.domain}`;n.innerHTML=`
527
+ ${h(e)}
528
+ `}).catch(()=>{l(n,`Could not load comparison. Visit ${e.name} to compare scripture.`,e)})}var D="wide_votd_";function R(){return new Date().toISOString().slice(0,10)}function q(t){try{let e=`${D}${t}`,r=localStorage.getItem(e);if(!r)return null;let n=JSON.parse(r);return n.date!==R()?(localStorage.removeItem(e),null):n.data}catch(e){return null}}function Q(t,e){try{let r=`${D}${t}`;localStorage.setItem(r,JSON.stringify({date:R(),data:e}))}catch(r){}}function S(t,e){let r=m(t,e),n=g(r,t);f(n);let o=t.dataset.size==="compact";function s(i){let d=i.translation||e.defaultTranslation,c=i.url||`https://${e.domain}`;n.innerHTML=`
529
529
  <div class="wide-accent-bar"></div>
530
- <div class="wide-ribbon${i?" compact":""}">
531
- <p class="wide-verse-text${i?" compact":""}">${o.text}</p>
530
+ <div class="wide-ribbon${o?" compact":""}">
531
+ <p class="wide-verse-text${o?" compact":""}">${i.text}</p>
532
532
  </div>
533
- <div class="wide-body${i?" compact":""}">
533
+ <div class="wide-body${o?" compact":""}">
534
534
  <div class="wide-meta">
535
- <span class="wide-ref">${o.reference}</span>
535
+ <span class="wide-ref">${i.reference}</span>
536
536
  <span class="wide-badge">${d.toUpperCase()}</span>
537
537
  <span class="wide-badge">Verse of the Day</span>
538
538
  </div>
539
539
  </div>
540
- <div class="wide-actions${i?" compact":""}">
541
- <a class="wide-link" href="${p}" target="_blank" rel="noopener">
540
+ <div class="wide-actions${o?" compact":""}">
541
+ <a class="wide-link" href="${c}" target="_blank" rel="noopener">
542
542
  More at ${e.name} ${w}
543
543
  </a>
544
544
  <button class="wide-copy-btn" type="button">
545
545
  ${b} Copy
546
546
  </button>
547
547
  </div>
548
- ${g(e)}
549
- `;let m=n.querySelector(".wide-copy-btn");m&&k(m,`${o.text} \u2014 ${o.reference}`)}let a=G(e.site);if(a){s(a);return}fetch(e.votdEndpoint).then(o=>{if(!o.ok)throw new Error(`HTTP ${o.status}`);return o.json()}).then(o=>{if(!(o!=null&&o.text))throw new Error("No VOTD data");F(e.site,o),s(o)}).catch(()=>{c(n,`Could not load verse of the day. Visit ${e.name} for daily scripture.`,e)})}function W(t,e){let r=l(t,e),n=u(r,t),i=t.dataset.placeholder||`Search ${e.scriptureLabel}\u2026`;n.innerHTML=`
548
+ ${h(e)}
549
+ `;let p=n.querySelector(".wide-copy-btn");p&&k(p,`${i.text} \u2014 ${i.reference}`)}let a=q(e.site);if(a){s(a);return}fetch(e.votdEndpoint).then(i=>{if(!i.ok)throw new Error(`HTTP ${i.status}`);return i.json()}).then(i=>{if(!(i!=null&&i.text))throw new Error("No VOTD data");Q(e.site,i),s(i)}).catch(()=>{l(n,`Could not load verse of the day. Visit ${e.name} for daily scripture.`,e)})}function M(t,e){let r=m(t,e),n=g(r,t),o=t.dataset.placeholder||`Search ${e.scriptureLabel}\u2026`;n.innerHTML=`
550
550
  <div class="wide-search-wrap">
551
551
  <form class="wide-search-form" action="https://${e.domain}${e.searchPath}" method="get" target="_blank">
552
552
  <input
553
553
  class="wide-search-input"
554
554
  type="search"
555
555
  name="q"
556
- placeholder="${i}"
557
- aria-label="${i}"
556
+ placeholder="${o}"
557
+ aria-label="${o}"
558
558
  autocomplete="off"
559
559
  />
560
560
  <button class="wide-search-btn" type="submit">Search</button>
561
561
  </form>
562
562
  </div>
563
- ${g(e)}
564
- `}function T(t,e,r){switch(e){case"verse":S(t,r);break;case"chapter":M(t,r);break;case"person":H(t,r);break;case"compare":V(t,r);break;case"votd":D(t,r);break;case"search":W(t,r);break;default:break}}function R(t){document.querySelectorAll(`[${t.attribute}]`).forEach(r=>{if(r.shadowRoot)return;let n=r.dataset[t.attribute.replace("data-","")];n&&T(r,n,t)})}(function(){let e=h;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>R(e)):R(e),new MutationObserver(n=>{n.forEach(i=>{i.addedNodes.forEach(s=>{var o;if(s.nodeType!==Node.ELEMENT_NODE)return;let a=s;if(a.hasAttribute(e.attribute)&&!a.shadowRoot){let d=a.dataset[e.attribute.replace("data-","")];d&&T(a,d,e)}(o=a.querySelectorAll)==null||o.call(a,`[${e.attribute}]`).forEach(d=>{if(!d.shadowRoot){let p=d.dataset[e.attribute.replace("data-","")];p&&T(d,p,e)}})})})}).observe(document.body||document.documentElement,{childList:!0,subtree:!0})})();})();
563
+ ${h(e)}
564
+ `}function J(t,e,r){let n=[...r,"theme","style-variant","size","show-original"];return class extends HTMLElement{static get observedAttributes(){return n}connectedCallback(){this.shadowRoot||(this._syncDataAttrs(),e(this,u))}attributeChangedCallback(o,s,a){if(s===a||!this.shadowRoot)return;let i=this.shadowRoot;for(;i.firstChild;)i.firstChild.remove();this._syncDataAttrs(),e(this,u)}_syncDataAttrs(){let o=u.attribute.replace("data-","");this.dataset[o]=t;for(let c of r){let p=this.getAttribute(c);p!==null&&(this.dataset[c]=p)}let s=this.getAttribute("theme");s!==null&&(this.dataset.theme=s);let a=this.getAttribute("style-variant");a!==null&&(this.dataset.style=a);let i=this.getAttribute("size");i!==null&&(this.dataset.size=i);let d=this.getAttribute("show-original");d!==null&&(this.dataset.showOriginal=d)}}}function K(){if(typeof customElements=="undefined")return;let t=[["wide-verse","verse",T,["ref","translation","show-original"]],["wide-chapter","chapter",C,["ref"]],["wide-person","person",E,["slug"]],["wide-compare","compare",L,["a","b"]],["wide-votd","votd",S,[]],["wide-search","search",M,["placeholder"]]];for(let[e,r,n,o]of t)customElements.get(e)||customElements.define(e,J(r,n,o))}K();function H(t,e,r){switch(e){case"verse":T(t,r);break;case"chapter":C(t,r);break;case"person":E(t,r);break;case"compare":L(t,r);break;case"votd":S(t,r);break;case"search":M(t,r);break;default:break}}function P(t){document.querySelectorAll(`[${t.attribute}]`).forEach(r=>{if(r.shadowRoot)return;let n=r.dataset[t.attribute.replace("data-","")];n&&H(r,n,t)})}(function(){let e=u;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>P(e)):P(e),new MutationObserver(n=>{n.forEach(o=>{o.addedNodes.forEach(s=>{var i;if(s.nodeType!==Node.ELEMENT_NODE)return;let a=s;if(a.hasAttribute(e.attribute)&&!a.shadowRoot){let d=a.dataset[e.attribute.replace("data-","")];d&&H(a,d,e)}(i=a.querySelectorAll)==null||i.call(a,`[${e.attribute}]`).forEach(d=>{if(!d.shadowRoot){let c=d.dataset[e.attribute.replace("data-","")];c&&H(d,c,e)}})})})}).observe(document.body||document.documentElement,{childList:!0,subtree:!0})})();})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "widebible-embed",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Embed WideBible scripture widgets on any website. Lightweight (~5KB gzipped), zero dependencies, Shadow DOM isolation, 3 themes (light/dark/sepia).",
5
5
  "main": "dist/embed.min.js",
6
6
  "module": "dist/embed.esm.js",
@@ -35,4 +35,4 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  }
38
- }
38
+ }