1 /** 2 * Creates a NavigatorView. This view shows a minified version of the mindmap + 3 * controls for adjusting the zoom. 4 * 5 * @constructor 6 */ 7 mindmaps.NavigatorView = function() { 8 var self = this; 9 10 var $content = $("#template-navigator").tmpl(); 11 var $contentActive = $content.children(".active").hide(); 12 var $contentInactive = $content.children(".inactive").hide(); 13 var $dragger = $("#navi-canvas-overlay", $content); 14 var $canvas = $("#navi-canvas", $content); 15 16 /** 17 * Returns the content. 18 * 19 * @returns {jQuery} 20 */ 21 this.getContent = function() { 22 return $content; 23 }; 24 25 /** 26 * Shows the active content. 27 */ 28 this.showActiveContent = function() { 29 $contentInactive.hide(); 30 $contentActive.show(); 31 }; 32 33 /** 34 * Shows the inactive content. 35 */ 36 this.showInactiveContent = function() { 37 $contentActive.hide(); 38 $contentInactive.show(); 39 }; 40 41 /** 42 * Adjusts the size of the red rectangle. 43 * 44 * @param {Number} width 45 * @param {Nubmer} height 46 */ 47 this.setDraggerSize = function(width, height) { 48 $dragger.css({ 49 width : width, 50 height : height 51 }); 52 }; 53 54 /** 55 * Sets the position of the dragger rectangle. 56 * 57 * @param {Number} x 58 * @param {Number} y 59 */ 60 this.setDraggerPosition = function(x, y) { 61 $dragger.css({ 62 left : x, 63 top : y 64 }); 65 }; 66 67 /** 68 * Sets the height of the mini canvas. 69 * 70 * @param {Number} height 71 */ 72 this.setCanvasHeight = function(height) { 73 $("#navi-canvas", $content).css({ 74 height : height 75 }); 76 }; 77 78 /** 79 * Gets the width of the mini canvas. 80 * 81 * @returns {Number} 82 */ 83 this.getCanvasWidth = function() { 84 return $("#navi-canvas", $content).width(); 85 }; 86 87 this.init = function(canvasSize) { 88 $("#navi-slider", $content).slider({ 89 // TODO remove magic numbers. get values from presenter 90 min : 0, 91 max : 11, 92 step : 1, 93 value : 3, 94 slide : function(e, ui) { 95 if (self.sliderChanged) { 96 self.sliderChanged(ui.value); 97 } 98 } 99 }); 100 101 $("#button-navi-zoom-in", $content).button({ 102 text : false, 103 icons : { 104 primary : "ui-icon-zoomin" 105 } 106 }).click(function() { 107 if (self.buttonZoomInClicked) { 108 self.buttonZoomInClicked(); 109 } 110 }); 111 112 $("#button-navi-zoom-out", $content).button({ 113 text : false, 114 icons : { 115 primary : "ui-icon-zoomout" 116 } 117 }).click(function() { 118 if (self.buttonZoomOutClicked) { 119 self.buttonZoomOutClicked(); 120 } 121 }); 122 123 // make draggable 124 $dragger.draggable({ 125 containment : "parent", 126 start : function(e, ui) { 127 if (self.dragStart) { 128 self.dragStart(); 129 } 130 }, 131 drag : function(e, ui) { 132 if (self.dragging) { 133 var x = ui.position.left; 134 var y = ui.position.top; 135 self.dragging(x, y); 136 } 137 }, 138 stop : function(e, ui) { 139 if (self.dragStop) { 140 self.dragStop(); 141 } 142 } 143 }); 144 }; 145 146 /** 147 * Draws the complete mindmap onto the mini canvas. 148 * 149 * @param {mindmaps.MindMap} mindmap 150 * @param {Number} scaleFactor 151 */ 152 this.draw = function(mindmap, scaleFactor) { 153 var root = mindmap.root; 154 var canvas = $canvas[0]; 155 var width = canvas.width; 156 var height = canvas.height; 157 var ctx = canvas.getContext("2d"); 158 ctx.clearRect(0, 0, width, height); 159 ctx.lineWidth = 1.8; 160 161 drawNode(root, width / 2, height / 2); 162 163 // draw rect for root 164 ctx.fillRect(width / 2 - 4, height / 2 - 2, 8, 4); 165 166 function scale(value) { 167 return value / scaleFactor; 168 } 169 170 function drawNode(node, x, y) { 171 ctx.save(); 172 ctx.translate(x, y); 173 174 if (!node.collapseChildren) { 175 node.forEachChild(function(child) { 176 ctx.beginPath(); 177 ctx.strokeStyle = child.branchColor; 178 ctx.moveTo(0, 0); 179 var posX = scale(child.offset.x); 180 var posY = scale(child.offset.y); 181 // var textWidth = 182 // ctx.measureText(child.getCaption()).width; 183 textWidth = 5; 184 185 /** 186 * draw two lines: one going up to the node, and a second 187 * horizontal line for the node caption. if node is left of 188 * the parent (posX < 0), we shorten the first line and draw 189 * the rest horizontally to arrive at the node's offset 190 * position. in the other case, we draw the line to the 191 * node's offset and draw another for the text. 192 */ 193 if (posX < 0) { 194 var firstStop = posX + textWidth; 195 var secondStop = posX; 196 } else { 197 var firstStop = posX; 198 var secondStop = posX + textWidth; 199 } 200 ctx.lineTo(firstStop, posY); 201 ctx.lineTo(secondStop, posY); 202 203 ctx.stroke(); 204 drawNode(child, secondStop, posY); 205 }); 206 } 207 ctx.restore(); 208 } 209 }; 210 211 /** 212 * Shows the zoom level as percentage. 213 * 214 * @param {String} zoom 215 */ 216 this.showZoomLevel = function(zoom) { 217 $("#navi-zoom-level").text(zoom); 218 }; 219 220 /** 221 * Sets the value of the zoom slider. 222 * 223 * @param {Integer} value 224 */ 225 this.setSliderValue = function(value) { 226 $("#navi-slider").slider("value", value); 227 }; 228 }; 229 230 /** 231 * Creates a new NavigatorPresenter. 232 * 233 * @constructor 234 * @param {mindmaps.EventBus} eventBus 235 * @param {mindmaps.NavigatorView} view 236 * @param {mindmaps.CanvasContainer} container 237 * @param {mindmaps.ZoomController} zoomController 238 */ 239 mindmaps.NavigatorPresenter = function(eventBus, view, container, 240 zoomController) { 241 var self = this; 242 var $container = container.getContent(); 243 var viewDragging = false; 244 var scale = zoomController.DEFAULT_ZOOM; 245 var canvasSize = new mindmaps.Point(); 246 var docSize = null; 247 var mindmap = null; 248 249 /** 250 * Calculates and sets the size of the dragger element. 251 */ 252 function calculateDraggerSize() { 253 var cw = $container.width() / scale; 254 var ch = $container.height() / scale; 255 // doc.x / container.x = canvas.x / dragger.x 256 var width = (cw * canvasSize.x) / docSize.x; 257 var height = (ch * canvasSize.y) / docSize.y; 258 259 // limit size to bounds of canvas 260 if (width > canvasSize.x) { 261 width = canvasSize.x; 262 } 263 264 if (height > canvasSize.y) { 265 height = canvasSize.y; 266 } 267 268 view.setDraggerSize(width, height); 269 } 270 271 /** 272 * Calculates and sets the size of the mini canvas. 273 */ 274 function calculateCanvasSize() { 275 var width = view.getCanvasWidth(); 276 var _scale = docSize.x / width; 277 var height = docSize.y / _scale; 278 279 view.setCanvasHeight(height); 280 281 canvasSize.x = width; 282 canvasSize.y = height; 283 } 284 285 /** 286 * Calculates and sets the possition of the dragger element. 287 */ 288 function calculateDraggerPosition() { 289 var sl = $container.scrollLeft() / scale; 290 var st = $container.scrollTop() / scale; 291 292 // sl / dox = dl / cw 293 // dl = sl * cw / dox 294 var left = sl * canvasSize.x / docSize.x; 295 var top = st * canvasSize.y / docSize.y; 296 view.setDraggerPosition(left, top); 297 } 298 299 /** 300 * Calculates and sets the zoom level. 301 */ 302 function calculateZoomLevel() { 303 var zoomlevel = scale * 100 + " %"; 304 view.showZoomLevel(zoomlevel); 305 } 306 307 /** 308 * Calculates and sets the slider value for the zoom level. 309 */ 310 function calculateSliderValue() { 311 var val = scale / zoomController.ZOOM_STEP - 1; 312 view.setSliderValue(val); 313 } 314 315 /** 316 * Initialize view when a document was opened. 317 */ 318 function documentOpened(doc) { 319 docSize = doc.dimensions; 320 mindmap = doc.mindmap; 321 322 calculateCanvasSize(); 323 calculateDraggerPosition(); 324 calculateDraggerSize(); 325 calculateZoomLevel(); 326 calculateSliderValue(); 327 renderView(); 328 329 view.showActiveContent(); 330 331 // move dragger when container was scrolled 332 $container.bind("scroll.navigator-view", function() { 333 if (!viewDragging) { 334 calculateDraggerPosition(); 335 } 336 }); 337 } 338 339 /** 340 * Update the canvas of the view component. 341 */ 342 function renderView() { 343 // draw canvas 344 var scale = docSize.x / canvasSize.x; 345 view.draw(mindmap, scale); 346 } 347 348 /** 349 * Reset when document was closed. 350 */ 351 function documentClosed() { 352 docSize = null; 353 mindmap = null; 354 scale = 1; 355 // clean up 356 // remove listeners 357 $container.unbind("scroll.navigator-view"); 358 359 view.showInactiveContent(); 360 } 361 362 /** 363 * View callbacks. 364 * 365 * @ignore 366 */ 367 368 view.dragStart = function() { 369 viewDragging = true; 370 }; 371 372 // scroll container when the dragger is dragged 373 view.dragging = function(x, y) { 374 var scrollLeft = scale * docSize.x * x / canvasSize.x; 375 var scrollTop = scale * docSize.y * y / canvasSize.y; 376 $container.scrollLeft(scrollLeft).scrollTop(scrollTop); 377 }; 378 379 view.dragStop = function() { 380 viewDragging = false; 381 }; 382 383 view.buttonZoomInClicked = function() { 384 zoomController.zoomIn(); 385 }; 386 387 view.buttonZoomOutClicked = function() { 388 zoomController.zoomOut(); 389 }; 390 391 view.sliderChanged = function(value) { 392 zoomController.zoomTo((value + 1) * zoomController.ZOOM_STEP); 393 }; 394 395 // set dragger size when container was resized 396 container.subscribe(mindmaps.CanvasContainer.Event.RESIZED, function() { 397 if (docSize) { 398 calculateDraggerSize(); 399 } 400 }); 401 402 // document events 403 eventBus.subscribe(mindmaps.Event.DOCUMENT_OPENED, documentOpened); 404 eventBus.subscribe(mindmaps.Event.DOCUMENT_CLOSED, documentClosed); 405 406 // node events 407 eventBus.subscribe(mindmaps.Event.NODE_MOVED, renderView); 408 eventBus.subscribe(mindmaps.Event.NODE_BRANCH_COLOR_CHANGED, renderView); 409 eventBus.subscribe(mindmaps.Event.NODE_CREATED, renderView); 410 eventBus.subscribe(mindmaps.Event.NODE_DELETED, renderView); 411 eventBus.subscribe(mindmaps.Event.NODE_OPENED, renderView); 412 eventBus.subscribe(mindmaps.Event.NODE_CLOSED, renderView); 413 414 eventBus.subscribe(mindmaps.Event.ZOOM_CHANGED, function(zoomFactor) { 415 scale = zoomFactor; 416 calculateDraggerPosition(); 417 calculateDraggerSize(); 418 calculateZoomLevel(); 419 calculateSliderValue(); 420 }); 421 422 this.go = function() { 423 view.init(); 424 view.showInactiveContent(); 425 }; 426 };