Undo
La classe Undo permet de défaire et de refaire l'exécution d'une série d'appels de fonctions avec ou sans arguments. NOTE : La classe Undo est un code indépendant qui peut être utilisé sans Objective.js dans tout projet en JavaScript.
Voir Editor pour un exemple élémentaire et les articles Éditer un clip vidéo et Configurer des graphiques par Plotly parmi d'autres pour des exemples d'éditeurs plus complets.
Une instance de Undo gère une pile d'appels de fonctions.
La méthode undo
dépile l'appel de fonction en haut de la pile et l'exécute.
La méthode redo
dépile l'appel de fonction ajouté à la pile par la méthode undo
et l'exécute.
Une fonction qui peut se défaire empile l'appel de fonction qui annule son exécution.
Une instance de Model peut défaire et refaire les modifications de ses données.
Une instance de Editor a des actions pour défaire et refaire les modifications opérées sur un modèle.
- function Undo(size = 100) {
- if (! Number.isInteger(size))
- throw new TypeError();
- if (size < 1)
- throw new RangeError();
- this._size = size;
- this._undo = [];
- this._redo = null;
- }
Crée une instance de la classe Undo.
size
définit le nombre d'appels de fonctions que la pile peut contenir, 100 par défaut.
- Object.defineProperty(Undo.prototype, 'size', {
- get: function() {
- return this._size;
- },
- set: function(n) {
- if (! Number.isInteger(n))
- throw new TypeError();
- if (n < 1)
- throw new RangeError();
- if (this._size > n)
- this._undo = this._undo.slice(-n);
- this._redo = null;
- this._size = n;
- }
- });
size
est un accesseur qui retourne ou change la taille de la pile d'appels de fonctions gérée par this
.
- Object.defineProperty(Undo.prototype, 'undoLength', {
- get: function() {
- return this._undo.length;
- }
- });
undoLength
est un accesseur qui retourne le nombre d'appels de fonctions que this
peut défaire.
- Object.defineProperty(Undo.prototype, 'redoLength', {
- get: function() {
- return this._redo === null ? 0 : this._redo.length;
- }
- });
redoLength
est un accesseur qui retourne le nombre d'appels de fonctions que this
peut refaire.
- Undo.prototype.push = function(f, ...args) {
- if (this._undo.length >= this._size)
- this._undo.shift();
- this._undo.push(args.length ? [f, args] : f);
- this._redo = null;
- return this;
- };
push
ajoute la fonction f
avec les arguments args
à la pile des appels de fonctions à défaire de this
.
Si la pile de this
est pleine, l'appel de fonction en bas de la pile des fonctions à défaire est supprimé.
IMPORTANT : Empiler une fonction à défaire vide la pile des fonctions à refaire.
- Undo.prototype.pop = function() {
- this._undo.pop();
- this._redo = null;
- return this;
- };
pop
retire l'appel de fonction en haut de la pile des appels de fonctions à défaire de this
.
NOTE : pop
est normalement uniquement appelée par undo
.
- Undo.prototype.undo = function() {
- let exp = this._undo.pop();
- if (exp === undefined)
- return false;
- let redo = this._redo;
- if (Array.isArray(exp))
- exp[0](...exp[1]);
- else
- exp();
- exp = this._undo.pop();
- if (exp === undefined)
- return true;
- this._redo = redo;
- if (this._redo === null)
- this._redo = [];
- else if (this._redo.length >= this._size)
- this._redo.shift();
- this._redo.push(exp);
- return true;
- };
undo
dépile l'appel de fonction en haut de la pile des fonctions à défaire de this
et l'exécute.
Une fonction qui peut se défaire empile l'appel de fonction qui annule son exécution.
undo
dépile cet appel et l'ajoute à la pile des fonctions à refaire de this
.
- Undo.prototype.redo = function() {
- if (! this._redo)
- return false;
- let exp = this._redo.pop();
- if (exp === undefined)
- return false;
- let redo = this._redo;
- if (Array.isArray(exp))
- exp[0](...exp[1]);
- else
- exp();
- this._redo = redo;
- return true;
- };
redo
dépile l'appel de fonction en haut de la pile des fonctions à refaire de this
et l'exécute.
- Undo.prototype.clear = function() {
- this._undo = [];
- this._redo = null;
- return this;
- };
clear
vide la pile des appels de fonctions à défaire et à refaire.
Test
Téléchargez le code de la classe Calculator et du programme de test unitaire de la classe Undo :
- <?php head('javascript', '/objectivejs/Undo.js'); ?>
Ajoute la balise <script src="/objectivejs/Undo.js"></script>
à la section <head>
du document HTML.
head
est une fonction d'iZend.
Adaptez le code à votre environnement de développement.
- <?php head('javascript', '/objectivejs/tests/Calculator.js'); ?>
Ajoute la balise <script src="/objectivejs/tests/Calculator.js"></script>
à la section <head>
du document HTML.
- var undo = new Undo();
- console.log(undo.size); // 100
- undo.size = 20;
- console.log(undo.size); // 20
- console.log(undo.undoLength); // 0
Crée une instance de la classe Undo, affiche la taille de sa pile des appels de fonctions à défaire, la change et l'affiche.
- var calc = new Calculator();
Crée une instance de la classe Calculator. Voir Objective.
- function fmul(val) {
- undo.push(() => fdiv(val));
- calc.mul(val);
- }
- function fdiv(val) {
- undo.push((v) => fmul(v), val); // context
- calc.div(val);
- }
fmul
empile la division de la valeur de la calculette par val
avant de la multiplier par val
.
fdiv
empile la multiplication de la valeur de la calculette par val
avant de la diviser par val
.
fmul
empile une fonction. div
empile une fonction avec un argument.
- calc.value = 3;
- fmul(calc.value);
- fmul(4);
- console.log(calc.value); // 36
- console.log(undo.undoLength); // 2
- undo.undo();
- undo.undo();
- console.log(calc.value); // 3
- console.log(undo.undoLength); // 0
- console.log(undo.redoLength); // 2
- undo.undo(); // one too many
- undo.redo();
- undo.redo();
- console.log(calc.value); // 36
- console.log(undo.undoLength); // 2
- console.log(undo.redoLength); // 0
- undo.redo(); // one too many
- undo.undo();
- undo.undo();
- console.log(calc.value); // 3
Affichez la page générée par le fichier testUndo.phtml et vérifiez la trace dans la console du navigateur :
100
20
0
36
2
3
0
2
36
2
0
3
Commentaires