twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

language.js (3408B)


      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 }