node-tp2-vidar 1.0.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/.claude/settings.local.json +7 -0
- package/.idea/Node-TP2.iml +8 -0
- package/.idea/SweepConfig.xml +14 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/README.md +413 -0
- package/index.js +209 -0
- package/package.json +21 -0
- package/test.js +27 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="WEB_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$" />
|
|
5
|
+
<orderEntry type="inheritedJdk" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
</module>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="dev.sweep.assistant.components.SweepConfig">
|
|
4
|
+
<option name="autoApprovedTools">
|
|
5
|
+
<set>
|
|
6
|
+
<option value="list_files" />
|
|
7
|
+
<option value="read_file" />
|
|
8
|
+
<option value="search_files" />
|
|
9
|
+
<option value="find_usages" />
|
|
10
|
+
<option value="get_errors" />
|
|
11
|
+
</set>
|
|
12
|
+
</option>
|
|
13
|
+
</component>
|
|
14
|
+
</project>
|
package/.idea/vcs.xml
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# node-tp2
|
|
2
|
+
|
|
3
|
+
Package NPM combinant un générateur de **Lorem Ipsum** et un afficheur de cartes **Magic: The Gathering** via l'API publique Scryfall.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table des matières
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [Partie 1 — Lorem Ipsum](#partie-1--lorem-ipsum)
|
|
11
|
+
- [Fonctionnement interne](#fonctionnement-interne)
|
|
12
|
+
- [Usage en JavaScript](#usage-en-javascript)
|
|
13
|
+
- [Usage via le DOM](#usage-via-le-dom)
|
|
14
|
+
- [Partie 2 — Magic: The Gathering](#partie-2--magic-the-gathering)
|
|
15
|
+
- [Fonctionnement interne](#fonctionnement-interne-1)
|
|
16
|
+
- [Usage en JavaScript](#usage-en-javascript-1)
|
|
17
|
+
- [Usage via le DOM](#usage-via-le-dom-1)
|
|
18
|
+
- [Exemple HTML complet](#exemple-html-complet)
|
|
19
|
+
- [Référence API](#référence-api)
|
|
20
|
+
- [Publier sur NPM](#publier-sur-npm)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install node-tp2
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Ou, en clonant le dépôt directement :
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
git clone https://github.com/VidarIvory/Node-TP2.git
|
|
34
|
+
cd Node-TP2
|
|
35
|
+
npm install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Partie 1 — Lorem Ipsum
|
|
41
|
+
|
|
42
|
+
### Fonctionnement interne
|
|
43
|
+
|
|
44
|
+
Le générateur s'appuie sur une liste de **~130 mots** inspirés du latin classique (tiré du *De Finibus Bonorum et Malorum* de Cicéron). Aucune dépendance externe n'est requise.
|
|
45
|
+
|
|
46
|
+
Le processus de génération fonctionne en trois niveaux :
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Mot → Phrase → Paragraphe → Texte final
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
| Niveau | Détail |
|
|
53
|
+
|------------|------------------------------------------------|
|
|
54
|
+
| Mot | Tiré aléatoirement dans la liste de 130 mots |
|
|
55
|
+
| Phrase | 5 à 14 mots, première lettre en majuscule, terminée par un point |
|
|
56
|
+
| Paragraphe | 3 à 6 phrases assemblées |
|
|
57
|
+
| Texte | N paragraphes séparés par une ligne vide |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Usage en JavaScript
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
const { Ipsum } = require('node-tp2');
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Générer des paragraphes
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
const lorem = new Ipsum({ paragrafs: 5 });
|
|
71
|
+
|
|
72
|
+
console.log(lorem.text);
|
|
73
|
+
// Affiche 5 paragraphes de texte latin aléatoire
|
|
74
|
+
|
|
75
|
+
// L'objet est aussi directement convertible en chaîne
|
|
76
|
+
console.log(String(lorem));
|
|
77
|
+
console.log(`${lorem}`);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Générer un nombre précis de mots
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const lorem = new Ipsum({ words: 50 });
|
|
84
|
+
|
|
85
|
+
console.log(lorem.text);
|
|
86
|
+
// Affiche exactement 50 mots séparés par des espaces
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Valeur par défaut
|
|
90
|
+
|
|
91
|
+
Sans option, 1 paragraphe est généré :
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
const lorem = new Ipsum();
|
|
95
|
+
console.log(lorem.text); // 1 paragraphe
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Usage via le DOM
|
|
101
|
+
|
|
102
|
+
La méthode statique `Ipsum.init()` parcourt automatiquement la page HTML et remplit tous les éléments dont la classe contient le mot `ipsum`.
|
|
103
|
+
|
|
104
|
+
**Appel unique à placer en fin de page ou dans un DOMContentLoaded :**
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<script>
|
|
108
|
+
const { Ipsum } = require('node-tp2');
|
|
109
|
+
Ipsum.init();
|
|
110
|
+
</script>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Classes CSS reconnues
|
|
114
|
+
|
|
115
|
+
| Classe CSS | Comportement |
|
|
116
|
+
|--------------------|---------------------------------------------------|
|
|
117
|
+
| `ipsum` | Insère 3 paragraphes dans des balises `<p>` |
|
|
118
|
+
| `ipsum-p-{n}` | Insère **n** paragraphes dans des balises `<p>` |
|
|
119
|
+
| `ipsum-w-{n}` | Insère exactement **n** mots en texte brut |
|
|
120
|
+
|
|
121
|
+
#### Exemples HTML
|
|
122
|
+
|
|
123
|
+
```html
|
|
124
|
+
<!-- 3 paragraphes par défaut -->
|
|
125
|
+
<div class="ipsum"></div>
|
|
126
|
+
|
|
127
|
+
<!-- 12 paragraphes -->
|
|
128
|
+
<div class="ipsum-p-12"></div>
|
|
129
|
+
|
|
130
|
+
<!-- 122 mots -->
|
|
131
|
+
<div class="ipsum-w-122"></div>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Résultat généré dans le DOM pour `ipsum-p-2` :
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<div class="ipsum-p-2">
|
|
138
|
+
<p>Lorem qui eos reiciendis error commodo itaque mollit quae.</p>
|
|
139
|
+
<p>Ratione omnis quaerat reprehenderit aut voluptatem veniam modi deserunt.</p>
|
|
140
|
+
</div>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Partie 2 — Magic: The Gathering
|
|
146
|
+
|
|
147
|
+
### Fonctionnement interne
|
|
148
|
+
|
|
149
|
+
La classe `Mtg` communique avec l'[API publique Scryfall](https://scryfall.com/docs/api) pour récupérer les données d'une carte MTG au format JSON.
|
|
150
|
+
|
|
151
|
+
Deux modes sont disponibles :
|
|
152
|
+
|
|
153
|
+
| Mode | Endpoint Scryfall appelé |
|
|
154
|
+
|---------------|---------------------------------------------------|
|
|
155
|
+
| Aléatoire | `GET https://api.scryfall.com/cards/random` |
|
|
156
|
+
| Par édition | `GET https://api.scryfall.com/cards/{ed}/{id}` |
|
|
157
|
+
|
|
158
|
+
Les paramètres `ed` et `id` correspondent au **code de l'édition** (ex: `dom`, `mh2`, `lea`) et au **numéro de collectionneur** de la carte dans cette édition.
|
|
159
|
+
|
|
160
|
+
> Les codes d'édition Scryfall sont consultables sur [scryfall.com/sets](https://scryfall.com/sets).
|
|
161
|
+
|
|
162
|
+
L'instance `Mtg` est **thenable** : elle peut être utilisée directement avec `await` ou `.then()`, sans appeler de méthode supplémentaire. La requête HTTP démarre dès la construction de l'objet.
|
|
163
|
+
|
|
164
|
+
Compatibilité réseau :
|
|
165
|
+
- Navigateur et Node.js 18+ : utilise `fetch` natif
|
|
166
|
+
- Node.js < 18 : utilise le module `https` de la librairie standard
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Usage en JavaScript
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
const { Mtg } = require('node-tp2');
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Carte aléatoire
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
const card = await new Mtg({ rand: true });
|
|
180
|
+
|
|
181
|
+
console.log(card.name); // "Black Lotus"
|
|
182
|
+
console.log(card.type_line); // "Artifact"
|
|
183
|
+
console.log(card.set_name); // "Limited Edition Alpha"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Carte spécifique (édition + numéro)
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
// Carte #1 de l'édition "Dominaria" (code: dom)
|
|
190
|
+
const card = await new Mtg({ ed: 'dom', id: 1 });
|
|
191
|
+
|
|
192
|
+
console.log(card.name); // "Karn, Scion of Urza"
|
|
193
|
+
console.log(card.collector_number); // "1"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Avec `.then()` / `.catch()`
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
new Mtg({ rand: true })
|
|
200
|
+
.then(card => console.log(card.name))
|
|
201
|
+
.catch(err => console.error('Erreur:', err.message));
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Accéder aux données après résolution
|
|
205
|
+
|
|
206
|
+
Une fois l'instance résolue, la carte est stockée dans `instance.card` :
|
|
207
|
+
|
|
208
|
+
```js
|
|
209
|
+
const mtg = new Mtg({ rand: true });
|
|
210
|
+
await mtg;
|
|
211
|
+
|
|
212
|
+
console.log(mtg.card.name);
|
|
213
|
+
console.log(mtg.card.image_uris.normal); // URL de l'image
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Structure de la réponse Scryfall (extrait)
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"name": "Karn, Scion of Urza",
|
|
221
|
+
"type_line": "Legendary Planeswalker — Karn",
|
|
222
|
+
"set": "dom",
|
|
223
|
+
"set_name": "Dominaria",
|
|
224
|
+
"collector_number": "1",
|
|
225
|
+
"image_uris": {
|
|
226
|
+
"normal": "https://cards.scryfall.io/normal/..."
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### Usage via le DOM
|
|
234
|
+
|
|
235
|
+
La méthode statique `Mtg.init()` parcourt la page et remplit automatiquement tous les éléments dont la classe commence par `mtg-`.
|
|
236
|
+
|
|
237
|
+
```html
|
|
238
|
+
<script>
|
|
239
|
+
const { Mtg } = require('node-tp2');
|
|
240
|
+
Mtg.init();
|
|
241
|
+
</script>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
#### Classes CSS reconnues
|
|
245
|
+
|
|
246
|
+
| Classe CSS | Comportement |
|
|
247
|
+
|-------------------------|-------------------------------------------------------|
|
|
248
|
+
| `mtg-rand` | Affiche une carte aléatoire |
|
|
249
|
+
| `mtg-ed{code}-id{n}` | Affiche la carte #n de l'édition dont le code est `code` |
|
|
250
|
+
|
|
251
|
+
#### Exemples HTML
|
|
252
|
+
|
|
253
|
+
```html
|
|
254
|
+
<!-- Carte aléatoire -->
|
|
255
|
+
<div class="mtg-rand"></div>
|
|
256
|
+
|
|
257
|
+
<!-- Carte #122 de l'édition "Dominaria" (code: dom) -->
|
|
258
|
+
<div class="mtg-eddom-id122"></div>
|
|
259
|
+
|
|
260
|
+
<!-- Carte #45 de l'édition "Modern Horizons 2" (code: mh2) -->
|
|
261
|
+
<div class="mtg-edmh2-id45"></div>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### Rendu HTML généré
|
|
265
|
+
|
|
266
|
+
Chaque `<div>` est rempli avec la structure suivante :
|
|
267
|
+
|
|
268
|
+
```html
|
|
269
|
+
<div class="mtg-card">
|
|
270
|
+
<img src="https://cards.scryfall.io/normal/..." alt="Nom de la carte" style="max-width:100%">
|
|
271
|
+
<div class="mtg-card-info">
|
|
272
|
+
<strong>Nom de la carte</strong>
|
|
273
|
+
<em>Type de la carte</em>
|
|
274
|
+
<span>Nom de l'édition — #Numéro</span>
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> En cas d'erreur (carte introuvable, réseau indisponible), l'élément affiche `Carte introuvable.` et l'erreur est loggée dans la console.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Exemple HTML complet
|
|
284
|
+
|
|
285
|
+
```html
|
|
286
|
+
<!DOCTYPE html>
|
|
287
|
+
<html lang="fr">
|
|
288
|
+
<head>
|
|
289
|
+
<meta charset="UTF-8">
|
|
290
|
+
<title>Démo node-tp2</title>
|
|
291
|
+
<style>
|
|
292
|
+
body { font-family: sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 1rem; }
|
|
293
|
+
.mtg-card { border: 1px solid #ccc; padding: 1rem; border-radius: 8px; max-width: 300px; }
|
|
294
|
+
.mtg-card-info { margin-top: .5rem; display: flex; flex-direction: column; gap: .25rem; }
|
|
295
|
+
</style>
|
|
296
|
+
</head>
|
|
297
|
+
<body>
|
|
298
|
+
|
|
299
|
+
<h1>Lorem Ipsum</h1>
|
|
300
|
+
|
|
301
|
+
<h2>Défaut (3 paragraphes)</h2>
|
|
302
|
+
<div class="ipsum"></div>
|
|
303
|
+
|
|
304
|
+
<h2>5 paragraphes</h2>
|
|
305
|
+
<div class="ipsum-p-5"></div>
|
|
306
|
+
|
|
307
|
+
<h2>20 mots</h2>
|
|
308
|
+
<div class="ipsum-w-20"></div>
|
|
309
|
+
|
|
310
|
+
<hr>
|
|
311
|
+
|
|
312
|
+
<h1>Magic: The Gathering</h1>
|
|
313
|
+
|
|
314
|
+
<h2>Carte aléatoire</h2>
|
|
315
|
+
<div class="mtg-rand"></div>
|
|
316
|
+
|
|
317
|
+
<h2>Karn, Scion of Urza (dom #1)</h2>
|
|
318
|
+
<div class="mtg-eddom-id1"></div>
|
|
319
|
+
|
|
320
|
+
<script src="https://unpkg.com/node-tp2/index.js"></script>
|
|
321
|
+
<script>
|
|
322
|
+
const { Ipsum, Mtg } = require('node-tp2');
|
|
323
|
+
Ipsum.init();
|
|
324
|
+
Mtg.init();
|
|
325
|
+
</script>
|
|
326
|
+
|
|
327
|
+
</body>
|
|
328
|
+
</html>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Référence API
|
|
334
|
+
|
|
335
|
+
### `new Ipsum(options)`
|
|
336
|
+
|
|
337
|
+
| Option | Type | Description |
|
|
338
|
+
|--------------|--------|---------------------------------------------|
|
|
339
|
+
| `paragrafs` | number | Nombre de paragraphes à générer (défaut: 1) |
|
|
340
|
+
| `words` | number | Nombre exact de mots à générer |
|
|
341
|
+
|
|
342
|
+
> Si les deux options sont fournies, `words` est prioritaire.
|
|
343
|
+
|
|
344
|
+
**Propriétés de l'instance :**
|
|
345
|
+
|
|
346
|
+
| Propriété | Type | Description |
|
|
347
|
+
|-----------|--------|-------------------------------------|
|
|
348
|
+
| `text` | string | Le texte généré |
|
|
349
|
+
|
|
350
|
+
**Méthodes statiques :**
|
|
351
|
+
|
|
352
|
+
| Méthode | Description |
|
|
353
|
+
|----------------|---------------------------------------------------------------|
|
|
354
|
+
| `Ipsum.init()` | Scanne le DOM et remplit les éléments avec la classe `ipsum*` |
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
### `new Mtg(options)`
|
|
359
|
+
|
|
360
|
+
| Option | Type | Description |
|
|
361
|
+
|--------|---------|------------------------------------------------------|
|
|
362
|
+
| `rand` | boolean | Si `true`, récupère une carte aléatoire |
|
|
363
|
+
| `ed` | string | Code de l'édition Scryfall (ex: `dom`, `mh2`, `lea`) |
|
|
364
|
+
| `id` | number | Numéro de collectionneur de la carte dans l'édition |
|
|
365
|
+
|
|
366
|
+
> `ed` et `id` doivent être fournis ensemble. `rand: true` prend le dessus.
|
|
367
|
+
|
|
368
|
+
**L'instance est thenable :** elle peut être passée directement à `await` ou `.then()`.
|
|
369
|
+
|
|
370
|
+
**Propriétés de l'instance (après résolution) :**
|
|
371
|
+
|
|
372
|
+
| Propriété | Type | Description |
|
|
373
|
+
|-----------|--------|------------------------------------------|
|
|
374
|
+
| `card` | object | Objet JSON complet retourné par Scryfall |
|
|
375
|
+
|
|
376
|
+
**Méthodes statiques :**
|
|
377
|
+
|
|
378
|
+
| Méthode | Description |
|
|
379
|
+
|--------------|-----------------------------------------------------------------|
|
|
380
|
+
| `Mtg.init()` | Scanne le DOM et remplit les éléments avec la classe `mtg-*` |
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Publier sur NPM
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
# 1. Se connecter à son compte NPM
|
|
388
|
+
npm login
|
|
389
|
+
|
|
390
|
+
# 2. Vérifier que le nom du package est disponible
|
|
391
|
+
npm search node-tp2
|
|
392
|
+
|
|
393
|
+
# 3. Publier
|
|
394
|
+
npm publish
|
|
395
|
+
|
|
396
|
+
# 4. Pour mettre à jour plus tard : incrémenter la version puis republier
|
|
397
|
+
npm version patch # 1.0.0 → 1.0.1
|
|
398
|
+
npm version minor # 1.0.0 → 1.1.0
|
|
399
|
+
npm version major # 1.0.0 → 2.0.0
|
|
400
|
+
npm publish
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
> Le champ `"name"` dans `package.json` doit être unique sur le registre NPM. Si `node-tp2` est déjà pris, choisir un nom différent ou utiliser un scope : `@votre-username/node-tp2`.
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Auteur
|
|
408
|
+
|
|
409
|
+
**Vidar Ivory** — [GitHub](https://github.com/VidarIvory)
|
|
410
|
+
|
|
411
|
+
## Licence
|
|
412
|
+
|
|
413
|
+
ISC
|
package/index.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ─── Word List ────────────────────────────────────────────────────────────────
|
|
4
|
+
const WORD_LIST = [
|
|
5
|
+
'lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipiscing', 'elit',
|
|
6
|
+
'sed', 'do', 'eiusmod', 'tempor', 'incididunt', 'ut', 'labore', 'et', 'dolore',
|
|
7
|
+
'magna', 'aliqua', 'enim', 'ad', 'minim', 'veniam', 'quis', 'nostrud',
|
|
8
|
+
'exercitation', 'ullamco', 'laboris', 'nisi', 'aliquip', 'ex', 'ea', 'commodo',
|
|
9
|
+
'consequat', 'duis', 'aute', 'irure', 'reprehenderit', 'voluptate', 'velit',
|
|
10
|
+
'esse', 'cillum', 'fugiat', 'nulla', 'pariatur', 'excepteur', 'sint', 'occaecat',
|
|
11
|
+
'cupidatat', 'non', 'proident', 'sunt', 'culpa', 'qui', 'officia', 'deserunt',
|
|
12
|
+
'mollit', 'anim', 'est', 'laborum', 'perspiciatis', 'unde', 'omnis', 'iste',
|
|
13
|
+
'natus', 'error', 'voluptatem', 'accusantium', 'doloremque', 'laudantium',
|
|
14
|
+
'totam', 'rem', 'aperiam', 'eaque', 'ipsa', 'quae', 'illo', 'inventore',
|
|
15
|
+
'veritatis', 'quasi', 'architecto', 'beatae', 'vitae', 'dicta', 'explicabo',
|
|
16
|
+
'nemo', 'ipsam', 'quia', 'voluptas', 'aspernatur', 'odit', 'aut', 'fugit',
|
|
17
|
+
'magni', 'dolores', 'eos', 'ratione', 'sequi', 'nesciunt', 'neque', 'porro',
|
|
18
|
+
'quisquam', 'dolorem', 'adipisci', 'numquam', 'eius', 'modi', 'tempora',
|
|
19
|
+
'quaerat', 'minima', 'nostrum', 'exercitationem', 'ullam', 'corporis', 'suscipit',
|
|
20
|
+
'laboriosam', 'quid', 'rerum', 'facilis', 'provident', 'similique', 'dignissimos',
|
|
21
|
+
'blanditiis', 'praesentium', 'voluptatum', 'deleniti', 'atque', 'corrupti',
|
|
22
|
+
'quos', 'maxime', 'placeat', 'facere', 'possimus', 'omnis', 'voluptas',
|
|
23
|
+
'assumenda', 'repellendus', 'temporibus', 'autem', 'quibusdam', 'officiis',
|
|
24
|
+
'debitis', 'rerum', 'necessitatibus', 'saepe', 'eveniet', 'voluptates',
|
|
25
|
+
'repudiandae', 'recusandae', 'itaque', 'earum', 'hic', 'tenetur', 'sapiente',
|
|
26
|
+
'delectus', 'reiciendis', 'voluptatibus', 'maiores', 'alias', 'consequatur',
|
|
27
|
+
'aut', 'perferendis', 'doloribus', 'asperiores', 'repellat'
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// ─── Ipsum Class ──────────────────────────────────────────────────────────────
|
|
31
|
+
class Ipsum {
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.text = this._generate();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_randomWord() {
|
|
38
|
+
return WORD_LIST[Math.floor(Math.random() * WORD_LIST.length)];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_generateSentence() {
|
|
42
|
+
const length = Math.floor(Math.random() * 10) + 5; // 5–14 words
|
|
43
|
+
const words = [];
|
|
44
|
+
for (let i = 0; i < length; i++) words.push(this._randomWord());
|
|
45
|
+
words[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);
|
|
46
|
+
return words.join(' ') + '.';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_generateParagraph() {
|
|
50
|
+
const sentenceCount = Math.floor(Math.random() * 4) + 3; // 3–6 sentences
|
|
51
|
+
const sentences = [];
|
|
52
|
+
for (let i = 0; i < sentenceCount; i++) sentences.push(this._generateSentence());
|
|
53
|
+
return sentences.join(' ');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_generateWords(count) {
|
|
57
|
+
const words = [];
|
|
58
|
+
for (let i = 0; i < count; i++) words.push(this._randomWord());
|
|
59
|
+
return words.join(' ');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_generate() {
|
|
63
|
+
if (this.options.words) {
|
|
64
|
+
return this._generateWords(this.options.words);
|
|
65
|
+
}
|
|
66
|
+
const count = this.options.paragrafs || 1;
|
|
67
|
+
const paragraphs = [];
|
|
68
|
+
for (let i = 0; i < count; i++) paragraphs.push(this._generateParagraph());
|
|
69
|
+
return paragraphs.join('\n\n');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
toString() {
|
|
73
|
+
return this.text;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Scans the DOM and fills elements based on their CSS class
|
|
77
|
+
static init() {
|
|
78
|
+
if (typeof document === 'undefined') return;
|
|
79
|
+
|
|
80
|
+
document.querySelectorAll('[class*="ipsum"]').forEach(el => {
|
|
81
|
+
const cls = el.className;
|
|
82
|
+
const pMatch = cls.match(/ipsum-p-(\d+)/);
|
|
83
|
+
const wMatch = cls.match(/ipsum-w-(\d+)/);
|
|
84
|
+
|
|
85
|
+
if (pMatch) {
|
|
86
|
+
const ipsum = new Ipsum({ paragrafs: parseInt(pMatch[1]) });
|
|
87
|
+
const paras = ipsum.text.split('\n\n');
|
|
88
|
+
el.innerHTML = paras.map(p => `<p>${p}</p>`).join('');
|
|
89
|
+
} else if (wMatch) {
|
|
90
|
+
const ipsum = new Ipsum({ words: parseInt(wMatch[1]) });
|
|
91
|
+
el.textContent = ipsum.text;
|
|
92
|
+
} else {
|
|
93
|
+
// Default: class="ipsum" → 3 paragraphs
|
|
94
|
+
const ipsum = new Ipsum({ paragrafs: 3 });
|
|
95
|
+
const paras = ipsum.text.split('\n\n');
|
|
96
|
+
el.innerHTML = paras.map(p => `<p>${p}</p>`).join('');
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── HTTP Helper ──────────────────────────────────────────────────────────────
|
|
103
|
+
function httpGet(url) {
|
|
104
|
+
// Use native fetch if available (browser or Node 18+)
|
|
105
|
+
if (typeof fetch !== 'undefined') {
|
|
106
|
+
return fetch(url).then(r => {
|
|
107
|
+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
108
|
+
return r.json();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fallback for older Node.js versions
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
const https = require('https');
|
|
115
|
+
https.get(url, res => {
|
|
116
|
+
let data = '';
|
|
117
|
+
res.on('data', chunk => (data += chunk));
|
|
118
|
+
res.on('end', () => {
|
|
119
|
+
try { resolve(JSON.parse(data)); }
|
|
120
|
+
catch (e) { reject(e); }
|
|
121
|
+
});
|
|
122
|
+
}).on('error', reject);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ─── Mtg Class ────────────────────────────────────────────────────────────────
|
|
127
|
+
class Mtg {
|
|
128
|
+
constructor(options = {}) {
|
|
129
|
+
this.options = options;
|
|
130
|
+
this.card = null;
|
|
131
|
+
this._promise = this._fetch();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
_fetch() {
|
|
135
|
+
let url;
|
|
136
|
+
|
|
137
|
+
if (this.options.rand) {
|
|
138
|
+
url = 'https://api.scryfall.com/cards/random';
|
|
139
|
+
} else if (this.options.ed !== undefined && this.options.id !== undefined) {
|
|
140
|
+
// Scryfall: /cards/{set_code}/{collector_number}
|
|
141
|
+
url = `https://api.scryfall.com/cards/${this.options.ed}/${this.options.id}`;
|
|
142
|
+
} else {
|
|
143
|
+
return Promise.reject(new Error('MTG: provide { rand: true } or { ed, id }'));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return httpGet(url).then(card => {
|
|
147
|
+
this.card = card;
|
|
148
|
+
return card;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Makes Mtg instances thenable (await new Mtg({...}))
|
|
153
|
+
then(resolve, reject) {
|
|
154
|
+
return this._promise.then(resolve, reject);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
catch(reject) {
|
|
158
|
+
return this._promise.catch(reject);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
static _renderCard(card, el) {
|
|
162
|
+
const img =
|
|
163
|
+
card.image_uris?.normal ||
|
|
164
|
+
card.card_faces?.[0]?.image_uris?.normal ||
|
|
165
|
+
'';
|
|
166
|
+
|
|
167
|
+
el.innerHTML = `
|
|
168
|
+
<div class="mtg-card">
|
|
169
|
+
${img ? `<img src="${img}" alt="${card.name}" style="max-width:100%">` : ''}
|
|
170
|
+
<div class="mtg-card-info">
|
|
171
|
+
<strong>${card.name}</strong>
|
|
172
|
+
<em>${card.type_line || ''}</em>
|
|
173
|
+
<span>${card.set_name || ''} — #${card.collector_number || ''}</span>
|
|
174
|
+
</div>
|
|
175
|
+
</div>`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Scans the DOM and fills elements based on their CSS class
|
|
179
|
+
static init() {
|
|
180
|
+
if (typeof document === 'undefined') return;
|
|
181
|
+
|
|
182
|
+
document.querySelectorAll('[class*="mtg-"]').forEach(async el => {
|
|
183
|
+
const cls = el.className;
|
|
184
|
+
let mtgInstance;
|
|
185
|
+
|
|
186
|
+
if (cls.includes('mtg-rand')) {
|
|
187
|
+
mtgInstance = new Mtg({ rand: true });
|
|
188
|
+
} else {
|
|
189
|
+
// Matches: mtg-ed{set}-id{number} e.g. mtg-ed12-id122 or mtg-edMH2-id45
|
|
190
|
+
const match = cls.match(/mtg-ed(\w+)-id(\d+)/);
|
|
191
|
+
if (match) {
|
|
192
|
+
mtgInstance = new Mtg({ ed: match[1], id: match[2] });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (mtgInstance) {
|
|
197
|
+
try {
|
|
198
|
+
const card = await mtgInstance;
|
|
199
|
+
Mtg._renderCard(card, el);
|
|
200
|
+
} catch (e) {
|
|
201
|
+
el.textContent = 'Carte introuvable.';
|
|
202
|
+
console.error(e);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = { Ipsum, Mtg };
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-tp2-vidar",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lorem ipsum generator and MTG card viewer via Scryfall API",
|
|
5
|
+
"homepage": "https://github.com/VidarIvory/Node-TP2#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/VidarIvory/Node-TP2/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/VidarIvory/Node-TP2.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"author": "Vidar Ivory",
|
|
15
|
+
"type": "commonjs",
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"keywords": ["lorem", "ipsum", "mtg", "magic", "scryfall", "generator"],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "node test.js"
|
|
20
|
+
}
|
|
21
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { Ipsum, Mtg } = require('./index');
|
|
3
|
+
|
|
4
|
+
// ─── Test Ipsum ───────────────────────────────────────────────────────────────
|
|
5
|
+
console.log('=== IPSUM TESTS ===\n');
|
|
6
|
+
|
|
7
|
+
const byParagrafs = new Ipsum({ paragrafs: 2 });
|
|
8
|
+
console.log('2 paragraphs:');
|
|
9
|
+
console.log(byParagrafs.text);
|
|
10
|
+
console.log();
|
|
11
|
+
|
|
12
|
+
const byWords = new Ipsum({ words: 10 });
|
|
13
|
+
console.log('10 words:', byWords.text);
|
|
14
|
+
console.log();
|
|
15
|
+
|
|
16
|
+
// ─── Test Mtg ─────────────────────────────────────────────────────────────────
|
|
17
|
+
console.log('=== MTG TESTS ===\n');
|
|
18
|
+
|
|
19
|
+
const rand = new Mtg({ rand: true });
|
|
20
|
+
rand
|
|
21
|
+
.then(card => console.log('Random card:', card.name, '—', card.set_name))
|
|
22
|
+
.catch(err => console.error('Error:', err.message));
|
|
23
|
+
|
|
24
|
+
const specific = new Mtg({ ed: 'dom', id: 1 });
|
|
25
|
+
specific
|
|
26
|
+
.then(card => console.log('Specific card (dom/1):', card.name))
|
|
27
|
+
.catch(err => console.error('Error:', err.message));
|