/// <summary> /// /// </summary> /// <param name="page"></param> /// <returns></returns> internal bool IsTemplate(AjaxPage page) { bool result = false; if (Action != null) { // action is literal render/dispose if (Action.Expression == "render" || Action.Expression == "dispose") { result = true; } else { // if not within a dataview or template, then any binding expressions // for toggle action would not be dependent on template context data, // so use the newly created page instance with a null context var action = Action.Evaluate(page == null ? Page.Current as AjaxPage : page); if (action.IsValid && (action.DisplayValue == "render" || action.DisplayValue == "dispose")) { result = true; } } } return(result); }
internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { bool canRender; AttributeBinding ifBinding; if (!TryRenderIf(page, templateNames, writer, out ifBinding, out canRender)) { Abort(page, templateNames, writer); return; } if (!canRender) return; AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string) contentTemplateBinding.Value).Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries) : new string[0]; RenderStartTag(page, writer, ifBinding, contentTemplateBinding); foreach (var block in Blocks) block.Render(page, templateNames.Concat(ownTemplateNames).ToArray(), writer); RenderEndTag(writer); }
protected bool TryContentTemplate(AjaxPage page, IEnumerable <string> templateNames, System.IO.TextWriter writer, out AttributeBinding contentTemplateBinding) { if (ContentTemplate == null) { contentTemplateBinding = null; return(true); } contentTemplateBinding = ContentTemplate.Evaluate(page); return(contentTemplateBinding.IsValid && contentTemplateBinding.Value is string); }
internal override AttributeBinding Evaluate(AjaxPage page) { var path = Path; string sourcePath; if (Parameters.TryGetValue("source", out sourcePath) && sourcePath.Length > 0) { path = sourcePath + "." + path; } var result = page.EvaluatePath(path); if (result.IsValid) { if (result.Value == null) { string nullValue; if (Parameters.TryGetValue("nullValue", out nullValue)) { result.Value = nullValue; } } else { string format; if (Parameters.TryGetValue("format", out format) && !string.IsNullOrEmpty(format) && result.Value is IFormattable) { result.Value = ((IFormattable)result.Value).ToString(format, null); } else { string transformText; if (transform == null && Parameters.TryGetValue("transform", out transformText) && transformText.Length > 0) { transform = Transform.Compile(transformText); } if (result.IsValid && transform != null) { IEnumerable transformed; result.IsValid = transform.TryExecute(page, (IEnumerable)result.Value, out transformed); result.Value = result.IsValid ? transformed : null; } } } } return(new AttributeBinding(Attribute, result)); }
internal override AttributeBinding Evaluate(AjaxPage page) { // Evaluate the binding path var result = page.EvaluatePath(Path); // Invalid result if (result.Property == null) { result.IsValid = false; } // # syntax else if (Extension == "#") { string format = null; Parameters.TryGetValue("format", out format); string value; result.IsValid = Adapter.TryGetDisplayValue(result.Property, format, result.Value, out value); result.Value = value; } // @ syntax else if (Extension == "@") { if (string.IsNullOrEmpty(Path)) { if (!(page.Context.DataItem is Adapter)) { throw new ApplicationException("No path was specified for the \"@\" markup extension, and the source is not an adapter."); } result.Value = page.Context.DataItem; } else { ExoWeb.OnBeforeCreateAdapter(this, result.Source, result.Property); result = new BindingResult() { Value = new Adapter(result, Parameters), IsValid = result.IsValid, Property = result.Property, Source = result.Source }; } } return(new AttributeBinding(Attribute, result)); }
internal override void Render(AjaxPage page, string[] templateNames, TextWriter writer) { try { ExoWeb.OnBeginRender(page, this); foreach (var block in Blocks) { block.Render(page, templateNames.Concat(ContentTemplateNames).ToArray(), writer); } } finally { ExoWeb.OnEndRender(page, this); } }
internal override void Abort(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { // Write out a render/dispose toggle since it will be interpreted as a template // and conditionally render. Otherwise, the content within the toggle is not affected // by the toggle from a rendering perspective. if (IsTemplate(page)) { base.Abort(page, templateNames, writer); } else { AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { base.Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string)contentTemplateBinding.Value).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : new string[0]; RenderStartTag(page, writer, // replace data-sys-attach attribute with sys:attach and add data-continue attribute to notify client that children were allowed to render attributes => MergeAttribute(AbortSysAttachDataAttribute(attributes), "data-continue", value => "data-continue"), // abort rendering true, // pass in all binding attributes If == null ? null : If.Evaluate(page), On == null ? null : On.Evaluate(page), ClassName == null ? null : ClassName.Evaluate(page), Action == null ? null : Action.Evaluate(page), GroupName == null ? null : GroupName.Evaluate(page), StrictMode == null ? null : StrictMode.Evaluate(page), When == null ? null : When.Evaluate(page), contentTemplateBinding); // continue rendering child blocks in the same data context foreach (var block in Blocks) { block.Render(page, templateNames.Concat(ownTemplateNames).ToArray(), writer); } RenderEndTag(writer); } }
protected bool TryRenderIf(AjaxPage page, IEnumerable <string> templateNames, System.IO.TextWriter writer, out AttributeBinding ifBinding, out bool canRender) { if (If == null) { ifBinding = null; canRender = true; return(true); } ifBinding = If.Evaluate(page); if (!ifBinding.IsValid) { canRender = false; return(false); } canRender = JavaScriptHelpers.IsTruthy(ifBinding.Value); return(true); }
internal override AttributeBinding Evaluate(AjaxPage page) { IEnumerable <KeyValuePair <string, object> > arguments = page.Context.Variables.Concat(new KeyValuePair <string, object>[] { new KeyValuePair <string, object>("$dataItem", page.Context.DataItem), new KeyValuePair <string, object>("$index", page.Context.Index), new KeyValuePair <string, object>("$context", page.Context), new KeyValuePair <string, object>("$id", (Func <string, string>)(id => page.Context.GetInstanceId(id))), }); if (!isValid) { return(new AttributeBinding(Attribute, BindingResult.Invalid)); } else { if (script == null) { lock (this) { if (script == null) { script = new JavaScript.ScriptFunction(Page.ScriptEngineFactory, arguments.Select(a => a.Key), Path); } } } try { return(new AttributeBinding(Attribute, new BindingResult() { Value = script.Evaluate(arguments.Select(a => a.Value), Page.ScriptMarshaller), IsValid = true })); } catch { isValid = false; return(new AttributeBinding(Attribute, BindingResult.Invalid)); } } }
internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { bool canRender; AttributeBinding ifBinding; if (!TryRenderIf(page, templateNames, writer, out ifBinding, out canRender)) { Abort(page, templateNames, writer); return; } if (!canRender) { return; } AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string)contentTemplateBinding.Value).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : new string[0]; RenderStartTag(page, writer, ifBinding, contentTemplateBinding); foreach (var block in Blocks) { block.Render(page, templateNames.Concat(ownTemplateNames).ToArray(), writer); } RenderEndTag(writer); }
protected void RenderStartTag(AjaxPage page, System.IO.TextWriter writer, params AttributeBinding[] bindings) { RenderStartTag(page, writer, null, bindings); }
internal override AttributeBinding Evaluate(AjaxPage page) { return binding; }
protected bool TryRenderIf(AjaxPage page, IEnumerable<string> templateNames, System.IO.TextWriter writer, out AttributeBinding ifBinding, out bool canRender) { if (If == null) { ifBinding = null; canRender = true; return true; } ifBinding = If.Evaluate(page); if (!ifBinding.IsValid) { canRender = false; return false; } canRender = JavaScriptHelpers.IsTruthy(ifBinding.Value); return true; }
/// <summary> /// Renders the block to the specified writer in the context of the specified page. /// </summary> /// <param name="page"></param> /// <param name="templateNames"></param> /// <param name="writer"></param> internal virtual void Render(AjaxPage page, string[] templateNames, TextWriter writer) { writer.Write(Markup); }
internal override AttributeBinding Evaluate(AjaxPage page) { IEnumerable<KeyValuePair<string, object>> arguments = page.Context.Variables.Concat( new KeyValuePair<string, object>[] { new KeyValuePair<string, object>("$dataItem", page.Context.DataItem), new KeyValuePair<string, object>("$index", page.Context.Index), new KeyValuePair<string, object>("$context", page.Context), new KeyValuePair<string, object>("$id", (Func<string, string>)(id => page.Context.GetInstanceId(id))), }); if (!isValid) return new AttributeBinding(Attribute, BindingResult.Invalid); else { if (script == null) { lock (this) { if (script == null) script = new JavaScript.ScriptFunction(Page.ScriptEngineFactory, arguments.Select(a => a.Key), Path); } } try { return new AttributeBinding(Attribute, new BindingResult() { Value = script.Evaluate(arguments.Select(a => a.Value), Page.ScriptMarshaller), IsValid = true }); } catch { isValid = false; return new AttributeBinding(Attribute, BindingResult.Invalid); } } }
internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { bool canRender; AttributeBinding ifBinding; if (!TryRenderIf(page, templateNames, writer, out ifBinding, out canRender)) { Abort(page, templateNames, writer); return; } if (!canRender) return; // Output the original template if toggle on was not specified if (On == null) { Abort(page, templateNames, writer); return; } // Get the data associated with the data view var onBinding = On.Evaluate(page); // Output the original template if no data for on was found if (!onBinding.IsValid) { Abort(page, templateNames, writer); return; } var onValue = onBinding.Value; var classValue = ""; var classBinding = (AttributeBinding)null; if (ClassName != null) { classBinding = ClassName.Evaluate(page); // Output the original template if no data for class was found if (!classBinding.IsValid) { Abort(page, templateNames, writer); return; } classValue = (string)classBinding.Value; } ToggleAction? actionValue; var actionBinding = (AttributeBinding)null; // Get the value of the toggle action (i.e.: show, hide, etc.) if (Action != null) { actionBinding = Action.Evaluate(page); // Output the original template if no data for action was found if (!actionBinding.IsValid) { Abort(page, templateNames, writer); return; } actionValue = (ToggleAction)Enum.Parse(typeof(ToggleAction), (string)actionBinding.Value, true); } else if (!string.IsNullOrEmpty(classValue)) actionValue = ToggleAction.AddClass; else actionValue = ToggleAction.Show; var groupNameBinding = (AttributeBinding)null; if (GroupName != null) { groupNameBinding = GroupName.Evaluate(page); // Output the original template if no data for group name was found if (!groupNameBinding.IsValid) { Abort(page, templateNames, writer); return; } } var strictModeValue = false; var strictModeBinding = (AttributeBinding)null; if (StrictMode != null) { strictModeBinding = StrictMode.Evaluate(page); // Output the original template if no data for strict mode was found if (!strictModeBinding.IsValid) { Abort(page, templateNames, writer); return; } if (strictModeBinding.Value is bool) strictModeValue = (bool)strictModeBinding.Value; else strictModeValue = bool.Parse((string)strictModeBinding.Value); } bool? equals; var whenBinding = (AttributeBinding)null; // Evaluate whether the on and when conditions are equal or // satisified, which determines whether the toggle is on or off if (When == null) { if (strictModeValue) { // In strict mode the on value must be a boolean true if (!(onValue is bool)) throw new ApplicationException(string.Format("With strict mode enabled, toggle:on should be a value of type Boolean, actual type \"{0}\".", onValue == null ? "null" : onValue.GetType().Name)); equals = (bool) onValue; } else if (onValue is System.Collections.IEnumerable) { equals = false; // Satisfied if there are any items foreach (object o in (System.Collections.IEnumerable)onValue) { equals = true; break; } } else { // Otherwise, check to see that the on value is "truthy" equals = JavaScriptHelpers.IsTruthy(onValue); } } else { whenBinding = When.Evaluate(page); var whenValue = whenBinding.Value; if (whenValue == null) equals = (onValue == null); else if (whenValue is FunctionInstance) { object result; try { result = Page.ScriptMarshaller.Unwrap(((FunctionInstance)whenValue).Call(null, Page.ScriptMarshaller.Wrap(onValue))); } catch { Abort(page, templateNames, writer); return; } if (strictModeValue) { if (!(result is bool)) throw new ApplicationException(string.Format("With strict mode enabled, toggle:when function should return a value of type Boolean, found type \"{0}\".", result == null ? "null" : result.GetType().Name)); equals = (bool)result; } else { equals = JavaScriptHelpers.IsTruthy(result); } } else { equals = whenValue.Equals(onValue); } } // If no class value is defined then abort if ((actionValue == ToggleAction.AddClass || actionValue == ToggleAction.RemoveClass) && string.IsNullOrEmpty(classValue)) { Abort(page, templateNames, writer); return; } bool render = actionValue == ToggleAction.Render || actionValue == ToggleAction.Dispose; AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string) contentTemplateBinding.Value).Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries) : new string[0]; using (var context = render ? page.BeginContext(page.Context.DataItem, null) : null) { RenderStartTag(page, writer, attrs => MergeAttribute(MergeAttribute(MergeAttribute(attrs, "class", value => { if (actionValue == ToggleAction.AddClass || actionValue == ToggleAction.RemoveClass) { if ((actionValue == ToggleAction.AddClass && equals.Value) || (actionValue == ToggleAction.RemoveClass && !equals.Value)) value = AttributeHelper.EnsureClassName(value, classValue); else value = AttributeHelper.RemoveClassName(value, classValue); } // Add/remove the "toggle-on" and "toggle-off" classes based on state value = AttributeHelper.EnsureClassName(value, equals.Value ? "toggle-on" : "toggle-off"); value = AttributeHelper.RemoveClassName(value, equals.Value ? "toggle-off" : "toggle-on"); return value; }).ToArray(), "style", value => { if (actionValue == ToggleAction.Show || actionValue == ToggleAction.Hide || actionValue == ToggleAction.Render || actionValue == ToggleAction.Dispose) { if (((actionValue == ToggleAction.Show || actionValue == ToggleAction.Render) && equals.Value) || ((actionValue == ToggleAction.Hide || actionValue == ToggleAction.Dispose) && !equals.Value)) { if (AttributeHelper.GetCssStyle(value, "display") == "none") value = AttributeHelper.RemoveCssStyle(value, "display"); } else value = AttributeHelper.EnsureCssStyle(value, "display", "none"); } return value; }).ToArray(), "disabled", value => { if (actionValue == ToggleAction.Enable || actionValue == ToggleAction.Disable) { if ((actionValue == ToggleAction.Enable && equals.Value) || (actionValue == ToggleAction.Disable && !equals.Value)) value = null; else value = "disabled"; } return value; }), ifBinding, onBinding, classBinding, actionBinding, groupNameBinding, strictModeBinding, whenBinding, contentTemplateBinding, // If this is render/dispose, include the nested template index as a special attribute render ? new AttributeBinding(new Attribute() { Name = "data-sys-tmplidx", Value = NestedTemplateIndex.ToString() }, null) : null, render ? new AttributeBinding(new Attribute() { Name = "data-sys-tcindex", Value = context.Id }, null) : null); // Only render the inner blocks if the template would be rendered client-side if (!render || (actionValue == ToggleAction.Render && equals.Value) || (actionValue == ToggleAction.Dispose && !equals.Value)) { foreach (var block in Blocks) block.Render(page, templateNames.Concat(ownTemplateNames).ToArray(), writer); } RenderEndTag(writer); } }
internal override void Abort(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { // Write out a render/dispose toggle since it will be interpreted as a template // and conditionally render. Otherwise, the content within the toggle is not affected // by the toggle from a rendering perspective. if (IsTemplate(page)) base.Abort(page, templateNames, writer); else { AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { base.Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string) contentTemplateBinding.Value).Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries) : new string[0]; RenderStartTag(page, writer, // replace data-sys-attach attribute with sys:attach and add data-continue attribute to notify client that children were allowed to render attributes => MergeAttribute(AbortSysAttachDataAttribute(attributes), "data-continue", value => "data-continue"), // abort rendering true, // pass in all binding attributes If == null ? null : If.Evaluate(page), On == null ? null : On.Evaluate(page), ClassName == null ? null : ClassName.Evaluate(page), Action == null ? null : Action.Evaluate(page), GroupName == null ? null : GroupName.Evaluate(page), StrictMode == null ? null : StrictMode.Evaluate(page), When == null ? null : When.Evaluate(page), contentTemplateBinding); // continue rendering child blocks in the same data context foreach (var block in Blocks) block.Render(page, templateNames.Concat(ownTemplateNames).ToArray(), writer); RenderEndTag(writer); } }
internal abstract AttributeBinding Evaluate(AjaxPage page);
internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { RenderStartTag(page, writer); }
/// <summary> /// Render the data view to the output for the current page. /// </summary> /// <param name="page"></param> /// <param name="templateNames"></param> /// <param name="writer"></param> internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { bool canRender; AttributeBinding ifBinding; if (!TryRenderIf(page, templateNames, writer, out ifBinding, out canRender)) { Abort(page, templateNames, writer); return; } if (!canRender) return; // Output the original template if data source was not specified if (Data == null) { Abort(page, templateNames, writer); return; } // Get the data associated with the data view var dataBinding = Data.Evaluate(page); // Output the original template if no data was found if (!dataBinding.IsValid) { Abort(page, templateNames, writer); return; } // Render the inline template for top level dataviews string templateId = null; string controlId = null; bool renderControlId = false; if (page.Context.IsGlobal) { templateId = page.NextControlId; controlId = Attributes.Where(a => a.Name == "id").Select(a => a.Value).FirstOrDefault(); if (controlId == null) { renderControlId = true; controlId = page.NextControlId; } writer.Write("<"); writer.Write(Tag); writer.Write(" class='sys-template' id='"); writer.Write(templateId); writer.Write("'>"); writer.Write(Template); writer.Write("</"); writer.Write(Tag); writer.Write(">"); } AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string) contentTemplateBinding.Value).Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries) : new string[0]; RenderStartTag(page, writer, ifBinding, dataBinding, contentTemplateBinding, // Include the nested template index as a special attribute new AttributeBinding(new Attribute() { Name = "data-sys-tmplidx", Value = NestedTemplateIndex.ToString() }, null), // If this is a top-level dataview then we will need to ensure an id so that linking can occur renderControlId ? new AttributeBinding(new Attribute() { Name = "id", Value = controlId }, null) : null); // Convert the data into a list IEnumerable list; if (dataBinding.Value == null) list = new object[0]; else if (dataBinding.Value is IEnumerable && !(dataBinding.Value is string)) list = (IEnumerable) dataBinding.Value; else list = new[] {dataBinding.Value}; // Process the template for each list item var index = 0; foreach (var item in list) { // Begin a new template context using (var context = page.BeginContext(item, index++, null)) { RenderContextBeginMarker(context, Tag, writer); foreach (var block in Blocks) block.Render(page, templateNames.Concat(ownTemplateNames).ToArray(), writer); RenderContextEndMarker(context, Tag, writer); } } RenderEndTag(writer); // Render script linking logic if (page.Context.IsGlobal) writer.Write("<script type=\"text/javascript\">$exoweb({{ domReady: function() {{ Sys.Application.linkElement(document.getElementById(\"{0}\"), document.getElementById(\"{1}\")); }} }});</script>", controlId, templateId); }
public void Render(AjaxPage page, System.IO.TextWriter writer, bool isHtmlBool, bool generateIds, bool isTextArea) { // Bound attribute if (binding != null) { // Valid binding if (binding.IsValid) { // Render the binding value for sys attributes if (attribute.Name.StartsWith("sys:")) { var attributeName = attribute.Name.Substring(4); // Render two-way binding expressions if (attribute.Binding.IsTwoWay) { RenderAttribute(writer, "data-sys-" + attributeName, attribute.Binding.Expression); } if (binding.Value != null) { if (attributeName != "if" && attributeName != "innerhtml" && attributeName != "innertext" && !(isTextArea && attributeName == "value") && !attribute.Name.StartsWith("sys:class-")) { if (isHtmlBool) { if (JavaScriptHelpers.IsTruthy(binding.Value)) { RenderAttribute(writer, attributeName, attributeName); } } else { RenderAttribute(writer, attributeName, binding.Value.ToString()); } } } } else { RenderAttribute(writer, "data-" + attribute.Name.Replace(':', '-'), attribute.Binding.Expression); } } // Invalid binding else { RenderAttribute(writer, attribute.Name, attribute.Binding.Expression); } } else if (generateIds && (attribute.Name == "id" || attribute.Name == "sys:id")) { RenderAttribute(writer, "id", page.Context.GetInstanceId(attribute.Value)); } // Simple attribute else if (attribute.Name.Contains(":") && attribute.Name != "sys:id") { RenderAttribute(writer, "data-" + attribute.Name.Replace(':', '-'), attribute.Value); } else if (isHtmlBool) { if (JavaScriptHelpers.IsTruthy(attribute.Value)) { RenderAttribute(writer, attribute.Name, attribute.Name); } } else { RenderAttribute(writer, attribute.Name, attribute.Value); } }
internal override AttributeBinding Evaluate(AjaxPage page) { var path = Path; string sourcePath; if (Parameters.TryGetValue("source", out sourcePath) && sourcePath.Length > 0) path = sourcePath + "." + path; var result = page.EvaluatePath(path); if (result.IsValid) { if (result.Value == null) { string nullValue; if (Parameters.TryGetValue("nullValue", out nullValue)) result.Value = nullValue; } else { string format; if (Parameters.TryGetValue("format", out format) && !string.IsNullOrEmpty(format) && result.Value is IFormattable) result.Value = ((IFormattable)result.Value).ToString(format, null); else { string transformText; if (transform == null && Parameters.TryGetValue("transform", out transformText) && transformText.Length > 0) transform = Transform.Compile(transformText); if (result.IsValid && transform != null) { IEnumerable transformed; result.IsValid = transform.TryExecute(page, (IEnumerable)result.Value, out transformed); result.Value = result.IsValid ? transformed : null; } } } } return new AttributeBinding(Attribute, result); }
protected void RenderStartTag(AjaxPage page, System.IO.TextWriter writer, Func <IEnumerable <AttributeBinding>, IEnumerable <AttributeBinding> > attributeTransform, params AttributeBinding[] bindings) { RenderStartTag(page, writer, attributeTransform, false, bindings); }
protected void RenderStartTag(AjaxPage page, System.IO.TextWriter writer, Func <IEnumerable <AttributeBinding>, IEnumerable <AttributeBinding> > attributeTransform, bool abort, params AttributeBinding[] bindings) { // Immediately abort if no tag name if (Tag == null) { return; } // Open Tag writer.Write("<" + Tag); // Attributes string innerContent = null; var attributes = (Attributes ?? new List <Attribute>()) .Select(attribute => attribute.Binding == null ? new AttributeBinding(attribute, null) : attribute.Binding.Evaluate(page)); // Adding binding attributes if necessary if (bindings != null && bindings.Length > 0) { attributes = attributes.Concat(bindings.Where(b => b != null)); } // Transform the attributes if necessary if (attributeTransform != null) { attributes = attributeTransform(attributes); } string classNames = null; bool foundId = false; bool isTextArea = Tag.Equals("textarea", StringComparison.InvariantCultureIgnoreCase); var attrs = attributes.ToArray(); // Write the attributes to the output stream foreach (var attribute in attrs) { // Ensure that multiple id attributes are not specified if (!page.Context.IsGlobal && (attribute.Name == "id" || attribute.Name == "sys:id")) { if (foundId) { throw new ApplicationException("Found multiple id attributes: " + Markup); } foundId = true; } // Determine if the attribute represents bound element content if (attribute.IsBound) { if (attribute.Name == "sys:innerhtml" || (isTextArea && attribute.Name == "sys:value")) { innerContent = (attribute.DisplayValue ?? ""); } else if (attribute.Name == "sys:innertext") { innerContent = HttpUtility.HtmlEncode(attribute.DisplayValue ?? ""); } } bool isHtmlBoolean; var attributeName = attribute.Name.StartsWith("sys:") ? attribute.Name.Substring(4) : attribute.Name; if (Tag.Equals("input", StringComparison.InvariantCultureIgnoreCase)) { var attr = attrs.SingleOrDefault(a => a.Name.Equals("type", StringComparison.InvariantCultureIgnoreCase) && a.IsValid && a.Value != null); if (attr == null) { isHtmlBoolean = HtmlHelpers.IsBooleanAttribute(attributeName, Tag, null, true); } else { isHtmlBoolean = HtmlHelpers.IsBooleanAttribute(attributeName, Tag, attr.Value.ToString()); } } else { isHtmlBoolean = HtmlHelpers.IsBooleanAttribute(attributeName, Tag); } if (abort) { attribute.Abort(writer, isHtmlBoolean); } else { if (attribute.Name == "class") { if (classNames == null) { classNames = (string)attribute.Value; } else { classNames += " " + (string)attribute.Value; } } else { if (attribute.Name.StartsWith("sys:class-")) { // If binding evaluates as truthy, then render the store the class name if (JavaScriptHelpers.IsTruthy(attribute.Value)) { string sysClassValue = attribute.Name.Substring(10); if (classNames == null) { classNames = sysClassValue; } else { classNames += (classNames.Length > 0 ? " " : "") + sysClassValue; } } } attribute.Render(page, writer, isHtmlBoolean, !page.Context.IsGlobal, isTextArea); } } } // Write direct class and sys:class- attribute values together. Note: by checking // for null we may be avoiding writing a class attribute altogether whereas the // client framework would have produced an empty class attribute. if (classNames != null) { writer.Write(" class=\""); HttpUtility.HtmlAttributeEncode(classNames, writer); writer.Write("\""); } // Close Tag if (IsEmpty) { if (!string.IsNullOrEmpty(innerContent)) { writer.Write(">" + innerContent + "</" + Tag + ">"); } else if (HtmlHelpers.IsSelfClosing(Tag)) { writer.Write(" />"); } else { writer.Write("></" + Tag + ">"); } } else if (!string.IsNullOrEmpty(innerContent)) { writer.Write(">" + innerContent); } else { writer.Write(">"); } }
internal override void Render(AjaxPage page, string[] templateNames, TextWriter writer) { try { ExoWeb.OnBeginRender(page, this); foreach (var block in Blocks) block.Render(page, templateNames.Concat(ContentTemplateNames).ToArray(), writer); } finally { ExoWeb.OnEndRender(page, this); } }
/// <summary> /// Render the data view to the output for the current page. /// </summary> /// <param name="page"></param> /// <param name="templateNames"></param> /// <param name="writer"></param> internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { bool canRender; AttributeBinding ifBinding; if (!TryRenderIf(page, templateNames, writer, out ifBinding, out canRender)) { Abort(page, templateNames, writer); return; } if (!canRender) { return; } // Output the original template if data source was not specified if (Data == null) { Abort(page, templateNames, writer); return; } // Get the data associated with the data view var dataBinding = Data.Evaluate(page); // Output the original template if no data was found if (!dataBinding.IsValid) { Abort(page, templateNames, writer); return; } // Render the inline template for top level dataviews string templateId = null; string controlId = null; bool renderControlId = false; if (page.Context.IsGlobal) { templateId = page.NextControlId; controlId = Attributes.Where(a => a.Name == "id").Select(a => a.Value).FirstOrDefault(); if (controlId == null) { renderControlId = true; controlId = page.NextControlId; } writer.Write("<"); writer.Write(Tag); writer.Write(" class='sys-template' id='"); writer.Write(templateId); writer.Write("'>"); writer.Write(Template); writer.Write("</"); writer.Write(Tag); writer.Write(">"); } AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string)contentTemplateBinding.Value).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : new string[0]; RenderStartTag(page, writer, ifBinding, dataBinding, contentTemplateBinding, // Include the nested template index as a special attribute new AttributeBinding(new Attribute() { Name = "data-sys-tmplidx", Value = NestedTemplateIndex.ToString() }, null), // If this is a top-level dataview then we will need to ensure an id so that linking can occur renderControlId ? new AttributeBinding(new Attribute() { Name = "id", Value = controlId }, null) : null); // Convert the data into a list IEnumerable list; if (dataBinding.Value == null) { list = new object[0]; } else if (dataBinding.Value is IEnumerable && !(dataBinding.Value is string)) { list = (IEnumerable)dataBinding.Value; } else { list = new[] { dataBinding.Value } }; // Process the template for each list item var index = 0; foreach (var item in list) { // Begin a new template context using (var context = page.BeginContext(item, index++, null)) { RenderContextBeginMarker(context, Tag, writer); foreach (var block in Blocks) { block.Render(page, templateNames.Concat(ownTemplateNames).ToArray(), writer); } RenderContextEndMarker(context, Tag, writer); } } RenderEndTag(writer); // Render script linking logic if (page.Context.IsGlobal) { writer.Write("<script type=\"text/javascript\">$exoweb({{ domReady: function() {{ Sys.Application.linkElement(document.getElementById(\"{0}\"), document.getElementById(\"{1}\")); }} }});</script>", controlId, templateId); } }
/// <summary> /// /// </summary> /// <param name="page"></param> /// <returns></returns> internal bool IsTemplate(AjaxPage page) { bool result = false; if (Action != null) { // action is literal render/dispose if (Action.Expression == "render" || Action.Expression == "dispose") result = true; else { // if not within a dataview or template, then any binding expressions // for toggle action would not be dependent on template context data, // so use the newly created page instance with a null context var action = Action.Evaluate(page == null ? Page.Current as AjaxPage : page); if (action.IsValid && (action.DisplayValue == "render" || action.DisplayValue == "dispose")) result = true; } } return result; }
internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { bool canRender; AttributeBinding ifBinding; if (!TryRenderIf(page, templateNames, writer, out ifBinding, out canRender)) { Abort(page, templateNames, writer); return; } if (!canRender) return; // Output the original template if data source was not specified if (Data == null) { Abort(page, templateNames, writer); return; } // Get the data associated with the content control var dataBinding = Data.Evaluate(page); var templateBinding = Template != null ? Template.Evaluate(page) : null; var dataTypeBinding = DataType != null ? DataType.Evaluate(page) : null; // Output the original template if the data binding expression could not be evaluated if ((!dataBinding.IsValid && (dataTypeBinding == null || !dataTypeBinding.IsValid)) || (templateBinding != null && !templateBinding.IsValid)) { Abort(page, templateNames, writer); return; } AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string) contentTemplateBinding.Value).Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries) : new string[0]; // Just render an empty content element if the data is null if (dataBinding.IsValid && dataBinding.Value == null) { RenderStartTag(page, writer, ifBinding, dataBinding, contentTemplateBinding); RenderEndTag(writer); return; } // Get the binding data object data; bool isAdapter; ModelType referenceType; Type valueType; bool isList; // Valid binding if (dataBinding.IsValid) { data = dataBinding.Value; isAdapter = data is Adapter; object realData = isAdapter ? ((Adapter)data).RawValue : data; ModelProperty property = dataBinding.Property; referenceType = realData is ModelInstance ? ((ModelInstance)realData).Type : property is ModelReferenceProperty ? ((ModelReferenceProperty)property).PropertyType : null; valueType = realData != null && !(realData is ModelInstance || realData is IEnumerable<ModelInstance>) ? realData.GetType() : property is ModelValueProperty ? ((ModelValueProperty)property).PropertyType : null; isList = (property != null && property.IsList) || (realData is IEnumerable && !(realData is string)); } // Datatype hint only else { var dataType = (string)dataTypeBinding.Value; isAdapter = Data is Binding.AdapterExtension || dataType == "ExoWeb.View.Adapter"; isList = dataType.EndsWith("[]"); if (isList) dataType = dataType.Substring(0, dataType.Length - 2); valueType = dataType == "String" ? typeof(string) : dataType == "Number" ? typeof(decimal) : dataType == "Date" ? typeof(DateTime) : dataType == "Boolean" ? typeof(bool) : null; referenceType = valueType == null ? ModelContext.Current.GetModelType(dataType) : null; data = null; } // Evaluate content:template binding to get this content control's declare template(s) var templates = templateBinding != null && templateBinding.DisplayValue != null ? templateBinding.DisplayValue.Split(' ') : new string[0]; // Join in the ContentTemplateNames value (sys:content-template) and the context's template names templates = templateNames.Concat(templates).Concat(ownTemplateNames).Distinct().ToArray(); // Find the correct template var template = FindTemplate(page, isAdapter, referenceType, valueType, isList, templates); // Output the original template if a matching template could not be found if (template == null) { writer.Write("<!-- A template could not be found matching the specified criteria (TagName={0}, Adapter={1}, Type={2}{3}, IsList={4}, Names='{5}') -->", Tag, isAdapter, referenceType, valueType, isList, string.Join(", ", templates)); Abort(page, templateNames, writer); return; } // Render the template inside a new template context using (var context = page.BeginContext(data, null)) { // Render the original content start tag RenderStartTag(page, writer, attributes => template.Class.Length > 0 ? MergeClassName(attributes, template) : attributes, ifBinding, dataBinding, contentTemplateBinding, templateBinding, new AttributeBinding(new Attribute() { Name = "data-sys-tcindex", Value = context.Id }, null)); // Render the content template template.Render(page, templates.Where(t => !template.Name.Contains(t)).Concat(template.ContentTemplateNames).ToArray(), writer); // Render the original content end tag RenderEndTag(writer); } }
protected void RenderStartTag(AjaxPage page, System.IO.TextWriter writer, Func<IEnumerable<AttributeBinding>, IEnumerable<AttributeBinding>> attributeTransform, params AttributeBinding[] bindings) { RenderStartTag(page, writer, attributeTransform, false, bindings); }
internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { bool canRender; AttributeBinding ifBinding; if (!TryRenderIf(page, templateNames, writer, out ifBinding, out canRender)) { Abort(page, templateNames, writer); return; } if (!canRender) { return; } // Output the original template if toggle on was not specified if (On == null) { Abort(page, templateNames, writer); return; } // Get the data associated with the data view var onBinding = On.Evaluate(page); // Output the original template if no data for on was found if (!onBinding.IsValid) { Abort(page, templateNames, writer); return; } var onValue = onBinding.Value; var classValue = ""; var classBinding = (AttributeBinding)null; if (ClassName != null) { classBinding = ClassName.Evaluate(page); // Output the original template if no data for class was found if (!classBinding.IsValid) { Abort(page, templateNames, writer); return; } classValue = (string)classBinding.Value; } ToggleAction?actionValue; var actionBinding = (AttributeBinding)null; // Get the value of the toggle action (i.e.: show, hide, etc.) if (Action != null) { actionBinding = Action.Evaluate(page); // Output the original template if no data for action was found if (!actionBinding.IsValid) { Abort(page, templateNames, writer); return; } actionValue = (ToggleAction)Enum.Parse(typeof(ToggleAction), (string)actionBinding.Value, true); } else if (!string.IsNullOrEmpty(classValue)) { actionValue = ToggleAction.AddClass; } else { actionValue = ToggleAction.Show; } var groupNameBinding = (AttributeBinding)null; if (GroupName != null) { groupNameBinding = GroupName.Evaluate(page); // Output the original template if no data for group name was found if (!groupNameBinding.IsValid) { Abort(page, templateNames, writer); return; } } var strictModeValue = false; var strictModeBinding = (AttributeBinding)null; if (StrictMode != null) { strictModeBinding = StrictMode.Evaluate(page); // Output the original template if no data for strict mode was found if (!strictModeBinding.IsValid) { Abort(page, templateNames, writer); return; } if (strictModeBinding.Value is bool) { strictModeValue = (bool)strictModeBinding.Value; } else { strictModeValue = bool.Parse((string)strictModeBinding.Value); } } bool?equals; var whenBinding = (AttributeBinding)null; // Evaluate whether the on and when conditions are equal or // satisified, which determines whether the toggle is on or off if (When == null) { if (strictModeValue) { // In strict mode the on value must be a boolean true if (!(onValue is bool)) { throw new ApplicationException(string.Format("With strict mode enabled, toggle:on should be a value of type Boolean, actual type \"{0}\".", onValue == null ? "null" : onValue.GetType().Name)); } equals = (bool)onValue; } else if (onValue is System.Collections.IEnumerable) { equals = false; // Satisfied if there are any items foreach (object o in (System.Collections.IEnumerable)onValue) { equals = true; break; } } else { // Otherwise, check to see that the on value is "truthy" equals = JavaScriptHelpers.IsTruthy(onValue); } } else { whenBinding = When.Evaluate(page); var whenValue = whenBinding.Value; if (whenValue == null) { equals = (onValue == null); } else if (whenValue is FunctionInstance) { object result; try { result = Page.ScriptMarshaller.Unwrap(((FunctionInstance)whenValue).Call(null, Page.ScriptMarshaller.Wrap(onValue))); } catch { Abort(page, templateNames, writer); return; } if (strictModeValue) { if (!(result is bool)) { throw new ApplicationException(string.Format("With strict mode enabled, toggle:when function should return a value of type Boolean, found type \"{0}\".", result == null ? "null" : result.GetType().Name)); } equals = (bool)result; } else { equals = JavaScriptHelpers.IsTruthy(result); } } else { equals = whenValue.Equals(onValue); } } // If no class value is defined then abort if ((actionValue == ToggleAction.AddClass || actionValue == ToggleAction.RemoveClass) && string.IsNullOrEmpty(classValue)) { Abort(page, templateNames, writer); return; } bool render = actionValue == ToggleAction.Render || actionValue == ToggleAction.Dispose; AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string)contentTemplateBinding.Value).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : new string[0]; using (var context = render ? page.BeginContext(page.Context.DataItem, null) : null) { RenderStartTag(page, writer, attrs => MergeAttribute(MergeAttribute(MergeAttribute(attrs, "class", value => { if (actionValue == ToggleAction.AddClass || actionValue == ToggleAction.RemoveClass) { if ((actionValue == ToggleAction.AddClass && equals.Value) || (actionValue == ToggleAction.RemoveClass && !equals.Value)) { value = AttributeHelper.EnsureClassName(value, classValue); } else { value = AttributeHelper.RemoveClassName(value, classValue); } } // Add/remove the "toggle-on" and "toggle-off" classes based on state value = AttributeHelper.EnsureClassName(value, equals.Value ? "toggle-on" : "toggle-off"); value = AttributeHelper.RemoveClassName(value, equals.Value ? "toggle-off" : "toggle-on"); return(value); }).ToArray(), "style", value => { if (actionValue == ToggleAction.Show || actionValue == ToggleAction.Hide || actionValue == ToggleAction.Render || actionValue == ToggleAction.Dispose) { if (((actionValue == ToggleAction.Show || actionValue == ToggleAction.Render) && equals.Value) || ((actionValue == ToggleAction.Hide || actionValue == ToggleAction.Dispose) && !equals.Value)) { if (AttributeHelper.GetCssStyle(value, "display") == "none") { value = AttributeHelper.RemoveCssStyle(value, "display"); } } else { value = AttributeHelper.EnsureCssStyle(value, "display", "none"); } } return(value); }).ToArray(), "disabled", value => { if (actionValue == ToggleAction.Enable || actionValue == ToggleAction.Disable) { if ((actionValue == ToggleAction.Enable && equals.Value) || (actionValue == ToggleAction.Disable && !equals.Value)) { value = null; } else { value = "disabled"; } } return(value); }), ifBinding, onBinding, classBinding, actionBinding, groupNameBinding, strictModeBinding, whenBinding, contentTemplateBinding, // If this is render/dispose, include the nested template index as a special attribute render ? new AttributeBinding(new Attribute() { Name = "data-sys-tmplidx", Value = NestedTemplateIndex.ToString() }, null) : null, render ? new AttributeBinding(new Attribute() { Name = "data-sys-tcindex", Value = context.Id }, null) : null); // Only render the inner blocks if the template would be rendered client-side if (!render || (actionValue == ToggleAction.Render && equals.Value) || (actionValue == ToggleAction.Dispose && !equals.Value)) { foreach (var block in Blocks) { block.Render(page, templateNames.Concat(ownTemplateNames).ToArray(), writer); } } RenderEndTag(writer); } }
Template FindTemplate(AjaxPage page, bool isAdapter, ModelType entityType, Type valueType, bool isList, string[] names) { // Find the appropriate template var results = page.Templates.OfType<Template>().Reverse().Where( t => { // First ensure the template tags match if (t.Tag != Tag) return false; // Then see if the template is for adapter bindings if (isAdapter != t.IsAdapter) return false; // Then verify the datatypes if (!String.IsNullOrEmpty(t.DataType)) { // Reference if (entityType != null) { // Verify the type matches if it is not the base type var type = entityType; while (type != null && type.Name != t.DataType) type = type.BaseType; if (type == null) return false; } // Value else { string type = JsonConverter.GetJsonValueType(valueType); // Use "Array" as the type name for template matching for value types that are IEnumerable and not string. // JsonConverter is primarily used for serialization of instance data and so the type returned will be // the declared type of the property. Currently value type arrays are not fully supported but can be // represented as "Object" for minimal functionality. For template matching we really want "Array" instead, // since even in the case of custom serialization the value would be deserialized as an Array client-side // and so if used in template matching the type name would be "Array". if ((type == null || type == "Object") && typeof(IEnumerable).IsAssignableFrom(valueType)) type = "Array"; if (type == null || type != t.DataType) return false; } } // Check whether the template is specific to references if (t.IsReference != null && t.IsReference != (entityType != null)) return false; // Check whether the template is specific to lists if (t.IsList != null && t.IsList != isList) return false; // Finally, verify that the template names match sufficiently foreach (var name in t.Name) { if (!names.Contains(name)) return false; } return true; }); return results.FirstOrDefault(); }
protected bool TryContentTemplate(AjaxPage page, IEnumerable<string> templateNames, System.IO.TextWriter writer, out AttributeBinding contentTemplateBinding) { if (ContentTemplate == null) { contentTemplateBinding = null; return true; } contentTemplateBinding = ContentTemplate.Evaluate(page); return contentTemplateBinding.IsValid && contentTemplateBinding.Value is string; }
internal override AttributeBinding Evaluate(AjaxPage page) { return(binding); }
Template FindTemplate(AjaxPage page, bool isAdapter, ModelType entityType, Type valueType, bool isList, string[] names) { // Find the appropriate template return(page.Templates.OfType <Template>().Reverse().FirstOrDefault( t => { // First ensure the template tags match if (t.Tag != Tag) { return false; } // Then see if the template is for adapter bindings if (isAdapter != t.IsAdapter) { return false; } // Then verify the datatypes if (!String.IsNullOrEmpty(t.DataType)) { // Reference if (entityType != null) { // Verify the type matches if it is not the base type var type = entityType; while (type != null && type.Name != t.DataType) { type = type.BaseType; } if (type == null) { return false; } } // Value else { string type = JsonConverter.GetJsonValueType(valueType); // Use "Array" as the type name for template matching for value types that are IEnumerable and not string. // JsonConverter is primarily used for serialization of instance data and so the type returned will be // the declared type of the property. Currently value type arrays are not fully supported but can be // represented as "Object" for minimal functionality. For template matching we really want "Array" instead, // since even in the case of custom serialization the value would be deserialized as an Array client-side // and so if used in template matching the type name would be "Array". if ((type == null || type == "Object") && typeof(IEnumerable).IsAssignableFrom(valueType)) { type = "Array"; } if (type == null || type != t.DataType) { return false; } } } // Check whether the template is specific to references if (t.IsReference != null && t.IsReference != (entityType != null)) { return false; } // Check whether the template is specific to lists if (t.IsList != null && t.IsList != isList) { return false; } // Finally, verify that the template names match sufficiently foreach (var name in t.Name) { if (!names.Contains(name)) { return false; } } return true; })); }
internal override AttributeBinding Evaluate(AjaxPage page) { // Evaluate the binding path var result = page.EvaluatePath(Path); // Invalid result if (result.Property == null) result.IsValid = false; // # syntax else if (Extension == "#") { string format = null; Parameters.TryGetValue("format", out format); string value; result.IsValid = Adapter.TryGetDisplayValue(result.Property, format, result.Value, out value); result.Value = value; } // @ syntax else if (Extension == "@") { if (string.IsNullOrEmpty(Path)) { if (!(page.Context.DataItem is Adapter)) throw new ApplicationException("No path was specified for the \"@\" markup extension, and the source is not an adapter."); result.Value = page.Context.DataItem; } else { var args = ExoWeb.OnBeforeCreateAdapter(this, result.Source, result.Property); result = new BindingResult() { Value = new Adapter(result, args != null ? args.ModifiedParameters ?? Parameters : Parameters), IsValid = result.IsValid, Property = result.Property, Source = result.Source }; } } return new AttributeBinding(Attribute, result); }
internal override void Render(AjaxPage page, string[] templateNames, System.IO.TextWriter writer) { bool canRender; AttributeBinding ifBinding; if (!TryRenderIf(page, templateNames, writer, out ifBinding, out canRender)) { Abort(page, templateNames, writer); return; } if (!canRender) { return; } // Output the original template if data source was not specified if (Data == null) { Abort(page, templateNames, writer); return; } // Get the data associated with the content control var dataBinding = Data.Evaluate(page); var templateBinding = Template != null?Template.Evaluate(page) : null; var dataTypeBinding = DataType != null?DataType.Evaluate(page) : null; // Output the original template if the data binding expression could not be evaluated if ((!dataBinding.IsValid && (dataTypeBinding == null || !dataTypeBinding.IsValid)) || (templateBinding != null && !templateBinding.IsValid)) { Abort(page, templateNames, writer); return; } AttributeBinding contentTemplateBinding; if (!TryContentTemplate(page, templateNames, writer, out contentTemplateBinding)) { Abort(page, templateNames, writer); return; } var ownTemplateNames = contentTemplateBinding != null ? ((string)contentTemplateBinding.Value).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : new string[0]; // Just render an empty content element if the data is null if (dataBinding.IsValid && dataBinding.Value == null) { RenderStartTag(page, writer, ifBinding, dataBinding, contentTemplateBinding); RenderEndTag(writer); return; } // Get the binding data object data; bool isAdapter; ModelType referenceType; Type valueType; bool isList; // Valid binding if (dataBinding.IsValid) { data = dataBinding.Value; isAdapter = data is Adapter; object realData = isAdapter ? ((Adapter)data).RawValue : data; ModelProperty property = dataBinding.Property; referenceType = realData is ModelInstance ? ((ModelInstance)realData).Type : property is ModelReferenceProperty ? ((ModelReferenceProperty)property).PropertyType : null; valueType = realData != null && !(realData is ModelInstance || realData is IEnumerable <ModelInstance>) ? realData.GetType() : property is ModelValueProperty ? ((ModelValueProperty)property).PropertyType : null; isList = (property != null && property.IsList) || (realData is IEnumerable && !(realData is string)); } // Datatype hint only else { var dataType = (string)dataTypeBinding.Value; isAdapter = Data is Binding.AdapterExtension || dataType == "ExoWeb.View.Adapter"; isList = dataType.EndsWith("[]"); if (isList) { dataType = dataType.Substring(0, dataType.Length - 2); } valueType = dataType == "String" ? typeof(string) : dataType == "Number" ? typeof(decimal) : dataType == "Date" ? typeof(DateTime) : dataType == "Boolean" ? typeof(bool) : null; referenceType = valueType == null?ModelContext.Current.GetModelType(dataType) : null; data = null; } // Evaluate content:template binding to get this content control's declare template(s) var templates = templateBinding != null && templateBinding.DisplayValue != null?templateBinding.DisplayValue.Split(' ') : new string[0]; // Join in the ContentTemplateNames value (sys:content-template) and the context's template names templates = templateNames.Concat(templates).Concat(ownTemplateNames).Distinct().ToArray(); // Find the correct template var template = FindTemplate(page, isAdapter, referenceType, valueType, isList, templates); // Output the original template if a matching template could not be found if (template == null) { writer.Write("<!-- A template could not be found matching the specified criteria (TagName={0}, Adapter={1}, Type={2}{3}, IsList={4}, Names='{5}') -->", Tag, isAdapter, referenceType, valueType, isList, string.Join(", ", templates)); Abort(page, templateNames, writer); return; } // Render the template inside a new template context using (var context = page.BeginContext(data, null)) { // Render the original content start tag RenderStartTag(page, writer, attributes => template.Class.Length > 0 ? MergeClassName(attributes, template) : attributes, ifBinding, dataBinding, contentTemplateBinding, templateBinding, new AttributeBinding(new Attribute() { Name = "data-sys-tcindex", Value = context.Id }, null)); // Render the content template template.Render(page, templates.Where(t => !template.Name.Contains(t)).Concat(template.ContentTemplateNames).ToArray(), writer); // Render the original content end tag RenderEndTag(writer); } }
protected void RenderStartTag(AjaxPage page, System.IO.TextWriter writer, Func<IEnumerable<AttributeBinding>, IEnumerable<AttributeBinding>> attributeTransform, bool abort, params AttributeBinding[] bindings) { // Immediately abort if no tag name if (Tag == null) return; // Open Tag writer.Write("<" + Tag); // Attributes string innerContent = null; var attributes = (Attributes ?? new List<Attribute>()) .Select(attribute => attribute.Binding == null ? new AttributeBinding(attribute, null) : attribute.Binding.Evaluate(page)); // Adding binding attributes if necessary if (bindings != null && bindings.Length > 0) attributes = attributes.Concat(bindings.Where(b => b != null)); // Transform the attributes if necessary if (attributeTransform != null) attributes = attributeTransform(attributes); string classNames = null; bool foundId = false; bool isTextArea = Tag.Equals("textarea", StringComparison.InvariantCultureIgnoreCase); var attrs = attributes.ToArray(); // Write the attributes to the output stream foreach (var attribute in attrs) { // Ensure that multiple id attributes are not specified if (!page.Context.IsGlobal && (attribute.Name == "id" || attribute.Name == "sys:id")) { if (foundId) throw new ApplicationException("Found multiple id attributes: " + Markup); foundId = true; } // Determine if the attribute represents bound element content if (attribute.IsBound) { if (attribute.Name == "sys:innerhtml" || (isTextArea && attribute.Name == "sys:value")) innerContent = (attribute.DisplayValue ?? ""); else if (attribute.Name == "sys:innertext") innerContent = HttpUtility.HtmlEncode(attribute.DisplayValue ?? ""); } bool isHtmlBoolean; var attributeName = attribute.Name.StartsWith("sys:") ? attribute.Name.Substring(4) : attribute.Name; if (Tag.Equals("input", StringComparison.InvariantCultureIgnoreCase)) { var attr = attrs.SingleOrDefault(a => a.Name.Equals("type", StringComparison.InvariantCultureIgnoreCase) && a.IsValid && a.Value != null); if (attr == null) isHtmlBoolean = HtmlHelpers.IsBooleanAttribute(attributeName, Tag, null, true); else isHtmlBoolean = HtmlHelpers.IsBooleanAttribute(attributeName, Tag, attr.Value.ToString()); } else isHtmlBoolean = HtmlHelpers.IsBooleanAttribute(attributeName, Tag); if (abort) attribute.Abort(writer, isHtmlBoolean); else { if (attribute.Name == "class") { if (classNames == null) classNames = (string)attribute.Value; else classNames += " " + (string)attribute.Value; } else { if (attribute.Name.StartsWith("sys:class-")) { // If binding evaluates as truthy, then render the store the class name if (JavaScriptHelpers.IsTruthy(attribute.Value)) { string sysClassValue = attribute.Name.Substring(10); if (classNames == null) classNames = sysClassValue; else classNames += (classNames.Length > 0 ? " " : "") + sysClassValue; } } attribute.Render(page, writer, isHtmlBoolean, !page.Context.IsGlobal, isTextArea); } } } // Write direct class and sys:class- attribute values together. Note: by checking // for null we may be avoiding writing a class attribute altogether whereas the // client framework would have produced an empty class attribute. if (classNames != null) { writer.Write(" class=\""); HttpUtility.HtmlAttributeEncode(classNames, writer); writer.Write("\""); } // Close Tag if (IsEmpty) { if (!string.IsNullOrEmpty(innerContent)) writer.Write(">" + innerContent + "</" + Tag + ">"); else if (HtmlHelpers.IsSelfClosing(Tag)) writer.Write(" />"); else writer.Write("></" + Tag + ">"); } else if (!string.IsNullOrEmpty(innerContent)) writer.Write(">" + innerContent); else writer.Write(">"); }
public void Render(AjaxPage page, System.IO.TextWriter writer, bool isHtmlBool, bool generateIds, bool isTextArea) { // Bound attribute if (binding != null) { // Valid binding if (binding.IsValid) { // Render the binding value for sys attributes if (attribute.Name.StartsWith("sys:")) { var attributeName = attribute.Name.Substring(4); // Render two-way binding expressions if (attribute.Binding.IsTwoWay) RenderAttribute(writer, "data-sys-" + attributeName, attribute.Binding.Expression); if (binding.Value != null) { if (attributeName != "if" && attributeName != "innerhtml" && attributeName != "innertext" && !(isTextArea && attributeName == "value") && !attribute.Name.StartsWith("sys:class-")) { if (isHtmlBool) { if (JavaScriptHelpers.IsTruthy(binding.Value)) RenderAttribute(writer, attributeName, attributeName); } else RenderAttribute(writer, attributeName, binding.Value.ToString()); } } } else RenderAttribute(writer, "data-" + attribute.Name.Replace(':', '-'), attribute.Binding.Expression); } // Invalid binding else RenderAttribute(writer, attribute.Name, attribute.Binding.Expression); } else if (generateIds && (attribute.Name == "id" || attribute.Name == "sys:id")) RenderAttribute(writer, "id", page.Context.GetInstanceId(attribute.Value)); // Simple attribute else if (attribute.Name.Contains(":") && attribute.Name != "sys:id") RenderAttribute(writer, "data-" + attribute.Name.Replace(':', '-'), attribute.Value); else if (isHtmlBool) { if (JavaScriptHelpers.IsTruthy(attribute.Value)) RenderAttribute(writer, attribute.Name, attribute.Name); } else RenderAttribute(writer, attribute.Name, attribute.Value); }