Source: plugin/persist.js

  1. /**
  2. * Copyright (c) 2015 NAVER Corp.
  3. * egjs projects are licensed under the MIT license
  4. */
  5. // jscs:disable maximumLineLength
  6. eg.module("persist", ["jQuery", eg, window, document], function($, ns, global, doc) {
  7. "use strict";
  8. // jscs:enable maximumLineLength
  9. var wp = global.performance;
  10. var history = global.history;
  11. var agent = ns.agent();
  12. var isNeeded = (function() {
  13. var version = parseFloat(agent.os.version);
  14. return !(
  15. agent.os.name === "ios" ||
  16. (agent.os.name === "mac" && agent.browser.name === "safari") ||
  17. (agent.os.name === "android" &&
  18. (version <= 4.3 && agent.browser.webview === true || version < 3))
  19. );
  20. })();
  21. var JSON = global.JSON;
  22. var CONST_PERSIST = "___persist___";
  23. var GLOBAL_KEY = "KEY" + CONST_PERSIST;
  24. var $global = $(global);
  25. var isPersisted = $global.attr(CONST_PERSIST) === true;
  26. // In case of IE8, TYPE_BACK_FORWARD is undefined.
  27. var isBackForwardNavigated = (wp && wp.navigation &&
  28. (wp.navigation.type === (wp.navigation.TYPE_BACK_FORWARD || 2)));
  29. var isSupportState = "replaceState" in history && "state" in history;
  30. var storage = (function() {
  31. if (isStorageAvailable(global.sessionStorage)) {
  32. return global.sessionStorage;
  33. } else if (isStorageAvailable(global.localStorage)) {
  34. return global.localStorage;
  35. }
  36. })();
  37. function isStorageAvailable(storage) {
  38. if (!storage) {
  39. return;
  40. }
  41. var TMP_KEY = "__tmp__" + CONST_PERSIST;
  42. try {
  43. // In case of iOS safari private mode, calling setItem on storage throws error
  44. storage.setItem(TMP_KEY, CONST_PERSIST);
  45. // In Chrome incognito mode, can not get saved value
  46. // In IE8, calling storage.getItem occasionally makes "Permission denied" error
  47. return storage.getItem(TMP_KEY) === CONST_PERSIST;
  48. } catch (e) {
  49. return false;
  50. }
  51. }
  52. if (!isSupportState && !storage) {
  53. return;
  54. }
  55. // jscs:disable maximumLineLength
  56. /* jshint ignore:start */
  57. if (!JSON) {
  58. console.warn(
  59. "The JSON object is not supported in your browser.\r\n" +
  60. "For work around use polyfill which can be found at:\r\n" +
  61. "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON#Polyfill");
  62. return;
  63. }
  64. /* jshint ignore:end */
  65. // jscs:enable maximumLineLength
  66. /**
  67. * This jQuery custom event is fired when device rotates.
  68. *
  69. * @ko 기기가 회전할 때 발생하는 jQuery 커스텀 이벤트
  70. * @name jQuery#persist
  71. * @event
  72. * @deprecated since version 1.2.0
  73. * @example
  74. * $(window).on("persist",function(){
  75. * var state = $.persist("KEY");
  76. * // Restore state
  77. * });
  78. *
  79. */
  80. function onPageshow(e) {
  81. isPersisted = isPersisted || (e.originalEvent && e.originalEvent.persisted);
  82. if (!isPersisted && isBackForwardNavigated) {
  83. $global.trigger("persist");
  84. } else {
  85. reset();
  86. }
  87. }
  88. /*
  89. * flush current history state
  90. */
  91. function reset() {
  92. setState(null);
  93. }
  94. /*
  95. * Get state value
  96. */
  97. function getState() {
  98. var state;
  99. var stateStr = storage ?
  100. storage.getItem(global.location.href + CONST_PERSIST) : history.state;
  101. // the storage is clean
  102. if (stateStr === null) {
  103. return {};
  104. }
  105. // "null" is not a valid
  106. var isValidStateStr = typeof stateStr === "string" &&
  107. stateStr.length > 0 && stateStr !== "null";
  108. var isValidType;
  109. try {
  110. state = JSON.parse(stateStr);
  111. // like '[ ... ]', '1', '1.234', '"123"' is also not valid
  112. isValidType = !($.type(state) !== "object" || state instanceof Array);
  113. if (!isValidStateStr || !isValidType) {
  114. throw new Error();
  115. }
  116. } catch (e) {
  117. warnInvalidStorageValue();
  118. state = {};
  119. }
  120. // Note2 (Android 4.3) return value is null
  121. return state;
  122. }
  123. function warnInvalidStorageValue() {
  124. /* jshint ignore:start */
  125. console.warn("window.history or session/localStorage has no valid " +
  126. "format data to be handled in persist.");
  127. /* jshint ignore:end */
  128. }
  129. function getStateByKey(key) {
  130. var result = getState()[key];
  131. // some device returns "null" or undefined
  132. if (result === "null" || typeof result === "undefined") {
  133. result = null;
  134. }
  135. return result;
  136. }
  137. /*
  138. * Set state value
  139. */
  140. function setState(state) {
  141. if (storage) {
  142. if (state) {
  143. storage.setItem(
  144. global.location.href + CONST_PERSIST, JSON.stringify(state));
  145. } else {
  146. storage.removeItem(global.location.href + CONST_PERSIST);
  147. }
  148. } else {
  149. try {
  150. history.replaceState(
  151. state === null ? null : JSON.stringify(state),
  152. doc.title,
  153. global.location.href
  154. );
  155. } catch (e) {
  156. /* jshint ignore:start */
  157. console.warn(e.message);
  158. /* jshint ignore:end */
  159. }
  160. }
  161. state ? $global.attr(CONST_PERSIST, true) : $global.attr(CONST_PERSIST, null);
  162. }
  163. function setStateByKey(key, data) {
  164. var beforeData = getState();
  165. beforeData[key] = data;
  166. setState(beforeData);
  167. }
  168. /**
  169. * Get or store the current state of the web page using JSON.
  170. * @ko 웹 페이지의 현재 상태를 JSON 형식으로 저장하거나 읽는다.
  171. * @method jQuery.persist
  172. * @param {String} key The key of the state information to be stored <ko>저장할 상태 정보의 키</ko>
  173. * @param {Object} [state] The value to be stored in a given key <ko>키에 저장할 값</ko>
  174. * @example
  175. // when 'key' and 'value' are given, it saves state object
  176. $.persist("KEY",state);
  177. // when only 'key' is given, it loads state object
  178. var state = $.persist("KEY");
  179. * @example
  180. // this is deprecated API
  181. // save state without Key
  182. $.persist(state);
  183. // get state without Key
  184. var state = $.persist();
  185. */
  186. $.persist = function(state, data) {
  187. var key;
  188. if (typeof state === "string") {
  189. key = state;
  190. } else {
  191. key = GLOBAL_KEY;
  192. data = arguments.length === 1 ? state : null;
  193. }
  194. if (data || arguments.length === 2) {
  195. setStateByKey(key, data);
  196. }
  197. return getStateByKey(key);
  198. };
  199. /**
  200. * Return whether you need "Persist" module by checking the bfCache support of the current browser
  201. * @ko 현재 브라우저의 bfCache 지원여부에 따라 persist 모듈의 필요여부를 반환한다.
  202. * @group jQuery Extension
  203. * @namespace
  204. * @property {function} isNeeded
  205. * @example
  206. $.persist.isNeeded();
  207. */
  208. $.persist.isNeeded = function() {
  209. return isNeeded;
  210. };
  211. // in case of reload
  212. !isBackForwardNavigated && reset();
  213. $.event.special.persist = {
  214. setup: function() {
  215. $global.on("pageshow", onPageshow);
  216. },
  217. teardown: function() {
  218. $global.off("pageshow", onPageshow);
  219. },
  220. trigger: function(e) {
  221. //If you use 'persist' event, you can get global-key only!
  222. e.state = getStateByKey(GLOBAL_KEY);
  223. }
  224. };
  225. return {
  226. "isBackForwardNavigated": isBackForwardNavigated,
  227. "onPageshow": onPageshow,
  228. "reset": reset,
  229. "getState": getState,
  230. "setState": setState,
  231. "GLOBALKEY": GLOBAL_KEY
  232. };
  233. });
comments powered by Disqus