Merge lp:~bcsaller/juju-gui/viewmodel-improvements into lp:juju-gui/experimental
- viewmodel-improvements
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 363 |
Proposed branch: | lp:~bcsaller/juju-gui/viewmodel-improvements |
Merge into: | lp:juju-gui/experimental |
Diff against target: |
1267 lines (+444/-455) 8 files modified
app/views/service.js (+0/-1) app/views/topology/relation.js (+2/-2) app/views/topology/service.js (+23/-42) app/views/utils.js (+230/-177) test/test_environment_view.js (+58/-50) test/test_service_module.js (+14/-0) test/test_topology.js (+0/-79) undocumented (+117/-104) |
To merge this branch: | bzr merge lp:~bcsaller/juju-gui/viewmodel-improvements |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju GUI Hackers | Pending | ||
Review via email: mp+145755@code.launchpad.net |
Commit message
Description of the change
View model improvements
Change BoundingBoxes to retain reference to module. This allows box models
to directly resolve the db.Model backing them. This also allow access to the
DOMNode in the canvas directly. Certain features of box directly depend on
methods being available in the passed in module, mocking this is still
possible and shown in tests.
Benjamin Saller (bcsaller) wrote : | # |
Gary Poster (gary) wrote : | # |
Hey Ben. I was just skimming this and admiring it in a pre-review kind of way when I noticed that there's a conflict in test/test_
Thanks
Gary
- 353. By Benjamin Saller
-
merge trunk
Nicola Larosa (teknico) wrote : | # |
Land with changes.
This looks like a nice refactoring. Thanks for regenerating the
"undocumented" file. Please have a look at the comments below.
https:/
File app/views/
https:/
app/views/
A more descriptive name than "d" on these methods would be nice, some
day.
https:/
app/views/
view) {
Ditto for "m". Can we do away with these one-letter names, anyway? They
hamper readability.
https:/
File app/views/utils.js (right):
https:/
app/views/
keeps their position
It is not a class anymore, is it? Can this comment be converted to a YUI
comment block somehow?
https:/
app/views/
canvas and context.
s/Module/module/
https:/
app/views/
Add a "@method toBoundingBoxes" directive, and remove one asterisk from
the block closing mark.
https:/
app/views/
and other convenience data.
Indent the text, to show that it continues from the line above.
https:/
File test/test_
https:/
test/test_
s/pos/position/ ?
https:/
test/test_
boxes.wordpress
Nicer. :-)
Nicola Larosa (teknico) wrote : | # |
Uhm, I should mention that while "make test-prod" completes
successfully, in "make test-debug" mocha times out after setup, right
before running the tests. "make test-debug" does complete successfully
in trunk, here.
- 354. By Benjamin Saller
-
the check is at the level of the menu, not the destroy action
- 355. By Benjamin Saller
-
lint
Benjamin Saller (bcsaller) wrote : | # |
Please take a look.
Gary Poster (gary) wrote : | # |
I was in the middle of a review--with a long hiatus for various calls
and other responsibilitie
comments are from the previous revision, so they may or may not still be
pertinent. I'm sending them off and will start a new review after
lunch.
https:/
File app/views/
https:/
app/views/
On 2013/01/31 10:44:24, teknico wrote:
> A more descriptive name than "d" on these methods would be nice, some
day.
+1
https:/
app/views/
Shouldn't this be box.model? If so, why does it still work? If it is
really ok for this to be the box, why should we call it the service?
Please address this one way or another so it is clear to the reader what
is going on--and, if necessary, tested that the method is doing what we
expect.
https:/
app/views/
db.services, this.service_
So, this mutates something or other? I need to go read it...
https:/
app/views/
Y.Object.
Why do we have to stash this?
If we really want this on the module, I'd like it initialized and
explained in the initializer, the way you do with service_boxes.
https:/
app/views/
do we need this?
If we do, then we should only store service_boxes on topo, and not
locally on "this," right? (No need to change this now, just a fly-by
thought.)
Madison Scott-Clary (makyo) wrote : | # |
Reviewing this in light of the possible changes to my current branch.
Overall I think it's really good stuff (I like the new box object much
better, as it makes things a little more readable, to me), and will help
me out in SOME respects, but only a few. Thanks for the work. I'll
defer to the others for landability, as they've got good comments.
Madison Scott-Clary (makyo) wrote : | # |
Increasing the number of units for a service is not reflected in the
environment view (increased WP from 1 to 10; service isn't resized,
health graph remains the same, unit count still says 1). Additionally,
I receive some errors when trying to create relationships between
services. Sometimes, it'll fail out when trying to find available
endpoints, and sometimes it dies on creating a relation. I was trying
with haproxy. After doing so, several things seemed to fail in a
cascade.
Benjamin Saller (bcsaller) wrote : | # |
On 2013/01/31 19:51:03, matthew.scott wrote:
> Increasing the number of units for a service is not reflected in the
environment
> view (increased WP from 1 to 10; service isn't resized, health graph
remains the
> same, unit count still says 1). Additionally, I receive some errors
when trying
> to create relationships between services. Sometimes, it'll fail out
when trying
> to find available endpoints, and sometimes it dies on creating a
relation. I
> was trying with haproxy. After doing so, several things seemed to
fail in a
> cascade.
You found a pre-existing bug but the fix is a small delta. Trying to
only incrementally render the env view lead to calling update() and
rendered()
on dbchange but we were only doing this when env view was the current
view. This has been changed in the branch to always update the env view
on db change as well as any other actions that need to be taken.
Gary Poster (gary) wrote : | # |
Hi Ben. Thank you for this very nice branch.
Land with changes, as documented in this review and my previous one.
I asked Matt to look at this as well if he has time. I didn't have time
to look at it during normal hours, but already had some comments waiting
in the wings, so decided to complete my review.
Gary
https:/
File app/views/
https:/
app/views/
service.modelId ||
These sorts of changes--change calls to attrs--are throughout the
branch, and nice.
https:/
File app/views/
https:/
app/views/
db.services, this.service_
From comment on previous partial review: I now see that
this.service_boxes is mutated. Cool.
https:/
File app/views/utils.js (right):
https:/
app/views/
Nice.
https:/
app/views/
Now that it is a property, wouldn't this be better as a noun,
"nearestConnector"?
https:/
app/views/
Likewise: "connectorPair"?
https:/
File test/test_
https:/
test/test_
new model data',
Nice test.
https:/
File undocumented (right):
https:/
undocumented:247: app/models/
We went from 234 to 247 undocumented functions. It's our policy to try
to keep that number stable or lower even when we regenerate the
"undocumented" file. I reviewed why the number went up, and I see that
it is largely or exclusively because of the property getter and setter
functions in the utils work you did. You documented the properties
quite nicely, so, without a counterargument to convince me otherwise,
this case seems to be one in which our yuidoc linter needs to be
improved. Therefore, I'd like to honor our policy, but I'd ask you to
do it by simply filing a bug against our yuidoc linter describing the
situation, and how the linter could be improved to no longer complain
about this situation. If you'd also like to document 13 random
functions arbitrarily to keep our count down, I won't stop you, but I do
not ask for that and in fact would prefer if that task did not slow
landing the branch down.
Benjamin Saller (bcsaller) wrote : | # |
https:/
File app/views/
https:/
app/views/
Y.Object.
On 2013/01/31 17:30:12, gary.poster wrote:
> Why do we have to stash this?
> If we really want this on the module, I'd like it initialized and
explained in
> the initializer, the way you do with service_boxes.
removed.
https:/
app/views/
do we need this?
On 2013/01/31 17:30:12, gary.poster wrote:
> If we do, then we should only store service_boxes on topo, and not
locally on
> "this," right? (No need to change this now, just a fly-by thought.)
Moved to topo.
https:/
File app/views/utils.js (right):
https:/
app/views/
On 2013/02/01 02:26:05, gary.poster wrote:
> Now that it is a property, wouldn't this be better as a noun,
> "nearestConnector"?
these two methods take arguments and are not normal properties so they
retain the 'get' prefix.
Benjamin Saller (bcsaller) wrote : | # |
*** Submitted:
View model improvements
Change BoundingBoxes to retain reference to module. This allows box
models
to directly resolve the db.Model backing them. This also allow access to
the
DOMNode in the canvas directly. Certain features of box directly depend
on
methods being available in the passed in module, mocking this is still
possible and shown in tests.
R=teknico, gary.poster, matthew.scott
CC=
https:/
Preview Diff
1 | === modified file 'app/views/service.js' |
2 | --- app/views/service.js 2013-01-24 19:11:19 +0000 |
3 | +++ app/views/service.js 2013-01-31 16:56:20 +0000 |
4 | @@ -118,7 +118,6 @@ |
5 | getModelURL = this.get('getModelURL'), |
6 | db = this.get('db'), |
7 | unit_names = ev.unit_names; |
8 | - console.log('_removeUnitCallback with: ', arguments); |
9 | |
10 | if (ev.err) { |
11 | db.notifications.add( |
12 | |
13 | === modified file 'app/views/topology/relation.js' |
14 | --- app/views/topology/relation.js 2013-01-30 12:38:03 +0000 |
15 | +++ app/views/topology/relation.js 2013-01-31 16:56:20 +0000 |
16 | @@ -776,8 +776,8 @@ |
17 | */ |
18 | subordinateRelationsForService: function(service) { |
19 | return this.relations.filter(function(relation) { |
20 | - return (relation.source.modelId() === service.modelId() || |
21 | - relation.target.modelId() === service.modelId()) && |
22 | + return (relation.source.modelId === service.modelId || |
23 | + relation.target.modelId === service.modelId) && |
24 | relation.isSubordinate; |
25 | }); |
26 | }, |
27 | |
28 | === modified file 'app/views/topology/service.js' |
29 | --- app/views/topology/service.js 2013-01-30 12:38:03 +0000 |
30 | +++ app/views/topology/service.js 2013-01-31 16:56:20 +0000 |
31 | @@ -102,7 +102,7 @@ |
32 | serviceDblClick: function(d, self) { |
33 | // Just show the service on double-click. |
34 | var topo = self.get('component'), |
35 | - service = topo.serviceForBox(d); |
36 | + service = d.model; |
37 | // The browser sends a click event right before the dblclick one, and it |
38 | // opens the service menu: close it before moving to the service details. |
39 | self.service_click_actions.hideServiceMenu(null, self); |
40 | @@ -194,7 +194,7 @@ |
41 | // Get the service element |
42 | var topo = context.get('component'); |
43 | var box = topo.get('active_service'); |
44 | - var service = topo.serviceForBox(box); |
45 | + var service = box.model; |
46 | context.service_click_actions |
47 | .hideServiceMenu(d, context); |
48 | context.service_click_actions |
49 | @@ -210,7 +210,7 @@ |
50 | // Get the service element |
51 | var topo = context.get('component'); |
52 | var box = topo.get('active_service'); |
53 | - var service = topo.serviceForBox(box); |
54 | + var service = box; |
55 | context.service_click_actions |
56 | .hideServiceMenu(box, context); |
57 | context.service_click_actions |
58 | @@ -252,31 +252,16 @@ |
59 | var topo = this.get('component'); |
60 | var vis = topo.vis; |
61 | var db = topo.get('db'); |
62 | - var services = db.services.map(views.toBoundingBox); |
63 | - |
64 | - this.services = services; |
65 | - |
66 | - Y.each(services, function(service) { |
67 | - // Update services with existing positions. |
68 | - // In the future it would be better to sync |
69 | - // the model to the existing box. |
70 | - var existing = this.service_boxes[service.id]; |
71 | - if (existing) { |
72 | - service.pos = existing.pos; |
73 | - service.inDrag = existing.inDrag; |
74 | - } |
75 | - service.margins(service.subordinate ? |
76 | - this.subordinate_margins : |
77 | - this.service_margins); |
78 | - this.service_boxes[service.id] = service; |
79 | - }, this); |
80 | + |
81 | + views.toBoundingBoxes(this, db.services, this.service_boxes); |
82 | + this.services = Y.Object.values(this.service_boxes); |
83 | |
84 | // XXX: containment breaking alias, do we need this? |
85 | topo.service_boxes = this.service_boxes; |
86 | |
87 | // Nodes are mapped by modelId tuples. |
88 | this.node = vis.selectAll('.service') |
89 | - .data(services, function(d) {return d.modelId();}); |
90 | + .data(this.services, function(d) {return d.modelId;}); |
91 | }, |
92 | |
93 | /** |
94 | @@ -363,7 +348,7 @@ |
95 | } |
96 | |
97 | selection.attr('transform', function(d, i) { |
98 | - return d.translateStr(); |
99 | + return d.translateStr; |
100 | }); |
101 | if (topo.get('active_service') === d) { |
102 | self.updateServiceMenuLocation(); |
103 | @@ -443,7 +428,7 @@ |
104 | }) |
105 | .call(this.dragBehavior) |
106 | .attr('transform', function(d) { |
107 | - return d.translateStr(); |
108 | + return d.translateStr; |
109 | }) |
110 | .call(self.createServiceNode); |
111 | |
112 | @@ -529,7 +514,7 @@ |
113 | // binding as the event handler will |
114 | // use that index. |
115 | node.each(function(d) { |
116 | - var service = topo.serviceForBox(d), |
117 | + var service = d.model, |
118 | annotations = service.get('annotations'), |
119 | x, y; |
120 | |
121 | @@ -667,7 +652,7 @@ |
122 | return d.w / 10 * 7; |
123 | }) |
124 | .attr('y', function(d) { |
125 | - return d.getRelativeCenter()[1] - (d.w / 6) / 2; |
126 | + return d.relativeCenter[1] - (d.w / 6) / 2; |
127 | }) |
128 | .append('title') |
129 | .text(function(d) { |
130 | @@ -712,7 +697,7 @@ |
131 | |
132 | node.select('.service-status') |
133 | .attr('transform', function(d) { |
134 | - return 'translate(' + d.getRelativeCenter() + ')'; |
135 | + return 'translate(' + d.relativeCenter + ')'; |
136 | }); |
137 | node.select('.service-health-mask') |
138 | .attr('width', function(d) { |
139 | @@ -838,7 +823,7 @@ |
140 | var cp_width = cp.getClientRect().width, |
141 | menu_left = service.x * z + service.w * z / 2 < |
142 | this.width * z / 2, |
143 | - service_center = service.getRelativeCenter(); |
144 | + service_center = service.relativeCenter; |
145 | if (menu_left) { |
146 | cp.removeClass('left') |
147 | .addClass('right'); |
148 | @@ -896,7 +881,7 @@ |
149 | showServiceMenu: function(box, module, context) { |
150 | var serviceMenu = module.get('container').one('#service-menu'); |
151 | var topo = module.get('component'); |
152 | - var service = topo.serviceForBox(box); |
153 | + var service = box.model; |
154 | |
155 | if (box && !serviceMenu.hasClass('active')) { |
156 | topo.set('active_service', box); |
157 | @@ -950,12 +935,12 @@ |
158 | */ |
159 | destroyServiceConfirm: function(m, view) { |
160 | // Set service in view. |
161 | - view.set('destroy_service', m); |
162 | + view.set('destroy_service', m.model); |
163 | |
164 | // Show dialog. |
165 | view.set('destroy_dialog', views.createModalPanel( |
166 | 'Are you sure you want to destroy the service? ' + |
167 | - 'This cannot be undone.', |
168 | + 'This cannot be undone.', |
169 | '#destroy-modal-panel', |
170 | 'Destroy Service', |
171 | Y.bind(function(ev) { |
172 | @@ -963,9 +948,8 @@ |
173 | var btn = ev.target; |
174 | btn.set('disabled', true); |
175 | view.service_click_actions |
176 | - .destroyService(m, view, btn); |
177 | - }, |
178 | - this))); |
179 | + .destroyService(m, view, btn); |
180 | + }, this))); |
181 | }, |
182 | |
183 | /* |
184 | @@ -976,15 +960,14 @@ |
185 | destroyService: function(m, view, btn) { |
186 | var env = view.get('component').get('env'), |
187 | service = view.get('destroy_service'); |
188 | - env.destroy_service( |
189 | - service.get('id'), |
190 | - Y.bind(this._destroyCallback, view, |
191 | - service, view, btn)); |
192 | + env.destroy_service(service.get('id'), |
193 | + Y.bind(this._destroyCallback, view, |
194 | + service, view, btn)); |
195 | }, |
196 | |
197 | _destroyCallback: function(service, view, btn, ev) { |
198 | var getModelURL = view.get('component').get('getModelURL'), |
199 | - db = view.get('component').get('db'); |
200 | + db = view.get('component').get('db'); |
201 | if (ev.err) { |
202 | db.notifications.add( |
203 | new models.Notification({ |
204 | @@ -993,8 +976,7 @@ |
205 | level: 'error', |
206 | link: getModelURL(service), |
207 | modelId: service |
208 | - }) |
209 | - ); |
210 | + })); |
211 | } else { |
212 | var relations = db.relations.get_relations_for_service(service); |
213 | Y.each(relations, function(relation) { |
214 | @@ -1007,7 +989,6 @@ |
215 | btn.set('disabled', false); |
216 | } |
217 | |
218 | - |
219 | } |
220 | }, { |
221 | ATTRS: {} |
222 | |
223 | === modified file 'app/views/utils.js' |
224 | --- app/views/utils.js 2013-01-24 17:43:57 +0000 |
225 | +++ app/views/utils.js 2013-01-31 16:56:20 +0000 |
226 | @@ -607,224 +607,277 @@ |
227 | }; |
228 | |
229 | |
230 | - |
231 | /* |
232 | - * Utility class that encapsulates Y.Models and keeps their positional |
233 | + * Utility class that encapsulates Y.Models and keeps their position |
234 | * state within an svg canvas. |
235 | * |
236 | * As a convenience attributes of the encapsulated model are exposed |
237 | * directly as attributes. |
238 | */ |
239 | - function BoundingBox() { |
240 | - var x, y, w, h, value, modelId, boxMargins; |
241 | - function Box() {} |
242 | - |
243 | - Box.model = function(_) { |
244 | - if (!arguments.length) { |
245 | - return modelId; |
246 | - } |
247 | - modelId = [_.name, _.get('id')]; |
248 | - |
249 | - // Copy all the attrs from model to Box |
250 | - Y.mix(Box, _.getAttrs()); |
251 | - return Box; |
252 | - }; |
253 | - |
254 | - Box.margins = function(_) { |
255 | - if (!arguments.length) { |
256 | - return boxMargins; |
257 | - } |
258 | - boxMargins = _; |
259 | - return Box; |
260 | - }; |
261 | - |
262 | - Object.defineProperties(Box, { |
263 | - pos: { |
264 | - writeable: true, |
265 | - get: function() { |
266 | - return {x: this.x, y: this.y, w: this.w, h: this.h}; |
267 | - }, |
268 | - set: function(value) { |
269 | - Y.mix(this, value, true, ['x', 'y', 'w', 'h']); |
270 | - } |
271 | - }, |
272 | - x: { |
273 | - writeable: true, |
274 | - get: function() { return x;}, |
275 | - set: function(value) { |
276 | - this.px = this.x; |
277 | - x = value; |
278 | - return this;} |
279 | - }, |
280 | - y: { |
281 | - writeable: true, |
282 | - get: function() { return y;}, |
283 | - set: function(value) { |
284 | - this.py = this.y; |
285 | - y = value; |
286 | - return this; |
287 | - } |
288 | - } |
289 | - }); |
290 | - |
291 | - Box.getXY = function() {return [this.x, this.y];}; |
292 | - Box.getWH = function() {return [this.w, this.h];}; |
293 | + var _box = {}; |
294 | + function positionProp(name) { |
295 | + return { |
296 | + get: function() {return this['_' + name];}, |
297 | + set: function(value) { |
298 | + this['p' + name] = this['_' + name]; |
299 | + this['_' + name] = value; |
300 | + } |
301 | + }; |
302 | + } |
303 | + |
304 | + Object.defineProperties(_box, { |
305 | + x: positionProp('x'), |
306 | + y: positionProp('y'), |
307 | + w: positionProp('w'), |
308 | + h: positionProp('h'), |
309 | + |
310 | + pos: { |
311 | + get: function() { return {x: this.x, y: this.y, w: this.w, h: this.h};}, |
312 | + set: function(value) { |
313 | + Y.mix(this, value, true, ['x', 'y', 'w', 'h']); |
314 | + } |
315 | + }, |
316 | + |
317 | + translateStr: { |
318 | + get: function() { return 'translate(' + this.x + ',' + this.y + ')';} |
319 | + }, |
320 | + |
321 | + model: { |
322 | + get: function() { |
323 | + if (!this._modelName) { return null;} |
324 | + return this.topology.serviceForBox(this); |
325 | + }, |
326 | + set: function(value) { |
327 | + if (Y.Lang.isValue(value)) { |
328 | + Y.mix(this, value.getAttrs(), true); |
329 | + this._modelName = value.name; |
330 | + } |
331 | + } |
332 | + }, |
333 | + modelId: { |
334 | + get: function() { return this._modelName + '-' + this.id;} |
335 | + }, |
336 | + node: { |
337 | + get: function() { return this.module.getServiceNode(this.id);} |
338 | + }, |
339 | + topology: { |
340 | + get: function() { return this.module.get('component');} |
341 | + }, |
342 | + xy: { |
343 | + get: function() { return [this.x, this.y];} |
344 | + }, |
345 | + wh: { |
346 | + get: function() { return [this.w, this.h];} |
347 | + }, |
348 | + |
349 | + /* |
350 | + * Extract margins from the supplied module. |
351 | + */ |
352 | + margins: { |
353 | + get: function() { |
354 | + if (!this.module) { |
355 | + // Used in testing. |
356 | + return {top: 0, bottom: 0, left: 0, right: 0}; |
357 | + } |
358 | + if (this.subordinate) { |
359 | + return this.module.subordinate_margins; |
360 | + } |
361 | + return this.module.service_margins; |
362 | + } |
363 | + }, |
364 | |
365 | /* |
366 | * Returns the center of the box with the origin being the upper-left |
367 | * corner of the box. |
368 | */ |
369 | - Box.getRelativeCenter = function() { |
370 | - var margins = this.margins(); |
371 | - return [ |
372 | - (this.w / 2) + (margins && |
373 | - (margins.left * this.w / 2 - |
374 | - margins.right * this.w / 2) || 0), |
375 | - (this.h / 2) - (margins && |
376 | - (margins.bottom * this.h / 2 - |
377 | - margins.top * this.h / 2) || 0) |
378 | - ]; |
379 | - }; |
380 | + relativeCenter: { |
381 | + get: function() { |
382 | + var margins = this.margins; |
383 | + return [ |
384 | + (this.w / 2) + (margins && |
385 | + (margins.left * this.w / 2 - |
386 | + margins.right * this.w / 2) || 0), |
387 | + (this.h / 2) - (margins && |
388 | + (margins.bottom * this.h / 2 - |
389 | + margins.top * this.h / 2) || 0) |
390 | + ];} |
391 | + }, |
392 | |
393 | /* |
394 | * Returns the absolute center of the box on the canvas. |
395 | */ |
396 | - Box.getCenter = function() { |
397 | - var center = this.getRelativeCenter(); |
398 | - center[0] += this.x; |
399 | - center[1] += this.y; |
400 | - return center; |
401 | - }; |
402 | - |
403 | + center: { |
404 | + get: function() { |
405 | + var c = this.relativeCenter; |
406 | + c[0] += this.x; |
407 | + c[1] += this.y; |
408 | + return c; |
409 | + } |
410 | + }, |
411 | |
412 | /* |
413 | * Returns true if a given point in the form [x, y] is within the box. |
414 | + * Transform could be extracted from the topology but the current |
415 | + * arguments ease testing. |
416 | */ |
417 | - Box.containsPoint = function(point, transform) { |
418 | - transform = transform || { |
419 | - scale: function() { return 1; }, |
420 | - translate: function() { return [0, 0]; } |
421 | - }; |
422 | - var s = transform.scale(), tr = transform.translate(); |
423 | - if (point[0] >= this.x * s + tr[0] && |
424 | - point[0] <= this.x * s + this.w * s + tr[0] && |
425 | - point[1] >= this.y * s + tr[1] && |
426 | - point[1] <= this.y * s + this.h * s + tr[1]) { |
427 | - return true; |
428 | + containsPoint: { |
429 | + writable: true, // For test overrides. |
430 | + configurable: true, |
431 | + value: function(point, transform) { |
432 | + transform = transform || { |
433 | + scale: function() { return 1; }, |
434 | + translate: function() { return [0, 0]; } |
435 | + }; |
436 | + var tr = transform.translate(), |
437 | + s = transform.scale(); |
438 | + |
439 | + return (point[0] >= this.x * s + tr[0] && |
440 | + point[0] <= this.x * s + this.w * s + tr[0] && |
441 | + point[1] >= this.y * s + tr[1] && |
442 | + point[1] <= this.y * s + this.h * s + tr[1]); |
443 | } |
444 | - return false; |
445 | - }; |
446 | + }, |
447 | |
448 | /* |
449 | - * Return the 50% points along each side as xy pairs |
450 | + * Return the 50% points along each side as [x, y] pairs. |
451 | */ |
452 | - Box.getConnectors = function() { |
453 | - // Since the service nodes have a shadow that takes up a bit of |
454 | - // space on the sides and bottom of the actual node itself, add a bit |
455 | - // of a margin to the actual connecting points. The margin is specified |
456 | - // as a percentage of the width or height, as those are affected by the |
457 | - // scale. This is calculated by taking the distance of the shadow from |
458 | - // the edge of the actual shape and calculating it as a percentage of |
459 | - // the total height of the shape. |
460 | - var margins = this.margins(); |
461 | - return { |
462 | - top: [ |
463 | - this.x + (this.w / 2), |
464 | - this.y + (margins && (margins.top * this.h) || 0) |
465 | - ], |
466 | - right: [ |
467 | - this.x + this.w - (margins && (margins.right * this.w) || 0), |
468 | - this.y + (this.h / 2) - ( |
469 | - margins && (margins.bottom * this.h / 2 - |
470 | - margins.top * this.h / 2) || 0) |
471 | - ], |
472 | - bottom: [ |
473 | - this.x + (this.w / 2), |
474 | - this.y + this.h - (margins && (margins.bottom * this.h) || 0) |
475 | - ], |
476 | - left: [ |
477 | - this.x + (margins && (margins.left * this.w) || 0), |
478 | - this.y + (this.h / 2) - ( |
479 | - margins && (margins.bottom * this.h / 2 - |
480 | - margins.top * this.h / 2) || 0) |
481 | - ] |
482 | - }; |
483 | - }; |
484 | + connectors: { |
485 | + get: function() { |
486 | + // Since the service nodes have a shadow that takes up a bit of |
487 | + // space on the sides and bottom of the actual node itself, add a bit |
488 | + // of a margin to the actual connecting points. The margin is specified |
489 | + // as a percentage of the width or height, as those are affected by the |
490 | + // scale. This is calculated by taking the distance of the shadow from |
491 | + // the edge of the actual shape and calculating it as a percentage of |
492 | + // the total height of the shape. |
493 | + var margins = this.margins; |
494 | + return { |
495 | + top: [ |
496 | + this.x + (this.w / 2), |
497 | + this.y + (margins && (margins.top * this.h) || 0) |
498 | + ], |
499 | + right: [ |
500 | + this.x + this.w - (margins && (margins.right * this.w) || 0), |
501 | + this.y + (this.h / 2) - ( |
502 | + margins && (margins.bottom * this.h / 2 - |
503 | + margins.top * this.h / 2) || 0) |
504 | + ], |
505 | + bottom: [ |
506 | + this.x + (this.w / 2), |
507 | + this.y + this.h - (margins && (margins.bottom * this.h) || 0) |
508 | + ], |
509 | + left: [ |
510 | + this.x + (margins && (margins.left * this.w) || 0), |
511 | + this.y + (this.h / 2) - ( |
512 | + margins && (margins.bottom * this.h / 2 - |
513 | + margins.top * this.h / 2) || 0) |
514 | + ] |
515 | + }; |
516 | + } |
517 | + }, |
518 | |
519 | - Box._distance = function(xy1, xy2) { |
520 | - return Math.sqrt(Math.pow(xy1[0] - xy2[0], 2) + |
521 | - Math.pow(xy1[1] - xy2[1], 2)); |
522 | - }; |
523 | + _distance: { |
524 | + value: function(xy1, xy2) { |
525 | + return Math.sqrt(Math.pow(xy1[0] - xy2[0], 2) + |
526 | + Math.pow(xy1[1] - xy2[1], 2)); |
527 | + } |
528 | + }, |
529 | |
530 | /* |
531 | * Connectors are defined on four borders, find the one closes to |
532 | * another BoundingBox |
533 | */ |
534 | - Box.getNearestConnector = function(other_box) { |
535 | - var connectors = this.getConnectors(), |
536 | - result = null, |
537 | - shortest_d = Infinity, |
538 | - source = other_box; |
539 | - // duck typing |
540 | - if ('getXY' in other_box) { |
541 | - source = other_box.getXY(); |
542 | + getNearestConnector: { |
543 | + value: function(box_or_xy) { |
544 | + var connectors = this.connectors, |
545 | + result = null, |
546 | + shortest_d = Infinity, |
547 | + source = box_or_xy; |
548 | + |
549 | + if (box_or_xy.xy !== undefined) { |
550 | + source = box_or_xy.xy; |
551 | + } |
552 | + Y.each(connectors, function(ep) { |
553 | + // Take the distance of each XY pair |
554 | + var d = this._distance(source, ep); |
555 | + if (!Y.Lang.isValue(result) || d < shortest_d) { |
556 | + shortest_d = d; |
557 | + result = ep; |
558 | + } |
559 | + }, this); |
560 | + return result; |
561 | } |
562 | - |
563 | - Y.each(connectors, function(ep) { |
564 | - // Take the distance of each XY pair |
565 | - var d = this._distance(source, ep); |
566 | - if (!Y.Lang.isValue(result) || d < shortest_d) { |
567 | - shortest_d = d; |
568 | - result = ep; |
569 | - } |
570 | - }, this); |
571 | - return result; |
572 | - }; |
573 | + }, |
574 | |
575 | /* |
576 | * Return [this.connector.XY, other.connector.XY] (in that order) |
577 | * that as nearest to each other. This can be used to define start-end |
578 | * points for routing. |
579 | */ |
580 | - Box.getConnectorPair = function(other_box) { |
581 | - var sc = Box.getConnectors(), |
582 | - oc = other_box.getConnectors(), |
583 | - result = null, |
584 | - shortest_d = Infinity; |
585 | - |
586 | - Y.each(sc, function(ep1) { |
587 | - Y.each(oc, function(ep2) { |
588 | - // Take the distance of each XY pair |
589 | - var d = this._distance(ep1, ep2); |
590 | - if (!Y.Lang.isValue(result) || d < shortest_d) { |
591 | - shortest_d = d; |
592 | - result = [ep1, ep2]; |
593 | - } |
594 | - }, other_box); |
595 | - }, this); |
596 | - return result; |
597 | - }; |
598 | - |
599 | - Box.translateStr = function() { |
600 | - return 'translate(' + this.getXY() + ')'; |
601 | - }; |
602 | - |
603 | - Box.modelId = function() { |
604 | - return modelId[0] + '-' + modelId[1]; |
605 | - }; |
606 | - |
607 | - return Box; |
608 | + getConnectorPair: { |
609 | + value: function(other_box) { |
610 | + var sc = this.connectors, |
611 | + oc = other_box.connectors, |
612 | + result = null, |
613 | + shortest_d = Infinity; |
614 | + |
615 | + Y.each(sc, function(ep1) { |
616 | + Y.each(oc, function(ep2) { |
617 | + // Take the distance of each XY pair |
618 | + var d = this._distance(ep1, ep2); |
619 | + if (!Y.Lang.isValue(result) || d < shortest_d) { |
620 | + shortest_d = d; |
621 | + result = [ep1, ep2]; |
622 | + } |
623 | + }, other_box); |
624 | + }, this); |
625 | + return result; |
626 | + } |
627 | + } |
628 | + }); |
629 | + |
630 | + /** |
631 | + * @method BoundingBox |
632 | + * @param {Module} module Typically service module. |
633 | + * @param {Model} model Model object. |
634 | + * @return {BoundingBox} A Box model. |
635 | + */ |
636 | + function BoundingBox(module, model) { |
637 | + var b = Object.create(_box); |
638 | + b.module = module; |
639 | + b.model = model; |
640 | + |
641 | + return b; |
642 | } |
643 | |
644 | views.BoundingBox = BoundingBox; |
645 | |
646 | - views.toBoundingBox = function(model) { |
647 | - var box = new BoundingBox(); |
648 | - box.model(model); |
649 | - return box; |
650 | + /** |
651 | + * Covert an Array of services into BoundingBoxes. If |
652 | + * existing is supplied it should be a map of {id: box} |
653 | + * and will be updated in place by merging changed attribute |
654 | + * into the index. |
655 | + * |
656 | + * @param {ServiceModule} Module holding box canvas and context. |
657 | + * @param {ModelList} services Service modellist. |
658 | + * @param {Object} existing id:box mapping. |
659 | + * @return {Object} id:box mapping. |
660 | + **/ |
661 | + views.toBoundingBoxes = function(module, services, existing) { |
662 | + var result = existing || {}; |
663 | + Y.each(services, function() { |
664 | + var id = this.get('id'); |
665 | + if (result[id] !== undefined) { |
666 | + result[id].model = this; |
667 | + } else { |
668 | + result[id] = new BoundingBox(module, this); |
669 | + } |
670 | + }); |
671 | + return result; |
672 | }; |
673 | |
674 | |
675 | + |
676 | /** |
677 | * Decorate a relation with some related/derived data. |
678 | * |
679 | @@ -841,9 +894,9 @@ |
680 | source: source, |
681 | target: target, |
682 | compositeId: ( |
683 | - source.modelId() + |
684 | + source.modelId + |
685 | (hasRelations ? ':' + relation.endpoints[0][1].name : '') + |
686 | - '-' + target.modelId() + |
687 | + '-' + target.modelId + |
688 | (hasRelations ? ':' + relation.endpoints[1][1].name : '')) |
689 | }; |
690 | Y.mix(decorated, relation.getAttrs()); |
691 | |
692 | === modified file 'test/test_environment_view.js' |
693 | --- test/test_environment_view.js 2013-01-29 19:12:52 +0000 |
694 | +++ test/test_environment_view.js 2013-01-31 16:56:20 +0000 |
695 | @@ -789,7 +789,7 @@ |
696 | }); |
697 | |
698 | describe('view model support infrastructure', function() { |
699 | - var Y, views, models; |
700 | + var Y, views, models, module, service; |
701 | |
702 | before(function(done) { |
703 | Y = YUI(GlobalConfig).use(['juju-views', 'juju-models'], |
704 | @@ -800,48 +800,46 @@ |
705 | }); |
706 | }); |
707 | |
708 | + beforeEach(function() { |
709 | + service = new models.Service({ |
710 | + id: 'mediawiki', |
711 | + exposed: true}); |
712 | + module = { |
713 | + topology: { |
714 | + serviceForBox: function() {return service;} |
715 | + }}; |
716 | + }); |
717 | + |
718 | it('must be able to get us nearest connectors', |
719 | function() { |
720 | - var b1 = views.BoundingBox(), |
721 | - b2 = views.BoundingBox(); |
722 | + var b1 = views.BoundingBox(module, service), |
723 | + b2 = views.BoundingBox(module, service); |
724 | |
725 | - // raw poperty access |
726 | + // raw property access |
727 | b1.x = 0; b1.y = 0; |
728 | b1.w = 100; b1.h = 200; |
729 | |
730 | // Use pos to set b2 |
731 | b2.pos = {x: 200, y: 300, w: 100, h: 200}; |
732 | |
733 | - b1.getXY().should.eql([0, 0]); |
734 | - b2.getWH().should.eql([100, 200]); |
735 | + b1.xy.should.eql([0, 0]); |
736 | + b2.wh.should.eql([100, 200]); |
737 | |
738 | - b1.margins({ |
739 | - top: 0, |
740 | - bottom: 0, |
741 | - right: 0, |
742 | - left: 0 |
743 | - }); |
744 | b1.getNearestConnector([0, 0]); |
745 | |
746 | b1.getNearestConnector(b2).should |
747 | - .eql(b1.getConnectors().bottom); |
748 | + .eql(b1.connectors.bottom); |
749 | |
750 | - b2.margins({ |
751 | - top: 0, |
752 | - bottom: 0, |
753 | - right: 0, |
754 | - left: 0 |
755 | - }); |
756 | b2.getNearestConnector(b1).should |
757 | - .eql(b2.getConnectors().top); |
758 | + .eql(b2.connectors.top); |
759 | |
760 | b1.getConnectorPair(b2).should.eql([ |
761 | - b1.getConnectors().bottom, |
762 | - b2.getConnectors().top]); |
763 | + b1.connectors.bottom, |
764 | + b2.connectors.top]); |
765 | }); |
766 | |
767 | it('must be able to tell if a point is inside a box', function() { |
768 | - var b = views.BoundingBox(); |
769 | + var b = views.BoundingBox(module, service); |
770 | b.pos = {x: 100, y: 100, w: 50, h: 50}; |
771 | |
772 | b.containsPoint([125, 125]).should.equal(true); |
773 | @@ -850,10 +848,10 @@ |
774 | |
775 | it('must be able to save and restore old position information', |
776 | function() { |
777 | - var b1 = views.BoundingBox(), |
778 | - b2 = views.BoundingBox(); |
779 | + var b1 = views.BoundingBox(module, service), |
780 | + b2 = views.BoundingBox(module, service); |
781 | |
782 | - // raw poperty access |
783 | + // raw property access |
784 | b1.x = 0; b1.y = 0; |
785 | b1.w = 100; b1.h = 200; |
786 | |
787 | @@ -872,13 +870,10 @@ |
788 | |
789 | }); |
790 | |
791 | - it('must be able to access model attributes easily', function() { |
792 | - var service = new models.Service({id: 'mediawiki', |
793 | - exposed: true}), |
794 | - b1 = new views.BoundingBox(); |
795 | - b1.model(service); |
796 | + it('must be able to access model attributes', function() { |
797 | + var b1 = new views.BoundingBox(module, service); |
798 | |
799 | - b1.modelId().should.equal('service-mediawiki'); |
800 | + b1.modelId.should.equal('service-mediawiki'); |
801 | |
802 | // properties of the model have mapped to the box |
803 | b1.id.should.equal('mediawiki'); |
804 | @@ -887,36 +882,49 @@ |
805 | |
806 | it('must be able to update position data and not touch model data', |
807 | function() { |
808 | - var service = new models.Service({id: 'mediawiki', |
809 | - exposed: true}), |
810 | - b1 = new views.BoundingBox(); |
811 | - b1.model(service); |
812 | + var b1 = views.BoundingBox(module, service); |
813 | b1.x = 0; b1.y = 0; |
814 | b1.w = 100; b1.h = 200; |
815 | b1.id.should.equal('mediawiki'); |
816 | |
817 | // X/Y updated, other keys ignored |
818 | - b1.pos = {x: 100, y: 100, id: 'mediawiki'}; |
819 | + b1.pos = {x: 100, y: 100, id: 'blubber'}; |
820 | b1.x.should.equal(100); |
821 | b1.id.should.equal('mediawiki'); |
822 | - |
823 | }); |
824 | |
825 | it('must be able to map from sequence of models to boundingboxes', |
826 | function() { |
827 | var services = new models.ServiceList(); |
828 | services.add([{id: 'mysql'}, |
829 | - {id: 'haproxy'}, |
830 | - {id: 'memcache'}, |
831 | - {id: 'wordpress'}]); |
832 | - |
833 | - services.size().should.equal(4); |
834 | - var boxes = services.map(views.toBoundingBox); |
835 | - boxes.length.should.equal(4); |
836 | - boxes[0].id.should.equal('mysql'); |
837 | - boxes[3].id.should.equal('wordpress'); |
838 | - }); |
839 | - |
840 | + {id: 'haproxy'}, |
841 | + {id: 'memcache'}, |
842 | + {id: 'wordpress'}]); |
843 | + |
844 | + services.size().should.equal(4); |
845 | + var boxes = views.toBoundingBoxes(module, services); |
846 | + boxes.mysql.id.should.equal('mysql'); |
847 | + boxes.wordpress.id.should.equal('wordpress'); |
848 | + }); |
849 | + |
850 | + it('must be able to update boxes with new model data', |
851 | + function() { |
852 | + var services = new models.ServiceList(); |
853 | + services.add([{id: 'mysql', exposed: false}, |
854 | + {id: 'haproxy'}, |
855 | + {id: 'memcache'}, |
856 | + {id: 'wordpress'}]); |
857 | + |
858 | + services.size().should.equal(4); |
859 | + var boxes = views.toBoundingBoxes(module, services); |
860 | + var mysql = services.getById('mysql'); |
861 | + |
862 | + boxes.mysql.exposed.should.equal(false); |
863 | + mysql.set('exposed', true); |
864 | + |
865 | + // The third argument here implies an update. |
866 | + views.toBoundingBoxes(module, services, boxes); |
867 | + boxes.mysql.exposed.should.equal(true); |
868 | + }); |
869 | }); |
870 | - |
871 | })(); |
872 | |
873 | === modified file 'test/test_service_module.js' |
874 | --- test/test_service_module.js 2013-01-29 18:59:37 +0000 |
875 | +++ test/test_service_module.js 2013-01-31 16:56:20 +0000 |
876 | @@ -232,6 +232,20 @@ |
877 | cancelButton.simulate('click'); |
878 | }); |
879 | |
880 | + it('should prevent the Juju GUI service from being destroyed', function() { |
881 | + var service = db.services.add({ |
882 | + id: 'gui', |
883 | + charm: 'cs:precise/juju-gui-7' |
884 | + }); |
885 | + var box = views.BoundingBox(serviceModule, service); |
886 | + var menu = view.get('container').one('#service-menu'); |
887 | + view.topo.set('active_service', service); |
888 | + |
889 | + serviceModule.service_click_actions |
890 | + .toggleServiceMenu(box, serviceModule, serviceModule); |
891 | + menu.one('.destroy-service').hasClass('disabled').should.equal(true); |
892 | + }); |
893 | + |
894 | it('must not process service clicks after a dragend', function() { |
895 | // Test the work-around that prevents serviceClick from doing its work if |
896 | // called after dragend. Behaviour-driven testing via a tool such as |
897 | |
898 | === modified file 'test/test_topology.js' |
899 | --- test/test_topology.js 2013-01-28 19:45:48 +0000 |
900 | +++ test/test_topology.js 2013-01-31 16:56:20 +0000 |
901 | @@ -93,83 +93,4 @@ |
902 | Y.Lang.isValue(topo.vis).should.equal(true); |
903 | }); |
904 | |
905 | - it('should prevent the Juju GUI service from being destroyed', function() { |
906 | - var service = { |
907 | - charm: 'cs:precise/juju-gui-7' |
908 | - }; |
909 | - var fauxTopo = { |
910 | - get: function() { |
911 | - return null; |
912 | - }, |
913 | - serviceForBox: function() { |
914 | - return service; |
915 | - } |
916 | - }; |
917 | - // The context is used to do the destroying, so if it does not have the |
918 | - // destroy method, an exception will be raised if the service would be |
919 | - // destroyed. |
920 | - var context = { |
921 | - get: function(name) { |
922 | - if (name === 'component') { |
923 | - return fauxTopo; |
924 | - } |
925 | - }, |
926 | - service_click_actions: { |
927 | - hideServiceMenu: function() {}, |
928 | - destroyServiceConfirm: function() {} |
929 | - } |
930 | - }; |
931 | - topo.modules.ServiceModule.destroyServiceClick(undefined, context); |
932 | - }); |
933 | -}); |
934 | - |
935 | -describe('service menu', function() { |
936 | - var Y, views; |
937 | - |
938 | - before(function(done) { |
939 | - Y = YUI(GlobalConfig).use(['juju-topology'], function(Y) { |
940 | - views = Y.namespace('juju.views'); |
941 | - done(); |
942 | - }); |
943 | - }); |
944 | - |
945 | - it('should disable the "Destroy" menu for the Juju GUI service', function() { |
946 | - var service = { |
947 | - charm: 'cs:precise/juju-gui-7' |
948 | - }; |
949 | - var addedClassName; |
950 | - var menu = { |
951 | - hasClass: function() { |
952 | - return false; |
953 | - }, |
954 | - addClass: function() {}, |
955 | - one: function() { |
956 | - return { |
957 | - addClass: function(className) { |
958 | - addedClassName = className; |
959 | - } |
960 | - }; |
961 | - } |
962 | - }; |
963 | - var fauxView = { |
964 | - get: function(name) { |
965 | - if (name === 'container') { |
966 | - return {one: function() { return menu; }}; |
967 | - } else if (name === 'component') { |
968 | - return { |
969 | - set: function() {}, |
970 | - serviceForBox: function() { |
971 | - return service; |
972 | - } |
973 | - }; |
974 | - } |
975 | - }, |
976 | - updateServiceMenuLocation: function() {} |
977 | - }; |
978 | - var view = new views.ServiceModule(); |
979 | - view.service_click_actions.toggleServiceMenu( |
980 | - service, fauxView, undefined); |
981 | - assert.equal(addedClassName, 'disabled'); |
982 | - }); |
983 | - |
984 | }); |
985 | |
986 | === modified file 'undocumented' |
987 | --- undocumented 2013-01-25 17:51:29 +0000 |
988 | +++ undocumented 2013-01-31 16:56:20 +0000 |
989 | @@ -1,16 +1,16 @@ |
990 | -app/store/env.js:306 "status" |
991 | -app/store/env.js:31 "initializer" |
992 | -app/store/env.js:120 "_dispatch_rpc_result" |
993 | -app/store/env.js:107 "dispatch_result" |
994 | -app/store/env.js:112 "_dispatch_event" |
995 | -app/store/env.js:222 "get_charm" |
996 | -app/store/env.js:76 "on_message" |
997 | -app/store/env.js:72 "on_close" |
998 | -app/store/env.js:49 "destructor" |
999 | -app/store/env.js:226 "get_service" |
1000 | -app/store/env.js:70 "on_open" |
1001 | -app/store/env.js:54 "connect" |
1002 | -app/store/env.js:401 "get_endpoints" |
1003 | +app/store/env.js:73 "on_open" |
1004 | +app/store/env.js:229 "get_service" |
1005 | +app/store/env.js:115 "_dispatch_event" |
1006 | +app/store/env.js:79 "on_message" |
1007 | +app/store/env.js:404 "get_endpoints" |
1008 | +app/store/env.js:110 "dispatch_result" |
1009 | +app/store/env.js:309 "status" |
1010 | +app/store/env.js:75 "on_close" |
1011 | +app/store/env.js:57 "connect" |
1012 | +app/store/env.js:34 "initializer" |
1013 | +app/store/env.js:123 "_dispatch_rpc_result" |
1014 | +app/store/env.js:52 "destructor" |
1015 | +app/store/env.js:225 "get_charm" |
1016 | app/store/charm.js:24 "find" |
1017 | app/store/charm.js:11 "success" |
1018 | app/store/charm.js:65 "_normalizeCharms" |
1019 | @@ -25,32 +25,46 @@ |
1020 | app/store/notifications.js:25 "message" |
1021 | app/store/notifications.js:129 "title" |
1022 | app/store/notifications.js:137 "message" |
1023 | +app/views/utils.js:732 "scale" |
1024 | app/views/utils.js:166 "substitute" |
1025 | -app/views/utils.js:569 "isFloat" |
1026 | app/views/utils.js:251 "hasSVGClass" |
1027 | +app/views/utils.js:637 "get" |
1028 | app/views/utils.js:296 "toggleSVGClass" |
1029 | -app/views/utils.js:646 "set" |
1030 | -app/views/utils.js:645 "get" |
1031 | +app/views/utils.js:784 "value" |
1032 | +app/views/utils.js:698 "get" |
1033 | app/views/utils.js:135 "noop" |
1034 | -app/views/utils.js:653 "get" |
1035 | app/views/utils.js:259 "addSVGClass" |
1036 | +app/views/utils.js:577 "isFloat" |
1037 | +app/views/utils.js:218 "renderable_charm" |
1038 | app/views/utils.js:138 "console" |
1039 | -app/views/utils.js:218 "renderable_charm" |
1040 | +app/views/utils.js:730 "value" |
1041 | app/views/utils.js:113 "noop" |
1042 | app/views/utils.js:280 "removeSVGClass" |
1043 | +app/views/utils.js:714 "get" |
1044 | app/views/utils.js:371 "_addAlertMessage" |
1045 | -app/views/utils.js:698 "translate" |
1046 | app/views/utils.js:228 "humanizeNumber" |
1047 | -app/views/utils.js:654 "set" |
1048 | app/views/utils.js:339 "action" |
1049 | +app/views/utils.js:618 "positionProp" |
1050 | app/views/utils.js:195 "bindModelView" |
1051 | -app/views/utils.js:565 "isInt" |
1052 | -app/views/utils.js:610 "BoundingBox" |
1053 | -app/views/utils.js:639 "set" |
1054 | +app/views/utils.js:667 "get" |
1055 | +app/views/utils.js:644 "get" |
1056 | +app/views/utils.js:622 "set" |
1057 | +app/views/utils.js:573 "isInt" |
1058 | +app/views/utils.js:638 "set" |
1059 | +app/views/utils.js:822 "value" |
1060 | +app/views/utils.js:670 "get" |
1061 | +app/views/utils.js:673 "get" |
1062 | +app/views/utils.js:749 "get" |
1063 | +app/views/utils.js:733 "translate" |
1064 | +app/views/utils.js:681 "get" |
1065 | +app/views/utils.js:621 "get" |
1066 | app/views/utils.js:419 "invokeCallback" |
1067 | -app/views/utils.js:612 "Box" |
1068 | +app/views/utils.js:661 "get" |
1069 | +app/views/utils.js:653 "set" |
1070 | +app/views/utils.js:664 "get" |
1071 | +app/views/utils.js:649 "get" |
1072 | app/views/utils.js:132 "native" |
1073 | -app/views/utils.js:697 "scale" |
1074 | +app/views/utils.js:795 "value" |
1075 | app/views/environment.js:24 "initializer" |
1076 | app/views/charm-panel.js:1168 "calculatePanelPosition" |
1077 | app/views/charm-panel.js:476 "initializer" |
1078 | @@ -103,35 +117,35 @@ |
1079 | app/views/unit.js:292 "retryRelation" |
1080 | app/views/unit.js:157 "confirmRemoved" |
1081 | app/views/unit.js:185 "_removeUnitCallback" |
1082 | -app/views/service.js:875 "render" |
1083 | -app/views/service.js:338 "fitToWindow" |
1084 | -app/views/service.js:688 "render" |
1085 | -app/views/service.js:288 "initializer" |
1086 | -app/views/service.js:740 "saveConfig" |
1087 | -app/views/service.js:191 "_destroyCallback" |
1088 | +app/views/service.js:263 "_exposeServiceCallback" |
1089 | +app/views/service.js:501 "updateConstraints" |
1090 | +app/views/service.js:338 "getHeight" |
1091 | +app/views/service.js:229 "unexposeService" |
1092 | app/views/service.js:116 "_removeUnitCallback" |
1093 | -app/views/service.js:264 "_exposeServiceCallback" |
1094 | -app/views/service.js:257 "exposeService" |
1095 | -app/views/service.js:770 "_setConfigCallback" |
1096 | +app/views/service.js:405 "render" |
1097 | +app/views/service.js:699 "showErrors" |
1098 | +app/views/service.js:769 "_setConfigCallback" |
1099 | +app/views/service.js:874 "render" |
1100 | +app/views/service.js:417 "confirmRemoved" |
1101 | app/views/service.js:52 "_modifyUnits" |
1102 | -app/views/service.js:406 "render" |
1103 | -app/views/service.js:418 "confirmRemoved" |
1104 | -app/views/service.js:339 "getHeight" |
1105 | -app/views/service.js:182 "destroyService" |
1106 | -app/views/service.js:440 "doRemoveRelation" |
1107 | -app/views/service.js:887 "filterUnits" |
1108 | -app/views/service.js:615 "render" |
1109 | +app/views/service.js:256 "exposeService" |
1110 | +app/views/service.js:181 "destroyService" |
1111 | +app/views/service.js:614 "render" |
1112 | +app/views/service.js:527 "_setConstraintsCallback" |
1113 | +app/views/service.js:287 "initializer" |
1114 | +app/views/service.js:687 "render" |
1115 | app/views/service.js:30 "modifyUnits" |
1116 | -app/views/service.js:300 "getServiceTabs" |
1117 | -app/views/service.js:166 "confirmDestroy" |
1118 | -app/views/service.js:502 "updateConstraints" |
1119 | -app/views/service.js:528 "_setConstraintsCallback" |
1120 | +app/views/service.js:739 "saveConfig" |
1121 | +app/views/service.js:886 "filterUnits" |
1122 | +app/views/service.js:190 "_destroyCallback" |
1123 | +app/views/service.js:466 "_removeRelationCallback" |
1124 | +app/views/service.js:236 "_unexposeServiceCallback" |
1125 | +app/views/service.js:337 "fitToWindow" |
1126 | app/views/service.js:88 "_addUnitCallback" |
1127 | app/views/service.js:23 "resetUnits" |
1128 | -app/views/service.js:700 "showErrors" |
1129 | -app/views/service.js:230 "unexposeService" |
1130 | -app/views/service.js:237 "_unexposeServiceCallback" |
1131 | -app/views/service.js:467 "_removeRelationCallback" |
1132 | +app/views/service.js:165 "confirmDestroy" |
1133 | +app/views/service.js:299 "getServiceTabs" |
1134 | +app/views/service.js:439 "doRemoveRelation" |
1135 | app/views/topology/panzoom.js:151 "rescale" |
1136 | app/views/topology/panzoom.js:77 "zoomHandler" |
1137 | app/views/topology/panzoom.js:47 "renderSlider" |
1138 | @@ -140,55 +154,55 @@ |
1139 | app/views/topology/panzoom.js:123 "_fire_zoom" |
1140 | app/views/topology/panzoom.js:105 "zoom_out" |
1141 | app/views/topology/panzoom.js:33 "componentBound" |
1142 | -app/views/topology/relation.js:269 "addRelButtonClicked" |
1143 | -app/views/topology/relation.js:362 "snapOutOfService" |
1144 | -app/views/topology/relation.js:514 "cancelRelationBuild" |
1145 | -app/views/topology/relation.js:602 "ambiguousAddRelationCheck" |
1146 | +app/views/topology/relation.js:836 "relationClick" |
1147 | +app/views/topology/relation.js:517 "cancelRelationBuild" |
1148 | +app/views/topology/relation.js:429 "addRelationDragEnd" |
1149 | app/views/topology/relation.js:55 "initializer" |
1150 | -app/views/topology/relation.js:247 "updateSubordinateRelationsCount" |
1151 | -app/views/topology/relation.js:262 "draglineClicked" |
1152 | -app/views/topology/relation.js:169 "drawRelationGroup" |
1153 | -app/views/topology/relation.js:123 "updateLinks" |
1154 | +app/views/topology/relation.js:593 "addRelationStart" |
1155 | +app/views/topology/relation.js:367 "snapOutOfService" |
1156 | app/views/topology/relation.js:65 "update" |
1157 | -app/views/topology/relation.js:378 "addRelationDragStart" |
1158 | -app/views/topology/relation.js:85 "renderedHandler" |
1159 | -app/views/topology/relation.js:290 "addRelation" |
1160 | +app/views/topology/relation.js:274 "addRelButtonClicked" |
1161 | +app/views/topology/relation.js:237 "drawRelation" |
1162 | +app/views/topology/relation.js:84 "renderedHandler" |
1163 | +app/views/topology/relation.js:460 "_removeRelationCallback" |
1164 | +app/views/topology/relation.js:295 "addRelation" |
1165 | +app/views/topology/relation.js:174 "drawRelationGroup" |
1166 | +app/views/topology/relation.js:60 "render" |
1167 | +app/views/topology/relation.js:689 "addRelationEnd" |
1168 | +app/views/topology/relation.js:267 "draglineClicked" |
1169 | +app/views/topology/relation.js:485 "removeRelationConfirm" |
1170 | +app/views/topology/relation.js:409 "addRelationDrag" |
1171 | +app/views/topology/relation.js:88 "processRelation" |
1172 | +app/views/topology/relation.js:772 "subordinateRelationsForService" |
1173 | +app/views/topology/relation.js:728 "_addRelationCallback" |
1174 | +app/views/topology/relation.js:605 "ambiguousAddRelationCheck" |
1175 | +app/views/topology/relation.js:252 "updateSubordinateRelationsCount" |
1176 | +app/views/topology/relation.js:338 "snapToService" |
1177 | +app/views/topology/relation.js:779 "subRelBlockMouseEnter" |
1178 | +app/views/topology/relation.js:383 "addRelationDragStart" |
1179 | +app/views/topology/relation.js:128 "updateLinks" |
1180 | app/views/topology/relation.js:448 "removeRelation" |
1181 | -app/views/topology/relation.js:778 "subRelBlockMouseEnter" |
1182 | -app/views/topology/relation.js:835 "relationClick" |
1183 | -app/views/topology/relation.js:60 "render" |
1184 | -app/views/topology/relation.js:403 "addRelationDrag" |
1185 | -app/views/topology/relation.js:727 "_addRelationCallback" |
1186 | -app/views/topology/relation.js:590 "addRelationStart" |
1187 | -app/views/topology/relation.js:89 "processRelation" |
1188 | -app/views/topology/relation.js:232 "drawRelation" |
1189 | -app/views/topology/relation.js:771 "subordinateRelationsForService" |
1190 | -app/views/topology/relation.js:457 "_removeRelationCallback" |
1191 | -app/views/topology/relation.js:333 "snapToService" |
1192 | -app/views/topology/relation.js:423 "addRelationDragEnd" |
1193 | -app/views/topology/relation.js:686 "addRelationEnd" |
1194 | -app/views/topology/relation.js:787 "subRelBlockMouseLeave" |
1195 | -app/views/topology/relation.js:482 "removeRelationConfirm" |
1196 | -app/views/topology/service.js:966 "destroyService" |
1197 | -app/views/topology/service.js:758 "fade" |
1198 | -app/views/topology/service.js:777 "renderedHandler" |
1199 | -app/views/topology/service.js:934 "show_service" |
1200 | -app/views/topology/service.js:136 "serviceMouseLeave" |
1201 | -app/views/topology/service.js:108 "serviceDblClick" |
1202 | -app/views/topology/service.js:746 "show" |
1203 | -app/views/topology/service.js:752 "hide" |
1204 | -app/views/topology/service.js:214 "serviceAddRelMouseDown" |
1205 | -app/views/topology/service.js:380 "update" |
1206 | -app/views/topology/service.js:242 "updateData" |
1207 | -app/views/topology/service.js:975 "_destroyCallback" |
1208 | -app/views/topology/service.js:74 "initializer" |
1209 | -app/views/topology/service.js:84 "serviceClick" |
1210 | -app/views/topology/service.js:825 "updateServiceMenuLocation" |
1211 | -app/views/topology/service.js:233 "serviceAddRelMouseUp" |
1212 | -app/views/topology/service.js:172 "serviceStatusMouseOut" |
1213 | -app/views/topology/service.js:288 "dragend" |
1214 | -app/views/topology/service.js:118 "serviceMouseEnter" |
1215 | -app/views/topology/service.js:943 "destroyServiceConfirm" |
1216 | +app/views/topology/relation.js:788 "subRelBlockMouseLeave" |
1217 | +app/views/topology/service.js:893 "show_service" |
1218 | +app/views/topology/service.js:112 "serviceMouseEnter" |
1219 | +app/views/topology/service.js:731 "hide" |
1220 | +app/views/topology/service.js:208 "serviceAddRelMouseDown" |
1221 | +app/views/topology/service.js:130 "serviceMouseLeave" |
1222 | +app/views/topology/service.js:737 "fade" |
1223 | +app/views/topology/service.js:166 "serviceStatusMouseOut" |
1224 | +app/views/topology/service.js:78 "serviceClick" |
1225 | +app/views/topology/service.js:68 "initializer" |
1226 | +app/views/topology/service.js:267 "dragend" |
1227 | +app/views/topology/service.js:227 "serviceAddRelMouseUp" |
1228 | +app/views/topology/service.js:784 "updateServiceMenuLocation" |
1229 | +app/views/topology/service.js:725 "show" |
1230 | +app/views/topology/service.js:102 "serviceDblClick" |
1231 | +app/views/topology/service.js:924 "destroyService" |
1232 | +app/views/topology/service.js:359 "update" |
1233 | +app/views/topology/service.js:902 "destroyServiceConfirm" |
1234 | +app/views/topology/service.js:236 "updateData" |
1235 | +app/views/topology/service.js:932 "_destroyCallback" |
1236 | +app/views/topology/service.js:756 "renderedHandler" |
1237 | app/views/topology/topology.js:163 "getter" |
1238 | app/views/topology/topology.js:172 "setter" |
1239 | app/views/topology/topology.js:159 "getter" |
1240 | @@ -199,7 +213,6 @@ |
1241 | app/views/topology/topology.js:177 "setter" |
1242 | app/views/topology/topology.js:26 "initializer" |
1243 | app/views/topology/topology.js:63 "renderOnce" |
1244 | -app/views/topology/viewport.js:31 "resized" |
1245 | app/models/models.js:357 "getNotificationsForModel" |
1246 | app/models/models.js:429 "getModelListByModelName" |
1247 | app/models/models.js:420 "getModelById" |
1248 | @@ -224,11 +237,11 @@ |
1249 | app/models/models.js:239 "has_relation_for_endpoint" |
1250 | app/models/models.js:324 "add" |
1251 | app/models/models.js:338 "trim" |
1252 | -app/models/endpoints.js:40 "add" |
1253 | -app/models/endpoints.js:29 "convert" |
1254 | -app/models/charm.js:155 "validator" |
1255 | -app/models/charm.js:113 "parse" |
1256 | -app/models/charm.js:77 "sync" |
1257 | -app/models/charm.js:105 "failure" |
1258 | -app/models/charm.js:48 "initializer" |
1259 | -app/models/charm.js:129 "compare" |
1260 | +app/models/endpoints.js:42 "add" |
1261 | +app/models/endpoints.js:30 "convert" |
1262 | +app/models/charm.js:157 "validator" |
1263 | +app/models/charm.js:115 "parse" |
1264 | +app/models/charm.js:79 "sync" |
1265 | +app/models/charm.js:131 "compare" |
1266 | +app/models/charm.js:107 "failure" |
1267 | +app/models/charm.js:50 "initializer" |
Reviewers: mp+145755_ code.launchpad. net,
Message:
Please take a look.
Description:
View model improvements
Change BoundingBoxes to retain reference to module. This allows box
models
to directly resolve the db.Model backing them. This also allow access to
the
DOMNode in the canvas directly. Certain features of box directly depend
on
methods being available in the passed in module, mocking this is still
possible and shown in tests.
https:/ /code.launchpad .net/~bcsaller/ juju-gui/ viewmodel- improvements/ +merge/ 145755
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/7231067/
Affected files: service. js topology/ service. js environment_ view.js service_ module. js topology. js
A [revision details]
M app/views/
M app/views/
M app/views/utils.js
M test/test_
M test/test_
M test/test_
M undocumented