cookies.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /*
  2. * Cookies.js - 1.2.3-grav
  3. * https://github.com/ScottHamper/Cookies
  4. *
  5. * With SameSite support by Grav
  6. *
  7. * This is free and unencumbered software released into the public domain.
  8. */
  9. const factory = function(window) {
  10. if (typeof window.document !== 'object') {
  11. throw new Error('Cookies.js requires a `window` with a `document` object');
  12. }
  13. const Cookies = (key, value, options) => {
  14. return arguments.length === 1
  15. ? Cookies.get(key)
  16. : Cookies.set(key, value, options);
  17. };
  18. // Allows for setter injection in unit tests
  19. Cookies._document = window.document;
  20. // Used to ensure cookie keys do not collide with
  21. // built-in `Object` properties
  22. Cookies._cacheKeyPrefix = 'cookey.'; // Hurr hurr, :)
  23. Cookies._maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC');
  24. Cookies.defaults = {
  25. path: '/',
  26. secure: false,
  27. sameSite: 'Lax'
  28. };
  29. Cookies.get = (key) => {
  30. if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
  31. Cookies._renewCache();
  32. }
  33. const value = Cookies._cache[Cookies._cacheKeyPrefix + key];
  34. return value === undefined ? undefined : decodeURIComponent(value);
  35. };
  36. Cookies.set = (key, value, options) => {
  37. options = Cookies._getExtendedOptions(options);
  38. options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires);
  39. Cookies._document.cookie = Cookies._generateCookieString(key, value, options);
  40. return Cookies;
  41. };
  42. Cookies.expire = (key, options) => {
  43. return Cookies.set(key, undefined, options);
  44. };
  45. Cookies._getExtendedOptions = (options) => {
  46. return {
  47. path: options && options.path || Cookies.defaults.path,
  48. domain: options && options.domain || Cookies.defaults.domain,
  49. expires: options && options.expires || Cookies.defaults.expires,
  50. secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure,
  51. sameSite: options && options.sameSite || Cookies.defaults.sameSite
  52. };
  53. };
  54. Cookies._isValidDate = (date) => {
  55. return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
  56. };
  57. Cookies._getExpiresDate = (expires, now) => {
  58. now = now || new Date();
  59. if (typeof expires === 'number') {
  60. expires = expires === Infinity
  61. ? Cookies._maxExpireDate
  62. : new Date(now.getTime() + expires * 1000);
  63. } else if (typeof expires === 'string') {
  64. expires = new Date(expires);
  65. }
  66. if (expires && !Cookies._isValidDate(expires)) {
  67. throw new Error('`expires` parameter cannot be converted to a valid Date instance');
  68. }
  69. return expires;
  70. };
  71. Cookies._generateCookieString = (key, value, options) => {
  72. key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent);
  73. key = key.replace(/\(/g, '%28').replace(/\)/g, '%29');
  74. value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent);
  75. options = options || {};
  76. let cookieString = key + '=' + value;
  77. cookieString += options.path ? ';path=' + options.path : '';
  78. cookieString += options.domain ? ';domain=' + options.domain : '';
  79. cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : '';
  80. cookieString += options.secure ? ';secure' : '';
  81. cookieString += options.sameSite ? ';SameSite=' + options.sameSite : '';
  82. return cookieString;
  83. };
  84. Cookies._getCacheFromString = (documentCookie) => {
  85. let cookieCache = {};
  86. const cookiesArray = documentCookie ? documentCookie.split('; ') : [];
  87. for (let i = 0; i < cookiesArray.length; i++) {
  88. const cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]);
  89. if (cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined) {
  90. cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] = cookieKvp.value;
  91. }
  92. }
  93. return cookieCache;
  94. };
  95. Cookies._getKeyValuePairFromCookieString = (cookieString) => {
  96. // "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
  97. let separatorIndex = cookieString.indexOf('=');
  98. // IE omits the "=" when the cookie value is an empty string
  99. separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex;
  100. const key = cookieString.substr(0, separatorIndex);
  101. let decodedKey;
  102. try {
  103. decodedKey = decodeURIComponent(key);
  104. } catch (e) {
  105. if (console && typeof console.error === 'function') {
  106. console.error('Could not decode cookie with key "' + key + '"', e);
  107. }
  108. }
  109. return {
  110. key: decodedKey,
  111. value: cookieString.substr(separatorIndex + 1) // Defer decoding value until accessed
  112. };
  113. };
  114. Cookies._renewCache = () => {
  115. Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie);
  116. Cookies._cachedDocumentCookie = Cookies._document.cookie;
  117. };
  118. Cookies._areEnabled = () => {
  119. const testKey = 'cookies.js';
  120. const areEnabled = Cookies.set(testKey, 1).get(testKey) === '1';
  121. Cookies.expire(testKey);
  122. return areEnabled;
  123. };
  124. Cookies.enabled = Cookies._areEnabled();
  125. return Cookies;
  126. };
  127. global.Cookies = (global && typeof global.document === 'object') ? factory(global) : factory;
  128. export default global.Cookies;