| /*! | 
|  * content-type | 
|  * Copyright(c) 2015 Douglas Christopher Wilson | 
|  * MIT Licensed | 
|  */ | 
|   | 
| 'use strict' | 
|   | 
| /** | 
|  * RegExp to match *( ";" parameter ) in RFC 7231 sec 3.1.1.1 | 
|  * | 
|  * parameter     = token "=" ( token / quoted-string ) | 
|  * token         = 1*tchar | 
|  * tchar         = "!" / "#" / "$" / "%" / "&" / "'" / "*" | 
|  *               / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" | 
|  *               / DIGIT / ALPHA | 
|  *               ; any VCHAR, except delimiters | 
|  * quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE | 
|  * qdtext        = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text | 
|  * obs-text      = %x80-FF | 
|  * quoted-pair   = "\" ( HTAB / SP / VCHAR / obs-text ) | 
|  */ | 
| var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g // eslint-disable-line no-control-regex | 
| var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/ // eslint-disable-line no-control-regex | 
| var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/ | 
|   | 
| /** | 
|  * RegExp to match quoted-pair in RFC 7230 sec 3.2.6 | 
|  * | 
|  * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text ) | 
|  * obs-text    = %x80-FF | 
|  */ | 
| var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g // eslint-disable-line no-control-regex | 
|   | 
| /** | 
|  * RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6 | 
|  */ | 
| var QUOTE_REGEXP = /([\\"])/g | 
|   | 
| /** | 
|  * RegExp to match type in RFC 7231 sec 3.1.1.1 | 
|  * | 
|  * media-type = type "/" subtype | 
|  * type       = token | 
|  * subtype    = token | 
|  */ | 
| var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/ | 
|   | 
| /** | 
|  * Module exports. | 
|  * @public | 
|  */ | 
|   | 
| exports.format = format | 
| exports.parse = parse | 
|   | 
| /** | 
|  * Format object to media type. | 
|  * | 
|  * @param {object} obj | 
|  * @return {string} | 
|  * @public | 
|  */ | 
|   | 
| function format (obj) { | 
|   if (!obj || typeof obj !== 'object') { | 
|     throw new TypeError('argument obj is required') | 
|   } | 
|   | 
|   var parameters = obj.parameters | 
|   var type = obj.type | 
|   | 
|   if (!type || !TYPE_REGEXP.test(type)) { | 
|     throw new TypeError('invalid type') | 
|   } | 
|   | 
|   var string = type | 
|   | 
|   // append parameters | 
|   if (parameters && typeof parameters === 'object') { | 
|     var param | 
|     var params = Object.keys(parameters).sort() | 
|   | 
|     for (var i = 0; i < params.length; i++) { | 
|       param = params[i] | 
|   | 
|       if (!TOKEN_REGEXP.test(param)) { | 
|         throw new TypeError('invalid parameter name') | 
|       } | 
|   | 
|       string += '; ' + param + '=' + qstring(parameters[param]) | 
|     } | 
|   } | 
|   | 
|   return string | 
| } | 
|   | 
| /** | 
|  * Parse media type to object. | 
|  * | 
|  * @param {string|object} string | 
|  * @return {Object} | 
|  * @public | 
|  */ | 
|   | 
| function parse (string) { | 
|   if (!string) { | 
|     throw new TypeError('argument string is required') | 
|   } | 
|   | 
|   // support req/res-like objects as argument | 
|   var header = typeof string === 'object' | 
|     ? getcontenttype(string) | 
|     : string | 
|   | 
|   if (typeof header !== 'string') { | 
|     throw new TypeError('argument string is required to be a string') | 
|   } | 
|   | 
|   var index = header.indexOf(';') | 
|   var type = index !== -1 | 
|     ? header.slice(0, index).trim() | 
|     : header.trim() | 
|   | 
|   if (!TYPE_REGEXP.test(type)) { | 
|     throw new TypeError('invalid media type') | 
|   } | 
|   | 
|   var obj = new ContentType(type.toLowerCase()) | 
|   | 
|   // parse parameters | 
|   if (index !== -1) { | 
|     var key | 
|     var match | 
|     var value | 
|   | 
|     PARAM_REGEXP.lastIndex = index | 
|   | 
|     while ((match = PARAM_REGEXP.exec(header))) { | 
|       if (match.index !== index) { | 
|         throw new TypeError('invalid parameter format') | 
|       } | 
|   | 
|       index += match[0].length | 
|       key = match[1].toLowerCase() | 
|       value = match[2] | 
|   | 
|       if (value.charCodeAt(0) === 0x22 /* " */) { | 
|         // remove quotes | 
|         value = value.slice(1, -1) | 
|   | 
|         // remove escapes | 
|         if (value.indexOf('\\') !== -1) { | 
|           value = value.replace(QESC_REGEXP, '$1') | 
|         } | 
|       } | 
|   | 
|       obj.parameters[key] = value | 
|     } | 
|   | 
|     if (index !== header.length) { | 
|       throw new TypeError('invalid parameter format') | 
|     } | 
|   } | 
|   | 
|   return obj | 
| } | 
|   | 
| /** | 
|  * Get content-type from req/res objects. | 
|  * | 
|  * @param {object} | 
|  * @return {Object} | 
|  * @private | 
|  */ | 
|   | 
| function getcontenttype (obj) { | 
|   var header | 
|   | 
|   if (typeof obj.getHeader === 'function') { | 
|     // res-like | 
|     header = obj.getHeader('content-type') | 
|   } else if (typeof obj.headers === 'object') { | 
|     // req-like | 
|     header = obj.headers && obj.headers['content-type'] | 
|   } | 
|   | 
|   if (typeof header !== 'string') { | 
|     throw new TypeError('content-type header is missing from object') | 
|   } | 
|   | 
|   return header | 
| } | 
|   | 
| /** | 
|  * Quote a string if necessary. | 
|  * | 
|  * @param {string} val | 
|  * @return {string} | 
|  * @private | 
|  */ | 
|   | 
| function qstring (val) { | 
|   var str = String(val) | 
|   | 
|   // no need to quote tokens | 
|   if (TOKEN_REGEXP.test(str)) { | 
|     return str | 
|   } | 
|   | 
|   if (str.length > 0 && !TEXT_REGEXP.test(str)) { | 
|     throw new TypeError('invalid parameter value') | 
|   } | 
|   | 
|   return '"' + str.replace(QUOTE_REGEXP, '\\$1') + '"' | 
| } | 
|   | 
| /** | 
|  * Class to represent a content type. | 
|  * @private | 
|  */ | 
| function ContentType (type) { | 
|   this.parameters = Object.create(null) | 
|   this.type = type | 
| } |