|
| 1 | +-- Check if variable missing or an empty string |
| 2 | +local function isVariableEmpty(s) |
| 3 | + return s == nil or s == '' |
| 4 | +end |
| 5 | + |
| 6 | +-- Check if variable is present |
| 7 | +local function isVariablePopulated(s) |
| 8 | + return not isVariableEmpty(s) |
| 9 | +end |
| 10 | + |
| 11 | +-- Check whether an argument is present in kwargs |
| 12 | +-- If it is, return the value |
| 13 | +local function tryOption(options, key) |
| 14 | + |
| 15 | + -- Protect against an empty options |
| 16 | + if not (options and options[key]) then |
| 17 | + return nil |
| 18 | + end |
| 19 | + |
| 20 | + -- Retrieve the option |
| 21 | + local option_value = pandoc.utils.stringify(options[key]) |
| 22 | + -- Verify the option's value exists, return value otherwise nil. |
| 23 | + if isVariablePopulated(option_value) then |
| 24 | + return option_value |
| 25 | + else |
| 26 | + return nil |
| 27 | + end |
| 28 | +end |
| 29 | + |
| 30 | +-- Retrieve the option value or use the default value |
| 31 | +local function getOption(options, key, default) |
| 32 | + return tryOption(options, key) or default |
| 33 | +end |
| 34 | + |
| 35 | +-- Validate input file path is not empty |
| 36 | +local function checkFile(input) |
| 37 | + if input then |
| 38 | + return true |
| 39 | + else |
| 40 | + quarto.log.error("Error: file path is required for the embedio shortcode.") |
| 41 | + assert(false) |
| 42 | + end |
| 43 | +end |
| 44 | + |
| 45 | +-- Avoid duplicate class definitions |
| 46 | +local initializedSlideCSS = false |
| 47 | + |
| 48 | +-- Load CSS into header once |
| 49 | +local function ensureSlideCSSPresent() |
| 50 | + if initializedSlideCSS then return end |
| 51 | + initializedSlideCSS = true |
| 52 | + |
| 53 | + -- Default CSS class |
| 54 | + local slideCSS = [[ |
| 55 | + <style> |
| 56 | + .slide-deck { |
| 57 | + border: 3px solid #dee2e6; |
| 58 | + width: 100%; |
| 59 | + } |
| 60 | + </style> |
| 61 | + ]] |
| 62 | + |
| 63 | + -- Inject into the header |
| 64 | + quarto.doc.include_text("in-header", slideCSS) |
| 65 | +end |
| 66 | + |
| 67 | +-- Define a function to generate HTML code for an iframe element |
| 68 | +local function iframe_helper(file_name, height, full_screen_link, class, template, type) |
| 69 | + -- Check if the file exists |
| 70 | + checkFile(file_name) |
| 71 | + |
| 72 | + if isVariablePopulated(class) then |
| 73 | + class = ' class="' .. class .. '"' |
| 74 | + else |
| 75 | + class = "" |
| 76 | + end |
| 77 | + |
| 78 | + -- Define a template for displaying a full-screen link |
| 79 | + local template_full_screen = [[ |
| 80 | + <p><a href=%q target="_blank">View %s in full screen</a></p> |
| 81 | + ]] |
| 82 | + |
| 83 | + -- Combine the template with file name and height to generate HTML code |
| 84 | + local combined_str = string.format( |
| 85 | + [[%s %s]], |
| 86 | + -- Include full-screen link if specified |
| 87 | + (full_screen_link == "true" and string.format(template_full_screen, file_name, type) or ""), |
| 88 | + -- Insert the iframe template with file name and height |
| 89 | + string.format(template, class, file_name, height) |
| 90 | + ) |
| 91 | + |
| 92 | + -- Return the combined HTML as a pandoc RawBlock |
| 93 | + return pandoc.RawBlock('html', combined_str) |
| 94 | +end |
| 95 | + |
| 96 | +-- Define the html() function for embedding HTML files |
| 97 | +local function html(args, kwargs, meta, raw_args) |
| 98 | + -- Check if the output format is HTML |
| 99 | + if not quarto.doc.is_format("html") then return end |
| 100 | + |
| 101 | + -- Get the HTML file name, height, and full-screen link option |
| 102 | + local file_name = pandoc.utils.stringify(args[1] or kwargs["file"]) |
| 103 | + local height = getOption(kwargs, "height", "475px") |
| 104 | + local full_screen_link = getOption(kwargs, "full-screen-link", "true") |
| 105 | + local class = getOption(kwargs, "class", "") |
| 106 | + |
| 107 | + -- Define the template for embedding HTML files |
| 108 | + local template_html = [[ |
| 109 | + <div%s> |
| 110 | + <iframe src=%q height=%q></iframe> |
| 111 | + </div> |
| 112 | + ]] |
| 113 | + |
| 114 | + -- Call the iframe_helper() function with the HTML template |
| 115 | + return iframe_helper(file_name, height, full_screen_link, class, template_html, "webpage") |
| 116 | +end |
| 117 | + |
| 118 | +-- Define the revealjs() function for embedding Reveal.js slides |
| 119 | +local function revealjs(args, kwargs, meta, raw_args) |
| 120 | + -- Check if the output format is HTML |
| 121 | + if not quarto.doc.is_format("html") then return end |
| 122 | + |
| 123 | + -- Ensure that the Reveal.js CSS is present |
| 124 | + ensureSlideCSSPresent() |
| 125 | + |
| 126 | + -- Get the slide file name, height, and full-screen link option |
| 127 | + local file_name = pandoc.utils.stringify(args[1] or kwargs["file"]) |
| 128 | + local height = getOption(kwargs, "height", "475px") |
| 129 | + local full_screen_link = getOption(kwargs, "full-screen-link", "true") |
| 130 | + local class = getOption(kwargs, "class", "") |
| 131 | + |
| 132 | + -- Define the template for embedding Reveal.js slides |
| 133 | + local template_revealjs = [[ |
| 134 | + <div%s> |
| 135 | + <iframe class="slide-deck" src=%q height=%q></iframe> |
| 136 | + </div> |
| 137 | + ]] |
| 138 | + |
| 139 | + -- Call the iframe_helper() function with the Reveal.js template |
| 140 | + return iframe_helper(file_name, height, full_screen_link, class, template_revealjs, "slides") |
| 141 | +end |
| 142 | + |
| 143 | +local function audio(args, kwargs, meta) |
| 144 | + |
| 145 | + if not quarto.doc.is_format("html") then return end |
| 146 | + |
| 147 | + -- Start of HTML tag |
| 148 | + local htmlTable = {"<figure "} |
| 149 | + |
| 150 | + -- Extracting caption, class, and download from kwargs |
| 151 | + local caption = pandoc.utils.stringify(kwargs.caption) |
| 152 | + local class = pandoc.utils.stringify(kwargs.class) |
| 153 | + local download_link = getOption(kwargs, "download-link", "false") |
| 154 | + |
| 155 | + -- Extracting input from args or kwargs |
| 156 | + local input = pandoc.utils.stringify(args[1] or kwargs.file) |
| 157 | + checkFile(input) |
| 158 | + |
| 159 | + -- Add class attribute if provided |
| 160 | + if class then |
| 161 | + table.insert(htmlTable, 'class="' .. class .. '">') |
| 162 | + end |
| 163 | + |
| 164 | + -- Add download link if provided |
| 165 | + if download_link == "true" then |
| 166 | + table.insert(htmlTable, '<p><a href="' .. input ..'"> Download audio file</a></p><br />') |
| 167 | + end |
| 168 | + |
| 169 | + -- Start the audio tag |
| 170 | + table.insert(htmlTable, "<audio controls") |
| 171 | + |
| 172 | + -- Add source attribute with input file path |
| 173 | + table.insert(htmlTable, ' src="' .. input .. '"') |
| 174 | + |
| 175 | + -- Automatically detect file type from the file extension |
| 176 | + local fileType = string.match(input, "%.([^%.]+)$") |
| 177 | + |
| 178 | + if fileType then |
| 179 | + table.insert(htmlTable, ' type="audio/' .. fileType .. '">') |
| 180 | + else |
| 181 | + quarto.log.error("Error: Unable to detect file type from the audio file path.") |
| 182 | + assert(false) |
| 183 | + end |
| 184 | + |
| 185 | + -- Add source element for browsers that do not support the audio tag |
| 186 | + table.insert(htmlTable, 'Your browser does not support the audio tag.') |
| 187 | + |
| 188 | + -- Add closing audio tag |
| 189 | + table.insert(htmlTable, "</audio>") |
| 190 | + |
| 191 | + -- Add caption if provided |
| 192 | + if caption then |
| 193 | + table.insert(htmlTable, '<figcaption>' .. caption .. '</figcaption>') |
| 194 | + end |
| 195 | + |
| 196 | + -- Add closing figure tag |
| 197 | + table.insert(htmlTable, "</figure>") |
| 198 | + |
| 199 | + return pandoc.RawBlock('html', table.concat(htmlTable)) |
| 200 | +end |
| 201 | + |
| 202 | + |
| 203 | +local function pdf(args, kwargs, meta) |
| 204 | + |
| 205 | + if not quarto.doc.is_format("html") then return end |
| 206 | + |
| 207 | + -- Supported options for now |
| 208 | + local pdf_file_name = pandoc.utils.stringify(args[1] or kwargs["file"]) |
| 209 | + checkFile(pdf_file_name) |
| 210 | + |
| 211 | + local height = getOption(kwargs, "height", "600px") |
| 212 | + local width = getOption(kwargs, "width", "100%") |
| 213 | + local download_link = getOption(kwargs, "download-link", "true") |
| 214 | + |
| 215 | + -- HTML block |
| 216 | + local template_pdf = [[ |
| 217 | + <object data=%q type="application/pdf" width=%q height=%q> |
| 218 | + <p>Unable to display PDF file. <a href=%q>Download</a> instead.</p> |
| 219 | + </object> |
| 220 | + ]] |
| 221 | + |
| 222 | + local template_pdf_download_link = [[ |
| 223 | + <p><a href=%q target="_blank">Download PDF File</a></p> |
| 224 | +]] |
| 225 | + |
| 226 | + -- Obtain the combined template |
| 227 | + local combined_str = string.format( |
| 228 | + [[%s %s]], |
| 229 | + (download_link == "true" and string.format(template_pdf_download_link, pdf_file_name) or ""), |
| 230 | + string.format(template_pdf, pdf_file_name, width, height, pdf_file_name) |
| 231 | + ) |
| 232 | + |
| 233 | + -- Return as HTML block |
| 234 | + return pandoc.RawBlock('html', combined_str) |
| 235 | +end |
| 236 | + |
| 237 | +return { |
| 238 | + ['audio'] = audio, |
| 239 | + ['pdf'] = pdf, |
| 240 | + ['revealjs'] = revealjs, |
| 241 | + ['html'] = html |
| 242 | +} |
0 commit comments