-- 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: 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 = "" -- inject the rendering code quarto.doc.include_text("after-body", scriptTag) end end }}