170 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			170 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | /** | ||
|  |  * negotiator | ||
|  |  * Copyright(c) 2012 Isaac Z. Schlueter | ||
|  |  * Copyright(c) 2014 Federico Romero | ||
|  |  * Copyright(c) 2014-2015 Douglas Christopher Wilson | ||
|  |  * MIT Licensed | ||
|  |  */ | ||
|  | 
 | ||
|  | 'use strict'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Module exports. | ||
|  |  * @public | ||
|  |  */ | ||
|  | 
 | ||
|  | module.exports = preferredCharsets; | ||
|  | module.exports.preferredCharsets = preferredCharsets; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Module variables. | ||
|  |  * @private | ||
|  |  */ | ||
|  | 
 | ||
|  | var simpleCharsetRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Parse the Accept-Charset header. | ||
|  |  * @private | ||
|  |  */ | ||
|  | 
 | ||
|  | function parseAcceptCharset(accept) { | ||
|  |   var accepts = accept.split(','); | ||
|  | 
 | ||
|  |   for (var i = 0, j = 0; i < accepts.length; i++) { | ||
|  |     var charset = parseCharset(accepts[i].trim(), i); | ||
|  | 
 | ||
|  |     if (charset) { | ||
|  |       accepts[j++] = charset; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   // trim accepts
 | ||
|  |   accepts.length = j; | ||
|  | 
 | ||
|  |   return accepts; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Parse a charset from the Accept-Charset header. | ||
|  |  * @private | ||
|  |  */ | ||
|  | 
 | ||
|  | function parseCharset(str, i) { | ||
|  |   var match = simpleCharsetRegExp.exec(str); | ||
|  |   if (!match) return null; | ||
|  | 
 | ||
|  |   var charset = match[1]; | ||
|  |   var q = 1; | ||
|  |   if (match[2]) { | ||
|  |     var params = match[2].split(';') | ||
|  |     for (var j = 0; j < params.length; j++) { | ||
|  |       var p = params[j].trim().split('='); | ||
|  |       if (p[0] === 'q') { | ||
|  |         q = parseFloat(p[1]); | ||
|  |         break; | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   return { | ||
|  |     charset: charset, | ||
|  |     q: q, | ||
|  |     i: i | ||
|  |   }; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Get the priority of a charset. | ||
|  |  * @private | ||
|  |  */ | ||
|  | 
 | ||
|  | function getCharsetPriority(charset, accepted, index) { | ||
|  |   var priority = {o: -1, q: 0, s: 0}; | ||
|  | 
 | ||
|  |   for (var i = 0; i < accepted.length; i++) { | ||
|  |     var spec = specify(charset, accepted[i], index); | ||
|  | 
 | ||
|  |     if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { | ||
|  |       priority = spec; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   return priority; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Get the specificity of the charset. | ||
|  |  * @private | ||
|  |  */ | ||
|  | 
 | ||
|  | function specify(charset, spec, index) { | ||
|  |   var s = 0; | ||
|  |   if(spec.charset.toLowerCase() === charset.toLowerCase()){ | ||
|  |     s |= 1; | ||
|  |   } else if (spec.charset !== '*' ) { | ||
|  |     return null | ||
|  |   } | ||
|  | 
 | ||
|  |   return { | ||
|  |     i: index, | ||
|  |     o: spec.i, | ||
|  |     q: spec.q, | ||
|  |     s: s | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Get the preferred charsets from an Accept-Charset header. | ||
|  |  * @public | ||
|  |  */ | ||
|  | 
 | ||
|  | function preferredCharsets(accept, provided) { | ||
|  |   // RFC 2616 sec 14.2: no header = *
 | ||
|  |   var accepts = parseAcceptCharset(accept === undefined ? '*' : accept || ''); | ||
|  | 
 | ||
|  |   if (!provided) { | ||
|  |     // sorted list of all charsets
 | ||
|  |     return accepts | ||
|  |       .filter(isQuality) | ||
|  |       .sort(compareSpecs) | ||
|  |       .map(getFullCharset); | ||
|  |   } | ||
|  | 
 | ||
|  |   var priorities = provided.map(function getPriority(type, index) { | ||
|  |     return getCharsetPriority(type, accepts, index); | ||
|  |   }); | ||
|  | 
 | ||
|  |   // sorted list of accepted charsets
 | ||
|  |   return priorities.filter(isQuality).sort(compareSpecs).map(function getCharset(priority) { | ||
|  |     return provided[priorities.indexOf(priority)]; | ||
|  |   }); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Compare two specs. | ||
|  |  * @private | ||
|  |  */ | ||
|  | 
 | ||
|  | function compareSpecs(a, b) { | ||
|  |   return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Get full charset string. | ||
|  |  * @private | ||
|  |  */ | ||
|  | 
 | ||
|  | function getFullCharset(spec) { | ||
|  |   return spec.charset; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Check if a spec has any quality. | ||
|  |  * @private | ||
|  |  */ | ||
|  | 
 | ||
|  | function isQuality(spec) { | ||
|  |   return spec.q > 0; | ||
|  | } |