/* * L.SVG renders vector layers with SVG. All SVG-specific code goes here. */ L.SVG = L.Renderer.extend({ _initContainer: function () { this._container = L.SVG.create('svg'); this._paths = {}; this._initEvents(); // makes it possible to click through svg root; we'll reset it back in individual paths this._container.setAttribute('pointer-events', 'none'); }, _update: function () { if (this._map._animatingZoom) { return; } L.Renderer.prototype._update.call(this); var b = this._bounds, size = b.getSize(), container = this._container, pane = this.getPane(); // hack to make flicker on drag end on mobile webkit less irritating if (L.Browser.mobileWebkit) { pane.removeChild(container); } L.DomUtil.setPosition(container, b.min); // update container viewBox so that we don't have to change coordinates of individual layers container.setAttribute('width', size.x); container.setAttribute('height', size.y); container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' ')); if (L.Browser.mobileWebkit) { pane.appendChild(container); } }, // methods below are called by vector layers implementations _initPath: function (layer) { var path = layer._path = L.SVG.create('path'); if (layer.options.className) { L.DomUtil.addClass(path, layer.options.className); } if (layer.options.clickable) { L.DomUtil.addClass(path, 'leaflet-clickable'); } this._updateStyle(layer); }, _addPath: function (layer) { var path = layer._path; this._container.appendChild(path); this._paths[L.stamp(path)] = layer; }, _removePath: function (layer) { var path = layer._path; L.DomUtil.remove(path); delete this._paths[L.stamp(path)]; }, _updatePath: function (layer) { layer._project(); layer._update(); }, _updateStyle: function (layer) { var path = layer._path, options = layer.options; if (!path) { return; } if (options.stroke) { path.setAttribute('stroke', options.color); path.setAttribute('stroke-opacity', options.opacity); path.setAttribute('stroke-width', options.weight); path.setAttribute('stroke-linecap', options.lineCap); path.setAttribute('stroke-linejoin', options.lineJoin); if (options.dashArray) { path.setAttribute('stroke-dasharray', options.dashArray); } else { path.removeAttribute('stroke-dasharray'); } } else { path.setAttribute('stroke', 'none'); } if (options.fill) { path.setAttribute('fill', options.fillColor || options.color); path.setAttribute('fill-opacity', options.fillOpacity); path.setAttribute('fill-rule', 'evenodd'); } else { path.setAttribute('fill', 'none'); } path.setAttribute('pointer-events', options.pointerEvents || (options.clickable ? 'auto' : 'none')); }, _updatePoly: function (layer, closed) { this._setPath(layer, L.SVG.pointsToPath(layer._parts, closed)); }, _updateCircle: function (layer) { var p = layer._point, r = layer._radius, r2 = layer._radiusY || r, 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) { layer._path.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); }, _bringToBack: function (layer) { L.DomUtil.toBack(layer._path); }, // TODO remove duplication with L.Map _initEvents: function () { L.DomEvent.on(this._container, 'click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu', this._fireMouseEvent, this); }, _fireMouseEvent: function (e) { this._paths[L.stamp(e.target)]._fireMouseEvent(e); } }); L.extend(L.SVG, { create: function (name) { return document.createElementNS('http://www.w3.org/2000/svg', name); }, // generates SVG path string for multiple rings, with each ring turning into "M..L..L.." instructions pointsToPath: function (rings, closed) { var str = '', i, j, len, len2, points, p; for (i = 0, len = rings.length; i < len; i++) { points = rings[i]; for (j = 0, len2 = points.length; j < len2; j++) { p = points[j]; str += (j ? 'L' : 'M') + p.x + ' ' + p.y; } // closes the ring for polygons; "x" is VML syntax str += closed ? (L.Browser.svg ? 'z' : 'x') : ''; } // SVG complains about empty path strings return str || 'M0 0'; } }); L.Browser.svg = !!(document.createElementNS && L.SVG.create('svg').createSVGRect); L.svg = function (options) { return L.Browser.svg || L.Browser.vml ? new L.SVG(options) : null; }; // default instance to use when adding vectors to the map L.SVG.instance = L.svg();