forked from reddit-archive/reddit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Matt Lee
committed
Oct 7, 2016
1 parent
01ce8b5
commit ad08159
Showing
1 changed file
with
143 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
!function(r) { | ||
|
||
// this key will be used to sync sessionStorages through localStorage | ||
var SYNC_EVENT_KEY = '__synced_session_storage__'; | ||
var PERSIST_SYNCED_KEYS_KEY = '__synced_session_storage_keys__'; | ||
|
||
/* | ||
SessionStorage is too restrictive; each new tab in sessionStorage is | ||
considered a separate session. LocalStorage never expires; manually | ||
expiring data is a pain, and often we just want data to last for the | ||
(logical) session. Enter SyncedSessionStorage. | ||
SyncedSessionStorage is a wrapper around SessionStorage that uses | ||
LocalStorage events for syncing data across multiple open tabs. | ||
*/ | ||
function SyncedSessionStorage(sync_key) { | ||
var persisted_synced_keys = sessionStorage.getItem(PERSIST_SYNCED_KEYS_KEY); | ||
|
||
if (persisted_synced_keys) { | ||
// existing session in this tab, use the existing sessionStorage data | ||
this._bootstrapped = true; | ||
this._synced_storage_keys = JSON.parse(persisted_synced_keys); | ||
} else { | ||
// send a request to bootstrap from existing sessions if there are any | ||
// any existing sessions will send back a 'bootstrap' event containing | ||
// the bootstrap data | ||
this._bootstrapped = false; | ||
this._synced_storage_keys = {}; | ||
this._sync({ | ||
type: 'init', | ||
}); | ||
} | ||
|
||
window.addEventListener('storage', function(e) { | ||
// the localStorage.removeItem will come in with a null newValue | ||
if (e.key === SYNC_EVENT_KEY && e.newValue) { | ||
var event = JSON.parse(e.newValue); | ||
this._handleSync(event); | ||
} | ||
}.bind(this)); | ||
} | ||
|
||
SyncedSessionStorage.prototype = { | ||
constructor: SyncedSessionStorage, | ||
|
||
getItem: function(key) { | ||
if (key in this._synced_storage_keys) { | ||
return sessionStorage.getItem(key); | ||
} else { | ||
return null; | ||
} | ||
}, | ||
|
||
setItem: function(key, value) { | ||
this._setItem(key, value); | ||
this._sync({ | ||
type: 'set', | ||
key: key, | ||
value: value.toString(), | ||
}); | ||
}, | ||
|
||
removeItem: function(key) { | ||
if (key in this._synced_storage_keys) { | ||
this._removeItem(key); | ||
this._sync({ | ||
type: 'remove', | ||
key: key, | ||
}); | ||
} | ||
}, | ||
|
||
_sync: function(event) { | ||
// set and remove event data from localStorage, triggering storage event | ||
localStorage.setItem(SYNC_EVENT_KEY, JSON.stringify(event)); | ||
localStorage.removeItem(SYNC_EVENT_KEY); | ||
}, | ||
|
||
_handleSync: function(event) { | ||
// handle the storage event triggered from other tabs | ||
if (event.type === 'set') { | ||
this._setItem(event.key, event.value); | ||
} else if (event.type === 'remove') { | ||
this._removeItem(event.key); | ||
} else if (event.type === 'init') { | ||
this._sendBootstrapEvent(); | ||
} else if (event.type === 'bootstrap') { | ||
this._handleBootstrapEvent(event.payload); | ||
} | ||
}, | ||
|
||
_sendBootstrapEvent: function() { | ||
// when a new session needs bootstrapping, send entire current state | ||
// if we've seen an 'init' event before receiving a 'bootstrap' event, it | ||
// almost certainly means this was just the first tab open in the session | ||
if (!this._bootstrapped) { | ||
this._bootstrapped = true; | ||
} | ||
|
||
var payload = {}; | ||
|
||
for (var key in this._synced_storage_keys) { | ||
payload[key] = sessionStorage.getItem(key); | ||
} | ||
|
||
this._sync({ | ||
type: 'bootstrap', | ||
payload: payload, | ||
}); | ||
}, | ||
|
||
_handleBootstrapEvent: function(payload) { | ||
if (this._bootstrapped) { return; } | ||
|
||
for (var key in payload) { | ||
this._synced_storage_keys[key] = 1; | ||
sessionStorage.setItem(key, payload[key]); | ||
} | ||
|
||
this._bootstrapped = true; | ||
}, | ||
|
||
_removeItem: function(key) { | ||
sessionStorage.removeItem(key); | ||
delete this._synced_storage_keys[key]; | ||
sessionStorage.setItem( | ||
PERSIST_SYNCED_KEYS_KEY, | ||
JSON.stringify(this._synced_storage_keys) | ||
) | ||
}, | ||
|
||
_setItem: function(key, value) { | ||
sessionStorage.setItem(key, value); | ||
this._synced_storage_keys[key] = 1; | ||
sessionStorage.setItem( | ||
PERSIST_SYNCED_KEYS_KEY, | ||
JSON.stringify(this._synced_storage_keys) | ||
) | ||
}, | ||
}; | ||
|
||
r.syncedSessionStorage = new SyncedSessionStorage(); | ||
}(r); |