// ==UserScript==
// @name wu::forums Math Symbol Support
// @namespace http://www.dwarfrune.com
// @description View, Enter and Edit Math Symbols in wu::forums Messages (v1.4)
// @include http://www.ocf.berkeley.edu/~wwu/cgi-bin/yabb/YaBB.cgi?board=*;*
// ==/UserScript==
//
//
// Revision History:
//    09 Feb 2009 -- 1.4: Fixed rare crashing bug (thanks, Eigenray!)
//
//    23 Oct 2007 -- 1.3: Fixed bug with lone backslashes and preview
//
//    21 Sep 2007 -- 1.2: Added input textbox resizing
//
//    16 Feb 2007 -- 1.1: Fixed namespace collision with UBB tags (!)
//
//    16 Feb 2007 -- 1.0: Verified proper operation in Safari; first
//                   official release
//
//    15 Feb 2007 -- RC2: Rewrote event handling for IE compatibility
//
//    14 Feb 2007 -- RC1.1: Minor bugfix for disappearing spaces when
//                   editing a post repeatedly
//
//    14 Feb 2007 -- Release Candidate 1: added in symbol translation
//                   functionality from older scripts, changed symbol
//                   entry UI to more menu-like behavior, chenged
//                   symbol text to use LaTeX-like \symbol scheme.
//
//    05 Feb 2007 -- Graphical Symbol Entry Beta: simple rollover
//                   menu for selecting symbols
//
//
// Copyright (c) 2007, 2009 Shawn Menninga
// All rights reserved
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copy-
//       right notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Shawn Menninga nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


wu_symbols = {
	// === fields ===
	
	// web URL of the symbols directory (with trailing /)
	// NOTE: if this changes, the RegExp in the messageImagesToText
	//       method MUST be changed as well
	url: "http://www.ocf.berkeley.edu/~wwu/YaBBImages/symbols/",
	
	// list of all symbols, orederd by entry section
	// as section symbol/name: row list as array of array of
	//     "symbol[:alias][;cols[,rows]]"
	//     or ["inline", "superscript", "subscript"]
	// (a symbol may appear multiple times)
	entryTable: {
		surd: [ "Common Symbols", [
			[ "surd", "sum", "prod", "int", "oint", "angle", "measuredangle", "sphericalangle", "therefore", "imath", "jmath", "ell", "emptyset", "infty", "varaleph", "bbz", "bbq", "bbr", "bbc", "re", "im", "partial" ],
			[ "pm", "mp", "largetimes", "div", "spot", "smallcirc", "supast", "oplus", "otimes", "lnot", "cap", "cup", "vee", "wedge", "subset", "notsubset", "subseteq", "nsubseteq", "supset", "notsupset", "supseteq", "nsupseteq" ],
			[ "ne", "lt", "nless", "gt", "ngtr", "le", "nleq", "ge", "ngeq", "ll", "gg", "sim", "nsim", "approx", "notapprox", "simeq", "approxeq", "cong", "ncong", "equiv", "propto" ],
			[ "vert", "nmid", "parallel:parallel", "nparallel", "perp", "exists", "nexists", "forall", "in", "notin", "to", "bigto", "leftrightarrow", "bigleftrightarrow", "nrightarrow", "nbigrightarrow", "nleftrightarrow", "nbigleftrightarrow", "onetoone", "onto", "onetooneonto" ],
			[ "alpha", "beta", "cgamma", "gamma", "cdelta", "delta", "epsilon", "varepsilon", "theta", "pi", "rho", "sigma", "phi", "varphi", "comega", "omega" ],
			[ "lp", "rp", "lbrack", "rbrack", "lbrace", "rbrace", "langle", "rangle", "lceil", "rceil", "lfloor", "rfloor", "vert:lvert", "vert:rvert", "parallel:lVert", "parallel:rVert" ]
		] ],
		ca: [ "Regular Letters", [
			[
				[ "ca", "supca", "subca" ], [ "cb", "supcb", "subcb" ], [ "cc", "supcc", "subcc" ], [ "cd", "supcd", "subcd" ], [ "ce", "supce", "subce" ],
				[ "cf", "supcf", "subcf" ], [ "cg", "supcg", "subcg" ], [ "ch", "supch", "subch" ], [ "ci", "supci", "subci" ], [ "cj", "supcj", "subcj" ],
				[ "ck", "supck", "subck" ], [ "cl", "supcl", "subcl" ], [ "cm", "supcm", "subcm" ]
			],
			[
				[ "a", "supa", "suba" ], [ "b", "supb", "subb" ], [ "c", "supc", "subc" ], [ "d", "supd", "subd" ], [ "e", "supe", "sube" ],
				[ "f", "supf", "subf" ], [ "g", "supg", "subg" ], [ "h", "suph", "subh" ], [ "i", "supi", "subi" ], [ "j", "supj", "subj" ],
				[ "k", "supk", "subk" ], [ "l", "supl", "subl" ], [ "m", "supm", "subm" ]
			],
			[
				[ "cn", "supcn", "subcn" ], [ "co", "supco", "subco" ], [ "cp", "supcp", "subcp" ], [ "cq", "supcq", "subcq" ], [ "cr", "supcr", "subcr" ],
				[ "cs", "supcs", "subcs" ], [ "ct", "supct", "subct" ], [ "cu", "supcu", "subcu" ], [ "cv", "supcv", "subcv" ], [ "cw", "supcw", "subcw" ],
				[ "cx", "supcx", "subcx" ], [ "cy", "supcy", "subcy" ], [ "cz", "supcz", "subcz" ]
			],
			[
				[ "n", "supn", "subn" ], [ "o", "supo", "subo" ], [ "p", "supp", "subp" ], [ "q", "supq", "subq" ], [ "r", "supr", "subr" ],
				[ "s", "sups", "subs" ], [ "t", "supt", "subt" ], [ "u", "supu", "subu" ], [ "v", "supv", "subv" ], [ "w", "supw", "subw" ],
				[ "x", "supx", "subx" ], [ "y", "supy", "suby" ], [ "z", "supz", "subz" ]
			],
			[
				[ "0", "sup0", "sub0" ], [ "1", "sup1", "sub1" ], [ "2", "sup2", "sub2" ], [ "3", "sup3", "sub3" ], [ "4", "sup4", "sub4" ],
				[ "5", "sup5", "sub5" ], [ "6", "sup6", "sub6" ], [ "7", "sup7", "sub7" ], [ "8", "sup8", "sub8" ], [ "9", "sup9", "sub9" ]
			]
		] ],
		bba: [ "\"Fancy\" Letters", [
			[ "bfca", "bfcb", "bfcc", "bfcd", "bfce", "bfcf", "bfcg", "bfch", "bfci", "bfcj", "bfck", "bfcl", "bfcm", "bfcn", "bfco", "bfcp", "bfcq", "bfcr", "bfcs", "bfct", "bfcu", "bfcv", "bfcw", "bfcx", "bfcy", "bfcz" ],
			[ "bfa", "bfb", "bfc", "bfd", "bfe", "bff", "bfg", "bfh", "bfi", "bfj", "bfk", "bfl", "bfm", "bfn", "bfo", "bfp", "bfq", "bfr", "bfs", "bft", "bfu", "bfv", "bfw", "bfx", "bfy", "bfz" ],
			[ "rmca", "rmcb", "rmcc", "rmcd", "rmce", "rmcf", "rmcg", "rmch", "rmci", "rmcj", "rmck", "rmcl", "rmcm", "rmcn", "rmco", "rmcp", "rmcq", "rmcr", "rmcs", "rmct", "rmcu", "rmcv", "rmcw", "rmcx", "rmcy", "rmcz" ],
			[ "rma", "rmb", "rmc", "rmd", "rme", "rmf", "rmg", "rmh", "rmi", "rmj", "rmk", "rml", "rmm", "rmn", "rmo", "rmp", "rmq", "rmr", "rms", "rmt", "rmu", "rmv", "rmw", "rmx", "rmy", "rmz" ],
			[ "frakca", "frakcb", "frakcc", "frakcd", "frakce", "frakcf", "frakcg", "frakch", "frakci", "frakcj", "frakck", "frakcl", "frakcm", "frakcn", "frakco", "frakcp", "frakcq", "frakcr", "frakcs", "frakct", "frakcu", "frakcv", "frakcw", "frakcx", "frakcy", "frakcz" ],
			[ "fraka", "frakb", "frakc", "frakd", "frake", "frakf", "frakg", "frakh", "fraki", "frakj", "frakk", "frakl", "frakm", "frakn", "frako", "frakp", "frakq", "frakr", "fraks", "frakt", "fraku", "frakv", "frakw", "frakx", "fraky", "frakz" ],
			[ "scra", "scrb", "scrc", "scrd", "scre", "scrf", "scrg", "scrh", "scri", "scrj", "scrk", "scrl", "scrm", "scrn", "scro", "scrp", "scrq", "scrr", "scrs", "scrt", "scru", "scrv", "scrw", "scrx", "scry", "scrz" ],
			[ "cala", "calb", "calc", "cald", "cale", "calf", "calg", "calh", "cali", "calj", "calk", "call", "calm", "caln", "calo", "calp", "calq", "calr", "cals", "calt", "calu", "calv", "calw", "calx", "caly", "calz" ],
			[ "bba", "bbb", "bbc", "bbd", "bbe", "bbf", "bbg", "bbh", "bbi", "bbj", "bbk", "bbl", "bbm", "bbn", "bbo", "bbp", "bbq", "bbr", "bbs", "bbt", "bbu", "bbv", "bbw", "bbx", "bby", "bbz" ],
			[
			  [ "rmch", , "subrmch" ], [ "scrh", , "subscrh" ], [ "scrl", , "subscrl" ]
			]
		] ],
		alpha: [ "Greek Letters", [
			[	"rmca:Alpha;2", "rmcb:Beta;2", "cgamma;2", "cdelta;2", "rmce:Epsilon;4", "rmcz:Zeta;2", "rmch:Eta;2", "ctheta;4", "rmci:Iota;2", "rmck:Kappa;2", "clambda;2", "rmcm:Mu;2", "rmcn:Nu;2" ],
			[
				[ "alpha", "supalpha", "subalpha" ], [ "beta", "supbeta", "subbeta" ], [ "gamma", "supgamma", "subgamma" ], [ "delta", "supdelta", "subdelta" ], [ "epsilon", "supepsilon", "subepsilon" ], [ "varepsilon", "supvarepsilon", "subvarepsilon" ],
				[ "zeta", "supzeta", "subzeta" ], [ "eta", "supeta", "subeta" ], [ "theta", "suptheta", "subtheta" ], [ "vartheta", "supvartheta", "subvartheta" ], [ "iota", "supiota", "subiota" ], [ "kappa", "supkappa", "subkappa" ],
				[ "lambda", "suplambda", "sublambda" ], [ "mu", "supmu", "submu" ], [ "nu", "supnu", "subnu" ]
			],
			[ "cxi;2", "rmco:Omicron;2", "cpi;4", "rmcp:Rho;4", "csigma;4", "rmct:Tau;2", "cupsilon;2", "cphi;4", "rmcx:Chi;2", "cpsi;2", "comega" ],
			[
				[ "xi", "supxi", "subxi" ], [ "o", "supo", "subo" ], [ "pi", "suppi", "subpi" ], [ "varpi", "supvarpi", "subvarpi" ], [ "rho", "suprho", "subrho" ], [ "varrho", "supvarrho", "subvarrho" ], 
				[ "sigma", "supsigma", "subsigma" ], [ "varsigma", "supvarsigma", "subvarsigma" ], [ "tau", "suptau", "subtau" ], [ "upsilon", "supupsilon", "subupsilon" ], [ "phi", "supphi", "subphi" ], [ "varphi", "supvarphi", "subvarphi" ], [ "chi", "supchi", "subchi" ],
				[ "psi", "suppsi", "subpsi" ], [ "omega", "supomega", "subomega" ]
			]
		] ],
		plus: [ "Operators", [
			[ "plus", "minus", "pm", "mp", "largetimes", "div", "divideontimes", "cdot", "spot", "smallcirc", "bullet", "circ", "diamond", "star", "triangleleft", "triangleright", "centerdot", "smallsetminus", "supast", "supfrown" ],
			[ "oplus", "ominus", "otimes", "oslash", "odot", "osmallplus", "circleddash", "circledcirc", "circledast", "boxplus", "boxminus", "boxtimes", "boxdot", "dotplus", "bigtriangledown", "bigtriangleup", "dagger", "ddag:ddagger", "intercal", "wr" ],
			[ "sum", "prod", "coprod", "amalg", "int", "oint", "smallint", "setminus", "bigcirc", "bigoplus", "bigotimes", "bigodot", "bigcap", "bigcup", "biguplus", "bigsqcup", "bigvee", "bigwedge", "leftthreetimes", "rightthreetimes" ],
			[ "cap", "doublecap", "cup", "doublecup", "uplus", "uminus", "sqcap", "sqcup", "wedge", "barwedge", "doublebarwedge", "vee", "veebar", "curlyvee", "curlywedge", "times", "ltimes", "rtimes" ],
			[
				[ "plus", "supplus", "subplus" ], [ "minus", "supminus", "subminus" ]
			]
		] ],
		eq: [ "Relations", [
			[ "eq", "ne", "lt", "nless", "le", "nleq", "leqq", "nleqq", "lvertneqq", "leqslant", "nleqslant", "eqslantless", "ll", "llless;2", "gt", "ngtr", "ge", "ngeq", "geqq", "ngeqq", "gvertneqq", "geqslant", "ngeqslant", "eqslantgtr", "gg", "gggtr;2", "asymp" ],
			[ "doteq", "equiv", "bbumpeq", "bumpeq", "circeq", "eqcirc", "fallingdotseq", "doteqdot", "risingdotseq", "defs", "triangleq", "vdash", "nvdash", "v2dash", "nv2dash", "vddash", "nvddash", "vvdash", "nvvddash", "vdvdash", "dashv", "lessdot", "gtrdot", "lessgtr", "lesseqgtr", "lesseqqgtr", "gtrless", "gtreqless", "gtreqqless" ],
			[ "sim", "thicksim", "nsim", "notsim", "backsim", "simeq", "backsimeq", "cong", "ncong", "lesssim", "lnsim", "precsim", "precnsim", "gtrsim", "gnsim", "succsim", "succnsim", "approx", "thickapprox", "notapprox", "approxeq", "lessapprox", "lnapprox", "precapprox", "precnapprox", "gtrapprox", "gnapprox", "succapprox", "succnapprox" ],
			[ "prec", "nprec", "preceq", "npreceq", "precneqq", "preccurlyeq", "notpreccurlyeq", "curlyeqprec", "succ", "nsucc", "succeq", "nsucceq", "succneqq", "succcurlyeq", "notsucccurlyeq", "curlyeqsucc", "vartriangleleft", "ntriangleleft", "ndres", "trianglelefteq", "ntrianglelefteq", "blacktriangleleft", "vartriangleright", "ntriangleright", "nrres", "trianglerighteq", "ntrianglerighteq", "blacktriangleright" ],
			[ "vert", "nmid", "parallel:parallel", "nparallel", "models", "shortmid", "nshortmid", "shortparallel", "nshortparallel", "perp", "frown", "smile", "smallfrown", "smallsmile", "bowtie", "join", "propto", "varpropto", "between", "in", "notin", "inbag", "owns", "because", "therefore", "backepsilon", "multimap", "pitchfork" ],
			[ "subset", "notsubset", "ssubset", "sqsubset", "subseteq", "nsubseteq", "varsubsetneq", "subsetneq", "subseteqq", "nsubseteqq", "varsubsetneqq", "subsetneqq", "sqsubseteq", "supset", "notsupset", "ssupset", "sqsupset", "supseteq", "nsupseteq", "varsupsetneq", "supsetneq", "supseteqq", "nsupseteqq", "varsupsetneqq", "supsetneqq", "sqsupseteq" ],
			
			[
				[ "eq", "supeq", "subeq" ], [ "lt", "suplt", "sublt" ], [ "gt", "supgt", "subgt" ], [ "le", "suple", "suble" ], [ "ge", "supge", "subge" ]
			]
			
		] ],
		to: [ "Arrows", [
			[ "to", "nrightarrow", "longrightarrow", "bigto", "nbigrightarrow", "biglongrightarrow", "rrightarrow", "hookrightarrow", "rightrightarrows", "rightharpoonup", "rightharpoondown", "curvearrowright", "circlearrowright", "looparrowright", "rsh", "rightarrowtail", "twoheadrightarrow" ],
			[ "leftarrow", "nleftarrow", "longleftarrow", "bigleftarrow", "nbigleftarrow", "biglongleftarrow", "lleftarrow", "hookleftarrow", "leftleftarrows", "leftharpoonup", "leftharpoondown", "curvearrowleft", "circlearrowleft", "looparrowleft", "lsh", "leftarrowtail", "twoheadleftarrow" ],
			[ "leftrightarrow", "nleftrightarrow", "longleftrightarrow", "bigleftrightarrow", "nbigleftrightarrow", "biglongleftrightarrow", "leftrightarrows", "rightleftarrows", "leftrightharpoons", "rightleftharpoons", "leftrightsquigarrow", "nearrow", "nwarrow", "swarrow", "searrow" ],
			[ "uparrow", "biguparrow", "upuparrows", "upharpoonleft", "restriction", "downarrow", "bigdownarrow", "downdownarrows", "downharpoonleft", "downharpoonright", "updownarrow", "bigupdownarrow" ],
			[ "onetoone", "onto", "onetooneonto", "pfun", "pinj", "ffun", "finj", "psurj", "bij", "mapsto", "longmapsto", "rightsquigarrow" ]
		] ],
		infty: [ "Other Symbols", [
			[ "aleph", "varaleph", "beth", "gimel", "daleth", "eth", "finv", "game", "hbar", "hslash", "imath", "jmath", "bbbk", "ell", "perp:bot", "top", "wp", "digamma", "varkappa", "mho", "cdots;2", "ddots;2", "ldots:ldots;2", "vdots" ],
			[ "surd", "nabla", "partial", "infty", "emptyset", "varnothing", "exists", "nexists", "forall", "complement", "lnot", "angle", "measuredangle", "sphericalangle", "acute", "bar", "breve", "check", "ddot", "dot", "grave", "hat", "tilde", "vec", "smallprime", "prime", "backprime" ],
			[ "lp", "rp", "lbrack", "rbrack", "lbrace", "rbrace", "langle", "rangle", "vert:lvert", "vert:rvert", "lceil", "rceil", "lfloor", "rfloor", "lplp", "rprp", "llbrack", "rrbrack", "llangle", "rrangle","parallel:lVert", "parallel:rVert", "limg", "rimg", "lblot", "rblot" ],

			[
				[ "infty", "supinfty", "subinfty" ], [ "lp", "suplp", "sublp" ], [ "rp", "suprp", "subrp" ], [ "in", , "subin" ], [ "to", , "subto" ], [ "perp", "supperp" ]
			]
		] ],
		backp: [ "Miscelaneous", [
			[ "bang", "invbang", "atsign", "octothorpe", "dollar", "percent", "hat", "amp", "supast", "shortminus", "backtick", "apostrophe", "backquote", "quote", "colon", "semicolon", "comma", "period", "questionmark", "invquestion", "solidus" ],
			[ "backp", "doubles", "dagger:dag", "ddag", "ldots", "copyright", "circleds", "ss", "pounds", "yen", "checkmark", "semi", "eighthnote", "sharp", "flat", "natural", "diagdown", "diagup", "wutang;3,2" ],
			[ "vartriangle", "blacktriangle", "triangledown", "blacktriangledown", "bigbox", "square", "blacksquare", "bigdiamond", "lozenge", "blacklozenge", "bigstar", "maltese", "clubsuit", "diamondsuit", "heartsuit", "spadesuit", "smiley_down", "smiley_up" ]
		] ]
	},
	
	// list of aliases from symbol name to graphic name
	// as graphic name: "preferred alias name" or
	//     as ["preferred alias name", "alias name", ...] or
	//     as [null, "alias name", ...] if symbol name is preferred
	aliasTable: {
		ca: "A", cb: "B", cc: "C", cd: "D", ce: "E", cf: "F", cg: "G", ch: "H", ci: "I", cj: "J", ck: "K", cl: "L", cm: "M",
		cn: "N", co: "O", cp: "P", cq: "Q", cr: "R", cs: "S", ct: "T", cu: "U", cv: "V", cw: "W", cx: "X", cy: "Y", cz: "Z",
		supca: "supA", supcb: "supB", supcc: "supC", supcd: "supD", supce: "supE", supcf: "supF", supcg: "supG", supch: "supH", supci: "supI", supcj: "supJ", supck: "supK", supcl: "supL", supcm: "supM",
		supcn: "supN", supco: "supO", supcp: "supP", supcq: "supQ", supcr: "supR", supcs: "supS", supct: "supT", supcu: "supU", supcv: "supV", supcw: "supW", supcx: "supX", supcy: "supY", supcz: "supZ",
		subca: "subA", subcb: "subB", subcc: "subC", subcd: "subD", subce: "subE", subcf: "subF", subcg: "subG", subch: "subH", subci: "subI", subcj: "subJ", subck: "subK", subcl: "subL", subcm: "subM",
		subcn: "subN", subco: "subO", subcp: "subP", subcq: "subQ", subcr: "subR", subcs: "subS", subct: "subT", subcu: "subU", subcv: "subV", subcw: "subW", subcx: "subX", subcy: "subY", subcz: "subZ",
		bfca: "bfA", bfcb: "bfB", bfcc: "bfC", bfcd: "bfD", bfce: "bfE", bfcf: "bfF", bfcg: "bfG", bfch: "bfH", bfci: "bfI", bfcj: "bfJ", bfck: "bfK", bfcl: "bfL", bfcm: "bfM",
		bfcn: "bfN", bfco: "bfO", bfcp: "bfP", bfcq: "bfQ", bfcr: "bfR", bfcs: "bfS", bfct: "bfT", bfcu: "bfU", bfcv: "bfV", bfcw: "bfW", bfcx: "bfX", bfcy: "bfY", bfcz: "bfZ",
		rmca: ["rmA","calpha","Alpha"], rmcb: ["rmB","cbeta","Beta"], rmcc: "rmC", rmcd: "rmD", rmce: ["rmE","cepsilon","Epsilon"], rmcf: "rmF", rmcg: "rmG", rmch: ["rmH","ceta","Eta"], rmci: ["rmI","ciota","Iota"], rmcj: "rmJ", rmck: ["rmK","ckappa","Kappa"], rmcl: "rmL", rmcm: ["rmM","cmu","Mu"],
		rmcn: ["rmN","cnu","Nu"], rmco: ["rmO","comicron","Omicron"], rmcp: ["rmP","crho","Rho"], rmcq: "rmQ", rmcr: "rmR", rmcs: "rmS", rmct: ["rmT","ctau","Tau"], rmcu: "rmU", rmcv: "rmV", rmcw: "rmW", rmcx: ["rmX","cchi","Chi"], rmcy: "rmY", rmcz: ["rmZ","czeta", "Zeta"],
		frakca: "frakA", frakcb: "frakB", frakcc: "frakC", frakcd: "frakD", frakce: "frakE", frakcf: "frakF", frakcg: "frakG", frakch: "frakH", frakci: "frakI", frakcj: "frakJ", frakck: "frakK", frakcl: "frakL", frakcm: "frakM",
		frakcn: "frakN", frakco: "frakO", frakcp: "frakP", frakcq: "frakQ", frakcr: "frakR", frakcs: "frakS", frakct: "frakT", frakcu: "frakU", frakcv: "frakV", frakcw: "frakW", frakcx: "frakX", frakcy: "frakY", frakcz: "frakZ",
		cgamma: "Gamma", cdelta: "Delta", ctheta: "Theta", clambda: "Lambda", cxi: "Xi", cpi: "Pi", csigma: "Sigma", cupsilon: "Upsilon", cphi: "Phi", cpsi: "Psi", comega: "Omega",
		o: [,"omicron"], supo: [,"supomicron"], subo: [,"subomicron"],
		dollar: "$", percent: "%", shortminus: "_", amp:"&", octothorpe:"#", backp: "paragraph", doubles: "section", dagger: [,"dag"], ddag: [,"ddagger"], ldots: ["dots","ellipsis"], pounds: [,"sterling"],
		doublecap: "Cap", doublecup: "Cup", join: [,"Join"], vert: ["mid", "lvert", "rvert"], parallel: ["|", "Vert", "lVert", "rVert"], nvddash: "nvDash", nvvddash: "nVDash", ssubset: "Subset", ssupset: "Supset",
		bbumpeq: "Bumpeq", vvdash: "Vvdash", vddash: "vDash", v2dash: "Vdash", vartriangleleft: "dres", vartriangleright: "rres", vee: [,"lor"], wedge: [,"land"],
		ge: "geq", le: "leq", ne: "neq", gg: ">>", gggtr: [">>", "ggg"], ll: "<<", llless: ["<<<", "lll"],
		bigdownarrow: "Downarrow", bigleftarrow: "Leftarrow", bigleftrightarrow: ["Leftrightarrow", "iff"], biglongleftarrow: "Longleftarrow", biglongleftrightarrow: "Longleftrightarrow", biglongrightarrow: "Longrightarrow", to: [,"rightarrow"], bigto: ["Rightarrow","bigrightarrow","implies"], biguparrow: "Uparrow", bigupdownarrow: "Updownarrow",
		lleftarrow: "Lleftarrow", lsh: "Lsh", rrightarrow: "Rrightarrow", rsh: "Rsh", restriction: ["upharpoonright", "project" ],
		perp: [,"bot"], owns: [,"ni"], im: "Im", re: "Re", bbbk: "Bbbk", circleds: "circledS", finv: "Finv", game: "Game",
		lp: "(", rp: ")", lbrack: "[", rbrack: "]", lbrace: "{", rbrace: "}",
		bigbox : "Box", bigdiamond: "Diamond", lnot: "neg", surd: "sqrt",
		apostrophe: ["'", "tick"], atsign: "@", bang: "!", colon: ":", comma: ",", questionmark: "?", period:".", semicolon: ";", semi: "varsemicolon", solidus: "/",
		eq: "=", gt: [">", "gtr"], eq: "=", lt: ["<", "less"], minus: "-", plus: "+", nv2dash: "nVdash",
		llangle: "Langle", rrangle: "Rangle", llbrack: "[[", rrbrack: "]]", lplp: "((", rprp: "))", supast: ["*", "asterisk"], supfrown: "tie",
		subrmch: "subrmH", suplp: "sup(", suprp: "sup)", sublp: "sub(", subrp: "sub)",
		supeq: "sup=", subeq: "sub=", suplt: "sup<", sublt: "sub<", supgt: "sup>", subgt: "sub>", supge: "supgeq", subge: "subgeq", suple: "supleq", suble: "subleq",
		supminus: "sup-", subminus: "sub-", supplus: "sup+", subplus: "sub+"
	},
	
	// list of known valid UBB tags to exclude from image-to-text match
	ubbTagNames: [ "b", "i", "u", "s", "glow", "shadow", "move", "pre", "left", "center", "right", "hr", "size", "font", "color", "url", "ftp", "img", "email", "table", "tr", "td", "sup", "sub", "tt", "code", "quote", "list", "hide", "hideb" ],
	
	
	// prefix tree of all symbols and aliases, built dynamically by initTables()
	prefixTree: { },
	
	// Set of valid UBB tags, built dynamically by initTables()
	ubbTagSet: { },
	
	// the message textarea HTML DOM object
	textarea: null,
	
	// the "include math symbols" checkbox
	checkbox: null,

	// the currently displayed symbol selection menu pane
	paneShown: null,

	// flag to track state of menu during initial display
	paneShowing: false,
	
	// the symbols displayed in the history list
	historyList: { },
	
	// flags to track state across history navigation
	needsMessageTranslation: true,
	needsDisplayTranslation: true,

	
	
	// === methods ===

	// overall initialization
	init: function () { with (this) {
		initTables();
		if (getTextarea()) {
			initEntry();
			messageImagesToText();
			resizeTextarea();
		}
		registerEvent(window, 'load', load);
		try { addEventListener('pageshow', load, false); } catch (x) { }
	} },
	
	// initialize data structures
	initTables: function () {
		for(section in this.entryTable) {
			var rows = this.entryTable[section][1];
			for(var i = 0; i < rows.length; i++) {
				var supOrSub = false;
				for(var j = 0; j < rows[i].length; j++) {
					if (typeof rows[i][j] == "string") {
						this.insertPrefix(rows[i][j]);
						if (supOrSub) rows[i][j] = [ rows[i][j], null, null ];
					} else if (rows[i][j] != null) {
						if (!supOrSub) {
							for(var k = 0; k < j; k++) rows[i][k] = [ rows[i][k], null, null ];
							supOrSub = true;
						}
						for(var k = 0; k < 3; k++) if (rows[i][j][k]) this.insertPrefix(rows[i][j][k]);
					}
				}
			}
		}
		for(symbol in this.aliasTable) {
			if (typeof this.aliasTable[symbol] == "string") {
				this.insertPrefix(symbol, this.aliasTable[symbol]);
			} else for(var i = 0; i < this.aliasTable[symbol].length; i++) {
				this.insertPrefix(symbol, this.aliasTable[symbol][i])
			}
		}
		for(var i = 0; i < this.ubbTagNames.length; i++) {
			this.ubbTagSet[this.ubbTagNames[i]] = true;
		}
	},
	
	// add a string to the prefix table
	insertPrefix: function (symbol, alias) {
		if (symbol.indexOf(':') >= 0) symbol = symbol.substring(0, symbol.indexOf(':'));
		else if (symbol.indexOf(';') >= 0) symbol = symbol.substring(0, symbol.indexOf(';'));
		if (!alias) {
			alias = symbol;
		} else {
			// sanity-check symbol
			var sym = this.lookupPrefix(symbol);
			if (sym == null || symbol != sym.image) {
				throw (alias + " is not an alias for " + symbol + " (" + symbol + " is not defined)");
			}
			// sanity-check alias
			this.checkAlias(symbol, alias);
		}
		var node = this.prefixTree;
		for(var i = 0; i < alias.length; i++) {
			var prefix = alias.charAt(i);
			if (typeof node[prefix] == "undefined") node[prefix] = new Object();
			node = node[prefix];
		}
		node["image"] = symbol;
	},
	
	// lookup a string in the prefix table
	lookupPrefix: function(text) {
		var node = this.prefixTree, img = null, len = null;
		for(var i = 0; i < text.length; i++) {
			var prefix = text.charAt(i);
			if (typeof node["image"] != "undefined") { img = node["image"]; len = i }
			if (typeof node[prefix] == "undefined") { node = null; break; }
			node = node[prefix];
		}
		if (node && typeof node["image"] != "undefined") { img = node["image"]; len = text.length }
		return img ? { image: img, length: len } : null;
	},
	
	// lookup an image in the alias table
	getPreferredAlias: function(image) {
		if (typeof this.aliasTable[image] == "undefined") return image;
		var alias = this.aliasTable[image];
		if (typeof alias == "string") return alias;
		if (typeof alias[0] == "undefined" || alias[0] == null) return image;
		return alias[0];
	},
	
	// sanity check a given alias
	checkAlias: function(symbol, alias) {
		if (alias == symbol) return;
		if (typeof this.aliasTable[symbol] == "undefined") {
			throw (alias + " is not an alias for " + symbol + " (" + symbol + " has no aliases)");
		} else if (typeof this.aliasTable[symbol] == "string") {
			if (alias != this.aliasTable[symbol]) {
				throw (alias + " is not an alias for " + symbol);
			}
		} else {
			var ok = false;
			for(var i = 0; i < this.aliasTable[symbol].length; i++) {
				if (alias == this.aliasTable[symbol][i]) ok = true;
			}
			if (!ok) throw (alias + " is not an alias for " + symbol);
		}
	},
	
	// get the textarea HTML DOM object
	getTextarea: function () {
		var textareas = document.getElementsByTagName("TEXTAREA");
		for(var i = 0; i < textareas.length; i++) {
			if (textareas[i].name == "message") {
				return this.textarea = textareas[i];
			}
		}
		return this.textarea = null;
	},
	
	// resize the textarea to make better use of screen realestate
	resizeTextarea: function () {
		var node = this.textarea;
		while (node && node.tagName != "TD") node = node.parentNode;
		if (!node) return;
		this.textarea.style.width = (node.clientWidth > 390 ? node.clientWidth - 40 : 350) + "px";
		while (node && (node.tagName != "TABLE" || node.className != "bordercolor")) node = node.parentNode;
		if (!node) return;
		if (node.clientHeight < document.body.clientHeight * .75) {
			var height = this.textarea.clientHeight + document.body.clientHeight * .75 - node.clientHeight;
			this.textarea.style.height = (height > 180 ? height : 180) + "px";
		}
	},
	
	// convert text in message to image tags
	messageTextToImages: function () {
		var text = this.textarea.value, inCode = false, out = "";
		var r = new RegExp(/(?:\[\/?code\])|(?:\\(?:(?:[^\s\\]+)|(?:\\[^\s\\]*)))/gi);
		var match, begin = 0;
		while ((match = r.exec(text)) != null) {
			if (match[0].charAt(0) == '[') {
				inCode = (match[0].charAt(1) != '/');
			} else if (!inCode) {
				if (match.index > begin) out += text.substring(begin, match.index);
				if (match[0].charAt(1) == '\\') {
					out += '\\';
					begin = r.lastIndex = match.index + 2;
				} else {
					var symbol = this.lookupPrefix(match[0].substring(1));
					if (symbol) {
						out += "[img]" + this.url + symbol.image + ".gif[/img]";
						begin = r.lastIndex = match.index + symbol.length + 1;
						if (begin < text.length && text.charAt(begin) == ' ') begin++;
					} else {
						out += '\\';
						begin = r.lastIndex = match.index + 1;
					}
				}
			}
		}
		if (begin < text.length) out += text.substring(begin);
		this.textarea.value = out;
		this.needsMessageTranslation = true;
	},
	
	// convert image tags (and old [symbol] and
	//     [smiley=symbol.gif] syntax) in message to text
	messageImagesToText: function () {
		var text = this.textarea.value, inCode = false, out = "";
		// escape all backslashes
		text = text.replace(/\\/g, "\\\\")
		// convert any images
		var r = new RegExp(/(?:\[\/?[Cc][Oo][Dd][Ee]\])|(?:\[[Ii][Mm][Gg]\]\s*http:\/\/www\.ocf\.berkeley\.edu\/~wwu\/YaBBImages\/symbols\/([0-9a-z_]+)\.gif\s*\[\/[Ii][Mm][Gg]\])|(?:\[([a-z][0-9a-z_]+)\])|(?:\[[Ss][Mm][Ii][Ll][Ee][Yy]=([0-9a-z_]+)\.gif\])/g);
		var match, begin = 0;
		while ((match = r.exec(text)) != null) {
			if (match[0].match(/^\[\/?code\]$/i)) {
				inCode = (match[0].charAt(1) != '/');
			} else if (!inCode) {
				var i = 1; while (i < match.length && typeof match[i] == "undefined") i++;
				var image = match[i], symbol = this.lookupPrefix(image);
				if (symbol != null &&
					(symbol.image == image || symbol.image == "surd" && image == "sqrt") &&
					(i != 2 || typeof this.ubbTagSet[image] == "undefined"))
				{
					this.addHistory(symbol.image);
					image = this.getPreferredAlias(image);
					if (match.index > begin) out += text.substring(begin, match.index);
					begin = r.lastIndex;
					out += '\\' + image;
					if (begin < text.length && (text.charAt(begin) == ' ' ||
						this.lookupPrefix(image + text.substring(begin)).length > image.length))
					{
						out += " ";
					}
				}
			}
		}
		if (begin < text.length) out += text.substring(begin);
		if (this.checkbox.checked) {
			// unescape backslashes inside code blocks
			text = out; out = ""; begin = 0; inCode = false;
			var rcode = new RegExp(/\[\/?code\]/gi);
			while ((match = rcode.exec(text)) != null) {
				if (match[0].charAt(1) != '/' && !inCode) {
					out += text.substring(begin, rcode.lastIndex);
					begin = rcode.lastIndex;
					inCode = true;
				} else if (match[0].charAt(1) == '/' && inCode) {
					out += text.substring(begin, rcode.lastIndex).replace(/\\\\/g, '\\');
					begin = rcode.lastIndex;
					inCode = false;
				}
			}
			if (begin < text.length) out += text.substring(begin);
		} else {
			// revert to single backslashes
			out = out.replace(/\\\\/g, '\\');
		}
		this.textarea.value = out;
		this.needsMessageTranslation = false;
	},
	
	// convert old [symbol] and [smiley=symbol.gif] tags for display
	displayTextToImages: function () {
		var cells = document.getElementsByTagName("td");
		for(var i = 0; i < cells.length; ++i) {
			if (cells[i].className == "windowbg" || cells[i].className == "windowbg2") {
				var n = cells[i].firstChild;
				while (n != null) {
					if (n.nodeType == 3) {
						var text = n.data;
						var r = new RegExp(/(?:\[([a-z][0-9a-z_]+)\])|(?:\[[Ss][Mm][Ii][Ll][Ee][Yy]=([0-9a-z_]+)\.gif\])/g);
						var match, begin = 0;
						while ((match = r.exec(text)) != null) {
							var j = 1; while (j < match.length && typeof match[j] == "undefined") j++;
							var image = match[j], symbol = this.lookupPrefix(image);
							if (symbol != null &&
								(symbol.image == image ||
									symbol.image == "surd" && image == "sqrt"))
							{
								if (match.index > begin) {
									n.parentNode.insertBefore(document.createTextNode(text.substring(begin, match.index)), n);
								}
								begin = r.lastIndex;
								var img = document.createElement("img");
								img.src = this.url + symbol.image + ".gif";
								n.parentNode.insertBefore(img, n);
							}
						}
						if (begin < text.length) {
							n.parentNode.insertBefore(document.createTextNode(text.substring(begin)), n);
						}
						var prev = n.previousSibling;
						n.parentNode.removeChild(n);
						n = prev;
					}
					
					if (n.firstChild != null &&
					    ((n.tagName != "TD") || (n.className != "windowbg" && n.className != "windowbg2")) &&
					    ((n.tagName != "TABLE") || n.className != "code") &&
					    (n.tagName != "TEXTAREA"))
					{
						n = n.firstChild
					} else {
						while (n != null && n != cells[i] && n.nextSibling == null) {
							n = n.parentNode;
						}
						if (n != null && n != cells[i]) {
							n = n.nextSibling;
						} else {
							break;
						}
					}
				}
			}
		}
		this.needsDisplayTranslation = false;
	},
	
	// graphical entry initialization
	initEntry: function () {
		// create "Add Symbols" row above textarea
		var mtd = this.textarea.parentNode; while (mtd.tagName != "TD") mtd = mtd.parentNode;
		var mtr = mtd.parentNode;
		var mtbody = mtr.parentNode;
		var newtr = document.createElement("tr");
		newtr.id = "wu_symbol_row";
		mtbody.insertBefore(newtr, mtr);
		newtr = document.getElementById("wu_symbol_row");
		// newtr.innerHTML = '<td class="windowbg2" bgcolor="#444444" width="23%"><font size=2><b>Add Symbols:</b></font></td><td id="wu_symbol_cell" valign=middle class="windowbg2" bgcolor="#444444"></td>';
		// var newtd = document.getElementById("wu_symbol_cell");
		newtr.appendChild(document.createElement("td"));
		var newtd = newtr.lastChild;
		newtd.className = "windowbg2";
		newtd.style.backgroundColor = "#444444";
		newtd.appendChild(document.createElement("font"));
		var newfont = newtd.lastChild;
		newfont.size = "2";
		newfont.appendChild(document.createElement("b"));
		newfont.lastChild.appendChild(document.createTextNode("Add Symbols:"));
		newtr.appendChild(document.createElement("td"));
		newtd = newtr.lastChild;
		newtd.id = "wu_symbol_cell";
		newtd.className = "windowbg2";
		newtd.style.verticalAlign = "middle";
		newtd.style.backgroundColor = "#444444";
		
		// create table of symbol sections
		newtd.appendChild(document.createElement("table"));
		var otable = newtd.lastChild;
		otable.cellSpacing = otable.cellPadding = 0;
		otable.style.borderWidth = "1px";
		otable.style.borderStyle = "solid";
		otable.style.borderColor = "black";
		otable.appendChild(document.createElement("tbody"));
		var otbody = otable.lastChild;
		otbody.appendChild(document.createElement("tr"));
		var otr = otbody.lastChild;
		otr.appendChild(document.createElement("td"));
		var otd = otr.lastChild;
		otd.appendChild(document.createElement("table"));
		var table = otd.lastChild;
		table.id = "wu_symbolmenu_table";
		table.cellSpacing = table.cellPadding = 0;
		table.style.backgroundColor = "#333333"; // "#272A2F"
		this.registerEvent(table, 'mouseover', this.highlight);
		this.registerEvent(table, 'mouseout', this.unhighlight);
		this.registerEvent(table, 'click', this.display)
		table.style.cursor = "pointer";
		newtd.appendChild(document.createElement("br"));
		table.appendChild(document.createElement("tbody"));
		var tbody = table.lastChild;
		tbody.appendChild(document.createElement("tr"));
		var tr = tbody.lastChild;
		
		// create symbol section images
		var maxHeight = 20;
		for (var section in this.entryTable) {
			tr.appendChild(document.createElement("td"));
			var td = tr.lastChild;
			td.setAttribute("name", section);
			td.id = "wu_symbol_menu_" + section;
			td.appendChild(document.createElement("div"));
			var div = td.lastChild;
			div.style.width = "38px";
			div.style.height = maxHeight + "px";
			div.style.textAlign = "center";
			div.style.verticalAlign = "middle";
			div.appendChild(document.createElement("img"));
			var img = div.lastChild;
			img.id = "wu_symbol_img_" + section;
			img.src = this.url + section + ".gif";
			img.title = this.entryTable[section][0];
			if ((img.clientHeight + 4) > maxHeight) {
				maxHeight = img.clientHeight + 4;
				for(var tdd = tr.firstChild; tdd != td; tdd = tdd.nextSibling) {
					tdd.lastChild.style.height = maxHeight + "px";
				}
			}
		}
		
		// create "Insert Math Symbols" checkbox
		var inputs = document.getElementsByTagName("INPUT");
		mtd = null;
		for(var i = 0; i < inputs.length; i++) {
			if (inputs[i].type == "checkbox" && inputs[i].name == "ns") {
				mtd = inputs[i].parentNode;
				while (mtd.tagName != "TD") mtd = mtd.parentNode;
				break;
			}
		}
		mtr = mtd.parentNode;
		mtbody = mtr.parentNode;
		newtr = document.createElement("tr");
		newtr.id = "wu_symbol_checkrow";
		mtbody.insertBefore(newtr, mtr);
		newtr = document.getElementById("wu_symbol_checkrow");
		// newtr.innerHTML = '<td class="windowbg"><font size=2><b>Insert Symbols:</b></font></td><td class="windowbg"><input id="wu_symbol_checkbox" type=checkbox> <font size="1">Check this if you want to insert math symbols as graphics</font></td>';
		// this.checkbox = document.getElementById("wu_symbol_checkbox");
		newtr.appendChild(document.createElement("td"));
		var newtd = newtr.lastChild;
		newtd.className = "windowbg";
		newtd.appendChild(document.createElement("font"));
		var newfont = newtd.lastChild;
		newfont.size = "2";
		newfont.appendChild(document.createElement("b"));
		newfont.lastChild.appendChild(document.createTextNode("Insert Symbols:"));
		newtr.appendChild(document.createElement("td"));
		newtd = newtr.lastChild;
		newtd.className = "windowbg";
		var newinput = document.createElement("input");
		newinput.id = "wu_symbol_checkbox";
		newinput.type = "checkbox";
		newtd.appendChild(newinput);
		this.checkbox = newtd.lastChild;
		newtd.appendChild(document.createTextNode(" "));
		newtd.appendChild(document.createElement("font"));
		newfont = newtd.lastChild;
		newfont.size = "1";
		newfont.appendChild(document.createTextNode("Check this if you want to insert math symbols as graphics"));
		
		// add global click, blur, resize listeners
		this.registerEvent(document.body, 'click', this.undisplay);
		this.registerEvent(window, 'blur', this.undisplay);
		this.registerEvent(window, 'resize', this.resize);
		
		// add form submittal and reset listeners
		this.registerEvent(document.forms.namedItem("postmodify"), 'submit', this.submit);
		this.registerEvent(document.forms.namedItem("postmodify"), 'reset', this.reset);
	},
	
	// entry pane builder
	createPane: function (td, left, top) {
		document.body.appendChild(document.createElement("div"));
		var div = document.body.lastChild;
		div.setAttribute("name", td.getAttribute("name"));
		div.id = "wu_symbol_pane_" + td.getAttribute("name");
		div.style.position = "absolute";
		div.style.left = left + "px";
		div.style.top = top + "px";
		div.style.width = "600px";
		div.style.height = "400px";
		this.registerEvent(div, 'click', this.select);
		div.appendChild(document.createElement("table"));
		var xtable = div.lastChild;
		xtable.style.borderWidth = "1px";
		xtable.style.borderStyle = "solid";
		xtable.style.borderColor = "black";
		xtable.style.backgroundColor = "#0A246A";
		xtable.cellSpacing = 1; xtable.cellPadding = 4;
		xtable.appendChild(document.createElement("tbody"));
		var xtbody = xtable.lastChild;
		xtbody.appendChild(document.createElement("tr"));
		var xtr = xtbody.lastChild;
		xtr.appendChild(document.createElement("td"));
		var xtd = xtr.lastChild;
		
		xtd.appendChild(document.createElement("table"));
		var itable = xtd.lastChild;
		itable.appendChild(document.createElement("tbody"));
		var itbody = itable.lastChild;
		var rows = this.entryTable[td.getAttribute("name")][1], nColumns = 0, hasSupSub = false, r;
		for(r = 0; r < rows.length; r++) {
			if (typeof rows[r][0] != "string") {
				hasSupSub = true;
				if ((rows[r].length << 1) > nColumns) nColumns = rows[r].length << 1;
			} else if (rows[r].length > nColumns) nColumns = rows[r].length;
		}
		var prevRowSpan = 0; // kludge for wutang with rowspan = 2
		for(r = 0; r < rows.length; r++) {
			if (typeof rows[r][0] == "string") {
				itbody.appendChild(document.createElement("tr"));
				var itr = itbody.lastChild, cells = prevRowSpan;
				prevRowSpan = 0;
				for(var c = 0; c < rows[r].length; c++) {
					itr.appendChild(document.createElement("td"));
					var itd = itr.lastChild;
					if (typeof rows[r][c] == "string") this.createImage(itd, rows[r][c]);
					cells += (itd.colSpan && parseInt(itd.colSpan) > 1) ? parseInt(itd.colSpan) : 1;
					if (itd.rowSpan && parseInt(itd.rowSpan) > 1) {
						prevRowSpan = (itd.colSpan && parseInt(itd.colSpan) > 1) ? parseInt(itd.colSpan) : 1;
					}
				}
				if (cells < nColumns) {
					itr.appendChild(document.createElement("td"));
					var itd = itr.lastChild;
					itd.colSpan = (nColumns - cells);
				}
			} else {
				prevRowSpan = 0;
				if (r > 0 && typeof rows[r-1][0] == "string") {
					itbody.appendChild(document.createElement("tr"));
					var itr = itbody.lastChild;
					itr.appendChild(document.createElement("td"));
					var itd = itr.lastChild;
					itd.colSpan = nColumns;
					itd.appendChild(document.createTextNode("\u00A0"));
				}
				itbody.appendChild(document.createElement("tr"));
				var itr1 = itbody.lastChild;
				itbody.appendChild(document.createElement("tr"));
				var itr2 = itbody.lastChild;
				for(var c = 0; c < rows[r].length; c++) {
					itr1.appendChild(document.createElement("td"));
					var itd = itr1.lastChild;
					itd.rowSpan = 2;
					if (typeof rows[r][c] == "object") {
						if (typeof rows[r][c][0] == "string") this.createImage(itd, rows[r][c][0]);
						itr1.appendChild(document.createElement("td"));
						var itd = itr1.lastChild;
						if (typeof rows[r][c][1] == "string") this.createImage(itd, rows[r][c][1]);
						itr2.appendChild(document.createElement("td"));
						var itd = itr2.lastChild;
						if (typeof rows[r][c][2] == "string") this.createImage(itd, rows[r][c][2]);
					} else {
						itd.colSpan = "2";
					}
				}
				if ((rows[r].length << 1) < nColumns) {
					itr1.appendChild(document.createElement("td"));
					var itd = itr1.lastChild;
					itd.colSpan = nColumns - (rows[r].length << 1);
					itd.rowSpan = 2;
				}
				if (r != rows.length - 1) {
					itbody.appendChild(document.createElement("tr"));
					var itr = itbody.lastChild;
					itr.appendChild(document.createElement("td"));
					var itd = itr.lastChild;
					itd.colSpan = nColumns;
					itd.appendChild(document.createTextNode("\u00A0"));
				}
			}
		}
		
		div.style.display = "block";
		div.style.width = xtable.clientWidth + "px";
		div.style.height = xtable.clientHeight + "px";
		return div;
	},
	
	// create image for pane
	createImage: function (td, symbol) {
		var alias;
		if (symbol.indexOf(';') >= 0) {
			var rc = symbol.substring(symbol.indexOf(';') + 1);
			td.style.textAlign = "center";
			if (rc.indexOf(',') < 0) {
				td.colSpan = parseInt(rc);
			} else {
				td.style.verticalAlign = "middle";
				td.colSpan = parseInt(rc.substring(0, rc.indexOf(',')));
				td.rowSpan = parseInt(rc.substring(rc.indexOf(',') + 1));
			}
			symbol = symbol.substring(0, symbol.indexOf(';'));
		}
		if (symbol.indexOf(':') < 0) {
			alias = this.getPreferredAlias(symbol);
		} else {
			alias = symbol.substring(symbol.indexOf(':') + 1);
			symbol = symbol.substring(0, symbol.indexOf(':'));
			this.checkAlias(symbol, alias);
		}
		td.appendChild(document.createElement("img"));
		var img = td.lastChild;
		img.src = this.url + symbol + ".gif";
		img.title = '\\' + alias;
		img.style.cursor = "pointer";
	},
	
	// insert symbol into textarea
	insert: function(symbol) {
		// add to the history list
		this.addHistory(symbol);
		// attempt to call the AddText function defined in ubcc.js
		var text = '\\' + symbol + ' ';
		try {
			if (unsafeWindow) {
				unsafeWindow.AddText(text);
			} else {
				AddText(text);
			}
		} catch (x) {
			// fall back on simple appending to textarea.value
			this.textarea.value += text;
		}
	},
	
	// insert symbol into the history list
	addHistory: function(symbol) {
		// check the use symbols checkbox
		this.checkbox.checked = true;
		// check if already in the list
		var image = this.lookupPrefix(symbol).image;
		if (typeof this.historyList[image] != "undefined") return;
		this.historyList[image] = true;
		// prepend to history image list
		var cell = document.getElementById("wu_symbol_cell")
		if (cell.firstChild.tagName == "TABLE") {
			// create space between history list and menu table
			cell.insertBefore(document.createElement("br"), cell.firstChild);
			cell.insertBefore(document.createElement("br"), cell.firstChild);
			// kludge to workaround an occasional FF bug where the
			//    height of the TD is not changed when the first
			//    image is inserted)
			setTimeout("document.getElementById('wu_symbol_cell').style.display = '" + cell.style.display + "';", 0);
			cell.style.display = "none";
		} else {
			// create space between history images
			cell.insertBefore(document.createTextNode("\u00A0"), cell.firstChild);
		}
		var img = document.createElement("img");
		img.src = this.url + image;
		img.title = "\\" + this.getPreferredAlias(image);
		img.style.cursor = "pointer";
		this.registerEvent(img, 'click', this.select);
		cell.insertBefore(img, cell.firstChild);
	},
	
	// clear the history list
	clearHistory: function () {
		this.historyList = { };
		var cell = document.getElementById("wu_symbol_cell");
		while (cell.firstChild.tagName != "TABLE") cell.removeChild(cell.firstChild);
	},

	// display a pane (section) of related symbols for the given
	//   menu entry
	showpane: function (td) {
		var elem = document.getElementById("wu_symbolmenu_table");
		var left = 0, top = elem.clientHeight;
		while (elem != document.body) {
			if (elem.offsetLeft > 0) left += elem.offsetLeft;
			if (elem.offsetTop > 0) top += elem.offsetTop;
			elem = elem.offsetParent;
		}
		this.paneShown = document.getElementById("wu_symbol_pane_" + td.getAttribute("name"));
		if (this.paneShown == null) {
			this.paneShown = this.createPane(td, left, top);
		} else {
			this.paneShown.style.left = left + "px";
			this.paneShown.style.top = top + "px";
			this.paneShown.style.display = "block";
		}
	},
	
	// cross-platform event registration
	registerEvent: function (object, event, fn) {
		if (object.addEventListener) {
			object.addEventListener(event, fn, false);
		} else if (object.attachEvent) {
			object.attachEvent("on" + event, fn);
		} else if (typeof object["on" + event] != "undefined") {
			var prevfn = object["on" + event];
			// yea for closures
			object["on" + event] = function (e) {
				fn(e);
				if (typeof prevfn == "function") {
					return prevfn(e);
				} else {
					eval(prevfn);
				}
			}
		} else {
			object["on" + event] = fn;
		}
	},



	// === event handlers ===
	
	// do post-load initialization
	load: function(e) {
		if (wu_symbols.textarea != null &&
			wu_symbols.needsMessageTranslation)
		{
			wu_symbols.messageImagesToText();
		}
		if (wu_symbols.needsDisplayTranslation) {
			wu_symbols.displayTextToImages();
		}
		if (wu_symbols.textarea != null) {
			wu_symbols.resizeTextarea();
		}
	},
	
	// highlight a "menu" choice
	highlight: function(e) {
		var event = e ? e : window.event;
		var target = event.target ? event.target : event.srcElement;
		var relatedTarget = event.relatedTarget ? event.relatedTarget : event.fromElement;
		try {
			// ignore if mousing from descendent to ancestor
			var rel = relatedTarget;
			while (rel && rel != target) rel = rel.parentNode;
			if (rel == target) return;
			//    or if not mousing into a specific cell
			var targ = target;
			while (targ.tagName != "TD" && targ.tagName != "TABLE") {
				if (targ == relatedTarget) return;
				targ = targ.parentNode;
			}
			if (targ == relatedTarget || targ.tagName == "TABLE") return;
		} catch (x) { return; }
		// show the related menu pane if appropriate
		if (wu_symbols.paneShown)
		{
			wu_symbols.paneShown.style.display = "none";
			document.getElementById("wu_symbol_menu_" + wu_symbols.paneShown.getAttribute("name")).style.backgroundColor = "#333333";
			wu_symbols.showpane(targ);
		}
		// highlight the table cell
		targ.style.backgroundColor = "#0A246A";
	},
	
	// unhighlight a "menu" choice
	unhighlight: function(e) {
		var event = e ? e : window.event;
		var target = event.target ? event.target : event.srcElement;
		var relatedTarget = event.relatedTarget ? event.relatedTarget : event.toElement;
		// ignore if menu shown
		if (wu_symbols.paneShown) return;
		try {
			// ignore if mousing to descendent from ancestor
			var rel = relatedTarget;
			while (rel && rel != target) rel = rel.parentNode;
			if (rel == target) return;
			//    or if not mousing from a specific cell
			var targ = target;
			while (targ.tagName != "TD" && targ.tagName != "TABLE") {
				if (targ == relatedTarget) return;
				targ = targ.parentNode;
			}
			if (targ == relatedTarget || targ.tagName == "TABLE") return;
		} catch (x) { return; }
		// unhighlight the table cell
		targ.style.backgroundColor = "#333333";
	},
	
	// display the symbol selection menu
	display:  function (e) {
		var event = e ? e : window.event;
		var target = event.target ? event.target : event.srcElement;
		// ignore if menu already displayed
		if (wu_symbols.paneShown) return;
		try {
			// ignore if not clicking a specific cell
			var targ = target;
			while (targ.tagName != "TD" && targ.tagName != "TABLE") targ = targ.parentNode;
			if (targ.tagName == "TABLE") return;
		} catch (x) { return; }
		// show the related menu pane
		wu_symbols.paneShowing = true;
		wu_symbols.showpane(targ);
	},
	
	// hide the symbol selection menu
	undisplay: function (e) {
		var event = e ? e : window.event;
		var target = event.target ? event.target : event.srcElement;
		// ignore if menu not shown or in process of being shown
		if (!wu_symbols.paneShown || wu_symbols.paneShowing) {
			wu_symbols.paneShowing = false;
			return;
		}
		// hide menu pane
		wu_symbols.paneShown.style.display = "none";
		var header = document.getElementById("wu_symbol_menu_" + wu_symbols.paneShown.getAttribute("name"));
		wu_symbols.paneShown = null;
		try {
			// unhighlight menu header unless clicked on it
			var targ = target;
			while (targ && targ.tagName != "TD" && targ.tagName != "TABLE") {
				targ = targ.parentNode;
			}
			if (targ != header) header.style.backgroundColor = "#333333";
		} catch (x) { return; }
	},
	
	// insert the clicked symbol to the textarea
	select: function (e) {
		var event = e ? e : window.event;
		var target = event.target ? event.target : event.srcElement;
		try { if (target.tagName != "IMG") return; } catch (x) { return; }
		wu_symbols.insert(target.title.substring(1));
	},
	
	// reposition the active menu pane when the window is resized
	resize: function (e) {
		if (wu_symbols.textarea != null) {
			// reset to default width and height
			wu_symbols.textarea.style.width = "350px";
			wu_symbols.textarea.style.height = "180px";
			// resize testarea after resize event completes
			setTimeout(function () { wu_symbols.resizeTextarea(); }, 0);
		}
		if (wu_symbols.paneShown) {
			wu_symbols.showpane(document.getElementById("wu_symbol_menu_" + wu_symbols.paneShown.getAttribute("name")));
		}
	},
	
	// convert text to images before submitting the form
	submit: function (e) {
		if (wu_symbols.checkbox.checked) {
			wu_symbols.messageTextToImages();
		}
		// reconvert images after submitting the form
		setTimeout(function () { wu_symbols.messageImagesToText(); }, 0);
	},
	
	// clear the history, then reconvert images to text *after* resetting the form
	reset: function (e) {
		wu_symbols.clearHistory();
		wu_symbols.checkbox.checked = false;
		setTimeout(function () { wu_symbols.messageImagesToText(); }, 0);
	}
}

wu_symbols.init();
