quran-madina-html 2.1.0 → 2.1.1

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
@@ -1,319 +1,39 @@
1
- # Quran Madina Html (no-images)
1
+ # Quran Madina Html
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/quran-madina-html.svg)](https://www.npmjs.com/package/quran-madina-html)
4
- [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
- [![CodeQL](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/github-code-scanning/codeql)
6
- [![pylint](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/pylint.yml/badge.svg)](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/pylint.yml)
7
- [![py_test](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/py_test.yml/badge.svg)](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/py_test.yml)
8
- [![npm-grunt](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/npm-grunt.yml/badge.svg)](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/npm-grunt.yml)
9
- [![Socket Badge](https://badge.socket.dev/npm/package/quran-madina-html)](https://socket.dev/npm/package/quran-madina-html)
3
+ [![npm](https://img.shields.io/npm/v/quran-madina-html.svg?label=npm)](https://www.npmjs.com/package/quran-madina-html)
4
+ [![React](https://img.shields.io/npm/v/@tarekeldeeb/quran-madina-react.svg?label=react)](https://www.npmjs.com/package/@tarekeldeeb/quran-madina-react)
5
+ [![pub.dev](https://img.shields.io/pub/v/quran_madina_flutter.svg?label=flutter)](https://pub.dev/packages/quran_madina_flutter)
6
+ [![Docs](https://img.shields.io/badge/docs-online-00796b.svg)](https://tarekeldeeb.github.io/quran-madina-html/docs/)
7
+ [![License](https://img.shields.io/badge/license-Waqf%20GPL%202.0-blue.svg)](LICENSE.md)
8
+ [![build](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/npm-grunt.yml/badge.svg)](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/npm-grunt.yml)
9
+ [![tests](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/py_test.yml/badge.svg)](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/py_test.yml)
10
+ [![Flutter](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/flutter.yml/badge.svg)](https://github.com/tarekeldeeb/quran-madina-html/actions/workflows/flutter.yml)
10
11
 
11
- A Quran renderer for the browser that looks like the printed Madina Mushaf — **without a single image**.
12
- Every glyph is real, selectable, copy-pasteable text laid out with pure HTML/CSS, wrapped in one custom
13
- element:
12
+ Quran pages that look like the printed Madina Mushaf, as real text: selectable, searchable and
13
+ copyable, with no images. Lines are justified the way the print fills them, by stretching the
14
+ letters themselves. One tag renders a page, a verse range or a few words, with highlighted
15
+ words and emphasised letters, in the 1441H, 1405H or IndoPak print.
14
16
 
15
- ```html
16
- <quran-madina-html sura="2" aya="255"></quran-madina-html>
17
- ```
18
-
19
- **[See it live →](https://tarekeldeeb.github.io/quran-madina-html/demo/index.html)** (view the page source too —
20
- GitHub strips `<script>` tags from this README, so the tag can't render *here*, but it's just as simple as
21
- the snippet above once it's on your own page.)
22
-
23
- ## Why
24
-
25
- * **Pixel-faithful to the printed Madina Mushaf** — same line breaks, same justification, same page layout.
26
- * **Pure HTML/CSS, no images** — text stays selectable, searchable, and copy-pasteable; pages load fast.
27
- * **One HTML tag** — no build step, no framework required.
28
- * **Three layouts** — the 1441H and 1405H Madina prints and the 15-line IndoPak, at any font size.
29
- * **Word-level addressing** — render any word range, even across ayas, pages, and suras.
30
-
31
- ## New in 2.1: emphasise letters
32
-
33
- `letters` marks single letters inside words, in a colour of your choice, optionally blinking —
34
- on plain text and inside `highlight`/`error` words. See [Emphasising letters](#emphasising-letters).
35
-
36
- ```html
37
- <quran-madina-html sura="1" aya="2-4" highlight="1" error="4"
38
- letters="1:3, 4:3+5 lapis blink, 6:4 emerald"></quran-madina-html>
39
- ```
40
-
41
- Also new: a [Flutter wrapper](#flutter-wrapper) for Android, iOS, macOS and the web, which
42
- can work offline.
43
-
44
- ## New in 2.0: lines justified like the print
45
-
46
- Version 2 stretches the **letters themselves** (kashida strokes and wider letter forms, the way a
47
- calligrapher fills a line) instead of inserting tatweel characters or squashing whole lines. It
48
- uses the [DigitalKhatt](https://github.com/DigitalKhatt/digitalkhatt.org) fonts by Amine Anane
49
- (OFL), with HarfBuzz deciding every stretch at build time; the browser only needs the font and
50
- one `font-feature-settings` per word. The text is still real, selectable text.
51
-
52
- Three layouts, picked with `data-layout` on the script tag:
53
-
54
- <table>
55
- <tr>
56
- <td align="center"><img src="demo/img/v2-p50-1441H.png" width="220" alt="page 50, 1441H"><br/><code>1441H</code> (default)<br/>New Madina Mushaf</td>
57
- <td align="center"><img src="demo/img/v2-p50-1405H.png" width="220" alt="page 50, 1405H"><br/><code>1405H</code><br/>Old Madina Mushaf</td>
58
- <td align="center"><img src="demo/img/v2-p50-IndoPak.png" width="220" alt="page 50, IndoPak"><br/><code>IndoPak</code><br/>15-line IndoPak</td>
59
- </tr>
60
- </table>
61
-
62
- ```html
63
- <script src="https://unpkg.com/quran-madina-html" data-layout="1441H" data-font-size="22"></script>
64
- <quran-madina-html page="50"></quran-madina-html>
65
- ```
66
-
67
- | | `1441H` | `1405H` | `IndoPak` |
68
- |---|---|---|---|
69
- | Font | DigitalKhatt New Madina | DigitalKhatt Old Madina | DigitalKhatt IndoPak |
70
- | Text | [Tanzil](https://tanzil.net) Uthmani; pause marks of the 1441H print | Tanzil Uthmani | DigitalKhatt's IndoPak text (IndoPak spelling) |
71
- | Line breaks | the 1441H print (King Fahd Complex page data) | the 1405H print | DigitalKhatt's IndoPak layout |
72
- | Pages | 604 | 604 | 610 |
73
-
74
- * Everything else works the same in every layout: `page`, `sura`/`aya`, `words`, `highlight`,
75
- `error`, `headless`, `notitle`, copy and translate. `sura`/`aya` always use the Kufan
76
- numbering, so the same attributes show the same verse in every layout.
77
- * Any font size: the v2 databases are in em, so `data-font-size` just scales the page.
78
- * Spacing follows the print: words keep the print's ink-to-ink gaps, loose lines stretch
79
- letters, dense lines narrow their words (never the aya numbers). See
80
- [`docs/v2`](docs/v2/design-decisions.md) for how it was measured against the print.
81
- * **Upgrading from 1.x** is a drop-in replacement: existing tags render with 2.0 unchanged.
82
- Pick a layout with `data-layout`; 1.x's font options no longer apply (pin
83
- `quran-madina-html@1` to keep the 1.x renderer). The 1.x documentation is in
84
- [README_v1.md](README_v1.md).
85
-
86
- ## Next to the print
87
-
88
- Page 106 of the printed (1405H) Madina Mushaf, and the same page rendered with
89
- `data-layout="1405H"` — same lines, same words on each line, as live text:
90
-
91
- <table>
92
- <tr>
93
- <td align="center"><img src="demo/img/p106-image.JPG" width="260" alt="Scanned Madina Mushaf page 106"><br/>Printed Madina Mushaf</td>
94
- <td align="center"><img src="demo/img/p106-1405H.png" width="260" alt="quran-madina-html rendering of page 106"><br/><code>&lt;quran-madina-html page="106" headless="true"&gt;</code></td>
95
- </tr>
96
- </table>
97
-
98
- ## Features
99
-
100
- ### Verse ranges — framed or frameless
101
-
102
- `sura`/`aya` renders just the ayat you ask for, laid out exactly as they sit on the Madina page.
103
- By default you get a header (sura name + copy/translate icons); add `headless="true"` to strip
104
- that chrome and keep only the Quran text:
105
-
106
- <table>
107
- <tr>
108
- <td align="center">
109
-
110
- ```html
111
- <quran-madina-html sura="5" aya="1-2">
112
- </quran-madina-html>
113
- ```
114
-
115
- <img src="demo/img/verse-default.png" width="300" alt="Verse range with the default header">
116
- </td>
117
- <td align="center">
118
-
119
- ```html
120
- <quran-madina-html sura="5" aya="1-2"
121
- headless="true">
122
- </quran-madina-html>
123
- ```
17
+ <p align="center">
18
+ <img src="https://raw.githubusercontent.com/tarekeldeeb/quran-madina-html/main/docs/assets/img/showcase.gif" width="720" alt="quran-madina-html rendering a page, a verse, highlighted words, blinking letters, a selection across suras, and three prints">
19
+ </p>
124
20
 
125
- <img src="demo/img/verse-headless.png" width="300" alt="Same verse range, headless">
126
- </td>
127
- </tr>
128
- </table>
21
+ <p align="center">
22
+ <a href="https://tarekeldeeb.github.io/quran-madina-html/docs/"><b>Documentation</b></a> ·
23
+ <a href="https://tarekeldeeb.github.io/quran-madina-html/demo/index.html"><b>Live demo</b></a>
24
+ </p>
129
25
 
130
- ### Word-level selection
26
+ ## Getting started
131
27
 
132
- The `words` attribute picks out a word range — 1-based, inclusive — and hides the rest of the
133
- text *in place*, so the original Madina line justification is preserved instead of being
134
- reflowed. Counting continues past the end of the given aya, so a selection can cross aya, page,
135
- and even sura boundaries:
136
-
137
- <table>
138
- <tr>
139
- <td align="center">
28
+ ### HTML
140
29
 
141
30
  ```html
142
- <quran-madina-html sura="1" aya="1"
143
- words="1:2">
144
- </quran-madina-html>
145
- ```
146
-
147
- first two words of Al-Fatiha; the rest of the
148
- line stays in place, hidden (the blank before
149
- the closing quote)
150
- <br/><br/>
151
- <img src="demo/img/words-inline.png" width="216" alt="words attribute, inline selection">
152
- </td>
153
- <td align="center">
154
-
155
- ```html
156
- <quran-madina-html sura="1" aya="7"
157
- words="1-14">
158
- </quran-madina-html>
159
- ```
160
-
161
- words 1-14 counted from Al-Fatiha's last aya
162
- spill into Surat Al-Baqara — its basmala
163
- counts as words 10-13
164
- <br/><br/>
165
- <img src="demo/img/words-multiline.png" width="300" alt="words attribute, spanning Al-Fatiha into Al-Baqara">
166
- </td>
167
- </tr>
168
- </table>
169
-
170
- ### Highlighting words
171
-
172
- `highlight` (soft yellow) and `error` (soft red) mark a word range — same 1-based, inclusive
173
- format as `words` — on top of whatever is being shown. Unlike `words`, they work with or without
174
- a `words` selection, and even on a full `page` render:
175
-
176
- ```html
177
- <quran-madina-html sura="1" aya="1" highlight="2-3"></quran-madina-html>
178
- ```
179
-
180
- <img src="demo/img/highlight.png" width="216" alt="highlight attribute marking words 2-3 of Al-Fatiha's first aya">
181
-
182
- ### Emphasising letters
183
-
184
- `letters` marks single letters inside words, for teaching a letter or pointing at a mistake:
185
- comma-separated groups of `word:letters colour blink`. Word numbers count like `highlight`/`error`;
186
- letters are `3`, `3-5` or `3+5`; the colour is optional — `crimson` (default), `emerald`, `lapis`,
187
- `gold`, `violet` or any CSS colour; `blink` is optional too (no animation by default). The letter
188
- gets a soft halo of its own colour that sits behind the surrounding ink; with `blink` the halo
189
- pulses gently while the letter keeps its colour in every frame (still in print and with reduced
190
- motion). It works inside `highlight`/`error` words and gets a lighter shade on dark pages.
191
- Nothing changes size or position.
192
-
193
- ```html
194
- <quran-madina-html sura="1" aya="2-4" highlight="1" error="4"
195
- letters="1:3, 4:3+5 lapis blink, 6:4 emerald"></quran-madina-html>
196
- ```
197
-
198
- <img src="demo/img/letters.png" width="300" alt="letters attribute marking letters inside highlighted, error and plain words">
199
-
200
- Lam-alef (لا) is a single glyph, so marking either of its letters colours both.
201
-
202
- ## Getting Started
203
-
204
- ### Installation
205
-
206
- Install via **npm**:
31
+ <script src="https://unpkg.com/quran-madina-html@2"></script>
207
32
 
208
- ```bash
209
- npm install quran-madina-html
210
- ```
211
-
212
- Or include directly via **CDN script tag** in your HTML `<head>`:
213
-
214
- ```html
215
- <script type="text/javascript" src="https://unpkg.com/quran-madina-html"></script>
216
- ```
217
-
218
- * ``data-layout``: ``1441H`` (default), ``1405H``, ``IndoPak`` — see *New in 2.0* above
219
- * ``data-font-size`` defaults to 16 (px); any size works
220
-
221
- Then in your body, just add the tag.
222
-
223
- ```html
224
- <quran-madina-html sura="2" aya="8-10"></quran-madina-html>
225
- ```
226
-
227
- If the selected aya(s) fit on a single line, the default is to generate an inline ``<span>`` element, otherwise a ``<div>`` is generated.
228
-
229
- You can also restrict the output to a specific word sequence with the ``words`` attribute (1-based, inclusive; ``start-end``, ``start:end`` or a single ``index``). Word indices are counted starting from the given ``sura``/``aya`` and may run past it, so the rendered selection can span multiple ayas — and even cross page and sura boundaries. The **basmala counts as 4 real, individually selectable words** — matching the flat Tanzil word indexing most consumers count against (At-Tawba has none; Al-Fatiha's basmala is its real aya 1). This holds both when the selection crosses into a new sura and when it is **anchored at a sura's aya 1** (word 1 is then بسم and word 5 the aya's first word), while the **sura title** stays uncounted decoration, shown for context only. Display follows the printed Mushaf: when **all 4** basmala words fall inside the selection (or on a full ``page`` render) they show as the traditional **﷽ ligature**; a partial selection (1–3 of the 4) renders the individual words. Either way the basmala occupies 4 word indices. The non-selected words are kept in place so the original Madina line layout is preserved — only the chosen words are shown. Aya-number markers are not counted.
230
-
231
- > **Changed in 0.9.0:** the basmala used to be a *never counted* decoration; a cross-sura ``words``
232
- > index now lands 4 words later than in 0.8.x.
233
- >
234
- > **Changed in 0.9.3:** a ``words`` walk anchored at a sura's aya 1 now counts that sura's own
235
- > basmala too (0.9.0–0.9.2 only counted *crossed-into* basmalas, so aya-1-anchored word indices
236
- > landed 4 words early relative to the Tanzil flat indexing).
237
- >
238
- > **Changed in 0.9.5:** a ``words`` walk that runs past the end of An-Nas now **wraps around to
239
- > Al-Fatiha** and keeps counting (previously it stopped, silently truncating the selection);
240
- > Al-Fatiha's title stays uncounted decoration, and its basmala — being its real aya 1 —
241
- > renders as normal aya words.
242
-
243
- ```html
244
- <quran-madina-html sura="1" aya="1" words="1:2"></quran-madina-html> <!-- first two words only -->
245
- <quran-madina-html sura="1" aya="1" words="3-10"></quran-madina-html> <!-- words 3..10, spanning ayas 1-3 -->
246
- <quran-madina-html sura="1" aya="7" words="1-14"></quran-madina-html> <!-- spans Al-Fatiha into Al-Baqara:
247
- 9 words + 4 basmala words + الٓمٓ -->
248
- ```
249
-
250
- The ``words`` attribute is ignored when rendering a full ``page``.
251
-
252
- To embed a selection inside your own chrome (e.g. a quiz that already labels the sura/aya), the ``notitle`` attribute hides the crossed-into sura's **name text** while keeping its decorated title line (the ornamental frame stays, empty) — and the basmala, being real counted words, still renders:
253
-
254
- ```html
255
- <quran-madina-html sura="1" aya="7" words="1-14" notitle="true"></quran-madina-html>
256
- ```
257
-
258
- #### Highlighting and marking words
259
-
260
- Two more range attributes lay a visual mark over a sub-range of whatever is being shown —
261
- ``highlight`` (soft yellow) and ``error`` (soft red) — using the same 1-based, inclusive range
262
- format as ``words`` (``start-end``, ``start:end``, or a single index). Unlike ``words``, they
263
- work in **every** render mode: alongside a ``words`` selection, on a plain ``sura``/``aya`` verse
264
- (counted from that aya's first word), and even on a full ``page`` render (counted from the
265
- page's first aya — a page never starts mid-aya, so this is always well-defined). A range must
266
- fall entirely within whatever is actually displayed (the ``words`` selection if one is set,
267
- otherwise the whole verse/page); an out-of-range or malformed value is ignored with a console
268
- warning rather than breaking the render. When ``highlight`` and ``error`` overlap on the same
269
- word, ``error`` wins.
270
-
271
- ```html
272
- <quran-madina-html sura="1" aya="7" words="1-14" highlight="10-13" error="1-2"></quran-madina-html>
273
- <quran-madina-html sura="2" aya="255" highlight="5-8"></quran-madina-html> <!-- no words needed -->
274
- <quran-madina-html page="2" error="1-4"></quran-madina-html> <!-- counted from the page's first aya -->
275
- ```
276
-
277
- Colors come from the ``--qmh-highlight``/``--qmh-error`` CSS custom properties on
278
- ``quran-madina-html`` (defaults: soft yellow/soft red) — override them, or the
279
- ``.quran-madina-html-word-highlight``/``.quran-madina-html-word-error`` classes directly, from
280
- your own stylesheet.
281
-
282
- Another option exists to render a complete quran page:
283
-
284
- ```html
285
- <quran-madina-html page="106"></quran-madina-html>
286
- ```
287
-
288
- By default a multiline render gets a header (sura name + copy/translate icons); clicking any aya (inline or multiline) opens a small copy/translate popup scoped to it. Set ``headless="true"`` to drop the header and render only the Quran text:
289
-
290
- ```html
291
- <quran-madina-html sura="2" aya="8-10" headless="true"></quran-madina-html> <!-- no header -->
33
+ <quran-madina-html sura="2" aya="255"></quran-madina-html>
292
34
  ```
293
35
 
294
- ### Attributes Reference
295
-
296
- | Attribute | Type | Default | Description |
297
- |---|---|---|---|
298
- | `sura` | `number \| string` | - | Sura number (1–114). Required when using `aya`. |
299
- | `aya` | `number \| string` | - | Single aya or verse range (e.g., `255`, `"8-10"`). |
300
- | `page` | `number \| string` | - | Full Mushaf page number (1–604; IndoPak 1–610). Mutually exclusive with `sura`/`aya`. |
301
- | `words` | `string` | - | 1-based word index or range (e.g., `"1:2"`, `"3-10"`, or single index `"5"`). |
302
- | `highlight` | `string` | - | 1-based word range to mark soft yellow (same format as `words`). Works with or without `words`, and with `page` too. |
303
- | `error` | `string` | - | 1-based word range to mark soft red (same format as `words`). Wins over `highlight` on overlap. |
304
- | `letters` | `string` | - | Letters to emphasise inside words: `"1:3 crimson, 4:3-5 emerald blink"` (word:letters, optional colour — crimson by default — and optional `blink`). Works with `highlight`/`error`, `words` and `page`. |
305
- | `headless` | `boolean \| string` | `false` | Set to `"true"` to remove top sura header chrome and copy/translate icons. |
306
- | `notitle` | `boolean \| string` | `false` | Set to `"true"` to hide sura name text while retaining title frame decoration. |
307
- | `data-layout` | `string` | `"1441H"` | Layout: `1441H`, `1405H`, `IndoPak`. |
308
- | `data-font-size` | `number \| string` | `16` | Font size in px; any size. |
309
- | `data-cdn` | `string` | unpkg | Base URL for the CSS and databases (self-hosting, offline apps). |
310
-
311
- ## React wrapper
312
-
313
- For React apps (web, or mobile via a Capacitor WebView), use
314
- [`@tarekeldeeb/quran-madina-react`](https://www.npmjs.com/package/@tarekeldeeb/quran-madina-react),
315
- a thin component wrapping the `<quran-madina-html>` tag — lives in this repo's [`react/`](react)
316
- folder and is released in lockstep with this package.
36
+ ### React
317
37
 
318
38
  ```bash
319
39
  npm install @tarekeldeeb/quran-madina-react react
@@ -322,86 +42,36 @@ npm install @tarekeldeeb/quran-madina-react react
322
42
  ```jsx
323
43
  import QuranMadinaHtml from "@tarekeldeeb/quran-madina-react";
324
44
 
325
- <QuranMadinaHtml page={106} layout="1441H" />
45
+ <QuranMadinaHtml sura={2} aya="255" />
326
46
  ```
327
47
 
328
- See [`react/README.md`](react/README.md) for the full prop reference and the Capacitor
329
- (iOS/Android) packaging guide.
330
-
331
- ## Flutter wrapper
332
-
333
- For Flutter apps, use `quran_madina_flutter`, a widget with the same options as the React
334
- component. It lives in this repo's [`flutter/`](flutter) folder and is released in step with
335
- this package.
336
-
337
- - **Android, iOS, macOS:** the text renders in the platform's own WebView (WKWebView on Apple
338
- devices), the same engine as a Capacitor app.
339
- - **Web:** no WebView; the `<quran-madina-html>` tag goes straight into the page.
340
- - **Offline:** `dart run quran_madina_flutter:bundle` copies the library, databases and fonts
341
- into the app's assets. `bundled: true` then loads them from there instead of unpkg.
48
+ ### Flutter
342
49
 
343
50
  ```yaml
344
51
  dependencies:
345
- quran_madina_flutter:
346
- git:
347
- url: https://github.com/tarekeldeeb/quran-madina-html
348
- path: flutter
52
+ quran_madina_flutter: ^2.1.0
349
53
  ```
350
54
 
351
55
  ```dart
352
56
  import 'package:quran_madina_flutter/quran_madina_flutter.dart';
353
57
 
354
- QuranMadinaFlutter(sura: 1, aya: '2-4', letters: '1:3, 4:3-5 emerald blink')
355
- QuranMadinaFlutter(page: 106, bundled: true) // offline
356
- ```
357
-
358
- See [`flutter/README.md`](flutter/README.md) for the full option reference and the one-time
359
- platform settings for offline use. The example app's web build runs live at
360
- <https://tarekeldeeb.github.io/quran-madina-html/flutter/>.
361
-
362
- ## Dev Setup
363
-
364
- The project is published on npm ``npm install quran-madina-html``, with sources, assets and distributables.
365
- Alternatively, you can fork this repo, then clone it.
366
-
367
- ```bash
368
- apt install python3-distutils nodejs npm chromium-driver
369
- npm install // install components and scripts
370
- npm run release // build the dist with dependencies
371
-
372
- ```
373
-
374
- (`npm run build-db` still rebuilds the 1.x databases in `assets/db/`; the runtime no longer reads them.)
375
-
376
- The databases (`assets/db/v2`) are built from the Tanzil text and a checkout of
377
- [digitalkhatt.org](https://github.com/DigitalKhatt/digitalkhatt.org) (fonts and layouts):
378
-
379
- ```bash
380
- python src/db/v2/build_v2.py --layout 1441H --tanzil quran-uthmani.txt --dk-src ../digitalkhatt.org/ClientApp/src
381
- python src/db/v2/build_v2.py --layout 1405H --tanzil quran-uthmani.txt --dk-src ../digitalkhatt.org/ClientApp/src
382
- python src/db/v2/build_v2.py --layout IndoPak --dk-src ../digitalkhatt.org/ClientApp/src
383
- for db in Madina-1441H Madina-1405H IndoPak-15; do python src/db/shard_db.py assets/db/v2/$db.json assets/db/v2; done
384
- python src/db/v2/check_v2_db.py assets/db/v2/Madina-1441H.json
58
+ QuranMadinaFlutter(sura: 2, aya: '255')
385
59
  ```
386
60
 
387
- Download the Tanzil text as *Uthmani*, with pause marks, sajda and rub-el-hizb signs.
388
-
389
- The README screenshots in `demo/img` are taken from the current build by
390
- `python scripts/readme_screenshots.py` (with `npm run start` running).
61
+ Everything else (pages, word selection, highlighting, letters, layouts, styling, offline use,
62
+ and every option) is in the [documentation](https://tarekeldeeb.github.io/quran-madina-html/docs/).
391
63
 
392
64
  ## Credits
393
65
 
394
- * Quran text: [Tanzil Project](https://tanzil.net) (Uthmani 1.1).
395
- * v2 fonts and the 1405H/IndoPak layouts: [DigitalKhatt](https://github.com/DigitalKhatt/digitalkhatt.org)
396
- by Amine Anane, sponsored by Tarteel (fonts OFL 1.1, code MIT). The fonts in `assets/db/v2` are
397
- modified copies (stretch-position features added) under the same license, renamed `QMH ...`.
398
- * 1441H line breaks: King Fahd Glorious Quran Printing Complex page data, as published in the
399
- [`quran-qcf4`](https://www.npmjs.com/package/quran-qcf4) package (data under MIT); only line
66
+ - Quran text: [Tanzil Project](https://tanzil.net) (Uthmani 1.1).
67
+ - Fonts and the 1405H and IndoPak layouts: [DigitalKhatt](https://github.com/DigitalKhatt/digitalkhatt.org)
68
+ by Amine Anane, sponsored by Tarteel (fonts OFL 1.1, code MIT). The fonts in `assets/db/v2`
69
+ are modified copies, with stretch features added, under the same license and renamed `QMH ...`.
70
+ - 1441H line breaks: King Fahd Glorious Quran Printing Complex page data, as published in the
71
+ [`quran-qcf4`](https://www.npmjs.com/package/quran-qcf4) package (MIT); only the line
400
72
  boundaries are used.
401
73
 
402
- ## Demo
403
-
404
- <https://tarekeldeeb.github.io/quran-madina-html/demo/index.html>
405
-
406
- Don't forget to see the page source!
74
+ ## License
407
75
 
76
+ Code and assets are licensed under the ["Waqf" General Public License 2.0](https://github.com/ojuba-org/waqf/tree/master/2.0).
77
+ See [LICENSE.md](LICENSE.md).
@@ -1,2 +1,2 @@
1
- /*! quran-madina-html 17-10-2026 */
1
+ /*! quran-madina-html 18-10-2026 */
2
2
  !function(){var z="quran-madina-html",n=/localhost/.test(document.location.hostname)?"../":`https://www.unpkg.com/${z}/`,P={content:"Loading .."},g=/[\u0621-\u064A\u0671-\u06D3]/,N="﷽";function j(t,e){return 1===e&&0!==t}var l={};function m(e){try{for(var t=sessionStorage.length-1;0<=t;t--){var r=sessionStorage.key(t);r&&0===r.indexOf("quran-madina-html:"+e)&&sessionStorage.removeItem(r)}}catch(t){}Object.keys(l).forEach(function(t){0===t.indexOf(e)&&delete l[t]})}var o={};function a(s,t,e,r){var i,n=!r&&function(t){if(l[t])return l[t];try{var e=sessionStorage.getItem("quran-madina-html:"+t);if(e)return l[t]=JSON.parse(e)}catch(t){}return null}(s);n?t&&setTimeout(function(){t(n)},0):o[s]?o[s].push({success:t,error:e}):(o[s]=[{success:t,error:e}],(i=new XMLHttpRequest).onreadystatechange=function(){if(i.readyState===XMLHttpRequest.DONE){var t=o[s];if(delete o[s],200===i.status){var e=JSON.parse(i.responseText),r=s,n=e,a=i.responseText;l[r]=n;try{sessionStorage.setItem("quran-madina-html:"+r,a)}catch(t){}t.forEach(function(t){t.success&&t.success(e)})}else t.forEach(function(t){t.error&&t.error(i)})}},i.open("GET",s,!0),i.send())}function T(t){console.log(z+"> "+t)}function H(t){return null!=t&&(""===(t=String(t).trim().toLowerCase())||"true"===t||"1"===t||"yes"===t)}function M(t){var e=(e=(e=getComputedStyle(t).color)&&e.match(/[\d.]+/g))?.2126*r(+e[0])+.7152*r(+e[1])+.0722*r(+e[2]):1;function r(t){return(t/=255)<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4)}t.setAttribute("data-qmh-text-scheme",.5<e?"dark":"light")}function B(t){return Array(2).fill(t.split("-")[0]-1)}function O(t){return 2==t.split("-").length?t.split("-").map(t=>parseInt(t)+1):Array(2).fill(parseInt(t.split("-")[0])+1)}function D(t){return null==t||2!=(t=1==(t=t.split(/[-:]/).map(t=>parseInt(t,10))).length?[t[0],t[0]]:t).length||isNaN(t[0])||isNaN(t[1])||t[0]<1||t[1]<t[0]?null:t}function F(t,e,r){return null==e?null:e[0]<r[0]||e[1]>r[1]?(T(`${t} range ${e[0]}-${e[1]} outside displayed words ${r[0]}-${r[1]}, ignoring`),null):e}function V(t,e){if(null!=t&&"auto"!==t){if("no"===t)return!0;T("yes"===t?'inline="yes" is not implemented yet; falling back to "auto"':"Bad inline parameter: "+t)}return e}function u(t){return g.test(t)}function R(t,e){return(0===t?2:1)<=e}function X(t){var e=0;return t.t.split(/\s+/).forEach(function(t){u(t)&&(e+=1)}),e}function W(t){var e=0;return t.r.forEach(function(t){e+=X(t)}),e}function Y(t){for(var e=t.p,r=null,n=0;n<t.r.length;n++)null!==r&&t.r[n].l<r&&(e+=1),r=t.r[n].l;return e}function Z(t,e,r){for(var n=P.suras[t].ayas,a=0,s=e;s<=r;s++)R(t,s)&&(a+=W(n[s]));return a}function tt(t,e,r,n,a){for(var s=0,i=t;i<=r;i++)for(var l=P.suras[i].ayas,o=i===r?n:l.length-1,u=i===t?e:0;u<=o;u++){var c=l[u];void 0!==c&&c.p===a&&R(i,u)&&(s+=W(c))}return s}function et(t,e="class",n=null,a="transparent"){var s="tag"===e.toLowerCase()?document.getElementsByTagName(t):document.getElementsByClassName(t);Array.from(s).forEach(function(r){r.onmouseover=function(){var t,e=n||(t=r,getComputedStyle(t).getPropertyValue("--qmh-hover").trim())||`rgba(${(getComputedStyle(t).color.match(/[\d.]+/g)||[128,128,128]).slice(0,3).join(",")},0.12)`;Array.from(s).forEach(function(t){t.style.backgroundColor=e})},r.onmouseout=function(){Array.from(s).forEach(function(t){t.style.backgroundColor=a})}})}function rt(t,e){var r=(t,e)=>String(t).padStart(e,"0"),r=[z+"-part",`${z}-${r(t,3)}-`+r(e,3)];return(-1==e||1==t&&0==e)&&r.push("quran-madina-html-sura-start"),r}function y(){var t=document.createElement("div");return t.innerHTML='<svg viewBox="0 0 24 24" class="quran-madina-html-icon" width="20px"><path d="M16.02 20.96H3.78c-.41 0-.75-.34-.75-.75V7.74c0-.41.34-.75.75-.75h7.87c.21 0 .39.08.53.22l4.37 4.37c.14.14.22.32.22.53v8.11c0 .4-.34.74-.75.74ZM4.53 19.47h10.75v-6.61h-3.62c-.41 0-.75-.34-.75-.75V8.48H4.53v10.99Z"></path><path d="m20.74 7.63-4.37-4.37c-.14-.14-.36-.2-.53-.22H8.01c-.41 0-.75.34-.75.75V5.5h1.49v-.97h6.34v3.62c0 .41.34.75.75.75h3.62v8.19h-1.2v1.49h1.95c.41 0 .75-.34.75-.75V8.16c0-.21-.08-.4-.22-.53Z"></path></svg>'.trim(),t.firstChild}function v(){var t=document.createElement("div");return t.innerHTML='<svg class="quran-madina-html-icon" viewBox="0 0 820 615" style="height:20px"><path d="M70 1.9C37.6 8.8 12.6 33.4 3.4 67.5c-1.8 6.6-1.9 11.9-1.9 98v91l2.3 8c5.1 17.8 17 36.4 29.9 46.7 10.8 8.6 25.4 15.4 38.1 17.8 3.1.6 15.2 1 26.8 1 17.7 0 21.3.2 21.7 1.4.4.9.6 17.1.7 36.1 0 32.4.2 34.9 2 38.5 6.4 12.5 21.4 17.4 33.3 10.8 2.3-1.3 7.5-6.4 12.1-11.9l63-74.1c.6-.4 16.3-.8 34.9-.8H300l-.2-18.3-.3-18.2-42.8.1-43.1.5c-.2.2-12.7 15.1-27.9 33.1-15.1 18-27.8 32.4-28.1 32.1-.3-.4-.6-15.3-.6-33.2v-32.6l-38.8-.1c-42.1-.2-43.9-.4-54.3-6-6.5-3.6-16.9-14.6-20.2-21.5-5.8-11.9-5.7-10-5.7-100.7 0-92-.2-89.4 6.3-101.7 3.9-7.3 13.5-17.1 20.3-20.7C76 36.7 66.1 37 244.5 37L412 38.1c18 4.1 33.5 20.5 37.5 39.6 2.2 10.2 2.1 164.6 0 174.8-3.9 18.3-16.4 32.7-33.5 38.5-3.2 1.1-7.8 2-10 2-3.1 0-4 .4-4 1.7 0 1-.2 8.8-.3 17.3-.2 8.5-.2 16.2 0 17 .9 3.3 21.7-.5 34.1-6 13.7-6.3 27.5-17.9 36.2-30.5 5-7.2 10.8-20.4 13.2-30.1l2.3-8.9V165 76.5l-2.3-9c-8.1-31.4-30.5-54.9-61.7-64.8l-7-2.2-169-.2C87.7.1 78 .2 70 1.9zm277.294 102.559q19.5-.5 28.25 24.5 2.5 7.25-3.75 19.75-5.75 11.75-9.75 16-19.5 21.5-67 25.25-27.75 2.25-50.25-2.25-5.25 38.25-16.5 50-26 26.75-66 29.5-27.75 2-42.5-11-20.25-17.75-16.5-48.5 3-23.75 14.25-42.5 2.75-4.5 4.75-7.5 2-3 3-4.25 2.25-2.75 4.75-1.25 2.75 1.5 0 6.25-12.75 24.5-10 45 4 30.5 44.5 30.5 30.25 0 53.25-15.5 12.5-8.25 14.75-14.25-2.25-20.75-13.75-35.75-3.5-4.25-1.75-8l9.5-23.5q3-7.5 8.25 1.25l8.75 14.75q7.75 4.25 28.25 5 16.75-19.25 39.25-36.25 22.5-16.75 36.25-17.25zm11.5 40.5q-12.5-9.5-31.75-5.75-19 3.75-41.25 19.5 50.75.5 73-13.75zm-71.5-89.75q1.5-2 4.25-1.25 5 1.75 9.5 4.5 4.75 2.75 8.75 6.25 2 2 .75 4.5l-12 19q-1.75 2.5-4.5.5-2.5-2-18-11.25-2.75-1.75-1-4.25zm278.784 232.623c-4.7.4-4.7.4-6.7 6.1-1.2 3.1-9.3 25.9-18 50.7l-36.6 102.7-3.5 9.8h17.9 17.8l5.7-17 5.7-17h31.7 31.8l5.6 17.2 5.6 17.3 17.9-.3 18-.3-6-17.2-15-43.2-13.3-38.5-14.1-41-9.9-28.7c-.3-.6-30.2-1.1-34.6-.6zm35.7 106c-.2.2-10 .2-21.9.1l-21.5-.3 9.8-29.5 11-32.5c1.2-2.9 1.8-1.4 12.1 29.4l10.5 32.8zm148.8 139.609c32.4-6.9 57.4-31.5 66.6-65.6 1.8-6.6 1.9-11.9 1.9-98v-91l-2.3-8c-5.1-17.8-17-36.4-29.9-46.7-10.8-8.6-25.4-15.4-38.1-17.8-3.1-.6-15.2-1-26.8-1-17.7 0-21.3-.2-21.7-1.4-.4-.9-.6-17.1-.7-36.1 0-32.4-.2-34.9-2-38.5-6.4-12.5-21.4-17.4-33.3-10.8-2.3 1.3-7.5 6.4-12.1 11.9l-63 74.1c-.6.4-16.3.8-34.9.8h-33.7l.2 18.3.3 18.2 42.8-.1 43.1-.5c.2-.2 12.7-15.1 27.9-33.1 15.1-18 27.8-32.4 28.1-32.1.3.4.6 15.3.6 33.2v32.6l38.8.1c42.1.2 43.9.4 54.3 6 6.5 3.6 16.9 14.6 20.2 21.5 5.8 11.9 5.7 10 5.7 100.7 0 92 .2 89.4-6.3 101.7-3.9 7.3-13.5 17.1-20.3 20.7-11.4 6.1-1.5 5.8-179.9 5.8-108.2 0-164.5-.4-167.5-1.1-18-4.1-33.5-20.5-37.5-39.6-2.2-10.2-2.1-164.6 0-174.8 3.9-18.3 16.4-32.7 33.5-38.5 3.2-1.1 7.8-2 10-2 3.1 0 4-.4 4-1.7 0-1 .2-8.8.3-17.3.2-8.5.2-16.2 0-17-.9-3.3-21.7.5-34.1 6-13.7 6.3-27.5 17.9-36.2 30.5-5 7.2-10.8 20.4-13.2 30.1l-2.3 8.9v88.5 88.5l2.3 9c8.1 31.4 30.5 54.9 61.7 64.8l7 2.2 169 .2 177.5-1.6z"/></svg>'.trim(),t.firstChild}function nt(t){var e=document.createElement("quran-madina-html-header"),t=(e.innerHTML=t,y()),r=(t.addEventListener("click",I),v());return r.addEventListener("click",$),e.appendChild(t),e.appendChild(r),e}var h=null;function J(){h&&(h.popup.classList.remove(z+"-open"),document.removeEventListener("click",h.outsideClick),document.removeEventListener("keydown",h.onEscape),h=null)}function w(e,t,r,n,a){J();function s(t){u.contains(t.target)||J()}function i(t){"Escape"===t.key&&J()}l=e,(o=document.querySelector(`body > ${z}-aya-popup`))||((o=document.createElement(z+"-aya-popup")).appendChild(y()),o.appendChild(v()),document.body.appendChild(o)),["--qmh-header","--qmh-background"].forEach(function(t){o.style.setProperty(t,getComputedStyle(l).getPropertyValue(t))});var l,o,u=o,c=u.children[0],d=u.children[1];c.onclick=function(t){t.stopPropagation();t="“"+(t=e,t=Array.from(t.getElementsByClassName(r)).map(function(t){return t.textContent}).join(" ").replace(/\s+/g," ").trim(),G()?b(t):t)+"”\n\n"+P.suras[n-1].name;navigator.clipboard.writeText(t),alert("⎘ تم نسخ:\n\n"+t),J()},d.onclick=function(t){t.stopPropagation(),window.open(`https://quran.com/${n}/`+a,"_blank"),J()},c=u,d=(d=t).getBoundingClientRect(),c.style.left=Math.max(4,d.left)+"px",c.style.top=(0<d.top-34-6?d.top-34-6:d.bottom+6)+"px",u.classList.add(z+"-open");document.addEventListener("click",s),document.addEventListener("keydown",i),h={popup:u,outsideClick:s,onEscape:i}}function at(r,n,a,s){s<1||Array.from(r.getElementsByClassName(n)).forEach(function(e){e.style.cursor="pointer",e.onclick=function(t){t.stopPropagation(),w(r,e,n,a,s)}})}function st(t,e,r){t.style="display:block;",t.style.setProperty("font-family",P.font_family,""),t.style.setProperty("font-size",P.font_size+"px",""),"me_quran"===P.font_family&&t.style.setProperty("line-height",2*P.font_size+"px",""),G()&&t.style.setProperty("line-height","1.8",""),t.style.width=P.line_width+10+"px",r||t.style.setProperty("box-shadow","inset "+(e%2==1?"":"-")+"8px 0 7px -7px #333","")}function G(){return 2===P.version}function it(t,e,r){e=P.lines[e-1][r-1];t._qmh={row:e,words:0},e.z&&(t.style.setProperty("font-size",e.z+"em",""),t.style.setProperty("line-height",String(1.8/e.z),"")),t.style.setProperty("width",e.w/(e.z||1)+"em",""),t.style.setProperty("margin","0 auto",""),t.style.setProperty("white-space","nowrap",""),"text"!==e.k&&t.style.setProperty("text-align","center",""),e.x&&t.style.setProperty("transform",`scaleX(${e.x})`,"")}function U(a,t,s,i){t._qmh||(t._qmh={row:null,words:0});var l=t._qmh,o=null!==l.row&&"text"===l.row.k;s.t.split(" ").forEach(function(t,e){var r,n;""!==t&&(0<l.words&&(o?((n=document.createElement("span")).className=z+"-space",n.textContent=" ",r=l.row.s[l.words-1],n.style.cssText=0<=r?`display:inline-block;width:${r}em`:`display:inline-block;width:0;margin-left:${r}em`,a.appendChild(n)):a.appendChild(document.createTextNode(" "))),l.words=l.words+1,(r=document.createElement("span")).textContent=t,r.classList.add(z+"-word"),o&&s.f&&s.f[e]&&(r.style.fontFeatureSettings=s.f[e].split(",").map(function(t){t=t.split("=");return`"q${t[0].slice(2)}${Number(t[1]||1).toString(36)}"`}).join(", ")),o&&l.row.c&&t.indexOf("۝")<0&&(r.style.display="inline-block",r.style.transform=`scaleX(${l.row.c})`,0==(n=l.words-1)?r.style.transformOrigin="right":n===l.row.s.length&&(r.style.transformOrigin="left")),i&&i(r,t),a.appendChild(r))})}function b(t){return t.replace(/ا͏ٔ͏/g,"أ").replace(/إ͏/g,"إ").replace(/و͏ٔ͏/g,"ؤ").replace(/ي͏ٔ͏/g,"ئ").replace(/͏/g,"").replace(/ࣰ/g,"ً").replace(/ࣱ/g,"ٌ").replace(/ࣲ/g,"ٍ")}function lt(t,e){G()||(0<=e?t.style.setProperty("transform",`scaleX(${e*(P.stretch_scale||1)})`,""):t.style.setProperty("text-align","center",""))}function E(t,e,r,n){n&&e>=n[0]&&e<=n[1]?t.classList.add(z+"-word-error"):r&&e>=r[0]&&e<=r[1]&&t.classList.add(z+"-word-highlight")}function ot(r,t,n,a,s,i,e,l){return G()&&e&&l?U(r,l,e,function(t,e){u(e)&&(a+=1,t.setAttribute("data-qmh-w",a)),n&&(a<n[0]||a>n[1])&&t.classList.add(z+"-word-hidden"),E(t,a,s,i)}):t.split(/(\s+)/).forEach(function(t){var e;""!==t&&(/^\s+$/.test(t)?r.appendChild(document.createTextNode(t)):((e=document.createElement("span")).textContent=t,e.classList.add(z+"-word"),u(t)&&(a+=1,e.setAttribute("data-qmh-w",a)),n&&(a<n[0]||a>n[1])&&e.classList.add(z+"-word-hidden"),E(e,a,s,i),r.appendChild(e)))}),a}var C={crimson:"#a3263a",emerald:"#0f6b4f",lapis:"#1f4fb0",gold:"#8a5a00",violet:"#5b2aa8"};function x(t){var n;return null!=t&&(n=[],String(t).split(",").forEach(function(t){var a,e,r,t=t.trim().match(/^(\d+)\s*:\s*([\d+\-\s]*\d)\s*(\S.*)?$/);t&&(a=[],t[2].replace(/\s+/g,"").split("+").forEach(function(t){t=t.match(/^(\d+)(?:-(\d+))?$/);if(t)for(var e=parseInt(t[1],10),r=t[2]?parseInt(t[2],10):e,n=e;n<=r&&n-e<50;n++)1<=n&&a.push(n)}),a.length)&&(e=!1,(r=t[3]?t[3].trim().split(/\s+/):[]).length&&"blink"===r[r.length-1].toLowerCase()&&(e=!0,r.pop()),(r=r.length?r.join(" "):null)&&C[r.toLowerCase()]&&(r=C[r.toLowerCase()]),n.push({word:parseInt(t[1],10),letters:a,color:r,blink:e}))}),n.length)?n:null}var q=/[\u0300-\u036F\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E8\u06EA-\u06ED\u08D3-\u08FF\u200C\u200D\u0640]/;function A(t){var e=document.createElement("span");return e.className=z+"-ink",e.textContent=t,e}function _(e,r){var n=e.textContent,a=function(t){for(var e=[],r=0;r<t.length;r++){var n=t[r];q.test(n)||e.push(r)}return e.push(t.length),e}(n),s=[],i=0,l=!1;return Object.keys(r).map(Number).filter(function(t){return t<a.length}).sort(function(t,e){return t-e}).forEach(function(t){a[t-1]>i&&s.push(A(n.slice(i,a[t-1])));var e=document.createElement("span");e.className=z+"-letter",r[t].color&&e.style.setProperty("--qmh-letter",r[t].color),r[t].blink&&e.classList.add(z+"-letter-blink"),e.textContent=n.slice(a[t-1],a[t]),s.push(e),i=a[t],l=!0}),l&&(i<n.length&&s.push(A(n.slice(i))),e.textContent="",s.forEach(function(t){e.appendChild(t)}),e.classList.add(z+"-word-lettered"),1)}function k(t,e,r){var n,a,s;t.classList.remove(z+"-lettered"),e&&(M(t),n={},a={},t.querySelectorAll(`.${z}-word[data-qmh-w]`).forEach(function(t){n[t.getAttribute("data-qmh-w")]=t}),e.forEach(function(e){var r=a[e.word]=a[e.word]||{};e.letters.forEach(function(t){r[t]={color:e.color,blink:e.blink}})}),s=[],Object.keys(a).forEach(function(t){var e=n[parseInt(t,10)+(r||0)];e&&_(e,a[t])||s.push(t)}),s.length&&T("letters: no such word/letter for word(s) "+s.join(", ")),t.classList.add(z+"-lettered"))}function ut(e,r,t,n,a){function s(t){return!t||e+1>=t[0]&&e+r<=t[1]}function i(t){return t&&!s(t)&&e+r>=t[0]&&e+1<=t[1]}return G()||!s(t)||i(n)||i(a)?{ligature:!1}:(t=null,a&&s(a)?t="error":n&&s(n)&&(t="highlight"),{ligature:!0,cls:t})}function L(t,e){for(var r=0;r<t.r.length;r++)if(t.r[r].l===e)return t.r[r];return null}function S(t,e,r){var n,t=P.suras[t].ayas;return e<=0||void 0===(n=t[e-1])||n.p!==t[e].p||null===L(n,r)}function K(t,e,r,n,a){for(var s=P.suras[t].ayas,i=[],l=n+a;0<=l&&l<s.length&&void 0!==s[l]&&s[l].p==e;l+=a){var o=L(s[l],r);if(null===o)break;if(a<0){if(i.unshift(o),S(t,l,r))break}else i.push(o)}return i}function Q(t,e){for(var r=0;r<e.length;r++){var n=document.createElement("div");n.style.cssText="display:inline;visibility:hidden",G()?U(n,t,e[r]):n.textContent=e[r].t,t.appendChild(n)}}function I(){var t=this.parentElement.parentElement,e=t.querySelector("quran-madina-html-header"),r=(e=e?e.textContent.replace(/\s+/g," ").trim():null!=(e=t.getAttribute("sura"))?P.suras[e-1].name:"",(r=(t=(t=t).cloneNode(!0)).querySelector("quran-madina-html-header"))&&r.parentNode.removeChild(r),Array.from(t.querySelectorAll(`.${z}-word-hidden`)).forEach(function(t){t.parentNode.removeChild(t)}),Array.from(t.querySelectorAll("div,span")).forEach(function(t){/visibility:\s*hidden/.test(t.getAttribute("style")||"")&&t.parentNode.removeChild(t)}),t),t=r.querySelectorAll("quran-madina-html-line"),t=t.length?Array.from(t).map(function(t){return t.textContent.replace(/\s+/g," ").trim()}).filter(function(t){return""!==t}).join(" "):r.textContent.replace(/\s+/g," ").trim(),r="“"+(t=G()?b(t):t)+"”\n\n"+e;navigator.clipboard.writeText(r),alert("⎘ تم نسخ:\n\n"+r)}function $(){var t=this.parentElement.parentElement;let e;e=null!=t.getAttribute("sura")&&null!=t.getAttribute("aya")?`https://quran.com/${t.getAttribute("sura")}/`+t.getAttribute("aya"):"https://quran.com/page/"+t.getAttribute("page"),window.open(e,"_blank")}function ct(l,t,e,o,r,n,a,u,c,d){(c||d)&&M(l);var t=function(t,e,r){for(var n=[],a=null,s=0,i=P.suras.length,l=2===e&&0<t?1:e,o=0;o<i;o++){for(var u=(t+o)%i,c=P.suras[u].ayas,d=!1,h=0===o?l:0;h<c.length;h++){var p=R(u,h),f=c[h];if(void 0===f)break;for(var g=0;g<f.r.length;g++){var m,y=f.r[g];""!==y.t&&(m=f.p+":"+y.l,a&&a.key===m||(a={key:m,stretch:y.s,parts:[]},n.push(a)),a.parts.push({sura:u,ayaIdx:h,part:y,countable:p}))}if(p&&(s+=W(f))>=r[1]){d=!0;break}}if(d)break}for(var v=0,w=(n.forEach(function(t){t.parts.forEach(function(t){t.countable&&(v+=X(t.part))}),t.lastWord=v}),0);w<n.length-1&&n[w].lastWord<r[0];)w+=1;for(var b=w;b<n.length-1&&n[b].lastWord<r[1];)b+=1;return{groups:n.slice(w,b+1),counterStart:0<w?n[w-1].lastWord:0}}(t,e,o),s=t.groups,h=V(a,1<s.length),p=t.counterStart;J(),l.innerHTML="",l.removeAttribute("style"),l.classList.toggle(z+"-inline",!h&&!n),h&&(e=parseInt(s[0].key.split(":")[0],10),st(l,e,r),r||l.appendChild(nt(P.suras[s[0].parts[0].sura].name))),s.forEach(function(t,e){var r,i=document.createElement("quran-madina-html-line");h?(i.style.setProperty("display","block",""),G()&&(r=t.key.split(":"),it(i,parseInt(r[0],10),parseInt(r[1],10))),lt(i,t.stretch),S((r=t.parts[0]).sura,r.ayaIdx,r.part.l)||Q(i,K(r.sura,P.suras[r.sura].ayas[r.ayaIdx].p,r.part.l,r.ayaIdx,-1))):(i.style.setProperty("font-family",P.font_family,""),i.style.setProperty("font-size",P.font_size+"px","")),l.appendChild(i),t.parts.forEach(function(t){var e,r,n,a=document.createElement("div"),s=rt(t.sura+1,t.ayaIdx-1);DOMTokenList.prototype.add.apply(a.classList,s),t.countable?(n=0<(e=j(t.sura,t.ayaIdx)?X(t.part):0)?ut(p,e,o,c,d):{ligature:!1}).ligature?((r=document.createElement("span")).classList.add(z+"-word",z+"-basmala"),n.cls&&r.classList.add(z+"-word-"+n.cls),r.textContent=N,a.appendChild(r),p+=e):p=ot(a,t.part.t,o,p,c,d,t.part,i):u&&(0===t.ayaIdx||0===t.sura&&1===t.ayaIdx)?((n=document.createElement("span")).textContent=t.part.t,n.style.visibility="hidden",a.appendChild(n)):a.textContent=t.part.t,a.style.cssText="display:inline",i.appendChild(a),et(s.slice(-1)[0]),at(l,s.slice(-1)[0],t.sura+1,t.ayaIdx-1)}),h&&e===s.length-1&&(r=t.parts[t.parts.length-1],Q(i,K(r.sura,P.suras[r.sura].ayas[r.ayaIdx].p,r.part.l,r.ayaIdx,1)))})}var dt,t,c,e=16,r=[6,100],s=document.currentScript||document.querySelector(`script[src*="${z}"]`),ht=s.getAttribute("data-name")||"Madina05",pt=(s.getAttribute("data-font")||"me_quran").replaceAll(" ","_"),i=parseFloat(s.getAttribute("data-font-size"))||e,d=((i<r[0]||r[1]<i)&&(T(`font-size ${i} outside ${r[0]}..${r[1]}, using `+e),i=e),s.getAttribute("data-layout")||"1441H"),r=(["data-font","data-name"].forEach(function(t){s.hasAttribute(t)&&console.warn(z+`> ${t} is ignored since 2.0: every tag uses the v2 fonts `+'(choose a layout with data-layout="1441H" | "1405H" | "IndoPak")')}),{"1441H":"Madina-1441H","1405H":"Madina-1405H",indopak:"IndoPak-15"}),e=(void 0===r[d]&&(d=void 0!==r[String(d).toLowerCase()]?String(d).toLowerCase():(console.warn(z+`> unknown data-layout "${d}", using 1441H`),"1441H")),s.getAttribute("data-cdn")),e=(e&&(n=e.replace(/\/?$/,"/")),T(`layout: ${d} size: `+i),n+"dist/"+z+".min.css?v=1.1");function ft(t){c=n+`assets/db/${ht+`-${pt}-${i=t}px`}/`}document.getElementById(z)||(dt=document.getElementsByTagName("head")[0],(t=document.createElement("link")).id=z,t.rel="stylesheet",t.type="text/css",t.href=e,t.media="all",dt.appendChild(t)),ft(i);var gt,p={};function f(t,e){if(!P.juz)return-1;for(var r=Math.max(e,2),n=0,a=P.juz,s=0;s<a.length&&(a[s][0]<t||a[s][0]===t&&a[s][1]<=r);s++)n=s;return n}function mt(t,i){if(P.juz){for(var l,e=[],r=0;r<t.length;r++){var n=t[r];0<=n&&n<P.juz.length&&!p[n]&&e.indexOf(n)<0&&e.push(n)}0===e.length?i():(l=e.length,e.forEach(function(s){var t;a(c+"juz-"+((t=s+1)<10?"0":"")+t+".json",function(t){for(var e=t,r=0;r<e.d.length;r++){var n=e.d[r];P.suras[n[0]].ayas[n[1]]={p:n[2],r:n[3]}}if(e.L&&P.lines)for(var a in e.L)Object.prototype.hasOwnProperty.call(e.L,a)&&(P.lines[a-1]=e.L[a]);p[s]=!0,0==--l&&i()},function(t){console.error(z+"> failed to load juz "+(s+1),t),p[s]=!0,0==--l&&i()})}))}else i()}function yt(t){if(!P.juz)return[];var e,r=[];if(null!=t.getAttribute("sura")&&null!=t.getAttribute("aya")){var n,a=B(t.getAttribute("sura"))[0],s=O(t.getAttribute("aya"));if(null!=t.getAttribute("words"))return[n=f(a,s[0]),(n+1)%P.juz.length];for(e=f(a,s[0]);e<=f(a,s[1]);e++)r.push(e)}else{if(null==t.getAttribute("page"))return[];var i=P.pages[parseInt(t.getAttribute("page"),10)-1];if(!i)return[];for(e=f(i[0],i[1]);e<=f(i[2],i[3]);e++)r.push(e)}return r}function vt(){const t=new FontFace(P.font_family,"url("+encodeURI(null==(e=P.font_url)||/^(https?:)?\/\//.test(e)?e:n+String(e).replace(/^\//,""))+")");var e;t.load().then(()=>{document.fonts.add(t)});class r extends HTMLElement{static get observedAttributes(){return["page","page_param","aya","sura","words","highlight","error","headless","quotes","inline","notitle","letters"]}constructor(){super(),this._data={}}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}get page(){return this.getAttribute("page")}set page(t){this._data.page=t}get page_param(){return this.getAttribute("page_param")}set page_param(t){this._data.page_param=t}get aya(){return this.getAttribute("aya")}set aya(t){this._data.aya=t}get sura(){return this.getAttribute("sura")}set sura(t){this._data.sura=t}get words(){return this.getAttribute("words")}set words(t){this._data.words=t}get highlight(){return this.getAttribute("highlight")}set highlight(t){this._data.highlight=t}get error(){return this.getAttribute("error")}set error(t){this._data.error=t}get letters(){return this.getAttribute("letters")}set letters(t){this._data.letters=t}get headless(){return this.getAttribute("headless")}set headless(t){this._data.headless=t}get quotes(){return this.getAttribute("quotes")}set quotes(t){this._data.quotes=t}get inline(){return this.getAttribute("inline")}set inline(t){this._data.inline=t}get notitle(){return this.getAttribute("notitle")}set notitle(t){this._data.notitle=t}render(e=this){var r;this._data.rendering||mt(yt(r=this),function(){if(!r._data.rendering){r._data.rendering=!0;try{e._qmhLetterShift=0;var t=x(r.letters);null!=r.letters&&null==t&&T("Bad letters parameter: "+r.letters),e._qmhLetters=t,r.doRender(e),k(e,t,e._qmhLetterShift)}finally{r._data.rendering=!1}}})}doRender(e){var r,n,a,s,t=null,i=null!=this.highlight?D(this.highlight):null,l=(null!=this.highlight&&null==i&&T("Bad highlight parameter: "+this.highlight),null!=this.error?D(this.error):null),o=(null!=this.error&&null==l&&T("Bad error parameter: "+this.error),this.page),u=H(this.headless),c=H(this.notitle),d=null!=this.quotes&&!H(this.quotes),h=this.inline,p=null!=this.sura&&null!=this.aya;if(p)y=g=B(this.sura)[0],a=!1,[m,v]=O(this.aya),null!=this.page&&T("Ignoring page parameter!"),o=P.suras[g].ayas[m].p;else{if(null==this.page)return console.error(z+"> Bad arguments: Not rendering!"),1;if(P.pages)var f=P.pages[o-1],g=f[0],m=f[1],y=f[2],v=f[3];else{for(v=m=y=g=0;P.suras[g].ayas.slice(-1)[0].p<o;)g+=1;for(y=g;y<114&&P.suras[y].ayas[0].p<=o;)y+=1;for(y-=1;P.suras[g].ayas[m].p<o;)m+=1;for(v=P.suras[y].ayas.length-1;P.suras[y].ayas[v].p>o;)v-=1}a=!0,null!=this.inline&&T("Ignoring inline parameter with page!")}if(null!=this.words)if(p){if(null!=(t=D(this.words)))return v!==m&&T("Ignoring aya range end with words parameter!"),500<t[1]-t[0]+1&&(T("words selection capped at 500 words"),t[1]=t[0]+499),void ct(e,g,m,t,u,d,h,c,i=F("highlight",i,t),l=F("error",l,t));T("Bad words parameter: "+this.words)}else T("Ignoring words parameter with page!");if(p&&Y(P.suras[y].ayas[v])!=o)s=2===m&&0<g?W(P.suras[g].ayas[1]):0,f=Z(g,m,v),c=function(t){return null==t?null:[t[0]+s,t[1]+s]},e._qmhLetterShift=s,i=c(F("highlight",i,[1,f])),l=c(F("error",l,[1,f])),ct(e,g,m,[s+1,s+f],u,d,h,!1,i,l);else{var t=p?Z(g,m,v):tt(g,m,y,v,parseInt(o,10)),i=F("highlight",i,[1,t]),l=F("error",l,[1,t]),w=null!=i||null!=l||null!=e._qmhLetters,b=(w&&M(e),J(),e.innerHTML="",e.removeAttribute("style"),a=V(h,a=(r=P.suras[g].ayas[m].r[0].l)!=(n=P.suras[y].ayas[v].r.slice(-1)[0].l)?!0:a),e.classList.toggle(z+"-inline",!a&&!d),a&&(st(e,o,u),u||e.appendChild(nt(P.suras[g].name))),m),E=g,C=0;for(let t=r;t<=n;t++){const $=t;var x=document.createElement("quran-madina-html-line"),q=(e.appendChild(x),a?(x.style.setProperty("display","block",""),G()&&it(x,parseInt(o,10),t)):(x.style.setProperty("font-family",P.font_family,""),x.style.setProperty("font-size",P.font_size+"px","")),a&&p&&t===r&&Q(x,K(g,o,r,m,-1)),g==y?v:P.suras[E].ayas.length-1);for(let t=b;t<=Math.min(b+5,q);t++){var A,_,k,L,S,I=P.suras[E].ayas[t];void 0!==I&&I.p==o&&(I=I.r.filter(t=>t.l==$)).length&&(a&&lt(x,I[0].s),A=document.createElement("div"),_=rt(E+1,t-1),DOMTokenList.prototype.add.apply(A.classList,_),w&&R(E,t)?(S=0<(k=j(E,t)?X(I[0]):0)?ut(C,k,null,i,l):{ligature:!1}).ligature?((L=document.createElement("span")).classList.add(z+"-word",z+"-basmala"),S.cls&&L.classList.add(z+"-word-"+S.cls),L.textContent=N,A.appendChild(L),C+=k):C=ot(A,I[0].t,null,C,i,l,I[0],x):G()&&R(E,t)?U(A,x,I[0]):A.textContent=!G()&&j(E,t)&&""!==I[0].t?N:I[0].t,A.style.cssText="display:inline",x.appendChild(A),et(_.slice(-1)[0]),at(e,_.slice(-1)[0],E+1,t-1),b=t,S=E<113?P.suras[E+1].ayas[0]:void 0,q<=b)&&void 0!==S&&S.p==o&&S.r[0].l==$+1&&(E+=1,b=0)}a&&p&&t===n&&Q(x,K(y,o,n,v,1))}}}}customElements.define(z,r)}function wt(){P.font_size=i,P.line_width_em=P.line_width,P.line_width=P.line_width*i,vt()}a((c=n+`assets/db/v2/${gt=r[d]||"Madina-"+d}/`)+"manifest.json",function(e){if(e.content_hash){var t="quran-madina-html:hash:"+c,r=null;try{r=sessionStorage.getItem(t)}catch(t){}null!==r&&r!==e.content_hash&&m(c);try{sessionStorage.setItem(t,e.content_hash)}catch(t){}}P={},Object.keys(e).forEach(function(t){"suras"!==t&&"n_pages"!==t&&(P[t]=e[t])}),P.suras=e.suras.map(function(t){return{name:t[0],ayas:new Array(t[1])}}),P.lines=new Array(e.n_pages),wt()},function(){c=null,a(n+`assets/db/v2/${gt}.json`,function(t){P=t,wt()},function(t){console.error(z+"> no v2 DB for layout "+d,t)})},!0)}();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quran-madina-html",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "devDependencies": {
5
5
  "grunt": "^1.6.2",
6
6
  "grunt-contrib-clean": "^2.0.1",
@@ -39,7 +39,7 @@
39
39
  "madina"
40
40
  ],
41
41
  "author": "Tarek Eldeeb",
42
- "license": "ISC",
42
+ "license": "SEE LICENSE IN LICENSE.md",
43
43
  "bugs": {
44
44
  "url": "https://github.com/tarekeldeeb/quran-madina-html/issues"
45
45
  },