/*
 * Constructor
 */
function DateSelector( sName, sValue, sPath )
{
        var oSelf = this

        // Путь к картинкам
        this.sClassPath = sPath

        // Нода из BuildSelector()
        this.oSelector = null

        // Название месяцев
        this.aMonths = new Array( 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь')
        this.bMonths = new Array( 'Января', 'Февраля', 'Марта', 'Апреля', 'Мая', 'Июня', 'Июля', 'Августа', 'Сентября', 'Октября', 'Ноября', 'Декабря')
        // Корневой элемент контрола
        this.oNode = document.createElement('table')
        this.oNode.className = 'DS_Control'

        // input[@type='text'] в документе
        this.oInput = this.oNode.insertRow(-1).insertCell(-1).appendChild( this.CreateElement('input', 'text') )
        this.oInput.parentNode.style.borderRightWidth = '0'
        this.oInput.name = sName
        this.oInput.value = sValue
        this.oInput.onblur = function() { oSelf.ParseStrToValue(oSelf.oInput.value) }
        this.oInput.onclick = function() { oSelf.HideSelector() }

        // Кнопка
        this.oButton = this.oNode.rows[this.oNode.rows.length-1].insertCell(-1).appendChild( document.createElement('div') )
        this.oButton.parentNode.style.borderLeftWidth = '0'
        this.oButton.appendChild( document.createElement('img') )
        this.oButton.lastChild.src = this.sClassPath + 'dateselector_ar-d.gif'
        this.oButton.onclick = function(oEvent)
        {
                if( !oSelf.oSelector )
                {
                        oSelf.ShowSelector(oSelf.iYear, oSelf.iMonth)
                }
                else
                {
                        oSelf.HideSelector()
                }
                try { event.cancelBubble = true } catch (oException) { oEvent.stopPropagation() }
        }

        // input[@type='hidden'] в документе
        this.oHidden = this.oNode.lastChild.lastChild.lastChild.appendChild( this.CreateElement('input', 'hidden') )
        this.oHidden.name = sName

        // Автозакрывание селектора
        //document.onclick = function() { oSelf.HideSelector() }

        // Текущее значение даты
        this.ParseStrToValue(this.oInput.value)

        this.sClientName = this.GetClientName()

        return this
}

/*
 * Private
 */

// Установить внутренние значения даты
DateSelector.prototype.SetValue = function( iYear, iMonth, iDay )
{
        if(
                ( (iYear > 9 && iYear < 100) || (iYear > 999 && iYear < 10000) ) &&
                ( iMonth > 0 && iMonth < 13 ) &&
                ( iDay <= this.GetDaysCountByMonth(iYear, iMonth) )
        )
        {
                this.iYear = parseInt(iYear,10);
                this.iMonth = parseInt(iMonth,10);
                this.iDay = parseInt(iDay,10);
                //this.oInput.value = iDay + ' ' + this.bMonths[iMonth-1].toLowerCase() + ' ' + iYear
                this.oInput.value = iDay + '/' + iMonth + '/' + iYear
                this.oHidden.value = iDay + '-' + iMonth + '-' + iYear
        }
        else
        {
                //this.oInput.value = this.iDay + ' ' + this.bMonths[this.iMonth-1].toLowerCase() + ' ' + this.iYear
                this.oInput.value = iDay + '/' + iMonth + '/' + iYear
                this.oHidden.value = this.iDay + '-' + this.iMonth + '-' + this.iYear
        }
}

// Отпарсить строку и попробовать получить три int: iYear, iMonth, iDay
DateSelector.prototype.ParseStrToValue = function( sStr )
{
        var re, aResult

        // '2003-4-16'
        re = /^(\d{2,4})\-(\d{1,2})\-(\d{1,2})$/
        aResult = re.exec(sStr)
        if( aResult )
        {
                this.SetValue( aResult[1], aResult[2], aResult[3] )
                return true
        }

        // '16.4.2003', '16/4/2003', '16 4 2003'
        re = /^(\d{1,2})[\.\/\s](\d{1,2})[\.\/\s](\d{2,4})$/
        aResult = re.exec(sStr)
        if( aResult )
        {
                this.SetValue( aResult[3], aResult[2], aResult[1] )
                return true
        }

        // '16-апр-2003', '16 апр 2003'
        re = /^(\d{1,2})[\s\.\-]([a-я]+)[\s\.\-](\d{2,4})$/i
        aResult = re.exec(sStr)
        if( aResult )
        {
                for( var i=0; i<this.aMonths.length; i++ )
                {
                        var sMonthNormalized = this.aMonths[i].toLowerCase()
                        if( sMonthNormalized.indexOf(aResult[2].toLowerCase()) == 0 )
                        {
                                this.SetValue( aResult[3], i+1, aResult[1] )
                                return true
                        }
                }
        }

        return false
}

// Вернуть количество дней в заданном месяце
DateSelector.prototype.GetDaysCountByMonth = function( iYear, iMonth )
{
        var dDate = new Date(iYear,iMonth,-1)
        return (dDate.getDate() + 1)
}

// Вернуть "правильное" значение месяца, т.е. вместо 13 -- 1 и вместо 0 -- 12
DateSelector.prototype.GetMonthNumber = function( iMonth )
{
        return ( iMonth < 1 || iMonth > 12 ) ? Math.abs( iMonth-12 ) : iMonth
}

// Вернуть "правильное" значение года по месяцу, т.е. вместо 2003(13) -- 2004(1)
DateSelector.prototype.GetYearNumber = function( iYear, iMonth )
{
        if ( iMonth < 1  ) return ( iYear-1 )
        if ( iMonth > 12 ) return ( parseInt(iYear)+1 )
        return iYear
}

// Приделать ноду с Selector к контролу
DateSelector.prototype.ShowSelector = function( iYear, iMonth )
{
        if ( !this.oSelector )
        {
                this.oButton.className = 'Pushed'
                this.oSelector = this.BuildSelector( this.BuildYearMonthSelector(iYear, iMonth), this.BuildDaySelector(iYear, iMonth) )
                this.oNode.parentNode.appendChild( this.oSelector )
        }
}

// Убить ноду с Selector
DateSelector.prototype.HideSelector = function()
{
        if ( this.oSelector && this.oSelector.parentNode )
        {
                this.oButton.className = ''
                this.oSelector.parentNode.removeChild(this.oSelector)
                this.oSelector = null
        }
}

// "Перерисовать" ноду с Selector к контролу
DateSelector.prototype.RedrawSelector = function( iYear, iMonth )
{
        this.HideSelector()
        this.ShowSelector(iYear, iMonth)
}

// Сгенерить ноду с выбором даты
DateSelector.prototype.BuildSelector = function( oYearMonthSelector, oDaySelector )
{
        var oNode = document.createElement('div')

        oNode.appendChild( document.createElement('fieldset') )
        oNode.lastChild.appendChild(oYearMonthSelector)
        oNode.lastChild.appendChild(oDaySelector)

        oNode.className = 'DS_Selector'
        // Эта заплатка сделана потому, что Moz и MSIE по-разному трактуют CSS
        oNode.style.width = ( this.sClientName == 'msie' ) ? '1%' : ''

        return oNode
}

// Сгенерить ноду с выбором месяца и года. Текущие значения установить в iYear, iMonth
// Такая замороченная нода получилась coz of moz
DateSelector.prototype.BuildYearMonthSelector = function( iYear, iMonth )
{
        var oCurrentRow, oNode
        var oSelf = this

        oNode = document.createElement('div')
        oNode.className = 'DS_YearmonthSelector'
        oNode.appendChild( document.createElement('table') )

        oCurrentRow = oNode.lastChild.insertRow(-1)
        oCurrentRow.insertCell(-1).appendChild( document.createElement('img') )
        oCurrentRow.lastChild.lastChild.src = this.sClassPath + 'dateselector_ar-l.gif'
        oCurrentRow.lastChild.lastChild.alt = this.aMonths[ this.GetMonthNumber(iMonth-1) - 1 ] + ' ' + this.GetYearNumber(iYear, iMonth-1)
        oCurrentRow.lastChild.lastChild.setAttribute('month', this.GetMonthNumber(iMonth-1))
        oCurrentRow.lastChild.lastChild.setAttribute('year', this.GetYearNumber(iYear, iMonth-1))
        oCurrentRow.lastChild.lastChild.onclick = function() { oSelf.RedrawSelector( this.getAttribute('year'), this.getAttribute('month') ) }

        oCurrentRow.insertCell(-1).appendChild( document.createTextNode( this.aMonths[iMonth-1] + ' ' + iYear ) )
        oCurrentRow.lastChild.style.textAlign = 'center'

        oCurrentRow.insertCell(-1).appendChild( document.createElement('img') )
        oCurrentRow.lastChild.style.textAlign = 'right'
        oCurrentRow.lastChild.lastChild.src = this.sClassPath + 'dateselector_ar-r.gif'
        oCurrentRow.lastChild.lastChild.alt = this.aMonths[ this.GetMonthNumber(parseInt(iMonth)+1) - 1 ] + ' ' + this.GetYearNumber(iYear, parseInt(iMonth)+1)
        oCurrentRow.lastChild.lastChild.setAttribute('month', this.GetMonthNumber(parseInt(iMonth)+1))
        oCurrentRow.lastChild.lastChild.setAttribute('year', this.GetYearNumber(iYear, parseInt(iMonth)+1))
        oCurrentRow.lastChild.lastChild.onclick = function() { oSelf.RedrawSelector( this.getAttribute('year'), this.getAttribute('month') ) }

        return oNode
}

// Сгенерить ноду с выбором дня месяца iMonth года iYear
DateSelector.prototype.BuildDaySelector = function( iYear, iMonth )
{
        var oNode, i, j, oCurrentRow
        var oSelf = this
        var aDayOfWeekTitles = new Array('пн','вт','ср','чт','пт','сб','вс')
        var dFirstDay = new Date(iYear,iMonth-1,0)
        var iDaysCount = this.GetDaysCountByMonth(iYear,iMonth)

        oNode = document.createElement('table')
        oNode.className = 'DS_DaySelector'

        // Нужно отбить позицию до требумого дня недели
        oCurrentRow = oNode.insertRow(-1)

        for ( i=0; i<dFirstDay.getDay(); i++ ) oCurrentRow.insertCell(-1)

        // Собственно числа месяца
        for ( i=0; i<iDaysCount; i++ )
        {
                if ( (i+dFirstDay.getDay())%7 == 0 ) oCurrentRow = oNode.insertRow(-1)
                oCurrentRow.insertCell(-1).appendChild( document.createElement('span') ).appendChild( document.createTextNode(i+1) )
                oCurrentRow.lastChild.className = ( this.iYear == iYear && this.iMonth == iMonth && this.iDay == i+1 ) ? 'DS_Selected' : ''
                oCurrentRow.lastChild.onclick = function()
                {
                        // Процедура выбора дня
                        oSelf.SetValue( iYear, iMonth, this.lastChild.lastChild.nodeValue )
                        oSelf.HideSelector()
                }
        }

        // Заголовок с днями недели
        oNode.createTHead().insertRow(0)
        for ( i=0; i<aDayOfWeekTitles.length; i++ )
        {
                oNode.tHead.rows[0].insertCell(i).appendChild( document.createTextNode(aDayOfWeekTitles[i]) )
        }

        return oNode
}

// Определение типа клиекта. Блин, не хотел я этого делать, но пришлось...
// Возвращает: msie|opera|mozilla
DateSelector.prototype.GetClientName = function()
{
        // Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0) Opera 7.10 [en]
        if( navigator.userAgent.indexOf('Opera') != -1 ) return 'opera'

        // Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.3) Gecko/20030312
        if( navigator.userAgent.indexOf('Gecko') != -1 ) return 'mozilla'

        // Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705)
        if( navigator.userAgent.indexOf('MSIE') != -1 ) return 'msie'

        if( navigator.userAgent.indexOf('Safari') != -1 ) return 'msie'
}

// Надо бы передеать createElement ...
// TODO: посмотреть такую функциональность на других браузерах
DateSelector.prototype.CreateElement = function( sNodeName, sTypeAttr )
{
        if( this.sClientName == 'msie' )
        {
                return document.createElement('<'+sNodeName+' type="'+sTypeAttr+'"/>')
        }
        else
        {
                var oElement = document.createElement(sNodeName)
                oElement.setAttribute('type', sTypeAttr)
                return oElement
        }
}

