function decode(s) {
    try {
        return decodeURIComponent(s).replace(/\r\n|\r|\n/g, "\r\n");
    } catch (e) {
        return "Opera percent-decode error";
    }
}

function encode(s) {
    try {
        return encodeURIComponent(s);
    } catch (e) {
        return "Opera%20percent-encode%20error";
    }
}

function getLocationQueryStringHvalue(name) {
    var qs = window.location.search.toString();
    if (qs.indexOf("?") == -1) {
        return "";
    }
    qs = qs.substr(1);
    var result = "";
    qs.replace(/([^=&]+)=([^&]*)/g, function(match, hname, hvalue) {
        if (hname == name) {
            result = decode(hvalue);
        }
    });
    return result;
}

function escapeInvalidHH(s) {
    return s.replace(/%(?![0-9A-F]{2})/gi, function() {
        return "%25";
    });
}

function escapeUnsafeHH(s) {
    return s.replace(/%00|%01|%02|%03|%04|%05|%06|%07|%08|%0B|%0C|%0E|%0F|%10|%11|%12|%13|%14|%15|%16|%17|%18|%19|%1A|%1B|%1C|%1D|%1E|%1F/gi, function(match) {
        return encode(match);
    });
}

function MailtoURIParser(s) {
    this.parse(escapeUnsafeHH(escapeInvalidHH("to=" + s.substr(7).replace(/\?/, "&"))));
}

function filterSingleLine(s) {
    return s.replace(/\r|\n/g, "");
}

MailtoURIParser.prototype = {
    parse : function (dataset) {
        this.to = this.subject = this.body = this.cc = this.bcc = "";
        var ref = this;
        dataset.replace(/([^=&]+)=([^&]*)/g, function(match, hname, hvalue){
            hname = decode(hname).toLowerCase();
            if (hname == "to") {
                if (hvalue != "") {
                    if (ref.to != "")
                        ref.to += "%2C%20";
                    ref.to += hvalue;
                }
            } else if (hname == "cc") {
                if (hvalue != "") {
                    if (ref.cc != "")
                        ref.cc += "%2C%20";
                    ref.cc += hvalue;
                }
            } else if (hname == "bcc") {
                if (hvalue != "") {
                    if (ref.bcc != "")
                        ref.bcc += "%2C%20";
                    ref.bcc += hvalue;
                }
            } else if (hname == "subject") {
                ref.subject = hvalue;
            } else if (hname == "body") {
                if (!(hvalue == "" && ref.body == "")) {
                    if (ref.body != "")
                        ref.body += "%0D%0A";
                    ref.body += hvalue;
                }
            }
        });
        this.to = encode(filterSingleLine(decode(this.to)));
        this.subject = encode(filterSingleLine(decode(this.subject)));
        this.body = encode(decode(this.body));
        this.cc = encode(filterSingleLine(decode(this.cc)));
        this.bcc = encode(filterSingleLine(decode(this.bcc)));
    },
    format : function(s) {
        var ref = this;
        return s.replace(/(%%)|(%T)|(%S)|(%M)|(%C)|(%B)|(%w)/g, function(match, a, b, c, d, e, f, g) {
            if (a) return "%";
            if (b) return ref.to;
            if (c) return ref.subject;
            if (d) return ref.body;
            if (e) return ref.cc;
            if (f) return ref.bcc;
            if (g) return encode("mailto:" + ref.to + "?subject=" + ref.subject + "&body=" + ref.body + "&cc=" + ref.cc + "&bcc=" + ref.bcc);
        });
    }
};
