Improved multiplatform support and adding more replacements for Latin characters.
This commit is contained in:
parent
d875029940
commit
5d68e79c2b
@ -1,201 +1,201 @@
|
||||
"
|
||||
I provide support for external helper apps for Grafoscopio, publishing, collaboration
|
||||
and data management.
|
||||
"
|
||||
Class {
|
||||
#name : #ExternalApp,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'name',
|
||||
'url',
|
||||
'downloadUrl',
|
||||
'description',
|
||||
'sha1',
|
||||
'md5',
|
||||
'binaryLocation'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #installation }
|
||||
ExternalApp class >> compareHashFor: aFileName with: aSHAString [
|
||||
|
||||
aSHAString = (SHA1 new hashMessage: aFileName asFileReference binaryReadStream contents) hex
|
||||
ifFalse: [ ^ false ]
|
||||
ifTrue: [ ^ true ].
|
||||
]
|
||||
|
||||
{ #category : #configuration }
|
||||
ExternalApp class >> configureFossil [
|
||||
"Stablish where is located fossil according to the operative system and/or the input of the user"
|
||||
| fileStream fossil |
|
||||
|
||||
fileStream := UIManager default fileOpen: 'Path to the fossil program binary'.
|
||||
fileStream isNil ifTrue: [ ^nil ].
|
||||
fossil := fileStream name asFileReference fullName.
|
||||
|
||||
]
|
||||
|
||||
{ #category : #configuration }
|
||||
ExternalApp class >> configurePandoc [
|
||||
"Stablish where is located pandoc according to the operative system and/or the input of the user"
|
||||
| fileStream pandoc |
|
||||
|
||||
fileStream := UIManager default fileOpen: 'Path to pandoc program binary'.
|
||||
fileStream isNil ifTrue: [ ^nil ].
|
||||
pandoc := fileStream name asFileReference fullName.
|
||||
|
||||
]
|
||||
|
||||
{ #category : #installation }
|
||||
ExternalApp class >> installSQLite32Bits [
|
||||
"I dowload the SQLite binary for the hosting platform, uncompress it and made it available as with the name
|
||||
NBSQLite is wating for"
|
||||
|
||||
| packageUrl sha1 localPath packageZipName unzippedFolder |
|
||||
|
||||
localPath := FileSystem disk workingDirectory parent / 'bin'.
|
||||
Smalltalk platform name = 'unix'
|
||||
ifTrue: [
|
||||
packageUrl := 'http://sqlite.org/2016/sqlite-tools-linux-x86-3110100.zip'.
|
||||
sha1 := '21a80cefa91d5de50256996fc55990a027c350fd'].
|
||||
Smalltalk platform name = 'Win32'
|
||||
ifTrue: [
|
||||
packageUrl := 'http://sqlite.org/2016/sqlite-dll-win32-x86-3110100.zip'.
|
||||
sha1 := 'cfd6f64ba1fb5de1ccf8321e29764c690c25e0a0'].
|
||||
Smalltalk platform name = 'Mac OS'
|
||||
ifTrue: [
|
||||
packageUrl := 'http://sqlite.org/2016/sqlite-tools-osx-x86-3110100.zip'.
|
||||
sha1 := 'c78b3b92bd37554694d2f73dbecfef1902c15ba7'].
|
||||
self isSQLite32BitsInstalled
|
||||
ifTrue: [ self inform: 'SQLite ya está instalado en el sistema' ]
|
||||
ifFalse: [
|
||||
GrafoscopioBrowser
|
||||
downloadingFrom: packageUrl
|
||||
withMessage: 'Descargando SQLite...'
|
||||
into: localPath asFileReference.
|
||||
packageZipName := (packageUrl splitOn: '/') last.
|
||||
sha1 = (SHA1 new hashMessage: (localPath / packageZipName) asFileReference binaryReadStream contents) hex
|
||||
ifFalse: [ self inform: 'SQLite: Descarga no exitosa.
|
||||
Por favor intente el procedimiento de nuevo o manualmente']
|
||||
ifTrue: [
|
||||
ZipArchive new
|
||||
readFrom: (localPath / packageZipName);
|
||||
extractAllTo: localPath asFileReference.
|
||||
unzippedFolder := packageZipName copyReplaceAll: '.zip' with: ''.
|
||||
(localPath / unzippedFolder ) children do:
|
||||
[:file | file moveTo: (localPath / file basenameWithIndicator)].
|
||||
(localPath / 'sqlite3') copyTo: (localPath / 'libsqlite3.so').
|
||||
(localPath / unzippedFolder ) deleteAll.
|
||||
self inform: 'SQLite instalado existosammente!'
|
||||
].
|
||||
"Cleaning leftovers"
|
||||
(localPath / packageZipName) delete
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #configuration }
|
||||
ExternalApp class >> installSQLite32BitsUI [
|
||||
"I verify if SQLite for 32 bits is installed in the proper location. If yes, I inform that. If not,
|
||||
I made the installation"
|
||||
| install |
|
||||
install := (UIManager default
|
||||
confirm: '¿Desea instalar el motor de base de datos (SQLite)?'
|
||||
label: 'Instalar base de datos?').
|
||||
install ifTrue: [self installSQLite32Bits]
|
||||
|
||||
]
|
||||
|
||||
{ #category : #installation }
|
||||
ExternalApp class >> isSQLite32BitsInstalled [
|
||||
"I verify if the SQLite binary for the hosting platform is installed"
|
||||
|
||||
^ (FileSystem disk workingDirectory parent / 'bin' / 'libsqlite3.so') asFileReference exists
|
||||
|
||||
]
|
||||
|
||||
{ #category : #configuration }
|
||||
ExternalApp class >> pandoc [
|
||||
"I define where the pandoc external app is located"
|
||||
| app |
|
||||
app := ExternalApp new
|
||||
name: 'pandoc';
|
||||
url: 'http://pandoc.org';
|
||||
description: 'Pandoc is a free and open-source software document converter, widely used as a writing tool (especially by scholars) and as a basis for publishing workflows. It was originally created by John MacFarlane, a philosophy professor at the University of California, Berkeley. (from https://en.wikipedia.org/wiki/Pandoc)'.
|
||||
app binaryLocation: '/usr/bin/pandoc' asFileReference.
|
||||
^ app
|
||||
|
||||
|
||||
|
||||
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> binaryLocation [
|
||||
^ binaryLocation
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> binaryLocation: anObject [
|
||||
binaryLocation := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> description [
|
||||
^ description
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> description: anObject [
|
||||
description := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> downloadUrl [
|
||||
^ downloadUrl
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> downloadUrl: anObject [
|
||||
downloadUrl := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> md5 [
|
||||
^ md5
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> md5: anObject [
|
||||
md5 := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> name [
|
||||
^ name
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> name: anObject [
|
||||
name := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> sha1 [
|
||||
^ sha1
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> sha1: anObject [
|
||||
sha1 := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> url [
|
||||
^ url
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> url: anObject [
|
||||
url := anObject
|
||||
]
|
||||
"
|
||||
I provide support for external helper apps for Grafoscopio, publishing, collaboration
|
||||
and data management.
|
||||
"
|
||||
Class {
|
||||
#name : #ExternalApp,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'name',
|
||||
'url',
|
||||
'downloadUrl',
|
||||
'description',
|
||||
'sha1',
|
||||
'md5',
|
||||
'binaryLocation'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #installation }
|
||||
ExternalApp class >> compareHashFor: aFileName with: aSHAString [
|
||||
|
||||
aSHAString = (SHA1 new hashMessage: aFileName asFileReference binaryReadStream contents) hex
|
||||
ifFalse: [ ^ false ]
|
||||
ifTrue: [ ^ true ].
|
||||
]
|
||||
|
||||
{ #category : #configuration }
|
||||
ExternalApp class >> configureFossil [
|
||||
"Stablish where is located fossil according to the operative system and/or the input of the user"
|
||||
| fileStream fossil |
|
||||
|
||||
fileStream := UIManager default fileOpen: 'Path to the fossil program binary'.
|
||||
fileStream isNil ifTrue: [ ^nil ].
|
||||
fossil := fileStream name asFileReference fullName.
|
||||
|
||||
]
|
||||
|
||||
{ #category : #configuration }
|
||||
ExternalApp class >> configurePandoc [
|
||||
"Stablish where is located pandoc according to the operative system and/or the input of the user"
|
||||
| fileStream pandoc |
|
||||
|
||||
fileStream := UIManager default fileOpen: 'Path to pandoc program binary'.
|
||||
fileStream isNil ifTrue: [ ^nil ].
|
||||
pandoc := fileStream name asFileReference fullName.
|
||||
|
||||
]
|
||||
|
||||
{ #category : #installation }
|
||||
ExternalApp class >> installSQLite32Bits [
|
||||
"I dowload the SQLite binary for the hosting platform, uncompress it and made it available as with the name
|
||||
NBSQLite is wating for"
|
||||
|
||||
| packageUrl sha1 localPath packageZipName unzippedFolder |
|
||||
|
||||
localPath := FileSystem disk workingDirectory parent / 'bin'.
|
||||
Smalltalk platform name = 'unix'
|
||||
ifTrue: [
|
||||
packageUrl := 'http://sqlite.org/2016/sqlite-tools-linux-x86-3110100.zip'.
|
||||
sha1 := '21a80cefa91d5de50256996fc55990a027c350fd'].
|
||||
Smalltalk platform name = 'Win32'
|
||||
ifTrue: [
|
||||
packageUrl := 'http://sqlite.org/2016/sqlite-dll-win32-x86-3110100.zip'.
|
||||
sha1 := 'cfd6f64ba1fb5de1ccf8321e29764c690c25e0a0'].
|
||||
Smalltalk platform name = 'Mac OS'
|
||||
ifTrue: [
|
||||
packageUrl := 'http://sqlite.org/2016/sqlite-tools-osx-x86-3110100.zip'.
|
||||
sha1 := 'c78b3b92bd37554694d2f73dbecfef1902c15ba7'].
|
||||
self isSQLite32BitsInstalled
|
||||
ifTrue: [ self inform: 'SQLite ya está instalado en el sistema' ]
|
||||
ifFalse: [
|
||||
GrafoscopioBrowser
|
||||
downloadingFrom: packageUrl
|
||||
withMessage: 'Descargando SQLite...'
|
||||
into: localPath asFileReference.
|
||||
packageZipName := (packageUrl splitOn: '/') last.
|
||||
sha1 = (SHA1 new hashMessage: (localPath / packageZipName) asFileReference binaryReadStream contents) hex
|
||||
ifFalse: [ self inform: 'SQLite: Descarga no exitosa.
|
||||
Por favor intente el procedimiento de nuevo o manualmente']
|
||||
ifTrue: [
|
||||
ZipArchive new
|
||||
readFrom: (localPath / packageZipName);
|
||||
extractAllTo: localPath asFileReference.
|
||||
unzippedFolder := packageZipName copyReplaceAll: '.zip' with: ''.
|
||||
(localPath / unzippedFolder ) children do:
|
||||
[:file | file moveTo: (localPath / file basenameWithIndicator)].
|
||||
(localPath / 'sqlite3') copyTo: (localPath / 'libsqlite3.so').
|
||||
(localPath / unzippedFolder ) deleteAll.
|
||||
self inform: 'SQLite instalado existosammente!'
|
||||
].
|
||||
"Cleaning leftovers"
|
||||
(localPath / packageZipName) delete
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #configuration }
|
||||
ExternalApp class >> installSQLite32BitsUI [
|
||||
"I verify if SQLite for 32 bits is installed in the proper location. If yes, I inform that. If not,
|
||||
I made the installation"
|
||||
| install |
|
||||
install := (UIManager default
|
||||
confirm: '¿Desea instalar el motor de base de datos (SQLite)?'
|
||||
label: 'Instalar base de datos?').
|
||||
install ifTrue: [self installSQLite32Bits]
|
||||
|
||||
]
|
||||
|
||||
{ #category : #installation }
|
||||
ExternalApp class >> isSQLite32BitsInstalled [
|
||||
"I verify if the SQLite binary for the hosting platform is installed"
|
||||
|
||||
^ (FileSystem disk workingDirectory parent / 'bin' / 'libsqlite3.so') asFileReference exists
|
||||
|
||||
]
|
||||
|
||||
{ #category : #configuration }
|
||||
ExternalApp class >> pandoc [
|
||||
"I define where the pandoc external app is located"
|
||||
| app |
|
||||
app := ExternalApp new
|
||||
name: 'pandoc';
|
||||
url: 'http://pandoc.org';
|
||||
description: 'Pandoc is a free and open-source software document converter, widely used as a writing tool (especially by scholars) and as a basis for publishing workflows. It was originally created by John MacFarlane, a philosophy professor at the University of California, Berkeley. (from https://en.wikipedia.org/wiki/Pandoc)'.
|
||||
app binaryLocation: '/usr/bin/pandoc' asFileReference.
|
||||
^ app
|
||||
|
||||
|
||||
|
||||
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> binaryLocation [
|
||||
^ binaryLocation
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> binaryLocation: anObject [
|
||||
binaryLocation := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> description [
|
||||
^ description
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> description: anObject [
|
||||
description := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> downloadUrl [
|
||||
^ downloadUrl
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> downloadUrl: anObject [
|
||||
downloadUrl := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> md5 [
|
||||
^ md5
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> md5: anObject [
|
||||
md5 := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> name [
|
||||
^ name
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> name: anObject [
|
||||
name := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> sha1 [
|
||||
^ sha1
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> sha1: anObject [
|
||||
sha1 := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> url [
|
||||
^ url
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
ExternalApp >> url: anObject [
|
||||
url := anObject
|
||||
]
|
||||
|
@ -1,199 +1,199 @@
|
||||
"
|
||||
I model a documentation object for Grafoscopio.
|
||||
Documents are stored in a fossil repository and have
|
||||
relative paths to it.
|
||||
"
|
||||
Class {
|
||||
#name : #GrafoscopioDocumentation,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'repository',
|
||||
'documents',
|
||||
'localPlace',
|
||||
'name'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> documents [
|
||||
^ self newDefault documents
|
||||
]
|
||||
|
||||
{ #category : #api }
|
||||
GrafoscopioDocumentation class >> download: aFileName [
|
||||
self newDefault download: aFileName
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> listOutdatedDocs [
|
||||
^ self newDefault listOutdatedDocs
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> localPlace [
|
||||
^ self newDefault localPlace
|
||||
]
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
GrafoscopioDocumentation class >> newDefault [
|
||||
^ self new
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> update [
|
||||
^ self newDefault update
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> updateAll [
|
||||
GfUIHelpers docsCollection do: [ :docs | docs update ]
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> updateAllUI [
|
||||
"Updates documentation (manual, tutorials) from the official repository for a given documentation."
|
||||
| update |
|
||||
update := (UIManager default
|
||||
confirm: 'Do you wish to update the documentation?'
|
||||
label: 'Update documentation').
|
||||
update
|
||||
ifTrue: [ self updateAll ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> addDocument: aFilePath [
|
||||
"I add the document contained in aFilePath (a String) to the list of documents that belong
|
||||
to this collection, taking care of avoiding repetitions"
|
||||
|
||||
(self documents includes: aFilePath) ifFalse: [ self documents add: aFilePath ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> documents [
|
||||
^ documents ifNil: [ documents := OrderedCollection new ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> documents: anObject [
|
||||
documents := anObject
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> download: fileNameWithRelativePath [
|
||||
| fileName parentFolder sanitized lastVersion |
|
||||
fileName := (fileNameWithRelativePath splitOn: $/) last.
|
||||
sanitized := self repository sanitize: fileNameWithRelativePath.
|
||||
lastVersion := self repository lastVersionPath: fileNameWithRelativePath.
|
||||
parentFolder := GrafoscopioUtils
|
||||
ensureCreateDirectory: sanitized into: self localPlace.
|
||||
GrafoscopioUtils
|
||||
downloadingFrom: self repository remote asString, lastVersion
|
||||
withMessage: 'Downloading ', fileName
|
||||
into: parentFolder
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> isFileUpdatedFor: relativeFilePath [
|
||||
"I compare if the local and remote copies of a relativeFilePath are updated for the current
|
||||
documentation and return true if they are and false in any other case"
|
||||
| localFile |
|
||||
localFile := self localPlace / relativeFilePath.
|
||||
localFile exists
|
||||
ifFalse: [ ^ false ]
|
||||
ifTrue: [
|
||||
^ ExternalApp
|
||||
compareHashFor: localFile
|
||||
with: (self repository lastHashNumberFor: relativeFilePath) ]
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> listOutdatedDocs [
|
||||
"I return the list of all documentent where the local copy and the remote copy doesn't match"
|
||||
^ self documents reject: [ :doc | (self isFileUpdatedFor: doc) ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> localPlace [
|
||||
^ localPlace.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> localPlace: aFileDirectory [
|
||||
localPlace := aFileDirectory.
|
||||
self localPlace exists ifFalse: [ self localPlace ensureCreateDirectory ].
|
||||
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> name [
|
||||
^ name
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> name: anObject [
|
||||
name := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> openNotebookAt: index [
|
||||
"I open a notebook included with the documentation, located at a given index (anInteger)"
|
||||
| notebookTemp |
|
||||
(index between: 1 and: self documents size)
|
||||
ifFalse: [ ^ self ]
|
||||
ifTrue: [
|
||||
notebookTemp := (self localPlace fullName, '/', (self documents at: index)) asFileReference.
|
||||
notebookTemp exists
|
||||
ifTrue: [GrafoscopioNotebook new openFromFile: notebookTemp]
|
||||
ifFalse: [ self updateUI ]]
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> registerIntoDockingBar [
|
||||
"I detect if any of the documents that are part of my collection contains any metadata
|
||||
indicating if they should be registered in the main docking bar, and in such case, I add them."
|
||||
self documents
|
||||
do: [ :doc | | metadata currentNotebook |
|
||||
currentNotebook := GrafoscopioNotebook new loadFromFile: self localPlace / doc.
|
||||
metadata := currentNotebook metadata.
|
||||
metadata ifNotNil: [
|
||||
metadata
|
||||
at: 'showOnHelp'
|
||||
ifPresent: [ self error:' missing class grafoscopio docking bar' "GrafoscopioDockingBar addToHelpMenu: currentNotebook" ] ] ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> repository [
|
||||
^ repository
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> repository: aFossilRepo [
|
||||
repository := aFossilRepo
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> update [
|
||||
(self listOutdatedDocs)
|
||||
ifEmpty: [
|
||||
self inform: 'All documents in the ', self name,' collection are already updated'.
|
||||
^ self ]
|
||||
ifNotEmpty: [:outdatedDocs |
|
||||
outdatedDocs do: [ :eachDoc | self download: eachDoc ].
|
||||
self inform: 'Updating of ', self name,' documentation finished.' ]
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> updateDocsPlaceUI [
|
||||
self current localPlace: (UIManager default chooseDirectory: 'Path to the documentation folder')
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> updateUI [
|
||||
"Updates documentation (manual, tutorials) from the official repository for a given documentation."
|
||||
| update |
|
||||
update := (UIManager default
|
||||
confirm: 'Do you wish to update the ', self name,' documentation?'
|
||||
label: 'Update ', self name, ' documentation').
|
||||
update ifTrue: [ self update ]
|
||||
]
|
||||
"
|
||||
I model a documentation object for Grafoscopio.
|
||||
Documents are stored in a fossil repository and have
|
||||
relative paths to it.
|
||||
"
|
||||
Class {
|
||||
#name : #GrafoscopioDocumentation,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'repository',
|
||||
'documents',
|
||||
'localPlace',
|
||||
'name'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> documents [
|
||||
^ self newDefault documents
|
||||
]
|
||||
|
||||
{ #category : #api }
|
||||
GrafoscopioDocumentation class >> download: aFileName [
|
||||
self newDefault download: aFileName
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> listOutdatedDocs [
|
||||
^ self newDefault listOutdatedDocs
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> localPlace [
|
||||
^ self newDefault localPlace
|
||||
]
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
GrafoscopioDocumentation class >> newDefault [
|
||||
^ self new
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> update [
|
||||
^ self newDefault update
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> updateAll [
|
||||
GfUIHelpers docsCollection do: [ :docs | docs update ]
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation class >> updateAllUI [
|
||||
"Updates documentation (manual, tutorials) from the official repository for a given documentation."
|
||||
| update |
|
||||
update := (UIManager default
|
||||
confirm: 'Do you wish to update the documentation?'
|
||||
label: 'Update documentation').
|
||||
update
|
||||
ifTrue: [ self updateAll ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> addDocument: aFilePath [
|
||||
"I add the document contained in aFilePath (a String) to the list of documents that belong
|
||||
to this collection, taking care of avoiding repetitions"
|
||||
|
||||
(self documents includes: aFilePath) ifFalse: [ self documents add: aFilePath ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> documents [
|
||||
^ documents ifNil: [ documents := OrderedCollection new ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> documents: anObject [
|
||||
documents := anObject
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> download: fileNameWithRelativePath [
|
||||
| fileName parentFolder sanitized lastVersion |
|
||||
fileName := (fileNameWithRelativePath splitOn: $/) last.
|
||||
sanitized := self repository sanitize: fileNameWithRelativePath.
|
||||
lastVersion := self repository lastVersionPath: fileNameWithRelativePath.
|
||||
parentFolder := GrafoscopioUtils
|
||||
ensureCreateDirectory: sanitized into: self localPlace.
|
||||
GrafoscopioUtils
|
||||
downloadingFrom: self repository remote asString, lastVersion
|
||||
withMessage: 'Downloading ', fileName
|
||||
into: parentFolder
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> isFileUpdatedFor: relativeFilePath [
|
||||
"I compare if the local and remote copies of a relativeFilePath are updated for the current
|
||||
documentation and return true if they are and false in any other case"
|
||||
| localFile |
|
||||
localFile := self localPlace / relativeFilePath.
|
||||
localFile exists
|
||||
ifFalse: [ ^ false ]
|
||||
ifTrue: [
|
||||
^ ExternalApp
|
||||
compareHashFor: localFile
|
||||
with: (self repository lastHashNumberFor: relativeFilePath) ]
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> listOutdatedDocs [
|
||||
"I return the list of all documentent where the local copy and the remote copy doesn't match"
|
||||
^ self documents reject: [ :doc | (self isFileUpdatedFor: doc) ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> localPlace [
|
||||
^ localPlace.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> localPlace: aFileDirectory [
|
||||
localPlace := aFileDirectory.
|
||||
self localPlace exists ifFalse: [ self localPlace ensureCreateDirectory ].
|
||||
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> name [
|
||||
^ name
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> name: anObject [
|
||||
name := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> openNotebookAt: index [
|
||||
"I open a notebook included with the documentation, located at a given index (anInteger)"
|
||||
| notebookTemp |
|
||||
(index between: 1 and: self documents size)
|
||||
ifFalse: [ ^ self ]
|
||||
ifTrue: [
|
||||
notebookTemp := (self localPlace fullName, '/', (self documents at: index)) asFileReference.
|
||||
notebookTemp exists
|
||||
ifTrue: [GrafoscopioNotebook new openFromFile: notebookTemp]
|
||||
ifFalse: [ self updateUI ]]
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> registerIntoDockingBar [
|
||||
"I detect if any of the documents that are part of my collection contains any metadata
|
||||
indicating if they should be registered in the main docking bar, and in such case, I add them."
|
||||
self documents
|
||||
do: [ :doc | | metadata currentNotebook |
|
||||
currentNotebook := GrafoscopioNotebook new loadFromFile: self localPlace / doc.
|
||||
metadata := currentNotebook metadata.
|
||||
metadata ifNotNil: [
|
||||
metadata
|
||||
at: 'showOnHelp'
|
||||
ifPresent: [ self error:' missing class grafoscopio docking bar' "GrafoscopioDockingBar addToHelpMenu: currentNotebook" ] ] ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> repository [
|
||||
^ repository
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
GrafoscopioDocumentation >> repository: aFossilRepo [
|
||||
repository := aFossilRepo
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> update [
|
||||
(self listOutdatedDocs)
|
||||
ifEmpty: [
|
||||
self inform: 'All documents in the ', self name,' collection are already updated'.
|
||||
^ self ]
|
||||
ifNotEmpty: [:outdatedDocs |
|
||||
outdatedDocs do: [ :eachDoc | self download: eachDoc ].
|
||||
self inform: 'Updating of ', self name,' documentation finished.' ]
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> updateDocsPlaceUI [
|
||||
self current localPlace: (UIManager default chooseDirectory: 'Path to the documentation folder')
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioDocumentation >> updateUI [
|
||||
"Updates documentation (manual, tutorials) from the official repository for a given documentation."
|
||||
| update |
|
||||
update := (UIManager default
|
||||
confirm: 'Do you wish to update the ', self name,' documentation?'
|
||||
label: 'Update ', self name, ' documentation').
|
||||
update ifTrue: [ self update ]
|
||||
]
|
||||
|
@ -1,24 +1,24 @@
|
||||
"
|
||||
A GrafoscopioDocumentationTest is a test class for testing the behavior of GrafoscopioDocumentation
|
||||
"
|
||||
Class {
|
||||
#name : #GrafoscopioDocumentationTest,
|
||||
#superclass : #TestCase,
|
||||
#category : #'Grafoscopio-Utils-Tests'
|
||||
}
|
||||
|
||||
{ #category : #'tests-utility' }
|
||||
GrafoscopioDocumentationTest >> defaultTestRepo [
|
||||
"I create a default documentation repository, that is used for several test."
|
||||
|
||||
| testRepo |
|
||||
testRepo := GrafoscopioDocumentation new.
|
||||
testRepo repository: (FossilRepo new remote: 'http://mutabit.com/repos.fossil/grafoscopio');
|
||||
localPlace: FileLocator temp asFileReference /'Grafoscopio';
|
||||
name: 'test Repo'.
|
||||
testRepo
|
||||
addDocument: 'Docs/Es/Tutoriales/tutorial.ston';
|
||||
addDocument: 'Docs/En/Books/Manual/manual.ston';
|
||||
addDocument: 'Docs/En/Books/DataActivism/techniques-for-datactivism.ston'.
|
||||
^ testRepo
|
||||
]
|
||||
"
|
||||
A GrafoscopioDocumentationTest is a test class for testing the behavior of GrafoscopioDocumentation
|
||||
"
|
||||
Class {
|
||||
#name : #GrafoscopioDocumentationTest,
|
||||
#superclass : #TestCase,
|
||||
#category : #'Grafoscopio-Utils-Tests'
|
||||
}
|
||||
|
||||
{ #category : #'tests-utility' }
|
||||
GrafoscopioDocumentationTest >> defaultTestRepo [
|
||||
"I create a default documentation repository, that is used for several test."
|
||||
|
||||
| testRepo |
|
||||
testRepo := GrafoscopioDocumentation new.
|
||||
testRepo repository: (FossilRepo new remote: 'http://mutabit.com/repos.fossil/grafoscopio');
|
||||
localPlace: FileLocator temp asFileReference /'Grafoscopio';
|
||||
name: 'test Repo'.
|
||||
testRepo
|
||||
addDocument: 'Docs/Es/Tutoriales/tutorial.ston';
|
||||
addDocument: 'Docs/En/Books/Manual/manual.ston';
|
||||
addDocument: 'Docs/En/Books/DataActivism/techniques-for-datactivism.ston'.
|
||||
^ testRepo
|
||||
]
|
||||
|
@ -1,164 +1,164 @@
|
||||
"
|
||||
I contain simple general functionality used by Grafoscopio, Dataviz
|
||||
or other related projects.
|
||||
"
|
||||
Class {
|
||||
#name : #GrafoscopioUtils,
|
||||
#superclass : #Object,
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #utilities }
|
||||
GrafoscopioUtils class >> checksumFor: aFileReference [
|
||||
^ (SHA1 new hashMessage: aFileReference binaryReadStream contents) hex.
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioUtils class >> download: fileNameWithRelativePath from: urlString into: aFolder [
|
||||
| fileName parentFolder |
|
||||
fileName := (fileNameWithRelativePath splitOn: $/) last.
|
||||
parentFolder := self ensureCreateDirectory: fileNameWithRelativePath into: aFolder.
|
||||
self
|
||||
downloadingFrom: urlString, fileNameWithRelativePath
|
||||
withMessage: 'Downloading ', fileName
|
||||
into: parentFolder
|
||||
]
|
||||
|
||||
{ #category : #'graphical interface' }
|
||||
GrafoscopioUtils class >> downloadingFrom: downloadUrl withMessage: aString into: location [
|
||||
| fileName |
|
||||
fileName := (self sanitize: downloadUrl) segments last.
|
||||
(location / fileName) ensureDelete.
|
||||
[: bar |
|
||||
bar title: aString.
|
||||
[ZnClient new
|
||||
enforceHttpSuccess: true;
|
||||
url: downloadUrl;
|
||||
downloadTo: location;
|
||||
signalProgress: true
|
||||
]
|
||||
on: HTTPProgress
|
||||
do: [ :progress |
|
||||
progress isEmpty ifFalse: [ bar current: progress percentage ].
|
||||
progress resume ].
|
||||
] asJob run.
|
||||
|
||||
^ (location / fileName) asFileReference
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
GrafoscopioUtils class >> ensureCreateDirectory: fileNameWithRelativePath into: aFolder [
|
||||
| relativePathFolders parentFolder newPath |
|
||||
relativePathFolders := (fileNameWithRelativePath splitOn: $/) allButLast.
|
||||
newPath := aFolder path.
|
||||
relativePathFolders do: [ :folder | newPath := newPath / folder ].
|
||||
parentFolder := newPath asFileReference.
|
||||
parentFolder ensureCreateDirectory.
|
||||
^ parentFolder
|
||||
|
||||
]
|
||||
|
||||
{ #category : #persistence }
|
||||
GrafoscopioUtils class >> exportAsSton: anObject on: aFileReference [
|
||||
aFileReference exists ifTrue: [ aFileReference ensureDelete ].
|
||||
aFileReference ensureCreateFile.
|
||||
aFileReference writeStreamDo: [ :stream |
|
||||
(STON writer on: stream)
|
||||
newLine: String crlf;
|
||||
prettyPrint: true;
|
||||
keepNewLines: true;
|
||||
nextPut: anObject ].
|
||||
|
||||
|
||||
|
||||
]
|
||||
|
||||
{ #category : #'graphical interface' }
|
||||
GrafoscopioUtils class >> getContentsFrom: url withMessage: aString [
|
||||
| client |
|
||||
[: bar |
|
||||
bar title: aString.
|
||||
[client := ZnClient new
|
||||
enforceHttpSuccess: true;
|
||||
get: (url);
|
||||
signalProgress: true
|
||||
]
|
||||
on: HTTPProgress
|
||||
do: [ :progress |
|
||||
progress isEmpty ifFalse: [ bar current: progress percentage ].
|
||||
progress resume ].
|
||||
] asJob run.
|
||||
^ client contents.
|
||||
]
|
||||
|
||||
{ #category : #persistence }
|
||||
GrafoscopioUtils class >> joinLinesFor: aCollection [
|
||||
|
||||
| joinedLines |
|
||||
|
||||
joinedLines := WriteStream on: ''.
|
||||
aCollection do: [ :line | joinedLines nextPutAll: line; crlf ].
|
||||
^ joinedLines contents
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
GrafoscopioUtils class >> perform: aString on: anObject [
|
||||
|
||||
| msg |
|
||||
msg := self selectorAndArgumentsFrom: aString.
|
||||
msg
|
||||
at: 'args'
|
||||
ifPresent: [ ^ anObject perform: (msg at: 'selector') withArguments: (msg at: 'args') ]
|
||||
ifAbsent: [ ^ anObject perform: (msg at: 'selector') ]
|
||||
]
|
||||
|
||||
{ #category : #private }
|
||||
GrafoscopioUtils class >> sanitize: url [
|
||||
"I remove white spaces in url's and prepend 'http://' to urls when it is ommited, so
|
||||
operations that rely on sane and well formed urls don't throw error messages."
|
||||
| sanitized modUrl |
|
||||
modUrl := url.
|
||||
[modUrl asString endsWith: ' ']
|
||||
whileTrue: [ modUrl := modUrl copyFrom: 1 to: (modUrl size - 1) ].
|
||||
(url asString beginsWith: 'http') "http or https"
|
||||
ifFalse: [ sanitized := ('http://', modUrl) asUrl ]
|
||||
ifTrue: [ sanitized := modUrl asUrl ].
|
||||
^ sanitized
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
GrafoscopioUtils class >> selectorAndArgumentsFrom: aString [
|
||||
"I return from aString a dictionary that contains a message and an array of arguments used
|
||||
in such string. Notice that the keyword message must contain spaces between the ':' and its
|
||||
respective argument.
|
||||
See GrafoscopioUtilsTest for examples"
|
||||
| msgArray answer keywords selector args |
|
||||
answer := OrderedDictionary new.
|
||||
msgArray := aString splitOn: ':'.
|
||||
msgArray size = 1
|
||||
ifTrue: [
|
||||
^ answer
|
||||
at: 'selector' put: (msgArray at: 1) asSymbol;
|
||||
yourself ].
|
||||
selector := ''.
|
||||
args := OrderedCollection new.
|
||||
aString
|
||||
splitOn: Character space
|
||||
do: [ :part |
|
||||
part endsWithAColon
|
||||
ifTrue: [ selector := selector, part ]
|
||||
ifFalse: [ args add: part ] ].
|
||||
answer
|
||||
at: 'selector' put: selector asSymbol;
|
||||
at: 'args' put: args asArray.
|
||||
^ answer
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioUtils class >> update [
|
||||
"Updates GrafoscopioUtils with new versions of itself take from the source code repository."
|
||||
Gofer new
|
||||
smalltalkhubUser: 'Offray' project: 'Grafoscopio';
|
||||
package: 'Grafoscopio-Utils';
|
||||
load.
|
||||
]
|
||||
"
|
||||
I contain simple general functionality used by Grafoscopio, Dataviz
|
||||
or other related projects.
|
||||
"
|
||||
Class {
|
||||
#name : #GrafoscopioUtils,
|
||||
#superclass : #Object,
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #utilities }
|
||||
GrafoscopioUtils class >> checksumFor: aFileReference [
|
||||
^ (SHA1 new hashMessage: aFileReference binaryReadStream contents) hex.
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioUtils class >> download: fileNameWithRelativePath from: urlString into: aFolder [
|
||||
| fileName parentFolder |
|
||||
fileName := (fileNameWithRelativePath splitOn: $/) last.
|
||||
parentFolder := self ensureCreateDirectory: fileNameWithRelativePath into: aFolder.
|
||||
self
|
||||
downloadingFrom: urlString, fileNameWithRelativePath
|
||||
withMessage: 'Downloading ', fileName
|
||||
into: parentFolder
|
||||
]
|
||||
|
||||
{ #category : #'graphical interface' }
|
||||
GrafoscopioUtils class >> downloadingFrom: downloadUrl withMessage: aString into: location [
|
||||
| fileName |
|
||||
fileName := (self sanitize: downloadUrl) segments last.
|
||||
(location / fileName) ensureDelete.
|
||||
[: bar |
|
||||
bar title: aString.
|
||||
[ZnClient new
|
||||
enforceHttpSuccess: true;
|
||||
url: downloadUrl;
|
||||
downloadTo: location;
|
||||
signalProgress: true
|
||||
]
|
||||
on: HTTPProgress
|
||||
do: [ :progress |
|
||||
progress isEmpty ifFalse: [ bar current: progress percentage ].
|
||||
progress resume ].
|
||||
] asJob run.
|
||||
|
||||
^ (location / fileName) asFileReference
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
GrafoscopioUtils class >> ensureCreateDirectory: fileNameWithRelativePath into: aFolder [
|
||||
| relativePathFolders parentFolder newPath |
|
||||
relativePathFolders := (fileNameWithRelativePath splitOn: $/) allButLast.
|
||||
newPath := aFolder path.
|
||||
relativePathFolders do: [ :folder | newPath := newPath / folder ].
|
||||
parentFolder := newPath asFileReference.
|
||||
parentFolder ensureCreateDirectory.
|
||||
^ parentFolder
|
||||
|
||||
]
|
||||
|
||||
{ #category : #persistence }
|
||||
GrafoscopioUtils class >> exportAsSton: anObject on: aFileReference [
|
||||
aFileReference exists ifTrue: [ aFileReference ensureDelete ].
|
||||
aFileReference ensureCreateFile.
|
||||
aFileReference writeStreamDo: [ :stream |
|
||||
(STON writer on: stream)
|
||||
newLine: String crlf;
|
||||
prettyPrint: true;
|
||||
keepNewLines: true;
|
||||
nextPut: anObject ].
|
||||
|
||||
|
||||
|
||||
]
|
||||
|
||||
{ #category : #'graphical interface' }
|
||||
GrafoscopioUtils class >> getContentsFrom: url withMessage: aString [
|
||||
| client |
|
||||
[: bar |
|
||||
bar title: aString.
|
||||
[client := ZnClient new
|
||||
enforceHttpSuccess: true;
|
||||
get: (url);
|
||||
signalProgress: true
|
||||
]
|
||||
on: HTTPProgress
|
||||
do: [ :progress |
|
||||
progress isEmpty ifFalse: [ bar current: progress percentage ].
|
||||
progress resume ].
|
||||
] asJob run.
|
||||
^ client contents.
|
||||
]
|
||||
|
||||
{ #category : #persistence }
|
||||
GrafoscopioUtils class >> joinLinesFor: aCollection [
|
||||
|
||||
| joinedLines |
|
||||
|
||||
joinedLines := WriteStream on: ''.
|
||||
aCollection do: [ :line | joinedLines nextPutAll: line; crlf ].
|
||||
^ joinedLines contents
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
GrafoscopioUtils class >> perform: aString on: anObject [
|
||||
|
||||
| msg |
|
||||
msg := self selectorAndArgumentsFrom: aString.
|
||||
msg
|
||||
at: 'args'
|
||||
ifPresent: [ ^ anObject perform: (msg at: 'selector') withArguments: (msg at: 'args') ]
|
||||
ifAbsent: [ ^ anObject perform: (msg at: 'selector') ]
|
||||
]
|
||||
|
||||
{ #category : #private }
|
||||
GrafoscopioUtils class >> sanitize: url [
|
||||
"I remove white spaces in url's and prepend 'http://' to urls when it is ommited, so
|
||||
operations that rely on sane and well formed urls don't throw error messages."
|
||||
| sanitized modUrl |
|
||||
modUrl := url.
|
||||
[modUrl asString endsWith: ' ']
|
||||
whileTrue: [ modUrl := modUrl copyFrom: 1 to: (modUrl size - 1) ].
|
||||
(url asString beginsWith: 'http') "http or https"
|
||||
ifFalse: [ sanitized := ('http://', modUrl) asUrl ]
|
||||
ifTrue: [ sanitized := modUrl asUrl ].
|
||||
^ sanitized
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
GrafoscopioUtils class >> selectorAndArgumentsFrom: aString [
|
||||
"I return from aString a dictionary that contains a message and an array of arguments used
|
||||
in such string. Notice that the keyword message must contain spaces between the ':' and its
|
||||
respective argument.
|
||||
See GrafoscopioUtilsTest for examples"
|
||||
| msgArray answer keywords selector args |
|
||||
answer := OrderedDictionary new.
|
||||
msgArray := aString splitOn: ':'.
|
||||
msgArray size = 1
|
||||
ifTrue: [
|
||||
^ answer
|
||||
at: 'selector' put: (msgArray at: 1) asSymbol;
|
||||
yourself ].
|
||||
selector := ''.
|
||||
args := OrderedCollection new.
|
||||
aString
|
||||
splitOn: Character space
|
||||
do: [ :part |
|
||||
part endsWithAColon
|
||||
ifTrue: [ selector := selector, part ]
|
||||
ifFalse: [ args add: part ] ].
|
||||
answer
|
||||
at: 'selector' put: selector asSymbol;
|
||||
at: 'args' put: args asArray.
|
||||
^ answer
|
||||
]
|
||||
|
||||
{ #category : #updating }
|
||||
GrafoscopioUtils class >> update [
|
||||
"Updates GrafoscopioUtils with new versions of itself take from the source code repository."
|
||||
Gofer new
|
||||
smalltalkhubUser: 'Offray' project: 'Grafoscopio';
|
||||
package: 'Grafoscopio-Utils';
|
||||
load.
|
||||
]
|
||||
|
@ -1,8 +1,8 @@
|
||||
"
|
||||
A GrafoscopioUtilsTest is a test class for testing the behavior of GrafoscopioUtils
|
||||
"
|
||||
Class {
|
||||
#name : #GrafoscopioUtilsTest,
|
||||
#superclass : #TestCase,
|
||||
#category : #'Grafoscopio-Utils-Tests'
|
||||
}
|
||||
"
|
||||
A GrafoscopioUtilsTest is a test class for testing the behavior of GrafoscopioUtils
|
||||
"
|
||||
Class {
|
||||
#name : #GrafoscopioUtilsTest,
|
||||
#superclass : #TestCase,
|
||||
#category : #'Grafoscopio-Utils-Tests'
|
||||
}
|
||||
|
@ -1,113 +1,113 @@
|
||||
"
|
||||
I model the interface between a CodiMD (https://demo.codimd.org) documentation
|
||||
server and Grafoscopio.
|
||||
I enable the interaction between Grafoscopio notebooks and CodiMD documents,
|
||||
so one document can start online (as a CodiMD pad) and continue as a Grafoscopio
|
||||
notebook or viceversa.
|
||||
"
|
||||
Class {
|
||||
#name : #HedgeDoc,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'server',
|
||||
'pad',
|
||||
'contents',
|
||||
'url'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc class >> newDefault [
|
||||
^ self new
|
||||
defaultServer.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> contents [
|
||||
^ contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> contents: anObject [
|
||||
contents := anObject
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> defaultServer [
|
||||
self server: 'https://docutopia.tupale.co'.
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> htmlUrl [
|
||||
| link |
|
||||
link := self url copy.
|
||||
link segments insert: 's' before: 1.
|
||||
^ link
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> importContents [
|
||||
self contents: self retrieveContents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> pad [
|
||||
^ pad
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> pad: anObject [
|
||||
pad := anObject
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> retrieveContents [
|
||||
self url ifNil: [ ^ self ].
|
||||
^ (self url addPathSegment: 'download') retrieveContents
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> retrieveHtmlContents [
|
||||
| htmlContents |
|
||||
self url ifNil: [ ^ self ].
|
||||
htmlContents := self htmlUrl.
|
||||
^ htmlContents retrieveContents
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> saveContentsToFile: aFileLocator [
|
||||
self url ifNil: [ ^ self ].
|
||||
^ (self url addPathSegment: 'download') saveContentsToFile: aFileLocator
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> saveHtmlContentsToFile: aFileLocator [
|
||||
self url ifNil: [ ^ self ].
|
||||
^ self htmlUrl saveContentsToFile: aFileLocator
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> server [
|
||||
^ server
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> server: aUrlString [
|
||||
server := aUrlString
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> url [
|
||||
^ url asUrl
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> url: anObject [
|
||||
url := anObject
|
||||
]
|
||||
|
||||
{ #category : #visiting }
|
||||
HedgeDoc >> visit [
|
||||
WebBrowser openOn: self server, '/', self pad.
|
||||
]
|
||||
"
|
||||
I model the interface between a CodiMD (https://demo.codimd.org) documentation
|
||||
server and Grafoscopio.
|
||||
I enable the interaction between Grafoscopio notebooks and CodiMD documents,
|
||||
so one document can start online (as a CodiMD pad) and continue as a Grafoscopio
|
||||
notebook or viceversa.
|
||||
"
|
||||
Class {
|
||||
#name : #HedgeDoc,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'server',
|
||||
'pad',
|
||||
'contents',
|
||||
'url'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc class >> newDefault [
|
||||
^ self new
|
||||
defaultServer.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> contents [
|
||||
^ contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> contents: anObject [
|
||||
contents := anObject
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> defaultServer [
|
||||
self server: 'https://docutopia.tupale.co'.
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> htmlUrl [
|
||||
| link |
|
||||
link := self url copy.
|
||||
link segments insert: 's' before: 1.
|
||||
^ link
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> importContents [
|
||||
self contents: self retrieveContents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> pad [
|
||||
^ pad
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> pad: anObject [
|
||||
pad := anObject
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> retrieveContents [
|
||||
self url ifNil: [ ^ self ].
|
||||
^ (self url addPathSegment: 'download') retrieveContents
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> retrieveHtmlContents [
|
||||
| htmlContents |
|
||||
self url ifNil: [ ^ self ].
|
||||
htmlContents := self htmlUrl.
|
||||
^ htmlContents retrieveContents
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> saveContentsToFile: aFileLocator [
|
||||
self url ifNil: [ ^ self ].
|
||||
^ (self url addPathSegment: 'download') saveContentsToFile: aFileLocator
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
HedgeDoc >> saveHtmlContentsToFile: aFileLocator [
|
||||
self url ifNil: [ ^ self ].
|
||||
^ self htmlUrl saveContentsToFile: aFileLocator
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> server [
|
||||
^ server
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> server: aUrlString [
|
||||
server := aUrlString
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> url [
|
||||
^ url asUrl
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
HedgeDoc >> url: anObject [
|
||||
url := anObject
|
||||
]
|
||||
|
||||
{ #category : #visiting }
|
||||
HedgeDoc >> visit [
|
||||
WebBrowser openOn: self server, '/', self pad.
|
||||
]
|
||||
|
@ -1,185 +1,185 @@
|
||||
"
|
||||
I model a Mardeep file as described in https://casual-effects.com/markdeep/
|
||||
"
|
||||
Class {
|
||||
#name : #Markdeep,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'tocStyle',
|
||||
'comments',
|
||||
'header',
|
||||
'tail',
|
||||
'body',
|
||||
'language',
|
||||
'markdownFile'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
Markdeep class >> fromMarkdownFile: aFileReference [
|
||||
^ self new fromMarkdownFile: aFileReference.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> body [
|
||||
^ body
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> body: anObject [
|
||||
body := anObject
|
||||
]
|
||||
|
||||
{ #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 : #'instance creation' }
|
||||
Markdeep >> contents [
|
||||
| output |
|
||||
output := '' writeStream.
|
||||
output
|
||||
nextPutAll: self header; crlf;
|
||||
nextPutAll: self body; crlf;
|
||||
nextPutAll: self tail; crlf; crlf;
|
||||
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 fullName, '.html') asFileReference.
|
||||
self exportAsFileOn: newFile.
|
||||
]
|
||||
|
||||
{ #category : #persistence }
|
||||
Markdeep >> exportAsFileOn: aFileReference [
|
||||
aFileReference exists ifFalse: [ aFileReference ensureCreateFile ].
|
||||
aFileReference writeStreamDo: [ :stream |
|
||||
stream nextPutAll: self contents ].
|
||||
self inform: 'Exported as: ', String cr, aFileReference fullName
|
||||
]
|
||||
|
||||
{ #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 >> header [
|
||||
^ self fontAwesomeHeader
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> header: anObject [
|
||||
header := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> language [
|
||||
^ language
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> language: anObject [
|
||||
language := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> markdownFile [
|
||||
^ markdownFile
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> markdownFile: aFileReference [
|
||||
"I provide information about which Markdown file was used to generate the Markdeep body"
|
||||
markdownFile := aFileReference
|
||||
]
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
Markdeep >> processMarkdownFor: aFileReference [
|
||||
"comment stating purpose of message"
|
||||
| markdownContent |
|
||||
self markdownFile: aFileReference.
|
||||
markdownContent := Markdown fromFile: aFileReference.
|
||||
self body: (markdownContent commentYAMLMetadata contents).
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> tail [
|
||||
"I enable the document tail, which, in turn, enables a Markdeep document"
|
||||
^ '
|
||||
<!-- Markdeep: -->
|
||||
|
||||
<style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style>
|
||||
<script>window.markdeepOptions = {tocStyle: "medium"}</script>
|
||||
<script src="markdeep.min.js" charset="utf-8"></script>
|
||||
<script
|
||||
src="https://casual-effects.com/markdeep/latest/markdeep.min.js?"
|
||||
charset="utf-8">
|
||||
</script>
|
||||
<!--<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>--> '
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> tail: anObject [
|
||||
tail := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> tocStyle [
|
||||
^ tocStyle
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> tocStyle: anObject [
|
||||
tocStyle := anObject
|
||||
]
|
||||
"
|
||||
I model a Mardeep file as described in https://casual-effects.com/markdeep/
|
||||
"
|
||||
Class {
|
||||
#name : #Markdeep,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'tocStyle',
|
||||
'comments',
|
||||
'header',
|
||||
'tail',
|
||||
'body',
|
||||
'language',
|
||||
'markdownFile'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
Markdeep class >> fromMarkdownFile: aFileReference [
|
||||
^ self new fromMarkdownFile: aFileReference.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> body [
|
||||
^ body
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> body: anObject [
|
||||
body := anObject
|
||||
]
|
||||
|
||||
{ #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 : #'instance creation' }
|
||||
Markdeep >> contents [
|
||||
| output |
|
||||
output := '' writeStream.
|
||||
output
|
||||
nextPutAll: self header; crlf;
|
||||
nextPutAll: self body; crlf;
|
||||
nextPutAll: self tail; crlf; crlf;
|
||||
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 fullName, '.html') asFileReference.
|
||||
self exportAsFileOn: newFile.
|
||||
]
|
||||
|
||||
{ #category : #persistence }
|
||||
Markdeep >> exportAsFileOn: aFileReference [
|
||||
aFileReference exists ifFalse: [ aFileReference ensureCreateFile ].
|
||||
aFileReference writeStreamDo: [ :stream |
|
||||
stream nextPutAll: self contents ].
|
||||
self inform: 'Exported as: ', String cr, aFileReference fullName
|
||||
]
|
||||
|
||||
{ #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 >> header [
|
||||
^ self fontAwesomeHeader
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> header: anObject [
|
||||
header := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> language [
|
||||
^ language
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> language: anObject [
|
||||
language := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> markdownFile [
|
||||
^ markdownFile
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> markdownFile: aFileReference [
|
||||
"I provide information about which Markdown file was used to generate the Markdeep body"
|
||||
markdownFile := aFileReference
|
||||
]
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
Markdeep >> processMarkdownFor: aFileReference [
|
||||
"comment stating purpose of message"
|
||||
| markdownContent |
|
||||
self markdownFile: aFileReference.
|
||||
markdownContent := Markdown fromFile: aFileReference.
|
||||
self body: (markdownContent commentYAMLMetadata contents).
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> tail [
|
||||
"I enable the document tail, which, in turn, enables a Markdeep document"
|
||||
^ '
|
||||
<!-- Markdeep: -->
|
||||
|
||||
<style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style>
|
||||
<script>window.markdeepOptions = {tocStyle: "medium"}</script>
|
||||
<script src="markdeep.min.js" charset="utf-8"></script>
|
||||
<script
|
||||
src="https://casual-effects.com/markdeep/latest/markdeep.min.js?"
|
||||
charset="utf-8">
|
||||
</script>
|
||||
<!--<script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>--> '
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> tail: anObject [
|
||||
tail := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> tocStyle [
|
||||
^ tocStyle
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdeep >> tocStyle: anObject [
|
||||
tocStyle := anObject
|
||||
]
|
||||
|
@ -1,169 +1,169 @@
|
||||
"
|
||||
I model a Markdown document.
|
||||
At some point the idea is to have a full native parser implemented to deal
|
||||
with my syntax, but meanwhile I will be collaborating with external parsers,
|
||||
particularly the ones provided by Pandoc and/or Lunamark.
|
||||
"
|
||||
Class {
|
||||
#name : #Markdown,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'contents',
|
||||
'file'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
Markdown class >> fromFile: aFileReference [
|
||||
^ self new fromFile: aFileReference
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown class >> yamlMetadataDelimiter [
|
||||
^ '---'
|
||||
|
||||
]
|
||||
|
||||
{ #category : #operation }
|
||||
Markdown >> commentYAMLMetadata [
|
||||
| newContents |
|
||||
self detectYAMLMetadata ifFalse: [ ^ self ].
|
||||
newContents := '' writeStream.
|
||||
newContents nextPutAll: '<!--@yaml:'; crlf.
|
||||
newContents nextPutAll: self extractYAMLMetadata.
|
||||
newContents nextPutAll: String cr.
|
||||
newContents nextPutAll: '-->'; crlf.
|
||||
(self lines copyFrom: self locateYAMLMetadataClosing + 2 to: self lines size) do: [ :line |
|
||||
newContents nextPutAll: line; crlf ].
|
||||
self contents: newContents contents.
|
||||
^ self contents
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> containsYAMLMetadataClosing [
|
||||
^ self locateYAMLMetadataClosing > 0
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> contents [
|
||||
^ contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> contents: anObject [
|
||||
contents := anObject
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> detectYAMLMetadata [
|
||||
| lines |
|
||||
lines := self lines.
|
||||
^ self startsWithYAMLMetadataDelimiter
|
||||
and: [ lines allButFirst
|
||||
detect: [ :currentLine | currentLine beginsWith: self class yamlMetadataDelimiter ]
|
||||
ifFound: [ ^ true ] ifNone: [ ^ false ] ]
|
||||
]
|
||||
|
||||
{ #category : #operation }
|
||||
Markdown >> exportMetadataAsJson [
|
||||
"TBD: Lua scripts should be checked and installed when missing. Maybe a shared location
|
||||
in '.local/share/Grafoscopio/Scripts' should be developed in the near future."
|
||||
| output luaScript |
|
||||
luaScript := FileLocator home / '.local/share/Brea/scripts/meta-to-json.lua'.
|
||||
Smalltalk platformName = 'unix' ifTrue: [
|
||||
OSSUnixSubprocess new
|
||||
workingDirectory: self file parent fullName;
|
||||
command: 'pandoc';
|
||||
arguments: { '--lua-filter=', luaScript fullName . self file basename };
|
||||
redirectStdout;
|
||||
redirectStdin;
|
||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
||||
output := process isSuccess
|
||||
ifTrue: [ outString ]
|
||||
ifFalse: [ errString ]
|
||||
]].
|
||||
^ output correctAccentedCharacters
|
||||
]
|
||||
|
||||
{ #category : #operation }
|
||||
Markdown >> exportMetadataAsYaml [
|
||||
| exportedFile |
|
||||
exportedFile := FileLocator temp / 'metadata.yaml'.
|
||||
MarkupFile exportAsFileOn: exportedFile containing: self yamlMetadataAsString.
|
||||
^ exportedFile
|
||||
]
|
||||
|
||||
{ #category : #operation }
|
||||
Markdown >> extractYAMLMetadata [
|
||||
| output yamlLines |
|
||||
self detectYAMLMetadata ifFalse: [ ^ nil ].
|
||||
yamlLines := self lines copyFrom: 2 to: (self locateYAMLMetadataClosing).
|
||||
output := '' writeStream.
|
||||
yamlLines do: [ :line |
|
||||
output
|
||||
nextPutAll: line;
|
||||
nextPut: Character cr. ].
|
||||
^ output contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> file [
|
||||
^ file
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> file: aFileReference [
|
||||
"I store the origen/destination of the Markdown contents."
|
||||
file := aFileReference
|
||||
]
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
Markdown >> fromFile: aFileReference [
|
||||
self contents: aFileReference contents.
|
||||
self file: aFileReference
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> lines [
|
||||
^ self contents lines.
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> locateYAMLMetadataClosing [
|
||||
"I return the line where the closing of the YAML metadata occurs or 0 if no closing is found."
|
||||
| result |
|
||||
self startsWithYAMLMetadataDelimiter ifFalse: [ ^ self ].
|
||||
result := 0.
|
||||
self lines allButFirst doWithIndex: [ :currentLine :i |
|
||||
(currentLine beginsWith: self class yamlMetadataDelimiter) ifTrue: [ result := i ]].
|
||||
^ result
|
||||
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> metadata [
|
||||
| rawMeta |
|
||||
rawMeta := PPYAMLGrammar new parse: self extractYAMLMetadata.
|
||||
rawMeta associationsDo: [ :assoc |
|
||||
assoc value = 'false' ifTrue: [ assoc value: false ].
|
||||
assoc value = 'true' ifTrue: [ assoc value: true ] ].
|
||||
^ rawMeta
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> startsWithYAMLMetadataDelimiter [
|
||||
^ self lines first beginsWith: self class yamlMetadataDelimiter
|
||||
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> yamlMetadataAsString [
|
||||
| output |
|
||||
self extractYAMLMetadata ifNil: [ ^ nil ].
|
||||
output := String new writeStream.
|
||||
output nextPutAll: self class yamlMetadataDelimiter; cr.
|
||||
output nextPutAll: self extractYAMLMetadata.
|
||||
output nextPutAll: self class yamlMetadataDelimiter; cr.
|
||||
^ output contents.
|
||||
]
|
||||
"
|
||||
I model a Markdown document.
|
||||
At some point the idea is to have a full native parser implemented to deal
|
||||
with my syntax, but meanwhile I will be collaborating with external parsers,
|
||||
particularly the ones provided by Pandoc and/or Lunamark.
|
||||
"
|
||||
Class {
|
||||
#name : #Markdown,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'contents',
|
||||
'file'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
Markdown class >> fromFile: aFileReference [
|
||||
^ self new fromFile: aFileReference
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown class >> yamlMetadataDelimiter [
|
||||
^ '---'
|
||||
|
||||
]
|
||||
|
||||
{ #category : #operation }
|
||||
Markdown >> commentYAMLMetadata [
|
||||
| newContents |
|
||||
self detectYAMLMetadata ifFalse: [ ^ self ].
|
||||
newContents := '' writeStream.
|
||||
newContents nextPutAll: '<!--@yaml:'; crlf.
|
||||
newContents nextPutAll: self extractYAMLMetadata.
|
||||
newContents nextPutAll: String cr.
|
||||
newContents nextPutAll: '-->'; crlf.
|
||||
(self lines copyFrom: self locateYAMLMetadataClosing + 2 to: self lines size) do: [ :line |
|
||||
newContents nextPutAll: line; crlf ].
|
||||
self contents: newContents contents.
|
||||
^ self contents
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> containsYAMLMetadataClosing [
|
||||
^ self locateYAMLMetadataClosing > 0
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> contents [
|
||||
^ contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> contents: anObject [
|
||||
contents := anObject
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> detectYAMLMetadata [
|
||||
| lines |
|
||||
lines := self lines.
|
||||
^ self startsWithYAMLMetadataDelimiter
|
||||
and: [ lines allButFirst
|
||||
detect: [ :currentLine | currentLine beginsWith: self class yamlMetadataDelimiter ]
|
||||
ifFound: [ ^ true ] ifNone: [ ^ false ] ]
|
||||
]
|
||||
|
||||
{ #category : #operation }
|
||||
Markdown >> exportMetadataAsJson [
|
||||
"TBD: Lua scripts should be checked and installed when missing. Maybe a shared location
|
||||
in '.local/share/Grafoscopio/Scripts' should be developed in the near future."
|
||||
| output luaScript |
|
||||
luaScript := FileLocator home / '.local/share/Brea/scripts/meta-to-json.lua'.
|
||||
Smalltalk platformName = 'unix' ifTrue: [
|
||||
OSSUnixSubprocess new
|
||||
workingDirectory: self file parent fullName;
|
||||
command: 'pandoc';
|
||||
arguments: { '--lua-filter=', luaScript fullName . self file basename };
|
||||
redirectStdout;
|
||||
redirectStdin;
|
||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
||||
output := process isSuccess
|
||||
ifTrue: [ outString ]
|
||||
ifFalse: [ errString ]
|
||||
]].
|
||||
^ output correctAccentedCharacters
|
||||
]
|
||||
|
||||
{ #category : #operation }
|
||||
Markdown >> exportMetadataAsYaml [
|
||||
| exportedFile |
|
||||
exportedFile := FileLocator temp / 'metadata.yaml'.
|
||||
MarkupFile exportAsFileOn: exportedFile containing: self yamlMetadataAsString.
|
||||
^ exportedFile
|
||||
]
|
||||
|
||||
{ #category : #operation }
|
||||
Markdown >> extractYAMLMetadata [
|
||||
| output yamlLines |
|
||||
self detectYAMLMetadata ifFalse: [ ^ nil ].
|
||||
yamlLines := self lines copyFrom: 2 to: (self locateYAMLMetadataClosing).
|
||||
output := '' writeStream.
|
||||
yamlLines do: [ :line |
|
||||
output
|
||||
nextPutAll: line;
|
||||
nextPut: Character cr. ].
|
||||
^ output contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> file [
|
||||
^ file
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> file: aFileReference [
|
||||
"I store the origen/destination of the Markdown contents."
|
||||
file := aFileReference
|
||||
]
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
Markdown >> fromFile: aFileReference [
|
||||
self contents: aFileReference contents.
|
||||
self file: aFileReference
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> lines [
|
||||
^ self contents lines.
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> locateYAMLMetadataClosing [
|
||||
"I return the line where the closing of the YAML metadata occurs or 0 if no closing is found."
|
||||
| result |
|
||||
self startsWithYAMLMetadataDelimiter ifFalse: [ ^ self ].
|
||||
result := 0.
|
||||
self lines allButFirst doWithIndex: [ :currentLine :i |
|
||||
(currentLine beginsWith: self class yamlMetadataDelimiter) ifTrue: [ result := i ]].
|
||||
^ result
|
||||
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Markdown >> metadata [
|
||||
| rawMeta |
|
||||
rawMeta := PPYAMLGrammar new parse: self extractYAMLMetadata.
|
||||
rawMeta associationsDo: [ :assoc |
|
||||
assoc value = 'false' ifTrue: [ assoc value: false ].
|
||||
assoc value = 'true' ifTrue: [ assoc value: true ] ].
|
||||
^ rawMeta
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> startsWithYAMLMetadataDelimiter [
|
||||
^ self lines first beginsWith: self class yamlMetadataDelimiter
|
||||
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
Markdown >> yamlMetadataAsString [
|
||||
| output |
|
||||
self extractYAMLMetadata ifNil: [ ^ nil ].
|
||||
output := String new writeStream.
|
||||
output nextPutAll: self class yamlMetadataDelimiter; cr.
|
||||
output nextPutAll: self extractYAMLMetadata.
|
||||
output nextPutAll: self class yamlMetadataDelimiter; cr.
|
||||
^ output contents.
|
||||
]
|
||||
|
@ -1,23 +1,23 @@
|
||||
"
|
||||
I model common operations made with several markup files.
|
||||
"
|
||||
Class {
|
||||
#name : #MarkupFile,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'file'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #persistence }
|
||||
MarkupFile class >> exportAsFileOn: aFileReferenceOrFileName containing: text [
|
||||
| file |
|
||||
file := aFileReferenceOrFileName asFileReference.
|
||||
file ensureDelete.
|
||||
file exists ifFalse: [ file ensureCreateFile ].
|
||||
file writeStreamDo: [ :stream |
|
||||
stream nextPutAll: text withInternetLineEndings].
|
||||
self inform: 'Exported as: ', String cr, file fullName.
|
||||
^ file
|
||||
]
|
||||
"
|
||||
I model common operations made with several markup files.
|
||||
"
|
||||
Class {
|
||||
#name : #MarkupFile,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'file'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #persistence }
|
||||
MarkupFile class >> exportAsFileOn: aFileReferenceOrFileName containing: text [
|
||||
| file |
|
||||
file := aFileReferenceOrFileName asFileReference.
|
||||
file ensureDelete.
|
||||
file exists ifFalse: [ file ensureCreateFile ].
|
||||
file writeStreamDo: [ :stream |
|
||||
stream nextPutAll: text withInternetLineEndings].
|
||||
self inform: 'Exported as: ', String cr, file fullName.
|
||||
^ file
|
||||
]
|
||||
|
@ -1,46 +1,46 @@
|
||||
"
|
||||
I provide some utilites to work with OrgMode files.
|
||||
|
||||
https://orgmode.org/
|
||||
"
|
||||
Class {
|
||||
#name : #OrgMode,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'contents',
|
||||
'file'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
OrgMode class >> fromFile: aFileReference [
|
||||
^ self new contents: aFileReference contents.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
OrgMode >> contents [
|
||||
^ contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
OrgMode >> contents: anObject [
|
||||
contents := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
OrgMode >> file [
|
||||
^ file
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
OrgMode >> file: anObject [
|
||||
file := anObject
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
OrgMode >> selectHeadlines [
|
||||
|
||||
^ self contents lines select: [ :line |
|
||||
line isOrgModeHeader ]
|
||||
]
|
||||
"
|
||||
I provide some utilites to work with OrgMode files.
|
||||
|
||||
https://orgmode.org/
|
||||
"
|
||||
Class {
|
||||
#name : #OrgMode,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'contents',
|
||||
'file'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
OrgMode class >> fromFile: aFileReference [
|
||||
^ self new contents: aFileReference contents.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
OrgMode >> contents [
|
||||
^ contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
OrgMode >> contents: anObject [
|
||||
contents := anObject
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
OrgMode >> file [
|
||||
^ file
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
OrgMode >> file: anObject [
|
||||
file := anObject
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
OrgMode >> selectHeadlines [
|
||||
|
||||
^ self contents lines select: [ :line |
|
||||
line isOrgModeHeader ]
|
||||
]
|
||||
|
@ -1,146 +1,146 @@
|
||||
"
|
||||
I model the interaction between Pandoc and Grafoscopio.
|
||||
"
|
||||
Class {
|
||||
#name : #Pandoc,
|
||||
#superclass : #Object,
|
||||
#classInstVars : [
|
||||
'executable'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
Pandoc class >> downloadLuaFilters [
|
||||
self luaFilters do: [ :filter | | filterUrl |
|
||||
filterUrl := filter asUrl.
|
||||
(FileLocator temp asFileReference / (filterUrl segments last)) exists
|
||||
ifFalse: [
|
||||
ZnClient new
|
||||
url: filterUrl;
|
||||
downloadTo: FileLocator temp ] ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Pandoc class >> executable [
|
||||
^ executable ifNil: [ self executableLocation ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Pandoc class >> executable: aFileReference [
|
||||
executable := aFileReference
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Pandoc class >> executableLocation [
|
||||
| location |
|
||||
location := '/usr/bin/pandoc'.
|
||||
location asFileReference exists
|
||||
ifTrue: [ ^ location ]
|
||||
ifFalse: [ self definePandocExecutable ]
|
||||
]
|
||||
|
||||
{ #category : #utility }
|
||||
Pandoc class >> extractImagesInUnixFor: aFileReference withFilter: aLuaFilter [
|
||||
"I use Pandoc Lua scripting capabilities to extract al images links in aFileReference"
|
||||
|
||||
OSSUnixSubprocess new
|
||||
command: 'pandoc';
|
||||
arguments: {aFileReference fullName . '--lua-filter=',aLuaFilter fullName };
|
||||
redirectStdout;
|
||||
redirectStderr;
|
||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
||||
process isSuccess
|
||||
ifTrue: [
|
||||
^ ((Soup fromString: outString) findAllTags: 'td') collect: [ :each | each next ] ]
|
||||
ifFalse: [
|
||||
"OSSUnixProcessExitStatus has a nice #printOn: "
|
||||
Transcript show: 'Command exit with error status: ', process exitStatusInterpreter printString; cr.
|
||||
Transcript show: 'Stderr contents: ', errString.
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #converters }
|
||||
Pandoc class >> htmlToMarkdown: inputFile [
|
||||
|
||||
| outputFile |
|
||||
outputFile := FileLocator temp / 'body.md'.
|
||||
outputFile ensureDelete.
|
||||
outputFile ensureCreateFile.
|
||||
OSSUnixSubprocess new
|
||||
command: 'pandoc';
|
||||
arguments: {'-f'. 'html'. '-t'. 'markdown'. '--atx-headers'. inputFile fullName.
|
||||
'--output'. outputFile fullName };
|
||||
redirectStdout;
|
||||
redirectStderr;
|
||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
||||
process isSuccess
|
||||
ifTrue: [ ^ outputFile contents ]
|
||||
ifFalse: [ ^inputFile contents ]
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
Pandoc class >> listImagesFrom: aFileReference [
|
||||
"I provide a list of all images contained in aFile."
|
||||
|
||||
| filter commandString outputString |
|
||||
filter := FileLocator temp asFileReference / 'image-links.lua'.
|
||||
filter exists
|
||||
ifFalse: [ self downloadLuaFilters ].
|
||||
commandString := 'pandoc ' , aFileReference fullName
|
||||
, ' --lua-filter=' , filter fullName.
|
||||
^ self extractImagesInUnixFor: aFileReference withFilter: filter
|
||||
]
|
||||
|
||||
{ #category : #utility }
|
||||
Pandoc class >> luaFilters [
|
||||
"I define the location of set of scripts, that allows to change the default behaviour of Pandoc
|
||||
and/or the processing of supported markup languages.
|
||||
|
||||
For more information about Lua filters see:
|
||||
|
||||
https://pandoc.org/lua-filters.html
|
||||
"
|
||||
|
||||
| filters |
|
||||
filters := OrderedCollection new.
|
||||
filters
|
||||
add: 'http://mutabit.com/repos.fossil/dataweek/doc/tip/Artefactos/Scripts/image-links.lua'.
|
||||
^ filters
|
||||
]
|
||||
|
||||
{ #category : #converters }
|
||||
Pandoc class >> markdownToHtml: inputFile [
|
||||
|
||||
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ]) ifTrue: [ ^ self markdownToHtmlOnUnix: inputFile ].
|
||||
Smalltalk os isWindows ifTrue: [ ^ self markdownToHtmlOnWindows: inputFile ].
|
||||
]
|
||||
|
||||
{ #category : #converters }
|
||||
Pandoc class >> markdownToHtmlOnUnix: inputFile [
|
||||
|
||||
| outputFile |
|
||||
outputFile := FileLocator temp / (inputFile basenameWithoutExtension , '.html').
|
||||
outputFile ensureDelete.
|
||||
outputFile ensureCreateFile.
|
||||
OSSUnixSubprocess new
|
||||
command: 'pandoc';
|
||||
arguments: {'-f'. 'markdown+startnum+task_lists'. '-t'. 'html'. inputFile fullName.
|
||||
'--output'. outputFile fullName };
|
||||
redirectStdout;
|
||||
redirectStderr;
|
||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
||||
process isSuccess
|
||||
ifTrue: [ ^ outputFile ]
|
||||
ifFalse: [ ^ inputFile ]
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #converters }
|
||||
Pandoc class >> markdownToHtmlOnWindows: inputFile [
|
||||
|
||||
|
||||
^ LibC resultOfCommand: 'pandoc -f markdown+startnum+task_lists -t html', inputFile fullName.
|
||||
]
|
||||
"
|
||||
I model the interaction between Pandoc and Grafoscopio.
|
||||
"
|
||||
Class {
|
||||
#name : #Pandoc,
|
||||
#superclass : #Object,
|
||||
#classInstVars : [
|
||||
'executable'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
Pandoc class >> downloadLuaFilters [
|
||||
self luaFilters do: [ :filter | | filterUrl |
|
||||
filterUrl := filter asUrl.
|
||||
(FileLocator temp asFileReference / (filterUrl segments last)) exists
|
||||
ifFalse: [
|
||||
ZnClient new
|
||||
url: filterUrl;
|
||||
downloadTo: FileLocator temp ] ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Pandoc class >> executable [
|
||||
^ executable ifNil: [ self executableLocation ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Pandoc class >> executable: aFileReference [
|
||||
executable := aFileReference
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Pandoc class >> executableLocation [
|
||||
| location |
|
||||
location := '/usr/bin/pandoc'.
|
||||
location asFileReference exists
|
||||
ifTrue: [ ^ location ]
|
||||
ifFalse: [ self definePandocExecutable ]
|
||||
]
|
||||
|
||||
{ #category : #utility }
|
||||
Pandoc class >> extractImagesInUnixFor: aFileReference withFilter: aLuaFilter [
|
||||
"I use Pandoc Lua scripting capabilities to extract al images links in aFileReference"
|
||||
|
||||
OSSUnixSubprocess new
|
||||
command: 'pandoc';
|
||||
arguments: {aFileReference fullName . '--lua-filter=',aLuaFilter fullName };
|
||||
redirectStdout;
|
||||
redirectStderr;
|
||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
||||
process isSuccess
|
||||
ifTrue: [
|
||||
^ ((Soup fromString: outString) findAllTags: 'td') collect: [ :each | each next ] ]
|
||||
ifFalse: [
|
||||
"OSSUnixProcessExitStatus has a nice #printOn: "
|
||||
Transcript show: 'Command exit with error status: ', process exitStatusInterpreter printString; cr.
|
||||
Transcript show: 'Stderr contents: ', errString.
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #converters }
|
||||
Pandoc class >> htmlToMarkdown: inputFile [
|
||||
|
||||
| outputFile |
|
||||
outputFile := FileLocator temp / 'body.md'.
|
||||
outputFile ensureDelete.
|
||||
outputFile ensureCreateFile.
|
||||
OSSUnixSubprocess new
|
||||
command: 'pandoc';
|
||||
arguments: {'-f'. 'html'. '-t'. 'markdown'. '--atx-headers'. inputFile fullName.
|
||||
'--output'. outputFile fullName };
|
||||
redirectStdout;
|
||||
redirectStderr;
|
||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
||||
process isSuccess
|
||||
ifTrue: [ ^ outputFile contents ]
|
||||
ifFalse: [ ^inputFile contents ]
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
Pandoc class >> listImagesFrom: aFileReference [
|
||||
"I provide a list of all images contained in aFile."
|
||||
|
||||
| filter commandString outputString |
|
||||
filter := FileLocator temp asFileReference / 'image-links.lua'.
|
||||
filter exists
|
||||
ifFalse: [ self downloadLuaFilters ].
|
||||
commandString := 'pandoc ' , aFileReference fullName
|
||||
, ' --lua-filter=' , filter fullName.
|
||||
^ self extractImagesInUnixFor: aFileReference withFilter: filter
|
||||
]
|
||||
|
||||
{ #category : #utility }
|
||||
Pandoc class >> luaFilters [
|
||||
"I define the location of set of scripts, that allows to change the default behaviour of Pandoc
|
||||
and/or the processing of supported markup languages.
|
||||
|
||||
For more information about Lua filters see:
|
||||
|
||||
https://pandoc.org/lua-filters.html
|
||||
"
|
||||
|
||||
| filters |
|
||||
filters := OrderedCollection new.
|
||||
filters
|
||||
add: 'http://mutabit.com/repos.fossil/dataweek/doc/tip/Artefactos/Scripts/image-links.lua'.
|
||||
^ filters
|
||||
]
|
||||
|
||||
{ #category : #converters }
|
||||
Pandoc class >> markdownToHtml: inputFile [
|
||||
|
||||
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ]) ifTrue: [ ^ self markdownToHtmlOnUnix: inputFile ].
|
||||
Smalltalk os isWindows ifTrue: [ ^ self markdownToHtmlOnWindows: inputFile ].
|
||||
]
|
||||
|
||||
{ #category : #converters }
|
||||
Pandoc class >> markdownToHtmlOnUnix: inputFile [
|
||||
|
||||
| outputFile |
|
||||
outputFile := FileLocator temp / (inputFile basenameWithoutExtension , '.html').
|
||||
outputFile ensureDelete.
|
||||
outputFile ensureCreateFile.
|
||||
OSSUnixSubprocess new
|
||||
command: 'pandoc';
|
||||
arguments: {'-f'. 'markdown+startnum+task_lists'. '-t'. 'html'. inputFile fullName.
|
||||
'--output'. outputFile fullName };
|
||||
redirectStdout;
|
||||
redirectStderr;
|
||||
runAndWaitOnExitDo: [ :process :outString :errString |
|
||||
process isSuccess
|
||||
ifTrue: [ ^ outputFile ]
|
||||
ifFalse: [ ^ inputFile ]
|
||||
]
|
||||
]
|
||||
|
||||
{ #category : #converters }
|
||||
Pandoc class >> markdownToHtmlOnWindows: inputFile [
|
||||
|
||||
"ToDo: This command still doesn't receive any arguments."
|
||||
^ (LibC resultOfCommand: 'pandoc ', inputFile fullName) correctAccentedCharacters.
|
||||
]
|
||||
|
@ -1,178 +1,178 @@
|
||||
"
|
||||
I model a work (book, booklet, web page, etc) in Pandoc, its table of contents, its metadata file to
|
||||
control exportation and other elements.
|
||||
|
||||
I can be used to improve reproductibility of published works that use Pandoc.
|
||||
|
||||
By default it is supposed that a root folder contains the set of folders, organized by
|
||||
language (following the ISO 639-1 two letters convetion) where the contents of the work
|
||||
and their translations are located.
|
||||
Chapters, subchapters, sections and subsections are contained there as Markdown files
|
||||
and its order is stated as a ordered dictionary for each language.
|
||||
A YAML metadata block is used in each file to map translations between files and languages
|
||||
and other sources, synchronizations and meta data.
|
||||
"
|
||||
Class {
|
||||
#name : #PandocWork,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'language',
|
||||
'contents',
|
||||
'metadataFiles',
|
||||
'rootFolder',
|
||||
'manifests'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> buildManifestFileForLanguage: anISOCode on: aFileName [
|
||||
"anISOCode is the ISO 639-1 two letters language code"
|
||||
|
||||
| manifestFile |
|
||||
manifestFile := (self rootFolder / anISOCode / 'manifests' / aFileName ) asFileReference ensureCreateFile.
|
||||
self buildManifestForLanguage: 'es'.
|
||||
GrafoscopioUtils exportAsSton: (self manifests at: anISOCode) on: manifestFile.
|
||||
^ manifestFile
|
||||
]
|
||||
|
||||
{ #category : #utlity }
|
||||
PandocWork >> buildManifestForLanguage: anISOCode [
|
||||
self
|
||||
metadataManifestForLanguage: anISOCode;
|
||||
contentsManifestForLanguage: anISOCode.
|
||||
^ self manifests
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> contents [
|
||||
^ contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> contents: anOrderedDictionary [
|
||||
"I model the table of contents of the work.
|
||||
The key of the dictionary is the folder, inside the language folder (see the language variable)
|
||||
where the files are stored, and the value is and ordered collection of the files on such folder
|
||||
which are part ot the exported result, without the file extension (by default is supposed to be '.md')"
|
||||
contents := anOrderedDictionary
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> contentsManifest [
|
||||
|
||||
| checksums |
|
||||
checksums := OrderedDictionary new.
|
||||
self contents keysDo: [ :folder |
|
||||
(self contents at: folder) do: [ :fileName | | keyName contentFile |
|
||||
keyName := fileName, self defaultFileExtension.
|
||||
contentFile := self rootFolder / self language / folder / keyName.
|
||||
checksums at: keyName put: (GrafoscopioUtils checksumFor: contentFile)].
|
||||
self manifest at: folder put: checksums ].
|
||||
^ self manifest
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> contentsManifestForLanguage: anISOCode [
|
||||
|
||||
| checksums |
|
||||
checksums := OrderedDictionary new.
|
||||
self contents keysDo: [ :folder |
|
||||
(self contents at: folder) do: [ :fileName | | keyName contentFile |
|
||||
keyName := fileName, self defaultFileExtension.
|
||||
contentFile := self rootFolder / anISOCode / folder / keyName.
|
||||
checksums at: keyName put: (GrafoscopioUtils checksumFor: contentFile)].
|
||||
(self manifestForLanguage: anISOCode)
|
||||
add: {folder -> checksums} asOrderedDictionary ].
|
||||
^ self manifests at: anISOCode
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> defaultFileExtension [
|
||||
^ '.md'
|
||||
|
||||
|
||||
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> language [
|
||||
^ language
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> language: aISOLangString [
|
||||
"I model the lanaguage of a work as a ISO 639-1 two letters string.
|
||||
I used to stablish the folder where the content is stored, following the convention a folder
|
||||
by language."
|
||||
language := aISOLangString
|
||||
]
|
||||
|
||||
{ #category : #utlity }
|
||||
PandocWork >> manifestForLanguage: anISOCode [
|
||||
self manifests at: anISOCode ifAbsent: [
|
||||
self manifests
|
||||
at: anISOCode put: OrderedCollection new;
|
||||
yourself].
|
||||
^ self manifests at: anISOCode
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> manifests [
|
||||
^ manifests ifNil: [ ^ manifests := OrderedDictionary new ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> manifests: anOrderedDictionary [
|
||||
manifests := anOrderedDictionary
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> metadataFiles [
|
||||
^ metadataFiles
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> metadataFiles: aCollection [
|
||||
"I model the YAML metadata files that are used to control the output of the exportation.
|
||||
I can have several files, controlling several outputs, one for PDF, one for HTML, one for EPUB
|
||||
and so on.
|
||||
This should be stated in the name of the metadatafile and by default will be controlling PDF
|
||||
output."
|
||||
metadataFiles := aCollection
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> metadataManifest [
|
||||
|
||||
| languageFolder |
|
||||
languageFolder := self language.
|
||||
"Could the similar parts of this and contentsManifest be refactored?"
|
||||
self metadataFiles do: [ :fileName | | contentFile |
|
||||
contentFile := self rootFolder / languageFolder / fileName.
|
||||
self manifest at: languageFolder put: {fileName -> (GrafoscopioUtils checksumFor: contentFile)} asOrderedDictionary ].
|
||||
^ self manifest
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> metadataManifestForLanguage: anISOCode [
|
||||
"anISOCode is the ISO 639-1 two letters language code"
|
||||
|
||||
"Could the similar parts of this and contentsManifest be refactored?"
|
||||
self metadataFiles do: [ :fileName | | contentFile |
|
||||
contentFile := self rootFolder / anISOCode / fileName.
|
||||
(self manifestForLanguage: anISOCode)
|
||||
add: {fileName -> (GrafoscopioUtils checksumFor: contentFile)} asOrderedDictionary].
|
||||
^ self manifests at: anISOCode
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> rootFolder [
|
||||
^ rootFolder
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> rootFolder: aFileReference [
|
||||
"I model the folder where the Markdown files are located."
|
||||
rootFolder := aFileReference
|
||||
]
|
||||
"
|
||||
I model a work (book, booklet, web page, etc) in Pandoc, its table of contents, its metadata file to
|
||||
control exportation and other elements.
|
||||
|
||||
I can be used to improve reproductibility of published works that use Pandoc.
|
||||
|
||||
By default it is supposed that a root folder contains the set of folders, organized by
|
||||
language (following the ISO 639-1 two letters convetion) where the contents of the work
|
||||
and their translations are located.
|
||||
Chapters, subchapters, sections and subsections are contained there as Markdown files
|
||||
and its order is stated as a ordered dictionary for each language.
|
||||
A YAML metadata block is used in each file to map translations between files and languages
|
||||
and other sources, synchronizations and meta data.
|
||||
"
|
||||
Class {
|
||||
#name : #PandocWork,
|
||||
#superclass : #Object,
|
||||
#instVars : [
|
||||
'language',
|
||||
'contents',
|
||||
'metadataFiles',
|
||||
'rootFolder',
|
||||
'manifests'
|
||||
],
|
||||
#category : #'Grafoscopio-Utils'
|
||||
}
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> buildManifestFileForLanguage: anISOCode on: aFileName [
|
||||
"anISOCode is the ISO 639-1 two letters language code"
|
||||
|
||||
| manifestFile |
|
||||
manifestFile := (self rootFolder / anISOCode / 'manifests' / aFileName ) asFileReference ensureCreateFile.
|
||||
self buildManifestForLanguage: 'es'.
|
||||
GrafoscopioUtils exportAsSton: (self manifests at: anISOCode) on: manifestFile.
|
||||
^ manifestFile
|
||||
]
|
||||
|
||||
{ #category : #utlity }
|
||||
PandocWork >> buildManifestForLanguage: anISOCode [
|
||||
self
|
||||
metadataManifestForLanguage: anISOCode;
|
||||
contentsManifestForLanguage: anISOCode.
|
||||
^ self manifests
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> contents [
|
||||
^ contents
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> contents: anOrderedDictionary [
|
||||
"I model the table of contents of the work.
|
||||
The key of the dictionary is the folder, inside the language folder (see the language variable)
|
||||
where the files are stored, and the value is and ordered collection of the files on such folder
|
||||
which are part ot the exported result, without the file extension (by default is supposed to be '.md')"
|
||||
contents := anOrderedDictionary
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> contentsManifest [
|
||||
|
||||
| checksums |
|
||||
checksums := OrderedDictionary new.
|
||||
self contents keysDo: [ :folder |
|
||||
(self contents at: folder) do: [ :fileName | | keyName contentFile |
|
||||
keyName := fileName, self defaultFileExtension.
|
||||
contentFile := self rootFolder / self language / folder / keyName.
|
||||
checksums at: keyName put: (GrafoscopioUtils checksumFor: contentFile)].
|
||||
self manifest at: folder put: checksums ].
|
||||
^ self manifest
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> contentsManifestForLanguage: anISOCode [
|
||||
|
||||
| checksums |
|
||||
checksums := OrderedDictionary new.
|
||||
self contents keysDo: [ :folder |
|
||||
(self contents at: folder) do: [ :fileName | | keyName contentFile |
|
||||
keyName := fileName, self defaultFileExtension.
|
||||
contentFile := self rootFolder / anISOCode / folder / keyName.
|
||||
checksums at: keyName put: (GrafoscopioUtils checksumFor: contentFile)].
|
||||
(self manifestForLanguage: anISOCode)
|
||||
add: {folder -> checksums} asOrderedDictionary ].
|
||||
^ self manifests at: anISOCode
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> defaultFileExtension [
|
||||
^ '.md'
|
||||
|
||||
|
||||
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> language [
|
||||
^ language
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> language: aISOLangString [
|
||||
"I model the lanaguage of a work as a ISO 639-1 two letters string.
|
||||
I used to stablish the folder where the content is stored, following the convention a folder
|
||||
by language."
|
||||
language := aISOLangString
|
||||
]
|
||||
|
||||
{ #category : #utlity }
|
||||
PandocWork >> manifestForLanguage: anISOCode [
|
||||
self manifests at: anISOCode ifAbsent: [
|
||||
self manifests
|
||||
at: anISOCode put: OrderedCollection new;
|
||||
yourself].
|
||||
^ self manifests at: anISOCode
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> manifests [
|
||||
^ manifests ifNil: [ ^ manifests := OrderedDictionary new ]
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> manifests: anOrderedDictionary [
|
||||
manifests := anOrderedDictionary
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> metadataFiles [
|
||||
^ metadataFiles
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> metadataFiles: aCollection [
|
||||
"I model the YAML metadata files that are used to control the output of the exportation.
|
||||
I can have several files, controlling several outputs, one for PDF, one for HTML, one for EPUB
|
||||
and so on.
|
||||
This should be stated in the name of the metadatafile and by default will be controlling PDF
|
||||
output."
|
||||
metadataFiles := aCollection
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> metadataManifest [
|
||||
|
||||
| languageFolder |
|
||||
languageFolder := self language.
|
||||
"Could the similar parts of this and contentsManifest be refactored?"
|
||||
self metadataFiles do: [ :fileName | | contentFile |
|
||||
contentFile := self rootFolder / languageFolder / fileName.
|
||||
self manifest at: languageFolder put: {fileName -> (GrafoscopioUtils checksumFor: contentFile)} asOrderedDictionary ].
|
||||
^ self manifest
|
||||
]
|
||||
|
||||
{ #category : #utilities }
|
||||
PandocWork >> metadataManifestForLanguage: anISOCode [
|
||||
"anISOCode is the ISO 639-1 two letters language code"
|
||||
|
||||
"Could the similar parts of this and contentsManifest be refactored?"
|
||||
self metadataFiles do: [ :fileName | | contentFile |
|
||||
contentFile := self rootFolder / anISOCode / fileName.
|
||||
(self manifestForLanguage: anISOCode)
|
||||
add: {fileName -> (GrafoscopioUtils checksumFor: contentFile)} asOrderedDictionary].
|
||||
^ self manifests at: anISOCode
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> rootFolder [
|
||||
^ rootFolder
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
PandocWork >> rootFolder: aFileReference [
|
||||
"I model the folder where the Markdown files are located."
|
||||
rootFolder := aFileReference
|
||||
]
|
||||
|
@ -1,35 +1,38 @@
|
||||
Extension { #name : #String }
|
||||
|
||||
{ #category : #'*Grafoscopio-Utils' }
|
||||
String >> asCapitalizedPhrase [
|
||||
"I convert phrases like 'THIS IS A PHRASE' into 'This is a Phrase'."
|
||||
|
||||
^ Character space join: (self substrings collect: [:each | each asLowercase capitalized ])
|
||||
]
|
||||
|
||||
{ #category : #'*Grafoscopio-Utils' }
|
||||
String >> asDashedLowercase [
|
||||
"I convert phrases like 'This is a phrase' into 'this-is-a-phrase'."
|
||||
|
||||
^ '-' join: (self substrings collect: [:each | each asLowercase ])
|
||||
]
|
||||
|
||||
{ #category : #'*Grafoscopio-Utils' }
|
||||
String >> correctAccentedCharacters [
|
||||
|
||||
| output |
|
||||
output := self
|
||||
copyReplaceAll: 'ó' with: 'ó'.
|
||||
output := output copyReplaceAll: 'á' with: 'á'.
|
||||
output := output copyReplaceAll: 'é' with: 'é'.
|
||||
output := output copyReplaceAll: 'Ã' with: 'í'.
|
||||
output := output copyReplaceAll: 'ú' with: 'ú'.
|
||||
^ output
|
||||
]
|
||||
|
||||
{ #category : #'*Grafoscopio-Utils' }
|
||||
String >> isOrgModeHeader [
|
||||
^ self beginsWithAnyOf: #('* ' '** ' '*** ' '**** ' '***** ' '***** ')
|
||||
|
||||
|
||||
]
|
||||
Extension { #name : #String }
|
||||
|
||||
{ #category : #'*Grafoscopio-Utils' }
|
||||
String >> asCapitalizedPhrase [
|
||||
"I convert phrases like 'THIS IS A PHRASE' into 'This is a Phrase'."
|
||||
|
||||
^ Character space join: (self substrings collect: [:each | each asLowercase capitalized ])
|
||||
]
|
||||
|
||||
{ #category : #'*Grafoscopio-Utils' }
|
||||
String >> asDashedLowercase [
|
||||
"I convert phrases like 'This is a phrase' into 'this-is-a-phrase'."
|
||||
|
||||
^ '-' join: (self substrings collect: [:each | each asLowercase ])
|
||||
]
|
||||
|
||||
{ #category : #'*Grafoscopio-Utils' }
|
||||
String >> correctAccentedCharacters [
|
||||
|
||||
| output |
|
||||
output := self
|
||||
copyReplaceAll: 'ó' with: 'ó'.
|
||||
output := output copyReplaceAll: 'á' with: 'á'.
|
||||
output := output copyReplaceAll: 'é' with: 'é'.
|
||||
output := output copyReplaceAll: 'Ã' with: 'í'.
|
||||
output := output copyReplaceAll: 'ú' with: 'ú'.
|
||||
output := output copyReplaceAll: 'ñ' with: 'ñ'.
|
||||
output := output copyReplaceAll: 'â' with: '“'.
|
||||
output := output copyReplaceAll: 'â' with: '”'.
|
||||
^ output
|
||||
]
|
||||
|
||||
{ #category : #'*Grafoscopio-Utils' }
|
||||
String >> isOrgModeHeader [
|
||||
^ self beginsWithAnyOf: #('* ' '** ' '*** ' '**** ' '***** ' '***** ')
|
||||
|
||||
|
||||
]
|
||||
|
@ -1 +1 @@
|
||||
Package { #name : #'Grafoscopio-Utils' }
|
||||
Package { #name : #'Grafoscopio-Utils' }
|
||||
|
Loading…
Reference in New Issue
Block a user