/*jslint browser: true, passfail: false, forin: true, undef: true, white: true*/

/*global console, $, $$, $A, $H, $F, Autocompleter, BodyLoaded, BrowseActions, Class, Constants, Effect, Element, Event, FileProgress, Prototype, SWFUpload, Ajax, escape*/

/*and sketchier...these should prolly go in Constants*/
/*global contacts, lcontacts, PAGE_PATH*/
if (typeof(console) == 'undefined') {
    console = {'log': function () {}};
}

window.reported_error = false;
window.onerror = function (e, f, l) {
    if (!window.reported_error) {
        var dbr = new Ajax.Request("/jse", {parameters: {'e': e, 'f': f, 'l': l, 'loc': location.href}});
        window.reported_error = true;
    }
};

Function.prototype.stop_calls_at = function (count) {
    var f = this;
    return function () {
        if (count-- > 0) {
            return f.apply(this, arguments);
        }
    };
};

var Jcached = {
    cache: {},
    set: function (name, value, expiration) {
        var c = Jcached.cache[name];

        if (!c) {
            Jcached.cache[name] = {};
            c = Jcached.cache[name];
        }

        c.value = value;
        c.expires = expiration ? (new Date()).getTime() + expiration : 0;
    },
    get: function (name) {
        var c = Jcached.cache[name];
        if (!c || (c.expires && (new Date()).getTime() > c.expires)) {
            delete Jcached.cache[name];
            return false;
        }

        return c.value;
    }
};

Function.prototype.cached = function (expiration) {
    // holds the value of thunk (no-arg function) in a cache for a period of time (or forever)
    var r = Math.random();
    var f = this;
    return function () {
        var x = Jcached.get(r);
        if (x !== false) {
            return x;
        }

        x = f();
        Jcached.set(r, x, expiration);
        return x;
    };
};

/*global DomUtil, Modal*/
var Util = {
    center: function (elm) {
        elm = $(elm);
        var l = (document.viewport.getWidth() - elm.getWidth()) / 2;
        elm.setStyle({left: Math.floor(l) + "px"});
    },
    pinTop: function (elm, slide) {
        elm = $(elm);
        if (slide) {
            elm.setStyle({top: window.pageYOffset + "px"});
        } else {
            var e = new Effect.Move(elm, {y: window.pageYOffset, mode: 'absolute', duration: 0.25});
        }
    },
    getTickWaiter: function (n, action) {
        var ticks = 0;
        return function () {
            if (ticks == n) {
                action();
            }
            ticks++;
        };
    },
    calcBox: function (t1, l1, t2, l2, box) {
        box.top = Math.min(t1, t2);
        box.left = Math.min(l1, l2);
        box.width = Math.abs(l1 - l2);
        box.height = Math.abs(t1 - t2);
    },
    initBox: function (top, left, box) {
        box.top = top;
        box.left = left;
        box.width = 0;
        box.height = 0;
    },
    pointOnBox: function (left, top, box) {
        return (top  >= box.top  && top  <= box.top  + box.height &&
                left >= box.left && left <= box.left + box.width);
    },
    cmpBox: function (left, top, box) {
        // returns cmp-style results regarding the point's position relative to the box
        if (top < box.top || left < box.left) {
            return -1;
        }

        if (top > box.top  + box.height || left > box.left + box.width) {
            return 1;
        }

        return 0;
    },
    boxOnBox: function (box1, box2) {
        var max_top = Math.max(box1.top, box2.top);
        var max_left = Math.max(box1.left, box2.left);
        var min_bottom = Math.min(box1.top + box1.height, box2.top + box2.height);
        var min_right = Math.min(box1.left + box1.width, box2.left + box2.width);

        return (max_top < min_bottom && max_left < min_right);
    },
    reduceBox: function (box, pct) { // reduces size of the box, keeping center
        var out = {};
        out.width = box.width * pct;
        out.height = box.height * pct;
        out.top = box.top + (box.height - out.height) / 2;
        out.left = box.left + (box.width - out.width) / 2;

        return out;
    },
    getBox: function (elm) {
        elm = $(elm);
        var d = elm.getDimensions();
        var p = elm.viewportOffset();
        return {top: p.top, left: p.left, width: d.width, height: d.height};
    },
    ts: function () {
        var d = new Date();
        return d.getUTCFullYear().toString() + "-" +
            (d.getUTCMonth() + 1).toString().lpad(2) + "-" +
            d.getUTCDate().toString().lpad(2) + " " +
            d.getUTCHours().toString().lpad(2) + ":" +
            d.getUTCMinutes().toString().lpad(2) + ":" +
            d.getUTCSeconds().toString().lpad(2);
    },
    time: function () {
        return (new Date()).getTime();
    },
    last_time: false,
    delta: function (init) {
        var time = Util.time();
        if (Util.last_time && (!init || typeof(init) != 'boolean')) {
            Util.log(time - Util.last_time);
        }
        Util.last_time = time;
        if (typeof(init) == 'string') {
            Util.log("^ " + init);
        }
    },
    toggle_names: {},
    toggle: function (name) {
        if (Util.toggle_names[name]) {
            Util.toggle_names[name] = false;
        } else {
            Util.toggle_names[name] = true;
        }

        return Util.toggle_names[name];
    },
    reset_toggle: function (name) {
        Util.set_toggle(name, false);
    },
    set_toggle: function (name, val) {
        Util.toggle_names[name] = val;
    },
    set_next_toggle: function (name, val) {
        Util.toggle_names[name] = !val;
    },
    url_hash: function () {

        var parts = location.href.split("#");
        if (parts.length < 2) {
            return "";
        } else {
            return parts[parts.length - 1];
        }
    },
    copy_to_clipboard: function (text, alt_title, alt_body)
    {
        var cb_elt = $("hold_clipboard");
        cb_elt.value = text;
        if (cb_elt.createTextRange) {
            var range = cb_elt.createTextRange();
            if (range && (typeof(BodyLoaded) == 'undefined' || BodyLoaded == 1)) {
                try {
                    range.execCommand('Copy');
                } catch (e) {
                    alt_body = alt_body || "Please copy the text below:";
                    alt_title = alt_title || "Copy text";

                    DomUtil.fillVal(text, 'text-to-copy');
                    DomUtil.fillVal(alt_body, "copy-modal-body");
                    Modal.show(alt_title, DomUtil.fromElm('copy-modal'));
                    $('text-to-copy').select();
                }
            }
        } else {
            if (!$("flashcb")) {
                var divholder = document.createElement('div');
                divholder.id = "flashcb";
                document.body.appendChild(divholder);
            }
            $("flashcb").innerHTML = '';
            var divinfo = '<embed src="/static/swf/_clipboard.swf" FlashVars="clipboard=' + encodeURIComponent(cb_elt.value) + '" width="0" height="0" type="application/x-shockwave-flash"></embed>';
            $("flashcb").innerHTML = divinfo;
        }
    },
    scrollTop: function () {
        return window.scrollY || document.documentElement.scrollTop || 0;
    },
    scrollLeft: function () {
        return window.scrollX || document.documentElement.scrollLeft || 0;
    },
    setCursor: function (cursor) {
        if (!document.styleSheets[0].cssRules) {
            return;
        }
        (document.styleSheets[0].rules || document.styleSheets[0].cssRules)[0].style.cursor = cursor;
        (document.styleSheets[0].rules || document.styleSheets[0].cssRules)[1].style.cursor = cursor;
    },
    clearCursor: function () {
        if (!document.styleSheets[0].cssRules) {
            return;
        }
        (document.styleSheets[0].rules || document.styleSheets[0].cssRules)[0].style.cursor = 'auto';
        (document.styleSheets[0].rules || document.styleSheets[0].cssRules)[1].style.cursor = 'pointer';
    },
    noHorizScroll: function () {
        if (!/Mac.*(Firefox\/3|Camino)/.match(navigator.userAgent)) {
            document.body.style.overflowX = 'hidden';
        }
    },
    allowHorizScroll: function () {
        document.body.style.overflowX = '';
    },
    scried: {},
    scry: function (id) {
        /* It's like $, but returns a cached reference to the extended element
         * It's only going to look it up once, so don't use if you're playing
         * games with creating new elements with old id's.
         *
         * If the DOM obj has a reference back to Javascript, eventually
         * kill it so it doesn't leak.
         *
         * The name is stolen from a mythical facebook function name.
         * Look it up in the dictionary if you wanna know what it means.
         */
        var scried = Util.scried;
        var elm = scried[id];
        if (!elm) {
            elm = $(id);
            scried[id] = elm;
        }
        return elm;
    },
    pathDepth: function (path) {
        var parts = path.split("/");

        var depth = 0;
        for (var i = 0; i < parts.length; i++) {
            if (parts[i].length) {
                depth++;
            }
        }

        return depth;
    },
    normPath: function (path) {
        if (!path || path.charAt(path.length - 1) != '/') {
            return path;
        }

        return path.substr(0, path.length - 1);
    },
    normDir: function (dir_path) {
        return Util.normPath(dir_path) + "/";
    },
    urlquote: function (path) {
        return path.split("/").map(encodeURIComponent).join("/");
    },
    unevent: function (d) { // Douglas Crockford's purge from http://javascript.crockford.com/memory/leak.html, but just for mouse events
        if (d.attributes) {
            d.onclick = null;
            d.onmouseover = null;
            d.onmouseout = null;
            d.onmousedown = null;
            d.onmouseup = null;
            d.onmousemove = null;
        }

        var a = d.childNodes, i, l;
        if (a) {
            l = a.length;
            for (i = 0; i < l; i += 1) {
                Util.unevent(a[i]);
            }
        }
    },
    yank: function (elm) {
        // yank an element out of the dom without memory leaking
        Util.unevent(elm);

        //Element.remove(elm);

        // interesting way to delete a node that doesn't create pseudo-leaks in ie
        if (!Util.dom_trash_can) {
            Util.dom_trash_can = Util.scry('trash-can');
        }
        Util.dom_trash_can.insert(elm);
        Util.dom_trash_can.update();

        elm = null;

        return elm;
    },
    ie6: window.external && typeof window.XMLHttpRequest == "undefined",
    ie: Prototype.Browser.IE,
    linux_ff3: navigator.userAgent.toLowerCase().indexOf('linux') > -1,
    log: function () {
        Util.scry('ieconsole').innerHTML += $A(arguments).join(" ") + "<br>";
    },
    childElement: function (elm, index) {
        if (!elm) {
            return;
        }
        var count = 0;
        var nodes = elm.childNodes;
        var l, i;

        l = nodes.length;
        for (i = 0; i < l; ++i) {
            var e = nodes[i];
            if (e.tagName && count++ == index) {
                return e;
            }
        }
    },
    disableSelection: function (elm) { // Bret Taylor's disable selection from http://ajaxcookbook.org/disable-text-selection
        elm.onselectstart = function () {
            return false;
        };
        elm.unselectable = "on";
        elm.style.MozUserSelect = "none";
        elm.style.cursor = "default";
    },
    enableSelection: function (elm) {
        elm.onselectstart = function () {
            return true;
        };
        elm.unselectable = "off";
        elm.style.MozUserSelect = "";
        elm.style.cursor = "";
    },
    bsearch: function (lst, elm, key) {
        if (!key) {
            key = function (e) {
                return e;
            };
        }

        var hi = lst.length; // exclusive on hi
        var lo = 0; // inclusive on lo

        while (hi > lo) {
            var mid = Math.floor(hi / 2 + lo / 2);
            var test = key(lst[mid]);

            if (test > elm) {
                hi = mid;
            } else if (test < elm) {
                lo = mid + 1;
            } else {
                return mid;
            }
        }

        return -1;
    },
    nonce: function () {
        var d = new Date();
        var time_part = d.getTime().toString();
        var rand_part = Math.floor(Math.random() * 1000000).toString().lpad(6);
        return time_part + rand_part;
    },
    focus: function (elm) {
        elm = $(elm);
        try {
            elm.focus();
        } catch (e) {
            // it's okay if focus fails
        }
    },
    sumStyles: function (element, style_list) {
        var elm_total = 0;
        if (element) {
    	    style_list.each(
                function (attr) {
                    elm_total += parseInt(element.getStyle(attr), 10) || 0; // IE chokes on undefined borders
                }
            );
	    }
        return elm_total;
    },
    syncHeight: function () {
        // This gets maximum height of elemens with class sync-height and subtracts the padding and border from the height;
        var max_height = $$('.sync-height').invoke("getHeight").max() - Util.sumStyles($$('.sync-height')[0], ["border-left-width", "padding-left", "padding-right", "border-right-width"]);
        $$('.sync-height').invoke("setStyle", {"height": max_height > 0 ? max_height + "px" : "auto"});
    }
};
Util.scrollLeft = Util.scrollLeft.cached(50);
Util.scrollTop = Util.scrollTop.cached(50);

document.observe("dom:loaded", Util.syncHeight);

String.prototype.widthSplit = function (width) {
    width = width || 15;
    var out = [];
    var s = this;

    var start = 0;
    var snip = s.substring(start, start + width);
    while (snip !== '') {
        out.push(snip);
        start += width;
        snip = s.substring(start, start + width);
    }

    return out;
};

String.prototype.lpad = function (width, filler) {
    var str = this;
    filler = filler || "0";

    while (str.length < width) {
        str = filler + str;
    }
    return str;
};

String.prototype.pad_nums = function () {
    return this.replace(/(\d+)/, function (s) {
        return s.lpad(10);
    });
};

String.prototype.reverse = function () {
    var splitext = this.split("");
    var revertext = splitext.reverse();
    var reversed = revertext.join("");
    return reversed;
};

String.prototype.replace_last = function (bad, good) {
    var rstr = this.reverse();
    var rbad = bad.reverse();
    var rgood = good.reverse();

    return rstr.replace(rbad, rgood).reverse();
};

String.prototype.create = function (s) {
    return s;
};

String.prototype.snippet = function (maxchars, where) {
    maxchars = maxchars || 45;
    where = where || 0.75;

    if (this.length <= maxchars) {
        return this;
    }

    var dot_pos = this.lastIndexOf(".");
    var ext = "";

    if (dot_pos > 0) {
        ext = this.substr(dot_pos);
        maxchars = maxchars - ext.length;
    } else {
        dot_pos = this.length;
        ext = "";
    }

    maxchars = maxchars - this.create("...").length;

    var left = Math.floor(maxchars * where);
    var right_count = maxchars - left;
    var right = dot_pos - right_count;
    var pre = this.substr(0, left);
    var post = this.substr(right, dot_pos - right);

    return pre + "..." + post + ext;
};


var Emstring = Class.create(
    {
        initialize: function (s) {
            this.s = s;
            this.info = this.widthInfo();
            this.length = s.length ? this.info[this.s.length - 1] : 0;
        },
        create: function (s) {
            return new Emstring(s);
        },
        widthInfo: function () {
            var r = {};
            r[-1] = 0;

            for (var i = 0; i < this.s.length; i++) {
                r[i] = r[i - 1] + this.ems(this.s.charAt(i));
            }

            return r;
        },
        findSpot: function (em) {
            if (!em) {
                return 0;
            }

            var s = 0;
            var e = this.s.length;
            while (s <= e) {
                var mid = Math.floor(s / 2 + e / 2);
                var count = this.info[mid - 1];

                if (count > em) {
                    e = mid - 1;
                } else if (count < em) {
                    s = mid + 1;
                } else { //exact match
                    return mid;
                }
            }

            // missed exact match, but close
            if (s > mid) {
                return s;
            } else {
                return mid;
            }
        },
        ems: function (c) {
            if (c == 'i' || c == 'l') {
                return 0.3;
            }
            if (c == 'm' || c == 'w') {
                return 1;
            }
            return 0.6;
        },
        substr: function (start, stop) {
            start = this.findSpot(start);
            stop = stop !== null ? this.findSpot(stop) : this.s.length;

            return new Emstring(this.s.substr(start, stop));
        },
        indexOf: function (s) {
            var at = this.s.indexOf(s);
            return at > -1 ? this.info[at - 1] : -1;
        },
        lastIndexOf: function (s) {
            var backwards_pos = this.s.reverse().indexOf(s.reverse());
            if (backwards_pos < 0) {
                return -1;
            }

            return (this.s.length - backwards_pos) - s.length;
        },
        toString: function () {
            return this.s;
        },
        snippet: String.prototype.snippet
    });

var Job = {
    complete: {},
    handled: function (job_id) {
        if (!job_id) {
            return false;
        }

        var already_handled = !!Job.complete[job_id];
        Job.complete[job_id] = true;

        return already_handled;
    },
    peek: function (job_id) {
        if (!job_id) {
            return false;
        }

        return !!Job.complete[job_id];
    }
};

/*global Notify*/
var RequestWatcher = {
    reqs: [],
    working_msg: "Still working...",
    TIMEOUT: 5,
    watch: function (req, no_message) {
        var r = RequestWatcher.reqs;

        if (!r.length) {
            RequestWatcher.int_id = setInterval(RequestWatcher.check_up, 500);
        }

        if (no_message) {
            req.skip_message = true;
        }

        r.push([req, Util.time()]);
    },
    check_up: function () {
        RequestWatcher.scan(false); // req_to_remove=false
    },
    remove: function (req) {
        RequestWatcher.scan(req); // req_to_remove=req
    },
    scan: function (req_to_remove) {
        var now = Util.time();
        var l = RequestWatcher.reqs.length;
        var r = RequestWatcher.reqs;

        var out = [];

        for (var i = 0; i < l; i++) {
            var req = r[i][0];
            var started = r[i][1];
            var delta = now - started;

            if (req.transport.readyState == 4) {
                // done with the request
                Notify.clearIf(RequestWatcher.working_msg);
                continue;
            }

            if (delta > 2000 && !req.skip_message) {
                req.skip_message = true;
                Notify.ServerSuccess(RequestWatcher.working_msg);
            }

            if (delta > RequestWatcher.TIMEOUT * 1000 && req.job) {
                req.transport.abort();
            }

            if (req != req_to_remove) {
                out.push([req, started]);
            } else {
                req.transport.abort();
            }
        }

        RequestWatcher.reqs = out;

        if (!out.length) {
            clearInterval(RequestWatcher.int_id);
        }
    }
};

/*global ModalProgress*/
var ProgressWatcher = {
    job_info: {},
    INIT_POLL_INT: 1000,
    FAILS_MEAN_FAIL: 3,
    MODAL_WAIT_MS: 1000,
    watch: function (req) {
        ProgressWatcher.job_info[req.job_id] = {};
        var info = ProgressWatcher.job_info[req.job_id];

        info.req = req;
        info.poll_int = ProgressWatcher.INIT_POLL_INT;
        info.poll_count = 0;
        info.int_id = setInterval(ProgressWatcher.update_for(req.job_id), info.poll_int);
        info.failures = 0;
        info.start_time = Util.time();
    },
    update_for: function (id) {
        return function () {
            return ProgressWatcher.update(id);
        };
    },
    backoff: function (id) {
        var info = ProgressWatcher.job_info[id];
        clearInterval(info.int_id);
        info.poll_int = Math.min(Math.floor(info.poll_int * 1.5), 30000); // 30 seconds is longest poll interval
        info.int_id = setInterval(ProgressWatcher.update_for(id), info.poll_int);
    },
    update: function (id) {
        var req_info = ProgressWatcher.job_info[id];
        if (Job.peek(id)) {
            return ProgressWatcher.done(id);
        }
        req_info.poll_count++;

        if (req_info.poll_count % 10 === 0) {
            // time to slow this train down
            ProgressWatcher.backoff(id);
        }

        if (!req_info.modaled && Util.time() - req_info.start_time > ProgressWatcher.MODAL_WAIT_MS) {
            var options = req_info.req.options;
            ModalProgress.show(options.progress_text, options.cover_this);
            options.onProgress = ModalProgress.update;
            req_info.modaled = true;
        }

        var ar = new Ajax.Request("/job_status/" + id,
        {
            method: 'post',
            t: Constants.TOKEN,
            onSuccess: function (req) {
                var info = ProgressWatcher.job_info[id];
                var progress = req.responseText;
                var kill_watcher = false;

                if (progress.indexOf("err") === 0) {
                    ProgressWatcher.done(id);
                    ModalProgress.hide();

                    if (info.req.options.onFailure && !Job.handled(id)) {
                        info.req.options.onFailure(req);
                    }

                    return;
                }

                if (progress.indexOf("done") === 0) {
                    // go pick up the results, passing them back like normal
                    info.req.options.job = false; // return the handlers to normal mode

                    if (!Job.handled(id)) {
                        var ar = new Ajax.Request("/job_results/" + id, {
                            onSuccess: function (r) {
                                if (Job.handled(id)) {
                                    return;
                                }
                                Notify.clearIf(RequestWatcher.working_msg);
                                if (info.req.options.onSuccess) {
                                    info.req.options.onSuccess(r);
                                }
                            },
                            onFailure: function (r) {
                                if (Job.handled(id)) {
                                    return;
                                }
                                Notify.clearIf(RequestWatcher.working_msg);
                                if (info.req.options.onFailure) {
                                    info.req.options.onFailure(r);
                                }
                            }
                        });

                    }

                    // progress is 100%
                    var parts = progress.split("/");
                    progress = parts[1] + "/" + parts[1];

                    ProgressWatcher.done(id);
                    ModalProgress.hide();
                } else {
                    // update the progress counter
                    try {
                        if (info.req.options.onProgress) {
                            info.req.options.onProgress(req.responseText);
                        }
                    } catch (e) {}
                }
            },
            onFailure: function (req) {
                var info = ProgressWatcher.job_info[id];

                info.failures++;
                if (info.failures >= ProgressWatcher.FAILS_MEAN_FAIL) {
                    // we're not going to try again
                    if (info.req.options.onFailure) {
                        info.req.options.onFailure(req, true); // force=true
                    }
                    RequestWatcher.remove(info.req);
                    ProgressWatcher.done(id);
                    ModalProgress.hide();
                }
            }
        });
    },
    done: function (id) {
        var info = ProgressWatcher.job_info[id];
        clearInterval(info.int_id);
        delete ProgressWatcher.job_info[id];
        ModalProgress.hide();
    }
};

Ajax.DBRequest = Class.create(Ajax.Request,
{
    initialize: function ($super, url, options) {
        options = options || {};
        options.method = 'post';
        options.parameters = options.parameters || {};
        options.parameters.t = Constants.TOKEN;
        var cleanUp = options.cleanUp || function (ok) {};

        if (options.job) {
            this.job_id = Util.nonce();
            options.parameters.job_id = this.job_id;
            ProgressWatcher.watch(this);
        }

        RequestWatcher.watch(this, !!options.job); //no_still_working=!!options.job

        var origOnFailure = options.onFailure;
        var origOnSuccess = options.onSuccess;
        var origOnComplete = options.onComplete;

        options.onFailure = function (req) {
            if (Job.handled(req.request.job_id)) {
                return;
            }
            Notify.ServerError();
            cleanUp(false); // okay=false
            if (origOnFailure) {
                origOnFailure(req);
            }
        };

        options.onSuccess = function (req) {
            if (Job.handled(req.request.job_id)) {
                return;
            }

            if (!req.responseText.length) {
                if (!options.job) {
                    if (!options.noAutonotify) {
                        Notify.ServerError();
                    }

                    if (origOnFailure) {
                        origOnFailure(req);
                    }
                }
            } else if (req.responseText.indexOf('err:') === 0) {
                if (!options.noAutonotify) {
                    Notify.ServerError(req.responseText.substr(4));
                }

                if (origOnFailure) {
                    origOnFailure(req);
                }
            } else {
                if (origOnSuccess) {
                    origOnSuccess(req);
                }
                if (origOnComplete) {
                    origOnComplete(req);
                }
            }

            cleanUp(true); // okay=true
        };

        if (options.job) {
            url += (url.indexOf("?") != -1 ? "&" : "?") + "long_running";
        }

        // make the request
        $super(url, options);
    }
});

/*global SuggestionInput*/
var Sharing = {
    SUBMITTED: false,
    add_collab: function (button) {
        button = $(button);
        button.blur();
        var tr = button.up('tr');
        var td = button.up('td');
        var input = td.previous('td').down('input');

        var email = input.value;
        if (!email.length || email == Sharing.COLLAB_DEFAULT) {
            return false;
        }

        var remove_link = new Element('a', {'class': 'panel new_item share-button', 'style': 'margin-left: 5px;', href: '#'}).update('<img src="/static/images/icons/delete.gif?$svn_rev" border="0" align="absbottom">Remove');
        remove_link.observe('click', function (e) {
            Sharing.remove_row(e.target);
            Event.stop(e);
            return false;
        });

        var input_name = input.name;
        var input_value = input.value;
        Sharing.reset_input(input, true); // plus_one=true

        var hidden_input = new Element('input', {'type': 'hidden', 'class': 'textinput collabo', 'name': input_name, 'value': input_value});

        var new_row = new Element('tr');
        var new_td = new Element('td');
        new_td.insert(hidden_input);
        new_row.insert(new_td);
        var new_span = new Element('span').update(input_value.truncate(30));
        new_td.insert(new_span);
        var another_td = new Element('td').update(remove_link);
        new_row.insert(another_td);
        tr.insert({before: new_row});
    },
    postPreviewSubmit : function (vars) {
        $('collab-input').setValue($F('new-collab-input'));
        $('collab-input').addClassName('suggestion-input-unfaded');
        $('collab-input').defaulted = false;
        if ($('new-folder-name')) {
            $('folder_name').setValue($F('new-folder-name'));
            $('folder_name').addClassName('suggestion-input-unfaded');
            $('folder_name').defaulted = false;
        }
        $('hidden-custom-message').setValue($F('invite-message'));
        if (Sharing.is_new) {
            Sharing.doSubmit('share'); // note that keys of this specific dictionary (and any dictionary that goes into doSubmit) cannot contain dashes (-)
        } else {
            $('collab-form').submit();
        }
    },
    previewShare : function (e, deftext, force_fill) {

        Event.stop(e);
        var collabValToFill = $F('collab-input');
        if ($('collab-input').defaulted === true) {
            collabValToFill = '';
        }
        if ($('folder_name')) {
            var nameValToFill = $F('folder_name');
            if ($('folder_name').defaulted === true) {
                nameValToFill = '';
            }
            DomUtil.fillVal(nameValToFill, 'new-folder-name');
        }
        DomUtil.fillVal(collabValToFill, 'cur-invite-recipients');
        DomUtil.fillVal($('hidden-custom-message').value, 'cur-custom-message');

        Modal.show('Share folder', DomUtil.fromElm('preview-share'));
        $('invite-message').focus();
        var ac = new Autocompleter.Contacts("new-collab-input", "choices-whobulk-modal", contacts, lcontacts, {tokens: [',', ';']});
        return false;
    },
    remove_div: function (me) {
        $(me).up('div').remove();
        return false;
    },

    remove_row: function (me) {
        var form = $(me).up('form');
        $(me).up('tr').remove();
        Sharing.renumber(form);
        return false;
    },

    COLLAB_DEFAULT: "Type invitee e-mail addresses here, separated by commas.",
    reset_input: function (elm, plus_one) {
        elm = $(elm);
        elm.name = Sharing.next_name(elm.name, plus_one);
        elm.value = "";
        elm.style.color = '#ccc';
        elm.value = Sharing.COLLAB_DEFAULT;
        elm.onfocus = function () {
            Sharing.clear_input(elm);
        };
        elm.removeClassName('badtextinput');
    },
    clear_input: function (elm, force) {
        elm = $(elm);
        if (elm.value == Sharing.COLLAB_DEFAULT || force) {
            elm.setValue("");
        }
        elm.style.color = '#000000';
        elm.cleared = true;
        elm.isset = true;
    },
    blur: function (elm, value) {
        elm = $(elm);
        if (elm.value.strip() === "") {
            elm.cleared = false;
            if (elm.hasClassName('badtextinput')) {
                elm.removeClassName('badtextinput');
            }
            elm.style.color = '#999';
            elm.value = value;

            elm.isset = false;
        }
    },
    blur_textarea : function (elm, value) {
        elm = $(elm);
        if (elm.value === "") {
            elm.cleared = false;
            elm.style.color = '#999';
            elm.value = value;
        }
    },
    next_name: function (prev_name, plus_one) {
        var num = $('collab-form').getElementsByClassName('collabo').length - 1;
        if (plus_one) {
            num++;
        }
        return "who-" + num;
    },
    noSubmit: function () {
        return false;
    },
    ERROR_MESSAGE: "Please enter collaborator e-mail(s)",
    doSubmit: function (action, path, vars) {
        var non_email_action = action == 'unshare' || action == 'rejoin' || action == 'ignore' || action == 'leave' || action == 'share' || action == 'show';

        Sharing.dropDefaults('collab-form', non_email_action); // clear_all=non_email_action
        if (action)
        {
            if (action == "share_existing") {
                $('collab-form').action = "/share_existing" + path;
            } else if (path) {
                $('collab-form').action = "/share_action/" + action + path;
            } else {
                $('collab-form').action = "/share";
            }
        }
        Sharing.renumber('collab-form');
        Sharing.SUBMITTED = true;

        Sharing.add_vars('collab-form', vars);

        SuggestionInput.do_blank('folder_name');
        SuggestionInput.do_blank('collab-input');
        $('collab-form').submit();
    },
    add_vars: function (form, vars) {
        form = $(form);
        for (var k in vars) {
            form.insert(new Element('input', {'type': 'hidden', 'name': k, 'value': vars[k]}));
        }
    },
    add_collabs: function (form, emails, start_at) {
        start_at = start_at || $$(".collabo").length;

        var d = {};
        for (var i = 0; i < emails.length; i++) {
            var e = emails[i].strip();
            if (e.length) {
                d["who-" + (i + start_at)] = e;
            }
        }
        Sharing.add_vars(form, d);
    },
    turn_bad: function (input, message) {
        input = $(input);
        input.addClassName('badtextinput');
        input.setValue(message);
    },
    dropDefaults: function (form, clear_all) {
        form = $(form);
        var inputs = form.getElementsByClassName('collabo');
        $A(inputs).each(function (x) {
            if (clear_all || x.value == Sharing.COLLAB_DEFAULT || x.value.length === 0) {
                x.up('tr').remove();
            }
        });
    },
    renumber: function (form) {
        form = $(form);
        var inputs = form.getElementsByClassName('collabo');
        for (var i = 0; i < inputs.length; i++) {
            inputs[i].name = "who-" + i;
        }
    },
    del_user: function (who, button, path, options) {
        options = options || {};
        var person = options.self_remove ? "yourself" : who;
        var keep_files = options.keep_files ? "please" : "";
        var message = options.message || "remove " + person;

        if (options.skip_confirm || confirm("Are you sure you want to " + message + "?")) {
            var params = "who=" + encodeURIComponent(who) + "&keep_files=" + keep_files;
            if (options.self_remove) {
                Sharing.doSubmit('leave', {'keep_files': keep_files});
            } else {
                var abdr = new Ajax.DBRequest("/share_ajax/del_user/" + PAGE_PATH + "?" + params, {
                    onSuccess: function (req) {
                        if (options.kill_row) {
                            Sharing.remove_row(button);
                        }
                        if (options.kill_div) {
                            Sharing.remove_div(button);
                        }
                    }
                });
            }
        }
        return false;
    },
    leave: function (who, path) {
        Modal.show("Leave shared folder?", DomUtil.fromElm('leave-confirm'));
    },
    unshare: function (who, path) {
        Modal.show("Unshare folder?", DomUtil.fromElm('unshare-confirm'));
    },
    ignore: function (who, path) {
        Modal.show("Ignore folder?", DomUtil.fromElm('ignore-confirm'));
    },
    cancel_user: function (who, button, path) {
        return Sharing.del_user(who, button, path, {message: "cancel " + who + "'s invite", kill_div: true});
    },
    kick_user: function (name, who, button, path) {
        DomUtil.fillVal(name, 'kick-confirm-victim');
        Modal.show("Kick " + name + " out of folder?", DomUtil.fromElm('kick-confirm'), {button: button, victim: who});
    },
    reinvite_user: function (who, button, path) {
        var params = "action=reinvite_user&who=" + encodeURIComponent(who);
        var adbr = new Ajax.DBRequest("/share_ajax/reinvite_user/" + PAGE_PATH + "?" + params, {
            onSuccess: function (req) {
                Notify.ServerSuccess(who + " was re-invited successfully");
            }
        });
        return false;
    },
    toggle_files: function (link) {
        link = $(link);
        var was_hidden = link.text == "Show files...";
        link.update(was_hidden ? "Hide files" : "Show files...");
        var the_iframe = Util.scry('share-file-frame');
        var e = new Effect.toggle(the_iframe, 'blind', {
            duration: 0.5,
            afterFinish: function () {
                Util.scry('share-file-frame').style.height = '';
            }
        });
        return false;
    },
    pageLeaveCheck: function (event) {
        event = event || window.event;
        if (!Sharing.SUBMITTED && $('collab-form')) {
            var items = $('collab-form').getElementsByClassName('collabo');
            if (items.length === 0 || (items.length == 1 && ($F(items[0]) == Sharing.COLLAB_DEFAULT || $F(items[0]).strip() === ''))) {
                return;
            }

            var m = "You need to click the \"" + $F('share-invite-button') + "\" button to finalize your list and send the invites.";
            event.returnValue = m;
            return m;
        }
    }
};

/*global TreeView, FileQueue*/
var Upload = {
    SWFU: false,
    init: function (late_game) {
        var i = {};
        i[Constants.tcn] = Upload.touch;
        var f = Upload.initSWFU(i);
        if (!late_game) {
            Event.observe(window, 'load', f);
        } else {
            f(); // run it now!
        }

        Upload.operaHack();

        if (!late_game) {
            Upload.checkForFallback.delay(Util.linux_ff3 ? 0 : 5); // if we haven't loaded the flash in 5 seconds, give up
        }
    },
    initSWFU: function (pp) {
        return function () {
            var swf_upload_control = new SWFUpload({
                // Backend settings
                upload_url: "http://" + Constants.BLOCK_CLUSTER + "/upload",
                file_post_name: "file",

                // Flash file settings
                file_size_limit : "307200",
                file_types : "*",
                file_types_description : "All Files",
                file_upload_limit : "0", // Even though I only want one file I want the user to be able to try again if an upload fails

                // Button settings
                button_placeholder_id : "spanButtonPlaceholder",
                button_window_mode: SWFUpload.WINDOW_MODE.TRANSPARENT,
                button_width: 125,
                button_height: 20,
                button_image_url: Util.linux_ff3 ? "/static/images/upload_button.gif" : "",
                button_text:  Util.linux_ff3 ? "<span class='flash-button'>Select files...</span>" : "",
                button_text_style: ".flash-button {color: #ffffff; font-size: 11pt;" +
                                                   'font-family: "lucida grande","lucida sans unicode",tahoma,verdana,arial,sans-serif;' +
                                                   "text-align: center; line-height: 16px;}",

                // Event handler settings
                swfupload_loaded_handler : Upload.flashLoaded,

                file_dialog_start_handler : FileQueue.chooseFiles,
                file_queued_handler : Upload.fileQueued,
                file_queue_error_handler : Upload.fileQueueError,
                file_dialog_complete_handler : Upload.fileDialogComplete,

                //upload_start_handler : Upload.uploadStart,
                upload_progress_handler : Upload.uploadProgress,
                upload_error_handler : Upload.uploadError,
                upload_success_handler : Upload.uploadSuccess,
                upload_complete_handler : Upload.uploadComplete,

                // Flash Settings
                flash_url : "/static/swf/swfupload.swf",

                custom_settings : {
                    progress_target : "fsUploadProgress",
                    upload_successful : false
                },

                post_params: pp,
                debug: Constants.upload_debug || false
            });
            Upload.SWFU = swf_upload_control;
        };
    },
    updatePostParams: function (dict) {
        var pp = Upload.SWFU.getSetting("post_params");
        for (var key in dict) {
            pp[key] = dict[key];
        }

        Upload.SWFU.setPostParams(pp);
    },
    fileBrowse: function () {
        Upload.SWFU.cancelUpload();
        Upload.SWFU.selectFiles();
    },
    fileQueueError: function (fileObj, error_code, message)  {
        try {
            // Handle this error separately because we don't want to create a FileProgress element for it.
            switch (error_code) {
            case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:
                alert("Msg! Syntax");
                break;
            case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:
                alert("Msg! Syntax");
                break;
            case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE:
                alert("Msg! Syntax");
                break;
            default:
                alert("Msg! Syntax");
                this.debug("Error Code: " + error_code +
                           ", File name: " + fileObj.name +
                           ", File size: " + fileObj.size +
                           ", Message: " + message);
                break;
            }
        } catch (e) {}
    },
    fileQueued: function (fileObj) {
        FileQueue.push(fileObj);
    },

    fileDialogComplete: function (num_files_selected) {
    },

    uploadNext: function () {
        Upload.SWFU.startUpload();
    },

    pause: function () {
        Upload.SWFU.stopUpload();
    },

    uploadProgress: function (fileObj, bytesLoaded, bytesTotal) {
        FileQueue.update(fileObj, bytesLoaded, bytesTotal);
        Modal.confirm_close = true;
    },

    uploadSuccess: function (fileObj, server_data) {
        Modal.confirm_close = false;
        if (server_data == " ") {
            FileQueue.errored(fileObj);
            Notify.ServerError();
        } else if (server_data == "quota") {
            FileQueue.errored(fileObj);
            Notify.ServerError("Your upload failed because you are over quota.");
        } else {
            FileQueue.update(fileObj, "done");
        }
    },

    uploadComplete: function (fileObj) {
        Modal.refresh_after_close = true;
        FileQueue.completed(fileObj);

        if (FileQueue.empty()) {
            FileQueue.doneUploading();
        } else {
            Upload.uploadNext();
        }
    },

    uploadError: function (fileObj, error_code, message) {
        var file = fileObj;
        Modal.confirm_close = false;
        try {
            var txtFileName = document.getElementById("txtFileName");
            txtFileName.value = "";


            // Handle this error separately because we don't want to create a FileProgress element for it.
            switch (error_code) {
            case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL:
                alert("There was a configuration error.  You will not be able to upload a resume at this time.");
                this.debug("Error Code: No backend file, File name: " + file.name + ", Message: " + message);
                return;
            case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:
                alert("You may only upload 1 file.");
                this.debug("Error Code: Upload Limit Exceeded, File name: " + file.name +
                           ", File size: " + file.size +
                           ", Message: " + message);
                return;
            case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
            case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
                break;
            default:
                alert("An error occurred in the upload. Try again later.");
                this.debug("Error Code: " + error_code +
                           ", File name: " + file.name +
                           ", File size: " + file.size +
                           ", Message: " + message);
                return;
            }

            fileObj.id = "singlefile";  // This makes it so FileProgress only makes a single UI element, instead of one for each file
            var progress = new FileProgress(fileObj, this.customSettings.progress_target);
            progress.SetError();
            progress.ToggleCancel(false);

            switch (error_code) {
            case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
                progress.SetStatus("Upload Error");
                this.debug("Error Code: HTTP Error, File name: " + file.name +
                           ", Message: " + message);
                break;
            case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:
                progress.SetStatus("Upload Failed.");
                this.debug("Error Code: Upload Failed, File name: " + file.name +
                           ", File size: " + file.size + ", Message: " + message);
                break;
            case SWFUpload.UPLOAD_ERROR.IO_ERROR:
                progress.SetStatus("Server (IO) Error");
                this.debug("Error Code: IO Error, File name: " + file.name +
                           ", Message: " + message);
                break;
            case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
                progress.SetStatus("Security Error");
                this.debug("Error Code: Security Error, File name: " + file.name +
                           ", Message: " + message);
                break;
            case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
                progress.SetStatus("Upload Cancelled");
                this.debug("Error Code: Upload Cancelled, File name: " + file.name +
                           ", Message: " + message);
                break;
            case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
                progress.SetStatus("Upload Stopped");
                this.debug("Error Code: Upload Stopped, File name: " + file.name +
                           ", Message: " + message);
                break;
            }
        } catch (e) {}

        if (FileQueue.lastOne()) {
            FileQueue.doneUploading();
        }

        FileQueue.errored(fileObj);
    },
    grabURL: function () {
        var path = $F('file-box');
        if (/(^http|^https|^ftp):\/\//.match(path)) {
            $('url').value = path;
        }
        return true;
    },
    set_dest: function (path) {
        var path_parts = path.split("/");
        var folder_name = path_parts[path_parts.length - 1];
        if (!folder_name.length) {
            folder_name = "My AccessManager";
            path = "/";
        }

        $('dest-folder-text').update(folder_name);
        $('dest-folder').value = path;

        var basic_link = Util.scry('basic-uploader-url');
        if (basic_link) {
            basic_link.href = basic_link.href.replace(/(\/upload)(.*)(\?basic=1)/,
                function (s, u, f, b) {
                    return u + Util.urlquote(path) + b;
                });
        }
    },
    treeview_handler: function (path, obj) {
        Upload.set_dest(path);
        FileQueue.clear();
    },
    new_folder: function () {
        TreeView.hide();
        Modal.show("Create new folder...", DomUtil.fromElm('create-folder'), {'action': Upload.do_new_folder});
        if (!Util.ie) {
            $('first-treeview-link').onclick();
        }
    },
    do_new_folder: function () {
        if (!Modal.vars.selected_path) {
            Notify.ServerError("Please select a parent folder.");
            return;
        }

        var to = $F('entered-name');
        var straight_from = decodeURIComponent(Modal.vars.selected_path);
        var from = Util.urlquote(straight_from);

        Util.setCursor('progress');
        var adbr = new Ajax.DBRequest("/cmd/new" + from + "?to_path=" + to, {
            onSuccess: function (req) {
                Upload.treeview_handler(Util.normPath(straight_from) + "/" + to);
                TreeView.reset();
            },
            cleanUp: function () {
                Util.clearCursor();
            }
        });
    },
    flashLoaded: function () {
        Upload.flash_loaded = true;
        $('upload-loading').hide();
        $('upload-buttons').show();
    },
    checkForFallback: function () {
        if (!Upload.flash_loaded) {
            location.replace("/upload?basic=1");
        } else {
            clearInterval(Upload.opera_tid);
        }
    },
    operaHack: function () {
        if (Prototype.Browser.Opera) {
            Upload.opera_tid = setInterval(function () {
                $('opera-dummy-div').toggle();
            }, 200);
        }
    }
};

/*global Sprite*/
var TreeView = {
    tv: {},
    set_params: function (params) {
        TreeView.ajax_params = params;
    },
    init: function (h, autohide, tree_id) {
        tree_id = tree_id || 'treeview';
        TreeView.tv[tree_id] = {};
        var treeview = TreeView.tv[tree_id];

        treeview.autohide = autohide === null ? true : autohide;
        treeview.handler = h;
        treeview.viewdiv = $(tree_id);
        treeview.hidefunc = TreeView.hide.bindAsEventListener(this);
    },
    reset: function () {
        var dbr = new Ajax.DBRequest("/ajax_subtreeview", {
            parameters: TreeView.ajax_params,
            onSuccess: function (req) {
                for (var tree_id in TreeView.tv) {
                    TreeView.tv[tree_id].viewdiv.down(".treeview-folders").update(req.responseText);
                }
            }
        });
    },
    toggle: function (e, tree_id) {
        Event.stop(e);
        var treeview = TreeView.tv[tree_id || 'treeview'];

        if (treeview.shown) {
            treeview.shown = false;
            TreeView.hide(e, tree_id);
        } else {
            treeview.shown = true;
            TreeView.show(e.target, tree_id);
        }
        return false;
    },
    hide: function (e, tree_id) {
        var treeview = TreeView.tv[tree_id || 'treeview'];
        if (!e || !$(e.target).descendantOf(treeview.viewdiv)) {
            treeview.viewdiv.hide();
            Event.stopObserving(window, 'click', treeview.hidefunc);
            treeview.shown = false;
        }
    },
    show: function (link, tree_id) {
        var treeview = TreeView.tv[tree_id || 'treeview'];

        link = $(link);
        link.blur();
        var linkPos = link.cumulativeOffset();
        treeview.viewdiv.setStyle({'top': (linkPos.top + link.getHeight()) + "px", 'left': (linkPos.left - 4) + "px"});
        treeview.viewdiv.show();
        Event.observe(window, 'click', treeview.hidefunc);
    },
    toggleNode: function (button) {
        button = $(button);

        var img = button.down('img');
        if (img.className.match("_plus")) {
            Sprite.replace(img, "_plus", "_minus");
        } else {
            Sprite.replace(img, "_minus", "_plus");
        }
        button.up().next('div').toggle();
        button.blur();
        return false;
    },
    toggleNodeAjax: function (button, parent_path) {
        if (button.fetched_children) {
            return TreeView.toggleNode(button);
        }
        button = $(button);
        var img = button.down('img');
        var orig_sprite = Sprite.get(img);
        img.src = "/static/images/icons/ajax-loading-small.gif";

        var dbr = new Ajax.DBRequest("/ajax_subtreeview" + parent_path, {
            parameters: TreeView.ajax_params,
            onSuccess: function (req) {
                var d =  new Element("div", {style: "display: none;"}).update(req.responseText);
                button.up().insert({after: d});
                button.fetched_children = true;

                Sprite.set(img, orig_sprite);
                return TreeView.toggleNode(button);
            },
            cleanUp: function (ok) {
                if (/loading/.match(img.src)) {
                    Sprite.set(img, orig_sprite);
                }
            }
        });

        return false;
    },
    handle: function (path, obj) {
        var tree_ids = $H(TreeView.tv).keys();
        var treeview = $(obj).ancestors().find(function (div) {
            return tree_ids.include(div.id);
        });
        if (!treeview) {
            return;
        }

        treeview = TreeView.tv[treeview.id];
        if (treeview.handler) {
            treeview.handler(path, obj);
        }
        if (treeview.autohide) {
            TreeView.hide(treeview.id);
        }
    }
};

var ProgressBar = {
    MAGIC: 42,
    make: function (id, width, start_text) {
        width = width || 200;
        var wstr = width.toString() + "px";
        start_text = typeof(start_text) != 'undefined' ? start_text :"0%";
        var outer = new Element('div', {'class': 'outer-progress-bar', 'style': 'width: ' + wstr});
        var inner = new Element('div', {'class': 'inner-progress-bar', 'id': "pb_" + id, 'style': 'width: ' + wstr});
        var under = new Element('div', {'class': 'under-pb progress-bar', 'style': 'width: ' + wstr});
        var over  = new Element('div', {'style': 'display: none', 'class': 'over-pb progress-bar', 'id': "pb_" + id + "_over"});
        var upct  = new Element('div', {'class': 'pb-percentage', 'id': "pb_" + id + "_upct", 'style': 'width: ' + wstr});
        upct.update(start_text);
        var opct  = new Element('div', {'class': 'pb-percentage', 'id': "pb_" + id + "_opct", 'style': 'width: ' + wstr});
        opct.update(start_text);

        under.insert(upct);
        over.insert(opct);
        inner.insert(under);
        inner.insert(over);
        outer.insert(inner);

        over.progress_width = width;
        return outer;
    },
    reset: function (id) {
        ProgressBar.set(id, 0);
    },
    set: function (id, frac, text) {
        frac = Math.min(frac, 1);
        text = typeof(text) != 'undefined' && text !== false ? text : Math.floor(frac * 100).toString() + "%";

        var over = $("pb_" + id + "_over");
        if (!over) {
            return;
        }

        var wideness = over.progress_width * frac;
        over.show();
        over.makeClipping().setStyle({'width': wideness.toString() + 'px', backgroundColor: '#3a93d2'});
        $("pb_" + id + "_upct").innerHTML = text;
        $("pb_" + id + "_opct").innerHTML = text;
    },
    errorState: function (id) {
        var over = $("pb_" + id + "_over");
        if (!over) {
            return;
        }

        var wideness = over.progress_width;
        over.show();
        over.makeClipping().setStyle({'width': wideness.toString() + 'px', backgroundColor: '#d23a3a'});
        $("pb_" + id + "_upct").innerHTML = "Error";
        $("pb_" + id + "_opct").innerHTML = "Error";
    }
};

var ModalProgress = {
    show: function (text, cover_this) {
        if (!text) {
            return;
        }
        cover_this = $(cover_this || 'browse-box');

        var loadingDiv = $('modal-progress-overlay');
        loadingDiv.clonePosition(cover_this);

        if (!loadingDiv.getWidth()) {
            return;
        }

        $('modal-progress-text').update(text);
        $('modal-progress-bar').setOpacity(1);
        $('modal-progress-bar').update(ProgressBar.make("modal-progress", 150, ""));

        // keep the modal progress bar >= 120px from the top of the screen (and visible)
        $('modal-progress-content').style.top = (Math.max(0, Util.scrollTop() - cover_this.cumulativeOffset().top) + 120) + "px";

        Effect.Appear(loadingDiv, {to: 0.7, duration: 0.25 });
        Effect.Appear('modal-progress-content', {duration: 0.25 });
    },
    update: function (progress) {
        if (progress.indexOf("/") > 0) {
            var parts = progress.split("/");
            progress = Number(parts[0]) / Number(parts[1]);
        }
        if (progress) {
            ProgressBar.set("modal-progress", progress, "");
        }
    },
    hide: function () {
        Effect.Fade('modal-progress-overlay', { duration: 0.25 });
        Effect.Fade('modal-progress-content', { duration: 0.25 });
    }
};

var FileQueue = {
    fileRows: {},
    fileProgress: {},
    uploading: false,
    toUpload: 0,
    completed_files: {},
    empty: function () {
        return !FileQueue.toUpload;
    },
    numShown: function () {
        return $H(FileQueue.fileRows).keys().length;
    },
    lastOne: function () {
        return 1 == FileQueue.toUpload;
    },
    push: function (fileObj) {
        var name = fileObj.name;
        var id = fileObj.id;

        FileQueue.fileProgress[id] = ProgressBar.make(id, 200, ""); // width=200, start_text=""

        var row = new Element("tr");
        var ntd = new Element("td", {'width': 125});
        var ndiv = new Element("div", {'style': "float:left;overflow:hidden;width:125px;"});
        ndiv.update(name.snippet(20));
        ntd.insert(ndiv);
        var ptd = new Element("td", {'width': 200});
        ptd.insert(FileQueue.fileProgress[id]);
        var rtd = new Element("td", {'width': 75});

        FileQueue.fileRows[id] = row;
        $('file-queue').insert(row);
        row.update(ntd);
        row.insert(ptd);
        row.insert(rtd);
        var rmvb = FileQueue.removeButton(id);
        rtd.insert(rmvb);

        $('file-queue-table').show();
        FileQueue.toUpload++;
    },
    removeButton: function (id) {
        var link = new Element("a", {'class': 'panel', 'href': '#', 'style': "width:75px; display: block;"});
        link.onclick = function () {
            FileQueue.remove(id);
        };
        link.update('<img src="/static/images/icons/delete.gif" align="absbottom" border="0">&nbsp;Remove');
        return link;
    },
    remove: function (id) {
        Upload.SWFU.cancelUpload(id);
        FileQueue.fileRows[id].remove();
        if (!FileQueue.completed_files[id]) {
            FileQueue.toUpload = Math.max(0, FileQueue.toUpload - 1);
        }
        delete FileQueue.fileRows[id];
        delete FileQueue.fileProgress[id];

        if (FileQueue.numShown() === 0) {
            $('file-queue-table').hide();
            FileQueue.doneUploading();
        }
    },
    update: function (fileObj, progress, max) {
        var text = false;

        if (progress == "done") {
            text = "Done";
            progress = max = 1;
        } else if (progress / max == 1) {
            text = "Saving...";
        }

        ProgressBar.set(fileObj.id, progress / max, text);
    },
    errored: function (fileObj) {
        ProgressBar.errorState(fileObj.id);
        FileQueue.doneUploading();
    },
    completed: function (fileObj) {
        FileQueue.toUpload = Math.max(0, FileQueue.toUpload - 1);
        FileQueue.completed_files[fileObj.id] = true;

        var row = FileQueue.fileRows[fileObj.id];
        if (row) {
            row.down("a").update('<img src="/static/images/icons/delete.gif" align="absbottom" border="0">&nbsp;Hide');
        }
    },
    clear : function (clear_all) {
        for (var id in FileQueue.fileRows) {
            if (clear_all || FileQueue.completed_files[id]) {
                FileQueue.remove(id);
                Upload.SWFU.cancelUpload(id);
            }
        }
    },
    chooseFiles: function (button) {
        button = $('choose-button');
        if (button.value.match("Choose")) {
            button.value = button.value.replace("Choose", "Add more");
        }
        FileQueue.colorButtons('upload-button');
        return false;
    },
    uploadFiles: function () {
        if (!FileQueue.toUpload) {
            return;
        }

        Upload.updatePostParams({'dest': $F('dest-folder'), 't': Constants.TOKEN});

        if (!FileQueue.uploading) {
            Upload.uploadNext();
            $('upload-button').value = "Uploading...";
            FileQueue.uploading = true;
        } else {
            /* don't pause...
             Upload.pause();
             $('upload-button').value = "Start Upload";
             FileQueue.uploading = false
             */
        }
    },
    colorButtons: function (primary) {
        $A(['choose-button', 'upload-button']).each(function (button) {
            if (button == primary) {
                $(button).removeClassName('grayed');
            } else {
                $(button).addClassName('grayed');
            }
        });
    },
    doneUploading: function () {
        FileQueue.colorButtons('choose-button');
        $('upload-button').value = "Start Upload";
        FileQueue.uploading = false;
    }
};

var Platform = {
    Windows: navigator.userAgent.indexOf("Win") > -1,
    XP: navigator.userAgent.indexOf("Windows NT 5") > -1,
    Vista: navigator.userAgent.indexOf("Windows NT 6") > -1,
    Mac: navigator.appVersion.indexOf("Mac") > -1
};


var Welcome = {
    clear_placeholder: function (elt)
    {
        if (elt.value == "you@example.com") {
            elt.value = "";
        }

        $("submit").disabled = false;
    }
};

var Revisions = {
    confirm_purge: function (name) {
        DomUtil.fillVal(name, "purge-victim");
        Modal.show("Purge file?", DomUtil.fromElm('purge-confirm'));
        return false;
    },
    purge: function () {
        $('purge-form').submit();
    }
};

var DomUtil = {
    fromElm: function (elm) {
        return $(elm).innerHTML;
        //return $(elm).childElements().invoke('cloneNode', true); //better, but skips text nodes
    },
    fillVal: function (val, className) {
        $$("." + className).each(
            function (x) {
                x = $(x);
                if (x.tagName == 'INPUT') {
                    x.value = val;
                    x.defaultValue = val;
                } else {
                    x.innerHTML = val;
                }
            });
    }
};

/*global Browse, ActAsBlock*/
var Modal = {
    show: function (title, content, vars, focus, width) {
        Modal.vars = vars || {};
        var icon = Modal.vars.icon || 'help';
        width = width || 500;

        var modal_top = (document.viewport.getScrollOffsets().top + 150);
        Util.scry('modal').setStyle({top: modal_top + "px"});
        Util.scry('modal').setStyle({width: width + "px", margin: "0 0 0 " + Math.floor(-width / 2).toString() + "px"});

        // With Icon - $('modal-title').innerHTML = ' <img align="absmiddle" src="/static/images/icons/' + icon + '.gif" style="margin-top: -4px">&nbsp;' + title;
        Util.scry('modal-title').innerHTML = title;

        var first_kid = Util.childElement($('modal-content'), 0);
        if (first_kid && first_kid != content) {
            $('grave-yard').insert(first_kid);
        }

        var content_div = new Element('div');
        content_div.update(content);
        Util.scry('modal-content').insert(content_div);
        Util.scry('modal-overlay').setOpacity(0.6);
        Util.scry('modal-overlay').show();
        if (content.show) {
            content.show();
        }

        Element.show('modal');
        Util.scry('modal-behind').setStyle({height: (Util.scry('modal').getHeight() + 20) + "px",
                                            width: (Util.scry('modal').getWidth() + 20) + "px",
                                            margin: "0 0 0 " + Math.floor(-width / 2 - 10).toString() + "px",
                                            top: (modal_top - 10) + "px"});
        Util.scry('modal-behind').setOpacity(0.2);
        Util.scry('modal-behind').show();

        if (focus) {
            // We have
            $('modal-content').select("#" + focus.id).first().focus();
        } else {
            if (!Util.ie) {
                var firstButton = Util.scry('modal').down('input[type=button]') || Util.scry('modal').down('input[type=submit]');
                if (firstButton) {
                    firstButton.focus();
                }
            }
        }
        ActAsBlock.register(false, "modal");

    },
    shown: function () {
        return Util.scry('modal').visible();
    },
    hide: function (e) {
        if (e) {
            Event.stop(e);
        }

        if (Modal.confirm_close) {
            if (!confirm("Leaving this box may cut your operation short.\n\nAre you sure you want to?")) {
                return;
            }
            Modal.confirm_close = false;
        }

        Element.hide('modal-behind');
        Element.hide('modal-overlay');
        Element.hide('modal');

        if (Modal.track_id) {
            clearInterval(Modal.track_id);
            Modal.track_id = false;
        }

        if (Modal.refresh_after_close && Browse.files && Browse.files.length) {
            Browse.reload();
            Modal.refresh_after_close = false;
        }
    },
    track_resizes: function () {
        Modal.track_id = setInterval(Modal.resize_bg, 150);
    },
    resize_bg: function () {
        var h = Util.scry('modal').getHeight();
        if (Modal.old_height != h) {
            Modal.old_height = h;
            Util.scry('modal-behind').setStyle({height: (h + 20) + "px"});
        }
    },
    vars: {}
};

var Tabs = {
    init: function ()
    {
        var tabs_li = document.getElementsByClassName("subtab");
        for (var i = 0;i < tabs_li.length; i++)
        {
            var a = tabs_li[i].down();
            var url = a.href.split("/");
            a.href = "#" + url[url.length - 1];
        }
    },

    check_url: function (defaulttab)
    {
        var current = Util.url_hash();
        if (Tabs.last_shown == current) {
            return;
        }

        Tabs.last_shown = current;
        if (Util.url_hash()) {
            Tabs.showTab(Util.url_hash() + '-tab', Util.url_hash());
        } else {
            Tabs.showTab(defaulttab + '-tab', defaulttab);
        }
    },

    showTab: function (elm, tab)
    {
        //  Get element and remove the highlight from the link
        elm = $(elm);

        // Set all the tabs to not selected
        var tab_list = document.getElementsByClassName("subtab");
        var i;
        for (i = 0; i < tab_list.length; i++) {
            tab_list[i].removeClassName("selected");
        }

        // Set all the content to be hidden
        var content_tabs = document.getElementsByClassName("content-tab");

        for (i = 0; i < content_tabs.length; i++) {
            content_tabs[i].hide();
        }

        // Show the selected content
        var sTab = $(tab + "-tab") || $$(".subtab").first();
        var sContent = $(tab + "-content") || $$(".content-tab").first();
        if (sTab) {
            sTab.addClassName("selected");
        }
        if (sContent) {
            sContent.show();
            Util.syncHeight();
            var inputs = sContent.select('input[type=text]', 'textarea');
            if (inputs) {
                Util.focus(inputs[0]); // focus on the first input area on the tab
            }
        }
    }
};

var Hosts = {
    edit: function (id) {
        var elm = $("host" + id);
        if (elm.editing) {
            return;
        }

        elm.editing = true;
        var content = elm.innerHTML;
        elm.previous = content;

        elm.innerHTML = "<input type='text' class='skinny-input' size='20' maxlength='256' style=\"word-wrap: break-word;\" value=\"" + content.escapeHTML().gsub('"', '&quot;') + "\">&nbsp;" +
            "<input type='button' onclick='Hosts.doneEditing(\"" + id + "\");' class='button' value='Save'>&nbsp;" +
            "<input type='button' onclick='Hosts.cancelEditing(\"" + id + "\");' class='button grayed' value='Cancel'>";
        var i = elm.down('input');
        Event.observe(i, 'keydown', Hosts.checkKey(id));
        i.select();
        return false;
    },
    doneEditing: function (id) {
        var elm = $("host" + id);
        var newName = elm.down('input').value;

        var dbr = new Ajax.DBRequest("/computer_edit?host_id=" + id + "&name=" + newName, {
            onSuccess: function (req) {
                Hosts.unedit(elm, req.responseText);
            }
        });
    },
    cancelEditing: function (id) {
        var elm = $("host" + id);
        Hosts.unedit(elm, elm.previous);
    },
    unedit: function (elm, value) {
        elm.editing = false;
        elm.innerHTML = value;
    },
    unlink: function (id, name, plat) {
        DomUtil.fillVal(name, 'unlink-confirm-name');
        Modal.show("Unlink computer?", DomUtil.fromElm('unlink-confirm'), {host_id: id, plat: plat});
    },
    doUnlink: function (id, plat) {
        var dbr = new Ajax.DBRequest("/computer_edit?host_id=" + id + "&unlink=yessir", {
            onSuccess: function (req) {
                Hosts.killRow(id);
                Hosts.dec_count(plat);
            }
        });
    },
    dec_count: function (plat) {
        var count_div = $(plat + "-count");
        if (!count_div) {
            return;
        }

        var parts = count_div.innerHTML.split(" ");
        var count = parseInt(parts.shift(), 10);
        var desc = parts.join(" ");

        if (!count) {
            return;
        }

        count--;
        if (count == 1 && desc.charAt(desc.length - 1) == 's') {
            desc = desc.substr(0, desc.length - 1);
        } else if (count != 1 && desc.charAt(desc.length - 1) != 's') {
            desc = desc + "s";
        }

        count_div.innerHTML = count.toString() + " " + desc;
    },
    killRow: function (id) {
        var table = $("host" + id).up("table");
        $("host" + id).up("tr").remove();
        if (Hosts.rowCount() === 0) {
            var row = new Element("tr");
            var td = new Element("td", {colspan: 4});
            td.innerHTML = "<center>You no longer have any hosts linked.</center>";
            table.insert(row);
            row.insert(td);
        }
    },
    rowCount: function () {
        return $$(".host-row").length;
    },
    checkKey: function (id) {
        return function (e) {
            e = e || window.event;

            if (e.keyCode == Event.KEY_RETURN) {
                Hosts.doneEditing(id);
            }
            if (e.keyCode == Event.KEY_ESC) {
                Hosts.cancelEditing(id);
            }
        };
    }
};


Autocompleter.Contacts = Class.create(Autocompleter.Base, {
    initialize: function (element, update, array, larray, options) {
        this.baseInitialize(element, update, options);
        this.options.array = array;
        this.options.larray = larray;
    },

    getUpdatedChoices: function () {
        this.updateChoices(this.options.selector(this));
    },

    setOptions: function (options) {
        this.options = Object.extend({
            choices: 5,
            selector: function (instance) {
                var ret       = []; // Beginning matches
                var entry     = instance.getToken().toLowerCase();

                var l = instance.options.array.length;
                var c = instance.options.choices;
                var a = instance.options.array;
                var la = instance.options.larray;

                for (var i = 0; i < l && ret.length < c; i++) {
                    var regex_parts = [];
                    if (entry.indexOf(" ") == -1) {
                        regex_parts.push("\\s+");
                    }
                    if (entry.indexOf("+") == -1) {
                        regex_parts.push("\\+");
                    }
                    if (entry.indexOf("@") == -1) {
                        regex_parts.push("@");
                    }
                    if (entry.indexOf(".") == -1) {
                        regex_parts.push("\\.");
                    }
                    if (entry.indexOf("&lt;") == -1) {
                        regex_parts.push("&lt;");
                    }
                    var regex = RegExp("(" + regex_parts.join("|") + ")");
                    var elem = a[i];
                    var lelm = la[i];
                    var parts = regex_parts.length ? lelm.split(regex) : [lelm];

                    var foundPos = 0;
                    var pl = parts.length;
                    for (var p = 0; p < pl; p++) {
                        if (!parts[p]) {
                            continue;
                        }
                        if (parts[p].indexOf(entry) === 0) {
                            ret.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                            elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                            foundPos + entry.length) + "</li>");
                            break;
                        }
                        foundPos += parts[p].length;
                    }
                }
                return "<ul>" + ret.join('') + "</ul>";
            }
        }, options || { });
    },
    selectEntry: function () {

        var entry = this.getCurrentEntry();
        var curText = entry.innerHTML;

        // find the email address in between the brackets if they're in there
        var delims = [["&gt;", "&lt;"], [">", "&lt;"]]; // the latter is for safari on the mac. don't ask why
        for (var i = 0; i < delims.length; i++) {
            var gtlt = delims[i];
            var gtPos = curText.lastIndexOf(gtlt[0]);
            if (gtPos == curText.length - gtlt[0].length) {
                var ltPos = curText.lastIndexOf(gtlt[1]);
                if (ltPos != -1) {
                    entry.innerHTML = curText.substr(ltPos + gtlt[1].length, gtPos - ltPos - gtlt[1].length);
                }
            }
        }

        // if we have a lot of tokens, choose one to use as the separator and auto-add it
        var tok = this.options.tokens.length > 1 ? this.options.tokens[0] + " ": "";
        entry.innerHTML += tok;

        this.active = false;
        this.updateElement(entry);
    }
});

var LeftNavBox = {
    close: function (ex) {
        var t = $(ex).up().up('div');
        var d = 1.0;
        var ef;
        ef = new Effect.BlindUp(t, {duration: d});
        ef = new Effect.Fade(t, {duration: d});
        return false;
    }
};

var ThumbVote = {
    decline: function (me) {
        var dbr = new Ajax.DBRequest("/thumbs?declined=ajax");
    }
};

var Flash = {
    show: function (message) {
        var container = new Element("div", {"id": "flash-container"});
        var actual_flash = new Element("p", {"id": "flash"});

        actual_flash.update(message);
        container.update(actual_flash);

        $("notice-container").update(container);
        Flash.scheduleHide();
    },

    scheduleHide: function () {
        if (Flash.hideTimer) {
            Flash.cancelHide();
        }
        Flash.hideTimer = setTimeout(Flash.hide, 10000);
    },

    cancelHide: function () {
        clearTimeout(Flash.hideTimer);
    },

    hide: function () {
        var ef = new Effect.BlindFadeUp("flash-container");
    }
};

var Invitations = {
    submit: function (e) {
        e = e || window.event;

        if (e.keyCode == Event.KEY_RETURN) {
            Invitations.send();
        }
    },
    send: function () {
        var recip = $F('invite-recip');
        if (recip != $('invite-recip').title) {
            var req = new Ajax.DBRequest("/send_invite?emails=" + encodeURIComponent(recip), {
                onSuccess: function (req) {
                    Flash.show(req.responseText.substr(5));

                },
                onFailure: function (req) {
                    Flash.show(req.responseText.substr(4));
                },
                noAutonotify: true
            });
        } else {
            Flash.show("Please enter an email address.");
        }
        return false;
    },
    clearInput : function () {
        if ($('invite-recip').value == "E-mail address") {
            $('invite-recip').value = "";
            $('invite-recip').setStyle({"color" : ""});
        }
    }
};

var Feed = {
    firstTime: true,
    showCommentBox: function (on, h) {
        if (Feed.firstTime) {
            $('comment').observe('keydown', Feed.onEnterGo);
            Feed.firstTime = false;
        }

        if ($('comment-row').style.display == 'none') {
            Effect.BlindFadeDown('comment-row', {duration: 0.5,
                afterFinish: function () {
                    $('comment-row').style.height = '';
                    $('comment').focus();
                }
            });
        } else {
            $('comment').focus();
        }

        /*
         $('comment-h').value = h ? h : "";
         if (on) {
         $('comment-on').update(" on " + on);
         $('comment-on').show();
         } else {
         $('comment-on').hide();
         }
         Feed.enableCommentables();
        */
        return false;
    },
    hideCommentBox: function () {
        Effect.BlindFadeUp('comment-row', {duration: 0.5});
        $('comment').value = "";
        Feed.disableCommentables();
    },
    COMMENT_DEFAULT: "Add a comment to the Recent Events feed",
    addComment: function () {
        if (!$F('comment').length || $('comment').value == Feed.COMMENT_DEFAULT) {
            return false;
        }

        var dbr = new Ajax.DBRequest("/share_ajax/add_comment", {
            parameters: {'comment': $F('comment'), 'ns_id': $F('comment-ns_id')},
            onSuccess: function (req) {
                Feed.addCommentRow(req.responseText);
                $('comment').value = "";
            }
        });
        return false;
    },
    addCommentRow: function (blurb) {
        var tr = new Element("tr");
        var td1 = new Element("td", {'valign': 'top', 'class': 'note'});
        var icon = new Element("img", {'src': '/static/images/icons/comment.gif'});
        td1.insert(icon);
        var td2 = new Element("td", {'valign': 'top', 'class': 'note'});
        td2.innerHTML = blurb;
        var td3 = new Element("td", {'valign': 'top', 'class': 'note', 'width': '100', 'nowrap': 'nowrap', 'align': 'right'});
        td3.innerHTML = "(just added)";
        tr.update(td1);
        tr.insert(td2);
        tr.insert(td3);

        $('event-table').down('tr').insert({before: tr});
    },
    commentablesEnabled: false,
    enableCommentables: function () {
        $$('.add_comment_icon').invoke('show');
        Feed.commentablesEnabled = true;
    },
    disableCommentables: function () {
        $$('.add_comment_icon').invoke('hide');
        Feed.commentablesEnabled = true;
    },
    mouseover: function (text_elm) {
        if (!Feed.commentablesEnabled) {
            return;
        }
        $(text_elm).show();
    },
    mouseout: function (text_elm) {
        if (!Feed.commentablesEnabled) {
            return;
        }
        $(text_elm).hide();
    },
    onEnterGo: function (e) {
        if (e.keyCode == Event.KEY_RETURN) {
            Feed.addComment();
        }
    },


    feedPages: [],
    currentPage: 0,
    maxPage: 0,
    ns_id: false,
    goBack: function () {
        if (Feed.currentPage === 0) {
            return;
        }
        Feed.showLoading(true);
        Feed.showPage(--Feed.currentPage);
        Feed.hideLoading();

        return false;
    },
    goForward: function () {
        if (Feed.currentPage === 0 && !Feed.feedPages.length) {
            Feed.feedPages.push(Feed.getCurrentPage());
        }

        if (Feed.currentPage < Feed.maxPage) {
            Feed.showLoading(true);
            Feed.showPage(++Feed.currentPage);
            Feed.hideLoading();
            return false;
        } else {
            var ns_str = Feed.ns_id ? "&ns_id=" + Feed.ns_id.toString() : "&is_home=yes";

            Feed.showLoading();
            var dbr = new Ajax.DBRequest("/next_events?cur_page=" + (Feed.currentPage + 1).toString() + ns_str, {
                onSuccess: function (req) {
                    Feed.feedPages.push(req.responseText);
                    Feed.showPage(++Feed.currentPage);
                    Feed.maxPage = Feed.currentPage;
                }
            });
        }
        return false;
    },
    getCurrentPage: function () {
        return $('event-table').innerHTML;
    },
    showLoading: function (no_text, cover_this, just_icon) {
        just_icon = true;
        cover_this = cover_this || $('event-table').up('table');
        var loadingDiv = $('feed-loading');
        loadingDiv.clonePosition(cover_this);

        if (loadingDiv.getWidth() === 0) {
            return;
        }

        loadingDiv.setOpacity(0.9);
        if (no_text) {
            $('feed-loading-text').update();
        } else {
            $('feed-loading-text').update("<img src='/static/images/icons/ajax-loading.gif' style='vertical-align: bottom;'/>" + (just_icon ? "" : "Loading..."));
        }
        $('feed-loading').show();
    },
    hideLoading: function () {
        $('feed-loading').hide();
    },
    showPage: function (num) {
        Feed.hideLoading();
        $('event-table').update(Feed.feedPages[num]);
        Feed.updateLinks();
    },
    updateLinks: function () {
        var parts = [];
        if (Feed.currentPage > 0) {
            parts.push('<a href="#" onclick="javascript: return Feed.goBack();">&laquo;&nbsp;back</a>');
        }
        if (Feed.currentPage < Feed.maxPage || !$('more-events').empty()) {
            parts.push('<a href="#" onclick="javascript: return Feed.goForward();">next&nbsp;&raquo;</a>');
        }
        $('event-page-links').update(parts.join("&nbsp;|&nbsp;"));
    },
    reload: function () {
        if (!Feed.lastShown) { // there's no currently loaded feed => nothing to reload
            return;
        }

        Feed.feedPages = [];
        Feed.currentPage = 0;
        Feed.maxPage = 0;
        Feed.ns_id = false;
        Feed.lastShown = 0;
    },

    toggle: function (elm) {
        elm = $(elm);
        var events = $('events-container');

        if (Feed.hidden === undefined) {
            Feed.hidden = (events.style.display == "none");
        }

        if (!Feed.hidden && !Feed.busy) {
            var hide_request = new Ajax.DBRequest("/hide_events?hide=1");
            Feed.busy = true;
            Feed.hidden = true;

            Sprite.replace(elm.down(), "s_dropdown_arrow_small", "s_dropdown_arrow_small2");
            var hide_effect = new Effect.BlindUp(events, {'afterFinish': function () {
                Feed.busy = false;
            }});
        }
        else if (events.style.display == "none" && !Feed.busy) {
            var show_request = new Ajax.DBRequest("/hide_events?hide=0");
            Feed.hidden = false;
            Feed.busy = true;

            Sprite.replace(elm.down(), "s_dropdown_arrow_small2", "s_dropdown_arrow_small");
            var show_effect = new Effect.BlindDown(events, {'afterFinish': function () {
                events.style.height = "auto";
                Feed.busy = false;
            }});
        }
        return false;
    }
};

Effect.BlindFadeUp = function (elm, opts) {
    var ef;
    ef = new Effect.BlindUp(elm, opts);
    ef = new Effect.Fade(elm, opts);
};

Effect.BlindFadeDown = function (elm, opts) {
    var ef;
    ef = new Effect.BlindDown(elm, opts);
    ef = new Effect.Appear(elm, opts);
};

var Base = {
    mouseOut: false,
    sf_hidden: false,
    showSharedFolders: function (link, e) {
        Event.stop(e);

        var folderList = $('shared-folder-dropdown');

        Event.observe(folderList, 'mouseover', Base.mouseOverList);
        Event.observe(folderList, 'mouseout', Base.mouseOutList);
        Event.observe(e.target, 'mouseout', Base.mouseOutList);
        Event.observe(e.target, 'mouseover', Base.mouseOverList);
        Event.observe(document, 'click', Base.hideSharedFolders);

        Base.sf_hidden = false;

        // wait a sec before we show it
        setTimeout(
            function () {
                if (Base.sf_hidden) {
                    return;
                }

                var shadow = $('share-menu-shadow');
                var numFolders = folderList.select('li').length;
                folderList.clonePosition($(link).down("img"), {offsetTop: 8, offsetLeft: 1, setWidth: false, setHeight: false});
                folderList.style.height = (numFolders > 9) ? "200px" : "";
                folderList.show();

                shadow.clonePosition($(link).down("img"), {offsetTop: 9, offsetLeft: 2, setWidth: false, setHeight: false});
                shadow.clonePosition(folderList, {setTop: false, setLeft: false});
                shadow.setOpacity(0.2);
                shadow.show();
            }, 350);

        return false;
    },
    hideSharedFolders: function (e) {
        var folderList = $('shared-folder-dropdown');
        $('shared-folder-dropdown').hide();
        $('share-menu-shadow').hide();

        Event.stopObserving(window, 'click', Base.hideSharedFolders);
        Event.stopObserving(folderList, 'mouseover', Base.mouseOverList);
        Event.stopObserving(folderList, 'mouseout', Base.mouseOutList);
        clearTimeout(Base.mouseOut);
        Base.mouseOut = false;
        Base.sf_hidden = true;
    },
    mouseOverList: function () {
        clearTimeout(Base.mouseOut);
        Base.mouseOut = false;
    },
    mouseOutList: function (e, bigger_num) {
        clearTimeout(Base.mouseOut);
        Base.mouseOut = setTimeout(Base.hideSharedFolders, bigger_num || 200);
    }
};

var Notify = {
    ServerError: function (msg) {
        return Notify.showDiv(msg, 'server-error', "There was a problem completing this request.");
    },
    ServerSuccess: function (msg) {
        return Notify.showDiv(msg, 'server-success', "Your request completed successfully.");
    },
    showDiv: function (msg, div, default_msg) {
        Notify.last_msg = msg;
        Notify.clearAll();

        msg = msg || default_msg;
        var box = Util.scry(div);
        if (!box) {
            return;
        }
        box.down('span').update(msg);

        Util.center(box);
        var ef;
        ef = new Effect.BlindFadeDown(box, {duration: 0.5, scaleTo: 75, queue: {scope: 'notify'}});
        ef = new Effect.BlindFadeUp(box, {duration: 0.3, scaleFrom: 75, delay: 8, queue: {scope: 'notify'}});

        if (Util.ie6) {
            box.scrollTo();
        }
    },
    clearAll: function () {
        $$('.notify').invoke('hide');

        // and clear out any outstanding effects
        var q = Effect.Queues.get('notify');
        q.effects = [];
        clearInterval(q.interval);
        q.interval = null;
    },
    clearIf: function (msg) {
        if (Notify.last_msg == msg) {
            Notify.clearAll();
        }
    }
};

var Tooltip = {
    attach: function (elm, content, trigger) {
        content = content.widthSplit(50).invoke('escapeHTML').join("&#8203;<wbr>");
        elm = $(elm);
        trigger = trigger ? $(trigger) : null;

        var id = elm.id ? elm.id : Math.floor(Math.random() * 10000 + 1);
        var d = new Element('div', {'id': id + "-tooltip", 'class': 'tooltip'});
        d.setStyle({display: "none", position: "absolute"});
        d.update(content);
        $('floaters').insert(d);
        elm.tooltip = d;

        elm.out_target = trigger ? true : false;
        elm.observe('mouseout', Tooltip.mouseout('target', elm));
        elm.observe('mouseover', Tooltip.mouseover('target', elm));

        elm.out_trigger = trigger ? false : true;
        if (trigger) {
            trigger.observe('mouseout', Tooltip.mouseout('trigger', elm));
            trigger.observe('mouseover', Tooltip.mouseover('trigger', elm));
        }

        elm.out_tooltip = true;
        d.observe('mouseout', Tooltip.mouseout('tooltip', elm));
        d.observe('mouseover', Tooltip.mouseover('tooltip', elm));
    },
    update: function (elm, content) {
        if (elm.tooltip) {
            $(elm.tooltip).update(content);
        }
    },
    mouseover: function (thing, elm) {
        return function () {
            elm['out_' + thing] = false;
        };
    },
    mouseout: function (thing, elm) {
        return function () {
            elm['out_' + thing] = true;

            Tooltip.hide_if_out.defer(elm);
        };
    },
    show_by: function (e) {
        var d = $(e.tooltip);
        d.clonePosition(e, {setWidth: false, setHeight: false, offsetTop: Math.floor(e.getHeight() / 2), offsetLeft: e.getWidth() + 1});
        d.show();
    },
    hide_if_out: function (e) {
        if (!e.out_target || !e.out_trigger || !e.out_tooltip) {
            return;
        }

        var d = $(e.tooltip);
        d.hide();
    },
    show: function (elm, content, trigger) {
        elm = $(elm);
        trigger = trigger ? $(trigger) : null;

        if (!elm.tooltip) {
            Tooltip.attach(elm, content, trigger);
        }

        Tooltip.show_by(elm);
    }
};

/*global SortSet*/
var BrowseURL = {
    PATH: 0,
    SORT: 1,
    DEL:  2,
    get_sort: function () {
        return Util.url_hash().split(":")[BrowseURL.SORT];
    },
    get_path: function () {
        return Util.url_hash().split(":")[BrowseURL.PATH];
    },
    get_del: function () {
        return Util.url_hash().split(":")[BrowseURL.DEL];
    },
    make_url: function (field, new_val) {
        var p = Util.url_hash().split(":");
        p[field] = new_val;
        return "#" + p.join(":");
    },
    get_sort_url: function (id, dir) {
        return BrowseURL.make_url(BrowseURL.SORT, id + "," + (dir?1:0));
    },
    get_path_url: function (path) {
        if (path.charAt(path.length - 1) != '/') {
            path += "/";
        }

        if (Browse.root) {
            var parts = path.split("/");
            path = "/" + parts.slice(Browse.root_depth + 1).join("/");
        }

        return BrowseURL.make_url(BrowseURL.PATH, path);
    },
    get_del_url: function (del) {
        return BrowseURL.make_url(BrowseURL.DEL, del ? 1 : 0);
    },
    check_url: function () {
        var hash = Util.url_hash();
        if (!hash.length && BrowseURL.last_hash && BrowseURL.last_hash.length) {

            // HACK: This is to fix a bug in safari that wouldn't stop the javascript setinterval when leaving the current page and going to a pdf.
            // It caused the page location to be reverted to the previous url on the next interval call.  This will be fixed in future
            // versions of Safari.
            if (location.href.match(/\.pdf/) && Prototype.Browser.WebKit) {
                clearInterval(BrowseURL.checkInterval);
                return;
            }

            location.href = "#" + BrowseURL.last_hash;

            if (!Constants.IS_PROD) {
                alert("restored location. don't do whatever you just did...");
            }
            return;
        }

        var ihash = null;
        if (BrowseURL.last_hash != hash) {
            BrowseURL.set_iframe_hash(hash);
            ihash = hash;
        }

        // you'd think we could just take the ihash directly from the iframe,
        // but ie has a little lag, so we don't if we just changed it
        ihash = ihash || BrowseURL.get_iframe_hash();
        if (ihash && ihash != hash) {
            location.href = "#" + ihash;
            hash = ihash;
        }

        if (BrowseURL.last_hash != hash) {
            var parts = hash.split(":");
            var old = (BrowseURL.last_hash || '').split(":");
            BrowseURL.last_hash = hash;

            if (old.length > BrowseURL.SORT - 1 && old[BrowseURL.SORT] != parts[BrowseURL.SORT]) {
                SortSet.url_sort();
            }

            if ((old[BrowseURL.PATH] != parts[BrowseURL.PATH]) ||
                (old[BrowseURL.DEL] != parts[BrowseURL.DEL])) {
                var deleted_changed = old[BrowseURL.DEL] != parts[BrowseURL.DEL];
                Browse.deleted_shown = parts[BrowseURL.DEL] == '1'? 1 : 0;
                Browse.reload((Browse.root || "") + parts[BrowseURL.PATH], deleted_changed); // force=deleted_changed
            }
        }
    },
    set_iframe_hash: function (hash) {
        if (typeof(BrowseURL.url_iframe) === 'undefined') {
            BrowseURL.url_iframe = $('url-iframe');
        }

        if (!BrowseURL.url_iframe) {
            return;
        }

        document.frames['the-iframe'].location.href = "/blank" + hash;
    },
    get_iframe_hash: function (hash) {
        if (typeof(BrowseURL.url_iframe) == 'undefined') {
            BrowseURL.url_iframe = $('url-iframe');
        }

        if (!BrowseURL.url_iframe) {
            return false;
        }

        return document.frames['the-iframe'].location.href.split("/blank")[1];
    }
};


var SortSet = {
    sort: function (elm, cmp) {
        elm.blur();
        Util.setCursor('progress');
        var direction = SortSet.pick(elm);
        Browse.sort(cmp, !direction); // reverse=!direction
        Util.clearCursor();
        return true;
    },
    pick: function (elm) {
        var dir = Util.toggle(elm.id);

        var options = $$('.sort_option');
        options.each(
            function (x) {
                if (x != elm) {
                    Sprite.src(x.down('img'), 'downtick-spacer');
                    x.onmouseout = null;
                    x.onmouseover = null;
                    Util.reset_toggle(x.id);
                    x.href = BrowseURL.get_sort_url(x.id, false);
                } else {
                    var which = dir ? "up" : "down";
                    Sprite.src(x.down('img'), 'sort-' + which + 'tick-on');
                    x.onmouseout = function () {
                        Sprite.src(x.down('img'), 'sort-' + which + 'tick-off');
                    };
                    x.onmouseover = function () {
                        Sprite.src(x.down('img'), 'sort-' + which + 'tick-on');
                    };
                    x.href = BrowseURL.get_sort_url(x.id, dir);
                }
            });
        return dir;
    },
    make_url: function (id, dir) {
        return id + "," + (dir?1:0);
    },
    last_url: '',
    url_sort: function () {
        var piece = BrowseURL.get_sort();

        if (piece && piece.length) {
            var parts = piece.split(",");
            var elm = document.getElementById(parts[0]);
            if (!elm) {
                return false;
            }
            Util.set_next_toggle(elm.id, parts[1] == '1' ? true : false);
            elm.onclick();

            return true;
        }

        return false;
    }
};

var Sort = {
    FILES_BY_NAME: function (x, y) {
        // sort directories first
        var dir_diff = y.dir - x.dir;
        if (dir_diff) {
            return dir_diff;
        }

        // sort alphabetical
        var xname = x.filename.toLowerCase();
        var yname = y.filename.toLowerCase();
        return xname == yname ? 0 : (xname.pad_nums() < yname.pad_nums() ? -1 : 1);
    },
    FILES_BY_SIZE: function (x, y) {
        // sort directories first
        var dir_diff = y.dir - x.dir;
        if (dir_diff) {
            return -1 * dir_diff;
        }

        if (x.dir) { // sort directories by name, not size
            return Sort.FILES_BY_NAME(x, y);
        }

        // sort bigger first
        return y.bytes - x.bytes;
    },
    FILES_BY_MODIFIED: function (x, y) {
        var xts = x.ts;
        var yts = y.ts;

        // sort latest first
        return xts == yts ? 0 : (xts < yts ? 1 : -1);
    }
};

var Browse = {
    msg: false,
    files: [],
    selected_files: [],
    drop_targets: [],
    selection: [],
    drag_watch: null,
    drag_end_watch: null,
    dragging: false,
    drag_startPos: null,
    drag_box: {},
    ticker: 0,
    details: true,
    last_sort: [Sort.FILES_BY_NAME, false],

    emptyCheck: function () {
        if (!Browse.files.length) {
            Browse.show_message("Folder contains deleted files.");
        }
    },
    show_message: function (msg) {
        if (typeof(msg) != typeof('string')) {
            var message = $('browse-files').down(".browse-message");
            if (message) {
                message.show();
            }

            return;
        }

        Browse.msg = msg;

        var d = Element("div", {'class': 'browse-message'});
        d.update(msg);
        $('browse-files').insert(d);
    },
    hide_message: function () {
        var message = $('browse-files').down(".browse-message");
        if (message) {
            message.hide();
        }
    },
    setRoot: function (root) {
        if (root.charAt(root.length - 1) == '/') {
            root = root.substr(0, root.length - 1);
        }

        Browse.root = root;
        Browse.root_depth = Util.pathDepth(root);
    },
    sort: function (cmp, reverse, force) {
        if (!force && cmp == Browse.last_sort[0] &&
            reverse == Browse.last_sort[1]) {
            return;
        }

        Browse.last_sort = [cmp, reverse];

        if (reverse) {
            var orig_cmp = cmp;
            cmp = function (x, y) {
                return -1 * orig_cmp(x, y);
            };
        }

        Browse.files.sort(cmp);
        Browse.drop_targets.sort(cmp);
        Browse.refill();
        Browse.refresh_positions();

        return false;
    },
    resort: function () {
        var l = Browse.last_sort;
        Browse.sort(l[0], l[1], true); // force=true
        Browse.refresh_drop_positions();
    },
    refill: function ()
    {
        var container = $('browse-files');

        Browse.files.each(function (x) {
            container.insert(x.div);
            if (x.selected) {
                x.select();
            }
        });
    },
    add_file: function (file, need_new) {
        if (file.filename) {
            Browse.files.push(file);
        } else {
            Browse.has_parent_link = true;
            Browse.parent_link = file;
        }

        if (!Browse.file_div) {
            Browse.file_div = $('browse-files');
        }
        file.render(need_new, file.href);
        file.setOpacity();

        file.cachePos.bind(file).defer();
        if (file.drop_target) {
            Browse.drop_targets.push(file);
        }
        if (file.bytes < 0) {
            Browse.deleted_shown = true;
        }
    },
    refresh_drop_positions: function () {
        Browse.drop_targets.invoke("cachePos");
    },
    refresh_positions: function () {
        Browse.files.invoke("cachePos");
        if (Browse.has_parent_link) {
            Browse.parent_link.cachePos();
        }
    },
    remove_selected: function () {
        Browse.files.each(function (x) {
            if (x.selected) {
                x.div.remove();
            }
        });
        Browse.files = Browse.files.findAll(function (x) {
            return !x.selected;
        });
        Browse.refresh_positions();
    },
    find_icons: function () {
        if (!Browse.files.length) {
            return;
        }

        Browse.updateOffset();

        var test = Browse.files[0];
        if (test.box) {
            var old_box_top = test.box.top;
            test.cachePos();
        }

        if (!test.box || old_box_top != test.box.top) {
            Browse.refresh_positions();
        }
    },
    cache_selection: function () {
        Browse.selection = Browse.files.pluck('selected');
    },
    get_selected: function () {
        return Browse.selected_files;
    },
    deselect_all: function () {
        BrowseActions.clear();
        Browse.deselect_all_but(false);
    },
    deselect_all_but: function (one) {
        Browse.selected_files.each(function (f) {
            if (f != one) {
                f.deselect();
            }
        });
        Browse.selected_files = one ? [one] : [];
    },
    clicked_scrollbar: function (e) {
        var b = $('browse-files');
        var p = b.viewportOffset();
        var left = p.left + b.clientWidth;
        var right = p.left + b.offsetWidth;

        return left < e.clientX && e.clientX < right;
        // something about b.componentFromPoint(e.clientX, e.clientY) for IE
    },
    drag_start: function (e) {
        return; // no drag select yet
        /*
        Event.stop(e);
        Browse.find_icons();

        if (Browse.clicked_scrollbar(e)) {
            return;
        }

        if (e.ctrlKey)
            Browse.cache_selection();

        Browse.drag_watch = Browse.drag.bindAsEventListener(this);
        Browse.drag_end_watch = Browse.drag_end.bindAsEventListener(this);
        Event.observe(document, "mousemove", Browse.drag_watch);
        Event.observe(document, "mouseup", Browse.drag_end_watch);

        Browse.drag_startPos = {top: e.clientY, left: e.clientX};
        Util.initBox(e.clientY, e.clientX, Browse.drag_box);
        Browse.dragging = true;

        $('browse-selection').setOpacity(0.5);
        */
    },
    drag: function (e) {
        if (!Browse.dragging) {
            return;
        }
        Event.stop(e);

        var box = Browse.drag_box;

        Util.calcBox(e.clientY, e.clientX, Browse.drag_startPos.top, Browse.drag_startPos.left, box);
        Browse.draw_box(box);
        box.ctrl = e.ctrlKey;
        box.shift = e.shiftKey;

        if (box.width + box.height > 10) {
            Browse.select_under.defer();
        }
    },
    select_under: function () {
        var box = Browse.drag_box;
        var shiftKey = box.shift;
        var ctrlKey = box.ctrl;

        if (!Browse.dragging) {
            return;
        }
        for (var i = 0; i < Browse.files.length; i++) {
            var f = Browse.files[i];
            if (f.overlaps(box)) {
                if (ctrlKey) {
                    f.toggle(Browse.selection[i]);
                } else {
                    f.select();
                }
            } else {
                if (ctrlKey) {
                    f.set(Browse.selection[i]);
                } else if (!shiftKey) {
                    f.deselect();
                }
            }
        }
    },
    select_range_to: function (elm, additive) {
        var other = Browse.shift_start;
        if (!other) {
            Browse.shift_start = elm;
            elm.select();
        } else {
            var selecting = false;
            for (var i = 0; i < Browse.files.length; i++) {
                var f = Browse.files[i];

                if (!additive) {
                    f.set(selecting);
                } else if (selecting) {
                    f.select();
                }

                if (f == elm || f == other) {
                    f.select();
                    selecting = !selecting;
                }
            }
        }
    },
    drag_end: function (e) {
        if (e) {
            var box = Browse.drag_box;
            if (box.width + box.height < 10) {
                Browse.deselect_all();
            }
        }

        Browse.dragging = false;
        Event.stopObserving(document, "mousemove", Browse.drag_watch);
        Event.stopObserving(document, "mouseup", Browse.drag_end_watch);
        Browse.drag_watch = null;
        $('browse-selection').hide();
    },
    draw_box: function (box) {
        var s = $('browse-selection').style;
        s.top = box.top + "px";
        s.left = box.left + "px";
        s.width = box.width + "px";
        s.height = box.height + "px";
        s.display = '';
    },
    clone_selected: function (e) {
        Browse.sel_clones = $A();
        Browse.sel_clone_origin = {y: e.clientY, x: e.clientX, st: Util.scrollTop(), sl: Util.scrollLeft()};
        Util.scry('ghost-icons').update('');
        for (var i = 0; i < Browse.files.length; i++) {
            var f = Browse.files[i];
            if (f.selected) {
                var div = f.div.cloneNode(true); // deep=true
                div.file = null; // don't need this reference ever
                div.hide();
                div.absolutize();
                f.cachePos();
                var p = f.box;
                var top = p.top + Util.scrollTop() - (Util.ie6 ? 54 : 0); // FIXME: horrible ie6 hack
                var left = p.left + Util.scrollLeft();
                div.style.top = top + "px";
                div.style.left = left + "px";
                div.style.width = p.width + "px";
                div.style.height = p.height + "px";

                div.removeClassName("file-select");
                div.removeClassName("file-highlight");
                div.removeClassName("file-selected-highlight");
                div.origin = {'top': top, 'left': left};
                div.setOpacity(0.5);

                Util.scry('ghost-icons').insert(div);
                Browse.sel_clones.push(div);
            }
        }
    },
    draw_clones: function (e) {
        if (!this.sel_clone_origin) {
	        return;
	    }
        var dx = e.clientX -  this.sel_clone_origin.x;
        var dy = e.clientY -  this.sel_clone_origin.y;
        var ds  = Util.scrollTop() - this.sel_clone_origin.st;
        var dl  = Util.scrollLeft() - this.sel_clone_origin.sl;

        for (var i = 0; i < Browse.sel_clones.length; i++) {
            var div = Browse.sel_clones[i];
            div.style.display = '';
            div.style.top = (div.origin.top + dy + ds).toString() + "px";
            div.style.left = (div.origin.left + dx + dl).toString() + "px";
        }
    },
    kill_clones: function () {
        if (this.sel_clones) {
            this.sel_clones.map(Util.yank);
            this.sel_clones = null;
            this.sel_clone_origin = null;
        }
    },
    find_drop_target: function (e) {
        if (!Browse.sel_clone_origin) {
            return;
        }

        var x = e.clientX + Util.scrollLeft() - Browse.sel_clone_origin.sl;
        var y = e.clientY + Util.scrollTop() - Browse.sel_clone_origin.st + (Util.ie6 ? 52 : 0); // FIXME: horrible ie6 hack

        var i = Util.bsearch(Browse.drop_targets, 0,
            function (f) {
                var c = Util.cmpBox(x, y, f.box);
                // this next line's weird
                // 1) -c because we want box relative to point
                // 2) fail with -1 if it's selected
                return (c !== 0) ? -c : (f.selected ? -1 : 0);
            }
        );

        if (i != -1) {
            return Browse.drop_targets[i];
        }

        return false;
    },
    remove_drop_target: function (t) {
        var targets = Browse.drop_targets;
        var l = targets.length;
        var out = [];

        for (var i = 0; i < l; i++) {
            var test = targets[i];
            if (t != test) {
                out.push(test);
            }
        }

        Browse.drop_targets = out;
    },
    highlight_drop_target: function (e) {
        var t = Browse.find_drop_target(e);

        if (Browse.last_drop_target && Browse.last_drop_target != t) {
            Browse.last_drop_target.drop_lowlight();
        }
        Browse.last_drop_target = t;

        if (t) {
            t.drop_highlight();
        }
    },
    show_copy_move: function (t) {
        if (Browse.copy_move_over == t) {
            return;
        }
        Browse.copy_move_over = t;

        var fg = Browse.copy_move_texts();
        var bg = Browse.copy_move_overlays();
        var all = bg.concat(fg);

        var left = t.box.left + Browse.sel_clone_origin.sl;
        var top = t.box.top + Browse.sel_clone_origin.st;

        if (t.box.height > t.box.width) {
            // tile vertically
            all.each(function (x) {
                var s = x.style;
                s.width = this.box.width + "px";
                s.height = Math.round(this.box.height / 2) + "px";
                s.left = left + "px";
                s.display = '';
                s.lineHeight = s.height;
            }, t);
            bg[0].style.top = top + "px";
            bg[1].style.top = (top + Math.round(t.box.height / 2)) + "px";
            fg[0].style.top = top + "px";
            fg[1].style.top = (top + Math.round(t.box.height / 2)) + "px";
        } else {
            // tile horizontally
            all.each(function (x) {
                var s = x.style;
                s.height = this.box.height + "px";
                s.width = Math.round(this.box.width / 2) + "px";
                s.top = top + "px";
                s.display = '';
                s.lineHeight = s.height;
            }, t);
            bg[0].style.left = left + "px";
            bg[1].style.left = (left + Math.round(t.box.width / 2)) + "px";
            fg[0].style.left = left + "px";
            fg[1].style.left = (left + Math.round(t.box.width / 2)) + "px";
        }
        fg[0].box = Util.getBox(fg[0]);
        fg[1].box = Util.getBox(fg[1]);
    },
    bold_copy_move: function (e) {
        var opts = Browse.copy_move_texts();
        var highlight = 0;

        if (e.clientY > opts[1].box.top) {
            highlight = 1;
        }

        if (opts[0].last_highlight == highlight) {
            return;
        }
        opts[0].last_highlight = highlight;

        opts[highlight].addClassName('copy-move-bold');
        opts[1 - highlight].removeClassName('copy-move-bold');
    },
    hide_copy_move: function () {
        Browse.copy_move_over = false;
        Browse.copy_move_overlays().invoke('hide');
        Browse.copy_move_texts().invoke('hide');
    },
    over_copy_option: function (e) {
        return Util.pointOnBox(e.clientX, e.clientY, Util.getBox('copy-text'));
    },
    cmos: [],
    copy_move_overlays: function () {
        if (!Browse.cmos.length) {
            Browse.cmos = $$('.copy-move-overlay');
        }

        return Browse.cmos;
    },
    cmts: [],
    copy_move_texts: function () {
        if (!Browse.cmts.length) {
            Browse.cmts = $$('.copy-move-text');
        }

        return Browse.cmts;
    },
    find_file: function (where) {
        return Browse.files.find(function (x) {
            return x.where == where;
        });
    },
    pull_file: function (where) {
        var files = Browse.files;
        var l = files.length;
        var out = [];

        var file = false;
        for (var i = 0; i < l; i++) {
            var f = files[i];
            if (f.where == where) {
                file = f;
            } else {
                out.push(f);
            }
        }

        Browse.files = out;
        return file;
    },
    reset_state: function () {
        // drop dom references for the gc's sake
        Browse.files.invoke('unload');

        if (Browse.parent_link) {
            Browse.parent_link.unload();
        }

        Browse.msg = false;
        Browse.dragging = false;
        Browse.files = [];
        Browse.selected_files = [];
        Browse.sel_clones = [];
        Browse.sel_clone_origin = null;
        Browse.drop_targets = [];
        Browse.last_drop_target = null;
        Browse.selection = [];
        $('browse-files').update();
        Browse.has_parent_link = false;
        Browse.parent_link = null;
        Browse.in_placer = null;
    },
    update: function (content) {
        $('browse-files').update(content);
    },
    reload: function (path, force) {
        if (Browse.reloading) { // one reload at a time
            return;
        }
        if (Util.normPath(path) == Util.normPath(Browse.current_path) && !force) { // don't reload same path
            return;
        }

        if (!path) {
            path = Browse.current_path;
        }

        Browse.reloading = true;

        Browse.current_path = path;

        Util.setCursor('progress');
        Feed.showLoading(false, 'browse-files', true); // just_icon=true

        var del = Browse.deleted_shown ? "&show_deleted=yah" : "";

        var dbr = new Ajax.DBRequest("/browse2" + path + "?ajax=yes" + del, {
            parameters: {d: Browse.root_depth, mini: Browse.minimode},
            onSuccess: function (req) {
                Browse.reset_state();
                Browse.breadcrumb(path);
                Browse.update(req.responseText);

                if (!Browse.msg) {
                    Browse.resort();
                }
            },
            cleanUp: function () {
                Util.clearCursor();
                Feed.hideLoading();
                Browse.reloading = false;
            },
            no_feed_reload: true
        });

        return true;
    },
    unload: function () {
        Browse.reset_state();
        var dd = $('dropdown');
        if (dd) {
            dd = Util.yank(dd);
        }
    },
    breadcrumb: function (path) {
        path = Util.normPath(path);
        var parts = path.split("/");

        var min_snip_len = 8;
        var max_total_len = 40;

        var last_folder = decodeURIComponent(parts[parts.length - 1]);
        var last_snip_len = max_total_len - min_snip_len * (parts.length - 1);
        var rest_snip_len = min_snip_len;
        if (last_snip_len >= last_folder.length) {
            rest_snip_len = Math.floor((max_total_len - last_folder.length) / (parts.length - 1));
        } else {
            rest_snip_len = min_snip_len;
        }

        var bc = "";
        var total_up = parts.slice(0, Browse.root_depth).join("/");

        var link0 = "<a href='";
        var link1 = "' onclick='return Browse.reload(\"";
        var link2 = "\"";
        var link3 = ");'>";
        var link4 = "</a>";

        for (var i = Browse.root ? Browse.root_depth : 0 ; i < parts.length; i++) {
            if (i === 0) {
                var my_accessmanager = (rest_snip_len < 10) ? "AccessManager" : "My AccessManager";

                bc += link0 + BrowseURL.get_path_url("/") + link1 + "/" + link2 + ((path == '/')?", force=true":"") + link3 + my_accessmamanger.snippet(rest_snip_len) + link4;
            } else {
                if (!parts[i].length) {
                    continue;
                }
                total_up += "/" + parts[i];
                total_up = total_up.replace("'", "%27"); // make sure we don't screw up this html writing
                var last_piece = (i == parts.length - 1);
                var name = new Emstring(decodeURIComponent(parts[i]).escapeHTML());
                name = last_piece ? name.snippet(last_snip_len) : name.snippet(rest_snip_len);
                bc += (bc.length ? " &#187; " : "") + link0 + BrowseURL.get_path_url(total_up) + link1 + total_up + link2 + (last_piece?", force=true":"") + link3 + name + link4;
            }
        }
        $('browse-location').update(bc);
    },
    viewportOffset: function () {
        if (!Browse.files.length) {
            return;
        }

        if (!Browse.div_parent) {
            var op = Browse.files[0].div.offsetParent;
            if (!op) {
                return;
            }

            Browse._viewportOffset = {};
            Browse.div_parent = $(op);
            Browse._cumulativeOffset = Browse.div_parent.cumulativeOffset();
        }

        var l = Util.scrollLeft(Browse.div_parent);
        var t = Util.scrollTop(Browse.div_parent);

        if (!Browse.scrollTop || !Browse.scrollLeft || Browse.scrollTop != t  || Browse.scrollLeft != l) {
            Browse._viewportOffset.top = Browse._cumulativeOffset.top - t;
            Browse._viewportOffset.left = Browse._cumulativeOffset.left - l;
            Browse.scrollLeft = l;
            Browse.scrollTop = t;
        }

        return Browse._viewportOffset;
    },
    updateOffset: function () {
        if (!Browse.div_parent) {
            return;
        }

        Browse._cumulativeOffset = Browse.div_parent.cumulativeOffset();
        Browse.viewportOffset();
    },
    selectable: function () {
        Util.enableSelection(Util.scry('browse-files'));
    },
    unselectable: function () {
        Util.disableSelection(Util.scry('browse-files'));
    }
};

/*global FileOps*/
var BrowseFile = Class.create(
{
    initialize: function (icon, where, href, caption, filename, size, bytes, ago, ts, actions, hash, is_dir, drop_target, need_new) {
        this.icon = icon;
        this.caption = caption;
        this.filename = filename;
        this.where = where;
        this.hash = hash;
        this.href = href;
        this.size = size != 'None' ? size : "";
        this.bytes = bytes;
        this.ago = ago;
        this.ts = ts;
        this.selected = false;
        this.drag_startPos = null;
        this.drop_target = drop_target;
        this.dir = is_dir ? 1 : 0;
        this.dragging = false;

        this.str_actions = actions;
        actions = actions.split("|");
        this.main_actions = actions[0].split(" ");
        this.more_actions = actions[1] && actions[1].length  ? actions[1].split(" ") : [];
        this.rest_actions = actions[2] && actions[2].length ? actions[2].split(" ") : [];

        Browse.add_file(this, need_new);
    },
    drag_dist: function (e) {
        return Math.abs(this.drag_startPos.x - e.clientX) +
            Math.abs(this.drag_startPos.y - e.clientY);
    },
    render: function (new_file, new_link) {
        if (new_file) {
            this.div = new Element("div", {'class': Browse.details ? "browse-file-box-details" : "browse-file-box-iconic"});

            var del_class = this.bytes != '-1' ? "" : " deleted_file_line";
            var mini_class = Browse.minimode ? " details-filename-mini" : "";
            var link = new_link ? new_link : "#";
            this.div.update("<div style='position: relative;'>" +
                            "<div class='details-icon'><img class='sprite s_" + this.icon + "' src='/static/images/icons/icon_spacer.gif' align='absbottom'></div>" +
                            "<div class='details-filename" + del_class + mini_class + "'><a href='" + link + "'>" + this.caption + "</a></div>" +
                            "<div class='details-size'>" + (this.size || '&nbsp;') + "</div>" +
                            "<div class='details-modified'>" + this.ago + "</div>" +
                            "<a class='dropdown-arrow' style='visibility: hidden;' href='#'><img src='/static/images/big-dropdown.gif'></a>" +
                            "<br class='clear'/><div class='miniscule-text'>&nbsp;</div></div>");
        } else {
            this.div = Util.childElement(Browse.file_div, Browse.files.length - (Browse.has_parent_link ?  0 : 1));
        }

        return this.listen(this.div);
    },
    listen: function (div) {
        var rel_div = Util.childElement(div, 0);
        var filename_div = Util.childElement(rel_div, 1);
        var a = $(Util.childElement(filename_div, 0));
        this.a = a;

        if (this.dir) {
            a.href = BrowseURL.get_path_url(this.where);
            this.dir_click = (function () {
                return Browse.reload(this.where);
            }).bind(this);
            a.observe('click', this.dir_click);
        }

        a.title = this.filename;

        this._over = this.over.bind(this);
        this._out = this.out.bind(this);
        Event.observe(div, 'mouseover', this._over);
        Event.observe(div, 'mouseout', this._out);

        if (this.filename) {
            this._down = this.down.bindAsEventListener(this);
            Event.observe(div, 'mousedown', this._down);
            this._ignorenonlink = this.ignorenonlink.bindAsEventListener(this);
            Event.observe(div, 'click', this._ignorenonlink);
            this.arrow_link = Util.childElement(rel_div, 4);
            this._down_dropdown = this.down_dropdown.bind(this);
            Event.observe(this.arrow_link, 'mousedown', this._down_dropdown);
        }

        div.file = this;

        return div;
    },
    unload: function () {
        if (this.dir_click) {
            this.a.stopObserving('click', this.dir_click);
        }

        Event.stopObserving(this.div, 'mouseover', this._over);
        Event.stopObserving(this.div, 'mouseout', this._out);

        if (this.filename) {
            Event.stopObserving(this.div, 'mousedown', this._down);
            Event.stopObserving(this.div, 'click', this._ignorenonlink);
            Event.stopObserving(this.arrow_link, 'mousedown', this._down_dropdown);
        }

        this.a = null;
        this.div.file = null;
        this.div = null;
        this.arrow_link = null;
    },
    tooltip: function () {
        if (this.caption.unescapeHTML() != this.filename && this.filename.length) {
            Tooltip.show(this.a, this.filename);
        }
    },
    rename: function (where, is_folder, hash) {
        var new_name = FileOps.filename(where);

        this.caption = new_name.snippet();
        this.filename = new_name;
        this.where = Util.urlquote(where);
        this.hash = hash;
        this.ago = is_folder ? "" : "just now";

        if (is_folder) {
            this.href = BrowseURL.get_path_url(this.where);
        } else {
            var parts = this.href.split("/");
            parts[parts.length - 1] = Util.urlquote(new_name) + "?w=" + hash;
            this.href = parts.join("/");
        }

        this.a.update(this.caption);
        this.a.title = this.filename;
        this.a.href = this.href;
    },
    move: function (afterFinish) {
        var ef = new Effect.Fade(this.div, {'afterFinish': afterFinish});
    },
    del: function (afterFinish) {
        if (!Browse.deleted_shown) {
            var ef = new Effect.Fade(this.div, {'afterFinish': afterFinish});
            Browse.emptyCheck();
        } else {
            this.size = "None";
            this.bytes = -1;
            this.ago = this.dir ? "" : "just now";
            this.main_actions = this.dir ? ["full_restore"] : "undelete purge".split(" ");
            this.more_actions = this.dir ? ["purge_folder"] : ["revisions"];
            this.rest_actions = [];

            var a = this.div.down("a");
            a.addClassName("deleted_file_line");

            if (this.dir) {
                Sprite.src(this.div.down('img'), 'folder_gray');
            }
        }
    },
    purge: function (afterFinish) {
        var ef = new Effect.Fade(this.div, {'afterFinish': afterFinish});
        Browse.emptyCheck();
    },
    setOpacity: function () {
        if (this.size == 'None') {
            this.div.down("div").down("img").setOpacity(0.5);
        }
    },
    cachePos: function () {
        if (!this.div) {
            return;
        }

        if (!this.box) {
            this.box = {};
        }

        var offset = Browse.viewportOffset();
        if (!offset) {
            return;
        }

        this.box.left = offset.left + this.div.offsetLeft;
        this.box.top = offset.top + this.div.offsetTop;

        if (!this.box.width) {
            this.box.width = this.div.getWidth();
            this.box.height = this.div.getHeight();
        }
    },
    overlaps: function (box) {
        return Util.boxOnBox(box, this.box);
    },
    ignorenonlink: function (e) {
        if (e.target.tagName != 'A') {
            Event.stop(e);
        }
    },
    over: function () {
        if (Browse.dragging) {
            return;
        }

        if (this.filename) {
            this.dropdown_arrow(true); // on=true
        }

        if (!this.selected) {
            this.div.addClassName("file-highlight");
        } else {
            this.div.addClassName("file-selected-highlight");
        }
    },
    out: function (e) {
        if (!this.div) {
            return;
        }
        if (e.toElement) {
            if (e.toElement.className == 'tooltip' || $(e.toElement) == this.div || $(e.toElement).descendantOf(this.div)) {
                return;
            }
        }

        if (!this.selected) {
            this.div.removeClassName("file-highlight");
            if (this.filename) {
                this.dropdown_arrow(false); // on=false
            }
        } else {
            this.div.removeClassName("file-selected-highlight");
        }
    },
    down: function (e) {
        if (this.editing) {
            return;
        }
        if (e) {
            Event.stop(e);
        }

        this.click_select(e);
        Browse.find_icons();
        BrowseActions.kill_dropdowns();

        this.drag_startPos = {x: e.clientX, y: e.clientY};
        this.dragging = true;
        this.drag_watch = this.drag.bindAsEventListener(this);
        this.up_watch = this.up.bindAsEventListener(this);

        Event.observe(document, "mousemove", this.drag_watch);
        Event.observe(document, "mouseup", this.up_watch);
        Browse.clone_selected(e);
    },
    show_dropdown: function (show_all) {
        BrowseActions.kill_dropdowns();
        this.dropdown_arrow(true);  // on=true
        BrowseActions.dropdown(this, show_all);
    },
    down_dropdown: function (e) {
        this.click_select(e);
        this.show_dropdown(true); // show_all=true
    },
    up: function (e) {
        if (this.editing) {
            return;
        }
        Event.stop(e);

        var dropped = this.drag_end(e);

        if (e.target.tagName != "A" && !dropped) {
            this.show_dropdown();
        }

        Event.stopObserving(document, "mousemove", this.drag_watch);
        Event.stopObserving(document, "mouseup", this.up_watch);
    },
    click_select: function (e) {
        Event.stop(e);

        // no multiple select yet
        if (false && e.shiftKey && e.ctrlKey) {
            Browse.select_range_to(this, true); // additive=true
        } else if (false && e.shiftKey) {
            Browse.select_range_to(this);
        } else if (false && e.ctrlKey) {
            this.toggle();
            this.over();
        } else if (!this.selected) {
            this.select();
            Browse.deselect_all_but(this);
        }

        if (false && (!e.shiftKey || e.ctrlKey)) {
            Browse.shift_start = this;
        }
    },
    drag: function (e) {
        Event.stop(e);
        if (this.drag_dist(e) < 10) {
            return;
        }

        Browse.dragging = true;
        Util.setCursor('move');
        Browse.draw_clones(e);
        Util.noHorizScroll();
        Browse.highlight_drop_target(e);
    },
    drag_end: function (e) {
        if (!Browse.dragging) {
            return false;
        }

        Browse.dragging = false;
        Util.clearCursor();
        var f = Browse.find_drop_target(e);
        Util.allowHorizScroll(); // weird, but you can't turn this back on before calculating the drop target...

        var dropped = false;
        if (f) {
            f.drop_lowlight();

            if (this.bytes == -1) {
                Notify.ServerError("Moving deleted " + (this.dir ? "folders" : "files") + " is not allowed.");
            } else if (this.where == '/Public') {
                Notify.ServerError("Moving your Public folder is not allowed.");
            } else if (this.where == '/Photos') {
                Notify.ServerError("Moving your Photos folder is not allowed.");
            } else if (f != this) {
                var files = Browse.get_selected();
                if (!files.length) {
                    return;
                }
                var file = files[0];
                var folder = f;

                var action = file.dir ? FileOps.show_move_confirm : FileOps.do_move;

                action(file.where, decodeURIComponent(folder.where), file.dir); // is_folder=file.dir
                BrowseActions.kill_dropdowns();
                dropped = true;
            }
        }
        Browse.kill_clones(e);

        return dropped;
    },
    select: function () {
        this.selected = true;
        this.div.addClassName("file-select");
        Browse.selected_files.push(this);
    },
    deselect: function () {
        this.selected = false;
        this.div.removeClassName("file-select");
        this.div.removeClassName("file-highlight");
        this.div.removeClassName("file-selected-highlight");
        this.dropdown_arrow(false); // show=false
    },
    toggle: function (old_selected) {
        if (old_selected === undefined) {
            old_selected = this.selected;
        }

        if (old_selected) {
            return this.deselect();
        } else {
            return this.select();
        }
    },
    set: function (select) { // opposite of toggle
        return this.toggle(!select);
    },
    drop_highlight: function () {
        this.div.addClassName("drop-highlight");
    },
    drop_lowlight: function () {
        this.div.removeClassName("drop-highlight");
    },
    dropdown_arrow: function (show) {
        if (this.div) {
            if (show) {
                if (this.div.select('.dropdown-arrow').length) {
                    this.div.select('.dropdown-arrow')[0].style.visibility = "";
                }
            } else {
                if (this.div.select('.dropdown-arrow').length) {
                    this.div.select('.dropdown-arrow')[0].style.visibility = "hidden";
                }
            }
        }
    },
    edit: function (new_folder) {
        if (Browse.in_placer) {
            Browse.in_placer.file.editing = false;
            Browse.in_placer.editor.dispose();
            Browse.in_placer.name.innerHTML = Browse.in_placer.name_old_innerHTML;
            Browse.in_placer = null;
        }

        Browse.selectable();
        this.editing = true;
        var name = this.div.down("a");
        var orig_innerHTML = name.innerHTML;
        name.innerHTML = this.filename.escapeHTML();
        var action = new_folder ? "new" : "rename";
        var action_text = new_folder ? "Create" : "Rename";
        var is_folder = this.dir ? "yes" : "";

        var editor = new Ajax.InPlaceEditor(name, "/cmd/" + action + this.where + "?long_running",
            {okControl: 'link', cancelControl: 'link', htmlResponse: false,
            highlightColor: '#ddf0ff', highlightEndColor: '#fafdff',
            okText: action_text, cancelText: 'Cancel', clickToEditText: '', cols: Browse.minimode ? 15 : 25,
            callback: function (f, val) {
                return {to_path: val, 't': Constants.TOKEN, 'folder': is_folder};
            },
            ajaxOptions: {'method': 'post'},
            onComplete: (function (req) {
                if (req && req.status.toString().charAt(0) == '5') {
                    return;
                }
                Browse.unselectable();
                editor.dispose();
                this.editing = false;
                Browse.in_placer = null;
                if (!req) {
                    if (new_folder) {
                        this.purge(Browse.show_message); // afterwards=Browse.show_message
                    } else {
                        name.innerHTML = orig_innerHTML;
                    }
                    return;
                }
                if (req.responseText.indexOf('err:') === 0) {
                    Notify.ServerError(req.responseText.substr(4));
                    editor.dispose();
                    this.editing = false;
                    Browse.in_placer = null;
                    Browse.unselectable();
                    if (new_folder) {
                        this.del(Browse.show_message); // afterwards=Browse.show_message
                    } else {
                        this.rename(decodeURIComponent(this.where), is_folder);
                    }
                } else {
                    var parts = req.responseText.split(":");
                    var new_where = parts[0];
                    var new_hash = parts[1];

                    if (new_folder) {
                        // try to find an old folder named the same thing to not show
                        var f = Browse.find_file(Util.urlquote(new_where));
                        if (f && f.bytes.toString() == "-1") {
                            f.purge();
                        }
                    }

                    this.rename(new_where, is_folder, new_hash);
                    Browse.resort();
                    this.div.scrollTo();

                    if (new_folder) {
                        TreeView.reset(); //refresh the folder list
                    }
                }
            }).bind(this),
            onFailure: (function () {
                editor.dispose();
                this.editing = false;
                Browse.in_placer = null;
                Browse.unselectable();
                if (new_folder) {
                    this.del(Browse.show_message); // afterwards=Browse.show_message
                } else {
                    name.innerHTML = orig_innerHTML;
                }
                Notify.ServerError();
            }).bind(this)});
        editor.enterEditMode();
        Browse.in_placer = {file: this, editor: editor, new_folder: new_folder, name: name, name_old_innerHTML: orig_innerHTML};
    }
});

/*global HoverIconSwap*/
var BrowseActions = {
    option_dict: {
        'share_here': {"verb": 'share_existing', "icon": 'user_add', "text": 'Share this'},
        'share': {"verb": 'share_existing', "icon": 'user_add', "text": 'Share folder'},
        'share_opts_here': {"verb": 'share', "icon": 'user_add', "text": 'Sharing info'},
        'share_opts': {"verb": 'share', "icon": 'user_add', "text": 'Sharing info'},
        'revisions': {"verb": 'revisions', "icon": 'time', "text": 'Revisions'},

        'undelete': {
            "href": function () {
                return "/revisions" + this.where + "?undelete=yes";
            },
            "icon": "basket_remove",
            "text": "Undelete"
        },

        'copy_url': {
            "icon": "world_link",
            "text": 'Copy public URL',
            "public_href": function () {
                return "http://" + Constants.PUBSERVER + '/u/' + Constants.uid + this.where.substring(7);
            },
            "onclick": function (e) {
                BrowseActions.showCopyPublicUrlModal("http://" + Constants.PUBSERVER + '/u/' + Constants.uid + this.where.substring(7));
                Event.stop(e);
            }
        },

        'download': {
            "href": function () {
                return Constants.protocol + "://" + Constants.block + '/get' + this.where + this.hash + "&dl=1";
            },
            "icon": "page_white_go",
            "text": "Download file"
        },

        'view': {
            "href": function () {
                return Constants.protocol + "://" + Constants.block + '/get' + this.where + this.hash;
            },
            "icon": "page_white_magnify",
            "text": "View file"
        },

        'zipped_dl': {
            "href": function () {
                return Constants.protocol + "://" + Constants.block + '/zip' + this.where + this.hash;
            },
            "icon": "page_white_compressed",
            "text": "Download .ZIP"
        },

        'photos': {
            "href": function () {
                return '/photos' + this.where.substring(7);
            },
            "icon": "pictures",
            "text": "Gallery view"
        },

        'a_photo': {
            "href": function () {
                return '/photoshow' + this.where.substring(7);
            },
            "icon": "pictures",
            "text": "Gallery view"
        },

        'rejoin': {"verb": "rejoin", "icon": "folder_user", "text": "Rejoin share"},

        'restore': {
            "href": function () {
                return '/restore' + this.where + "?prev=" + encodeURIComponent(location.href);
            },
            "icon": "time_go",
            "text": "Restore files"
        },

        'full_restore': {
            "href": function () {
                return '/restore' + this.where + "?prev=" + encodeURIComponent(location.href);
            },
            "icon": "time_go",
            "text": "Restore folder"
        },

        'show_del': {
            "onclick": function (e) {
                Event.stop(e);
                location.href = BrowseURL.get_del_url(true);
            },
            "icon": "page_white_stack",
            "text": "Show deleted files"
        },

        'hide_del': {
            "onclick": function (e) {
                Event.stop(e);
                location.href = BrowseURL.get_del_url(false);
            },
            "icon": "page_white_stack",
            "text": "Hide deleted files"
        },

        'copy': {
            "icon": "page_white_copy",
            "text": 'Copy file to...',
            "onclick": function (e) {
                FileOps.show_copy(this.where);
                Event.stop(e);
            }
        },

        'copy_folder': {
            "icon": "folder_page",
            "text": 'Copy folder to...',
            "onclick": function (e) {
                FileOps.show_copy(this.where, true); // is_folder=true
                Event.stop(e);
            }
        },

        'move': {
            "icon": "page_white_put",
            "text": 'Move file to...',
            "onclick": function (e) {
                FileOps.show_move(this.where);
                Event.stop(e);
            }
        },

        'move_folder': {
            "icon": "folder_go",
            "text": 'Move folder to...',
            "onclick": function (e) {
                FileOps.show_move(this.where, true); // is_folder=true
                Event.stop(e);
            }
        },

        'rename': {
            "icon": "page_white_edit",
            "text": 'Rename file...',
            "onclick": function (e) {
                var file = Browse.find_file(this.where);
                if (file) {
                    file.edit();
                }
                Event.stop(e);
            }
        },

        'rename_folder': {
            "icon": "folder_edit",
            "text": 'Rename folder...',
            "onclick": function (e) {
                var file = Browse.find_file(this.where);
                if (file) {
                    file.edit();
                }
                Event.stop(e);
            }
        },

        'delete': {
            "icon": "page_white_delete",
            "text": 'Delete file...',
            "onclick": function (e) {
                FileOps.show_delete(this.where);
                Event.stop(e);
            }
        },

        'delete_folder': {
            "icon": "folder_delete",
            "text": 'Delete folder...',
            "onclick": function (e) {
                FileOps.show_delete(this.where, true); // is_folder=true
                Event.stop(e);
            }
        },

        'purge': {
            "icon": "time_delete",
            "text": "Purge",
            "onclick": function (e) {
                FileOps.show_purge(this.where);
                Event.stop(e);
            }
        },

        'purge_folder': {
            "icon": "time_delete",
            "text": "Purge folder",
            "onclick": function (e) {
                FileOps.show_purge(this.where, true); // is_folder=true
                Event.stop(e);
            }
        },

        'new_folder': {
            "icon": "folder_add",
            "text": 'Create folder',
            "onclick": function (e) {
                FileOps.inplace_new_folder(this.where);
                Event.stop(e);
                return false;
            }
        },

        'upload': {
            "icon": "page_white_get",
            "text": "Upload",
            "onclick": function (e) {
                FileOps.show_upload(this.where);
                Event.stop(e);
            }
        }
    },
    generate_li: function (where, what, hash, isActionBar) {
        var obj = Object.clone(BrowseActions.option_dict[what]);
        obj.where = where;
        obj.hash = hash ? "?w=" + hash : "";
        var li = new Element("li");

        var a  = new Element('a', {"target": "_top", "class": "background-icon", "href": obj.onclick ? "#" : obj.verb ? ("/" + obj.verb + where) : obj.href()});

        //sigh
        if (!isActionBar) {
            HoverIconSwap.register(a);
        }

        if (obj.onclick) {
            a.observe('click', (function (e) {
                obj.onclick(e);
                BrowseActions.kill_dropdowns();
            }).bindAsEventListener(obj));
        }
        var img = Sprite.make(obj.icon, {'class': 'icon_no_hover'});
        var img2 = Sprite.make(obj.icon + "_blue", {'class': 'icon_hover'});
        a.update(img);
        a.insert(img2);
        a.update(a.innerHTML + obj.text);

        li.update(a);

        return li;
    },
    fillActionUL: function (where, actions, ul, add_separator, hash) {
        ul.update(add_separator ? "<li class='action-separator'>|</li>" : "");
        if (actions && actions.length) {
            ul.show();
            actions.each(
                function (action) {
                    ul.insert(BrowseActions.generate_li(where, action, hash, true));
                }
            );
        } else {
            ul.hide();
        }
    },
    showFor: function (file) {
        return function () {
            $('browse-filename').update(file.caption.widthSplit().join("&#8203;<wbr>"));

            if (file.size == "None") {
                $('browse-filesize').update("<i>deleted</i>");
            } else if (file.size !== false) {
                $('browse-filesize').update(file.size);
            } else {
                $('browse-filesize').update();
            }

            if (file.ago) {
                $('browse-filemodified').update("Last modified " + file.ago);
            } else {
                $('browse-filemodified').update();
            }

            var ul = $('browse-file-actions').down("ul");
            BrowseActions.fillActionUL(file.where, file.main_actions, ul, true); // add_separator=true
            BrowseActions.fillActionUL(file.where, file.more_actions, $('more-file-actions'));
        };
    },
    clear: function (file) {
        $('browse-filename').update();
        $('browse-filesize').update();
        $('browse-filemodified').update();
        $('browse-file-actions').down("ul").update();
        $('more-file-actions').update();
    },
    more: function (link) {
        link = $(link);
        BrowseActions.more_link = link;
        link.blur();

        var pos = link.positionedOffset();
        var dim = link.getDimensions();

        BrowseActions.kill_dropdowns();
        var div = $('more-actions');

        link.style.border = div.style.border;
        BrowseActions.more_link.style.backgroundColor = "#e6f2ff";

        div.setStyle({'position': 'absolute', 'left': pos.left + "px", 'top': (pos.top + dim.height - 3) + 'px'});
        div.show();
        Event.observe(document, 'click', BrowseActions.hide_more);

        return false;
    },
    hide_more: function () {
        $('more-actions').hide();
        if (BrowseActions.more_link) {
            BrowseActions.more_link.style.border = '';
            BrowseActions.more_link.style.backgroundColor = '';
            Event.stopObserving(document, 'click', BrowseActions.hide_more);
        }
    },
    kill_dropdowns: function () {
        BrowseActions.hide_more();
        if ($('dropdown')) {
            $('dropdown').hide();
        }
    },
    dropdown: function (file, show_all, e) {
        var elt = file.div;
        if (e) {
            Event.stop(e);
        }
        if (!elt) {
            return;
        }

        var where = file.where;
        var hash = file.hash;
        var options = file.main_actions.concat(file.more_actions);

        if (file.rest_actions.length == 1) { // don't waste time with "More..." here
            show_all = true;
        }

        if (show_all) {
            options = options.concat(file.rest_actions);
        }

        if (!options.length) {
            return;
        }

        var dd = $("dropdown");
        if (dd) {
            Event.stopObserving(document, "click", dd.listener);
            dd = Util.yank(dd);

            if (Browse.more_link) {
                Event.stopObserving(Browse.more_link, 'click', Browse.more_link_action);
                Browse.more_link = null;
                Browse.more_link_action = null;
            }
        }

        elt = $(elt);
        var div = new Element("div", {id: "dropdown", "style": "position:absolute; left: 0px; top: 0px; margin:0;padding:0; width: 137px"});
        var menu = new Element("ul", {"class": "dropdown dropdown-lite note"});
        $A(options).each(function (option) {
            menu.insert(BrowseActions.generate_li(where, option, hash));
        });

        var more_link = null;
        if (!show_all && file.rest_actions.length) {
            more_link = (new Element("a", {"href": "#", "style": "text-align: right"})).update("More &#187;");
            Browse.more_link = more_link;
            Browse.more_link_action = BrowseActions.dropdown.curry(file, true); // show_all=true
            more_link.observe("click", Browse.more_link_action);
            var more_li = new Element("li");
            menu.insert(more_li);
            more_li.insert(more_link);
        }

        div.insert(menu);

        menu.listener =
            function (e) {
                var dd_elt = $("dropdown");
                if (dd_elt) {
                    dd_elt = Util.yank(dd_elt);
                }

                Event.stopObserving(document, "click", menu.listener);

                if (Browse.more_link) {
                    Event.stopObserving(Browse.more_link, 'click', Browse.more_link_action);
                    Browse.more_link = null;
                    Browse.more_link_action = null;
                }

                if (e.target.tagName != 'A' || e.target.href.length <= 2) {
                    Event.stop(e);
                }
            };

        Event.observe(document, "click", menu.listener);

        $(elt.offsetParent).insert(div);

        var pos;
        if (Util.ie6) {
            pos = {top: elt.offsetTop, left: elt.offsetLeft};
        } else {
            pos = elt.positionedOffset();
        }
        var dim = elt.getDimensions();
        var dimen = div.getDimensions();
        div.style.left = (pos.left - dimen.width + dim.width) + "px";
        div.style.top = (pos.top + dim.height - 1 + (Util.ie6 ? 71 : 0)) + "px"; // FIXME: horrible ie6 hack

        if (!Util.ie) {
            var vpos_top = div.cumulativeOffset().top - Util.scrollTop();
            if (dimen.height + vpos_top > (window.innerHeight || document.documentElement.clientHeight)) {
                setTimeout(function () {
                    div.scrollIntoView(false);
                    div = null;
                }, 100);
            }
        }
        window.focus();

        return false;
    },
    showCopyPublicUrlModal: function (url) {
        Modal.show("Copy Public URL", DomUtil.fromElm('copy-public-url'));
        $('public_url').setValue(url);
        $('public_url').select();
    }
};

var FileOps = {
    filename: function (path) {
        path = Util.normPath(decodeURIComponent(path));
        path = path.split("/");
        var filename = path.pop();

        if (filename === "") { // looks like top level AccessManager
            return "My AccessManager";
        }

        return filename;
    },
    dir_handler: function (path, obj) {
        if (typeof(obj) == 'string') {
            obj = $(obj);
        }

        var mydiv = obj.up('div');
        mydiv.addClassName("highlight");

        if (Modal.vars.selected && Modal.vars.selected != mydiv) {
            Modal.vars.selected.removeClassName("highlight");
        }

        Modal.vars.selected = mydiv;

        if (Modal.shown()) {
            obj.blur();
        }

        Modal.vars.selected_path = encodeURIComponent(path);
    },
    show_folder_pick: function (title, file, action, for_folder) {
        DomUtil.fillVal(FileOps.filename(file), 'folder-pick-file');
        Modal.show(title, DomUtil.fromElm('folder-pick'), {'where': file, 'action': action, 'folder': for_folder});

        // prep by selecting the first
        var first_link = $('first-treeview-link');
        if (!Util.ie) {
            first_link.onclick();
        }
    },
    show_copy: function (file_path, is_folder) {
        var f = is_folder ? "folder" : "file";
        DomUtil.fillVal("copy", 'folder-pick-action');
        DomUtil.fillVal("Copy " + f, 'folder-pick-action-text');
        DomUtil.fillVal(f, 'folder-pick-file-folder');
        FileOps.show_folder_pick("Copy " + f + " to...", file_path, FileOps.do_copy, is_folder);
    },
    show_move: function (file_path, is_folder) {
        var f = is_folder ? "folder" : "file";
        DomUtil.fillVal("move", 'folder-pick-action');
        DomUtil.fillVal("Move " + f, 'folder-pick-action-text');
        DomUtil.fillVal(f, 'folder-pick-file-folder');
        FileOps.show_folder_pick("Move " + f + " to...", file_path, FileOps.do_move, is_folder);
    },
    show_move_confirm: function (from, to, for_folder) {
        var f = for_folder ? "folder" : "file";
        DomUtil.fillVal(f, 'move-confirm-file-folder');
        DomUtil.fillVal(FileOps.filename(from), 'move-confirm-filename');
        DomUtil.fillVal(FileOps.filename(decodeURIComponent(to)), 'move-confirm-dest');
        DomUtil.fillVal("Move " + f, 'move-confirm-action-text');
        Modal.show("Move " + f + "?", DomUtil.fromElm('move-confirm'), {'from': from, 'to': to, 'for_folder': for_folder});
    },
    show_rename: function (file_path) {
        DomUtil.fillVal(FileOps.filename(file_path), 'rename-filename');
        Modal.show("Rename file", DomUtil.fromElm('rename-file'), {'where': file_path, 'action': FileOps.do_rename});
    },
    show_delete: function (file_path, is_folder) {
        var f = is_folder ? "folder" : "file";
        DomUtil.fillVal(FileOps.filename(file_path), 'delete-filename');
        DomUtil.fillVal(f, 'delete-file-folder');
        Modal.show("Delete " + f + "?", DomUtil.fromElm('delete-file'), {'where': file_path, 'action': FileOps.do_delete, 'folder': is_folder});
    },
    show_purge: function (file_path, is_folder) {
        var f = is_folder ? "folder" : "file";
        DomUtil.fillVal(FileOps.filename(file_path), 'purge-filename');
        DomUtil.fillVal(f, 'purge-file-folder');
        DomUtil.fillVal("Purge " + f, 'purge-action-text');
        Modal.show("Purge " + f + "?", DomUtil.fromElm('purge-file'), {'where': file_path, 'action': FileOps.do_purge, 'folder': is_folder});
    },
    show_new_folder: function (file_path) {
        Modal.show("Create folder", DomUtil.fromElm('new-folder'), {'where': file_path, 'action': FileOps.do_new_folder});
    },
    show_upload: function (folder_path) {
        DomUtil.fillVal(FileOps.filename(folder_path), 'upload-dest');
        var w = DomUtil.fromElm('upload-file');
        Modal.show("Upload", w, {'where': folder_path, 'action': FileOps.do_upload}, false, 600); // item_focus=false, modal_width=600
        Modal.track_resizes();

        Upload.set_dest(Util.normPath(decodeURIComponent(folder_path)));
        Upload.init(true); // late_game=true
    },
    do_copy: function (from, to) {
        Util.setCursor('progress');
        from = from || Modal.vars.where;
        to = to ? encodeURIComponent(to) : Modal.vars.selected_path;
        var file = FileOps.filename(from);
        var old_name = file;
        var folder_copy = Modal.vars.folder;

        if (!to) {
            Notify.ServerError("You should select a destination for the file.");
            Util.clearCursor();
            return;
        }

        if (folder_copy && Util.normDir(decodeURIComponent(to)).indexOf(Util.normDir(decodeURIComponent(from))) === 0) {
            Notify.ServerError("You cannot copy a folder into itself.");
            Util.clearCursor();
            return;
        }

        var copying_to_here = (decodeURIComponent(from) == Util.normPath(decodeURIComponent(to)) + "/" + file);

        var dbr = new Ajax.DBRequest("/cmd/copy" + from + "?to_path=" + to, {
            parameters: {folder: folder_copy ? "yes" : ""},
            job: !!folder_copy,
            progress_text: "Copying folder...",
            onSuccess: function (req) {
                var parts = req.responseText.split(":");
                var new_where = parts[0];
                var new_hash = parts[1];
                var name = FileOps.filename(new_where);

                if (copying_to_here) {
                    var file = Browse.find_file(from);
                    var where = file.where.replace_last(encodeURIComponent(old_name), encodeURIComponent(name));
                    var href = file.href.replace_last(escape(old_name), escape(name));
                    href = href.replace_last("w=" + file.hash, "w=" + new_hash);

                    var caption = name.snippet();

                    var f = new BrowseFile(file.icon, where, href, caption, name, file.size, file.bytes, file.ago, file.ts, file.str_actions, new_hash, file.dir, file.drop_target, true); // need_new=true

                    Browse.resort();
                }

                Notify.ServerSuccess("Copied '" + name.snippet() + "' successfully.");
            },
            cleanUp: function (req) {
                Util.clearCursor();
            }
        });
    },
    do_move: function (from, to, for_folder) {
        Util.setCursor('progress');
        from = from || Modal.vars.where;
        to = to ? encodeURIComponent(to) : Modal.vars.selected_path;
        var file = FileOps.filename(from);
        var folder_move = for_folder !== undefined ? for_folder : Modal.vars.folder;

        if (!to) {
            Notify.ServerError("You should select a destination for the file.");
            Util.clearCursor();
            return;
        }

        if (decodeURIComponent(from) == decodeURIComponent(to) + "/" + file) {
            Util.clearCursor();
            return;
        }

        if (folder_move && Util.normDir(decodeURIComponent(to)).indexOf(Util.normDir(decodeURIComponent(from))) === 0) {
            Notify.ServerError("You cannot move a folder into itself.");
            Util.clearCursor();
            return;
        }

        var dbr = new Ajax.DBRequest("/cmd/move" + from + "?to_path=" + to, {
            parameters: {folder: folder_move ? "yes" : ""},
            job: !!folder_move,
            progress_text: "Moving folder...",
            onSuccess: function (req) {
                Notify.ServerSuccess("Moved '" + FileOps.filename(from).snippet() + "' successfully.");
                var file = Browse.pull_file(from);
                file.move(file.dir ? Browse.refresh_drop_positions : false);
                if (file.dir) {
                    Browse.remove_drop_target(file);
                }
                TreeView.reset();
            },
            cleanUp: function (req) {
                Util.clearCursor();
            }
        });
    },
    do_rename: function (to_path) {
        Util.setCursor('progress');
        var from = Modal.vars.where;
        var to = encodeURIComponent(to_path);
        var dbr = new Ajax.DBRequest("/cmd/rename" + from + "?to_path=" + to, {
            onSuccess: function (req) {
                var parts = req.responseText.split(":");
                var new_where = parts[0];
                var new_hash = parts[1];
                var new_name = FileOps.filename(new_where);
                Notify.ServerSuccess("Renamed '" + FileOps.filename(from).snippet() + "' to '" + new_name.snippet() + "' successfully.");
                var file = Browse.find_file(from);
                file.rename(new_where, new_hash);
            },
            cleanUp: function (req) {
                Util.clearCursor();
            }
        });
    },
    do_delete: function () {
        Util.setCursor('progress');
        var from = Modal.vars.where;
        var folder_delete = Modal.vars.folder;
        var dbr = new Ajax.DBRequest("/cmd/delete" + from, {
            parameters: {folder: folder_delete ? "yes" : ""},
            job: !!folder_delete,
            progress_text: "Deleting folder...",
            onSuccess: function (req) {
                Notify.ServerSuccess("Deleted '" + FileOps.filename(from).snippet() + "' successfully.");
                var file = Browse.deleted_shown ? Browse.find_file(from) : Browse.pull_file(from);
                if (file) {
                    file.del(file.dir ? Browse.refresh_drop_positions : false);
                }
                if (file.dir) {
                    Browse.remove_drop_target(file);
                }
                TreeView.reset();
            },
            cleanUp: function (req) {
                Util.clearCursor();
            }
        });
    },
    do_purge: function () {
        Util.setCursor('progress');
        var from = Modal.vars.where;
        var dbr = new Ajax.DBRequest("/revisions" + from + "?purge=file&ajax=true", {
            parameters: {folder: Modal.vars.folder ? "yes" : ""},
            job: !!Modal.vars.folder,
            progress_text: "Purging folder...",
            onSuccess: function (req) {
                Notify.ServerSuccess("Purged '" + FileOps.filename(from).snippet() + "' successfully.");
                var file = Browse.pull_file(from);
                if (file) {
                    file.purge();
                }
            },
            cleanUp: function (req) {
                Util.clearCursor();
            }
        });
    },
    do_upload: function () {
        Util.setCursor('progress');

        $('dest-upload-folder').value = decodeURIComponent(Modal.vars.where);

        // submit the form, the loaded page will do the right thing
        $('upload-form').submit();

        frames['upload-frame'].onload =
            function (e) {
                var text = e.target.documentElement.textContent;
                if (text == 'winner!') {
                    Browse.reload();
                    Notify.ServerSuccess("Uploaded file successfully");
                } else {
                    Notify.ServerError();
                }
            };

        Util.clearCursor();
    },
    inplace_new_folder: function (where) {
        if (Browse.in_placer && Browse.in_placer.new_folder) {
            return;
        }

        var name = "New Folder";
        if (where.charAt(where.length - 1) != "/") {
            where += "/";
        }

        // make a placeholder
        Browse.hide_message();
        var f = new BrowseFile('folder', where, "/browse2" + where + name, name, name, false, 0, "", Util.ts(), "share zipped_dl upload||copy_folder move_folder rename_folder delete_folder", "", true, true, true); // size=false, bytes=0, is_dir=true, drop_target=true, need_new=true

        Browse.resort();

        f.div.scrollTo();
        f.edit(true); // new_folder=true
    }
};

var Forms = {
    submitOnlyOnce: function () {
        var ret = Forms.submitted !== true;
        Forms.submitted = true;

        return ret;
    },
    disable: function (me) {
    	if (me) {
    	    setTimeout(function () {
                me.disabled = true;
            }, 0);
    	}
    },
    clearInput: function (elm, value) {
        elm = $(elm);
        if (elm.value == value) {
            elm.value = "";
            elm.style.color = '#444444';
        }
    }
};

var Upgrade = {
    card_toggle: function (chosen) {
        return function (type) {
            var type_elm = $(type);

            if (type == chosen || !chosen) {
                type_elm.removeClassName("cc-icon-off");
            }
            else {
                type_elm.addClassName("cc-icon-off");
            }
        };
    },
    highlightCardtype: function () {
        var ccn = $('ccn');

        if (Upgrade.last_val == ccn.value) {
            return;
        }

        Upgrade.last_val = ccn.value;

        var ccnum = ccn.value;
        var first_two = ccnum.substr(0, 2);
        var all = $A(['visa', 'mastercard', 'amex']);
        var choice = null;

        if (ccnum.charAt(0) == '4') {
            choice = 'visa';
        }
        else if (first_two == '34' || first_two == '37') {
            choice = 'amex';
        }
        else if (parseInt(first_two, 10) >= 51 && parseInt(first_two, 10) <= 55) {
            choice = 'mastercard';
        }

        all.each(Upgrade.card_toggle(choice));
    },
    runCardHighlighter: function () {
        setInterval(Upgrade.highlightCardtype, 200);
    },
    highlightPlan: function () {
        var plans = $A(['fifty-plan', '100-plan', '250-plan', 'free-plan']);
        var checked = plans.map(Util.scry).find(function (x) {
            if (x) {
                return x.checked;
            }
        });

        if (Upgrade.last_checked == checked) {
            return;
        }

        if (Upgrade.last_checked) {
            Util.scry(Upgrade.last_checked.id + "-div").removeClassName("payment-option-selected");
        }

        if (checked) {
            Util.scry(checked.id + "-div").addClassName("payment-option-selected");

            // if (Upgrade.hasSub && checked.id == "free-plan")
            //   Upgrade.enableNext();
            // else if (!Upgrade.hasSub && checked.id == "free-plan")
            //   Upgrade.disableNext();
            // else if (Upgrade.hasSub && checked.id == "fifty-plan")
            //   Upgrade.enableNext();
            // else
            //   Upgrade.enableNext();
            //Upgrade.showPlanInfo(checked);
        }

        Upgrade.last_checked = checked;
    },
    enableNext: function () {
        var elm = $("next-button");
        elm.enable();
        elm.removeClassName("disabled-button");
    },
    disableNext: function () {
        var elm = $("next-button");
        elm.disable();
        elm.addClassName("disabled-button");
    },
    runPlanHighlighter: function () {
        setInterval(Upgrade.highlightPlan, 100);
    },
    showPlanInfo: function (plan) {
        var info = { "50-plan": "It's 50 GB", "100-plan": "It's 100 GB", "250-plan": "It's 250 GB", "free-plan": "It's free"};
        Util.scry("plan-specific").update(info[plan.id]);
    }
};

var Home = {
    showScreencast: function () {

        if (!Home.screencast)
        {
            var scale = new Effect.Scale('spacer', 0, {});
            var slidedown = new Effect.SlideDown('screencast', {afterFinish: function () {
                $('screencast-placeholder').hide();
                $('actual-screencast').show();
            }});
            Home.screencast = true;
        }
        else
        {
            $('spacer').style.height = "135px";
            $('screencast').hide();
            $('actual-screencast').hide();
            Home.screencast = false;
        }
    },
    showFeedback: function (e) {
        if (e) {
            Event.stop(e);
        }

        Modal.show("Tell us what you think", $('feedback-div'), {icon: 'information'}, $('feedback_textarea'));
        return false;
    },
    hide: function (elm, value) {
        var ef = Effect.BlindFadeUp($(elm).up('div'), {duration:0.5});
        var request = new Ajax.DBRequest("/hide/"+value);
    }
};

var Install = {
    pingForLinkedHost: function (share_path) {
        var request = new Ajax.Request("/host_linked",
            {method: 'get',
            onSuccess: function () {
                location.href = "/share" + share_path;
            },
            onFailure: function () {
                setTimeout(Install.pingForLinkedHost.curry(share_path), 3000);
            }
        });
    }
};

var Tour = {
    load: function (page, link) {
        if (!this.tab_count) {
            Tour.countTabs();
        }

        page = Math.max(0, Math.min(parseInt(page, 10), Tour.tab_count - 1));

        if (page == Tour.c_page) {
            return false;
        }

        var selected = Util.childElement(Util.scry('tour-tabs-list'), Tour.curPage());
        var new_selected = Util.childElement(Util.scry('tour-tabs-list'), page);
        var request = new Ajax.Updater(Util.scry('tour-page'), "/tour?p=" + page.toString(), {
            method: 'get',
            onComplete: function () {
                if (link) {
                    link.blur();
                }

                Tour.showhideNext(page);
                Tour.c_page = page;

                window.scrollTo(0, 0);
                location.href = "#" + (page + 1);

                selected.removeClassName('tour-tab-selected');
                new_selected.addClassName('tour-tab-selected');
            }
        });

        return false;
    },
    c_page: null,
    curPage: function () {
        if (Tour.c_page === null) {
            var match = /\?p=(\d)/.exec(location.href);
            Tour.c_page = (match) ? match[1] : 0;
        }

        return parseInt(Tour.c_page, 10);
    },
    tab_count: null,
    showhideNext: function (page) {
        if (Tour.tab_count === null) {
            Tour.countTabs();
        }

        if (page + 1 < Tour.tab_count) {
            Util.scry('next-page-link').style.visibility = 'visible';
        }
        else {
            Util.scry('next-page-link').style.visibility = 'hidden';
        }
    },
    loadNext: function (link) {
        Tour.load(Tour.curPage() + 1, link);
        return false;
    },
    countTabs: function () {
        Tour.tab_count = Util.scry('tour-tabs-list').childElements().length;
    },
    checkUrl: function () {
        var p = Util.url_hash();
        if (p.length) {
            p = parseInt(p, 10) - 1;
        }
        else {
            var r = /\?p=(\d*)/.exec(location.href);

            if (r) {
                p = parseInt(r[1], 10);
            }
            else {
                p = 0;
            }
        }

        Tour.load(p);
    }
};

var Dropdown = {
    mouseOut: false,
    hidden: false,
    init: function () {
        var selected = document.getElementsByClassName("selected");
        if (selected[0]) {
            Dropdown.original_selected = selected[0];
        }
    },
    showDropdown: function (link, e, elm) {
        if (!elm) {
            Dropdown.hideDropdown(e);
            return;
        }
        link.addClassName("hover");

        Event.stop(e);
        var dd_elm = $(elm);

        if (Dropdown.current != dd_elm) {
            if (Dropdown.current) {
                Dropdown.hideDropdown(e);
            }
        }

        Dropdown.current = dd_elm;
        Dropdown.current_link = link;

        Event.observe(dd_elm, 'mouseover', Dropdown.mouseOverList);
        Event.observe(dd_elm, 'mouseout', Dropdown.mouseOutList);
        Event.observe(e.target, 'mouseout', Dropdown.mouseOutList);
        Event.observe(e.target, 'mouseover', Dropdown.mouseOverList);
        Event.observe(document, 'click', Dropdown.hideDropdown);
        Dropdown.hidden = false;
        clearTimeout(Dropdown.currentOpen);
        // wait a sec before we show it
        Dropdown.currentOpen = setTimeout(
            function () {
                dd_elm.style.display = "block";
            }, 1);

        return false;
    },
    hideDropdown: function (e) {
        if (!Dropdown.current_link) {
            return;
        }

        clearTimeout(Dropdown.currentOpen);
        Dropdown.current_link.removeClassName("hover");
        var dd_elm = Dropdown.current;
        if (!dd_elm) {
            return;
        }
        if (Dropdown.original_selected) {
            //  Dropdown.original_selected.addClassName("selected");
            if (dd_elm.up() != Dropdown.original_selected) {
                dd_elm.up().removeClassName("selected");
            }
        }

        dd_elm.hide();

        Event.stopObserving(window, 'click', Dropdown.hideDropdown);
        Event.stopObserving(dd_elm, 'mouseover', Dropdown.mouseOverList);
        Event.stopObserving(dd_elm, 'mouseout', Dropdown.mouseOutList);

        clearTimeout(Dropdown.mouseOut);

        Dropdown.mouseOut = false;
        Dropdown.hidden = true;
    },
    mouseOverList: function () {
        clearTimeout(Dropdown.mouseOut);
        Dropdown.mouseOut = false;
    },
    mouseOutList: function (e, bigger_num) {
        clearTimeout(Dropdown.mouseOut);
        Dropdown.mouseOut = setTimeout(Dropdown.hideDropdown, 500);
    }
};


var LiveSearch = {
    search: function (search_string, update_elm, action) {
        search_string = search_string.strip();

        if (search_string.length < 3) {
            $(update_elm).update("");
        }
        else {
            var request = new Ajax.Request(action, {
                parameters: {'search_string': search_string},
                method: 'get',
                onSuccess: function (req) {
                    $(update_elm).update(req.responseText);
                    LiveSearch.highlight(update_elm, search_string);
                }
            });
        }
    },

    highlight: function (elm, search_string) {
        var regx = new RegExp(RegExp.escape(search_string), "i");
        elm = $(elm);
        $$('.livesearch_result_a').each(
            function (elm) {
                elm.innerHTML = elm.innerHTML.gsub(regx, function (match) {
                    return '<span class=\'highlight\'>' + match[0] + '</span>';
                });
            }
        );
        $$('.livesearch_result_p').each(
            function (elm) {
                elm.innerHTML = elm.innerHTML.stripTags().gsub(regx, function (match) {
                    return '<span class=\'highlight\'>' + match[0] + '</span>';
                });
            }
        );

    },
    MAX_RESULTS: 10
};


var Email = {
    mailto: function (link, name, domain) {
        if (!domain) {
            domain = "virtualbx.com";
        }

        link.href = "mailto:" + name + "@" + domain;
        link.onMouseover = null;
    }
};

var SuggestionInput = {
    register: function (elm) {
        elm = $(elm);
        var rel_form = elm.up('form');

        var init_val = elm.getValue();
        if (init_val === '' || init_val === elm.title) {
            elm.setValue(elm.title); // title holds the default value
            elm.defaulted = true;
        }
        else {
            elm.defaulted = false;
            elm.addClassName('suggestion-input-unfaded');
        }

        elm.observe('blur', SuggestionInput.blur);
        elm.observe('focus', SuggestionInput.focus);

        if (rel_form) {
            if (!elm.id) {
                elm.id = "r_elm_id_" + Math.random().toString();
            }

            rel_form.observe('submit', SuggestionInput.blank(elm.id));
        }
    },
    register_all: function () {
        $$('.suggestion-input').each(SuggestionInput.register);
    },
    blank: function (elm_id) {
        return function () {
            var elm = Util.scry(elm_id);
            if (!elm) {
                return;
            }

            if (elm && elm.defaulted) {
                elm.setValue('');
            }
        };
    },
    do_blank: function (elm_id) {
        SuggestionInput.blank(elm_id)();
    },
    focus: function (e) {
        var elm = $(e.target);
        if (!elm) {
            return;
        }

        if (elm.defaulted) {
            elm.addClassName('suggestion-input-unfaded');
            elm.setValue('');
            elm.defaulted = false;
        }
    },
    blur: function (e) {
        var elm = $(e.target);
        if (!elm) {
            return;
        }

        if (elm.getValue() === '') {
            elm.removeClassName('suggestion-input-unfaded');
            elm.setValue(elm.title);
            elm.defaulted = true;
        }
    }
};
Event.observe(window, 'load', SuggestionInput.register_all);

var HoverIconSwap = {
    register_all: function () {
        $$('.background-icon').each(HoverIconSwap.register);
    },
    register: function (elm) {
        elm = $(elm);

        elm.observe('mouseover', HoverIconSwap.mouseover);
        elm.observe('mouseout', HoverIconSwap.mouseout);
    },
    mouseover: function (e) {
        var elm = $(e.target);

        elm.addClassName("hover_swap");
    },
    mouseout: function (e) {
        var elm = $(e.target);

        elm.removeClassName("hover_swap");
    },
    getFileName: function (elm) {
        var fileName = elm.src.split("/");
        return fileName[fileName.length - 1];
    }
};
document.observe("dom:loaded", HoverIconSwap.register_all);


var SharingMembers = {
    register_all: function () {
        $$('.dropdown-link-a').each(SharingMembers.register);
        Event.observe(document, 'click', SharingMembers.kill_current);
    },
    register: function (elm) {
        elm = $(elm);

        elm.observe('mouseover', SharingMembers.mouseover);
        elm.observe('mouseout', SharingMembers.mouseout);
        elm.observe('click', SharingMembers.click);

    },
    mouseover: function (e) {
        var elm = $(e.target);

        if (!elm) {
            return;
        }

        elm.addClassName("hover");
    },
    mouseout: function (e) {
        var elm = $(e.target);

        if (!elm) {
            return;
        }

        elm.removeClassName("hover");
    },
    click: function (e) {
        Event.stop(e);
        var elm = $(e.target);

        if (!elm) {
            return;
        }

        SharingMembers.kill_current(false);

        SharingMembers.current = elm;

        elm.addClassName("clicked");
        elm.up().style.zIndex = "50";

        var next = elm.next();
        if (next) {
            next.style.display = "block";
        }
    },
    kill_current: function (e) {
        if (!SharingMembers.current) {
            return;
        }

        var elm = $(SharingMembers.current);

        if (!elm) {
            return;
        }

        if (elm.next())
        {
            elm.removeClassName("clicked");
            elm.up().style.zIndex = "";
            elm.next().style.display = "none";
        }

    }
};

var Sprite = {
    SPACER: "/static/images/icons/icon_spacer.gif",
    src: function (elm, name) {
        elm = $(elm);
        Sprite.clear(elm);
        elm.addClassName("s_" + name);
    },
    replace: function (elm, orig, replacement) {
        elm.className = elm.className.replace(orig, replacement);
    },
    clear: function (elm) {
        elm = $(elm);
        elm.className = elm.classNames().reject(function (x) {
            return !x.indexOf("s_");
        }).join(" ");
    },
    make: function (name, attr) {
        attr.src = Sprite.SPACER;
        attr['class'] = " " + attr['class'] || "";
        attr['class'] = "sprite s_" + name + attr['class'];

        return new Element("img", attr);
    },
    get: function (elm) {
        return elm.className;
    },
    set: function (elm, val) {
        elm.className = val;
        elm.src = Sprite.SPACER;
    }
};

var ActAsBlock = {
    elm_list: ["margin-left", "margin-right", "padding-left", "padding-right", "border-left-width", "border-right-width"],
    parent_list: ["padding-left", "padding-right", "border-left-width", "border-right-width"],

    register: function (e, elm) {
        elm = elm || document.body;
        var elms = $(elm).getElementsByClassName("act_as_block");

        for (var i = 0; i < elms.length; i = i + 1) {
            ActAsBlock.resize(elms[i]);
        }

    },

    resize: function (elm) {
        elm = $(elm);

        var parent = elm.up();
        var elm_total = Util.sumStyles(elm, ActAsBlock.elm_list);
        var parent_total = Util.sumStyles(parent, ActAsBlock.parent_list);

        elm.style.width = "1px";

        var width = (parent.getWidth() - elm_total - parent_total);

        if (width > 0) {
            elm.style.width = width + "px";
        }
    }
};
Event.observe(window, 'load', ActAsBlock.register);

var Inbox = {
    overQuotaModal: function (button, folder_name, used, free) {
        Modal.show('Quota Warning', $('overquota-modal'));

        var modal_content = $('modal-content');

        $$('.shared-folder-name').invoke("update", folder_name);
        $$('.shared-folder-size').invoke("update", used);

        var submit = modal_content.getElementsByClassName('joinbutton');
        submit[0].onclick = function () {
            button.onclick = null;
            button.click();
        };
        submit[0].value = "Join " + folder_name;

        return false;
    }
};

