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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(node:*)"
5
+ ]
6
+ }
7
+ }
@@ -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>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/Node-TP2.iml" filepath="$PROJECT_DIR$/.idea/Node-TP2.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
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));