diff src/DML/MainVisBundle/Resources/assets/marionette/modules/ContextModule/ContextModule.30-StateHistory.js @ 0:493bcb69166c

added public content
author Daniel Wolff
date Tue, 09 Feb 2016 20:54:02 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DML/MainVisBundle/Resources/assets/marionette/modules/ContextModule/ContextModule.30-StateHistory.js	Tue Feb 09 20:54:02 2016 +0100
@@ -0,0 +1,164 @@
+"use strict";
+
+App.module("ContextModule", function(ContextModule, App, Backbone, Marionette, $, _, Logger) {
+
+    // Define private variables
+    var logger = null;
+
+    ContextModule.addInitializer(function(options){
+        
+//        logger = Logger.get("ContextModule.StateBookmark");
+//        logger.setLevel(Logger.DEBUG);
+        
+        /**
+         * StateHistory stores a history of a serialized state
+         * and provides an API to switch between these states
+         * 
+         * A user simply updates the value of currentSerializedState attribute
+         * and calls undo(), redo() or reset() to move along the history
+         * There is no direct access to undo and redo stacks, but it is possible
+         * to know about them by calling canUndo() and canRedo()
+         * 
+         * serialize / unserialize deal with the object of the following structure
+         *      {
+         *          undoStack: [{recent}, {less recent}, ... {the oldest}]
+         *          redoStack: [{next}, {the following}, ... {the last}]
+         *          currentSerializedState: {}
+         *      }
+         *      
+         * attribute compoundChangeDetector is a function that helps decide if the changes between
+         *      the given three serialized states can be treated as a single change.
+         *      This is needed to avoid too many undo states on simple actions like text typing
+         *      compoundChangeDetector returns true if the change between the first, the second
+         *      and the third object can be considered as a single compound change
+         *      (i.e. there is no need to create an extra undo action)
+         */
+        ContextModule.StateHistory = Backbone.Model.extend({
+            defaults: {
+                maxStackSize: 50,
+                currentSerializedState: undefined,
+                compoundChangeDetector: undefined
+            },
+
+            _NONE: 0,
+            _UNDO: 1,
+            _REDO: 2,
+            _UNSERIALIZE: 3,
+            
+            /**
+             * @memberOf App.ContextModule.StateBookmark
+             */
+            initialize: function() {
+                this._undoStack = []; 
+                this._redoStack = [];
+                this._currentSerializedStateChangeMode = this._NONE;
+                
+                this.on("change:maxStackSize", this._trimStacks, this);
+                this.on("change:currentSerializedState", this._registerNewSerializedState, this);
+            },
+
+            _registerNewSerializedState: function() {
+                var previousSerialisedState = this.previous("currentSerializedState");
+                if (this._currentSerializedStateChangeMode == this._UNDO) {
+                    this._redoStack.unshift(previousSerialisedState);
+                } else if (this._currentSerializedStateChangeMode == this._REDO) {
+                    this._undoStack.unshift(previousSerialisedState);
+                } else if (this._currentSerializedStateChangeMode == this._UNSERIALIZE) {
+                } else {
+                    if (!_.isEqual(previousSerialisedState, this.attributes.currentSerializedState)) {
+                        if (this._undoStack.length == 0
+                            || !_.isFunction(this.attributes.compoundChangeDetector)
+                            || !this.attributes.compoundChangeDetector.call(currentSerializedState, previousSerialisedState, this._undoStack[0]))
+                        {
+                            this._undoStack.unshift(previousSerialisedState);
+                        }
+                        this._redoStack = [];
+                    }
+                }
+                this._trimStacks();
+            },
+            
+            getCurrentSerializedState: function() {
+                
+            },
+            
+            undo: function() {
+                if (this._undoStack.length) {
+                    this._currentSerializedStateChangeMode = this._UNDO;
+                    this.set("currentSerializedState", this._undoStack.shift());
+                    this._currentSerializedStateChangeMode = this._NONE;
+                } else {
+                    throw "Undo was called when undo stack was empty";
+                }
+            },
+            
+            redo: function() {
+                if (this._redoStack.length) {
+                    this._currentSerializedStateChangeMode = this._REDO;
+                    this.set("currentSerializedState", this._redoStack.shift());
+                    this._currentSerializedStateChangeMode = this._NONE;
+                } else {
+                    throw "Redo was called when undo stack was empty";
+                }
+            },
+            
+            reset: function() {
+                var stacksWereNotEmpty = this._undoStack.length || this._redoStack.length;
+                this._undoStack = [];
+                this._redoStack = [];
+                if (stacksWereNotEmpty) {
+                    this.trigger("change");
+                }
+            },
+            
+            canUndo: function() {
+                return !!this._undoStack.length;
+            },
+            
+            canRedo: function() {
+                return !!this._redoStack.length;
+            },
+            
+            serialize: function() {
+//                logger.debug("method called: StateHistory::serialize");
+
+                var result = {
+                        currentSerializedState: this.attributes.currentSerializedState,
+                        undoStack: _.clone(this._undoStack),
+                        redoStack: _.clone(this._redoStack),
+                };
+
+                return result;
+            },
+
+            unserialize: function(serializedAttributes) {
+                this._currentSerializedStateChangeMode = this._UNSERIALIZE;
+                
+                var fixedSerializedAttributes = serializedAttributes;
+                if (!_.isSimpleObject(serializedAttributes)) {
+                    fixedSerializedAttributes = {};
+                }
+                
+                this._undoStack = _.isArray(fixedSerializedAttributes.undoStack) ?
+                        fixedSerializedAttributes.undoStack : [];
+                this._redoStack = _.isArray(fixedSerializedAttributes.redoStack) ?
+                        fixedSerializedAttributes.redoStack : [];
+                this._trimStacks();
+
+                this.set("currentSerializedState", fixedSerializedAttributes.currentSerializedState);
+
+                this._currentSerializedStateChangeMode = this._NONE;
+            },
+            
+            _trimStacks: function() {
+                if (this._undoStack.length > this.attributes.maxStackSize) {
+                    this._undoStack = this._undoStack.slice(0, this.attributes.maxStackSize);
+                }
+                if (this._redoStack.length > this.attributes.maxStackSize) {
+                    this._redoStack = this._redoStack.slice(0, this.attributes.maxStackSize);
+                }
+            }
+        });
+
+    });
+}, Logger);