@@ -168,11 +168,11 @@ export function renderTags (view) {
168168}
169169
170170function slugifyWithUTF8 ( text ) {
171- // remove html tags and trim spaces
171+ // remove HTML tags and trim spaces
172172 let newText = stripTags ( text . toString ( ) . trim ( ) )
173- // replace all spaces in between to dashes
173+ // replace space between words with dashes
174174 newText = newText . replace ( / \s + / g, '-' )
175- // slugify string to make it valid for attribute
175+ // slugify string to make it valid as an attribute
176176 newText = newText . replace ( / ( [ ! " # $ % & ' ( ) * + , . / : ; < = > ? @ [ \\ \] ^ ` { | } ~ ] ) / g, '' )
177177 return newText
178178}
@@ -859,16 +859,44 @@ const anchorForId = id => {
859859 return anchor
860860}
861861
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+
862892const linkifyAnchors = ( level , containingElement ) => {
863893 const headers = containingElement . getElementsByTagName ( `h${ level } ` )
864894
865895 for ( let i = 0 , l = headers . length ; i < l ; i ++ ) {
866896 const header = headers [ i ]
867897 if ( header . getElementsByClassName ( 'anchor' ) . length === 0 ) {
868898 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 ) )
872900 }
873901 if ( ! ( typeof header . id === 'undefined' || header . id === '' ) ) {
874902 header . insertBefore ( anchorForId ( header . id ) , header . firstChild )
@@ -894,20 +922,43 @@ function getHeaderContent (header) {
894922 return headerHTML [ 0 ] . innerHTML
895923}
896924
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+
897932export function deduplicatedHeaderId ( view ) {
933+ // headers contained in the last change
898934 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+ }
911962 }
912963 }
913964 }
0 commit comments