Simon Hunt

GUI -- Implemented the flash service.

Change-Id: I4fb03f4c8df687ab499921d1bf2b8cb424ec306e
1 +/*
2 + * Copyright 2014,2015 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 -- Flash Service -- CSS file
19 + */
20 +
21 +#flash {
22 + z-index: 1400;
23 +}
24 +
25 +#flash svg {
26 + position: absolute;
27 + bottom: 0;
28 + opacity: 0.8;
29 +}
30 +
31 +.light #flash svg g.flashItem rect {
32 + fill: #ccc;
33 +}
34 +.dark #flash svg g.flashItem rect {
35 + fill: #555;
36 +}
37 +
38 +#flash svg g.flashItem text {
39 + stroke: none;
40 + text-anchor: middle;
41 + alignment-baseline: middle;
42 + font-size: 16pt;
43 +}
44 +.light #flash svg g.flashItem text {
45 + fill: #333;
46 +}
47 +.dark #flash svg g.flashItem text {
48 + fill: #999;
49 +}
1 +/*
2 + * Copyright 2015 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 -- Flash Service
19 +
20 + Provides a mechanism to flash short informational messages to the screen
21 + to alert the user of something, e.g. "Hosts visible" or "Hosts hidden".
22 + */
23 +(function () {
24 + 'use strict';
25 +
26 + // injected references
27 + var $log, $timeout;
28 +
29 + // configuration
30 + var defaultSettings = {
31 + fade: 200,
32 + showFor: 1200
33 + },
34 + w = '100%',
35 + h = 200,
36 + xpad = 20,
37 + ypad = 10,
38 + rx = 10,
39 + vbox = '-200 -' + (h/2) + ' 400 ' + h;
40 +
41 + // internal state
42 + var settings,
43 + timer = null,
44 + data = [];
45 +
46 + // DOM elements
47 + var flashDiv, svg;
48 +
49 +
50 + function computeBox(el) {
51 + var text = el.select('text'),
52 + box = text.node().getBBox();
53 +
54 + // center
55 + box.x = -box.width / 2;
56 + box.y = -box.height / 2;
57 +
58 + // add some padding
59 + box.x -= xpad;
60 + box.width += xpad * 2;
61 + box.y -= ypad;
62 + box.height += ypad * 2;
63 +
64 + return box;
65 + }
66 +
67 + function updateFlash() {
68 + if (!svg) {
69 + svg = flashDiv.append('svg').attr({
70 + width: w,
71 + height: h,
72 + viewBox: vbox
73 + });
74 + }
75 +
76 + var items = svg.selectAll('.flashItem')
77 + .data(data);
78 +
79 + // this is when there is an existing item
80 + items.each(function (msg) {
81 + var el = d3.select(this),
82 + box;
83 +
84 + el.select('text').text(msg);
85 + box = computeBox(el);
86 + el.select('rect').attr(box);
87 + });
88 +
89 +
90 + // this is when there is no existing item
91 + var entering = items.enter()
92 + .append('g')
93 + .attr({
94 + class: 'flashItem',
95 + opacity: 0
96 + })
97 + .transition()
98 + .duration(settings.fade)
99 + .attr('opacity', 1);
100 +
101 + entering.each(function (msg) {
102 + var el = d3.select(this),
103 + box;
104 +
105 + el.append('rect').attr('rx', rx);
106 + el.append('text').text(msg);
107 + box = computeBox(el);
108 + el.select('rect').attr(box);
109 + });
110 +
111 + items.exit()
112 + .transition()
113 + .duration(settings.fade)
114 + .attr('opacity', 0)
115 + .remove();
116 +
117 + if (svg && data.length === 0) {
118 + svg.transition()
119 + .delay(settings.fade + 10)
120 + .remove();
121 + svg = null;
122 + }
123 + }
124 +
125 + function flash(msg) {
126 + if (timer) {
127 + $timeout.cancel(timer);
128 + }
129 +
130 + timer = $timeout(function () {
131 + data = [];
132 + updateFlash();
133 + }, settings.showFor);
134 +
135 + data = [msg];
136 + updateFlash();
137 + }
138 +
139 + angular.module('onosLayer')
140 + .factory('FlashService', ['$log', '$timeout',
141 + function (_$log_, _$timeout_) {
142 + $log = _$log_;
143 + $timeout = _$timeout_;
144 +
145 + function initFlash(opts) {
146 + settings = angular.extend({}, defaultSettings, opts);
147 + flashDiv = d3.select('#flash');
148 + }
149 +
150 + return {
151 + initFlash: initFlash,
152 + flash: flash
153 + };
154 + }]);
155 +
156 +}());
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
60 60
61 <script src="fw/layer/layer.js"></script> 61 <script src="fw/layer/layer.js"></script>
62 <script src="fw/layer/panel.js"></script> 62 <script src="fw/layer/panel.js"></script>
63 + <script src="fw/layer/flash.js"></script>
63 64
64 <!-- Framework and library stylesheets included here --> 65 <!-- Framework and library stylesheets included here -->
65 <!-- TODO: use a single catenated-minified file here --> 66 <!-- TODO: use a single catenated-minified file here -->
...@@ -69,6 +70,7 @@ ...@@ -69,6 +70,7 @@
69 <link rel="stylesheet" href="fw/svg/glyph.css"> 70 <link rel="stylesheet" href="fw/svg/glyph.css">
70 <link rel="stylesheet" href="fw/svg/icon.css"> 71 <link rel="stylesheet" href="fw/svg/icon.css">
71 <link rel="stylesheet" href="fw/layer/panel.css"> 72 <link rel="stylesheet" href="fw/layer/panel.css">
73 + <link rel="stylesheet" href="fw/layer/flash.css">
72 <link rel="stylesheet" href="fw/nav/nav.css"> 74 <link rel="stylesheet" href="fw/nav/nav.css">
73 75
74 <!-- This is where contributed javascript will get injected --> 76 <!-- This is where contributed javascript will get injected -->
......
...@@ -65,8 +65,9 @@ ...@@ -65,8 +65,9 @@
65 .controller('OnosCtrl', [ 65 .controller('OnosCtrl', [
66 '$log', '$route', '$routeParams', '$location', 66 '$log', '$route', '$routeParams', '$location',
67 'KeyService', 'ThemeService', 'GlyphService', 'PanelService', 67 'KeyService', 'ThemeService', 'GlyphService', 'PanelService',
68 + 'FlashService',
68 69
69 - function ($log, $route, $routeParams, $location, ks, ts, gs, ps) { 70 + function ($log, $route, $routeParams, $location, ks, ts, gs, ps, flash) {
70 var self = this; 71 var self = this;
71 72
72 self.$route = $route; 73 self.$route = $route;
...@@ -79,6 +80,7 @@ ...@@ -79,6 +80,7 @@
79 ks.installOn(d3.select('body')); 80 ks.installOn(d3.select('body'));
80 gs.init(); 81 gs.init();
81 ps.init(); 82 ps.init();
83 + flash.initFlash();
82 84
83 $log.log('OnosCtrl has been created'); 85 $log.log('OnosCtrl has been created');
84 86
......
1 +/*
2 + * Copyright 2015 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 -- Flash Service - Unit Tests
19 + */
20 +describe('factory: fw/layer/flash.js', function () {
21 + var $log, $timeout, fs, flash, d3Elem;
22 +
23 + beforeEach(module('onosLayer'));
24 +
25 + beforeEach(inject(function (_$log_, _$timeout_, FnService, FlashService) {
26 + $log = _$log_;
27 + $timeout = _$timeout_;
28 + fs = FnService;
29 + flash = FlashService;
30 + d3Elem = d3.select('body').append('div').attr('id', 'myflashdiv');
31 + flash.initFlash();
32 + }));
33 +
34 + afterEach(function () {
35 + d3.select('#myflashdiv').remove();
36 + });
37 +
38 + function flashItemSelection() {
39 + return d3Elem.selectAll('.flashItem');
40 + }
41 +
42 + it('should define FlashService', function () {
43 + expect(flash).toBeDefined();
44 + });
45 +
46 + it('should define api functions', function () {
47 + expect(fs.areFunctions(flash, [
48 + 'initFlash', 'flash'
49 + ])).toBeTruthy();
50 + });
51 +
52 + it('should have no items to start', function () {
53 + expect(flashItemSelection().size()).toBe(0);
54 + });
55 +
56 + it('should flash the message Foo', function () {
57 + var item, rect, text;
58 + flash.flash('foo');
59 + setTimeout(function () {
60 + item = flashItemSelection();
61 + expect(item.size()).toEqual(1);
62 + expect(item.classed('flashItem')).toBeTruthy();
63 + expect(item.select('rect').size()).toEqual(1);
64 + text = item.select('text');
65 + expect(text.size()).toEqual(1);
66 + expect(text.text()).toEqual('foo');
67 + }, 500);
68 + });
69 +
70 + // TODO: testing these time-sensitive behaviors is hard...
71 + // need to work on this some other time.
72 +});