asciiblaster- draw irc art in your web browser |
git clone git://git.acid.vegas/asciiblaster.git |
Log | Files | Refs | Archive | README |
undo.js (5528B)
1 var undo = (function(){ 2 3 var max_states = 200; 4 5 // undotimetotal = 0; 6 7 var stack = {undo: [], redo: []}; 8 var current_undo = null; 9 var dom = {undo: undo_el, redo: redo_el}; 10 dom.undo.is_visible = dom.redo.is_visible = false 11 12 var LexState = function(lex){ 13 this.fg = lex.fg; 14 this.bg = lex.bg; 15 this.char = lex.char; 16 this.opacity = lex.opacity; 17 }; 18 19 var update_dom_visibility = function(type){ 20 var el = dom[type] 21 if (el.is_visible){ 22 if (stack[type].length === 0) { 23 el.classList.add('hidden') 24 el.is_visible = false 25 } 26 } else if (stack[type].length > 0){ 27 el.classList.remove('hidden') 28 el.is_visible = true 29 } 30 } 31 var update_dom = function(){ 32 update_dom_visibility('undo') 33 update_dom_visibility('redo') 34 } 35 36 // state is an undo or redo state that might contain these props 37 // { lexs: {'0,0': LexState, ...}, // for sparse lex changes (eg brush, fill) 38 // focus: {x:, y: }, 39 // size: {w:, h: }, 40 // rects: [{x:, y:, w:, h:, lexs: [LexState, ...]}, ...] 41 // } 42 var new_state = function(){ 43 var state = {lexs:{}}; 44 save_focus(canvas.focus_x, canvas.focus_y, state) 45 return state 46 } 47 var new_redo = function(){ 48 return new_state() 49 } 50 var new_undo = function(){ 51 current_undo = new_state() 52 stack.redo = [] 53 stack.undo.push(current_undo) 54 if (stack.undo.length > max_states) stack.undo.shift(); 55 update_dom() 56 return current_undo 57 } 58 59 var save_focus = function(x, y, state){ 60 state = state || current_undo 61 state.focus = {x:x, y:y} 62 } 63 var save_size = function(w, h, state){ 64 state = state || current_undo 65 state.size = {w:w, h:h}; 66 } 67 // the reason for stringifying the x y coords is so that each 68 // coordinate is saved only once in an undo state. 69 // otherwise there would be problems with, eg, a brush stroke 70 // that passed over the same grid cell twice. 71 var save_lex = function(x, y, lex, state){ 72 // var start = Date.now() 73 state = state || current_undo 74 var lexs = state.lexs; 75 var xy = x + "," + y; 76 if (xy in lexs) return; 77 lexs[xy] = new LexState(lex) 78 // undotimetotal += Date.now() - start 79 } 80 var save_focused_lex = function(state){ 81 state = state || current_undo 82 var x = canvas.focus_x 83 var y = canvas.focus_y 84 save_lex(x, y, canvas.aa[y][x], state) 85 } 86 var save_rect = function(xpos, ypos, w, h, state){ 87 if (w === 0 || h === 0) return; 88 state = state || current_undo; 89 state.rects = state.rects || [] 90 var aa = canvas.aa; 91 var rect = {x: xpos, y: ypos, w: w, h: h, lexs: []} 92 var lexs = rect.lexs 93 var xlen = xpos + w 94 var ylen = ypos + h 95 for (var y = ypos; y < ylen; y++){ 96 var aay = aa[y] 97 for (var x = xpos; x < xlen; x++){ 98 lexs.push(new LexState(aay[x])) 99 } 100 } 101 state.rects.push(rect) 102 } 103 var save_resize = function(w, h, old_w, old_h, state){ 104 state = state || current_undo 105 save_size(old_w, old_h, state) 106 if (old_w > w){ 107 // .---XX 108 // | XX 109 // |___XX 110 save_rect(w, 0, old_w - w, old_h, state) 111 if (old_h > h){ 112 // .----. 113 // | | 114 // XXXX_| 115 save_rect(0, h, w, old_h - h, state) 116 } 117 } else if (old_h > h){ 118 // .----. 119 // | | 120 // XXXXXX 121 save_rect(0, h, old_w, old_h - h, state) 122 } 123 } 124 125 var restore_state = function(state){ 126 // all redo states will have a cached undo state on them 127 // an undo state might have a cached redo state 128 // if it doesn't have one, generate one 129 var make_redo = ! ('redo' in state || 'undo' in state); 130 var aa = canvas.aa 131 var lex, lexs; 132 133 if (make_redo){ 134 state.redo = new_redo() 135 136 // copy saved rects that intersect with current canvas size 137 // important to do this before resizing canvas 138 if ('rects' in state){ 139 for (var ri=0, rect; rect=state.rects[ri]; ri++){ 140 if (rect.x >= canvas.w || 141 rect.y >= canvas.h) continue; 142 var w = Math.min(rect.w, canvas.w - rect.x) 143 var h = Math.min(rect.h, canvas.h - rect.y) 144 save_rect(rect.x, rect.y, w, h, state.redo) 145 } 146 } 147 if ('size' in state){ 148 save_resize(state.size.w, state.size.h, canvas.w, canvas.h, state.redo) 149 } 150 } 151 152 if ('size' in state){ 153 canvas.resize(state.size.w, state.size.h, true); 154 } 155 156 if ('rects' in state){ 157 for (var ri=0, rect; rect=state.rects[ri]; ri++){ 158 lexs = rect.lexs 159 for (var li=0; lex=lexs[li]; li++){ 160 var x = (li % rect.w) + rect.x 161 var y = ((li / rect.w)|0) + rect.y 162 aa[y][x].assign(lex) 163 } 164 } 165 } 166 167 lexs = state.lexs 168 for (var key in lexs){ 169 var xy = key.split(','); 170 lex = aa[xy[1]][xy[0]] 171 if (make_redo) 172 save_lex(xy[0], xy[1], lex, state.redo) 173 lex.assign(lexs[key]) 174 } 175 176 if ('focus' in state){ 177 canvas.focus_x = state.focus.x 178 canvas.focus_y = state.focus.y 179 if (current_canvas === canvas){ 180 canvas.focus() 181 } 182 } 183 } 184 185 var undo = function(){ 186 var state = stack.undo.pop(); 187 if (!state) return; 188 189 restore_state(state) 190 191 // now take the applied undo state and store it on the redo state 192 // and push the redo state to the redo stack 193 state.redo.undo = state 194 stack.redo.push(state.redo) 195 delete state.redo 196 197 update_dom() 198 } 199 200 var redo = function(){ 201 var state = stack.redo.pop(); 202 if (!state) return; 203 204 restore_state(state) 205 206 state.undo.redo = state 207 stack.undo.push(state.undo) 208 delete state.undo 209 210 update_dom() 211 } 212 213 return { 214 stack: stack, 215 new: new_undo, 216 // new_redo: new_redo, 217 save_focus: save_focus, 218 save_size: save_size, 219 save_lex: save_lex, 220 save_focused_lex: save_focused_lex, 221 save_rect: save_rect, 222 save_resize: save_resize, 223 undo: undo, 224 redo: redo 225 } 226 227 })()