├── .gitignore
├── LICENSE
├── README.md
├── _extensions
└── custom-numbered-blocks
│ ├── _extension.yml
│ ├── custom-numbered-blocks.lua
│ ├── custom-numbered-blocks.lua.bak
│ └── style
│ ├── foldbox.css
│ ├── foldbox.lua
│ └── foldbox.tex
├── doc
├── example.html
├── example.pdf
└── example_files
│ └── libs
│ ├── bootstrap
│ ├── bootstrap-icons.css
│ ├── bootstrap-icons.woff
│ ├── bootstrap.min.css
│ └── bootstrap.min.js
│ ├── clipboard
│ └── clipboard.min.js
│ ├── quarto-contrib
│ └── foldbox
│ │ └── foldbox.css
│ └── quarto-html
│ ├── anchor.min.js
│ ├── popper.min.js
│ ├── quarto-syntax-highlighting.css
│ ├── quarto.js
│ ├── tippy.css
│ └── tippy.umd.min.js
└── example.qmd
/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.pdf
3 | !doc/*.*
4 | *_files/
5 | !doc/*_files/
6 | *.json
7 | .Rproj.user
8 | /.luarc.json
9 | list-of-*.qmd
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Ute Hahn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Custom Numbered Blocks Extension for Quarto
2 |
3 | This extension provides user defined custom div classes (environments) that come with numbering, such as theorems, examples, exercises. Numbered blocks can be cross referenced.
4 |
5 | - By default, the div's text block is enclosed in a collapsible box, similar to quarto callouts.
6 | - Groups of classes can be defined that share style and numbering.
7 | - Lists-of-classes can be extracted, such as a list of all theorems. It is also possible to generate a list for a group of classes.
8 |
9 | The filter supports output formats pdf and html.
10 |
11 |
12 | 
13 |
14 | ## Status
15 |
16 | Seems that Quarto 1.3 handles pdf books differently from Quarto 1.2. If chapters contain additional level 1 heading, this messes up numbering in Quarto 1.3 pdf books. I will likely fix that soon.
17 |
18 | There may come changes to the yaml-UI for lists-of-classes, also soon ;-).
19 |
20 |
21 | ## Installing
22 |
23 | ```bash
24 | quarto add ute/custom-numbered-blocks
25 | ```
26 |
27 | This will install the extension under the `_extensions` subdirectory.
28 | If you're using version control, you will want to check in this directory.
29 |
30 | ## Using
31 |
32 | Usage is illustrated more comprehensively in `example.qmd`.
33 |
34 | ### Defining and using a user defined class
35 | To specify a new class of numbered div blocks, `Example`, say, add yaml code:
36 | ```yaml
37 | custom-numbered-blocks:
38 | classes:
39 | Example: default
40 | ```
41 | Use the class in a fenced dive. Title can be provided as a header immediately after div.
42 | ```
43 | ::: Example
44 | ### the best possible example, ever
45 | here is some exemplary text
46 | :::
47 | ```
48 |
49 | ### Change default options for a class
50 | The default style renders as a collapsible box with title button, similar to quarto callouts. It comes with a small close button bottom right. You can change the following options in yaml or individually in the div specification:
51 | - `colors` : an array of two hex rgb color codes, for title button color and frame color. `colors: [a08080, 500000]` would give a pink title button and dark red frame.
52 | - `collapse`: boolean, default `true`. Initial state of the collapsible box.
53 | - `label`: the label to print before the number (string).
54 | - `boxstyle`: set to `foldbox.simple` for a boxed environment without close button. There will quite likely come more options in a future version.
55 | - `listin`: register for a [list-of](#lists-of-listin-version)
56 |
57 | ### Groups of classes with joint counter and joint default style
58 | Jointly counted block classes are specified by yaml option `groups`. These groups can also have a common default style. For each class, option `group` indicates membership.
59 |
60 | **Example**: we want to jointly count theorems, lemmas and propositions, and render boxes with initially open status, but propositions should be collapsed:
61 | ```yaml
62 | custom-numbered-blocks:
63 | groups:
64 | thmlike:
65 | collapse: false
66 | classes:
67 | Theorem:
68 | group: thmlike
69 | Proposition:
70 | group: thmlike
71 | collapse: true
72 | Lemma:
73 | group: thmlike
74 | ```
75 |
76 | ### Lists-of ("listin" version)
77 | To generate a list of all divs belonging to a class, `Example`, say, add key listin to the class and give the name of the list. The same can be done for groups of classes. This will produce a file `list-of-`name`.qmd` that contains headers and references to the respective blocks. The following code will generage files `list-of-allthingsmath.qmd` and `list-of-examples.qmd`:
78 |
79 | ```yaml
80 | custom-numbered-blocks
81 | groups:
82 | thmlike:
83 | collapse: false
84 | listin: [allthingsmath]
85 | Example:
86 | listin: [examples, allthingsmath]
87 | ```
88 |
89 | ## Example
90 |
91 | Here is the source code for a (not so) minimal example: [example.qmd](https://ute.github.io/custom-numbered-blocks/example.qmd). And here's the rendered [example.html](https://ute.github.io/custom-numbered-blocks/doc/example.html) and [example.pdf](https://ute.github.io/custom-numbered-blocks/doc/example.pdf)
92 |
93 | ## Limitations
94 | - References to bibliography in the title are not resolved, see [this issue by ntq2022](https://github.com/ute/custom-numbered-blocks/issues/7). This is due to the sequence of processing references. Pull requests are welcome - I am not sure
95 | if I will have time to dig into this in the nearer future.
96 | - Cross-reference labels that are interpretable for Quarto, such as labels starting with `thm-` or `fig-`, cannot be used with this extension, since they will be processed by Quarto. This results in unexpected output, see [this issue by gyu-eun-lee](https://github.com/ute/custom-numbered-blocks/issues/8).
97 |
98 | ## Workarounds and precautions to avoid clashes with other extensions
99 | - If you use [parse-latex](https://github.com/tarleb/parse-latex), make sure that custom-numbered-blocks comes first in the filter pipeline to process LaTeX references (`\ref`).
100 | - Further headers within custom numbered blocks will mess up indentation of paragraphs following that block. To avoid that, include headers in a div, for example
101 | ```markdown
102 | ::: {.myblock}
103 | ### heading of my custom block
104 | blabla
105 |
106 | ::::{}
107 | ### new header
108 | ::::
109 | other stuff
110 | ```
111 |
--------------------------------------------------------------------------------
/_extensions/custom-numbered-blocks/_extension.yml:
--------------------------------------------------------------------------------
1 | title: Custom-numbered-blocks
2 | author: Ute Hahn
3 | version: 0.1.3
4 | quarto-required: ">=1.2.0"
5 | contributes:
6 | filters:
7 | - custom-numbered-blocks.lua
8 |
9 |
--------------------------------------------------------------------------------
/_extensions/custom-numbered-blocks/custom-numbered-blocks.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | MIT License
3 |
4 | Copyright (c) 2023 Ute Hahn
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 | ]]--
24 | -- pre-pre-release
25 | --
26 | -- partial rewrite, complete later
27 |
28 | -- nice rename function learned from shafayetShafee :-)
29 | local str = pandoc.utils.stringify
30 | local pout = quarto.log.output
31 |
32 | -- important quasi global variables
33 |
34 | local ishtml = quarto.doc.is_format("html")
35 | local ispdf = quarto.doc.is_format("pdf")
36 | local fmt=""
37 | if ishtml then fmt="html" elseif ispdf then fmt = "pdf" end
38 |
39 | -- TODO encapsulate stylez into a doc thing or so
40 | -- maybe later allow various versions concurrently.
41 | -- this could probably be problematic because of option clashes in rendered header?
42 |
43 | local stylename="foldbox"
44 |
45 | local stylez = require("style/"..stylename)
46 |
47 |
48 | --- TODO: better encapsulation (luxury :-P)
49 |
50 | fbx={ -- global table, holds information for processing fboxes
51 | xreffile = "._xref.json" -- default name, set to lastfile in initial meta analysis
52 | }
53 |
54 |
55 | -- utility functions ---
56 | local function DeInline(tbl)
57 | local result ={}
58 | for i, v in pairs(tbl) do
59 | pdtype = pandoc.utils.type(v)
60 | if pdtype == "Inlines" or pdtype =="boolean"
61 | then
62 | result[i] = str(v)
63 | elseif pdtype == "List" then result[i] = DeInline(v)
64 | end
65 | end
66 | return(result)
67 | end
68 |
69 | local function tablecontains(tbl, val)
70 | if tbl ~= nil and val ~= nil then
71 | for _, v in ipairs(tbl) do
72 | if val == v then return true end
73 | end
74 | end
75 | return false
76 | end
77 |
78 | local function ifelse(condition, iftrue, iffalse)
79 | if condition then return iftrue else return iffalse end
80 | end
81 |
82 | local function replaceifnil(existvalue, replacevalue)
83 | if existvalue ~= nil then return existvalue else return replacevalue end
84 | end
85 |
86 | local function replaceifempty(existvalue, replacevalue)
87 | if existvalue == nil or existvalue=="" then return replacevalue else return existvalue end
88 | end
89 |
90 |
91 | local function updateTable (oldtbl, newtbl, ignorekeys)
92 | local result = {}
93 | -- copy old attributes
94 | for k, v in pairs(oldtbl) do result[k] = v end
95 | if newtbl ~= nil then if type(newtbl) == "table" then
96 | if newtbl[1] == nil then -- it is an ok table with key value pairs
97 | for k, v in pairs(newtbl) do
98 | if not(tablecontains(ignorekeys, k)) then
99 | result[k] = v
100 | end
101 | end
102 | -- special: set reflabel to label if not given in attribs
103 | -- if newattribs["reflabel"] == nil then result.reflabel = result.label end
104 | -- TODO: do this elsewhere
105 | end
106 | end
107 | end
108 | return(result)
109 | end
110 |
111 |
112 |
113 | ---- init step ---------------------------
114 | --- init = require ("fbx1")
115 |
116 | -- find chapter number and file name
117 | -- returns a table with keyed entries
118 | -- processedfile: string,
119 | -- ishtmlbook: boolean,
120 | -- chapno: string (at least if ishtmlbook),
121 | -- unnumbered: boolean - initial state of section / chapter
122 | -- if the user has given a chapno yaml entry, then unnumbered = false
123 |
124 | -- !!! for pdf, the workflow is very different! ---
125 | -- also find out if lastfile of a book
126 |
127 | -- find first and last file of a book, and chapter number of that file
128 | local function chapterinfo(book, fname)
129 | local first = ""
130 | local last = ""
131 | local chapno = nil
132 | local info = {}
133 | --if book.render then
134 | for _, v in pairs(book.render) do
135 | if str(v.type) == "chapter" then
136 | last = pandoc.path.split_extension(str(v.file))
137 | if first == "" then first = last end
138 | if last == fname then chapno = v.number end
139 | end
140 | end
141 | info.islast = (fname == last)
142 | info.isfirst = (fname == first)
143 | info.lastchapter = last
144 | info.chapno = chapno
145 | return(info)
146 | end
147 |
148 | local function Meta_findChapterNumber(meta)
149 | local processedfile = pandoc.path.split_extension(PANDOC_STATE.output_file)
150 | fbx.isbook = meta.book ~= nil
151 | fbx.ishtmlbook = meta.book ~= nil and not quarto.doc.is_format("pdf")
152 | fbx.processedfile = processedfile
153 |
154 | fbx.output_file = PANDOC_STATE.output_file
155 | -- pout(" now in "..processedfile.." later becomes ".. str(fbx.output_file))
156 |
157 | fbx.isfirstfile = not fbx.ishtmlbook
158 | fbx.islastfile = not fbx.ishtmlbook
159 | if fbx.isbook then
160 | local chinfo = chapterinfo(meta.book, processedfile)
161 | if fbx.ishtmlbook then
162 | fbx.xreffile= "._htmlbook_xref.json"
163 | else
164 | fbx.xreffile= "._pdfbook_xref.json"
165 | -- fbx.xreffile= "._"..chinfo.lastchapter.."_xref.json"
166 | end
167 | fbx.isfirstfile = chinfo.isfirst
168 | fbx.islastfile = chinfo.islast
169 |
170 | fbx.unnumbered = false
171 | -- user set chapter number overrides information from meta
172 | if meta.chapno then
173 | fbx.chapno = str(meta.chapno)
174 | else
175 | if chinfo.chapno ~= nil then
176 | fbx.chapno = str(chinfo.chapno)
177 | else
178 | fbx.chapno = ""
179 | fbx.unnumbered = true
180 | end
181 | end
182 | else -- not a book.
183 | fbx.xreffile ="._"..processedfile.."_xref.json"
184 | fbx.chapno = ""
185 | fbx.unnumbered = true
186 | end
187 | end
188 |
189 | local function makeKnownClassDetector(knownclasses)
190 | return function(div)
191 | for _, cls in pairs(div.classes) do
192 | if tablecontains(knownclasses, cls) then return str(cls) end
193 | end
194 | return nil
195 | end
196 | end
197 |
198 | local function Meta_initClassDefaults (meta)
199 | -- do we want to prefix fbx numbers with section numbers?
200 | local cunumbl = meta["custom-numbered-blocks"]
201 | fbx.knownclasses = {}
202 | fbx.lists = {}
203 | --[[ TODO later
204 | if meta.fbx_number_within_sections then
205 | fbx.number_within_sections = meta.fbx_number_within_sections
206 | else
207 | fbx.number_within_sections = false
208 | end
209 | --]]
210 | -- prepare information for numbering fboxes by class
211 | -- fbx.knownClasses ={}
212 | fbx.classDefaults ={}
213 | local groupDefaults = {default = stylez.defaultOptions} -- not needed later
214 | fbx.counter = {unnumbered = 0} -- counter for unnumbered divs
215 | -- ! unnumbered not for classes that have unnumbered as default !
216 | -- fbx.counterx = {}
217 | if cunumbl.classes == nil then
218 | print("== @%!& == Warning == &!%@ ==\n wrong format for fboxes yaml: classes needed")
219 | return
220 | end
221 |
222 | -- simplified copy of yaml data: inlines to string
223 | if cunumbl.groups then
224 | for key, val in pairs(cunumbl.groups) do
225 | local ginfo = DeInline(val)
226 | --[[
227 | pout("==== before after ======="); pout(ginfo)
228 | if ginfo.boxstyle then
229 | local mainstyle, substyle = ginfo.boxstyle:match "([^.]*).(.*)"
230 | -- pout("main "..mainstyle.." :: "..substyle)
231 | -- TODO: here account for multiple styles
232 | end
233 | --]]--
234 | ginfo = updateTable(stylez.defaultOptions, ginfo)
235 | --fbx.
236 | groupDefaults[key] = ginfo
237 | -- pout("-----group---"); pout(ginfo)
238 | end
239 | end
240 | for key, val in pairs(cunumbl.classes) do
241 | local clinfo = DeInline(val)
242 | -- pout("==== before after ======="); pout(clinfo)
243 | -- classinfo[key] = DeInline(val)
244 | table.insert(fbx.knownclasses, str(key))
245 | local theGroup = replaceifnil(clinfo.group, "default")
246 | clinfo = updateTable(groupDefaults[theGroup], clinfo)
247 | clinfo.label = replaceifnil(clinfo.label, str(key))
248 | clinfo.reflabel = replaceifnil(clinfo.reflabel, clinfo.label)
249 | -- assign counter --
250 | clinfo.cntname = replaceifnil(clinfo.group, str(key))
251 | fbx.counter[clinfo.cntname] = 0 -- sets the counter up if non existing
252 | fbx.classDefaults[key] = clinfo
253 | -- pout("---class----"); pout(clinfo)
254 | end
255 | fbx.is_cunumblo = makeKnownClassDetector(fbx.knownclasses)
256 | -- gather lists-of and make filenames by going through all classes
257 | for _, val in pairs(fbx.classDefaults) do
258 | -- pout("--classdefault: "..str(key))
259 | -- pout(val)
260 | if val.listin then
261 | for _,v in ipairs(val.listin) do
262 | fbx.lists[v] = {file = "list-of-"..str(v)..".qmd"}
263 | end
264 | end
265 | end
266 | -- initialize lists
267 | for key, val in pairs(fbx.lists) do
268 | val.contents = ifelse(fbx.isfirstfile, "\\providecommand{\\Pageref}[1]{\\hfill p.\\pageref{#1}}", "")
269 | -- listin approach does not require knownclass, since listin is in classdefaults
270 | end
271 | -- pout(fbx.lists)
272 | --]]
273 | -- document can give the chapter number for books in yaml header
274 | -- this becomes the counter Prefix
275 | end
276 |
277 | local initMeta = function(m)
278 | if m["custom-numbered-blocks"] then
279 | Meta_findChapterNumber(m)
280 | Meta_initClassDefaults(m)
281 | else
282 | print("== @%!& == Warning == &!%@ ==\n missing cunumblo key in yaml")
283 | end
284 | return(m)
285 | end
286 |
287 | ----------------------- oldcode, mostly -------------------------------------
288 |
289 |
290 | ------- numbering and class attributes ------
291 |
292 | local function fboxDiv_setAttributes(el, cls, prefix)
293 | local ela = el.attributes -- shortcut
294 | local ClassDef = fbx.classDefaults[cls]
295 | --local unnumbered = ClassDef.numbered == "false"
296 | local numbered = ClassDef.numbered ~= "false"
297 | local tag = ela.tag
298 | local tagged = tag ~= nil
299 | local id = el.identifier
300 | local autoid =""
301 | -- local titl = ela.title
302 | local cntkey = ClassDef.cntname
303 | local counter = {}
304 | local cnts = 0
305 | local idnumber = "0.0"
306 |
307 | -- set prefix
308 | ela._prefix = prefix
309 |
310 | id = replaceifnil(id ,"")
311 | tag = replaceifnil(tag ,"")
312 |
313 | --- determine if numbered and / or tagged ------
314 |
315 | if tagged then numbered = false end
316 | if el.classes:includes("unnumbered") then numbered = false end
317 |
318 | if ela.numtag then
319 | tag = ela.numtag
320 | -- print("!!! also hier mal ein numtag.\n")
321 | numbered = true
322 | tagged = true
323 | end
324 |
325 | -- make counts ---
326 |
327 | if not numbered then cntkey = "unnumbered" end
328 |
329 | cnts = fbx.counter[cntkey] +1
330 | fbx.counter[cntkey] = cnts
331 |
332 | idnumber = ifelse(prefix ~= "", prefix .. '.' .. cnts, str(cnts))
333 | --[[
334 | if prefix ~="" then idnumber = prefix .. '.' .. cnts
335 | else idnumber = str(cnts)
336 | end
337 |
338 | if numbered then
339 | if not tagged then tag = idnumber
340 | else tag = idnumber.."("..tag..")"
341 | end
342 | end
343 | ]]--
344 | if numbered then tag = idnumber..ifelse(tagged, "("..tag..")", "" ) end
345 |
346 | if id == "" then
347 | if numbered then
348 | autoid = ela._fbxclass..'-'..tag
349 | else
350 | autoid = ela._fbxclass..'*-'..idnumber
351 | end
352 | -- changed my mind here: always give autoid
353 | else autoid = ela._fbxclass..'-id-'..id
354 | end
355 |
356 | -- do not change identifier el.identifier = id
357 |
358 | ela._autoid = autoid
359 |
360 | ela._tag = tag
361 |
362 | ela._file = fbx.processedfile -- necessary to reference between chapters. At least with quarto 1.3
363 | -- pout("tag: "..tag)
364 | -- pout(ela)
365 | return(el)
366 | end
367 |
368 | -- initial attributes without prefix and counts to allow for inner boxes
369 |
370 | local function fboxDiv_mark_for_processing(div)
371 | local diva=div.attributes
372 | local cls = fbx.is_cunumblo(div)
373 | local ClassDef = fbx.classDefaults[cls]
374 | if(cls) then
375 | diva._process_me = "true"
376 | diva._fbxclass = str(cls)
377 | diva._prefix = ""
378 | diva._tag = ""
379 | diva._collapse = str(replaceifnil(diva.collapse, ClassDef.collapse))
380 | diva._boxstyle = str(replaceifnil(diva.boxstyle, ClassDef.boxstyle))
381 | diva._label = str(replaceifnil(diva.label, ClassDef.label))
382 | diva._reflabel = str(replaceifnil(diva.reflabel, ClassDef.reflabel))
383 | end
384 | return(div)
385 | end
386 |
387 |
388 |
389 | local function Pandoc_prefix_count(doc)
390 | -- do evt later: non numeric chapernumbers
391 | local secno = 0
392 | local prefix = "0"
393 | if fbx.ishtmlbook then prefix = fbx.chapno end
394 |
395 | -- pout("this is a book?"..str(fbx.ishtmlbook))
396 |
397 | --- make numbering and prep div blocks ---
398 | --[[------- comment -----------
399 | quarto (1.2) books allow level 1 headings within a chapter.
400 | This would give a mess for crossreference numbers: e.g. multiple examples 3.1,
401 | from chapter 1 (with 2 l1 headers ) and chapter 3.
402 | Therefore I decided to ignore level 1 headings in chapters.
403 | This can easily be changed, then the crossref is for the last occurence only.
404 | Maybe one day when there is more fine tuning concerning individual numbering depth.
405 | If this happens before quarto 1.4
406 |
407 | --]]---------- end comment ------------
408 | for i, blk in ipairs(doc.blocks) do
409 | -- print(prefix.."-"..i.." "..blk.t.."\n")
410 |
411 | if blk.t=="Header" and not fbx.ishtmlbook then
412 | if (blk.level == 1) then -- increase prefix
413 | if blk.classes:includes("unnumbered")
414 | then
415 | prefix = ""
416 | else
417 | if blk.attr.attributes.secno then
418 |
419 | prefix = str(blk.attr.attributes.secno)
420 | else
421 | secno = secno + 1
422 | prefix = str(secno)
423 | end
424 | end
425 | -- reset counters in fbx --
426 | -- this would be more complicated if there are different levels
427 | -- of numbering depth
428 | -- then: add a numdepth variable to fbx with a list of keys
429 | for k in pairs(fbx.counter) do fbx.counter[k]=0 end
430 | end
431 |
432 | -- problem: only the outer divs are captured
433 | elseif blk.t=="Div" then
434 | local known = fbx.is_cunumblo(blk)
435 | if known then
436 | blk = fboxDiv_setAttributes(blk, known, prefix)
437 | end
438 | end
439 | end
440 | return(doc)
441 | end
442 |
443 | -- if no id, get from first included header, if possible
444 | local function Divs_getid(el)
445 | -- local known = getKnownEnv(el.attr.classes)
446 | local ela = el.attributes
447 | local id = el.identifier
448 |
449 | if not ela._process_me then return(el) end
450 | -- pout("--- processing item with id ".. replaceifempty(id, "LEER"))
451 |
452 | if id == nil or id =="" then
453 | -- try in next header
454 | el1 = el.content[1]
455 | if el1.t=="Header" then
456 | -- pout("--- looking at header with id "..el1.identifier)
457 | -- pout("--- still processing item with id ".. replaceifempty(id, "LEER"))
458 | -- pout("replacing id")
459 | id = el1.identifier
460 | el.identifier = id
461 | end
462 | end
463 | if id == nil or id ==""
464 | then
465 | -- pout("immer noch leer")
466 | if ela._autoid ~= nil then
467 | id = ela._autoid
468 | el.identifier = id
469 | end
470 | --else pout("nix autoid in ");pout(ela._autoid)
471 | end
472 | -- pout("resulting el:"); pout(el.attr)
473 | return(el)
474 | end
475 |
476 |
477 | --- utility function: stringify and sanitize math, depends on format ---
478 | local function str_sanimath(theInline, fmt)
479 | local newstring = theInline:walk{
480 | Math = function(ma)
481 | local mathtxt = str(ma.text)
482 | if fmt == "html" then
483 | return {'\\(' .. mathtxt .. '\\)'}
484 | elseif fmt == "pdf" then
485 | return {'\\(' .. mathtxt .. '\\)'}
486 | elseif fmt == "md" then
487 | return {'$' .. mathtxt .. '$'}
488 | else return {mathtxt}
489 | end
490 | end
491 | }
492 | return str(newstring)
493 | end
494 |
495 |
496 | ----------- title of divs ----------------
497 | local function Divs_maketitle(el)
498 | -- local known = getKnownEnv(el.attr.classes)
499 | local ela = el.attributes
500 | local titl = ela.title
501 | local mdtitl = replaceifnil(ela.title, "")
502 | local ClassDef = {}
503 | -- local id = el.identifier
504 |
505 | if not ela._process_me then return(el) end
506 | -- pout("--- processing item with id ".. replaceifempty(el.identifier, "LEER"))
507 |
508 | ClassDef = fbx.classDefaults[ela._fbxclass]
509 |
510 | if titl == nil then
511 | el1 = el.content[1]
512 | if el1.t=="Header" then
513 | -- sanitize math inline. depends on format
514 | ela.title = str(el1.content) -- readable version without math
515 | mdtitl = str_sanimath(el1.content, "md")
516 | if ishtml then titl = str_sanimath(el1.content, "html")
517 | elseif ispdf then titl = str_sanimath(el1.content, "pdf")
518 | else titl = mdtitl
519 | end
520 | -- pout("--- looking at header with id "..el1.identifier)
521 | -- pout("--- still processing item with id ".. replaceifempty(id, "LEER"))
522 | --[[
523 | if id =="" or id == nil then
524 | pout("replacing id")
525 | id = el1.identifier
526 | el.identifier = id
527 | end
528 | ]]--
529 | table.remove(el.content, 1)
530 | else titl = ""
531 | end
532 | end
533 | ela._title = titl -- keep the title as attribute for pandoc
534 | ela._mdtitle = mdtitl -- for list of
535 | -- replace empty identifier with autoid
536 | -- if el.identifier == "" then el.identifier = ela._autoid end
537 | -- pout("--> sanitarer titel: "..mdtitl)
538 | -- ela._tag = ""
539 | -- pout("resulting el:"); pout(el)
540 | return(el)
541 | end
542 |
543 | ---------------- initialize xref ----------
544 | -- xrefinit = require ("fbx3")
545 |
546 | -- xref split into prepare and finalize to allow xref in titles (future)
547 | local function Pandoc_preparexref(doc)
548 | -- local xref={}
549 | local id = ""
550 | local cnt = 0
551 | local bla={}
552 | local xinfo={}
553 | local file_autoid = {}
554 | local exists = false
555 | if fbx.xref == nil then fbx.xref ={} end
556 | xref = fbx.xref
557 | cnt = #xref
558 | if cnt > 0 then
559 | for i, xinf in ipairs(xref) do
560 | file_autoid[xinf.file..xinf.autoid] = i
561 | end
562 | -- pout(autoids)
563 | end
564 | for _, blk in ipairs(doc.blocks) do
565 | if blk.attributes then
566 | bla = blk.attributes
567 | if bla._process_me then
568 | --pout("fbox "..blk.attributes._tag)
569 | ------------- an fbox :) ------------
570 | if blk.identifier == nil then id = ""
571 | else id = blk.identifier end
572 | xinfo = {
573 | id = id,
574 | autoid = bla._autoid,
575 | cls = bla._fbxclass,
576 | label = bla._label,
577 | reflabel = bla._reflabel,
578 | reftag = bla._tag,
579 | refnum = replaceifempty(bla._tag, "??"),
580 | file = pandoc.path.split_extension(fbx.output_file)
581 | }
582 | -- if not xinfo.reftag then xinfo.reftag ="" end
583 | -- if xinfo.refnum == "" then xinfo.refnum ="??" end
584 | --[[
585 | if bla._tag
586 | then
587 | if bla._tag ~="" then xinfo.refnum = bla._tag else xinfo.reftag="??" end
588 | end
589 | ]]--
590 | --- check if autoid already exist in database. otherwise update
591 | oldxrefno = file_autoid[xinfo.file..xinfo.autoid]
592 | if oldxrefno == nil then
593 | cnt = cnt+1
594 | bla._xrefno = cnt
595 | table.insert (xref, cnt, xinfo)
596 | else
597 | bla._xrefno = oldxrefno
598 | xref[oldxrefno] = xinfo
599 | end
600 | end
601 | end
602 | end
603 | return(doc)
604 | end
605 |
606 |
607 | local function Pandoc_finalizexref(doc)
608 | xref = fbx.xref -- shortcut
609 | local bla = {}
610 | -- pout("------- finale ----")
611 | for i, blk in ipairs(doc.blocks) do
612 | bla = blk.attributes
613 | --pout(bla)
614 | if bla then
615 | if bla._process_me == "true" then
616 | ------------- an fbox :) ------------
617 | xindex = tonumber(bla._xrefno)
618 | if xindex then
619 | xref[xindex].neu = true -- mark as new entry
620 | if bla._title then xref[xindex].title = bla._title end
621 | end
622 |
623 | -- else pout("ochje.")
624 | end
625 | end
626 | end
627 | -- pout(xref)
628 | --- write to disc --
629 | --- check if this was the last file to process ---
630 | return(doc)
631 | end
632 |
633 |
634 | local function Meta_writexref(meta)
635 | local xref = fbx.xref
636 | local xrjson = quarto.json.encode(fbx.xref)
637 | local file = io.open(fbx.xreffile,"w")
638 | if file ~= nil then
639 | file:write(xrjson)
640 | file:close()
641 | end
642 | if fbx.islastfile then
643 | -- pout(fbx.processedfile.." -- nu aufräum! aber zack ---")
644 | for i, v in ipairs(xref) do
645 | if not v.neu then
646 | -- pout("killed")
647 | -- pout(v)
648 | xref[i] = nil
649 | end
650 | end
651 | -- pout("-------- überlebende")
652 | -- pout(xref)
653 | end
654 | end
655 |
656 |
657 | local function Meta_readxref(meta)
658 | local file = io.open(fbx.xreffile,"r")
659 | if file then
660 | local xrfjson = file:read "*a"
661 | file:close()
662 | --[[
663 | if xrfjson then meta.fbx.xref = quarto.json.decode(xrfjson)
664 | else meta.fbx.xref = {} end
665 | -- pout("eingelesen")
666 | -- pout(meta.fbx.xref)
667 | else meta.fbx.xref ={}
668 | --]]--
669 | if xrfjson then fbx.xref = quarto.json.decode(xrfjson)
670 | else fbx.xref = {} end
671 | -- pout("eingelesen")
672 | --pout(fbx.xref)
673 | else fbx.xref ={}
674 | end
675 | return(meta)
676 | end
677 | -------------- render -------------------
678 | -- render = require ("fbx4")
679 |
680 | local tt_from_attributes_id = function(A, id)
681 | --local tyti =""
682 | --local tt = {}
683 | --if A._tag == "" then tyti = A._label
684 | --else tyti = A._label..' '..A._tag end
685 | -- print("TYTI: === "..tyti)
686 | local thelink = "#"..id
687 | if fbx.ishtmlbook and A._file~=nil then thelink = A._file..".qmd"..thelink end
688 | return {id = id,
689 | type = A._fbxclass,
690 | tag = A._tag,
691 | title = A._title,
692 | typlabel = A._label,
693 | typlabelTag = A._label .. ifelse(A._tag == "",""," "..A._tag),
694 | mdtitle = A._mdtitle,
695 | collapse = A._collapse,
696 | boxstyle = A._boxstyle,
697 | link = thelink
698 | }
699 | -- pout("====nun====");pout(tt)
700 | --return(tt)
701 | end
702 |
703 | insertStylesPandoc = function(doc)
704 | -- if stylez.extractStyleFromYaml then stylez.extractStyleFromYaml() end
705 | if stylez.insertPreamble and (quarto.doc.is_format("html") or quarto.doc.is_format("pdf"))
706 | then stylez.insertPreamble(doc, fbx.classDefaults, fmt) end
707 | return(doc)
708 | end;
709 |
710 | renderDiv = function(thediv)
711 | local A = thediv.attributes
712 | local tt = {}
713 | if A._fbxclass ~= nil then
714 |
715 | collapsstr = str(A._collapse)
716 | tt = tt_from_attributes_id(A, thediv.identifier)
717 |
718 | local fmt='html'
719 | if quarto.doc.is_format("pdf") then fmt = "tex" end;
720 | if #thediv.content > 0 and thediv.content[1].t == "Para" and
721 | thediv.content[#thediv.content].t == "Para" then
722 | table.insert(thediv.content[1].content, 1,
723 | pandoc.RawInline(fmt, stylez.blockStart(tt, fmt)))
724 | table.insert(thediv.content,
725 | pandoc.RawInline(fmt, stylez.blockEnd(tt, fmt)))
726 | else
727 | table.insert(thediv.content, 1,
728 | pandoc.RawBlock(fmt, stylez.blockStart(tt, fmt)))
729 | table.insert(thediv.content,
730 | pandoc.RawBlock(fmt, stylez.blockEnd(tt, fmt)))
731 | end
732 | --]]
733 | end
734 | return(thediv)
735 | end -- function renderDiv
736 |
737 |
738 | ------------- xrefs
739 | -- learned from nameref extension by shafayeedShafee
740 | -- TODO: make everything with walk. Looks so nice
741 | local function resolveref(data)
742 | return {
743 | RawInline = function(el)
744 | local refid = el.text:match("\\ref{(.*)}")
745 | if refid then
746 | if data[refid] then
747 | local href = '#'..refid
748 | if fbx.ishtmlbook then
749 | href = data[refid].file .. '.html' .. href
750 | end
751 | return pandoc.Link(data[refid].refnum, href)
752 | end end
753 | end
754 | }
755 | end
756 |
757 | -- TODO: with filenames for books
758 |
759 | function Pandoc_resolvexref(doc)
760 | local xrefdata = {}
761 | local xref = fbx.xref
762 | for _, xinf in pairs(xref) do
763 | if xinf.id then if xinf.id ~= "" then
764 | xrefdata[xinf.id] = xinf
765 | end end
766 | end
767 | -- pout(xrefdata)
768 | return doc:walk(resolveref(xrefdata))
769 | end
770 | -----------
771 |
772 | --- remove all attributes that start with underscore.
773 | -- could theoretically give clashes with filters that need persistent such attributes
774 | function Div_cleanupAttribs (el)
775 | if el.attributes._process_me then
776 | for k, v in pairs(el.attributes) do
777 | if string.sub(k, 1, 1) =="_" then el.attributes[k] = nil end
778 | end
779 | end
780 | return el
781 | end
782 |
783 | -- debugging stuff
784 | --[[
785 |
786 | local function pandocblocks(doc)
787 | for k,v in pairs(doc.blocks) do
788 | pout(v.t)
789 | end
790 | end
791 |
792 | local function pandocdivs(div)
793 | pout(div.t.." - "..div.identifier)
794 | pout(div.attributes)
795 | end
796 | ]]--
797 |
798 | local function Pandoc_makeListof(doc)
799 | local tt = {}
800 | local thelists = {}
801 | local zeile = ""
802 | local lstfilemode = ifelse(fbx.isfirstfile, "w", "a")
803 | if not fbx.lists then return(doc) end
804 | for i, blk in ipairs(doc.blocks) do
805 | --[[ -- this may require manual deletion of headers in the list-of.qmd
806 | -- and does in this form not help with html books anyway --
807 | if blk.t=="Header" then
808 | if blk.level==1 then
809 | zeile = "\n\n## "..str(blk.content).."\n"
810 | --- add to all lists
811 | for _, lst in pairs (fbx.lists) do
812 | lst.contents = lst.contents..zeile
813 | end
814 | end
815 | elseif blk.t=="Div" then
816 | ]]--
817 | if blk.t=="Div" then
818 | if blk.attributes._process_me then
819 | thelists = fbx.classDefaults[blk.attributes._fbxclass].listin
820 | if thelists ~= nil and thelists ~="" then
821 | tt = tt_from_attributes_id (blk.attributes, blk.identifier)
822 | -- pout("thett------");pout(tt)
823 | -- zeile = ("\n[**"..tt.typtitel.."**](#"..blk.identifier..")"..ifelse(tt.mdtitle=="","",": "..tt.mdtitle)..
824 | -- " \\Pageref{"..blk.identifier.."}\n")
825 | -- TODO: should be like [**R-tip 1.1**](intro.qmd#Rtip-install)
826 | --zeile = ("\n[**"..tt.titeltyp.." \\ref{"..blk.identifier.."}**]" ..
827 | -- ifelse(tt.mdtitle=="","",": "..tt.mdtitle) ..
828 | -- " \\Pageref{"..blk.identifier.."}\n")
829 | zeile = ("\n [**"..tt.typlabelTag.."**](" .. tt.link ..")" ..
830 | ifelse(tt.mdtitle=="","",": "..tt.mdtitle) .. "\\Pageref{".. tt.id .."}\n")
831 | for _, lst in ipairs (thelists) do
832 | fbx.lists[lst].contents = fbx.lists[lst].contents..zeile
833 | end
834 | end
835 | end
836 | end
837 | end
838 | --- write to file ---
839 | for nam, lst in pairs(fbx.lists) do
840 | if lst.file ~= nil then
841 | local file = io.open(lst.file, lstfilemode)
842 | if file then
843 | file:write(lst.contents)
844 | file:close()
845 | else pout("cannot write to file "..lst.file)
846 | end
847 | end
848 | end
849 | return(doc)
850 | end
851 |
852 | return{
853 | {Meta = initMeta}
854 | --[[
855 | ,{Pandoc = function(d)
856 | for k, v in pairs(stylez) do
857 | pout(k..": ".. type(v))
858 | end
859 | end
860 | }
861 | ]]--
862 | , {Meta = Meta_readxref, Div=fboxDiv_mark_for_processing,
863 | Pandoc = Pandoc_prefix_count}
864 | -- , {Div=pandocdivs, Pandoc=pandocblocks}
865 | --[[ ]]
866 |
867 | , {Div = Divs_getid, Pandoc = Pandoc_preparexref}
868 | , {Pandoc = Pandoc_resolvexref}
869 | , {Div = Divs_maketitle}
870 | , {Pandoc = Pandoc_finalizexref}
871 | , {Meta = Meta_writexref, Pandoc = Pandoc_makeListof}
872 | , {Div = renderDiv}
873 | , {Pandoc = insertStylesPandoc}
874 | , {Div = Div_cleanupAttribs}
875 | --[[
876 |
877 | -- ]]
878 | }
879 |
880 |
--------------------------------------------------------------------------------
/_extensions/custom-numbered-blocks/custom-numbered-blocks.lua.bak:
--------------------------------------------------------------------------------
1 | --[[
2 | MIT License
3 |
4 | Copyright (c) 2023 Ute Hahn
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 | ]]--
24 | -- pre-pre-release
25 | --
26 | -- partial rewrite, complete later
27 |
28 | -- nice rename function learned from shafayetShafee :-)
29 | local str = pandoc.utils.stringify
30 | local pout = quarto.log.output
31 |
32 | -- important quasi global variables
33 |
34 | local ishtml = quarto.doc.is_format("html")
35 | local ispdf = quarto.doc.is_format("pdf")
36 | local fmt=""
37 | if ishtml then fmt="html" elseif ispdf then fmt = "pdf" end
38 |
39 | -- TODO encapsulate stylez into a doc thing or so
40 | -- maybe later allow various versions concurrently.
41 | -- this could probably be problematic because of option clashes in rendered header?
42 |
43 | local stylename="foldbox"
44 |
45 | local stylez = require("style/"..stylename)
46 |
47 |
48 | --- TODO: better encapsulation (luxury :-P)
49 |
50 | fbx={ -- global table, holds information for processing fboxes
51 | xreffile = "._xref.json" -- default name, set to lastfile in initial meta analysis
52 | }
53 |
54 |
55 | -- utility functions ---
56 | local function DeInline(tbl)
57 | local result ={}
58 | for i, v in pairs(tbl) do
59 | pdtype = pandoc.utils.type(v)
60 | if pdtype == "Inlines" or pdtype =="boolean"
61 | then
62 | result[i] = str(v)
63 | elseif pdtype == "List" then result[i] = DeInline(v)
64 | end
65 | end
66 | return(result)
67 | end
68 |
69 | local function tablecontains(tbl, val)
70 | if tbl ~= nil and val ~= nil then
71 | for _, v in ipairs(tbl) do
72 | if val == v then return true end
73 | end
74 | end
75 | return false
76 | end
77 |
78 | local function ifelse(condition, iftrue, iffalse)
79 | if condition then return iftrue else return iffalse end
80 | end
81 |
82 | local function replaceifnil(existvalue, replacevalue)
83 | if existvalue ~= nil then return existvalue else return replacevalue end
84 | end
85 |
86 | local function replaceifempty(existvalue, replacevalue)
87 | if existvalue == nil or existvalue=="" then return replacevalue else return existvalue end
88 | end
89 |
90 |
91 | local function updateTable (oldtbl, newtbl, ignorekeys)
92 | local result = {}
93 | -- copy old attributes
94 | for k, v in pairs(oldtbl) do result[k] = v end
95 | if newtbl ~= nil then if type(newtbl) == "table" then
96 | if newtbl[1] == nil then -- it is an ok table with key value pairs
97 | for k, v in pairs(newtbl) do
98 | if not(tablecontains(ignorekeys, k)) then
99 | result[k] = v
100 | end
101 | end
102 | -- special: set reflabel to label if not given in attribs
103 | -- if newattribs["reflabel"] == nil then result.reflabel = result.label end
104 | -- TODO: do this elsewhere
105 | end
106 | end
107 | end
108 | return(result)
109 | end
110 |
111 |
112 |
113 | ---- init step ---------------------------
114 | --- init = require ("fbx1")
115 |
116 | -- find chapter number and file name
117 | -- returns a table with keyed entries
118 | -- processedfile: string,
119 | -- ishtmlbook: boolean,
120 | -- chapno: string (at least if ishtmlbook),
121 | -- unnumbered: boolean - initial state of section / chapter
122 | -- if the user has given a chapno yaml entry, then unnumbered = false
123 |
124 | -- !!! for pdf, the workflow is very different! ---
125 | -- also find out if lastfile of a book
126 |
127 | -- find first and last file of a book, and chapter number of that file
128 | local function chapterinfo(book, fname)
129 | local first = ""
130 | local last = ""
131 | local chapno = nil
132 | local info = {}
133 | --if book.render then
134 | for _, v in pairs(book.render) do
135 | if str(v.type) == "chapter" then
136 | last = pandoc.path.split_extension(str(v.file))
137 | if first == "" then first = last end
138 | if last == fname then chapno = v.number end
139 | end
140 | end
141 | info.islast = (fname == last)
142 | info.isfirst = (fname == first)
143 | info.lastchapter = last
144 | info.chapno = chapno
145 | pout("chapter inf:", info)
146 | return(info)
147 | end
148 |
149 | local function Meta_findChapterNumber(meta)
150 | local processedfile = pandoc.path.split_extension(PANDOC_STATE.output_file)
151 | fbx.processedfile = processedfile
152 | fbx.xreffile ="._"..processedfile.."_xref.json"
153 | fbx.output_file = PANDOC_STATE.output_file
154 | -- pout(" now in "..processedfile.." later becomes ".. str(fbx.output_file))
155 | fbx.ishtmlbook = meta.book ~= nil and not quarto.doc.is_format("pdf")
156 | fbx.isfirstfile = not fbx.ishtmlbook
157 | fbx.islastfile = not fbx.ishtmlbook
158 | if fbx.ishtmlbook then
159 | local chinfo = chapterinfo(meta.book, processedfile)
160 | fbx.xreffile= "._"..chinfo.lastchapter.."_xref.json"
161 | fbx.isfirstfile = chinfo.isfirst
162 | fbx.islastfile = chinfo.islast
163 |
164 | fbx.unnumbered = false
165 | -- user set chapter number overrides information from meta
166 | if meta.chapno then
167 | fbx.chapno = str(meta.chapno)
168 | else
169 | if chinfo.chapno ~= nil then
170 | fbx.chapno = str(chinfo.chapno)
171 | else
172 | fbx.chapno = ""
173 | fbx.unnumbered = true
174 | end
175 | end
176 | else -- not a book.
177 | fbx.chapno = ""
178 | fbx.unnumbered = true
179 | end
180 | end
181 |
182 | local function makeKnownClassDetector(knownclasses)
183 | return function(div)
184 | for _, cls in pairs(div.classes) do
185 | if tablecontains(knownclasses, cls) then return str(cls) end
186 | end
187 | return nil
188 | end
189 | end
190 |
191 | local function Meta_initClassDefaults (meta)
192 | -- do we want to prefix fbx numbers with section numbers?
193 | local cunumbl = meta["custom-numbered-blocks"]
194 | fbx.knownclasses = {}
195 | fbx.lists = {}
196 | --[[ TODO later
197 | if meta.fbx_number_within_sections then
198 | fbx.number_within_sections = meta.fbx_number_within_sections
199 | else
200 | fbx.number_within_sections = false
201 | end
202 | --]]
203 | -- prepare information for numbering fboxes by class
204 | -- fbx.knownClasses ={}
205 | fbx.classDefaults ={}
206 | local groupDefaults = {default = stylez.defaultOptions} -- not needed later
207 | fbx.counter = {unnumbered = 0} -- counter for unnumbered divs
208 | -- ! unnumbered not for classes that have unnumbered as default !
209 | -- fbx.counterx = {}
210 | if cunumbl.classes == nil then
211 | print("== @%!& == Warning == &!%@ ==\n wrong format for fboxes yaml: classes needed")
212 | return
213 | end
214 |
215 | -- simplified copy of yaml data: inlines to string
216 | if cunumbl.groups then
217 | for key, val in pairs(cunumbl.groups) do
218 | local ginfo = DeInline(val)
219 | --[[
220 | pout("==== before after ======="); pout(ginfo)
221 | if ginfo.boxstyle then
222 | local mainstyle, substyle = ginfo.boxstyle:match "([^.]*).(.*)"
223 | -- pout("main "..mainstyle.." :: "..substyle)
224 | -- TODO: here account for multiple styles
225 | end
226 | --]]--
227 | ginfo = updateTable(stylez.defaultOptions, ginfo)
228 | --fbx.
229 | groupDefaults[key] = ginfo
230 | -- pout("-----group---"); pout(ginfo)
231 | end
232 | end
233 | for key, val in pairs(cunumbl.classes) do
234 | local clinfo = DeInline(val)
235 | -- pout("==== before after ======="); pout(clinfo)
236 | -- classinfo[key] = DeInline(val)
237 | table.insert(fbx.knownclasses, str(key))
238 | local theGroup = replaceifnil(clinfo.group, "default")
239 | clinfo = updateTable(groupDefaults[theGroup], clinfo)
240 | clinfo.label = replaceifnil(clinfo.label, str(key))
241 | clinfo.reflabel = replaceifnil(clinfo.reflabel, clinfo.label)
242 | -- assign counter --
243 | clinfo.cntname = replaceifnil(clinfo.group, str(key))
244 | fbx.counter[clinfo.cntname] = 0 -- sets the counter up if non existing
245 | fbx.classDefaults[key] = clinfo
246 | -- pout("---class----"); pout(clinfo)
247 | end
248 | fbx.is_cunumblo = makeKnownClassDetector(fbx.knownclasses)
249 | -- gather lists-of and make filenames by going through all classes
250 | for _, val in pairs(fbx.classDefaults) do
251 | -- pout("--classdefault: "..str(key))
252 | -- pout(val)
253 | if val.listin then
254 | for _,v in ipairs(val.listin) do
255 | fbx.lists[v] = {file = "list-of-"..str(v)..".qmd"}
256 | end
257 | end
258 | end
259 | -- initialize lists
260 | for key, val in pairs(fbx.lists) do
261 | val.contents = ifelse(fbx.isfirstfile, "\\providecommand{\\Pageref}[1]{\\hfill p.\\pageref{#1}}", "")
262 | -- listin approach does not require knownclass, since listin is in classdefaults
263 | end
264 | -- pout(fbx.lists)
265 | --]]
266 | -- document can give the chapter number for books in yaml header
267 | -- this becomes the counter Prefix
268 | end
269 |
270 | local initMeta = function(m)
271 | if m["custom-numbered-blocks"] then
272 | Meta_findChapterNumber(m)
273 | Meta_initClassDefaults(m)
274 | else
275 | print("== @%!& == Warning == &!%@ ==\n missing cunumblo key in yaml")
276 | end
277 | return(m)
278 | end
279 |
280 | ----------------------- oldcode, mostly -------------------------------------
281 |
282 |
283 | ------- numbering and class attributes ------
284 |
285 | local function fboxDiv_setAttributes(el, cls, prefix)
286 | local ela = el.attributes -- shortcut
287 | local ClassDef = fbx.classDefaults[cls]
288 | --local unnumbered = ClassDef.numbered == "false"
289 | local numbered = ClassDef.numbered ~= "false"
290 | local tag = ela.tag
291 | local tagged = tag ~= nil
292 | local id = el.identifier
293 | local autoid =""
294 | -- local titl = ela.title
295 | local cntkey = ClassDef.cntname
296 | local counter = {}
297 | local cnts = 0
298 | local idnumber = "0.0"
299 |
300 | -- set prefix
301 | ela._prefix = prefix
302 |
303 | id = replaceifnil(id ,"")
304 | tag = replaceifnil(tag ,"")
305 |
306 | --- determine if numbered and / or tagged ------
307 |
308 | if tagged then numbered = false end
309 | if el.classes:includes("unnumbered") then numbered = false end
310 |
311 | if ela.numtag then
312 | tag = ela.numtag
313 | -- print("!!! also hier mal ein numtag.\n")
314 | numbered = true
315 | tagged = true
316 | end
317 |
318 | -- make counts ---
319 |
320 | if not numbered then cntkey = "unnumbered" end
321 |
322 | cnts = fbx.counter[cntkey] +1
323 | fbx.counter[cntkey] = cnts
324 |
325 | idnumber = ifelse(prefix ~= "", prefix .. '.' .. cnts, str(cnts))
326 | --[[
327 | if prefix ~="" then idnumber = prefix .. '.' .. cnts
328 | else idnumber = str(cnts)
329 | end
330 |
331 | if numbered then
332 | if not tagged then tag = idnumber
333 | else tag = idnumber.."("..tag..")"
334 | end
335 | end
336 | ]]--
337 | if numbered then tag = idnumber..ifelse(tagged, "("..tag..")", "" ) end
338 |
339 | if id == "" then
340 | if numbered then
341 | autoid = ela._fbxclass..'-'..tag
342 | else
343 | autoid = ela._fbxclass..'*-'..idnumber
344 | end
345 | -- changed my mind here: always give autoid
346 | else autoid = ela._fbxclass..'-id-'..id
347 | end
348 |
349 | -- do not change identifier el.identifier = id
350 |
351 | ela._autoid = autoid
352 |
353 | ela._tag = tag
354 |
355 | ela._file = fbx.processedfile -- necessary to reference between chapters. At least with quarto 1.3
356 | -- pout("tag: "..tag)
357 | -- pout(ela)
358 | return(el)
359 | end
360 |
361 | -- initial attributes without prefix and counts to allow for inner boxes
362 |
363 | local function fboxDiv_mark_for_processing(div)
364 | local diva=div.attributes
365 | local cls = fbx.is_cunumblo(div)
366 | local ClassDef = fbx.classDefaults[cls]
367 | if(cls) then
368 | diva._process_me = "true"
369 | diva._fbxclass = str(cls)
370 | diva._prefix = ""
371 | diva._tag = ""
372 | diva._collapse = str(replaceifnil(diva.collapse, ClassDef.collapse))
373 | diva._boxstyle = str(replaceifnil(diva.boxstyle, ClassDef.boxstyle))
374 | diva._label = str(replaceifnil(diva.label, ClassDef.label))
375 | diva._reflabel = str(replaceifnil(diva.reflabel, ClassDef.reflabel))
376 | end
377 | return(div)
378 | end
379 |
380 |
381 |
382 | local function Pandoc_prefix_count(doc)
383 | -- do evt later: non numeric chapernumbers
384 | local secno = 0
385 | local prefix = "0"
386 | if fbx.ishtmlbook then prefix = fbx.chapno end
387 |
388 | -- pout("this is a book?"..str(fbx.ishtmlbook))
389 |
390 | --- make numbering and prep div blocks ---
391 | --[[------- comment -----------
392 | quarto (1.2) books allow level 1 headings within a chapter.
393 | This would give a mess for crossreference numbers: e.g. multiple examples 3.1,
394 | from chapter 1 (with 2 l1 headers ) and chapter 3.
395 | Therefore I decided to ignore level 1 headings in chapters.
396 | This can easily be changed, then the crossref is for the last occurence only.
397 | Maybe one day when there is more fine tuning concerning individual numbering depth.
398 | If this happens before quarto 1.4
399 |
400 | --]]---------- end comment ------------
401 | for i, blk in ipairs(doc.blocks) do
402 | -- print(prefix.."-"..i.." "..blk.t.."\n")
403 |
404 | if blk.t=="Header" and not fbx.ishtmlbook then
405 | if (blk.level == 1) then -- increase prefix
406 | if blk.classes:includes("unnumbered")
407 | then
408 | prefix = ""
409 | else
410 | secno = secno + 1
411 | prefix = str(secno)
412 | end
413 | -- reset counters in fbx --
414 | -- this would be more complicated if there are different levels
415 | -- of numbering depth
416 | -- then: add a numdepth variable to fbx with a list of keys
417 | for k in pairs(fbx.counter) do fbx.counter[k]=0 end
418 | end
419 |
420 | -- problem: only the outer divs are captured
421 | elseif blk.t=="Div" then
422 | local known = fbx.is_cunumblo(blk)
423 | if known then
424 | blk = fboxDiv_setAttributes(blk, known, prefix)
425 | end
426 | end
427 | end
428 | return(doc)
429 | end
430 |
431 | -- if no id, get from first included header, if possible
432 | local function Divs_getid(el)
433 | -- local known = getKnownEnv(el.attr.classes)
434 | local ela = el.attributes
435 | local id = el.identifier
436 |
437 | if not ela._process_me then return(el) end
438 | -- pout("--- processing item with id ".. replaceifempty(id, "LEER"))
439 |
440 | if id == nil or id =="" then
441 | -- try in next header
442 | el1 = el.content[1]
443 | if el1.t=="Header" then
444 | -- pout("--- looking at header with id "..el1.identifier)
445 | -- pout("--- still processing item with id ".. replaceifempty(id, "LEER"))
446 | -- pout("replacing id")
447 | id = el1.identifier
448 | el.identifier = id
449 | end
450 | end
451 | if id == nil or id ==""
452 | then
453 | -- pout("immer noch leer")
454 | if ela._autoid ~= nil then
455 | id = ela._autoid
456 | el.identifier = id
457 | end
458 | --else pout("nix autoid in ");pout(ela._autoid)
459 | end
460 | -- pout("resulting el:"); pout(el.attr)
461 | return(el)
462 | end
463 |
464 |
465 | --- utility function: stringify and sanitize math, depends on format ---
466 | local function str_sanimath(theInline, fmt)
467 | local newstring = theInline:walk{
468 | Math = function(ma)
469 | local mathtxt = str(ma.text)
470 | if fmt == "html" then
471 | return {'\\(' .. mathtxt .. '\\)'}
472 | elseif fmt == "pdf" then
473 | return {'\\(' .. mathtxt .. '\\)'}
474 | elseif fmt == "md" then
475 | return {'$' .. mathtxt .. '$'}
476 | else return {mathtxt}
477 | end
478 | end
479 | }
480 | return str(newstring)
481 | end
482 |
483 |
484 | ----------- title of divs ----------------
485 | local function Divs_maketitle(el)
486 | -- local known = getKnownEnv(el.attr.classes)
487 | local ela = el.attributes
488 | local titl = ela.title
489 | local mdtitl = replaceifnil(ela.title, "")
490 | local ClassDef = {}
491 | -- local id = el.identifier
492 |
493 | if not ela._process_me then return(el) end
494 | -- pout("--- processing item with id ".. replaceifempty(el.identifier, "LEER"))
495 |
496 | ClassDef = fbx.classDefaults[ela._fbxclass]
497 |
498 | if titl == nil then
499 | el1 = el.content[1]
500 | if el1.t=="Header" then
501 | -- sanitize math inline. depends on format
502 | ela.title = str(el1.content) -- readable version without math
503 | mdtitl = str_sanimath(el1.content, "md")
504 | if ishtml then titl = str_sanimath(el1.content, "html")
505 | elseif ispdf then titl = str_sanimath(el1.content, "pdf")
506 | else titl = mdtitl
507 | end
508 | -- pout("--- looking at header with id "..el1.identifier)
509 | -- pout("--- still processing item with id ".. replaceifempty(id, "LEER"))
510 | --[[
511 | if id =="" or id == nil then
512 | pout("replacing id")
513 | id = el1.identifier
514 | el.identifier = id
515 | end
516 | ]]--
517 | table.remove(el.content, 1)
518 | else titl = ""
519 | end
520 | end
521 | ela._title = titl -- keep the title as attribute for pandoc
522 | ela._mdtitle = mdtitl -- for list of
523 | -- replace empty identifier with autoid
524 | -- if el.identifier == "" then el.identifier = ela._autoid end
525 | -- pout("--> sanitarer titel: "..mdtitl)
526 | -- ela._tag = ""
527 | -- pout("resulting el:"); pout(el)
528 | return(el)
529 | end
530 |
531 | ---------------- initialize xref ----------
532 | -- xrefinit = require ("fbx3")
533 |
534 | -- xref split into prepare and finalize to allow xref in titles (future)
535 | local function Pandoc_preparexref(doc)
536 | -- local xref={}
537 | local id = ""
538 | local cnt = 0
539 | local bla={}
540 | local xinfo={}
541 | local file_autoid = {}
542 | local exists = false
543 | if fbx.xref == nil then fbx.xref ={} end
544 | xref = fbx.xref
545 | cnt = #xref
546 | if cnt > 0 then
547 | for i, xinf in ipairs(xref) do
548 | file_autoid[xinf.file..xinf.autoid] = i
549 | end
550 | -- pout(autoids)
551 | end
552 | for _, blk in ipairs(doc.blocks) do
553 | if blk.attributes then
554 | bla = blk.attributes
555 | if bla._process_me then
556 | --pout("fbox "..blk.attributes._tag)
557 | ------------- an fbox :) ------------
558 | if blk.identifier == nil then id = ""
559 | else id = blk.identifier end
560 | xinfo = {
561 | id = id,
562 | autoid = bla._autoid,
563 | cls = bla._fbxclass,
564 | label = bla._label,
565 | reflabel = bla._reflabel,
566 | reftag = bla._tag,
567 | refnum = replaceifempty(bla._tag, "??"),
568 | file = fbx.output_file
569 | }
570 | -- if not xinfo.reftag then xinfo.reftag ="" end
571 | -- if xinfo.refnum == "" then xinfo.refnum ="??" end
572 | --[[
573 | if bla._tag
574 | then
575 | if bla._tag ~="" then xinfo.refnum = bla._tag else xinfo.reftag="??" end
576 | end
577 | ]]--
578 | --- check if autoid already exist in database. otherwise update
579 | oldxrefno = file_autoid[xinfo.file..xinfo.autoid]
580 | if oldxrefno == nil then
581 | cnt = cnt+1
582 | bla._xrefno = cnt
583 | table.insert (xref, cnt, xinfo)
584 | else
585 | bla._xrefno = oldxrefno
586 | xref[oldxrefno] = xinfo
587 | end
588 | end
589 | end
590 | end
591 | return(doc)
592 | end
593 |
594 |
595 | local function Pandoc_finalizexref(doc)
596 | xref = fbx.xref -- shortcut
597 | local bla = {}
598 | -- pout("------- finale ----")
599 | for i, blk in ipairs(doc.blocks) do
600 | bla = blk.attributes
601 | --pout(bla)
602 | if bla then
603 | if bla._process_me == "true" then
604 | ------------- an fbox :) ------------
605 | xindex = tonumber(bla._xrefno)
606 | if xindex then
607 | xref[xindex].neu = true -- mark as new entry
608 | if bla._title then xref[xindex].title = bla._title end
609 | end
610 |
611 | -- else pout("ochje.")
612 | end
613 | end
614 | end
615 | -- pout(xref)
616 | --- write to disc --
617 | --- check if this was the last file to process ---
618 | return(doc)
619 | end
620 |
621 |
622 | local function Meta_writexref(meta)
623 | local xref = fbx.xref
624 | local xrjson = quarto.json.encode(fbx.xref)
625 | local file = io.open(fbx.xreffile,"w")
626 | if file ~= nil then
627 | file:write(xrjson)
628 | file:close()
629 | end
630 | if fbx.islastfile then
631 | -- pout(fbx.processedfile.." -- nu aufräum! aber zack ---")
632 | for i, v in ipairs(xref) do
633 | if not v.neu then
634 | -- pout("killed")
635 | -- pout(v)
636 | xref[i] = nil
637 | end
638 | end
639 | -- pout("-------- überlebende")
640 | -- pout(xref)
641 | end
642 | end
643 |
644 |
645 | local function Meta_readxref(meta)
646 | local file = io.open(fbx.xreffile,"r")
647 | if file then
648 | local xrfjson = file:read "*a"
649 | file:close()
650 | --[[
651 | if xrfjson then meta.fbx.xref = quarto.json.decode(xrfjson)
652 | else meta.fbx.xref = {} end
653 | -- pout("eingelesen")
654 | -- pout(meta.fbx.xref)
655 | else meta.fbx.xref ={}
656 | --]]--
657 | if xrfjson then fbx.xref = quarto.json.decode(xrfjson)
658 | else fbx.xref = {} end
659 | -- pout("eingelesen")
660 | --pout(fbx.xref)
661 | else fbx.xref ={}
662 | end
663 | return(meta)
664 | end
665 | -------------- render -------------------
666 | -- render = require ("fbx4")
667 |
668 | local tt_from_attributes_id = function(A, id)
669 | --local tyti =""
670 | --local tt = {}
671 | --if A._tag == "" then tyti = A._label
672 | --else tyti = A._label..' '..A._tag end
673 | -- print("TYTI: === "..tyti)
674 | local thelink = "#"..id
675 | if fbx.ishtmlbook and A._file~=nil then thelink = A._file..".qmd"..thelink end
676 | return {id = id,
677 | type = A._fbxclass,
678 | tag = A._tag,
679 | title = A._title,
680 | typlabel = A._label,
681 | typlabelTag = A._label .. ifelse(A._tag == "",""," "..A._tag),
682 | mdtitle = A._mdtitle,
683 | collapse = A._collapse,
684 | boxstyle = A._boxstyle,
685 | link = thelink
686 | }
687 | -- pout("====nun====");pout(tt)
688 | --return(tt)
689 | end
690 |
691 | insertStylesPandoc = function(doc)
692 | -- if stylez.extractStyleFromYaml then stylez.extractStyleFromYaml() end
693 | if stylez.insertPreamble and (quarto.doc.is_format("html") or quarto.doc.is_format("pdf"))
694 | then stylez.insertPreamble(doc, fbx.classDefaults, fmt) end
695 | return(doc)
696 | end;
697 |
698 | renderDiv = function(thediv)
699 | local A = thediv.attributes
700 | local tt = {}
701 | if A._fbxclass ~= nil then
702 |
703 | collapsstr = str(A._collapse)
704 | tt = tt_from_attributes_id(A, thediv.identifier)
705 |
706 | local fmt='html'
707 | if quarto.doc.is_format("pdf") then fmt = "tex" end;
708 | if #thediv.content > 0 and thediv.content[1].t == "Para" and
709 | thediv.content[#thediv.content].t == "Para" then
710 | table.insert(thediv.content[1].content, 1,
711 | pandoc.RawInline(fmt, stylez.blockStart(tt, fmt)))
712 | table.insert(thediv.content,
713 | pandoc.RawInline(fmt, stylez.blockEnd(tt, fmt)))
714 | else
715 | table.insert(thediv.content, 1,
716 | pandoc.RawBlock(fmt, stylez.blockStart(tt, fmt)))
717 | table.insert(thediv.content,
718 | pandoc.RawBlock(fmt, stylez.blockEnd(tt, fmt)))
719 | end
720 | --]]
721 | end
722 | return(thediv)
723 | end -- function renderDiv
724 |
725 |
726 | ------------- xrefs
727 | -- learned from nameref extension by shafayeedShafee
728 | -- TODO: make everything with walk. Looks so nice
729 | local function resolveref(data)
730 | return {
731 | RawInline = function(el)
732 | local refid = el.text:match("\\ref{(.*)}")
733 | if refid then
734 | if data[refid] then
735 | local href = '#'..refid
736 | if fbx.ishtmlbook then
737 | href = data[refid].file .. href
738 | end
739 | return pandoc.Link(data[refid].refnum, href)
740 | end end
741 | end
742 | }
743 | end
744 |
745 | -- TODO: with filenames for books
746 |
747 | function Pandoc_resolvexref(doc)
748 | local xrefdata = {}
749 | local xref = fbx.xref
750 | for _, xinf in pairs(xref) do
751 | if xinf.id then if xinf.id ~= "" then
752 | xrefdata[xinf.id] = xinf
753 | end end
754 | end
755 | -- pout(xrefdata)
756 | return doc:walk(resolveref(xrefdata))
757 | end
758 | -----------
759 |
760 | --- remove all attributes that start with underscore.
761 | -- could theoretically give clashes with filters that need persistent such attributes
762 | function Div_cleanupAttribs (el)
763 | if el.attributes._process_me then
764 | for k, v in pairs(el.attributes) do
765 | if string.sub(k, 1, 1) =="_" then el.attributes[k] = nil end
766 | end
767 | end
768 | return el
769 | end
770 |
771 | -- debugging stuff
772 | --[[
773 |
774 | local function pandocblocks(doc)
775 | for k,v in pairs(doc.blocks) do
776 | pout(v.t)
777 | end
778 | end
779 |
780 | local function pandocdivs(div)
781 | pout(div.t.." - "..div.identifier)
782 | pout(div.attributes)
783 | end
784 | ]]--
785 |
786 | local function Pandoc_makeListof(doc)
787 | local tt = {}
788 | local thelists = {}
789 | local zeile = ""
790 | local lstfilemode = ifelse(fbx.isfirstfile, "w", "a")
791 | if not fbx.lists then return(doc) end
792 | for i, blk in ipairs(doc.blocks) do
793 | --[[ -- this may require manual deletion of headers in the list-of.qmd
794 | -- and does in this form not help with html books anyway --
795 | if blk.t=="Header" then
796 | if blk.level==1 then
797 | zeile = "\n\n## "..str(blk.content).."\n"
798 | --- add to all lists
799 | for _, lst in pairs (fbx.lists) do
800 | lst.contents = lst.contents..zeile
801 | end
802 | end
803 | elseif blk.t=="Div" then
804 | ]]--
805 | if blk.t=="Div" then
806 | if blk.attributes._process_me then
807 | thelists = fbx.classDefaults[blk.attributes._fbxclass].listin
808 | if thelists ~= nil and thelists ~="" then
809 | tt = tt_from_attributes_id (blk.attributes, blk.identifier)
810 | -- pout("thett------");pout(tt)
811 | -- zeile = ("\n[**"..tt.typtitel.."**](#"..blk.identifier..")"..ifelse(tt.mdtitle=="","",": "..tt.mdtitle)..
812 | -- " \\Pageref{"..blk.identifier.."}\n")
813 | -- TODO: should be like [**R-tip 1.1**](intro.qmd#Rtip-install)
814 | --zeile = ("\n[**"..tt.titeltyp.." \\ref{"..blk.identifier.."}**]" ..
815 | -- ifelse(tt.mdtitle=="","",": "..tt.mdtitle) ..
816 | -- " \\Pageref{"..blk.identifier.."}\n")
817 | zeile = ("\n [**"..tt.typlabelTag.."**](" .. tt.link ..")" ..
818 | ifelse(tt.mdtitle=="","",": "..tt.mdtitle) .. "\\Pageref{".. tt.id .."}\n")
819 | for _, lst in ipairs (thelists) do
820 | fbx.lists[lst].contents = fbx.lists[lst].contents..zeile
821 | end
822 | end
823 | end
824 | end
825 | end
826 | --- write to file ---
827 | for nam, lst in pairs(fbx.lists) do
828 | if lst.file ~= nil then
829 | local file = io.open(lst.file, lstfilemode)
830 | if file then
831 | file:write(lst.contents)
832 | file:close()
833 | else pout("cannot write to file "..lst.file)
834 | end
835 | end
836 | end
837 | return(doc)
838 | end
839 |
840 | return{
841 | {Meta = initMeta}
842 | --[[
843 | ,{Pandoc = function(d)
844 | for k, v in pairs(stylez) do
845 | pout(k..": ".. type(v))
846 | end
847 | end
848 | }
849 | ]]--
850 | , {Meta = Meta_readxref, Div=fboxDiv_mark_for_processing,
851 | Pandoc = Pandoc_prefix_count}
852 | -- , {Div=pandocdivs, Pandoc=pandocblocks}
853 | --[[ ]]
854 |
855 | , {Div = Divs_getid, Pandoc = Pandoc_preparexref}
856 | , {Pandoc = Pandoc_resolvexref}
857 | , {Div = Divs_maketitle}
858 | , {Pandoc = Pandoc_finalizexref}
859 | , {Meta = Meta_writexref, Pandoc = Pandoc_makeListof}
860 | , {Div = renderDiv}
861 | , {Pandoc = insertStylesPandoc}
862 | , {Div = Div_cleanupAttribs}
863 | --[[
864 |
865 | -- ]]
866 | }
867 |
868 |
--------------------------------------------------------------------------------
/_extensions/custom-numbered-blocks/style/foldbox.css:
--------------------------------------------------------------------------------
1 | .fbx-default{
2 | --color1: #c7c7d0;
3 | --color2: #a3a3aa;
4 |
5 | --border-color: var(--color2);
6 | --title-color: #1b1b1b;
7 | --title-background-color: var(--color1);
8 | --title-padding: 0.5rem;
9 | --border-radius: .3rem;
10 |
11 | --padding-left: .8rem; /* for title (summary) and content (next div) */
12 | --padding-content: 0.5rem;
13 |
14 | --thickshadow: 4px 4px 6px #555;
15 | --thinshadow: 1px 1px 2px #555;
16 | --noshadow: 0px 0px 0px #FFF;
17 | }
18 |
19 |
20 | /* for button placement */
21 | details.fbx-default[open] {
22 | position: relative;
23 | }
24 |
25 | details.fbx-default > summary{
26 | background-color: var(--title-background-color);
27 | color: var(--title-color);
28 |
29 | cursor: pointer;
30 |
31 | border-radius: var(--border-radius);
32 | border-left: var(--border-radius) solid var(--border-color);
33 | border-bottom: 1px solid var(--border-color);
34 | box-shadow: var(--thinshadow);
35 |
36 | padding: var(--title-padding);
37 | padding-left: var(--padding-left);
38 | }
39 |
40 | details.fbx-default > summary:hover{
41 | box-shadow: var(--thickshadow);
42 | }
43 |
44 | details.fbx-default[open] > summary{
45 | border-bottom-left-radius: 0px;
46 | }
47 |
48 | details.fbx-default > div{
49 | padding: var(--padding-content);
50 | padding-left: var(--padding-left);
51 | padding-bottom: 0;
52 |
53 | border-left: var(--border-radius) solid var(--border-color);
54 | border-top: 0;
55 | margin-top: 0;
56 | border-left: .3rem solid var(--border-color);
57 | border-bottom: 1px solid var(--border-color);
58 | border-bottom-left-radius: .3rem;
59 | }
60 |
61 | /* simple box */
62 |
63 | details.fbx-simplebox > div{
64 | padding-bottom: 0;
65 | border-right: 1px solid var(--border-color);
66 | border-bottom-right-radius: .3rem;
67 | }
68 |
69 |
70 | /* ------ closebutton --- */
71 |
72 | .closebutton{
73 | --close-text: "▲";
74 | --close-background-color: #dddddd;
75 | --close-hover-background-color: var(--title-background-color);
76 | --close-hover-color: var(--title-color);
77 | }
78 |
79 | /* adjust this by hand if close button gets in the way: just add at the end
80 | details.closebutton[open]{
81 | padding-bottom: 1rem;
82 | }
83 | */
84 |
85 | details.closebutton[open] > summary::after {
86 | display: button;
87 | content: var(--close-text);
88 | /*
89 | background-color: var(--close-background-color);
90 | */
91 | border: 1px solid var(--close-background-color);
92 | border-radius: 0.3rem;
93 | padding: .2rem 0.5rem 0.2rem 0.5rem;
94 | box-shadow: var(--thinshadow);
95 | position: absolute;
96 | right: 0;/*1rem;*/
97 | bottom: .5rem;
98 | }
99 |
100 | details.closebutton[open] > summary:hover::after {
101 | background-color: var(--close-hover-background-color);
102 | border: 1px solid var(--border-color);
103 | color: var(--close-hover-color);
104 | box-shadow: var(--thickshadow);
105 | }
106 |
107 |
108 |
--------------------------------------------------------------------------------
/_extensions/custom-numbered-blocks/style/foldbox.lua:
--------------------------------------------------------------------------------
1 | -- nice rename function learned from shafayetShafee :-)
2 | local str = pandoc.utils.stringify
3 | local pout = quarto.log.output
4 |
5 |
6 | return {
7 |
8 | defaultOptions={
9 | numbered = "true",
10 | boxstyle = "foldbox.default",
11 | collapse = "true",
12 | colors = {"c0c0c0","808080"}
13 | },
14 |
15 | blockStart = function (tt, fmt)
16 | local Open =""
17 | local BoxStyle =" fbx-default closebutton"
18 | local texEnv = "fbx"
19 | if #tt.title > 0 then tt.typlabelTag = tt.typlabelTag..": " end
20 | if fmt =="html" then
21 | if tt.collapse =="false" then Open=" open" end
22 | if tt.boxstyle =="foldbox.simple"
23 | then
24 | BoxStyle=" fbx-simplebox fbx-default"
25 | -- Open=" open" do not force override. Chose this in yaml or individually.
26 | -- we would want e.g to have remarks closed by default
27 | end
28 | result = (''..''..tt.typlabelTag..''..tt.title .. '
')
29 | return result
30 |
31 | elseif fmt =="tex" then
32 | if tt.boxstyle=="foldbox.simple" then texEnv = "fbxSimple" end
33 | return('\\begin{'..texEnv..'}{'..tt.type..'}{'..tt.typlabelTag..'}{'..tt.title..'}\n'..
34 | '\\phantomsection\\label{'..tt.id..'}\n')
35 | else
36 | return("Hallihallo")
37 | end
38 | end,
39 |
40 | blockEnd = function (tt, fmt)
41 | local texEnv = "fbx"
42 | if fmt =="html" then
43 | return('
')
44 | elseif fmt =="tex" then
45 | if tt.boxstyle=="foldbox.simple" then texEnv = "fbxSimple" end
46 | return('\\end{'..texEnv..'}\n')
47 | else return ('ende mit format '..fmt..'=================')
48 | end
49 | end,
50 |
51 | insertPreamble = function(doc, classDefs, fmt)
52 | local ishtml = quarto.doc.is_format("html")
53 | local ispdf = quarto.doc.is_format("pdf")
54 | local StyleCSSTeX = {}
55 |
56 | -- if fmt==nil then pout("=== NIX ======= Format ") else
57 | -- pout("============== Format : "..str(fmt)) end
58 | -- make css or preamble tex for colors
59 | local extractStyleFromMeta = function (fmt)
60 | local result
61 | if classDefs ~= nil then
62 | for cls, options in pairs(classDefs) do
63 | --quarto.log.output(cls)
64 | if options.colors then
65 | -- quarto.log.output(" --> Farben!")
66 | if fmt == "html" then
67 | table.insert(StyleCSSTeX, "."..cls.." {\n")
68 | for i, col in ipairs(options.colors) do
69 | table.insert(StyleCSSTeX, " --color"..i..": #"..col..";\n")
70 | end
71 | table.insert(StyleCSSTeX, "}\n")
72 | elseif fmt == "pdf" then
73 | for i, col in ipairs(options.colors) do
74 | table.insert(StyleCSSTeX, "\\definecolor{"..cls.."-color"..i.."}{HTML}{"..col.."}\n")
75 | end
76 | end
77 | end
78 | end
79 | end
80 | result = pandoc.utils.stringify(StyleCSSTeX)
81 | if fmt == "html" then result = "" end
82 | if fmt == "pdf" then result="%%==== colors from yaml ===%\n"..result.."%=============%\n" end
83 | return(result)
84 | end
85 |
86 | local preamblestuff = extractStyleFromMeta(fmt)
87 | -- quarto.log.output(preamblestuff)
88 |
89 | if fmt == "html"
90 | then
91 | quarto.doc.add_html_dependency({
92 | name = 'foldbox',
93 | -- version = '0.0.1',
94 | --stylesheets = {'style/'..style..'.css'}
95 | stylesheets = {'style/foldbox.css'}
96 | })
97 | elseif fmt == "pdf"
98 | then
99 | quarto.doc.use_latex_package("tcolorbox","many")
100 | quarto.doc.include_file("in-header", 'style/foldbox.tex')
101 | end
102 | if preamblestuff then quarto.doc.include_text("in-header", preamblestuff) end
103 | return(doc)
104 | end
105 | }
--------------------------------------------------------------------------------
/_extensions/custom-numbered-blocks/style/foldbox.tex:
--------------------------------------------------------------------------------
1 | %%%% ---foldboxy preamble ----- %%%%%
2 |
3 | \definecolor{fbx-default-color1}{HTML}{c7c7d0}
4 | \definecolor{fbx-default-color2}{HTML}{a3a3aa}
5 |
6 | \definecolor{fbox-color1}{HTML}{c7c7d0}
7 | \definecolor{fbox-color2}{HTML}{a3a3aa}
8 |
9 | % arguments: #1 typelabelnummer: #2 titel: #3
10 | \newenvironment{fbx}[3]{\begin{tcolorbox}[enhanced, breakable,%
11 | attach boxed title to top*={xshift=1.4pt},
12 | boxed title style={boxrule=0.0mm, fuzzy shadow={1pt}{-1pt}{0mm}{0.1mm}{gray}, arc=.3em, rounded corners=east, sharp corners=west}, colframe=#1-color2, colbacktitle=#1-color1, colback = white, coltitle=black, titlerule=0mm, toprule=0pt, bottomrule=.7pt, leftrule=.3em, rightrule=0pt, outer arc=.3em, arc=0pt, sharp corners = east, left=.5em, bottomtitle=1mm, toptitle=1mm,title=\textbf{#2}\hspace{0.5em}{#3}]}
13 | {\end{tcolorbox}}
14 |
15 | % boxed environment with right border
16 | \newenvironment{fbxSimple}[3]{\begin{tcolorbox}[enhanced, breakable,%
17 | attach boxed title to top*={xshift=1.4pt},
18 | boxed title style={boxrule=0.0mm, fuzzy shadow={1pt}{-1pt}{0mm}{0.1mm}{gray}, arc=.3em, rounded corners=east, sharp corners=west}, colframe=#1-color2, colbacktitle=#1-color1, colback = white, coltitle=black, titlerule=0mm, toprule=0pt, bottomrule=.7pt, leftrule=.3em, rightrule=.7pt, outer arc=.3em, left=.5em, right=.5em, bottomtitle=1mm, toptitle=1mm,title=\textbf{#2}\hspace{0.5em}{#3}]}
19 | {\end{tcolorbox}}
20 |
21 | %%%% --- end foldboxy preamble ----- %%%%%
22 |
--------------------------------------------------------------------------------
/doc/example.html:
--------------------------------------------------------------------------------
1 |
2 |
With this filter, you can define custom div classes (environments) that come with numbering, such as theorems, examples, exercises. The filter supports output formats pdf and html.
98 |
99 | Feature 1.1: Numbering
100 |
Numbering is (currently) within section for single documents, or within chapter for books. Grouped classes share the same counter, and the same default style.
101 |
Numbered custom blocks can be cross-referenced with \ref.
102 |
Default numbering can be switched off for the whole class by setting the numbered to false, or for an individual block by adding the class unnumbered.
103 |
Crossreferences my need a re-run to update.
104 |
105 | Feature: Boxes can be nested
However, inner boxes are not numbered – it would be hard to put them in a sequence with outer boxes anyway.
106 |
107 |
108 |
109 |
110 |
111 | Feature 1.2: Block box style
The default style for custom divs is foldbox: a collapsible similar to quarto’s callouts, with a collapse button at the bottom that makes it easier collapse long boxes, and box open to the right. It comes with the variant foldbox.simple, with closed box and no additional close button. (needs a fix for the moment)
112 |
113 |
114 |
115 | To do 1.1: Custom styles
116 |
117 |
create an API for user defined block styles
118 |
provide an example
119 |
and documentation
120 |
121 |
122 |
123 |
124 | Done, may change 1.2: Custom list of blocks
Generate .qmd files that contains a list of selected block classes, intermitted with headers from the document for easy customization and commenting. This way, one can make a list of all definitions, examples, or {theorems, propositions and lemmas} etc., edit it later and attach to the main document. If you edit, make sure to rename the autogenerated list first, otherwise it will get overwritten in the next run and all work is lost …
125 |
Currently, you need to give a key listin for any class or group of classes that should appear in a list of things. The value of this key is an array of names, also if only one list is to be generated. These names are turned into files list-of-name.qmd. I am considering replacing the yaml interface by a sub-key to custom-numbered-classes. This would allow to define arbitrary classes that can be attached to any custom div block, such as .important.
126 |
127 |
128 |
129 |
130 |
2 Pseudomath examples
131 |
132 | Definition 2.1: F\(\alpha\)ncybox
A box is called f\(\alpha\)ncybox if it looks quite fancy.
133 |
In this context, by fancy we mean that the title of the box appears as a clickable button when rendered as html, where clickable implies that it throws a small shadow that becomes bigger when hovering over it.
134 |
135 |
136 |
137 | Definition 2.2
A box is called f\(\alpha\)ncybox if it looks quite fancy.
138 |
In this context, by fancy we mean that the title of the box appears as a clickable button when rendered as html, where clickable implies that it throws a small shadow that becomes bigger when hovering over it.
139 |
140 |
141 |
142 | Corollary 2.3
By Definition 2.1, foldboxes are fancyboxes.
143 |
144 |
145 |
146 | Conjecture 2.4
Students are lured into clicking on the title and unfolding the fancybox.
147 |
148 |
149 |
150 | Theorem 2.5
This extension has been written by a teacher who hopes that students read the course notes…
151 |
152 |
153 |
Theorem 2.5 is suggested by Conjecture 2.4, but it cannot be proven theoretically. It does give rise to more conjectures, though. 2.1
154 |
155 | Conjecture
The teacher mentioned in Theorem 2.5 is a statistician who got addicted to quarto due to James J Balamuta’s web-r extension, and desparately wanted to have a common counter for theorem and alike. She got also convinced that everything is possible in quarto by the many nice extensions from Shafayet Khan Shafee.
156 |