/**
 * Credox Framework [JavaScript], ver. 0.1 alpha / 29.11.2007
 *
 *
 * This JavaScript is part of Credox Framework [www.credox.org]
 * The script is freely distributable under the terms of an MIT license
 *
 *
 * Copyright (c) 2007 Hristo Drumev [www.hdrumev.com]
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 *
 * @author Hristo Drumev
 * @package framework
 * @license MIT license
 * @copyright Hristo Drumev [www.hdrumev.com]
 * @version 0.1 alpha / 29.11.2007
 *
 *
 * include:
 *   core.js           - Standard JS objects extensions - ver. 0.7 / 11.12.2007 alpha
 *   object.event.js   - Event [object] - ver. 0.7 / 05.10.2007 beta
 *   object.window.js  - Window [object] - ver. 0.2 / 13.07.2007 beta
 *   object.color.js   - Color [object] - ver. 0.2 / 17.08.2007 alpha
 *   object.cookie.js  - Cookie [object] - ver. 0.2 / 07.09.2007
 *   object.utils.js   - Utils [object] - ver. 0.1 / 05.10.2007 alpha
 *   patches.js        - Patches - ver. 0.1 / 17.09.2007
 *
 */


// file: core.js ---------------------------------------------------------------




var Namespace = function( name, namespace )
{
  if( !name )
    return window[name] || {};
  name = name.split( '.' );
  namespace = namespace || window;
  for( var i = 0, length = name.length; i < length; ++i )
  {
    if( typeof( namespace[name[i]] ) == 'undefined' )
      namespace[name[i]] = {};
    namespace = namespace[name[i]];
  }
  return namespace;
};

var Credox = new Namespace();



var Class = function( superclass, classprototype )
{
  var subclass = function()
  {
    if( typeof this.create == 'function' )
      this.create.apply( this, arguments );
  }
  if( superclass )
  {
    function ancestor() {};
    ancestor.prototype = superclass.prototype;
    subclass.prototype = new ancestor();
    subclass.prototype.constructor = this;
  }
  if( classprototype )
    for( var property in classprototype )
      subclass.prototype[property] = classprototype[property];
  return subclass;
}



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

Array.prototype.lastIndexOf = function( value )
{
  for( var i = this.length; --i; )
    if( this[i] == value )
      return i;
  return -1;
}

Array.prototype.contain = function( value )
{
  return this.indexOf( value ) > -1 ? true : false;
}

Array.prototype.each = function( callback )
{
  for( var i = 0, length = this.length; i < length; ++i )
    callback( this[i] );
  return this;
}

Array.prototype.random = function()
{
  return this[Math.floor( this.length * Math.random() )];
}



Function.prototype.bind = function()
{
  var object = this, args = arguments, target = arguments[0];
  return function()
  {
    return object.apply( target, args );
  }
}

Function.prototype.bindAsEvent = function( target )
{
  var object = this;
  return function( event )
  {
    return object.apply( target, event || window.event );
  }
}



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

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

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

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

String.prototype.stripHTMLTags = function()
{
  return this.replace( /<\/?[^>]+>/gi, '' );
}

String.prototype.capitalize = function()
{
  return this.charAt( 0 ).toUpperCase() + this.substring( 1 ).toLowerCase();
}



Number.prototype.toInt = function()
{
  return parseInt( this, 10 );
}

Number.prototype.toFloat = function()
{
  return parseFloat( this );
}

Number.prototype.toRange = function( min, max )
{
  return this < min ? min : ( this > max ? max : this );
}



Math.randomInt = function( min, max )
{
  return parseInt( min + Math.random()*( max - min ), 10 );
}

Math.randomFloat = function( min, max )
{
  return min + Math.random()*( max - min );
}


// file: object.event.js -------------------------------------------------------


var Event = {};


Event.KEY_BACKSPACE =   8;
Event.KEY_TAB       =   9;
Event.KEY_ENTER     =  13;
Event.KEY_PAUSE     =  19;
Event.KEY_ESC       =  27;
Event.KEY_PAGEUP    =  33;
Event.KEY_PAGEDOWN  =  34;
Event.KEY_END       =  35;
Event.KEY_HOME      =  36;
Event.KEY_LEFT      =  37;
Event.KEY_UP        =  38;
Event.KEY_RIGHT     =  39;
Event.KEY_DOWN      =  40;
Event.KEY_INSERT    =  45;
Event.KEY_DELETE    =  46;
Event.KEY_F1        = 112;
Event.KEY_F2        = 113;
Event.KEY_F3        = 114;
Event.KEY_F4        = 115;
Event.KEY_F5        = 116;
Event.KEY_F6        = 117;
Event.KEY_F7        = 118;
Event.KEY_F8        = 119;
Event.KEY_F9        = 120;
Event.KEY_F10       = 121;
Event.KEY_F11       = 122;
Event.KEY_F12       = 123;


Event.DOMLoaded = false;


Event.init = function( event )
{
  this.event = !event ? window.event : event;
  this.type = this.event.type;
  this.mouse = this.mouseXY( this.event );
  this.client = this.clientXY( this.event );
  this.element = this.getElement( this.event );
  this.relatedElement = this.getRelatedElement( this.event );
  this.shift = this.event.shiftKey;
  this.ctrl = this.event.ctrlKey;
  this.alt = this.event.altKey;
  this.left = this.event.which == null ? this.event.button == 1 : this.event.button == 0;
  this.middle = this.event.which == null ? this.event.button == 4 : this.event.which == 2;
  this.right = this.event.which == null ? this.event.button == 2 : this.event.which == 3;
  this.wheel = /(DOMMouseScroll|mousewheel)/.test( this.type )
               ? this.event.wheelDelta
                 ? this.event.wheelDelta / 120
                 : -( this.event.detail || 0 ) / 3
               : 0;
  this.keyCode = this.event.keyCode || this.event.which;
  return this;
}

Event._exec = function( event )
{
  event = event || window.event;
  for( var i = 0, len = this._eventList.length; i < len; i++ )
    if( this._eventList[i].type == event.type )
      this._eventList[i].fn.call( this, event );
}

Event._execDOMLoad = function( event )
{
  event = event || window.event;
  for( var i = 0, len = this._eventListDOMLoad.length; i < len; i++ )
    this._eventListDOMLoad[i].call( this, event );
}

Event.add = function( element, type, fn )
{
  if( element.addEventListener && !window.opera )
    element.addEventListener( type == 'mousewheel' ? 'DOMMouseScroll' : type, fn, false );
  else
    if( element['on' + type] )
      if( element._eventList )
      {
        for( var i = 0, len = element._eventList.length; i < len; i++ )
          if( element._eventList[i].type == type && element._eventList[i].fn === fn )
            return;
        element._eventList[element._eventList.length] = { type: type, fn: fn };
      }
      else
      {
        if( element['on' + type] === fn )
          return;
        element._eventList = new Array( { type: type, fn: element['on' + type] }, { type: type, fn: fn } );
        element['on' + type] = this._exec;
      }
    else
      element['on' + type] = fn;
}

Event.remove = function( element, type, fn )
{
  if( element.removeEventListener && !window.opera )
    element.removeEventListener( type == 'mousewheel' ? 'DOMMouseScroll' : type, fn, false );
  else
    if( element._eventList )
      for( var i = element._eventList.length, item = null; i--; )
      {
        if( ( item = element._eventList[i] ) && item.type == type && item.fn === fn )
          element._eventList[i] = null;
      }
    else
      element['on' + type] = null;
}

Event.getElement = function( event )
{
  event = event || window.event;
  var element = event.target || event.srcElement;
  return element.nodeType == 3 ? element.parentNode : element;
}

Event.getRelatedElement = function( event )
{
  event = event || window.event;
  if( event.type != 'mouseover' && event.type != 'mouseout' )
    return null;
  var element = event.relatedTarget ||
               ( event.type == 'mouseover' ? event.fromElement : event.toElement );
  return element && element.nodeType == 3 ? element.parentNode : element;
}

Event.mouseXY = function( event )
{
  event = event || window.event;
  if( /(click|mouse|menu)/.test( event.type ) )
    return { x: event.pageX || event.clientX + document.documentElement.scrollLeft,
             y: event.pageY || event.clientY + document.documentElement.scrollTop };
  else
    return { x: 0, y: 0 };
}

Event.clientXY = function( event )
{
  event = event || window.event;
  if( /(click|mouse|menu)/.test( event.type ) )
    return { x: event.pageX ? event.pageX - window.pageXOffset : event.clientX,
             y: event.pageY ? event.pageY - window.pageYOffset : event.clientY };
  else
    return { x: 0, y: 0 };
}

Event.onDOMLoad = function( fn )
{
  if( this.DOMLoaded )
    fn.call( this, event || window.event );
  if( window.attachEvent || /webkit|safari|khtml/i.test( navigator.userAgent ) )
  {
    if( !this._eventListDOMLoad )
      this._eventListDOMLoad = [];
    var len = this._eventListDOMLoad.length;
    for( var i = 0; i < len; i++ )
      if( this._eventListDOMLoad[i] === fn )
        return;
    this._eventListDOMLoad[len] = fn;
    if( len > 0 )
      return;
    if( window.attachEvent && !window.opera ) // MSIE
    {

      var _timer = window.setInterval(
        function()
        {
           try
           {
            document.firstChild.doScroll( 'left' );
            window.clearInterval( _timer );
            Event._execDOMLoad();
           }
           catch( e ) {}
        }, 50 );
    }
    else // Safari, Opera
    {
      var _timer = window.setInterval(
        function()
        {
          if( /loaded|complete/.test( document.readyState ) )
          {
            window.clearInterval( _timer );
            Event._execDOMLoad();
          }
        }, 50 );
    }
  }
  else if( document.addEventListener )
    this.add( document, 'DOMContentLoaded', fn );
  else
    this.add( window, 'load', fn );
}

Event.onDOMLoad( function() { Event.DOMLoaded = true; } );


// file: object.window.js ------------------------------------------------------


var Window = {};


Window.width = function()
{
  return window.innerWidth                    ||
         document.documentElement.clientWidth ||
         document.body.clientWidth;
}

Window.height = function()
{
  return window.innerHeight                    ||
         document.documentElement.clientHeight ||
         document.body.clientHeight;
}

Window.scrollWidth = function()
{
  return document.body.scrollWidth ||
         Math.max( document.documentElement.offsetWidth,
                   document.documentElement.scrollWidth );
}

Window.scrollHeight = function()
{
  return document.body.scrollHeight ||
         Math.max( document.documentElement.offsetHeight,
                   document.documentElement.scrollHeight );
}

Window.scrollLeft = function()
{
  return window.pageXOffset                  ||
         document.documentElement.scrollLeft ||
         document.body.scrollLeft            || 0;
}

Window.scrollTop = function()
{
  return window.pageYOffset                 ||
         document.documentElement.scrollTop ||
         document.body.scrollTop            || 0;
}


// file: object.color.js -------------------------------------------------------


var Color = {};


Color.byName = function( name )
{
  switch( name.toLowerCase() )
  {
    case 'aqua'    : return '#00ffff';
    case 'black'   : return '#000000';
    case 'blue'    : return '#0000ff';
    case 'fuchsia' : return '#ff00ff';
    case 'gray'    : return '#808080';
    case 'green'   : return '#008000';
    case 'lime'    : return '#00ff00';
    case 'maroon'  : return '#800000';
    case 'navy'    : return '#000080';
    case 'olive'   : return '#808000';
    case 'orange'  : return '#ffa500';
    case 'purple'  : return '#800080';
    case 'red'     : return '#ff0000';
    case 'silver'  : return '#c0c0c0';
    case 'teal'    : return '#008080';
    case 'white'   : return '#ffffff';
    case 'yellow'  : return '#ffff00';
    default        : return null;
  }
}

Color.isColor = function( value )
{
  return /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2});?$/i.test( value ) ||
         /^#([0-9a-f])([0-9a-f])([0-9a-f]);?$/i.test( value ) ||
         /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\);?$/.test( value ) ||
         /^rgb\(\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\);?$/.test( value ) ||
         /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*((?:\d+(?:\.\d+)?)|(?:\.\d+))\s*\);?$/.test( value ) ||
         /^rgba\(\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*,\s*((?:\d+(?:\.\d+)?)|(?:\.\d+))\s*\);?$/.test( value ) ||
         Color.byName( value ) !== null;
}

Color.parse = function( value )
{
  value = String( value ).toLowerCase();
  if( value.indexOf( '#' ) == 0 )
  {
    if( value.length == 7 )
      return value;
    var part = value.slice( 1, 4 ).split( '' );
    return '#' + part[0] + part[0] + part[1] + part[1] + part[2] + part[2];
  }
  else if( value.indexOf( 'rgb' ) >= 0 )
  {
    var part = value.replace( /^\s+(rgb|rgba)\s+\(|\)\s+$/, '' ).split( ',' );
    return Color.fromRGB( part[0], part[1], part[2] );
  }
  else
  {
    var color = Color.byName( value );
    if( color )
      return color;
  }
  return value;
}

Color.toInteger = function( value )
{
  return parseInt( '0x' + Color.parse( value ).replace( '#', '' ) );
}

Color.toRGB = function( value )
{
  value = Color.toInteger( value );
  return { red  : ( value & 0xff0000 ) >>> 16,
           green: ( value & 0x00ff00 ) >>>  8,
           blue : ( value & 0x0000ff ) };
}

Color.fromRGB = function( red, green, blue )
{
  if( /%/g.test( red ) )
  {
    red *= 2.55;
    green *= 2.55;
    blue *= 2.55;
  }
  return '#' + (                 1 << 24 |
                 parseInt( red   ) << 16 |
                 parseInt( green ) <<  8 |
                 parseInt( blue  )       ).toString( 16 ).substr( 1 );
}


// file: object.cookie.js ------------------------------------------------------


var Cookie = {};


Cookie.get = function( name )
{
  return ( !document.cookie.length || ( str = document.cookie.indexOf( name + '=' ) ) == -1 ) ? null : document.cookie.substring( ( str += ( name + '=' ).length ), ( result = document.cookie.indexOf( ';', str ) ) == -1 ? document.cookie.length : result );
}

Cookie.set = function( name, value, expires, path, domain, secure )
{
  if( expires )
  {
    var date = new Date();
    expires = date.setTime( date.getTime() + ( expires * 86400000 ) ); // 24*60*60*1000 (ms. in 1 day)
  }
  document.cookie += name + '=' + value + ( expires ? '; expires=' + expires.toGMTString() : '' ) + '; path=' + ( path ? path :'/' ) + ( domain ? '; domain=' + domain : '' ) + ( secure ? ';secure' : '' );
}

Cookie.unset = function( name )
{
  if( this.isSet() )
    this.save( name, '', -1 );
}

Cookie.isSet = function( name )
{
  return !!( document.cookie.indexOf( name + '=' ) + 1 );
}


// file: object.utils.js -------------------------------------------------------


var Utils = {}; // namespace


Utils.include = function( src, type, callback )
{
  if( !type )
  {
    if( /.js/i.test( src ) )
      type = 'text/javascript';
    if( /.css/i.test( src ) )
      type = 'text/css';
  }
  var file;
  if( type == 'text/javascript' )
  {
    file = document.createElement( 'script' );
    file.setAttribute( 'src', src );
  }
  else if( type == 'text/css' )
  {
    file = document.createElement( 'link' );
    file.setAttribute( 'rel', 'stylesheet' );
    file.setAttribute( 'href', src );
  }
  else
    return;
  file.setAttribute( 'type', type );
  var head = document.getElementsByTagName( 'head' )[0];
  head.appendChild( file );
  if( typeof( callback ) == 'function' )
  {
    if( /WebKit|Safari|Khtml/i.test( navigator.userAgent ) && navigator.userAgent.indexOf( 'Version/2' ) >= 0 ) // Safari 2
    {
      var iframe = document.createElement( 'iframe' );
      iframe.style.display = 'none';
      iframe.setAttribute( 'src', file );
      head.appendChild( iframe );
      iframe.onload = callback();
    }
    else
    {
      if( document.all )
        file.onreadystatechange = function() // IE
        {
          if( /complete|loaded/.test( file.readyState ) )
            callback();
        }
      else
        file.onload = callback; // FF
    }
  }
}

Utils.clone = function( src )
{
  var type = String( src.constructor ).match( /\s*function (.*)\(/ )[1];
  if( type == 'Object' )
  {
    var instance = function() {};
    instance.prototype = src;
    var object = new instance();
    for( property in object )
      if( typeof( object[property] ) == 'object' )
        object[property] = object[property].clone();
    return object;
  }
  if( type == 'Array' )
    return this.slice( 0 );
  if( type == 'Date' )
    return new Date( src.getTime() );
  // type is Boolean, Function, Number, RegExp, String
  return src.valueOf ? src.valueOf() : src;
}

Utils.extend = function()
{
  var target = arguments[0];
  for( var i = 1, length = arguments.length; i < length; ++i )
  {
    var source = arguments[i];
    if( source && target.prototype && source.prototype )
      target.prototype.extend( source.prototype )
    else
      for( var property in source )
        target[property] = source[property];
  }
  return target;
}


// file: patches.js ------------------------------------------------------------


if( /^html$/.test( document.documentElement.tagName ) ) // is XHTML
  document.writeln = document.write = function( xhtml )
  {
    if ( xhtml.match( /^<\// ) )
      return;
    if ( !window.opera )
      xhtml = xhtml.replace( /&(?![#a-z0-9]+;)/g, "&" );
    xhtml = xhtml.replace( /<([a-z]+)(.*[^\/])>$/, "<$1$2></$1>");
    var XMLNS = 'http:\/\/www.w3.org\/1999\/xhtml';
    if ( window.opera || /Apple/.test( navigator.vendor ) )
      xhtml = xhtml.replace( /(<[a-z]+)/g, "$1 xmlns='" + XMLNS + "'" );
    var element = document;
    while( element.lastChild && element.lastChild.nodeType == 1 ) // ELEMENT_NODE
      element = element.lastChild;
    element = element.parentNode;
    var parser = new DOMParser();
    var XMLdoc = parser.parseFromString( '<div xmlns="' + XMLNS + '">' + xhtml + '</div>', 'application/xhtml+xml' );
    var nodes = XMLdoc.documentElement.childNodes;
    for( var i = 0, len = nodes.length; i < len; i++)
      element.appendChild( document.importNode( nodes[i], true ) );
  };

if( window.attachEvent && !document.epando ) // only for IE6-
  try
  {
    document.execCommand( 'BackgroundImageCache', false, true );
  }
  catch( err ) {}



