We are calling jstorage from jquery as in:
$.jstorage.set("foo","bar object");
Later on we look for this key "foo" is gone! Why would this be? We set breakpoints on the jstorage deleteKey method and it is not being called.
Specifically:
$.jstorage.index()
does not contain "foo".
Good lord I found the answer. Let's look at the _save method in jstorage.js, used to persist that set call:
function _save(){
_dropOldEvents(); // remove expired events
try{
_storage_service.jStorage = JSON.stringify(_storage);
// If userData is used as the storage engine, additional
if(_storage_elm) {
_storage_elm.setAttribute("jStorage",_storage_service.jStorage);
_storage_elm.save("jStorage");
}
_storage_size = _storage_service.jStorage?String(_storage_service.jStorage).length:0;
}catch(E7){/* probably cache is full, nothing is saved this way*/}
}
So, let's see, in the jstorage library, where is persists its data, if the local storage is full, and it cannot write, then it simply takes that exception and swallows it. Ugh!
Could I please have my exceptions? In our local copy we at least output this to the console log. But now I know, local storage with its cap of 5 MB on FireFox and 2.5 MB on Chrome, really was too good to be true. I'm a sad panda.
Related
Update:
Please see the answer noted below as, ultimately, the problem had nothing to do with jsquery.
=============
Issue:
I submit an object to jquery to convert into a serialized string that will become part of a "POST" request to a server, and the data returned from the serialization request is different than the data sent on many occasions.
An example:
The JavaScript code that implements the server POST request:
function send_data(gpg_data) {
var query_string;
query_string = '?' + $.param(gpg_data, traditional = true);
console.log('gpg_data =', gpg_data)
console.log('query_string =', query_string);
$.post(server_address + query_string);
return;
}
This is the structure sent to the jquery param() function.
(copied from the browser console in developer mode.)
gpg_data =
{controller_status: 'Connected', motion_state: 'Stopped', angle_dir: 'Stopped', time_stamp: 21442, x_axis: 0, …}
angle_dir: "Stopped"
controller_status: "Connected"
force: 0
head_enable: 0
head_x_axis: 0
head_y_axis: 0
motion_state: "Stopped"
time_stamp: 21490
trigger_1: 0
trigger_2: 0
x_axis: 0
y_axis: "0.00"
. . . and the returned "query string" was:
query_string = ?controller_status=Connected&motion_state=Stopped&angle_dir=Stopped&time_stamp=21282&x_axis=0&y_axis=0.00&head_x_axis=0&head_y_axis=0&force=0&trigger_1=1&trigger_2=1&head_enable=0
The data received by the server is:
ImmutableMultiDict([('controller_status', 'Connected'), ('motion_state', 'Stopped'), ('angle_dir', 'Stopped'), ('time_stamp', '21282'), ('x_axis', '0'), ('y_axis', '0.00'), ('head_x_axis', '0'), ('head_y_axis', '0'), ('force', '0'), ('trigger_1', '1'), ('trigger_2', '1'), ('head_enable', '0')])
For example, note that "trigger_1" returns 1 when the data sent to it is a zero.
I have tried setting the query to "traditional = true" to revert to an earlier style of query handling as some articles suggested - which did not work. I tried this with jquery 3.2 and 3.6.
I am not sure exactly how jquery manages to munge the data so I have no idea where to look.
I have looked at my script and at the unpacked jquery code, and I can make no sense out of why or how it does what it does.
Any help understanding this would be appreciated.
P.S.
web searches on "troubleshooting jquery" returned very complex replies that had more to do with editing e-commerce web pages with fancy buttons and logins than with simply serializing data.
P.P.S.
I am tempted to just chuck the jquery and write my own serialization routine. (grrrr!)
===================
Update:
As requested, a link to the browser-side context.
To run: unpack the zip file in a folder somewhere and attach an analog joystick/gamepad to any USB port, then launch index.html in a local browser. Note that a purely digital gamepad - with buttons only or with a joystick that acts like four buttons - won't work.
You will want to try moving joystick axes 1 and 2, (programmatically axes 0 and 1) and use the first (0th) trigger button.
You will get a zillion CORS errors and it will complain bitterly that it cannot reach the server, but the server side context requires a GoPiGo-3 robot running GoPiGo O/S 3.0.1, so I did not include it.
Note: This does not work in Firefox as Firefox absolutely requires a "secure context" to use the Gamepad API. It does work in the current version of Chrome, (Version 97.0.4692.99 (Official Build) (64-bit)), but throws warnings about requiring a secure context.
Please also note that I have made every attempt I know how to try to troubleshoot the offending JavaScript, but trying to debug code that depends on real-time event handling in a browser is something I have not figured out how to do - despite continuous searching and efforts. Any advice on how to do this would be appreciated!
======================
Update:
Researching debugging JavaScript in Chrome disclosed an interesting tidbit:
Including the line // #ts-check as the first line in the JavaScript code turns on additional "linting" (?) or other checks that, (mostly) were a question of adding "var" to the beginning of variable declarations.
However. . . .
There was one comment it made:
gopigo3_joystick.x_axis = Number.parseFloat((jsdata.axes[0]).toFixed(2));
gopigo3_joystick.y_axis = Number.parseFloat(jsdata.axes[1]).toFixed(2);
I could not assign gopigo3_joystick.y_axis to a string object, (or something like that), and I was scratching my head - that was one of the pesky problems I was trying to solve!
If you look closely at that second line, you will notice I forgot a pair of parenthesis, and that second line should look like this:
gopigo3_joystick.y_axis = Number.parseFloat((jsdata.axes[1]).toFixed(2));
Problem solved - at least with respect to that problem.
I figured it out and it had nothing to do with jquery.
Apparently two things are true:
The state of the gpg_data object's structure is "computed", (snapshot taken), the first time the JavaScript engine sees the structure and that is the state that is saved, (even though the value may change later on). In other words, that value is likely totally bogus.
Note: This may only be true for Chrome. Previous experiments with Firefox showed that these structures were updated each time they were encountered and the values seen in the console were valid. Since Firefox now absolutely requires a secure context to use the gamepad API, I could not use Firefox for debugging.
I was trying to be "too clever". Given the following code snippet:
function is_something_happening(old_time, gopigo3_joystick) {
if (gopigo3_joystick.trigger_1 == 1 || gopigo3_joystick.head_enable == 1) {
if (old_time != Number.parseFloat((gopigo3_joystick.time_stamp).toFixed(0))) {
send_data(gopigo3_joystick)
old_time = gopigo3_joystick.time_stamp
}
}
return;
}
The idea behind this particular construction was to determine if "something interesting" is happening, where "something interesting" is defined as:
A keypress, (handled separately)
A joystick movement if either the primary trigger or the pinky trigger is pressed.
Movement without any trigger pressed is ignored so that if the user accidentally bumps against the joystick, the robot doesn't go running around.
Therefore the joystick data only gets updated if the trigger is pressed. In other words, trigger "release" events - the trigger is now = 0 - are not recorded.
The combination of these two events - Chrome taking a "snapshot" of object variables once and once only, (or not keeping them current) - and the trigger value persisting, lead me to believe that jquery was the problem since the values appeared to be different on each side of the jquery call.
I'm using "localforage" in a project and have a mysterious problem.
Through redux / immutableJS I'm storing some settings like "locale", reading it from storage sometimes works but often not.
I hooked into localeforages query system:
var store = transaction.objectStore(self._dbInfo.storeName);
var req = store.get(key);
req.onsuccess = function() {
var value = req.result;
console.log(value);
// ...
}
This should output the plain, serialized immutable data string:
"[\"~#iM\",[\"locale\",\"de_DE\"]]"
But very often I instead receive this:
"[\"~#iM\",[\"locale\",null]]"
When the error occurs the Developer Tools' "application" tab is showing the correct value (so not NULL).
Same codebase, absolutely NO CHANGES, shows different behavior in current Chrome (never working) and Firefox (sometimes working).
Anyone got a similar error in the past?
I don't understand how a plain string value could sometimes work and sometimes not.
I´m working on a big angular.js project.
Now, I´ve to profile some pages about the performance.
With the console.log of the project I´m looking for the performance problems.
I miss if console.log can output the current time (yes, I know in Chrome you can set up).
Is there a way (like in log4java) to format the output?
Thanks in advance,
Be careful, the console object should not be activated in production mainly because it can breaks the code for some users (for example IE8 or less users).
But if you want to test on a tool that you know, you can use the Web API interface which provides some useful method on this object:
https://developer.mozilla.org/en-US/docs/Web/API/Console
(This doc is from the Mozilla Developer Network (MDN) and therefore mainly applies to Firefox, but at the end of that page you can find links to the corresponding docs for the IE, Chrome and Safari DevTools as well as Firebug).
For example, you can use the time() method for your use case:
console.time("answer time");
// ... some other code
console.timeEnd("answer time");
Outputs something like this:
You could try something like this:
console = window.console ? console : {};
native_log = console.log;
window.console.log = function myConsole() {
// Cannot log if console.log is not present natively,
// so fail silently (in IE without debugger opened)
if (!native_log)
return;
var args = Array.prototype.slice.call(arguments);
args.unshift(new Date());
native_log.apply(console, args);
}
console.log('hello'); // Date 2014-12-02T14:14:50.138Z "hello"
Of course, you would not let the new Date() as is, but it gives you the idea.
In my hydbrid app (Phonegap), I am trying to write to localStorage in a very standard way :
window.localStorage.setItem("proDB", JSON.stringify(data));
or
window.localStorage["proDB"] = JSON.stringify(data);
But it doesn't work on Safari on iPad 2 (iOS 7.1).
It doesn't work and the whole app stops.
Here's the userAgent of this ipad :
Can you help me ?
Thanks
Please check whether you have Private Browsing enabled in Safari. In Safari Private Browsing mode, you get a quota of zero. Hence, all calls to localStorage.setItem will throw a quota exceeded error. Personally I think this is a huge mistake by Safari (as so many sites break), but it is what it is so we have to find a way around it. We can do this by:
Detecting whether we have a functional localStorage
Falling back to some replacement if not.
Read on if you want the details :)
1: Detecting a functional local storage
I am currently using this code to detect whether local storage is available, and fall back to a shim if not:
var DB;
try {
var x = '_localstorage_test_' + Date.now();
localStorage.setItem(x, x);
var y = localStorage.getItem(x);
localStorage.removeItem(x);
if (x !== y) {throw new Error();} // check we get back what we stored
DB = localStorage; // all fine
}
catch(e) {
// no localstorage available, use shim
DB = new MemoryStorage('my-app');
}
EDIT: Since writing this I have packaged up the feature detecting code. If you are using NPM you can install storage-available like so:
npm install --save storage-available
then you can use it in your code like this:
if (require('storage-available')('localStorage')) {
// Yay!
}
else {
// Awwww.....
}
2. Fall back to a shim
The easiest way to deal with the issue once we have detected the problem is to fall back to some other object that does not throw errors on every write.
memorystorage is a little library I wrote that follows the Web Storage API but just stores everything in memory. Because it uses the same API, you can use it as a drop-in replacement for localStorage and everything will function fine (though no data will survive page reload). It's Open Source so use as you please.
Background info
For more information on MemoryStorage and this issue in general, read my blog post on this topic: Introducing MemoryStorage.
I have set local storage key values through below logic using swift2.2
let jsStaring = "localStorage.setItem('Key', 'value')"
self.webView.stringByEvaluatingJavaScriptFromString(jsStaring)
Your first setItem example is correct. I don't believe that you can do the second option (localStorage["someKey"] = "someValue") though. Stick with the first one.
You mention hybrid - is it a PhoneGap or some other framework? Where in the app are you calling localStorage.setItem? If PhoneGap, be sure that everything has loaded via onDeviceReady first before trying to access localStorage:
<script type="text/javascript">
// Wait for PhoneGap to load
document.addEventListener("deviceready", onDeviceReady, false);
// PhoneGap is ready
function onDeviceReady() {
window.localStorage.setItem("key", "value");
}
</script>
Also, if the app freezes/stops working, in my experience it's because somewhere in the code you are accessing an object that is undefined. Perhaps try some debugging by checking if localStorage is undefined and logging it? Are you 100% sure that the "setItem" line is where it fails? Console.log is your friend, prove it! :)
if (localStorage === undefined) {
console.log("oops, localStorage not initialized yet.");
}
else {
window.localStorage.setItem("proDB", JSON.stringify(data));
console.log("localStorage available.");
}
I am trying to browse a website, however, it only works under Windows and Mac because they use the navigator.platform from JavaScript to find out the architecture I am running on. Of course, they also use the browser's user agent, but that was easy to spoof.
Here is the .js in question: http://pastebin.com/f56fd608d. The code responsible for browser detection is at the top. Is there any way of changing the .js file before the site runs, or something similar, so I can eliminate the check?
Using the JavaScript console yields:
>navigator.platform
Linux i686
Evidently I changed the browser's user agent, but navigator.platform does not seem to take it's value from the user agent.
Maybe someone knows how to change the value returned by navigator.platform, because I hate running Windows under VirtualBox to use this site.
EDIT:
This could be of interest because Linux users might be artificially denied access to websites, and can do nothing about it.
var fakePlatformGetter = function () {
return "your fake platform";
};
if (Object.defineProperty) {
Object.defineProperty(navigator, "platform", {
get: fakePlatformGetter
});
Object.defineProperty(Navigator.prototype, "platform", {
get: fakePlatformGetter
});
} else if (Object.prototype.__defineGetter__) {
navigator.__defineGetter__("platform", fakePlatformGetter);
Navigator.prototype.__defineGetter__("platform", fakePlatformGetter);
}
Since you can't directly set navigator.platform, you will have to be sneaky - create an object that behaves like navigator, replace its platform, then set navigator to it.
var fake_navigator = {};
for (var i in navigator) {
fake_navigator[i] = navigator[i];
}
fake_navigator.platform = 'MyOS';
navigator = fake_navigator;
If you execute this code before the document loads (using GreaseMonkey, an addon or a Chrome extension), then the page will see navigator.platform as "MyOS".
Note: tested only in Chrome.
Provided that the browser you're using supports Object.defineProperty() (it likely does), a more modern way of achieving the same goal is as follows:
Object.defineProperty(navigator, 'platform', {
value: 'my custom value',
configurable: true // necessary to change value more than once
});
This allows you to set it to any custom value you want, and it also allows you to change it as many times as you want without needing to reload the page.
For a Mozilla-based browser, GreaseSpot / Code Snippets # Hijacking browser properties demonstrates how it may be done. This code may be injected from a GreaseMonkey script.
about:config - > general.platform.override
Attempting to change this property (at any time) in Firefox yields:
Error: setting a property that has only a getter
Source File: index.html
Line: 1
So I think you will have a hard time.
I'd try to contact the author about obtaining a fix.