/*!
 * Denbel object
 * @version: 1.0
 * @author: M.F.Endenburg
 * @copyright (c) Denbel Systems, 2007
 * www.denbel.nl
 */

var undefined;

/**
 * Denbel object
 * @var object
 */
var Denbel =
{
	/**
	 * Loads a Denbel namespace
	 * @return object
	 */
	load: function()
	{
    var a = arguments, o = null, i, j, d;
    
    for( i = 0; i < a.length; i++ )
    {
      d = a[i].split( '.' );
      o = Denbel;

      // Denbel is implied, so it is ignored if it is included
      for( j = ( d[0] == 'Denbel' ) ? 1 : 0; j < d.length; j++ )
      {
        o[d[j]] = o[d[j]] || {};
        o = o[d[j]];
      }
    }
    return o;
	},
	
    /**
     * Converts this object to its string representation
     * @return string
     */
	toString: function()
	{
    return 'Denbel';
	}
};

// load namespace
Denbel.load( 'lang.StringExtender' );

/**
 * constructor
 * @return void
 */
Denbel.lang.StringExtender = function(){};

// prototype
Denbel.lang.StringExtender.prototype =
{
  /**
   * Encodes the string to UTF-8
   * @return string
   */
  utf8Encode: function()
  {
    var value = this;
    value.replace( /\r\n/g, "\n" );
    var utftext = '';
    var c;
    
    for( var n = 0; n < value.length; n++ )
    {
      c = value.charCodeAt( n );
      
      if( c < 128 )
      {
        utftext += String.fromCharCode( c );
      }
      else if( ( c > 127 ) && ( c < 2048 ) )
      {
        utftext += String.fromCharCode( ( c >> 6 ) | 192 );
        utftext += String.fromCharCode( ( c & 63 ) | 128 );
      }
      else
      {
        utftext += String.fromCharCode( ( c >> 12 ) | 224 );
        utftext += String.fromCharCode( ( ( c >> 6 ) & 63 ) | 128 );
        utftext += String.fromCharCode( ( c & 63 ) | 128 );
      }
    }
    
    return utftext;
  },
  
  /**
   * Decodes the string from UTF-8
   * @return string
   */
  utf8Decode: function()
  {
    var value = this;
    var str = '';
    var i = 0;
    var c = c1 = c2 = 0;
    
    while( i < value.length )
    {
      c = value.charCodeAt( i );
      
      if( c < 128 )
      {
        str += String.fromCharCode( c );
      }
      else if( ( c > 191 ) && ( c < 224 ) )
      {
        c2 = value.charCodeAt( i + 1 );
        str += String.fromCharCode( ( ( c & 31 ) << 6 ) | ( c2 & 63 ) );
        i += 2;
      }
      else
      {
        c2 = value.charCodeAt( i + 1 );
        c3 = value.charCodeAt( i + 2 );
        str += String.fromCharCode( ( ( c & 15 ) << 12 ) | ( ( c2 & 63 ) << 6 ) | ( c3 & 63 ) );
        i += 3;
      }
    }
    
    return str;
  },
    
  /**
   * Returns a value indicating this string starts with given value
   * @param string value
   * @return bool
   */
  startsWith: function( value )
  {
    return ( this.substring( 0, value.length ) == value );
  },
  
  /**
   * Returns a value indicating this string ends with given value
   * @param string value
   * @return bool
   */
  endsWith: function( value )
  {
    return ( this.substring( ( this.length - value.length ) ) == value );
  },
  
  /**
   * Removes any leading or trailing spaces or white space characters
   * @return String
   */
  trim: function()
  {
    var value = this;
    
    value = value.replace( /^\s+/, '' );
    value = value.replace( /\s+$/, '' );
    
    return value;
  }
};

// add StringExtender to String class
YAHOO.lang.augmentProto( String, Denbel.lang.StringExtender );

// load lang.Date namespace
Denbel.load( 'lang.Date' );

/**
 * Static Date class with utilities
 */
Denbel.lang.Date =
{
  /**
   * A string to Date function
   * @param string s date string like yyyy-MM-dd hh:mm:ss
   * @return Date
   */
  stringToDate: function( s )
  {
    if( !s || s.length == 0 )
    {
      return null;
    }
    
    // separate the date from the time
    var parts = s.split( ' ' );
    
    var sep = null;
    
    if( parts[0].indexOf( '-' ) > -1 )
    {
      sep = '-';
    }
    else if( parts[0].indexOf( '/' ) > -1 )
    {
      sep = '/';
    }
    else
    {
      return null;
    }
    
    var i = 0;
    var dateParts = parts[0].split( sep );
    var d = new Date();
    
    d.setFullYear( dateParts[0] );
    d.setMonth( dateParts[1] - 1 );
    d.setDate( dateParts[2] );
    
    if( parts.length > 1 )
    {
      sep = ':';
      
      var timeParts = parts[1].split( sep );
      
      d.setHours( timeParts[0] );
      d.setMinutes( timeParts[1] );
      d.setSeconds( timeParts[2] );
    }
    
    return d;
  },
  
  /**
   * Formats a given Date object to string
   * @param string s PHP-like format (Y = year, m = month, d = date, H = hours, i = minutes, s = seconds)
   * @param Date d
   * @return string
   */
  format: function( str, date )
  {
    try
    {
      var m = ( date.getMonth() + 1 );
      m = ( m.toString().length == 2 ) ? m : '0' + m;
      
      var Y = date.getFullYear();
      var d = ( date.getDate().toString().length == 2 ) ? date.getDate().toString() : '0' + date.getDate().toString();
      var H = ( date.getHours().toString().length == 2 ) ? date.getHours().toString() : '0' + date.getHours().toString();
      var i = ( date.getMinutes().toString().length == 2 ) ? date.getMinutes().toString() : '0' + date.getMinutes().toString();
      var s = ( date.getSeconds().toString().length == 2 ) ? date.getSeconds().toString() : '0' + date.getSeconds().toString();
      
      return Denbel.util.replaceAll(
        ['Y', 'm', 'd', 'H', 'i', 's'],
        [Y, m, d, H, i, s],
        str
      );
    }
    catch( ex )
    {
      return s;
    }
  },
  
  /**
   * Returns a string representing the month
   * @param Date date
   * @return String
   */
  getMonth: function( date )
  {
    if( !R )
    {
      return null;
    }
    
    var months = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'];
    return R.get( 'MONTH_' + months[date.getMonth()].toUpperCase() );
  },
  
  /**
   * Returns a string representing the day
   * @param Date date
   * @return String
   */
  getDay: function( date )
  {
    if( !R )
    {
      return null;
    }
    
    var days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
    return R.get( 'DAY_' + days[date.getDay()].toUpperCase() );
  },
  
  /**
   * Converts a Date object to a string
   * @param Date d
   * @return string in the manner: yyyy-MM-dd hh:mm:ss
   */
  dateToString: function( d )
  {
    return d.getFullYear().toString() + '-' + ( d.getMonth() + 1 ) + '-' + d.getDate() + ' ' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds();
  }
}

// load Denbel.lang.Array
Denbel.load( 'lang.Array' );

Denbel.lang.Array.prototype =
{
    /**
     * Returns a value indicating the specified value exists in this Array instance
     * @param variant value
     * @return bool
     */
    contains: function( value )
    {
      return Denbel.util.inArray( value, this, false );
    },
    
    /**
     * Returns the index of the given value if it exists in this Array instance
     * @param variant value
     * @return int
     */
    indexOf: function( value )
    {
      return Denbel.util.inArray( value, this, true );
    },
    
    /**
     * Converts this object to a string representation
     * @return string
     */
    asString: function()
    {
      return 'Array[' + this.length + ']';
    }
};

// add Denbel.lang.Array to Array class
YAHOO.lang.augmentProto( Array, Denbel.lang.Array );

// load util.inArray
Denbel.load( 'util.inArray' );

/**
 * checks if needle is in the haystack array
 * @param variant needle
 * @param Array haystack
 * @param bool set to true to make the return value the index of the needle if found
 * @return bool
 */
Denbel.util.inArray = function( needle, haystack, returnIndex )
{
  if( !haystack || !YAHOO.lang.isArray( haystack ) || haystack.length == 0 )
  {
    return null;
  }
  
  var found = false;
  var index = null;
  
  for( var i = 0; i < haystack.length; i++ )
  {
    if( haystack[i] == needle )
    {
      found = true;
      index = i;
      break;
    }
  }
  
  if( returnIndex && found )
  {
    return index;
  }
  else
  {
    return found;
  }
}

// load util.isVisible
Denbel.load( 'util.isVisible' );

/**
 * Gets a value indicating the given element is visible
 * @param Variant ID string or HTMLElement object
 * @return Boolean
 */
Denbel.util.isVisible = function( element )
{
  if( !element )
  {
    return false;
  }
  
  if( !YAHOO.util.Dom.inDocument( element ) )
  {
    return false;
  }
  
  if( YAHOO.lang.isString( element ) )
  {
    element = YAHOO.util.Dom.get( element );
  }
  
  if( !element )
  {
    return false;
  }
  
  if( YAHOO.util.Dom.getRegion( element ).top == false || YAHOO.util.Dom.getStyle( element, 'display' ) == 'none' || YAHOO.util.Dom.getStyle( element, 'visibility' ) == 'hidden' )
  {
    return false;
  }
  
  var reg = YAHOO.util.Dom.getRegion( element );
  
  if( !reg || ( reg.height == 0 && reg.width == 0 ) )
  {
    return false;
  }
  
  return true;
};

// load util.getHeight
Denbel.load( 'util.getHeight' );

/**
 * Gets the height of an element
 * @param mixed string ID or HTMLElement object
 * @return int height in pixels
 */
Denbel.util.getHeight = function( element )
{
  if( !element )
  {
    return null;
  }
  
  if( YAHOO.lang.isString( element ) )
  {
    element = YAHOO.util.Dom.get( element );
  }
  
  if( !element )
  {
    return null;
  }
  
  return element.offsetHeight;
}

// load util.replaceAll
Denbel.load( 'util.replaceAll' );

/**
 * Replaces all occurances in a string
 * @param Variant search
 * @param Variant replace
 * @param string subject
 * @return string
 */
Denbel.util.replaceAll = function( search, replace, subject )
{
  if( !YAHOO.lang.isString( subject ) )
  {
    YAHOO.log( 'replaceAll expects the argument subject as a string', 'error', 'Denbel.util.replaceAll' );
    return subject;
  }

  if( YAHOO.lang.isArray( search ) && YAHOO.lang.isArray( replace ) )
  {
    var max = 0;
    
    if( replace.length <= search.length )
    {
      max = replace.length;
    }
    else
    {
      max = search.length;
    }
    
    for( var i = 0; i < max; i++ )
    {
      subject = Denbel.util.replaceAll( search[i], replace[i], subject );
    }
  }
  else if( YAHOO.lang.isArray( search ) )
  {
    for( var i = 0; i < search.length; i++ )
    {
      subject = Denbel.util.replaceAll( search[i], replace, subject );
    }
  }
  else
  {
    while( subject.indexOf( search ) > -1 )
    {
      subject = subject.replace( search, replace );
    }
  }
  
  return subject;
}

// load util.stripTags
Denbel.load( 'util.stripTags' );

/**
 * Strips all HTML code from a string
 * @param string s
 * @return string
 */
Denbel.util.stripTags = function( s )
{
  if( !s )
  {
    s = '';
  }
  
  return s.replace( /<\/?[^>]+>/gi, '' );
}

// load util.htmlEntities
Denbel.load( 'util.htmlEntities' );

/**
 * To HTML entities
 * @param String s
 * @return String
 */
Denbel.util.htmlEntities = function( s )
{
  return Denbel.util.replaceAll( ['&', '"', '>', '<'], ['&amp;', '&quot;', '&gt;', '&lt;'], s );
}

// load util.unHtmlEntities
Denbel.load( 'util.unHtmlEntities' );

/**
 * Unescape HTML entities
 * @param String s
 * @return String
 */
Denbel.util.unHtmlEntities = function( s )
{
  return Denbel.util.replaceAll( ['&amp;', '&quot;', '&gt;', '&lt;', '&nbsp;'], ['&', '"', '>', '<', ' '], s );
}

// load util.round namespace
Denbel.load( 'util.round' );

/**
 * Apply a substitute function for the Math.round function that takes an extra argument
 * @param Number n input number
 * @param int d the number of decimal places to round to
 * @return Number
 */
Denbel.util.round = function( n, d )
{
  if( !YAHOO.lang.isNumber( n ) )
  {
    n = parseFloat( n );
    
    if( !YAHOO.lang.isNumber( n ) )
    {
      YAHOO.log( 'Input number not numeric.', 'error', 'Denbel.util.round' );
      return 'NaN';
    }
  }
  
  if( !d || d == 0 )
  {
    n = Math.round( n );
  }
  else
  {
    n = n.toFixed( d );
  }
  
  return n;
}

// load util.Rand namespace
Denbel.load( 'util.Rand' );

/**
 * Generate random number
 * @param int min
 * @param int max
 * @return int
 */
Denbel.util.Rand = function( min, max )
{
  if( min == null || max == null )
  {
    return ( Math.random() * 10000 );
  }
  
  return Math.floor( ( ( max - ( min - 1 ) ) * Math.random() ) + min );
};

// load util.formatNumber namespace
Denbel.load( 'util.formatNumber' );

/**
 * Formats a number to a human readable string
 * @param Number n the input number
 * @param int d the number of decimal places. The input number will be rounded when needed. (defaults to zero)
 * @param string ds the decimal separator (defaults to a dot '.')
 * @param string ts the thousands separator (defaults to a comma ',')
 * @return string the formatted number or NaN if input was not a numeric value
 */
Denbel.util.formatNumber = function( n, d, ds, ts )
{
  if( !YAHOO.lang.isNumber( n ) )
  {
    n = parseFloat( n );
    
    if( !YAHOO.lang.isNumber( n ) )
    {
      YAHOO.log( 'Input number not numeric.', 'error', 'Denbel.util.formatNumber' );
      return 'NaN';
    }
  }
  
  if( !d )
  {
    d = 0;
  }
  
  if( !ds )
  {
    ds = '.';
  }
  
  if( !ts )
  {
    ts = ',';
  }
  
  n = n.toString(); // make the number a string
  var pos = n.indexOf( '.' ); // position of the first dot
  var i = 0; // iterator
  
  if( pos == -1 )
  {
    // there were no decimals in the input number
    
    if( d > 0 )
    {
      // there are decimals requested in the output, so we need to fill them with zeros
      n += '.'; // add a dot to begin with
      
      // and add the number of decimals requested
      for( i = 0; i < d; i++ )
      {
        n += '0';
      }
    }
  }
  else
  {
    // there were decimals in the input number
    
    // get the number of decimals
    var decimals = n.substring( ( pos + 1 ) ).length;
    
    if( decimals > d )
    {
      // the number of decimals in the input are more than we want in the output
      n = Denbel.util.round( n, d ).toString();
    }
    else if( decimals < d )
    {
      // the number of decimals in the input are less than we want in the output
      
      // we need to append the missing decimals with zeros
      for( i = decimals; i < d; i++ )
      {
        n += '0';
      }
    }
  }
  
  // return the (modified) input
  return n;
}

// load util.isA
Denbel.load( 'util.isA' );

/**
 * Tests if a given object is of a certain type
 * @param object the object to test
 * @param string the object type to test for
 * @return bool
 */
Denbel.util.isA = function( obj, type )
{
  if( !YAHOO.lang.isString( type ) || !YAHOO.lang.isObject( obj ) )
  {
    YAHOO.log( 'isA( x, y ): invalid data type for argument', 'error' );
    return false;
  }
  
  var r = false;
  
  try
  {
    switch( Denbel.util.BrowserDetect.browser )
    {
      case 'Firefox':
          r = ( ( obj.constructor.toString().toLowerCase() == '[' + type.toLowerCase() + ']' ) ? true : false );
          break;
      
      case 'Explorer':
          if( obj.tagName )
          {
              var objType = 'HTML' + obj.tagName + 'Element';
              r = ( ( objType.toLowerCase() == type.toLowerCase() ) ? true : false );
          }
          else
          {
              r = ( ( obj.toString().toLowerCase() == type.toLowerCase() ) ? true : false );
          }
          break;
      
      case 'Safari':
          r = ( ( obj.toString().toLowerCase() == '[object ' + type.toLowerCase() + ']' ) ? true : false );
          break;
      
      default:
          r = false;
          break;
    }
  }
  catch( e )
  {
    YAHOO.log( 'Error: ' + e, 'error' );
    r = false;
  }
  
  return r;
}

// load namespace
Denbel.load( 'lang.Object' );

/**
 * Denbel.lang.Object
 * Global dynamic object
 * @return void
 */
Denbel.lang.Object = function()
{
  this.properties = new Array();
  this.propertyValues = new Array();
};

// prototype
Denbel.lang.Object.prototype =
{
  // fields
  properties: null,
  propertyValues: null,
  
  /**
   * Returns the value of a named property
   * @param string name of the property
   * @return mixed value of the property
   */
  get: function( name )
  {
    var i = Denbel.util.inArray( name, this.properties, true );
    
    if( i == null )
    {
      return null;
    }
      
    return this.propertyValues[i];
  },
  
  /**
   * Sets the value of a named property
   * @param string name of the property
   * @param mixed value of the property
   * @return void
   */
  set: function( name, value )
  {
    var i = Denbel.util.inArray( name, this.properties, true );
    
    if( i )
    {
      this.propertyValues[i] = value;
    }
    else
    {
      this.properties.push( name );
      var newIndex = ( this.properties.length - 1 );
      this.propertyValues[newIndex] = value;
    }
  },
  
  /**
   * Converts this object to an Array
   * @return Array
   */
  toArray: function()
  {
    var arr = new Array();
    
    var v = '';
    var n = '';
    
    for( var i = 0; i < this.properties.length; i++ )
    {
      arr[this.properties[i]] = this.propertyValues[i];
    }
      
    return arr;
  },
  
  /**
   * Converts this object to its string representation
   * @return string
   */
  toString: function()
  {
    return 'Denbel.lang.Object';
  }
};

// load namespace
Denbel.load( 'util.BrowserDetect' );

/**
 * BrowserDetect static object
 */
Denbel.util.BrowserDetect =
{
  /**
   * Initializer
   * @return void
   */
  init: function()
  {
    this.browser = this.searchString( this.dataBrowser ) || "An unknown browser";
    this.version = this.searchVersion( navigator.userAgent )
        || this.searchVersion( navigator.appVersion )
        || "an unknown version";
    this.OS = this.searchString( this.dataOS ) || "an unknown OS";
  },
    
  /**
   * Converts this object to its string representation
   * @return string
   */
  toString: function()
  {
    return this.browser + ' ' + this.version + ' ' + this.OS;
  },
    
  searchString: function ( data )
  {
    for( var i = 0; i < data.length; i++ )
    {
      var dataString = data[i].string;
      var dataProp = data[i].prop;
      this.versionSearchString = data[i].versionSearch || data[i].identity;
      if( dataString )
      {
        if( dataString.indexOf( data[i].subString ) != -1 )
        {
            return data[i].identity;
        }
      }
      else if( dataProp )
      {
        return data[i].identity;
      }
    }
  },
    
  searchVersion: function( dataString )
  {
    var index = dataString.indexOf( this.versionSearchString );
    if( index == -1 )
    {
      return;
    }
    return parseFloat( dataString.substring( index + this.versionSearchString.length + 1 ) );
  },
    
  dataBrowser: [
    {   string: navigator.userAgent,
        subString: "OmniWeb",
        versionSearch: "OmniWeb/",
        identity: "OmniWeb"
    },
    {
        string: navigator.vendor,
        subString: "Apple",
        identity: "Safari"
    },
    {
        prop: window.opera,
        identity: "Opera"
    },
    {
        string: navigator.vendor,
        subString: "iCab",
        identity: "iCab"
    },
    {
        string: navigator.vendor,
        subString: "KDE",
        identity: "Konqueror"
    },
    {
        string: navigator.userAgent,
        subString: "Firefox",
        identity: "Firefox"
    },
    {
        string: navigator.vendor,
        subString: "Camino",
        identity: "Camino"
    },
    {       // for newer Netscapes (6+)
        string: navigator.userAgent,
        subString: "Netscape",
        identity: "Netscape"
    },
    {
        string: navigator.userAgent,
        subString: "MSIE",
        identity: "Explorer",
        versionSearch: "MSIE"
    },
    {
        string: navigator.userAgent,
        subString: "Gecko",
        identity: "Mozilla",
        versionSearch: "rv"
    },
    {       // for older Netscapes (4-)
        string: navigator.userAgent,
        subString: "Mozilla",
        identity: "Netscape",
        versionSearch: "Mozilla"
    }
  ],
  
  dataOS : [
    {
      string: navigator.platform,
      subString: "Win",
      identity: "Windows"
    },
    {
      string: navigator.platform,
      subString: "Mac",
      identity: "Mac"
    },
    {
      string: navigator.platform,
      subString: "Linux",
      identity: "Linux"
    }
  ]
};

Denbel.util.BrowserDetect.init();
YAHOO.log( 'Browser detected: ' + Denbel.util.BrowserDetect.browser + ' ' + Denbel.util.BrowserDetect.version + ' on ' + Denbel.util.BrowserDetect.OS );
