index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /**
  2. *
  3. *
  4. * @author Jerry Bendy <jerry@icewingcc.com>
  5. * @licence MIT
  6. *
  7. */
  8. (function(self) {
  9. 'use strict';
  10. var nativeURLSearchParams = (function() {
  11. // #41 Fix issue in RN
  12. try {
  13. if (self.URLSearchParams && (new self.URLSearchParams('foo=bar')).get('foo') === 'bar') {
  14. return self.URLSearchParams;
  15. }
  16. } catch (e) {}
  17. return null;
  18. })(),
  19. isSupportObjectConstructor = nativeURLSearchParams && (new nativeURLSearchParams({a: 1})).toString() === 'a=1',
  20. // There is a bug in safari 10.1 (and earlier) that incorrectly decodes `%2B` as an empty space and not a plus.
  21. decodesPlusesCorrectly = nativeURLSearchParams && (new nativeURLSearchParams('s=%2B').get('s') === '+'),
  22. __URLSearchParams__ = "__URLSearchParams__",
  23. // Fix bug in Edge which cannot encode ' &' correctly
  24. encodesAmpersandsCorrectly = nativeURLSearchParams ? (function() {
  25. var ampersandTest = new nativeURLSearchParams();
  26. ampersandTest.append('s', ' &');
  27. return ampersandTest.toString() === 's=+%26';
  28. })() : true,
  29. prototype = URLSearchParamsPolyfill.prototype,
  30. iterable = !!(self.Symbol && self.Symbol.iterator);
  31. if (nativeURLSearchParams && isSupportObjectConstructor && decodesPlusesCorrectly && encodesAmpersandsCorrectly) {
  32. return;
  33. }
  34. /**
  35. * Make a URLSearchParams instance
  36. *
  37. * @param {object|string|URLSearchParams} search
  38. * @constructor
  39. */
  40. function URLSearchParamsPolyfill(search) {
  41. search = search || "";
  42. // support construct object with another URLSearchParams instance
  43. if (search instanceof URLSearchParams || search instanceof URLSearchParamsPolyfill) {
  44. search = search.toString();
  45. }
  46. this [__URLSearchParams__] = parseToDict(search);
  47. }
  48. /**
  49. * Appends a specified key/value pair as a new search parameter.
  50. *
  51. * @param {string} name
  52. * @param {string} value
  53. */
  54. prototype.append = function(name, value) {
  55. appendTo(this [__URLSearchParams__], name, value);
  56. };
  57. /**
  58. * Deletes the given search parameter, and its associated value,
  59. * from the list of all search parameters.
  60. *
  61. * @param {string} name
  62. */
  63. prototype['delete'] = function(name) {
  64. delete this [__URLSearchParams__] [name];
  65. };
  66. /**
  67. * Returns the first value associated to the given search parameter.
  68. *
  69. * @param {string} name
  70. * @returns {string|null}
  71. */
  72. prototype.get = function(name) {
  73. var dict = this [__URLSearchParams__];
  74. return this.has(name) ? dict[name][0] : null;
  75. };
  76. /**
  77. * Returns all the values association with a given search parameter.
  78. *
  79. * @param {string} name
  80. * @returns {Array}
  81. */
  82. prototype.getAll = function(name) {
  83. var dict = this [__URLSearchParams__];
  84. return this.has(name) ? dict [name].slice(0) : [];
  85. };
  86. /**
  87. * Returns a Boolean indicating if such a search parameter exists.
  88. *
  89. * @param {string} name
  90. * @returns {boolean}
  91. */
  92. prototype.has = function(name) {
  93. return hasOwnProperty(this [__URLSearchParams__], name);
  94. };
  95. /**
  96. * Sets the value associated to a given search parameter to
  97. * the given value. If there were several values, delete the
  98. * others.
  99. *
  100. * @param {string} name
  101. * @param {string} value
  102. */
  103. prototype.set = function set(name, value) {
  104. this [__URLSearchParams__][name] = ['' + value];
  105. };
  106. /**
  107. * Returns a string containg a query string suitable for use in a URL.
  108. *
  109. * @returns {string}
  110. */
  111. prototype.toString = function() {
  112. var dict = this[__URLSearchParams__], query = [], i, key, name, value;
  113. for (key in dict) {
  114. name = encode(key);
  115. for (i = 0, value = dict[key]; i < value.length; i++) {
  116. query.push(name + '=' + encode(value[i]));
  117. }
  118. }
  119. return query.join('&');
  120. };
  121. // There is a bug in Safari 10.1 and `Proxy`ing it is not enough.
  122. var forSureUsePolyfill = !decodesPlusesCorrectly;
  123. var useProxy = (!forSureUsePolyfill && nativeURLSearchParams && !isSupportObjectConstructor && self.Proxy);
  124. /*
  125. * Apply polifill to global object and append other prototype into it
  126. */
  127. Object.defineProperty(self, 'URLSearchParams', {
  128. value: (useProxy ?
  129. // Safari 10.0 doesn't support Proxy, so it won't extend URLSearchParams on safari 10.0
  130. new Proxy(nativeURLSearchParams, {
  131. construct: function(target, args) {
  132. return new target((new URLSearchParamsPolyfill(args[0]).toString()));
  133. }
  134. }) :
  135. URLSearchParamsPolyfill)
  136. });
  137. var USPProto = self.URLSearchParams.prototype;
  138. USPProto.polyfill = true;
  139. /**
  140. *
  141. * @param {function} callback
  142. * @param {object} thisArg
  143. */
  144. USPProto.forEach = USPProto.forEach || function(callback, thisArg) {
  145. var dict = parseToDict(this.toString());
  146. Object.getOwnPropertyNames(dict).forEach(function(name) {
  147. dict[name].forEach(function(value) {
  148. callback.call(thisArg, value, name, this);
  149. }, this);
  150. }, this);
  151. };
  152. /**
  153. * Sort all name-value pairs
  154. */
  155. USPProto.sort = USPProto.sort || function() {
  156. var dict = parseToDict(this.toString()), keys = [], k, i, j;
  157. for (k in dict) {
  158. keys.push(k);
  159. }
  160. keys.sort();
  161. for (i = 0; i < keys.length; i++) {
  162. this['delete'](keys[i]);
  163. }
  164. for (i = 0; i < keys.length; i++) {
  165. var key = keys[i], values = dict[key];
  166. for (j = 0; j < values.length; j++) {
  167. this.append(key, values[j]);
  168. }
  169. }
  170. };
  171. /**
  172. * Returns an iterator allowing to go through all keys of
  173. * the key/value pairs contained in this object.
  174. *
  175. * @returns {function}
  176. */
  177. USPProto.keys = USPProto.keys || function() {
  178. var items = [];
  179. this.forEach(function(item, name) {
  180. items.push(name);
  181. });
  182. return makeIterator(items);
  183. };
  184. /**
  185. * Returns an iterator allowing to go through all values of
  186. * the key/value pairs contained in this object.
  187. *
  188. * @returns {function}
  189. */
  190. USPProto.values = USPProto.values || function() {
  191. var items = [];
  192. this.forEach(function(item) {
  193. items.push(item);
  194. });
  195. return makeIterator(items);
  196. };
  197. /**
  198. * Returns an iterator allowing to go through all key/value
  199. * pairs contained in this object.
  200. *
  201. * @returns {function}
  202. */
  203. USPProto.entries = USPProto.entries || function() {
  204. var items = [];
  205. this.forEach(function(item, name) {
  206. items.push([name, item]);
  207. });
  208. return makeIterator(items);
  209. };
  210. if (iterable) {
  211. USPProto[self.Symbol.iterator] = USPProto[self.Symbol.iterator] || USPProto.entries;
  212. }
  213. function encode(str) {
  214. var replace = {
  215. '!': '%21',
  216. "'": '%27',
  217. '(': '%28',
  218. ')': '%29',
  219. '~': '%7E',
  220. '%20': '+',
  221. '%00': '\x00'
  222. };
  223. return encodeURIComponent(str).replace(/[!'\(\)~]|%20|%00/g, function(match) {
  224. return replace[match];
  225. });
  226. }
  227. function decode(str) {
  228. return str
  229. .replace(/[ +]/g, '%20')
  230. .replace(/(%[a-f0-9]{2})+/ig, function(match) {
  231. return decodeURIComponent(match);
  232. });
  233. }
  234. function makeIterator(arr) {
  235. var iterator = {
  236. next: function() {
  237. var value = arr.shift();
  238. return {done: value === undefined, value: value};
  239. }
  240. };
  241. if (iterable) {
  242. iterator[self.Symbol.iterator] = function() {
  243. return iterator;
  244. };
  245. }
  246. return iterator;
  247. }
  248. function parseToDict(search) {
  249. var dict = {};
  250. if (typeof search === "object") {
  251. // if `search` is an array, treat it as a sequence
  252. if (isArray(search)) {
  253. for (var i = 0; i < search.length; i++) {
  254. var item = search[i];
  255. if (isArray(item) && item.length === 2) {
  256. appendTo(dict, item[0], item[1]);
  257. } else {
  258. throw new TypeError("Failed to construct 'URLSearchParams': Sequence initializer must only contain pair elements");
  259. }
  260. }
  261. } else {
  262. for (var key in search) {
  263. if (search.hasOwnProperty(key)) {
  264. appendTo(dict, key, search[key]);
  265. }
  266. }
  267. }
  268. } else {
  269. // remove first '?'
  270. if (search.indexOf("?") === 0) {
  271. search = search.slice(1);
  272. }
  273. var pairs = search.split("&");
  274. for (var j = 0; j < pairs.length; j++) {
  275. var value = pairs [j],
  276. index = value.indexOf('=');
  277. if (-1 < index) {
  278. appendTo(dict, decode(value.slice(0, index)), decode(value.slice(index + 1)));
  279. } else {
  280. if (value) {
  281. appendTo(dict, decode(value), '');
  282. }
  283. }
  284. }
  285. }
  286. return dict;
  287. }
  288. function appendTo(dict, name, value) {
  289. var val = typeof value === 'string' ? value : (
  290. value !== null && value !== undefined && typeof value.toString === 'function' ? value.toString() : JSON.stringify(value)
  291. );
  292. // #47 Prevent using `hasOwnProperty` as a property name
  293. if (hasOwnProperty(dict, name)) {
  294. dict[name].push(val);
  295. } else {
  296. dict[name] = [val];
  297. }
  298. }
  299. function isArray(val) {
  300. return !!val && '[object Array]' === Object.prototype.toString.call(val);
  301. }
  302. function hasOwnProperty(obj, prop) {
  303. return Object.prototype.hasOwnProperty.call(obj, prop);
  304. }
  305. })(typeof global !== 'undefined' ? global : (typeof window !== 'undefined' ? window : this));