;;; ox-reveal.el --- reveal.js Presentation Back-End for Org Export Engine ;; Copyright (C) 2013 Yujie Wen ;; Author: Yujie Wen ;; 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 . ;; Please see "Readme.org" for detail introductions. ;; Pull request: Multiplex Support - Stephen Barrett %t

%a

%e

%d

" "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 "default" "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 "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" "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 '(classList markdown highlight zoom notes) "Default builtin plugins" :group 'org-export-reveal :type '(set (const classList) (const markdown) (const highlight) (const zoom) (const notes) (const search) (const remotes) (const multiplex))) (defun if-format (fmt val) (if val (format fmt val) "")) (defun frag-class (frag) ;; Return proper HTML string description of fragment style. (cond ((string= frag t) " class=\"fragment\"") (frag (format " class=\"fragment %s\"" frag)))) (defun org-reveal-export-block (export-block contents info) "Transocde a EXPORT-BLOCK element from Org to Reveal. CONTENTS is nil. NFO 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") (concat "")) ((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
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 (org-html-format-headline--wrap headline 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 (+ 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. "
\n") (if (eq level hlevel) ;; Add an extra "
" to group following slides ;; into vertical ones. "
\n") ;; Start a new slide. (format "
\n" (or (org-element-property :CUSTOM_ID headline) (concat "sec-" (mapconcat 'number-to-string (org-export-get-headline-number headline info) "-"))) (if-format " data-state=\"%s\"" (org-element-property :REVEAL_DATA_STATE headline)) (if-format " data-transition=\"%s\"" (org-element-property :REVEAL_DATA_TRANSITION headline)) (if-format " data-background=\"%s\"" (org-element-property :REVEAL_BACKGROUND headline)) (if-format " data-background-size=\"%s\"" (org-element-property :REVEAL_BACKGROUND_SIZE headline)) (if-format " data-background-repeat=\"%s\"" (org-element-property :REVEAL_BACKGROUND_REPEAT headline)) (if-format " data-background-transition=\"%s\"" (org-element-property :REVEAL_BACKGROUND_TRANS headline)) (if-format " %s" (org-element-property :REVEAL_EXTRA_ATTR headline))) ;; The HTML content of this headline. (format "\n%s\n" level1 (if-format " class=\"fragment %s\"" (org-element-property :REVEAL-FRAG headline)) full-text level1) ;; When there is no section, pretend there is an empty ;; one to get the correct
" to stop vertical slide ;; grouping. "
\n") (if (and (= level 1) (org-export-last-sibling-p headline info)) ;; Last head 1. Stop all slides. "
"))))))) (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) "Return the HTML contents for declaring reveal stylesheets using custom variable `org-reveal-root'." (let ((root-path (file-name-as-directory (plist-get info :reveal-root)))) (concat ;; stylesheets (format " " root-path root-path (plist-get info :reveal-theme)) ;; extra css (let ((extra-css (plist-get info :reveal-extra-css))) (if extra-css (format "" extra-css) "")) ;; print-pdf (format " " root-path)))) (defun org-reveal-mathjax-scripts (info) "Return the HTML contents for declaring MathJax scripts" (if (plist-get info :reveal-mathjax) ;; MathJax enabled. (format "\n" (plist-get info :reveal-mathjax-url)))) (defun org-reveal-scripts (info) "Return the necessary scripts for initializing reveal.js using custom variable `org-reveal-root'." (let* ((root-path (file-name-as-directory (plist-get info :reveal-root)))) (concat ;; reveal.js/lib/js/head.min.js ;; reveal.js/js/reveal.js (format " " root-path root-path) ;; plugin headings " \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 "
    \n") ((< level prev_level) ;; Need to end previous list item and the whole list. "\n
\n") (t ;; level == prev_level, Need to end previous list item. "\n")) (format "
  • \n%s\n%s" (or (org-element-property :CUSTOM_ID headline) (concat "sec-" (mapconcat 'number-to-string (org-export-get-headline-number 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 "

    %s

    " (org-export-translate "Table of Contents" :html info)) (org-reveal-toc-headlines-r headlines info 0 hlevel 1 1) (if headlines "
  • \n\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 "
    \n%s
    \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-format-list-item (contents type checkbox info &optional term-counter-id frag headline) "Format a list item into Reveal.js HTML." (let* (;; The argument definition of `org-html-checkbox' differs ;; between Org-mode master and 8.2.5h. To deal both cases, ;; both argument definitions are tried here. (org-checkbox (condition-case nil (org-html-checkbox checkbox info) ;; In case of wrong number of arguments, try another one ((debug wrong-number-of-arguments) (org-html-checkbox checkbox)))) (checkbox (concat org-checkbox (and checkbox " ")))) (concat (case type (ordered (concat "" (if headline (concat headline "
    ")))) (unordered (concat "" (if headline (concat headline "
    ")))) (descriptive (concat "" (concat checkbox (or term-counter-id "(no term)")) "
    "))) (unless (eq type 'descriptive) checkbox) contents (case type (ordered "") (unordered "") (descriptive "
    "))))) (defun org-reveal-item (item contents info) "Transcode an ITEM element from Org to Reveal. CONTENTS holds the contents of the item. INFO is aplist holding contextual information." (let* ((plain-list (org-export-get-parent item)) (type (org-element-property :type plain-list)) (counter (org-element-property :counter item)) (checkbox (org-element-property :checkbox item)) (tag (let ((tag (org-element-property :tag item))) (and tag (org-export-data tag info)))) (frag (org-export-read-attribute :attr_reveal plain-list :frag))) (org-reveal-format-list-item contents type checkbox info (or tag counter) frag))) (defun org-reveal-parse-token (key &optional value) "Return HTML tags or perform SIDE EFFECT according to key" (case (intern key) (split "
    \n
    "))) (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-paragraph (paragraph contents info) "Transcode a PARAGRAPH element from Org to Reveal HTML. CONTENTS is the contents of the paragraph, as a string. INFO is the plist used as a communication channel." (let ((parent (org-export-get-parent paragraph))) (cond ((and (eq (org-element-type parent) 'item) (= (org-element-property :begin paragraph) (org-element-property :contents-begin parent))) ;; leading paragraph in a list item have no tags contents) ((org-html-standalone-image-p paragraph info) ;; standalone image (let ((frag (org-export-read-attribute :attr_reveal paragraph :frag))) (if frag (progn ;; This is ugly; need to update if the output from ;; org-html-format-inline-image changes. (unless (string-match "class=\"figure\"" contents) (error "Unexpected HTML output for image!")) (replace-match (concat "class=\"figure fragment " frag " \"") t t contents)) contents))) (t (format "\n%s

    " (or (frag-class (org-export-read-attribute :attr_reveal paragraph :frag)) "") 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 "
    " 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 "
    \n%s
    " (or (frag-class frag) " class=\"example\"") label code) (format "
    \n%s%s\n
    " (if (not caption) "" (format "" (org-export-data caption info))) (format "\n
    %s
    " (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 "
    \n%s
    " (frag-class (org-export-read-attribute :attr_reveal quote-block :frag)) contents)) (defun org-reveal-table (table contents info) "Transcode a TABLE element from Org to Reveal. CONTENTS holds the contents of the table INFO is a plist holding contextual information." (format "\n%s
    " (frag-class (org-export-read-attribute :attr_reveal table :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 " \n\n\n" (if-format " lang=\"%s\"" (plist-get info :language))) "\n" (if-format "%s\n" (org-export-data (plist-get info :title) info)) (if-format "\n" (plist-get info :author)) (if-format "\n" (plist-get info :description)) (if-format "\n" (plist-get info :keywords)) (org-reveal-stylesheets info) (org-reveal-mathjax-scripts info) (org-reveal--build-pre/postamble 'head-preamble info) " \n" (org-reveal--build-pre/postamble 'preamble info) "
    \n" (if (plist-get info :reveal-title-slide) (concat "
    \n" (format-spec (plist-get info :reveal-title-slide-template) (org-html-format-spec info)) "\n
    \n") "") contents "
    \n" (org-reveal--build-pre/postamble 'postamble info) (org-reveal-scripts info) " \n")) (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