@@ -168,11 +168,11 @@ export function renderTags (view) {
168
168
}
169
169
170
170
function slugifyWithUTF8 ( text ) {
171
- // remove html tags and trim spaces
171
+ // remove HTML tags and trim spaces
172
172
let newText = stripTags ( text . toString ( ) . trim ( ) )
173
- // replace all spaces in between to dashes
173
+ // replace space between words with dashes
174
174
newText = newText . replace ( / \s + / g, '-' )
175
- // slugify string to make it valid for attribute
175
+ // slugify string to make it valid as an attribute
176
176
newText = newText . replace ( / ( [ ! " # $ % & ' ( ) * + , . / : ; < = > ? @ [ \\ \] ^ ` { | } ~ ] ) / g, '' )
177
177
return newText
178
178
}
@@ -859,16 +859,44 @@ const anchorForId = id => {
859
859
return anchor
860
860
}
861
861
862
+ const createHeaderId = ( headerContent , headerIds = null ) => {
863
+ // to escape characters not allow in css and humanize
864
+ const slug = slugifyWithUTF8 ( headerContent )
865
+ let id
866
+ if ( window . linkifyHeaderStyle === 'keep-case' ) {
867
+ id = slug
868
+ } else if ( window . linkifyHeaderStyle === 'lower-case' ) {
869
+ // to make compatible with GitHub, GitLab, Pandoc and many more
870
+ id = slug . toLowerCase ( )
871
+ } else if ( window . linkifyHeaderStyle === 'gfm' ) {
872
+ // see GitHub implementation reference:
873
+ // https://gist.github.com/asabaylus/3071099#gistcomment-1593627
874
+ // it works like 'lower-case', but ...
875
+ const idBase = slug . toLowerCase ( )
876
+ id = idBase
877
+ if ( headerIds !== null ) {
878
+ // ... making sure the id is unique
879
+ let i = 1
880
+ while ( headerIds . has ( id ) ) {
881
+ id = idBase + '-' + i
882
+ i ++
883
+ }
884
+ headerIds . add ( id )
885
+ }
886
+ } else {
887
+ throw new Error ( 'Unknown linkifyHeaderStyle value "' + window . linkifyHeaderStyle + '"' )
888
+ }
889
+ return id
890
+ }
891
+
862
892
const linkifyAnchors = ( level , containingElement ) => {
863
893
const headers = containingElement . getElementsByTagName ( `h${ level } ` )
864
894
865
895
for ( let i = 0 , l = headers . length ; i < l ; i ++ ) {
866
896
const header = headers [ i ]
867
897
if ( header . getElementsByClassName ( 'anchor' ) . length === 0 ) {
868
898
if ( typeof header . id === 'undefined' || header . id === '' ) {
869
- // to escape characters not allow in css and humanize
870
- const id = slugifyWithUTF8 ( getHeaderContent ( header ) )
871
- header . id = id
899
+ header . id = createHeaderId ( getHeaderContent ( header ) )
872
900
}
873
901
if ( ! ( typeof header . id === 'undefined' || header . id === '' ) ) {
874
902
header . insertBefore ( anchorForId ( header . id ) , header . firstChild )
@@ -894,20 +922,43 @@ function getHeaderContent (header) {
894
922
return headerHTML [ 0 ] . innerHTML
895
923
}
896
924
925
+ function changeHeaderId ( $header , id , newId ) {
926
+ $header . attr ( 'id' , newId )
927
+ const $headerLink = $header . find ( `> a.anchor[href="#${ id } "]` )
928
+ $headerLink . attr ( 'href' , `#${ newId } ` )
929
+ $headerLink . attr ( 'title' , newId )
930
+ }
931
+
897
932
export function deduplicatedHeaderId ( view ) {
933
+ // headers contained in the last change
898
934
const headers = view . find ( ':header.raw' ) . removeClass ( 'raw' ) . toArray ( )
899
- for ( let i = 0 ; i < headers . length ; i ++ ) {
900
- const id = $ ( headers [ i ] ) . attr ( 'id' )
901
- if ( ! id ) continue
902
- const duplicatedHeaders = view . find ( `:header[id="${ id } "]` ) . toArray ( )
903
- for ( let j = 0 ; j < duplicatedHeaders . length ; j ++ ) {
904
- if ( duplicatedHeaders [ j ] !== headers [ i ] ) {
905
- const newId = id + j
906
- const $duplicatedHeader = $ ( duplicatedHeaders [ j ] )
907
- $duplicatedHeader . attr ( 'id' , newId )
908
- const $headerLink = $duplicatedHeader . find ( `> a.anchor[href="#${ id } "]` )
909
- $headerLink . attr ( 'href' , `#${ newId } ` )
910
- $headerLink . attr ( 'title' , newId )
935
+ if ( headers . length === 0 ) {
936
+ return
937
+ }
938
+ if ( window . linkifyHeaderStyle === 'gfm' ) {
939
+ // consistent with GitHub, GitLab, Pandoc & co.
940
+ // all headers contained in the document, in order of appearance
941
+ const allHeaders = view . find ( `:header` ) . toArray ( )
942
+ // list of finaly assigned header IDs
943
+ const headerIds = new Set ( )
944
+ for ( let j = 0 ; j < allHeaders . length ; j ++ ) {
945
+ const $header = $ ( allHeaders [ j ] )
946
+ const id = $header . attr ( 'id' )
947
+ const newId = createHeaderId ( getHeaderContent ( $header ) , headerIds )
948
+ changeHeaderId ( $header , id , newId )
949
+ }
950
+ } else {
951
+ // the legacy way
952
+ for ( let i = 0 ; i < headers . length ; i ++ ) {
953
+ const id = $ ( headers [ i ] ) . attr ( 'id' )
954
+ if ( ! id ) continue
955
+ const duplicatedHeaders = view . find ( `:header[id="${ id } "]` ) . toArray ( )
956
+ for ( let j = 0 ; j < duplicatedHeaders . length ; j ++ ) {
957
+ if ( duplicatedHeaders [ j ] !== headers [ i ] ) {
958
+ const newId = id + j
959
+ const $header = $ ( duplicatedHeaders [ j ] )
960
+ changeHeaderId ( $header , id , newId )
961
+ }
911
962
}
912
963
}
913
964
}
0 commit comments