// Copyright 2008-2011 Andrey Zholos. All rights reserved.

/* Menu class */

Menu = function() {
    this.c = [];
}

Menu.prototype.i = function(el) {
    if (this.el) {
        delete this.hlp;
        this.el.style.display = "none";
        removeChildren(this.el);
    }
    this.el = el;
    if (el) {
        this.cp = [];
        this.cv = [];
        this.e(true);
        el.style.display = "block";
    }
    this.e();
}

Menu.prototype.a = function(a) {
    if (!this.o) {
        this.o = new Array(this.c.length);
        this.oi = 0;
        this.ob = this.c.length;
    }
    if (a) {
        for (var i = 0, n = this.c.length; i < n; i++) {
            if (arrayEqual(this.c[i], a)) {
                this.o[i] = ++this.oi;
                break;
            }
        }
        if (i >= n) {
            this.c.push(a);
            this.o.push(++this.oi);
        }
    }
}

Menu.prototype.e = function(i) {
    if (!this.o)
        if (i) {
            this.o = [];
            for (var i = 0; i < this.c.length;)
                this.o.push(++i);
            this.ob = 0;
        } else
            return;

    if (this.el) {
        for (var i = 0; i < this.ob; i++)
            if (!this.o[i]) {
                this.cp[i].style.visibility = "hidden";
                this.cv[i].unregister();
                this.cv[i] = null;
            }
        var n = this.el.firstChild;
        for (; i < this.c.length; i++) {
            var c = new chat.C(this.c[i]);
            var p = createElement("p");
            p.className = "command";
            p.style.margin = "0px";
            p.style.padding = ".25em .5em";
            c.d(p);
            registerEventHandler(p, "mouseover", function(m, p) { return function(ev) { m.hl(p); }; }(this, p));
            var v = registerEventHandler(p, "click", function(cs, c) { return function(ev) { cs.cact(c); }; }(chat, c));
            v.c = c;
            this.el.insertBefore(p, n);
            this.cp.splice(i - this.ob, 0, p);
            this.cv.splice(i - this.ob, 0, v);
        }
        this.el.style.width = this.el.firstChild ? "" : "10em";
        if (this.ob && i > this.ob) {
            var hr = createElement("div");
            hr.style.borderBottom = "1px dashed #999";
            hr.style.margin = "2px 0px";
            this.el.insertBefore(hr, n);
        }
    }

    var cn = [];
    for (var oi = 1;; oi++) {
        var i = this.o.indexOf(oi);
        if (i < 0)
            break;
        cn.push(this.c[i]);
    }
    this.c = cn;
    delete this.o;
    delete this.oi;
    delete this.ob;
}

Menu.prototype.hl = function(p) {
    if (this.hlp) {
        this.hlp.className = "command";
        delete this.hlp;
    }
    var i = this.cp.indexOf(p);
    if (i >= 0 && this.cv[i]) {
        this.hlp = p;
        p.className = "command-selected";
    }
}

Menu.prototype.hlk = function(d) {
    var i = this.cp.indexOf(this.hlp);
    if (d == 0) {
        if (i >= 0 && this.cv[i])
            chat.cact(this.cv[i].c);
    } else
        for (var j = 0; j < this.cp.length; j++) {
            i += d;
            if (i < 0)
                i = this.cp.length - 1;
            else if (i >= this.cp.length)
                i = 0;
            if (this.cv[i]) {
                this.hl(this.cp[i]);
                break;
            }
        }
}


/* Chat class */

function Chat() {
}

// Command
Chat.prototype.C = function(a) {
    if (a) {
        a = a.slice();
        this.el = [];
        var titles = [];
        if (a.length && a[a.length-1].charAt(0) == "*") {
            titles = a.pop().slice(1);
            loop: for (;;) {
                switch (titles.charAt(0)) {
                    case "*":
                        break;
                    case "=":
                        this.remain = true;
                        break;
                    default:
                        break loop;
                }
                titles = titles.slice(1);
            }
            titles = titles.split(",");
        }
        for (var i = 0; i < a.length; i++) {
            switch (a[i].charAt(0)) {
            case "'":
                this.el.push(new chat.El(a[i].slice(1)));
                break;
            case "w":
                this.el.push(new chat.Ec("", 0, titles.shift()));
                break;
            case "W":
                this.el.push(new chat.Ec("", 1, titles.shift()));
                break;
            case "t":
                this.el.push(new chat.Et([], titles.shift()));
                break;
            case "o":
                this.el.push(new chat.Eo(0, "", "oci", titles.shift()));
                break;
            case "c":
                this.el.push(new chat.Eo(0, "", "c", titles.shift()));
                break;
            case "i":
                this.el.push(new chat.Eo(0, "", "i", titles.shift()));
                break;
            }
        }
        this.plain = false
    } else {
        this.el = [ new chat.El(">"), new chat.Ec("", 2) ];
        this.plain = true;
    }
}

Chat.prototype.C.prototype.c = function(clear) {
    var c = new this.constructor([]);
    c.remain = this.remain;
    for (var i = 0, n = this.el.length; i < n; i++)
        c.el.push(this.el[i].c(clear));
    return c;
}

Chat.prototype.C.prototype.i = function(ei) {
    removeChildren(ei);
    var c = { ei: ei, cs: this };
    for (var i = 0, n = this.el.length; i < n; i++) {
        c.i = i;
        this.el[i].i(c);
    }
    this.focus = -1;
    setTimeout(function(cs) { return function() { cs.tab(1); }; }(this), 1);
}

Chat.prototype.C.prototype.tab = function(delta) {
    var n = this.el.length;
    if (this.el[this.focus] && this.el[this.focus].unfocus)
        this.el[this.focus].unfocus();

    function next(x) { x += delta; if (x < 0) x = n - 1; if (x > n) x = 0; return x; };
    var m = next(this.focus);
    var i = m;
    do {
        if (i < n && this.el[i].focus) {
            this.focus = i;
            this.el[i].focus();
            return;
        }
        i = next(i);
    } while (i != m);

    this.focus = 0;
    client.body.focus();
}

Chat.prototype.C.prototype.d = function(p) {
    removeChildren(p);
    var c = { p: p };
    for (var i = 0, n = this.el.length; i < n; i++) {
        c.i = i;
        this.el[i].d(c);
    }
}

Chat.prototype.C.prototype.auto = function() {
    var c = { v: [], auto: true };
    for (var i = 0, n = this.el.length; i < n; i++)
        this.el[i].v(c);
    return c.auto;
}

Chat.prototype.C.prototype.empty = function() {
    var c = { v: [], empty: true };
    for (var i = 0, n = this.el.length; i < n; i++)
        this.el[i].v(c);
    return c.empty;
}

Chat.prototype.C.prototype.v = function() {
    var c = { v: [] };
    for (var i = 0, n = this.el.length; i < n; i++)
        this.el[i].v(c);
    return c.v;
}

Chat.prototype.C.prototype.co = function(co) {
    return this.el[this.focus] && this.el[this.focus].co && this.el[this.focus].co(co) || false;
}

Chat.prototype.C.prototype.ct = function(ct) {
    return this.el[this.focus] && this.el[this.focus].ct && this.el[this.focus].ct(ct) || false;
}

Chat.prototype.C.prototype.lo = function(lo) {
    for (var i = 0, n = this.el.length; i < n; i++)
        if (this.el[i].ot)
            lo[this.el[i].ot] = true;
}


// basic command element
Chat.prototype.E = function(f) {
    f.prototype.ec = function() {
        var ec = createElement("td");
        ec.style.verticalAlign = "middle";
        ec.style.paddingLeft = ec.style.paddingRight = ".4em";
        return ec;
    }
    return f;
}

// text command element
Chat.prototype.Ec = Chat.prototype.E(function(t, cm, title) {
    this.t = t || "";
    this.cm = cm;
    this.title = title;
});

Chat.prototype.Ec.prototype.c = function(clear) {
    return new this.constructor(clear ? "" : this.t, this.cm, this.title);
}

Chat.prototype.Ec.prototype.i = function(c) {
    var e = createElement("input");
    e.setAttribute("type", "text");
    e.style.color = "black"; // help override custom themes
    e.style.backgroundColor = "white";
    e.style.borderStyle = "none";
    e.style.width = "99%";
    e.value = this.t;
    if (this.cm == 2) {
        var ec = this.ec();
        ec.style.backgroundColor = "white";
        ec.style.borderLeft = "1px solid #ccc";
        ec.appendChild(e);
        c.ei.appendChild(ec);
    } else {
        var eqlc = this.ec();
        eqlc.style.width = "1px";
        eqlc.style.padding = "0px .1em 0px .15em";
        var eql = createElement("span");
        eql.appendChild(document.createTextNode("\u201f"));
        eqlc.appendChild(eql);
        c.ei.appendChild(eqlc);

        var ec = this.ec();
        ec.style.padding = "0px";
        var ed = createElement("div");
        ed.style.padding = "1px";
        ed.style.backgroundColor = "white";
        ed.style.border = "1px solid #ccc";
        ed.appendChild(e);
        ec.appendChild(ed);
        c.ei.appendChild(ec);

        var eqrc = this.ec();
        eqrc.style.width = "1px";
        eqrc.style.padding = "0px .15em 0px .1em";
        var eqr = createElement("span");
        eqr.appendChild(document.createTextNode("\u201d"));
        eqrc.appendChild(eqr);
        c.ei.appendChild(eqrc);

        var quote = function(cm, e, eql, eqr) { return function quote() { eql.style.display = eqr.style.display = (cm ? /"/ : /"|\s/).test(e.value) ? "" : "none"; }; }(this.cm, e, eql, eqr);
        quote();
        quote = function(quote) { return function() { setTimeout(quote, 1); }; }(quote);
        registerEventHandler(e, "keypress", quote);
        registerEventHandler(e, "keydown", quote);
        registerEventHandler(e, "focus", function(cs, i) { return function() { cs.focus = i; }; }(c.cs, c.i));
    }
    this.ev = e;
}

Chat.prototype.Ec.prototype.focus = function() {
    this.ev.focus();
    this.ev.select();
}

Chat.prototype.Ec.prototype.d = function(c) {
    if (this.title) {
        if (c.p.firstChild)
            c.p.appendChild(document.createTextNode(" "));
        var span = createElement("span");
        span.className = "command-fieldname";
        span.appendChild(document.createTextNode(this.title));
        c.p.appendChild(span);
    }
}

Chat.prototype.Ec.prototype.v = function(c) {
    this.t = this.ev.value;
    if (this.cm == 2) {
        c.v = [];
        var w0 = this.t.match(/^\s*(\W?)(.*?)\s*$/);
        if (w0) {
            var w = w0[2].match(/([^\s'"]\S*|('[^']*'|"[^"]*")([^\s'"]*('[^']*'|"[^"]*"))*)(?=\s|$)/g);
            if (w) {
                for (var i = 0, n = w.length; i < n; i++)
                    c.v.push("'" + (i == 0 && w0[1] ? w0[1] : "") + (w[i].match(/^['"].*['"]$/) ? w[i].replace(/('[^']*')+|("[^"]*")+/g, function(x) { return x.slice(1,-1).replace(x.charAt(0)=="'"?/''/g:/""/g,x.charAt(0)) }) : w[i]));
            }
        }
    } else
        c.v.push("'" + this.t);
    c.auto = false;
    if (this.t)
        c.empty = false;
}

// label command element
Chat.prototype.El = Chat.prototype.E(function(l) {
    this.l = l;
});

Chat.prototype.El.prototype.c = function() {
    return new this.constructor(this.l);
}

Chat.prototype.El.prototype.i = function(c) {
    var e = createElement("span");
    e.style.whiteSpace = "nowrap";
    e.appendChild(document.createTextNode(this.l));
    var ec = this.ec();
    ec.style.width = "1px";
    ec.appendChild(e);
    if (c.i == 0) {
        ec.style.cursor = "default";
        registerEventHandler(ec, "click", function(cs) { return function(ev) { cs.mact(true); }; }(chat));
        registerEventHandler(ec, "mouseover", function(cs, ec) { return function(ev) { cs.evmi(ev, ec); }; }(chat, ec));
        registerEventHandler(ec, "mouseout", function(cs, ec) { return function(ev) { cs.evmo(ev, ec); }; }(chat, ec));
    }
    c.ei.appendChild(ec);
}

Chat.prototype.El.prototype.d = function(c) {
    if (c.p.firstChild)
        c.p.appendChild(document.createTextNode(" "));
    var span = createElement("span");
    span.appendChild(document.createTextNode(this.l));
    c.p.appendChild(span);
}

Chat.prototype.El.prototype.v = function(c) {
    c.v.push("'" + this.l);
}

// tile command element
Chat.prototype.Et = Chat.prototype.E(function(xy, title) {
    this.xy = xy;
    this.title = title;
});

Chat.prototype.Et.prototype.c = function(clear) {
    return new this.constructor(clear ? [] : this.xy, this.title);
}

Chat.prototype.Et.prototype.i = function(c) {
    var ec = this.ec();
    ec.style.backgroundColor = "#fee";
    ec.style.borderLeft = "1px solid #ccc";
    ec.style.cursor = "default";
    registerEventHandler(ec, "click", function(es) { return function() { es.xy = []; es.uv(); } }(this));
    c.ei.appendChild(ec);
    this.ec = ec;
    this.uv();
}

Chat.prototype.Et.prototype.d = function(c) {
    if (this.title) {
        if (c.p.firstChild)
            c.p.appendChild(document.createTextNode(" "));
        var span = createElement("span");
        span.className = "command-fieldname";
        span.appendChild(document.createTextNode(this.title));
        c.p.appendChild(span);
    }
}

Chat.prototype.Et.prototype.v = function(c) {
    c.v.push(";" + this.xy.join(","));
    c.auto = false;
}

Chat.prototype.Et.prototype.focus = function(c) {
    this.ec.style.backgroundColor = "#d5ffbb";
}

Chat.prototype.Et.prototype.unfocus = function(c) {
    this.ec.style.backgroundColor = "#fff";
}

Chat.prototype.Et.prototype.ct = function(c) {
    this.xy = [c.x, c.y];
    this.uv();
    setTimeout(function(cs) { return function() { cs.evke(); }; }(chat), 100);
    return true;
}

Chat.prototype.Et.prototype.uv = function() {
    removeChildren(this.ec);
    if (this.xy.length) {
        var el = createElement("span");
        el.appendChild(document.createTextNode("tile"));
        this.ec.appendChild(el);
    }
}

// object command element
Chat.prototype.Eo = Chat.prototype.E(function(ot, on, ol, title) {
    this.ot = ot;
    this.on = on;
    this.ol = ol;
    this.title = title;
});

Chat.prototype.Eo.prototype.c = function(clear) {
    return new this.constructor(clear ? 0 : this.ot, clear ? "" : this.on, this.ol, this.title);
}

Chat.prototype.Eo.prototype.i = function(c) {
    var ec = this.ec();
    ec.style.backgroundColor = "#fee";
    ec.style.borderLeft = "1px solid #ccc";
    ec.style.cursor = "default";
    registerEventHandler(ec, "click", function(es) { return function() { es.ot = 0; es.uv(); } }(this));
    c.ei.appendChild(ec);
    this.ec = ec;
    this.uv();
}

Chat.prototype.Eo.prototype.d = function(c) {
    if (this.title) {
        if (c.p.firstChild)
            c.p.appendChild(document.createTextNode(" "));
        var span = createElement("span");
        span.className = "command-fieldname";
        span.appendChild(document.createTextNode(this.title));
        c.p.appendChild(span);
    }
}

Chat.prototype.Eo.prototype.v = function(c) {
    c.v.push(":" + this.ot);
    c.auto = false;
}

Chat.prototype.Eo.prototype.focus = function(c) {
    this.ec.style.backgroundColor = "#ffe6bb";
}

Chat.prototype.Eo.prototype.unfocus = function(c) {
    this.ec.style.backgroundColor = "#fff";
}

Chat.prototype.Eo.prototype.co = function(c) {
    if (this.ol.indexOf(c.name.charAt(0)) < 0)
        return false;

    this.ot = c.t;
    this.on = c.name.slice(1);
    this.uv();
    setTimeout(function(cs) { return function() { cs.evke(); }; }(chat), 100);
    return true;
}

Chat.prototype.Eo.prototype.uv = function() {
    removeChildren(this.ec);
    if (this.ot) {
        var el = createElement("span");
        el.appendChild(document.createTextNode(this.on));
        this.ec.appendChild(el);
    }
}


// Line
Chat.prototype.L = function(n, v, k, t) {
    this.n = n; // name
    this.v = v; // type
    this.k = k; // kind
    this.t = t; // text

    var p = createElement("p");
    var c = [ , "action",,,, "thought", "item", "narrative" ][k];
    p.className = "line" + (c ? " " + c : "");
    if (n) {
        var s = createElement("span");
        c = [, "creature", "person", "observer"][v];
        s.className = "name" + (c ? " " + c : "");
        s.appendChild(document.createTextNode(n));
        s.appendChild(document.createTextNode(k == 1 ? " " : ": "));
        p.appendChild(s);
    }
    if (t) {
        if (k == 8) {
            var d = createElement("div");
            d.className = "read";
            if (n)
                d.appendChild(p);
            t = t.split('\n');
            if (!t[t.length-1])
                t.pop();
            while (t.length) {
                p = createElement("p");
                p.appendChild(document.createTextNode(t.shift()));
                d.appendChild(p);
            }
            p = d;
        } else 
            p.appendChild(document.createTextNode(t));
    }
    this.p = p;
}

// Chat
Chat.prototype.Ca = function() {
    this.m.a([].slice.call(arguments));
}

Chat.prototype.La = function(n, v, k, t) {
    var l = new this.L(n, v, k, t);
    this.ll.push(l);

    var down = this.et.scrollTop >= this.et.scrollHeight - this.et.clientHeight - 1;
    this.et.appendChild(l.p);
    if (down)
        this.et.scrollTop = this.et.scrollHeight;
}

Chat.prototype.Ld = function(n) {
    for (var i = this.ll.length - n; i > 0; i--) {
        var l = this.ll.shift();
        this.et.removeChild(l.p);
    }
}

Chat.prototype.evmi = function(ev, e) {
    delete this.emhide;
}

Chat.prototype.evmo = function(ev, e) {
    if (!this.m.moh)
        return;
    for (var related = ev.relatedTarget || ev.toElement; related && related != document; related = related.parentNode)
        if (related == e)
            return;
    this.emhide = (new Date).getTime();
    setTimeout(function(cs, hide) { return function() { if (cs.emhide == hide) cs.m.i(); }; }(this, this.emhide), 250);
}

Chat.prototype.cact = function(c) {
    if (this.m.el)
        this.m.i();
    else if (inv.im && inv.im.m.el) {
        inv.im.m.i();
        delete inv.im;
    }
    this.c = c;
    this.c.i(chat.eir);
    if (this.c.auto())
        setTimeout(function(cs) { return function() { cs.evke(); }; }(this), 100);
}

Chat.prototype.mact = function(moh) {
    if (this.m.el)
        this.m.i();
    else if (inv.im && inv.im.m.el) {
        inv.im.m.i();
        delete inv.im
    } else {
        this.m.i(this.em);
        this.m.moh = moh;
        client.click("!");
    }
}

Chat.prototype.evke = function() {
    var v = this.c.v();
    if (v.length) {
        v.push("");
        client.click(v);
        this.hh.push(this.c.c());
        this.hp = this.hh.length;
        this.c = this.c.remain ? this.c.c(true) : new this.C;
        this.c.i(this.eir);
    }
}

Chat.prototype.evk = function(ev) {
    var key = ev.keyCode || ev.which;

    if (this.evkfilter && this.evkfilter.key == key)
        return;
    this.evkfilter = { key: key };
    setTimeout(function(cs, f) { return function() { if (cs.evkfilter == f) delete cs.evkfilter; }; }(this, this.evkfilter), 1);

    if (key == 13) {
        if (this.m.el && this.m.hlp)
            this.m.hlk(0);
        else
            this.evke();
        stopEventPropagation(ev);
    } else if (key == 27) {
        if (this.c.plain && this.c.empty())
            this.mact();
        else {
            this.c = new this.C;
            this.c.i(this.eir);
        }
        stopEventPropagation(ev);
    } else if (key == 9 && !ev.ctrlKey && !ev.metaKey && !ev.altKey) {
        this.c.tab(ev.shiftKey ? -1 : 1);
        preventEventDefault(ev);
        stopEventPropagation(ev);
    } else if (!ev.shiftKey && !ev.ctrlKey && !ev.metaKey && !ev.altKey) {
        if (key == 38) {
            if (this.m.el)
                this.m.hlk(-1);
            else {
                if (--this.hp < 0)
                    this.hp = this.hh.length;
                var h = this.hh[this.hp];
                this.c = h ? h.c() : new this.C;
                this.c.i(this.eir);
            }
            stopEventPropagation(ev);
        } else if (key == 40) {
            if (this.m.el)
                this.m.hlk(1);
            else {
                if (++this.hp > this.hh.length)
                    this.hp = this.hh.length;
                var h = this.hh[this.hp];
                this.c = h ? h.c() : new this.C;
                this.c.i(this.eir);
            }
            stopEventPropagation(ev);
        }
    }
}

Chat.prototype.layout = function() {
    var input = emSize() * 2;

    this.et.style.left = makeSize(0);
    this.et.style.top = makeSize(0);
    this.et.style.width = makeSize(this.div.clientWidth - 16);
    this.et.style.height = makeSize(this.div.clientHeight - input - 1 - 16);

    this.eit.style.left = makeSize(0);
    this.eit.style.top = makeSize(this.div.clientHeight - input);
    this.eit.style.width = makeSize(this.div.clientWidth);
    this.eit.style.height = makeSize(input);

    this.em.style.left = makeSize(0);
    this.em.style.bottom = makeSize(input + 1);
    this.em.style.maxHeight = makeSize(this.div.clientHeight - input - 1 - 10);
}

Chat.prototype.lo = function() {
    var lo = {}; // lingering object tags
    if (this.c)
        this.c.lo(lo);
    for (var i = 0, n = this.hh.length; i < n; i++)
        this.hh[i].lo(lo);
    return lo;
}

Chat.prototype.reset = function() {
    this.ll = []; // lines
    this.hh = []; // command history
    this.hp = 0;  // history position
    this.c = new this.C; // current command
    this.m = new Menu; // command menu

    // base element
    removeChildren(this.div);

    // text element
    this.et = createElement("div");
    this.et.id = "text";
    this.et.style.position = "absolute";
    this.et.style.overflow = "auto";
    this.div.appendChild(this.et);

    // input element container
    this.eit = createElement("table");
    this.eit.id = "input";
    this.eit.cellPadding = 0;
    this.eit.cellSpacing = 0;
    this.eit.style.position = "absolute";

    var eib = createElement("tbody");
    this.eir = createElement("tr");
    eib.appendChild(this.eir);
    this.eit.appendChild(eib);
    this.div.appendChild(this.eit);

    // command list container
    var em = createElement("div");
    em.className = "menu";
    em.style.position = "absolute";
    em.style.overflow = "auto";
    em.style.backgroundColor = "";
    em.style.borderBottomStyle = em.style.borderLeftStyle = "none";
    em.style.display = "none";
    registerEventHandler(em, "mouseover", function(cs) { return function(ev) { cs.evmi(ev, em); }; }(this));
    registerEventHandler(em, "mouseout", function(cs) { return function(ev) { cs.evmo(ev, em); }; }(this));
    this.div.appendChild(em);
    this.em = em;

    this.c.i(this.eir);
    this.mfirst = true;

    if (this.bodyevk)
        this.bodyevk.unregister();
    this.bodyevk = registerEventHandler(document, "keydown", function(cs) { return function(ev) { cs.evk(ev); }; }(this));

    this.layout();
}

Chat.prototype.post = function() {
    this.m.e();
    if (!this.m.el && this.mfirst) {
        delete this.mfirst;
        this.mact();
    }
}


/* Inv class */

function Inv() {
}

Inv.prototype.reset = function() {
    this.pl = []; // list of panels
    this.il = []; // list of icons keyed by tag
    this.el = []; // list of panel divs
    delete this.im; // item owning menu

    // base element
    removeChildren(this.div);
    if (this.evdiv != this.div) {
        registerEventHandler(this.div, "mousedown", function(event) { event.preventDefault(); }); // default is to drag, which is not helpful
        this.evdiv = this.div;
    }
}

Inv.prototype.post = function() {
    if (this.cm) {
        this.cm.e();
        delete this.cm;
    }
    this.layout();
}

Inv.prototype.I = function(si, so) {
    this.si = si; // src
    this.so = so; // open
}

Inv.prototype.I.prototype.i = function(open) {
    var e = open ? this.eio : this.eii;
    if (e)
        return e.parentNode ? e.cloneNode(false) : e;
    else if (open && !this.so)
        return null;
    else {
        var i = createElement("img");
        i.setAttribute("src", open ? this.so : this.si);
        if (open)
            this.eio = i;
        else
            this.eii = i;
        return i;
    }
}

Inv.prototype.P = function(o, c) {
    this.o = o;
    this.c = c;
    if (!c) {
        this.pt = [];
        this.pl = [];
    }

    this.m = new Menu; // command menu
}

Inv.prototype.P.prototype.size = 42;

Inv.prototype.P.prototype.i = function(i) {
    var p = i[i.length - 1];
    if (typeof p == "object") {
        i.pop();
        if (!this.pl) {
            this.pt = [];
            this.pl = [];
        }
        this.p(p);
    } else {
        delete this.pt;
        delete this.pl;
    }
    if (i.length && (this.icon != i[0] || this.title != i[1])) {
        this.icon = i[0];
        this.title = i[1];
        this.ii();
    }
}

Inv.prototype.P.prototype.ii = function() {
    var ei = createElement("div");
    ei.className = this.ep ? "item-selected" : "item";
    ei.style.styleFloat = ei.style.cssFloat = "left";
    ei.style.height = ei.style.width = makeSize(this.size);

    if (this.title)
        ei.setAttribute("title", capitalize(this.title));
    if (this.icon) {
        var i = inv.il[this.icon];
        ei.appendChild(i.i());
        var o = i.i(true);
        if (o) {
            o = ei.appendChild(o);
            o.style.display = "none";
        }
    }
    if (this.ei)
        this.c.epd.replaceChild(ei, this.ei);
    else
        this.c.epd.appendChild(ei);

    registerEventHandler(ei, "click", function(p) { return function(ev) { p.ic(); } }(this));
    this.ei = ei;
}

Inv.prototype.P.prototype.ic = function() {
    var po = this.ep && true, mo = this.m.el && true;

    if (po || !mo) {
        if (this.m.el) {
            this.m.i();
            delete inv.im;
        } else {
            if (chat.m.el)
                chat.m.i();
            else if (inv.im && inv.im.m.el)
                inv.im.m.i();
            this.m.i(chat.em);
            inv.im = this;
            client.click('!' + this.addr());
        }
    }

    if (mo || !po) {
        for (var i = 0, n = this.c.pl.length; i < n; i++) {
            var p = this.c.pl[i];
            if (p.ei) {
                p.ei.className = "item";
                var o = p.ei.firstChild.nextSibling;
                if (o) {
                    o.style.display = "none";
                    o.previousSibling.style.display = "";
                }
            }
            p.pd();
        }
        inv.layout();
        if (this.ei) {
            this.ei.className = "item-selected";
            var o = this.ei.firstChild.nextSibling;
            if (o) {
                o.style.display = po ? "none" : "";
                o.previousSibling.style.display = po ? "" : "none";
            }
        }
        if (po)
            client.click('#' + this.c.addr());
        else {
            this.pi();
            client.click('#' + this.addr());
        }
    }
}

Inv.prototype.P.prototype.addr = function() {
    var c = [];
    for (var x = this; x.c; x = x.c)
        c.unshift(x.o);
    c.unshift(inv.pl.indexOf(x));
    return c.join(",");
}

Inv.prototype.P.prototype.pi = function() {
    if (!this.ep && this.pl) {
        this.ep = createElement("div");
        this.ep.className = "panel";
        this.ep.style.position = "absolute";
        this.ep.style.top = makeSize(0);
        this.pn(this.name, true);
        this.epd = createElement("div");
        this.epd.style.overflow = "auto";
        this.ep.appendChild(this.epd);
        for (var i = 0, n = this.pl.length; i < n; i++)
            this.pl[i].ii();
        for (var x = this; x.c; x = x.c);
        x = inv.pl[inv.pl.indexOf(x) + 1];
        inv.div.insertBefore(this.ep, x && x.ep || null);
    }
}

Inv.prototype.P.prototype.pn = function(name, make) {
    if (this.ep) {
        if (this.name && !make)
            this.ep.removeChild(this.ep.firstChild);
        if (name) {
            var h = createElement("p");
            h.appendChild(document.createTextNode(capitalize(name)));
            this.ep.insertBefore(h, this.ep.firstChild);
        }
    }
    this.name = name;
}

Inv.prototype.P.prototype.pd = function() {
    if (this.ep) {
        for (var i = 0, n = this.pl.length; i < n; i++) {
            var p = this.pl[i];
            if (p.ei) {
                this.epd.removeChild(p.ei);
                delete p.ei;
            }
            if (p.pl)
                p.pd();
        }
        inv.div.removeChild(this.ep);
        delete this.ep;
    }
}

Inv.prototype.P.prototype.p = function(p) {
    if (p[0])
        this.pn(p[0]);
    if (p.length)
        this.pi();
    else
        this.pd();
    for (var i = 1, n = p.length; i < n; i++) {
        var x = p[i];
        if (typeof x == "number") {
            var c = this.pt[x];
            delete this.pt[x];
            if (c.ei && this.ep)
                this.epd.removeChild(c.ei);
            c.pd();
            var j = this.pl.indexOf(c);
            if (j >= 0)
                this.pl.splice(j, 1);
        } else if (x[0] < 0) {
            var c = this.pt[-x[0]];
            if (c) {
                for (var j = 1; j < x.length; j++)
                    c.m.a(x[j]);
                c.m.e();
            }
        } else {
            var c = this.pt[x[0]];
            if (!c) {
                c = new this.constructor(x[0], this);
                this.pt[x[0]] = c;
                this.pl.push(c);
            }
            c.i(x.slice(1));
        }
    }
}

Inv.prototype.sizeS = function(w, h) {
    this.div.style.width = makeSize(w);
    this.div.style.height = makeSize(h);
    this.divw = w;
    this.divh = h;
    this.layout();
}

Inv.prototype.layout = function() {
    var n = this.div.childNodes.length;
    var em = emSize();
    var pad = em * 1.25;
    var l = this.P.prototype.size + 4 + em * .25;
    var m = Math.floor((this.divw - n * (pad + 1) + 1) / l);
    var w = Math.floor(Math.max(m / (n + 1), 2)) * l + pad + 1;
    var rw = Math.max(w, this.divw - w * (n - 1));

    var x = 0;
    for (var el = this.div.firstChild; el; el = el.nextSibling) {
        el.style.left = makeSize(x);
        el.style.width = makeSize(el.nextSibling ? w - 1 : rw);
        el.style.height = makeSize(this.divh);
        if (el.firstChild != el.lastChild)
            el.lastChild.style.height = makeSize(this.divh - el.firstChild.clientHeight - 1 - em * .5);
        el.style.borderRightStyle = el.nextSibling ? "solid" : "";
        x += w;
    }
}

Inv.prototype.Ia = function(i, si, so) {
    this.il[i] = new this.I(si, so);
    return i;
}

Inv.prototype.Pa = function(i) {
    this.pl.splice(i, 0, new this.P);
}

Inv.prototype.Pd = function(i) {
    this.pl[i].d();
}

Inv.prototype.Pv = function(i, p) {
    this.pl[i].p(p);
}


/* initialization */

chat = new Chat;
inv = new Inv;

