﻿dojo.require("dijit.form.Button");
dojo.require("dijit.form.CheckBox");
dojo.require("dijit.form.DropDownButton");
dojo.require("dijit.form.HorizontalSlider");
dojo.require("dijit.form.RadioButton");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.layout.BorderContainer");
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit.layout.TabContainer");
dojo.require("dijit.Dialog");
dojo.require("dijit.Menu");
dojo.require("dijit.MenuItem");
dojo.require("dijit.TitlePane");
dojo.require("dijit.TooltipDialog");
dojo.require("dijit.Tree");
dojo.require("dijit.tree.TreeStoreModel");
dojo.require("dojo.cookie");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojo.DeferredList");
dojo.require("dojo.dnd.Moveable");
dojo.require("dojo.hash");
dojo.require("dojo.i18n");
dojo.require("dojo.parser");
dojo.require("dojox.analytics.Urchin");
dojo.require("dojox.image.Gallery");
dojo.require("dojox.image.ThumbnailPicker");

dojo.requireLocalization("dijit", "common", null, "ROOT,ja");

google_ad_client = "pub-4143471800690907";
google_ad_slot = "1075731603";
google_ad_width = 160;
google_ad_height = 600;
amazon_ad_tag = "haxe-22";
amazon_ad_width = "120";
amazon_ad_height = "240";
amazon_ad_logo = "hide";
amazon_ad_link_target = "new";
amazon_ad_border = "hide";
amazon_ad_discount = "remove";

dijit.Tree.prototype.persist = false;

var ga;

var localize = function (obj, prop) {
	if (arguments.length == 1)
		return obj + "." + dojo.locale + ".txt";
	return prop + dojo.locale in obj ? obj[prop + dojo.locale] : obj[prop];
};

var defaults;
var data = [];
var body;
var subTypes = [];
var sex = "m";
var state = 0;
var bodyDirection = 0;
var otherDirection = 0;
var headDirection = 0;

dojo.declare("sayuri.Preview", [dijit._Widget, dijit._Templated], {
	templateString: '<div>'
		+ '<div dojoAttachPoint="main" class="preview">'
		+ '<img src="${_blankGif}" style="z-index: 2;" />'
		+ '<img src="${_blankGif}" style="z-index: 3;" />'
		+ '<img src="${_blankGif}" style="z-index: 6;" />'
		+ '<img src="${_blankGif}" style="z-index: 5;" />'
		+ '<img src="${_blankGif}" style="z-index: 4;" />'
		+ '<img src="${_blankGif}" style="z-index: 7;" />'
		+ '<img src="${_blankGif}" style="z-index: 8;" dojoAttachPoint="shield" />'
		+ '<img src="${_blankGif}" style="z-index: 1;" />'
		+ '<img src="${_blankGif}" style="z-index: 0;" dojoAttachPoint="cart" />'
		+ '<div dojoAttachEvent="onclick:_onPreviewClick" style="position: absolute; width: 100%; height: 100%; z-index: 20;">'
		+ '</div>'
		+ '</div>'
		+ '<input dojoAttachPoint="auto" id="${id}-auto" type="checkbox" dojoType="dijit.form.CheckBox" checked="checked" dojoAttachEvent="onChange:update" />'
		+ '<label for="${id}-auto">${autoLabel}</label>'
		+ '<input dojoAttachPoint="slider" type="text" dojoType="dijit.form.HorizontalSlider" value="0" disabled="disabled" dojoAttachEvent="onChange:_onSliderChanged" />'
		+ '</div>',
	widgetsInTemplate: true,
	autoLabel: "auto", // localize
	postCreate: function () {
		this.imgs = dojo.query("> img", this.main);
	},
	startAnimation: function () {
		var i = 0;
		var onChange = dojo.hitch(this, this._onSliderChanged);
		this.timer = setInterval(function () {
			onChange(i++);
		}, 80);
	},
	stopAnimation: function () {
		if (this.timer !== undefined) {
			clearInterval(this.timer);
			this.timer = undefined;
		}
	},
	update: function () {
		this.stopAnimation();
		var stylesList = [];
		dojo.forEach(this.imgs, function (img, i) {
			img.style.visibility = "hidden";
			if (dojo.isIE < 7)
				img.filters(0).Enabled = false;
			var s, direction;
			if (i == 7 || i == 8) {
				s = 0;
				direction = otherDirection;
			} else {
				s = state;
				direction = bodyDirection;
			}
			if (!data[i] || !data[i].d || !data[i].d[s] || !data[i].d[s][direction])
				return;

			if (dojo.isIE < 7) {
				img.filters(0).src = "image/" + data[i].i + ".png";
				img.filters(0).Enabled = true;
			} else
				img.style.backgroundImage = "url(image/" + data[i].i + ".png)";
			dojo.style(img, {
				width: data[i].w + "px",
				height: data[i].h + "px"
			});

			var createStyles = function (v, hd) {
				if (!v)
					return [img, "visibility", "hidden"];
				var x = 256 / 2 - v.x;
				var y = 256 * (i == 7 ? 1 : 3) / 4 - v.y;
				if (!v.b) {
					var o = body[s][direction][hd];
					if (!o)
						return null;
					x += o.x;
					y += o.y;
				}
				if (i == 8) {
					switch (direction) {
						case 0: y -= 23; break;
						case 1: x += 30; y -= 18; break;
						case 2: x += 35; break;
						case 3: x += 30; y += 18; break;
						case 4: y += 23; break;
						case 5: x -= 30; y += 18; break;
						case 6: x -= 35; break;
						case 7: x -= 30; y -= 18; break;
					}
				}
				return [img, { visibility: "visible", left: x + "px", top: y + "px", clip: "rect(0 " + v.c + ")"}];
			};
			if (i == 7) { // falcon
				stylesList.push(dojo.map(data[i].d[s][direction], createStyles));
				return;
			}
			switch (s) {
				case 0:
				case 2:
				case 4:
					if (data[0].d[s][direction].length * 8 != data[i].d[s][direction].length)
						dojo.style.apply(dojo, createStyles(data[i].d[s][direction][headDirection], headDirection));
					else
						stylesList.push(dojo.map(data[i].d[s][direction].slice(headDirection * 8, headDirection * 8 + 8), function (v) {
							return createStyles(v, headDirection);
						}));
					break;
				case 1:
				case 3:
				case 5:
					stylesList.push(dojo.map(data[i].d[s][direction], createStyles));
					break;
			}
		});
		if (stylesList.length > 0) {
			this.stylesList = dojo.map(stylesList, function (styles) { return dojo.filter(styles, function (style) { return style != null; }) });
			if (this.auto.attr('value'))
				this.startAnimation();
			else {
				var max = 0;
				dojo.forEach(stylesList, function (styles) {
					if (max < styles.length)
						max = styles.length;
				});
				this.slider.attr({ discreteValues: max, maximum: max - 1, value: 0, disabled: false });
				this.slider.onChange(0);
			}
		} else
			this.slider.attr('disabled', true);
	},
	_onPreviewClick: function (e) {
		var x = (e.layerX || e.offsetX) - 256 / 2;
		var y = (e.layerY || e.offsetY) - 256 * 3 / 4;
		var deg = Math.acos(x / Math.sqrt(x * x + y * y)) * 180 / Math.PI;
		if (y < 0)
			deg = 360 - deg;
		deg = (deg - 90 + 360 / 16 + 360) % 360;
		var newDirection = Math.floor(deg * 8 / 360);
		if (e.shiftKey)
			otherDirection = newDirection;
		else {
			switch (state) {
				case 0:
				case 2:
					switch ((newDirection - bodyDirection + 8) % 8) {
						case 4:
							// back
							bodyDirection = newDirection;
						case 0:
							// front and back
							headDirection = 0;
							break;
						case 1:
							// right 1 step
							if (headDirection == 1) {
								bodyDirection = newDirection;
								headDirection = 0;
							} else
								headDirection = 1;
							break;
						case 7:
							// left 1 step
							if (headDirection == 2) {
								bodyDirection = newDirection;
								headDirection = 0;
							} else
								headDirection = 2;
							break;
						case 2:
						case 3:
							// right 2 or 3 steps
							bodyDirection = (newDirection + 7) % 8;
							headDirection = 1;
							break;
						case 6:
						case 5:
							// left 2 or 3 steps
							bodyDirection = (newDirection + 1) % 8;
							headDirection = 2;
							break;
					}
					break;
				case 1:
				case 3:
				case 4:
				case 5:
					bodyDirection = newDirection;
					break;
			}
			otherDirection = bodyDirection;
		}
		switch (otherDirection) {
			case 0:
			case 1:
			case 6:
			case 7:
				this.cart.style.zIndex = 0;
				this.shield.style.zIndex = 8;
				break;
			default:
				this.cart.style.zIndex = 9;
				this.shield.style.zIndex = 0;
				break;
		}
		this.update();
	},
	_onSliderChanged: function (value) {
		dojo.forEach(this.stylesList, function (styles) {
			dojo.style.apply(dojo, styles[value % styles.length]);
		});
	}
});

var weaponSubType;
function setStyle(subType) {
	if (subType !== undefined) {
		weaponSubType = subType;
		dojo.forEach(sayuri.WeaponList.prototype.lists, function (list) {
			list.updateItemClasses();
		});
	}
}

dojo.declare("sayuri.TreeNode", dijit._TreeNode, {
	// remove expandoNodeText, iconNode
	templateString:
		'<div class="dijitTreeNode" waiRole="presentation">'
		+ '<div dojoAttachPoint="rowNode" class="dijitTreeRow" waiRole="presentation" dojoAttachEvent="onmouseenter:_onMouseEnter, onmouseleave:_onMouseLeave, onclick:_onClick, ondblclick:_onDblClick">'
		+ '<img src="${_blankGif}" alt="" dojoAttachPoint="expandoNode" class="dijitTreeExpando" waiRole="presentation">'
		+ '<span dojoAttachPoint="contentNode" class="dijitTreeContent" waiRole="presentation">'
		+ '<span dojoAttachPoint="labelNode" class="dijitTreeLabel" wairole="treeitem" tabindex="-1" waiState="selected-false" dojoAttachEvent="onfocus:_onLabelFocus">'
		+ '</span>'
		+ '</span>'
		+ '</div>'
		+ '<div dojoAttachPoint="containerNode" class="dijitTreeContainer" waiRole="presentation" style="display: none;">'
		+ '</div>'
		+ '</div>',
	_updateItemClasses: function (item) {
		var tree = this.tree, model = tree.model;
		if (tree._v10Compat && item === model.root)
			item = null;
		this._applyClassAndStyle(item, "label", "Label");
		this._applyClassAndStyle(item, "row", "Row");
	},
	_setExpando: function (processing) {
		var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened", "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
			idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
		dojo.removeClass(this.expandoNode, styles);
		dojo.addClass(this.expandoNode, styles[idx]);
	}
});

var bodyDeferred = new dojo.Deferred();
var bodyStore = new dojo.data.ItemFileReadStore({ url: localize("Body") });
bodyStore.fetch({ onComplete: function () { bodyDeferred.callback(); } });
var itemDeferred = new dojo.Deferred();
var itemStore = new dojo.data.ItemFileReadStore({ url: localize("Item") });
itemStore.fetch({ onComplete: function () { itemDeferred.callback(); } });

dojo.declare("sayuri.LinkMixin", null, {
	items: null, // required
	href: "", 	// required
	title: "", 	// optional
	updateHref: function () {
		var hrefs = [];
		dojo.forEach(this.items, function (item, type) {
			if (item)
				hrefs.push((type < 2 ? bodyStore : itemStore).getIdentity(item));
		});
		this.attr('href', '#' + hrefs.join('_'));
	},
	getTitle: function () {
		var titles = [];
		dojo.forEach(this.items, function (item, type) {
			if (item)
				titles.push(type < 2 ? bodyStore.getValue(item, 't') : itemStore.getLabel(item));
		});
		return titles.join(', ');
	}
});

dojo.declare("sayuri.LinkItem", [dijit._Widget, dijit._Templated, dijit._Contained, sayuri.LinkMixin], {
	templateString: '<div style="white-space: nowrap;">'
		+ '<div class="dijitInline dijitEditorIcon dijitEditorIconDelete" style="cursor: pointer;" dojoAttachEvent="onclick:_onDeleteClick"></div>'
		+ '<a href="${href}" title="${title}" dojoAttachPoint="linkNode" dojoAttachEvent="onclick:_onClick">${title}</a>'
		+ '</div>',
	postMixInProperties: function () {
		this.inherited(arguments);
		if (!this.title)
			this.title = this.getTitle();
	},
	_onClick: function (e) {
		var parent = this.getParent();
		parent.attr({ href: this.href, title: this.title });
		parent.setItems(this.items.slice(0));
		dojo.stopEvent(e);
	},
	_onDeleteClick: function () {
		this.getParent().removeChild(this);
	}
});

dojo.declare("sayuri.Link", [dijit._Widget, dijit._Templated, dijit._Container, sayuri.LinkMixin], {
	templateString: '<div>'
		+ '<div style="white-space: nowrap;">'
		+ '<div class="dijitInline dijitEditorIcon dijitEditorIconSave" style="cursor: pointer;" title="${buttonSave}" dojoAttachEvent="onclick:_onSaveClick"></div>'
		+ '<a dojoAttachPoint="linkNode" dojoAttachEvent="onclick:_onClick"></a>'
		+ '</div>'
		+ '<div dojoAttachPoint="containerNode"></div>'
		+ '</div>',
	attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
		href: { node: "linkNode", type: "attribute" },
		title: [
			{ node: "linkNode", type: "attribute" },
			{ node: "linkNode", type: "innerHTML" }
		]
	}),
	postMixInProperties: function () {
		this.inherited(arguments);
		var messages = dojo.i18n.getLocalization("dijit", "common");
		this.buttonSave = messages.buttonSave;
	},
	buildRendering: function () {
		this.inherited(arguments);
		var cookie = dojo.cookie('link');
		if (cookie) {
			var self = this;
			new dojo.DeferredList([bodyDeferred, itemDeferred]).addCallback(function () {
				dojo.forEach(cookie.split('-'), function (href) {
					var items = self.parse(href);
					if (items)
						self.addChild(new sayuri.LinkItem({ items: items, href: '#' + href }));
				});
			});
		}
	},
	parse: function (href) {
		var items = href.split('_');
		if (items.length < 2)
			return null;
		dojo.forEach(items, function (id, i) {
			(i < 2 ? bodyStore : itemStore).fetchItemByIdentity({
				identity: id,
				onItem: function (item) {
					items[i] = item;
				}
			});
		});
		return items[0] && items[1] ? items : null;
	},
	setHref: function (href) {
		var items = this.parse(href);
		if (!items)
			items = this.parse(href = 'MaleNovice_1');
		this.setItems(items);
		this.attr({ href: '#' + href, title: this.getTitle() });
	},
	setItems: function (items) {
		var self = this;
		self.items = items;
		sex = bodyStore.getIdentity(items[0]).charAt(0).toLowerCase();
		var deferreds = [];
		dojo.forEach(items, function (item, type) {
			var store = type < 2 ? bodyStore : itemStore;
			var dir = type < 2 ? 'body/' : 'item/';
			var id = type < 2 ? bodyStore.getValue(item, sex + "d") : itemStore.getValue(item, sex);
			var sprite = store.getValue(item, sex + "s");
			deferreds.push(dojo.xhrGet({
				url: dir + id + '.txt',
				handleAs: 'json',
				load: function (response) {
					var v = response.value;
					if (sprite)
						v.i = sprite;
					data[type] = v;
					if (response.body) {
						body = response.body;
						setStyle(response.subType);
						if (response.skill)
							skillList.setSkill(response.skill);
						else
							skillList.reset();
						// remove weapon
						data[5] = null;
						data[6] = null;
					} else if (response.subType) {
						var clearItems = {};
						for (var i = 0; i < subTypes.length; i++) {
							if (i != type && subTypes[i] !== undefined && (subTypes[i] & response.subType) != 0)
								clearItems[i] = null;
						}
						subTypes[type] = response.subType;
						self.updateItems(clearItems);
					}
				}
			}));
		});
		new dojo.DeferredList(deferreds).addCallback(preview, "update");
	},
	updateItems: function (items) {
		var self = this;
		var changed = false;
		var newSex = sex;
		for (var type in items) {
			if (type == 0)
				newSex = bodyStore.getIdentity(items[0]).charAt(0).toLowerCase();
		}
		var deferreds = [];
		for (var type in items) {
			(function (type, item) {
				if (item ? type == 1 || self.items[type] != item : self.items[type]) {
					changed = true;
					if (type < 5)
						self.items[type] = item;
					if (item) {
						var dir = type < 2 ? 'body/' : 'item/';
						var id = type < 2 ? bodyStore.getValue(item, newSex + "d") : itemStore.getValue(item, newSex);
						var sprite = type < 2 ? bodyStore.getValue(item, newSex + "s") : null;
						deferreds.push(dojo.xhrGet({
							url: dir + id + '.txt',
							handleAs: 'json',
							load: function (response) {
								if (type == 5 || type == 6) {
									var jobNames = [
										"Novice", "Swordman", "Magician", "Archer", "Acolyte", "Thief", "Merchant", "SuperNovice", "Ninja", "Gunslinger",
										"Knight", "PecoKnight", "Wizard", "Hunter", "Prieset", "Assassin", "Blacksmith",
										"Crusader", "PecoCrusader", "BabyPecoCrusader", "Sage", "Bard", "Dancer", "Monk", "Rouge", "Alchemist",
										"GameMaster"
									];
									var v = response[jobNames[weaponSubType]];
								} else {
									var v = response.value;
									if (sprite)
										v.i = sprite;
								}
								data[type] = v;

								if (type == 0) {
									sex = newSex;
									body = response.body;
									setStyle(response.subType);
									if (response.skill)
										skillList.setSkill(response.skill);
									else
										skillList.reset();
									data[5] = null;
									data[6] = null;
								} else if (type >= 2) {
									itemPane.attr({
										title: localize(response, "name"),
										content: '<img class="res" src="image/' + response.image + '.png" />'
											+ '<div class="dijitInline pre_wrap">' + localize(response, "desc") + '</div>'
									});
									itemPane.show();
									if (type == 2 || type == 3 || type == 4) {
										for (var i = 0; i < subTypes.length; i++) {
											if (i != type && subTypes[i] !== undefined && (subTypes[i] & response.subType) != 0) {
												delete data[i];
												delete subTypes[i];
												delete self.items[i];
											}
										}
										subTypes[type] = response.subType;
									} else if (type == 11) {
										cardPane.attr('content', '<img src="image/' + response.card + '.png" />');
										cardPane.show();
									}
								}
							}
						}));
					}
				}
			})(type, items[type]);
		}
		if (!changed)
			return;
		new dojo.DeferredList(deferreds).addCallback(function () {
			self.updateHref();
			self.attr({ title: self.getTitle() });
			preview.update();
		});
	},
	_save: function () {
		dojo.cookie('link', this.getChildren().map(function (child) { return child.href.substr(1); }).join('-'), { expires: 7 });
	},
	_onClick: function (e) {
		dojo.stopEvent(e);
	},
	_onSaveClick: function (e) {
		var href = this.href;
		var children = this.getChildren().filter(function (child) { return child.href == href; });
		this.addChild(children.length > 0 ? children[0] : new sayuri.LinkItem({ items: this.items, href: this.href, title: this.title }), "first");
		this._save();
	},
	removeChild: function (child) {
		this.inherited(arguments);
		this._save();
	}
});

dojo.declare("sayuri.Tree", dijit.Tree, {
	showRoot: false,
	openOnDblClick: true,
	type: 0,
	sex: "",
	_createTreeNode: function (args) {
		return new sayuri.TreeNode(args);
	},
	onClick: function (item, node) {
		var items = {};
		items[this.type] = item;
		if (this.type == 0 && sex != this.sex)
			bodyStore.fetchItemByIdentity({
				identity: "1",
				onItem: function (item) {
					items[1] = item;
					link.updateItems(items);
				}
			});
		else
			link.updateItems(items);
	}
});

dojo.declare("sayuri.HairTree", sayuri.Tree, {
	// add onmousemove event
	templateString: dojo.cache("dijit", "templates/Tree.html",
		'<div class="dijitTree dijitTreeContainer" waiRole="tree" dojoAttachEvent="onkeypress:_onKeyPress, onmousemove:onMouseMove">'
		+ '<div class="dijitInline dijitTreeIndent" style="position: absolute; top: -9999px" dojoAttachPoint="indentDetector">'
		+ '</div>'
		+ '</div>'),
	postCreate: function () {
		this.inherited(arguments);
		this.imgNodes = {
			m: dojo.place('<div style="visibility: hidden; position: absolute; z-index: 100; width: 200px; height: 160px; background: url(hairm.png) no-repeat;"></div>', dojo.body()),
			f: dojo.place('<div style="visibility: hidden; position: absolute; z-index: 100; width: 200px; height: 160px; background: url(hairf.png) no-repeat;"></div>', dojo.body())
		};
	},
	_onNodeMouseEnter: function (node, evt) {
		this.imgNode = this.imgNodes[sex];
		dojo.style(this.imgNode, {
			left: (evt.pageX + 10) + "px",
			top: (evt.pageY + 10) + "px",
			backgroundPosition: (1 - parseInt(this.model.getIdentity(node.item), 10)) * 200 + "px 0",
			visibility: "visible"
		});
	},
	onMouseMove: function (evt) {
		if (this.imgNode) {
			dojo.style(this.imgNode, {
				left: (evt.pageX + 10) + "px",
				top: (evt.pageY + 10) + "px"
			});
		}
	},
	_onNodeMouseLeave: function (node, evt) {
		this.imgNode.style.visibility = "hidden";
		this.imgNode = null;
	}
});

dojo.declare("sayuri.List", dijit._Widget, {
	"class": "dijitTreeContainer",
	store: null,
	type: 0,
	postCreate: function () {
		this.inherited(arguments);
		var self = this;
		self.connect(self.domNode, "onclick", self._onClick);
		self.connect(self.domNode, "ondblclick", self._onDblClick);
		self.connect(self.domNode, "onkeypress", self._onKeyPress);
		self.store.fetchItemByIdentity({
			identity: -self.type,
			onItem: function (root) {
				var items = [];
				self.items = self.store.getValues(root, "c");
				dojo.forEach(self.items, function (item) {
					items.push(
						'<div class="dijitTreeRow"><span class="dijitTreeContent dijitTreeLabel" tabindex="-1">',
						self.store.getLabel(item),
						'</span></div>'
					);
				});
				self.domNode.innerHTML = items.join('');
				var labels = self.labels = dojo.query("span", self.domNode);
				labels.forEach(function (label) {
					dojo.connect(label, "onfocus", self, "_onLabelFocus");
					dojo.connect(label, "onblur", self, "_onLabelBlur");
					var row = label.parentNode;
					dojo.connect(row, "onmouseenter", self, "_onMouseEnter");
					dojo.connect(row, "onmouseleave", self, "_onMouseLeave");
				});
				self.setSelected(labels[0], true);
				self.lastFocused = labels[0];
				self.updateItemClasses();
			}
		});
	},
	updateItemClasses: function () {
	},
	onClick: function (item) {
		var items = {};
		items[this.type] = item;
		link.updateItems(items);
	},
	onDblClick: function () {
	},
	_onKeyPress: function (/*Event*/e) {
		if (e.altKey)
			return;
		var node = e.target;
		if (node.tagName != "SPAN") {
			var nodes = dojo.query("span", node);
			if (nodes.length != 1)
				return;
			node = nodes[0];
		}

		var dk = dojo.keys;
		var key = e.charOrCode;
		if (typeof key == "string") {
			if (!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) {
				this._onLetterKeyNav({ node: node, key: key.toLowerCase() });
				dojo.stopEvent(e);
			}
		} else {
			var map = this._keyHandlerMap;
			if (!map) {
				map = {};
				map[dk.ENTER] = "_onEnterKey";
				map[dk.UP_ARROW] = "_onUpArrow";
				map[dk.DOWN_ARROW] = "_onDownArrow";
				map[dk.HOME] = "_onHomeKey";
				map[dk.END] = "_onEndKey";
				this._keyHandlerMap = map;
			}
			if (map[key]) {
				this[map[key]]({ node: node/*, item: treeNode.item*/ });
				dojo.stopEvent(e);
			}
		}
	},
	_onEnterKey: function (/*Object*/message) {
		//this._publish("execute", { item: message.item, node: message.node} );
		this.onClick(this.items[this.labels.indexOf(message.node)], message.node);
	},
	_onDownArrow: function (/*Object*/message) {
		var index = this.labels.indexOf(message.node);
		if (index >= 0 && index < this.labels.length)
			this.labels[index + 1].focus();
	},
	_onUpArrow: function (message) {
		var index = this.labels.indexOf(message.node);
		if (index > 0)
			this.labels[index - 1].focus();
	},
	_onHomeKey: function () {
		this.labels[0].focus();
	},
	_onEndKey: function () {
		this.labels[this.labels.length - 1].focus();
	},
	_onLetterKeyNav: function (message) {
		var index = this.labels.indexOf(message.node);
		var startIndex = index;
		do {
			if (++index == this.labels.length)
				index = 0;
		} while (index != startIndex && this.labels[index].innerHTML.charAt(0).toLowerCase() != message.key);
		if (index != startIndex)
			this.labels[index].focus();
	},
	_onClick: function (e) {
		var node = e.target;
		if (node.tagName != "SPAN") {
			var nodes = dojo.query("span", node);
			if (nodes.length != 1)
				return;
			node = nodes[0];
		}
		//this._publish("execute", { item: nodeWidget.item, node: nodeWidget });
		this.onClick(this.items[this.labels.indexOf(node)], node);
		node.focus();
		dojo.stopEvent(e);
	},
	_onDblClick: function (e) {
		var node = e.target;
		if (node.tagName != "SPAN") {
			var nodes = dojo.query("span", node);
			if (nodes.length != 1)
				return;
			node = nodes[0];
		}
		//this._publish("execute", { item: nodeWidget.item, node: nodeWidget });
		this.onDblClick(this.items[this.labels.indexOf(node)], node);
		node.focus();
		dojo.stopEvent(e);
	},
	setSelected: function (node, selected) {
		node.setAttribute("tabIndex", selected ? "0" : "-1");
		dojo.toggleClass(node.parentNode, "dijitTreeRowSelected", selected);
	},
	_onLabelFocus: function (evt) {
		var node = evt.target;
		dojo.addClass(node, "dijitTreeLabelFocused");
		if (node != this.lastFocused)
			this.setSelected(this.lastFocused, false);
		this.setSelected(node, true);
		this.lastFocused = node;
	},
	_onLabelBlur: function (evt) {
		dojo.removeClass(evt.target, "dijitTreeLabelFocused");
	},
	_onMouseEnter: function (evt) {
		var node = evt.target;
		if (node.tagName == "SPAN")
			node = node.parentNode;
		dojo.addClass(node, "dijitTreeRowHover");
	},
	_onMouseLeave: function (evt) {
		var node = evt.target;
		if (node.tagName == "SPAN")
			node = node.parentNode;
		dojo.removeClass(node, "dijitTreeRowHover");
	},
	resize: function (changeSize) {
		if (changeSize) {
			dojo.marginBox(this.domNode, changeSize);
			dojo.style(this.domNode, "overflow", "auto");
		}
	}
});

dojo.declare("sayuri.WeaponList", sayuri.List, {
	lists: [],
	postCreate: function () {
		this.inherited(arguments);
		this.lists.push(this);
	},
	updateItemClasses: function () {
		var self = this;
		this.labels.forEach(function (node, i) {
			var item = self.items[i];
			var subType = self.store.getValue(item, sex + "s");
			dojo.toggleClass(node, "noAct", !(subType & 1 << weaponSubType));
		});
	}
});

dojo.declare("sayuri.TabContainer", dijit.layout.TabContainer, {
	// selectChild() return loadDeferred
	selectChild: function (page) {
		page = dijit.byId(page);
		var loadDeferred;
		if (this.selectedChildWidget != page) {
			loadDeferred = this._transition(page, this.selectedChildWidget);
			this.selectedChildWidget = page;
			dojo.publish(this.id + "-selectChild", [page]);
			if (ga && ga.tracker)
				ga.tracker._trackEvent("Tabs", page.title);
		} else {
			loadDeferred = new dojo.Deferred();
			loadDeferred.callback();
		}
		return loadDeferred;
	},
	_transition: function (newWidget, oldWidget) {
		if (oldWidget)
			this._hideChild(oldWidget);
		var loadDeferred = this._showChild(newWidget);
		if (newWidget.resize) {
			if (this.doLayout)
				newWidget.resize(this._containerContentBox || this._contentBox);
			else
				newWidget.resize();
		}
		return loadDeferred;
	},
	_showChild: function (page) {
		var children = this.getChildren();
		page.isFirstChild = (page == children[0]);
		page.isLastChild = (page == children[children.length - 1]);
		page.selected = true;
		dojo.removeClass(page.domNode, "dijitHidden");
		dojo.addClass(page.domNode, "dijitVisible");
		return page._onShow();
	}
});

dojo.declare("sayuri.TreePane", dijit.layout.ContentPane, {
	// _onShow() returns loadDeferred
	className: "",
	model: null,
	type: 0,
	sex: "",
	_onShow: function () {
		var loadDeferred;
		if (!this._wasShown) {
			var args = { type: this.type };
			if (this.model) {
				args.model = this.model;
				args.sex = this.sex;
			} else
				args.store = itemStore;
			loadDeferred = this.attr("content", new sayuri[this.className](args));
		} else {
			loadDeferred = new dojo.Deferred();
			loadDeferred.callback();
		}
		this.inherited(arguments);
		return loadDeferred;
	}
});

dojo.declare("sayuri.TitlePane", dijit.TitlePane, {
	// remove onclick:_onTitleClick, arrowNode, arrowNodeInner
	// add close button, support d&d at focusNode, but removed support of open / close
	_state: { offset: 0, zIndex: 0 },
	closable: true,
	templateString:
		'<div>'
		+ '<div class="dijitTitlePaneTitle" dojoAttachPoint="titleBarNode">'
		+ '<div class="dijitTitlePaneTitleFocus" dojoAttachPoint="focusNode" dojoAttachEvent="onmousedown:_onTitleClick">'
		+ '<span dojoAttachPoint="closeButtonNode" class="dijitDialogCloseIcon" dojoAttachEvent="onclick:hide"></span>'
		+ '<span dojoAttachPoint="titleNode" class="dijitTitlePaneTextNode"></span>'
		+ '</div>'
		+ '</div>'
		+ '<div class="dijitTitlePaneContentOuter" dojoAttachPoint="hideNode" waiRole="presentation">'
		+ '<div class="dijitReset" dojoAttachPoint="wipeNode" waiRole="presentation">'
		+ '<div class="dijitTitlePaneContentInner" dojoAttachPoint="containerNode" waiRole="region" tabindex="-1" id="${id}_pane">'
		+ "<!-- nested divs because wipeIn()/wipeOut() doesn't work right on node w/padding etc.  Put padding on inner div. -->"
		+ '</div>'
		+ '</div>'
		+ '</div>'
		+ '</div>',
	postCreate: function () {
		this._cookieName = this.id + "_pos";
		if (!this.closable)
			this.closeButtonNode.style.display = "none";
		else
			this._trackMouseState(this.closeButtonNode, "dijitDialogCloseIcon");
		this._movable = new dojo.dnd.Moveable(this.domNode, { handle: this.focusNode });
		dojo.subscribe("/dnd/move/stop", this, "_endDrag");
		this.inherited(arguments);
		if (this.domNode.style.display != "none")
			this.show();
	},
	destroy: function () {
		this._movable.destroy();
		this.inherited(arguments);
	},
	show: function () {
		if (!this.positioned) {
			var cookie = dojo.cookie(this._cookieName);
			if (cookie)
				var position = cookie.split('!');
			else {
				position = [this._state.offset + "px", this._state.offset + "px"];
				this._state.offset += 25;
			}
			dojo.style(this.domNode, {
				top: position[1],
				left: position[0],
				zIndex: this._state.zIndex,
				position: "absolute"
			});
			this._state.zIndex++;
			this.positioned = true;
		}
		this.domNode.style.display = "";
		this._onShow();
	},
	hide: function () {
		this.domNode.style.display = "none";
		this.onHide();
	},
	_setCss: function () {
	},
	_onTitleClick: function () {
		this.domNode.style.zIndex = this._state.zIndex++;
	},
	_endDrag: function (e) {
		if (e && e.node && e.node === this.domNode) {
			var style = this.domNode.style;
			dojo.cookie(this._cookieName, style.left + "!" + style.top, { expires: 365 });
		}
	}
});

dojo.declare("sayuri.SkillItem", [dijit._Widget, dijit._Templated], {
	templateString: '<td>'
		+ '<div dojoAttachPoint="titleNode" style="width: 60px; overflow: hidden; white-space: nowrap; -o-text-overflow: ellipsis; text-overflow: ellipsis; font-size: 90%;"></div>'
		+ '<img dojoAttachPoint="iconNode" src="${_blankGif}" style="display: none; width: 24px; height: 24px;" dojoAttachEvent="onmouseenter:onMouseEnter, onmousemove:onMouseMove, onmouseleave:onMouseLeave" />'
		+ '</td>',
	setItem: function (skill) {
		this.titleNode.innerHTML = skill.n;
		dojo.style(this.iconNode, {
			backgroundPosition: -skill.o + "px 0",
			display: "inline"
		});
		this.description = skill.d;
	},
	reset: function () {
		this.titleNode.innerHTML = "";
		this.iconNode.style.display = "none";
		this.description = null;
	},
	onMouseEnter: function (evt) {
		if (this.description) {
			this.list.popupNode.innerHTML = this.description;
			dojo.style(this.list.popupNode, {
				left: (evt.pageX + 10) + "px",
				top: (evt.pageY + 10) + "px",
				visibility: "visible"
			});
		}
	},
	onMouseMove: function (evt) {
		dojo.style(this.list.popupNode, {
			left: (evt.pageX + 10) + "px",
			top: (evt.pageY + 10) + "px"
		});
	},
	onMouseLeave: function (evt) {
		this.list.popupNode.style.visibility = "hidden";
	}
});

dojo.declare("sayuri.SkillList", [dijit._Widget, dijit._Templated], {
	templateString: '<div><table style="border-spacing: 5px;">'
	+ '<tbody dojoAttachPoint="tableNode" style="text-align: center;"></tbody>'
	+ '</table></div>',
	postCreate: function () {
		this.inherited(arguments);
		this.popupNode = dojo.place('<div style="visibility: hidden; position: absolute; z-index: 100; width: 300px; background-color: #ffffff; border: solid 1px #777777; padding: 5px;"></div>', dojo.body()),
		this.items = [];
		for (var i = 0; i < 8; i++) {
			var tr = dojo.create('tr', null, this.tableNode);
			for (var j = 0; j < 7; j++) {
				var item = new sayuri.SkillItem({ list: this });
				item.placeAt(tr);
				this.items.push(item);
			}
		}
	},
	_setSkillInternal: function () {
		var self = this;
		self.reset();
		dojo.forEach(self.skills, function (skill) {
			self.items[skill.i].setItem(self.skillData[skill.s]);
		});
	},
	onShow: function () {
		if (this.enabled)
			return;
		var self = this;
		dojo.xhrGet({
			url: localize('Skill'),
			handleAs: 'json',
			load: function (response) {
				var skillData = {};
				dojo.forEach(response, function (item) {
					skillData[item.i] = item;
				});
				self.skillData = skillData;
				self._setSkillInternal();
				self.enabled = true;
			}
		});
		dojo.forEach(self.items, function (item) {
			item.iconNode.style.backgroundImage = "url(skill.png)";
		});
	},
	setSkill: function (skills) {
		this.skills = skills;
		if (this.enabled)
			this._setSkillInternal();
	},
	reset: function () {
		dojo.forEach(this.items, function (item) {
			item.reset();
		});
	}
});

var searchKeys;
var onSearchButtonClicked = function () {
	if (!searchKeys) {
		dojo.xhrGet({
			url: localize('SearchKey'),
			handleAs: 'json',
			sync: true,
			load: function (response) {
				searchKeys = response;
			}
		});
	}
	searchPane.show();
	searchKey.focus();
};
var onSaveButtonClicked = function () {
	savePane.show();
};
var onSkillButtonClicked = function () {
	skillPane.show();
};
function onBackgroundButtonClicked() {
	gallery.setDataStore(new dojo.data.ItemFileReadStore({ url: "LoginScreen.txt" }), {});
	galleryPane.show();
}

var normalize = (function () {
	// This function contains Japanese characters.
	var list =
		("ｦ を ｧ ぁ ｨ ぃ ｩ ぅ ｪ ぇ ｫ ぉ ｬ ゃ ｭ ゅ ｮ ょ ｯ っ ｰ ー ｱ あ ｲ い ｳ う ｴ え ｵ お ｶ か ｷ き ｸ く ｹ け ｺ こ "
		+ "ｻ さ ｼ し ｽ す ｾ せ ｿ そ ﾀ た ﾁ ち ﾂ つ ﾃ て ﾄ と ﾅ な ﾆ に ﾇ ぬ ﾈ ね ﾉ の ﾊ は ﾋ ひ ﾌ ふ ﾍ へ ﾎ ほ "
		+ "ﾏ ま ﾐ み ﾑ む ﾒ め ﾓ も ﾔ や ﾕ ゆ ﾖ よ ﾗ ら ﾘ り ﾙ る ﾚ れ ﾛ ろ ﾜ わ ﾝ ん "
		+ "ｳﾞ ゔ ｶﾞ が ｷﾞ ぎ ｸﾞ ぐ ｹﾞ げ ｺﾞ ご ｻﾞ ざ ｼﾞ じ ｽﾞ ず ｾﾞ ぜ ｿﾞ ぞ ﾀﾞ だ ﾁﾞ ぢ ﾂﾞ づ ﾃﾞ で ﾄﾞ ど "
		+ "ﾊﾞ ば ﾊﾟ ぱ ﾋﾞ び ﾋﾟ ぴ ﾌﾞ ぶ ﾌﾟ ぷ ﾍﾞ べ ﾍﾟ ぺ ﾎﾞ ぼ ﾎﾟ ぽ").split(' ');
	var map = {};
	for (var i = 0; i < list.length; i += 2)
		map[list[i]] = list[i + 1];
	return function (value) {
		value = value.toLowerCase().replace(/(?:([ァ-ヶ])|([ｳｶ-ﾄ]ﾞ|[ﾊ-ﾎ][ﾞﾟ]|[ｦ-ﾝ]))/g,
			function ($0, $1, $2) {
				return $1 ? String.fromCharCode($1.charCodeAt(0) - 0x60) : map[$2];
			}
		);
		value = value.replace('ー', '');
		value = value.replace(/[:―.,。、]+/g, " ");
		value = value.replace(/(?:重量|系列|装備|要求れべる|防御|武器れべる|攻撃|位置|属性|種類|職業)/g, " ");
		value = value.replace(/\b(?:weight|class|level|jobs|strength|defense|rate|position|on|slot|a|the|of|that|to|and|by|required|item|is|with|be|an|in|for|from|it|this|can|when|which|has|or|classes)\b/g, '');
		return value.split(/\s+/);
	};
})();

var onSearchKeyChanged = function (newValue) {
	searchResult.destroyDescendants();
	if (!newValue)
		return;
	var childPanes = panes.getChildren();
	var words = normalize(newValue);
	var i = 0;
	dojo.forEach(searchKeys, function (key) {
		if (i > 20 || !dojo.every(words, function (word) { return key[1].indexOf(word) >= 0; }))
			return;
		i++;
		var pane = childPanes[key[2] + 1];
		var id = key[3];
		searchResult.addChild(new dijit.MenuItem({
			label: key[0],
			onClick: function () {
				panes.selectChild(pane).addCallback(function () {
					var list = pane.getChildren()[0];
					list.store.fetchItemByIdentity({
						identity: id,
						onItem: function (item) {
							list.onClick(item);
							list.labels[dojo.indexOf(list.items, item)].focus();
						}
					});
				});
			}
		}));
	});
};

var onBackgroundOpen = function () {
	if (thumbnail.store == null) {
		thumbnail.setDataStore(new dojo.data.ItemFileReadStore({ data: { items: [
			{ imageUrlThumb: "bg0.png" },
			{ imageUrlThumb: "bg1.png" },
			{ imageUrlThumb: "bg2.png" },
			{ imageUrlThumb: "bg3.png" },
			{ imageUrlThumb: "bg4.png" }
		]}}), {});
		dojo.subscribe(thumbnail.getClickTopicName(), function (e) {
			preview.main.style.backgroundImage = "url(" + e.url + ")";
		});
	}
	dijit.TooltipDialog.prototype.onOpen.apply(this, arguments);
};

var onStateChanged = function (value) {
	state = value;
	if (state == 1 || state == 3 || state == 4 || state == 5)
		headDirection = 0;
	preview.update();
};

var onOtherOpen = function () {
	this.reset();
	dijit.TooltipDialog.prototype.onOpen.apply(this, arguments);
};
var onOtherExecute = function (values) {
	var other = values.other;
	if (other) {
		if (other[0] == "f") {
			data[7] = defaults[other];
			data[8] = null;
		} else {
			data[7] = null;
			data[8] = defaults[other];
		}
	}
	var types = values.takeoff;
	if (types)
		dojo.forEach(types, function (type) {
			data[type] = null;
			if (type == 7)
				data[8] = null;
		});
	preview.update();
};

var maleBodyModel = new dijit.tree.TreeStoreModel({ store: bodyStore, childrenAttrs: ["c"], query: { i: 'MaleBody'} });
var femaleBodyModel = new dijit.tree.TreeStoreModel({ store: bodyStore, childrenAttrs: ["c"], query: { i: 'FemaleBody'} });
var hairModel = new dijit.tree.TreeStoreModel({ store: bodyStore, childrenAttrs: ["c"], query: { i: 'Hair'} });

dojo.xhrGet({
	url: 'Defaults.txt',
	handleAs: 'json',
	load: function (response) {
		defaults = response;
	}
});

var loadDeferred = new dojo.Deferred();
dojo.addOnLoad(loadDeferred, "callback");

new dojo.DeferredList([bodyDeferred, itemDeferred, loadDeferred]).addCallback(function () {
	link.setHref(dojo.hash());
	dojo.byId("loadtime").innerHTML = ((new Date()).getTime() - start.getTime()) / 1000.;
	dojo.body().removeChild(dojo.byId("loader"));
});
