/* 
 * jsExtensions JavaScriptUtils module
 * 
 * Inits standard javascript objects extensions.
 * 
 */



/*
 *  General extension function (imported from Prototype framework)
 */
Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};



/*
 *  Date object extension
 */ 
Date.prototype.getISOWeek = function(utc) {
    var y = utc ? this.getUTCFullYear(): this.getFullYear();
    var m = utc ? this.getUTCMonth() + 1: this.getMonth() + 1;
    var d = utc ? this.getUTCDate() : this.getDate();
    // If month jan. or feb.
    if (m < 3) {
      var a = y - 1;
      var b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
      var c = ( (a - 1) / 4 | 0) - ( (a - 1) / 100 | 0) + ( (a - 1) / 400 | 0);
      var s = b - c;
      var e = 0;
      var f = d - 1 + 31 * (m - 1);
    }
    // If month mar. through dec.
    else {
      var a = y;
      var b = (a / 4 | 0) - ( a / 100 | 0) + (a / 400 | 0);
      var c = ( (a - 1) / 4 | 0) - ( (a - 1) / 100 | 0) + ( (a - 1) / 400 | 0);
      var s = b - c;
      var e = s + 1;
      var f = d + ( (153 * (m - 3) + 2) / 5 | 0) + 58 + s;
    }
    var g = (a + b) % 7;
    // ISO Weekday (0 is monday, 1 is tuesday etc.)
    var d = (f + g - e) % 7;
    var n = f + 3 - d;
    if (n < 0)
      var w = 53 - ( (g - s) / 5 | 0);
    else if (n > 364 + s)
      var w = 1;
    else
      var w = (n / 7 | 0) + 1;
    return w;
  };
  
function getWeekNumber(day, month, year) { // Month in range 0 - 11!
	var tempDate = new Date();
	tempDate.setFullYear(year);
	tempDate.setMonth(month);
	tempDate.setDate(day);
	return tempDate.getISOWeek();
}

Date.prototype.getISODay = function(utc) {
    var d = utc ? this.getUTCDay() : this.getDay();
	return d ? d : 7;
	};
Date.prototype.getNameOfDay = function(utc) { return this.nameOfDays[this.getISODay(utc)]; };
Date.prototype.getShortNameOfDay = function(utc) { return this.nameOfDaysShort[this.getISODay(utc)]; };
Date.prototype.getNameOfMonth = function(utc) { return this.nameOfMonths[utc ? this.getUTCMonth() : this.getMonth()]; };
Date.prototype.getNameOfMonthIn = function(utc) { return this.nameOfMonthsIn[utc ? this.getUTCMonth() : this.getMonth()]; };
Date.prototype.isLeapYear = function(utc) {
    var y = utc ? this.getUTCFullYear() : this.getFullYear();
    return !(y % 4) && (y % 100) || !(y % 400) ? true : false;
  };
Date.prototype.getDaysInMonth = function(utc) {
    var m = utc ? this.getUTCMonth() : this.getMonth();
    // If feb.
    if (m == 1) return this.isLeapYear() ? 29 : 28;
    // If Apr, Jun, Sep or Nov return 30; otherwise 31
    return (m == 3 || m == 5 || m == 8 || m == 10) ? 30 : 31;
  };
Date.prototype.set = function(yearVal, monthVal, dayVal) {
	this.setFullYear(yearVal, monthVal, dayVal);
	return this;
}
Date.prototype.format =
  function(s, utc) {
    // Split into array
    s = s.split('');
    var l = s.length;
    var r = '';
    var n = m = null;
    for (var i=0; i<l; i++) {
      switch(s[i]) {
        // Day of the month, 2 digits with leading zeros: 01 to 31
        case 'd':
          n = utc ? this.getUTCDate() : this.getDate();
          if (n * 1 < 10)
            r += '0';
          r += n;
          break;
        // A textual representation of a day, three letters:  Mon through Sun 
        case 'D':
          r += this.getShortNameOfDay(utc);
          break;
        // Day of the month without leading zeros:   1 to 31
        case 'j':
          r += utc ? this.getUTCDate() : this.getDate();
          break;
        // Lowercase l A full textual representation of the day of the week: Sunday (0) through Saturday (6) 
        case 'l':
          r += this.getNameOfDay(utc);
          break;
        // ISO-8601 numeric representation of the day of the week: 1 (for Monday) through 7 (for Sunday) 
        case 'N':
          r += this.getISODay(utc);
          break;
        // Numeric representation of the day of the week: 0 (for Sunday) through 6 (for Saturday) 
        case 'w':
          r += utc ? this.getUTCDay() : this.getDay();
          break;
        // The day of the year (starting from 0) 0 through 365
        case 'z':
          n = 0;
          m = utc ? this.getUTCMonth() : this.getMonth();
          for(var i=0; i<m; i++)
            n += Date.daysInMonth[i]
          if (this.isLeapYear())
            n++;
          n += utc ? this.getUTCDate() : this.getDate();
          n--;
          r += n;
          break;
        //   break;
        // ISO-8601 week number of year, weeks starting on Monday
        case 'W':
          r += this.getISOWeek(utc);
          break;
        // A full textual representation of a month, such as January or March:  January through December 
        case 'F':
          r += this.getNameOfMonthIn(utc);
          break;
        // Numeric representation of a month, with leading zeros 01 through 12 
        case 'm':
          n = utc ? this.getUTCMonth() : this.getMonth();
          n++;
          if (n < 10)
            r += '0';
          r += n;
          break;
        // A short textual representation of a month, three letters:  Jan through Dec 
        case 'M':
          r += this.getNameOfMonth(utc).substring(0,3);
          break;
        // Numeric representation of a month, without leading zeros:  1 through 12 
        case 'n':
          n = utc ? this.getUTCMonth() : this.getMonth();
           r += ++n;
          break;
        // Number of days in the given month: 28 through 31 
        case 't':
          r += this.getDaysInMonth(utc);
          break;
        // Whether it's a leap year:  1 if it is a leap year, 0 otherwise.
        case 'L':
          if (this.isLeapYear(utc))
            r += '1';
          else
            r += '0';
          break;
        // ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead
        /*
        case 'o':
          break;
        */
        // A full numeric representation of a year, 4 digits
        case 'Y':
          r += utc ? this.getUTCFullYear() : this.getFullYear();
          break;
        // A two digit representation of a year
        case 'y':
          n = utc ? this.getUTCFullYear() : this.getFullYear();
          r += (n + '').substring(2);
          break;
        // Lowercase Ante meridiem and Post meridiem am or pm 
        case 'a':
          n = utc ? this.getUTCHours() : this.getHours();
          r += n < 12 ? 'am' : 'pm';
          break;
        // Uppercase Ante meridiem and Post meridiem AM or PM 
        case 'A':
          n = utc ? this.getUTCHours() : this.getHours();
          r += n < 12 ? 'AM' : 'PM';
          break;
        // Swatch Internet time 000 through 999 
        // case 'B':
        //   break;
        // 12-hour format of an hour without leading zeros
        case 'g':
          n = utc ? this.getUTCHours() : this.getHours();
          if (n > 12)
            n -= 12;
          r += n;
          break;
        // 24-hour format of an hour without leading zeros 0 through 23
        case 'G':
          r += this.getHours();
          break;
        //  12-hour format of an hour with leading zeros 01 through 12 
        case 'h':
          n = utc ? this.getUTCHours() : this.getHours();
          if (n > 12)
            n -= 12;
          if (n < 10)
            r += '0';
          r += n;
          break;
        // 24-hour format of an hour with leading zeros 00 through 23 
        case 'H':
          n = utc ? this.getUTCHours() : this.getHours();
          if (n < 10)
            r += '0';
          r += n;
          break;
        // i Minutes with leading zeros 00 to 59 
        case 'i':
          n = utc ? this.getUTCMinutes() : this.getMinutes();
          if (n < 10)
            r += '0';
          r += n;
          break;
        // s Seconds, with leading zeros 00 through 59 
        case 's':
          n = utc ? this.getUTCSeconds() : this.getSeconds();
          if (n < 10)
            r += '0';
          r += n;
          break;
        // Milliseconds
        case 'u':
          r += utc ? this.getUTCMilliseconds() : this.getMilliseconds();
          break;
        // Timezone identifier
        // case 'e':
        //   break;
        // Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise. 
        case 'I':
          if (this.getMinutes() != this.getUTCMinutes)
            r += '1';
          else
            r += '0';
          break;
        // Difference to Greenwich time (GMT) in hours
        case 'O':
          n = this.getTimezoneOffset() / 60;
          if (n >= 0)
            r += '+';
          else
            r += '-';
          n = Math.abs(n);
          if (Math.abs(n) < 10)
            r += '0';
           r += n + '00';
          break;
        // Difference to Greenwich time (GMT) with colon between hours and minutes: Example: +02:00 
        case 'P':
          n = this.getTimezoneOffset() / 60;
          if (n >= 0)
            r += '+';
          else
            r += '-';
          n = Math.abs(n);
          if (Math.abs(n) < 10)
            r += '0';
           r += n + ':00';
          break;
        // T Timezone abbreviation EST, MDT etc. 
        // case 'T':
        //   break;
        // Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. 
        case 'Z':
          r += this.getTimezoneOffset() * 60;
          break;
        // ISO 8601 date: 2004-02-12T15:19:21+00:00 
        case 'c':
          r += this.format('Y-m-d',utc) + 'T' + this.format('H:i:sP',utc);
          break;
        // RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200 
        case 'r':
          r += this.format('D, j M Y H:i:s P',utc);
          break;
        case 'U':
          r += this.getTime();
          break;
        default:
          r += s[i];
      }
    }
    return r
  };  


var date = new Date(); // Helper object
var now = new Date; // Global object with current date



/*
 *  String object extension
 */ 
String.prototype.levenshtein =
  function(t) {
    // ith character of s
    var si; 
    // cost
    var c;
    // Step 1
    var n = this.length;
    var m = t.length;
    if (!n)
      return m;
    if (!m)
      return n;
    // Matrix
    var mx = [];
    // Step 2 - Init matrix
    for (var i=0; i<=n; i++) {
      mx[i] = [];
      mx[i][0] = i;
    }
    for (var j=0; j<=m; j++)
      mx[0][j] = j;
    // Step 3 - For each character in this
    for (var i=1; i<=n; i++) {
      si = this.charAt(i - 1);
      // Step 4 - For each character in t do step 5 (si == t.charAt(j - 1) ? 0 : 1) and 6
      for (var j=1; j<=m; j++)
        mx[i][j] = Math.min(mx[i - 1][j] + 1, mx[i][j - 1] + 1, mx[i - 1][j - 1] + (si == t.charAt(j - 1) ? 0 : 1));
    }
    // Step 7
    return mx[n][m];
  };
String.prototype.substrCount = function(s) {
    return this.length && s ? (this.split(s)).length - 1 : 0;
  };
String.prototype.replaceAll = function(pcFrom, pcTo) {
	var i = this.indexOf(pcFrom);
	var c = this;
	while (i > -1){
		c = c.replace(pcFrom, pcTo);
		i = c.indexOf(pcFrom);
	}
	return c;
}
String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g,"");
  };
String.prototype.number = function() {
    return parseInt(this);
  };  
  
  
  
  
/*
 * Numeric array prototype extension
 */
Array.prototype.sum = function(){
	for(var i=0,sum=0;i<this.length;sum+=this[i++]);
	return sum;
}
Array.prototype.normalize = function(normalizer){
	for (var i=0; i<this.length; i++) this[i] = this[i] / normalizer;
	return this;
}


Math.log10 = function(base) {
	return Math.log(base)/Math.LN10;
}


/*
 * Array prototype extension (Extracted from prototype)
 */
var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    iterator = iterator.bind(context);
    try {
      this._each(function(value) {
        iterator(value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var index = -number, slices = [], array = this.toArray();
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  },

  all: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result = true;
    this.each(function(value, index) {
      result = result && !!iterator(value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result = false;
    this.each(function(value, index) {
      if (result = !!iterator(value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },

  detect: function(iterator, context) {
    iterator = iterator.bind(context);
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(filter, iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(filter);

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator(value, index));
    });
    return results;
  },

  include: function(object) {
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = fillWith === undefined ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator, context) {
    iterator = iterator.bind(context);
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == undefined || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == undefined || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator, context) {
    iterator = iterator.bind(context);
    return this.map(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.map();
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (Object.isFunction(args.last()))
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
};

Object.extend(Array.prototype, Enumerable);






function roundNumber(num, dec) {
	var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
	return result;
}







/*
* jQuery Text Children plugin
* Examples and documentation at: http://plugins.learningjquery.com/textchildren/
* Version: 0.1 (02/27/2008)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
* Requires: jQuery v1.1.3.1 or later
*/

(function($) {
$.fn.textChildren = function(n, options) {
 if (typeof n == 'object') {
   options = n;
   n = 'all';
 }
 var opts = $.extend({}, $.fn.textChildren.defaults, options || {}); 

 var output = opts.outputType == 'string' ? '' : [];
 var contents = '';
 this.each(function(index) {
   var nodeIndex = n;
   var txtNodes = $.grep( this.childNodes, function(node) {
     return (node.nodeType == 3);
   });
 
   if (n == 'first') {
     nodeIndex = 0;
   } else if (n == 'last') {
     nodeIndex = txtNodes.length - 1;
   } else if ( n < 0 ) {
     nodeIndex = txtNodes.length + n;
   }
   if ( nodeIndex >= txtNodes.length || nodeIndex < 0 ) {
     return;
   } 
   if (n == undefined || n == 'all') {
     for (var i=0; i < txtNodes.length; i++) {
       contents = opts.trim ? $.trim(txtNodes[i].nodeValue) : txtNodes[i].nodeValue;
       if (opts.outputType == 'array') {
         ( output.length && contents == '') ? output : output.push(contents);
       } else {
         output += output == '' ? contents : opts.stringDelimiter + contents;
       }
     }
   } else {
     contents = opts.trim ? $.trim(txtNodes[nodeIndex].nodeValue) : txtNodes[nodeIndex].nodeValue;
     if (opts.outputType == 'array') {
       output.push(contents);
     } else {
       output += output == '' ? contents : opts.stringDelimiter + contents;
     }
   }
 });
 return output;
};


$.fn.textChildren.defaults = {
 outputType: 'string',       // one of 'string' or 'array'
 stringDelimiter: '',        // if outputting to string, inserts a delimiter between nodes
 trim: true                  // whether to trim white space from start/end of text nodes
};

})(jQuery);

