1 /** 2 * <pre> 3 * Creates a new MindMapModel. 4 * 5 * This object represents the underlying mind map model and provides access 6 * to the document, the mind map and the currently selected node. 7 * 8 * All changes to the mind map pass through this object, either through calling 9 * methods directly or using the executeAction() method to perform NodeActions. 10 * </pre> 11 * 12 * @constructor 13 * @param {mindmaps.EventBus} eventBus 14 * @param {mindmaps.CommandRegistry} commandRegistry 15 */ 16 mindmaps.MindMapModel = function(eventBus, commandRegistry) { 17 var self = this; 18 this.document = null; 19 this.selectedNode = null; 20 21 /** 22 * Gets the current document. 23 * 24 * @returns {mindmaps.Document} the current document. 25 */ 26 this.getDocument = function() { 27 return this.document; 28 }; 29 30 /** 31 * Sets the current document and will publish a DOCUMENT_OPENED or 32 * DOCUMENT_CLOSED event. 33 * 34 * @param {mindmaps.Document} doc or pass null to close the document 35 */ 36 this.setDocument = function(doc) { 37 this.document = doc; 38 if (doc) { 39 eventBus.publish(mindmaps.Event.DOCUMENT_OPENED, doc); 40 } else { 41 eventBus.publish(mindmaps.Event.DOCUMENT_CLOSED); 42 } 43 }; 44 45 /** 46 * Gets the current mind map associated with the document. 47 * 48 * @returns {mindmaps.MindMap} the mind map or null 49 */ 50 this.getMindMap = function() { 51 if (this.document) { 52 return this.document.mindmap; 53 } 54 return null; 55 }; 56 57 /** 58 * Initialise. 59 * 60 * @private 61 */ 62 this.init = function() { 63 var createCommand = commandRegistry.get(mindmaps.CreateNodeCommand); 64 createCommand.setHandler(this.createNode.bind(this)); 65 66 var deleteCommand = commandRegistry.get(mindmaps.DeleteNodeCommand); 67 deleteCommand.setHandler(this.deleteNode.bind(this)); 68 69 eventBus.subscribe(mindmaps.Event.DOCUMENT_CLOSED, function() { 70 createCommand.setEnabled(false); 71 deleteCommand.setEnabled(false); 72 }); 73 74 eventBus.subscribe(mindmaps.Event.DOCUMENT_OPENED, function() { 75 createCommand.setEnabled(true); 76 deleteCommand.setEnabled(true); 77 }); 78 }; 79 80 /** 81 * Deletes a node or the currently selected one if no argument is passed. 82 * 83 * @param {mindmaps.Node} [node] defaults to currently selected. 84 */ 85 this.deleteNode = function(node) { 86 if (!node) { 87 node = this.selectedNode; 88 } 89 var map = this.getMindMap(); 90 var action = new mindmaps.action.DeleteNodeAction(node, map); 91 this.executeAction(action); 92 }; 93 94 /** 95 * Attaches a new node the mind map. If invoked without arguments, it will 96 * add a new child to the selected node with an automatically generated 97 * position. 98 * 99 * @param {mindmaps.Node} node the new node 100 * @param {mindmaps.Node} parent 101 */ 102 this.createNode = function(node, parent) { 103 var map = this.getMindMap(); 104 if (!(node && parent)) { 105 parent = this.selectedNode; 106 var action = new mindmaps.action.CreateAutoPositionedNodeAction( 107 parent, map); 108 } else { 109 var action = new mindmaps.action.CreateNodeAction(node, parent, map); 110 } 111 112 this.executeAction(action); 113 }; 114 115 /** 116 * Sets the node as the currently selected. 117 * 118 * @param {mindmaps.Node} node 119 */ 120 this.selectNode = function(node) { 121 if (node === this.selectedNode) { 122 return; 123 } 124 125 var oldSelected = this.selectedNode; 126 this.selectedNode = node; 127 eventBus.publish(mindmaps.Event.NODE_SELECTED, node, oldSelected); 128 }; 129 130 /** 131 * Changes the caption for the passed node or for the selected one if node 132 * is null. 133 * 134 * @param {mindmaps.Node} node 135 * @param {String} caption 136 */ 137 this.changeNodeCaption = function(node, caption) { 138 if (!node) { 139 node = this.selectedNode; 140 } 141 142 var action = new mindmaps.action.ChangeNodeCaptionAction(node, caption); 143 this.executeAction(action); 144 }; 145 146 /** 147 * Executes a node action. An executed action might raise an event over the 148 * event bus and cause an undo event to be emitted via MindMapModel#undoAction. 149 * 150 * @param {mindmaps.Action} action 151 */ 152 this.executeAction = function(action) { 153 var executed = action.execute(); 154 155 // cancel action if false was returned 156 if (executed !== undefined && !executed) { 157 return false; 158 } 159 160 // publish event 161 if (action.event) { 162 if (!Array.isArray(action.event)) { 163 action.event = [ action.event ]; 164 } 165 eventBus.publish.apply(eventBus, action.event); 166 } 167 168 // register undo function if available 169 if (action.undo) { 170 var undoFunc = function() { 171 self.executeAction(action.undo()); 172 }; 173 174 // register redo function 175 var redoFunc = null; 176 if (action.redo) { 177 redoFunc = function() { 178 self.executeAction(action.redo()); 179 }; 180 } 181 182 // emit undo event 183 if (this.undoEvent) { 184 this.undoEvent(undoFunc, redoFunc); 185 } 186 } 187 }; 188 189 /** 190 * Event that is fired when a new undo operation should be recorded. 191 * 192 * @event 193 * @param {Function} undoFunc 194 * @param {Function} [redoFunc] 195 */ 196 this.undoEvent = function(undoFunc, redoFunc) { 197 }; 198 199 this.init(); 200 };