diff --git a/README.md b/README.md index 4a6e140..a43e90d 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,24 @@ try { } ``` +### Used in Hash +Basic Persist supports only url excluding hash. If you want to use hash, use the code below. +```js +import { HashPersist, registerHashPersist } from "@egjs/persist"; + +// Register globally to use HashPersist. +registerHashPersist(); + + +// Use HashPersist +const persist = new HashPersist(); + +// set +persist.set("a", "A"); + +// get +persist.get("a"); +``` ### Used in SPA diff --git a/index.d.ts b/index.d.ts index 91d6260..72e634d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -30,12 +30,16 @@ declare module "@egjs/persist" { public remove(path: string): this; } + export class HashPersist extends Persist { + } + export class PersistQuotaExceededError extends Error { public name: string; public storageType: "SessionStorage" | "LocalStorage" | "History" | "None"; public key: string; public size: number; } + export function releaseEvent(): void; export function updateDepth(type?: number): void; export function replaceDepth(): void; export default Persist; diff --git a/package.json b/package.json index f522608..cab019c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@egjs/persist", - "version": "2.6.0-snapshot", + "version": "2.7.0-beta.0", "description": "Provide cache interface to handle persisted data among history navigation.", "main": "dist/persist.js", "module": "dist/persist.esm.js", @@ -8,7 +8,7 @@ "types": "./index.d.ts", "sideEffects": false, "scripts": { - "start": "webpack-dev-server --open", + "start": "rollup -c -w", "build": "rollup -c", "build:webpack": "webpack --env production", "test": "karma start", diff --git a/rollup.config.js b/rollup.config.js index f32399a..52df164 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -18,7 +18,7 @@ export default [ ], }), esm({ - input: "./src/Persist.js", + input: "./src/index.js", output: "./dist/persist.esm.js", }), ]; diff --git a/src/HashPersist.js b/src/HashPersist.js new file mode 100644 index 0000000..711a151 --- /dev/null +++ b/src/HashPersist.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ +/* eslint-disable class-methods-use-this */ +/* eslint-disable no-use-before-define */ +import {location} from "./browser"; +import {CONST_HASH} from "./consts"; +import Persist from "./Persist"; + +/** + * Get or store the current state of the web page using JSON according to hash. + * @ko 웹 페이지의 현재 상태를 hash에 따라 JSON 형식으로 저장하거나 읽는다. + * @memberof eg.Persist + * @alias eg.Persist.HashPersist + * @extends eg.Persist + * + * @support {"ie": "9+", "ch" : "latest", "ff" : "latest", "sf" : "latest" , "edge" : "latest", "ios" : "7+", "an" : "2.3+ (except 3.x)"} + */ +class HashPersist extends Persist { + /** + * Read value + * @param {String|Array} [path] - target path + * @return {String|Number|Boolean|Object|Array} + */ + get(path) { + return this._get(this._getKey(), this._getPath(path)); + } + /** + * Save value + * @param {String|Array} path - target path + * @param {String|Number|Boolean|Object|Array} value - value to save + * @return {Persist} + */ + set(path, value) { + return this._set(this._getKey(), this._getPath(path), value); + } + /** + * Remove value + * @param {String|Array} [path] - target path + * @return {Persist} + */ + remove(path) { + return this._remove(this._getKey(), this._getPath(path)); + } + _getKey() { + return `${CONST_HASH}${location.hash}`; + } + _getPath(path) { + let nextPath = path; + + if (Array.isArray(nextPath)) { + nextPath = [this.key, ...nextPath]; + } else { + nextPath = `${this.key}.${nextPath}`; + } + return nextPath; + } +} + +export default HashPersist; diff --git a/src/Persist.js b/src/Persist.js index a05849d..2c9f9e8 100755 --- a/src/Persist.js +++ b/src/Persist.js @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ +/* eslint-disable class-methods-use-this */ /* eslint-disable no-use-before-define */ import { reset, @@ -5,186 +10,9 @@ import { getStateByKey, getStorage, } from "./storageManager"; -import PersistQuotaExceededError from "./PersistQuotaExceededError"; -import {isNeeded, getUrl, getStorageKey, getNavigationType, isQuotaExceededError} from "./utils"; -import {console, window} from "./browser"; -import {TYPE_BACK_FORWARD, TYPE_NAVIGATE, CONST_PERSIST_STATE, CONST_DEPTHS, CONST_LAST_URL} from "./consts"; - -let currentUrl = ""; - - -function execRec(obj, path, func) { - let _obj = obj; - - if (!_obj) { - _obj = isNaN(path[0]) ? {} : []; - } - - const head = path.shift(); - - if (path.length === 0) { - if (_obj instanceof Array && isNaN(head)) { - console.warn("Don't use key string on array"); - } - func(_obj, head); - return _obj; - } - - _obj[head] = execRec(_obj[head], path, func); - return _obj; -} - -function setPersistState(key, value) { - try { - setStateByKey(CONST_PERSIST_STATE, key, value); - } catch (e) { - if (catchQuotaExceededError(e, CONST_PERSIST_STATE, value)) { - if (key === CONST_LAST_URL) { - setPersistState(key, value); - } else if (key === CONST_DEPTHS) { - setPersistState(key, value && value.slice(1)); - } - } - } -} -function getPersistState(key) { - return getStateByKey(CONST_PERSIST_STATE, key); -} - -function replaceDepth() { - const url = getUrl(); - - if (currentUrl === url) { - return; - } - const prevUrl = currentUrl; - - try { - currentUrl = url; - - const depths = getPersistState(CONST_DEPTHS) || []; - - // remove prev url - const prevIndex = depths.indexOf(prevUrl); - - if (prevIndex >= 0) { - depths.splice(prevIndex, 1); - reset(getStorageKey(prevUrl)); - } - - // remove next url info - const currentIndex = depths.indexOf(url); - - if (currentIndex >= 0) { - depths.splice(currentIndex, 1); - reset(getStorageKey(url)); - } - - depths.push(url); - setPersistState(CONST_DEPTHS, depths); - setPersistState(CONST_LAST_URL, url); - } catch (e) { - // revert currentUrl - currentUrl = prevUrl; - throw e; - } -} - -function updateDepth(type = 0) { - const url = getUrl(); - - if (currentUrl === url) { - return; - } - // url is not the same for the first time, pushState, or replaceState. - const prevUrl = currentUrl; - - try { - currentUrl = url; - const depths = getPersistState(CONST_DEPTHS) || []; - - if (type === TYPE_BACK_FORWARD) { - // Change current url only - const currentIndex = depths.indexOf(url); - - ~currentIndex && setPersistState(CONST_LAST_URL, url); - } else { - const prevLastUrl = getPersistState(CONST_LAST_URL); - - reset(getStorageKey(url)); - - if (type === TYPE_NAVIGATE && url !== prevLastUrl) { - // Remove all url lists with higher index than current index - const prevLastIndex = depths.indexOf(prevLastUrl); - const removedList = depths.splice(prevLastIndex + 1, depths.length); - - removedList.forEach(removedUrl => { - reset(getStorageKey(removedUrl)); - }); - // If the type is NAVIGATE and there is information about current url, delete it. - const currentIndex = depths.indexOf(url); - - ~currentIndex && depths.splice(currentIndex, 1); - } - // Add depth for new address. - if (depths.indexOf(url) < 0) { - depths.push(url); - } - setPersistState(CONST_DEPTHS, depths); - setPersistState(CONST_LAST_URL, url); - } - } catch (e) { - // revert currentUrl - currentUrl = prevUrl; - throw e; - } -} - -function catchQuotaExceededError(e, key, value) { - if (clearFirst()) { - return true; - } else if (isQuotaExceededError(e)) { - throw new PersistQuotaExceededError(key, value ? JSON.stringify(value) : ""); - } else { - throw e; - } -} - -function clearFirst() { - const depths = getPersistState(CONST_DEPTHS) || []; - const removed = depths.splice(0, 1); - - if (!removed.length) { - // There is an error because there is no depth to add data. - return false; - } - const removedUrl = removed[0]; - - reset(getStorageKey(removedUrl)); - if (currentUrl === removedUrl) { - currentUrl = ""; - setPersistState(CONST_LAST_URL, ""); - if (!depths.length) { - // I tried to add myself, but it didn't add up, so I got an error. - return false; - } - } - setPersistState(CONST_DEPTHS, depths); - // Clear the previous record and try to add data again. - return true; -} - -function clear() { - const depths = getPersistState(CONST_DEPTHS) || []; - - depths.forEach(url => { - reset(getStorageKey(url)); - }); - - reset(CONST_PERSIST_STATE); - - currentUrl = ""; -} +import {isNeeded, getUrlKey, execRec} from "./utils"; +import {TYPE_NAVIGATE} from "./consts"; +import {clear, updateDepth, catchQuotaExceededError} from "./historyManager"; /** * Get or store the current state of the web page using JSON. @@ -223,29 +51,43 @@ class Persist { constructor(key) { this.key = key || ""; } - /** * Read value * @param {String?} path target path * @return {String|Number|Boolean|Object|Array} */ get(path) { + return this._get(this.key, path); + } + /** + * Save value + * @param {String|Array} [path] - target path + * @param {String|Number|Boolean|Object|Array} value value to save + * @return {Persist} + */ + set(path, value) { + return this._set(this.key, path, value); + } + /** + * Remove value + * @param {String|Array} path - target path + * @return {Persist} + */ + remove(path) { + return this._remove(this.key, path); + } + _get(key, path) { // update url for pushState, replaceState updateDepth(TYPE_NAVIGATE); - // find path - const urlKey = getStorageKey(getUrl()); - const globalState = getStateByKey(urlKey, this.key); - + const urlKey = getUrlKey(); + const globalState = getStateByKey(urlKey, key); if (!path || path.length === 0) { return globalState; } - const pathToken = path.split("."); - let currentItem = globalState; - let isTargetExist = true; for (let i = 0; i < pathToken.length; i++) { @@ -260,18 +102,11 @@ class Persist { } return currentItem; } - /** - * Save value - * @param {String} path target path - * @param {String|Number|Boolean|Object|Array} value value to save - * @return {Persist} - */ - set(path, value) { + _set(key, path, value) { // update url for pushState, replaceState updateDepth(TYPE_NAVIGATE); - // find path - const key = this.key; - const urlKey = getStorageKey(getUrl()); + + const urlKey = getUrlKey(); const globalState = getStateByKey(urlKey, key); try { @@ -290,23 +125,17 @@ class Persist { } } catch (e) { if (catchQuotaExceededError(e, urlKey, value)) { - this.set(path, value); + this._set(key, path, value); } } return this; } - /** - * Remove value - * @param {String} path target path - * @return {Persist} - */ - remove(path) { + _remove(key, path) { // update url for pushState, replaceState updateDepth(TYPE_NAVIGATE); // find path - const key = this.key; - const urlKey = getStorageKey(getUrl()); + const urlKey = getUrlKey(); const globalState = getStateByKey(urlKey, key); try { @@ -327,41 +156,11 @@ class Persist { } } catch (e) { if (catchQuotaExceededError(e)) { - this.remove(path); + this._remove(key, path); } } return this; } } - -if ("onpopstate" in window) { - window.addEventListener("popstate", () => { - // popstate event occurs when backward or forward - try { - updateDepth(TYPE_BACK_FORWARD); - } catch (e) { - // Global function calls prevent errors. - if (!isQuotaExceededError(e)) { - throw e; - } - } - }); -} - -// If navigation's type is not TYPE_BACK_FORWARD, delete information about current url. -try { - updateDepth(getNavigationType()); -} catch (e) { - // Global function calls prevent errors. - if (!isQuotaExceededError(e)) { - throw e; - } -} - -export { - updateDepth, - replaceDepth, -}; - export default Persist; diff --git a/src/PersistHistory.js b/src/PersistHistory.js new file mode 100644 index 0000000..1b74c73 --- /dev/null +++ b/src/PersistHistory.js @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ +const PersistHistory = { + url: "", + hashUrl: "", + hash: null, + length: 0, + useHash: false, +}; + +export default PersistHistory; diff --git a/src/PersistQuotaExceededError.js b/src/PersistQuotaExceededError.js index 21aa09a..bd74250 100644 --- a/src/PersistQuotaExceededError.js +++ b/src/PersistQuotaExceededError.js @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ import {getStorage, getStorageType} from "./storageManager"; const setPrototypeOf = Object.setPrototypeOf || ((obj, proto) => { diff --git a/src/browser.js b/src/browser.js index fbdcf0f..f5bf53e 100755 --- a/src/browser.js +++ b/src/browser.js @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ const win = (typeof window !== `undefined` && window) || {}; export {win as window}; diff --git a/src/consts.js b/src/consts.js index b3d61b0..81ed5fc 100644 --- a/src/consts.js +++ b/src/consts.js @@ -1,8 +1,14 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ import {performance, navigator, parseFloat} from "./browser"; export const CONST_PERSIST = "___persist___"; -export const CONST_PERSIST_STATE = `state${CONST_PERSIST}`; +export const CONST_HASH = `___hash___`; export const CONST_DEPTHS = "depths"; +export const CONST_HASH_DEPTHS_PERSIST = `${CONST_HASH}${CONST_DEPTHS}${CONST_PERSIST}`; +export const CONST_PERSIST_STATE = `state${CONST_PERSIST}`; export const CONST_LAST_URL = "lastUrl"; const navigation = performance && performance.navigation; diff --git a/src/historyManager.js b/src/historyManager.js new file mode 100644 index 0000000..ae326ff --- /dev/null +++ b/src/historyManager.js @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ +/* eslint-disable no-use-before-define */ +import { + reset, + setState, + getState, + getStateByKey, + setStateByKey, + getDepths, + getPersistState, +} from "./storageManager"; +import PersistHistory from "./PersistHistory"; +import {location, history, document, window} from "./browser"; +import {getNavigationType, getUrl, getStorageKey, getUrlKey, getHashUrl} from "./utils"; +import {TYPE_BACK_FORWARD, TYPE_NAVIGATE, CONST_PERSIST_STATE, CONST_DEPTHS, CONST_LAST_URL, CONST_HASH_DEPTHS_PERSIST, CONST_HASH} from "./consts"; +import PersistQuotaExceededError from "./PersistQuotaExceededError"; + + +function setHashDepths(depths, key = getUrlKey()) { + return setStateByKey(key, CONST_HASH_DEPTHS_PERSIST, depths); +} + +/* + * flush current history state for hash + */ +function resetHash(key, hash) { + const originalState = getState(key); + const hashDepths = originalState[CONST_HASH_DEPTHS_PERSIST]; + + if (hashDepths && hashDepths.length) { + if (typeof hash === "undefined") { + const nextState = {}; + + for (const name in originalState) { + if (name.indexOf(CONST_HASH) === 0) { + nextState[name] = originalState[name]; + } + } + setState(key, nextState); + } else { + delete originalState[`${CONST_HASH}${hash}`]; + setState(key, originalState); + } + } else { + setState(key, null); + } +} +function replaceHashDepth() { + const prevHash = PersistHistory.hash; + const prevUrl = PersistHistory.url; + const prevUrlKey = getStorageKey(prevUrl); + const prevHashUrl = PersistHistory.hashUrl; + + const hashUrl = getHashUrl(); + const urlKey = getUrlKey(); + const hash = location.hash; + const hashLength = history.length; + + if (hashUrl === prevHashUrl) { + return; + } + + PersistHistory.hash = hash; + PersistHistory.hashUrl = hashUrl; + PersistHistory.length = hashLength; + // remove hash info + resetHash(getStorageKey(prevUrl), prevHash); + + // remove prev hash + const prevDepths = getHashDepths(prevUrlKey); + const prevHashIndex = prevDepths.indexOf(prevHash); + + if (prevHashIndex > -1) { + prevDepths.splice(prevHashIndex, 1); + } + const isSameKey = urlKey === prevUrlKey; + const depths = isSameKey ? prevDepths : getHashDepths(); + const hashIndex = depths.indexOf(hash); + + // remove next hash info + if (hashIndex > -1) { + resetHash(urlKey, hash); + depths.splice(hashIndex, 1); + } + if (isSameKey) { + const addedIndex = hashIndex === -1 || hashIndex > prevHashIndex ? + prevHashIndex : prevHashIndex - 1; + + depths.splice(addedIndex, 0, hash); + } else { + depths.splice(0, 0, hash); + setHashDepths(prevDepths, prevUrlKey); + } + + // add next hash + setHashDepths(depths, urlKey); +} +function updateHashDepth(type = 0) { + const prevLength = PersistHistory.length; + const prevHash = PersistHistory.hash; + const prevUrl = PersistHistory.url; + const prevHashUrl = PersistHistory.hashUrl; + + const url = getUrl(); + const hashUrl = getHashUrl(); + const urlKey = getUrlKey(); + const hash = location.hash; + const hashLength = history.length; + + let hashType = type; + + if (hashUrl === prevHashUrl) { + return; + } + const historyState = history.state; + + if (historyState == null) { + hashType = TYPE_NAVIGATE; + + // warning + history.replaceState({[CONST_PERSIST_STATE]: true}, document.title, location.href); + } else if (type === TYPE_BACK_FORWARD && hashLength !== prevLength) { + hashType = TYPE_NAVIGATE; + } + PersistHistory.hash = hash; + PersistHistory.hashUrl = hashUrl; + PersistHistory.length = hashLength; + + const depths = getHashDepths(); + + if (hashType === TYPE_BACK_FORWARD) { + return; + } + + // remove url key + // remove hash key for first refresh or first navigate + resetHash(urlKey, hash); + + // Remove all url lists with higher index than current index + if (hashType === TYPE_NAVIGATE) { + const prevLastIndex = prevUrl === url ? + depths.indexOf(prevHash) : 0; + + if (prevLastIndex > -1 && prevHash != null) { + const removedList = depths.splice(prevLastIndex + 1, depths.length); + + removedList.forEach(removedHash => { + resetHash(urlKey, removedHash); + }); + } + // If the type is NAVIGATE and there is information about current hash, delete it. + const currentIndex = depths.indexOf(hash); + + ~currentIndex && depths.splice(currentIndex, 1); + } + + // Add depth for new address. + if (depths.indexOf(hash) < 0) { + depths.push(hash); + } + setHashDepths(depths); +} + +export function getHashDepths(key = getUrlKey()) { + return getStateByKey(key, CONST_HASH_DEPTHS_PERSIST) || []; +} + +export function isQuotaExceededError(e) { + return e.name === "QuotaExceededError" || e.name === "PersistQuotaExceededError"; +} + +export function catchQuotaExceededError(e, key, value) { + if (clearFirst()) { + return true; + } else if (isQuotaExceededError(e)) { + throw new PersistQuotaExceededError(key, value ? JSON.stringify(value) : ""); + } else { + throw e; + } +} + +export function trySetPersistState(key, value) { + try { + setStateByKey(CONST_PERSIST_STATE, key, value); + } catch (e) { + if (catchQuotaExceededError(e, CONST_PERSIST_STATE, value)) { + if (key === CONST_LAST_URL) { + trySetPersistState(key, value); + } else if (key === CONST_DEPTHS) { + trySetPersistState(key, value && value.slice(1)); + } + } + } +} + +export function clearFirst() { + const depths = getDepths(); + const removed = depths.splice(0, 1); + + if (!removed.length) { + // There is an error because there is no depth to add data. + return false; + } + const removedUrl = removed[0]; + + reset(getStorageKey(removedUrl)); + if (PersistHistory.url === removedUrl) { + PersistHistory.url = ""; + trySetPersistState(CONST_LAST_URL, ""); + if (!depths.length) { + // I tried to add myself, but it didn't add up, so I got an error. + return false; + } + } + + trySetPersistState(CONST_DEPTHS, depths); + // Clear the previous record and try to add data again. + return true; +} + +export function replaceDepth() { + const url = getUrl(); + + if (PersistHistory.useHash) { + replaceHashDepth(); + } + if (PersistHistory.url === url) { + return; + } + const prevUrl = PersistHistory.url; + + try { + PersistHistory.url = url; + + const depths = getDepths(); + + // remove prev url + const prevIndex = depths.indexOf(prevUrl); + + if (prevIndex >= 0) { + depths.splice(prevIndex, 1); + resetHash(getStorageKey(prevUrl)); + } + + // remove next url info + const currentIndex = depths.indexOf(url); + + if (currentIndex >= 0) { + depths.splice(currentIndex, 1); + resetHash(getStorageKey(url)); + } + if (prevIndex >= 0) { + const addedIndex = currentIndex === -1 || currentIndex > prevIndex ? + prevIndex : + prevIndex - 1; + + depths.splice(addedIndex, 0, url); + } else { + depths.push(url); + } + trySetPersistState(CONST_DEPTHS, depths); + trySetPersistState(CONST_LAST_URL, url); + } catch (e) { + // revert PersistHistory.url + PersistHistory.url = prevUrl; + throw e; + } +} + +export function updateDepth(type = 0) { + const url = getUrl(); + + if (PersistHistory.useHash) { + updateHashDepth(type); + } + if (PersistHistory.url === url) { + return; + } + // url is not the same for the first time, pushState, or replaceState. + const prevUrl = PersistHistory.url; + + try { + PersistHistory.url = url; + const depths = getDepths(); + + if (type === TYPE_BACK_FORWARD) { + // Change current url only + const currentIndex = depths.indexOf(url); + + ~currentIndex && trySetPersistState(CONST_LAST_URL, url); + } else { + const prevLastUrl = getPersistState(CONST_LAST_URL); + + // remove url key + // remove hash key for first refresh or first navigate + resetHash(getStorageKey(url)); + + if (type === TYPE_NAVIGATE && url !== prevLastUrl) { + // Remove all url lists with higher index than current index + const prevLastIndex = depths.indexOf(prevLastUrl); + + if (prevLastIndex >= -1) { + const removedList = depths.splice(prevLastIndex + 1, depths.length); + + removedList.forEach(removedUrl => { + reset(getStorageKey(removedUrl)); + }); + } + // If the type is NAVIGATE and there is information about current url, delete it. + const currentIndex = depths.indexOf(url); + + ~currentIndex && depths.splice(currentIndex, 1); + } + // Add depth for new address. + if (depths.indexOf(url) < 0) { + depths.push(url); + } + trySetPersistState(CONST_DEPTHS, depths); + trySetPersistState(CONST_LAST_URL, url); + } + } catch (e) { + // revert PersistHistory.url + PersistHistory.url = prevUrl; + throw e; + } +} + +export function clear() { + const depths = getDepths(); + + depths.forEach(url => { + reset(getStorageKey(url)); + }); + + reset(CONST_PERSIST_STATE); + + PersistHistory.url = ""; + PersistHistory.hashUrl = ""; + PersistHistory.hash = null; +} + +export function registerHashPersist() { + if (PersistHistory.useHash) { + return; + } + PersistHistory.useHash = true; + updateHashDepth(getNavigationType()); +} + +export function releaseEvent() { + window.removeEventListener("popstate", onPopState); +} +function onPopState() { + // popstate event occurs when backward or forward + try { + updateDepth(TYPE_BACK_FORWARD); + } catch (e) { + // Global function calls prevent errors. + if (!isQuotaExceededError(e)) { + throw e; + } + } +} +// execute global +if ("onpopstate" in window) { + window.addEventListener("popstate", onPopState); +} + +// If navigation's type is not TYPE_BACK_FORWARD, delete information about current url. +try { + updateDepth(getNavigationType()); +} catch (e) { + // Global function calls prevent errors. + if (!isQuotaExceededError(e)) { + throw e; + } +} diff --git a/src/index.js b/src/index.js index a677072..992ac50 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,20 @@ -import Persist, {updateDepth, replaceDepth} from "./Persist"; +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ +import Persist from "./Persist"; +import HashPersist from "./HashPersist"; import PersistQuotaExceededError from "./PersistQuotaExceededError"; - export { updateDepth, replaceDepth, + registerHashPersist, + releaseEvent, +} from "./historyManager"; + +export { + HashPersist, PersistQuotaExceededError, }; diff --git a/src/index.umd.js b/src/index.umd.js index 337d5c7..9395498 100755 --- a/src/index.umd.js +++ b/src/index.umd.js @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ import Persist, * as modules from "./index"; for (const name in modules) { diff --git a/src/persist-migrate.js b/src/persist-migrate.js index 6bd3dba..b42e2f0 100644 --- a/src/persist-migrate.js +++ b/src/persist-migrate.js @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ /* eslint-disable */ import {window} from "./browser"; import { getStorageKey, getUrl } from "./utils"; diff --git a/src/storageManager.js b/src/storageManager.js index 09950dc..e9ae07c 100644 --- a/src/storageManager.js +++ b/src/storageManager.js @@ -1,5 +1,9 @@ -import {window, history, location, sessionStorage, localStorage} from "./browser"; -import {CONST_PERSIST} from "./consts"; +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ +import {window, console, history, location, sessionStorage, localStorage} from "./browser"; +import {CONST_DEPTHS, CONST_PERSIST, CONST_PERSIST_STATE} from "./consts"; const isSupportState = history && "replaceState" in history && "state" in history; let storageType = "None"; @@ -48,7 +52,7 @@ function warnInvalidStorageValue() { /* * Get state value */ -function getState(key) { +export function getState(key) { let state; let stateStr; @@ -95,7 +99,7 @@ function getState(key) { /* * Set state value */ -function setState(key, state) { +export function setState(key, state) { if (storage) { if (state) { storage.setItem( @@ -169,3 +173,11 @@ export function setStateByKey(key, valueKey, data) { export function reset(key) { setState(key, null); } + +export function getPersistState(key) { + return getStateByKey(CONST_PERSIST_STATE, key); +} + +export function getDepths() { + return getPersistState(CONST_DEPTHS) || []; +} diff --git a/src/utils.js b/src/utils.js index fda2eff..3543fd9 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,8 @@ -import {navigator, parseFloat, performance, location} from "./browser"; +/* + * Copyright (c) 2015 NAVER Corp. + * egjs projects are licensed under the MIT license + */ +import {navigator, parseFloat, performance, location, console} from "./browser"; import {CONST_PERSIST} from "./consts"; const userAgent = navigator ? navigator.userAgent : ""; @@ -26,14 +30,43 @@ export function getNavigationType() { performance.navigation.type; } + +export function getHashUrl() { + return location ? location.href : ""; +} + export function getUrl() { - return location ? location.href.split("#")[0] : ""; + return getHashUrl().split("#")[0]; } export function getStorageKey(name) { return name + CONST_PERSIST; } -export function isQuotaExceededError(e) { - return e.name === "QuotaExceededError" || e.name === "PersistQuotaExceededError"; +export function getUrlKey() { + return getStorageKey(getUrl()); +} + +export function execRec(obj, path, func) { + let _obj = obj; + + if (!_obj) { + const firstElement = path[0]; + + _obj = isNaN(firstElement) || firstElement === "" ? {} : []; + } + + + const head = path.shift(); + + if (path.length === 0) { + if (_obj instanceof Array && isNaN(head)) { + console.warn("Don't use key string on array"); + } + func(_obj, head); + return _obj; + } + + _obj[head] = execRec(_obj[head], path, func); + return _obj; } diff --git a/test/manual/hash.html b/test/manual/hash.html new file mode 100755 index 0000000..10eb5c4 --- /dev/null +++ b/test/manual/hash.html @@ -0,0 +1,46 @@ + +
+