public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, Action <string> logError) { htmlAttributes = null; var target = string.Empty; var pivot = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "target": if (value != "docs" && value != "chromeless" && value != "pdf") { logError($"Unexpected target \"{value}\". Permitted targets are \"docs\", \"chromeless\" or \"pdf\"."); return(false); } target = value; break; case "pivot": if (!pivotRegex.IsMatch(value)) { logError($"Invalid pivot \"{value}\". Pivot must be a comma-delimited list of pivot names. Pivot names must be lower-case and contain only letters, numbers or dashes."); return(false); } pivot = value; break; default: logError($"Unexpected attribute \"{name}\"."); return(false); } } if (target == string.Empty && pivot == string.Empty) { logError($"Either target or privot must be specified."); return(false); } if (target == "pdf" && pivot != string.Empty) { logError($"Pivot not permitted on pdf target."); return(false); } htmlAttributes = new HtmlAttributes(); if (target != string.Empty) { htmlAttributes.AddProperty("data-zone", target); } if (pivot != string.Empty) { htmlAttributes.AddProperty("data-pivot", pivot.Trim().ReplaceRegex(pivotReplaceCommasRegex, " ")); } return(true); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, Action <string> logWarning, MarkdownObject markdownObject) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var model = string.Empty; var action = string.Empty; var submitText = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "model": model = value; break; case "action": action = value; break; case "submittext": submitText = WebUtility.HtmlEncode(value); break; default: logError($"Unexpected attribute \"{name}\"."); return(false); } } if (action == string.Empty) { logError("Form action must be specified."); return(false); } if (submitText == string.Empty) { logError("Submit text must be specified."); return(false); } htmlAttributes = new HtmlAttributes(); if (model != string.Empty) { htmlAttributes.AddProperty("data-model", model); } htmlAttributes.AddProperty("data-action", action); htmlAttributes.AddClass("chromeless-form"); renderProperties.Add(new KeyValuePair <string, string>("submitText", submitText)); return(true); }
protected override void Write(HtmlRenderer renderer, LinkToParsedModel obj) { RequestLifetimeService requestLifetimeService = null; if (_serviceProvider.GetRequiredService <IHttpContextAccessor>().HttpContext.Items .TryGetValue("RequestLifetime", out var lifetimeService)) { requestLifetimeService = (RequestLifetimeService)lifetimeService; } if (requestLifetimeService == null) { throw ExceptionFactory.SoftException(ExceptionEnum.SomethingWentWrong, $"If you see this message pls contact admin, {obj.Id}"); } var publicContext = (PublicContext)_serviceProvider.GetService(typeof(PublicContext)); var isPostOrComment = publicContext.IsPostOrComment(obj.Id); var attr = new HtmlAttributes(); attr.AddClass("link-to"); attr.AddProperty("target", "_blank"); attr.AddProperty("rel", "noopener noreferrer"); attr.AddProperty("href", $"/{requestLifetimeService.Board.Name}/{requestLifetimeService.Post.Id}#{obj.Id}"); attr.AddProperty("data-thread", requestLifetimeService.Post.Id.ToString()); var innerText = ">>" + obj.Id; var mention = new Mention { MentionId = obj.Id, MentionerId = requestLifetimeService.MyId, IsComment = requestLifetimeService.IsComment, }; requestLifetimeService.AddMention(mention); if (isPostOrComment == 0) { attr.AddProperty("data-post", obj.Id.ToString()); innerText += " (OP)"; } else if (isPostOrComment == 1) { attr.AddProperty("data-comment", obj.Id.ToString()); } else { attr.AddProperty("data-post", obj.Id.ToString()); attr.AddProperty("data-comment", obj.Id.ToString()); } renderer.Write("<a").WriteAttributes(attr).Write($">{innerText}</a>"); }
public void TestAddProperty() { var attributes = new HtmlAttributes(); attributes.AddProperty("key1", "1"); Assert.NotNull(attributes.Properties); Assert.AreEqual(new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("key1", "1") }, attributes.Properties); attributes.AddPropertyIfNotExist("key1", "1"); Assert.NotNull(attributes.Properties); Assert.AreEqual(new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("key1", "1") }, attributes.Properties); attributes.AddPropertyIfNotExist("key2", "2"); Assert.AreEqual(new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("key1", "1"), new KeyValuePair <string, string>("key2", "2") }, attributes.Properties); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, BlockProcessor processor) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var src = string.Empty; var alt = string.Empty; var type = string.Empty; var loc_scope = string.Empty; var border = true; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "alt-text": alt = value; break; case "type": type = value; break; case "loc-scope": loc_scope = value; break; case "source": src = value; break; case "border": if (!bool.TryParse(value, out border)) { border = true; } break; case "lightbox": break; default: logError($"Image reference '{src}' is invalid per the schema. Unexpected attribute: '{name}'."); return(false); } } if (string.IsNullOrEmpty(type)) { type = "content"; } if (string.IsNullOrEmpty(src)) { logError("source is a required attribute. Please ensure you have specified a source attribute."); } if (string.IsNullOrEmpty(alt) && type != "icon") { logError("alt-text is a required attribute. Please ensure you have specified an alt-text attribute."); } if ((string.IsNullOrEmpty(alt) && type != "icon") || string.IsNullOrEmpty(src)) { return(false); } htmlAttributes = new HtmlAttributes(); htmlAttributes.AddProperty("src", src); if (type == "icon") { htmlAttributes.AddProperty("role", "presentation"); } else { htmlAttributes.AddProperty("alt", alt); } var id = GetHtmlId(processor.LineIndex, processor.Column); if (type == "complex") { htmlAttributes.AddProperty("aria-describedby", id); } RenderDelegate = (renderer, obj) => { var currentLightbox = string.Empty; var currentBorderStr = string.Empty; var currentBorder = true; obj.Attributes.TryGetValue("lightbox", out currentLightbox); //it's okay if this is null obj.Attributes.TryGetValue("border", out currentBorderStr); //it's okay if this is null if (!bool.TryParse(currentBorderStr, out currentBorder)) { currentBorder = true; } if (!string.IsNullOrEmpty(currentLightbox)) { var lighboxHtmlAttributes = new HtmlAttributes(); lighboxHtmlAttributes.AddProperty("href", $"{currentLightbox}#lightbox"); lighboxHtmlAttributes.AddProperty("data-linktype", $"relative-path"); renderer.Write("<a").WriteAttributes(lighboxHtmlAttributes).WriteLine(">"); } if (currentBorder) { renderer.WriteLine("<div class=\"mx-imgBorder\"><p>"); } //if obj.Count == 0, this signifies that there is no long description for the image. if (obj.Count == 0) { renderer.Write("<img").WriteAttributes(obj).WriteLine(">"); } else { if (type == "complex" && obj.Count == 0) { logError("If type is \"complex\", then descriptive content is required. Please make sure you have descriptive content."); return(false); } var htmlId = GetHtmlId(obj.Line, obj.Column); renderer.Write("<img").WriteAttributes(obj).WriteLine(">"); renderer.WriteLine($"<div id=\"{htmlId}\" class=\"visually-hidden\">"); renderer.WriteChildren(obj); renderer.WriteLine("</div>"); } if (currentBorder) { renderer.WriteLine("</p></div>"); } if (!string.IsNullOrEmpty(currentLightbox)) { renderer.WriteLine($"</a>"); } return(true); }; return(true); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, Action <string> logWarning, TripleColonBlock block) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var src = string.Empty; var title = string.Empty; var maxWidth = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "title": title = value; break; case "max-width": maxWidth = value; break; case "source": src = value; break; default: logError($"Video reference '{src}' is invalid per the schema. Unexpected attribute: '{name}'."); return(false); } } if (string.IsNullOrEmpty(src)) { logError("source is a required attribute. Please ensure you have specified a source attribute."); return(false); } if (!src.Contains("channel9.msdn.com") && !src.Contains("youtube.com/embed") && !src.Contains("microsoft.com/en-us/videoplayer/embed")) { logWarning($"Video source, '{src}', should be from https://channel9.msdn.com, https://www.youtube.com/embed, or https://www.microsoft.com/en-us/videoplayer/embed"); } if (src.Contains("channel9.msdn.com") && !src.Contains("/player")) { logWarning($"Your source from channel9.msdn.com does not end in '/player'. Please make sure you are correctly linking to the Channel 9 video player. "); } htmlAttributes = new HtmlAttributes(); htmlAttributes.AddProperty("src", QuoteSectionNoteRender.FixUpLink(src)); htmlAttributes.AddProperty("allowFullScreen", "true"); htmlAttributes.AddProperty("frameBorder", "0"); if (!string.IsNullOrEmpty(title)) { htmlAttributes.AddProperty("title", title); } if (!string.IsNullOrEmpty(maxWidth)) { int number; if (!int.TryParse(maxWidth, out number)) { logError($"Video reference '{src}' is invalid. 'max-width' must be a number."); return(false); } htmlAttributes.AddProperty("style", $"max-width:{maxWidth}px;"); } RenderDelegate = (renderer, obj) => { renderer.WriteLine("<div class=\"embeddedvideo\">"); renderer.Write($"<iframe").WriteAttributes(obj).WriteLine(">"); renderer.WriteLine("</div>"); return(true); }; return(true); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, BlockProcessor processor) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var source = string.Empty; var range = string.Empty; var id = string.Empty; var highlight = string.Empty; var language = string.Empty; var interactive = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "source": source = value; break; case "range": range = value; break; case "id": id = value; break; case "highlight": highlight = value; break; case "language": language = value; break; case "interactive": interactive = value; break; default: logError($"Unexpected attribute \"{name}\"."); return(false); } } if (string.IsNullOrEmpty(source)) { logError("source is a required attribute. Please ensure you have specified a source attribute"); return(false); } if (string.IsNullOrEmpty(language)) { language = InferLanguageFromFile(source, logError); } htmlAttributes = new HtmlAttributes(); htmlAttributes.AddProperty("class", $"lang-{language}"); if (!string.IsNullOrEmpty(interactive)) { htmlAttributes.AddProperty("data-interactive", language); htmlAttributes.AddProperty("data-interactive-mode", interactive); } if (!string.IsNullOrEmpty(highlight)) { htmlAttributes.AddProperty("highlight-lines", highlight); } RenderDelegate = (renderer, obj) => { var currentId = string.Empty; var currentRange = string.Empty; var currentSource = string.Empty; obj.Attributes.TryGetValue("id", out currentId); //it's okay if this is null obj.Attributes.TryGetValue("range", out currentRange); //it's okay if this is null obj.Attributes.TryGetValue("source", out currentSource); //source has already been checked above var(code, codePath) = _context.ReadFile(currentSource, InclusionContext.File, obj); if (string.IsNullOrEmpty(code)) { logError($"The code snippet \"{currentSource}\" could not be found."); return(false); } var updatedCode = GetCodeSnippet(currentRange, currentId, code, logError).TrimEnd(); if (updatedCode == string.Empty) { return(false); } renderer.WriteLine("<pre>"); renderer.Write("<code").WriteAttributes(obj).Write(">"); renderer.WriteLine(updatedCode); renderer.WriteLine("</code></pre>"); return(true); }; return(true); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, BlockProcessor processor) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var src = string.Empty; var alt = string.Empty; var type = string.Empty; var loc_scope = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "alt-text": alt = value; break; case "type": type = value; break; case "loc-scope": loc_scope = value; break; case "source": src = value; break; default: logError($"Unexpected attribute \"{name}\"."); return(false); } } if (string.IsNullOrEmpty(type)) { type = "content"; } if (string.IsNullOrEmpty(src)) { logError($"source is a required attribute. Please ensure you have specified a source attribute."); } if (string.IsNullOrEmpty(alt) && type != "icon") { logError($"alt-text is a required attribute. Please ensure you have specified an alt-text attribute."); } if ((string.IsNullOrEmpty(alt) && type != "icon") || string.IsNullOrEmpty(src)) { return(false); } htmlAttributes = new HtmlAttributes(); htmlAttributes.AddProperty("src", src); if (type == "icon") { htmlAttributes.AddProperty("role", "presentation"); } else { htmlAttributes.AddProperty("alt", alt); } var id = GetHtmlId(processor.LineIndex, processor.Column); if (type == "complex") { htmlAttributes.AddProperty("aria-describedby", id); } RenderDelegate = (renderer, obj) => { //if obj.Count == 0, this signifies that there is no long description for the image. if (obj.Count == 0) { renderer.Write("<img").WriteAttributes(obj).WriteLine(">"); } else { if (type == "complex" && obj.Count == 0) { logError($"If type is \"complex\", then descriptive content is required. Please make sure you have descriptive content."); return(false); } var htmlId = GetHtmlId(obj.Line, obj.Column); renderer.Write("<img").WriteAttributes(obj).WriteLine(">"); renderer.WriteLine($"<div id=\"{htmlId}\" class=\"visually-hidden\">"); renderer.WriteChildren(obj); renderer.WriteLine("</div>"); } return(true); }; return(true); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, BlockProcessor processor) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var model = string.Empty; var action = string.Empty; var submitText = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "model": model = value; break; case "action": action = value; break; case "submittext": submitText = WebUtility.HtmlEncode(value); break; default: logError($"Unexpected attribute \"{name}\"."); return(false); } } if (action == string.Empty) { logError("Form action must be specified."); return(false); } if (submitText == string.Empty) { logError("Submit text must be specified."); return(false); } htmlAttributes = new HtmlAttributes(); if (model != string.Empty) { htmlAttributes.AddProperty("data-model", model); } htmlAttributes.AddProperty("data-action", action); htmlAttributes.AddClass("chromeless-form"); renderProperties.Add(new KeyValuePair <string, string>("submitText", submitText)); RenderDelegate = (renderer, obj) => { var buttonText = "Submit"; obj.RenderProperties.TryGetValue("submitText", out buttonText); renderer.Write("<form").WriteAttributes(obj).WriteLine(">"); renderer.WriteLine("<div></div>"); renderer.WriteLine($"<button class=\"button is-primary\" disabled=\"disabled\" type=\"submit\">{buttonText}</button>"); renderer.WriteLine("</form>"); return(true); }; return(true); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, Action <string> logWarning, MarkdownObject markdownObject) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var src = string.Empty; var alt = string.Empty; var type = string.Empty; var loc_scope = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "alt-text": alt = value; break; case "type": type = value; break; case "loc-scope": loc_scope = value; break; case "source": src = value; break; case "border": break; case "lightbox": break; case "link": break; default: logError($"Image reference '{src}' is invalid per the schema. Unexpected attribute: '{name}'."); return(false); } } if (string.IsNullOrEmpty(type)) { type = "content"; } if (string.IsNullOrEmpty(src)) { logError("source is a required attribute. Please ensure you have specified a source attribute."); } if (string.IsNullOrEmpty(alt) && type != "icon") { logError("alt-text is a required attribute. Please ensure you have specified an alt-text attribute."); } // add loc scope missing/invalid validation here if ((string.IsNullOrEmpty(alt) && type != "icon") || string.IsNullOrEmpty(src)) { return(false); } htmlAttributes = new HtmlAttributes(); // alt is allowed to be empty for icon type image if (string.IsNullOrEmpty(alt) && type == "icon") { htmlAttributes.AddProperty("src", _context.GetLink(src, markdownObject)); } else { htmlAttributes.AddProperty("src", _context.GetImageLink(src, markdownObject, alt)); } if (type == "icon") { htmlAttributes.AddProperty("role", "presentation"); } else { htmlAttributes.AddProperty("alt", alt); } var id = GetHtmlId(markdownObject); if (type == "complex") { htmlAttributes.AddProperty("aria-describedby", id); } return(true); }
public bool Render(HtmlRenderer renderer, MarkdownObject obj, Action <string> logWarning) { var tripleColonObj = (ITripleColon)obj; if (!tripleColonObj.Attributes.TryGetValue("type", out var currentType)) { currentType = "content"; } tripleColonObj.Attributes.TryGetValue("lightbox", out var currentLightbox); //it's okay if this is null tripleColonObj.Attributes.TryGetValue("border", out var currentBorderStr); //it's okay if this is null tripleColonObj.Attributes.TryGetValue("link", out var currentLink); //it's okay if this is null if (!bool.TryParse(currentBorderStr, out var currentBorder)) { if (currentType == "icon") { currentBorder = false; } else { currentBorder = true; } } if (currentBorder) { if (tripleColonObj is Block) { renderer.WriteLine("<p class=\"mx-imgBorder\">"); } else { renderer.WriteLine("<span class=\"mx-imgBorder\">"); } } else { if (tripleColonObj is Block) { renderer.WriteLine("<p>"); } } if (!string.IsNullOrEmpty(currentLink)) { var linkHtmlAttributes = new HtmlAttributes(); currentLink = _context.GetLink(currentLink, obj); linkHtmlAttributes.AddProperty("href", $"{currentLink}"); renderer.Write("<a").WriteAttributes(linkHtmlAttributes).WriteLine(">"); } else if (!string.IsNullOrEmpty(currentLightbox)) { var lightboxHtmlAttributes = new HtmlAttributes(); var path = _context.GetLink(currentLightbox, obj); lightboxHtmlAttributes.AddProperty("href", $"{path}#lightbox"); lightboxHtmlAttributes.AddProperty("data-linktype", $"relative-path"); renderer.Write("<a").WriteAttributes(lightboxHtmlAttributes).WriteLine(">"); } if (currentType != "complex") { renderer.Write("<img").WriteAttributes(obj).WriteLine(">"); if (tripleColonObj is ContainerBlock && (tripleColonObj as ContainerBlock).LastChild != null) { var inline = ((tripleColonObj as ContainerBlock).LastChild as ParagraphBlock).Inline; renderer.WriteChildren(inline); } } else { if (currentType == "complex" && tripleColonObj.Count == 0) { logWarning("If type is \"complex\", then descriptive content is required. Please make sure you have descriptive content."); return(false); } var htmlId = GetHtmlId(obj); renderer.Write("<img").WriteAttributes(obj).WriteLine(">"); renderer.WriteLine($"<div id=\"{htmlId}\" class=\"visually-hidden\">"); renderer.WriteChildren(tripleColonObj as ContainerBlock); renderer.WriteLine("</div>"); } if (!string.IsNullOrEmpty(currentLightbox) || !string.IsNullOrEmpty(currentLink)) { renderer.WriteLine($"</a>"); } if (tripleColonObj is Block) { renderer.WriteLine("</p>"); } else { if (currentBorder) { renderer.WriteLine("</span>"); } renderer.WriteChildren(tripleColonObj as ContainerInline); } return(true); }
public void TestCopyTo() { var from = new HtmlAttributes(); from.AddClass("test"); from.AddProperty("key1", "1"); var to = new HtmlAttributes(); from.CopyTo(to); Assert.True(ReferenceEquals(from.Classes, to.Classes)); Assert.True(ReferenceEquals(from.Properties, to.Properties)); // From: Classes From: Properties To: Classes To: Properties // test1: null null null null from = new HtmlAttributes(); to = new HtmlAttributes(); from.CopyTo(to, false, false); Assert.Null(to.Classes); Assert.Null(to.Properties); // test2: ["test"] ["key1", "1"] null null from = new HtmlAttributes(); to = new HtmlAttributes(); from.AddClass("test"); from.AddProperty("key1", "1"); from.CopyTo(to, false, false); Assert.AreEqual(new List <string>() { "test" }, to.Classes); Assert.AreEqual(new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("key1", "1") }, to.Properties); // test3: null null ["test"] ["key1", "1"] from = new HtmlAttributes(); to = new HtmlAttributes(); to.AddClass("test"); to.AddProperty("key1", "1"); from.CopyTo(to, false, false); Assert.AreEqual(new List <string>() { "test" }, to.Classes); Assert.AreEqual(new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("key1", "1") }, to.Properties); // test4: ["test1"] ["key2", "2"] ["test"] ["key1", "1"] from = new HtmlAttributes(); to = new HtmlAttributes(); from.AddClass("test1"); from.AddProperty("key2", "2"); to.AddClass("test"); to.AddProperty("key1", "1"); from.CopyTo(to, false, false); Assert.AreEqual(new List <string>() { "test", "test1" }, to.Classes); Assert.AreEqual(new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("key1", "1"), new KeyValuePair <string, string>("key2", "2") }, to.Properties); }
public bool TryProcessAttributes(IDictionary <string, string> attributes, out HtmlAttributes htmlAttributes, out IDictionary <string, string> renderProperties, Action <string> logError, Action <string> logWarning, MarkdownObject markdownObject) { htmlAttributes = null; renderProperties = new Dictionary <string, string>(); var source = string.Empty; var range = string.Empty; var id = string.Empty; var highlight = string.Empty; var language = string.Empty; var interactive = string.Empty; foreach (var attribute in attributes) { var name = attribute.Key; var value = attribute.Value; switch (name) { case "source": source = value; break; case "range": range = value; break; case "id": id = value; break; case "highlight": highlight = value; break; case "language": language = value; break; case "interactive": interactive = value; break; default: logError($"Unexpected attribute \"{name}\"."); return(false); } } if (string.IsNullOrEmpty(source)) { logError("source is a required attribute. Please ensure you have specified a source attribute"); return(false); } if (string.IsNullOrEmpty(language)) { language = InferLanguageFromFile(source, logError); } htmlAttributes = new HtmlAttributes(); htmlAttributes.AddProperty("class", $"lang-{language}"); if (!string.IsNullOrEmpty(interactive)) { htmlAttributes.AddProperty("data-interactive", language); htmlAttributes.AddProperty("data-interactive-mode", interactive); } if (!string.IsNullOrEmpty(highlight)) { htmlAttributes.AddProperty("highlight-lines", highlight); } return(true); }