Files
cookiecode/dist/cookiecode.js
T
2026-07-21 22:12:43 +02:00

478 lines
12 KiB
JavaScript

/*
* CookieCode - apprendre l'algorithmique avec des recettes de cuisine.
* Microlibrairie sans dépendance, compatible navigateur, CDN et Node/CommonJS.
*/
((root, factory) => {
if (typeof module === "object" && module.exports) {
module.exports = factory();
} else {
root.CookieCode = factory();
}
})(typeof globalThis !== "undefined" ? globalThis : this, () => {
var configuration = {
sortie: "console",
afficherNotions: true,
};
function configurer(options) {
options = options || {};
if (Object.hasOwn(options, "sortie")) {
configuration.sortie = options.sortie;
}
if (Object.hasOwn(options, "afficherNotions")) {
configuration.afficherNotions = Boolean(options.afficherNotions);
}
return CookieCode;
}
function texte(valeur, defaut) {
if (valeur === undefined || valeur === null || valeur === "") {
return defaut || "";
}
return String(valeur);
}
function echapperHTML(valeur) {
return texte(valeur)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function creerElement(type, nom, quantite, unite, valeur) {
if (!nom && nom !== 0) {
throw new Error(
"CookieCode : un nom est obligatoire pour creer un " + type + ".",
);
}
return {
type: type,
nom: texte(nom),
quantite: quantite,
unite: unite,
valeur: valeur,
};
}
function ingredient(nom, quantite, unite) {
return creerElement("ingredient", nom, quantite, unite);
}
function materiel(nom) {
return creerElement("materiel", nom);
}
function constante(nom, valeur) {
return creerElement("constante", nom, undefined, undefined, valeur);
}
function preparation(nom) {
return {
type: "preparation",
nom: texte(nom, "preparation"),
etat: {},
definir: function (cle, valeur) {
this.etat[cle] = valeur;
return this;
},
est: function (valeur) {
return (
this.etat.texture === valeur ||
this.etat.etat === valeur ||
this.etat[valeur] === true
);
},
};
}
function nomDe(element) {
if (element && typeof element === "object" && element.nom !== undefined) {
return texte(element.nom);
}
return texte(element);
}
function quantiteDe(element, quantite, unite) {
var q = quantite !== undefined ? quantite : element?.quantite;
var u = unite !== undefined ? unite : element?.unite;
if (q === undefined || q === null || q === "") {
return "";
}
return texte(q) + (u ? " " + texte(u) : "");
}
function formatElement(element, quantite, unite) {
var quantiteTexte = quantiteDe(element, quantite, unite);
var nom = nomDe(element);
return quantiteTexte ? quantiteTexte + " de " + nom : nom;
}
function conditionLisible(condition) {
if (typeof condition === "string") {
return condition;
}
if (typeof condition === "boolean") {
return condition ? "condition vraie" : "condition fausse";
}
if (typeof condition === "function") {
return "condition fournie";
}
return "condition";
}
function evaluerCondition(condition) {
if (typeof condition === "function") {
return Boolean(condition());
}
if (typeof condition === "boolean") {
return condition;
}
return true;
}
function creerEtape(texteEtape, notion, enfants) {
return {
texte: texteEtape,
notion: notion,
enfants: enfants || [],
};
}
function Recette(nom) {
if (!nom && nom !== 0) {
throw new Error("CookieCode : une recette doit avoir un nom.");
}
this.nom = texte(nom);
this.etapes = [];
this.notions = {
programme: true,
variables: [],
constantes: [],
fonctions: [],
boucles: false,
conditions: false,
sequence: true,
};
}
Recette.prototype._noterFonction = function (nom) {
if (this.notions.fonctions.indexOf(nom) === -1) {
this.notions.fonctions.push(nom);
}
};
Recette.prototype._noterVariable = function (nom) {
if (nom && this.notions.variables.indexOf(nom) === -1) {
this.notions.variables.push(nom);
}
};
Recette.prototype._noterConstante = function (nom) {
if (nom && this.notions.constantes.indexOf(nom) === -1) {
this.notions.constantes.push(nom);
}
};
Recette.prototype._ajouterEtape = function (texteEtape, notion, enfants) {
this.etapes.push(creerEtape(texteEtape, notion, enfants));
return this;
};
Recette.prototype.prendre = function (nom, quantite, unite) {
this._noterVariable(nomDe(nom));
this._noterFonction("prendre");
return this._ajouterEtape(
"Prendre " + formatElement(nom, quantite, unite) + ".",
"variable",
);
};
Recette.prototype.utiliser = function (outil) {
this._noterConstante(nomDe(outil));
this._noterFonction("utiliser");
return this._ajouterEtape("Utiliser " + nomDe(outil) + ".", "constante");
};
Recette.prototype.ajouter = function (element, quantite, unite) {
this._noterFonction("ajouter");
if (element && element.type === "ingredient") {
this._noterVariable(element.nom);
}
return this._ajouterEtape(
"Ajouter " + formatElement(element, quantite, unite) + ".",
"fonction",
);
};
Recette.prototype.retirer = function (element) {
this._noterFonction("retirer");
return this._ajouterEtape("Retirer " + nomDe(element) + ".", "fonction");
};
Recette.prototype.couper = function (element, forme) {
this._noterFonction("couper");
return this._ajouterEtape(
"Couper " + nomDe(element) + (forme ? " en " + texte(forme) : "") + ".",
"fonction",
);
};
Recette.prototype.melanger = function () {
this._noterFonction("melanger");
return this._ajouterEtape("Melanger.", "fonction");
};
Recette.prototype.verser = function (contenu, contenant) {
this._noterFonction("verser");
return this._ajouterEtape(
"Verser " +
nomDe(contenu) +
(contenant ? " dans " + nomDe(contenant) : "") +
".",
"fonction",
);
};
Recette.prototype.chauffer = function (element, temperature) {
this._noterFonction("chauffer");
return this._ajouterEtape(
"Chauffer " +
nomDe(element) +
(temperature ? " a " + nomDe(temperature) : "") +
".",
"fonction",
);
};
Recette.prototype.cuire = function (duree, unite) {
this._noterFonction("cuire");
return this._ajouterEtape(
"Cuire" +
(duree
? " pendant " + texte(duree) + (unite ? " " + texte(unite) : "")
: "") +
".",
"fonction",
);
};
Recette.prototype.attendre = function (duree, unite) {
this._noterFonction("attendre");
return this._ajouterEtape(
"Attendre " + texte(duree) + (unite ? " " + texte(unite) : "") + ".",
"fonction",
);
};
Recette.prototype.repeter = function (nombre, action) {
this.notions.boucles = true;
this._noterFonction("repeter");
var bloc = new Recette("bloc repeter");
if (typeof action === "function") {
action(bloc);
}
this._fusionnerNotions(bloc);
return this._ajouterEtape(
"Repeter " + texte(nombre) + " fois :",
"boucle",
bloc.etapes,
);
};
Recette.prototype.si = function (condition, action, sinonAction) {
this.notions.conditions = true;
this._noterFonction("si");
var bloc = new Recette("bloc si");
var blocSinon = new Recette("bloc sinon");
var conditionVraie = evaluerCondition(condition);
if (conditionVraie && typeof action === "function") {
action(bloc);
}
if (!conditionVraie && typeof sinonAction === "function") {
sinonAction(blocSinon);
}
this._fusionnerNotions(bloc);
this._fusionnerNotions(blocSinon);
var enfants = bloc.etapes;
if (blocSinon.etapes.length > 0) {
enfants = enfants.concat([
creerEtape("Sinon :", "condition", blocSinon.etapes),
]);
}
return this._ajouterEtape(
"Si " + conditionLisible(condition) + " :",
"condition",
enfants,
);
};
Recette.prototype.servir = function (element) {
this._noterFonction("servir");
this._ajouterEtape(
"Servir" + (element ? " " + nomDe(element) : "") + ".",
"fonction",
);
return this.afficher();
};
Recette.prototype.afficher = function () {
var cible;
if (configuration.sortie === "console" || !configuration.sortie) {
if (typeof console !== "undefined" && console.log) {
console.log(this.toText());
}
return this;
}
if (
typeof configuration.sortie === "string" &&
typeof document !== "undefined"
) {
cible = document.querySelector(configuration.sortie);
if (cible) {
cible.innerHTML = this.toHTML();
}
}
return this;
};
Recette.prototype._fusionnerNotions = function (autreRecette) {
autreRecette.notions.variables.forEach((nom) => {
this._noterVariable(nom);
});
autreRecette.notions.constantes.forEach((nom) => {
this._noterConstante(nom);
});
autreRecette.notions.fonctions.forEach((nom) => {
this._noterFonction(nom);
});
this.notions.boucles = this.notions.boucles || autreRecette.notions.boucles;
this.notions.conditions =
this.notions.conditions || autreRecette.notions.conditions;
};
Recette.prototype._lignesEtapes = function (etapes, prefixe, indentation) {
var lignes = [];
etapes.forEach(function (etape, index) {
var numero = prefixe ? prefixe + "." + (index + 1) : String(index + 1);
lignes.push(indentation + numero + ". " + etape.texte);
if (etape.enfants && etape.enfants.length > 0) {
lignes = lignes.concat(
this._lignesEtapes(etape.enfants, numero, indentation + " "),
);
}
}, this);
return lignes;
};
Recette.prototype._lignesNotions = function () {
var lignes = [
"",
"Notions utilisees :",
"- Recette = programme",
"- Sequence = les etapes sont executees dans l'ordre",
];
if (this.notions.variables.length > 0) {
lignes.push(
"- Ingredients = variables : " + this.notions.variables.join(", "),
);
}
if (this.notions.constantes.length > 0) {
lignes.push(
"- Materiel / valeurs fixes = constantes : " +
this.notions.constantes.join(", "),
);
}
if (this.notions.fonctions.length > 0) {
lignes.push(
"- Actions = fonctions : " + this.notions.fonctions.join(", "),
);
}
if (this.notions.boucles) {
lignes.push("- Repeter = boucle");
}
if (this.notions.conditions) {
lignes.push("- Si = condition");
}
return lignes;
};
Recette.prototype.toText = function () {
var lignes = ["Recette : " + this.nom, "", "Etapes :"];
lignes = lignes.concat(this._lignesEtapes(this.etapes, "", ""));
if (configuration.afficherNotions) {
lignes = lignes.concat(this._lignesNotions());
}
return lignes.join("\n");
};
Recette.prototype.toHTML = function () {
var html = '<section class="cookiecode-recette">';
html += "<h2>Recette : " + echapperHTML(this.nom) + "</h2>";
html += "<h3>Etapes</h3>";
html += etapesHTML(this.etapes);
if (configuration.afficherNotions) {
html += "<h3>Notions utilisees</h3><ul>";
this._lignesNotions()
.slice(2)
.forEach((ligne) => {
html += "<li>" + echapperHTML(ligne.replace(/^- /, "")) + "</li>";
});
html += "</ul>";
}
html += "</section>";
return html;
};
function etapesHTML(etapes) {
var html = "<ol>";
etapes.forEach((etape) => {
html += "<li>" + echapperHTML(etape.texte);
if (etape.enfants && etape.enfants.length > 0) {
html += etapesHTML(etape.enfants);
}
html += "</li>";
});
html += "</ol>";
return html;
}
function recette(nom) {
return new Recette(nom);
}
var CookieCode = {
configurer: configurer,
recette: recette,
ingredient: ingredient,
materiel: materiel,
constante: constante,
preparation: preparation,
version: "0.1.0",
};
return CookieCode;
});