Пример #1
0
        public override BlockState TryOpen(BlockProcessor processor)
        {
            var slice          = processor.Line;
            var column         = processor.Column;
            var sourcePosition = processor.Start;

            if (processor.IsCodeIndent ||
                !ExtensionsHelper.MatchStart(ref slice, ":::"))
            {
                return(BlockState.None);
            }

            ExtensionsHelper.SkipSpaces(ref slice);

            var extensionName = "triple-colon";
            ITripleColonExtensionInfo    extension;
            IDictionary <string, string> attributes;
            HtmlAttributes htmlAttributes;
            IDictionary <string, string> renderProperties;
            Action <string> logError = (string message) => _context.LogError(
                $"invalid-{extensionName}",
                $"Invalid {extensionName} on line {processor.LineIndex}. \"{slice.Text}\" is invalid. {message}",
                null,
                line: processor.LineIndex);

            if (!TryMatchIdentifier(ref slice, out extensionName) ||
                !_extensions.TryGetValue(extensionName, out extension) ||
                !extension.TryValidateAncestry(processor.CurrentContainer, logError) ||
                !TryMatchAttributes(ref slice, out attributes, extensionName, extension.SelfClosing, logError) ||
                !extension.TryProcessAttributes(attributes, out htmlAttributes, out renderProperties, logError))
            {
                return(BlockState.None);
            }

            var block = new TripleColonBlock(this)
            {
                Closed           = false,
                Column           = column,
                Line             = processor.LineIndex,
                Span             = new SourceSpan(sourcePosition, slice.End),
                Extension        = extension,
                RenderProperties = renderProperties
            };

            if (htmlAttributes != null)
            {
                block.SetData(typeof(HtmlAttributes), htmlAttributes);
            }

            processor.NewBlocks.Push(block);

            if (extension.SelfClosing)
            {
                return(BlockState.BreakDiscard);
            }

            return(BlockState.ContinueDiscard);
        }
Пример #2
0
        public override BlockState TryOpen(BlockProcessor processor)
        {
            var slice          = processor.Line;
            var sourcePosition = processor.Start;

            if (processor.IsCodeIndent ||
                !ExtensionsHelper.MatchStart(ref slice, ":::"))
            {
                return(BlockState.None);
            }

            ExtensionsHelper.SkipSpaces(ref slice);

            var             extensionName = "triple-colon";
            Action <string> logError      = (string message) => _context.LogError(
                $"invalid-{extensionName}",
                $"{message}",
                null,
                line: processor.LineIndex);
            Action <string> logWarning = (string message) => _context.LogWarning(
                $"invalid-{extensionName}",
                $"{message}",
                null,
                line: processor.LineIndex);

            var block = new TripleColonBlock(this)
            {
                Closed = false,
                Column = processor.Column,
                Line   = processor.LineIndex,
                Span   = new SourceSpan(sourcePosition, slice.End),
            };

            if (!TryMatchIdentifier(ref slice, out extensionName) ||
                !_extensions.TryGetValue(extensionName, out var extension) ||
                !extension.TryValidateAncestry(processor.CurrentContainer, logError) ||
                !TryMatchAttributes(ref slice, out var attributes, extensionName, extension.SelfClosing, logError) ||
                !extension.TryProcessAttributes(attributes, out var htmlAttributes, out var renderProperties, logError, logWarning, block))
            {
                return(BlockState.None);
            }

            block.Extension        = extension;
            block.Attributes       = attributes;
            block.RenderProperties = renderProperties;

            if (htmlAttributes != null)
            {
                block.SetData(typeof(HtmlAttributes), htmlAttributes);
            }

            processor.NewBlocks.Push(block);

            if (extension.GetType() == typeof(ImageExtension) &&
                htmlAttributes != null &&
                ImageExtension.RequiresClosingTripleColon(attributes))
            {
                block.EndingTripleColons = true;
                return(BlockState.ContinueDiscard);
            }

            if (extension.SelfClosing)
            {
                return(BlockState.BreakDiscard);
            }

            return(BlockState.ContinueDiscard);
        }
Пример #3
0
 public bool Render(HtmlRenderer renderer, TripleColonBlock block)
 {
     return(RenderDelegate != null
         ? RenderDelegate(renderer, block)
         : false);
 }
Пример #4
0
        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 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.");
            }
            if ((string.IsNullOrEmpty(alt) && type != "icon") || string.IsNullOrEmpty(src))
            {
                return(false);
            }
            htmlAttributes = new HtmlAttributes();
            htmlAttributes.AddProperty("src", _context.GetLink(src, InclusionContext.File, InclusionContext.RootFile, block));

            if (type == "icon")
            {
                htmlAttributes.AddProperty("role", "presentation");
            }
            else
            {
                htmlAttributes.AddProperty("alt", alt);
            }
            var id = GetHtmlId(block.Line, block.Column);

            if (type == "complex")
            {
                htmlAttributes.AddProperty("aria-describedby", id);
            }

            RenderDelegate = (renderer, obj) =>
            {
                var currentType      = string.Empty;
                var currentLightbox  = string.Empty;
                var currentBorderStr = string.Empty;
                var currentBorder    = true;
                var currentLink      = string.Empty;
                if (!obj.Attributes.TryGetValue("type", out currentType))
                {
                    currentType = "content";
                }
                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
                obj.Attributes.TryGetValue("link", out currentLink);         //it's okay if this is null
                if (!bool.TryParse(currentBorderStr, out currentBorder))
                {
                    if (currentType == "icon")
                    {
                        currentBorder = false;
                    }
                    else
                    {
                        currentBorder = true;
                    }
                }

                if (!string.IsNullOrEmpty(currentLink))
                {
                    var linkHtmlAttributes = new HtmlAttributes();
                    currentLink = _context.GetLink(currentLink, InclusionContext.File, InclusionContext.RootFile, obj);
                    linkHtmlAttributes.AddProperty("href", $"{currentLink}");
                    renderer.Write("<a").WriteAttributes(linkHtmlAttributes).WriteLine(">");
                }
                else if (!string.IsNullOrEmpty(currentLightbox))
                {
                    var lighboxHtmlAttributes = new HtmlAttributes();
                    var path = _context.GetLink(currentLightbox, InclusionContext.File, InclusionContext.RootFile, obj);
                    lighboxHtmlAttributes.AddProperty("href", $"{path}#lightbox");
                    lighboxHtmlAttributes.AddProperty("data-linktype", $"relative-path");
                    renderer.Write("<a").WriteAttributes(lighboxHtmlAttributes).WriteLine(">");
                }
                if (currentBorder)
                {
                    renderer.WriteLine("<div class=\"mx-imgBorder\"><p>");
                }
                if (currentType != "complex")
                {
                    renderer.Write("<img").WriteAttributes(obj).WriteLine(">");
                }
                else
                {
                    if (currentType == "complex" && obj.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.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) || !string.IsNullOrEmpty(currentLink))
                {
                    renderer.WriteLine($"</a>");
                }

                return(true);
            };

            return(true);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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 = 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();
            htmlAttributes.AddClass("zone");
            if (target != string.Empty)
            {
                htmlAttributes.AddClass("has-target");
                htmlAttributes.AddProperty("data-target", target);
            }
            if (pivot != string.Empty)
            {
                htmlAttributes.AddClass("has-pivot");
                htmlAttributes.AddProperty("data-pivot", pivot.Trim().ReplaceRegex(pivotReplaceCommasRegex, " "));
            }
            return(true);
        }
Пример #7
0
 public bool Render(HtmlRenderer renderer, TripleColonBlock block)
 {
     return(false);
 }
Пример #8
0
        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 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))
                {
                    logWarning($"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);
        }
Пример #9
0
        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 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);
        }