Merge branch 'maint'
[org-mode.git] / contrib / lisp / ox-s5.el
blob0496aae19294df48b60eef11b2a8eb6ac3e50197
1 ;;; ox-s5.el --- S5 Presentation Back-End for Org Export Engine
3 ;; Copyright (C) 2011-2014 Rick Frankel
5 ;; Author: Rick Frankel <emacs at rickster dot com>
6 ;; Keywords: outlines, hypermedia, S5, wp
8 ;; This file is not part of GNU Emacs.
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.
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.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
23 ;;; Commentary:
25 ;; This library implements an S5 Presentation back-end for the Org
26 ;; generic exporter.
28 ;; Installation
29 ;; ------------
30 ;; Get the s5 scripts from
31 ;; http://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).
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.
44 ;; To test it, run:
46 ;; M-x org-s5-export-as-html
48 ;; in an Org mode buffer. See ox.el and ox-html.el for more details
49 ;; on how this exporter works.
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.
57 (require 'ox-html)
58 (eval-when-compile (require 'cl))
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)))
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)
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)
100 (defcustom org-s5-theme-file nil
101 "Url to S5 theme (slides.css) file. Can be overriden 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)
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 overriden with the S5_UI_URL property."
115 :group 'org-export-s5
116 :type 'string)
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)))
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)))
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.
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.
141 Note that changing the preamble or postamble will break the
142 core S5 stylesheets.")
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.
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.
152 Setting the S5_POSTAMBLE option -- or the :s5-postamble in publishing
153 projects -- will take precedence over this variable.
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" "&#x20;")
159 (string :tag "Custom formatting string")
160 (function :tag "Function (must return a string)")))
162 (defcustom org-s5-preamble "&#x20;"
163 "Peamble inserted into the S5 layout section.
165 When set to a string, use this string as the preamble.
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.
171 Setting S5_PREAMBLE option -- or the :s5-preamble in publishing
172 projects -- will take precedence over this variable.
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" "&#x20;")
178 (string :tag "Custom formatting string")
179 (function :tag "Function (must return a string)")))
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.
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)
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 "&nbsp;&nbsp;&nbsp;") (org-html--tags tags info))))
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))))))
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' type='text/javascript'></script>")) "\n")))
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))))
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)))
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))))
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"))
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")))
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.
356 If narrowing is active in the current buffer, only export its
357 narrowed part.
359 If a region is active, export that region.
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.
365 When optional argument SUBTREEP is non-nil, export the sub-tree
366 at point, extracting information from the headline properties
367 first.
369 When optional argument VISIBLE-ONLY is non-nil, don't export
370 contents of hidden elements.
372 When optional argument BODY-ONLY is non-nil, only write code
373 between \"<body>\" and \"</body>\" tags.
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.
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))))
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.
390 If narrowing is active in the current buffer, only export its
391 narrowed part.
393 If a region is active, export that region.
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.
399 When optional argument SUBTREEP is non-nil, export the sub-tree
400 at point, extracting information from the headline properties
401 first.
403 When optional argument VISIBLE-ONLY is non-nil, don't export
404 contents of hidden elements.
406 When optional argument BODY-ONLY is non-nil, only write code
407 between \"<body>\" and \"</body>\" tags.
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.
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)))
421 (defun org-s5-publish-to-html (plist filename pub-dir)
422 "Publish an org file to S5 HTML Presentation.
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.
428 Return output file name."
429 (org-publish-org-to 's5 filename ".html" plist pub-dir))
431 (provide 'ox-s5)
433 ;;; ox-s5.el ends here