How to invalidate localStorage after breaking change - javascript

I have this little piece of code which is allowing me to check if user has localStorage set before I've made breaking changes in it's format.
var sw =
{
storage: {
// change this date when you create breaking changes in local storage
// any local storage set before this time will be invalidated & set again
lastBreakingUpdateTime: new Date(2017, 4, 24),
local: {
set: function(key, value) {
try {
window.localStorage.setItem(key.toString(), value.toString());
return true;
}
catch(e) {
return false;
}
},
get: function(key) {
var value = window.localStorage.getItem(key.toString());
if (value === 'true')
return true;
if (value === 'false')
return false;
// isNan returns false for empty string
// empty string is considered 0 by isNaN, but NaN by parseInt :)
if (isNaN(value) || value === '')
return value;
// return value converted to number
return +value;
},
markAsSetNow: function() {
sw.storage.local.set('timeWhenSet', new Date());
},
isOutdatedOrNotSet: function() {
var lastSetTime = sw.storage.local.get('timeWhenSet');
if (!lastSetTime || Date.parse(lastSetTime) <= sw.storage.lastBreakingUpdateTime)
return true;
return false;
}
}
}
}
Issue with this code is that in javascript Date.Parse is unreliable accross browsers - each browser has different implementation. I need to modify this code so that it works reliably in every major browser.

Use time in ms to compare the dates in order to check the cache:
const ms = new Date().getTime();
It is cross-browser

In the end I have decided to use a format version number, like ayinloya has suggested in comments. Here is my complete local storage handling code, if someone wants to use in their apps.
var appName =
{
storage: {
// increment this when you create breaking changes in local storage format
// versions must start from 1, version 0 is invalid
formatVersion: 1,
// type can be 'localStorage' or 'sessionStorage'
available: function(type) {
try {
var storage = window[type],
x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch(e) {
return false;
}
},
local: {
// Use this function over window.localStorage.setItem() because
// localStorage.setItem() or sessionStorage.setItem() may throw
// an exception if the storage is full.
// in Mobile Safari (since iOS 5) it always throws when the user
// enters private mode (Safari sets quota to 0 bytes in private mode,
// contrary to other browsers, which allow storage in private mode,
// using separate data containers).
set: function(key, value) {
try {
window.localStorage.setItem(key.toString(), value.toString());
return true;
}
catch(e) {
return false;
}
},
get: function(key) {
var value = window.localStorage.getItem(key.toString());
if (value === 'true')
return true;
if (value === 'false')
return false;
// isNan returns false for empty string
// empty string is considered 0 by isNaN, but NaN by parseInt :)
if (isNaN(value) || value === '')
return value;
// return value converted to number
return +value;
},
setFormatVersion: function() {
appName.storage.local.set('formatVersion', appName.storage.formatVersion);
},
isOutdatedOrNotSet: function() {
var version = appName.storage.local.get('formatVersion');
if (!version || version < appName.storage.formatVersion)
return true;
return false;
}
}
}
}

Related

React object property assignment only works the first time

For the following code, parameters are js objects whose structures are initialized as follows:
statePiece = {
field_name: { disabled: false, exampleValue: "arbitrary" },
field_name2: {
/* ... */
},
field_nameN: {
/* ... */
}
};
userField = "field_name_string";
sesarValues = {
format: "one2one",
selectedField: "latitude",
disabledSelf: true,
addField: 0
};
This function works correctly and returns the modified statePiece as returnTemp the first time a particular statePiece.field_name is modified
export let setUserField = (statePiece, userField, sesarValues) => {
console.log("set user field", userField, "set mappval", sesarValues);
var temp = { ...statePiece }; //(this.state.fields[each].mappedTo != null) ? (this.state.fields[userField].mappedTo) : [];
var XUnit = statePiece[userField];
if (typeof userField != "string") {
console.log("not string");
for (var each of userField) {
if (sesarValues) {
temp[each].mappedTo = sesarValues.selectedField;
temp[each].disabled = true;
} else {
temp[each].disabled = !temp[each].disabled;
delete temp[each].mappedTo;
}
}
} else {
//is string
console.log("is string");
console.log(XUnit);
if (sesarValues) {
if (XUnit.disabled === true) XUnit.disabled = false;
console.log("1");
console.log(XUnit);
XUnit.disabled = true;
console.log(XUnit);
XUnit.mappedTo = sesarValues.selectedField;
} else {
console.log("2");
temp[userField].disabled = !temp[userField].disabled;
delete temp[userField].mappedTo;
}
}
let returnTemp = { ...temp, [userField]: XUnit };
console.log("set UF debug ", returnTemp);
console.log(returnTemp["FACILITY_CODE"]);
return returnTemp;
};
But after that, changing the statePiece.userField.mappedTo value fails to alter the object property. Or at least alter it permanently. When I console log the returnTemp variable, I see the entry has lost its mappedTo entry(as should happen) without it being replaced with the new userField.
However, when I console.log(returnTemp[userField]) it shows the entry values with the expected mappedTo key: value pair.
Not sure what's going on here.
From the usage of userField, I can work out that it could be an Array or a String.
However you have done something curious with it in the following expression:
var XUnit = statePiece[userField];
Given userField is a String, the above expression is fine.
However, where it is an array, XUnit will be undefined.
Also doing the same where userField is an Array in the following line means that you're setting the userField.toString() as a key mapped to undefined.
let returnTemp = { ...temp, [userField]: XUnit };
I'd assign XUnit where the condition checks out that userField is a String and just return temp.
else {
//is string
var XUnit = statePiece[userField];
//...
}
return temp;

Javascript boolean logic error

My code:
var old = localStorage.getItem('GotoBeginning');
console.log("old is "+ old);
if (old===true) {
console.log("Returning true");
$("#gobeg").prop('checked', true);
return true;
} else {
console.log("Returning false");
$("#gobeg").prop('checked', false);
return false;
}
The value in localStorage for GotoBeginning is true.
My console.log shows:
old is true
Returning false
I expected the following output:
old is true
Returning true
The storage api in the browser only stores strings. This should work:
if (old === 'true') {
// ^ ^
} else {
}
As mentioned in a comment by IrkenInvader, you can extract the proper type like so:
var old = JSON.parse(localStorage.getItem('GotoBeginning'))
// now old will be a proper boolean so this will work
if (old) {
} else {
}

Refreshing a webpage is actually redirecting to bing

I have a script, in which I am adding custom URL to the browser back button
for this I used this particular script
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery.cookie.js"></script>
<script type="text/javascript" src="js/native.history.js"></script>
<script type="text/javascript" src="js/go.new.js"></script>
<script>
$(window).load(function()
{
setTimeout(backtrap, 100);
if (!($.cookie("clkd"))){
$.cookie("clkd", 1, { expires : 14 , path: '/'});
}
setInterval(toggle_image, 1000);
});
window.onpageshow = function (e) {
if (e.persisted) {
location.reload();
}
};
window.onpopstate = function(event) {
if (document.location.toString().indexOf("redir=1")>0){
window.location.href = "<?=$$last_offer?>";
//window.location.href = "<?=$szLinkURL?>";
}
};
function toggle_image()
{
var height = $('#banner-img').height();
$('#banner-img').toggle();
$('#loader-img').toggle();
$('#loader-img').css({'height':height+'px'});
}
function show_star(rate,id)
{
$('#al-stars-'+id).html('<span class="stars" id="stars-'+id+'">'+parseFloat(rate)+'</span>');
$('#stars-'+id).stars();
}
$.fn.stars = function()
{
return $(this).each(function() {
$(this).html($('<span />').width(Math.max(0, (Math.min(5, parseFloat($(this).html())))) * 16));
});
}
</script>
Now the script seems to work fine, but I face a new proble, that is:
when I am clicking the refresh button on the browser, the page is rediecting to bing.com
but I dont know why this is happening...
in case if needed here's the script in the go.new.js
/**
* go.js
*
* Functions for go.php and the backtrap
*/
// --------------------------------------------------------------------------------------------------------------------
// Code for parsing query string gratefully lifted from http://unixpapa.com/js/querystring.html, with an
// addition for modifying/generating new query strings.
// Query String Parser
//
// qs= new QueryString()
// qs= new QueryString(string)
//
// Create a query string object based on the given query string. If
// no string is given, we use the one from the current page by default.
//
// qs.value(key)
//
// Return a value for the named key. If the key was not defined,
// it will return undefined. If the key was multiply defined it will
// return the last value set. If it was defined without a value, it
// will return an empty string.
//
// qs.values(key)
//
// Return an array of values for the named key. If the key was not
// defined, an empty array will be returned. If the key was multiply
// defined, the values will be given in the order they appeared on
// in the query string.
//
// qs.keys()
//
// Return an array of unique keys in the query string. The order will
// not necessarily be the same as in the original query, and repeated
// keys will only be listed once.
//
// QueryString.decode(string)
//
// This static method is an error tolerant version of the builtin
// function decodeURIComponent(), modified to also change pluses into
// spaces, so that it is suitable for query string decoding. You
// shouldn't usually need to call this yourself as the value(),
// values(), and keys() methods already decode everything they return.
//
// Note: W3C recommends that ; be accepted as an alternative to & for
// separating query string fields. To support that, simply insert a semicolon
// immediately after each ampersand in the regular expression in the first
// function below.
function QueryString(qs)
{
this.dict= {};
// If no query string was passed in use the one from the current page
if (!qs) qs= location.search;
// Delete leading question mark, if there is one
if (qs.charAt(0) == '?') qs= qs.substring(1);
// Parse it
var re= /([^=&]+)(=([^&]*))?/g;
while (match= re.exec(qs))
{
var key= decodeURIComponent(match[1].replace(/\+/g,' '));
var value= match[3] ? QueryString.decode(match[3]) : '';
if (this.dict[key])
this.dict[key].push(value);
else
this.dict[key]= [value];
}
}
QueryString.decode= function(s)
{
s= s.replace(/\+/g,' ');
s= s.replace(/%([EF][0-9A-F])%([89AB][0-9A-F])%([89AB][0-9A-F])/g,
function(code,hex1,hex2,hex3)
{
var n1= parseInt(hex1,16)-0xE0;
var n2= parseInt(hex2,16)-0x80;
if (n1 == 0 && n2 < 32) return code;
var n3= parseInt(hex3,16)-0x80;
var n= (n1<<12) + (n2<<6) + n3;
if (n > 0xFFFF) return code;
return String.fromCharCode(n);
});
s= s.replace(/%([CD][0-9A-F])%([89AB][0-9A-F])/g,
function(code,hex1,hex2)
{
var n1= parseInt(hex1,16)-0xC0;
if (n1 < 2) return code;
var n2= parseInt(hex2,16)-0x80;
return String.fromCharCode((n1<<6)+n2);
});
s= s.replace(/%([0-7][0-9A-F])/g,
function(code,hex)
{
return String.fromCharCode(parseInt(hex,16));
});
return s;
};
QueryString.prototype.value= function (key)
{
var a= this.dict[key];
return a ? a[a.length-1] : undefined;
};
QueryString.prototype.values= function (key)
{
var a= this.dict[key];
return a ? a : [];
};
QueryString.prototype.keys= function ()
{
var a= [];
for (var key in this.dict)
a.push(key);
return a;
};
QueryString.prototype.set = function(key, value)
{
this.dict[key] = [value];
};
QueryString.prototype.add = function(key, value)
{
if (typeof this.dict[key] == 'undefined') {
this.dict[key] = [value];
} else {
this.dict[key].push(value);
}
};
QueryString.prototype.toString = function()
{
var pieces = [];
for (var key in this.dict) {
for (var idx in this.dict[key]) {
pieces.push( encodeURIComponent(key) + '=' + encodeURIComponent(this.dict[key][idx]));
}
}
return pieces.join('&');
};
// --------------------------------------------------------------------------------------------------------------------
// Load ourselves into the user's history, with an altered query string. The 'bt' var tells us what step of the
// back trap we were LAST on. After that, we redirect ourselves to the next URL.
function backtrap() {
var qs = new QueryString();
qs.set('redir', 1);
var own_url = window.location.href;
var qs_at = own_url.indexOf('?');
var doped_url;
if (qs_at == -1) {
doped_url = own_url + '?' + qs.toString();
} else {
doped_url = own_url.substring(0, qs_at) + '?' + qs.toString();
}
History.pushState({}, '', doped_url);
History.pushState({} ,'', own_url);
alert(doped_url);
alert(own_url);
}
for better understanding
check this link and detect
http://gomotrak.com/main/click.php?c=2173&key=jm64njqpq608t19bgqi3fwfq&c2=AA09_00000&c3=[zone]
[NOTE]
The backbutton is working fine, there is no issue with the back-button...
Problem is, when i am clicking on the refresh button of the browser in the address bar, its reloading the bing page.
Also $$last_offer is absolutely fine cause its working this way
if(stripos($ua,'android') !== false)
{
$agent = 'a';
setlocale(LC_ALL, 'pt_BR.UTF-8');
$last_CSVfp = fopen("offer_android.csv", "r");
$last_offer_data = array();
if($last_CSVfp !== FALSE)
{
while(! feof($last_CSVfp))
{
$last_offer_data[] = fgetcsv($last_CSVfp,"",",");
}
}
fclose($last_CSVfp);
$last_arr_size = count($last_offer_data);
$last_offer = "offer".intval(intval($last_arr_size)+1);
}
so $last_offer becomes "offer3" for example
and $$last_offer becomes "$offer3" its like alias of a phpvariabledenoted by anotherphp variable`

Url encoding for query params in Ember.js

I am using Ember.js, version 1.7.0-beta.1, in my latest project. I use the query params feature to make a list survive a hard refresh (e.g. after a reload, the selected items in the list are still selected).
I got a controller whom manages that:
export default Ember.ObjectController.extend({
queryParams: [{selectedFiles: 'files'}],
selectedFiles: Ember.A([]), //list of file ids
... //other props
actions: {
selectFile: function(file) {
//set or remove the file id to the selectedFiles property
}
});
It works awesome, but with one condition: the url is url-encoded:
Chrome & IE:
path/354?files=%5B"6513"%2C"6455"%2C"6509"%2C"6507"%2C"6505"%2C"6504"%2C"6511"%5D
FF (automaticly sets the brackets):
path/354?files="6513"%2C"6455"%2C"6509"%2C"6507"%2C"6505"%2C"6504"%2C"6511"]
Is there a way in Ember to decode the query-param-string to a more readible format? Maybe I could use the decodeURIComponent() function somewhere?
The desired output:
path/354?files=["6513","6455","6509","6507","6505","6504","6511"]
I had a very similar problem, and made it work by overriding serializeQueryParam and deserializeQueryParam in the route.
In the controller you would have:
queryParams: ['files'],
files: []
And in the route:
serializeQueryParam: function(value, urlKey, defaultValueType) {
if (defaultValueType === 'array') {
return value;
// Original: return JSON.stringify(value);
}
return '' + value;
},
and:
deserializeQueryParam: function(value, urlKey, defaultValueType) {
if (defaultValueType === 'array') {
var arr = [];
for (var i = 0; i < value.length; i++) {
arr.push(parseInt(value[i], 10));
}
return arr;
// Original: return Ember.A(JSON.parse(value));
}
if (defaultValueType === 'boolean') {
return (value === 'true') ? true : false;
} else if (defaultValueType === 'number') {
return (Number(value)).valueOf();
}
return value;
},
The url will then become something like:
?files[]=1&files[]=2&files[]=3
Which will then be a real array on the server side.
Take a look at this working example on jsbin.com

determine whether Web Storage is supported or not

I need to verify that Web Storage API is supported and available (it may be disabled due to security issues).
So, I thought it would suffice to check whether the type sessionStorage or localStorage is defined or not:
if (typeof sessionStorage != 'undefined')
{
alert('sessionStorage available');
}
else
{
alert('sessionStorage not available');
}
However, I was wondering if it could be possible that the type exists, but I wouldn't been able to use the Web Storage API anyway.
Remarks:
I know Firefox will throw a security error if cookies are disabled and sessionStorage or localStorage are accessed.
Why don't you use the Modernizr library to detect if local storage is supported or not? Any differences between browers will be taken care of for you, you can then just use code like this:
if (Modernizr.localstorage) {
// browser supports local storage
} else {
// browser doesn't support local storage
}
I think you're on the right track with your original code, no need to make this too fancy.
Using the KISS principle with no additional dependencies in your code:
var storageEnabled = function() {
try {
sessionStorage.setItem('test-key','test-value');
if (sessionStorage.getItem('test-key') == 'test-value'){
return true;
}
} catch (e) {};
return false;
};
alert(storageEnabled() ? 'sessionStorage available' : 'sessionStorage not available');
try{
ssSupport = Object.prototype.toString.call( sessionStorage ) === "[object Storage]";
}
catch(e){
ssSupport = false;
}
So, because Modernizr.localstorage respectively Modernizr.sessionstorage will return true while Firefox might be used with disabled Cookies (which will lead into an security error) or any other proprietary (unexpected) behavior could occur: I've written my own webStorageEnabled function which seems to work very well.
function cookiesEnabled()
{
// generate a cookie to probe cookie access
document.cookie = '__cookieprobe=0;path=/';
return document.cookie.indexOf('__cookieprobe') != -1;
}
function webStorageEnabled()
{
if (typeof webStorageEnabled.value == 'undefined')
{
try
{
localStorage.setItem('__webstorageprobe', '');
localStorage.removeItem('__webstorageprobe');
webStorageEnabled.value = true;
}
catch (e) {
webStorageEnabled.value = false;
}
}
return webStorageEnabled.value;
}
// conditional
var storage = new function()
{
if (webStorageEnabled())
{
return {
local: localStorage,
session: sessionStorage
};
}
else
{
return {
local: cookiesEnabled() ? function()
{
// use cookies here
}() : null,
session: function()
{
var data = {};
return {
clear: function () {
data = {};
},
getItem: function(key) {
return data[key] || null;
},
key: function(i)
{
var index = 0;
for (var value in data)
{
if (index == i)
return value;
++index;
}
},
removeItem: function(key) {
delete data[key];
},
setItem: function(key, value) {
data[key] = value + '';
}
};
}()
};
}
}
Hope this will be useful for someone too.
My version (because IE 9 running in IE 8 more on an intranet site is broken).
if (typeof (Storage) != "undefined" && !!sessionStorage.getItem) {
}
a longer version that adds setObject to allow storing objects:
var sstorage;
if (typeof (Storage) != "undefined" && !!sessionStorage.getItem) {
Storage.prototype.setObject = function (key, value) {
this.setItem(key, JSON.stringify(value));
};
Storage.prototype.getObject = function (key) {
return JSON.parse(this.getItem(key));
};
if (typeof sessionStorage.setObject == "function") {
sstorage = sessionStorage;
}
else {
setupOldBrowser();
}
}
else {
setupOldBrowser();
}
function setupOldBrowser() {
sstorage = {};
sstorage.setObject = function (key, value) {
this[key] = JSON.stringify(value);
};
sstorage.getObject = function (key) {
if (typeof this[key] == 'string') {
return JSON.parse(this[key]);
}
else {
return null;
}
};
sstorage.removeItem = function (key) {
delete this[key];
};
}
Here's what I do to use session storage if available if it's not, use cookies..
var setCookie;
var getCookie;
var sessionStorageSupported = 'sessionStorage' in window
&& window['sessionStorage'] !== null;
if (sessionStorageSupported) {
setCookie = function (cookieName, value) {
window.sessionStorage.setItem(cookieName, value);
return value; //you can introduce try-catch here if required
};
getCookie = function (cookieName) {
return window.sessionStorage.getItem(cookieName);
};
}
else {
setCookie = function (cookieName, value) {
$.cookie(cookieName, value);
return value; // null if key not present
};
getCookie = function(cookieName) {
console.log("using cookies");
return $.cookie(cookieName);
};
}

Categories

Resources