if(typeof(nuf)==='undefined'){
	nuf = {};
}

function $(element) { 
   return  document.getElementById(element); 
}
function $$(element) { 
   return  document.getElementsByName(element); 
}

// if nuf.decimalSep global is undefined, then just default to dot 
if(typeof(nuf.decimalSep)==='undefined'){nuf.decimalSep='.';}
if(typeof(nuf.thousandSep)==='undefined'){nuf.thousandSep=',';}
if(typeof(nuf.datePattern)==='undefined'){nuf.datePattern='dd-mm-yyyy';}
if(typeof(nuf.dateSep)==='undefined'){nuf.dateSep='-';}
if(typeof(nuf.debug)==='undefined'){nuf.debug=0;}

//////////////// String PROTOTYPES   ////////////////////////////////

String.prototype.startsWith = function(str) {return (this.match("^"+str)==str)} // do not change in === notation
String.prototype.endsWith = function(str) {return (this.match(str+"$")==str)}

// trimming with array ops
String.prototype.trim = function() { return this.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }

// string replicator 
String.prototype.times = function(n) { s = ''; for (i = 0; i < n; i++) s += this; return s; }

// zero padding and trailing
String.prototype.zp = function(n) { return '0'.times(n - this.length) + this; }
String.prototype.zt = function(n) { return this + '0'.times(n - this.length); }

// string reverse
String.prototype.reverse = function() { return this.split('').reverse().join(''); }

// clear format from a string representation of a number
String.prototype.clean = function() {
	var pattern = '[^0-9|'+nuf.thousandSep+'|-]';
	return this.replace(/pattern/g, ''); 
}

String.prototype.isDate = function() {
	return this.parseDateTime()!=null;
}

String.prototype.isTime = function() {
	return this.parseTime()!=null;
}

String.prototype.parseDateTime = function() {
	//debugger;
	var parts = this.trim().split(' ');
	var dt1;
	var dt2;
	if(parts.length < 1||parts.length>2)return null;
	
	if(parts.length==2) {
		dt1 = parts[0].parseDate();
		dt2 = parts[1].parseTime();
		
	} else {
		dt1 = parts[0].parseDate();
		dt2 = new Date().trunc(); 
	}
	
	if (dt1==null || dt2==null)return null;
	
	dt1.setHours( dt2.getHours(), dt2.getMinutes(), dt2.getSeconds(), dt2.getMilliseconds());
	
	return dt1;
	
}

String.prototype.parseTime = function() { 
	var hs='';
	var ms='';
	var ss='0';
	var ap = "AM";
	var f = "h";
	var c;
	var tm = this;
	
	var timeParts = tm.trim().match(/^(([0-1]?[0-9])|([2][0-3]))(:([0-5]?[0-9])(:([0-5]?[0-9]))?)?$/);
	if (timeParts) {
		hs = timeParts[1]; // note: modified on 2/5 from [2] that was working on IE10 but not on ie8
		ms = timeParts[5];
		ss = timeParts[7];
	} else {
		timeParts = tm.trim().match(/^(([0-1]?[0-9])|([2][0-3]))([0-5][0-9])$/);
		if (timeParts) {
			hs = timeParts[1]; // note: modified on 2/5 from [2] that was working on IE10 but not on ie8
			ms = timeParts[4];
			ss = '00';
		}
	}
	if(!hs)hs='00';
	if(!ms)ms='00';
	if(!ss)ss='00';
	
	if (hs=='' && ms=='') {
		if (tm.length==4 && tm.indexOf(":")==-1 && tm.indexOf(" ")==-1 && tm.indexOf(".")==-1 && tm.indexOf("-")==-1) {
			hs = tm.substring(0,2);
			ms = tm.substring(2);
		}
	}
	
	if (hs=='' && ms=='') {
		//alert('here');
		for(var i=0;i<tm.length;i++)  {
			c = tm.substring(i,i+1);
			c = c.toUpperCase();
			
			if( c>='0' && c<='9' ){
				if(f=='h'){
					hs=hs+c;
				} else if(f=='m') {
					ms=ms+c;
				}
				
			} else if(c=="A"||c=="P"||c=="M") {
				
				if(f=='ap'){
					ap=ap+c;
					
				} else {
					
					if(c=="A"||c=="P") {
						f='ap';
						ap=c + 'M';
						break;
						
					} else {
						return null;
					}
				}
			} else if(c==":"||c=="."||c==" "||c=="-") {
				if(f=='h'){
					f='m';
				} else if(f=='m'){
					f='ap';
				} else {
					return null;
				}
			}
			
		}
	}
		
	if ( hs.length < 1 || hs.length > 2 || ms.length>2 || ap.length!=2 ) {
		return null;
	}
	
	if ( ms.length==0 ) {
		ms = "00";
	}
	
	if (hs.length==1) {
		hs = '0' + hs;
	}
	
	if (ms.length==1) {
		ms = '0' + ms;
	}
	if (ss && ss.length==1) {
		ss = '0' + ss;
	}
	ap=ap.toUpperCase();
		
	if (ms.length == 2) {
		if (parseFloat(ms)>59 || parseFloat(ms)<0) {
			return null;
		}
	}
	
	if (hs.length == 2) {
		if (parseFloat(hs)>24 || parseFloat(hs)<0) {
			return null;
		} else {
			if (parseFloat(hs)>12) {

			} else {
				if (ap=='PM') {
					hs = parseFloat(hs) + 12;
				}
			}
		}
	}
	
	var st = new Date();
	st.setMinutes(ms);
	st.setHours(hs)
	st.setSeconds(ss,0); //to fix 00:59 issue, set seconds and millisecords 
	return st;
}

String.prototype.parseDate = function() { 
	var c='';               
	var d='';           
	var m='';            
	var y='';            
	var f='d';  
		
	if ( nuf.datePattern.indexOf('d')=== -1 ) {
		f='m';
	}
	
	for(var i=0;i<this.length;i++)  {
		c = this.substring(i,i+1);

		if( c>='0' && c<='9' ){
			if(f=='m')m=m+c;
			else if(f=='d')d=d+c;
			else y=y+c;       

		} else if(c=="/"||c=="-"||c==".") {
			if(f=='d'){
				f='m';
			} else if(f=='m'){
				f='y';
			} else {
				return null;
			}
		} else {                       
			return null;
		}
	}
	//alert('y.length:'+ y.length);
	if (y.length==0) {
		var dty=new Date();
		//alert(dty.getFullYear());
		y = dty.getFullYear()+'';
	}
	//alert('y:'+ y);
	if(m.length>2||m.length<1||d.length>2||d.length<1||(y.length!=4&&y.length!=2)){    
		return null;
	}
	
	if (y.length == 2) {
		if (parseFloat(y)>50) {
			y='19'+y;
		}else {
			y='20'+y;
		}
	}
	
	var dt = new Date();
	dt.setFullYear(y,m-1,d);
	dt.setHours(0,0,0,0);  // since we only care about dates, we set all hour fields to 0
	
	// after setting the month, check that the month we set is the same as the 
	// month we set.  This is because if you say dt.setDate(50), it chnages the 
	// month of the date to the next month!
	if(dt.getMonth()==(m-1)) {
		//same month we set at line 214!
		return dt;	
	} else {
		return null;
	}
		
	
}

String.prototype.parseNumber = function() { 
	var tmp = this.trim();
	if (this.isNumeric()) {
		
		var ret = parseFloat( this.IntegerPart().toString() + '.'+ this.DecimalPart().toString() );
		return ret;
		
	} else {
		return 0;
	} 
}
String.prototype.IntegerPart = function() { 
	
	if ( this.isNumeric() ) {
		
		var parts = this.split(nuf.decimalSep);
		var intPart = parts[0];
		
		if(nuf.thousandSep == '.') {
			intPart = intPart.replace(/\./g,''); //remove all thousand separators
		} else {
			intPart = intPart.replace(/\,/g,''); //remove all thousand separators
		}
		
		var ret = parseInt(intPart,10);
		return ret;
		
	} else {
		return null;
	}
}

// returns an array of 2 or one part of a number.
// If the string is numeric decimal, an array of 2 elements
// if the string is numeric integer, an array of 1 element
// if the string is null or empty, an array of 0 elements
String.prototype.NumParts = function() { 
	return this ? this.split(nuf.decimalSep): new Array();
}

String.prototype.DecimalPart = function() { 
	
	if ( this.isNumeric() ) {
		var parts = this.split(nuf.decimalSep);
		if(parts.length==2) {
			return parts[1];
		} else {
			return 0;
		}
		
	} else {
		return 0;
	}
}


String.prototype.isNumeric = function() { 
		
	if ( this == '' || this == null || this=='undefined') {
		return false; 
		//return true if it is an empty vALUE.
	}
	var ValidChars = '0123456789';
	var parts = this.split(nuf.decimalSep);
	var Char;
	
	if (parts.length == 0 || parts.length > 2 ) return false;
		
	// validate integer part parts[0]
	var integerPart = parts[0].reverse();
	
	for (i = 0; i < integerPart.length; i++) { 
		Char = integerPart.charAt(i); 
		if (( Char=='+' || Char=='-' ) && i==(integerPart.length-1)) {
			//allow this, it is OK!!
			// we have a sign symbol at the beginning of the number!!
			// since we havwe reversed the string, then we check the end of the integerPart : i==(integerPart.length-1)
		}else {
			var indexof = ValidChars.indexOf(Char);
			if (i > 0 && i % 3 == 0) {
				// every third character, allow numbers and the thousand separator!!
				if (indexof==-1 && Char!=nuf.thousandSep) {
					return false;
				}
				
			} else {
				if (indexof==-1)return false;
			}		
		}
	}
	
	if (parts.length==2) {
	
		// only numbers allowed!
		var decimalPart = parts[1];
		for (i = 0; i < decimalPart.length; i++) { 
			Char = decimalPart.charAt(i); 
			var indexof = ValidChars.indexOf(Char);
			if (indexof==-1)return false;
		}
	}
	
	return true;
}

//////////////// Number PROTOTYPES   ////////////////////////////////
// nuf.decimalSep, nuf.thousandSep
// string method wrappers
Number.prototype.zp = function(n) { return this.toString().zp(n); }
Number.prototype.zt = function(n) { return this.toString().zt(n); }
Number.prototype.substr = function(n) { return this.toString().substr(n); }
Number.prototype.reverse = function() { return this.toString().reverse(); }

// number sign 'bit' (boolean)
Number.prototype.sign = function() { return this < 0; }

// decimal digits truncation
Number.prototype.truncate   = function(n) { return Math.round(this * Math.pow(10, n)) / Math.pow(10, n); }

// fractional part of a number
Number.prototype.fractional = function()  { return parseFloat(this) - parseInt(this,10); }

// integer thousand separators
Number.prototype.group = function()
{
	var s = parseInt(this,10).reverse(), r = '';

	for (var i = 0; i < s.length; i++)
		r += (i > 0 && i % 3 == 0 ? nuf.thousandSep : '') + s.charAt(i);
	
	return r.reverse();
}

// format a number with n decimal digits, thousands separator and sign
Number.prototype.format = function(n)
{
	// remember the input sign and cancel it
	var a = Math.abs(this);

	// truncate and zero-trail the fractional part
	var f = a.fractional().truncate(n).substr(2).zt(n);
	if(n==0) {
		return (' -'.substr(this.sign(), 1) + a.group()).trim();
	} else {
	// sign + grouped integer part + dot + fractional part
		return (' -'.substr(this.sign(), 1) + a.group() + nuf.decimalSep + f).trim();
	}
}

////////////////////////////////////////////////////////////////////////////////

Date.prototype.after= function(n) {
	return this.compareTo(n)>0;
}

Date.prototype.before= function(n) {
	return this.compareTo(n)<0;
}

Date.prototype.onSameDate= function(n) {
	return this.truncate().compareTo(n.truncate())==0;
}

Date.prototype.compareTo= function(n /* Date */) {
	var dt = n;
	if (typeof(dt)=='string') {
		dt = n.parseDate();
	}
	
	 if (this.getTime() > dt.getTime()) {
        ret = 1;
    }

    if (this.getTime() < dt.getTime()) {
        ret = -1;
    }

    if (this.getTime() == dt.getTime()) {
        ret = 0;
    }
    
    return ret;
	

}

Date.prototype.formatTime = function() { 
	
	var dt,mn,yr,h,m;
	if (this==null) {
		return null;
		
	} else {
		h = this.getHours();
		m = this.getMinutes();
		
		if ( m< 10)m='0'+m;
		if ( h< 10)h='0'+h;
		return h + ':' + m;
	}
}

Date.prototype.formatDate = function() {  
	var dt,mn,yr;
	if (this==null) {
		return null;
	} else {
		dt = this.getDate();
		mn = this.getMonth()+1;
		if ( dt< 10)dt='0'+dt;
		if ( mn< 10)mn='0'+mn; 
		return dt + nuf.dateSep + mn + nuf.dateSep + this.getFullYear();
	} 
}

Date.prototype.formatDateTime = function() {

	var dt,mn,h,m,s;
	if (this==null) {
		return null;
	} else {
		dt = this.getDate();
		mn = this.getMonth()+1;
		h = this.getHours();
		m = this.getMinutes();
		s = this.getSeconds();
		
		if ( m< 10)m='0'+m;
		if ( h< 10)h='0'+h;
		if ( dt< 10)dt='0'+dt;
		if ( mn< 10)mn='0'+mn; 
		if ( s< 10)s='0'+s; 
		return dt + nuf.dateSep + mn + nuf.dateSep + this.getFullYear() + ' ' + h + ':' + m + ':' + s;
	}
}


Date.prototype.truncate = function() {  
	var dt = new Date();
	dt.setFullYear(this.getFullYear(), this.getMonth(), this.getDate());
	dt.setHours(0,0,0,0);
	return dt;
};

Date.prototype.trunc = function() {  
	return this.truncate();
};

Date.prototype.inFuture= function() {
	
	var dt1 = this.truncate();
	var dt2 = new Date().truncate();
    return dt1.compareTo(dt2) >0;
};

Date.prototype.getMonthDays = function(month) {
	var temp_date = new Date(this.getFullYear(), this.getMonth()+1, 1);
	temp_date.setDate(temp_date.getDate()-1);
	return temp_date.getDate();
};

Date.prototype.isLeap = function() { new Date(this.getFullYear(),1,29).getDate() == 29};

Date.prototype.inPast= function() {
		
    return this.truncate().compareTo(new Date().truncate())<0;
}

Date.prototype.add = function( /**String*/unit, /**Number*/value ) {   
  
   unit = unit.replace( /s$/ ).toLowerCase();   
    var copiedDate = new Date(this.getTime());
	
   switch ( unit ) {   
      case "year":   
         copiedDate.setYear( this.getFullYear() + value );   
         break;   
      case "month":   
         copiedDate.setMonth( this.getMonth() + value );   
         break;   
      case "week":   
         copiedDate.setTime( this.getTime() + value * 604800000 );   
         break;   
      case "day":   
         copiedDate.setTime( this.getTime() + value * 86400000 );   
         break;   
      case "hour":   
         copiedDate.setTime( this.getTime() + value * 3600000 );   
         break;   
      case "minute":   
         copiedDate.setTime( this.getTime() + value * 60000 );   
         break;   
      case "second":   
         copiedDate.setTime( this.getTime() + value * 1000 );   
         break;   
      case "nanosecond":   
         // Fall Through   
      default:   
         copiedDate.setTime( this.getTime() + value );   
         break;   
   }   
  
   return copiedDate;   
};   
  
Date.prototype.subtract = function( /**String*/unit, /**Number*/value ) {   
  
   unit = unit.replace( /s$/ ).toLowerCase();   
   var copiedDate = new Date(this.getTime());
   
   switch ( unit ) {   
      case "year":   
         copiedDate.setYear( this.getFullYear() - value );   
         break;   
      case "month":   
         copiedDate.setMonth( this.getMonth() - value );   
         break;   
      case "week":   
         copiedDate.setTime( this.getTime() - value * 604800000 );   
         break;   
      case "day":   
         copiedDate.setTime( this.getTime() - value * 86400000 );   
         break;   
      case "hour":   
         copiedDate.setTime( this.getTime() - value * 3600000 );   
         break;   
      case "minute":   
         copiedDate.setTime( this.getTime() - value * 60000 );   
         break;   
      case "second":   
         copiedDate.setTime( this.getTime() - value * 1000 );   
         break;   
      case "nanosecond":   
         // Fall Through   
      default:   
         copiedDate.setTime( this.getTime() - value );   
         break;   
   } 
   return copiedDate;
}; 

Date.prototype.addMinutes= function( /**Number*/val ) {   
	return this.add('minute',val);
};

Date.prototype.addYears= function( /**Number*/val ) {   
	return this.add('year',val);
};
Date.prototype.addMonths= function( /**Number*/val ) {   
	return this.add('month',val);
};
Date.prototype.addDays = function( /**Number*/val ) {   
	return this.add('day',val);
};

Date.prototype.today = function() {
	return new Date().trunc();
}
