// ondoc -- Tomas Hlavaty 28feb2009

/// lisp

var Scl = 1;

function format(N, S) {
    var X = "" + N;
    var L = X.length;
    return X.slice(0, L - S) + "." + X.slice(L - S, L);
}

function real(N, S) {
    return N / Math.pow(10, S);
}

function Cons(Car, Cdr) {
    this.car = Car;
    this.cdr = Cdr;
    this._isCons = "b92e7eb4b4a84432696d4892e2c114b3";
}

function isCons(X) {
    return X && X._isCons == "b92e7eb4b4a84432696d4892e2c114b3";
}

function cons(A, D) {
    return new Cons(A, D);
}

function Sym(Nm, Val, Prop) {
    this._nm = Nm;
    this._val = Val;
    this._prop = Prop;
    this._isSym = "b32e74b4b5a844626967489282c194b0";
}

function isSym(X) {
    return X && X._isSym == "b32e74b4b5a844626967489282c194b0";
}

var Syms = {};

function mkSym(Nm, Val, Prop) {
    var X = new Sym(Nm, Val, Prop);
    Syms[Nm] = X;
    return X;
}

function xget(Sym) {
    return isSym(Sym) ? Sym._val : Sym;
}

function xset(Sym, Val) {
    if(!isSym(Sym)) throw "Sym expected";
    Sym._val = Val;
    return Sym._val;
}

var NIL = mkSym("NIL");
var T = mkSym("T");

xset(NIL, NIL);
xset(T, T);
// TODO set props for NIL and T

function intern(Sym) {
    //if(!(Sym in Syms)) Syms[Sym] = mkSym(Sym, NIL, NIL);
    return Syms[Sym] || (Syms[Sym] = mkSym(Sym, NIL, NIL));
}

function isNil(X) {
    return X === NIL;
}

function isT(X) {
    return X === T;
}

function car(L) {
    return isNil(L) ? NIL : L.car;
}

function cdr(L) {
    return isNil(L) ? NIL : L.cdr;
}

function caar(L) {
    return car(car(L));
}

function cadr(L) {
    return car(cdr(L));
}

function cdar(L) {
    return cdr(car(L));
}

function cddr(L) {
    return cdr(cdr(L));
}

function caddr(L) {
    return car(cdr(cdr(L)));
}

function cdddr(L) {
    return cdr(cdr(cdr(L)));
}

function cadddr(L) {
    return car(cdr(cdr(cdr(L))));
}

function cddddr(L) {
    return cdr(cdr(cdr(cdr(L))));
}

function caddddr(L) {
    return car(cdr(cdr(cdr(cdr(L)))));
}

function cadddddr(L) {
    return car(cdr(cdr(cdr(cdr(cdr(L))))));
}

function array2list(A) {
    var L = NIL;
    for(var I = A.length - 1; 0 <= I; I--) {
        L = cons(A[I], L);
    }
    return L;
}

function list2array(L) {
    var A = [];
    while(!isNil(L)) {
        A.push(L.car);
        L = L.cdr;
    }
    return A;
}

function object2list(A) {
    var L = NIL;
    for(var I in A) {
        L = cons(cons(I, A[I]), L);
    }
    return L;
}

function reverse(L) {
    var X = NIL;
    while(!isNil(L)) {
        X = cons(L.car, X);
        L = L.cdr;
    }
    return X;
}

function eq(X, Y) {
    if(X === Y) return T;
    //if(X == Y) return T;
    return NIL;
}

function xdelete(A, L) {
    var X = NIL;
    while(!isNil(L)) {
        if(isNil(eq(A, L.car))) X = cons(L.car, X);
        L = L.cdr;
    }
    return reverse(X);
}

function member(A, L) {
    while(!isNil(L)) {
        if(!isNil(eq(A, L.car))) return L;
        L = L.cdr;
    }
    return NIL;
}

function lsApply(Fn, Args) {
}

function jsFn(Fn) {
    var F = function() {
        return lsApply(Fn, list2array(arguments));
    };
    return F;
}

function jsApply() {
    var Fn = arguments.shift();
    var Args = arguments.shift();
    var Rest = arguments;
    return Fn.apply(list2array(Args).unshift.apply(list2array(Rest)));
}

var Xeval = {};

function xdef(Nm, Fn) {
    if(!(Nm in Syms)) intern(Nm);
    Xeval[Nm] = Fn;
}

xdef("quote", function(E) {
        return cdr(E); // or cdr?
    });
xdef("if", function(E) {
        if(!isNil(xeval(cadr(E)))) return xeval(caddr(E));
        else return xeval(cadddr(E));
    });
xdef("prog", function(E) {
        var L = cdr(E);
        var X = NIL;
        while(!isNil(L)) {
            X = xeval(L.car);
            L = L.cdr;
        }
        return X;
    });

function xeval(E) {
    if(isSym(E)) return xget(E);
    else if(!isCons(E)) return E;
    else if(car(E)._nm in Xeval) return Xeval[car(E)._nm](E);
    else return xapply(xeval(car(E)), map(xeval, cdr(E)));
}

function initLisp() {
    //xalert(xeval(NIL), xeval(T), xeval(12), xeval(12.3), xeval({x: "hi"}), xeval([1, 2]));
    //xalert(xeval(cons(intern("quote"), cons("whoa", NIL))));
    //xalert(xeval(cons(intern("quote"), cons("whoa", cons("b", NIL)))));
    //xalert(intern("if"));
    //xalert(object2list(Syms));
    //xalert(object2list(Xeval));
    //xalert(xeval(cons(intern("if"), cons(T, cons("yes", cons("no", NIL))))));
    //xalert(xeval(cons(intern("if"), cons(NIL, cons("yes", cons("no", NIL))))));
    //xalert(xeval(cons(intern("quote"), intern("whoa"))));
}

// parser

function isWhite(C) {
    return isNil(C) ? false : 0 <= " \t\n\r".indexOf(C);
}

function parse(S) { // TODO cons . notation
    var L = S.split("");
    function peek() {
        return 0 < L.length ? L[0] : NIL;
    }
    function xchar() {
        return 0 < L.length ? L.shift() : NIL;
    }
    function skip() {
        while(isWhite(peek())) xchar();
    }
    function many() {
        var X;
        while(!isNil(peek()) && peek() != ")") {
            var O = one();
            if(O || typeof O == "number") X = cons(O, X ? X : NIL);
        }
        if(X) X = reverse(X);
        return X;
    }
    function tok() {
        var Tok = [];
        var N = true;
        var F = false;
        var S;
        if(peek() == "+" || peek() == "-") S = xchar();
        while(!isNil(peek()) && peek() != ")" && !isWhite(peek())) {
            var C = xchar();
            if(N && C == ".") break;
            if(!(0 <= "0123456789".indexOf(C))) N = false;
            Tok.push(C);
        }
        if(N && C == ".") {
            var Tok2 = [];
            while(!isNil(peek()) && peek() != ")" && !isWhite(peek())) {
                var C = xchar();
                if(!(0 <= "0123456789".indexOf(C))) N = false;
                Tok2.push(C);
            }
            if(isNil(Scl || NIL)) {
                Tok2.unshift(".");
                Tok = Tok.concat(Tok2);
                if(N) F = true;
            } else {
                if(N) {
                    if(Tok.length == 0 && Tok2.length == 0) Tok = ["."];
                    else for(var I = 0; I < Scl; I++)
                             Tok.push(Tok2.shift() || "0");
                } else {
                    Tok2.unshift(".");
                    Tok = Tok.concat(Tok2);
                }
            }
        }
        if(0 < Tok.length) {
            var X = Tok.join("");
            if(X == ".") return X;
            if(N) {
                X = F ? parseFloat(X) : parseInt(X);
                if(S == "-") X = -X;
            } else X = intern(S ? S + X : X);
            return X;
        }
    }
    function str() {
        var Tok = [];
        while(peek() != '"') {
            var C = xchar();
            if(C == "\\") C = xchar();
            else if(C == "^") {
                C = xchar();
                if(C == "I") C = "\t";
                else if(C == "J") C = "\n";
                else if(C == "M") C = "\r";
                else C = String.fromCharCode(C == "?" ? 127 : C & 0x1f);
            }
            Tok.push(C);
        }
        if(xchar() != '"') throw "Unbalanced double quote";
        if(0 < Tok.length) return Tok.join(""); //.replace(/\r\n/g, "\n");
    }
    function one() {
        skip();
        var X;
        var C = peek();
        if(!isNil(C) && C != ")") {
            if(C == "(") {
                xchar();
                X = many() || NIL;
                if(xchar() != ")") throw "Unbalanced parenthesis";
            } else if(C == '"') {
                xchar();
                X = str();
            } else X = tok();
        }
        return X;
    }
    var X = many();
    if(0 < L.length) throw "Parsing not completed";
    return X ? X : NIL;
}

function unparse(X) {
    var A = [];
    if(isSym(X)) {
        A.push(X._nm);
    } else if(isCons(X)) {
        A.push("(");
        while(isCons(X)) {
            A.push(unparse(X.car));
            X = X.cdr;
            if(!isNil(X)) A.push(" ");
        }
        if(!isNil(X)) {
            A.push(". ");
            A.push(unparse(X));
        }
        A.push(")");
    } else {
        if(typeof X == "number") A.push(X);
        else {
            var Y = X.split("");
            for(var I = 0; I < Y.length; I++) {
                if(Y[I] == "\\") Y[I] = "\\\\";
                else if(Y[I] == "\"") Y[I] = "\\\"";
            }
            var S = Y.join("");
            //var S = X.replace(/\"/g, "\\\"").replace(/\\/g, "\\\\");
            A.push("\"" + S + "\"");
        }
    }
    return A;
}

function isArray(A) {
    return A && A.constructor == Array;
}

function flatten(A) {
    var X = [];
    function rec(B) {
        var N = B.length;
        var I;
        for(I = 0; I < N; I++) {
            if(isArray(B[I])) rec(B[I]);
            else X.push(B[I]);
        }
    }
    rec(A);
    return X;
}

function lisp2string(Any) {
    return flatten(unparse(Any)).join("");
}

function xalert() {
    var X = [];
    var N = arguments.length;
    var I;
    for(I = 0; I < N; I++) {
        X.push(flatten(unparse(arguments[I])).join(""));
    }
    alert(X.join(" "));
}

function xmsg(A) {
    var B = parse(A);
    var X = flatten(unparse(B)).join("");
    mk(w("test"), "pre", {}, {}, "'" + A + "' => " + X);
}

function initLisp() {
    xmsg("");
    xmsg("  ");
    xmsg("1");
    xmsg("1 \"string\" a 2.3");
    xmsg("NIL");
    xmsg("T");
    xmsg("()");
    xmsg("1 ()");
    xmsg("1 () (())");
    xmsg("1 ( ) (( )) ((( ))) (( )) ( ) 2");
    xmsg("(-213cons 1 NIL)");
    xmsg("(cons 1 NIL) 2 (-3 b c ) \"st\\\"r^Ii\\\"ng^M^J2\" () 4.5678 9");
    xmsg("(cons T T)");
    xmsg("(list)");
    xmsg("(list 1 -2 -3.4 5 6.7 9. .23 -0 -0.0)");
    xmsg("1 (NIL (T 2 3.5 ) if)");
    xmsg("1 (NIL (T 2 . 3.5) if)");
    xmsg("1 (NIL . (T 2 . 3.5 6) if)");
    xmsg("1 (NIL (T 2 (. 3.5 . 6)) if)");
    //xalert(xeval(NIL), xeval(T), xeval(12), xeval(12.3), xeval({x: "hi"}));
    //xalert(xeval(cons(intern("if"), NIL)));
    //xalert(cons(NIL, "if"));
    xmsg("(595 842) (20 12 0 0 0 NIL NIL) (NIL NIL NIL NIL NIL NIL NIL) (&quot;hallo second page^M^Jsecond line&quot; 35 24 NIL 65 154 205 50 NIL NIL) (&quot;hello^M^Jtomas&quot; 340 260 NIL NIL NIL NIL NIL NIL NIL)");
}

/// utils

// z-index fix http://mahzeh.org/?p=23
var Msie = 0 < window.navigator.userAgent.indexOf("MSIE"); // :-(

var Mx;
var My;
var Mb;

function max() {
    var X = arguments[0];
    var N = arguments.length;
    for(var I = 1; I < N; I++) {
        if(X < arguments[I]) X = arguments[I];
    }
    return X;
}

function min() {
    var X = arguments[0];
    var N = arguments.length;
    for(var I = 1; I < N; I++) {
        if(arguments[I] < X) X = arguments[I];
    }
    return X;
}

function lim(L, M, H) {
    return min(max(L, M), H);
}

function w(N) {
    return document.getElementById(N);
}

function wempty(W) {
    while(0 < W.childNodes.length) {
        W.removeChild(W.childNodes[0]);
    }
    return W;
}

function style2string(S) {
    var X = "";
    for(var K in S) {
        X += K + ":" + S[K] + ";";
    }
    return 0 < X.length ? X : undefined;
}

// http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
function windowSize() {
    var W = 0, H = 0;
    if(typeof(window.innerWidth) == 'number') { // !ie
        W = window.innerWidth;
        H = window.innerHeight;
    } else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) { // ie6+ std
        W = document.documentElement.clientWidth;
        H = document.documentElement.clientHeight;
    } else if(document.body && (document.body.clientWidth || document.body.clientHeight)) { // ie4
        W = document.body.clientWidth;
        H = document.body.clientHeight;
    }
    return [W, H];
}
function scrollXY() {
  var X = 0, Y = 0;
  if(typeof(window.pageYOffset) == 'number') { // Netscape
      Y = window.pageYOffset;
      X = window.pageXOffset;
  } else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) { // dom
      Y = document.body.scrollTop;
      X = document.body.scrollLeft;
  } else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) { // ie6 std
      Y = document.documentElement.scrollTop;
      X = document.documentElement.scrollLeft;
  }
  return [X, Y];
}

// http://www.netlobo.com/javascript_getelementsbyclassname.html
// if(!document.getElementsByClassName) {
//     //alert(":-(");
//     document.getElementsByClassName = function(N){
//         var X = new Array();
//         var L = document.getElementsByTagName("*");
//         for(var I = 0; I < L.length; I++) {
//             if(0 <= L[I].className.indexOf(" ")) {
//                 var C = L[I].className.split(" ");
//                 for(var J = 0; J < C.length; J++)
//                     if(C[J] == N) X.push(L[I]);
//             }
//             else if(L[I].className == N) X.push(L[I]);
//         }
//         return X;
//     }
// }

function mb1() {
    return Mb && ((Mb & 1) == 1);
}

function body() { // TODO BUG returns html element in IE; fixed? TODO test
    return document.body || document.documentElement.childNodes[1];
}

// // http://www.webdeveloper.com/forum/archive/index.php/t-151280.html
// function getMouse(E) {
//     var X = 0;
//     var Y = 0;
//     if(E.pageX) { // Moz
//         X = E.pageX + window.pageXOffset;
//         Y = E.pageY + window.pageYOffset;
//     } else if(E.clientX){ // IE
//         if(document.documentElement) { //IE 6+ strict mode
//             X = E.clientX + document.documentElement.scrollLeft;
//             Y = E.clientY + document.documentElement.scrollTop;
//         } else if(document.body){ //Other IE
//             X = E.clientX + document.body.scrollLeft;
//             Y = E.clientY + document.body.scrollTop;
//         }
//     } else return false; // old browsers
//     return [X, Y];
// }

function cb(Cb) {
    return function(E) {
        if(!E) E = window.event;
        //var M = getMouse(E);
        //Mx = E.pageX || E.clientX + body().scrollLeft;
        //My = E.pageY || E.clientY + body().scrollTop;
        //Mx = M[0];
        //My = M[1];
        Mx = E.clientX + body().scrollLeft;
        My = E.clientY + body().scrollTop;
        Mb = (E.which ? E.which == 1 : (E.button & 1) == 1) ? 1 : undefined;
        var Z = Cb(E);
        if(Z) {
            if(isArray(Z)) {
                if(Z[1]) {
                    E.cancelBubble = true;
                    if(E.stopPropagation) E.stopPropagation();
                }
                Z = Z[0];
            } else {
                E.cancelBubble = true;
                if(E.stopPropagation) E.stopPropagation();
            }
        }
        return Z;
    };
}

var draggableOn = false; // chrome drops events:-{
function draggable(W, H, A, C) {
    var P = document;
    H.onmousedown = cb(
        function(E) {
            var Self = this;
            if(!draggableOn) {
                var X0 = Mx;
                var Y0 = My;
                var X = Mx;
                var Y = My;
                if(mb1() && (C ? C(W, X, Y) : true)) {
                    draggableOn = Self;
                    var M = P.onmousemove;
                    var U = P.onmouseup;
                    var B = body();
                    var Bc = B.style.cursor;
                    var Wc = W.style.cursor;
                    var Hc = H.style.cursor;
                    B.style.cursor = "move";
                    W.style.cursor = "move";
                    H.style.cursor = "move";
                    function cleanup() {
                        draggableOn = false;
                        P.onmousemove = M;
                        P.onmouseup = U;
                        B.style.cursor = Bc;
                        W.style.cursor = Wc;
                        H.style.cursor = Hc;
                    }
                    P.onmousemove = cb(
                        function(E) {
                            if(mb1() && draggableOn == Self) {
                                if(A(W, Mx - X, My - Y)) { // moved
                                    X = Mx;
                                    Y = My;
                                }
                            } else cleanup(); // broken chrome:-{
                            return true;
                        });
                    P.onmouseup = cb(
                        function(E) {
                            if(mb1() && draggableOn == Self)
                                A(W, Mx - X, My - Y, true, Mx != X0 || My != Y0);
                            cleanup();
                            return true;
                        });
                }
            }
            return true;
        });
    return W;
}

function eTarget(E) {
    var X;
	if (!E) E = window.event;
	if (E.target) X = E.target;
	else if (E.srcElement) X = E.srcElement;
	if (X.nodeType == 3) X = X.parentNode; // defeat Safari bug
    return X;
}

function isString() {
    if(typeof arguments[0] == "string") return true;
    if(typeof arguments[0] == "object")
        return arguments[0].constructor.toString().match(/string/i);
    return false;
}

function mk(P, N, V, S, T) {
    var W;
    if(N == "text") {
        W = document.createTextNode(V);
    } else {
        W = document.createElement(N);
        for(var K in V) if(V[K]) W[K] = V[K];
        if(S) W.style.cssText = isString(S) ? S : style2string(S);
        if(T) mk(W, "text", T);
    }
    P.appendChild(W);
    return W;
}

// dataflow

function Var(V, D, T, P, R, Df) {
    this.V = V; // val
    this.D = D || NIL; // default
    this.T = T || NIL; // type
    this.P = P || NIL; // pretty
    this.R = R || NIL; // parse
    this.Df = Df || NIL; // default input fn
    this.H = NIL;
    this.Hown = NIL;
    if(!isNil(this.D)) {
        var X = this;
        this.D.hook(function(V) {X.notify();});
    }
    return this;
}
Var.prototype.get = function() {
    return isNil(this.V) ? this.getDefault() : this.V;
};
Var.prototype.getOwn = function() {
    return this.V;
};
Var.prototype.getDefault = function() {
    var X = NIL;
    if(!isNil(this.D)) {
        X = this.D.get();
        if(!isNil(this.Df)) X = this.Df(X);
    }
    return X;
}
Var.prototype.set = function(V, Q, U) {
    this.V = V;
    if(!Q || !isNil(Q)) this.notify(U);
    if(!Q || !isNil(Q)) this.notifyOwn(U);
    return this.V;
};
Var.prototype.tag = function() {
    return this.T;
};
Var.prototype.pretty = function() {
    return this.P;
};
Var.prototype.parse = function() {
    return this.R;
};
Var.prototype.hook = function(H) {
    this.H = cons(H, this.H);
    return H;
};
Var.prototype.notify = function(U) {
    var L = this.H;
    while(!isNil(L)) {
        L.car(this, U);
        L = L.cdr;
    }
};
Var.prototype.hookOwn = function(H) {
    this.Hown = cons(H, this.Hown);
    return H;
};
Var.prototype.notifyOwn = function(U) {
    var L = this.Hown;
    while(!isNil(L)) {
        L.car(this, U);
        L = L.cdr;
    }
};

function Mux(V, P, N, T, Pp, Pf, Nf) {
    this.V = V; // val
    this.P = P || NIL; // positive input
    this.N = N || NIL; // negative input
    this.T = T || NIL; // type
    this.Pp = Pp || NIL; // pretty
    this.Pf = Pf || NIL; // positive input fn
    this.Nf = Nf || NIL; // negative input fn
    this.H = NIL;
    this.Hown = NIL;
    var X = this;
    if(!isNil(this.P)) this.P.hook(function(V) {X.notify();});
    if(!isNil(this.N)) this.N.hook(function(V) {X.notify();});
    return this;
}
Mux.prototype.get = function() {
    var X = NIL;
    if(isNil(this.V)) {
        if(!isNil(this.N)) X = this.N.get();
        if(!isNil(this.Nf)) X = this.Nf(X);
    } else {
        if(!isNil(this.P)) X = this.P.get();
        if(!isNil(this.Pf)) X = this.Pf(X);
    }
    return X;
};
Mux.prototype.getOwn = function() {
    return this.V;
};
Mux.prototype.set = function(V, Q, U) {
    this.V = V;
    if(!Q || !isNil(Q)) this.notify(U);
    if(!Q || !isNil(Q)) this.notifyOwn(U);
    return this.V;
};
Mux.prototype.tag = function() {
    return this.T;
};
Mux.prototype.pretty = function() {
    return this.Pp;
};
Mux.prototype.hook = function(H) {
    this.H = cons(H, this.H);
    return H;
};
Mux.prototype.notify = function(U) {
    var L = this.H;
    while(!isNil(L)) {
        L.car(this, U);
        L = L.cdr;
    }
};
Mux.prototype.hookOwn = function(H) {
    this.Hown = cons(H, this.Hown);
    return H;
};
Mux.prototype.notifyOwn = function(U) {
    var L = this.Hown;
    while(!isNil(L)) {
        L.car(this, U);
        L = L.cdr;
    }
};

//var createCSSClass = function(selector, style)
//var setStyle = function(S, K, V)
// var setStyle = function(selector, style)
// {
//     //    var selector = S;
//     //    var style = K + ":" + V;
// 	// using information found at: http://www.quirksmode.org/dom/w3c_css.html
// 	// doesn't work in Opera due to lack of styleSheets support
// 	if(!document.styleSheets) return;
// 	if(document.getElementsByTagName("head").length == 0) return;
// 	var styleSheet;
// 	var mediaType;
//     var media;
// 	if(document.styleSheets.length > 0)
// 	{
// 		for(var i = 0; i<document.styleSheets.length; i++)
// 		{
// 			if(document.styleSheets[i].disabled) continue;
// 			media = document.styleSheets[i].media;
// 			mediaType = typeof media;
// 			// IE
// 			if(mediaType == "string")
// 			{
// 				if(media == "" || media.indexOf("screen") != -1)
// 				{
// 					styleSheet = document.styleSheets[i];
// 				}
// 			}
// 			else if(mediaType == "object")
// 			{
// 				if(media.mediaText == "" || media.mediaText.indexOf("screen") != -1)
// 				{
// 					styleSheet = document.styleSheets[i];
// 				}
// 			}
// 			// stylesheet found, so break out of loop
// 			if(typeof styleSheet != "undefined") break;
// 		}
// 	}
// 	// if no style sheet is found
// 	if(typeof styleSheet == "undefined")
// 	{
// 		// create a new style sheet
// 		var styleSheetElement = document.createElement("style");
// 		styleSheetElement.type = "text/css";
// 		// add to <head>
// 		document.getElementsByTagName("head")[0].appendChild(styleSheetElement);
// 		// select it
// 		for(i = 0; i<document.styleSheets.length; i++)
// 		{
// 			if(document.styleSheets[i].disabled) continue;
// 			styleSheet = document.styleSheets[i];
// 		}
//         if(!styleSheet) return; // thl opera "fix" :-(
// 		// get media type
// 		media = styleSheet.media;
// 		mediaType = typeof media;
// 	}
// 	// IE
// 	if(mediaType == "string")
// 	{
// 		for(i = 0;i<styleSheet.rules.length;i++)
// 		{
// 			// if there is an existing rule set up, replace it
// 			if(styleSheet.rules[i].selectorText.toLowerCase() == selector.toLowerCase())
// 			{
// 				styleSheet.rules[i].style.cssText = style;
// 				return;
// 			}
// 		}
// 		// or add a new rule
// 		styleSheet.addRule(selector,style);
// 	}
// 	else if(mediaType == "object")
// 	{
// 	for(i = 0;i<styleSheet.cssRules.length;i++)
// 	{
// 		// if there is an existing rule set up, replace it
// 		if(styleSheet.cssRules[i].selectorText.toLowerCase() == selector.toLowerCase())
// 		{
// 			styleSheet.cssRules[i].style.cssText = style;
// 			return;
// 		}
// 	}
// 	// or insert new rule
// 	styleSheet.insertRule(selector + "{" + style + "}", styleSheet.cssRules.length);
// 	}
// };

/// app

var Fonts = {
    NIL: "default",
    10: "monospace",
    11: "monospace bold",
    12: "monospace italic",
    13: "monospace bold italic",
    20: "sans-serif",
    21: "sans-serif bold",
    22: "sans-serif italic",
    23: "sans-serif bold italic",
    30: "serif",
    31: "serif bold",
    32: "serif italic",
    33: "serif bold italic"
};

function prettyFont(V) {
    return Fonts[V];
}

function prettyNil(V) {
    return isNil(V) ? "" : V;
}

function prettyInt(V) {
    return isNil(V) ? "" : "" + V;
}

function prettyReal(V) {
    return isNil(V) ? "" : format(V, Scl);
}

function parseIntX() {
    var X = parseInt.apply(this, arguments);
    return isNaN(X) ? NIL : X;
}

function parseFix(V) {
    var X = parseFloat(V);
    return isNaN(X) ? NIL : Math.round(X * Math.pow(10, Scl));
}

function int2realX(V) {
    return isNil(V) ? NIL : Math.round(V * Math.pow(10, Scl));
}

function px2pt(V) {
    return isNil(V) ? NIL : V * Wpt / Wpx;
}

function pt2px(V) {
    return isNil(V) ? NIL : V * Wpx / Wpt;
}

function Xpt2px(V) {
    return isNil(V) ? NIL : (V - Xpt) * Wpx / Wpt;
}

function Ypt2px(V) {
    return isNil(V) ? NIL : (Hpt - (V - Ypt)) * Wpx / Wpt;
}

function Xpx2pt(V) {
    return isNil(V) ? NIL : V * Wpt / Wpx + Xpt;
}

function Ypx2pt(V) {
    return isNil(V) ? NIL : (Hpx - V) * Wpt / Wpx + Ypt;
}

var Ed = new Var(NIL);
var Sel = new Var(NIL);
var Mode = new Var(NIL);

var EdX; // w("o") rel
var EdY;

var Xpt; // from server
var Ypt;
var Wpt;
var Hpt;
var Wpx;
var Hpx;

// from server
var Ddf = new Var(NIL);
var Dds = new Var(NIL, NIL, NIL, prettyInt, parseIntX);
var Ddc = new Var(NIL, NIL, "color");

// computed
var Df = new Var(NIL, Ddf, NIL, prettyFont);
var Ds = new Var(NIL, Dds, NIL, prettyInt, parseIntX);
var Dc = new Var(NIL, Ddc, "color");
var Dv = new Var(NIL, Ds, NIL, prettyReal, parseFix, int2realX);
var Dhx = new Var(NIL, NIL, NIL, prettyReal, parseFix);
var Dh = new Mux(NIL, Ds, Dhx, NIL, prettyReal, int2realX, NIL);

var It = new Var(NIL);
var If = new Var(NIL, Df, NIL, prettyFont);
var Is = new Var(NIL, Ds, NIL, prettyInt, parseIntX);
var Ic = new Var(NIL, Dc, "color");
var Ivm = new Mux(NIL, Is, Dv, NIL, prettyReal, int2realX, NIL);
var Iv = new Var(NIL, Ivm, NIL, prettyReal, parseFix);
var Ihx = new Var(NIL, NIL, NIL, prettyReal, parseFix);
var Ihm = new Mux(NIL, Is, Dh, NIL, prettyReal, int2realX, NIL);
var Ih = new Mux(NIL, Ihx, Ihm, NIL, prettyReal);

function show(W, V, D) {
    if(V && !isNil(V)) {
        W.style.visibility = "visible";
        W.style.display = "block";
    } else {
        W.style.visibility = "hidden";
        if(!D || isNil(D)) W.style.display = "none";
    }
}

function hideDialog(W) {
    show(W, NIL);
}

function px(V) {
    return V + "px";
}

function showDialog(D) {
    var A = windowSize();
    var B = scrollXY();
    var W = A[0];
    var H = A[1];
    var X = B[0];
    var Y = B[1];
    if(D.style.visibility != "visible") {
        show(D, T);
        D.style.left = px(X + (W - D.clientWidth) / 2);
        D.style.top = px(Y + (H - D.clientHeight) / 2);
    } else {
        var Sx1 = X;
        var Sx2 = X + W;
        var Sy1 = Y;
        var Sy2 = Y + H;
        var Dx1 = D.offsetLeft;
        var Dx2 = D.offsetLeft + D.offsetWidth;
        var Dy1 = D.offsetTop;
        var Dy2 = D.offsetTop + D.offsetHeight;
        if(Dx2 < Sx1 || Sx2 < Dx1 || Dy2 < Sy1 || Sy2 < Dy1) { // off screen
            D.style.left = px(X + (W - D.clientWidth) / 2);
            D.style.top = px(Y + (H - D.clientHeight) / 2);
        }
    }
}
// function showDialog(W) {
//     var B = body();
//     if(W.style.visibility != "visible") {
//         W.style.visibility = "visible";
//         W.style.display = "block";
//         W.style.left = px(B.scrollLeft + (B.clientWidth - W.clientWidth) / 2);
//         W.style.top = px(B.scrollTop + (B.clientHeight - W.clientHeight) / 2);
//     } else {
//         var Sx1 = B.scrollLeft + B.clientLeft;
//         var Sx2 = B.scrollLeft + B.clientWidth;
//         var Sy1 = B.scrollTop + B.clientTop;
//         var Sy2 = B.scrollTop + B.clientHeight;
//         var Wx1 = W.offsetLeft;
//         var Wx2 = W.offsetLeft + W.offsetWidth;
//         var Wy1 = W.offsetTop;
//         var Wy2 = W.offsetTop + W.offsetHeight;
//         if(Wx2 < Sx1 || Sx2 < Wx1 || Wy2 < Sy1 || Sy2 < Wy1) { // off screen
//             W.style.left = px(B.scrollLeft + (B.clientWidth - W.clientWidth) / 2);
//             W.style.top = px(B.scrollTop + (B.clientHeight - W.clientHeight) / 2);
//         }
//     }
// }

function toggleDialog(W) {
    if(W.style.visibility == "hidden") showDialog(W);
    else hideDialog(W);
}

function font2family(V) {
    var X = {1: "monospace", 2: "sans-serif", 3: "serif"};
    return X[Math.floor(V / 10)];
}

function font2weight(V) {
    return ((V % 10) & 1) == 1 ? "bold" : "normal";
}

function font2style(V) {
    return ((V % 10) & 2) == 2 ? "italic" : "normal";
}

// annot

function parseColor(V) {
    if(isNil(V)) return [NIL, NIL, NIL];
    var C = V.split("");
    if(C[0] == "#") { // #RRGGBB
        var R = parseIntX("" + C[1] + C[2], 16);
        var G = parseIntX("" + C[3] + C[4], 16);
        var B = parseIntX("" + C[5] + C[6], 16);
        return [R, G, B];
    } else { // rgb(R,G,B)
        var M = /(\d+)\D+(\d+)\D+(\d+)/(V);
        var R = parseIntX(M[1]);
        var G = parseIntX(M[2]);
        var B = parseIntX(M[3]);
        return [R, G, B];
    }
    return NIL;
}

function pad(S, N, C) {
    S = S.split("");
    var L = S.length;
    for(var I = L; I < N; I++)
        S.unshift(C);
    return S.join("");
}

function rgb(R, G, B) {
    R = pad(lim(0, R, 255).toString(16).toUpperCase(), 2, "0");
    G = pad(lim(0, G, 255).toString(16).toUpperCase(), 2, "0");
    B = pad(lim(0, B, 255).toString(16).toUpperCase(), 2, "0");
    return "#" + R + G + B;
}

function showEdDialog(A, X, Y) {
    Ed.set(A);
    var O = w("o");
    var P = O.parentNode.parentNode;
    EdX = X - P.offsetLeft + P.scrollLeft;
    EdY = Y - P.offsetTop + P.scrollTop;
    //xalert(Mx, My, EdX, EdY);
    if(isNil(A)) { // create
        It.set(NIL);
        If.set(NIL);
        Is.set(NIL);
        Ic.set(NIL);
        Ivm.set(NIL);
        Iv.set(NIL);
        Ihx.set(NIL);
        Ihm.set(NIL);
        Ih.set(NIL);
    } else { // edit
        It.set(A.T.getOwn());
        If.set(A.F.getOwn());
        Is.set(A.S.getOwn());
        Ic.set(A.C.getOwn());
        Ivm.set(A.Vm.getOwn());
        Iv.set(A.V.getOwn());
        Ihx.set(A.Hx.getOwn());
        Ihm.set(A.Hm.getOwn());
        Ih.set(A.H.getOwn());
    }
    showDialog(w("e"));
}

var allowOnclick = true; // to inhibit onclick after dragging

function dragAnnot(W, Dx, Dy, F, M) {
    if(Mode.get() == "Text") {
        W.style.left = px(W.offsetLeft + Dx);
        W.style.top = px(W.offsetTop + Dy);
        allowOnclick = F && !M;
    } else {
        var L = Sel.get();
        if(isNil(L)) {
            W.style.left = px(W.offsetLeft + Dx);
            W.style.top = px(W.offsetTop + Dy);
        } else {
            while(!isNil(L)) {
                var A = L.car;
                A.style.left = px(A.offsetLeft + Dx);
                A.style.top = px(A.offsetTop + Dy);
                L = L.cdr;
            }
        }
    }
    return true;
}

function dragAnnotWhen(W, X, Y) {
    if(Mode.get() == "Text") return true;
    var L = Sel.get();
    return Mode.get() == "Move" && (isNil(L) || !isNil(member(W, L)));
}

// http://bytes.com/groups/javascript/158427-read-image-style-top
// no IE though:-( http://erik.eae.net/archives/2007/07/27/18.54.15/
var PIXEL = /^\d+(px)?$/i;
function getComputedHeight(W) {
    var V = W.currentStyle.height;
    if (PIXEL.test(V)) return parseInt(V);
    var S = W.style.height;
    var R = W.runtimeStyle.height;
    W.runtimeStyle.height = W.currentStyle.height;
    W.style.height = V || 0;
    V = W.style.pixelHeight;
    W.style.height = S;
    W.runtimeStyle.height = R;
    return V;
}
function getComputedTop(W) {
    var V = W.currentStyle.top;
    if (PIXEL.test(V)) return parseInt(V);
    var S = W.style.top;
    var R = W.runtimeStyle.top;
    W.runtimeStyle.top = W.currentStyle.top;
    W.style.top = V || 0;
    V = W.style.pixelTop;
    W.style.top = S;
    W.runtimeStyle.top = R;
    return V;
}
function computedStyle(W, P) {
    if(window.getComputedStyle) // Moz
        return window.getComputedStyle(W, "").getPropertyValue(P);
    else if(W.currentStyle) // IE5+
        switch(P) {
        case "height": return getComputedHeight(W);
        case "top": return getComputedTop(W);
        default: return W.currentStyle[P];
        }
    else if (W.style) // IE4
        return W.style[P];
}

function Annot() {
    this.D = mk(w("o"), "div", {}, {
            position: "absolute",
            left: px(EdX),
            top: px(EdY),
            //overflow: "auto",
            "z-index": 20
            //border: "1px solid yellow",
        });
    this.D.annot = this;

    this.T = new Var(NIL);
    this.F = new Var(NIL, Df);
    this.S = new Var(NIL, Ds);
    this.C = new Var(NIL, Dc);
    //this.V = new Var(NIL, Dv);
    this.Vm = new Mux(NIL, this.S, Dv, NIL, prettyReal, int2realX, NIL);
    this.V = new Var(NIL, this.Vm, NIL, prettyReal, parseFix);
    //this.H = new Var(NIL, Dh);
    this.Hx = new Var(NIL, NIL, NIL, prettyReal, parseFix);
    this.Hm = new Mux(NIL, this.S, Dh, NIL, prettyReal, int2realX, NIL);
    this.H = new Mux(NIL, this.Hx, this.Hm, NIL, prettyReal);

    var A = this;
    var update = function() {
        wempty(A.D);
        var AH = mk(A.D, "div", {}, {
                position: "absolute",
                left: "-5px",
                top: "-5px",
                width: "5px",
                height: "5px",
                background: isNil(member(A.D, Sel.get())) ? "yellow" : "red"
            });
        var X = A.T.get();
        if(isNil(X)) return;
        var V = A.V.get();
        var H = A.H.get();
        var VV = pt2px(real(V, Scl));
        var HH = pt2px(real(H, Scl));
        var L = X.split("\n");
        var D = A.D;
        for(var I = 0; I < L.length; I++) {
            var LI = L[I].slice().replace(/ /g, "\u00A0");
            var NI = I * VV;
            var DI = mk(D, "div", {}, {position: "absolute", top: px(NI)});
            var Up = 0;
            if(isNil(H)) {
                //var R = mk(DI, "div", {}, {"border": "solid 1px yellow"}, LI);
                var R = mk(DI, "div", {}, {}, LI);
                Up = parseInt(computedStyle(R, "height"));
            } else {
                var R = DI; //mk(DI, "div");
                var LL = LI.split(""); // TODO filter out ^M + in .l too
                for(var J = 0; J < LL.length; J++) {
                    var NJ = J * HH;
                    var RR = mk(R, "div", {},
                                {position: "absolute",
                                 //"border": "solid 1px pink",
                                 left: px(NJ),
                                 width: px(HH),
                                 "text-align": "center"},
                                LL[J]);
                    if(J == 0) {
                        var Up2 = parseInt(computedStyle(RR, "height"));
                        if(Up < Up2) Up = Up2;
                    }
                }
            }
            var Top = parseInt(computedStyle(DI, "top"));
            DI.style.top = px(Top - Up);
            //alert(px2pt(Up) + " " + L[I]);
        }
    };
    this.T.hook(update);
    this.F.hook(function(V) {
            var X = V.get();
            A.D.style.fontFamily = font2family(X);
            A.D.style.fontWeight = font2weight(X);
            A.D.style.fontStyle = font2style(X);
        });
    this.S.hook(function(V) {A.D.style.fontSize = px(pt2px(V.get()));});
    this.C.hook(function(V) {
            var C = parseColor(V.get());
            A.D.style.color = rgb(C[0], C[1], C[2]);
        });
    this.V.hook(update);
    this.H.hook(update);

    Sel.hook(function(V) {
            if(isNil(member(A.D, V.get())))
                A.D.childNodes[0].style.background = "yellow";
            else
                A.D.childNodes[0].style.background = "red";
        });
    this.D.onclick = cb(function() {
            if(!allowOnclick) {
                allowOnclick = true;
                return true; // no bubble
            }
            switch(Mode.get()) {
            case "Text": showEdDialog(A, Mx, My); break;
            case "Select":
                var L = Sel.get();
                if(isNil(member(A.D, L))) {
                    Sel.set(cons(A.D, xdelete(A.D, L)));
                } else {
                    Sel.set(xdelete(A.D, L));
                }
                break;
            case "Move": break;
            case "Delete":
                var L = Sel.get();
                if(isNil(L)) {
                    A.remove();
                } else {
                    if(!isNil(member(A.D, L))) {
                        while(!isNil(L)) {
                            L.car.annot.remove();
                            L = L.cdr;
                        }
                        Sel.set(NIL);
                    }
                }
                break;
            }
            return true; // no bubble
        });

    draggable(this.D, this.D, dragAnnot, dragAnnotWhen);

    this.F.notify();
    this.S.notify();
    this.C.notify();
    this.T.notify();

    return this.D;
}
Annot.prototype.remove = function() {
    this.D.parentNode.removeChild(this.D);
};
Annot.prototype.sexp = function() {
    var C = parseColor(this.C.getOwn());
    return array2list([this.T.getOwn(),
                       Math.round(Xpx2pt(this.D.offsetLeft)),
                       Math.round(Ypx2pt(this.D.offsetTop)),
                       this.F.getOwn(),
                       this.S.getOwn(),
                       C[0],
                       C[1],
                       C[2],
                       isNil(this.V.getOwn()) ? this.Vm.getOwn() : this.V.getOwn(),
                       this.H.getOwn(),
                       isNil(this.H.getOwn()) ? this.Hm.getOwn() : this.Hx.getOwn()
                       ]);
};

function Box(X, Y, W, H) {
    var O = w("o");
    var P = O.parentNode.parentNode;
    this.X = X;
    this.Y = Y;
    this.W = W;
    this.H = H;
    this.D = mk(O, "div", {}, {
            position: "absolute",
            left: px(Xpt2px(this.X)),
            top: px(Ypt2px(this.Y + this.H)),

            width: px(pt2px(this.W)),
            height: px(pt2px(this.H)),
            "background-color": "yellow", // TODO as option?
            opacity: 0.4, filter: "alpha(opacity=40)", // TODO as option?

            "z-index": 20
        });
    this.D.annot = this;
    var A = this;
    this.D.onclick = cb(function() {
            if("Delete" == Mode.get()) {A.remove();}
            return true; // no bubble
        });
    return this.D;
}
Box.prototype.remove = function() {this.D.parentNode.removeChild(this.D);};
Box.prototype.sexp = function() {
    //var C = parseColor(this.C.getOwn());
    return array2list([1,
                       Math.round(this.X),
                       Math.round(this.Y),
                       Math.round(this.W),
                       Math.round(this.H)
                       // C[0],
                       // C[1],
                       // C[2]
                       ]);
};

function Link(X, Y, W, H, U) {
    var O = w("o");
    var P = O.parentNode.parentNode;
    this.X = X;
    this.Y = Y;
    this.W = W;
    this.H = H;
    this.U = U;
    this.D = mk(O, "a", {href: this.U}, {
            position: "absolute",
            left: px(Xpt2px(this.X)),
            top: px(Ypt2px(this.Y + this.H)),

            width: px(pt2px(this.W)),
            height: px(pt2px(this.H)),
            "background-color": "blue",
            opacity: 0.3, filter: "alpha(opacity=30)",

            "z-index": 20
        });
    this.D.annot = this;
    var A = this;
    this.D.onclick = cb(function() {
            switch(Mode.get()) {
                case "Delete":
                    A.remove();
                    return [false, true]; // ignore href, no bubble
                    break;
                case "Link":
                    var U = prompt("Please enter the URL of the link:", A.U);
                    if(U && (0 < U.length)) {A.U = U; A.D.href = A.U;}
                    return [false, true]; // ignore href, no bubble
                    break;
                default: window.location = A.U;
            }
            return true; // no bubble
        });
    return this.D;
}
Link.prototype.remove = function() {this.D.parentNode.removeChild(this.D);};
Link.prototype.sexp = function() {
    return array2list([2,
                       Math.round(this.X),
                       Math.round(this.Y),
                       Math.round(this.W),
                       Math.round(this.H),
                       this.U
                      ]);
};

// rest

function pgNo() {
    var S = window.location.pathname.split("/");
    S.pop();
    return parseInt(S.pop());
}

function annots() {
    var X = w("i");
    var W = w("o");
    var P = [];
    var L = [];
    var N = W.childNodes.length;
    for(var I = 0; I < N; I++) {
        var D = W.childNodes[I];
        if(D.nodeType == 1 && (D.nodeName == "div" || D.nodeName == "a") && D.annot)
            L.push(D.annot.sexp());
    }
    L.unshift(pgNo());
    P.push(array2list(L));

    var C = parseColor(Dc.getOwn());
    P.unshift(array2list([Df.getOwn(),
                          Ds.getOwn(),
                          C[0],
                          C[1],
                          C[2],
                          Dv.getOwn(),
                          isNil(Dh.getOwn()) ? Dhx.getOwn() : T
                          ]));
    return array2list(P);
}

function onDownload(E) {
    var P = annots();
    var F = eTarget(E).parentNode;
    F.childNodes[1].value = lisp2string(P);
    //xalert(lisp2string(P));
    window.onbeforeunload = cb(function(E) {});
    F.submit();
}

function cbSelect(Cb) {
    return function(E) {
        var W = eTarget(E);
        var X = W.options[W.selectedIndex].value;
        Cb(X == "NIL" ? NIL : X);
    };
}

function selectOption(W, X) {
    var L = W.options;
    var I;
    for(I = 0; I < L.length; I++) {
        var O = L[I].value;
        if((isNil(X) && O == "NIL") || (X == O)) break;
    }
    W.selectedIndex = I < L.length ? I : undefined;
}

function optionYes(V, X) {
    var Y = V.getOwn();
    return isNil(X) && isNil(Y) || X == Y ? "yes" : undefined;
}

// function mkSlider(P, Cb, Ww, Hw, Wh, Hh, Bw) {
//     Hw = Hw || 15;
//     Wh = Wh || Hw;
//     Hh = Hh || Hw;
//     Bw = Bw || 1;
//     var D = mk(P, "div", {}, {
//             position: "relative",
//             border: "inset " + px(Bw),
//             width: Ww ? px(Ww) : "100%",
//             height: px(Hw),
//             cursor: "pointer",
//             "background-color": "white"
//         });
//     var H = mk(D, "div", {}, {
//             position: "absolute",
//             border: "outset " + px(Bw),
//             width: px(Wh - 2 * Bw),
//             height: px(Hh - 2 * Bw),
//             "background-color": "#666"
//         });
//     function most() {
//         //alert(D.offsetLeft + " " + D.offsetWidth);
//         //return D.offsetWidth - Wh - 2 * Bw;
//         //alert(parseInt(computedStyle(D, "width")));
//         return parseInt(computedStyle(D, "width")) - Wh - 2 * Bw;
//     }
//     function move(X) {
//         //alert(X + " " + parseInt(lim(0, X, most())));
//         H.style.left = px(parseInt(lim(0, X, most())));
//         //alert(computedStyle(H, "left"));
//         //H.style.left = px(50);
//     }
//     var Chg = function(X) {
//         move(X);
//         if(Cb) Cb(H.offsetLeft / most());
//     };
//     D.onclick = cb(function(E) {
//             Chg(Mx - Wh / 2 - D.offsetLeft - Bw - D.offsetParent.offsetLeft);
//             return true;
//         });
//     H.onclick = cb(function(E) {return true;});
//     draggable(H, H, function(W, Dx, Dy, F, M) {
//             var X = W.offsetLeft + Dx;
//             if(0 <= X && X <= most()) {
//                 Chg(X);
//                 return true;
//             }
//             return false;
//         });
//     D.sliderValue = function(V, C) {
//         if(0 <= V) {
//             var M = most();
//             alert(V + " " + M);
//             move(V * M);
//             if(C && Cb) Cb(H.offsetLeft / M);
//         }
//         return H.offsetLeft / M;
//     };
//     return D;
// }

function mkSelectN(P, A, Z, V, I, If) {
    I = I || 1;
//     var S = mk(P, "select");
//     S.onchange = cbSelect(function(X) {V.set(X, S);});
//     V.hook(function(V, U) {if(U !== S) selectOption(S, V.getOwn());});
//     mk(S, "option", {value: "NIL", selected: optionYes(V, NIL)}, {}, "default");
//     var Y = V.pretty();
    // TODO better widget - optimize
//     for(var J = A; J <= Z; J += (I || 1))
//         mk(S, "option", {value: J, selected: optionYes(V, J)}, {},
//            isNil(Y) ? J : Y(J));
    //var W = mk(P, "table", {}, {border: "1px dotted gray"});
    var W = mk(P, "table", {}, {
            //"background-color": "white"
            //border: "1px dotted gray"
            //"border-collapse": "collapse"
        });
    var R = mk(W, "tr");
    var C = mk(mk(R, "td"), "input", {type: "checkbox"});
    //var Che = function () {C.checked = !isNil(V.getOwn());};
    //Che();
    var Val = function(X) {return parseInt(A + X * (Z - A));};
//     var S = mkSlider(mk(R, "td", {}, {width: "100%"}), function(X) {
//             V.set(Val(X), W);
//             E.value = V.pretty()(V.get());
//         }, 100);
    var E = mk(mk(R, "td"), "input", {
            type: "text",
            size: 5,
            //value: V.pretty()(V.get())
        });
    var Lim = function(X, I) {
        I = I || 0;
        X = X || V.get();
        X = isNil(X) ? A : X;
        return lim(A, parseInt(X + I), Z);
    };
//     this.Sli = function(X, I) {
//         X = Lim(X, I);
//         S.sliderValue((X - A) / (Z - A));
//         return X;
//     };
    var D = mk(R, "td");
    var Up = mk(D, "div", {}, {
            "font-size": "small",
            cursor: "pointer"
        }, "\u25B2"); //9650
    var Dn = mk(D, "div", {}, {
            "font-size": "small",
            cursor: "pointer"
        }, "\u25BC"); //9660
    var Vis = function() {
        //S.style.visibility = C.checked ? "visible":"hidden";
        //show(E, C.checked);
        //show(Up, C.checked);
        //show(Dn, C.checked);
        E.style.visibility = C.checked ? "visible":"hidden";
        Up.style.visibility = C.checked ? "visible":"hidden";
        Dn.style.visibility = C.checked ? "visible":"hidden";
    };
    //Vis();
    If = If || function(X) {
        return isNil(X) ? A : X;
    };
    C.onchange = cb(function(Ev) {
            Vis();
            //V.set(C.checked ? this.Sli() : NIL, W);
            V.set(C.checked ? If(V.get()) : NIL, W, W);
            E.value = V.pretty()(V.get());
        });
    E.onchange = cb(function(Ev) {
            var X = V.parse()(E.value);
            if(!isNil(X)) {
                //V.set(this.Sli(X), W);
                V.set(Lim(X), W, W);
                E.value = V.pretty()(V.get());
            }
        });
    Up.onclick = cb(function(Ev) {
            //V.set(this.Sli(V.get(), I), W);
            V.set(Lim(V.get(), I), W, W);
            E.value = V.pretty()(V.get());
        });
    Dn.onclick = cb(function(Ev) {
            //V.set(this.Sli(V.get(), -I), W);
            V.set(Lim(V.get(), -I), W, W);
            E.value = V.pretty()(V.get());
        });
    V.hookOwn(function(V, U) {
            //alert("yes1");
            if(U !== W) {
                //alert("yes2");
                C.checked = !isNil(V.getOwn());
                Vis();
                E.value = V.pretty()(V.get());
            }
        });
    //    return this;
    return W;
}

function mkText(P, V) {
    var X = V.get();
    var W = mk(P, "textarea", {rows: 8, cols: 40}, {}, prettyNil(X));
    W.onchange = cb(function() {
            var Y = W.value; // bizarre;-)
            V.set(0 < Y.length ? Y : NIL, W);
        });
    V.hook(function(V, U) {if(U !== W) W.value = prettyNil(V.getOwn());});
    return W;
}

function mkSelectFont(P, V) {
    var S = mk(P, "select");
//     S.onchange = cbSelect(function(X) {V.set(parseIntX(X), S);});
//     V.hook(function(V, U) {if(U !== S) selectOption(S, V.getOwn());});
//     for(var I in Fonts)
//         mk(S, "option", {value: I, selected: optionYes(V, I)}, {}, Fonts[I]);
    for(var I in Fonts)
        mk(S, "option", {value: I}, {}, Fonts[I]);
    S.onchange = cbSelect(function(X) {V.set(parseIntX(X), S, S);});
    V.hookOwn(function(V, U) {
            if(U !== S) selectOption(S, V.getOwn());
        });
    return S;
}

function mkSelectColor(P, V) {
    var S = mk(P, "select");
    var L = [["#000000", "Black", "white"],
             ["#808080", "Gray"],
             ["#A9A9A9", "Dark Gray"],
             ["#D3D3D3", "Light Gray"],
             ["#FFFFFF", "White"],
             ["#7FFFD4", "Aquamarine"],
             ["#0000FF", "Blue"],
             ["#000080", "Navy", "white"],
             ["#800080", "Purple", "white"],
             ["#FF1493", "Deep Pink"],
             ["#EE82EE", "Violet"],
             ["#FFC0CB", "Pink"],
             ["#006400", "Dark Green", "white"],
             ["#008000", "Green", "white"],
             ["#9ACD32", "Yellow Green"],
             ["#FFFF00", "Yellow"],
             ["#FFA500", "Orange"],
             ["#FF0000", "Red"],
             ["#A52A2A", "Brown"],
             ["#DEB887", "Burly Wood"],
             ["#F5F5DC", "Beige"]];
//     mk(S, "option", {value: "NIL", selected: optionYes(V, NIL)}, {}, "default");
//     for(var I = 0; I < L.length; I++) {
//         var X = L[I][0];
//         mk(S, "option", {value: X, selected: optionYes(V, X)},
//            {background: X, color: L[I][2]}, L[I][1]);
//     }
//     S.onchange = cbSelect(function(X) {V.set(X, S);});
//     V.hook(function(V, U) {if(U !== S) selectOption(S, V.getOwn());});
    mk(S, "option", {value: "NIL"}, {}, "default");
    for(var I = 0; I < L.length; I++) {
        var X = L[I][0];
        mk(S, "option", {value: X}, {background: X, color: L[I][2]}, L[I][1]);
    }
    S.onchange = cbSelect(function(X) {V.set(X, S, S);});
    V.hookOwn(function(V, U) {
            if(U !== S) selectOption(S, V.getOwn());
        });
    return S;
}

function mkVar(P, V) {
    var W;
    if(V.tag() == "color") {
        var S = {
            border: "1px solid black",
            background: V.get(),
            font: "monospace"
        };
        W = mk(P, "span", {}, S, "\u00A0\u00A0\u00A0\u00A0\u00A0");
    } else {
        var X = V.get();
        var Y = V.pretty();
        var Z = isNil(Y) ? (isNil(X) ? "NIL" : X) : Y(X);
        W = mk(P, "span", {}, {}, Z);
    }
    V.hook(function (V) {
            var X = V.get();
            var Y = V.pretty();
            var Z = isNil(Y) ? (isNil(X) ? "NIL" : X) : Y(X);
            if(isNil(X)) W.innerText = Z;
            else if(V.tag() == "color") W.style.backgroundColor = X;
            else W.innerText = Z;
        });
    return W;
}

function dragDialog(W, Dx, Dy) {
    W.style.left = px(W.offsetLeft + Dx);
    W.style.top = px(W.offsetTop + Dy);
    return true;
}

function mkDialog(Id, Lbl, Cfn, Zfn, Hfn) {
    var B = body();
    var D = mk(B, "div", {id: Id}, {
            border: "1px solid black",
            background: "#eef",
            position: "absolute",
            visibility: "hidden",
            display: "none",
            "z-index": 100
        });
    var H = mk(D, "div", {},
               "text-align:center;background:#dde;padding:0.2em", Lbl);
    if(Hfn) Hfn(D, H);
    var C = mk(D, "div", {}, "padding:0.5em");
    if(Cfn) Cfn(D, C);
    var Z = mk(D, "div", {},
               "text-align:right;padding-left:0.5em;padding-right:0.2em");
    if(Zfn) Zfn(D, Z);
    draggable(D, H, dragDialog);
    return C;
}

//var optShow = new Var();
//var edShow = new Var();

function mkDh(P) {
    var W = mk(P, "div");
    var R = mk(W, "div");
    var C = mk(R, "input", {type: "checkbox"});
    mk(R, "text", "from font size");
    var R2 = mk(W, "div");
    var S = mkSelectN(R2, 10, 1000, Dhx, 1,
                      function(X) {return int2realX(Ds.get());});
    C.onchange = cb(function(E) {
            Dh.set(C.checked ? T : NIL, W, W);
            show(S, C.checked ? NIL : T);
        });
    Dh.hookOwn(function(V, U) {
            if(U !== W) {
                C.checked = !isNil(V.getOwn());
                show(S, C.checked ? NIL : T);
            }
        });
}

function cfnOptDlg(D, C) {
    //mk(C, "span", {}, {"background-color": "yellow"}, "What?");
    //var P = mk(C, "table", {}, {"background-color": "green"});
    var P = mk(C, "table");
    var R0 = mk(P, "tr");
    mk(R0, "td");
    mk(R0, "td", {}, {"text-align": "center"}, "Chosen");
    mk(R0, "td", {}, {"text-align": "center"}, "Used");
    var R1 = mk(P, "tr");
    mk(R1, "td", {}, {}, "Font Type:");
    mkSelectFont(mk(R1, "td"), Df);
    mkVar(mk(R1, "td", {}, {"text-align": "center"}), Df);
    var R2 = mk(P, "tr");
    mk(R2, "td", {}, {}, "Font Size:");
    mkSelectN(mk(R2, "td"), 1, 100, Ds);
//     var Sds = mkSelectN(mk(R2, "td"), 1, 100, Ds);
//     optShow.hook(function(V) {
//             if(!isNil(V)) {
//                 Sds.Sli();
//                 alert("done");
//             }
//         });
    mkVar(mk(R2, "td", {}, {"text-align": "center"}), Ds);
    var R3 = mk(P, "tr");
    mk(R3, "td", {}, {}, "Text Color:");
    mkSelectColor(mk(R3, "td"), Dc);
    mkVar(mk(R3, "td", {}, {"text-align": "center"}), Dc);
    var R4 = mk(P, "tr");
    mk(R4, "td", {}, {}, "Line Spacing:");
    mkSelectN(mk(R4, "td"), 10, 1000, Dv, 1);
    mkVar(mk(R4, "td", {}, {"text-align": "center"}), Dv);
    var R5 = mk(P, "tr");
    mk(R5, "td", {}, {}, "Letter Spacing:");
    mkDh(mk(R5, "td", {}, {border: "1px dashed gray"}));
    mkVar(mk(R5, "td", {}, {"text-align": "center"}), Dh);
}

function zfnOptDlg(D, Z) {
    var P = {
        type: "button",
        value: "Hide",
        onclick: cb(function() {
                hideDialog(D);
                //setTimeout('optShow.set(NIL)', 1000);
            })
    };
    mk(Z, "input", P, {margin: "0.5em"});
}

function mkIv(P) {
    var W = mk(P, "div");
    var R = mk(W, "div");
    var S = mkSelectN(R, 10, 1000, Iv, 1);
    var R2 = mk(W, "div");
    var C = mk(R2, "input", {type: "checkbox"});
    mk(R2, "text", "from font size");
    Ivm.hookOwn(function(V, U) {
            if(U !== W) {
                C.checked = !isNil(V.getOwn());
            }
        });
    Iv.hookOwn(function(V, U) {
            if(U !== W) {
                show(R2, isNil(V.getOwn()) ? T : NIL);
            }
        });
    C.onchange = cb(function(E) {
            Ivm.set(C.checked ? T : NIL, W, W);
        });
}

function mkIh(P) {
    var W = mk(P, "div");
    var R = mk(W, "div");
    var C = mk(R, "input", {type: "checkbox"});
    mk(R, "text", "manual");
    var R2 = mk(W, "div");
    var S = mkSelectN(R2, 10, 1000, Ihx, 1,
                      function(X) {return int2realX(Is.get());});
    var R3 = mk(W, "div");
    var Cm = mk(R3, "input", {type: "checkbox"});
    mk(R3, "text", "from font size");
    Ihm.hookOwn(function(V, U) {
            if(U !== W) {
                Cm.checked = !isNil(V.getOwn());
            }
        });
    Ih.hookOwn(function(V, U) {
            if(U !== W) {
                C.checked = !isNil(V.getOwn());
                show(R2, C.checked ? T : NIL);
                show(R3, C.checked ? NIL : T);
            }
        });
    C.onchange = cb(function(E) {
            Ih.set(C.checked ? T : NIL, W, W);
            show(R2, C.checked ? T : NIL);
            show(R3, C.checked ? NIL : T);
        });
    Cm.onchange = cb(function(E) {
            Ihm.set(Cm.checked ? T : NIL, W, W);
        });
}

function cfnEdDlg(D, C) {
    var P = mk(C, "table");
    var R0 = mk(P, "tr");
    mk(R0, "td", {}, {}, "Text:");
    mkText(mk(R0, "td", {colSpan: 2}), It);
    var Rh = mk(P, "tr");
    mk(Rh, "td");
    mk(Rh, "td", {}, {"text-align": "center"}, "Chosen");
    mk(Rh, "td", {}, {"text-align": "center"}, "Used");
    var R1 = mk(P, "tr");
    mk(R1, "td", {}, {}, "Font Type:");
    mkSelectFont(mk(R1, "td"), If);
    mkVar(mk(R1, "td", {}, {"text-align": "center"}), If);
    var R2 = mk(P, "tr");
    mk(R2, "td", {}, {}, "Font Size:");
    mkSelectN(mk(R2, "td"), 1, 100, Is);
    mkVar(mk(R2, "td", {}, {"text-align": "center"}), Is);
    var R3 = mk(P, "tr");
    mk(R3, "td", {}, {}, "Text Color:");
    mkSelectColor(mk(R3, "td"), Ic);
    mkVar(mk(R3, "td", {}, {"text-align": "center"}), Ic);
    var R4 = mk(P, "tr");
    mk(R4, "td", {}, {}, "Line Spacing:");
    mkIv(mk(R4, "td", {}, {border: "1px dashed gray"}));
    mkVar(mk(R4, "td", {}, {"text-align": "center"}), Iv);
    var R5 = mk(P, "tr");
    mk(R5, "td", {}, {}, "Letter Spacing:");
    mkIh(mk(R5, "td", {}, {border: "1px dashed gray"}));
    mkVar(mk(R5, "td", {}, {"text-align": "center"}), Ih);
}

function zfnEdDlg(D, Z) {
    var update = function() {
        var X = It.get();
        var A = Ed.get();
        if(isNil(A)) { // create
            if(!isNil(X)) {
                var A = new Annot().annot;
                A.T.set(It.getOwn());
                A.F.set(If.getOwn());
                A.S.set(Is.getOwn());
                A.C.set(Ic.getOwn());
                A.Vm.set(Ivm.getOwn());
                A.V.set(Iv.getOwn());
                A.Hx.set(Ihx.getOwn());
                A.Hm.set(Ihm.getOwn());
                A.H.set(Ih.getOwn());
                Ed.set(A);
            }
        } else { // edit
            if(isNil(X)) {
                Sel.set(xdelete(A.D, Sel.get()));
                A.remove();
            } else {
                A.T.set(It.getOwn());
                A.F.set(If.getOwn());
                A.S.set(Is.getOwn());
                A.C.set(Ic.getOwn());
                A.Vm.set(Ivm.getOwn());
                A.V.set(Iv.getOwn());
                A.Hx.set(Ihx.getOwn());
                A.Hm.set(Ihm.getOwn());
                A.H.set(Ih.getOwn());
            }
        }
        //xalert(annots());
    };
    /*    var A = mk(Z, "input", { // TODO revert
            type: "button",
            value: "Revert",
            onclick: cb(function() {Ed.set(Ed.get());}),
            }, {margin: "0.5em"}); */
    var A = mk(Z, "input", {
            type: "button",
            value: "Apply",
            onclick: cb(function() {update();})
        }, {margin: "0.5em"});
    var B = mk(Z, "input", {
            type: "button",
            value: "Ok",
            onclick: cb(function() {update(); hideDialog(D);})
        }, {margin: "0.5em"});
    var P = {
        type: "button",
        value: "Cancel",
        onclick: cb(function() {hideDialog(D);})
    };
    var C = mk(Z, "input", P, {margin: "0.5em"});
}

function hfnEdDlg(D, H) {
    Ed.hook(function(V) {H.innerText = isNil(V.get()) ? "Create" : "Text";});
}

function mkAjax() {
    if(window.XMLHttpRequest) return new XMLHttpRequest();
    else return new ActiveXObject("Microsoft.XMLHTTP");
}

function mkUnloadForm () {
    // var I = mk(body(), "iframe", {
    //         src: window.location.href.split("?")[0] + "@unload"
    //     }, {
    //         width: px(0),
    //         height: px(0),
    //         border: px(0),
    //         padding: px(0), // TODO take no space at the bottom
    //         margin: px(0)
    //     });
    // window.onunload = cb(function unload (E) {
    //                          ///if(window.ondocUnload != "Saving...") {
    //                          //    window.ondocUnload = "Saving...";
    //                          var A = lisp2string(annots());
    //                          var V = I.contentWindow.document.getElementById("v");
    //                          V.value = A;
    //                          alert(A + "\n\n" + C.innerText);
    //                          var C = w("cfg";
    //                          if(C && (C.innerText != A) && confirm("Save changes?"))
    //                              V.parentNode.submit();
    //                          return true;
    //                      });
    var C = w("cfg");
    function changed(A) {
        var B = C && C.innerText;
        if(B) {
            B = parse(B);
            B = cons(caddr(B), cons(cons(pgNo(), cdddr(B)), NIL));
            B = lisp2string(B);
        }
        //alert(A + "\n===\n" + B + "\n===\n" + (A && B && (A != B)));
        return A && B && (A != B);
    }
    window.onbeforeunload = cb(function unload (E) { // must be sync!
                                   var A = lisp2string(annots());
                                   if(changed(A)) {
                                       var M = "Unsaved changes will be lost.";
                                       if(E) E.returnValue = M; // ie+ff
                                       return M; // safari
                                   }
                             // if(changed(A) && confirm("Save changes?")) {
                             //     var Ajax = mkAjax();
                             //     if(Ajax) {
                             //         Ajax.open("POST", window.location.href.split("?")[0] + "@unload", false);
                             //         var P = "v=" + A;
                             //         Ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                             //         Ajax.setRequestHeader("Content-length", P.length);
                             //         Ajax.setRequestHeader("Connection", "close");
                             //         Ajax.send(P);
                             //         if(Ajax.responseText != "Ok")
                             //             alert("Error when saving changes: '" + Ajax.responseText + "'");
                             //     } else alert("Error when saving changes (no Ajax).");
                             // }
                             // return true;
                               });
}

function mkSaveButton(P) {
    var F = mk(P, "form");
    F.method = "post";
    F.action = "@unload"; //window.location.href.split("?")[0] + "@unload";
    F.style.cssText = "display:-moz-inline-stack;" + style2string({
            display: "inline-block",
            zoom: 1,
            "*display": "inline",

            padding: "0px",
            margin: "0px"
        });
    var V = mk(F, "input", {type: "hidden", name: "v"});
    function onUnload (E) {
        V.value = lisp2string(annots());
        //alert(V.value);
        window.onbeforeunload = cb(function(E) {});
        V.parentNode.submit();
    }
    mk(F, "span", {onclick: cb(onUnload)}, {}, "Save");
}

function initAnnot(O) {
    function initText(X) {
        EdX = Xpt2px(parseIntX(cadr(X)));
        EdY = Ypt2px(parseIntX(caddr(X)));
        var A = new Annot().annot;
        A.T.set(car(X));

        X = cdddr(X);
        A.F.set(parseIntX(car(X)));
        A.S.set(parseIntX(cadr(X)));
        X = cddr(X);
        var R = parseIntX(car(X));
        var G = parseIntX(cadr(X));
        var B = parseIntX(caddr(X));
        if(!isNil(R) && !isNil(G) && !isNil(B)) A.C.set(rgb(R, G, B));
        X = cdddr(X);
        if(isT(car(X))) {
            A.Vm.set(T);
            A.V.set(NIL);
        } else {
            A.Vm.set(NIL);
            A.V.set(parseIntX(car(X)));
        }
        if(isNil(cadr(X))) {
            A.Hm.set(isT(caddr(X)) ? T : NIL);
            A.Hx.set(NIL);
            A.H.set(NIL);
        } else {
            A.Hm.set(NIL);
            A.Hx.set(parseIntX(caddr(X)));
            A.H.set(T);
        }
    }
    function initBox(L) {
        var X = cadr(L);
        var Y = caddr(L);
        var W = cadddr(L);
        var H = caddddr(L);
        new Box(X, Y, W, H);
    }
    function initLink(L) {
        var X = cadr(L);
        var Y = caddr(L);
        var W = cadddr(L);
        var H = caddddr(L);
        var U = cadddddr(L);
        new Link(X, Y, W, H, U);
    }
    var C = w("cfg");
    if(C) {
        var V = parse(C.innerText);
        //alert(C.innerText);
        //xalert(V);
        wempty(O);

        var X = car(V);
        Xpt = parseIntX(car(X));
        Ypt = parseIntX(cadr(X));
        Wpt = parseIntX(caddr(X));
        Hpt = parseIntX(cadddr(X));
        Wpx = w("i").width;
        Hpx = w("i").height;
        w("o").style.width = px(Wpx);
        w("o").style.height = px(Hpx);

        X = cadr(V);
        Ddf.set(parseIntX(car(X)));
        Dds.set(parseIntX(cadr(X)));
        X = cddr(X);
        var R = parseIntX(car(X));
        var G = parseIntX(cadr(X));
        var B = parseIntX(caddr(X));
        Ddc.set(rgb(R, G, B));

        X = caddr(V);
        Df.set(parseIntX(car(X)));
        Ds.set(parseIntX(cadr(X)));
        X = cddr(X);
        R = parseIntX(car(X));
        G = parseIntX(cadr(X));
        B = parseIntX(caddr(X));
        if(!isNil(R) && !isNil(G) && !isNil(B))
            Dc.set(rgb(R, G, B));
        X = cdddr(X);
        Dv.set(parseIntX(car(X)));
        if(isT(cadr(X))) {
            Dhx.set(NIL);
            Dh.set(T);
        } else {
            Dhx.set(parseIntX(cadr(X)));
            Dh.set(NIL);
        }

        for(var L = cdddr(V); !isNil(L); L = cdr(L)) {
            X = car(L);
            if(isString(car(X))) initText(X);
            else
                switch(car(X)) {
                case 1: initBox(X); break;
                case 2: initLink(X); break;
                }
        }
    } else {
        wempty(O);
    }
}

//<protocol>//<host>[:<port>]/<pathname>[<query>][<location>] TODO fix url parsing

function init() {
    //alert(document.compatMode);
    mkUnloadForm();

    mkDialog("s", "Options", cfnOptDlg, zfnOptDlg);
    mkDialog("e", "Text", cfnEdDlg, zfnEdDlg, hfnEdDlg);

    var I = w("i");
    I.parentNode.style.position = "relative";
//     if(Msie) {
//         I.parentNode.style.zIndex = 10000;
//         I.style.zIndex = -1;
//         //I.style.setAttribute("z-index", -1);
//         var O1 = mk(I.parentNode, "div", {}, {
//                 position: "relative",
//                 "z-index": 1000
//             });
//         var O2 = mk(O1, "div", {}, {
//                 position: "absolute",
//                 left: "0px",
//                 top: "0px",
//                 width: px(I.width),
//                 height: px(I.height)
//             });
//         var O = mk(O2, "div", {id: "o"}, {
//                 position: "relative",
//                 "z-index": 10
//             });
//     } else {
//     var O = mk(I.parentNode, "div", {id: "o"}, {
//             position: "absolute",
//             left: "0px",
//             top: "0px",
//             width: px(I.width),
//             height: px(I.height),
//             "z-index": 10
//         });
    var O = w("o");
//     }

    initAnnot(O);

    O.onclick = cb(function () {
                       var M = Mode.get();
                       switch(M) {
                       case "Select": Sel.set(NIL); break;
                       case "Text": showEdDialog(NIL, Mx, My); break;
                       case "Here":
                           var O = w("o");
                           var P = O.parentNode.parentNode;
                           var X = Math.round(Xpx2pt(Mx - P.offsetLeft + P.scrollLeft));
                           var Y = Math.round(Ypx2pt(My - P.offsetTop + P.scrollTop));
                           var U = window.location.href.split("?")[0].split("#")[0];
                           // http://localhost:1431/d/400/2/?x=+285&y=+669#d
                           var W = U + "?x=" + X + "&y=" + Y + "#d";
                           //alert(W);
                           window.location = W;
                           break;
                       }
                   });

    // TODO refactor common behaviour Annot < Text | Box | Link
    var Xa, Ya;
    var TmpBox = mk(w("o"), "div", {}, {
                        position: "absolute",
                        left: px(0),
                        top: px(0),
                        
                        width: px(0),
                        height: px(0),
                        "background-color": "yellow", // TODO as option?
                        opacity: 0.4, filter: "alpha(opacity=40)", // TODO as option?

                        //overflow: "auto",
                        "z-index": 20
                        //border: "1px solid yellow",
                    });
    function dragTmpBox(W, Dx, Dy, F, M) {
        if(Mode.get() == "Box") {
            W.style.backgroundColor = "yellow"; // TODO as option?
            W.style.opacity = 0.4; W.style.filter = "alpha(opacity=40)"; // TODO as option?
            var V = 10;
            var H = ((Dx < -V) || (V < Dx)) && ((Dy < -V) || (V < Dy));
            if(H) {
                var Xb = Xa + Dx;
                var Yb = Ya + Dy;
                var Ax = Xa < Xb ? Xa : Xb;
                var Bx = Xa < Xb ? Xb : Xa;
                var Ay = Ya < Yb ? Ya : Yb;
                var By = Ya < Yb ? Yb : Ya;
                var P = O.parentNode.parentNode;
                W.style.left = px(Ax - P.offsetLeft + P.scrollLeft);
                W.style.top = px(Ay - P.offsetTop + P.scrollTop);
                if(F) { // up
                    W.style.width = px(0);
                    W.style.height = px(0);
                    new Box(Xpx2pt(Ax - P.offsetLeft + P.scrollLeft),
                            Ypx2pt(By - P.offsetTop + P.scrollTop),
                            px2pt(Bx - Ax),
                            px2pt(By - Ay));
                } else { // move
                    W.style.width = px(Bx - Ax);
                    W.style.height = px(By - Ay);
                }
            } else {
                W.style.width = px(0);
                W.style.height = px(0);
            }
            return false;
        }
        if(Mode.get() == "Link") {
            W.style.backgroundColor = "blue";
            W.style.opacity = 0.3; W.style.filter = "alpha(opacity=30)";
            var V = 10;
            var H = ((Dx < -V) || (V < Dx)) && ((Dy < -V) || (V < Dy));
            if(H) {
                var Xb = Xa + Dx;
                var Yb = Ya + Dy;
                var Ax = Xa < Xb ? Xa : Xb;
                var Bx = Xa < Xb ? Xb : Xa;
                var Ay = Ya < Yb ? Ya : Yb;
                var By = Ya < Yb ? Yb : Ya;
                var P = O.parentNode.parentNode;
                W.style.left = px(Ax - P.offsetLeft + P.scrollLeft);
                W.style.top = px(Ay - P.offsetTop + P.scrollTop);
                if(F) { // up
                    var U = prompt("Please enter the URL of the link:");
                    W.style.width = px(0);
                    W.style.height = px(0);
                    if(U && (0 < U.length)) {
                        new Link(Xpx2pt(Ax - P.offsetLeft + P.scrollLeft),
                                 Ypx2pt(By - P.offsetTop + P.scrollTop),
                                 px2pt(Bx - Ax),
                                 px2pt(By - Ay),
                                 U);
                    }
                } else { // move
                    W.style.width = px(Bx - Ax);
                    W.style.height = px(By - Ay);
                }
            } else {
                W.style.width = px(0);
                W.style.height = px(0);
            }
            return false;
        }
        return true;
    }
    function dragTmpBoxWhen(W, X, Y) {
        if(Mode.get() == "Box" || Mode.get() == "Link") {
            Xa = X;
            Ya = Y;
            TmpBox.style.width = px(0);
            TmpBox.style.height = px(0);
            return true;
        }
        return false;
    }
    draggable(TmpBox, O, dragTmpBox, dragTmpBoxWhen);

    //var M = document.getElementsByClassName("mm");
    //    for(var I = 0; I < M.length; I++) {
    var P = w("mm"); //M[I];

        wempty(P);
        mk(P, "text", " ");
        mkSaveButton(P);

        mk(P, "text", " ");

        var F = mk(P, "form");
        F.method = "post";
        F.action = "@ddoc"; //window.location.href.split("?")[0] + "@ddoc";
        F.style.cssText = "display:-moz-inline-stack;" + style2string({
                display: "inline-block",
                zoom: 1,
                "*display": "inline",

                padding: "0px",
                margin: "0px"
            });
        mk(F, "span", {onclick: cb(onDownload)}, {}, "Download");
        mk(F, "input", {type: "hidden", name: "v"});

        mk(P, "text", " ~");

        var C = ["Here", "Text", "Box", "Link", "Select", "Move", "Delete"];
        for(var J = 0; J < C.length; J++) {
            //            mk(P, "text", J == 1 ? " :: " : " ");
            mk(P, "text", " ");
            (function() {
                var N = C[J];
                //var D = mk(P, "span", {className: N}, {}, N);
                var D = mk(P, "span", {id: N}, {}, N);
                D.onclick = cb(function() {Mode.set(N);});
                Mode.hook(function(V) {
                        //setStyle("." + N, "font-weight:" + (V.get() == N ? "bold" : "normal"));
                        w(N).className = (V.get() == N ? "on" : "off");
                    });
            })();
        }
        Mode.set(C[0]);
        Mode.notify();

        mk(P, "text", " ~ ");
        mk(P, "span", {onclick: cb(function() {
                        showDialog(w("s"));
                        //setTimeout('optShow.set(T)', 1000);
                    })}, {}, "Options");

        mk(P, "text", " ~ ");
        mk(P, "a", {href: ["m", "a", "i", "l", "t", "o", ":", "t", "o", "m",
                           "@", "l", "o", "g", "a", "n", "d", ".", "c", "o",
                           "m"].join("")}, {}, "Feedback");

        if(Msie) {
            var X = mk(P, "div", {}, {color: "red"});
            mk(X, "text", "Microsoft Internet Explorer is ");
            mk(X, "a", {href: "http://logand.com/sw/ondoc/index.html#browsers"},
               {}, "not supported");
            //mk(X, "text", " for editing annotations");
        }
        //    }
}

if(!Msie && !HTMLElement.prototype.innerText) {
    HTMLElement.prototype.__defineGetter__("innerText", function () {return(this.textContent);});
    HTMLElement.prototype.__defineSetter__("innerText", function(V) {this.textContent = V;});
}

