mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
91 lines
3.0 KiB
JavaScript
91 lines
3.0 KiB
JavaScript
|
/**
|
||
|
* koSession offers observables whose values are tied to the browser session or history:
|
||
|
*
|
||
|
* sessionValue(key) - an observable preserved across history entries and reloads.
|
||
|
*
|
||
|
* Note: we could also support "browserValue", shared across all tabs and across browser restarts
|
||
|
* (same as sessionValue but using window.localStorage), but it seems more appropriate to store
|
||
|
* such values on the server.
|
||
|
*/
|
||
|
|
||
|
/* global window, $ */
|
||
|
|
||
|
var _ = require('underscore');
|
||
|
var ko = require('knockout');
|
||
|
|
||
|
/**
|
||
|
* Maps a string key to an observable. The space of keys is shared for all kinds of observables,
|
||
|
* and they differ only in where they store their state. Each observable gets several extra
|
||
|
* properties:
|
||
|
* @property {String} ksKey The key used for storage. It should be unique across koSession values.
|
||
|
* @property {Object} ksDefault The default value if the storage doesn't have one.
|
||
|
* @property {Function} ksFetch The method to fetch the value from storage.
|
||
|
* @property {Function} ksSave The method to save the value to storage.
|
||
|
*/
|
||
|
var _sessionValues = {};
|
||
|
|
||
|
function createObservable(key, defaultValue, methods) {
|
||
|
var obs = _sessionValues[key];
|
||
|
if (!obs) {
|
||
|
_sessionValues[key] = obs = ko.observable();
|
||
|
obs.ksKey = key;
|
||
|
obs.ksDefaultValue = defaultValue;
|
||
|
obs.ksFetch = methods.fetch;
|
||
|
obs.ksSave = methods.save;
|
||
|
obs.dispose = methods.dispose;
|
||
|
|
||
|
// We initialize the observable before setting rateLimit, to ensure that the initialization
|
||
|
// doesn't end up triggering subscribers that are about to be added (which seems to be a bit
|
||
|
// of a problem with rateLimit extender, and possibly deferred). This workaround relies on the
|
||
|
// fact that the extender modifies its target without creating a new one.
|
||
|
obs(obs.ksFetch());
|
||
|
obs.extend({deferred: true});
|
||
|
|
||
|
obs.subscribe(function(newValue) {
|
||
|
if (newValue !== this.ksFetch()) {
|
||
|
console.log("koSession: %s changed %s -> %s", this.ksKey, this.ksFetch(), newValue);
|
||
|
this.ksSave(newValue);
|
||
|
}
|
||
|
}, obs);
|
||
|
}
|
||
|
return obs;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns an observable whose value sticks across reloads and navigation, but is different for
|
||
|
* different browser tabs. E.g. it may be used to reflect whether a side pane is open.
|
||
|
* The `key` isn't visible to the user, so pick any unique string name.
|
||
|
*/
|
||
|
function sessionValue(key, optDefault) {
|
||
|
return createObservable(key, optDefault, sessionValueMethods);
|
||
|
}
|
||
|
exports.sessionValue = sessionValue;
|
||
|
|
||
|
var sessionValueMethods = {
|
||
|
'fetch': function() {
|
||
|
var value = window.sessionStorage.getItem(this.ksKey);
|
||
|
if (!value) {
|
||
|
return this.ksDefaultValue;
|
||
|
}
|
||
|
try {
|
||
|
return JSON.parse(value);
|
||
|
} catch (e) {
|
||
|
return this.ksDefaultValue;
|
||
|
}
|
||
|
},
|
||
|
'save': function(value) {
|
||
|
window.sessionStorage.setItem(this.ksKey, JSON.stringify(value));
|
||
|
},
|
||
|
'dispose': function(value) {
|
||
|
window.sessionStorage.removeItem(this.ksKey);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function onApplyState() {
|
||
|
_.each(_sessionValues, function(obs, key) {
|
||
|
obs(obs.ksFetch());
|
||
|
});
|
||
|
}
|
||
|
|
||
|
$(window).on('applyState', onApplyState);
|