MiniDocs/src/MiniDocs/Markdeep.class.st

595 lines
16 KiB
Smalltalk
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"
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',
'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 [
^ 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 |
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 [
^ config ifNil: [ config := Dictionary new]
]
{ #category : #accessing }
Markdeep >> config: aDictionary [
config := aDictionary
]
{ #category : #'instance creation' }
Markdeep >> contents [
| output |
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.
]
{ #category : #accessing }
Markdeep >> converPubPubFootnoteBetween: footnote and: nextFootnote in: footnotesArray [
| currentNoteIndex nextNoteIndex response noteLines |
currentNoteIndex := footnotesArray indexOf: '[^',footnote, ']: '.
nextNoteIndex := footnotesArray indexOf: '[^',nextFootnote, ']: '.
noteLines := footnotesArray copyFrom: currentNoteIndex to: nextNoteIndex - 1.
response := '' writeStream.
noteLines do: [:line |
line
ifNotEmpty: [ response nextPutAll: line, String lf ]
"ifEmpty: [ response nextPutAll: ' ' ]?"
].
response nextPutAll: String lf.
^ 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 [
^ navTop ifNil: [ navTop := '' ]
]
{ #category : #'as yet unclassified' }
Markdeep >> navTop: aString [
navTop:= aString.
]
{ #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 }
Markdeep >> pubPubFootnotesLinesRangeFor: contentSection [
| beginningLine endingLine |
beginningLine := contentSection lines size + 1.
contentSection lines doWithIndex: [:line :i |
((line includesSubstring: '::: {.pub-notes}') or: [line includesSubstring: '::: pub-notes'])
ifTrue: [ beginningLine := i ].
(i > beginningLine and: [ line beginsWith: ':::' ])
ifTrue: [
endingLine := i.
^ {beginningLine . endingLine}
]
]
]
{ #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
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
]
{ #category : #accessing }
Markdeep >> removeAlternativeImagesArray [
| replacements |
self body ifNil: [^ self].
replacements := self selectPubPubLinksWithSize: 3.
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|
licenseDiv := '
<div>
**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.
(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))).
response := '' writeStream.
newBodyLines do: [:line |
response nextPutAll: line, String lf
].
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 [
| 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 [
^ title ifNil: [ title := self metadata at: 'title' ifAbsent: [ '' ] ]
]
{ #category : #accessing }
Markdeep >> title: anObject [
title := anObject
]
{ #category : #accessing }
Markdeep >> tocStyle [
^ self options at: 'tocStyle' ifAbsent: [ 'short' ]
]
{ #category : #accessing }
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 ].
^ ''
]