Skip to content

Commit 3c13dca

Browse files
committed
Add Piper's blog post and add lightbox extensions
1 parent d337888 commit 3c13dca

File tree

7 files changed

+419
-20
lines changed

7 files changed

+419
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
title: Lightbox
2+
author: Posit Software, PBC
3+
version: 0.1.9
4+
quarto-required: ">=1.2.198"
5+
contributes:
6+
filters:
7+
- lightbox.lua
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
3+
4+
body:not(.glightbox-mobile) div.gslide div.gslide-description,
5+
body:not(.glightbox-mobile) div.gslide-description .gslide-title,
6+
body:not(.glightbox-mobile) div.gslide-description .gslide-desc {
7+
color: var(--quarto-body-color);
8+
background-color: var(--quarto-body-bg);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
-- whether we're automatically lightboxing
2+
local auto = false
3+
4+
-- whether we need lightbox dependencies added
5+
local needsLightbox = false
6+
7+
-- a counter used to ensure each image is in its own gallery
8+
local imgCount = 0
9+
10+
-- attributes to forward from the image to the newly created link
11+
local kDescription = "description"
12+
local kForwardedAttr = {
13+
"title", kDescription, "desc-position",
14+
"type", "effect", "zoomable", "draggable"
15+
}
16+
17+
local kLightboxClass = "lightbox"
18+
local kNoLightboxClass = "nolightbox"
19+
local kGalleryPrefix = "quarto-lightbox-gallery-"
20+
21+
-- A list of images already within links that we can use to filter
22+
local imagesWithinLinks = pandoc.List({})
23+
24+
local function readAttrValue(el, attrName)
25+
if attrName == kDescription then
26+
local doc = pandoc.read(el.attr.attributes[attrName])
27+
local attrInlines = doc.blocks[1].content
28+
return pandoc.write(pandoc.Pandoc(attrInlines), "html")
29+
else
30+
return el[attrName]
31+
end
32+
33+
end
34+
35+
return {
36+
{
37+
Meta = function(meta)
38+
39+
-- If the mode is auto, we need go ahead and
40+
-- run if there are any images (ideally we would)
41+
-- filter to images in the body, but that can be
42+
-- left for future me to deal with
43+
-- supports:
44+
-- lightbox: auto
45+
-- or
46+
-- lightbox:
47+
-- match: auto
48+
local lbMeta = meta.lightbox
49+
if lbMeta ~= nil and type(lbMeta) == 'table' then
50+
if lbMeta[1] ~= nil then
51+
if lbMeta[1]['text'] == "auto" then
52+
auto = true
53+
end
54+
elseif lbMeta.match ~= nil and pandoc.utils.stringify(lbMeta.match) == 'auto' then
55+
auto = true
56+
elseif lbMeta == true then
57+
auto = true
58+
end
59+
end
60+
end,
61+
-- Find images that are already within links
62+
-- we'll use this to filter out these images if
63+
-- the most is auto
64+
Link = function(linkEl)
65+
pandoc.walk_inline(linkEl, {
66+
Image = function(imageEl)
67+
imagesWithinLinks[#imagesWithinLinks + 1] = imageEl
68+
end
69+
})
70+
end
71+
},{
72+
Div = function(div)
73+
if div.classes:includes("cell") and div.attributes["lightbox"] ~= nil then
74+
meta = quarto.json.decode(div.attributes["lightbox"])
75+
local imgCount=0
76+
div = div:walk({
77+
Image = function(imgEl)
78+
imgCount = imgCount + 1
79+
if (type(meta) == "table" and meta[kNoLightboxClass] == true) or meta == false then
80+
imgEl.classes:insert(kNoLightboxClass)
81+
else
82+
if not auto and ((type(meta) == "table" and not meta[kNoLightboxClass]) or meta == true) then
83+
imgEl.classes:insert(kLightboxClass)
84+
end
85+
if (type(meta) == "table") then
86+
if meta.group then
87+
imgEl.attr.attributes.group = meta.group or imgEl.attr.attributes.group
88+
end
89+
for _, v in next, kForwardedAttr do
90+
if type(meta[v]) == "table" and #meta[v] > 1 then
91+
-- if list attributes it should be one per plot
92+
if imgCount > #meta[v] then
93+
quarto.log.warning("More plots than '" .. v .. "' passed in YAML chunk options.")
94+
else
95+
attrLb = meta[v][imgCount]
96+
end
97+
else
98+
-- Otherwise reuse the single attributes
99+
attrLb = meta[v]
100+
end
101+
imgEl.attr.attributes[v] = attrLb or imgEl.attr.attributes[v]
102+
end
103+
end
104+
end
105+
return imgEl
106+
end
107+
})
108+
div.attributes["lightbox"] = nil
109+
end
110+
return div
111+
end
112+
},
113+
{
114+
Image = function(imgEl)
115+
if quarto.doc.is_format("html:js") then
116+
local isAlreadyLinked = imagesWithinLinks:includes(imgEl)
117+
if (not isAlreadyLinked and auto and not imgEl.classes:includes(kNoLightboxClass))
118+
or imgEl.classes:includes('lightbox') then
119+
-- note that we need to include the dependency for lightbox
120+
needsLightbox = true
121+
imgCount = imgCount + 1
122+
123+
-- remove the class from the image
124+
imgEl.attr.classes = imgEl.attr.classes:filter(function(clz)
125+
return clz ~= kLightboxClass
126+
end)
127+
128+
-- attributes for the link
129+
local linkAttributes = {}
130+
131+
-- mark this image as a lightbox target
132+
linkAttributes.class = kLightboxClass
133+
134+
-- get the alt text from image and use that as title
135+
local title = nil
136+
if imgEl.caption ~= nil and #imgEl.caption > 0 then
137+
linkAttributes.title = pandoc.utils.stringify(imgEl.caption)
138+
elseif imgEl.attributes['fig-alt'] ~= nil and #imgEl.attributes['fig-alt'] > 0 then
139+
linkAttributes.title = pandoc.utils.stringify(imgEl.attributes['fig-alt'])
140+
end
141+
142+
-- move a group attribute to the link, if present
143+
if imgEl.attr.attributes.group ~= nil then
144+
linkAttributes.gallery = imgEl.attr.attributes.group
145+
imgEl.attr.attributes.group = nil
146+
else
147+
linkAttributes.gallery = kGalleryPrefix .. imgCount
148+
end
149+
150+
-- forward any other known attributes
151+
for i, v in ipairs(kForwardedAttr) do
152+
if imgEl.attr.attributes[v] ~= nil then
153+
-- forward the attribute
154+
linkAttributes[v] = readAttrValue(imgEl, v)
155+
156+
-- clear the attribute
157+
imgEl.attr.attributes[v] = nil
158+
end
159+
160+
-- clear the title
161+
if (imgEl.title == 'fig:') then
162+
imgEl.title = ""
163+
end
164+
165+
end
166+
167+
-- wrap decorated images in a link with appropriate attrs
168+
local link = pandoc.Link({imgEl}, imgEl.src, nil, linkAttributes)
169+
return link
170+
end
171+
end
172+
end,
173+
Meta = function(meta)
174+
-- If we discovered lightbox-able images
175+
-- we need to include the dependencies
176+
if needsLightbox then
177+
-- add the dependency
178+
quarto.doc.add_html_dependency({
179+
name = 'glightbox',
180+
scripts = {'resources/js/glightbox.min.js'},
181+
stylesheets = {'resources/css/glightbox.min.css', 'lightbox.css'}
182+
})
183+
184+
-- read lightbox options
185+
local lbMeta = meta.lightbox
186+
local lbOptions = {}
187+
local readEffect = function(el)
188+
local val = pandoc.utils.stringify(el)
189+
if val == "fade" or val == "zoom" or val == "none" then
190+
return val
191+
else
192+
error("Invalid effect " + val)
193+
end
194+
end
195+
196+
-- permitted options include:
197+
-- lightbox:
198+
-- effect: zoom | fade | none
199+
-- desc-position: top | bottom | left |right
200+
-- loop: true | false
201+
-- class: <class-name>
202+
local effect = "zoom"
203+
local descPosition = "bottom"
204+
local loop = true
205+
local skin = nil
206+
207+
-- The selector controls which elements are targeted.
208+
-- currently, it always targets .lightbox elements
209+
-- and there is no way for the user to change this
210+
local selector = "." .. kLightboxClass
211+
212+
if lbMeta ~= nil and type(lbMeta) == 'table' then
213+
if lbMeta.effect ~= nil then
214+
effect = readEffect(lbMeta.effect)
215+
end
216+
217+
if lbMeta['desc-position'] ~= nil then
218+
descPosition = pandoc.utils.stringify(lbMeta['desc-position'])
219+
end
220+
221+
if lbMeta['css-class'] ~= nil then
222+
skin = pandoc.utils.stringify(lbMeta['css-class'])
223+
end
224+
225+
if lbMeta.loop ~= nil then
226+
loop = lbMeta.loop
227+
end
228+
end
229+
230+
-- Generate the options to configure lightbox
231+
local options = {
232+
selector = selector,
233+
closeEffect = effect,
234+
openEffect = effect,
235+
descPosition = descPosition,
236+
loop = loop,
237+
}
238+
if skin ~= nil then
239+
options.skin = skin
240+
end
241+
local optionsJson = quarto.json.encode(options)
242+
243+
-- generate the initialization script with the correct options
244+
local scriptTag = "<script>var lightboxQuarto = GLightbox(" .. optionsJson .. ");</script>"
245+
246+
-- inject the rendering code
247+
quarto.doc.include_text("after-body", scriptTag)
248+
249+
end
250+
end
251+
}}

0 commit comments

Comments
 (0)