Strange behavior checking for Web Storage availability - javascript

Here's my code:
if(typeof(Storage)!==undefined) {
// Web storage support
if(localStorage.hashes != "") {
var hashes = jQuery.parseJSON(localStorage.hashes);
for (var i = 0; i < hashes.length; i++) {
// Do stuff here
}
}
else {
var hashes = [];
}
}
else {
// No web storage support
}
I don't really know what's going on, but when I try to load the page with this code from a device for the first time, the rest of my code doesn't work the way it should. However, if I comment it out then visit the page for the first time everything works. I can then uncomment it, reload the page, and everything will continue to work. This is really the best I can describe what's happening.

I believe you could have done a little more debugging before posting this.
Have you tried logging/ adding checks (to see where exactly this issue is coming from and what the error is) ?
But since we're here, here are my tips for localStorage :
Use modernizr (http://modernizr.com/)
if(Modernizr.localstorage){ /* Your code */}
Make some generic get and set functions
function get(key) {
if(Modernizr.localstorage) {
if(localStorage[key] != null) {
return localStorage[key];
}
}
return null;
}
function set(key, value) {
if(Modernizr.localstorage) {
localStorage[key] = value;
}
return null;
}
This is pretty rough, you tweek it to make it safer and respond to your needs
Put try/catch inside your get and set functions, you don't want write operations to impact your program

I figured it out! So in the above code I check if they had localStorage available, and if they do, I just assume that they have some hashes stored in there. This obviously creates a problem on their first visit as they wouldn't have any hashes saved yet. So I have to check if they have any, if they do, then do the for loop, otherwise just set it to an empty array. Like so!
if(typeof(Storage) !== "undefined") {
// Web storage support
if(localStorage.hashes != "") {
hashes = JSON.parse(localStorage.getItem("hashes"));
if(hashes) {
for (var i = 0; i < hashes.length; i++) {
// Do Stuff
}
} else {
hashes = [];
}
}
else {
hashes = [];
}
}
else {
// No web storage support
hashes = [];
}
Thanks to jbabey for tips on cleaning up my code some!

Related

How to create a bookmarklet with multiple functions

I'm new to creating JavaScript bookmarklets but have got a certain way in solving my problems but have got stuck on one final bit.
Basically, I want to create a bookmarklet that will replace text in 2 places in the URL - the subdomain and the URI.
I have managed to do this for the first part:
(function() {
window.location = window.location
.toString()
.replace(/^https:\/\/www\./, "https://edit.");
})();
Next, I need to grab some metadata (cab-id) from the page. I've managed to do this an print it to the console:
function getCabID() {
var metas = document.getElementsByTagName("meta");
for (var i = 0; i < metas.length; i++) {
if (metas[i].getAttribute("name") == "cab-id") {
return metas[i].getAttribute("content");
}
}
return "";
}
console.log(getCabID());
The next thing I need to do is replace the end of the url (everything from "www.xxxxxx.org.uk/*" with the following:
/EPiServer/CMS/Home#context=epi.cms.contentdata:///
I can't figure out how to do this, I'm really struggling. I've come up with the following but it's not working:
(function() {
var url=window.location.href;
stringUrl=String(url);
stringUrl=stringUrl.replace(/^https:\/\/www.xxxxxx.org.uk\/, "https://edit.xxxxxx.org.uk/EPiServer/CMS/Home#context=epi.cms.contentdata:///");
document.location=stringUrl;
})();
I'll also need to pop the cab-id at the end of all this directly after ///.
Sorry for the long question but what I need to do is:
Make the 3rd one actually work!
Combine the 3 functions
Any tips would be massively appreciated :D
As I understood your question, the following bookmarklet probably allows to combine the 2nd and 3rd steps:
javascript:(function() {
window.location.href = "https://edit.xxxxxx.org.uk/EPiServer/CMS/Home#context=epi.cms.contentdata:///" + getCabID();
function getCabID() {
var metas = document.getElementsByTagName("meta");
for (var i = 0; i < metas.length; i++) {
if (metas[i].getAttribute("name") == "cab-id") {
return metas[i].getAttribute("content");
}
}
return "";
}
})();

Retrieving double redirects through MediaWiki API

I want to write a script to resolve double redirects automatically after a page move. Here is what I have started with:
(function () {
var api = new mw.Api();
api.get( {
action: 'query',
list: 'backlinks',
blpageid: mw.config.get('wgArticleId'),
blfilterredir: 'redirects',
blredirect: true,
bllimit: 500
} ).done( function (data) {
var fixed = 0;
redirects = data.query.backlinks;
for (var i=0; i<redirects.length; i++) {
var doubles = redirects[i].redirlinks;
if (doubles === undefined) {
continue;
}
for (var j=0; j<doubles.length; j++) {
console.log(doubles[j]);
fixed ++;
}
}
mw.notify(fixed);
} );
})();
The problem is that if I run this function on a page like Wikipedia:Blocking policy the script returns some pages that are not actually double-redirects, but merely redirects containing links to it.
I can check each of them to see where they are actually pointing to, but isn't there any better way? i.e. a simple method to retrieve double redirects only.
If you are happy with using python and the pywikibot framework there is https://m.mediawiki.org/wiki/Manual:Pywikibot/redirect.py which should do what you want.
Otherwise you might want to work with the output of Special:DoubleRedirects.

Check if localStorage is available

I know there has been many questions about checking for localStorage but what if someone manually shuts it off in their browser? Here's the code I'm using to check:
localStorage.setItem('mod', 'mod');
if (localStorage.getItem('mod') != null){
alert ('yes');
localStorage.removeItem('mod');
} else {
alert ('no');
}
Simple function and it works. But if I go into my Chrome settings and choose the option "Don't Save Data" (I don't remember exactly what it's called), when I try to run this function I get nothing but Uncaught Error: SecurityError: DOM Exception 18. So is there a way to check if the person has it turned off completely?
UPDATE: This is the second function I tried and I still get no response (alert).
try {
localStorage.setItem('name', 'Hello World!');
} catch (e) {
if (e == QUOTA_EXCEEDED_ERR) {
alert('Quota exceeded!');
}
}
Use modernizr's approach:
function isLocalStorageAvailable(){
var test = 'test';
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch(e) {
return false;
}
}
if(isLocalStorageAvailable()){
// available
}else{
// unavailable
}
It's not as concise as other methods but that's because it's designed to maximise compatibility.
The original source: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
Working example: http://jsfiddle.net/6sm54/2/
I'd check that localStorage is defined prior to any action that depends on it:
if (typeof localStorage !== 'undefined') {
var x = localStorage.getItem('mod');
} else {
// localStorage not defined
}
UPDATE:
If you need to validate that the feature is there and that it is also not turned off, you have to use a safer approach. To be perfectly safe:
if (typeof localStorage !== 'undefined') {
try {
localStorage.setItem('feature_test', 'yes');
if (localStorage.getItem('feature_test') === 'yes') {
localStorage.removeItem('feature_test');
// localStorage is enabled
} else {
// localStorage is disabled
}
} catch(e) {
// localStorage is disabled
}
} else {
// localStorage is not available
}
Feature-detecting local storage is tricky. You need to actually reach into it. The reason for this is that Safari has chosen to offer a functional localStorage object when in private mode, but with it's quotum set to zero. This means that although all simple feature detects will pass, any calls to localStorage.setItem will throw an exception.
Mozilla's Developer Network entry on the Web Storage API's has a dedicated section on feature detecting local storage. Here is the method recommended on that page:
function storageAvailable(type) {
try {
var storage = window[type],
x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch(e) {
return false;
}
}
And here is how you would use it:
if (storageAvailable('localStorage')) {
// Yippee! We can use localStorage awesomeness
}
else {
// Too bad, no localStorage for us
}
If you are using NPM, you can grab storage-available using
npm install -S storage-available
then use the function like so:
if (require('storage-available')('localStorage')) {
// Yippee! We can use localStorage awesomeness
}
Disclaimer: Both the documentation section on MDN and the NPM package were authored by me.
MDN updated the storage detect function. In 2018, it's more reliable:
function storageAvailable() {
try {
var storage = window['localStorage'],
x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch(e) {
return e instanceof DOMException && (
// everything except Firefox
e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === 'QuotaExceededError' ||
// Firefox
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
// acknowledge QuotaExceededError only if there's something already stored
storage && storage.length !== 0;
}
}
Browsers that support localStorage will have a property on the window object named localStorage. However, for various reasons, just asserting that property exists may throw exceptions. If it does exist, that is still no guarantee that localStorage is actually available, as various browsers offer settings that disable localStorage. So a browser may support localStorage, but not make it available to the scripts on the page. One example of that is Safari, which in Private Browsing mode gives us an empty localStorage object with a quota of zero, effectively making it unusable. However, we might still get a legitimate QuotaExceededError, which only means that we've used up all available storage space, but storage is actually available. Our feature detect should take these scenarios into account.
See here for a brief history of feature-detecting localStorage.
With this function you can check if localstorage is available or not, and you keep under control the possible exceptions.
function isLocalStorageAvailable() {
try {
var valueToStore = 'test';
var mykey = 'key';
localStorage.setItem(mykey, valueToStore);
var recoveredValue = localStorage.getItem(mykey);
localStorage.removeItem(mykey);
return recoveredValue === valueToStore;
} catch(e) {
return false;
}
}
It is better to check availability of localStorage in conjunction with cookies, because if cookie is enabled the browser could detect that localStorage is available and type it as object, but provide no possibility to work with it. You use the next function to detect both localStorage and cookies:
const isLocalStorage = () => {
try {
if (typeof localStorage === 'object' && navigator.cookieEnabled) return true
else return false
} catch (e) {
return false
}
}
You can try this method
Anytime validate the content of the localstore
const name = localStorage.getItem('name');
if(name){
console.log('Exists');
}else
{
console.log('Not found');
}
I tried this solution in Chrome, Firefox and Edge and it worked correctly.
if (localStorage.getItem('itemName') === null )
{
// your code here ...
}
if the local variable on localStorage do not exist it will brind false.
You can create a checker function which tries to get a non existing item from the localStorage. When the localStorage is turned on you will normally get null returned. But when the localStorage is turned off an error will be thrown. You don't have to set any item for the checking.
const checkLocalStorage = () => {
try {
localStorage.getItem("x");
return true;
} catch {
return false;
}
}
Modifying Joe's answer to add a getter makes it easier to use. With the below you simply say: if(ls)...
Object.defineProperty(this, "ls", {
get: function () {
var test = 'test';
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch(e) {
return false;
}
}
});
Here is an easy check:
if(typeof localStorage === 'undefined'){
Use this to check localStorage is set or not. Its help you to get status of Localstorage.
if( window.localStorage.fullName !== undefined){
//action
}else{
}

what is wrong with this JS cookie Script?

I have a code that has no syntax errors (Dreamweaver) but the Chrome JS console is saying that ExistsCookie is undefined. The cookie was in the cookie list for that site but the page is not redirecting. What am I doing wrong? NOTE: I know people can turn cookies off.
var cname = "voicevote"
var data ="1";
function CheckForCookie()
{
if( ExistsCookie(cname) )
{
window.location.replace("cookie.htm")
}
}
Most likely, ExistsCookie is a function that you haven't included in your script- if this was taken from a tutorial on some other website, look there- there may be a function on that page which you forgot to include in your code.
EDIT: After some googling, it looks like this is what you need:
function ExistsCookie(name)
{
var aCookie = document.cookie.split("; ");
for (var i=0; i < aCookie.length; i++)
{
var aCrumb = aCookie[i].split("=");
if (name == aCrumb[0])
return true;
}
return false;
}
(Source, which appears to match original question)

IE Caching issue is breaking my lookup field

I'm doing a project which uses javascript to get info from a view (written in Python and using the Django interface) based on the text a user enters in a field (querying on every keyup), and then display that info back. Basically, this either displays 'no job found' or displays the name, username, and balance for that job. In Firefox, this all works great. I can enter a JobID, it tells me the ID is new, and I can create the job. I can then immediately come back to the page and enter that ID, and my lookup returns the right info about the job.
The thing is, Internet Explorer 8 is being lazy. If I type a job ID in IE8, my functions calls the lookup page (/deposits/orglookup/?q=123) and gets a value. So if, for example, it gets False, I can then create a new job with that ID. If I then browse back and enter that same number in that same lookup field, Internet Explorer does not refresh the lookup page, so it returns false again. If I browse to that lookup page, I see that false value, but if I refresh it, I get the right information again. Any idea on how I can force this query every time I type in my lookup field, and not like IE refer to the cached page?
I will add that it does not do me much good to fix this on a per-user basis, as this is an organization-wide application, so I really could use a fix I can write into my code somewhere to force IE to actually refresh the lookup page every time it is supposed to.
Here's the code for the lookup function, if it helps. It is a bit messy, but I didn't write it so I'll try to include everything relevant:
$("#id_JobID").keyup(
function(event){
//only fire gets on 0-9, kp 0-9, backspace, and delete
if (event.keyCode in { 96:1, 97:1, 98:1, 99:1, 100:1, 101:1, 102:1, 103:1, 104:1, 105:1,
46:1,48:1, 49:1, 50:1, 51:1, 52:1, 53:1, 54:1, 55:1, 56:1, 57:1, 8:1})
{
if ($("#loadimg").attr("src") != "/static/icons/loading.gif") {
$("#loadimg").attr("src", "/static/icons/loading.gif");
}
if ($("#loadimg").length < 1) {
$("#id_JobID").parent().append("<img id=loadimg src=/static/icons/loading.gif>");
}
clearTimeouts(null); //clear all existing timeouts to stop any running lookups
GetCounter++;
currLoc = window.location.href.slice(window.location.href.indexOf('?') + 1).split('/').slice(-2,-1);
if (currLoc == 'restorebatch') {
var TimeoutId = setTimeout(function() {dynamicSearch('restorelookup');}, 400);
} else {
var TimeoutId = setTimeout(function() {dynamicSearch('orglookup');}, 400);
}
//alert(TimeoutID);
TimeoutBag[GetCounter] = {
'RequestNumber': GetCounter,
'TimeoutId': TimeoutId
}
}
}
);
function clearTimeouts(TimeoutBagKeys) //TimeoutBagKeys is an array that contains keys into the TimeoutBag of Timeout's you want to clear
{
if(TimeoutBagKeys == null) //if TimeoutBagKeys is null, clear all timeouts.
{
for (var i = 0; i < TimeoutBag.length; i++)
{
if (TimeoutBag[i] != null) {
clearTimeout(TimeoutBag[i].TimeoutId);
}
}
}
else //otherwise, an array of keys for the timeout bag has been passed in. clear those timeouts.
{
var ClearedIdsString = "";
for (var i = 0; i < TimeoutBagKeys.length; i++)
{
if (TimeoutBag[TimeoutBagKeys[i]] != null)
{
clearTimeout(TimeoutBag[TimeoutBagKeys[i]].TimeoutId);
ClearedIdsString += TimeoutBag[TimeoutBagKeys[i]].TimeoutId;
}
}
}
}
function dynamicSearch(viewname) {
$(".lookup_info").slideUp();
if ($("#id_JobID").val().length >= 3) {
var orgLookupUrl = "/deposits/" + viewname + "/?q=" + $("#id_JobID").val();
getBatchInfo(orgLookupUrl);
}
else if ($("#id_JobID").val().length == 0) {
$("#loadimg").attr("src", "/static/icons/blank.gif");
$(".lookup_info").slideUp();
}
else {
$("#loadimg").attr("src", "/static/icons/loading.gif");
$(".lookup_info").slideUp();
}
}
function getBatchInfo(orgLookupUrl) {
$.get(orgLookupUrl, function(data){
if (data == "False") {
$("#loadimg").attr("src", "/static/icons/red_x.png");
$(".lookup_info").html("No batch found - creating new batch.");
$("#lookup_submit").val("Create");
$(".lookup_info").slideDown();
toggleDepInputs("on");
}
else {
$("#loadimg").attr("src", "/static/icons/green_check.png");
$("#lookup_submit").val("Submit");
$(".lookup_info").html(data);
$(".lookup_info").slideDown()
toggleDepInputs("off");
};
});
}
There are three solutions to this:
Use $.post instead of $.get.
Add a random GET parameter to your URL, e.g. ?update=10202203930489 (of course, it needs to be different on every request).
Prohibit caching on server-side by sending the right headers (if-modified-since).
You need to make the URL unique for every request. The failproof way is to introduce new GET parameter which has a timestamp as its value - so the URL is unique with every request, since timestamp is always changing, so IE can't cache it.
url = "/deposits/orglookup/?q=123&t=" + new Date().getTime()
So instead of only one parameter (q) you now have two (q and t) but since servers usually don't care bout extra parameters then it's all right
One trick that often works is to append a timestamp to the lookup URL as a querystring parameter, thus generating a unique URL each time the request is made.
var orgLookupUrl = "/deposits/" +
viewname + "/?q=" +
$("#id_JobID").val() + "&time=" + new Date().getTime();;

Categories