File: UI\MobileControls\Adapters\UpWmlMobileTextWriter.cs
Project: ndp\fx\src\mit\System\Web\System.Web.Mobile.csproj (System.Web.Mobile)
//------------------------------------------------------------------------------
// <copyright file="UpWmlMobileTextWriter.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
using System.Web.Mobile;
using System.Web.UI.MobileControls;
using System.Security.Permissions;
 
using SR=System.Web.UI.MobileControls.Adapters.SR;
 
#if COMPILING_FOR_SHIPPED_SOURCE
using Adapters=System.Web.UI.MobileControls.ShippedAdapterSource;
namespace System.Web.UI.MobileControls.ShippedAdapterSource
#else
using Adapters=System.Web.UI.MobileControls.Adapters;
namespace System.Web.UI.MobileControls.Adapters
#endif    
 
{
    /*
     * UpWmlMobileTextWriter class.
     *
     * Copyright (c) 2000 Microsoft Corporation
     */
 
    /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter"]/*' />
    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    [Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
    public class UpWmlMobileTextWriter : WmlMobileTextWriter
    {
        private int       _screenWidth;
        private int       _screenHeight;
        private bool      _inHyperlink = false;
        private bool      _inPostBack = false;
        private bool      _inSoftkey = false;
        private Alignment _lastAlignment = Alignment.Left;
        private Wrapping  _lastWrapping = Wrapping.Wrap;
        private int       _currentCardIndex = -1;
        private ArrayList _cards = new ArrayList();
        private int       _currentCardAnchorCount = 0;
        private int       _currentCardPostBacks = 0;
        private int       _currentCardSubmits = 0;
        private bool      _canRenderMixedSelects = false;
        private bool      _requiresOptionSubmitCard = false;
        private int       _optionSubmitCardIndex = 0;
        private String    _optionMenuName = null;
        
        private String    _linkText = null;
        private String    _targetUrl = null;
        private String    _softkeyLabel = null;
        private bool      _encodeUrl = false;
        private bool      _useMenuOptionTitle = false;
        
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.UpWmlMobileTextWriter"]/*' />
        public UpWmlMobileTextWriter(TextWriter writer, MobileCapabilities device, MobilePage page) 
            : base(writer, device, page)
        {
            _screenWidth = device.ScreenCharactersWidth;
            _screenHeight = device.ScreenCharactersHeight;
            _canRenderMixedSelects = device.CanRenderMixedSelects;
        }
 
        private UpCard CurrentCard
        {
            get
            {
                return (UpCard)_cards[_currentCardIndex];
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.BeginForm"]/*' />
        public override void BeginForm(Form form)
        {
            ResetState();
            if (AnalyzeMode)
            {
                AllocateNewCard();
                base.BeginForm(form);
            }
            else
            {
                if (form == form.MobilePage.ActiveForm)
                {
                    PreRenderActiveForm();
                }
 
                base.BeginForm(form);
                RenderCardOpening(0);
            }
        }
 
        private static readonly int _filePathSuffixLength = 
            Constants.UniqueFilePathSuffixVariableWithoutEqual.Length + 1;
        private int _sessionCount = -1;
        private int SessionCount
        {
            get
            {
                if (_sessionCount == -1)
                {
                    _sessionCount = 0;
                    String filePathSuffix = 
                        Page.Request.QueryString[Constants.UniqueFilePathSuffixVariableWithoutEqual];
 
                    if (filePathSuffix != null && filePathSuffix.Length == _filePathSuffixLength)
                    {
                        Char c = filePathSuffix[_filePathSuffixLength - 1];
                        if (Char.IsDigit(c))
                        {
                            _sessionCount = (int)Char.GetNumericValue(c);
                        }
                    }
                }
 
                return _sessionCount;
            }
        }
 
        private bool RequiresLoopDetectionCard
        {
            get
            {
                IDictionary dictionary = Page.Adapter.CookielessDataDictionary;
                if((dictionary != null) && (dictionary.Count > 0))
                {
                    return true;
                }
                return SessionCount == 9;
            }
        }
 
        private void PreRenderActiveForm()
        {
            if (Device.RequiresUniqueFilePathSuffix && RequiresLoopDetectionCard)
            {
                Debug.Assert(!AnalyzeMode);
                Write(String.Format(CultureInfo.InvariantCulture, _loopDetectionCard, Page.ActiveForm.ClientID));
            }
        }
 
        private String _cachedFormQueryString;
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.CalculateFormQueryString"]/*' />
        protected override String CalculateFormQueryString()
        {
            if(_cachedFormQueryString != null)
            {
                return _cachedFormQueryString;
            }
            String queryString = null;
            if (CurrentForm.Method != FormMethod.Get)
            {
                queryString = Page.QueryStringText;
            }
 
            if (Device.RequiresUniqueFilePathSuffix)
            {
                String ufps = Page.UniqueFilePathSuffix;
                if(this.HasFormVariables)
                {
                    if (SessionCount == 9)
                    {
                        ufps += '0';
                    }
                    else
                    {
                        ufps += (SessionCount + 1).ToString(CultureInfo.InvariantCulture);
                    }
                }
                if (queryString != null && queryString.Length > 0)
                {
                    queryString = String.Concat(ufps, "&", queryString);
                }
                else
                {
                    queryString = ufps;
                }
            }
            _cachedFormQueryString = queryString;
            return queryString;
        }
 
        private const String _loopDetectionCard = 
            "<card ontimer=\"#{0}\"><onevent type=\"onenterbackward\"><prev /></onevent><timer value=\"1\" /></card>";
 
        internal override bool ShouldWriteFormID(Form form)
        {
            if (RequiresLoopDetectionCard)
            {
                return true;
            }
            return base.ShouldWriteFormID(form);
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.EndForm"]/*' />
        public override void EndForm()
        {
            if (AnalyzeMode)
            {
                CheckRawOutput();
                CurrentCard.AnchorCount = _currentCardAnchorCount;
                base.EndForm();
            }
            else
            {
                RenderCardClosing(_currentCardIndex);
                base.EndForm();
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderEndForm"]/*' />
        protected override void RenderEndForm()
        {
            base.RenderEndForm();
            if (_requiresOptionSubmitCard)
            {
                Write("<card id=\"");
                Write(_postBackCardPrefix);
                Write("0");
                Write(_optionSubmitCardIndex++);
                WriteLine("\">");
 
                Write("<onevent type=\"onenterforward\">");
                RenderGoAction(null, _postBackEventArgumentVarName, WmlPostFieldType.Variable, true);
                WriteLine("</onevent>");
 
                WriteLine("<onevent type=\"onenterbackward\"><prev /></onevent>");
                WriteLine("</card>");
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderText"]/*' />
        public override void RenderText(String text, bool breakAfter, bool encodeText)
        {
            if (AnalyzeMode)
            {
                if (CurrentCard.HasInputElements && !Device.CanRenderAfterInputOrSelectElement)
                {
                    BeginNextCard();
                }
 
                CheckRawOutput();
                if (_inHyperlink || _inPostBack)
                {
                    // When analyzing, accumulate link text for use in figuring
                    // out softkey.
                    if (_inSoftkey)
                    {
                        _linkText += text;
                    }
                }
                else
                {
                    // Text cannot come after a menu.
                    if (CurrentCard.RenderAsMenu)
                    {
                        CurrentCard.RenderAsMenu = false;
                        CurrentCard.MenuCandidate = false;
                    }
                    else if (CurrentCard.MenuCandidate)
                    {
                        // Calculate weight of static items before a menu.
                        // This is used to check for screens that scroll past their
                        // initial content.
 
                        int weight = text != null ? text.Length : 0;
                        if (breakAfter)
                        {
                            weight = ((weight - 1) / _screenWidth + 1) * _screenWidth;
                        }
                        CurrentCard.StaticItemsWeight += weight;
                    }
                }
            }
            else
            {
                bool willRenderText = false;
                if (_inHyperlink || _inPostBack)
                {
                    // If rendering in menu, simply accumulate text.
                    if (CurrentCard.RenderAsMenu)
                    {
                        _linkText += text;
                    }
                    else if (!CurrentCard.UsesDefaultSubmit)
                    {
                        willRenderText = true;
                    }
                }
                else
                {
                    willRenderText = true;
                }
 
                if (willRenderText)
                {
                    // Some browsers that
                    // RendersBreakBeforeWmlSelectAndInput have the odd behavior
                    // of *not* rendering a break if there is nothing on the
                    // card before it, and entering an explicit <br> creates two
                    // breaks.  Therefore, we just render a &nbsp; in this
                    // situation.
                    
                    if (!CurrentCard.RenderedTextElementYet &&
                        Device.RendersBreakBeforeWmlSelectAndInput &&
                        !((WmlPageAdapter)Page.Adapter).IsKDDIPhone())
                    {
                        CurrentCard.RenderedTextElementYet = true;
                        if (breakAfter && (text != null && text.Length == 0))
                        {
                            base.RenderText("&nbsp;", false, false);
                        } 
                    }
                    base.RenderText(text, breakAfter, encodeText);
                }
                
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderBeginHyperlink"]/*' />
        public override void RenderBeginHyperlink(String targetUrl, 
                                                  bool encodeUrl, 
                                                  String softkeyLabel, 
                                                  bool implicitSoftkeyLabel,
                                                  bool mapToSoftkey)
        {
            if (_inHyperlink || _inPostBack)
            {
                throw new Exception();
            }
 
            // AUI 4137
            if (targetUrl != null && targetUrl.Length > 0 && targetUrl[0] != '#')
            {
                targetUrl = Page.MakePathAbsolute(targetUrl);
            }
 
            if (AnalyzeMode)
            {
                if (CurrentCard.HasInputElements && !Device.CanRenderAfterInputOrSelectElement)
                {
                    BeginNextCard();
                }
 
                CheckRawOutput();
 
                // Try to map to softkey if possible.
                if (mapToSoftkey && CurrentCard.SoftkeysUsed < NumberOfSoftkeys)
                {
                    _inSoftkey = true;
                    _targetUrl = targetUrl;
                    _softkeyLabel = softkeyLabel;
                    _encodeUrl = encodeUrl;
                    _linkText = String.Empty;
                }
            }
            else
            {
                if (CurrentCard.RenderAsMenu)
                {
                    if (!CurrentCard.MenuOpened)
                    {
                        OpenMenu();
                    }
 
                    // In menu mode, actual rendering is done on RenderEndHyperlink,
                    // when we have all available info.
 
                    _targetUrl = targetUrl;
                    _softkeyLabel = softkeyLabel;
                    _useMenuOptionTitle = mapToSoftkey && !implicitSoftkeyLabel;
                    _encodeUrl = encodeUrl;
                    _linkText = String.Empty;
                }
                else if (!CurrentCard.UsesDefaultSubmit)
                {
                    base.RenderBeginHyperlink(targetUrl, 
                                              encodeUrl, 
                                              softkeyLabel, 
                                              implicitSoftkeyLabel, 
                                              mapToSoftkey);
                }
            }
            _inHyperlink = true;
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderEndHyperlink"]/*' />
        public override void RenderEndHyperlink(bool breakAfter)
        {
            if (!_inHyperlink)
            {
                throw new Exception();
            }
 
            _inHyperlink = false;
            if (AnalyzeMode)
            {
                CheckRawOutput();
                if (CurrentCard.MenuCandidate)
                {
                    CurrentCard.RenderAsMenu = true;
                }
                CurrentCard.HasNonStaticElements = true;
 
                if (_inSoftkey)
                {
                    // Add a softkey if possible.
 
                    _inSoftkey = false;
                    UpHyperlinkSoftkey softkey = new UpHyperlinkSoftkey();
                    softkey.TargetUrl = _targetUrl;
                    softkey.EncodeUrl = _encodeUrl;
                    if (_softkeyLabel == null || _softkeyLabel.Length == 0)
                    {
                        _softkeyLabel = _linkText;
                    }
                    softkey.Label = _softkeyLabel;
                    CurrentCard.Softkeys[CurrentCard.SoftkeysUsed++] = softkey;
                }
            }
            else
            {
                if (CurrentCard.RenderAsMenu)
                {
                    Write("<option onpick=\"");
                    if (_targetUrl.StartsWith(Constants.FormIDPrefix, StringComparison.Ordinal))
                    {
                        // no encoding needed if pointing to another form id
                        Write(_targetUrl);
                    }
                    else if (!_encodeUrl)
                    {
                        Write(EscapeAmpersand(_targetUrl));
                    }
                    else
                    {
                        WriteEncodedUrl(_targetUrl);
                    }
                    Write("\"");
 
                    if (_useMenuOptionTitle && IsValidSoftkeyLabel(_softkeyLabel))
                    {
                        WriteTextEncodedAttribute("title", _softkeyLabel);
                    }
 
                    Write(">");
                    WriteEncodedText(_linkText);
                    WriteEndTag("option");
                }
                else if (!CurrentCard.UsesDefaultSubmit)
                {
                    base.RenderEndHyperlink(breakAfter);
                }
 
            }
            _currentCardAnchorCount++;
            
            if (!AnalyzeMode &&
                    _currentCardAnchorCount == CurrentCard.AnchorCount && 
                    _currentCardIndex < _cards.Count - 1)
            {
                BeginNextCard();
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderTextBox"]/*' />
        public override void RenderTextBox(String id, 
                                           String value,
                                           String format, 
                                           String title,
                                           bool password, 
                                           int size, 
                                           int maxLength, 
                                           bool generateRandomID,
                                           bool breakAfter)
        {
            if (AnalyzeMode)
            {
                CheckRawOutput();
 
                // If an anchor precedes this control, then break to the
                // next card.
                if (_currentCardAnchorCount > 0)
                {
                    BeginNextCard();
                }
                else if (CurrentCard.HasInputElements && 
                            (!Device.CanRenderInputAndSelectElementsTogether ||
                                !Device.CanRenderAfterInputOrSelectElement))
                {
                    BeginNextCard();
                }
 
                CurrentCard.RenderAsMenu = false;
                CurrentCard.MenuCandidate = false;
                CurrentCard.HasNonStaticElements = true;
                CurrentCard.HasInputElements = true;
            }
            else
            {
                // Don't write breaks after textboxes on UP.
                base.RenderTextBox(id, value, format, title, password, size, maxLength, generateRandomID, false);
 
                // If we can't render more than one input element on this card, and
                // there are no anchors left to render, break here.
 
                if (!Device.CanRenderAfterInputOrSelectElement && _currentCardIndex < _cards.Count - 1)
                {
                    CurrentCard.NoOKLink = true;
                    BeginNextCard();
                }
                else if (CurrentCard.AnchorCount == 0 && _currentCardIndex < _cards.Count - 1 &&
                        !Device.CanRenderInputAndSelectElementsTogether)
                {
                    CurrentCard.NoOKLink = true;
                    BeginNextCard();
                }
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderImage"]/*' />
        public override void RenderImage(String source, 
                                         String localSource, 
                                         String alternateText, 
                                         bool breakAfter)
        {
            if (AnalyzeMode)
            {
                if (CurrentCard.HasInputElements && !Device.CanRenderAfterInputOrSelectElement)
                {
                    BeginNextCard();
                }
 
                CheckRawOutput();
 
                if (_inHyperlink || _inPostBack)
                {
                    CurrentCard.RenderAsMenu = false;
                    CurrentCard.MenuCandidate = false;
                }
                else
                {
                    if (CurrentCard.RenderAsMenu)
                    {
                        // Images cannot come after a menu on a card.
                        CurrentCard.RenderAsMenu = false;
                        CurrentCard.MenuCandidate = false;
                    }
                    else if (CurrentCard.MenuCandidate)
                    {
                        CurrentCard.StaticItemsWeight += _screenWidth;
                    }
                }
            }
            else
            {
                // AUI 4137
                if (source != null)
                {
                    source = Page.MakePathAbsolute(source);
                }
 
                if (_inHyperlink || _inPostBack)
                {
                    if (CurrentCard.RenderAsMenu)
                    {
                        _linkText += alternateText;
                    }
                    else if (!CurrentCard.UsesDefaultSubmit)
                    {
                        base.RenderImage(source, localSource, alternateText, breakAfter);
                    }
                }
                else
                {
                    base.RenderImage(source, localSource, alternateText, breakAfter);
                }
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderBeginPostBack"]/*' />
        public override void RenderBeginPostBack(String softkeyLabel, 
                                                 bool implicitSoftkeyLabel, 
                                                 bool mapToSoftkey)
        {
            if (_inHyperlink || _inPostBack)
            {
                throw new Exception();
            }
 
            if (AnalyzeMode)
            {
                if (CurrentCard.HasInputElements && !Device.CanRenderAfterInputOrSelectElement)
                {
                    BeginNextCard();
                }
 
                CheckRawOutput();
 
                // Try to map to softkey if possible.
                if (mapToSoftkey && CurrentCard.SoftkeysUsed < NumberOfSoftkeys)
                {
                    _inSoftkey = true;
                    _softkeyLabel = softkeyLabel;
                    _linkText = String.Empty;
                }
 
            }
            else
            {
                if (CurrentCard.RenderAsMenu)
                {
                    if (!CurrentCard.MenuOpened)
                    {
                        OpenMenu();
                    }
 
                    // In menu mode, actual rendering is done on RenderEndPostBack,
                    // when we have all available info.
                    _softkeyLabel = softkeyLabel;
                    _useMenuOptionTitle = mapToSoftkey && !implicitSoftkeyLabel;
                    _linkText = String.Empty;
                }
                else if (!CurrentCard.UsesDefaultSubmit)
                {
                    base.RenderBeginPostBack(softkeyLabel, implicitSoftkeyLabel, mapToSoftkey);
                }
            }
            _inPostBack = true;
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderEndPostBack"]/*' />
        public override void RenderEndPostBack(String target, String argument, WmlPostFieldType postBackType, bool includeVariables, bool breakAfter)
        {
            if (!_inPostBack)
            {
                throw new Exception();
            }
 
            _inPostBack = false;
            if (AnalyzeMode)
            {
                CheckRawOutput();
 
                if (CurrentCard.MenuCandidate)
                {
                    // If all postback menu items go to one target, we can write the do
                    // to hit that target. Otherwise, we must post back to the form.
 
                    if (CurrentCard.RenderAsMenu)
                    {
                        if (CurrentCard.MenuTarget != target)
                        {
                            CurrentCard.MenuTarget = null;
                        }
                    }
                    else
                    {
                        CurrentCard.MenuTarget = target;
                        CurrentCard.RenderAsMenu = true;
                    }
                }
                CurrentCard.HasNonStaticElements = true;
 
                if (_inSoftkey)
                {
                    // Map to softkey.
                    _inSoftkey = false;
                    UpPostBackSoftkey softkey = new UpPostBackSoftkey();
                    softkey.Target = target;
                    softkey.Argument = argument;
                    softkey.PostBackType = postBackType;
                    softkey.IncludeVariables = includeVariables;
                    if (_softkeyLabel == null || _softkeyLabel.Length == 0)
                    {
                        _softkeyLabel = _linkText;
                    }
                    softkey.Label = _softkeyLabel;
                    CurrentCard.Softkeys[CurrentCard.SoftkeysUsed++] = softkey;
                }
                AnalyzePostBack(includeVariables, postBackType);
            }
            else
            {
                if (CurrentCard.RenderAsMenu)
                {
                    // Render as a menu item.
 
                    WriteBeginTag("option");
                    if (!_canRenderMixedSelects)
                    {
                        if (_useMenuOptionTitle && IsValidSoftkeyLabel(_softkeyLabel))
                        {
                            WriteTextEncodedAttribute("title", _softkeyLabel);
                        }
 
                        _requiresOptionSubmitCard = true;
                        Write("><onevent type=\"onpick\"><go href=\"#");
                        Write(_postBackCardPrefix);
                        Write("0");
                        Write(_optionSubmitCardIndex);
                        Write("\">");
                        Write("<setvar name=\"");
                        Write(_postBackEventTargetVarName);
                        Write("\" value=\"");
                        if (_optionMenuName != null)
                        {
                            Write(_optionMenuName);
                        }
                        Write("\" />");
                        Write("<setvar name=\"");
                        Write(_postBackEventArgumentVarName);
                        Write("\" value=\"");
                    }
                    else
                    {
                        Write(" value=\"");
                    }
 
                    if (CurrentCard.MenuTarget != null)
                    {
                        if (argument != null)
                        {
                            WriteEncodedText(argument);
                        }
                    }
                    else
                    {
                        WriteEncodedText(target);
                        if (argument != null)
                        {
                            Write(",");
                            WriteEncodedText(argument);
                        }
                    }
                    if (!_canRenderMixedSelects)
                    {
                        Write("\" /></go></onevent>");
                    }
                    else
                    {
                        Write("\"");
                        if (_useMenuOptionTitle && IsValidSoftkeyLabel(_softkeyLabel))
                        {
                            WriteTextEncodedAttribute("title", _softkeyLabel);
                        }
                        Write(">");
                    }
 
                    WriteEncodedText(_linkText);
                    WriteEndTag("option");
                }
                else if (!CurrentCard.UsesDefaultSubmit)
                {
                    base.RenderEndPostBack(target, argument, postBackType, 
                            includeVariables, breakAfter);
                }
            }
            _currentCardAnchorCount++;
 
            if (!AnalyzeMode &&
                    _currentCardAnchorCount == CurrentCard.AnchorCount && 
                    _currentCardIndex < _cards.Count - 1)
            {
                BeginNextCard();
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.BeginCustomMarkup"]/*' />
        public override void BeginCustomMarkup()
        {
            if (!AnalyzeMode && !CurrentCard.MenuOpened)
            {
                EnsureLayout();
            }
        }
 
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.AnalyzePostBack"]/*' />
        protected override void AnalyzePostBack(bool includeVariables, WmlPostFieldType postBackType)
        {
            base.AnalyzePostBack(includeVariables, postBackType);
            if (CurrentForm.Action.Length > 0)
            {
                if (postBackType == WmlPostFieldType.Submit)
                {
                    _currentCardSubmits++;
                    CurrentCard.ExternalSubmitMenu = true;
                }
                else
                {
                    _currentCardPostBacks++;
                }
                if (_currentCardPostBacks > 0 && _currentCardSubmits > 0)
                {
                    // Posts to more than one target, so we can't use a menu card.
 
                    CurrentCard.RenderAsMenu = false;
                    CurrentCard.MenuCandidate = false;
                }
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderBeginSelect"]/*' />
        public override void RenderBeginSelect(String name, String iname, String ivalue, String title, bool multiSelect)
        {
            if (AnalyzeMode)
            {
                CheckRawOutput();
                if (_currentCardAnchorCount > 0)
                {
                    // If an anchor precedes this control, then break to the
                    // next card.
                    BeginNextCard();
                }
                else if (CurrentCard.HasInputElements &&
                            (!Device.CanRenderInputAndSelectElementsTogether ||
                                !Device.CanRenderAfterInputOrSelectElement))
                {
                    BeginNextCard();
                }
 
                CurrentCard.RenderAsMenu = false;
                CurrentCard.MenuCandidate = false;
                CurrentCard.HasNonStaticElements = true;
                CurrentCard.HasInputElements = true;
            }
            else
            {
                base.RenderBeginSelect(name, iname, ivalue, title, multiSelect);
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderEndSelect"]/*' />
        public override void RenderEndSelect(bool breakAfter)
        {
            if (AnalyzeMode)
            {
                CheckRawOutput();
            }
            else
            {
                // Don't write breaks after selects on UP.
                base.RenderEndSelect(false);
 
                // If we can't render more than one input element on this card, and
                // there are no anchors left to render, break here.
 
                if (!Device.CanRenderAfterInputOrSelectElement && _currentCardIndex < _cards.Count - 1)
                {
                    CurrentCard.NoOKLink = true;
                    BeginNextCard();
                } 
                else if (CurrentCard.AnchorCount == 0 && _currentCardIndex < _cards.Count - 1 &&
                        !Device.CanRenderInputAndSelectElementsTogether)
                {
                    CurrentCard.NoOKLink = true;
                    BeginNextCard();
                }
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderSelectOption"]/*' />
        public override void RenderSelectOption(String text)
        {
            if (AnalyzeMode)
            {
                CheckRawOutput();
            }
            else
            {
                base.RenderSelectOption(text);
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.RenderSelectOption1"]/*' />
        public override void RenderSelectOption(String text, String value)
        {
            if (AnalyzeMode)
            {
                CheckRawOutput();
            }
            else
            {
                base.RenderSelectOption(text, value);
            }
        }
 
        private void OpenMenu()
        {
            // Close any character formatting tags before starting a <do>.
            CloseCharacterFormat();
 
            String menuTarget;
            String menuTargetClientID;
            WmlPostFieldType postFieldType;
 
            if (CurrentCard.ExternalSubmitMenu)
            {
                menuTarget = null;
                menuTargetClientID = null;
                postFieldType = WmlPostFieldType.Submit;
            }
            else
            {
                if (CurrentCard.MenuTarget != null)
                {
                    menuTarget = CurrentCard.MenuTarget;
                    if (menuTarget.IndexOf(":", StringComparison.Ordinal) >= 0) 
                    {
                        menuTargetClientID = menuTarget.Replace(":", "_");
                    }
                    else
                    {
                        menuTargetClientID = menuTarget;
                    }
                }
                else
                {
                    menuTarget = CurrentForm.UniqueID;
                    menuTargetClientID = CurrentForm.ClientID;
                }
 
                postFieldType = WmlPostFieldType.Variable;
            }
 
            if (!_canRenderMixedSelects)
            {
                _optionMenuName = menuTarget;
                menuTargetClientID = null;
            }
            else
            {
                String GoLabel = SR.GetString(SR.WmlMobileTextWriterGoLabel);
                RenderDoEvent("accept", 
                              menuTarget, 
                              menuTargetClientID != null ? MapClientIDToShortName(menuTargetClientID, false) : null,
                              postFieldType,
                              GoLabel,
                              true);
            }
 
            base.RenderBeginSelect(menuTargetClientID, null, null, null, false);
 
            CurrentCard.MenuOpened = true;
        }
 
        private void CloseMenu()
        {
            base.RenderEndSelect(false);
            CurrentCard.MenuOpened = false;
        }
 
        // Overriden to convert relative file paths to absolute file paths,
        // due to a redirection issue on UP phones.
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.CalculateFormPostBackUrl"]/*' />
        protected override String CalculateFormPostBackUrl(bool externalSubmit, ref bool encode)
        {
            String url = CurrentForm.Action;
            if (externalSubmit && url.Length > 0)
            {
                // Not only do we need to resolve the URL, but we need to make it absolute.
                url = Page.MakePathAbsolute(CurrentForm.ResolveUrl(url));
                encode = false;
            }
            else
            {
                url = Page.AbsoluteFilePath;
                encode = true;
            }
            return url;
        }
 
        // Captures raw output written to the writers during analyze mode.
 
        private void CheckRawOutput()
        {
            Debug.Assert(AnalyzeMode);
 
            EmptyTextWriter innerWriter = (EmptyTextWriter)InnerWriter;
            if (innerWriter.NonWhiteSpaceWritten)
            {
                CurrentCard.RenderAsMenu = false;
                CurrentCard.MenuCandidate = false;
            }
            innerWriter.Reset();
        }
 
        // Overriden to always write the "align" or "wrap" attributes when
        // they are changing, even if they are set to defaults.
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.OpenParagraph"]/*' />
        protected override void OpenParagraph(WmlLayout layout, bool writeAlignment, bool writeWrapping)
        {
            base.OpenParagraph(layout,
                               writeAlignment || layout.Align != _lastAlignment,
                               writeWrapping  || layout.Wrap != _lastWrapping);
            _lastAlignment = layout.Align;
            _lastWrapping = layout.Wrap;
        }
 
        // Resets writer state between forms.
 
        private void ResetState()
        {
            if (AnalyzeMode)
            {
                _currentCardPostBacks = 0;
                _currentCardSubmits = 0;
                _inHyperlink = false;
                _inPostBack = false;
                _inSoftkey = false;
                if (_cards.Count > 0)
                {
                    _cards.Clear();
                }
            }
            _currentCardAnchorCount = 0;
            _currentCardIndex = 0;
            _currentCardPostBacks = 0;
            _currentCardSubmits = 0;
            _requiresOptionSubmitCard = false;
        }
 
        private UpCard AllocateNewCard()
        {
            UpCard card = new UpCard();
            card.Softkeys = new UpSoftkey[NumberOfSoftkeys];
            _cards.Add(card);
            return card;
        }
 
        // Analyze an individual card.
 
        private void PostAnalyzeCard(int cardIndex)
        {
            UpCard card = (UpCard)_cards[cardIndex];
            if (card.RenderAsMenu)
            {
                // If the card is the last card, and has only
                // one anchor that can be mapped to a softkey, 
                // ignore the 
 
                if (card.AnchorCount == 1 && cardIndex == _cards.Count - 1 && card.SoftkeysUsed == 1)
                {
                    card.RenderAsMenu = false;
                }
 
                // If the card has a lot of static content followed by
                // a number of links, don't render it as a menu card, 
                // because the card would scroll off the static content
                // to get to the menu.
 
                else if (card.StaticItemsWeight >= 3 * _screenWidth)
                {
                    card.RenderAsMenu = false;
                }
            }
        }
 
        /// <include file='doc\UpWmlMobileTextWriter.uex' path='docs/doc[@for="UpWmlMobileTextWriter.PostAnalyzeForm"]/*' />
        protected override void PostAnalyzeForm()
        {
            base.PostAnalyzeForm();
 
            for(int i = 0; i < _cards.Count; i++)
            {
                PostAnalyzeCard(i);
            }
 
            // If the last card ends with an input element and an anchor
            // then use the anchor as a do tag to submit the form, and
            // don't render as a separate anchor (otherwise, the user would
            // see an extra screen at the end with a single anchor)
            UpCard lastCard = CurrentCard;
            if (lastCard.HasInputElements && _currentCardAnchorCount >= 1 && 
                    _currentCardAnchorCount <= Device.DefaultSubmitButtonLimit)
            {
                lastCard.UsesDefaultSubmit = true;
            }
        }
 
        private void RenderCardOpening(int cardIndex)
        {
            UpCard card = (UpCard)_cards[cardIndex];
            if (card.RenderAsMenu)
            {
            }
            else
            {
                // Render softkeys
                if (card.HasNonStaticElements)
                {
                    for (int i = 0; i < card.SoftkeysUsed; i++)
                    {
                        RenderSoftkey(i == 0 ? "accept" : "options", card.Softkeys[i]);
                    }
                }
                else if (cardIndex == _cards.Count - 1)
                {
                    // Render the last card with an extra <do>, so that
                    // it overrides the default function of the OK button,
                    // which is to go back a screen.
                    //EnsureLayout();
                    Write("<do type=\"accept\"><noop /></do>");
                }
            }
        }
 
        private void RenderCardClosing(int cardIndex)
        {
            UpCard card = (UpCard)_cards[cardIndex];
 
            if (cardIndex < _cards.Count - 1 && !card.NoOKLink)
            {
                // Add a link to go to the next card.
 
                UpCard nextCard = (UpCard)_cards[cardIndex + 1];
                String OkLabel = SR.GetString(SR.WmlMobileTextWriterOKLabel);
                RenderBeginHyperlink("#" + nextCard.Id, false, OkLabel, true, true);
                RenderText(OkLabel);
                RenderEndHyperlink(true);
            }
 
            if (card.RenderAsMenu)
            {
                CloseMenu();
            }
        }
 
        private void BeginNextCard()
        {
            if (AnalyzeMode)
            {
                // Add a softkey on the current card, to go to the new card.
 
                String nextCardId = "card" + (_currentCardIndex + 1).ToString(CultureInfo.InvariantCulture);
 
                UpHyperlinkSoftkey softkey = new UpHyperlinkSoftkey();
                softkey.TargetUrl = "#" + nextCardId;
                softkey.EncodeUrl = false;
                softkey.Label = "OK";
                SetPrimarySoftkey(softkey);
 
                CurrentCard.AnchorCount = _currentCardAnchorCount;
 
                UpCard card = AllocateNewCard();
                card.Id = nextCardId;
                _currentCardIndex++;
            }
            else
            {
                RenderCardClosing(_currentCardIndex);
                CloseParagraph();
                WriteEndTag("card");
                WriteLine();
    
                _currentCardIndex++;
                _lastAlignment = Alignment.Left;
                _lastWrapping = Wrapping.NoWrap;
    
                WriteBeginTag("card");
                WriteAttribute("id", CurrentCard.Id);
                String formTitle = CurrentForm.Title;
                if (formTitle != null && formTitle.Length > 0)
                {
                    WriteTextEncodedAttribute("title", formTitle);
                }
                WriteLine(">");
                RenderCardOpening(_currentCardIndex);
            }
 
            _currentCardAnchorCount = 0;
            _currentCardPostBacks = 0;
            _currentCardSubmits = 0;
        }
 
        private void SetPrimarySoftkey(UpSoftkey softkey)
        {
            for (int i = NumberOfSoftkeys - 1; i > 0; i--)
            {
                CurrentCard.Softkeys[i] = CurrentCard.Softkeys[i - 1];
            }
            CurrentCard.Softkeys[0] = softkey;
            if (CurrentCard.SoftkeysUsed < NumberOfSoftkeys)
            {
                CurrentCard.SoftkeysUsed++;
            }
        }
 
        private void RenderSoftkey(String doType, UpSoftkey softkey)
        {
            UpHyperlinkSoftkey linkSoftkey = softkey as UpHyperlinkSoftkey;
            if (linkSoftkey != null)
            {
                WriteBeginTag("do");
                WriteAttribute("type", doType);
                WriteTextEncodedAttribute("label", linkSoftkey.Label);
                Write(">");
                WriteBeginTag("go");
                Write(" href=\"");
 
                if (linkSoftkey.EncodeUrl)
                {
                    WriteEncodedUrl(linkSoftkey.TargetUrl);
                }
                else
                {
                    Write(EscapeAmpersand(linkSoftkey.TargetUrl));
                }
                Write("\" />");
                WriteEndTag("do");
                return;
            }
 
            UpPostBackSoftkey postBackSoftkey = softkey as UpPostBackSoftkey;
            if (postBackSoftkey != null)
            {
                RenderDoEvent(doType, 
                              postBackSoftkey.Target, 
                              postBackSoftkey.Argument, 
                              postBackSoftkey.PostBackType, 
                              postBackSoftkey.Label,
                              postBackSoftkey.IncludeVariables); 
                return;
            }
        }
 
        private class UpSoftkey
        {
            public String Label;
        }
 
        private class UpHyperlinkSoftkey : UpSoftkey
        {
            public String TargetUrl;
            public bool EncodeUrl;
        }
 
        private class UpPostBackSoftkey : UpSoftkey
        {
            public String Target;
            public String Argument;
            public WmlPostFieldType PostBackType;
            public bool IncludeVariables;
        }
 
        private class UpCard
        {
            public String Id;
            public bool MenuCandidate = true;
            public bool RenderAsMenu = false;
            public int StaticItemsWeight = 0;
            public bool HasNonStaticElements = false;
            public bool MenuOpened = false;
            public bool HasInputElements = false;
            public bool UsesDefaultSubmit = false;
            public int SoftkeysUsed = 0;
            public UpSoftkey[] Softkeys = null;
            public int AnchorCount = 0;
            public String MenuTarget = null;
            public bool ExternalSubmitMenu = false;
            public bool NoOKLink = false;
            public bool RenderedTextElementYet = false;
            
        }
 
    }
}