﻿/****************************************
*               RCDateToken
*****************************************/

/// <summary>
/// Richer Components - Date Token Class
/// </summmary>
function RCDateToken( token )
{
	this.DatePart = RCDatePart.None;
    this.Length = 0;
	this.LengthFixed = false;
	this.Token = '';
	this.PosStart = 0;
	this.NonStandar = false;
	this.Index = -1;
} // RCDateToken

/****************************************
*               RCDatePicker
*****************************************/

/// <summary>
/// Richer Components - Date Picker Class
/// </summmary>
function RCDatePicker( calendarObject )
{
    this.Calendar = calendarObject;
    this.ID = calendarObject.ID + '_Picker';
    this.Tokens = new Array();
    this.Format = 'MM/dd/yyyy';
    this.TextBoxSize = 250;
    this.TextBoxElement = null;
    
    this._currentTokenIndex = -1;
    this._currentType = ''; // current text typed by the user
    this._currentTypeIndex = 0; // index of the last match for the current type
} // RCDatePicker

/// <summary>
/// Richer Components - Date Picker Class Prototype
/// </summary>
RCDatePicker.prototype = 
{
    /// <summary>
    /// Initializes this picker instance
    /// </summary>
    Init : function()
    {
        this._GetDateTimeTokens();
		this._FormatDateTimeTokens();
    }, // Init
    
    /// <summary>
    /// Shows the current date to the user
    /// </summary>
    ShowDate : function( updateSelection )
    {
        if ( this.GetTextBoxControl() != null )
        {
            if ( this.Calendar.SelectedDate != null )
            {
                this.GetTextBoxControl().value = this._BuildDateString( this.Calendar.SelectedDate );
                
                if ( updateSelection )
                    this.UpdateSelectionAtCaretPos();
            }
            else
            {
                //clear the text box
                this.GetTextBoxControl().value = '';
            }
        }
    }, // ShowDate
    
    /// <summary>
    /// Search and returns the next token using the given
    /// initial reference token index
    /// </summary>
    _SearchNextToken : function( referenceTokenIndex )
	{
		var token = null;
		
		for (var i = referenceTokenIndex + 1; i < this.Tokens.length; i++)
		{
			if ( this.Tokens[i].NonStandar )
				continue;
				
			token = this.Tokens[i];
			break;
		}
		
		return token;
	}, // _SearchNextToken
	
	/// <summary>
    /// Search and returns the previous token using the given
    /// initial reference token index
    /// </summary>
	_SearchPreviousToken : function( referenceTokenIndex )
	{
		var token = null;
		
		for (var i = (referenceTokenIndex - 1); i >= 0; i--)
		{
			if ( this.Tokens[i].NonStandar )
				continue;
				
			token = this.Tokens[i];
			break;
		}
		
		return token;
	}, // _SearchPreviousToken
    
    /// <summary>
    /// Obtain the date time tokens on the datetime format
    /// </summary>
    _GetDateTimeTokens : function()
	{
		var temp = null;
		var token = null;
		var character = null;

		for (var i = 0; i < this.Format.length; i++)
		{
			character = this.Format.charAt(i);
			
			if ( temp == null )
			{
				temp = character;
				
				if ( i + 1 < this.Format.length )
					continue;
			} 
			else if ( temp.charAt(temp.length - 1) == character )
			{
				temp += '' + character;
				
				if ( i + 1 < this.Format.length )
					continue;
			}

			token = new RCDateToken();
			token.Token = temp;
			this.Tokens[ this.Tokens.length ] = token;
			
			temp = character;
			
			if ( i + 1 >= this.Format.length && temp.charAt(temp.length - 1) != character )
			{
				token = new RCDateToken();
				token.Token = temp;
				this.Tokens[ this.Tokens.length ] = token;
			}
		}
	}, // _GetDateTimeTokens
	
	/// <summary>
    /// Increment the date
    /// </summary>
	_IncrementDate : function( increment, datePart, dateObject )
	{
		var day = dateObject.getDate();
		var month = dateObject.getMonth();
		var year = dateObject.getFullYear();
		var second = dateObject.getSeconds();
		var minute = dateObject.getMinutes();
		var hour = dateObject.getHours();
		var newDate = null;
		
		switch ( datePart ) 
		{
			case RCDatePart.Day:
				day += increment;
				break;
				
			case RCDatePart.Month:
				month += increment;
				break;
				
			case RCDatePart.Year:
				year += increment;
				break;
				
			case RCDatePart.Second:
				second += increment;
				break;
				
			case RCDatePart.Minute:
				minute += increment;
				break;
				
			case RCDatePart.Hour:
				hour += increment;
				break;
				
			case RCDatePart.AMPM:
				if ( hour >= 12 )
					hour -= 12;
				else
					hour += 12;
				break;
		
			default:
				alert( 'Invalid date part \'' + datePart + '\'' );
				break;
		}
		
		newDate = new Date( year, month, day, hour, minute, second );
		
		return newDate;
	}, // _IncrementDate
    
    /// <summary>
    /// Format the date time tokens for 
    /// this picker instance
    /// </summary>
    _FormatDateTimeTokens : function()
	{
		var pos = 0;
		var token = null;
		
		for (var i = 0; i < this.Tokens.length; i++)
		{
		    token = this.Tokens[i];
		
			switch ( token.Token )
			{						
				case 'dd': case 'd':
					token.Length = 2;
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Day;
					break;
					
				case 'HH': case 'H':
				case 'hh': case 'h':
					token.Length = 2;
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Hour;
					break;
				
				case 'mm': case 'm':
					token.Length = 2;
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Minute;
					break;
					
				case 'MM': case 'M':
					token.Length = 2;
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Month;
					break;
					
				case 'ss': case 's':
					token.Length = 2;
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Second;
					break;
					
				case 'tt': case 't':
					token.Length = 2;
					token.LengthFixed = true;
					token.DatePart = RCDatePart.AMPM;
					break;
					
				case 'yyyy':
					token.Length = 4;
					token.DatePart = RCDatePart.Year;
					break;
					
				case 'ddd':
					token.IsText = true;
					token.Length = $MaxStringLength( this.Calendar.AbbreviatedDayNames );
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Day;
					break;
					
				case 'dddd':
					token.IsText = true;
					token.Length = $MaxStringLength( this.Calendar.DayNames );
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Day;
					break;
					
				case 'MMM':
					token.IsText = true;
					token.Length = $MaxStringLength( this.Calendar.AbbreviatedMonthNames );
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Month;
					break;
					
				case 'MMMM':
					token.IsText = true;
					token.Length = $MaxStringLength( this.Calendar.MonthNames );
					token.LengthFixed = true;
					token.DatePart = RCDatePart.Month;
					break;
					
				default:
					token.IsText = true;
					token.Length = token.Token.length;
					token.NonStandar = true;
					break;
			}
			
			token.Index = i;
			token.PosStart = pos;
			pos += token.Length;
		}
	}, // _FormatDateTimeTokens
    
    /// <summary>
    /// Build and returns the date string 
    /// for the given date object
    /// </summary>
    _BuildDateString : function( dateObject )
	{
		var dateString = '';
		var tempValue = null;
		var token = null;
		
		for (var i = 0; i < this.Tokens.length; i++)
		{
		    token = this.Tokens[i];
		    
			switch ( token.Token )
			{
				case 'd': case 'dd':
					tempValue = dateObject.getDate();
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, (token.Token.length == 1) ? ' ': '0', true );
					dateString += tempValue;
					break;
					
				case 'M': case 'MM':
					tempValue = dateObject.getMonth() + 1;
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, (token.Token.length == 1) ? ' ': '0', true );
					dateString += tempValue;
					break;
				
				case 'h': case 'hh':
					tempValue = dateObject.getHours();
					
					if ( tempValue >= 12 )
						tempValue -= 12;
					if ( tempValue == 0 )
						tempValue = 12;
					
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, (token.Token.length == 1) ? ' ': '0', true );
					dateString += tempValue;
					break;
				
				case 'H': case 'HH':
					tempValue = dateObject.getHours();
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, (token.Token.length == 1) ? ' ': '0', true );
					dateString += tempValue;
					break;
					
				case 'm': case 'mm':
					tempValue = dateObject.getMinutes();
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, (token.Token.length == 1) ? ' ': '0', true );
					dateString += tempValue;
					break;
					
				case 's': case 'ss':
					tempValue = dateObject.getSeconds();
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, (token.Token.length == 1) ? ' ': '0', true );
					dateString += tempValue;
					break;
					
				case 'yyyy':
					dateString += dateObject.getFullYear();						
					break;
					
				case 't': case 'tt':
					tempValue = dateObject.getHours();
					
					if ( tempValue >= 12 )
						tempValue = 'PM';
					else
						tempValue = 'AM';
						
					dateString += tempValue;
					break;
					
				case 'ddd':
					tempValue = this.Calendar.AbbreviatedDayNames[ dateObject.getDay() ];
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, ' ', false );
					dateString += tempValue;
					break;
					
				case 'dddd':
					tempValue = this.Calendar.DayNames[ dateObject.getDay() ];
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, ' ', false );
					dateString += tempValue;
					break;
					
				case 'MMM':
					tempValue = this.Calendar.AbbreviatedMonthNames[ dateObject.getMonth() ];
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, ' ', false );
					dateString += tempValue;
					break;
					
				case 'MMMM':
					tempValue = this.Calendar.MonthNames[ dateObject.getMonth() ];
					if ( token.LengthFixed )
						tempValue = $FixString( tempValue, token.Length, ' ', false );
					dateString += tempValue;
					break;
					
				default:
					dateString += token.Token;
					break;
			}
		}
		
		return dateString;
	}, // _BuildDateString

    /// <summary>
    /// Returns the TextBox control of this picker instance
    /// </summary>
    GetTextBoxControl : function()
    {
        if ( this.TextBoxElement == null )
            this.TextBoxElement = $( this.ID );
            
        return this.TextBoxElement;
    }, // GetTextBoxControl
    
    /// <summary>
    /// Selects and focus the given token on 
    /// the associated text box for this picker instance
    /// </summary>
    SelectToken : function( token )
    {
        $SetSelectionRange( this.GetTextBoxControl(), token.PosStart, token.Length);
    }, // SelectToken

    /// <summary>
    /// On Mouse Up Event
    /// </summary>
    _OnMouseUp : function()
    {
        if ( this.Calendar.SelectedDate != null )
            this.UpdateSelectionAtCaretPos();
    }, // _OnMouseUp
    
    /// <summary>
    /// On Mouse Down Event
    /// </summary>
    _OnMouseDown : function()
    {
        if ( this.Calendar.SelectedDate != null )
            $SetSelectionRange( this.GetTextBoxControl(), 0, 0 );
    }, // _OnMouseDown
    
    /// <summary>
    /// On Key Up Event
    /// </summary>
    _OnKeyUp : function()
    {
        return false;
    }, // _OnKeyUp
    
    /// <summary>
    /// On Key Press Event
    /// </summary>
    _OnKeyPress : function()
    {
        return false;
    }, // _OnKeyPress
    
    /// <summary>
    /// On Key Down Event
    /// </summary>
    _OnKeyDown : function( eventObj )
    {
        var key = null;
		var currentToken = null;
		var returnValue = false;
		var index = null;
		
		//Exit function if not selected date present
		if ( this.Calendar.SelectedDate == null )
		    return;
		
		key = eventObj.which ? eventObj.which : eventObj.keyCode; 
		currentToken = this.Tokens[ this._currentTokenIndex ];
		
		switch ( key )
		{
			case Keys.UpArrow:
			    if ( ! this.Calendar.ReadOnly )
				    this.Calendar._SelectDate( this._IncrementDate( 1, currentToken.DatePart, this.Calendar.SelectedDate ), true );
				break;
				
			case Keys.DownArrow:
			    if ( ! this.Calendar.ReadOnly )
				    this.Calendar._SelectDate( this._IncrementDate( -1, currentToken.DatePart, this.Calendar.SelectedDate ), true );
				break;
				
			case Keys.RightArrow:
			case Keys.Tab:
			case Keys.Space:
			case Keys.Enter:
			case Keys.PageUp:
			case Keys.End:
				currentToken = this._SearchNextToken( currentToken.Index );
				if ( currentToken == null )
					currentToken = this.Tokens[ this._currentTokenIndex ];
				else
					this._currentTokenIndex = currentToken.Index;
				break;
				
			case Keys.LeftArrow:
			case Keys.Delete:
			case Keys.Backspace:
			case Keys.PageDown:
			case Keys.Escape:
			case Keys.Home:
				currentToken = this._SearchPreviousToken( currentToken.Index );
				if ( currentToken == null )
					currentToken = this.Tokens[ this._currentTokenIndex ];
				else
					this._currentTokenIndex = currentToken.Index;
				break;
				
			case Keys.Shift:
            case Keys.Ctrl:
            case Keys.Alt:
            case Keys.CapsLock:
                //Do nothing
                break;
						
			default:
			    var character = null;
			    var array = null;
			    
			    character = String.fromCharCode( key );
			    
			    //If the character is a number
			    //Codes: 0 = 48, ..., 9 = 57
			    if ( key >= 48 && key <= 57 )
			    {
			        /*
			        //Clear any invalid value
			        if ( this._currentType != '' && 
			             ! IsNumeric( this._currentType ) )
			        {
			            this._currentType = '';
			        }
			        */
			    }
			    //If the character is a letter
			    //Codes: a = 65, ..., z = 90
			    else if ( key >= 65 && key <= 90 && ! this.Calendar.ReadOnly )
			    {
			        //Clear any invalid value
			        if ( this._currentType != '' && 
			             IsNumeric( this._currentType ) )
			        {
			            this._currentType = '';
			        }
			        
			        //Get the datepart array
			        array = this._GetDatePartArray( currentToken );
	
			        if ( array != null )
			        {
			            //save current index
			            index = this._currentTypeIndex;
			            //Update typed value
			            this._currentType += character;
			            //get the match index
			            this._currentTypeIndex = this._FindMatch( array, this._currentType, this._currentTypeIndex );
			            
			            if ( this._currentTypeIndex == null )
			            {
			                this._currentType = character;
			                this._currentTypeIndex = index;
			                
			                //try again
			                this._currentTypeIndex = this._FindMatch( array, this._currentType, this._currentTypeIndex );
			            }
			            
			            if ( this._currentTypeIndex == null )
			            {
			                this._currentType = '';
			                this._currentTypeIndex = index;
			            }
			            else
		                {
			                //select the date
			                this.Calendar._SelectDate(  
			                    this._SetDatePart
			                    ( 
			                        this.Calendar.SelectedDate, 
			                        currentToken.DatePart, 
			                        this._currentTypeIndex
			                    ),
			                    true
			                );
    			            
			                //Update index
			                this._currentTypeIndex++;
			            }
			        }	        
			    }
				break;
		}
		
		this.ShowDate();
		this.SelectToken( currentToken );

		return returnValue;
    }, // _OnKeyDown
    
    /// <summary>
    /// On Focus Event Handler
    /// </summary>
    _OnFocus : function()
    {
        if ( this.Calendar.Behavior == RCBehavior.LitePicker )
        {
            this.Calendar.PopupControlAnchorID = this.ID;
            this.Calendar.Show();
        }
    }, // _OnFocus
    
    /// <summary>
    /// Increment the date
    /// </summary>
	_SetDatePart : function( dateObject, datePart, value )
	{
		var newDate = dateObject.clone();
		var daysInMonth = null;
		var newValue = null;
		
		switch ( datePart ) 
		{
			case RCDatePart.Day:
			    daysInMonth = $DaysInMonth( newDate.getMonth(), newDate.getFullYear() );
				newValue = ( value - newDate.getDay() ) + newDate.getDate();
				
				//stay within the current month
				if ( newValue > daysInMonth )
				    newValue -= 7;
				else if ( newValue < 1 )
				    newValue += 7;
				    
				newDate.setDate( newValue );
				break;
				
			case RCDatePart.Month:
				newDate.setMonth( value );
				break;
		
			default:
				alert( 'Invalid date part \'' + datePart + '\' for this function' );
				break;
		}
		
		return newDate;
	}, // _SetDatePart
    
    /// <summary>
    /// Search for a math index on the given array, returns null if
    /// no match was found
    /// </summary>
    _FindMatch : function( array, pattern, startIndex )
    {
        var match = null;
        var rewind = false;
        
        if ( startIndex >= array.length )
            startIndex = 0;
        
        for (var i = startIndex; i < array.length && match == null; i++)
        {
            if ( array[i].toLowerCase().indexOf( pattern.toLowerCase() ) == 0 )
            {
                match = i;
            }
            else if ( rewind && i == startIndex )
            {
                break;
            }
            else if ( i + 1 >= array.length && startIndex > 0 )
            {
                i = -1;
                rewind = true;
            }
        }
        
        return match;
    }, // _FindMatch
    
    /// <summary>
    /// Returns the array for the date part of the given token
    /// </summary>
    _GetDatePartArray : function( token )
    {
        var array = null;
        
        switch ( token.DatePart )
        {
            case RCDatePart.Day:
                if ( token.Token == 'ddd' )
                    array = this.Calendar.AbbreviatedDayNames;
                else if ( token.Token == 'dddd' )
                    array = this.Calendar.DayNames;
                break;
                
            case RCDatePart.Month:
                if ( token.Token == 'MMM' )
                    array = this.Calendar.AbbreviatedMonthNames;
                else if ( token.Token == 'MMMM' )
                    array = this.Calendar.MonthNames;
                break;
        
            default:
                array = null;
                break;
        }
        
        return array;
    }, // _GetDatePartArray
    
    /// <summary>
    /// On Select Event
    /// </summary>
    _OnSelect : function()
    {
        return false;
    }, // _OnSelect
    
    /// <summary>
    /// On Select Start Event
    /// </summary>
    _OnSelectStart : function()
    {
        var returnValue = true;
        
        if( window.event != null ) 
		{
			window.event.returnValue = true; 
			window.event.cancelBubble = true; 
		}
		
		return returnValue; 
    }, // _OnSelectStart
    
    /// <summary>
    /// On Drag Start Event
    /// </summary>
    _OnDragStart : function()
    {
        return false;
    }, // _OnDragStart
    
    /// <summary>
    /// Update the selection at the caret current position
    /// on this picker instance text box control
    /// </summary>
    UpdateSelectionAtCaretPos : function()
    {
        this.SelectTokenAt( $GetCaretPosition( this.GetTextBoxControl() ) );
    }, // UpdateSelectionAtCaretPos
    
    /// <summary>
    /// Select the token within the given position
    /// (column position within this picker instance text box control)
    /// </summary>
    SelectTokenAt : function( position )
    {
        var token = null;
			
		token = this.GetTokenAt( position );
		this._currentTokenIndex = token.Index;
		this.SelectToken( token );
    }, // SelectTokenAt
    
    /// <summary>
    /// Returns the token at the given position
    /// within this picker instance text box control
    /// </summary>
    GetTokenAt : function( position )
    {
        var token = null;
		var i = 0;
		var standarTokenFound = false;
		var takeAny = false;
		var increment = 1;
		
		while ( token == null )
		{
			if ( ! this.Tokens[i].NonStandar )
				standarTokenFound = true;
			
			if ( takeAny && ! this.Tokens[i].NonStandar )
				token = this.Tokens[i];
			else if ( position <= (this.Tokens[i].PosStart + this.Tokens[i].Length) )
				token = this.Tokens[i];
			
			if ( token != null && token.NonStandar )
			{	
				if ( increment > 0 && standarTokenFound )
					increment = -1;
					
				token = null;
				takeAny = true;
			}
									
			i += increment;
				
			if ( i < 0 || i >= this.Tokens.length )
				break;
		}
					
		return token
    }, // GetTokenAt
    
    /// <summary>
    /// Returns the HTML markup
    /// </summary>
    ToHTML : function()
    {
        var textBox = null;
        
        //create the picker text box
        textBox = new RCHTMLObject( 'input' );
        textBox.Attributes.Add('id', this.ID);
        textBox.Attributes.Add('type', 'text');
        textBox.Attributes.Add('class', this.Calendar.Style.Picker);
        textBox.Style.Add('width', this.TextBoxSize + 'px');
        
        //Attach events
        textBox.Attributes.Add('onMouseUp', 'return ' + this.Calendar.ID + '.Picker._OnMouseUp()');
        textBox.Attributes.Add('onMouseDown', 'return ' + this.Calendar.ID + '.Picker._OnMouseDown()');
        textBox.Attributes.Add('onKeyUp', 'return ' + this.Calendar.ID + '.Picker._OnKeyUp()');
        textBox.Attributes.Add('onKeyPress', 'return ' + this.Calendar.ID + '.Picker._OnKeyPress()');
        textBox.Attributes.Add('onKeyDown', 'return ' + this.Calendar.ID + '.Picker._OnKeyDown( event )');
        textBox.Attributes.Add('onSelect', 'return ' + this.Calendar.ID + '.Picker._OnSelect()');
        textBox.Attributes.Add('onSelectStart', 'return ' + this.Calendar.ID + '.Picker._OnSelectStart()');
        textBox.Attributes.Add('onDragStart', 'return ' + this.Calendar.ID + '.Picker._OnDragStart()');
        textBox.Attributes.Add('onFocus', 'return ' + this.Calendar.ID + '.Picker._OnFocus()');
        //textBox.Attributes.Add('onBlur', 'return ' + this.Calendar.ID + '.Hide()');
        
        return textBox.ToHTML();
    } // ToHTML
} // RCDatePicker.prototype