summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authortimb <opuscule@gmail.com>2015-05-25 10:33:38 -0400
committertimb <opuscule@gmail.com>2015-05-25 10:33:38 -0400
commitec1f78308cddedff6d0b8c6488036d9e83b81aa2 (patch)
tree4c031776d8bc02c96cbf33d5eb6fc9674f4943cf /js
parent4da580e1a1a9a86c826fd403da8031cea2bc3a96 (diff)
undo (not hooked up yet)
Diffstat (limited to 'js')
-rw-r--r--js/undo.js173
1 files changed, 173 insertions, 0 deletions
diff --git a/js/undo.js b/js/undo.js
new file mode 100644
index 0000000..3bdc0a9
--- /dev/null
+++ b/js/undo.js
@@ -0,0 +1,173 @@
+var undo = (function(){
+
+var max_states = 200;
+
+// undotimetotal = 0;
+
+var stack = {undo: [], redo: []};
+var current_undo = null;
+var dom = {undo: undo_el, redo: redo_el};
+dom.undo.is_visible = dom.redo.is_visible = false
+
+var LexState = function(lex){
+ this.fg = lex.fg;
+ this.bg = lex.bg;
+ this.char = lex.char;
+ this.opacity = lex.opacity;
+};
+
+var update_dom_visibility = function(type){
+ var el = dom[type]
+ if (el.is_visible){
+ if (stack[type].length === 0) {
+ el.classList.add('hidden')
+ el.is_visible = false
+ }
+ } else if (stack[type].length > 0){
+ el.classList.remove('hidden')
+ el.is_visible = true
+ }
+}
+var update_dom = function(){
+ update_dom_visibility('undo')
+ update_dom_visibility('redo')
+}
+
+var new_state = function(){
+ var state = {lexs:{}};
+ save_focus(canvas.focus_x, canvas.focus_y, state)
+ return state
+}
+var new_redo = function(){
+ return new_state()
+}
+var new_undo = function(){
+ current_undo = new_state()
+ stack.redo = []
+ stack.undo.push(current_undo)
+ if (stack.undo.length > max_states) stack.undo.shift();
+ update_dom()
+ return current_undo
+}
+
+var save_focus = function(x, y, state){
+ state = state || current_undo
+ state.focus = {x:x, y:y}
+}
+var save_size = function(w, h, state){
+ state = state || current_undo
+ state.size = {w:w, h:h};
+}
+// the reason for stringifying the x y coords is so that each
+// coordinate is saved only once in an undo state.
+// otherwise there would be problems with, eg, a brush stroke
+// that passed over the same grid cell twice.
+var save_lex = function(x, y, lex, state){
+ // var start = Date.now()
+ state = state || current_undo
+ var lexs = state.lexs;
+ var xy = x + "," + y;
+ if (xy in lexs) return;
+ lexs[xy] = new LexState(lex)
+ // undotimetotal += Date.now() - start
+}
+var save_focused_lex = function(state){
+ state = state || current_undo
+ var x = canvas.focus_x
+ var y = canvas.focus_y
+ save_lex(x, y, canvas.aa[y][x], state)
+}
+var save_rect = function(xpos, ypos, w, h, state){
+ state = state || current_undo;
+ var aa = canvas.aa;
+
+ var xlen = xpos + w
+ var ylen = ypos + h
+
+ for (var x = xpos; x < xlen; x++){
+ for (var y = ypos; y < ylen; y++){
+ save_lex(x, y, aa[y][x], state)
+ }
+ }
+}
+
+var restore_state = function(state){
+ // all redo states will have a cached undo state on them
+ // an undo state might have a cached redo state
+ // if it doesn't have one, generate one
+ var make_redo = ! ('redo' in state || 'undo' in state);
+ var aa = canvas.aa
+ var lexs = state.lexs
+
+ if (make_redo){
+ state.redo = new_redo()
+ }
+
+ if ('size' in state){
+ if (make_redo){
+ save_size(canvas.w, canvas.h, state.redo)
+ // note that resizing canvas redo saves the whole canvas instead of
+ // just the changed part which is inefficient...
+ save_rect(0,0, canvas.w, canvas.h, state.redo)
+ make_redo = false // already saved whole canvas, skip lexs below
+ }
+ canvas.resize(state.size.w, state.size.h);
+ }
+
+ for (var key in lexs){
+ var xy = key.split(',');
+ var lex = aa[xy[1]][xy[0]]
+ if (make_redo)
+ save_lex(xy[0], xy[1], lex, state.redo)
+ lex.assign(lexs[key])
+ }
+
+ if ('focus' in state){
+ canvas.focus_x = state.focus.x
+ canvas.focus_y = state.focus.y
+ if (focused_input === canvas) canvas.focus()
+ }
+}
+
+var undo = function(){
+ var state = stack.undo.pop();
+ if (!state) return;
+
+ restore_state(state)
+
+ // now take the applied undo state and store it on the redo state
+ // and push the redo state to the redo stack
+ state.redo.undo = state
+ stack.redo.push(state.redo)
+ delete state.redo
+
+ update_dom()
+}
+
+var redo = function(){
+ var state = stack.redo.pop();
+ if (!state) return;
+
+ restore_state(state)
+
+ state.undo.redo = state
+ stack.undo.push(state.undo)
+ delete state.undo
+
+ update_dom()
+}
+
+return {
+ stack: stack,
+ new: new_undo,
+// new_redo: new_redo,
+ save_focus: save_focus,
+ save_size: save_size,
+ save_lex: save_lex,
+ save_focused_lex: save_focused_lex,
+ save_rect: save_rect,
+ undo: undo,
+ redo: redo
+}
+
+})()