twitst4tz

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

stringify.js (8230B)


      1 'use strict';
      2 
      3 var utils = require('./utils');
      4 var formats = require('./formats');
      5 var has = Object.prototype.hasOwnProperty;
      6 
      7 var arrayPrefixGenerators = {
      8     brackets: function brackets(prefix) { // eslint-disable-line func-name-matching
      9         return prefix + '[]';
     10     },
     11     comma: 'comma',
     12     indices: function indices(prefix, key) { // eslint-disable-line func-name-matching
     13         return prefix + '[' + key + ']';
     14     },
     15     repeat: function repeat(prefix) { // eslint-disable-line func-name-matching
     16         return prefix;
     17     }
     18 };
     19 
     20 var isArray = Array.isArray;
     21 var push = Array.prototype.push;
     22 var pushToArray = function (arr, valueOrArray) {
     23     push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
     24 };
     25 
     26 var toISO = Date.prototype.toISOString;
     27 
     28 var defaults = {
     29     addQueryPrefix: false,
     30     allowDots: false,
     31     charset: 'utf-8',
     32     charsetSentinel: false,
     33     delimiter: '&',
     34     encode: true,
     35     encoder: utils.encode,
     36     encodeValuesOnly: false,
     37     formatter: formats.formatters[formats['default']],
     38     // deprecated
     39     indices: false,
     40     serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching
     41         return toISO.call(date);
     42     },
     43     skipNulls: false,
     44     strictNullHandling: false
     45 };
     46 
     47 var stringify = function stringify( // eslint-disable-line func-name-matching
     48     object,
     49     prefix,
     50     generateArrayPrefix,
     51     strictNullHandling,
     52     skipNulls,
     53     encoder,
     54     filter,
     55     sort,
     56     allowDots,
     57     serializeDate,
     58     formatter,
     59     encodeValuesOnly,
     60     charset
     61 ) {
     62     var obj = object;
     63     if (typeof filter === 'function') {
     64         obj = filter(prefix, obj);
     65     } else if (obj instanceof Date) {
     66         obj = serializeDate(obj);
     67     } else if (generateArrayPrefix === 'comma' && isArray(obj)) {
     68         obj = obj.join(',');
     69     }
     70 
     71     if (obj === null) {
     72         if (strictNullHandling) {
     73             return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset) : prefix;
     74         }
     75 
     76         obj = '';
     77     }
     78 
     79     if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) {
     80         if (encoder) {
     81             var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset);
     82             return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset))];
     83         }
     84         return [formatter(prefix) + '=' + formatter(String(obj))];
     85     }
     86 
     87     var values = [];
     88 
     89     if (typeof obj === 'undefined') {
     90         return values;
     91     }
     92 
     93     var objKeys;
     94     if (isArray(filter)) {
     95         objKeys = filter;
     96     } else {
     97         var keys = Object.keys(obj);
     98         objKeys = sort ? keys.sort(sort) : keys;
     99     }
    100 
    101     for (var i = 0; i < objKeys.length; ++i) {
    102         var key = objKeys[i];
    103 
    104         if (skipNulls && obj[key] === null) {
    105             continue;
    106         }
    107 
    108         if (isArray(obj)) {
    109             pushToArray(values, stringify(
    110                 obj[key],
    111                 typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
    112                 generateArrayPrefix,
    113                 strictNullHandling,
    114                 skipNulls,
    115                 encoder,
    116                 filter,
    117                 sort,
    118                 allowDots,
    119                 serializeDate,
    120                 formatter,
    121                 encodeValuesOnly,
    122                 charset
    123             ));
    124         } else {
    125             pushToArray(values, stringify(
    126                 obj[key],
    127                 prefix + (allowDots ? '.' + key : '[' + key + ']'),
    128                 generateArrayPrefix,
    129                 strictNullHandling,
    130                 skipNulls,
    131                 encoder,
    132                 filter,
    133                 sort,
    134                 allowDots,
    135                 serializeDate,
    136                 formatter,
    137                 encodeValuesOnly,
    138                 charset
    139             ));
    140         }
    141     }
    142 
    143     return values;
    144 };
    145 
    146 var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
    147     if (!opts) {
    148         return defaults;
    149     }
    150 
    151     if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
    152         throw new TypeError('Encoder has to be a function.');
    153     }
    154 
    155     var charset = opts.charset || defaults.charset;
    156     if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
    157         throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
    158     }
    159 
    160     var format = formats['default'];
    161     if (typeof opts.format !== 'undefined') {
    162         if (!has.call(formats.formatters, opts.format)) {
    163             throw new TypeError('Unknown format option provided.');
    164         }
    165         format = opts.format;
    166     }
    167     var formatter = formats.formatters[format];
    168 
    169     var filter = defaults.filter;
    170     if (typeof opts.filter === 'function' || isArray(opts.filter)) {
    171         filter = opts.filter;
    172     }
    173 
    174     return {
    175         addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix,
    176         allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
    177         charset: charset,
    178         charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
    179         delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter,
    180         encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode,
    181         encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
    182         encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
    183         filter: filter,
    184         formatter: formatter,
    185         serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
    186         skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
    187         sort: typeof opts.sort === 'function' ? opts.sort : null,
    188         strictNullHandling: typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling
    189     };
    190 };
    191 
    192 module.exports = function (object, opts) {
    193     var obj = object;
    194     var options = normalizeStringifyOptions(opts);
    195 
    196     var objKeys;
    197     var filter;
    198 
    199     if (typeof options.filter === 'function') {
    200         filter = options.filter;
    201         obj = filter('', obj);
    202     } else if (isArray(options.filter)) {
    203         filter = options.filter;
    204         objKeys = filter;
    205     }
    206 
    207     var keys = [];
    208 
    209     if (typeof obj !== 'object' || obj === null) {
    210         return '';
    211     }
    212 
    213     var arrayFormat;
    214     if (opts && opts.arrayFormat in arrayPrefixGenerators) {
    215         arrayFormat = opts.arrayFormat;
    216     } else if (opts && 'indices' in opts) {
    217         arrayFormat = opts.indices ? 'indices' : 'repeat';
    218     } else {
    219         arrayFormat = 'indices';
    220     }
    221 
    222     var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
    223 
    224     if (!objKeys) {
    225         objKeys = Object.keys(obj);
    226     }
    227 
    228     if (options.sort) {
    229         objKeys.sort(options.sort);
    230     }
    231 
    232     for (var i = 0; i < objKeys.length; ++i) {
    233         var key = objKeys[i];
    234 
    235         if (options.skipNulls && obj[key] === null) {
    236             continue;
    237         }
    238         pushToArray(keys, stringify(
    239             obj[key],
    240             key,
    241             generateArrayPrefix,
    242             options.strictNullHandling,
    243             options.skipNulls,
    244             options.encode ? options.encoder : null,
    245             options.filter,
    246             options.sort,
    247             options.allowDots,
    248             options.serializeDate,
    249             options.formatter,
    250             options.encodeValuesOnly,
    251             options.charset
    252         ));
    253     }
    254 
    255     var joined = keys.join(options.delimiter);
    256     var prefix = options.addQueryPrefix === true ? '?' : '';
    257 
    258     if (options.charsetSentinel) {
    259         if (options.charset === 'iso-8859-1') {
    260             // encodeURIComponent('&#10003;'), the "numeric entity" representation of a checkmark
    261             prefix += 'utf8=%26%2310003%3B&';
    262         } else {
    263             // encodeURIComponent('✓')
    264             prefix += 'utf8=%E2%9C%93&';
    265         }
    266     }
    267 
    268     return joined.length > 0 ? prefix + joined : '';
    269 };