Represents a toggle control for conditionally rendering content.
Inheritance: Control
Ejemplo n.º 1
0
Archivo: Block.cs Proyecto: vc3/ExoWeb
		/// <summary>
		/// Parses the children of the specified element into a list of template blocks.
		/// </summary>
		/// <param name="source">The original string source of the template.</param>
		/// <param name="element">The element to parse</param>
		/// <param name="withinTemplate">Indicates that the children of the given element are within a template control.</param>
		/// <param name="lastNestedTemplateIndex">Tracks the index of the last template element that was encountered. Passed through to recursive calls excluding templates.</param>
		/// <param name="nestedTemplates">Out parameter that returns the number of template controls represented by the children of the given element.</param>
		/// <returns>A list of template blocks.</returns>
		static List<Block> ParseChildren(string source, XmlElement element, bool withinTemplate, int lastNestedTemplateIndex, out int nestedTemplates)
		{
			// Track the blocks represented by the current element
			var blocks = new List<Block>();

			nestedTemplates = 0;

			// Process the child nodes of the current element
			foreach (XmlNode node in element.ChildNodes)
			{
				switch (node.NodeType)
				{
					// XML Element, which could be a control, bound element start tag, or just literal content
					case XmlNodeType.Element:

						var child = (XmlElement)node;

						// Control
						if (child.HasAttribute("sys:attach") ||
							child.HasAttribute("sys:if") ||
							child.HasAttribute("sys:content-template") ||
							child.HasAttribute("sys:id") ||
							child.HasAttribute("id"))
						{
							bool parseChildren = true;
							Control control;
							if (child.HasAttribute("sys:attach"))
							{
								switch (child.GetAttribute("sys:attach"))
								{
									// Template
									case "template":
										control = new Template()
										{
											Attributes = GetAttributes(child, "class", "sys:attach", "sys:if", "sys:content-template", "template:name", "template:kind", "template:datatype", "template:islist", "template:isreference"),
											Name = GetLiteralTokens(child, "template:name"),
											Source = source + " [" + child.GetAttribute("template:name") + "]" + (child.HasAttribute("template:datatype") ? " - " + child.GetAttribute("template:datatype") : ""),
											IsList = GetBoolean(child, "template:islist"),
											IsReference = GetBoolean(child, "template:isreference"),
											Kind = GetStringLiteral(child, "template:kind"),
											DataType = GetStringLiteral(child, "template:datatype"),
											Class = GetLiteralTokens(child, "class").Where(c => c.ToLower() != "sys-template").ToArray(),
											ContentTemplateNames = GetLiteralTokens(child, "sys:content-template")
										};
										break;

									// DataView
									case "dataview":
										control = new DataView()
										{
											Attributes = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template", "dataview:data"),
											Data = GetBinding(child, "dataview:data"),
											Template = GetTemplate(child),
											ContentTemplate = GetBinding(child, "sys:content-template")
										};
										break;

									// Content
									case "content":
										control = new Content()
										{
											Attributes = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template", "content:data", "content:template"),
											Data = GetBinding(child, "content:data"),
											Template = GetBinding(child, "content:template"),
											DataType = GetBinding(child, "content:datatype"),
											ContentTemplate = GetBinding(child, "sys:content-template")
										};
										break;

									// Toggle
									case "toggle":
										control = new Toggle() 
										{
											Attributes = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template", "toggle:on", "toggle:action", "toggle:class", "toggle:groupname", "toggle:strictmode", "toggle:when"),
											On = GetBinding(child, "toggle:on"),
											Class = GetBinding(child, "toggle:class"),
											ClassName = GetBinding(child, "toggle:classname"),
											Action = GetBinding(child, "toggle:action"),
											GroupName = GetBinding(child, "toggle:groupname"),
											StrictMode = GetBinding(child, "toggle:strictmode"),
											When = GetBinding(child, "toggle:when"),
											ContentTemplate = GetBinding(child, "sys:content-template")
										};
										break;

									// ToggleGroup
									case "togglegroup":
										control = new ToggleGroup()
										{
											Attributes = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template"),
											ContentTemplate = GetBinding(child, "sys:content-template")
										};
										break;

									// Behavior
									case "behavior":
										control = new Behavior()
										{
											Attributes = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template"),
											ContentTemplate = GetBinding(child, "sys:content-template")
										};
										break;

									// Html
									case "html":
										control = new Html()
										{
											Attributes = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template"),
											ContentTemplate = GetBinding(child, "sys:content-template")
										};
										break;

									default:
										throw new ArgumentException("Controls of type '" + child.GetAttribute("sys:attach") + "' are not supported for server rendering.");
								}

								// Add data-sys-attach to indicate the control type when performing linking on the client
								control.Attributes.Add(new Attribute() { Name = "data-sys-attach", Value = child.GetAttribute("sys:attach") });
							}
							else
							{
								control = new Control()
								{
									Attributes = GetAttributes(child, "sys:if", "sys:content-template", "sys:innerhtml", "sys:innertext"),
									ContentTemplate = GetBinding(child, "sys:content-template"),
									IsEmpty = child.ChildNodes.Count == 0
								};
								if (child.InnerXml.Trim().StartsWith("{") && child.InnerXml.Trim().EndsWith("}"))
								{
									parseChildren = false;
									control.Attributes.Add(new Attribute() { Name = "innerhtml", Binding = Binding.Parse(GetDefaultProperty(child), child.InnerXml.Trim()), Value = child.InnerXml.Trim() });
									control.IsEmpty = true;
								}
							}

							var isTemplate = IsTemplate(control);

							// If the sys:content-template attribute is found, then ensure it is within or on a templated control
							if (child.HasAttribute("sys:content-template") && !withinTemplate && !isTemplate)
								throw new ApplicationException("The sys:content-template attribute must be used on or within an control that implements Sys.UI.IContentTemplateConsumer.");

							// Process the controls child blocks
							if (parseChildren)
							{
								// Determine the number of top-level templates represented by this node
								int numTopLevelTemplates;

								if (isTemplate)
								{
									// A templated control represents only 1 top-level nested template
									numTopLevelTemplates = 1;

									// Set nested template index
									control.NestedTemplateIndex = lastNestedTemplateIndex + 1;

									// Parse child blocks as a new template region.  This means that lastNestedTemplateIndex
									// starts fresh (-1) and the number of child templates are not relevant here.
									control.Blocks = ParseChildren(source, child, true);
								}
								else
									// Parse children and capture the number of top-level templates contained within
									control.Blocks = ParseChildren(source, child, withinTemplate, lastNestedTemplateIndex, out numTopLevelTemplates);

								// Increment the number of nested templates and last index by
								// the number of top-level templates that this node represents
								nestedTemplates += numTopLevelTemplates;
								lastNestedTemplateIndex += numTopLevelTemplates;
							}
							else
								// A non-control with a binding expression as its inner-html contains no blocks
								control.Blocks = new List<Block>();

							control.Tag = child.Name;
							control.Markup = GetMarkup(child);
							control.If = GetBinding(child, "sys:if");

							// Add the control
							blocks.Add(control);
						}

						// Element
						else if (child.Attributes.Cast<XmlAttribute>().Any(a => a.Value.StartsWith("{") && a.Value.EndsWith("}")) ||
								 (child.ChildNodes.Cast<XmlNode>().All(n => n.NodeType != XmlNodeType.Element) && child.InnerXml.Trim().StartsWith("{") && child.InnerXml.Trim().EndsWith("}")))
						{
							var isBinding = child.InnerXml.Trim().StartsWith("{") && child.InnerXml.Trim().EndsWith("}");

							// Add the bound element
							var e = new Element()
							{
								Markup = isBinding || child.ChildNodes.Count == 0 ? GetMarkup(child) : GetElementMarkup(child),
								Attributes = GetAttributes(child),
								Tag = child.Name,
								IsEmpty = isBinding || child.ChildNodes.Count == 0
							};

							if (isBinding)
								e.Attributes.Add(new Attribute() { Name = "innerhtml", Binding = Binding.Parse(GetDefaultProperty(child), child.InnerXml.Trim()) });

							blocks.Add(e);

							// Process child nodes, if the element content is not bound
							if (!e.IsEmpty)
							{
								int numTopLevelTemplates;
								var children = ParseChildren(source, child, withinTemplate, lastNestedTemplateIndex, out numTopLevelTemplates);
								lastNestedTemplateIndex += numTopLevelTemplates;
								nestedTemplates += numTopLevelTemplates;

								blocks.AddRange(children);
								blocks.Add(new Block() { Markup = "</" + child.Name + ">" });
							}
						}

						// Literal
						else
						{
							// Get the blocks contained by the literal element
							int numTopLevelTemplates;
							var children = ParseChildren(source, child, withinTemplate, lastNestedTemplateIndex, out numTopLevelTemplates);
							lastNestedTemplateIndex += numTopLevelTemplates;
							nestedTemplates += numTopLevelTemplates;

							// Add the entire element as a block if it only contains literal content
							if (children.Count == 0 || (children.Count == 1 && children.First().GetType() == typeof(Block)))
								blocks.Add(new Block() { Markup = GetMarkup(child) });

							// Otherwise, process the child blocks
							else
							{
								blocks.Add(new Block() { Markup = GetElementMarkup(child) });
								blocks.AddRange(children);
								blocks.Add(new Block() { Markup = "</" + child.Name + ">" });
							}
						}
						break;

					// Literal content
					case XmlNodeType.Text:
						blocks.Add(new Block() { Markup = GetMarkup(node) });
						break;
				}
			}

			// Condense adjacent literal blocks
			Block literal = null;
			for (int i = blocks.Count - 1; i >= 0; i--)
			{
				if (blocks[i].GetType() == typeof(Block))
				{
					if (literal == null)
						literal = blocks[i];
					else
					{
						literal.Markup = blocks[i].Markup + literal.Markup;
						blocks.RemoveAt(i);
					}
				}
				else
					literal = null;
			}

			return blocks;
		}
Ejemplo n.º 2
0
        /// <summary>
        /// Parses the children of the specified element into a list of template blocks.
        /// </summary>
        /// <param name="source">The original string source of the template.</param>
        /// <param name="element">The element to parse</param>
        /// <param name="withinTemplate">Indicates that the children of the given element are within a template control.</param>
        /// <param name="lastNestedTemplateIndex">Tracks the index of the last template element that was encountered. Passed through to recursive calls excluding templates.</param>
        /// <param name="nestedTemplates">Out parameter that returns the number of template controls represented by the children of the given element.</param>
        /// <returns>A list of template blocks.</returns>
        static List <Block> ParseChildren(string source, XmlElement element, bool withinTemplate, int lastNestedTemplateIndex, out int nestedTemplates)
        {
            // Track the blocks represented by the current element
            var blocks = new List <Block>();

            nestedTemplates = 0;

            // Process the child nodes of the current element
            foreach (XmlNode node in element.ChildNodes)
            {
                switch (node.NodeType)
                {
                // XML Element, which could be a control, bound element start tag, or just literal content
                case XmlNodeType.Element:

                    var child = (XmlElement)node;

                    // Control
                    if (child.HasAttribute("sys:attach") ||
                        child.HasAttribute("sys:if") ||
                        child.HasAttribute("sys:content-template") ||
                        child.HasAttribute("sys:id") ||
                        child.HasAttribute("id"))
                    {
                        bool    parseChildren = true;
                        Control control;
                        if (child.HasAttribute("sys:attach"))
                        {
                            switch (child.GetAttribute("sys:attach"))
                            {
                            // Template
                            case "template":
                                control = new Template()
                                {
                                    Attributes           = GetAttributes(child, "class", "sys:attach", "sys:if", "sys:content-template", "template:name", "template:kind", "template:datatype", "template:islist", "template:isreference"),
                                    Name                 = GetLiteralTokens(child, "template:name"),
                                    Source               = source + " [" + child.GetAttribute("template:name") + "]" + (child.HasAttribute("template:datatype") ? " - " + child.GetAttribute("template:datatype") : ""),
                                    IsList               = GetBoolean(child, "template:islist"),
                                    IsReference          = GetBoolean(child, "template:isreference"),
                                    Kind                 = GetStringLiteral(child, "template:kind"),
                                    DataType             = GetStringLiteral(child, "template:datatype"),
                                    Class                = GetLiteralTokens(child, "class").Where(c => c.ToLower() != "sys-template").ToArray(),
                                    ContentTemplateNames = GetLiteralTokens(child, "sys:content-template")
                                };
                                break;

                            // DataView
                            case "dataview":
                                control = new DataView()
                                {
                                    Attributes      = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template", "dataview:data"),
                                    Data            = GetBinding(child, "dataview:data"),
                                    Template        = GetTemplate(child),
                                    ContentTemplate = GetBinding(child, "sys:content-template")
                                };
                                break;

                            // Content
                            case "content":
                                control = new Content()
                                {
                                    Attributes      = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template", "content:data", "content:template"),
                                    Data            = GetBinding(child, "content:data"),
                                    Template        = GetBinding(child, "content:template"),
                                    DataType        = GetBinding(child, "content:datatype"),
                                    ContentTemplate = GetBinding(child, "sys:content-template")
                                };
                                break;

                            // Toggle
                            case "toggle":
                                control = new Toggle()
                                {
                                    Attributes      = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template", "toggle:on", "toggle:action", "toggle:class", "toggle:groupname", "toggle:strictmode", "toggle:when"),
                                    On              = GetBinding(child, "toggle:on"),
                                    Class           = GetBinding(child, "toggle:class"),
                                    ClassName       = GetBinding(child, "toggle:classname"),
                                    Action          = GetBinding(child, "toggle:action"),
                                    GroupName       = GetBinding(child, "toggle:groupname"),
                                    StrictMode      = GetBinding(child, "toggle:strictmode"),
                                    When            = GetBinding(child, "toggle:when"),
                                    ContentTemplate = GetBinding(child, "sys:content-template")
                                };
                                break;

                            // ToggleGroup
                            case "togglegroup":
                                control = new ToggleGroup()
                                {
                                    Attributes      = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template"),
                                    ContentTemplate = GetBinding(child, "sys:content-template")
                                };
                                break;

                            // Behavior
                            case "behavior":
                                control = new Behavior()
                                {
                                    Attributes      = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template"),
                                    ContentTemplate = GetBinding(child, "sys:content-template")
                                };
                                break;

                            // Html
                            case "html":
                                control = new Html()
                                {
                                    Attributes      = GetAttributes(child, "sys:attach", "sys:if", "sys:content-template"),
                                    ContentTemplate = GetBinding(child, "sys:content-template")
                                };
                                break;

                            default:
                                throw new ArgumentException("Controls of type '" + child.GetAttribute("sys:attach") + "' are not supported for server rendering.");
                            }

                            // Add data-sys-attach to indicate the control type when performing linking on the client
                            control.Attributes.Add(new Attribute()
                            {
                                Name = "data-sys-attach", Value = child.GetAttribute("sys:attach")
                            });
                        }
                        else
                        {
                            control = new Control()
                            {
                                Attributes      = GetAttributes(child, "sys:if", "sys:content-template", "sys:innerhtml", "sys:innertext"),
                                ContentTemplate = GetBinding(child, "sys:content-template"),
                                IsEmpty         = child.ChildNodes.Count == 0
                            };
                            if (child.InnerXml.Trim().StartsWith("{") && child.InnerXml.Trim().EndsWith("}"))
                            {
                                parseChildren = false;
                                control.Attributes.Add(new Attribute()
                                {
                                    Name = "innerhtml", Binding = Binding.Parse(GetDefaultProperty(child), child.InnerXml.Trim()), Value = child.InnerXml.Trim()
                                });
                                control.IsEmpty = true;
                            }
                        }

                        var isTemplate = IsTemplate(control);

                        // If the sys:content-template attribute is found, then ensure it is within or on a templated control
                        if (child.HasAttribute("sys:content-template") && !withinTemplate && !isTemplate)
                        {
                            throw new ApplicationException("The sys:content-template attribute must be used on or within an control that implements Sys.UI.IContentTemplateConsumer.");
                        }

                        // Process the controls child blocks
                        if (parseChildren)
                        {
                            // Determine the number of top-level templates represented by this node
                            int numTopLevelTemplates;

                            if (isTemplate)
                            {
                                // A templated control represents only 1 top-level nested template
                                numTopLevelTemplates = 1;

                                // Set nested template index
                                control.NestedTemplateIndex = lastNestedTemplateIndex + 1;

                                // Parse child blocks as a new template region.  This means that lastNestedTemplateIndex
                                // starts fresh (-1) and the number of child templates are not relevant here.
                                control.Blocks = ParseChildren(source, child, true);
                            }
                            else
                            {
                                // Parse children and capture the number of top-level templates contained within
                                control.Blocks = ParseChildren(source, child, withinTemplate, lastNestedTemplateIndex, out numTopLevelTemplates);
                            }

                            // Increment the number of nested templates and last index by
                            // the number of top-level templates that this node represents
                            nestedTemplates         += numTopLevelTemplates;
                            lastNestedTemplateIndex += numTopLevelTemplates;
                        }
                        else
                        {
                            // A non-control with a binding expression as its inner-html contains no blocks
                            control.Blocks = new List <Block>();
                        }

                        control.Tag    = child.Name;
                        control.Markup = GetMarkup(child);
                        control.If     = GetBinding(child, "sys:if");

                        // Add the control
                        blocks.Add(control);
                    }

                    // Element
                    else if (child.Attributes.Cast <XmlAttribute>().Any(a => a.Value.StartsWith("{") && a.Value.EndsWith("}")) ||
                             (child.ChildNodes.Cast <XmlNode>().All(n => n.NodeType != XmlNodeType.Element) && child.InnerXml.Trim().StartsWith("{") && child.InnerXml.Trim().EndsWith("}")))
                    {
                        var isBinding = child.InnerXml.Trim().StartsWith("{") && child.InnerXml.Trim().EndsWith("}");

                        // Add the bound element
                        var e = new Element()
                        {
                            Markup     = isBinding || child.ChildNodes.Count == 0 ? GetMarkup(child) : GetElementMarkup(child),
                            Attributes = GetAttributes(child),
                            Tag        = child.Name,
                            IsEmpty    = isBinding || child.ChildNodes.Count == 0
                        };

                        if (isBinding)
                        {
                            e.Attributes.Add(new Attribute()
                            {
                                Name = "innerhtml", Binding = Binding.Parse(GetDefaultProperty(child), child.InnerXml.Trim())
                            });
                        }

                        blocks.Add(e);

                        // Process child nodes, if the element content is not bound
                        if (!e.IsEmpty)
                        {
                            int numTopLevelTemplates;
                            var children = ParseChildren(source, child, withinTemplate, lastNestedTemplateIndex, out numTopLevelTemplates);
                            lastNestedTemplateIndex += numTopLevelTemplates;
                            nestedTemplates         += numTopLevelTemplates;

                            blocks.AddRange(children);
                            blocks.Add(new Block()
                            {
                                Markup = "</" + child.Name + ">"
                            });
                        }
                    }

                    // Literal
                    else
                    {
                        // Get the blocks contained by the literal element
                        int numTopLevelTemplates;
                        var children = ParseChildren(source, child, withinTemplate, lastNestedTemplateIndex, out numTopLevelTemplates);
                        lastNestedTemplateIndex += numTopLevelTemplates;
                        nestedTemplates         += numTopLevelTemplates;

                        // Add the entire element as a block if it only contains literal content
                        if (children.Count == 0 || (children.Count == 1 && children.First().GetType() == typeof(Block)))
                        {
                            blocks.Add(new Block()
                            {
                                Markup = GetMarkup(child)
                            });
                        }

                        // Otherwise, process the child blocks
                        else
                        {
                            blocks.Add(new Block()
                            {
                                Markup = GetElementMarkup(child)
                            });
                            blocks.AddRange(children);
                            blocks.Add(new Block()
                            {
                                Markup = "</" + child.Name + ">"
                            });
                        }
                    }
                    break;

                // Literal content
                case XmlNodeType.Text:
                    blocks.Add(new Block()
                    {
                        Markup = GetMarkup(node)
                    });
                    break;
                }
            }

            // Condense adjacent literal blocks
            Block literal = null;

            for (int i = blocks.Count - 1; i >= 0; i--)
            {
                if (blocks[i].GetType() == typeof(Block))
                {
                    if (literal == null)
                    {
                        literal = blocks[i];
                    }
                    else
                    {
                        literal.Markup = blocks[i].Markup + literal.Markup;
                        blocks.RemoveAt(i);
                    }
                }
                else
                {
                    literal = null;
                }
            }

            return(blocks);
        }