Skip to content

Instantly share code, notes, and snippets.

@NoxArt
Created May 14, 2012 06:21
Show Gist options
  • Save NoxArt/2692147 to your computer and use it in GitHub Desktop.
Save NoxArt/2692147 to your computer and use it in GitHub Desktop.
JavaScript Undo/Redo
###
Class managing undo/redo
###
class History
###
Properties
@actions [{name(string), identifier(string), undo(callback), redo(callback)}]
@current pointer to a current point in history list, all the lower is history and all the higher ones are future
###
###
@param config { limit, massUndoDelay, massRedoDelay }
@param undoCallback function|null additional and optional undo callback
@param redoCallback function|null additional and optional undo callback
###
constructor: (@config, @undoCallback = null, @redoCallback = null)->
# LIFO structure - last action is the first to be undoed
@actions = []
@current = -1
###
Stores the action that happened and it's undo callback
Caller must ensure undo and redo callbacks turn the model into a proper state
@param string name
@param function actionDo callback describing what's happened, serves as a 'redo' callback
@param function actionUndo
@optionalParam string identifier identifier, for example name of the mark that has been just added
###
happened: (name, actionDo, actionUndo, id = '')->
@clearFuture()
@actions.push {
name: name,
identifier: id,
undo: actionUndo,
redo: actionDo
}
@current++
if @limit isnt Infinity
while @actions.length > @limit
@actions.shift()
@current--
undefined
###
Triggers a given action and stores it as redo
Caller must ensure undo and redo callbacks turn the model into a proper state
@param string name
@param function actionDo callback describing should now happen, serves also as a 'redo' callback
@param function actionUndo
@optionalParam string id identifier, for example name of the mark that has been just added
###
happen: (name, actionDo, actionUndo, id = '')->
@happened name, actionDo, actionUndo, id
actionDo()
###
Undos X steps
@optionalParam int steps
###
undo: (steps = 0)->
if steps is 0
return @undoOne()
# iterator
step = 0
undoCallback = =>
@undoOne()
@undoCallback() if @undoCallback
if ++step <= steps
setTimeout undoCallback, @config.massUndoDelay
undoCallback()
# Undos one step
undoOne: ->
if @actions[ @current ]
@actions[ @current ].undo()
@current--
###
Redos X steps
@optionalParam int steps
###
redo: (steps = 0)->
if steps is 0
return @redoOne()
# iterator
step = 0
redoCallback = =>
@redoOne()
@redoCallback() if @redoCallback
if ++step <= steps
setTimeout redoCallback, @config.massRedoDelay
redoCallback()
# Redos one step
redoOne: ->
if @actions[ @current + 1 ]
@current++
@actions[ @current ].redo()
###
Returns all entries that can be undo-ed
@return [historyEntry]
###
getHistory: -> return if @current is -1 then [] else @actions.slice 0, @current + 1
###
Returns all entries that can be redo-ed
@return [historyEntry]
###
getFuture: -> return if @current + 1 >= @actions.length then [] else @actions.slice @current + 1, @actions.length
###
Has future entries = can redo?
@returns bool
###
hasFuture: -> return @current + 1 < @actions.length
###
Has history entries = can undo?
@returns bool
###
hasHistory: -> return @current > -1
# Removes all entries that could have been redo-ed
clearFuture: ->
if @current + 1 < @actions.length
@actions.splice @current + 1, @actions.length - @current - 1
# Expose the class
return History
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment