topoSprite.js 7 KB
/*
 * 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 -- Topology Sprite Module.
 Defines behavior for loading sprites into the sprite layer.
 */

(function () {
    'use strict';

    // injected refs
    var $log, $http, fs, gs, sus, wss;

    // constants
    var tssid = 'TopoSpriteService: ',
        fontsize = 20;  // default font size 20pt.

    // internal state
    var spriteLayer, defsElement;


    function registerPathsAsGlyphs(paths) {
        var custom = {},
            ids = [];

        function mkd(d) {
            return fs.isA(d) ? d.join('') : d;
        }

        paths.forEach(function (path) {
            var tag = 'spr_' + path.tag;

            if (path.glyph) {
                // assumption is that we are using a built-in glyph
                return;
            }

            custom['_' + tag] = path.viewbox || '0 0 1000 1000';
            custom[tag] = mkd(path.d);
            ids.push(tag);
        });

        gs.registerGlyphs(custom);
        gs.loadDefs(defsElement, ids, true);
    }

    function applyStrokeStyle(s, use) {
        var style;
        if (s) {
            style = {};
            angular.forEach(s, function (value, key) {
                style['stroke-' + key] = value;
            });
            use.style(style);
        }
    }

    function applyFillClass(f, use) {
        use.classed('fill-' + f, true);
    }

    function doSprite(spr, def, pathmeta) {
        var pmeta = pathmeta[def.path],
            c = spr.class || 'gray1',
            p = spr.pos || [0,0],
            lab = spr.label,
            dim = def.dim || [40,40],
            w = dim[0],
            h = dim[1],
            dy = def.labelyoff || 1,
            sc = def.scale,
            xfm = sus.translate(p),
            g, attr, use;

        if (sc) {
            xfm += sus.scale(sc, sc);
        }

        g = spriteLayer.append('g')
            .classed(c, true)
            .attr('transform', xfm);

        attr = {
            width: w,
            height: h,
            'xlink:href': '#' + pmeta.u
        };

        use = g.append('use').attr(attr);
        applyStrokeStyle(pmeta.s, use);
        applyFillClass(def.fill, use);


        // add subpaths if they have been defined
        if (fs.isA(def.subpaths)) {
            def.subpaths.forEach(function (v) {
                pmeta = pathmeta[v.path];
                attr = {
                    width: w,
                    height: h,
                    'xlink:href': '#' + pmeta.u,
                    transform: sus.translate(v.pos)
                };
                use = g.append('use').attr(attr);
                applyStrokeStyle(pmeta.s, use);
                applyFillClass(def.subpathfill, use);
            });
        }

        if (lab) {
            g.append('text')
                .text(lab)
                .attr({ x: w / 2, y: h * dy });
        }
    }

    function doLabel(label) {
        var c = label.class || 'gray1',
            p = label.pos || [0,0],
            sz = label.size || 1.0,
            g = spriteLayer.append('g')
                .classed(c, true)
                .attr('transform', sus.translate(p))
                .append('text')
                .text(label.text)
                .style('font-size', (fontsize * sz)+'pt');
    }


    // ==========================
    // event handlers

    // Handles response from 'spriteListRequest' which lists all the
    // registered sprite definitions on the server.
    // (see onos-upload-sprites)
    function inList(payload) {
        $log.debug(tssid + 'Registered sprite definitions:', payload.names);
        // Some day, we will make this list available to the user in
        //  a dropdown selection box...
    }

    // Handles response from 'spriteDataRequest' which provides the
    //  data for the requested sprite definition.
    function inData(payload) {
        var data = payload.data,
            name, desc, pfx, sprites, labels, alpha,
            paths, defn, load,
            pathmeta = {},
            defs = {},
            warn = [];

        if (!data) {
            $log.warn(tssid + 'No sprite data loaded.');
            return;
        }
        name = data.defn_name;
        desc = data.defn_desc;
        paths = data.paths;
        defn = data.defn;
        load = data.load;
        pfx = tssid + '[' + name + ']: ';

        $log.debug("Loading sprites...[" + name + "]", desc);

        function no(what) {
            warn.push(pfx + 'No ' + what + ' property defined');
        }

        if (!paths) no('paths');
        if (!defn) no('defn');
        if (!load) no('load');

        if (warn.length) {
            $log.error(warn.join('\n'));
            return;
        }

        // any custom paths need to be added to the glyph DB, and imported
        registerPathsAsGlyphs(paths);

        paths.forEach(function (p) {
            pathmeta[p.tag] = {
                s: p.stroke,
                u: p.glyph || 'spr_' + p.tag
            };
        });

        defn.forEach(function (d) {
            defs[d.id] = d;
        });

        // sprites, labels and alpha are each optional components of the load
        sprites = load.sprites;
        labels = load.labels;
        alpha = load.alpha;

        if (alpha) {
            spriteLayer.style('opacity', alpha);
        }

        if (sprites) {
            sprites.forEach(function (spr) {
                var def = defs[spr.id];
                doSprite(spr, def, pathmeta);
            });
        }

        if (labels) {
            labels.forEach(doLabel);
        }
    }


    function loadSprites(layer, defsElem, defname) {
        var name = defname || 'sprites';
        spriteLayer = layer;
        defsElement = defsElem;

        $log.info(tssid + 'Requesting sprite definition ['+name+']...');

        wss.sendEvent('spriteListRequest');
        wss.sendEvent('spriteDataRequest', {name: name});
    }

    // === -----------------------------------------------------
    // === MODULE DEFINITION ===

    angular.module('ovTopo')
    .factory('TopoSpriteService',
        ['$log', '$http', 'FnService', 'GlyphService',
            'SvgUtilService', 'WebSocketService',

        function (_$log_, _$http_, _fs_, _gs_, _sus_, _wss_) {
            $log = _$log_;
            $http = _$http_;
            fs = _fs_;
            gs = _gs_;
            sus = _sus_;
            wss = _wss_;

            return {
                loadSprites: loadSprites,
                spriteListResponse: inList,
                spriteDataResponse: inData
            };
        }]);

}());