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 OpenSource, léger et permettant une facilité et souplesse dans le développement de vos applications Web.
Une création SASU DNada.
Mise à jour le 11/05/2020.

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 un<script src="/plume.js"></script> en HTML.

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

<link href="/lib/plume.css" rel="stylesheet"/>
<script src="/lib/plume.js"></script>

</head>
<body>

{.mon_template() … .}

</body>
</html>
D'un script JS :
$.start(function() {
$('mon_id').on('clic', function(event, tag) {

})
$.ajax.ma_methode(42, [1, 2, 3, 5, 7]).then(function(resultat) {

})
});
Et côté serveur PHP :
<?php
// permet d'ajouter des fonctionnalités de debugging au poste indiqué par son IP
define('PLUME_TRACE_IP', '77.0.1.2');
// spécifie une adresse où le développeur pourra recevoir des données de débugging.
define('PLUME_MAIL_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();
// affiche la page HTML et les templates (voir HTML : Template)
Template::display();
// exemple de fonction template (dans l'exemple HTML plus haut)
function template_mon_template($pattern) {

}
// exemple de méthode ajax (dans l'exemple JS plus haut)
function ajax_ma_methode($premier = 'default', $deuxieme = false, $etc = null) {

}
?>

HTML

Plume apporte des fonctionnalités au niveau du HTML.

HTML : Template

Le mécanisme de template permet de relier des bloques nommés de texte présents en page HTML à une fonction PHP, pour générer du HTML dynamique.

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.
{?nom_de_fonction()   …  ?}
{*nomDeFonction(un, deux, trois) … *}
Les templates peuvent contenir des champs, modifiable depuis la fonction PHP, dont le format est un nom de variable PHP (sans le dollar) encadré d'accolade.
{+fonction()
texte statique {variable_dynamique} texte statique
+}

Template : Principe

Exemple{*xxx() html *} etfunction template_xxx() { /* php */ }.
Pour arriver dynamiquement à ce résultat :
<h1>Liste des satellites de Jupiter</h1>
<ol>
<li>Io</li>
<li>Europe</li>
<li>Ganymède</li>
<li>Callisto</li>
</ol>
On créé une page HTML (exemple page.html), contenant :
<h1>Liste des satellites de {planete}</h1>
<ol>
{*satellite()
<li>{nom}</li>
*}
</ol>
On code en PHP, les deux templates "planete" et "satellite" :
<?php
require("plume.php");
function template_planete() {
return "Jupiter";
}
function template_satellite($pattern) {
foreach (['Io', 'Europe', 'Ganymède', 'Callisto'] as $satellite)
$html .= $pattern−>html( [ 'nom' => $satellite ] );
return $html;
}
Et on termine en demandant d'afficher ces templates :
Template::display("page.html");
?>

Template : Imbrication

Les templates peuvent s'imbriquer.
En reprenant l'exemple précédent, on peut afficher plus d'information par satellite.
<h1>Liste des satellites de Jupiter</h1>
<ol>
<li>
Io
<ul>
<li>Diamètre: 3 643Km</li>
<li>Période: 1,769d</li>
<li>Gravité: 1,79m/s2</li>
</ul>
</li>
<li>
Europe
<ul>
<li>…</li>
</ul>
</li>
<li>
Ganymède
<ul>
<li>…</li>
</ul>
</li>
<li>
Callisto
<ul>
<li>…</li>
</ul>
</li>
</ol>
On modifie la page HTML, avec :
{*planete()
<h1>Liste des satellites de {planete}</h1>
<ol>
{+satellite()
<li>
{nom}
<ul>
{.information()
<li>{nom}: {valeur}</li>
.}
</ul>
</li>
+}
</ol>
*}
Et on code en PHP, le template « planete », son template imbriqué « satellite », et le sous-template imbriqué « information » :
<?php
require("plume.php");
$planetes = [
"Jupiter" => [
[
"Io" => [
"Diamètre" => "3 643Km",
"Période" => "1,769d",
"Gravité" => "1,79m/s2",
],
// autre satellite
],
// autre planète
],
];
function template_planete($pattern) {
global $planetes;

foreach ($planetes as $planete => $satellites) {
$html .= $pattern−>html([
'planete' => $planete,
'satellite' => $satellites
]);
}
return $html;
}
function template_satellite($pattern, $satellites) {
foreach ($satellites as $nom => $information) {
$html .= $pattern−>html([
'nom' => $nom,
'information' => $information
]);
}
return $html;
}
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 de faire quelques tests côté template.
{?tests()
{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>}}
?}
<?php
function template_tests($pattern) {
return $pattern−>html([
'var_ok' => 'x',
'var_ko' => '', // ou false ou null
]);
}
?>
Affichera :
test ok
sinon ko
x ou alors ko
<span> x </span>

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−>parameter(1),
'deux' => $pattern−>parameter(2),
'trois' => $pattern−>parameter(3),
'quatre' => $pattern−>parameter(4)
]; // affichera : <span>1:I, 2:II, 3:III, 4:IV</span>
}
?>

Template : méthodes

L'API template comprend :
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
Template::display();
?>
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
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>
parameter():
Permet de récupérer les paramètres :
() nombre; (true) ['un', …], (0) "un, ..."; (x) "x"
<html>
{?foo(un , "deux", 3)

?}
</html>
<?php
function template_foo($pattern) {
$count = $pattern−>parameter(); // renverra le nombre de paramètres => 3
$list = $pattern−>parameter(true); // renverra la liste des paramètres => ['un', 'deux', '3']
$element = $pattern−>parameter(1) // si positif non−nul, renverra le xème élément => 'un'
$string = $pattern−>parameter(0) // renverra la chaîne de paramètre => 'un , "deux", 3'
}
?>
extract():
Permet d'extraire un template ou la totalité d'un fichier de templates.
  1. Paramètre: 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 //, par de la racine du serveur
  2. Paramètre: Nom du template : si absent, prend la totalité du fichier
    <html>
    Partie non−extraite
    {?foo()
    text {field}
    {+bar()
    and {field}
    +}
    ?}
    Partie non−extraite
    </html>
    <?php
    // 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;

    function template_foo($pattern) {
    return $pattern−>html([ 'field' => "foo" ]);
    }
    function template_bar($pattern) {
    return $pattern−>html([ 'field' => "bar" ]);
    }
    ?>
Rendra :
text test and
text foo and bar
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" ]);
}
?>
ajax():
Cette méthode recherche si une fonction RPC est appelé, et si c'est le cas, lance la méthode associée et sort du PHP.
<?php
Template::ajax();
Template::display();
?>

Define:

Des fonctionnalités plus poussées mais non obligatoire peuvent être mis en fonctionnement.
PLUMEFILETRIGGER:
SiPLUME_FILE_TRIGGER 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
Cela sert pour modifier le chemin pour d'autre média par exemple :
define("PLUMEFILETRIGGER", function($path) {
global $mobile;
if ($mobile)
return pregreplace('/.(?=w+)$/', '-mobile.', $path);
return $path;
});
PLUMETEMPLATE_TRIGGER:
SiPLUME_TEMPLATE_TRIGGER est défini comme fonction, elle sera appalé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()*}
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 : $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 $.on
<div onclick="monClick()"></div>
Est identique à :
<div $click="monClick(this)"></div>
De plus, lors de l'appel de la méthode JS :

HTML : calendar

Ajout un nouveau type d'input pour la saisie de jour, avec popup-window associé : de à
<input type="calendar">
<!−−− interval −−−>
<input type="calendar" end="+">
<input type="calendar" begin="">

HTML : slide(s)

Ajout un nouveau type d'input de valeur sous forme d'une glissière.
Accepte les attributs suivants :

slide

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

slides

La différence est au niveau des attributs : Double glissière à deux valeurs :
<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>
valeur minimum : et maximum :

HTML : strip

Ajout d'un nouveau type d'input de sélection d'énumérés. L'attributenum peut recevoir soit une chaîne de mots, soit une liste de chaînes, soit un objet d'attribut/valeur.
<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 : Segmented

Ajout d'un nouveau type d'input de sélection d'énumérés. L'attributenum peut recevoir soit une chaîne de mots, soit une liste de chaînes, soit un objet d'attribut/valeur.
<input type="segmented" enum="lundi mardi mercredi jeudi vendredi" value="mardi"/>
<input type="segmented" enum="['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi']" value=""/>
<input type="segmented" enum="{lun: 'lundi', mar: 'mardi', mer: 'mercredi', jeu: 'jeudi', ven: 'vendredi'}"/>

HTML : menu

Ajout d'un menu associé à un élément HTML (input, div, …) , avec recherche saisissable (et beaucoup d'options comportementales possibles).
La création du menu est de type: Les données sont passées dans l'attribut$menu de l'élément HTML ou en retour d'ajax ou encore en retour du load (par son return ou son callback) ou open (uniquement son callback), et peuvent être de format :
  1. Tableau de liste :
    ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', '', 'samedi', 'dimanche']
  2. Tableau nommé :
    {lun: 'lundi', mar: 'mardi', mer: 'mercredi', jeu: 'jeudi', ven: 'vendredi', sep: '', sam: 'samedi', dim: 'dimanche'}
Chaques éléments peut être de syntaxe :
  1. Chaîne de caractère
  2. Structure pouvant contenir :
    {
    text: "texte", // l'intitulé en clair
    id: "identifiant", // l'identifiant (dans le cas d'un tableau nommé), si absent, l'id prend la valeur du texte
    disabled: boolean, // désactivé si true
    mark: "classMark", // soit false ou true (ou "check") pour indiquer un "✓", soit le nom d'une classe CSS, qui s'affichera en amont de l'intitulé
    image: "src", // la source d'une image
    key: "", // raccourci clavier éventuel
    }
Si le texte égale un signe moins (-), cet élément sera remplacé par un trait de séparation horizontal non sélectionnable<hr/>.
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) : Le return du "open" peut modifier l'affichage du menu, en renvoyant un tableau de :
  1. booléan : indiquant si l'élément est activé (false égale un { disabled: true} )
  2. structure : de même type que précédemment.

HTML : modal

Propose l'affichage d'une boite d'information de type modal (bloquant temporairement ou par action de l'utilisateur pour valider ce message)
Cette méthode est en mode asynchrone : elle revient immédiatement alors que le modal n'est pas encore affiché.
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.
Le bouton le plus à droite est actionné par la touche retour-chariot, et celle la plus à gauche par la touche d'échappement.
<script>
// temporaire (sans gestion du retour de délai)
$.modal("Merci beaucoup.");

// information (sans gestion du bouton OK)
$.modal("Un erreur est survenue: " . error.message, "OK");

// choix à deux boutons avec retour asynchrone
$.modal("Merci de valider cette action", {cancel: "Annuler", add: "Ajouter"}, function(action) {
if (action == "add")

});

// choix à deux boutons avec retour en promesse
$.modal("Merci de valider cette action", {cancel: "Annuler", add: "Ajouter"}).then(function(action) {
if (action == "add")

});
</script>
Exemples :

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 implémente également d'autres méthodes, tel que :

JS : trace()

trace() est un alias de console.log, mais qui ne fonctionne que sur le navigateur du développeur. Il n'a aucun effet pour un usage autre que de développement. (voirPLUME_TRACE_IP)
Pour des traces depuis le serveur, voir PHP : trace()

JS : $

Permet de rechercher un élément dans le Document de la page HTML ou depuis un élément :
ele = $('abc');			// recherche dans tout le Document
ele = parent.$('abc'); // recherche depuis l'élément parent
Pour une recherche uniquement sur le body :
ele = $().$('HEAD');	// recherche une balide <head> dans le <body>
ele = $('HEAD'); // recherche une balide <head> dans tout le Document
Si la recherche n'abouti pas, la méthode renvoiundefined.
La recherche se fait sur :
  1. id: par son ID (qui ne doit pas être tout en majuscule et uniquement dans tout le Document) :
    ele = $('div');			// renverra <… id="div">
    ele = $('DIv'); // renverra <… id="DIv">
    ele = $('DIV); // attention : dû aux majuscules, ne renverra pas <… id="DIV"> mais plutôt le 1er DIV trouvé dans Document
    // pour une recherche d'un id en majuscule, précédé le d'un '#' : ele = $('#ID')
    ele = bar.$(
    'div'); // attention : dû à une recherche depuis un élément, ne renverra pas <… id="div"> mais plutôt le 1er DIV enfant de bar
  2. TAGNAME: par son tagName (qui doit être tout en majuscule si recherche dans tout le Document) :
    ele = $('DIV');			// renverra le 1er <div> trouvé dans le Document
    ele = bar.$('div'); // renverra le 1er <div> enfant de bar
    ele = $('DIv'); // attention : dû à une minuscule, ne renverra pas le 1er <DIV> dans le Document, mais plutôt un id égale à "DIv" <… id="DIv">
  3. #id: par son ID :
    ele = $('#div');		// renverra <… id="div">
    ele = $('#DIv'); // renverra <… id="DIv">
    ele = $('#DIV); // renverra <… id="DIV">
    ele = $(
    '# div); // renverra une erreur de syntaxe
    ele = bar.$('#div'); // sera identique à $('#div')
  4. class (.): par son attribut de classe :
    ele = $('.foo');		// renverra le 1er élément contenant la classe "foo" <… class="… foo …">
    ele = bar.$('.foo'); // renverra le 1er <… class="… foo …"> enfant de bar
  5. type (?): par son attribut type :
    ele = $('?foo');			// renverra le 1er <… type="foo">
    ele = bar.$('?foo'); // renverra le 1er <… type="foo"> enfant de bar
  6. name (@): par son attribut name, avec des variantes (^$*) possibles :
    ele = $('@foo');			// renverra le 1er élément dont le name égale à "foo" <… name="foo">
    ele = bar.$('@foo'); // renverra le 1er <… name="foo"> enfant de bar
    ele = $('@^foo'); // renverra le 1er élément dont le name commence par "foo", exemple: <… name="foobar">
    ele = $('@$foo'); // renverra le 1er élément dont le name fini par "foo", exemple: <… name="barfoo">
    ele = $('@*foo'); // renverra le 1er élément dont le name fini par "foo", exemple: <… name="barfoobar">
  7. parent (^): en remontant à son parent :
    ele = tag.$('^');				// renverra le parent de tag
    ele = tag.$('^5'); // renverra le 5ème parent de tag
    ele = tag.$('^div'); // renverra le 1er parent DIV de tag
    ele = tag.$('^=div'); // idem mais en incluant en 1ère recherche, le tag lui−même
    ele = tag.$('^=main.foo@bar'); // idem mais en cherchant un parent de genre <MAIN class="abc foo def" name="bar">
    ele = tag.$('^ div'); // attention: dû à l'espace, la recherche est tag.$('^').$('DIV')
  8. next (+): en cherchant l'élément de même niveau situé après lui :
    ele = tag.$('+');				// renverra le 1er élément à la suite et au même niveau que tag
    ele = tag.$('+5'); // renverra le 5ème élément à la suite et au même niveau que tag
    ele = tag.$('+div'); // renverra le 1er DIV à la suite et au même niveau que tag
    ele = tag.$('+div.foo?bar'); // idem mais recherche un élément de genre <DIV class="abc foo def" type="bar">
    ele = tag.$('+ div'); // attention, dû à l'espace, la recherche est tag.$('+').('DIV')
  9. previous (-): en cherchant l'élément de même niveau situé avant lui :
    ele = tag.$('');				// renverra le 1er élément en amont et au même niveau que tag
    ele = tag.$('−5'); // renverra le 5ème élément en amont et au même niveau que tag
    ele = tag.$('−div'); // renverra le 1er DIV en amont et au même niveau que tag
    ele = tag.$('−div.foo@bar'); // idem mais recherche un élément de genre <DIV class="abc foo def" name="bar">
    ele = tag.$('− div'); // attention, dû à l'espace, la recherche est tag.$('−').('DIV')
  10. [attribut]: sur un attribut précis (avec les variantes traditionnelles) :
    ele = $('[hidden]');			// recherche tous les éléments dans le Document ayant un attribut hidden
    ele = $('[hidden=true]) // recherche tous les éléments dans le Document ayant un attribut hidden à true
La recherche peut renvoyer un tableau, avec la méthode$$ :
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> :)
Et inversement, la recherche peut s'effectuer sur un tableau :
$element1 = [element1, element2].$('abc');
$elements = $$('TABLE').$$('CAPTION');
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>
Enfin, on peut chaîner les recherches dans la chaîne de recherche :
$$('#marqueur ^4 + DIV INPUT?text.valide@personnel[nom][]')
ou chaîner par appel successif :
$('marqueur').$('^4').$('+').$$('DIV').$$('INPUT?text.valide@personnel[nom][]')
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 : $.for()

$.for() permet d'appliquer une boucle intelligente et protégée.
$.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 function)
$.for(NodeListe, function(valeur, index) {}); // itère même si non itérable
$.for(42, function(valeur, index) {}); // itère 42 fois (et renvoi un [] comme si c'était un map())

JS : $.create()

Cette méthode permet de créer un nouvel élement :
$.create();				// dans le document (donc pas visible, à vous de faire un appendChild insertBefore)
$().create(); // en fin de body
$('DIV').create(); // dans un DIV existant
On peut indiquer en premier paramètre :
create("TAGNAME");			// son tagName, exemple "DIV"
create(element); // clôner un élément existant, exemple $("mon_id")
create('<tag>'); // à partir de balisage HTML
create("texte simple"); // créer simplement du texte
On peut lui attribuer en 2ème paramètres soit le nom d'une classe CSS, le name, le id, soit des attributs lors de sa création :
create(tag, {id: "xxx", "ma_classe");
create(tag, {id: "xxx", "#mon−id");
create(tag, {id: "xxx", "@le_nom_de_cet_element");
create(tag, {id: "xxx", style: {backgroundColor: 'yellow'}});
On peut spécifier en 3ème paramètre, sa position :
create(tag, false);				// soit en dernier élément si omis ou false.
create(tag, false, element); // avant un élément
create(tag, false, true); // en 1er
create(tag, false, x); // en xème élément
Si pas besoin d'attribut, passer soit{}, soitnull, soit false.

JS : $.on()

Rattache un ou plusieurs évènements à un élément.
// rattache 2 évènements
element.on("mousedown, mouseup", function(event, tag) { … }):
element.on(['click', 'keypress'], listen);
// rattache sur plusieurs élements, avec passage d'un complement de données
element.$$('DIV').on('mouseleave', function(event, tag, complement), complement);

JS : $.att()

Permet de lire ou d'assigner une valeur à un attribut.
var att = element.att('name');				// renvoi la valeur de name
var att = element.att(['name', 'id']); // renvoi 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); // clone les attributs de source à element
element.att('name,', null); // enlève l'attribut name
[tag1, tag2].att(…); // att() appliqué sur plusieurs éléments

JS : $.design()

Permet d'ajouter, enlever ou vérifier une classe CSS à un élément.
element.design("class1 classx", true);		// ajout les 2 classes à element
element.design("class1 classx", false); // enlève les 2 classes (si présentes)
var present = element.design("class"); // test la présence de la classe
[tag1, tag2].design(…); // design() appliqué récursivement sur le tableau

JS : $.css()

Permet de lire le style CSS calculé ou d'affecter de nouveaux styles CSS.
var css = element.css("border");				// renvoi le border calculé
var css = element.css("border margin"); // renvoi un tableau contenant le border et la margin calculé
var css = element.css(["border" , …]); // renvoi un tableau de style
var tag = element.css("border: 1px ; …"); // affecte le bording et d'autre valeur, …
var tag = element.css({border: '1px' , …}); // même principe mais passé sous forme d'un object.
var tag = element.css(["border"], source); // clone les css de la source
var tag = element.css({border: 0}, source); // clone avec des valeurs par défaut
Le nommage accepte autant la syntaxe conventionnelle kebab-case]() que la syntaxe condensée [camelCase que
Certains styles peuvent être considéré en affectation ou en lecture (en ajoutant le suffixe "i") comme des valeurs numériques.
Cela s'applique sur les styles : margin, border, padding, mais aussi tous les suffixes : top, right, bottom, left, height et width.
Idem avec le suffixe w (pour width) et h (pour height) permettant d'avoir respectivement la somme du left+right ou la somme du top+bottom, que l'on pourra appliquer en lecture à margin, border et padding.
Même principe pour la lecture en mode hexadécimal, des couleurs qui finissent par "color", en ajoutant le suffixe "x" (background-colorx, borderColorx, colorx, …).
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("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
element.css("border", 12); // affecterait 12px sur tous les bords
element.css("margin", [12, 6, 10, 8]); // affecterait une marge de top:12px, right:6px, bottom:10px et left:8px
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

JS : $.setPos(), $.setSize(), $.setRect()

Permet de positionner (avecstyle.left etstyle.top) et dimensionner (avecstyle.width etstyle.height) l'élément.
tag.setPos(point);			// positionnement
tag.setSize(point); // dimensionne
tag.setSize(rect); // dimensionne en utilisant la taille du rect
tag.setRect(rect); // positionnement et dimensionne
Ces méthodes utilisent des notions de $Point et de $Rect.

JS : $.position()

Permet de lire les dimensions et positions absolues d'un élément
var pos = element.position();					// renvoi les positions de l'élément
var pos = element.position($('content')); // renvoi les positions à partir de l'élément content
var pos = element.position(true); // renvoi les positions de l'élément relatif à son scroll
var pos = element.position().border.topLeft.x; // position gauche
Renvoi l'objet :
{
inner: {
topLeft, // $Point haut gauche (voir plus bas l'objet $Point)
botRight, // $Point bas droit
size, // $Point représentant la taille
},
padding: {
topLeft, // …
botRight, // …
size, // …
top, // valeur entière représentant la taille du padding−top
left, // …
bottom,
right
},
border: {
topLeft,
botRight,
size,
top,
left,
bottom,
right
},
margin: {
topLeft,
botRight,
size,
top,
left,
bottom,
right
}
}
Cette méthode utilise des notions de $Point et de $Rect.
liste des valeurs renvoyées par position()

JS : $Point

Cet objet représente un point, avec les attributs{x, y} Il permet de manipuler les points avec les méthodes suivantes :
// création
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]); // fonctionne avec un tableau
let point = new $Point({x: 1, y: 2}); // fonctionne avec une simple structure
let point = new $Point({topLeft: {x: 1, y: 2}}); // fonctionne avec une structure topLeft
let point = new $Point({left: 1, top: 2}); // fonctionne avec 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()
let point = point1.copy(); // duplique le point
// modification
let p = point.add(point1, …).add(x, y); // renvoi un nouveau point, ajouté de la liste de points passés en paramètre
let p = point.sub(point1, …).sub(x, y); // renvoi un nouveau point, enlevé de la liste de points passés en paramètre
let p = point.cut(point).cut(x, y); // renvoi un nouveau point, correspondant au point du paramètre − point du this
point.move(point1, …).move(x, y); // ajout la liste de points passés en paramètre à point et le renvoi
point.back(point1, …).back(x, y); // enlève la liste de points passés en paramètre à point et le renvoi
point.rect(x1, y1, x2, y2); // réinitialise
let p = point.min(point1, …).min(x, y); // renvoi un nouveau point contenant les coordonnées minimums entre lui et les points passés en paramètres
let p = point.max(point1, …).min(x, y); // renvoi un nouveau point contenant les coordonnées maximums entre lui et les points passés en paramètres
point.minimize(point1, …).min(x, y); // affecte à point le point contenant les coordonnées minimums entre lui et les points passés en paramètres
point.maximize(point1, …).min(x, y); // affecte à point le point contenant les coordonnées maximums entre lui et les points passés en paramètres
point.x = 0;
point.y = 6.56;
// comparaison
let t = point.compare(point2); // renvoi true si point a les mêmes coordonnées que point2
let t = point.angle(point2); // renvoi false si point2 n'est pas viable, 0 s'il est égal à point, valeur en radian > 0 indiquant la direction, si différent
let t = point.includes(rect); // strictement identique que $Rect.includes(point) mais partant du point −> renvoi true si point est à l'intérieur de rect
// utilisation
let x = point.x;
let y = point.y;
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 dimmension de tag
$('CANVAS').setPos(point); // positionne et dimensionne le canvas
point.trace(); // permet d'afficher visuellement un point en trace dans la fenêtre
// exemple
div.setPos($Point(event).move(point, x, y, event2, [x, y]).sub(5, 5));
valeurs renvoyé par point.angle()

JS : $Rect

Cet objet représente un rectangle, avec les attributs $Point{topLeft, botRight, size} Il permet de manipuler les rectangles avec les méthodes suivantes :
// création
let rect = new $Rect(); // rectangle vide
let rect = new $Rect(x1, y1, x2, y2); // point en utilisant 4 valeurs numériques
let rect = new $Rect([x1, y1, x2, y2]); // fonctionne avec un tableau de 2 points
let rect = new $Rect({x: 1, y: 2}, {x: 3, y: 4}); // fonctionne avec de simples structures
let rect = new $Rect(topBot); // fonctionne avec une structure topLeft, botRight
let rect = new $Rect(topSize); // fonctionne avec une structure topLeft, size
let rect = new $Rect({left: 1, top: 2, bottom: 3, right: 4}); // fonctionne avec une simple structure
let rect = new $Rect(event1, event2); // fonctionne à partir de clientX et clientY
let rect = new $Rect(rect); // duplique
let rect = $Rect(); // sucre du new $Rect()
// décalage et/ou redimensionnement
let r = rect.add(rect1).add(point); // renvoi un nouveau rect, ajouté de rect1, …
let r = rect.sub(rect1).sub(point); // renvoi un nouveau rect, enlevé de rect1, …
rect.move(rect1).move(point); // déplace rect, et le renvoi
rect.back(rect1).back(point); // déplace rect dans l'autre sens (comme un sub) et le renvoi
rect.include(rect1).include(point); // se transforme en rect englobant son rect et le rect1 ou point
rect.resize(width, height); // ajoute ou enlève à botRight
rect.resize(point); // ajoute ou enlève à botRight
rect.resize(rect); // ajoute ou enlève à topLeft et à botRight
rect.scale(width, height); // si positif redimensionne right ou bottom, si négatif redimensionne left ou top
rect.intersect(rect2); // redimensionne rect pour correspondre au rectancle formé par l'intersection des deux rectangles
rect.size = point;
// comparaison
let t = rect.includes(rect1); // renvoi true si rect1 est totalement à l'intérieur
let t = rect.contains(rect2); // renvoi true si rect1 est en partie ou totalement à l'intérieur
let t = rect.excludes(rect1); // renvoi true si rect1 est totalement à l'extérieur
let t = rect.includes(point); // strictement identique à $Point.includes(rect) −> renvoi true si point est à l'intérieur de rect
// utilisation
let struct = rect.tolebori(); // renvoi une structure {top, left, bottom, right} contenant {topLeft.y, topLeft.x, botRight.y, botRight.x}
let struct = rect.tolebori(true); // renvoi rect après avoir mis à jour son tolebori
let tl = rect.topLeft; // $Point de l'angle le plus haut à gauche
let br = rect.botRight; // $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;
element_html.setRect(point); // positionne et dimensionne
rect.trace(); // permet d'afficher visuellement un rectangle en trace dans la fenêtre
// exemple
div.setRect($Rect(event, 50, 10).move(point, x, y, event2, [x, y]).sub(5, 5));
Le point topLeft sera toujours au dessus et sur la gauche du point botRight.

JS : $.start()

Plume lance les fonctions passées dans$.start(); lorsque la page est chargée et prête.
// 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.
});
Il est possible de bloquer les starts, en attente de données qui pourrait venir après le load de la page, tel que les requêtes asynchrones ajax, les promesses, les database, …
Le principe est de préciser en premier paramètre le ou les flags qui sont indispensables à la bonne exécution du contenu de la fonction; puis lorsque ces flags sont opérationnels, de l'indiqueer en appelant$.start avec uniquement le flag concerné.
(Cet exemple utilise des fonctions asynchrone RPC expliquées plus bas)
// exécute une fonction en asynchrone, suite à un retour du serveur PHP
ajax.differe_ready().then(function() {
// traitement
$.start("ready"); // déverouille ce flag 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 flag "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 flags "ready" et "impossible" seront déverouillés,
// donc jamais, puisque le flag "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 flag
$.start(function() {
});
Les flags sont de type chaîne de caractères, mais l'on peut également rattacher une valeur à ces flags, qui sera récupéré dans le start :
// 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, flags) {
console.log(flags.ready, flags.ressource);
});

JS : $.database()

Permet d'accéder à la base locale du navigateur
// connexion sur le database database1 en v1, contenant les store1, …, créés s'il le faut
var database = $.database("database1", ['store1', …], 1, function(db) {database = db; $.start('database');});
// attention : ouverture en asynchrone, il est important d'utiliser la variable qu'après appel du succes, d'où l'appel du start avec le flag 'database'
$.start('database', function() {
var record = {id: 42, nom: "Dupond"};

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

// lecture
database.read('store1', record.id).then(function(record) {
$('nom').value = record.nom;
}).catch(function (err) {
$.modal("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) {
// …
});
});
});
Cet exemple utilise l'itération $.for et les dialogues $.modal.

JS : extension

Plume ajoute des fonctionnalités aux objets existants.

Function.delay()

Cette méthode lance en asynchrone différé la fonction spécifiée, en lui passant le timer et éventuellement unthis, et renvoi le minuteur, pour utilisation d'unclearTimeout eventuel.
Exemple :
function ma_fonction(parametre) {
console.log(this, parametre);
}
ma_fonction.delay($('BODY'), 3000, 42); // affichera dans 3s : <body> 42
ma_fonction.delay(500, 42); // affichera dans une demie−seconde : <window> 42

String.sprintf()

Cette méthode renvoi une chaîne formatée en utilisant les paramètres passés.
Le format est composé de caractères (qui seront affichés tel quel) et d'insertion des paramètres.
La syntaxe de ces insertions est : % position complement flag signe largeur precision type. 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. % : forçant d'insérer non pas un paramètre, mais le caractère pourcentage (%).
Exemples :
console.log( "(%1u, %7u, %7u, %7u)".sprintf(42, −42.24, " +42", "a 42" ) );			// affichera (42,      42,      42,       0)
console.log( "(%1d, %7d, %7d, %7d)".sprintf(42, −42.24, " +42", "a 42" ) ); // affichera (42, −42, 42, 0)
console.log( "(%1f, %7f, %7f, %7f)".sprintf(42, −42.24, " +.42", "a 42.24" ) ); // affichera (42, −42.24, 0.42, 0)
console.log( "(%1s, %7s, %7s, %7s)".sprintf(42, −42.24, " +.42", "a 42" ) ); // affichera (42, −42.24, +.42, a 42)
console.log( "(%%)".sprintf(42, −42.24, " +.42", "a 42" ) ); // affichera (%)
console.log( "(%2$x7.1f)".sprintf(42, 42.24, " +.42", "a 42" ) ); // affichera (xxx42.2)
console.log( "(%2$x−7.3f)".sprintf(42, 42.24, " +.42", "a 42" ) ); // affichera (42.240x)
console.log( "(%.3f)".sprintf(4224.2442 ) ); // affichera (4224.244)
console.log( "(%10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera ( abc)
console.log( "(%−10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera (abc )
console.log( "(%−−10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera (abc−−−−−−−)
console.log( "(%.+10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera (.......abc)
console.log( "(%.10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera (.......abc)
console.log( "(%.10.0s)".sprintf("abcd" ) ); // affichera (......abcd)
console.log( "(%.4u, %+.4u, %.4u, %+.4u)".sprintf(42, 42, −42, −42) ); // affichera (0042, +042, 0042, +042)
console.log( "(%.4d, %+.4d, %.4d, %+.4d)".sprintf(42, 42, −42, −42) ); // affichera (0042, +042, −042, −042)

Array.first()

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

Array.second()

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

Array.last()

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

Array.delete()

Cette méthode efface l'élément passé en paramètre, puis renvoi l'index (à partir de 0) de l'élément effacé, sinon ne touche pas au tableau et renvoi -1.
let tab = ['a', 'b', 'c'];
tab.delete('b'); // renvoi 1
console.log(tab); // renvoi ['a', 'c']

Date.addDate()

Cette méthode ajoute ou enlève un nombre de jour à la date.
Exemple :
var date = new Date(2020, 0, 1, 12);
date.addDate(1);
console.log(date);
Renverra :

Date.copy()

Cette méthode renvoi une copie de la date.
Exemple :
var origine = new Date(2020, 0, 1, 12);
var reference = origine;
reference.addDate(1);
var copie = origine.copy();
reference.addDate(3);
console.log(reference, copie);
Renverra :

window.location.args

Renvoi 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 :
{
0: "nom",
1: "André",
length: 2,
nom: "Dupon",
prenom: "André"
}

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 :
[ 'rubrique', 'action' ]

window.navigator.name

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

window.navigator.mobile

Renvoi unboolean indiquant si le navigateur est sur un smartphone ou tablette.
Exemple avec votre navigateurwindow.navigator.mobile renverra :

window.navigator.version

Renvoi la version
Exemple avec votre navigateurwindow.navigator.version renverra :

JS : RPC

Plume intégre des mécanismes de RPC.

RPC : Fonctions

Il est possible d'appeler des fonctions serveurs (uniquement en PHP actuellement) en asynchrone depuis le JS.
Fonctions PHP, que le développeur autorise, avec passage de paramètres et retour d'une valeur.
Pour cela, la fonction doit être appelée à travers la classe$.ajax, classe, et cette fonction doit être préfixée parajax_ côté PHP.
Exemple avec côté client JS :
$.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);
});
Et côté serveur PHP :
function ajax_ma_methode($premier = 'default', $deuxieme = false, $etc = null) {
return [ 'ok' => [42] ];
}
Template::ajax(); // à placer avant tout envoi (tel que echo ou Template::display())
Le retour de donnée se fait donc par un return côté PHP, qui sera transmis à la fonction promesse de JS. (Il est donc inutile, voir proscrit de faire desecho)
On peut également lui faire passer un ou plusieurs fichiers (via un<input type="file"/>) ou une notion de timeout :
<script>
$('INPUT?file').on('change', function() {
if (! this.files.length)
return;
let file = this.files[0];
if (file.size > 100*1024*1024)
window.alert("Fichier trop volumineux.");
else
// le fichier, ou tableau de fichiers doivent toujours être en dernier paramètre (sauf si option de timeout)
$.ajax.download(42, file).then(function(resultat) {
if (resultat === true)
window.alert("Téléchargement réussi")
else
window.alert("Problème lors du téléchargement: " + resultat);
});
});
</script>
<?php
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);
}
return true;
}
?>
Il existe des options, qu'il est possible de passer en dernier paramètre, en retour de la méthode$.ajax.option().
Ces options sont :
timeout: une valeur entière positive exprimant le nombre de milliseconde avant que la requête aboutisse à un échec (ce qui déclenchera lecatch de la promesse).
progress: une fonction qui sera appelée régulièrement durant l'envoi et la réception, avec en paramètre :
  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éan 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).
Exemples :
// 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
progress.value = undefined; // met en attente la jauge pendant le traitement PHP
else
if (event.loaded < event.total)
progress.value = (event.total − event.loaded) / event.total; // diminue la jauge durant la réception
else
progress.display = 'none'; // ferme la jauge lorsque terminé
}})).then(succes);

RPC : Manuel

Il est également possible d'employer la classe$.ajax pour lancer juste des requêtes simples asynchrones (donc sans créer de fonction PHP particulière), en interceptant manuellement les demandes et en renvoyant une promesse en retour :
<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;
}
?>

PHP

Plume ajoute des fonctionnalités au PHP.

PHP : constante

Ces constantes permettent de personnaliser Plume :

PLUME_TRACE_IP

PLUME_TRACE_IP permet d'ajouter des fonctionnalités de debugging au poste indiqué par son IP (à chercher sur monip.org par exemple).
Il accepte : Exemple :
define('PLUME_TRACE_IP', "77.0.1.2");
define('PLUME_TRACE_IP', "77.0.1.*");
define('PLUME_TRACE_IP', "77.0.[0−3].*");
define('PLUME_TRACE_IP', ["77.0.1.2", "168.0.1.2"]);
define('PLUME_TRACE_IP', true);
define('PLUME_TRACE_IP', false);

PLUME_MAIL_REPORT

PLUME_MAIL_REPORT peut spécifier une adresse où le développeur pourra recevoir des données de débugging, de type report.
Exemple :
define('PLUME_MAIL_REPORT', "contact−technique@ma−societe.com");

PLUME_LOG

PLUME_LOG peut spécifier un dossier, où Plume cherchera à enregistrer un journal des activités SQL. Voir PHP : Log et report.
Exemple :
define('PLUME_LOG', "/www/data/log");
Par défaut, PHP va chercher un dossier :
  1. De nom "log"
  2. Se terminant par ".data/log".
  3. Se terminant par ".log".
En partant du dossier où se trouveplume.php, puis en remontant jusqu'à la racine.
Si aucun dossier n'est trouvé, le mécanisme de log n'est pas activé.
Exemple avec /www/monsite/global/plume.php :
  1. /www/monsite/global/log/
  2. /www/monsite/global.data/log/
  3. /www/monsite/global.log/
  4. /www/monsite/log/
  5. /www/monsite.data/log/
  6. /www.log/
  7. /www/log/
  8. /www.data/log/
  9. /www.log/

PLUME_SESSION

Plume lance automatiquement unsession_start().
Si vous souhaitez interdire à Plume ce mécanisme, par exemple pour le faire en amont afin de mettre à jour une variable de$_SESSION avant de charger Plume, il suffit de déclarer à faux cette constante :
<?php
define('PLUME_SESSION', false);
// avec par exemple…
session_cache_expire(366 * 24 * 60);
session_start();
$_SESSION['authentification'] = false;
require('plume.php');
?>

PHP : trace()

Procédure PHP trace() permet d'afficher des données PHP directement sur la console du navigateur, uniquement en mode développement (voirPLUME_TRACE_IP).
<?php
define('PLUME_TRACE_IP', "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_usage('titre'); // envoi la mémoire et le temps utilisé depuis le dernier trace_usage()
?>
Attention : pour fonctionner, vous devez appeler display, send ou passer par une fonction RPC
// 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 renvoit 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;
}
?>

PHP : Base

Classe PHP simplifiant la vie du développeur, par sa facilité d'utilisation, sa lisibilité et ses fonctionnalités poussées.
Tous les résultats suivants prendront cette table en exemple :
id nom ville
42 Enzo Annecy
74 Patricia La Paz
500 Yvan Annecy

Base : open

Ouverture d'une base :
$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 : 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.
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 à :

Base : read

On peut lire un enregistrement avecbase−>read().
read(= 1 champ)
Si un seul champ demandé, renvoi une chaîne correspondant à ce champ.
$record = $base−>read("select nom from table where id = 42");
Renvoi :
"Enzo"
read(> 1 champ)
Si plusieurs champs demandés, renvoi un tableau associatif :
$record = $base−>read("select id, * from table where id = 42");
Renvoi :
[
"id" => "42",
"nom" => "Enzo",
"ville" => "Annecy"
]
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" :
$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 ]

Base : select

On peut lire une liste d'enregistrement avecbase−>select().
$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é, renvoi une liste indexée, de la valeur de ce champ.
$liste = $base−>select("select nom from table");
Renvoi :
[
"Enzo",
"Patricia",
"Yvan"
]
select(>= 2 champs)
Si plusieurs champs demandés, renvoi un tableau associatif dont la clé correspond au premier champ, et la valeur, un tableau associatif de ces champs :
$liste = $base−>select("select id, nom from table where 1");
Renvoi :
[
"42" => [ "id" => "42", "nom" => "Enzo" ],
"74" => [ "id" => "74", "nom" => "Patricia" ],
"500" => [ "id" => "500", "nom" => "Yvan" ]
]
Ce champ clé devant être unique, le champ "id" est généralement utilisé.
Exemple destructif à ne pas utiliser dans ce cas :
$liste = $base−>select("select ville, nom from table where 1");
Renvoi :
[
"500" => [ "ville" => "Annecy", "nom" => "Yvan" ],
"74" => [ "ville" => "La Paz", "nom" => "Patricia" ],
]
selecti()
Avec l'extension "i", le$base−>selecti renvoi toujours une liste indexée.
$liste = $base−>selecti("select id, nom from table where 1");
Renvoi :
[
[ "id" => "42", "nom" => "Enzo" ],
[ "id" => "74", "nom" => "Patricia" ],
[ "id" => "500", "nom" => "Yvan" ]
]
selects()
Avec l'extension "s", le$base−>selects renvoi toujours une liste indexée, dont la clé regroupe une sous-liste indexée des enregistrements ayant cette clé.
$liste = $base−>selects("select ville, * from table where 1");
Renvoi :
[
"Annecy" => [
[ "ville" => "Annecy", "id" => "42", "nom" => "Enzo" ],
[ "ville" => "Annecy", "id" => "500", "nom" => "Yvan" ],
],
"La Paz" => [
[ "ville" => "La Paz", "id" => "74", "nom" => "Patricia" ],
]
]

Base : Enregistrement

On peut enregistrer des données, en préparant en premier lieu, l'enregistrement.
set()
// on prépare l'enregistrement, champ par champ
$base−>set("champ", "valeur");
// ou en bloc
$base−>set( [ "champ" => "valeur" ] );
insert()
En forçant l'ajout d'un nouvel enregistrement.
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.
$id = $base−>insert("table");
update()
En modifiant un enregistrement déjà présent.
Paramètrage :
  1. Le nom de la table.
  2. La clausewhere ou le "id".
Si la clausewhere se résume à faire unid = {i}, il suffit de passer cet "id" à la place de la clausewhere :
$ok = $base−>update("table", $id);
modify()
En modifiant un enregistrement déjà présent ou en ajoutant un nouvel enregistrement.
Paramètrage :
  1. Le nom de la table.
  2. Le "id" (pas de clausewhere ).
Cela correspond plus ou moins à faire unif (read("id")) update("table", "id"); else insert("table sans le id");
delete()
Effacement d'un enregistrement.
Paramètrage :
  1. Le nom de la table
  2. La clausewhere ou le "id".
Si l'on veut effacer toute la table, il est préférable de faire undeletes Paramètrage :
  1. Le nom de la table
    $ok = $base−>deletes("table");

Base : Requêtes

On peut également faire des requêtes particulières.
desc()
Permet d'avoir la description des tables et champs.
desc()
Renvoi la liste des tables.
$tables = $base−>desc('');
Renvoi (par exemple) :
[ "table 1", "table 2", "tablex" ]
desc('', true)
Renvoi la description des tables.
$tables = $base−>desc('', true);
Renvoi (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%')
Renvoi la liste des tables correspondant au filtre.
$tables = $base−>desc('table %');
Renvoi (par exemple) :
[ "table 1", "table 2" ]
desc(table, false)
Renvoi la liste des champs d'une table précise.
$champs = $base−>desc('table');
Renvoi (par exemple) :
[ "id", "nom", "ville" ]
desc(table, true)
Renvoi la description des champs d'une table précise.
$champs = $base−>desc('table', true);
Renvoi (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)
Renvoi l'énuméré d'un champ de typeenum ouset.
$champs = $base−>desc('table', "ville");
Renvoi :
[ "Annecy", "La Paz" ]
foreignKey()
Renvoi la liste des clés étrangères des tables demanadées, regroupé par table.
Il accepte en paramètre : Les tableaux renvoyés contiennent : Exemples :
$foreign1 = $base−>foreignKey('table');
$foreign2 = $base−>foreignKey(['table', 'personnels']);
$foreigns = $base−>foreignKey();
Pourrait renvoyer :
$foreign1 = [
'nom' => [ 'name' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville' => [ 'name' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
];
$foreign2 = [
'table' => [
'nom' => [ 'name' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville' => [ 'name' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
],
'personnels' => [
'telephone' => [ 'name' => "personnels", 'source' => "telephone", 'foreign' => "annuaire", 'key' => "id" ],
],
];
$foreigns = [
'table' => [
'nom' => [ 'name' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville' => [ 'name' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
],
'personnels' => [
'telephone' => [ 'name' => "personnels", 'source' => "telephone", 'foreign' => "annuaire", 'key' => "id" ],
],
'societes' => [
'directeur' => [ 'name' => "societes", 'source' => "directeur", 'foreign' => "personnels", 'key' => "id" ],
],
];
sql()
Il est possible de produire n'importe quelles requêtes SQL spécifiques.
$base−>sql('ma requete sql');
$base−>sql("alter table {w} comment {s}", $ma_table, $mon_commentaire);
trace()
Enfin, possibilité de générer des traces (envoyé directement sur la console du navigateur) :
// renvoi ['sql','result','error','time'] sur la dernière requête
$trace = $base−>trace();
// envoi sur la console.log ['sql','result','error','time'] sur la dernière requête
$base−>trace('title');
// temps maximum avant de déclencher les logs (voir plus bas "PHP : Log et report")
$base−>trace_log(0.500);
close()
Il est recommander de fermer les accès aux bases avant de terminer le programme PHP.
$base−>close();

Base : clause where

Les requêtes SQL utilisé dans read(), select(), update() et modify(), peut être rassemblé dans l'annuaire.
Exemple :
// Avec cette valeur dans l'annuaire
sql:requete_42 select * from table where 1;
// la requête :
$base−>read("requete_42");
// sera remplacé par :
$base−>read("select * from table where 1");

Cette clausewhere peut également contenir (après remplacement) des insertions, permettant de formattage et la protection de variable extérieur à la requête.
Ces insertions ont une syntaxe de une ou plusieurs lettre entre guillemets, qui sécurisera, formatera et placera les paramètres passées à la fonction, dans la requête.
$record = $base−>read("select * from table where id = {i} and nom = {s} and ville in {as}", "42", "Enzo", ["Annecy"]);
Les insertions possibles sont : Exemple :
// Avec cette valeur dans l'annuaire
value:client ma_table
// ce format SQL
$base−>read("select * from {:client} where 1 {and validate = 42} {?and name like {sb}} and level in {as}", false, "En", [−1, null, "etc", "('"%_)", ""]);
// sera remplacé par cette chaîne de caractères
$base−>read('select * from ma_table where 1 and name like
"En%" and level in ("−1", "", "etc", "('"%_)", "")');

PHP : mail

Classe PHP permettant de préparer et envoyer des mails.
// 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"))
echo
"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."];
Le constructeur accepte d'initialiser directement certains paramètres :
//	$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) :
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().

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 :
function template_produit($pattern) {
return markdown("mon_fichier_markdown.md");
}
Où depuis un template :
<html>
{+markdown(mon_fichier_markdown)+}
</html>
L'implémentation Markdown respecte la syntaxe suivante :

Markdown : Texte

Une fin de ligne provoquera un retour à la ligne HTML<br/> automatiquement.
Sauf si la ligne finie ou commence par une balise en ligne, tel que<span>, <code>, dans ce cas, pour forcer le retour à la ligne, il faut ajouter un espace en fin de ligne.
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 rien n'interdit d'insérer des balises HTML. Exemple .
Les {templates} sont gérés, en dehors des blocs de code.

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

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--)

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.

Les adresses mails présentes dans la partie texte (et non dans un bloc de code) sont automatiquement protégées et transformées en adresse mail HTML.
Exemple : contact@d-nada.com
Donnera :

Markdown : Image

Syntaxe : !texte alternatif 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 supprimé de ce texte) : largeur/hauteur/centrage.
Avec :
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 ?
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 ?
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 ?
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 ?
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 ?
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 plus pour une liste non-ordonnée (•, •, •), ou moins+ 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

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.
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 1ère citation
> ceci n'est pas une citation
Donnera :
Exemple de citation
imbriqué sur
plusieurs niveaux
et sur
plusieurs lignes
fin de la 1ère citation
> ceci n'est pas une citation

Markdown : Sommaire

Syntaxe : !{Table of Contents} ou !{Table of Contents(de 1 à 6)}
Formatage : construit dynamiquement un sommaire, encadré de<nav>.
Exemple :
!{Table of Contents(5)}
Donnera le sommaire de cette page en affichant que les 4 premiers niveaux.

Markdown : Social

Syntaxe : ?[social](lien facebook)
Formatage : remplace par un lien rattaché à un icône propre au réseau social spécifié.
Exemple :
?[facebook](mon facebook)	?[skype](mon skype)	?[blog](url vers mon blog).
Donnera :
Facebook
mon skype
Blog
.

PHP : Log et report

Plume permet d'enregistrer automatiquement toutes les requêtes SQL et erreurs éventuelles, dans un fichier.log journalisé et archivé.zip.
Ce fichier se trouve dans un dossier sur le serveur, à créer pour activer ce mécanisme (voirPLUME_LOG), et nommé base_YYYY-MM-DD.log
L'enregistrement s'effectue uniquement si durant la requête SQL :
  1. Une erreur SQL survient
  2. Un warning SQL survient
  3. Le temps d'exécution de la requête SQL dépasse un délai bien précis, spécifié parBase.trace_log().
Ce délai pouvant être modifié en passant une autre valeur flottant, exprimée en seconde précis à la microseconde (par défaut, une demie seconde).
Exemplestrace_log(0.5) pour une demie-seconde, outrace_log(0) pour loguer toutes les requêtes.
De plus, en mode développement, une trace de l'erreur ou warning est affichée sur la console du navigateur.
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.
Une synthèse des erreurs et warnings peut être envoyé automatiquement par mail journalièrement, voir PLUME_MAIL_REPORT.

Installation


Suivre les 4 étapes suivantes :
  1. Télécharger les fichiers : com.dnada.plume.zip (44Ko).
  2. Les placer sur votre serveur
  3. Les appeler côté page HTML (de préférence dans un fichier entete.html qui sera appelé dans toutes vos pages, par exemple, via un template de type {header}) :
    <!DOCTYPE html>
    <html lang="fr">
    <head>

    <link href="/lib/plume.css" rel="stylesheet"/>
    <script src="/lib/plume.js"></script>

    </head>
    <body>

    {.mon_template() .}

    </body>
    </html>
  4. et dans vos sources PHP (de préférence dans un fichier.php, qui sera inclus via un require())) :
    <?php
    define('PLUME_MAIL_REPORT', "report@ma−societe.com");
    define('PLUME_TRACE_IP', '77.0.1.2');
    require('plume.php');
    $base = new Base("mon−client");
    Template::ajax();
    Template::display();

    function template_mon_template($pattern) {

    }
    // fonction appelé en JS : $.ajax.ma_methode(par1).then(function(resultat) { }
    function ajax_ma_methode($par1 = 'default') {

    }
Et c'est tout !
Ah Si ! Dites nous ce que vous pensez de cette librairie, faites nous remontez vos remarques, bug (pas possible), améliorations (possible), 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 écrite de la société DNada. (Mais bon, aucune crainte à avoir, aucun refus n'a été fait jusqu'à présent).
Pour toutes demandes ou questions, merci de nous contacter :