Last Updated: December 26, 2018
·
5.968K
· kerrishotts

Cross-platform mobile XHR (PhoneGap/Cordova)

Unless one is using an existing mobile framework when writing a mobile app using PhoneGap/Cordova, it will almost certainly be necessary to write an XHR handler for loading various assets. While this is easy for iOS and Android, WP7 is another mess altogether.

Below is some code that I'm using in my projects. It supports Android, iOS, and WP7 devices -- including handling the fact that WP7 requires assets to be relative to the /app/www directory instead of the www directory on other platforms. This means that one can load local resources across all three platforms without needing to worry about WP7's quirks.

WP7 also has other issues, namely: no synchronous loading of content, and only one XHR request can be going on simultaneously (at least for local content) or one will receive spurious content. Therefore, the code below provides a queue to deal with the latter. Unfortunately the only way out of the former is to avoid synchronous XHR in the first place.

Note: My namespace here is PKUTIL; feel free to adjust as desired. Code is MIT-licensed.

/**
 *
 * Loads a file or URL and returns it to the completion
 * handler. The completion handler must be of the form
 * fn(success/failure, data).
 *
 * @param theFileName       the file or URL to load
 * @param aSync             if TRUE, load asynchronously
 * @param completion        completion block
 *
 */
PKUTIL.loadQueue = Array();
PKUTIL.XHRinProgress = false;
PKUTIL.XHRTimer = -1;

PKUTIL.load = function(theFileName, aSync, completion)
{
  if (device.platform != "WinCE")
  {
    PKUTIL._load(theFileName, aSync, completion);
    return;
  }
  PKUTIL.loadQueue.push(function()
  {
    PKUTIL._load(theFileName, aSync, completion);
  });

  if (PKUTIL.XHRTimer < 0)
  {
    PKUTIL.XHRTimer = setInterval(PKUTIL._XHRQueue, 100);
  }
}

PKUTIL._XHRQueue = function()
{
  if (PKUTIL.XHRinProgress)
  {
    return;
  }
  if (PKUTIL.loadQueue.length > 0)
  {
    var f = PKUTIL.loadQueue.pop();
    f();
  }
}

PKUTIL._load = function(theFileName, aSync, completion)
{
  if (!window.XMLHttpRequest)
  {
    if (completion)
    {
      completion(PKUTIL.COMPLETION_FAILURE, "This browser does not support XMLHttpRequest.");
      return;
    }
  }

  PKUTIL.XHRinProgress = true;

  var r = new XMLHttpRequest();
  r.onreadystatechange = function()
  {
    if (r.readyState == 4)// loaded
    {
      if (r.status == 200 || r.status == 0)// success
      {
        if (completion)
        {
          completion(true, r.responseText);
          PKUTIL.XHRinProgress = false;
        }
      } else// failed to load
      {
        if (completion)
        {
          completion(false, r.status);
          PKUTIL.XHRinProgress = false;
        }
      }
    }
  }
  if (device.platform == "WinCE" && theFileName.substr(0, 4) != "http")
  {
    r.open('GET', "/app/www/" + theFileName, true);
  } else
  {
    r.open('GET', theFileName, aSync);
  }
  r.send();
}