Spaces:
Running
Running
-- whether we're automatically lightboxing | |
local auto = false | |
-- whether we need lightbox dependencies added | |
local needsLightbox = false | |
-- a counter used to ensure each image is in its own gallery | |
local imgCount = 0 | |
-- attributes to forward from the image to the newly created link | |
local kDescription = "description" | |
local kForwardedAttr = { | |
"title", kDescription, "desc-position", | |
"type", "effect", "zoomable", "draggable" | |
} | |
local kLightboxClass = "lightbox" | |
local kNoLightboxClass = "nolightbox" | |
local kGalleryPrefix = "quarto-lightbox-gallery-" | |
-- A list of images already within links that we can use to filter | |
local imagesWithinLinks = pandoc.List({}) | |
local function readAttrValue(el, attrName) | |
if attrName == kDescription then | |
local doc = pandoc.read(el.attr.attributes[attrName]) | |
local attrInlines = doc.blocks[1].content | |
return pandoc.write(pandoc.Pandoc(attrInlines), "html") | |
else | |
return el[attrName] | |
end | |
end | |
return { | |
{ | |
Meta = function(meta) | |
-- If the mode is auto, we need go ahead and | |
-- run if there are any images (ideally we would) | |
-- filter to images in the body, but that can be | |
-- left for future me to deal with | |
-- supports: | |
-- lightbox: auto | |
-- or | |
-- lightbox: | |
-- match: auto | |
local lbMeta = meta.lightbox | |
if lbMeta ~= nil and type(lbMeta) == 'table' then | |
if lbMeta[1] ~= nil then | |
if lbMeta[1].text == "auto" then | |
auto = true | |
end | |
elseif lbMeta.match ~= nil and pandoc.utils.stringify(lbMeta.match) == 'auto' then | |
auto = true | |
elseif lbMeta == true then | |
auto = true | |
end | |
end | |
end, | |
-- Find images that are already within links | |
-- we'll use this to filter out these images if | |
-- the most is auto | |
Link = function(linkEl) | |
pandoc.walk_inline(linkEl, { | |
Image = function(imageEl) | |
imagesWithinLinks[#imagesWithinLinks + 1] = imageEl | |
end | |
}) | |
end | |
},{ | |
Div = function(div) | |
if div.classes:includes("cell") and div.attributes["lightbox"] ~= nil then | |
meta = quarto.json.decode(div.attributes["lightbox"]) | |
local imgCount=0 | |
div = div:walk({ | |
Image = function(imgEl) | |
imgCount = imgCount + 1 | |
if (type(meta) == "table" and meta[kNoLightboxClass] == true) or meta == false then | |
imgEl.classes:insert(kNoLightboxClass) | |
else | |
if not auto and ((type(meta) == "table" and not meta[kNoLightboxClass]) or meta == true) then | |
imgEl.classes:insert(kLightboxClass) | |
end | |
if (type(meta) == "table") then | |
if meta.group then | |
imgEl.attr.attributes.group = meta.group or imgEl.attr.attributes.group | |
end | |
for _, v in next, kForwardedAttr do | |
if type(meta[v]) == "table" and #meta[v] > 1 then | |
-- if list attributes it should be one per plot | |
if imgCount > #meta[v] then | |
quarto.log.warning("More plots than '" .. v .. "' passed in YAML chunk options.") | |
else | |
attrLb = meta[v][imgCount] | |
end | |
else | |
-- Otherwise reuse the single attributes | |
attrLb = meta[v] | |
end | |
imgEl.attr.attributes[v] = attrLb or imgEl.attr.attributes[v] | |
end | |
end | |
end | |
return imgEl | |
end | |
}) | |
div.attributes["lightbox"] = nil | |
end | |
return div | |
end | |
}, | |
{ | |
Image = function(imgEl) | |
if quarto.doc.is_format("html:js") then | |
local isAlreadyLinked = imagesWithinLinks:includes(imgEl) | |
if (not isAlreadyLinked and auto and not imgEl.classes:includes(kNoLightboxClass)) | |
or imgEl.classes:includes('lightbox') then | |
-- note that we need to include the dependency for lightbox | |
needsLightbox = true | |
imgCount = imgCount + 1 | |
-- remove the class from the image | |
imgEl.attr.classes = imgEl.attr.classes:filter(function(clz) | |
return clz ~= kLightboxClass | |
end) | |
-- attributes for the link | |
local linkAttributes = {} | |
-- mark this image as a lightbox target | |
linkAttributes.class = kLightboxClass | |
-- get the alt text from image and use that as title | |
local title = nil | |
if imgEl.caption ~= nil and #imgEl.caption > 0 then | |
linkAttributes.title = pandoc.utils.stringify(imgEl.caption) | |
elseif imgEl.attributes['fig-alt'] ~= nil and #imgEl.attributes['fig-alt'] > 0 then | |
linkAttributes.title = pandoc.utils.stringify(imgEl.attributes['fig-alt']) | |
end | |
-- move a group attribute to the link, if present | |
if imgEl.attr.attributes.group ~= nil then | |
linkAttributes.gallery = imgEl.attr.attributes.group | |
imgEl.attr.attributes.group = nil | |
else | |
linkAttributes.gallery = kGalleryPrefix .. imgCount | |
end | |
-- forward any other known attributes | |
for i, v in ipairs(kForwardedAttr) do | |
if imgEl.attr.attributes[v] ~= nil then | |
-- forward the attribute | |
linkAttributes[v] = readAttrValue(imgEl, v) | |
-- clear the attribute | |
imgEl.attr.attributes[v] = nil | |
end | |
-- clear the title | |
if (imgEl.title == 'fig:') then | |
imgEl.title = "" | |
end | |
end | |
-- wrap decorated images in a link with appropriate attrs | |
local link = pandoc.Link({imgEl}, imgEl.src, nil, linkAttributes) | |
return link | |
end | |
end | |
end, | |
Meta = function(meta) | |
-- If we discovered lightbox-able images | |
-- we need to include the dependencies | |
if needsLightbox then | |
-- add the dependency | |
quarto.doc.add_html_dependency({ | |
name = 'glightbox', | |
scripts = {'resources/js/glightbox.min.js'}, | |
stylesheets = {'resources/css/glightbox.min.css', 'lightbox.css'} | |
}) | |
-- read lightbox options | |
local lbMeta = meta.lightbox | |
local lbOptions = {} | |
local readEffect = function(el) | |
local val = pandoc.utils.stringify(el) | |
if val == "fade" or val == "zoom" or val == "none" then | |
return val | |
else | |
error("Invalid effect " + val) | |
end | |
end | |
-- permitted options include: | |
-- lightbox: | |
-- effect: zoom | fade | none | |
-- desc-position: top | bottom | left |right | |
-- loop: true | false | |
-- class: <class-name> | |
local effect = "zoom" | |
local descPosition = "bottom" | |
local loop = true | |
local skin = nil | |
-- The selector controls which elements are targeted. | |
-- currently, it always targets .lightbox elements | |
-- and there is no way for the user to change this | |
local selector = "." .. kLightboxClass | |
if lbMeta ~= nil and type(lbMeta) == 'table' then | |
if lbMeta.effect ~= nil then | |
effect = readEffect(lbMeta.effect) | |
end | |
if lbMeta['desc-position'] ~= nil then | |
descPosition = pandoc.utils.stringify(lbMeta['desc-position']) | |
end | |
if lbMeta['css-class'] ~= nil then | |
skin = pandoc.utils.stringify(lbMeta['css-class']) | |
end | |
if lbMeta.loop ~= nil then | |
loop = lbMeta.loop | |
end | |
end | |
-- Generate the options to configure lightbox | |
local options = { | |
selector = selector, | |
closeEffect = effect, | |
openEffect = effect, | |
descPosition = descPosition, | |
loop = loop, | |
} | |
if skin ~= nil then | |
options.skin = skin | |
end | |
local optionsJson = quarto.json.encode(options) | |
-- generate the initialization script with the correct options | |
local scriptTag = "<script>var lightboxQuarto = GLightbox(" .. optionsJson .. ");</script>" | |
-- inject the rendering code | |
quarto.doc.include_text("after-body", scriptTag) | |
end | |
end | |
}} | |