/*
	Base.js, version 1.2
	Copyright 2006-2008, Dean Edwards
	License: http://www.opensource.org/licenses/mit-license.php

        Modified to fit into the Casco namespace
*/

(function(global) {

var BASE         = /eval/.test(detect) ? /\bbase\b/ : /.*/; // some platforms don't allow decompilation
var CONSTRUCTING = "#constructing";
var HIDDEN       = ["constructor", "toString"];             // only override these when prototyping

var Undefined = new Function;
var slice = Array.prototype.slice;
var prototyping;

// http://dean.edwards.name/weblog/2006/03/base/

Casco.Base = subclass.call(Object, {
  constructor: function() {
    if (arguments.length > 0) {
      this.extend(arguments[0]);
    }
  },

  base: Undefined, // Call this method from any other method to invoke the current method's ancestor (super)

  extend: function() {
    var args = slice.call(arguments);
    args.unshift(this);
    return extend.apply(null, args);
  }
}, Casco.Base = {
  ancestorOf: function(klass) {
    return ancestorOf(this, klass);
  },

  extend: function() {
    return subclass.apply(this, arguments);
  },

  forEach: function(object, block, context) {
    for (var key in object) {
      if (this.prototype[key] === undefined) {
        block.call(context, object[key], key, object);
      }
    }
  },

  implement: function(source) {
    if (ancestorOf(Casco.Base, source)) {
      source(this.prototype); // cast
    } else {
      // Add the interface using the extend() function.
      extend(this.prototype, source);
    }
    return this;
  }
});

function ancestorOf(ancestor, fn) {
  // Check if a function is in another function's inheritance chain.
  while (fn) {
    if (!fn.ancestor) return false;
    fn = fn.ancestor;
    if (fn == ancestor) return true;
  }
  return false;
};

function detect(_) {
  // Two types of detection:
  //  1. Object detection
  //    e.g. detect("(java)");
  //    e.g. detect("!(document.addEventListener)");
  //  2. Platform detection (browser sniffing)
  //    e.g. detect("MSIE");
  //    e.g. detect("MSIE|opera");

  var jscript = NaN/*@cc_on||@_jscript_version@*/; // http://dean.edwards.name/weblog/2007/03/sniff/#comment85164
  var java = global.java ? true : false;
  if (global.navigator) {
    var MSIE = /MSIE[\d.]+/g;
    var element = document.createElement("span");
    // Close up the space between name and version number.
    //  e.g. MSIE 6 -> MSIE6
    var userAgent = navigator.userAgent.replace(/([a-z])[\s\/](\d)/gi, "$1$2");
    // Fix opera's (and others) user agent string.
    if (!jscript) userAgent = userAgent.replace(MSIE, "");
    if (MSIE.test(userAgent)) userAgent = userAgent.match(MSIE)[0] + " " + userAgent.replace(MSIE, "");
    userAgent = navigator.platform + " " + userAgent;
    java &= navigator.javaEnabled();
  }

  detect = function(expression) {
    var r = false;
    var not = expression.charAt(0) == "!";
    if (not) expression = expression.slice(1);
    if (expression.charAt(0) == "(") {
      // Object detection.
      try {
        eval("r=!!" + expression);
      } catch (error) {
        // the test failed
      }
    } else {
      // Browser sniffing.
      r = new RegExp("(" + expression + ")", "i").test(userAgent);
    }
    return !!(not ^ r);
  };
  return detect(_);
};

function extend(object, source) { // or extend(object, key, value)
  if (object && source) {
    if (arguments.length > 2) { // Extending with a key/value pair.
      var key = source;
      source = {};
      source[key] = arguments[2];
    }
    var proto = (typeof source == "function" ? Function : Object).prototype;
    // Add constructor, toString etc
    var i = HIDDEN.length, key;
    if (prototyping) while (key = HIDDEN[--i]) {
      var value = source[key];
      if (value != proto[key]) {
        if (BASE.test(value)) {
          override(object, key, value)
        } else {
          object[key] = value;
        }
      }
    }
    // Copy each of the source object's properties to the target object.
    for (key in source) if (proto[key] === undefined) {
      var value = source[key];
      // Object detection.
      if (key.charAt(0) == "@") {
        if (detect(key.slice(1))) arguments.callee(object, value);
        continue;
      }
      // Check for method overriding.
      var ancestor = object[key];
      if (ancestor && typeof value == "function") {
        if (value != ancestor && !ancestorOf(value, ancestor)) {
          if (BASE.test(value)) {
            override(object, key, value);
          } else {
            value.ancestor = ancestor;
            object[key] = value;
          }
        }
      } else {
        object[key] = value;
      }
    }
  }
  return object;
};

function override(object, name, method) {
  // Override an existing method.
  var ancestor = object[name];
  var superObject = prototyping; // late binding for classes
  if (superObject && ancestor != superObject[name]) superObject = null;
  function _base() {
    var previous = this.base;
    this.base = superObject ? superObject[name] : ancestor;
    var returnValue = method.apply(this, arguments);
    this.base = previous;
    return returnValue;
  };
  _base.ancestor = ancestor;
  object[name] = _base;
};

function subclass(_instance, _static) {
  // Build the prototype.
  prototyping = this.prototype;
  var _prototype = new this;
  extend(_prototype, _instance);
  prototyping = null;

  // Create the wrapper for the constructor function.
  var _constructor = _prototype.constructor;
  function _class() {
    // Don't call the constructor function when prototyping.
    if (!prototyping) {
      if (this.constructor == arguments.callee || this[CONSTRUCTING]) {
        // Instantiation.
        this[CONSTRUCTING] = true;
        _constructor.apply(this, arguments);
        delete this[CONSTRUCTING];
      } else {
        // Casting.
        return extend(arguments[0], _prototype);
      }
    }
    return this;
  };
  _prototype.constructor = _class;

  // Build the static interface.
  for (var i in Casco.Base) _class[i] = this[i];
  _class.ancestor = this;
  _class.base = Undefined;
  _class.init = Undefined;
  extend(_class, _static);
  _class.prototype = _prototype;
  _class.init();

  return _class;
};

})(this);

