Merge lp:~bcsaller/juju-gui/persistent-layout into lp:juju-gui/experimental
- persistent-layout
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 332 |
Proposed branch: | lp:~bcsaller/juju-gui/persistent-layout |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
1142 lines (+498/-315) (has conflicts) 9 files modified
app/app.js (+6/-1) app/models/models.js (+0/-1) app/store/notifications.js (+12/-13) app/views/environment.js (+21/-0) app/views/topology/service.js (+249/-146) test/test_environment_view.js (+44/-5) test/test_service_module.js (+2/-2) test/test_topology.js (+14/-2) undocumented (+150/-145) Text conflict in test/test_topology.js |
To merge this branch: | bzr merge lp:~bcsaller/juju-gui/persistent-layout |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email: mp+143980@code.launchpad.net |
Commit message
Description of the change
Support for service positions from server
Provides support for loading initial and updated position
from annotations stored on the server. Updates occur
via a shared codepath with the drag event handler
to ensure proper updates are applied over time.
There is still an issue with deltas interrupting the drag.
Benjamin Saller (bcsaller) wrote : | # |
Gary Poster (gary) wrote : | # |
Hey Ben. Thank you for getting this in.
The branch has some conflicts. They don't look too bad, but they will take at least a bit of investigation. I might have a chance to try and resolve them Sunday so we can expedite the review.
Thanks
Gary
On Jan 19, 2013, at 2:13 AM, Benjamin Saller <email address hidden> wrote:
> Reviewers: mp+143980_
>
> Message:
> Please take a look.
>
> Description:
> Support for service positions from server
>
> Provides support for loading initial and updated position
> from annotations stored on the server. Updates occur
> via a shared codepath with the drag event handler
> to ensure proper updates are applied over time.
>
> https:/
>
> (do not edit description out of merge proposal)
>
>
> Please review this at https:/
>
> Affected files:
> A [revision details]
> M app/app.js
> M app/models/
> M app/views/
> M app/views/
> M test/test_
> M test/test_
> M test/test_
> M undocumented
>
>
>
> --
> https:/
> Your team Juju GUI Hackers is requested to review the proposed merge of lp:~bcsaller/juju-gui/persistent-layout into lp:juju-gui.
- 333. By Benjamin Saller
-
merge trunk
- 334. By Benjamin Saller
-
Don't fear the linter
Benjamin Saller (bcsaller) wrote : | # |
Please take a look.
Benjamin Saller (bcsaller) wrote : | # |
Thanks for taking a peek, I resolved the merge and proposed again.
Benjamin Saller (bcsaller) wrote : | # |
Please take a look.
Gary Poster (gary) wrote : | # |
Hi Ben. Thank you for this branch. Land with changes (specifically
reinstating some of Nicola's changes that you lost, as mentioned below).
Tests pass for me.
The functionality works well, except for variants of the pre-existing
bug 1099921: moving services while the delta stream is being processed
leads to unexpected and undesired behavior. Either the drag is stopped,
or the drag completes but the processed location then moves the service
to where it was before. Hopefully we can get that addressed soon.
If it helps move things along in terms of Monday/Tuesday delivery, I
would be OK with you landing this and then responding to the
second/subsequent reviews in a separate branch. OTOH, if it doesn't
help you, don't do it. :-)
Thanks
Gary
https:/
File app/app.js (right):
https:/
app/app.js:351: active.update();
cool
https:/
File app/views/
https:/
app/views/
Moving the functions out of the event registration does make it easier
to read. However, it makes the diff much harder to read. For instance,
AFAICT, there are no changes to these functions, but I'm coming to that
conclusion by eyeball rather than by mechanical diff. This would be the
kind of change I'd like to see in a separate branch to keep the branch
size down, to reduce the development time, and to ease reviews of the
pertinent code changes to actually implement this feature.
https:/
app/views/
We now have this comment both in the event registration and in the
function definition. I'm not sure that inline event handler functions
don't have their place, to be honest. If we do separate them, I think
we can only expect to maintain comments in one place. I believe that
the place should be the function, so I would recommend deleting this
copy of the comment.
https:/
app/views/
function() {
I prefer the change that Nicola made, that you have reverted. In that
change, he renamed "ControlPanel" to "ServiceMenu," which seemed like a
naming improvement to Nicola and to his two reviewers. He also was able
to be explicit that we only needed to "hide" here, which would be nice
if we can manage it.
https:/
app/views/
self.service_
You've reverted Nicola's changes. I think they were improvements, but
even if you disagree, socially a discussion would be better. Please
don't do this.
https:/
app/views/
Benjamin Saller (bcsaller) wrote : | # |
Thanks for the review, I'll attempt to address the issues today.
Francesco Banconi (frankban) wrote : | # |
Hi Ben, thanks for this branch.
I have not so much to add to Gary's review. Please see just the one
comment below.
https:/
File app/views/
https:/
app/views/
d});
Isn't serviceMoved already fired by self.drag?
- 335. By Benjamin Saller
-
review changes
Benjamin Saller (bcsaller) wrote : | # |
https:/
File app/app.js (right):
https:/
app/app.js:351: active.update();
On 2013/01/20 15:58:45, gary.poster wrote:
> cool
Thanks, cutting to this should actually be a big improvement. I was
worried there would still be more work left before this would work
properly but it seems fine like this. This cuts the number of render
passes substantially and reduces the amount of work we do quite a lot.
https:/
File app/views/
https:/
app/views/
On 2013/01/20 15:58:45, gary.poster wrote:
> We now have this comment both in the event registration and in the
function
> definition. I'm not sure that inline event handler functions don't
have their
> place, to be honest. If we do separate them, I think we can only
expect to
> maintain comments in one place. I believe that the place should be
the
> function, so I would recommend deleting this copy of the comment.
Agreed with comment placement, this was the result of trying to quickly
cut a smaller branch, bad porting.
https:/
app/views/
function() {
On 2013/01/20 15:58:45, gary.poster wrote:
> I prefer the change that Nicola made, that you have reverted. In that
change,
> he renamed "ControlPanel" to "ServiceMenu," which seemed like a naming
> improvement to Nicola and to his two reviewers. He also was able to
be explicit
> that we only needed to "hide" here, which would be nice if we can
manage it.
Not intentional, this is how the trunk merge fell out. I'll restore his
version (which I had a hand in as well and agreed with).
https:/
app/views/
self.service_
On 2013/01/20 15:58:45, gary.poster wrote:
> You've reverted Nicola's changes. I think they were improvements, but
even if
> you disagree, socially a discussion would be better. Please don't do
this.
Bad Merge, fixed.
https:/
app/views/
On 2013/01/20 15:58:45, gary.poster wrote:
> You've reverted Nicola's changes. Please don't do this.
Same
https:/
File test/test_
https:/
test/test_
Y.clone(
This change may be worth of review comment. This is needed because we
delete annotations once their x/y has been read and applied. This
creates an issue in testing because its a reference all the way back to
the original test data stub.
Benjamin Saller (bcsaller) wrote : | # |
*** Submitted:
Support for service positions from server
Provides support for loading initial and updated position
from annotations stored on the server. Updates occur
via a shared codepath with the drag event handler
to ensure proper updates are applied over time.
There is still an issue with deltas interrupting the drag.
R=gary.poster, frankban
CC=
https:/
Preview Diff
1 | === modified file 'app/app.js' |
2 | --- app/app.js 2013-01-18 18:54:21 +0000 |
3 | +++ app/app.js 2013-01-21 15:11:21 +0000 |
4 | @@ -325,6 +325,7 @@ |
5 | Y.log(evt, 'debug', 'App: Database changed'); |
6 | |
7 | var self = this; |
8 | + var active = this.get('activeView'); |
9 | |
10 | // Compare endpoints map against db to see if it needs to be changed. |
11 | var updateNeeded = this.db.services.some(function(service) { |
12 | @@ -346,7 +347,11 @@ |
13 | } |
14 | |
15 | // Redispatch to current view to update. |
16 | - this.dispatch(); |
17 | + if (active && active.name === 'EnvironmentView') { |
18 | + active.update(); |
19 | + } else { |
20 | + this.dispatch(); |
21 | + } |
22 | }, |
23 | |
24 | /** |
25 | |
26 | === modified file 'app/models/models.js' |
27 | --- app/models/models.js 2012-11-20 16:55:43 +0000 |
28 | +++ app/models/models.js 2013-01-21 15:11:21 +0000 |
29 | @@ -32,7 +32,6 @@ |
30 | Y.each(data, function(value, key) { |
31 | o[key] = value; |
32 | }); |
33 | - // XXX Fire model changed event manually if we need it later? |
34 | } |
35 | } |
36 | } |
37 | |
38 | === modified file 'app/store/notifications.js' |
39 | --- app/store/notifications.js 2012-11-16 08:25:02 +0000 |
40 | +++ app/store/notifications.js 2013-01-21 15:11:21 +0000 |
41 | @@ -146,7 +146,6 @@ |
42 | * Process new delta stream events and see if we need new notifications |
43 | */ |
44 | generate_notices: function(delta_evt) { |
45 | - console.log('Generating Notices', this, this.getAttrs()); |
46 | var self = this, |
47 | rules = this.ingest_rules, |
48 | app = this.get('app'), |
49 | @@ -161,18 +160,18 @@ |
50 | model; |
51 | |
52 | /* |
53 | - * Data ingestion rules |
54 | - * Create notifications for incoming deltas |
55 | - * Promote some notifications to the 'show me' list |
56 | - * Also: |
57 | - * - for each change event see if there is an notice |
58 | - * relating to that object in the model list |
59 | - * -- see if the current change event invalidates the need |
60 | - * to show the existing notices |
61 | - * -- make the new notice as 'must see' or not ( |
62 | - * errors, etc) |
63 | - * - add a notification for the event |
64 | - */ |
65 | + * Data ingestion rules |
66 | + * Create notifications for incoming deltas |
67 | + * Promote some notifications to the 'show me' list |
68 | + * Also: |
69 | + * - for each change event see if there is an notice |
70 | + * relating to that object in the model list |
71 | + * -- see if the current change event invalidates the need |
72 | + * to show the existing notices |
73 | + * -- make the new notice as 'must see' or not ( |
74 | + * errors, etc) |
75 | + * - add a notification for the event |
76 | + */ |
77 | |
78 | // Dispatch ingestion rules (which may mutate either the |
79 | // current 'notifications' or models within it (notice status) |
80 | |
81 | === modified file 'app/views/environment.js' |
82 | --- app/views/environment.js 2013-01-15 09:27:53 +0000 |
83 | +++ app/views/environment.js 2013-01-21 15:11:21 +0000 |
84 | @@ -28,6 +28,27 @@ |
85 | preventable: false}); |
86 | }, |
87 | |
88 | + /** |
89 | + * Wrapper around topo.update. Rather than |
90 | + * re-rendering a whole topology the view |
91 | + * can require data updates when needed. |
92 | + * Ideally even this shouldn't be needed |
93 | + * as we can observe ModelList change events |
94 | + * and debounce update calculations |
95 | + * internally. |
96 | + * |
97 | + * @method update |
98 | + * @chainable |
99 | + **/ |
100 | + update: function() { |
101 | + this.topo.update(); |
102 | + return this; |
103 | + }, |
104 | + |
105 | + /** |
106 | + * @method render |
107 | + * @chainable |
108 | + **/ |
109 | render: function() { |
110 | var container = this.get('container'), |
111 | topo = this.topo; |
112 | |
113 | === modified file 'app/views/topology/service.js' |
114 | --- app/views/topology/service.js 2013-01-18 21:31:45 +0000 |
115 | +++ app/views/topology/service.js 2013-01-21 15:11:21 +0000 |
116 | @@ -24,29 +24,11 @@ |
117 | }, |
118 | |
119 | '.service-status': { |
120 | - mouseover: {callback: function(d, self) { |
121 | - d3.select(this) |
122 | - .select('.unit-count') |
123 | - .attr('class', 'unit-count show-count'); |
124 | - }}, |
125 | - mouseout: {callback: function(d, self) { |
126 | - d3.select(this) |
127 | - .select('.unit-count') |
128 | - .attr('class', 'unit-count hide-count'); |
129 | - }} |
130 | + mouseover: 'serviceStatusMouseOver', |
131 | + mouseout: 'serviceStatusMouseOut' |
132 | }, |
133 | - '.topology .crosshatch-background rect:first-child': { |
134 | - /** |
135 | - * If the user clicks on the background we cancel any active add |
136 | - * relation. |
137 | - */ |
138 | - click: {callback: function(d, self) { |
139 | - var container = self.get('container'), |
140 | - topo = self.get('component'); |
141 | - container.all('.environment-menu.active').removeClass('active'); |
142 | - self.service_click_actions.hideServiceMenu(null, self); |
143 | - topo.fire('clearState'); |
144 | - }} |
145 | + '.zoomPlane': { |
146 | + click: 'zoomPlaneClick' |
147 | }, |
148 | '.graph-list-picker .picker-button': { |
149 | click: 'showGraphListPicker' |
150 | @@ -56,63 +38,16 @@ |
151 | }, |
152 | // Menu/Controls |
153 | '.view-service': { |
154 | - /** The user clicked on the "View" menu item. */ |
155 | - click: {callback: function(data, context) { |
156 | - // Get the service element |
157 | - var topo = context.get('component'); |
158 | - var box = topo.get('active_service'); |
159 | - var service = topo.serviceForBox(box); |
160 | - context.service_click_actions |
161 | - .hideServiceMenu(box, context); |
162 | - context.service_click_actions |
163 | - .show_service(service, context); |
164 | - }} |
165 | + click: 'viewServiceClick' |
166 | }, |
167 | '.destroy-service': { |
168 | - /** The user clicked on the "Destroy" menu item. */ |
169 | - click: {callback: function(data, context) { |
170 | - // Get the service element |
171 | - var topo = context.get('component'); |
172 | - var box = topo.get('active_service'); |
173 | - var service = topo.serviceForBox(box); |
174 | - // The user is not allowed to destroy the Juju GUI service because |
175 | - // it would break the application they are currently using. |
176 | - if (utils.isGuiService(service)) { |
177 | - return; |
178 | - } |
179 | - context.service_click_actions |
180 | - .hideServiceMenu(box, context); |
181 | - context.service_click_actions |
182 | - .destroyServiceConfirm(service, context); |
183 | - }} |
184 | + click: 'destroyServiceClick' |
185 | } |
186 | }, |
187 | d3: { |
188 | '.service': { |
189 | - 'mousedown.addrel': {callback: function(d, context) { |
190 | - var evt = d3.event; |
191 | - var topo = context.get('component'); |
192 | - context.longClickTimer = Y.later(750, this, function(d, e) { |
193 | - // Provide some leeway for accidental dragging. |
194 | - if ((Math.abs(d.x - d.oldX) + Math.abs(d.y - d.oldY)) / |
195 | - 2 > 5) { |
196 | - return; |
197 | - } |
198 | - |
199 | - // Sometimes mouseover is fired after the mousedown, so ensure |
200 | - // we have the correct event in d3.event for d3.mouse(). |
201 | - d3.event = e; |
202 | - |
203 | - // Start the process of adding a relation |
204 | - topo.fire('addRelationDragStart', {service: d}); |
205 | - }, [d, evt], false); |
206 | - }}, |
207 | - 'mouseup.addrel': {callback: function(d, context) { |
208 | - // Cancel the long-click timer if it exists. |
209 | - if (context.longClickTimer) { |
210 | - context.longClickTimer.cancel(); |
211 | - } |
212 | - }} |
213 | + 'mousedown.addrel': 'serviceAddRelMouseDown', |
214 | + 'mouseup.addrel': 'serviceAddRelMouseUp' |
215 | } |
216 | }, |
217 | yui: { |
218 | @@ -120,6 +55,9 @@ |
219 | show: 'show', |
220 | hide: 'hide', |
221 | fade: 'fade', |
222 | + dragstart: 'dragstart', |
223 | + drag: 'drag', |
224 | + dragend: 'dragend', |
225 | hideServiceMenu: {callback: function() { |
226 | this.service_click_actions.hideServiceMenu(null, this); |
227 | }}, |
228 | @@ -127,6 +65,10 @@ |
229 | } |
230 | }, |
231 | |
232 | + // Margins applied on update to Box instances. |
233 | + subordinate_margins: {top: 0.05, bottom: 0.1, left: 0.084848, right: 0.084848}, |
234 | + service_margins: {top: 0, bottom: 0.1667, left: 0.086758, right: 0.086758}, |
235 | + |
236 | initializer: function(options) { |
237 | ServiceModule.superclass.constructor.apply(this, arguments); |
238 | |
239 | @@ -210,6 +152,82 @@ |
240 | topo.fire('mouseMove'); |
241 | }, |
242 | |
243 | + /** |
244 | + * Handle mouseover service status |
245 | + **/ |
246 | + serviceStatusMouseOver: function(d, context) { |
247 | + d3.select(this) |
248 | + .select('.unit-count') |
249 | + .attr('class', 'unit-count show-count'); |
250 | + }, |
251 | + |
252 | + serviceStatusMouseOut: function(d, context) { |
253 | + d3.select(this) |
254 | + .select('.unit-count') |
255 | + .attr('class', 'unit-count hide-count'); |
256 | + }, |
257 | + |
258 | + /** |
259 | + * If the user clicks on the background we cancel any active add |
260 | + * relation. |
261 | + */ |
262 | + zoomPlaneClick: function(d, self) { |
263 | + var container = self.get('container'), |
264 | + topo = self.get('component'); |
265 | + container.all('.environment-menu.active').removeClass('active'); |
266 | + self.service_click_actions.toggleControlPanel(null, self); |
267 | + topo.fire('clearState'); |
268 | + }, |
269 | + |
270 | + /** The user clicked on the "View" menu item. */ |
271 | + viewServiceClick: function(d, context) { |
272 | + // Get the service element |
273 | + var topo = context.get('component'); |
274 | + var box = topo.get('active_service'); |
275 | + var service = topo.serviceForBox(box); |
276 | + context.service_click_actions |
277 | + .toggleControlPanel(box, context); |
278 | + context.service_click_actions |
279 | + .show_service(service, context); |
280 | + }, |
281 | + |
282 | + /** The user clicked on the "Destroy" menu item. */ |
283 | + destroyServiceClick: function(data, context) { |
284 | + // Get the service element |
285 | + var topo = context.get('component'); |
286 | + var box = topo.get('active_service'); |
287 | + var service = topo.serviceForBox(box); |
288 | + context.service_click_actions |
289 | + .toggleControlPanel(box, context); |
290 | + context.service_click_actions |
291 | + .destroyServiceConfirm(service, context); |
292 | + }, |
293 | + |
294 | + serviceAddRelMouseDown: function(d, context) { |
295 | + var evt = d3.event; |
296 | + var topo = context.get('component'); |
297 | + context.longClickTimer = Y.later(750, this, function(d, e) { |
298 | + // Provide some leeway for accidental dragging. |
299 | + if ((Math.abs(d.x - d.oldX) + Math.abs(d.y - d.oldY)) / |
300 | + 2 > 5) { |
301 | + return; |
302 | + } |
303 | + |
304 | + // Sometimes mouseover is fired after the mousedown, so ensure |
305 | + // we have the correct event in d3.event for d3.mouse(). |
306 | + d3.event = e; |
307 | + |
308 | + // Start the process of adding a relation |
309 | + topo.fire('addRelationDragStart', {service: d}); |
310 | + }, [d, evt], false); |
311 | + }, |
312 | + |
313 | + serviceAddRelMouseUp: function(d, context) { |
314 | + // Cancel the long-click timer if it exists. |
315 | + if (context.longClickTimer) { |
316 | + context.longClickTimer.cancel(); |
317 | + } |
318 | + }, |
319 | /* |
320 | * Sync view models with current db.models. |
321 | */ |
322 | @@ -224,46 +242,122 @@ |
323 | |
324 | Y.each(services, function(service) { |
325 | // Update services with existing positions. |
326 | + // In the future it would be better to sync |
327 | + // the model to the existing box. |
328 | var existing = this.service_boxes[service.id]; |
329 | if (existing) { |
330 | service.pos = existing.pos; |
331 | + service.inDrag = existing.inDrag; |
332 | } |
333 | service.margins(service.subordinate ? |
334 | - { |
335 | - top: 0.05, |
336 | - bottom: 0.1, |
337 | - left: 0.084848, |
338 | - right: 0.084848} : |
339 | - { |
340 | - top: 0, |
341 | - bottom: 0.1667, |
342 | - left: 0.086758, |
343 | - right: 0.086758}); |
344 | + this.subordinate_margins : |
345 | + this.service_margins); |
346 | this.service_boxes[service.id] = service; |
347 | }, this); |
348 | + |
349 | + // XXX: containment breaking alias, do we need this? |
350 | topo.service_boxes = this.service_boxes; |
351 | |
352 | // Nodes are mapped by modelId tuples. |
353 | this.node = vis.selectAll('.service') |
354 | - .data(services, function(d) { return d.modelId();}); |
355 | + .data(services, function(d) { |
356 | + return d.modelId();}); |
357 | }, |
358 | |
359 | /** |
360 | - * Handle dragend events for a service. |
361 | + * Handle drag events for a service. |
362 | * |
363 | * @param {object} svc A service object. |
364 | * @param {object} i Unused. |
365 | * @return {undefined} Side effects only. |
366 | */ |
367 | - _dragend: function(d, i) { |
368 | - var topo = this.get('component'); |
369 | + dragstart: function(d, self) { |
370 | + var topo = self.get('component'); |
371 | + d.oldX = d.x; |
372 | + d.oldY = d.y; |
373 | + self.get('container').all('.environment-menu.active') |
374 | + .removeClass('active'); |
375 | + self.service_click_actions.hideServiceMenu(null, self); |
376 | + console.log('dragstart'); |
377 | + }, |
378 | + |
379 | + dragend: function(d, self) { |
380 | + var topo = self.get('component'); |
381 | if (topo.buildingRelation) { |
382 | topo.fire('addRelationDragEnd'); |
383 | } |
384 | - // Do not update annotations if the GUI is in read-only mode. |
385 | - else if (!topo.get('env').get('readOnly')) { |
386 | - topo.get('env').update_annotations(d.id, {'gui.x': d.x, 'gui.y': d.y}); |
387 | - } |
388 | + else { |
389 | + topo.get('env').update_annotations( |
390 | + d.id, {'gui.x': d.x, 'gui.y': d.y}, |
391 | + function() { |
392 | + // Force a reposition at the end. |
393 | + d.inDrag = false; |
394 | + //self.drag.call(self.getServiceNode(d.id), |
395 | + // d, self, {x:d.x, y: d.y}, false); |
396 | + }); |
397 | + } |
398 | + console.log('dragend'); |
399 | + }, |
400 | + |
401 | + /** |
402 | + * Specialized drag event handler |
403 | + * when called as an event handler it |
404 | + * Allows optional extra param, pos |
405 | + * which when used overrides the mouse |
406 | + * handling. This method can then be |
407 | + * though of as 'drag to position'. |
408 | + * |
409 | + * @method drag |
410 | + * @param {Box} d viewModel BoundingBox. |
411 | + * @param {ServiceModule} self ServiceModule. |
412 | + * @param {Object} pos (optional) containing x/y numbers. |
413 | + * @param {Boolean} includeTransition (optional) Use transition to drag. |
414 | + * |
415 | + * [At the time of this writing useTransition works in practice but |
416 | + * introduces a timing issue in the tests.] |
417 | + **/ |
418 | + drag: function(d, self, pos, includeTransition) { |
419 | + var topo = self.get('component'); |
420 | + var selection = d3.select(this); |
421 | + |
422 | + if (topo.buildingRelation) { |
423 | + topo.fire('addRelationDrag', { box: d }); |
424 | + return; |
425 | + } |
426 | + if (self.longClickTimer) { |
427 | + self.longClickTimer.cancel(); |
428 | + } |
429 | + // Translate the service (and, potentially, menu). |
430 | + if (pos) { |
431 | + d.x = pos.x; |
432 | + d.y = pos.y; |
433 | + // Explicitly reassign data. |
434 | + selection = selection.data([d]); |
435 | + } else { |
436 | + d.x += d3.event.dx; |
437 | + d.y += d3.event.dy; |
438 | + } |
439 | + |
440 | + if (includeTransition) { |
441 | + selection = selection.transition() |
442 | + .duration(500) |
443 | + .ease('elastic'); |
444 | + } |
445 | + |
446 | + selection.attr('transform', function(d, i) { |
447 | + return d.translateStr(); |
448 | + }); |
449 | + if (topo.get('active_service') === d) { |
450 | + self.updateServiceMenuLocation(); |
451 | + } |
452 | + |
453 | + // Clear any state while dragging. |
454 | + self.get('container').all('.environment-menu.active') |
455 | + .removeClass('active'); |
456 | + topo.fire('cancelRelationBuild'); |
457 | + // Update relation lines for just this service. |
458 | + topo.fire('serviceMoved', { service: d }); |
459 | + console.log('drag'); |
460 | }, |
461 | |
462 | /* |
463 | @@ -291,46 +385,16 @@ |
464 | .padding(300); |
465 | } |
466 | |
467 | + if (!this.dragBehavior) { |
468 | + this.dragBehavior = d3.behavior.drag() |
469 | + .on('dragstart', function(d) { self.dragstart.call(this, d, self);}) |
470 | + .on('drag', function(d) { self.drag.call(this, d, self);}) |
471 | + .on('dragend', function(d) { self.dragend.call(this, d, self);}); |
472 | + } |
473 | + |
474 | //Process any changed data. |
475 | this.updateData(); |
476 | |
477 | - var drag = d3.behavior.drag() |
478 | - .on('dragstart', function(d) { |
479 | - d.oldX = d.x; |
480 | - d.oldY = d.y; |
481 | - self.get('container').all('.environment-menu.active') |
482 | - .removeClass('active'); |
483 | - self.service_click_actions.hideServiceMenu(null, self); |
484 | - }) |
485 | - .on('drag', function(d, i) { |
486 | - if (topo.buildingRelation) { |
487 | - topo.fire('addRelationDrag', { box: d }); |
488 | - } else { |
489 | - if (self.longClickTimer) { |
490 | - self.longClickTimer.cancel(); |
491 | - } |
492 | - |
493 | - // Translate the service (and, potentially, menu). |
494 | - d.x += d3.event.dx; |
495 | - d.y += d3.event.dy; |
496 | - d3.select(this).attr('transform', function(d, i) { |
497 | - return d.translateStr(); |
498 | - }); |
499 | - if (topo.get('active_service') === d) { |
500 | - self.updateServiceMenuLocation(); |
501 | - } |
502 | - |
503 | - // Clear any state while dragging. |
504 | - self.get('container').all('.environment-menu.active') |
505 | - .removeClass('active'); |
506 | - topo.fire('cancelRelationBuild'); |
507 | - |
508 | - // Update relation lines for just this service. |
509 | - topo.fire('serviceMoved', { service: d }); |
510 | - } |
511 | - }) |
512 | - .on('dragend', Y.bind(this._dragend, this)); |
513 | - |
514 | // Generate a node for each service, draw it as a rect with |
515 | // labels for service and charm. |
516 | var node = this.node; |
517 | @@ -344,32 +408,49 @@ |
518 | var new_services = this.services.filter(function(boundingBox) { |
519 | return !Y.Lang.isNumber(boundingBox.x); |
520 | }); |
521 | - this.tree.nodes({children: new_services}); |
522 | - |
523 | + if (new_services) { |
524 | + this.tree.nodes({children: new_services}); |
525 | + } |
526 | // enter |
527 | node |
528 | .enter().append('g') |
529 | .attr('class', function(d) { |
530 | return (d.subordinate ? 'subordinate ' : '') + 'service'; |
531 | }) |
532 | - .call(drag) |
533 | + .call(this.dragBehavior) |
534 | .attr('transform', function(d) { |
535 | return d.translateStr(); |
536 | }) |
537 | - .call(function() { |
538 | - // Create new nodes. |
539 | - self.createServiceNode(this); |
540 | - }); |
541 | + .call(self.createServiceNode); |
542 | |
543 | // Update all nodes. |
544 | self.updateServiceNodes(node); |
545 | |
546 | // Remove old nodes. |
547 | node.exit() |
548 | + .each(function(d) { |
549 | + delete self.service_boxes[d.id]; |
550 | + }) |
551 | .remove(); |
552 | }, |
553 | |
554 | /** |
555 | + * Get a d3 selected node for a given service by id. |
556 | + * |
557 | + * @method getServiceNode |
558 | + * @return {d3.selection} selection || null. |
559 | + **/ |
560 | + getServiceNode: function(id) { |
561 | + if (this.node === undefined) { |
562 | + return null; |
563 | + } |
564 | + var node = this.node.filter(function(d, i) { |
565 | + return d.id === id; |
566 | + }); |
567 | + return node && node[0][0] || null; |
568 | + }, |
569 | + |
570 | + /** |
571 | * @method createServiceNode fills a service node with empty structures |
572 | * that will be filled out in the update stage. |
573 | * @param {object} node the node to construct. |
574 | @@ -415,6 +496,30 @@ |
575 | service_scale_width = this.service_scale_width, |
576 | service_scale_height = this.service_scale_height; |
577 | |
578 | + // Apply Position Annotations |
579 | + // This is done after the services_boxes |
580 | + // binding as the event handler will |
581 | + // use that index. |
582 | + node.each(function(d) { |
583 | + var service = topo.serviceForBox(d), |
584 | + annotations = service.get('annotations'), |
585 | + x, y; |
586 | + |
587 | + if (!annotations) {return;} |
588 | + x = annotations['gui.x'], |
589 | + y = annotations['gui.y']; |
590 | + if (!d || |
591 | + (x !== undefined && x !== d.x) && |
592 | + (y !== undefined && y !== d.y)) { |
593 | + // Delete gui.x and gui.y from annotations |
594 | + // as we use the values. |
595 | + delete annotations['gui.x']; |
596 | + delete annotations['gui.y']; |
597 | + if (!d.inDrag) { |
598 | + self.drag.call(this, d, self, {x: x, y: y}); |
599 | + } |
600 | + }}); |
601 | + |
602 | // Size the node for drawing. |
603 | node.attr('width', function(d) { |
604 | // NB: if a service has zero units, as is possible with |
605 | @@ -659,8 +764,6 @@ |
606 | renderedHandler: function() { |
607 | var container = this.get('container'); |
608 | |
609 | - this.update(); |
610 | - |
611 | // Ensure relation labels are sized properly. |
612 | container.all('.rel-label').each(function(label) { |
613 | var width = label.one('text').getClientRect().width + 10; |
614 | @@ -769,13 +872,13 @@ |
615 | * |
616 | * @method showServiceMenu |
617 | * @param {object} box The presentation state for the service. |
618 | - * @param {object} view The environment view. |
619 | + * @param {object} module The service module.. |
620 | * @param {object} context The service context. |
621 | * @return {undefined} Side effects only. |
622 | */ |
623 | - showServiceMenu: function(box, view, context) { |
624 | - var svc_menu = view.get('container').one('#service-menu'); |
625 | - var topo = view.get('component'); |
626 | + showServiceMenu: function(box, module, context) { |
627 | + var svc_menu = module.get('container').one('#service-menu'); |
628 | + var topo = module.get('component'); |
629 | var service = topo.serviceForBox(box); |
630 | |
631 | if (box && !svc_menu.hasClass('active')) { |
632 | @@ -786,7 +889,7 @@ |
633 | if (utils.isGuiService(service)) { |
634 | svc_menu.one('.destroy-service').addClass('disabled'); |
635 | } |
636 | - view.updateServiceMenuLocation(); |
637 | + module.updateServiceMenuLocation(); |
638 | } |
639 | }, |
640 | |
641 | @@ -795,13 +898,13 @@ |
642 | * |
643 | * @method hideServiceMenu |
644 | * @param {object} box The presentation state for the service (unused). |
645 | - * @param {object} view The environment view. |
646 | + * @param {object} module The service module. |
647 | * @param {object} context The service context (unused). |
648 | * @return {undefined} Side effects only. |
649 | */ |
650 | - hideServiceMenu: function(box, view, context) { |
651 | - var svc_menu = view.get('container').one('#service-menu'); |
652 | - var topo = view.get('component'); |
653 | + hideServiceMenu: function(box, module, context) { |
654 | + var svc_menu = module.get('container').one('#service-menu'); |
655 | + var topo = module.get('component'); |
656 | |
657 | if (svc_menu.hasClass('active')) { |
658 | svc_menu.removeClass('active'); |
659 | |
660 | === modified file 'test/test_environment_view.js' |
661 | --- test/test_environment_view.js 2013-01-18 10:15:33 +0000 |
662 | +++ test/test_environment_view.js 2013-01-21 15:11:21 +0000 |
663 | @@ -11,7 +11,8 @@ |
664 | ['service', 'add', { |
665 | 'charm': 'cs:precise/wordpress-6', |
666 | 'id': 'wordpress', |
667 | - 'exposed': false |
668 | + 'exposed': false, |
669 | + 'annotations': {'gui.x': 100, 'gui.y': 200} |
670 | }], |
671 | ['service', 'add', { |
672 | 'charm': 'cs:precise/mediawiki-3', |
673 | @@ -92,7 +93,6 @@ |
674 | env = new juju.Environment({conn: conn}); |
675 | env.connect(); |
676 | conn.open(); |
677 | - env.dispatch_result(environment_delta); |
678 | done(); |
679 | }); |
680 | }); |
681 | @@ -102,12 +102,14 @@ |
682 | done(); |
683 | }); |
684 | |
685 | - beforeEach(function(done) { |
686 | + beforeEach(function() { |
687 | container = Y.Node.create('<div />').setStyle('visibility', 'hidden'); |
688 | Y.one('body').prepend(container); |
689 | db = new models.Database(); |
690 | - db.on_delta({data: environment_delta}); |
691 | - done(); |
692 | + // Use a clone to avoid any mutation |
693 | + // to the input set (as happens with processed |
694 | + // annotations, its a direct reference). |
695 | + db.on_delta({data: Y.clone(environment_delta)}); |
696 | }); |
697 | |
698 | afterEach(function(done) { |
699 | @@ -396,6 +398,43 @@ |
700 | }); |
701 | }); |
702 | |
703 | + it('must be able to use position annotations', |
704 | + function() { |
705 | + var view = new views.environment({ |
706 | + container: container, |
707 | + db: db, |
708 | + env: env |
709 | + }); |
710 | + var tmp_data = { |
711 | + op: 'delta', |
712 | + result: [ |
713 | + ['service', 'add', |
714 | + { |
715 | + 'subordinate': true, |
716 | + 'charm': 'cs:precise/wordpress-6', |
717 | + 'id': 'wordpress', |
718 | + 'annotations': {'gui.x': 374.1, 'gui.y': 211.2} |
719 | + }]]}; |
720 | + var properTransform = /translate\((\d+\.?\d*),(\d+\.?\d*)\)/; |
721 | + var node, match; |
722 | + |
723 | + view.render(); |
724 | + |
725 | + // Test values from initial load. |
726 | + node = view.topo.modules.ServiceModule.getServiceNode('wordpress'); |
727 | + match = node.getAttribute('transform').match(properTransform); |
728 | + match[1].should.eql('100'); |
729 | + match[2].should.eql('200'); |
730 | + |
731 | + db.on_delta({ data: tmp_data }); |
732 | + view.update(); |
733 | + |
734 | + //On annotation change position should be updated. |
735 | + match = node.getAttribute('transform').match(properTransform); |
736 | + match[1].should.eql('374.1'); |
737 | + match[2].should.eql('211.2'); |
738 | + }); |
739 | + |
740 | it('must be able to render subordinate relation indicators', |
741 | function() { |
742 | var view = new views.environment({ |
743 | |
744 | === modified file 'test/test_service_module.js' |
745 | --- test/test_service_module.js 2013-01-18 18:42:37 +0000 |
746 | +++ test/test_service_module.js 2013-01-21 15:11:21 +0000 |
747 | @@ -56,7 +56,7 @@ |
748 | { id: 'wordpress', |
749 | x: 100.1, |
750 | y: 200.2}; |
751 | - serviceModule._dragend(d, 0); |
752 | + serviceModule.dragend(d, serviceModule); |
753 | assert.isTrue(called); |
754 | location['gui.x'].should.equal(100.1); |
755 | location['gui.y'].should.equal(200.2); |
756 | @@ -70,7 +70,7 @@ |
757 | y: 200.2}; |
758 | var topo = serviceModule.get('component'); |
759 | topo.buildingRelation = true; |
760 | - serviceModule._dragend(d, 0); |
761 | + serviceModule.dragend(d, serviceModule); |
762 | assert.isFalse(called); |
763 | location['gui.x'].should.equal(0); |
764 | location['gui.y'].should.equal(0); |
765 | |
766 | === modified file 'test/test_topology.js' |
767 | --- test/test_topology.js 2013-01-21 13:43:21 +0000 |
768 | +++ test/test_topology.js 2013-01-21 15:11:21 +0000 |
769 | @@ -113,10 +113,13 @@ |
770 | if (name === 'component') { |
771 | return fauxTopo; |
772 | } |
773 | + }, |
774 | + service_click_actions: { |
775 | + toggleControlPanel: function() {}, |
776 | + destroyServiceConfirm: function() {} |
777 | } |
778 | }; |
779 | - topo.events.ServiceModule.scene['.destroy-service'].click.callback( |
780 | - undefined, context); |
781 | + topo.modules.ServiceModule.destroyServiceClick(undefined, context); |
782 | }); |
783 | }); |
784 | |
785 | @@ -153,9 +156,18 @@ |
786 | if (name === 'container') { |
787 | return {one: function() { return menu; }}; |
788 | } else if (name === 'component') { |
789 | +<<<<<<< TREE |
790 | return { set: function() {}, |
791 | serviceForBox: function(box) { return service;} |
792 | }; |
793 | +======= |
794 | + return { |
795 | + set: function() {}, |
796 | + serviceForBox: function() { |
797 | + return service; |
798 | + } |
799 | + }; |
800 | +>>>>>>> MERGE-SOURCE |
801 | } |
802 | }, |
803 | updateServiceMenuLocation: function() {} |
804 | |
805 | === modified file 'undocumented' |
806 | --- undocumented 2013-01-18 18:42:37 +0000 |
807 | +++ undocumented 2013-01-21 15:11:21 +0000 |
808 | @@ -1,18 +1,18 @@ |
809 | -app/app.js:599 "callback" |
810 | app/app.js:100 "callback" |
811 | -app/store/env.js:223 "status" |
812 | -app/store/env.js:175 "get_service" |
813 | -app/store/env.js:69 "on_open" |
814 | -app/store/env.js:27 "initializer" |
815 | -app/store/env.js:78 "on_message" |
816 | -app/store/env.js:73 "on_close" |
817 | -app/store/env.js:171 "get_charm" |
818 | -app/store/env.js:45 "destructor" |
819 | -app/store/env.js:111 "dispatch_result" |
820 | -app/store/env.js:117 "_dispatch_event" |
821 | -app/store/env.js:126 "_dispatch_rpc_result" |
822 | -app/store/env.js:268 "get_endpoints" |
823 | -app/store/env.js:50 "connect" |
824 | +app/app.js:648 "callback" |
825 | +app/store/env.js:31 "initializer" |
826 | +app/store/env.js:239 "get_service" |
827 | +app/store/env.js:55 "connect" |
828 | +app/store/env.js:83 "on_message" |
829 | +app/store/env.js:74 "on_open" |
830 | +app/store/env.js:235 "get_charm" |
831 | +app/store/env.js:78 "on_close" |
832 | +app/store/env.js:116 "dispatch_result" |
833 | +app/store/env.js:415 "get_endpoints" |
834 | +app/store/env.js:320 "status" |
835 | +app/store/env.js:131 "_dispatch_rpc_result" |
836 | +app/store/env.js:50 "destructor" |
837 | +app/store/env.js:122 "_dispatch_event" |
838 | app/store/charm.js:66 "_normalizeCharms" |
839 | app/store/charm.js:24 "find" |
840 | app/store/charm.js:11 "success" |
841 | @@ -27,38 +27,39 @@ |
842 | app/store/notifications.js:25 "message" |
843 | app/store/notifications.js:129 "title" |
844 | app/store/notifications.js:137 "message" |
845 | -app/views/utils.js:370 "_addAlertMessage" |
846 | -app/views/utils.js:825 "BoxPair" |
847 | -app/views/utils.js:702 "scale" |
848 | -app/views/utils.js:227 "humanizeNumber" |
849 | -app/views/utils.js:137 "console" |
850 | -app/views/utils.js:828 "pair" |
851 | +app/views/utils.js:166 "substitute" |
852 | +app/views/utils.js:251 "hasSVGClass" |
853 | +app/views/utils.js:296 "toggleSVGClass" |
854 | +app/views/utils.js:616 "BoundingBox" |
855 | +app/views/utils.js:135 "noop" |
856 | +app/views/utils.js:259 "addSVGClass" |
857 | +app/views/utils.js:642 "get" |
858 | +app/views/utils.js:138 "console" |
859 | +app/views/utils.js:703 "scale" |
860 | +app/views/utils.js:704 "translate" |
861 | +app/views/utils.js:218 "renderable_charm" |
862 | +app/views/utils.js:826 "BoxPair" |
863 | app/views/utils.js:113 "noop" |
864 | -app/views/utils.js:617 "Box" |
865 | -app/views/utils.js:250 "hasSVGClass" |
866 | -app/views/utils.js:338 "action" |
867 | -app/views/utils.js:134 "noop" |
868 | -app/views/utils.js:658 "get" |
869 | -app/views/utils.js:217 "renderable_charm" |
870 | -app/views/utils.js:651 "set" |
871 | -app/views/utils.js:641 "get" |
872 | -app/views/utils.js:566 "isInt" |
873 | -app/views/utils.js:615 "BoundingBox" |
874 | -app/views/utils.js:570 "isFloat" |
875 | -app/views/utils.js:650 "get" |
876 | -app/views/utils.js:703 "translate" |
877 | -app/views/utils.js:418 "invokeCallback" |
878 | -app/views/utils.js:559 "toString" |
879 | -app/views/utils.js:295 "toggleSVGClass" |
880 | -app/views/utils.js:258 "addSVGClass" |
881 | -app/views/utils.js:659 "set" |
882 | -app/views/utils.js:279 "removeSVGClass" |
883 | -app/views/utils.js:194 "bindModelView" |
884 | -app/views/utils.js:165 "substitute" |
885 | -app/views/utils.js:644 "set" |
886 | -app/views/utils.js:131 "native" |
887 | +app/views/utils.js:280 "removeSVGClass" |
888 | +app/views/utils.js:371 "_addAlertMessage" |
889 | +app/views/utils.js:829 "pair" |
890 | +app/views/utils.js:228 "humanizeNumber" |
891 | +app/views/utils.js:339 "action" |
892 | +app/views/utils.js:567 "isInt" |
893 | +app/views/utils.js:195 "bindModelView" |
894 | +app/views/utils.js:618 "Box" |
895 | +app/views/utils.js:651 "get" |
896 | +app/views/utils.js:560 "toString" |
897 | +app/views/utils.js:645 "set" |
898 | +app/views/utils.js:419 "invokeCallback" |
899 | +app/views/utils.js:571 "isFloat" |
900 | +app/views/utils.js:132 "native" |
901 | +app/views/utils.js:652 "set" |
902 | +app/views/utils.js:660 "set" |
903 | +app/views/utils.js:659 "get" |
904 | app/views/environment.js:24 "initializer" |
905 | -app/views/environment.js:31 "render" |
906 | +app/views/environment.js:31 "update" |
907 | +app/views/environment.js:36 "render" |
908 | app/views/charm-panel.js:1168 "calculatePanelPosition" |
909 | app/views/charm-panel.js:476 "initializer" |
910 | app/views/charm-panel.js:250 "render" |
911 | @@ -111,126 +112,130 @@ |
912 | app/views/unit.js:297 "retryRelation" |
913 | app/views/unit.js:118 "confirmResolved" |
914 | app/views/unit.js:23 "initializer" |
915 | -app/views/service.js:488 "updateConstraints" |
916 | -app/views/service.js:514 "_setConstraintsCallback" |
917 | -app/views/service.js:846 "filterUnits" |
918 | +app/views/service.js:875 "render" |
919 | +app/views/service.js:338 "fitToWindow" |
920 | +app/views/service.js:688 "render" |
921 | app/views/service.js:288 "initializer" |
922 | -app/views/service.js:547 "render" |
923 | -app/views/service.js:372 "render" |
924 | -app/views/service.js:451 "_removeRelationCallback" |
925 | +app/views/service.js:740 "saveConfig" |
926 | app/views/service.js:191 "_destroyCallback" |
927 | -app/views/service.js:338 "fitToWindow" |
928 | app/views/service.js:116 "_removeUnitCallback" |
929 | -app/views/service.js:737 "_setConfigCallback" |
930 | -app/views/service.js:402 "confirmRemoved" |
931 | app/views/service.js:264 "_exposeServiceCallback" |
932 | -app/views/service.js:667 "showErrors" |
933 | app/views/service.js:257 "exposeService" |
934 | -app/views/service.js:800 "render" |
935 | +app/views/service.js:888 "filterUnits" |
936 | +app/views/service.js:770 "_setConfigCallback" |
937 | app/views/service.js:52 "_modifyUnits" |
938 | -app/views/service.js:707 "saveConfig" |
939 | -app/views/service.js:608 "render" |
940 | +app/views/service.js:406 "render" |
941 | +app/views/service.js:418 "confirmRemoved" |
942 | app/views/service.js:339 "getHeight" |
943 | app/views/service.js:182 "destroyService" |
944 | +app/views/service.js:440 "doRemoveRelation" |
945 | +app/views/service.js:615 "render" |
946 | app/views/service.js:30 "modifyUnits" |
947 | -app/views/service.js:424 "doRemoveRelation" |
948 | app/views/service.js:300 "getServiceTabs" |
949 | app/views/service.js:166 "confirmDestroy" |
950 | +app/views/service.js:502 "updateConstraints" |
951 | +app/views/service.js:528 "_setConstraintsCallback" |
952 | app/views/service.js:88 "_addUnitCallback" |
953 | app/views/service.js:23 "resetUnits" |
954 | +app/views/service.js:700 "showErrors" |
955 | app/views/service.js:230 "unexposeService" |
956 | app/views/service.js:237 "_unexposeServiceCallback" |
957 | -app/views/topology/panzoom.js:94 "zoom_out" |
958 | +app/views/service.js:467 "_removeRelationCallback" |
959 | +app/views/topology/panzoom.js:151 "rescale" |
960 | app/views/topology/panzoom.js:77 "zoomHandler" |
961 | app/views/topology/panzoom.js:47 "renderSlider" |
962 | -app/views/topology/panzoom.js:112 "_fire_zoom" |
963 | -app/views/topology/panzoom.js:165 "renderedHandler" |
964 | -app/views/topology/panzoom.js:103 "zoom_in" |
965 | -app/views/topology/panzoom.js:140 "rescale" |
966 | +app/views/topology/panzoom.js:114 "zoom_in" |
967 | +app/views/topology/panzoom.js:176 "renderedHandler" |
968 | +app/views/topology/panzoom.js:123 "_fire_zoom" |
969 | +app/views/topology/panzoom.js:105 "zoom_out" |
970 | app/views/topology/panzoom.js:33 "componentBound" |
971 | -app/views/topology/relation.js:78 "processRelation" |
972 | -app/views/topology/relation.js:403 "_removeRelationCallback" |
973 | -app/views/topology/relation.js:325 "addRelationDragStart" |
974 | -app/views/topology/relation.js:460 "cancelRelationBuild" |
975 | -app/views/topology/relation.js:391 "removeRelation" |
976 | -app/views/topology/relation.js:309 "snapOutOfService" |
977 | -app/views/topology/relation.js:724 "subRelBlockMouseEnter" |
978 | -app/views/topology/relation.js:632 "addRelationEnd" |
979 | -app/views/topology/relation.js:350 "addRelationDrag" |
980 | -app/views/topology/relation.js:280 "snapToService" |
981 | -app/views/topology/relation.js:251 "draglineClicked" |
982 | -app/views/topology/relation.js:158 "drawRelationGroup" |
983 | -app/views/topology/relation.js:112 "updateLinks" |
984 | -app/views/topology/relation.js:428 "removeRelationConfirm" |
985 | -app/views/topology/relation.js:236 "updateSubordinateRelationsCount" |
986 | -app/views/topology/relation.js:55 "render" |
987 | -app/views/topology/relation.js:270 "addRelation" |
988 | -app/views/topology/relation.js:257 "addRelButtonClicked" |
989 | -app/views/topology/relation.js:781 "relationClick" |
990 | -app/views/topology/relation.js:673 "_addRelationCallback" |
991 | -app/views/topology/relation.js:717 "subordinateRelationsForService" |
992 | -app/views/topology/relation.js:372 "addRelationDragEnd" |
993 | -app/views/topology/relation.js:90 "processRelations" |
994 | -app/views/topology/relation.js:50 "initializer" |
995 | -app/views/topology/relation.js:536 "addRelationStart" |
996 | -app/views/topology/relation.js:733 "subRelBlockMouseLeave" |
997 | -app/views/topology/relation.js:60 "update" |
998 | -app/views/topology/relation.js:221 "drawRelation" |
999 | -app/views/topology/relation.js:74 "renderedHandler" |
1000 | -app/views/topology/relation.js:548 "ambiguousAddRelationCheck" |
1001 | -app/views/topology/service.js:635 "renderedHandler" |
1002 | -app/views/topology/service.js:181 "serviceMouseLeave" |
1003 | -app/views/topology/service.js:604 "show" |
1004 | -app/views/topology/service.js:219 "updateData" |
1005 | -app/views/topology/service.js:668 "showGraphListPicker" |
1006 | -app/views/topology/service.js:610 "hide" |
1007 | -app/views/topology/service.js:747 "show_service" |
1008 | -app/views/topology/service.js:616 "fade" |
1009 | -app/views/topology/service.js:128 "initializer" |
1010 | -app/views/topology/service.js:156 "serviceDblClick" |
1011 | -app/views/topology/service.js:138 "serviceClick" |
1012 | -app/views/topology/service.js:258 "update" |
1013 | -app/views/topology/service.js:163 "serviceMouseEnter" |
1014 | -app/views/topology/service.js:779 "destroyService" |
1015 | -app/views/topology/service.js:678 "hideGraphListPicker" |
1016 | -app/views/topology/service.js:788 "_destroyCallback" |
1017 | -app/views/topology/service.js:756 "destroyServiceConfirm" |
1018 | -app/views/topology/service.js:685 "updateServiceMenuLocation" |
1019 | -app/views/topology/topology.js:137 "serviceForBox" |
1020 | -app/views/topology/topology.js:175 "setter" |
1021 | -app/views/topology/topology.js:174 "getter" |
1022 | -app/views/topology/topology.js:107 "computeScales" |
1023 | -app/views/topology/topology.js:157 "getter" |
1024 | +app/views/topology/relation.js:269 "addRelButtonClicked" |
1025 | +app/views/topology/relation.js:101 "processRelations" |
1026 | +app/views/topology/relation.js:362 "snapOutOfService" |
1027 | +app/views/topology/relation.js:456 "_removeRelationCallback" |
1028 | +app/views/topology/relation.js:513 "cancelRelationBuild" |
1029 | +app/views/topology/relation.js:55 "initializer" |
1030 | +app/views/topology/relation.js:247 "updateSubordinateRelationsCount" |
1031 | +app/views/topology/relation.js:262 "draglineClicked" |
1032 | +app/views/topology/relation.js:169 "drawRelationGroup" |
1033 | +app/views/topology/relation.js:123 "updateLinks" |
1034 | +app/views/topology/relation.js:378 "addRelationDragStart" |
1035 | +app/views/topology/relation.js:834 "relationClick" |
1036 | +app/views/topology/relation.js:85 "renderedHandler" |
1037 | +app/views/topology/relation.js:601 "ambiguousAddRelationCheck" |
1038 | +app/views/topology/relation.js:481 "removeRelationConfirm" |
1039 | +app/views/topology/relation.js:60 "render" |
1040 | +app/views/topology/relation.js:770 "subordinateRelationsForService" |
1041 | +app/views/topology/relation.js:403 "addRelationDrag" |
1042 | +app/views/topology/relation.js:425 "addRelationDragEnd" |
1043 | +app/views/topology/relation.js:89 "processRelation" |
1044 | +app/views/topology/relation.js:232 "drawRelation" |
1045 | +app/views/topology/relation.js:685 "addRelationEnd" |
1046 | +app/views/topology/relation.js:290 "addRelation" |
1047 | +app/views/topology/relation.js:726 "_addRelationCallback" |
1048 | +app/views/topology/relation.js:589 "addRelationStart" |
1049 | +app/views/topology/relation.js:786 "subRelBlockMouseLeave" |
1050 | +app/views/topology/relation.js:444 "removeRelation" |
1051 | +app/views/topology/relation.js:333 "snapToService" |
1052 | +app/views/topology/relation.js:65 "update" |
1053 | +app/views/topology/relation.js:777 "subRelBlockMouseEnter" |
1054 | +app/views/topology/service.js:727 "show" |
1055 | +app/views/topology/service.js:799 "hideGraphListPicker" |
1056 | +app/views/topology/service.js:72 "initializer" |
1057 | +app/views/topology/service.js:288 "dragend" |
1058 | +app/views/topology/service.js:789 "showGraphListPicker" |
1059 | +app/views/topology/service.js:947 "destroyService" |
1060 | +app/views/topology/service.js:206 "serviceAddRelMouseDown" |
1061 | +app/views/topology/service.js:110 "serviceMouseEnter" |
1062 | +app/views/topology/service.js:225 "serviceAddRelMouseUp" |
1063 | +app/views/topology/service.js:164 "serviceStatusMouseOut" |
1064 | +app/views/topology/service.js:739 "fade" |
1065 | +app/views/topology/service.js:234 "updateData" |
1066 | +app/views/topology/service.js:82 "serviceClick" |
1067 | +app/views/topology/service.js:733 "hide" |
1068 | +app/views/topology/service.js:758 "renderedHandler" |
1069 | +app/views/topology/service.js:915 "show_service" |
1070 | +app/views/topology/service.js:924 "destroyServiceConfirm" |
1071 | +app/views/topology/service.js:364 "update" |
1072 | +app/views/topology/service.js:128 "serviceMouseLeave" |
1073 | +app/views/topology/service.js:956 "_destroyCallback" |
1074 | +app/views/topology/service.js:100 "serviceDblClick" |
1075 | +app/views/topology/service.js:806 "updateServiceMenuLocation" |
1076 | +app/views/topology/topology.js:163 "getter" |
1077 | +app/views/topology/topology.js:172 "setter" |
1078 | +app/views/topology/topology.js:159 "getter" |
1079 | +app/views/topology/topology.js:109 "computeScales" |
1080 | +app/views/topology/topology.js:176 "getter" |
1081 | +app/views/topology/topology.js:171 "getter" |
1082 | +app/views/topology/topology.js:139 "serviceForBox" |
1083 | +app/views/topology/topology.js:177 "setter" |
1084 | +app/views/topology/topology.js:26 "initializer" |
1085 | app/views/topology/topology.js:63 "renderOnce" |
1086 | -app/views/topology/topology.js:26 "initializer" |
1087 | -app/views/topology/topology.js:161 "getter" |
1088 | -app/views/topology/topology.js:169 "getter" |
1089 | -app/views/topology/topology.js:170 "setter" |
1090 | -app/views/topology/viewport.js:43 "resized" |
1091 | -app/models/models.js:305 "setter" |
1092 | -app/models/models.js:430 "getModelListByModelName" |
1093 | -app/models/models.js:325 "add" |
1094 | -app/models/models.js:171 "update_service_unit_aggregates" |
1095 | -app/models/models.js:358 "getNotificationsForModel" |
1096 | -app/models/models.js:69 "process_delta" |
1097 | -app/models/models.js:456 "on_delta" |
1098 | -app/models/models.js:421 "getModelById" |
1099 | -app/models/models.js:149 "get_informative_states_for_service" |
1100 | -app/models/models.js:204 "process_delta" |
1101 | -app/models/models.js:339 "trim" |
1102 | -app/models/models.js:385 "initializer" |
1103 | -app/models/models.js:437 "getModelFromChange" |
1104 | -app/models/models.js:273 "get_relations_for_service" |
1105 | -app/models/models.js:330 "comparator" |
1106 | -app/models/models.js:120 "add" |
1107 | -app/models/models.js:112 "_setDefaultsAndCalculatedValues" |
1108 | -app/models/models.js:297 "valueFn" |
1109 | -app/models/models.js:446 "reset" |
1110 | -app/models/models.js:130 "get_units_for_service" |
1111 | -app/models/models.js:108 "process_delta" |
1112 | -app/models/models.js:345 "removeOldest" |
1113 | -app/models/models.js:240 "has_relation_for_endpoint" |
1114 | -app/models/models.js:231 "process_delta" |
1115 | +app/views/topology/viewport.js:31 "resized" |
1116 | +app/models/models.js:357 "getNotificationsForModel" |
1117 | +app/models/models.js:429 "getModelListByModelName" |
1118 | +app/models/models.js:420 "getModelById" |
1119 | +app/models/models.js:68 "process_delta" |
1120 | +app/models/models.js:445 "reset" |
1121 | +app/models/models.js:304 "setter" |
1122 | +app/models/models.js:119 "add" |
1123 | +app/models/models.js:384 "initializer" |
1124 | +app/models/models.js:344 "removeOldest" |
1125 | +app/models/models.js:455 "on_delta" |
1126 | +app/models/models.js:272 "get_relations_for_service" |
1127 | +app/models/models.js:436 "getModelFromChange" |
1128 | +app/models/models.js:107 "process_delta" |
1129 | +app/models/models.js:148 "get_informative_states_for_service" |
1130 | +app/models/models.js:170 "update_service_unit_aggregates" |
1131 | +app/models/models.js:230 "process_delta" |
1132 | +app/models/models.js:129 "get_units_for_service" |
1133 | +app/models/models.js:296 "valueFn" |
1134 | +app/models/models.js:111 "_setDefaultsAndCalculatedValues" |
1135 | +app/models/models.js:329 "comparator" |
1136 | +app/models/models.js:203 "process_delta" |
1137 | +app/models/models.js:239 "has_relation_for_endpoint" |
1138 | +app/models/models.js:324 "add" |
1139 | +app/models/models.js:338 "trim" |
1140 | app/models/endpoints.js:43 "add" |
1141 | app/models/endpoints.js:32 "convert" |
1142 | app/models/charm.js:155 "validator" |
Reviewers: mp+143980_ code.launchpad. net,
Message:
Please take a look.
Description:
Support for service positions from server
Provides support for loading initial and updated position
from annotations stored on the server. Updates occur
via a shared codepath with the drag event handler
to ensure proper updates are applied over time.
https:/ /code.launchpad .net/~bcsaller/ juju-gui/ persistent- layout/ +merge/ 143980
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/7132061/
Affected files: models. js environment. js topology/ service. js environment_ view.js service_ module. js topology. js
A [revision details]
M app/app.js
M app/models/
M app/views/
M app/views/
M test/test_
M test/test_
M test/test_
M undocumented