sample.js 6.1 KB
/*
 * Copyright 2014 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.
 */

/*
 Sample module file to illustrate framework integration.

 @author Simon Hunt
 */

(function (onos) {
    'use strict';

    var pi = Math.PI,
        svg,
        dotG,
        nCircles = 12,
        circleData = [],
        dotId = 0,
        angle = 360 / nCircles,
        baseAngle = -90 - angle,
        groupRadius = 120,
        dotRadius = 24,
        dotMoveMs = 800,
        dotAppearMs = 300,
        dotEase = 'elastic',
        colorScale = d3.scale.linear()
            .domain([-pi/2, 2*pi/4, 3*pi/2])
            .range(['green', 'goldenrod', 'blue']);

    // set the size of the SVG layer to match that of the view
    function sizeSvg(view) {
        svg.attr({
            width: view.width(),
            height: view.height()
        });
    }

    // gets invoked only the first time the view is loaded
    function preload(view, ctx) {
        // prepare our SVG layer...
        svg = view.$div.append('svg');
        sizeSvg(view);
        dotG = svg.append('g').attr('id', 'dots');
    }

    // gets invoked just before our view is loaded
    function reset(view) {
        // clear dot group and reset circle data
        dotG.html('');
        circleData = [];
        // also clear text, if any
        svg.selectAll('text').remove();
    }

    function updateCirclePositions(view, addNew) {
        var w = view.width(),
            h = view.height(),
            ox = w / 2,
            oy = h / 2;

        // reposition existing dots
        circleData.forEach(function (c, i) {
            var inc = addNew ? 1 : 0,
                theta = ((i + inc) * angle + baseAngle) * pi/180,
                dx = Math.cos(theta) * groupRadius,
                dy = Math.sin(theta) * groupRadius,
                x = ox + dx,
                y = oy + dy;
            if (!addNew && i === 0) {
                x = ox;
                y = oy;
            }
            c.cx = x;
            c.cy = y;
            c.rgb = colorScale(theta);
        });

        if (addNew) {
            // introduce a new dot
            circleData.unshift({
                cx: ox,
                cy: oy,
                id: dotId++
            });
        }

        // +1 to account for the circle in the center..
        if (circleData.length > nCircles + 1) {
            circleData.splice(nCircles + 1, 1);
        }
    }

    function doCircles(view) {
        var ox = view.width() / 2,
            oy = view.height() / 2,
            stroke = 'black',
            fill = 'red',
            hoverFill = 'magenta';

        // move existing circles, and add a new one
        updateCirclePositions(view, true);

        var circ = dotG.selectAll('circle')
            .data(circleData, function (d) { return d.id; });

        // operate on existing elements
        circ.on('mouseover', null)
            .on('mouseout', null)
            .on('click', null)
            .transition()
            .duration(dotMoveMs)
            .ease(dotEase)
            .attr({
                cx: function (d) { return d.cx; },
                cy: function (d) { return d.cy; }
            })
            .style({
                cursor: 'default',
                fill: function (d) { return d.rgb; }
            });

        // operate on entering elements
        circ.enter()
            .append('circle')
            .attr({
                cx: function (d) { return d.cx; },
                cy: function (d) { return d.cy; },
                r: 0
            })
            .style({
                fill: fill,
                stroke: stroke,
                'stroke-width': 3.5,
                cursor: 'pointer',
                opacity: 0
            })
            .on('mouseover', function (d) {
                d3.select(this).style('fill', hoverFill);
            })
            .on('mouseout', function (d) {
                d3.select(this).style('fill', fill);
            })
            .on('click', function (d) {
                setTimeout(function() {
                    doCircles(view, true);
                }, 10);
            })
            .transition()
            .delay(dotMoveMs)
            .duration(dotAppearMs)
            .attr('r', dotRadius)
            .style('opacity', 1);

        // operate on exiting elements
        circ.exit()
            .transition()
            .duration(750)
            .style('opacity', 0)
            .attr({
                cx: ox,
                cy: oy,
                r: groupRadius - dotRadius
            })
            .remove();
    }

    function load(view, ctx) {
        var ctxText = ctx ? 'Context is "' + ctx + '"' : '';

        // display our view context
        if (ctxText) {
            svg.append('text')
                .text(ctxText)
                .attr({
                    x: 20,
                    y: '1.5em'
                })
                .style({
                    fill: 'darkgreen',
                    'font-size': '20pt'
                });
        }

        doCircles(view);
    }

    function resize(view, ctx) {
        sizeSvg(view);
        updateCirclePositions(view);

        // move exiting dots into new positions, relative to view size
        var circ = dotG.selectAll('circle')
            .data(circleData, function (d) { return d.id; });
        circ.attr({
                cx: function (d) { return d.cx; },
                cy: function (d) { return d.cy; }
            });
    }

    // == register our view here, with links to lifecycle callbacks

    onos.ui.addView('sample', {
        preload: preload,
        reset: reset,
        load: load,
        resize: resize
    });

}(ONOS));