loleaflet/Makefile.am                            |    2 
 loleaflet/src/layer/vector/CircleMarker.js       |    2 
 loleaflet/src/layer/vector/Path.Drag.js          |   11 
 loleaflet/src/layer/vector/Path.Transform.SVG.js |   36 ++
 loleaflet/src/layer/vector/Path.js               |  137 +++++++++
 loleaflet/src/layer/vector/Polygon.js            |    4 
 loleaflet/src/layer/vector/Polyline.js           |    2 
 loleaflet/src/layer/vector/Renderer.js           |   67 ++++
 loleaflet/src/layer/vector/SVG.js                |   36 +-
 loleaflet/src/layer/vector/SVGGroup.js           |  224 ++++++++++++---
 loleaflet/src/layer/vector/SplitPanesRenderer.js |   59 ++++
 loleaflet/src/layer/vector/SplitPanesSVG.js      |  323 +++++++++++++++++++++++
 12 files changed, 815 insertions(+), 88 deletions(-)

New commits:
commit a59076e1ca88a6f2c8c67ce45bf769e963cf0717
Author:     Dennis Francis <[email protected]>
AuthorDate: Tue Jul 7 14:29:54 2020 +0530
Commit:     Dennis Francis <[email protected]>
CommitDate: Wed Jul 8 16:57:44 2020 +0200

    add split-panes support for overlay layer
    
    (This is only for svg renderer)
    
    There are separate svg DOM-nodes for each split-pane with view-box set
    appropriately. The L.Path based objects are shared for each
    split-pane, but there will be separate identical 'path' DOM-nodes for
    each svg container.
    
    This patch introduces L.SplitPanesRenderer/L.SplitPanesSVG (has
    same external api as L.Renderer/L.SVG). These are wrapper classes to host
    child renderers, one per split-pane and delegate calls to them
    appropriately.
    
    Change-Id: Id44e9a1312500e6b43cdd8e4f42e235b43d22772
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/98354
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Tested-by: Jenkins
    Reviewed-by: Dennis Francis <[email protected]>

diff --git a/loleaflet/Makefile.am b/loleaflet/Makefile.am
index 2b2dd22c8..589a95448 100644
--- a/loleaflet/Makefile.am
+++ b/loleaflet/Makefile.am
@@ -234,6 +234,8 @@ LOLEAFLET_JS =\
        src/layer/vector/CircleMarker.js \
        src/layer/vector/Circle.js \
        src/layer/vector/SVG.js \
+       src/layer/vector/SplitPanesRenderer.js \
+       src/layer/vector/SplitPanesSVG.js \
        src/layer/vector/Path.Transform.SVG.js \
        src/core/Handler.js \
        src/layer/vector/SVGGroup.js \
diff --git a/loleaflet/src/layer/vector/CircleMarker.js 
b/loleaflet/src/layer/vector/CircleMarker.js
index 837471440..0e45bddf6 100644
--- a/loleaflet/src/layer/vector/CircleMarker.js
+++ b/loleaflet/src/layer/vector/CircleMarker.js
@@ -66,7 +66,7 @@ L.CircleMarker = L.Path.extend({
        },
 
        _empty: function () {
-               return this._radius && 
!this._renderer._bounds.intersects(this._pxBounds);
+               return this._radius && 
!this._renderer.intersectsBounds(this._pxBounds);
        }
 });
 
diff --git a/loleaflet/src/layer/vector/Path.Drag.js 
b/loleaflet/src/layer/vector/Path.Drag.js
index 4ca7ff847..b1375aacf 100644
--- a/loleaflet/src/layer/vector/Path.Drag.js
+++ b/loleaflet/src/layer/vector/Path.Drag.js
@@ -72,9 +72,7 @@ L.Handler.PathDrag = L.Handler.extend(/** @lends  
L.Path.Drag.prototype */ {
                        (this._path.options.className + ' ' + 
L.Handler.PathDrag.DRAGGING_CLS) :
                         L.Handler.PathDrag.DRAGGING_CLS;
 
-               if (this._path._path) {
-                       L.DomUtil.addClass(this._path._path, 
L.Handler.PathDrag.DRAGGING_CLS);
-               }
+               this._path.addClass(L.Handler.PathDrag.DRAGGING_CLS);
        },
 
        /**
@@ -85,9 +83,8 @@ L.Handler.PathDrag = L.Handler.extend(/** @lends  
L.Path.Drag.prototype */ {
 
                this._path.options.className = this._path.options.className
                        .replace(new RegExp('\\s+' + 
L.Handler.PathDrag.DRAGGING_CLS), '');
-               if (this._path._path) {
-                       L.DomUtil.removeClass(this._path._path, 
L.Handler.PathDrag.DRAGGING_CLS);
-               }
+
+               this._path.removeClass(L.Handler.PathDrag.DRAGGING_CLS);
 
                if (!this._path.options.manualDrag) {
                        L.DomEvent.off(document, 'mousemove touchmove', 
this._onDrag,    this);
@@ -119,7 +116,7 @@ L.Handler.PathDrag = L.Handler.extend(/** @lends  
L.Path.Drag.prototype */ {
                this._matrix = [1, 0, 0, 1, 0, 0];
                L.DomEvent.stop(evt.originalEvent);
 
-               L.DomUtil.addClass(this._path._renderer._container, 
'leaflet-interactive');
+               this._path._renderer.addContainerClass('leaflet-interactive');
 
                if (!this._path.options.manualDrag) {
                        L.DomEvent
diff --git a/loleaflet/src/layer/vector/Path.Transform.SVG.js 
b/loleaflet/src/layer/vector/Path.Transform.SVG.js
index fb76c9cf6..4abc4528a 100644
--- a/loleaflet/src/layer/vector/Path.Transform.SVG.js
+++ b/loleaflet/src/layer/vector/Path.Transform.SVG.js
@@ -4,7 +4,7 @@ L.SVG.include({
         * Reset transform matrix
         */
        _resetTransformPath: function(layer) {
-               layer._path.setAttributeNS(null, 'transform', '');
+               layer.getPathNode(this).setAttributeNS(null, 'transform', '');
        },
 
        /**
@@ -13,8 +13,40 @@ L.SVG.include({
         * @param {Array.<Number>} matrix
         */
        transformPath: function(layer, matrix) {
-               layer._path.setAttributeNS(null, 'transform',
+               layer.getPathNode(this).setAttributeNS(null, 'transform',
                        'matrix(' + matrix.join(' ') + ')');
        }
 
 });
+
+L.SplitPanesSVG.include({
+       /**
+        * Reset transform matrix
+        */
+       _resetTransformPath: function(layer) {
+               if (layer.options.fixed === true) {
+                       
this._childRenderers['fixed']._resetTransformPath(layer);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._resetTransformPath(layer);
+               });
+       },
+
+       /**
+        * Applies matrix transformation to SVG
+        * @param {L.Path}         layer
+        * @param {Array.<Number>} matrix
+        */
+       transformPath: function(layer, matrix) {
+               if (layer.options.fixed === true) {
+                       this._childRenderers['fixed'].transformPath(layer, 
matrix);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer.transformPath(layer, matrix);
+               });
+       }
+});
diff --git a/loleaflet/src/layer/vector/Path.js 
b/loleaflet/src/layer/vector/Path.js
index 51665ba9d..1c06e53ef 100644
--- a/loleaflet/src/layer/vector/Path.js
+++ b/loleaflet/src/layer/vector/Path.js
@@ -21,10 +21,12 @@ L.Path = L.Layer.extend({
                fillRule: 'evenodd',
 
                // className: ''
-               interactive: true
+               interactive: true,
+               fixed: false,
        },
 
        onAdd: function () {
+               this._pathNodeCollection = new L.Path.PathNodeCollection();
                this._renderer = this._map.getRenderer(this);
                this._renderer._initPath(this);
                this._reset();
@@ -33,6 +35,7 @@ L.Path = L.Layer.extend({
 
        onRemove: function () {
                this._renderer._removePath(this);
+               this._pathNodeCollection.clear();
        },
 
        getEvents: function () {
@@ -80,5 +83,135 @@ L.Path = L.Layer.extend({
        _clickTolerance: function () {
                // used when doing hit detection for Canvas layers
                return (this.options.stroke ? this.options.weight / 2 : 0) + 
(L.Browser.touch ? 10 : 0);
-       }
+       },
+
+       addPathNode: function (pathNode, actualRenderer) {
+
+               this._path = undefined;
+
+               if (!this._pathNodeCollection) {
+                       this._pathNodeCollection = new 
L.Path.PathNodeCollection();
+               }
+
+               this._pathNodeCollection.add(new L.Path.PathNodeData(pathNode, 
actualRenderer));
+       },
+
+       getPathNode: function (actualRenderer) {
+               return this._pathNodeCollection.getPathNode(actualRenderer);
+       },
+
+       addClass: function (className) {
+               this._pathNodeCollection.addOrRemoveClass(className, true /* 
add */);
+       },
+
+       removeClass: function (className) {
+               this._pathNodeCollection.addOrRemoveClass(className, false /* 
add */);
+       },
+
+});
+
+L.Path.PathNodeData = L.Class.extend({
+
+       initialize: function (pathNode, actualRenderer) {
+
+               console.assert(pathNode, 'invalid pathNode argument!');
+               console.assert(actualRenderer, 'invalid actualRenderer 
argument!');
+
+               if (!(pathNode instanceof Node)) {
+                       console.error('Not a node instance!');
+               }
+
+               this._pathNode = pathNode;
+               this._actualRenderer = actualRenderer;
+               this._data = {};
+       },
+
+       key: function () {
+               return L.Path.PathNodeData.key(this._actualRenderer);
+       },
+
+       getNode: function () {
+               if (!(this._pathNode instanceof Node)) {
+                       console.error('Not a node instance!');
+               }
+               return this._pathNode;
+       },
+
+       getActualRenderer: function () {
+               return this._actualRenderer;
+       },
+
+       setCustomField: function (fieldName, value) {
+               console.assert(typeof fieldName === 'string' && fieldName, 
'invalid fieldName');
+               this._data[fieldName] = value;
+       },
+
+       getCustomField: function (fieldName) {
+               console.assert(typeof fieldName === 'string' && fieldName, 
'invalid fieldName');
+               return this._data[fieldName];
+       },
+
+       clearCustomField: function (fieldName) {
+               console.assert(typeof fieldName === 'string' && fieldName, 
'invalid fieldName');
+               delete this._data[fieldName];
+       },
+
+       addOrRemoveClass: function (className, add) {
+               if (add) {
+                       L.DomUtil.addClass(this._pathNode, className);
+               }
+               else {
+                       L.DomUtil.removeClass(this._pathNode, className);
+               }
+       },
+
+});
+
+L.Path.PathNodeData.key = function (layer) {
+       return L.stamp(layer);
+};
+
+L.Path.PathNodeCollection = L.Class.extend({
+
+       initialize: function () {
+               this.clear();
+       },
+
+       add: function (pathNodeData) {
+
+               console.assert(pathNodeData instanceof L.Path.PathNodeData,
+                       'invalid pathNodeData argument!');
+
+               this._collection[pathNodeData.key()] = pathNodeData;
+       },
+
+       clear: function () {
+               this._collection = {};
+       },
+
+       getPathNode: function (actualRenderer) {
+
+               console.assert(actualRenderer, 'invalid actualRenderer 
argument!');
+               var key = L.Path.PathNodeData.key(actualRenderer);
+               var nodeData = this._collection[key];
+
+               console.assert(nodeData, 'cannot find path node!');
+
+               return nodeData.getNode();
+       },
+
+       forEachNode: function (callback) {
+               var that = this;
+               Object.keys(this._collection).forEach(function (key) {
+                       callback(that._collection[key]);
+               });
+       },
+
+       addOrRemoveClass: function (className, add) {
+               console.assert(className, 'className not provided!');
+               this.forEachNode(function (nodeData) {
+                       nodeData.addOrRemoveClass(className, add);
+               });
+       },
+
 });
diff --git a/loleaflet/src/layer/vector/Polygon.js 
b/loleaflet/src/layer/vector/Polygon.js
index 647438a8b..25118e62c 100644
--- a/loleaflet/src/layer/vector/Polygon.js
+++ b/loleaflet/src/layer/vector/Polygon.js
@@ -42,7 +42,9 @@ L.Polygon = L.Polyline.extend({
        },
 
        _clipPoints: function () {
-               if (this.options.noClip) {
+               if (this.options.noClip || this._renderer instanceof 
L.SplitPanesSVG) {
+                       // TODO: need some work to get this right and 
performant, especially in the case of
+                       // a poly* spread across multiple split-panes.
                        this._parts = this._rings;
                        return;
                }
diff --git a/loleaflet/src/layer/vector/Polyline.js 
b/loleaflet/src/layer/vector/Polyline.js
index c0650db51..0cab5e463 100644
--- a/loleaflet/src/layer/vector/Polyline.js
+++ b/loleaflet/src/layer/vector/Polyline.js
@@ -165,7 +165,7 @@ L.Polyline = L.Path.extend({
 
        // clip polyline by renderer bounds so that we have less to render for 
performance
        _clipPoints: function () {
-               if (this.options.noClip) {
+               if (this.options.noClip || this._renderer instanceof 
L.SplitPanesSVG) {
                        this._parts = this._rings;
                        return;
                }
diff --git a/loleaflet/src/layer/vector/Renderer.js 
b/loleaflet/src/layer/vector/Renderer.js
index eff612bc9..b2507db6d 100644
--- a/loleaflet/src/layer/vector/Renderer.js
+++ b/loleaflet/src/layer/vector/Renderer.js
@@ -17,6 +17,11 @@ L.Renderer = L.Layer.extend({
                L.stamp(this);
        },
 
+       setParentRenderer: function (parent) {
+               console.assert(parent !== this, 'self reference');
+               this._parentRenderer = parent;
+       },
+
        onAdd: function () {
                if (!this._container) {
                        this._initContainer(); // defined by renderer 
implementations
@@ -26,7 +31,13 @@ L.Renderer = L.Layer.extend({
                        }
                }
 
-               this.getPane().appendChild(this._container);
+               if (this._parentRenderer) {
+                       
this._parentRenderer.getContainer().appendChild(this._container);
+               }
+               else {
+                       this.getPane().appendChild(this._container);
+               }
+
                this._update();
        },
 
@@ -53,12 +64,41 @@ L.Renderer = L.Layer.extend({
 
        _update: function () {
                // update pixel bounds of renderer container (for 
positioning/sizing/clipping later)
+               if (this._parentRenderer) {
+                       var posBounds = 
this._parentRenderer.getChildPosBounds(this);
+                       this._position = posBounds.position;
+                       this._bounds = posBounds.bounds;
+                       return;
+               }
+
                var p = this.options.padding,
                    size = this._map.getSize(),
-                   min = 
this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
+                   min = 
this._map.containerPointToLayerPointIgnoreSplits(size.multiplyBy(-p)).round();
 
                this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p 
* 2)).round());
-       }
+               this._position = this._bounds.min;
+       },
+
+       getContainer: function () {
+               return this._container;
+       },
+
+       getBounds: function () {
+               return this._bounds;
+       },
+
+       intersectsBounds: function (pxBounds) {
+               return this._bounds.intersects(pxBounds);
+       },
+
+       addContainerClass: function (className) {
+               L.DomUtil.addClass(this._container, className);
+       },
+
+       removeContainerClass: function (className) {
+               L.DomUtil.removeClass(this._container, className);
+       },
+
 });
 
 
@@ -68,9 +108,17 @@ L.Map.include({
                var renderer = layer.options.renderer || 
this._getPaneRenderer(layer.options.pane) || this.options.renderer || 
this._renderer;
 
                if (!renderer) {
-                       renderer = this._renderer = (L.SVG && L.svg()) || 
(L.Canvas && L.canvas());
+                       if (this._splitPanesContext) {
+                               renderer = this._renderer = (L.SVG && 
L.SplitPanesSVG && L.splitPanesSVG()) ||
+                                       (L.Canvas && L.SplitPanesCanvas && 
L.splitPanesCanvas());
+                       }
+                       else {
+                               renderer = this._renderer = (L.SVG && L.svg()) 
|| (L.Canvas && L.canvas());
+                       }
                }
 
+               console.assert(renderer, 'Could create a renderer!');
+
                if (!this.hasLayer(renderer)) {
                        this.addLayer(renderer);
                }
@@ -84,9 +132,18 @@ L.Map.include({
 
                var renderer = this._paneRenderers[name];
                if (renderer === undefined) {
-                       renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas 
&& L.canvas({pane: name}));
+                       if (this._splitPanesContext) {
+                               renderer = (L.SVG && L.SplitPanesSVG && 
L.splitPanesSVG({pane: name})) ||
+                                       (L.Canvas && L.SplitPanesCanvas && 
L.splitPanesCanvas({pane: name}));
+                       }
+                       else {
+                               renderer = (L.SVG && L.svg({pane: name})) || 
(L.Canvas && L.canvas({pane: name}));
+                       }
+
+                       console.assert(renderer, 'Could create a renderer!');
                        this._paneRenderers[name] = renderer;
                }
+
                return renderer;
        }
 });
diff --git a/loleaflet/src/layer/vector/SVG.js 
b/loleaflet/src/layer/vector/SVG.js
index 4ae1e3edb..3ac66e901 100644
--- a/loleaflet/src/layer/vector/SVG.js
+++ b/loleaflet/src/layer/vector/SVG.js
@@ -17,11 +17,12 @@ L.SVG = L.Renderer.extend({
 
                L.Renderer.prototype._update.call(this);
 
-               var b = this._bounds,
-                   size = b.getSize(),
-                   container = this._container;
+               var b = this._bounds;
+               var size = b.getSize();
+               var position = this._position;
+               var container = this._container;
 
-               L.DomUtil.setPosition(container, b.min);
+               L.DomUtil.setPosition(container, position);
 
                // set size of svg-container if changed
                if (!this._svgSize || !this._svgSize.equals(size)) {
@@ -31,14 +32,15 @@ L.SVG = L.Renderer.extend({
                }
 
                // movement: update container viewBox so that we don't have to 
change coordinates of individual layers
-               L.DomUtil.setPosition(container, b.min);
+               L.DomUtil.setPosition(container, position);
                container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, 
size.y].join(' '));
        },
 
        // methods below are called by vector layers implementations
 
        _initPath: function (layer) {
-               var path = layer._path = L.SVG.create('path');
+               var path = L.SVG.create('path');
+               layer.addPathNode(path, this);
 
                if (layer.options.className) {
                        L.DomUtil.addClass(path, layer.options.className);
@@ -57,7 +59,7 @@ L.SVG = L.Renderer.extend({
        },
 
        _initGroup: function (layer) {
-               layer._path = L.SVG.create('g');
+               layer.addPathNode(L.SVG.create('g'), this);
        },
 
        _fireMouseEvent: function (e) {
@@ -77,21 +79,21 @@ L.SVG = L.Renderer.extend({
        },
 
        _addGroup: function (layer) {
-               this._container.appendChild(layer._path);
+               this._container.appendChild(layer.getPathNode(this));
        },
 
        _addPath: function (layer) {
-               this._container.appendChild(layer._path);
-               layer.addInteractiveTarget(layer._path);
+               this._container.appendChild(layer.getPathNode(this));
+               layer.addInteractiveTarget(layer.getPathNode(this));
        },
 
        _removeGroup: function (layer) {
-               L.DomUtil.remove(layer._path);
+               L.DomUtil.remove(layer.getPathNode(this));
        },
 
        _removePath: function (layer) {
-               L.DomUtil.remove(layer._path);
-               layer.removeInteractiveTarget(layer._path);
+               L.DomUtil.remove(layer.getPathNode(this));
+               layer.removeInteractiveTarget(layer.getPathNode(this));
        },
 
        _updatePath: function (layer) {
@@ -100,7 +102,7 @@ L.SVG = L.Renderer.extend({
        },
 
        _updateStyle: function (layer) {
-               var path = layer._path,
+               var path = layer.getPathNode(this),
                    options = layer.options;
 
                if (!path) { return; }
@@ -158,16 +160,16 @@ L.SVG = L.Renderer.extend({
        },
 
        _setPath: function (layer, path) {
-               layer._path.setAttribute('d', path);
+               layer.getPathNode(this).setAttribute('d', path);
        },
 
        // SVG does not have the concept of zIndex so we resort to changing the 
DOM order of elements
        _bringToFront: function (layer) {
-               L.DomUtil.toFront(layer._path);
+               L.DomUtil.toFront(layer.getPathNode(this));
        },
 
        _bringToBack: function (layer) {
-               L.DomUtil.toBack(layer._path);
+               L.DomUtil.toBack(layer.getPathNode(this));
        }
 });
 
diff --git a/loleaflet/src/layer/vector/SVGGroup.js 
b/loleaflet/src/layer/vector/SVGGroup.js
index 8ded23f26..7aca7c219 100644
--- a/loleaflet/src/layer/vector/SVGGroup.js
+++ b/loleaflet/src/layer/vector/SVGGroup.js
@@ -12,8 +12,10 @@ L.SVGGroup = L.Layer.extend({
 
        initialize: function (bounds, options) {
                L.setOptions(this, options);
+               this._pathNodeCollection = new L.Path.PathNodeCollection();
                this._bounds = bounds;
                this._rect = L.rectangle(bounds, this.options);
+               this._hasSVGNode = false;
                if (L.Browser.touch && !L.Browser.pointer) {
                        this.options.manualDrag = true;
                }
@@ -23,20 +25,28 @@ L.SVGGroup = L.Layer.extend({
        },
 
        setVisible: function (visible) {
-               if (this._svg != null) {
+               this._forEachSVGNode(function (svgNode) {
+                       svgNode.setAttribute('opacity', 0);
                        if (visible)
-                               this._svg.setAttribute('visibility', 'visible');
+                               svgNode.setAttribute('visibility', 'visible');
                        else
-                               this._svg.setAttribute('visibility', 'hidden');
-               }
+                               svgNode.setAttribute('visibility', 'hidden');
+               });
        },
 
        sizeSVG: function () {
+
+               if (!this._hasSVGNode) {
+                       return;
+               }
+
                var size = 
L.bounds(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
                        
this._map.latLngToLayerPoint(this._bounds.getSouthEast())).getSize();
 
-               this._svg.setAttribute('width', size.x);
-               this._svg.setAttribute('height', size.y);
+               this._forEachSVGNode(function (svgNode) {
+                       svgNode.setAttribute('width', size.x);
+                       svgNode.setAttribute('height', size.y);
+               });
        },
 
        parseSVG: function (svgString) {
@@ -45,30 +55,41 @@ L.SVGGroup = L.Layer.extend({
        },
 
        addEmbeddedSVG: function (svgString) {
-               var doc = this.parseSVG(svgString);
+               var svgDoc = this.parseSVG(svgString);
 
-               if (doc.lastChild.localName !== 'svg')
+               if (svgDoc.lastChild.localName !== 'svg')
                        return;
 
-               this._svg = this._path.insertBefore(doc.lastChild, 
this._rect._path);
-               this._dragShape = this._rect._path;
-               this._svg.setAttribute('pointer-events', 'none');
-               this._svg.setAttribute('opacity', this._dragStarted ? 1 : 0);
+               var svgLastChild = svgDoc.lastChild;
+               var thisObj = this;
+               this._forEachGroupNode(function (groupNode, rectNode, nodeData) 
{
+                       var svgNode = groupNode.insertBefore(svgLastChild, 
rectNode);
+                       nodeData.setCustomField('svg', svgNode);
+                       nodeData.setCustomField('dragShape', rectNode);
+                       thisObj._dragShapePresent = true;
+                       svgNode.setAttribute('pointer-events', 'none');
+                       svgNode.setAttribute('opacity', thisObj._dragStarted ? 
1 : 0);
+               });
+
+               this._hasSVGNode = true;
+
                this.sizeSVG();
                this._update();
        },
 
        _onDragStart: function(evt) {
-               if (!this._map || !this._dragShape || !this.dragging)
+               if (!this._map || !this._dragShapePresent || !this.dragging)
                        return;
                this._dragStarted = true;
                this._moved = false;
 
                if (!this.options.manualDrag) {
-                       L.DomEvent.on(this._dragShape, 'mousemove', 
this._onDrag, this);
-                       L.DomEvent.on(this._dragShape, 'mouseup', 
this._onDragEnd, this);
-                       if (this.dragging.constraint)
-                               L.DomEvent.on(this._dragShape, 'mouseout', 
this._onDragEnd, this);
+                       this._forEachDragShape(function (dragShape) {
+                               L.DomEvent.on(dragShape, 'mousemove', 
this._onDrag, this);
+                               L.DomEvent.on(dragShape, 'mouseup', 
this._onDragEnd, this);
+                               if (this.dragging.constraint)
+                                       L.DomEvent.on(dragShape, 'mouseout', 
this._onDragEnd, this);
+                       }.bind(this));
                }
 
                var data = {
@@ -82,7 +103,7 @@ L.SVGGroup = L.Layer.extend({
        },
 
        _onDrag: function(evt) {
-               if (!this._map || !this._dragShape || !this.dragging)
+               if (!this._map || !this._dragShapePresent || !this.dragging)
                        return;
 
                if (!this._moved) {
@@ -94,14 +115,16 @@ L.SVGGroup = L.Layer.extend({
        },
 
        _onDragEnd: function(evt) {
-               if (!this._map || !this._dragShape || !this.dragging)
+               if (!this._map || !this._dragShapePresent || !this.dragging)
                        return;
 
                if (!this.options.manualDrag) {
-                       L.DomEvent.off(this._dragShape, 'mousemove', 
this._onDrag, this);
-                       L.DomEvent.off(this._dragShape, 'mouseup', 
this._onDragEnd, this);
-                       if (this.dragging.constraint)
-                               L.DomEvent.off(this._dragShape, 'mouseout', 
this._onDragEnd, this);
+                       this._forEachDragShape(function (dragShape) {
+                               L.DomEvent.off(dragShape, 'mousemove', 
this._onDrag, this);
+                               L.DomEvent.off(dragShape, 'mouseup', 
this._onDragEnd, this);
+                               if (this.dragging.constraint)
+                                       L.DomEvent.off(dragShape, 'mouseout', 
this._onDragEnd, this);
+                       }.bind(this));
                }
 
                this._moved = false;
@@ -146,53 +169,78 @@ L.SVGGroup = L.Layer.extend({
                this._renderer._initPath(this._rect);
                this._renderer._addGroup(this);
 
-               if (this._path && this._rect._path) {
+               this._forEachGroupNode(function (groupNode, rectNode, nodeData) 
{
+
+                       if (!groupNode || !rectNode) {
+                               return;
+                       }
+
                        this._rect._map = this._map;
                        this._rect._renderer = this._renderer;
-                       L.DomUtil.addClass(this._path, 
'leaflet-control-buttons-disabled');
+                       L.DomUtil.addClass(groupNode, 
'leaflet-control-buttons-disabled');
 
                        if (this.options.svg) {
                                var doc = this.parseSVG(this.options.svg);
                                if (doc && doc.lastChild.localName === 'svg') {
-                                       this._svg = 
this._path.appendChild(doc.lastChild);
-                                       this._svg.setAttribute('opacity', 0);
-                                       
this._svg.setAttribute('pointer-events', 'none');
-                                       this.sizeSVG();
+                                       this._hasSVGNode = true;
+                                       var svgNode = 
groupNode.appendChild(doc.lastChild);
+                                       nodeData.setCustomField('svg', svgNode);
+                                       svgNode.setAttribute('opacity', 0);
+                                       svgNode.setAttribute('pointer-events', 
'none');
                                }
                                delete this.options.svg;
                        }
 
-                       this._path.appendChild(this._rect._path);
-                       this._dragShape = this._rect._path;
+                       groupNode.appendChild(rectNode);
+                       nodeData.setCustomField('dragShape', rectNode);
+                       this._dragShapePresent = true;
 
                        if (!this.options.manualDrag) {
-                               L.DomEvent.on(this._rect._path, 'mousedown', 
this._onDragStart, this);
+                               L.DomEvent.on(rectNode, 'mousedown', 
this._onDragStart, this);
                        }
-               }
+               }.bind(this));
+
+               this.sizeSVG();
+
                this._update();
        },
 
        onRemove: function () {
                this._rect._map = this._rect._renderer = null;
-               this.removeInteractiveTarget(this._rect._path);
-               L.DomUtil.remove(this._rect._path);
+               this._pathNodeCollection.forEachNode(function (nodeData) {
+
+                       var actualRenderer = nodeData.getActualRenderer();
+                       var rectNode = this._rect.getPathNode(actualRenderer);
+
+                       this.removeInteractiveTarget(rectNode);
+                       L.DomUtil.remove(rectNode);
+
+               }.bind(this));
+
                this.removeEmbeddedSVG();
                this._renderer._removeGroup(this);
        },
 
        removeEmbeddedSVG: function () {
-               if (this._svg) {
-                       this._dragShape = null;
-                       L.DomUtil.remove(this._svg);
-                       delete this._svg;
-                       this._update();
+               if (!this._hasSVGNode) {
+                       return;
                }
+
+               this._pathNodeCollection.forEachNode(function (nodeData) {
+                       var svgNode = nodeData.getCustomField('svg');
+                       L.DomUtil.remove(svgNode);
+                       nodeData.clearCustomField('svg');
+               });
+
+               this._dragShapePresent = false;
+               this._hasSVGNode = false;
+               this._update();
        },
 
        _hideEmbeddedSVG: function () {
-               if (this._svg) {
-                       this._svg.setAttribute('opacity', 0);
-               }
+               this._forEachSVGNode(function (svgNode) {
+                       svgNode.setAttribute('opacity', 0);
+               });
        },
 
        _transform: function(matrix) {
@@ -216,23 +264,95 @@ L.SVGGroup = L.Layer.extend({
        },
 
        _showEmbeddedSVG: function () {
-               if (this._svg) {
-                       this._svg.setAttribute('opacity', 1);
-               }
+               this._forEachSVGNode(function (svgNode) {
+                       svgNode.setAttribute('opacity', 1);
+               });
        },
 
        _update: function () {
                this._rect.setBounds(this._bounds);
-               if (this._svg) {
-                       var point = 
this._map.latLngToLayerPoint(this._bounds.getNorthWest());
-                       this._svg.setAttribute('x', point.x);
-                       this._svg.setAttribute('y', point.y);
-               }
+               var point = 
this._map.latLngToLayerPoint(this._bounds.getNorthWest());
+
+               this._forEachSVGNode(function (svgNode) {
+                       svgNode.setAttribute('x', point.x);
+                       svgNode.setAttribute('y', point.y);
+               }.bind(this));
        },
 
        _updatePath: function () {
                this._update();
-       }
+       },
+
+       addPathNode: function (pathNode, actualRenderer) {
+
+               this._path = undefined;
+
+               if (!this._pathNodeCollection) {
+                       this._pathNodeCollection = new 
L.Path.PathNodeCollection();
+               }
+
+               this._pathNodeCollection.add(new L.Path.PathNodeData(pathNode, 
actualRenderer));
+       },
+
+       getPathNode: function (actualRenderer) {
+
+               console.assert(this._pathNodeCollection, 'missing 
_pathNodeCollection member!');
+               return this._pathNodeCollection.getPathNode(actualRenderer);
+       },
+
+       addClass: function (className) {
+               this._pathNodeCollection.addOrRemoveClass(className, true /* 
add */);
+       },
+
+       removeClass: function (className) {
+               this._pathNodeCollection.addOrRemoveClass(className, false /* 
add */);
+       },
+
+       _forEachGroupNode: function (callback) {
+
+               var that = this;
+               this._pathNodeCollection.forEachNode(function (nodeData) {
+
+                       var actualRenderer = nodeData.getActualRenderer();
+                       var groupNode = nodeData.getNode();
+                       var rectNode = that._rect.getPathNode(actualRenderer);
+
+                       callback(groupNode, rectNode, nodeData);
+
+               });
+
+               return true;
+       },
+
+       _forEachSVGNode: function (callback) {
+               if (!this._hasSVGNode) {
+                       return false;
+               }
+
+               this._pathNodeCollection.forEachNode(function (nodeData) {
+                       var svgNode = nodeData.getCustomField('svg');
+                       if (svgNode) {
+                               callback(svgNode);
+                       }
+               });
+
+               return true;
+       },
+
+       _forEachDragShape: function (callback) {
+               if (!this._dragShapePresent) {
+                       return false;
+               }
+
+               this._pathNodeCollection.forEachNode(function (nodeData) {
+                       var dragShape = nodeData.getCustomField('dragShape');
+                       if (dragShape) {
+                               callback(dragShape);
+                       }
+               });
+
+               return true;
+       },
 
 });
 
diff --git a/loleaflet/src/layer/vector/SplitPanesRenderer.js 
b/loleaflet/src/layer/vector/SplitPanesRenderer.js
new file mode 100644
index 000000000..4f7f06073
--- /dev/null
+++ b/loleaflet/src/layer/vector/SplitPanesRenderer.js
@@ -0,0 +1,59 @@
+/* -*- js-indent-level: 8 -*- */
+/*
+ * L.SplitPanesRenderer is a base class for split-panes renderer 
implementations (only SVG for now);
+ * handles renderer container, bounds and zoom animation.
+ */
+
+L.SplitPanesRenderer = L.Layer.extend({
+
+       options: {
+               // how much to extend the clip area around the map view 
(relative to its size)
+               // e.g. 0.1 would be 10% of map view in each direction; 
defaults to clip with the map view
+               padding: 0
+       },
+
+       initialize: function (options) {
+               L.setOptions(this, options);
+               L.stamp(this);
+       },
+
+       onAdd: function () {
+
+               this._splitPanesContext = this._map.getSplitPanesContext();
+               console.assert(this._splitPanesContext, 'no split-panes context 
object!');
+
+               if (!this._container) {
+                       this._initContainer(); // defined by renderer 
implementations
+               }
+
+               this.getPane().appendChild(this._container);
+               this._update();
+       },
+
+       onRemove: function () {
+               L.DomUtil.remove(this._container);
+       },
+
+       setParentRenderer: function () {
+               console.error('SplitPanesRenderer cannot be a child renderer!');
+       },
+
+       // All child renderers have dedicated event listeners.
+       getEvents: function () {
+               return {};
+       },
+
+       _animateZoom: function () {
+       },
+
+       _update: function () {
+       },
+
+       getContainer: function () {
+               return this._container;
+       },
+
+       getBoundsList: function () {
+               return undefined;
+       },
+});
diff --git a/loleaflet/src/layer/vector/SplitPanesSVG.js 
b/loleaflet/src/layer/vector/SplitPanesSVG.js
new file mode 100644
index 000000000..c0f8974ce
--- /dev/null
+++ b/loleaflet/src/layer/vector/SplitPanesSVG.js
@@ -0,0 +1,323 @@
+/* -*- js-indent-level: 8 -*- */
+/*
+ * L.SplitPanesSVG renders vector layers with SVG for split-panes.
+ */
+
+L.SplitPanesSVG = L.SplitPanesRenderer.extend({
+       _initContainer: function () {
+
+               this._container = document.createElement('div');
+
+               this._setupPaneRenderers();
+       },
+
+       _setupPaneRenderers: function () {
+
+               if (this._childRenderers) {
+                       return;
+               }
+
+               var map = this._map;
+               this._rendererIds = ['fixed', 'topleft', 'topright', 
'bottomleft', 'bottomright'];
+               this._splitPaneNames = ['topleft', 'topright', 'bottomleft', 
'bottomright'];
+               this._childRenderers = {};
+               this._rendererIds.forEach(function (rendererId) {
+                       var svgRenderer = L.svg(this.options);
+                       this._childRenderers[rendererId] = svgRenderer;
+                       svgRenderer.rendererId = rendererId;
+                       svgRenderer.setParentRenderer(this);
+                       map.addLayer(svgRenderer);
+               }, this);
+       },
+
+       _forEachPaneRenderer: function (callback) {
+               return this._forEachChildRenderer(callback, true /* skipFixed 
*/);
+       },
+
+       _forEachChildRenderer: function (callback, skipFixed) {
+               if (!this._childRenderers) {
+                       return false;
+               }
+
+               this._rendererIds.forEach(function (rendererId) {
+
+                       if (skipFixed === true && rendererId === 'fixed') {
+                               return;
+                       }
+
+                       var renderer = this._childRenderers[rendererId];
+                       callback(renderer, rendererId);
+
+               }, this);
+
+               return true;
+       },
+
+       _disposePaneRenderers: function () {
+
+               if (!this._childRenderers) {
+                       return;
+               }
+
+               this._rendererIds.forEach(function (rendererId) {
+                       this._map.removeLayer(this._childRenderers[rendererId]);
+                       this._childRenderers[rendererId] = undefined;
+               }, this);
+
+               this._childRenderers = undefined;
+       },
+
+       onRemove: function () {
+               this._disposePaneRenderers();
+               L.SplitPanesRenderer.prototype.onRemove.call(this);
+       },
+
+       getChildPosBounds: function (childRenderer) {
+               console.assert(typeof childRenderer.rendererId === 'string', 
'Child renderer does not have a rendererId!');
+               var rendererId = childRenderer.rendererId;
+               var renderer = this._childRenderers[rendererId];
+               console.assert(renderer && L.stamp(renderer) === 
L.stamp(childRenderer), 'Child renderer does not belong to parent!');
+
+               var splitPos = this._splitPanesContext.getSplitPos();
+               var size = this._map.getSize();
+               var pixelOrigin = this._map.getPixelOrigin();
+               // Container coordinates.
+               var topLeft = new L.Point(0, 0);
+               // pos and boundPos should be in layer coordinates.
+               var pos = undefined;
+               var boundPos = undefined;
+
+               if (rendererId === 'fixed') {
+                       // This is for displaying the pane-splitter 
horizontal/vertical lines.
+                       // is always glued to (0, 0) of the document.
+                       // The size is always the map's view size.
+                       pos = 
this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+                       boundPos = topLeft.subtract(pixelOrigin);
+               }
+               else if (rendererId === 'bottomright') {
+                       // this is the default splitPane where are no visible 
splits (splitPos = (0, 0)).
+                       topLeft = splitPos;
+                       size = size.subtract(splitPos);
+                       pos = 
this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+                       boundPos = pos;
+               }
+               else if (rendererId === 'topleft') {
+                       // is always glued to (0, 0) of the document.
+                       size.x = splitPos.x - 1;
+                       size.y = splitPos.y - 1;
+                       pos = 
this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+                       boundPos = topLeft.subtract(pixelOrigin);
+               }
+               else if (rendererId === 'topright') {
+                       // is always glued to top (y = 0) of the document.
+                       topLeft.x = splitPos.x;
+                       size.x -= splitPos.x;
+                       size.y = splitPos.y - 1;
+                       pos = 
this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+                       boundPos = new L.Point(pos.x, topLeft.y - 
pixelOrigin.y);
+               }
+               else if (rendererId === 'bottomleft') {
+                       // is always glued to left (x = 0) of the document.
+                       topLeft.y = splitPos.y;
+                       size.y -= splitPos.y;
+                       size.x = splitPos.x - 1;
+                       pos = 
this._map.containerPointToLayerPointIgnoreSplits(topLeft).round();
+                       boundPos = new L.Point(topLeft.x - pixelOrigin.x, 
pos.y);
+               }
+               else {
+                       console.error('unhandled rendererId : ' + rendererId);
+               }
+
+               var bounds = new L.Bounds(boundPos, boundPos.add(size));
+
+               return {
+                       bounds: bounds,
+                       position: pos,
+               };
+       },
+
+       getEvents: function () {
+               var events = {
+                       splitposchanged: this._update
+               };
+
+               return events;
+       },
+
+       _update: function () {
+
+               this._forEachChildRenderer(function (renderer) {
+                       renderer._update();
+               });
+       },
+
+       // methods below are called by vector layers implementations
+
+       _initPath: function (layer) {
+
+               if (layer.options.fixed === true) {
+                       this._childRenderers['fixed']._initPath(layer);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._initPath(layer);
+               });
+       },
+
+       _initGroup: function (layer) {
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._initGroup(layer);
+               });
+       },
+
+       _fireMouseEvent: function () {
+               // child renderers listen for ['mouseenter', 'mouseout'], and 
it refires with additional info
+               // but these events have any listeners ? may be DomEvent.js 
generates other events ?
+
+               // TODO: make the child renderers call this and create the 
right ones.
+       },
+
+       _addGroup: function (layer) {
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._addGroup(layer);
+               });
+       },
+
+       _addPath: function (layer) {
+
+               if (layer.options.fixed === true) {
+                       this._childRenderers['fixed']._addPath(layer);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._addPath(layer);
+               });
+       },
+
+       _removeGroup: function (layer) {
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._removeGroup(layer);
+               });
+       },
+
+       _removePath: function (layer) {
+               if (layer.options.fixed === true) {
+                       this._childRenderers['fixed']._removePath(layer);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._removePath(layer);
+               });
+       },
+
+       // should not forward to children.
+       _updatePath: function (layer) {
+               layer._project();
+               layer._update();
+       },
+
+       _updateStyle: function (layer) {
+
+               if (layer.options.fixed === true) {
+                       this._childRenderers['fixed']._updateStyle(layer);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._updateStyle(layer);
+               });
+       },
+
+       // enough to forward the _setPath for the actual path-node modification 
part.
+       _updatePoly: function (layer, closed) {
+               this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed));
+       },
+
+       // enough to forward the _setPath for the actual path-node modification 
part.
+       _updateCircle: function (layer) {
+               var p = layer._point;
+               var r = layer._radius;
+               var r2 = layer._radiusY || r;
+               var arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
+
+               // drawing a circle with two half-arcs
+               var d = layer._empty() ? 'M0 0' :
+                       'M' + (p.x - r) + ',' + p.y +
+                       arc + (r * 2) + ',0 ' +
+                       arc + (-r * 2) + ',0 ';
+
+               this._setPath(layer, d);
+       },
+
+       _setPath: function (layer, path) {
+
+               if (layer.options.fixed === true) {
+                       this._childRenderers['fixed']._setPath(layer, path);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._setPath(layer, path);
+               });
+       },
+
+       // SVG does not have the concept of zIndex so we resort to changing the 
DOM order of elements
+       _bringToFront: function (layer) {
+               if (layer.options.fixed === true) {
+                       this._childRenderers['fixed']._bringToFront(layer);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._bringToFront(layer);
+               });
+       },
+
+       _bringToBack: function (layer) {
+
+               if (layer.options.fixed === true) {
+                       this._childRenderers['fixed']._bringToBack(layer);
+                       return;
+               }
+
+               this._forEachPaneRenderer(function (paneRenderer) {
+                       paneRenderer._bringToBack(layer);
+               });
+       },
+
+       intersectsBounds: function (pxBounds) {
+               for (var i = 0; i < this._rendererIds.length; ++i) {
+                       var rendererId = this._rendererIds[i];
+                       if 
(this._childRenderers[rendererId].intersectsBounds(pxBounds)) {
+                               return true;
+                       }
+               }
+
+               return false;
+       },
+
+       addContainerClass: function (className) {
+               L.DomUtil.addClass(this._container, className);
+               this._forEachChildRenderer(function (childRenderer) {
+                       childRenderer.addContainerClass(className);
+               });
+       },
+
+       removeContainerClass: function (className) {
+               L.DomUtil.removeClass(this._container, className);
+               this._forEachChildRenderer(function (childRenderer) {
+                       childRenderer.removeContainerClass(className);
+               });
+       },
+
+});
+
+L.splitPanesSVG = function (options) {
+       return new L.SplitPanesSVG(options);
+};
_______________________________________________
Libreoffice-commits mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to