fronde 0.3.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/fronde +15 -30
- data/lib/ext/nil_time.rb +25 -0
- data/lib/ext/r18n.rb +37 -0
- data/lib/ext/time.rb +39 -0
- data/lib/ext/time_no_time.rb +23 -0
- data/lib/fronde/cli/commands.rb +97 -104
- data/lib/fronde/cli/data/Rakefile +8 -0
- data/lib/fronde/cli/data/config.yml +13 -0
- data/lib/fronde/cli/data/gitignore +6 -0
- data/lib/fronde/cli/data/zsh_completion +37 -0
- data/lib/fronde/cli/helpers.rb +55 -0
- data/lib/fronde/cli/opt_parse.rb +140 -0
- data/lib/fronde/cli/throbber.rb +110 -0
- data/lib/fronde/cli.rb +42 -42
- data/lib/fronde/config/data/org-config.el +25 -0
- data/lib/fronde/config/data/ox-fronde.el +158 -0
- data/lib/fronde/config/data/themes/umaneti/css/htmlize.css +364 -0
- data/lib/fronde/config/data/themes/umaneti/css/style.css +250 -0
- data/lib/fronde/config/data/themes/umaneti/img/bottom.png +0 -0
- data/lib/fronde/config/data/themes/umaneti/img/content.png +0 -0
- data/lib/fronde/config/data/themes/umaneti/img/tic.png +0 -0
- data/lib/fronde/config/data/themes/umaneti/img/top.png +0 -0
- data/lib/fronde/config/helpers.rb +62 -0
- data/lib/fronde/config/lisp.rb +80 -0
- data/lib/fronde/config.rb +148 -98
- data/lib/fronde/emacs.rb +23 -20
- data/lib/fronde/index/atom_generator.rb +55 -66
- data/lib/fronde/index/data/all_tags.org +19 -0
- data/lib/fronde/index/data/template.org +26 -0
- data/lib/fronde/index/data/template.xml +37 -0
- data/lib/fronde/index/org_generator.rb +72 -88
- data/lib/fronde/index.rb +57 -86
- data/lib/fronde/org/file.rb +299 -0
- data/lib/fronde/org/file_extracter.rb +101 -0
- data/lib/fronde/org.rb +105 -0
- data/lib/fronde/preview.rb +43 -39
- data/lib/fronde/slug.rb +54 -0
- data/lib/fronde/source/gemini.rb +34 -0
- data/lib/fronde/source/html.rb +67 -0
- data/lib/fronde/source.rb +209 -0
- data/lib/fronde/sync/neocities.rb +220 -0
- data/lib/fronde/sync/rsync.rb +46 -0
- data/lib/fronde/sync.rb +32 -0
- data/lib/fronde/templater.rb +101 -71
- data/lib/fronde/version.rb +1 -1
- data/lib/tasks/cli.rake +33 -0
- data/lib/tasks/org.rake +58 -43
- data/lib/tasks/site.rake +66 -31
- data/lib/tasks/sync.rake +37 -40
- data/lib/tasks/tags.rake +11 -7
- data/locales/en.yml +61 -14
- data/locales/fr.yml +69 -14
- metadata +77 -95
- data/lib/fronde/config/lisp_config.rb +0 -340
- data/lib/fronde/config/org-config.el +0 -19
- data/lib/fronde/config/ox-fronde.el +0 -121
- data/lib/fronde/org_file/class_methods.rb +0 -72
- data/lib/fronde/org_file/extracter.rb +0 -72
- data/lib/fronde/org_file/htmlizer.rb +0 -43
- data/lib/fronde/org_file.rb +0 -298
- data/lib/fronde/utils.rb +0 -229
@@ -1,340 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
require 'open-uri'
|
5
|
-
require 'fronde/version'
|
6
|
-
|
7
|
-
def fetch_org_version
|
8
|
-
# Retrieve last org version from git repository tags page.
|
9
|
-
tag_rx = Regexp.new(
|
10
|
-
'<a href=\'/cgit/emacs/org-mode.git/tag/\?h=' \
|
11
|
-
'(?<tag>release_(?<number>[^\']+))\'>\k<tag></a>'
|
12
|
-
)
|
13
|
-
versions = URI(
|
14
|
-
'https://git.savannah.gnu.org/cgit/emacs/org-mode.git/refs/'
|
15
|
-
).open.readlines.map do |line|
|
16
|
-
line.match(tag_rx) { |matchdata| matchdata[:number] }
|
17
|
-
end
|
18
|
-
versions.compact.first
|
19
|
-
end
|
20
|
-
|
21
|
-
module Fronde
|
22
|
-
# This module contains utilitary methods to ease ~org-config.el~
|
23
|
-
# file generation
|
24
|
-
module LispConfig
|
25
|
-
# Fetch and return the last published version of Org.
|
26
|
-
#
|
27
|
-
# @return [String] the new x.x.x version string of Org
|
28
|
-
def org_last_version
|
29
|
-
return @org_version if @org_version
|
30
|
-
if File.exist?('var/tmp/last_org_version')
|
31
|
-
@org_version = File.read('var/tmp/last_org_version')
|
32
|
-
return @org_version
|
33
|
-
end
|
34
|
-
@org_version = fetch_org_version
|
35
|
-
FileUtils.mkdir_p 'var/tmp'
|
36
|
-
File.write('var/tmp/last_org_version', @org_version)
|
37
|
-
@org_version
|
38
|
-
end
|
39
|
-
|
40
|
-
# Generate emacs lisp configuration file for Org and write it.
|
41
|
-
#
|
42
|
-
# This method saves the generated configuration in the file
|
43
|
-
# ~org-config.el~ at the root of your project, overwriting it if it
|
44
|
-
# existed already.
|
45
|
-
#
|
46
|
-
# @return [Integer] the length written (as returned by the
|
47
|
-
# underlying ~File.write~ method call)
|
48
|
-
# rubocop:disable Metrics/MethodLength
|
49
|
-
def write_org_lisp_config(with_tags: false)
|
50
|
-
projects = org_generate_projects(with_tags: with_tags)
|
51
|
-
workdir = Dir.pwd
|
52
|
-
content = File.read(File.expand_path('./org-config.el', __dir__))
|
53
|
-
.gsub('__VERSION__', Fronde::VERSION)
|
54
|
-
.gsub('__WORK_DIR__', workdir)
|
55
|
-
.gsub('__FRONDE_DIR__', __dir__)
|
56
|
-
.gsub('__ORG_VER__', org_last_version)
|
57
|
-
.gsub(
|
58
|
-
'__ALL_PROJECTS__',
|
59
|
-
projects.values.join("\n ")
|
60
|
-
)
|
61
|
-
.gsub('__THEME_CONFIG__', org_default_theme_config)
|
62
|
-
.gsub('__ALL_PROJECTS_NAMES__', project_names(projects))
|
63
|
-
.gsub('__LONG_DATE_FMT__', r18n_full_datetime_format)
|
64
|
-
.gsub('__AUTHOR_EMAIL__', get('author_email', ''))
|
65
|
-
.gsub('__AUTHOR_NAME__', get('author'))
|
66
|
-
FileUtils.mkdir_p "#{workdir}/var/lib"
|
67
|
-
File.write("#{workdir}/var/lib/org-config.el", content)
|
68
|
-
end
|
69
|
-
# rubocop:enable Metrics/MethodLength
|
70
|
-
|
71
|
-
# Generate emacs directory variables file.
|
72
|
-
#
|
73
|
-
# This method generate the file ~.dir-locals.el~, which is
|
74
|
-
# responsible to load fronde Org settings when visiting an Org file
|
75
|
-
# of this fronde instance.
|
76
|
-
#
|
77
|
-
# @return [Integer] the length written (as returned by the
|
78
|
-
# underlying ~File.write~ method call)
|
79
|
-
def write_dir_locals
|
80
|
-
workdir = Dir.pwd
|
81
|
-
# rubocop:disable Layout/LineLength
|
82
|
-
File.write(
|
83
|
-
"#{workdir}/.dir-locals.el",
|
84
|
-
"((org-mode . ((eval . (load-file \"#{workdir}/var/lib/org-config.el\")))))"
|
85
|
-
)
|
86
|
-
# rubocop:enable Layout/LineLength
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def r18n_full_datetime_format
|
92
|
-
locale = R18n.get.locale
|
93
|
-
date_fmt = R18n.t.fronde.index.full_date_format(
|
94
|
-
date: locale.full_format
|
95
|
-
)
|
96
|
-
date_fmt = locale.year_format.sub('_', date_fmt)
|
97
|
-
time_fmt = locale.time_format.delete('_').strip
|
98
|
-
R18n.t.fronde.index.full_date_with_time_format(
|
99
|
-
date: date_fmt, time: time_fmt
|
100
|
-
)
|
101
|
-
end
|
102
|
-
|
103
|
-
def ruby_to_lisp_boolean(value)
|
104
|
-
return 't' if value == true
|
105
|
-
'nil'
|
106
|
-
end
|
107
|
-
|
108
|
-
def project_names(projects)
|
109
|
-
names = projects.keys.map do |p|
|
110
|
-
["\"#{p}\"", "\"#{p}-assets\""]
|
111
|
-
end.flatten
|
112
|
-
names << "\"theme-#{get('theme')}\"" unless get('theme') == 'default'
|
113
|
-
sources.each do |s|
|
114
|
-
# Default theme defined in settings is already included
|
115
|
-
next unless s['theme'] && s['theme'] != get('theme')
|
116
|
-
# Never include theme named 'default' as it does not rely on any
|
117
|
-
# file to export.
|
118
|
-
next if s['theme'] == 'default'
|
119
|
-
theme = "\"theme-#{s['theme']}\""
|
120
|
-
next if names.include? theme
|
121
|
-
names << theme
|
122
|
-
end
|
123
|
-
names.join(' ')
|
124
|
-
end
|
125
|
-
|
126
|
-
# Return the full path to the publication path of a given project
|
127
|
-
# configuration.
|
128
|
-
#
|
129
|
-
# @param project [Hash] a project configuration (as extracted from
|
130
|
-
# the ~sources~ key)
|
131
|
-
# @return [String] the full path to the target dir of this project
|
132
|
-
def publication_path(project)
|
133
|
-
publish_in = [Dir.pwd]
|
134
|
-
if project['type'] == 'gemini'
|
135
|
-
publish_in << get('gemini_public_folder', 'public_gmi')
|
136
|
-
else
|
137
|
-
publish_in << get('public_folder')
|
138
|
-
end
|
139
|
-
publish_in << project['target'] unless project['target'] == '.'
|
140
|
-
publish_in.join('/')
|
141
|
-
end
|
142
|
-
|
143
|
-
# Return the publication function needed for a given project
|
144
|
-
# configuration.
|
145
|
-
#
|
146
|
-
# @param project [Hash] a project configuration (as extracted from
|
147
|
-
# the ~sources~ key)
|
148
|
-
# @return [String] the org publication function name
|
149
|
-
def publication_function(project)
|
150
|
-
case project['type']
|
151
|
-
when 'gemini'
|
152
|
-
'org-gmi-publish-to-gemini'
|
153
|
-
else
|
154
|
-
'org-html-publish-to-html'
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def org_project(project_name, opts)
|
159
|
-
shared_lines = org_project_shared_lines(opts)
|
160
|
-
project_config = [
|
161
|
-
org_project_config(project_name, opts, shared_lines),
|
162
|
-
org_assets_config(project_name, shared_lines)
|
163
|
-
]
|
164
|
-
themeconf = org_theme_config(opts['theme'])
|
165
|
-
project_config << themeconf unless themeconf == ''
|
166
|
-
project_config.join("\n ")
|
167
|
-
end
|
168
|
-
|
169
|
-
def org_default_html_postamble
|
170
|
-
<<~POSTAMBLE
|
171
|
-
<p><span class="author">#{R18n.t.fronde.org.postamble.written_by}</span>
|
172
|
-
#{R18n.t.fronde.org.postamble.with_emacs_html}</p>
|
173
|
-
<p class="date">#{R18n.t.fronde.org.postamble.last_modification}</p>
|
174
|
-
<p class="validation">%v</p>
|
175
|
-
POSTAMBLE
|
176
|
-
end
|
177
|
-
|
178
|
-
def org_default_gemini_postamble
|
179
|
-
format(
|
180
|
-
"📅 %<date>s\n📝 %<author>s %<creator>s",
|
181
|
-
author: R18n.t.fronde.org.postamble.written_by,
|
182
|
-
creator: R18n.t.fronde.org.postamble.with_emacs,
|
183
|
-
date: R18n.t.fronde.org.postamble.last_modification
|
184
|
-
)
|
185
|
-
end
|
186
|
-
|
187
|
-
def org_default_html_head
|
188
|
-
<<~HTMLHEAD
|
189
|
-
<link rel="stylesheet" type="text/css" media="screen"
|
190
|
-
href="__DOMAIN__/assets/__THEME__/css/style.css">
|
191
|
-
<link rel="stylesheet" type="text/css" media="screen"
|
192
|
-
href="__DOMAIN__/assets/__THEME__/css/htmlize.css">
|
193
|
-
__ATOM_FEED__
|
194
|
-
HTMLHEAD
|
195
|
-
end
|
196
|
-
|
197
|
-
def org_default_html_options(project)
|
198
|
-
defaults = {
|
199
|
-
'html-postamble' => org_default_html_postamble,
|
200
|
-
'html-head' => '__ATOM_FEED__',
|
201
|
-
'html-head-include-default-style' => 't',
|
202
|
-
'html-head-include-scripts' => 't'
|
203
|
-
}
|
204
|
-
curtheme = project['theme'] || get('theme')
|
205
|
-
return defaults if curtheme.nil? || curtheme == 'default'
|
206
|
-
defaults['html-head'] = org_default_html_head
|
207
|
-
defaults['html-head-include-default-style'] = 'nil'
|
208
|
-
defaults['html-head-include-scripts'] = 'nil'
|
209
|
-
defaults
|
210
|
-
end
|
211
|
-
|
212
|
-
def org_publish_options(project)
|
213
|
-
defaults = {
|
214
|
-
'section-numbers' => 'nil',
|
215
|
-
'with-toc' => 'nil'
|
216
|
-
}
|
217
|
-
if project['type'] == 'gemini'
|
218
|
-
defaults['gemini-postamble'] = org_default_gemini_postamble
|
219
|
-
else
|
220
|
-
defaults.merge!(
|
221
|
-
org_default_html_options(project),
|
222
|
-
get('org-html', {}),
|
223
|
-
project['org-html'] || {}
|
224
|
-
)
|
225
|
-
end
|
226
|
-
defaults.merge(project['org-options'] || {})
|
227
|
-
end
|
228
|
-
|
229
|
-
def expand_vars_in_html_head(head, project)
|
230
|
-
curtheme = project['theme'] || get('theme')
|
231
|
-
# Head may be frozen when coming from settings
|
232
|
-
head = head.gsub('__THEME__', curtheme)
|
233
|
-
.gsub('__DOMAIN__', get('domain'))
|
234
|
-
return head.gsub('__ATOM_FEED__', '') unless project['is_blog']
|
235
|
-
atomfeed = <<~ATOMFEED
|
236
|
-
<link rel="alternate" type="application/atom+xml" title="Atom 1.0"
|
237
|
-
href="#{get('domain')}/feeds/index.xml" />
|
238
|
-
ATOMFEED
|
239
|
-
head.gsub('__ATOM_FEED__', atomfeed)
|
240
|
-
end
|
241
|
-
|
242
|
-
def cast_lisp_value(value)
|
243
|
-
return 't' if value.is_a?(TrueClass)
|
244
|
-
return 'nil' if value.nil? || value.is_a?(FalseClass)
|
245
|
-
value.strip.gsub(/"/, '\"')
|
246
|
-
end
|
247
|
-
|
248
|
-
def build_project_org_headers(project)
|
249
|
-
orgtplopts = org_publish_options(project)
|
250
|
-
lisp_keywords = ['t', 'nil', '1', '-1', '0'].freeze
|
251
|
-
orgtplopts.map do |k, v|
|
252
|
-
v = expand_vars_in_html_head(v, project) if k == 'html-head'
|
253
|
-
val = cast_lisp_value(v)
|
254
|
-
if lisp_keywords.include? val
|
255
|
-
":#{k} #{val}"
|
256
|
-
else
|
257
|
-
":#{k} \"#{val}\""
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
def org_generate_projects(with_tags: false)
|
263
|
-
projects = {}
|
264
|
-
projects_sources = sources
|
265
|
-
if with_tags
|
266
|
-
tags_conf = build_source('tags')
|
267
|
-
tags_conf['recursive'] = false
|
268
|
-
projects_sources << tags_conf
|
269
|
-
end
|
270
|
-
projects_sources.each do |opts|
|
271
|
-
opts['org_headers'] = build_project_org_headers(opts)
|
272
|
-
projects[opts['name']] = org_project(opts['name'], opts)
|
273
|
-
end
|
274
|
-
projects
|
275
|
-
end
|
276
|
-
|
277
|
-
def org_default_theme_config
|
278
|
-
theme_config = org_theme_config(get('theme'))
|
279
|
-
return theme_config if theme_config == ''
|
280
|
-
format("\n %<conf>s", conf: theme_config)
|
281
|
-
end
|
282
|
-
|
283
|
-
def org_theme_config(theme)
|
284
|
-
return '' if theme.nil? || theme == 'default'
|
285
|
-
workdir = Dir.pwd
|
286
|
-
[
|
287
|
-
format('("theme-%<theme>s"', theme: theme),
|
288
|
-
format(' :base-directory "%<wd>s/themes/%<theme>s"',
|
289
|
-
wd: workdir, theme: theme),
|
290
|
-
# rubocop:disable Layout/LineLength
|
291
|
-
' :base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|js\\\\\\|css\\\\\\|otf\\\\\\|ttf\\\\\\|woff2?"',
|
292
|
-
# rubocop:enable Layout/LineLength
|
293
|
-
' :recursive t',
|
294
|
-
format(' :publishing-directory "%<wd>s/%<pub>s/assets/%<theme>s"',
|
295
|
-
wd: workdir, pub: get('public_folder'), theme: theme),
|
296
|
-
' :publishing-function org-publish-attachment)'
|
297
|
-
].join("\n ").strip
|
298
|
-
end
|
299
|
-
|
300
|
-
def org_project_shared_lines(project)
|
301
|
-
[
|
302
|
-
format(':base-directory "%<path>s"', path: project['path']),
|
303
|
-
format(
|
304
|
-
':publishing-directory "%<path>s"',
|
305
|
-
path: publication_path(project)
|
306
|
-
),
|
307
|
-
format(
|
308
|
-
':recursive %<rec>s',
|
309
|
-
rec: ruby_to_lisp_boolean(project['recursive'])
|
310
|
-
)
|
311
|
-
]
|
312
|
-
end
|
313
|
-
|
314
|
-
def org_project_config(project_name, project, shared_lines)
|
315
|
-
project_lines = [
|
316
|
-
format('"%<name>s"', name: project_name),
|
317
|
-
':base-extension "org"',
|
318
|
-
format(
|
319
|
-
':publishing-function %<fun>s',
|
320
|
-
fun: publication_function(project)
|
321
|
-
)
|
322
|
-
] + shared_lines + project['org_headers']
|
323
|
-
if project['exclude']
|
324
|
-
project_lines << format(
|
325
|
-
':exclude "%<value>s"', value: project['exclude']
|
326
|
-
)
|
327
|
-
end
|
328
|
-
format('(%<pr>s)', pr: project_lines.join("\n "))
|
329
|
-
end
|
330
|
-
|
331
|
-
def org_assets_config(project_name, shared_lines)
|
332
|
-
assets_lines = [
|
333
|
-
format('"%<name>s-assets"', name: project_name),
|
334
|
-
':base-extension "jpg\\\\\\|gif\\\\\\|png\\\\\\|svg\\\\\\|pdf"',
|
335
|
-
':publishing-function org-publish-attachment'
|
336
|
-
] + shared_lines
|
337
|
-
format('(%<assets>s)', assets: assets_lines.join("\n "))
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
;; Add org-mode to load path
|
2
|
-
(add-to-list 'load-path (expand-file-name "org-__ORG_VER__/lisp" "__WORK_DIR__/lib"))
|
3
|
-
;; Load last version of htmlize.el
|
4
|
-
(load-file (expand-file-name "htmlize.el" "__WORK_DIR__/lib"))
|
5
|
-
|
6
|
-
;; Current project options
|
7
|
-
(setq fronde/version "__VERSION__"
|
8
|
-
fronde/current-work-dir "__WORK_DIR__"
|
9
|
-
user-mail-address "__AUTHOR_EMAIL__"
|
10
|
-
user-full-name "__AUTHOR_NAME__"
|
11
|
-
org-html-metadata-timestamp-format "__LONG_DATE_FMT__"
|
12
|
-
org-gmi-timestamp-format "__LONG_DATE_FMT__"
|
13
|
-
org-publish-project-alist
|
14
|
-
`(__ALL_PROJECTS____THEME_CONFIG__
|
15
|
-
("website" :components (__ALL_PROJECTS_NAMES__))))
|
16
|
-
|
17
|
-
;; Load fronde lib
|
18
|
-
(load-file (expand-file-name "ox-gmi.el" "__WORK_DIR__/lib"))
|
19
|
-
(load-file (expand-file-name "ox-fronde.el" "__FRONDE_DIR__"))
|
@@ -1,121 +0,0 @@
|
|
1
|
-
;;; ox-fronde.el --- Fronde Gem specific helpers for Org Export Engine -*- lexical-binding: t; -*-
|
2
|
-
|
3
|
-
;; Copyright (C) 2011-2019 Free Software Foundation, Inc.
|
4
|
-
|
5
|
-
;; Author: Étienne Deparis <etienne at depar dot is>
|
6
|
-
;; Keywords: org, export
|
7
|
-
|
8
|
-
;; This file is not part of GNU Emacs.
|
9
|
-
|
10
|
-
;; GNU Emacs is free software: you can redistribute it and/or modify
|
11
|
-
;; it under the terms of the GNU General Public License as published by
|
12
|
-
;; the Free Software Foundation, either version 3 of the License, or
|
13
|
-
;; (at your option) any later version.
|
14
|
-
|
15
|
-
;; GNU Emacs is distributed in the hope that it will be useful,
|
16
|
-
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
-
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
-
;; GNU General Public License for more details.
|
19
|
-
|
20
|
-
;; You should have received a copy of the GNU General Public License
|
21
|
-
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
22
|
-
|
23
|
-
;;; Commentary:
|
24
|
-
|
25
|
-
;; This library implements specific helpers function, needed by the Ruby
|
26
|
-
;; Gem Fronde, which offers an easy way to publish a static website
|
27
|
-
;; using Org files as sources.
|
28
|
-
|
29
|
-
;;; Code:
|
30
|
-
|
31
|
-
(require 'org)
|
32
|
-
(require 'ox-html)
|
33
|
-
(require 'ox-gmi)
|
34
|
-
|
35
|
-
;;; Function Declarations
|
36
|
-
|
37
|
-
(defvar fronde/version ""
|
38
|
-
"Version of the current fronde installation")
|
39
|
-
|
40
|
-
(defvar fronde/current-work-dir nil
|
41
|
-
"Location of the current fronde website base directory.")
|
42
|
-
|
43
|
-
(defvar fronde/org-temp-dir nil
|
44
|
-
"Location of the local Org temporary directory (where to place
|
45
|
-
org timestamps and id locations).")
|
46
|
-
|
47
|
-
(defun fronde/org-html-format-spec (upstream info)
|
48
|
-
"Return format specification for preamble and postamble.
|
49
|
-
INFO is a plist used as a communication channel."
|
50
|
-
(let ((output (funcall upstream info)))
|
51
|
-
(push `(?A . ,(format "<span class=\"author\">%s</span>"
|
52
|
-
(org-export-data (plist-get info :author) info)))
|
53
|
-
output)
|
54
|
-
(push `(?k . ,(org-export-data (plist-get info :keywords) info)) output)
|
55
|
-
(push `(?K . ,(format "<ul class=\"keywords-list\">%s</ul>"
|
56
|
-
(mapconcat
|
57
|
-
(lambda (k) (format "<li class=\"keyword\">%s</li>" k))
|
58
|
-
(split-string (or (plist-get info :keywords) "") ",+ *")
|
59
|
-
"\n")))
|
60
|
-
output)
|
61
|
-
(push `(?l . ,(org-export-data (plist-get info :language) info)) output)
|
62
|
-
(push `(?n . ,(format "Fronde %s" fronde/version)) output)
|
63
|
-
(push `(?N . ,(format "<a href=\"https://git.umaneti.net/fronde/about/\">Fronde</a> %s" fronde/version)) output)
|
64
|
-
(push `(?x . ,(org-export-data (plist-get info :description) info)) output)
|
65
|
-
(push `(?X . ,(format "<p>%s</p>"
|
66
|
-
(org-export-data (plist-get info :description) info)))
|
67
|
-
output)))
|
68
|
-
|
69
|
-
(defun fronde/org-gmi-format-spec (upstream info)
|
70
|
-
"Return format specification for gemini postamble.
|
71
|
-
INFO is a plist used as a communication channel."
|
72
|
-
(let ((output (funcall upstream info)))
|
73
|
-
(push `(?n . ,(format "Fronde %s" fronde/version)) output)))
|
74
|
-
|
75
|
-
(defun fronde/org-i18n-export (link description format)
|
76
|
-
"Export a i18n link"
|
77
|
-
(let* ((splitted-link (split-string link "|"))
|
78
|
-
(path (car splitted-link))
|
79
|
-
(desc (or description path))
|
80
|
-
(lang (cadr splitted-link)))
|
81
|
-
(pcase format
|
82
|
-
(`html (if lang
|
83
|
-
(format "<a href=\"%s\" hreflang=\"%s\">%s</a>"
|
84
|
-
path lang desc)
|
85
|
-
(format "<a href=\"%s\">%s</a>" path desc)))
|
86
|
-
(`latex (format "\\href{%s}{%s}" path desc))
|
87
|
-
(_ (format "%s (%s)" desc path)))))
|
88
|
-
|
89
|
-
(defun fronde/org-i18n-follow (link)
|
90
|
-
"Visit a i18n link"
|
91
|
-
(browse-url (car (split-string link "|"))))
|
92
|
-
|
93
|
-
(org-link-set-parameters "i18n"
|
94
|
-
:export #'fronde/org-i18n-export
|
95
|
-
:follow #'fronde/org-i18n-follow)
|
96
|
-
|
97
|
-
|
98
|
-
;;; Set configuration options
|
99
|
-
|
100
|
-
(setq fronde/org-temp-dir (expand-file-name "var/tmp" fronde/current-work-dir)
|
101
|
-
org-publish-timestamp-directory (expand-file-name "timestamps/" fronde/org-temp-dir)
|
102
|
-
org-id-locations-file (expand-file-name "id-locations.el" fronde/org-temp-dir)
|
103
|
-
make-backup-files nil
|
104
|
-
enable-local-variables :all
|
105
|
-
org-confirm-babel-evaluate nil
|
106
|
-
org-export-with-broken-links t
|
107
|
-
org-html-doctype "html5"
|
108
|
-
org-html-html5-fancy t
|
109
|
-
org-html-htmlize-output-type 'css
|
110
|
-
org-html-text-markup-alist '((bold . "<strong>%s</strong>")
|
111
|
-
(code . "<code>%s</code>")
|
112
|
-
(italic . "<em>%s</em>")
|
113
|
-
(strike-through . "<del>%s</del>")
|
114
|
-
(underline . "<span class=\"underline\">%s</span>")
|
115
|
-
(verbatim . "<code>%s</code>")))
|
116
|
-
(advice-add 'org-html-format-spec :around #'fronde/org-html-format-spec)
|
117
|
-
(advice-add 'org-gmi--format-spec :around #'fronde/org-gmi-format-spec)
|
118
|
-
|
119
|
-
(provide 'ox-fronde)
|
120
|
-
|
121
|
-
;;; ox-fronde.el ends here
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Fronde
|
4
|
-
# This module holds class methods for the {Fronde::OrgFile} class.
|
5
|
-
module OrgFileClassMethods
|
6
|
-
def source_for_target(file_name)
|
7
|
-
# file_name may be frozen...
|
8
|
-
src = file_name.sub(/\.html\z/, '.org')
|
9
|
-
pubfolder = Fronde::Config.get('public_folder')
|
10
|
-
src.sub!(/^#{pubfolder}\//, '')
|
11
|
-
# Look for match in each possible sources. The first found wins.
|
12
|
-
Fronde::Config.sources.each do |project|
|
13
|
-
if project['target'] == '.'
|
14
|
-
origin = File.join(project['path'], src)
|
15
|
-
else
|
16
|
-
origin = File.join(
|
17
|
-
project['path'], src.sub(/^#{project['target']}\//, '')
|
18
|
-
)
|
19
|
-
end
|
20
|
-
return origin if File.exist?(origin)
|
21
|
-
end
|
22
|
-
nil
|
23
|
-
end
|
24
|
-
|
25
|
-
def target_for_source(file_name, project, with_public_folder: true)
|
26
|
-
return nil if file_name.nil?
|
27
|
-
# file_name may be frozen...
|
28
|
-
target = file_name.sub(/\.org\z/, '.html').sub(/^#{Dir.pwd}\//, '')
|
29
|
-
if project.nil?
|
30
|
-
subfolder = File.basename(File.dirname(target))
|
31
|
-
target = File.basename(target)
|
32
|
-
target = "#{subfolder}/#{target}" if subfolder != '.'
|
33
|
-
else
|
34
|
-
project_relative_path = project['path'].sub(/^#{Dir.pwd}\//, '')
|
35
|
-
target.sub!(/^#{project_relative_path}\//, '')
|
36
|
-
target = "#{project['target']}/#{target}" if project['target'] != '.'
|
37
|
-
end
|
38
|
-
return target unless with_public_folder
|
39
|
-
pubfolder = Fronde::Config.get('public_folder')
|
40
|
-
"#{pubfolder}/#{target}"
|
41
|
-
end
|
42
|
-
|
43
|
-
def project_for_source(file_name)
|
44
|
-
# Look for match in each possible sources. The first found wins.
|
45
|
-
Fronde::Config.sources.each do |project|
|
46
|
-
project_relative_path = project['path'].sub(/^#{Dir.pwd}\//, '')
|
47
|
-
return project if file_name.match?(/^#{project_relative_path}\//)
|
48
|
-
end
|
49
|
-
nil
|
50
|
-
end
|
51
|
-
|
52
|
-
def slug(title)
|
53
|
-
title.downcase.tr(' ', '-')
|
54
|
-
.encode('ascii', fallback: ->(k) { translit(k) })
|
55
|
-
.gsub(/[^\w-]/, '').delete_suffix('-')
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def translit(char)
|
61
|
-
return 'a' if ['á', 'à', 'â', 'ä', 'ǎ', 'ã', 'å'].include?(char)
|
62
|
-
return 'e' if ['é', 'è', 'ê', 'ë', 'ě', 'ẽ'].include?(char)
|
63
|
-
return 'i' if ['í', 'ì', 'î', 'ï', 'ǐ', 'ĩ'].include?(char)
|
64
|
-
return 'o' if ['ó', 'ò', 'ô', 'ö', 'ǒ', 'õ'].include?(char)
|
65
|
-
return 'u' if ['ú', 'ù', 'û', 'ü', 'ǔ', 'ũ'].include?(char)
|
66
|
-
return 'y' if ['ý', 'ỳ', 'ŷ', 'ÿ', 'ỹ'].include?(char)
|
67
|
-
return 'c' if char == 'ç'
|
68
|
-
return 'n' if char == 'ñ'
|
69
|
-
'-'
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Fronde
|
4
|
-
# This module holds extracter methods for the {Fronde::OrgFile} class.
|
5
|
-
module OrgFileExtracter
|
6
|
-
private
|
7
|
-
|
8
|
-
# Main method, which will call the other to initialize an
|
9
|
-
# {Fronde::OrgFile} instance.
|
10
|
-
def extract_data
|
11
|
-
@content = File.read @file
|
12
|
-
@title = extract_title
|
13
|
-
@subtitle = extract_subtitle
|
14
|
-
@date = extract_date
|
15
|
-
@author = extract_author
|
16
|
-
@keywords = extract_keywords
|
17
|
-
@lang = extract_lang
|
18
|
-
@excerpt = extract_excerpt
|
19
|
-
end
|
20
|
-
|
21
|
-
def extract_date
|
22
|
-
timerx = '([0-9:]{5})(?::([0-9]{2}))?'
|
23
|
-
m = /^#\+date: *<([0-9-]{10}) [\w.]+(?: #{timerx})?> *$/i.match(@content)
|
24
|
-
return nil if m.nil?
|
25
|
-
@notime = m[2].nil?
|
26
|
-
if @notime
|
27
|
-
time = '00:00:00'
|
28
|
-
else
|
29
|
-
time = "#{m[2]}:#{m[3] || '00'}"
|
30
|
-
end
|
31
|
-
DateTime.strptime("#{m[1]} #{time}", '%Y-%m-%d %H:%M:%S')
|
32
|
-
end
|
33
|
-
|
34
|
-
def extract_title
|
35
|
-
m = /^#\+title:(.+)$/i.match(@content)
|
36
|
-
if m.nil?
|
37
|
-
# Avoid to leak absolute path
|
38
|
-
project_relative_path = @file.sub(/^#{Dir.pwd}\//, '')
|
39
|
-
return project_relative_path
|
40
|
-
end
|
41
|
-
m[1].strip
|
42
|
-
end
|
43
|
-
|
44
|
-
def extract_subtitle
|
45
|
-
m = /^#\+subtitle:(.+)$/i.match(@content)
|
46
|
-
return '' if m.nil?
|
47
|
-
m[1].strip
|
48
|
-
end
|
49
|
-
|
50
|
-
def extract_author
|
51
|
-
m = /^#\+author:(.+)$/i.match(@content)
|
52
|
-
return Fronde::Config.get('author') if m.nil?
|
53
|
-
m[1].strip
|
54
|
-
end
|
55
|
-
|
56
|
-
def extract_keywords
|
57
|
-
m = /^#\+keywords:(.+)$/i.match(@content)
|
58
|
-
return [] if m.nil?
|
59
|
-
m[1].split(',').map(&:strip)
|
60
|
-
end
|
61
|
-
|
62
|
-
def extract_lang
|
63
|
-
m = /^#\+language:(.+)$/i.match(@content)
|
64
|
-
return Fronde::Config.get('lang') if m.nil?
|
65
|
-
m[1].strip
|
66
|
-
end
|
67
|
-
|
68
|
-
def extract_excerpt
|
69
|
-
@content.scan(/^#\+description:(.+)$/i).map { |l| l[0].strip }.join(' ')
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'fronde/config'
|
4
|
-
require 'fronde/emacs'
|
5
|
-
|
6
|
-
module Fronde
|
7
|
-
# This module holds HTML formatter methods for the {Fronde::OrgFile}
|
8
|
-
# class.
|
9
|
-
module OrgFileHtmlizer
|
10
|
-
private
|
11
|
-
|
12
|
-
# Format {Fronde::OrgFile#keywords} list in an HTML listing.
|
13
|
-
#
|
14
|
-
# @return [String] the HTML keywords list
|
15
|
-
def keywords_to_html
|
16
|
-
domain = Fronde::Config.get('domain')
|
17
|
-
klist = @keywords.map do |k|
|
18
|
-
<<~KEYWORDLINK
|
19
|
-
<li class="keyword">
|
20
|
-
<a href="#{domain}/tags/#{Fronde::OrgFile.slug(k)}.html">#{k}</a>
|
21
|
-
</li>
|
22
|
-
KEYWORDLINK
|
23
|
-
end.join
|
24
|
-
"<ul class=\"keywords-list\">#{klist}</ul>"
|
25
|
-
end
|
26
|
-
|
27
|
-
# Format {Fronde::OrgFile#date} as a HTML `time` tag.
|
28
|
-
#
|
29
|
-
# @return [String] the HTML `time` tag
|
30
|
-
def date_to_html(dateformat = :full)
|
31
|
-
return '<time></time>' if @date.nil?
|
32
|
-
"<time datetime=\"#{@date.rfc3339}\">#{datestring(dateformat)}</time>"
|
33
|
-
end
|
34
|
-
|
35
|
-
# Format {Fronde::OrgFile#author} in a HTML `span` tag with a
|
36
|
-
# specific class.
|
37
|
-
#
|
38
|
-
# @return [String] the author HTML `span`
|
39
|
-
def author_to_html
|
40
|
-
"<span class=\"author\">#{@author}</span>"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|