annotate src/DML/MainVisBundle/Resources/assets/marionette/modules/ContextModule/ContextModule.30-StateHistory.js @ 1:f38015048f48 tip

Added GPL
author Daniel Wolff
date Sat, 13 Feb 2016 20:43:38 +0100
parents 493bcb69166c
children
rev   line source
Daniel@0 1 "use strict";
Daniel@0 2
Daniel@0 3 App.module("ContextModule", function(ContextModule, App, Backbone, Marionette, $, _, Logger) {
Daniel@0 4
Daniel@0 5 // Define private variables
Daniel@0 6 var logger = null;
Daniel@0 7
Daniel@0 8 ContextModule.addInitializer(function(options){
Daniel@0 9
Daniel@0 10 // logger = Logger.get("ContextModule.StateBookmark");
Daniel@0 11 // logger.setLevel(Logger.DEBUG);
Daniel@0 12
Daniel@0 13 /**
Daniel@0 14 * StateHistory stores a history of a serialized state
Daniel@0 15 * and provides an API to switch between these states
Daniel@0 16 *
Daniel@0 17 * A user simply updates the value of currentSerializedState attribute
Daniel@0 18 * and calls undo(), redo() or reset() to move along the history
Daniel@0 19 * There is no direct access to undo and redo stacks, but it is possible
Daniel@0 20 * to know about them by calling canUndo() and canRedo()
Daniel@0 21 *
Daniel@0 22 * serialize / unserialize deal with the object of the following structure
Daniel@0 23 * {
Daniel@0 24 * undoStack: [{recent}, {less recent}, ... {the oldest}]
Daniel@0 25 * redoStack: [{next}, {the following}, ... {the last}]
Daniel@0 26 * currentSerializedState: {}
Daniel@0 27 * }
Daniel@0 28 *
Daniel@0 29 * attribute compoundChangeDetector is a function that helps decide if the changes between
Daniel@0 30 * the given three serialized states can be treated as a single change.
Daniel@0 31 * This is needed to avoid too many undo states on simple actions like text typing
Daniel@0 32 * compoundChangeDetector returns true if the change between the first, the second
Daniel@0 33 * and the third object can be considered as a single compound change
Daniel@0 34 * (i.e. there is no need to create an extra undo action)
Daniel@0 35 */
Daniel@0 36 ContextModule.StateHistory = Backbone.Model.extend({
Daniel@0 37 defaults: {
Daniel@0 38 maxStackSize: 50,
Daniel@0 39 currentSerializedState: undefined,
Daniel@0 40 compoundChangeDetector: undefined
Daniel@0 41 },
Daniel@0 42
Daniel@0 43 _NONE: 0,
Daniel@0 44 _UNDO: 1,
Daniel@0 45 _REDO: 2,
Daniel@0 46 _UNSERIALIZE: 3,
Daniel@0 47
Daniel@0 48 /**
Daniel@0 49 * @memberOf App.ContextModule.StateBookmark
Daniel@0 50 */
Daniel@0 51 initialize: function() {
Daniel@0 52 this._undoStack = [];
Daniel@0 53 this._redoStack = [];
Daniel@0 54 this._currentSerializedStateChangeMode = this._NONE;
Daniel@0 55
Daniel@0 56 this.on("change:maxStackSize", this._trimStacks, this);
Daniel@0 57 this.on("change:currentSerializedState", this._registerNewSerializedState, this);
Daniel@0 58 },
Daniel@0 59
Daniel@0 60 _registerNewSerializedState: function() {
Daniel@0 61 var previousSerialisedState = this.previous("currentSerializedState");
Daniel@0 62 if (this._currentSerializedStateChangeMode == this._UNDO) {
Daniel@0 63 this._redoStack.unshift(previousSerialisedState);
Daniel@0 64 } else if (this._currentSerializedStateChangeMode == this._REDO) {
Daniel@0 65 this._undoStack.unshift(previousSerialisedState);
Daniel@0 66 } else if (this._currentSerializedStateChangeMode == this._UNSERIALIZE) {
Daniel@0 67 } else {
Daniel@0 68 if (!_.isEqual(previousSerialisedState, this.attributes.currentSerializedState)) {
Daniel@0 69 if (this._undoStack.length == 0
Daniel@0 70 || !_.isFunction(this.attributes.compoundChangeDetector)
Daniel@0 71 || !this.attributes.compoundChangeDetector.call(currentSerializedState, previousSerialisedState, this._undoStack[0]))
Daniel@0 72 {
Daniel@0 73 this._undoStack.unshift(previousSerialisedState);
Daniel@0 74 }
Daniel@0 75 this._redoStack = [];
Daniel@0 76 }
Daniel@0 77 }
Daniel@0 78 this._trimStacks();
Daniel@0 79 },
Daniel@0 80
Daniel@0 81 getCurrentSerializedState: function() {
Daniel@0 82
Daniel@0 83 },
Daniel@0 84
Daniel@0 85 undo: function() {
Daniel@0 86 if (this._undoStack.length) {
Daniel@0 87 this._currentSerializedStateChangeMode = this._UNDO;
Daniel@0 88 this.set("currentSerializedState", this._undoStack.shift());
Daniel@0 89 this._currentSerializedStateChangeMode = this._NONE;
Daniel@0 90 } else {
Daniel@0 91 throw "Undo was called when undo stack was empty";
Daniel@0 92 }
Daniel@0 93 },
Daniel@0 94
Daniel@0 95 redo: function() {
Daniel@0 96 if (this._redoStack.length) {
Daniel@0 97 this._currentSerializedStateChangeMode = this._REDO;
Daniel@0 98 this.set("currentSerializedState", this._redoStack.shift());
Daniel@0 99 this._currentSerializedStateChangeMode = this._NONE;
Daniel@0 100 } else {
Daniel@0 101 throw "Redo was called when undo stack was empty";
Daniel@0 102 }
Daniel@0 103 },
Daniel@0 104
Daniel@0 105 reset: function() {
Daniel@0 106 var stacksWereNotEmpty = this._undoStack.length || this._redoStack.length;
Daniel@0 107 this._undoStack = [];
Daniel@0 108 this._redoStack = [];
Daniel@0 109 if (stacksWereNotEmpty) {
Daniel@0 110 this.trigger("change");
Daniel@0 111 }
Daniel@0 112 },
Daniel@0 113
Daniel@0 114 canUndo: function() {
Daniel@0 115 return !!this._undoStack.length;
Daniel@0 116 },
Daniel@0 117
Daniel@0 118 canRedo: function() {
Daniel@0 119 return !!this._redoStack.length;
Daniel@0 120 },
Daniel@0 121
Daniel@0 122 serialize: function() {
Daniel@0 123 // logger.debug("method called: StateHistory::serialize");
Daniel@0 124
Daniel@0 125 var result = {
Daniel@0 126 currentSerializedState: this.attributes.currentSerializedState,
Daniel@0 127 undoStack: _.clone(this._undoStack),
Daniel@0 128 redoStack: _.clone(this._redoStack),
Daniel@0 129 };
Daniel@0 130
Daniel@0 131 return result;
Daniel@0 132 },
Daniel@0 133
Daniel@0 134 unserialize: function(serializedAttributes) {
Daniel@0 135 this._currentSerializedStateChangeMode = this._UNSERIALIZE;
Daniel@0 136
Daniel@0 137 var fixedSerializedAttributes = serializedAttributes;
Daniel@0 138 if (!_.isSimpleObject(serializedAttributes)) {
Daniel@0 139 fixedSerializedAttributes = {};
Daniel@0 140 }
Daniel@0 141
Daniel@0 142 this._undoStack = _.isArray(fixedSerializedAttributes.undoStack) ?
Daniel@0 143 fixedSerializedAttributes.undoStack : [];
Daniel@0 144 this._redoStack = _.isArray(fixedSerializedAttributes.redoStack) ?
Daniel@0 145 fixedSerializedAttributes.redoStack : [];
Daniel@0 146 this._trimStacks();
Daniel@0 147
Daniel@0 148 this.set("currentSerializedState", fixedSerializedAttributes.currentSerializedState);
Daniel@0 149
Daniel@0 150 this._currentSerializedStateChangeMode = this._NONE;
Daniel@0 151 },
Daniel@0 152
Daniel@0 153 _trimStacks: function() {
Daniel@0 154 if (this._undoStack.length > this.attributes.maxStackSize) {
Daniel@0 155 this._undoStack = this._undoStack.slice(0, this.attributes.maxStackSize);
Daniel@0 156 }
Daniel@0 157 if (this._redoStack.length > this.attributes.maxStackSize) {
Daniel@0 158 this._redoStack = this._redoStack.slice(0, this.attributes.maxStackSize);
Daniel@0 159 }
Daniel@0 160 }
Daniel@0 161 });
Daniel@0 162
Daniel@0 163 });
Daniel@0 164 }, Logger);