﻿
Element.implement({

	toggleDisplay: function(state, d){
	
		if(!this.nativeDisplay){ if(!d) var d = this.getStyle("display"); this.nativeDisplay = (d == "none") ? "block" : d; }
		if((this.getStyle("display") == "none" && !state) || state == "on") this.setStyle("display", this.nativeDisplay);
		else this.setStyle("display", "none");
	},
	hide: function(){ this.setStyle("display", "none"); },
	show: function(d){ if(!d) d = "block"; this.setStyle("display", d); }
});


var QuickKanji = {
	init: function(){
		var Buttons = this.Menu.Buttons;
		
		for(instance in Buttons){
		
			var button = Buttons[instance];
			button.addEvent("click", this.Effects.toggleButton);
		}
		
		var stageTabs = [this.Stage.Radical, this.Stage.Strokes, this.Stage.On, this.Stage.Kun, this.Stage.Level];
		
		for(i=0;i<stageTabs.length;i++){
		
			var tab = stageTabs[i];
			tab.addEvent("mouseover", this.Effects.hoverTab);
			tab.addEvent("mouseout", this.Effects.unhoverTab);
		}
		
		Buttons.All.addEvent("click", this.Requests.send.bind(Buttons.All, "all"));
		Buttons.Radical.addEvent("click", this.Requests.send.bind(Buttons.Radical, "radicals"));
		Buttons.Strokes.addEvent("click", this.Requests.send.bind(Buttons.Strokes, "strokes"));
			
		Buttons.Level.addEvent("click", this.injectList.bind(Buttons.Level, "level", this.Levels));
		Buttons.On.addEvent("click", this.injectList.bind(Buttons.On, "onSyllable", this.Syllables));
		Buttons.Kun.addEvent("click", this.injectList.bind(Buttons.Kun, "kunSyllable", this.Syllables));
		
		this.Stage.Radical.addEvent("click", QuickKanji.Requests.send.bind(this.Stage.Radical, "listByRadical"));
		this.Stage.Strokes.addEvent("click", QuickKanji.Requests.send.bind(this.Stage.Strokes, "listByStrokes"));
		this.Stage.Level.addEvent("click", QuickKanji.Requests.send.bind(this.Stage.Level, "listByLevel"));
		
		this.Stage.Compounds.out = false;
		this.Stage.More.addEvent("click", QuickKanji.Effects.expandList.bind(this.Stage.More, this.Stage.Compounds));
		
		this.Stage.AddCompound.show("inline");
		this.Stage.AddCompound.addEvent("click", function(){
		
			this.Tabs.show.bind(this.Tabs)("contribute");
			this.Contribute.show("compound");
			this.Contribute.Compound.Form.compound.focus();
		
		}.bind(this));
		
		this.Voting.Kanji.No.addEvent("click", this.Voting.sendVote.bind(this.Voting.Kanji,"kanji","no"));
		this.Voting.Kanji.Yes.addEvent("click", this.Voting.sendVote.bind(this.Voting.Kanji,"kanji","yes"));
		
		this.Voting.Compound.No.addEvent("click", this.Voting.sendVote.bind(this.Voting.Compound,"compound","no"));
		this.Voting.Compound.Yes.addEvent("click", this.Voting.sendVote.bind(this.Voting.Compound,"compound","yes"));
		
		this.Menu.Search.onsubmit = function(){
			
			var text = this.text.value.clean();
			
			if(text.match(/[a-z0-9]/i) && text.length < 3){
			
				alert("Sorry, searches in English need to be at least 3 letters");
				return false;
			}
			this.text.value = text;
			var sendSearch = QuickKanji.Requests.send.bind(this.text);
			sendSearch("search", text);
			return false;
		}
		
		this.Menu.Menu.removeClass('loading');
		
		window.addEvent("scroll", function(){
		
			if(this.Stage.docked) return;
			$clear(this.timer);	
			this.timer = function(){
			
				var scrollTop = window.getScrollTop();
				var panelTop  = this.Stage.Panel.getStyle("top").toInt();
				if(scrollTop > 110 || panelTop > 0){
					var scroll = scrollTop - 100;
					if(scroll < 0) scroll = 0;
					this.Effects.panelSlide.start(scroll);
				}
			}.bind(this).delay(2000);
		}.bind(this));
		
		this.Stage.Pin.addEvent("click", function(){
			
			if(this.docked){
				this.docked = false;
				this.Pin.src = QuickKanji.Img.PinOut;
				this.Pin.title = QuickKanji.Text.Undocked;
			} else {
				this.docked = true;
				this.Pin.src = QuickKanji.Img.PinIn;
				this.Pin.title = QuickKanji.Text.Docked;
				this.Panel.setStyle("top", 0);
			}	
		
		}.bind(this.Stage));
		
		
		// Init the contribute area
		
		this.Contribute.Kanji.Form.onsubmit = function(){
			
			var add = {
				type:this.type.value,
				kanji:this.kanji.value,
				radical:this.radical.value,
				strokes:this.strokes.value,
				on:this.on.value,
				kun:this.kun.value,
				meaning:this.meaning.value,
				notes:this.notes.value
			}
			
			QuickKanji.Contribute.Kanji.Loading.show("inline");
			QuickKanji.Contribute.Kanji.Message.empty();
			QuickKanji.Requests.contribute.post(add);
			return false;		
		}
		
		this.Contribute.Compound.Form.onsubmit = function(){
		
			var add = {
				type:this.type.value,
				compound:this.compound.value,
				reading:this.reading.value,
				meaning:this.meaning.value
			}
			QuickKanji.Contribute.Compound.Loading.show("inline");
			QuickKanji.Requests.contribute.post(add);
			QuickKanji.Contribute.Compound.Message.empty();
			return false;
		}
		
		this.Contribute.Translation.Form.onsubmit = function(){
		
			var add = {
				type: this.type.value,
				id: this.id.value,
				translation: this.text.value
			}
			QuickKanji.Contribute.Translation.Loading.show("inline");
			QuickKanji.Requests.contribute.post(add);
			QuickKanji.Contribute.Translation.Message.empty();
			return false;
		}
		
		this.Contribute.Example.Form.onsubmit = function(){
			
			var add = {
				type:this.type.value,
				compound:this.compound.value,
				sentence:this.sentence.value,
				reading:this.reading.value,
				meaning:this.meaning.value
			}
			QuickKanji.Contribute.Example.Loading.show("inline");
			QuickKanji.Requests.contribute.post(add);
			QuickKanji.Contribute.Example.Message.empty();
			return false;
		}
		
		this.Contribute.Kanji.Kanji.addEvent("blur", function(){
		
			var kanji = this.get('value');
			var larger = $("contribute_larger");
			larger.setStyle("color", "#933");
			larger.set('text', kanji);
		});
				
		this.Contribute.Kanji.Button.addEvent("click", this.Contribute.show.pass("kanji"));
		this.Contribute.Compound.Button.addEvent("click", this.Contribute.show.pass("compound"));
		
		
		// Init the stage tabs
		
		this.Tabs.Info.addEvent("click", this.Tabs.show.bind(this.Tabs, "stage"));
		this.Tabs.Contribute.addEvent("click", this.Tabs.show.bind(this.Tabs, "contribute"));

		this.Tabs.Chat.addEvent("click", function(){			
			var new_id = (this.id == "tabs_chat_off") ? "tabs_chat_on" : "tabs_chat_off";
			this.id = new_id;
			$("chat").toggleDisplay();
		});
		
		
		this.Chat.Login.onsubmit = function(){
					
			var name = this.username.value;
			var last = QuickKanji.Chat.lastLine;
			if(!name) return false;
			QuickKanji.Chat.tempName = name;
			QuickKanji.Requests.chat.post({action:'login',name:name,last:last});
			return false;		
		}
		
		this.Chat.Form.onsubmit = function(){
		
			var message = this.message.value;
			if(!message.clean()) return false;
			var name    = QuickKanji.Chat.Name;
			var last    = QuickKanji.Chat.lastLine;
			QuickKanji.Requests.chat.post({action:'send',name:name,last:last,message:message});
			this.message.value = "";
			return false;	
		}
		
		this.Chat.Logout.addEvent("click", function(){
			if(!this.Name) return;
			QuickKanji.Requests.chat.post({action:'logout',name:this.Name});
		}.bind(this.Chat));
		
		this.Stage.Roll.state = "out";
		this.Stage.Roll.addEvent("click", function(){
		
			var active = QuickKanji.Stage.active;
			var box = (active == "stage") ? $("stage") : $("contribute");
			var effect = (active == "stage") ? QuickKanji.Effects.rollStage : QuickKanji.Effects.rollContribute;
			
			if(this.state == "out"){
				var offset = -(box.getStyle("height").toInt());
				effect.start({"top": offset, "marginTop": offset});
				this.state = "in";
				this.src = QuickKanji.Img.RollDown;
			} else {
				effect.start({"top": 0, "marginTop": 0, "marginBottom": 0});
				this.state = "out";
				this.src = QuickKanji.Img.RollUp;
			}
		});
		
		// Init the buttons to add items to the vocab list
		this.Vocab.Kanji.Button.addEvent("click", this.Vocab.handleVocab.bind(this.Vocab.Kanji, "kanji"));
		this.Vocab.Compound.Button.addEvent("click", this.Vocab.handleVocab.bind(this.Vocab.Compound, "compound"));
		
		// Init the vocab list (cookie) itself
		var kanjiCookie = Cookie.read("vocab_kanji") || "";
		var compoundCookie = Cookie.read("vocab_compound") || "";
		this.Vocab.Kanji.list = kanjiCookie.split("|").clean();
		this.Vocab.Compound.list = compoundCookie.split("|").clean();
		
		// Init the tabs to swap between kanji and compound lists
		this.Vocab.Kanji.Tab.addEvent("click", this.Vocab.getList.pass("kanji"));
		this.Vocab.Compound.Tab.addEvent("click", this.Vocab.getList.pass("compound"));
		
		// Init the menu button to call up the list
		this.Menu.Vocab.addEvent("click", function(e){
			var event = new Event(e);
			var list = (event.control) ? "compound" : "kanji";
			QuickKanji.Content.Tabs.show();
			this.getList(list);
		}.bind(this.Vocab));
		
		document.addEvent("keydown", function(e){
				
			if(!QuickKanji.current) return;
			var event = new Event(e);
			if(e.key == "right"){
				var next = QuickKanji.current.getNext();
				if(next) next.fireEvent("click", true);
			} else if(e.key == "left"){
				var prev = QuickKanji.current.getPrevious();
				if(prev) prev.fireEvent("click", true);
			}
		});
		
		this.Stage.active = "stage";
		
		// Get the "Kanji of the Day"
		this.Requests.send("daily");
		
		// Preload the other images
		for(i=0;i<this.Img.Preload.length;i++){
		
			var src = "images/" + this.Img.Preload[i];			
			new Asset.image(src);		
		}
		
	},
	
	Menu: {
	
		Menu: $("menu"),
		Buttons: {
			All: $("menu_all"),
			Radical: $("menu_radical"),
			Strokes: $("menu_strokes"),
			Level: $("menu_level"),
			On: $("menu_on"),
			Kun: $("menu_kun")
		},
		Search: $("menu_search"),
		Vocab: $("menu_vocab"),
		currentButton: null
	},
	
	Content: {
	
		Tabs: $("vocab_buttons"),
		List: $("list"),
		Compound: {
			Container: $("comp"),
			Japanese: $("compound_japanese"),
			Meaning: $("compound_meaning"),
			Examples: $("compound_examples"),
			AddExample: $("compound_addexample"),
			Translation: $("compound_translation")
		}	
	},
	
	Stage: {
	
		Panel: $("panel"),
		Message: $("stage_message"),
		Kanji: $("stage_kanji"),
		Radical: $("stage_radical"),
		Strokes: $("stage_strokes"),
		On: $("stage_on"),
		Kun: $("stage_kun"),
		Level: $("stage_level"),
		ShiftJIS: $("stage_shiftjis"),
		Meaning: $("stage_meaning"),
		Compounds: $("stage_compounds"),
		More: $("stage_more"),
		AddCompound: $("stage_addcompound"),
		Voting: $("stage_voting"),
		Pin: $("stage_pin"),
		Roll: $("stage_roll"),
		docked: true
	},
	
	Vocab: {
		Kanji: {
			Button: $("stage_vocab"),
			Tab: $("vocab_buttons_kanji"),
			Message: $("stage_message"),
			cookie: "vocab_kanji"
		},
		Compound: {
			Button: $("compound_vocab"),
			Tab: $("vocab_buttons_compound"),
			Message: $("compound_vocab_message"),
			cookie: "vocab_compound"
		},	
		initIcon: function(area,vocab){
		
			var img = area.Button;
			var cookie = area.currentCookie;
			if(area.list.contains(vocab)){
				area.vocabAction = "remove";
				img.src = QuickKanji.Img.VocabRemove;
				var title = (area == QuickKanji.Vocab.Compound)
					? QuickKanji.Text.VocabCompoundRemove
					: QuickKanji.Text.VocabRemove;
				img.title = title;
			} else {
				area.vocabAction = "add";
				img.src = QuickKanji.Img.VocabAdd;
				var title = (area == QuickKanji.Vocab.Compound)
					? QuickKanji.Text.VocabCompoundAdd
					: QuickKanji.Text.VocabAdd;
				img.title = title;
			}
			
			area.currentVocab = vocab;
			img.show();
		},
		handleVocab: function(type){
		
			var img = this.Button;
			
			if(this.vocabAction == "add"){
				this.list.push(this.currentVocab);
				img.src = QuickKanji.Img.VocabRemove;
				img.title = QuickKanji.Text.VocabRemove;
				this.vocabAction = "remove";
				var text = (type == "compound")
					? QuickKanji.Text.VocabCompoundAdded
					: QuickKanji.Text.VocabAdded;
				this.Message.set('text', text);
				this.Message.setProperty("class", "pos");
			} else if(this.vocabAction == "remove"){
				this.list.erase(this.currentVocab);
				img.src = QuickKanji.Img.VocabAdd;
				img.title = QuickKanji.Text.VocabAdd;
				this.vocabAction = "add";
				var text = (type == "compound")
					? QuickKanji.Text.VocabCompoundRemoved
					: QuickKanji.Text.VocabRemoved;
				this.Message.set('text', text);
				this.Message.setProperty("class", "neg");
			}
			
			Cookie.write(this.cookie, this.list.join("|"), {duration: 365});
		},
		getList: function(type){
		
			var vocab = QuickKanji.Vocab;
			
			if(type == "kanji"){
				var list = vocab.Kanji.list;
				var activeTab   = vocab.Kanji.Tab;
				var inactiveTab = vocab.Compound.Tab;
			} else {
				var list = vocab.Compound.list;
				var activeTab   = vocab.Compound.Tab;
				var inactiveTab = vocab.Kanji.Tab;
			}
			
			activeTab.addClass("active");
			inactiveTab.removeClass("active");

			QuickKanji.injectList(type, list, true);
			if(QuickKanji.current) delete QuickKanji.current;
			
			
		}
	},
	
	Voting: {
	
		Kanji: {
			No: $("stage_vote_no"),
			Yes: $("stage_vote_yes"),
			Loading: $("stage_vote_loading"),
			Current: $("stage_vote_current")
		},
		Compound: {
			No: $("compound_vote_no"),
			Yes: $("compound_vote_yes"),
			Loading: $("compound_vote_loading"),
			Current: $("compound_vote_current"),
			Message: $("compound_vote_message")
		},
		Example: {
		
		
		},
		
		sendVote: function(type,vote){
			if(QuickKanji.Effects.rollVotes.tweening) return;
			QuickKanji.Requests.vote.post({type:type,target:this.id,vote:vote});
			QuickKanji.Requests.vote.area = this;
			this.Loading.toggleDisplay("on", "inline");
		}
	},
	
	Contribute: {
	
		Kanji: {
			Button: $("contribute_buttons_kanji"),
			Form: $("contribute_form"),
			Loading: $("contribute_loading"),
			Kanji: $("contribute_kanji"),
			Message: $("contribute_message")
		},
		Compound: {
			
			Button: $("contribute_buttons_compound"),
			Form: $("contribute_compound_form"),
			Loading: $("contribute_compound_loading"),
			Message: $("contribute_message")
		},
		Translation: {
			Form: $("compound_translation_form"),
			Loading: $("compound_translation_loading"),
			Message: $("compound_translation_message"),
			Text: $("compound_translation_text"),
			Submit: $("compound_translation_submit")
		},
		Example: {
			Form: $("compound_addexample"),
			Loading: $("compound_addexample_loading"),
			Message: $("compound_addexample_message")
		},
		show: function(form){
		
			var current = (form == "kanji") ? QuickKanji.Contribute.Kanji: QuickKanji.Contribute.Compound;
			var hidden  = (form == "kanji") ? QuickKanji.Contribute.Compound: QuickKanji.Contribute.Kanji;
			current.Form.show();
			hidden.Form.hide();
			current.Button.addClass("active");
			hidden.Button.removeClass("active");
			current.Message.empty();
			hidden.Message.empty();
		}
	},
	
	Tabs: {
	
		Info: $("tabs_info_on"),
		Contribute: $("tabs_contribute_off"),
		Assoc: ["stage", "contribute"],
		Chat: $("tabs_chat_off"),
		show: function(s){
		
			if(QuickKanji.Stage.active == s && QuickKanji.Stage.Roll.stage == "out") return;
			
			if(s == "stage"){
				this.Info.setProperty("id", "tabs_info_on");
				this.Contribute.setProperty("id", "tabs_contribute_off");
			} else {
				this.Info.setProperty("id", "tabs_info_off");
				this.Contribute.setProperty("id", "tabs_contribute_on");
			}
				
			this.Assoc.each(function(id){
			
				var container = $(id);
				container.setStyles({"top": 0, "marginTop": 0, "marginBottom": 0});
				if(id == s)	container.show();
				else		container.hide();
			});

			QuickKanji.Stage.Roll.src = QuickKanji.Img.RollUp;
			QuickKanji.Stage.Roll.state = "out";
			QuickKanji.Stage.active = s;			
		}
	},
	
	Chat: {
		Login: $("chat_login"),
		Logout: $("chat_logout"),
		Main: $("chat_main"),
		Messages: $("chat_messages"),
		Form: $("chat_form"),
		update: function(){
			var lastLine = QuickKanji.Chat.lastLine;
			QuickKanji.Requests.chat.post({action:'update',last:lastLine});
		},
		lastLine: 0,
		delay: 5000
	},
	
	Effects: {
	
		hoverTab: function(){
			this.setStyle("background", "#eef");
		},
		unhoverTab: function(){
			this.setStyle("background", "none");
		},
		toggleButton: function(){
		
			var menu = QuickKanji.Menu;
			menu.currentButton = this;
			for(instance in menu.Buttons){
				var button = menu.Buttons[instance];
				if(this == button) button.addClass("active");
				else button.removeClass("active");
			}
		},
		panelSlide: new Fx.Tween("moving_panel", {property: "top", duration:2000,transition: Fx.Transitions.Back.easeInOut}),
		rollStage: new Fx.Morph("stage", {duration: 500}),
		rollContribute: new Fx.Morph("contribute", {duration: 500}),
		rollVotes: new Fx.Tween("compound_vote_message", {
			property: 'left',
			duration: 800,
			onComplete:function(){
				var left = QuickKanji.Voting.Compound.Message.getStyle("left").toInt();
				if(left < 0) this.tweening = false;
			}
		}),
		expandList: function(list){
		
			list.out = (list.out) ? false : true;
			var text = (list.out) ? "Less..." : "More...";
			this.set('text', text);
			var count = 1;
			list.getElements("li").each(function(li){
				if(!list.out && count > 3) li.addClass("hidden");
				else li.removeClass("hidden");
				count++;
			});
		}
	},
	
	Requests: {
	
		send: function(type, pass){
		
			if((type == "info" || type == "compound") && this.tagName && this.tagName.toLowerCase() == "li"){
				if(QuickKanji.current) QuickKanji.current.removeClass("active");
				this.addClass("active");
				QuickKanji.current = this;
			} else { 
			 	if(QuickKanji.current) delete QuickKanji.current;
			}
			
			if(type == "info" || type == "daily") var ajax = QuickKanji.Requests.info;
			else if(type == "compound") var ajax = QuickKanji.Requests.compound;
			else var ajax = QuickKanji.Requests.list;
			
			if(ajax.running) ajax.cancel();
			
			var request = {type:type};
			
			if(pass){
				var el = $(this);
				var text = (this.get('tag') == "input") ? this.get('value') : this.get('text');
				if(!text || text == "-") return;
				request.q = text;
			}
			ajax.post(request);
			QuickKanji.Menu.Menu.addClass('loading');
		},
		
		list: new Request({url: "lib/ajax/quickkanji.php", onComplete: function(json){
			
			json = JSON.decode(json);
			QuickKanji.injectList(json.type, json.data);
			QuickKanji.Menu.Menu.removeClass('loading');
		}}),
		info: new Request({url: "lib/ajax/quickkanji.php", onComplete: function(json){
		
			json = JSON.decode(json);
			QuickKanji.Menu.Menu.removeClass('loading');
			if(json.error){
				QuickKanji.Stage.Message.setStyle("color", "#c33");
				QuickKanji.Stage.Message.set('text', json.error.message);
				return false;
			}
			QuickKanji.Tabs.show("stage");
			QuickKanji.injectInfo(json.data);
		}}),
		compound: new Request({url: "lib/ajax/quickkanji.php", onComplete: function(json){
			json = JSON.decode(json);
			QuickKanji.Menu.Menu.removeClass('loading');
			QuickKanji.injectCompound(json.data, QuickKanji.Content.control);
		}}),
		
		contribute: new Request({url: "lib/ajax/contribute.php", onComplete: function(json){
		
			json = JSON.decode(json);
			
			switch(json.type){
				case "compound":    var box = QuickKanji.Contribute.Compound; break;
				case "translation": var box = QuickKanji.Contribute.Translation; break;
				case "example":     var box = QuickKanji.Contribute.Example; break;
				default: 	    var box = QuickKanji.Contribute.Kanji;
			}
				
			if(json.result == "success"){
			
				var message = "Your " + json.type + " was accepted.";
				box.Message.setStyles({backgroundColor:"#f2fff2",color:"#393"});
				box.Message.set('text', message);
				
				for(i=0;i<box.Form.length;i++){
					if(box.Form[i].type == "text" || box.Form[i].tagName.toLowerCase() == "textarea") box.Form[i].value = "";
				}
				if(json.type == "kanji") $("contribute_larger").setStyle("color", "#393");
				if(json.type == "translation"){
					box.Text.setStyles({backgroundColor: '#f6f6f6',borderColor: '#ccc'});
					box.Text.disabled = true;
					box.Submit.disabled = true;
				}
			} if(json.result == "error"){
				box.Message.setStyles({backgroundColor:"#fff2f2",color:"#933"});
				box.Message.set('text', json.error);
			}
			
			box.Loading.hide();
			
		}}),
		
		vote: new Request({url: "lib/ajax/vote.php", onComplete: function(json){
		
			json = JSON.decode(json);
			
			var area = this.area;			
			area.Loading.hide();
			if(json.result == "success" && json.target == area.id){
			
				if(json.sum > 0)	var dir = "pos";
				else if(json.sum < 0)	var dir = "neg";
				else			var dir = "";
				
				area.Current.setProperty("class", dir);
				area.Current.set('text', json.sum);
			}
			
			if(json.type == "compound" && json.result == "success"){
				var fx = QuickKanji.Effects.rollVotes;
				fx.tweening = true;
				fx.left = QuickKanji.Voting.Compound.Message.getStyle("left");
				fx.start(fx.left,0).chain(function(){
					this.start.delay(800,this,this.left);
				});
			}
		
		}}),
		
		chat: new Request({url: "lib/ajax/chat.php", onComplete: function(json){
		
			json = JSON.decode(json);
			
			if(json.action == "login"){
			
				if(json.result == "success"){
				
					var name = QuickKanji.Chat.tempName;			
					
					$("chat_namespan").set('text', name);
					$("chat_messages_login").set('text', json.time);
					QuickKanji.Chat.Name = name;
					QuickKanji.Chat.Form.name.value = name;
					QuickKanji.Chat.Login.toggleDisplay("off");
					QuickKanji.Chat.Main.toggleDisplay("on");
					QuickKanji.Chat.Form.message.focus();

				} else if(json.result == "error"){
					$("chat_usererror").set('text', "* "+json.error);
				}
			} else if(json.action == "logout"){
			
				QuickKanji.Chat.Login.toggleDisplay("on");
				QuickKanji.Chat.Main.toggleDisplay("off");
				$clear(QuickKanji.Chat.timer);
				
				var chatTab = QuickKanji.Tabs.Chat;
				chatTab.id = "tabs_chat_off";
				$("chat").toggleDisplay("off");		
				
				return;			
			}
			
			if(json.lines){
			
				for(i=0;i<json.lines.length;i++){
				
					var name = new Element("span");
					name.setProperty("class", "name");
					name.set('text', json.lines[i].name + ":");
					var text = new Element("span");
					text.setProperty("class", "text");
					text.set('text', json.lines[i].line);
					
					var li = new Element("li");
					if(json.lines[i].active == "false") li.addClass("inactive");
					li.adopt(name);
					li.adopt(text);
					li.inject(QuickKanji.Chat.Messages);
					
					if((i >= json.lines.length-5) && json.action != "login"){
					
						var len = json.lines.length - 5;
						var duration = Math.floor((i-len+1)/(json.lines.length-len)*1500);
						li.effect('backgroundColor',{duration:duration,transition:Fx.Transitions.linear}).start("#fea", "#fff");
					}
				}
				
				var scroll = QuickKanji.Chat.Messages.getScrollSize().y;
				QuickKanji.Chat.Messages.scrollTo(0,scroll);
				QuickKanji.Chat.lastLine = json.last;
			}
			
			if(json.users){
			
				$("chat_userspan").set('text', json.users);
			}
			
			if(json.action != "login" && json.result != "error"){
				$clear(QuickKanji.Chat.timer);
				QuickKanji.Chat.timer = QuickKanji.Chat.update.delay(QuickKanji.Chat.delay);
			}
			
		}})
	},
		
	injectList: function(type, array, vocab){
	
		if(type == "level"){
		
			var titles = array.explanations;
			array = array.names;
		}
		var ul = new Element("ul",{id: "list"});
		
		if(type == "radical") var currentStrokes = 0;
		for(i=0;i<array.length;i++){
			
			if(array[i] == "") continue;
			
			var li = new Element("li");
			if(type == "radical"){
			
				if(currentStrokes != array[i].strokes){
					var hint = new Element("li",{'class':'strokes'});
					hint.set('text', array[i].strokes);
					ul.adopt(hint);
					currentStrokes = array[i].strokes;
				}
				li.set('text', array[i].radical);
			} else	li.set('text', array[i]);
			
			switch(type){
			
				case "level":
					li.setProperty("class", "wide");
					li.setProperty("title", titles[i]);
					li.addEvent("click", QuickKanji.Requests.send.bind(li, "listByLevel"));
					break;
				case "onSyllable":
					li.addEvent("click", QuickKanji.Requests.send.bind(li, "listByOnSyllable"));
					if(i % 5 == 0){
						 li.setProperty("class", "clear");
						 ul.adopt(new Element("div"));
					}
					break;
				case "kunSyllable":
					li.addEvent("click", QuickKanji.Requests.send.bind(li, "listByKunSyllable"));
					if(i % 5 == 0){
						li.setProperty("class", "clear");
						ul.adopt(new Element("div"));
					}
					break;
				case "onReading":
					li.addEvent("click", QuickKanji.Requests.send.bind(li, "listByOnWords"));
					li.setProperty("class", "full");
					break;
				case "kunReading":
					li.addEvent("click", QuickKanji.Requests.send.bind(li, "listByKunWords"));
					li.setProperty("class", "full");
					break;
				case "radical":
					li.addEvent("click", QuickKanji.Requests.send.bind(li, "listByRadical"));
					break;
				case "strokes":
					li.addEvent("click", QuickKanji.Requests.send.bind(li, "listByStrokes"));
					li.setProperty("class", "numeral");
					break;
				case "kanji":
					li.addEvent("click", QuickKanji.Requests.send.bind(li, "info"));
					break;
				case "compound":
					li.addEvent("click", function(e){
						// Keeping the vocab list defaults to ON for now...
						QuickKanji.Content.control = true;
						QuickKanji.Requests.send.bind(this)("compound", true);
					}.bind(li));
					li.setProperty("class", "compound");
					break;		
			}
			ul.adopt(li);
		}
		
		var list = QuickKanji.Content.List.empty();
		if(array.length == 0) list.adopt(new Element("div", {id: "no_results"}).set('text', "- No Results -"));
		else list.adopt(ul);
		
		if(!vocab) QuickKanji.Content.Tabs.hide();
		QuickKanji.Content.List.show();
		QuickKanji.Content.Compound.Container.hide();
		
	},
	
	injectInfo: function(info){
	
		var Stage = QuickKanji.Stage;
		var list = [];
		
		Stage.Kanji.set('text', info.kanji);
		
		/* Remove Tool Tips (prevents memory leaks) */
		Stage.Kanji.removeEvents();
		$$(".tool-tip").each(function(el){
			el.remove();
		});
		
		if(info.notes){
			Stage.Kanji.set("title", info.notes);
			list.push(Stage.Kanji);
		} else {
			Stage.Kanji.removeProperty("title");
		}
		Stage.Radical.set('text', info.radical);
		Stage.Strokes.set('text', info.strokes);
		Stage.Level.set('text', info.level);
		Stage.ShiftJIS.set('text', info.shiftJIS);
		Stage.Meaning.set('text', info.meaning);
		Stage.Compounds.empty();
		
		Stage.On.empty();
		Stage.On.adopt(this.compileReading(info.onReadings, "on"));
		Stage.Kun.empty();
		Stage.Kun.adopt(this.compileReading(info.kunReadings, "kun"));
		var myTips = new TipsFixed('.readingHelp', QuickKanji.Tips);		
		if(info.compounds && info.compounds.length > 3) Stage.More.show();
		else Stage.More.hide();
		
		if(info.compounds){
			
			for(i=0;i<info.compounds.length;i++){
			
				var compound = info.compounds[i].compound;
				var reading  = info.compounds[i].reading;
				var meaning  = info.compounds[i].meaning;
				
				var title = reading;
				// More helpful for learning to leave the meaning out??
				// title += " " + meaning;
				title += '<div class="click">Click for more info.</div>';
				var li = new Element("li", {'title':title});
				if(i>2) li.addClass("hidden");
				li.set('text', compound);
				QuickKanji.Content.control = false;
				li.addEvent("click", QuickKanji.Requests.send.bind(li, "compound"));
				list.push(li);
				Stage.Compounds.adopt(li);
			}
			
			Stage.More.set('text', "More...");
			Stage.Compounds.out = false;
			Stage.Compounds.adopt(li);
			Stage.Compounds.show();
		} else {
			Stage.Compounds.hide();
		}
		
		if(info.user){
			Stage.Voting.toggleDisplay("on");
			if(info.votes > 0)	var dir = "pos";
			else if(info.votes < 0) var dir = "neg";
			else				var dir = "";
			
			QuickKanji.Voting.Kanji.id = info.id;
			QuickKanji.Voting.Kanji.Current.setProperty("class", dir);
			QuickKanji.Voting.Kanji.Current.set('text', info.votes);
		} else {
			Stage.Voting.toggleDisplay("off");
		}
		
		if(info.daily){
			var div = new Element("div", {'class':'daily'});
			div.set('html', "Kanji<br/>of the Day:");
			Stage.Message.adopt(div);
		} else {
			Stage.Message.empty();
		}
		
		new TipsFixed(list, QuickKanji.Tips);
		QuickKanji.Vocab.initIcon(QuickKanji.Vocab.Kanji,info.kanji);
		
	},
	
	injectCompound: function(data, control){
	
		var compound = QuickKanji.Content.Compound;
		compound.Container.show();
		
		compound.Japanese.empty();
		for(i=0;i<data.compound.length;i++){
		
			var letter = data.compound.charAt(i);
			if(letter.match(/[\u4e00-\u9fff]/)){
				var span = new Element("span");
				span.set('text', letter);
				span.addEvent("click", QuickKanji.Requests.send.bind(span, "info"));
				compound.Japanese.adopt(span);
			} else {
				compound.Japanese.appendText(letter);
			}
		}
		
		compound.Japanese.setProperty("title", data.reading);
		
		// Set the compound id for voting
		QuickKanji.Voting.Compound.id = data.id;
		if(data.votes > 0)	var dir = "pos";
		else if(data.votes < 0) var dir = "neg";
		else			var dir = "";
		QuickKanji.Voting.Compound.Current.setProperty("class", dir);
		QuickKanji.Voting.Compound.Current.set('text', data.votes);
		
		// Set up the translate form
		if(!data.meaning || data.votes <= -5){
			var translation = QuickKanji.Contribute.Translation;
			translation.Form.show();
			translation.Text.setStyles({backgroundColor: '#eee',borderColor: '#888'});
			translation.Text.disabled = false;
			translation.Submit.disabled = false;
			translation.Form.id.value = data.id;
			var message = (data.votes < -5) ? QuickKanji.Text.TranslationEdit : QuickKanji.Text.TranslationNone;
			translation.Submit.value = (data.votes < -5) ? "Edit" : "Add";
			translation.Message.set('text', message);
			translation.Message.setStyles({background:'none',color:'#ca880c'});
			if(data.votes <= -5) translation.Form.text.value = data.meaning;
		} else {
			QuickKanji.Contribute.Translation.Form.hide();
		}
		
		var tips = new Array();
		tips.push(compound.Japanese);
		
		if(data.votes > -5) compound.Meaning.set('text', data.meaning);
		else compound.Meaning.empty();
		compound.Examples.empty();
		
		if(data.examples){
		   for(i=0;i<data.examples.length;i++){
		
			var example = data.examples[i];
			var voting = new Element("div", {'class': 'voting'});
			
			var yes = new Element("img", {src: QuickKanji.Img.VoteYes});
			var no = new Element("img", {src: QuickKanji.Img.VoteNo});
			var loading = new Element("img", {src: QuickKanji.Img.Loading,'class':'loading'});
			if(example.votes == 0) var dir = "";
			else var dir = (example.votes > 0) ? "pos" : "neg";
			var current = new Element("span", {'class':dir});
			current.set('text', example.votes);
			
			yes.addEvent("click", QuickKanji.Voting.sendVote.bind(voting,"example","yes"));
			no.addEvent("click", QuickKanji.Voting.sendVote.bind(voting,"example","no"));
			
			voting.adopt(yes);
			voting.adopt(no);
			voting.adopt(current);
			voting.adopt(loading);
			
			voting.id = example.id;
			voting.Loading = loading;
			voting.Current = current;
			
			var dt = new Element("dt", {'text': example.sentence, "title":example.reading});
			var dd = new Element("dd", {'text': example.meaning});
			if(i%2){
				dt.addClass("odd");
				dd.addClass("odd");
			}
			compound.Examples.adopt(voting);
			compound.Examples.adopt(dt);
			compound.Examples.adopt(dd);
			tips.push(dt);
		   }
		}
		
		QuickKanji.Content.Tabs.hide();
		if(!control) QuickKanji.Content.List.hide();
		
		new TipsFixed(tips, QuickKanji.Tips);
		QuickKanji.Contribute.Example.Form.compound.value = data.compound;
		QuickKanji.Contribute.Example.Message.empty();
		
		
		QuickKanji.Vocab.initIcon(QuickKanji.Vocab.Compound,data.compound);
		QuickKanji.Vocab.Compound.Message.empty();
		
	},
	
	compileReading: function(readings, type){
	
		var holder = new Element("span");
		var listType = (type == "on") ? "listByOnWords" : "listByKunWords";
		
		for(i=0;i<readings.length;i++){
					
			var reading = readings[i];
			var readingHolder = new Element("span");
			
			var base = new Element("span");
			if(reading.base != "-"){
				base.setProperty("class", "readingHelp");
				base.setProperty("title", QuickKanji.Text.Base);
			}
			base.set('text', reading.base);
			base.injectInside(readingHolder);
			
			if(reading.okuri){
				var okuri = new Element("span");
				okuri.setProperty("class", "okuri readingHelp");
				okuri.setProperty("title", QuickKanji.Text.Okuri);
				okuri.set('text', reading.okuri);
				okuri.injectInside(readingHolder);	
			}
			
			readingHolder.injectInside(holder);
			readingHolder.setProperty("class", "clickable");
			
			var text = readingHolder.get('text');
			
			if(text && text != "-"){
				readingHolder.addEvent("click", QuickKanji.Requests.send.bind(readingHolder, listType));
			}
		
			if(i < readings.length-1) holder.appendText(",");
		}
		return holder;
	},
	
	Syllables: ["あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ", "が", "ぎ", "ぐ", "げ", "ご", "さ", "し", "す", "せ", "そ", "ざ", "じ", "ず", "ぜ", "ぞ", "た", "ち", "つ", "て", "と", "だ", "ぢ", "づ", "で", "ど", "な", "に", "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", "ば", "び", "ぶ", "べ", "ぼ", "ま", "み", "む", "め", "も", "や", "-", "ゆ", "-", "よ", "ら", "り", "る", "れ", "ろ", "わ"],
	Levels: {
		names: ["教（一）", "教（二）", "教（三）", "教（四）", "教（五）", "教（六）", "常用", "常用外"],
		explanations: ["First Grade Level", "Second Grade Level", "Third Grade Level", "Fourth Grade Level", "Fifth Grade Level", "Sixth Grade Level", "General Use", "Other"]
	},
	
	Text: {
		Base: "This is the base of this reading. In standard Japanese it will not be written out.",
		Okuri: "This is the \"okurigana\" for this reading. In standard written Japanese it will be written out after the character.",
		Docked: "Panel is docked (click to undock).",
		Undocked: "Panel is undocked (click to dock).",
		VocabAdd: "Add this kanji to my vocab list.",
		VocabRemove: "Remove this kanji from my vocab list.",
		VocabAdded: "Kanji added to your vocab list.",
		VocabRemoved: "Kanji removed from your vocab list.",
		VocabCompoundAdd: "Add this compound to my vocab list.",
		VocabCompoundRemove: "Remove this compound from my vocab list.",
		VocabCompoundAdded: "Compound added to your vocab list.",
		VocabCompoundRemoved: "Compound removed from your vocab list.",
		TranslationNone: "This compound does not have a translation yet. Be the first to add one!",
		TranslationEdit: "You can edit this translation to improve it!"
	},
	Img:
	{
		PinIn: "images/buttons/pin_in.gif",
		PinOut: "images/buttons/pin_out.gif",
		RollUp: "images/buttons/roll_up.gif",
		RollDown: "images/buttons/roll_down.gif",
		VocabAdd: "images/icons/vocab_add.gif",
		VocabRemove: "images/icons/vocab_remove.gif",
		VoteYes: "images/icons/plus.gif",
		VoteNo: "images/icons/minus.gif",
		Loading: "images/icons/loading.gif",
		Preload:["icons/vocab_remove.gif","tabs/chat_on.gif","tabs/contribute_on.gif","tabs/info_off.gif"]
	},
	Tips:
	{
		fixed: true
	}
}


window.addEvent("load", QuickKanji.init.bind(QuickKanji));
var myTips = new TipsFixed('.help', QuickKanji.Tips);

