diff options
Diffstat (limited to 'js/undo.js')
| -rw-r--r-- | js/undo.js | 173 |
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 +} + +})() |
