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, htmlMode, newUI, generic) {
    // Escape & between 'mailto:' and '?' and convert the first '?' to '&'.
    s = "to=" + s.substr(7);
    var qm = s.indexOf('?');
    if (qm != -1) {
        var query = s.substr(qm + 1);
        s = s.substring(0, qm).replace(/&/g, "%26");
        if (query != "") {
            s += "&" + query;
        }
    } else {
        s = s.replace(/&/g, "%26");
    }
    this.parse(escapeUnsafeHH(escapeInvalidHH(s)), htmlMode, newUI, generic);
}

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

MailtoURIParser.prototype = {
    parse : function (dataset, htmlMode, newUI, generic) {
        if (typeof htmlMode != "boolean") {
            htmlMode = false;
        }
        if (typeof newUI != "boolean") {
            newUI = false;
        }
        if (typeof generic != "boolean") {
            generic = false;
        }
        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)));
        
        // Yahoo HTML Mode workarounds
        if (htmlMode) {
            
        }
        
        // Yahoo mail "'" workaround
        this.to = this.to.replace(/\'/g, "%27");
        this.subject = this.subject.replace(/\'/g, "%27");
        this.body = this.body.replace(/\'/g, "%27");
        this.cc = this.cc.replace(/\'/g, "%27");
        this.bcc = this.bcc.replace(/\'/g, "%27")
        
        // Use with "http://compose.mail.yahoo.com/?To=%T&Subj=%S&Body=%M&Cc=%C&Bcc=%B";
        if (!newUI && !generic) {
            this.to = encode(this.to);
            this.subject = encode(this.subject);
            this.body = encode(this.body);
            this.cc = encode(this.cc);
            this.bcc = encode(this.bcc);
        } else if (newUI && !generic) {
            //  New UI redirects hate newlines for some reason
            this.to = this.to.replace(/%0D%0A/gi, "");
            this.subject = this.subject.replace(/%0D%0A/gi, "");
            this.body = this.body.replace(/%0D%0A/gi, "");
            this.cc = this.cc.replace(/%0D%0A/gi, "");
            this.bcc = this.bcc.replace(/%0D%0A/gi, "");
        }
        
        
    },
    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);
        });
    }
};