27

9

Quoted directly from user Brian Webster from this thread.

The FogBugz wiki has a nice feature where it will periodically save a draft to the server of the edits you're making to a page, so that if your browser crashes or the pages accidentally gets closed, you don't lose all your work. However, this same feature doesn't exist when you're replying to an e-mail or editing a case, and I can't count the number of times that the web page I'm on is accidentally closed for one reason or another and I lose the reply I just spent 20 minutes drafting.

Fog Creek Case FC2127806

flag

1 Answer

6

I ran into the same problem, and created the following BugMonkey script to give me some rudimentary save and restore functionality:

name:        Save Drafts
description: Save drafts of bug events (on modern browsers)
author:      Daniel LeCheminant
version:     1.0.0

js:
(function() {
    var storage = window.localStorage;
    var popup = window.api.PopupManager.newPopup("Drafts");

    var init = function() {
        var ixLastDraft = -1;

        setInterval(function() {
            $("div.body.editable textarea:not([ixDraft])").each(function() {
                var textarea = this;
                var ixDraft = ixLastDraft = nextDraft(ixLastDraft);

                $(this)
                .attr("ixDraft", ixDraft)
                .keyup(function() {
                    save(ixDraft, $(this).val());
                });

                $(this).closest(".editable").find("input.dlgButton")
                .click(function() {
                    deleteDraft(ixDraft);
                });

                var target1 = $(this).closest(".bugevent").find(".summary:last")
                var target2 = target1.find("div");

                $("<a>")
                .css({ cursor: "pointer", "text-decoration": "underline" })
                .addClass("action")
                .css($.browser.msie ? { "margin-left": "60px"} : { "float": "right" })
                .text("Drafts")
                .appendTo(target2.length ? target2 : target1)
                .click(function() {
                    var draftList = $("<div>");
                    for (var ixDraft = 0; ixDraft < 100; ixDraft++) {
                        var sText = load(ixDraft);
                        if (sText && sText.length > 0) {
                            var row = $("<div>")
                            .css({
                                "width": "230px",
                                "border": "1px dotted #ccc",
                                "background-color": "#f8f8f8",
                                padding: "4px",
                                margin: "2px"
                            })
                            .appendTo(draftList);

                            $("<img>")
                            .attr("src", StaticContentUrl("images/delete.gif"))
                            .addClass("draftDeleter")
                            .css({
                                "vertical-align": "top",
                                "margin-right": "8px",
                                "cursor": "pointer",
                                "float": "left"
                            })
                            .appendTo(row);

                            $("<div>")
                            .addClass("draftSelector")
                            .css({
                                "width": "200px",
                                "max-height": "100px",
                                "text-overflow": "ellipsis",
                                "font-size": "10px",
                                overflow: "hidden",
                                "cursor": "pointer",
                                "display": "inline-block",
                                "float": "right"
                            })
                            .attr("title", sText)
                            .text(sText)
                            .appendTo(row);

                            $("<div>").css("clear", "both").appendTo(row);

                            row.contents().attr("ixDraft", ixDraft);
                        }
                    };

                    if (!draftList.find("div").length) {
                        $("<div>").text("No Saved Drafts").appendTo(draftList);
                    }

                    popup.setHtml(draftList.html());
                    popup.showPopup(); popup.hide(); // Workaround positioning bug
                    popup.showPopup(this);

                    $("div.draftSelector")
                    .click(function() {
                        var ixDraft = $(this).attr("ixDraft");

                        $(textarea).val(load(ixDraft));
                        popup.hide();
                    })

                    $("img.draftDeleter")
                    .click(function() {
                        var ixDraft = $(this).attr("ixDraft");
                        deleteDraft(ixDraft);
                        popup.hide();
                    });
                });
            });
        }, 250);

        return true;
    };

    var save = function(ixDraft, text) {
        storage["draft_" + ixDraft] = text;
    };

    var load = function(ixDraft) {
        return storage["draft_" + ixDraft];
    };

    var deleteDraft = function(ixDraft) {
        storage.removeItem("draft_" + ixDraft);
    };

    var nextDraft = function(last) {
        for (var i = last + 1; i < 100; i++) {
            var sText = load(i);
            if (!sText || !sText) return i;
        }
        return -1;
    }

    $(function() {
        if (!$("#BugFields").length || !storage) return;
        /* Handle switching from view to edit */
        TabManager.renderView = (function(fnOld) {
            return function(F) {
                return fnOld.call(TabManager, F) && init();
            };
        })(TabManager.renderView);

        init();
    });
})();

It keeps track of your edits as you type, and if the window is closed for some reason, you can get back the saved text by clicking on the desired draft from a popup:

Restoring a draft

This takes advantage of local storage, so it's only supported by modern browsers like IE8, FF and Chrome.

Until Fog Creek adds this functionality, this workaround may be sufficient.

link|flag
Nice! A few comments: (1) The draft is deleted if you press Cancel. The problem here is that pressing Cancel doesn't prompt for confirmation, so you lose your edits if you accidentally press Cancel instead of Send. (2) Doesn't work with Rich Text emails. – danielm Mar 26 2011 at 0:49

Your Answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.