internal void SetUpClickableControl( WebControl clickableControl )
        {
            if( resource == null && postBack == null && script == "" )
                return;

            clickableControl.CssClass = clickableControl.CssClass.ConcatenateWithSpace( "ewfClickable" );

            if( resource != null && EwfPage.Instance.IsAutoDataUpdater ) {
                postBack = EwfLink.GetLinkPostBack( resource );
                resource = null;
            }

            Func<string> scriptGetter;
            if( resource != null )
                scriptGetter = () => "location.href = '" + EwfPage.Instance.GetClientUrl( resource.GetUrl() ) + "'; return false";
            else if( postBack != null ) {
                EwfPage.Instance.AddPostBack( postBack );
                scriptGetter = () => PostBackButton.GetPostBackScript( postBack );
            }
            else
                scriptGetter = () => script;

            // Defer script generation until after all controls have IDs.
            EwfPage.Instance.PreRender += delegate { clickableControl.AddJavaScriptEventScript( JsWritingMethods.onclick, scriptGetter() ); };
        }
        void ControlTreeDataLoader.LoadData()
        {
            var isTextarea = rows > 1;
            textBox = new WebControl( isTextarea ? HtmlTextWriterTag.Textarea : HtmlTextWriterTag.Input );
            PreRender += delegate {
                if( !isTextarea )
                    textBox.Attributes.Add( "type", masksCharacters ? "password" : "text" );
                textBox.Attributes.Add( "name", UniqueID );
                if( isTextarea )
                    textBox.Attributes.Add( "rows", rows.ToString() );
                if( maxLength.HasValue )
                    textBox.Attributes.Add( "maxlength", maxLength.Value.ToString() );
                if( readOnly )
                    textBox.Attributes.Add( "readonly", "readonly" );
                if( disableBrowserAutoComplete || autoCompleteService != null )
                    textBox.Attributes.Add( "autocomplete", "off" );
                if( suggestSpellCheck.HasValue )
                    textBox.Attributes.Add( "spellcheck", suggestSpellCheck.Value.ToString().ToLower() );

                var value = formValue.GetValue( AppRequestState.Instance.EwfPageRequestState.PostBackValues );
                var valueOrWatermark = watermarkText.Any() && !value.Any() ? watermarkText : value;
                if( isTextarea )
                    AddTextareaValue( textBox, valueOrWatermark );
                else if( !masksCharacters )
                    textBox.Attributes.Add( "value", valueOrWatermark );
            };
            Controls.Add( textBox );

            if( watermarkText.Any() ) {
                textBox.AddJavaScriptEventScript( JsWritingMethods.onfocus, "if( value == '" + watermarkText + "' ) value = ''" );
                textBox.AddJavaScriptEventScript( JsWritingMethods.onblur, "if( value == '' ) value = '" + watermarkText + "'" );
                EwfPage.Instance.ClientScript.RegisterOnSubmitStatement(
                    GetType(),
                    UniqueID + "watermark",
                    "$( '#" + textBox.ClientID + "' ).filter( function() { return this.value == '" + watermarkText + "'; } ).val( '' )" );
            }

            var jsNeededForImplicitSubmission = postBack != null || autoPostBack ||
                                                ( autoCompleteService != null && autoCompleteOption == AutoCompleteOption.PostBackOnTextChangeAndItemSelect );
            if( postBack == null && ( autoPostBack || ( autoCompleteService != null && autoCompleteOption != AutoCompleteOption.NoPostBack ) ) )
                postBack = EwfPage.Instance.DataUpdatePostBack;

            if( postBack != null )
                EwfPage.Instance.AddPostBack( postBack );
            PreRender += delegate { PostBackButton.EnsureImplicitSubmission( this, jsNeededForImplicitSubmission ? postBack : null ); };

            if( autoPostBack || ( autoCompleteService != null && autoCompleteOption == AutoCompleteOption.PostBackOnTextChangeAndItemSelect ) ) {
                PreRender += delegate {
                    // Use setTimeout to prevent keypress and change from *both* triggering post-backs at the same time when Enter is pressed after a text change.
                    textBox.AddJavaScriptEventScript(
                        JsWritingMethods.onchange,
                        "setTimeout( function() { " + PostBackButton.GetPostBackScript( postBack, includeReturnFalse: false ) + "; }, 0 )" );
                };
            }

            if( ToolTip != null || ToolTipControl != null )
                new ToolTip( ToolTipControl ?? EnterpriseWebFramework.Controls.ToolTip.GetToolTipTextControl( ToolTip ), textBox );
        }
        internal static void AddCheckBoxAttributes( WebControl checkBoxElement, Control checkBox, FormValue<bool> checkBoxFormValue,
            FormValue<CommonCheckBox> radioButtonFormValue, string radioButtonListItemId, PostBack postBack, bool autoPostBack,
            IEnumerable<string> onClickJsMethods)
        {
            checkBoxElement.Attributes.Add( "type", checkBoxFormValue != null ? "checkbox" : "radio" );
            checkBoxElement.Attributes.Add( "name", checkBoxFormValue != null ? checkBox.UniqueID : ( (FormValue)radioButtonFormValue ).GetPostBackValueKey() );
            if( radioButtonFormValue != null )
                checkBoxElement.Attributes.Add( "value", radioButtonListItemId ?? checkBox.UniqueID );
            if( checkBoxFormValue != null
                    ? checkBoxFormValue.GetValue( AppRequestState.Instance.EwfPageRequestState.PostBackValues )
                    : radioButtonFormValue.GetValue( AppRequestState.Instance.EwfPageRequestState.PostBackValues ) == checkBox )
                checkBoxElement.Attributes.Add( "checked", "checked" );

            PostBackButton.EnsureImplicitSubmission( checkBoxElement, postBack );
            var isSelectedRadioButton = radioButtonFormValue != null &&
                                        radioButtonFormValue.GetValue( AppRequestState.Instance.EwfPageRequestState.PostBackValues ) == checkBox;
            var postBackScript = autoPostBack && !isSelectedRadioButton
                                     ? PostBackButton.GetPostBackScript( postBack ?? EwfPage.Instance.DataUpdatePostBack, includeReturnFalse: false )
                                     : "";
            var customScript = StringTools.ConcatenateWithDelimiter( "; ", onClickJsMethods.ToArray() );
            checkBoxElement.AddJavaScriptEventScript( JsWritingMethods.onclick, StringTools.ConcatenateWithDelimiter( "; ", postBackScript, customScript ) );
        }
        /// <summary>
        /// Ensures that the specified control will submit the form when the enter key is pressed while the control has focus. Specify null for the post-back to
        /// rely on HTML's built-in implicit submission behavior, which will simulate a click on the submit button.
        /// </summary>
        internal static void EnsureImplicitSubmission( WebControl control, PostBack postBack, string predicate = "" )
        {
            if( postBack != null ) {
                control.AddJavaScriptEventScript(
                    JsWritingMethods.onkeypress,
                    "if( event.which == 13 " + predicate.PrependDelimiter( " && " ) + " ) { " + GetPostBackScript( postBack ) + "; }" );
                return;
            }
            if( EwfPage.Instance.SubmitButtonPostBack != null )
                return;

            var sentences = new[]
                {
                    "EWF does not allow form controls to use HTML's built-in implicit submission on a page with no submit button.", "There are two reasons for this.",
                    "First, the behavior of HTML's implicit submission appears to be somewhat arbitrary when there is no submit button; see http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#implicit-submission.",
                    "Second, we don't want the implicit submission behavior of form controls to unpredictably change if a submit button is added or removed."
                };
            throw new ApplicationException( StringTools.ConcatenateWithDelimiter( " ", sentences ) );
        }