<?php
session_start();
?>

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <link rel="icon" href="logo.png">
  <title>InterMap</title>
  <meta name="description" content="InterMap">
  <meta name="author" content="AD">
  <link rel="stylesheet" href="style.css">
  <link rel="stylesheet" href="image-picker/image-picker.css">
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:700,400&amp;subset=cyrillic,latin,greek,vietnamese">
</head>

<body>
<script src="snap/dist/snap.svg-min.js"></script>
<script src="jszip/dist/jszip.min.js"></script>
<script src="FileSaver/FileSaver.js"></script>
<script src="jscolor/jscolor.js"></script>
<script src="jquery/jquery-3.2.1.min.js"></script>
<script src="image-picker/image-picker.min.js"></script>

<div id="paper">
    <svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg"></svg>
</div>

<div id="leftPanel">
	<div id="dNoElementSelected">
		<p id="pNoElementSelected">Click on an element to modify its properties</p>
		<div class="bar"></div>
	</div>
	<div id="dElementSelected" style="display: none;">
		<div id="dModifiersContainer">
			<p id="pNoModifiers">No modifiers are present on this element yet.</p>
			
			<div class="modifier">
				<table class="fullWidth">
					<tr>
						<td> <p class="noMargin modifierTitle">> Modifier1</p> </td>
						<td class="rightText"> <button class="niceButton redButton" onclick="">REMOVE MODIFIER</button> </td>
					</tr>
				</table>
				
				<table class="fullWidth">
					<tr>
						<td> <p class="noMargin modifierHalfTitle">Conditions : </p> </td>
						<td class="rightText"> <button class="niceButton greenButton">+ ADD CONDITION</button> </td>
					</tr>
				</table>
				</br>
				<div class="conditions">
					<p class="noMargin greyText templateNoConditions">No conditions are set for this modifier. All effects will be applied.</p>
					<div class="condition templateConditionDefault">Default State</div>
					<div class="condition templateConditionSelection">When Selected</div>
					<div class="condition templateConditionHover">When Hovered</div>
					<div class="condition templateConditionLayerSelection">
						<p class="noMargin modifierQuarterTitle">On Layer Selection</p>
						<p> Layer : <select>
						</select></p>
					</div>
				</div>
				</br>
				<table class="fullWidth">
					<tr>
						<td> <p class="noMargin modifierHalfTitle">Effects : </p> </td>
						<td class="rightText"> <button class="niceButton greenButton" onclick="">+ ADD EFFECT</button> </td>
					</tr>
				</table>
				</br>
				<div class="effects">
					<p class="noMargin greyText templateNoEffects">No effects are set for this modifier. It won't do anything.</p>
					<div class="condition templateEffectFill">
						<p class="noMargin modifierQuarterTitle">Change fill color</p>
						<p><input class="colorPicker jscolor{hash:true,onFineChange:'updateEffectColor(this)'}" value="#ffffff"></p>
					</div>
					<div class="condition templateEffectStroke">
						<p class="noMargin modifierQuarterTitle">Change stroke color</p>
						<p><input class="colorPicker jscolor{hash:true,onFineChange:'updateEffectColor(this)'}" value="#ffffff"></p>
					</div>
					<div class="condition templateEffectInfoPanel">
						<p class="noMargin modifierQuarterTitle">Display an info panel</p>
						<textarea rows="10" cols="50" placeholder="No info will be displayed" input="updateInfoPanelText()"></textarea>
						<p> Position : <select>
							<option value="right">Right</option>
							<option value="top">Top</option>
							<option value="left">Left</option>
							<option value="bottom">Bottom</option>
						</select></p>
						<p> Horizontal Offset : <input type="number" class="fifthWidth"> px</p>
						<p> Vertical Offset : <input type="number" class="fifthWidth"> px</p>
						<p> Horizontal Scale : <input type="number" step="0.02" min="0.01" class="fifthWidth"></p>
						<p> Vertical Scale : <input type="number" step="0.02" min="0.01" class="fifthWidth"></p>
						<p> Style : <select class="image-picker show-html">
						</select></p>
					</div>
					<div class="condition templateEffectLegend">
						<p class="noMargin modifierQuarterTitle">Set as legend</p>
						<p> Position : <select>
							<option value="bottom-right">Bottom-Right</option>
							<option value="bottom-left">Bottom-Left</option>
							<option value="top-right">Top-Right</option>
							<option value="top-left">Top-Left</option>
						</select></p>
						<p> Scale : <input type="number" step="0.02" min="0.01" class="fifthWidth"></p>
					</div>
					<div class="condition templateEffectPathAnimation">
						<p class="noMargin modifierQuarterTitle">Path animation</p>
						<p> Time : <input type="number" class="fifthWidth"> ms</p>
						<p> Start : <input type="number" step="0.5" min="0" max="100" class="fifthWidth"> %</p>
						<p> End : <input type="number" step="0.5" min="0" max="100" class="fifthWidth"> %</p>
					</div>
					<div class="condition templateEffectCenterCamera">
						<p class="noMargin modifierQuarterTitle">Center Camera on element</p>
					</div>
				</div>
				</br>
			</div>
		</div>
		<button class="niceButton greenButton" onclick="addEmptyModifier(selectedElement);reconstructModifiersUI(selectedElement)">+ ADD MODIFIER</button>
	</div>
</div>

<div id="interface">
    <button class="niceButton orangeButton" onclick="showPopUp('customSvgFile')">CHANGE TEMPLATE</button>
	<button class="niceButton orangeButton" onclick="showPopUp('importData')">IMPORT MAP</button>
    <button class="niceButton orangeButton" onclick="exportData()">EXPORT MAP</button>
	
    <div class="bar"></div>
	<button class="niceButton orangeButton" onclick="testMap()">TEST MAP</button>
    <button class="niceButton orangeButton" onclick="showPopUp('integration')">INTEGRATION TO YOUR SITE</button>
	
	<div class="bar"></div>
	<button class="niceButton orangeButton" onclick="showPopUp('globalOptions')">GLOBAL OPTIONS</button>
    <button class="niceButton orangeButton" onclick="tryShowPopUpTimeline()">TIMELINE</button>
	
	<div class="bar"></div>
    <button class="niceButton orangeButton" onclick="showPopUp('customInfoPanel')">IMPORT CUSTOM INFO PANEL</button>
	<!--IRELAND :
	<div class="bar"></div>
    <button class="niceButton blueButton" onclick="selectElement(s.select('#elem43'))">SELECT Port</button>
    <button class="niceButton blueButton" onclick="hideElement(s.select('#elem25'));hideElement(s.select('#elem29'));animatePath(s.select('#elem45'));selectElement(s.select('#elem41'))">Anim1</button>
    <button class="niceButton blueButton" onclick="showElement(s.select('#elem25'));animatePath(s.select('#elem25'));selectElement(s.select('#elem47'))">Anim2</button>
    <button class="niceButton blueButton" onclick="showElement(s.select('#elem29'));animatePath(s.select('#elem29'));selectElement(s.select('#elem43'))">Anim3</button>-->
	
	<!--EUROPE :
	<div class="bar"></div>
    <button class="niceButton blueButton" onclick="selectElement(s.select('#elem167'))">SELECT Gen</button>
    <button class="niceButton blueButton" onclick="animatePath(s.select('#elem165'));selectElement(s.select('#elem169'))">Anim1</button>-->
</div>

<div id="bgpopup" onClick="hidePopUp()">
</div>

<div id="popUpAddCondition" class="popup">
	<h3>Select the condition you want to add</h3>
	<p>
		<select id="ddConditionChoice" onchange="changeDescriptionCondition()">
			<option value="default">Default</option>
			<option value="selection">Selection</option>
			<option value="hover">Hover</option>
			<option value="layerSelection">Layer Selection</option>
		</select>
		<button class="niceButton smallButton3 blueButton" onclick="addCondition()">ADD</button>
	</p>
	<div id="dConditionDescription">
		<p id="pDescriptionConditionDefault"> This modifier will only be applied when the element is not selected nor hovered </p>
		<p id="pDescriptionConditionSelection"> This modifier will be applied when the element is selected by a mouse click</p>
		<p id="pDescriptionConditionHover"> This modifier will be applied when the element is hovered by the mouse</p>
		<p id="pDescriptionConditionLayerSelection"> This modifier will be applied when a given layer becomes selected in the timeline</p>
	</div>
</div>

<div id="popUpAddEffect" class="popup">
	<h3>Select the effect you want to add</h3>
	<p>
		<select id="ddEffectChoice" onchange="changeDescriptionEffect()">
			<option value="fill">Fill color</option>
			<option value="stroke">Stroke color</option>
			<option value="infoPanel">Info panel</option>
			<option value="legend">Legend</option>
			<option value="pathAnimation">Path animation</option>
			<option value="centerCamera">Center camera</option>
		</select>
		<button class="niceButton smallButton3 blueButton" onclick="addEffect()">ADD</button>
	</p>
	<div id="dEffectDescription">
		<p id="pDescriptionEffectFill"> Changes the fill color of the element to the chosen color </p>
		<p id="pDescriptionEffectStroke"> Changes the stroke color of the element to the chosen color </p>
		<p id="pDescriptionEffectInfoPanel"> Displays a customable info panel </p>
		<p id="pDescriptionEffectLegend"> This element will no longer be movable nor zoomable, and can therefore be used as the legend for your map </p>
		<p id="pDescriptionEffectPathAnimation"> All paths on this element will be displayed as they are getting drawn </p>
		<p id="pDescriptionEffectCenterCamera"> The camera will be centered on this element </p>
	</div>
</div>

<div id="popUpCustomSvgFile" class="popup">
	<h3>Change the template of your interactive map</h3>
	<table>
		<tr>
			<td><h3>Earth</h3></td>
			<td><img src="thumbnails/test.png" alt="Earth" style="width:256px;height:128px;"></td>
			<td>Map of the entire Earth, showing borders and lakes.</td>
			<td><button class="niceButton smallButton2 greenButton" onClick="loadLocalSvgFile('test.svg')">SELECT</button></td>
		</tr>
		<tr>
			<td><h3>Test</h3></td>
			<td><img src="thumbnails/test2.png" alt="Test" style="width:256px;height:128px;"></td>
			<td>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam a tincidunt nunc.</td>
			<td><button class="niceButton smallButton2 greenButton" onClick="loadLocalSvgFile('test2.svg')">SELECT</button></td>
		</tr>
	</table>
	</br>
	</br>
	<p> Upload your own .svg file : <input type="file" id="fileCustomSvgFile"></input> <button class="niceButton smallButton2 blueButton" onClick="loadCustomSvgFile()">SELECT</button> </p>
</div>

<div id="popUpCustomInfoPanel" class="popup">
	<h3>Upload a .svg to create a new style for your info panels</h3>
	<p> The .svg must contain a rectangle which has a fill color of <u style="color:#c0ffee">#c0ffee</u> (or <u style="color:#c0ffee">c0ffeeff</u>  in inkscape).</p>
	<p> This rectangle will be replaced by your customizable text.</p>
	<p><input type="file" id="fileCustomInfoPanel"></input></p>
	<p><button class="niceButton smallButton2 blueButton" onClick="loadCustomInfoPanel()">SELECT</button></p>
</div>

<div id="popUpImportData" class="popup">
	<h3>Import a previously exported Inter Map</h3>
	<p> Select the .zip file exported earlier </p>
	<p><input type="file" id="fileImportData"></input></p>
	<p><button class="niceButton smallButton2 blueButton" onClick="importData()">SELECT</button></p>
</div>

<div id="popUpGlobalOptions" class="popup">
	<h3>Global Options</h3>
	<h4>Camera movement on selection</h4>
	<p>Move camera on selection : <input type="checkbox" id="cCameraMovement" onchange="config.cameraMovementEnabled = this.checked;document.getElementById('ddCameraMovement').disabled = !this.checked;"></p>
	<p>Movement method : <select id="ddCameraMovement" onchange="config.cameraMovementStyle = this.value;">
		<option value="standard">Direct</option>
		<option value="dezoom">Dezoom and zoom</option>
		<option value="fast">Fast</option>
	</select></p>
</div>

<div id="popUpTimeline" class="popup">
	<h3>Timeline</h3>
	<p> Upload your custom timeline : <input type="file" id="fileCustomTimeline"></input> <button class="niceButton smallButton2 blueButton" onClick="loadCustomTimeline()">SELECT</button> </p>
</div>

<div id="popUpNewTimeline" class="popup">
	<h3>Timeline</h3>
	<p> This .svg file contains several layers. A timeline could be created, displaying only one layer at a time.</p>
	<p> Choose the layers that will be part of the timeline, the others will remain untouched. If no or one layer is selected, no timeline will be created. </p>
	<div id="dTimelineCheckBoxes">
	</div>
	<p><button class="niceButton smallButton2 blueButton" onClick="validateNewTimeline()">VALIDATE</button></p>
</div>

<div id="popUpIntegration" class="popup">
	<h3>Integrate the interactive map to your website</h3>
	<p> To integrate the map in your site, follow these steps : </p>
	<p> 1- Copy the following code snippet and paste it where you want your map to be displayed </p>
	<p><textarea rows="8" cols="50" disabled>
<script src="PATH_TO_SNAP/dist/snap.svg-min.js"></script>

<div style="width:100%;height:800px;background:#F0F0F0">
	<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:100%"></svg>
</div>

<script src="PATH_TO_DATA/data.json"></script>
<script src="PATH_TO_INTER/inter.js"></script></textarea><p>
	<p> 2- Download <a href="http://snapsvg.io/">the Snap.svg library</a>, place the downloaded folder on your website, and replace PATH_TO_SNAP in the code you pasted by the path of the folder on your site </p>
	<p> 3- Download <a href="http://feudal-ambitions.com/InterMap/inter.js" download="inter.js">inter.js</a>, place the downloaded file on your website, and replace PATH_TO_INTER in the code you pasted by the path of the file on your site </p>
	<p> 4- Export your map, extract the file data.json inside, place it on your website, and replace PATH_TO_DATA in the code you pasted by the path of the file on your site </p>
	<p> If, for any reason, you cannot add files on your website, you can remove the <i>src</i> attribute of each <i>script</i> tag, and paste the content of the file before <i>&lt;/script></i>.
	For instance <i>&lt;script src="PATH_TO_INTER/inter.js">&lt;/script></i> will become <i>&lt;script>[CONTENT OF inter.js]&lt;/script></i>.</p>
</div>

<div id="popUpLoading" class="popup">
	<h3>Loading ...</h3>
</div>

<script>
var zoomSpeed = 1.1;
var currentZoom = 1;

var s = Snap("#svg");

var paper = document.getElementById("svg");
if (paper.addEventListener) {
	paper.addEventListener("mousewheel", mouseWheelHandler, false);
	paper.addEventListener("DOMMouseScroll", mouseWheelHandler, false);
}
else {
	paper.attachEvent("onmousewheel", mouseWheelHandler);
}

var svgX = paper.getBoundingClientRect().left;
var svgY = paper.getBoundingClientRect().top;

var superGroup, mapGroup, legendGroup, timelineGroup, tempGroup;
var layerNullMap, layerNullLegend;
var svgFileContent = null;
var timelineContent = null;
var id = 0;
var customId = 0;

var allModifiers = [];
var allElements = [];
var elementsData = {};

var layerNames = [];
var layers = {};
var timelineLayers = [];
var isTimelineActive = true;
var elementsInNullLayer = false;
var layerCountInTimeline = 0;
var selectedLayer = null;

var infoPanelNames = [];
var infoPanels = {};

var selectedElement = null;
var currentModifier = null;

var areGroupsFinal = true;

var config = {
	cameraMovementEnabled : true,
	cameraMovementStyle : "standard"
};

var dNoElementSelected = document.getElementById("dNoElementSelected");
var dElementSelected = document.getElementById("dElementSelected");

var dModifiersContainer = document.getElementById("dModifiersContainer");
var pNoModifiers = document.getElementById("pNoModifiers");

var dTimelineCheckBoxes = document.getElementById("dTimelineCheckBoxes");

var templateModifier = document.getElementsByClassName("modifier")[0];

var templateConditions = {};
templateConditions['none'] = document.getElementsByClassName("templateNoConditions")[0];
templateConditions['default'] = document.getElementsByClassName("templateConditionDefault")[0];
templateConditions['selection'] = document.getElementsByClassName("templateConditionSelection")[0];
templateConditions['hover'] = document.getElementsByClassName("templateConditionHover")[0];
templateConditions['layerSelection'] = document.getElementsByClassName("templateConditionLayerSelection")[0];

var templateEffects = {};
templateEffects['none'] = document.getElementsByClassName("templateNoEffects")[0];
templateEffects['fill'] = document.getElementsByClassName("templateEffectFill")[0];
templateEffects['stroke'] = document.getElementsByClassName("templateEffectStroke")[0];
templateEffects['infoPanel'] = document.getElementsByClassName("templateEffectInfoPanel")[0];
templateEffects['legend'] = document.getElementsByClassName("templateEffectLegend")[0];
templateEffects['pathAnimation'] = document.getElementsByClassName("templateEffectPathAnimation")[0];
templateEffects['centerCamera'] = document.getElementsByClassName("templateEffectCenterCamera")[0];

var ddConditionChoice = document.getElementById("ddConditionChoice");
var dConditionDescription = document.getElementById("dConditionDescription");
var descriptionConditions = {};
descriptionConditions['default'] = document.getElementById("pDescriptionConditionDefault");
descriptionConditions['selection'] = document.getElementById("pDescriptionConditionSelection");
descriptionConditions['hover'] = document.getElementById("pDescriptionConditionHover");
descriptionConditions['layerSelection'] = document.getElementById("pDescriptionConditionLayerSelection");

var ddEffectChoice = document.getElementById("ddEffectChoice");
var dEffectDescription = document.getElementById("dEffectDescription");
var descriptionEffects = {};
descriptionEffects['fill'] = document.getElementById("pDescriptionEffectFill");
descriptionEffects['stroke'] = document.getElementById("pDescriptionEffectStroke");
descriptionEffects['infoPanel'] = document.getElementById("pDescriptionEffectInfoPanel");
descriptionEffects['legend'] = document.getElementById("pDescriptionEffectLegend");
descriptionEffects['pathAnimation'] = document.getElementById("pDescriptionEffectPathAnimation");
descriptionEffects['centerCamera'] = document.getElementById("pDescriptionEffectCenterCamera");

var bgpopup = document.getElementById("bgpopup");
var popups = {};
popups["addCondition"] = document.getElementById("popUpAddCondition");
popups["addEffect"] = document.getElementById("popUpAddEffect");
popups["customSvgFile"] = document.getElementById("popUpCustomSvgFile");
popups["customInfoPanel"] = document.getElementById("popUpCustomInfoPanel");
popups["importData"] = document.getElementById("popUpImportData");
popups["globalOptions"] = document.getElementById("popUpGlobalOptions");
popups["timeline"] = document.getElementById("popUpTimeline");
popups["newTimeline"] = document.getElementById("popUpNewTimeline");
popups["integration"] = document.getElementById("popUpIntegration");
popups["loading"] = document.getElementById("popUpLoading");

hidePopUp();
changeDescriptionCondition();
changeDescriptionEffect();
setConfigUI();

loadLocalInfoPanel("classic");
loadLocalInfoPanel("modern");
loadLocalInfoPanel("modern2");
loadLocalInfoPanel("modern3");

loadLocalSvgFile("test2modified.svg");

//------------------------------------------------------------------------
//Utils
//------------------------------------------------------------------------

function isElementFinal(element) {
	if (areGroupsFinal) {
		return element.children().length == 0 || (element.type == "g" && !(element.attr("id") && (element.attr("id").startsWith("layer") || element.attr("id") == "tempGroup" || element.attr("id") == "mapGroup" || element.attr("id") == "timelineGroup")));
	}
	else {
		return element.children().length == 0;
	}
	
}

function isElementReal(element) {
	return !(element.type.includes("#") || element.type.includes(":") || element.type == "defs");
}

function isLayer(element) {
	return element.type == "g" && element.attr("id") && element.attr("id").startsWith("layer");
}

function setFillColor(colorFill, element) {
	element.attr({
		fill : colorFill
	});
	
	element.children().forEach(function(child) {
		if (child.type != "#text") {
			setFillColor(colorFill, child);
		}
	});
}

function animateFillColor(colorFill, element) {
	var currentColor = Snap.color(element.attr("fill"))
	
	var fromFill;
	if (currentColor.hex == "none") {
		fromFill = Snap.color("black");
	}
	else {
		fromFill = currentColor;
	}
	
	var toFill = Snap.color(colorFill)
	
	Snap.animate([fromFill.r, fromFill.g, fromFill.b], [toFill.r, toFill.g, toFill.b], function (val) {
		var fillColor = "rgb(" + Math.round(val[0]) + "," + Math.round(val[1]) + "," + Math.round(val[2]) + ")";
		
		element.attr({ //removes the old value
			fill : fillColor
		});
		
		element.attr({
			style :  element.attr("style") + ";fill: " + fillColor
		});
	}, 200);
	
	element.children().forEach(function(child) {
		if (child.type != "#text") {
			animateFillColor(colorFill, child);
		}
	});
}

function setStrokeColor(colorStroke, element) {
	element.attr({
		stroke : colorStroke
	});
	
	element.children().forEach(function(child) {
		if (child.type != "#text") {
			setStrokeColor(colorStroke, child);
		}
	});
}

function animateStrokeColor(colorStroke, element) {
	var currentColor = Snap.color(element.attr("stroke"))
	
	var fromStroke;
	if (currentColor.hex == "none") {
		fromStroke = Snap.color("black");
	}
	else {
		fromStroke = currentColor;
	}
	
	var toStroke = Snap.color(colorStroke)
	
	Snap.animate([fromStroke.r, fromStroke.g, fromStroke.b], [toStroke.r, toStroke.g, toStroke.b], function (val) {
		var strokeColor = "rgb(" + Math.round(val[0]) + "," + Math.round(val[1]) + "," + Math.round(val[2]) + ")";
		
		element.attr({ //removes the old value
			stroke : strokeColor
		});
		
		element.attr({
			style :  element.attr("style") + ";stroke: " + strokeColor
		});
	}, 200);
	
	element.children().forEach(function(child) {
		if (child.type != "#text") {
			animateStrokeColor(colorStroke, child);
		}
	});
}

function centerCamera(element) {
	var ebox = element.getBBox();
	
	var box = ebox;
	if (element.data("infoPanelsGroup")) {
		var ibox = element.data("infoPanelsGroup").getBBox();
		
		if (ibox.w != 0 || ibox.h != 0) {
			box.x = Math.min(box.x, ibox.x);
			box.y = Math.min(box.y, ibox.y);
			
			box.x2 = Math.max(box.x2, ibox.x2);
			box.y2 = Math.max(box.y2, ibox.y2);
			
			box.w = box.x2 - box.x;
			box.h = box.y2 - box.y;
			
			box.cx = box.x + box.w / 2;
			box.cy = box.y + box.h / 2;
		}
	}
	
	switch (config.cameraMovementStyle) {
		default :
			var finalMatrix = new Snap.Matrix();
			finalMatrix.translate(-box.cx + paper.getBoundingClientRect().width / 2,
								  -box.cy + paper.getBoundingClientRect().height / 2);
			
			var scaleX = 0.8 * paper.getBoundingClientRect().width / box.w;
			var scaleY = 0.8 * paper.getBoundingClientRect().height / box.h;
			var scale = Math.min(scaleX, scaleY);
			finalMatrix.scale(scale, scale, box.cx, box.cy);
			
			mapGroup.stop();
			mapGroup.animate({ transform: finalMatrix}, 700, mina.easeinout);
		break;
		case 'dezoom' :
			var finalMatrix = new Snap.Matrix();
			finalMatrix.translate(-box.cx + paper.getBoundingClientRect().width / 2,
								  -box.cy + paper.getBoundingClientRect().height / 2);
			
			var scaleX = 0.8 * paper.getBoundingClientRect().width / box.w;
			var scaleY = 0.8 * paper.getBoundingClientRect().height / box.h;
			var scale = Math.min(scaleX, scaleY);
			finalMatrix.scale(scale, scale, box.cx, box.cy);
			
			
			var current = mapGroup.transform().localMatrix;
			
			var half = new Snap.Matrix();
			half.scale(0.8, 0.8, paper.getBoundingClientRect().width / 2, paper.getBoundingClientRect().height / 2);
			half.add(0.5 * current.a + 0.5 * finalMatrix.a,
					 0.5 * current.b + 0.5 * finalMatrix.b,
					 0.5 * current.c + 0.5 * finalMatrix.c,
					 0.5 * current.d + 0.5 * finalMatrix.d,
					 0.5 * current.e + 0.5 * finalMatrix.e,
					 0.5 * current.f + 0.5 * finalMatrix.f);
			
			mapGroup.stop();
			mapGroup.animate({ transform: half}, 700, ease1, function() {
				mapGroup.animate({ transform: finalMatrix}, 700, ease2);
			});
		break;
		case 'fast' :
			var finalMatrix = new Snap.Matrix();
			finalMatrix.translate(-box.cx + paper.getBoundingClientRect().width / 2,
								  -box.cy + paper.getBoundingClientRect().height / 2);
			
			var scaleX = 0.7 * paper.getBoundingClientRect().width / box.w;
			var scaleY = 0.7 * paper.getBoundingClientRect().height / box.h;
			var scale = Math.min(scaleX, scaleY);
			finalMatrix.scale(scale, scale, box.cx, box.cy);
			
			mapGroup.stop();
			mapGroup.animate({ transform: finalMatrix}, 600, ease1);
		break;
	}
	
	
	
	/*var first = new Snap.Matrix();
	first.scale(0.8, 0.8, paper.getBoundingClientRect().width / 2, paper.getBoundingClientRect().height / 2);
	first.add(0.7 * current.a + 0.3 * finalMatrix.a,
		     0.7 * current.b + 0.3 * finalMatrix.b,
		     0.7 * current.c + 0.3 * finalMatrix.c,
		     0.7 * current.d + 0.3 * finalMatrix.d,
		     0.7 * current.e + 0.3 * finalMatrix.e,
		     0.7 * current.f + 0.3 * finalMatrix.f);
			 
	var second = new Snap.Matrix();
	second.scale(0.8, 0.8, paper.getBoundingClientRect().width / 2, paper.getBoundingClientRect().height / 2);
	second.add(0.3 * current.a + 0.7 * finalMatrix.a,
		     0.3 * current.b + 0.7 * finalMatrix.b,
		     0.3 * current.c + 0.7 * finalMatrix.c,
		     0.3 * current.d + 0.7 * finalMatrix.d,
		     0.3 * current.e + 0.7 * finalMatrix.e,
		     0.3 * current.f + 0.7 * finalMatrix.f);
	
	mapGroup.stop();
	mapGroup.animate({ transform: first}, 700, ease1, function() {
		mapGroup.animate({ transform: second}, 700, mina.linear, function() {
			mapGroup.animate({ transform: finalMatrix}, 700, ease2);
		});
	});*/
}

function centerMap() {
	var box = mapGroup.getBBox(true);
	var m = new Snap.Matrix();
	m.translate(-box.cx + paper.getBoundingClientRect().width / 2,
				-box.cy + paper.getBoundingClientRect().height / 2);
	
	var scaleX = 0.9 * paper.getBoundingClientRect().width / box.w;
	var scaleY = 0.9 * paper.getBoundingClientRect().height / box.h;
	var scale = Math.min(scaleX, scaleY);
	m.scale(scale, scale, box.cx, box.cy);
	mapGroup.transform(m);
}

function animatePath(element, effect) {
	//Inspired by
	//https://github.com/maxwellito/vivus
	
	if (element.type == "g") {
		element.children().forEach(function(child) {
			animatePath(element, effect);
		});
	}
	else if (element.type == "path") {
		var mask = element.clone();
		
		mask.attr({
			stroke: "#ffffff" //for whatever reason, the mask has to be white
		});

		if (element.attr("mask") != "none") {
			//remove the old mask to improve performance
			Snap.select(element.attr("mask").slice(5, -2)).remove();
		}
		
		element.attr({
			mask: mask
		});
		
		var length = Math.ceil(element.node.getTotalLength());
		var dashGap = 2;
		
		mask.node.style.strokeDasharray  = length + ' ' + (length + dashGap * 2);
		
		var anim = Snap.animate((length + dashGap) * (100 - effect.start) / 100, (length + dashGap) * (100 - effect.end) / 100, function (val) {		
			mask.node.style.strokeDashoffset = val;
		}, effect.time, mina.linear);
	}
}

function showElement(element) {
	element.node.style.display = "";
}


function hideElement(element) {
	element.node.style.display = "none";
}


function ease1(x) {
	return Math.sqrt(1 - (x-1) * (x-1));
}

function ease2(x) {
	return (1 - Math.sqrt(1 - (x * 0.9) * (x * 0.9))) / 0.564110;
}

//------------------------------------------------------------------------
// Change svg file
//------------------------------------------------------------------------

function loadLocalSvgFile(fileName) {
	initLoad();
	Snap.load(fileName, function (f) {
		loadSvgFragment(f, false);
	});
	
}


function loadCustomSvgFile() {
	initLoad();
	var file = document.getElementById("fileCustomSvgFile").files[0];
	if (file) {
		var reader = new FileReader();
		reader.readAsText(file, "UTF-8");
		reader.onload = function (evt) {
			loadSvgFragment(Snap.parse(evt.target.result), false);
		}
		reader.onerror = function (evt) {
			console.log("error reading file");
		}
	}
	else {
		hidePopUp();
	}
}

function loadImportedSvgFile(data) {
	initLoad();
	loadSvgFragment(Snap.parse(data.map), true);
}

function initLoad() {
	showPopUp('loading');
	
	if (selectedElement) {
		deselectElement(selectedElement);
	}
	
	if (superGroup) {
		superGroup.remove();
	}
	
	superGroup = s.g();
	superGroup.attr({
		id: "superGroup"
	});

	mapGroup = s.g();
	mapGroup.attr({
		id: "mapGroup"
	});
	mapGroup.drag();
	superGroup.append(mapGroup);

	legendGroup = s.g();
	legendGroup.attr({
		id: "legendGroup"
	});
	superGroup.append(legendGroup);

	timelineGroup = s.g();
	timelineGroup.attr({
		id: "timelineGroup"
	});
	superGroup.append(timelineGroup);
	
	tempGroup = s.g();
	tempGroup.attr({
		id: "tempGroup"
	});
	
	layerNames = [];
	layers = {};
	timelineLayers = [];
	selectedLayer = null;
	isTimelineActive = true;
	elementsInNullLayer = false;
	
	layerNullMap = s.g();
	layerNullMap.attr({
		id: "layerNullMap"
	});
	mapGroup.append(layerNullMap);
	
	layerNullLegend = s.g();
	layerNullLegend.attr({
		id: "layerNullLegend"
	});
	legendGroup.append(layerNullLegend);
	
	layerNames.push("layerNull");
	layers["layerNull"] = {
		map : layerNullMap,
		legend : layerNullLegend,
		timelineActivated : false
	};
	
	allModifiers = [];
	allElements = [];
	elementsData = {};
}

function loadSvgFragment(f, isImported) {
	tempGroup.append(f);
	
	if (!isImported) {
		initializeID(tempGroup);
	}
	
	svgFileContent = tempGroup.innerSVG();
	
	initializeElementsLayer(tempGroup, null);
	initializeLayers(tempGroup);
	tempGroup.remove();
	
	initializeElements(mapGroup);
	
	isTimelineActive = false;
	if (!isImported && ((elementsInNullLayer && layerNames.length > 1) || (!elementsInNullLayer && layerNames.length > 2))) {
		initNewTimelinePopUp();
	}
	else {
		timelineContent = null;
		centerMap();
	
		hidePopUp();
	}
	/*if (!isImported && isTimelineActive && layerNames.length > 2) { //at least layerNull (which is empty) + 2 others
		createDefaultTimeline();
	}*/
}

function initializeID(element) {
	if (isElementFinal(element)) {
		element.data("id", "elem" + (id++));
		element.attr({
			id: "" + element.data("id")
		});
		
	}
	else {
		element.children().forEach(function(child) {
			initializeID(child);
		});
	}
}

function initializeElementsLayer(element, layer) {
	if (isElementFinal(element)) {
		if (layer == null) {
			element.data("layer", "layerNull");
		}
		else {
			element.data("layer", layer);
		}
	}
	else if (isLayer(element)) {
		element.children().forEach(function(child) {
			initializeElementsLayer(child, element.attr("id"));
		});
	}
	else {
		element.children().forEach(function(child) {
			initializeElementsLayer(child, null);
		});
	}
}

function initializeLayers(element) {
	if (isElementFinal(element)) {
		layerNullMap.append(element);
		
		if (isElementReal(element)) {
			isTimelineActive = false;
			elementsInNullLayer = true;
		}
	}
	else if (isLayer(element)) {
		var layerLegend = s.g();
		layerLegend.attr({
			id: element.attr("id") + "Legend"
		});
		legendGroup.append(layerLegend);
		
		layerNames.push(element.attr("id"));
		layers[element.attr("id")] = {
			map : element,
			legend : layerLegend,
			timelineActivated : false
		};
		
		element.attr({
			id: "" + element.attr("id") + "Map"
		});
		mapGroup.append(element);
		
		
	}
	else {
		element.children().forEach(function(child) {
			initializeLayers(child);
		});
	}
}

function initializeElements(element) {
	if (isElementFinal(element)) {
		
		if (element.attr("id")) {
			var elementParent = s.g();
			elementParent.attr({
				id: element.attr("id") + "Parent"
			});
			element.parent().append(elementParent);
			elementParent.append(element);
			
			element.data("parent", elementParent);
			element.data("isSelected", false);
			element.data("isHovered", false);
			element.data("modifiers", []);
			element.data("isLegend", false);
			element.data("firstCheck", true);
			
			elementsData[element.attr("id")] = {
				firstSelection : true
			}
			
			element.click(clickCallback);
			element.mouseover(hoverCallback);
			element.mouseout(outCallback);
			
			allElements.push(element);
		}
	}
	else {
		element.children().forEach(function(child) {
			initializeElements(child);
		});
	}
}

//------------------------------------------------------------------------
//Timeline & Layers
//------------------------------------------------------------------------

function initNewTimelinePopUp() {
	dTimelineCheckBoxes.innerHTML = "";
	
	for (var i = 0; i < layerNames.length; i++) {
		if (i != 0) {
			dTimelineCheckBoxes.innerHTML += "<p>" + layerNames[i] + "<input type='checkbox' id='checkbox" + layerNames[i] + "' checked></p>";
		}
		else if (elementsInNullLayer) {
			dTimelineCheckBoxes.innerHTML += "<p>All elements with no layer<input type='checkbox' id='checkbox" + layerNames[i] + "'></p>";
		}
	}

	showPopUp('newTimeline');
}

function validateNewTimeline() {
	var results = {};
	layerCountInTimeline = 0;
	 
	results[layerNames[0]] = false;
	 
	for (var i = 0; i < layerNames.length; i++) {
		if (i != 0 || elementsInNullLayer) {
			results[layerNames[i]] = document.getElementById("checkbox" + layerNames[i]).checked;
			
			if (results[layerNames[i]]) {
				layerCountInTimeline ++;
			}
		}
	}
	
	if (layerCountInTimeline >= 2) {
		isTimelineActive = true;
		timelineLayers = [];
		
		for (var i = 0; i < layerNames.length; i++) {
			layers[layerNames[i]].timelineActivated = results[layerNames[i]];
			if (layers[layerNames[i]].timelineActivated) {
				timelineLayers.push(layerNames[i]);
			}
		}
		createDefaultTimeline();
	}
	else {
		//do nothing, everything is set correctly
	}
	
	hidePopUp();
}

function selectLayer(layer, isFirstSelection) {
	if (!isFirstSelection && selectedLayer == layer) {
		return;
	}
	
	if (selectedElement) {
		deselectElement(selectedElement);
	}
	
	var oldSelectedLayer = selectedLayer;
	selectedLayer = layer;
	
	if (!layers[selectedLayer].timelineActivated) {
		console.log("ERROR : selectedLayer is not in timeline");
	}
	
	if (isFirstSelection) {
		for (var i = 0; i < layerNames.length; i++) {
			if (layers[layerNames[i]].timelineActivated) {
				layers[layerNames[i]].map.node.style.display = "none";
				layers[layerNames[i]].legend.node.style.display = "none";
				
				layers[layerNames[i]].map.attr({opacity: 0});
				layers[layerNames[i]].legend.attr({opacity: 0});
				
				layers[layerNames[i]].map.node.style.pointerEvents = "none";
				layers[layerNames[i]].legend.node.style.pointerEvents = "none";
			}
		}		
		layers[selectedLayer].map.attr({opacity: 1});
		layers[selectedLayer].legend.attr({opacity: 1});
	}
	else {
		if (!layers[oldSelectedLayer].timelineActivated) {
			console.log("ERROR : oldSelectedLayer is not in timeline");
		}
	
		layers[oldSelectedLayer].map.node.style.pointerEvents = "none";
		layers[oldSelectedLayer].legend.node.style.pointerEvents = "none";
		
		layers[oldSelectedLayer].map.animate({
			opacity: 0
		}, 1000);
		layers[oldSelectedLayer].legend.animate({
			opacity: 0
		}, 1000);
		
		layers[selectedLayer].map.animate({
			opacity: 1
		}, 1000);
		layers[selectedLayer].legend.animate({
			opacity: 1
		}, 1000);
	}
	layers[selectedLayer].map.node.style.display = "";
	layers[selectedLayer].legend.node.style.display = "";
		
	layers[selectedLayer].map.node.style.pointerEvents = "";
	layers[selectedLayer].legend.node.style.pointerEvents = "";
	
	checkAllModifiers();
}

function initTimeline() {
	timelineGroup.clear();
	timelineGroup.transform(new Snap.Matrix());
}

function finishTimeline() {
	timelineContent = timelineGroup.innerSVG();
	
	if (timelineLayers.length != layerCountInTimeline) {
		console.log("ERROR : timelineLayers.length != layerCountInTimeline");
	}
	
	var possibleTimelineSelectors = collectTimelineSelectors();
	
	if (possibleTimelineSelectors.length < layerCountInTimeline) {
		createDefaultTimeline();
		alert("There isn't enough elements on this timeline to function correctly");
	}
	else {
		var count = 0;
		for (var i = (possibleTimelineSelectors.length - layerCountInTimeline); i < possibleTimelineSelectors.length; i++) {
			var element = possibleTimelineSelectors[i];
			
			var layerName = timelineLayers[count];
			
			layers[layerName].selector = element;
			element.data("timelineLayerName", layerName);
			
			element.attr({
				opacity:0.6
			});
			
			if (count == 0) {
				element.attr({
					opacity:1
				});
			}
			
			element.click(timelineClickCallbackOpacity);
			element.mouseover(timelineHoverCallbackOpacity);
			element.mouseout(timelineOutCallbackOpacity);
			
			count++;
		}
	}
	
	selectLayer(timelineLayers[0], true);
	centerMap();
	hidePopUp();
}

function createDefaultTimeline() {
	initTimeline();
	
	var w = paper.getBoundingClientRect().width;
	var h = paper.getBoundingClientRect().height;
	
	var rect = s.rect(w * (1/8), h * (28/32), w * (6/8), h * (1/32));
	rect.attr({
		fill:"#606060"
	});
	timelineGroup.add(rect);
	
	for (var i = 0; i < layerCountInTimeline; i++) {
		var path = s.path("m 0,0 31.33407146,0 6.330439,35.637144 0,35.637144 0,35.637142 -6.330439,35.63715 -31.33407146,0 -6.33043814,-35.63715 0,-35.637142 0,-35.637144 z");
		var bbox = path.getBBox();
		
		var m = new Snap.Matrix();
		m.translate(w * (2/8) + i * w * (4/8) / (layerCountInTimeline - 1) - bbox.w / 2, h * (28/32) + h * (1/32) / 2 - bbox.h / 2);
		path.transform(m);
		path.attr({
			fill:"#707070"
		});
		
		timelineGroup.add(path);
	}
	finishTimeline();
}

function loadCustomTimeline() {
	var file = document.getElementById("fileCustomTimeline").files[0];
	if (file) {
		var reader = new FileReader();
		reader.readAsText(file, "UTF-8");
		reader.onload = function (evt) {
			loadFragmentTimeline(Snap.parse(evt.target.result));
		}
		reader.onerror = function (evt) {
			console.log("error reading file");
		}
	}
	else {
		hidePopUp();
	}
}

function loadImportedTimeline(data) {
	isTimelineActive = (data.timeline != null);
	if (isTimelineActive) {
		timelineLayers = data.timelineLayers;
		layerCountInTimeline = timelineLayers.length;
		
		for (var i = 0; i < layerNames; i++) {
			layers[layerNames[i]].timelineActivated = false;
		}
		for (var i = 0; i < layerCountInTimeline; i++) {
			layers[timelineLayers[i]].timelineActivated = true;
		}
		
		loadFragmentTimeline(Snap.parse(data.timeline));
	}
}

function loadFragmentTimeline(f) {
	initTimeline();
	
	timelineGroup.add(f);
	
	var w = paper.getBoundingClientRect().width;
	var h = paper.getBoundingClientRect().height;
	
	var bbox = timelineGroup.getBBox();
	
	var m = new Snap.Matrix();
	m.translate(-bbox.cx + w / 2,
				-bbox.cy + h * (19/20));
	timelineGroup.transform(m);
	
	finishTimeline();
}

function collectTimelineSelectors() {
	var queue = [];
	queue.push(timelineGroup);
	
	var possibleTimelineSelectors = [];
	
	while (queue.length > 0) {
		var element = queue.shift();
		
		if (isElementFinal(element) && isElementReal(element)) {
			possibleTimelineSelectors.push(element);
		}
		else {
			for (var i = 0; i < element.children().length; i++) {
				queue.push(element.children()[i]);
			}
		}
	}
	
	return possibleTimelineSelectors;
}

function tryShowPopUpTimeline() {
	if (isTimelineActive) {
		showPopUp("timeline");
	}
	else {
		alert("The current svg file doesn't fit the requirements to have a timeline");
	}
}

function timelineClickCallbackOpacity() {
	layers[selectedLayer].selector.animate({
		opacity:0.6
	}, 200);
		
	selectLayer(this.data("timelineLayerName"), false);
	
	this.animate({
		opacity:1
	}, 200);
}

function timelineHoverCallbackOpacity() {
	this.animate({
		opacity:0.8
	}, 200);
}

function timelineOutCallbackOpacity() {
	if (selectedLayer == this.data("timelineLayerName")) {
		this.animate({
			opacity:1
		}, 200);
	}
	else {
		this.animate({
			opacity:0.6
		}, 200);
	}
}

//------------------------------------------------------------------------
//Config
//------------------------------------------------------------------------

function setConfigUI() {
	document.getElementById("cCameraMovement").checked = config.cameraMovementEnabled;
	document.getElementById("ddCameraMovement").disabled = !config.cameraMovementEnabled;
	document.getElementById("ddCameraMovement").value = config.cameraMovementStyle;
}

//------------------------------------------------------------------------
//Info panels
//------------------------------------------------------------------------

function loadLocalInfoPanel(name) {
	infoPanelNames.push(name);
	Snap.load("infoPanels/" + name + ".svg", function (f) {
		var tempGroup = s.g();
		tempGroup.add(f);
		infoPanels[name] = {
			svg : tempGroup.innerSVG().toString(),
			thumbnail : "infoPanels/thumbnails/" + name + ".png"
		};
		tempGroup.remove();
	});
}

function loadCustomInfoPanel() {
	var file = document.getElementById("fileCustomInfoPanel").files[0];
	if (file) {
		var reader = new FileReader();
		reader.readAsText(file, "UTF-8");
		reader.onload = function (evt) {
			var name = "custom" + (++customId);
			
			var f = Snap.parse(evt.target.result);
			var groupInfoPanel = s.g();
			groupInfoPanel.add(f);
			
			var allRects = groupInfoPanel.selectAll("rect");
			var toBeReplaced = null;
			
			for (var i = 0; i < allRects.length; i++) {
				if (Snap.color(allRects[i].attr("fill")).hex == "#c0ffee") {
					toBeReplaced = allRects[i];
					break;
				}
			}
			
			if (!toBeReplaced) {
				groupInfoPanel.remove();
				alert("The svg file is not valid for a info panel");
				return;
			}
			
			var x = toBeReplaced.attr("x");
			var y = toBeReplaced.attr("y");
			var width = toBeReplaced.attr("width");
			var height = toBeReplaced.attr("height");
			var parent = toBeReplaced.parent();
			
			toBeReplaced.remove();
			
			parent.add(Snap.parse('<foreignObject x="' + x + '" y="' + y + '" width="' + width + '" height="' + height + '" class="foreignInfoPanel"></foreignObject>'));
			
			infoPanelNames.push(name);
			infoPanels[name] = {
				svg : groupInfoPanel.innerSVG().toString(),
				thumbnail : "infoPanels/thumbnails/custom.png"
			};
			
			groupInfoPanel.remove();
	
			if (selectedElement) {
				reconstructModifiersUI(selectedElement);
			}
			
			hidePopUp();
		}
		reader.onerror = function (evt) {
			console.log("error reading file");
		}
	}
	else {
		hidePopUp();
	}
}

function activateInfoPanel(element, effect) {
	var f = Snap.parse(infoPanels[effect.style].svg);
	f.node.getElementsByClassName("foreignInfoPanel")[0].innerHTML = '<div xmlns="http://www.w3.org/1999/xhtml" class="infoPanel">' + effect.text + '</div>';
	
	var infoPanel = s.g();
	infoPanel.append(f);
	
	infoPanel.attr({
		id : "infoPanel" + (id++)
	});
	
	if (!element.data("infoPanelsGroup")) {
		var infoPanelsGroup = s.g();
		
		infoPanelsGroup.attr({
			id : "infoPanelsGroup" + element.attr("id")
		});
		
		if (element.data("isLegend")) {
			layers[element.data("layer")].legend.append(infoPanelsGroup);
		}
		else {
			layers[element.data("layer")].map.append(infoPanelsGroup);
		}
		element.data("infoPanelsGroup", infoPanelsGroup);
	}
	
	var m = new Snap.Matrix();
	
	//as we're putting the info panel in a layer, we need to erase the transform on this layer to avoid applying them twice
	m.add(element.data("infoPanelsGroup").transform().globalMatrix.invert());
	m.add(element.transform().globalMatrix);
	var bbox = element.getBBox(true);
	var infoBbox = infoPanel.getBBox();
	switch (effect.position) {
		default : //right
			m.translate((bbox.x2 - infoBbox.x), 
						 bbox.cy - infoBbox.cy);
		break;
		case 'top' :
			m.translate((bbox.cx - infoBbox.cx), 
						 bbox.y - infoBbox.y2);
		break;
		case 'left' :
			m.translate((bbox.x - infoBbox.x2), 
						 bbox.cy - infoBbox.cy);
		break;
		case 'bottom' :
			m.translate((bbox.cx - infoBbox.cx), 
						 bbox.y2 - infoBbox.y);
		break;
	}
	m.translate(effect.horizontalOffset, effect.verticalOffset);
	m.scale(effect.horizontalScale, effect.verticalScale);
	
	infoPanel.transform(m);
	
	var textElement = infoPanel.select(".foreignInfoPanel");
	m = new Snap.Matrix();
	m.scale(1/effect.horizontalScale, 1/effect.verticalScale);
	textElement.transform(m);
	
	element.data("infoPanelsGroup").append(infoPanel);
	effect.infoPanel = infoPanel.attr("id");
}

//------------------------------------------------------------------------
//Legend
//------------------------------------------------------------------------

function activateLegend(element, effect) {
	if (element.data("isLegend")) {
		return;
	}
	
	element.data("legendUpdate", true);
	element.data("isLegend", true);
	
	if (element.data("infoPanelsGroup")) {
		layers[element.data("layer")].legend.append(element.data("infoPanelsGroup"));
	}
	
	var legendGroup = s.g();
	legendGroup.attr({
		id : "legendGroup" + element.attr("id")
	});
	layers[element.data("layer")].legend.append(legendGroup);
	legendGroup.append(element);
	
	//First, scale the element to get its new bbox
	var m = new Snap.Matrix();
	m.scale(effect.scale);
	legendGroup.transform(m);
	var bbox = element.getBBox();
	
	//the bbox of the group is not used, as it is not well computed with some paths
	
	var localMatrix = legendGroup.transform().localMatrix;
	var x = localMatrix.x(bbox.x, bbox.y);
	var y = localMatrix.y(bbox.x, bbox.y);
	var x2 = localMatrix.x(bbox.x2, bbox.y2);
	var y2 = localMatrix.y(bbox.x2, bbox.y2);
	
	//Then, translate it (independantly of the scale)
	m = new Snap.Matrix();
	switch (effect.position) {
		default : //bottom right
			m.translate(-x2 + paper.getBoundingClientRect().width, 
						-y2 + paper.getBoundingClientRect().height);
		break;
		case 'bottom-left' :
			m.translate(-x, 
						-y2 + paper.getBoundingClientRect().height);
		break;
		case 'top-right' :
			m.translate(-x2 + paper.getBoundingClientRect().width, 
						-y);
		break;
		case 'top-left' :
			m.translate(-x, 
						-y);
		break;
	}
	m.scale(effect.scale); //and scale it again (as the previous scale will be erased)
	legendGroup.transform(m)
	
	element.data("legendGroup", legendGroup);
	
	if (element.data("isSelected")) {
		reconstructSelector(element);
	}
}

function deactivateLegend(element, effect) {
	if (!element.data("isLegend")) {
		return;
	}
	
	element.data("legendUpdate", true);
	element.data("isLegend", false);
	
	if (element.data("infoPanelsGroup")) {
		layers[element.data("layer")].map.append(element.data("infoPanelsGroup"));
	}
	
	element.data("parent").append(element);
	
	if (element.data("legendGroup")) {
		element.data("legendGroup").remove();
	}
	
	if (element.data("isSelected")) {
		reconstructSelector(element);
	}
}

//------------------------------------------------------------------------
//Callbacks & UI
//------------------------------------------------------------------------

function clickCallback(event) {
	var element = this;
	
	if (element.data("isSelected")) {
		deselectElement(element);
	}
	else {
		selectElement(element);
	}
}

function hoverCallback(event) {
	var element = this;
	
	element.data("isHovered", true);
	checkModifiers(element);
}

function outCallback(event) {
	var element = this;
	
	element.data("isHovered", false);
	checkModifiers(element);
}

function updateConditionLayer() {
	var value = this.options[this.selectedIndex].value;

	this.condition.layer = value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateEffectColor(colorPicker) {
	colorPicker.valueElement.effect.color = "#" + colorPicker;
	colorPicker.valueElement.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateInfoPanelText() {
	this.effect.text = this.value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateInfoPanelPosition() {
	var value = this.options[this.selectedIndex].value;

	this.effect.position = value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateInfoPanelVerticalOffset() {
	this.effect.verticalOffset = this.value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateInfoPanelHorizontalOffset() {
	this.effect.horizontalOffset = this.value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateInfoPanelVerticalScale() {
	this.effect.verticalScale = this.value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateInfoPanelHorizontalScale() {
	this.effect.horizontalScale = this.value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateInfoPanelStyle(select, pickerOption, event) {
	var value = this[0].options[this[0].selectedIndex].value;
	
	this[0].effect.style = value;
	this[0].modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateLegendPosition() {
	var value = this.options[this.selectedIndex].value;

	this.effect.position = value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updateLegendScale() {
	this.effect.scale = this.value;
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updatePathAnimationTime() {
	this.effect.time = Number(this.value);
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updatePathAnimationStart() {
	this.effect.start = Number(this.value);
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function updatePathAnimationEnd() {
	this.effect.end = Number(this.value);
	this.modifier.modified = true;
	checkModifiers(selectedElement);
}

function selectElement(element) {
	if (selectedElement != null) {
		deselectElement(selectedElement);
	}
		
	selectedElement = element;
	element.data("isSelected", true);
	
	dNoElementSelected.style.display = 'none';
	dElementSelected.style.display = 'block';
	
	if (elementsData[element.attr("id")].firstSelection) {
		elementsData[element.attr("id")].firstSelection = false;
		addDefaultModifiers(element);
	}
	
	reconstructModifiersUI(element);
	checkModifiers(element);
	reconstructSelector(element);
	
	if (!element.data("isLegend") && config.cameraMovementEnabled) {
		centerCamera(element);
	}
}

function reconstructSelector(element) {
	if (element.data("selector")) {
		element.data("selector").remove();
	}
	var bbox = element.getBBox();
	var selector = s.rect(bbox.x, bbox.y, bbox.w, bbox.h);
	
	selector.attr({
		stroke : "red", 
		style : "stroke-width:" + paper.getBoundingClientRect().width / 100 + ";stroke-opacity:0.7;fill-opacity:0;pointer-events:none;"
	});
	
	if (element.data("isLegend")) {
		element.data("legendGroup").append(selector);
	}
	else if (isTimelineActive) {
		layers[element.data("layer")].map.append(selector);
	}
	else {
		mapGroup.append(selector);
	}
	
	element.data("selector", selector);
}

function deselectElement(element) {
	selectedElement = null;
	element.data("isSelected", false);
	
	dNoElementSelected.style.display = 'block';
	dElementSelected.style.display = 'none';
	
	if (element.data("selector")) {
		element.data("selector").remove();
		element.data("selector", null);
	}
	
	checkModifiers(element);
}

//------------------------------------------------------------------------
//Modifiers
//------------------------------------------------------------------------

function addEmptyModifier(element) {
	var modifier = {
		conditions : [],
		effects : [],
		name : "Modifier" + (element.data("modifiers").length + 1),
		activated : false,
		modified : false,
		element : element.attr("id")
	};
	
	allModifiers.push(modifier);
	element.data("modifiers").push(modifier);
	
	return modifier;
}

function removeModifier(element, modifier) {
	element.data("legendUpdate", true); //the modifier may contain a legend effect
	
	deactivateModifier(element, modifier);
	
	var array = element.data("modifiers");
	array.splice(array.indexOf(modifier), 1);
	allModifiers.splice(allModifiers.indexOf(modifier), 1);
	
	checkModifiers(element);
	reconstructModifiersUI(element);
}

function addDefaultModifiers(element) {
	var defaultModifier = addEmptyModifier(element);
	var selectionModifier = addEmptyModifier(element);
	var hoverModifier = addEmptyModifier(element);
	
	defaultModifier.name = "Default Modifier";
	selectionModifier.name = "Select Modifier";
	hoverModifier.name = "Hover Modifier";
	
	defaultModifier.conditions.push({
		type : 'default'
	});
	
	selectionModifier.conditions.push({
		type : 'selection'
	});
	
	hoverModifier.conditions.push({
		type : 'hover'
	});
	
	//console.log(element.attr("fill"))
	
	var fill;
	if (element.node.getAttribute("fill") || (element.node.getAttribute("style") && element.node.getAttribute("style").includes("fill"))) {
		fill = Snap.color(element.attr("fill")).hex;
	}
	else {
		fill = "none";
	}
	
	if (fill != 'none') {
		defaultModifier.effects.push({
			type : 'fill',
			color : fill
		});
		
		selectionModifier.effects.push({
			type : 'fill',
			color : fill
		});
		
		/*selectionModifier.effects.push({
			type : 'infoPanel',
			text : "Kapoue",
			position : "right",
			verticalOffset : 0,
			horizontalOffset : 0,
			verticalScale : 1,
			horizontalScale : 1,
			style : 'classic'
		});*/
		
		hoverModifier.effects.push({
			type : 'fill',
			color : fill
		});
	}
	
	if (element.attr("stroke") && element.attr("stroke") != 'none') {
		defaultModifier.effects.push({
			type : 'stroke',
			color : Snap.color(element.attr("stroke")).hex
		});
		
		selectionModifier.effects.push({
			type : 'stroke',
			color : Snap.color(element.attr("stroke")).hex
		});
		
		hoverModifier.effects.push({
			type : 'stroke',
			color : Snap.color(element.attr("stroke")).hex
		});
	}
}

function addCondition() {
	var value = ddConditionChoice.options[ddConditionChoice.selectedIndex].value;
	
	var condition = {
		type : value
	};
	
	switch(condition.type) {
		case 'layerSelection':
			if (isTimelineActive) {
				condition.layer = timelineLayers[0];
			}
			else {
				alert("The timeline is not active");
			}
		break;
		default:
			//nothing
		break;
	}
	
	currentModifier.modified = true;
	currentModifier.conditions.push(condition);
	
	checkModifiers(selectedElement);
	reconstructModifiersUI(selectedElement);
	
	hidePopUp();
}

function addEffect() {
	var value = ddEffectChoice.options[ddEffectChoice.selectedIndex].value;
	
	var effect = {
		type : value
	};
	
	switch(effect.type) {
		case 'fill':
			effect.color = "#FFFFFF";
		break;
		case 'stroke':
			effect.color = "#000000";
		break;
		case 'infoPanel':
			effect.text = "";
			effect.position = "right";
			effect.verticalOffset = 0;
			effect.horizontalOffset = 0;
			effect.verticalScale = 1;
			effect.horizontalScale = 1;
			effect.style = 'classic';
		break;
		case 'legend':
			effect.position = "bottom-right";
			effect.scale = 1;
		break;
		case 'pathAnimation':
			effect.time = 1000;
			effect.start = 0;
			effect.end = 100;
		break;
		default:
			//nothing
		break;
	}
	
	currentModifier.modified = true;
	currentModifier.effects.push(effect);
	
	checkModifiers(selectedElement);
	reconstructModifiersUI(selectedElement);
	
	hidePopUp();
}

function checkAllModifiers() {
	for (var i = 0; i < allElements.length; i++) {
		checkModifiers(allElements[i]);
	}
}

function checkModifiers(element) {
	var modifiers = element.data("modifiers");
	for (var i = 0; i < modifiers.length; i++) {
		checkModifier(element, modifiers[i]);
	}
	
	if (element.data("legendUpdate")) {
		element.data("legendUpdate", false);
		
		for (var i = 0; i < modifiers.length; i++) {
			legendCheckModifier(element, modifiers[i]);
		}
	}
	element.data("firstCheck", false);
}

function checkModifier(element, modifier) {
	if (modifier.activated && modifier.modified) {
		deactivateModifier(element, modifier);
	}
	modifier.modified = false;
	
	var should = shouldBeActive(element, modifier);
	
	if (should) {
		setModifier(element, modifier);
	}
	
	if (should && !modifier.activated) {
		activateModifier(element, modifier);
	}
	else if (!should && modifier.activated) {
		deactivateModifier(element, modifier);
	}
}

function legendCheckModifier(element, modifier) {
	if (!modifier.activated) {
		return;
	}
	
	for (var i = 0; i < modifier.effects.length; i++) {
		var effect = modifier.effects[i];
		
		if (effect.type == "infoPanel") {
			deactivateEffect(element, effect);
			activateEffect(element, effect);
		}
	}
}

function shouldBeActive(element, modifier) {
	for (var i = 0; i < modifier.conditions.length; i++) {
		var condition = modifier.conditions[i]; 
		
		if (!isConditionSatisfied(element, condition)) {
			return false;
		}
	}

	return true;
}

function isConditionSatisfied(element, condition) {
	switch(condition.type) {
		case 'default':
			return !element.data("isSelected") && !element.data("isHovered")
		break;
		case 'selection':
			return element.data("isSelected")
		break;
		case 'hover':
			return element.data("isHovered")
		break;
		case 'layerSelection':
			return selectedLayer == condition.layer;
		break;
		default:
			console.log("UNKNOWN CONDITION TYPE : " + condition.type)
		break;
	}
}

function setModifier(element, modifier) {
	for (var i = 0; i < modifier.effects.length; i++) {
		var effect = modifier.effects[i]; 
		
		setEffect(element, effect);
	}
}

function setEffect(element, effect) {
	switch(effect.type) {
		case 'fill':
			if (element.data("firstCheck")) {
				setFillColor(effect.color, element);
			}
			else {
				animateFillColor(effect.color, element);
			}
		break;
		case 'stroke':
			if (element.data("firstCheck")) {
				setStrokeColor(effect.color, element);
			}
			else {
				animateStrokeColor(effect.color, element);
			}
		break;
		default:
			//this effect is only used during transitions
		break;
	}
}

function activateModifier(element, modifier) {
	modifier.activated = true;
	
	for (var i = 0; i < modifier.effects.length; i++) {
		var effect = modifier.effects[i]; 
		
		activateEffect(element, effect);
	}
}

function activateEffect(element, effect) {
	switch(effect.type) {
		case 'infoPanel':
			activateInfoPanel(element, effect);
		break;
		case 'legend':
			activateLegend(element, effect);
		break;
		case 'pathAnimation':
			animatePath(element, effect);
		break;
		case 'centerCamera':
			centerCamera(element);
		break;
		default:
			//this effect has already been set
		break;
	}
}

function deactivateModifier(element, modifier) {
	modifier.activated = false;
	
	for (var i = 0; i < modifier.effects.length; i++) {
		var effect = modifier.effects[i]; 
		
		deactivateEffect(element, effect);
	}
}

function deactivateEffect(element, effect) {
	switch(effect.type) {
		case 'infoPanel':
			var oldInfoPanel = superGroup.select("#" + effect.infoPanel);
			if (oldInfoPanel) {
				oldInfoPanel.remove();
				effect.infoPanel = null;
			}
		break;
		case 'legend':
			deactivateLegend(element, effect);
		break;
		case 'pathAnimation':
			//nothing
		break;
		default:
			//this effect has already been set and does not need deactivation
		break;
	}
}

//------------------------------------------------------------------------
//Modifiers UI
//------------------------------------------------------------------------

function reconstructModifiersUI(element) {
	dModifiersContainer.innerHTML = "";
	
	var modifiers = element.data("modifiers");
	
	if (modifiers.length == 0) {
		dModifiersContainer.appendChild(pNoModifiers);
		return;
	}
	
	for (var i = 0; i < modifiers.length; i++) {
		var modifier = modifiers[i];
		
		var cloneModifier = templateModifier.cloneNode(true);
		cloneModifier.getElementsByClassName("modifierTitle")[0].innerHTML = modifier.name;
		
		//Remove button
		var removeButton = cloneModifier.getElementsByClassName("redButton")[0];
		removeButton.addEventListener('click', function(e) {
			removeModifier(element, this.modifier);
		}, false);
		removeButton.modifier = modifier;
		
		//Adds the elements for each condition
		var conditions = cloneModifier.getElementsByClassName("conditions")[0];
		conditions.innerHTML = "";
		if (modifier.conditions.length == 0) {
			conditions.appendChild(templateConditions['none'].cloneNode(true));
		}
		else {
			for (var j = 0; j < modifier.conditions.length; j++) {
				conditions.appendChild(getConditionUI(modifier, modifier.conditions[j]));
			}
		}
		
		//Add condition button
		var addConditionButton = cloneModifier.getElementsByClassName("greenButton")[0];
		addConditionButton.addEventListener('click', function(e) {
			currentModifier = this.modifier;
			showPopUp('addCondition');
		}, false);
		addConditionButton.modifier = modifier;
		
		//Adds the elements for each effect
		var effects = cloneModifier.getElementsByClassName("effects")[0];
		effects.innerHTML = "";
		if (modifier.effects.length == 0) {
			effects.appendChild(templateEffects['none'].cloneNode(true));
		}
		else {
			for (var j = 0; j < modifier.effects.length; j++) {
				effects.appendChild(getEffectUI(modifier, modifier.effects[j]));
			}
		}
		
		//Add effect button
		var addEffectButton = cloneModifier.getElementsByClassName("greenButton")[1];
		addEffectButton.addEventListener('click', function(e) {
			currentModifier = this.modifier;
			showPopUp('addEffect');
		}, false);
		addEffectButton.modifier = modifier;
	
		dModifiersContainer.appendChild(cloneModifier);
	}
	
	jscolor.installByClassName("jscolor"); //'reboots' jscolor
}

function getConditionUI(modifier, condition) {
	var clone = templateConditions[condition.type].cloneNode(true);
	
	switch(condition.type) {
		case 'layerSelection':
			var select = clone.getElementsByTagName("select")[0];
			
			select.innerHTML = "";
			for (var i = 0; i < timelineLayers.length; i++) {
				select.innerHTML += "<option value='" + timelineLayers[i] + "'>" + timelineLayers[i] + "</option>";
			}
			
			select.value = condition.layer;
			select.modifier = modifier;
			select.condition = condition;
			
			select.addEventListener('change', updateConditionLayer, false);
			
			return clone;
		break;
		default:
			return clone;
		break;
	}
}

function getEffectUI(modifier, effect) {
	var clone = templateEffects[effect.type].cloneNode(true);
	
	switch(effect.type) {
		case 'fill':
			var colorPicker = clone.getElementsByClassName("colorPicker")[0];
			
			colorPicker.value = effect.color;
			colorPicker.modifier = modifier;
			colorPicker.effect = effect;
			
			return clone;
		break;
		case 'stroke':
			var colorPicker = clone.getElementsByClassName("colorPicker")[0];
			
			colorPicker.value = effect.color;
			colorPicker.modifier = modifier;
			colorPicker.effect = effect;
			
			return clone;
		break;
		case 'infoPanel':
			var textArea = clone.getElementsByTagName("textarea")[0];
			var selectPosition = clone.getElementsByTagName("select")[0];
			var verticalOffset = clone.getElementsByTagName("input")[1];
			var horizontalOffset = clone.getElementsByTagName("input")[0];
			var verticalScale = clone.getElementsByTagName("input")[3];
			var horizontalScale= clone.getElementsByTagName("input")[2];
			var selectStyle = clone.getElementsByTagName("select")[1];
			
			for (var i = 0; i < infoPanelNames.length; i++) {
				selectStyle.innerHTML += "<option data-img-src='" + infoPanels[infoPanelNames[i]].thumbnail + "' value='" + infoPanelNames[i] + "'>";
			}
			selectStyle.value = effect.style;
			$(selectStyle).imagepicker({
				selected : updateInfoPanelStyle
			});
			
			textArea.value = effect.text;
			textArea.modifier = modifier;
			textArea.effect = effect;
			
			selectPosition.value = effect.position;
			selectPosition.modifier = modifier;
			selectPosition.effect = effect;
			
			verticalOffset.value = effect.verticalOffset;
			verticalOffset.modifier = modifier;
			verticalOffset.effect = effect;
			
			horizontalOffset.value = effect.horizontalOffset;
			horizontalOffset.modifier = modifier;
			horizontalOffset.effect = effect;
			
			verticalScale.value = effect.verticalScale;
			verticalScale.modifier = modifier;
			verticalScale.effect = effect;
			
			horizontalScale.value = effect.horizontalScale;
			horizontalScale.modifier = modifier;
			horizontalScale.effect = effect;
			
			selectStyle.modifier = modifier;
			selectStyle.effect = effect;
			
			textArea.addEventListener('input', updateInfoPanelText, false);
			selectPosition.addEventListener('change', updateInfoPanelPosition, false);
			verticalOffset.addEventListener('input', updateInfoPanelVerticalOffset, false);
			horizontalOffset.addEventListener('input', updateInfoPanelHorizontalOffset, false);
			verticalScale.addEventListener('input', updateInfoPanelVerticalScale, false);
			horizontalScale.addEventListener('input', updateInfoPanelHorizontalScale, false);
			
			return clone;
		break;
		case 'legend':
			var selectPosition = clone.getElementsByTagName("select")[0];
			var scaleInput = clone.getElementsByTagName("input")[0];
			
			selectPosition.value = effect.position;
			selectPosition.modifier = modifier;
			selectPosition.effect = effect;
			
			scaleInput.value = effect.scale;
			scaleInput.modifier = modifier;
			scaleInput.effect = effect;
			
			selectPosition.addEventListener('change', updateLegendPosition, false);
			scaleInput.addEventListener('input', updateLegendScale, false);			
			return clone;
		break;
		case 'pathAnimation':
			var timeInput = clone.getElementsByTagName("input")[0];
			var startInput = clone.getElementsByTagName("input")[1];
			var endInput = clone.getElementsByTagName("input")[2];
			
			timeInput.value = effect.time;
			timeInput.modifier = modifier;
			timeInput.effect = effect;
			
			startInput.value = effect.start;
			startInput.modifier = modifier;
			startInput.effect = effect;
			
			endInput.value = effect.end;
			endInput.modifier = modifier;
			endInput.effect = effect;
			
			timeInput.addEventListener('input', updatePathAnimationTime, false);	
			startInput.addEventListener('input', updatePathAnimationStart, false);	
			endInput.addEventListener('input', updatePathAnimationEnd, false);	
			return clone;
		break;
		default:
			return clone;
		break;
	}
}

function changeDescriptionCondition() {
	var value = ddConditionChoice.options[ddConditionChoice.selectedIndex].value;
	
	dConditionDescription.innerHTML = "";
	dConditionDescription.appendChild(descriptionConditions[value]);
}

function changeDescriptionEffect() {
	var value = ddEffectChoice.options[ddEffectChoice.selectedIndex].value;
	
	dEffectDescription.innerHTML = "";
	dEffectDescription.appendChild(descriptionEffects[value]);
}

//------------------------------------------------------------------------
//Export & Import & Test
//------------------------------------------------------------------------

function getSavedData() {
	var savedData = {
		map : svgFileContent,
		infoPanelNames : infoPanelNames,
		infoPanels : infoPanels,
		allModifiers : allModifiers,
		elementsData : elementsData,
		timeline : timelineContent,
		timelineLayers : timelineLayers,
		config : config
	};
	
	return savedData;
}
function exportData() {
	
	var savedData = getSavedData();
	
	var zip = new JSZip();
	zip.file("data.json", "var data = " + JSON.stringify(savedData) + ";");
	
	zip.generateAsync({type:"blob"}).then(function(content) {
		saveAs(content, "inter.zip");
	});
}

function importData() {
	var file = document.getElementById("fileImportData").files[0];
	if (file) {
		JSZip.loadAsync(file).then(function(zip) {
			showPopUp('loading');
			
			zip.file("data.json").async("string")
			.then(function success(content) {
				var data = JSON.parse(content.slice(11, -1));
				
				loadImportedSvgFile(data);
				loadImportedTimeline(data);
				infoPanelNames = data.infoPanelNames;
				infoPanels = data.infoPanels;
				allModifiers = data.allModifiers;
				elementsData = data.elementsData;
				config = data.config;
				
				var allElemsWithModifiers = [];
				for (var i = 0; i < allModifiers.length; i++) {
					allModifiers[i].activated = false;
					var element = superGroup.select("#" + allModifiers[i].element);
					element.data("modifiers").push(allModifiers[i]);
					allElemsWithModifiers.push(element);
				}
				
				for (var i = 0; i < allElemsWithModifiers.length; i++) {
					checkModifiers(allElemsWithModifiers[i]);
				}
				
				setConfigUI();
				hidePopUp();
			}, function error(e) {
				console.log("Cannot load the data");
			});
		});
	}
	else {
		hidePopUp();
	}
}

function testMap() {
	localStorage.setItem("data", JSON.stringify(getSavedData()));
	 window.open('testMap.php','_blank');
}

//------------------------------------------------------------------------
//Popup
//------------------------------------------------------------------------

function hidePopUp() {
	bgpopup.style.visibility = "hidden";
	for (var name in popups) {
		if (popups.hasOwnProperty(name)) {
			popups[name].style.visibility = "hidden";
		}
	}
}

function showPopUp(name) {
	hidePopUp();
	
	bgpopup.style.visibility = "visible";
	popups[name].style.visibility = "visible";
}

//------------------------------------------------------------------------
//Wheel
//------------------------------------------------------------------------

function mouseWheelHandler(event) {
	var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
	
	if ((event.wheelDelta || -event.detail) > 0) {
        var t = new Snap.Matrix();
        t.scale(zoomSpeed, zoomSpeed, event.pageX-svgX, event.pageY-svgY);
        t.add(mapGroup.transform().localMatrix);
        mapGroup.transform(t);
		
		currentZoom *= zoomSpeed;
	}
	else {
		var t = new Snap.Matrix();
        t.scale(1/zoomSpeed, 1/zoomSpeed, event.pageX-svgX, event.pageY-svgY);
        t.add(mapGroup.transform().localMatrix);
        mapGroup.transform(t);
		
		currentZoom /= zoomSpeed;
	}
}

</script>
</body>
</html>