1
 65

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.

  1. function Undo(size = 100) {
  2.     if (! Number.isInteger(size))
  3.         throw new TypeError();
  4.  
  5.     if (size < 1)
  6.         throw new RangeError();
  7.  
  8.     this._size = size;
  9.  
  10.     this._undo = [];
  11.     this._redo = null;
  12. }

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.

  1. Object.defineProperty(Undo.prototype, 'size', {
  2.     get:    function() {
  3.         return this._size;
  4.     },
  5.     set:    function(n) {
  6.         if (! Number.isInteger(n))
  7.             throw new TypeError();
  8.  
  9.         if (n < 1)
  10.             throw new RangeError();
  11.  
  12.         if (this._size > n)
  13.             this._undo = this._undo.slice(-n);
  14.  
  15.         this._redo = null;
  16.  
  17.         this._size = n;
  18.     }
  19. });

size est un accesseur qui retourne ou change la taille de la pile d'appels de fonctions gérée par this.

  1. Object.defineProperty(Undo.prototype, 'undoLength', {
  2.     get:    function() {
  3.         return this._undo.length;
  4.     }
  5. });

undoLength est un accesseur qui retourne le nombre d'appels de fonctions que this peut défaire.

  1. Object.defineProperty(Undo.prototype, 'redoLength', {
  2.     get:    function() {
  3.         return this._redo === null ? 0 : this._redo.length;
  4.     }
  5. });

redoLength est un accesseur qui retourne le nombre d'appels de fonctions que this peut refaire.

  1. Undo.prototype.push = function(f, ...args) {
  2.     if (this._undo.length >= this._size)
  3.         this._undo.shift();
  4.  
  5.     this._undo.push(args.length ? [f, args] : f);
  6.  
  7.     this._redo = null;
  8.  
  9.     return this;
  10. };

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.

  1. Undo.prototype.pop = function() {
  2.     this._undo.pop();
  3.  
  4.     this._redo = null;
  5.  
  6.     return this;
  7. };

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.

  1. Undo.prototype.undo = function() {
  2.     let exp = this._undo.pop();
  3.  
  4.     if (exp === undefined)
  5.         return false;
  6.  
  7.     let redo = this._redo;
  8.  
  9.     if (Array.isArray(exp))
  10.         exp[0](...exp[1]);
  11.     else
  12.         exp();
  13.  
  14.     exp = this._undo.pop();
  15.  
  16.     if (exp === undefined)
  17.         return true;
  18.  
  19.     this._redo = redo;
  20.  
  21.     if (this._redo === null)
  22.         this._redo = [];
  23.     else if (this._redo.length >= this._size)
  24.         this._redo.shift();
  25.  
  26.     this._redo.push(exp);
  27.  
  28.     return true;
  29. };

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.

  1. Undo.prototype.redo = function() {
  2.     if (! this._redo)
  3.         return false;
  4.  
  5.     let exp = this._redo.pop();
  6.  
  7.     if (exp === undefined)
  8.         return false;
  9.  
  10.     let redo = this._redo;
  11.  
  12.     if (Array.isArray(exp))
  13.         exp[0](...exp[1]);
  14.     else
  15.         exp();
  16.  
  17.     this._redo = redo;
  18.  
  19.     return true;
  20. };

redo dépile l'appel de fonction en haut de la pile des fonctions à refaire de this et l'exécute.

  1. Undo.prototype.clear = function() {
  2.     this._undo = [];
  3.     this._redo = null;
  4.  
  5.     return this;
  6. };

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 :

  1. <?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.

  1. <?php head('javascript', '/objectivejs/tests/Calculator.js'); ?>

Ajoute la balise <script src="/objectivejs/tests/Calculator.js"></script>à la section <head> du document HTML.

  1. var undo = new Undo();
  2.  
  3. console.log(undo.size);         // 100
  4.  
  5. undo.size = 20;
  6.  
  7. console.log(undo.size);         // 20
  8.  
  9. 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.

  1. var calc = new Calculator();

Crée une instance de la classe Calculator. Voir Objective.

  1. function fmul(val) {
  2.     undo.push(() => fdiv(val));
  3.     calc.mul(val);
  4. }
  5.  
  6. function fdiv(val) {
  7.     undo.push((v) => fmul(v), val); // context
  8.     calc.div(val);
  9. }

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.

  1. calc.value = 3;
  2.  
  3. fmul(calc.value);
  4. fmul(4);
  5.  
  6. console.log(calc.value);        // 36
  7.  
  8. console.log(undo.undoLength);   // 2
  9.  
  10. undo.undo();
  11. undo.undo();
  12.  
  13. console.log(calc.value);        // 3
  14.  
  15. console.log(undo.undoLength);   // 0
  16. console.log(undo.redoLength);   // 2
  17.  
  18. undo.undo();    // one too many
  19.  
  20. undo.redo();
  21. undo.redo();
  22.  
  23. console.log(calc.value);        // 36
  24.  
  25. console.log(undo.undoLength);   // 2
  26. console.log(undo.redoLength);   // 0
  27.  
  28. undo.redo();    // one too many
  29.  
  30. undo.undo();
  31. undo.undo();
  32.  
  33. 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
VOIR AUSSI

Editor, Model, UndoPanel

Commentaires

Votre commentaire :
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip aide 2000

Entrez un maximum de 2000 caractères.
Améliorez la présentation de votre texte avec les balises de formatage suivantes :
[p]paragraphe[/p], [b]gras[/b], [i]italique[/i], [u]souligné[/u], [s]barré[/s], [quote]citation[/quote], [pre]tel quel[/pre], [br]à la ligne,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]commande[/code], [code=langage]code source en c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].