/* * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* ONOS GUI -- SVG -- Util Service */ /* The SVG Util Service provides a miscellany of utility functions. */ (function () { 'use strict'; // injected references var $log, fs; angular.module('onosSvg') .factory('SvgUtilService', ['$log', 'FnService', function (_$log_, _fs_) { $log = _$log_; fs = _fs_; // TODO: change 'force' ref to be 'force.alpha' ref. function createDragBehavior(force, selectCb, atDragEnd, dragEnabled, clickEnabled) { var draggedThreshold = d3.scale.linear() .domain([0, 0.1]) .range([5, 20]) .clamp(true), drag, fSel = fs.isF(selectCb), fEnd = fs.isF(atDragEnd), fDEn = fs.isF(dragEnabled), fCEn = fs.isF(clickEnabled), bad = []; function naf(what) { return 'SvgUtilService: createDragBehavior(): ' + what + ' is not a function'; } if (!force) { bad.push('SvgUtilService: createDragBehavior(): ' + 'Bad force reference'); } if (!fSel) { bad.push(naf('selectCb')); } if (!fEnd) { bad.push(naf('atDragEnd')); } if (!fDEn) { bad.push(naf('dragEnabled')); } if (!fCEn) { bad.push(naf('clickEnabled')); } if (bad.length) { $log.error(bad.join('\n')); return null; } function dragged(d) { var threshold = draggedThreshold(force.alpha()), dx = d.oldX - d.px, dy = d.oldY - d.py; if (Math.abs(dx) >= threshold || Math.abs(dy) >= threshold) { d.dragged = true; } return d.dragged; } drag = d3.behavior.drag() .origin(function(d) { return d; }) .on('dragstart', function(d) { if (clickEnabled() || dragEnabled()) { d3.event.sourceEvent.stopPropagation(); d.oldX = d.x; d.oldY = d.y; d.dragged = false; d.fixed |= 2; d.dragStarted = true; } }) .on('drag', function(d) { if (dragEnabled()) { d.px = d3.event.x; d.py = d3.event.y; if (dragged(d)) { if (!force.alpha()) { force.alpha(.025); } } } }) .on('dragend', function(d) { if (d.dragStarted) { d.dragStarted = false; if (!dragged(d)) { // consider this the same as a 'click' // (selection of a node) if (clickEnabled()) { selectCb.call(this, d); } } d.fixed &= ~6; // hook at the end of a drag gesture if (dragEnabled()) { atDragEnd.call(this, d); } } }); return drag; } function loadGlow(defs, r, g, b, id) { var glow = defs.append('filter') .attr('x', '-50%') .attr('y', '-50%') .attr('width', '200%') .attr('height', '200%') .attr('id', id); glow.append('feColorMatrix') .attr('type', 'matrix') .attr('values', '0 0 0 0 ' + r + ' ' + '0 0 0 0 ' + g + ' ' + '0 0 0 0 ' + b + ' ' + '0 0 0 1 0 '); glow.append('feGaussianBlur') .attr('stdDeviation', 3) .attr('result', 'coloredBlur'); glow.append('feMerge').selectAll('feMergeNode') .data(['coloredBlur', 'SourceGraphic']) .enter().append('feMergeNode') .attr('in', String); } function loadGlowDefs(defs) { loadGlow(defs, 0.0, 0.0, 0.7, 'blue-glow'); loadGlow(defs, 1.0, 1.0, 0.3, 'yellow-glow'); } // --- Ordinal scales for 7 values. // blue brown brick red sea green purple dark teal lime var lightNorm = ['#3E5780', '#78533B', '#CB4D28', '#018D61', '#8A2979', '#006D73', '#56AF00'], lightMute = ['#A8B8CC', '#CCB3A8', '#FFC2BD', '#96D6BF', '#D19FCE', '#8FCCCA', '#CAEAA4'], darkNorm = ['#304860', '#664631', '#A8391B', '#00754B', '#77206D', '#005959', '#428700'], darkMute = ['#304860', '#664631', '#A8391B', '#00754B', '#77206D', '#005959', '#428700']; var colors= { light: { norm: d3.scale.ordinal().range(lightNorm), mute: d3.scale.ordinal().range(lightMute) }, dark: { norm: d3.scale.ordinal().range(darkNorm), mute: d3.scale.ordinal().range(darkMute) } }; function cat7() { var tcid = 'd3utilTestCard'; function getColor(id, muted, theme) { // NOTE: since we are lazily assigning domain ids, we need to // get the color from all 4 scales, to keep the domains // in sync. var ln = colors.light.norm(id), lm = colors.light.mute(id), dn = colors.dark.norm(id), dm = colors.dark.mute(id); if (theme === 'dark') { return muted ? dm : dn; } else { return muted ? lm : ln; } } function testCard(svg) { var g = svg.select('g#' + tcid), dom = d3.range(7), k, muted, theme, what; if (!g.empty()) { g.remove(); } else { g = svg.append('g') .attr('id', tcid) .attr('transform', 'scale(4)translate(20,20)'); for (k=0; k<4; k++) { muted = k%2; what = muted ? ' muted' : ' normal'; theme = k < 2 ? 'light' : 'dark'; dom.forEach(function (id, i) { var x = i * 20, y = k * 20, f = get(id, muted, theme); g.append('circle').attr({ cx: x, cy: y, r: 5, fill: f }); }); g.append('rect').attr({ x: 140, y: k * 20 - 5, width: 32, height: 10, rx: 2, fill: '#888' }); g.append('text').text(theme + what) .attr({ x: 142, y: k * 20 + 2, fill: 'white' }) .style('font-size', '4pt'); } } } return { testCard: testCard, getColor: getColor }; } function translate(x, y) { if (fs.isA(x) && x.length === 2 && !y) { return 'translate(' + x[0] + ',' + x[1] + ')'; } return 'translate(' + x + ',' + y + ')'; } function scale(x, y) { return 'scale(' + x + ',' + y + ')'; } function skewX(x) { return 'skewX(' + x + ')'; } function rotate(deg) { return 'rotate(' + deg + ')'; } function stripPx(s) { return s.replace(/px$/,''); } function safeId(s) { return s.replace(/[^a-z0-9]/gi, '-'); } function makeVisible(el, b) { el.style('visibility', (b ? 'visible' : 'hidden')); } function isVisible(el) { return el.style('visibility') === 'visible'; } return { createDragBehavior: createDragBehavior, loadGlowDefs: loadGlowDefs, cat7: cat7, translate: translate, scale: scale, skewX: skewX, rotate: rotate, stripPx: stripPx, safeId: safeId, visible: function (el, x) { if (x === undefined) { return isVisible(el); } else { makeVisible(el, x); } } }; }]); }());