ONOS-1904 : Initial patchset for defining the Sprite Service.
- reverting topo-theme.css so that the original sprite layer still works (if anyone out there is using it).
- adding hard-coded sprite and layout for testing purposes (they'll be removed later).

Change-Id: I5f78c5cbc66cfe9ad0ac0292d5ffb1e19b611fa5
diff --git a/web/gui/src/main/webapp/app/fw/svg/sprite-theme.css b/web/gui/src/main/webapp/app/fw/svg/sprite-theme.css
new file mode 100644
index 0000000..a65562f
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/svg/sprite-theme.css
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2017-present 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.
+ */
+
+
+/* ------------------------------------------------- */
+/* Sprite Layer */
+
+/* TODO: revisit these styles */
+
+
+#ov-topo svg #topo-sprites .gold1 use {
+    stroke: #fda;
+    fill: none;
+}
+#ov-topo svg #topo-sprites .gold1 text {
+    fill: #eda;
+}
+
+#ov-topo svg #topo-sprites .blue1 use {
+    stroke: #bbd;
+    fill: none;
+}
+#ov-topo svg #topo-sprites .blue1 text {
+    fill: #cce;
+}
+
+#ov-topo svg #topo-sprites .gray1 use {
+    stroke: #ccc;
+    fill: none;
+}
+#ov-topo svg #topo-sprites .gray1 text {
+    fill: #ddd;
+}
+
+/* fills */
+#ov-topo svg #topo-sprites use.fill-gray2 {
+    fill: #eee;
+}
+
+#ov-topo svg #topo-sprites use.fill-blue2 {
+    fill: #bce;
+}
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/fw/svg/sprite.js b/web/gui/src/main/webapp/app/fw/svg/sprite.js
new file mode 100644
index 0000000..2f24f9b
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/svg/sprite.js
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2017-present 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 -- Sprite Service
+ *  For defining sprites and layouts (of sprites).
+ */
+(function () {
+    'use strict';
+
+    // injected references
+    var $log, fs, sds;
+
+    // configuration of default options
+    var optDefaults = {
+        sprite: {
+            builder: {
+                // none for now
+            },
+            addRect: {
+                // none for now
+            },
+            addPath: {
+                // none for now
+            }
+        },
+        layout: {
+            builder: {
+                grid: 10            // grid square size (in layout coord-space
+            },
+            addSprite: {
+                anchor: 'topleft'       // topleft, center
+            },
+            addLabel: {
+                anchor: 'center',       // center, left, right
+                fontStyle: 'normal'     // normal, italic, bold
+            }
+        }
+    };
+
+    // internal state
+    var sprites,    // sprite cache
+        layouts,    // layout cache
+        api;
+
+    // ----------------------------------------------------------------------
+    // === Sprite Builder ===
+
+    // Sample usage:
+    //
+    //     ss.createSprite('foo', 100, 100)
+    //         .addPath('M40,40h20v20h-20z')
+    //         .addRect(50, 50, 10, 20)
+    //         .register();
+
+    function spriteBuilder(id, w, h, opts) {
+        var o = angular.extend({}, optDefaults.sprite.builder, opts),
+            builder,
+            paths = [],
+            rects = [];
+
+        // TODO: verify id has not already been registered
+
+        // x,y is top left corner; w,h is width and height of rectangle
+        function addRect(x, y, w, h, opts) {
+            var o = angular.extend({}, optDefaults.sprite.addRect, opts);
+
+            rects.push({
+                x: x, y: y, w: w, h: h
+            });
+            return builder;
+        }
+
+        function addPath(d, opts) {
+            var o = angular.extend({}, optDefaults.sprite.addPath, opts);
+
+            if (fs.isS(d)) {
+                paths.push(d);
+            } else if (fs.isA(d)) {
+                paths.push(d.join(''));
+            } else {
+                $log.warn('addPath: path not a string or array', d);
+            }
+            return builder;
+        }
+
+        function register() {
+            sprites.set(id, builder);
+        }
+
+        // define the builder object...
+        builder = {
+            type: 'sprite',
+            data: {
+                id: id,
+                w: w,
+                h: h,
+                opts: o
+            },
+            paths: paths,
+            rects: rects,
+
+            // builder API
+            addRect: addRect,
+            addPath: addPath,
+            register: register
+        };
+
+        return builder;
+    }
+
+    // ----------------------------------------------------------------------
+    // === Layout Builder ===
+
+    // Sample usage:
+    //
+    //     ss.createLayout('fooLayout', 400, 300)
+    //         .addSprite('foo', 10, 10, 40)
+    //         .addSprite('foo', 60, 10, 40)
+    //         .addSprite('foo', 110, 10, 40)
+    //         .register();
+
+    function layoutBuilder(id, w, h, opts) {
+        var o = angular.extend({}, optDefaults.layout.builder, opts),
+            builder,
+            sprs = [],
+            labs = [];
+
+        // TODO: verify id has not already been registered
+
+        function addSprite(id, x, y, w, opts) {
+            var o = angular.extend({}, optDefaults.layout.addSprite, opts),
+                s = sprites.get(id);
+
+            if (!s) {
+                $log.warn('no such sprite:', id);
+                return builder;
+            }
+
+            sprs.push({
+                sprite: s, x: x, y: y, w: w, anchor: o.anchor
+            });
+            return builder;
+        }
+
+        function addLabel(text, x, y, opts) {
+            var o = angular.extend({}, optDefaults.layout.addLabel, opts);
+
+            labs.push({
+                text: text, x: x, y: y, anchor: o.anchor, style: o.fontStyle
+            });
+            return builder;
+        }
+
+        function register() {
+            layouts.set(id, builder);
+        }
+
+        // define the builder object...
+        builder = {
+            type: 'layout',
+            data: {
+                id: id,
+                w: w,
+                h: h,
+                opts: o
+            },
+            sprites: sprs,
+            labels: labs,
+
+            // builder API
+            addSprite: addSprite,
+            addLabel: addLabel,
+            register: register
+        };
+
+        return builder;
+    }
+
+    // ----------------------------------------------------------------------
+    // === API functions ===
+
+    // Clears the sprite / layout caches.
+    function clear() {
+        sprites = d3.map();
+        layouts = d3.map();
+    }
+
+    // Initializes the sprite / layout caches with core elements.
+    function init() {
+        sds.registerCoreSprites(api);
+    }
+
+    // Returns a sprite "builder", which can be used to programmatically
+    // define a sprite.
+    function createSprite(id, w, h) {
+        $log.debug('createSprite:', id, w, 'x', h);
+        return spriteBuilder(id, w, h);
+    }
+
+    // Returns a layout "builder", which can be used to programmatically
+    // define a layout.
+    function createLayout(id, w, h, grid) {
+        $log.debug('createLayout:', id, w, 'x', h, '(grid=' + grid + ')');
+        return layoutBuilder(id, w, h, grid);
+    }
+
+    // Registers a sprite defined by the given object (JSON structure).
+    function registerSprite(json) {
+        $log.debug('registerSprite:', json);
+        // TODO: create and register a sprite based on JSON data
+    }
+
+    // Registers a layout defined by the given object (JSON structure).
+    function registerLayout(json) {
+        $log.debug('registerLayout:', json);
+        // TODO: create and register a layout based on JSON data
+    }
+
+    // Returns the sprite with the given ID, or undefined otherwise.
+    function sprite(id) {
+        return sprites.get(id);
+    }
+
+    // Returns the layout with the given ID, or undefined otherwise.
+    function layout(id) {
+        layouts.get(id);
+    }
+
+    // Returns a count of registered sprites and layouts.
+    function count() {
+        return {
+            sprites: sprites.size(),
+            layouts: layouts.size()
+        };
+    }
+
+    // Dumps the cache contents to console
+    function dump() {
+        $log.debug('Dumping Caches...');
+        $log.debug('sprites:', sprites);
+        $log.debug('layouts:', layouts);
+    }
+
+    // ----------------------------------------------------------------------
+
+    angular.module('onosSvg')
+    .factory('SpriteService',
+        ['$log', 'FnService', 'SpriteDataService',
+
+        function (_$log_, _fs_, _sds_) {
+            $log = _$log_;
+            fs = _fs_;
+            sds = _sds_;
+
+            api = {
+                clear: clear,
+                init: init,
+                createSprite: createSprite,
+                createLayout: createLayout,
+                registerSprite: registerSprite,
+                registerLayout: registerLayout,
+                sprite: sprite,
+                layout: layout,
+                count: count,
+                dump: dump
+            };
+            return api;
+        }]
+    )
+    .run(['$log', function ($log) {
+        $log.debug('Clearing sprite and layout caches');
+        clear();
+    }]);
+
+}());
diff --git a/web/gui/src/main/webapp/app/fw/svg/spriteData.js b/web/gui/src/main/webapp/app/fw/svg/spriteData.js
new file mode 100644
index 0000000..756d4b1
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/svg/spriteData.js
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017-present 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 -- Sprite Data Service
+ *  Bundled sprite and layout definitions.
+ */
+(function () {
+    'use strict';
+
+    // ----------------------------------------------------------------------
+    // === Sprite Data ===
+
+    var cloud = {
+        // TODO: define cloud sprite...
+        vbox: '0 0 305 186',
+        d: [
+            "M91.2,48.4C121.2,6.3,187.9-13.4,219,45.6",
+            "M43.1,139.6C21.8,142.9-15.6,108.4,26.1,79",
+            "M103.7,150C89,205.2-11.2,167.4,30.5,138",
+            "M192.3,147.3c-33.5,48-82.1,32.3-94.5-8.2",
+            "M267.1,115c27.9,67.8-77.6,74.3-83.1,41",
+            "M34.3,89.9C10.8,79,59.5,10.7,97.2,39.6",
+            "M211.9,34.2c51.9-38.8,118,57.4,59,94.5"
+        ],
+        style: {
+            fill: 'none',
+            'stroke-miterlimit': 10
+        }
+    };
+
+    // TODO: define other core sprites here...
+
+    // ----------------------------------------------------------------------
+    // === API functions ===
+
+    function registerCoreSprites(ssApi) {
+        ssApi.registerSprite(cloud);
+
+        // TODO: add base set of sprites here ...
+
+        // ----------------------------------------------------------$$$
+        // This following code is for initial development of Topo2 sprite layer
+        ssApi.createSprite('rack', 40, 50)
+            .addRect(0, 0, 40, 50)
+            .addPath([
+                'M5,20h30v5h-30z',
+                'M5,30h30v5h-30z',
+                'M5,40h30v5h-30z'
+            ])
+            .register();
+
+        ssApi.createLayout('segmentRouting', 130, 75)
+            .addSprite('rack', 10, 40, 20)
+            .addSprite('rack', 40, 40, 20)
+            .addSprite('rack', 70, 40, 20)
+            .addSprite('rack', 100, 40, 20)
+            .addLabel('Segment Routing', 120, 10, {anchor: 'right'})
+            .register();
+
+        ssApi.dump();
+        // ----------------------------------------------------------$$$
+    }
+
+    // ----------------------------------------------------------------------
+
+    angular.module('onosSvg')
+        .factory('SpriteDataService', [
+            function () {
+                return {
+                    registerCoreSprites: registerCoreSprites
+                };
+            }
+        ]);
+
+}());
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index 40edebe..9f85a21 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -69,6 +69,8 @@
     <script src="app/fw/svg/map.js"></script>
     <script src="app/fw/svg/zoom.js"></script>
     <script src="app/fw/svg/svgUtil.js"></script>
+    <script src="app/fw/svg/sprite.js"></script>
+    <script src="app/fw/svg/spriteData.js"></script>
 
     <script src="app/fw/remote/remote.js"></script>
     <script src="app/fw/remote/urlfn.js"></script>
@@ -104,6 +106,7 @@
     <link rel="stylesheet" href="app/fw/mast/mast-theme.css">
     <link rel="stylesheet" href="app/fw/svg/glyph.css">
     <link rel="stylesheet" href="app/fw/svg/glyph-theme.css">
+    <link rel="stylesheet" href="app/fw/svg/sprite-theme.css">
     <link rel="stylesheet" href="app/fw/svg/icon.css">
     <link rel="stylesheet" href="app/fw/svg/icon-theme.css">
     <link rel="stylesheet" href="app/fw/layer/panel.css">
@@ -264,7 +267,7 @@
 <script>
     <!-- Inject user agent info into html element to allow CSS sensitivity. -->
     (function () {
-        var t = !!('ontouchstart' in window) || !!('onmsgesturechange' in window);
+        var t = ('ontouchstart' in window) || ('onmsgesturechange' in window);
         d3.select(document.documentElement)
             .attr('data-useragent', navigator.userAgent)
             .attr('data-platform', navigator.platform)
diff --git a/web/gui/src/main/webapp/onos.js b/web/gui/src/main/webapp/onos.js
index 2e0d651..631ef84 100644
--- a/web/gui/src/main/webapp/onos.js
+++ b/web/gui/src/main/webapp/onos.js
@@ -91,10 +91,10 @@
             '$log', '$scope', '$route', '$routeParams', '$location',
             'KeyService', 'ThemeService', 'GlyphService', 'VeilService',
             'PanelService', 'FlashService', 'QuickHelpService', 'EeService',
-            'WebSocketService',
+            'WebSocketService', 'SpriteService',
 
             function (_$log_, $scope, $route, $routeParams, $location,
-                      ks, ts, gs, vs, ps, flash, qhs, ee, wss) {
+                      ks, ts, gs, vs, ps, flash, qhs, ee, wss, ss) {
                 var self = this;
                 $log = _$log_;
 
@@ -112,6 +112,7 @@
                 ks.installOn(d3.select('body'));
                 ks.bindQhs(qhs);
                 gs.init();
+                ss.init();
                 vs.init();
                 ps.init();
                 saucy(ee, ks);
diff --git a/web/gui/src/main/webapp/tests/app/fw/svg/sprite-spec.js b/web/gui/src/main/webapp/tests/app/fw/svg/sprite-spec.js
new file mode 100644
index 0000000..ed7c315
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/fw/svg/sprite-spec.js
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2017-present 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 -- Sprite Service - Unit Tests
+ */
+
+describe('factory: fw/svg/sprite.js', function () {
+    var $log, fs, ss, d3Elem, svg;
+
+    // config...
+    var numBaseSprites = 1,
+        sampleData = 'M91.2,48.4C121.2,6.3,187.9-13.4,219,45.6';
+
+    // load modules we need
+    beforeEach(module('onosUtil', 'onosSvg'));
+
+    // inject the services we need
+    beforeEach(inject(function (_$log_, FnService, SpriteService) {
+        var body = d3.select('body');
+        $log = _$log_;
+        fs = FnService;
+        ss = SpriteService;
+
+        // NOTE: once we get to loading sprites into the DOM, we'll need these:
+        // d3Elem = body.append('defs').attr('id', 'myDefs');
+        // svg = body.append('svg').attr('id', 'mySvg');
+    }));
+
+    // clean up after each test
+    afterEach(function () {
+        // d3.select('#mySvg').remove();
+        // d3.select('#myDefs').remove();
+        ss.clear();
+    });
+
+    // === UNIT TESTS ===
+
+    it('should define SpriteService', function () {
+        expect(ss).toBeDefined();
+    });
+
+    it('should define api functions', function () {
+        expect(fs.areFunctions(ss, [
+            'clear', 'init',
+            'createSprite', 'createLayout',
+            'registerSprite', 'registerLayout',
+            'sprite', 'layout',
+            'count', 'dump'
+        ])).toBe(true);
+    });
+
+    it('should start no sprites or layouts', function () {
+        var c = ss.count();
+        expect(c.sprites).toBe(0);
+        expect(c.layouts).toBe(0);
+    });
+
+    //    Programmatic build of a sprite
+    it('should register a simple sprite', function () {
+        ss.createSprite('foo', 303, 185)
+            .addPath(sampleData)
+            .register();
+
+        expect(ss.count().sprites).toBe(1);
+
+        var s = ss.sprite('foo');
+        expect(s).toBeDefined();
+        // TODO: verify internal structure of sprite
+
+    });
+});
\ No newline at end of file