|
|
| (5 versions intermédiaires par le même utilisateur non affichées) |
| Ligne 2 : |
Ligne 2 : |
|
| |
|
| /* ============================================================ | | /* ============================================================ |
| * Infothèque — Gadget "Ajouter un téléchargement" | | * Barre de recherche de la page d'accueil |
| * À coller dans : MediaWiki:Common.js (à la suite du contenu existant)
| |
| *
| |
| * N'agit que sur la page d'édition en mode source (là où
| |
| * #wpTextbox1 existe). Ajoute un bouton qui ouvre un petit
| |
| * formulaire, puis insère automatiquement le bon bloc de
| |
| * paramètres numérotés dans l'appel {{Téléchargements|...}}
| |
| * existant (ou en crée un nouveau à l'endroit du curseur si
| |
| * aucun n'est trouvé sur la page).
| |
| *
| |
| * - Le format (disquette / CD / ZIP) est traduit automatiquement
| |
| * en icône — voir ITH_DL_FORMATS ci-dessous.
| |
| * - Plusieurs images possibles : 1 image = insérée telle quelle,
| |
| * 2+ images = galerie en grille (<gallery mode="packed">).
| |
| * - Un champ "Clé de produit / n° de série" facultatif, ajouté
| |
| * proprement à la description (formaté en <code>).
| |
| * - Toute valeur libre insérée dans le wikitexte a ses "|"
| |
| * échappés en {{!}}, sinon un pipe tapé par l'utilisateur
| |
| * casserait l'appel du modèle (même souci que celui qui
| |
| * cassait le rendu du tableau — voir Modèle:Téléchargements).
| |
| * ============================================================ */ | | * ============================================================ */ |
|
| |
| (function () { | | (function () { |
| 'use strict'; | | 'use strict'; |
|
| |
| var ITH_DL_FORMATS = {
| |
| disquette: { label: 'Disquette', wikitext: '[[Fichier:Icône disquettes.png|centré]]' },
| |
| cd: { label: 'CD-ROM / DVD', wikitext: '[[Fichier:Icone_CD.png|centré|sans_cadre]]' },
| |
| zip: { label: 'Fichier ZIP / téléchargement numérique', wikitext: '[[Fichier:Zipfldr.dll 14 123-3.png|centré|sans_cadre]]' },
| |
| autre: { label: 'Autre (icône personnalisée)', wikitext: '' }
| |
| };
| |
|
| |
| function escapePipes(str) {
| |
| return String(str || '').split('|').join('{{!}}');
| |
| }
| |
|
| |
|
| function init() { | | function init() { |
| var textarea = document.getElementById('wpTextbox1'); | | var trigger = document.querySelector('.ith-home-search'); |
| if (!textarea) { | | var real = document.getElementById('citizen-search-summary'); |
| return; // pas en édition source | | if (!trigger || !real) { |
| | return; |
| } | | } |
| addButton(textarea); | | trigger.setAttribute('role', 'button'); |
| }
| | trigger.setAttribute('tabindex', '0'); |
| | | trigger.addEventListener('click', function () { |
| function addButton(textarea) {
| | real.click(); |
| var btn = document.createElement('button');
| |
| btn.type = 'button';
| |
| btn.id = 'ith-dl-add-btn'; | |
| btn.className = 'ith-dl-add-btn';
| |
| btn.textContent = '+ Ajouter un téléchargement';
| |
| btn.addEventListener('click', function () {
| |
| openModal(textarea);
| |
| });
| |
| textarea.parentNode.insertBefore(btn, textarea);
| |
| }
| |
| | |
| function openModal(textarea) {
| |
| var overlay = document.createElement('div');
| |
| overlay.className = 'ith-dl-overlay'; | |
| | |
| var box = document.createElement('div');
| |
| box.className = 'ith-dl-modal';
| |
| box.innerHTML =
| |
| '<h3>Ajouter un téléchargement</h3>' +
| |
| '<label>Édition <span class="ith-dl-req">*</span><input type="text" id="ith-dl-edition" placeholder="Ex : Windows 95"></label>' +
| |
| '<label>Version<input type="text" id="ith-dl-version" placeholder="Ex : OSR 2.5 OEM 4.03.1216"></label>' +
| |
| '<label>Langue<input type="text" id="ith-dl-langue" value="Français"></label>' +
| |
| '<label>Description<textarea id="ith-dl-description" rows="2" placeholder="Notes libres, facultatif"></textarea></label>' +
| |
| '<label>Clé de produit / n° de série<textarea id="ith-dl-serial" rows="2" placeholder="Facultatif. Une ligne par clé, ex : Product key: XXXXX-XXXXX-XXXXX Code key: 341-0005934"></textarea></label>' +
| |
| '<label>Format<select id="ith-dl-format"></select></label>' +
| |
| '<label id="ith-dl-format-autre-wrap" style="display:none">Icône personnalisée (wikitexte)<input type="text" id="ith-dl-format-autre" placeholder="[[Fichier:Mon icône.png|centré]]"></label>' +
| |
| '<label>Images (photos, boîtes, disques...)</label>' +
| |
| '<div id="ith-dl-images"></div>' +
| |
| '<button type="button" id="ith-dl-add-image" class="ith-dl-btn-secondary ith-dl-btn-small">+ Ajouter une image</button>' +
| |
| '<label style="margin-top:12px">Liens de téléchargement <span class="ith-dl-req">*</span>' +
| |
| '<textarea id="ith-dl-liens" rows="3" placeholder="Une ligne par lien, format : URL | Texte du lien"></textarea>' +
| |
| '</label>' +
| |
| '<div class="ith-dl-actions">' +
| |
| '<button type="button" id="ith-dl-cancel" class="ith-dl-btn-secondary">Annuler</button>' +
| |
| '<button type="button" id="ith-dl-submit" class="ith-dl-btn-primary">Ajouter</button>' +
| |
| '</div>' +
| |
| '<p class="ith-dl-error" id="ith-dl-error" style="display:none"></p>';
| |
| | |
| overlay.appendChild(box);
| |
| document.body.appendChild(overlay);
| |
| | |
| var select = box.querySelector('#ith-dl-format');
| |
| Object.keys(ITH_DL_FORMATS).forEach(function (key) {
| |
| var opt = document.createElement('option'); | |
| opt.value = key;
| |
| opt.textContent = ITH_DL_FORMATS[key].label;
| |
| select.appendChild(opt);
| |
| }); | | }); |
| select.addEventListener('change', function () { | | trigger.addEventListener('keydown', function (e) { |
| var autreWrap = box.querySelector('#ith-dl-format-autre-wrap');
| | if (e.key === 'Enter' || e.key === ' ') { |
| autreWrap.style.display = select.value === 'autre' ? '' : 'none';
| | e.preventDefault(); |
| });
| | real.click(); |
| | |
| var imagesWrap = box.querySelector('#ith-dl-images');
| |
| function addImageRow() {
| |
| var row = document.createElement('div');
| |
| row.className = 'ith-dl-image-row';
| |
| row.innerHTML =
| |
| '<input type="text" class="ith-dl-image-file" placeholder="Nom du fichier, ex : Win95disks.jpg">' +
| |
| '<input type="text" class="ith-dl-image-caption" placeholder="Légende (facultatif)">' +
| |
| '<button type="button" class="ith-dl-btn-remove-image" title="Retirer">×</button>';
| |
| row.querySelector('.ith-dl-btn-remove-image').addEventListener('click', function () {
| |
| imagesWrap.removeChild(row);
| |
| });
| |
| imagesWrap.appendChild(row);
| |
| }
| |
| addImageRow(); // une première ligne par défaut
| |
| box.querySelector('#ith-dl-add-image').addEventListener('click', addImageRow);
| |
| | |
| box.querySelector('#ith-dl-cancel').addEventListener('click', closeModal);
| |
| overlay.addEventListener('click', function (e) {
| |
| if (e.target === overlay) closeModal(); | |
| });
| |
| | |
| box.querySelector('#ith-dl-submit').addEventListener('click', function () {
| |
| submit(textarea);
| |
| });
| |
| | |
| function closeModal() {
| |
| document.body.removeChild(overlay);
| |
| }
| |
| | |
| function buildImageWikitext() {
| |
| var rows = Array.prototype.slice.call(imagesWrap.querySelectorAll('.ith-dl-image-row'));
| |
| var images = rows.map(function (row) {
| |
| var file = row.querySelector('.ith-dl-image-file').value.trim();
| |
| var caption = row.querySelector('.ith-dl-image-caption').value.trim(); | |
| return { file: file, caption: caption }; | |
| }).filter(function (img) { return img.file; });
| |
| | |
| if (images.length === 0) {
| |
| return '';
| |
| } | | } |
| if (images.length === 1) {
| |
| var f = images[0].file.replace(/^Fichier:/i, '');
| |
| var parts = ['[[Fichier:' + f, 'centré', 'vignette'];
| |
| if (images[0].caption) parts.push(images[0].caption);
| |
| return parts.join('|') + ']]';
| |
| }
| |
| var lines = images.map(function (img) {
| |
| var f = img.file.replace(/^Fichier:/i, '');
| |
| return 'Fichier:' + f + (img.caption ? '|' + img.caption : '');
| |
| });
| |
| return '<gallery mode="packed">\n' + lines.join('\n') + '\n</gallery>';
| |
| }
| |
|
| |
| function buildDescriptionWithSerial(description, serialRaw) {
| |
| var serialLines = serialRaw.split('\n').map(function (l) { return l.trim(); }).filter(Boolean);
| |
| if (serialLines.length === 0) {
| |
| return description;
| |
| }
| |
| var serialBlock = "'''Clé de produit :'''<br>" +
| |
| serialLines.map(function (l) { return '<code>' + l + '</code>'; }).join('<br>');
| |
| return description ? description + '\n\n' + serialBlock : serialBlock;
| |
| }
| |
|
| |
| function submit(ta) {
| |
| var edition = box.querySelector('#ith-dl-edition').value.trim();
| |
| var liensRaw = box.querySelector('#ith-dl-liens').value.trim();
| |
| var errEl = box.querySelector('#ith-dl-error');
| |
|
| |
| if (!edition || !liensRaw) {
| |
| errEl.textContent = 'Édition et au moins un lien de téléchargement sont obligatoires.';
| |
| errEl.style.display = '';
| |
| return;
| |
| }
| |
|
| |
| var version = box.querySelector('#ith-dl-version').value.trim();
| |
| var langue = box.querySelector('#ith-dl-langue').value.trim();
| |
| var descriptionRaw = box.querySelector('#ith-dl-description').value.trim();
| |
| var serialRaw = box.querySelector('#ith-dl-serial').value.trim();
| |
| var description = buildDescriptionWithSerial(descriptionRaw, serialRaw);
| |
| var formatKey = select.value;
| |
| var format = formatKey === 'autre'
| |
| ? box.querySelector('#ith-dl-format-autre').value.trim()
| |
| : ITH_DL_FORMATS[formatKey].wikitext;
| |
| var image = buildImageWikitext();
| |
| var liens = buildLiens(liensRaw);
| |
|
| |
| var data = {
| |
| edition: edition,
| |
| version: version,
| |
| langue: langue,
| |
| description: description,
| |
| format: format,
| |
| image: image,
| |
| liens: liens
| |
| };
| |
|
| |
| insertIntoTextarea(ta, data);
| |
| closeModal();
| |
| }
| |
| }
| |
|
| |
| function buildLiens(raw) {
| |
| var lines = raw.split('\n').map(function (l) { return l.trim(); }).filter(Boolean);
| |
| var formatted = lines.map(function (line) {
| |
| var parts = line.split('|');
| |
| var url = (parts[0] || '').trim();
| |
| var texte = (parts[1] || 'Téléchargement').trim();
| |
| return '[' + url + ' ' + texte + ']';
| |
| }); | | }); |
| if (formatted.length === 1) {
| |
| return formatted[0];
| |
| }
| |
| return formatted.map(function (f) { return '* ' + f; }).join('\n');
| |
| }
| |
|
| |
| function insertIntoTextarea(textarea, data) {
| |
| var text = textarea.value;
| |
| var startIdx = text.indexOf('{{Téléchargements');
| |
|
| |
| if (startIdx === -1) {
| |
| var block = '{{Téléchargements\n' + buildParamLines(1, data) + '\n}}';
| |
| insertAtCursor(textarea, block);
| |
| return;
| |
| }
| |
|
| |
| // Trouve l'accolade fermante correspondante en comptant la profondeur des {{ }}.
| |
| var depth = 0;
| |
| var endIdx = -1;
| |
| for (var i = startIdx; i < text.length - 1; i++) {
| |
| if (text[i] === '{' && text[i + 1] === '{') { depth++; i++; }
| |
| else if (text[i] === '}' && text[i + 1] === '}') {
| |
| depth--; i++;
| |
| if (depth === 0) { endIdx = i; break; }
| |
| }
| |
| }
| |
|
| |
| if (endIdx === -1) {
| |
| // Structure inattendue : on n'insère pas au hasard, on ajoute un nouveau bloc à part.
| |
| var fallback = '{{Téléchargements\n' + buildParamLines(1, data) + '\n}}';
| |
| insertAtCursor(textarea, fallback);
| |
| return;
| |
| }
| |
|
| |
| var callText = text.slice(startIdx, endIdx + 1);
| |
| var maxIndex = 0;
| |
| var re = /edition(\d+)\s*=/g;
| |
| var m;
| |
| while ((m = re.exec(callText))) {
| |
| maxIndex = Math.max(maxIndex, parseInt(m[1], 10));
| |
| }
| |
| var nextIndex = maxIndex + 1;
| |
|
| |
| if (nextIndex > 10) {
| |
| alert('Ce tableau contient déjà 10 téléchargements (le maximum géré par le modèle). Préviens l\'admin du wiki pour étendre le modèle.');
| |
| return;
| |
| }
| |
|
| |
| var paramLines = buildParamLines(nextIndex, data);
| |
| // insère juste avant le "}}" final (donc avant l'avant-dernier caractère de endIdx)
| |
| var insertPos = endIdx - 1;
| |
| var newText = text.slice(0, insertPos) + '\n' + paramLines + text.slice(insertPos);
| |
| textarea.value = newText;
| |
| textarea.dispatchEvent(new Event('input', { bubbles: true }));
| |
| textarea.focus();
| |
| }
| |
|
| |
| function buildParamLines(n, data) {
| |
| // edition/version/langue/description = texte libre -> pipes échappés.
| |
| // format/image/liens = wikitexte déjà construit ([[...]], <gallery>, [url texte])
| |
| // dont les pipes internes sont protégés par [[ ]] / <gallery> / pas de pipe du tout.
| |
| return [
| |
| ' |edition' + n + '=' + escapePipes(data.edition),
| |
| ' |version' + n + '=' + escapePipes(data.version),
| |
| ' |langue' + n + '=' + escapePipes(data.langue),
| |
| ' |description' + n + '=' + escapePipes(data.description),
| |
| ' |format' + n + '=' + data.format,
| |
| ' |image' + n + '=' + data.image,
| |
| ' |liens' + n + '=' + data.liens
| |
| ].join('\n');
| |
| }
| |
|
| |
| function insertAtCursor(textarea, text) {
| |
| var start = textarea.selectionStart || 0;
| |
| var end = textarea.selectionEnd || 0;
| |
| var before = textarea.value.slice(0, start);
| |
| var after = textarea.value.slice(end);
| |
| textarea.value = before + text + after;
| |
| textarea.dispatchEvent(new Event('input', { bubbles: true }));
| |
| textarea.focus();
| |
| } | | } |
|
| |
|