ox-s5.el (15973B)
1 ;;; ox-s5.el --- S5 Presentation Back-End for Org Export Engine 2 3 ;; Copyright (C) 2011-2014, 2021 Rick Frankel 4 5 ;; Author: Rick Frankel <emacs at rickster dot com> 6 ;; Keywords: outlines, hypermedia, S5, wp 7 8 ;; This file is not part of GNU Emacs. 9 10 ;; This program is free software; you can redistribute it and/or modify 11 ;; it under the terms of the GNU General Public License as published by 12 ;; the Free Software Foundation, either version 3 of the License, or 13 ;; (at your option) any later version. 14 15 ;; This program is distributed in the hope that it will be useful, 16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 ;; GNU General Public License for more details. 19 20 ;; You should have received a copy of the GNU General Public License 21 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 22 23 ;;; Commentary: 24 25 ;; This library implements an S5 Presentation back-end for the Org 26 ;; generic exporter. 27 28 ;; Installation 29 ;; ------------ 30 ;; Get the s5 scripts from 31 ;; https://meyerweb.com/eric/tools/s5/ 32 ;; (Note that the default s5 version is set for using the alpha, 1.2a2. 33 ;; Copy the ui dir to somewhere reachable from your published presentation 34 ;; The default (`org-s5-ui-url') is set to "ui" (e.g., in the 35 ;; same directory as the html file). 36 37 ;; Usage 38 ;; ----- 39 ;; Follow the general instructions at the above website. To generate 40 ;; incremental builds, you can set the HTML_CONTAINER_CLASS on an 41 ;; object to "incremental" to make it build. If you want an outline to 42 ;; build, set the :INCREMENTAL property on the parent headline. 43 44 ;; To test it, run: 45 ;; 46 ;; M-x org-s5-export-as-html 47 ;; 48 ;; in an Org mode buffer. See ox.el and ox-html.el for more details 49 ;; on how this exporter works. 50 51 ;; TODOs 52 ;; ------ 53 ;; The title page is formatted using format-spec. This is error prone 54 ;; when details are missing and may insert empty tags, like <h2></h2>, 55 ;; for missing values. 56 57 (require 'ox-html) 58 (eval-when-compile (require 'cl)) 59 60 (org-export-define-derived-backend 's5 'html 61 :menu-entry 62 '(?s "Export to S5 HTML Presentation" 63 ((?H "To temporary buffer" org-s5-export-as-html) 64 (?h "To file" org-s5-export-to-html) 65 (?o "To file and open" 66 (lambda (a s v b) 67 (if a (org-s5-export-to-html t s v b) 68 (org-open-file (org-s5-export-to-html nil s v b))))))) 69 :options-alist 70 '((:html-link-home "HTML_LINK_HOME" nil nil) 71 (:html-link-up "HTML_LINK_UP" nil nil) 72 (:s5-postamble "S5_POSTAMBLE" nil org-s5-postamble newline) 73 (:s5-preamble "S5_PREAMBLE" nil org-s5-preamble newline) 74 (:html-head-include-default-style "HTML_INCLUDE_DEFAULT_STYLE" nil nil) 75 (:html-head-include-scripts "HTML_INCLUDE_SCRIPTS" nil nil) 76 (:s5-version "S5_VERSION" nil org-s5-version) 77 (:s5-theme-file "S5_THEME_FILE" nil org-s5-theme-file) 78 (:s5-ui-url "S5_UI_URL" nil org-s5-ui-url) 79 (:s5-default-view "S5_DEFAULT_VIEW" nil org-s5-default-view) 80 (:s5-control-visibility "S5_CONTROL_VISIBILITY" nil 81 org-s5-control-visibility)) 82 :translate-alist 83 '((headline . org-s5-headline) 84 (plain-list . org-s5-plain-list) 85 (inner-template . org-s5-inner-template) 86 (template . org-s5-template))) 87 88 (defgroup org-export-s5 nil 89 "Options for exporting Org mode files to S5 HTML Presentations." 90 :tag "Org Export S5" 91 :group 'org-export-html) 92 93 (defcustom org-s5-version "1.2a2" 94 "Version of s5 being used (for version metadata.) Defaults to 95 s5 v2 alpha 2. 96 Can be overridden with S5_VERSION." 97 :group 'org-export-s5 98 :type 'string) 99 100 (defcustom org-s5-theme-file nil 101 "Url to S5 theme (slides.css) file. Can be overridden with the 102 S5_THEME_FILE property. If nil, defaults to 103 `org-s5-ui-url'/default/slides.css. If it starts with anything but 104 \"http\" or \"/\", it is used as-is. Otherwise the link in generated 105 relative to `org-s5-ui-url'. 106 The links for all other required stylesheets and scripts will be 107 generated relative to `org-s5-ui-url'/default." 108 :group 'org-export-s5 109 :type 'string) 110 111 (defcustom org-s5-ui-url "ui" 112 "Base url to directory containing S5 \"default\" subdirectory 113 and the \"s5-notes.html\" file. 114 Can be overridden with the S5_UI_URL property." 115 :group 'org-export-s5 116 :type 'string) 117 118 (defcustom org-s5-default-view 'slideshow 119 "Setting for \"defaultView\" meta info." 120 :group 'org-export-s5 121 :type '(choice (const slideshow) (const outline))) 122 123 (defcustom org-s5-control-visibility 'hidden 124 "Setting for \"controlVis\" meta info." 125 :group 'org-export-s5 126 :type '(choice (const hidden) (const visibile))) 127 128 (defvar org-s5--divs 129 '((preamble "div" "header") 130 (content "div" "content") 131 (postamble "div" "footer")) 132 "Alist of the three section elements for HTML export. 133 The car of each entry is one of 'preamble, 'content or 'postamble. 134 The cdrs of each entry are the ELEMENT_TYPE and ID for each 135 section of the exported document. 136 137 If you set `org-html-container-element' to \"li\", \"ol\" will be 138 uses as the content ELEMENT_TYPE, generating an XOXO format 139 slideshow. 140 141 Note that changing the preamble or postamble will break the 142 core S5 stylesheets.") 143 144 (defcustom org-s5-postamble "<h1>%a - %t</h1>" 145 "Preamble inserted into the S5 layout section. 146 When set to a string, use this string as the postamble. 147 148 When set to a function, apply this function and insert the 149 returned string. The function takes the property list of export 150 options as its only argument. 151 152 Setting the S5_POSTAMBLE option -- or the :s5-postamble in publishing 153 projects -- will take precedence over this variable. 154 155 Note that the default css styling will break if this is set to nil 156 or an empty string." 157 :group 'org-export-s5 158 :type '(choice (const :tag "No postamble" " ") 159 (string :tag "Custom formatting string") 160 (function :tag "Function (must return a string)"))) 161 162 (defcustom org-s5-preamble " " 163 "Peamble inserted into the S5 layout section. 164 165 When set to a string, use this string as the preamble. 166 167 When set to a function, apply this function and insert the 168 returned string. The function takes the property list of export 169 options as its only argument. 170 171 Setting S5_PREAMBLE option -- or the :s5-preamble in publishing 172 projects -- will take precedence over this variable. 173 174 Note that the default css styling will break if this is set to nil 175 or an empty string." 176 :group 'org-export-s5 177 :type '(choice (const :tag "No preamble" " ") 178 (string :tag "Custom formatting string") 179 (function :tag "Function (must return a string)"))) 180 181 (defcustom org-s5-title-slide-template 182 "<h1>%t</h1> 183 <h2>%s</h2> 184 <h2>%a</h2> 185 <h3>%e</h3> 186 <h4>%d</h4>" 187 "Format template to specify title page section. 188 See `org-html-postamble-format' for the valid elements which 189 can be included. 190 191 It will be wrapped in the element defined in the :html-container 192 property, and defaults to the value of `org-html-container-element', 193 and have the id \"title-slide\"." 194 :group 'org-export-s5 195 :type 'string) 196 197 (defun org-s5--format-toc-headline (headline info) 198 "Return an appropriate table of contents entry for HEADLINE. 199 Note that (currently) the S5 exporter does not support deep links, 200 so the table of contents is not \"active\". 201 INFO is a plist used as a communication channel." 202 (let* ((headline-number (org-export-get-headline-number headline info)) 203 (section-number 204 (and (not (org-export-low-level-p headline info)) 205 (org-export-numbered-headline-p headline info) 206 (concat (mapconcat 'number-to-string headline-number ".") ". "))) 207 (tags (and (eq (plist-get info :with-tags) t) 208 (org-export-get-tags headline info)))) 209 (concat section-number 210 (org-export-data 211 (org-export-get-alt-title headline info) info) 212 (and tags " ") (org-html--tags tags info)))) 213 214 (defun org-s5-toc (depth info) 215 (let* ((headlines (org-export-collect-headlines info depth)) 216 (toc-entries 217 (mapcar (lambda (headline) 218 (cons (org-s5--format-toc-headline headline info) 219 (org-export-get-relative-level headline info))) 220 (org-export-collect-headlines info depth)))) 221 (when toc-entries 222 (concat 223 (format "<%s id='table-of-contents' class='slide'>\n" 224 (plist-get info :html-container)) 225 (format "<h1>%s</h1>\n" 226 (org-html--translate "Table of Contents" info)) 227 "<div id=\"text-table-of-contents\">" 228 (org-html--toc-text toc-entries) 229 "</div>\n" 230 (format "</%s>\n" (plist-get info :html-container)))))) 231 232 (defun org-s5--build-head (info) 233 (let* ((dir (plist-get info :s5-ui-url)) 234 (theme (or (plist-get info :s5-theme-file) "default/slides.css"))) 235 (mapconcat 236 'identity 237 (list 238 "<!-- style sheet links -->" 239 (mapconcat 240 (lambda (list) 241 (format 242 (concat 243 "<link rel='stylesheet' href='%s/default/%s' type='text/css'" 244 " media='%s' id='%s' />") 245 dir (nth 0 list) (nth 1 list) (nth 2 list))) 246 (list 247 '("outline.css" "screen" "outlineStyle") 248 '("print.css" "print" "slidePrint") 249 '("opera.css" "projection" "operaFix")) "\n") 250 (format (concat 251 "<link rel='stylesheet' href='%s' type='text/css'" 252 " media='screen' id='slideProj' />") 253 (if (string-match-p "^\\(http\\|/\\)" theme) theme 254 (concat dir "/" theme))) 255 "<!-- S5 JS -->" 256 (concat 257 "<script src='" dir 258 "/default/slides.js'></script>")) "\n"))) 259 260 (defun org-s5--build-meta-info (info) 261 (concat 262 (org-html--build-meta-info info) 263 (format "<meta name=\"version\" content=\"S5 %s\" />\n" 264 (plist-get info :s5-version)) 265 (format "<meta name='defaultView' content='%s' />\n" 266 (plist-get info :s5-default-view)) 267 (format "<meta name='controlVis' content='%s' />" 268 (plist-get info :s5-control-visibility)))) 269 270 (defun org-s5-headline (headline contents info) 271 (let ((org-html-toplevel-hlevel 1) 272 (class (or (org-element-property :HTML_CONTAINER_CLASS headline) "")) 273 (level (org-export-get-relative-level headline info))) 274 (when (and (= 1 level) (not (string-match-p "\\<slide\\>" class))) 275 (org-element-put-property headline :HTML_CONTAINER_CLASS (concat class " slide"))) 276 (org-html-headline headline contents info))) 277 278 (defun org-s5-plain-list (plain-list contents info) 279 "Transcode a PLAIN-LIST element from Org to HTML. 280 CONTENTS is the contents of the list. INFO is a plist holding 281 contextual information. 282 If a containing headline has the property :INCREMENTAL, 283 then the \"incremental\" class will be added to the to the list, 284 which will make the list into a \"build\"." 285 (let* ((type (org-element-property :type plain-list)) 286 (tag (case type 287 (ordered "ol") 288 (unordered "ul") 289 (descriptive "dl")))) 290 (format "%s\n%s%s" 291 (format 292 "<%s class='org-%s%s'>" tag tag 293 (if (org-export-get-node-property :INCREMENTAL plain-list t) 294 " incremental" "")) 295 contents 296 (format "</%s>" tag)))) 297 298 (defun org-s5-inner-template (contents info) 299 "Return body of document string after HTML conversion. 300 CONTENTS is the transcoded contents string. INFO is a plist 301 holding export options." 302 (concat contents "\n")) 303 304 (defun org-s5-template (contents info) 305 "Return complete document string after HTML conversion. 306 CONTENTS is the transcoded contents string. INFO is a plist 307 holding export options." 308 (let ((info (plist-put 309 (plist-put 310 (plist-put info :html-preamble (plist-get info :s5-preamble)) 311 :html-postamble 312 (plist-get info :s5-postamble)) 313 :html-divs 314 (if (equal "li" (plist-get info :html-container)) 315 (cons '(content "ol" "content") org-s5--divs) 316 org-s5--divs)))) 317 (mapconcat 318 'identity 319 (list 320 (org-html-doctype info) 321 (format "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"%s\" xml:lang=\"%s\">" 322 (plist-get info :language) (plist-get info :language)) 323 "<head>" 324 (org-s5--build-meta-info info) 325 (org-s5--build-head info) 326 (org-html--build-head info) 327 (org-html--build-mathjax-config info) 328 "</head>" 329 "<body>" 330 "<div class=\"layout\">" 331 "<div id=\"controls\"><!-- no edit --></div>" 332 "<div id=\"currentSlide\"><!-- no edit --></div>" 333 (org-html--build-pre/postamble 'preamble info) 334 (org-html--build-pre/postamble 'postamble info) 335 "</div>" 336 (format "<%s id=\"%s\" class=\"presentation\">" 337 (nth 1 (assq 'content org-html-divs)) 338 (nth 2 (assq 'content org-html-divs))) 339 ;; title page 340 (format "<%s id='title-slide' class='slide'>" 341 (plist-get info :html-container)) 342 (format-spec org-s5-title-slide-template (org-html-format-spec info)) 343 (format "</%s>" (plist-get info :html-container)) 344 ;; table of contents. 345 (let ((depth (plist-get info :with-toc))) 346 (when depth (org-s5-toc depth info))) 347 contents 348 (format "</%s>" (nth 1 (assq 'content org-html-divs))) 349 "</body>" 350 "</html>\n") "\n"))) 351 352 (defun org-s5-export-as-html 353 (&optional async subtreep visible-only body-only ext-plist) 354 "Export current buffer to an HTML buffer. 355 356 If narrowing is active in the current buffer, only export its 357 narrowed part. 358 359 If a region is active, export that region. 360 361 A non-nil optional argument ASYNC means the process should happen 362 asynchronously. The resulting buffer should be accessible 363 through the `org-export-stack' interface. 364 365 When optional argument SUBTREEP is non-nil, export the sub-tree 366 at point, extracting information from the headline properties 367 first. 368 369 When optional argument VISIBLE-ONLY is non-nil, don't export 370 contents of hidden elements. 371 372 When optional argument BODY-ONLY is non-nil, only write code 373 between \"<body>\" and \"</body>\" tags. 374 375 EXT-PLIST, when provided, is a property list with external 376 parameters overriding Org default settings, but still inferior to 377 file-local settings. 378 379 Export is done in a buffer named \"*Org S5 Export*\", which 380 will be displayed when `org-export-show-temporary-export-buffer' 381 is non-nil." 382 (interactive) 383 (org-export-to-buffer 's5 "*Org S5 Export*" 384 async subtreep visible-only body-only ext-plist (lambda () (nxml-mode)))) 385 386 (defun org-s5-export-to-html 387 (&optional async subtreep visible-only body-only ext-plist) 388 "Export current buffer to a S5 HTML file. 389 390 If narrowing is active in the current buffer, only export its 391 narrowed part. 392 393 If a region is active, export that region. 394 395 A non-nil optional argument ASYNC means the process should happen 396 asynchronously. The resulting file should be accessible through 397 the `org-export-stack' interface. 398 399 When optional argument SUBTREEP is non-nil, export the sub-tree 400 at point, extracting information from the headline properties 401 first. 402 403 When optional argument VISIBLE-ONLY is non-nil, don't export 404 contents of hidden elements. 405 406 When optional argument BODY-ONLY is non-nil, only write code 407 between \"<body>\" and \"</body>\" tags. 408 409 EXT-PLIST, when provided, is a property list with external 410 parameters overriding Org default settings, but still inferior to 411 file-local settings. 412 413 Return output file's name." 414 (interactive) 415 (let* ((extension (concat "." org-html-extension)) 416 (file (org-export-output-file-name extension subtreep)) 417 (org-export-coding-system org-html-coding-system)) 418 (org-export-to-file 's5 file 419 async subtreep visible-only body-only ext-plist))) 420 421 (defun org-s5-publish-to-html (plist filename pub-dir) 422 "Publish an org file to S5 HTML Presentation. 423 424 FILENAME is the filename of the Org file to be published. PLIST 425 is the property list for the given project. PUB-DIR is the 426 publishing directory. 427 428 Return output file name." 429 (org-publish-org-to 's5 filename ".html" plist pub-dir)) 430 431 (provide 'ox-s5) 432 433 ;;; ox-s5.el ends here