blob: 1cbdcf943b9bb371c3e0243033c61e84f66355db [file] [log] [blame]
Simon Hunt737c89f2015-01-28 12:23:19 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Simon Hunt737c89f2015-01-28 12:23:19 -08003 *
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 -- SVG -- Util Service
19 */
20
21/*
22 The SVG Util Service provides a miscellany of utility functions.
23 */
24
25(function () {
26 'use strict';
27
28 // injected references
29 var $log, fs;
30
Simon Huntf4ef6dd2016-02-03 17:05:14 -080031 // TODO: change 'force' ref to be 'force.alpha' ref.
32 function createDragBehavior(force, selectCb, atDragEnd,
33 dragEnabled, clickEnabled) {
34 var draggedThreshold = d3.scale.linear()
35 .domain([0, 0.1])
36 .range([5, 20])
37 .clamp(true),
38 drag,
39 fSel = fs.isF(selectCb),
40 fEnd = fs.isF(atDragEnd),
41 fDEn = fs.isF(dragEnabled),
42 fCEn = fs.isF(clickEnabled),
43 bad = [];
44
45 function naf(what) {
46 return 'SvgUtilService: createDragBehavior(): ' + what +
47 ' is not a function';
48 }
49
50 if (!force) {
51 bad.push('SvgUtilService: createDragBehavior(): ' +
52 'Bad force reference');
53 }
54 if (!fSel) {
55 bad.push(naf('selectCb'));
56 }
57 if (!fEnd) {
58 bad.push(naf('atDragEnd'));
59 }
60 if (!fDEn) {
61 bad.push(naf('dragEnabled'));
62 }
63 if (!fCEn) {
64 bad.push(naf('clickEnabled'));
65 }
66
67 if (bad.length) {
68 $log.error(bad.join('\n'));
69 return null;
70 }
71
72 function dragged(d) {
73 var threshold = draggedThreshold(force.alpha()),
74 dx = d.oldX - d.px,
75 dy = d.oldY - d.py;
76 if (Math.abs(dx) >= threshold || Math.abs(dy) >= threshold) {
77 d.dragged = true;
78 }
79 return d.dragged;
80 }
81
82 drag = d3.behavior.drag()
83 .origin(function(d) { return d; })
84 .on('dragstart', function(d) {
85 if (clickEnabled() || dragEnabled()) {
86 d3.event.sourceEvent.stopPropagation();
87
88 d.oldX = d.x;
89 d.oldY = d.y;
90 d.dragged = false;
91 d.fixed |= 2;
92 d.dragStarted = true;
93 }
94 })
95 .on('drag', function(d) {
96 if (dragEnabled()) {
97 d.px = d3.event.x;
98 d.py = d3.event.y;
99 if (dragged(d)) {
100 if (!force.alpha()) {
101 force.alpha(.025);
102 }
103 }
104 }
105 })
106 .on('dragend', function(d) {
107 if (d.dragStarted) {
108 d.dragStarted = false;
109 if (!dragged(d)) {
110 // consider this the same as a 'click'
111 // (selection of a node)
112 if (clickEnabled()) {
113 selectCb.call(this, d);
114 }
115 }
116 d.fixed &= ~6;
117
118 // hook at the end of a drag gesture
119 if (dragEnabled()) {
120 atDragEnd.call(this, d);
121 }
122 }
123 });
124
125 return drag;
126 }
127
128
129 function loadGlow(defs, r, g, b, id) {
130 var glow = defs.append('filter')
131 .attr('x', '-50%')
132 .attr('y', '-50%')
133 .attr('width', '200%')
134 .attr('height', '200%')
135 .attr('id', id);
136
137 glow.append('feColorMatrix')
138 .attr('type', 'matrix')
139 .attr('values',
140 '0 0 0 0 ' + r + ' ' +
141 '0 0 0 0 ' + g + ' ' +
142 '0 0 0 0 ' + b + ' ' +
143 '0 0 0 1 0 ');
144
145 glow.append('feGaussianBlur')
146 .attr('stdDeviation', 3)
147 .attr('result', 'coloredBlur');
148
149 glow.append('feMerge').selectAll('feMergeNode')
150 .data(['coloredBlur', 'SourceGraphic'])
151 .enter().append('feMergeNode')
152 .attr('in', String);
153 }
154
155 function loadGlowDefs(defs) {
156 loadGlow(defs, 0.0, 0.0, 0.7, 'blue-glow');
157 loadGlow(defs, 1.0, 1.0, 0.3, 'yellow-glow');
158 }
159
160 // --- Ordinal scales for 7 values.
161
162 // blue brown brick red sea green purple dark teal lime
163 var lightNorm = ['#3E5780', '#78533B', '#CB4D28', '#018D61', '#8A2979', '#006D73', '#56AF00'],
164 lightMute = ['#A8B8CC', '#CCB3A8', '#FFC2BD', '#96D6BF', '#D19FCE', '#8FCCCA', '#CAEAA4'],
165
166 darkNorm = ['#304860', '#664631', '#A8391B', '#00754B', '#77206D', '#005959', '#428700'],
167 darkMute = ['#304860', '#664631', '#A8391B', '#00754B', '#77206D', '#005959', '#428700'];
168
169 var colors= {
170 light: {
171 norm: d3.scale.ordinal().range(lightNorm),
172 mute: d3.scale.ordinal().range(lightMute)
173 },
174 dark: {
175 norm: d3.scale.ordinal().range(darkNorm),
176 mute: d3.scale.ordinal().range(darkMute)
177 }
178 };
179
180 function cat7() {
181 var tcid = 'd3utilTestCard';
182
183 function getColor(id, muted, theme) {
184 // NOTE: since we are lazily assigning domain ids, we need to
185 // get the color from all 4 scales, to keep the domains
186 // in sync.
187 var ln = colors.light.norm(id),
188 lm = colors.light.mute(id),
189 dn = colors.dark.norm(id),
190 dm = colors.dark.mute(id);
191 if (theme === 'dark') {
192 return muted ? dm : dn;
193 } else {
194 return muted ? lm : ln;
195 }
196 }
197
198 function testCard(svg) {
199 var g = svg.select('g#' + tcid),
200 dom = d3.range(7),
201 k, muted, theme, what;
202
203 if (!g.empty()) {
204 g.remove();
205
206 } else {
207 g = svg.append('g')
208 .attr('id', tcid)
209 .attr('transform', 'scale(4)translate(20,20)');
210
211 for (k=0; k<4; k++) {
212 muted = k%2;
213 what = muted ? ' muted' : ' normal';
214 theme = k < 2 ? 'light' : 'dark';
215 dom.forEach(function (id, i) {
216 var x = i * 20,
217 y = k * 20,
218 f = get(id, muted, theme);
219 g.append('circle').attr({
220 cx: x,
221 cy: y,
222 r: 5,
223 fill: f
224 });
225 });
226 g.append('rect').attr({
227 x: 140,
228 y: k * 20 - 5,
229 width: 32,
230 height: 10,
231 rx: 2,
232 fill: '#888'
233 });
234 g.append('text').text(theme + what)
235 .attr({
236 x: 142,
237 y: k * 20 + 2,
238 fill: 'white'
239 })
240 .style('font-size', '4pt');
241 }
242 }
243 }
244
245 return {
246 testCard: testCard,
247 getColor: getColor
248 };
249 }
250
251 function translate(x, y) {
252 if (fs.isA(x) && x.length === 2 && !y) {
253 return 'translate(' + x[0] + ',' + x[1] + ')';
254 }
255 return 'translate(' + x + ',' + y + ')';
256 }
257
258 function scale(x, y) {
259 return 'scale(' + x + ',' + y + ')';
260 }
261
262 function skewX(x) {
263 return 'skewX(' + x + ')';
264 }
265
266 function rotate(deg) {
267 return 'rotate(' + deg + ')';
268 }
269
270 function stripPx(s) {
271 return s.replace(/px$/,'');
272 }
273
274 function safeId(s) {
275 return s.replace(/[^a-z0-9]/gi, '-');
276 }
277
278 function makeVisible(el, b) {
279 el.style('visibility', (b ? 'visible' : 'hidden'));
280 }
281
282 function isVisible(el) {
283 return el.style('visibility') === 'visible';
284 }
285
286 function visible(el, x) {
287 if (x === undefined) {
288 return isVisible(el);
289 } else {
290 makeVisible(el, x);
291 }
292 }
293
Simon Hunt737c89f2015-01-28 12:23:19 -0800294 angular.module('onosSvg')
Simon Huntf4ef6dd2016-02-03 17:05:14 -0800295 .factory('SvgUtilService', ['$log', 'FnService',
Simon Hunt737c89f2015-01-28 12:23:19 -0800296 function (_$log_, _fs_) {
297 $log = _$log_;
298 fs = _fs_;
299
Simon Hunt737c89f2015-01-28 12:23:19 -0800300 return {
301 createDragBehavior: createDragBehavior,
Simon Hunt0ee28682015-02-12 20:48:11 -0800302 loadGlowDefs: loadGlowDefs,
Simon Huntc9b73162015-01-29 14:02:15 -0800303 cat7: cat7,
Simon Hunt4b668592015-01-29 17:33:53 -0800304 translate: translate,
Simon Hunt56004a82015-02-19 13:53:20 -0800305 scale: scale,
306 skewX: skewX,
307 rotate: rotate,
Simon Hunt7c8ab8d2015-02-03 15:05:15 -0800308 stripPx: stripPx,
Simon Hunt18bf9822015-02-12 17:35:45 -0800309 safeId: safeId,
Simon Huntf4ef6dd2016-02-03 17:05:14 -0800310 visible: visible
Simon Hunt737c89f2015-01-28 12:23:19 -0800311 };
312 }]);
313}());