OLD | NEW |
1 'use strict'; | 1 'use strict'; |
2 | 2 |
3 /** | 3 /** |
4 * IMPORTANT | 4 * IMPORTANT |
5 * | 5 * |
6 * This module represents a single step in the refactor of the environment | 6 * This module represents a single step in the refactor of the environment |
7 * view. This module is THROW AWAY CODE. Each forthcoming branch should | 7 * view. This module is THROW AWAY CODE. Each forthcoming branch should |
8 * begin by moving relevant code to the proper module, binding that | 8 * begin by moving relevant code to the proper module, binding that |
9 * module to Topo and removing code from here. | 9 * module to Topo and removing code from here. |
10 * | 10 * |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
46 d3.select(this) | 46 d3.select(this) |
47 .select('.unit-count') | 47 .select('.unit-count') |
48 .attr('class', 'unit-count show-count'); | 48 .attr('class', 'unit-count show-count'); |
49 }}, | 49 }}, |
50 mouseout: {callback: function(d, self) { | 50 mouseout: {callback: function(d, self) { |
51 d3.select(this) | 51 d3.select(this) |
52 .select('.unit-count') | 52 .select('.unit-count') |
53 .attr('class', 'unit-count hide-count'); | 53 .attr('class', 'unit-count hide-count'); |
54 }} | 54 }} |
55 }, | 55 }, |
56 | |
57 '.rel-label': { | 56 '.rel-label': { |
58 click: 'relationClick', | 57 click: 'relationClick', |
59 mousemove: 'mousemove' | 58 mousemove: 'mousemove' |
60 }, | 59 }, |
61 | |
62 '.topology .crosshatch-background rect:first-child': { | 60 '.topology .crosshatch-background rect:first-child': { |
63 /** | 61 /** |
64 * If the user clicks on the background we cancel any active add | 62 * If the user clicks on the background we cancel any active add |
65 * relation. | 63 * relation. |
66 */ | 64 */ |
67 click: {callback: function(d, self) { | 65 click: {callback: function(d, self) { |
68 var container = self.get('container'); | 66 var container = self.get('container'); |
69 container.all('.environment-menu.active').removeClass('active'); | 67 container.all('.environment-menu.active').removeClass('active'); |
70 self.service_click_actions.toggleControlPanel(null, self); | 68 self.service_click_actions.toggleControlPanel(null, self); |
71 self.cancelRelationBuild(); | 69 self.cancelRelationBuild(); |
72 self.hideSubordinateRelations(); | 70 self.hideSubordinateRelations(); |
73 }}, | 71 }}, |
74 mousemove: 'mousemove' | 72 mousemove: 'mousemove' |
75 }, | 73 }, |
76 '.dragline': { | 74 '.dragline': { |
77 /** The user clicked while the dragline was active. */ | 75 /** The user clicked while the dragline was active. */ |
78 click: {callback: function(d, self) { | 76 click: {callback: function(d, self) { |
79 // It was technically the dragline that was clicked, but the | 77 // It was technically the dragline that was clicked, but the |
80 // intent was to click on the background, so... | 78 // intent was to click on the background, so... |
81 self.backgroundClicked(); | 79 self.backgroundClicked(); |
82 }} | 80 }} |
83 }, | 81 }, |
84 | |
85 '#zoom-out-btn': {click: 'zoom_out'}, | |
86 '#zoom-in-btn': {click: 'zoom_in'}, | |
87 '.graph-list-picker .picker-button': { | 82 '.graph-list-picker .picker-button': { |
88 click: 'showGraphListPicker' | 83 click: 'showGraphListPicker' |
89 }, | 84 }, |
90 '.graph-list-picker .picker-expanded': { | 85 '.graph-list-picker .picker-expanded': { |
91 click: 'hideGraphListPicker' | 86 click: 'hideGraphListPicker' |
92 }, | 87 }, |
93 // Menu/Controls | 88 // Menu/Controls |
94 '.add-relation': { | 89 '.add-relation': { |
95 /** The user clicked on the "Build Relation" menu item. */ | 90 /** The user clicked on the "Build Relation" menu item. */ |
96 click: { | 91 click: { |
(...skipping 28 matching lines...) Expand all Loading... |
125 service = context.serviceForBox(box); | 120 service = context.serviceForBox(box); |
126 context.service_click_actions | 121 context.service_click_actions |
127 .toggleControlPanel(box, context); | 122 .toggleControlPanel(box, context); |
128 context.service_click_actions | 123 context.service_click_actions |
129 .destroyServiceConfirm(service, context); | 124 .destroyServiceConfirm(service, context); |
130 }} | 125 }} |
131 } | 126 } |
132 }, | 127 }, |
133 d3: { | 128 d3: { |
134 '.service': { | 129 '.service': { |
135 'mousedown.addrel': {callback: function(d, self) { | 130 'mousedown.addrel': {callback: function(d, context) { |
136 var evt = d3.event; | 131 var evt = d3.event; |
137 self.longClickTimer = Y.later(750, this, function(d, e) { | 132 context.longClickTimer = Y.later(750, this, function(d, e) { |
138 // Provide some leeway for accidental dragging. | 133 // Provide some leeway for accidental dragging. |
139 if ((Math.abs(d.x - d.oldX) + Math.abs(d.y - d.oldY)) / | 134 if ((Math.abs(d.x - d.oldX) + Math.abs(d.y - d.oldY)) / |
140 2 > 5) { | 135 2 > 5) { |
141 return; | 136 return; |
142 } | 137 } |
143 | 138 |
144 // Sometimes mouseover is fired after the mousedown, so ensure | 139 // Sometimes mouseover is fired after the mousedown, so ensure |
145 // we have the correct event in d3.event for d3.mouse(). | 140 // we have the correct event in d3.event for d3.mouse(). |
146 d3.event = e; | 141 d3.event = e; |
147 | 142 |
148 // Start the process of adding a relation | 143 // Start the process of adding a relation |
149 self.addRelationDragStart(d, self); | 144 context.addRelationDragStart(d, context); |
150 }, [d, evt], false); | 145 }, [d, evt], false); |
151 }}, | 146 }}, |
152 'mouseup.addrel': {callback: function(d, self) { | 147 'mouseup.addrel': {callback: function(d, context) { |
153 // Cancel the long-click timer if it exists. | 148 // Cancel the long-click timer if it exists. |
154 if (self.longClickTimer) { | 149 if (context.longClickTimer) { |
155 self.longClickTimer.cancel(); | 150 context.longClickTimer.cancel(); |
156 } | 151 } |
157 }} | 152 }} |
158 } | 153 } |
159 }, | 154 }, |
160 yui: { | 155 yui: { |
161 windowresize: 'setSizesFromViewport' | 156 windowresize: { |
| 157 callback: 'setSizesFromViewport', |
| 158 context: 'module'}, |
| 159 rendered: 'renderedHandler' |
162 } | 160 } |
163 }, | 161 }, |
164 | 162 |
165 initializer: function(options) { | 163 initializer: function(options) { |
166 MegaModule.superclass.constructor.apply(this, arguments); | 164 MegaModule.superclass.constructor.apply(this, arguments); |
167 this.publish('navigateTo', {preventable: false}); | |
168 | 165 |
169 // Build a service.id -> BoundingBox map for services. | 166 // Build a service.id -> BoundingBox map for services. |
170 this.service_boxes = {}; | 167 this.service_boxes = {}; |
171 | 168 |
172 // Set a default | 169 // Set a default |
173 this.set('currentServiceClickAction', 'toggleControlPanel'); | 170 this.set('currentServiceClickAction', 'toggleControlPanel'); |
174 }, | 171 }, |
175 | 172 |
176 render: function() { | |
177 MegaModule.superclass.render.apply(this, arguments); | |
178 var container = this.get('container'); | |
179 container.setHTML(Templates.overview()); | |
180 this.svg = container.one('.topology'); | |
181 | |
182 this.renderOnce(); | |
183 | |
184 return this; | |
185 }, | |
186 /* | |
187 * Construct a persistent scene that is managed in update. | |
188 */ | |
189 renderOnce: function() { | |
190 var self = this, | |
191 container = this.get('container'), | |
192 height = 600, | |
193 width = 640, | |
194 fill = d3.scale.category20(); | |
195 | |
196 this.service_scale = d3.scale.log().range([150, 200]); | |
197 this.service_scale_width = d3.scale.log().range([164, 200]), | |
198 this.service_scale_height = d3.scale.log().range([64, 100]); | |
199 this.xscale = d3.scale.linear() | |
200 .domain([-width / 2, width / 2]) | |
201 .range([0, width]), | |
202 this.yscale = d3.scale.linear() | |
203 .domain([-height / 2, height / 2]) | |
204 .range([height, 0]); | |
205 | |
206 // Create a pan/zoom behavior manager. | |
207 var zoom = d3.behavior.zoom() | |
208 .x(this.xscale) | |
209 .y(this.yscale) | |
210 .scaleExtent([0.25, 2.0]) | |
211 .on('zoom', function() { | |
212 // Keep the slider up to date with the scale on other sorts | |
213 // of zoom interactions | |
214 var s = self.slider; | |
215 s.set('value', Math.floor(d3.event.scale * 100)); | |
216 self.rescale(vis, d3.event); | |
217 }); | |
218 self.zoom = zoom; | |
219 | |
220 // Set up the visualization with a pack layout. | |
221 var vis = d3.select(container.getDOMNode()) | |
222 .select('.crosshatch-background') | |
223 .append('svg:svg') | |
224 .attr('pointer-events', 'all') | |
225 .attr('width', width) | |
226 .attr('height', height) | |
227 .append('svg:g') | |
228 .call(zoom) | |
229 // Disable zoom on double click. | |
230 .on('dblclick.zoom', null) | |
231 .append('g'); | |
232 | |
233 vis.append('svg:rect') | |
234 .attr('class', 'graph') | |
235 .attr('fill', 'rgba(255,255,255,0)'); | |
236 | |
237 this.vis = vis; | |
238 this.tree = d3.layout.pack() | |
239 .size([width, height]) | |
240 .value(function(d) { | |
241 return Math.max(d.unit_count, 1); | |
242 }) | |
243 .padding(300); | |
244 | |
245 this.updateCanvas(); | |
246 }, | |
247 | |
248 serviceClick: function(d, context) { | 173 serviceClick: function(d, context) { |
249 // Ignore if we clicked outside the actual service node. | 174 // Ignore if we clicked outside the actual service node. |
250 var container = context.get('container'), | 175 var topo = context.get('component'), |
251 mouse_coords = d3.mouse(container.one('svg').getDOMNode()); | 176 container = context.get('container'), |
252 if (!d.containsPoint(mouse_coords, context.zoom)) { | 177 mouse_coords = d3.mouse(container.one('svg').getDOMNode()); |
| 178 if (!d.containsPoint(mouse_coords, topo.zoom)) { |
253 return; | 179 return; |
254 } | 180 } |
255 // Get the current click action | 181 // Get the current click action |
256 var curr_click_action = context.get('currentServiceClickAction'); | 182 var curr_click_action = context.get('currentServiceClickAction'); |
257 // Fire the action named in the following scheme: | 183 // Fire the action named in the following scheme: |
258 // service_click_action.<action> | 184 // service_click_action.<action> |
259 // with the service, the SVG node, and the view | 185 // with the service, the SVG node, and the view |
260 // as arguments. | 186 // as arguments. |
261 (context.service_click_actions[curr_click_action])( | 187 (context.service_click_actions[curr_click_action])( |
262 d, context, this); | 188 d, context, this); |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 self.addRelationDrag | 240 self.addRelationDrag |
315 .call(self, self.get('addRelationStart_service'), node); | 241 .call(self, self.get('addRelationStart_service'), node); |
316 } | 242 } |
317 }, | 243 }, |
318 | 244 |
319 /* | 245 /* |
320 * Sync view models with current db.models. | 246 * Sync view models with current db.models. |
321 */ | 247 */ |
322 updateData: function() { | 248 updateData: function() { |
323 //model data | 249 //model data |
324 var vis = this.vis, | 250 var topo = this.get('component'), |
325 db = this.get('component').get('db'), | 251 vis = topo.vis, |
| 252 db = topo.get('db'), |
326 relations = db.relations.toArray(), | 253 relations = db.relations.toArray(), |
327 services = db.services.map(views.toBoundingBox); | 254 services = db.services.map(views.toBoundingBox); |
328 | 255 |
329 this.services = services; | 256 this.services = services; |
330 | 257 |
331 Y.each(services, function(service) { | 258 Y.each(services, function(service) { |
332 // Update services with existing positions. | 259 // Update services with existing positions. |
333 var existing = this.service_boxes[service.id]; | 260 var existing = this.service_boxes[service.id]; |
334 if (existing) { | 261 if (existing) { |
335 service.pos = existing.pos; | 262 service.pos = existing.pos; |
336 } | 263 } |
337 service.margins(service.subordinate ? | 264 service.margins(service.subordinate ? |
338 { | 265 { |
339 top: 0.05, | 266 top: 0.05, |
340 bottom: 0.1, | 267 bottom: 0.1, |
341 left: 0.084848, | 268 left: 0.084848, |
342 right: 0.084848} : | 269 right: 0.084848} : |
343 { | 270 { |
344 top: 0, | 271 top: 0, |
345 bottom: 0.1667, | 272 bottom: 0.1667, |
346 left: 0.086758, | 273 left: 0.086758, |
347 right: 0.086758}); | 274 right: 0.086758}); |
348 this.service_boxes[service.id] = service; | 275 this.service_boxes[service.id] = service; |
349 }, this); | 276 }, this); |
350 this.rel_pairs = this.processRelations(relations); | 277 this.rel_pairs = this.processRelations(relations); |
351 | 278 |
352 // Nodes are mapped by modelId tuples. | 279 // Nodes are mapped by modelId tuples. |
353 this.node = vis.selectAll('.service') | 280 this.node = vis.selectAll('.service') |
354 .data(services, function(d) { | 281 .data(services, function(d) { |
355 return d.modelId();}); | 282 return d.modelId();}); |
356 }, | 283 }, |
357 | 284 |
358 /* | 285 /* |
359 * Attempt to reuse as much of the existing graph and view models | 286 * Attempt to reuse as much of the existing graph and view models |
360 * as possible to re-render the graph. | 287 * as possible to re-render the graph. |
361 */ | 288 */ |
362 updateCanvas: function() { | 289 update: function() { |
363 var self = this, | 290 var self = this, |
364 tree = this.tree, | 291 topo = this.get('component'), |
365 vis = this.vis; | 292 width = topo.get('width'), |
| 293 height = topo.get('height'); |
| 294 |
| 295 if (!this.service_scale) { |
| 296 this.service_scale = d3.scale.log().range([150, 200]); |
| 297 this.service_scale_width = d3.scale.log().range([164, 200]), |
| 298 this.service_scale_height = d3.scale.log().range([64, 100]); |
| 299 } |
| 300 |
| 301 if (!this.tree) { |
| 302 this.tree = d3.layout.pack() |
| 303 .size([width, height]) |
| 304 .value(function(d) { |
| 305 return Math.max(d.unit_count, 1); |
| 306 }) |
| 307 .padding(300); |
| 308 } |
366 | 309 |
367 //Process any changed data. | 310 //Process any changed data. |
368 this.updateData(); | 311 this.updateData(); |
369 | 312 |
370 var drag = d3.behavior.drag() | 313 var drag = d3.behavior.drag() |
371 .on('dragstart', function(d) { | 314 .on('dragstart', function(d) { |
372 d.oldX = d.x; | 315 d.oldX = d.x; |
373 d.oldY = d.y; | 316 d.oldY = d.y; |
374 self.get('container').all('.environment-menu.active') | 317 self.get('container').all('.environment-menu.active') |
375 .removeClass('active'); | 318 .removeClass('active'); |
(...skipping 13 matching lines...) Expand all Loading... |
389 d3.select(this).attr('transform', function(d, i) { | 332 d3.select(this).attr('transform', function(d, i) { |
390 return d.translateStr(); | 333 return d.translateStr(); |
391 }); | 334 }); |
392 if (self.get('active_service') === d) { | 335 if (self.get('active_service') === d) { |
393 self.updateServiceMenuLocation(); | 336 self.updateServiceMenuLocation(); |
394 } | 337 } |
395 | 338 |
396 // Clear any state while dragging. | 339 // Clear any state while dragging. |
397 self.get('container').all('.environment-menu.active') | 340 self.get('container').all('.environment-menu.active') |
398 .removeClass('active'); | 341 .removeClass('active'); |
399 self.service_click_actions.toggleControlPanel(null, self); | |
400 self.cancelRelationBuild(); | 342 self.cancelRelationBuild(); |
401 | 343 |
402 // Update relation lines for just this service. | 344 // Update relation lines for just this service. |
403 updateLinkEndpoints(d); | 345 updateLinkEndpoints(d); |
404 } | 346 } |
405 }) | 347 }) |
406 .on('dragend', function(d, i) { | 348 .on('dragend', function(d, i) { |
407 if (self.buildingRelation) { | 349 if (self.buildingRelation) { |
408 self.addRelationDragEnd(); | 350 self.addRelationDragEnd(); |
409 } | 351 } |
(...skipping 15 matching lines...) Expand all Loading... |
425 .getConnectorPair(relation.target()), | 367 .getConnectorPair(relation.target()), |
426 s = connectors[0], | 368 s = connectors[0], |
427 t = connectors[1]; | 369 t = connectors[1]; |
428 rel_group.select('line') | 370 rel_group.select('line') |
429 .attr('x1', s[0]) | 371 .attr('x1', s[0]) |
430 .attr('y1', s[1]) | 372 .attr('y1', s[1]) |
431 .attr('x2', t[0]) | 373 .attr('x2', t[0]) |
432 .attr('y2', t[1]); | 374 .attr('y2', t[1]); |
433 rel_group.select('.rel-label') | 375 rel_group.select('.rel-label') |
434 .attr('transform', function(d) { | 376 .attr('transform', function(d) { |
435 // XXX: This has to happen on update, not enter | |
436 return 'translate(' + | 377 return 'translate(' + |
437 [Math.max(s[0], t[0]) - | 378 [Math.max(s[0], t[0]) - |
438 Math.abs((s[0] - t[0]) / 2), | 379 Math.abs((s[0] - t[0]) / 2), |
439 Math.max(s[1], t[1]) - | 380 Math.max(s[1], t[1]) - |
440 Math.abs((s[1] - t[1]) / 2)] + ')'; | 381 Math.abs((s[1] - t[1]) / 2)] + ')'; |
441 }); | 382 }); |
442 }); | 383 }); |
443 } | 384 } |
444 | 385 |
445 // Generate a node for each service, draw it as a rect with | 386 // Generate a node for each service, draw it as a rect with |
446 // labels for service and charm. | 387 // labels for service and charm. |
447 var node = this.node; | 388 var node = this.node; |
448 | 389 |
449 // Rerun the pack layout. | 390 // Rerun the pack layout. |
450 // Pack doesn't honor existing positions and will | 391 // Pack doesn't honor existing positions and will |
451 // re-layout the entire graph. As a short term work | 392 // re-layout the entire graph. As a short term work |
452 // around we layout only new nodes. This has the side | 393 // around we layout only new nodes. This has the side |
453 // effect that node nodes can overlap and will | 394 // effect that node nodes can overlap and will |
454 // be fixed later. | 395 // be fixed later. |
455 var new_services = this.services.filter(function(boundingBox) { | 396 var new_services = this.services.filter(function(boundingBox) { |
456 return !Y.Lang.isNumber(boundingBox.x); | 397 return !Y.Lang.isNumber(boundingBox.x); |
457 }); | 398 }); |
458 this.tree.nodes({children: new_services}); | 399 this.tree.nodes({children: new_services}); |
459 | 400 |
460 // enter | 401 // enter |
461 node | 402 node |
462 .enter().append('g') | 403 .enter().append('g') |
463 .attr('class', function(d) { | 404 .attr('class', function(d) { |
464 return (d.subordinate ? 'subordinate ' : '') + 'service'; | 405 return (d.subordinate ? 'subordinate ' : '') + 'service'; |
465 }) | 406 }) |
466 .call(drag) | 407 .call(drag) |
467 .on('mousedown.addrel', function(d) { | 408 .attr('transform', function(d) { |
468 self.d3Events['.service']['mousedown.addrel'] | 409 return d.translateStr(); |
469 .call(this, d, self, d3.event); | 410 }); |
470 }) | |
471 .on('mouseup.addrel', function(d) { | |
472 self.d3Events['.service']['mouseup.addrel'] | |
473 .call(this, d, self, d3.event); | |
474 }) | |
475 .attr('transform', function(d) { | |
476 return d.translateStr();}); | |
477 | 411 |
478 // Update | 412 // Update |
479 this.drawService(node); | 413 this.drawService(node); |
480 | 414 |
481 // Exit | 415 // Exit |
482 node.exit() | 416 node.exit() |
483 .call(function(d) { | 417 .remove(); |
484 // TODO: update the service_boxes | |
485 // removing the bound data | |
486 }) | |
487 .remove(); | |
488 | 418 |
489 function updateLinks() { | 419 function updateLinks() { |
490 // Enter. | 420 // Enter. |
491 var g = self.drawRelationGroup(), | 421 var g = self.drawRelationGroup(), |
492 link = g.selectAll('line.relation'); | 422 link = g.selectAll('line.relation'); |
493 | 423 |
494 // Update (+ enter selection). | 424 // Update (+ enter selection). |
495 link.each(self.drawRelation); | 425 link.each(self.drawRelation); |
496 | 426 |
497 // Exit | 427 // Exit |
498 g.exit().remove(); | 428 g.exit().remove(); |
499 } | 429 } |
500 | 430 |
501 // Draw or schedule redraw of links. | 431 // Draw or schedule redraw of links. |
502 updateLinks(); | 432 updateLinks(); |
503 | 433 |
504 }, | 434 }, |
505 | 435 |
506 /* | 436 /* |
507 * Draw a new relation link with label and controls. | 437 * Draw a new relation link with label and controls. |
508 */ | 438 */ |
509 drawRelationGroup: function() { | 439 drawRelationGroup: function() { |
510 // Add a labelgroup. | 440 // Add a labelgroup. |
511 var self = this, | 441 var self = this, |
512 g = self.vis.selectAll('g.rel-group') | 442 vis = this.get('component').vis, |
513 .data(self.rel_pairs, function(r) { | 443 g = vis.selectAll('g.rel-group') |
514 return r.modelIds(); | 444 .data(self.rel_pairs, function(r) { |
515 }); | 445 return r.modelIds(); |
| 446 }); |
516 | 447 |
517 var enter = g.enter(); | 448 var enter = g.enter(); |
518 | 449 |
519 enter.insert('g', 'g.service') | 450 enter.insert('g', 'g.service') |
520 .attr('id', function(d) { | 451 .attr('id', function(d) { |
521 return d.id; | 452 return d.id; |
522 }) | 453 }) |
523 .attr('class', function(d) { | 454 .attr('class', function(d) { |
524 // Mark the rel-group as a subordinate relation if need be. | 455 // Mark the rel-group as a subordinate relation if need be. |
525 return (d.scope === 'container' ? | 456 return (d.scope === 'container' ? |
526 'subordinate-rel-group ' : '') + | 457 'subordinate-rel-group ' : '') + |
527 'rel-group'; | 458 'rel-group'; |
528 }) | 459 }) |
529 .append('svg:line', 'g.service') | 460 .append('svg:line', 'g.service') |
530 .attr('class', function(d) { | 461 .attr('class', function(d) { |
531 // Style relation lines differently depending on status. | 462 // Style relation lines differently depending on status. |
532 return (d.pending ? 'pending-relation ' : '') + | 463 return (d.pending ? 'pending-relation ' : '') + |
533 (d.scope === 'container' ? 'subordinate-relation ' : '') + | 464 (d.scope === 'container' ? 'subordinate-relation ' : '') + |
534 'relation'; | 465 'relation'; |
535 }); | 466 }); |
536 | 467 |
537 g.selectAll('rel-label').remove(); | 468 g.selectAll('.rel-label').remove(); |
538 g.selectAll('text').remove(); | 469 g.selectAll('text').remove(); |
539 g.selectAll('rect').remove(); | 470 g.selectAll('rect').remove(); |
540 var label = g.append('g') | 471 var label = g.append('g') |
541 .attr('class', 'rel-label') | 472 .attr('class', 'rel-label') |
542 .attr('transform', function(d) { | 473 .attr('transform', function(d) { |
543 // XXX: This has to happen on update, not enter | 474 // XXX: This has to happen on update, not enter |
544 var connectors = d.source().getConnectorPair(d.target()), | 475 var connectors = d.source().getConnectorPair(d.target()), |
545 s = connectors[0], | 476 s = connectors[0], |
546 t = connectors[1]; | 477 t = connectors[1]; |
547 return 'translate(' + | 478 return 'translate(' + |
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
844 | 775 |
845 /* | 776 /* |
846 * Utility function to get subordinate relations for a service. | 777 * Utility function to get subordinate relations for a service. |
847 */ | 778 */ |
848 subordinateRelationsForService: function(service) { | 779 subordinateRelationsForService: function(service) { |
849 return this.rel_pairs.filter(function(p) { | 780 return this.rel_pairs.filter(function(p) { |
850 return p.modelIds().indexOf(service.modelId()) !== -1 && | 781 return p.modelIds().indexOf(service.modelId()) !== -1 && |
851 p.scope === 'container'; | 782 p.scope === 'container'; |
852 }); | 783 }); |
853 }, | 784 }, |
854 renderSlider: function() { | |
855 var self = this, | |
856 value = 100, | |
857 currentScale = this.get('scale'); | |
858 // Build a slider to control zoom level | |
859 if (currentScale) { | |
860 value = currentScale * 100; | |
861 } | |
862 var slider = new Y.Slider({ | |
863 min: 25, | |
864 max: 200, | |
865 value: value | |
866 }); | |
867 slider.render('#slider-parent'); | |
868 slider.after('valueChange', function(evt) { | |
869 // Don't fire a zoom if there's a zoom event already in progress; | |
870 // that will run rescale for us. | |
871 if (d3.event && d3.event.scale && d3.event.translate) { | |
872 return; | |
873 } | |
874 self._fire_zoom((evt.newVal - evt.prevVal) / 100); | |
875 }); | |
876 self.slider = slider; | |
877 }, | |
878 | |
879 /* | 785 /* |
880 * Utility method to get a service object from the DB | 786 * Utility method to get a service object from the DB |
881 * given a BoundingBox. | 787 * given a BoundingBox. |
882 */ | 788 */ |
883 serviceForBox: function(boundingBox) { | 789 serviceForBox: function(boundingBox) { |
884 var db = this.get('component').get('db'); | 790 var db = this.get('component').get('db'); |
885 return db.services.getById(boundingBox.id); | 791 return db.services.getById(boundingBox.id); |
886 }, | 792 }, |
887 | 793 |
888 | 794 |
(...skipping 23 matching lines...) Expand all Loading... |
912 * Finish DOM-dependent rendering | 818 * Finish DOM-dependent rendering |
913 * | 819 * |
914 * Some portions of the visualization require information pulled | 820 * Some portions of the visualization require information pulled |
915 * from the DOM, such as the clientRects used for sizing relation | 821 * from the DOM, such as the clientRects used for sizing relation |
916 * labels and the viewport size used for sizing the whole graph. This | 822 * labels and the viewport size used for sizing the whole graph. This |
917 * is called after the view is attached to the DOM in order to | 823 * is called after the view is attached to the DOM in order to |
918 * perform all of that work. In the app, it's called as a callback | 824 * perform all of that work. In the app, it's called as a callback |
919 * in app.showView(), and in testing, it needs to be called manually, | 825 * in app.showView(), and in testing, it needs to be called manually, |
920 * if the test relies on any of this data. | 826 * if the test relies on any of this data. |
921 */ | 827 */ |
922 postRender: function() { | 828 renderedHandler: function() { |
923 var container = this.get('container'); | 829 var container = this.get('container'); |
924 | 830 |
| 831 this.update(); |
| 832 |
925 // Set the sizes from the viewport. | 833 // Set the sizes from the viewport. |
926 this.setSizesFromViewport(); | 834 this.setSizesFromViewport(); |
927 | 835 |
928 // Ensure relation labels are sized properly. | 836 // Ensure relation labels are sized properly. |
929 container.all('.rel-label').each(function(label) { | 837 container.all('.rel-label').each(function(label) { |
930 var width = label.one('text').getClientRect().width + 10; | 838 var width = label.one('text').getClientRect().width + 10; |
931 label.one('rect').setAttribute('width', width) | 839 label.one('rect').setAttribute('width', width) |
932 .setAttribute('x', -width / 2); | 840 .setAttribute('x', -width / 2); |
933 }); | 841 }); |
934 | 842 |
935 // Preserve zoom when the scene is updated. | |
936 var changed = false, | |
937 currentScale = this.get('scale'), | |
938 currentTranslate = this.get('translate'); | |
939 if (currentTranslate && currentTranslate !== this.zoom.translate()) { | |
940 this.zoom.translate(currentTranslate); | |
941 changed = true; | |
942 } | |
943 if (currentScale && currentScale !== this.zoom.scale()) { | |
944 this.zoom.scale(currentScale); | |
945 changed = true; | |
946 } | |
947 if (changed) { | |
948 this._fire_zoom(0); | |
949 } | |
950 | |
951 // Render the slider after the view is attached. | |
952 // Although there is a .syncUI() method on sliders, it does not | |
953 // seem to play well with the app framework: the slider will render | |
954 // the first time, but on navigation away and back, will not | |
955 // re-render within the view. | |
956 this.renderSlider(); | |
957 | |
958 // Chainable method. | 843 // Chainable method. |
959 return this; | 844 return this; |
960 }, | 845 }, |
961 | 846 |
962 /* | 847 /* |
963 * Event handler for the add relation button. | 848 * Event handler for the add relation button. |
964 */ | 849 */ |
965 addRelation: function(evt) { | 850 addRelation: function(evt) { |
966 var curr_action = this.get('currentServiceClickAction'), | 851 var curr_action = this.get('currentServiceClickAction'), |
967 container = this.get('container'); | 852 container = this.get('container'); |
968 if (curr_action === 'show_service') { | 853 if (curr_action === 'show_service') { |
969 this.set('currentServiceClickAction', 'addRelationStart'); | 854 this.set('currentServiceClickAction', 'addRelationStart'); |
970 } else if (curr_action === 'addRelationStart' || | 855 } else if (curr_action === 'addRelationStart' || |
971 curr_action === 'ambiguousAddRelationCheck') { | 856 curr_action === 'ambiguousAddRelationCheck') { |
972 this.set('currentServiceClickAction', 'toggleControlPanel'); | 857 this.set('currentServiceClickAction', 'toggleControlPanel'); |
973 } // Otherwise do nothing. | 858 } // Otherwise do nothing. |
974 }, | 859 }, |
975 | 860 |
976 addRelationDragStart: function(d, context) { | 861 addRelationDragStart: function(d, context) { |
977 // Create a pending drag-line. | 862 // Create a pending drag-line. |
978 var dragline = this.vis.append('line') | 863 var vis = this.get('component').vis, |
979 .attr('class', 'relation pending-relation dragline dragging'), | 864 dragline = vis.append('line') |
980 self = this; | 865 .attr('class', |
| 866 'relation pending-relation dragline dragging'), |
| 867 self = this; |
981 | 868 |
982 // Start the line between the cursor and the nearest connector | 869 // Start the line between the cursor and the nearest connector |
983 // point on the service. | 870 // point on the service. |
984 var mouse = d3.mouse(Y.one('svg').getDOMNode()); | 871 var mouse = d3.mouse(Y.one('.topology svg').getDOMNode()); |
985 self.cursorBox = views.BoundingBox(); | 872 self.cursorBox = new views.BoundingBox(); |
986 self.cursorBox.pos = {x: mouse[0], y: mouse[1], w: 0, h: 0}; | 873 self.cursorBox.pos = {x: mouse[0], y: mouse[1], w: 0, h: 0}; |
987 var point = self.cursorBox.getConnectorPair(d); | 874 var point = self.cursorBox.getConnectorPair(d); |
988 dragline.attr('x1', point[0][0]) | 875 dragline.attr('x1', point[0][0]) |
989 .attr('y1', point[0][1]) | 876 .attr('y1', point[0][1]) |
990 .attr('x2', point[1][0]) | 877 .attr('x2', point[1][0]) |
991 .attr('y2', point[1][1]); | 878 .attr('y2', point[1][1]); |
992 self.dragline = dragline; | 879 self.dragline = dragline; |
993 | 880 |
994 // Start the add-relation process. | 881 // Start the add-relation process. |
995 context.service_click_actions | 882 context.service_click_actions |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1084 Y.bind(function(ev) { | 971 Y.bind(function(ev) { |
1085 ev.preventDefault(); | 972 ev.preventDefault(); |
1086 var confirmButton = ev.target; | 973 var confirmButton = ev.target; |
1087 confirmButton.set('disabled', true); | 974 confirmButton.set('disabled', true); |
1088 view.removeRelation(d, context, view, confirmButton); | 975 view.removeRelation(d, context, view, confirmButton); |
1089 }, | 976 }, |
1090 this))); | 977 this))); |
1091 }, | 978 }, |
1092 | 979 |
1093 cancelRelationBuild: function() { | 980 cancelRelationBuild: function() { |
| 981 var vis = this.get('component').vis; |
1094 if (this.dragline) { | 982 if (this.dragline) { |
1095 // Get rid of our drag line | 983 // Get rid of our drag line |
1096 this.dragline.remove(); | 984 this.dragline.remove(); |
1097 this.dragline = null; | 985 this.dragline = null; |
1098 } | 986 } |
1099 this.clickAddRelation = null; | 987 this.clickAddRelation = null; |
1100 this.set('currentServiceClickAction', 'toggleControlPanel'); | 988 this.set('currentServiceClickAction', 'toggleControlPanel'); |
1101 this.buildingRelation = false; | 989 this.buildingRelation = false; |
1102 this.show(this.vis.selectAll('.service')) | 990 this.show(vis.selectAll('.service')) |
1103 .classed('selectable-service', false); | 991 .classed('selectable-service', false); |
1104 }, | 992 }, |
1105 | 993 |
1106 /** | 994 /** |
1107 * The user clicked on the environment view background. | 995 * The user clicked on the environment view background. |
1108 * | 996 * |
1109 * If we are in the middle of adding a relation, cancel the relation | 997 * If we are in the middle of adding a relation, cancel the relation |
1110 * adding. | 998 * adding. |
1111 * | 999 * |
1112 * @method backgroundClicked | 1000 * @method backgroundClicked |
1113 * @return {undefined} Side effects only. | 1001 * @return {undefined} Side effects only. |
1114 */ | 1002 */ |
1115 backgroundClicked: function() { | 1003 backgroundClicked: function() { |
1116 if (this.clickAddRelation) { | 1004 if (this.clickAddRelation) { |
1117 this.cancelRelationBuild(); | 1005 this.cancelRelationBuild(); |
1118 } | 1006 } |
1119 }, | 1007 }, |
1120 | 1008 |
1121 /** | 1009 /** |
1122 * An "add relation" action has been initiated by the user. | 1010 * An "add relation" action has been initiated by the user. |
1123 * | 1011 * |
1124 * @method startRelation | 1012 * @method startRelation |
1125 * @param {object} service The service that is the source of the | 1013 * @param {object} service The service that is the source of the |
1126 * relation. | 1014 * relation. |
1127 * @return {undefined} Side effects only. | 1015 * @return {undefined} Side effects only. |
1128 */ | 1016 */ |
1129 startRelation: function(service) { | 1017 startRelation: function(service) { |
1130 // Set flags on the view that indicate we are building a relation. | 1018 // Set flags on the view that indicate we are building a relation. |
| 1019 var vis = this.get('component').vis; |
| 1020 |
1131 this.buildingRelation = true; | 1021 this.buildingRelation = true; |
1132 this.clickAddRelation = true; | 1022 this.clickAddRelation = true; |
1133 | 1023 |
1134 this.show(this.vis.selectAll('.service')); | 1024 this.show(vis.selectAll('.service')); |
1135 | 1025 |
1136 var db = this.get('component').get('db'), | 1026 var db = this.get('component').get('db'), |
1137 getServiceEndpoints = this.get('component') | 1027 getServiceEndpoints = this.get('component') |
1138 .get('getServiceEndpoints'), | 1028 .get('getServiceEndpoints'), |
1139 endpoints = models.getEndpoints( | 1029 endpoints = models.getEndpoints( |
1140 service, getServiceEndpoints(), db), | 1030 service, getServiceEndpoints(), db), |
1141 // Transform endpoints into a list of relatable services (to the | 1031 // Transform endpoints into a list of relatable services (to the |
1142 // service). | 1032 // service). |
1143 possible_relations = Y.Array.map( | 1033 possible_relations = Y.Array.map( |
1144 Y.Array.flatten(Y.Object.values(endpoints)), | 1034 Y.Array.flatten(Y.Object.values(endpoints)), |
1145 function(ep) {return ep.service;}), | 1035 function(ep) {return ep.service;}), |
1146 invalidRelationTargets = {}; | 1036 invalidRelationTargets = {}; |
1147 | 1037 |
1148 // Iterate services and invert the possibles list. | 1038 // Iterate services and invert the possibles list. |
1149 db.services.each(function(s) { | 1039 db.services.each(function(s) { |
1150 if (Y.Array.indexOf(possible_relations, | 1040 if (Y.Array.indexOf(possible_relations, |
1151 s.get('id')) === -1) { | 1041 s.get('id')) === -1) { |
1152 invalidRelationTargets[s.get('id')] = true; | 1042 invalidRelationTargets[s.get('id')] = true; |
1153 } | 1043 } |
1154 }); | 1044 }); |
1155 | 1045 |
1156 // Fade elements to which we can't relate. | 1046 // Fade elements to which we can't relate. |
1157 // Rather than two loops this marks | 1047 // Rather than two loops this marks |
1158 // all services as selectable and then | 1048 // all services as selectable and then |
1159 // removes the invalid ones. | 1049 // removes the invalid ones. |
1160 this.fade(this.vis.selectAll('.service') | 1050 this.fade(vis.selectAll('.service') |
1161 .classed('selectable-service', true) | 1051 .classed('selectable-service', true) |
1162 .filter(function(d) { | 1052 .filter(function(d) { |
1163 return (d.id in invalidRelationTargets && | 1053 return (d.id in invalidRelationTargets && |
1164 d.id !== service.id); | 1054 d.id !== service.id); |
1165 })) | 1055 })) |
1166 .classed('selectable-service', false); | 1056 .classed('selectable-service', false); |
1167 | 1057 |
1168 // Store possible endpoints. | 1058 // Store possible endpoints. |
1169 this.set('addRelationStart_possibleEndpoints', endpoints); | 1059 this.set('addRelationStart_possibleEndpoints', endpoints); |
1170 // Set click action. | 1060 // Set click action. |
1171 this.set('currentServiceClickAction', 'ambiguousAddRelationCheck'); | 1061 this.set('currentServiceClickAction', 'ambiguousAddRelationCheck'); |
1172 }, | 1062 }, |
1173 | 1063 |
1174 | |
1175 /* | |
1176 * Zoom in event handler. | |
1177 */ | |
1178 zoom_out: function(data, context) { | |
1179 var slider = context.slider, | |
1180 val = slider.get('value'); | |
1181 slider.set('value', val - 25); | |
1182 }, | |
1183 | |
1184 /* | |
1185 * Zoom out event handler. | |
1186 */ | |
1187 zoom_in: function(data, context) { | |
1188 var slider = context.slider, | |
1189 val = slider.get('value'); | |
1190 slider.set('value', val + 25); | |
1191 }, | |
1192 | |
1193 /* | |
1194 * Wraper around the actual rescale method for zoom buttons. | |
1195 */ | |
1196 _fire_zoom: function(delta) { | |
1197 var vis = this.vis, | |
1198 zoom = this.zoom, | |
1199 evt = {}; | |
1200 | |
1201 // Build a temporary event that rescale can use of a similar | |
1202 // construction to d3.event. | |
1203 evt.translate = zoom.translate(); | |
1204 evt.scale = zoom.scale() + delta; | |
1205 | |
1206 // Update the scale in our zoom behavior manager to maintain state. | |
1207 zoom.scale(evt.scale); | |
1208 | |
1209 // Update the translate so that we scale from the center | |
1210 // instead of the origin. | |
1211 var rect = vis.select('rect'); | |
1212 evt.translate[0] -= parseInt(rect.attr('width'), 10) / 2 * delta; | |
1213 evt.translate[1] -= parseInt(rect.attr('height'), 10) / 2 * delta; | |
1214 zoom.translate(evt.translate); | |
1215 | |
1216 this.rescale(vis, evt); | |
1217 }, | |
1218 | |
1219 /* | |
1220 * Rescale the visualization on a zoom/pan event. | |
1221 */ | |
1222 rescale: function(vis, evt) { | |
1223 // Make sure we don't scale outside of our bounds. | |
1224 // This check is needed because we're messing with d3's zoom | |
1225 // behavior outside of mouse events (e.g.: with the slider), | |
1226 // and can't trust that zoomExtent will play well. | |
1227 var new_scale = Math.floor(evt.scale * 100); | |
1228 if (new_scale < 25 || new_scale > 200) { | |
1229 evt.scale = this.get('scale'); | |
1230 } | |
1231 // Store the current value of scale so that it can be restored later. | |
1232 this.set('scale', evt.scale); | |
1233 // Store the current value of translate as well, by copying the event | |
1234 // array in order to avoid reference sharing. | |
1235 this.set('translate', evt.translate.slice(0)); | |
1236 vis.attr('transform', 'translate(' + evt.translate + ')' + | |
1237 ' scale(' + evt.scale + ')'); | |
1238 this.updateServiceMenuLocation(); | |
1239 }, | |
1240 | |
1241 /* | 1064 /* |
1242 * Event handler to show the graph-list picker | 1065 * Event handler to show the graph-list picker |
1243 */ | 1066 */ |
1244 showGraphListPicker: function(evt) { | 1067 showGraphListPicker: function(evt) { |
1245 var container = this.get('container'), | 1068 var container = this.get('container'), |
1246 picker = container.one('.graph-list-picker'); | 1069 picker = container.one('.graph-list-picker'); |
1247 picker.addClass('inactive'); | 1070 picker.addClass('inactive'); |
1248 picker.one('.picker-expanded').addClass('active'); | 1071 picker.one('.picker-expanded').addClass('active'); |
1249 }, | 1072 }, |
1250 | 1073 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1286 }, | 1109 }, |
1287 | 1110 |
1288 /* | 1111 /* |
1289 * Set the visualization size based on the viewport | 1112 * Set the visualization size based on the viewport |
1290 */ | 1113 */ |
1291 setSizesFromViewport: function() { | 1114 setSizesFromViewport: function() { |
1292 // This event allows other page components that may unintentionally | 1115 // This event allows other page components that may unintentionally |
1293 // affect the page size, such as the charm panel, to get out of the | 1116 // affect the page size, such as the charm panel, to get out of the |
1294 // way before we compute sizes. Note the | 1117 // way before we compute sizes. Note the |
1295 // "afterPageSizeRecalculation" event at the end of this function. | 1118 // "afterPageSizeRecalculation" event at the end of this function. |
1296 Y.fire('beforePageSizeRecalculation'); | |
1297 // start with some reasonable defaults | 1119 // start with some reasonable defaults |
1298 var vis = this.vis, | 1120 console.log('setSizesFromViewPort', this, arguments); |
1299 container = this.get('container'), | 1121 var topo = this.get('component'), |
1300 xscale = this.xscale, | 1122 container = this.get('container'), |
1301 yscale = this.yscale, | 1123 vis = topo.vis, |
1302 svg = container.one('svg'), | 1124 xscale = topo.xScale, |
1303 canvas = container.one('.crosshatch-background'); | 1125 yscale = topo.yScale, |
| 1126 svg = container.one('svg'), |
| 1127 canvas = container.one('.topology-canvas'); |
| 1128 |
| 1129 topo.fire('beforePageSizeRecalculation'); |
1304 // Get the canvas out of the way so we can calculate the size | 1130 // Get the canvas out of the way so we can calculate the size |
1305 // correctly (the canvas contains the svg). We want it to be the | 1131 // correctly (the canvas contains the svg). We want it to be the |
1306 // smallest size we accept--no smaller or bigger--or else the | 1132 // smallest size we accept--no smaller or bigger--or else the |
1307 // presence or absence of scrollbars may affect our calculations | 1133 // presence or absence of scrollbars may affect our calculations |
1308 // incorrectly. | 1134 // incorrectly. |
1309 canvas.setStyles({height: 600, width: 800}); | 1135 canvas.setStyles({height: 600, width: 800}); |
1310 var dimensions = utils.getEffectiveViewportSize(true, 800, 600); | 1136 var dimensions = utils.getEffectiveViewportSize(true, 800, 600); |
1311 // Set the svg sizes. | 1137 // Set the svg sizes. |
1312 svg.setAttribute('width', dimensions.width) | 1138 svg.setAttribute('width', dimensions.width) |
1313 .setAttribute('height', dimensions.height); | 1139 .setAttribute('height', dimensions.height); |
1314 | 1140 |
1315 // Set the internal rect's size. | 1141 // Set the internal rect's size. |
1316 svg.one('rect') | 1142 svg.one('rect') |
1317 .setAttribute('width', dimensions.width) | 1143 .setAttribute('width', dimensions.width) |
1318 .setAttribute('height', dimensions.height); | 1144 .setAttribute('height', dimensions.height); |
1319 canvas | 1145 canvas |
1320 .setStyle('height', dimensions.height) | 1146 .setStyle('height', dimensions.height) |
1321 .setStyle('width', dimensions.width); | 1147 .setStyle('width', dimensions.width); |
1322 | 1148 |
1323 // Reset the scale parameters | 1149 // Reset the scale parameters |
1324 this.xscale.domain([-dimensions.width / 2, dimensions.width / 2]) | 1150 topo.xScale.domain([-dimensions.width / 2, dimensions.width / 2]) |
1325 .range([0, dimensions.width]); | 1151 .range([0, dimensions.width]); |
1326 this.yscale.domain([-dimensions.height / 2, dimensions.height / 2]) | 1152 topo.yScale.domain([-dimensions.height / 2, dimensions.height / 2]) |
1327 .range([dimensions.height, 0]); | 1153 .range([dimensions.height, 0]); |
1328 | 1154 |
1329 this.width = dimensions.width; | 1155 topo.set('size', [dimensions.width, dimensions.height]); |
1330 this.height = dimensions.height; | 1156 topo.fire('afterPageSizeRecalculation'); |
1331 Y.fire('afterPageSizeRecalculation'); | |
1332 }, | 1157 }, |
1333 | 1158 |
1334 /* | 1159 /* |
1335 * Update the location of the active service panel | 1160 * Update the location of the active service panel |
1336 */ | 1161 */ |
1337 updateServiceMenuLocation: function() { | 1162 updateServiceMenuLocation: function() { |
1338 var container = this.get('container'), | 1163 var topo = this.get('component'), |
1339 cp = container.one('.environment-menu.active'), | 1164 container = this.get('container'), |
1340 service = this.get('active_service'), | 1165 cp = container.one('.environment-menu.active'), |
1341 tr = this.zoom.translate(), | 1166 service = this.get('active_service'), |
1342 z = this.zoom.scale(); | 1167 tr = topo.get('translate'), |
| 1168 z = topo.get('scale'); |
| 1169 |
1343 if (service && cp) { | 1170 if (service && cp) { |
1344 var cp_width = cp.getClientRect().width, | 1171 var cp_width = cp.getClientRect().width, |
1345 menu_left = service.x * z + service.w * z / 2 < | 1172 menu_left = service.x * z + service.w * z / 2 < |
1346 this.width * z / 2, | 1173 this.width * z / 2, |
1347 service_center = service.getRelativeCenter(); | 1174 service_center = service.getRelativeCenter(); |
1348 if (menu_left) { | 1175 if (menu_left) { |
1349 cp.removeClass('left') | 1176 cp.removeClass('left') |
1350 .addClass('right'); | 1177 .addClass('right'); |
1351 } else { | 1178 } else { |
1352 cp.removeClass('right') | 1179 cp.removeClass('right') |
(...skipping 15 matching lines...) Expand all Loading... |
1368 }, | 1195 }, |
1369 | 1196 |
1370 serviceMouseEnter: function(d, context) { | 1197 serviceMouseEnter: function(d, context) { |
1371 var rect = Y.one(this); | 1198 var rect = Y.one(this); |
1372 // Do not fire if this service isn't selectable. | 1199 // Do not fire if this service isn't selectable. |
1373 if (!utils.hasSVGClass(rect, 'selectable-service')) { | 1200 if (!utils.hasSVGClass(rect, 'selectable-service')) { |
1374 return; | 1201 return; |
1375 } | 1202 } |
1376 | 1203 |
1377 // Do not fire unless we're within the service box. | 1204 // Do not fire unless we're within the service box. |
1378 var container = context.get('container'), | 1205 var topo = context.get('component'), |
| 1206 container = context.get('container'), |
1379 mouse_coords = d3.mouse(container.one('svg').getDOMNode()); | 1207 mouse_coords = d3.mouse(container.one('svg').getDOMNode()); |
1380 if (!d.containsPoint(mouse_coords, context.zoom)) { | 1208 if (!d.containsPoint(mouse_coords, topo.zoom)) { |
1381 return; | 1209 return; |
1382 } | 1210 } |
1383 | 1211 |
1384 // Do not fire if we're on the same service. | 1212 // Do not fire if we're on the same service. |
1385 if (d === context.get('addRelationStart_service')) { | 1213 if (d === context.get('addRelationStart_service')) { |
1386 return; | 1214 return; |
1387 } | 1215 } |
1388 | 1216 |
1389 context.set('potential_drop_point_service', d); | 1217 context.set('potential_drop_point_service', d); |
1390 context.set('potential_drop_point_rect', rect); | 1218 context.set('potential_drop_point_rect', rect); |
(...skipping 15 matching lines...) Expand all Loading... |
1406 } | 1234 } |
1407 }, | 1235 }, |
1408 | 1236 |
1409 serviceMouseLeave: function(d, self) { | 1237 serviceMouseLeave: function(d, self) { |
1410 // Do not fire if we aren't looking for a relation endpoint. | 1238 // Do not fire if we aren't looking for a relation endpoint. |
1411 if (!self.get('potential_drop_point_rect')) { | 1239 if (!self.get('potential_drop_point_rect')) { |
1412 return; | 1240 return; |
1413 } | 1241 } |
1414 | 1242 |
1415 // Do not fire if we're within the service box. | 1243 // Do not fire if we're within the service box. |
1416 var container = self.get('container'), | 1244 var topo = this.get('component'), |
| 1245 container = self.get('container'), |
1417 mouse_coords = d3.mouse(container.one('svg').getDOMNode()); | 1246 mouse_coords = d3.mouse(container.one('svg').getDOMNode()); |
1418 if (d.containsPoint(mouse_coords, self.zoom)) { | 1247 if (d.containsPoint(mouse_coords, topo.zoom)) { |
1419 return; | 1248 return; |
1420 } | 1249 } |
1421 var rect = Y.one(this).one('.service-border'); | 1250 var rect = Y.one(this).one('.service-border'); |
1422 self.set('potential_drop_point_service', null); | 1251 self.set('potential_drop_point_service', null); |
1423 self.set('potential_drop_point_rect', null); | 1252 self.set('potential_drop_point_rect', null); |
1424 utils.removeSVGClass(rect, 'hover'); | 1253 utils.removeSVGClass(rect, 'hover'); |
1425 | 1254 |
1426 if (self.dragline) { | 1255 if (self.dragline) { |
1427 self.dragline.attr('class', | 1256 self.dragline.attr('class', |
1428 'relation pending-relation dragline dragging'); | 1257 'relation pending-relation dragline dragging'); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1479 view.set('active_context', context); | 1308 view.set('active_context', context); |
1480 cp.addClass('active'); | 1309 cp.addClass('active'); |
1481 view.updateServiceMenuLocation(); | 1310 view.updateServiceMenuLocation(); |
1482 } | 1311 } |
1483 }, | 1312 }, |
1484 | 1313 |
1485 /* | 1314 /* |
1486 * View a service | 1315 * View a service |
1487 */ | 1316 */ |
1488 show_service: function(m, context) { | 1317 show_service: function(m, context) { |
1489 context.get('component') | 1318 var topo = context.get('component'); |
1490 .fire('navigateTo', {url: '/service/' + m.get('id') + '/'}); | 1319 topo.detachContainer(); |
| 1320 topo.fire('navigateTo', {url: '/service/' + m.get('id') + '/'}); |
1491 }, | 1321 }, |
1492 | 1322 |
1493 /* | 1323 /* |
1494 * Show a dialog before destroying a service | 1324 * Show a dialog before destroying a service |
1495 */ | 1325 */ |
1496 destroyServiceConfirm: function(m, view) { | 1326 destroyServiceConfirm: function(m, view) { |
1497 // Set service in view. | 1327 // Set service in view. |
1498 view.set('destroy_service', m); | 1328 view.set('destroy_service', m); |
1499 | 1329 |
1500 // Show dialog. | 1330 // Show dialog. |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1560 view.startRelation(service); | 1390 view.startRelation(service); |
1561 // Store start service in attrs. | 1391 // Store start service in attrs. |
1562 view.set('addRelationStart_service', m); | 1392 view.set('addRelationStart_service', m); |
1563 }, | 1393 }, |
1564 | 1394 |
1565 /* | 1395 /* |
1566 * Test if the pending relation is ambiguous. Display a menu if so, | 1396 * Test if the pending relation is ambiguous. Display a menu if so, |
1567 * create the relation if not. | 1397 * create the relation if not. |
1568 */ | 1398 */ |
1569 ambiguousAddRelationCheck: function(m, view, context) { | 1399 ambiguousAddRelationCheck: function(m, view, context) { |
1570 var endpoints = view | 1400 var endpoints = view.get( |
1571 .get('addRelationStart_possibleEndpoints')[m.id], | 1401 'addRelationStart_possibleEndpoints')[m.id], |
1572 container = view.get('container'); | 1402 container = view.get('container'), |
| 1403 topo = view.get('component'); |
1573 | 1404 |
1574 if (endpoints.length === 1) { | 1405 if (endpoints && endpoints.length === 1) { |
1575 // Create a relation with the only available endpoint. | 1406 // Create a relation with the only available endpoint. |
1576 var ep = endpoints[0], | 1407 var ep = endpoints[0], |
1577 endpoints_item = [ | 1408 endpoints_item = [ |
1578 [ep[0].service, { | 1409 [ep[0].service, { |
1579 name: ep[0].name, | 1410 name: ep[0].name, |
1580 role: 'server' }], | 1411 role: 'server' }], |
1581 [ep[1].service, { | 1412 [ep[1].service, { |
1582 name: ep[1].name, | 1413 name: ep[1].name, |
1583 role: 'client' }]]; | 1414 role: 'client' }]]; |
1584 view.service_click_actions | 1415 view.service_click_actions |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1622 .addRelationEnd(endpoints_item, view, context); | 1453 .addRelationEnd(endpoints_item, view, context); |
1623 }); | 1454 }); |
1624 | 1455 |
1625 // Add a cancel item. | 1456 // Add a cancel item. |
1626 menu.one('.cancel').on('click', function(evt) { | 1457 menu.one('.cancel').on('click', function(evt) { |
1627 menu.removeClass('active'); | 1458 menu.removeClass('active'); |
1628 view.cancelRelationBuild(); | 1459 view.cancelRelationBuild(); |
1629 }); | 1460 }); |
1630 | 1461 |
1631 // Display the menu at the service endpoint. | 1462 // Display the menu at the service endpoint. |
1632 var tr = view.zoom.translate(), | 1463 var tr = topo.zoom.translate(), |
1633 z = view.zoom.scale(); | 1464 z = topo.zoom.scale(); |
1634 menu.setStyle('top', m.y * z + tr[1]); | 1465 menu.setStyle('top', m.y * z + tr[1]); |
1635 menu.setStyle('left', m.x * z + m.w * z + tr[0]); | 1466 menu.setStyle('left', m.x * z + m.w * z + tr[0]); |
1636 menu.addClass('active'); | 1467 menu.addClass('active'); |
1637 view.set('active_service', m); | 1468 view.set('active_service', m); |
1638 view.set('active_context', context); | 1469 view.set('active_context', context); |
1639 view.updateServiceMenuLocation(); | 1470 view.updateServiceMenuLocation(); |
1640 }, | 1471 }, |
1641 | 1472 |
1642 /* | 1473 /* |
1643 * Fired when clicking the second service is clicked in the | 1474 * Fired when clicking the second service is clicked in the |
1644 * add relation flow. | 1475 * add relation flow. |
1645 * | 1476 * |
1646 * :param endpoints: array of two endpoints, each in the form | 1477 * :param endpoints: array of two endpoints, each in the form |
1647 * ['service name', { | 1478 * ['service name', { |
1648 * name: 'endpoint type', | 1479 * name: 'endpoint type', |
1649 * role: 'client or server' | 1480 * role: 'client or server' |
1650 * }] | 1481 * }] |
1651 */ | 1482 */ |
1652 addRelationEnd: function(endpoints, view, context) { | 1483 addRelationEnd: function(endpoints, view, context) { |
1653 // Redisplay all services | 1484 // Redisplay all services |
1654 view.cancelRelationBuild(); | 1485 view.cancelRelationBuild(); |
1655 | 1486 |
1656 // Get the vis, and links, build the new relation. | 1487 // Get the vis, and links, build the new relation. |
1657 var vis = view.vis, | 1488 var vis = view.get('component').vis, |
1658 env = view.get('component').get('env'), | 1489 env = view.get('component').get('env'), |
1659 db = view.get('component').get('db'), | 1490 db = view.get('component').get('db'), |
1660 source = view.get('addRelationStart_service'), | 1491 source = view.get('addRelationStart_service'), |
1661 relation_id = 'pending:' + endpoints[0][0] + endpoints[1][0]; | 1492 relation_id = 'pending:' + endpoints[0][0] + endpoints[1][0]; |
1662 | 1493 |
1663 if (endpoints[0][0] === endpoints[1][0]) { | 1494 if (endpoints[0][0] === endpoints[1][0]) { |
1664 view.set('currentServiceClickAction', 'toggleControlPanel'); | 1495 view.set('currentServiceClickAction', 'toggleControlPanel'); |
1665 return; | 1496 return; |
1666 } | 1497 } |
1667 | 1498 |
1668 // Create a pending relation in the database between the | 1499 // Create a pending relation in the database between the |
1669 // two services. | 1500 // two services. |
1670 db.relations.create({ | 1501 db.relations.create({ |
1671 relation_id: relation_id, | 1502 relation_id: relation_id, |
1672 display_name: 'pending', | 1503 display_name: 'pending', |
1673 endpoints: endpoints, | 1504 endpoints: endpoints, |
1674 pending: true | 1505 pending: true |
1675 }); | 1506 }); |
1676 | 1507 |
1677 // Firing the update event on the db will properly redraw the | 1508 // Firing the update event on the db will properly redraw the |
1678 // graph and reattach events. | 1509 // graph and reattach events. |
1679 db.fire('update'); | 1510 //db.fire('update'); |
| 1511 view.get('component').bindAllD3Events(); |
| 1512 view.update(); |
1680 | 1513 |
1681 // Fire event to add relation in juju. | 1514 // Fire event to add relation in juju. |
1682 // This needs to specify interface in the future. | 1515 // This needs to specify interface in the future. |
1683 env.add_relation( | 1516 env.add_relation( |
1684 endpoints[0][0] + ':' + endpoints[0][1].name, | 1517 endpoints[0][0] + ':' + endpoints[0][1].name, |
1685 endpoints[1][0] + ':' + endpoints[1][1].name, | 1518 endpoints[1][0] + ':' + endpoints[1][1].name, |
1686 Y.bind(this._addRelationCallback, this, view, relation_id) | 1519 Y.bind(this._addRelationCallback, this, view, relation_id) |
1687 ); | 1520 ); |
1688 view.set('currentServiceClickAction', 'toggleControlPanel'); | 1521 view.set('currentServiceClickAction', 'toggleControlPanel'); |
1689 }, | 1522 }, |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1732 requires: [ | 1565 requires: [ |
1733 'd3', | 1566 'd3', |
1734 'd3-components', | 1567 'd3-components', |
1735 'juju-templates', | 1568 'juju-templates', |
1736 'node', | 1569 'node', |
1737 'event', | 1570 'event', |
1738 'juju-models', | 1571 'juju-models', |
1739 'juju-env' | 1572 'juju-env' |
1740 ] | 1573 ] |
1741 }); | 1574 }); |
OLD | NEW |