D-Nada Vos développements
d'application et site web
humain et sur-mesure
contact@d-nada.com

Plume

Plume est un framework Javascript/PHP orienté objet et OpenSource, léger et permettant une facilité et souplesse dans le développement de vos applications Web.
Une création DNada.
Mise à jour le .

Sommaire

Présentation

Dans les grandes lignes, Plume permet de : Pour bénéficier de ces fonctions (et d'autres), il suffit de faire unrequire("plume.php"); en PHP et………… et bien c'est tout.

Exemple typique d'une page HTML :
<!DOCTYPE html>
<html lang="fr">
<head>

</head>
<body>

<!−−− une partie dynamique qui sera remplacé par une fonction PHP −−−>
<ul>
{.ma_liste()
<li id="item{id}">
{text}
</li>
.}
</ul>

<button id="mon_bouton">Mon bouton</button>

</body>
</html>
D'un script JS :
<script>
$.start(function() {
// une méthode JS qui sera appelée lors d'un clic sur l'élément HTML avec l'id=mon_bouton
const mon_bouton = $('mon_bouton');

mon_bouton.on('clic', function(event, tag) {

// appel d'une méthode PHP depuis JS
$.ajax.ma_methode(42, [1, 2, 3, 5, 7]).then(function(resultat) {resultat
mon_bouton.innerText = resultat.titre;
mon_bouton.style.backgroundColor = resultat.couleur;
})
})

});
</script>
Et d'un script PHP :
<?php
// permet d'ajouter des fonctionnalités de debugging au poste indiqué par son IP
define('PLUME_DEBUGGING', '79.0.1.2');

// spécifie une adresse mail où le développeur pourra recevoir des données de débugging.
define('PLUME_REPORT', "report@ma−societe.com");

// ajoute plume au PHP
require('plume.php');

// ouvre la base de données
$base = new Base("mon−client");

// gère les méthodes asynchrones ajax (voir JS : RPC)
Template::ajax(function() use($base) { $base−>close(); });

// affiche la page HTML et les templates (voir HTML : Template)
Template::display();

// exemple de fonction template (dans l'exemple HTML plus haut)
function template_ma_liste($pattern) {
global $base;
return $base−>select()
}
// exemple de méthode ajax (dans l'exemple JS plus haut)
function ajax_ma_methode($premier = 'default', $deuxieme = false, $etc = null) {
return ['titre' => "Nouveau nom du bouton", 'couleur' => "blue"];
}
?>
Cette exemple simple montre :

HTML

Plume apporte des fonctionnalités au niveau du HTML.

HTML : Template

Le mécanisme de template permet de décorréler la partie statique d'une page HTML de la partie dynamique généré par un script PHP.
Elle permet de dynamiser des parties à l'intérieur des pages HTML, en les reliant à des fonctions PHP, pour générer du HTML dynamique, qui viendra remplacer ces parties statiques HTML.

Cette page HTML :
<html>
<body>
La valeur est 42 !
</body>
</html>
Peut être généré par une page template HTML :
<html>
<body>
{*ma_fonction_php(42)
La valeur est {valeur} !
*}
</body>
</html>
Associée à une fonction PHP qui viendra remplacer la partie dynamiquement du template :
<?php
function template_ma_fonction_php($pattern) {
return "La valeur = 42";
}
?>
Renverra :
<html>
<body>
La valeur = 42
</body>
</html>
Ou encore via avec en paramètre passé à cette fonction :
<?php
function template_ma_fonction_php($pattern) {
return "La valeur = " . $pattern−>parameter(1) . '.';
}
?>
Renverra :
<html>
<body>
La valeur = 42.
</body>
</html>
Ou mieux encore, par une fonction PHP qui viendra remplacer les parties précises du template :
<?php
function template_ma_fonction_php($pattern) {
return $pattern−>html([
'valeur' => $pattern−>parameter(1),
]);
}
?>
Renverra :
<html>
<body>
La valeur = 42 !
</body>
</html>

Template : Définition

La syntaxe d'un template est délimité par des accolades, unifié par un caractère non-alphabétique et nommé avec les mêmes règles de nommage des fonctions PHP (alphabétique et espace souligné), suivi d'éventuel paramètres, encadré de parenthèse.
<html>
{?nom_de_fonction() … ?}
{*nomDeFonction(un, deux, trois) … *}
</html>
Les templates peuvent contenir des champs, modifiable depuis la fonction PHP, dont le nommage reprend la syntaxe des variables PHP (sans le dollar) encadré d'accolade.
<html>
{+fonction()
texte statique {variable_dynamique} texte statique
+}
</html>

Template : Principe

Pour arriver dynamiquement à ce résultat :
<html>
<h1>Liste des satellites de Jupiter</h1>
<ol>
<li>Io</li>
<li>Europe</li>
<li>Ganymède</li>
<li>Callisto</li>
</ol>
</html>
On créé une page HTML (exemple page.html), contenant :
<html>
<h1>Liste des satellites de {planete}</h1>
<ol>
{*satellite()
<li>{nom}</li>
*}
</ol>
</html>
On code en PHP, les deux templates "planete" et "satellite" :
<?php
require("plume.php");

function template_planete() {
return "Jupiter";
}

function template_satellite($pattern) {
$html = '';
foreach (['Io', 'Europe', 'Ganymède', 'Callisto'] as $satellite)
$html .= $pattern−>html( [ 'nom' => $satellite ] );
return $html;
}
?>
Et on termine en demandant d'afficher ces templates :
<?php
Template::display("page.html");
?>

Si un tableau est renvoyés par une méthode template, il sera converti en chaîne de caractère, permettant de travailler non-pas sur des concaténations de chaîne de caractères, mais sur des suites d'éléments :
<?php
function template_bidule($pattern) {
$html = [];
foreach (['Io', 'Europe', 'Ganymède', 'Callisto'] as $satellite)
$html[] = $pattern−>html( [ 'nom' => $satellite ] );
return $html;

}
?>

Template : Imbrication

Les templates peuvent s'imbriquer.
Le principe est de nommer la ou les sous-templates avec un nom, encadré d'un caractère non-alphabétique différent que le template conteneur.
Le nom de ses sous-template correspondent à des noms de fonction PHP (comme pour le template conteneur), et ces derniers seront appelés durant l'exécution du$pattern−>html() du template conteneur, en lui passant en 2ème paramètre un paramètre "Extra" reprenant la valeur passé dans lepattern.

En reprenant l'exemple précédent, on peut afficher plus d'information par satellite.
<html>
<h1>Liste des satellites de Jupiter</h1>
<ol>
<li>
Io
<ul>
<li>Diamètre: 3 643Km</li>
<li>Période: 42h</li>
<li>Gravité: 1,79m/s²</li>
</ul>
</li>
<li>
Europe
<ul>
<li>Diamètre: 3 121Km</li>
<li>Période: 85h</li>
<li>Gravité: 1,315m/s²</li>
</ul>
</li>
<li>
Ganymède
<ul>
<li>Diamètre: 5 268Km</li>
<li>Période: 7j</li>
<li>Gravité: 1,428m/s²</li>
</ul>
</li>
<li>
Callisto
<ul>
<li>Diamètre: 4 806Km</li>
<li>Période: 16j</li>
<li>Gravité: 1,236m/s²</li>
</ul>
</li>
</ol>
</html>
On modifie la page HTML, avec :
<html>
{*planete()
<h1>Liste des satellites de {planete}</h1>
<ol>
{+satellite()
<li>
{nom}
<ul>
{.information()
<li>{nom}: {valeur}</li>
.}
</ul>
</li>
+}
</ol>
*}
</html>
Et on code en PHP, le template « planete », le template imbriqué « satellite », et le sous-template imbriqué « information » :
<?php
require("plume.php");

$planetes = [
"Jupiter" => [
[
"Io" => [
"Diamètre" => "3 643Km",
"Période" => "42h",
"Gravité" => "1,79m/s²",
],
// autre satellite
],
// autre planète
],
];
// template qui sera appelé (par Template::displey()) qu'une seule fois
function template_planete($pattern) {
global $planetes;

foreach ($planetes as $planete => $satellites) {
$html .= $pattern−>html([
'planete' => $planete,
'satellite' => $satellites
]);
}
return $html;
}
// template qui sera appelé pa template_planete autant de fois qu'il y a de planète, avec en extra sa liste de satellites
function template_satellite($pattern, $satellites) {
foreach ($satellites as $nom => $information) {
$html[] = $pattern−>html([
'nom' => $nom,
'information' => $information
]);
}
return implode('', $html);
}
// template qui sera appelé pa template_satellite autant de fois qu'il y a de satellite, avec en extra la liste de ses caractéristiques
function template_information($pattern, $information) {
foreach ($information as $nom => $valeur) {
$html .= $pattern−>html([
'nom' => $nom,
'valeur' => $valeur
]);
}
return $html;
}

Template::display("page.html");
?>

Template : Contrôle

Il est possible d'appliquer des tests côté template.
Il suffit d'encadrer le nom de la macro suivi du texte (pouvant contenir d'autre template) et séparé par un de ces caractères :
  • "?" le template sera remplacé que si la valeur de la macro est vraie.
  • "!" le template sera remplacé que si la valeur de la macro est fausse.
  • "|" le template sera remplacé par sa valeur, ou si fausse, par le texte.

Exemple:
<html>
{?tests()
<div>
{var_ok?test ok}{var_ko?test ko}
{var_ok!sinon ok}{var_ko!sinon ko}
{var_ok|ou alors ok} {var_ko|ou alors ko}
{{var_ko! <span> {var_ok} </span>}}
</div>
?}
</html>
<?php
function template_tests($pattern) {
return $pattern−>html([
'var_ok' => 'yes!',
'var_ko' => '', // ou 0, false, null</abbr>
]);
}
?>
Affichera :
<div>
test ok
sinon ko
x ou alors ko
<span> x </span>
</div>

Template : Paramètrage

On peut passer des paramètres aux fonctions template :
<span>{+parametrage(I, II, III, IV) 1:{un}, 2:{deux}, 3:{trois}, 4:{quatre} +}</span>
<?php
function template_parametrage($pattern) {
return [
'un' => $pattern−>first(),
'deux' => $pattern−>second(),
'trois' => $pattern−>third(),
'quatre' => $pattern−>parameter(4)
]; // affichera : <span>1:I, 2:II, 3:III, 4:IV</span>
}
?>
Voir parameter() pour plus d'information.

Template : relecture en RPC

Enfin, il est possible d'utiliser la notion de template, et durant la vie de la page, d'actualiser ce template depuis le JavaScript, en utilisant le mécanisme RPC, en réutilisant la même fonction PHP.
<html>
<!−−− page.html −−−>
<ul id="ma_liste">
<!−−− template qui sera défini par la fonction template_ma_liste lors de l'affichage de la page −−−>
{*ma_liste(5)
<li>{num}</li>
*}
</ul>
</html>
<script>
// et également suite à un clic fait par l'utilisateur sur cette liste par la fonction ajax_ma_liste
window.on('ma_liste', function() {
$.ajax.ma_liste(42).then(function(html) {
$('#ma_liste').innerHTML = html;
});
});
</script>
<?php
// fonction appelée la 1ère fois pour renvoyer la page HTML, avec le nombre 5 figé dans la page.html
function template_ma_liste($pattern, $valeur_ajax = null) {
$max = $valeur_ajax ?? $pattern−>parameter(1);
for (number = 1; number < $max; ++ number)
$html .= $pattern−>html(['num' => $number]);
return $html;
}
// fonction appelée lors d'une demande ajax, et qui va simuler un RPC pour appeler la fonction template, mais avec le nombre 42
function ajax_ma_liste($valeur_ajax) {
return Template::extract('page.html', "ma_liste", $valeur_ajax);
}
?>
Cet exemple utilise les fonctions asynchrone RPC expliquées plus bas et la méthode extract et render expliquées dans le chapitre suivant.

Template : méthodes

L'API template comprend :

Méthode display:

Permet de transformer les templates puis d'afficher la page HTML ainsi générée.
<?php
if ($il_y_a_une_erreur])
Template::display("error.html");
else if ($il_y_a_quelque_chose])
Template::display("//library/other.html");
else<br> Template::display();
?>
Attention : suite à l'appel de cette méthode, les trace() ne sont plus actif, puisque cette méthode s'occupe de les insérer dans la page HTML.

Note : il est important d'appeler cette méhode, même si la page HTML ne contient pas de template, parce que cette méthode insère les traces, les fonctions ajax et le script et CSS Plume.
Il est possible de se passer de cette méthode, mais il faudra se passer des traces, fonctions ajax et insérer à la main dans le <head> les deux lignes suivantes ou d'utiliser la méthode conclude :
<html>
<head>
<link href="https://www.d−nada.com/plume/plume.css" rel="stylesheet">
<script src="https://www.d−nada.com/plume/plume.js"></script>
</head>
</html>

Méthode html()

Permet de générer dynamique du HTML, en passant des variables pouvant contenir des balises HTML
<html>
{?foo()
Balise {exemple}.
?}
</html>
<?php
function template_foo($pattern) {
return $pattern−>text([ 'exemple' => "<u>prise en compte</u>" ]);
}
?>
Rendra :
Balise prise en compte

Méthode text:

Permet de générer dynamique du HTML, en protégeant les balises HTML.
<html>
{?foo()
Balise {exemple}.
?}
</html>
<?php
function template_foo($pattern) {
return $pattern−>text([ 'exemple' => "<u>non−prise en compte</u>" ]);
}
?>
Rendra :
Balise <u>non-prise en compte</u>

Comme pour [Template−>html()](#### Méthode html()), cette méthode utilisé sans paramètre, permet de renvoyer le contenu de la balise, permettant par exemple de tester si le template est utilisé en tant que conteneur ou non :
<html>
C'est élément {?test_vide()?}
C
'est élément {?test_vide() avec du contenu ?}
<html>
<?php
function template_test_vide($pattern) {
return $pattern−>text() ? "contient quelque chose." : "est vide.";
}
?>
Rendra :
C'est élément est vide.
C'est élément contient quelque chose.

Méthode parameter()

Permet de récupérer les paramètres :
  • parameter(x) : renvoie le xème paramètre.
  • parameter(1) : identique à first().
  • parameter(2) : identique à second().
  • parameter(3) : identique à third().
  • parameter(0) : renvoie l'ensemble des paramètres sous forme d'une chaîne de caractère : "un, ...".
  • parameter(true) : renvoie l'ensemble des paramètres sous forme d'un tableau : ['un', …].
  • parameter(): renvoie le nombre de paramètre.
    <html>
    {?foo(un , "deux", 3)

    ?}
    </html>
    <?php
    function template_foo($pattern) {
    $count = $pattern−>parameter(); // renverra le nombre de paramètres => 3
    $element = $pattern−>parameter(1) // si positif non−nul, renverra le xème élément => 'un'
    $list = $pattern−>parameter(true); // renverra la liste des paramètres => ['un', 'deux', '3']
    $string = $pattern−>parameter(0) // renverra la chaîne de paramètre => 'un , "deux", 3'
    }
    ?>

Méthode first()

Renvoie le premier paramètre.
<html>
{?foo(one, two, three)?}
</html>
<?php
function template_foo($pattern) {
return $pattern−>first(); // remplace le template par "one"
}
?>

Méthode second()

Renvoie le second paramètre.
<html>
{?foo(one, two, three)?}
</html>
<?php
function template_foo($pattern) {
return $pattern−>second(); // remplace le template par "two"
}
?>

Méthode third()

Renvoie le troisième paramètre.
<html>
{?foo(one, two, three)?}
</html>
<?php
function template_foo($pattern) {
return $pattern−>third(); // remplace le template par "three"
}
?>

Méthode extract()

Permet d'extraire un template ou le rendu HTML d'un template suivant son nombre de paramètre :
  • si moins de 3 paramètres : renvoie un template.
  • si 3 paramètres : renvoie le rendu HTML du template.
Sa signature est :
  1. Nom du fichier :
    • Si vide ou absent, prend le fichier de même nom que le dossier où se trouve le PHP, suffixé de ".html"
    • Si commence par //, part de la racine du serveur
  2. Nom du template : si vide, prend la totalité du fichier comme template
  3. Extra : si ce paramètre est présent, le rendu ne sera pas un template mais un rendu HTML, et la fonction du template sera exécuté avec second paramètre "extra" cette valeur (qui peut être égale ànull).


Exemple:
<html>
Partie non−extraite
{?foo()
text {field}
{+bar()
and {field}
+}
?}
Partie non−extraite
</html>
<?php
function template_foo($pattern) {
return $pattern−>html([ 'field' => "foo" ]);
}
function template_bar($pattern, $extra = null) {
return $pattern−>html([ 'field' => "bar" ]);
}

// récupère le pattern "foo" de la page "page.html"
$pattern = Template::extract("/page.html", "foo");

// parse simple
echo $pattern−>html([ 'field' => "test and" ]);
echo "<br>";

// ou parse en profondeur dans les imbrications
$foo = Template::render(template_foo($pattern));
echo $foo;
?>
Rendra :
text test and
text foo and bar
Si vous n'utilisez pas de paramètre "Extra" des [imbrication de template][Template : Imbrication], il est possible de raccourcir cette présentation :
<?php
return Template::render(template_models(Template::extract('', "models")));
?>
Par :
<?php
return Template::extract('', "models", null);
?>

Méthode render()

Même principe que pour display, mais en lui passant la chaîne de caractère.
<?php
$foo = Template::render("
{+foo()
and {field}
+}
");
function template_foo($pattern) {
return $pattern−>html([ 'field' => "foo" ]);
}
?>
Note : L'autre différence avec la méthode display, c'est qu'elle ne s'occupe pas d'insérer la notion de trace(), ni les fonctions ajax, ni le script et CSS Plume.
Elle est à utiiser avant tout pour gérer des morceaux de page HTML. Pour un rendu d'une page entière, il est préférable d'utiliser la méthode display ou alors utilisé en plus la méthode conclude.

Méthode conclude:

Cette méthode sert à ajouter les ressource nécessaire au bon fonctionnement de Plume, tel que les traces, les fonctions ajax, …
Elle ne devrait pas être utilisée, puisqu'elle est intégrée à la méthode display, mais peut être utile dans certain cas, tel qu'une sortie parexit :
<?php
if ($error_quil_faut_corriger) {
trace("Contexte", $error_quil_faut_corriger);
Template::conclude();
exit;
}
?>

Méthode ajax()

Cette méthode recherche si une fonction RPC est appelée, et si c'est le cas, lance la méthode associée et sort du PHP (elle exécute unexit), sinon revient sans rien faire.
Elle est obligatoire pour que le mécanisme de RPC fonctionne. Si absente, aucune méthode JS de type$.ajax.method() ne fonctionnera.
Elle peut prendre un paramètre optionnel qui sera la fonction à appeler en fin de traitement de la méthode, pour fermer un fichier, une base, …
<?php
Template::ajax();
Template::display();

// ou avec action post ajax passé en paramètre
$base = new Base("ma_base");
Template::ajax(fn() => $base−>close());

Template::display();
$base−>close();
?>
Note : Plume renvoie trois informations complémentaire dans l'entête HTTP "Server-Timing" des réponses Ajax, qu'il est possible d'intercepter au niveau JS pour le débogging, de syntaxe :Server−Timing: plume;dur=x.xxx,mem;dur=yyy,len;dur=zzz
  • x.xxx: Le temps écoulé pour traiter la requête côté serveur, en microsecondes.
  • yyy: La quantité de mémoire utilisée par PHP pour traiter cette requête.
  • zzz: le nombre d'octet de cette réponse.

Define:

Des fonctionnalités plus bas niveau, mais non obligatoire peuvent être mis en fonctionnement.

PLUME_PATH:

SiPLUME_PATH est défini comme fonction, elle sera appelée avant toutes transformations, afin de modifier le chemin d'accès au fichier templates.
  1. Paramètre: le chemin du fichier
  • En retour : renvoyant ce chemin éventuellement transformé.
Cela sert pour modifier le chemin par exemple pour d'autre média, pour un mode débogging, pour un client particulier, … :
<?php
define("PLUME_PATH", function($path) {
global $mobile;
if ($mobile)
return preg_replace('/.(?=w+)$/', '−mobile.', $path);
return $path;
});
?>

PLUME_TEMPLATE:

SiPLUME_TEMPLATE est défini comme fonction, elle sera appelée après toutes transformations, afin de préparer cette transformation du template.
  1. Paramètre: la page transformé, mais contenant encore les templates non reconnus du genre :{*un_template_inconnu_dans_php()*}
  • En retour : obligatoire renvoyant la page avec d'éventuelle transformation.

Exemple:
<?php
define("PLUME_TEMPLATE", function($page) {
if (preg_match_all('/{(W)w+(?:(.*?)).*?}s', $page, $template_missing))
trace("Template missing", array_columns($template_missing, 0));
return str_replace('DNADA', "<a href='https://www.dnada.fr'>DNADA</a>");
});
?>

PLUME_PUR:

SiPLUME_PUR est défini afalse, (par défaut correspond àtrue), Plume ne cherchera pas à protéger les adresses (téléphone, mail, …).

HTML : Extension des éléments HTML standard

INPUT attribut "indeterminate"

Si on ajoute à un input de type "checkbox" l'attribut "indeterminate", cette boite à cocher aura 3 états (au lieu des 2) : cocher, décocher et indéfini.
<html>
<label><input type="checkbox"> Boite à cocher à deux états standard</label>
<label><input type="checkbox" indeterminate="false"> Boite à cocher à trois états</label>
<label><input type="checkbox" indeterminate="true"> Boite à cocher à trois états et mis à l'état indéfini</label>
</html>
Rendra


INPUT.state()

Cette méthode permet de gérer l'état "indéfini" des boites à cocher. ❝
  • Si passé avec un argument négatif ouundefined, affecte sonindeterminate = true
  • Si passé avec un élément vrai (true, 1, "une chaîne non vide", …), affecte sonchecked = true
  • Sinon, affecte sonchecked = false

Exemple:
<script>
const input = $('INPUT?checkbox');
const state = input.state(); // renvoie l'état : true si cocher, false si non cocher, undefined si indéfini
input.state($.NEXT); // simule un click
input.state(false); // si faux (false, 0, '', …) décoche la boite à cocher
input.state(undefined); // passe la boite à cocher dans un état indéfini
input.state(−1); // même principe que undefined</abbr>
input.state(true); // si vrai (true, > 0, objet, …) coche la boite à cocher
let indefini = input.indeterminate; // récupère le booléan de l'état d'indéfini
</script>
Note : Le fait d'accepter indifféremment la valeurundefined ou négative comme un état "indéfini" permet (entre autre) de traiter cet état soit comme faisant partie plutôt de cocher ou plutôt de non-cocher :
<script>
let état_vrai = undefined; // ou faux ou vrai
if (état_vrai)
// uniquement si cocher uniquement

let état_pas_faux = −1; // ou faux ou vrai
if (état_pas_faux)
// si cocher ou indéfini
</script>

HTML : $event

Ajout l'attribut $event au balise HTML, identique à l'attribut onevent, mais permet d'appeler la fonction JS, en passant les mêmes paramètres et contexte que la Element.on
<div onclick="monClick()"></div>
Est identique à :
<div $click="monClick(this)"></div>
Avec en plus, lors de l'appel de la méthode JS :
  • Lethis est positionné sur la balise rattaché à cet évènement.
  • Un 2ème paramètre est passé représentant le targetCible, soit la balise HTML ayant réellement reçu l'évènement.

HTML : calendrier

Il est possible de faire apparaitre un calendrier de 3 manières différentes :
  • Sous forme d'un panneau surgissant (popup) rattaché aux<input type="date">.
  • Sous forme d'un panneau surgissant (popup) associé à un élément HTML.
  • Sous forme d'un élément HTML indépendant non-popup et donc toujours visible.
La présentation du calendrier est segmentée en 3 parties :
  1. Le nom du mois et de l'année en cours d'affichage (en haut à gauche). Un clic sur cette zone permet d'afficher un panneau pour repositionner le calendrier directement sur une un mois et/ou année.
  2. Une barre de navigation avec les boutons :
    • ◀ et ▶ : Permettant de passer au mois précédent ou suivant.
    • ▲ et ▼ : Permettant de passer à l'énuméré précédent ou suivant (voir l'attribut enum).
    • : Permettant de repositionner le calendrier sur la 1ère date sélectionnée, si sélection existante.
    • 📅 : Permettant de repositionner le calendrier sur la date du jour.
    • 🗑 : Permettant de vider la zone de saisie stockant la date.
  3. Le corps du calendrier, contenant en colonne les jours de semaines et en lignes les semaines scrollables :
    • Le mois en 1ère semaine (uniquement dans les calendriers fixes)
    • La date du jour.
    • Un marquage de couleur indiquant la date du jour.
    • Un éventuel marquage de couleur indiquant les jours fériés.
    • D'autre élément HTML personnalisable par le développeur (voir PlumeDate.reloadDate()).

Calendrier : De type Date

Plume surcharge les balisesinput de type "date" , en donnant la possibilité aux anciens navigateurs d'avoir un popup, tout en ajoutant des fonctionnalités aux navigateurs récents, avec les attributs suivants :
  • enum : permet de spécifier une suite de liste de date ou d'interval formatés, qui seront seul à être sélectionnable.
  • holiday : true ou disabled indique qu'il faut afficher les jours feriés, et si "disabled" que ces jours ne sont pas sélectionnables.
  • interval : si "true" la sélection est constitué d'une date de début et de fin.
  • min : date minimum au format aaaa-mm-jj
  • max : date maximum au format aaaa-mm-jj
  • stamp : permet de définir et regrouper les jours de la semaine sélectionnables (1 = lundi à 7 = dimanche). Elle est constitué de groupes de jours, séparé par des virgules et ayant 2 syntaxes possibles : sélecteur ou sélecteur:sélection. Un clic sur un jour d'un sélecteur, sélectionnera l'ensemble des autres jours de ce sélecteur. La sélection permet de sélectionner des jours différents si besoin.
    • 1234567 : les jours sélectionnables. Exemples: "12345", "1245", "67", …
    • 1-7 : les jours sous forme d'interval. Exemples: "1-5", "4-7", …
    • 1-7:1-5 : le selecteur des jours + ':' + la sélection de jour qui sera saisie. Exemple: "12:1-3,45:3-5".
  • week : la liste des jours sous forme de suite de chiffre (0 = dimanche, 1 = lundi, …, 7 = dimanche, uni éventuellement par un trait d'union), qui seront affichés et formeront la semaine dans l'ordre indiqué, suivi éventuellement d'un '+' et des jours à griser :
    • 1-5 : affiche une semaine de 5 jours, du lundi au vendredi, le samedi et dimanche disparaissent.
    • 1-5+67 : affiche une semaine de 7 jours mais dont les samedis et dimanches seront grisés et non-sélectionnables.
    • 0-6 : affichera une semaine entière, mais en commencant par dimanche

Exemple:
<html>
<input type="date">
<input type="date" stamp="">
<!−−− interval −−−>
<input type="calendar" end="+">
<input type="calendar" begin="">
</html>
Ces attributs peuvent être transmis depuis JS via une structureElement.$date à créer si besoin, dans ce cas, l'attribut passé en paramètre dans cette structure est prioritaire à l'attribut passé dans la balise HTML :
<script>
const input = document.body.create('input', {type: 'date'});
input.$date = {};
input.$date.format = '%d/%m/%y';
$.ajax.recuperer_enumere().then(enumerate => input.$date.enum = enumerate);
</script>

Note : si desenum sont spécifiés, le comportement du calendrier change légèrement, au niveau des sélections possible bien sûr, mais également au niveau des boutons de navigation, qui ne passe plus d'un mois à l'autre (◀ et ▶), mais d'un énuméré à un autre (▲ et ▼).

Un popup calendrier s'ouvre et se ferme automatiquement par rapport aux actions de l'utilisateur. Néanmoins, il est possible de fermer ce calendrier depuis JS, en appelant la méthode générique $.closes().

Calendrier : Associé

Reprend le concept de l'INPUT de type Date mais en l'appliquant sur n'importe quel élément HTML.
Il suffit pour cela de placer l'attributdate.
La balise reprend les mêmes attributs que précédemment (enum,holiday, …) et en ajoutant les attrbuts suivants :
  • date: qui peut contenir :
    • "false" : pour désactiver le calendrier.
    • "true" : pour rattacher un calendrier.
    • "un format": une chaîne de cartactère pour formater la date. Voir Date.format() pour plus d'information.
  • format (ou depuis la structureElement.$date.format): qui n'est qu'un alias de date uniquement pour spécifier le formatage. L'attributdate reste obligatoire pour activer le calendrier.

À noter que si ce n'est pas une date unique mais un interval entre deux dates qui est demandé (interval="true"), il est possible de différencier le formatage en mettant les substitutions en minuscules pour la date de début et en majuscule pour la date de fin (voir exemple plus bas).
<html>
<span date="%d/%m/%y">Exemple de popup−date<span>
</html>
Exemple avec une balide de type <span> :
<span date="%lw %d/%2m/%4y" stamp="1−2,4−5" week="1245+67">
exemple
</span>
Qui rend clickable les paires lundi-mardi ou jeudi-vendredi et qui grise les week-ends et masque les mercredis. : Merci de cliquer ici pour sélectionner une date.
Ou avec ce <div> :
<div date="true" interval="true" format="du %d/%2m/%4y au %D/%2M/%4Y" week="0−5">
</div>
Qui permet de faire une sélection et affiche les deux dates depuis un calendrier commençant par dimanche et en excluant les samedis :
Merci de cliquer ici afin de faire apparaitre un calendrier, permettant de sélectionner par glissement (ou via la touche majuscule), un interval entre deux dates

La valeur qui sera utilisée dans le calendrier et modifiée depuis le calendrier, est enregistrée dans (par ordre de priorité) :
  1. Element.$value : enregistré uniquement si elle contient une valeur différente deundefined ou denull.
  2. Element.$value() : si existe, appelle cette méthode sans paramètre pour récupérer la date et avec la date modifiée en paramètre pour la stocker.
  3. Element.value : appartenant aux élémentsINPUT etTEXTAREA.
  4. Element.value() : si existe appelle cette méthode comme pour la méthodeElement.$value().
  5. Attribute('value') : si l'élément possède un attribut "value" (même vide).
  6. Element.innerText : correspondant aux éléments HTML.

Calendrier : Fixe

Plume ajoute la balise HTML<plume−date> pour gérer un calendrier directement dans la page, toujours visible.
Il reprend tous les mécanismes et attributs indiqués précédement, en ajoutant :
  • height (ouElement.$date.height): la hauteur fixe (en pixel) des jours, qui ne peut être inférieur à 17. Si absent, la hauteur est calculée suivant le contenu des jours (voir plus bas PlumeDate.reloadDate).

Exemple:
<html>
<plume−date holiday="true" interval="false" stamp="12:1−3,45:3−5" week="1−7" height="33" style="height: 227px; width: 230px;" />
</html>

Le résultat est stocké dans sa valeurPlumeDate.value.

Il implémente également un ensemble de méthode :

PlumeDate.eventDateCallback()

Cette méthode permet de passer une méthode à une instance dePlumeDate.
Cette dernière sera appelée lorsqu'un évenement de typemousedown ouchange se produit sur le calendrier, avec en signature :
  1. Event: un objet de type "Event" contenant :
    • type: de l'élément.
    • dayTag: l'élément HTML représentant le jour.
    • dayTime: la date sous forme numérique.
    • daySelect:true si le jour a été sélectionné.
  2. Target: l'élément HTML sur lequel l'évenement s'applique.

Exemple:
<script>
const calendar = $('PLUME−DATE');
calendar.eventDateCallback(eventDate);

function eventDate(event, tag) {
switch (event) {
case 'mousedown':
break;
case 'change':
break;
}
const date = new Date(event.dayTime * (24*60*60*1000));
}
</script>

PlumeDate.reloadDate()

Cette méthode permet de passer éventuellement une méthode à une instance dePlumeDate et lui demander de rafraîchir le calendrier.
Si méthode passée, cette dernière sera appelée de 2 façon différentes :
  1. Pour chaque jour à afficher, avec en signature :
    1. dayTag: l'élément HTML contenant le jour, et possédant les propriétés suivantes :
      • dayTag.$time : la date en milliseconde sous forme numérique. Qui peut être transformée en jour avecnew Date(dayTag.$time).
      • dayTag.$tick : la date en jour sous forme numérique. Qui est égale àdayTag.$time / (24*60*60*1000).
      • dayTag.$weekday : le jour de la semaine de 1 pour lundi à 7 pour dimanche.
      • dayTag.selectDay() : méthode permettant de sélectionner ce jour (voir plus bas).
      • dayTag.disableDay() : méthode permettant de désativer ce jour (voir plus bas).
    • Elle peut interagir avec le calendrier en renvoyant :
      • Soit une selection, sous forme d'un boolean qui permettra de savoir s'il faut sélectionner ce jour.
      • soit des Options, sous forme d'une structure pouvant contenir :
        • options.select: sélectionne (si vrai) ou déselectionne (si faux) le jour affiché ou ne fait rien (siundefined).
        • options.disable: active (si vrai) ou désactive (si faux) le jour affiché ou ne fait rien (siundefined).
        • options.height: permet de spécifier la hauteur en pixel propre à ce jour.
  2. Dans le cas où l'on aurait besoin de connaitre la 1ère date de la sélection (cas d'un clic sur ""), avec en signature :
    1. Aucun paramètre.
    • Doit renvoyer dans ce cas la valeur du 1er jour sous forme de microseconde, ou faux si aucune date sélectionnée.
Cette méthode est surtout utilisée pour personnaliser le contenu desdayTag:
Si cette méthode ne passe aucun paramètre (ouundefined), l'éventuelle précédente méthode passée est conservé. Si la méthode doit être supprimée, il faut lui passerfalse en paramètre.

Exemple en ajoutant une boite-à-cocher sur tous les mercredis et en désactivant les mardis.
<script>
const calendar = $('PLUME−DATE');
calendar.reloadDate(function(dayTag) {
if (! dayTag) {
return Date.now();
}

const dayWeek = dayTag.$weekday;

// créé une boite à cocher pour tous les mercredis
if (dayWeek == 3) {
const label = dayTag.create('label');
label.innerText = "option: ";
label.create('input', {type: 'checkbox'});
}

// désactive les mardis
return {
disable: dayWeek == 2,
};
});
</script>
<html>
<plume−date holiday="true" interval="false" stamp="12:1−3,45:3−5" week="1−5" style="height: 300px; width: 600px;" />
</html>

Attention: au niveau développement, le calendrier doit être considéré comme une représentation d'une partie des jours, qui doivent être géré en dehors par votre programme JS.

Plusieurs méthodes sont rattachées àdayTag :

dayTag.selectDay()

Cette méthode rattachée àdayTag permet de sélectionner ou non le jour représenté pardayTag et prend en signature :
  1. Selection: sélectionne si vrai ou désélectionne si faux, ou ne fait rien siundefined.
  • Renvoie toujours son état sous forme d'un boolean.

dayTag.disableDay()

Cette méthode rattachée àdayTag permet de désactiver ou activer le jour représenté pardayTag et prend en signature :
  1. Selection: désactive si vrai ou active si faux le jour, ou ne fait rien siundefined.
  • Renvoie toujours son état sous forme d'un boolean.

HTML : slide(s)

Ajout un nouveau type d'input de valeur sous forme d'une glissière.
Accepte les attributs suivants :
  • min : valeur entière la plus basse.
  • max : valeur entière la plus haute.
  • value : valeur optionnelle d'initialisation, qui sera modifiée.
  • rule : sitrue affiche une échelle, si un nombre entier positif ou un tableau[2, 3, 5, 7, 11, 13, 17, 19, 99], représente le nombre de pas de l'échelle.
  • for : optionnel, permet de spécifier un élément qui recevra la valeur (voir les fonctions du $)

slide

Glissière à une seule valeur :
<html>
<input type="slide" min="0" max="9" rule="2" for="#slide_value_id" value="1" style="width: 200px;">
<span id="slide_value_id"></span>
</html>
valeur :

slides

La différence est au niveau des attributs :
  • value : qui contient deux valeurs numériques, séparées par une virgule.
  • for : qui contient l'id de l'élément, préfixé automatiquement d'un "1" et "2".
Double glissière à deux valeurs :
<html>
<input type="slides" min="1" max="9" rule="[1, 4, 7, 9]" for="#slides_value_id" value="2,3" style="width: 300px;">
<span id="slides_value_id1"></span>
<span id="slides_value_id2"></span>
</html>
valeur minimum : et maximum :

HTML : strip

Ajout d'un nouveau type d'input de sélection d'énumérés :
  • De type "strip" :
  • De type "zip" :
L'attributenum peut recevoir soit une chaîne de mots, soit une liste de chaînes, soit un objet d'attribut/valeur.
<html>
<input type="strip" enum="lundi mardi mercredi jeudi vendredi" value="mardi">
<input type="strip" enum="['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi']" value="">
<input type="strip" enum="{lun: 'lundi', mar: 'mardi', mer: 'mercredi', jeu: 'jeudi', ven: 'vendredi'}">
</html>
Voici la liste des attributs pouvant être utilisés :
  • Attributclass : permet de personnaliser les éléments internes à cet énumérés, en spécifiant ce nom de classe soit sur l'input, soit sur le template, et le précédé du nom de la baliseplume−enum dans la feuille de style.
  • Attributdisabled et la variableElement.disabled :
    • true si désactivé entièrement.
    • Une chaîne de mots séparés d'espaces indiquant les éléments à désactiver.
    • Un tableau, de genre :['item'] donc chaque valeurs indique l'élement à désactiver.
    • Une structure, de genre :{key: item} donc chaque clés indique l'élement à désactiver.
  • Attributenum : attributs contenant la liste des données, sous forme de :
    • Une chaîne de mots séparés d'espaces.
    • Un tableau, de genre :['item']
    • Une structure, de genre :{key: item}
  • Attributmin : indique le nombre minimum sélectionnable. Si = "0" indique que l'énuméré peut avoir aucun élément sélectionné. Par défaut = "1".
  • Attributmax : indique si l'énuméré peut recevoir plusieurs valeurs :
    • "true" : accepte plusieurs valeurs possible
    • séparateur (non numérique) : indique le caractère de séparation qui sera utilisé pour séparé les choix possibles, dansvalue.
    • nombre + séparateur : indique le nombre maximum de possibilité (2, 3, …) et le caractère de séparation sera utilisé pour séparé les choix possibles, dansvalue.
    • sinon (par defaut), un seul choix possible
  • Attributorientation : sens de l'affichage :
    • down: affichage de haut en bas
    • right: affichage de gauche à droite (par défaut)
  • MéthodeElement.select() : permet de pré-selectionner un ensemble d'énumérés (même si pas multiple) sans les sélectionner dansvalue. La syntaxe est la même que pour l'attribut "show". Un clic ou un changement deElement.value effaçant cette pré-sélection, attention donc à bien faireElement.value = avantElement.select = .
  • Attributshow et la variableElement.show : permettent d'indiquer à tout moment les éléments sélectionner. Peut contenir :
    • Une chaîne, avec les éléments séparés par le séparateur indiqué dans l'attribut "multiple" ou par un espace
    • Une valeur numérique, dont chaque bit représente l'état du xème élément.
    • Un tableau des éléments actifs
    • Toute autre valeur (false, objet, …) est considéré comme faux.
  • Attributtemplate : indique le id d'un<template> permettant de personnaliser l'affichage. Les données sont récupérés des éléments fils HTML de ce template, référencé par leur attribut "value".
  • Attributtype : accepte le type "strip" ou "zip"
  • Attributvalue et la variableElement.value : la valeur ou les valeurs séparés par le caractère de séparation.
Exemple :
<html>
<style>
plume−enum.rank span {
background−color: yellow;
}
<style>
<input type="strip" template="rank" name="ranked" value="">
<template id="rank" class="rank">
<span value="flat" title="Classe de haut en bas"><svg width="24" height="20"><path d="M8,2v18l2,−6h−4l2,6"/></svg></span>
<span value="deep" title="Classe par imbrication"><svg width="24" height="20"><path d="M4,6h18l−6,2v−4l6,2"/></span>
<span value="both" title="Classe par imbrication et de haut en bas"><svg width="24" height="20"><path d="M4,6h18l−6,2v−4l6,2M8,2v18l2,−6h−4l2,6"/></span>
</template>

<input id="format" type="strip" enum="B I U S" max="true">
</html>
<script>
$.start(function() {
$('format').on('change', function() {
console.log(this.id, this.show);
});
});
</script>

Il est possible de créer ce type d'élément HTML directement depuis le JS :
<script>
const zip = document.create('input', {type: "zip", template: "un−template"});
</script>

Il est possible de connaitre le dernier sélectionné :
<script>
document.body.create('input', {type: 'zip', enum: {'0': "zéro", '1': "un", '2': "deux"}}).on('change', function(event, tag) {
trace('click', this.lastSelected, this.values.includes(this.lastSelected) ? 'sélectionné' : 'déselectionné');
});
</script>

HTML : Simon

Plume apporte une nouvelle balise de saisie<plume−simon> : 😶 😀 ️🙂 😐 🙁 😡
Elle permet de rassembler un ensemble fini d'éléments sous une forme de roue (ressemblant au jeu Simon).
Il est possible de regrouper les éléments sur plusieurs couches, du plus centré au plus exentré.

Simon : HTML


Cette balise HTML possède les attributs suivants :
  • degree: valeur numérique représentant l'orientation, de l'ensemble des niveaux de la roue (sauf contre indication propre à un niveau). Compris entre 0 et 360 degré, 0 (ou 360, 720, …) commençant en haut, 90 à droite, 180 en bas et 270 à gauche. Par défaut = 0.
  • clock: valeur booléan indiquant le sens de l'affichage des éléments, pour l'ensemble des niveaux de la roue (sauf contre indication propre à un niveau), si vrai, tourne dans le sens des aiguilles d'une montre, ou si faux, dans le sens inverse. Par défaut =true.
  • min: valeur numérique supérieur ou égale à 0, spécifiant le nombre minimum d'un élément sélectionnable. Par défaut = 1.
  • max: valeur numérique supérieur à 0, spécifiant le nombre maximum d'un élément sélectionnable. Par défaut = 1.
  • width: valeur numérique supérieur à 1, indiquant le diametre en pixel de la roue. Par défaut = 24.
  • height: identique à width. Prendra la valeur la plus petite si les deux attributs sont indiqués.
  • value: valeur d'un des éléments, qui sera sélectionné.
  • values: suite de valeur des éléments, qui seront sélectionnés, séparé généralement par une virgule (ou tout caractère ne se trouvant pas dans la liste des valeurs possibles (voir plus bas)).
  • color: valeur de la couleur du fond et de l'encre de cet item, formé d'une chaîne "fond encre". Par défaut = "#ffffff #000000" (noir sur fond blanc).
  • active: valeur de la couleur du fond et de l'encre de cet item lorsqu'il est sélectionné, formé d'une chaîne "fond encre". Par défaut = "#ffff00 #000000" (noir sur fond jaune).
  • shadow: valeur de la couleur du fond et de l'encre de cet item lorsqu'il n'est pas sélectionné, formé d'une chaîne "fond encre". Par défaut = noir sur la couleur du fond 16% plus sombre.
  • disabled: attribut boolean indiquant si le Simon peut être utilisé.
  • style: valeurs CSS pouvant préciser :
    • width: en pixel, prioritaire sur l'attribut width.
    • height: en pixel, prioritaire sur l'attribut height.
    • font-family: nom de la famille de police ou de la police. Par défaut : sans-serif.
    • font-size: en pixel, indiquant la taille de la police à utiliser. Par défaut = 14px.

Les éléments sont placés dans le corps de la balise, à l'intérieur des balises suivantes :
  • item: balise indiquant le texte à afficher (texte qui doit rester court pour être affichable), et pouvant posséder l'attribut :
    • value: indiquant la valeur de cet élément.
  • level: balise permettant de créer un niveau extérieur, contenant à son tour des balisesitem etlevel, et pouvant contenir les attributs :
    • degree: identique à l'attributdegree mais propre à ce niveau.
    • clock: identique à l'attributclock mais propre à ce niveau.
    • color: valeur de la couleur du fond et de l'encre de cet item, formé d'une chaîne "fond encre". Par défaut = "#ffffff #000000" (noir sur fond blanc).
    • active: valeur de la couleur du fond et de l'encre de cet item lorsqu'il est sélectionné, formé d'une chaîne "fond encre". Par défaut = "#ffff00 #000000" (noir sur fond jaune).
    • shadow: valeur de la couleur du fond et de l'encre de cet item lorsqu'il n'est pas sélectionné, formé d'une chaîne "fond encre". Par défaut = noir sur la couleur du fond 16% plus sombre.
Les niveaux commencent au centre de la roue, la position de la ou des baliseslevel ne sont pas importantes, elle peut être placée indifféremment avant, au milieu ou après les balisesitem, et si plusieurs baliseslevel sont présents dans un même niveau, elles seront regroupées.

Exemple HTML :
<html>
<plume−simon degree="360" clock="true" min="0" max="8" width="80" style="height:150px; font−size: 12px;" values="1,7">
<item value="1">I</item>
<item value="2">II</item>
<item value="3">III</item>
<level degree="180" clock="false"> <!−−− prochain niveau commence en bas et remonte sur la droite −−−>
<level> <!−−− et le dernier niveau recommande en haut pour descendre sur la droite −−−>
<item value="8">VIII</item>
<item value="9">IX</item>
<item value="10">X</item>
<item value="11">XI</item>
<item value="12">XII</item>
</level>
<item value="4">IV</item>
<item value="5">V</item>
<item value="6">VI</item>
<item value="7">VII</item>
</level>
</plume−simon>
</html>
Donnera uns balise Simon sur 3 niveaux, avec la possibilité de choisir entre 0 et 8 éléments, pré-sélectionné sur les éléments 1 et 7 :
I II III VIII IX X XI XII IV V VI VII

Simon : JS


Il est possible de créer et gérer cette balise en JS.

La création se fait comme toute balise HTML, avecDocument.createElement('plume−simon') ou avecElement.create('plume−simon').

Il est possible d'interagir directement depuis les propriétés suivantes de l'objetPlumeSimon :
  • degree: identique à l'attribut degree. Cette propriété accepte qu'une valeur numérique,PlumeSimon.degree = NaN ne changera pas la valeur.
  • radian: identique àdegree mais avec une valeur en radian. Donc de 0 à 2π. Cette propriété accepte qu'une valeur > 2, unPlumeSimon.radian = NaN ne changera pas la valeur.
  • clock: identique à l'attribut clock. UnPlumeSimon.clock = undefined ne changera pas la valeur.
  • min: identique à l'attribut min.
  • max: identique à l'attribut max.
  • width: identique à l'attribut width.
  • height: identique à l'attribut height.
  • value: valeur d'un des éléments deitems, qui sera sélectionné. Identique à l'attribut value.
  • values: tableau de valeur des éléments deitems, qui seront sélectionnés, séparé généralement par une virgule (ou tout caractère ne se trouvant pas dans la liste des valeurs deitems).
  • style: ClassList de l'élément.
  • items: tableau indexé contenant les éléments du premier niveau.
  • disabled: si vrai, le Simon ne peut être clickable.

Un tableau indexéitems peut avoir les propriétés suivantes :
  • items : un tableau indexé contenant les éléments d'un niveau supplémentaire extérieur.
  • degree : orientation en degré propre à ce niveau.
  • radian : orientation en radian propre à ce niveau.
  • clock : sens propre à ce niveau.

Attention :PlumeSimon va chercher à se redessiner à chaque affectation d'un nouveau tableau dansitems, mais ne fera rien si on modifie directement un sous-niveau. Il faut bien réaffecter le tableau si changement d'un de ses propriétés.
<script>
const simon = document.create('plume−simon');

const items = [
"niveau 1 element 1",
"niveau 1 element 2",
];
// affichera un simon de 2 éléments sur 1 seul niveau
simon.items = items;

// evolue le tableau d'un niveau, mais qui ne réaffichera pas automatique le simon
items.items = [
"niveau 2 element 1",
"niveau 2 element 2",
];
items.items.degree = 180; // le 2ème niveau par du bas

// affichera un simon de 4 éléments sur 2 niveaux
simon.items = items;
</script>

Les éléments sont sous forme de valeur de type :
  • Chaîne de caractère, qui sera utilisé pour affichée l'élément et comme valeur de cet élément.
  • Structure, comprenant les propriétés suivantes :
    • value: valeur de l'élément.
    • text: texte à afficher (de préférence court).
    • color: valeur de la couleur du fond et de l'encre de cet item, formé d'une chaîne "fond encre" ou d'un tableau indexé ["fond", "encore"]. Par défaut = "#ffffff #000000" (noir sur fond blanc).
    • active: valeur de la couleur du fond et de l'encre de cet item lorsqu'il est sélectionné, formé d'une chaîne "fond encre" ou d'un tableau indexé ["fond", "encore"]. Par défaut = "#ffff00 #000000" (noir sur fond jaune).
    • shadow: valeur de la couleur du fond et de l'encre de cet item lorsqu'il n'est pas sélectionné, formé d'une chaîne "fond encre". Par défaut = noir sur la couleur du fond 16% plus sombre.
Les retours-chariot sont acceptés dans le texte, et peut être représenté avec le méta-caractère "\n" ou la balise<br> (seul balise HTML acceptée).

Il est possible de rattacher l'évenementchange sur la balise afin d'intercepter les changements de valeurs.

Exemple (identique au HTML précédent, mais) en JS :
<script>
const input = document.create('plume−simon');
input.att('style', "height: 100px; font−size: 12px;");

const items = [
[
{value: 4, text: "IV"},
{value: 5, text: "V"},
{value: 6, text: "VI"},
{value: 7, text: "VII"},
],
{value: 1, text: "I"},
{value: 2, text: "II"},
{value: 3, text: "III"},
];
input.min = 0;
input.max = 2;
input.width = 150;
items[0].degree = 180;
// items[0].radian = Math.PI;
items[0].clock = false;

input.items = items;
input.values = [1, 7];

input.on('change', () => {
// si choix multiple (max > 1) affiche le dernier sélectionné
if (input.values) {
// si max > 1
trace("dernier sélectionné", input.value);
}
// si clic sur "I", supprime les autres choix
if (input.value == 1)
input.value = 1;
});
</script>

HTML : Menu

Il est possible d'assigner un menu à un élément HTML :
Directement depuis le HTML, avec l'ajout du typemenu (associé à l'attributenum etmultiple) ou de l'attributmenu :
<html>
<label>
Liste des items:
<input type='menu' enum='["item1", "item2", "item…"]' value="item1">
</label>
<span menu='["item1", "item2", "item…"]'>Liste des items</span>
<input menu='{i: "Un", ii: "Deux", iii: "Trois", iv: "Quatre", v: "Cinq", vi: "Six", vii: "Sept", viii: "Huit", ix: "Neuf", x: "Dix"}'>
</html>

Par défaut, un seul élément est sélectionnable, sauf si l'attributmultiple est positionné àtrue.
En spécifiant un nom de fonction PHP RPC suivi de parenthèse (obligatoires) encadrant d'éventuel paramètres :
<html>
<span menu='exemple(un, deux)' name="hello" id="hola">Bonjour</span>
</html>

Le menu va essayer d'appeler cette fonction, de signature "ajax_menu_<nom>($attributs, $parametres, …)" pour alimenter son menu.
Le 1er paramètre de cette fonction, est un tableau de tous les attributs de l'élément HTML rattaché au menu, et l'attribut "menu" contient la chaîne des paramètres éventuels passés.
Les autres paramètres correspondent aux paramètres éventuels passés.
<?php
function ajax_menu_exemple($attributs, $arg1 = null, $arg2 = null) {
return [$arg1, $arg2];
}
?>
Dans l'exemple :
<script>
$attributs = [
'menu' => "un, deux",
'name' => "hello",
'id' => "hola",
];
</script>
L'attribut menu peut contenir le nom d'une variable global JS contenu dans l'objetwindow.$menus, permettant de programmer plus finement le comportement du menu.
Sa structure se compose des méthodes suivantes, toutes optionnelles, qui seront appelées à certain moment de la vie du menu :
  • load: appelé au début de page -> permettant de gérer la post-récupération des données du menu.
  • open: juste avant la demande d'ouverture du menu -> permettant de mettre à jour les items du menu (actif, titre, …) ou d'interdire l'ouverture.
  • change: appelé lors d'un changement du contenu du trigger de type INPUT, uniquement lorsque le menu est ouvert -> pour modifier le contenu du menu en direct.
  • over: lorsque la souris se déplace sur les items du menu.
  • select: lorsque l'utilisateur aura sélectionné un élément -> pour déclencher d'autre action.
  • alias: lorsqu'un item est sélectionné par un raccourci clavier.
  • close: lorsque le menu se ferme.
Enfin, les options permettent d'affiner des comportements bien précis.
<script>
window.$menus.mon_menu = {
// appelé pour initialiser le menu au démarrage ou suite à un reloadMenu()
load: [ // sous forme d'un tableau indexé
"texte", // le texte de l'item
{ // ou sa version plus complète
text: "texte {x:makeup}", // le texte de l'item contenant d'éventuel expressions intégrées
html: "<span>texte</span>", // ou sa forme HTML
key: 'clé', // la clé unique associée
alias: '±K', // le raccourci clavier précédé eventuellement de + (alt) et/ou − (shift) (voir plus bas)
disabled: false, // si true, l'item n'est pas sélectionnable
enabled: true, // si === false, l'item n'est pas sélectionnable, si true prioritaire sur le disabled
confirm: '', // si chaîne de caractère non vide, elle sera affichée pour confirmer la sélection de cet item
response: '', // si confirm, nom des boutons de validation
},
"−−−", // séparateur
false, // séparateur
{ separator: true }, // séparateur
],
load: { // sous forme d'un tableau associatif
key1: "texte",
key2: {
text: "texte",
html: "<span>texte</span>",
alias: 'K',
disabled: false,
enabled: true,
},
key3: "−−−", // séparateur
key4: false, // séparateur
. key5: { separator: true }, // séparateur
},
load: function(menu, update, reload, complement) { // ou sous forme d'une méthode renvoyant un tableau
// renvoie directement un tableau
return { };

// renvoie en asynchrone
$.ajax.rpc().then(function(data) {
menu.value = 42; // corrige la valeur passée dans un reload éventuel, pour le communiquer au select()
update(data);
});

// Attention au classement des items avec un tableau ayant des clés numériques qui sera affichés sur ces clés
return { 2:'A', 9:'B', 1:'C' }; // affichera un menu : C A B
return { s2:'A', s9:'B', s1:'C' }; // affichera un menu : A B C, parce que les clés sont des chaînes de caractères
return [ 'A', 'B', 'C' ]; // affichera un menu : A B C, mais avec des clés 0, 1 et 2
return [ {key: 2, text: 'A'}, {key: 9, text: 'B'}, {key: 1, text: 'C'} ]; // affichera un menu : A B C, avec les clés : 2, 9, 1

},
// appelé lors d'une demande d'ouverture de menu ou juste avant l'appel du select lors d'un raccourci clavier (si la méthode alias n'est pas défini)
open: function(menu, event, update) {
const contextual = event.button == 2; // pour différencier le menu contextuel d'un autre clic, dans le cas où right = "all"

menu.makeup = 'x'; // pour personnaliser le text des items avec les expressions intégrées (voir plus bas)

return true; // autorise l'ouverture
return {} || []; // ouvre avec de nouvelle donnée (même principe que pour le load)
return false; // interdit l'ouverture
return; // interdit l'ouverture

// ou actualiser le menu au moment de l'ouverture (mais mécanisme plus lent pour l'utilisateur)
// si le menu a déjà été récupéré, le renvoie
if (this.cache)
return this.cache;
// sinon, le demande au serveur, le stock puis le renvoie
if (update) { // test que l'on peut supprimer si pas de raccourci clavier dans ce menu
$.ajax.rpc(menu.trigger.value).then(data => update(this.cache = data));
}
},
// appelé juste après l'affichage du menu, afin d'éventuellement apporter des modifications dessus
display: function(menu) {
const content = menu.menu.shadowRoot.$('UL');
const items = content.$$('LI'); // récupère les items fabriqués avec <i>texte</i><span style="position:absolute">suffixe</span>
const width = items.reduce((width, item) => {
const large = item.offsetWidth + item.parentElement.lastElementChild.offsetWidth;
return large > width ? large : width;
}, 0) + 40;
if (width > content.offsetWidth) {
content.style.left = content.offsetLeft + content.offsetWidth − width + 'px';
content.style.width = width + 'px';
}
},
// appelé lors d'un changement du contenu du trigger, uniquement si le menu est ouvert
change: function(menu, event, update) {
const value = menu.trigger.value;
menu.items.forEach(item => item.hidden = value && ! value.includes(item.text));

return; // ne fait rien
return false; // ne fait rien
return true; // réaffiche le menu
return {} || [] // réaffiche ce nouveau menu
},
// appelé lors d'une sélection d'un item du menu
select: function(menu, item ou items, event, reloaded) {
const item = items[0]; // dans le cas où l'option.multiple = vraie

switch (item.key) {
case 'clé':
// …
break;
}
return true; // valide et referme le menu
return "valeur"; // valide sur la valeur renvoyée et ferme le menu
return false; // ne valide pas la sélection et ferme le menu
return; // ne valide pas la sélection et laisse ouvert
},
// si présent, sera appelé sur un raccourci clavier à la place d'un open() + select()
alias: function(menu, item ou items, event, reloaded) {
const item = items[0];
this.open(menu);
if (item.enabled || item.enabled === undefined && ! item.disabled)
return this.select(menu, item);
return true; // arrêt et supprime la propagation du raccourci
return false; // continue à chercher sur d'éventuel autre menu et supprime la propagation du raccourci
return; // propage le raccourci
},
// si présent, sera appelé juste après un "open" pour renvoyer la position du menu
position: function(menu, event, box) {
// box: élément HTML contenant le menu
return; // position 'auto'
return [left, top];
return {left: left, top: top};
return new Point(left, top);
return event.target.bound.botRight.sub(box.offsetWidth);
},
// appelé sur le survol d'un item
over: function(menu, item, event) {
return false; // interdit le survol de l'élément
},
// appelé lors d'une demande de fermeture du menu
close: function(menu) {
},
// option supplémentaire sur le comportement du menu (avec leurs valeurs par défaut)
option: {
cursor: true || false,
delay: 0 || 1000,
filter: true,
focus: true,
mark: false || true,
max: 1000,
min: 3,
position: 'auto' || function(menu, event, box) { … },
right: true,
search: undefined || false || true,
multiple: true,
},
event: null, // est mis à jour par PlumeMenu
};
</script>
Pour modifier le comportement du menu, plusieurs options sont possibles, accessible dans la structureoption :
  • cursor: sifalse, n'affiche pas de curseur "menu" au survol de l'élément.undefined correspondant à vrai par défaut.
  • delay: permet de retarder l'affichage du menu de x millisecondes.undefined correspondant à 0 par défaut.
  • filter: si vrai, filtre les éléments affichés par rapport au contenu de la zone de saisie.undefined correspondant à faux par défaut.
  • focus: si vrai, ouvre le menu dès l'entrée dans la zone de saisie.
  • mark: si positif, indique que la ou les valeurs passées danstrigger.reloadMenu() vont être coché ("✓") dans le menu, même si leload spécifie une fonction (normalement, c'est à la charge de cette méthode ou àopen() de mettre à jour l'attributcheck des items).
  • max: si positif, indique le nombre maximum d'élément à afficher.undefined correspondant à aucune limite par défaut.
  • min: si positif, indique le nombre minimum d'élément avant d'autoriser son ouverture.undefined correspondant à 0 par défaut.
  • multiple: si vrai, permet la sélection de plusieurs éléments (le paramètre items devenant un tableau).undefined correspondant à faux par défaut.
  • position: le menu se positionne toujours en dessous ou au dessus de la zone de saisie ou à la position du clic de la souris, mais il est possible de spécifier sa position :
    • Si 'auto' (par défaut) positionne suivant le clic
    • Si 'left': positionne toujours sur la gauche de l'élément HTML.
    • Si 'right': positionne toujours sur la droite de l'élément HTML.
    • Si c'est une function(menu, event, box): peut renvoyer une position calculée qui servira au positionnement du menu, de type [left, top] ou {left, top} ou {x, y} (ou Point, Rect).
  • right: permet d'indiquer le comportement du clic droit :
    • Siundefined (par défaut): le menu apparait sur le clic gauche uniquement, le clic droit affichant le menu-contextuel du navigateur
    • Si faux: le menu apparait sur le clic gauche et le clic droit ne permet plus d'afficher le menu-contextuel du navigateur
    • Si vrai: le menu apparait que sur le clic droit uniquement, le gauche ne provoquant plus l'apparition d'un menu.
    • Si égale à "all": le menu apparait sur les deux clics, permettant d'afficher éventuellement 2 menus distincts (différenciable avecevent.button == 2 dans leopen()).
  • search: indique si la saisie de caractère permet de se rapprocher des premiers éléments, avec comme valeur possible :
    • faux : aucun rapprochement, la saisie reste sur l'éventuel "input".
    • true : rapprochement en "commençant par"
    • * : rapprochement "contenant".

Les paramètres passés à ces méthodes, sont :
  • menu: structure, contenant :
    • name: nom de l'attribut HTML "menu".
    • trigger: l'élément auquel est rattaché ce menu
    • target: l'élément qui a déclenché l'ouverture du menu (donc soit trigger (ex:UL) soit un de ses enfants (ex:LI)).
    • config: son this
    • reload: valeur passé par la méthodetrigger.reloadMenu(valeur).
    • reloaded: à true si le select() a été appelé suite à un reload(), permettant par exemple de ne pas faire certaines actions, alors que cette demande ne provient pas d'une action utilisateur, mais du programme.
    • items: tableau contenant les items (voir plus bas). Ce tableau est indexé sur l'ordre d'affichage et également nommé par les clés :

Exemple:
<script>
// exemple :
items: [
0: { key: 'un', text: "item1" },
1: { key: 'deux', text: "item2" },
2: { key: 'trois', text: "item3" },
un: { key: 'un', text: "item1" },
deux: { key: 'deux', text: "item2" },
trois: { key: 'trois', text: "item3" },
];
// utilisable sous forme d'itération :
items.forEach(item => …);
// et également sous forme direct associatif :
items['deux'].disabled = true;
items.deux.check = true;
</script>
    • menu: l'élément HTML plume-menu
    • selected: tableau mis à jour après sélection d'un item et reflétant la liste des items cochés.
    • value: valeur passée par la méthode reloadMenu(), pouvant être interceptée et corrigée dans la méthodeload(reload) et utilisée dans la méthodeselect(reload).
    • cursor: valeur pouvant être passée dans la méthodeopen() avec l'option.multiple a faux, permettant d'indiquer le numéro de l'index (0, 1, …) de l'item qui doit être coché (avec une marquecheck).
  • complement : valeur optionnelle passée par la méthode reloadMenu(complement) à la méthodeload(complement)
  • update : méthode que l'on peut appeler pour renvoyer un résultat en différé (asynchrone).
  • items : correspond à :
    • sioption.multiple à vrai : un tableau des items cochés, le 1er élément étant celui sélectionné par l'utilisateur.
    • sioption.multiple est faux : à la structure de l'item sélectionné.
  • item: utilisé dansover(), correspond à menu.items[index], contenant une structure d'item (voir plus bas).
  • event: un Event qui s'est produit lors de l'appel à cette méthode (pour gérer les méta-touches, …).

Ce paramètremenu est également rattaché à l'élément HTML via la variableElement.$menu, permettant par exemple de récupérer le dernier élément sélectionné :
<html>
<input type="menu" enum='{un: "I", deux: "II", trois: "III", quatre: "IV"}' id="romain">
</html>
<script>
$('input#romain').on('change', function(event) {
trace('changement du menu romain', this.$menu.selected.key);
});
</script>

La méthode alias (ou select si absente) sera appelée qu'une seule fois, même si ce menu est rattaché à plusieurs éléments.
L'ensemble des items peuvent être passés sous forme d'un tableau indexé ou d'un structure.
Chaque item peut être représenté par différent type :
  • Une chaîne de caractère ou valeur numérique : chaîne de caractère qui sera affiché. Ce texte peut contenir des expressions intégrées (voir plus bas).
  • Un booléen : indiquant un trait de séparation horizontal sur tout la largeur du menu. Identique àitem.separator ou àitem.text: "−−−".
  • Une structure composée des attributs tous optionnels suivants :
    • key: mot clé ou index associé à l'item. S'il n'est pas communiqué, la clé de l'item sera soit l'index du tableau, soit la clé de la structure.
    • text: chaîne de caractère qui sera affiché. Ce texte peut contenir des expressions intégrées (voir plus bas).
    • html: chaîne de caractère qui si présent, sera affichée à la place detext et pouvant contenir des balises HTML, CSS, et des expressions intégrées.
    • alt: chaîne de caractère remplaçanttext ouhtml si le texte doit changer sur l'appui de la touche de modification "Alt" (Option sur MacOS). Il peut contenir des expressions intégrées et des balises uniquement s'il remplace unhtml.
    • shift: même principe quealt mais pour la touche de modification "Majuscule".
    • shiftAlt: même principe quealt mais si les 2 touches de modification "Majuscule" et "Alt" sont appuyés.
    • disabled: si vrai, désactive cet élément, donc toujours visible mais non utilisable (grisé) et reste visible à la différence de "hidden". Sidisabled === −1, le grisé n'est pas activé.
    • enabled: si vrai (ouundefined), active cet élément, le "vrai" est prioritaire sur le "vrai" du disabled
    • hidden: si vrai, ne tient pas compte de cet élément, donc non utilisable et non visible.
    • separator: si vrai, permet d'afficher un trait de séparation horizontal sur tout la largeur du menu, identique àitem.text: "−−−" ou à unitem = false.
    • check: si vrai, cet item sera marqué d'un coche "✓", il est prioritaire sur la mark.
    • image: URL optionnelle vers une icône qui sera affichée en amont ou à la place du texte de l'item. L'image est positionné en haut à gauche de l'item (au niveau de la mark) et réduite au maximum à la hauteur de l'item. C'est au graphiste de fournir une image avec la largeur, la transparence et les bordure nécessaire à sa bonne visibilité.
    • mark: permet d'afficher une marque en amont du texte :
      • Si vrai, cet item sera marqué d'un tiret "-",
      • Si numérique utilise les marques prédéfinies suivantes :
10✧ : étincelle vide20✦ : étincelle pleine
1◊ : losange11☆ : étoile vide21★ : étoile pleine
2✻ : astérisque12○ : rond vide22● : rond plein
3✘ : ︎croix13△ : triangle vide23▲ : triangle plein
4✎ : crayon14◻ : carré vide24◼ : carré plein
5➤︎ : flèche15⃟ : losange vide25◆ : losange plein
6➥︎ : retour16♤ : pic vide26♠ : pic plein
7➽︎ : signe17♧ : trèfle vide27♣ : trèfle plein
8☞ : doigt18♡ : cœur vide28♥ : cœur plein
9¶ : paragraphe19⚐ : drapeau vide29⚑ : drapeau plein
      • Si caractère (tel que ⭐, ⚫, ⚪, ♿, ♻, …), sera utilisé comme marque. Attention,"1" représente un caractère et affichera1, alors que1 représente une valeur numérique et affichera le 1er élment de la liste de marques prédéfinis précédente, soit :.
      • Si chaîne de caractère de type URL (http://site.com/path/image.png ou path/image.svg) vers une ressource (image, svg, …), cette ressource servira de marque.
    • alias: chaîne de caractère indiquant la touche qui peut déclencher ce choix, associé à une touche majuscule, option, … (voir les raccourcis clavier).
    • suffix: Chaîne de caractère qui sera affichée sur la droite, séparé du texte principal.
    • button: chemin vers un bouton HTML ("#id", "div.classe > input@nom", …) présent qui lors du clic sur ce bouton, déclenchera les mêmes actions que le choix de cet item, avec évenutellement la confirmation.
    • confirm: chaîne de caractère qui si présent, sera affichée si l'item est sélectionné, afin de demander à l'utilisateur de valider son choix. Généralement utilsé sur les items finissant par 3 points de suspension, alertant sur une action sensible. Utilise les Expression intégrée.
    • optional: associé àconfirm, permet de proposer de ne plus afficher cette alerte à l'utilisateur suivant son choix. Doit correspondre à une chaîne de caractère qui sera le mot-clé du tableau$.message.optionals ou être vrai (et dans ce cas, le mot-clé est égal àkey). Voir les options des messages pour plus d'explication.
    • response: nom des boutons pour une demande de confirmation (voir l'attribut confirm) :
      • Soit un tableau indexé contenant les deux noms : le 1er est le bouton d'annulation, le 2ème le bouton de confirmation.
      • Soit une chaîne de caractère du nom représentant le bouton de confirmation, le bouton d'annulation étant par défaut "Annuler"
      • Si non-présent, prend par défaut :["Annuler", "OK"]
  • Tous les autres types (undefined,function ousymbol) seront ignorés.

Exemple :
<html>
<span menu="exemple_menu">Exemple de menu associé à une zone clickable, dont certains items pourront être déclenchés également par ces 2 boutons : </span>
<button id="exempleMenuBouton">un bouton (simple)</button>
et
<button id="exempleMenuBoutonConfirm">un bouton avec confirmation…</button>
Et en résultat : <span id="exempleMenuResultat"></span>.
</html>
<script>
window.$menus.exemple_menu = {
load: [
42, // numérique
"Texte simple", // texte
false, // séparateur
{
key: 'simple',
text: "Texte simple <span>but</span>"
},
{
text: "Avec un raccourci clavier (Alias)",
alias: "−+⌫",
},
{
key: 'button simple',
text: "Un bouton simple (voir exemple sur la droite ☞)",
button: "#exempleMenuBouton",
},
{
key: 'button with confirm',
text: "Un bouton avec confirmation… (voir exemple sur la droite ☞)",
button: "#exempleMenuBoutonConfirm",
confirm: "Merci de valider ce clic",
response: ["Ne pas cliquer", "Cliquer"],
alias: "−+J",
optional: true,
},
{
text: "Confirm…",
confirm: "Merci de valider ce choix",
response: ["Ne pas valider", "Valider"],
},
{
text: "Désactivé (Disabled)",
disabled: true,
},
{
text: "Activé (Enabled)",
enabled: true, // valeur prioritaire sur le disabled
disabled: true, // valeur non pris en compte
},
{
text: "Item avant le Hidden",
hidden: false,
},
{
text: "Hidden",
hidden: true,
},
{
text: "Item après le Hidden",
hidden: false,
},
{
html: "HTML avec des balises en couleurs et <b style='color: orange'>en gras</b> et <i style='color: magenta'>en italic</i>.",
},
{
text: "Check",
check: true,
},
{
text: "Mark true",
mark: true,
},
{
text: "Mark numérique",
mark: 4,
},
{
text: "Mark UTF",
mark: "",
},
{
text: "Image",
image: "imagemenu.png", // image d'un cône de chantier
},
{
text: "Avec suffix",
suffix: "avec suffixe",
},
{
text: "Avec suffix masquant son raccourci",
suffix: "suffixe",
alias: "−+F19",
},
{
text: "Expression = ( {1:un}{2:deux} )",
},
],
open: function(menu) {
menu.makeup = ! menu.makeup || menu.makeup >= 3 ? 1 : menu.makeup + 1;
return true;
},
select: function(menu, items) {
$('exempleMenuResultat').innerText = items.map(item => item.key).join(', ');
},
option: {
multiple: true,
}
}
</script>
Donnera : Exemple de menu associé à une zone clickable, dont certains items pourront être déclenchés également par ces 2 boutons : et Et en résultat : .
Les expressions intégrées sont des parties dynamique du texte d'un item, permettant de personnaliser le texte par rapport au mot-clé placé dansmenu.makeup.
Leur syntaxe est encadrées d'accolades, commençant par un mot-clé (qui peut être également un chiffre ou vide (par défaut)), et séparé par le reste du texte de substitution, par deux-petits-points :
<script>
window.$menus.menu = {
load: {
item: {
text: "Enregistrer {:l'élément}{multiple:les éléments}…",
confirm: "Merci de valider votre demande d'enregistrer {:cet élément}{multiple:ces éléments}"
},
},
nombre_element() {

}
open: function(menu) {
menu.makeup = this.nombre_element() > 1 ? 'multiple' : false;
},
};
</script>
C'est ce mot-clé qui permet de savoir quelle substitution doit être gardée ou non-prise en compte, par rapport à la valeur demenu.makeup.
Il est possible de passer plusieurs clés, en typantmenu.makeup :
  • comme un tableau indexé contenant des mot-clés
  • comme une structure contenant des mot-clé et/ou des booléans

Exemple:
<script>
window.$menus.menu = {
load: {
item: {text: "Enregistrer {:l'élément}{pluriel:les éléments} {local:en local}{serveur:sur le serveur}{pluriel:}"},
},
open: function(menu) {
menu.makeup = [ ];
// affichera: Enregistrer l'élément en local

menu.makeup = [ 'pluriel', 'local' ];
// affichera: Enregistrer les éléments en local

menu.makeup = [ 'serveur' ];
// affichera: Enregistrer l'élément sur le serveur

menu.makeup = [ 'pluriel', 'serveur' ];
// affichera: Enregistrer les éléments sur le serveur

menu.makeup = { pluriel: true, cible: 'quelque part' };
// affichera: Enregistrer les éléments sur le serveur
},
};
</script>

Si la ou les clés indiquées dansmenu.makeup ne sont pas présentes dansitem.text, c'est l'expression intégrée par défaut (celle qui n'a pas de clé{: xxx}) qui sera utilisée.
Les raccourcies claviers appel uniquement la méthodeshortcut sans gérer le contexte, c'est à la méthode de vérifier si l'action est possible et sur quoi elle est possible.
Ils sont toujours associés à la touche "⌃ Contrôle" sur un Windows ou un Linux, ou la touche "⌘ Commande" sur un Macintosh, et désactivé sur un mobile.
Il est possible d'associé la touche "Majuscule" en précédent le raccourci par le signe "-" et/ou la touche "Alt/Option" par le signe "+".
Certains raccourcis sont à éviter s'ils entrent en conflit avec les raccourcis du navigateur, voir carrément inactivé (raccourci "N" par exemple).
Voici le nom des touches particulières (et leurs caractères d'affichage utilisés) :
  • ↑ ArrowDown
  • ← ArrowLeft
  • → ArrowRight
  • ↓ ArrowUp
  • ⌫ Backspace
  • ⌧ Clear
  • ⌦ Delete
  • ⇲ End
  • ⏎ Enter
  • ␛ Escape
  • ⇱ Home
  • ⇣ PageDown
  • ⇡ PageUp
  • ⇥ Tab
  • les touches de fonction de F1 à F19
En dehors de la méthode générique $.closes(), il est possible de contrôler le menu depuis JS, depuis un ensemble de méthode rattaché à l'élément HTML.

Méthode : attachMenu()

Il est possible de rattacher un menu à un élément HTML depuis JS, via la méthodeElement.attachMenu().
Sa signature est :
Exemple:
<script>
// version simple
$('INPUT').attachMenu(["item1", "item2", "itemx"]);

// ou version plus complète
$('SPAN').attachMenu(menu_conf, 'mon_menu');
</script>
Dans ce premier exemple, on attache un menu composé de 3 choix à l'élément "INPUT".
Et dans ce dernier exemple, on attache un menu nommé "mon_menu" sur l'élément "SPAN", menu défini par la variable "menu_conf".

Si l'élément est éditable (input, …), celui-ci interagirera avec le menu.

Il est possible de rattacher un menu à un ensemble d'éléments :
<script>
[tag1, tag2, …].attachMenu(conf, 'contextual');
$$('SPAN').attachMenu(conf, 'contextual');
</script>
Afin de ne pas recréer un menu pour chaques éléments du tableau, il est conseillé de nommer ce menu ('contextual dans l'exemple précédent), qui sera créé une seule fois et partagé par tous les éléments.

Si l'élément a déjà été rattaché à un menu, les nouveaux rattachements ne seront pas pris en compte et un warning sera affiché sur la console en mode développement :

⚠️Warning: Plume menu already attached to this item.

Afin d'éviter cette alerte, il est possible de tester la présence d'un menu avant de rattacher, en vérifiant la présence de la structure$menu crée dans l'élément, lors du premier rattachement :
<script>
if (! input.$menu)
input.attachMenu(conf);
</script>

Il est également possible de savoir si un menu est ouvert :
<script>
if ($.popuped != 'menu') {
// aucun menu n'est ouvert
}
if (! $.popuped) {
// aucun popup (menu, message, calendrier, dialog, …) n'est ouvert
}
</script>

Méthode : reloadMenu()

Cette méthodeelement.reloadMenu() permet de provoquer une modification du menu, depuis js :
<script>
input.menu.reloadMenu("nouvelle valeur");
</script>
Cette méthode n'existe pas dans l'élément natif, elle est ajoutée lors du rattachement du menu sur l'élément HTML (via l'attributmenu, la méthodeattachMenu(), …).
Elle est accessible également depuis la structure menu :
<script>
window.$menus.mon_menu = {
select: function(menu, items, event, reloaded) {
if (! reloaded) // pour ne pas boucler
menu.reloadMenu(42);
}
};
</script>
Elle a en signature :
  • Clés: une valeur ou tableau de valeur représentant les clés qui seront recherchés et sélectionné dans le menu, et provoque l'appel deload(…, reload) en passant dans le paramètre "reload" cette valeur, puis deselect(…, reloaded = true)
  • complement : une structure optionnelle passée en 4ème paramètre àload(…, complement)
Cette méthode renvoie la structure menu (voir plus haut pour la description).
Un appel à cette fonction sans paramètre, ne fera que redéclencher le mécanisme deload, sans appelerselect.
Il est possible de corriger cette valeur passée en 3ème paramètre 'reload' duload(), en la modifiant dans la structuremenu.value. C'est cette dernière qui sera communiquée auselect() :
<html>
<input menu="mon_menu" id="mon−menu">
</html>
<script>
$('mon−menu').reloadMenu(1);
window.$menus.mon_menu = {
load: function(menu, update, reload, complement) {
menu.value = 2; // modifie la valeur commmuniquée par reloadMenu()
return [0: 'zéro', 1: 'un', 2: 'deux', 3: 'trois'];
}
select: function(menu, items, event, reloaded) {
trace('item', items[0].key); // renverra 2
}
};
</script>

Méthode : activeMenu()

Cette méthode permet de désactiver ou réactiver un menu, en lui passant respectivementfalse outrue.
Par défaut, le menu est bien entendu activé.
<script>
input.menu.activeMenu(false);
</script>
Cette méthode n'existe pas dans l'élément natif, elle est ajoutée lors du rattachement du menu sur l'élément HTML (via l'attributmenu, la méthodeattachMenu(), …).
Elle est accessible également depuis la structure menu :
<script>
window.$menus.mon_menu = {
select: function(menu, items, event, reloaded) {
menu.activeMenu(false);
}
};
</script>

Méthode : shortcut()

Une méthode pour désactiver ou temporiser les raccourcis claviers.
Utiliser si vous ne souhaitez pas que les menus soient accessibles temporairement.
Elle prend trois valeurs possible en paramètre :
  • faux : désactive les raccourcis
  • vrai : temporise les raccourcis, permettant de mémoriser les raccourcis clavier jusqu'au moment de réactivation, où ces raccourcis s'exécuteront.
  • aucun paramètre (ouundefined) : réactive les raccourcis, en exécutant les raccourcis éventuellement temporisés.

Exemple:
<script>
// les raccourcis clavier ne seront executés qu'après avoir relachés la souris
window.on('mousedown', () => PlumeMenu.shortcut(true));
window.on('mouseup', () => PlumeMenu.shortcut());
</script>
À noter que $.message() désactive automatiquement les menus.
Exemple simple statique :
<html>
<input type="text" menu="[Monsieur Madame Mademoiselle]">
</html>

Exemple avec clé différente de la valeur
<html>
<body>
<button search="monsieur">Cliquer ici pour afficher les messieurs</button>
<button search="madame">Cliquer ici pour afficher les dames</button>
<span>Ceci est mon menu</span>
</body>

</html>

<script>
$.start(function() {
// rattache le menu depuis js (on aurait pu faire <span menu="menu_conf"> aussi)
const menu = $('SPAN');
menu.attachMenu(menu_conf, 'mon_menu');

// active les déclencheurs
$$('BUTTON').on('click', button => menu.reloadMenu(button.att('search')));

// gestion du menu
window.$menus.menu_conf = {
caches: {},

load: function(menu, update, reload) {
if (! reload) // ne rien faire au démarrage, uniquement lors d'un déclenchement
return;

// gère une notion de cache supplémentaire
const cache = caches[reload];
if (cache)
return cache;

// appel le serveur si pas en cache
$.ajax.mon_menu(reload).then(function(data) {
caches[reload] = data;
update(data);
});
},

select: function(menu, item, index, event, reloaded) {
trace('la clé choisie', item.key);
return item.value;
}
}
});
</script>

<?php
// init plume, base et ajax
require('plume.php');
$base = new Base("ma_base");
Template::ajax(function() use($base) { $base−>close(); });

// appelé lors d'un déclenchement d'un load n'ayant pas le cache
function ajax_mon_menu($civilite) {
global $base;
return $base−>select("select id, texte from ma_table where civilite = {s}", $civilite);
}
?>
Voir la gestion des bases de données pour plus d'explication sur$base et la gestion des RPC pour plus d'explication sur ajax.

Petit rappel concernant lethis à l'intérieur des fonctions :
<script>
window.$menus.monMenu = {
cache: {},
load: function() {
// le this est bien monMenu dans une fonction fléchée
$.ajax.rpc().then(resultat => this.cache = resultat);

// le this est bien monMenu dans une fonction fléchée avec fermeture
$.ajax.rpc().then(resultat => { this.cache = resultat });

// le this dans cette déclaration de fonction, n'est pas monMenu, mais pointe sur window (ou undefined en mode strict)
function callback(resultat) { this.cache = resultat }
$.ajax.rpc().then(callback);

// même principe pour une fonction anonyme
$.ajax.rpc().then(function(resultat) { this.cache = resultat });

// pour palier à cela, on peut passer par une variable locale pour mémoriser et lui passer le bon this</abbr>
const that_self_zice_etc = this;
$.ajax.rpc().then(function(resultat) { that_self_zice_etc.cache = resultat });

// ou encore mieux, en imposant le bon this via un bind lors de la construction de la fonction
$.ajax.rpc().then((function(resultat) { this.cache = resultat }).bind(this));
};
}
</script>
Voir l'opérateur this pour plus d'information.

HTML : message

Plume propose l'affichage d'une boite de message (alerte, …) de type modal, bloquant l'utilisateur, temporairement ou jusqu'à l'action par validation du message.

Exemples :
avec en retour, le bouton sélectionné : cliquer sur un bouton pour voir le résultat

Cette méthode est toujours en mode asynchrone, en d'autre terme, elle revient immédiatement alors que la boite de message n'est peut être pas encore affichée.
La réponse (le bouton appuyé par l'utilisateur) se faisant par fonction callback asynchrone passé en dernier paramètre ou par une promesse JS.

Sa signature est :
  • Icône : paramètre optionnel, sous forme d'un caractère de type emoji, permettant de "typer" l'information communiquée. Quelques exemples d'icone : '⚠️', '❌', '❓', '❔', '❗️', '❕️', '📣', '📢', '🔔', '💬', '💭', '🗯🛠', '💣', '📨', '📖', '🔍', '🔒'.
    • L'icône peut être seule, ou suivi d'un titre, séparé par ":", qui sera affiché à la droite de l'icône.
  • Message : texte de présentation, pouvant contenir des retours chariots et du HTML. Les éventuels espaces et retour-chariot en début et fin de texte sont ignorés.
  • Boutons: liste des actions possibles, sous forme :
    • de l'absence de ce paramètre ou d'unundefined : dans ce cas, propose juste une temporisation (d'un temps calculé et nécessaire à la lecture du texte) avant de fermer la boite de message.
    • d'une chaîne : un seul bouton
    • d'un tableau indexé ou une structure associative contenant :
      1. Soit le texte du bouton
      2. Soit une structure de type { text: "texte du bouton", key: 'raccourci-clavier' }
  • Fonction de sortie :
    • si présent : fonction qui sera rappelée après action de l'utilisateur, avec l'index ou la clé du bouton choisi par l'utilisateur.
    • si absent : renvoie une promesse.
  • this : le this optionnel passé uniquement à la fonction callback.

Il est possible également de passer en remplacement de tout ou d'une partie de ces paramètres, une structure reprenant l'ensemble de ces paramètres et en ajoutant de nouvelles :
  • type: type de la boite de message. (voir les spécialisations)
  • icon: reprenant la partie gauche du ":" du paramètre "Icône".
  • title: reprenant la partie droite du ":" du paramètre "Icône".
  • message: reprenant le paramètre "Message".
  • buttons: reprenant le paramètre "Boutons".
  • callback: reprenant le paramètre "Fonction de sortie" (utiliser de préférence la Promesse).
  • self: reprenant le paramètre "this".
  • optional: permet d'implémenter le mécanisme de boite à cocher optionnel.
  • texts: permet de passer du texte au mécanime de substitution.
  • reference: permet d'afficher une information technique.
  • class: permet de définir une className.

Exemple:
<script>
$message({
type: 'fault|error|warning|message', // type de boite de message, paramètre optionnel (voir plus bas les Spécialisations)
icon: "", // icône qui sera affiché en début de boite de message.
title: "titre", // texte qui sera affiché à la droite de l'icône.
message: "message…", // texte HTML.
buttons: "bouton", // liste du seul bouton sous forme d'une chaîne de caractère
buttons: ["bouton", {text, key}], // liste des boutons sous forme d'une liste de chaîne de caractère ou structure
callback: function(), // fonction synchrone qui sera appelée
self: this, // uniquement si callback est défini et indiquant le this qui sera passé au callback.
optional: "key", // si présent, lance un mécanisme de boite à cocher optionnel suivant le choix de l'utilisateur. Voir plus bas pour plus d'explication.
texts: [], // si présent, permet de substituer les "^0" dans les title et message
reference: "réf. xxxx", // si présent, affiche cette référence en dehors du texte, en haut à droite de la boite de message
class: "className", // si présent, ajout un ensemble de styles ayant ce nom de classe (en plus des styles générique plume−message)
});
</script>

Le bouton le plus à droite est actionné par la touche retour-chariot "⏎", et celle la plus à gauche par la touche d'échappement "␛".
En dehors du premier et dernier bouton, les éventuels raccourcis clavier sont toujours couplés à la touche méta (touche commande "⌘" sous MacOS et Contrôle sous Windows/Linux), qui peut apparaitre à côté des boutons associés si maintien prolongé de la touche méta.
La fonction callback ou la promesse est appelée avec en paramètre :
  • Si la liste des boutons était un tableau indexé : l'index du bouton sélectionné (0, 1, …).
  • Si la liste des boutons était une structure : une chaîne de caractère correspondant à la clé du bouton.
  • Si aucune liste : renvoie la valeur 0.

Exemples :
<script>
// temporaire (sans gestion du retour de délai)
$.message("Merci beaucoup.");

// information (avec un seul bouton OK, non géré)
$.message("Un erreur est survenue: " . error.message, "OK");

// choix à deux boutons (sous forme d'un tableau) avec retour d'un callback asynchrone
$.message("Merci de valider cette action", ["Annuler", "D'accord"], function(action) {
if (action == 1)
…; // d'accord
});

// choix à deux boutons (sous forme d'une structure) avec retour d'une promesse
$.message("Merci de valider cette action", {cancel: "Annuler", ok: "D'accord"}).then(function(action) {
switch (action) {
case "ok":

}
});

// choix à trois boutons avec raccourci clavier (escape est automatiquement attribué au 1er "cancel" et retour−chariot au dernier "D'accord")
$.message("Merci de valider cette action", {cancel: "Annuler", other: {"J'hésite", key: "J"}, ok: "D'accord"}).then(action => {
switch (action) {
case "ok":

}
});

// fenêtre avec icone d'alerte (voir plus bas la Spécialisation $.message.warning())
$.message('⚠️', "Une exception est survenue.\n\n" + error, "OK");

// fenêtre avec icone + titre d'alerte
$.message('⚠️:Attention', "Une exception est survenue.\n\n" + error, "OK");

// gestion du this</abbr>
window.variable = 'globale';
var une_structure = {
variable: 'locale',

une_methode: function() {
$.message("Callback avec le passage de this", "OK", function(action) { trace('variable ', this.variable) }); // affichera la globale à window
$.message("Callback avec le passage de this", "OK", function(action) { trace('variable ', this.variable) }, this); // affichera la locale à une_structure
$.message("Callback avec le passage de this", "OK").then(function(action) { trace('variable ', this.variable) }); // affichera la globale à window
$.message("Callback avec le passage de this", "OK").then(action => trace('variable ', this.variable)); // affichera la locale à une_structure
$.message("Callback avec le passage de this", "OK").then(action => { trace('variable ', this.variable) }); // affichera la locale à une_structure
}
}

// exemple utilisation en synchrone
(async function(param) {
if (demande_une_validation_client) {
const action = await $.message("texte", ['Annuler', 'Valider']);
if (! action)
return;
}
console.log('ok');
})(42);

// apport de styles personnalisés
$.message("Message avec un style en <span>gras</span> et <output>souligné</ouput>.", {class: 'personnel'});
</script>

<style>
plume−message span {
font−weight: bold;
}
.personnel output {
text−decoration: underline;
}
</style>

Il est possible de savoir si une boite de message est ouverte depuis votre script, en testant la valeur de$.popuped:
<script>
if ($.popuped != 'message') {
// aucune boite de message n'est ouverte
}
if (! $.popuped) {
// aucun popup (menu, message, calendrier, dialog, …) n'est ouvert
}
</script>

Il est également possible de fermer depuis JS une boite de message, en appelant la méthode générique $.closes().

Message: Boutons

Il est possible de passer une signature simplifiée comportant le libellé, suivi d'autant de paramètre de type chaîne de caractère qu'il y a d'intitulé de bouton, et automatiquement suivi d'une promesse.
<script>
function action(bouton) {
switch (bouton) {
case 0:
trace("bouton de gauche");
break;
case 1:
trace("bouton centré");
break;
case 2:
trace("bouton de droite");
break;
}
}

// au lieu de cette syntaxe :
$.message('Texte du message", ['bouton de gauche', 'bouton centré', 'bouton de droite'], action);

// il est possible de l'écrire de cette manière :
$.message(
'Texte du message", 'bouton de gauche', 'bouton centré', 'bouton de droite').then(action);
</script>

Message: Spécialisation fault(), error(), warning() et message()

Quatres sous-méthodes proposent l'accès simplifiées à des messages standardisées :
  • $.message.fault() : à utiliser sur des problèmes internes et/ou bloquant.
  • $.message.error() : à utiliser sur des erreurs.
  • $.message.warning() : à utiliser pour indiquer un avertissement.
  • $.message.notice() : à utiliser pour communiquer une information.

Ces méthodes ont en signature :
  • Titre : chaîne de caractère optionel pour indiquer un titre. Si paramètre absent ou vide, affiche un titre en rapport à la spécialité du message.
  • Message : chaîne de caractère obligatoire contenant le message.
  • Boutons : tableau ou structure optionnel pour afficher une liste de bouton. Si absent :["OK"]
  • Optionnel : chaîne de caractère optionnel permettant d'appliquer le mécanimse de message optionnel sur ce message.
  • Options: structure optionnelle permettant de passer des fonctionnalités supplémentaires.
Si présent, le paramètre boutons doit être sous forme d'un tableau ou d'une structure, même s'il y a un seul ou aucun bouton.

Cette méthode renvoie toujours une promesse pour gérer la suite de l'action.
<script>
$.message.fault("Un problème important est survenu : erreur numéro 42");
$.message.error("Erreur", "Un problème est survenu : erreur numéro 42");
$.message.warning('', "Cette action est non−réversible", "optionel_warning_007");
$.message.notice("Une question", "Merci de valider", ["Non", "Oui"], "optionel_info_007").then(oui => trace("L'utilisateur a choisi ", oui ? "OUI", "NON"));
</script>

Message: Substitution

Il est possible de personnaliser les textes des titres (title) et des messages (message) en plaçant la substitution au niveau du texte par le méta-caractère "^" suivi d'un numéro, qui servira d'index pour rechercher dans le tableauparam.texts.
<script>
const params = {
title: "Erreur #^0",
message: "Exemple d'insertion : erreur numéro ^0: ^1.",
};
params.texts = [
42,
"Erreur totale",
];
$.message(params);
</script>

Message: Optional

Si le paramètreoptional est passé (uniquement en structure de paramètre$.message(…, {optional: xxx});), il permet de déclencher un mécanisme de boîte de message optionnelle.

Sa valeur est une chaîne de caractère qui référence cette boîte de message et la relie à une des clés de la structure$.message.optionals :
  • Si la clé est non trouvée ouundefined, la boîte de message est affichée avec une boite à cocher (non-cochée par défaut) suivi du label stoqué dans la variable$.message.optional (ou si absent, du texte par défaut "Ne plus afficher ce message"), permettant à l'utilisateur de choisir de ne plus afficher cette boîte de message.
  • Si trouvé et différent deundefined, la boîte de message n'est pas affichée, et la fonctioncallback ou la promesse est exécutée avec cette valeur.
Si l'utilisateur décoche cette boite à cocher et valide son choix avec un bouton, la valeur de ce bouton (numérique si tableau indexé ou chaîne de caractère si structure) est mémorisée dans la structure$.message.optionals, afin de ne plus afficher cette boîte de message pour les prochaines fois.
<script>
// initialise au démarrage
$.start(function() {

// modification du texte par défaut si besoin
$.message.optional = "Ne plus afficher ce message";

// supprime l'éventuel option
$.message.optionals.exemple1 = undefined;

// gestion de l'énuméré des options présent sur l'interface
$('@state').on('change', event => $.message.optionals.exemple1 = event.target.value == 'undefined' ? undefined : event.target.value);
});


// gère le bouton déclenchant le message depuis l'interface
$('BUTTON@afficher).on('click', () => {

// affiche le message
$.message("Exemple de boîte de message. Contenant plusieurs lignes Ligne 2 Ligne 3…", {optional:
'exemple1', buttons: {oui: 'Oui', non: 'Non'} }).then(bouton => {
// met à jour la valeur sur l'interface
$(
'@result').value = bouton;
//
$(
'@state').value = $.message.optionals.exemple1 === undefined ? 'undefined' : $.message.optionals.exemple1;
});

});

</script>

<html>
<button name="afficher">Bouton déclenchant la boîte de message</button>

État que l
'on retrouve dans $.message.optionals :
<input type="strip" enum="undefined oui non" value="undefined" name="state">

État renvoyé par la boîte de message :
<input type="strip" enum="oui non" name="result">

</html>
Ce script précédent donnera cet exemple : avec :
  • État que l'on retrouve dans$.message.optionals:
    (cliquer surundefined pour réinitialiser la coche)
  • État renvoyé par la boîte de message:

Pour un contrôle plus fin, il est possible de passer une fonction à la variable$.message.optionals, avec en signature :
  1. La clé de l'option du message
  2. La valeur du bouton sélectionné.
Cette fonction synchrone sera toujours appelée avec le 1er paramètre uniquement, avant ouverture de la boîte de message, et utilisera le retour pour le comportement du message, et sera appelée avec les 2 paramètres si l'utilisateur décoche la boite à cocher et valide son choix.
<script>
const mes_options = {
exemple1: undefined,
}
function optionals(message, optional) {
if (optional === undefined)
return mes_options[message];
mes_options[message] = optional;
}
$.message.optionals = optionals;
$.message("Exemple de boîte de message", {optional: 'exemple1', buttons: ['Oui', 'Non']}).then(bouton => {

});
</script>

Enfin, pour un contrôle déporté, il est possible de déclarer une fonction côté serveur, avec 2 signatures/appels possibles :
  1. La fonction est appelée en début de page HTML (donc après les$.start()), sans paramètre, et devra renvoyer la structure contenant l'ensemble des optionals.
  2. la fonction sera appelée uniquement si l'utilisateur décoche la boite à cocher, avec en signature : clé du message, valeur du bouton.
Le nom de la fonction (côté PHP) doit correspondre àajax_message_<nom spécifié dans la variable $.message.optionals> :
<script>
$.message.optionals = "boites_optionnelles";
$.start(function() {
$.message({message: "Exemple de boîte de message", optional: 'exemple1', buttons: ['Oui', 'Non']}).then(bouton => {

});
}
</script>
<?php
function ajax_message_boites_optionnelles($message = null, $optional = null) {
if ($message === null)
return $_SESSION['mes_options'];
$_SESSION['mes_options'][$message] = $optional;
}
?>

HTML : Dialog


Plume ajoute une gestion de boîte de dialogue, en surchargeant l'élément HTML <dialog>.
Elle apporte des fonctionnalités supplémentaire sur les navigateurs compatible avec <dialog> et une fonctionnalité totale sur les anciennes versions de navigateur.
Elle prend en charge la gestion "modal" ou "flottante", l'affichage d'une barre de titre, le déplacement et redimensionnement du dialogue, de la mémorisation de ces informations localement sur le navigateur (dans localStore), et les événements de validation, d'échappement, de redimensionnement et de fermeture.

La boîte de dialogue se créé en HTML avec une simple balise conteneur <dialog> (que le navigateur soit ou non compatible) et l'attributplume="dialog".
Elle doit contenir obligatoirement un ou deux éléments HTML :
  1. Si un seul élément HTML, celui-ci correspond au conteneur du dialogue, qui ne possèdera pas de barre de dialogue et ne pourra être déplacé (avec la souris, …) par l'utilisateur.
  2. Si deux éléments, le premier (généralement un <legend>) contiendra le titre de la barre du dialogue, qui lui permettra d'être déplaçable par l'utilisateur.

La boîte de dialogue est sensible à la hauteur et largeur maximum (max-height et max-width) et minimum (min-height et min-width) défini éventuellement sur ce conteneur (mais pas sur le <dialog>).
l'attributname permet de personnaliser les paramètrages de position et taille propre à cette boîte. Si l'attribut est absent, sonid sera utilisé.

Il est possible d'ajouter des éléments HTML de type<button> ou<input> pour contrôler la gestion de fermeture du dialogue :
Il faut pour cela que cet élément HTML ne fasse partie d'un formulaire et ajouter un attributtype suivant :
  • submit: qui générera un évenement éponyme, c'est à la méthode qui interceptera l'évenement de fermer le dialogue (ou non).
  • close: qui fermera la boîte de dialogue et générera un évenement éponyme.
Exemple :
<html>
<dialog plume="dialog">
<legend>Dialogue</legend>
<main>
contenu de la boîte de dialogue
<form>
avec un formulaire
Ce bouton ne provoquera que l'action associé au formulaire : <input type="submit">
</form>
<footer>
<button type="close">Annuler</button>
<button type="submit">Valider</button>
</footer>
</main>
</dialog>
</html>
<script>
const dialog = $('dialog');
dialog.on(
'submit', function(event) {
const ok = true;
if (ok)
this.close();
else
$.message.warning("Merci de modifier un truc avant de valider.");
});
</script>

Méthodes de l'élément HTMLHTMLDialogElement :
Méthode statique et propriété de la classePlumeDialog :
  • close(): permet de fermer toutes les boîtes de dialogue ouvertes.
  • opened: tableau contenant toutes les boîtes de dialogue ouverte, du premier plan au plan le plus éloigné.

Il est possible de savoir si une boite de dialogue est ouverte depuis votre script, en testant la valeur de$.popuped:
<script>
if (! $.popuped) {
// aucun popup (menu, message, calendrier, dialog, …) n'est ouvert
}
while ($.popuped == 'dialog') {
// une boite de dialogue est ouverte et au premier plan
$.closes(); // on la ferme
}
if (PlumeDialog.opened.length) {
// au moins une boite de dialogue est ouverte
PlumeDialog.close(); // on ferme toutes les boîtes
}
</script>

Il est également possible de fermer depuis JS une boite de message, en appelant la méthode générique $.closes().

Événements rattachés au dialogue :
  • open: lorsque la boîte de dialogue s'ouvre.
  • submit: lorsque l'utilisateur saisie la touche "retour-chariot" ou "entrée" en dehors d'un éventuel élément HTML de saisie (input ou textarea).
  • cancel: lorsque l'utilisateur saisie la touche "escape" en dehors d'un éventuel élément HTML de saisie (input ou textarea).
  • resize: lors d'un redimensionnement de la boîte de dialogue.
  • close: lorsque la boîte de dialogue se ferme.
    <html>
    <dialog id="boite−de−dialogue" plume="dialog" name="exemple" class="mon−dialogue" style="min−height: 150px; min−width: 100px">
    <legend>Titre de la boîte</legend>
    <div>
    le contenu de la boite de dialogue,
    redimensionnable et repositionnable.
    <button>Fermer</button>
    </div>
    </dialog>
    <button click="boite_de_dialogue()">Ouverture d'une boite de dialogue</button>
    </html>
    <script>
    function boite_de_dialogue() {
    const dialog = $('#boite−de−dialogue');
    dialog.show();
    dialog.on(
    'cancel submit', () => dialog.close());
    dialog.$(
    'button').on('click', () => dialog.close());
    }
    </script>
Titre de la boîte
le contenu de la boite de dialogue,
redimensionnable et repositionnable.

HTML : PlumeGroup


Plume apporte une gestion d'onglet, via l'objet JSPlumeGroup.

La préparation du contenu des onglets se fait en HTML, en précisant l'attributplume="group" sur un élément <fieldset> HTML contenant les onglets.
Chaque onglet doit contenir un <legend> précisant le titre de son onglet.
<html>
<fieldset plume="group" justify="center">
<section>
<legend style="width: 80px">Un</legend>
contenu
</section>
<section>
</section>
</fieldset>
</html>
Un Premier contenu avec justify=""
deux Deuxième contenu

Le conteneur doit être de préférence un <fieldset> et possède les attributs suivants :
  • plume qui doit obligatoirement être mis à "group".
  • justify: permettant de préciser la position des titres des onglets :
    1. left: centré à gauche.
    2. center: centré au milieu (par défaut).
    3. right: centré à droite
    4. rise: placé au dessus des onglets, cet attribut pouvant être conjugué avec les 3 premiers. Exemple: "center rise".

Par défaut, c'est le premier onglet qui sera affiché.

L'élément HTML contiendra la propriétéplumeGroup, correspondant à l'instance gérant ce groupe d'onglets et proposant les méthodes suivantes :

PlumeGroup - indexed()

Cette méthode renvoie soit le numéro de l'onglet actuellement affiché (1er, 2ème, …) ou 0 si aucun aucun n'est affiché.

Elle a en signature, un seul paramètre :
  • Soit rien (ouundefined) : qui ne fera que renvoyer l'index de l'onglet actuel ou 0 si aucun onglet affiché.
  • Soit un Élement HTML représentant ou inclus dans un onglet
  • Soit une chaîne de caractère représentant le chemin HTML, comme pour un $(), vers un élément HTML.
  • Soit une valeur numérique, afin de vérifier que ce rang d'onglet est valide
    <script>
    const onglets = $('groupe').plumeGroup;
    var index = onglets.indexed(); // renvoie l'index de l'onglet affiché ou 0
    var index = onglets.indexed('mon_onglet'); // renvoie l'index de l'onglet d'id "mon_onglet"
    var index = onglets.indexed($('mon_onglet')); // renvoie l'index de l'onglet d'id "mon_onglet"
    var index = onglets.indexed(5); // renvoie 5 s'il y a au moins 5 onglets, sinon 0
    </script>

PlumeGroup - selected()

Cette méthode permet de sélectionner ou savoir quel onglet est affiché.

Elle a en signature le même paramètre que la méthode PlumeOnglet.indexed() auquel est ajouté ces choix :
  • Soitnull ou la valeur numérique 0 pour n'afficher aucun onglet.
  • Soittrue pour passer à l'onglet suivant.
  • Soitfalse pour passer à l'onglet précédent.

Dans tous les cas, la méthode renvoie l'élément HTML représentant l'onglet affiché ouundefined si aucun onglet n'est affiché.
Si le paramètre passé ne correspond pas à un onglet, aucun changement d'onglet n'est effectué.
Dans ce cas ou dans le cas ou l'onglet choisi est modifié par la méthode PlumeGroup.selecting(), l'onglet affiché et renvoyé sera peut être différent de celui demandé.
<script>
const panels = $('groupe').plumeGroup;
var panel = panels.selected(); // renvoie l'onglet affiché ou undefined</abbr>
var panel = panels.selected('mon_onglet'); // affiche et renvoie l'onglet d'id "mon_onglet", sinon renvoie undefined</abbr>
var panel = panels.selected($('mon_onglet')); // affiche et renvoie l'onglet d'id "mon_onglet", sinon renvoie undefined</abbr>
var panel = panels.selected(5); // affiche et renvoie le 5ème onglet, sinon renvoie undefined</abbr>
var panel = panels.selected(undefined); // n'affiche aucun onglet et renvoie undefined</abbr>
var panel = panels.selected(null); // n'affiche aucun onglet et renvoie undefined</abbr>
var panel = panels.selected(0); // n'affiche aucun onglet et renvoie undefined</abbr>
var panel = panels.selected(false); // affiche et renvoie l'onglet suivant, sinon renvoie le dernier onglet ou undefined</abbr>
var panel = panels.selected(true); // affiche et renvoie l'onglet précédent, sinon renvoie le premier onglet ou undefined</abbr>
</script>

PlumeGroup - selecting

Cette propriété peut être assigné à une méthode utilisateur, qui sera appelée à chaque changement d'onglet, avec en signature :
  1. Évenement déclencheur :
    • Soit sous forme d'unEvent si c'est une demande utilisateur (click, …).
    • Soitfalse lors du premier affichage.
    • Soittrue si c'est une demande provenant d'un programme JS.
  2. Élement HTML de l'onglet demandé.
  3. Élement HTML de l'onglet actuel.
  • Elle peut renvoyer :
    • Une valeur numérique représentant le rang d'un onglet.
    • Ou un Element HTML, pour refuser ce changement et se positionner sur cet onglet
    • Oufalse refusant ce changement d'onglet.
    • Tout autre valeur indiquera l'acceptation du changement d'onglet.

Un refus ne re-déclenche pas cette méthode.
Cette méthode est appelée avant le changement d'affichage d'onglet.
<html>
<fieldset id="groupe" plume="group">
<section>
<legend>Windows</legend>
Windows est au départ une interface graphique unifiée produite par Microsoft, qui est devenue ensuite une gamme de systèmes d’exploitation à part entière, principalement destinés aux ordinateurs compatibles PC…
(extrait de https://fr.wikipedia.org/wiki/Microsoft_Windows)
Il est très facile de passer sur un système concurent tel que GNU/Linux ou MacOS.
<button onclick="$('groupe').selected(2)">➔ GNU/Linux</button>
<button onclick="$('groupe').selected('fieldset section =2')">➔ MacOS</button>
</section>
<section>
<legend>GNU/Linux</legend>
GNU/Linux est un système d’exploitation libre créé en 1983 par Richard Stallman, maintenu par le projet GNU. Il reprend les concepts et le fonctionnement d’UNIX…
(extrait de https://fr.wikipedia.org/wiki/GNU)
De sa relation avec le monde UNIX et notament de BSD UNIX, il est très facile de passer vers un système concurent tel que MacOS, plus difficilement sur Windows.
<button onclick="$('groupe').selected('macos')">➔ MacOS</button>
</section>
<section id='macos'>
<legend>MacOS</legend>
MacOS est un système d’exploitation partiellement propriétaire développé et commercialisé par Apple depuis 1998. Avec iOS, iPadOS, watchOS et tvOS, il fait partie des systèmes d'exploitation d'Apple…
(extrait de https://fr.wikipedia.org/wiki/MacOS)
Il est fondé sur le système d'exploitation NextStep, lui même développé sur l'architecture BSD UNIX.
De part son interface innovent et intuitif, les utilisateurs Windows s'y retrouve, et par ses fondations UNIX, les utiliseurs GNU/Linux l'apprécie.
</section>
</fieldset>
</html>
<script>
$.start(() => {
// prend le contrôle du choix des onglets
$('groupe').plumeGroup.selecting = (event, newTab, oldTab) => {
switch (event) {
case false:
case true:
break;
default: // instanceof Event
const oldIdx = this.indexed(oldTab);
const newIdx = this.indexed(newTab);
const oldTxt = oldTab.$('legend').innerText;
const newTxt = newTab.$('legend').innerText;
console.log("Passage du %dème onglet '%s' au %dème onglet '%s'", oldIdx, oldTxt, newIdx, newTxt);

switch (newTxt) {
case 'GNU/Linux':
return 3; // propose plutôt MacOS
case 'Windows':
return false; // refuse
}
}
};
});
</script>

HTML : ColorPanel


Plume surcharge les balisesinput de type "color" , en donnant la possibilité aux anciens navigateurs d'avoir un popup, tout en ajoutant une uniformalisation entre tous les navigateurs récents, en leurs apportant quelques améliorations.
Elle le fait en exécutant un Color.attachPanel() sur chaque<input type="color">.

Cette balise HTML accepte les attributs suivants :
  • strange: caractère qui sera utilisé pour afficher le sens de la 1ère couleur ou "false" pour indiquer que cette couleur n'est pas utilisable. Par défaut = "T" comme Transparent.
  • strange: le caractère "strange" suivi d'un code de couleur sous forme "#rrggbb" (ou "#rrggbbaa") qui sera renvoyé si cette 1ère couleur est sélectionnée.
    <html>
    <label>Choisir une couleur : <input type="color"><output>(La 1ère étant Transparente)</output></label>
    <label>Choisir une couleur : <input type="color" strange="D"><output>(La 1ère étant la couleur par Défaut)</output></label>
    <label>Choisir une couleur : <input type="color" strange="D#ffffff"><output>(La 1ère étant une couleur blanche)</output></label>
    <label>Choisir une couleur : <input type="color" strange="false"><output>(La 1ère n'étant pas accessible)</output></label>
    </html>

Attention : la valeur de l'élément est une couleur sous forme "#RRGGBB", mais à la différence d'un<input type="color"> standard, elle peut également être :
  • undefined dans le cas où aucune couleur n'avait et n'a été sélectionnée.
  • '#0000' (couleur transparente sous forme #RGBA) dans le cas où la couleur sélectionnée est la 1ère couleur ("strange") et que l'attribut "strange" a un comportement par défaut.
  • '' (chaîne vide) dans le cas où la couleur sélectionnée est la 1ère couleur ("strange") et que l'attribut "strange" a été affecté à un sens.

De plus, il est possible de placer une deuxième couleur #RRGGBBAA derrière la première afin de proposer le choix d'une couleur de fond et de texte dans le même<input>.
Dans ce cas, la propriété et attributvalue est composée des deux valeurs (fond + texte)
et l'<input> change d'affichage avec une pastille clickable en son centre, représentant la couleur du texte.
<html>
<label>Le choix d'une couleur de fond et/ou de texte : <input type="color" value="#000 #fff"></label>
</html>


JS

Plume apporte des fonctionnalités supplémentaires au Javascript et facilite le développement.
La principale est la classe$ qui est un querySelector évolué.
Mais Plume implémente également d'autres méthodes, tel que :
  • create pour créer des éléments.
  • start pour travailler sur la page HTML chargée.
  • on pour gérer les événements.
  • att pour gérer les attributs.
  • css pour gérer les styles.
  • bound pour récupérer les positions et dimensions absolues

JS : trace()

Comme le trace() en PHP, La fonctiontrace() permet d'utiliser la fonction console.log, uniquement en mode développement.
Avec la possibilité de formater la sortie avec un formatage de type String.format() (%s, %5d, %2$x-7.3f, …), les paramètres n'ont utilisé par le format, seront affichés après le formatage.
Cette fonction ne fait rien si aucun paramètre n'est passé. Elle renvoie toujours un booléen indiquant si l'exécution se déroule en mode développement ou non.
Exemples :
<script>
trace("hello");
trace("valeur[", input.value, ']', window); // affiche sur la console : valeur[ une−tabulation 42 une−tabulation ] une−tabulation <Window>
trace("valeur[%d]", input.value, window); // affiche sur la console : valeur[42] une−tabulation <Window>
if (trace())
$.create('div', 'developpement').innerHTML = "Texte affiché qu'en mode développement";
</script>

Il est préférable d'utilisertrace() plutôt queconsole.log(),console.info(),console.warn() ouconsole.error(), pour son inaction en mode non-développement, une recherche rapide des traces dans le source, et de toutes les fonctionnalités suivantes :

JS : trace.substitution


Au méta-caractère de substitution de cette fonction String.format(), la fonctiontrace() ajoute 1 méta-caractère supplémentaire :
  1. %a: permettant d'afficher un tableau ou structure en utilisant la méthode console.table() :

Exemple pour l'affichage de tableaux :
<script>
trace("Exemple de contenu d'un tableau indexé : %a", [ "one", "two", "three" ]);
trace("Exemple de contenu d'un tableau structuré : %a", [ {number: 1, text: "one"}, {number: 2, text: "two"}, {number: 3, text: "three"} ]);
trace("Exemple de contenu d'une structure : %a", {numbers: [1, 2, 3], texts: [ "one", "two", "three" ]} );
</script>
Rendra :
Exemple d'utilisation du méta-caractère "%a"

À noter que le tableau affiché étant sous forme d'un bloc ne pouvant s'intégrer au titre, il sera toujours extrait du titre et afficher sous ce dernier.

JS : trace.surbrillance


Si le premier paramètre est une chaîne de caractère, elle sera surlignée pour la faire ressortir du reste de la trace.
Dans le cas où l'on souhaite arrêter cet effet de surlignement à l'intérieur de ce premier paramètre, il faut faire suivre le titre par un deux-petits-points (:) ou une ouverture de parenthèse :
<script>
trace("titre");
trace("titre", "texte", 42);
trace("titre(%s, %d)", "texte", 42);
trace("titre interne: %s, %d", "texte", 42);
trace("%s: %s, %d", "titre externe", "texte", 42);
trace("titre %s, %d", "texte", 42);
</script>
Rendra :
Exemple de rendu de ces traces

JS : trace.niveau


Il est possible également d'utiliser d'autre niveau de sortie, en précisant un préfixe spécial encadré de chevron (<…>), en début de la 1ère chaîne (donc pas sur les paramètres de substitution).
Les 4 mot-clés/niveaux sont :
Niveau Syntaxe Contracté Initial Numérique
journal <log> <l> <1>
information <information> <info> <i> <2>
alerte <warning> <warn> <w> <3>
erreur <error> <err> <e> <4>

Exemple:
<script>
// correspond au niveau 1 console.log()
trace("log");
trace("<log>log");
trace("<l>log");
trace("<1>log");
// correspond au niveau 2 console.info()
trace("<information>info");
trace("<info>info");
trace("<i>info");
trace("<2>info");
// correspond au niveau 3 console.warn()
trace("<warning>warn");
trace("<warn>warn");
trace("<w>warn");
trace("<3>warn");
// correspond au niveau 4 console.error()
trace("<error>error");
trace("<err>error");
trace("<e>error");
trace("<4>error");
trace("<error>Erreur ok"); // affichera "Erreur ok" en mode error
trace("%sErreur ko", "<error>"); // affichera "<error>Erreur ko" en mode log
</script>
Exemple de rendu de ces traces


JS : trace.groupe


Il est possible de grouper les traces afin de pouvoir les "replier" pour une meilleur visibilité, en précisant les 2 préfixes suivants :
  • <grp> : pour débuter un nouveau niveau de groupe.
  • <end> : pour clôturer un de groupe.

Exemple :
<script>
trace('trace avant les ouvertures');
trace('<grp>Debut du groupe de niveau 1');
trace('trace avant ré−ouverture');
trace('<grp>Debut du groupe de niveau 2');
trace('trace interne au niveau 2');
trace('<end>Fin du groupe de niveau 2');
trace('trace après le niveau 2');
trace('<end>Fin du groupe de niveau 1');
trace('trace après les ouvertures');
</script>
Pourrait donner sur la console, un résultat de type :
trace avant les ouvertures
Debut du groupe de niveau 1 trace avant ré-ouverture
Debut du groupe de niveau 2 trace interne au niveau 2
Fin du groupe de niveau 2
trace après le niveau 2
Fin du groupe de niveau 1
trace après les ouvertures

JS : trace.style


Enfin, il est possible d'insérer des styles et couleurs à une partie de la trace, en plaçant dans ce 1er paramètre, la syntaxe : "⸨[encre][fond][styles]… texte …⸩" :
  • "⸨" et "⸩" sont les caractères qui vont encadrer le texte où s'appliquera la modification.
  • encre est un caractère en minuscule ou une valeur numérique
  • fond est un caractère en majuscule ou une valeur numérique
  • styles est zéro ou plusieurs caractères en minuscule
0 / knoir (blacK) kKkRkLkMkGkYkCkW
1 / rRouge rKrRrLrMrGrYrCrW
2 / gvert (Green) gKgRgLgMgGgYgCgW
3 / yjaune (Yellow) yKyRyLyMyGyYyCyW
4 / lbLeu lKlRlLlMlGlYlClW
5 / mMagenta mKmRmLmMmGmYmCmW
6 / cCyan cKcRcLcMcGcYcCcW
7 / wblanc (White) wKwRwLwMwGwYwCwW
bgras (Bold)
iItalic
usouligné (Underline)
sbarré (Strike)
<script>
trace("Exemples de couleur: ⸨kW:%s⸩, ⸨rC:%s⸩, ⸨gM:%s⸩, ⸨yL:%s⸩, ⸨lY:%s⸩, ⸨mG:%s⸩, ⸨cR:%s⸩, ⸨wK:%s⸩", "kW:noir sur blanc", "rC:rouge sur cyan", "gM:vert sur magenta", "yL:jaune sur bleu", "lY:bleu sur jaune", "mG:magenta sur vert", "cR:cyan sur rouge", "wK:blanc sur noir");
trace("Exemples de styles: ⸨b:%s⸩, ⸨i:%s⸩, ⸨u:%s⸩, ⸨s:%s⸩, ⸨bius:%s⸩", "b:gras", "i:italic", "u:souligné", "s:barré", "bius:tous");
</script>
Voir le résultat dans la Console de votre navigateur.
Note : ne fonctionne pas (encore) sur Safari.

JS : trace.stack

Attention : cette fonction sera supprimée au profit du mécanisme du ### Trace : trace().

Pour le débugging, il est possible de récupérer sous forme d'un tableau et/ou d'afficher sur la console, la pile des appels de méthode jusqu'à cette trace.
Si un paramètre est passé à la méthode$.trace.stack(), elle affichera sur la console la liste des méthodes et dans tous les cas, retournera un tableau des méthodes contenant :
  1. Le nom de la méthode
  2. Le nom du script contenant la méthode
  3. Le numéro de la ligne dans le script

Exemple:
<script>
function un() {
const classe = new Classe();
classe.deux();
};
class Classe {
deux() { trois() }
}
function trois() {
function quatre() { const liste = $.trace.stack('pile') }
quatre();
}
un();
</script>
Dans cet exemple, une trace est affichée sur la console et la constante "liste" contient :
<json>
[
[ 'trois.quatre', 'script.js', 8 ],
[ 'trois', 'script.js', 10 ],
[ 'Classe.deux', 'script.js', 6 ],
[ 'un', 'script.js', 3],
]
</json>

JS : trace.setter

Il est possible de surveiller la modification d'un attribut d'un objet, avec la méthode$.trace.setter() avec les paramètres :
  • L'objet contenant l'attribut à surveiller.
  • Le nom de l'attribut, ou une chaîne vide pour indiquer de surveiller tous les attributs de cet objet.
  • L'information pour la surveillance, qui peut être :
    • Soit un chaîne de caractère qui sera affichée sur la console, avec le nom de l'attribut, la nouvelle valeur et l'ancienne valeur entre parenthèse.
    • Soit une méthode, qui sera appelée avant modification, avec les paramètres suivants : la nouvelle valeur, l'objet, le nom de l'attribut.
Pour fonctionner, cette méthode renvoie l'objet (sous forme d'un proxy), qui doit remplacer l'ancien objet.
<script>
let structure = {a: 1, b: 2};
structure = $.trace.setter(structure, 'b', "Surveillance");
structure.a = 41;
structure.b = 42; // affichera sur la console : Surveillance b 42 (2)

let tableau = [];
tableau = $.trace.setter(tableau, 'length', (valeur, objet, attribut) => trace("Ajout de", objet.length − valeur, "élément(s)") && debugger);
tableau.push(42); // affichera sur la console : Ajout de 1 élément(s)
</script>

JS : $

Cette fonction permet de rechercher un ou plusieurs éléments dans leDocument de la page HTML ou depuis un autre élément parent.
C'est une version évoluée dequerySelector() tel que la simplification de recherche par type ou par nom, par parent ou frère ou par enfant direct, ou par fonction.

Exemples :
<script>
tag = $('TAG'); // recherche un élément TAG dans tout le Document
tag = parent.$('id'); // recherche un élément "id" depuis l'élément "parent"
</script>

Il peut soit rechercher un élément avec la méthode$(), soit plusieurs élément avec la méthode$$() (voir plus bas).
Il peut rechercher depuis la racine du document avec la fonction$() ou$$() ou depuis un élément parent avec la méthodeElement.$() ouElement.$$().
<script>
tag = $('SCRIPT'); // recherche une balide <script> dans tout le Document, donc dans le <head>, le <body>, après le <body>, …
tag = $('BODY').$('SCRIPT'); // recherche une balide <script> uniquement dans le <body>
tag = $('BODY SCRIPT'); // idem
tag = document.body.$('SCRIPT'); // idem
</script>

La signature de la fonction$() est :
  1. Un premier paramètre indiquant quoi rechercher :
    • Une chaîne de caractère représentant le chemin pour accéder à ou aux éléments (voir à partir du chapitre suivant).
    • Un élément HTML : aucune action et cet élément est renvoyé.
    • Une fonction : qui sera appelé sur chaque élément et indiquera en retour s'il faut le renvoyer (voir plus bas)
  2. Un deuxième paramètre optionnel permettant des options sur le résultat de la recherche :
    • Un numérique positif : indiquant qu'il doit renvoyer le xème premier élément trouvé.
    • Un numérique négatif : indiquant qu'il doit renvoyer le xème dernier élément trouvé.
  • En retour la fonction renvoie soit l'élément trouvé, soitundefined la recherche n'abouti pas.
Pour les particularités de la signature de la fonction$$() voir plus bas.

Exemple :
<html>
<ul>
<li>un</li>
<li>deux</li>
<li>trois</li>
</ul>
</html>
<script>
tag = $('LI', 2); // renverra <li>deux</li>
</script>

Les chapitres suivants décrivent le premier paramètre de recherche.

$ : id

Recherche une balise HTML par son ID, qui doit être constitué uniquement de lettre, chiffre et des caractères "-" et "_".
Attention : cette syntaxe est la seule à faire une distinction entre les majuscules et minuscules :
  • Si tout est en majuscule : la recherche ne se fera pas sur son ID mais sur le nom de sa balise, comme pour la syntaxe par dièse.
  • Dans tous les autres cas : c'est une recherche du ID.
Cette exception "tout majuscule" uniquement présente dans la syntaxe$() est une facilité pour le développeur.
<script>
tag = $('div'); // renverra <… id="div">
tag = $('DIv'); // renverra <… id="DIv">
tag = $('DIV); // attention : dû aux majuscules, ne renverra pas <… id="DIV"> mais plutôt le 1er DIV trouvé dans Document
</script>
Attention également à la syntaxeélément.$('DIV') (vu plus bas) qui recherche normalement sans l'exception du "tout majuscule" de la syntaxe précédente :
<script>
tag = bar.$('div'); // attention : dû à une recherche depuis un élément, ne renverra pas <… id="div"> mais plutôt le 1er DIV enfant de bar
</script>

$ : #ID

Si vous souhaitez rechercher un ID qui est tout en majuscule, ou par méthodologie de travail, il faut précédé l'ID d'un dièse :
<script>
tag = $('#div'); // renverra <… id="div">
tag = $('#DIv'); // renverra <… id="DIv">
tag = $('#DIV'); // renverra <… id="DIV">
tag = $('# div'); // renverra une erreur de syntaxe
tag = bar.$('#div'); // sera identique à $('#div') puisqu'un ID est (normalement) unique
</script>

$ : Nom de l'élément

Par son tagName (qui doit être tout en majuscule si recherche dans tout le Document) :
<script>
tag = $('DIV'); // renverra le 1er <div> trouvé dans le Document
tag = bar.$('div'); // renverra le 1er <div> enfant de bar
tag = $('DIv'); // attention : dû à une minuscule, ne renverra pas le 1er <DIV> dans le Document, mais plutôt un id égale à "DIv" <… id="DIv">
Attention : il est recommandé de mettre en minuscule le nom des balises, certaines versions de navigateurs (tel que FireFox 86.0 ou Safari 14.0), ne trouvent pas certaines balises tel que div.$('SVG'), mais n'auront aucune problème avec div.$('svg').

$ : Nom de la classe

Par son attribut de classe :
<script>
tag = $('.foo'); // renverra le 1er élément contenant la classe "foo" <… class="… foo …">
tag = bar.$('.foo'); // renverra le 1er <… class="… foo …"> enfant de bar
</script>

$ : Type

Par son attribut type :
<script>
tag = $('?foo'); // renverra le 1er <… type="foo">
tag = bar.$('?foo'); // renverra le 1er <… type="foo"> enfant de bar
</script>

$ : Nom

Par son attribut name, avec des variantes (^$*) possibles :
<script>
tag = $('@foo'); // renverra le 1er élément dont le name égale à "foo" <… name="foo">
tag = bar.$('@foo'); // renverra le 1er <… name="foo"> enfant de bar
tag = $('@^foo'); // renverra le 1er élément dont le name commence par "foo", exemple: <… name="foobar">
tag = $('@$foo'); // renverra le 1er élément dont le name fini par "foo", exemple: <… name="barfoo">
tag = $('@*foo'); // renverra le 1er élément dont le name fini par "foo", exemple: <… name="barfoobar">

$ : Attribut

  1. [attribut]: sur un attribut précis (avec les variantes traditionnelles) :

Exemple:
<script>
tag = $('[class]'); // recherche tous les éléments dans le Document ayant un attribut 'class&apos;
tag = $('[class=valeur]) // recherche tous les éléments dans le Document ayant un attribut 'class' à 'valeur'
tag = $(
'[class~=valeur]) // recherche tous les éléments dans le Document ayant un attribut 'class&apos; possèdant le mot 'valeur' (séparé par des espaces)
tag = $('[class^=valeur]) // recherche tous les éléments dans le Document ayant un attribut 'class' commençant par 'valeur'
tag = $(
'[class$=valeur]) // recherche tous les éléments dans le Document ayant un attribut 'class&apos; finissant par 'valeur'
tag = $('[class*=valeur]) // recherche tous les éléments dans le Document ayant un attribut 'class' contenant 'valeur'
tag = $(
'[class~=valeur]) // recherche tous les éléments dans le Document ayant un attribut 'class&apos; possèdant le mot 'valeur'
tag = $('[class=valeur i]) // recherche insensible à la case des caractères
</script>

$ : Parent

En remontant à son parent :
<script>
tag = tag.$('^'); // renverra le parent de tag
tag = tag.$('^5'); // renverra le 5ème parent de tag
tag = tag.$('^div'); // renverra le 1er parent DIV de tag
tag = tag.$('^=div'); // idem mais en incluant en 1ère recherche, le tag lui−même
tag = tag.$('^=main.foo@bar'); // idem mais en cherchant un parent de genre <MAIN class="abc foo def" name="bar">
tag = tag.$('^ div'); // attention: dû à l'espace, la recherche est tag.$('^').$('DIV')
</script>
Attention à ne pas mettre d'espace entre le caractère "^" et l'information à rechercher, sinon le comportement sera de remonter d'un parent, puis de rechercher dans ce parent l'information indiqué :
<html>
<div>
<span>
enfant SPAN à DIV
</span>
<article>
<section id="enfant">

</section>
<span>
enfant SPAN à ARTICLE
</span>
</article>
</div>
</html>
<script>
const enfant1 = $("#enfant ^DIV SPAN"); // renverra : enfant SPAN à DIV
const enfant2 = $("#enfant ^ DIV SPAN"); // renverra : enfant SPAN à ARTICLE
</script>

Il est possible d'aller plus loin dans la recherche d'un ou plusieurs parents avec la méthode Element.closest() et Element.closests().

$ : Enfant

En cherchant un élément directement fils (quequerySelector ne permet pas) :
<script>
tag = tag.$('> div'); // renverra le 1er DIV enfant direct de tag
tag = tag.$('> div > span'); // renverra le 1er SPAN enfant direct du 1er DIV enfant direct de tag
</script>

$ : Frère

En cherchant l'élément de même niveau situé après lui :
<script>
tag = tag.$('+'); // renverra le 1er élément à la suite et au même niveau que tag
tag = tag.$('+5'); // renverra le 5ème élément à la suite et au même niveau que tag
tag = tag.$('+div'); // renverra le 1er DIV à la suite et au même niveau que tag
tag = tag.$('+div.foo?bar'); // idem mais recherche un élément de genre <DIV class="abc foo def" type="bar">
tag = tag.$('+ div'); // attention, dû à l'espace, la recherche est tag.$('+').('DIV')
</script>
Ou en cherchant l'élément de même niveau situé avant lui :
<script>
tag = tag.$(''); // renverra le 1er élément en amont et au même niveau que tag
tag = tag.$('−5'); // renverra le 5ème élément en amont et au même niveau que tag
tag = tag.$('−div'); // renverra le 1er DIV en amont et au même niveau que tag
tag = tag.$('−div.foo@bar'); // idem mais recherche un élément de genre <DIV class="abc foo def" name="bar">
tag = tag.$('− div'); // attention, dû à l'espace, la recherche est tag.$('−').('DIV')
</script>

$ : fonction

Il est possible de pousser le filtrage des éléments HTML en passant une fonction en paramètre, qui aura la même fonction que la méthode Array.filter().
<script>
const tag = $('FORM').$(tag => tag.offsetHeight > 30);
</script>
Attention à ne pas confondre ce premier paramètre de typeFunction avec le deuxième paramètre qui peut être également de typeFunction mais pour une autre utilité (voir plus bas)

$ : $$

Il est possible de ne pas rechercher qu'un seul élément, mais un ensemble d'élément, en utilisant la fonction$$() :
<script>
elements = tag.$$('div'); // renverra un tableau de tous les DIV contenu dans tag
elements = $$('INPUT'); // renverra tous les INPUT contenu dans le Document
elements = $$('DNADA'); // renverra un tableau vide, puisque le W3C n'a pas encore accepté la balise HTML <dnada> :)
</script>

Elle reprend la même signature que pour la fonction $(), mais avec quelques fonctionnalités supplémentaires :
  1. Un premier paramètre indiquant quoi rechercher :
    • Une chaîne de caractère représentant le chemin pour accéder aux éléments (voir en amont à partir du chapitre id).
    • Un élément HTML : aucune action et cet élément est renvoyé sous forme d'un tableau.
    • Une fonction : qui sera appelé sur chaque élément et indiquera en retour s'il faut le renvoyer (voir plus bas)
  2. Un deuxième paramètre optionnel permettant des options sur le résultat de la recherche :
    • Un numérique positif : indiquant qu'il doit renvoyer les x premières éléments trouvés.
    • Un numérique négatif : indiquant qu'il doit renvoyer les x derniers éléments trouvés.
    • La constante :$.APPOINT qui indique qu'il doit renvoyer en plus du tableau indexé, une structure avec en clé, l'attribut name des éléments trouvés.
    • Une chaîne de caractère : indiquant qu'il ne doit pas renvoyer un tableau indexé, mais une structure dont les clés correspondent à la valeur de l'attribut, indiqués par ce paramètre, des éléments HTML trouvé.
    • Une méthode permettant de générer des clés pour les éléments et apportant une notion de filtrage, avec en signature :
      1. L'élément
      2. L'index : partant de 0
      3. Le tableau des éléments trouvés
      4. La structure des éléments renvoyés
      • Et en retour la clé qui sera ajouté à ce tableau ou la valeurfalse ouundefined ounull (ou tout ce qui ne serait pas une valeur numérique ou chaîne de caractère) si l'élément ne doit pas être retenu. Sitrue est renvoyé, la clé ne sera pas modifiée.
  • En retour la fonction renvoie soit un tableau (et/ou structure) des éléments trouvés, soit un tableau (ou structure) vide la recherche n'abouti pas.

Dans le cas de doublon sur des clés (retournées par l'attribut, l'APPOINT ou le générateur de clé), leurs élément HTML seront mémorisés dans un tableau indexé.
<html>
<ul>
<li name="one" ref="un">1</li>
<li name="two" ref="deux">2</li>
<li name="three" ref="trois">3</li>
<li name="three" ref="quatre">4</li>
<li name="five" ref="quatre">5</li>
<li name="six" ref="six">6</li>
<li name="seven" ref="sept">7</li>
</ul>
</html>
<script>
trace('ALL', $$('LI') );
trace('POSITIF', $$('LI', 4) );
trace('NEGATIF', $$('LI', −4) );
trace('APPOINT', $$('LI', $.APPOINT) );
trace('ATTRIBUT', $$('LI', 'ref') );
trace('GENERATOR', $$('LI', function(tag, idx, tags, struct) {
const name = tag.getAttribute('name');
if (idx < 5 && ! tags[name]) // uniquement les 5 premiers unifiés
return name;
}) );
</script>
Renverra :
ALL: [
0: <li name="one" ref="un">,
1: <li name="two" ref="deux">,
2: <li name="three" ref="trois">,
3: <li name="three" ref="quatre">,
4: <li name="five" ref="quatre">,
5: <li name="six" ref="six">,
6: <li name="seven" ref="sept">
]
POSITIF: [
0: <li name="one" ref="un">,
1: <li name="two" ref="deux">,
2: <li name="three" ref="trois">,
3: <li name="three" ref="quatre">,
]
NEGATIF: [
0: <li name="three" ref="quatre">,
1: <li name="five" ref="quatre">,
2: <li name="six" ref="six">,
3: <li name="seven" ref="sept">
]
APPOINT: {
one: <li name="one" ref="un">,
two: <li name="two" ref="deux">,
three: [
0: <li name="three" ref="trois">,
1: <li name="three" ref="quatre">
],
five: <li name="five" ref="quatre">,
six: <li name="six" ref="six">,
seven: <li name="seven" ref="sept">,
}
ATTRIBUT: {
un: <li name="one" ref="un">,
deux: <li name="two" ref="deux">,
trois: <li name="three" ref="trois">,
quatre: [
0: <li name="three" ref="quatre">,
1: <li name="five" ref="quatre">,
]
six: <li name="six" ref="six">,
sept: <li name="seven" ref="sept">,
}
GENERATOR:
{
one: <li name="one" ref="un">,
two: <li name="two" ref="deux">,
three: <li name="three" ref="trois">,
five: <li name="five" ref="quatre">,
}

Attention à ne pas confondre ce deuxième paramètre de typeFunction avec le premier paramètre qui peut être également de typeFunction mais pour une autre utilité (voir plus haut).

$ : tableau

Et inversement, la recherche peut s'effectuer sur un tableau indexé :
<script>
$element1 = [element1, element2].$('abc');
$elements = $$('TABLE').$$('CAPTION');
</script>

$ : groupe

Il est possible de faire une recherche groupée :
<html>
<main>
<dl>
<dt id="10">Titre 1</dt>
<dd>
<ul>
<li id="11">Titre 1.1</li>
<li id="12">Titre 1.2</li>
</ul>
</dd>
<dt id="20">Titre 2</dt>
<dd>
<ul>
<li id="21">Titre 2.1</li>
<li id="22">Titre 2.2</li>
</ul>
</dd>
</dl>
</main>
</html>
<script>
elemnts = $('MAIN').$$('LI|DT'); // renverra tous les LI puis les DT : <li id="11">, <li id="12">, <li id="21">, <li id="22">, <dt id="10">, <dt id="20">
elemnts = $('MAIN').$$('LI,DT'); // renverra dans l'ordre trouvé : <dt id="10">, <li id="11">, <li id="12">, <dt id="20">, <li id="21">, <li id="22">
</script>

$ : chaînage

Enfin, on peut chaîner les recherches dans la chaîne de recherche :
<script>
$$('#marqueur ^4 + DIV INPUT?text.valide@personnel[nom][]')
</script>
ou chaîner par appel successif :
<script>
$('marqueur').$('^4').$('+').$$('DIV').$$('INPUT?text.valide@personnel[nom][]')
</script>
Les deux cas renvoyant un tableau identique de 3 éléments HTML, d'id = « un », « trois » et « sept », pour l'HTML suivant :
<body>
<article>
<section>
<div>
<fieldset>
<ul>
<li id="marqueur">
</li>
</ul>
</fieldset>
</div>
</section>
<section>
<div>
<fieldset>
<input class="valide" name="personnel[nom][]" id="un">
<input class="invalide" name="personnel[nom][]" id="deux">
<input class="important valide" name="personnel[nom][]" id="trois">
<input class="valide" name="personnel[prenom][]" id="quatre">
</fieldset>
</div>
<input type="text" class="valide" name="personnel[nom][]" id="cinq">
<div>
<fieldset>
<input class="invalide" name="personnel[nom][]" id="six">
<input class="valide" name="personnel[nom][]" id="sept">
<input class="valide" name="personnel[prenom][]" id="huit">
</fieldset>
</div>
</section>
</article>
</body>

JS : $.start()

Plume lance les fonctions passées dans$.start(); lorsque la page est chargée et prête.
<script>
// Exemple d'utilisation
function demarrage() {
// les instructions qui seront executées uniquement lorsque la page sera prête.
}
$.start(demarrage);

// ou plus simplement en employant une fonction anonyme intégrée
$.start(function() {
// les instructions qui seront executées uniquement lorsque la page sera prête.
});
</script>

Il est également possible de bloquer les starts, en attente de données qui pourraient venir après le load de la page, tel que les [requêtes asynchrones ajax]((## JS : RPC), les promesses, les base de données, …
Le principe est de préciser en premier paramètre le ou les jetons qui sont indispensables à la bonne exécution du contenu de la fonction; puis lorsque ces jetons sont opérationnels, de l'indiqueer en appelant$.start avec uniquement le jeton concerné.
(Cet exemple utilise des fonctions asynchrone RPC expliquées plus bas)
<script>
// exécute une fonction en asynchrone, suite à un retour du serveur PHP
ajax.differe_ready().then(function() {
// traitement
$.start("ready"); // déverouille ce jeton permettant de débloquer les starts ayant besoin de ce traitement
});
// fonction qui ne sera lancée que lorsque la page sera prête ET que le jeton "ready" sera déverouillé,
// donc après le retour de l'ajax "differe_ready"
$.start("ready", function() {
});
// fonction qui ne sera lancée que lorsque la page sera prête ET que les 2 jetons "ready" et "impossible" seront déverouillés,
// donc jamais, puisque le jeton "impossible" n'est pas déverouillé dans cet exemple.
$.start("ready impossible", function() {
});
// alors que cette fonction sera appelée dès que la page sera prête, sans tenir compte d'aucun jeton
$.start(function() {
});
</script>
Les jetons sont de type chaîne de caractères, mais l'on peut également rattacher une valeur à ces jetons, qui sera récupérée dans le start :
<script>
// demande d'information supplémentaire au serveur
ajax.reading().then(function(result) {
if (result.ressource)
$.start({ressource: result.ressource});
});
$.start(function() {
// attend que l'utilisateur clic sur son bouton
$.$('button#goUser').on('click', function() {
$.start({ready: true});
});
});
// lorsque ces deux évenements seront réunis, affiche le résultat dans la console
$.start("ready ressource", function(event, jetons) {
console.log(jetons.ready, jetons.ressource);
});
</script>

Enfin, Les starts sont executés normalement dans l'ordre où ils ont été appelé, mais l'on peut forcer les starts pour qu'ils soient prioriser par rapport aux autres starts.
Dans ce cas, au lieu de spécifier un jeton sous forme de chaîne, il faut passer le booléantrue en premier paramètre :
<script>
$.start(function() {
trace("Appel qui se fera après, parce que de priorité normale ");

// utilisation
trace('option', window.options);
});

$.start( true, function() {
trace("Appel qui se fera avant, parce que de priorité haute ");

// initialisation
window.options = 42;
});
</script>

JS : $.for()

$.for() permet d'appliquer une boucle intelligente et protégée.
Sa signature est :
  1. Objet : sur lequel s'appliquera la boucle, qui peut être un objet, une structure, ou tableau indexé ou similaire (Element, …), ou une valeur numérique.
  2. Fonction : qui sera appelée, avec les paramètres classiques :
    1. La valeur
    2. La clé ou index
    3. L'objet
  3. This : optionnel qui sera affecté au this de la fonction appelée. L'object sera passé si ce This est omis.
  4. Filtre : optionnel qui permet de ne pas retenir les éléments correspondant à ce paramètre (qui peut être explicitementundefined). Si omis, aucun filtre n'est appliqué.
Cette méthode renvoie les éléments renvoyés et éventuellement filtré.
La fonction peut être omis dans le cas d'un besoin de filtrage uniquement ou pour un objet numérique (voir exemples) :
<script>
$.for([], function(valeur, index) {}, this); // itère comme un [].forEach, avec passage du this optionnel
$.for({}, function(valeur, index) {}); // itère sur les attributs d'un objet (pas les fonctions)
$.for(NodeListe, function(valeur, index) {}); // itère même si non itérable
$.for(7, (valeur, index) => `${index}:${valeur}`); // itère 7 fois et renvoie ["0:1", "1:2", "2:3", "3:4", "4:5", "5:6", "6:7"]
const iterations = $.for(7); // renvoie [0, 1, 2, 3, 4, 5, 6]
const boxes = $.for({a:undefined, b:null, c:false, d:0, e:''}, false, null, undefined); // renvoie {b:null, c:false, d:0, e:''}
</script>

JS : $.create()

Cette méthode permet de créer un nouvel élement, avec en signature :
  1. Le type d'élément:
  2. Les informations complémentaires, tel que la classe, les attributs, …
    <script>
    const tag = $.create(); // dans le document (donc pas visible, à vous de faire un appendChild insertBefore)
    const tag = document.body.create(); // dans le <body>
    const tag = $('DIV').create(); // dans un DIV existant
    </script>
On lui indique le type d'élément à créer, en premier paramètre, sous forme :
  • Le nom d'une balise HTML, tel queDIV,INPUT.
  • Une chaîne de caractère représentant une balise, encadré de chevron.
  • Du texte, qui sera ajouté (à la différence d'uninnerHTML qui vient remplacer).
  • Un élément HTML (balise, template, …) existant qui sera déplacé.
  • Un ensemble d'éléments HTML (children, …) qui seront créés à l'intérieur de l'élément.
Pour ce dernier cas, l'élément renvoyé reste l'élément HTML de cette méthode et les éventuels attributs (2ème paramètre) sont appliqués sur ce dernier.
<script>
$.create('TAGNAME); // son tagName, exemple "DIV" (non sensible à la case)
$.create("<tag>"); // à partir de balisage HTML
$.create("texte simple"); // créer simplement du texte
$.create(element); // clôner un élément existant, exemple $("mon_id")
$.create($(TEMPLATE)); // crée un nouvel ensemble d'élément HTML en copiant le <template> dans <element>
$.create("DIV").create("SPAN").innerText = "texte"; // permet d'ajouter un <div><span>texte</span></div> à <element>
$.create(element.children); // ajout l'ensemble des enfants d'élément
On peut lui indiquer des informations complémentaires en deuxième paramètre :
  • le nom d'une classe CSS
  • lename (précédé d'un @)
  • l'id (précédé d'un #)
  • le type (précédé d'un ?)
  • des attributs lors de sa création (encadré d'accolade)

Exemple:
<script>
$.create(tag, {id: "xxx", style: {backgroundColor: 'yellow'}});
$.create(tag, "ma_classe"); // équivalent à $.create(tag, {class: "ma_classe"});
$.create(tag, "#mon−id"); // équivalent à $.create(tag, {id: "mon−id"});
$.create(tag, "#mon−id @mon−nom?mon−type"); // équivalent à $.create(tag, {id: "mon−id", name: "mon−nom", type: "mon−type"});
</script>
Puisque cette méthode utilise Element.att() pour gérer les attributs, on peut utiliser les "inner*", directement sous forme d'attribut, afin d'enchaîne avec le this :
<script>
const mon_element = $.create('DIV', {innerText: "Ceci est un exemple de texte"});
// est identique à
const mon_element = $.create('DIV');
mon_element.innerText = "Ceci est un exemple de texte";
</script>

Exemple simple pour télécharger un fichier en "tâche de fond" :
<html>
<div>
<!−−− créé une action déclencheur −−−>
<button>Télécharger</button>
<!−−− avec d'eventuel paramètre −−−>
<input name="param" type="checkbox" value="yes"/>
</div>
</html>
<script>
// après chargement
$.start(() => {
// si click
$('BUTTON').on('click', event => {
// récupère l'eventuel paramètre
const param = $('input@param').checked ? 'yes' : 'no';
// créé un lien virtuel non−rattaché à la page et clic dessus pour appeler le serveur
$.create('a', {href: location.href + '?download=' + param}).click();
});
});
</script>
<?php
// si appel
$download = $_REQUEST['download'] ?? false;
if ($download) {
// prépare le retour d'un fichier téléchargeable
header('Content−Type: text/txt');
header('Content−Disposition: attachment; filename="fichier.txt');
// de contenu
echo "Hello word ";
exit;
}
?>

Pour créer des éléments à l'interieur ou par rapport à un autre élément, il faut utiliser la méthode Element.create().

JS : $.css()

Cette méthode sert à gérer (ajouter, modifier et effacer) des propriétés sur les feuilles de style.
À ne pas confondre avec la méthode homographe Element.css() qui elle ne sert qu'à récupérer des styles calculés sur un élément HTML.

Les modifications s'effectue uniquement sur une nouvelle feuille de style dynamique, elle ne modifie pas les feuilles de style communiquées par le serveur.

Sa signature est :
  1. Le sélecteur, avec comme syntaxe :
    • Soit une chaîne de caractère représentant un sélecteur.
    • Soit un ensemble de sélecteur séparé par des virgules
    • Soit un tableau de chaîne de caractère de sélecteurs.
  2. La valeur, avec comme syntaxe :
    • Soitnull indiquant la suppression de ce sélecteur
    • Soit une chaîne de caractère remplaçant entièrement le sélecteur.
    • Soit une structure ajoutant (ou supprimant si la valeur est égale ànull) un ensemble de propriété valeur à un sélecteur existant ou non.

Exemple :
<style>
div > input[type=text] {
background: gray ! important;
font−size: 14px;
}
</style>
<html>
<div>
<input type="text">
</div>
</html>
<script>
// pour l'ensemble des balises INPUT de type texte…
// remplace tous les styles éventuellements associés, par ces 2 propriétés
$.css('div > input[type=text]', "background: blue ! important; border−left: 5px");
/*
font−size: 14px; // conserve
background: blue ! important; // surcharge
border−left: 5px; // ajoute
*/

// puis modifie la propriété background, ajout la couleur, et supprime la bordure de gauche
$.css('div > input[type=text]', {
color: "yellow",
borderLeft: null,
});
/*
font−size: 14px;
background: blue ! important;
color: yellow; // modifie et supprime le border−left
*/

// remplace juste la hauteur
$.css('div > input[type=text]', "height: 18px");
/*
background: gray ! important; // retrouve
font−size: 14px; // retrouve
height: 18px; // ajoute
*/

// enfin supprime les modifications
$.css('div > input[type=text]', null);
/*
background: gray ! important; // retrouve
font−size: 14px; // retrouve
*/
</script>

JS : $.clone()

Permet de clôner l'intégralité de l'objet passé en paramètre.
Il va sur chaque propriété de l'objet, essayer d'appeler une méthodevalue.clone() si elle existe, sinon, clône en dupliquant la nouvelle valeur.
L'objetArray implémente la méthode Array.clone().
<script>
const origin = {a: 1};
const alias = origin;
const clone = $.clone(origin);
alias.a = 2;
origin.b = 3;
clone.c = 4;
// rendra
origin = {a: 2, b: 3};
alias = {a: 2, b: 3};
clone = {a: 1, c: 4};
</script>

Pour implémenter la méthode dans d'autre objet, il suffit de faire :
<script>
UnObjet.prototype.clone = function() {
return { le contenu de mon objet qui est important de clôner };
}
</script>
Ou
<script>
class MonObjet {
clone() {
return {};
}
}
</script>

JS : $.isEmpty()

Renvoietrue si l'objet passé en paramètre est vide.
Comme pour $.clone(), son mécanisme appel la méthodeobjet.isEmpty() si elle existe, sinon cherchera à regarder s'il y a au moins une valeur (hors méthode inclus dans son prototype).
<script>
$.isEmpty({}); // renverra true</abbr>
$.isEmpty({a: 1}); // renverra false</abbr>
$.isEmpty([]); // renverra true</abbr>
$.isEmpty([42]); // renverra false</abbr>
</script>
L'objetArray implémente déjà cette méthode Array.isEmpty() :

JS : $.purify()

Cette méthode renvoie une copie de l'objet passé en paramètre épuré des valeurs àundefined.
Elle a en signature :
  1. Objet : original
  2. Filtrage: définissant quel mécanisme de filtre appliquer :
    • undefined ou absent : épure l'objet que sur ses attributs àundefined.
    • true: épure l'objet que sur ses attributs de valeur fausse (undefined,null,false, 0 ou "").
    • Sinon = épure l'objet que sur ses attributs àundefined ou les attributs identiques au paramètre passé (par comparaison par égalité stricte).
  • Renvoie un nouvel objet épuré.

Comme pour $.clone(), son mécanisme appel la méthodevaleur.purify() sur l'objet, si elle existe, sinon cherchera à filtrer l'élément.
L'objetArray implémente déjà sa méthode Array.purify().
<script>
const objet = {a: 7, b: undefined, c: null, d: false, e: 0, f: '', g: 'end'};
objet.purify(); // renverra {a: 7, c: null, d: false, e: 0, f: '', g: 'end'}
objet.purify(true); // renverra {a: 7, g: 'end'}
objet.purify(0); // renverra {a: 7, c: null, d: false, f: '', g: 'end'}
objet.purify(7); // renverra { c: null, d: false, e: 0, f: '', g: 'end'}
objet.purify('7'); // renverra {a: 7, c: null, d: false, e: 0, f: '', g: 'end'}
</script>

JS : $.closes()

Cette méthode permet de fermer l'éventuel popup (menu, calendrier, message, …) qui serait encore ouvert.
Normalement, les popup se ferment toutes seules, lors de la perte du focus sur l'élement rattaché : un menu se ferme losque l'on clique à côté, ….; mais il arrive dans certain cas, que l'on veuille anticiper la fermeture du popup resté ouvert.
<script>
$.closes();
</script>

JS : $.occurrence

Cette variable correspond au dernier évenement traité par un un Element.on()

JS : $.waiting()

Cette méthode permet d'afficher une animation d'attente, si vous avez besoin de faire patienter l'utilisateur.
Elle est, par exemple, utilisée dans les options d'ajax.

Pour l'activer, sa signature est soit :
  • Un booléan àtrue : activant une animation par défaut.
  • Une chaîne de caractère : contenant l'URL vers une image animée.
  • Un élément HTML : qui contiendra tout le nécessaire pour afficher l'animation.
  • Une structure contenant des attributs pour une balise <IMG>, qui viendront remplacer l'animation par défaut, pouvant comprendre :
    1. src: l'URL vers l'image animée.
    2. style: des styles de centrage, …
    3. background: qui sera utilisé à la place du fond opaque par défaut.
    4. tagName: pour créer éventuellement une autre balise qu'une balise IMG.
Pour l'arrêter, aucun paramètre doit être passé.
<script>
// exemple simple
$.waiting(true);

// exemple en personnalisant l'image
$.waiting("/lib/image/waiting.gif");

// exemple en personnalisant l'animation
$.waiting($.create('img', {src: "/lib/image/waiting.gif", style: {margin: '25% 50%', width: '120px'}}));

// exemple en personnalisant un peu plus l'image
$.waiting({src: "mon−image.png", style: {margin: '25% 50%', width: '150px'}, background: "#8883", tagName: 'img'});

// permet de fermer cette attente
$.waiting();
</script>

JS : Point

Cet objet représente un point, avec les attributs{x, y}

Point : Création

La création d'un point se fait par new Point(), en lui passant le x,y ou un élément DOM (event ou tag).
<script>
let point = new Point(); // point de coordonnée (0,0)
let point = new Point(x, y); // point en utilisant 2 valeurs numériques
let point = new Point([x, y]); // accepte un tableau de 2 valeurs numériques
let point = new Point({x: 1, y: 2}); // accepte une simple structure
let point = new Point(new Rect(1, 2, 3, 4)); // accepte le topLeft d'un Rect (voir plus bas)
let point = new Point({topLeft: {x: 1, y: 2}}); // accepte une structure topLeft
let point = new Point({left: 1, top: 2}); // accepte une simple structure
let point = new Point(event); // point à partir du clientX et clientY
let point = new Point(element); // point à partir du offsetLeft et offsetTop
let point = new Point(point); // duplique
let point = Point(x, y); // sucre du new Point()
</script>
Note : Le fait de passer un élément comme initiateur du constructeur, renvoie un point "relatif" au parent de cet élément, et non un point absolue sur l'écran. Si c'est ce dernier qui est recherché, il vaut mieux utiliser bound.
Note : la construction d'un point autorise un 3ème paramètre atypique, de type méthode, qui sera appelé à chaque fois qu'il y aura modification sur lex ouy du point. Mécanisme notament utilisé dans le Rect.size.
Sa signature est :
  1. x : la nouvelle valeur de x ouundefined si la modification porte sur y
  2. y : la nouvelle valeur de y ouundefined si la modification porte sur x
  3. old : l'ancienne valeur avant modification
  • Ne prend pas en compte de valeur de retour, la modification se fera toujours après l'appel à cette méthode-setter.

Exemple:
<script>
const p = new Point(0, 0,
function(x, y, old) {
if (x !== undefined)
trace('modification de X passant de %d à %d', old, x);
else<br> trace('modification de Y passant de %d à %d', old, y);
}
);
</script>

Point : Utilisation

Il est possible de récupérer et modifier les attributs x et y directement.
<script>
point.x = 0;
point.y = 6.56;
</script>

Il est également possible d'utiliser le point pour positionner ou dimensionner un élément HTML :
<script>
let x = point.x;
point.y = y;
point.setPos(element_html); // positionne l'élément HTML par rapport au point
element_html.setPos(point); // positionne le top et left de l'élément
element_html.setSize(point); // positionne le height et width de l'élément
element_html.setSize(tag); // positionne le height et width de l'élément par rapport au dimension de tag
// exemple en positionnant un canvas
$('CANVAS').setPos(point);
</script>
Point.setPos() est pratiquement identique àElement.setPos(), la différence est que cette dernière accepte plus de types de paramètre qu'un simpe Point, voir Element.setPos().

Point : Modification

Cet objet peut être modifié directement en affectant une valeur numérique à x ou y, ou par un ensemble de méthodes.
Ces méthodes sont doublées :
  1. Tout en minuscule : qui applique le résultat sur le this, avant de le renvoyer.
  2. Commence par une lettre Majuscule : même mécanisme, mais en clônant le point avant, afin de ne pas modifier lePoint d'origine.
Les paramètres acceptés sont :

Point Modification : add()

Cette méthode permet de "déplacer" le point, en appliquant une addition (this + paramètre).
<script>
point.add(point1).add(x, y); // ajout point1 à point et le renvoie.
let p = point.Add(point1, …); // Même principe que add() mais crée d'abord un nouveau Point afin de ne pas toucher au "point" d'origine
point.add(new Rect(0, 1, 2, 3)); // ajout le topLeft de Rect (soit {x:0, y:1}) à point et le renvoie.
</script>

Point Modification : sub()

Cette méthode applique une soustraction (this - paramètre).
<script>
point.sub(point1).sub(x, y); // renvoie un nouveau point, enlevé de la liste de points passés en paramètre
let p = point.Sub(point1, …).back(x, y); // même principe que sub() mais crée d'abord un nouveau Point afin de ne pas toucher au "point" d'origine
point.sub(new Rect(0, 1, 2, 3)); // enlève le topLeft de Rect (soit {x:0, y:1}) à point et le renvoie.
</script>

Point Modification : cut()

Cette méthode applique une soustraction inverse (paramètre - this).
<script>
point.cut(point).cut(x, y); // renvoie un nouveau point, correspondant au point du paramètre − point (this)
let p = point.Cut(point); // Même principe que cut() mais crée d'abord un nouveau Point afin de ne pas toucher au "point" d'origine
point.cut(new Rect(0, 1, 2, 3)); // enlève le point à topLeft de Rect (soit {x:0, y:1}) à point et le renvoie.
</script>

Point Modification : diff()

Cette méthode applique une soustraction absolue abs(paramètre - this) permettant de renvoyer une distance toujours positive entre deux points.
<script>
point.diff(point).diff(x, y); // renvoie une distance, correspondant au point du paramètre − point (this)
let p = point.diff(point); // Même principe que diff() mais crée d'abord un nouveau Point afin de ne pas toucher au "point" d'origine
point.diff(new Rect(0, 1, 2, 3)); // renvoie la différence entre point le topLeft de Rect (soit {x:0, y:1})
</script>

Point Modification : min()

Cette méthode permet de retenir que le x et le y le plus petit.
Elle accepte plusieurs paramètres en entrée, correspondant à autant de point (ou équivalent aux paramètres passés lors de la construction d'un point).
<script>
const point = new Point(3, 4); // point = {x: 3, y: 4}
point.min(2, 5); // point = {x: 2, y: 4}
const point2 = point.Min(3, 1); // point = {x: 2, y: 4} et point2 = {x: 2, y: 1}
point.min(point2, −2, 3); // point = {x: −2, y: 1}
point.min.apply([{0, 0}, point2]); // point = {x: −2, y: 0}
point.min(new Rect(−1, −2, +1, +2));// point = {x: −2, y: −2}
</script>

Point Modification : max()

Cette méthode permet de retenir que le x et le y le plus grand.
Elle accepte plusieurs paramètres en entrée, correspondant à autant de point (ou équivalent aux paramètres passés lors de la construction d'un point).
<script>
const point = new Point(3, 4); // point = {x: 3, y: 4}
point.max(5, 2); // point = {x: 5, y: 4}
const point2 = point.Max(1, 6); // point = {x: 5, y: 4} et point2 = {x: 5, y: 6}
point.max(point2, 7, 3); // point = {x: 7, y: 6}
point.max.apply([{8, 0}, point2]); // point = {x: 8, y: 6}
point.max(new Rect(6, 7, 8, 9)); // point = {x: 8, y: 7}
</script>

Point Modification : prod()

Cette méthode permet d'appliquer une multiplication (ou division avec 1/x) sur le x et le y du point.
Attention, cette méthode a 3 signatures bien précises :
  • Soit une seule valeur numérique : l'appliquera simultanément au x et y du point.
  • Soit x, y
  • Soit un point

Exemple:
<script>
const point = new Point(100, 200); // point = {x: 100, y: 200}
point.prod(2, 0.5); // x * 2 et y / 2 ⇒ {x: 200, y: 100}
const p = point.Prod(0.5, 2); // p = {x: 100, y: 200} et point vaut toujours {x: 200, y: 100}
point.prod(10); // x et y multipliés par 10 ⟹ {x: 2000, y: 1000}
point.prod(p); // x * p.x et y * p.y ➾ {x: 200, y: 100}
point.prod(new Rect(2, 3, 4, 5)); // x * 2 et y * 3 ⇒ {x: 400, y: 300}
</script>

Point Modification : round()

La méthoderound permet d'arrondir les coordonnées du point suivant différent mécanisme.
<script>
point.round(round); // Modifie et renvoie le point arrondi à l'entier suivant round : −1: floor, 0: trunc, 1: round, 2: ceil
let p = point.Round(round); // Même principe que round() mais crée d'abord un nouveau Point afin de ne pas toucher à point
</script>
La valeur du paramètre peut être :
Paramètre Math -1.9 -1.6 -1.5 -1.4 -1.1 -1.0 1.0 1.1 1.4 1.5 1.6 1.9 Comportement
-1 floor -2 -1 1 Arrondi à l'entier inférieur ou égal
0 trunc -1 1 N'arrondi pas
1 round -2 -1 1 2 Arrondi au 0,5 supérieur
2 ceil -1 1 2 Arrondi à l'entier supérieur ou égal
Si le paramètre "round" est omis ou ne correspond à aucune de ces valeurs, c'est la valeur 2 (ceil) qui est utilisé.

Point Modification : rect()

Cette méthode permet de transformer un Point en un Rect (sans toucher au point), en lui communiquant une taille.
<script>
const point = new Point(100, 200);
const rect1 = point.rect(10, 20); // utilise une largeur de 10 et hauteur de 20 pour renvoyer un rectangle {top: 200, left: 100, bottom: 220, right: 110}
const rect2 = point.rect(point); // prend la taille passé par point pour renvoyer un rectangle {top: 200, left: 100, bottom: 400, right: 200}
const rect3 = point.rect(rect1); // reprend la taille de rect1 pour renvoyer un rectangle {top: 200, left: 100, bottom: 120, right: 110}
</script>

Point : Comparaison

Un ensemble de méthode permettant de comparer les points entre eux.

Point Comparaison : equal()

Cette méthode renvoie vrai si les deux points sont identiques.
<script>
let t = point.equal(point2); // renvoie true si point a les mêmes coordonnées que point2
</script>
Il est possible de passer un paramètre de flou supplémentaire, pour indiquer que le rectangle peut-être égale si leurs coordonnées est proche d'une certaine tolérance.
Ce paramètre n'est possible qu'avec unPoint passé en 1er paramètre.
<script>
const cible = new Point(10, 10);
const acote = new Point( 8, 12);
const proche = cible.equal(acote, 2); // renvoie true</abbr>
const loin = cible.equal(acote, 1); // renvoie false</abbr>
</script>

Point Comparaison : vector()

Cette méthode permet de connaitre la position entre deux points, elle renvoie la distance et l'angle du pointthis au point passé en paramètre.
Elle a en signature :
  1. Un point (ou équivalent aux paramètres passés lors de la construction d'un point).
  2. Un boolean indiquant si àtrue qu'il faut partir du point passé en paramètre jusqu'au point duthis.
Si aucun paramètre n'est passé, elle simulera le Point(0, 0).
Elle renvoie une structure contenant :
  • degree : l'angle en degré, entre 0 et 360.
  • radian : l'angle en radian, entre 0 et 2 * π.
  • radius : la distance entre les deux points, toujours positif.
Si les deux points sont à la même position, toutes ces valeurs sont égales à 0.
<script>
let distance = point.vector(point2).radius;
const identique = new Point(0, 0).vector(); // renvoie {degree: 0, radian: 0, radius: 0}
const vecteur1 = new Point().vector(new Point(10, 10)); // renvoie {degree: 135, radian: 2.356194490192345, radius: 14.142135623730951}
const vecteur2 = new Point().vector(10, 10, true); // renvoie {degree: 315, radian: 5.497787143782138, radius: 14.142135623730951}
const vecteur3 = new Point(10, 10).vector(); // renvoie {degree: 315, radian: 5.497787143782138, radius: 14.142135623730951}
</script>
valeurs renvoyées par point.vector() en radian ou degré

Point Comparaison : includes()

Cette méthode renvoie vrai si le point est à l'interieur du rectangle passé en paramètre.
Cette méthode est identique à rect.inludes().
<script>
let t = point.includes(rect); // renvoie true si point est à l'intérieur de rect −> strictement identique à Rect.includes(point) mais partant du point
</script>

Point : trace()

Comme pour le Rect, il est possible de tracer visuellement le point pour le développeur, par une représentation sous forme de croix, en utilisant la méthodetrace.
Plusieurs type de paramètre possible, qu'il est possible d'enchaîner pour faire une animation :
  • booléen : vrai, il affiche une trace, sinon, il efface la trace.
  • Chaîne de caractère : la trace est nommée afin de la rendre unique parmis toutes les traces des rectangles.
  • Couleur chaîne : chaîne de caractère reprenant le principe précédent, mais représentant une des couleurs HTML qui sera utilisée pour afficher la trace.
  • Couleur numérique de 1 à 9 : une couleur extraite d'une liste de contante : 1= bleu, 2= violet, 3= vert, 4= jaune, 5= cyan, 6= saumon, 7= brun, 8= rouge, et 9= gris.
  • Valeur numérique nul ou négative : efface la trace, comme pour unfalse
  • Délai numérique supérieur à 9 : délai en milliseconde avant d'afficher la couleur suivante, ou d'effacer la trace si plus de paramètre.
  • Point, Rect ou Element : permettant de relativiser cette trace par rapport au point, rectangle ou à la position de l'élément HTML.
  • Fonction : appelée lors du traitement de cette étape, avec le passage des arguments : this, tableau des paramètres restant et la prise en compte de la valeur éventuelle de retour.

Exemple:
<script>
point.trace(true); // affiche la trace
point.trace(false); // supprime la trace
point.trace(de 1 à 9); // une trace avec une couleur passant par bleu (1), violet (2), vert (3), jaune (4), cyan (5), saumon (6), brun (7), rouge (8), et gris (9)
point.trace('color'); // une trace précise et unique sur l'écran avec la couleur indiquée (blue, yellow, cyan, red, …)
point.trace('color', 500); // une trace qui disparait au bout d'une demie seconde
point.trace(500, 'green', 500, 'red', 1500); // une trace qui s'affiche au bout d'une demie seconde en vert, puis au bout d'une seconde en rouge, et disparait après une seconde et demie.
point.trace('blue', 500, point2, p => {trace(p); return 'pink'}); // affiche une trace bleue, attend 1/2 seconde avant de soustraire point2, faire une trace sur la console et changer la couleur en rose.
</script>
Comme la plus part des autres méthodes, la méthodetrace accepte la Majuscule en première lettre, permettant de ne pas perturber des délais parallèles :
<script>
p1.trace(1000, 'blue'); // devrait afficher une couleur bleue au bout de 1 seconde
p1.trace(1000, 'red'); // mais à cause de cette 2ème trace, supprime la 1ère par un affichage en rouge au bout de 2 secondes
p2.Trace(1000, 'blue'); // affichera une couleur bleue au bout de 1 seconde, sur une trace "parallèle"
p2.trace(1000, 'red'); // et grâce au Trace précédent (ou indifféremment sur cette 2ème péthode), affichagera en plus en rouge au bout de 2 secondes
</script>

Point : setter

Un mécansime plus poussé permet d'ajouter une méthode lors de l'initialisation du point, méthode qui sera appelée si une modification de coordonnée intervient sur le point.
Sa signature est constitués de 2 paramètres : x et y, qui contiennent soit undefined, soit la nouvelle valeur qui sera affectée au point, après le return de cette méthode.
<script>
const point = new Point(10, 100, function(x, y) {
if (x !== undefined)
trace('modification des abscisses en', x);
if (y !== undefined)
trace('modification des ordonnées en', y);
});
point.y = 3; // affichera : modification des ordonnées en 3
point.add(5); // affichera : modification des abscisses en 15
</script>

JS : Rect

Cet objet représente un rectangle, avec 3 attributs Point{topLeft, botRight, size} Attention: Le point topLeft sera toujours inférieur ou égal au point botRight, donc toujours au dessus et sur la gauche de ce dernier.

Rect : Création

La création d'un rectangle se fait parnew Rect(), en lui passant les coordonnées sous formes de valeur numérique, de points, de balise DOM, d'évenement, de Rect ou simplement vide :
<script>
let rect = new Rect(); // rectangle vide (en position 0 et de taille 0)
let rect = new Rect([x1, y1, x2, y2]); // initialise avec un tableau de coordonnées
let rect = new Rect({top: 1, left: 2, bottom: 3, right: 4}); // initialise avec une structure "tolebori"
let rect = new Rect(tag); // initialise à partir des offsets
let rect = new Rect(event); // initialise à partir de clientX et clientY
let rect = new Rect(event_start, event2_stop); // initialise à partir de 2 événements distinctes
let rect = new Rect(x1, y1, x2, y2); // initialise avec des coordonnées
let rect = new Rect(topLeft, botRight); // initialise avec 2 points
let rect = new Rect(point, rect); // initialise avec un point qui devient le topLeft, et un rect dont on récupère la taille
let rect = new Rect(point, x2, y2); // initialise avec un point qui devient le topLeft, et les autre deux dimensions
let rect = new Rect({x: 1, y: 2}, {x: 3, y: 4}); // initialise avec de simples structures
let rect = new Rect(rect); // clônage
let rect = Rect(data); // sucre du new Rect(data)
</script>

Rect : Utilisation

Il est possible de récupérer et modifier les attributs topLeft, botRight et size directement.
Puisque topLeft, botRight et size sont des points, il est possible d'appliquer les méthodes des points sur ces 3 attributs.
Et d'utiliser le rectangle pour positionner et dimensionner un élément :
<script>
let point = rect.topLeft; // Point de l'angle le plus haut à gauche
rect.botRight = point; // Point de l'angle le plus bas à droite
let size = rect.size; // Point représentant la taille du rectangle
let x1 = rect.topLeft.x;
let y1 = rect.topLeft.y;
let x2 = rect.botRight.x;
let y2 = rect.botRight.y;
let width = rect.size.x;
let height = rect.size.y;
rect.size = rect.topLeft;
rect.botRight.y += 10;
rect.setRect(element); // positionne et dimensionne une balise HTML
element_html.setRect(rect); // idem mais en partant de la balise
rect.trace(1); // permet d'afficher visuellement un rectangle en trace, le paramètre servant à unifier les traces, un false supprimant tous les traces.
</script>
Si vous souhaitez modifier directement la taille d'un rectangle, il est possible d'utiliser une des méthodes ci-dessous, ou d'affecter l'attributbotRight, ou l'attributsize :
<script>
const rect = new Rect(10, 10, 110, 210); // la taille est de 100 x 200
rect.botRight.x += 20; // la taille est maintenant de 120 x 200
const corner = rect.botRight; // corner est un pointer direct sur le coin bas gauche de rect
corrner.y −= 50; // la taille est maintenant de 120 x 150
rect.size.x = 50; // la taille est maintenant de 50 x 150
const size = rect.size; // size pointe sur la taille de ce rectangle
size.y = 100; // la taille est maintenant de 50 x 100
</script>

Rect : Modification

Il est possible de "déplacer" ou redimensionner les rectangles, soit en modifiant directement ces attributs, soit en utilisant un ensemble de méthode de mofication.
Ces méthodes sont doublées, si le nom de la méthode est :
  1. Tout en minuscule : qui applique une auto-modification sur le this, avant de le renvoyer.
  2. Commence par une lettre Majuscule : même mécanisme, mais en clônant l'object avant, afin de ne pas modifier le rectangle d'origine.

Rect Modification : add()

La méthodeadd applique un déplacement ou addition au rectange, en reprenant les mêmes signatures que pour une création de Rect, avec :
  • Si un point est passé ((Point),(x),(x, y), …), le rectangle sera juste déplacé.
  • Si un rectangle est passé ((Rect),(x1, y1, x2, x2),(topLeft, botRight),(Element), …) : le rectangle sera déplacé et redimensionné.

Exemple:
<script>
rect.add(rect1).add(point).add(x, y); // Ajoute rect1 (ou point ou x,y) à rect et le renvoie. On peut dire qu'il déplace (et déforme) le rect.
rect.add({x: 1, y: −2, w: 3, h: −4}); // Transforme le rect en le poussant de 1 sur la droite, remontant de 2, elargissant de 3 et rétrécissant de 4 en hauteur.
let r = rect.Add(rect1); // Même principe que add() mais crée d'abord un nouveau Rect afin de ne pas toucher à rect.
</script>

Rect Modification : sub()

La méthodesub applique une soustraction (déplacement dans l'autre sens) identique mais inversé par rapport à Rect.add().
<script>
rect.sub(rect1).sub(point).sub(x,y); // Renvoie un nouveau rect, enlevé de rect1, …
rect.sub({x: 1, y: −2, w: 3, h: −4}); // Transforme le rect en le poussant de 1 sur la gauche, descendant de 2, maigrissant de 3 et elargissant de 4 en hauteur.
let r = rect.sub(rect1).sub(point); // Même principe que sub() mais crée d'abord un nouveau Rect afin de ne pas toucher à rect.
</script>

Rect Modification : resize()

La méthoderesize permet de redimensionner le rectangle
Sa signature est :
  • Soit 1 valeur numérique : représentant la modification de size.x
  • Soit 2 valeurs numérique : représentant la modification de size
  • Soit un Point : représentant la modification de size
  • Soit un Rect : dont sa taille sera utilisé pour modifier le size
  • Soit un élément HTML : dont sa taille sera utilisé pour modifier le size
Renvoie le this, permettant de chaîner les méthodes.
Si la valeur passée pour lex ouy :
  • est un nombre positif : il sera utilisé pour positionner la taille du rectangle.
  • est un nombre égale à 0 : il générera un rectangle vide, en plaçant son botRight à la même position que son topLeft.
  • est un nombre négatif ou n'est pas un nombre (null,undefined, -1,false, …): il ne sera pas utilisé.

Exemple:
<script>
const point = new Point(4, 8);
const rect = new Rect(10, 30, 110, 210); // rect = {left: 10, top: 30, right: 110, bottom: 210}
rect.resize(10); // rect = {left: 10, top: 30, right: 20, bottom: 210} −> right = left + 10
let copy = rect.Resize(20); // rect = {left: 10, top: 30, right: 20, bottom: 210} & copy = {left: 10, top: 30, right: 40, bottom: 210}
rect.resize(2, 4); // rect = {left: 10, top: 30, right: 12, bottom: 34} −> right = left + 2, bottom = top + 4
rect.resize(−1, 6); // rect = {left: 10, top: 30, right: 110, bottom: 36} −> right n'est pas touché, bottom = top + 6
rect.resize(point); // rect = {left: 10, top: 30, right: 14, bottom: 38} −> right = left + 4, bottom = top + 8
rect.resize(copy); // rect = {left: 10, top: 30, right: 40, bottom: 210} −> right = left + (4010), bottom = top + (21030)
rect.resize(tag); // rect est redimensioné par rapport à la taille de l'élément HTML tag
</script>
Note : si vous souhaitez modifier la taille du rectangle en relation avec sa taille actuelle, il faut utiliser la méthode scale().
Utilisation de Rect.resize()

Rect Modification : scale()

La méthodescale permet d'élargir ou rétrécir le rectangle (en passant unPoint), en touchant éventuellement sa position (en passant unRect)
Sa signature est :
  • Soit 1 valeur numérique : représentant le décalage de botRight.x
  • Soit 2 valeurs numérique : représentant le décalage de botRight
  • Soit un Point : représentant le décalage de botRight
  • Soit 4 valeurs numériques : représentant le décalage de topLeft et botRight
  • Soit un Rect : représentant le décalage de topLeft et botRight
  • Soit un élément HTML : représentant le décalage de topLeft et botRight
Renvoie le this, permettant de chaîner les méthodes.
<script>
const point = new Point(−2, −4);
const rect = new Rect(10, 30, 110, 210); // rect = {left: 10, top: 30, right: 110, bottom: 210}
rect.scale(10); // rect = {left: 10, top: 30, right: 120, bottom: 210} −> right += 10
let copy = rect.Scale(10); // rect = {left: 10, top: 30, right: 120, bottom: 210} & copy = {left: 10, top: 30, right: 140, bottom: 210}
rect.scale(2, 4); // rect = {left: 10, top: 30, right: 122, bottom: 214} −> right += 2 & bottom += 4
rect.scale(point); // rect = {left: 10, top: 30, right: 120, bottom: 210} −> right −= 2 & bottom −= 4
rect.scale(1, 2, −4, −8); // rect = {left: 11, top: 32, right: 116, bottom: 202} −> left += 1 & top += 2 & right −= 4 & bottom −= 8
rect.scale(copy); // rect = {left: 21, top: 62, right: 256, bottom: 412} −> left += 10 & top += 30 & right += 140 & bottom += 210
copy.scale(−10, −20, −200, −190); // copy = {left: 0, top: 10, right: 0, bottom: 20} −> right: 140200 = −60 qui est ramené à left & bottom: 210190 = 20 qui reste puisque > à top
</script>
Note : cette méthode ajoute ou retranche à la taille actuelle du rectangle, sans jamais être inférieur à zéro. Si vous souhaitez directement modifier la taille à une valeur précise, il faut utiliser l'attribut size ou la méthode resize().
Utilisation de Rect.scale()

Rect Modification : clip()

La méthodeclip permet de déplacer la position top-left en agrandissant ou rétrécissant si besoin la taille afin que la position bas-gauche ne bouge pas.
Sa signature est :
  • Soit 1 valeur numérique : représentant le décalage de topLeft.x
  • Soit 2 valeurs numérique : représentant le décalage de topLeft
  • Soit un Point : représentant le décalage de topLeft
  • Soit un Rect : dont sa position topLeft sera utilisée pour décalage le topLeft
  • Soit un élément HTML : dont sa position topLeft sera utilisée pour décalage le topLeft
Renvoie le this, permettant de chaîner les méthodes.
<script>
const point = new Point(−2, −4);
const rect = new Rect(10, 30, 110, 210); // rect = {left: 10, top: 30, right: 110, bottom: 210}
rect.clip(10); // rect = {left: 20, top: 30, right: 110, bottom: 210} −> left += 10
let copy = rect.Clip(20); // rect = {left: 20, top: 30, right: 110, bottom: 210} & copy = {left: 30, top: 40, right: 110, bottom: 210}
rect.clip(2, 4); // rect = {left: 20, top: 34, right: 110, bottom: 210} −> left += 2 & top += 4
rect.clip(point); // rect = {left: 20, top: 30, right: 110, bottom: 210} −> left −= 2 & top −= 4
rect.clip(copy); // rect = {left: 50, top: 70, right: 110, bottom: 210} −> left += 30 & top += 40
rect.clip(100, 100); // rect = {left: 150, top: 170, right: 150, bottom: 210} −> left += 100 & top += 100, puisque dépassement, right est ramené à left
</script>
Utilisation de Rect.clip()

Rect Modification : enclose()

La méthodeenclose agrandie (vers le haut gauche et/ou vers le bas droit) si besoin le rectangle afin d'englober le rectangle ou point passé en paramètre.
<script>
rect.enclose(rect1).include(point); // Se transforme en rect englobant son rect et le rect1 ou point
let r = rect.Include(rect1).include(point); // Même principe que include() mais crée d'abord un nouveau Rect afin de ne pas toucher à rect.
</script>
Utilisation de Rect.enclose()


Note : cette méthode différe de Rect.exclose() si le paramètre est un rectangle, mais est identique si un paramètrePoint est passé, puisque le point n'a par définition aucune taille.
Note : ne pas confondre cette méthode de modificationRect.enclose() avec la méthode de comparaison Rect.includes().

Rect Modification : exclose()

La méthodeexclose() agrandie (vers le haut gauche et/ou vers le bas droit) si besoin le rectangle afin de toucher les bords du rectangle ou point passé en paramètre.
<script>
rect.exclose(rect1).exclose(point); // Se transforme en rect englobant son rect et le rect1 ou point
let r = rect.Exclose(rect1).exclose(point); // Même principe que exclose() mais crée d'abord un nouveau Rect afin de ne pas toucher à rect.
</script>
Utilisation de Rect.exclose()


Note : cette méthode différe de Rect.enclose() si le paramètre est un rectangle, mais est identique si un paramètrePoint est passé, puisque le point n'a par définition aucune taille.

Rect Modification : intersect()

La méthodeintersect rétréci le rectangle pour former le rectangle correspondant à l'intersection des 2 rectangles (l'emplacement qui est recouvert par les 2 rectangles), pouvant être un rectangle vide si aucune intersection.
<script>
rect.intersect(rect2); // Redimensionne rect pour correspondre au rectancle formé par l'intersection des deux rectangles
let r = rect.Intersect(rect2); // Même principe que intersect() mais crée d'abord un nouveau Rect afin de ne pas toucher à rect.
</script>

Rect Modification : prod()

Cette méthode permet d'appliquer une multiplication sur le topLeft et botRight du rectangle.
Attention, cette méthode a une signature précise :
  • une seule valeur numérique : l'appliquera simultanément au x et y du topLeft et botRight.
  • valeur_x, valeur_y : appliquera valeur_x au aux x et valeur_y aux y du topLeft et botRight.
  • left, top, right, bottom : 4 valeurs appliquées à chaque coordonnées
  • un seul point : appliquera simultanément au topLeft et botRight
  • topLeft, botRight : appliquera ces 2 points respectivement au topLeft et botRight
  • rectangle

Exemple:
<script>
const rect = new Rect(100, 200, 300, 400); // rect = {left: 100, top: 200, right: 400, bottom: 600}
rect.prod(2); // rect = {left: 200, top: 400, right: 800, bottom: 1200}
const r = rect.Prod(0.01); // r = {left: 1, top: 2, right: 4, bottom: 6} et rect n'a pas changé
const p = new Point(2, 0.5);
rect.prod(0.5); // rect = {left: 100, top: 200, right: 400, bottom: 600}
rect.prod(2, 1); // rect = {left: 200, top: 400, right: 400, bottom: 600}
rect.prod(1, 2, 3, 4); // rect = {left: 200, top: 800, right: 1200, bottom: 2400}
rect.prod(p); // rect = {left: 400, top: 1600, right: 600, bottom: 1600} bottom ramené à 1600 ne pouvant être inférieur à top
rect.prod(p, p); // rect = {left: 800, top: 800, right: 1200, bottom: 800}
rect.prod(r); // rect = {left: 800, top: 1600, right: 4800, bottom: 4800}
</script>

Rect Modification : round()

La méthoderound permet d'arrondir les coordonnées du rectangle suivant différent mécanisme.
<script>
rect.round(round); // Renvoie le rect arrondi à l'entier suivant la valeur de round : −1: floor, 0: trunc, 1: round, 2: ceil
const r = point.Round(round); // Même principe que round() mais crée d'abord un nouveau Rect afin de ne pas toucher à rect.
</script>
En prenant exemple d'un carré de position -1,5 à +1,5 (carré noir sur le schéma), la valeur du paramètre peut être :
Paramètre Math -1.9 -1.6 -1.5 -1.4 -1.1 -1.0 1.0 1.1 1.4 1.5 1.6 1.9 couleur schéma Comportement
-1 floor -2 -1 1 jaune Arrondi à l'entier inférieur ou égal
0 trunc -1 1 rouge N'arrondi pas
1 round -2 -1 1 2 jaune ou bleu Arrondi au 0,5 supérieur
2 ceil -1 1 2 jaune Arrondi à l'entier supérieur ou égal
Si le paramètre "round" est omis ou ne correspond à aucune de ces valeurs, c'est la valeur 2 (ceil) qui est utilisé.
-2-1,5-10+1+1,5+2 -2-1,5-10+1+1,5+2

Rect : Comparaison

Un ensemble de méthode permettant de comparer les rectangles entre eux.

Rect Comparaison : equal()

Cette méthode renvoie vrai, si le rectangle a les mêmes coordonnées que le rectangle passé en paramètre.
<script>
let t = rect.equal(rect1); // renvoie true si rect est égal à rect1
let t = rect.equal(point1, point2); // renvoie true si rect est egal au rectangle formé par les deux points
let t = rect.equal(left, top, right, bottom); // renvoie true si rect est egal au rectangle formé par les 4 valeurs
</script>
Il est possible de passer un paramètre de flou supplémentaire, pour indiquer que le rectangle peut-être égale si leurs coordonnées est proche d'une certaine tolérance.
Ce paramètre n'est possible qu'avec unRect passé en 1er paramètre.
<script>
const cible = new Rect(10, 10, 20, 20);
const acote = new Rect( 8, 12, 22, 18);
const proche = cible.equal(acote, 2); // renvoie true</abbr>
const loin = cible.equal(acote, 1); // renvoie false</abbr>
</script>

Rect Comparaison : includes()

Cette méthode renvoie vrai, si le rectangle ou point passé en paramètre, est entièrement inclus dans le rectangle
<script>
let t = rect.includes(rect1); // renvoie true si rect1 est totalement à l'intérieur
let t = rect.includes(point); // renvoie true si point est à l'intérieur de rect −> strictement identique à Point.includes(rect)
</script>
Note : ne pas confondre cette méthode de comparaisonRect.includes() avec la méthode de modification Rect.enclose().

Rect Comparaison : contains()

Cette méthode renvoie vrai, si le rectangle passé en paramètre, touche le rectangle
<script>
let t = rect.contains(rect2); // renvoie true si rect1 est en partie ou totalement à l'intérieur
</script>

Rect Comparaison : excludes()

Cette méthode renvoie vrai, si le rectangle passé en paramètre, ne touche pas le rectangle
<script>
let t = rect.excludes(rect1); // renvoie true si rect1 est totalement à l'extérieur
</script>

Rect Comparaison : empty()

Cette méthode renvoie vrai, si ce rectangle est vide.
<script>
let t = rect.empty(); // renvoie true si rect.topLeft == rect.botRight
</script>

Rect : trace()

Comme pour le Point, il est possible de tracer visuellement le rectangle pour le développeur, en utilisant la méthodetrace.
Plusieurs type de paramètre possible, qu'il est possible d'enchaîner pour faire une animation :
  • booléen : vrai, il affiche une trace, sinon, il efface la trace.
  • Chaîne de caractère : la trace est nommée afin de la rendre unique parmis toutes les traces des rectangles.
  • Couleur chaîne : chaîne de caractère reprenant le principe précédent, mais représentant une des couleurs HTML qui sera utilisée pour afficher la trace.
  • Couleur numérique de 1 à 9 une couleur extraite d'une liste de contante : 1= bleu, 2= violet, 3= vert, 4= jaune, 5= cyan, 6= saumon, 7= brun, 8= rouge, et 9= gris.
  • Valeur numérique nul ou négative : efface la trace, comme pour unfalse
  • Délai numérique supérieur à 9 : délai en milliseconde avant d'afficher la couleur suivante, ou d'effacer la trace si plus de paramètre.
  • Point, Rect ou Element : permettant de relativiser cette trace par rapport au point, rectangle ou à la position de l'élément HTML.
  • Fonction : appelée lors du traitement de cette étape, avec le passage des arguments : this, tableau des paramètres restant et la prise en compte de la valeur éventuelle de retour.

Exemple:
<script>
rect.trace(true); // affiche la trace
rect.trace(false); // supprime la trace
rect.trace(de 1 à 9); // une trace avec une couleur passant par bleu (1), violet (2), vert (3), jaune (4), cyan (5), saumon (6), brun (7), rouge (8), et gris (9)
rect.trace('color'); // une trace précise et unique sur l'écran avec la couleur indiquée (blue, yellow, cyan, red, …)
rect.trace('color', 500); // une trace qui disparait au bout d'une demie seconde
rect.trace(500, 'green', 500, 'red', 1500); // une trace qui s'affiche au bout d'une demie seconde en vert, puis au bout d'une seconde en rouge, et disparait après une seconde et demie.
rect.trace('blue', 500, point, p => {trace(p); return 'pink'}); // affiche une trace bleue, attend 1/2 seconde avant de soustraire point, faire une trace sur la console et changer la couleur en rose.
</script>
Comme la plus part des autres méthodes, la méthodetrace accepte la Majuscule en première lettre, permettant de ne pas perturber des délais parallèles :
<script>
r1.trace(1000, 'blue'); // devrait afficher une couleur bleue au bout de 1 seconde
r1.trace(1000, 'red'); // mais à cause de cette 2ème trace, supprime la 1ère par un affichage en rouge au bout de 2 secondes
r2.Trace(1000, 'blue'); // affichera une couleur bleue au bout de 1 seconde, sur une trace "parallèle"
r2.trace(1000, 'red'); // et grâce au Trace précédent (ou indifféremment sur cette 2ème péthode), affichagera en plus en rouge au bout de 2 secondes
</script>

JS : Color

Objet permettant le traitement des couleurs.

Color: création

Permet de créer une nouvelle couleur à partir de différent format.
<script>
const rgb = new Color("rgb(255, 192, 203, 0.5)"); // pour du rose semi−transparent.
const hex = new Color("#ffc0cb80"); // idem
const hsl = new Color("hsla(350deg, 100%, 80%, 0.5)"); // idem
const clone = new Color(rgb);
</script>

Color: rgb()

S'auto-transforme si besoin sa couleur en une couleur RGB, avec la structure :
<script>
{
red: 0, // un entier allant de 0 à 255 représentant les nuances de rouge
green: 0, // un entier allant de 0 à 255 représentant les nuances de vert
blue: 0, // un entier allant de 0 à 255 représentant les nuances de bleu
alpha: 0, // une valeur allant de 0 à 1 représentant la couche alpha, de 0 pour transparent à 1 pour opaque
}
</script>
Il existe également la méthodeRGB() (en majuscule) qui est identique àrgb() mais renvoie un nouvel objetColor sans toucher à l'original.
<script>
const hsl = new Color("hsla(240 100% 80% / 5%)"); // créé une couleur HSL
const rgb1 = couleur.RGB(); // transforme hsl en une couleur RGB et la renvoie dans rgb1, sans toucher hsl qui reste en HSL
const rgb2 = couleur.rgb(); // auto−transforme hsl en une couleur RGB, et fait pointer hsl vers rgb2
</script>

Color: hsl()

S'auto-transforme si besoin sa couleur en une couleur HSL, avec la structure :
<script>
{
hue: 0, // une valeur allant de 0 à 360 (non−inclus) représentant la teinte en degré dans le spectre des couleurs avec 0º rouge, 120º:vert, 240º:bleu
saturation: 0, // une valeur allant de 0 à 1 représentant la saturation, de 0 pour grisâtre à 1 pour une couleur pure.
lightness: 0, // une valeur allant de 0 à 1 représentant la luminosité, de 0 pour noir à 1 pour blanc, en passant par 0.5 pour du gris (si saturation faible) à couleur normale (si saturation élevée)
alpha: 0, // une valeur allant de 0 à 1 représentant la couche alpha, de 0 pour transparent à 1 pour opaque
}
</script>
Il existe également la méthodeHSL() (en majuscule) qui est identique àhsl() mais renvoie un nouvel objetColor sans toucher à l'original.
<script>
const rgb = new Color("ffc0cb80"); // créé une couleur RGB
const hsl1 = couleur.HSL(); // transforme rgb en une couleur HSL et la renvoie dans hsl1, sans toucher rgb qui reste en RGB
const hsl2 = couleur.hsl(); // auto−transforme rgb en une couleur HSL, et fait pointer rgb vers hsl2
</script>

Color: hex()

Renvoie une chaîne de caractère de format "rrggbbaa".
Exemple: "ffc0cb80" pour du rose semi-transparent

Elle a en signature un paramètre numérique optionnel :
  • La valeur 3: si cela est possible, elle renverra une valeur grise sous forme : #rgb, en omettant la couche alpha éventuelle, sinon renverra #rrfggbb
  • La valeur 4: si cela est possible, elle renverra une valeur grise sous forme : #rgba (même si alpha égale f), sinon renverra #rrfggbbaa
  • La valeur 6: renvoie #rrggbb, en omettant la couche alpha éventuelle
  • La valeur 8: renvoie #rrggbbaa (même si alpha égale ff).
  • Toute autre valeur ou paramètre manquant : renvoie "#rrggbb" si alpha vaut "ff", sinon renvoie "#rrggbbaa".

Color: toString()

Renvoie une chaîne de caractère, suivant la dernière transformation :
  • rgb : rgba(<red>,<green>,<blue>,<alpha>). Exemple :rgba(255,192,203,0.5) pour du rose semi-transparent
  • hsl : hsla(<hue>deg, <saturation>%, <lightness>%, <alpha>). Exemple :hsla(350deg, 100%, 80%, 0.5) pour du rose semi-transparent

Color: panel()

Cette méthode statique permet de déclencher l'ouverture manuel du ColorPanel sur un élément autre que<input type="color">.
Sa signature est constituée de 3 paramètres :
  1. position : permettant de positionner le ColorPanel, de type Point, Rect, Event, Element.
  2. value : la ou les couleurs déjà sélectionnées, de type chaîne de caractère, de format "#rrggbb".
  3. complement : plusieurs types sont possibles :
    • Aucun paramètre ouundefined ounull : se comporte par défaut.
    • élément HTML: identique à{head: élément}
    • Structure : regroupant un ensemble de paramètres complémentaires :
      • head : élément HTML qui sera ajouté en haut du ColorPanel.
      • strange: comportement de la 1ère couleur :
        • Caractère : qui sera affiché pour indiquer le sens de cette 1ère couleur, si sélectionné, renverra le code "#" (sauf si modifié par strangeCode).
        • false: pour indiquer que cette 1ère couleur n'est pas utilisable.
        • Par défaut: "T" (comme Transparent) sera affiché et si sélectionné, renverra le code "#0000" (sauf si modifié par strangeCode).
      • strangeCode: code de couleur de la 1ère couleur. Si faux, égale à "#0000" si le "Strange" a un comportement par défaut, et "" si "Strange" est défini.
      • over: fonction appelée lorsque l'utilisateur passe la souris au dessus des couleurs avec en signature :
        1. Le code couleur de la couleur survolée.
        2. La constante "over"
        • Lethis est positionné sur la valeurcomplement.target ou surcomplement.
      • target: valeur passée en tant quethis à la méthodeover.
  • Color.panel() renvoie une promesse avec la couleur sélectionnée.

Exemple:
<script>
$('SPAN#couleur').on('mousedown', function() => {
Color.panel(this, this.value, {
head: null, // aucun entête (par défaut)
strange: true, // couleur "T" comme transparente (par défaut)
}).then(color => {
this.value = color; // this.value est affectée automatiquement sur un Color.attachPanel()
this.backgroundColor = color; //
});
});
</script>

Color: attachPanel()

Cette méthode statique permet de rattacher un ColorPanel à n'importe quel élément HTML, en modifiant en retour savalue :
<script>
const tag = $('SPAN#couleur');
Color.attachPanel(tag);
tag.on('change', () => {
trace('couleur', tag.value);
})
</script>
La couleur du fond de l'élément HTML est automatiquement modifié suivant la couleur sélectionnée.
Le resultat de savalue est différent suivant le choix de la couleur sélectionnée :
  • undefined : Aucune couleur n'était et n'a été sélectionnée
  • #RRGGBB : une des couleurs du ColorPanel.
  • #0000: couleur transparente (de format #RGBA) indiquant que c'est la 1ère couleur (strange) qui a été sélectionnée et que cette 1ère couleur a un comportement par défaut.
  • '' (chaîne de caractère vide): indiquant que c'est la 1ère couleur (strange) qui a été sélectionnée et que cette 1ère couleur a un comportement modifié par l'optioncomplement.strange.

Color: closePanel()

Cette méthode statique permet de fermer manuellement le ColorPanel éventuellement ouvert.
Ce ColorPanel se ferme automatiquement suite à un choix d'une couleur ou lors d'un clic en dehors du ColorPanel, mais il est dans certain cas nécessaire de fermer la fenêtre suite à une autre action de l'utilisateur :
<script>
const entete = document.create('label');
entete.create('input', {type: 'checkbox', checked: true}).on('change', Color.closePanel);
entete.create("Fermer la palette de couleur");
$('SPAN#couleur').on('mousedown', function() => {
Color.panel(this, this.value, entete).then(color => {
trace('palette fermée ', entete.checked ? "normalement" : "via la boite à cocher")
this.value = color;
this.backgroundColor = color;
});
});
</script>

JS : PlumeData

Cette classe permet d'accéder à la base locale du navigateur.

Il est possible de stocker des informations sur le navigateur, stocké dans une base de donnée locale.
Plusieurs bases peuvent être présentes sur le navigateur, chacune contenant des stores (ou tables), contenant des enregistrements, contenant des champs nommés.
Chaque base à une notion de version de la base, et le fait que chaque enregistrement peut avoir des champs différents.

Exemple :
<script>
// connexion sur le database database1 en v1, contenant les store1, …, qui seront créés s'il le faut
const database = new PlumeData("database1", ['store1', …], 1);

let record = {id: 42, nom: "Dupond"};

// enregistrement
database.write(store, record);

// lecture
database.read('store1', record.id).then(record => {
$('nom').value = record.nom;
}).catch(err => {
$.message("Erreur sur la lecture de cet enregistrement d'id : " . record.id)
});

// ou lecture avec valeur par défaut si non trouvé
database.read('store1', record.id, {nom: ''}).then(function(record) {
$('nom').value = record.nom;
});

// lecture d'un store
database.select(store, []).then(function(list) {
$.for(list, function(record) {
// …
});
});
</script>
Cet exemple utilise l'itération $.for et les dialogues $.message.

PlumeData : new

Pour ouvrir la base locale (et la créé si besoin), il faut instancier la classePlumeData.
Sa signature est :
  1. Le nom de la base
  2. Un tableau contenant l'ensemble des nom des tables, qui seront utilisés et créé si manquante.
  3. Une valeur numérique entière réprésentant la version de la base. Si la version présente sur le navigateur est différente, elle sera initialisé avant d'être ouverte.
  4. La méthode d'ouverture appelé en asynchrone lorsque la base sera complétement ouverte. Le paramètre de cette méthode contient la référence vers la base.

Exemple:
<script>
const database = new PlumeData("database1", ['store1', …], 1, (db) => $.start('database'));
$.start('database', () => {
// ce start ne sera executé qu'après la connexion complète à la base
});
</script>

PlumeData : read()

Cette méthode permet de lire un enregistrement.
(à finir)

PlumeData : select()

Cette méthode permet de rechercher une liste d'enregistrement.
(à finir)

PlumeData : write()

Cette méthode permet de mémoriser un enregistrement dans un store.
Sa signature est :
  1. Le nom du store
  2. La valeur numérique entière représentant l'identifiant de l'enregistrement.
  3. La structure contenant l'enregistrement, de type {nomduchamp : valeurduchamp}.
  4. La méthode optionnelle qui sera appelée lorsque l'enregistrement sera effectuée.
  • Si aucune méthode asynchrone est passée, cette méthode renvoie une promesse

Exemple:
<script>
database.write(store, 1, record, function(target) {
trace("Enregistrement réalisé")
});
database.write(store, 1, record).then(function(target) {
trace("Enregistrement réalisé")
});
</script>

Il est possible de mutualiser les enregistrements en coupant la méthode en deux appels :
<script>
const store = database.write(store);
store.write(1, {champ: "valeur 1"});
store.write(2, {champ: "valeur 2"});
</script>

Il est enfin possible de passer l'identifiant dans l'enregistrement, avec le nomid :
<script>
database.write(store, {id: 1, champ: 42});
</script>

JS : Function

Plume ajoute des fonctionnalités aux fonctions :

Function.delay()

Cette méthode lance en asynchrone différé la fonction spécifiée, en lui passant le timer et éventuellement unthis, et renvoie le minuteur, pour utilisation d'unclearTimeout eventuel.
Sa signature est :
  • objet (optionnel) qui sera passé en this à la méthode.
  • valeur numérique entière représentant en milliseconde le temps minimum d'attente avant d'appeler la méthode :

Exemple:
<script>
function ma_fonction(parametre) {
console.log(this, parametre);
}
ma_fonction.delay(3000, 42, $('BODY')); // affichera dans 3s : <body> 42
ma_fonction.delay(500, 42); // affichera dans une demie−seconde : <window> 42

// pour un appel simple d'un méthode anonyme, il est plus simple d'utiliser setTimeout
(function(){ … }).delay(500, this);
// … est certainement moins lisibile que …
window.setTimeout(() => { … }, 500, this);

// attention à bien passer le this dans certain cas
localStore.setItem(localStore, 1000, 'mon_store', JSON.stringify({abc: 123}));
</script>
Si le delai est négatif, l'appel sera annulé si un autre appel à cette même méthode est executé alors que la période d'attente précédente n'est pas révolu, ceci permettant de relancer plusieurs fois la méthode au bout d'un période précise, mais qu'un seul appel se fera.
<script>
let timer = ma_fonction.delay(1000, 42); // essaye de lancer la méthode dans une seconde
if (timer)
window.clearTimeout(timer); // arrête l'appel précédent (trop long)
timer = ma_fonction.delay(500, 42); // avant d'essayer de le relancer dès dans une 1/2 seconde

// peut être avantagement remplacé par

ma_fonction.delay(−1000, 42); // essaye d'appeler ma_fonction au bout d'une seconde
ma_fonction.delay(−500, 42); // appel annulé, et réssaye à nouveau au bout d'une 1/2 seconde

</script>
Pour lancer une méthode en boucle, il vaut mieux utiliser la methode suivante Function.until()

Function.until()

Il est possible de lancer une méthode en asynchrone, qui se répetera si besoin.
Sa signature est :
  • this : le this qui sera utilisé à l'intérieur de la méthode, on peut passernull en paramètre si pas de this utilisée dans la fonction
  • paramètres : une série de paramètres (optionnelles) qui seront passés à la méthode
Elle renvoie une promesse avec le résultat de la méthode.
La méthode est appelée avec les paramètres passés àuntil() avec en premier paramètre, un compteur partant de 0.
Elle devra renvoyer quelque chose (mêmenull) qui sera passée comme paramètre à la promesse réussie.
Si cette méthode ne renvoie rien (undefined), elle sera relancée en aysnchrone, jusqu'à quelle renvoie quelque chose, mécanisme permettant de générer une boucle de typeuntil.
Exemple :
<script>
const source = [1, 2, 3, 5, 7, 11, 15 ];
trace('début);

function loop(compteur, nombre_premiers, pas) {
const traces = nombre_premiers.splice(0, pas).map(function(premier) { // on retire les x éléments, que l'on traite
return
' & ' + premier;
});
trace(compteur,
'−>', traces);
if (! nombre_premiers.length)
return "fini";
}
loop.until(
null // this, // pas passe de this puisque inutilisé à l'intérieur de cette méthode
Array.from(source), // fait une copie de la source, qui sera passé au paramètre "nombre_premiers"
2 // le nombre d'élément que l'on traitera, qui sera passé au paramètre "pas"
).then(function(resultat) {
trace(resultat +
'.');
});

trace(
'suite);
</script>
Affichera :
début
suite

0 −> 1 & 2
1 −> 3 & 5
2 −> 7 & 11
3 −> 15
fini.
Ce mécanisme peut s'appliquer sur une méthode immédiate :
<script>
(function(parametres) { … }).until(parametres).then(promesse);
(() => { … }).until();
</script>
Si besoin uniquement de faire un fork (donc sans la partie until), il faut renvoyer quelque chose.
<script>
trace('debut');
(() => {
trace('fork');
return true;
}).until();
trace('continue');
</script>
Renverra :
debut
continue<br>fork
Un exemple de traitement simple pour traiter en parallèle une importante liste de données, qui bloquerait le navigateur avec un traitement mono-tâche :
<script>
(iteration => {
// si plus de donnée −> fin de traitement
if (! this.length)
return true;
const data = this.shift();
// traitement
trace('iteration', iteration, data);
}).until(Array(1000000).fill('plume'));
</script>

Mixin

Plume ajoute un sucre afin de simuler le multi-héritage au sein des classes JS, via le mécanisme de mixin.

Normalement, il n'est pas possible qu'une classe hérite de plusieurs classes en JS et le mécanisme des interfaces n'existent pas.
Plume propose d'ajouter la fonctionmixin() juste après l'extends d'une déclaration et en passant en paramètre, la classe principale, puis la ou les mixins.
Chaque mixin devant être une fonction qui doit renvoyer la construction d'une nouvelle classe, passée en paramètre, et apportant ses propres nouvelles méthodes.
<script>
// déclaration d'un premier mixin, avec une seule méthode, qui pourra être hérité dans plusieurs classes
function mixin1(inherit) {
return class extends inherit {
methode_mixin1() {}
}
}
// déclaration d'un deuxième mixin, en version flèchée.
const mixin2 = inherit => class extends inherit {
methode_mixin2() {}
}
// déclaration d'une classe qui servira d'héritage
class SuperClasse {
superMethode() {}
}
// déclaration de notre classe qui hérite de SuperClasse et des mixins
class MaClasse extends mixin(SuperClasse, mixin1, mixin2) {
maMethode() {}
}
// construction de la classe et utilisation de l'ensemble des méthodes héritées
const maClasse = new MaClasse();
maClasse.maMethode();
maClasse.superMethode();
maClasse.methode_mixin1();
maClasse.methode_mixin2();
</script>

JS : Primitive

Plume ajoute des fonctionnalités aux valeurs primitives (tel que les chaînes de caractères, les nombres, …) :

Object.identical()

Cette méthode permet de vérifier qu'un objet est égale à un autre objet, en comparant chaque valeur en profondeur, même si leurs valeurs ne sont pas dans le même sens.
Sa signature est :
  1. 1er objet.
  2. 2ème objet.
  • Renvoie un booléan indiquanttrue si les deux objets sont égaux.
    <script>
    const egale = Object.identical(
    {a: 1, b: null, c: false, d: undefined},
    {d: undefined, a: 1, c: false, b: null},
    );

    const different = Object.identical(
    {a: 1, b: undefined},
    {a: 1},
    );
    </script>

Note : il existe le pendant de cette méthode côté tableau Array.identical().

Number.forEach()

Cette méthode permet d'itérer un nombre de fois bien précis.
La méthode passée en paramètre, a en signature :
  1. index : valeur numérique partant de 0 et allant jusqu'à la valeur duthis 1.
  2. maximum: valeur indiquant le nombre d'itération
Note : Il est nécessaire d'encadrer de parenthèse les constantes numériques, pour pouvoir appliquer cette méthode dessus.
<script>
let nbr = 5;
nbr.foreach((cpt, max) => trace(cpt, '/', max)); // affichera sur la console 0/5 1/5 2/5 3/5 4/5

(5).forEach(x => trace(x)); // affichera sur la console 0 1 2 3 4

(0).forEach(x => trace(x)); // n'itérera pas et n'affichera rien
(−1).forEach(x => trace(x)); // idem

const list = $('UL');
(list.children.length − 5).forEach(x => list.removeChild(list.lastElementChild)); // ne conservera maximum que 5 premiers éléments de cette liste
</script>

Number.filter()

Cette méthode permet d'itérer un nombre de fois bien précis et de renvoyer un tableau indexé.
La méthode passée en paramètre, a en signature :
  1. index : valeur numérique partant de 0 et allant jusqu'à la valeur duthis 1.
  2. maximum: valeur indiquant le nombre d'itération
  • valeur renvoyée qui si vraie, renverra ce nombre dans le tableau renvoyé.
Note : Il est nécessaire d'encadrer de parenthèse les constantes numériques, pour pouvoir appliquer cette méthode dessus.
<script>
let nbr = 7;
trace( nbr.filter((cpt, max) => cpt & 1).join(', ') ); // affichera sur la console 1, 3, 5
trace( (5).filter(x => x & 1 == 0).join(', ') ); // affichera sur la console 0, 2, 4
trace( (5).filter(x => x >= 5) ); // affichera sur la console un tableau vide

(0).filter(x => true); // n'itérera pas et renverra un tableau vide
(−1).filter(x => true); // idem
</script>

Number.map()

Cette méthode permet d'itérer un nombre de fois bien précis et de renvoyer un tableau indexé.
La méthode passée en paramètre, a en signature :
  1. index : valeur numérique partant de 0 et allant jusqu'à la valeur duthis 1.
  2. maximum: valeur indiquant le nombre d'itération
  • valeur renvoyée qui s'ajoutera au tableau indexé renvoyé.
Note : Il est nécessaire d'encadrer de parenthèse les constantes numériques, pour pouvoir appliquer cette méthode dessus.
<script>
let nbr = 5;
trace( nbr.map((cpt, max) => cpt + '/' + max).join(', ') ); // affichera sur la console 0/5, 1/5, 2/5, 3/5, 4/5

trace( (5).map(x => x + 1).join(', ') ); // affichera sur la console 1, 2, 3, 4, 5

(0).map(x => trace(x)); // n'itérera pas et renverra un tableau vide
(−1).map(x => trace(x)); // idem
</script>

Number.toLocaleName()

Cette méthode renvoie une chaîne de caractère représentant son nombre sous forme de lettre.
Elle a en signature :
  1. locale : paramètre optionnel permettant de spécifié la langue de conversion. Par défaut utilise la langue du navigateur (la même valeur renvoyé par window.navigator.language()).
  2. option : structure pouvant contenir les options suivantes :
    • cardinal: permet de forcer au singulier pour une représentation des cardinaux.
      <script>
      (42).toLocaleName(); // renverra quarante deux
      (−3.14).toLocaleName(); // renverra moins trois virgule quatorze
      (123 456 789 012).toLocaleName(); // renverra cent vingt trois milliard quatre cent cinquante six million sept cent quatre−vingt neuf mille douze
      </script>

Math.between()

Cette methode statique permet de renvoyer un nombre situé entre un minimum et un maximum:
  1. la valeur minimum
  2. la value à calibrer
  3. la valeur maximum
  • Renvoie "valeur" ou "maximum" si valeur est supérieur à maximum, ou "minimum" si valeur est inférieur à minimum.

Exemple:
<script>
var value = Math.between(−3, −4, 99); // renverra −3
var value = Math.between(−3, 100, 99); // renverra 99
var value = Math.between(−3, 12, 99); // renverra 12
var value = Math.between(−3, 12, 99);
// est identique à
var value = Math.max(−3, Math.min(12, 99));
</script>

String.format()

Cette méthode renvoie une chaîne de caractère formatée en utilisant les paramètres passés.
Le format est composé de caractères (qui seront affichés tel quel) et de caractère de substitution de paramètres.
La syntaxe de ces substitutions est : % [ position$ ] [ remplissage ] [ +- ] largeur [ . [ complément ] precision ] type.
  • % : est le caractère pourcentage (%) obligatoire indiquant le début de la substitution.
  • position : est un entier positif optionnel suivi du caractère dollar ($), indiquant le numéro du paramètre à inserer. Si omis, prend le 1er, puis passe au suivant.
  • remplissage : est un caractère optionnel, qui sera utilisé pour le remplissage, afin d'atteindre la taille du nombre de caractère indiqué par largeur. Si omis, rempli avec des espaces.
Attention avec le caractère de remplissage "." qui peut être confondu avec le caractère de séparation utilisé pour la précision. Dans ce cas, il faut bien spécifier 2 "." (voir exemple plus bas).
  • signe : correspond au caractère optionnel suivant :
    • le signe moins (-) : si présent, le formatage avec le caractère de remplissage se fera sur la droite de la valeur. Si omis, ajout le caractère de remplissage sur la gauche.
    • le signe plus (+) : affiche le + avant un nombre positif pour les types "d" ou "f".
  • largeur : est un nombre entier obligatoire indiquant la largeur maximum de la chaîne de sortie (partie décimale d'un type f inclus).
Attention : si la taille de la valeur est supérieur à cette largeur, elle sera affiché entièrement. Si besoin de tronquer, il faut utiliser la précision.
  • complément : est un caractère optionnel, précédé d'un point ou virgule et suivi d'une précision, indiquant le caractère qui sera utilisé pour compléter le nombre de précision, uniquement pour le type f. Si omis, ne complète pas.
Attention : Si c'est une virgule qui est spécifiée, c'est ce caractère qui sera utilisé pour séparé la partie entière de la partie décimale.
  • precision : est un nombre entier optionnel, précédé d'un point ou virgule ou d'un complément, indiquant :
    • soit la taille de la partie flottante pour un type f.
    • soit la taille maximum qui sera conservé pour un type s.
  • type : est un des caractères obligatoires suivants :
    • u : affiche le paramètre sous forme d'un nombre entier positif ou nul.
    • d : affiche le paramètre sous forme d'un nombre entier.
    • f : affiche le paramètre sous forme d'un nombre flottant.
    • s : affiche le paramètre sous forme d'une chaîne de caractère.
    • S : (en majuscule) affiche le paramètre sous forme d'une chaîne de caractère sur une seule ligne, en rendant les caractères de contrôle lisibles.
    • % : forçant d'insérer non pas un paramètre, mais le caractère pourcentage (%).
Exemples :
<script>
console.log( "(%d −> %u −> %f −> %s)".format(1, 2, 3, 4) ); // affichera (1 −> 2 −> 3 −> 4)
console.log( "(%4$d <− %3$u <− %2$f <− %1$s)".format(1, 2, 3, 4) ); // affichera (4 <− 3 <− 2 <− 1)
console.log( "(%x3s|%−3s|% 3s|%03s|%.3.s)".format(1, 2, 3, 4, 5) ); // affichera (xx1|−−2| 3|004|..5)

console.log( "(%⎵1s, %⎵7s, %⎵7s, %⎵7s)".format(42, −42.24, " +.42", "a 42" ) ); // affichera (42, ⎵−42.24, ⎵⎵ +.42, ⎵⎵⎵a 42)
console.log( "(%⎵1u, %⎵7u, %⎵7u, %⎵7u)".format(42, −42.24, " +42", "a 42" ) ); // affichera (42, ⎵⎵⎵⎵⎵42, ⎵⎵⎵⎵⎵42, ⎵⎵⎵⎵⎵⎵0)
console.log( "(%⎵1d, %⎵7d, %⎵7d, %⎵7d)".format(42, −42.24, " +42", "a 42" ) ); // affichera (42, ⎵⎵⎵⎵−42, ⎵⎵⎵⎵⎵42, ⎵⎵⎵⎵⎵⎵0)
console.log( "(%⎵1f, %⎵7f, %⎵7f, %⎵7f)".format(42, −42.24, " +.42", "a 42.24" ) ); // affichera (42, ⎵−42.24, ⎵⎵⎵0.42, ⎵⎵⎵⎵⎵⎵0)

console.log( "(%_3d, %_3d, %_−3d, %_−3d, %_+3d, %_+3d)".format(5, −5, 5, −5, 5, −5) ); // affichera (__5, _−5, 5__, −5_, _+5, _−5)
console.log( "(%03d, %03d, %0−3d, %0−3d, %0+3d, %0+3d)".format(5, −5, 5, −5, 5, −5) ); // affichera (005, −05, 500, −50, +05, −05)

console.log( "(%%)".format(42, −42.24, " +.42", "a 42" ) ); // affichera (%)

console.log("[%s]", "un texte constitué de plusieurs lignes et de tabulation."); // affichera la chaîne sur 3 lignes sans rendre visible les tabulations
console.log("[%S]", "un texte constitué de plusieurs lignes et de tabulation."); // affichera [un texte⏎constitué de plusieurs lignes⏎et⇥de⇥tabulation.]
</script>

String.count()

Cette méthode permet de renvoyer le nombre de fois où la sous-chaîne de caractère, passé en paramètre, a été trouvée dans lethis.
<script>
const reference = "123−45−678−−9";
var count = reference.count(""); // renverra 4
var count = reference.count("−−"); // renverra 1
var count = reference.count("−−−"); // renverra 0
</script>

String.emphasis()

Cette méthode permet de renvoyer la chaîne de caractèresthis exempte d'accent.
Utilisé avant tout pour les tries et recherches (alternatif ànew Intl.Collator(navigator.language, {usage: 'search', sensitivity: 'base'}).compare(str1, str2)).
<script>
const emphasis = reference.emphasis("EeÉéÇç"); // renverra EeEeCc
</script>

String.toCapitalCase()

Cette méthode permet de changer la case des capitales (premières lettres des mots) de cette chaîne, avec en signature :
  1. L'emplacement des capitales, avec en valeur soit :
    • Aucun paramètre ouundefined ou = 1 ou valeur fausse : met la première lettre en majuscule.
    • Expression régulière : met les lettres recherchées en majuscule. Attention, les parenthèses de groupement ne sont pas autorisées dans cette expression rationnelle.
    • Sinon (> 1, vraie, …) : met toutes les premières lettres de chaque mot en majuscule.
Toutes les autres lettres sont placées en minuscule.
  1. locale : paramètre optionnel permettant de spécifié la langue de conversion. Par défaut utilise la langue du navigateur (la même valeur renvoyé par window.navigator.language()).


JS : Array

Plume ajoute des fonctionnalités aux tableaux indexés :

Array.first()

Cette méthode renvoie le premier élément, ounull si tableau vide.

Array.second()

Cette méthode renvoie le second élément, ounull si inexistant.

Array.last()

Cette méthode renvoie le dernier élément, ounull si tableau vide.

Array.delete()

Au même titre que Array.splice(), cette méthode efface l'élément recherché en paramètre, mais la différence est qu'il recherche l'élément (et non sa position) :
Il a en signature un seul paramètre :
  • Soit une valeur dont il recherchera en test strict (===).
  • Soit une méthode qui devra renvoyer vrai si l'élément doit être effacé, avec en signature :
    1. L'élément en cours
    2. L'index de l'élément
    3. Le tableau
  • Soit un booléan, effacant l'ensemble du tableau sitrue (applique unArray.splice(0)).
  • Renvoie le nombre d'élément effacé ou 0 si aucun élément effacé.

Exemple:
<script>
const tab = ['a', 0, 'B', 0, 'c', '0', 'D'];
tab.delete(0); // renvoie 2, le '0' !== 0
console.log(tab); // renvoie ['a', 'B', 'c', '0', 'D']
tab.delete((item, index) => item < 'a'); // renvoie 3 correspondant aux lettres majuscule et nombre
console.log(tab); // renvoie ['a', 'c']
</script>
Si vous savez qu'un seul élément sera effacé, vous avez la possibilité entre utiliserArray.delete() plus lisible, ouArray.splice(Array.findIndex(), 1) plus rapide.
Dans tous les autres cas (plusieurs élément à effacer, effacement conditionné, pas sûr d'avoir un seul élément, …) il est préférable d'utiliserArray.delete().
<script>
const tab = [ {id: 1}, {id: 2} ];
tab.delete(item => item.id == 1);
// est identique et plus lisible mais moins rapide que
tab.splice(tab.findIndex(item => item.id == 1), 1);
</script>

Array.identical()

Cette méthode renvoie true si le tableau passé en paramètre est égal au this. L'ordre des éléments est pris en compte.
<script>
console.log( [1, 3, 'x'].identical([1, 3, 'x']) ); // renverra true
console.log( [1, 3, 'x'].identical([1, null, 'x']) ); // renverra false
console.log( [1, 3, 'x'].identical([1, 3]) ); // renverra false
console.log( [1, 3, 'x'].identical([1, 'x', 3]) ); // renverra false
</script>
Note : il existe le pendant de cette méthode côté objet Object.identical().

Array.fromEach()

Cette méthode permet de créer une structure ({nom: valeur}) en parcourant un tableau.
Elle a la même signature que Array.map() :
La seule différence est que la fonction appelée doit renvoyer un couple[clé, valeur], avec une clé qui soit vraie (donc pas denull,undefined,false, zéro, chaîne de caractère vide, …).
<script>
var struct = ['abc', '0', 64].fromEach((value, index) => [value, index]); // renverra {abc: 0, 0: 1, 64: 2}
var struct = ['abc', 'abc', 0, '0', 0, 64].fromEach((value, index) => [value, index]); // renverra {abc: 1, 0: 3, 64: 4}
</script>

Array.search()

Cette méthode renvoie un nouveau tableau filtré sur la condition renvoyée par la fonction passée en paramètre. Mais à la différence de Array.filter(), le résultat renvoyé par la méthode correspond au résultat renvoyé par la méthodeCallback passée dansArray.search.
Cette méthode a la même signature que Array.filter().
<script>
const filter = [true, '', "ok", null].search((value, index) => value && index); // renverra [2]
const filter = [true, '', "ok", null].search((value, index) => value && index + 1); // renverra [1, 3]
</script>

Array.unique()

Cette méthode renvoie une copie superficielle du tableau en omettant les doublons.
<script>
console.log( [1, 3, 1, 7].unique() ); // renverra [1, 3, 7]
const a = {voyelle: "A"}, b = {consonne: "B"}, c = {consonne: "C"};
console.log( [a, b, a, a, c].unique() ); // renverra [a, b, c]
</script>
Il est possible de lui passer un ou plusieurs paramètres de filtrage, permettant en plus d'unifier, de filtrer ces paramètres.
Cas particulier avec le paramètretrue qui indiquera de filtrer tous les éléments de valeur fausse (false,undefined,null, "" ou 0).
<script>
[64, 42, 8, undefined, null, 0, '', false, 8, 42, 64].unique(); // renverra [64, 42, 8, undefined, null, 0, '', false]
[64, 42, 8, undefined, null, 0, '', false, 8, 42, 64].unique(true); // renverra [64, 42, 8]
[64, 42, 8, undefined, null, 0, '', false, 8, 42, 64].unique(undefined); // renverra [64, 42, 8, null, 0, '', false]
[64, 42, 8, undefined, null, 0, '', false, 8, 42, 64].unique(undefined, false); // renverra [64, 42, 8, null, 0, '']
</script>

Array.clone()

À la différence de Array.from(), cette méthode renvoie une copie complète (et pas seulement superficielle) du tableau.
Elle s'appuie pour cela sur la méthode $.clone().
<script>
const objet = {a: 0};
const origin = [objet, 1];
const shallow = Array.from(origin);
const clone = $.clone(origin);
origin.push(2);
shallow.push(3);
clone.push(4);
shallow[0].a = 'modif';
// Renverra
origin = [{a: 'modif'}, 1, 2];
shallow = [{a: 'modif'}, 1, 3];
clone = [{a: 0}, 1, 4];
</script>

Array.isEmpty()

Cette méthode renvoietrue si lelength du tableau est égale à 0, sinon renvoiefalse.
Elle est surtout implémentée pour le mécanisme de $.isEmpty()
<script>
Array.prototype.isEmpty = function() {
return ! this.length;
}
</script>

Array.purify()

Cette méthode renvoie un tableau épuré des élémentsundefined.
Elle a en signature un seul paramètre optionnel :
  • undefined ou absent : épure le tableau que sur lesundefined.
  • true: épure le tableau des valeurs correspondant à faux (undefined,null,false, 0 ou "").
  • Sinon = épure le tableau sur lesundefined et les valeurs identiques au paramètre passé (par comparaison par égalité stricte).
Renvoie un nouveau tableau épuré.
<script>
const liste = [7, undefined, null, false, 0, '', 'end'];
liste.purify(); // renverra [7, null, false, 0, '', 'end']
liste.purify(true); // renverra [7, 'end']
liste.purify(0); // renverra [7, null, false, '', 'end']
liste.purify(7); // renverra [ null, false, 0, '', 'end']
liste.purify("7"); // renverra [7, null, false, 0, '', 'end']
</script>

Array.toggle()

Cette méthode permet de modifier le contenu du tableau, en ajoutant ou retirant un élément directement dans le tableau.
Sa signature est :
  1. valeur : la valeur qui sera ajoutée ou retirée.
  2. action : quel type d'action à réaliser, avec :
    • si vrai: la valeur sera ajoutée
    • si fausse: la valeur sera retirée
    • si omis ou égal àundefined ou ànull, la valeur sera ajoutée si inexistante, et retirée si présente.
  • La méthode renvoie le tableau modifiée.

Array.intersect()

Cette méthode permet de faire une intersection sur des tableaux, elle renvoie un nouveau tableau dont les éléments se trouve dans le tableau duthis et des tableaux passés en paramètre.
<script>
[1, 42, 7, "ok"].intersect(["ok", 2, 42, "1"]); // renverra [42, "ok"]
list.intersect(array1, array2, arrayx);
</script>

Pour une union utiliser Array.concat(), pour une différence utiliser Array.diff(), pour une exclusion utiliser Array.exclude().

Array.exclude()

Cette méthode permet de faire une exclusion sur des tableaux, elle renvoie un nouveau tableau dont les éléments se trouve pas plus d'une fois dans tableau duthis et des tableaux passés en paramètre.
<script>
[1, 42, 7, "ok"].exclude(["ok", 2, 42, "1"]); // renverra [1, 7, 2, "1"]
list.exclude(array1, array2, arrayx);
</script>

Pour une union utiliser Array.concat(), pour une différence utiliser Array.diff(), pour une intersection utiliser Array.intersect().

Array.diff()

Cette méthode permet de faire une différence sur des tableaux, elle renvoie un nouveau tableau correspondant aux éléments duthis dont on a retiré les éléments des tableaux passés en paramètre.
<script>
[1, 42, 7, "ok"].diff(["ok", 2, 42, "1"]); // renverra [1, 7]
list.diff(array1, array2, arrayx);
</script>

Pour une union utiliser Array.concat(), pour une exclusion utiliser Array.exclude(), pour une intersection utiliser Array.intersect().

JS : Set

Plume ajoute des fonctionnalités aux ensembles :

Set.filter()

Ajout de la fonction filter() sur les ensembles, permettant de renvoyer un tableau contenant tous les éléments dont le callback revient avec vrai :
La signature reste la même que pour la méthodeArray.filter() :
  1. Callback : la méthode appelée avec en signature :
    1. élément
    2. index
    3. tableau reprenant l'ensemble des éléments
    • renvoie vrai si cet élément doit être retenu
  2. this : permettant de passer si besoin, un autre this au Callback
  • Renvoie un nouveau tableau indexé

Exemple:
<script>
const set = new Set([5, 0, 'a', '', true, false, {}, null, undefined]);
const array = set.filter((element, idx, ensemble) => element);
// renverra [ 5, 'a', true, {} ]
</script>

JS : Date

Plume ajoute des fonctionnalités aux dates :

Date.format()

Cette méthode renvoie sa date sous forme d'une chaîne de caractères formatée lisible.
Elle a en signature :
  • Le format : sous forme de chaîne de caractères pouvant contenir les séquences de substitutions suivantes :
    • %1d: retourne la date du jour sous forme de 1 ou 2 chiffres, pouvant aller de "1" à "31".
    • %2d: retourne la date du jour sous forme de 2 chiffres, pouvant aller de "01" à "31".
    • %d : identique à %2d.
    • %1m: retourne le mois sous forme d'un nombre, pouvant aller de "1" à "12".
    • %2m: retourne le mois sous forme d'un nombre à 2 chiffres, pouvant aller de "01" à "12".
    • %lm: retourne le mois sous sa forme longue, pouvant aller de "janvier" à "décembre".
    • %sm: retourne le mois sous sa forme courte (short), pouvant aller de "jan." à "déc.".
    • %nm: retourne le mois sous sa forme abrégé (narrow), pouvant aller de "J" à "D".
    • %m : identique à %2m.
    • %2y: retourne l'année sous forme de 2 chiffres, pouvant aller de "01" à "99". Attention à l'ambiguïté entre "2001" et "1901".
    • %4y: retourne l'année sous forme de 4 chiffres, pouvant aller de "0000" à "9999".
    • %y : identique à %4y.
    • %1h: retourne l'heure sous forme de 1 ou 2 chiffres, pouvant aller de "0" à "23".
    • %2h: retourne l'heure sous forme de 2 chiffres, pouvant aller de "00" à "23".
    • %h : identique à %2h.
    • %1i: retourne les minutes sous forme de 1 ou 2 chiffres, pouvant aller de "0" à "59".
    • %2i: retourne les minutes sous forme de 2 chiffres, pouvant aller de "00" à "59".
    • %i : identique à %2i.
    • %1e: retourne les secondes sous forme de 1 ou 2 chiffres, pouvant aller de "0" à "59".
    • %2e: retourne les secondes sous forme de 2 chiffres, pouvant aller de "00" à "59".
    • %e : identique à %2s.
    • %lw: retourne le jour de la semaine sous sa forme longue, pouvant aller de "lundi" à "dimanche".
    • %sw: retourne le jour de la semaine sous sa forme courte (short), pouvant aller de "lun." à "dim.".
    • %nw: retourne le jour de la semaine sous sa forme abrégé (narrow), pouvant aller de "L" à "D". Attention à l'ambiguïté entre "Mardi" et "Mercredi".
    • %0w: retourne le jour de la semaine sous forme d'un chiffre, où dimanche = 0, lundi = 1, …
    • %1w: retourne le jour de la semaine sous forme d'un chiffre, où dimanche = 1, lundi = 2, …
    • %7w: retourne le jour de la semaine sous forme d'un chiffre, où dimanche = 7, lundi = 1, …
    • %% : retourne la chaîne de caractère "%".
  • La localisation sous forme d'une chaîne de caractère respectant la balise BCP 47.
  • La sensibilité à la case de la chaîne de formatage, permettant de traiter deux dates avec un même formatage :
    • Si vrai, ne prend en compte uniquement que les séquences de substitution en majuscule.
    • Si faux, ne prend en compte uniquement que les séquences de substitution en minuscule.
    • Siundefined, est insensible au majuscule/minuscule.
Si aucun format n'est passé, elle retourne un résultat respectant l'ISO 8601 identique au résultat de Date.toISOString().

Exemple :
<script>
const d = new Date("2000−02−29T12:30:00");
trace(d.format());
// affichera : Le mardi 29 février 2000 à 12h30.

trace(d.format("Le %lw %d %lm %y à %hh%i."));
// affichera : Le mardi 29 février 2000 à 12h30.

const debut = d.add(−1);
const fin = d.add(+1);
trace(fin.format(debug.format("Depuis le %d/%2m/%4y jusqu'au %D/%2M/%4Y.", undefined, false), undefined, true));
// affichera :
</script>

Date.parse()

Surcharge cette méthode existante, par l'ajout d'un paramètre de formatage, identique à la méthode Date.format() précédente.
La différence dans les séquences de substitutions sont :
  • %d: accepte les jours précédés ou non d'un zéro.
  • %m: identique mais pour les mois.
  • %y: accepte les années sur 2 ou 4 chiffres. Attention, les années sur 2 chiffres seront traduites comme des années 2000.
Sa signature est :
  1. Entrée : Une chaîne de caractère qui sera analysée pour en extraire une date.
  2. Formatage
  3. Date de fin : booléen indiquant si l'on doit rechercher une date de fin
  • Retour : La date au format Date ou false.
Exemple :
<script>
const basic = Date.parse("2000−02−29"); // créera la Date 29/02/2000
const erreur = Date.parse("29−02−2000"); // générera une erreur
const accepte = Date.parse("29−02−2000", "%d−%m−%y"); // créera la Date 29/02/2000
</script>

Date.addDate()

Cette méthode ajoute ou enlève un nombre de jour à la date, et la renvoie.
Exemple :
<script>
const date = new Date(2020, 11, 31, 12);
date.addDate(1);
console.log(date);
date.addDate(−1);
console.log(date);
</script>
Renverra : .

Attention : cette méthode modifie la date utilisée. Si l'ajout doit se faire en renvoyant une nouvelle date sans toucher à la date passée, il faut utiliser sa méthode jumelleDate.AddDate() :
<script>
const date1 = new Date(2020, 0, 1, 12);
const date2 = date1.AddDate(1);
console.log(date1, date2);
</script>
Renverra :

Date.holiday()

Permet d'indiquer si la date correspond à un jour ferié.
Elle renvoie une chaîne de caractère décrivant le nom du jour ferié ou false si jour non-férié.
Il est possible de passer en paramètre la localisation international, qui en son absence, utilisera la localisation du navigateur.
<script>
const jour = new Date('2022−04−17');
const ouvre = jour.holiday('fr−FR'); // renverra false</abbr>
jour.addDate(1);
const ferie = jour.holiday('fr−FR'); // renverra "Lundi de Pâcques"
</script>

JS : Element

JS : Element.$()

Permet de récupérer un élément HTML par rapport à un autre élément HTML.
Voir $() pour plus d'information.

JS : Element.$$()

Permet de récupérer un ensemble d'élément HTML par rapport à un autre élément HTML.
Voir $$() pour plus d'information.

JS : Element.create()

Permet de créer un élément HTML à l'intérieur d'un élément HTML.

Il a en signature, les mêmes paramètres que $.create(), mais en lui ajoutant un troisième de positionnement :
  1. Le type d'élément:
  2. Les informations complémentaires, tel que la classe, les attributs, …
  3. La position où créer ce nouvel élément par rapport à l'élément de base.

Ce troisième paramètre de positionnement à en syntae :
  • si absent ouundefined ounull : ajout en dernière position dans l'élément.
  • si > 0 : ajout à la position indiquée : 1 ➔ 1ère position (comme Element.prepend()), 2 ➔ 2ème position, …. Si le paramètre est supérieur au nombre d'éléments, il sera positionné en dernière position.
  • si <= 0 : ajout à la position indiquée en partant de la fin: 0 ➔ en dernière position (comme Element.append()), -1 ➔ en avant dernière position (donc juste avant le dernier), …. Si le paramètre est inféroeir au nombre d'éléments, il sera positionné en première position.
  • si ==Node ouElement : ajout juste avant cet élément (qui doit être inclus dans l'élément).
  • sifalse : ajout juste avant l'élément, donc en dehors de l'élément, un peu comme Element.before().
  • sitrue : ajout juste après l'élément, donc en dehors de l'élément, un peu comme Element.after().
Positionnements dans un Element.create()


Exemple avec le paramètre de positionnement :
<script>
element.create('tag', {att}); // ajoute en dernier élément si omis (ou correspondant à une valeur fausse)
element.create('tag', {att}, 1); // ajoute en 1er
element.create('tag', {att}, x); // ajoute en xème élément, avec x > 0
element.create('tag', {att}, 0); // ajoute en dernier
element.create('tag', {att}, false); // ajoute juste avant element
element.create('tag', {att}, true); // ajoute juste après element
element.create('tag', {att}, cible); // ajoute avant cible (si cible est faux, cela ajoutera le TAG en dernière position dans element)
</script>
Si besoin d'un 3ème paramètre, sans passer d'attribut, passerfalse ou{} en 2ème paramètre.


JS : Element.att()

Permet de lire ou d'assigner une valeur à un attribut.
Si le 1er paramètre est :
  • Une chaîne de caractère représentant le nom de l'attribut, le 2ème peut être :
    • Aucun paramètre : la méthode renvoie la valeur de cet attribut.
    • Une valeur : la méthode affecte cette nouvelle valeur à cet attribut.
    • Une chaîne vide : la méthode vide l'attribut.
    • null : la méthode supprime l'attribut.
  • Un tableau représentant un ensemble de nom d'attribut, le 2ème reprend les mêmes possibilité que précédement.
  • Une structure {nom: valeur}, chaque valeur est affecté à l'attribut représenté par le nom, et supprimé si la valeur est égal ànull.
  • Un élément HTML, tous ces attributs sont propagés sur l'élément cible.

Exemple:
<script>
var att = element.att('name'); // renvoie la valeur de name
var att = element.att(['name', 'id']); // renvoie un objet { name: "valeur", id: "valeur" }
element.att('name,', "valeur"); // affecter "valeur" à name
element.att({name: "valeur"}); // même chose
element.att(['name', 'class'], "valeur"); // affecte "valeur" aux attributs name, class,
element.att(source); // clône les attributs de source à element
element.att('name', null); // supprime l'attribut name
[tag1, tag2].att(…); // att() appliqué sur plusieurs éléments
</script>
Plusieurs attributs spécifiques virtuels ont été ajoutés :
  • class : représente l'attribut classname
  • innerText : ne correspond pas à un attribut, mais permet de remplacer unelement.innerText = valeur
  • innerHTML : ne correspond pas à un attribut, mais permet de remplacer unelement.innerHTML = valeur
  • style : ne correspond pas à un attribut, mais permet de remplacer unelement.css(valeur)

JS : Element.css()

Cette méthode sert à calculer et renvoyer les styles en se basant sur les feuilles de style.
À ne pas confondre avec la méthode homographe $.css() qui elle sert à gérer (ajout, modification et effacement) des propriétés des feuilles de style.

Sa signature est constitué d'un seul paramètre :
  • Soit une chaîne de caractère représentant la caractéristique du style à calculer.
  • Soit un tableau de chaîne de caractère représentant plusieurs styles, qui sera renvoyé sous forme d'une structure.

Le nommage accepte autant la syntaxe conventionnelle kebab-case (exemple: border-bottom-left-radius) que la syntaxe condensée camelCase (exemple: borderBottomLeftRadius).
En ajoutant un suffixe sur certains styles, la valeur (en lecture seule) sera sous une forme différente :
  • "i" : renvoie sous forme d'un tableau de valeur entière (en supprimant le suffixe 'px') :
    • margini: un tableau [top, right, bottom, left] de valeur entière des tailles des marges.
    • borderi: un tableau [top, right, bottom, left] de valeur entière des tailles des bordures, les couleurs et styles de bordure ne sont pas prises en compte.
    • paddingi: un tableau [top, right, bottom, left] de valeur entière des tailles des paddings.
    • *−topi: une valeur entière, donc arrondi et sans le suffixe "px". Exemple:border−width−topi.
    • *−righti: une valeur entière., donc arrondi et sans le suffixe "px".
    • *−bottomi: une valeur entière, donc arrondi et sans le suffixe "px".
    • *−lefti: une valeur entière, donc arrondi et sans le suffixe "px".
    • *−heighti: une valeur entière, donc arrondi et sans le suffixe "px".
    • *−widthi: une valeur entière, donc arrondi et sans le suffixe "px".
    • *−colori: la couleur sous forme RGB d'un tableau de décimal [r, g, b, a], exemple :border−left−colori qui pourrait donner [255, 192, 203, 0.5] pour du rose semi-transparent.
  • "s" : renvoie sous forme d'une structure de valeur numérique (en supprimant le suffixe 'px') :
    • margins: une structure {top:, right:, bottom:, left: } contenant des valeurs entières des tailles des marges.
    • borders: une structure {top:, right:, bottom:, left: } contenant des valeurs entières des tailles des bordure, les couleurs et styles de bordure ne sont pas prises en compte.
    • paddings: une structure {top:, right:, bottom:, left: } contenant des valeurs entières des tailles des paddings.
    • *−colors: la couleur sous forme d'un object Color contenant la structure RGB {red, green, blue, alpha}, exemple :border−left−colors qui pourrait donner {red: 255, green: 192, blue: 203, alpha: 0.5} pour du rose semi-transparent.
  • "w" : renvoie la somme des largeurs :
    • marginw: la valeur entière de margin-left + margin-right, utilisable uniquement en lecture.
    • borderw: la valeur entière de border-left + border-right, utilisable uniquement en lecture.
    • paddingw: la valeur entière de padding-left + padding-right, utilisable uniquement en lecture.
  • "h" : renvoie la somme des hauteurs :
    • marginh: la valeur entière de margin-top + margin-bottom, utilisable uniquement en lecture.
    • borderh: la valeur entière de border-top + border-bottom, utilisable uniquement en lecture.
    • paddingh: la valeur entière de padding-top + padding-bottom, utilisable uniquement en lecture.
  • "h" et "x" : propre aux couleurs :
    • *−colorh: la couleur sous forme d'un object Color contenant la structure HSL {hue, saturation, lightness, alpha}, exemple :border−left−colorh qui pourrait donner {hue: 350, saturation: 1, lightness: 0.8, alpha: 0.5} pour du rose semi-transparent.
    • *−colorx: la couleur sous forme RGB en hexadécimal "rrggbbaa", exemple :border−left−colorx qui pourrait donner "ffc0cb80" pour du rose semi-transparent.

Exemples :
<script>
var css = element.css("border"); // renverrait (par exemple) la chaîne "6px 12px 6px 12px"
var css = element.css("borderi"); // renverrait plutôt un tableau de valeur numérique [6, 12, 6, 12]
var css = element.css(["border", "margin"]); // renverrait (par exemple) une structure de type {border: "6px 12px 6px 12px", margin: "10px 0px 10px 0px"}
var css = element.css("padding−top"); // renverrait (par exemple) la chaîne "8px"
var css = element.css("paddingTopi"); // renverrait plutôt la valeur numérique 8
var css = element.css("borderh"); // renverrait une valeur numérique correspondant à borderTop + borderBottom
var css = element.css("marginw"); // renverrait une valeur numérique correspondant à marginLeft + marginRight
var css = element.css("backgroundColor"); // renverrait (par exemple) la chaîne "rgb(1, 128, 255)"
var css = element.css("backgroundColorx"); // renverrait plutôt la chaîne 0180ff00
</script>

Il est possible d'utiliser sur le résultat des 3 attributscolori,colors etcolorh, la méthodetoString() pour renvoyer une valeur utilisable en CSS et la méthodecopy() pour dupliquer la sructure :
<script>
const hsl = element.css('backgroundColorh');
hsl.l *= 0.8;
element.backgroundColor = hsl.toString();
// ou directement
element.backgroundColor = hsl;
// ou si besoin de modifier le CSS
element.css('background−color', hsl);

const par_defaut = element1.css('colors');
let couleur = element2.css('color');
if (! couleur.a) // si transparent ou couleur non défini
couleur = par_defaut.copy();
</script>

JS : Element.setPos()

Permet de positionner (avecstyle.left etstyle.top) l'élément.
<script>
tag.setPos(point); // positionnement
tag.setPos(rect); // positionnement par rapport au point rect.topLeft
tag.setSize(point); // dimensionne
tag.setSize(rect); // dimensionne en utilisant la taille du rect
tag.setRect(rect); // positionnement et dimensionne
</script>
Cette méthode utilise des notions de Point et de Rect.

JS : Element.setSize()

Permet de dimensionner (avecstyle.width etstyle.height) l'élément.
<script>
tag.setPos(point); // positionnement
tag.setPos(rect); // positionnement par rapport au point rect.topLeft
tag.setSize(point); // dimensionne
tag.setSize(rect); // dimensionne en utilisant la taille du rect
tag.setRect(rect); // positionnement et dimensionne
</script>
Cette méthode utilise des notions de Point et de Rect.

JS : Element.setRect()

Permet de positionner (avecstyle.left etstyle.top) et dimensionner (avecstyle.width etstyle.height) l'élément.
<script>
tag.setPos(point); // positionnement
tag.setPos(rect); // positionnement par rapport au point rect.topLeft
tag.setSize(point); // dimensionne
tag.setSize(rect); // dimensionne en utilisant la taille du rect
tag.setRect(rect); // positionnement et dimensionne
</script>
Cette méthode utilise des notions de Point et de Rect.

JS : Element.bound

bound est un objetBound (en lecture seul) héritant deRect, permettant d'accéder aux différentes dimensions d'un élément HTML :inner,padding,border etmargin.
En plus duRect, il contient les attributs suivants :
  • inner(): méthode renvoyant les dimensions interne de l'élément.
  • padding(): méthode renvoyant les dimensions avec l'espacement autour de l'élément.
  • border(): méthode renvoyant les dimensions de la bordure.
  • margin(): méthode renvoyant les dimensions externe de l'élément.
  • paddings: structure {top, left, bottom, right} des largeurs et hauteurs des paddings de l'élément
  • borders: même structure mais pour les bordures.
  • margins: même structure mais pour les marges.
    <script>
    const bound = element.bound; // renvoie un Bound (donc un Rect) contenant les dimensions absolues de la bordure
    const point = element.bound.topLeft; // renvoie la position haut−gauche de la bordure
    const gauche = element.bound.topLeft.x; // renvoie la position gauche de la bordure
    const haute = bound.paddings.top + bound.borders.top + bound.margins.top; // renvoie la hauteur des bords hauts de l'élément
    element.bound.add(100, 200).setPos(); // permet d'avancer l'élément (si son display est différent de static)
    </script>
Voir le chapitre sur Rect() pour une utilisation plus poussée de ses méthodes.

Note : Cette méthode est en fait un attribut en lecture seule, qui génère à chaque fois un nouveauBound. Ce qui veut dire qu'il est préférable de le conserver dans une variable à part, s'il y a plusieurs manipulation à faire dessus. Inversement, il est possible d'appliquer une fonction d'auto-modification (`.add()`, …) dessus sans perturber le bound de l'élément :
<script>
const element = $('element');
// il est préférable de faire :
const sprite = element.bound;
const decalage = sprite.topLeft.Add(50, 50);
const cadre = $('MAIN').bound.enclose(sprite);
// plutôt que :
const decalage = element.bound.topLeft.Add(50, 50);
const cadre = $('MAIN').bound.enclose(element.bound);

// et il est inutile de faire :
const point = element.bound.Add(50, 50);
// aucun problème et plus rapide d'utiliser une auto−modification dessus
const point = element.bound.add(50, 50);
</script>

Par défaut, cebound contient les coordonnées duborder, mais peut être modifiée par les méthodes suivantes.

bound.inner()

Cette méthode renvoie le mêmeBound, mais contenant la position absolue intérieur de l'élément. Il modifie donc le "bound" d'origine.
Sa signature est :
  • conteneur : paramètre optionnel pouvant être un élément parent, ou son chemin (sous forme de chaîne de caractère) ou unRect ou unPoint pour renvoyer sa dimension par rapport à cet élément. Même principe quebound.inner().sub(conteneur).
  • scroll : paramètre optionnel booléan indiquant si la position doit renvoyer sa position décalée éventuellement par son scroll et sa taille réelle (et non visible).

Une méthode "synonyme"bound.Inner() (la première lettre en majuscule) permet de renvoyer un objetRect qui est une copie du "bound", donc qui ne modifie pas le "bound" d'origine.
<script>
let inner = element.bound.inner(); // renvoie element.getBoundingClientRect() + border + padding
let inner = element.bound.inner(true); // renvoie le même rectangle − element.scrollLeft & Top de taille element.scrollWidth & Height
let inner = element.bound.inner(element.offsetParent); // renvoie la distance jusqu'à cet élément parent
let inner = element.bound.inner(new Point(10, 10)); // renvoie le même rectangle − 10 pixels.

const bound = element.bound;
trace("est identique", bound.equal(bound.margin())); // bound passe des dimensions des bordures aux dimensions des marges
trace("est différent", ! bound.equal(bound.Inner())); // le "I" majuscule de Inner, protège le bound qui reste aux dimensions précédentes de margin
</script>

bound.border()

Cette méthode est identique à bound.inner() mais renvoie unBound contenant la position absolue des bordures de l'élément.
Une lecture de l'attributElement.bound renvoie par défaut les mêmes dimensions queElement.bound.border(), il est donc pas utile d'utiliser cette méthode, sauf pour les raisons suivantes :
  • Lisibilité: unbound.border() est certainement plus compréhensible qu'un simplebound.
  • Paramètrage (scroll et/ou conteneur): qui n'est pas possible de passer dans une simple lectureElement.bound.
  • Réinitialisation: si l'on souhaite relire les dimensions des bordures après utilisation d'une de ces méthodes. Exemple :

Exemple:
<script>
const bound = element.bound.margin();
trace('marge', bound);
bound.border();
trace('bordure', bound);
</script>

bound.margin()

Cette méthode est identique à bound.inner() mais renvoie unBound contenant la position absolue extérieur de l'élément.

bound.setPos()

Comme pour Element.setPos(), cette méthode permettent de repositionner l'élément dubound.
<script>
element.bound.add(100, 200).setPos(); // permet d'avancer l'élément (si son display est différent de static)
</script>

bound.setRect()

Comme pour Element.setRect(), cette méthode permette de redimensionner l'élément dubound.
<script>
element.bound.scale(10, 10, −10, −10).setRect(); // Permet de réduire de 10 pixels cet élément
</script>

JS : Element.on()

Appel une fonction lorsqu'un évenement se déclenche sur un élément HTML. elle reprend le principe du EventTarget.addEventListener() en lui ajoutant une souplesse et fonctionalités supplémentaires.
On peut associer un ou plusieurs événements à un ou plusieurs éléments HTML.
Sa signature est :
  • Événements : sous forme d'une chaîne séparé par un ou plusieurs caractères non-alphabétique (espace, tabulation, virgule, …) ou sous forme d'un tableau de chaîne, correspondant au nom des évements (mousedown, keyup, …) et/ou aux mot-clés suivants :
    • catch: si spécifié dans une liste d'évenement passé en paramètre, tous les événements qui le suivent seront en plus associés àwindow, permettant de gérer simplement des mécanismes de "message" (voir plus bas pour l'exemple).
    • once: si spécifié, la fonction déclencheur sera exécutée une fois en début.
  • Déclencheur : méthode qui sera appelée, avec en signature :
    • évenement : l'objet de type Event
    • cible : l'élément HTML cible (qui peut être différent de l'élément déclencheur, voir l'exemple plus bas).
    • complement : le complément éventuellement passé à la méthode on()
    • Le this est positionné sur l'élément HTML déclencheur (voir l'exemple plus bas).
  • Complément : optionnel, qui sera passé en 3ème paramètre de la méthode déclencheur
Il est possible d'appliquer l'évenement sur une balise HTML ou sur un tableau contenant des balises HTML.
Cette méthode renvoie le this, permettant de chaîner plusieurs méthodes sur ce même élément.
Si la fonction déclencheur renvoiefalse (sous forme d'un booléen et non une valeur fausse), l'évenement s'arrête et les éventuels autres fonctions déclencheurs ne seront pas appelées.
<script>
// rattache 2 évènements
element.on("mousedown, mouseup", ma_fonction):
element.on(['click', 'keypress'], listen);

// rattache sur plusieurs élements
$$('DIV').on('mouseleave', ma_fonction);

// passage d'un complement de données
element.on('keydown', fonction(event, tag, complement) {

}, complement);

// définition d'un déclencheur avec un 1er appel
element.on(['once', 'keydown'], fonction(event, tag) {

});

// différence entre this et tag
$('UL').on('mouseover', fonction(event, tag) {
// le tag sera égale à un des <LI> survolé de cet <UL>
// alors que le this sera égale à <UL>
});
// attention au méthode fléchée qui ne peuvent avoir leur propre this</abbr>
class UnObjet {
append(input) {
input.on('mousedown', function(event, tag) {
// le this correspond à l'input (et le tag également puisqu'un input ne peut pas contenir d'autre balise)
});
input.on('mouseup', (event, tag) => {
// le this correspond à l'instance d'objet UnObjet (et le tag toujours à l'input)
});
}
}
new UnObjet()−>append($('INPUT'));

// chainage des évenements
window.on('keydown', function() {

}).on('keypress', function() {

}).on('keyup', function() {

});

// arrête la propagation de l'évenement
const list = $('UL');
const items = list.$$('LI');
items.on('click', function() {
trace("Déclenchera bien cette première méthode sur le <LI>.");
return false;
});
items.on('click', function() {
trace("Ne déclenchera pas cette 2ème méthode, à cause du false précédent.");
});
list.on('click', function() {
trace("Ne déclenchera pas non plus cette méthode sur <UL>, toujours à cause du false.");
});

// utilisation du catch</abbr>
$('DIV').on('mousedown', 'catch', 'mousemove', 'mouseup', function(event, tag) => {
switch (event) {
case 'mousedown': // cet évenement est antérieur au catch et n'est déclenché que lors de l'appuie sur le DIV
this.style.outline = '5px double yellow';
this.$moved = true;
break;
case 'mousemove': // sans le catch, cet évenement ne pourrait avoir lieu si la souris était en dehors du DIV lors de son déplacement
if (this.$moved) {
this.setPos(new Point(event).sub(this.offsetParent));
}
break;
case 'mouseup': // sans le catch, cet évenement ne pourrait avoir lieu si la souris était en dehors du DIV lors de son relâchement
if (this.$moved) {
this.style.outline = '';
this.$moved = false;
}
break;
}
});
</script>
Note : le dernier évenementevent est toujours stocké dans la variable globale $.occurrence.

JS : Element.off()

Permet de détacher un évenement, qui aurait été ajouté par la méthode Element.on() :
Doit reprendre uniquement que les deux premiers paramètres : Événements et Déclencheur.
<script>
// ajoute
$('main').on("mousedown", trigger);

// retire
$('main').off("mousedown", trigger);
</script>

JS : Element.closest()

Complémentaire à $('^'), cette fonction étend la fonction native Element.closest() en permettant de rechercher le premier parent de l'élément répondant à un critère passé en paramètre, avec en signature :
  1. Recherche : représentant le critère de recherche, sous forme d'une valeur ou d'un tableau, de type :
    • Chaîne de caractère : représentant un sélecteur CSS de syntaxenom #id .class @name ?type [attribut].
    • Fonction : est une méthode qui sera appelée récursivement sur chaque parents, avec en signature :
      1. le parent.
      2. le niveau de profondeur, partant de 0 (ou 1 si options.exclude est vrai).
      • et renvoyant vrai si trouvé.
    • Element HTML : est l'élément à rechercher.
    • Valeur numérique : le nombre de parent à remonter.
  2. Limite : paramètre optionnel, représentant la limite, sous forme d'une valeur ou d'un tableau, de type identique au 1er paramètre de recherche.
  3. Options : booléan ou structure d'options :
    • Booléan d'exclusion : précisant si vrai, qu'il ne faut pas tenir compte de l'élémentthis dans la liste des éléments possibles.
    • exclude: reprenant la valeur du booléan d'exclusion.
    • searchs: indiquant si vrai que dans le cas d'une recherche par tableau, l'ensemble des éléments doit être vrai. Par défaut, un seul élément trouvé suffit.
    • limits: indiquant si vrai que dans le cas d'une limite par tableau, l'ensemble des éléments doit être vrai. Par défaut, une seule limite suffit.
    • value: indiquant si vrai que la valeur renvoyée sera la valeur retournée par la fonction, par défaut renverra l'élément HTML parent.
  • Le retour est soit l'élément HTML parent répondant au critère(s) (ou la valeur si options.value) soitundefined.

Le 1er et 2ème paramètre sont identiques en fonctionnalité, la différence est que la recherche renvoie l'élément et arrête de remonter, alors que la limite ne fait qu'arrêter la recherche.
La recherche s'effectue avant la limite.
Pour le 1er paramètre "recherche" ou 2ème paramètre "limite", il est possible de passer plusieurs critères sous la forme d'un tableau reprenant plusieurs types de critère, la recherche aboutissant que si au moins un élément est vrai ou si options.searchs (ou options.limits) l'ensemble de ces critères est vrai.
Dans le cas où le 2ème paramètre "limite" n'est pas utile ni passé, et que l'on a besoin d'options ou d'une exclusion, on peu passer une valeur fausse ou rabattre les options en 2ème paramètre.
S'il faut renvoyer tous les parents répondant à la recherche, il faut employer la méthode Element.closests().

Sauf indication contraire, l'ensemble desclosest dans cet exemple, renverront la balise<div class='grand−pere'> :
<html>
<body>
<main>
<div class='grand−pere'>
<div class='parent'>
<span id='enfant'>départ</span>
</div>
</div>
</main>
</body>
</html>
<script>
const enfant = $('enfant');
let papy;
papy = enfant.closest('div.grand−pere'); // identique à enfant.$('^=div.grand−pere')
papy = enfant.closest((tag, deep) => tag.tagName == 'DIV' && tag.classname == 'grand−pere'); // identique
papy = enfant.closest(2); // identique à enfant.$('^2')
if (enfant.closest(papy)) trace('hérite'); // permettant de voir si ce papy est un des parents de enfant
papy = enfant.closest(['div.grand−pere', 3]); // recherche grand−pere ou (au pire) renvoie le 3ème parent
papy = enfant.closest([3, 'div.grand−pere']); // recherche le 3ème parent ou (au mieux) grand−pere
papy = enfant.closest('div.grand−pere', 2); // recherche grand−pere dans les 2 parents les plus proches
papy = enfant.closest('div.grand−pere', 1); // renvoie undefined puisque papy est situé au niveau 2
papy = enfant.closest(3, 'div.grand−pere'); // renvoie undefined, car rencontrera la limite du grand−pere avant d'accéder au 3ème parent
papy = enfant.closest('div.grand−pere', [2, 'main']); // recherche le grand−pere jusqu'au 1er main trouvé ou dans les 2 parents les plus proches
papy = papy.closest('div.grand−pere', true); // renvoie undefined car malgré le fait que le this soit le grand−pere, il est exclus de la recherche
papy = papy.closest('div.grand−pere', { // idem
exclude: true,
searchs: false,
limits: false,
value: false,
});
papy = enfant.closest(tag => tag.classname == 'grand−pere' && tag.tagName, {value: true}); // renverra "DIV"
</script>

JS : Element.closests()

Cette méthode est une extension de la méthode Element.closest(), et permet de rechercher les parents de l'élément répondant à un critère passé en paramètre, avec en signature :
  1. Recherche : représentant le critère de recherche, sous forme d'une valeur ou d'un tableau, de type :
    • Chaîne de caractère : représentant un sélecteur CSS de syntaxenom #id .class @name ?type [attribut].
    • Fonction : est une méthode qui sera appelée récursivement sur chaque parents, avec en signature :
      1. le parent.
      2. le niveau de profondeur, partant de 0 (ou 1 si options.exclude est vrai).
      • et renvoyant vrai si trouvé.
    • Element HTML : est l'élément à rechercher.
    • Valeur numérique : le nombre de parent à remonter.
    • Boolean àfalse : indiquant qu'il faut tout renvoyer (aucune limite).
  2. Limite : paramètre optionnel, représentant la limite, sous forme d'une valeur ou d'un tableau, de type identique au 1er paramètre de recherche.
  3. Options : booléan ou structure d'options :
    • Booléan d'exclusion : précisant si vrai, qu'il ne faut pas tenir compte de l'élémentthis dans la liste des éléments possibles.
    • exclude: reprenant la valeur du booléan d'exclusion.
    • searchs: indiquant si vrai que dans le cas d'une recherche par tableau, l'ensemble des éléments doit être vrai. Par défaut, un seul élément trouvé suffit.
    • limits: indiquant si vrai que dans le cas d'une limite par tableau, l'ensemble des éléments doit être vrai. Par défaut, une seule limite suffit.
    • value: indiquant si vrai que la valeur renvoyée sera la valeur retournée par la fonction, par défaut renverra l'élément HTML parent.
  • Le retour est un tableau contenant tous les parents (ou les valeurs si options.value) répondant aux critères, ou un tableau vide si aucun parent n'est trouvé.

Le 1er et 2ème paramètre sont identiques en fonctionnalité, la différence est que la recherche ajout l'élément au tableau et arrête de remonter, alors que la limite ne fait qu'arrêter la recherche.
Pour le 1er paramètre "recherche" ou 2ème paramètre "limite", il est possible de passer plusieurs critères sous la forme d'un tableau reprenant plusieurs types de critère, la recherche aboutissant que si Dans le cas où le 2ème paramètre "limite" n'est pas utile ni passé, et que l'on a besoin d'options ou d'une exclusion, il faut passer faux comme valeur de limite (aucune limite).
Dans le cas où le 2ème paramètre "limite" n'est pas utile ni passé, et que l'on a besoin d'options ou d'une exclusion, on peu passer une valeur fausse ou rabattre les options en 2ème paramètre.
S'il faut renvoyer que le 1er parent trouvé, il faut employer la méthode Element.closest().

Sauf indication contraire, l'ensemble desclosests dans cet exemple, renverront un tableau contenant la balise<div class='grand−pere'> :
<html>
<body>
<main>
<div class='grand−pere'>
<div class='parent'>
<span id='enfant'>départ</span>
</div>
</div>
</main>
</body>
</html>
<script>
const enfant = $('enfant');
let famille;
famille = enfant.closests('div.grand−pere'); // identique à [ enfant.$('^=div.grand−pere') ]
famille = enfant.closests(2); // identique à [ enfant.$('^2') ]
famille = enfant.closests((tag, deep) => tag.tagName == 'DIV' && tag.classname == 'grand−pere'); // identique
if (enfant.closests(famille[0]).length) trace('herite'); // permettant de voir si grandParent est parent de enfant
famille = enfant.closests(['main', 1]); // renvoie [ enfant, parent, main ]
famille = enfant.closests([1, 'main'], true); // renvoie [ parent, main ]
famille = enfant.closests('div.grand−pere', 3); // recherche grand−pere dans les 3 parents les plus proches
famille = enfant.closests(3, 'div.grand−pere'); // renvoie [], car rencontrera la limite du grand−pere avant le 3ème parent
famille = enfant.closests('div.grand−pere', [3, 'main']); // recherche le div jusqu'au 1er main trouvé ou dans les 3 parents les plus proches ou le main
famille = enfant.closests('span', true); // renvoie [], ne renvoie pas le this, grâce à l'exclusion, malgré qu'il répond au critère de recherche.
const tout = enfant.closests(true); // renvoie tous les parents [ span, parent, grand−pere, main, body, html ]
</script>

JS : Element.publisher()

Cette méthode permet de rendre éditable un élément HTML, et a en signature :
  1. Une suite de paramètres optionnels :
    1. start : valeur numérique positif optionnelle, du début de la sélection. Par défaut = 0.
    2. end : valeur numérique optionnelle et supérieur ou égale à Début, de la fin de la sélection. Par défaut = la longueur du nombre de caractère.
    3. filter: scalaire ou tableau de typeRegExp permettant de définir la syntaxe acceptée.
  2. Options : une structure contenant les paramètres suivants :
    • start: même comportement que le paramètre homonyme
    • end: même comportement que le paramètre homonyme
    • filter: même comportement que le paramètre homonyme
    • default: indique la valeur qui remplace une chaîne vide
  • Elle renvoie une promesse qui sera exécutée lorsque l'utilisateur validera sa saisie.
La promesse passe l'élément édité, qui contient un attributEvent event qui a provoqué l'appel de cette promesse (Enter,Blur, …).
Sistart etend ne sont pas utilisé, il est possible de passer directementfilter en seul paramètre.
<html>
<div id="edit"></div>
</html>
<script>
$('edit').publisher().then(element => {
trace('saisie', element.innerText);
trace('sortie', element.event.type);
});

// exemple n'acceptant que des valeurs numériques avec virgule optionnelle.
return $('edit').publisher({filter: /^−?d+(?:,d*)?$});
</script>

JS : Elements

Plume ajoute des fonctionnalités aux collections HTML :

Elements.concat()

Ajout de la fonction concat() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer un tableau contenant tous les tableaux (ou similaire) passés en paramètre :
<script>
const tag = $('MAIN');
const divs = document.body.children.concat([tag], tag.children);
</script>

Elements.deep()

Similaire de la fonction Elements.flat() mais à la différence de cette dernière, elle ne renverra que les éléments du niveau de profondeur indiqué, et donc en omettant de prendre les parents intermédiaires :
Le niveau 0 correspond à une copie deelement.children sous forme d'un tableau.
Si aucun paramètre n'est passé, il renverra les éléments de niveau 1.
<html>
<body>
<article>
<section>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</section>
<section>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</section>
</article>
<article>
aucune information
</article>
<article>
<section>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</section>
<section>
aucune information
</section>
</article>
</body>
</html>
<script>
const divs = document.body.children.deep(0); // renverra un tableau contenant uniquement les 3 <article> = à document.body.children
const divs = document.body.children.deep(1); // renverra un tableau contenant uniquement les 4 <section>
const divs = document.body.children.deep(2); // renverra un tableau contenant uniquement les 5 <ul>
const divs = document.body.children.deep(3); // renverra un tableau contenant uniquement les 15 <li>
const divs = document.body.children.deep(); // identique à deep(1)
</script>

Elements.every()

Ajout de la fonction every() sur les listes d'éléments, tel queNodeList,HTMLCollection, itérerant sur chaque élément jusqu'au 1er callback renvoyant faux, pour retournerfalse, sinon renvoietrue:
<script>
const allOK = document.body.children.every(tag => tag.classList.includes('ok'));
</script>

Elements.filter()

Ajout de la fonction filter() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer un tableau contenant tous les éléments dont le callback revient avec vrai :
<script>
const divs = document.body.children.filter(tag => tag.tagName == 'DIV');
</script>

Elements.find()

Ajout de la fonction find() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer le premier élément dont le callback revient avec vrai :
<script>
const divs = document.body.children.find(tag => tag.tagName == 'DIV');
</script>

Elements.findIndex()

Ajout de la fonction findIndex() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer l'index (commençant à 0) du premier élément dont le callback revient avec vrai, ou -1 si non trouvé :
<script>
const index = document.body.children.findIndex(tag => tag.tagName == 'DIV');
</script>

Elements.flat()

Ajout de la fonction flat() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer l'ensemble des éléments jusqu'au niveau de profondeur indiqué :
<html>
<body>
<article>
<section>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</section>
<section>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</section>
</article>
<article>
aucune information
</article>
<article>
<section>
<ul>
<li></li>
<li></li>
<li></li>
</ul>
</section>
<section>
aucune information
</section>
</article>
</body>
</html>
<script>
const divs = document.body.children.flat(0); // renverra un tableau contenant tous les "article"
const divs = document.body.children.flat(1); // renverra un tableau contenant tous les "article", et "section"
const divs = document.body.children.flat(100); // renverra un tableau contenant tous les "article", "section", "ul", et "li"
</script>

Elements.flatMap()

Ajout de la fonction flatMap() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer un tableau contenant tous les sous-éléments renvoyer par callback :
<script>
const divs = document.body.children.flatMap(tag => tag.clasName);
</script>

Elements.forEach()

Ajout de la fonction forEach() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant d'itérer sur tous les éléments :
<script>
document.body.children.forEach(tag => tag.private = 42);
</script>

Elements.map()

Ajout de la fonction map() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer un tableau contenant tous les éléments renvoyé par le callback :
<script>
const clasName = document.body.children.map(tag => tag.clasName);
</script>

Elements.reduce()

Ajout de la méthode reduce() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer une valeur retourné par itération sur le callback :
<script>
const large = document.body.children.reduce((large, tag) => Math.max(tag.offsetWidth, large), 0);
</script>

Elements.slice()

Ajout de la fonction slice() sur les listes d'éléments, tel queNodeList,HTMLCollection, permettant de renvoyer un extrait du tableau :
<script>
const copy = document.body.children.slice(); // copie superficiel des éléments
const start = document.body.children.slice(1); // extrait à partir du 2ème élément jusqu'au dernier
const last = document.body.children.slice(−2); // extrait à partir de l'avant dernier jusqu'au dernier
const middle = document.body.children.slice(3, 5); // extrait à partir du 4ème élément jusqu'au 5ème
const extract = document.body.children.slice(3, −2); // extrait à partir du 4ème élément jusqu'à l'avant dernier
</script>

Elements.some()

Ajout de la fonction some() sur les listes d'éléments, tel queNodeList,HTMLCollection, itérerant sur chaque élément jusqu'au 1er callback renvoyant vrai, pour retournertrue, sinon renvoiefalse:
<script>
const onceOK = document.body.children.some(tag => tag.classList.includes('ok'));
</script>

Elements.classList

Ajout de la structureclassList permettant l'appel des méthodes rattachés à l'objet ClassList, et reprenant les méthodes suivantes, sur les listes d'éléments, tel queNodeList etHTMLCollection :
Avec les variantes suivantes : À noter que cette structure est également rattachée sous forme d'une méthode àArray.classList(), permettant aux tableaux, d'appliquer ces méthodes aux valeurs de typeElement.
<script>
document.body.children.classList.add('actif');
const elements_actifs = document.body.children.classList.contains('actif');
const elements_actifs = document.body.children.classList.entries('actif');
const elements_actifs = document.body.children.classList.forEach('actif');
const elements_actifs = document.body.children.classList.item('actif');
document.body.children.classList.remove('actif');
const elements_actifs = document.body.children.classList.replace('actif');
const elements_actifs = document.body.children.classList.supports('actif');
const elements_actifs = document.body.children.classList.toggle('actif');
const elements_actifs = document.body.children.classList.values('actif');
const elements_actifs = document.body.children.classList.filter('actif');
</script>

JS : window

Plume ajoute des fonctionnalités à la variable globalewindow :

window.location.args

Renvoie un tableau en lecture seul, contenant la liste des variables passées au GET et en attribut, le couple variable = valeur.
Exemple avec une url de type https://www.d-nada.com/rubrique/action/index.php?nom=Dupon&prenom=André,window.location.args renverra :
<script>
{
0: "nom",
1: "prenom",
length: 2,
nom: "Dupon",
prenom: "André"
}

// affichera dans la console : "Nom renseigné"
trace('Nom', window.location.args.nom ? "renseigné" : "non−renseigné");

// affichera dans la console : "nom Dupon", "prenom André"
window.location.args.forEach(arg => trace(arg, location.args[arg]));
</script>

window.location.folders()

Est une méthode renvoyant un tableau indexé contenant la liste des dossiers de l'URL.
Exemple avec une url de type https://www.d-nada.com/rubrique/action/index.php?nom=Dupon&prenom=André,window.location.folders renverra :
<script>
[ 'rubrique', 'action' ]
</script>

window.navigator.name

Renvoie une chaîne contenant le nom du navigateur dans l'énuméré suivant : chrome, firefox, opera, safari, samsung, …
Exemple avec votre navigateurwindow.navigator.name renvoie :

window.navigator.mobile

Renvoie un booléen indiquant si le navigateur est sur un smartphone ou tablette.
Exemple avec votre navigateurwindow.navigator.mobile renvoie :

window.navigator.version

Renvoie la version
Exemple avec votre navigateurwindow.navigator.version renvoie :

window.navigator.scheme

Renvoie le mode sombredark oulight (ouundefined si le mode d'affichage n'est pas supporté par le navigateur ou systèe d'exploitation).
Exemple avec votre navigateurwindow.navigator.scheme renvoie :

JS : RPC (Ajax)

Plume intégre des mécanismes de RPC, permettant de communiquer facilement et en asynchrone entre le navigateur et le serveur.

RPC : Fonctions

Il est possible d'appeler en asynchrone des fonctions PHP présentent sur les serveurs, depuis le JS.
Fonctions PHP autorisées par le développeur, avec passage de paramètres (passés par valeur) et retour d'une valeur pouvant être primaire, tableau ou structure.
Pour cela, la fonction doit être appelée côté JS à travers la classe$.ajax, et cette fonction doit être préfixée parajax_ côté PHP.
Le retour de donnée se fait par unreturn de valeur côté PHP, qui sera transmis à la fonction sous forme d'une promesse de JS.
Il est donc inutile, voir proscrit de faire desecho côté PHP.
Exemple avec la fonction "ma_methode", avec côté client JS :
<script>
$.ajax.ma_methode(premier, deuxieme, etc).then(function(resultat) {
alert("Valeur de retour correct :", Array.isArray(resultat.ok) && resultat.ok.length == 1 && resultat.ok[0] === 42);
});
</script>
Et côté serveur PHP :
<script>
Template::ajax();

function ajax_ma_methode($premier = 'default', $deuxieme = false, $etc = null) {
return [ 'ok' => [42] ];
}
</script>
Pour que les fonctions "ajax" soient opérationnelles, il est important de placer la fonction Template::ajax() en début de programme PHP.

RPC : Fichier

Il est possible de lui faire passer un ou plusieurs fichiers (via un<input type="file">) :
<script>
// le choix d'une nouvelle image faite par l'utilisateur déclenche cette méthode
$$('INPUT?file').on('change', function() {
if (! this.files.length)
return;
// on récupère la 1ère image et on vérifie sa taille
let file = this.files[0];
if (file.size > 100*1024*1024)
window.alert("Fichier trop volumineux.");
else {
// on transmet ce fichier au serveur
$.ajax.download(42, file).then(function(resultat) {
if (resultat === true)
window.alert("Téléchargement réussi")
else<br> window.alert("Problème lors du téléchargement: " + resultat);
});
}
});
</script>
<?php
// on récupère le nouveau fichier sur le serveur
function ajax_download($quarante_deux) {
$files = $_FILES['files'];
if (! $files || count($files["tmp_name"]) == 0)
return "Fichier manquant.";
foreach ($files["tmp_name"] as $idx => $tmp) {
$name = $files["name"][$idx];
$type = $files["type"][$idx];
$size = $files["size"][$idx] / (1024*1024);
if (! preg_match('{^text/(w+)$}', $type, $type))
return "Type '$type' du fichier '$name' non pris en charge.";
if ($size > 100)
return "Taille ({$size}Mo) du fichier '$name' trop volumineux.";

if (! $file = @fopen($files["tmp_name"][$idx], 'r'))
return "Ouverture du fichier '$name' impossible.";
// fread…
fclose($file);

// ou
$name = "/image/" . preg_replace(['/^W+|W+$/', '/[^w .−]+/'], ['', '_'], $files['name'][$idx]);
if (@move_uploaded_file($files["tmp_name"][$idx], $_SERVER['DOCUMENT_ROOT'] . $name))
// …
}
return true;
}
?>

RPC : Formulaire

Il est possible de passer en paramètre un<form> qui sera utilisé par le serveur :
<html>
<form method="post" enctype="multipart/form−data">
<input name="f[secret]" type="hidden">
<input name="f[name]">
<input name="f[photo]" type="file">
<input type="submit" value="Enregistrer">
</form>
</html>
<script>
$.ajax.formulaire( new FormData( $('FORM') ) ).then(message => $.message.notice(message || "Erreur d'accès au serveur"));
</script>
<?php
function ajax_formulaire($formData) {
$form = $formData['f'];

$file = $_FILES['f[photo]'];
$path = date("ymd−His−");
$form['photo'] = "/photos/" . date("ymd−His−") . rsha1_file($path) . '_' . reg_replace('{[^w−.]+}', '_', basename($path));
@move_uploaded_file($path, $_SERVER['DOCUMENT_ROOT'] . $form['photo']);

$base = new Base('BDD');
$base−>set($form);
$base−>insert('user');
$base−>close();

return "Enregistrement réussi";
}
?>

RPC : Cookie

Plume intègre un mécanisme de cookie propre aux échanges RPC.
À la différence des cookies du navigateur, ces cookies ont une portée uniquement au niveau de la fenêtre. En d'autre terme, ces cookies sont différents d'une fenêtre à une autre, même si ces fenêtres utilient la même URL.
L'utilisation se fait :
  • Côté JS avec$.ajax_cookie : tout ce que cette structure contient, sera communiqué à chaque fois aux fonctions PHP.
  • Côté PHP avecglobal $PLUME_COOKIE : tout ce qui sera modifié dans cette global, sera répercuté côté JS.

Exemple :
<script>
$.ajax_cookie = {context: 1};
$.ajax.php1().then(() => trace('js1=', $.ajax_cookie.context)); // affichera js1=2
$.ajax.php2().then(() => trace('js2=', $.ajax_cookie.context)); // affichera js2=3
</script>
<?php
function ajax_php1() {
global $PLUME_COOKIE;
trace('PHP1=', $PLUME_COOKIE['context]); // affichera PHP1=1
$PLUME_COOKIE[
'context] = 2;
}
function ajax_php2() {
global $PLUME_COOKIE;
trace('PHP2=', $PLUME_COOKIE['context]); // affichera PHP2=2
$PLUME_COOKIE[
'context] = 3;
}
?>
Cet exemple affichera sur la console : PHP1=1, js1=2, PHP2=2, js2=3

Pour aller plus loin, il est possible de placer une méthode dans$.ajax_cookie, dans ce cas, cette méthode pourra intervenir à chaque échange, et sera appelée :
  • Lors de l'appel d'un ajax et devra renvoyer les données qui seront transmis au serveur.
  • Lors du retour d'un appel ajax, avec en paramètre les données revenant du serveur.
Exemple :
<script>
$.start(() => {
let my_cookie =
$.ajax_cookie = cookies => {
if (arguments.length) {
my_cookie = cookies || {};
my_cookie.when = new Date(); // je rajoute des infos supplémentaires
}
return my_cookie;
};
});
</script>

RPC : Options

Il existe des options, qu'il est possible d'activer :
  • Soit dans la structure globale$.ajax_options afin qu'il s'applique à l'ensemble des appels.
  • Soit en les passant en paramètre (via la méthode$.ajax_option()), pour une activation ponctuelle sur un appel.
Ce dernier est prioritaire sur la structure globale et n'est pas vraiment un paramètre, puisqu'il ne sera pas communiqué à la fonction PHP.
<script>
$.ajax_options = {timeout: 1000, waiting: true};
$.ajax.fonctionPHP(parametre1, $.ajax_option({timeout: 1000, waiting: true}), parametre2); // la fonction PHP ajax_fonctionPHP() recevra les paramètres (parametre1, parametre2)
</script>
Ces options sont :
  1. timeout: une valeur entière positive exprimant le nombre de milliseconde avant que la requête aboutisse à un échec (ce qui déclenchera le catch de la promesse).
  2. progress: une fonction qui sera appelée régulièrement durant l'envoi et la réception. avec en signature :
    1. event contenant :
      • total: indiquant le nombre total de caractères à envoyer ou recevoir.
      • loaded: indiquant le nombre d'octets déjà envoyé ou reçu.
      • lengthComputable : booléen indiquant que total est valide (donc durant l'envoi).
      • target: le XMLHttpRequest, permettant d'accéder aux éléments tel quereadyState,response,status,getResponseHeader(), …
      • timeStamp: rattaché à l'évenement.
    2. download : àtrue si le download est en cours (undefined si c'est le upload).
  3. waiting: une valeur permettant d'afficher une animation, à partir de 3 secondes (ou à partir de$.ajax_options.wait) de non-réponse du serveur, et prenant en paramètre, le même paramètre que $.waiting().
  4. wait: le nombre de milliseconde à attendre avant de déclencher lewaiting. Par défaut égal 3000 millisecondes.
  5. approval: une fonction qui sera appelée après chaque retour réussi et avant lethen(), permettant d'intercepter et eventuellement transformer chaque retour de donnée du serveur, avec en signature :
    1. La donnée retournée par le serveur
    • Doit renvoyer une donnée qui peut être identique ou transformée.

Exemples pour l'optionprogress :
<script>
// si la requête prend plus d'une demie−seconde, la fonction catch sera appelée
$.ajax.ma_fonction1(parametres, $.ajax_option({timeout: 500})).then(function(success) {…}).catch(function(error) {…});

// affiche visuellement la durée restante du transfert, durant l'envoi et réception de l'appel de cette méthode PHP
let progress = $('PROGRESS');
progress.value = 0; // part de zéro
progress.display = 'inherit'; // et affiche la jauge
$.ajax.ma_fonction2(file, $.ajax_option({progress: function(event, download) {
if (! download)
if (event.loaded < event.total)
progress.value = event.loaded / event.total; // augmente la jauge durant l'envoi
else<br> progress.value = undefined; // met en attente la jauge pendant le traitement PHP
else<br> if (event.loaded < event.total)
progress.value = (event.total − event.loaded) / event.total; // diminue la jauge durant la réception
else<br> progress.display = 'none'; // ferme la jauge lorsque terminé
}})).then(succes);
</script>

Exemples pour l'optionwaiting :
<script>
$.ajax_options.waiting = true;
$.ajax_options.waiting = "/lib/image/waiting.gif";
$.ajax_options.wait = 10000; // change à partir de maintenant l'attente à 10s
$.ajax_options.waiting = $.create('img', {src: "/lib/image/waiting.gif", style: {margin: '25% 50%', width: '120px'}});
$.ajax_options.waiting = {src: "mon−image.png", style: {margin: '25% 50%', width: '150px'}, background: "#8883", tagName: 'img'};
</script>

Exemple pour l'optionapproval :
<script>
function my_approval(data) {
if (data && data.fault) {
trace("<e>Fault", data.fault);
delete data.fault;
}
return data;
}
$.ajax.first_method().then(data => {
data = my_approval(data);
trace(data);
});
$.ajax.second_method().then(data => second_method(my_approval(data)));
$.ajax.third_method().then(data => my_approval);

// peut être remplacé par
$.ajax_options.approval = data => {
if (data && data.fault) {
trace("<e>Fault", data.fault);
delete data.fault;
}
return data;
};
$.ajax.first_method().then(data => trace(data));
$.ajax.second_method().then(data => second_method(data));
$.ajax.third_method();

</script>

RPC : Manuel

Même si plus bas niveau et plus difficile d'utilisation, il est également possible d'employer la méthode$.ajax() pour lancer juste des requêtes simples asynchrones (donc sans créer de fonction PHP particulière).
Et côté PHP, d'intercepter manuellement ces requêtes Ajax avec la méthode staticTemplate::request(), et renvoyer le résultat avec la méthode staticTemplate::send().
Résultat qui sera retourné à la méthode$.ajax() sous forme d'une promesse :
<script>
// appel index.php avec le paramètre par1 = 42, puis appel la fonction "traitement()" avec la réponse de la requête.
$.ajax({par1: 42}).then(traitement);

// appel request.php à la racine du serveur avec post1 à 42 en POST, avec 1s maxi
$.ajax('post', "/request.php", {post1: 42}, 1000).then(function(response) {
// traitement de la réponse
});
<script>
<?php
if (Template::request('post1') == 42) {
Template::send([ 'ok' => "yes" ]);
exit;
}
?>

JS : Action

Plume ajoute un mécanisme d'empilement d'annulations au niveau de JS, permettant de simplifier la vie du développeur, en lui proposant une solution simple et structurante pour intégrer cette notion annulation dans son application.
Elle se présente sous la forme d'une unique classeAction.

Lorsque l'on veut réagir à une action utilisateur, afin de provoquer une réaction, qui peut être annulable, il suffit de créer une instance deAction.
Cette instance d'objet n'a pas besoin d'être mémorisée dans une variable, elle peut être utilisé directement pour accéder à deux méthodes principales : Action.todo() et Action.undo().
La première permet d'exécuter la "réaction" (le "à faire" ou le "refaire") et la seconde permet d'exécuter la réaction contraire afin de revenir en arrière (l'annulation).
Le "todo" s'exécutera en parallèle juste après la création et le "Action" s'exécutera sur un appel à Action.cancel().

Pour intégrer ce mécanime structurant, la partie du programme qui doit réagir à l'action de l'utilisateur, doit être encapsulé dans la méthode Action.todo(), et utiliser les paramètres passés lors de l'instanciation de ceAction et le développeur doit penser à créer un programme qui doit faire l'inverse en l'incluant dans la méthode Action.cancel().

Voici la liste des attributs et méthodes associées à cette classe :

Action.new()

Cet objet permet d'instancier une action annulable.
Le constructeur a en signature, une suite d'argument qui seront renvoyés dans les 2 méthodes Action.todo() et Action.undo(), et dont le 1er argument doit être obligatoirement une chaîne de caractère, permettant de signer en interne cette action. Exemples: 'cut', 'move', 'test', …
<html>
Exemple:
<button id="todo">Action</button>
<label>Réaction: <span>0</span> clic</label>
<button id="cancel">Annulation</button>
</html>
<script>
$('todo').on('click', event => {
new Action('click button', $('LABEL SPAN'))
.todo((type, account) => account.innerText = +account.innerText + 1 )
.undo((type, account) => account.innerText = +account.innerText − 1 );
});

$('cancel').on('click', event => Action.cancel() );
</script>
Exemple:

À noter qu'il est possible de passer des arguments à ces deux méthodes via :
  • Des variables locales accessibles.
  • Des paramètres passées lors dunew.
  • Des arguments passées à la structure générique Action.state (voir plus bas).
    </script>
    let local_arg = 0;
    const param = {
    struct_arg: 0,
    };
    const action = new Action('multiple', param);
    action.todo(function(type, param) {
    ++ local_arg;
    ++ param.struct_arg;
    ++ this.state.state_arg; // identique à type.state.state_arg
    trace("TODO", local_arg, param.struct_arg, this.state.state_arg);
    });
    action.undo(function(type) {
    −− local_arg;
    −− param.struct_arg;
    −− this.state.state_arg; // identique à type.state.state_arg
    trace("TODO", local_arg, param.struct_arg, this.state.state_arg);
    });
    action.state.state_arg = 0;
    </script>

L'instance peut être utilisé sous forme d'une chaîne de caractère, permettant de connaitre la signature de l'action :
<script>
const action = new Action("signature");

// on peut tester la signature de l'action
switch (action.toString()) {
case "signature":
trace('ok');
break;
}

// mais ce n'est pas une chaîne de caractère
if (action === "signature")
trace('non');

// utile dans un tableau
const libelles = {
'signature': "Action numéro un",

};
trace('libelle', libelles[action]);

// convertie en chaîne de caractère
const signatures = action + 's';
const signature = action.toString();

// et bien sûr, cela reste un objet Action
action.todo();
</script>

Attention : en JS, un switch fait une comparaison en mode stricte, il faut donc pour cette instruction, passer par unAction.toString() ou équivalent (action + '', …).

Action.todo()

Cette méthode d'instance permet de spécifier la fonction qui sera exécutée pour réagir à l'action.
Elle a en signature :
  1. La fonction qui sera exécutée en asynchrone pour réagir à l'action.
  2. Le paramètre optionnelthis qui sera utilisé lors de l'appel de la fonction.
  • Elle renvoie la même instance afin de chaîner sur d'autre méthode (telle que Action.undo())

La fonction passée en paramètre, a en signature par défaut la liste des paramètres passés dans le new Action() ("signature" incluse) et sera exécuté en asynchrone.
<script>
new Action('mon action', 1, true, $('DIV')).todo(
function(action, numerique, boolean, tag) {
trace("exécution en différé de l'action");
}
);
trace("retour immédiat de la demande d'action");
</script>

Particularité sur le 1er paramètre "signature", qui n'est pas une chaîne de caractère passé lors de l'instanciation, mais bien l'instance de la classe Action.
Il est possible de connaitre sa signature, en utilisant ce paramètre comme une chaîne de caractère (voir plus haut).

Cette classe donne également accès en écriture à deux variablesaction.arg etaction.args, permettant de modifier temporairement les paramètres envoyés à Action.undo() que l'on ne pourrait par exemple, n'avoir que lors de l'exécution de ce Action.todo() :
  • Si la variableaction.arg est affectée, elle permet d'ajouter simplement un paramètre supplémentaire aux paramètres passés au Action.todo().
  • Si la variableaction.args est affectée, elle représente alors un tableau remplaçant l'ensemble des paramètres.
Attention: la variableaction.args ne doit pas contenir pas le 1er paramètre "signature", la signature n'étant pas modifiable et sera toujours envoyée en tant que 1er paramètre au Action.undo().
Exemples :
<script>
// un annulé qui renverra : 'test1', 1, 2, 3
new Action('test1', 1, 2, 3) . todo((action, ...args) => { }) . undo((action, ...args) => trace(action, ...args));

// un annulé qui renverra : 'test2', 4, 5, 6
new Action('test2', 1, 2, 3) . todo((action, ...args) => { action.args = [4, 5, 6] }) . undo((action, ...args) => trace(action, ...args));

// un annulé qui renverra : 'test3', 1, 2, 3, {a: 7}
new Action('test3', 1, 2, 3) . todo((action, ...args) => { action.arg = {a: 7} }) . undo((action, ...args) => trace(action, ...args));
</script>

Il est possible de provoquer l'exécution du ou des "todo" déjà rattachés à l'action, en appelant simplement sa méthode Action.todo() sans aucun paramètre.
Cela peut être utile dans le cas où il faudrait ne créer qu'une annulation, uniquement sur la 1ère action, mais pas pour les suivantes (exemple avec un keyDown).
</script>
// récupère la dernière action
const action = Action.latest('annulable−seulement−en−1ere−action');
if (action)
action.todo();
else {
const origin = 42;
const parameter = {
next: origin,
count: 0,
};
const action = new Action('annulable−seulement−en−1ere−action', origin, parameter);
action.todo(function(type, origin, parameter) {
++ parameter.count;
trace("TODO", parameter.count, parameter.next);
});
action.undo(function(type, origin, parameter) {
parameter.next = origin;
trace("UNDO", parameter.count, parameter.next);
});
}
</script>

Note : Action.todo() n'est pas obligatoire pour le fonctionnement d'un annulé, mais fortement recommandé, afin de compartimenter la réaction au sein d'un programme et permettre la notion de Action.redo().
<script>
// il est possible de développer comme cela
$('tag').on('click', () => {
event.target.disable = false;
const action = new Action('tag clic')
.undo(type => {
});
});

// mais il est préférable de développer en encapsulant la réaction
$('tag').on('click', () => {
reaction();
const action = new Action('tag clic')
.todo(type => {
event.target.disable = false;
})
.undo(type => {
});
});
</script>

Action.undo()

Cette méthode d'instance permet de spécifier les actions à exécuter pour revenir en arrière.
Elle a en signature :
  1. La fonction qui sera exécutée pour réagir à une demande d'annulation.
  2. Le paramètre optionnelthis qui sera utilisé lors de l'appel de la fonction.
  • Elle renvoie la même instance afin de chaîner sur d'autre méthode (tel que Action.todo())

La fonction passée en paramètre, a en signature la liste des paramètres passés dans le new Action() ou renvoyés dansaction.args dans un Action.todo(), la signature en 1er paramètre incluse, et peut renvoyer une valeur qui sera renvoyée à la méthode Action.cancel().

Particularité sur le 1er paramètre "signature", qui n'est pas une chaîne de caractère passé lors de l'instanciation, mais bien l'instance de la classe Action.
Il est possible de connaitre sa signature, en utilisant ce paramètre comme une chaîne de caractère (voir plus haut).
<script>
const action = new Action('test', 7); // lui passe la valeur 7 pour réaction

action.todo((action, value) => action.args = [42]); // réaction avec 7 et renvoie 42 pour annulation
action.undo((action, value) => value); // annule avec 42 et le renvoie

const retour = Action.cancel(); // renverra 42
</script>

Il est possible de provoquer l'exécution du "undo" déjà rattaché à son Action, en appelant sa méthode Action.undo() sans aucun paramètre.
Cette simulation d'annulation, ne fait qu'exécuter cette action, sans la dépiler des actions.
</script>
const firstTime = ! counter ++;
$('BUTTON?action').on('click', () => {
const origin = 42;
const parameter = {
next: origin
};
const action = new Action('multiple', origin, parameter);
action.todo(function(type, origin, parameter) {
++ parameter.next;
trace("TODO", parameter.next);
});
action.undo(function(type, origin, parameter) {
parameter.next = origin;
trace("UNDO", parameter.next);
});
});
$('BUTTON?init').on('click', () => Action.latest('multiple').undo());
$('BUTTON?cancel').on('click', () => Action.cancel());
</script>

Action.clear()

Cette méthode statique de classe permet d'initialiser si besoin l'historique des actions.
Cette historique d'action est vidée lors d'un changement ou répétition de page, mais il peut être l'utiliser dans le cas d'un raffraichissement de page provoqué par de l'Ajax par exemple.

Action.latest()

Cette méthode statique de classe permet de renvoyer la dernière action, avec filtrage éventuel, sans chercher à dépiler cette action.
Elle est utile pour rattacher d'autre réaction (voir Action.append()) ou pour connaitre la dernière action, ou simplement savoir s'il y a des actions.
Elle a en signature :
  1. signature : si présent la dernière action doit être égale à cette signature, sinon, renvoienull.
  2. une suite de paramètre optionnel, qui si correspond aux premiers paramètres passés lors de l'instanciation, confirmera le renvoie de cette dernière action.
  • Renvoie :
    • L'instance trouvée ou la dernière instance si "signature" n'est pas communiqué
    • Ounull si non trouvé
    • Ouundefined si plus d'action dans l'historique.
      <script>
      new Action('un');
      const un = Action.latest('un'); // renvoie le dernier "un"

      new Action('deux', 1);
      new Action('deux', 2);
      const deux = Action.latest('deux'); // renvoie le dernier "deux#2"
      const precis = Action.latest('deux', 2); // renvoie le dernier "deux#2"
      const aucun = Action.latest('deux', 1); // renvoie null</abbr>

      if (Action.latest('test'))
      ; // execute uniquement si la dernière action est "test"

      // avec enchaine sur d'autre réaction
      Action.latest()?.append().todo(() => { … }).undo(() => { … });
      </script>

Action.cancel()

Cette méthode statique de classe permet de revenir sur la dernière action, en exécutant le Action.undo() et de dépiler sur l'action précédente.
Elle ne possède aucun paramètre et renvoie l'éventuelle valeur renvoyée par la méthode exécutée dans Action.undo().
Si plusieurs méthode Action.undo() sont rattachés à l'action (voir Action.append()), c'est la 1ère valeur communiquée qui sera renvoyée.
<script>
// intercepte le raccourci clavier d'annulation
window.on('keydown', event => {
if ((window.navigator.pcos ? event.ctrlKey : event.metaKey) && event.key == 'Z')
const valeur_de_retour = Action.cancel();
});
</script>

Action.redo()

Cette méthode statique de classe permet de reproduire la dernière action dépilée, en exécutant le Action.todo() et de rempiler sur l'action suivante.
Elle ne possède aucun paramètre et ne renvoie aucune valeur.

Action.append()

Cette méthode d'instance permet de rattacher d'autre réaction à cette action.
Elle est utile lorsque différentes réaction doivent se faire sur une même action, mais à plusieurs endroits dans le programme.
Elle a en signature - comme pour l'instanciationnew Action() - la liste des paramètres (hors le 1er paramètre "signature") et renvoie l'instance pour chaînage avec d'autres méthodes.
La fonction de réaction sera exécuté immédiatement en asynchrone.
L'ordre d'exécution des Action.todo() lors d'un Action.redo(), sera en 1er celui associé à l'instance, puis dans l'ordre des Action.append() exécutés.
L'ordre d'exécution des Action.undo() lors d'un Action.cancel(), sera inversé : en 1er le dernier Action.append() jusqu'à l'instanciationnew Action().
<script>
const action = new Action('test', 1, 2).todo((type, un, deux) => { … }).undo((type, un, deux) => { … });
if (true)
action.append(3, 4).todo((trois, quatre) => { … }).undo((type, trois, quatre) => { … });
</script>

Action.state

Cet attribut de classe permet d'associer d'autres informations propre à l'action, que l'on souhaiterait utiliser en dehors des réactions. Ces informations sont partagés par l'ensemble des réactions.
Si cette valeur est une structure, elle viendra en complément aux éventuelles autres valeurs, sinon elle écrasera l'ancienne éventuelle valeur.
<script>
const action = new Action().todo(action => …).undo(action => …);
trace(last.state); // renverra {}

action.state = {a: 1};
trace(action.state); // renverra {a: 1}

action.append().todo(action => …).undo(action => action.state = {b: 2});
trace(action.state); // renverra {a: 1, b: 2}

trace(Action.latest().state); // renverra {a: 1, b: 2}

const last = Action.latest();
last.state = 42;
trace(Action.latest().state); // renverra 42
</script>

Action.params

Tableau contenant la liste des arguments, initialisée lors de l'instanciation, et qui sera passée au Action.todo().
Ce tableau permet de rendre ces paramètres accessibles en dehors des méthodes de la classe.
Il ne contient que les paramètres, hors signature.
<script>
const last = Action.latest();
if (last)
trace('params', last.params);
</script>

Note : les arguments passés lors d'un Action.append() ne sont pas accessible, seul sont accessible ceux passés lors dunew Action().

Il peut également servir pour mettre à jour ces paramètres, suite à un Action.latest() :
</script>
// récupère la dernière action
const action = Action.latest('annulable−seulement−en−1ere−action');
if (! action) {
const action = new Action('annulable−seulement−en−1ere−action', 1);
action.todo((type, counter) => trace("counter =", counter));
// va afficher dans un 1er temps : "counter = 1"
} else {
// met à jour le paramètre counter
++ action.params[0];
// demande à relancer la réaction
action.todo();
// va afficher dans un 2ème temps : "counter = 2"
}
</script>

Note : là encore, cela ne concerne que les paramètres initiés lors de l'instanciation, on peut modifier les paramètres des Action.append().

PHP

Plume ajoute également des fonctionnalités au PHP, afin de faciliter la vie du développeur.

PHP : session_start()


Plume exécute automatiquementsession_start(), sauf s'il a déjà été exécuté en amont.
<?php
require('plume.php');
?>

PHP : constante

Ces constantes permettent de personnaliser Plume :

PLUME_DEBUGGING

PLUME_DEBUGGING permet d'ajouter des fonctionnalités de debugging au poste indiqué par son IP, appelé le "mode développement"
Il accepte :
  • une chaîne de caractères
  • une expression régulière (le '.' séparant 2 chiffres n'est pas considéré dans ce cas comme un méta-caractère, mais juste comme le caractère point).
  • un tableau de chaîne de caractères/expression régulière
  • un booléen, àtrue force le mode debugging pour tous, àfalse, interdit pour tout le monde
  • la chaîne de caractère "?" : permet d'afficher uniquement son IP dans la console du navigateur. À utiliser ponctuellement pour savoir quelle adresse placer.
Exemple :
<?php
define('PLUME_DEBUGGING', "77.0.1.2");
define('PLUME_DEBUGGING', "77.0.1.*");
define('PLUME_DEBUGGING', "77.0.[0−3].*");
define('PLUME_DEBUGGING', ["77.0.1.2", "168.0.1.2"]);
define('PLUME_DEBUGGING', true);
define('PLUME_DEBUGGING', false);
define('PLUME_DEBUGGING', "?");
?>

PLUME_LOG

PLUME_LOG peut spécifier un dossier, où Plume cherchera à enregistrer le journal des activités SQL et d'autres journaux, tel que les Failed.
S'il est égale à faux: il interdit ce comportement.
S'il correspond à un nom de dossier viable (qui sera créé si besoin), c'est ce dossier qui sera utilisé.
Exemple :
<?php
define('PLUME_LOG', "/www/data/log");
?>
Dans les autres cas, s'il est égale à vrai ou s'il n'est pas défini, Plume cherchera à créer un dossier avec cet algorithme :
Le nom du dossier sera :
  1. "log"
  2. Ou se terminant par ".data/log".
  3. Ou se terminant par ".log".
En partant du dossier où se trouve la librairieplume.php, puis en remontant jusqu'à la racine du disque.
Si aucun dossier n'est trouvé, le mécanisme de log n'est pas activé.
Exemple avec /www/mon-site/sous-dossier/plume.php :
  1. /www/mon-site/sous-dossier/log/ ➽ logs propre au site, et situé au même niveau que plume.php, dans son dossier "log"
  2. /www/mon-site/sous-dossier.data/log/ ➽ logs propre au site, et situé dans un dossier "data" partagé à d'autre information
  3. /www/mon-site/sous-dossier.log/ ➽ logs propre au site, et situé à la racine du site
  4. /www/mon-site/log/ ➽ logs propre au site, et situé à la racine du site, dans son dossier "log"
  5. /www/mon-site.data/log/ ➽ logs propre au site mais situé en dehors du site, dans un dossier "data" partagé à d'autre information
  6. /www/mon-site.log/ ➽ logs propre au site mais situé en dehors du site, dans son dossier "log"
  7. /www/log/ ➽ logs commun à l'ensemble des sites, mais situé au même niveau que les sites
  8. /www.data/log/ ➽ logs commun à l'ensemble des sites, et situé dans un dossier partagé à d'autre information
  9. /www.log/ ➽ logs commun à l'ensemble des sites, et situé à la racine du disque
Vous pouvez retrouver ce résultat en appelant la fonction PHP get_log_dir().

Les fichiers de log ont un nommage de type : prefixe_YYYY-MM-DD.log
Si un fichier log est trop volumineux (> 50Mo), le reste des informations seront enregistrées dans des fichiers nommés prefixe_YYYY-MM-DD_Z.log, ou Z est l'index commençant à 1.

Liste des logs actuellement gérés par Plume :

PLUME_REPORT

Une synthèse des journaux d'erreurs et warnings, peut être envoyé automatiquement par mail journalièrement.PLUME_REPORT peut spécifier une adresse mail où le développeur pourra recevoir des informations de débugging, de type report.
Exemple :
<?php
define('PLUME_REPORT', "contact−technique@ma−societe.com");
define('PLUME_REPORT', false); // pas de mail
?>

PLUME_IMPORT

Plume import automatiquement les ressourcesplume.js etplume.css nécessaire côté navigateur, en les cherchant sur le serveur officiel de Plume.
Mais (cas qui ne devrait pas se produire) il est possible de modifier ce comportement en plaçant une URL pointant sur la ressource (sans suffixe .css ou .js) dans la constantePLUME_IMPORT.
<?php
define('PLUME_IMPORT', "https://monserveur.com/ressource/plume");
require('plume.php');

// ou
define('PLUME_IMPORT', "/ressource/plume");
require('plume.php');
?>

PHP : Trace

Plume intégre au niveau PHP (comme au niveau JS) un mécanisme simple et efficace de trace pour les développeurs.

Trace : trace()

Comme le trace en Javascript, La procédure PHPtrace() permet d'afficher des données PHP directement sur la console du navigateur en mode développement, ou ne fait rien en mode normal (voir PLUME_DEBUGGING).
Le premier paramètre est de préférence une chaîne de caractère, titrant la trace ou un formatage éventuel s'il contient des substitutions, utilisant le même principe que pour format (%s, %5d, %2$x-7.3f, …), mais avec en plus les méta-caractères propre au PHP sprintf(bcdeEfFgGhHosuxX%).
Et comme en Javascript, il est possible d'intégrer le niveau d'affichage :<log>,<info>,<warn> ou<error>. Voir trace() pour plus d'information.
Cette procédure renvoietrue si le mode développement est activé etfalse dans le cas contraire.
<?php
define('PLUME_DEBUGGING', "192.168.0.1"); // l'adresse du développeur
require('plume.php');
trace('texte'); // envoi le texte sur la console
trace('titre', $var1, $var2, …); // envoi le texte et les variables
trace('nom=%s, %dsecondes', 'enzo', 9, −1); // formate avant d'envoyer "nom=enzo, 9secondes" suivi de "−1"

// les traces ne fonctionne que …
trace('exemple', $_REQUEST);

// si l'on appelle un display (pour afficher une page template)
Template::display();

// ou si l'on renvoie manuellement un retour
if (Template::request('post1') == 42) {
Template::send();
exit;
}

// ou encore si une fonction ajax est appelée
function ajax_ma_fonction() {
return;
}

// test si c'est en mode développement
if (trace()) {
trace("<error>une erreur grave…");
}
?>

Cette trace propre au PHP accepte le méta-caractère paragraphe§.
Si la chaîne de caractère du titre contient le caractère "§", il sera remplacé par la position de l'appel dutrace() dans le source, avec la syntaxe : ⁚fichier⁚ligne
<?php
trace('Je suis ici §:', 'caractère normal de paragraphe § qui ne sera transformé."); // ligne 123 du script.php
?>
Affichera
Je suis ici script.php:123: caractère normal de paragraphe § qui ne sera transformé.

En plus du formatage propre à PHP, latrace() accepte le formatage que l'on retrouve sur la fonction trace() en JS :
<?php
trace('<e>Ceci est une erreur');
trace("⸨gMs:%s⸩", "Exemple de texte souligné en vert sur magenta");
?>

Si appelé sans paramètre, elle ne fera que renvoyer un booléen indiquant si le mode développement et donc les traces sont activées ou non.
<?php
if (trace())
; // exécution d'une partie PHP uniquement pour les développeurs
?>

Attention: lestrace() sont communiquées au navigateur via des mécanismes internes à display() ou ajax.
Cela veut dire deux choses :
  1. Les traces qui sont appelées après l'appel de la procédure statique Template::display, ne fonctionneront plus.
  2. Si cette méthode n'est pas appelée dans votre programme, les traces ne seront pas communiquées à la console.
Pour le 1er cas, il est important de placer la procédure Template::display en fin de programme et pour le 2ème cas, vous pouvez utiliser la procédure Template::conclude() qui est faite pour cela :
<?php
if ($error_quil_faut_corriger) {
trace("Contexte", $error_quil_faut_corriger);
Template::conclude();
exit;
}
?>

Note : Si une redirection est demandée (avec unheader("Location: /other.php");), les traces éventuelles en amont ne sont pas perdues, et seront regroupées et affichées lorsque une prochaine page HTML sera affichée.
<?php	// index.php
$login = $_REQUEST['login'] ?? '';
trace('login:', $login);
if (! $login)
header('Location: login.php');
?>
<?php // login.php
trace("Demande d'authentification…");
Template::display('login.html');
?>
Rendra sur la console:
 ➜ Redirigé vers https://www.my-site.com/login.php
login
Demande d'authentification…

Trace : traceability()

Avec cette méthode, Plume propose une traçabilité, c'est à dire un suivi, de vos fonctions PHP sur la console du navigateur, afin de savoir où passe votre programme PHP.
L'utilisation de cette méthode consiste à encadrer vos méthodes PHP, par une traçabilité d'appel à placer en début de méthode et une autre traçabilité de retour, à placer avec chaquereturn et en fin de fonction PHP :
  1. Traçabilité d'appel de début (doit contenir qu'un seul paramètre) :
    1. Test: paramètre indiquant s'il faut tracer ou non cette méthode :
      • Soit une valeur booléen, àtrue pour tracer.
      • Soit une valeur numérique, différente de zéro pour tracer.
      • Soit une chaîne de caractère, généralement__CLASS__ .':' . __FUNCTION__, suivi d'autre paramètre optionnel, généralement les paramètres de la fonction, forçant la tracabilité de l'appel. Cette signature est plus lourde mais permet de personnaliser les paramètres.
  2. Traçabilité de retour ou de fin (doit contenir zéro ou plus de un paramètre) :
    1. Test: paramètre indiquant s'il faut tracer ou non cette méthode (qui doit être identique au précédent) :
      • Soit une valeur booléen, àtrue pour tracer.
      • Soit une valeur numérique, différente de zéro pour tracer.
      • Soit aucun paramètre, qui forcera la tracabilité de retour.
    2. Retour : valeur obligatoire de retour. Si la méthode ne renvoie rien (unreturn), il faut passer la valeurNULL.
    3. Compléments: paramètres optionnels complémentaire qui seront tracés.

C'est la même fonction employée en début et fin de fonction, ce qui les différencies est le nombre de paramètres passés :
  • Pour l'appel de début : 1 seul paramètre.
  • Pour l'appel de retour ou de fin : 0 ou plus de 1 paramètre.
Appel de retour à utiliser conjointement avec unreturn.

Un exemple simple consiste à appeler la méthode en mettant le test en tant que 2ème paramètre :
<?php
function ma_fonction() {
// tracabilité d'appel en début de fonction
traceability($trace_active == 'OUI');
traceability($traces_activees & 1);

// tracabilité de retour juste après chaque return ou en fin de fonction
return traceability($trace_active == 'OUI', 42);
return traceability($traces_activees & 1, null);
return traceability($trace_active_ok, $result > 0, $result, "$this");
// ou en fin de fonction
traceability($traces_activees & 1, null);
}
?>
Mais une façon un peu plus rapide en execution, mais moins lisibile, consiste à sortir ce test de la méthode :
<?php
function ma_fonction() {
// tracabilité d'appel en début de fonction
if ($trace_active == 'OUI') traceability(true);
if ($traces_activees & 1) traceability(true);

// tracabilité de retour juste après chaque return</abbr>
if ($trace_active == 'OUI', 42) return traceability(true, null); else return;
if ($traces_activees & 1) return traceability(true, null); else return;
if ($trace_active_ok) return traceability(true, $result > 0, $result, "$this"); else return;
// ou en fin de fonction
if ($trace_active_ok) traceability(true, null);
}
?>
On peut même en profiter pour personnaliser la trace ou simplifier la sortie en cas de non retour de valeur :
<?php
function ma_fonction() {
// tracabilité d'appel en début de fonction
if ($trace_active) traceability(__CLASS__ . ':' . __FUNCTION__, $parametre1, substr($parametre2, 0, 50) . '' . substr($parametre2, −50));

// tracabilité de retour juste après chaque return</abbr>
if ($trace_active) return traceability(); else return;
// ou en fin de fonction
if ($trace_active) traceability();
}
?>

Il est important d'encapsuler la valeur renvoyée pas lesreturn dans le 2ème paramètre de cette méthodetraceability() et de s'assurer de sa présence en fin de vos méthodes.
Comme il est important de placer latraceability en toute première ligne de (voir sur la même ligne que) la fonction PHP, donc si possible avant les static, global, const, ….

Exemples de script PHP :
<?php
function ma_fonction($parametre) {

echo "ma fonction";
if ($parametre)
return 0;
echo "suite de ma fonction";

return 42;
}
function ma_procédure_qui_ne_renvoie_rien($parametre) {

echo "ma procédure";
if ($parametre)
return;
echo "suite de ma procédure";

}
class mon_objet {
function __construct() {

echo "mon constructeur";

}
}
?>

Qui devient :
<?php
define('TRACABILITE', true); // permet d'activer la traçabilité

function ma_fonction($parametre) {
traceability(TRACABILITE);

echo "ma fonction";
if ($parametre)
return traceability(TRACABILITE, 0);
echo "suite de ma fonction";

return traceability(TRACABILITE, 42);
}
function ma_procédure_qui_ne_renvoie_rien($parametre) {
traceability(TRACABILITE);

echo "ma procédure";
if ($parametre)
return traceability(TRACABILITE, null);
echo "suite de ma procédure";

traceability(TRACABILITE, null);
}
class mon_objet {
function __construct() {
traceability(TRACABILITE);

echo "mon constructeur";

traceability(TRACABILITE, $this); // exemple (sans return) qui permet de tracer l'objet créé
}
}
?>

Note : avec untraceability(test, null) dans un constructeur, lenull sera remplacé par son nom de classe d'objet dans la console.


La traçabilité se présente dans la console, sous 2 forme: une trace en continue et un tableau de synthèse :

La traçabilité en continue contient :
  1. Compteur : commençant par 001, et incrémenté à chaque appel d'une sous-méthode.
  2. Niveau précédent : commençant à 1
  3. Sens : soit vers la droite (⋯⋯▶︎) pour indiqué un appel vers cette nouvelle méthode, soit vers la gauche (◀︎⋯⋯︎︎) indiquant un retour de cette méthode. Si aucune trace n'est faite entre les deux, une double flèche (◀︎⋯⋯▶︎) indique un aller/retour.
  4. Niveau de la fonction PHP
  5. Méthode : le nom de la méthode, précédé de sa classe éventuelle (séparé par un ":").
  6. Paramètres : encadré de parenthèse (uniquement pour les appels et raccourci si trop long).
  7. Lignes : un dièse '#' suivi du numéro de la ligne appelante dans le fichier PHP suivi d'une fleche indiquant le sens suivi de la ligne appelé ou de retour.
  8. Retour : la valeur renvoyée par la méthode (uniquement pour les retours).
  9. Temps : qu'à pris cette fonction PHP en milliseconde, encadré de chevron (uniquement pour les retours).

Le tableau de synthèse est affiché en fin de console (en fin d'exécution du programme PHP), avec en colonne :
  • file : Le fichier PHP.
  • class : La classe éventuelle, colonne absente si aucune classe n'est utilisée.
  • method : La méthode.
  • line : la ligne dans le fichier.
  • call : le nombre d'appel à cette méthode.
  • time : le temps total passée dans cette méthode, en seconde.
  • bug : si présent, et positif indique qu'il manque un appel detraceability de fin, et inversement si négatif, indique un appel detraceability d'appel manquant.
Cette synthèse permet de surveiller et si besoin optimiser les méthodes qui seraient appelées trop souvent ou prendrait trop de temps.


Il existe une 3ème signature à cette fonction, sous forme d'une liste d'options à lui passer :
  • out_call : permettant de n'afficher que la traçabilité en continue, donc sans la partie synthèse.
  • out_summary : permettant de n'afficher que la partie synthèse, donc sans la partie traçabilité en continue.
  • out_all (par défaut) : permettant d'afficher les 2 parties : la traçabilité en continue et la synthèse.

Exemple :
<?php
traceability(['out_summary']);
?>

Trace : PLUME_TRACE

Il est possible d'orienter tous les flux destrace() (PHP comme JS) vers un fichier hébergé côté serveur PHP, afin de les lire en différés.
Ce mécanisme permet d'avoir des traces lorsque la console du navigateur n'est pas accessible (développement mobile, navigateur client, …).
Pour activer ce mécanisme, Il faut définir la constantePLUME_TRACE avec une valeur "canal" unique.
Exemple :
<?php
define('PLUME_TRACE', ! navigator('mobile') && trace() ? 4274 : false);
?>
Le canal passée peut être numérique ou chaîne de caractère, et permet de filtrer ces traces de l'ensemble des traces enregistrées, et également de protéger certaines informations sensibles (mot de passe, CB, …) contenu dans ces traces, d'une lecture indésirable.

Il est possible de consulter ces traces sur la console du navigateur du développeur, avec la méthode$.trace.file(), en l'incluant dans une page HTML comme le montre cet exemple :
<!DOCTYPE html>
<html lang="fr" class="{folder}">
<head>
<meta charset="utf−8">
<title>Traceur</title>
<meta name="copyright" content="D−Nada">
<meta name="author" lang="fr" content="Chevillard">
<meta name="viewport" content="width=device−width, initial−scale=1.0">
<style>
html { background: white; }
@media (prefers−color−scheme: dark) { html { filter: invert(1) hue−rotate(.5turn); } }
</style>
<script src="/plume/plume.js"></script>
<script>
function display(chanel) {
$.trace.file($('chanel').value, $('server').value + "/plume.php", $('timer').value);
}
</script>
</head>
<body>
<label>URL :<input id="server" onchange="display()"></label>
<label>Numéro du canal :<input id="chanel" value="4274" onchange="display()"> ou vide pour arrêter</label>
<label>URL :<input id="timer" value="2000" onchange="display()"></label>
À consulter sur la console du navigateur.
</body>
</html>
La signature de cette méthode est :
  • Une valeur unique représentant le canal à écouter.
  • L'URL vers plume.php qui a enregistré les log.
  • Une valeur numérique positive optionnelle indiquant le temps de rafraichissement en milliseconde, égale à 2s si omis.

Trace : Warning et Error PHP


Les erreurs PHP provenant d'un appel ajax sont redirigés vers la console du navigateur, permettant de ne pas perturber les éventuelles informations retournées et le fonctionnement du script JS.
Le retour ajax contiendra le texte sur l'erreur sous forme d'un attribut 'fault' et éventuellement son numéro dans 'faultRef' contenant cette erreur (pour afficher avec$.message par exemple).
Exemple côté JS :
<script>
$.ajax.appel().then(function(retour) {
if (retour && retour.fault)
$.message('⚠️', "Une exception est survenue.\n\n<span class='reference'>" + (data.faultRef || "syntaxError") + "</span>" + data.fault, "OK");
});
</script>
Le résultat côté console développeur devrait ressembler à (suivant le rendu du navigateur) :
⚠️ Warning: Illegal offset type in /home/sitecomhh/www/site/etc/index.php on line 141
⛔️ Parse error: syntax error, unexpected '$compute' (T_VARIABLE) in /home/sitecomhh/www/site/etc/index.php on line 896

PHP : Failed

Plume inclus un mécanisme simple d'exception pour les développeurs, via la classe staticFailed
Lorsque le script PHP rencontre un problème qui ne devrait pas survenir, le développeur peut ajouter une exceptionFailed afin de (se) signaler ce problème et provoquer la sortie du programme.
Pour chaque cas, il est conseillé au développeur de créer une exception différente, afin de la retrouver plus facilement dans ses sources.
La syntaxe de ses exceptions est :
  1. Failed:: + le nom interne de l'erreur (en forme camelCase ou snake_case)
  2. Arrêt : si ce 1er paramètre est un booléen, il précise si l'exception doit (true) provoquer une sortie de programme (comportement par défaut en l'absence de ce paramètre) ou (false) juste une trace et continuer le programme.
  3. Informations privées : si ce paramètre est un tableau, il sera composé de données qui seront affichés sur la console et dans le log, mais uniquement pour le développeur. Si ce paramètre est omis, les informations de la $_SESSION seront utilisés.
  4. Informations publiques : Une suite de paramètres optionnels permettant de personnaliser le message d'erreur publique (voir plus bas) qui sera affiché pour tout le monde, avec le mécanisme de substitution de vsprintf.

Exemple:
<?php
if ($unCasQuiNeDevraitPasSurvenirOuIlFautPrevenirLeDeveloppeurPourCorrigerOuAmeliorerLeSource)
Failed::UneErreurBienPrecise(['session' => $_SESSION, 'conf' => $conf, 'file' => $file], "quarante−deux");
if ($erreur != 0)
Failed:une_autre_erreur_pas_normal();
?>
Note : Si aucun paramètre privé n'est passé, il sera remplacé par la structure{_SESSION: $_SESSION} Note : Le paramètrestack est passé dans tous les caswhereami(0) dans les informations privées.

Failed : configuration()

L'ensemble de ces exceptions doivent être initialisées en début de programme, avec cette fonction, de signature :
  1. Structure dont les clés représentent l'exception, et la valeur, une chaine de caractère que l'on peut formater par substitution comme pour format (%s, %5d, %2$x-7.3f, …).
  2. Fonction de complétion, de signature :
    1. Failed : le nom de l'exception.
    2. Privée : Le paramètre "information privée" passé dans l'exception.
    3. Pile : La liste des appels (correspondant à unwhereami(0))
    • Le retour est le nouveau tableau d'information privée.
  3. Un signalement indiquant si le développeur doit être alerté (ne fonctionne jamais en mode développement, uniquement en mode production) :
    • Soit sous forme d'un booléan, et si vrai, l'adresse mail PLUME_REPORT sera utilisé.
    • Soit sous forme d'une adresse mail qui sera utilisé pour l'envoi de l'alerte.
    • Par défaut =true.
      <?php
      Failed::configuration([
      'UneErreurBienPrecise' => "Un cas %s qui ne devrait pas survenir",
      // …
      ]);
      ?>
Si l'exception n'est pas renseignée dans le premier paramètre deFailed::configuration, le message publique sera le message renseigné par la cléunknown (ou le message en dur "Unknown error") suivi de l'ensemble des paramètres publiques.

Un 2ème paramètre est possible indiquant une fonction permettant de compléter les informations privées, qui sera appelée lorsque le programme rencontre unFailed, avec en signature :
<?php
Failed::configuration([
'UneErreurBienPrecise' => "Un cas %s qui ne devrait pas survenir",
// …
'assert' => "Erreur de paramètrage (#%d).",
'unknown' => "Erreur inconnue (%s).",
], function($failed, $private, $stack) {
global $general, $base;
if (! $private)
$private = [];
return $private + ['general' => $general]; // attention à la priorité du concat PHP qui ne retiendra que le doublon à gauche du "+"
});
?>
Note : Lors d'un Failed, toutes les bases sont automatiquement fermées, et les les historiques clôturés.

Failed : assert

Cette méthode statiqueFailed::assert() permet de générer une exception pour, avant tout, contrôler les informations passées en paramètre aux méthodes, avec 2 types de signatures possibles :
  1. Signature générique :
    1. Tableau contenant pour chaque paramètres testés, son marqueur de donnée (voir plus bas).
    2. Tableau contenant la valeur des paramètres passés.
  2. Signature précise :
    1. Valeur de test : si vrai, déclenche l'exception.
    2. valeurs privées : tableau regroupant des informations pour le développeur.

Les marqueurs de données sont des chaînes de caratères, représentant une syntaxe abrégée (pour les nombres), ou une expression régulière (pour les chaînes de caractères), de syntaxe :
  • Trois points de suspension :
    • '…' : le paramètre doit être un numérique.
    • '0…' : le paramètre doit être un numérique et supérieur ou égal à la valeur passée (0 dans l'exemple).
    • '…10' : le paramètre doit être un numérique et inférieur ou égal à la valeur passée (10 dans l'exemple).
    • '-10…10' : le paramètre doit être un numérique et compris ou égale aux deux valeurs passées (entre -10 et +10 dans l'exemple).
  • La barre horizontale '|' :
    • Excusif : exemple 'monsieur|madame|mademoiselle' qui est obligatoirement soit "monsieur", "madame" ou "mademoiselle"
    • Inclusif : exemple 'monsieur|madame|mademoiselle|' identique mais le dernier "|" indique qu'il peut être vide.
  • Point '.' représentant n'importe quel caractère
  • Plus '+' indiquant une répétition obligatoire : exemple '.+' doit être une chaîne de caractère non vide.
  • Caractère '\w{0, 255}' : le paramètre doit être une chaîne de caractère compris entre 0 et 255 lettre ou chiffre (ou "_").
  • Tableau '[\w+]' : le paramètre doit être un tableau contenant un type bien précis (des lettres dans l'exemple)
    <?php
    function une_methode($numerique_non_nul, $texte, $enumere_optionnel) {
    Failed::assert([ '1…', '\w{1, 255}', 'bleu|blanc|rouge|' ], [ 'numerique_non_nul' => $numerique_non_nul, 'texte' => $texte, 'enumere_optionnel' => $enumere_optionnel ]);

    }
    function une_methode($un_parametre) {
    Failed::assert(! $un_parametre || strlen($un_parametre) > 255 || preg_match('/\W/', $un_parametre), [ 'un_parametre' => $un_parametre ]);

    }
    ?>

Le message d'erreur est par défaut "Assert error (#%d).", mais peut être pesonnalisé avec la clé :assert:
<?php
Failed::configuration([
'assert' => "Erreur de paramètrage (#%d).",
]);
?>

Failed : JS

L'exception générée suite à une requête HTTP, sera affichée dans la page, et générée depuis une méthode ajax ou template sera renvoyée sous forme de trace sur la console du navigateur, et pourra être intercepté côté Javascript :
<script>
$.ajax.appel().then(function(retour) {
if (retour && retour.failed)
$.message('⚠️', "Une exception est survenue.\n\n<span class='reference'>" + (data.failedRef || "syntaxError") + "</span>" + data.failed, "OK");
});
</script>

Failed : Fichier

Il sera également sauvegardé dans un fichier log journalier, présent sur le serveur de nommage error_AAAA-MM-JJ.log et dont chaque ligne aura les champs suivants séparés d'une tabulation :
  • Date sous forme : AAMMJJ HHMMSS
  • Mot clé "Failed"
  • IP
  • URL
  • Nom de l'exception du Failed
  • Message publique qui est affichée
  • Tableau privée, sous forme JSON

À noter : Si une erreur PHP survient lors d'un appel AJAX, cette erreur est également enregistrée dans ce journal, avec la syntaxe suivante :
  • Date sous forme : AAMMJJ HHMMSS
  • Mot clé "error"
  • IP
  • URL
  • Message d'erreur

Failed : Exemples

Exemple fictif :
<?php
201231 124907 failed 76.200.201.202 https://www.d−nada.com/test/ appendUnauthorized Ajout non−autorisé {"id":"7","client":"abc","login":"laurent","pref":{"entitled":true,"emplacement":123}}
201231 125142 error 76.200.201.202 https://www.d−nada.com/test/ Warning: Invalid argument supplied for foreach() in /home/server/www/test/dev.php on line 170
?>
Autre exemple:
<?php
Failed:setFailed([

'readError' => "Erreur de lecture sur la table '%s' de la base '%s'",

])
if ($erreur_grave) {
// si le développeur décide de provoquer l'exception readError, sous forme d'une boite d'alerte, d'un message sur la console (en mode développement) et arrêter le programme PHP
if ($afficher_exception)
Failed::readError($signal, $table, $base);
// sinon, le développeur décide d'afficher uniquement l'information sur la console (si mode développeur)
Failed::trace('readError', $signal, $table, $base);
}
?>

PHP : get_log_dir()

Fonction permettant de renvoyer le chemin absolu vers le dossier des journaux. Voir PLUME_LOG pour plus d'information sur son résultat.
Sa signature est :
  1. Nom du fichier : chaîne de caractère optionnelle permettant de spécifier éventuellement un nom de fichier.
  • Elle renvoie :
    • Si PLUME_LOG est défini àfalse, elle renvoie une chaîne vide.
    • Si le nom du fichier n'est pas indiqué, elle renvoie le chemin complet, sans slash en fin de nom de dossier.
    • Si le nom du fichier est défini, elle renvoie le chemin complet, plus le nom du fichier au format : "/NOM-DU-FICHIER_YYYY-MM-JJ.log"
      <?php
      $chemin = get_log_dir() . '/mon_log.log'; // renvoie le dossier + un nom de fichier
      $chemin = get_log_dir('mon_log.log'); // renvoie le dossier associé au nom du fichier
      if ($chemin) // test si le log est valide
      error_log(strtr(json_encode($les_informations), "\n\t", " "), 3, $chemin); // ajout les informations au log
      ?>

PHP : whereami()

Fonction permettant de renvoyer les différents appels PHP qui ont été effectués depuis le début jusqu'à l'endroit où cette méthode est appelée.
Sa signature est :
  • Limite: nombre de sortie, avec :
    • > 1: renvoie un tableau du nombre maximum d'appel indiqué par cette valeur.
    • = 1: (par défaut) ne renvoie qu'une seule sortie, celle où se trouve l'appel àwhereami().
    • = 0: renvoie l'ensemble des appels.
    • < 0: renvoie qu'une seule sortie, la remontant d'autant que la valeur indiquée (ce qui veut dire qu'une limite de -1 est identique à 1).
  • Formatage: type de sortie, avec :
    • Chaîne de caractère : avec les méta "%f" pour fichier, "%l" pour ligne, "%c" pour classe, "%m" pour méthode, et "%a" pour arguments.
    • Tableau ["classe+méthode", "fonction", "simplifié"] : contenant les 3 formats possibles (voir l'exemple plus bas).
    • vrai: la sortie est une structure correspondant à['file' => "fichier.php", 'line' => 0, 'class' => "", 'method' => "", 'args' => []].
    • null (ou sans ce paramètre): la sortie est une chaîne de caractère de formefichier⁚line:classe.methode([args]) avec les arguments.
    • fausse: la sortie est une chaîne de caractère de forme "fichier⁚line:classe.methode([])", donc sans argument.

Exemple:
<?php
// déclaration d'une fonction appelant la classe
function one($a) {
$exemple = new Exemple();
$exemple−>two(2); // ligne:4
}
// déclaration d'une classe
class Exemple {
function two($b) {
trace('string:', whereami()); // ligne:9
trace('strings:', whereami(2, ["%f#%l=%c.%m(%a)", "%f#%l=%m(%a)", "%f#%l"]));
trace('array:', whereami(0, true)));
trace('before:', whereami(−2, "%c.%m(%a)%l")));
}
}
// appel de la fonction
one(1, "abc"); // ligne:16
?>
Renverra :
		string:	"script.php:9"				// format simplifié
		strings:	[
"script.php#10", // format simplifié
"script.php#4=Exemple.two(2)", // format classe+méthode
]
		array:	[
{
file: "script.php",
line: 11,
class: "",
method: "",
args: []
},
{
file: "script.php",
line: 4,
class: "Exemple",
method: "two",
args: [2]
},
{
file: "script.php",
line: 16,
class: "",
method: "one",
args: [1, "abc"]
},
]
		before:	"Exemple.two(2)4"

PHP : array_some()

Plume intègre la fonctionarray_some() permettant d'appeler une fonction sur tous les éléments d'un tableau, jusqu'à que cette fonction renvoie vrai, et dans ce cas, s'arrête d'itéré sur le tableau et renvoietrue.
Sinon, renvoiefalse, ce qui est toujours le cas sur un tableau vide.
Sa signature est :
  1. Tableau : le tableau à itérer.
  2. Fonction : la fonction a appelée qui est de signature :
    1. Valeur : la valeur d'un élément du tableau.
    2. Clé : la clé ou index de l'élément.
    3. Tableau : le tableau itéré.
    • Renvoie vrai pour s'arrêter ou faux pour continuer à itérer.
  • Renvoietrue si un élément est vrai, sinonfalse.

PHP : array_every()

Plume intègre la fonctionarray_every() permettant d'appeler une fonction sur tous les éléments d'un tableau, jusqu'à que cette fonction renvoie faux, et dans ce cas, s'arrête d'itéré sur le tableau et renvoiefalse.
Sinon, renvoietrue, ce qui est toujours le cas sur un tableau vide.
Sa signature est :
  1. Tableau : le tableau à itérer.
  2. Fonction : la fonction a appelée qui est de signature :
    1. Valeur : la valeur d'un élément du tableau.
    2. Clé : la clé ou index de l'élément.
    3. Tableau : le tableau itéré.
    • Renvoie faux pour s'arrêter ou vrai pour continuer à itérer.
  • Renvoietrue si tous les éléments sont vrais, sinonfalse.

PHP : enumerate()

Fonction permettant d'émuler le mot-cléenumerate que l'on retrouve dans d'autre langage, tel que 'C', 'Java', …, en attendant PHP 8.1.
Il suffit de passer les différentes énumérations en tant que paramètre :
<?php
enumerate(
'LUNDI',
'MARDI',
'MERCREDI',
'JEUDI',
'VENDREDI',
'SAMEDI',
'DIMANCHE'
);
switch ($valeur) {
LUNDI:
echo LUNDI; // affichera 0
break;
MARDI:
echo MARDI; // affichera 1
break;
}
?>
Il est possible également de choisir les valeurs à affecter à ces énumérations, en passant à la place un tableau :
<?php
enumerate([
1=> 'LUNDI',
'MARDI',
'MERCREDI',
'JEUDI',
'VENDREDI',
'SAMEDI',
1=> 'DIMANCHE'
]);
switch ($valeur) {
LUNDI:
echo LUNDI; // affichera 1
break;
MARDI:
echo MARDI; // affichera 2
break;
}
?>
Qu'il faudra transformer en PHP 8.1 en :
<?php
enum Jour: integer {
LUNDI:1;
MARDI;
MERCREDI;
JEUDI;
VENDREDI;
SAMEDI;
DIMANCHE: −1;
};
switch ($valeur) {
Jour::LUNDI:
echo Jour::LUNDI; // affichera 1
break;
Jour::MARDI:
echo Jour::MARDI; // affichera 2
break;
}
?>

PHP : Sérialisation


Plume propose plusieurs moyens de sérialisation.

Sérialisation : json_encodes


Cette méthode est un synonyme à la méthode native json_encode() en apportant des possibilités d'optimisation et de gestion de l'UTF.
Elle a en signature :
  1. Valeur à sérialiser.
  2. Strict indiquant si vrai qu'il faut respecter le format JSON. Par défaut faux indique que la donnée pourra être optimisé.
  • Renvoie un JSON sous forme de texte.

Sérialisation : json_decodes


Cette méthode est un synonyme à la méthode native json_decode(), avec une analyse moins strict et la gestion de l'UTF.
Elle a en signature :
  1. JSON à dé-sérialiser.
  2. Strict indiquant si vrai qu'il faut respecter le format JSON. Par défaut faux indique que l'on peut accepter un format plus souple.
  • Renvoie une valeur PHP.

Cette méthode met à jour un tableau globale$json_decodes_error contenant la liste des erreurs eventuellement rencontrés lors de l'analyse du JSON, avec pour chaque erreur :
  • line: le numéro de la ligne (commence à 1).
  • column: le numéro de la position du caractère (commence à 1).
  • error : la signature de l'erreur, avec :
    • JSON_ARRAY : problème sur l'analyse d'un tableau indexé.
    • JSON_EOF : fin du fichier rencontré alors que l'analyse n'était pas fini.
    • JSON_KEY : problème sur le ":" manquant après une clé.
    • JSON_STRING : problème sur la fermeture d'une chaîne de caractère.
    • JSON_STRUCT : problème sur l'analyse d'une structure nommé.
  • comment : un texte explicatif en clair sur l'erreur rencontré.

PHP : Base


Base est une classe PHP simplifiant la vie du développeur, par sa facilité d'utilisation, sa lisibilité et ses fonctionnalités poussées.

Pour la bonne compréhension des résultats des exemples qui suivent, cette table est utilisée en exemple :
id nom ville age
42 Enzo Annecy 35
74 Patricia La Paz 28
500 Yvan Annecy 35
501 Yannis Annecy 20

Base : open

Ouverture d'une base :
<?php
$base = new Base("signature");
?>
Base va ouvrir une base, en recherchant les informations de connexion, rattachées à la signature, dans un annuaire.
L'annuaire est un fichier qui peut être présent sur le serveur aux endroits suivants :
  • PLUME_BASE : est une constante que le développeur peut passer à plume, sous formedefine('PLUME_BASE', "/xxx/yyy/annuaire.xxx");
  • sinon cherche dans le dossier où se trouve plume.php, un fichier finissant par .php et nommé :
    • FQDN (exemple : www.d-nada.com)
    • domaine name (exemple d-nada.com)
    • domaine (exemple d-nada)
    • la constante : "base.php"
  • si non-trouvé dans ce dossier, recherche à nouveau mais dans le dossier parent, …
Pour des raisons de sécurité, ce fichier "annuaire" est préfixé .php et donc de syntaxe PHP :
<?php
/*
open:signature mysql://login:password@server[/base]
sql:clé select … {x} …
value:clé constante
data:clé data
*/
?>
Ce fichier annuaire peut contenir plusieurs commandes. La commande quenew Base() recherche estopen.
<?php
open:signature mysql://login:password@server[/base]
?>
Pour le moment, seul MySQL est porté, les autres bases tel que Oracle ou PostgreSQL, peuvent être ajouté facilement.
Les autres commandes servent à :
  • sql : stoquer des requêtes SQL.
  • value : à placer des constantes pour les requêtes SQL.
  • data : pour des constantes de développement.

Base : read

On peut lire un enregistrement avecbase−>read().

read(= 1 champ)

Si un seul champ demandé, renvoie une chaîne correspondant à ce champ.
<?php
$record = $base−>read("select nom from table where id = 42");
?>
Renvoie :
"Enzo"
Si besoin, il est possible néanmoins de forcer le renvoie d'un tableau plutôt qu'une valeur, en indiquant une constante supplémentaire dans la requête :
<?php
$record = $base−>read("select 0, nom from table where id = 42");
?>
Renvoie :
[
0 => "0",
"nom" => "Enzo"
]

read(> 1 champ)

Si plusieurs champs demandés, renvoie un tableau associatif :
<?php
$record = $base−>read("select id, * from table where id = 42");
?>
Renvoie :
[
"id" => "42",
"nom" => "Enzo",
"ville" => "Annecy",
"age" => 35
]
Attention aux homographes de champs identiques sur deux tables différentes, la dernière valeurs écrasant toujours les autres.
Exemple fréquent avec les "id" :
<?php
$base = [
'table1' => [
[ 'id' => 1, 'lien' => 2 ],
],
'table2' => [
[ 'id' => 2 ]
]
];
// avec juste une étoile, le table2.id viendra écraser la valeur du table1.id
trace($base−>read("select * from table1, table2 where table1.id = 1 && table1.lien = table.2.id"));
// renverra [ 'id' => 2, 'lien' => 2 ]

// si l'on veut conserver uniquement le table1.id, il faut ajouter cette dernière en fin du select
trace($base−>read("select *, table1.id from table1, table2 where table1.id = 1 && table1.lien = table.2.id"));
// renverra [ 'id' => 1, 'lien' => 2 ]

// et si l'on veut conserver les id, il faut les nommer
trace($base−>read("select *, table1.id id1, table2.id id2 from table1, table2 where table1.id = 1 && table1.lien = table.2.id"));
// renverra [ 'id' => 2, 'lien' => 2, 'id1' => 1, 'id2' => 2 ]
?>

Enfin, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

Base : select

On peut lire une liste d'enregistrement avecbase−>select().
<?php
$liste = $base−>select("select * from ma_table where id = 42 order by nom");
$liste = $base−>select("show table status like 'ESSAI%'");
?>

select(1 champ)

Si un seul champ demandé, renvoie une liste indexée, de la valeur de ce champ.
<?php
$liste = $base−>select("select nom from table");
?>
Renvoie :
[
"Enzo",
"Patricia",
"Yvan",
"Yannis"
]

select(2 champs)

Si deux champs demandés, renvoie une liste indexée, de la valeur du premier champ (généralement l'id), avec la valeur du 2ème champ.
<?php
$liste = $base−>select("select id, nom from table");
?>
Renvoie :
[
42 => "Enzo",
74 => "Patricia",
500 => "Yvan",
501 => "Yannis"
]

select(>= 3 champs)

Si plus de 3 champs demandés, renvoie un tableau associatif dont la clé correspond au premier champ, et la valeur, un tableau associatif de ces champs :
<?php
$liste = $base−>select("select id, nom, age from table where 1");
?>
Renvoie :
[
"42" => [ "id" => "42", "nom" => "Enzo", "age" => "35" ],
"74" => [ "id" => "74", "nom" => "Patricia", "age" => "28" ],
"500" => [ "id" => "500", "nom" => "Yvan", "age" => "35" ],
"501" => [ "id" => "501", "nom" => "Yannis", "age" => "20" ]
]
Ce champ clé devant être unique, le champ "id" est généralement utilisé.
Exemple montrant une perte d'information (doublon sur la ville) à ne pas utiliser dans ce cas :
<?php
$liste = $base−>select("select ville, age, nom, id from table");
?>
Renvoie :
[
"Annecy" => [ "ville" => "Annecy", "age" => "20", "nom" => "Yannis", "id" => "501" ],
"La Paz" => [ "ville" => "La Paz", "age" => "28", "nom" => "Patricia", "id" => "74" ],
]
// l'enregistrement "Enzo" est écrasés par "Yvan" qui lui−même est écrasé par le dernier enregistrement lu "Yannis"
Si vous ne savez pas si votre clé possède des doublons et que vous ne souhaitez pas utiliserid, tout en voulant tout récupérer, vous devez utiliser la méthode Base->list() qui va générer une index numérique à la place de la clé.

Attention également, comme pour le base->read() à l'ordre des colonnes pouvant écraser les colonnes homographes :
<?php
$liste = $base−>select("select table1.id, table2.id from table1, table2");
?>
Renverra une liste d'une seule valeur destable2.id.

select(profondeur)

Si le premier paramètre, précédent la requête, est une valeur numérique, elle indique une notion de groupe sur le résultat renvoyé.
La valeur entière indique sur combien de champ du select, le regroupement se fera.
Si la valeur entière est inférieur à 2, elle reproduit le même principe que s'il n'y avait pas de profondeur.
<?php
// regroupement sur la ville et le nom
$liste = $base−>select(2, "select ville, nom, age, id from table");
?>
Renvoie :
[
"Annecy" => [
"Enzo" => [ "ville" => "Annecy", "nom" => "Enzo", "age" => "35", "id" => "42" ],
"Yvan" => [ "ville" => "Annecy", "nom" => "Yvan", "age" => "35", "id" => "500" ],
"Yannis" => [ "ville" => "Annecy", "nom" => "Yannis", "age" => "20", "id" => "501" ],
],
"La Paz" => [
"Patricia" => [ "ville" => "La Paz", "nom" => "Patricia", "age" => "28", "id" => "74" ]
]
]
<?php
// regroupement sur la ville, puis l'âge, puis le nom
$liste = $base−>select(3, "select ville, age, nom, id from table");
?>
Renvoie :
[
"Annecy" => [
"35" => [
"Enzo" => [ "ville" => "Annecy", "age" => "35", "nom" => "Enzo", "id" => "42" ],
"Yvan" => [ "ville" => "Annecy", "age" => "35", "nom" => "Yvan", "id" => "500" ],
],
"20" => [
"Yannis" => [ "ville" => "Annecy", "age" => "20", "nom" => "Yannis", "id" => "501" ],
],
],
"La Paz" => [
"28" => [
"Patricia" => [ "ville" => "La Paz", "age" => "28", "nom" => "Patricia", "id" => "74" ]
]
]
]

Enfin, comme pour la majorité des méthodes deBase, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

Base : list

Même principe que pour le$base−>select mais sans notion de nombre de champ : renvoie toujours une liste indexée (donc pas de risque d'écrasement des doublons).
<?php
$liste = $base−>list("select ville, nom, age, id from table");
?>
Renvoie :
[
0 => [ "ville" => "Annecy", "nom" => "Enzo", "age" => "35", "id" => "42" ],
1 => [ "ville" => "Annecy", "nom" => "Yvan", "age" => "35", "id" => "500" ],
2 => [ "ville" => "Annecy", "nom" => "Yannis", "age" => "20", "id" => "501" ],
3 => [ "ville" => "La Paz", "nom" => "Patricia", "age" => "28", "id" => "74" ],
]
Attention :list() renverra toujours un tableau d'une structure nommé, même avec un seul élément.
<?php
$liste = $base−>list("select nom from table");
?>
Renvoie :
[
0 => [ "nom" => "Enzo" ],
1 => [ "nom" => "Yvan" ],
2 => [ "nom" => "Yannis" ],
3 => [ "nom" => "Patricia" ],
]
Si le retour doit être une liste indexé de chaîne de caractère, il faut utiiser le select()
<?php
$liste = $base−>select("select nom from table");
?>
Renvoie :
[
0 => "Enzo",
1 => "Patricia",
2 => "Yvan",
]

Attention également, comme pour le base->read() à l'ordre des colonnes pouvant écraser les colonnes homographes :
<?php
$liste = $base−>list("select table1.id, table2.id from table1, table2");
?>
Renverra une liste d'une seule valeur destable2.id.

list(profondeur)

Même principe de regroupement que pour le$base−>select, mais avec la profondeur pouvant partir de 1 :
<?php
// regroupement sur la ville
$liste = $base−>list(1, "select ville, age, nom, id from table");
?>
Renvoie :
[
"Annecy" => [
0 => [ "ville" => "Annecy", "age" => "35", "nom" => "Enzo", "id" => "42" ],
1 => [ "ville" => "Annecy", "age" => "35", "nom" => "Yvan", "id" => "500" ],
2 => [ "ville" => "Annecy", "age" => "20", "nom" => "Yannis", "id" => "501" ],
],
"La Paz" => [
0 => [ "ville" => "La Paz", "age" => "28", "nom" => "Patricia", "id" => "74" ],
],
]
Avec une profondeur de 2 :
<?php
// regroupement sur la ville et l'âge
$liste = $base−>list(2, "select ville, age, nom, id from table");
?>
Renvoie :
[
"Annecy" => [
"35" => [
0 => [ "ville" => "Annecy", "age" => "35", "nom" => "Enzo", "id" => "42" ],
1 => [ "ville" => "Annecy", "age" => "35", "nom" => "Yvan", "id" => "500" ],
],
"20" => [
0 => [ "ville" => "Annecy", "age" => "20", "nom" => "Yannis", "id" => "501" ],
],
],
"La Paz" => [
"28" => [
0 => [ "ville" => "La Paz", "age" => "28", "nom" => "Patricia", "id" => "74" ],
],
],
]
Avec une profondeur de 3 :
<?php
// regroupement sur la ville, puis l'âge, puis le nom
$liste = $base−>list(3, "select ville, age, nom, id from table");
?>
Renvoie :
[
"Annecy" => [
"35" => [
"Enzo" => [
0 => [ "ville" => "Annecy", "age" => "35", "nom" => "Enzo", "id" => "42" ],
],
"Yvan" => [
1 => [ "ville" => "Annecy", "age" => "35", "nom" => "Yvan", "id" => "500" ],
],
],
"20" => [
"Yannis" => [
0 => [ "ville" => "Annecy", "age" => "20", "nom" => "Yannis", "id" => "501" ],
]
],
],
"La Paz" => [
"28" => [
"Patricia" => [
0 => [ "ville" => "La Paz", "age" => "28", "nom" => "Patricia", "id" => "74" ],
],
],
],
]

Enfin, comme pour la majorité des méthodes deBase, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

Base : Enregistrement

On peut enregistrer des données, en deux étapes :
  1. Préparation des données, avec la méthode set().
  2. Enregistrement des données, avec les méthodes insert(), update() ou modify().

set()

Cette méthode permet de préparer les valeurs d'un enregistrement, avec 3 signatures possibles :
  1. Soit en passant le nom du champ et sa valeur :
    1. Nom du champ
    2. Valeur
    3. Format optionnel
  2. Soit en passant une structure, contenant le nom des champs en clés.
    1. Structure
    2. Formatages optionnels
    3. Filtre optionnel des champs
  3. Soit aucun paramètre afin d'initialiser d'éventuel champ mémorisé.

Si besoin d'un filtrage sans formatage, le formatage peut contenir une valeur fausse (false, 0,[],"", …).
PlusieursBase−>set() peuvent se faire, mais c'est lors de l'utilisation des méthodes (insert(), update() ou modify()) que l'enregistrement final s'effectuera.
<?php
// on prépare l'enregistrement, champ par champ
$base−>set("champ", "valeur");
$base−>set("champ2", 42);
$base−>set("champ3", null); // pour passer le champ à NULL
// ou champ par champ avec option
$base−>set("champ", "valeur", "d");

// ou en structure
$base−>set( [ "champ" => "valeur" ] );
// ou en structure avec option
$base−>set( [ "champ" => "valeur" ], [ "champ" => "d" ] );
// ou en structure avec filtrage
$base−>set( [ "champ" => "valeur", "champ_non_mémorisé" ], false, [ "champ" ] );
// ou aucune mémorisation si trop (ou pas assez) de filtrage
$base−>set( [ "champ" => "valeur", "champ_non_mémorisé" ], false, [ ] );

// ou en partant d'un autre enregistrement
$base−>set( $base−>read("select fieldX, valueX from une_autre_table where id = 42") );
?>

Le fait de passer plusieurs fois le même nom de champ, ne retiendra que la dernier en écrasant l'ancienne valeur :
<?php
$base−>set("champ", 8);
$base−>set("champ", 42); // c'est la valeur 42 qui sera enregistrée dans le champ
?>

Il est possible de passer le nom (ou l'alias) de la table dans le nom du champ.
<?php
$base−>set("table.champ", $valeur);
?>

Il est possible de spécifier un formatage de la valeur avant enregistrement :
<?php
$base−>set($champ, $valeur, "i"); // formate une valeur numérique entière
$base−>set($champ, $valeur, "f"); // formate un flottant
$base−>set($champ, $valeur, "d"); // formate une date
$base−>set($champ, $valeur, "t"); // formate une heure
$base−>set($champ, $valeur, "dt"); // formate une date et heure
$base−>set([ 'codepostal' => "75000" ], [ 'codepostal' => "i" ] ); // formatage avec passage de structure
?>
Voir les substitutions pour plus d'explication sur le formatage.

Par défaut, c'est le type de champ en base de donnée, qui servira pour le formatage de la valeur, avec les variantes suivantes :
  • Une valeur chaîne de caractères sera protégée au niveau des apostrophes.
  • Une valeur chaîne de caractères vide sur un champ de type clé étrangère acceptant leNull sera transformé enNull.
  • Une valeur numérique ou un type attendu numérique (INT,DECIMAL,TIMESTAMP,BIT, …) est toujours passés sous forme numérique (avec prise en compte de la virgule française si présente).
  • Une valeurBooléan est soit transformée en chaîne de caractère "true" ou "false", soit en une valeur numérique 1 ou 0.
  • Une valeurNULL passée ne sera pas transmise à la base de donnée si le champ n'accepte pas lesNULL.
  • Une valeurarray sera convertie pour un typeSET ou un typeJSON.
  • Une valeurobject sera convertie pour un typeJSON.
  • Tout autre cas sera ignoré.
Exemple:
<?php
$base−>sql("
CREATE TABLE IF NOT EXISTS `exemple_table` (
`id` INT(20) NOT NULL AUTO_INCREMENT,
`texte` VARCHAR(255) NOT NULL,
`pointeur` INT(20) NULL COMMENT 'foreign key vers table_cible.id',
`numerique` DOUBLE(10) NOT NULL,
`etat` BOOLEAN NOT NULL,
`null_oui` VARCHAR(255) NULL,
`null_non` VARCHAR(255) NOT NULL,
`ensemble` SET('vert', 'orange', 'rouge') NOT NULL,
`tableau` JSON NOT NULL,
`json` JSON NOT NULL,
`autre` VARCHAR(255) NOT NULL,
CONSTRAINT `exemple_pointeur` FOREIGN KEY (`pointeur`) REFERENCES `table_cible`(`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4 COLLATE=UTF8MB4_UNICODE_CI;
");
$base−>set([
'texte' => "abc'def",
'pointeur' => '',
'numerique' => "−3,14",
'etat' => "42",
'null_oui' => NULL,
'null_non' => NULL,
'ensemble' => ['vert', 'rouge'],
'tableau' => [ 42, "chaine" ],
'json' => [ 'abc' => 123 ],
'autre' => fopen('un fichier'),
]);
$base−>insert('exemple_table');
?>
Donnera une requête de type :
INSERT INTO `exemple_table`
(`texte`, `pointeur`, `numerique`, `etat`, `null_oui`, `ensemble`, `tableau`, `json` ) VALUES
('abc\'def', NULL, −3.14, 1, NULL, 'vert,rouge', '[42,\'chaine\']', '{'abc':123}')


Après chaque requête d'enregistrement (insert(), update() ou modify()), ce buffer est réinitialisé automatiquement, mais il est possible d'initialiser manuellement ce buffer en passant aucun paramètre à cette méthodes :
<?php
$base−>set("champ", "valeur");
$base−>set(); // réinitialise le stockage des champs précédents
?>

insert()

Cette méthode permet d'ajouter un nouvel enregistrement dans la base de donnée.
Sa signature est :
  1. Un seul paramètre : le nom de la table.
  • Si l'ajout est réussi, le retour correspond auid du nouvel enregistrement (toujours positif non-nul), sinon à false.
    <?php
    $id = $base−>insert("table");
    ?>

Comme pour la majorité des méthodes deBase, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

update()

Cette méthode permet la modification d'un ou plusieurs enregistrements déjà présent.
Sa signature est :
  1. Le nom de la table.
  2. La clausewhere, sous 4 syntaxes différentes :
    1. un "where" classique avec ou sans paramètres supplémentaire de substitutions.
    2. une valeur numérique positif non-nul représentant son "id".
    3. un tableau de valeur numérique positif non-nul représentant l'ensemble des "id".
    4. un tableau de nom de champ qui seront repris des valeurs passées au $base->set() (et qui seront retirées de la requête de mise-à-jour).
  • Renvoie le nombre d'enregistrement modifié, qui peut être égale à 0 si aucun enregistrement n'a été trouvé ou modifié (colonne identique, …), ou false si erreur
Exemples:
<?php
// where classique
$base−>set('titre', "titre 1");
$nombre = $base−>update("table", 'champ = {s}', "valeur");
if ($nombre === false) {
trace('Erreur', $base−>error(true));
} else {
trace('Nombre d'enregistrement mis à jour', $nombre);
}

// where par son id unique
$base−>set(
'titre', "titre 1");
$one = $base−>update("table", $id);

// where tableau d'id
$base−>set(
'titre', "titre 1");
$count = $base−>update("table", [$id, $id2]);

// where tableau de champ
$base−>set(
'titre', "titre 1");
$base−>set(
'city', "Annecy");
$count = $base−>update("table", [
'city']);
?>

Comme pour la majorité des méthodes deBase, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

modify()

En modifiant un enregistrement déjà présent ou en ajoutant un nouvel enregistrement si non-présent.

Sa signature est :
  1. Le nom de la table.
  2. La clausewhere, sous 3 syntaxes différentes :
    1. une valeur numérique positif non-nul représentant son "id".
    2. un tableau de nom de champ qui seront repris des valeurs passées au $base->set() (et qui seront retirées de la requête d'insertion si présent en base).
    3. la syntaxe classique d'une clause where.

Attention : la modification s'effectuera sur l'ensemble des enregistrements répondant à la clausewhere.

Renvoie le ID si la modification est réussie, sinon false.
Attention, à la différence d'un update(), cette méthode ne renvoie pas le nombre d'enregistrement, mais bien le ID de l'enregistrement modifié (ou créé) ou false si erreur.
<?php
$base−>set('champ', "valeur");
$id = $base−>modify("table", $id);
if (! $id)
trace("Erreur lors de l'enregistrement", $base−>error(true));

$base−>set('nom', "Enzo");
$base−>set('code', "42");
$base−>set('champ', "valeur");
$id = $base−>modify("table", ['nom', 'code']);
if (! $id)
trace("Erreur lors de l'enregistrement", $base−>error(true));
?>
Cela correspond plus ou moins à faire unif (read("id")) update("table", "id"); else insert("table sans le id");

Remarque : Les $base->set() précédant peuvent contenir un champ 'id', qui ne sera pas utilisé par cette fonction, afin de ne pas rentrer en conflit avec la colonne 'id' de la base.
<?php
// sur un exemple d'une table possédant qu'un seul enregistrement de ID 1
$record = $base−>read("select * from table where id = 1"); // récupère cet enregistrement, son ID à 1 compris
$base−>set($record); // prépare son enregistrement
$id = $base−>modify("table", 42); // essaye de modifier l'enregistrement 42 qui dans cet exemple n'existe pas
// cette table contient maintenant 2 enregistrements de ID 1 et 2 ($id == 2)
?>
Comme pour la majorité des méthodes deBase, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

delete()

Effacement d'un enregistrement.
Sa signature est :
  1. Le nom de la table
  2. La clausewhere ou le "id".
Renvoie le nombre d'enregistrement effacé, ou false si erreur
Attention, ce nombre peut être égale à zéro, dans le cas ou le where n'est pas trouvé.
<?php
$ok = $base−>delete("table", "where");
if ($nombre === false)
trace('Erreur', $base−>error(true));
else<br> trace('Nombre d'enregistrement effacé', $nombre);
?>

Comme pour la majorité des méthodes deBase, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

deletes()

Si l'on veut effacer toute la table, il est préférable de faire undeletes() Sa signature est :
  1. Le nom de la table

Cette méthode utilise la requête SQLTRUNCATE, qui ne vérifie pas les clés étrangères et réinitialise l'auto-incrémentation.

Exemple:
<?php
$ok = $base−>deletes("table");
// inutile de faire $base−>increment("table", 1);
?>

Comme pour la majorité des méthodes deBase, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

Base : Substitution


Les requêtes peuvent contenir des substitutions, permettant le formatage (et dû coup la protection des injections SQL de données extérieurs à la requête).
Les substitutions reprennent le mécanisme des "printf", mais avec un marquage sous forme de lettre encadrés d'accolades, qui sécurisera formatera et placera les paramètres passées à la fonction, dans la requête.
<?php
$record = $base−>read("select * from table where id = {i} and nom = {s} and ville in {as}", "42", "Enzo", ["Annecy"]);
?>
Les substitutions possibles sont :
  • {a} : insère un tableau (array) encadré de parenthèse. Cette lettre doit être associé avec une des lettres suivantes. À noter que si la valeur passée n'est pas un tableau, il sera considéré néanmoins comme un tableau d'un seul élément.
    <?php
    $base−>read("select * from client where ville in {as}", [ "Annecy", "La Paz" ]);
    // est identique à
    $base−>read("select * from client where ville in ( 'Annecy', 'La Paz' )");

    $base−>select("select id from client where ville in {as}", "Annecy");
    // est identique à (même si "Annecy" n'est pas un tableau)
    $base−>select("select id from client where ville in ( 'Annecy' )");
    ?>
  • {b} ou {sb} ou {bs} : insère une chaîne "commençant par" (begin string), en protégeant les caractères '%' et '_', et en ajoutant le symbole pourcentage (%) en fin de chaîne, pour la recherche parlike.
    <?php
    $base−>read("select * from client where ville like {sb}", "a\"b'c%d_e");
    // est identique à
    $base−>read("select * from client where ville like 'a\"b\\'c\\%d\\_e%'");
    ?>
  • {c} ou {sc} ou (cs) : insère une chaîne "contenant" (containt string), en protégeant les caractères '%' et '_', et en encadrant la chaîne du symbole pourcentage (%), pour la recherche parlike.
    <?php
    $base−>read("select * from client where ville like {sc}", "a\"b'c%d_e");
    // est identique à
    $base−>read("select * from client where ville like '%a\"b\\'c\\%d\\_e%'");
    ?>
  • {d} : insère une date, au format YYYY-MM-DD.
    <?php
    $base−>read("select * from client where jour = {d}", '030201');
    $base−>read("select * from client where jour = {d}", '20010203');
    $base−>read("select * from client where jour = {d}", date(2001, 2, 3));
    $base−>read("select * from client where jour = {d}", '1/2/3');
    $base−>read("select * from client where jour = {d}", '2003−2−01');
    $base−>read("select * from client where jour = {d}", '1/2/3 4:5:6');
    $base−>read("select * from client where jour = {d}", '2003−02−01 04:05:06');
    // sera toujours identique à
    $base−>read("select * from client where jour = '2001−02−03'");
    ?>
  • {dt} : insère une date et heure, au format YYYY-MM-DD HH:MM:SS. Utilise la même syntaxe que pour {d}.
  • {e} ou {se} ou (es) : insère une chaîne "finissant par" (end string), en protégeant les caractères '%' et '_', et en ajoutant le symbole pourcentage (%) en début de chaîne, pour la recherche parlike.
    <?php
    $base−>read("select * from client where ville like {se}", "a\"b'c%d_e");
    // est identique à
    $base−>read("select * from client where ville like '%a\"b\\'c\\%d\\_e'");
    ?>
  • {f} : insère un flottant.
  • {i} : insère un entier (integer).
    <?php
    $base−>read("select * from client where id = {i}", " 42 ");
    // est identique à
    $base−>read("select * from client where id = 42");
    ?>
  • {n} : insère une chaîne ounull si le paramètre est vide ou nul. Cette lettre doit être associé avec une des lettres suivantes.
    <?php
    $base−>read("select * from client where ville = {sn}", '');
    // est identique à
    $base−>read("select * from client where ville = NULL");
    // alors que
    $base−>read("select * from client where ville = {s}", '');
    // est identique à
    $base−>read("select * from client where ville = ''");
    ?>
  • {p} ou {sp} ou {ps} : insère une chaîne de caractère (protected string), en protégeant les caractères '%' et '_' pour qu'ils se comportent comme de simples caractères (et non des méta-caractères) avec unlike.
    <?php
    $base−>read("select * from client where ville like {sl}", "a\"b'c%d_e");
    // est identique à
    $base−>read("select * from client where ville like 'a\"b\\'c\\%d\\_e'");
    ?>
  • {s} : insère une chaîne de caractère (string), encadré de guillemet.
    <?php
    $base−>read("select * from client where ville = {s}", "a\"b'c%d_e");
    // est identique à
    $base−>read("select * from client where ville = 'a\"b\\'c%d_e'");

    // attention à plutôt utiliser {b}, {c}, {e} ou {p} avec un like plutôt qu'un {s}
    $base−>read("select * from client where ville like {s}", "%a_z%");
    // conservera les méta−caractères
    $base−>read("select * from client where ville like '%a_z%");
    // alors qu'avec par exemple {p}
    $base−>read("select * from client where ville like {p}", "%a_z%");
    // protégera les caractères
    $base−>read("select * from client where ville like '\%a\_z\%");
    ?>
  • {t} : insère une heure (time) au format HH:MM:SS.
    <?php
    $base−>read("select * from client where seconde = {t} && minute = {t} && heure = {t} && alarme = {t}", 62, '61:63', '1h2m003', "5°4'3\"2");
    // est identique à
    $base−>read("select * from client where seconde = '00:01:02' && minute = '01:02:03' && heure = '01:02:03' && alarme = '124:03:02'");
    ?>
  • {v} : utilisé pour les alias, ne conserve que les caractères alphanumérique.
    <?php
    $base−>read("select ville as {v} from client", "=az_09+42");
    // est identique à
    $base−>read("select ville as az_09 from client");
    ?>
  • {w} : insère que des noms de tables, champs, …, contenant uniquement des lettres et chiffres ou "_" ou "-" et encadre de guillemet oblique`.
    <?php
    $base−>read("select {w} from client", "=az_09+42");
    // est identique à
    $base−>read("select `az_09` from client");
    ?>
  • {x} : insère sans formatage ni contrôle. Bien contrôler la valeur pour éviter les attaques de type SQL Injection.
    <?php
    // si $input = "Annecy"
    $base−>read("select * from client where ville = {x}", "'" . $input . "'");
    // est identique à
    $base−>read("select * from client where ville = 'Annecy'");
    // mais si $input = "'; SHOW TABLE STATUS; DELETE FROM client; SELECT * FROM client where ville = '"
    $base−>read("select * from client where ville = {x}", "'" . $input . "'");
    // vous risquez d'avoir une fuite de la structure de vos tables et l'effacement d'une table
    $base−>read("select * from client where ville = ''; SHOW TABLE STATUS; DELETE FROM client; SELECT * FROM client where ville = ''");
    ?>
  • {...} : conserve si le paramètre est vrai, sinon supprime cette substitution.
    <?php
    $base−>read("select * from table where 1{ && nom = {s}}{ && prenom = {s}}", $nom != '', $nom, $prenom != '', $prenom);
    ?>
  • {? ...} : même principe, mais ne dépile pas le paramètre, afin de l'utiliser dans la substitution.
    <?php
    $base−>read("select * from table where 1{? && nom = {s}}{? && prenom = {s}}", $nom, $prenom);
    ?>
  • {:clé} : insert non pas un paramètre, mais une valeur trouvé avec la commande value dans l'annuaire.
    // Avec cette valeur dans l'annuaire
    value:client ma_table
    <?php
    // ce format SQL contenant des substitutions
    $base−>read(
    "select * from {:client} where 1 {and validate = 42} {?and name like {sb}} and {w} in {as}",
    false,
    "En",
    "level",
    [−001, "", null, "etc", "('\"%_)"]
    );

    // est identique à celle−là
    $base−>read("select * from ma_table where 1 and name like 'En%' and `level` in ('−1', '', '', 'etc', '(\'\"%_)')");
Il est possible de spécifier un paramètre (sans passer au paramètre suivant) en précédent la lettre de formatage (a,i,sp, …), du numéro de la position du paramètre à insérer :
<?php
// le {1s} permet de réutiliser le 1er paramètre "Annecy" sans passer au paramètre suivant
$base−>read(
"select * from table where ville = {s} || commune = {1s} || departement = {s}",
"Annecy",
"Haute−Savoie"
);
// est identique à
$base−>read("select * from table where ville = 'Annecy' || commune = 'Annecy' || departement = 'Haute−Savoie'");
?>
Attention : Si le nombre de paramètre passé est supérieur au nombre de substitution, ces paramètres seront ignorés.
Et inversement si le nombre de paramètre passé est inférieur au nombre de substitution, ces dernières ne seront pas traités et resteront sous forme de lettre encadré d'accolades (ce qui peut être utile pour stocker par exemple du JSon en base de donnée) :
<?php
// cette requête renverra tous les enregistrements dont la colonne "value" commence par : France
$list = $base−>list("select * from {w} like {sb}, 'value', "France");

// alors que celle−ci renverra tous les enregistrements dont la colonne "value" égale à la constante : {sb}
$list = $base−>list(
"select * from {w} like '{sb}'", 'value');
?>

Base : Requête statique

Les requêtes SQL utilisé l'ensemble de ces méthodes, peuvent être stoquées dans un annuaire.
<annuaire>
// Avec cette valeur dans l'annuaire
sql:requete_42 select * from table where id = {i};
</annuaire>
<?php
// la requête :
$base−>read("requete_42", 64);

// sera remplacé par :
$base−>read("select * from table where id = 64");
?>

Base : Simulation

La procédurebase−>simulate() permet de simuler l'ensemble des requêtes de modification (insert, update, delete, sql), en les désactivant au niveau de la base de données, afin de vérifier que tout est fonctionnel en mode développement sans perturber la base.
Sa signature est :
  • Une chaîne de caractère qui sera utilisée en titre sur la console du navigateur.
  • Ou faux permettant de désactiver la simulation et revenir dans un mode normal.
Si une tracing est présente sur une requête, c'est sa trace qui sera affichée dans la console, avec le sufixe "[simulate]".
Ce paramètre est identique au titre de la méthode trace(), elle accepte donc les mêmes fonctionnalités de formatage.
Exemple :
<?php
// active la simulation
$base−>simulate("<i>Simulateur");

// n'enregistrera rien mais affichera les requêtes en Console
$base−>set("champ", "valeur");
$base−>insert("table");

// désactive la simulation
$base−>simulate(false);

// enregistrera le nouveau enregistrement
$base−>set("champ", "valeur");
$base−>insert("table");
?>

Ce mécanisme n'interfere pas avec les requêtes en lecture (select, desc, …), et ne gère pas les erreurs SQL en renvoyant toujours la valeur 0 pour les insert, delete, ….

Base : Requêtes

On peut également faire des requêtes particulières.

prepare()

Permet de préparer une requête ou partie de requête à insérer ensuite dans une des méthodesread,select, oulist, et reprenant leur syntaxe.
<?php
$sql = $base−>prepare("from ville = {s}", $ville);
$noms = $base−>select("select nom " . $sql . " order by nom");
$ages = $base−>select("select age {x} order by age", $sql);
?>

Cette méthode permet de préparer en amont des parties ou la totalité d'une requête SQL, mais, par sa signature, peut remplacer n'importe quelle méthode de lecture (telle que select() ou read()) pour le passer dans un trace par exemple.
<?php
trace('trace', $base−>prepare("select nom from ville = {s} order by nom", $ville));
$liste_ville = $base−>select ("select nom from ville = {s} order by nom", $ville);
?>

desc()

Permet d'avoir la description des tables et champs, avec les signatures possibles suivantes :
  1. paramètre représentant le nom de la table :
    • Si absent ou vide : renvoie l'ensemble des tables
    • Si la chaîne contient '%' : renvoie uniquement les tables commençant ou finissant par cette chaîne.
    • Si la chaîne ne contient pas '%' : renvoie les informations sur cette table.
  2. paramètre permettant d'affiner le retour d'information :
    • false (par défaut) : juste le nom des tables ou des colonnes.
    • true : renvoie un tableau d'information sur les tables ou colonnes.
    • chaine : renvoie la liste des énumérés (enum ou set) d'un champs.

Voici en détails les différentes signatures :
desc()
Renvoie la liste des tables.
<?php
$tables = $base−>desc('');
?>
Renvoie (par exemple) :
[ "table 1", "table 2", "tablex" ]
desc('', true)
Renvoie la description des tables.
<?php
$tables = $base−>desc('', true);
?>
Renvoie (par exemple) :
[
"table 1" => ['Name'=> "table1", 'Engine'=> "InnoDB", 'Auto_increment'=> 1, 'Comment'=> "Commentaire", 'Collation'=> "utf8mb4_unicode_ci", 'Data_length'=> 16384, …],
"table 2" => […],
"table" => […]
]
desc('filtre%')
Renvoie la liste des tables correspondant au filtre.
<?php
$tables = $base−>desc('table %');
?>
Renvoie (par exemple) :
[ "table 1", "table 2" ]
desc(table, false)
Renvoie la liste des champs d'une table précise.
<?php
$champs = $base−>desc('table');
?>
Renvoie (par exemple) :
[ "id", "nom", "ville" ]
desc(table, true)
Renvoie la description des champs d'une table précise.
<?php
$champs = $base−>desc('table', true);
?>
Renvoie (par exemple) :
[
"id" => ['Field' => "id", 'Type' => "int(11)", 'Key' => "PRI", 'Null' => "NO", 'Default' => null, 'Collation' => null, 'Comment' => "", 'Privileges' => "select,insert,update,references", 'Extra' => "auto_increment"],
"nom" => ['Field' => "nom", 'Type' => "varchar(100)", 'Key' => "", 'Null' => "NO", 'Default' => null, 'Collation' => null, 'Comment' => "", 'Privileges' => "select,insert,update,references", 'Extra' => ""],
"ville" => ['Field' => "ville", 'Type' => "varchar(100)", 'Key' => "", 'Null' => "NO", 'Default' => null, 'Collation' => null, 'Comment' => "", 'Privileges' => "select,insert,update,references", 'Extra' => ""]
]
desc(table, enum)
Renvoie l'énuméré d'un champ de typeenum ouset.
<?php
$champs = $base−>desc('table', "ville");
?>
Renvoie :
[ "Annecy", "La Paz" ]

foreignKey()

Renvoie la liste des clés étrangères des tables demanadées, regroupé par table.
Sa signature est :
  • Une chaîne représentant le nom de la table, renverra une structure des clés étrangères de cette table.
  • Une chaîne se terminant par "%", renverra une structure des clés étrangères des tables commençant par cette chaîne de caractère.
  • Une liste de noms de table : renverra une structure de ces tables, contenant la structure de leurs clés étrangères.
  • Aucun paramètre : renvoie l'ensemble des tables.
Les tableaux renvoyés contiennent :
  • name: nom de la contrainte (unique au sein de la base de donnée), cette information est également utilisée comme index à ce tableau.
  • table: nom de la table source
  • source: nom de la colonne source
  • foreign: nom de la table cible
  • key: nom de la colonne cible (généralement id)
Exemples :
<?php
$unique = $base−>foreignKey('table');
$commencant = $base−>foreignKey('tab%');
$multiple = $base−>foreignKey(['table', 'personnels']);
$tout = $base−>foreignKey();
?>
Pourrait renvoyer :
$unique = [
'nom_to_personnel' => [ 'name' => "nom_to_personnel", 'table' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville_to_villes' => [ 'name' => "ville_to_villes", 'table' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
];
$commencant = [
'nom_to_personnel' => [ 'name' => "nom_to_personnel", 'table' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville_to_villes' => [ 'name' => "ville_to_villes", 'table' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
];
$multiple = [
'table' => [
'nom_to_personnel' => [ 'name' => "nom_to_personnel", 'table' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville_to_villes' => [ 'name' => "ville_to_villes", 'table' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
],
'personnels' => [
'telephone_to_annuaire' => [ 'name' => "telephone_to_annuaire", 'table' => "personnels", 'source' => "telephone", 'foreign' => "annuaire", 'key' => "id" ],
],
];
$tout = [
'table' => [
'nom_to_personnel' => [ 'name' => "nom_to_personnel", 'table' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville_to_villes' => [ 'name' => "ville_to_villes", 'table' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
],
'personnels' => [
'telephone_to_annuaire' => [ 'name' => "telephone_to_annuaire", 'table' => "personnels", 'source' => "telephone", 'foreign' => "annuaire", 'key' => "id" ],
],
'societes' => [
'directeur_to_personnel' => [ 'name' => "directeur_to_personnel", 'table' => "societes", 'source' => "directeur", 'foreign' => "personnels", 'key' => "id" ],
],
];

increment()

Il est possible de consulter ou modifier l'auto incrémentation d'une table, avec cette méthode, avec en signature :
  1. table : le nom de la table.
  2. incrément : la nouvelle valeur numérique entière positive de l'incrémentation outrue pour le remettre à 1 ou faux (ou rien) pour juste une lecture.
  • Renvoie la valeur de l'auto incrémentation.

À la différence de la méthode mysqli::$insert_id, cette méthode permet de modifier la valeur de l'auto-incrémentation et n'a pas besoin de faire requête SQL d'insertion pour renvoyer la valeur.

Exemple :
<?php
$table = 'table1';

// lecture
$increment = $base−>increment($table);
$increment = $base−>increment($table, false);

// incrément manuellement
$base−>increment($table, increment + 1);

// purge
$id = $base−>read("select max(id) from {w}", $table);
$base−>increment($table, increment + 1);

// ré−initialise
$base−>delete($table, 'true');
$base−>increment($table, 1);
$base−>increment($table, true);
?>

primary()

Cette méthode permet de renvoyer le nom de la colonne primaire d'une table, généralement "id" mais qui peut être différent.
Sa signature est :
  1. Nom de la table
  2. Protection : si vrai, encapsule le nom d'anti-slash.
    <?php
    $id = $base−>read("select {w} from ma_table", $base−>primary("ma_table"))
    // renverra généralement : id
    $id = $base−>read("select {w} from ma_table", $base−>primary("ma_table", true))
    // renverra généralement un `id`
    ?>

sql()

Il est possible de produire n'importe quelles requêtes SQL spécifiques.
<?php
$base−>sql('ma requete sql');
$base−>sql("alter table {w} comment {s}", $ma_table, $mon_commentaire);
?>

Comme pour la majorité des méthodes deBase, il est possible d'ajouter une trace pour le développeur, en la plaçant en premier paramètre voir plus d'information trace().

requested()

Renvoie la dernière requête (suite à un select, read, update, …, sql()).

host()

Renvoie le type de connexion utilisé (identique à mysql->host_info).

error()

Il est possible de tester l'erreur suite à une requête :
Sa signature comporte un seul élément :
  • false ou 0 : renvoie la valeur numérique de l'erreur ou 0 si aucune erreur.
  • true ou 1 : renvoie la chaîne de caractère décrivant l'erreur, ou vide si aucune erreur.
  • null ou aucun paramètre : renvoie les deux valeurs : renvoie un tableau contenant différentes informations sur l'erreur :
    • 0: le numéro de l'erreur ou du warning.
    • 1: le texte en clair
    • 2 et plus : les différentes informations dynamiques relatives à cette erreur, tel que 'column', …
    • nom : ces mêmes informations dynamiques sont accessibles via leur nom.
Ces informations peuvent être disponible directement en passant l'index ou le nom de l'informations recherchées.

Exemples:
<?php
$base−>error(false); // renverra le numéro d'erreur ou 0 si pas d'erreur
$base−>error(true); // renverra "message" ou '' si pas d'erreur
$base−>error(); // renverra [numéro, "message", …] ou [0, ''] si pas d'erreur
?>
Note : l'erreur peut être un "warning", dans ce cas, le numéro sera négatif.
Exemple un débordement de taille d'une colonneCHAR provoquera juste un "warning"[−1265, "data truncated", "field", 1, 'column' => "field", 'row' => 1].

Rappel : toutes les erreurs provoquent un mécanisme de journalisation et d'affichage sur la Console (en mode développement), qu'il est possible d'annihiler avec la fonction $base->tracing().

export()

Plume intègre un mécanisme d'exportation, avec cette méthode, de signature :
  1. Tables: nom de la ou des tables, de syntaxe :
    • Une chaîne de caractère indiquant le nom et acceptant le méta caractère '%' permettant de spécifié "commençant par". Exemple:"table%".
    • Une liste des noms des tables. Exemple["table1", "tables%"].
    • Une structure avec en clé, le nom du tableau, et en valeur, la clause "where" d'un SELECT SQL.
  2. Sortie: où enregistré l'exportation, avec en syntaxe :
    • Le nom d'un fichier : qui centralisera l'ensemble des tables à exporter.
    • Le nom d'un fichier avec le caractère '%' : qui sera remplacé par le nom de la table, afin de créer autant de fichier que de table (uniquement avec PLUME_EXPORT_FILE).
  3. Format de sortie à produire : constantes numériques à additionner avec l'opération "|" :
Génère un export :
PLUME_EXPORT_FORMAT_SQLSous forme de requête SQL.
PLUME_EXPORT_FORMAT_CSVExploitable par les tableurs.
PLUME_EXPORT_FORMAT_TXTExploitable par les traitements de texte.
PLUME_EXPORT_FORMAT_XMLExploitable par les CMS.
PLUME_EXPORT_FORMAT_TABLisible par les humains.
PLUME_EXPORT_FORMAT_HTMLExploitable par un navigateur.
PLUME_EXPORT_FORMAT_EXTSuivant l'extension du fichier :.sql,.csv,.txt,.xml,.tab ou.html ou si non-reconnu, utilisePLUME_EXPORT_FORMAT_TXT. (par défaut)
Type de sortie de l'export :
PLUME_EXPORT_OUT_FILEEnregistré dans un fichier, renseigné en 2ème paramètre. Si existant, l'export sera ajouté en fin de fichier. (par défaut)
PLUME_EXPORT_OUT_CREATEEnregistré dans un nouveau fichier, renseigné en 2ème paramètre, qui sera remis à zéro si existant.
PLUME_EXPORT_OUT_ZIPEnregistré dans un fichier, renseigné en 2ème paramètre, qui sera compressé en ".zip".
PLUME_EXPORT_OUT_LOADEnvoyé sous forme d'un fichier (avec les headers HTTP). Si le nom du fichier en 2ème paramètre est vide, "export-" suivi de la date et heure sera utilisé.
PLUME_EXPORT_OUT_STRINGEnvoyé sous forme d'une chaîne de caractère. Le nom du fichier en 2ème paramètre n'est pas pris en compte.
En ajoutant les éventuelles options :
PLUME_EXPORT_OPTION_HEADRajoute l'information des colonnes (la structure ou les entêtes) aux données.
PLUME_EXPORT_OPTION_STRUCTExport uniquement les informations sur les colonnes, sans les données.
PLUME_EXPORT_OPTION_CONTROLPermet de remplacer les caractères de nouvelle ligne " " par un caractère visuel, le retour-chariot " " par et la tabulation par ou de protéger ces caractères de contrôle dans un export SQL.
PLUME_EXPORT_OPTION_REMOVEPermet d'inclure la commandeDROP ouDELETE à un export SQL.
Et en spécifiant si besoin l'internationalisation :
PLUME_EXPORT_LOCALE_USIndique une notion anglaise pour le séparateur de champ (,) et les valeurs numériques (3.14). (par défaut)
PLUME_EXPORT_LOCALE_FRIndique une notion française pour le séparateur de champ (;) et les valeurs numériques (3,14).

Cette méthode renvoie :
  • Dans le cas duPLUME_EXPORT_OUT_STRING, la chaîne de caractère de l'exportation.
  • Avec avec un nom de fichier contenant un "%", avec (PLUME_EXPORT_OUT_FILE,PLUME_EXPORT_OUT_CREATE,PLUME_EXPORT_OUT_ZIP) le nombre de fichier créé.
  • Dans tous les autres cas, untrue si export réussi, oufalse si erreur.

Si plusieurs fichiers sont demandés en sortie (avec le méta-caractère "%") avec l'optionPLUME_EXPORT_OUT_ZIP, un seul fichier.zip sera créé, contenant ces fichiers compressés.

Exemples:
<?php
// export dans le dossier "export−sql", plusieurs fichiers "sql" + le nom de la table, les tables commençant par "client1_"
$base−>export('client1_%', "export−sql/sql%.sql");

// export dans un fichier "export−sql−ma_table.sql" tout au format SQL
$base−>export('ma_table', "export−sql−%.sql", PLUME_EXPORT_FORMAT_SQL | PLUME_EXPORT_OPTION_HEAD);

// renvoie un fichier de format CSV français
$base−>export(['ma_table'], "export.text", PLUME_EXPORT_FORMAT_CSV | PLUME_EXPORT_OUT_LOAD | PLUME_EXPORT_LOCALE_FR);

// renvoie une chaîne de caractère de format TXT
$export = $base−>export('ma_table', '', PLUME_EXPORT_FORMAT_TXT | PLUME_EXPORT_OUT_STRING);

// export juste une partie des tables commençant par "mes_tables_", et compresse le tout dans un fichier mes_tables.zip
$base−>export(['mes_tables_%' => $base−>prepare("la_ville == {s} && le_nom is not null", $city)], PLUME_EXPORT_OUT_FILE | PLUME_EXPORT_OUT_ZIP);
?>

En partant des données suivantes :
Nom de la table : table_db
NomTypeValeur
idclé primaire entière auto-incrémentée123
texttexte de 50 indexé et obligatoireabc
numericréel à 20-4,2
dotcaractèrepoint
commacaractèrevirgule
semicoloncaractèrepoint-virgule
spacecaractèreespace
lowercaractèreinférieur
ampersandcaractèreesperluette
uppercaractèresupérieur
returncaractèreretour à la ligne
tabulationcaractèretabulation
backslashcaractèreanti-slash
quotecaractèreapostrophe
doublequotecaractèreguillemet
anullvaleur acceptant le NULLvaleur NULL

Nous aurons les résultats suivants (avec l'optionPLUME_EXPORT_OPTION_HEAD) :
Export SQL
L'export SQL génère un export sous forme de requête SQL exploitable par des bases de données.
En reprenant les données de l'exemple précédent, le resultat donnerait :
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+00:00";
CREATE TABLE `table_db` (
`id` int(20) NOT NULL,
`text` varchar(50) NOT NULL COMMENT 'nom de famille',
`numeric` float DEFAULT '20',
`dot` varchar(1) DEFAULT NULL COMMENT 'un point',
`comma` varchar(1) DEFAULT NULL COMMENT 'une virgule',
`semicolon` varchar(1) DEFAULT NULL COMMENT 'un point−virgule',
`space` varchar(1) DEFAULT NULL COMMENT 'un espace',
`lower` varchar(1) DEFAULT NULL COMMENT 'un chevron d''ouverture',
`ampersand` varchar(1) DEFAULT NULL COMMENT 'un esperluette',
`upper` varchar(1) DEFAULT NULL COMMENT 'un chevron de fermeture',
`newline` varchar(1) DEFAULT NULL COMMENT 'un retour à la ligne',
`tabulation` varchar(1) DEFAULT NULL COMMENT 'une tabulation',
`backslash` varchar(1) DEFAULT NULL COMMENT 'un anti−slash',
`quote` varchar(1) DEFAULT NULL COMMENT 'une apostrophe',
`doublequote` varchar(1) DEFAULT NULL COMMENT 'un guillemet',
`anull` text DEFAULT NULL COMMENT 'un champ à NULL',
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='table de démonstration';

INSERT INTO `table_db` (`id`, `text`, 'numeric', 'dot', 'comma', 'semicolon', 'space', 'lower', 'ampersand', 'upper', 'newline', 'tabulation', 'backslash', 'quote', 'doublequote', 'anull') VALUES
(123, "abc", −4.2, ".", ",", ";", " ", "<", "&", ">", "
", " (tabulation) ", "\", "'", '"', null),


ALTER TABLE `table_db`
ADD PRIMARY KEY (`id`),
ADD KEY `table_db_numeric` (`numeric`),
MODIFY `id` int(20) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT = 1234,
ADD CONSTRAINT `table_db_numeric` FOREIGN KEY (`numeric`) REFERENCES `constraint_db` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,

COMMIT;

Avec l'optionPLUME_EXPORT_OPTION_CONTROL, cela rendrait :
INSERT INTO `table_db` (`id`, `text`, 'numeric', 'dot', 'comma', 'semicolon', 'space', 'lower', 'ampersand', 'upper', 'newline', 'tabulation', 'backslash', 'quote', 'doublequote', 'anull')  VALUES
(123, "abc", −4.2, ".", ",", ";", " ", "<", "&", ">", " ", " ", "\", "'", '"', null),

Cet export est insensible aux localesPLUME_EXPORT_LOCALE_XX.
Export CSV
L'export CSV génère un export exploitable par les tableurs.
Les colonnes sont séparées par une virgule, et encadrés de guillemet si contient une virgule, un retour-chariot ou un guillemet, le guillemet est doublé, leNULL est remplacé par une valeur vide.

En reprenant les données de l'exemple précédent, le résultat rendrait :
id,text,numeric,dot,comma,semicolon,space,lower,ampersand,upper,newline,tabulation,backslash,quote,doublequote,anull
123,abc,−4.2,.,",",;, ,<,&,>,"
", ,,',"""",
Avec l'optionPLUME_EXPORT_OPTION_CONTROL, cela rendrait :
id,text,numeric,dot,comma,semicolon,space,lower,ampersand,upper,newline,tabulation,backslash,quote,doublequote,anull
123,abc,−4.2,.,",",;, ,<,&,>,⏎,⇥,,',"""",
Et avec l'optionPLUME_EXPORT_LOCALE_FR, cela rendrait :
id;text;numeric;dot;comma;semicolon;space;lower;ampersand;upper;newline;tabulation;backslash;quote;doublequote;anull
123;abc;−4,2;.;,;";"; ;<;&;>;"
"; ;;';"""";
Export TXT
L'export TXT génère un export exploitable par les traitements de texte.
Les colonnes sont séparées par une tabulation, le caractère de tabulation est remplacée par un espace.

En reprenant les données de l'exemple précédent, le resultat donnerait (avec le séparateur représenté ici par un " ▶︎ ") :
id ▶︎ text ▶︎ numeric ▶︎ dot ▶︎ comma ▶︎ semicolon ▶︎ space ▶︎ lower ▶︎ ampersand ▶︎ upper ▶︎ newline ▶︎ tabulation ▶︎ backslash ▶︎ quote ▶︎ doublequote ▶︎ anull
123 ▶︎ abc ▶︎ −4.2 ▶︎ . ▶︎ , ▶︎ ; ▶︎ ▶︎ < ▶︎ & ▶︎ > ▶︎
▶︎ ▶︎ ▶︎ ' ▶︎ " ▶︎
Et avec les optionsPLUME_EXPORT_LOCALE_FR | PLUME_EXPORT_OPTION_CONTROL, cela rendrait :
id ▶︎ text ▶︎ numeric ▶︎ dot ▶︎ comma ▶︎ semicolon ▶︎ space ▶︎ lower ▶︎ ampersand ▶︎ upper ▶︎ newline ▶︎ tabulation ▶︎ backslash ▶︎ quote ▶︎ doublequote ▶︎ anull
123 ▶︎ abc ▶︎ −4,2 ▶︎ . ▶︎ , ▶︎ ; ▶︎ ▶︎ < ▶︎ & ▶︎ > ▶︎ ⏎ ▶︎ ⇥ ▶︎ ▶︎ ' ▶︎ " ▶︎
Export XML
L'export XML génère un export exploitable par les CMS.
Les colonnes sont séparées par des <balises>, les chevrons et esperluettes étant transformés avec le codage des &amp;...;, et les colonnes àNULL éliminées.

En reprenant les données de l'exemple précédent, le resultat donnerait :
<!DOCTYPE list PUBLIC "https://www.d−nada.com/plume/export.dtd">
<db:export version="1.0" xmlns:db="https://www.d−nada.com/produit/plume/#Export+XML">
<db:table name="table_db">
<db:structure comment="table de démonstration">
<db:column type="integer" size="20" null="no" increment="39">id</db:column>
<db:column type="string" size="50" null="false" comment="nom de famille">text</db:column>
<db:column type="float" null="true" default="20" comment="">numeric</db:column>
<db:column type="string" size="1" null="true" comment="un point">dot</db:column>
<db:column type="string" size="1" null="true" comment="une virgule">comma</db:column>
<db:column type="string" size="1" null="true" comment="un point−virgule">semicolon</db:column>
<db:column type="string" size="1" null="true" comment="un espace">space</db:column>
<db:column type="string" size="1" null="true" comment="un chevron d'ouverture">lower</db:column>
<db:column type="string" size="1" null="true" comment="un esperluette">ampersand</db:column>
<db:column type="string" size="1" null="true" comment="un chevron de fermeture">upper</db:column>
<db:column type="string" size="1" null="true" comment="un retour à la ligne">newline</db:column>
<db:column type="string" size="1" null="true" comment="une tabulation">tabulation</db:column>
<db:column type="string" size="1" null="true" comment="un anti−slash">backslash</db:column>
<db:column type="string" size="1" null="true" comment="une apostrophe">quote</db:column>
<db:column type="string" size="1" null="true" comment="un guillemet">doublequote</db:column>
<db:column type="string" null="true" comment="un champ à NULL">anull</db:column>
</db:structure>
<db:data>
<db:record id="123">
<db:field name="text">abc</db:field>
<db:field name="numeric">−4.2</db:field>
<db:field name="dot">.</db:field>
<db:field name="comma">,</db:field>
<db:field name="semicolon">;</db:field>
<db:field name="space"> </db:field>
<db:field name="lower"><</db:field>
<db:field name="ampersand">&</db:field>
<db:field name="upper">></db:field>
<db:field name="newline">
</db:field>
<db:field name="tabulation"> </db:field>
<db:field name="backslash"></db:field>
<db:field name="quote">'</db:field>
<db:field name="doublequote">"</db:field>
</db:record>
</db:data>
</db:table>
</db:export>

Cet export est sensible aux localesPLUME_EXPORT_LOCALE_XX mais pas à l'optionPLUME_EXPORT_OPTION_CONTROL.
Export TAB
L'export TAB génère un export lisible par les humains.
De syntaxe : texttabulation -4.2tabulation.,; <>↩"tabulationtabulationtabulation
Les colonnes sont justifiées par des espaces (sur la droite ou la gauche si numérique) et séparées par des tabulations, les caractères de contrôles et leNULL sont remplacés par leurs caractères visuels.

En reprenant les données de l'exemple précédent, le resultat donnerait (avec le séparateur représenté ici par un " ▶︎ ") :
id ▶︎ text ▶︎ numeric ▶︎ dot ▶︎ comma ▶︎ semicolon ▶︎ space ▶︎ lower ▶︎ ampersand ▶︎ upper ▶︎ newline ▶︎ tabulation ▶︎ backslash ▶︎ quote ▶︎ doublequote ▶︎ anull
123 ▶︎ abc ▶︎ −4,2 ▶︎ . ▶︎ , ▶︎ ; ▶︎ ▶︎ < ▶︎ & ▶︎ > ▶︎ ⏎ ▶︎ ⇥ ▶︎ ▶︎ ' ▶︎ " ▶︎ ␀

Cet export est nativement sensible à l'optionPLUME_EXPORT_OPTION_CONTROL.
Export HTML
L'export HTML génère un export exploitable par un navigateur.
De syntaxe :<td>text</td><td>-4.2</td><td>.,;retour−chariottabulation"</td><td>&lt;&></td><td><i>NULL</i></td></td><td></td>
En reprenant les données de l'exemple précédent, le resultat donnerait :
<table class="plume_table_table_db">
<style>
table.plume_table_table_db td:nth−child(2) { text−align : right ; }
</style>
<thead>
<th>id</th>
<th>text</th>
<th>numeric</th>
<th>dot</th>
<th>comma</th>
<th>semicolon</th>
<th>space</th>
<th>lower</th>
<th>ampersand</th>
<th>upper</th>
<th>newline</th>
<th>tabulation</th>
<th>backslash</th>
<th>quote</th>
<th>doublequote</th>
<th>anull</th>
</thead>
<tbody>
<tr>
<th>123</th>
<td>−4.2</td>
<td>.</td>
<td>,</td>
<td>;</td>
<td> </td>
<td>&lt;</td>
<td>&amp;</td>
<td>&gt;</td>
<td>
</td>
<td> </td>
<td></td>
<td>'</td>
<td>"</td>
<td><i>␀</i></td>
</tr>
</tbody>
</table>

Cet export est sensible aux localesPLUME_EXPORT_LOCALE_XX et à l'optionPLUME_EXPORT_OPTION_CONTROL.

close()

Il est recommander de fermer les accès aux bases avant de terminer le programme PHP.
<?php
$base−>close();
?>
La fermeture d'une base entraine la clôture de son éventuel historisation en cours.

Base : Traces

La base de donnée introduit un ensemble de mécanisme de trace pour suivre ou alerter sur des problèmes SQL ou des requêtes trop longues.

Chacune des méthodes de lecture ou modification de données peut commencer par un premier paramètre "de localisation" de type chaîne de caractère commençant par :
  • Soit un "!" indiquant qu'il faut tracer cette requête dans la console du développeur.
  • Soit un "?" indiquant qu'il faut faire de même mais uniquement si $base->tracing(true).

De plus, dans le cas d'erreur SQL ou de dépassement de temps, la requête SQL sera toujours tracée dans la console et si PLUME_LOG est activée, elle sera enregistrée dans un fichier de log et une synthèse journalière sera mailée au développeur.

trace()

Cette méthode permet de générer des traces, méthode que l'on peut employer de deux façons différentes :
  1. $base->trace(titre) : envoyé directement sur la console du navigateur.
  2. $base->trace() : sans paramètre, permet de renvoyer une structure contenant la trace (pour enregistrer l'information par exemple).

Exemple:
<?php
$trace = $base−>trace(); // renvoie ['sql','result','error','time'] sur la dernière requête
$base−>trace('title'); // envoi sur la console.log ['sql','result','error','time'] sur la dernière requête
?>

tracing()

Cette fonction permet de contrôler plus finement le fonctionnement des traces.

Sa signature est constituée d'une suite de paramètres pouvant prendre l'un des types suivants :
  • Booléen : sitrue, force l'affichage les traces commençant par "?", sifalse, affiche uniquement les traces commençant par "!".
  • Entier : indique que ce numéro d'erreur ne doit plus être tracé. Si négatif, spécifie le numéro de warning. Si égale à 0 (par défaut), toutes les erreurs sont tracées. Chaque nouveau numéro s'ajoute à la liste des numéros à ne plus surveiller, jusqu'à rencontrer le numéro 0.
  • Réel : indique le nombre de seconde maximum avant de déclencher la trace, si inférieur à 0 aucun dépassement ne sera tracé, si égal à 0 toutes les requêtes seront tracées. Par défaut égale à une demie-seconde (0,5).
  • Chaîne de caractère : ajoute cette chaîne en trace au méthode n'ayant pas de chaîne de localisation.
  • Tableau de chaîne de caractère : suite d'expression régulière : permettant de masquer certaine information, avant l'enregistrement et diffusion du log, telle que les mots de passe…, avec la syntaxe =
    • Soit une liste qui seront remplacés par la chaîne de caractère : "/*/"
    • Soit une structure dont les clés seront recherchées et remplacées par leurs valeurs.
Cette méthode renverra toujours l'étattrue oufalse du mode forcé.

Rappel : Puisque cette methode trace() repose sur la fonction trace, elle hérite des comportements et formatage de cette dernière, et notament du méta-caractère "§".

Attention à bien différencier les numéro d'erreur (sous forme d'entier) du dépassement de temps (sous forme de réel).
Par défaut, sa signature correspond à$base−>tracing(false, 0, 0.5, "", []);.

Exemples :
<?php
$base−>tracing(0.500); // log activé sur les requêtes mettant plus d'une 1/2 secondes à répondre
$base−>tracing(0.0); // log activé sur toutes les requêtes

// masque les rubriques password
$base−>tracing(["/`?password`? = '(.*?)'/"]);
$base−>tracing([
"/`?password`? = '(.*?)'/" => "(masqué)",
]);
$base−>tracing([]); // supprimes tous les masquages

$base−>tracing(1451); // log inhibé sur l'erreur SQL 1451: "Cannot delete or update a parent row: a foreign key constraint fails"
$base−>delete("table", $id);
if ($base−>error() == 1451)
; // je gère cette erreur
$base−>tracing(−1265, 1452); // log inhibé sur le warning SQL 1265 et l'erreur SQL 1452
$base−>tracing(0); // réactivation des logs sur l'ensemble des erreurs

$mode_force_active = $base−>tracing(); // par défaut = false</abbr>
$record = $base−>select( "select * from table"); // n'affiche aucune trace
$record = $base−>list('?trace1', "select * from table"); // n'affiche pas trace1
$record = $base−>read('!trace2', "select * from table"); // affiche toujours trace2
$base−>tracing(true); // active la trace pour les ?traces
$record = $base−>sql('?trace3', "select * from table"); // affiche trace3
$record = $base−>select( "select * from table"); // n'affiche toujours pas de trace
$base−>tracing(false); // désactive la trace pour les ?traces
$record = $base−>update('?trace4', "table", $id); // n'affiche plus trace4
$base−>tracing("forcé §:"); // active la trace forçée
$record = $base−>select('?trace6', "select * from table"); // n'affiche plus trace6
$record = $base−>list('!trace7', "select * from table"); // affiche toujours trace7
$record = $base−>sql("select * from table"); // affiche cette trace avec le titre "forcé script.php:421:"
$base−>tracing(""); // desactive l'ajout de la trace forcée
?>
Petite remarque concernant le préfixe trace : un retour à la ligne est ajouté dans le log (de la console) entre ce préfixe et la requête SQL, sauf si la requête ne contient pas de retour-chariot ou si le préfixe se termine par le caractère ":".
<?php
$base−>read('?⭐ Prefixe 1', "select id from table where id > 0");
$base−>read('?⭐ Prefixe 2', "select id from table where id > 0");
$base−>read('?⭐ Prefixe 3:', "select id from table where id > 0");
?>
Affichera dans la console du navigateur :
⭐ Prefixe 1: select id from table where id > 0
⭐ Prefixe 2:
select id
from table
where id > 0
⭐ Prefixe 3: select id
from table
where id > 0

Log

Suivant les appels des méthodes de trace précédentes, les erreurs, warning et requête trop lentes vont s'afficher sur la console du navigateur et également être journalisé sur le serveur et synthétisé par mail journalier.
Le fichier.log est enregistré dans le dossier des log sur le serveur, avec le nom : base_AAAA-MM-JJ.log
Le format du fichier est une ligne = une trace, avec les colonnes suivantes, découpées par des tabulations :
  1. Date du jour : sous forme YYMMDD.
  2. Moment : sous forme HHMMSS.
  3. Utilisateur : sous forme d'un nom (extrait de$_SESSION['PHP_AUTH_USER']) suivi du caractère arobas (@) suivi de l'adresse IP de l'utilisateur.
  4. Temps : valeur flottante représentant le temps pris par la requête, en seconde précis à la microseconde.
  5. Mémoire : valeur flottante représentant la mémoire utilisés actuellement par PHP en giga octet (voirmemory_get_usage()).
  6. Résultat :
    • Soit le résultat, si un seul champ.
    • Soit le caractère dièse (#), suivi du nombre de champ ou enregistrement retourné.
    • Soit une erreur, sous forme d'un nombre positif, suivi du symbole égale (=), suivi du texte de l'erreur.
    • Soit un warning, sous forme d'un nombre négatif, suivi du symbole égale (=), suivi du texte du warning.
  7. SQL : la reqête SQL.
  8. URL : la requête URL côté navigateur.
  9. Programme : la pile de fonctions PHP, séparée par des tabulations, décomposée en différentes valeurs, séparée elles-mêmes par deux-petits-points (:) :
    • Le niveau de la pile, le 1 étant la première a avoir été lancée.
    • Le nom du fichier PHP.
    • Le numéro de la ligne dans ce fichier.
    • Le nom de la fonction PHP.
    • Les paramètres, entre parenthèse, passées à cette fonction.
Exemple :
yymmdd hhmmss	user@ip.address	time	memory	result|#lines|number=error	request	url	sub:php:line:function(args)
201231 123059 enzo@78.233.207.42 0.0006 0.0010 #18 select * from ma_table where id = 2 && enabled = 1 https://mon−site.com/rubrique/?event=235346 3:/library.php:1041:Base.select($sql, 2) 2:/lib/plume.php:688:ajax_ma_fonction('texte', 2942, 42) 1:/index.php:1485:Template.ajax()
Dans cet exemple, une requête a été faite avec succès, ce 31/12/2020 à 12h 30m 59s par "enzo" d'IP 78.233.207.42, qui a pris 0.0006s, pour 1Mo, et a renvoyé 18 enregistrements, avec la requête SQL : https://mon-site.com/rubrique/?event=235346.
Il a été lancé par la méthodeselect de la classeBase depuis la ligne 1041 du programme/library.php, lui-même, appelé parajax_ma_fonction en ligne 688 de/lib/plume.php, lui-même parTemplate.ajax appelé en ligne 1485 de/index.php.

Base : History

En plus de la journalisation, il est possible d'enregistrer dans un autre journal, les actions d'enregistrement faites sur la base, afin de pouvoir revenir en arrière.
Mais à la différence de la journalisation qui a pour but principal d'enregistrer les problèmes (erreur SQL ou dépassement de temps), cette historisation permet de tracer toutes les requêtes SQL, et donc les actions faites sur une base et traduire automatiquement les actions inverses pour revenir en arrière.
Le mécanisme consiste en la mémorisation des requêtes faites et la production des requêtes inverses pour revenir en arrière, le tout enregistré dans un fichier local sur le serveur.
Attention: cela ne fonctionne qu'à travers les méthodes SQL de Plume suivantes : insert(), update(), modify() et delete().

Ce mécanisme se résume en une première fonction histories() qui permet d'activer ce mécanisme et lui passer certaines consignes, puis d'encadrer les parties de requête SQL par la méthode history().

histories()

Cette méthode permet de gérer les historisations.
Elle est généralement utilisée une seule fois et en début de programme, pour lui communiquer les données nécessaire afin d'authentifier l'utilisateur lors des historisations suivantes.
Sa signature est :
  • Activation : permet d'indiquer si l'historisation sera actif ou non :
    • Si vrai : active l'historisation dans un fichier/history_YYMMJJ.log situé dans le dossier des journaux.
    • Si faux : désactive l'historisation.
    • Si chaîne de caractère : active l'historisation en nommant le nom du journal (contenu dans le dossier des journaux) ou le chemin vers le journal où sera enregistrés l'historisation.
  • Suite de paramètre qui seront enregistrés dans le journal.
Exemple :
<?php
$base = open($base_de_donnée);
$user = $base−>read("select * from auth where login = {s} && pass = {w}", $_POST['login'], $_POST['pass']);

$base−>histories(true, $user['lastname'], $user['id']);
?>
Par défaut, si cette méthode n'est pas appelée, l'historisation n'est pas active.

history()

Cette méthode permet d'encadrer l'historisation.

Sa signature est la suivante :
  1. action :
    • Soit une chaîne de caractère, indiquant un titre qui lancera le début d'une nouvelle historisation, en groupant les requêtes SQL qui suivent sous forme d'une action nommée.
    • Soit une valeurtrue qui sera remplacé par un titre de type "fichier:ligne:classe.ma_fonction()" marquant l'endroit de l'appel de ce début d'historisation.
    • Soit une valeur fausse (false,null, une chaîne vide, …, ou aucun paramètre) permettant de fermer l'historisation en cours.
    • Soit une valeur numérique indiquant qu'il faut remonter l'historisation jusqu'à un niveau précis (voir valeur de retour de cette méthode). La valeur 1 (ou inférieur) indiquant de tout fermer.
  2. Suite de paramètre qui seront enregistrés en complément dans le journal.
  • La valeur de retour est le niveau d'historisation en cours.

Exemple:
<?php
// commence à historiser
$base−>history("Début", 42);

$base−>set($champs);
$base−>insert("?exemple", $table);

$base−>set(['champ' => "valeur"]);
$base−>update("?exemple", $table);

// mémorise cette historisation, nommé "Début", constitué de 2 actions : insert + update
$base−>history();
?>

Il est possible d'encapsuler plusieurs historisation :
<script>
$base−>history("1er niveau");

$base−>set($champs);
$base−>insert($table);

$base−>history("2ème niveau");

$base−>set($champs);
$base−>insert($table);

$base−>history();

$base−>history();
</script>

L'historisation ne se fera pas si aucune requête n'a été faite.
<script>
$base−>history(true);
// aucune requête −> aucune trace
$base−>history();
</script>

Note : Il est possible de passer des paramètres complémentaire en début et/ou en fin de mémorisation. Elle sera enregistrée à la suite des paramètre passées par la méthode histories().
Note : L'historisation n'enregistre pas les parties de requête SQL que l'on cherche à masquer avec la méthode [tracing](#### tracing()).

history.log

Le fichier des historisations est de format :
  • en première ligne, séparé par des tabulations :
    • La date sous forme YYYY/MM/AA HH:MM:SS
    • L'adresse IP de l'utilisateur
    • Le titre passé danshistory()
    • La liste des paramètres passés dans la méthode histories() et la méthode history().
  • Dans chaque ligne suivante, chaque commande, toujours séparé par des tabulations :
    • Le type de commande :insert,update,delete oumodify.
    • La trace éventuelle passée en 1er paramètre de ses méthodes (sans le "?" ou le "!").
    • La requête SQL qui a été faite.
    • La ou les requêtes SQL qui doivent être faites pour revenir en arrière.
Note : Ces lignes de commande sont classés par ordre chronologique inverse.
<?php
$base−>histories("suivi.back", "général 1", "général 2");

$base−>history("titre", "début 1", "début 2");

$base−>set('un_champ', 42);
$base−>insert('ma_table');

$base−>set('un_champ', 7);
$base−>update("?mise à jour", 'ma_table');

$base−>history(false, "fin 1", "fin 2");
?>
Historisera :
<log>
2023/12/31 23:59:59 ▶︎ 192.128.0.1 ▶︎ titre ▶︎ général 1 ▶︎ général 2 ▶︎ début 1 ▶︎ début 2 ▶︎ fin 1 ▶︎ fin 2
update ▶︎ mise à jour ▶︎ update ma_table set un_champ = 7 where id = 16743; ▶︎ update ma_table set un_champ = 42 where id = 16743;
insert ▶︎ ︎ ▶ insert into ma_table (un_champ) values (42); ︎ ▶ delete from ma_table where id = 16743;
</log>

PLUME_HISTORY

La liste des enregistrements historisés dans ce journal, est limité à 20 enregistrements maximum.
Si l'on veut repousser à plus d'enregistrement, il faut le définir dans la constantePLUME_HISTORY.
<?php
define('PLUME_HISTORY', 20);
require('plume.php');
?>

PHP : Tag

Plume apporte la classeTag (et sa sous-classTags) permettant de récupérer une ressource Internet et de manipuler les balises HTML, comme le ferait un navigateur client.

Tag : Création

L'instanciation de cet objetTag permet de créer une nouvelle balise, mais surtout de récupérer une ressource, en lui communiquant une URL.
Sa signature est :
  1. La balise sous forme de :
    • Nom de la balise
    • URL à charger d'une page HTML, image, …, de type : http://fqdn/chemin
    • Fichier : chemin vers un fichier local (qui a été sauvegardé par exemple avec la méthode write)
    • NULL pour créer une balise de type texte.
    • "" une chaîne de caractère vide pour créer une balise Document (le plus haut niveau des balises).
  2. Les attributs éventuels sous forme d'un tableau.
    <?php
    // création
    $html = new Tag("https://dnada.fr/produit/plume/#PHP+%3A+Tag");
    if (! $html)
    die "Impossible d'accéder à la page";
    $tag = new Tag('input', ['type' => "text", 'att−perso' => 42]);
    $tagText = new Tag(null, "text HTML");
    $document = new Tag('', "text HTML");
    ?>
L'objetTag renvoyé correspond à la première balise de type Document. Il contient également des informations sur la page HTML.

Il est possible de faire une requete de méthode "POST" :
<?php
// en POST application/x−www−form−urlencoded
$reponse = new Tag("https://site.com/form", "champ1=valeur 1&champx=valeur x");

// en POST multipart/form−data
$reponse = new Tag("https://site.com/form", ['champ1' => "valeur 1", 'champx' => "valeur x"]);
?>

Tag : document()

Cette méthode renvoie un tableau d'informations de base niveau sur la ressource qui a été chargée.
Sa signature est :
  1. Nom : le nom de l'information recherché. Si omis, renvoie toutes les informations.
  • Informations renvoyées :
    • url: l'URL utilisé lors dunew.
    • requests: tableau contenant les informations des requêtes de redirection.
      <?php
      $html = new Tag("https://dnada.fr");
      $document = $html−>document();
      ?>

Tag : status()

Cette méthode permet de renvoyer le statut. de la dernière requête HTTP.
Elle a en signature :
  • Type de sortie :
    1. Soit fausse (ou absent) : renvoie la valeur numérique.
    2. Soit vrai : renvoie la forme numérique + texte.
      <?php
      $html = new Tag("https://dnada.fr");
      trace("statut", $html−>status());
      ?>

Tag : data()

Cette méthode permet de renvoyer en brut les données deTag.
Permettant par exemple de récupérer une image sur un serveur distant ou rechercher un texte dans une balise.
<?php
$image = new Tag("https://dnada.fr/produit/plume/logo.png");
$image = $image−>data();

$page = new Tag("https://www.dnada.fr/");
if (strpos($page−>data(), "dnada") !== false)
;
?>

Tag : parent()

Cette méthode permet de renvoyer le parent ou un des parents répondant à un critère de recherche.
Sa signature est :
  1. La recherche :
    • Si aucun (ounull), renvoie son parent direct.
    • Si chaîne de caractère: recherche en utilisant ce critère.
    • Si numérique, renvoie le xème parent.
    • Si fonction PHP: renvoie le parent où cette fonction aura répondu vrai.
  2. L'inclusion : si vrai, vérifie le critère sur lui-même avant de rechercher sur ses parents.
  • Renvoie le parent trouvé, ouNULL si le parent n'est pas trouvé, ou si l'élément n'a pas de parent (cas d'une création ou d'un détachement par exemple).
    <?php
    $parent = $tag−>parent();
    $parent = $tag−>parent('tag#id.class@name?type[attribut~=value]', false);
    $parent = $tag−>parent(3);
    $parent = $tag−>parent(fn($parent) => $parent−>id);
    ?>

Tag : child()

Cette méthode permet de renvoyer l'élément HTML répondant au critère recherché.
Elle a en signature :
  1. Le chemin pour aller jusqu'à l'élément recherché.
  2. La position optionnel si l'on cherche à avoir le xème élément, ou le -xème élément en partant de la fin.
  • Renvoie l'élément sous forme d'un objetTag ou null si non trouvé.
    <?php
    // renvoie la première balise répondant au paramètre passé
    $tag = $html−>child("div#id ul.class li@name input?type form[method=post]");

    // renvoie le 2ème INPUT rencontré
    $input = $tag−>child('input', 2);

    // renvoie le dernier INPUT rencontré
    $input = $tag−>child('input', −1);

    // renvoie l''enfant direct correspondant à un INPUT
    $input = $tag−>child('>input');

    // recherche un DIV
    $tag = $html−>child("div.class1.class2");
    ?>

À la différence de la méthode Tag-children(), cette méthode permet une recherche profonde et unitaire sur l'ensemble des enfants et descendant de l'élément.

Tag : children()

Cette méthode permet de renvoyer l'ensemble des enfants HTML directs (si absence de paramètre) ou un tabeau d'éléments HTML répondant au critère recherché.
Elle a en signature:
  1. Le chemin pour aller jusqu'aux éléments recherchés. Si absent, cette méthode renvoie la liste des éléments directs de l'élément.
  2. Le nombre optionnel si l'on cherche à avoir les xème premier éléments, ou les -xème derniers éléments.
  • Renvoie un tableau des éléments trouvés ou un tableau vide si non trouvé.
    <?php
    // renvoie les enfants directs de $tag
    $children = $tag−>children();

    // recherche les balises répondant au paramètre passé
    $tags = $html−>children("div#id ul.class li@name input?type form[method=post]");

    // renvoie les 2 premiers INPUT rencontrés
    $inputs = $tag−>children('input', 2);

    // renvoie les 2 derniers INPUT rencontrés
    $inputs = $tag−>children('input', −2);

    // renvoie les 2 premiers INPUT enfants direct
    $inputs = $tag−>children('>input', 2);
    ?>

À la différence de la méthode Tag-child(), cette méthode permet également une recherche sur les enfants directs de l'élément, et renvoie toujours un tableau.

Tag : att()

Tag propose une méthodeTag−>att() permettant d'accéder aux informations rattachés à un élément HTML, avec en signature :
  • le nom de l'attribut
  • la valeur si cet attribut doit être modifié
  1. renvoie la valeur de l'attribut si un seul paramètre est passé.
    <?php
    $att = $tag−>att('href');
    $att = $tag−>att('attribut', "value");
    ?>

Tag : attributs

En plus de la méthode [Tag−>att()](### Tag : att()),Tag propose un accès direct à certains attributs :
  • Tag->tagName : en lecture seul, correspond au nom de la balise HTML.
  • Tag->id : correspond à l'attribut id
  • Tag->className : correspond à l'attribut class
  • Tag->name : correspond à l'attribut name
  • Tag->type : correspond à l'attribut type
    <?php
    $tagName = $tag−>tagName;
    $id = $tag−>id;
    $className = $tag−>className;
    $name = $tag−>name;
    $type = $tag−>type;
    ?>

Tag : text()

Permet de consulter ou remplacer la partie texte d'un élément HTML.
<?php
$text = $tag−>text();

$tag−>text("abc");
?>

Tag : value()

Permet de lire ou écrire une valeur sur certains éléments HTML, tel que<input>.
<?php

$value = $input−>value();
$value = $input−>value("value");
?>

Tag : append()

Permet d'ajouter (et créer si besoin) un élément dans un parent.
<?php

// modification d'un tag
$child = $parent−>append('tagName', ["att" => "value"]);
$child = $parent−>append($tag);
$child = $parent−>append(null, "text HTML");
?>

Tag : detach()

Permet d'enlever un élément HTML de son parent direct, avec en signature :
  1. L'élément à détacher ou vide si l'élément à détacher correspond au "parent".
  • Renvoie soit l'élement, soitNULL si l'élément n'appartient pas au parent;
    <?php
    // détache tag de parent
    $child = $parent−>detach($tag);

    // se détache de son parent
    $child = $tag−>detach();
    ?>

Après son détachement, l'élément a son parent àNULL).

Tag : inputs

Cette propriété permet de récupérer un tableau sous forme d'un objetTags pour manipuler simplement un formulaire.
Elle ne fonctionne que sur un élément HTML de type<form>
Elle est plus simple que l'utilisation deTag−>children('input') + Tag−>children('textarea') + Tag−>children('select') + …
<?php
$form = $html−>child('form');
$inputs = $form−>inputs;

// en itération
foreach ($form−>inputs as $name => $value)
trace($name, $value);

// modifie un input
$form−>inputs['name'] = "abc";
?>

Tag : submit()

Cette méthode permet de soumettre un formulaire.
Elle ne fonctionne que sur un élément HTML de type<form> et renverra le résultat HTML.
<?php
$form = $html−>child('form');
$form−>inputs['name'] = "abc";
$html = $form−>submit();
?>

Tag : header()

Cette méthode permet de renvoyer un tableau des headers deTag.
<?php
$html = new Tag("https://dnada.fr");
if ($html−>status() != 200)
die "Impossible d'accéder au serveur";
$heads = $html−>header();
?>

Tag : write()

Cette méthode permet d'enregistrer en brut les données deTag.
Permettant par exemple de récupérer et enregistrer une image.
Elle a en signature :
  1. Fichier : le nom du fichier
  2. Entête : si vrai, enregistre également les entêtes HTTP en plus des données.
Exemple :
<?php
$image = new Tag("https://dnada.fr/produit/plume/logo.png");
$image = $image−>write("/tmp/image.png");

$page = new Tag("https://www.site.com/path");
if ($cache)
$page−>write($cache, true);
// ...
if ($cache && file_exists($cache))
$page = new tag($cache);
?>

PHP : Mail

Classe PHP permettant de préparer et envoyer des mails.

Exemple :
<?php
// préparation du mail
$mail = new Mail();

// entête
$mail−>from("emetteur@mail.com");
$mail−>to("destinataire@mail.com");
$mail−>to(["destinataire@mail.com"]);
$mail−>cc("copie@mail.com");
$mail−>cc(["copie@mail.com"]);
$mail−>bcc("copie−caché@mail.com");
$mail−>bcc(["copie−caché@mail.com"]);
$mail−>returnPath("emetteur@mail.com");

$mail−>subject("titre du mail");

// corps du mail
$pattern = Template::extract("//message/page.html", "mail_template");
$message = Template::render($pattern−>html(['mail' => $message, 'signer' => $signature]));
$mail−>html(
"texte ou <html>");

if (! $mail−>attachment(
"/fichier.ext", "nom du fichier"))
trace(
"fichier inexistant");

// envoi
$mail−>trace(
"mail"); // trace sur la console
if ($mail−>send()) // envoi le mail
return ['success' =>
"Mail anvoyé avec succès."];
return ['error' =>
"Problème lors de l'envoi du mail."];
?>

Mail : constructeur

Le constructeur accepte d'initialiser directement certains paramètres :
<?php
// $mail = new Mail($from = '', $to = '', $subject = '', $body = '');
?>
Et avec cette forme simplifiée et raccourcie, un dernier paramètre "send" mis à "true", envoye immédiatement le mail (sans avoir besoin de stocker l'instance Mail) :
<?php
new Mail("emetteur@dnada.fr", "client@client.com", "Mail de DNADA", "Merci pour votre réponse.\nCordialement", true);
?>
Attention : à bien mettre une majuscule sur l'objet Mail, sinon PHP utilisera la fonction native mail().

Mail : from() :

Cette méthodefrom() permet d'indiquer l'adresse mail de l'émetteur.
Sa signature est :
  • adresse : chaîne de caractère de l'adresse mail

Mail : to() :

Cette méthodeto() permet d'ajouter l'adresse mail d'un destinataire principal.
Sa signature est :
  • adresse : chaîne de caractère de l'adresse mail

Mail : cc() :

Cette méthodecc() permet d'ajouter l'adresse mail d'un destinataire secondaire (carbon copy).
Sa signature est :
  • adresse : chaîne de caractère de l'adresse mail

Mail : bcc() :

Cette méthodebcc() permet d'ajouter l'adresse mail d'un destinataire caché (back carbon copy).
Sa signature est :
  • adresse : chaîne de caractère de l'adresse mail

Mail : returnPath() :

Cette méthodereturnPath() permet d'ajouter une adresse mail de redirection en cas de retour de mail.
Sa signature est :
  • adresse : chaîne de caractère de l'adresse mail
Cette méthode est inutile si l'adresse passée àfrom() est la même.

Mail : subject() :

Cette méthodesubject() permet d'ajouter l'objet du mail.
Sa signature est :
  • objet : chaîne de caractère spécifiant le sujet du mail.

Mail : html() :

Cette méthodehtml() permet d'ajouter le corps du mail.
Sa signature est :
  • corps : chaîne de caractère de type texte, ou HTML.

Mail : attachment() :

Cette méthodeattachment() permet de rattacher une pièce jointe au mail.
Sa signature est :
  • source : chaîne de caractère indiquant le chemin du fichier sur le serveur.
  • name : chaîne de caractère indiquant le nom du fichier qui sera présenté par le client mail.
  • retourne un booléan avectrue si le fichier a bien été ajouté.

Mail : send() :

Cette méthodesend() permet d'envoyer le mail.
Elle n'a aucun paramètre et retourne un booléan indiquant si le mail est valide pour être envoyé.

Mail : trace() :

Cette méthodetrace() permet d'envoyer des informations sur la console du navigateur, pour la partie débugging, uniquement si le mode trace est activé.
Sa signature est :
  • titre : chaîne de caractère représentant l'intitulé en début de trace sur la console.
  • encodage : booléan optionnel pour soit d'afficher sur la console le mail visuellementfalse (par défaut) ou en bruttrue.
  • retourne la chaîne de caractère envoyé sur la console.

PHP : Markdown

Fonction PHP permettant de réaliser des markdowns (tel que la plus grosse partie des pages de ce site ou simplement cette documentation que vous lisez actuellement).

À implémenter depuis PHP :
<html>
{markhtml}
</html>
<script>
function template_markhtml($pattern) {

// en lui passant le chemin vers le fichier markdown
return markdown("mon_fichier_markdown.md");

// ou en lui passant le texte markdown
$markdown = file_get_contents("mon_fichier_markdown.md");
return markdown($markdown);
}
</script>

Où depuis un template :
<html>
{+markdown(mon_fichier_markdown)+}
</html>
L'implémentation Markdown respecte la syntaxe suivante :

Markdown : Texte

Le texte se saisie tel quel.
Une fin de ligne provoquera un retour à la ligne HTML<br> automatiquement.
Ceci est un exemple de texte.
Qui sera afficher ensuite sous forme HTML.
Un ensemble de caractères permettent des mises en page (tel que# * −), pour afficher ce caractère, il suffit de placer un anti-slash avant "\" (exemple\# \* \−).
Les caractères< > et & utilisés en HTML, sont naturellement protégés, mais les balises HTML sont tout de même reconnu.
Ceci est < un exemple \\ de texte & tordu > avec une balise : <button>Bouton</button>.
Ceci est < un exemple \ de texte & tordu > avec une balise : .
Les {templates Plume} sont gérés, en dehors des blocs de code.
Avec une fonction template PHP :
<?php
function template_test_code($pattern) {
return "<u>Fonction template_test_code() PHP executée</u>";
}
?>
Exemple de code ({test_code}) inséré et executé dans le texte.
`
Exemple de code ({test_code}) inséré mais non executé dans le texte.
`
?>
On aurait le résutat suivant :
Exemple de code (Fonction template_test_code() PHP executée) inséré et executé dans le texte.
Exemple de code ({test_code}) inséré mais non executé dans le texte.

Markdown : Emphase gras

Syntaxe : le caractère astérisque* encadrant un bloc de mots
Formatage : encadre avec la balise<strong> Exemple :
Exemple de texte *en gras sur une ligne* ou
sur plusieurs *lignes
blabla blabla blabla blabla blabla
en finissant* à tout instant.
(Ceci n'est pas \*du texte en gras mais juste encadré d'astérisques\*)
Donnera :
Exemple de texte en gras sur une ligne ou
sur plusieurs lignes
blabla blabla blabla blabla blabla
en finissant
à tout instant.
(Ceci n'est pas *du texte en gras mais juste encadré d'astérisques*)

Markdown : Emphase souligné

Syntaxe : le caractère espace-souligné_ encadrant un bloc de mots
Formatage : encadre avec la balise<u> Exemple :
Exemple de texte _souligné sur une ligne_ ou
sur plusieurs _lignes
blabla blabla blabla blabla blabla
en finissant_ à tout instant.
(Ceci n'est pas \_du texte souligné mais juste encadré d'un espace souligné\_)
Donnera :
Exemple de texte souligné sur une ligne ou
sur plusieurs lignes
blabla blabla blabla blabla blabla
en finissant
à tout instant.
(Ceci n'est pas _du texte souligné mais juste encadré d'un espace souligné_)

Markdown : Emphase barré

Syntaxe : deux fois le moins encadrant un bloc de mots
Formatage : encadre avec la balise<s> Exemple :
Exemple de texte −−barré sur une ligne−− ou
sur plusieurs −−lignes
blabla blabla blabla blabla blabla
en finissant−− à tout instant.
(Ceci n'est pas \−−du texte barré mais juste encadré de 2 moins−−)
Donnera :
Exemple de texte barré sur une ligne ou
sur plusieurs lignes
blabla blabla blabla blabla blabla
en finissant
à tout instant.
(Ceci n'est pas --du texte barré mais juste encadré de 2 moins--)

Attention : ne pas confondre le formatage de liste qui doit impérativement commencer en début de ligne et ce formatage qui doit être à l'intérieur d'une ligne, quitte à le faire précéder d'un espace :
Ceci est un texte contenant −− un texte barré −− dans sa ligne.
−− Ceci est un texte barré, qui commence en début de ligne, mais par un espace −−
− Ceci est une liste de niveau 1, qui commence en début de ligne.
−− Ceci est une liste de niveau 2, qui commence en début de ligne.

Exemple:
Ceci est un texte contenant un texte barré dans sa ligne.
Ceci est un texte barré, qui commence en début de ligne, mais par un espace
  • Ceci est une liste de niveau 1, qui commence en début de ligne.
    • Ceci est une liste de niveau 2, qui commence en début de ligne.

Markdown : Titre

Syntaxe : le caractère dièse# en début de ligne
Formatage : encadre avec les balises<h1>, <h2>, … Exemple :
# Titre 1
# Titre 2
## Titre 3
### Titre 4
#### Titre 5
##### Titre 6
\# Ceci n'est pas un titre
Donnera :

Titre 1

Titre 2

Titre 3

Titre 4

Titre 5
Titre 6
# Ceci n'est pas un titre

Il est possible d'activer l'ancrage sur les titres, lors du défilement de la page de haut en bas, avec la fonctionnalité CSS Scroll Snap.
Pour cela, vous devez préciser le type d'accroche, viascroll−snap−type, à placer sur un des conteneurs contenant votre Markdown, typiquement, lebody de la page :
<style>
body {
scroll−snap−type: y proximity;
}
</style>

Après avoir changé l'état de cette boite à cocher, fait défiler la page et voyez comment le navigateur s'arrêtera ou non sur les titres.

À noter que le texte d'un titre (hors espace avant et après), permet de créer les liens qui pointent éventuellement sur lui. Il doit donc être unique.
Si plus de deux titres sont identiques, seul le premier titre sera référencé par l'ensemble des liens, et une trace (donc uniquement en mode développement) s'affichera indiquant ce doublon :
### Ceci est un titre en doublon
....
### Ceci est un titre en doublon (suivi de plusieurs espaces)
Affichera dans la console du navigateur :
❌ ► Markdown duplicate title : ### Ceci est un titre en doublon

Markdown : Ligne

Syntaxe : Trois caractères moins minimum en début de ligne
Formatage : transformer en balise<hr> Exemple :
---
Donnera :

Markdown : Lien

Syntaxe : [texte descriptif](url)
Formatage : encadre de la balise<a>
Exemple :
Lien externe [Site de DNADA](https://www.d−nada.com) et lien interne à la page [1er chapitre](# Présentation).
Donnera :
Lien externe Site de DNADA et lien interne à la page 1er chapitre.

Il est possible de faire des liens relatifs, vers le site hébergeant la page, en omettant le FQDN, et en commencant l'adresse par un "/" :
[url vers le une page présent sur le site](/command/index.php?user=dupon)
[url vers le home](/)

Le contenu du texte et de l'URL sont insensible au formatage Markdown, l'URL accepte les crochets (tant que l'on referme ces crochets inclus) et le texte les parenthèses (tant que l'on referme ces parenthèses incluses) et les guillemets obliques` permettant le formatage du lien sous forme de code.
[lien possible _ encadré de deux espaces soulignés _ et contenant [des crochets [sur plusieurs [niveaux]]]](/)
[lien avec url contenant des parenthèses et formatage non interprèté](## PHP : get_log_dir())
[`lien formaté sous forme de code`](### Markdown : Lien).
Donnera :
lien possible _ encadré de deux espaces soulignés _ et contenant [des crochets [sur plusieurs [niveaux]]]
lien avec url contenant des parenthèses et formatage non interprèté
lien formaté sous forme de code.

Il est possible de mettre des liens interne vers un des titres de ce document, en plaçant le texte du titre dans l'URL :
### Un titre
...
Lien vers [ce titre](### Un titre).

Si un lien interne ne pointe sur aucun titre, une trace sera affichée dans la console (donc uniquement en mode développement) :
Lien vers [un titre inexistant](### Erreur de titre inexistant).
Affichera dans la console du navigateur :
❌ ► Markdown broken link to non-existent title : ### Erreur de titre inexistant

Les adresses mails présentes dans la partie texte (et non dans un bloc de code) sont automatiquement protégées des robots, c'est à dire, non envoyé dans la page HTML, mais transformées en adresse mail HTML avant d'être inséré plus tard via plume.js.
Exemple : contact@d-nada.com
Donnera bien :


À noter qu'il est possible d'insérer du code en ligne à l'intérieur d'un lien :
Exemple avec : [`Plume`](https://fr.wikipedia.org/wiki/Plume)
Qui donnera le lien : Plume.

Markdown : Image

Syntaxe : ![texte alternatif](url)
Formatage : transforme avec la balise<img>
Exemple :
Image : ![image représentant une plume](exemple image.png)
Donnera :
Image :
image représentant une plume
Il est possible de définir la taille (en hauteur et/ou largeur) et de justifier la position de l'image dans le texte, avec la syntaxe suivante en fin de texte explicatif (qui ne sera pas repris dans le texte) : largeur/hauteur/centrage.
Avec :
  • Largeur : rien ou une valeur numérique en pixel (ex: 100px), …, ou en pourcentage par rapport à la largeur de la fenêtre (ex: 50%).
  • Hauteur : rien ou une valeur numérique en pixel (ex: 100px), …, mais n'accepte pas de pourcentage.
  • Centrage : rien ou un des énumérés suivants : left, center, right, larboard, starboard
    • (aucun formatage) : positionne l'image dans le texte (prévoir une image relativement petite dans ce cas).

Exemples d'insertion d'images :
![image représentant une petite plume 100px/ /](exemple image.png)
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses.
image représentant une petite plume

Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
    • left : positionne l'image sur la gauche, sans texte sur la droite.
      ![image représentant une plume à gauche 300px/ /left](exemple image.png)
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses.
image représentant une plume à gauche

Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
    • center : positionne l'image centré au milieu de la page, sans aucun texte.
      ![image représentant une plume centrée 300px/ /center](exemple image.png)
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses.
image représentant une plume centrée

Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
    • right : positionne l'image sur la droite, sans texte sur la gauche.
      ![image représentant une plume à droite 300px/ /right](exemple image.png)
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité.
image représentant une plume à droite

Le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
    • larboard : positionne l'image sur la gauche (babord) du texte.
      ![image représentant une plume à babord 200px/ /larboard](exemple image.png) (avec un espace pour provoquer un retour à la ligne entre les deux textes)
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
image représentant une plume à babord

Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
    • starboard : positionne l'image sur la droite (tribord) du texte.
      ![image représentant une plume à tribord 200px/ /starboard](exemple image.png) (avec un espace pour provoquer un retour à la ligne entre les deux textes)
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
image représentant une plume à tribord

Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?

Markdown : Liste

Syntaxe : le caractère moins pour une liste non-ordonnée (•, •, •), ou plus+ pour une liste ordonnée (1, 2, 3), en début de ligne, suivi d'un eventuel "titre :"
Formatage : encadre avec la balise<ul> ou<ol>, et encadre de<span> l'eventuel titre.
Exemple :
− titre : texte

− Action
− Aventure
− Fantastique
−− Bande−dessinée
−− Roman
−−− Stephen King
−−− H.P. Lovecarft
−−− Edgar Allan Poe
−−− J.R.R Tolkien
−− Film
−− Série
− Policier
− Suspense
+ Mercure
+ Vénus
+ Terre
++ Lune
+ Mars
++ Phobos
++ Déimos
+ Jupiter
++ Io
++ Europe
++ Ganymède
++ Callisto
+− 79 satellites
+ Saturne
+− 200 satellites
+ Uranus
+− 27 satellites
+ Neptune
+− 14 satellites
Donnera :
  • titre : texte
  • Action
  • Aventure
  • Fantastique
    • Bande-dessinée
    • Roman
      • Stephen King
      • H.P. Lovecarft
      • Edgar Allan Poe
      • J.R.R Tolkien
    • Film
    • Série
  • Policier
  • Suspense
  1. Mercure
  2. Vénus
  3. Terre
    1. Lune
  4. Mars
    1. Phobos
    2. Déimos
  5. Jupiter
    1. Io
    2. Europe
    3. Ganymède
    4. Callisto
    • 79 satellites
  6. Saturne
    • 200 satellites
  7. Uranus
    • 27 satellites
  8. Neptune
    • 14 satellites

Attention : ne pas confondre le formatage de barré qui doit être compris dans une ligne et ce formatage qui obligatoirement commencer en début de ligne.

Markdown : Code

Syntaxe : un caractère guillemet oblique` Formatage : encadre soit de balise<code> s'il tient sur une ligne, soit<pre> s'il tient sur plusieurs lignes
Exemple :
Exemple de `code` sur une seule ligne et
`
Exemple d'un bloc de code
sur plusieurs lignes.
`
Donnera :
Exemple decode sur une seule ligne et
Exemple d'un bloc de code
sur plusieurs lignes.

À noter qu'il est possible d'insérer du code en ligne à l'intérieur d'un lien :
Exemple avec : [`Plume`](https://fr.wikipedia.org/wiki/Plume)
Qui donnera le lien : Plume.

Les blocs de code ont leurs propres formatage de couleur, pour une meilleur lisibilité :
  • des balises HTML (<cite><var></var></cite>).
  • des mot-clés tel que if, return (<cite>)
  • des $variables et Classe:: (<var>)
  • des "constantes" (<samp>)
  • des // commentaires (<abbr>)
Exemple :
<button name="ok"> Action </button>

// commentaire
function fonction($parametre) {
while ($parametre > 0) {
if ($parametre)
$parametre −= 1;
Template::render("<div>texte constant</div>");
}
return;
}

Markdown : Citation

Syntaxe : un ou plusieurs caractères> en début de ligne
Formatage : encadre avec la balise<blockquote> Exemple :
> Exemple de citation
>> imbriqué sur
>>> plusieurs niveaux
>> et sur
>> plusieurs lignes
> fin de la première citation
\> ceci n'est pas une citation
Donnera :
Exemple de citation
imbriqué sur
plusieurs niveaux
et sur
plusieurs lignes
fin de la première citation
> ceci n'est pas une citation

Markdown : Sommaire

Syntaxe : !{Table of Contents} ou !{Table of Contents(de 0 à 6)} ou !{Table of Contents(de -1 à -6)}
Paramètre : si positif ou 0, indique la profondeur du niveau à refermer, si négatif, indique combien de niveau doivent être affiché.
Formatage : construit dynamiquement un sommaire, avec notion d'ouverture et fermeture des niveaux, encadré de la balise HTML<nav>.
Exemple :
!{Table of Contents(5)}
Donnera le sommaire de cette page en affichant que les 5 premiers niveaux, et en masquant le 6ème.

Markdown : Date

Syntaxe : !{date(formatage)}
Paramètre : le paramètrage est identique au paramètre de la fonction PHP Date.
Formatage : construit dynamiquement une date, encadrée de la balise HTML

Markdown : Social

Syntaxe : ?[social](lien facebook)
Formatage : remplace par un lien rattaché à une icône propre au réseau social spécifié.
Exemple :
?[facebook](mon facebook)	?[skype](mon skype)	?[blog](url vers mon blog).
Donnera :



Installation

Plume est compatible avec les vieux navigateurs (tel que Safari 10) et à partir de PHP 7.2 jusqu'à PHP 8.3.

Suivre les 2 étapes suivantes :
  1. Télécharger le fichier plume.php : com.dnada.plume.zip.
  2. Le placer sur votre serveur avec les 3 fichiers suivants : index.html, index.php et script.js
index.html :
<!DOCTYPE html>
<html lang="fr">
<head>

<link href="style.css" rel="stylesheet">
<script src="script.js"></script>

</head>
<body>
<div id="ajax_id">
</div>
<div>
{.mon_template() .}
</div>
</body>
</html>
script.js :
<script>
$.start(function() {
trace("Exemple de trace depuis JS");
$('ajax_id').innerHTML = $.ajax.ma_methode(42);
});
</script>
index.php :
<?php
define('PLUME_REPORT', "report@ma−societe.com");
define('PLUME_DEBUGGING', '77.0.1.2');
require('plume.php');
define('TRACE', true);
// $base = new Base("mon−client");
Template::ajax(function() { });
Template::display();

function template_mon_template($pattern) {
traceability(TRACE);

trace("Exemple de trace depuis PHP");
$html = "<span>Voici mon premier template.</span>";

return traceability(TRACE, $html);
}
// fonction appelé en JS : $.ajax.ma_methode(par1).then(function(resultat) { }
function ajax_ma_methode($parametre1 = 'default') {
traceability(TRACE);

trace("Exemple de trace depuis un appel JS−>PHP");
$valeur = "Voici ma première fonction ajax avec le paramètre : " + parametre1;

return traceability(TRACE, $valeur);
}
?>
Et c'est tout ! Regarder le résultat de votre page, et n'oubliez pas de regarder la partie Console du navigateur.
Ah Si ! Dites nous ce que vous pensez de cette librairie, faites nous remontez vos remarques, bug (pas possible), améliorations (avec plaisir), ou simplement un petit merci, c'est par ici :

Copyright

Cette librairie Plume est l'œuvre et la propriété de DNada.
Même si gratuite, cette librairie ne peut être utilisée partiellement ou intégralement dans des développements rémunérées, sauf par consentement écrit de DNada. (Mais bon, aucune crainte à avoir, aucun refus n'a été fait jusqu'à présent).
Pour toutes demandes ou questions, merci de nous contacter :