/**
 * Cookies objects manager.
 *
 * Classed used to prevent situations when couple developers would overwrite
 *  their cookie settings, when same Cookie is created more then once per page.
 *
 * @author Marek Snopkowski <marek@kramsoft.eu>
 * @version 2010-01-16
 */
var CookieJar = {
 cookies: {},
 defaultName: 'xc',


 /**
  * Get instance of cookie object.
  *
  * @param {String} name Name of cookie we want to fetch/create.
  * @return Cookie object either new one or previously created (in that case options and data arguments are omitted).
  * @type Cookie
  */
 get: function(name, options, data){
  name = name || this.defaultName;
  if (Object.isString(name) && this.cookies[name])
   return this.cookies[name];

  return this.cookies[name] = new Cookie(name, options, data);
 }
};

/**
 * Cookie wrapper.
 *
 * Classed used to prevent situations when couple developers would overwrite
 *  their cookie settings, when same Cookie is created more then once per page.
 *
 * Cookie wrapper was based on Jason McCreary's Cookie object.
 * Read more on: http://jason.pureconcepts.net/articles/javascript_cookie_object
 *
 * @author Marek Snopkowski <marek@kramsoft.eu>
 * @version 2010-01-16
 */
var Cookie = Class.create({
 cookies: [],

 name: null,
 data: {},
 options: {
  expires: null,
  domain: "",
  path: "",
  secure: false
 },


 /**
  * Cookie object c-tor.
  *
  * @constructor
  * @param {String} name Cookie's name.
  * @param {Object} options [optional] Cookie's object options (includes expires, domain, path and secure).
  * @param {Object} data [optional] Cookie's object initial data.
  */
 initialize: function(name, options, data) {
  if(!Object.isString(name) || name.blank())
   throw('Cookie object requires valid, non-empty name.');

  this.name    = name;
  this.options = Object.extend(this.options, options || {});

  var payload = this.retrieve();
  this.data = payload && payload.isJSON() ? payload.evalJSON() : (data || {});
  this._save();
 },


 /**
  * Get object's data variable.
  *
  * @return data cookie's private field.
  * @type Array
  */
 getAll: function() {
  return this.data;
 },


 /**
  * Get object's data.
  *
  * @param {String} name Cookie's parameter name.
  * @param {mixed} defaultValue [optional] Cookie's default parameter's value.
  * @return Cookie's parameter value.
  * @type mixed
  */
 get: function(name, defaultValue) {
  if('undefined'==typeof defaultValue)defaultValue=null;
  return this.has(name) ? this.data[name] : defaultValue;
 },


 /**
  * Set object's data.
  *
  * Set method triggers private _save method.
  *
  * @see #_save
  *
  * @param {String} name Cookie's parameter name.
  * @param {mixed} value Value of Cookie's parameter.
  * @return Cookie object.
  * @type Cookie
  */
 set: function(name, value) {
  this.data[name] = value || null;
  this._save();

  return this;
 },


 /**
  * Unset object's data.
  *
  * Unset method triggers private _save method.
  *
  * @see #_save
  *
  * @param {String} name Cookie's parameter name.
  * @return Cookie object.
  * @type Cookie
  */
 unset: function(name) {
  delete this.data[name];
  this._save();

  return this;
 },


 /**
  * Check if Cookie object's has particular parameter.
  *
  * @param {String} name Cookie's parameter name.
  * @return Indicator if parameter exists in object's data.
  * @type boolean
  */
 has: function(name) {
  return 'undefined'!=typeof this.data[name];
 },


 /**
  * Fetch related cookie data from webbrowser.
  *
  * @return Cookie data in raw format.
  * @type String
  */
 retrieve: function() {
  var start = document.cookie.indexOf(this.name + "=");

  if(start == -1) {
   return null;
  }
  if(this.name != document.cookie.substr(start, this.name.length)) {
   return null;
  }

  var len = start + this.name.length + 1;
  var end = document.cookie.indexOf(';', len);

  if(end == -1) {
   end = document.cookie.length;
  }

  return unescape(document.cookie.substring(len, end));
 },


 /**
  * Erase related cookie.
  */
 erase: function() {
  document.cookie = this.name + '=' + this._options() + ';expires=Thu, 01-Jan-1970 00:00:01 GMT';
 },


 /**
  * Save object's data in cookie.
  *
  * This method is private since each operation changing object's data is automatically saved.
  *
  * @private
  * @return Cookie object.
  * @type Cookie
  */
 _save: function() {
  var expires = '';

  if (this.options.expires) {
   var today = new Date();
   expires = this.options.expires * 86400000;
   expires = ';expires=' + new Date(today.getTime() + expires);
  }

  document.cookie = this.name + '=' + escape(Object.toJSON(this.data)) + this._options() + expires;

  return this;
 },


 /**
  * Fetch object's options (besides expires option).
  *
  * Method is used only in order to clear related with object cookie.
  *
  * @private
  * @return Options in JS format.
  * @type String
  */
 _options: function() {
  return (this.options.path ? ';path=' + this.options.path : '')
    + (this.options.domain ? ';domain=' + this.options.domain : '')
    + (this.options.secure ? ';secure' : '');
 }
});
