charset.js (3081B)
1 /** 2 * negotiator 3 * Copyright(c) 2012 Isaac Z. Schlueter 4 * Copyright(c) 2014 Federico Romero 5 * Copyright(c) 2014-2015 Douglas Christopher Wilson 6 * MIT Licensed 7 */ 8 9 'use strict'; 10 11 /** 12 * Module exports. 13 * @public 14 */ 15 16 module.exports = preferredCharsets; 17 module.exports.preferredCharsets = preferredCharsets; 18 19 /** 20 * Module variables. 21 * @private 22 */ 23 24 var simpleCharsetRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/; 25 26 /** 27 * Parse the Accept-Charset header. 28 * @private 29 */ 30 31 function parseAcceptCharset(accept) { 32 var accepts = accept.split(','); 33 34 for (var i = 0, j = 0; i < accepts.length; i++) { 35 var charset = parseCharset(accepts[i].trim(), i); 36 37 if (charset) { 38 accepts[j++] = charset; 39 } 40 } 41 42 // trim accepts 43 accepts.length = j; 44 45 return accepts; 46 } 47 48 /** 49 * Parse a charset from the Accept-Charset header. 50 * @private 51 */ 52 53 function parseCharset(str, i) { 54 var match = simpleCharsetRegExp.exec(str); 55 if (!match) return null; 56 57 var charset = match[1]; 58 var q = 1; 59 if (match[2]) { 60 var params = match[2].split(';') 61 for (var j = 0; j < params.length; j++) { 62 var p = params[j].trim().split('='); 63 if (p[0] === 'q') { 64 q = parseFloat(p[1]); 65 break; 66 } 67 } 68 } 69 70 return { 71 charset: charset, 72 q: q, 73 i: i 74 }; 75 } 76 77 /** 78 * Get the priority of a charset. 79 * @private 80 */ 81 82 function getCharsetPriority(charset, accepted, index) { 83 var priority = {o: -1, q: 0, s: 0}; 84 85 for (var i = 0; i < accepted.length; i++) { 86 var spec = specify(charset, accepted[i], index); 87 88 if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) { 89 priority = spec; 90 } 91 } 92 93 return priority; 94 } 95 96 /** 97 * Get the specificity of the charset. 98 * @private 99 */ 100 101 function specify(charset, spec, index) { 102 var s = 0; 103 if(spec.charset.toLowerCase() === charset.toLowerCase()){ 104 s |= 1; 105 } else if (spec.charset !== '*' ) { 106 return null 107 } 108 109 return { 110 i: index, 111 o: spec.i, 112 q: spec.q, 113 s: s 114 } 115 } 116 117 /** 118 * Get the preferred charsets from an Accept-Charset header. 119 * @public 120 */ 121 122 function preferredCharsets(accept, provided) { 123 // RFC 2616 sec 14.2: no header = * 124 var accepts = parseAcceptCharset(accept === undefined ? '*' : accept || ''); 125 126 if (!provided) { 127 // sorted list of all charsets 128 return accepts 129 .filter(isQuality) 130 .sort(compareSpecs) 131 .map(getFullCharset); 132 } 133 134 var priorities = provided.map(function getPriority(type, index) { 135 return getCharsetPriority(type, accepts, index); 136 }); 137 138 // sorted list of accepted charsets 139 return priorities.filter(isQuality).sort(compareSpecs).map(function getCharset(priority) { 140 return provided[priorities.indexOf(priority)]; 141 }); 142 } 143 144 /** 145 * Compare two specs. 146 * @private 147 */ 148 149 function compareSpecs(a, b) { 150 return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0; 151 } 152 153 /** 154 * Get full charset string. 155 * @private 156 */ 157 158 function getFullCharset(spec) { 159 return spec.charset; 160 } 161 162 /** 163 * Check if a spec has any quality. 164 * @private 165 */ 166 167 function isQuality(spec) { 168 return spec.q > 0; 169 }