Javascript mailto_uri_parser object documentation for : parse: The parse function takes a mailto URI, normalizes it, filters it, parses it and decodes it into an object of raw name|value pairs. I'm calling these objects KeyMaps. KeyMap objects just looks like this: var example = {"subject" : "test", "body" : "line1\r\nline2"}; Usage: var fields = mailto_uri_parser.parse("mailto:?subject=test"); alert(mailto_uri_parser.utils.getEncodedValueFromKeyMap(fields, "subject")) // "test" The parser follows RFC2368 for how to deal with hnames: 1. When a duplicate hname is encountered, its value overwrites the previous one. 2. Hname matching is case-insensitive. In addition: 1. Pairs with a final empty value are stripped from the object. 2. Newlines in names and values are normalized to \r\n. 3. Functions assume everything's UTF-8. 4. Unsafe %HH are treated literally and unsafe raw chars are encoded as %HH. 5. Newlines are stripped from to, cc, bcc and subject values. (No other values are stripped as it's unknown what other hnames this should apply to. The parser uses an array of hnames to decide what hnames newline stripping applies to, so you could always add more hnames to the array.) parseSpecial: This parse function does things a little differently. It supports joining of values for duplicate hnames. However, because the joining method for each hname can be different, you need to pass in a type list object to specify how each hname should be handled. Example: var typeList = { "to" : ["address", true], "cc" : ["address", true], "bcc" : ["address", true], "subject" : ["single-line", true], "body" : ["multi-line", false] }; (Each name in the list must be lowercase). The first element in the array is the type. It can be address, single-line or multi-line. How each type is handled: address - Join all non-empty values for the hname with ", " multi-line - Join the first non-empty value and all values after that (even if they're empty) with "\r\n". single-line - Always use the last value for an hname. (This just happens to be what RFC2368 implies and what the regular parse function does. But, using it allows you to control the newline stripping. See below.) You can add support for another type in the parser if needed. It's just a case of adding another "else if" condition. The second element in the array is a boolean, which tells the parser to strip newlines from the name's value or not. If the type is something else or the hname the parser sees is not in the type list, duplicates will be treated the same way as the regular parse function does. (Note though that if you specify an unknown type for an hname in the type list, you can use this to treat the name like the regular parse functiion, but also control the stripping of newlines from its values) If you do not pass in a typeList the default one above will be used. Usage: var fields = mailto_uri_parser.parseSpecial("mailto:?cc=1&cc=2&body=line1&body=line2", typeList); alert(mailto_uri_parser.utils.getValueFromKeyMap(fields, "subject")); // "1, 2" alert(mailto_uri_parser.utils.getValueFromKeyMap(fields, "body")); // "line1\r\nline2" utils: The utils sub-object contains a bunch of functions that are not only useful to the parse functions, but also useful when used directly. Usage: mailto_uri_parser.utils.functionName(args); functions: trim(s) - Just trims a string of white-space on both sides. escapeInvalidHH(s) - Because decodeURIComponent throws an exception when it hits invalid %HH (like "%3Y"), you need to escape the invalid %HH as %25HH before using decodeURIComponent on the string. escapeUnsafeHH(s) - converts unsafe %HH to %25HH so that when the value is percent-decoded, unsafe chars don't get decoded to their raw form. See the function for a list of %HH that are considered unsafe. escapeUnsafeRaw(s) - Converts unsafe raw characters in percent-decoded values to %HH, so that when the value is percent-encoded, the unsafe %HH come out as %25HH. See the function for the list of code points that are considered unsafe. normalizeNewlinesToCRLF(s) - converts stray \r and stray \n to \r\n normalizeNewlinesToLF(s) - converts \r\n, and stray \r to \n. encode(s) - percent-encodes a string. It normalizes the newlines to \r\n and falls back to a percent-encoded error string when there's an exception. encodeAlreadyEncoded(s) - percent-encodes an already-percent-encoded string. It falls back to a double-percent-encoded error string when there's an exception. decode(s) - percent-decodes a string. It makes use of all the escape and normalize functions above to get clean percent-decoded values. It falls back to an error string when there's an exception. filterNewlines(s) - strips \r and \n normalizeEncoding(s) - percent-decodes a string and re-percent-encodes it. This basically filters and normalizes everything so that only %HH that need to be encoded are and so there are no unsafe %HH in the string. createDatasetFromMailtoURI(uri) - This takes a mailto URI and converts it to "hname1=hvalue&hname2=hvalue" so that it can be split by '&'. This function makes sure that '&' before '?' in a mailto URI are converted to %26 in the dataset so they don't cause the dataset to be split in the wrong spots. This function also trims the uri string and checks that it begins with a case-insensitive "mailto:' before doing anything. If the uri string is not a mailto URI, an exception is thrown. Also, since fragment identifiers currently should not be used in a mailto URI, the first # and everything after it in the URI is stripped off to discourage use of fragment identifiers. You can remove the replace(/#.+/, "") if you just want all # to be normalized to %23 instead (which is probably what the creator of the URI meant). In the future, if fragment identifiers are actually used for something in mailto URIs, all the code will have to be modified and take that into account. But, will worry about that if and when it happens. getValueFromKeyMap(map, name) - gets the raw value from the KeyMap. If name isn't in the KeyMap (using hasOwnProperty for checking), an empty string is returned instead. name is also converted to lowercase before checking. getEncodedValueFromKeyMap(map, name) - encoded version of the above. cleanKeyMapOfEmptyValues(map) - Takes a key map and returns a new one that doesn't have any names with empty values. convertKeyMapNamesToLowerCase(map) - Takes a KeyMap and returns a new one with all the names in lowercase. Other functions use this to convert the KeyMap to lowercase before they search through it. createURIFromKeyMap(map) - Creates a properly-encoded, normalized and clean mailto URI from the key map. The following two are really just useful wrappers: normalizeMailtoURI(uri) - Tears apart a mailto URI (by parsing it etc.) and puts it back together all nice and neat. (makes use of createURIFromKeyMap) normalizeMailtoURISpecial(uri, typeList) - Same thing except that it accepts a type list argument, which it uses to pass to the parseSpecial parse function. This function is very useful as you can take a mailto URI with duplicate hvalues, join those values and produce a normalized mailto URI with no duplicate hnames, all in one shot. formatStringFromKeyMap(map, s, encode_replacements=true) - Takes a keymap and a template string. It searchs for replacement keys in the string and replaces them with the values in the KeyMap that they represent. Setting encoded_replacements to false will cause the values to not be percent-encoded when they're inserted. (You usually want them to be percent-encoded.) The main use for this is webmail compose template strings like this: "http://example.org/compose.php?uri=%s&to=[to]&subject=[subject]&body=[body]&cc=[cc]&bcc=[bcc]". Here's the full syntax for the template string: /* formatStringFromKeyMap syntax for s argument [ starts the bracket state [[ avoids starting the bracket state \[ = [ in the bracket state (escaping [ with \ in the bracket state is only necessary if the [ is the first char inside the brackets) \] = ] in the bracket state \\ = \ in the bracket state \ + anything else is treated literally in the bracket state ] (or end of string) ends bracket state string found inside brackets is converted to lowercase before checking for a match examples: [to] = to hvalue [subject] = subject hvalue [body] = body hvalue [cc] = cc hvalue [bcc] = bcc hvalue [1[] = hvalue for hname 1[ [1\[] = hvalue for hname 1[ [\[\\\]] = hvalue for hname [\] (These examples are outside a string. If inside a string, each \ in these examples needs to be escaped with \) If an hname inside the brackets is not supported, the brackets and everything in them are discarded Outside of bracket state: %s = whole mailto uri %% = % % not followed by s or % is treated literally Again, setting the encode_replacements argument to false will insert the values without percent-encoding them. You usually want it set to true. */ Note: The parser does not validate anything. It just deals with arbitrary encoded and decoded values. For example, it doesn't take a percent-decoded value and check that it's an address and run it through an address-spec validator or anything. Mailto URI validator and composer: Some examples: var fields = mailto_uri_parser.parseSpecial("mailto:%22a%5C%5Cb%22%20%3Cemail1%40example.com%3E%2C%20email2%40example.com?to=&to=email3%40example.com%2C%20email4%40example.com&to=email5%40example.com%2C%20email6%40example.com&to=&to=&subject=You%20should%20not%20see%20me%2E&subject=last%20call%20for%20cats%20%26%20dogs%2E&body=&body=&body=&body=line1%0D%0Aline2&body=&body=&body=&body=line6&body=line7%0D%0A1+2+3+4+5%0D%0AColumn1A%09Column1B%09Column1C%09Column1D%0D%0A%E2%88%9A&cc=%22a%5C%5Cb%22%20%3Csomeone1%40example.com%3E%2C%20someone2%40example.com&cc=&cc=someone3%40example.com%2C%20someone4%40example.com%2C%20%22foo%20%5C%22bar%5C%22%22%20%3Cfoo%40example%2Ecom%3E&cc=&cc=&bcc=%22a%5C%5Cb%22%20%3Csomeoneelse1%40example.com%3E%2C%20someoneelse2%40example.com&bcc=&bcc=someoneelse3%40example.com%2C%20someoneelse4%40example.com&bcc=&bcc="); var compose = "http://squirrelmailserver.com/src/compose.php?send_to=[to]&subject=[subject]&body=[body]&send_to_cc=[cc]&send_to_bcc=[bcc]"; var compose2 = "https://mail.google.com/mail/?extsrc=mailto&url=%s"; alert(mailto_uri_parser.utils.formatStringFromKeyMap(fields, compose)); alert(mailto_uri_parser.utils.formatStringFromKeyMap(fields, compose2)); alert(mailto_uri_parser.utils.getValueFromKeyMap(fields, "body")); alert(mailto_uri_parser.utils.getEncodedValueFromKeyMap(fields, "body"));