/**
 * Class extension
 */
Class.def = function(definition) {
  var klass = Class.create();
  if(definition.__extend__) 
    Class.inherit(klass, definition.__extend__);
  if(definition.__static__)
    Object.extend(klass, definition.__static__);
  if(definition.__include__) {
    if(definition.__include__.constructor != Array)
      definition.__include__ = [definition.__include__];
    definition.__include__.each(function(include){
      Object.extend(klass.prototype, include);
    });
  }
  ["__static__","__extend__","__include__"].each(function(n){delete definition[n];});
  Object.extend(klass.prototype, definition);
  return klass;
}

Class.inherit = function(child, parent) {
  Object.extend(child, parent);
  child.__super__ = parent.prototype;
  var dummy = function(){};
  dummy.prototype = parent.prototype;
  child.prototype = new dummy();
  child.prototype.constructor = child;
  return child;
}


/**
 * Hash extension
 */
Hash.toAttribute = function() {
  return this.collect(function(pair){return [pair.key,"=", '"', pair.value, '"'].join("");}).join(" ");
}

/**
 * Error extension
 */
Error.defException = function(message, parent){
     return function() {
        var args = $A(arguments);
        args[0] = args[0] || {};
        parent = parent || Error;
        if(args[0] instanceof Error) {
            var exc = Object.extend(new parent(), args.first());
        }else{
            var exc = new parent();
            if(message) exc.message = new Template(message).evaluate(args.first());
        }
        exc.constructor = this.constructor;
        return exc;
    }
}

/**
 * JSON extension
 */
var JSON = {
    stringToJSON : function(str) {
        var parts = str.toArray();
        parts.each(function(c,i){
            if(c == '"' ||
               c == '\\' ||
               c.charCodeAt(0) < 32 ||
               c.charCodeAt(0) >= 128)
                parts[i] = JSON.escapeChar(c);
        });
        return "\"" + parts.join("") + "\"";
    },

    escapeChar : function(c) {
        if(c == "\"" || c == "\\") return "\\" + c;
        else if (c == "\b") return "\\b";
        else if (c == "\f") return "\\f";
        else if (c == "\n") return "\\n";
        else if (c == "\r") return "\\r";
        else if (c == "\t") return "\\t";
        var hex = c.charCodeAt(0).toString(16);
        if(hex.length == 1) return "\\u000" + hex;
        else if(hex.length == 2) return "\\u00" + hex;
        else if(hex.length == 3) return "\\u0" + hex;
        else return "\\u" + hex;
    },

    arrayToJSON : function(arr) {
        var value = [];
        arr.each(function(x){
            value.push(Object.toJSON(x));
        });
        return "[" + value.join(", ") + "]";
    },

    objToJSON : function(obj) {
        var value = [];
        for(attr in obj) {
            if(obj[attr] == null) {
                value.push("\"" + attr + "\": null");
            }else if(typeof obj[attr] != "function") {
                value.push("\"" + attr + "\": " + Object.toJSON(obj[attr]));
            }
        }
        return "{" + value.join(", ") + "}";
    }
}

/**
 * Objct extension
 */
Object.toJSON = function(obj) {
    if(obj == null) {
        return "null";
    }else if(obj.constructor == String) {
        return JSON.stringToJSON(obj);
    }else if(obj.constructor == Array) {
        return JSON.arrayToJSON(obj);
    }else if(obj.constructor == Number || obj.constructor == Boolean){
        return obj.toString();
    }else if(obj.constructor == Date) {
        return obj.valueOf().toString();
    }else {
        return JSON.objToJSON(obj);
    }
}

/**
 * Function extension
 *
 */
// original from http://nanto.asablo.jp/blog/2005/12/13/176033
Function.prototype.curry = function () {
  var args = arguments;
  var self = this;
  return function () {
    Array.prototype.unshift.apply(arguments, args);
    return self.apply(this, arguments);
  };
}
