/* SERIALIZE & DESERIALIZE QUERY STRING */
/* todo: switch to the "new" version below */

function qs_serialize(obj) {
  var s = [];
  for (var p in obj) {
    if (obj[p] != undefined) {
      try {
        s.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
      } catch(e) {
        s.push(escape(p) + "=" + escape(obj[p]));
      }
    }
  }
  return s.join("&");
}

function qs_deserialize(s) {
  var obj = {};
  s = (s.indexOf('?') == -1) ? s : s.substr(s.indexOf('?') + 1, s.length);
  var a = s.split('&');
  for(var i = 0; i < a.length; i++) {
    var nv = a[i].split('=');
    if (nv[0] && nv[1]) {
      try {
        obj[decodeURIComponent(nv[0])] = decodeURIComponent(nv[1]);
      } catch(e) {
        obj[unescape(nv[0])] = unescape(nv[1]);
      }
    }
  }
  return obj;
}


/* NEW VERSION! */

/* == QUERY STRING ==
  
  USAGE:

  var foo = QueryString();
  * returns data Object from document.location.search
  
  var foo = QueryString( { a:1, b:2 } );
  * returns serialized "?a=1&b=2" string
  
  var foo = QueryString( "?a=1&b=2" );
  * returns deserialized { a:1, b:2 } object
  
  var foo = QueryString( "/foo?a=0&c=3", { a:1, b:2 } );
  * returns "/foo?a=1&c=3&b=2" url

  var foo = QueryString( "/foo?a=0&c=3", "?a=1&b=2" );
  * returns "/foo?a=1&c=3&b=2" url
  
*/

function QueryString(arg1, arg2, arg3) {
  
  var serialize = function(obj) {
    var qs = [];
    for (var n in obj) {
      if (obj[n] != undefined) {
        v = obj[n];
        if (typeof v != "object") {
          v = [v];
        }
        for (var i = 0; i < v.length; i++) {
          if (v[i] == "") {
            qs.push(encodeURIComponent(n));
          } else {
            qs.push(encodeURIComponent(n) + "=" + encodeURIComponent(v[i]));
          }
        }
      }
    }
    return qs.length ? ("?" + qs.join("&")) : "";
  }
  
  var deserialize = function(str, convert_to_number) {
    str = str.replace(/^[^?]*([?]|$)/, "");
    if (str == "") return {};
    var qs = str.split("&");
    var obj = {};
    for (var i = 0; i < qs.length; i++) {
      var nv = qs[i].split("=");
      var n = decodeURIComponent(nv[0]);
      if (nv.length == 1) {
        obj[n] = "";
      } else {
        var v = decodeURIComponent(nv[1]);
        v = convert_to_number && !isNaN(v) ? Number(v) : v;
        if (typeof obj[n] == "object") {
          obj[n].push(v);
        } else if (typeof obj[n] != "undefined") {
          obj[n] = [obj[n], v];
        } else {
          obj[n] = v;
        }
      }
    }
    return obj;
  }
  
  var build_url = function(url, obj, mode) {
    // mode: 0 = update url qs with obj (passed obj overrides)
    //       1 = update url qs with obj (url qs overrides)
    //       2 = completely replace url qs with obj
    
    var url_qs = deserialize(url);
    
    if (typeof obj == "string") {
      obj = deserialize(obj);
    }
    
    var qs;
    if (mode == 2) {
      qs = obj;
    } else if (mode == 1) {
      for (var prop in url_qs) {
        obj[prop] = url_qs[prop];
      }
      qs = obj;
    } else {
      for (var prop in obj) {
        url_qs[prop] = obj[prop];
      }
      qs = url_qs;
    }
    
    var qs_str = serialize(qs);
    if (qs_str != "") {
      url = url.replace(/[?].*$/, "");
      return url + qs_str;
    } else {
      return url;
    }
  }
  
  if (typeof arg1 == "string" && arg2 != undefined) {
    return build_url(arg1, arg2, arg3);
    
  } else if (typeof arg1 == "object") {
    return serialize(arg1);
    
  } else if (typeof arg1 == "string") {
    return deserialize(arg1);
    
  } else {
    return deserialize(document.location.search);
  }
}
