_EtagSupport__etag
ts52852067.45
__name__
history.js
content_type
application/javascript
data

/**\n * Package: svedit.history\n *\n * Licensed under the Apache License, Version 2\n *\n * Copyright(c) 2010 Jeff Schiller\n */\n \n // Dependencies:\n // 1) jQuery\n // 2) svgtransformlist.js\n // 3) svgutils.js\n \n var svgedit = svgedit || {};\n \n (function() {\n \n if (!svgedit.history) {\n svgedit.history = {};\n }\n \n // Group: Undo/Redo history management\n \n \n svgedit.history.HistoryEventTypes = {\n BEFORE_APPLY: \'before_apply\',\n AFTER_APPLY: \'after_apply\',\n BEFORE_UNAPPLY: \'before_unapply\',\n AFTER_UNAPPLY: \'after_unapply\'\n };\n \n var removedElements = {};\n \n /**\n * Interface: svgedit.history.HistoryCommand\n * An interface that all command objects must implement.\n *\n * interface svgedit.history.HistoryCommand {\n * void apply(svgedit.history.HistoryEventHandler);\n * void unapply(svgedit.history.HistoryEventHandler);\n * Element[] elements();\n * String getText();\n *\n * static String type();\n * }\n *\n * Interface: svgedit.history.HistoryEventHandler\n * An interface for objects that will handle history events.\n *\n * interface svgedit.history.HistoryEventHandler {\n * void handleHistoryEvent(eventType, command);\n * }\n *\n * eventType is a string conforming to one of the HistoryEvent types.\n * command is an object fulfilling the HistoryCommand interface.\n */\n \n // Class: svgedit.history.MoveElementCommand\n // implements svgedit.history.HistoryCommand\n // History command for an element that had its DOM position changed\n //\n // Parameters:\n // elem - The DOM element that was moved\n // oldNextSibling - The element\'s next sibling before it was moved\n // oldParent - The element\'s parent before it was moved\n // text - An optional string visible to user related to this change\n svgedit.history.MoveElementCommand = function(elem, oldNextSibling, oldParent, text) {\n this.elem = elem;\n this.text = text ? ("Move " + elem.tagName + " to " + text) : ("Move " + elem.tagName);\n this.oldNextSibling = oldNextSibling;\n this.oldParent = oldParent;\n this.newNextSibling = elem.nextSibling;\n this.newParent = elem.parentNode;\n };\n svgedit.history.MoveElementCommand.type = function() { return \'svgedit.history.MoveElementCommand\'; }\n svgedit.history.MoveElementCommand.prototype.type = svgedit.history.MoveElementCommand.type;\n \n // Function: svgedit.history.MoveElementCommand.getText\n svgedit.history.MoveElementCommand.prototype.getText = function() {\n return this.text;\n };\n \n // Function: svgedit.history.MoveElementCommand.apply\n // Re-positions the element\n svgedit.history.MoveElementCommand.prototype.apply = function(handler) {\n // TODO(codedread): Refactor this common event code into a base HistoryCommand class.\n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);\n }\n \n this.elem = this.newParent.insertBefore(this.elem, this.newNextSibling);\n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);\n }\n };\n \n // Function: svgedit.history.MoveElementCommand.unapply\n // Positions the element back to its original location\n svgedit.history.MoveElementCommand.prototype.unapply = function(handler) {\n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);\n }\n \n this.elem = this.oldParent.insertBefore(this.elem, this.oldNextSibling);\n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);\n }\n };\n \n // Function: svgedit.history.MoveElementCommand.elements\n // Returns array with element associated with this command\n svgedit.history.MoveElementCommand.prototype.elements = function() {\n return [this.elem];\n };\n \n \n // Class: svgedit.history.InsertElementCommand\n // implements svgedit.history.HistoryCommand\n // History command for an element that was added to the DOM\n //\n // Parameters:\n // elem - The newly added DOM element\n // text - An optional string visible to user related to this change\n svgedit.history.InsertElementCommand = function(elem, text) {\n this.elem = elem;\n this.text = text || ("Create " + elem.tagName);\n this.parent = elem.parentNode;\n this.nextSibling = this.elem.nextSibling;\n };\n svgedit.history.InsertElementCommand.type = function() { return \'svgedit.history.InsertElementCommand\'; }\n svgedit.history.InsertElementCommand.prototype.type = svgedit.history.InsertElementCommand.type;\n \n // Function: svgedit.history.InsertElementCommand.getText\n svgedit.history.InsertElementCommand.prototype.getText = function() {\n return this.text;\n };\n \n // Function: svgedit.history.InsertElementCommand.apply\n // Re-Inserts the new element\n svgedit.history.InsertElementCommand.prototype.apply = function(handler) { \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);\n }\n \n this.elem = this.parent.insertBefore(this.elem, this.nextSibling); \n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);\n }\n };\n \n // Function: svgedit.history.InsertElementCommand.unapply\n // Removes the element\n svgedit.history.InsertElementCommand.prototype.unapply = function(handler) {\n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);\n }\n \n this.parent = this.elem.parentNode;\n this.elem = this.elem.parentNode.removeChild(this.elem);\n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);\n }\n };\n \n // Function: svgedit.history.InsertElementCommand.elements\n // Returns array with element associated with this command\n svgedit.history.InsertElementCommand.prototype.elements = function() {\n return [this.elem];\n };\n \n \n // Class: svgedit.history.RemoveElementCommand\n // implements svgedit.history.HistoryCommand\n // History command for an element removed from the DOM\n //\n // Parameters:\n // elem - The removed DOM element\n // oldNextSibling - the DOM element\'s nextSibling when it was in the DOM\n // oldParent - The DOM element\'s parent\n // text - An optional string visible to user related to this change\n svgedit.history.RemoveElementCommand = function(elem, oldNextSibling, oldParent, text) {\n this.elem = elem;\n this.text = text || ("Delete " + elem.tagName);\n this.nextSibling = oldNextSibling;\n this.parent = oldParent;\n \n // special hack for webkit: remove this element\'s entry in the svgTransformLists map\n svgedit.transformlist.removeElementFromListMap(elem);\n };\n svgedit.history.RemoveElementCommand.type = function() { return \'svgedit.history.RemoveElementCommand\'; }\n svgedit.history.RemoveElementCommand.prototype.type = svgedit.history.RemoveElementCommand.type;\n \n // Function: svgedit.history.RemoveElementCommand.getText\n svgedit.history.RemoveElementCommand.prototype.getText = function() {\n return this.text;\n };\n \n // Function: RemoveElementCommand.apply\n // Re-removes the new element\n svgedit.history.RemoveElementCommand.prototype.apply = function(handler) { \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);\n }\n \n svgedit.transformlist.removeElementFromListMap(this.elem);\n this.parent = this.elem.parentNode;\n this.elem = this.parent.removeChild(this.elem);\n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);\n }\n };\n \n // Function: RemoveElementCommand.unapply\n // Re-adds the new element\n svgedit.history.RemoveElementCommand.prototype.unapply = function(handler) { \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);\n }\n \n svgedit.transformlist.removeElementFromListMap(this.elem);\n if(this.nextSibling == null) {\n if(window.console) console.log(\'Error: reference element was lost\');\n }\n this.parent.insertBefore(this.elem, this.nextSibling);\n \n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);\n }\n };\n \n // Function: RemoveElementCommand.elements\n // Returns array with element associated with this command\n svgedit.history.RemoveElementCommand.prototype.elements = function() {\n return [this.elem];\n };\n \n \n // Class: svgedit.history.ChangeElementCommand\n // implements svgedit.history.HistoryCommand\n // History command to make a change to an element. \n // Usually an attribute change, but can also be textcontent.\n //\n // Parameters:\n // elem - The DOM element that was changed\n // attrs - An object with the attributes to be changed and the values they had *before* the change\n // text - An optional string visible to user related to this change\n svgedit.history.ChangeElementCommand = function(elem, attrs, text) {\n this.elem = elem;\n this.text = text ? ("Change " + elem.tagName + " " + text) : ("Change " + elem.tagName);\n this.newValues = {};\n this.oldValues = attrs;\n for (var attr in attrs) {\n if (attr == "#text") this.newValues[attr] = elem.textContent;\n else if (attr == "#href") this.newValues[attr] = svgedit.utilities.getHref(elem);\n else this.newValues[attr] = elem.getAttribute(attr);\n }\n };\n svgedit.history.ChangeElementCommand.type = function() { return \'svgedit.history.ChangeElementCommand\'; }\n svgedit.history.ChangeElementCommand.prototype.type = svgedit.history.ChangeElementCommand.type;\n \n // Function: svgedit.history.ChangeElementCommand.getText\n svgedit.history.ChangeElementCommand.prototype.getText = function() {\n return this.text;\n };\n \n // Function: svgedit.history.ChangeElementCommand.apply\n // Performs the stored change action\n svgedit.history.ChangeElementCommand.prototype.apply = function(handler) {\n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);\n }\n \n var bChangedTransform = false;\n for(var attr in this.newValues ) {\n if (this.newValues[attr]) {\n if (attr == "#text") this.elem.textContent = this.newValues[attr];\n else if (attr == "#href") svgedit.utilities.setHref(this.elem, this.newValues[attr])\n else this.elem.setAttribute(attr, this.newValues[attr]);\n }\n else {\n if (attr == "#text") {\n this.elem.textContent = "";\n }\n else {\n this.elem.setAttribute(attr, "");\n this.elem.removeAttribute(attr);\n }\n }\n \n if (attr == "transform") { bChangedTransform = true; }\n }\n \n // relocate rotational transform, if necessary\n if(!bChangedTransform) {\n var angle = svgedit.utilities.getRotationAngle(this.elem);\n if (angle) {\n var bbox = elem.getBBox();\n var cx = bbox.x + bbox.width/2,\n cy = bbox.y + bbox.height/2;\n var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join(\'\');\n if (rotate != elem.getAttribute("transform")) {\n elem.setAttribute("transform", rotate);\n }\n }\n }\n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);\n }\n \n return true;\n };\n \n // Function: svgedit.history.ChangeElementCommand.unapply\n // Reverses the stored change action\n svgedit.history.ChangeElementCommand.prototype.unapply = function(handler) {\n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);\n }\n \n var bChangedTransform = false;\n for(var attr in this.oldValues ) {\n if (this.oldValues[attr]) {\n if (attr == "#text") this.elem.textContent = this.oldValues[attr];\n else if (attr == "#href") svgedit.utilities.setHref(this.elem, this.oldValues[attr]);\n else this.elem.setAttribute(attr, this.oldValues[attr]);\n }\n else {\n if (attr == "#text") {\n this.elem.textContent = "";\n }\n else this.elem.removeAttribute(attr);\n }\n if (attr == "transform") { bChangedTransform = true; }\n }\n // relocate rotational transform, if necessary\n if(!bChangedTransform) {\n var angle = svgedit.utilities.getRotationAngle(this.elem);\n if (angle) {\n var bbox = this.elem.getBBox();\n var cx = bbox.x + bbox.width/2,\n cy = bbox.y + bbox.height/2;\n var rotate = ["rotate(", angle, " ", cx, ",", cy, ")"].join(\'\');\n if (rotate != this.elem.getAttribute("transform")) {\n this.elem.setAttribute("transform", rotate);\n }\n }\n }\n \n // Remove transformlist to prevent confusion that causes bugs like 575.\n svgedit.transformlist.removeElementFromListMap(this.elem);\n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);\n }\n \n return true;\n };\n \n // Function: ChangeElementCommand.elements\n // Returns array with element associated with this command\n svgedit.history.ChangeElementCommand.prototype.elements = function() {\n return [this.elem];\n };\n \n \n // TODO: create a \'typing\' command object that tracks changes in text\n // if a new Typing command is created and the top command on the stack is also a Typing\n // and they both affect the same element, then collapse the two commands into one\n \n \n // Class: svgedit.history.BatchCommand\n // implements svgedit.history.HistoryCommand\n // History command that can contain/execute multiple other commands\n //\n // Parameters:\n // text - An optional string visible to user related to this change\n svgedit.history.BatchCommand = function(text) {\n this.text = text || "Batch Command";\n this.stack = [];\n };\n svgedit.history.BatchCommand.type = function() { return \'svgedit.history.BatchCommand\'; }\n svgedit.history.BatchCommand.prototype.type = svgedit.history.BatchCommand.type;\n \n // Function: svgedit.history.BatchCommand.getText\n svgedit.history.BatchCommand.prototype.getText = function() {\n return this.text;\n };\n \n // Function: svgedit.history.BatchCommand.apply\n // Runs "apply" on all subcommands\n svgedit.history.BatchCommand.prototype.apply = function(handler) {\n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_APPLY, this);\n }\n \n var len = this.stack.length;\n for (var i = 0; i < len; ++i) {\n this.stack[i].apply(handler);\n }\n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_APPLY, this);\n }\n };\n \n // Function: svgedit.history.BatchCommand.unapply\n // Runs "unapply" on all subcommands\n svgedit.history.BatchCommand.prototype.unapply = function(handler) {\n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.BEFORE_UNAPPLY, this);\n }\n \n for (var i = this.stack.length-1; i >= 0; i--) {\n this.stack[i].unapply(handler);\n }\n \n if (handler) {\n handler.handleHistoryEvent(svgedit.history.HistoryEventTypes.AFTER_UNAPPLY, this);\n }\n };\n \n // Function: svgedit.history.BatchCommand.elements\n // Iterate through all our subcommands and returns all the elements we are changing\n svgedit.history.BatchCommand.prototype.elements = function() {\n var elems = [];\n var cmd = this.stack.length;\n while (cmd--) {\n var thisElems = this.stack[cmd].elements();\n var elem = thisElems.length;\n while (elem--) {\n if (elems.indexOf(thisElems[elem]) == -1) elems.push(thisElems[elem]);\n }\n }\n return elems;\n };\n \n // Function: svgedit.history.BatchCommand.addSubCommand\n // Adds a given command to the history stack\n //\n // Parameters:\n // cmd - The undo command object to add\n svgedit.history.BatchCommand.prototype.addSubCommand = function(cmd) {\n this.stack.push(cmd);\n };\n \n // Function: svgedit.history.BatchCommand.isEmpty\n // Returns a boolean indicating whether or not the batch command is empty\n svgedit.history.BatchCommand.prototype.isEmpty = function() {\n return this.stack.length == 0;\n };\n \n \n // Class: svgedit.history.UndoManager\n // Parameters:\n // historyEventHandler - an object that conforms to the HistoryEventHandler interface\n // (see above)\n svgedit.history.UndoManager = function(historyEventHandler) {\n this.handler_ = historyEventHandler || null;\n this.undoStackPointer = 0;\n this.undoStack = [];\n \n // this is the stack that stores the original values, the elements and\n // the attribute name for begin/finish\n this.undoChangeStackPointer = -1;\n this.undoableChangeStack = [];\n };\n \n // Function: svgedit.history.UndoManager.resetUndoStack\n // Resets the undo stack, effectively clearing the undo/redo history\n svgedit.history.UndoManager.prototype.resetUndoStack = function() {\n this.undoStack = [];\n this.undoStackPointer = 0;\n };\n \n // Function: svgedit.history.UndoManager.getUndoStackSize\n // Returns: \n // Integer with the current size of the undo history stack\n svgedit.history.UndoManager.prototype.getUndoStackSize = function() {\n return this.undoStackPointer;\n };\n \n // Function: svgedit.history.UndoManager.getRedoStackSize\n // Returns: \n // Integer with the current size of the redo history stack\n svgedit.history.UndoManager.prototype.getRedoStackSize = function() {\n return this.undoStack.length - this.undoStackPointer;\n };\n \n // Function: svgedit.history.UndoManager.getNextUndoCommandText\n // Returns: \n // String associated with the next undo command\n svgedit.history.UndoManager.prototype.getNextUndoCommandText = function() { \n return this.undoStackPointer > 0 ? this.undoStack[this.undoStackPointer-1].getText() : "";\n };\n \n // Function: svgedit.history.UndoManager.getNextRedoCommandText\n // Returns: \n // String associated with the next redo command\n svgedit.history.UndoManager.prototype.getNextRedoCommandText = function() { \n return this.undoStackPointer < this.undoStack.length ? this.undoStack[this.undoStackPointer].getText() : "";\n };\n \n // Function: svgedit.history.UndoManager.undo\n // Performs an undo step\n svgedit.history.UndoManager.prototype.undo = function() {\n if (this.undoStackPointer > 0) {\n var cmd = this.undoStack[--this.undoStackPointer];\n cmd.unapply(this.handler_);\n }\n };\n \n // Function: svgedit.history.UndoManager.redo \n // Performs a redo step\n svgedit.history.UndoManager.prototype.redo = function() {\n if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) {\n var cmd = this.undoStack[this.undoStackPointer++];\n cmd.apply(this.handler_);\n }\n };\n \n // Function: svgedit.history.UndoManager.addCommandToHistory\n // Adds a command object to the undo history stack\n //\n // Parameters: \n // cmd - The command object to add\n svgedit.history.UndoManager.prototype.addCommandToHistory = function(cmd) {\n // FIXME: we MUST compress consecutive text changes to the same element\n // (right now each keystroke is saved as a separate command that includes the\n // entire text contents of the text element)\n // TODO: consider limiting the history that we store here (need to do some slicing)\n \n // if our stack pointer is not at the end, then we have to remove\n // all commands after the pointer and insert the new command\n if (this.undoStackPointer < this.undoStack.length && this.undoStack.length > 0) {\n this.undoStack = this.undoStack.splice(0, this.undoStackPointer);\n }\n this.undoStack.push(cmd);\n this.undoStackPointer = this.undoStack.length;\n };\n \n \n // Function: svgedit.history.UndoManager.beginUndoableChange\n // This function tells the canvas to remember the old values of the \n // attrName attribute for each element sent in. The elements and values \n // are stored on a stack, so the next call to finishUndoableChange() will \n // pop the elements and old values off the stack, gets the current values\n // from the DOM and uses all of these to construct the undo-able command.\n //\n // Parameters: \n // attrName - The name of the attribute being changed\n // elems - Array of DOM elements being changed\n svgedit.history.UndoManager.prototype.beginUndoableChange = function(attrName, elems) {\n var p = ++this.undoChangeStackPointer;\n var i = elems.length;\n var oldValues = new Array(i), elements = new Array(i);\n while (i--) {\n var elem = elems[i];\n if (elem == null) continue;\n elements[i] = elem;\n oldValues[i] = elem.getAttribute(attrName);\n }\n this.undoableChangeStack[p] = {\'attrName\': attrName,\n \'oldValues\': oldValues,\n \'elements\': elements};\n };\n \n // Function: svgedit.history.UndoManager.finishUndoableChange\n // This function returns a BatchCommand object which summarizes the\n // change since beginUndoableChange was called. The command can then\n // be added to the command history\n //\n // Returns: \n // Batch command object with resulting changes\n svgedit.history.UndoManager.prototype.finishUndoableChange = function() {\n var p = this.undoChangeStackPointer--;\n var changeset = this.undoableChangeStack[p];\n var i = changeset[\'elements\'].length;\n var attrName = changeset[\'attrName\'];\n var batchCmd = new svgedit.history.BatchCommand("Change " + attrName);\n while (i--) {\n var elem = changeset[\'elements\'][i];\n if (elem == null) continue;\n var changes = {};\n changes[attrName] = changeset[\'oldValues\'][i];\n if (changes[attrName] != elem.getAttribute(attrName)) {\n batchCmd.addSubCommand(new svgedit.history.ChangeElementCommand(elem, changes, attrName));\n }\n }\n this.undoableChangeStack[p] = null;\n return batchCmd;\n };\n \n \n })();

precondition
size
20645
title