Category Archives: JavaScript

Regular Expression Method Fails on UA Strings

06 Jun 2013

This caught me at first – but upon closer inspection it makes sense – I was writing something that was looking at the User Agent of a device to see if it was Android using the “test” regex method and got the opposite result from what I was expecting. I expected that the “test” regex on the UA for the word “Android” would return true but instead I always got false. Here’s an example:

...
var isAnd = /Android/gi;
console.log(isAnd.test(navigator.userAgent));
...

On the surface this should be true, yet it returns false in the console. See this next example:

...
var isAnd = /Android/gi;
console.log(isAnd.test('android'));
...

Now the above returns true – something in the UA string is breaking the test. Here are the UA’s that I’ve tried with the above:

  • Galaxy Tab 2: Mozilla/5.0 (Linux; U; Android 4.1.1;en-us; GT-P5113 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30
  • Nexus 7: Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19

On a whim I remove the forward slashes and perform the following test:

...
var isAnd = /Android/gi;
console.log(isAnd.test('Mozilla5.0 (Linux; Android 4.1.1; Nexus 7 BuildJRO03D) AppleWebKit535.19 (KHTML, like Gecko) Chrome18.0.1025.166  Safari535.19'));
...

Ah, this is what we’re looking for, that test works. So in order to do a successful pattern match using the “test” regular expression method on the User Agent string you would have to first remove all instances of “/” or escape them with a backslash (\/).

In Regular Expressions certain characters have a special meaning – I would think that a string that the regex is being run on would be immune but that’s apparently not the case. Forward slashes are used to create regular expression literals and if present within the string, well…. who knows what happens, presumeably its interpreted as another regular expression – and then things just won’t work. For that reason forward slashes need to be escaped with another character that has a special meaning in regular expressions – the backslash. So, this “\/” actually represents a single literal forward slash.

Yeah, this gets a little messy, you could do one of two things. You could clean the UA string first by using something like this (which, btw, is a little aggressive but is a good example of what you can do):

...
var str = 'some big string that needs to be "cleaned"';
str.replace(/([\[\]\*\^\&\$\.\(\)\?\/\\\+\{\}\|])/g, "\\$1");
...

Or, and I like this approach a lot for this particular application, you could just use a different method entirely, such as the “search” regex method, which is impervious to this situation:

...
var ua = navigator.userAgent.toLowerCase();
console.log(ua.search('android') != -1)
...

And this gives us the predictable “true” as a result.

jQuery onChange Event and Text Fields

18 Apr 2013

Seems like jQuery’s change event when bound to an input or textarea element doesn’t work like it should. What happens is that when those elements are blurred *then* onChange fires – completely not what we want to happen. According to the documentation this is actually by design. The only input elements that have an onChange event that behaves in a manner that makes sense are radios, checkboxs and select boxes. For anything else the onChange event will fire when the element loses focus!

...
$('input').change(function() {
  console.log('change, but only after the text input is blurred');
});

<input type=text">
...

There are a couple of events that you can use as an alternative, one is the “onInput” event. Curiously while this works in jQuery it is not part of jQuery’s API documentation (v1.9.1) – even though it works. The onInput event is part of the HTML 5 spec so my assumption is that jQuery’s support of it is here to stay, its just not part of the documentation for unknown reasons.

...
$('input').input(function() {
  console.log('behaves like a change event: as the text input element\'s value is changed the oninput event is fired');
});

<input type=text">
...

The caveat here – yes, you knew there would be one – is that since oninput is a member of the HTML5 spec that not every browser will support it. Thankfully IE9 and up supports it as does the crop of modern browsers.

For something more universal you might consider key bindings such as onKeydown or onKeyup, and then not worry about browser support.

...
$('input').keyup(function() {
  console.log('The text input element\'s value has changed');
});

<input type=text">
...

jQuery.parseJSON vs JSON.parse vs json_parse – Memory Profiles During AJAX Requests

10 Apr 2013

When doing an AJAX JSONP request with jQuery you rely on the jQuery.parseJSON method to parse the JSONP response into an object. While jQuery.parseJSON works most of the time I’ve seen it behave as if a memory leak is occurring. This isn’t new to jQuery.ajax() as it has somewhat of a history with memory leaks. The version I am using currently is jQuery 1.9.0, and with a particular project I am seeing a “stair-step” memory leak pattern where the usual ebb and flow of memory being allocated/deallocated appears as a predictable pattern. However, at a certain point there is a spike that itself never goes down which in effect sets the normal ebb/flow memory usage pattern at a higher level than previous. This happens 4 times within my web app, each spike setting a higher and higher average level of memory usage.

The Application

To understand what this is all about I should give a brief description of this particular application. What I have is an app that will be wrapped in PhoneGap. When this app launches on a mobile device it will download an XML document. That document contains data referencing all of the app’s downloadable assets. The app will then check to see if the data exists locally or not (if it did it would have been stored on the app via WebSQL) and if not (or there has been an update as determined by each asset’s timestamp) it will determine what files need to be downloaded and commence the download operation.

The “download” consists of AJAX calls to individual JSONP-type documents which have a structure similar to {date:value,name:value,data:value} where “data” is an image serialized to base64.

There are hundreds of these asset files. The individual files allow for the creation of a download/progress indicator where the file that is currently being downloaded is of course a percentage of the total.

The rest of this discussion deals with the downloading of the application’s assets as that is where the memory issue occurs.

jQuery.parseJSON

Anyone who uses jQuery.ajax() who works with JSON/JSONP will (or at least *should*) be using jQuery.parseJSON. The jQuery framework will take the JSON/JSONP server response and transparently transform it into a JavaScript Object to do with as the developer sees fit.

Initially I implemented the AJAX request exactly as I’ve done many times before not seeing the need to deviate from what has been a predictable pattern of interacting with JSONP server responses. Here is a code snippet of such a setup:

...
$.ajax({
   url:       service_path_here,
   type:      'GET',
   dataType:  'JSONP',
   timeout:    30000
...

Simple stuff, we’re requesting a JSONP response from the server and we have a timeout of 30 seconds.

Observe the following memory usage timeline obtained from Chrome via a script using the above AJAX configuration. The script “loops” in a synchronous fashion through an array of URL’s in an effort to cache the data within the browser. The workflow is: make request > download > parse > store via WebSQL > on txn success/failure move to next URL and start the process over:

jquery_memory_leak_complete_download

You can see quite plainly the pattern here and it is evident that we have a memory leak. Upon investigating the timeline I can see the exact AJAX calls where the leaks occur. The data contained within the JSONP payloads is the aforementioned base64 string and their lengths exceed 2 million characters. I now know the culprit but not why the leak happens but suspect that there is something wrong with jQuery’s handling of the data, in particular, jQuery.parseJSON.

JSON.parse

Some Googling on the topic reveals that there is some previous history on memory leaks while using jQuery to do AJAX calls. One note in particular mentioned the eval method being the root cause. jQuery’s JSON parser is based on work by Douglas Crockford who is well known within the JavaScript community. All web app developmers should be aware of him and his JSON parser script as it is commonly used/referenced by many frameworks. Anyway, I know from having visited D Crockford’s site many times that he offers more than one JSON parser and that one of them explicitly avoids using the eval method in its parsing routines.

Before I proceed to json_parse (the “eval-less” parser) I want to try JSON.parse. It follows that his JSON.parse script will also exhibit the memory leak when presented with JSON exceeding 2 million characters (and thus by extension any JavaScript library’s JSON parser based on his work, of which there are many). Here is the config for that test:

...
$.ajax({
   url:       service_path_here,
   type:      'GET',
   dataType:  'JSONP',
   timeout:    30000,
   converters: {'text json':JSON.parse} 
...

And the memory profile While using JSON.parse:

json.parse_memory_leak_complete_download

We can see the same thing happening – time to use Douglas Crockford’s alternative parser – json_parse.

json_parse

Douglas Crockford describes json_parse as “an alternative JSON parse function that uses recursive descent instead of eval.” Perfect – we want to get away from eval, here’s its implementation within jQuery:

...
$.ajax({
   url:       service_path_here,
   type:      'GET',
   dataType:  'JSONP',
   timeout:    30000,
   converters: {'text json':json_parse} 
...

And the resulting memory usage:

json_parse_memory_leak_non_optimized_complete_download

Thats what we’re looking for – you can see the spikes as the multi-million character JSON payloads get parsed and you can see GC happening afterwards. Memory gets reclaimed just the way we want it to.

From here we can delve further into optimizing the code responsible for these graphics – which is what I’ve done. A week or so of optimization and in some cases code-rewrite results in the following final download memory footprint for my web app:

json_parse_memory_leak_complete_download

Through streamlining a number of things – including removing all my attempts at debugging the issue – I’ve cut the download time and the overall memory foot print has been further improved – possible in large part to json_parse.

Conclusion

jQuery.parseJSON is useful for the vast majority of applications. However, if you are dealing with large JSON payloads – in excess of 1.8 to 2+ million characters in length you should use an alternative JSON parser such as json_parse. Do so and you will ensure that memory is reclaimed as needed and thus avoid app slugishness and crashes.

Here’s the link to get both JSON.parse and json_parse: https://github.com/douglascrockford/JSON-js.

Detecting Network Connectivity With HTML AIR Apps

19 Feb 2013

Nice thing about my job is how varied it is – this time around I’m jumping into HTML AIR apps and the first thing on the list (actually this is the second, the first thing is setting up the AIR debugger… ) is to be able to detect the presence of the network. Its not too difficult, here we go…

Assumptions

First, I assume that you have Dreamweaver with the AIR extension installed. Next I assume that you know how to create the AIR runtime.

If you want to debug this then you should have the AIR SDK present on your machine – debugging is out of scope of this article but will pen some notes on how to do it at some point.

Setup

What I have done here is written an object that will do the “hard work” for you, its just copy and paste. Before we get to that you must first set up your HTML page with the necessary includes – note the script tags in the following bare-bones example:

<html>
  <head>
    <title>Untitled Document
    <script language="javascript" src="scripts/AIRAliases.js"></script>
    <script src="servicemonitor.swf" type="application/x-shockwave-flash"></script>
  </head>
  <body>
  </body>
</html>

You will notice that the first SCRIPT tag references AIRAliases.js (available in the “frameworks” folder within the AIR SDK) – this is necessary only if you are using the shortcut reference to AIR specific methods/functions, which I am. Next you will notice the second SCRIPT tag with the reference to “servicemonitor.swf” (also available in the AIR SDK). This second file is an ActionScript library that encapsulates the service monitor framework which is separate from the AIR framework.

Code

Lets now take a look at the bit of code that I’ve written which is where the network detection setup happens:

  var network = (function(){
    var monitor;
    function initNetwork(b,u,l){
      if (air){
        setNetworkDefaults(b);
        startNetMonitor(u,l);
      }
    }
    function startNetMonitor(u,l){
      monitor = new air.URLMonitor(new air.URLRequest(u)); 
      monitor.addEventListener(air.StatusEvent.STATUS,l); 
      monitor.start(); 
    }
    function setNetworkDefaults(b){
      air.URLRequestDefaults.manageCookies = air.URLRequestDefaults.useCache = b;
    }
    function getMonitorStatus(){
      return monitor.available;	
    }
    return{
      initNetwork:initNetwork,
      getMonitorStatus:getMonitorStatus
    }
  })();

This is designed to be left as-is, you configure it via its initNetwork method where you set your own function to be called when the network status changes. The getMonitorStatus method allows you to go get the network status whenever you want.

Lets see how to use them:

network.initNetwork(urlRequestDefaults,URLtoMonitor,CallBackFunc)

As mentioned this inits the monitor, the following table describes the required arguments.

Argument Type Description
URLtoMonitor String This is the URL to monitor. You could set it as ‘http://www.google.com’ if you are simply looking to monitor the network in general, or maybe it makes sense to use a specific URL so that you know if your web services are up or not.
CallBackFunc Function This is your custom callback function which will be called each time the network status changes. A simple boolean is returned.
urlRequestDefaults Boolean The ability to set 2 AIR network defaults to true or false as you wish – this isn’t related to network monitoring, just the network in general. This will set the values for the following two AIR network options:

  • manageCookies
  • useCache

IMO passing false here is a good idea.

An example of initing the network monitor also must involve a load listener so that it is inited after the document has been loaded:

...
window.addEventListener('load',
  function(){
    network.initNetwork(false,'http://www.cnn.com',myCallback);
  }, false
);
...

network.getMonitorStatus()

You can call this at any point to get the current network status. A little redundant yet maybe useful to some. This will return a boolean.

Lastly, if the variable “network” is reserved by your particular application then feel free to change it.

To test your code run your AIR app and make sure that it is either printing out something to the ADL console orprinting something to the screen that tells you the status of the network. Maybe something like this:

...
window.addEventListener('load',
  function(){
    network.initNetwork(false,'http://www.cnn.com',myCallback);
  }, false
);
...
function myCallBack(bool){
  var el = document.getElementsByTagName('body');
  el[0].innerHTML = bool ? 'Network is present' : 'Network is disconnected';
}
...

Now, assuming you have all the other code in place and are looking at your AIR app on your desktop, simply enable/disable your network by unplugging your Ethernet or disabling WiFi. Give it a few seconds and you will see your app telling you your network status.

Safari JS Engine > Chrome JS Engine?

13 Dec 2012

Got a Parser error in OSX Safari today that didn’t appear in Chrome. On the surface it appears as if its just a silly mistake that I overlooked – another developer used the “default” keyword in his XML, which I converted to JSON and used in my app which appeared similar to this:

...
var something = [{default:'abc'}];
var cat = something[0].default;
...

Chrome on Windows and OSX were fine yet Safari on OSX tripped on it. The answer is rather obvious – the word “default” is a reserved keyword and as such should be avoided. As a quick reference the rest of the keywords are:

break case, catch, continue, debugger, default, delete, do, else, finally, for, function, if, in, instanceof, new, return, switch, this, throw, try, typeof, var, void, while, with

The fix is simple, just treat the object as an associative array and use “default” as the key to get the desired value, like so:

...
var something = [{default:'abc'}];
var cat = something['default'];
...

Simple – and legal and in fact the first example is as well. While it is poor form to use a reserved keyword in the first example it is entirely legal to do so in that manner. The keyword exclusion only refers to using the keywords as identifiers which is not the case here. For reference see ECMAScript 5.1 here.

An example of using a keyword as an identifier (and thus something to be avoided) would be:

...
function default(){
   // wrong!
}
...

It appears that Apple has taken things a bit too far as they often do. See this list of browsers that correctly parse the sample JS with no issues:

Browsers that Accepted the first example:

  1. Windows
    1. Safari 5.1.1
    2. FireFox 17.0.1
    3. Chrome 23.0.1271.97
    4. IE 9
  2. OSX
    1. Chrome 10.0.648.151

Browsers that didn’t:

  1. OSX Safari 5.0.5

So… there it is. Just a quirk of Safari’s JS parser to be aware of. Its yet another example of the “Great Apple Irony” – their supposed adherence to standards. I suppose they are saving us from ourselves in a way as we as developers should know better but its still goes against the grain, so-to-speak.