diff --git a/Ooui.AspNetCore/ElementResult.cs b/Ooui.AspNetCore/ElementResult.cs index 23234a6..ae8cc0f 100644 --- a/Ooui.AspNetCore/ElementResult.cs +++ b/Ooui.AspNetCore/ElementResult.cs @@ -1,28 +1,30 @@ using System; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + namespace Ooui.AspNetCore { public class ElementResult : ActionResult - { + { readonly Element element; - readonly string title; - + readonly string title; + public ElementResult (Element element, string title = "") - { + { this.element = element; - this.title = title; + this.title = title; } - public override async Task ExecuteResultAsync (ActionContext context) + public override async Task ExecuteResultAsync (ActionContext context) { var response = context.HttpContext.Response; response.StatusCode = 200; response.ContentType = "text/html; charset=utf-8"; var sessionId = WebSocketHandler.BeginSession (context.HttpContext, element); - var html = UI.RenderTemplate (WebSocketHandler.WebSocketPath + "?id=" + sessionId, title: title); + var initialHtml = element.OuterHtml; + Console.WriteLine(initialHtml); + var html = UI.RenderTemplate (WebSocketHandler.WebSocketPath + "?id=" + sessionId, title: title, initialHtml: initialHtml); var htmlBytes = Encoding.UTF8.GetBytes (html); response.ContentLength = htmlBytes.Length; using (var s = response.Body) { diff --git a/Ooui.Forms/Renderers/ViewRenderer.cs b/Ooui.Forms/Renderers/ViewRenderer.cs index b425349..0344223 100644 --- a/Ooui.Forms/Renderers/ViewRenderer.cs +++ b/Ooui.Forms/Renderers/ViewRenderer.cs @@ -21,6 +21,8 @@ namespace Ooui.Forms.Renderers /// protected virtual bool ManageNativeControlLifetime => true; + protected override bool HtmlNeedsFullEndElement => TagName == "div"; + public ViewRenderer (string tagName = "div") : base (tagName) { diff --git a/Ooui.Forms/VisualElementRenderer.cs b/Ooui.Forms/VisualElementRenderer.cs index 628d863..b436710 100644 --- a/Ooui.Forms/VisualElementRenderer.cs +++ b/Ooui.Forms/VisualElementRenderer.cs @@ -62,6 +62,8 @@ namespace Ooui.Forms } } + protected override bool HtmlNeedsFullEndElement => TagName == "div"; + public VisualElementRenderer (string tagName = "div") : base (tagName) { _propertyChangedHandler = OnElementPropertyChanged; diff --git a/Ooui/Client.js b/Ooui/Client.js index e053d5c..d19f249 100644 --- a/Ooui/Client.js +++ b/Ooui/Client.js @@ -38,6 +38,8 @@ function getSize () { // Main entrypoint function ooui (rootElementPath) { + return; + var initialSize = getSize (); var wsArgs = (rootElementPath.indexOf("?") >= 0 ? "&" : "?") + "w=" + initialSize.width + "&h=" + initialSize.height; diff --git a/Ooui/Div.cs b/Ooui/Div.cs index d4d8009..f9098c4 100644 --- a/Ooui/Div.cs +++ b/Ooui/Div.cs @@ -5,6 +5,8 @@ namespace Ooui { public class Div : Element { + protected override bool HtmlNeedsFullEndElement => true; + public Div () : base ("div") { diff --git a/Ooui/Element.cs b/Ooui/Element.cs index 05626be..2e32512 100644 --- a/Ooui/Element.cs +++ b/Ooui/Element.cs @@ -243,5 +243,38 @@ namespace Ooui return base.SaveStateMessageIfNeeded (message); } } + + protected virtual bool HtmlNeedsFullEndElement => false; + + public override void WriteOuterHtml (System.Xml.XmlWriter w) + { + w.WriteStartElement (TagName); + w.WriteAttributeString ("id", Id); + var style = Style.ToString (); + if (style.Length > 0) { + w.WriteAttributeString ("style", style); + } + lock (attributes) { + foreach (var a in attributes) { + var value = (a.Value == null) ? "null" : Convert.ToString (a.Value, System.Globalization.CultureInfo.InvariantCulture); + w.WriteAttributeString (a.Key, value); + } + } + WriteInnerHtml (w); + if (HtmlNeedsFullEndElement) { + w.WriteFullEndElement (); + } + else { + w.WriteEndElement (); + } + } + + public virtual void WriteInnerHtml (System.Xml.XmlWriter w) + { + var children = Children; + foreach (var c in children) { + c.WriteOuterHtml (w); + } + } } } diff --git a/Ooui/Node.cs b/Ooui/Node.cs index 18d16a0..374c016 100644 --- a/Ooui/Node.cs +++ b/Ooui/Node.cs @@ -180,5 +180,24 @@ namespace Ooui } return false; } + + public virtual string OuterHtml { + get { + using (var stream = new System.IO.MemoryStream ()) { + var settings = new System.Xml.XmlWriterSettings { + OmitXmlDeclaration = true, + ConformanceLevel = System.Xml.ConformanceLevel.Fragment, + CloseOutput = false, + }; + using (var w = System.Xml.XmlWriter.Create (stream, settings)) { + WriteOuterHtml (w); + } + stream.Position = 0; + return new System.IO.StreamReader (stream).ReadToEnd (); + } + } + } + + public abstract void WriteOuterHtml (System.Xml.XmlWriter w); } } diff --git a/Ooui/Style.cs b/Ooui/Style.cs index 6a6f307..71f3177 100644 --- a/Ooui/Style.cs +++ b/Ooui/Style.cs @@ -399,7 +399,7 @@ namespace Ooui o.Append (head); o.Append (p.Key); o.Append (":"); - o.Append (String.Format (System.Globalization.CultureInfo.InvariantCulture, "{0}", p.Value)); + o.Append (Convert.ToString (p.Value, System.Globalization.CultureInfo.InvariantCulture)); head = ";"; } } diff --git a/Ooui/TextArea.cs b/Ooui/TextArea.cs index b7cc341..f3f2d5f 100644 --- a/Ooui/TextArea.cs +++ b/Ooui/TextArea.cs @@ -31,6 +31,8 @@ namespace Ooui set => SetAttributeProperty ("cols", value); } + protected override bool HtmlNeedsFullEndElement => true; + public TextArea () : base ("textarea") { @@ -52,5 +54,10 @@ namespace Ooui } return base.TriggerEventFromMessage (message); } + + public override void WriteInnerHtml (System.Xml.XmlWriter w) + { + w.WriteString (val ?? ""); + } } } diff --git a/Ooui/TextNode.cs b/Ooui/TextNode.cs index 47441c6..8fccf8c 100644 --- a/Ooui/TextNode.cs +++ b/Ooui/TextNode.cs @@ -20,5 +20,10 @@ namespace Ooui { Text = text; } + + public override void WriteOuterHtml (System.Xml.XmlWriter w) + { + w.WriteString (text); + } } } diff --git a/Ooui/UI.cs b/Ooui/UI.cs index 33c39ac..c865a3c 100644 --- a/Ooui/UI.cs +++ b/Ooui/UI.cs @@ -44,7 +44,10 @@ namespace Ooui -
+ +
+@InitialHtml +
@@ -390,9 +393,9 @@ namespace Ooui } } - public static string RenderTemplate (string webSocketPath, string title = "") + public static string RenderTemplate (string webSocketPath, string title = "", string initialHtml = "") { - return Template.Replace ("@WebSocketPath", webSocketPath).Replace ("@Styles", rules.ToString ()).Replace ("@Title", title); + return Template.Replace ("@WebSocketPath", webSocketPath).Replace ("@Styles", rules.ToString ()).Replace ("@Title", title).Replace ("@InitialHtml", initialHtml); } class DataHandler : RequestHandler diff --git a/Tests/WriteHtmlTests.cs b/Tests/WriteHtmlTests.cs new file mode 100644 index 0000000..5d95fb8 --- /dev/null +++ b/Tests/WriteHtmlTests.cs @@ -0,0 +1,95 @@ +using System; + +#if NUNIT +using NUnit.Framework; +using TestClassAttribute = NUnit.Framework.TestFixtureAttribute; +using TestMethodAttribute = NUnit.Framework.TestCaseAttribute; +#else +using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + +using Ooui; + +namespace Tests +{ + [TestClass] + public class WriteHtmlTests + { + System.Text.RegularExpressions.Regex idre = new System.Text.RegularExpressions.Regex ("\\sid=\"[^\"]*\""); + + string OuterHtmlWithoutIds (Element e) + { + return idre.Replace (e.OuterHtml, ""); + } + + [TestMethod] + public void TextAreaWithTextStyled () + { + var e = new TextArea { + Value = "Hello World!", + }; + e.Style.BackgroundColor = "#18f"; + Assert.AreEqual ("", OuterHtmlWithoutIds (e)); + } + + [TestMethod] + public void TextAreaEmptyStyled () + { + var e = new TextArea (); + e.Style.BackgroundColor = "#18f"; + Assert.AreEqual ("", OuterHtmlWithoutIds (e)); + } + + [TestMethod] + public void Style () + { + var e = new Div (); + e.Style.BackgroundColor = "#18f"; + Assert.AreEqual ("
", OuterHtmlWithoutIds (e)); + } + + [TestMethod] + public void TwoGrandChildren () + { + var e = new Div (new Div (new Anchor (), new Anchor ()), new Paragraph ()); + Assert.AreEqual ("

", OuterHtmlWithoutIds (e)); + } + + [TestMethod] + public void Child () + { + var e = new Div (new Anchor ()); + Assert.AreEqual ("
", OuterHtmlWithoutIds (e)); + } + + [TestMethod] + public void TextChild () + { + var e = new Paragraph ("Hello world!"); + Assert.AreEqual ("

Hello world!

", OuterHtmlWithoutIds (e)); + } + + [TestMethod] + public void IdIsFirst () + { + var e = new Anchor (); + Assert.IsTrue (e.OuterHtml.StartsWith ("
", OuterHtmlWithoutIds (e)); + } + + [TestMethod] + public void AnchorHRef () + { + var e = new Anchor { + HRef = "http://google.com" + }; + Assert.AreEqual ("", OuterHtmlWithoutIds (e)); + } + } +}