|
const cookieStorage = { |
|
|
|
get COOKIE() { return window.document.cookie; }, |
|
set COOKIE(v: string) { window.document.cookie = v; }, |
|
|
|
get ITEMS(): { [key: string]: string } { |
|
return this.COOKIE |
|
.split(/;\s*/) |
|
.filter(Boolean) |
|
.map(s => s.match(/^([^=]+)=(.*)$/)) |
|
.reduce((mem, [str, key, value]) => { |
|
mem[key] = decodeURIComponent(value); |
|
return mem; |
|
}, {}); |
|
}, |
|
|
|
get DEFAULT_OPTIONS() { |
|
const nextYear = new Date(); |
|
nextYear.setFullYear(nextYear.getFullYear() + 1); |
|
|
|
return { |
|
path: '/', |
|
domain: window.location.hostname, |
|
expires: nextYear, |
|
}; |
|
}, |
|
|
|
get DEFAULT_CHUNK() { |
|
return { |
|
size: 4000, |
|
maxCount: 10, |
|
}; |
|
}, |
|
|
|
|
|
_getOptionsString(options: { path?: string, domain?: string, expires?: Date|number } = {}) { |
|
const opts = Object.assign(this.DEFAULT_OPTIONS, options) as { |
|
path: string, |
|
domain: string, |
|
expires?: Date|number|string, |
|
}; |
|
|
|
if (typeof opts.expires === 'number') |
|
opts.expires = (opts.expires !== 0) ? new Date(opts.expires) : undefined; |
|
if (opts.expires instanceof Date) |
|
opts.expires = opts.expires.toUTCString(); |
|
|
|
return Object.keys(opts) |
|
.filter(key => Boolean(opts[key])) |
|
.map(key => `${key}=${opts[key]}`) |
|
.join('; '); |
|
}, |
|
|
|
|
|
getItem(key: string) { |
|
return this.ITEMS[key]; |
|
}, |
|
|
|
setItem( |
|
key: string, |
|
value: string, |
|
options: { path?: string, domain?: string, expires?: Date|number } = {} |
|
) { |
|
const optionsString = this._getOptionsString(options); |
|
|
|
const encoded = encodeURIComponent(value); |
|
if (encoded.length > this.DEFAULT_CHUNK.size) { |
|
console.warn('Cookie limit is 4 KB.'); |
|
} |
|
|
|
this.COOKIE = `${key}=${encodeURIComponent(value)}; ${optionsString}`; |
|
}, |
|
|
|
removeItem(key: string) { |
|
this.setItem(key, '', { expires: -1 }); |
|
}, |
|
|
|
|
|
setItemByChunks( |
|
key: string, |
|
value: string, |
|
options: { path?: string, domain?: string, expires?: Date|number } = {}, |
|
chunkOptions: { size?: number, maxCount?: number } = {} |
|
) { |
|
const { size: chunkSize, maxCount: chunkMaxCount } = Object.assign(this.DEFAULT_CHUNK, chunkOptions) as { |
|
size: number, |
|
maxCount: number, |
|
}; |
|
|
|
const optionsString = this._getOptionsString(options); |
|
|
|
encodeURIComponent(value) |
|
.match(/(%.{2}|.)/g) |
|
.reduce((chunks, symbol) => { |
|
let lastIndex = chunks.length - 1; |
|
let lastChunk = chunks[lastIndex]; |
|
|
|
if ((lastChunk.length + symbol.length) > chunkSize) { |
|
lastIndex += 1; |
|
lastChunk = ''; |
|
} |
|
|
|
chunks[lastIndex] = lastChunk + symbol; |
|
return chunks; |
|
}, ['']) |
|
.forEach((chunk, index) => { |
|
this.COOKIE = `${key}-${index}=${chunk}; ${optionsString}`; |
|
}); |
|
}, |
|
|
|
getItemByChunks(key: string) { |
|
const items = this.ITEMS; |
|
return Object |
|
.keys(items) |
|
.map(k => k.match(/(^.*?)\-(\d+$)/)) |
|
.filter(Boolean) |
|
.filter(([k, name, index]) => name === key) |
|
.map(([k, name, index]) => index) |
|
.map(Number) |
|
.sort() |
|
.reduce((result, index) => result + items[`${key}-${index}`], ''); |
|
}, |
|
|
|
removeItemByChunks(key: string) { |
|
Object |
|
.keys(this.ITEMS) |
|
.map(k => k.match(/(^.*?)\-(\d+$)/)) |
|
.filter(Boolean) |
|
.filter(([k, name]) => name === key) |
|
.forEach(([k]) => this.removeItem(k)); |
|
}, |
|
|
|
|
|
clear() { |
|
Object |
|
.keys(this.ITEMS) |
|
.forEach(key => this.removeItem(key)); |
|
}, |
|
} |