Simon Hunt
Committed by Gerrit Code Review

ONOS-2849: Refactored topo dialog to general DialogService. Implemented confirma…

…tion dialog in App view.

Change-Id: Ib20e98253b2d13f7d7debef2dea5a530b61ced99
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/*
18 + ONOS GUI -- Dialog Service -- CSS file
19 + */
20 +
21 +.dialog h2 {
22 + padding: 0 4px;
23 + margin: 0;
24 + word-wrap: break-word;
25 + display: inline-block;
26 + width: 210px;
27 + vertical-align: middle;
28 +}
29 +.light .dialog h2 {
30 + color: black;
31 +}
32 +.dark .dialog h2 {
33 + color: #ddd;
34 +}
35 +
36 +.dialog .dialog-button {
37 + display: inline-block;
38 + cursor: pointer;
39 + height: 20px;
40 + padding: 2px 6px;
41 + margin: 4px;
42 + float: right;
43 +}
44 +
45 +.light .dialog .dialog-button {
46 + background-color: #fec;
47 +}
48 +.dark .dialog .dialog-button {
49 + background-color: #369;
50 +}
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/*
18 + ONOS GUI -- Layer -- Dialog Service
19 +
20 + Builds on the panel service to provide dialog functionality.
21 + */
22 +(function () {
23 + 'use strict';
24 +
25 + // injected refs
26 + var $log, $window, fs, ps, bns;
27 +
28 + // configuration
29 + var defaultSettings = {
30 + width: 300,
31 + edge: 'left'
32 + };
33 +
34 + // internal state
35 + var pApi, panel, dApi;
36 +
37 + // TODO: ONOS-3741
38 + // TODO: ESC key invokes Cancel callback
39 + // TODO: Enter invokes OK callback
40 +
41 + // create the dialog; return its API
42 + function createDialog(id, opts) {
43 + var header, body, footer,
44 + settings = angular.extend({}, defaultSettings, opts),
45 + p = ps.createPanel(id, settings),
46 + cls = opts && opts.cssCls;
47 +
48 + p.classed('dialog', true);
49 + if (cls) {
50 + p.classed(cls, true);
51 + }
52 + panel = p;
53 +
54 + function reset() {
55 + p.empty();
56 + p.append('div').classed('header', true);
57 + p.append('div').classed('body', true);
58 + p.append('div').classed('footer', true);
59 +
60 + header = p.el().select('.header');
61 + body = p.el().select('.body');
62 + footer = p.el().select('.footer');
63 + }
64 +
65 + function hAppend(x) {
66 + if (typeof x === 'string') {
67 + return header.append(x);
68 + }
69 + header.node().appendChild(x.node());
70 + return header;
71 + }
72 +
73 + function bAppend(x) {
74 + if (typeof x === 'string') {
75 + return body.append(x);
76 + }
77 + body.node().appendChild(x.node());
78 + return body;
79 + }
80 +
81 + function fAppend(x) {
82 + if (typeof x === 'string') {
83 + return footer.append(x);
84 + }
85 + footer.node().appendChild(x.node());
86 + return footer;
87 + }
88 +
89 + function destroy() {
90 + ps.destroyPanel(id);
91 + }
92 +
93 + return {
94 + reset: reset,
95 + appendHeader: hAppend,
96 + appendBody: bAppend,
97 + appendFooter: fAppend,
98 + destroy: destroy
99 + };
100 + }
101 +
102 + function makeButton(text, callback) {
103 + var cb = fs.isF(callback);
104 +
105 + function invoke() {
106 + cb && cb();
107 + panel.hide();
108 + }
109 + return createDiv('dialog-button')
110 + .text(text)
111 + .on('click', invoke);
112 + }
113 +
114 + function setTitle(title) {
115 + if (pApi) {
116 + pApi.appendHeader('h2').text(title);
117 + }
118 + return dApi;
119 + }
120 +
121 + function addContent(content) {
122 + if (pApi) {
123 + pApi.appendBody(content);
124 + }
125 + return dApi;
126 + }
127 +
128 + function addButton(text, cb) {
129 + if (pApi) {
130 + pApi.appendFooter(makeButton(text, cb));
131 + }
132 + return dApi;
133 + }
134 +
135 + // opens the dialog (creates if necessary)
136 + function openDialog(id, opts) {
137 + $log.debug('Open DIALOG', id, opts);
138 + if (!pApi) {
139 + pApi = createDialog(id, opts);
140 + }
141 + pApi.reset();
142 + panel.show();
143 +
144 + // return the dialog object API
145 + dApi = {
146 + setTitle: setTitle,
147 + addContent: addContent,
148 + addButton: addButton
149 + };
150 + return dApi;
151 + }
152 +
153 + // closes the dialog (destroying panel)
154 + function closeDialog() {
155 + $log.debug('Close DIALOG');
156 + if (pApi) {
157 + panel.hide();
158 + pApi.destroy();
159 + pApi = null;
160 + dApi = null;
161 + }
162 + }
163 +
164 + // creates a detached div, returning D3 selection
165 + // optional CSS class may be provided
166 + function createDiv(cls) {
167 + var div = d3.select(document.createElement('div'));
168 + if (cls) {
169 + div.classed(cls, true);
170 + }
171 + return div;
172 + }
173 +
174 + angular.module('onosLayer')
175 + .factory('DialogService',
176 + ['$log', '$window', 'FnService', 'PanelService', 'ButtonService',
177 +
178 + // TODO: for now, $window is not used, but we should provide an option
179 + // to center the dialog on the window.
180 +
181 + function (_$log_, _$window_, _fs_, _ps_, _bns_) {
182 + $log = _$log_;
183 + $window = _$window_;
184 + fs = _fs_;
185 + ps = _ps_;
186 + bns = _bns_;
187 +
188 + return {
189 + openDialog: openDialog,
190 + closeDialog: closeDialog,
191 + createDiv: createDiv
192 + };
193 + }]);
194 +
195 +}());
...@@ -30,3 +30,8 @@ ...@@ -30,3 +30,8 @@
30 #ov-app input#uploadFile { 30 #ov-app input#uploadFile {
31 display: none; 31 display: none;
32 } 32 }
33 +
34 +#app-dialog p {
35 + color: darkred;
36 + font-size: 12pt;
37 +}
......
...@@ -24,16 +24,20 @@ ...@@ -24,16 +24,20 @@
24 // constants 24 // constants
25 var INSTALLED = 'INSTALLED', 25 var INSTALLED = 'INSTALLED',
26 ACTIVE = 'ACTIVE', 26 ACTIVE = 'ACTIVE',
27 - APP_MGMENT_REQ = 'appManagementRequest', 27 + appMgmtReq = 'appManagementRequest',
28 - FILE_UPLOAD_URL = 'applications/upload'; 28 + fileUploadUrl = 'applications/upload',
29 + dialogId = 'app-dialog',
30 + dialogOpts = {
31 + edge: 'right'
32 + };
29 33
30 angular.module('ovApp', []) 34 angular.module('ovApp', [])
31 .controller('OvAppCtrl', 35 .controller('OvAppCtrl',
32 ['$log', '$scope', '$http', 36 ['$log', '$scope', '$http',
33 'FnService', 'TableBuilderService', 'WebSocketService', 'UrlFnService', 37 'FnService', 'TableBuilderService', 'WebSocketService', 'UrlFnService',
34 - 'KeyService', 38 + 'KeyService', 'DialogService',
35 39
36 - function ($log, $scope, $http, fs, tbs, wss, ufs, ks) { 40 + function ($log, $scope, $http, fs, tbs, wss, ufs, ks, ds) {
37 $scope.ctrlBtnState = {}; 41 $scope.ctrlBtnState = {};
38 $scope.uploadTip = 'Upload an application (.oar file)'; 42 $scope.uploadTip = 'Upload an application (.oar file)';
39 $scope.activateTip = 'Activate selected application'; 43 $scope.activateTip = 'Activate selected application';
...@@ -77,23 +81,49 @@ ...@@ -77,23 +81,49 @@
77 ['scroll down', 'See more apps'] 81 ['scroll down', 'See more apps']
78 ]); 82 ]);
79 83
80 - $scope.appAction = function (action) { 84 +
81 - if ($scope.ctrlBtnState.selection) { 85 + function createConfirmationText(action, sid) {
82 - $log.debug('Initiating ' + action + ' of ' + $scope.selId); 86 + var content = ds.createDiv();
83 - wss.sendEvent(APP_MGMENT_REQ, { 87 + content.append('p').text(action + ' ' + sid);
88 + return content;
89 + }
90 +
91 + function confirmAction(action) {
92 + var sid = $scope.selId,
93 + spar = $scope.sortParams;
94 +
95 + function dOk() {
96 + $log.debug('Initiating', action, 'of', sid);
97 + wss.sendEvent(appMgmtReq, {
84 action: action, 98 action: action,
85 - name: $scope.selId, 99 + name: sid,
86 - sortCol: $scope.sortParams.sortCol, 100 + sortCol: spar.sortCol,
87 - sortDir: $scope.sortParams.sortDir 101 + sortDir: spar.sortDir
88 }); 102 });
89 } 103 }
104 +
105 + function dCancel() {
106 + $log.debug('Canceling', action, 'of', sid);
107 + }
108 +
109 + ds.openDialog(dialogId, dialogOpts)
110 + .setTitle('Confirm Action')
111 + .addContent(createConfirmationText(action, sid))
112 + .addButton('OK', dOk)
113 + .addButton('Cancel', dCancel);
114 + }
115 +
116 + $scope.appAction = function (action) {
117 + if ($scope.ctrlBtnState.selection) {
118 + confirmAction(action);
119 + }
90 }; 120 };
91 121
92 $scope.$on('FileChanged', function () { 122 $scope.$on('FileChanged', function () {
93 var formData = new FormData(); 123 var formData = new FormData();
94 if ($scope.appFile) { 124 if ($scope.appFile) {
95 formData.append('file', $scope.appFile); 125 formData.append('file', $scope.appFile);
96 - $http.post(ufs.rsUrl(FILE_UPLOAD_URL), formData, { 126 + $http.post(ufs.rsUrl(fileUploadUrl), formData, {
97 transformRequest: angular.identity, 127 transformRequest: angular.identity,
98 headers: { 128 headers: {
99 'Content-Type': undefined 129 'Content-Type': undefined
......
...@@ -96,23 +96,6 @@ html[data-platform='iPad'] #topo-p-detail { ...@@ -96,23 +96,6 @@ html[data-platform='iPad'] #topo-p-detail {
96 height: 30px; 96 height: 30px;
97 } 97 }
98 98
99 -/* --- Topo Dialog Panel --- */
100 -
101 -#topo-p-dialog .dialog-button {
102 - display: inline-block;
103 - cursor: pointer;
104 - height: 20px;
105 - padding: 2px 6px;
106 - margin: 4px;
107 - float: right;
108 -}
109 -
110 -.light #topo-p-dialog .dialog-button {
111 - background-color: #fec;
112 -}
113 -.dark #topo-p-dialog .dialog-button {
114 - background-color: #369;
115 -}
116 99
117 /* --- general topo-panel styling --- */ 100 /* --- general topo-panel styling --- */
118 101
......
...@@ -16,175 +16,29 @@ ...@@ -16,175 +16,29 @@
16 16
17 /* 17 /*
18 ONOS GUI -- Topology Dialog Module. 18 ONOS GUI -- Topology Dialog Module.
19 - Defines functions for manipulating a dialog box. 19 + Creates a dialog box for the topology view.
20 */ 20 */
21 21
22 (function () { 22 (function () {
23 'use strict'; 23 'use strict';
24 24
25 - // injected refs
26 - var $log, $window, $rootScope, fs, ps, bns;
27 -
28 // constants 25 // constants
29 - var pCls = 'topo-p dialog', 26 + var idDialog = 'topo-p-dialog',
30 - idDialog = 'topo-p-dialog', 27 + opts = {
31 - panelOpts = { 28 + cssCls: 'topo-p'
32 - width: 300,
33 - edge: 'left'
34 - };
35 -
36 - // internal state
37 - var pApi, panel, dApi;
38 -
39 - // TODO: ESC key invokes Cancel callback
40 - // TODO: Enter invokes OK callback
41 -
42 - // create the dialog; return its API
43 - function createDialog() {
44 - var header, body, footer,
45 - p = ps.createPanel(idDialog, panelOpts);
46 - p.classed(pCls, true);
47 - panel = p;
48 -
49 - function reset() {
50 - p.empty();
51 - p.append('div').classed('header', true);
52 - p.append('div').classed('body', true);
53 - p.append('div').classed('footer', true);
54 -
55 - header = p.el().select('.header');
56 - body = p.el().select('.body');
57 - footer = p.el().select('.footer');
58 - }
59 -
60 - function hAppend(x) {
61 - if (typeof x === 'string') {
62 - return header.append(x);
63 - }
64 - header.node().appendChild(x.node());
65 - return header;
66 - }
67 -
68 - function bAppend(x) {
69 - if (typeof x === 'string') {
70 - return body.append(x);
71 - }
72 - body.node().appendChild(x.node());
73 - return body;
74 - }
75 -
76 - function fAppend(x) {
77 - if (typeof x === 'string') {
78 - return footer.append(x);
79 - }
80 - footer.node().appendChild(x.node());
81 - return footer;
82 - }
83 -
84 - function destroy() {
85 - ps.destroyPanel(idDialog);
86 - }
87 -
88 - return {
89 - reset: reset,
90 - appendHeader: hAppend,
91 - appendBody: bAppend,
92 - appendFooter: fAppend,
93 - destroy: destroy
94 - };
95 - }
96 -
97 - function makeButton(text, callback) {
98 - var cb = fs.isF(callback);
99 -
100 - function invoke() {
101 - cb && cb();
102 - panel.hide();
103 - }
104 - return createDiv('dialog-button')
105 - .text(text)
106 - .on('click', invoke);
107 - }
108 -
109 - function setTitle(title) {
110 - if (pApi) {
111 - pApi.appendHeader('h2').text(title);
112 - }
113 - return dApi;
114 - }
115 -
116 - function addContent(content) {
117 - if (pApi) {
118 - pApi.appendBody(content);
119 - }
120 - return dApi;
121 - }
122 -
123 - function addButton(text, cb) {
124 - if (pApi) {
125 - pApi.appendFooter(makeButton(text, cb));
126 - }
127 - return dApi;
128 - }
129 -
130 - // opens the dialog (creates if necessary)
131 - function openDialog() {
132 - $log.debug('Open DIALOG');
133 - if (!pApi) {
134 - pApi = createDialog();
135 - }
136 - pApi.reset();
137 - panel.show();
138 -
139 - // return the dialog object API
140 - dApi = {
141 - setTitle: setTitle,
142 - addContent: addContent,
143 - addButton: addButton
144 }; 29 };
145 - return dApi;
146 - }
147 -
148 - // closes the dialog (destroying panel)
149 - function closeDialog() {
150 - $log.debug('Close DIALOG');
151 - if (pApi) {
152 - panel.hide();
153 - pApi.destroy();
154 - pApi = null;
155 - dApi = null;
156 - }
157 - }
158 -
159 - // creates a detached div, returning D3 selection
160 - // optional CSS class may be provided
161 - function createDiv(cls) {
162 - var div = d3.select(document.createElement('div'));
163 - if (cls) {
164 - div.classed(cls, true);
165 - }
166 - return div;
167 - }
168 30
169 // ========================== 31 // ==========================
170 32
171 angular.module('ovTopo') 33 angular.module('ovTopo')
172 .factory('TopoDialogService', 34 .factory('TopoDialogService',
173 - ['$log', '$window', '$rootScope', 'FnService', 'PanelService', 'ButtonService', 35 + ['DialogService',
174 -
175 - function (_$log_, _$window_, _$rootScope_,
176 - _fs_, _ps_, _bns_) {
177 - $log = _$log_;
178 - $window = _$window_;
179 - $rootScope = _$rootScope_;
180 - fs = _fs_;
181 - ps = _ps_;
182 - bns = _bns_;
183 36
37 + function (ds) {
184 return { 38 return {
185 - openDialog: openDialog, 39 + openDialog: function () { return ds.openDialog(idDialog, opts); },
186 - closeDialog: closeDialog, 40 + closeDialog: ds.closeDialog,
187 - createDiv: createDiv 41 + createDiv: ds.createDiv
188 }; 42 };
189 }]); 43 }]);
190 }()); 44 }());
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
73 73
74 <script src="app/fw/layer/layer.js"></script> 74 <script src="app/fw/layer/layer.js"></script>
75 <script src="app/fw/layer/panel.js"></script> 75 <script src="app/fw/layer/panel.js"></script>
76 + <script src="app/fw/layer/dialog.js"></script>
76 <script src="app/fw/layer/flash.js"></script> 77 <script src="app/fw/layer/flash.js"></script>
77 <script src="app/fw/layer/quickhelp.js"></script> 78 <script src="app/fw/layer/quickhelp.js"></script>
78 <script src="app/fw/layer/veil.js"></script> 79 <script src="app/fw/layer/veil.js"></script>
...@@ -86,6 +87,7 @@ ...@@ -86,6 +87,7 @@
86 <link rel="stylesheet" href="app/fw/svg/glyph.css"> 87 <link rel="stylesheet" href="app/fw/svg/glyph.css">
87 <link rel="stylesheet" href="app/fw/svg/icon.css"> 88 <link rel="stylesheet" href="app/fw/svg/icon.css">
88 <link rel="stylesheet" href="app/fw/layer/panel.css"> 89 <link rel="stylesheet" href="app/fw/layer/panel.css">
90 + <link rel="stylesheet" href="app/fw/layer/dialog.css">
89 <link rel="stylesheet" href="app/fw/layer/flash.css"> 91 <link rel="stylesheet" href="app/fw/layer/flash.css">
90 <link rel="stylesheet" href="app/fw/layer/quickhelp.css"> 92 <link rel="stylesheet" href="app/fw/layer/quickhelp.css">
91 <link rel="stylesheet" href="app/fw/layer/veil.css"> 93 <link rel="stylesheet" href="app/fw/layer/veil.css">
......