// JSDeferred
$.deferred.define();

var WEBROOT = "/";

$(document).ready(function() {
	new interFORest();
});


function interFORest() {
	this.canvas = document.getElementById("canvas");
	if (!this.canvas || !this.canvas.getContext)
		return;
	this.context = this.canvas.getContext("2d");
	this.canvas_width = this.canvas.width;
	this.canvas_height = this.canvas.height;
	this.update_canvas_timer_wait = 50;
	this.canvas_layer = document.getElementById("canvas_layer");

	var _style = document.defaultView.getComputedStyle(this.canvas, "");
	this.CANVAS_FONT = [_style.fontStyle, _style.fontVarian, _style.fontWeight, _style.fontSize, "/", _style.lineHeight, _style.fontFamily].join(" ");
	var _div = document.createElement("div");
	_div.textContent = "inteFORest";
	_div.setAttribute("style", "font:" + this.CANVAS_FONT + ";visibility:hidden;");
	document.body.appendChild(_div);
	this.CANVAS_FONT_HEIGHT = _div.clientHeight;
	document.body.removeChild(_div);

	this.words_layer = document.getElementById("words_layer");

	this.forest_layer = document.getElementById("forest_layer");
	this.message_layer = document.getElementById("message_layer");

	this.forest_canvas = document.createElement("canvas");
	this.forest_canvas.width = this.canvas_width;
	this.forest_canvas.height = this.canvas_height;
	this.forest_context = this.forest_canvas.getContext("2d");
	this.forest_ground = document.getElementById("forest_ground");
	this.forest_trees = document.getElementById("forest_trees");
	this.forest_arrow = document.getElementById("forest_arrow");

	this.zoomin_layer = document.getElementById("zoomin_layer");
	this.zoomin_canvas = document.createElement("canvas");
	this.zoomin_canvas.width = this.canvas_width;
	this.zoomin_canvas.height = this.canvas_height;
	this.zoomin_context = this.zoomin_canvas.getContext("2d");
	this.zoomin_background = new Image();
	this.zoomin_background.src = WEBROOT+"images/zoomin_ground.jpg";
	this.marked_tree = null;
	this.selected_x = -1;
	this.selected_y = -1;

	this.zoomin_offscreen_x1 = -1;
	this.zoomin_offscreen_y1 = -1;
	this.zoomin_offscreen_x2 = -1;
	this.zoomin_offscreen_y2 = -1;
	this.focused_trees = [];
	this.drawn_trees = {};

	this.move_id = 0;
	this.move_timer_wait = 50;

	this.zoomin_layer_arrow = new Image();
	this.zoomin_layer_arrow.src = WEBROOT+"images/arrow.png";
	this.zoomin_layer_up = document.getElementById("zoomin_layer_up");
	this.zoomin_layer_down = document.getElementById("zoomin_layer_down");
	this.zoomin_layer_left = document.getElementById("zoomin_layer_left");
	this.zoomin_layer_right = document.getElementById("zoomin_layer_right");

	this.forest_navi = document.getElementById("forest_navi");
	this.forest_navi_canvas = document.getElementById("forest_navi_canvas");
	this.forest_navi_context = this.forest_navi_canvas.getContext("2d");
	this.forest_navi_trees = document.getElementById("forest_navi_trees");

	this.zukan_layer = document.getElementById("zukan_layer");
	this.zukan_board = document.getElementById("zukan_board");
	this.zukan_canvas = document.getElementById("zukan_canvas");
	this.zukan_context = this.zukan_canvas.getContext("2d");
	this.zukan_close_button = document.getElementById("zukan_close_button");

	this.zukan_last_showing_image = -1;
	this.zukan_last_showing_description = -1;

	this.zukan_bookmark_index = -1;
	this.zukan_page_background_r = new Image();
	this.zukan_page_background_r.src = WEBROOT+"images/zukan_page_r.png";
	this.zukan_page_background_l = new Image();
	this.zukan_page_background_l.src = WEBROOT+"images/zukan_page_l.png";
	this.zukan_labels = document.getElementById("zukan_labels");

	this.FLIP_START = 0;
	this.FLIP_WAIT = 1;
	this.FLIP_ANIM = 2;
	this.FLIP_END = 3;
	this.flip_state = this.FLIP_END;
	this.FLIP_RIGHT = true;
	this.FLIP_LEFT = false;
	this.flipDescScale = 1;
	this.zukan_last_flip_description = -1;

	this.zoomin_to_forest = 0;
	this.zoomin_to_forest_max = 10;
	this.forest_to_zoomin = 0;
	this.forest_to_zoomin_max = 10;

	this.black_mask = document.getElementById("black_mask");

	this.map_min_x = 0;
	this.map_max_x = 1024;
	this.map_min_y = 0;
	this.map_max_y = 768;
	this.NAVI_AREA_COLOR = "rgba(0, 0, 0, 0.6)";
	this.NAVI_BOOKMARK_COLOR = "rgba(0, 0, 255, 0.6)";
	this.ZOOMIN_TREE_TITLE_COLOR = "white";
	this.ZOOMIN_TREE_SHADOW_COLOR = "black";

	this.FOREST_VIEW = "forest";
	this.FOREST_TO_ZOOMIN = "_forestToZoomin";
	this.ZOOMIN_VIEW = "zoom";
	this.ZOOMIN_TO_FOREST = "_zoominToForest";
	this.ZUKAN_VIEW = "detail";
	this.view = this.FOREST_VIEW;

	this.LEAF_COLOR_1 = "rgb(0, 51, 0)";
	this.LEAF_COLOR_2 = "rgb(51, 102, 30)";
	this.LEAF_COLOR_3 = "rgb(153, 152, 0)";
	this.FOREST_CANVAS_TREE_RATE = 0.2;

	//auto reload
	this.MESSAGES_AUTO_RELOAD_TIME = 1000 * 60 * 5;
	this.ZAWAZAWA_AUTO_RELOAD_TIME = 1000 * 60 * 1;
	this.WORDS_AUTO_RELOAD_TIME = 1000 * 60 * 5;

	var self = this;
	//get all informations.
	var callback = function(contents) {
		self.trees = JSON.parse(contents);
		self.initialize();
	}
	$.get(WEBROOT+"alltrees.json", callback, "text");
}


interFORest.prototype.initialize = function() {
	this.initializeTrees();

	var self = this;

	var canvas_listener = function(e) {
		self.onClickOnCanvas(e);
	};
	var canvas_mousemove_listener = function(e) {
		self.onMouseMoveOnCanvas(e);
	};
	this.canvas.addEventListener("click", canvas_listener, false);
	this.canvas.addEventListener("mousemove", canvas_mousemove_listener, false);


	var zoomin_layer_click_listener = function(e) {
		self.onClickOnZoominCanvas(e);
	}
	var zoomin_layer_movein_listener = function(e) {
		self.onMouseMoveOnZoomin(e);
	}
	this.zoomin_layer.addEventListener("click", zoomin_layer_click_listener, false);
	this.zoomin_layer.addEventListener("mousemove", zoomin_layer_movein_listener, false);

	var MOVE_AMOUNT = 20;
	var zoomin_layer_up_listener = function(e) {
		e.stopPropagation();
		self.moveZoominView(0, -MOVE_AMOUNT);
	};
	var zoomin_layer_down_listener = function(e) {
		e.stopPropagation();
		self.moveZoominView(0, MOVE_AMOUNT);
	};
	var zoomin_layer_right_listener = function(e) {
		e.stopPropagation();
		self.moveZoominView(MOVE_AMOUNT, 0);
	};
	var zoomin_layer_left_listener = function(e) {
		e.stopPropagation();
		self.moveZoominView(-MOVE_AMOUNT, 0);
	};
	this.zoomin_layer_up.addEventListener("click", zoomin_layer_up_listener, false);
	this.zoomin_layer_down.addEventListener("click", zoomin_layer_down_listener, false);
	this.zoomin_layer_right.addEventListener("click", zoomin_layer_right_listener, false);
	this.zoomin_layer_left.addEventListener("click", zoomin_layer_left_listener, false);

	var forest_navi_listener = function(e) {
		e.stopPropagation();
		self.onClickOnForestNavi(e);
	};
	this.forest_navi.addEventListener("click", forest_navi_listener, false);


	var hide_zukan_listener = function(e) {
		self.hideZukan(e);
	};
	this.black_mask.addEventListener("click", hide_zukan_listener, false);
	this.zukan_close_button.addEventListener("click", hide_zukan_listener, false);


	var zukan_layer_mousedown_listener = function(e) {
		self.onMouseDownOnZukanLayer(e);
	}
	var zukan_layer_mouseup_listener = function(e) {
		self.onMouseUpOnZukanLayer(e);
	}
	var zukan_layer_mousemove_listener = function(e) {
		self.onMouseMoveOnZukanLayer(e);
	}
	var zukan_layer_mouseout_listener = function(e) {
		self.onMouseOutOnZukanLayer(e);
	}
	this.zukan_layer.addEventListener("mousedown", zukan_layer_mousedown_listener, false);
	this.zukan_layer.addEventListener("mouseup", zukan_layer_mouseup_listener, false);
	this.zukan_layer.addEventListener("mousemove", zukan_layer_mousemove_listener, false);
	this.zukan_layer.addEventListener("mouseout", zukan_layer_mouseout_listener, false);

	// Keyboard Navigation
	var keydown_event_listener = function(e) {
		self.onKeyDownOnWindow(e);
    }
	window.addEventListener("keydown", keydown_event_listener, false);

	$("._zukan_page_label").click(function() {
		var el = $(this).closest("div").find("._zukan_serialno_label");
		el.attr("readOnly", false).addClass("editing");
		el.get(0).focus();
	});

	$("._zukan_serialno_label").keydown(function(e) {
		var val = $(this).val();
		if (e.keyCode == 27) {
			$(this).get(0).blur();
			return;
		} else if (e.keyCode == 13) {
			var val = $(this).val();
			if (self.isValidPageNumber(val)) {
				self.showTree(parseInt(val, 10) - 1, true);
				$(this).get(0).blur();
			}
			return;
		}
		$(this).toggleClass("notfound", !self.isValidPageNumber(val));
	}).blur(function() {
		$(this).val(self.showing_index + 1).attr("readOnly", true).removeClass("editing").removeClass("notfound");

	});

	//zawazawa
	//this.reloadZawazawa();
	//messages
	//this.reloadMessages();

	var treesimage = "images/forest_trees.png";
	this.forest_trees.setAttribute("src", treesimage);
	this.forest_navi_trees.setAttribute("src", treesimage);
	//words
	this.reloadWords();

	// tree.hash -> this.trees[id]
	$.get(WEBROOT+"hash2id.json", function(contents) {
		self.treeIdFromHash = JSON.parse(contents);
	}, "text");

	this.updateCanvas();
	// parse url hash
	this.parseURLHash();
}

interFORest.prototype.initializeTrees = function() {
	var rate = this.FOREST_CANVAS_TREE_RATE;
	// XXX O(trees.length)
	for (var i = 0, n = this.trees.length, tree; i < n; i++) {
		tree = this.trees[i];
		if (tree == null) {
			continue;
		}
		var halfWidth = tree.width * rate / 2;

		tree.forest_x1 = tree.x - ((halfWidth < 5) ? 3 : halfWidth);
		tree.forest_y1 = tree.y - tree.height * rate;
		tree.forest_x2 = tree.x + ((halfWidth < 5) ? 7 : halfWidth);
		tree.forest_y2 = tree.y;
	}
}


//
// canvas
//
interFORest.prototype.updateCanvas = function() {
	clearTimeout(this.update_canvas_timer);
	var self = this;

	var redraw_func = function() {
		var view = self.view;
		if (view == self.FOREST_VIEW) {
			self.updateForest();
		} else if (view == self.FOREST_TO_ZOOMIN) {
			self.updateForestToZoomin();
		} else if (view == self.ZOOMIN_VIEW) {
			self.updateZoomin();
		} else if (view == self.ZOOMIN_TO_FOREST) {
			self.updateZoominToForest();
		} else if (view == self.ZUKAN_VIEW) {
			self.updateZukan();
		}
		self.update_canvas_timer = setTimeout(redraw_func, self.update_canvas_timer_wait);
	}
	this.update_canvas_timer = setTimeout(redraw_func, this.update_canvas_timer_wait);
}

interFORest.prototype.onClickOnCanvas = function(e) {
	var view = this.view;
	if (view == this.FOREST_VIEW) {
		this.onClickOnForestCanvas(e);
	}
}

interFORest.prototype.onMouseMoveOnCanvas = function(e) {
	var view = this.view;
	if (view == this.FOREST_VIEW) {
		this.onMouseMoveOnForestCanvas(e);
	}
}


//
// Forest
//
interFORest.prototype.showForestView = function() {
	this.view = this.FOREST_VIEW;
	this.updateURLHash();

	$("#logo").show();
	$("#about").show();
	$(this.zoomin_layer).hide();
	$(this.forest_layer).show();

	this.setMarkedTree(null);
	$(this.forest_arrow).hide();

	//this.zawazawa();
	this.forest_is_dirty = true;

	$(this.canvas).removeClass("__zoomin");
}

// on-screen
interFORest.prototype.updateForest = function() {
	var src = this.forest_canvas, src_ctx = this.forest_context;
	var dst = this.canvas, dst_ctx = this.context;
	var width = dst.width;
	var height = dst.height;

	if (this.forest_is_dirty) {
		this.forest_is_dirty = false;
		dst_ctx.globalCompositeOperation = "copy";
		if (!CanvasUtils.isCopyOkWithTransparent)
			dst_ctx.clearRect(0, 0, width, height);
		dst_ctx.drawImage(src, 0, 0);
	}
}

interFORest.prototype.showMarkerOnForest = function() {
	var tree = this.marked_tree;
	var marker = this.forest_arrow;
	if (tree == null) {
		$(marker).hide();
		return;
	}

	var rate = 0.5;
	var width = marker.width;
	var height = marker.height;
	var x = tree.x - width / 2;
	var y = tree.forest_y1- height - 5;
	$(marker).css("top", y).css("left", x).show();
}

interFORest.prototype.onClickOnForestCanvas = function(e) {
	var x, y;

	if (this.marked_tree != null) {
		x = this.marked_tree.x;
		y = this.marked_tree.y - 12;
	} else {
		return;
		x = this.mouseX(e);
		y = this.mouseY(e);
	}

	this.forestToZoomin(x, y);
}

interFORest.prototype.onMouseMoveOnForestCanvas = function(e) {
	var prev_tree = this.marked_tree;
	var x = this.mouseX(e);
	var y = this.mouseY(e);
	var tree = this.getTreeOnForestCanvas(x, y);
	$(this.canvas).toggleClass("clickable", (tree !== null));
	this.setMarkedTree(tree);
	this.showMarkerOnForest();
}

interFORest.prototype.getTreeOnForestCanvas = function(x, y) {
	// XXX O(trees.length)
	for (var i = 0, n = this.trees.length, tree; i < n; i++) {
		tree = this.trees[i];
		if (tree == null) {
			continue;
		}
		if (tree.forest_x1 <= x  && x < tree.forest_x2 &&
		    tree.forest_y1 <= y  && y < tree.forest_y2) {
			return tree;
		}
	}
	return null;
}


//
// words
//
interFORest.prototype.reloadWords = function() {
	var self = this;
	//all words
	var callback4words = function(contents) {
		self.showWords(contents);
	}
	$.get(WEBROOT+"allwords.json", callback4words, "text");

	var waitFor = function() {
		if (self.is_update_words == false) {
			self.reloadWords();
		} else {
			setTimeout(waitFor, 1000);
		}
	}
	var func = function() {
		self.is_update_words = true;
		setTimeout(waitFor, 1000);
	}
	setTimeout(func, this.WORDS_AUTO_RELOAD_TIME);
}

interFORest.prototype.showWords = function(contents) {
	if (this.yurayurawords) {
		for (var i = 0; i < this.yurayurawords.length; i++) {
			var obj = this.yurayurawords[i];
			this.words_layer.removeChild(obj.ui);
		}
	}
	var words = JSON.parse(contents);
	this.yurayurawords = new Array();
	for (word in words) {
		var ui = document.createElement("div");
		ui.textContent = word;
		var fontsize = 8 + words[word]*2;
		if (30 < fontsize) {
			fontsize = 30;
		}
		ui.setAttribute("class", "word_label");
		ui.setAttribute("style", "font-size:"+fontsize+"px;");
		var x = 25 + Math.random() * (1024-50);
		var y = 25 + Math.random() * (768-50);
		ui.style.opacity = 0.3;
		this.words_layer.appendChild(ui);
		y -= ui.offsetHeight;
		x -= ui.offsetWidth;
		if (x < 25 ) {
			x = 25;
		}
		if (y < 25 ) {
			y = 25;
		}
		ui.style.left = x+"px";
		ui.style.top = y+"px";

		var obj = new Object();
		obj.ui = ui;
		obj.status = 4;
		obj.substatus = 0;
		obj.maxcomma = Math.random()*200;
		obj.opacity = 0.3;
		this.yurayurawords.push(obj);
	}
	this.yurayura();
}

interFORest.prototype.yurayura = function() {
	for (var i = 0; i < this.yurayurawords.length; i++) {
		var obj = this.yurayurawords[i];
		switch (obj.status) {
			case 0 : {
				var random = Math.random();
				if (random < 0.1) {
					obj.status = 1;
					obj.maxcomma = 100+random * 100;
					obj.ui.style.opacity = 0.3;
					obj.opacity = 0.3;
				}
				break;
			}
			case 1 : {
				//increase
				var next = obj.opacity + (1/obj.maxcomma);
				if (next >= 1) {
					next = 1;
					obj.status = 2;
					obj.substatus = 0;
					obj.maxcomma = 30+Math.random() * 20;
				}
				obj.opacity = next;
				obj.ui.style.opacity = next;
				break;
			}
			case 2 : {
				obj.substatus += 1;
				if (obj.substatus >= obj.maxcomma) {
					obj.status = 3;
					obj.maxcomma = 100+Math.random() * 40;
				}
				break;
			}
			case 3 : {
				var next = obj.opacity - (1/obj.maxcomma);
				if (next <= 0.3) {
					next = 0.3;
					obj.status = 4;
					obj.maxcomma = 100+Math.random() * 100;
					obj.substatus = 0;
				}
				obj.opacity = next;
				obj.ui.style.opacity = next;
				break;
			}
			case 4 : {
				obj.substatus += 1;
				if (obj.substatus >= obj.maxcomma) {
					obj.status = 0;
				}
				break;
			}
		}
	}
	if (this.is_update_words == true) {
		this.is_update_words = false;
		return;
	}
	var self = this;
	var func = function() {
		self.yurayura();
	}
	this.yurayuratimer = setTimeout(func, 100);
}


//
// zawazawa
//
interFORest.prototype.reloadZawazawa = function() {
	var self = this;
	var callback4zawazawa = function(contents) {
		self.startZawazawa(contents);
	}
	$.get(WEBROOT+"updatedtrees.json", callback4zawazawa, "text");

	//reload image too
	var millisec = (new Date()).getTime();
	var treesimage = "images/forest_trees.png?"+millisec;
	this.forest_trees.setAttribute("src", treesimage);
	this.forest_navi_trees.setAttribute("src", treesimage);

	var func = function() {
		self.reloadZawazawa();
	}
	setTimeout(func, this.ZAWAZAWA_AUTO_RELOAD_TIME);
}

interFORest.prototype.startZawazawa = function(contents) {
	this.zawazawas = JSON.parse(contents);
	this.zawazawa();
}

interFORest.prototype.zawazawa = function() {
	clearTimeout(this.zawazawatimer);
	if (this.view != this.FOREST_VIEW)
		return;

	var paths = {}
	paths[this.LEAF_COLOR_1] = [];
	paths[this.LEAF_COLOR_2] = [];
	paths[this.LEAF_COLOR_3] = [];

	var scale = this.FOREST_CANVAS_TREE_RATE;
	var shouldDrawZawazawa = false;
	for (var i = 0, n = this.zawazawas.length; i < n; i++) {
		var zawazawa = this.zawazawas[i];
		var tree = this.trees[zawazawa.id];
		if (tree == null)
			continue;

		var edges = zawazawa.edges;
		var dx = tree.x - tree.width / 2 * scale;
		var dy = tree.y - tree.height * scale;
		for (var y = 0; y < edges.length; y++) {
			var edge = edges[y];
			var sx = edge[0];
			var ex = edge[1];
			if (sx == ex) {
				continue;
			}
			var next = 1+Math.random()*300;
			for (var x = sx, color; x < ex; x+=next) {
				var random = Math.random();
				if (random < 0.9) {
					continue;
				}
				var colorrandom = (1-random)/(1-0.9);
				if (colorrandom < 0.05) {
					color = this.LEAF_COLOR_3;
				} else if (random < 0.525) {
					color = this.LEAF_COLOR_2;
				} else {
					color = this.LEAF_COLOR_1;
				}
				// x and y are must be in canvas coordinate
				paths[color].push([dx + x * scale, dy + y * scale]);
				shouldDrawZawazawa = true;
			}
		}
	}
	if (shouldDrawZawazawa) {
		this.drawZawazawa(this.forest_canvas, this.forest_context, paths);
		this.forest_is_dirty = true;
	}

	var self = this;
	var func = function() {
		self.zawazawa();
	}
	this.zawazawatimer = setTimeout(func, 250);
}

interFORest.prototype.drawZawazawa = function(canvas, ctx, paths) {
	var widthOfCanvas = canvas.width;
	var heightOfCanvas = canvas.height;
	ctx.clearRect(0, 0, widthOfCanvas, heightOfCanvas);
	ctx.save();
	for (color in paths) {
		ctx.strokeStyle = color;
		ctx.beginPath();
		var points = paths[color];
		for (var i = 0, n = points.length, point, x, y; i < n; i++) {
			point = points[i];
			x = point[0];
			y = point[1];
			ctx.moveTo(x-1, y);
			ctx.lineTo(x+1, y);
			//ctx.moveTo(x-1, y-1);
			//ctx.lineTo(x+1, y+1);
		}
		ctx.stroke();
	}
	ctx.restore();
}


//
// Drain Message
//
interFORest.prototype.reloadMessages = function() {
	var self = this;
	var callback4messages = function(contents) {
		self.startDrainMessage(contents);
	}
	$.get(WEBROOT+"updatedmessages.json", callback4messages, "text");

	var func = function() {
		self.reloadMessages();
	}
	setTimeout(func, this.MESSAGES_AUTO_RELOAD_TIME);
}

interFORest.prototype.startDrainMessage = function(contents) {
	this.messages = JSON.parse(contents);
	var self = this;
	var func = function() {
		self.drainMessage(0, self.messages.length/(self.MESSAGES_AUTO_RELOAD_TIME/1000));
	}
	this.draintimer = setTimeout(func, 1000);
}

interFORest.prototype.drainMessage = function(index, rate) {
	if (index >= this.messages.length) {
		return;
	}

	var i = index;
	if (Math.random() < rate) {
		var message = this.messages[i];
		var widthOfCanvas = this.forest_canvas.width;
		var heightOfCanvas = this.forest_canvas.height;
		var location = Math.random();
		var ex = 0;
		var ey = 0;
		var halfOfWidth = widthOfCanvas / 2;
		var halfOfHeight = heightOfCanvas / 2;
		if (location < 0.33333) {
			ex = Math.random() * halfOfWidth;
			ey = 70 + Math.random() * halfOfHeight;
		} else if (location < 0.66666) {
			ex = halfOfWidth + Math.random() * halfOfWidth;
			ey = Math.random() * halfOfHeight;
		} else {
			ex = halfOfWidth + Math.random() * halfOfWidth;
			ey = halfOfHeight + Math.random() * halfOfHeight;
		}
		//var ex = Math.random() * widthOfCanvas;
		//var ey = Math.random() * heightOfCanvas;
		var ui = document.createElement("div");
		ui.setAttribute("class", "drain_message");
		ui.textContent = message.m;
		this.message_layer.appendChild(ui);
		message.ui = ui;
		message.ui.style.opacity = 0;
		message.ui.style.fontSize = (20+Math.random()*20)+"px";
		var xOfMessage = ex - message.ui.offsetWidth / 2;
		xOfMessage = xOfMessage < 0 ? 5 : xOfMessage;
		var yOfMessage = ey - message.ui.offsetHeight / 2;
		message.ui.style.left = xOfMessage+"px";
		message.ui.style.top = yOfMessage+"px";
		this.fadeMessage(message, 0, 100);
		//this.fuwafuwaMessage(message, ex, ey, 0, 100);
		index = i+1;
	} else {
		index = i;
	}

	var self = this;
	var func = function(ii, rate) {
		self.drainMessage(ii, rate);
	}

	this.draintimer = setTimeout(func, 1000, index, rate);
}

interFORest.prototype.fadeMessage = function(message, comma, maxcomma) {
	var self = this;
	comma += 1;
	if (comma == maxcomma) {
		if (message.ui.parentNode) {
			message.ui.parentNode.removeChild(message.ui);
		}
		return;
	}
	var maxDIV3 = maxcomma/3;
	if (comma < maxDIV3) {
		//fade in
		var rate = comma / maxDIV3;
		message.ui.style.opacity = rate;
	} else if (comma > maxDIV3*2) {
		var rate = (comma-maxDIV3*2) / maxDIV3;
		message.ui.style.opacity = (1-rate);
	}
	var func = function(m, c, max) {
		self.fadeMessage(m, c, max);
	}
	setTimeout(func, 100, message, comma, maxcomma);
}
/*
interFORest.prototype.fuwafuwaMessage = function(message, ex, ey, comma, maxcomma) {
	var self = this;
	comma += 1;
	if (comma == maxcomma) {
		message.ui.parentNode.removeChild(message.ui);
		return;
	}
	var rate = comma / maxcomma;
	var currentx = message.x + (ex - message.x) * rate;
	var currenty = message.y + (ey - message.y) * rate;
	message.ui.style.left = currentx+"px";
	message.ui.style.top = currenty+"px";
	message.ui.style.opacity = (1-rate);
	var func = function(m, x, y, c, max) {
		self.fuwafuwaMessage(m, x, y, c, max);
	}
	setTimeout(func, 100, message, ex, ey, comma, maxcomma);
}
*/


//
// Forest -> Zoomin
//
interFORest.prototype.forestToZoomin = function(x, y, callback) {
	this.view = this.FOREST_TO_ZOOMIN;
	$(this.canvas).addClass("__zoomin");
	$("#about").hide();
	$("#logo").hide();

	// SLOW:
	//this.drawZoominView(x, y, false, false);

	this.forest_to_zoomin = this.forest_to_zoomin_max;
	// transition effecct
	if (!callback) {
		var self = this;
		callback = function () {
			self.showZoominView(x, y)
		}
	}
	this.transiteCallback = callback;
}

interFORest.prototype.updateForestToZoomin = function() {
	// transition effect
	var dst = this.canvas, dst_ctx = this.context;
	var width = dst.width;
	var height = dst.height;
	var navi_canvas = this.forest_navi_canvas;

	var count = --this.forest_to_zoomin;
	if (count > 0) {
		var rate = count / this.forest_to_zoomin_max;
		var w = navi_canvas.width + (width - navi_canvas.width) * rate;
		var h = navi_canvas.height + (height - navi_canvas.height) * rate;

		dst_ctx.save();
		dst_ctx.globalCompositeOperation = "source-over"
		dst_ctx.mozImageSmoothingEnabled = false;
		dst_ctx.clearRect(0, 0, width, height);
		dst_ctx.fillStyle = "rgba(255, 255, 255, 0.2)";
		dst_ctx.fillRect(0, 0, w, h);
		dst_ctx.strokeStyle = "rgb(255, 255, 255)";
		dst_ctx.beginPath();
		dst_ctx.moveTo(0, h);
		dst_ctx.lineTo(w, h);
		dst_ctx.lineTo(w, 0);
		dst_ctx.stroke();
		try {
			dst_ctx.drawImage(this.forest_ground, 0, 0, w, h);
			dst_ctx.drawImage(this.forest_trees, 0, 0, w, h);
		} catch (ex) {
		}
		dst_ctx.restore();
	} else if (count == 0) {
		this.zoomin_is_dirty = true;
		this.transiteCallback();
	}
}


//
// Zoomin
//
interFORest.prototype.showZoominView = function(x, y) {
	this.view = this.ZOOMIN_VIEW;


	$(this.zoomin_layer).show();
	$(this.forest_navi).show();
	$("#zoomin_layer_controller").show();

	$(this.forest_layer).hide();

	this.drawZoominView(x, y, false, false);
	this.updateURLHash();
}

// on screen
interFORest.prototype.updateZoomin = function() {
	var src = this.zoomin_canvas, src_ctx = this.zoomin_context;
	var dst = this.canvas, dst_ctx = this.context;
	var width = dst.width;
	var height = dst.height;

	if (this.zoomin_is_dirty) {
		this.zoomin_is_dirty = false;
		dst_ctx.globalCompositeOperation = "copy";
		// XXX commented out below for perfomance reasons
		// to move background, uncomment out below, add some codes and
		// remove (add|remove)Class("__zoomin")
		/*
		try {
			dst_ctx.drawImage(this.zoomin_background, 0, 0);
		} catch (ex) {
			// zoomin_background is proabbly not loaded
			dst_ctx.clearRect(0, 0, width, height)
		}
		dst_ctx.globalCompositeOperation = "source-over";
		*/
		if (!CanvasUtils.isCopyOkWithTransparent)
			dst_ctx.clearRect(0, 0, width, height);
		dst_ctx.drawImage(src, 0, 0);
		if (this.marked_tree != null)
			this.drawMarkerOnZoominCanvas(dst_ctx);
	}
}

interFORest.prototype.drawMarkerOnZoominCanvas = function(ctx) {
	var width = this.zoomin_layer_arrow.width;
	var height = this.zoomin_layer_arrow.height;
	var tree = this.marked_tree;

	// reset marker position
	this.marker_x1 = -1;
	this.marker_y1 = -1;
	this.makrer_x2 = -1;
	this.marker_y2 = -1;


	if (this.locateTreeOnZoominCanvas(tree)) {
		ctx.save();
		ctx.translate(tree.zoomin_x, tree.zoomin_y1);
		ctx.globalCompositeOperation = "source-over";
		var scale = (tree.zoomin_scale < 1) ? tree.zoomin_scale : 1;
		ctx.scale(scale, scale);
		try {
			ctx.drawImage(this.zoomin_layer_arrow, -width / 2,  -height * 2);
			this.marker_x1 = tree.zoomin_x - width / 2 * scale;
			this.marker_y1 = tree.zoomin_y1 - height * 2 * scale;
			this.marker_x2 = this.marker_x1 + width * scale;
			this.marker_y2 = this.marker_y1 + height * scale;
		} catch (ex) {}
		ctx.restore();
	}
}

interFORest.prototype.onClickOnZoominCanvas = function(e) {
	if (this.view != this.ZOOMIN_VIEW) {
		return;
	}

	var mx = this.mouseX(e);
	var my = this.mouseY(e);

	var selected_id = this.getTreeIdOnZoominCanvas(mx, my);
	if (selected_id < 0 || selected_id == undefined)
		return;

	this.showZukan(selected_id, false);
}

interFORest.prototype.getTreeIdOnZoominCanvas = function(x, y) {
	// first we chcek the marker
	if (this.marker_x1 <= x && x < this.marker_x2 &&
	    this.marker_y1 <= y && y < this.marker_y2) {
		return this.getTreeIdFromHash(this.marked_tree.hash);
	}

	for (var i = this.focused_trees.length-1, tree; i >= 0; i--) {
		tree = this.focused_trees[i];
		if (this.locateTreeOnZoominCanvas(tree) &&
		    tree.zoomin_x1 <= x && x < tree.zoomin_x2 &&
		    tree.zoomin_y1 <= y && y < tree.zoomin_y2) {
			return this.getTreeIdFromHash(tree.hash);
		}

		if (!tree.text_width || tree.text_width <= 0)
			continue;

		var text_x1 = tree.zoomin_x - tree.text_width / 2;
		var text_y1 = tree.zoomin_y1 - tree.text_height;
		var text_x2 = tree.zoomin_x + tree.text_width / 2;
		var text_y2 = tree.zoomin_y1;
		if (text_x1 <= x && x < text_x2 && text_y1 <= y && y < text_y2) {
			return this.getTreeIdFromHash(tree.hash);
		}
	}
	return -1;
}

interFORest.prototype.onMouseMoveOnZoomin = function(e) {
	var index = this.getTreeIdOnZoominCanvas(this.mouseX(e), this.mouseY(e));
	if (index < 0 || index == undefined) {
		$(this.zoomin_layer).
			attr("title", "").
			removeClass("clickable");
	} else {
		var tree = this.trees[index];
		$(this.zoomin_layer).
			attr("title", tree.title).
			addClass("clickable");
	}
}

// off screen
interFORest.prototype.drawZoominView = function(x, y, use_cached_trees, use_cached_screen) {
	// For map, this.selected_##
	//
	//    x1        x2
	// y1 +---------+
	//    |         |
	//    |   x,y   |
	//    |         |
	// y2 +---------+
	//
	x = Math.min(Math.max(Math.round(x), this.map_min_x), this.map_max_x);
	y = Math.min(Math.max(Math.round(y), this.map_min_y), this.map_max_y);
	var x1 = x - 25;
	var y1 = y - 25;
	var x2 = x + 25;
	var y2 = y + 25;

	if (this.selected_x1 == x1 && this.selected_y1 == y1 &&
	    this.selected_x2 == x2 && this.selected_y2 == y2)
		return;


	if (!use_cached_screen || !this.drawCachedZoominView(x1, y1, x2, y2))
		this.clearZoominView();

	// update map area
	this.selected_x = x;
	this.selected_y = y;
	this.selected_x1 = x1;
	this.selected_y1 = y1;
	this.selected_x2 = x2;
	this.selected_y2 = y2;

	this.drawTreesOnZoominView(!!use_cached_trees, !!use_cached_screen);
	this.updateForestNavi();
}

interFORest.prototype.clearZoominView = function() {
	var canvas = this.zoomin_canvas;
	var ctx = this.zoomin_context;
	ctx.clearRect(0, 0, canvas.width, canvas.height);
	this.drawn_trees = {};
	this.zoomin_is_dirty = true;
}

interFORest.prototype.drawTreesOnZoominView =  function(use_cached_trees, use_cached_screen) {
	if (!!use_cached_trees) {
		for (var i = 0, n = this.focused_trees.length, tree; i < n; i++) {
			tree = this.focused_trees[i];
			if (!!use_cached_screen && (tree.hash in this.drawn_trees))
				continue;
			this.drawTreeOnZoominView(tree);
		}
		// ensure marked tree is drawn
		if (this.marked_tree != null) {
			var tree = this.marked_tree;
			if (!use_cached_screen || !(tree.hash in this.drawn_trees))
				this.drawTreeOnZoominView(tree);
		}
		return;
	}

	var x1 = this.selected_x1, y1 = this.selected_y1;
	var x2 = this.selected_x2, y2 = this.selected_y2;
	var focused_trees = [];
	// XXX O(trees.length)
	for (var i = 0, n = this.trees.length, tree, x, y; i < n; i++) {
		tree = this.trees[i];
		if (tree == null) {
			continue;
		}
		x = tree.x;
		y = tree.y;
		if (x1 <= x && x < x2 && y1 <= y && y < y2) {
			focused_trees.push(tree);
		}
	}

	var self = this;
	loop(focused_trees.length, function(i) {
		var tree = focused_trees[i];
		if (!(tree.hash in self.drawn_trees)) {
			if (!tree.img) {
				self.drawn_trees[tree.hash] = true;
				var img = new Image();
				img.onload = function() {
					self.drawTreeOnZoominView(tree);
				}
				img.onerror = function() {
					delete tree.img;
				}
				img.src = self.getTreeImageURL(tree);
				tree.img = img;
			} else if (tree.img.width > 0) {
				self.drawn_trees[tree.hash] = true;
				self.drawTreeOnZoominView(tree);
			}
		}
	});

	this.focused_trees = focused_trees;
}

interFORest.prototype.drawTreeOnZoominView = function(tree) {
	tree.text_width = -1;
	tree.text_height = -1	;

	if (!this.locateTreeOnZoominCanvas(tree)) {
		delete this.drawn_trees[tree.hash];
		return;
	}

	if (tree.zoomin_width <= 0 || tree.zoomin_height <= 0) {
		delete this.drawn_trees[tree.hash];
		return;
	}

	var img = tree.img;
	var ctx = this.zoomin_context;
	ctx.save();
	ctx.globalAlpha = tree.zoomin_opacity;
	try {
		ctx.drawImage(img, tree.zoomin_x1, tree.zoomin_y1, tree.zoomin_width, tree.zoomin_height);
	} catch (ex) {
		delete this.drawn_trees[tree.hash];
		ctx.restore();
		return;
	}
	ctx.globalAlpha = 1;
	ctx.fillStyle = this.ZOOMIN_TREE_TITLE_COLOR;
	ctx.shadowColor = this.ZOOMIN_TREE_SHADOW_COLOR;
	ctx.shadowBlur = 3;
	ctx.shadowOffsetX = 0;
	ctx.shadowOffsetY = 0;
	ctx.font = this.CANVAS_FONT;
	ctx.textAlign = "center";

	// from jquery.text-overflow.js
	var title = tree.title;
	var shortTitle = title;
	while (title.length > 0 && ctx.measureText(shortTitle).width > 216 /* width of #zukan_labels */) {
		title = title.substr(0, title.length - 1);
		shortTitle = title + "...";
	}


	ctx.fillText(shortTitle, tree.zoomin_x, tree.zoomin_y1);
	tree.text_width = ctx.measureText(shortTitle).width;
	tree.text_height = this.CANVAS_FONT_HEIGHT;
	ctx.restore();
	this.zoomin_is_dirty = true;

	this.drawn_trees[tree.hash] = true;
}

interFORest.prototype.locateTreeOnZoominCanvas = function(tree) {
	if (tree == null)
		return false;
	if (!tree.img || tree.img.width <= 0)
		return false;

	var x = tree.x, y = tree.y;
	var x1 = this.selected_x1, y1 = this.selected_y1;
	var x2 = this.selected_x2, y2 = this.selected_y2;
	if (x < x1 || x2 <= x || y < y1 || y2 <= y)
		return false;

	var x = tree.x;
	var y = tree.y;
	var x1 = this.selected_x1, y1 = this.selected_y1;
	var x2 = this.selected_x2, y2 = this.selected_y2;

	var CANVAS_WIDTH = this.canvas_width;
	var CANVAS_HEIGHT = this.canvas_height;
	var TREE_AREA = 50;
	var GROUND_Y = CANVAS_HEIGHT / 4;
	var TREE_Y_PER_PIXEL = (CANVAS_HEIGHT - GROUND_Y) / TREE_AREA;
	var TREE_FAR_RATE = 0.3;
	var TREE_Y_RATE = (TREE_FAR_RATE)/TREE_AREA;
	var TREE_Y_RATE = (1)/TREE_AREA;
	var TREE_SX = 20;
	var TREE_X_PER_PIXEL = (CANVAS_WIDTH - TREE_SX*2) / TREE_AREA;

	var zoomin_x = TREE_SX + (x - x1) * TREE_X_PER_PIXEL;
	var zoomin_y = GROUND_Y + (y - y1) * TREE_Y_PER_PIXEL;

	if (zoomin_x < 0 || CANVAS_WIDTH <= zoomin_x ||
	    zoomin_y < 250 || CANVAS_HEIGHT <= zoomin_y)
		return false;

	var scale = TREE_FAR_RATE + (y - y1) * TREE_Y_RATE;
	var opacity = 0.3 + scale;
	var img = tree.img;
	var zoomin_w = img.width * scale;
	var zoomin_h = img.height * scale;

	// For zoomin_canvas (with no transform!), tree.zoomin_##
	//
	//    x1        x2
	// y1 +---------+
	//    |    ^    |
	//    |   ^|^   |
	//    |    |    |
	// y2 +---------+
	//        x,y
	//
	tree.zoomin_x = zoomin_x;
	tree.zoomin_y = zoomin_y;
	tree.zoomin_x1 = zoomin_x - zoomin_w / 2;
	tree.zoomin_y1 = zoomin_y - zoomin_h;
	tree.zoomin_x2 = zoomin_x + zoomin_w / 2;
	tree.zoomin_y2 = zoomin_y;
	tree.zoomin_width = zoomin_w;
	tree.zoomin_height = zoomin_h;

	tree.zoomin_scale = scale;
	tree.zoomin_opacity = (opacity > 1) ? 1 : opacity;
	return true;
}

interFORest.prototype.moveToTreeZoominView = function(tree) {
	this.moveZoominView(tree.x - this.selected_x, tree.y - this.selected_y);
}

interFORest.prototype.moveZoominView = function(move_x, move_y) {
	clearTimeout(this.move_timer);
	var move_id = ++this.move_id;
	var n = 10;
	var dx = move_x / n;
	var dy = move_y / n;
	var x = this.selected_x;
	var y = this.selected_y;
	var self = this;
	var move_func = function (i) {
		if (self.move_id != move_id) {
			return;
		}

		var next_x = x + dx * i;
		var next_y = y + dy * i;
		self.drawZoominView(next_x, next_y, i != n, i == n);
		if (i < n)
			self.move_timer = setTimeout(move_func, self.move_timer_wait, ++i);
		else
			self.updateURLHash();

	}
	this.move_timer = setTimeout(move_func, this.move_timer_wait, 1);
}

interFORest.prototype.drawCachedZoominView = function(x1, y1, x2, y2) {
	if (!CanvasUtils.isCopyOkWithTransparent)
		return false;

	var TREE_AREA = 50;
	var CANVAS_WIDTH = this.canvas_width;
	var prev_x1 = this.selected_x1, prev_y1 = this.selected_y1;
	var prev_x2 = this.selected_x2, prev_y2 = this.selected_y2;
	if (prev_y1 == y1 && prev_y2 == y2) {
		var canvas = this.zoomin_canvas;
		var ctx = this.zoomin_context;
		// <- move left
		// x1   prev_x1   x2   prev_x2
		if (prev_x1 < x2 && x2 < prev_x2) {
			/*
			this.scan_x1 = x1;
			this.scan_y1 = y1;
			this.scan_x2 = prev_x1;
			this.scan_y2 = y2;
			*/
			var dx = (prev_x1 - x1) / TREE_AREA * CANVAS_WIDTH;
			var dy = 0;
			this._drawCachedScreen(canvas, ctx, dx, dy);
			this.zoomin_is_dirty = true;
			return true;
		}

		// move right ->
		// prev_x1 x1 prev_x2 x2
		if (prev_x1 < x1 && x1 < prev_x2) {
			/*
			this.scan_x1 = prev_x2;
			this.scan_y1 = y1;
			this.scan_x2 = x2;
			this.scan_y2 = y2;
			*/
			var dx = -(x2 - prev_x2) / TREE_AREA * CANVAS_WIDTH;
			var dy = 0;
			this._drawCachedScreen(canvas, ctx, dx, dy);
			this.zoomin_is_dirty = true;
			return true;
		}
	}

	return false;
}

interFORest.prototype._drawCachedScreen = function(canvas, ctx, dx, dy) {
	var width = canvas.width;
	var height = canvas.height;
	var clear_x1 = 0;
	var clear_x2 = dx;
	if (dx < 0) {
		// clear right of canvas
		clear_x1 = width + dx;
		clear_x2 = -dx;
	}

	ctx.save();
	ctx.globalAlpha = 1;
	ctx.globalCompositeOperation = "copy";
	ctx.drawImage(canvas, dx, dy);
	ctx.clearRect(clear_x1, 0, clear_x2, height);
	ctx.restore();
}


interFORest.prototype.setMarkedTree = function(tree) {
	if (tree != null) {
		this.marked_tree = tree;
		this.zoomin_is_dirty = true;
	} else {
		this.marked_tree = null;
	}
}


//
// Forest Navi
//
interFORest.prototype.updateForestNavi = function() {
	var CANVAS_WIDTH = this.forest_navi_canvas.width;
	var CANVAS_HEIGHT = this.forest_navi_canvas.height;
	var SCALE_X = CANVAS_WIDTH / this.map_max_x;
	var SCALE_Y = CANVAS_HEIGHT / this.map_max_y;
	var x = this.selected_x1 * SCALE_X;
	var y = this.selected_y1 * SCALE_Y;
	var width = 50 * SCALE_X;
	var height = 50 * SCALE_Y;
	var ctx = this.forest_navi_context;

	ctx.save();
	ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
	ctx.fillStyle = "rgba(255, 255, 255, 0.2)";
	ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
	ctx.strokeStyle = "white";
	ctx.strokeRect(-1, -1, CANVAS_WIDTH, CANVAS_HEIGHT)
	ctx.fillStyle =  this.NAVI_AREA_COLOR;
	ctx.fillRect(x, y, width, height);
	ctx.restore();
	/*
	if (0 <= this.bookmark_index && this.bookmark_index != this.showing_index) {
		ctx.fillStyle = this.NAVI_BOOKMARK_COLOR;
		var bx = (this.bookmark_x - 10)  * SCALE_X;
		var by = (this.bookmark_y - 10)  * SCALE_Y;
		var bw = 20  * SCALE_Y;
		var bh = 20 * SCALE_Y;
		ctx.fillRect(bx, by, bw, bh);
	}
	*/
}

interFORest.prototype.onClickOnForestNavi = function(e) {
	this.zoominToForest();
}


//
// Zoomin -> Forest
//
interFORest.prototype.zoominToForest = function() {
	// transition effect
	this.zoomin_to_forest = this.zoomin_to_forest_max;
	this.view = this.ZOOMIN_TO_FOREST;
}

interFORest.prototype.updateZoominToForest = function() {
	$(this.forest_navi).hide();
	$("#zoomin_layer_controller").hide();

	var dst = this.canvas, dst_ctx = this.context;
	var width = dst.width;
	var height = dst.height;
	var navi_canvas = this.forest_navi_canvas;
	// transition effect
	var count = --this.zoomin_to_forest;
	if (count > 0) {
		var rate = 1 - count / this.zoomin_to_forest_max;
		var w = navi_canvas.width + (width - navi_canvas.width) * rate;
		var h = navi_canvas.height + (height - navi_canvas.height) * rate;

		dst_ctx.save();
		dst_ctx.globalCompositeOperation = "source-over";
		dst_ctx.mozImageSmoothingEnabled = false;
		dst_ctx.clearRect(0, 0, w, h);
		/*
		try {
			dst_ctx.drawImage(this.zoomin_background, 0, 0, w, h, 0, 0, w, h);
		} catch (ex) {
		}
		*/
		dst_ctx.fillStyle = "rgba(255, 255, 255, 0.2)";
		dst_ctx.fillRect(0, 0, w, h);
		dst_ctx.strokeStyle = "rgb(255, 255, 255)";
		dst_ctx.beginPath();
		dst_ctx.moveTo(0, h);
		dst_ctx.lineTo(w, h);
		dst_ctx.lineTo(w, 0);
		dst_ctx.stroke();
		try {
			dst_ctx.drawImage(this.forest_ground, 0, 0, w, h);
			dst_ctx.drawImage(this.forest_trees, 0, 0, w, h);
		} catch (ex) {
		}
		dst_ctx.restore();
	} else if (count == 0) {
		this.showForestView();
	}
}


//
// Zukan
//
interFORest.prototype.showZukan = function (index, doMoveToTree) {
	this.view = this.ZUKAN_VIEW;
	this.showTree(index, !!doMoveToTree);

	var clientWidth = this.zoomin_canvas.width;
	var clientHeight = this.zoomin_canvas.height;
	var zukanWidth = $(this.zukan_board).width();
	var zukanHeight = $(this.zukan_board).height();
	$(this.zukan_board).css({
		"left": (clientWidth-zukanWidth)/2,
		"top": (clientHeight-zukanHeight)/2,
		"opacity": 1
	});
	$(this.black_mask).show();
	$(this.zukan_board).show("normal", function () {
		// ellipsis() must be called after show()
		$(".zukan_desc_label").ellipsis();
	});

	var tree = this.trees[index];
	this.bookmark_index = index;
	this.bookmark_x = tree.x;
	this.bookmark_y = tree.y;
	/*
	var bookmarktext = "";
	for (var i = 0, n = tree.title.length; i < n; i++) {
		bookmarktext += tree.title.charAt(i);
		bookmarktext += "\n";
	}
	this.zukan_bookmark_text.textContent = bookmarktext;
	*/
}

interFORest.prototype.updateZukan = function() {
	this.updateZoomin();
}

interFORest.prototype.showTree = function(index, doMoveToTree) {
	var tree = this.trees[index];
	this.setMarkedTree(tree);
	this.showing_index = index;
	//this.showBookmark();
	this.updateURLHash();
	this.showTreeDescription(index);
	this.showTreeImage(index);
	if (!!doMoveToTree)
		this.moveToTreeZoominView(tree);

	// sometimes this.trees[index] is null
	var prev_index = index - 1;
	while (this.trees[prev_index] == null) {
		prev_index = (0 < prev_index) ? --prev_index : this.trees.length - 1
	}
	this.zukan_prev_index = prev_index;

	var next_index = index + 1;
	while (this.trees[next_index] == null) {
		next_index = (next_index < this.trees.length - 1) ? ++next_index : 0;
	}
	this.zukan_next_index = next_index;


	this.loadImages(this.zukan_prev_index);
	this.loadImages(this.zukan_next_index);
}

/*
interFORest.prototype.showBookmark = function() {
	if (this.showing_index == this.bookmark_index) {
		this.zukan_bookmark.style.display = "none";
	} else {
		if (this.showing_index > this.bookmark_index) {
			this.zukan_bookmark.style.left = "-1em";
			this.zukan_bookmark.style.right = "";
		} else {
			this.zukan_bookmark.style.left = "";
			this.zukan_bookmark.style.right = "-1em";
		}
		this.zukan_bookmark.style.display = "block";
	}
}
*/

interFORest.prototype.showTreeDescription = function(index) {
	if (this.zukan_last_showing_description == index)
		return;
	this.zukan_last_showing_description = index;

	buildTreeDescription(this.zukan_labels, this.trees[index], index, this.trees.length);
}


interFORest.prototype.isValidPageNumber = function(number) {
	if (!(/^\d+$/.test(number))) {
		return false;
	}

	number = parseInt(number, 10);
	if (isNaN(number) || !isFinite(number)) {
		return false;
	}
	var id = --number;
	if (id < 0 || this.trees.length <= id || this.trees[id] == null) {
		return false;
	}
	return true;
}


interFORest.prototype.showTreeImage = function(index) {
	if (this.zukan_last_showing_image == index)
		return;
	this.zukan_last_showing_image = index;

	var tree = this.trees[index];
	this.showTreeImageInternal(tree, true);

	if (!tree.rootimg || tree.rootimg.width <= 0) {
		if (!tree.rootimg) {
			var rootimg = new Image();
			rootimg.onerror = function() {
				delete tree.rootimg;
			}
			tree.rootimg = rootimg;
		}
		var self = this;
		tree.rootimg.onload = function() {
			self.showTreeImageInternal(tree, true);
		}
		tree.rootimg.src = this.getRootImageURL(tree);
	}

	if (!tree.img || tree.img.width <= 0) {
		if (!tree.img) {
			var img = new Image();
			img.onerror = function() {
				delete tree.img;
			}
			tree.img = img;
		}
		var self = this;
		tree.img.onload = function() {
			self.showTreeImageInternal(tree, true);
		}
		tree.img.src = this.getTreeImageURL(tree);
	}
}

interFORest.prototype.showTreeImageInternal = function(tree, doClear) {
	var CANVAS_WIDTH = this.zukan_canvas.width;
	var CANVAS_HEIGHT = this.zukan_canvas.height;
	var TREE_CENTER_X = CANVAS_WIDTH/2;
	var TREE_CENTER_Y = CANVAS_HEIGHT/2 + .5;
	var rate = 1;
	var scale  = 1;

	var ctx = this.zukan_context;
	if (!!doClear)
		ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
	ctx.beginPath();
	ctx.moveTo(0, TREE_CENTER_Y);
	ctx.lineTo(CANVAS_WIDTH, TREE_CENTER_Y);
	ctx.strokeStyle = "black";
	ctx.stroke();

	if (!tree.img || tree.img.width <= 0)
		return;

	var img = tree.img;
	var imgw = img.width * rate;
	var imgh = img.height * rate;

	if (imgh > CANVAS_HEIGHT / 2) {
		scale = CANVAS_HEIGHT / 2 / imgh;
		imgw *= scale;
		imgh *= scale;
	}

	var imgx = TREE_CENTER_X - imgw / 2;
	var imgy = TREE_CENTER_Y - imgh;
	ctx.drawImage(img, imgx, imgy, imgw, imgh);

	if (!tree.rootimg || tree.rootimg.width <= 0)
		return;

	var rootimg = tree.rootimg;
	var rootimgw = rootimg.width * rate * scale;
	var rootimgh = rootimg.height * rate * scale;
	var rootimgx = TREE_CENTER_X - rootimgw / 2;
	var rootimgy = TREE_CENTER_Y;
	ctx.drawImage(rootimg, rootimgx, rootimgy, rootimgw, rootimgh);
}

interFORest.prototype.onClickOnZukanBookmark = function(e) {
	if (0 <= this.bookmark_index && this.showing_index != this.bookmark_index) {
		var tree = this.trees[this.bookmark_index];
		this.moveZoominView(tree.x - this.selected_x, tree.y - this.selected_y);
		this.showTree(this.bookmark_index);
	}
}

interFORest.prototype.hideZukan = function(e) {
	if (this.view != this.ZUKAN_VIEW)
		return;

	this.flip_state = this.FLIP_END;

	var self = this;
	$(this.zukan_board).hide("normal", function() {
		self.bookmark_index = -1;
		self.updateForestNavi(self.selected_x, self.selected_y);
		self.view = self.ZOOMIN_VIEW;
		self.updateURLHash();
	})
	$(this.black_mask).fadeOut();
}

interFORest.prototype.loadImages = function(index) {
	var tree = this.trees[index];
	if (!tree.rootimg) {
		var rootimg = new Image();
		rootimg.onerror = function() {
			delete tree.rootimg;
		}
		rootimg.src = this.getRootImageURL(tree);
		tree.rootimg = rootimg;
	}

	if (!tree.img) {
		var img = new Image();
		img.onerror = function() {
			delete tree.img;
		}
		img.src = this.getTreeImageURL(tree);
		tree.img = img;
	}
}


//
// Zukan page flipping
//
interFORest.prototype.onMouseDownOnZukanLayer = function(e) {
	if (!this.isFlippableElement(e))
		return;
	if (e.button != 0)
		return;
	if (this.flip_state != this.FLIP_END && this.flip_state != this.FLIP_WAIT)
		return;

	var x = this.flipMouseX(e);
	this.flip_start = this.flipGetSide(x);

	// double click
	if (e.detail == 2) {
		if (this.flip_start == this.FLIP_RIGHT) {
			this.flipShowNextPage($(this.zukan_layer).width());
		} else {
			this.flipShowPrevPage(0);
		}
		return;
	}

	if (e.detail != 1)
		return;
	// ensure this is single click
	this.flip_state = this.FLIP_WAIT;
	//this.flipUpdate(x);

	var self = this;
	wait(0.2).next(function() {
		if (self.flip_state == self.FLIP_WAIT) {
			self.flip_state = self.FLIP_START;
			$(self.zukan_layer).addClass("dragging");
			self.flipUpdate(x);
		} else if (self.flip_state == self.FLIP_END) {
			self.flipEnd(false);
		}
	});
	//this.zukan_mekuri_r.style.display = "none";
	//this.zukan_mekuri_l.style.display = "none";
}

interFORest.prototype.onMouseUpOnZukanLayer = function(e) {
	if (!this.isFlippableElement(e))
		return;

	if (e.button != 0)
		return;
	if (this.flip_state == this.FLIP_WAIT) {
		this.flipEnd(false);
		return;
	}
	if (this.flip_state != this.FLIP_START)
		return;

	var x = this.flipMouseX(e);
	var end = this.flipGetSide(x);

	if (this.flip_start == this.FLIP_RIGHT && end == this.FLIP_LEFT) {
		this.flipShowNextPage(x);
	} else if (this.flip_start == this.FLIP_LEFT && end == this.FLIP_RIGHT) {
		this.flipShowPrevPage(x);
	} else {
		this.flipCancel(x);
	}
}

interFORest.prototype.onMouseMoveOnZukanLayer = function(e) {
	if (this.flip_state == this.FLIP_END) {
		clearTimeout(this.mekuri_r_timer);
		clearTimeout(this.mekuri_l_timer);

		var x = this.flipMouseX(e);
		var pos = this.flipGetSide(x);
		if (pos == this.FLIP_RIGHT) {
			$("#zukan_canvas").removeClass("mekuri_l");

			var self = this;
			this.mekuri_r_timer = setTimeout(function() {
				if (self.flip_state == self.FLIP_END)
					$("#zukan_labels").addClass("mekuri_r");
			}, 500);
		} else {
			$("#zukan_labels").removeClass("mekuri_r");

			var self = this;
			this.mekuri_l_timer = setTimeout(function() {
				if (self.flip_state == self.FLIP_END)
					$("#zukan_canvas").addClass("mekuri_l");
			}, 500);
		}
	} else if (this.flip_state == this.FLIP_START) {
		this.flipUpdate(this.flipMouseX(e));
	}
}

interFORest.prototype.onMouseOutOnZukanLayer = function(e) {
	var elem = e.relatedTarget;
	while (elem) {
		if (elem == this.zukan_close_button)
			break;
		if (elem == this.zukan_layer)
			return;
		try {
			elem = elem.parentNode;
		} catch (ex) {
			return
		}
	}

	clearTimeout(this.mekuri_r_timer);
	clearTimeout(this.mekuri_l_timer);
	if (this.flip_state == this.FLIP_START) {
		this.flipCancel(this.flipLastX);
	} else {
		$(this.zukan_board).css("opacity", 0.2);
		this.flipEnd(false);
	}
}


interFORest.prototype.flipUpdate = function(x) {
	this.flipLastX = x;
	var curr = this.showing_index;
	var next = this.zukan_next_index;
	var prev = this.zukan_prev_index;
	var pos = this.flipGetSide(x);
	var div = document.getElementById("zukan_labels2");

	if (this.flip_start == this.FLIP_RIGHT) {
		if (pos == this.FLIP_RIGHT) {
			this.showTreeImage(curr);
			this.flipDesc(curr, x);
			this.showTreeDescription(next);
		} else {
			if (div) {
			$("#zukan_labels2").hide();
			$(".mask").hide();
			}
			this.showTreeImage(curr);
			this.flipImage(next, x);
			this.showTreeDescription(next);
		}
	} else {
		if (pos == this.FLIP_LEFT) {
			if (div) {
			$("#zukan_labels2").hide();
			$(".mask").hide();
			}
			this.showTreeImage(prev);
			this.flipImage(curr, x);
			this.showTreeDescription(curr);
		} else {
			this.showTreeImage(prev);
			this.flipDesc(prev, x);
			this.showTreeDescription(curr);
		}
	}
}

interFORest.prototype.flipShowNextPage = function(x) {
	this.flip_state = this.FLIP_ANIM;
	this.flipUpdate(x);

	if (0 <= x) {
		var self = this;
		var flip_next_page = function(x) {
			self.flipShowNextPage(x);
		}
		setTimeout(flip_next_page, 50, x - 25);
	} else {
		this.showing_index = this.zukan_next_index;
		this.flipEnd(true);
	}
}

interFORest.prototype.flipShowPrevPage = function(x) {
	this.flip_state = this.FLIP_ANIM;
	this.flipUpdate(x);

	if (x < $(this.zukan_layer).width()) {
		var self = this;
		var flip_prev_page  = function(x) {
			self.flipShowPrevPage(x);
		}
		setTimeout(flip_prev_page, 50, x + 25);
	} else {
		this.showing_index = this.zukan_prev_index;
		this.flipEnd(true);
	}
}

interFORest.prototype.flipCancel = function(x) {
	this.flip_state = this.FLIP_ANIM;
	this.flipUpdate(x);
	$(this.zukan_layer).removeClass("dragging");

	if (0 <= x && x < $(this.zukan_layer).width()) {
		x += (this.flip_start == this.FLIP_LEFT) ? -25 : 25;
		var self = this;
		var flip_cancel  = function(x) {
			self.flipCancel(x);
		}
		setTimeout(flip_cancel, 50, x);
	} else {
		this.flipEnd(false);
	}
}

interFORest.prototype.flipEnd = function(doMoveToTree) {
	this.flip_state = this.FLIP_END;
	$(this.zukan_layer).removeClass("dragging");
	$("#zukan_labels").removeClass("mekuri_r");
	$("#zukan_canvas").removeClass("mekuri_l");
	var div = document.getElementById("zukan_labels2");
	if (div) {
	$(".mask").hide();
	$("#zukan_labels2").hide();
	}
	this.showTree(this.showing_index, !!doMoveToTree);
}

interFORest.prototype.flipGetSide = function(x) {
	var center_x = $(this.zukan_layer).width() / 2;
	return (x < center_x) ? this.FLIP_LEFT : this.FLIP_RIGHT;
}

interFORest.prototype.flipMouseX = function(e) {
	var x = this.mouseX(e);
	var elem = e.target;
	while (elem) {
		if (elem == this.zukan_layer)
			return x;

		if (elem.id == "zukan_labels2") {
			x *= this.flipDescScale;
		}
		x += elem.offsetLeft;
		elem = elem.offsetParent;
	}
	return -1;
}

interFORest.prototype.flipMouseY = function(e) {
	var y = this.mouseY(e);
	var elem = e.target;
	while (elem) {
		if (elem == this.zukan_layer)
			return y;
		y += elem.offsetTop;
		elem = elem.offsetParent;
	}
	return -1;
}

interFORest.prototype.flipImage = function(index, x) {
	$("#zukan_canvas").removeClass("mekuri_l");
	this.zukan_last_showing_image = -1;

	var CANVAS_WIDTH = this.zukan_canvas.width;
	var tree = this.trees[index];
	var ctx = this.zukan_context;
	ctx.save();
	ctx.translate(x, 0);
	ctx.scale((CANVAS_WIDTH - x) / CANVAS_WIDTH, 1);
	try {
		ctx.mozImageSmoothingEnabled = false;
		ctx.drawImage(this.zukan_page_background_l, 0, -100);
	} catch (ex) {}
	this.showTreeImageInternal(tree, false);
	ctx.restore();
}

interFORest.prototype.flipDesc = function(index, x) {
	$("#zukan_labels").removeClass("mekuri_r");
	var width = $(this.zukan_layer).width() / 2;
	var height = $(this.zukan_layer).height();
	var scale = (x - width) / width;
	scale = Math.max(Math.min(scale, 1), 0);

	var div = document.getElementById("zukan_labels2");
	if ("MozTransform" in document.documentElement.style ||
		"WebkitTransform" in document.documentElement.style) {
		div = this.flipDescCSSTransform(index, div, scale);
	} else {
		var canvas;
		if (div == null) {
			div = document.createElement("div");
			div.setAttribute("id", "zukan_labels2");
			div.setAttribute("class", "canvas");
			canvas = document.createElement("canvas");
			canvas.width = width;
			canvas.height = height;
			div.appendChild(canvas);
			this.zukan_layer.appendChild(div);
		}
		canvas = $(div).children("canvas").get(0);
		var ctx = canvas.getContext("2d");
		ctx.clearRect(0, 0, width, height);
		ctx.save();
		ctx.scale(scale, 1);
		try {
			ctx.drawImage(this.zukan_page_background_r, 0, -100);
		} catch (ex) {}
		ctx.restore();
	}

	$("#zukan_labels2").show();
}
interFORest.prototype.flipDescCSSTransform = function(index, div, scale) {
	if (div == null) {
		div = this.zukan_labels.cloneNode(true);
		div.setAttribute("id", "zukan_labels2");
		this.zukan_layer.appendChild(div);
		var mask = document.createElement("div");
		mask.setAttribute("class", "mask");
		this.zukan_layer.appendChild(mask);
	}

	if (this.zukan_last_flip_description != index)
		buildTreeDescription(div, this.trees[index], index, this.trees.length);
	this.zukan_last_flip_description = index;

	this.flipDescScale = scale;
	if ("MozTransform" in div.style) {
		div.style.MozTransformOrigin = "0 0";
		div.style.MozTransform = "scale(" + scale + ", 1)";
	} else if ("WebkitTransform" in div.style) {
		div.style.WebkitTransformOrigin = "0 0";
		div.style.WebkitTransform = "scale(" + scale + ", 1)";
	}
	$(".mask").show();
	$(div).addClass("mekuri_r");
	return div;
}

interFORest.prototype.isFlippableElement = function(e) {
	for (var target = e.target; target != null; target = target.parentNode) {
		if (target.id == "zukan_layer")
			return true;

		if (target.tagName.toLowerCase() == "a")
			return false;
		if (target.id == "zukan_close_button")
			return false;
		if (target.className == "_zukan_page_label") {
			return false;
		}
	}
	return true;
}


//
// URL Hash
//
interFORest.prototype.updateURLHash = function() {
	var view = this.view;
	if (view.charAt(0) == "_")
		return;

	var hashes = [view];

	if (view == this.ZOOMIN_VIEW) {
		hashes.push(this.selected_x);
		hashes.push(this.selected_y);
	} else if (view == this.ZUKAN_VIEW) {
		hashes.push(this.showing_index + 1);
	}

	window.location.hash = hashes.join("/");
	mj.analytics.log("/" + hashes.join("/"));
}

interFORest.prototype.parseURLHash = function() {
	var hashes = window.location.hash.substring(1).split("/");
	var view = hashes[0];
	if (view == this.FOREST_VIEW) {
	} else if (view == this.ZOOMIN_VIEW) {
		if (hashes.length < 3)
			return;

		var x = parseInt(hashes[1], 10);
		var y = parseInt(hashes[2], 10);
		if (!isNaN(x) && !isNaN(y)) {
			this.forestToZoomin(x, y);
		}
	} else if (view == this.ZUKAN_VIEW) {
		if (hashes.length < 2)
			return;

		var index = parseInt(hashes[1], 10);
		if (!isNaN(index)) {
			var tree = this.trees[--index];
			if (tree != null) {
				var x = tree.x;
				var y = tree.y;
				var self = this;
				var callback = function () {
					self.showZoominView(x, y);
					self.showZukan(index, true);
				}
				this.forestToZoomin(x, y, callback);
			}
		}
	}
}


//
// URLs
//
interFORest.prototype.getTreeImageURL = function(tree) {
	return WEBROOT+"images/trees/" + tree.hash + ".png";
}

interFORest.prototype.getRootImageURL = function(tree) {
	return WEBROOT+"images/trees/" + tree.hash + "_root.png";
}


//
// Keyboard Navigation
//
interFORest.prototype.onKeyDownOnWindow = function(e) {
	var kbdnav_fired = false;
	var MOVE_AMOUNT = 20;

	switch (this.view) {
		case this.FOREST_VIEW:
			switch (e.keyCode) {
				case e.DOM_VK_ADD:
				case e.DOM_VK_RETURN:
				case e.DOM_VK_ENTER:
					// Show No. 1 tree (Shiretoko Foundation)
					this.forestToZoomin(275, 505); kbdnav_fired = true; break;
			}
			break;
		case this.ZOOMIN_VIEW:
			switch (e.keyCode) {
				case e.DOM_VK_UP:
				case e.DOM_VK_PAGE_UP:
					this.moveZoominView(0, -MOVE_AMOUNT); kbdnav_fired = true; break;
				case e.DOM_VK_DOWN:
				case e.DOM_VK_PAGE_DOWN:
					this.moveZoominView(0, MOVE_AMOUNT); kbdnav_fired = true; break;
				case e.DOM_VK_RIGHT:
				case e.DOM_VK_END:
					this.moveZoominView(MOVE_AMOUNT, 0); kbdnav_fired = true; break;
				case e.DOM_VK_LEFT:
				case e.DOM_VK_HOME:
					this.moveZoominView(-MOVE_AMOUNT, 0); kbdnav_fired = true; break;
				case e.DOM_VK_SUBTRACT:
				case e.DOM_VK_ESCAPE:
					this.zoominToForest(); kbdnav_fired = true; break;
			}
			break;
		case this.ZUKAN_VIEW:
			switch (e.keyCode) {
				case e.DOM_VK_RIGHT:
					this.flipShowNextPage($(this.zukan_layer).width()); kbdnav_fired = true; break;
				case e.DOM_VK_LEFT:
					this.flipShowPrevPage(0); kbdnav_fired = true; break;
				case e.DOM_VK_SUBTRACT:
				case e.DOM_VK_ESCAPE:
					this.hideZukan(); kbdnav_fired = true; break;
			}
			break;
	}

	if (kbdnav_fired) {
		e.preventDefault();
		e.stopPropagation();
		return false;
	}

	return true;
}


//
// misc.
//
interFORest.prototype.getTreeIdFromHash = function(hash) {
	return this.treeIdFromHash[hash];
}

interFORest.prototype.mouseX = function(e) {
	return (e.layerX != undefined) ? e.layerX : e.offsetX;
}

interFORest.prototype.mouseY = function(e) {
	return (e.layerY != undefined) ? e.layerY : e.offsetY;
}
