/* 
	ko2ib.js
	Koji Ota	Nov 30, 2015
*/

//////////
function ptrace(str)
{
	console.log(str);
}

//////////
class Ko2Object {
	// オブジェクトを文字列化する
	static obj2str(obj) {
		var replacer = function(key, val) {
			return typeof val == "function" ? val.toString() : val;
		};
		var str = JSON.stringify(obj, replacer);
		return str;
	}
	
	// 文字列化されたオブジェクトの内容をオブジェクトに戻す
	static str2obj(obj) {
		var parser = function(key, val) {
			try {
				return (typeof val == "string" && val.match(/^function[\s\S]+?}$/)) ? eval('('+val+')') : val;
			} catch(e) {
				return val;
			}
		};
		var body = JSON.parse(str, parser);
		return body;
	}
}

//////////
// viewobj には以下のインターフェースがあることを期待する
// Ko2CardFrame参照
// view_card(card)			- カード情報を表示
// view_attachment(card) 	- 添付情報を表示
// view_history(card)		- 履歴情報を表示
// message(str)				- 情報(特にエラー)を表示
// error(str)				- エラー情報
class Ko2Card {
	constructor() {
		this._card = {};
		this._card.id = 0;
		this._card.pid = 0;
		this._card.name = null;
		this._card.closed = null;
		this._card.wiki = null;
		this._card.html = null;
		this._card.attachment = [];
		this._card.reference = [];
		this._card.history = [];
	}
	
	data() {
		if (arguments.length > 0) {
			this._card = arguments[0];
		}
		return this._card;
	}
	
	browse(viewobj) {
		viewobj.browse(this._card);
	}
	
	load(id, viewobj) {
		$.ajax({
			url: "/data/wiki/"+id,
			method: "get",
			success: (function(obj, viewer) {
					return function(jsonData) {
						var data = JSON.parse(jsonData);
						obj.data(data.card);
						viewer.view_card(obj._card);
					};
				})(this, viewobj),
			error: (function(obj, viewer) {
					return function(jsonData) {
						viewer.error("ページ情報の取得に失敗しました。");
					};
				})(this, viewobj)
		});
	}
	
	save(viewobj) {
		$.ajax({
			url: "/data/wiki/"+this._card.id,
			method: "post",
			data: {card: JSON.stringify(this._card)},
			success: (function(obj, viewer) {
					return function(jsonData) {
						var data = JSON.parse(jsonData);
						obj.data(data.card);
						viewer.view_card(obj._card);
					};
				})(this, viewobj),
			error: (function(obj, viewer) {
					return function(jsonData) {
						viewer.error("ページ情報の取得に失敗しました。");
					};
				})(this, viewobj)
		});
	}
	
	upload(formData, viewobj) {
		$.ajax({
			url: "/data/wiki/"+this._card.id+"/upload",
			type: 'post',
			enctype: "multipart/form-data",
			data: formData,
			processData: false,
			contentType: false,
			dataType: 'html',
			complete: function(XMLHttpRequest, textStatus){
				// AJAX通信完了時に呼ばれる関数です。successやerrorが呼ばれた後に呼び出されるAjax Eventです。
				//console.log("upload complete.");
			},
			success: (function(obj, viewer) {
				return function(jsonData, dataType) {
					//console.log("upload success.");
					var data = JSON.parse(jsonData);
					if (data.status == "OK") {
						obj._card.attachment = data.attachment;
						viewer.view_attachment(obj._card);
					}
				};
			})(this, viewobj),
			error: (function(obj, viewer) {
				return function(XMLHttpRequest, textStatus, errorThrown) {
					viewer.error("転送に失敗しました。("+textStatus+")");
				};
			})(this, viewobj),
		});
	}
	
	history(viewobj) {
		$.ajax({
			url: "/data/wiki/"+this._card.id+"/history",
			method: "get",
			success: (function(obj, viewer) {
				return function(jsonData) {
					var data = JSON.parse(jsonData);
					console.log(data.history);
					obj._card.history = data.history;
					viewer.view_history(obj._card);
				};
			})(this, viewobj),
			error: (function(obj, viewer) {
				return function(jsonData) {
					viewer.error("ページ情報の履歴情報取得に失敗しました。");
				};
			})(this, viewobj)
		});
	}
}


//////////
class Ko2Widget {
	constructor() {
		this._name = "unknown";
		this._node = null;
		this._mapped = false;
		this._visible = false;
		this._prop = {};
		this._display = null;
	}
	
	name() {
		if (arguments.length > 0) {
			this._name = arguments[0];
		}
		return this._name;
	}
	
	set(name, value) {
		this._prop[name] = value;
		return this;
	}
	
	get(name) {
		return this._prop[name];
	}
	
	setNode(node) {
		this._node = node;
		return node;
	}
	
	setNodeById(id) {
		this._node = document.getElementById(id);
		return this;
	}
	
	appendChild(widget) {
		this._node.appendChild(widget._node);
	}
	
	removeChildren() {
		while(this._node.firstChild) this._node.removeChild(this._node.firstChild);
		return this;
	}
	
	node() {
		return this._node;
	}
	
	visible() {
		if (arguments.length > 0) {
			this._visible = arguments[0];
		}
		return this._visible;
	}
	
	style(name) {
		if (arguments.length > 1) {
			this._node.style[name] = arguments[1];
			return this;
		} else {
			return this._node.style[name];
		}
	}
	
	id() {
		if (arguments.length > 0) {
			this._node.id = arguments[0];
			return this;
		} else {
			return this._node.id;
		}
	}
	
	className() {
		if (arguments.length > 0) {
			this._node.className = arguments[0];
			return this;
		} else {
			return this._node.className;
		}
	}
	
	create() {
	}
	
	resize(w, h) {
		this._node.style.width = w;
		this._node.style.height = h;
	}
	
	is_mapped() {
		return this._mapped;
	}
	
	html() {
		if (arguments.length > 0) {
			this._node.innerHTML = arguments[0];
			return this;
		} else {
			return this._node.innerHTML;
		}
	}
	
	show() {
		if (this._node == null) {
			throw "Ko2Widget::show():["+this._name+"] node is not defined.";
		}
	
		if (this._visible == true) {
			//ptrace("Ko2widget::show:["+this._name+"] already visible.");
			return
		}
		if (this._mapped == false) {
			this.create();
			this._mapped = true;
		}
		
		if (this._display == null) {
			this._display = this._node.style.display;
		}
		
		this._node.style.visibility = "visible";
		this._node.style.display = this._display;
		this._visible = true;
	}
	
	hide() {
		//ptrace("Ko2Widget::hide");
		if (this._node == null) {
			throw "Ko2Widget::hide():["+this._name+"] node is not defined.";
		}
		if (this._visible == false) {
			//ptrace("Ko2widget::hide:["+this._name+"] already invisible.");
			return
		}
		
		if (this._display == null) {
			this._display = this._node.style.display;
		}
		
		this._node.style.visibility = "hidden";
		this._node.style.display = "none";
		this._visible = false;
	}
}

//////////
class Ko2Container extends Ko2Widget {
	constructor() {
		super();
		this._node = document.createElement("div");
	}
}

//////////
class Ko2Label extends Ko2Widget {
	constructor() {
		super();
		this._node = document.createElement("span");
		if (arguments.length > 0) {
			this._node.innerHTML = arguments[0];
		}
	}
	
	addEventListener(type, listener) {
		this._node.addEventListener(type, listener);
		return this;
	}
}

//////////
class Ko2Button extends Ko2Label {
	constructor(label) {
		super();
		this._node = document.createElement("button");
		this._node.innerHTML = label;
	}
}

//////////
class Ko2Menu extends Ko2Container {
	constructor() {
		super();
		this._groups = [];
		this._menuBar = null;
		this._mainMenu = null;
	}
	
	add_group(name) {
		this._groups.push({name: name, items: []});
	}
	
	add(group, name, action) {
		for(var g=0; g<this._groups.length; g++) {
			if (group == this._groups[g].name) {
				this._groups[g].items.push({name: name, action: action});
			}
		}
	}
	
	create() {
		this._menuBar = new Ko2Container();
		this._menuBar
			.className("header_menu")
			;
		this.appendChild(this._menuBar);
		
		this._menuBar.appendChild(
			(new Ko2Label())
				.style("background", "gold")
				.style("color", "black")
				.html( "&nbsp;" + this._prop["pname"] + ", " + this._prop["pversion"] + "&nbsp;" )
		);
		
		this._menuBar.appendChild(
			(new Ko2Label())
				.style("background", "cornflowerblue")
				.style("color", "white")
				.html( "&nbsp; user [ " + this._prop["user"] + " ] &nbsp;" )
		);
		
		this._mainMenu = new Ko2Label();
		this._menuBar.appendChild( this._mainMenu );
		this.recreate_menu();
	}
	
	recreate_menu() {
		this._mainMenu.removeChildren();
		
		var mainMenu = this._mainMenu.node();
		for(var i=0; i<this._groups.length; i++) {
			var b = document.createElement("div");
			mainMenu.appendChild(b);
			b.className = "menu";
			b.innerHTML = this._groups[i].name + " |";
			var sb = document.createElement("div");
			b.appendChild(sb);
			sb.className = "submenu";
			for(var j=0; j<this._groups[i].items.length; j++) {
				var t = document.createElement("div");
				sb.appendChild(t);
				t.innerHTML = this._groups[i].items[j].name;
				//console.log(this._groups[i].name+">"+this._groups[i].items[j].name);
				t.addEventListener("click", this._groups[i].items[j].action);
			}
		}

		$(".header_menu .menu").mouseover(function(e) {
			$(this).children("div").show();
			var offset = $(this).offset();
			$(this).children("div").css("left", offset.left);
		});
		$(".header_menu .menu").mouseup(function(e) {
			$(this).children("div").hide();
		});
		$(".header_menu .menu").mouseout(function(e) {
			$(this).children("div").hide();
		});
	};
}

//////////
class Ko2LineMessage extends Ko2Container {
	constructor() {
		super();
		this.className("line_message");
		this.html("Information:");
	}
}

//////////
class Ko2Entry extends Ko2Widget {
	constructor(id) {
		super();
		this._node = document.createElement("input");
		this._node.type = "text";
		this._node.id = id;
	}
}

//////////
class Ko2Text extends Ko2Widget {
	constructor(id) {
		super();
		this._node = document.createElement("textarea");
		this._node.id = id;
	}
	text() {
		if (arguments.length > 0) {
			this._node.value = arguments[0];
		}
		return this._node.value;
	}
}

//////////
class Ko2Form extends Ko2Widget {
	constructor() {
		super();
		this._node = document.createElement("form");
		this._node.method = "post";
		this._node.enctype = 'multipart/form-data';
	}
	action(t) {
		this._node.action = v;
		return this;
	}
	formData() {
		return new FormData(this._node);
	}
}

//////////
class Ko2File extends Ko2Widget {
	constructor() {
		super();
		this._node = document.createElement("input");
		this._node.type = "file";
		this._node.name = "upfile";
	}
}

//////////
class Ko2Hidden extends Ko2Widget {
	constructor(name) {
		super();
		this._node = document.createElement("input");
		this._node.type = "hidden";
		this._node.name = name;
		this._node.value = null;
		if (arguments.length > 1) {
			this._node.value = arguments[1];
		}
	}
	value(v) {
		this._node.value = v;
	}
}

//////////
class Ko2Table extends Ko2Widget {
	constructor() {
		super();
		this._node = document.createElement("table");
		this._tr = null;
	}
	
	tr() {
		this._tr = document.createElement("tr");
		this._node.appendChild(this._tr);
		return this;
	}
	
	th(obj) {
		var th = document.createElement("th");
		var type = typeof(obj);
		if (type == "string") {
			th.innerHTML = obj;
			this._tr.appendChild(th);
		}
		return this;
	}
	
	td(obj) {
		var td = document.createElement("td");
		var type = typeof(obj);
		if (type == "string") {
			td.innerHTML = obj;
			this._tr.appendChild(td);
			if (arguments.length > 1) {
				var opts = arguments[1];
				if (typeof(opts.width) != "undefind") {
					this._node.tableLayout = "fixed";
					this._node.style.width = "100%";
					td.style.width = td.style.width;
				}
			}
		} else if (type == "object") {
			this._tr.appendChild(obj._node);
		}
		return this;
	}
}

//////////
class Ko2FloatingContainer extends Ko2Container {
	constructor() {
		super();
	}
	
	mousedown(e) {
		var node = e.target;
		this._dragging = true;
		this._position = {};
		this._position.x = 0;
		if (this._node.style.left != "") {
			this._position.x = parseInt(this._node.style.left.slice(0, -2));
		}
		this._position.y = 0;
		if (this._node.style.top != "") {
			this._position.y = parseInt(this._node.style.top.slice(0, -2));
		}
		//console.log("position.x="+this._node.style.left.slice(0, -2)+", position.y="+this._node.style.top.slice(0, -2));
		console.log("x="+this._position.x+", y="+this._position.y);
		this._position.clientX = e.clientX;
		this._position.clientY = e.clientY;
	}
	
	mousemove(e) {
		if (this._dragging == false) {
			return;
		}
		/*
		var node = e.target;
		var cX = e.clientX;
		var cY = e.clientY;
		var pX = e.pageX;
		var pY = e.pageY;
		var lX = e.layerX;
		var lY = e.layerY;
		var oX = obj.offsetLeft;
		var oY = obj.offsetTop;
		console.log("cX="+cX+",cY="+cY+",pX="+pX+",pY="+pY+",lX="+lX+",lY="+lY);
		*/
		var dX = e.clientX - this._position.clientX;
		var dY = e.clientY - this._position.clientY;
		this._position.x += dX;
		this._position.y += dY;
		this._node.style.left = this._position.x + "px";
		this._node.style.top  = this._position.y + "px";
		this._position.clientX = e.clientX;
		this._position.clientY = e.clientY;
	}
	
	mouseup(e) {
		this._dragging = false;
	}
	
	mouseleave(e) {
		this._dragging = false;
	}
	
	create() {
		this.style("position", "absolute");
		
		//this._node.style.left = 100 + "px";
		//this._node.style.top  = 100 + "px";
		
		this._base = new Ko2Container();
		this.appendChild(this._base);
		this._base.node().innerHTML = "Untitled";
		this._base
			.style("background", "orange")
			.style("position", "absolute")
			//.style("width", "400px")
			;
		
		this._dragging = false;
		this._base.node().addEventListener("mousedown", (function(obj) {
			return function(e) {
				obj.mousedown(e);
			};
		})(this), false);
		this._base.node().addEventListener("mousemove", (function(obj) {
			return function(e) {
				obj.mousemove(e);
			};
		})(this), false);
		this._base.node().addEventListener("mouseup", (function(obj) {
			return function(e) {
				obj.mouseup(e);
			};
		})(this), false);
		
		this._frame = new Ko2Container();
		this._frame
			.style("background", "white")
			;
		this._base.appendChild(this._frame);
	}
	
	frame() {
		return this._frame;
	}
}

//////////
class Ko2CardFrame extends Ko2Container {
	constructor() {
		super();
		this._cards = [];
		this._card = new Ko2Card();
		this._text = null;
	}
	card() {
		if (arguments.length > 0) {
			this._card.data(arguments[0]);
		}
		return this._card;
	}
	push() {
		console.log("+ push card");
		this._cards.push(this._card);
		this._card = new Ko2Card();
	}
	pop() {
		console.log("- push card");
		this._card = this._cards.pop();
	}
	set_editor() {
		if (arguments.length > 0) {
			this._text = arguments[0];
		}
		return this._text;
	}
	load_editor() {
		this._text.text(this._card.data().wiki);
	}
	load_card(id) {
		// card-id を要求して表示
		ptrace("Ko2CardFrame::load_card");
		this._card.load(id, this);
	}
	save_card() {
		// card-id を保存して表示
		ptrace("Ko2CardFrame::save_card");
		this._card.save(this);
	}
	
	// カードを表示する. 継承先で実装
	view_card(card) { }
	
	// カード履歴情報を表示する. 継承先で実装
	view_history(card) { }
	
	// カード添付情報を表示する. 継承先で実装
	view_attachment(card) { }
	
	// メッセージを表示する. 継承先で実装
	message(str) { }
	
	// エラーメッセージを表示する. 継承先で実装
	error(str) { }
}

//////////
//class Ko2Frame extends Ko2CardFrame {
//	constructor() {
//		super();
//		this._overlay = null;
//	}
//	
//	overlay() {
//		ptrace("overlay:"+this._overlay._visible);
//		return this._overlay;
//	}
//}

//////////
class Ko2OverlayFrame extends Ko2CardFrame {
	constructor(order) {
		super();
		this.className("overlay");
		this._node.style.zIndex = order; 
	}
}

//////////
class Ko2Wiki extends Ko2CardFrame {
	constructor(page) {
		super();
		this._page = page;
		this._frames = {};
		this._hook = {
			pre: {
				edit_cancel: function() {}
			},
			post: {
				edit_cancel: function() {}
			}
		};
	}
	
	create_browser() {
		ptrace("Ko2Wiki::create_browser");
		var b;
	
		this._frames.browser = {}
		this._frames.browser.frame = new Ko2Container();
		this._frames.browser.frame.name("browser");
			
		var search = new Ko2Container();
		search
			.style("textAlign", "right")
			.style("marginBottom", "1em")
			;
		this._frames.browser.frame.appendChild(search);
		this._frames.browser.input = new Ko2Entry();
		search.appendChild(this._frames.browser.input);
		b = new Ko2Button("検索");
		search.appendChild(b);
		b
			.style("marginLeft","5px")
			.addEventListener("click", (function(wiki) { return function(e) {wiki.raise("browser"); } })(this))
			;
	
		var menu = new Ko2Container();
		this._frames.browser.frame.appendChild(menu);
		menu.style("textAlign", "right");
		
		b = new Ko2Label("編集");
		menu.appendChild(b);
		b
			.className("clickable")
			.addEventListener("click", (function(wiki) { 
					return function(e) {
						wiki.load_editor();
						wiki.raise("editor");
					} 
				})(this))
			;
			
		menu.appendChild(new Ko2Label(" | "));

		b = new Ko2Label("アップロード");
		menu.appendChild(b);
		b
			.className("clickable")
			.addEventListener("click", (function(wiki) {
					return function(e) {
						wiki.raise("upload");
					} 
				})(this))
			;
		
		menu.appendChild(new Ko2Label(" | "));
		
		b = new Ko2Label("履歴");
		menu.appendChild(b);
		b
			.className("clickable")
			.addEventListener("click", (function(wiki) {
				console.log("history");
				return function(e) {
					wiki._card.history(wiki);
				};
			})(this))
			;
	
		this._frames.browser.view = new Ko2Container();
		this._frames.browser.view.className("content");
		this._frames.browser.frame.appendChild(this._frames.browser.view);
	
		this.appendChild(this._frames.browser.frame);
		
		this._frames.browser.frame.show();
	}
	
	create_editor() {
		ptrace("Ko2Wiki::create_editor");
	
		this._frames.editor = {};
		this._frames.editor.frame = new Ko2Container();
		this._frames.editor.frame.name("browser");
		
		var menu = new Ko2Container();
		this._frames.editor.frame.appendChild(menu);
		menu.style("textAlign", "right");
		
		var b;
		
		b = new Ko2Label("戻る");
		menu.appendChild(b);
		b
			.className("clickable")
			.addEventListener("click", (function(wiki) { return function(e) {wiki.raise("browser"); } })(this))
			;
		
		var d;
		var d1;
		//
		d = new Ko2Container();
		d.style("padding", "5px");
		this._frames.editor.frame.appendChild(d);
		//
		d1 = new Ko2Container();
		d.appendChild(d1);
		b = new Ko2Entry();
		d1.appendChild(b);
		//
		d1 = new Ko2Container();
		d.appendChild(d1);
		b = new Ko2Text();
		d1.appendChild(b);
		b
			.style("width",  "40em")
			.style("height", "20em")
			;
		
		this.set_editor(b);
		
		//
		d = new Ko2Container();
		d.style("padding", "5px");
		this._frames.editor.frame.appendChild(d);
		//
		b = new Ko2Button("Save");
		d.appendChild(b);
		b.addEventListener("click", (function(wiki) {
			return function(e) {
				wiki._card.data().wiki = wiki._text.text();
				console.log(wiki);
				wiki.save_card();
			};
		})(this));
		//
		b = new Ko2Button("Cancel");
		d.appendChild(b);
		b.style("marginLeft", "5px");
		b.addEventListener("click", (function(wiki) { 
			return function(e) {
				wiki._hook.pre.edit_cancel();
				wiki.raise("browser"); 
				wiki._hook.post.edit_cancel();
			} 
		})(this));
		
		this.appendChild(this._frames.editor.frame);
		
		this._frames.editor.frame.show();
	}
	
	create_upload() {
		ptrace("Ko2Wiki::create_upload");
	
		this._frames.upload = {};
		this._frames.upload.frame = new Ko2Container();
		this._frames.upload.frame.name("history");
	
		//
		var menu = new Ko2Container();
		this._frames.upload.frame.appendChild(menu);
		menu.style("textAlign", "right");

		var b;
		b = new Ko2Label("戻る");
		menu.appendChild(b);
		b
			.className("clickable")
			.addEventListener("click", (function(wiki) { return function(e) {wiki.raise("browser"); } })(this))
			;
		
		var frame = new Ko2Container();
		this._frames.upload.frame.appendChild(frame);
		
		this._frames.upload.file = new Ko2File();
		frame.appendChild(this._frames.upload.file);
		b = new Ko2Button("Upload");
		frame.appendChild(b);
		b.addEventListener("click", (function(wiki) {
			return function(e) {
				console.log("uploading...");
				var formData = new FormData();
				formData.append("files", wiki._frames.upload.file.node().files[0]);
				console.log(wiki._frames.upload.file.node().files[0]);
				
				this._card.upload({attachment: this.view_attachment, message: this.message, error: this.error});
			};
		})(this));
		
		this._frames.upload.view = new Ko2Container();
		this._frames.upload.frame.appendChild(this._frames.upload.view);
		
		this.appendChild(this._frames.upload.frame);
		
		this._frames.upload.frame.show();
	}
	
	create_listview() {
		ptrace("Ko2Wiki::create_listview");
	
		this._frames.listview = {};
		this._frames.listview.frame = new Ko2Container();
		this._frames.listview.frame.name("listview");
	
		//
		var menu = new Ko2Container();
		this._frames.listview.frame.appendChild(menu);
		menu.style("textAlign", "right");

		var b;
		b = new Ko2Label("戻る");
		menu.appendChild(b);
		b
			.className("clickable")
			.addEventListener("click", (function(wiki) { return function(e) {wiki.raise("browser"); } })(this))
			;
		
		// body {list: [...]}
		/*
		var body = document.createElement("div");
		frame.appendChild(body);
		var result = JSON.parse(data);
		var tbl = document.createElement("table");
		body.appendChild(tbl);
		var tr;
		var td;
		for(var i=0; i<result.list.length; i++) {
			//
			tr = document.createElement("tr");
			tbl.appendChild(tr);
			// ID
			td = document.createElement("td");
			tr.appendChild(td);
			td.innerHTML = result.list[i].id;
			// ページ名
			td = document.createElement("td");
			tr.appendChild(td);
			td.innerHTML = result.list[i].name;
		}
		*/
		
		this.appendChild(this._frames.listview.frame);
		
		this._frames.listview.frame.show();
	}
	
	create_history() {
		ptrace("Ko2Wiki::create_listview");
	
		this._frames.history = {};
		this._frames.history.frame = new Ko2Container();
		this._frames.history.frame.name("history");
	
		//
		var menu = new Ko2Container();
		this._frames.history.frame.appendChild(menu);
		menu.style("textAlign", "right");

		var b;
		b = new Ko2Label("戻る");
		menu.appendChild(b);
		b
			.className("clickable")
			.addEventListener("click", (function(wiki) { return function(e) {wiki.raise("browser"); } })(this))
			;
		
		this._frames.history.view = new Ko2Container();
		this._frames.history.frame.appendChild(this._frames.history.view);
		
		this.appendChild(this._frames.history.frame);
		
		this._frames.history.frame.show();
	}
	
	create() {
		console.log("Wiki::create");
		this.create_browser();
		this.create_editor();
		this.create_upload();
		this.create_listview();
		this.create_history();
		//this.create_minieditor();
		
		//test
		//this.raise("browser");
		//this.raise("editor");
	}
	
	raise(name) {
		console.log("Wiki::raise("+name+") mapped?",this.is_mapped());
	
		this.show();
		this._frames["browser"].frame.hide();
		this._frames["editor"].frame.hide();
		this._frames["upload"].frame.hide();
		this._frames["history"].frame.hide();
		this._frames["listview"].frame.hide();
	
		this._frames[name].frame.show();
	}
	
	load(id) {
		this._card.load(id, this);
	}
	
	list() {
		// wiki リストを要求して表示
		ptrace("Wiki::list");
		this.raise("listview");
		
		$.ajax({
			url: "/data/wiki/",
			method: "get",
			success: (function(wiki) {
					return function(jsonData) {
					};
				})(),
			error: (function(wiki) {
					return function(jsonData) {
					};
				})()
		});
	}
	
	view_card(card) {
		this.raise("browser");
		var body = card.html;
		if (card.attachment.length > 0) {
			body += "<h3>添付ファイル</h3><div class=quote><table>";
			for(var i=0; i<card.attachment.length; i++) {
				var entry = card.attachment[i]
				var date = new Date(entry.mtime);
				body += "<tr>"
					+ "<td><a href='"+entry.url+"' target='_blank'>"+entry.name+"</a></td>"
					+ "<td>|"+entry.size+" bytes</td>"
					+ "<td>|"+date+"</td>"
					+ "</tr>";
			}
			body += "</table></div>";
		}
		if (card.reference.length > 0) {
			body += "<hr>参照元: ";
			for(var i=0; i<card.reference.length; i++) {
				var r = card.reference[i];
				body += "<span class='ko2wiki' data-id='"+r.id+"' data-pid='"+r.pid+"'>" + r.name + "</span> &nbsp;";
			}
		}
		this._frames.browser.view.html(body);
		
		// clickable object
		var clickables = document.getElementsByClassName("ko2wiki");
		console.log("clickables="+clickables.length);
		for(var i=0; i<clickables.length; i++) {
			var c = clickables[i];
			console.log("name="+c.innerText+", id="+c.dataset.id+", pid="+c.dataset.pid);
			if (c.dataset.id == 0) {
				// 新規カードの作成する
				c.addEventListener("click", (function(wiki, name) {
					return function(e) {
						var id = wiki._card.data().id;
						wiki.push();
						wiki._card.data().pid = id;
						wiki._card.data().name = name;
						wiki._hook.pre.edit_cancel = (function(obj) {
							return function() {
								obj.pop();
								obj._hook.pre.edit_cancel = function() {};
							};
						})(wiki);
						wiki.load_editor();
						wiki.raise("editor");
					};
				})(this, c.innerText));
			} else {
				// 既存カードを表示する
				c.addEventListener("click", (function(wiki, id) {
					return function(e) {
						wiki.load(id);
					};
				})(this, c.dataset.id));
			}
		}
	}
	
	view_upload(card) {
		var tbl = new Ko2Table();
		for(var i=0; i<card.attachment.length; i++) {
			var f = card.attachment[i];
			tbl.tr();
			tbl
				.td("<td><a href='"+f.url+"' target='_blank'>"+f.name+"</a></td>")
				.td(f.size)
				.td(f.ctime)
				;
		}
		this._frames.upload.view.removeChildren();
		this._frames.upload.view.appendChild(tbl);
	}
	
	view_history(card) {
		this.raise("history");
		var body = "";
		for(var i=0; i<card.history.length; i++) {
			var rev = card.history[i];
			var date = new Date(rev.date);
			body += "<h3> revision:"+rev.revision+"</h3>";
			body += "Date: "+date+", By "+rev.who;
			for(var j=0; j<rev.changeset.length; j++) {
				var changeset = rev.changeset[j]
				body += "<h4>"+changeset.name+"</h4>";
				body += "<blockquote><pre>"+changeset.value+"</pre></blockquote>";
			}
		}
		this._frames.history.view.html(body);
	}
	
	message(str) {
		this._page.message(str);
	}
	
	error(str) {
		this._page.message("<font color=red>ERROR</font>:"+str);
	}
}

////////// FIXME:
class Ko2WikiMiniEditor extends Ko2OverlayFrame {
	constructor(order) {
		super(order);
		this._card = null;
		this._widget._frame = null;
		this._widget._name = null;
		this._widget._wiki = null;
	}
	
	create() {
		this._widget._frame = new Ko2FloatingContainer();
		this.appendChild(this._widget._frame);
		
		var frame = this._widget._frame.frame();
		frame.resize("400px", "300p");
		
		var tbl = new Ko2Table();
		frame.appendChild(tbl);
		
		this._widget._name = new Ko2Entry("name");
		tbl.tr()
			.td("名前", {width: "80px"})
			.td(this._minieditor._name);
			;
			
		this._widget._wiki = new Ko2Text("wiki");
		tbl.tr()
			.td("内容", {width: "80px"})
			.td(this._minieditor._content);
			;
			
		var buttons = new Ko2Container();
		frame.appendChild(buttons);
		
		var b;
		b = new Ko2Button("Save");
		buttons.appendChild(b);
		//
		b = new Ko2Button("Cancel");
		buttons.appendChild(b);
		b.style("marginLeft", "5px");
		b.addEventListener("click", (function(widget) { return function(e) { widget.hide(); }; })(this._overlay));
		
		this.hide();
	}
}

//////////
class Ko2Task extends Ko2Container {
	constructor(page) {
		super();
		this._page = page;
	}
}

//////////
class Ko2Notepad extends Ko2Container {
	constructor(page) {
		super();
		this._page = page;
	}
}

//////////
class Ko2Page extends Ko2Container {
	constructor(app) {
		super();

		this._app = app;
		this._menu = null;
		this._message = null;
		this._frame = null;
		this._overlay = null;
		this._tab = {};
	}
	
	create() {
		console.log("Ko2Page::create");
		var menu = (new Ko2Menu())
			.set("pname", this._prop["pname"])
			.set("pversion", this._prop["pversion"])
			.set("user", this._prop["user"])
			;
		this._menu = menu;
		
		// pages
		this._tab.wiki = new Ko2Wiki(this);
		this._tab.task = new Ko2Task(this);
		this._tab.notepad = new Ko2Notepad(this);
		
		// menu items
		menu.add_group("Account");
		menu.add("Account", "Logout", this._app.logout);
		
		menu.add_group("File");
		menu.add("File", "Folder", (function(file) { return function() { file.folder() }; })(this._tab["file"]));
		
		menu.add_group("Wiki");
		menu.add("Wiki", "Home", (function(wiki) { return function() { wiki.load(1) }; })(this._tab["wiki"]));
		menu.add("Wiki", "User", (function(wiki, page) { return function() { wiki.load(page.get("userid")) }; })(this._tab["wiki"], this));
		menu.add("Wiki", "List", (function(wiki) { return function() { wiki.list() }; })(this._tab["wiki"]));
		
		/*
		menu.add_group("Task");
		menu.add("Task", "Home", (function(name) { return function() { this._tab[name].home() }; })("task"));
		menu.add("Task", "List", (function(name) { return function() { this._tab[name].list() }; })("task"));
		menu.add("Task", "New",  (function(name) { return function() { this._tab[name].new() }; })("task"));
		
		menu.add_group("Notepad");
		menu.add("Notepad", "Home", (function(name) { return function() { this._tab[name].home() }; })("notepad"));
		menu.add("Notepad", "List", (function(name) { return function() { this._tab[name].list() }; })("notepad"));
		menu.add("Notepad", "New",  (function(name) { return function() { this._tab[name].new() }; })("notepad"));
		*/
		
		menu.add_group("Test");
		menu.add("Test", "Overlay", (function(page, wiki) { return function() { page.overlay(wiki); }; })(this, this._tab["wiki"]));
		
		this.appendChild(this._menu);
		this._menu.show();
		
		this._message = new Ko2LineMessage();
		this.appendChild(this._message);
		this._message.show();
		
		this.appendChild(this._tab.wiki);
		this.appendChild(this._tab.task);
		this.appendChild(this._tab.notepad);
	}
	
	app() {
		return this._app;
	}
	
	message(str) {
		this._message.html(str);
	}
	
	page(name) {
		return this._tab[name];
	}
	
	raise(name) {
		this._tab[name].show();
	}
	
	overlay(obj) {
		obj.overlay().show();
	}
}

//////////
class Ko2App {
	constructor(names) {
		this._page = new Ko2Page(this);
		this._page.setNodeById(names.page);
		
		this._prop = {};
		
		this._mapped = false;
	}
	
	set(name, value) {
		this._prop[name] = value;
		return this;
	}
	
	get(name) {
		return this._prop[name];
	}
	
	show() {
		if (this._mapped == true) {
			return;
		}
		
		this._page
			.set("pname", this._prop["pname"])
			.set("pversion", this._prop["pversion"])
			.set("user", this._prop["user"])
			.show()
		;
		
		$.ajax({
			url: "/data/user/"+this._prop["user"],
			method: "get",
			success: (function(app, page) {
				return function(jsonData) {
					var data = JSON.parse(jsonData);
					page.set("userid", data.card.id);
					page.page("wiki").card(data.card);
					page.page("wiki").view_card(data.card);
				};
			})(this, this._page),
			error: (function(page) {
				return function(jqXHR, textStatus, errorThrown) {
					page.message("<font color=red>ERROR</font>:ユーザー情報が取得できませんでした。("+textStatus+","+errorThrown+")");
				};
			})(this._page),
		});
		
		this._mapped = true;
	}
	
	logout() {
		window.location.href = "/logout";
	}
}
