<commit_before>﻿//This code is 100% based on https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet
//Huge thanks to jawj for implementing it first to make my job easy :-)

L.MarkerCluster.include({

	_2PI: Math.PI * 2,
	_circleFootSeparation: 25, //related to circumference of circle
	_circleStartAngle: Math.PI / 6,

	_spiralFootSeparation:  28, //related to size of spiral (experiment!)
	_spiralLengthStart: 11,
	_spiralLengthFactor: 5,

	_circleSpiralSwitchover: 9, //show spiral instead of circle from this marker count upwards.
								// 0 -> always spiral; Infinity -> always circle

	spiderfy: function () {
		if (this._group._spiderfied == this) {
			return;
		}

		var childMarkers = this.getAllChildMarkers(),
			group = this._group,
			map = group._map,
			center = map.latLngToLayerPoint(this._latlng),
			positions;

		this._group._unspiderfy();
		this._group._spiderfied = this;

		//TODO Maybe: childMarkers order by distance to center

		if (childMarkers.length >= this._circleSpiralSwitchover) {
			positions = this._generatePointsSpiral(childMarkers.length, center);
		} else {
			center.y += 10; //Otherwise circles look wrong
			positions = this._generatePointsCircle(childMarkers.length, center);
		}

		this._animationSpiderfy(childMarkers, positions);
	},

	unspiderfy: function () {
		var group = this._group,
			map = group._map,
			childMarkers = this.getAllChildMarkers(),
			m, i;

		this.setOpacity(1);
		for (i = childMarkers.length - 1; i >= 0; i--) {
			m = childMarkers[i];

			m.setLatLng(m._backupPosSpider);
			delete m._backupPosSpider;
			m.setZIndexOffset(0);

			L.FeatureGroup.prototype.removeLayer.call(group, m);

			map.removeLayer(m._spiderLeg);
			delete m._spiderLeg;
		}

		this._group._spiderfied = null;
	},

	_generatePointsCircle: function (count, centerPt) {
		var circumference = this._circleFootSeparation * (2 + count),
			legLength = circumference / this._2PI,  //radius from circumference
			angleStep = this._2PI / count,
			res = [],
			i, angle;

		res.length = count;

		for (i = count - 1; i >= 0; i--) {
			angle = this._circleStartAngle + i * angleStep;
			res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle));
		}

		return res;
	},

	_generatePointsSpiral: function (count, centerPt) {
		var legLength = this._spiralLengthStart,
			angle = 0,
			res = [],
			i;

		res.length = count;

		for (i = count - 1; i >= 0; i--) {
			angle += this._spiralFootSeparation / legLength + i * 0.0005;
			res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle));
			legLength += this._2PI * this._spiralLengthFactor / angle;
		}
		return res;
	}
});

L.MarkerCluster.include(!L.DomUtil.TRANSITION ? {
	//Non Animated versions of everything
	_animationSpiderfy: function (childMarkers, positions) {
		var group = this._group,
			i, m, leg;

		for (i = childMarkers.length - 1; i >= 0; i--) {
			m = childMarkers[i];

			m._backupPosSpider = m._latlng;
			m.setLatLng(map.layerPointToLatLng(positions[i]));
			m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING

			L.FeatureGroup.prototype.addLayer.call(group, m);

			leg = new L.Polyline([this._latlng, m._latlng], { weight: 1.5, color: '#222' });
			map.addLayer(leg);
			m._spiderLeg = leg;
		}
		this.setOpacity(0.3);
	}
} : {
	//Animated versions here
	_animationSpiderfy: function (childMarkers, positions) {
		var me = this,
			group = this._group,
			i, m, leg;

		for (i = childMarkers.length - 1; i >= 0; i--) {
			m = childMarkers[i];

			m._backupPosSpider = m._latlng;
			m.setLatLng(this._latlng);
			m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING
			m.setOpacity(0);

			L.FeatureGroup.prototype.addLayer.call(group, m);
		}

		setTimeout(function () {
			group._animationStart();
			for (i = childMarkers.length - 1; i >= 0; i--) {
				m = childMarkers[i];

				m.setLatLng(map.layerPointToLatLng(positions[i]));
				m.setOpacity(1);
				//Add Legs. TODO: Fade this in!

				leg = new L.Polyline([me._latlng, m._latlng], { weight: 1.5, color: '#222', opacity: 0 });
				map.addLayer(leg);
				m._spiderLeg = leg;

				//Chrome bug that prevents this working:
				//  http://stackoverflow.com/questions/8455200/svg-animate-with-dynamically-added-elements

				//http://stackoverflow.com/questions/5924238/how-do-you-animate-an-svg-path-in-ios
				//http://dev.opera.com/articles/view/advanced-svg-animation-techniques/

				var length = leg._path.getTotalLength();
				leg._path.setAttribute("stroke-dasharray", length + "," + length);

				var xmlns = "http://www.w3.org/2000/svg";

				var anim = document.createElementNS(xmlns, "animate");
				anim.setAttribute("attributeName", "stroke-dashoffset");
				anim.setAttribute("begin", "indefinite");
				anim.setAttribute("from", length);
				anim.setAttribute("to", 0);
				anim.setAttribute("dur", 0.25);
				leg._path.appendChild(anim);
				anim.beginElement();

				anim = document.createElementNS(xmlns, "animate");
				anim.setAttribute("attributeName", "stroke-opacity");
				anim.setAttribute("attributeName", "stroke-opacity");
				anim.setAttribute("begin", "indefinite");
				anim.setAttribute("from", 0);
				anim.setAttribute("to", 0.5);
				anim.setAttribute("dur", 0.25);
				leg._path.appendChild(anim);
				anim.beginElement();

			}
			me.setOpacity(0.3);

			//Set the opacity of the spiderLegs back to their correct value
			// The animations above override this until they complete.
			// Doing this at 250ms causes some minor flickering on FF, so just do it immediately
			// If the initial opacity of the spiderlegs isn't 0 then they appear before the animation starts.
			setTimeout(function () {
				for (i = childMarkers.length - 1; i >= 0; i--) {
					m = childMarkers[i]._spiderLeg;

					m.options.opacity = 0.5;
					m._path.setAttribute('stroke-opacity', 0.5);
				}
			}, 0);

			setTimeout(function () {


				group._animationEnd();
			}, 250);
		}, 0);
	}

});


L.MarkerClusterGroup.include({
	//The MarkerCluster currently spiderfied (if any)
	_spiderfied: null,

	_spiderfierOnAdd: function () {
		this._map.on('click zoomstart', this._unspiderfy, this);

		if (L.Browser.svg) {
			this._map._initPathRoot(); //Needs to happen in the pageload, not after, or animations don't work in chrome
			//  http://stackoverflow.com/questions/8455200/svg-animate-with-dynamically-added-elements

		}<commit_msg>Tidy ups, comments. Probably works with canvas now (not tested)<commit_after>﻿//This code is 100% based on https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet
//Huge thanks to jawj for implementing it first to make my job easy :-)

L.MarkerCluster.include({

	_2PI: Math.PI * 2,
	_circleFootSeparation: 25, //related to circumference of circle
	_circleStartAngle: Math.PI / 6,

	_spiralFootSeparation:  28, //related to size of spiral (experiment!)
	_spiralLengthStart: 11,
	_spiralLengthFactor: 5,

	_circleSpiralSwitchover: 9, //show spiral instead of circle from this marker count upwards.
								// 0 -> always spiral; Infinity -> always circle

	spiderfy: function () {
		if (this._group._spiderfied == this) {
			return;
		}

		var childMarkers = this.getAllChildMarkers(),
			group = this._group,
			map = group._map,
			center = map.latLngToLayerPoint(this._latlng),
			positions;

		this._group._unspiderfy();
		this._group._spiderfied = this;

		//TODO Maybe: childMarkers order by distance to center

		if (childMarkers.length >= this._circleSpiralSwitchover) {
			positions = this._generatePointsSpiral(childMarkers.length, center);
		} else {
			center.y += 10; //Otherwise circles look wrong
			positions = this._generatePointsCircle(childMarkers.length, center);
		}

		this._animationSpiderfy(childMarkers, positions);
	},

	unspiderfy: function () {
		var group = this._group,
			map = group._map,
			childMarkers = this.getAllChildMarkers(),
			m, i;

		this.setOpacity(1);
		for (i = childMarkers.length - 1; i >= 0; i--) {
			m = childMarkers[i];

			m.setLatLng(m._backupPosSpider);
			delete m._backupPosSpider;
			m.setZIndexOffset(0);

			L.FeatureGroup.prototype.removeLayer.call(group, m);

			map.removeLayer(m._spiderLeg);
			delete m._spiderLeg;
		}

		this._group._spiderfied = null;
	},

	_generatePointsCircle: function (count, centerPt) {
		var circumference = this._circleFootSeparation * (2 + count),
			legLength = circumference / this._2PI,  //radius from circumference
			angleStep = this._2PI / count,
			res = [],
			i, angle;

		res.length = count;

		for (i = count - 1; i >= 0; i--) {
			angle = this._circleStartAngle + i * angleStep;
			res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle));
		}

		return res;
	},

	_generatePointsSpiral: function (count, centerPt) {
		var legLength = this._spiralLengthStart,
			angle = 0,
			res = [],
			i;

		res.length = count;

		for (i = count - 1; i >= 0; i--) {
			angle += this._spiralFootSeparation / legLength + i * 0.0005;
			res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle));
			legLength += this._2PI * this._spiralLengthFactor / angle;
		}
		return res;
	}
});

L.MarkerCluster.include(!L.DomUtil.TRANSITION ? {
	//Non Animated versions of everything
	_animationSpiderfy: function (childMarkers, positions) {
		var group = this._group,
			i, m, leg;

		for (i = childMarkers.length - 1; i >= 0; i--) {
			m = childMarkers[i];

			m._backupPosSpider = m._latlng;
			m.setLatLng(map.layerPointToLatLng(positions[i]));
			m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING

			L.FeatureGroup.prototype.addLayer.call(group, m);

			leg = new L.Polyline([this._latlng, m._latlng], { weight: 1.5, color: '#222' });
			map.addLayer(leg);
			m._spiderLeg = leg;
		}
		this.setOpacity(0.3);
	}
} : {
	//Animated versions here
	_animationSpiderfy: function (childMarkers, positions) {
		var me = this,
			group = this._group,
			i, m, leg;

		for (i = childMarkers.length - 1; i >= 0; i--) {
			m = childMarkers[i];

			m._backupPosSpider = m._latlng;
			m.setLatLng(this._latlng);
			m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING
			m.setOpacity(0);

			L.FeatureGroup.prototype.addLayer.call(group, m);
		}

		setTimeout(function () {
			group._animationStart();

			var initialLegOpacity = L.Browser.svg ? 0 : 0.3,
				xmlns = L.Path.SVG_NS;


			for (i = childMarkers.length - 1; i >= 0; i--) {
				m = childMarkers[i];

				m.setLatLng(map.layerPointToLatLng(positions[i]));
				m.setOpacity(1);
				//Add Legs. TODO: Fade this in!

				leg = new L.Polyline([me._latlng, m._latlng], { weight: 1.5, color: '#222', opacity: initialLegOpacity });
				map.addLayer(leg);
				m._spiderLeg = leg;

				//Following animations don't work for canvas
				if (!L.Browser.svg) {
					continue;
				}

				//How this works:
				//http://stackoverflow.com/questions/5924238/how-do-you-animate-an-svg-path-in-ios
				//http://dev.opera.com/articles/view/advanced-svg-animation-techniques/

				//Animate length
				var length = leg._path.getTotalLength();
				leg._path.setAttribute("stroke-dasharray", length + "," + length);

				var anim = document.createElementNS(xmlns, "animate");
				anim.setAttribute("attributeName", "stroke-dashoffset");
				anim.setAttribute("begin", "indefinite");
				anim.setAttribute("from", length);
				anim.setAttribute("to", 0);
				anim.setAttribute("dur", 0.25);
				leg._path.appendChild(anim);
				anim.beginElement();

				//Animate opacity
				anim = document.createElementNS(xmlns, "animate");
				anim.setAttribute("attributeName", "stroke-opacity");
				anim.setAttribute("attributeName", "stroke-opacity");
				anim.setAttribute("begin", "indefinite");
				anim.setAttribute("from", 0);
				anim.setAttribute("to", 0.5);
				anim.setAttribute("dur", 0.25);
				leg._path.appendChild(anim);
				anim.beginElement();
			}
			me.setOpacity(0.3);

			//Set the opacity of the spiderLegs back to their correct value
			// The animations above override this until they complete.
			// Doing this at 250ms causes some minor flickering on FF, so just do it immediately
			// If the initial opacity of the spiderlegs isn't 0 then they appear before the animation starts.
			if (L.Browser.svg) {
				setTimeout(function() {
					for (i = childMarkers.length - 1; i >= 0; i--) {
						m = childMarkers[i]._spiderLeg;

						m.options.opacity = 0.5;
						m._path.setAttribute('stroke-opacity', 0.5);
					}
				}, 0);
			}

			setTimeout(function () {


				group._animationEnd();
			}, 250);
		}, 0);
	}

});


L.MarkerClusterGroup.include({
	//The MarkerCluster currently spiderfied (if any)
	_spiderfied: null,

	_spiderfierOnAdd: function () {
		this._map.on('click zoomstart', this._unspiderfy, this);

		if (L.Browser.svg) {
			this._map._initPathRoot(); //Needs to happen in the pageload, not after, or animations don't work in chrome
			//  http://stackoverflow.com/questions/8455200/svg-animate-with-dynamically-added-elements

		}
{'fraction_non_alphanumeric': 0.14159901975800276, 'fraction_numerical': 0.018073211824169093, 'mean_word_length': 3.391055817081372, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '102767', 'n_tokens_mistral': 5312, 'n_tokens_neox': 4434, 'n_words': 1020}