Array.prototype.shuffle = function() {
	for (var i = this.length - 1; i > 0; i--) {
		var j = Math.floor(Math.random() * (i + 1));
		var temp = this[i];
		this[i] = this[j];
		this[j] = temp;
	}
}

var QCU = function(template) {
	this.node = document.createElement("form");
	this.node.className = "qcu";
	this.id = QCU.createId();
	this.items = null;
	this.result = new ExerciceResult();
	this.init(template);
};

QCU.count = 0;

QCU.createId = function() {
	return "qcu." + (++QCU.count);
};

QCU.prototype.init = function(template) {
	var self = this;
	this.node.onsubmit = function() { self.onsubmit(); return false; };

	//items
	this.items = (template.getAttribute("type") == "select")
		? new QCUItemsOption(template, this.id)
		: new QCUItemsRadio(template, this.id);

	this.node.appendChild(this.items.node);

	//bouton
	var button = document.createElement("button");
	button.appendChild(document.createTextNode("Ok"));
	this.node.appendChild(button);

	this.node.appendChild(this.result.node);
};

QCU.prototype.onsubmit = function() {
	var selected = this.items.getSelected();
	if (!selected) {
		this.result.set(false, "Veuillez choisir une réponse !");
	}
	else {
		this.result.set(selected.answer, selected.msg);
	}
};

//--- Items Radio ---
var QCUItemsRadio = function(template, id) {
	this.list = [];
	this.id = id;
	this.init(template);
};

QCUItemsRadio.prototype.init = function(template) {
	this.node = document.createElement("table");
	var choices = document.getChildsByTagName(template, "choice");
	for (var i = 0; i < choices.length; i++) {
		var item = new QCUItemRadio(choices[i], this.id, this.id + "." + i);
		this.list.push(item);
		this.node.appendChild(item.node);
	}
};

QCUItemsRadio.prototype.getSelected = function() {
	for (var i = 0; i < this.list.length; i++) {
		var item = this.list[i];
		if (item.value())
			return item;
	}
	return null;
};

//--- Item Radio ---
var QCUItemRadio = function(template, name, id) {
	this.answer = template.getAttribute("result") == "true" ? true : false;
	this.msg = template.getAttribute("message");
	this.node = document.createElement("tr");
	this.name = name;
	this.id = id;
	this.init(template);
};

QCUItemRadio.prototype.init = function(template) {
	var td = document.createElement("td");
	this.node.appendChild(td);

	this.nodeInput = document.createElement("input");
	this.nodeInput.type = "radio";
	this.nodeInput.value = template.getAttribute("value");
	this.nodeInput.name = this.name;
	this.nodeInput.id = this.id;
	td.appendChild(this.nodeInput);

	var td = document.createElement("td");
	this.node.appendChild(td);

	var label = document.createElement("label");
	label.setAttribute("for", this.id); 
	td.appendChild(label);
	document.moveChilds(template, label);
};

QCUItemRadio.prototype.value = function() {
	return this.nodeInput.checked;
};

//--- Items Option ---
var QCUItemsOption = function(template, id) {
	this.list = [];
	this.node = document.createElement("select");
	this.id = id;
	this.init(template);
};

QCUItemsOption.prototype.init = function(template) {
	this.node.appendChild(document.createElement("option"));

	var choices = document.getChildsByTagName(template, "choice");
	for (var i = 0; i < choices.length; i++) {
		var item = new QCUItemOption(choices[i]);
		this.list.push(item);
		this.node.appendChild(item.node);
	}
};

QCUItemsOption.prototype.getSelected = function() {
	var idx = this.node.selectedIndex;
	return idx > 0 ? this.list[idx - 1] : null;
};

//--- Item Option ---
var QCUItemOption = function(template) {
	this.answer = template.getAttribute("result") == "true" ? true : false;
	this.msg = template.getAttribute("message");
	this.node = document.createElement("option");
	document.moveChilds(template, this.node);
};

var Exercice = function(template) {
	this.node = document.createElement("fieldset");
	this.explain = null;
	this.init(template);
};

Exercice.prototype.init = function(template) {
	this.node.className = "exercice";

	//question
	var question = document.getFirstChildByTagName(template, "question");
	if (question) {
		var div = document.createElement("div");
		div.className = "question";
		document.moveChilds(question, div);
		this.node.appendChild(div);
	}

	//titre
	var legend = document.createElement("legend");
	this.node.appendChild(legend);

	var title = document.getFirstChildByTagName(template, "title");
	if (title) {
		document.moveChilds(title, legend);
	}
	else {
		legend.appendChild(document.createTextNode("Exercice"));
	}
	
	document.mapWidgetTo(document.getChildsByTagName(template, "qcu"), QCU, this.node);
	document.mapWidgetTo(document.getChildsByTagName(template, "qcm"), QCM, this.node);
	document.mapWidgetTo(document.getChildsByTagName(template, "qnum"), QNum, this.node);
	document.mapWidgetTo(document.getChildsByTagName(template, "qplace"), QPlace, this.node);
	document.mapWidgetTo(document.getChildsByTagName(template, "explain"), ExerciceExplain, this.node);
};

var ExerciceExplain = function(template) {
	this.node = document.createElement("div");
	this.nodeContent = document.createElement("div");
	this.init(template);
};

ExerciceExplain.prototype.init = function(template) {
	var self = this;
	this.node.className = "explain";

	var a = document.createElement("a");
	a.onclick = function() { self.swVisibility() };
	a.appendChild(document.createTextNode(template.hasAttribute("title") ? template.getAttribute("title") : "explication"));
	this.setVisibility(false);
	this.node.appendChild(a);

	this.node.appendChild(this.nodeContent);
	this.nodeContent.className = "content";
	document.moveChilds(template, this.nodeContent);
};

ExerciceExplain.prototype.setVisibility = function(value) {
	this.nodeContent.style.display = value ? "" : "none";
};

ExerciceExplain.prototype.getVisibility = function() {
	return !this.nodeContent.style.display ? true : false;
};

ExerciceExplain.prototype.swVisibility = function() {
	this.setVisibility(!this.getVisibility());
};

var ExerciceResult = function() {
	this.node = document.createElement("div");
	this.triesCount = 0;
	this.previousValue = null;
	this.init();
};

ExerciceResult.prototype.init = function() {
	this.node.className = "result";
	this.node.appendChild(document.createTextNode(""));
};

ExerciceResult.prototype.set = function(value, message) {
	if (!this.previousValue || !value) this.triesCount++;
	this.previousValue = value;

	this.node.firstChild.nodeValue = message + " (tentatives : " + this.triesCount + ")";
	this.node.className = "result " + (
		value 
			? (this.triesCount < 3 ? "good" : "tomuchtries")
			: "wrong"
	);
};


var QNum = function(template) {
	this.node = document.createElement("form");
	this.nodeInput = document.createElement("input");
	this.nodeButton = document.createElement("button");
	this.result = new ExerciceResult();
	this.init(template);
};

QNum.prototype.parseFloat = function(str) {
	return parseFloat(str.replace(",", "."));
};

QNum.prototype.init = function(template) {
	var self = this;

	this.node.className = "qnum";
	this.node.onsubmit = function() { self.onsubmit(); return false };
	this.node.appendChild(this.nodeInput);

	//champ de saisie
//	this.nodeInput.setAttribute("type", "number");
	this.nodeInput.setAttribute("step", template.hasAttribute("step") ? template.getAttribute("step") : "any");
	if (template.hasAttribute("min")) this.nodeInput.setAttribute("min", template.getAttribute("min"));
	if (template.hasAttribute("max")) this.nodeInput.setAttribute("max", template.getAttribute("max"));

	//affichage de l’unité
	if (template.hasAttribute("unit")) {
		var span = document.createElement("span");
		span.className = "unit";
		span.appendChild(document.createTextNode(template.getAttribute("unit")));
		this.node.appendChild(span);
	}

	//bouton de validation
	this.nodeButton.appendChild(document.createTextNode("Ok"));
	this.node.appendChild(this.nodeButton);

	//
	this.choices = [];
	if (template.hasAttribute("value")) {
		var value = this.parseFloat(template.getAttribute("value"));
		this.choices.push(new QNumChoice(template.getAttribute("messageEqual"), true, value, template.getAttribute("marginOfError")));
		if (template.hasAttribute("messageLessThan")) this.choices.push(new QNumChoice(template.getAttribute("messageLessThan"), false, false, false, value));
		if (template.hasAttribute("messageGreaterThan")) this.choices.push(new QNumChoice(template.getAttribute("messageGreaterThan"), false, false, false, false, value));
		if (template.hasAttribute("messageNotEqual")) this.choices.push(new QNumChoice(template.getAttribute("messageNotEqual")));
	}

	var nodes = document.getChildsByTagName(template, "choice");
	for (var i = 0; i < nodes.length; i++) {
		this.choices.push(new QNumChoice().readTemplate(nodes[i]));
	}

	this.node.appendChild(this.result.node);
};

QNum.prototype.onsubmit = function() {
	var choice = this.getChoice(this.parseFloat(this.nodeInput.value));
	if (choice) {
		this.result.set(choice.result, choice.msg);
	}
	else {
		this.result.set(false, "Mauvaise réponse");
	}
};

QNum.prototype.getChoice = function(value) {
	for (var i = 0; i < this.choices.length; i++) {
		var choice = this.choices[i];
		if (choice.match(value))
			return choice;
	}
	return false;
};

var QNumChoice = function(msg, result, equal, marginOfError, lessThan, greaterThan) {
	this.msg = msg;
	this.result = result;
	this.equal = equal;
	this.marginOfError = marginOfError;
	this.lessThan = lessThan;
	this.greaterThan = greaterThan;
};

QNumChoice.prototype.readTemplate = function(template) {
	this.msg = template.getAttribute("message");
	this.result = template.getAttribute("result") == "true" ? true : false;

	this.equal = template.hasAttribute("equal") ? QNum.prototype.parseFloat(template.getAttribute("equal")) : false;
	this.marginOfError = template.hasAttribute("marginOfError") ? QNum.prototype.parseFloat(template.getAttribute("marginOfError")) : false;
	this.greaterThan = template.hasAttribute("greaterThan") ? QNum.prototype.parseFloat(template.getAttribute("greaterThan")) : false;
	this.lessThan = template.hasAttribute("lessThan") ? QNum.prototype.parseFloat(template.getAttribute("lessThan")) : false;

	return this;
};

QNumChoice.prototype.match = function(value) {
	if (this.equal) {
		var delta = this.marginOfError ? this.equal * this.marginOfError / 100 : 0.00001;
		return (this.equal - delta <= value && value <= this.equal + delta);
	}

	if (this.greaterThan && this.lessThan)
		return (this.greaterThan < value && value < this.lessThan);

	if (this.greaterThan)
		return value > this.greaterThan;

	if (this.lessThan)
		return value < this.lessThan;

	return true;
};
var QPlace = function(template) {
	this.node = document.createElement("form");
	this.nodePanel = document.createElement("div");
	this.nodeImg = document.createElement("img");
	this.nodeStock = document.createElement("div");
	this.result = new ExerciceResult();

	this.items = [];
	this.targets = [];
	this.drag = null;
	this.debug = (template.getAttribute("debug") == "true");
	this.shuffle = (template.getAttribute("shuffle") != "false");
	this.init(template);
};

QPlace.prototype.selectTarget = function(item) {
	var result = null;
	var distance = null;
	for (var i = 0; i < this.targets.length; i++) {
		var target = this.targets[i];
		var d = target.distanceFrom(item.node);
		if (!result || d < distance) {
			distance = d;
			result = target;
		}
	}
	return distance && distance < 64 ? result : null;
};

QPlace.prototype.getErrorItem = function() {
	for (var i = 0; i < this.items.length; i++) {
		var item = this.items[i];
		if (!item.value())
			return item;
	}
	return null;
};

QPlace.prototype.associated = function() {
	for (var i = 0; i < this.items.length; i++) {
		var item = this.items[i];
		if (!item.associated())
			return false;
	}
	return true;
};

QPlace.prototype.append = function(item) {
	this.items.push(item);
	this.nodeStock.appendChild(item.node);

	this.targets.push(item.target);
	this.nodePanel.appendChild(item.target.node);
};

QPlace.prototype.setPos = function(node, x, y) {
	this.nodePanel.appendChild(node);
	node.style.left = x + "px";
	node.style.top = y + "px";
	node.style.position = "absolute";
};

QPlace.prototype.shuffleChilds = function(parent) {
	var nodes = [];
	for (var node = parent.firstChild; node != null; node = node.nextSibling) {
		nodes.push(node);
	}
	nodes.shuffle();
	for (var i = 0; i < nodes.length; i++) {
		parent.appendChild(nodes[i]);
	}
};

QPlace.prototype.init = function(template) {
	var self = this;
	this.node.className = "qplace";
	if (this.debug) this.node.style.cursor = "crosshair";

	this.nodePanel.className = "panel";
	this.nodePanel.appendChild(this.nodeImg);
	this.nodeImg.src = template.getAttribute("image");

	this.nodePanel.appendChild(this.nodeStock);
	this.nodeStock.className = "stock";

	var items = document.getChildsByTagName(template, "item");
	for (var i = 0; i < items.length; i++) {
		var item = new QPlaceItem(this, items[i]);
		this.append(item);
	}
	if (this.shuffle) this.shuffleChilds(this.nodeStock);

	this.nodePanel.onmousemove = function(e) { self.onmousemove(e); }
	this.nodePanel.onmouseup = function(e) { self.onmouseup(e); }
	this.node.appendChild(this.nodePanel);

	//bouton
	var button = document.createElement("button");
	button.appendChild(document.createTextNode("Ok"));
	this.node.appendChild(button);

	this.node.appendChild(this.result.node);
	this.node.onsubmit = function() { self.onsubmit(); return false; };
};

QPlace.prototype.onmousemove = function(e) {
	if (this.debug) {
		var rect = this.node.getBoundingClientRect();
		console.log("(" + (e.clientX - rect.left) + ", " + (e.clientY - rect.top) + ")");
	}
	if (this.drag) this.drag.onmousemove(e);
};

QPlace.prototype.onmouseup = function(e) {
	if (this.drag) this.drag.stop();
	this.drag = null;
};

QPlace.prototype.onmousedown = function(item, e) {
	if (!this.drag)	this.drag = new QPlaceItemDrag(item, e);
};

QPlace.prototype.onsubmit = function() {
	var associated = this.associated();
	if (!associated) {
		this.result.set(false, "Vous devez placer tous les items !");
		return;
	}

	var item = this.getErrorItem();
	if (item) {
		this.result.set(false, item.msg ? item.msg : "Pas tout à fait");
		return;
	}

	this.result.set(true, "Bravo !");
};

//---QPlaceItem
QPlaceItem = function(parent, template) {
	this.parent = parent;
	this.node = document.createElement("div");
	this.drag = null;
	this.msg = template.getAttribute("message");
	this.target = new QPlaceTarget(parseInt(template.getAttribute("x")), parseInt(template.getAttribute("y")), template.getAttribute("align"));
	this.setAssocTarget(null);
	this.init(template);
};

QPlaceItem.prototype.setAssocTarget = function(target) {
	this.assocTarget = target;
	if (target) {
		this.setBasePos(target.x, target.y, target.align);
	}
};

QPlaceItem.prototype.value = function() {
	return this.target == this.assocTarget;
};

QPlaceItem.prototype.associated = function() {
	return this.assocTarget != null;
};

QPlaceItem.prototype.setBasePos = function(x, y, align) {
	var rect = this.node.getBoundingClientRect();
	var coord = false;

	switch (align) {
	case "bottom":
		coord = [x - rect.width / 2, y];
		break;

	case "top":
		coord = [x - rect.width / 2, y - rect.height];
		break;

	case "right":
		coord = [x, y - rect.height / 2];
		break;

	case "left":
		coord = [x - rect.width, y - rect.height / 2];
		break;

	case "middle":
	default:
		coord = [x - rect.width / 2, y - rect.height / 2];
		break;
	}

	if (coord) this.setPos(coord[0], coord[1]);
};

QPlaceItem.prototype.setPos = function(x, y) {
	this.x = x;
	this.y = y;
	this.parent.setPos(this.node, x, y);
};

QPlaceItem.prototype.selectTarget = function() {
	return this.parent.selectTarget(this);
};

QPlaceItem.prototype.init = function(template) {
	var self = this;
	this.node.className = "item";
	document.moveChilds(template, this.node);
	this.node.onmousedown = function(e) { self.onmousedown(e); }
};

QPlaceItem.prototype.onmousedown = function(e) {
	this.parent.onmousedown(this, e);
};

//--- QPlaceTarget
var QPlaceTarget = function(x, y, align) {
	this.x = x;
	this.y = y;
	this.align = align ? align : "bottom";
	this.node = document.createElement("div");
	this.init();
};

QPlaceTarget.prototype.init = function() {
	this.setActivated(false);
	this.node.style.left = this.x + "px";
	this.node.style.top = this.y + "px";
	this.node.style.position = "absolute";
};

QPlaceTarget.prototype.setActivated = function(value) {
	this.node.className = value ? "target selected" : "target";
};

QPlaceTarget.prototype.distanceFrom = function(node) {
	var rect = this.node.getBoundingClientRect();
	rectX = rect.left + rect.width / 2;
	rectY = rect.top + rect.height / 2;

	var rectFrom = node.getBoundingClientRect();
	rectFromX = rectFrom.left + rectFrom.width / 2;
	rectFromY = rectFrom.top + rectFrom.height / 2;

	return Math.sqrt(Math.pow(rectFromX - rectX, 2) + Math.pow(rectFromY - rectY, 2));
};

//--- QPlaceItemDrag
var QPlaceItemDrag = function(item, e) {
	item.setAssocTarget(null);
	this.target = null;
	this.item = item;
	this.fromX = e.clientX;
	this.fromY = e.clientY;
	var rectItem = item.node.getBoundingClientRect();
	var rectParent = item.parent.node.getBoundingClientRect();
	this.x = rectItem.left - rectParent.left;
	this.y = rectItem.top - rectParent.top;
};

QPlaceItemDrag.prototype.onmousemove = function(e) {
	this.item.setPos(this.x + e.clientX - this.fromX, this.y + e.clientY - this.fromY);
	this.setTarget(this.item.selectTarget())
};

QPlaceItemDrag.prototype.setTarget = function(target) {
	if (this.target) {
		this.target.setActivated(false);
		this.target = null;
	}
	if (target) {
		target.setActivated(true);
		this.target = target;
	}
}

QPlaceItemDrag.prototype.stop = function() {
	this.item.setAssocTarget(this.target);
	if (this.target) this.item.setAssocTarget(this.target);
	this.setTarget(null);
}
var QCM = function(template) {
	this.node = document.createElement("form");
	this.list = [];
	this.id = QCM.createId();
	this.messageTrue = template.hasAttribute("messageTrue") ? template.getAttribute("messageTrue") : "Bonne réponse !";
	this.messageFalse = template.hasAttribute("messageFalse") ? template.getAttribute("messageFalse") : "Faux !";
	this.result = new ExerciceResult();
	this.init(template);
};

QCM.count = 0;

QCM.createId = function() {
	return "qcm." + (++QCM.count);
};

QCM.prototype.init = function(template) {
	var self = this;
	this.node.className = "qcm";

	var table = document.createElement("table");
	this.node.appendChild(table);
	this.node.onsubmit = function() { self.onsubmit(); return false; };

	var choices = document.getChildsByTagName(template, "choice");
	for (var i = 0; i < choices.length; i++) {
		var item = new QCMItem(choices[i], this.id + "." + i);
		this.list.push(item);
		table.appendChild(item.node);
	}

	var button = document.createElement("button");
	button.appendChild(document.createTextNode("Ok"));
	this.node.appendChild(button);

	this.node.appendChild(this.result.node);
};

QCM.prototype.getWrongItem = function() {
	for (var i = 0; i < this.list.length; i++) {
		var item = this.list[i];
		if (item.value() != item.answer)
			return item;
	}
	return false;
};

QCM.prototype.onsubmit = function() {
	var wrongItem = this.getWrongItem();
	if (wrongItem) {
		this.result.set(false, wrongItem.msg ? wrongItem.msg : this.messageFalse);
	}
	else {
		this.result.set(true, this.messageTrue);
	}
};

var QCMItem = function(template, id) {
	this.node = document.createElement("tr");
	this.answer = template.getAttribute("result") == "true" ? true : false;
	this.msg = template.getAttribute("message");
	this.id = id;
	this.init(template);
};

QCMItem.prototype.init = function(template) {
	var td = document.createElement("td");
	this.node.appendChild(td);

	this.nodeInput = document.createElement("input");
	this.nodeInput.type = "checkbox";
	this.nodeInput.value = template.getAttribute("value");
	this.nodeInput.id = this.id;
	td.appendChild(this.nodeInput);

	var td = document.createElement("td");
	this.node.appendChild(td);

	var label = document.createElement("label");
	label.setAttribute("for", this.id); 
	td.appendChild(label);
	document.moveChilds(template, label);
};

QCMItem.prototype.value = function() {
	return this.nodeInput.checked;
};

var Glossary = function() {
	this.defs = [];
	this.hooks = [];
};

var glossary = new Glossary();

Glossary.prototype.empty = function() {
	return this.defs.length == 0;
};

Glossary.prototype.defId = function(def) {
	for (var i = 0; i < this.defs.length; i++) {
		if (def == this.defs[i])
			return i + 1;
	}
	return false;
;}

Glossary.prototype.addDefinitionLabel = function(node, def, label, cb) {
	var text = node.nodeValue;
	var pos = text.indexOf(label);
	if (pos > 0) {
		var item = def ? new GlossaryItem(label, def, cb) : false;
		var itemNode = item ? item.node : document.createTextNode(label);

		var left = text.substr(0, pos);
		var endpos = pos + label.length;
		var right = text.substr(endpos, text.length - endpos);

		node.nodeValue = left;
		node.parentNode.insertBefore(itemNode, node.nextSibling);
		node.parentNode.insertBefore(document.createTextNode(right), itemNode.nextSibling);
	}
};

Glossary.prototype.addDefinitionText = function(node, hook, cb) {
	this.addDefinitionLabel(node, hook.def, hook.str, cb);
};

Glossary.prototype.addDefinitionsText = function(node, cb) {
	for (var i = 0; i < this.hooks.length; i++) {
		this.addDefinitionText(node, this.hooks[i], cb);
	}
};

Glossary.prototype.addDefinitions = function(node, cb) {
	for (var child = node.firstChild; child != null; child = child.nextSibling) {
		if (child.nodeType == Node.ELEMENT_NODE) {
			this.addDefinitions(child, cb);
		}
		else if (child.nodeType == Node.TEXT_NODE) {
			this.addDefinitionsText(child, cb);
		}
	}
};

Glossary.prototype.append = function(defs) {
	for (var i = 0; i < defs.length; i++) {
		var def = defs[i];
		this.defs.push(def);
		for (var j = 0; j < def.search.length; j++) {
			this.hooks.push({"str": def.search[j], "def": def});
		}
		if ("exclude" in def) {
			for (var j = 0; j < def.exclude.length; j++) {
				this.hooks.push({"str": def.exclude[j], "def": false});
			}
		}
	}
	this.hooks.sort(function(a, b) {
		if (a.str.length > b.str.length)
			return -1;
		if (a.str.length < b.str.length)
			return 1;
		return 0;
	});
};

var GlossaryItem = function(label, def, cb) {
	this.node = document.createElement("abbr");
	this.node.className = "glossaryItem";
	this.node.appendChild(document.createTextNode(label));
	this.node.setAttribute("title", def.definition);
	this.def = def;
	this.node.onclick = function() { if (cb) cb(def); };
};

var GlossaryWidget = function(glossary) {
	this.node = document.createElement("div");
	this.glossary = glossary;
	this.init();
};

GlossaryWidget.prototype.init = function() {
	this.node.className = "glossary";

	var div = document.createElement("section");
	this.node.appendChild(div);

	var nodeClose = document.createElement("button");
	nodeClose.className = "close";
	nodeClose.appendChild(document.createTextNode("Fermer"))
	div.appendChild(nodeClose);
	
	var h1 = document.createElement("h1");
	h1.appendChild(document.createTextNode("Glossaire"));
	div.appendChild(h1);

	var dl = document.createElement("dl");
	div.appendChild(dl);

	for (var i = 0; i < this.glossary.defs.length; i++) {
		var def = this.glossary.defs[i];

		var dfn = document.createElement("dfn");
		dfn.id = glossary.defId(def);
		dfn.appendChild(document.createTextNode(def.title));

		var dt = document.createElement("dt");
		dt.appendChild(dfn);
		dl.appendChild(dt);

		var dd = document.createElement("dd");
		dd.innerHTML = def.definition;
		dl.appendChild(dd);
	}
	this.setVisibility(false);

	var self = this;
	this.node.onclick = function() { self.setVisibility(false) };
};

GlossaryWidget.prototype.setVisibility = function(value) {
	this.node.style.display = value ? "" : "none";
};

GlossaryWidget.prototype.getVisibility = function() {
	return this.node.style.display == "" ? true : false;
};

GlossaryWidget.prototype.swVisibility = function() {
	this.setVisibility(!this.getVisibility());
};

GlossaryWidget.prototype.show = function(def) {
	this.setVisibility(true);
	if (def) location.hash = glossary.defId(def);
};
var Menu = function(items, current) {
	this.node = document.createElement("div");
	this.nodeImg = document.createElement("img");
	this.nodePopup = document.createElement("div");
	this.current = current;
	this.hideTimeout = null;
	this.init(items);
};

Menu.prototype.createItems = function(items) {
	var ul = document.createElement("ul");
	for (key in items) {
		var item = items[key];
		if ("type" in item && item.type == "part") {
			var li = document.createElement("li");
			li.appendChild(document.createTextNode(item.title));
			li.className = "part";
			ul.appendChild(li);
			var partUl = document.createElement("ul");
			ul.appendChild(partUl);
			partUl.appendChild(this.createItems(item.menu));
		}
		else {
			ul.appendChild(new MenuItem(item, this.current == key).node);
		}
	}
	return ul;
};

Menu.prototype.addItem = function(node) {
	this.nodePopup.appendChild(node);
};

Menu.prototype.init = function(items) {
	var self = this;
	this.node.className = "menu";
	
	this.node.appendChild(this.nodeImg);
	this.nodeImg.src = "img/icons/menu_32.png";
	this.nodeImg.onclick = function() { self.onclick() };
	this.nodeImg.onmouseenter = function() { self.onmouseenter() };
	this.nodePopup.onmouseover = function() { self.onmouseover() };
	this.nodePopup.onmousemove = function() { self.onmouseover() };

	this.setVisibility(false);
	this.node.appendChild(this.nodePopup);
	this.nodePopup.className = "popup";
	this.addItem(this.createItems(items));
};

Menu.prototype.onclick = function() {
	this.swVisibility();
};

Menu.prototype.onmouseenter = function() {
	this.setVisibility(true);
	this.startHideTimeout();
};

Menu.prototype.startHideTimeout = function() {
	var self = this;

	if (this.hideTimeout) {
		clearTimeout(this.hideTimeout);
		this.hideTimeout = null;
	}

	this.hideTimeout = setTimeout(function() {
		self.setVisibility(false);
	}, 10000);
};

Menu.prototype.onmouseover = function() {
	this.startHideTimeout();
};

Menu.prototype.setVisibility = function(value) {
	this.nodePopup.style.display = value ? "" : "none";
};

Menu.prototype.getVisibility = function() {
	return this.nodePopup.style.display == "" ? true : false;
};

Menu.prototype.swVisibility = function() {
	this.setVisibility(!this.getVisibility());
};

var MenuItem = function(arr, current) {
	this.node = document.createElement("li");
	this.init(arr, current);
};

MenuItem.prototype.init = function(arr, current) {
	var a = document.createElement("a");
	if ("icon" in arr) {
		var icon = document.createElement("img");
		icon.src = arr.icon;
		a.appendChild(icon);
	}
	a.appendChild(document.createTextNode(arr.title));
	a.href = arr.path;
	this.node.appendChild(a);

	if (current) this.node.className = "current";
};
document.getChildsByTagName = function(parent, name) {
	var result = [];
	for (var i = 0; i < parent.childNodes.length; i++) {
		var child = parent.childNodes[i];
		if (child.nodeName.toLowerCase() == name.toLowerCase()) {
			result.push(child);
		}
	}
	return result;
};

document.getFirstChildByTagName = function(parent, name) {
	var childs = this.getChildsByTagName(parent, name);
	return childs.length > 0 ? childs[0] : false;
};

document.moveChilds = function(from, to) {
	while (from.firstChild) to.appendChild(from.firstChild);
};

document.mapWidget = function(nodes, Widget) {
	while (nodes.length > 0) {
		var node = nodes[0];
		var widget = new Widget(node);
		node.parentNode.replaceChild(widget.node, node);
	}
};

document.moveNodes = function(nodes, to) {
	for (var i = 0; i < nodes.length; i++) {
		to.appendChild(nodes[i]);
	}
};

document.mapWidgetTo = function(nodes, Widget, to) {
	for (var i = 0; i < nodes.length; i++) {
		var node = nodes[i];
		var widget = new Widget(node);
		to.appendChild(widget.node);
	}
};

document.createElementSVG = function(tagName) {
	return document.createElementNS("http://www.w3.org/2000/svg", tagName);
};
var Page = function(name) {
	this.name = name;
	this.menu = null;

	this.nodeHeader = document.createElement("header");
	this.nodeFooter = document.createElement("footer");
	this.nodeContent = document.createElement("div");
	this.widgetGlossary = !glossary.empty() ? new GlossaryWidget(glossary) : null;
};

Page.prototype.createPartners = function(list) {
	var div = document.createElement("div");
	div.className = "partners";

	for (var i = 0; i < list.length; i++) {
		var img = document.createElement("img");
		img.src = list[i];
		div.appendChild(img);
	}
	return div;		
};

Page.prototype.hasMathmlSupport = function() {
	return false;
};

Page.prototype.transformMathml = function(node) {
	for (var child = node.firstChild; child != null; child = child.nextSibling) {
		if (child.nodeType == Node.ELEMENT_NODE) {
			if (child.tagName.toLowerCase() == "math" && child.hasAttribute("alt")) {
				var img = document.createElement("img");
				img.className = "mathml";
				img.src = child.getAttribute("alt");
				child.parentNode.replaceChild(img, child);
				child = img;
			}
			this.transformMathml(child);
		}
	}
};

Page.prototype.hasThoptSupport = function() {
	return navigator.userAgent == "EspFx";
};

Page.prototype.enableThopt = function(node, value) {
	for (var child = node.firstChild; child != null; child = child.nextSibling) {
		if (child.nodeType == Node.ELEMENT_NODE) {
			if (child.tagName.toLowerCase() == "a" && child.className == "thermoptim") {
				child.className = "thermoptim " + (value ? "enabled" : "disabled");
			}
			this.enableThopt(child, value);
		}
	}
};

Page.prototype.show = function() {
	var self = this;
	if (!this.hasMathmlSupport())
		this.transformMathml(document.body);

	this.enableThopt(document.body, this.hasThoptSupport());

	//Content
	this.nodeContent.className = "content";
	while (document.body.firstChild) this.nodeContent.appendChild(document.body.firstChild);

	if (glossary) {
		glossary.addDefinitions(this.nodeContent, function(def) {
			self.widgetGlossary.show(def);
		});
	}
	document.mapWidget(this.nodeContent.getElementsByTagName("exercice"), Exercice);
	document.mapWidget(this.nodeContent.getElementsByTagName("sections"), Sections);

	//Menu
	if (typeof settings !== "undefined") {
		if ("menu" in settings) {
			this.menu = new Menu(settings.menu, this.name);
			this.nodeHeader.appendChild(this.menu.node);
		}

		//Footer
		if ("license" in settings) {
			this.nodeFooter.appendChild(document.createTextNode(settings.license));
		}

		if ("partners" in settings) {
			this.nodeFooter.appendChild(this.createPartners(settings.partners));
		}
		
		if (this.widgetGlossary) {
			var ul = document.createElement("ul");
			ul.className = "tools";
			
			var li = document.createElement("li");
			var img = document.createElement("img");
			img.src = "img/icons/lecon.png";
			li.appendChild(img);
			li.appendChild(document.createTextNode("Glossaire"));
			li.onclick = function() { self.widgetGlossary.show() };
			ul.appendChild(li);
			
			this.menu.addItem(ul);
		}
	}

	document.body.appendChild(this.nodeHeader);
	document.body.appendChild(this.nodeContent);
	if (this.nodeFooter.childNodes.length > 0) document.body.appendChild(this.nodeFooter);
	if (this.widgetGlossary) document.body.appendChild(this.widgetGlossary.node);
};

var Sections = function(template) {
	this.list = [];
	this.current = 0;

	this.node = document.createElement("div");
	this.menuNode = document.createElement("ul");
	this.viewNode = document.createElement("div");
	this.nextNode = document.createElement("img");
	this.previousNode = document.createElement("img");
	this.selectorNode = document.createElement("div");


	this.init(template);
};

Sections.prototype.init = function(template) {
	var self = this;
	this.node.className = "sections";
	this.node.appendChild(this.menuNode);
	this.node.appendChild(this.viewNode);
	this.node.appendChild(this.selectorNode);

	this.selectorNode.className = "selector";
	this.viewNode.className = "view";

	this.previousNode.src = "img/icons/previous_16.png";
	this.previousNode.onclick = function(e) { self.previous() };
	this.selectorNode.appendChild(this.previousNode);

	this.nextNode.src = "img/icons/next_16.png";
	this.nextNode.onclick = function(e) { self.next() };
	this.selectorNode.appendChild(this.nextNode);

	var end = document.createElement("div");
	end.className = "end";
	this.node.appendChild(end);

	this.extract(template);
	this.show(this.current);
};

Sections.prototype.push = function(item) {
	this.list.push(item);
	this.menuNode.appendChild(item.node);
};

Sections.prototype.extract = function(parent) {
	var sections = document.getChildsByTagName(parent, "section");
	for (var i = 0; i < sections.length; i++) {
		var section = sections[i];
		if (section.getAttribute("default") == "true") this.current = i;
		var h1 = document.getChildsByTagName(section, "h1");
		if (h1.length > 0) {
			var title = h1[0].innerText;
			var item = new SectionsItem(this, title, section, this.list.length);
			this.push(item);
		}
	}
}

Sections.prototype.previous = function() {
	this.showInc(-1);
	this.currentItem.scrollIntoView(false);
};

Sections.prototype.next = function() {
	this.showInc(+1);
	this.currentItem.scrollIntoView(false);
};

Sections.prototype.showItem = function(item) {
	if (this.currentItem) this.currentItem.setCurrent(false);
	while (this.viewNode.firstChild) this.viewNode.removeChild(this.viewNode.firstChild);
	this.currentItem = item;
	this.currentItem.setCurrent(true);
	this.viewNode.appendChild(item.target);
};

Sections.prototype.show = function(index) {
	this.current = index;
	this.previousNode.className = index > 0 ? "" : "disabled";
	this.nextNode.className = (index < this.list.length - 1) ? "" : "disabled";
	this.showItem(this.list[index]);
};

Sections.prototype.showInc = function(value) {
	var i = this.current + value;
	i = Math.min(i, this.list.length - 1)
	i = Math.max(i, 0);
	this.show(i);
};

var SectionsItem = function(parent, title, target, index) {
	this.parent = parent;
	this.title = title;
	this.target = target;
	this.index = index;
	this.node = document.createElement("li");
	this.init();
};

SectionsItem.prototype.init = function() {
	var self = this;

	this.node.appendChild(document.createTextNode(this.title));
	this.node.onclick = function(e) { self.onclick(e); };

	var end = document.createElement("div");
	end.className = "end";
	this.target.appendChild(end);
};

SectionsItem.prototype.onclick = function(e) {
	this.parent.show(this.index);
	this.scrollIntoView(false);
};

SectionsItem.prototype.setCurrent = function(value) {
	this.node.className = value ? "current" : "";
};

SectionsItem.prototype.scrollIntoView = function(alignWithTop) {
	this.node.scrollIntoView(alignWithTop);
};
