Merge lp:~bcsaller/juju-gui/service-click-actions-begone into lp:~bcsaller/juju-gui/viewmodel-improvements

Proposed by Benjamin Saller
Status: Merged
Merged at revision: 356
Proposed branch: lp:~bcsaller/juju-gui/service-click-actions-begone
Merge into: lp:~bcsaller/juju-gui/viewmodel-improvements
Diff against target: 1546 lines (+472/-467)
12 files modified
CHANGES.yaml (+7/-1)
Makefile (+7/-1)
app/app.js (+5/-1)
app/views/topology/service.js (+209/-237)
app/views/topology/topology.js (+3/-0)
app/views/utils.js (+17/-7)
docs/process.rst (+79/-63)
lib/server.js (+3/-5)
test/test_application_notifications.js (+1/-3)
test/test_environment_view.js (+5/-7)
test/test_service_module.js (+18/-24)
undocumented (+118/-118)
To merge this branch: bzr merge lp:~bcsaller/juju-gui/service-click-actions-begone
Reviewer Review Type Date Requested Status
Benjamin Saller Pending
Review via email: mp+145866@code.launchpad.net

Description of the change

Remove service click actions

This branch builds on the view model improvements to remove service click
actions, simplify the calling convention of methods that lived there and
remove their dependencies on the service_click_actions closure.

https://codereview.appspot.com/7228070/

To post a comment you must log in.
Revision history for this message
Benjamin Saller (bcsaller) wrote :

Reviewers: mp+145866_code.launchpad.net,

Message:
Please take a look.

Description:
Remove service click actions

This branch builds on the view model improvements to remove service
click
actions, simplify the calling convention of methods that lived there and

remove their dependencies on the service_click_actions closure.

https://code.launchpad.net/~bcsaller/juju-gui/service-click-actions-begone/+merge/145866

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/7228070/

Affected files:
   A [revision details]
   M app/views/topology/service.js
   M test/test_application_notifications.js
   M test/test_environment_view.js
   M test/test_service_module.js
   M undocumented

356. By Benjamin Saller

fix test

Revision history for this message
Benjamin Saller (bcsaller) wrote :
Revision history for this message
Nicola Larosa (teknico) wrote :

Land as is.

See also some comments below.

https://codereview.appspot.com/7228070/diff/3001/app/views/topology/service.js
File app/views/topology/service.js (right):

https://codereview.appspot.com/7228070/diff/3001/app/views/topology/service.js#newcode94
app/views/topology/service.js:94: // with this module as self and a box
as its argument.
Why is it called "d" if it's a box?

https://codereview.appspot.com/7228070/diff/3001/app/views/topology/service.js#newcode195
app/views/topology/service.js:195: self.showService(box);
And here we have both a "d" and a "box". Confusing.

https://codereview.appspot.com/7228070/diff/3001/app/views/topology/service.js#newcode203
app/views/topology/service.js:203: destroyServiceClick: function(data,
self) {
Oh, so "d" means "data".

https://codereview.appspot.com/7228070/diff/3001/app/views/topology/service.js#newcode906
app/views/topology/service.js:906: showService: function(d) {
The parameter is intended to be called "box", according to the comment
block right above, and it's probably a good idea.

https://codereview.appspot.com/7228070/diff/3001/test/test_application_notifications.js
File test/test_application_notifications.js (right):

https://codereview.appspot.com/7228070/diff/3001/test/test_application_notifications.js#newcode240
test/test_application_notifications.js:240: // The _destroyCallback args
are: service, view, confirm button, event.
Is this comment still valid?

https://codereview.appspot.com/7228070/

357. By Benjamin Saller

merge trunk with vm improvements

358. By Benjamin Saller

complete var rename

359. By Benjamin Saller

review change to comment

Revision history for this message
Benjamin Saller (bcsaller) wrote :
Revision history for this message
Madison Scott-Clary (makyo) wrote :

Two things, one comment in code.

Also encountered: attempting to destroy a service leads to the following
in juju: http://pastebin.ubuntu.com/1597784/ The WS then disconnects and
reconnects, and the dialog is left hanging out in the GUI. Confirmed
that this works in my branch, which I've yet to merge with anything.

https://codereview.appspot.com/7228070/diff/9001/app/views/topology/service.js
File app/views/topology/service.js (right):

https://codereview.appspot.com/7228070/diff/9001/app/views/topology/service.js#newcode342
app/views/topology/service.js:342: box.inDrag = 2;
Line above is still using service_click_actions, throwing an exception
when dragging a service (if it's not caught, relation lines don't move
until annotations come back and things are redrawn).

https://codereview.appspot.com/7228070/

Revision history for this message
Benjamin Saller (bcsaller) wrote :

On 2013/02/01 16:41:54, matthew.scott wrote:
> Two things, one comment in code.

> Also encountered: attempting to destroy a service leads to the
following in
> juju: http://pastebin.ubuntu.com/1597784/ The WS then disconnects and
> reconnects, and the dialog is left hanging out in the GUI. Confirmed
that this
> works in my branch, which I've yet to merge with anything.

https://codereview.appspot.com/7228070/diff/9001/app/views/topology/service.js
> File app/views/topology/service.js (right):

https://codereview.appspot.com/7228070/diff/9001/app/views/topology/service.js#newcode342
> app/views/topology/service.js:342: box.inDrag = 2;
> Line above is still using service_click_actions, throwing an exception
when
> dragging a service (if it's not caught, relation lines don't move
until
> annotations come back and things are redrawn).

I wasn't able to reproduce the destroy service issue. I'll resubmit this
with the other fix from the poor merge.

https://codereview.appspot.com/7228070/

Revision history for this message
Benjamin Saller (bcsaller) wrote :

https://codereview.appspot.com/7228070/diff/9001/app/views/topology/service.js
File app/views/topology/service.js (right):

https://codereview.appspot.com/7228070/diff/9001/app/views/topology/service.js#newcode342
app/views/topology/service.js:342: box.inDrag = 2;
On 2013/02/01 16:41:54, matthew.scott wrote:
> Line above is still using service_click_actions, throwing an exception
when
> dragging a service (if it's not caught, relation lines don't move
until
> annotations come back and things are redrawn).

ah, careless merge, thanks

https://codereview.appspot.com/7228070/

360. By Benjamin Saller

fix bad merge

Revision history for this message
Benjamin Saller (bcsaller) wrote :
Revision history for this message
Madison Scott-Clary (makyo) wrote :
Revision history for this message
Benjamin Saller (bcsaller) wrote :

*** Submitted:

Remove service click actions

This branch builds on the view model improvements to remove service
click
actions, simplify the calling convention of methods that lived there and

remove their dependencies on the service_click_actions closure.

R=teknico, matthew.scott
CC=
https://codereview.appspot.com/7228070

https://codereview.appspot.com/7228070/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CHANGES.yaml'
2--- CHANGES.yaml 2013-01-21 18:48:06 +0000
3+++ CHANGES.yaml 2013-02-04 14:37:21 +0000
4@@ -7,6 +7,7 @@
5 # this data in the GUI itself at some point. Here's an example of how to
6 # write the changes data.
7
8+# - unreleased:
9 # - 0.5.0:
10 # - Did something
11 # - Did something else
12@@ -20,7 +21,12 @@
13 # Release identifiers in this file should always be "unreleased" or
14 # [NUMBER].[NUMBER].[NUMBER] per http://semver.org/ .
15
16-- unrelased:
17+- 0.2.0:
18+ - Fix a number of bugs and UI misbehaviors.
19+ - Switch to a CSS minifier that does not require Java.
20+ - Remove login credentials from config.
21+ - Improve tests and testing infrastructure.
22+ - Improve project and code documentation.
23 - 0.1.5:
24 - Add support for recess as a CSS linter. Currently not enabled.
25 - Allow login credentials to be placed in config.
26
27=== modified file 'Makefile'
28--- Makefile 2013-01-30 17:48:54 +0000
29+++ Makefile 2013-02-04 14:37:21 +0000
30@@ -151,7 +151,7 @@
31 @echo "docs: generate project and code documentation"
32 @echo "view-docs: generate both doc sets and view them in the browser"
33 @echo "help: this description"
34- @echo "Other, less common targets are available, see Makefile."
35+ @echo "Other targets are available, see the Makefile."
36
37 build-shared/juju-ui/templates.js: $(TEMPLATE_TARGETS) bin/generateTemplates
38 mkdir -p build-shared/juju-ui/assets
39@@ -166,11 +166,17 @@
40 view-main-doc: main-doc
41 xdg-open docs/_build/html/index.html
42
43+sphinx:
44+ @echo "Deprecated. Please run 'make main-doc' or 'make view-main-doc'."
45+
46 code-doc: yuidoc/index.html
47
48 view-code-doc: code-doc
49 xdg-open yuidoc/index.html
50
51+yuidoc:
52+ @echo "Deprecated. Please run 'make code-doc' or 'make view-code-doc'."
53+
54 docs: code-doc main-doc
55
56 view-docs: docs
57
58=== modified file 'app/app.js'
59--- app/app.js 2013-01-31 02:59:16 +0000
60+++ app/app.js 2013-02-04 14:37:21 +0000
61@@ -365,9 +365,13 @@
62 });
63 }
64
65+ // Regardless of which view we are rendering
66+ // update the env view on db change.
67+ if (this.views.environment.instance) {
68+ this.views.environment.instance.topo.update();
69+ }
70 // Redispatch to current view to update.
71 if (active && active.name === 'EnvironmentView') {
72- active.update();
73 active.rendered();
74 } else {
75 this.dispatch();
76
77=== modified file 'app/views/topology/service.js'
78--- app/views/topology/service.js 2013-01-31 02:59:16 +0000
79+++ app/views/topology/service.js 2013-02-04 14:37:21 +0000
80@@ -52,9 +52,7 @@
81 dragstart: 'dragstart',
82 drag: 'drag',
83 dragend: 'dragend',
84- hideServiceMenu: {callback: function() {
85- this.service_click_actions.hideServiceMenu(null, this);
86- }},
87+ hideServiceMenu: 'hideServiceMenu',
88 rescaled: 'updateServiceMenuLocation'
89 }
90 },
91@@ -68,19 +66,16 @@
92 initializer: function(options) {
93 ServiceModule.superclass.constructor.apply(this, arguments);
94
95- // Build a service.id -> BoundingBox map for services.
96- this.service_boxes = {};
97-
98 // Set a default
99 this.set('currentServiceClickAction', 'toggleServiceMenu');
100 },
101
102- serviceClick: function(d, context) {
103+ serviceClick: function(box, self) {
104 // Ignore if we clicked outside the actual service node.
105- var topo = context.get('component');
106- var container = context.get('container');
107+ var topo = self.get('component');
108+ var container = self.get('container');
109 var mouse_coords = d3.mouse(container.one('svg').getDOMNode());
110- if (!d.containsPoint(mouse_coords, topo.zoom)) {
111+ if (!box.containsPoint(mouse_coords, topo.zoom)) {
112 return;
113 }
114 // serviceClick is being called after dragend is processed. In those
115@@ -90,26 +85,21 @@
116 return;
117 }
118 // Get the current click action
119- var curr_click_action = context.get('currentServiceClickAction');
120- // Fire the action named in the following scheme:
121- // service_click_action.<action>
122- // with the service, the SVG node, and the view
123- // as arguments.
124- context.service_click_actions[curr_click_action](
125- d, context, this);
126+ var curr_click_action = self.get('currentServiceClickAction');
127+ self[curr_click_action](box);
128 },
129
130- serviceDblClick: function(d, self) {
131+ serviceDblClick: function(box, self) {
132 // Just show the service on double-click.
133 var topo = self.get('component'),
134- service = d.model;
135+ service = box.model;
136 // The browser sends a click event right before the dblclick one, and it
137 // opens the service menu: close it before moving to the service details.
138- self.service_click_actions.hideServiceMenu(null, self);
139- self.service_click_actions.show_service(service, self);
140+ self.hideServiceMenu();
141+ self.showService(box);
142 },
143
144- serviceMouseEnter: function(d, context) {
145+ serviceMouseEnter: function(box, context) {
146 var rect = Y.one(this);
147 // Do not fire if this service isn't selectable.
148 if (!utils.hasSVGClass(rect, 'selectable-service')) {
149@@ -120,19 +110,19 @@
150 var topo = context.get('component');
151 var container = context.get('container');
152 var mouse_coords = d3.mouse(container.one('svg').getDOMNode());
153- if (!d.containsPoint(mouse_coords, topo.zoom)) {
154+ if (!box.containsPoint(mouse_coords, topo.zoom)) {
155 return;
156 }
157
158- topo.fire('snapToService', { service: d, rect: rect });
159+ topo.fire('snapToService', { service: box, rect: rect });
160 },
161
162- serviceMouseLeave: function(d, context) {
163+ serviceMouseLeave: function(box, context) {
164 // Do not fire if we're within the service box.
165 var topo = context.get('component');
166 var container = context.get('container');
167 var mouse_coords = d3.mouse(container.one('svg').getDOMNode());
168- if (d.containsPoint(mouse_coords, topo.zoom)) {
169+ if (box.containsPoint(mouse_coords, topo.zoom)) {
170 return;
171 }
172 var rect = Y.one(this).one('.service-border');
173@@ -149,7 +139,7 @@
174 * @param {object} context Unused.
175 * @return {undefined} Side effects only.
176 */
177- serviceMouseMove: function(d, context) {
178+ serviceMouseMove: function(box, context) {
179 var topo = context.get('component');
180 topo.fire('mouseMove');
181 },
182@@ -159,13 +149,13 @@
183 *
184 * @method serviceStatusMouseOver
185 **/
186- serviceStatusMouseOver: function(d, context) {
187+ serviceStatusMouseOver: function(box, context) {
188 d3.select(this)
189 .select('.unit-count')
190 .attr('class', 'unit-count show-count');
191 },
192
193- serviceStatusMouseOut: function(d, context) {
194+ serviceStatusMouseOut: function(box, context) {
195 d3.select(this)
196 .select('.unit-count')
197 .attr('class', 'unit-count hide-count');
198@@ -177,11 +167,11 @@
199 *
200 * @method canvasClick
201 */
202- canvasClick: function(d, self) {
203+ canvasClick: function(box, self) {
204 var container = self.get('container'),
205 topo = self.get('component');
206 container.all('.environment-menu.active').removeClass('active');
207- self.service_click_actions.hideServiceMenu(null, self);
208+ self.hideServiceMenu();
209 topo.fire('clearState');
210 },
211
212@@ -190,15 +180,12 @@
213 *
214 * @method viewServiceClick
215 */
216- viewServiceClick: function(d, context) {
217+ viewServiceClick: function(_, self) {
218 // Get the service element
219- var topo = context.get('component');
220+ var topo = self.get('component');
221 var box = topo.get('active_service');
222- var service = box.model;
223- context.service_click_actions
224- .hideServiceMenu(d, context);
225- context.service_click_actions
226- .show_service(service, context);
227+ self.hideServiceMenu();
228+ self.showService(box);
229 },
230
231 /**
232@@ -206,23 +193,21 @@
233 *
234 * @method destroyServiceClick
235 */
236- destroyServiceClick: function(data, context) {
237+ destroyServiceClick: function(_, self) {
238 // Get the service element
239- var topo = context.get('component');
240+ var topo = self.get('component');
241 var box = topo.get('active_service');
242 var service = box;
243- context.service_click_actions
244- .hideServiceMenu(box, context);
245- context.service_click_actions
246- .destroyServiceConfirm(service, context);
247+ self.hideServiceMenu(box);
248+ self.destroyServiceConfirm(box);
249 },
250
251- serviceAddRelMouseDown: function(d, context) {
252+ serviceAddRelMouseDown: function(box, self) {
253 var evt = d3.event;
254- var topo = context.get('component');
255- context.longClickTimer = Y.later(750, this, function(d, e) {
256+ var topo = self.get('component');
257+ self.longClickTimer = Y.later(750, this, function(d, e) {
258 // Provide some leeway for accidental dragging.
259- if ((Math.abs(d.x - d.oldX) + Math.abs(d.y - d.oldY)) /
260+ if ((Math.abs(box.x - box.oldX) + Math.abs(box.y - box.oldY)) /
261 2 > 5) {
262 return;
263 }
264@@ -232,14 +217,14 @@
265 d3.event = e;
266
267 // Start the process of adding a relation
268- topo.fire('addRelationDragStart', {service: d});
269- }, [d, evt], false);
270+ topo.fire('addRelationDragStart', {service: box});
271+ }, [box, evt], false);
272 },
273
274- serviceAddRelMouseUp: function(d, context) {
275+ serviceAddRelMouseUp: function(box, self) {
276 // Cancel the long-click timer if it exists.
277- if (context.longClickTimer) {
278- context.longClickTimer.cancel();
279+ if (self.longClickTimer) {
280+ self.longClickTimer.cancel();
281 }
282 },
283 /*
284@@ -253,51 +238,45 @@
285 var vis = topo.vis;
286 var db = topo.get('db');
287
288- views.toBoundingBoxes(this, db.services, this.service_boxes);
289- this.services = Y.Object.values(this.service_boxes);
290-
291- // XXX: containment breaking alias, do we need this?
292- topo.service_boxes = this.service_boxes;
293+ views.toBoundingBoxes(this, db.services, topo.service_boxes);
294
295 // Nodes are mapped by modelId tuples.
296 this.node = vis.selectAll('.service')
297- .data(this.services, function(d) {return d.modelId;});
298+ .data(Y.Object.values(topo.service_boxes),
299+ function(d) {return d.modelId;});
300 },
301
302 /**
303 * Handle drag events for a service.
304 *
305- * @param {object} svc A service object.
306- * @param {object} i Unused.
307+ * @param {Box} box A bounding box.
308+ * @param {Module} self Service Module.
309 * @return {undefined} Side effects only.
310 * @method dragstart
311 */
312- dragstart: function(d, self) {
313+ dragstart: function(box, self) {
314 var topo = self.get('component');
315- d.oldX = d.x;
316- d.oldY = d.y;
317- d.inDrag = 1;
318+ box.oldX = box.x;
319+ box.oldY = box.y;
320+ box.inDrag = 1;
321 },
322
323- dragend: function(d, self) {
324+ dragend: function(box, self) {
325 var topo = self.get('component');
326 if (topo.buildingRelation) {
327 topo.ignoreServiceClick = true;
328 topo.fire('addRelationDragEnd');
329- //d3.event.sourceEvent.preventDefault();
330 }
331 else {
332- if (!d.inDrag ||
333- (d.oldX === d.x &&
334- d.oldY === d.y)) {
335+ if (!box.inDrag ||
336+ (box.oldX === box.x &&
337+ box.oldY === box.y)) {
338 return;
339 }
340 topo.get('env').update_annotations(
341- d.id, {'gui.x': d.x, 'gui.y': d.y},
342+ box.id, {'gui.x': box.x, 'gui.y': box.y},
343 function() {
344- d.inDrag = false;
345- //self.drag.call(self.getServiceNode(d.id),
346- // d, self, {x: d.x, y: d.y}, false);
347+ box.inDrag = false;
348 });
349 }
350 },
351@@ -319,12 +298,12 @@
352 * [At the time of this writing useTransition works in practice but
353 * introduces a timing issue in the tests.]
354 **/
355- drag: function(d, self, pos, includeTransition) {
356+ drag: function(box, self, pos, includeTransition) {
357 var topo = self.get('component');
358 var selection = d3.select(this);
359
360 if (topo.buildingRelation) {
361- topo.fire('addRelationDrag', { box: d });
362+ topo.fire('addRelationDrag', { box: box });
363 return;
364 }
365 if (self.longClickTimer) {
366@@ -332,13 +311,13 @@
367 }
368 // Translate the service (and, potentially, menu).
369 if (pos) {
370- d.x = pos.x;
371- d.y = pos.y;
372+ box.x = pos.x;
373+ box.y = pos.y;
374 // Explicitly reassign data.
375- selection = selection.data([d]);
376+ selection = selection.data([box]);
377 } else {
378- d.x += d3.event.dx;
379- d.y += d3.event.dy;
380+ box.x += d3.event.dx;
381+ box.y += d3.event.dy;
382 }
383
384 if (includeTransition) {
385@@ -350,7 +329,7 @@
386 selection.attr('transform', function(d, i) {
387 return d.translateStr;
388 });
389- if (topo.get('active_service') === d) {
390+ if (topo.get('active_service') === box) {
391 self.updateServiceMenuLocation();
392 }
393
394@@ -358,13 +337,13 @@
395 self.get('container').all('.environment-menu.active')
396 .removeClass('active');
397
398- if (d.inDrag === 1) {
399- self.service_click_actions.hideServiceMenu(null, self);
400- d.inDrag = 2;
401+ if (box.inDrag === 1) {
402+ self.hideServiceMenu();
403+ box.inDrag = 2;
404 }
405 topo.fire('cancelRelationBuild');
406 // Update relation lines for just this service.
407- topo.fire('serviceMoved', { service: d });
408+ topo.fire('serviceMoved', { service: box });
409 },
410
411 /*
412@@ -414,9 +393,10 @@
413 // around we layout only new nodes. This has the side
414 // effect that service blocks can overlap and will
415 // be fixed later.
416- var new_services = this.services.filter(function(boundingBox) {
417- return !Y.Lang.isNumber(boundingBox.x);
418- });
419+ var new_services = Y.Object.values(topo.service_boxes)
420+ .filter(function(boundingBox) {
421+ return !Y.Lang.isNumber(boundingBox.x);
422+ });
423 if (new_services) {
424 this.tree.nodes({children: new_services});
425 }
426@@ -438,7 +418,7 @@
427 // Remove old nodes.
428 node.exit()
429 .each(function(d) {
430- delete self.service_boxes[d.id];
431+ delete topo.service_boxes[d.id];
432 })
433 .remove();
434 },
435@@ -821,15 +801,16 @@
436
437 if (service && cp) {
438 var cp_width = cp.getClientRect().width,
439- menu_left = service.x * z + service.w * z / 2 <
440- this.width * z / 2,
441- service_center = service.relativeCenter;
442+ menu_left = (service.x * z + service.w * z / 2 <
443+ topo.get('width') * z / 2),
444+ service_center = service.relativeCenter;
445+
446 if (menu_left) {
447 cp.removeClass('left')
448- .addClass('right');
449+ .addClass('right');
450 } else {
451 cp.removeClass('right')
452- .addClass('left');
453+ .addClass('left');
454 }
455 // Set the position of the div in the following way:
456 // top: aligned to the scaled/panned service minus the
457@@ -846,153 +827,144 @@
458 }
459 },
460
461- /*
462- * Actions to be called on clicking a service.
463- */
464- service_click_actions: {
465- /**
466- * Show (if hidden) or hide (if shown) the service menu.
467- *
468- * @method toggleServiceMenu
469- * @param {object} box The presentation state for the service.
470- * @param {object} view The environment view.
471- * @param {object} context The service context.
472- * @return {undefined} Side effects only.
473- */
474- toggleServiceMenu: function(box, view, context) {
475- var serviceMenu = view.get('container').one('#service-menu');
476-
477- if (serviceMenu.hasClass('active') || !box) {
478- this.hideServiceMenu(null, view);
479- } else {
480- this.showServiceMenu(box, view, context);
481- }
482- },
483-
484- /**
485- * Show the service menu.
486- *
487- * @method showServiceMenu
488- * @param {object} box The presentation state for the service.
489- * @param {object} module The service module..
490- * @param {object} context The service context.
491- * @return {undefined} Side effects only.
492- */
493- showServiceMenu: function(box, module, context) {
494- var serviceMenu = module.get('container').one('#service-menu');
495- var topo = module.get('component');
496- var service = box.model;
497-
498- if (box && !serviceMenu.hasClass('active')) {
499- topo.set('active_service', box);
500- topo.set('active_context', context);
501- serviceMenu.addClass('active');
502- // We do not want the user destroying the Juju GUI service.
503- if (utils.isGuiService(service)) {
504- serviceMenu.one('.destroy-service').addClass('disabled');
505- }
506- module.updateServiceMenuLocation();
507- }
508- },
509-
510- /**
511- * Hide the service menu.
512- *
513- * @method hideServiceMenu
514- * @param {object} box The presentation state for the service (unused).
515- * @param {object} module The service module.
516- * @param {object} context The service context (unused).
517- * @return {undefined} Side effects only.
518- */
519- hideServiceMenu: function(box, module, context) {
520- var serviceMenu = module.get('container').one('#service-menu');
521- var topo = module.get('component');
522-
523- if (serviceMenu.hasClass('active')) {
524- serviceMenu.removeClass('active');
525- topo.set('active_service', null);
526- topo.set('active_context', null);
527- // Most services can be destroyed via the GUI.
528- serviceMenu.one('.destroy-service').removeClass('disabled');
529- }
530- },
531-
532- /*
533- * View a service
534- *
535- * @method show_service
536- */
537- show_service: function(m, context) {
538- var topo = context.get('component');
539- topo.detachContainer();
540- topo.fire('navigateTo', {url: '/service/' + m.get('id') + '/'});
541- },
542-
543- /*
544- * Show a dialog before destroying a service
545- *
546- * @method destroyServiceConfirm
547- */
548- destroyServiceConfirm: function(m, view) {
549- // Set service in view.
550- view.set('destroy_service', m.model);
551-
552- // Show dialog.
553- view.set('destroy_dialog', views.createModalPanel(
554- 'Are you sure you want to destroy the service? ' +
555- 'This cannot be undone.',
556- '#destroy-modal-panel',
557- 'Destroy Service',
558- Y.bind(function(ev) {
559- ev.preventDefault();
560- var btn = ev.target;
561- btn.set('disabled', true);
562- view.service_click_actions
563- .destroyService(m, view, btn);
564- }, this)));
565- },
566-
567- /*
568- * Destroy a service.
569- *
570- * @method destroyService
571- */
572- destroyService: function(m, view, btn) {
573- var env = view.get('component').get('env'),
574- service = view.get('destroy_service');
575- env.destroy_service(service.get('id'),
576- Y.bind(this._destroyCallback, view,
577- service, view, btn));
578- },
579-
580- _destroyCallback: function(service, view, btn, ev) {
581- var getModelURL = view.get('component').get('getModelURL'),
582- db = view.get('component').get('db');
583- if (ev.err) {
584- db.notifications.add(
585- new models.Notification({
586- title: 'Error destroying service',
587- message: 'Service name: ' + ev.service_name,
588- level: 'error',
589- link: getModelURL(service),
590- modelId: service
591- }));
592- } else {
593- var relations = db.relations.get_relations_for_service(service);
594- Y.each(relations, function(relation) {
595- relation.destroy();
596- });
597- service.destroy();
598- db.fire('update');
599- }
600- view.get('destroy_dialog').hide();
601+ /**
602+ * Show (if hidden) or hide (if shown) the service menu.
603+ *
604+ * @method toggleServiceMenu
605+ * @param {object} box The presentation state for the service.
606+ * @return {undefined} Side effects only.
607+ */
608+ toggleServiceMenu: function(box) {
609+ var serviceMenu = this.get('container').one('#service-menu');
610+
611+ if (serviceMenu.hasClass('active') || !box) {
612+ this.hideServiceMenu();
613+ } else {
614+ this.showServiceMenu(box);
615+ }
616+ },
617+ /**
618+ * Show the service menu.
619+ *
620+ * @method showServiceMenu
621+ * @param {object} box The presentation state for the service.
622+ * @return {undefined} Side effects only.
623+ */
624+ showServiceMenu: function(box) {
625+ var serviceMenu = this.get('container').one('#service-menu');
626+ var topo = this.get('component');
627+ var service = box.model;
628+
629+ if (box && !serviceMenu.hasClass('active')) {
630+ topo.set('active_service', box);
631+ topo.set('active_context', box.node);
632+ serviceMenu.addClass('active');
633+ // We do not want the user destroying the Juju GUI service.
634+ if (utils.isGuiService(service)) {
635+ serviceMenu.one('.destroy-service').addClass('disabled');
636+ }
637+ this.updateServiceMenuLocation();
638+ }
639+ },
640+
641+ /**
642+ * Hide the service menu.
643+ *
644+ * @method hideServiceMenu
645+ * @return {undefined} Side effects only.
646+ */
647+ hideServiceMenu: function() {
648+ var serviceMenu = this.get('container').one('#service-menu');
649+ var topo = this.get('component');
650+
651+ if (serviceMenu.hasClass('active')) {
652+ serviceMenu.removeClass('active');
653+ topo.set('active_service', null);
654+ topo.set('active_context', null);
655+ // Most services can be destroyed via the GUI.
656+ serviceMenu.one('.destroy-service').removeClass('disabled');
657+ }
658+ },
659+
660+ /*
661+ * View a service
662+ *
663+ * @method showService
664+ * @param {Box} box Bound Box of service to navigate to.
665+ */
666+ showService: function(d) {
667+ var topo = this.get('component');
668+ topo.detachContainer();
669+ topo.fire('navigateTo', {url: '/service/' + d.id + '/'});
670+ },
671+
672+ /*
673+ * Show a dialog before destroying a service
674+ *
675+ * @method destroyServiceConfirm
676+ */
677+ destroyServiceConfirm: function(d) {
678+ // Set service in view.
679+ this.set('destroy_service', d.model);
680+
681+ // Show dialog.
682+ this.set('destroy_dialog', views.createModalPanel(
683+ 'Are you sure you want to destroy the service? ' +
684+ 'This cannot be undone.',
685+ '#destroy-modal-panel',
686+ 'Destroy Service',
687+ Y.bind(function(ev) {
688+ ev.preventDefault();
689+ var btn = ev.target;
690+ btn.set('disabled', true);
691+ this._activeButton = btn;
692+ this.destroyService(d, btn);
693+ }, this)));
694+ },
695+
696+ /*
697+ * Destroy a service.
698+ *
699+ * @method destroyService
700+ */
701+ destroyService: function(d, btn) {
702+ var env = this.get('component').get('env'),
703+ service = this.get('destroy_service');
704+ env.destroy_service(service.id,
705+ Y.bind(this._destroyCallback, this));
706+ },
707+
708+ _destroyCallback: function(ev) {
709+ var getModelURL = this.get('component').get('getModelURL'),
710+ service = this.get('destroy_service'),
711+ db = this.get('component').get('db');
712+
713+ if (ev.err) {
714+ db.notifications.add(new models.Notification({
715+ title: 'Error destroying service',
716+ message: 'Service name: ' + ev.service_name,
717+ level: 'error',
718+ link: getModelURL(service),
719+ modelId: service
720+ }));
721+ } else {
722+ var relations = db.relations.get_relations_for_service(service);
723+ Y.each(relations, function(relation) {
724+ relation.destroy();
725+ });
726+ service.destroy();
727+ db.fire('update');
728+ }
729+ this.get('destroy_dialog').hide();
730+ var btn = this._activeButton;
731+ if (btn) {
732 btn.set('disabled', false);
733+ this._activeButton = undefined;
734 }
735-
736 }
737+
738 }, {
739 ATTRS: {}
740-
741 });
742
743 views.ServiceModule = ServiceModule;
744
745=== modified file 'app/views/topology/topology.js'
746--- app/views/topology/topology.js 2013-01-30 12:38:03 +0000
747+++ app/views/topology/topology.js 2013-02-04 14:37:21 +0000
748@@ -32,6 +32,9 @@
749 maxZoom: 200
750 });
751
752+ // Build a service.id -> BoundingBox map for services.
753+ this.service_boxes = {};
754+
755 this._subscriptions = [];
756 },
757
758
759=== modified file 'app/views/utils.js'
760--- app/views/utils.js 2013-01-31 00:18:46 +0000
761+++ app/views/utils.js 2013-02-04 14:37:21 +0000
762@@ -607,14 +607,22 @@
763 };
764
765
766- /*
767- * Utility class that encapsulates Y.Models and keeps their position
768- * state within an svg canvas.
769+ /**
770+ * Utility object that encapsulates Y.Models and keeps their position
771+ * state within an SVG canvas.
772 *
773 * As a convenience attributes of the encapsulated model are exposed
774 * directly as attributes.
775- */
776+ *
777+ * @class BoundingBox
778+ * @param {Module} module Service module.
779+ * @param {Model} model Service model.
780+ **/
781+
782+ // Internal base object
783 var _box = {};
784+
785+ // Internal descriptor generator.
786 function positionProp(name) {
787 return {
788 get: function() {return this['_' + name];},
789@@ -625,6 +633,7 @@
790 };
791 }
792
793+ // Box Properties (and methods).
794 Object.defineProperties(_box, {
795 x: positionProp('x'),
796 y: positionProp('y'),
797@@ -841,7 +850,7 @@
798 * @param {Module} module Typically service module.
799 * @param {Model} model Model object.
800 * @return {BoundingBox} A Box model.
801- */
802+ **/
803 function BoundingBox(module, model) {
804 var b = Object.create(_box);
805 b.module = module;
806@@ -858,6 +867,7 @@
807 * and will be updated in place by merging changed attribute
808 * into the index.
809 *
810+ * @method toBoundingBoxes
811 * @param {ServiceModule} Module holding box canvas and context.
812 * @param {ModelList} services Service modellist.
813 * @param {Object} existing id:box mapping.
814@@ -877,7 +887,6 @@
815 };
816
817
818-
819 /**
820 * Decorate a relation with some related/derived data.
821 *
822@@ -886,7 +895,8 @@
823 * @param {Object} source The service from which the relation originates.
824 * @param {Object} target The service at which the relation terminates.
825 * @return {Object} An object with attributes matching the result of
826- * relation.getAttrs() plus "source", "target", and other convenience data.
827+ * relation.getAttrs() plus "source", "target",
828+ * and other convenience data.
829 */
830 views.DecoratedRelation = function(relation, source, target) {
831 var hasRelations = Y.Lang.isValue(relation.endpoints);
832
833=== modified file 'docs/process.rst'
834--- docs/process.rst 2013-01-25 15:44:49 +0000
835+++ docs/process.rst 2013-02-04 14:37:21 +0000
836@@ -89,29 +89,29 @@
837 - Run ``python improv.py -f sample.json`` in the ``rapi-rollup`` Juju branch,
838 and run ``make server`` with the ``juju-ui`` branch.
839
840- * Do not forget to clear the browser cache: ``index.html`` may be sticking
841+ - Do not forget to clear the browser cache: ``index.html`` may be sticking
842 around because of the cache.manifest.
843- * Verify that the browser reports no 404s and no Javascript errors in the
844+ - Verify that the browser reports no 404s and no Javascript errors in the
845 console.
846- * QA the changes if possible, exploring different use cases (and edge cases).
847- * Spend between 60 and 120 seconds exploring the entire app. Do different
848+ - QA the changes if possible, exploring different use cases (and edge cases).
849+ - Spend between 60 and 120 seconds exploring the entire app. Do different
850 things every time. Try to break the app, generally.
851
852 - [Once we support multiple browsers, try them all, at least briefly.]
853 - Review the diff, including notes from the above as appropriate.
854
855- * Make sure that new code has tests.
856- * Make sure user-facing changes are described in ``CHANGES.yaml``.
857- * Make sure you can understand the new code. Ask for clarification if
858+ - Make sure that new code has tests.
859+ - Make sure user-facing changes are described in ``CHANGES.yaml``.
860+ - Make sure you can understand the new code. Ask for clarification if
861 necessary.
862- * Consider the advice in this preferred `Software Architecture Cheat Sheet
863+ - Consider the advice in this preferred `Software Architecture Cheat Sheet
864 <http://gorban.org/post/32873465932/software-architecture-cheat-sheet>`_
865- * Make sure changes to a file correspond to the naming conventions and other
866+ - Make sure changes to a file correspond to the naming conventions and other
867 stylistic considerations local to that file, and within our project.
868- * Before you ask for a change, think and make sure you cannot compromise
869+ - Before you ask for a change, think and make sure you cannot compromise
870 reasonably with the coder. If there is a low importance disagreement, in
871 general the coder's position should win.
872- * Only make a rework request if absolutely necessary. They are very
873+ - Only make a rework request if absolutely necessary. They are very
874 expensive in time, money, and morale. One way to think about it is to
875 imagine filing a bug for the problem you see. Are you confident it is a
876 bug? If not, reconsider. If it would be a bug, how would you triage it?
877@@ -143,54 +143,66 @@
878 <https://launchpad.net/juju-gui/stable>. If the most recent version string
879 is ``unreleased``, do the following.
880
881- * Decide what the next version number should be (see `Semantic Versioning
882+ - Decide what the next version number should be (see `Semantic Versioning
883 <http://semver.org/>`_) and change ``unreleased`` to that value.
884- * Set a bzr tag for the release, e.g.: ``bzr tag 0.1.5``.
885- * Commit to the branch with this checkin message:
886+ - Set a bzr tag for the release, e.g.: ``bzr tag 0.1.5``.
887+ - Commit to the branch with this checkin message:
888 ``bzr commit -m 'Set version for release.'``
889- * Push the branch directly to the parent (``bzr push :parent`` should work).
890+ - Push the branch directly to the parent (``bzr push :parent`` should work).
891
892 - Run the tests and verify they pass: ``make test-prod`` and then
893 ``make test-debug``.
894 - Create the tarball: ``FINAL=1 make distfile``. The process will end by
895 reporting the name of the tarball it made.
896 - In an empty temporary directory somewhere else on your system, expand the
897- tarball: ``tar xvzf PATH_TO_TARBALL``
898-- While still in the directory where you extracted the tar file, change to the
899- ``build-prod`` directory and start a server: ``python -m SimpleHTTPServer
900- 8888``.
901+ tarball: ``tar xvzf PATH_TO_TARBALL``.
902+- Check that read and execute permissions for all are present on all files
903+ and directories, especially in the ``node_modules/`` directory.
904+- Ensure that the ``build-prod/juju-ui/version.js`` file contains a version
905+ string that combines the value in the branch's ``CHANGES.yaml`` with the
906+ branch's revno.
907+- While still in the directory where you extracted the tar file, run the
908+ command: ``NO_BZR=1 make prod``. Go to the URL shown in the terminal.
909 - In Chrome and Firefox, QA the application. At the very least, load the app,
910 open the charm panel, go to an inner page, and make sure there are no 404s
911- or Javascript errors in the console. We want a real QA script for the
912- future.
913+ or Javascript errors in the console. Ensure that the ``/juju-ui/version.js``
914+ URL shows the same version string as before. We want a real QA script for
915+ the future.
916+- Also do the same checks after running the command ``NO_BZR=1 make debug``.
917 - For now, we will assume you would like to verify the release on the
918 Launchpad staging server. As we become more confident with this process,
919 this step may become unnecessary. In the branch, run ``FINAL=1 make
920- dist``. This will step you through signing the tarball, connecting
921- to Launchpad, and uploading the release.
922+ dist``. This will step you through signing the tarball, connecting to
923+ Launchpad, and uploading the release.
924
925- * Note that you may need to ask the webops to turn off the two-factor
926- authentication on your Launchpad staging account in order for this to
927- work. Go to the ``#launchpad-ops`` channel on the Canonical IRC server and
928- ask something like "webops, could you disable 2FA on my staging account?"
929- * When Launchpad asks you what level of permissions to grant, assuming you
930+ - If you have two-factor authentication enabled on Launchpad, the staging
931+ server will ask for a one-time password: be sure to have your device
932+ available. (If you are a Canonical collaborator, you may try and ask the
933+ webops to turn off the two-factor authentication on your Launchpad staging
934+ account, but it may not be possible anyway. Go to the ``#u1-as`` channel
935+ on the Canonical IRC server and ask something like "webops, could you
936+ disable 2FA on my staging account?")
937+ - When Launchpad asks you what level of permissions to grant, assuming you
938 are running on your own computer that you manage securely, the easiest
939 thing to do is hopefully also reasonably safe: accept that the computer
940 may perform all actions, indefinitely.
941- * Go to <https://staging.launchpad.net/juju-gui/stable> and verify that you
942+ - Go to <https://staging.launchpad.net/juju-gui/stable> and verify that you
943 see a new release and a new download file.
944- * Download the file, expand it in a temporary directory, run ``python -m
945- SimpleHTTPServer 8888``, and do a quick double-check in the browser that
946- it is what you expect. Looking at ``juju-ui/version.js`` should also show
947- you the version you expect.
948- * This is a final release. Consider asking others to verify the package on
949- staging.
950+ - Download the file and compare it to the original tarball in the
951+ ``release/`` directory, verifying that they are identical (hint: use the
952+ ``cmp`` command).
953+ - This is a final release. Consider asking others to verify the package on
954+ staging. If there are problems, you will have to delete the release from
955+ staging, fix the problems and restart the process. However, *do not*
956+ reuse the version number that you deleted. A deleted release is dead,
957+ not invisible, and the next, fixed release should increase the revision
958+ number.
959
960 - Now it is time for the actual, real release. Head back to your branch and
961 run ``FINAL=1 PROD=1 make dist``. The computer will again walk you
962- through the process.
963+ through the process and upload the release, this time to production.
964
965- * Note that, one time per computer, you will again have to accept the
966+ - Note that, one time per computer, you will again have to accept the
967 Launchpadlib security token: In Launchpad, the staging site and the
968 production have fully separate databases, including authentication. What
969 is done in production will in many cases eventually be copied over to
970@@ -198,14 +210,13 @@
971
972 - Go to <https://launchpad.net/juju-gui/stable> and verify that you see
973 a new release and a new download file.
974-
975 - Set the version back to ``unreleased`` by doing the following.
976
977- * Restore ``- unreleased:`` as most recent version string in
978+ - Restore ``- unreleased:`` as most recent version string in
979 ``CHANGES.yaml``.
980- * Commit to the branch with this checkin message:
981+ - Commit to the branch with this checkin message:
982 ``bzr commit -m 'Set version back to unreleased.'``
983- * Push the branch directly to the parent (``bzr push :parent`` should work).
984+ - Push the branch directly to the parent (``bzr push :parent`` should work).
985
986 You are done!
987
988@@ -220,46 +231,51 @@
989 found on `Launchpad <https://launchpad.net/juju-gui/trunk>`_.
990 - Run the tests and verify they pass: ``make test-prod`` and then
991 ``make test-debug``.
992-- Create the tarball: ``make distfile``. It will end by reporting the name of
993- the tarball it made.
994+- Create the tarball: ``make distfile``. The process will end by reporting
995+ the name of the tarball it made.
996 - In an empty temporary directory somewhere else on your system, expand the
997 tarball: ``tar xvzf PATH_TO_TARBALL``.
998-- Looking at ``build-prod/juju-ui/version.js`` should show you a version string
999- that combines the value in the branch's ``CHANGES.yaml`` with the branch's
1000- revno.
1001-- While still in the directory where you extracted the tar file, change to the
1002- ``build-prod`` directory and start a server: ``python -m SimpleHTTPServer
1003- 8888``.
1004+- Check that read and execute permissions for all are present on all files
1005+ and directories, especially in the ``node_modules/`` directory.
1006+- Ensure that the ``build-prod/juju-ui/version.js`` file contains a version
1007+ string that combines the value in the branch's ``CHANGES.yaml`` with the
1008+ branch's revno.
1009+- While still in the directory where you extracted the tar file, run the
1010+ command: ``NO_BZR=1 make prod``. Go to the URL shown in the terminal.
1011 - In Chrome and Firefox, QA the application. At the very least, load the app,
1012 open the charm panel, go to an inner page, and make sure there are no 404s
1013- or Javascript errors in the console. We want a real QA script for the
1014- future.
1015+ or Javascript errors in the console. Ensure that the ``/juju-ui/version.js``
1016+ URL shows the same version string as before. We want a real QA script for
1017+ the future.
1018+- Also do the same checks after running the command ``NO_BZR=1 make debug``.
1019 - For now, we will assume you would like to verify the release on the
1020 Launchpad staging server. As we become more confident with this process,
1021 this step may become unnecessary. In the branch, run ``make dist``.
1022 This will step you through signing the tarball, connecting to
1023 Launchpad, and uploading the release.
1024
1025- * Note that you may need to ask the webops to turn off the two-factor
1026- authentication on your Launchpad staging account in order for this to
1027- work. Go to the ``#launchpad-ops`` channel on the Canonical IRC server and
1028- ask something like "webops, could you disable 2FA on my staging account?"
1029- * When Launchpad asks you what level of permissions to grant, assuming you
1030+ - If you have two-factor authentication enabled on Launchpad, the staging
1031+ server will ask for a one-time password: be sure to have your device
1032+ available. (If you are a Canonical collaborator, you may try and ask the
1033+ webops to turn off the two-factor authentication on your Launchpad staging
1034+ account, but it may not be possible anyway. Go to the ``#u1-as`` channel
1035+ on the Canonical IRC server and ask something like "webops, could you
1036+ disable 2FA on my staging account?")
1037+ - When Launchpad asks you what level of permissions to grant, assuming you
1038 are running on your own computer that you manage securely, the easiest
1039 thing to do is hopefully also reasonably safe: accept that the computer
1040 may perform all actions, indefinitely.
1041- * Go to <https://staging.launchpad.net/juju-gui/trunk> and verify that you
1042+ - Go to <https://staging.launchpad.net/juju-gui/trunk> and verify that you
1043 see a new release and a new download file.
1044- * Download the file, expand it in a temporary directory, run ``python -m
1045- SimpleHTTPServer 8888``, and do a quick double-check in the browser that
1046- it is what you expect. Looking at ``juju-ui/version.js`` should also show
1047- you the version you expect, as seen in the similar earlier step above.
1048+ - Download the file and compare it to the original tarball in the
1049+ ``release/`` directory, verifying that they are identical (hint: use the
1050+ ``cmp`` command).
1051
1052 - Now it is time for the actual, real release. Head back to your branch and
1053 run ``PROD=1 make dist``. The computer will again walk you through the
1054- process.
1055+ process and upload the release.
1056
1057- * Note that, one time per computer, you will again have to accept the
1058+ - Note that, one time per computer, you will again have to accept the
1059 Launchpadlib security token: In Launchpad, the staging site and the
1060 production have fully separate databases, including authentication. What
1061 is done in production will in many cases eventually be copied over to
1062
1063=== modified file 'lib/server.js'
1064--- lib/server.js 2012-12-20 21:59:21 +0000
1065+++ lib/server.js 2013-02-04 14:37:21 +0000
1066@@ -35,9 +35,6 @@
1067 console.log('Regenerating Views');
1068 });
1069
1070-// Handles requests to the root path ('/') my simply sending the 'shell' page
1071-// which creates the `Y.App` instance.
1072-
1073 server.get('/stats/', function(req, res) {
1074 res.json({
1075 uptime: process.uptime(),
1076@@ -61,8 +58,7 @@
1077 });
1078
1079 server.get('/juju-ui/:file', function(req, res) {
1080- var fileName = req.params.file;
1081- res.sendfile('build-shared/juju-ui/' + fileName);
1082+ res.sendfile('build-shared/juju-ui/' + req.params.file);
1083 });
1084
1085 server.get('/juju-ui/assets/combined-css/:file', function(req, res) {
1086@@ -73,6 +69,8 @@
1087 res.sendfile('app/favicon.ico');
1088 });
1089
1090+// Handles requests to the root ('/') and all other paths by
1091+// sending the 'shell' page that creates the `Y.App` instance.
1092 server.get('*', function(req, res) {
1093 res.sendfile('app/index.html');
1094 });
1095
1096=== modified file 'test/test_application_notifications.js'
1097--- test/test_application_notifications.js 2013-01-23 21:50:57 +0000
1098+++ test/test_application_notifications.js 2013-02-04 14:37:21 +0000
1099@@ -237,9 +237,7 @@
1100 var module = view.topo.modules.ServiceModule;
1101 // The callback hides the destroy dialog at the end of the process.
1102 module.set('destroy_dialog', {hide: NO_OP});
1103- // The _destroyCallback args are: service, view, confirm button, event.
1104- var args = [{}, module, {set: NO_OP}, ERR_EV];
1105- module.service_click_actions._destroyCallback.apply(module, args);
1106+ module._destroyCallback.call(module, ERR_EV);
1107 assert.equal(1, db.notifications.size());
1108 });
1109
1110
1111=== modified file 'test/test_environment_view.js'
1112--- test/test_environment_view.js 2013-01-31 02:59:16 +0000
1113+++ test/test_environment_view.js 2013-02-04 14:37:21 +0000
1114@@ -665,10 +665,8 @@
1115 var module = view.topo.modules.RelationModule;
1116 var sm = view.topo.modules.ServiceModule;
1117
1118- sm.service_click_actions.toggleServiceMenu(
1119- d3.select(service.getDOMNode()).datum(),
1120- sm,
1121- service);
1122+ sm.toggleServiceMenu(
1123+ d3.select(service.getDOMNode()).datum());
1124 // Mock an event object so that d3.mouse does not throw a NPE.
1125 d3.event = {};
1126 add_rel.simulate('click');
1127@@ -858,12 +856,12 @@
1128 // Use pos to set b2
1129 b2.pos = {x: 200, y: 300, w: 100, h: 200};
1130
1131- // update using property
1132+ // Update using property.
1133 b1.x = 100;
1134 b1.x.should.equal(100);
1135 b1.px.should.equal(0);
1136
1137- // update using pos
1138+ // Update using position.
1139 b2.pos = {x: 300};
1140 b2.x.should.equal(300);
1141 b2.px.should.equal(200);
1142@@ -875,7 +873,7 @@
1143
1144 b1.modelId.should.equal('service-mediawiki');
1145
1146- // properties of the model have mapped to the box
1147+ // Properties of the model have mapped to the box.
1148 b1.id.should.equal('mediawiki');
1149 b1.exposed.should.equal(true);
1150 });
1151
1152=== modified file 'test/test_service_module.js'
1153--- test/test_service_module.js 2013-01-31 16:54:09 +0000
1154+++ test/test_service_module.js 2013-02-04 14:37:21 +0000
1155@@ -77,7 +77,8 @@
1156 });
1157
1158 describe('service module events', function() {
1159- var db, juju, models, serviceModule, view, viewContainer, views, Y;
1160+ var db, juju, models, serviceModule, topo,
1161+ view, viewContainer, views, Y;
1162 before(function(done) {
1163 Y = YUI(GlobalConfig).use(['node',
1164 'juju-models',
1165@@ -105,7 +106,8 @@
1166 });
1167 view.render();
1168 view.rendered();
1169- serviceModule = view.topo.modules.ServiceModule;
1170+ topo = view.topo;
1171+ serviceModule = topo.modules.ServiceModule;
1172 });
1173
1174 afterEach(function() {
1175@@ -116,44 +118,37 @@
1176
1177 it('should toggle the service menu',
1178 function() {
1179- var box = serviceModule.service_boxes.haproxy;
1180+ var box = topo.service_boxes.haproxy;
1181 var menu = viewContainer.one('#service-menu');
1182 assert.isFalse(menu.hasClass('active'));
1183- serviceModule.service_click_actions.toggleServiceMenu(
1184- box, serviceModule, serviceModule);
1185+ serviceModule.toggleServiceMenu(box);
1186 assert(menu.hasClass('active'));
1187- serviceModule.service_click_actions.toggleServiceMenu(
1188- box, serviceModule, serviceModule);
1189+ serviceModule.toggleServiceMenu(box);
1190 assert.isFalse(menu.hasClass('active'));
1191 });
1192
1193 it('should show the service menu',
1194 function() {
1195- var box = serviceModule.service_boxes.haproxy;
1196+ var box = topo.service_boxes.haproxy;
1197 var menu = viewContainer.one('#service-menu');
1198 assert.isFalse(menu.hasClass('active'));
1199- serviceModule.service_click_actions.showServiceMenu(
1200- box, serviceModule, serviceModule);
1201+ serviceModule.showServiceMenu(box);
1202 assert(menu.hasClass('active'));
1203 // Check no-op.
1204- serviceModule.service_click_actions.showServiceMenu(
1205- box, serviceModule, serviceModule);
1206+ serviceModule.showServiceMenu(box);
1207 assert(menu.hasClass('active'));
1208 });
1209
1210 it('should hide the service menu',
1211 function() {
1212- var box = serviceModule.service_boxes.haproxy;
1213+ var box = topo.service_boxes.haproxy;
1214 var menu = viewContainer.one('#service-menu');
1215- serviceModule.service_click_actions.showServiceMenu(
1216- box, serviceModule, serviceModule);
1217+ serviceModule.showServiceMenu(box);
1218 assert(menu.hasClass('active'));
1219- serviceModule.service_click_actions.hideServiceMenu(
1220- null, serviceModule);
1221+ serviceModule.hideServiceMenu();
1222 assert.isFalse(menu.hasClass('active'));
1223 // Check no-op.
1224- serviceModule.service_click_actions.hideServiceMenu(
1225- null, serviceModule);
1226+ serviceModule.hideServiceMenu();
1227 assert.isFalse(menu.hasClass('active'));
1228 });
1229
1230@@ -161,7 +156,7 @@
1231 // Return the service menu.
1232 var clickService = function(service) {
1233 // Monkeypatch to avoid the click event handler bailing out early.
1234- serviceModule.service_boxes.haproxy.containsPoint = function() {
1235+ topo.service_boxes.haproxy.containsPoint = function() {
1236 return true;
1237 };
1238 // Click the service.
1239@@ -241,8 +236,7 @@
1240 var menu = view.get('container').one('#service-menu');
1241 view.topo.set('active_service', service);
1242
1243- serviceModule.service_click_actions
1244- .toggleServiceMenu(box, serviceModule, serviceModule);
1245+ serviceModule.toggleServiceMenu(box);
1246 menu.one('.destroy-service').hasClass('disabled').should.equal(true);
1247 });
1248
1249@@ -256,10 +250,10 @@
1250 { id: 'wordpress',
1251 containsPoint: function() { return true; }
1252 };
1253- serviceModule.service_click_actions.fake = function() {
1254+ serviceModule.fakeAction = function() {
1255 called = true;
1256 };
1257- serviceModule.set('currentServiceClickAction', 'fake');
1258+ serviceModule.set('currentServiceClickAction', 'fakeAction');
1259 topo.ignoreServiceClick = true;
1260 serviceModule.serviceClick(d, serviceModule);
1261 assert.isFalse(called);
1262
1263=== modified file 'undocumented'
1264--- undocumented 2013-01-31 00:02:54 +0000
1265+++ undocumented 2013-02-04 14:37:21 +0000
1266@@ -1,16 +1,16 @@
1267-app/store/env.js:73 "on_open"
1268-app/store/env.js:229 "get_service"
1269-app/store/env.js:115 "_dispatch_event"
1270-app/store/env.js:79 "on_message"
1271-app/store/env.js:404 "get_endpoints"
1272-app/store/env.js:110 "dispatch_result"
1273-app/store/env.js:309 "status"
1274-app/store/env.js:75 "on_close"
1275-app/store/env.js:57 "connect"
1276-app/store/env.js:34 "initializer"
1277-app/store/env.js:123 "_dispatch_rpc_result"
1278-app/store/env.js:52 "destructor"
1279-app/store/env.js:225 "get_charm"
1280+app/store/env.js:53 "destructor"
1281+app/store/env.js:230 "get_service"
1282+app/store/env.js:74 "on_open"
1283+app/store/env.js:124 "_dispatch_rpc_result"
1284+app/store/env.js:76 "on_close"
1285+app/store/env.js:116 "_dispatch_event"
1286+app/store/env.js:58 "connect"
1287+app/store/env.js:35 "initializer"
1288+app/store/env.js:311 "status"
1289+app/store/env.js:111 "dispatch_result"
1290+app/store/env.js:80 "on_message"
1291+app/store/env.js:226 "get_charm"
1292+app/store/env.js:406 "get_endpoints"
1293 app/store/charm.js:24 "find"
1294 app/store/charm.js:11 "success"
1295 app/store/charm.js:65 "_normalizeCharms"
1296@@ -25,69 +25,69 @@
1297 app/store/notifications.js:25 "message"
1298 app/store/notifications.js:129 "title"
1299 app/store/notifications.js:137 "message"
1300-app/views/utils.js:732 "scale"
1301+app/views/utils.js:818 "value"
1302 app/views/utils.js:166 "substitute"
1303+app/views/utils.js:621 "set"
1304 app/views/utils.js:251 "hasSVGClass"
1305-app/views/utils.js:637 "get"
1306 app/views/utils.js:296 "toggleSVGClass"
1307-app/views/utils.js:784 "value"
1308-app/views/utils.js:698 "get"
1309+app/views/utils.js:710 "get"
1310 app/views/utils.js:135 "noop"
1311+app/views/utils.js:791 "value"
1312 app/views/utils.js:259 "addSVGClass"
1313+app/views/utils.js:642 "get"
1314+app/views/utils.js:728 "scale"
1315+app/views/utils.js:646 "get"
1316 app/views/utils.js:577 "isFloat"
1317 app/views/utils.js:218 "renderable_charm"
1318+app/views/utils.js:726 "value"
1319 app/views/utils.js:138 "console"
1320-app/views/utils.js:730 "value"
1321 app/views/utils.js:113 "noop"
1322 app/views/utils.js:280 "removeSVGClass"
1323-app/views/utils.js:714 "get"
1324 app/views/utils.js:371 "_addAlertMessage"
1325 app/views/utils.js:228 "humanizeNumber"
1326+app/views/utils.js:635 "get"
1327 app/views/utils.js:339 "action"
1328+app/views/utils.js:658 "get"
1329 app/views/utils.js:618 "positionProp"
1330+app/views/utils.js:729 "translate"
1331+app/views/utils.js:636 "set"
1332 app/views/utils.js:195 "bindModelView"
1333 app/views/utils.js:667 "get"
1334-app/views/utils.js:644 "get"
1335-app/views/utils.js:622 "set"
1336+app/views/utils.js:620 "get"
1337 app/views/utils.js:573 "isInt"
1338-app/views/utils.js:638 "set"
1339-app/views/utils.js:822 "value"
1340 app/views/utils.js:670 "get"
1341-app/views/utils.js:673 "get"
1342-app/views/utils.js:749 "get"
1343-app/views/utils.js:733 "translate"
1344-app/views/utils.js:681 "get"
1345-app/views/utils.js:621 "get"
1346+app/views/utils.js:677 "get"
1347+app/views/utils.js:650 "set"
1348+app/views/utils.js:694 "get"
1349 app/views/utils.js:419 "invokeCallback"
1350 app/views/utils.js:661 "get"
1351-app/views/utils.js:653 "set"
1352 app/views/utils.js:664 "get"
1353-app/views/utils.js:649 "get"
1354 app/views/utils.js:132 "native"
1355-app/views/utils.js:795 "value"
1356+app/views/utils.js:745 "get"
1357+app/views/utils.js:780 "value"
1358 app/views/environment.js:24 "initializer"
1359-app/views/charm-panel.js:1168 "calculatePanelPosition"
1360-app/views/charm-panel.js:476 "initializer"
1361-app/views/charm-panel.js:250 "render"
1362-app/views/charm-panel.js:901 "onCharmDeployClicked"
1363-app/views/charm-panel.js:976 "createInstance"
1364-app/views/charm-panel.js:764 "hideDescription"
1365-app/views/charm-panel.js:1022 "setPanel"
1366-app/views/charm-panel.js:332 "showConfiguration"
1367-app/views/charm-panel.js:1151 "updatePanelPosition"
1368-app/views/charm-panel.js:1213 "killInstance"
1369-app/views/charm-panel.js:655 "render"
1370-app/views/charm-panel.js:1198 "setDefaultSeries"
1371-app/views/charm-panel.js:195 "mouseleave"
1372-app/views/charm-panel.js:417 "_showErrors"
1373-app/views/charm-panel.js:651 "initializer"
1374-app/views/charm-panel.js:480 "render"
1375-app/views/charm-panel.js:751 "showDescription"
1376-app/views/charm-panel.js:725 "_moveTooltip"
1377-app/views/charm-panel.js:209 "initializer"
1378-app/views/charm-panel.js:964 "setupOverlay"
1379-app/views/charm-panel.js:192 "mouseenter"
1380-app/views/charm-panel.js:1207 "getInstance"
1381+app/views/charm-panel.js:1209 "setDefaultSeries"
1382+app/views/charm-panel.js:196 "mouseleave"
1383+app/views/charm-panel.js:762 "showDescription"
1384+app/views/charm-panel.js:975 "setupOverlay"
1385+app/views/charm-panel.js:422 "_showErrors"
1386+app/views/charm-panel.js:987 "createInstance"
1387+app/views/charm-panel.js:251 "render"
1388+app/views/charm-panel.js:1162 "updatePanelPosition"
1389+app/views/charm-panel.js:193 "mouseenter"
1390+app/views/charm-panel.js:1224 "killInstance"
1391+app/views/charm-panel.js:736 "_moveTooltip"
1392+app/views/charm-panel.js:660 "initializer"
1393+app/views/charm-panel.js:1218 "getInstance"
1394+app/views/charm-panel.js:210 "initializer"
1395+app/views/charm-panel.js:1179 "calculatePanelPosition"
1396+app/views/charm-panel.js:333 "showConfiguration"
1397+app/views/charm-panel.js:485 "render"
1398+app/views/charm-panel.js:481 "initializer"
1399+app/views/charm-panel.js:664 "render"
1400+app/views/charm-panel.js:912 "onCharmDeployClicked"
1401+app/views/charm-panel.js:1033 "setPanel"
1402+app/views/charm-panel.js:775 "hideDescription"
1403 app/views/charm.js:32 "render"
1404 app/views/charm.js:161 "on_results_change"
1405 app/views/charm.js:118 "render"
1406@@ -154,65 +154,65 @@
1407 app/views/topology/panzoom.js:123 "_fire_zoom"
1408 app/views/topology/panzoom.js:105 "zoom_out"
1409 app/views/topology/panzoom.js:33 "componentBound"
1410-app/views/topology/relation.js:836 "relationClick"
1411-app/views/topology/relation.js:517 "cancelRelationBuild"
1412-app/views/topology/relation.js:429 "addRelationDragEnd"
1413-app/views/topology/relation.js:55 "initializer"
1414-app/views/topology/relation.js:593 "addRelationStart"
1415-app/views/topology/relation.js:367 "snapOutOfService"
1416-app/views/topology/relation.js:65 "update"
1417-app/views/topology/relation.js:274 "addRelButtonClicked"
1418-app/views/topology/relation.js:237 "drawRelation"
1419-app/views/topology/relation.js:84 "renderedHandler"
1420-app/views/topology/relation.js:460 "_removeRelationCallback"
1421-app/views/topology/relation.js:295 "addRelation"
1422-app/views/topology/relation.js:174 "drawRelationGroup"
1423-app/views/topology/relation.js:60 "render"
1424-app/views/topology/relation.js:689 "addRelationEnd"
1425-app/views/topology/relation.js:267 "draglineClicked"
1426-app/views/topology/relation.js:485 "removeRelationConfirm"
1427-app/views/topology/relation.js:409 "addRelationDrag"
1428-app/views/topology/relation.js:88 "processRelation"
1429-app/views/topology/relation.js:772 "subordinateRelationsForService"
1430-app/views/topology/relation.js:728 "_addRelationCallback"
1431-app/views/topology/relation.js:605 "ambiguousAddRelationCheck"
1432-app/views/topology/relation.js:252 "updateSubordinateRelationsCount"
1433-app/views/topology/relation.js:338 "snapToService"
1434-app/views/topology/relation.js:779 "subRelBlockMouseEnter"
1435-app/views/topology/relation.js:383 "addRelationDragStart"
1436-app/views/topology/relation.js:128 "updateLinks"
1437-app/views/topology/relation.js:448 "removeRelation"
1438-app/views/topology/relation.js:788 "subRelBlockMouseLeave"
1439-app/views/topology/service.js:893 "show_service"
1440-app/views/topology/service.js:112 "serviceMouseEnter"
1441-app/views/topology/service.js:731 "hide"
1442-app/views/topology/service.js:208 "serviceAddRelMouseDown"
1443-app/views/topology/service.js:130 "serviceMouseLeave"
1444-app/views/topology/service.js:737 "fade"
1445-app/views/topology/service.js:166 "serviceStatusMouseOut"
1446-app/views/topology/service.js:78 "serviceClick"
1447-app/views/topology/service.js:68 "initializer"
1448-app/views/topology/service.js:267 "dragend"
1449-app/views/topology/service.js:227 "serviceAddRelMouseUp"
1450-app/views/topology/service.js:784 "updateServiceMenuLocation"
1451-app/views/topology/service.js:725 "show"
1452-app/views/topology/service.js:102 "serviceDblClick"
1453-app/views/topology/service.js:924 "destroyService"
1454-app/views/topology/service.js:359 "update"
1455-app/views/topology/service.js:902 "destroyServiceConfirm"
1456-app/views/topology/service.js:236 "updateData"
1457-app/views/topology/service.js:932 "_destroyCallback"
1458-app/views/topology/service.js:756 "renderedHandler"
1459-app/views/topology/topology.js:163 "getter"
1460-app/views/topology/topology.js:172 "setter"
1461-app/views/topology/topology.js:159 "getter"
1462-app/views/topology/topology.js:109 "computeScales"
1463-app/views/topology/topology.js:176 "getter"
1464-app/views/topology/topology.js:171 "getter"
1465-app/views/topology/topology.js:139 "serviceForBox"
1466-app/views/topology/topology.js:177 "setter"
1467-app/views/topology/topology.js:26 "initializer"
1468-app/views/topology/topology.js:63 "renderOnce"
1469+app/views/topology/relation.js:610 "ambiguousAddRelationCheck"
1470+app/views/topology/relation.js:522 "cancelRelationBuild"
1471+app/views/topology/relation.js:260 "updateSubordinateRelationsCount"
1472+app/views/topology/relation.js:694 "addRelationEnd"
1473+app/views/topology/relation.js:343 "snapToService"
1474+app/views/topology/relation.js:372 "snapOutOfService"
1475+app/views/topology/relation.js:490 "removeRelationConfirm"
1476+app/views/topology/relation.js:453 "removeRelation"
1477+app/views/topology/relation.js:73 "update"
1478+app/views/topology/relation.js:68 "render"
1479+app/views/topology/relation.js:92 "renderedHandler"
1480+app/views/topology/relation.js:434 "addRelationDragEnd"
1481+app/views/topology/relation.js:300 "addRelation"
1482+app/views/topology/relation.js:63 "initializer"
1483+app/views/topology/relation.js:843 "relationClick"
1484+app/views/topology/relation.js:777 "subordinateRelationsForService"
1485+app/views/topology/relation.js:794 "subRelBlockMouseLeave"
1486+app/views/topology/relation.js:598 "addRelationStart"
1487+app/views/topology/relation.js:136 "updateLinks"
1488+app/views/topology/relation.js:414 "addRelationDrag"
1489+app/views/topology/relation.js:733 "_addRelationCallback"
1490+app/views/topology/relation.js:96 "processRelation"
1491+app/views/topology/relation.js:182 "drawRelationGroup"
1492+app/views/topology/relation.js:785 "subRelBlockMouseEnter"
1493+app/views/topology/relation.js:245 "drawRelation"
1494+app/views/topology/relation.js:272 "draglineClicked"
1495+app/views/topology/relation.js:279 "addRelButtonClicked"
1496+app/views/topology/relation.js:388 "addRelationDragStart"
1497+app/views/topology/relation.js:465 "_removeRelationCallback"
1498+app/views/topology/service.js:231 "serviceAddRelMouseUp"
1499+app/views/topology/service.js:917 "destroyServiceConfirm"
1500+app/views/topology/service.js:164 "serviceStatusMouseOut"
1501+app/views/topology/service.js:126 "serviceMouseLeave"
1502+app/views/topology/service.js:948 "_destroyCallback"
1503+app/views/topology/service.js:745 "show"
1504+app/views/topology/service.js:76 "serviceClick"
1505+app/views/topology/service.js:98 "serviceDblClick"
1506+app/views/topology/service.js:242 "updateData"
1507+app/views/topology/service.js:66 "initializer"
1508+app/views/topology/service.js:274 "dragend"
1509+app/views/topology/service.js:751 "hide"
1510+app/views/topology/service.js:757 "fade"
1511+app/views/topology/service.js:108 "serviceMouseEnter"
1512+app/views/topology/service.js:368 "update"
1513+app/views/topology/service.js:778 "renderedHandler"
1514+app/views/topology/service.js:906 "showService"
1515+app/views/topology/service.js:806 "updateServiceMenuLocation"
1516+app/views/topology/service.js:941 "destroyService"
1517+app/views/topology/service.js:212 "serviceAddRelMouseDown"
1518+app/views/topology/topology.js:28 "initializer"
1519+app/views/topology/topology.js:178 "getter"
1520+app/views/topology/topology.js:174 "setter"
1521+app/views/topology/topology.js:165 "getter"
1522+app/views/topology/topology.js:141 "serviceForBox"
1523+app/views/topology/topology.js:179 "setter"
1524+app/views/topology/topology.js:111 "computeScales"
1525+app/views/topology/topology.js:161 "getter"
1526+app/views/topology/topology.js:173 "getter"
1527+app/views/topology/topology.js:65 "renderOnce"
1528 app/models/models.js:357 "getNotificationsForModel"
1529 app/models/models.js:429 "getModelListByModelName"
1530 app/models/models.js:420 "getModelById"
1531@@ -239,9 +239,9 @@
1532 app/models/models.js:338 "trim"
1533 app/models/endpoints.js:42 "add"
1534 app/models/endpoints.js:30 "convert"
1535-app/models/charm.js:157 "validator"
1536-app/models/charm.js:115 "parse"
1537-app/models/charm.js:79 "sync"
1538-app/models/charm.js:131 "compare"
1539-app/models/charm.js:107 "failure"
1540-app/models/charm.js:50 "initializer"
1541+app/models/charm.js:119 "parse"
1542+app/models/charm.js:135 "compare"
1543+app/models/charm.js:111 "failure"
1544+app/models/charm.js:54 "initializer"
1545+app/models/charm.js:83 "sync"
1546+app/models/charm.js:161 "validator"

Subscribers

People subscribed via source and target branches

to all changes: