

var CACHE_VER  = Math.floor(Math.random()*99999999+1); // NO CACHE


// ------------------------------------------------------------------------------------------------
// Common Functions
// ------------------------------------------------------------------------------------------------

if (typeof(console) == "undefined") {
  console = { log: function() { }, dir: function() { } };
}

function noop() { }

function hasAncestorWithClass(n, classes) {
  
  if (typeof(classes) == "string") {
    classes = [classes];
  }
  
  var test = function(n) {

    for (var i = 0; i < classes.length; ++i) {
      // console.log(n.get("className"));
      if (n.hasClass(classes[i])) { return n; }
    }

    return false;

  };

  return (test(n) || n.ancestor(test));

};

function clone(o) {
  var newObj = (o instanceof Array) ? [] : {};
  for (i in o) {
    if (i == 'clone') continue;
    if (o[i] && typeof o[i] == "object") {
      newObj[i] = clone(o[i]);
    } else newObj[i] = o[i];
  } return newObj;
};


// ------------------------------------------------------------------------------------------------
// Base Class Extensions
// ------------------------------------------------------------------------------------------------

String.prototype.trim =  function() {
  return( this.replace(/^\s+|\s+$/, '') );
};

String.prototype.endsWith = function(sEnd) {
  return( this.substr(this.length-sEnd.length) == sEnd );
};

String.prototype.startsWith = function(sStart) {
  return( this.substr(0,sStart.length) == sStart );
};

Array.prototype.remove = function(from, to) {
  this.splice(from,
    !to ||
    1 + to - from + (!(to < 0 ^ from >= 0) && (to < 0 || -1) * this.length));
  return this.length;
};

Array.prototype.removeAt = function(at) {
  this.remove(at, at);  
};

Array.prototype.find = function(item) {
  for (var i = 0; i < this.length; ++i) {
    if (this[i] == item) { return i; }
  }
  return -1;
};


// ------------------------------------------------------------------------------------------------
// Taken from DateJS
// ------------------------------------------------------------------------------------------------

Date.isLeapYear = function (y) { 
    return (((y % 4 === 0) && (y % 100 !== 0)) || (y % 400 === 0)); 
};

Date.getDaysInMonth = function (year, month) {
    return [31, (Date.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
};

Date.prototype.isLeapYear = function () { 
    return (Date.isLeapYear(this.getFullYear())); 
};

Date.prototype.isWeekday = function () { 
    return !(this.is().sat() || this.is().sun());
};

Date.prototype.getDaysInMonth = function () { 
    return Date.getDaysInMonth(this.getFullYear(), this.getMonth());
};

Date.prototype.moveToFirstDayOfMonth = function () {
    return this.set({ day: 1 });
};

Date.prototype.moveToLastDayOfMonth = function () { 
    return this.set({ day: this.getDaysInMonth()});
};

Date.prototype.addDays = function (value) { 
    return this.addMilliseconds(value * 86400000); /* 60*60*24*1000 */
};

Date.prototype.addMilliseconds = function (value) {
    this.setMilliseconds(this.getMilliseconds() + value);
    return this;
};

Date.prototype.addSeconds = function (value) { 
    return this.addMilliseconds(value * 1000); 
};

Date.prototype.addMinutes = function (value) { 
    return this.addMilliseconds(value * 60000); /* 60*1000 */
};

Date.prototype.addHours = function (value) { 
    return this.addMilliseconds(value * 3600000); /* 60*60*1000 */
};

Date.prototype.addDays = function (value) { 
    return this.addMilliseconds(value * 86400000); /* 60*60*24*1000 */
};

Date.prototype.addWeeks = function (value) { 
    return this.addMilliseconds(value * 604800000); /* 60*60*24*7*1000 */
};

Date.prototype.addMonths = function (value) {
    var n = this.getDate();
    this.setDate(1);
    this.setMonth(this.getMonth() + value);
    this.setDate(Math.min(n, this.getDaysInMonth()));
    return this;
};

Date.prototype.addYears = function (value) {
    return this.addMonths(value * 12);
};

Date._validate = function (value, min, max, name) {
    if (typeof value != "number") {
      return;
      throw new TypeError(value + " is not a Number."); 
    } else if (value < min || value > max) {
      return;
      throw new RangeError(value + " is not a valid value for " + name + "."); 
    }
    return true;
};

Date.validateMillisecond = function (n) {
    return Date._validate(n, 0, 999, "milliseconds");
};

Date.validateSecond = function (n) {
    return Date._validate(n, 0, 59, "seconds");
};

Date.validateMinute = function (n) {
    return Date._validate(n, 0, 59, "minutes");
};

Date.validateHour = function (n) {
    return Date._validate(n, 0, 23, "hours");
};

Date.validateDay = function (n, year, month) {
    return Date._validate(n, 1, Date.getDaysInMonth(year, month), "days");
};

Date.validateMonth = function (n) {
    return Date._validate(n, 0, 11, "months");
};

Date.validateYear = function (n) {
    return Date._validate(n, 1, 9999, "seconds");
};

Date.prototype.set = function (config) {
    var x = config;

    if (!x.millisecond && x.millisecond !== 0) { 
        x.millisecond = -1; 
    }
    if (!x.second && x.second !== 0) { 
        x.second = -1; 
    }
    if (!x.minute && x.minute !== 0) { 
        x.minute = -1; 
    }
    if (!x.hour && x.hour !== 0) { 
        x.hour = -1; 
    }
    if (!x.day && x.day !== 0) { 
        x.day = -1; 
    }
    if (!x.month && x.month !== 0) { 
        x.month = -1; 
    }
    if (!x.year && x.year !== 0) { 
        x.year = -1; 
    }

    if (x.millisecond != -1 && Date.validateMillisecond(x.millisecond)) {
        this.addMilliseconds(x.millisecond - this.getMilliseconds()); 
    }
    if (x.second != -1 && Date.validateSecond(x.second)) {
        this.addSeconds(x.second - this.getSeconds()); 
    }
    if (x.minute != -1 && Date.validateMinute(x.minute)) {
        this.addMinutes(x.minute - this.getMinutes()); 
    }
    if (x.hour != -1 && Date.validateHour(x.hour)) {
        this.addHours(x.hour - this.getHours()); 
    }
    if (x.month !== -1 && Date.validateMonth(x.month)) {
        this.addMonths(x.month - this.getMonth()); 
    }
    if (x.year != -1 && Date.validateYear(x.year)) {
        this.addYears(x.year - this.getFullYear()); 
    }
    
	/* day has to go last because you can't validate the day without first knowing the month */
    if (x.day != -1 && Date.validateDay(x.day, this.getFullYear(), this.getMonth())) {
        this.addDays(x.day - this.getDate()); 
    }
    if (x.timezone) { 
        this.setTimezone(x.timezone); 
    }
    if (x.timezoneOffset) { 
        this.setTimezoneOffset(x.timezoneOffset); 
    }
    
    return this;   
};


// ------------------------------------------------------------------------------------------------
// OO
// ------------------------------------------------------------------------------------------------

var Singleton = {

  create: function(protoDef) {
  
    var c = function() { };

    c.prototype = protoDef;
    
    // ready
    
    var r = new c;
    
    if (r.ready) { r.ready(); }

    return (r);
  
  }

};

var Class = {

  create: function(protoDef) {

    var c;
 
    // create object based on constructor method 

    if (protoDef.initialize) {
      c = function() {
        this.initialize.apply(this, arguments);
      };
    } else {
      c = function() { };
    }

    // assign prototype 

    c.prototype = protoDef;

    // allow extension / specialization

    c.extend = function(protoDef) { // create a new class based on this one
      return Class.extend(this, protoDef);
    };
    
    c.specialize = function(protoDef, _parent) { // create a new class based on this one
      var sp = Class.specialize(this, protoDef);
      sp._parent = _parent;
      return sp;
    };

    // standard functionality
    
    c.prototype.loadProperties = function(defaults, user) {

      this.properties = defaults;
      for (key in user) {
        this.properties[key] = user[key];
      }

    };

    return c;

  },

  extend: function(base, protoDef) {

    var c = Class.create(protoDef); // start with our derived methods

    for (property in base.prototype) {
      if (!c.prototype[property]) { // if it's a function, and there isn't a derived version
        c.prototype[property] = base.prototype[property];
      }
    }

    return c;

  }, 
  
  specialize: function(base, protoDef) {

    var Cx = Class.extend(base, protoDef); 
    return new Cx();
    
  }


};


