ELEMENT_NODE = 1;

Event.key = function (e) {
	var specialCharTable1 = {
		9 : "TAB",
		27 : "ESC",
		33 : "PageUp",
		34 : "PageDown",
		35 : "End",
		36 : "Home",
		37 : "Left",
		38 : "Up",
		39 : "Right",
		40 : "Down",
		45 : "Insert",
		46 : "Delete"/*,
		comment out for browser comapatibility
		112 : "F1",
		113 : "F2",
		114 : "F3",
		115 : "F4",
		116 : "F5",
		117 : "F6",
		118 : "F7",
		119 : "F8",
		120 : "F9",
		121 : "F10",
		122 : "F11",
		123 : "F12"
					  */
		};
	var specialCharTable2 = {
		8 : "BS",
		13 : "RET",
		32 : "SPC"
		};

	// ignore Ctrl, Shift lonely input
	if ([16, 17].include(e.which)) return;

	//	alert([e.which, e.charCode,  e.keyCode, String.fromCharCode(e.which)].join(", "));

	var chr, input = "";
	if (e.ctrlKey)   input += "C-";
	if (e.altKey)    input += "M-";
	if (e.shiftKey)  input += "S-";
	if (navigator.userAgent.match(RegExp("Gecko/"))) {
		if (e.which == 0) {
			chr = specialCharTable1[e.keyCode];
		} else {
			chr = specialCharTable2[e.which];
			if (!chr) chr = String.fromCharCode(e.which).toLowerCase();
		}
	} else if (navigator.userAgent.match(RegExp("Opera"))) {
		chr = specialCharTable1[e.keyCode];
		if (!chr) chr = specialCharTable2[e.keyCode];
		if (!chr) chr = String.fromCharCode(e.which).toLowerCase();
	} else if (navigator.userAgent.match(RegExp("MSIE"))) {
		chr = specialCharTable1[e.keyCode];
		if (!chr) chr = specialCharTable2[e.keyCode];
		if (!chr) chr = String.fromCharCode(e.keyCode).toLowerCase();
	}
	input += chr;

	return input;
}


$N = Builder.node.bind(Builder);


var TrackTracer = Class.create(); {
	TrackTracer.prototype = {
		initialize : function (parent, num, height, width) {
			this.num = num;
			this.position = 1;
			this.listener = {};

			this.prepare();
			with (this.area.style) {
				height = height ? height : "10px";
				width  = width ? width : "100%";
			}
			with (this.bar.style) {
				width = "100%";
				height = "100%";
			}
			parent.appendChild(this.area);

			this.setPosition(1);
		},

		addEventListener : function (event, func) {
			if (!this.listener[event]) this.listener[event] = [];
			this.listener[event].push(func);
		},

		callEventListener : function (event, e) {
			if (!this.listener[event]) return;
			this.listener[event].each((function (i) {
				i.call(this, e);
			}).bind(this));
		},

		setPosition : function (pos) {
			var w = Element.getDimensions(this.bar).width / this.num;
			if (!w) return;
			with (this.ind.style) {
				width = [w, "px"].join("");
				height = "100%";
				marginLeft = [w * (pos - 1), "px"].join("");
			}
			this.position = pos;
		},

		prepare : function () {
			this.area = document.createElement("div");
			this.area.className = "tracktracer";

			this.bar = document.createElement("div");
			with (this.bar) {
				className = "tracktracer-bar";
			}
			this.ind = document.createElement("div");
			with (this.ind) {
				className = "tracktracer-indicator";
			}

			this.bar.appendChild(this.ind);
			this.area.appendChild(this.bar);

			Event.observe(this.bar, "click", (function (e) {
				var d = Element.getDimensions(this.bar);
				var w = d.width / this.num;
				var x = Event.pointerX(e) - this.bar.offsetLeft;
				this.num.times((function (i) {
					i++;
					if (x < w * i) {
						this.setPosition(i);
						this.callEventListener("change", {
							position : i
							});
						throw $break;
					}
				}).bind(this));
				Event.stop(e);
			}).bindAsEventListener(this));

			var down = false;
			Event.observe(this.ind, "mousedown", function (e) {
				down = true;
			});
			Event.observe(window, "mouseup", function (e) {
				down = false;
			});

			Event.observe(window, "mousemove", (function (e) {
				if (down) {
					var d = Element.getDimensions(this.bar);
					var w = d.width / this.num;
					var x = Event.pointerX(e) - this.bar.offsetLeft;
					this.num.times((function (i) {
						i++;
						if (x < w * i)  {
							if (this.position != i) {
								this.setPosition(i);
								this.callEventListener("change", {
									position : i
									});
							}
							throw $break;
						}
					}).bind(this));
					Event.stop(e);
				}
			}).bindAsEventListener(this), true);
		}
	}
}

var TrackTracerWithTimer = Class.create(); {
	Object.extend(TrackTracerWithTimer.prototype, TrackTracer.prototype);
	TrackTracerWithTimer.prototype._initialize = TrackTracer.prototype.initialize;
	Object.extend(TrackTracerWithTimer.prototype, {
		initialize : function (parent, num, height, width) {
			this._initialize(parent, num, height, width);
			this.timebar = document.createElement("div");
			this.timebar.className = "tracktracerwithtimer-timebar";
			with (this.bar.style) {
				position = "absolute";
			}
			with (this.ind.style) {
				position = "absolute";
				top = "0";
				zIndex = "100";
			}
			with (this.timebar.style) {
				position = "absolute";
				top = "0";
				left = "0";
				height = "100%";
				zIndex = "1";
			}
			this.bar.appendChild(this.timebar);

			this.now = 0;
			this.time = 60;
		},

		// override
		setPosition : function (pos) {
			var w = Element.getDimensions(this.bar).width / this.num;
			if (!w) return;
			with (this.ind.style) {
				width = [w, "px"].join("");
				height = "100%";
				left = [w * (pos - 1), "px"].join("");
			}
			this.position = pos;
		},

		updateTime : function () {
			var w = Element.getDimensions(this.bar).width / this.time;
			with (this.timebar.style) {
				width = [w * this.now, "px"].join("");
			}
		},

		setTime : function (now) {
			if (!now) return;
			this.now = now;
			this.updateTime();
		},

		startTimer : function () {
			this.timer = setInterval((function () {
				this.now++;
				if (this.now > this.time) clearInterval(this.timer);
				this.updateTime();
			}).bind(this), 1000);
		},

		resetTimer : function () {
			clearInterval(this.timer);
			this.now = 0;
			this.updateTime();
		}
	});
}

var SleepyPresentation = Class.create(); {
	SleepyPresentation.prototype = {
		initialize : function (target) {

			this.target = $(target);
			this.pages  = [];
			this.current = 0;

			var index = 0;
			$A(this.target.childNodes).each((function (i) {
				if (i.nodeType == ELEMENT_NODE &&
					Element.hasClassName(i, "section")) {
					this.pages.push(i);

					var link = document.createElement("a");
					link.className = "sleepy-presentation-link";
					link.href = "javascript:void(0)";
					link.index = index;
					link.appendChild(document.createTextNode(["[", index + 1, "]"].join("")));
					var parent = this.findHeading(i);
					if (parent) {
						parent.appendChild(link);
					} else {
						i.appendChild(link);
					}
					Event.observe(link, "click", (function (e) {
						this.show(Event.element(e).index);
						this.on();
					}).bindAsEventListener(this));

					/*
					var tkhs = document.getElementsByClassName("takahashi-method", i);
					tkhs.each((function (i) {
						var a = document.createElement("a");
						a.href = "javascript:void(0)";
						a.title = "launch Takahashi-method";
						a.appendChild(document.createTextNode(i.title || "TKHS-M"));

						i.parentNode.insertBefore(a, i);
					}).bind(this));
					 */
					index++;
				}
			}).bind(this));

			this.prepare();

			this.tracktracer = new TrackTracerWithTimer(this.area, this.pages.length);
			this.tracktracer.addEventListener("change", (function (e) {
				this.show(e.position - 1);
			}).bind(this));

			/*
			this.tracktracer.time = 60;
			this.tracktracer.startTimer();
			 */

			var m = window.location.hash.match(/^#SP(\.(\d+))?/);

			if (m) {
				if (m[0]) this.on();
				if (m[2]) this.show(Number(m[2]) - 1);
			}
		},

		on : function () {
			Element.show(this.area);
			Element.makeClipping(document.body);
			this.show(this.current);
		},

		off : function () {
			location.hash = "#";
			Element.hide(this.area);
			Element.undoClipping(document.body);
			Element.scrollTo(this.pages[this.current]);
		},

		toggle : function () {
			if (Element.visible(this.area)) {
				this.off();
			} else {
				this.on();
			}
		},


		show : function (num) {
			while (this.content.firstChild)
				this.content.removeChild(this.content.firstChild);
			while (this.status.firstChild)
				this.status.removeChild(this.status.firstChild);

			if (num > this.pages.length - 1) {
				this.current = this.pages.length - 1;
			} else if (num < 0) {
				this.current = 0;
			} else {
				this.current = num;
			}

			var status = [
				this.current + 1, "/", this.pages.length
				].join("");
			this.status.appendChild(document.createTextNode(status));

			window.location.hash = ["#SP.", this.current + 1].join("");
			this.content.appendChild(this.pages[this.current].cloneNode(true));
			this.tracktracer.setPosition(this.current+1);

			var h = this.findHeading(this.pages[this.current]);
			if (h) {
				document.title = this.EtoS(h);
			}
		},

		next : function () {
			this.show(this.current + 1);
		},

		prev : function () {
			this.show(this.current - 1);
		},


		sizing : function () {
			with (this.area.style) {
				width = window.innerWidth + "px";
				height = window.innerHeight + "px";
			}
			this.content.style.fontSize = window.innerHeight / 16.5 + "px";
			this.toc.style.height = window.innerHeight + "px";
			this.WBout.style.height = window.innerHeight + "px";
			this.WBout.style.fontSize = window.innerHeight / 5  + "px";
		},


		key : function (e) {
			window.status = Event.key(e);
			if (Event.element(e).nodeName.match(/(input|textarea)$/i)) return;
			switch (Event.key(e)) {
				case "RET":
				case "Right":
				case "PageDown":
				case "c": {
					this.next();
					Event.stop(e);
					break;
				}
				case "BS":
				case "Left":
				case "PageUp":
				case "x": {
					this.prev();
					Event.stop(e);
					break;
				}
				case "Up":
				case "Home":
				case "z": {
					this.show(0);
					Event.stop(e);
					break;
				}
				case "Down":
				case "End":
				case "v": {
					this.show(this.pages.length-1);
					Event.stop(e);
					break;
				}
				case "S-ESC": {
					this.toggle();
					Event.stop(e);
					break;
				}
				case "C-j":
				case "C-m": {
					var input = prompt(["Jump to (max:", this.pages.length, ")"].join(""));
					if (input && input != "") {
						this.show(Number(input) - 1);
					}
					Event.stop(e);
					break;
				}
				case "S-b": {
					Element.toggle(this.WBout);
					Event.stop(e);
					break;
				}
			}
			// alert(String.fromCharCode(e.which));
		},

		findHeading : function (e, level) {
			if (!level) level = 1;
			if (level > 6) return null;

			var nl = e.getElementsByTagName("h" + level);
			if (nl.length > 0) {
				return nl[0];
			} else {
				return arguments.callee.call(this, e, level + 1);
			}
		},

		EtoS : function (e) {
			var ret = [];
			$A(e.childNodes).each((function (i) {
				switch (i.nodeType) {
					case 1: {
						ret.push(this.EtoS(i));
						break;
					}
					case 3: {
						ret.push(i.nodeValue);
						break;
					}
				}
			}).bind(this));
			return ret.join("");
		},

		prepare : function () {
			
			this.area = $N("div", {"class":"sleepy-presentation"}, [
				this.content = $N("div", {"class":"sleepy-presentation-content"}),
				this.status  = $N("div", {"class":"sleepy-presentation-status"}),
				this.WBout   = $N("div", {"class":"sleepy-presentation-wbout"})
			]);
			document.body.appendChild(this.area);
			
			Element.hide(this.area);
			Element.hide(this.WBout);

			this.makeNaviagtion();
			this.makeTOC();
			this.makeWindow();

			Event.observe(this.area, "dblclick", (function (e) {
				this.next();
			}).bindAsEventListener(this));
			

			Event.observe(window, "keypress", this.key.bindAsEventListener(this));
			Event.observe(this.area, "mousewheel", (function (e) {
				if (e.wheel == 1) {
					// up
					this.prev();
				} else {
					this.next();
				}
			}).bindAsEventListener(this));

			Event.observe(window, "resize", this.sizing.bindAsEventListener(this));

			var processing = false;
			Event.observe(window, "mousemove", (function (e) {
				if (processing) return;
				if (Event.pointerY(e) > window.innerHeight * 0.93) {
					if (!Element.visible(this.navigation)) {
						Effect.BlindDown(this.navigation, {duration: 0.25});
						Effect.BlindDown(this.globalNavi, {duration: 0.25});
						processing = true;
						setTimeout(function () {
							processing = false;
						}, 1000);
					}
				} else {
					if (Element.visible(this.navigation)) {
						Effect.BlindUp(this.navigation, {duration: 0.5});
						Effect.BlindUp(this.globalNavi, {duration: 0.5});
						processing = true;
						setTimeout(function () {
							processing = false;
						}, 1500);
					}
				}

			}).bindAsEventListener(this));
			this.sizing();
		},



		makeNaviagtion : function () {
			var first, prev, next, last;
			this.navigation = $N("div", {"class":"sleepy-presentation-navigation"}, [
				first = $N("button", "<<"),
				prev  = $N("button", "<"),
				next  = $N("button", ">"),
				last  = $N("button", ">>")
			]);

			Event.observe(first, "click", (function (e) {
				this.show(0);
			}).bindAsEventListener(this));
			
			Event.observe(last, "click", (function (e) {
				this.show(this.pages.length - 1);
			}).bindAsEventListener(this));

			Event.observe(prev, "click", (function (e) {
				this.prev();
				Event.stop(e);
			}).bindAsEventListener(this));

			Event.observe(next, "click", (function (e) {
				this.next();
				Event.stop(e);
			}).bindAsEventListener(this));

			this.area.appendChild(this.navigation);

			
			var as, toc, bo;
			this.globalNavi = $N("div", {"class": "sleepy-presentation-global-navigation"}, [
				as  = $N("button", {title:"モードをスイッチ"}, "Switch"),
				toc = $N("button", {title:"目次を表示"}, "TOC"),
				bo  = $N("button", {title:"ブラックアウト"}, "BO"),
				con = $N("button", {title:"設定を表示"}, "C")
			]);
			
			as.onclick = (function (e) {
				this.toggle();
			}).bindAsEventListener(this);

			toc.onclick = (function (e) {
				Element.toggle(this.overlay);
			}).bindAsEventListener(this);

			bo.onclick = (function (e) {
				Element.toggle(this.WBout);
			}).bindAsEventListener(this);
			
			con.onclick = (function (e) {
				Element.toggle(this.window);
			}).bindAsEventListener(this);

			document.body.appendChild(this.globalNavi);
		},

		makeTOC : function () {
			this.overlay = $N("div", {"class":"sleepy-presentation-overlay"});
			this.overlay.style.height = this.getWindowHeight() + "px";
			Element.hide(this.overlay);
			document.body.appendChild(this.overlay);

			this.toc = $N("ol", {"class":"sleepy-presentation-toc"})
			
			this.toc.style.height = window.innerHeight + "px";
			this.pages.each((function (i, index) {
				var li = document.createElement("li");
				li.index = index;

				var t = this.findHeading(i);
				if (t) {
					li.appendChild(document.createTextNode(this.EtoS(t)));
				} else {
					li.appendChild(document.createTextNode(index + 1));
				}
				Event.observe(li, "click", (function (e) {
					var index = Event.element(e).index;
					if (Element.visible(this.area)) {
						this.show(index);
					} else {
						this.current = index;
						Element.scrollTo(this.pages[index]);
					}
					this.overlay.style.display = "none";
				}).bindAsEventListener(this));
				this.toc.appendChild(li);
			}).bind(this));

			this.overlay.appendChild(this.toc);
		},

		makeWindow : function () {
			var timeInput, start, reset, close, chkAutoHide;
			this.area.appendChild(
				this.window = $N("div", {"class":"sleepy-presentation-window"}, [
					$N("fieldset", [
						$N("legend", "Configure & Operations"),
						$N("p", [
							$N("label", [
								"Time:",
								timeInput = $N("input", {type:"text",size:"2",value:"20"}),
								"min."
								])
							]),
						$N("p", [
							start = $N("button", "Start"),
							reset = $N("button", "Reset")
						]),
						/*$N("p", [
							$N("label", [
								chkAutoHide = $N("input", {type:"checkbox",checked:"checked"}),
								"ナヴィゲーションを自動で隠す"
								])
						]),*/
						$N("p", [
							close = $N("button", "Close")
							])
						])
					])
				);
			Element.hide(this.window);
			//Event.observe(timeInput, "change", (function (e) {
			//	this.tracktracer.time = timeInput.value;
			//}).bindAsEventListener(this));
			

			Event.observe(start, "click", (function (e) {
				this.tracktracer.time = Number(timeInput.value) * 60;
				this.tracktracer.startTimer();
				window.status = "Timer started";
				Element.hide(this.window);
			}).bindAsEventListener(this));

			Event.observe(reset, "click", (function (e) {
				this.tracktracer.resetTimer();
			}).bindAsEventListener(this));
			
			Event.observe(close, "click", (function (e) {
				Element.hide(this.window);
			}).bindAsEventListener(this));

		},

		getWindowHeight : function () {
			// copy from lightbox plus (but codingrule)

			var ch, sh;
			if (window.innerHeight) {
				ch = window.innerHeight;
			} else if (document.documentElement && document.documentElement.clientHeight) {
				ch = document.documentElement.clientHeight;
			} else {
				ch = document.body.clientHeight;
			}

			if (window.innerHeight && window.scrollMaxY) {
				sh = window.innerHeight + window.scrollMaxY;
			} else if (document.body.scrollHeight > document.body.offsetHeight) {
				sh = document.body.scrollHeight;
			} else {
				sh = document.body.offsetHeight;
			}

			return (ch > sh) ? ch : sh;
		}
	}
}



Event.observe(window, "load", function (e) {
	if (!navigator.userAgent.match(/MSIE/))
		var level1 = new SleepyPresentation("content");
	/*
	Event.observe(document.body, "keypress", function (e) {
		window.status = Event.key(e);
	});
	 */

	
}, false);
