From dbc9a8e0fe85fa6b471c77de562c129dcc4cafc5 Mon Sep 17 00:00:00 2001 From: Brett Walker <bwalker@gitlab.com> Date: Mon, 4 Mar 2024 14:01:20 -0600 Subject: [PATCH] Enable header ids to be generated in markdown Changed: changed --- .../native_header_anchors.yml | 9 + .../ee/banzai/filter/sanitization_filter.rb | 22 +- .../helpers/vulnerabilities_helper_spec.rb | 4 +- .../graphql/vulnerabilities/fields_spec.rb | 17 +- .../output_example_snapshots/html.yml | 160 ++++---- .../snapshot_spec.html | 355 ++++++------------ glfm_specification/output_spec/spec.html | 10 +- .../filter/markdown_engines/glfm_markdown.rb | 12 +- lib/banzai/filter/sanitization_filter.rb | 37 +- lib/banzai/filter/table_of_contents_filter.rb | 6 + .../filter/table_of_contents_tag_filter.rb | 84 +++++ lib/banzai/pipeline/description_pipeline.rb | 4 +- .../markdown_engines/glfm_markdown_spec.rb | 34 +- .../banzai/filter/sanitization_filter_spec.rb | 30 ++ .../filter/table_of_contents_filter_spec.rb | 42 ++- .../table_of_contents_tag_filter_spec.rb | 82 ++++ 16 files changed, 522 insertions(+), 386 deletions(-) create mode 100644 config/feature_flags/gitlab_com_derisk/native_header_anchors.yml diff --git a/config/feature_flags/gitlab_com_derisk/native_header_anchors.yml b/config/feature_flags/gitlab_com_derisk/native_header_anchors.yml new file mode 100644 index 0000000000000..24f36fcd99f57 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/native_header_anchors.yml @@ -0,0 +1,9 @@ +--- +name: native_header_anchors +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/440733 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144690 +rollout_issue_url: +milestone: '17.0' +group: group::project management +type: gitlab_com_derisk +default_enabled: false diff --git a/ee/lib/ee/banzai/filter/sanitization_filter.rb b/ee/lib/ee/banzai/filter/sanitization_filter.rb index 2a32a4d501896..632f701582809 100644 --- a/ee/lib/ee/banzai/filter/sanitization_filter.rb +++ b/ee/lib/ee/banzai/filter/sanitization_filter.rb @@ -7,27 +7,11 @@ module SanitizationFilter extend ::Gitlab::Utils::Override extend ActiveSupport::Concern - override :customize_allowlist - def customize_allowlist(allowlist) - # Remove any `class` property not required for a - allowlist[:attributes]['a'].push('class') - allowlist[:transformers].push(self.class.remove_unsafe_a_class) - - super(allowlist) - end - class_methods do - def remove_unsafe_a_class - lambda do |env| - node = env[:node] - - return unless node.name == 'a' - return unless node.has_attribute?('class') - - return if node['class'] == ::Banzai::Filter::JiraPrivateImageLinkFilter::CSS_WITH_ATTACHMENT_ICON + def remove_link_class?(node) + return if node['class'] == ::Banzai::Filter::JiraPrivateImageLinkFilter::CSS_WITH_ATTACHMENT_ICON - node.remove_attribute('class') - end + super(node) end end end diff --git a/ee/spec/helpers/vulnerabilities_helper_spec.rb b/ee/spec/helpers/vulnerabilities_helper_spec.rb index 6044f920a9821..3ef6043107490 100644 --- a/ee/spec/helpers/vulnerabilities_helper_spec.rb +++ b/ee/spec/helpers/vulnerabilities_helper_spec.rb @@ -420,7 +420,7 @@ end it 'returns finding information' do - rendered_markdown = '<h1 data-sourcepos="1:1-1:9" dir="auto">
<a id="user-content-finding" class="anchor" href="#finding" aria-hidden="true"></a>Finding</h1>' + rendered_markdown = '<h1 data-sourcepos="1:1-1:9" dir="auto">
<a href="#finding" aria-hidden="true" class="anchor" id="user-content-finding"></a>Finding</h1>' expect(subject[:description_html]).to eq(rendered_markdown) end @@ -433,7 +433,7 @@ end it 'returns finding information' do - rendered_markdown = '<h1 data-sourcepos="1:1-1:15" dir="auto">
<a id="user-content-vulnerability" class="anchor" href="#vulnerability" aria-hidden="true"></a>Vulnerability</h1>' + rendered_markdown = '<h1 data-sourcepos="1:1-1:15" dir="auto">
<a href="#vulnerability" aria-hidden="true" class="anchor" id="user-content-vulnerability"></a>Vulnerability</h1>' expect(subject[:description_html]).to eq(rendered_markdown) end diff --git a/ee/spec/requests/api/graphql/vulnerabilities/fields_spec.rb b/ee/spec/requests/api/graphql/vulnerabilities/fields_spec.rb index d7ee48810651b..d4be9ecbe7827 100644 --- a/ee/spec/requests/api/graphql/vulnerabilities/fields_spec.rb +++ b/ee/spec/requests/api/graphql/vulnerabilities/fields_spec.rb @@ -70,12 +70,12 @@ let(:finding_description) { '# Finding' } it 'returns finding information' do - rendered_markdown = '<h1 data-sourcepos="1:1-1:9" dir="auto">
' \ - '<a id="user-content-finding" class="anchor" href="#finding" aria-hidden="true">' \ - '</a>Finding</h1>' + html = '<h1 data-sourcepos="1:1-1:9" dir="auto">
' \ + '<a href="#finding" aria-hidden="true" class="anchor" id="user-content-finding"></a>' \ + 'Finding</h1>' expect(subject.first['description']).to eq('# Finding') - expect(subject.first['descriptionHtml']).to eq(rendered_markdown) + expect(subject.first['descriptionHtml']).to eq(html) end end @@ -83,12 +83,13 @@ let(:vulnerability_description) { '# Vulnerability' } let(:finding_description) { '# Finding' } - it 'returns finding information' do - rendered_markdown = '<h1 data-sourcepos="1:1-1:15" dir="auto">
<a id="user-content-vulnerability" ' \ - 'class="anchor" href="#vulnerability" aria-hidden="true"></a>Vulnerability</h1>' + it 'returns vulnerability information' do + html = '<h1 data-sourcepos="1:1-1:15" dir="auto">
' \ + '<a href="#vulnerability" aria-hidden="true" class="anchor" id="user-content-vulnerability"></a>' \ + 'Vulnerability</h1>' expect(subject.first['description']).to eq('# Vulnerability') - expect(subject.first['descriptionHtml']).to eq(rendered_markdown) + expect(subject.first['descriptionHtml']).to eq(html) end end end diff --git a/glfm_specification/output_example_snapshots/html.yml b/glfm_specification/output_example_snapshots/html.yml index 4c5579bcc0177..de2d074261042 100644 --- a/glfm_specification/output_example_snapshots/html.yml +++ b/glfm_specification/output_example_snapshots/html.yml @@ -136,7 +136,7 @@ <h1>Foo</h1> static: |- <h1 data-sourcepos="1:1-1:5" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h1> wysiwyg: |- <h1 dir="auto">Foo</h1> 02_01_00__preliminaries__tabs__011: @@ -328,7 +328,7 @@ <p>bar</p> static: |- <h2 data-sourcepos="1:1-3:3" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h2> <p data-sourcepos="3:1-3:3" dir="auto">bar</p> wysiwyg: |- <h2 dir="auto">Foo</h2> @@ -381,17 +381,17 @@ <h6>foo</h6> static: |- <h1 data-sourcepos="1:1-1:5" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h1> <h2 data-sourcepos="2:1-2:6" dir="auto"> - <a id="user-content-foo-1" class="anchor" href="#foo-1" aria-hidden="true"></a>foo</h2> + <a href="#foo-1" aria-hidden="true" class="anchor" id="user-content-foo-1"></a>foo</h2> <h3 data-sourcepos="3:1-3:7" dir="auto"> - <a id="user-content-foo-2" class="anchor" href="#foo-2" aria-hidden="true"></a>foo</h3> + <a href="#foo-2" aria-hidden="true" class="anchor" id="user-content-foo-2"></a>foo</h3> <h4 data-sourcepos="4:1-4:8" dir="auto"> - <a id="user-content-foo-3" class="anchor" href="#foo-3" aria-hidden="true"></a>foo</h4> + <a href="#foo-3" aria-hidden="true" class="anchor" id="user-content-foo-3"></a>foo</h4> <h5 data-sourcepos="5:1-5:9" dir="auto"> - <a id="user-content-foo-4" class="anchor" href="#foo-4" aria-hidden="true"></a>foo</h5> + <a href="#foo-4" aria-hidden="true" class="anchor" id="user-content-foo-4"></a>foo</h5> <h6 data-sourcepos="6:1-6:10" dir="auto"> - <a id="user-content-foo-5" class="anchor" href="#foo-5" aria-hidden="true"></a>foo</h6> + <a href="#foo-5" aria-hidden="true" class="anchor" id="user-content-foo-5"></a>foo</h6> wysiwyg: |- <h1 dir="auto">foo</h1> <h2 dir="auto">foo</h2> @@ -428,7 +428,7 @@ <h1>foo <em>bar</em> *baz*</h1> static: |- <h1 data-sourcepos="1:1-1:19" dir="auto"> - <a id="user-content-foo-bar-baz" class="anchor" href="#foo-bar-baz" aria-hidden="true"></a>foo <em data-sourcepos="1:7-1:11">bar</em> *baz*</h1> + <a href="#foo-bar-baz" aria-hidden="true" class="anchor" id="user-content-foo-bar-baz"></a>foo <em data-sourcepos="1:7-1:11">bar</em> *baz*</h1> wysiwyg: |- <h1 dir="auto">foo <em>bar</em> *baz*</h1> 04_02_00__leaf_blocks__atx_headings__006: @@ -436,7 +436,7 @@ <h1>foo</h1> static: |- <h1 data-sourcepos="1:1-1:43" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h1> wysiwyg: |- <h1 dir="auto">foo</h1> 04_02_00__leaf_blocks__atx_headings__007: @@ -446,11 +446,11 @@ <h1>foo</h1> static: |- <h3 data-sourcepos="1:2-1:8" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h3> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h3> <h2 data-sourcepos="2:3-2:8" dir="auto"> - <a id="user-content-foo-1" class="anchor" href="#foo-1" aria-hidden="true"></a>foo</h2> + <a href="#foo-1" aria-hidden="true" class="anchor" id="user-content-foo-1"></a>foo</h2> <h1 data-sourcepos="3:4-3:8" dir="auto"> - <a id="user-content-foo-2" class="anchor" href="#foo-2" aria-hidden="true"></a>foo</h1> + <a href="#foo-2" aria-hidden="true" class="anchor" id="user-content-foo-2"></a>foo</h1> wysiwyg: |- <h3 dir="auto">foo</h3> <h2 dir="auto">foo</h2> @@ -482,9 +482,9 @@ <h3>bar</h3> static: |- <h2 data-sourcepos="1:1-1:9" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h2> <h3 data-sourcepos="2:3-2:18" dir="auto"> - <a id="user-content-bar" class="anchor" href="#bar" aria-hidden="true"></a>bar</h3> + <a href="#bar" aria-hidden="true" class="anchor" id="user-content-bar"></a>bar</h3> wysiwyg: |- <h2 dir="auto">foo</h2> <h3 dir="auto">bar</h3> @@ -494,9 +494,9 @@ <h5>foo</h5> static: |- <h1 data-sourcepos="1:1-1:40" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h1> <h5 data-sourcepos="2:1-2:12" dir="auto"> - <a id="user-content-foo-1" class="anchor" href="#foo-1" aria-hidden="true"></a>foo</h5> + <a href="#foo-1" aria-hidden="true" class="anchor" id="user-content-foo-1"></a>foo</h5> wysiwyg: |- <h1 dir="auto">foo</h1> <h5 dir="auto">foo</h5> @@ -505,7 +505,7 @@ <h3>foo</h3> static: |- <h3 data-sourcepos="1:1-1:16" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h3> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h3> wysiwyg: |- <h3 dir="auto">foo</h3> 04_02_00__leaf_blocks__atx_headings__013: @@ -513,7 +513,7 @@ <h3>foo ### b</h3> static: |- <h3 data-sourcepos="1:1-1:13" dir="auto"> - <a id="user-content-foo-b" class="anchor" href="#foo-b" aria-hidden="true"></a>foo ### b</h3> + <a href="#foo--b" aria-hidden="true" class="anchor" id="user-content-foo--b"></a>foo ### b</h3> wysiwyg: |- <h3 dir="auto">foo ### b</h3> 04_02_00__leaf_blocks__atx_headings__014: @@ -521,7 +521,7 @@ <h1>foo#</h1> static: |- <h1 data-sourcepos="1:1-1:6" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo#</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo#</h1> wysiwyg: |- <h1 dir="auto">foo#</h1> 04_02_00__leaf_blocks__atx_headings__015: @@ -531,11 +531,11 @@ <h1>foo #</h1> static: |- <h3 data-sourcepos="1:1-1:33" dir="auto"> - <a id="user-content-foo-" class="anchor" href="#foo-" aria-hidden="true"></a>foo <span data-escaped-char>#</span>##</h3> + <a href="#foo-c" aria-hidden="true" class="anchor" id="user-content-foo-<span data-escaped-char>c</span>"></a>foo <span data-escaped-char>#</span>##</h3> <h2 data-sourcepos="2:1-2:32" dir="auto"> - <a id="user-content-foo--1" class="anchor" href="#foo--1" aria-hidden="true"></a>foo #<span data-escaped-char>#</span>#</h2> + <a href="#foo-c-1" aria-hidden="true" class="anchor" id="user-content-foo-<span data-escaped-char>c</span>-1"></a>foo #<span data-escaped-char>#</span>#</h2> <h1 data-sourcepos="3:1-3:29" dir="auto"> - <a id="user-content-foo--2" class="anchor" href="#foo--2" aria-hidden="true"></a>foo <span data-escaped-char>#</span> + <a href="#foo-c-2" aria-hidden="true" class="anchor" id="user-content-foo-<span data-escaped-char>c</span>-2"></a>foo <span data-escaped-char>#</span> </h1> wysiwyg: |- <h3 dir="auto">foo ###</h3> @@ -549,7 +549,7 @@ static: |- <hr data-sourcepos="1:1-1:4"> <h2 data-sourcepos="2:1-2:6" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h2> <hr data-sourcepos="3:1-3:4"> wysiwyg: |- <hr> @@ -563,7 +563,7 @@ static: |- <p data-sourcepos="1:1-1:7" dir="auto">Foo bar</p> <h1 data-sourcepos="2:1-2:5" dir="auto"> - <a id="user-content-baz" class="anchor" href="#baz" aria-hidden="true"></a>baz</h1> + <a href="#baz" aria-hidden="true" class="anchor" id="user-content-baz"></a>baz</h1> <p data-sourcepos="3:1-3:7" dir="auto">Bar foo</p> wysiwyg: |- <p dir="auto">Foo bar</p> @@ -575,9 +575,9 @@ <h1></h1> <h3></h3> static: |- - <h2 data-sourcepos="1:1-1:3" dir="auto"></h2> - <h1 data-sourcepos="2:1-2:1" dir="auto"></h1> - <h3 data-sourcepos="3:1-3:7" dir="auto"></h3> + <h2 data-sourcepos="1:1-1:3" dir="auto"><a href="#" aria-hidden="true" class="anchor" id="user-content-"></a></h2> + <h1 data-sourcepos="2:1-2:1" dir="auto"><a href="#-1" aria-hidden="true" class="anchor" id="user-content--1"></a></h1> + <h3 data-sourcepos="3:1-3:7" dir="auto"><a href="#-2" aria-hidden="true" class="anchor" id="user-content--2"></a></h3> wysiwyg: |- <h2 dir="auto"></h2> <h1 dir="auto"></h1> @@ -588,10 +588,10 @@ <h2>Foo <em>bar</em></h2> static: |- <h1 data-sourcepos="1:1-3:0" dir="auto"> - <a id="user-content-foo-bar" class="anchor" href="#foo-bar" aria-hidden="true"></a>Foo <em data-sourcepos="1:5-1:9">bar</em> + <a href="#foo-bar" aria-hidden="true" class="anchor" id="user-content-foo-bar"></a>Foo <em data-sourcepos="1:5-1:9">bar</em> </h1> <h2 data-sourcepos="4:1-5:9" dir="auto"> - <a id="user-content-foo-bar-1" class="anchor" href="#foo-bar-1" aria-hidden="true"></a>Foo <em data-sourcepos="4:5-4:9">bar</em> + <a href="#foo-bar-1" aria-hidden="true" class="anchor" id="user-content-foo-bar-1"></a>Foo <em data-sourcepos="4:5-4:9">bar</em> </h2> wysiwyg: |- <h1 dir="auto">Foo <em>bar</em></h1> @@ -602,7 +602,7 @@ baz</em></h1> static: |- <h1 data-sourcepos="1:1-3:4" dir="auto"> - <a id="user-content-foo-barbaz" class="anchor" href="#foo-barbaz" aria-hidden="true"></a>Foo <em data-sourcepos="1:5-2:4">bar + <a href="#foo-bar-baz" aria-hidden="true" class="anchor" id="user-content-foo-bar-baz"></a>Foo <em data-sourcepos="1:5-2:4">bar baz</em> </h1> wysiwyg: |- @@ -614,7 +614,7 @@ baz</em></h1> static: |- <h1 data-sourcepos="1:3-3:4" dir="auto"> - <a id="user-content-foo-barbaz" class="anchor" href="#foo-barbaz" aria-hidden="true"></a>Foo <em data-sourcepos="1:7-2:6">bar + <a href="#foo-bar-baz" aria-hidden="true" class="anchor" id="user-content-foo-bar-baz"></a>Foo <em data-sourcepos="1:7-2:6">bar baz</em> </h1> wysiwyg: |- @@ -626,9 +626,9 @@ <h1>Foo</h1> static: |- <h2 data-sourcepos="1:1-3:0" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h2> <h1 data-sourcepos="4:1-5:1" dir="auto"> - <a id="user-content-foo-1" class="anchor" href="#foo-1" aria-hidden="true"></a>Foo</h1> + <a href="#foo-1" aria-hidden="true" class="anchor" id="user-content-foo-1"></a>Foo</h1> wysiwyg: |- <h2 dir="auto">Foo</h2> <h1 dir="auto">Foo</h1> @@ -639,11 +639,11 @@ <h1>Foo</h1> static: |- <h2 data-sourcepos="1:4-3:0" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h2> <h2 data-sourcepos="4:3-6:0" dir="auto"> - <a id="user-content-foo-1" class="anchor" href="#foo-1" aria-hidden="true"></a>Foo</h2> + <a href="#foo-1" aria-hidden="true" class="anchor" id="user-content-foo-1"></a>Foo</h2> <h1 data-sourcepos="7:3-8:5" dir="auto"> - <a id="user-content-foo-2" class="anchor" href="#foo-2" aria-hidden="true"></a>Foo</h1> + <a href="#foo-2" aria-hidden="true" class="anchor" id="user-content-foo-2"></a>Foo</h1> wysiwyg: |- <h2 dir="auto">Foo</h2> <h2 dir="auto">Foo</h2> @@ -676,7 +676,7 @@ <h2>Foo</h2> static: |- <h2 data-sourcepos="1:1-2:13" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h2> wysiwyg: |- <h2 dir="auto">Foo</h2> 04_03_00__leaf_blocks__setext_headings__008: @@ -710,7 +710,7 @@ <h2>Foo</h2> static: |- <h2 data-sourcepos="1:1-2:5" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h2> wysiwyg: |- <h2 dir="auto">Foo</h2> 04_03_00__leaf_blocks__setext_headings__011: @@ -718,7 +718,7 @@ <h2>Foo\</h2> static: |- <h2 data-sourcepos="1:1-2:4" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo\</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo\</h2> wysiwyg: |- <h2 dir="auto">Foo\</h2> 04_03_00__leaf_blocks__setext_headings__012: @@ -729,10 +729,10 @@ <p>of dashes"/></p> static: |- <h2 data-sourcepos="1:1-3:1" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>`Foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>`Foo</h2> <p data-sourcepos="3:1-3:1" dir="auto">`</p> <h2 data-sourcepos="5:1-7:12" dir="auto"> - <a id="user-content-a-titlea-lot" class="anchor" href="#a-titlea-lot" aria-hidden="true"></a><a title="a lot</h2> + <a href="#a-titlea-lot" aria-hidden="true" class="anchor" id="user-content-a-titlea-lot"></a><a title="a lot</h2> <p data-sourcepos="7:1-7:12" dir="auto">of dashes"/></p> wysiwyg: |- <h2 dir="auto">`Foo</h2> @@ -790,7 +790,7 @@ Bar</h2> static: |- <h2 data-sourcepos="1:1-3:3" dir="auto"> - <a id="user-content-foobar" class="anchor" href="#foobar" aria-hidden="true"></a>Foo + <a href="#foo-bar" aria-hidden="true" class="anchor" id="user-content-foo-bar"></a>Foo Bar</h2> wysiwyg: |- <h2 dir="auto">Foo @@ -807,7 +807,7 @@ <copy-code></copy-code> </div> <h2 data-sourcepos="4:1-6:3" dir="auto"> - <a id="user-content-bar" class="anchor" href="#bar" aria-hidden="true"></a>Bar</h2> + <a href="#bar" aria-hidden="true" class="anchor" id="user-content-bar"></a>Bar</h2> <p data-sourcepos="6:1-6:3" dir="auto">Baz</p> wysiwyg: |- <pre language="yaml" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code>Foo</code></pre> @@ -878,7 +878,7 @@ <h2>> foo</h2> static: |- <h2 data-sourcepos="1:1-2:6" dir="auto"> - <a id="user-content--foo" class="anchor" href="#-foo" aria-hidden="true"></a>> foo</h2> + <a href="#-foo" aria-hidden="true" class="anchor" id="user-content--foo"></a>> foo</h2> wysiwyg: |- <h2 dir="auto">> foo</h2> 04_03_00__leaf_blocks__setext_headings__024: @@ -889,7 +889,7 @@ static: |- <p data-sourcepos="1:1-1:3" dir="auto">Foo</p> <h2 data-sourcepos="3:1-5:3" dir="auto"> - <a id="user-content-bar" class="anchor" href="#bar" aria-hidden="true"></a>bar</h2> + <a href="#bar" aria-hidden="true" class="anchor" id="user-content-bar"></a>bar</h2> <p data-sourcepos="5:1-5:3" dir="auto">baz</p> wysiwyg: |- <p dir="auto">Foo</p> @@ -1090,13 +1090,13 @@ <hr /> static: |- <h1 data-sourcepos="1:1-1:9" dir="auto"> - <a id="user-content-heading" class="anchor" href="#heading" aria-hidden="true"></a>Heading</h1> + <a href="#heading" aria-hidden="true" class="anchor" id="user-content-heading"></a>Heading</h1> <div class="gl-relative markdown-code-block js-markdown-code"> <pre data-sourcepos="2:5-2:7" class="code highlight js-syntax-highlight language-plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span></code></pre> <copy-code></copy-code> </div> <h2 data-sourcepos="3:1-5:7" dir="auto"> - <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading</h2> + <a href="#heading-1" aria-hidden="true" class="anchor" id="user-content-heading-1"></a>Heading</h2> <div class="gl-relative markdown-code-block js-markdown-code"> <pre data-sourcepos="5:5-5:7" class="code highlight js-syntax-highlight language-plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">foo</span></code></pre> <copy-code></copy-code> @@ -1448,13 +1448,13 @@ <h1>baz</h1> static: |- <h2 data-sourcepos="1:1-3:3" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h2> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h2> <div class="gl-relative markdown-code-block js-markdown-code"> <pre data-sourcepos="3:1-5:3" class="code highlight js-syntax-highlight language-plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">bar</span></code></pre> <copy-code></copy-code> </div> <h1 data-sourcepos="6:1-6:5" dir="auto"> - <a id="user-content-baz" class="anchor" href="#baz" aria-hidden="true"></a>baz</h1> + <a href="#baz" aria-hidden="true" class="anchor" id="user-content-baz"></a>baz</h1> wysiwyg: |- <h2 dir="auto">foo</h2> <pre dir="auto" class="content-editor-code-block undefined code highlight"><code>bar</code></pre> @@ -2345,7 +2345,7 @@ </blockquote> static: |- <h1 data-sourcepos="1:1-1:7" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a><a data-sourcepos="1:3-1:7" href="/url">Foo</a> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a><a data-sourcepos="1:3-1:7" href="/url">Foo</a> </h1> <blockquote data-sourcepos="3:1-3:5" dir="auto"> <p data-sourcepos="3:3-3:5">bar</p> @@ -2360,7 +2360,7 @@ <p><a href="/url">foo</a></p> static: |- <h1 data-sourcepos="1:1-4:5" dir="auto"> - <a id="user-content-bar" class="anchor" href="#bar" aria-hidden="true"></a>bar</h1> + <a href="#bar" aria-hidden="true" class="anchor" id="user-content-bar"></a>bar</h1> <p data-sourcepos="4:1-4:5" dir="auto"><a data-sourcepos="4:1-4:5" href="/url">foo</a></p> wysiwyg: |- <pre>[foo]: /url</pre> @@ -2510,7 +2510,7 @@ static: |- <p data-sourcepos="3:1-3:3" dir="auto">aaa</p> <h1 data-sourcepos="6:1-6:5" dir="auto"> - <a id="user-content-aaa" class="anchor" href="#aaa" aria-hidden="true"></a>aaa</h1> + <a href="#aaa" aria-hidden="true" class="anchor" id="user-content-aaa"></a>aaa</h1> wysiwyg: |- <p dir="auto">aaa</p> <h1 dir="auto">aaa</h1> @@ -2784,7 +2784,7 @@ static: |- <blockquote data-sourcepos="1:1-3:5" dir="auto"> <h1 data-sourcepos="1:3-1:7"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h1> <p data-sourcepos="2:3-3:5">bar baz</p> </blockquote> @@ -2801,7 +2801,7 @@ static: |- <blockquote data-sourcepos="1:1-3:5" dir="auto"> <h1 data-sourcepos="1:2-1:6"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h1> <p data-sourcepos="2:2-3:5">bar baz</p> </blockquote> @@ -2818,7 +2818,7 @@ static: |- <blockquote data-sourcepos="1:4-3:6" dir="auto"> <h1 data-sourcepos="1:6-1:10"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h1> <p data-sourcepos="2:6-3:6">bar baz</p> </blockquote> @@ -2852,7 +2852,7 @@ static: |- <blockquote data-sourcepos="1:1-3:3" dir="auto"> <h1 data-sourcepos="1:3-1:7"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h1> <p data-sourcepos="2:3-3:3">bar baz</p> </blockquote> @@ -4142,11 +4142,11 @@ <ul data-sourcepos="1:1-4:5" dir="auto"> <li data-sourcepos="1:1-1:7"> <h1 data-sourcepos="1:3-1:7"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h1> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>Foo</h1> </li> <li data-sourcepos="2:1-4:5"> <h2 data-sourcepos="2:3-4:5"> - <a id="user-content-bar" class="anchor" href="#bar" aria-hidden="true"></a>Bar</h2> + <a href="#bar" aria-hidden="true" class="anchor" id="user-content-bar"></a>Bar</h2> baz</li> </ul> wysiwyg: |- @@ -7572,7 +7572,7 @@ <h3>foo\</h3> static: |- <h3 data-sourcepos="1:1-1:8" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo\</h3> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo\</h3> wysiwyg: |- <h3 dir="auto">foo\</h3> 06_13_00__inlines__hard_line_breaks__015: @@ -7580,7 +7580,7 @@ <h3>foo</h3> static: |- <h3 data-sourcepos="1:1-1:9" dir="auto"> - <a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h3> + <a href="#foo" aria-hidden="true" class="anchor" id="user-content-foo"></a>foo</h3> wysiwyg: |- <h3 dir="auto">foo</h3> 06_14_00__inlines__soft_line_breaks__001: @@ -7753,7 +7753,7 @@ <p data-sourcepos="1:1-1:4" dir="auto">text</p> <hr data-sourcepos="3:1-3:3"> <h2 data-sourcepos="4:1-5:3" dir="auto"> - <a id="user-content-title-yaml-front-matter" class="anchor" href="#title-yaml-front-matter" aria-hidden="true"></a>title: YAML front matter</h2> + <a href="#title-yaml-front-matter" aria-hidden="true" class="anchor" id="user-content-title-yaml-front-matter"></a>title: YAML front matter</h2> wysiwyg: |- <p dir="auto">text</p> <hr> @@ -7765,7 +7765,7 @@ static: |- <hr data-sourcepos="1:2-1:4"> <h2 data-sourcepos="2:1-3:3" dir="auto"> - <a id="user-content-title-yaml-front-matter" class="anchor" href="#title-yaml-front-matter" aria-hidden="true"></a>title: YAML front matter</h2> + <a href="#title-yaml-front-matter" aria-hidden="true" class="anchor" id="user-content-title-yaml-front-matter"></a>title: YAML front matter</h2> wysiwyg: |- <hr> <h2 dir="auto">title: YAML front matter</h2> @@ -7786,9 +7786,9 @@ <a href="#heading-1">Heading 1</a><ul><li><a href="#heading-2">Heading 2</a></li></ul> </li></ul> <h1 data-sourcepos="3:1-3:11" dir="auto"> - <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1> + <a href="#heading-1" aria-hidden="true" class="anchor" id="user-content-heading-1"></a>Heading 1</h1> <h2 data-sourcepos="5:1-5:12" dir="auto"> - <a id="user-content-heading-2" class="anchor" href="#heading-2" aria-hidden="true"></a>Heading 2</h2> + <a href="#heading-2" aria-hidden="true" class="anchor" id="user-content-heading-2"></a>Heading 2</h2> wysiwyg: |- <div class="table-of-contents gl-border-1 gl-border-solid gl-text-center gl-border-gray-100 gl-mb-5">Table of contents</div> <h1 dir="auto">Heading 1</h1> @@ -7810,9 +7810,9 @@ <a href="#heading-1">Heading 1</a><ul><li><a href="#heading-2">Heading 2</a></li></ul> </li></ul> <h1 data-sourcepos="3:1-3:11" dir="auto"> - <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1> + <a href="#heading-1" aria-hidden="true" class="anchor" id="user-content-heading-1"></a>Heading 1</h1> <h2 data-sourcepos="5:1-5:12" dir="auto"> - <a id="user-content-heading-2" class="anchor" href="#heading-2" aria-hidden="true"></a>Heading 2</h2> + <a href="#heading-2" aria-hidden="true" class="anchor" id="user-content-heading-2"></a>Heading 2</h2> wysiwyg: |- <div class="table-of-contents gl-border-1 gl-border-solid gl-text-center gl-border-gray-100 gl-mb-5">Table of contents</div> <h1 dir="auto">Heading 1</h1> @@ -7842,7 +7842,7 @@ static: |- <ul class="section-nav"><li><a href="#heading-1">Heading 1</a></li></ul> <h1 data-sourcepos="3:1-3:11" dir="auto"> - <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1> + <a href="#heading-1" aria-hidden="true" class="anchor" id="user-content-heading-1"></a>Heading 1</h1> wysiwyg: |- <div class="table-of-contents gl-border-1 gl-border-solid gl-text-center gl-border-gray-100 gl-mb-5">Table of contents</div> <h1 dir="auto">Heading 1</h1> @@ -8292,17 +8292,17 @@ TODO: Write canonical HTML for this example static: |- <h1 data-sourcepos="1:1-1:11" dir="auto"> - <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1> + <a href="#heading-1" aria-hidden="true" class="anchor" id="user-content-heading-1"></a>Heading 1</h1> <h2 data-sourcepos="3:1-3:12" dir="auto"> - <a id="user-content-heading-2" class="anchor" href="#heading-2" aria-hidden="true"></a>Heading 2</h2> + <a href="#heading-2" aria-hidden="true" class="anchor" id="user-content-heading-2"></a>Heading 2</h2> <h3 data-sourcepos="5:1-5:13" dir="auto"> - <a id="user-content-heading-3" class="anchor" href="#heading-3" aria-hidden="true"></a>Heading 3</h3> + <a href="#heading-3" aria-hidden="true" class="anchor" id="user-content-heading-3"></a>Heading 3</h3> <h4 data-sourcepos="7:1-7:14" dir="auto"> - <a id="user-content-heading-4" class="anchor" href="#heading-4" aria-hidden="true"></a>Heading 4</h4> + <a href="#heading-4" aria-hidden="true" class="anchor" id="user-content-heading-4"></a>Heading 4</h4> <h5 data-sourcepos="9:1-9:15" dir="auto"> - <a id="user-content-heading-5" class="anchor" href="#heading-5" aria-hidden="true"></a>Heading 5</h5> + <a href="#heading-5" aria-hidden="true" class="anchor" id="user-content-heading-5"></a>Heading 5</h5> <h6 data-sourcepos="11:1-11:16" dir="auto"> - <a id="user-content-heading-6" class="anchor" href="#heading-6" aria-hidden="true"></a>Heading 6</h6> + <a href="#heading-6" aria-hidden="true" class="anchor" id="user-content-heading-6"></a>Heading 6</h6> wysiwyg: |- <h1 dir="auto">Heading 1</h1> <h2 dir="auto">Heading 2</h2> @@ -8517,7 +8517,7 @@ </tbody> </table> <h1 data-sourcepos="6:1-6:21" dir="auto"> - <a id="user-content-content-after-table" class="anchor" href="#content-after-table" aria-hidden="true"></a>content after table</h1> + <a href="#content-after-table" aria-hidden="true" class="anchor" id="user-content-content-after-table"></a>content after table</h1> wysiwyg: |- <table><tbody><tr><th colspan="1" rowspan="1"><p dir="auto">header</p></th><th colspan="1" rowspan="1"><p dir="auto">header</p></th></tr><tr><td colspan="1" rowspan="1"><p dir="auto"><code>code</code></p></td><td colspan="1" rowspan="1"><p dir="auto">cell with <strong>bold</strong></p></td></tr><tr><td colspan="1" rowspan="1"><p dir="auto"><s>strike</s></p></td><td colspan="1" rowspan="1"><p dir="auto">cell with <em>italic</em></p></td></tr></tbody></table> <h1 dir="auto">content after table</h1> @@ -8536,16 +8536,16 @@ </li> </ul> <h1 data-sourcepos="3:1-3:7" dir="auto"> - <a id="user-content-lorem" class="anchor" href="#lorem" aria-hidden="true"></a>Lorem</h1> + <a href="#lorem" aria-hidden="true" class="anchor" id="user-content-lorem"></a>Lorem</h1> <p data-sourcepos="5:1-5:45" dir="auto">Well, that's just like... your opinion.. man.</p> <h2 data-sourcepos="7:1-7:8" dir="auto"> - <a id="user-content-ipsum" class="anchor" href="#ipsum" aria-hidden="true"></a>Ipsum</h2> + <a href="#ipsum" aria-hidden="true" class="anchor" id="user-content-ipsum"></a>Ipsum</h2> <h3 data-sourcepos="9:1-9:9" dir="auto"> - <a id="user-content-dolar" class="anchor" href="#dolar" aria-hidden="true"></a>Dolar</h3> + <a href="#dolar" aria-hidden="true" class="anchor" id="user-content-dolar"></a>Dolar</h3> <h1 data-sourcepos="11:1-11:10" dir="auto"> - <a id="user-content-sit-amit" class="anchor" href="#sit-amit" aria-hidden="true"></a>Sit amit</h1> + <a href="#sit-amit" aria-hidden="true" class="anchor" id="user-content-sit-amit"></a>Sit amit</h1> <h3 data-sourcepos="13:1-13:16" dir="auto"> - <a id="user-content-i-dont-know" class="anchor" href="#i-dont-know" aria-hidden="true"></a>I don't know</h3> + <a href="#i-dont-know" aria-hidden="true" class="anchor" id="user-content-i-dont-know"></a>I don't know</h3> wysiwyg: |- <div class="table-of-contents gl-border-1 gl-border-solid gl-text-center gl-border-gray-100 gl-mb-5">Table of contents</div> <h1 dir="auto">Lorem</h1> diff --git a/glfm_specification/output_example_snapshots/snapshot_spec.html b/glfm_specification/output_example_snapshots/snapshot_spec.html index e8c3551ea20dd..b176e0ebf8c4b 100644 --- a/glfm_specification/output_example_snapshots/snapshot_spec.html +++ b/glfm_specification/output_example_snapshots/snapshot_spec.html @@ -238,148 +238,11 @@ <h1 class="title">GitLab Flavored Markdown Internal Extensions</h1> <div class="version">Version alpha</div> -<ul class="section-nav"> -<li> -<a href="#preliminaries">Preliminaries</a><ul> -<li><a href="#characters-and-lines">Characters and lines</a></li> -<li><a href="#tabs">Tabs</a></li> -<li><a href="#insecure-characters">Insecure characters</a></li> -</ul> -</li> -<li> -<a href="#blocks-and-inlines">Blocks and inlines</a><ul> -<li><a href="#precedence">Precedence</a></li> -<li><a href="#container-blocks-and-leaf-blocks">Container blocks and leaf blocks</a></li> -</ul> -</li> -<li> -<a href="#leaf-blocks">Leaf blocks</a><ul> -<li><a href="#thematic-breaks">Thematic breaks</a></li> -<li><a href="#atx-headings">ATX headings</a></li> -<li><a href="#setext-headings">Setext headings</a></li> -<li><a href="#indented-code-blocks">Indented code blocks</a></li> -<li><a href="#fenced-code-blocks">Fenced code blocks</a></li> -<li><a href="#html-blocks">HTML blocks</a></li> -<li><a href="#link-reference-definitions">Link reference definitions</a></li> -<li><a href="#paragraphs">Paragraphs</a></li> -<li><a href="#blank-lines">Blank lines</a></li> -<li><a href="#tables-extension">Tables (extension)</a></li> -</ul> -</li> -<li> -<a href="#container-blocks">Container blocks</a><ul> -<li><a href="#block-quotes">Block quotes</a></li> -<li> -<a href="#list-items">List items</a><ul><li><a href="#motivation">Motivation</a></li></ul> -</li> -<li><a href="#task-list-items-extension">Task list items (extension)</a></li> -<li><a href="#lists">Lists</a></li> -</ul> -</li> -<li> -<a href="#inlines">Inlines</a><ul> -<li><a href="#backslash-escapes">Backslash escapes</a></li> -<li><a href="#entity-and-numeric-character-references">Entity and numeric character references</a></li> -<li><a href="#code-spans">Code spans</a></li> -<li><a href="#emphasis-and-strong-emphasis">Emphasis and strong emphasis</a></li> -<li><a href="#strikethrough-extension">Strikethrough (extension)</a></li> -<li><a href="#links">Links</a></li> -<li><a href="#images">Images</a></li> -<li><a href="#autolinks">Autolinks</a></li> -<li><a href="#autolinks-extension">Autolinks (extension)</a></li> -<li><a href="#raw-html">Raw HTML</a></li> -<li><a href="#disallowed-raw-html-extension">Disallowed Raw HTML (extension)</a></li> -<li><a href="#hard-line-breaks">Hard line breaks</a></li> -<li><a href="#soft-line-breaks">Soft line breaks</a></li> -<li><a href="#textual-content">Textual content</a></li> -</ul> -</li> -<li> -<a href="#gitlab-official-specification-markdown">GitLab Official Specification Markdown</a><ul> -<li><a href="#task-list-items">Task list items</a></li> -<li><a href="#front-matter">Front matter</a></li> -<li><a href="#table-of-contents">Table of contents</a></li> -</ul> -</li> -<li> -<a href="#gitlab-internal-extension-markdown">GitLab Internal Extension Markdown</a><ul> -<li><a href="#audio">Audio</a></li> -<li><a href="#video">Video</a></li> -<li><a href="#markdown-preview-api-request-overrides">Markdown Preview API Request Overrides</a></li> -<li> -<a href="#migrated-golden-master-examples">Migrated golden master examples</a><ul> -<li><a href="#attachment_image_for_group">attachment_image_for_group</a></li> -<li><a href="#attachment_image_for_project">attachment_image_for_project</a></li> -<li><a href="#attachment_image_for_project_wiki">attachment_image_for_project_wiki</a></li> -<li><a href="#attachment_link_for_group">attachment_link_for_group</a></li> -<li><a href="#attachment_link_for_project">attachment_link_for_project</a></li> -<li><a href="#attachment_link_for_project_wiki">attachment_link_for_project_wiki</a></li> -<li><a href="#attachment_link_for_group_wiki">attachment_link_for_group_wiki</a></li> -<li><a href="#audio-1">audio</a></li> -<li><a href="#audio_and_video_in_lists">audio_and_video_in_lists</a></li> -<li><a href="#blockquote">blockquote</a></li> -<li><a href="#bold">bold</a></li> -<li><a href="#bullet_list_style_1">bullet_list_style_1</a></li> -<li><a href="#bullet_list_style_2">bullet_list_style_2</a></li> -<li><a href="#bullet_list_style_3">bullet_list_style_3</a></li> -<li><a href="#code_block_javascript">code_block_javascript</a></li> -<li><a href="#code_block_plaintext">code_block_plaintext</a></li> -<li><a href="#code_block_unknown">code_block_unknown</a></li> -<li><a href="#color_chips">color_chips</a></li> -<li><a href="#description_list">description_list</a></li> -<li><a href="#details">details</a></li> -<li><a href="#diagram_kroki_nomnoml">diagram_kroki_nomnoml</a></li> -<li><a href="#diagram_plantuml">diagram_plantuml</a></li> -<li><a href="#diagram_plantuml_unicode">diagram_plantuml_unicode</a></li> -<li><a href="#div">div</a></li> -<li><a href="#emoji">emoji</a></li> -<li><a href="#emphasis">emphasis</a></li> -<li><a href="#figure">figure</a></li> -<li><a href="#footnotes">footnotes</a></li> -<li><a href="#frontmatter_json">frontmatter_json</a></li> -<li><a href="#frontmatter_toml">frontmatter_toml</a></li> -<li><a href="#frontmatter_yaml">frontmatter_yaml</a></li> -<li><a href="#hard_break">hard_break</a></li> -<li><a href="#headings">headings</a></li> -<li><a href="#horizontal_rule">horizontal_rule</a></li> -<li><a href="#html_marks">html_marks</a></li> -<li><a href="#image">image</a></li> -<li><a href="#inline_code">inline_code</a></li> -<li><a href="#inline_diff">inline_diff</a></li> -<li><a href="#label">label</a></li> -<li><a href="#link">link</a></li> -<li><a href="#math">math</a></li> -<li><a href="#ordered_list">ordered_list</a></li> -<li><a href="#ordered_list_with_start_order">ordered_list_with_start_order</a></li> -<li><a href="#ordered_task_list">ordered_task_list</a></li> -<li><a href="#ordered_task_list_with_order">ordered_task_list_with_order</a></li> -<li><a href="#reference_for_project_wiki">reference_for_project_wiki</a></li> -<li><a href="#strike">strike</a></li> -<li><a href="#table">table</a></li> -<li><a href="#table_of_contents">table_of_contents</a></li> -<li><a href="#task_list">task_list</a></li> -<li><a href="#video-1">video</a></li> -<li><a href="#word_break">word_break</a></li> -</ul> -</li> -<li><a href="#image-attributes">Image Attributes</a></li> -<li><a href="#footnotes-1">Footnotes</a></li> -</ul> -</li> -<li> -<a href="#gfm-undocumented-extensions-and-more-robust-test">GFM undocumented extensions and more robust test</a><ul> -<li><a href="#footnotes-2">Footnotes</a></li> -<li><a href="#when-a-footnote-is-used-multiple-times-we-insert-multiple-backrefs">When a footnote is used multiple times, we insert multiple backrefs.</a></li> -<li><a href="#footnote-reference-labels-are-href-escaped">Footnote reference labels are href escaped</a></li> -<li><a href="#interop">Interop</a></li> -<li><a href="#task-lists">Task lists</a></li> -</ul> -</li> -</ul> + <h1 data-sourcepos="3:1-3:15" dir="auto"> -<a id="user-content-preliminaries" class="anchor" href="#preliminaries" aria-hidden="true"></a>Preliminaries</h1> +<a href="#preliminaries" aria-hidden="true" class="anchor" id="user-content-preliminaries"></a>Preliminaries</h1> <h2 data-sourcepos="5:1-5:23" dir="auto"> -<a id="user-content-characters-and-lines" class="anchor" href="#characters-and-lines" aria-hidden="true"></a>Characters and lines</h2> +<a href="#characters-and-lines" aria-hidden="true" class="anchor" id="user-content-characters-and-lines"></a>Characters and lines</h2> <p data-sourcepos="7:1-8:9" dir="auto">Any sequence of [characters] is a valid CommonMark document.</p> <p data-sourcepos="10:1-13:26" dir="auto">A <a data-sourcepos="10:3-10:16" href="@">character</a> is a Unicode code point. Although some @@ -422,7 +285,7 @@ is <code data-sourcepos="53:5-53:5">!</code>, <code data-sourcepos="53:10-53:10" punctuation character] or anything in the general Unicode categories <code data-sourcepos="61:34-61:35">Pc</code>, <code data-sourcepos="61:40-61:41">Pd</code>, <code data-sourcepos="61:46-61:47">Pe</code>, <code data-sourcepos="61:52-61:53">Pf</code>, <code data-sourcepos="61:58-61:59">Pi</code>, <code data-sourcepos="61:64-61:65">Po</code>, or <code data-sourcepos="61:73-61:74">Ps</code>.</p> <h2 data-sourcepos="63:1-63:7" dir="auto"> -<a id="user-content-tabs" class="anchor" href="#tabs" aria-hidden="true"></a>Tabs</h2> +<a href="#tabs" aria-hidden="true" class="anchor" id="user-content-tabs"></a>Tabs</h2> <p data-sourcepos="65:1-68:16" dir="auto">Tabs in lines are not expanded to [spaces]. However, in contexts where whitespace helps to define block structure, tabs behave as if they were replaced by spaces with a tab stop @@ -607,11 +470,11 @@ code block starting with two spaces.</p> </div> </div> <h2 data-sourcepos="265:1-265:22" dir="auto"> -<a id="user-content-insecure-characters" class="anchor" href="#insecure-characters" aria-hidden="true"></a>Insecure characters</h2> +<a href="#insecure-characters" aria-hidden="true" class="anchor" id="user-content-insecure-characters"></a>Insecure characters</h2> <p data-sourcepos="267:1-268:42" dir="auto">For security reasons, the Unicode character <code data-sourcepos="267:46-267:51">U+0000</code> must be replaced with the REPLACEMENT CHARACTER (<code data-sourcepos="268:34-268:39">U+FFFD</code>).</p> <h1 data-sourcepos="270:1-270:20" dir="auto"> -<a id="user-content-blocks-and-inlines" class="anchor" href="#blocks-and-inlines" aria-hidden="true"></a>Blocks and inlines</h1> +<a href="#blocks-and-inlines" aria-hidden="true" class="anchor" id="user-content-blocks-and-inlines"></a>Blocks and inlines</h1> <p data-sourcepos="272:1-277:54" dir="auto">We can think of a document as a sequence of <a data-sourcepos="273:1-273:11" href="@">blocks</a>---structural elements like paragraphs, block quotations, lists, headings, rules, and code blocks. Some blocks (like @@ -619,7 +482,7 @@ block quotes and list items) contain other blocks; others (like headings and paragraphs) contain <a data-sourcepos="276:34-276:44" href="@">inline</a> content---text, links, emphasized text, images, code spans, and so on.</p> <h2 data-sourcepos="279:1-279:13" dir="auto"> -<a id="user-content-precedence" class="anchor" href="#precedence" aria-hidden="true"></a>Precedence</h2> +<a href="#precedence" aria-hidden="true" class="anchor" id="user-content-precedence"></a>Precedence</h2> <p data-sourcepos="281:1-283:59" dir="auto">Indicators of block structure always take precedence over indicators of inline structure. So, for example, the following is a list with two items, not a list with one item containing a code span:</p> @@ -647,17 +510,17 @@ step. Note that the first step requires processing lines in sequence, but the second can be parallelized, since the inline parsing of one block element does not affect the inline parsing of any other.</p> <h2 data-sourcepos="311:1-311:35" dir="auto"> -<a id="user-content-container-blocks-and-leaf-blocks" class="anchor" href="#container-blocks-and-leaf-blocks" aria-hidden="true"></a>Container blocks and leaf blocks</h2> +<a href="#container-blocks-and-leaf-blocks" aria-hidden="true" class="anchor" id="user-content-container-blocks-and-leaf-blocks"></a>Container blocks and leaf blocks</h2> <p data-sourcepos="313:1-316:13" dir="auto">We can divide blocks into two types: <a data-sourcepos="314:1-314:21" href="@">container blocks</a>, which can contain other blocks, and <a data-sourcepos="315:37-315:52" href="@">leaf blocks</a>, which cannot.</p> <h1 data-sourcepos="318:1-318:13" dir="auto"> -<a id="user-content-leaf-blocks" class="anchor" href="#leaf-blocks" aria-hidden="true"></a>Leaf blocks</h1> +<a href="#leaf-blocks" aria-hidden="true" class="anchor" id="user-content-leaf-blocks"></a>Leaf blocks</h1> <p data-sourcepos="320:1-321:18" dir="auto">This section describes the different kinds of leaf block that make up a Markdown document.</p> <h2 data-sourcepos="323:1-323:18" dir="auto"> -<a id="user-content-thematic-breaks" class="anchor" href="#thematic-breaks" aria-hidden="true"></a>Thematic breaks</h2> +<a href="#thematic-breaks" aria-hidden="true" class="anchor" id="user-content-thematic-breaks"></a>Thematic breaks</h2> <p data-sourcepos="325:1-328:20" dir="auto">A line consisting of 0-3 spaces of indentation, followed by a sequence of three or more matching <code data-sourcepos="326:28-326:28">-</code>, <code data-sourcepos="326:33-326:33">_</code>, or <code data-sourcepos="326:41-326:41">*</code> characters, each followed optionally by any number of spaces or tabs, forms a @@ -942,7 +805,7 @@ interpretations of a line, the thematic break takes precedence:</p> </div> </div> <h2 data-sourcepos="661:1-661:15" dir="auto"> -<a id="user-content-atx-headings" class="anchor" href="#atx-headings" aria-hidden="true"></a>ATX headings</h2> +<a href="#atx-headings" aria-hidden="true" class="anchor" id="user-content-atx-headings"></a>ATX headings</h2> <p data-sourcepos="663:1-673:35" dir="auto">An <a data-sourcepos="663:4-663:19" href="@">ATX heading</a> consists of a string of characters, parsed as inline content, between an opening sequence of 1--6 unescaped <code data-sourcepos="665:37-665:37">#</code> characters and an optional @@ -1219,7 +1082,7 @@ lines, and they can interrupt paragraphs:</p> </div> </div> <h2 data-sourcepos="991:1-991:18" dir="auto"> -<a id="user-content-setext-headings" class="anchor" href="#setext-headings" aria-hidden="true"></a>Setext headings</h2> +<a href="#setext-headings" aria-hidden="true" class="anchor" id="user-content-setext-headings"></a>Setext headings</h2> <p data-sourcepos="993:1-1001:54" dir="auto">A <a data-sourcepos="993:3-993:21" href="@">setext heading</a> consists of one or more lines of text, each containing at least one [non-whitespace character], with no more than 3 spaces indentation, followed by @@ -1720,7 +1583,7 @@ underline], such as</p> </div> </div> <h2 data-sourcepos="1572:1-1572:23" dir="auto"> -<a id="user-content-indented-code-blocks" class="anchor" href="#indented-code-blocks" aria-hidden="true"></a>Indented code blocks</h2> +<a href="#indented-code-blocks" aria-hidden="true" class="anchor" id="user-content-indented-code-blocks"></a>Indented code blocks</h2> <p data-sourcepos="1574:1-1580:44" dir="auto">An <a data-sourcepos="1574:4-1574:27" href="@">indented code block</a> is composed of one or more [indented chunks] separated by blank lines. An <a data-sourcepos="1576:4-1576:22" href="@">indented chunk</a> is a sequence of non-blank lines, @@ -1954,7 +1817,7 @@ are not included in it:</p> </div> </div> <h2 data-sourcepos="1844:1-1844:21" dir="auto"> -<a id="user-content-fenced-code-blocks" class="anchor" href="#fenced-code-blocks" aria-hidden="true"></a>Fenced code blocks</h2> +<a href="#fenced-code-blocks" aria-hidden="true" class="anchor" id="user-content-fenced-code-blocks"></a>Fenced code blocks</h2> <p data-sourcepos="1846:1-1850:61" dir="auto">A <a data-sourcepos="1846:3-1846:17" href="@">code fence</a> is a sequence of at least three consecutive backtick characters (<code data-sourcepos="1847:54-1847:56">`</code>) or tildes (<code data-sourcepos="1848:10-1848:10">~</code>). (Tildes and backticks cannot be mixed.) @@ -2471,7 +2334,7 @@ of <code data-sourcepos="2334:5-2334:13">language-</code> followed by the langua </div> </div> <h2 data-sourcepos="2444:1-2444:14" dir="auto"> -<a id="user-content-html-blocks" class="anchor" href="#html-blocks" aria-hidden="true"></a>HTML blocks</h2> +<a href="#html-blocks" aria-hidden="true" class="anchor" id="user-content-html-blocks"></a>HTML blocks</h2> <p data-sourcepos="2446:1-2447:53" dir="auto">An <a data-sourcepos="2446:4-2446:18" href="@">HTML block</a> is a group of lines that is treated as raw HTML (and will not be escaped in HTML output).</p> <p data-sourcepos="2449:1-2457:50" dir="auto">There are seven kinds of [HTML block], which can be defined by their @@ -3388,7 +3251,7 @@ deleted. The exception is inside <code data-sourcepos="3476:36-3476:40"><pre [above][HTML blocks], raw HTML blocks starting with <code data-sourcepos="3477:54-3477:58"><pre></code> <em data-sourcepos="3478:1-3478:5">can</em> contain blank lines.</p> <h2 data-sourcepos="3480:1-3480:29" dir="auto"> -<a id="user-content-link-reference-definitions" class="anchor" href="#link-reference-definitions" aria-hidden="true"></a>Link reference definitions</h2> +<a href="#link-reference-definitions" aria-hidden="true" class="anchor" id="user-content-link-reference-definitions"></a>Link reference definitions</h2> <p data-sourcepos="3482:1-3490:61" dir="auto">A <a data-sourcepos="3482:3-3482:32" href="@">link reference definition</a> consists of a [link label], indented up to three spaces, followed by a colon (<code data-sourcepos="3484:14-3484:14">:</code>), optional [whitespace] (including up to one @@ -3846,7 +3709,7 @@ no visible content:</p> </div> </div> <h2 data-sourcepos="4011:1-4011:13" dir="auto"> -<a id="user-content-paragraphs" class="anchor" href="#paragraphs" aria-hidden="true"></a>Paragraphs</h2> +<a href="#paragraphs" aria-hidden="true" class="anchor" id="user-content-paragraphs"></a>Paragraphs</h2> <p data-sourcepos="4013:1-4018:13" dir="auto">A sequence of non-blank lines that cannot be interpreted as other kinds of blocks forms a <a data-sourcepos="4014:25-4014:38" href="@">paragraph</a>. The contents of the paragraph are the result of parsing the @@ -3980,7 +3843,7 @@ break]:</p> </div> </div> <h2 data-sourcepos="4169:1-4169:14" dir="auto"> -<a id="user-content-blank-lines" class="anchor" href="#blank-lines" aria-hidden="true"></a>Blank lines</h2> +<a href="#blank-lines" aria-hidden="true" class="anchor" id="user-content-blank-lines"></a>Blank lines</h2> <p data-sourcepos="4171:1-4173:22" dir="auto">[Blank lines] between block-level elements are ignored, except for the role they play in determining whether a [list] is [tight] or [loose].</p> @@ -4006,7 +3869,7 @@ is [tight] or [loose].</p> </div> <div> <h2 data-sourcepos="4199:1-4199:21"> -<a id="user-content-tables-extension" class="anchor" href="#tables-extension" aria-hidden="true"></a>Tables (extension)</h2> +<a href="#tables-extension" aria-hidden="true" class="anchor" id="user-content-tables-extension"></a>Tables (extension)</h2> <p data-sourcepos="4201:1-4202:10">GFM enables the <code data-sourcepos="4201:18-4201:22">table</code> extension, where an additional leaf block type is available.</p> <p data-sourcepos="4204:1-4206:23">A <a data-sourcepos="4204:3-4204:12" href="@">table</a> is an arrangement of data with rows and columns, consisting of a @@ -4243,7 +4106,7 @@ cells are inserted. If there are greater, the excess is ignored:</p> </div> </div> <h1 data-sourcepos="4455:1-4455:18" dir="auto"> -<a id="user-content-container-blocks" class="anchor" href="#container-blocks" aria-hidden="true"></a>Container blocks</h1> +<a href="#container-blocks" aria-hidden="true" class="anchor" id="user-content-container-blocks"></a>Container blocks</h1> <p data-sourcepos="4457:1-4460:45" dir="auto">A <a data-sourcepos="4457:3-4457:38" href="#container-blocks">container block</a> is a block that has other blocks as its contents. There are two basic kinds of container blocks: [block quotes] and [list items]. @@ -4261,7 +4124,7 @@ to define the syntax, although it does not give a recipe for <em data-sourcepos= these constructions. (A recipe is provided below in the section entitled <a data-sourcepos="4473:1-4473:50" href="#appendix-a-parsing-strategy">A parsing strategy</a>.)</p> <h2 data-sourcepos="4475:1-4475:15" dir="auto"> -<a id="user-content-block-quotes" class="anchor" href="#block-quotes" aria-hidden="true"></a>Block quotes</h2> +<a href="#block-quotes" aria-hidden="true" class="anchor" id="user-content-block-quotes"></a>Block quotes</h2> <p data-sourcepos="4477:1-4479:78" dir="auto">A <a data-sourcepos="4477:3-4477:25" href="@">block quote marker</a> consists of 0-3 spaces of initial indent, plus (a) the character <code data-sourcepos="4478:67-4478:67">></code> together with a following space, or (b) a single character <code data-sourcepos="4479:52-4479:52">></code> not followed by a space.</p> @@ -4764,7 +4627,7 @@ the <code data-sourcepos="5028:6-5028:6">></code>:</p> </div> </div> <h2 data-sourcepos="5052:1-5052:13" dir="auto"> -<a id="user-content-list-items" class="anchor" href="#list-items" aria-hidden="true"></a>List items</h2> +<a href="#list-items" aria-hidden="true" class="anchor" id="user-content-list-items"></a>List items</h2> <p data-sourcepos="5054:1-5055:49" dir="auto">A <a data-sourcepos="5054:3-5054:18" href="@">list marker</a> is a [bullet list marker] or an [ordered list marker].</p> <p data-sourcepos="5057:1-5058:32" dir="auto">A <a data-sourcepos="5057:3-5057:25" href="@">bullet list marker</a> @@ -5857,7 +5720,7 @@ in the list item.</p> </div> </div> <h3 data-sourcepos="6274:1-6274:14" dir="auto"> -<a id="user-content-motivation" class="anchor" href="#motivation" aria-hidden="true"></a>Motivation</h3> +<a href="#motivation" aria-hidden="true" class="anchor" id="user-content-motivation"></a>Motivation</h3> <p data-sourcepos="6276:1-6276:64" dir="auto">John Gruber's Markdown spec says the following about list items:</p> <ol data-sourcepos="6278:1-6297:0" dir="auto"> <li data-sourcepos="6278:1-6281:0"> @@ -6034,7 +5897,7 @@ four-space rule in cases where the list marker plus its initial indentation takes four spaces (a common case), but diverge in other cases.</p> <div> <h2 data-sourcepos="6462:1-6462:30"> -<a id="user-content-task-list-items-extension" class="anchor" href="#task-list-items-extension" aria-hidden="true"></a>Task list items (extension)</h2> +<a href="#task-list-items-extension" aria-hidden="true" class="anchor" id="user-content-task-list-items-extension"></a>Task list items (extension)</h2> <p data-sourcepos="6464:1-6465:26">GFM enables the <code data-sourcepos="6464:18-6464:25">tasklist</code> extension, where an additional processing step is performed on [list items].</p> <p data-sourcepos="6467:1-6469:46">A <a data-sourcepos="6467:3-6467:21" href="@">task list item</a> is a [list item][list items] where the first block in it @@ -6091,7 +5954,7 @@ the final rendered document.</p> </div> </div> <h2 data-sourcepos="6529:1-6529:8" dir="auto"> -<a id="user-content-lists" class="anchor" href="#lists" aria-hidden="true"></a>Lists</h2> +<a href="#lists" aria-hidden="true" class="anchor" id="user-content-lists"></a>Lists</h2> <p data-sourcepos="6531:1-6533:46" dir="auto">A <a data-sourcepos="6531:3-6531:11" href="@">list</a> is a sequence of one or more list items [of the same type]. The list items may be separated by any number of blank lines.</p> @@ -6801,7 +6664,7 @@ two block elements in the list item:</p> </div> </div> <h1 data-sourcepos="7317:1-7317:9" dir="auto"> -<a id="user-content-inlines" class="anchor" href="#inlines" aria-hidden="true"></a>Inlines</h1> +<a href="#inlines" aria-hidden="true" class="anchor" id="user-content-inlines"></a>Inlines</h1> <p data-sourcepos="7319:1-7321:21" dir="auto">Inlines are parsed sequentially from the beginning of the character stream to the end (left to right, in left-to-right languages). Thus, for example, in</p> @@ -6819,7 +6682,7 @@ Thus, for example, in</p> <p data-sourcepos="7335:1-7336:9" dir="auto"><code data-sourcepos="7335:2-7335:3">hi</code> is parsed as code, leaving the backtick at the end as a literal backtick.</p> <h2 data-sourcepos="7339:1-7339:20" dir="auto"> -<a id="user-content-backslash-escapes" class="anchor" href="#backslash-escapes" aria-hidden="true"></a>Backslash escapes</h2> +<a href="#backslash-escapes" aria-hidden="true" class="anchor" id="user-content-backslash-escapes"></a>Backslash escapes</h2> <p data-sourcepos="7341:1-7341:57" dir="auto">Any ASCII punctuation character may be backslash-escaped:</p> <div> <div><a href="#example-308">Example 308</a></div> @@ -7002,7 +6865,7 @@ link references, and [info strings] in [fenced code blocks]:</p> </div> </div> <h2 data-sourcepos="7556:1-7556:42" dir="auto"> -<a id="user-content-entity-and-numeric-character-references" class="anchor" href="#entity-and-numeric-character-references" aria-hidden="true"></a>Entity and numeric character references</h2> +<a href="#entity-and-numeric-character-references" aria-hidden="true" class="anchor" id="user-content-entity-and-numeric-character-references"></a>Entity and numeric character references</h2> <p data-sourcepos="7558:1-7560:30" dir="auto">Valid HTML entity references and numeric character references can be used in place of the corresponding Unicode character, with the following exceptions:</p> @@ -7267,7 +7130,7 @@ documents.</p> </div> </div> <h2 data-sourcepos="7859:1-7859:13" dir="auto"> -<a id="user-content-code-spans" class="anchor" href="#code-spans" aria-hidden="true"></a>Code spans</h2> +<a href="#code-spans" aria-hidden="true" class="anchor" id="user-content-code-spans"></a>Code spans</h2> <p data-sourcepos="7861:1-7863:36" dir="auto">A <a data-sourcepos="7861:3-7861:22" href="@">backtick string</a> is a string of one or more backtick characters (<code data-sourcepos="7862:51-7862:53">`</code>) that is neither preceded nor followed by a backtick.</p> @@ -7576,7 +7439,7 @@ closing backtick strings to be equal in length:</p> </div> </div> <h2 data-sourcepos="8224:1-8224:31" dir="auto"> -<a id="user-content-emphasis-and-strong-emphasis" class="anchor" href="#emphasis-and-strong-emphasis" aria-hidden="true"></a>Emphasis and strong emphasis</h2> +<a href="#emphasis-and-strong-emphasis" aria-hidden="true" class="anchor" id="user-content-emphasis-and-strong-emphasis"></a>Emphasis and strong emphasis</h2> <p data-sourcepos="8226:1-8227:73" dir="auto">John Gruber's original <a data-sourcepos="8227:24-8227:67" href="http://daringfireball.net/projects/markdown/syntax#em" rel="nofollow noreferrer noopener" target="_blank">Markdown syntax description</a> says:</p> <blockquote data-sourcepos="8229:1-8232:6" dir="auto"> @@ -9384,7 +9247,7 @@ delimiters:</p> </div> <div> <h2 data-sourcepos="10357:1-10357:28"> -<a id="user-content-strikethrough-extension" class="anchor" href="#strikethrough-extension" aria-hidden="true"></a>Strikethrough (extension)</h2> +<a href="#strikethrough-extension" aria-hidden="true" class="anchor" id="user-content-strikethrough-extension"></a>Strikethrough (extension)</h2> <p data-sourcepos="10359:1-10360:10">GFM enables the <code data-sourcepos="10359:18-10359:30">strikethrough</code> extension, where an additional emphasis type is available.</p> <p data-sourcepos="10362:1-10362:59">Strikethrough text is any text wrapped in two tildes (<code data-sourcepos="10362:56-10362:56">~</code>).</p> @@ -9417,7 +9280,7 @@ parsing to cease:</p> </div> </div> <h2 data-sourcepos="10396:1-10396:8" dir="auto"> -<a id="user-content-links" class="anchor" href="#links" aria-hidden="true"></a>Links</h2> +<a href="#links" aria-hidden="true" class="anchor" id="user-content-links"></a>Links</h2> <p data-sourcepos="10398:1-10403:13" dir="auto">A link contains [link text] (the visible text), a [link destination] (the URI that is the link destination), and optionally a [link title]. There are two basic kinds of links in Markdown. In [inline links] the @@ -10762,7 +10625,7 @@ is followed by a link label (even though <code data-sourcepos="11942:43-11942:47 </div> </div> <h2 data-sourcepos="11961:1-11961:9" dir="auto"> -<a id="user-content-images" class="anchor" href="#images" aria-hidden="true"></a>Images</h2> +<a href="#images" aria-hidden="true" class="anchor" id="user-content-images"></a>Images</h2> <p data-sourcepos="11963:1-11971:55" dir="auto">Syntax for images is like the syntax for links, with one difference. Instead of [link text], we have an <a data-sourcepos="11965:1-11965:22" href="@">image description</a>. The rules for this are the @@ -11065,7 +10928,7 @@ backslash-escape the opening <code data-sourcepos="12285:31-12285:31">[</code>:< </div> </div> <h2 data-sourcepos="12320:1-12320:12" dir="auto"> -<a id="user-content-autolinks" class="anchor" href="#autolinks" aria-hidden="true"></a>Autolinks</h2> +<a href="#autolinks" aria-hidden="true" class="anchor" id="user-content-autolinks"></a>Autolinks</h2> <p data-sourcepos="12322:1-12324:18" dir="auto"><a data-sourcepos="12322:1-12322:13" href="@">Autolink</a>s are absolute URIs and email addresses inside <code data-sourcepos="12323:2-12323:2"><</code> and <code data-sourcepos="12323:10-12323:10">></code>. They are parsed as links, with the URL or email address as the link label.</p> @@ -11317,7 +11180,7 @@ spec</a>:</p> </div> <div> <h2 data-sourcepos="12622:1-12622:24"> -<a id="user-content-autolinks-extension" class="anchor" href="#autolinks-extension" aria-hidden="true"></a>Autolinks (extension)</h2> +<a href="#autolinks-extension" aria-hidden="true" class="anchor" id="user-content-autolinks-extension"></a>Autolinks (extension)</h2> <p data-sourcepos="12624:1-12625:29">GFM enables the <code data-sourcepos="12624:18-12624:25">autolink</code> extension, where autolinks will be recognised in a greater number of conditions.</p> <p data-sourcepos="12627:1-12631:8">[Autolink]s can also be constructed without requiring the use of <code data-sourcepos="12627:67-12627:67"><</code> and to <code data-sourcepos="12627:78-12627:78">></code> @@ -11523,7 +11386,7 @@ the address:</p> </div> </div> <h2 data-sourcepos="12856:1-12856:11" dir="auto"> -<a id="user-content-raw-html" class="anchor" href="#raw-html" aria-hidden="true"></a>Raw HTML</h2> +<a href="#raw-html" aria-hidden="true" class="anchor" id="user-content-raw-html"></a>Raw HTML</h2> <p data-sourcepos="12858:1-12861:57" dir="auto">Text between <code data-sourcepos="12858:15-12858:15"><</code> and <code data-sourcepos="12858:23-12858:23">></code> that looks like an HTML tag is parsed as a raw HTML tag and will be rendered in HTML without escaping. Tag and attribute names are not limited to current HTML tags, @@ -11846,7 +11709,7 @@ attributes:</p> </div> <div> <h2 data-sourcepos="13258:1-13258:34"> -<a id="user-content-disallowed-raw-html-extension" class="anchor" href="#disallowed-raw-html-extension" aria-hidden="true"></a>Disallowed Raw HTML (extension)</h2> +<a href="#disallowed-raw-html-extension" aria-hidden="true" class="anchor" id="user-content-disallowed-raw-html-extension"></a>Disallowed Raw HTML (extension)</h2> <p data-sourcepos="13260:1-13261:36">GFM enables the <code data-sourcepos="13260:18-13260:26">tagfilter</code> extension, where the following HTML tags will be filtered when rendering HTML output:</p> <ul data-sourcepos="13263:1-13272:0"> @@ -11885,7 +11748,7 @@ usually undesireable in the context of other rendered Markdown content.</p> </div> </div> <h2 data-sourcepos="13301:1-13301:19" dir="auto"> -<a id="user-content-hard-line-breaks" class="anchor" href="#hard-line-breaks" aria-hidden="true"></a>Hard line breaks</h2> +<a href="#hard-line-breaks" aria-hidden="true" class="anchor" id="user-content-hard-line-breaks"></a>Hard line breaks</h2> <p data-sourcepos="13303:1-13306:27" dir="auto">A line break (not in a code span or HTML tag) that is preceded by two or more spaces and does not occur at the end of a block is parsed as a <a data-sourcepos="13305:16-13305:35" href="@">hard line break</a> (rendered @@ -12087,7 +11950,7 @@ other block element:</p> </div> </div> <h2 data-sourcepos="13541:1-13541:19" dir="auto"> -<a id="user-content-soft-line-breaks" class="anchor" href="#soft-line-breaks" aria-hidden="true"></a>Soft line breaks</h2> +<a href="#soft-line-breaks" aria-hidden="true" class="anchor" id="user-content-soft-line-breaks"></a>Soft line breaks</h2> <p data-sourcepos="13543:1-13547:62" dir="auto">A regular line break (not in a code span or HTML tag) that is not preceded by two or more spaces or a backslash is parsed as a <a data-sourcepos="13545:1-13545:14" href="@">softbreak</a>. (A softbreak may be rendered in HTML either as a @@ -12126,7 +11989,7 @@ line break or as a space.</p> <p data-sourcepos="13585:1-13586:20" dir="auto">A renderer may also provide an option to render soft line breaks as hard line breaks.</p> <h2 data-sourcepos="13588:1-13588:18" dir="auto"> -<a id="user-content-textual-content" class="anchor" href="#textual-content" aria-hidden="true"></a>Textual content</h2> +<a href="#textual-content" aria-hidden="true" class="anchor" id="user-content-textual-content"></a>Textual content</h2> <p data-sourcepos="13590:1-13591:35" dir="auto">Any characters not given an interpretation by the above rules will be parsed as plain textual content.</p> <div> @@ -12164,14 +12027,14 @@ be parsed as plain textual content.</p> </div> </div> <h1 data-sourcepos="13634:1-13634:40" dir="auto"> -<a id="user-content-gitlab-official-specification-markdown" class="anchor" href="#gitlab-official-specification-markdown" aria-hidden="true"></a>GitLab Official Specification Markdown</h1> +<a href="#gitlab-official-specification-markdown" aria-hidden="true" class="anchor" id="user-content-gitlab-official-specification-markdown"></a>GitLab Official Specification Markdown</h1> <p data-sourcepos="13636:1-13638:104" dir="auto">Note: This specification is a work in progress. Only some of the official GLFM extensions are defined. We will continue to add any additional ones found in the <a data-sourcepos="13638:1-13638:103" href="https://docs.gitlab.com/ee/user/markdown.html" rel="nofollow noreferrer noopener" target="_blank">user-facing documentation for GitLab Flavored Markdown</a>.</p> <p data-sourcepos="13640:1-13641:69" dir="auto">There is currently only this single top-level heading, but the examples may be split into multiple top-level headings in the future.</p> <h2 data-sourcepos="13643:1-13643:18" dir="auto"> -<a id="user-content-task-list-items" class="anchor" href="#task-list-items" aria-hidden="true"></a>Task list items</h2> +<a href="#task-list-items" aria-hidden="true" class="anchor" id="user-content-task-list-items"></a>Task list items</h2> <p data-sourcepos="13645:1-13646:117" dir="auto">See <a data-sourcepos="13646:1-13646:70" href="https://docs.gitlab.com/ee/user/markdown.html#task-lists" rel="nofollow noreferrer noopener" target="_blank">Task lists</a> in the GitLab Flavored Markdown documentation.</p> <p data-sourcepos="13648:1-13651:39" dir="auto">Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above. @@ -12265,7 +12128,7 @@ loose text; it has strikethrough applied with CSS.</p> </div> </div> <h2 data-sourcepos="13749:1-13749:15" dir="auto"> -<a id="user-content-front-matter" class="anchor" href="#front-matter" aria-hidden="true"></a>Front matter</h2> +<a href="#front-matter" aria-hidden="true" class="anchor" id="user-content-front-matter"></a>Front matter</h2> <p data-sourcepos="13751:1-13752:121" dir="auto">See <a data-sourcepos="13752:1-13752:74" href="https://docs.gitlab.com/ee/user/markdown.html#front-matter" rel="nofollow noreferrer noopener" target="_blank">Front matter</a> in the GitLab Flavored Markdown documentation.</p> <p data-sourcepos="13754:1-13755:95" dir="auto">Front matter is metadata included at the beginning of a Markdown document, preceding the content. @@ -12362,7 +12225,7 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe </div> </div> <h2 data-sourcepos="13858:1-13858:20" dir="auto"> -<a id="user-content-table-of-contents" class="anchor" href="#table-of-contents" aria-hidden="true"></a>Table of contents</h2> +<a href="#table-of-contents" aria-hidden="true" class="anchor" id="user-content-table-of-contents"></a>Table of contents</h2> <p data-sourcepos="13860:1-13862:46" dir="auto">See <a data-sourcepos="13861:1-13861:84" href="https://docs.gitlab.com/ee/user/markdown.html#table-of-contents" rel="nofollow noreferrer noopener" target="_blank">table of contents</a> in the GitLab Flavored Markdown documentation.</p> @@ -12459,9 +12322,9 @@ line.</p> </div> </div> <h1 data-sourcepos="13964:1-13964:36" dir="auto"> -<a id="user-content-gitlab-internal-extension-markdown" class="anchor" href="#gitlab-internal-extension-markdown" aria-hidden="true"></a>GitLab Internal Extension Markdown</h1> +<a href="#gitlab-internal-extension-markdown" aria-hidden="true" class="anchor" id="user-content-gitlab-internal-extension-markdown"></a>GitLab Internal Extension Markdown</h1> <h2 data-sourcepos="13966:1-13966:8" dir="auto"> -<a id="user-content-audio" class="anchor" href="#audio" aria-hidden="true"></a>Audio</h2> +<a href="#audio" aria-hidden="true" class="anchor" id="user-content-audio"></a>Audio</h2> <p data-sourcepos="13968:1-13969:107" dir="auto">See <a data-sourcepos="13969:1-13969:60" href="https://docs.gitlab.com/ee/user/markdown.html#audio" rel="nofollow noreferrer noopener" target="_blank">audio</a> in the GitLab Flavored Markdown documentation.</p> <p data-sourcepos="13971:1-13973:63" dir="auto">GLFM renders image elements as an audio player as long as the resource’s file extension is @@ -12493,7 +12356,7 @@ Audio ignore the alternative text part of an image declaration.</p> </div> </div> <h2 data-sourcepos="14003:1-14003:8" dir="auto"> -<a id="user-content-video" class="anchor" href="#video" aria-hidden="true"></a>Video</h2> +<a href="#video" aria-hidden="true" class="anchor" id="user-content-video"></a>Video</h2> <p data-sourcepos="14005:1-14006:109" dir="auto">See <a data-sourcepos="14006:1-14006:62" href="https://docs.gitlab.com/ee/user/markdown.html#videos" rel="nofollow noreferrer noopener" target="_blank">videos</a> in the GitLab Flavored Markdown documentation.</p> <p data-sourcepos="14008:1-14010:64" dir="auto">GLFM renders image elements as a video player as long as the resource’s file extension is @@ -12525,7 +12388,7 @@ Videos ignore the alternative text part of an image declaration.</p> </div> </div> <h2 data-sourcepos="14041:1-14041:41" dir="auto"> -<a id="user-content-markdown-preview-api-request-overrides" class="anchor" href="#markdown-preview-api-request-overrides" aria-hidden="true"></a>Markdown Preview API Request Overrides</h2> +<a href="#markdown-preview-api-request-overrides" aria-hidden="true" class="anchor" id="user-content-markdown-preview-api-request-overrides"></a>Markdown Preview API Request Overrides</h2> <p data-sourcepos="14043:1-14045:42" dir="auto">This section contains examples of all controllers which use <code data-sourcepos="14043:62-14043:76">PreviewMarkdown</code> module and use different <code data-sourcepos="14044:20-14044:42">markdown_context_params</code>. They exercise the various <code data-sourcepos="14044:73-14044:88">preview_markdown</code> endpoints via <code data-sourcepos="14045:16-14045:40">glfm_example_metadata.yml</code>.</p> @@ -12606,9 +12469,9 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h2 data-sourcepos="14136:1-14136:34" dir="auto"> -<a id="user-content-migrated-golden-master-examples" class="anchor" href="#migrated-golden-master-examples" aria-hidden="true"></a>Migrated golden master examples</h2> +<a href="#migrated-golden-master-examples" aria-hidden="true" class="anchor" id="user-content-migrated-golden-master-examples"></a>Migrated golden master examples</h2> <h3 data-sourcepos="14138:1-14138:30" dir="auto"> -<a id="user-content-attachment_image_for_group" class="anchor" href="#attachment_image_for_group" aria-hidden="true"></a>attachment_image_for_group</h3> +<a href="#attachment_image_for_group" aria-hidden="true" class="anchor" id="user-content-attachment_image_for_group"></a>attachment_image_for_group</h3> <div> <div><a href="#example-697">Example 697</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12621,7 +12484,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14152:1-14152:32" dir="auto"> -<a id="user-content-attachment_image_for_project" class="anchor" href="#attachment_image_for_project" aria-hidden="true"></a>attachment_image_for_project</h3> +<a href="#attachment_image_for_project" aria-hidden="true" class="anchor" id="user-content-attachment_image_for_project"></a>attachment_image_for_project</h3> <div> <div><a href="#example-698">Example 698</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12634,7 +12497,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14166:1-14166:37" dir="auto"> -<a id="user-content-attachment_image_for_project_wiki" class="anchor" href="#attachment_image_for_project_wiki" aria-hidden="true"></a>attachment_image_for_project_wiki</h3> +<a href="#attachment_image_for_project_wiki" aria-hidden="true" class="anchor" id="user-content-attachment_image_for_project_wiki"></a>attachment_image_for_project_wiki</h3> <div> <div><a href="#example-699">Example 699</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12647,7 +12510,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14180:1-14180:29" dir="auto"> -<a id="user-content-attachment_link_for_group" class="anchor" href="#attachment_link_for_group" aria-hidden="true"></a>attachment_link_for_group</h3> +<a href="#attachment_link_for_group" aria-hidden="true" class="anchor" id="user-content-attachment_link_for_group"></a>attachment_link_for_group</h3> <div> <div><a href="#example-700">Example 700</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12660,7 +12523,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14194:1-14194:31" dir="auto"> -<a id="user-content-attachment_link_for_project" class="anchor" href="#attachment_link_for_project" aria-hidden="true"></a>attachment_link_for_project</h3> +<a href="#attachment_link_for_project" aria-hidden="true" class="anchor" id="user-content-attachment_link_for_project"></a>attachment_link_for_project</h3> <div> <div><a href="#example-701">Example 701</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12673,7 +12536,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14208:1-14208:36" dir="auto"> -<a id="user-content-attachment_link_for_project_wiki" class="anchor" href="#attachment_link_for_project_wiki" aria-hidden="true"></a>attachment_link_for_project_wiki</h3> +<a href="#attachment_link_for_project_wiki" aria-hidden="true" class="anchor" id="user-content-attachment_link_for_project_wiki"></a>attachment_link_for_project_wiki</h3> <div> <div><a href="#example-702">Example 702</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12686,7 +12549,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14222:1-14222:34" dir="auto"> -<a id="user-content-attachment_link_for_group_wiki" class="anchor" href="#attachment_link_for_group_wiki" aria-hidden="true"></a>attachment_link_for_group_wiki</h3> +<a href="#attachment_link_for_group_wiki" aria-hidden="true" class="anchor" id="user-content-attachment_link_for_group_wiki"></a>attachment_link_for_group_wiki</h3> <div> <div><a href="#example-703">Example 703</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12699,7 +12562,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14236:1-14236:9" dir="auto"> -<a id="user-content-audio-1" class="anchor" href="#audio-1" aria-hidden="true"></a>audio</h3> +<a href="#audio-1" aria-hidden="true" class="anchor" id="user-content-audio-1"></a>audio</h3> <div> <div><a href="#example-704">Example 704</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12712,7 +12575,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14250:1-14250:28" dir="auto"> -<a id="user-content-audio_and_video_in_lists" class="anchor" href="#audio_and_video_in_lists" aria-hidden="true"></a>audio_and_video_in_lists</h3> +<a href="#audio_and_video_in_lists" aria-hidden="true" class="anchor" id="user-content-audio_and_video_in_lists"></a>audio_and_video_in_lists</h3> <div> <div><a href="#example-705">Example 705</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12733,7 +12596,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14272:1-14272:14" dir="auto"> -<a id="user-content-blockquote" class="anchor" href="#blockquote" aria-hidden="true"></a>blockquote</h3> +<a href="#blockquote" aria-hidden="true" class="anchor" id="user-content-blockquote"></a>blockquote</h3> <div> <div><a href="#example-706">Example 706</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12748,7 +12611,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14288:1-14288:8" dir="auto"> -<a id="user-content-bold" class="anchor" href="#bold" aria-hidden="true"></a>bold</h3> +<a href="#bold" aria-hidden="true" class="anchor" id="user-content-bold"></a>bold</h3> <div> <div><a href="#example-707">Example 707</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12761,7 +12624,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14302:1-14302:23" dir="auto"> -<a id="user-content-bullet_list_style_1" class="anchor" href="#bullet_list_style_1" aria-hidden="true"></a>bullet_list_style_1</h3> +<a href="#bullet_list_style_1" aria-hidden="true" class="anchor" id="user-content-bullet_list_style_1"></a>bullet_list_style_1</h3> <div> <div><a href="#example-708">Example 708</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12776,7 +12639,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14318:1-14318:23" dir="auto"> -<a id="user-content-bullet_list_style_2" class="anchor" href="#bullet_list_style_2" aria-hidden="true"></a>bullet_list_style_2</h3> +<a href="#bullet_list_style_2" aria-hidden="true" class="anchor" id="user-content-bullet_list_style_2"></a>bullet_list_style_2</h3> <div> <div><a href="#example-709">Example 709</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12791,7 +12654,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14334:1-14334:23" dir="auto"> -<a id="user-content-bullet_list_style_3" class="anchor" href="#bullet_list_style_3" aria-hidden="true"></a>bullet_list_style_3</h3> +<a href="#bullet_list_style_3" aria-hidden="true" class="anchor" id="user-content-bullet_list_style_3"></a>bullet_list_style_3</h3> <div> <div><a href="#example-710">Example 710</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12806,7 +12669,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14350:1-14350:25" dir="auto"> -<a id="user-content-code_block_javascript" class="anchor" href="#code_block_javascript" aria-hidden="true"></a>code_block_javascript</h3> +<a href="#code_block_javascript" aria-hidden="true" class="anchor" id="user-content-code_block_javascript"></a>code_block_javascript</h3> <div> <div><a href="#example-711">Example 711</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12821,7 +12684,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14366:1-14366:24" dir="auto"> -<a id="user-content-code_block_plaintext" class="anchor" href="#code_block_plaintext" aria-hidden="true"></a>code_block_plaintext</h3> +<a href="#code_block_plaintext" aria-hidden="true" class="anchor" id="user-content-code_block_plaintext"></a>code_block_plaintext</h3> <div> <div><a href="#example-712">Example 712</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12836,7 +12699,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14382:1-14382:22" dir="auto"> -<a id="user-content-code_block_unknown" class="anchor" href="#code_block_unknown" aria-hidden="true"></a>code_block_unknown</h3> +<a href="#code_block_unknown" aria-hidden="true" class="anchor" id="user-content-code_block_unknown"></a>code_block_unknown</h3> <div> <div><a href="#example-713">Example 713</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12851,7 +12714,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14398:1-14398:15" dir="auto"> -<a id="user-content-color_chips" class="anchor" href="#color_chips" aria-hidden="true"></a>color_chips</h3> +<a href="#color_chips" aria-hidden="true" class="anchor" id="user-content-color_chips"></a>color_chips</h3> <div> <div><a href="#example-714">Example 714</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12872,7 +12735,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14420:1-14420:20" dir="auto"> -<a id="user-content-description_list" class="anchor" href="#description_list" aria-hidden="true"></a>description_list</h3> +<a href="#description_list" aria-hidden="true" class="anchor" id="user-content-description_list"></a>description_list</h3> <div> <div><a href="#example-715">Example 715</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12900,7 +12763,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14449:1-14449:11" dir="auto"> -<a id="user-content-details" class="anchor" href="#details" aria-hidden="true"></a>details</h3> +<a href="#details" aria-hidden="true" class="anchor" id="user-content-details"></a>details</h3> <div> <div><a href="#example-716">Example 716</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12919,7 +12782,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14469:1-14469:25" dir="auto"> -<a id="user-content-diagram_kroki_nomnoml" class="anchor" href="#diagram_kroki_nomnoml" aria-hidden="true"></a>diagram_kroki_nomnoml</h3> +<a href="#diagram_kroki_nomnoml" aria-hidden="true" class="anchor" id="user-content-diagram_kroki_nomnoml"></a>diagram_kroki_nomnoml</h3> <div> <div><a href="#example-717">Example 717</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12942,7 +12805,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14493:1-14493:20" dir="auto"> -<a id="user-content-diagram_plantuml" class="anchor" href="#diagram_plantuml" aria-hidden="true"></a>diagram_plantuml</h3> +<a href="#diagram_plantuml" aria-hidden="true" class="anchor" id="user-content-diagram_plantuml"></a>diagram_plantuml</h3> <div> <div><a href="#example-718">Example 718</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12961,7 +12824,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14513:1-14513:28" dir="auto"> -<a id="user-content-diagram_plantuml_unicode" class="anchor" href="#diagram_plantuml_unicode" aria-hidden="true"></a>diagram_plantuml_unicode</h3> +<a href="#diagram_plantuml_unicode" aria-hidden="true" class="anchor" id="user-content-diagram_plantuml_unicode"></a>diagram_plantuml_unicode</h3> <div> <div><a href="#example-719">Example 719</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12976,7 +12839,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14529:1-14529:7" dir="auto"> -<a id="user-content-div" class="anchor" href="#div" aria-hidden="true"></a>div</h3> +<a href="#div" aria-hidden="true" class="anchor" id="user-content-div"></a>div</h3> <div> <div><a href="#example-720">Example 720</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -12994,7 +12857,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14548:1-14548:9" dir="auto"> -<a id="user-content-emoji" class="anchor" href="#emoji" aria-hidden="true"></a>emoji</h3> +<a href="#emoji" aria-hidden="true" class="anchor" id="user-content-emoji"></a>emoji</h3> <div> <div><a href="#example-721">Example 721</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13007,7 +12870,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14562:1-14562:12" dir="auto"> -<a id="user-content-emphasis" class="anchor" href="#emphasis" aria-hidden="true"></a>emphasis</h3> +<a href="#emphasis" aria-hidden="true" class="anchor" id="user-content-emphasis"></a>emphasis</h3> <div> <div><a href="#example-722">Example 722</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13020,7 +12883,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14576:1-14576:10" dir="auto"> -<a id="user-content-figure" class="anchor" href="#figure" aria-hidden="true"></a>figure</h3> +<a href="#figure" aria-hidden="true" class="anchor" id="user-content-figure"></a>figure</h3> <div> <div><a href="#example-723">Example 723</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13048,7 +12911,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14605:1-14605:13" dir="auto"> -<a id="user-content-footnotes" class="anchor" href="#footnotes" aria-hidden="true"></a>footnotes</h3> +<a href="#footnotes" aria-hidden="true" class="anchor" id="user-content-footnotes"></a>footnotes</h3> <div> <div><a href="#example-724">Example 724</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13067,7 +12930,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14625:1-14625:20" dir="auto"> -<a id="user-content-frontmatter_json" class="anchor" href="#frontmatter_json" aria-hidden="true"></a>frontmatter_json</h3> +<a href="#frontmatter_json" aria-hidden="true" class="anchor" id="user-content-frontmatter_json"></a>frontmatter_json</h3> <div> <div><a href="#example-725">Example 725</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13084,7 +12947,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14643:1-14643:20" dir="auto"> -<a id="user-content-frontmatter_toml" class="anchor" href="#frontmatter_toml" aria-hidden="true"></a>frontmatter_toml</h3> +<a href="#frontmatter_toml" aria-hidden="true" class="anchor" id="user-content-frontmatter_toml"></a>frontmatter_toml</h3> <div> <div><a href="#example-726">Example 726</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13099,7 +12962,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14659:1-14659:20" dir="auto"> -<a id="user-content-frontmatter_yaml" class="anchor" href="#frontmatter_yaml" aria-hidden="true"></a>frontmatter_yaml</h3> +<a href="#frontmatter_yaml" aria-hidden="true" class="anchor" id="user-content-frontmatter_yaml"></a>frontmatter_yaml</h3> <div> <div><a href="#example-727">Example 727</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13114,7 +12977,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14675:1-14675:14" dir="auto"> -<a id="user-content-hard_break" class="anchor" href="#hard_break" aria-hidden="true"></a>hard_break</h3> +<a href="#hard_break" aria-hidden="true" class="anchor" id="user-content-hard_break"></a>hard_break</h3> <div> <div><a href="#example-728">Example 728</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13128,7 +12991,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14690:1-14690:12" dir="auto"> -<a id="user-content-headings" class="anchor" href="#headings" aria-hidden="true"></a>headings</h3> +<a href="#headings" aria-hidden="true" class="anchor" id="user-content-headings"></a>headings</h3> <div> <div><a href="#example-729">Example 729</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13151,7 +13014,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14714:1-14714:19" dir="auto"> -<a id="user-content-horizontal_rule" class="anchor" href="#horizontal_rule" aria-hidden="true"></a>horizontal_rule</h3> +<a href="#horizontal_rule" aria-hidden="true" class="anchor" id="user-content-horizontal_rule"></a>horizontal_rule</h3> <div> <div><a href="#example-730">Example 730</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13164,7 +13027,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14728:1-14728:14" dir="auto"> -<a id="user-content-html_marks" class="anchor" href="#html_marks" aria-hidden="true"></a>html_marks</h3> +<a href="#html_marks" aria-hidden="true" class="anchor" id="user-content-html_marks"></a>html_marks</h3> <div> <div><a href="#example-731">Example 731</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13191,7 +13054,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14756:1-14756:9" dir="auto"> -<a id="user-content-image" class="anchor" href="#image" aria-hidden="true"></a>image</h3> +<a href="#image" aria-hidden="true" class="anchor" id="user-content-image"></a>image</h3> <div> <div><a href="#example-732">Example 732</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13204,7 +13067,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14770:1-14770:15" dir="auto"> -<a id="user-content-inline_code" class="anchor" href="#inline_code" aria-hidden="true"></a>inline_code</h3> +<a href="#inline_code" aria-hidden="true" class="anchor" id="user-content-inline_code"></a>inline_code</h3> <div> <div><a href="#example-733">Example 733</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13217,7 +13080,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14784:1-14784:15" dir="auto"> -<a id="user-content-inline_diff" class="anchor" href="#inline_diff" aria-hidden="true"></a>inline_diff</h3> +<a href="#inline_diff" aria-hidden="true" class="anchor" id="user-content-inline_diff"></a>inline_diff</h3> <div> <div><a href="#example-734">Example 734</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13231,7 +13094,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14799:1-14799:9" dir="auto"> -<a id="user-content-label" class="anchor" href="#label" aria-hidden="true"></a>label</h3> +<a href="#label" aria-hidden="true" class="anchor" id="user-content-label"></a>label</h3> <div> <div><a href="#example-735">Example 735</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13244,7 +13107,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14813:1-14813:8" dir="auto"> -<a id="user-content-link" class="anchor" href="#link" aria-hidden="true"></a>link</h3> +<a href="#link" aria-hidden="true" class="anchor" id="user-content-link"></a>link</h3> <div> <div><a href="#example-736">Example 736</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13257,7 +13120,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14827:1-14827:8" dir="auto"> -<a id="user-content-math" class="anchor" href="#math" aria-hidden="true"></a>math</h3> +<a href="#math" aria-hidden="true" class="anchor" id="user-content-math"></a>math</h3> <div> <div><a href="#example-737">Example 737</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13276,7 +13139,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14847:1-14847:16" dir="auto"> -<a id="user-content-ordered_list" class="anchor" href="#ordered_list" aria-hidden="true"></a>ordered_list</h3> +<a href="#ordered_list" aria-hidden="true" class="anchor" id="user-content-ordered_list"></a>ordered_list</h3> <div> <div><a href="#example-738">Example 738</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13291,7 +13154,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14863:1-14863:33" dir="auto"> -<a id="user-content-ordered_list_with_start_order" class="anchor" href="#ordered_list_with_start_order" aria-hidden="true"></a>ordered_list_with_start_order</h3> +<a href="#ordered_list_with_start_order" aria-hidden="true" class="anchor" id="user-content-ordered_list_with_start_order"></a>ordered_list_with_start_order</h3> <div> <div><a href="#example-739">Example 739</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13306,7 +13169,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14879:1-14879:21" dir="auto"> -<a id="user-content-ordered_task_list" class="anchor" href="#ordered_task_list" aria-hidden="true"></a>ordered_task_list</h3> +<a href="#ordered_task_list" aria-hidden="true" class="anchor" id="user-content-ordered_task_list"></a>ordered_task_list</h3> <div> <div><a href="#example-740">Example 740</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13324,7 +13187,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14898:1-14898:32" dir="auto"> -<a id="user-content-ordered_task_list_with_order" class="anchor" href="#ordered_task_list_with_order" aria-hidden="true"></a>ordered_task_list_with_order</h3> +<a href="#ordered_task_list_with_order" aria-hidden="true" class="anchor" id="user-content-ordered_task_list_with_order"></a>ordered_task_list_with_order</h3> <div> <div><a href="#example-741">Example 741</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13339,7 +13202,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14914:1-14914:30" dir="auto"> -<a id="user-content-reference_for_project_wiki" class="anchor" href="#reference_for_project_wiki" aria-hidden="true"></a>reference_for_project_wiki</h3> +<a href="#reference_for_project_wiki" aria-hidden="true" class="anchor" id="user-content-reference_for_project_wiki"></a>reference_for_project_wiki</h3> <div> <div><a href="#example-742">Example 742</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13352,7 +13215,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14928:1-14928:10" dir="auto"> -<a id="user-content-strike" class="anchor" href="#strike" aria-hidden="true"></a>strike</h3> +<a href="#strike" aria-hidden="true" class="anchor" id="user-content-strike"></a>strike</h3> <div> <div><a href="#example-743">Example 743</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13365,7 +13228,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14942:1-14942:9" dir="auto"> -<a id="user-content-table" class="anchor" href="#table" aria-hidden="true"></a>table</h3> +<a href="#table" aria-hidden="true" class="anchor" id="user-content-table"></a>table</h3> <div> <div><a href="#example-744">Example 744</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13383,7 +13246,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14961:1-14961:21" dir="auto"> -<a id="user-content-table_of_contents" class="anchor" href="#table_of_contents" aria-hidden="true"></a>table_of_contents</h3> +<a href="#table_of_contents" aria-hidden="true" class="anchor" id="user-content-table_of_contents"></a>table_of_contents</h3> <div> <div><a href="#example-745">Example 745</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13408,7 +13271,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="14987:1-14987:13" dir="auto"> -<a id="user-content-task_list" class="anchor" href="#task_list" aria-hidden="true"></a>task_list</h3> +<a href="#task_list" aria-hidden="true" class="anchor" id="user-content-task_list"></a>task_list</h3> <div> <div><a href="#example-746">Example 746</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13426,7 +13289,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="15006:1-15006:9" dir="auto"> -<a id="user-content-video-1" class="anchor" href="#video-1" aria-hidden="true"></a>video</h3> +<a href="#video-1" aria-hidden="true" class="anchor" id="user-content-video-1"></a>video</h3> <div> <div><a href="#example-747">Example 747</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13439,7 +13302,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h3 data-sourcepos="15020:1-15020:14" dir="auto"> -<a id="user-content-word_break" class="anchor" href="#word_break" aria-hidden="true"></a>word_break</h3> +<a href="#word_break" aria-hidden="true" class="anchor" id="user-content-word_break"></a>word_break</h3> <div> <div><a href="#example-748">Example 748</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13452,7 +13315,7 @@ also requires an EE license enabling the <code data-sourcepos="14122:43-14122:53 </div> </div> <h2 data-sourcepos="15034:1-15034:19" dir="auto"> -<a id="user-content-image-attributes" class="anchor" href="#image-attributes" aria-hidden="true"></a>Image Attributes</h2> +<a href="#image-attributes" aria-hidden="true" class="anchor" id="user-content-image-attributes"></a>Image Attributes</h2> <p data-sourcepos="15036:1-15038:46" dir="auto">See <a data-sourcepos="15037:1-15037:104" href="https://docs.gitlab.com/ee/user/markdown.html#change-the-image-dimensions" rel="nofollow noreferrer noopener" target="_blank">Change the image dimensions</a> in the GitLab Flavored Markdown documentation.</p> @@ -13520,7 +13383,7 @@ where it makes sense.</p> </div> </div> <h2 data-sourcepos="15113:1-15113:12" dir="auto"> -<a id="user-content-footnotes-1" class="anchor" href="#footnotes-1" aria-hidden="true"></a>Footnotes</h2> +<a href="#footnotes-1" aria-hidden="true" class="anchor" id="user-content-footnotes-1"></a>Footnotes</h2> <p data-sourcepos="15115:1-15116:143" dir="auto">See <a data-sourcepos="15116:1-15116:142" href="https://docs.gitlab.com/ee/user/markdown.html#footnotes" rel="nofollow noreferrer noopener" target="_blank">the footnotes section of the user-facing documentation for GitLab Flavored Markdown</a>.</p> <div> @@ -13555,12 +13418,12 @@ where it makes sense.</p> </div> </div> <h1 data-sourcepos="15150:1-15150:50" dir="auto"> -<a id="user-content-gfm-undocumented-extensions-and-more-robust-test" class="anchor" href="#gfm-undocumented-extensions-and-more-robust-test" aria-hidden="true"></a>GFM undocumented extensions and more robust test</h1> +<a href="#gfm-undocumented-extensions-and-more-robust-test" aria-hidden="true" class="anchor" id="user-content-gfm-undocumented-extensions-and-more-robust-test"></a>GFM undocumented extensions and more robust test</h1> <p data-sourcepos="15152:1-15154:16" dir="auto">This section contains tests borrowed from <a href="https://github.com/github/cmark-gfm/blob/master/test/extensions.txt" rel="nofollow noreferrer noopener" target="_blank">https://github.com/github/cmark-gfm/blob/master/test/extensions.txt</a>. It includes items not found in the official GFM specification, such as footnotes and additional tests for tables, task lists, etc.</p> <h2 data-sourcepos="15156:1-15156:12" dir="auto"> -<a id="user-content-footnotes-2" class="anchor" href="#footnotes-2" aria-hidden="true"></a>Footnotes</h2> +<a href="#footnotes-2" aria-hidden="true" class="anchor" id="user-content-footnotes-2"></a>Footnotes</h2> <div> <div><a href="#example-755">Example 755</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13625,7 +13488,7 @@ task lists, etc.</p> </div> </div> <h2 data-sourcepos="15222:1-15222:71" dir="auto"> -<a id="user-content-when-a-footnote-is-used-multiple-times-we-insert-multiple-backrefs" class="anchor" href="#when-a-footnote-is-used-multiple-times-we-insert-multiple-backrefs" aria-hidden="true"></a>When a footnote is used multiple times, we insert multiple backrefs.</h2> +<a href="#when-a-footnote-is-used-multiple-times-we-insert-multiple-backrefs" aria-hidden="true" class="anchor" id="user-content-when-a-footnote-is-used-multiple-times-we-insert-multiple-backrefs"></a>When a footnote is used multiple times, we insert multiple backrefs.</h2> <div> <div><a href="#example-756">Example 756</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13650,7 +13513,7 @@ task lists, etc.</p> </div> </div> <h2 data-sourcepos="15248:1-15248:45" dir="auto"> -<a id="user-content-footnote-reference-labels-are-href-escaped" class="anchor" href="#footnote-reference-labels-are-href-escaped" aria-hidden="true"></a>Footnote reference labels are href escaped</h2> +<a href="#footnote-reference-labels-are-href-escaped" aria-hidden="true" class="anchor" id="user-content-footnote-reference-labels-are-href-escaped"></a>Footnote reference labels are href escaped</h2> <div> <div><a href="#example-757">Example 757</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> @@ -13672,7 +13535,7 @@ task lists, etc.</p> </div> </div> <h2 data-sourcepos="15271:1-15271:10" dir="auto"> -<a id="user-content-interop" class="anchor" href="#interop" aria-hidden="true"></a>Interop</h2> +<a href="#interop" aria-hidden="true" class="anchor" id="user-content-interop"></a>Interop</h2> <p data-sourcepos="15273:1-15273:27" dir="auto">Autolink and strikethrough.</p> <div> <div><a href="#example-758">Example 758</a></div> @@ -13716,7 +13579,7 @@ task lists, etc.</p> </div> </div> <h2 data-sourcepos="15319:1-15319:13" dir="auto"> -<a id="user-content-task-lists" class="anchor" href="#task-lists" aria-hidden="true"></a>Task lists</h2> +<a href="#task-lists" aria-hidden="true" class="anchor" id="user-content-task-lists"></a>Task lists</h2> <div> <div><a href="#example-760">Example 760</a></div> <div class="gl-relative markdown-code-block js-markdown-code"> diff --git a/glfm_specification/output_spec/spec.html b/glfm_specification/output_spec/spec.html index 07af9a162d589..78e1e57213895 100644 --- a/glfm_specification/output_spec/spec.html +++ b/glfm_specification/output_spec/spec.html @@ -253,7 +253,7 @@ version: alpha ...</p> <h1 data-sourcepos="8:1-8:14" dir="auto"> -<a id="user-content-introduction" class="anchor" href="#introduction" aria-hidden="true"></a>Introduction</h1> +<a href="#introduction" aria-hidden="true" class="anchor" id="user-content-introduction"></a>Introduction</h1> <p data-sourcepos="10:1-10:284" dir="auto">GitLab Flavored Markdown (GLFM) extends the <a data-sourcepos="10:45-10:108" href="https://spec.commonmark.org/current/" rel="nofollow noreferrer noopener" target="_blank">CommonMark specification</a> and is considered a strict superset of CommonMark. It also incorporates the extensions defined by the <a data-sourcepos="10:212-10:283" href="https://github.github.com/gfm/" rel="nofollow noreferrer noopener" target="_blank">GitHub Flavored Markdown specification</a>.</p> <p data-sourcepos="12:1-12:433" dir="auto">This specification will define the various official extensions that comprise GLFM. These extensions are GitLab independent - they do not require a GitLab server for parsing or interaction. The intent is to provide a specification that can be implemented in standard markdown editors. This includes many of the features listed in <a data-sourcepos="12:330-12:432" href="https://docs.gitlab.com/ee/user/markdown.html" rel="nofollow noreferrer noopener" target="_blank">user-facing documentation for GitLab Flavored Markdown</a>.</p> <p data-sourcepos="14:1-14:69" dir="auto">The CommonMark and GitHub specifications will not be duplicated here.</p> @@ -269,14 +269,14 @@ for a complete list of all examples, which are a superset of examples from:</p> </ul> <h1 data-sourcepos="27:1-27:40" dir="auto"> -<a id="user-content-gitlab-official-specification-markdown" class="anchor" href="#gitlab-official-specification-markdown" aria-hidden="true"></a>GitLab Official Specification Markdown</h1> +<a href="#gitlab-official-specification-markdown" aria-hidden="true" class="anchor" id="user-content-gitlab-official-specification-markdown"></a>GitLab Official Specification Markdown</h1> <p data-sourcepos="29:1-31:104" dir="auto">Note: This specification is a work in progress. Only some of the official GLFM extensions are defined. We will continue to add any additional ones found in the <a data-sourcepos="31:1-31:103" href="https://docs.gitlab.com/ee/user/markdown.html" rel="nofollow noreferrer noopener" target="_blank">user-facing documentation for GitLab Flavored Markdown</a>.</p> <p data-sourcepos="33:1-34:69" dir="auto">There is currently only this single top-level heading, but the examples may be split into multiple top-level headings in the future.</p> <h2 data-sourcepos="36:1-36:18" dir="auto"> -<a id="user-content-task-list-items" class="anchor" href="#task-list-items" aria-hidden="true"></a>Task list items</h2> +<a href="#task-list-items" aria-hidden="true" class="anchor" id="user-content-task-list-items"></a>Task list items</h2> <p data-sourcepos="38:1-39:117" dir="auto">See <a data-sourcepos="39:1-39:70" href="https://docs.gitlab.com/ee/user/markdown.html#task-lists" rel="nofollow noreferrer noopener" target="_blank">Task lists</a> in the GitLab Flavored Markdown documentation.</p> <p data-sourcepos="41:1-44:39" dir="auto">Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above. @@ -370,7 +370,7 @@ loose text; it has strikethrough applied with CSS.</p> </div> </div> <h2 data-sourcepos="142:1-142:15" dir="auto"> -<a id="user-content-front-matter" class="anchor" href="#front-matter" aria-hidden="true"></a>Front matter</h2> +<a href="#front-matter" aria-hidden="true" class="anchor" id="user-content-front-matter"></a>Front matter</h2> <p data-sourcepos="144:1-145:121" dir="auto">See <a data-sourcepos="145:1-145:74" href="https://docs.gitlab.com/ee/user/markdown.html#front-matter" rel="nofollow noreferrer noopener" target="_blank">Front matter</a> in the GitLab Flavored Markdown documentation.</p> <p data-sourcepos="147:1-148:95" dir="auto">Front matter is metadata included at the beginning of a Markdown document, preceding the content. @@ -467,7 +467,7 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe </div> </div> <h2 data-sourcepos="251:1-251:20" dir="auto"> -<a id="user-content-table-of-contents" class="anchor" href="#table-of-contents" aria-hidden="true"></a>Table of contents</h2> +<a href="#table-of-contents" aria-hidden="true" class="anchor" id="user-content-table-of-contents"></a>Table of contents</h2> <p data-sourcepos="253:1-255:46" dir="auto">See <a data-sourcepos="254:1-254:84" href="https://docs.gitlab.com/ee/user/markdown.html#table-of-contents" rel="nofollow noreferrer noopener" target="_blank">table of contents</a> in the GitLab Flavored Markdown documentation.</p> diff --git a/lib/banzai/filter/markdown_engines/glfm_markdown.rb b/lib/banzai/filter/markdown_engines/glfm_markdown.rb index cd0dfa0ab0653..6743e3a90371d 100644 --- a/lib/banzai/filter/markdown_engines/glfm_markdown.rb +++ b/lib/banzai/filter/markdown_engines/glfm_markdown.rb @@ -15,6 +15,7 @@ class GlfmMarkdown < Base full_info_string: true, github_pre_lang: true, hardbreaks: false, + header_ids: Banzai::Renderer::USER_CONTENT_ID_PREFIX, math_code: true, math_dollars: true, multiline_block_quotes: true, @@ -35,7 +36,16 @@ def render(text) private def render_options - sourcepos_disabled? ? OPTIONS.merge(sourcepos: false) : OPTIONS + return OPTIONS unless sourcepos_disabled? || headers_disabled? + + OPTIONS.merge( + sourcepos: !sourcepos_disabled?, + header_ids: headers_disabled? ? nil : OPTIONS[:header_ids] + ) + end + + def headers_disabled? + context[:no_header_anchors] || Feature.disabled?(:native_header_anchors) end end end diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb index da976cf9ddc9e..577d02cc5594c 100644 --- a/lib/banzai/filter/sanitization_filter.rb +++ b/lib/banzai/filter/sanitization_filter.rb @@ -25,11 +25,16 @@ def customize_allowlist(allowlist) # Remove any `style` properties not required for table alignment allowlist[:transformers].push(self.class.remove_unsafe_table_style) - # Allow `id` in a and li elements for footnotes - # and remove any `id` properties not matching for footnotes + # Allow `id` in `a` and `li` elements for footnotes + # and `a` elements for header anchors. + # Remove any `id` properties not matching allowlist[:attributes]['a'].push('id') allowlist[:attributes]['li'] = %w[id] - allowlist[:transformers].push(self.class.remove_non_footnote_ids) + allowlist[:transformers].push(self.class.remove_id_attributes) + + # Remove any `class` property not required for `a` + allowlist[:attributes]['a'].push('class') + allowlist[:transformers].push(self.class.remove_unsafe_link_class) # Allow section elements with data-footnotes attribute allowlist[:elements].push('section') @@ -55,18 +60,38 @@ def remove_unsafe_table_style end end - def remove_non_footnote_ids + def remove_unsafe_link_class + lambda do |env| + node = env[:node] + + return unless node.name == 'a' + return unless node.has_attribute?('class') + + node.remove_attribute('class') if remove_link_class?(node) + end + end + + def remove_link_class?(node) + return if node['class'] == 'anchor' + + true + end + + def remove_id_attributes lambda do |env| node = env[:node] return unless node.name == 'a' || node.name == 'li' return unless node.has_attribute?('id') + # footnote ids should not be removed + return if node.name == 'li' && node['id'].start_with?(Banzai::Filter::FootnoteFilter::FOOTNOTE_ID_PREFIX) return if node.name == 'a' && node['id'].start_with?(Banzai::Filter::FootnoteFilter::FOOTNOTE_LINK_ID_PREFIX) - return if node.name == 'li' && - node['id'].start_with?(Banzai::Filter::FootnoteFilter::FOOTNOTE_ID_PREFIX) + # links with generated header anchors should not be removed + return if node.name == 'a' && node['class'] == 'anchor' && + node['id'].start_with?(Banzai::Renderer::USER_CONTENT_ID_PREFIX) node.remove_attribute('id') end diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb index 3e6505c82c424..357b880b74f14 100644 --- a/lib/banzai/filter/table_of_contents_filter.rb +++ b/lib/banzai/filter/table_of_contents_filter.rb @@ -2,6 +2,11 @@ require 'cgi/util' +# TODO: This is now a legacy filter, and is only used with the Ruby parser. +# The current markdown parser now properly handles adding anchors to headers. +# The Ruby parser is now only for benchmarking purposes. +# issue: https://gitlab.com/gitlab-org/gitlab/-/issues/454601 + # Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/table_of_contents.js module Banzai module Filter @@ -25,6 +30,7 @@ class TableOfContentsFilter < HTML::Pipeline::Filter XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(CSS).freeze def call + return doc if MarkdownFilter.glfm_markdown?(context) && Feature.enabled?(:native_header_anchors) return doc if context[:no_header_anchors] result[:toc] = +"" diff --git a/lib/banzai/filter/table_of_contents_tag_filter.rb b/lib/banzai/filter/table_of_contents_tag_filter.rb index 4e80b543e2d08..b7b6fded06a33 100644 --- a/lib/banzai/filter/table_of_contents_tag_filter.rb +++ b/lib/banzai/filter/table_of_contents_tag_filter.rb @@ -14,6 +14,9 @@ module Filter class TableOfContentsTagFilter < HTML::Pipeline::Filter TEXT_QUERY = %q(descendant-or-self::text()[ancestor::p and contains(translate(., 'TOC', 'toc'), 'toc')]) + HEADER_CSS = 'h1, h2, h3, h4, h5, h6' + HEADER_XPATH = Gitlab::Utils::Nokogiri.css_to_xpath(HEADER_CSS).freeze + def call return doc if context[:no_header_anchors] @@ -43,6 +46,8 @@ def process_toc_tag_em(node) # Replace an entire `[TOC]` node with the result generated by # TableOfContentsFilter def process_toc_tag(node) + build_toc if Feature.enabled?(:native_header_anchors) + # we still need to go one step up to also replace the surrounding <p></p> node.parent.replace(result[:toc].presence || '') end @@ -56,6 +61,85 @@ def toc_tag_em?(node) def toc_tag?(node) node.parent.text.casecmp?('[toc]') end + + def build_toc + return if result[:toc] + + result[:toc] = +"" + + header_root = current_header = HeaderNode.new + + doc.xpath(HEADER_XPATH).each do |node| + header_anchor = node.children.first + next unless header_anchor + next unless header_anchor.name == 'a' + next unless header_anchor[:class] == 'anchor' + + # remove leading anchor `#` so we can add it back later + href = header_anchor[:href].slice(1..) + current_header = HeaderNode.new(node: node, href: href, previous_header: current_header) + end + + push_toc(header_root.children, root: true) + end + + def push_toc(children, root: false) + return if children.empty? + + klass = ' class="section-nav"' if root + + result[:toc] << "<ul#{klass}>" + children.each { |child| push_anchor(child) } + result[:toc] << '</ul>' + end + + def push_anchor(header_node) + result[:toc] << %(<li><a href="##{header_node.href}">#{header_node.text}</a>) + push_toc(header_node.children) + result[:toc] << '</li>' + end + + class HeaderNode + attr_reader :node, :href, :parent, :children + + def initialize(node: nil, href: nil, previous_header: nil) + @node = node + @href = CGI.escape(href) if href + @children = [] + + @parent = find_parent(previous_header) + @parent.children.push(self) if @parent + end + + def level + return 0 unless node + + @level ||= node.name[1].to_i + end + + def text + return '' unless node + + @text ||= CGI.escapeHTML(node.text) + end + + private + + def find_parent(previous_header) + return unless previous_header + + if level == previous_header.level + parent = previous_header.parent + elsif level > previous_header.level + parent = previous_header + else + parent = previous_header + parent = parent.parent while parent.level >= level + end + + parent + end + end end end end diff --git a/lib/banzai/pipeline/description_pipeline.rb b/lib/banzai/pipeline/description_pipeline.rb index 8236c7092d27a..e7632c2c4a36a 100644 --- a/lib/banzai/pipeline/description_pipeline.rb +++ b/lib/banzai/pipeline/description_pipeline.rb @@ -9,8 +9,8 @@ class DescriptionPipeline < FullPipeline def self.transform_context(context) super(context).merge( - # SanitizationFilter - allowlist: ALLOWLIST + allowlist: ALLOWLIST, # SanitizationFilter + no_header_anchors: true # header elements not allowed ) end end diff --git a/spec/lib/banzai/filter/markdown_engines/glfm_markdown_spec.rb b/spec/lib/banzai/filter/markdown_engines/glfm_markdown_spec.rb index da58b824a0604..dfd7b8f0cce98 100644 --- a/spec/lib/banzai/filter/markdown_engines/glfm_markdown_spec.rb +++ b/spec/lib/banzai/filter/markdown_engines/glfm_markdown_spec.rb @@ -5,13 +5,43 @@ RSpec.describe Banzai::Filter::MarkdownEngines::GlfmMarkdown, feature_category: :team_planning do it 'defaults to generating sourcepos' do engine = described_class.new({}) + expected = <<~TEXT + <h1 data-sourcepos="1:1-1:4"><a href="#hi" aria-hidden="true" class="anchor" id="user-content-hi"></a>hi</h1> + TEXT - expect(engine.render('# hi')).to eq %(<h1 data-sourcepos="1:1-1:4">hi</h1>\n) + expect(engine.render('# hi')).to eq expected end it 'turns off sourcepos' do engine = described_class.new({ no_sourcepos: true }) + expected = <<~TEXT + <h1><a href="#hi" aria-hidden="true" class="anchor" id="user-content-hi"></a>hi</h1> + TEXT - expect(engine.render('# hi')).to eq %(<h1>hi</h1>\n) + expect(engine.render('# hi')).to eq expected + end + + it 'turns off header anchors' do + engine = described_class.new({ no_header_anchors: true, no_sourcepos: true }) + expected = <<~TEXT + <h1>hi</h1> + TEXT + + expect(engine.render('# hi')).to eq expected + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(native_header_anchors: false) + end + + it 'turns off header anchors' do + engine = described_class.new({ no_sourcepos: true }) + expected = <<~TEXT + <h1>hi</h1> + TEXT + + expect(engine.render('# hi')).to eq expected + end end end diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index fc3a50c5ce2aa..e40b65fce7bd9 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -192,5 +192,35 @@ end end end + + describe 'link anchors' do + it 'allows id property on anchor links' do + exp = %q(<a href="#this-is-a-header" class="anchor" id="user-content-this-is-a-header"></a>) + act = filter(exp) + + expect(act.to_html).to eq exp + end + + it 'removes id property for non-anchor links' do + exp = %q(<a href="#this-is-a-header"></a>) + act = filter(%q(<a href="#this-is-a-header" id="user-content-this-is-a-header"></a>)) + + expect(act.to_html).to eq exp + end + + it 'removes id property for non-user-content links' do + exp = %q(<a href="#this-is-a-header" class="anchor"></a>) + act = filter(%q(<a href="#this-is-a-header" class="anchor" id="this-is-a-header"></a>)) + + expect(act.to_html).to eq exp + end + + it 'removes class property for non-anchor links' do + exp = %q(<a href="#this-is-a-header"></a>) + act = filter(%q(<a href="#this-is-a-header" class="some-other-class anchor"></a>)) + + expect(act.to_html).to eq exp + end + end end end diff --git a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb index 31e6313215e31..bd55cce8d3f79 100644 --- a/spec/lib/banzai/filter/table_of_contents_filter_spec.rb +++ b/spec/lib/banzai/filter/table_of_contents_filter_spec.rb @@ -2,6 +2,10 @@ require 'spec_helper' +# TODO: This is now a legacy filter, and is only used with the Ruby parser. +# The current markdown parser now properly handles adding anchors to headers. +# The Ruby parser is now only for benchmarking purposes. +# issue: https://gitlab.com/gitlab-org/gitlab/-/issues/454601 RSpec.describe Banzai::Filter::TableOfContentsFilter, feature_category: :team_planning do include FilterSpecHelper @@ -9,20 +13,28 @@ def header(level, text) "<h#{level}>#{text}</h#{level}>\n" end + before do + stub_feature_flags(native_header_anchors: false) + end + + # TODO: enable when feature flag is removed + # let_it_be(:context) { { markdown_engine: Banzai::Filter::MarkdownFilter::CMARK_ENGINE } } + let_it_be(:context) { {} } + it 'does nothing when :no_header_anchors is truthy' do exp = act = header(1, 'Header') - expect(filter(act, no_header_anchors: 1).to_html).to eq exp + expect(filter(act, context.merge({ no_header_anchors: 1 })).to_html).to eq exp end it 'does nothing with empty headers' do exp = act = header(1, nil) - expect(filter(act).to_html).to eq exp + expect(filter(act, context).to_html).to eq exp end 1.upto(6) do |i| it "processes h#{i} elements" do html = header(i, "Header #{i}") - doc = filter(html) + doc = filter(html, context) expect(doc.css("h#{i} a").first.attr('id')).to eq "user-content-header-#{i}" end @@ -30,57 +42,57 @@ def header(level, text) describe 'anchor tag' do it 'has an `anchor` class' do - doc = filter(header(1, 'Header')) + doc = filter(header(1, 'Header'), context) expect(doc.css('h1 a').first.attr('class')).to eq 'anchor' end it 'has a namespaced id' do - doc = filter(header(1, 'Header')) + doc = filter(header(1, 'Header'), context) expect(doc.css('h1 a').first.attr('id')).to eq 'user-content-header' end it 'links to the non-namespaced id' do - doc = filter(header(1, 'Header')) + doc = filter(header(1, 'Header'), context) expect(doc.css('h1 a').first.attr('href')).to eq '#header' end describe 'generated IDs' do it 'translates spaces to dashes' do - doc = filter(header(1, 'This header has spaces in it')) + doc = filter(header(1, 'This header has spaces in it'), context) expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-has-spaces-in-it' end it 'squeezes multiple spaces and dashes' do - doc = filter(header(1, 'This---header is poorly-formatted')) + doc = filter(header(1, 'This---header is poorly-formatted'), context) expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-is-poorly-formatted' end it 'removes punctuation' do - doc = filter(header(1, "This, header! is, filled. with @ punctuation?")) + doc = filter(header(1, "This, header! is, filled. with @ punctuation?"), context) expect(doc.css('h1 a').first.attr('href')).to eq '#this-header-is-filled-with-punctuation' end it 'removes any leading or trailing spaces' do - doc = filter(header(1, " \r\n\tTitle with spaces\r\n\t ")) + doc = filter(header(1, " \r\n\tTitle with spaces\r\n\t "), context) expect(doc.css('h1 a').first.attr('href')).to eq '#title-with-spaces' end it 'appends a unique number to duplicates' do - doc = filter(header(1, 'One') + header(2, 'One')) + doc = filter(header(1, 'One') + header(2, 'One'), context) expect(doc.css('h1 a').first.attr('href')).to eq '#one' expect(doc.css('h2 a').first.attr('href')).to eq '#one-1' end it 'prepends a prefix to digits-only ids' do - doc = filter(header(1, "123") + header(2, "1.0")) + doc = filter(header(1, "123") + header(2, "1.0"), context) expect(doc.css('h1 a').first.attr('href')).to eq '#anchor-123' expect(doc.css('h2 a').first.attr('href')).to eq '#anchor-10' end it 'supports Unicode' do - doc = filter(header(1, '한글')) + doc = filter(header(1, '한글'), context) expect(doc.css('h1 a').first.attr('id')).to eq 'user-content-한글' # check that we encode the href to avoid issues with the # ExternalLinkFilter (see https://gitlab.com/gitlab-org/gitlab/issues/26210) @@ -88,7 +100,7 @@ def header(level, text) end it 'limits header href length with 255 characters' do - doc = filter(header(1, 'a' * 500)) + doc = filter(header(1, 'a' * 500), context) expect(doc.css('h1 a').first.attr('href')).to eq "##{'a' * 255}" end @@ -97,7 +109,7 @@ def header(level, text) describe 'result' do def result(html) - HTML::Pipeline.new([described_class]).call(html) + HTML::Pipeline.new([described_class], context).call(html) end let(:results) { result(header(1, 'Header 1') + header(2, 'Header 2')) } diff --git a/spec/lib/banzai/filter/table_of_contents_tag_filter_spec.rb b/spec/lib/banzai/filter/table_of_contents_tag_filter_spec.rb index 322225b38a902..fca4410165700 100644 --- a/spec/lib/banzai/filter/table_of_contents_tag_filter_spec.rb +++ b/spec/lib/banzai/filter/table_of_contents_tag_filter_spec.rb @@ -44,4 +44,86 @@ end end end + + describe 'structure of a toc' do + def header(level, text) + "#{'#' * level} #{text}\n" + end + + def result(html) + HTML::Pipeline.new([Banzai::Filter::MarkdownFilter, described_class]).call(html) + end + + let(:results) { result("[toc]\n\n#{header(1, 'Header 1')}#{header(2, 'Header 2')}") } + let(:doc) { results[:output] } + + it 'is contained within a `ul` element' do + expect(doc.children.first.name).to eq 'ul' + expect(doc.children.first.attr('class')).to eq 'section-nav' + end + + it 'contains an `li` element for each header' do + expect(doc.css('li').length).to eq 2 + + links = doc.css('li a') + + expect(links.first.attr('href')).to eq '#header-1' + expect(links.first.text).to eq 'Header 1' + expect(links.last.attr('href')).to eq '#header-2' + expect(links.last.text).to eq 'Header 2' + end + + context 'table of contents nesting' do + let(:results) do + result( + <<~MARKDOWN + [toc] + + #{header(1, 'Header 1')} + #{header(2, 'Header 1-1')} + #{header(3, 'Header 1-1-1')} + #{header(2, 'Header 1-2')} + #{header(1, 'Header 2')} + #{header(2, 'Header 2-1')} + #{header(2, 'Header 2-1b')} + MARKDOWN + ) + end + + it 'keeps list levels regarding header levels' do + items = doc.css('li') + + # Header 1 + expect(items[0].ancestors).to satisfy_none { |node| node.name == 'li' } + + # Header 1-1 + expect(items[1].ancestors).to include(items[0]) + + # Header 1-1-1 + expect(items[2].ancestors).to include(items[0], items[1]) + + # Header 1-2 + expect(items[3].ancestors).to include(items[0]) + expect(items[3].ancestors).not_to include(items[1]) + + # Header 2 + expect(items[4].ancestors).to satisfy_none { |node| node.name == 'li' } + + # Header 2-1 + expect(items[5].ancestors).to include(items[4]) + + # Header 2-1b + expect(items[6].ancestors).to include(items[4]) + end + end + + context 'header text contains escaped content' do + let(:content) { '<img src="x" onerror="alert(42)">' } + let(:results) { result(header(1, content)) } + + it 'outputs escaped content' do + expect(doc.inner_html).to include(content) + end + end + end end -- GitLab