File size: 8,200 Bytes
2e2cacf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
-- 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
}}