Index: app/subapps/browser/views/charm.js
=== modified file 'app/subapps/browser/views/charm.js'
--- app/subapps/browser/views/charm.js 2013-07-17 18:27:38 +0000
+++ app/subapps/browser/views/charm.js 2013-07-18 14:23:40 +0000
@@ -177,6 +177,10 @@
return;
}
+ if (tabContent === 'Readme') {
+ this._loadReadmeTab();
+ return;
+ }
}, this)
);
},
@@ -407,6 +411,27 @@
},
/**
+ * Load Readme file content into the tab.
+ *
+ * @method _loadReadmeTab
+ */
+ _loadReadmeTab: function() {
+ // Start loading the readme so it's ready to go.
+ if (!this.loadedReadme) {
+ var tplNode = this.get('container');
+ var readme = this._locateReadme();
+
+ if (readme) {
+ this._loadFile(tplNode.one('#bws-readme'),
+ readme
+ );
+ } else {
+ this._noReadme(tplNode.one('#bws-readme'));
+ }
+ this.loadedReadme = true;
+ }
+ },
+ /**
Load the related charm data into the model for use.
@method _loadRelatedCharms
@@ -574,6 +599,7 @@
// Hold onto references of the indicators used so we can clean them all
// up. Indicators are keyed on their yuiid so we don't dupe them.
this.indicators = {};
+ this.loadedReadme = false;
this.loadedRelatedCharms = false;
this.loadedRelatedInterfaceCharms = false;
},
@@ -650,8 +676,7 @@
this.set('charm', charm);
var tplData = charm.getAttrs(),
- container = this.get('container'),
- sourceLink = this._getSourceLink();
+ container = this.get('container');
var link;
if (window.location.origin) {
@@ -661,9 +686,12 @@
this.get('charm').get('id');
}
tplData.isFullscreen = isFullscreen;
- tplData.sourceLink = sourceLink;
- tplData.prettyCommits = this._formatCommitsForHtml(
- tplData.recent_commits, sourceLink);
+ tplData.forInspector = this.get('forInspector');
+ if (!tplData.forInspector) {
+ tplData.sourceLink = this._getSourceLink();
+ tplData.prettyCommits = this._formatCommitsForHtml(
+ tplData.recent_commits, tplData.sourceLink);
+ }
tplData.interfaceIntro = this._getInterfaceIntroFlag(
tplData.requires, tplData.provides);
tplData.link = escape(link);
@@ -695,16 +723,6 @@
});
this._dispatchTabEvents(this.tabview);
- // Start loading the readme so it's ready to go.
- var readme = this._locateReadme();
-
- if (readme) {
- this._loadFile(tplNode.one('#bws-readme'),
- readme
- );
- } else {
- this._noReadme(tplNode.one('#bws-readme'));
- }
if (isFullscreen) {
if (!this.get('charm').get('relatedCharms')) {
@@ -726,7 +744,9 @@
// with .empty or something before rendering the charm view should work.
// But it doesn't so we scroll the nav bar into view, load the charm
// view at the top of the content.
- renderTo.one('.heading').scrollIntoView();
+ if (!tplData.forInspector) {
+ renderTo.one('.heading').scrollIntoView();
+ }
},
/**
@@ -790,6 +810,15 @@
charm: {},
/**
+ * @attribute forInspector
+ * @default {Boolean} false
+ * @type {Boolean}
+ */
+ forInspector: {
+ value: false
+ },
+
+ /**
@attribute isFullscreen
@default false
@type {Boolean}
Index: app/templates/serviceOverview.handlebars
=== modified file 'app/templates/serviceOverview.handlebars'
--- app/templates/serviceOverview.handlebars 2013-07-11 17:53:13 +0000
+++ app/templates/serviceOverview.handlebars 2013-07-17 19:51:05 +0000
@@ -3,7 +3,7 @@
-
+
Index: app/views/environment.js
=== modified file 'app/views/environment.js'
--- app/views/environment.js 2013-07-17 20:08:50 +0000
+++ app/views/environment.js 2013-07-18 14:23:40 +0000
@@ -170,6 +170,7 @@
configBase: {
db: this.topo.get('db'),
env: this.topo.get('env'),
+ store: this.topo.get('store'),
events: {
'.close': {'click': 'destroy'},
'.close-slot': {'click': 'hideSlot'}
@@ -180,26 +181,34 @@
'.tab': {'click': 'showViewlet'}
},
viewletEvents: {
- '.toggle-settings-help': { click: 'toggleSettingsHelp' },
- '.toggle-expose': { click: 'toggleExpose' },
+ 'a[data-unit]': { click: 'showUnitDetails'},
+ 'button.confirm': { click: 'saveConfig'},
+ '.cancel-destroy': {click: 'onCancelDestroy'},
+ '.charmURL': {click: 'onShowCharmDetails'},
'.config-file .fakebutton': { click: 'handleFileClick'},
'.config-file input[type=file]': { change: 'handleFileChange'},
- 'button.confirm': { click: 'saveConfig'},
+ '.destroy-service-icon': {click: 'onDestroyIcon'},
+ '.initiate-destroy': {click: 'onInitiateDestroy'},
'.num-units-control': {
- 'keydown': 'modifyUnits',
- 'blur': 'resetUnits'
+ keydown: 'modifyUnits',
+ blur: 'resetUnits'
},
- 'a[data-unit]': { click: 'showUnitDetails'},
- '.destroy-service-icon': {'click': 'onDestroyIcon'},
- '.initiate-destroy': {'click': 'onInitiateDestroy'},
- '.cancel-destroy': {'click': 'onCancelDestroy'},
- '.status-unit-header': {'click': 'toggleUnitHeader'},
- '.toggle-select-all': {'click': 'toggleSelectAllUnits'},
// Constraints viewlet events.
- '.save-constraints': {click: 'saveConstraints'}
+ '.save-constraints': {click: 'saveConstraints'},
+ '.status-unit-header': {click: 'toggleUnitHeader'},
+ '.toggle-expose': { click: 'toggleExpose' },
+ '.toggle-select-all': {click: 'toggleSelectAllUnits'},
+ '.toggle-settings-help': { click: 'toggleSettingsHelp' },
+ '.unit-details': { click: 'showUnit'}
},
- viewletList: ['overview', 'units', 'unit', 'config',
- 'constraints'],
+ viewletList: [
+ 'overview', // Default viewlet first.
+ 'charmDetails',
+ 'config',
+ 'constraints',
+ 'unit',
+ 'units'
+ ],
template: Y.juju.views.Templates['view-container']
},
configGhost: {
Index: test/index.html
=== modified file 'test/index.html'
--- test/index.html 2013-07-16 22:06:38 +0000
+++ test/index.html 2013-07-18 14:14:27 +0000
@@ -78,6 +78,7 @@
+
Index: app/views/service.js
=== modified file 'app/views/service.js'
--- app/views/service.js 2013-07-18 00:34:12 +0000
+++ app/views/service.js 2013-07-18 15:30:09 +0000
@@ -18,6 +18,7 @@
'use strict';
+
/**
* Provide the service views and mixins.
*
@@ -30,12 +31,12 @@
var ENTER = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.enter;
var ESC = Y.Node.DOM_EVENTS.key.eventDef.KEY_MAP.esc;
-
var views = Y.namespace('juju.views'),
Templates = views.Templates,
models = Y.namespace('juju.models'),
plugins = Y.namespace('juju.plugins'),
- utils = Y.namespace('juju.views.utils');
+ utils = Y.namespace('juju.views.utils'),
+ viewletNS = Y.namespace('juju.viewlets');
/**
* @class manageUnitsMixin
@@ -1573,6 +1574,21 @@
} else {
units.setAttribute('checked', 'checked');
}
+ },
+
+ /**
+ Loads the charm details view for the inspector.
+
+ @method onShowCharmDetails
+ @param {Event} ev the click event from the overview viewlet.
+
+ */
+ onShowCharmDetails: function(ev) {
+ ev.halt();
+ var db = this.inspector.get('db');
+ var charmId = ev.currentTarget.getAttribute('data-charmid');
+ var charm = db.charms.getById(charmId);
+ this.inspector.showViewlet('charmDetails', charm);
}
};
@@ -1812,7 +1828,7 @@
unitIPDescription: unit_ip_description,
relations: relations
};
- this.container = Y.Node.create(this.templateWrapper());
+ this.container = Y.Node.create(this.templateWrapper({}));
this.container.one('.content').setHTML(this.template(templateData));
}
},
@@ -1985,6 +2001,9 @@
}
};
+ // Add any imported viewlets into this DEFAULT_VIEWLETS from doom.
+ DEFAULT_VIEWLETS = Y.merge(DEFAULT_VIEWLETS, viewletNS);
+
// This variable is assigned an aggregate collection of methods and
// properties provided by various controller objects in the
// ServiceInspector constructor.
@@ -2074,5 +2093,7 @@
'event-key',
'transition',
'event-resize',
- 'json-stringify']
+ 'json-stringify',
+ // Imported viewlets
+ 'viewlet-charm-details']
});
Index: app/views/view-container.js
=== modified file 'app/views/view-container.js'
--- app/views/view-container.js 2013-07-18 00:34:12 +0000
+++ app/views/view-container.js 2013-07-18 15:08:29 +0000
@@ -328,6 +328,9 @@
viewletName = viewletName.currentTarget.getData('viewlet');
}
var viewlet = this.viewlets[viewletName];
+ if (!viewlet) {
+ console.warn('Attempted to load a viewlet that does not exist');
+ }
if (!model) {
model = this.get('model');
}
Index: app/views/viewlets/charm-details.js
=== added file 'app/views/viewlets/charm-details.js'
--- app/views/viewlets/charm-details.js 1970-01-01 00:00:00 +0000
+++ app/views/viewlets/charm-details.js 2013-07-18 15:17:46 +0000
@@ -0,0 +1,73 @@
+/*
+This file is part of the Juju GUI, which lets users view and manage Juju
+environments within a graphical interface (https://launchpad.net/juju-gui).
+Copyright (C) 2012-2013 Canonical Ltd.
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU Affero General Public License version 3, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License along
+with this program. If not, see
.
+*/
+
+'use strict';
+
+
+YUI.add('viewlet-charm-details', function(Y) {
+ var browserViews = Y.namespace('juju.browser.views'),
+ ns = Y.namespace('juju.viewlets'),
+ templates = Y.namespace('juju.views').Templates,
+ models = Y.namespace('juju.models');
+
+ ns.charmDetails = {
+ name: 'charmDetails',
+ slot: 'left-hand-panel',
+ templateWrapper: templates['left-breakout-panel'],
+ /**
+ Render the viewlet.
+
+ @method render
+ @param {Charm} charm An old charm model.
+ @param {Object} viewContainerAttrs This comes from the view-container
+ object.
+ */
+ render: function(charm, viewContainerAttrs) {
+ var store = viewContainerAttrs.store;
+ store.charm(charm.get('storeId'), {
+ 'success': function(data) {
+ var storeCharm = new models.BrowserCharm(data.charm);
+ var charmView = new browserViews.BrowserCharmView({
+ charm: storeCharm,
+ forInspector: true,
+ renderTo: this.container.one('.content'),
+ store: store
+ });
+ charmView.render();
+ },
+ 'failure': function(data) {
+ var charmView = new browserViews.BrowserCharmView({
+ charm: charm,
+ forInspector: true,
+ renderTo: this.container.one('.content'),
+ store: store
+ });
+ charmView.render();
+ }
+ }, this);
+ return this.templateWrapper({ initial: 'Loading...'});
+ }
+ };
+}, '0.0.1', {
+ requires: [
+ 'node',
+ 'subapp-browser-charmview',
+ 'juju-charm-models',
+ 'juju-view'
+ ]
+});
Index: test/test_inspector_charm.js
=== added file 'test/test_inspector_charm.js'
--- test/test_inspector_charm.js 1970-01-01 00:00:00 +0000
+++ test/test_inspector_charm.js 2013-07-18 14:23:40 +0000
@@ -0,0 +1,88 @@
+/*
+This file is part of the Juju GUI, which lets users view and manage Juju
+environments within a graphical interface (https://launchpad.net/juju-gui).
+Copyright (C) 2012-2013 Canonical Ltd.
+
+This program is free software: you can redistribute it and/or modify it under
+the terms of the GNU Affero General Public License version 3, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License along
+with this program. If not, see
.
+*/
+
+'use strict';
+
+describe('Inspector Charm', function() {
+ var charmID, container, fakeCharm, fakeStore, utils, viewlets, views, Y;
+
+ before(function(done) {
+ Y = YUI(GlobalConfig).use([
+ 'viewlet-charm-details',
+ 'juju-charm-store',
+ 'juju-tests-utils',
+ 'subapp-browser-views'
+ ], function(Y) {
+ utils = Y.namespace('juju-tests.utils');
+ viewlets = Y.namespace('juju.viewlets');
+ views = Y.namespace('juju.browser.views');
+
+ charmID = 'precise/apache2-10';
+ fakeCharm = {
+ get: function() {
+ return charmID;
+ }
+ };
+ window.flags = {serviceInspector: true};
+ done();
+ });
+ });
+
+ after(function() {
+ delete window.flags;
+ });
+
+ it('should ensure the viewlet exists', function() {
+ assert.equal(typeof viewlets.charmDetails, 'object');
+ });
+
+ it('renders the viewlet with a charm', function(done) {
+ var data = utils.loadFixture('data/browsercharm.json', false);
+ var testContainer = utils.makeContainer();
+
+ var fakeStore = new Y.juju.Charmworld2({});
+ fakeStore.set('datasource', {
+ sendRequest: function(params) {
+ // Stubbing the server callback value
+ params.callback.success({
+ response: {
+ results: [{
+ responseText: data
+ }]
+ }
+ });
+ }
+ });
+
+ views.BrowserCharmView = function(cfg) {
+ assert.isTrue(cfg.forInspector);
+ assert.equal(typeof cfg.store, 'object');
+ assert.equal(cfg.charm.get('id'), charmID);
+ return {
+ render: function() { done(); }
+ };
+ };
+ var viewletAttrs = {
+ store: fakeStore
+ };
+
+ viewlets.charmDetails.container = testContainer;
+ var content = viewlets.charmDetails.render(fakeCharm, viewletAttrs);
+ testContainer.setHTML('content');
+ });
+});
Index: test/test_view_container.js
=== modified file 'test/test_view_container.js'
--- test/test_view_container.js 2013-07-17 20:08:50 +0000
+++ test/test_view_container.js 2013-07-18 14:14:27 +0000
@@ -254,7 +254,7 @@
var constraints = viewContainer.viewlets.constraints;
constraints.render = function() {
this.container = Y.Node.create(
- juju.views.Templates['left-breakout-panel']());
+ juju.views.Templates['left-breakout-panel']({}));
};
viewContainer.showViewlet('constraints');