|
17 | 17 | window.Cookies = factory(); |
18 | 18 | } |
19 | 19 | }(function () { |
20 | | - |
21 | | - var pluses = /\+/g; |
22 | | - |
23 | | - function encode(s) { |
24 | | - return api.raw ? s : encodeURIComponent(s); |
| 20 | + var unallowedChars = { |
| 21 | + ';': '%3B', |
| 22 | + ',': '%2C', |
| 23 | + '"': '%22' |
| 24 | + }; |
| 25 | + var unallowedCharsInName = extend(unallowedChars, { |
| 26 | + '=': '%3D', |
| 27 | + '\t': '%09' |
| 28 | + }); |
| 29 | + var unallowedCharsInValue = extend(unallowedChars, { |
| 30 | + ' ': '%20' |
| 31 | + }); |
| 32 | + |
| 33 | + function encode (value, charmap) { |
| 34 | + for (var character in charmap) { |
| 35 | + value = value |
| 36 | + .replace(new RegExp(character, 'g'), charmap[character]); |
| 37 | + } |
| 38 | + return value; |
25 | 39 | } |
26 | 40 |
|
27 | | - function decode(s) { |
28 | | - return api.raw ? s : decodeURIComponent(s); |
| 41 | + function decode (value, charmap) { |
| 42 | + for (var character in charmap) { |
| 43 | + value = value |
| 44 | + .replace(new RegExp(charmap[character], 'g'), character); |
| 45 | + } |
| 46 | + return value; |
29 | 47 | } |
30 | 48 |
|
31 | | - function stringifyCookieValue(value) { |
32 | | - return encode(api.json ? JSON.stringify(value) : String(value)); |
| 49 | + function processWrite (value) { |
| 50 | + var stringified; |
| 51 | + try { |
| 52 | + stringified = JSON.stringify(value); |
| 53 | + if (/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/.test(stringified)) { |
| 54 | + value = stringified; |
| 55 | + } |
| 56 | + } catch(e) {} |
| 57 | + return encode(String(value), unallowedCharsInValue); |
33 | 58 | } |
34 | 59 |
|
35 | | - function parseCookieValue(s) { |
36 | | - if (s.indexOf('"') === 0) { |
| 60 | + function processRead (value, converter, json) { |
| 61 | + if (value.indexOf('"') === 0) { |
37 | 62 | // This is a quoted cookie as according to RFC2068, unescape... |
38 | | - s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); |
| 63 | + value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); |
39 | 64 | } |
40 | 65 |
|
41 | | - try { |
42 | | - // Replace server-side written pluses with spaces. |
43 | | - // If we can't decode the cookie, ignore it, it's unusable. |
44 | | - // If we can't parse the cookie, ignore it, it's unusable. |
45 | | - s = decodeURIComponent(s.replace(pluses, ' ')); |
46 | | - return api.json ? JSON.parse(s) : s; |
47 | | - } catch(e) {} |
48 | | - } |
| 66 | + value = decode(value, unallowedCharsInValue); |
| 67 | + |
| 68 | + if (json) { |
| 69 | + try { |
| 70 | + value = JSON.parse(value); |
| 71 | + } catch(e) {} |
| 72 | + } |
49 | 73 |
|
50 | | - function read(s, converter) { |
51 | | - var value = api.raw ? s : parseCookieValue(s); |
52 | 74 | return isFunction(converter) ? converter(value) : value; |
53 | 75 | } |
54 | 76 |
|
55 | | - function extend() { |
| 77 | + function extend () { |
56 | 78 | var key, options; |
57 | 79 | var i = 0; |
58 | 80 | var result = {}; |
|
65 | 87 | return result; |
66 | 88 | } |
67 | 89 |
|
68 | | - function isFunction(obj) { |
| 90 | + function isFunction (obj) { |
69 | 91 | return Object.prototype.toString.call(obj) === '[object Function]'; |
70 | 92 | } |
71 | 93 |
|
72 | 94 | var api = function (key, value, options) { |
| 95 | + var converter; |
| 96 | + |
| 97 | + if (isFunction(value)) { |
| 98 | + converter = value; |
| 99 | + value = undefined; |
| 100 | + } |
73 | 101 |
|
74 | 102 | // Write |
75 | 103 |
|
76 | | - if (arguments.length > 1 && !isFunction(value)) { |
| 104 | + if (arguments.length > 1 && !converter) { |
77 | 105 | options = extend(api.defaults, options); |
78 | 106 |
|
79 | 107 | if (typeof options.expires === 'number') { |
|
82 | 110 | } |
83 | 111 |
|
84 | 112 | return (document.cookie = [ |
85 | | - encode(key), '=', stringifyCookieValue(value), |
| 113 | + encode(key, unallowedCharsInName), '=', processWrite(value), |
86 | 114 | options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE |
87 | 115 | options.path ? '; path=' + options.path : '', |
88 | 116 | options.domain ? '; domain=' + options.domain : '', |
|
102 | 130 |
|
103 | 131 | for (; i < l; i++) { |
104 | 132 | var parts = cookies[i].split('='), |
105 | | - name = decode(parts.shift()), |
| 133 | + name = decode(parts.shift(), unallowedCharsInName), |
106 | 134 | cookie = parts.join('='); |
107 | 135 |
|
108 | 136 | if (key === name) { |
109 | | - // If second argument (value) is a function it's a converter... |
110 | | - result = read(cookie, value); |
| 137 | + result = processRead(cookie, converter, this.json); |
111 | 138 | break; |
112 | 139 | } |
113 | 140 |
|
114 | | - // Prevent storing a cookie that we couldn't decode. |
115 | | - if (!key && (cookie = read(cookie)) !== undefined) { |
116 | | - result[name] = cookie; |
| 141 | + if (!key) { |
| 142 | + result[name] = processRead(cookie, converter, this.json); |
117 | 143 | } |
118 | 144 | } |
119 | 145 |
|
120 | 146 | return result; |
121 | 147 | }; |
122 | 148 |
|
123 | 149 | api.get = api.set = api; |
| 150 | + api.getJSON = function() { |
| 151 | + return api.get.apply({ |
| 152 | + json: true |
| 153 | + }, [].slice.call(arguments)); |
| 154 | + }; |
124 | 155 | api.defaults = {}; |
125 | 156 |
|
126 | 157 | api.remove = function (key, options) { |
|
0 commit comments