-
-
Save remy/350433 to your computer and use it in GitHub Desktop.
if (typeof window.localStorage == 'undefined' || typeof window.sessionStorage == 'undefined') (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); | |
} | |
if (c.indexOf(nameEQ) == 0) { | |
return c.substring(nameEQ.length,c.length); | |
} | |
} | |
return null; | |
} | |
function setData(data) { | |
data = JSON.stringify(data); | |
if (type == 'session') { | |
window.name = data; | |
} else { | |
createCookie('localStorage', data, 365); | |
} | |
} | |
function clearData() { | |
if (type == 'session') { | |
window.name = ''; | |
} else { | |
createCookie('localStorage', '', 365); | |
} | |
} | |
function getData() { | |
var data = type == 'session' ? window.name : readCookie('localStorage'); | |
return data ? JSON.parse(data) : {}; | |
} | |
// 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); | |
} | |
}; | |
}; | |
if (typeof window.localStorage == 'undefined') window.localStorage = new Storage('local'); | |
if (typeof window.sessionStorage == 'undefined') window.sessionStorage = new Storage('session'); | |
})(); |
@brettwejrowski - I'm inclined to totally agree.
@remy, With cookies disabled, merely accessing window.localStorage
will throw a Security exception in Firefox (tested FF15). I'm having to add a try/catch in my app to fail gracefully.
In safari with private browsing, using local and session storage gives you:
Error: QUOTA_EXCEEDED_ERR: DOM Exception 22.
Would be nice with a try/catch at the top, so we also can use this in safari with private browsing. :-)
It seems that Opera Mini does not allow double quotes in cookies, that's why the polyfill is not working on it.
http://www.iandevlin.com/blog/2012/04/html5/cookies-json-localstorage-and-opera
I've made a simple fix with two replace
s, that turn double quotes into single quotes, and back.
So far it seems to work everywhere, including Opera Mini.
https://gist.github.com/ghinda/6036998
Thanks for this. One comment. You've added JSON.parse and JSON.stringify in the getter and setter. I would pull that for a more accurate representation of the API. The native setters and getters require strings, so it might produce unexpected behavior if the polyfill glosses over this. In the setter, I'm okay with leaving it, since the native would not throw an error anyway, it just does a .toString and stores '[object Object]', but in the getter, you'd have to change your logic between the native and the polyfill.
So:
function setData(data) {
data = typeof data !== 'string' ? JSON.stringify(data) : data;
if (type == 'session') {
window.name = data;
} else {
createCookie('localStorage', data, 365);
}
}
function getData() {
var data = type == 'session' ? window.name : readCookie('localStorage');
return data || {};
}
Thoughts?
setItem: function (key, value) {
data[key] = value+''; // forces the value to a string
this.length++;
setData(data);
}
Shouldn't you check if the key was already set before incrementing length? Like:
setItem: function (key, value) {
if (data[key] === undefined) this.length++;
data[key] = value+''; // forces the value to a string
setData(data);
}
In Privacy mode in Safari window.localStorage exists while it's not operable. so I think function to check if operable needed:
var isStorageOperable = function (storage) {
try {
storage.setItem("storage", "");
storage.getItem("storage");
storage.removeItem("storage");
return true;
}
catch (err) {
return false;
}
};
and than add check for operable additionally to check for typeof undefined.
@AirRider3 @vadzappa I put your fixes in https://gist.github.com/Contra/6368485 - I also applied that length fix to remove (it wasn't checking if the key existed before changing the length)
@AirRider3 @vadzappa @contra Even with that fix, I'm not sure that it works for private Safari. I think the override of
window.localStorage = new Storage('local');
doesn't do anything because window.localStorage is already implemented as far as private Safari is concerned, and allowing it to be overridden would be a security concern. Please correct me if I'm wrong - I'm still looking for a way to directly use window.localStorage/sessionStorage in private Safari without a data access layer. I tried running Contra's gist without success.
I think the only workaround for private browsing Safari requires you to overwrite the localStorage/sessionStorage prototypes, as overriding the object itself has no effect.
window.localStorage.__proto__ = new Storage('local');
Here's the complete gist with more complete localStorage support detection: https://gist.github.com/hasdavidc/8527456
Best Gist evar?
How to detect if the polyfill is really needed (like on iOS in Private mode where the object exists but it throws exceptions (QuotaExceededError: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota) in all methods)
from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
function() {
var mod = 'modernizr';
try {
localStorage.setItem(mod, mod);
localStorage.removeItem(mod);
return true;
} catch(e) {
return false;
}
}
what about wrapping the function definition in the catch to handle support detection as in https://gist.github.com/yehosef/57df0ec37d5096222cf0
You can add more tests for firefox etc. if desired. I'm dealing specifically with the ios private mode browser.
I've combined the ideas from @hasdavidc and @yehosef to account for the non existence of webstorage in older browsers and to accommodate for Safari's private browsing.
I've also tweaked the sessionStorage code so that it acts independent of the localStorage and is unique per window per tab to mimic the native behaviour.
See https://gist.github.com/jarrodirwin/0ce4c0888336b533b2c4
hello sir,,
i am quite new in web development..
i just wanted to make a website which can fetch users data and save it locally, using their cookies,..
so how can i do this..
language known by me- c,js,html5,css,jquery
Great job!
Hey Remy, I just came across this and wondered if you thought about implementing this w/o cookies... possibly with globalStorage and userData like this: https://github.com/wojodesign/local-storage-js