Developing Feather-Weight Webservices with JavaScript

Code for the Args() classes

  1. See Args() and Using JS code libraries for the development process and use cases of these classes.
  2. Neither of these follows the standard handling of URI query strings. See Passing JavaScript arguments via the src attribute for a discussion on what we left out and why.
  3. The argument values are sanitized against cross-site scripting. The keys, or parameter names, are not. Do not write them to the DOM without making sure they are safe.

“Naked” ArgsObj.js with direct access to params

/*
  Code from "Developing Featherweight Web Services with JavaScript"
      http://feather.elektrum.org/
  (c)An Elektrum Press, retain this notice
      License: http://feather.elektrum.org/appendix/licenses.html
*/

// Args object library -------------------------------------------

// constructor ------------------------
function Args () {
  var caller = this._findCaller();
  var qString = caller.src.replace(/^[^\?]+\??/,'');
  if ( qString ) {
     this._queryString = qString;
     this._parseArgs();
  }
}

// -----------------------------------
Args.prototype._parseArgs = function () {
   if ( ! this._queryString ) return false;
   var Pairs = this._queryString.split(/;/);
   for ( var i = 0; i < Pairs.length; i++ ) {
      var KeyVal = Pairs[i].split('=');
      if ( ! KeyVal.length == 2 ) continue;
      if ( ! ( KeyVal[0] || KeyVal[1] ) ) continue;
      if ( KeyVal[0].match(/^_/) ) continue;
      var key = unescape( KeyVal[0] );
      var val = unescape( KeyVal[1] );
      val = val.replace(/\+/g, ' ');
      val = val.replace(/&/g, '&amp;');
      val = val.replace(/>/g, '&gt;');
      val = val.replace(/</g, '&lt;');
      this[key] = val;
   }
}

// -----------------------------------
Args.prototype.getKeys = function () {
  var keys = new Object();
  for ( var attr in this ) {
    if ( attr.match(/^(_|getKeys)/) ) continue;
    keys[attr] = undefined;
  }
  return keys;
}

// a cache for use inside _findCaller()
if ( ! Args._SeenScriptCache ) Args._SeenScriptCache = new Array();
// -----------------------------------
Args.prototype._findCaller = function () {
  var scripts = document.getElementsByTagName('script');
  for ( var i = scripts.length - 1; i >= 0; i-- ) {
    var src = scripts[i].src;

    var rx = new RegExp(/^http:\/\/elektrum.org/i);
    if ( ! src.match(rx) ) continue; // ignore other sites' scripts

    if ( src.match(/^[^\?]+\?/) && ! Args._SeenScriptCache[i] ) {
      Args._SeenScriptCache[i] = 1;
      return scripts[i];
    }
    else
    {
      Args._SeenScriptCache[i] = 1; // mark it seen anyway
    }
  }
  // none has a query string, so default to most recently seen
  return scripts[ scripts.length - 1 ];
}

Original ArgsObj.js with params property

NB: this one doesn’t handle separated libraries; class code external to the script proper. It must reside inside the script that uses its object. Its findCaller() uses the first technique from Passing JavaScript arguments via the src attribute. The _findCaller() from above could just as easily be used here if you want to keep arguments inside the Arg.param property instead of at the top level of the object.

/*
  Code from "Developing Featherweight Web Services with JavaScript"
      http://feather.elektrum.org/
  (c)An Elektrum Press, retain this notice
      License: http://feather.elektrum.org/appendix/licenses.html
*/

// Args object library -------------------------------------------

// constructor ------------------------
function Args () {
  var caller = this.findCaller();
  var qString = caller.src.replace(/^[^\?]+\??/,'');
  if ( qString ) {
     this.queryString = qString;
     this.params = this.parseArgs();
  }
}

// -----------------------------------
Args.prototype.parseArgs = function () {
   var Params = new Object ();
   var query = this.queryString;
   if ( ! query ) return Params;
   var Pairs = query.split(/;/)
   for ( var i = 0; i < Pairs.length; i++ ) {
      var KeyVal = Pairs[i].split('=');
      if ( ! KeyVal.length == 2 ) continue;
      if ( ! ( KeyVal[0] || KeyVal[1] ) ) continue;
      var key = unescape( KeyVal[0] );
      var val = unescape( KeyVal[1] );
      val = val.replace(/\+/g, ' ');
      val = val.replace(/&/g, '&amp;');
      val = val.replace(/>/g, '&gt;');
      val = val.replace(/</g, '&lt;');
      Params[key] = val;
   }
   return Params;
}

// -----------------------------------
Args.prototype.findCaller = function () {
  var scripts = document.getElementsByTagName('script');
  return scripts[ scripts.length - 1 ];
}
« Extending the native Number() class · Code for the Query() class »
Google
 
Web Developing Featherweight Web Services with JavaScript
This is version 0.57b of this manual. It is a beta version with some gaps. We are grateful for feedback.

The code is the manual has not yet been fully tested against Internet Explorer. Bug reports are welcome.
An Elektrum Press Online