Sécuriser les iframes avec l'attribut sandbox - Alsacreations
Tiens ? Je découvre l'attribut sandbox pour les iframe. C'est pas que j'en fasse usage en général mais bon...
Tiens ? Je découvre l'attribut sandbox pour les iframe. C'est pas que j'en fasse usage en général mais bon...
Pour écouter un click hors d'un élément, on utilise l'event delegation: on vérifie si la target de l'event correspond ou pas à l'objet qu'on veut.
document.addEventListener('click', e => {
if (!element.contains(e.target)) callback();
});
};
onClickOutside('#my-element', () => console.log('Hello'));
// Will log 'Hello' whenever the user clicks outside of #my-element
Tiens, je ne savais pas qu'addEventListener acceptait des paramètres en option, en particulier un { once : true} qui permet de n'exécuter la fonction attachée à l'évènement qu'une seule fois...
const listenOnce = (el, evt, fn) =>
el.addEventListener(evt, fn, { once: true });
listenOnce(
document.getElementById('my-btn'),
'click',
() => console.log('Hello!')
); // 'Hello!' will only be logged on the first click
Deuxième épisode de mes notes perso sur la bibliothèque Image magic pour évoquer la classe ImagickDraw qui, comme son nom l'indique, permet de dessiner avec Imagick. ( 1er épisode )
On va se limiter à des usages basiques parce que la lecture du sommaire suffit à imaginer des possibilités certes alléchantes mais totalement hors de propos en ce qui me concerne (courbes de bézier etc)
Spoiler: c'est nettement moins intuitif et simple que pour le traitement d'image... l'écart entre Imagick et GD dans le domaine du tracé n'est plus si évident: ça mériterait de faire un classe pour simplifier tout ça... ( /me se jette sur son bujo pour griffonner spasmodiquement)
Cette classe crée un objet servant d'instructions à appliquer ensuite à un objet Imagick.
On partira du principe que vous avez fait des instances des classes comme suit:
$im=new Imagick(); # l'image dans laquelle vous allez appliquer l'objet ImagickDraw
$draw=new ImagikDraw();
$draw->setFont(fonte);
: sélectionne la fonte à utiliser$draw->setFontSize(taille);
: définit la taille en pixels$draw->setFillColor(couleur);
: définit la couleur de fond (normalement, couleur est un objet ImagickPixel, voir 1er épisode mais on peut passer une chaîne au format de couleur CSS )$draw->setTextAlignment(alignement);
: une des constantes d'alignement imagick::ALIGN_LEFT imagick::ALIGN_CENTER Imagick::ALIGN_RIGHT
$draw->setTextDecoration(decoration)
: une des constantes de décoration imagick::DECORATION_UNDERLINE imagick::DECORATION_OVERLINE imagick::DECORATION_LINETROUGH
et bien d'autres (comme du... bruit ?!).$draw->setTextAntialias(true);
: ajouter de l'antialias$draw->setTextInterwordSpacing(taille)
$draw->setTextInterlineSpacing(taille)
$draw->annotation(x,y,texte)
: trace un texte dans la police sélectionnée$im->annotateImage( $draw, 0, 0, 0, $fontName );
: ajoute le texte à l'image.$im->queryFonts($pattern = "*")
🆒 on peut filtrer avec une pattern à la façon de glob().$im->queryFontMetrics($draw, text);
on récupère un tableau comme ci-dessous
Array
(
[characterWidth] => 9
[characterHeight] => 9
[ascender] => 9 # la taille au-dessus de la baseline
[descender] => -2 # la taille sous la baseline
[textWidth] => 71
[textHeight] => 10
[maxHorizontalAdvance] => 12
[boundingBox] => Array
(
[x1] => 0
[y1] => -2
[x2] => 6.890625
[y2] => 7
)
[originX] => 70
[originY] => 0
)
$draw->setStrokeOpacity(0-1);
$draw->setStrokeColor(couleur);
$draw->setStrokewidth(nb);
$draw->setStrokeDashArray()
Le motif semble être plein de possibilités car on peut le définir très spécifiquement et même utiliser une image comme motif (->setStrokePatternURL)$draw->setFillColor(couleur);
$draw->circle ($x, $y, $x + $r, $y);
$draw->ellipse($ox,$oy,$rx,$ry,$start,$end);
$draw->point ($x, $y);
$draw->polygon ($tableau_de_coordonnées);
: array( 'x' => 3, 'y' => 4 ), array( 'x' => 2, 'y' => 6 ) $draw->polyline ($tableau_de_coordonnées);
: array( 'x' => 3, 'y' => 4 ), array( 'x' => 2, 'y' => 6 ) $draw->rectangle ($x, $y, $x2, $y2);
) $draw->roundRectangle ($x, $y, $x2, $y2,$rayonx,$rayony);
Pour écrire un texte dans une image:
$text='pipo et mollo';
$im=new Imagick();
$draw=new ImagickDraw();
//var_dump($im->queryFonts('*'));exit;
$draw->setFont( 'Courier' );
$draw->setFontSize( 20 );
$draw->setFillColor( 'black' );
$draw->setTextAntialias(true);
$metrics = $im->queryFontMetrics($draw, $text);
$draw->annotation(0,$metrics['ascender'],$text);
$im->newImage( 300, 20, 'lightblue');
$im->drawImage($draw);
$im->setImageFormat( 'png' );
header('Content-type: image/jpeg');
exit($im);
ce qui donne cette image
Il serait intéressant de pousser les tests mais là, j'ai pas le temps...
Peut-être reprendrais-je des trucs existant avec tout ça ( scriptopic, RoR, Stamp mematic etc etc.)
Allez, pour une fois, j'ai un peu de temps devant moi, je vais éplucher un peu la lib Image magick dont je parlais il n'y a guère...
Simple: pas besoin de plusieurs fonctions selon le format, il suffit de $images = new Imagick('image.jpg');
Même pas nécessaire de passer par un foreach, on fournit le tableau des fichiers voire directement un glob: $images = new Imagick(glob('images/*.JPG'));
$im->writeImage('image.jpg');
Si on laisse une dimension à 0, les proportions sont conservées (quand tu vois la merde que c'est avec GD !)
$image->thumbnailImage(100, 0);
header('Content-type: image/jpeg');
$image = new Imagick('image.jpg');
# ici on effectue un traitement puis on sort le résultat
echo $image;
$im->getImageWidth() $im->getImageHeight()
$im->getImageFormat()
$im->setImageFormat('png');
! 😍$im->NewImage(largeur,hauteur,couleur de fond);
Comme pour GD, il faut créer un objet couleur dans les traitements d'image, mais c'est plus simple qu'avec GD (et plus complet): il suffit de passer une couleur selon les normes CSS. Du coup, la transparence n'est pas gérée par une connerie de paramètre «alpha» mais simplement par ... rgba()... 💖
$couleur=new ImagickPixel("white");
$couleur=new ImagickPixel("#FFF");
$couleur=new ImagickPixel('rgb(255,255,200)');
$couleur=new ImagickPixel('rgba(255,255,200,0.5)');
🆒 ⮕ Ça peut paraître compliqué, mais en fait, dans les fonctions où on est sensé utiliser ImagickPixel, je me suis aperçu qu'on pouvait tout simplement passer une string contenant la couleur css... elles se démerdent seules. 😍
$im->borderImage(new ImagickPixel("white"), 5, 5);
$im->setFont("example.ttf");
$im->negateImage(0);
$im->normalizeImage();
$im->autoLevelImage();
$im->gammaImage(5);
$im->contrastImage(niveau)
$im->brightnessContrastImage(niveau luminosite, niveau contraste);
$im->equalizeImage()
$im->colorizeImage("red",0.5);
🤬 pas réussi à l'utiliser pour ce test...$i->resizeImage(250,0,Imagick::FILTER_POINT,0);
$im->cropImage(200,200,50,50);
$im->flipImage()
pour retourner horizontalement$im->flopImage()
pour retourner verticalement$im->rotateImage(new ImagickPixel('#00000000'), 75);
$im->->setImageOpacity(0.3);
$im->blurImage(5,2);
$im->gaussianBlurImage(5,2);
$im->motionBlurImage(5,5,45);
$im->posterizeImage(5,0);
`$im->addNoiseImage(imagick::NOISE_GAUSSIAN );
voir les constantes de type de bruit $im->sketchImage(rayon, deviation, angle);
$im ->charcoalImage(rayon, deviation);
$im->->shadeImage(1, 90, 2); # emboss + image grisée
$im->shadeImage(0, 90,2) # emboss sur l'image d'origine (couleur)
$im->oilPaintImage(5);
$im->edgeImage(5);
$im->waveImage( 10, 10);
$imageclonee=$image->clone();
$im->compositeImage($autreimage, imagick::COMPOSITE_OVER, 0, 0);
(la composition, la façon de mélanger les images, peut être une de celles-ci https://www.php.net/manual/fr/imagick.constants.php#imagick.constants.compositeop)Image Magick me semble particulièrement bien nommée tant les possibilités sont énormes et la simplicité d'utilisation étonnante: on sent une volonté de se simplifier la vie lors de l'utilisation... c'est juste beau.
Je voulais mettre une image de chaque effet dans les descriptions mais:
Tout le monde connaît les licences libres habituelles mais il y en a de moins connues et - souvent - moins compliquées à comprendre : petit florilège...
Permet de distribuer avec le même niveau de protection que le domaine public, c'est-à-dire AUCUN.
Faites ce que vous voulez et si un jour on se rencontre et que vous trouvez que mon boulot en vaut la peine, payez-moi une bière.
La réponse à toute question à propos des droits de reproduction ou problème légal est : «Sure, No problem. Don't worry, be happy. Now bugger off»
En gros, faites ce que vous voulez et ne me contactez pas.
Démerde-toi, demande pas d'aide et - surtout - ne dis à personne que ça vient de moi...
«Toute personne qu'on prendra à distribuer ce livre sans permission se verra considéré comme un super pote parce qu'on en a rien à foutre...»
Littéralement, la Licence Démerdez-vous... Je vous laisse la lire tranquilou pour vous faire une idée: toute tentative de résumé serait une trahison du texte original
«Cette Licence» se réfère à la version 1 de la «Demerden Sie Sich License» (le texte original en français). «Démerder», se réfère au sens de se «débrouiller». A aucun moment «cette Licence» ne vous demandera de vous enduire (vous ou votre œuvre) d'excréments humains (ni même animal). «Œuvre» est aussi appelée «travail» (quelques fois, l'extension «de sagouin» peut lui être apposée), «programme» ou tout autre terme relatif à ce qui a été effectué. Ainsi, une «documentation» ou un «manuel» peut être considéré comme une œuvre. «Auteur» signifie que la personne (ou groupe de personnes), qui utilise «cette Licence», se lave complètement les mains de la façon dont vous utiliserez son œuvre.
L'«Auteur» peut-être considéré comme irresponsable et il incombe à l'utilisateur en priorité de se «démerder» par lui même.
This software may not be used directly by any living being.
En gros, l'usage à toute personne vivante est formellement interdit.
Conditions très exotiques pour cette licence qui régule drastiquement la redistribution des copies du code:
- You do not talk about the FIGHT CLUB LICENSE.
- You DO NOT talk about the FIGHT CLUB LICENSE.
- If someone says "stop" or goes limp — or taps out — the project is over.
- Only two developers to a project.
- One project at a time.
- No shirts, no shoes.
- Projects will go on as long as they have to.
- If this is your first time reading the FIGHT CLUB LICENSE, you HAVE to license your next project under the FIGHT CLUB LICENSE.
L'auteur de toute modification doit redistribuer le travail modifié par un moyen plus contraignant et difficile que celui par lequel il a acquis le code original.
Heu, là, je vous laisse aller la voir tous seuls.... passque bobo têtête.
Pour faire court, c'est pas un bug c'est une fonctionnalité...
- Bugs in the licensed work are features, to be cherished, documented, and developed upon.
- Modified works must not include known bugs.
- Where identified, modified works' bugs shall be fixed.
- Authors and maintainers of the licensed work reserve the right to pull bug fixes from modified or derivative works without compensation, recognition, or any other reference to the authors of the bug fix.
Je crois que c'est clair...
Tu veux redistribuer ?! signe ici... avec ton sang.
Seule restriction : il est définitivement et formellement interdit de refactorer ce logiciel en Python 2.
Une licence qui autorise tout avec pour seule condition de ne pas être un connard (a dick)... l'auteur fournit une liste non exhaustive de ce qu'il appelle être un connard:
Vous voulez partager un logiciel ou un code en interdisant toute exploitation commerciale capitaliste et libérale ? Cette licence permet de restreindre l'usage et l'exploitation à certains types d'utilisateurs:
L'utilisateur a le droit... de demander la permission et l'auteur a l'obligation d'accepter
J'ai eu envie de faire ça car j'en avais assez de passer par le combo
Du coup, il suffit d'ajouter l'adresse de l'api à l'url vers la ressource distante...
Donc http://insta.com/image.jpg
devient par exemple http://api.warriordudimanche.net/fetchit?url=http://insta.com/image.jpg
Fetchit va récupérer la ressource en local et vous servir cette version au lieu de la distante. Comme d'hab' si cette ressource a déjà été récupérée elle n'est pas re téléchargée.
Le deuxième effet kiskool (paye ta réf de vieux) c'est que du coup, comme getlib, ça permet de récupérer toute lib en local et être plus RGPD friendly.
Afin d'éviter que votre server ne se retrouve floodé par des fichiers vidéos 8K à 60 gigots l'unité, il y a une limite de taille configurée dans la constante SIZE_LIMIT
, fixée par défaut à 10 Mo.
Comme d'hab', c'est cadeau... Utilisez, partagez, modifiez... juste respectez la Dont be a dick licence
Comme tout ça n'a de sens que si on héberge soi-même, vous pouvez récup' le zip qui va bien ici : https://api.warriordudimanche.net/fetchit/?download
Mes chers cons-patriotes, non aux fuites de données, vive le oueb libre et participatif et vive la france et bisou !
https://antoineboursin.fr/courses/creez-un-editeur-de-texte-wysiwyg
La vie est mal faite: je découvre document.execCommand() permettant de faire du richtext dans un élément contentEditable juste quand il est officiellement déclaré obsolète...
Bon, en même temps, sur stackoverflow, the holy baïbol, certains affirment que:
A l'occasion d'un ménage dans les brouillons de billets, je compile les trucs DEV qui trainaient par là
poe.com, un [bon?!] moyen de tester ChatGPT sans numéro de téléphone, en ne donnant qu'une adresse email, éventuellement jetable.
Je me suis dit, tiens, si je testais avec la génération d'un bout de code ?
Par manque de temps comme par curiosité, je lui ai soumis un truc qui me trotte dans la tête : la séparation des mots espagnols en syllabes (beaucoup moins simple qu'il n'y paraît à priori )
Ben je crois que j'ai cassé chatGPT... Je lui prompte:
crée une fonction php efficace permettant de séparer les mots espagnols en syllabes
Après quelques bouts de code qui pouvait sembler fonctionnels, la découpe ne se faisait jamais correctement.
Je lui fournis un mot ne fonctionnant pas, une explication du problème et lui demande de corriger:
cette fonction a des problèmes de caractères unicode, teste-le avec le mot español et recommence
En affinant et en redemandant de tester avec certains mots, l'appli me propose de nouvelles modifs mais aucune n'est fonctionnelle, contrairement à ce qu'affirme le chatbot: il me dit que la fonction découpe correctement les mots demandés... Donc soit elle ment soit elle ne teste pas le code (LOL)
Je lui demande donc de recommencer et c'est là qu'on rigole:
ça ne fonctionne toujours pas: ton code ne sépare pas bien les syllabes et il le fait dans le désordre (les syllabes ne sont pas dans l'ordre du mot) recommence
On voit que le bouzin part dans une sorte de boucle générative assez drôle, mélangeant les portions de code avec ses propres commentaires, répétés et entrecoupés...
function syllabifier($word) {
$syllable_patterns = array(
'/(qui|gue|gui|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
'/(que|qe|qui|qüe)[aeiouáéíóúü]*/i',
'/(gui|güi)[aeiouáéíóúü]*/i'
);
$syllables = array();
preg_match_all('/./u', $word, $matches);
$characters = $matches[0];
while (!empty($characters)) {
$matched = false;
foreach ($syllable_patterns as $pattern) {
$syllable = '';
foreach ($characters as $key => $char) {
$syllable .= $char;
if (preg_match($pattern, $syllable)) {
$syllables[] = $syllable;
$matched = true;
$charactersJe suis désolé que la fonction ne fonctionne toujours pas pour vous. Je vais essayer de proposer une nouvelle version de la fonction qui prend en compte les cas particuliers de la langue espagnole et qui retourne les syllabes dans l'ordre du mot. Voici la fonction modifiée :
```php
function syllabifier($word) {
$syllable_patterns = array(
'/(qui|gue|gui|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
'/(que|qe|qui|qüe)[aeiouáéíóúü]*/i',
'/(gui|güi)[aeiouáéíóúü]*/i'
);
$syllables = array();
preg_match_all('/./u', $word, $matches);
$characters = $matches[0];
while (!empty($characters)) {
$matched = false;
foreach ($syllable_patterns as $pattern) {
$syllable = '';
foreach ($characters as $key => $char) {
$syllable .= $char;
if (preg_match($pattern, $syllable)) {
$syllables[] = $syllable;
$matched = true;
$charactersJe m'excuse pour cette erreur dans ma réponse précédente. Voici la version corrigée de la fonction :
```php
function syllabifier($word) {
$syllable_patterns = array(
'/(ll|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
'/(que|qe|qui|qüe)[aeiouáéíóúü]*/i',
'/(gui|güi)[aeiouáéíóúü]*/i'
);
$syllables = array();
preg_match_all('/./u', $word, $matches);
$characters = $matches[0];
while (!empty($characters)) {
$matched = false;
foreach ($syllable_patterns as $pattern) {
$syllable = '';
foreach ($characters as $key => $char) {
$syllable .= $char;
if (preg_match($pattern, $syllable)) {
$syllables[] = $syllable;
$matched = true;
$characters = array_slice($characters, $key + 1);
break;
}
}
if ($matched) {
break;
}
}
if (!$matched) {
$syllables[] = $charactersJe m'excuse pour cette erreur dans ma réponse précédente. Voici la version corrigée de la fonction :
```php
function syllabifier($word) {
$syllable_patterns = array(
'/(ll|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
'/(que|qe|qui|qüe)[aeiouáéíóúü]*/i',
'/(gui|güi)[aeiouáéíóúü]*/i'
);
$syllables = array();
preg_match_all('/./u', $word, $matches);
$characters = $matches[0];
while (!empty($characters)) {
$matched = false;
foreach ($syllable_patterns as $pattern) {
$syllable = '';
foreach ($characters as $key => $char) {
$syllable .= $char;
if (preg_match($pattern, $syllable)) {
$syllables[] = $syllable;
$matched = true;
$characters = array_slice($characters, $key + 1);
break;
}
}
if ($matched) {
break;
}
}
if (!$matched) {
$syllables[] = $charactersJe suis désolé pour cette confusion. Voici une nouvelle version de la fonction qui devrait fonctionner correctement pour le mot "español" et qui retourne les syllabes dans l'ordre du mot :
```php
function syllabifier($word) {
$syllable_patterns = array(
'/(que|qui|gue|gui|güe|güi|[aeiouáéíóúü])[aeiouáéíóúü]*|[bcdfghjklmnpqrstvwxyzñ]+/i',
'/(que|qe|qui|qüe)[aeiouáééóúí]+/i',
'/(gui|güi)[aeiouáéíóúü]+/i'
);
$syllables = array();
preg_match_all('/./u', $word, $matches);
$characters = $matches[0];
while (!empty($characters)) {
$matched = false;
foreach ($syllable_patterns as $pattern) {
$syllable = '';
foreach ($characters as $key => $char) {
$
Il y a quelques jours, j'ai vu passer un article sur le contenu idéal d'un Readme ( https://warriordudimanche.net/article1840/6448d4a15a710 ) et en partageant le lien sur mastodon, on a émis l'idée de coder un assistant de génération qui permette de faire ça simplement...
Nicolas Delsaux* m'a alors passé quelques exemples d'applis déjà réalisées et - pour autant que j'ai pu en juger avec un test rapide - très bien foutues et très complètes:
le malheur était déjà en train de se produire: impossible d'empêcher mon cerveau de travailler en background façon «à la volette»
Du coup, si je voulais pouvoir faire autre chose, il fallait d'abord vider ma tête de ce «projet» LOL.
L'objectif est de pouvoir faire un readme rapidement, de pouvoir modifier «simplement» les éléments du contenu (ordre ou items), de pouvoir ajouter des captures d'écran, sélectionner une license...
La capture d'écran parle d'elle-même: on peut même choisir de générer des fichiers séparés pour certaines parties (Roadmap, Changelog...)
La configuration des items se fait via le fichier config/patrs.json
qui contient les objets, leur type (select/text/textarea etc), leur titre/label, leur placeholder, leur attribut name etc.
"Name":{
"type":"text",
"title":"The project name",
"name":"name",
"placeholder":"ReadmeToaster",
"class":"name",
"required":"required"
},
"Description":{
"type":"textarea",
"title":"A short description (what's the project's purpose ?)",
"name":"description",
"placeholder":"A tiny webapp to create normalised readme files",
"class":"description"
},
"Installation":{
"type":"textarea",
"title":"How to install or deploy ?",
"name":"installation",
"placeholder":"Just copy the folder onto your server and that's it... Noooo, I'm kidding !",
"class":"install"
},
"Contribute":{
"type":"textarea",
"name":"contribute",
"placeholder":"* translations,n* bug finding",
"class":"text",
"title":"Explain how to contribute"
},
"Contribute in a separated file":{
"type":"checkbox",
"name":"contribute_separate",
"title":"Put contribute in a separate file",
"placeholder":"",
"class":"",
"value":"contribute_text"
},
On peut ainsi facilement ajouter, modifier ou déplacer des items dans le frontend sans mettre les doigts dans le code. Si on peut pouvoir séparer un contenu dans un fichier à part, on colle un item checkbox dont le nom est ITEM_separate.
Le fichier généré sera un zip contenant tous les fichiers, y compris les éventuelles captures d'écran fournies.
Pas sûr que ça serve à quelqu'un vu que c'est plus un amusement qu'autre chose (ne me jugez pas), mais bon: si jamais quelqu'un le veut... c'est par là : https://outils.warriordudimanche.net/outils/readmetoaster/ et pour le télécharger: http://outils.warriordudimanche.net/outils/readmetoaster/?download
[*]
Pub gratuite pour Nicolas
En résumé, pour un bon .README, il faut les paragraphes:
Pour ma part, et comme un des commentateurs, je pense qu'il faudrait mettre le changelog dans un fichier séparé.
On en apprend tous les jours...
<?php
dirname(__DIR__).'../a/b/c';
peut-être fait avec
dirname(__DIR__, 3).'a/b/c';
Merci TImo ! (merci Timoooo)
Une API de text to speech qui comble les attentes que j'avais:
Au départ, je cherchais une lib me permettant de faire la même chose sans dépendre d'un service tiers (autonomie, disparition du service, passage au tout payant toussa).
Hélas, aucune lib gratuite n'a le niveau de qualité nécessaire, l'indépendance aux gafams et la simplicité d'installation/utilisation pour mes projets et applis en cours (si quelqu'un connaît une lib PHP ou JS pour faire ça, je prends avec enthousiasme)... Je me suis donc rabattu vers un microservice externe.
Ce qui m'embêtait, c'était que mes élèves se retrouvent sur mes pages - dans lesquelles je fais en sorte qu'aucune donnée ne sorte vers des tiers - avec un appel direct à une API extérieure, donc avec un problème RGPD éventuel.
Du coup, j'ai fait un wrapper de cette API que je peux moi-même appeler comme une API: ainsi, l'appel côté client se fait toujours vers mon serveur qui répercute la requête vers VoiceRSS, récupère le retour et renvoie sous forme de fichier mp3. De plus, si une requête a déjà été faite, elle a été mise en cache par le wrapper, ce qui diminue la charge serveur et le nombre d'appels vers l'API.
Si ça intéresse quelqu'un, je pourrais développer et filer le code...
Petit bout de code fait à la va-vite dans le but de vérifier la validité des liens d'une page. Mon objectif est de pouvoir à terme montrer automatiquement les liens ne répondant plus sur la page où je regroupe les liens de téléchargement illégal
Dans le code ci-dessous, j'utilise fetch et les promesses pour lancer des requêtes afin de styler les liens testés:
function checkLinks(nodelist){
function checkUrl(link){
return fetch(link).then(function(response){
return response.status;
}).catch(function(error){
return error.status;
});
}
if (!nodelist){
checkLinks(document.querySelectorAll('.checkLink'));
return;
}
for (let obj of nodelist){
if (obj.tagName=="A"){
checkUrl(obj.href).then(function(response){obj.classList.add("status"+response);});
}else if (obj.hasAttribute("src")){
checkUrl(obj.src).then(function(response){obj.classList.add("status"+response);});
}else{
checkLinks(obj.querySelectorAll("*[href],*[src]"));
}
}
}
checkLinks();
J'ai d'abord fait une fonction qui appelle une URL et renvoie une promesse qui, une fois résolue, renverra le statut de la requête. On y retrouve .then
qui renvoie le statut d'une requête qui aboutit et .catch
qui renvoie celui d'une requête se soldant par une erreur.
function checkUrl(link){
return fetch(link).then(function(response){
return response.status;
}).catch(function(error){
return error.status;
});
}
Les plus coquinous d'entre-vous me feront remarquer à juste titre que, vu ce que je renvoie, je pouvais me contenter de
function checkUrl(link){
return fetch(link).then(function(response){
return response.status;
});
}
Ceci dit, en prévoyant les deux cas, je m'autorise à gérer le retour différemment selon si ça aboutit ou pas (par exemple retourner «ok200» ou «error404»
Ce block n'est là que pour simplifier la vie de l'utilisateur en lui évitant de procéder lui-même au querySelectorAll()
if (!nodelist){
checkLinks(document.querySelectorAll('.checkLink'));
return;
}
Je parcours la nodeList en vérifiant l'URL passée en href (pour les A) ou en src (pour les img par exemple).
Le dernier cas est celui où l'on souhaite vérifier les liens contenus dans un div portant la classe .checkLink
: il suffit d'appeler la même fonction de façon récursive en lui fournissant le nodeList des liens contenus das le DIV en question. (ça permet de vérifier un grand nombre de liens sans avoir à leur ajouter individuellement la classe .checkLink
, ce qui est particulièrement utile quand on publie des articles en utilisant markdown )
for (let obj of nodelist){
if (obj.tagName=="A"){
checkUrl(obj.href).then(function(response){obj.classList.add("status"+response);});
}else if (obj.hasAttribute("src")){
checkUrl(obj.src).then(function(response){obj.classList.add("status"+response);});
}else{
checkLinks(obj.querySelectorAll("a,img"));
}
}
Il suffit de mettre la classe .checkLink
à tout objet dont on veut tester les liens et de coller la fonction dans la page puis de l'appeler via un checkLinks();
de bon aloi.
En l'état, la fonction ajoutera une classe .status200
pour les liens ok ou .status404
pour les URL qui ne répondent plus.
Il ne reste plus qu'à styler ces classes en changeant la couleur, le fond ou en ajoutant des emoji avec un petit content
. On peut même éventuellement masquer un objet dont l'URL ne répond pas...
Le script étant en JS, il se heurte évidemment aux règles de la politique CORS: toute requête hors du domaine en cours va échouer à moins de redéfinir le CORS dans le Head de la page via
<meta http-equiv="Content-Security-Policy" content="Content-Security-Policy:..."/>
Comme d'habitude, il s'agit autant d'un proof of concept que d'une truc utile... en tout cas, n'hésitez pas à en faire rigoureusement ce que vous voulez: c'est cadeau...