var unreserved = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~";
var reserved = "!*'();:@&=+$,/?%#[]";
var allowed = unreserved + reserved;
var hexchars = "0123456789ABCDEFabcdef";

function gethex(decimal) {
  return "%" + hexchars.charAt(decimal >> 4) + hexchars.charAt(decimal & 0xF);
}

function encode(decoded, charset) {
  var encoded = "";

  if (charset == "ascii") {
    var notascii = "";
    for (var i = 0; i < decoded.length; i++) {
      var ch = decoded.charAt(i);
      if (unreserved.indexOf(ch) != -1) {
        encoded = encoded + ch;
      } else {
        var charcode = decoded.charCodeAt(i);
        if (charcode < 128) {
          encoded = encoded + gethex(charcode);
        } else {
          encoded = encoded + ch;
          notascii = notascii + ch + " ";
        }
      }
    }
    return encoded;
  }

  if (charset == "utf8") {
    for (var i = 0; i < decoded.length; i++) {
      var ch = decoded.charAt(i);
      if (unreserved.indexOf(ch) != -1) {
        encoded = encoded + ch;
      } else {
        var charcode = decoded.charCodeAt(i);
        if (charcode < 128) encoded = encoded + gethex(charcode);
        if (charcode > 127 && charcode < 2048) {
          encoded = encoded + gethex((charcode >> 6) | 0xC0);
          encoded = encoded + gethex((charcode & 0x3F) | 0x80);
        }
        if (charcode > 2047 && charcode < 65536) {
          encoded = encoded + gethex((charcode >> 12) | 0xE0);
          encoded = encoded + gethex(((charcode >> 6) & 0x3F) | 0x80);
          encoded = encoded + gethex((charcode & 0x3F) | 0x80);
        }

        if (charcode > 65535) {
          encoded = encoded + gethex((charcode >> 18) | 0xF0);
          encoded = encoded + gethex(((charcode >> 12) & 0x3F) | 0x80);
          encoded = encoded + gethex(((charcode >> 6) & 0x3F) | 0x80);
          encoded = encoded + gethex((charcode & 0x3F) | 0x80);
        }
      }
    }
    return encoded;
  }
}

function debug(o) {
  var s = '{';
  for (var i in o) {
    var val = o[i] + '';
    if (val.indexOf('function') == -1) {
      if (s != '{') s += ', \n';
      s += '"' + i + '": ' + debugvalue(o[i]);
    }
  }
  return s + '}';
}

function debugvalue(o) {
  var t = getObjectClass(o);
  var s = '';
  switch (t) {
    case 'Array':
      s += '[';
      for (var i = 0; i < o.length; i++) {
        if (i != 0) s += ', ';
        s += debugvalue(o[i]);
      }
      s += ']';
      break;
    case 'String':
      s += '"' + o + '"';
      break;
    default:
      if (typeof o == 'object') {
        s += debug(o);
      } else {
        s += o;
      }
      break;
  }
  return s;
}

function getObjectClass(obj) {
  if (obj && obj.constructor && obj.constructor.toString) {
    var arr = obj.constructor.toString().match(
            /function\s*(\w+)/);

    if (arr && arr.length == 2) return arr[1];
  }

  return undefined;
}

function DummyHash() {
  var _hash = new Array();
  var _keys = new Array();
  var _count = 0;

  this.keys = function() {
    var ret = new Array();
    var l = _count;
    for (var i = 0; i < l; ++i) {
      ret.push(_keys[i]);
    }
    return ret;
  }

  this.values = function() {
    var ret = new Array();
    var l = _count;
    for (var i = 0; i < l; ++i) {
      var k = _keys[i];
      ret.push(_hash[_keys[i]]);
    }
    return ret;
  }

  this.add = function(key, value) {
    if (typeof (_hash[key]) === 'undefined') {
      _keys.push(key);
      _count++;
    }
    _hash[key] = value;
  }

  this.getItem = function(key) {
    return _hash[key];
  }

  this.setItem = function(key, value) {
    _hash[key] = value;
  }

  this.exists = function(key) {
    return typeof (_hash[key]) != 'undefined';
  }

  this.removeAll = function() {
    _hash = new Array();
    _keys = new Array();
    _count = 0;
  }

  this.count = function() {
    return _count;
  }

  // very slow for now
  this.remove = function(key) {
    var newHash = new Array();
    var newKeys = new Array();
    var l = _keys.length;
    _count = 0;
    for (var i = 0; i < l; ++i) {
      var ik = keys[i];
      if (ik != key) {
        newKeys.push(ik);
        newHash[ik] = _hash[ik];
        _count++;
      }
    }
    _hash = newHash;
    _keys = newKeys;
  }
}


var json = new Object();
json.remainings = 0;

json.load = function(url) {
  var obj = new JSONscriptRequest(url);
  obj.buildScriptTag(json.readyStateChange);
  obj.addScriptTag();
  json.obj = obj;
}