479 lines
12 KiB
Smalltalk
479 lines
12 KiB
Smalltalk
"
|
|
I model a Mardeep file as described in https://casual-effects.com/markdeep/
|
|
"
|
|
Class {
|
|
#name : #Markdeep,
|
|
#superclass : #Object,
|
|
#instVars : [
|
|
'title',
|
|
'body',
|
|
'comments',
|
|
'tail',
|
|
'language',
|
|
'config',
|
|
'metadata',
|
|
'head',
|
|
'navTop',
|
|
'options'
|
|
],
|
|
#category : #MiniDocs
|
|
}
|
|
|
|
{ #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
|
|
]
|
|
|
|
{ #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}' '::: {.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, '**'; 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 : #persistence }
|
|
Markdeep >> exportAsFile [
|
|
| newFile |
|
|
self markdownFile ifNil: [ self inform: 'Define an input Markdown file or use #exportAsFileOn: instead.' ].
|
|
newFile := (self markdownFile file fullName, '.html') asFileReference.
|
|
^ self notifyExportAsFileOn: newFile.
|
|
]
|
|
|
|
{ #category : #persistence }
|
|
Markdeep >> exportAsFileOn: aFileReference [
|
|
aFileReference ensureDelete.
|
|
aFileReference exists ifFalse: [ aFileReference ensureCreateFile ].
|
|
aFileReference writeStreamDo: [ :stream |
|
|
stream nextPutAll: self 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 >> 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 [
|
|
^ Markdown new fromFile: (self config at: 'markdownFile')
|
|
]
|
|
|
|
{ #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 : #persistence }
|
|
Markdeep >> notifyExportAsFileOn: aFileReference [
|
|
self exportAsFileOn: aFileReference.
|
|
self inform: 'Exported as: ', String cr, aFileReference fullName.
|
|
^ aFileReference
|
|
]
|
|
|
|
{ #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, ' )'
|
|
]
|
|
|
|
{ #category : #'instance creation' }
|
|
Markdeep >> processMarkdownFor: aFileReference [
|
|
"comment stating purpose of message"
|
|
| markdownContent |
|
|
self markdownFile: aFileReference.
|
|
markdownContent := Markdown fromFile: aFileReference.
|
|
self body: (markdownContent commentYAMLMetadata contents).
|
|
self metadata: markdownContent yamlMetadata
|
|
]
|
|
|
|
{ #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 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 >> pubPubFootnotesLinesRange [
|
|
| beginningLine endingLine |
|
|
beginningLine := self contents lines size + 1.
|
|
self contents lines doWithIndex: [:line :i |
|
|
(line beginsWith: '::: {.pub-notes}') ifTrue: [ beginningLine := i ].
|
|
(i > beginningLine and: [ line beginsWith: ':::' ])
|
|
ifTrue: [
|
|
endingLine := i.
|
|
^ {beginningLine . endingLine}
|
|
]
|
|
]
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
Markdeep >> pubPubFootnotesText [
|
|
| footnotesLines output |
|
|
footnotesLines := self contents lines
|
|
copyFrom: self pubPubFootnotesLinesRange first + 3
|
|
to: self pubPubFootnotesLinesRange second - 1.
|
|
output := '' writeStream.
|
|
footnotesLines do: [:line |
|
|
output
|
|
nextPutAll: line;
|
|
nextPutAll: String crlf.
|
|
].
|
|
^ output contents allButLast
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
Markdeep >> pubPubFootnotesToMarkdeep [
|
|
| footnotes sanitized cleanedFootnotesText parsedLinks |
|
|
footnotes := OrderedDictionary new.
|
|
parsedLinks := self pubPubFootnoteRawLinks.
|
|
parsedLinks ifEmpty: [ ^self ].
|
|
sanitized := self body.
|
|
parsedLinks do: [:link | | footnote |
|
|
footnote := self pubPubFootnoteMetadataFromString: link second.
|
|
footnote ifNotNil: [ | toReplace |
|
|
footnotes at: (footnote at: 'id') put: footnote.
|
|
toReplace := '[', link first, ']{', link second, '}'.
|
|
sanitized := sanitized copyReplaceAll: toReplace with: '[^', (footnote at: 'id'), ']'
|
|
]
|
|
].
|
|
cleanedFootnotesText := '' writeStream.
|
|
footnotes keysAndValuesDo: [:k :v |
|
|
cleanedFootnotesText
|
|
nextPutAll: '[^', k, ']: ';
|
|
nextPutAll: (v at: 'data-value'), String lf, String lf.
|
|
].
|
|
self body: (sanitized copyReplaceAll: self pubPubFootnotesText with: cleanedFootnotesText contents)
|
|
]
|
|
|
|
{ #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 := PubPubGrammar new document.
|
|
^ (parser parse: self body)
|
|
]
|
|
|
|
{ #category : #accessing }
|
|
Markdeep >> replaceBackslashBreaklines [
|
|
self bodyReplaceAll: '\
|
|
' with: '<br>
|
|
'
|
|
]
|
|
|
|
{ #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' ]
|
|
]
|
|
|
|
{ #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 ].
|
|
^ ''
|
|
]
|