diff options
| author | cashmere <cashmere@autistici.org> | 2026-08-14 19:48:51 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-08-14 19:48:51 +0200 |
| commit | 6e790cb1d15e7072817ff8646b4fc165b4408332 (patch) | |
| tree | 4268c1cd3647cc44ffbdb6d09cd2764248540cb4 | |
| parent | 94fdab14488a7d018cf8c4fa986846bf96a2fb67 (diff) | |
| download | emacs-6e790cb1d15e7072817ff8646b4fc165b4408332.tar.gz | |
md: 2026-08-14T19:48:51+02:00
| -rw-r--r-- | config.el | 8 | ||||
| -rw-r--r-- | config.org | 26 | ||||
| -rw-r--r-- | lisp/ox-reveal.el | 808 |
3 files changed, 822 insertions, 20 deletions
@@ -2298,7 +2298,7 @@ Timers that expired while Emacs was closed fire immediately." "\\`\\*elpaca-log\\*\\'" "\\`\\*Native-compile-Log\\*\\'" "\\`\\*Async-native-compile-log\\*\\'")) - ;; (helm-display-function #'helm-display-buffer-in-own-frame) + (helm-display-function #'helm-display-buffer-in-own-frame) :bind (:map helm-map ("C-j" . helm-next-line) ("C-k" . helm-previous-line))) @@ -2973,7 +2973,7 @@ so the working-tree diff stays visible until the user explicitly stages." (blink-cursor-mode 0) (setq-default cursor-type 'box) -(defvar cashmere/font-height 160) +(defvar cashmere/font-height 180) (defun cashmere/set-fonts (&optional frame) "Apply the MapleMono faces, but only on graphical frames. @@ -4366,4 +4366,8 @@ opening another file in same project does not re-notify." :ensure nil :commands (zfs)) +(load (expand-file-name "lisp/ox-reveal.el" user-emacs-directory) + nil + 'nomessage) + (provide 'init) @@ -2679,7 +2679,7 @@ Auto switch silos (different workspaces/folders) based on preconfigured director "\\`\\*elpaca-log\\*\\'" "\\`\\*Native-compile-Log\\*\\'" "\\`\\*Async-native-compile-log\\*\\'")) - ;; (helm-display-function #'helm-display-buffer-in-own-frame) + (helm-display-function #'helm-display-buffer-in-own-frame) :bind (:map helm-map ("C-j" . helm-next-line) ("C-k" . helm-previous-line))) @@ -3564,7 +3564,7 @@ Native =undo-redo= (via =evil-undo-system='undo-redo=); just raise the undo limi (blink-cursor-mode 0) (setq-default cursor-type 'box) -(defvar cashmere/font-height 160) +(defvar cashmere/font-height 180) (defun cashmere/set-fonts (&optional frame) "Apply the MapleMono faces, but only on graphical frames. @@ -3585,6 +3585,8 @@ created later still get them." (cashmere/set-fonts) #+end_src +#+RESULTS: + * essentials * themes @@ -5184,24 +5186,12 @@ opening another file in same project does not re-notify." :commands (zfs)) #+end_src -* COMMENT org-tree-slide +* ox-reveal #+begin_src emacs-lisp -(use-package org-tree-slide - :ensure t) -(when (require 'org-tree-slide nil t) - (global-set-key (kbd "<f8>") 'org-tree-slide-mode) - (global-set-key (kbd "S-<f8>") 'org-tree-slide-skip-done-toggle) - (define-key org-tree-slide-mode-map (kbd "<f9>") - 'org-tree-slide-move-previous-tree) - (define-key org-tree-slide-mode-map (kbd "<f10>") - 'org-tree-slide-move-next-tree) - (define-key org-tree-slide-mode-map (kbd "<f11>") - 'org-tree-slide-content) - (setq org-tree-slide-skip-outline-level 4) - (org-tree-slide-narrowing-control-profile) - (setq org-tree-slide-skip-comments 'inherit) - (setq org-tree-slide-skip-done nil)) +(load (expand-file-name "lisp/ox-reveal.el" user-emacs-directory) + nil + 'nomessage) #+end_src * provide init diff --git a/lisp/ox-reveal.el b/lisp/ox-reveal.el new file mode 100644 index 0000000..5c8539e --- /dev/null +++ b/lisp/ox-reveal.el @@ -0,0 +1,808 @@ +;;; ox-reveal.el --- reveal.js Presentation Back-End for Org Export Engine + +;; Copyright (C) 2013 Yujie Wen + +;; Author: Yujie Wen <yjwen.ty at gmail dot com> +;; Created: 2013-04-27 +;; Version: 1.0 +;; Package-Requires: ((org "8.0")) +;; Keywords: outlines, hypermedia, slideshow, presentation + +;; This file is not part of GNU Emacs. + +;;; Copyright Notice: + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +;; Please see "Readme.org" for detail introductions. + +;; Pull request: Multiplex Support - Stephen Barrett <Stephen dot Barrewtt at scss dot tcd dot ie + +;;; Code: + +(require 'ox-html) +(eval-when-compile (require 'cl)) + +(defun frag-style (frag) + "Return \"fragment\" if frag is t, which indicates to use +default fragment style, otherwise return \"fragment style\"." + (cond + ((string= frag t) "fragment") + (t (format "fragment %s" frag)))) + + +(org-export-define-derived-backend 'reveal 'html + + :menu-entry + '(?R "Export to reveal.js HTML Presentation" + ((?R "To file" org-reveal-export-to-html) + (?B "To file and Browse" org-reveal-export-to-html-and-browse))) + + :options-alist + '((:reveal-control nil "reveal_control" org-reveal-control t) + (:reveal-progress nil "reveal_progress" org-reveal-progress t) + (:reveal-history nil "reveal_history" org-reveal-history t) + (:reveal-center nil "reveal_center" org-reveal-center t) + (:reveal-rolling-links nil "reveal_rolling_links" org-reveal-rolling-links t) + (:reveal-slide-number nil "reveal_slide_number" org-reveal-slide-number t) + (:reveal-keyboard nil "reveal_keyboard" org-reveal-keyboard t) + (:reveal-overview nil "reveal_overview" org-reveal-overview t) + (:reveal-width nil "reveal_width" org-reveal-width t) + (:reveal-height nil "reveal_height" org-reveal-height) + (:reveal-margin "REVEAL_MARGIN" nil org-reveal-margin t) + (:reveal-min-scale "REVEAL_MIN_SCALE" nil org-reveal-min-scale t) + (:reveal-max-scale "REVEAL_MAX_SCALE" nil org-reveal-max-scale t) + (:reveal-root "REVEAL_ROOT" nil org-reveal-root t) + (:reveal-trans "REVEAL_TRANS" nil org-reveal-transition t) + (:reveal-speed "REVEAL_SPEED" nil org-reveal-transition-speed t) + (:reveal-theme "REVEAL_THEME" nil org-reveal-theme t) + (:reveal-extra-css "REVEAL_EXTRA_CSS" nil nil nil) + (:reveal-extra-js "REVEAL_EXTRA_JS" nil org-reveal-extra-js nil) + (:reveal-hlevel "REVEAL_HLEVEL" nil nil t) + (:reveal-title-slide nil "reveal_title_slide" org-reveal-title-slide t) + (:reveal-title-slide-template "REVEAL_TITLE_SLIDE_TEMPLATE" nil org-reveal-title-slide-template t) + (:reveal-title-slide-background "REVEAL_TITLE_SLIDE_BACKGROUND" nil nil t) + (:reveal-title-slide-background-size "REVEAL_TITLE_SLIDE_BACKGROUND_SIZE" nil nil t) + (:reveal-title-slide-background-repeat "REVEAL_TITLE_SLIDE_BACKGROUND_REPEAT" nil nil t) + (:reveal-title-slide-background-repeat "REVEAL_TITLE_SLIDE_BACKGROUND_TRANSITION" nil nil t) + (:reveal-mathjax nil "reveal_mathjax" org-reveal-mathjax t) + (:reveal-mathjax-url "REVEAL_MATHJAX_URL" nil org-reveal-mathjax-url t) + (:reveal-preamble "REVEAL_PREAMBLE" nil org-reveal-preamble t) + (:reveal-head-preamble "REVEAL_HEAD_PREAMBLE" nil org-reveal-head-preamble t) + (:reveal-postamble "REVEAL_POSTAMBLE" nil org-reveal-postamble t) + (:reveal-multiplex-id "REVEAL_MULTIPLEX_ID" nil org-reveal-multiplex-id nil) + (:reveal-multiplex-secret "REVEAL_MULTIPLEX_SECRET" nil org-reveal-multiplex-secret nil) + (:reveal-multiplex-url "REVEAL_MULTIPLEX_URL" nil org-reveal-multiplex-url nil) + (:reveal-multiplex-socketio-url "REVEAL_MULTIPLEX_SOCKETIO_URL" nil org-reveal-multiplex-socketio-url nil) + (:reveal-plugins "REVEAL_PLUGINS" nil nil t) + ) + + :translate-alist + '((export-block . org-reveal-export-block) + (headline . org-reveal-headline) + (inner-template . org-reveal-inner-template) + (keyword . org-reveal-keyword) + (plain-list . org-reveal-plain-list) + (quote-block . org-reveal-quote-block) + (section . org-reveal-section) + (src-block . org-reveal-src-block) + (template . org-reveal-template)) + + :filters-alist '((:filter-parse-tree . org-reveal-filter-parse-tree)) + ) + +(defcustom org-reveal-root "./reveal.js" + "The root directory of reveal.js packages. It is the directory + within which js/reveal.js is." + :group 'org-export-reveal) + +(defcustom org-reveal-hlevel 1 + "The minimum level of headings that should be grouped into +vertical slides." + :group 'org-export-reveal + :type 'integer) + +(defun org-reveal--get-hlevel (info) + "Get HLevel value safely. +If option \"REVEAL_HLEVEL\" is set, retrieve integer value from it, +else get value from custom variable `org-reveal-hlevel'." + (let ((hlevel-str (plist-get info :reveal-hlevel))) + (if hlevel-str (string-to-number hlevel-str) + org-reveal-hlevel))) + +(defcustom org-reveal-title-slide t + "Include a title slide." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-title-slide-template + "<h1>%t</h1> +<h2>%a</h2> +<h2>%e</h2> +<h2>%d</h2>" + "Format template to specify title page slide. +See `org-html-postamble-format' for the valid elements which +can be include." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-transition + "default" + "Reveal transistion style." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-transition-speed + "default" + "Reveal transistion speed." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-theme + "moon" + "Reveal theme." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-extra-js + "" + "URL to extra JS file." + :group 'org-export-reveal + :type 'string) + + +(defcustom org-reveal-multiplex-id "" + "The ID to use for multiplexing." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-multiplex-secret "" + "The secret to use for master slide." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-multiplex-url "" + "The url of the socketio server." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-multiplex-socketio-url + "" + "the url of the socketio.js library" + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-control t + "Reveal control applet." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-progress t + "Reveal progress applet." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-history nil + "Reveal history applet." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-center t + "Reveal center applet." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-rolling-links nil + "Reveal use rolling links." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-slide-number t + "Reveal showing slide numbers." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-keyboard t + "Reveal use keyboard navigation." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-overview t + "Reveal show overview." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-width -1 + "Slide width" + :group 'org-export-reveal + :type 'integer) + +(defcustom org-reveal-height -1 + "Slide height" + :group 'org-export-reveal + :type 'integer) + +(defcustom org-reveal-margin "-1" + "Slide margin" + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-min-scale "-1" + "Minimum bound for scaling slide." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-max-scale "-1" + "Maximum bound for scaling slide." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-mathjax nil + "Enable MathJax script." + :group 'org-export-reveal + :type 'boolean) + +(defcustom org-reveal-mathjax-url + "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" + "Default MathJax URL." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-preamble nil + "Preamble contents." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-head-preamble nil + "Preamble contents for head part." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-postamble nil + "Postamble contents." + :group 'org-export-reveal + :type 'string) + +(defcustom org-reveal-plugins + '(markdown highlight zoom notes) + "Reveal.js plugins." + :group 'org-export-reveal + :type '(set + (const markdown) + (const highlight) + (const zoom) + (const notes) + (const search) + (const math))) + +(defconst org-reveal-plugin-definitions + '((markdown "dist/plugin/markdown.js" "RevealMarkdown") + (highlight "dist/plugin/highlight.js" "RevealHighlight") + (zoom "dist/plugin/zoom.js" "RevealZoom") + (notes "dist/plugin/notes.js" "RevealNotes") + (search "dist/plugin/search.js" "RevealSearch") + (math "dist/plugin/math.js" "RevealMath"))) + +(defun if-format (fmt val) + (if val (format fmt val) "")) + +(defun org-reveal-attrs-list (attrs) + "Generate an HTML attribute list string from the list +ATTRS. ATTRS is expected to be a list of key-value pairs, ((key0 +. value0) (key1 . value1) ...), and for each pair of none-nil +value, an HTML attribute statement key=\"value\" is inserted into +the result string." + (mapconcat + (lambda (elem) + (let ((key (car elem)) + (value (car (cdr elem)))) + (if value (format " %s=\"%s\"" key value)))) + attrs "")) +(defun org-reveal-tag (tagname attrs content &optional sep) + "Generate an HTML tag of form <TAGNAME ATTRS>CONTENT</TAGNAME>. If +SEP is given, then the CONTENT is enclosed by SEP, otherwise it is enclosed by +a '\\n'" + (let ((sep_ (or sep "\n"))) + (format "<%s %s>%s%s%s</%s>" + tagname ; The leading tagname. + (if attrs (org-html--make-attribute-string attrs) "") + sep_ content sep_ tagname))) + +(defun frag-class (frag) + ;; Return proper HTML string description of fragment style. + (and frag + (format " class=\"%s\"" (frag-style frag)))) + +(defun org-reveal-export-block (export-block contents info) + "Transocde a EXPORT-BLOCK element from Org to Reveal. +CONTENTS is nil. INFO is a plist holding contextual information." + (let ((block-type (org-element-property :type export-block)) + (block-string (org-element-property :value export-block))) + (cond ((string= block-type "NOTES") + (org-reveal-tag 'aside '(('class . 'notes)) + (org-export-string-as block-string 'html 'body-only))) + ((string= block-type "HTML") + (org-remove-indentation block-string))))) + +(defun org-reveal-headline (headline contents info) + "Transcode a HEADLINE element from Org to Reveal. +CONTENTS holds the contents of the headline. INFO is a plist +holding contextual information." + ;; First call org-html-headline to get the formatted HTML contents. + ;; Then add enclosing <section> tags to mark slides. + (setq contents (or contents "")) + (let* ((numberedp (org-export-numbered-headline-p headline info)) + (level (org-export-get-relative-level headline info)) + (text (org-export-data (org-element-property :title headline) info)) + (todo (and (plist-get info :with-todo-keywords) + (let ((todo (org-element-property :todo-keyword headline))) + (and todo (org-export-data todo info))))) + (todo-type (and todo (org-element-property :todo-type headline))) + (tags (and (plist-get info :with-tags) + (org-export-get-tags headline info))) + (priority (and (plist-get info :with-priority) + (org-element-property :priority headline))) + ;; Create the headline text. + (full-text (funcall (or (plist-get info :html-format-headline-function) + ;; nil function, return a suggestive error + (error "Export failed. It seems you are using a stable release of Org-mode. Please use Org-reveal `stable' branch for Org-mode stable releases.")) + todo todo-type priority text tags info))) + (cond + ;; Case 1: This is a footnote section: ignore it. + ((org-element-property :footnote-section-p headline) nil) + ;; Case 2. This is a deep sub-tree: export it as a list item. + ;; Also export as items headlines for which no section + ;; format has been found. + ((org-export-low-level-p headline info) + ;; Build the real contents of the sub-tree. + (let* ((type (if numberedp 'ordered 'unordered)) + (itemized-body (org-reveal-format-list-item + contents type nil info nil 'none full-text))) + (concat + (and (org-export-first-sibling-p headline info) + (org-html-begin-plain-list type)) + itemized-body + (and (org-export-last-sibling-p headline info) + (org-html-end-plain-list type))))) + ;; Case 3. Standard headline. Export it as a section. + (t + (let* ((level1 (format "h%d" (+ level (1- org-html-toplevel-hlevel)))) + (hlevel (org-reveal--get-hlevel info)) + (first-content (car (org-element-contents headline)))) + (concat + (if (or (/= level 1) + (not (org-export-first-sibling-p headline info))) + ;; Stop previous slide. + "</section>\n") + (if (eq level hlevel) + ;; Add an extra "<section>" to group following slides + ;; into vertical ones. + "<section>\n") + ;; Start a new slide. + (format "<section%s%s>\n" + (org-reveal-attrs-list + `(("id" ,(or (org-element-property :CUSTOM_ID headline) + (org-export-get-reference headline info))) + ("data-state" ,(org-element-property :REVEAL_DATA_STATE headline)) + ("data-transition" ,(org-element-property :REVEAL_DATA_TRANSITION headline)) + ("data-background" ,(org-element-property :REVEAL_BACKGROUND headline)) + ("data-background-size" ,(org-element-property :REVEAL_BACKGROUND_SIZE headline)) + ("data-background-repeat" ,(org-element-property :REVEAL_BACKGROUND_REPEAT headline)) + ("data-background-transition" ,(org-element-property :REVEAL_BACKGROUND_TRANS headline)))) + (let ((extra-attrs (org-element-property :REVEAL_EXTRA_ATTR headline))) + (if extra-attrs (format " %s" extra-attrs) ""))) + "\n" + ;; The HTML content of this headline. + (org-reveal-tag level1 ;;"\n<h%d%s>%s</h%d>\n" + (let ((fragment (org-element-property :REVEAL-FRAG headline))) + (if fragment `(("class" ,(concat "fragment " fragment))))) + full-text + "") + "\n" + ;; When there is no section, pretend there is an empty + ;; one to get the correct <div class="outline- ...> + ;; which is needed by `org-info.js'. + (if (not (eq (org-element-type first-content) 'section)) + (concat (org-reveal-section first-content "" info) + contents) + contents) + (if (= level hlevel) + ;; Add an extra "</section>" to stop vertical slide + ;; grouping. + "</section>\n") + (if (and (= level 1) + (org-export-last-sibling-p headline info)) + ;; Last head 1. Stop all slides. + "</section>"))))))) + +(defgroup org-export-reveal nil + "Options for exporting Orgmode files to reveal.js HTML pressentations." + :tag "Org Export Reveal" + :group 'org-export) + +(defun org-reveal-stylesheets (info) + (let ((root-path (file-name-as-directory (plist-get info :reveal-root)))) + (concat + (format " +<link rel=\"stylesheet\" href=\"%sdist/reset.css\"/> +<link rel=\"stylesheet\" href=\"%sdist/reveal.css\"/> +<link rel=\"stylesheet\" href=\"%sdist/theme/%s.css\" id=\"theme\"/> +" + root-path root-path root-path + (plist-get info :reveal-theme)) + (let ((extra-css (plist-get info :reveal-extra-css))) + (if extra-css (format "<link rel=\"stylesheet\" href=\"%s\"/>" extra-css) ""))))) + +(defun org-reveal-mathjax-scripts (info) + "Return the HTML contents for declaring MathJax scripts" + (if (plist-get info :reveal-mathjax) + ;; MathJax enabled. + (format "<script type=\"text/javascript\" src=\"%s\"></script>\n" + (plist-get info :reveal-mathjax-url)))) + +(defun org-reveal-plugin-spec (plugin) + (cdr (assq plugin org-reveal-plugin-definitions))) + +(defun org-reveal-plugin-list (info) + (let ((configured (plist-get info :reveal-plugins))) + (cond + ((null configured) org-reveal-plugins) + ((string= configured "") nil) + (t (car (read-from-string configured)))))) + +(defun org-reveal-scripts (info) + (let* ((root-path (file-name-as-directory (plist-get info :reveal-root))) + (plugin-specs + (delq nil + (mapcar #'org-reveal-plugin-spec + (org-reveal-plugin-list info)))) + (extra-js (plist-get info :reveal-extra-js))) + (concat + (format "<script src=\"%sdist/reveal.js\"></script>\n" root-path) + (mapconcat + (lambda (spec) + (format "<script src=\"%s%s\"></script>" + root-path (nth 0 spec))) + plugin-specs + "\n") + (if (string= extra-js "") + "" + (format "\n<script src=\"%s\"></script>" extra-js)) + "\n<script>\nReveal.initialize({\n" + (format "controls: %s,\n" + (if (plist-get info :reveal-control) "true" "false")) + (format "progress: %s,\n" + (if (plist-get info :reveal-progress) "true" "false")) + (format "history: %s,\n" + (if (plist-get info :reveal-history) "true" "false")) + (format "center: %s,\n" + (if (plist-get info :reveal-center) "true" "false")) + (format "slideNumber: %s,\n" + (if (plist-get info :reveal-slide-number) "true" "false")) + (format "keyboard: %s,\n" + (if (plist-get info :reveal-keyboard) "true" "false")) + (format "overview: %s,\n" + (if (plist-get info :reveal-overview) "true" "false")) + (let ((width (plist-get info :reveal-width))) + (if (> width 0) (format "width: %d,\n" width) "")) + (let ((height (plist-get info :reveal-height))) + (if (> height 0) (format "height: %d,\n" height) "")) + (let ((margin (string-to-number (plist-get info :reveal-margin)))) + (if (>= margin 0) (format "margin: %.2f,\n" margin) "")) + (let ((min-scale (string-to-number (plist-get info :reveal-min-scale)))) + (if (> min-scale 0) (format "minScale: %.2f,\n" min-scale) "")) + (let ((max-scale (string-to-number (plist-get info :reveal-max-scale)))) + (if (> max-scale 0) (format "maxScale: %.2f,\n" max-scale) "")) + (format "transition: '%s',\ntransitionSpeed: '%s',\n" + (plist-get info :reveal-trans) + (plist-get info :reveal-speed)) + (if plugin-specs + (format "plugins: [%s],\n" + (mapconcat (lambda (spec) (nth 1 spec)) + plugin-specs + ", ")) + "") + "});\n</script>\n"))) + +(defun org-reveal-toc-headlines-r (headlines info prev_level hlevel prev_x prev_y) + "Generate toc headline text recursively." + (let* ((headline (car headlines)) + (text (org-export-data (org-element-property :title headline) info)) + (level (org-export-get-relative-level headline info)) + (x (if (<= level hlevel) (+ prev_x 1) prev_x)) + (y (if (<= level hlevel) 0 (+ prev_y 1))) + (remains (cdr headlines)) + (remain-text + (if remains + ;; Generate text for remain headlines + (org-reveal-toc-headlines-r remains info level hlevel x y) + ""))) + (concat + (cond + ((> level prev_level) + ;; Need to start a new level of unordered list + "<ul>\n") + ((< level prev_level) + ;; Need to end previous list item and the whole list. + "</li>\n</ul>\n") + (t + ;; level == prev_level, Need to end previous list item. + "</li>\n")) + (format "<li>\n<a href=\"#%s\">%s</a>\n%s" + (or (org-element-property :CUSTOM_ID headline) + (org-export-get-reference headline info)) + text remain-text)))) +(defun org-reveal-toc-headlines (headlines info) + "Generate the Reveal.js contents for headlines in table of contents. +Add proper internal link to each headline." + (let ((level (org-export-get-relative-level (car headlines) info)) + (hlevel (org-reveal--get-hlevel info))) + (concat + (format "<h2>%s</h2>" + (org-export-translate "Table of Contents" :html info)) + (org-reveal-toc-headlines-r headlines info 0 hlevel 1 1) + (if headlines "</li>\n</ul>\n" "")))) + + +(defun org-reveal-toc (depth info) + "Build a slide of table of contents." + (let ((headlines (org-export-collect-headlines info depth))) + (and headlines + (format "<section id=\"table-of-contents\">\n%s</section>\n" + (org-reveal-toc-headlines headlines info))))) + +(defun org-reveal-inner-template (contents info) + "Return body of document string after HTML conversion. +CONTENTS is the transcoded contents string. INFO is a plist +holding export options." + (concat + ;; Table of contents. + (let ((depth (plist-get info :with-toc))) + (when depth (org-reveal-toc depth info))) + ;; Document contents. + contents)) + +(defun org-reveal-parse-token (key &optional value) + "Return HTML tags or perform SIDE EFFECT according to key" + (case (intern key) + (split "</section>\n<section>"))) + +(defun org-reveal-parse-keyword-value (value) + "According to the value content, return HTML tags to split slides." + (let ((tokens (mapcar + (lambda (x) (split-string x ":")) + (split-string value)))) + (mapconcat + (lambda (x) (apply 'org-reveal-parse-token x)) + tokens + ""))) + + +(defun org-reveal-keyword (keyword contents info) + "Transcode a KEYWORD element from Org to HTML, +and may change custom variables as SIDE EFFECT. +CONTENTS is nil. INFO is a plist holding contextual information." + (let ((key (org-element-property :key keyword)) + (value (org-element-property :value keyword))) + (case (intern key) + (REVEAL (org-reveal-parse-keyword-value value)) + (REVEAL_HTML value)))) + +(defun org-reveal-plain-list (plain-list contents info) + "Transcode a PLAIN-LIST element from Org to Reveal. + +CONTENTS is the contents of the list. INFO is a plist holding +contextual information. + +Extract and set `attr_html' to plain-list tag attributes." + (org-reveal-tag (case (org-element-property :type plain-list) + (ordered "ol") + (unordered "ul") + (descriptive "dl")) + (org-export-read-attribute :attr_html plain-list) + contents)) + +(defun org-reveal--build-pre/postamble (type info) + "Return document preamble or postamble as a string, or nil." + (let ((section (plist-get info (intern (format ":reveal-%s" type)))) + (spec (org-html-format-spec info))) + (when section + (let ((section-contents + (if (functionp (intern section)) (funcall (intern section) info) + ;; else section is a string. + (format-spec section spec)))) + (when (org-string-nw-p section-contents) + (org-element-normalize-string section-contents)))))) + + +(defun org-reveal-section (section contents info) + "Transcode a SECTION element from Org to Reveal. +CONTENTS holds the contents of the section. INFO is a plist +holding contextual information." + ;; Just return the contents. No "<div>" tags. + contents) + +(defun org-reveal-src-block (src-block contents info) + "Transcode a SRC-BLOCK element from Org to Reveal. +CONTENTS holds the contents of the item. INFO is a plist holding +contextual information." + (if (org-export-read-attribute :attr_html src-block :textarea) + (org-html--textarea-block src-block) + (let ((lang (org-element-property :language src-block)) + (caption (org-export-get-caption src-block)) + (code (org-html-format-code src-block info)) + (frag (org-export-read-attribute :attr_reveal src-block :frag)) + (label (let ((lbl (org-element-property :name src-block))) + (if (not lbl) "" + (format " id=\"%s\"" + (org-export-solidify-link-text lbl)))))) + (if (not lang) + (format "<pre %s%s>\n%s</pre>" + (or (frag-class frag) " class=\"example\"") + label + code) + (format + "<div class=\"org-src-container\">\n%s%s\n</div>" + (if (not caption) "" + (format "<label class=\"org-src-name\">%s</label>" + (org-export-data caption info))) + (format "\n<pre %s%s>%s</pre>" + (or (frag-class frag) + (format " class=\"src src-%s\"" lang)) + label code)))))) + +(defun org-reveal-quote-block (quote-block contents info) + "Transcode a QUOTE-BLOCK element from Org to Reveal. +CONTENTS holds the contents of the block INFO is a plist holding +contextual information." + (format "<blockquote %s>\n%s</blockquote>" + (frag-class (org-export-read-attribute :attr_reveal quote-block :frag)) + contents)) + + +(defun org-reveal-template (contents info) + "Return complete document string after HTML conversion. +contents is the transcoded contents string. +info is a plist holding export options." + (concat + (format "<?xml version=\"1.0\" encoding=\"utf-8\"?> +<!DOCTYPE html>\n<html%s>\n<head>\n" + (if-format " lang=\"%s\"" (plist-get info :language))) + "<meta charset=\"utf-8\"/>\n" + "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\"/>\n" + (if-format "<title>%s</title>\n" (org-export-data (plist-get info :title) info)) + (if-format "<meta name=\"author\" content=\"%s\"/>\n" (plist-get info :author)) + (if-format "<meta name=\"description\" content=\"%s\"/>\n" (plist-get info :description)) + (if-format "<meta name=\"keywords\" content=\"%s\"/>\n" (plist-get info :keywords)) + (org-reveal-stylesheets info) + (org-reveal-mathjax-scripts info) + (org-reveal--build-pre/postamble 'head-preamble info) + "</head> +<body>\n" + (org-reveal--build-pre/postamble 'preamble info) +"<div class=\"reveal\"> +<div class=\"slides\">\n" + (if (plist-get info :reveal-title-slide) + (concat + (format "<section id=\"sec-title-slide\"%s%s%s%s>\n" + (if-format " data-background=\"%s\"" + (plist-get info :reveal-title-slide-background)) + (if-format " data-background-size=\"%s\"" + (plist-get info :reveal-title-slide-background-size)) + (if-format " data-background-repeat=\"%s\"" + (plist-get info :reveal-title-slide-background-repeat)) + (if-format " data-background-transition=\"%s\"" + (plist-get info :reveal-title-slide-background-transition))) + (format-spec (plist-get info :reveal-title-slide-template) (org-html-format-spec info)) + "\n</section>\n") + "") + contents + "</div> +</div>\n" + (org-reveal-scripts info) + (org-reveal--build-pre/postamble 'postamble info) + "</body> +</html>\n")) + +(defun org-reveal-filter-parse-tree (tree backend info) + "Do filtering before parsing TREE. + +Tree is the parse tree being exported. BACKEND is the export +back-end used. INFO is a plist-used as a communication channel. + +Assuming BACKEND is `reveal'. + +Each `attr_reveal' attribute is mapped to corresponding +`attr_html' attributes." + (org-element-map tree (remq 'item org-element-all-elements) + 'org-reveal-append-frag) + ;; Return the updated tree. + tree) + +(defun org-reveal-append-frag (elem) + "Read org-reveal's fragment attribute from ELEM and append +transformed fragment attribute to ELEM's attr_html plist." + (let ((frag-attr (org-export-read-attribute :attr_reveal elem :frag))) + (if frag-attr + (let ((attr-html (org-element-property :attr_html elem)) + (elem-type (org-element-type elem))) + (cond + ((and (string= elem-type 'plain-list) + (char-equal (string-to-char frag-attr) ?\()) + (mapcar* + (lambda (item frag) + "Overwrite item's `:checkbox' property with + reveal's fragment attribute." + (and (not (string= frag "none")) + (org-element-put-property + item :checkbox + (intern (cond ((string= frag t) "fragment") + (t (format "fragment %s" frag))))))) + (org-element-contents elem) + (car (read-from-string frag-attr)))) + (t + ;; Convert reveal's fragment attribute to HTML attribute. + (let ((attr-html (org-element-property :attr_html elem))) + (push + (cond ((string= frag-attr t) ":class fragment") + (t (concat ":class fragment " frag-attr))) + attr-html) + (org-element-put-property elem :attr_html attr-html)))))) + elem)) + +(defvar client-multiplex nil + "used to cause generation of client html file for multiplex") + +(defun org-reveal-export-to-html + (&optional async subtreep visible-only body-only ext-plist) + "Export current buffer to a reveal.js HTML file." + (interactive) + (let* ((extension (concat "." org-html-extension)) + (file (org-export-output-file-name extension subtreep)) + (clientfile (org-export-output-file-name (concat "_client" extension) subtreep))) + + ; export filename_client HTML file if multiplexing + (setq client-multiplex nil) + (setq retfile (org-export-to-file 'reveal file + async subtreep visible-only body-only ext-plist)) + + ; export the client HTML file if client-multiplex is set true + ; by previous call to org-export-to-file + (if (eq client-multiplex t) + (org-export-to-file 'reveal clientfile + async subtreep visible-only body-only ext-plist)) + (cond (t retfile)))) + +(defun org-reveal-export-to-html-and-browse + (&optional async subtreep visible-only body-only ext-plist) + "Export current buffer to a reveal.js and browse HTML file." + (interactive) + (browse-url-of-file (expand-file-name (org-reveal-export-to-html async subtreep visible-only body-only ext-plist)))) + +;;;###autoload +(defun org-reveal-publish-to-reveal + (plist filename pub-dir) + "Publish an org file to Html. + +FILENAME is the filename of the Org file to be published. PLIST +is the property list for the given project. PUB-DIR is the +publishing directory. + +Return output file name." + (org-publish-org-to 'reval filename ".html" plist pub-dir)) + + +(provide 'ox-reveal) + +;;; ox-reveal.el ends here |
