MiniDocs/src/MiniDocs/Markdeep.class.st

595 lines
16 KiB
Smalltalk
Raw Normal View History

"
I model a Mardeep file as described in https://casual-effects.com/markdeep/
"
Class {
#name : #Markdeep,
#superclass : #Markdown,
#instVars : [
'title',
'comments',
'tail',
'language',
'config',
'head',
2022-04-05 14:43:37 +00:00
'navTop',
'options'
],
#category : #'MiniDocs-Core'
}
{ #category : #'as yet unclassified' }
Markdeep class >> fromMarkdownFile: aFileReference [
^ self new fromMarkdownFile: aFileReference.
]
{ #category : #accessing }
Markdeep class >> fromPubPubTOC: orderedDictionary folder: folder index: ordinalPossitive [
| contentSection testFile |
contentSection := orderedDictionary associations at: ordinalPossitive.
testFile := folder / (contentSection key,'--', contentSection value),'md'.
^ self new fromMarkdownFile: testFile.
]
{ #category : #'instance creation' }
Markdeep >> authors [
self metadata at: 'authors' ifPresent: [:k | ^ '**', k, '**' ].
^ ''.
]
{ #category : #'instance creation' }
Markdeep >> authorsString [
self authors
ifNil: [ ^ '' ] ifNotNil: [ ^ ' ', self authors ]
]
{ #category : #accessing }
Markdeep >> body [
2023-03-06 02:25:06 +00:00
^ body ifNil: [^ '' ]
]
{ #category : #accessing }
Markdeep >> body: anObject [
body := anObject
]
{ #category : #accessing }
Markdeep >> bodyReplaceAll: original with: replacement [
self body: (self body copyReplaceAll: original with: replacement)
]
{ #category : #accessing }
Markdeep >> commentPubPubDelimiters [
| commented openners |
2023-05-23 13:08:35 +00:00
openners := #('::: {.pub-body-component}' '::: pub-body-component' '::: {.editor .Prosemirror}' '::: {.pub-notes}').
commented := self body.
openners do: [:openner |
commented := commented copyReplaceAll: openner with: '<!--@div-open ', openner, '-->'
].
commented := commented
copyReplaceAll: ':::
' with: '<!--@div-close ::: -->
'.
self body: commented
]
{ #category : #accessing }
Markdeep >> comments [
^ comments ifNil: [ ^ comments := true ]
]
{ #category : #accessing }
Markdeep >> comments: aBoolean [
"I tell if comments are enabled by default or not."
comments := aBoolean
]
{ #category : #utilities }
Markdeep >> commentsProvider [
"I return the url of the default service that provides annotation support.
I am used to add such support in the contents of the Markdeep page."
^ 'https://hypothes.is'
]
{ #category : #utilities }
Markdeep >> commentsProviderStrings [
"I associate a comments service provider with the string that is required to be added
to the document to enable such provider."
| providers |
providers := Dictionary new.
providers at: 'https://hypothes.is' put: '<!-- Hypothesis -->
<script src="https://hypothes.is/embed.js" async></script>'.
^ providers
]
{ #category : #utilities }
Markdeep >> commentsSupport [
"I enable comments of the page."
self comments ifFalse: [ ^ self ].
^ self commentsProviderStrings at: self commentsProvider
]
{ #category : #accessing }
Markdeep >> config [
2022-09-07 12:58:06 +00:00
^ config ifNil: [ config := Dictionary new]
]
{ #category : #accessing }
Markdeep >> config: aDictionary [
config := aDictionary
]
{ #category : #'instance creation' }
Markdeep >> contents [
| output |
2022-09-07 12:58:06 +00:00
self title ifNil: [ ^ self body ].
output := '' writeStream.
output
nextPutAll: self headContents; lf; lf;
nextPutAll: ' **', self title trimmed accentedCharactersCorrection, '**'; lf;
nextPutAll: self authorsString ; lf;
nextPutAll: '', self version; lf;
nextPutAll: self navTop; lf; lf;
nextPutAll: self body; lf; lf;
nextPutAll: self tail; lf; lf; lf; lf;
nextPutAll: self commentsSupport.
^ output contents.
]
2023-05-10 16:31:54 +00:00
{ #category : #accessing }
Markdeep >> converPubPubFootnoteBetween: footnote and: nextFootnote in: footnotesArray [
| currentNoteIndex nextNoteIndex response noteLines |
2023-05-10 16:53:54 +00:00
currentNoteIndex := footnotesArray indexOf: '[^',footnote, ']: '.
nextNoteIndex := footnotesArray indexOf: '[^',nextFootnote, ']: '.
noteLines := footnotesArray copyFrom: currentNoteIndex to: nextNoteIndex - 1.
2023-05-10 16:31:54 +00:00
response := '' writeStream.
noteLines do: [:line |
line
2023-05-10 16:53:54 +00:00
ifNotEmpty: [ response nextPutAll: line, String lf ]
"ifEmpty: [ response nextPutAll: ' ' ]?"
2023-05-10 16:31:54 +00:00
].
2023-05-13 00:21:04 +00:00
response nextPutAll: String lf.
2023-05-10 16:31:54 +00:00
^ response contents
]
{ #category : #utilities }
Markdeep >> fontAwesomeHeader [
"I enable the font awesome support in the document header"
^ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">'
]
{ #category : #'instance creation' }
Markdeep >> fromMarkdownFile: aFileReference [
"I create a Markdeep document from a given Markdown file."
self processMarkdownFor: aFileReference.
^ self.
]
{ #category : #accessing }
Markdeep >> fromPubPubToMarkdeep [
self
removeAutoGeneratedFileNotice;
removeCCByLicenseDiv;
commentPubPubDelimiters;
replaceEscapedCharacters;
renamePubPubFootnotes;
removeAlternativeImagesArray
]
{ #category : #accessing }
Markdeep >> gtTextFor: aView [
<gtView>
^ aView textEditor
title: 'Text';
text: [ self contents ]
]
{ #category : #accessing }
Markdeep >> head [
^ head ifNil: [ head := OrderedCollection new.
head add: self fontAwesomeHeader; yourself ]
]
{ #category : #accessing }
Markdeep >> head: anOrderedCollection [
head := anOrderedCollection
]
{ #category : #'instance creation' }
Markdeep >> headContents [
^ String streamContents: [ :stream |
stream
nextPutAll: '<head>';
nextPut: Character lf.
self head do: [ :line |
stream
nextPutAll: ' ';
nextPutAll: line;
nextPut: Character lf
].
stream
nextPutAll: '</head>';
nextPut: Character lf.
].
]
{ #category : #accessing }
Markdeep >> language [
^ language
]
{ #category : #accessing }
Markdeep >> language: anObject [
language := anObject
]
{ #category : #accessing }
Markdeep >> markdeepScriptTag [
^ '<script src="markdeep.min.js" charset="utf-8"></script>
<script
src="https://casual-effects.com/markdeep/latest/markdeep.min.js?"
charset="utf-8">
</script>'
]
{ #category : #accessing }
Markdeep >> markdownFile [
self file ifNil: [
self file: FileLocator temp / ('untitled--', NanoID generate, '.md.html') ].
^ (self file fullName withoutSuffix: '.html') asFileReference.
]
{ #category : #accessing }
Markdeep >> markdownFile: aFileReference [
"Where the Mardown file associated with me is stored. Used for sync. and import/export purposes."
self config at: 'markdownFile' put: aFileReference
]
{ #category : #'instance creation' }
Markdeep >> metadata [
^ metadata ifNil: [ metadata := OrderedDictionary new ]
]
{ #category : #accessing }
Markdeep >> metadata: anOrderedDictionary [
metadata := anOrderedDictionary
]
{ #category : #utilities }
Markdeep >> metadataFromXML: aXMLDocument [
| metaDict |
metaDict := OrderedDictionary new.
(aXMLDocument xpath: '//meta') do: [ :each |
metaDict at: (each @ 'name') stringValue put: (each @ 'content') stringValue ].
^ metaDict
]
{ #category : #'instance creation' }
Markdeep >> navTop [
2022-10-17 02:42:02 +00:00
^ navTop ifNil: [ navTop := '' ]
]
{ #category : #'as yet unclassified' }
Markdeep >> navTop: aString [
navTop:= aString.
]
2022-04-05 14:43:37 +00:00
{ #category : #accessing }
Markdeep >> options [
^ options ifNil: [
"Setting defaults accoding to: https://casual-effects.com/markdeep/#api"
options := Dictionary new
at: 'mode' put: nil;
at: 'lang' put: nil;
at: 'tocStyle' put: 'auto';
at: 'autoLinkImages' put: true;
yourself
]
]
{ #category : #printing }
Markdeep >> printOn: aStream [
super printOn: aStream.
aStream
nextPutAll: '( ', self title accentedCharactersCorrection, ' )'
]
{ #category : #'instance creation' }
Markdeep >> processMarkdownFor: aFileReference [
"comment stating purpose of message"
| markdownContent |
self file: aFileReference, 'html'.
markdownContent := Markdown fromFile: aFileReference.
self metadata: markdownContent yamlMetadata.
self body: (markdownContent commentYAMLMetadata contents).
]
{ #category : #accessing }
Markdeep >> pubPubFootnoteMetadataFromString: string [
| sanitized footnoteData altLine altString id |
(string lines size <= 1) ifTrue: [ ^ nil ].
sanitized := '' writeStream.
altString := string copyReplaceAll: '.footnote' with: ''.
altString := altString copyReplaceAll: ' node-type='
with: '
node-type= '.
altString := altString copyReplaceAll: ' data-value=' with: '
data-value='.
altString := altString copyReplaceAll: ' date-structured-value=' with: '
date-structured-value= '.
altString lines allButFirstDo: [:line |
(line beginsWith: '>')
ifTrue: [ altLine := line allButFirst ]
ifFalse: [ altLine := line ].
sanitized
nextPutAll: altLine trimBoth;
nextPutAll: String lf
].
sanitized := sanitized contents.
sanitized := sanitized copyReplaceAll: 'type=' with: 'type: '.
sanitized := sanitized copyReplaceAll: 'value=' with: 'value: '.
id := (altString lines first) allButFirst trimmed.
footnoteData := { 'id' -> id } asDictionary.
footnoteData addAll: (MiniDocs yamlToJson: sanitized trimmed).
^ footnoteData
]
{ #category : #accessing }
Markdeep >> pubPubFootnoteRawLinks [
^ self selectPubPubLinksWithSize: 2
]
{ #category : #accessing }
2023-05-13 00:21:04 +00:00
Markdeep >> pubPubFootnotesLinesRangeFor: contentSection [
| beginningLine endingLine |
2023-05-13 00:21:04 +00:00
beginningLine := contentSection lines size + 1.
contentSection lines doWithIndex: [:line :i |
2023-05-23 13:08:35 +00:00
((line includesSubstring: '::: {.pub-notes}') or: [line includesSubstring: '::: pub-notes'])
ifTrue: [ beginningLine := i ].
(i > beginningLine and: [ line beginsWith: ':::' ])
ifTrue: [
endingLine := i.
^ {beginningLine . endingLine}
]
]
]
2023-05-13 00:21:04 +00:00
{ #category : #accessing }
Markdeep >> pubPubFootnotesLinesRangeForBody [
^ self pubPubFootnotesLinesRangeFor: self body
]
{ #category : #accessing }
Markdeep >> pubPubFootnotesLinesRangeForContents [
^ self pubPubFootnotesLinesRangeFor: self contents
]
{ #category : #accessing }
Markdeep >> pubPubFootnotesText [
| footnotesLines output |
footnotesLines := self contents lines
2023-05-13 00:21:04 +00:00
copyFrom: self pubPubFootnotesLinesRangeForContents first + 3
to: self pubPubFootnotesLinesRangeForContents second - 1.
output := '' writeStream.
footnotesLines do: [:line |
output
nextPutAll: line;
nextPutAll: String crlf.
].
^ output contents allButLast
]
{ #category : #accessing }
Markdeep >> pubPubImageLinks [
^ self selectPubPubLinksWithSize: 3
]
{ #category : #accessing }
Markdeep >> pubPubImagesToMarkdeep [
| sanitized parsedLinks |
parsedLinks := self pubPubImageLinks.
parsedLinks ifEmpty: [ ^self ].
sanitized := self body.
parsedLinks do: [:link |
sanitized := sanitized copyReplaceAll: '{', link third, '}' with: ''
].
self body: sanitized
]
{ #category : #accessing }
Markdeep >> pubPubRawLinks [
| parser |
parser := PubPubGrammar2 new document.
^ (parser parse: self body)
]
{ #category : #accessing }
Markdeep >> reformatPubPubFootnotes [
| footnotesLines footnotesIDs toReplace response |
(self = self pubPubFootnotesLinesRangeForContents)
ifTrue: [^self].
footnotesLines := self contents lines
copyFrom: self pubPubFootnotesLinesRangeForContents first
to: self pubPubFootnotesLinesRangeForContents second.
footnotesIDs := self replacePubPubFootnotesIdentifiers.
toReplace := footnotesLines select: [:line |
(line includesSubstring: ' [[]{.pub-note-content-component}]{#fn-')
].
toReplace doWithIndex: [:replacement :i | | index |
index := footnotesLines indexOf: replacement.
footnotesLines at: index put: '[^', (footnotesIDs at: i),']: '
].
response := '' writeStream.
footnotesIDs allButLast doWithIndex: [:footnote :i |
response
nextPutAll:
(self
converPubPubFootnoteBetween: footnote
and: (footnotesIDs at: i + 1)
in: footnotesLines)
].
^ response contents
]
2023-05-09 19:45:23 +00:00
{ #category : #accessing }
Markdeep >> removeAlternativeImagesArray [
| replacements |
2023-05-09 19:45:23 +00:00
self body ifNil: [^ self].
replacements := self selectPubPubLinksWithSize: 3.
2023-05-09 19:45:23 +00:00
replacements ifEmpty: [^self].
replacements do: [:replacement |
self body:
(self body copyReplaceAll: replacement third with: '' )
].
self body: (self body copyReplaceAll: '{srcset=}' with: '').
]
{ #category : #accessing }
Markdeep >> removeAutoGeneratedFileNotice [
| autoGeneratedNotice |
autoGeneratedNotice := '**Notice:** This file is an auto-generated download and, as such, might
include minor display or rendering errors. For the version of record,
please visit the HTML version or download the PDF.
------------------------------------------------------------------------'.
self body: (self body copyReplaceAll: autoGeneratedNotice with: '')
]
{ #category : #accessing }
Markdeep >> removeCCByLicenseDiv [
| licenseDiv|
2023-05-23 13:08:35 +00:00
licenseDiv := '
<div>
2023-05-09 19:45:23 +00:00
**License:** [Creative Commons Attribution 4.0 International License
(CC-BY 4.0)](https://creativecommons.org/licenses/by/4.0/)
</div>'.
self body: (self body copyReplaceAll: licenseDiv with: '')
]
{ #category : #accessing }
Markdeep >> renamePubPubFootnotes [
| reformated bodyLines beforeFootnotes afterFootnotesRaw afterFootnotes newBodyLines response |
reformated := self reformatPubPubFootnotes.
2023-05-16 16:33:35 +00:00
(self pubPubFootnotesLinesRangeForBody class = Markdeep) ifTrue: [ ^self ].
bodyLines := self body lines.
beforeFootnotes := bodyLines copyFrom: 1 to: self pubPubFootnotesLinesRangeForBody first .
afterFootnotesRaw := bodyLines copyFrom: self pubPubFootnotesLinesRangeForBody second to: bodyLines size.
afterFootnotes := OrderedCollection new.
afterFootnotesRaw do:[:line |
(line beginsWith: ':::')
ifTrue: [
afterFootnotes
add: (line copyReplaceAll: ':::' with: '<!--@div-closer ::: -->').
]
].
newBodyLines :=
(beforeFootnotes copyWithAll:
(#('# Footnotes' '')
copyWithAll:(reformated lines
copyWithAll: afterFootnotes))).
2023-05-13 00:21:04 +00:00
response := '' writeStream.
newBodyLines do: [:line |
response nextPutAll: line, String lf
2023-05-10 16:31:54 +00:00
].
self body: response contents.
]
{ #category : #accessing }
Markdeep >> replaceBackslashBreaklines [
self bodyReplaceAll: '\
' with: '<br>
'
]
{ #category : #accessing }
Markdeep >> replaceEscapedCharacters [
self
title: (self title copyReplaceAll: '\#' with: '#');
body: (self body copyReplaceAll: '\#' with: '#');
body: (self body copyReplaceAll: '\[' with: '[');
body: (self body copyReplaceAll: '\]' with: ']');
body: (self body copyReplaceAll: '\*' with: '*')
]
{ #category : #accessing }
Markdeep >> replacePubPubFootnotesIdentifiers [
2023-05-10 16:53:54 +00:00
| footnotes sanitized parsedLinks linkIdentifiers |
footnotes := OrderedDictionary new.
parsedLinks := self pubPubFootnoteRawLinks.
parsedLinks ifEmpty: [ ^self ].
sanitized := self body.
linkIdentifiers := OrderedCollection new.
parsedLinks do: [:link | | id currentLinkText |
id := (link second splitOn: '.footnote') first trimmed.
linkIdentifiers add: id.
currentLinkText := '[', link first, ']{#',link second,'}'.
sanitized := sanitized copyReplaceAll: currentLinkText with: '[^', id, ']'
].
self body: sanitized.
^ linkIdentifiers
]
{ #category : #accessing }
Markdeep >> selectPubPubLinksWithSize: naturalNumber [
^ self pubPubRawLinks select: [ :each | each size = naturalNumber ]
]
{ #category : #accessing }
Markdeep >> tail [
"I enable the document tail, which, in turn, enables a Markdeep document"
| output |
output := '' writeStream.
output
nextPutAll: '<!-- Markdeep: -->'; lf; lf;
nextPutAll: '<style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style>'; lf;
nextPutAll: '<script>window.markdeepOptions = {tocStyle: "', self tocStyle,'"}</script>'; lf;
nextPutAll: self markdeepScriptTag; lf;
nextPutAll: '<!--<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>-->'.
^ output contents
]
{ #category : #accessing }
Markdeep >> tail: anObject [
tail := anObject
]
{ #category : #accessing }
Markdeep >> title [
2023-03-06 02:25:06 +00:00
^ title ifNil: [ title := self metadata at: 'title' ifAbsent: [ '' ] ]
]
{ #category : #accessing }
Markdeep >> title: anObject [
title := anObject
]
{ #category : #accessing }
Markdeep >> tocStyle [
2022-04-05 14:43:37 +00:00
^ self options at: 'tocStyle' ifAbsent: [ 'short' ]
]
{ #category : #accessing }
2022-04-05 14:43:37 +00:00
Markdeep >> tocStyle: aString [
"A string can be: 'auto' 'none' 'short' 'medium' or 'long'"
self options at: 'tocStyle' put: aString
]
{ #category : #'instance creation' }
Markdeep >> version [
self metadata at: 'version' ifPresent: [:value | ^ 'v',value ].
^ ''
]