second
[josuexyz/.git] / node_modules / negotiator / lib / language.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 = preferredLanguages;
17 module.exports.preferredLanguages = preferredLanguages;
18
19 /**
20  * Module variables.
21  * @private
22  */
23
24 var simpleLanguageRegExp = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
25
26 /**
27  * Parse the Accept-Language header.
28  * @private
29  */
30
31 function parseAcceptLanguage(accept) {
32   var accepts = accept.split(',');
33
34   for (var i = 0, j = 0; i < accepts.length; i++) {
35     var language = parseLanguage(accepts[i].trim(), i);
36
37     if (language) {
38       accepts[j++] = language;
39     }
40   }
41
42   // trim accepts
43   accepts.length = j;
44
45   return accepts;
46 }
47
48 /**
49  * Parse a language from the Accept-Language header.
50  * @private
51  */
52
53 function parseLanguage(str, i) {
54   var match = simpleLanguageRegExp.exec(str);
55   if (!match) return null;
56
57   var prefix = match[1],
58     suffix = match[2],
59     full = prefix;
60
61   if (suffix) full += "-" + suffix;
62
63   var q = 1;
64   if (match[3]) {
65     var params = match[3].split(';')
66     for (var j = 0; j < params.length; j++) {
67       var p = params[j].split('=');
68       if (p[0] === 'q') q = parseFloat(p[1]);
69     }
70   }
71
72   return {
73     prefix: prefix,
74     suffix: suffix,
75     q: q,
76     i: i,
77     full: full
78   };
79 }
80
81 /**
82  * Get the priority of a language.
83  * @private
84  */
85
86 function getLanguagePriority(language, accepted, index) {
87   var priority = {o: -1, q: 0, s: 0};
88
89   for (var i = 0; i < accepted.length; i++) {
90     var spec = specify(language, accepted[i], index);
91
92     if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
93       priority = spec;
94     }
95   }
96
97   return priority;
98 }
99
100 /**
101  * Get the specificity of the language.
102  * @private
103  */
104
105 function specify(language, spec, index) {
106   var p = parseLanguage(language)
107   if (!p) return null;
108   var s = 0;
109   if(spec.full.toLowerCase() === p.full.toLowerCase()){
110     s |= 4;
111   } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) {
112     s |= 2;
113   } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
114     s |= 1;
115   } else if (spec.full !== '*' ) {
116     return null
117   }
118
119   return {
120     i: index,
121     o: spec.i,
122     q: spec.q,
123     s: s
124   }
125 };
126
127 /**
128  * Get the preferred languages from an Accept-Language header.
129  * @public
130  */
131
132 function preferredLanguages(accept, provided) {
133   // RFC 2616 sec 14.4: no header = *
134   var accepts = parseAcceptLanguage(accept === undefined ? '*' : accept || '');
135
136   if (!provided) {
137     // sorted list of all languages
138     return accepts
139       .filter(isQuality)
140       .sort(compareSpecs)
141       .map(getFullLanguage);
142   }
143
144   var priorities = provided.map(function getPriority(type, index) {
145     return getLanguagePriority(type, accepts, index);
146   });
147
148   // sorted list of accepted languages
149   return priorities.filter(isQuality).sort(compareSpecs).map(function getLanguage(priority) {
150     return provided[priorities.indexOf(priority)];
151   });
152 }
153
154 /**
155  * Compare two specs.
156  * @private
157  */
158
159 function compareSpecs(a, b) {
160   return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;
161 }
162
163 /**
164  * Get full language string.
165  * @private
166  */
167
168 function getFullLanguage(spec) {
169   return spec.full;
170 }
171
172 /**
173  * Check if a spec has any quality.
174  * @private
175  */
176
177 function isQuality(spec) {
178   return spec.q > 0;
179 }