Skip to content

Instantly share code, notes, and snippets.

@majuric
Forked from jarrodirwin/storagePolyfill.js
Last active August 7, 2016 08:03
Show Gist options
  • Save majuric/fa08e4adc5277c2588f5 to your computer and use it in GitHub Desktop.
Save majuric/fa08e4adc5277c2588f5 to your computer and use it in GitHub Desktop.
LocalStorage/SessionStorage Polyfill with Safari Private Browsing support.
// Refer to:
// https://gist.github.com/remy/350433
// https://gist.github.com/jarrodirwin/0ce4c0888336b533b2c4
try {
// Test webstorage existence.
if (!window.localStorage || !window.sessionStorage) throw "exception";
// Test webstorage accessibility - Needed for Safari private browsing.
localStorage.setItem('storage_test', 1);
localStorage.removeItem('storage_test');
} catch(e) {
(function () {
var Storage = function (type) {
function createCookie(name, value, days) {
var date, expires;
if (days) {
date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
expires = "; expires="+date.toGMTString();
} else {
expires = "";
}
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=",
ca = document.cookie.split(';'),
i, c;
for (i=0; i < ca.length; i++) {
c = ca[i];
while (c.charAt(0)==' ') {
c = c.substring(1,c.length); 2480
}
if (c.indexOf(nameEQ) == 0) {
return c.substring(nameEQ.length,c.length);
}
}
return null;
}
function setData(data) {
// Convert data into JSON and encode to accommodate for special characters.
data = encodeURIComponent(JSON.stringify(data));
// Create cookie.
if (type == 'session') {
createCookie(getSessionName(), data, 365);
} else {
createCookie('localStorage', data, 365);
}
}
function clearData() {
if (type == 'session') {
createCookie(getSessionName(), '', 365);
} else {
createCookie('localStorage', '', 365);
}
}
function getData() {
// Get cookie data.
var data = type == 'session' ? readCookie(getSessionName()) : readCookie('localStorage');
// If we have some data decode, parse and return it.
return data ? JSON.parse(decodeURIComponent(data)) : {};
}
function getSessionName() {
// If there is no name for this window, set one.
// To ensure it's unquie use the current timestamp.
if(!window.name) {
window.name = new Date().getTime();
}
return 'sessionStorage' + window.name;
}
// Initialise if there's already data.
var data = getData();
return {
length: 0,
clear: function () {
data = {};
this.length = 0;
clearData();
},
getItem: function (key) {
return data[key] === undefined ? null : data[key];
},
key: function (i) {
// not perfect, but works
var ctr = 0;
for (var k in data) {
if (ctr == i) return k;
else ctr++;
}
return null;
},
removeItem: function (key) {
delete data[key];
this.length--;
setData(data);
},
setItem: function (key, value) {
data[key] = value+''; // forces the value to a string
this.length++;
setData(data);
}
};
};
// Replace window.localStorage and window.sessionStorage with out custom
// implementation.
var localStorage = new Storage('local');
var sessionStorage = new Storage('session');
window.localStorage = localStorage;
window.sessionStorage = sessionStorage;
// For Safari private browsing need to also set the proto value.
// Since accessing local storage when it's disabled in the browser
// or in the new version of Safari proto changing throws an error
// wrap it in a try catch block.
try {
window.localStorage.__proto__ = localStorage;
window.sessionStorage.__proto__ = sessionStorage;
} catch (err) {
console.log(err);
}
})();
}
@Thinkscape
Copy link

Warning - this polyfill will fail in many browsers

Even with the try/catch block, localStorage will still be broken in many cases. When it is disabled due to some privacy settings, any attempts to access window.localStorage or setItem() getItem() methods will throw exceptions, regardless of the polyfill.

Here are some notable examples where it breaks:

  • Mobile Chrome as a webview in many apps (i.e. official Twitter App on Android)
  • Chrome and Chrome Mobile with Local storage/cookies disabled (privacy settings).
  • Firefox with localStorage disabled.

The only reasonable workaround is using a wrapper instead of relying on window.localStorage function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment