PageRenderTime 63ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/files/jquery.layout/1.4.3/plugins/jquery.layout.state.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 474 lines | 281 code | 42 blank | 151 comment | 99 complexity | afda80b1610557fa300357ec07132a34 MD5 | raw file
  1. /**
  2. * jquery.layout.state 1.2
  3. * $Date: 2014-08-30 08:00:00 (Sat, 30 Aug 2014) $
  4. *
  5. * Copyright (c) 2014
  6. * Kevin Dalman (http://allpro.net)
  7. *
  8. * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
  9. * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
  10. *
  11. * @requires: UI Layout 1.4.0 or higher
  12. * @requires: $.ui.cookie (above)
  13. *
  14. * @see: http://groups.google.com/group/jquery-ui-layout
  15. */
  16. // NOTE: For best readability, view with a fixed-width font and tabs equal to 4-chars
  17. ;(function ($) {
  18. if (!$.layout) return;
  19. /**
  20. * UI COOKIE UTILITY
  21. *
  22. * A $.cookie OR $.ui.cookie namespace *should be standard*, but until then...
  23. * This creates $.ui.cookie so Layout does not need the cookie.jquery.js plugin
  24. * NOTE: This utility is REQUIRED by the layout.state plugin
  25. *
  26. * Cookie methods in Layout are created as part of State Management
  27. */
  28. if (!$.ui) $.ui = {};
  29. $.ui.cookie = {
  30. // cookieEnabled is not in DOM specs, but DOES works in all browsers,including IE6
  31. acceptsCookies: !!navigator.cookieEnabled
  32. , read: function (name) {
  33. var
  34. c = document.cookie
  35. , cs = c ? c.split(';') : []
  36. , pair, data, i
  37. ;
  38. for (i=0; pair=cs[i]; i++) {
  39. data = $.trim(pair).split('='); // name=value => [ name, value ]
  40. if (data[0] == name) // found the layout cookie
  41. return decodeURIComponent(data[1]);
  42. }
  43. return null;
  44. }
  45. , write: function (name, val, cookieOpts) {
  46. var params = ""
  47. , date = ""
  48. , clear = false
  49. , o = cookieOpts || {}
  50. , x = o.expires || null
  51. , t = $.type(x)
  52. ;
  53. if (t === "date")
  54. date = x;
  55. else if (t === "string" && x > 0) {
  56. x = parseInt(x,10);
  57. t = "number";
  58. }
  59. if (t === "number") {
  60. date = new Date();
  61. if (x > 0)
  62. date.setDate(date.getDate() + x);
  63. else {
  64. date.setFullYear(1970);
  65. clear = true;
  66. }
  67. }
  68. if (date) params += ";expires="+ date.toUTCString();
  69. if (o.path) params += ";path="+ o.path;
  70. if (o.domain) params += ";domain="+ o.domain;
  71. if (o.secure) params += ";secure";
  72. document.cookie = name +"="+ (clear ? "" : encodeURIComponent( val )) + params; // write or clear cookie
  73. }
  74. , clear: function (name) {
  75. $.ui.cookie.write(name, "", {expires: -1});
  76. }
  77. };
  78. // if cookie.jquery.js is not loaded, create an alias to replicate it
  79. // this may be useful to other plugins or code dependent on that plugin
  80. if (!$.cookie) $.cookie = function (k, v, o) {
  81. var C = $.ui.cookie;
  82. if (v === null)
  83. C.clear(k);
  84. else if (v === undefined)
  85. return C.read(k);
  86. else
  87. C.write(k, v, o);
  88. };
  89. /**
  90. * State-management options stored in options.stateManagement, which includes a .cookie hash
  91. * Default options saves ALL KEYS for ALL PANES, ie: pane.size, pane.isClosed, pane.isHidden
  92. *
  93. * // STATE/COOKIE OPTIONS
  94. * @example $(el).layout({
  95. stateManagement: {
  96. enabled: true
  97. , stateKeys: "east.size,west.size,east.isClosed,west.isClosed"
  98. , cookie: { name: "appLayout", path: "/" }
  99. }
  100. })
  101. * @example $(el).layout({ stateManagement__enabled: true }) // enable auto-state-management using cookies
  102. * @example $(el).layout({ stateManagement__cookie: { name: "appLayout", path: "/" } })
  103. * @example $(el).layout({ stateManagement__cookie__name: "appLayout", stateManagement__cookie__path: "/" })
  104. *
  105. * // STATE/COOKIE METHODS
  106. * @example myLayout.saveCookie( "west.isClosed,north.size,south.isHidden", {expires: 7} );
  107. * @example myLayout.loadCookie();
  108. * @example myLayout.deleteCookie();
  109. * @example var JSON = myLayout.readState(); // CURRENT Layout State
  110. * @example var JSON = myLayout.readCookie(); // SAVED Layout State (from cookie)
  111. * @example var JSON = myLayout.state.stateData; // LAST LOADED Layout State (cookie saved in layout.state hash)
  112. *
  113. * CUSTOM STATE-MANAGEMENT (eg, saved in a database)
  114. * @example var JSON = myLayout.readState( "west.isClosed,north.size,south.isHidden" );
  115. * @example myLayout.loadState( JSON );
  116. */
  117. // tell Layout that the state plugin is available
  118. $.layout.plugins.stateManagement = true;
  119. // Add State-Management options to layout.defaults
  120. $.layout.defaults.stateManagement = {
  121. enabled: false // true = enable state-management, even if not using cookies
  122. , autoSave: true // Save a state-cookie when page exits?
  123. , autoLoad: true // Load the state-cookie when Layout inits?
  124. , animateLoad: true // animate panes when loading state into an active layout
  125. , includeChildren: true // recurse into child layouts to include their state as well
  126. // List state-data to save - must be pane-specific
  127. , stateKeys: "north.size,south.size,east.size,west.size,"+
  128. "north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+
  129. "north.isHidden,south.isHidden,east.isHidden,west.isHidden"
  130. , cookie: {
  131. name: "" // If not specified, will use Layout.name, else just "Layout"
  132. , domain: "" // blank = current domain
  133. , path: "" // blank = current page, "/" = entire website
  134. , expires: "" // 'days' to keep cookie - leave blank for 'session cookie'
  135. , secure: false
  136. }
  137. };
  138. // Set stateManagement as a 'layout-option', NOT a 'pane-option'
  139. $.layout.optionsMap.layout.push("stateManagement");
  140. // Update config so layout does not move options into the pane-default branch (panes)
  141. $.layout.config.optionRootKeys.push("stateManagement");
  142. /*
  143. * State Management methods
  144. */
  145. $.layout.state = {
  146. /**
  147. * Get the current layout state and save it to a cookie
  148. *
  149. * myLayout.saveCookie( keys, cookieOpts )
  150. *
  151. * @param {Object} inst
  152. * @param {(string|Array)=} keys
  153. * @param {Object=} cookieOpts
  154. */
  155. saveCookie: function (inst, keys, cookieOpts) {
  156. var o = inst.options
  157. , sm = o.stateManagement
  158. , oC = $.extend(true, {}, sm.cookie, cookieOpts || null)
  159. , data = inst.state.stateData = inst.readState( keys || sm.stateKeys ) // read current panes-state
  160. ;
  161. $.ui.cookie.write( oC.name || o.name || "Layout", $.layout.state.encodeJSON(data), oC );
  162. return $.extend(true, {}, data); // return COPY of state.stateData data
  163. }
  164. /**
  165. * Remove the state cookie
  166. *
  167. * @param {Object} inst
  168. */
  169. , deleteCookie: function (inst) {
  170. var o = inst.options;
  171. $.ui.cookie.clear( o.stateManagement.cookie.name || o.name || "Layout" );
  172. }
  173. /**
  174. * Read & return data from the cookie - as JSON
  175. *
  176. * @param {Object} inst
  177. */
  178. , readCookie: function (inst) {
  179. var o = inst.options;
  180. var c = $.ui.cookie.read( o.stateManagement.cookie.name || o.name || "Layout" );
  181. // convert cookie string back to a hash and return it
  182. return c ? $.layout.state.decodeJSON(c) : {};
  183. }
  184. /**
  185. * Get data from the cookie and USE IT to loadState
  186. *
  187. * @param {Object} inst
  188. */
  189. , loadCookie: function (inst) {
  190. var c = $.layout.state.readCookie(inst); // READ the cookie
  191. if (c && !$.isEmptyObject( c )) {
  192. inst.state.stateData = $.extend(true, {}, c); // SET state.stateData
  193. inst.loadState(c); // LOAD the retrieved state
  194. }
  195. return c;
  196. }
  197. /**
  198. * Update layout options from the cookie, if one exists
  199. *
  200. * @param {Object} inst
  201. * @param {Object=} stateData
  202. * @param {boolean=} animate
  203. */
  204. , loadState: function (inst, data, opts) {
  205. if (!$.isPlainObject( data ) || $.isEmptyObject( data )) return;
  206. // normalize data & cache in the state object
  207. data = inst.state.stateData = $.layout.transformData( data ); // panes = default subkey
  208. // add missing/default state-restore options
  209. var smo = inst.options.stateManagement;
  210. opts = $.extend({
  211. animateLoad: false //smo.animateLoad
  212. , includeChildren: smo.includeChildren
  213. }, opts );
  214. if (!inst.state.initialized) {
  215. /*
  216. * layout NOT initialized, so just update its options
  217. */
  218. // MUST remove pane.children keys before applying to options
  219. // use a copy so we don't remove keys from original data
  220. var o = $.extend(true, {}, data);
  221. //delete o.center; // center has no state-data - only children
  222. $.each($.layout.config.allPanes, function (idx, pane) {
  223. if (o[pane]) delete o[pane].children;
  224. });
  225. // update CURRENT layout-options with saved state data
  226. $.extend(true, inst.options, o);
  227. }
  228. else {
  229. /*
  230. * layout already initialized, so modify layout's configuration
  231. */
  232. var noAnimate = !opts.animateLoad
  233. , o, c, h, state, open
  234. ;
  235. $.each($.layout.config.borderPanes, function (idx, pane) {
  236. o = data[ pane ];
  237. if (!$.isPlainObject( o )) return; // no key, skip pane
  238. s = o.size;
  239. c = o.initClosed;
  240. h = o.initHidden;
  241. ar = o.autoResize
  242. state = inst.state[pane];
  243. open = state.isVisible;
  244. // reset autoResize
  245. if (ar)
  246. state.autoResize = ar;
  247. // resize BEFORE opening
  248. if (!open)
  249. inst._sizePane(pane, s, false, false, false); // false=skipCallback/noAnimation/forceResize
  250. // open/close as necessary - DO NOT CHANGE THIS ORDER!
  251. if (h === true) inst.hide(pane, noAnimate);
  252. else if (c === true) inst.close(pane, false, noAnimate);
  253. else if (c === false) inst.open (pane, false, noAnimate);
  254. else if (h === false) inst.show (pane, false, noAnimate);
  255. // resize AFTER any other actions
  256. if (open)
  257. inst._sizePane(pane, s, false, false, noAnimate); // animate resize if option passed
  258. });
  259. /*
  260. * RECURSE INTO CHILD-LAYOUTS
  261. */
  262. if (opts.includeChildren) {
  263. var paneStateChildren, childState;
  264. $.each(inst.children, function (pane, paneChildren) {
  265. paneStateChildren = data[pane] ? data[pane].children : 0;
  266. if (paneStateChildren && paneChildren) {
  267. $.each(paneChildren, function (stateKey, child) {
  268. childState = paneStateChildren[stateKey];
  269. if (child && childState)
  270. child.loadState( childState );
  271. });
  272. }
  273. });
  274. }
  275. }
  276. }
  277. /**
  278. * Get the *current layout state* and return it as a hash
  279. *
  280. * @param {Object=} inst // Layout instance to get state for
  281. * @param {object=} [opts] // State-Managements override options
  282. */
  283. , readState: function (inst, opts) {
  284. // backward compatility
  285. if ($.type(opts) === 'string') opts = { keys: opts };
  286. if (!opts) opts = {};
  287. var sm = inst.options.stateManagement
  288. , ic = opts.includeChildren
  289. , recurse = ic !== undefined ? ic : sm.includeChildren
  290. , keys = opts.stateKeys || sm.stateKeys
  291. , alt = { isClosed: 'initClosed', isHidden: 'initHidden' }
  292. , state = inst.state
  293. , panes = $.layout.config.allPanes
  294. , data = {}
  295. , pair, pane, key, val
  296. , ps, pC, child, array, count, branch
  297. ;
  298. if ($.isArray(keys)) keys = keys.join(",");
  299. // convert keys to an array and change delimiters from '__' to '.'
  300. keys = keys.replace(/__/g, ".").split(',');
  301. // loop keys and create a data hash
  302. for (var i=0, n=keys.length; i < n; i++) {
  303. pair = keys[i].split(".");
  304. pane = pair[0];
  305. key = pair[1];
  306. if ($.inArray(pane, panes) < 0) continue; // bad pane!
  307. val = state[ pane ][ key ];
  308. if (val == undefined) continue;
  309. if (key=="isClosed" && state[pane]["isSliding"])
  310. val = true; // if sliding, then *really* isClosed
  311. ( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val;
  312. }
  313. // recurse into the child-layouts for each pane
  314. if (recurse) {
  315. $.each(panes, function (idx, pane) {
  316. pC = inst.children[pane];
  317. ps = state.stateData[pane];
  318. if ($.isPlainObject( pC ) && !$.isEmptyObject( pC )) {
  319. // ensure a key exists for this 'pane', eg: branch = data.center
  320. branch = data[pane] || (data[pane] = {});
  321. if (!branch.children) branch.children = {};
  322. $.each( pC, function (key, child) {
  323. // ONLY read state from an initialize layout
  324. if ( child.state.initialized )
  325. branch.children[ key ] = $.layout.state.readState( child );
  326. // if we have PREVIOUS (onLoad) state for this child-layout, KEEP IT!
  327. else if ( ps && ps.children && ps.children[ key ] ) {
  328. branch.children[ key ] = $.extend(true, {}, ps.children[ key ] );
  329. }
  330. });
  331. }
  332. });
  333. }
  334. return data;
  335. }
  336. /**
  337. * Stringify a JSON hash so can save in a cookie or db-field
  338. */
  339. , encodeJSON: function (json) {
  340. var local = window.JSON || {};
  341. return (local.stringify || stringify)(json);
  342. function stringify (h) {
  343. var D=[], i=0, k, v, t // k = key, v = value
  344. , a = $.isArray(h)
  345. ;
  346. for (k in h) {
  347. v = h[k];
  348. t = typeof v;
  349. if (t == 'string') // STRING - add quotes
  350. v = '"'+ v +'"';
  351. else if (t == 'object') // SUB-KEY - recurse into it
  352. v = parse(v);
  353. D[i++] = (!a ? '"'+ k +'":' : '') + v;
  354. }
  355. return (a ? '[' : '{') + D.join(',') + (a ? ']' : '}');
  356. };
  357. }
  358. /**
  359. * Convert stringified JSON back to a hash object
  360. * @see $.parseJSON(), adding in jQuery 1.4.1
  361. */
  362. , decodeJSON: function (str) {
  363. try { return $.parseJSON ? $.parseJSON(str) : window["eval"]("("+ str +")") || {}; }
  364. catch (e) { return {}; }
  365. }
  366. , _create: function (inst) {
  367. var s = $.layout.state
  368. , o = inst.options
  369. , sm = o.stateManagement
  370. ;
  371. // ADD State-Management plugin methods to inst
  372. $.extend( inst, {
  373. // readCookie - update options from cookie - returns hash of cookie data
  374. readCookie: function () { return s.readCookie(inst); }
  375. // deleteCookie
  376. , deleteCookie: function () { s.deleteCookie(inst); }
  377. // saveCookie - optionally pass keys-list and cookie-options (hash)
  378. , saveCookie: function (keys, cookieOpts) { return s.saveCookie(inst, keys, cookieOpts); }
  379. // loadCookie - readCookie and use to loadState() - returns hash of cookie data
  380. , loadCookie: function () { return s.loadCookie(inst); }
  381. // loadState - pass a hash of state to use to update options
  382. , loadState: function (stateData, opts) { s.loadState(inst, stateData, opts); }
  383. // readState - returns hash of current layout-state
  384. , readState: function (keys) { return s.readState(inst, keys); }
  385. // add JSON utility methods too...
  386. , encodeJSON: s.encodeJSON
  387. , decodeJSON: s.decodeJSON
  388. });
  389. // init state.stateData key, even if plugin is initially disabled
  390. inst.state.stateData = {};
  391. // autoLoad MUST BE one of: data-array, data-hash, callback-function, or TRUE
  392. if ( !sm.autoLoad ) return;
  393. // When state-data exists in the autoLoad key USE IT,
  394. // even if stateManagement.enabled == false
  395. if ($.isPlainObject( sm.autoLoad )) {
  396. if (!$.isEmptyObject( sm.autoLoad )) {
  397. inst.loadState( sm.autoLoad );
  398. }
  399. }
  400. else if ( sm.enabled ) {
  401. // update the options from cookie or callback
  402. // if options is a function, call it to get stateData
  403. if ($.isFunction( sm.autoLoad )) {
  404. var d = {};
  405. try {
  406. d = sm.autoLoad( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn
  407. } catch (e) {}
  408. if (d && $.isPlainObject( d ) && !$.isEmptyObject( d ))
  409. inst.loadState(d);
  410. }
  411. else // any other truthy value will trigger loadCookie
  412. inst.loadCookie();
  413. }
  414. }
  415. , _unload: function (inst) {
  416. var sm = inst.options.stateManagement;
  417. if (sm.enabled && sm.autoSave) {
  418. // if options is a function, call it to save the stateData
  419. if ($.isFunction( sm.autoSave )) {
  420. try {
  421. sm.autoSave( inst, inst.state, inst.options, inst.options.name || '' ); // try to get data from fn
  422. } catch (e) {}
  423. }
  424. else // any truthy value will trigger saveCookie
  425. inst.saveCookie();
  426. }
  427. }
  428. };
  429. // add state initialization method to Layout's onCreate array of functions
  430. $.layout.onCreate.push( $.layout.state._create );
  431. $.layout.onUnload.push( $.layout.state._unload );
  432. })( jQuery );