utils.js
1 'use strict'; 2 3 var formats = require('./formats'); 4 5 var has = Object.prototype.hasOwnProperty; 6 var isArray = Array.isArray; 7 8 var hexTable = (function () { 9 var array = []; 10 for (var i = 0; i < 256; ++i) { 11 array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase()); 12 } 13 14 return array; 15 }()); 16 17 var compactQueue = function compactQueue(queue) { 18 while (queue.length > 1) { 19 var item = queue.pop(); 20 var obj = item.obj[item.prop]; 21 22 if (isArray(obj)) { 23 var compacted = []; 24 25 for (var j = 0; j < obj.length; ++j) { 26 if (typeof obj[j] !== 'undefined') { 27 compacted.push(obj[j]); 28 } 29 } 30 31 item.obj[item.prop] = compacted; 32 } 33 } 34 }; 35 36 var arrayToObject = function arrayToObject(source, options) { 37 var obj = options && options.plainObjects ? { __proto__: null } : {}; 38 for (var i = 0; i < source.length; ++i) { 39 if (typeof source[i] !== 'undefined') { 40 obj[i] = source[i]; 41 } 42 } 43 44 return obj; 45 }; 46 47 var merge = function merge(target, source, options) { 48 /* eslint no-param-reassign: 0 */ 49 if (!source) { 50 return target; 51 } 52 53 if (typeof source !== 'object' && typeof source !== 'function') { 54 if (isArray(target)) { 55 target.push(source); 56 } else if (target && typeof target === 'object') { 57 if ( 58 (options && (options.plainObjects || options.allowPrototypes)) 59 || !has.call(Object.prototype, source) 60 ) { 61 target[source] = true; 62 } 63 } else { 64 return [target, source]; 65 } 66 67 return target; 68 } 69 70 if (!target || typeof target !== 'object') { 71 return [target].concat(source); 72 } 73 74 var mergeTarget = target; 75 if (isArray(target) && !isArray(source)) { 76 mergeTarget = arrayToObject(target, options); 77 } 78 79 if (isArray(target) && isArray(source)) { 80 source.forEach(function (item, i) { 81 if (has.call(target, i)) { 82 var targetItem = target[i]; 83 if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') { 84 target[i] = merge(targetItem, item, options); 85 } else { 86 target.push(item); 87 } 88 } else { 89 target[i] = item; 90 } 91 }); 92 return target; 93 } 94 95 return Object.keys(source).reduce(function (acc, key) { 96 var value = source[key]; 97 98 if (has.call(acc, key)) { 99 acc[key] = merge(acc[key], value, options); 100 } else { 101 acc[key] = value; 102 } 103 return acc; 104 }, mergeTarget); 105 }; 106 107 var assign = function assignSingleSource(target, source) { 108 return Object.keys(source).reduce(function (acc, key) { 109 acc[key] = source[key]; 110 return acc; 111 }, target); 112 }; 113 114 var decode = function (str, defaultDecoder, charset) { 115 var strWithoutPlus = str.replace(/\+/g, ' '); 116 if (charset === 'iso-8859-1') { 117 // unescape never throws, no try...catch needed: 118 return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape); 119 } 120 // utf-8 121 try { 122 return decodeURIComponent(strWithoutPlus); 123 } catch (e) { 124 return strWithoutPlus; 125 } 126 }; 127 128 var limit = 1024; 129 130 /* eslint operator-linebreak: [2, "before"] */ 131 132 var encode = function encode(str, defaultEncoder, charset, kind, format) { 133 // This code was originally written by Brian White (mscdex) for the io.js core querystring library. 134 // It has been adapted here for stricter adherence to RFC 3986 135 if (str.length === 0) { 136 return str; 137 } 138 139 var string = str; 140 if (typeof str === 'symbol') { 141 string = Symbol.prototype.toString.call(str); 142 } else if (typeof str !== 'string') { 143 string = String(str); 144 } 145 146 if (charset === 'iso-8859-1') { 147 return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) { 148 return '%26%23' + parseInt($0.slice(2), 16) + '%3B'; 149 }); 150 } 151 152 var out = ''; 153 for (var j = 0; j < string.length; j += limit) { 154 var segment = string.length >= limit ? string.slice(j, j + limit) : string; 155 var arr = []; 156 157 for (var i = 0; i < segment.length; ++i) { 158 var c = segment.charCodeAt(i); 159 if ( 160 c === 0x2D // - 161 || c === 0x2E // . 162 || c === 0x5F // _ 163 || c === 0x7E // ~ 164 || (c >= 0x30 && c <= 0x39) // 0-9 165 || (c >= 0x41 && c <= 0x5A) // a-z 166 || (c >= 0x61 && c <= 0x7A) // A-Z 167 || (format === formats.RFC1738 && (c === 0x28 || c === 0x29)) // ( ) 168 ) { 169 arr[arr.length] = segment.charAt(i); 170 continue; 171 } 172 173 if (c < 0x80) { 174 arr[arr.length] = hexTable[c]; 175 continue; 176 } 177 178 if (c < 0x800) { 179 arr[arr.length] = hexTable[0xC0 | (c >> 6)] 180 + hexTable[0x80 | (c & 0x3F)]; 181 continue; 182 } 183 184 if (c < 0xD800 || c >= 0xE000) { 185 arr[arr.length] = hexTable[0xE0 | (c >> 12)] 186 + hexTable[0x80 | ((c >> 6) & 0x3F)] 187 + hexTable[0x80 | (c & 0x3F)]; 188 continue; 189 } 190 191 i += 1; 192 c = 0x10000 + (((c & 0x3FF) << 10) | (segment.charCodeAt(i) & 0x3FF)); 193 194 arr[arr.length] = hexTable[0xF0 | (c >> 18)] 195 + hexTable[0x80 | ((c >> 12) & 0x3F)] 196 + hexTable[0x80 | ((c >> 6) & 0x3F)] 197 + hexTable[0x80 | (c & 0x3F)]; 198 } 199 200 out += arr.join(''); 201 } 202 203 return out; 204 }; 205 206 var compact = function compact(value) { 207 var queue = [{ obj: { o: value }, prop: 'o' }]; 208 var refs = []; 209 210 for (var i = 0; i < queue.length; ++i) { 211 var item = queue[i]; 212 var obj = item.obj[item.prop]; 213 214 var keys = Object.keys(obj); 215 for (var j = 0; j < keys.length; ++j) { 216 var key = keys[j]; 217 var val = obj[key]; 218 if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) { 219 queue.push({ obj: obj, prop: key }); 220 refs.push(val); 221 } 222 } 223 } 224 225 compactQueue(queue); 226 227 return value; 228 }; 229 230 var isRegExp = function isRegExp(obj) { 231 return Object.prototype.toString.call(obj) === '[object RegExp]'; 232 }; 233 234 var isBuffer = function isBuffer(obj) { 235 if (!obj || typeof obj !== 'object') { 236 return false; 237 } 238 239 return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj)); 240 }; 241 242 var combine = function combine(a, b) { 243 return [].concat(a, b); 244 }; 245 246 var maybeMap = function maybeMap(val, fn) { 247 if (isArray(val)) { 248 var mapped = []; 249 for (var i = 0; i < val.length; i += 1) { 250 mapped.push(fn(val[i])); 251 } 252 return mapped; 253 } 254 return fn(val); 255 }; 256 257 module.exports = { 258 arrayToObject: arrayToObject, 259 assign: assign, 260 combine: combine, 261 compact: compact, 262 decode: decode, 263 encode: encode, 264 isBuffer: isBuffer, 265 isRegExp: isRegExp, 266 maybeMap: maybeMap, 267 merge: merge 268 };