LEFT | RIGHT |
1 /* | 1 /* |
2 This file is part of the Juju GUI, which lets users view and manage Juju | 2 This file is part of the Juju GUI, which lets users view and manage Juju |
3 environments within a graphical interface (https://launchpad.net/juju-gui). | 3 environments within a graphical interface (https://launchpad.net/juju-gui). |
4 Copyright (C) 2012-2013 Canonical Ltd. | 4 Copyright (C) 2012-2013 Canonical Ltd. |
5 | 5 |
6 This program is free software: you can redistribute it and/or modify it under | 6 This program is free software: you can redistribute it and/or modify it under |
7 the terms of the GNU Affero General Public License version 3, as published by | 7 the terms of the GNU Affero General Public License version 3, as published by |
8 the Free Software Foundation. | 8 the Free Software Foundation. |
9 | 9 |
10 This program is distributed in the hope that it will be useful, but WITHOUT | 10 This program is distributed in the hope that it will be useful, but WITHOUT |
(...skipping 561 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 | 572 |
573 @method _onInitiateDestroy | 573 @method _onInitiateDestroy |
574 @param {Object} evt The event data. | 574 @param {Object} evt The event data. |
575 @return {undefined} Nothing. | 575 @return {undefined} Nothing. |
576 */ | 576 */ |
577 _onInitiateDestroy: function(evt) { | 577 _onInitiateDestroy: function(evt) { |
578 evt.halt(); | 578 evt.halt(); |
579 this.closeInspector(); | 579 this.closeInspector(); |
580 this.initiateServiceDestroy(); | 580 this.initiateServiceDestroy(); |
581 this.options.environment.topo.fire('clearState'); | 581 this.options.environment.topo.fire('clearState'); |
| 582 }, |
| 583 |
| 584 /** |
| 585 Keep checkboxes in sync with their textual representation. |
| 586 |
| 587 @method onCheckboxUpdate |
| 588 @param {Y.Event} ev the event from the change triggered. |
| 589 |
| 590 */ |
| 591 onCheckboxUpdate: function(ev) { |
| 592 var checked = ev.currentTarget.get('checked'); |
| 593 ev.currentTarget.ancestor('.toggle').one('.textvalue').set('text', |
| 594 checked); |
582 }, | 595 }, |
583 | 596 |
584 /** | 597 /** |
585 Handles exposing the service. | 598 Handles exposing the service. |
586 | 599 |
587 @method toggleExpose | 600 @method toggleExpose |
588 @param {Y.EventFacade} e An event object. | 601 @param {Y.EventFacade} e An event object. |
589 @return {undefined} Nothing. | 602 @return {undefined} Nothing. |
590 */ | 603 */ |
591 toggleExpose: function(e) { | 604 toggleExpose: function(e) { |
(...skipping 590 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1182 * @method _makeConflict | 1195 * @method _makeConflict |
1183 * @param {Y.Node} node of the input to mark. | 1196 * @param {Y.Node} node of the input to mark. |
1184 * | 1197 * |
1185 */ | 1198 */ |
1186 '_makeConflict': function(node) { | 1199 '_makeConflict': function(node) { |
1187 node.addClass('conflict'); | 1200 node.addClass('conflict'); |
1188 }, | 1201 }, |
1189 | 1202 |
1190 'changed': function(node, key, field) { | 1203 'changed': function(node, key, field) { |
1191 // Not all nodes need to show the conflict ux. This is true when | 1204 // Not all nodes need to show the conflict ux. This is true when |
1192 // multiple binds to a single model field are set, such as in pretty | 1205 // multiple binds to a single model field are set, such as in the |
1193 // toggle checkbox UI. | 1206 // checkbox widgets used in the inspector. |
1194 if (node.getData('skipconflictux')) { | 1207 if (node.getData('skipconflictux')) { |
1195 return; | 1208 return; |
1196 } | 1209 } |
1197 var modelValue = this.model.get(key); | 1210 var controls = this.container.one('.controls'); |
1198 var fieldValue = field.get(node); | 1211 if (this.changedValues[key]) { |
1199 if (modelValue !== fieldValue) { | |
1200 this._makeModified(node); | 1212 this._makeModified(node); |
| 1213 controls.removeClass('closed'); |
1201 } else { | 1214 } else { |
1202 this._clearModified(node); | 1215 this._clearModified(node); |
| 1216 // Databinding calls syncedFields if there are no more changed |
| 1217 // values, and that method is responsible for closing the controls. |
1203 } | 1218 } |
1204 }, | 1219 }, |
1205 | 1220 |
1206 'conflict': function(node, model, viewletName, resolve, binding) { | 1221 'conflict': function(node, model, viewletName, resolve, binding) { |
1207 // Not all nodes need to show the conflict ux. This is true when | 1222 // Not all nodes need to show the conflict ux. This is true when |
1208 // multiple binds to a single model field are set, such as in pretty | 1223 // multiple binds to a single model field are set, such as in the |
1209 // toggle checkbox UI. | 1224 // checkbox widgets used in the inspector. |
1210 if (node.getData('skipconflictux')) { | 1225 if (node.getData('skipconflictux')) { |
1211 return; | 1226 return; |
1212 } | 1227 } |
1213 /** | 1228 /** |
1214 Calls the databinding resolve method | 1229 Calls the databinding resolve method |
1215 @method sendResolve | 1230 @method sendResolve |
1216 */ | 1231 */ |
1217 var option; | 1232 var option; |
| 1233 var viewlet = this; |
1218 var key = node.getData('bind'); | 1234 var key = node.getData('bind'); |
1219 var modelValue = model.get(key); | 1235 var modelValue = model.get(key); |
1220 var field = binding.field; | 1236 var field = binding.field; |
1221 var wrapper = node.ancestor('.settings-wrapper'); | 1237 var wrapper = node.ancestor('.settings-wrapper'); |
1222 var resolver = wrapper.one('.resolver'); | 1238 var resolver = wrapper.one('.resolver'); |
1223 if (resolver) { | 1239 if (resolver) { |
1224 option = resolver.one('.config-field'); | 1240 option = resolver.one('.config-field'); |
1225 } | 1241 } |
1226 var handlers = []; | 1242 var handlers = []; |
1227 | 1243 |
| 1244 if (binding.annotations.conflict) { |
| 1245 binding.annotations.conflict.cancel(); |
| 1246 } |
| 1247 |
| 1248 binding.annotations.conflict = { |
| 1249 /** |
| 1250 Cancel this conflict handling UX. |
| 1251 |
| 1252 @method cancel |
| 1253 */ |
| 1254 cancel: function() { |
| 1255 handlers.forEach(function(h) { h.detach();}); |
| 1256 viewlet._clearModified(node); |
| 1257 viewlet._clearConflictPending(node); |
| 1258 viewlet._clearConflict(node); |
| 1259 resolver.addClass('hidden'); |
| 1260 delete binding.annotations.conflict; |
| 1261 } |
| 1262 }; |
1228 /** | 1263 /** |
1229 User selects one of the two conflicting values. | 1264 User selects one of the two conflicting values. |
1230 @method sendResolve | 1265 |
| 1266 @method sendResolve |
1231 */ | 1267 */ |
1232 function sendResolve(e) { | 1268 function sendResolve(e) { |
1233 e.halt(true); | 1269 e.halt(true); |
1234 var formValue = field.get(node); | 1270 binding.annotations.conflict.cancel(); |
1235 handlers.forEach(function(h) { h.detach();}); | 1271 if (e.currentTarget.hasClass('conflicted-env')) { |
1236 | 1272 resolve(modelValue); |
1237 /* jshint -W040 */ | 1273 } else { |
1238 // Ignore 'possible strict violation' | 1274 var formValue = field.get(node); |
1239 this._clearModified(node); | 1275 resolve(formValue); |
1240 this._clearConflict(node); | |
1241 | |
1242 if (resolver) { | |
1243 resolver.addClass('hidden'); | |
1244 } | 1276 } |
1245 | |
1246 if (e.currentTarget.hasClass('conflicted-env')) { | |
1247 resolve(node, viewletName, modelValue); | |
1248 } else { | |
1249 resolve(node, viewletName, formValue); | |
1250 } | |
1251 } | 1277 } |
1252 | 1278 |
1253 /** | 1279 /** |
1254 User selects a conflicting field, show the resolution UI | 1280 User selects a conflicting field, show the resolution UI |
1255 | 1281 |
1256 @method setupResolver | 1282 @method setupResolver |
1257 */ | 1283 */ |
1258 function setupResolver(e) { | 1284 function setupResolver(e) { |
1259 e.halt(true); | 1285 e.halt(true); |
1260 /* jshint -W040 */ | 1286 viewlet._clearConflictPending(node); |
1261 // Ignore 'possible strict violation' | 1287 viewlet._makeConflict(node); |
1262 this._clearConflictPending(node); | 1288 viewlet._makeConflict(option); |
1263 this._makeConflict(node); | 1289 option.setStyle('width', node.get('offsetWidth')); |
1264 // Checkboxes don't have the option node to select a value. | 1290 option.setHTML(modelValue); |
1265 if (option) { | 1291 resolver.removeClass('hidden'); |
1266 this._makeConflict(option); | |
1267 option.setStyle('width', node.get('offsetWidth')); | |
1268 option.setHTML(modelValue); | |
1269 } | |
1270 if (resolver) { | |
1271 resolver.removeClass('hidden'); | |
1272 } | |
1273 } | 1292 } |
1274 | 1293 |
1275 // On conflict just indicate. | 1294 // On conflict just indicate. |
1276 this._clearModified(node); | 1295 this._clearModified(node); |
1277 this._makeConflictPending(node); | 1296 this._makeConflictPending(node); |
1278 | 1297 |
1279 handlers.push(wrapper.delegate( | 1298 handlers.push(wrapper.delegate( |
1280 'click', | 1299 'click', setupResolver, '.conflict-pending')); |
1281 setupResolver, | 1300 handlers.push(wrapper.delegate('click', sendResolve, '.conflict')); |
1282 '.conflict-pending', | 1301 }, |
1283 this)); | 1302 |
1284 | 1303 'unsyncedFields': function() { |
1285 handlers.push(wrapper.delegate('click', sendResolve, | |
1286 '.conflict', this)); | |
1287 }, | |
1288 'unsyncedFields': function(dirtyFields) { | |
1289 var node = this.container.one('.controls .confirm'); | 1304 var node = this.container.one('.controls .confirm'); |
1290 if (!node.getData('originalText')) { | 1305 if (!node.getData('originalText')) { |
1291 node.setData('originalText', node.getHTML()); | 1306 node.setData('originalText', node.getHTML()); |
1292 } | 1307 } |
1293 node.setHTML('Overwrite'); | 1308 node.setHTML('Overwrite'); |
1294 }, | 1309 }, |
| 1310 |
1295 'syncedFields': function() { | 1311 'syncedFields': function() { |
1296 var node = this.container.one('.controls .confirm'); | 1312 var controls = this.container.one('.controls'); |
| 1313 var node = controls.one('.confirm'); |
1297 var title = node.getData('originalText'); | 1314 var title = node.getData('originalText'); |
| 1315 // For checkboxes remove their modified nodes. |
| 1316 this.container.all('.modified.boolean').remove(); |
| 1317 // All else remove their modified class |
1298 this.container.all('.modified').removeClass('modified'); | 1318 this.container.all('.modified').removeClass('modified'); |
1299 if (title) { | 1319 if (title) { |
1300 node.setHTML(title); | 1320 node.setHTML(title); |
1301 } | 1321 } |
| 1322 controls.addClass('closed'); |
1302 } | 1323 } |
1303 }; | 1324 }; |
1304 | 1325 |
1305 // Mixin Conflict Handling. | 1326 // Mixin Conflict Handling. |
1306 viewletNS.config = Y.merge(viewletNS.config, ConflictMixin); | 1327 viewletNS.config = Y.merge(viewletNS.config, ConflictMixin); |
1307 viewletNS.constraints = Y.merge(viewletNS.constraints, ConflictMixin); | 1328 viewletNS.constraints = Y.merge(viewletNS.constraints, ConflictMixin); |
1308 | 1329 |
1309 | 1330 |
1310 /** | 1331 /** |
1311 Service Inspector Viewlet Manager Controller | 1332 Service Inspector Viewlet Manager Controller |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1403 'panel', | 1424 'panel', |
1404 'transition', | 1425 'transition', |
1405 'view', | 1426 'view', |
1406 // Imported viewlets | 1427 // Imported viewlets |
1407 'viewlet-charm-details', | 1428 'viewlet-charm-details', |
1408 'viewlet-inspector-header', | 1429 'viewlet-inspector-header', |
1409 'viewlet-inspector-overview', | 1430 'viewlet-inspector-overview', |
1410 'viewlet-service-config', | 1431 'viewlet-service-config', |
1411 'viewlet-service-constraints', | 1432 'viewlet-service-constraints', |
1412 'viewlet-service-ghost', | 1433 'viewlet-service-ghost', |
1413 'viewlet-unit-details' | 1434 'viewlet-unit-details', |
| 1435 'viewlet-service-relations' |
1414 ] | 1436 ] |
1415 }); | 1437 }); |
1416 | 1438 |
LEFT | RIGHT |