/**
 * Bauglir JS extension
 *
 * extending native javascript objects
 *
 * @author Bronislav Klucka <Bronislav.Klucka@bauglir.com>
 * @copyright Copyright (c) 2009+, Bronislav Klučka
 * @license http://licence.bauglir.com/bsd.php BSD License
 * @version $Id: bauglir.js.js,v 1.4 2009-05-23 19:38:33 bauglir Exp $
 * @package BauglirWebCore
 * @subpackage Javascript
 */

/*global window, document, Bauglir */

(function () {
  var testArray = ['a', 'b'],
  testString = 'abcd',
  testNumber = 123,
  testDate = new Date();
  
  /////////////////////////////
  // ARRAY PROTOTYPE
  /////////////////////////////
  if (!testArray.contains)
  {
    /**
     * whether array contais value
     * @param value value to search for
     * @return bool
     */
    Array.prototype.contains = function (value)
    {
      return this.indexOf(value) > - 1;
    };
  }

  if (!testArray.each) 
  {
    /**
     * trigger callback function on each value (value is passed as first parametr, array is passed as second, index of value as third)
     *
     * if function returns false, traversing is stopped
     *
     * @param function callback
     */
    Array.prototype.each = function (callback)
    {
      var i, res, j;
      if (callback.constructor !== Function)
      {
        return;
      }
      for (i = 0, j = this.length; i < j; ++i)
      {
        res = callback(this[i], this, i);
        if (res === false)
        {
          break;
        }
      }
    };
  }

  if (!testArray.indexOf)
  {
    /**
     * returns index of value
     * if not founded -1 returned
     * @param mixed value
     * @return int
     */
    Array.prototype.indexOf = function (value)
    {
      for (var i = 0, j = this.length; i < j; i++)
      {
        if (this[i] === value)
        {
          return i;
        }
      }
      return -1;
    };
  }



  //////////////////////////////
  // DATE PROTOTYPES
  //////////////////////////////



  if (!testDate.date)
  {
    /**
     * set/get time in yyyy-mm-dd  format
     * @param string date
     * @return string
     */
    Date.prototype.date = function (datetime)
    {
      var dt = new Date(this),
      reg, parts;
      if (!datetime)
      {
        return dt.format("Y-m-d");
      }
      try
      {
        reg = /(\d{4})-(\d{2})-(\d{2})/;
        parts = datetime.match(reg);
        this.setFullYear(parts[1], parts[2] - 1, parts[3]);
        this.setHours(0, 0, 0, 0);
        return this;
      }
      catch (e)
      {
        return null;
      }
    };
  }

  if (!testDate.dateTime)
  {
    /**
     * set/get time in yyyy-mm-dd hh:nn:ss format
     * @param string datetime
     * @return string
     */
    Date.prototype.dateTime = function (datetime)
    {
      var dt = new Date(this),
      reg, parts;
      if (!datetime)
      {
        return dt.format("Y-m-d H:i:s");
      }
      try
      {
        if (datetime.length === 19)
        {
          reg = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/;
          parts = datetime.match(reg);
          this.setFullYear(parts[1], parts[2] - 1, parts[3]);
          this.setHours(parts[4], parts[5], parts[6], 0);
        }
        else if (datetime.length === 16)
        {
          reg = /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})/;
          parts = datetime.match(reg);
          this.setFullYear(parts[1], parts[2] - 1, parts[3]);
          this.setHours(parts[4], parts[5], 0, 0);
        }
        else if (datetime.length === 10)
        {
          reg = /(\d{4})-(\d{2})-(\d{2})/;
          parts = datetime.match(reg);
          this.setFullYear(parts[1], parts[2] - 1, parts[3]);
          this.setHours(0, 0, 0, 0);
        }
        return this;
      }
      catch (e)
      {
        return null;
      }
    };
  }


  if (!testDate.dayOfWeek)
  {
    /**
     * return day of week (1 for monday, 7 for sunday)
     * @return int
     */
    Date.prototype.dayOfWeek = function ()
    {
      var d = this.getDay();
      return (d === 0) ? 7 : d;
    };
  }

  if (!testDate.daysInAMonth)
  {
    /**
     * return days in month
     * @return int
     */
    Date.prototype.daysInAMonth = function ()
    {
      var dt = new Date(this),
      month = dt.getMonth(),
      day = 28;
      while (month === dt.getMonth())
      {
        day++;
        dt.setDate(day);
      }
      return day - 1;
    };
  }

  if (!testDate.format)
  {
    /**
     * format date and time according to passed string
     *
     * d: Day of the month, 2 digits with leading zeros  (01 to 31)
     * j: Day of the month without leading zeros (1 to 31)
     * m: Numeric representation of a month, with leading zeros (01 through 12)
     * n: Numeric representation of a month, without leading zeros (1 through 12)
     * Y: A full numeric representation of a year, 4 digits (1999 or 2003);
     * G: 24-hour format of an hour without leading zeros (0 through 23)
     * H: 24-hour format of an hour with leading zeros (00 through 23)
     * i: Minutes with leading zeros (00 to 59)
     * s: Seconds, with leading zeros (00 through 59)
     *
     * to prevent char from being expanded use backslash to escape it
     *
     *
     * @param formatString string to format date
     * @return string
     */
    Date.prototype.format = function (formatString)
    {
      var index = 0,
      result = [],
      chr,
      parts = formatString.split("");
      while (index < parts.length)
      {
        chr = parts[index];
        switch (chr)
        {
        case "d": 
          result.push(this.getDate().pad(2, '0', String.PadLeft));
          break;
        case "j": 
          result.push(this.getDate());
          break;
        case "m":
          result.push(this.monthOfYear().pad(2, '0', String.PadLeft));
          break;
        case "n": 
          result.push(this.monthOfYear());
          break;
        case "Y": 
          result.push(this.getFullYear());
          break;
        case "G": 
          result.push(this.getHours());
          break;
        case "H": 
          result.push(this.getHours().pad(2, '0', String.PadLeft));
          break;
        case "i": 
          result.push(this.getMinutes().pad(2, '0', String.PadLeft));
          break;
        case "s": 
          result.push(this.getSeconds().pad(2, '0', String.PadLeft));
          break;
        default:
          result.push(chr);

        }
        index++;
      }
      return result.join("");
    };
  }



  if (!testDate.monthOfYear)
  {
    /**
     * get/set month of year (1 for January, 12 for December)
     * @param int|null newMonth
     * @return int
     */
    Date.prototype.monthOfYear = function (newMonth)
    {
      var d = 0;
      if (Bauglir.integer(newMonth))
      {
        this.setMonth(newMonth - 1);
      }
      d = this.getMonth();
      return d + 1;
    };
  }




  if (!testDate.time)
  {
    /**
     * set/get time in hh:nn or hh:nn:ss  format
     * @param string datetime
     * @return string
     */
    Date.prototype.time = function (datetime)
    {
      var dt = new Date(this),
      reg, parts;
      if (!datetime)
      {
        /*datetime = '';
        datetime += dt.getHours().pad(2, '0', String.PadLeft) + ":";
        datetime += dt.getMinutes().pad(2, '0', String.PadLeft) + ":";
        datetime += dt.getSeconds().pad(2, '0', String.PadLeft);*/
        return dt.format("H:i:s");
      }
      try
      {
        if (datetime.length === 8)
        {
          reg = /(\d{2}):(\d{2}):(\d{2})/;
          parts = datetime.match(reg);
          this.setHours(parts[1], parts[2], parts[3], 0);
        }
        else if (datetime.length === 5)
        {
          reg = /(\d{2}):(\d{2})/;
          parts = datetime.match(reg);
          this.setHours(parts[1], parts[2], 0, 0);
        }
        return this;
      }
      catch (e)
      {
        return null;
      }
    };
  }

  if (!testDate.timeShort)
  {
    /**
     * set/get time in hh:nn format
     * @param string datetime
     * @return string
     */
    Date.prototype.timeShort = function (datetime)
    {
      if (datetime)
      {
        this.time(datetime);
      }
      return this.getHours().pad(2, '0', String.PadLeft) + ":" +
             this.getMinutes().pad(2, '0', String.PadLeft);
    };
  }

  //////////////////////////////
  // NUMBER PROTOTYPES
  //////////////////////////////
  Number.Integer = 1;
  Number.Int = Number.Integer;
  Number.Float = 2;

  if (!testNumber.pad)
  {
    /**
     * return string with newLength length (or bigger according to padString and padding)
     * @return int newLength new length
     * @return string padString string to be appended to
     * @return int padding where to add padString to
     */
    Number.prototype.pad = function (newLength, padString, padding)
    {
      var s = this + '';
      return s.pad(newLength, padString, padding);
    };
  }



  //////////////////////////////
  // OBJECT PROTOTYPE
  /////////////////////////////
  if (!Object.create)
  {
    Object.create = function (o)
    {
      function F()
      {
        this.self = this;
      }
      F.prototype = o;
      return new F();
    };
  }




  //////////////////////////////
  // STRING PROTOTYPE
  /////////////////////////////

  if (!testString.trim) 
  {

    /**
     * function to remove trailing white characters from string
     */
    String.prototype.trim = function ()
    {
      return (this.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, ""));
    };
  }

  if (!String.trim) 
  {
    /**
     * function to remove trailing white characters from string
     * @param string text, text to trim
     */
    String.trim = function (text)
    {
      return text.trim();
    };
  }

  if (!testString.isEmail)
  {
    /**
     * function to test whether text is email
     */
    String.prototype.isEmail = function ()
    {
      var reg = /^((?:(?:(?:[a-zA-Z0-9][\.\-\+_]?)*)[a-zA-Z0-9])+)\@((?:(?:(?:[a-zA-Z0-9][\.\-_]?){0,62})[a-zA-Z0-9])+)\.([a-zA-Z0-9]{2,6})$/;
      return reg.test(this);
    };
  }

  if (!String.isEmail)
  {
    /**
     * function to test whether text is email
     * @param string text, text to text
     */
    String.isEmail = function (text)
    {
      return text.isEmail();
    };
  }

  if (!String.randomChar)
  {
    /**
     * return random uppercase character
     * @return string
     */
    String.randomChar = function ()
    {
      return String.fromCharCode(65 + Math.round(Math.random() * 25));
    };
  }

  String.PadLeft = 0;
  String.PadRight = 1;
  String.PadBoth = 2;


  if (!testString.pad)
  {
    /**
     * return string with newLength length (or bigger according to padString and padding)
     * @return int newLength new length
     * @return string padString string to be appended to
     * @return int padding where to add padString to
     */
    String.prototype.pad = function (newLength, padString, padding)
    {
      var result = this;
      while (result.length < newLength)
      {
        if (padding === String.PadLeft)
        {
          result = padString + result;
        }
        else if (padding === String.PadRight)
        {
          result = result + padString;
        }
        else if (padding === String.PadBoth)
        {
          result = padString + result + padString;
        }
      }
      return result;
    };
  }

  if (!testString.htmlEncode)
  {
    /**
     * function converts text to html encoded text (&lt;, &gt;, etc.)
     * @param string text
     * @return string
     */
    String.prototype.htmlEncode = function ()
    {
      var translation = [],
      tr, chr,
      text = this;
      translation['&'] = "&amp;";
      translation['<'] = "&lt;";
      translation['>'] = "&gt;";
      for (chr in translation)
      {
        if (translation.hasOwnProperty(chr))
        {
          tr = translation[chr];
          while (text !== (text = text.replace(chr, tr)))
          {
            
          }
          
        }
      }
      return text;
    };
  }

}());
