second
[josuexyz/.git] / node_modules / negotiator / lib / charset.js
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 }