var __textbox_version = 1;

///<Documentation>
///<Class name="TextBox">
function TextBox(id, caption, text, watermark, mask, required)
{
    this.Constructor(id, caption, text, watermark, mask, required);
}

///<Constructor>
///<summary>TextBox constructor</summary>
///<param name="id" type="string" required="false">
///     The id to be assigned to the HTML control generated from the Render()
///     function. If this is not provided, than a GUID is automatically assigned.
///</param>
///<param name="caption" type="string" required="false">
///     The name to be displayed as a caption for the textbox
///</param>
///<param name="text" type="string" required="false">
///     The value to be entered into the textbox
///</param>
///<param name="watermark" type="string" required="false">
///     Watermark text to be placed in the text field whenever it is blank
///</param>
TextBox.prototype.Constructor = function(id, caption, text, watermark, mask, required)
{
    if (!__base_version || __base_version < 1)
        new Exception("TextBox", "Constructor", "The TextBox object requires the inclusion of the base.js file.").Throw();
    if (!__string_version || __string_version < 1)
        new Exception("TextBox", "Constructor", "The TextBox object requires the inclusion of the string.js file.").Throw();

    var tb = $getControl(id + '_obj');
    if (tb)
        tb = eval(tb.value);

    this.Control = $getControl(id + '_input');
    this.ID = id ? id : __getGUID();
    this.Caption = caption ? caption : tb ? tb.Caption : '';
    this.Disabled = tb ? tb.Disabled : false;
    this.Watermark = watermark ? watermark : tb ? tb.Watermark : '';
    this.Mask = mask ? mask : tb ? tb.Mask : '';
    this.IsNumeric = tb ? tb.IsNumeric : false;
    this.IsRequired = required ? required : tb ? tb.IsRequired : false;
    this.Type = 'TextBox';

    this.Text = text ? text : tb ? this.Unmask($getControl(id + '_input').value) : '';

}
///</Constructor>

///<Methods>
///<Method name="AddEvents">
///<summary>
///     Adds the events to a text box. This is for adding events to the control
///     after it has been rendered on an HTML page. Some browsers do not adequately
///     support adding the events when a control is appended as a child. In
///     those cases, call this methods after appending the control.
///</summary>
///<returns>HTML object</returns>
TextBox.prototype.AddEvents = function()
{
    var tb = $getControl(this.ID + '_input');
    if (!tb) return;

    if (tb.onblur && !tb.onblur.IsEmpty())
        new Event('onblur').AddToElement(tb, tb.onblur);

    if (tb.onfocus && !tb.onfocus.IsEmpty())
        new Event('onfocus').AddToElement(tb, tb.onfocus);

    if (tb.onkeyup && !tb.onkeyup.IsEmpty())
        new Event('onkeyup').AddToElement(tb, tb.onkeyup);

    if (tb.onkeydown && !tb.onkeydown.IsEmpty())
        new Event('onkeydown').AddToElement(tb, tb.onkeydown);
}
///</Method>

///<Method name="Render">
///<summary>Renders the textbox</summary>
///<returns>HTML object</returns>
TextBox.prototype.Render = function()
{
    var textbox = document.createElement("table"),
			row = textbox.insertRow(),
			caption = row.insertCell(),
			text = row.insertCell(),
			inp = document.createElement("<INPUT TYPE='text'>"),
			obj = document.createElement("<INPUT Type='hidden'>");

    textbox.id = this.ID;

    obj.id = this.ID + '_obj';
    obj.value = '(' + JSONstring.make(this) + ')';

    inp.id = this.ID + '_input';
    inp.className = "textbox_text";
    inp.value = this.Text;

    if (this.IsValid)
        inp.onblur = 'new ' + this.Type + '(\'' + this.ID + '\').IsValid();';

    if (!this.Watermark.IsEmpty())
    {
        if (this.Text.IsEmpty())
        {
            inp.value = this.Watermark;
            inp.className = "textbox_watermark";
        }
        inp.onfocus = 'textbox_RemoveWatermark(\'' + this.ID + '\');';
        inp.onblur += 'textbox_SetWatermark(\'' + this.ID + '\');';
    }

    if (!this.Mask.IsEmpty() && this.Mask.indexOf('#') > -1)
    {
        if (this.Text.IsEmpty() && this.Watermark.IsEmpty())
            inp.value = this.Mask.replace(/#/g, ' ');

        inp.onkeyup = 'textbox_Mask(\'' + this.ID + '\',event);';
        inp.onkeydown = 'if(textbox_IsMasked(\'' + this.ID + '\',event)) return false;';

        if (inp.value != this.Watermark)
            inp.value = this.ApplyMask();
    }

    if (this.IsNumeric)
        inp.onkeydown != 'if(!textbox_KeyIsNumeric(event)) return false;';

    caption.className = 'textbox_caption';
    caption.innerHTML = this.Caption;

    text.appendChild(inp);
    text.appendChild(obj);
    textbox.disabled = this.Disabled;

    return textbox;
}
///</Method>

///<Method name="GetCursorPosition">
///<summary>Gets the current position of the cursor within the textbox</summary>
///<returns>Integer</returns>
///<param name="control" type="string/object" required="false">
///     Watermark text to be placed in the text field whenever it is blank
///</param>
TextBox.prototype.GetCursorPosition = function()
{
    if (!this.Control)
        return 0;

    var currentRange = document.selection.createRange(),
			workRange = currentRange.duplicate();
    this.Control.select();
    var entireRange = document.selection.createRange(),
			cursorPosition = 0;
    while (workRange.compareEndPoints('StartToStart', entireRange) > 0)
    {
        workRange.moveStart("character", -1);
        cursorPosition++;
    }
    currentRange.select();
    return cursorPosition;
}
///</Method>

///<Method name="GetCursorPosition">
///<summary>Gets the current position of the cursor within the textbox</summary>
///<returns>Integer</returns>
TextBox.prototype.SetCursorPosition = function(position)
{
    if (!this.Control)
        return;

    if (this.Control.createTextRange)
    {
        var range = this.Control.createTextRange();
        range.move('character', position);
        range.select();
    }
    else
    {
        if (this.Control.selectionStart)
        {
            this.Control.focus();
            this.Control.setSelectionRange(position, position);
        }
        else
            this.Control.focus();
    }
}
///</Method>

///<Method name="Unmask">
///<summary>Returns the displayed text of the currently selected option</summary>
///<returns>String</returns>
TextBox.prototype.Unmask = function(text)
{
    if (!text)
    {
        if (this.Text)
            text = this.Text
        else
            return '';
    }

    if (this.Watermark == text)
        return '';

    if (!this.Mask || this.Mask.IsEmpty())
        return text;

    var last = '',
			unmasked = text,
			maskChars = this.Mask.replace(/#/g, '');
    for (var i = 0; i < maskChars.length; i++)
    {
        if (last == unmasked)
            return text;
        last = unmasked;

        var maskChar = maskChars.substr(i, 1);

        if (/[\(\)\\\*\+\?\^\$\|]/.test(maskChar))
            maskChar = '\\' + maskChar;
        var re = new RegExp(maskChar);
        unmasked = unmasked.replace(re, '');
    }

    return unmasked.Trim().replace(/ /g, '');
}
///</Method>

///<Method name="ApplyMask">
///<summary>Returns the displayed text of the currently selected option</summary>
///<returns>String</returns>
TextBox.prototype.ApplyMask = function()
{
    if (!this.Mask || this.Mask.IsEmpty() || this.Mask.indexOf('#') < 0)
        return;

    var textPosition = 0
    masked = '';

    for (var i = 0; i < this.Mask.length; i++)
    {
        var maskChar = this.Mask.substr(i, 1),
				textChar = this.Text.substr(textPosition, 1);

        if (maskChar == '#')
        {
            if (this.Text.length > textPosition)
                masked += textChar;
            else
                masked += ' ';

            textPosition++;
        }
        else
            masked += maskChar;
    }

    if (this.Text.length > textPosition)
        masked += this.Text.substr(textPosition);

    return masked.Trim();
}
///</Method>

///<Method name="GetText">
///<summary>Returns the displayed text of the currently selected option</summary>
///<returns>String</returns>
TextBox.prototype.IsValid = function()
{
    if (this.Control)
        new Exception().Detach(this.Control);

    if (this.IsRequired)
        if (this.Text.IsEmpty())
    {
        if (this.Control)
            new Exception(this.Type, 'IsValid', 'This field is required.').Attach(this.Control);
        return false;
    }

    return true;
}
///</Method>

///<EventMethod name="textbox_IsMasked">
textbox_IsMasked = function(id, e)
{
    //if it's a backspace or delete, check mask before removing the character
    if (e.keyCode == 8 || e.keyCode == 46)
    {
        var tb = new TextBox(id);
        if (!tb.Control || tb.Mask.IsEmpty()) return false;

        var maskChars = new Array(),
				cursor = e.keyCode == 8 ? tb.GetCursorPosition() - 1 : tb.GetCursorPosition();
        for (var i = 0; i < tb.Mask.length; i++)
            if (tb.Mask.substr(i, 1) != '#')
            maskChars.push(i);

        if (maskChars.Contains(cursor))
            return true;
    }
    return false;
}
///</EventMethod>

///<EventMethod name="textbox_checkNumeric">
textbox_KeyIsNumeric = function(e)
{
    if (e.keyCode < 58 || (e.keyCode > 95 && e.keyCode < 106) || (e.keyCode > 34 && e.keyCode < 47) || e.keyCode == 8)
        return true;
    return false;
}
///</EventMethod>

///<EventMethod name="textbox_RemoveWatermark">
textbox_RemoveWatermark = function(id)
{
    var tb = new TextBox(id);
    if (!tb.Control) return;

    if (tb.Control.value == tb.Watermark)
    {
        tb.Control.className = "textbox_text";
        tb.Control.value = "";

        if (!tb.Mask.IsEmpty())
        {
            tb.Control.value = tb.ApplyMask();
            tb.SetCursorPosition(tb.Mask.indexOf('#'));
        }
    }
}
///</EventMethod>

///<EventMethod name="textbox_SetWatermark">
textbox_SetWatermark = function(id)
{
    var tb = new TextBox(id);
    if (!tb.Control) return;

    if (tb.Control.value == tb.Watermark || tb.Control.value.length == 0 || tb.Control.value == tb.Mask.replace(/#/g, ' '))
    {
        tb.Control.className = "textbox_watermark";
        tb.Control.value = tb.Watermark;
    }
    else
        tb.Control.className = "textbox_text";
}
///</EventMethod>

///<EventMethod name="textbox_Mask">
textbox_Mask = function(id, e)
{
    var tb = new TextBox(id);
    if (!tb.Control) return;

    if (e)
    {
        var key = e.keyCode;
        //Skip all keys that don't produce output on the screen (function keys, escape, etc.)
        if ((key > 2 && key < 8) || (key > 12 && key < 28) || (key > 32 && key < 38) || key == 40 || key == 93 || (key > 111 && key < 146))
            return true;
    }

    if (!tb.Mask || tb.Mask.IsEmpty() || tb.Mask.indexOf('#') < 0)
        return;

    var cursor = tb.GetCursorPosition(),
			textPosition = 0,
			masked = tb.Text != tb.Control.value;
    tb.Control.value = tb.ApplyMask();

    if (masked)
        cursor = tb.Mask.indexOf('#', cursor);
    else
        cursor = tb.Mask.indexAfter('#', tb.Text.length);

    if (cursor > -1)
        tb.SetCursorPosition(cursor);
    else
        tb.SetCursorPosition(tb.Control.value.length);

}
///</EventMethod>
///</Methods>
///</Class>

///<Class name="PhoneBox">
PhoneBox = function(id, caption, text, watermark, mask, required)
{
    this.Constructor(id, caption, text, watermark, mask, required);
    this.Mask = '(###) ###-####x';
    this.IsNumeric = true;
    this.Type = 'PhoneBox';
}

///<Inherits class="TextBox">
PhoneBox.prototype = new TextBox();
///</Inherits>

///<Methods>
///<Method name="IsValid">
///<summary>Determines whether the number represents a valid phone number</summary>
///<param name="control" type="string" required="false">
///     The PhoneBox control to check.
///</param>
///<returns>Boolean</returns>
PhoneBox.prototype.IsValid = function()
{
    if (this.Control)
        new Exception().Detach(this.Control);

    var phone = null,
 			isValid = false;

    if (this.Text.IsEmpty())
        isValid = !this.IsRequired;
    else
    {
        phone = new Phone(this.Text);
        isValid = phone.IsValid();
    }

    if (!isValid)
    {
        if (this.Control)
            if (phone)
            phone.Exception.Attach(this.Control);
        else
            new Exception(this.Type, 'IsValid', 'This field is required').Attach(this.Control);
        return false;
    }

    return true;
}
///</Method>
///</Methods>
///</Class>

///<Class name="FaxBox">
FaxBox = function(id, caption, text, watermark, mask, required)
{
    this.Constructor(id, caption, text, watermark, mask, required);
    this.Mask = '(###) ###-####';
    this.IsNumeric = true;
    this.Type = 'FaxBox';
}

///<Inherits class="TextBox">
FaxBox.prototype = new TextBox();
///</Inherits>

///<Methods>
///<Method name="IsValid">
///<summary>Determines whether the number represents a valid phone number</summary>
///<param name="control" type="string" required="false">
///     The PhoneBox control to check.
///</param>
///<returns>Boolean</returns>
FaxBox.prototype.IsValid = function()
{
    if (this.Control)
        new Exception().Detach(this.Control);

    var fax = null,
 			isValid = false;

    if (this.Text.IsEmpty())
        isValid = !this.IsRequired;
    else
    {
        fax = new Fax(this.Text);
        isValid = fax.IsValid();
    }

    if (!isValid)
    {
        if (this.Control)
            if (fax)
            fax.Exception.Attach(this.Control);
        else
            new Exception(this.Type, 'IsValid', 'This field is required').Attach(this.Control);
        return false;
    }

    return true;
}
///</Method>
///</Methods>
///</Class>

///<Class name="EmailBox">
EmailBox = function(id, caption, text, watermark, mask, required)
{
    this.Constructor(id, caption, text, watermark, mask, required);
    this.Type = 'EmailBox';
}

///<Inherits class="TextBox">
EmailBox.prototype = new TextBox();
///</Inherits>

///<Methods>
///<Method name="IsValid">
///<summary>Determines whether the number represents a valid phone number</summary>
///<param name="control" type="string" required="false">
///     The PhoneBox control to check.
///</param>
///<returns>Boolean</returns>
EmailBox.prototype.IsValid = function()
{
    if (this.Control)
        new Exception().Detach(this.Control);

    var email = null;

    if (this.Text.IsEmpty())
        isValid = !this.IsRequired;
    else
    {
        email = new Email(this.Text);
        isValid = email.IsValid();
    }

    if (!isValid)
    {
        if (this.Control)
            if (email)
            email.Exception.Attach(this.Control);
        else
            new Exception(this.Type, 'IsValid', 'This field is required').Attach(this.Control);
        return false;
    }

    return true;
}
///</Method>
///</Methods>
///</Class>

///<UnitTests>
try
{
    if (__JSUnit_version > 0)
    {

        __jsUnit.AddTest('TextBox', 'Render', 'Tests the Render function of the textbox object.', 'positive',
			function()
			{
			    var tb = new TextBox(null, 'Test', 'Freakin junk', 'Enter a value'),
						actual = tb.Render().outerHTML.replace(/[\r\n]/g, ''),
						expected = '<TABLE id=mytest><TBODY><TR><TD class=textbox_caption>Test</TD><TD><INPUT id=mytest_input onblur="var validate=function()&#13;&#10;{&#9;&#13;&#10;&#9;if(this.IsRequired)&#13;&#10;&#9;&#9;if(this.Text.IsEmpty())&#13;&#10;&#9;&#9;{&#13;&#10;&#9;&#9;&#9;if(this.Control)&#13;&#10;&#9;&#9;&#9;&#9;new Exception(\'TextBox\',\'IsValid\',\'This field is required.\').Attach(this.ID+\'_input\');&#13;&#10;&#9;&#9;&#9;return false;&#13;&#10;&#9;&#9;}&#13;&#10;&#13;&#10;&#9;if(this.Control)&#13;&#10;&#9;&#9;new Exception().Detach(this.ID+\'_input\');&#13;&#10;&#9;return true;&#13;&#10;};validate(this,false);textbox_SetWatermark(\'mytest\');" onfocus="textbox_RemoveWatermark(\'mytest\');" value="Freakin junk"><INPUT id=mytest_obj type=hidden value=\'({"Control":null,&#13;&#10;"ID":"mytest",&#13;&#10;"Caption":"Test",&#13;&#10;"Disabled":false,&#13;&#10;"Watermark":"Enter a value",&#13;&#10;"Mask":"",&#13;&#10;"IsNumeric":false,&#13;&#10;"IsRequired":false,&#13;&#10;"Text":"Freakin junk"})\'></TD></TR></TBODY></TABLE>';

			    return new JSUnitTestResult(actual, expected, true, tb.ID);
			});
        __jsUnit.AddTest('PhoneBox', 'Render', 'Tests the Render function of the PhoneBox object.', 'positive',
			function()
			{
			    var tb = new PhoneBox(null, 'Test', '', 'Enter a value'),
						actual = tb.Render().outerHTML.replace(/[\r\n]/g, ''),
						expected = '<TABLE><TBODY><TR><TD class=textbox_caption>Test</TD><TD><INPUT class=textbox_text id=mytest_input onblur="textbox_SetWatermark(\'mytest_input\',\'Enter a value\');" onfocus="textbox_RemoveWatermark(\'mytest_input\',\'Enter a value\');" alt="Freakin junk" value="Freakin junk"></TD></TR></TBODY></TABLE>';

			    return new JSUnitTestResult(actual, expected, true, tb.ID);
			});
    }
}
catch (err) { }
///</UnitTests>
///</Documentation>
