// Generated by CoffeeScript 1.6.3
/* 
#
#   SmallClasses.js  -  A small library for JS classes inspired by Smalltalk's class system
#
#   http://michaelzeder.de
#
#   Crafted:
#     2013, Michael J. Zeder
#
#   License:
#     LGPL: http://www.gnu.org/licenses/lgpl.html
#     See the LICENSE file in the project's top-level directory for details.
#
#   Authors:
#     * Michael J. Zeder (mjz)
#     * ...
#
*/


(function() {
  var ClassDict, SCClass, SCMethod, SCObject, SCProperty, SCSharedProperty, SmallClasses, currentHandlerForMessageNotUnderstood, defaultHandlerForMessageNotUnderstood, messageNotUnderstood, root,
    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

  SCObject = (function() {
    var doIt, getter, setter;

    function SCObject(meta) {
      var self;
      self = {
        meta: meta,
        data: {}
      };
      this.get = function() {
        return getter.apply(self, arguments);
      };
      this.set = function() {
        return setter.call(self, arguments[0], this);
      };
      this["do"] = function() {
        return doIt.apply(this, arguments);
      };
      this["class"] = function() {
        return self.meta.classname;
      };
      this["class"].get = function(propertyname) {
        return SmallClasses.getSharedProperty(self.meta.classname, propertyname);
      };
      this["class"].set = function(hashOfProps) {
        return SmallClasses.setSharedProperty(self.meta.classname, hashOfProps);
      };
      this["class"]["do"] = function(key, param, context) {
        return SmallClasses["do"](self.meta.classname, key, param, context);
      };
      this.delegate = function(classname, key, param) {
        return SmallClasses["do"](classname, key, param, this);
      };
      this["class"].traits = function() {
        return SmallClasses.getTraits(self.meta.classname);
      };
      this["class"].mixins = function() {
        return SmallClasses.getMixins(self.meta.classname);
      };
      this["class"].behavesLike = function(otherClassname) {
        return SmallClasses.behavesLike(self.meta.classname, otherClassname);
      };
      this._inspectProperties = function() {
        return self.data;
      };
    }

    getter = function(key) {
      var lookUp, lookedUp, mixinClass, mixinClassname, myClass, _i, _len, _ref;
      if (ClassDict[this.meta.classname] == null) {
        return messageNotUnderstood('get', 'I am orphaned, my class does not exist anymore:', [this, this.meta.classname]);
      } else {
        myClass = ClassDict[this.meta.classname];
        if (myClass.properties[key] != null) {
          return this.data[key];
        } else {
          _ref = myClass.mixins;
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            mixinClassname = _ref[_i];
            if (ClassDict[mixinClassname] != null) {
              mixinClass = ClassDict[mixinClassname];
              if (mixinClass.properties[key] != null) {
                return this.data[key];
              }
            }
          }
          lookedUp = [];
          lookUp = function(arrayOfTraits) {
            var recursiveResult, traitClass, traitClassname, _j, _len1;
            for (_j = 0, _len1 = arrayOfTraits.length; _j < _len1; _j++) {
              traitClassname = arrayOfTraits[_j];
              if ((ClassDict[traitClassname] != null) && __indexOf.call(lookedUp, traitClassname) < 0) {
                traitClass = ClassDict[traitClassname];
                if (traitClass.properties[key] != null) {
                  return traitClass.properties[key];
                } else {
                  lookedUp.push(traitClassname);
                  recursiveResult = lookUp(traitClass.traits);
                  if (recursiveResult instanceof SCProperty) {
                    return recursiveResult;
                  }
                }
              }
            }
            return false;
          };
          if (lookUp(myClass.traits)) {
            return this.data[key];
          }
          return messageNotUnderstood('get', "I do not have a property named " + key + ".", [this]);
        }
      }
    };

    setter = function(hashOfProps, thisObject) {
      var key, lookUp, lookedUp, mixinClass, mixinClassname, mixinProperty, myClass, myProperty, setterReturn, traitsProperty, val, _i, _len, _ref;
      if (ClassDict[this.meta.classname] == null) {
        return messageNotUnderstood('set', 'I am orphaned, my class does not exist anymore:', [this, this.meta.classname]);
      } else if (Object.prototype.toString.call(hashOfProps) !== '[object Object]') {
        return messageNotUnderstood('set', 'Properties must be submitted in the form { key : value }.', [this, hashOfProps]);
      } else {
        myClass = ClassDict[this.meta.classname];
        setterReturn = [];
        for (key in hashOfProps) {
          val = hashOfProps[key];
          if (myClass.properties[key] != null) {
            myProperty = myClass.properties[key];
            if (myProperty.validate.call(thisObject, val)) {
              setterReturn.push(this.data[key] = myProperty.transform.call(thisObject, val));
              continue;
            } else {
              messageNotUnderstood('set', "I do not set an invalid value for property " + key + ".", [this, val]);
            }
          } else {
            _ref = myClass.mixins;
            for (_i = 0, _len = _ref.length; _i < _len; _i++) {
              mixinClassname = _ref[_i];
              if (ClassDict[mixinClassname] != null) {
                mixinClass = ClassDict[mixinClassname];
                if (mixinClass.properties[key] != null) {
                  mixinProperty = mixinClass.properties[key];
                  if (mixinProperty.validate.call(thisObject, val)) {
                    setterReturn.push(this.data[key] = mixinProperty.transform.call(thisObject, val));
                    continue;
                  } else {
                    messageNotUnderstood('set', "I do not set an invalid value for property " + key + ".", [this, val]);
                    continue;
                  }
                }
              }
            }
            lookedUp = [];
            lookUp = function(arrayOfTraits) {
              var recursiveResult, traitClass, traitClassname, _j, _len1;
              for (_j = 0, _len1 = arrayOfTraits.length; _j < _len1; _j++) {
                traitClassname = arrayOfTraits[_j];
                if ((ClassDict[traitClassname] != null) && __indexOf.call(lookedUp, traitClassname) < 0) {
                  traitClass = ClassDict[traitClassname];
                  if (traitClass.properties[key] != null) {
                    return traitClass.properties[key];
                  } else {
                    lookedUp.push(traitClassname);
                    recursiveResult = lookUp(traitClass.traits);
                    if (recursiveResult instanceof SCProperty) {
                      return recursiveResult;
                    }
                  }
                }
              }
              return false;
            };
            traitsProperty = lookUp(myClass.traits);
            if (traitsProperty) {
              if (traitsProperty.validate.call(thisObject, val)) {
                setterReturn.push(this.data[key] = traitsProperty.transform.call(thisObject, val));
                continue;
              } else {
                messageNotUnderstood('set', "I do not set an invalid value for property " + key + ".", [this, val]);
                continue;
              }
            }
            messageNotUnderstood('set', "I do not have a property named " + key + ".", [this, val]);
          }
        }
        if (setterReturn.length === 1) {
          return setterReturn[0];
        } else {
          return setterReturn;
        }
      }
    };

    doIt = function(key, parameter) {
      var lookUp, lookedUp, mixinClass, mixinClassname, myClass, traitsMethod, _i, _len, _ref;
      if (ClassDict[this["class"]()] == null) {
        return messageNotUnderstood('do', 'I am orphaned, my class does not exist anymore:', [this, this["class"]()]);
      } else {
        myClass = ClassDict[this["class"]()];
        if (myClass.methods[key] != null) {
          return myClass.methods[key].code.call(this, parameter);
        } else {
          _ref = myClass.mixins;
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            mixinClassname = _ref[_i];
            if (ClassDict[mixinClassname] != null) {
              mixinClass = ClassDict[mixinClassname];
              if (mixinClass.methods[key] != null) {
                return mixinClass.methods[key].code.call(this, parameter);
              }
            }
          }
          lookedUp = [];
          lookUp = function(arrayOfTraits) {
            var recursiveResult, traitClass, traitClassname, _j, _len1;
            for (_j = 0, _len1 = arrayOfTraits.length; _j < _len1; _j++) {
              traitClassname = arrayOfTraits[_j];
              if ((ClassDict[traitClassname] != null) && __indexOf.call(lookedUp, traitClassname) < 0) {
                traitClass = ClassDict[traitClassname];
                if (traitClass.methods[key] != null) {
                  return traitClass.methods[key];
                } else {
                  lookedUp.push(traitClassname);
                  recursiveResult = lookUp(traitClass.traits);
                  if (recursiveResult instanceof SCMethod) {
                    return recursiveResult;
                  }
                }
              }
            }
            return false;
          };
          traitsMethod = lookUp(myClass.traits);
          if (traitsMethod) {
            return traitsMethod.code.call(this, parameter);
          }
          return messageNotUnderstood('do', 'I do not know how to do:', [this, key]);
        }
      }
    };

    return SCObject;

  })();

  SCClass = (function() {
    function SCClass(classname) {
      this.classname = classname;
      this.traits = [];
      this.mixins = [];
      this.properties = {};
      this.methods = {};
      this.sharedProperties = {};
    }

    SCClass.prototype.comment = "I am a class, without a comment...";

    return SCClass;

  })();

  SCProperty = (function() {
    function SCProperty() {}

    SCProperty.prototype.validate = function(val) {
      return true;
    };

    SCProperty.prototype.transform = function(val) {
      return val;
    };

    SCProperty.prototype.comment = "I am a property (instance property), without a comment...";

    return SCProperty;

  })();

  SCSharedProperty = (function() {
    function SCSharedProperty() {
      this.value = null;
    }

    SCSharedProperty.prototype.validate = function(val) {
      return true;
    };

    SCSharedProperty.prototype.transform = function(val) {
      return val;
    };

    SCSharedProperty.prototype.comment = "I am a shared property (class property), without a comment...";

    return SCSharedProperty;

  })();

  SCMethod = (function() {
    function SCMethod() {}

    SCMethod.prototype.code = function() {};

    SCMethod.prototype.comment = "I am a method, without a comment...";

    return SCMethod;

  })();

  ClassDict = {};

  defaultHandlerForMessageNotUnderstood = currentHandlerForMessageNotUnderstood = function(message, reason, params) {
    var error;
    try {
      throw new Error('SC: Message not understood.');
    } catch (_error) {
      error = _error;
      return console.log("SC: Message not understood: " + message + "\n" + reason + "\n\n parameters:\n", params, "\ncall stack:\n", error.stack);
    }
  };

  messageNotUnderstood = function() {
    return currentHandlerForMessageNotUnderstood.apply(this, arguments);
  };

  SmallClasses = (function() {
    function SmallClasses() {}

    SmallClasses.loadPackages = function(SCPackages) {
      var key, _results;
      if (typeof SCPackages === 'object') {
        _results = [];
        for (key in SCPackages) {
          _results.push(this.loadPackage(SCPackages[key]));
        }
        return _results;
      } else {
        return messageNotUnderstood('loadPackages', 'Packages must be stored in object or array, not:', SCPackages);
      }
    };

    SmallClasses.loadPackage = function(SCPackage) {
      var classdescription, classname, methoddescription, methodname, newHashOfProps, propertydescription, propertyname, sharedpropertydescription, sharedpropertyname, _ref, _ref1, _results;
      if (Object.prototype.toString.call(SCPackage) !== '[object Object]') {
        return messageNotUnderstood('loadPackage', 'Package must be an object, not:', SCPackage);
      } else {
        _results = [];
        for (classname in SCPackage) {
          classdescription = SCPackage[classname];
          this.defineClass(classname);
          if (classdescription.comment != null) {
            this.setClassComment(classname, classdescription.comment);
          }
          if (classdescription.traits != null) {
            this.defineTraits(classname, classdescription.traits);
          }
          if (classdescription.mixins != null) {
            this.defineMixins(classname, classdescription.mixins);
          }
          if (classdescription.properties != null) {
            if (Object.prototype.toString.call(classdescription.properties) !== '[object Object]') {
              messageNotUnderstood('loadPackage', 'Property definitions must be an object, not:', classdescription.properties);
            }
            _ref = classdescription.properties;
            for (propertyname in _ref) {
              propertydescription = _ref[propertyname];
              this.defineProperty(classname, propertyname);
              if (propertydescription.comment != null) {
                this.setPropertyComment(classname, propertyname, propertydescription.comment);
              }
              if (propertydescription.validate != null) {
                this.definePropertyValidator(classname, propertyname, propertydescription.validate);
              }
              if (propertydescription.transform != null) {
                this.definePropertyTransformer(classname, propertyname, propertydescription.transform);
              }
            }
          }
          if (classdescription.sharedProperties != null) {
            if (Object.prototype.toString.call(classdescription.sharedProperties) !== '[object Object]') {
              messageNotUnderstood('loadPackage', 'Shared property definitions must be an object, not:', classdescription.sharedProperties);
            }
            _ref1 = classdescription.sharedProperties;
            for (sharedpropertyname in _ref1) {
              sharedpropertydescription = _ref1[sharedpropertyname];
              this.defineSharedProperty(classname, sharedpropertyname);
              if (sharedpropertydescription.comment != null) {
                this.setSharedPropertyComment(classname, sharedpropertyname, sharedpropertydescription.comment);
              }
              if (sharedpropertydescription.validate != null) {
                this.defineSharedPropertyValidator(classname, sharedpropertyname, sharedpropertydescription.validate);
              }
              if (sharedpropertydescription.transform != null) {
                this.defineSharedPropertyTransformer(classname, sharedpropertyname, sharedpropertydescription.transform);
              }
              if (sharedpropertydescription.initValue != null) {
                newHashOfProps = {};
                newHashOfProps[sharedpropertyname] = sharedpropertydescription.initValue;
                this.setSharedProperty(classname, newHashOfProps);
              }
            }
          }
          if (classdescription.methods != null) {
            if (Object.prototype.toString.call(classdescription.methods) !== '[object Object]') {
              messageNotUnderstood('loadPackage', 'Method definitions must be an object, not:', classdescription.methods);
            }
            _results.push((function() {
              var _ref2, _results1;
              _ref2 = classdescription.methods;
              _results1 = [];
              for (methodname in _ref2) {
                methoddescription = _ref2[methodname];
                this.defineMethod(classname, methodname);
                if (methoddescription.comment != null) {
                  this.setMethodComment(classname, methodname, methoddescription.comment);
                }
                if (methoddescription.code != null) {
                  _results1.push(this.setMethodCode(classname, methodname, methoddescription.code));
                } else {
                  _results1.push(void 0);
                }
              }
              return _results1;
            }).call(this));
          } else {
            _results.push(void 0);
          }
        }
        return _results;
      }
    };

    SmallClasses.getClasses = function() {
      var classname, _results;
      _results = [];
      for (classname in ClassDict) {
        _results.push(classname);
      }
      return _results;
    };

    SmallClasses.defineClass = function(classname) {
      if ((classname == null) || !/^[$A-Z_][0-9A-Z_$]*$/i.test(classname)) {
        return messageNotUnderstood('defineClass', "A class needs a valid identifier, not:", classname);
      } else {
        return ClassDict[classname] = new SCClass(classname);
      }
    };

    SmallClasses.removeClass = function(classname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('removeClass', "Class " + classname + " does not exist.");
      } else {
        return delete ClassDict[classname];
      }
    };

    SmallClasses.getClassComment = function(classname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getClassComment', "Class " + classname + " does not exist.");
      } else {
        return ClassDict[classname].comment;
      }
    };

    SmallClasses.setClassComment = function(classname, comment) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('setClassComment', "Class " + classname + " does not exist.");
      } else if (typeof comment !== 'string') {
        return messageNotUnderstood('setClassComment', "Comment must be a string.");
      } else {
        return ClassDict[classname].comment = comment;
      }
    };

    SmallClasses.getTraits = function(classname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getTraits', "Class " + classname + " does not exist.");
      } else {
        return ClassDict[classname].traits;
      }
    };

    SmallClasses.defineTraits = function(classname, traits) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('defineTraits', "Class " + classname + " does not exist.");
      } else if ((traits == null) || typeof traits !== 'object' || Object.prototype.toString.call(traits) !== '[object Array]') {
        return messageNotUnderstood('defineTraits', "Traits must be an array.", traits);
      } else {
        return ClassDict[classname].traits = traits;
      }
    };

    SmallClasses.getMixins = function(classname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getMixins', "Class " + classname + " does not exist.");
      } else {
        return ClassDict[classname].mixins;
      }
    };

    SmallClasses.defineMixins = function(classname, mixins) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('defineMixins', "Class " + classname + " does not exist.");
      } else if ((mixins == null) || typeof mixins !== 'object' || Object.prototype.toString.call(mixins) !== '[object Array]') {
        return messageNotUnderstood('defineMixins', "Mixins must be an array.", mixins);
      } else {
        return ClassDict[classname].mixins = mixins;
      }
    };

    SmallClasses.getProperties = function(classname) {
      var propertyname, _results;
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getProperties', "Class " + classname + " does not exist.");
      } else {
        _results = [];
        for (propertyname in ClassDict[classname].properties) {
          _results.push(propertyname);
        }
        return _results;
      }
    };

    SmallClasses.defineProperty = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('defineProperty', "Class " + classname + " does not exist.");
      } else if ((propertyname == null) || !/^[$A-Z_][0-9A-Z_$]*$/i.test(propertyname)) {
        return messageNotUnderstood('defineProperty', "A property needs a valid identifier, not:", propertyname);
      } else {
        return ClassDict[classname].properties[propertyname] = new SCProperty();
      }
    };

    SmallClasses.removeProperty = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('removeProperty', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('removeProperty', "Property " + propertyname + " does not exist.");
      } else {
        return delete ClassDict[classname].properties[propertyname];
      }
    };

    SmallClasses.getPropertyValidator = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getPropertyValidator', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('getPropertyValidator', "Property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].properties[propertyname].validate;
      }
    };

    SmallClasses.definePropertyValidator = function(classname, propertyname, validatorFunc) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('definePropertyValidator', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('definePropertyValidator', "Property " + propertyname + " does not exist.");
      } else if (typeof validatorFunc !== 'function') {
        return messageNotUnderstood('definePropertyValidator', "Validator must be a function of the form function(val){ return val?true:false }.");
      } else {
        return ClassDict[classname].properties[propertyname].validate = validatorFunc;
      }
    };

    SmallClasses.removePropertyValidator = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('removePropertyValidator', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('removePropertyValidator', "Property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].properties[propertyname].validate = SCProperty.prototype.validate;
      }
    };

    SmallClasses.getPropertyTransformer = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getPropertyTransformer', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('getPropertyTransformer', "Property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].properties[propertyname].transform;
      }
    };

    SmallClasses.definePropertyTransformer = function(classname, propertyname, transformerFunc) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('definePropertyTransformer', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('definePropertyTransformer', "Property " + propertyname + " does not exist.");
      } else if (typeof transformerFunc !== 'function') {
        return messageNotUnderstood('definePropertyTransformer', "Transformer must be a function of the form function(val){ return val }.");
      } else {
        return ClassDict[classname].properties[propertyname].transform = transformerFunc;
      }
    };

    SmallClasses.removePropertyTransformer = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('removePropertyTransformer', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('removePropertyTransformer', "Property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].properties[propertyname].transform = SCProperty.prototype.transform;
      }
    };

    SmallClasses.getPropertyComment = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getPropertyComment', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('getPropertyComment', "Property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].properties[propertyname].comment;
      }
    };

    SmallClasses.setPropertyComment = function(classname, propertyname, comment) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getPropertyComment', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].properties[propertyname] == null) {
        return messageNotUnderstood('getPropertyComment', "Property " + propertyname + " does not exist.");
      } else if (typeof comment !== 'string') {
        return messageNotUnderstood('getPropertyComment', "Comment must be a string.");
      } else {
        return ClassDict[classname].properties[propertyname].comment = comment;
      }
    };

    SmallClasses.getSharedProperties = function(classname) {
      var propertyname, _results;
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getSharedProperties', "Class " + classname + " does not exist.");
      } else {
        _results = [];
        for (propertyname in ClassDict[classname].sharedProperties) {
          _results.push(propertyname);
        }
        return _results;
      }
    };

    SmallClasses.defineSharedProperty = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('defineSharedProperty', "Class " + classname + " does not exist.");
      } else if ((propertyname == null) || !/^[$A-Z_][0-9A-Z_$]*$/i.test(propertyname)) {
        return messageNotUnderstood('defineSharedProperty', "A shared property needs a valid identifier, not:", propertyname);
      } else {
        return ClassDict[classname].sharedProperties[propertyname] = new SCSharedProperty();
      }
    };

    SmallClasses.removeSharedProperty = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('removeSharedProperty', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('removeSharedProperty', "Shared property " + propertyname + " does not exist.");
      } else {
        return delete ClassDict[classname].sharedProperties[propertyname];
      }
    };

    SmallClasses.getSharedPropertyValidator = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getSharedPropertyValidator', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('getSharedPropertyValidator', "Shared property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].sharedProperties[propertyname].validate;
      }
    };

    SmallClasses.defineSharedPropertyValidator = function(classname, propertyname, validatorFunc) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('defineSharedPropertyValidator', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('defineSharedPropertyValidator', "Shared property " + propertyname + " does not exist.");
      } else if (typeof validatorFunc !== 'function') {
        return messageNotUnderstood('defineSharedPropertyValidator', "Validator must be a function of the form function(val){ return val?true:false }.");
      } else {
        return ClassDict[classname].sharedProperties[propertyname].validate = validatorFunc;
      }
    };

    SmallClasses.removeSharedPropertyValidator = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('removeSharedPropertyValidator', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('removeSharedPropertyValidator', "Shared property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].sharedProperties[propertyname].validate = SCSharedProperty.prototype.validate;
      }
    };

    SmallClasses.getSharedPropertyTransformer = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getSharedPropertyTransformer', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('getSharedPropertyTransformer', "Shared property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].sharedProperties[propertyname].transform;
      }
    };

    SmallClasses.defineSharedPropertyTransformer = function(classname, propertyname, transformerFunc) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('defineSharedPropertyTransformer', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('defineSharedPropertyTransformer', "Property " + propertyname + " does not exist.");
      } else if (typeof transformerFunc !== 'function') {
        return messageNotUnderstood('defineSharedPropertyTransformer', "Transformer must be a function of the form function(val){ return val }.");
      } else {
        return ClassDict[classname].sharedProperties[propertyname].transform = transformerFunc;
      }
    };

    SmallClasses.removeSharedPropertyTransformer = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('removeSharedPropertyTransformer', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('removeSharedPropertyTransformer', "Shared Property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].sharedProperties[propertyname].transform = SCSharedProperty.prototype.transform;
      }
    };

    SmallClasses.getSharedPropertyComment = function(classname, propertyname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getSharedPropertyComment', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('getSharedPropertyComment', "Property " + propertyname + " does not exist.");
      } else {
        return ClassDict[classname].sharedProperties[propertyname].comment;
      }
    };

    SmallClasses.setSharedPropertyComment = function(classname, propertyname, comment) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getSharedPropertyComment', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].sharedProperties[propertyname] == null) {
        return messageNotUnderstood('getSharedPropertyComment', "Property " + propertyname + " does not exist.");
      } else if (typeof comment !== 'string') {
        return messageNotUnderstood('getSharedPropertyComment', "Comment must be a string.");
      } else {
        return ClassDict[classname].sharedProperties[propertyname].comment = comment;
      }
    };

    SmallClasses.getSharedProperty = function(classname, propertyname) {
      var lookUp, lookedUp, mixinClassname, thisClass, thisMixin, traitsSharedProperty, _i, _len, _ref;
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getSharedProperty', "Class " + classname + " does not exist.");
      } else {
        thisClass = ClassDict[classname];
        if (thisClass.sharedProperties[propertyname] != null) {
          return thisClass.sharedProperties[propertyname].value;
        } else {
          _ref = thisClass.mixins;
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            mixinClassname = _ref[_i];
            if (ClassDict[mixinClassname] != null) {
              thisMixin = ClassDict[mixinClassname];
              if (thisMixin.sharedProperties[propertyname] != null) {
                return thisMixin.sharedProperties[propertyname].value;
              }
            }
          }
          lookedUp = [];
          lookUp = function(arrayOfTraits) {
            var recursiveResult, traitClass, traitClassname, _j, _len1;
            for (_j = 0, _len1 = arrayOfTraits.length; _j < _len1; _j++) {
              traitClassname = arrayOfTraits[_j];
              if ((ClassDict[traitClassname] != null) && __indexOf.call(lookedUp, traitClassname) < 0) {
                traitClass = ClassDict[traitClassname];
                if (traitClass.sharedProperties[propertyname] != null) {
                  return traitClass.sharedProperties[propertyname];
                } else {
                  lookedUp.push(traitClassname);
                  recursiveResult = lookUp(traitClass.traits);
                  if (recursiveResult instanceof SCSharedProperty) {
                    return recursiveResult;
                  }
                }
              }
            }
            return false;
          };
          traitsSharedProperty = lookUp(thisClass.traits);
          if (traitsSharedProperty) {
            return traitsSharedProperty.value;
          }
          return messageNotUnderstood('getSharedProperty', 'I do not have a shared property named:', propertyname);
        }
      }
    };

    SmallClasses.setSharedProperty = function(classname, hashOfProps) {
      var key, thisClass, thisSharedProperty, val;
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('setSharedProperty', "Class " + classname + " does not exist.");
      } else if (Object.prototype.toString.call(hashOfProps) !== '[object Object]') {
        return messageNotUnderstood('setSharedProperty', 'Shared properties must be submitted in the form { key : value }, not:', hashOfProps);
      } else {
        thisClass = ClassDict[classname];
        for (key in hashOfProps) {
          val = hashOfProps[key];
          if (thisClass.sharedProperties[key] != null) {
            thisSharedProperty = thisClass.sharedProperties[key];
            if (thisSharedProperty.validate(val)) {
              return thisSharedProperty.value = thisSharedProperty.transform(val);
            } else {
              messageNotUnderstood('setSharedProperty', "I do not set an invalid value for property " + key + ":", val);
            }
          } else {
            messageNotUnderstood('setSharedProperty', 'I do not have a property named:', key);
          }
        }
      }
    };

    SmallClasses.getMethods = function(classname) {
      var methodname, _results;
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getMethods', "Class " + classname + " does not exist.");
      } else {
        _results = [];
        for (methodname in ClassDict[classname].methods) {
          _results.push(methodname);
        }
        return _results;
      }
    };

    SmallClasses.defineMethod = function(classname, methodname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('defineMethod', "Class " + classname + " does not exist.");
      } else if ((methodname == null) || !/^[$A-Z_][0-9A-Z_$]*$/i.test(methodname)) {
        return messageNotUnderstood('defineMethod', "A method needs a valid identifier, not:", methodname);
      } else {
        return ClassDict[classname].methods[methodname] = new SCMethod();
      }
    };

    SmallClasses.removeMethod = function(classname, methodname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('removeMethod', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].methods[methodname] == null) {
        return messageNotUnderstood('removeMethod', "Class " + classname + " has no method " + methodname + ".");
      } else {
        return delete ClassDict[classname].methods[methodname];
      }
    };

    SmallClasses.getMethodCode = function(classname, methodname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getMethodCode', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].methods[methodname] == null) {
        return messageNotUnderstood('getMethodCode', "Class " + classname + " has no method " + methodname + ".");
      } else {
        return ClassDict[classname].methods[methodname].code;
      }
    };

    SmallClasses.setMethodCode = function(classname, methodname, methodFunction) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('setMethodCode', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].methods[methodname] == null) {
        return messageNotUnderstood('setMethodCode', "Class " + classname + " has no method " + methodname + ".");
      } else if (typeof methodFunction !== 'function') {
        return messageNotUnderstood('setMethodCode', "A method must be a function, not:", methodFunction);
      } else {
        return ClassDict[classname].methods[methodname].code = methodFunction;
      }
    };

    SmallClasses.getMethodComment = function(classname, methodname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('getMethodComment', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].methods[methodname] == null) {
        return messageNotUnderstood('getMethodComment', "Class " + classname + " has no method " + methodname + ".");
      } else {
        return ClassDict[classname].methods[methodname].comment;
      }
    };

    SmallClasses.setMethodComment = function(classname, methodname, methodComment) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('setMethodComment', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].methods[methodname] == null) {
        return messageNotUnderstood('setMethodComment', "Class " + classname + " has no method " + methodname + ".");
      } else if (typeof methodComment !== 'string') {
        return messageNotUnderstood('setMethodComment', "Comment should be a string, not:", methodComment);
      } else {
        return ClassDict[classname].methods[methodname].comment = methodComment;
      }
    };

    SmallClasses["do"] = function(classname, methodname, parameter, context) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('do', "Class " + classname + " does not exist.");
      } else if (ClassDict[classname].methods[methodname] == null) {
        return messageNotUnderstood('do', "Class " + classname + " has no method " + methodname + ".");
      } else {
        return ClassDict[classname].methods[methodname].code.call((context ? context : this), parameter);
      }
    };

    SmallClasses.behavesLike = function(classname, otherClassname) {
      var isInDelegationChain;
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('behavesLike', "Class " + classname + " does not exist.");
      } else if (ClassDict[otherClassname] == null) {
        return messageNotUnderstood('behavesLike', "Class " + otherClassname + " does not exist.");
      } else {
        isInDelegationChain = false;
        if ((ClassDict[classname].traits.indexOf(otherClassname)) > -1) {
          isInDelegationChain = true;
        }
        if ((ClassDict[classname].mixins.indexOf(otherClassname)) > -1) {
          isInDelegationChain = true;
        }
        return isInDelegationChain;
      }
    };

    SmallClasses.get = function(classname, propertyname) {
      return getSharedProperty(classname, propertyname);
    };

    SmallClasses.set = function(classname, hashOfProps) {
      return setSharedProperty(classname, hashOfProps);
    };

    SmallClasses["new"] = function(classname) {
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('new', "Class " + classname + " does not exist.");
      } else {
        return new SCObject({
          classname: classname
        });
      }
    };

    SmallClasses.init = function(classname, initHash) {
      var newObject;
      if (ClassDict[classname] == null) {
        return messageNotUnderstood('new', "Class " + classname + " does not exist.");
      } else {
        newObject = new SCObject({
          classname: classname
        });
        newObject["do"]('init', initHash);
        return newObject;
      }
    };

    SmallClasses.setHandlerForMessageNotUnderstood = function(func) {
      if (func == null) {
        return currentHandlerForMessageNotUnderstood = defaultHandlerForMessageNotUnderstood;
      } else if (typeof func !== 'function') {
        return messageNotUnderstood('setHandlerForMessageNotUnderstood', 'I need a function, not:', func);
      } else {
        return currentHandlerForMessageNotUnderstood = func;
      }
    };

    SmallClasses.getHandlerForMessageNotUnderstood = function() {
      return currentHandlerForMessageNotUnderstood;
    };

    return SmallClasses;

  })();

  root = typeof window !== "undefined" && window !== null ? window : typeof exports !== "undefined" && exports !== null ? exports : this;

  root.SC = SmallClasses;

}).call(this);
