ox-s5.el (s5) and ox-deck.el (deck): Update HTML options names
[org-mode.git] / contrib / lisp / ox-deck.el
blob72dc08da3304a0516650b07f192aafeccad4601c
1 ;;; ox-deck.el --- deck.js Presentation Back-End for Org Export Engine
3 ;; Copyright (C) 2013 Rick Frankel
5 ;; Author: Rick Frankel <emacs at rickster dot com>
6 ;; Keywords: outlines, hypermedia, slideshow
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation, either version 3 of the License, or
11 ;; (at your option) any later version.
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ;;; Commentary:
23 ;; This library implements a deck.js presentation back-end for the Org
24 ;; generic exporter.
26 ;; Installation
27 ;; -------------
28 ;; Get a copy of deck.js from http://imakewebthings.com/deck.js/ or
29 ;; the gitub repository at https://github.com/imakewebthings/deck.js.
31 ;; Add the path to the extracted code to the variable
32 ;; `org-deck-directories' There are a number of customization in the
33 ;; org-export-deck group, most of which can be overrriden with buffer
34 ;; local customization (starting with DECK_.)
36 ;; See ox.el and ox-html.el for more details on how this exporter
37 ;; works (it is derived from ox-html.)
39 (require 'ox-html)
40 (eval-when-compile (require 'cl))
42 (org-export-define-derived-backend deck html
43 :menu-entry
44 (?d "Export to deck.js HTML Presentation"
45 ((?H "To temporary buffer" org-deck-export-as-html)
46 (?h "To file" org-deck-export-to-html)
47 (?o "To file and open"
48 (lambda (a s v b)
49 (if a (org-deck-export-to-html t s v b)
50 (org-open-file (org-deck-export-to-html nil s v b)))))))
51 :options-alist
52 ((:html-link-home "HTML_LINK_HOME" nil nil)
53 (:html-link-up "HTML_LINK_UP" nil nil)
54 (:html-mathjax "HTML_MATHJAX" nil "" space)
55 (:html-postamble nil "html-postamble" nil t)
56 (:html-preamble nil "html-preamble" nil t)
57 (:html-head-extra "HTML_HEAD" nil org-html-head-extra newline)
58 (:html-head-include-default-style "HTML_INCLUDE_STYLE" nil nil)
59 (:html-head-include-scripts "HTML_INCLUDE_SCRIPTS" nil nil)
60 (:deck-base-url "DECK_BASE_URL" nil org-deck-base-url)
61 (:deck-theme "DECK_THEME" nil org-deck-theme)
62 (:deck-transition "DECK_TRANSITION" nil org-deck-transition)
63 (:deck-include-extensions "DECK_INCLUDE_EXTENSIONS" nil
64 org-deck-include-extensions split)
65 (:deck-exclude-extensions "DECK_EXCLUDE_EXTENSIONS" nil
66 org-deck-exclude-extensions split))
67 :translate-alist
68 ((headline . org-deck-headline)
69 (inner-template . org-deck-inner-template)
70 (item . org-deck-item)
71 (template . org-deck-template)))
73 (defgroup org-export-deck nil
74 "Options for exporting Org mode files to deck.js HTML Presentations."
75 :tag "Org Export DECK"
76 :group 'org-export-html)
78 (defcustom org-deck-directories '("./deck.js")
79 "Directories to search for deck.js components (jquery,
80 modernizr; core, extensions and themes directories.)"
81 :group 'org-export-deck
82 :type '(repeat (string :tag "Directory")))
84 (defun org-deck--cleanup-components (components)
85 (remove-duplicates
86 (car (remove 'nil components))
87 :test (lambda (x y)
88 (string= (file-name-nondirectory x)
89 (file-name-nondirectory y)))))
91 (defun org-deck--find-extensions ()
92 "Returns a unique list of all extensions found in
93 in the extensions directories under `org-deck-directories'"
94 (org-deck--cleanup-components
95 (mapcar ; extensions under existing dirs
96 (lambda (dir)
97 (when (file-directory-p dir) (directory-files dir t "^[^.]")))
98 (mapcar ; possible extension directories
99 (lambda (x) (expand-file-name "extensions" x))
100 org-deck-directories))))
102 (defun org-deck--find-css (type)
103 "Return a unique list of all the css stylesheets in the themes/TYPE
104 directories under `org-deck-directories'."
105 (org-deck--cleanup-components
106 (mapcar
107 (lambda (dir)
108 (let ((css-dir (expand-file-name
109 (concat (file-name-as-directory "themes") type) dir)))
110 (when (file-directory-p css-dir)
111 (directory-files css-dir t "\\.css$"))))
112 org-deck-directories)))
114 (defun org-deck-list-components ()
115 "List all available deck extensions, styles and
116 transitions (with full paths) to a temporary buffer."
117 (interactive)
118 (let ((outbuf (get-buffer-create "*deck.js Extensions*")))
119 (with-current-buffer outbuf
120 (erase-buffer)
121 (insert "Extensions\n----------\n")
122 (insert (mapconcat 'identity (org-deck--find-extensions) "\n"))
123 (insert "\n\nStyles\n------\n")
124 (insert (mapconcat 'identity (org-deck--find-css "style") "\n"))
125 (insert "\n\nTransitions\n----------\n")
126 (insert (mapconcat 'identity (org-deck--find-css "transition") "\n")))
127 (switch-to-buffer-other-window outbuf)))
129 (defcustom org-deck-include-extensions nil
130 "If non-nil, list of extensions to include instead of all available.
131 Can be overriden or set with the DECK_INCLUDE_EXTENSIONS property.
132 During output generation, the extensions found by
133 `org-deck--find-extensions' are searched for the appropriate
134 files (scripts and/or stylesheets) to include in the generated
135 html. The href/src attributes are created relative to `org-deck-base-url'."
136 :group 'org-export-deck
137 :type '(repeat (string :tag "Extension")))
139 (defcustom org-deck-exclude-extensions nil
140 "If non-nil, list of extensions to exclude.
141 Can be overriden or set with the DECK_EXCLUDE_EXTENSIONS property."
142 :group 'org-export-deck
143 :type '(repeat (string :tag "Extension")))
145 (defcustom org-deck-theme "swiss.css"
146 "deck.js theme. Can be overriden with the DECK_THEME property.
147 If this value contains a path component (\"/\"), it is used as a
148 literal path (url). Otherwise it is prepended with
149 `org-deck-base-url'/themes/style/."
150 :group 'org-export-deck
151 :type 'string)
153 (defcustom org-deck-transition "fade.css"
154 "deck.js transition theme. Can be overriden with the
155 DECK_TRANSITION property.
156 If this value contains a path component (\"/\"), it is used as a
157 literal path (url). Otherwise it is prepended with
158 `org-deck-base-url'/themes/transition/."
159 :group 'org-export-deck
160 :type 'string)
162 (defcustom org-deck-base-url "deck.js"
163 "Url prefix to deck.js base directory containing the core, extensions
164 and themes directories.
165 Can be overriden with the DECK_BASE_URL property."
166 :group 'org-export-deck
167 :type 'string)
169 (defcustom org-deck-footer-template
170 "<h1>%author - %title</h1>"
171 "Format template to specify footer div.
172 Completed using `org-fill-template'.
173 Optional keys include %author, %email, %file, %title and %date.
174 This is included in a <footer> section."
175 :group 'org-export-deck
176 :type 'string)
178 (defcustom org-deck-header-template ""
179 "Format template to specify page. Completed using `org-fill-template'.
180 Optional keys include %author, %email, %file, %title and %date.
181 This is included in a <header> section."
182 :group 'org-export-deck
183 :type 'string)
185 (defcustom org-deck-title-page-style
186 "<style type='text/css'>
187 header, footer { left: 5px; width: 100% }
188 header { position: absolute; top: 10px; }
189 #title-slide h1 {
190 position: static; padding: 0;
191 margin-top: 10%;
192 -webkit-transform: none;
193 -moz-transform: none;
194 -ms-transform: none;
195 -o-transform: none;
196 transform: none;
198 #title-slide h2 {
199 text-align: center;
200 border:none;
201 padding: 0;
202 margin: 0;
204 </style>"
205 "CSS styles to use for title page"
206 :group 'org-export-deck
207 :type 'string)
209 (defcustom org-deck-title-page-template
210 "<div class='slide' id='title-slide'>
211 <h1>%title</h1>
212 <h2>%author</h2>
213 <h2>%email</h2>
214 <h2>%date</h2>
215 </div>"
216 "Format template to specify title page div.
217 Completed using `org-fill-template'.
218 Optional keys include %author, %email, %file, %title and %date.
219 Note that the wrapper div must include the class \"slide\"."
220 :group 'org-export-deck
221 :type 'string)
223 (defcustom org-deck-toc-style
224 "<style type='text/css'>
225 header, footer { left: 5px; width: 100% }
226 header { position: absolute; top: 10px; }
227 #table-of-contents h1 {
228 position: static; padding: 0;
229 margin-top: 10%;
230 -webkit-transform: none;
231 -moz-transform: none;
232 -ms-transform: none;
233 -o-transform: none;
234 Transform: none;
236 #title-slide h2 {
237 text-align: center;
238 border:none;
239 padding: 0;
240 margin: 0;
242 </style>"
243 "CSS styles to use for title page"
244 :group 'org-export-deck
245 :type 'string)
247 (defun org-deck-toc (depth info)
248 (concat
249 "<div id=\"table-of-contents\" class=\"slide\">\n"
250 (format "<h2>%s</h2>\n"
251 (org-html--translate "Table of Contents" info))
252 (org-html--toc-text
253 (mapcar
254 (lambda (headline)
255 (let* ((class (org-element-property :HTML_CONTAINER_CLASS headline))
256 (section-number
257 (when
258 (and (not (org-export-low-level-p headline info))
259 (org-export-numbered-headline-p headline info))
260 (concat
261 (mapconcat
262 'number-to-string
263 (org-export-get-headline-number headline info) ".") ". ")))
264 (title
265 (concat
266 section-number
267 (replace-regexp-in-string ; remove any links in headline...
268 "</?a[^>]*>" ""
269 (org-export-data
270 (org-element-property :title headline) info)))))
271 (cons
272 (if (and class (string-match-p "\\<slide\\>" class))
273 (format
274 "<a href='#outline-container-%s'>%s</a>"
275 (or (org-element-property :CUSTOM_ID headline)
276 (mapconcat
277 'number-to-string
278 (org-export-get-headline-number headline info) "-"))
279 title)
280 title)
281 (org-export-get-relative-level headline info))))
282 (org-export-collect-headlines info depth)))
283 "</div>\n"))
285 (defun org-deck--get-packages (info)
286 (let ((prefix (concat (plist-get info :deck-base-url) "/"))
287 (theme (plist-get info :deck-theme))
288 (transition (plist-get info :deck-transition))
289 (include (plist-get info :deck-include-extensions))
290 (exclude (plist-get info :deck-exclude-extensions))
291 (scripts '()) (sheets '()) (snippets '()))
292 (add-to-list 'scripts (concat prefix "jquery-1.7.2.min.js"))
293 (add-to-list 'scripts (concat prefix "core/deck.core.js"))
294 (add-to-list 'scripts (concat prefix "modernizr.custom.js"))
295 (add-to-list 'sheets (concat prefix "core/deck.core.css"))
296 (mapc
297 (lambda (extdir)
298 (let* ((name (file-name-nondirectory extdir))
299 (dir (file-name-as-directory extdir))
300 (path (concat prefix "extensions/" name "/"))
301 (base (format "deck.%s." name)))
302 (when (and (or (eq nil include) (member name include))
303 (not (member name exclude)))
304 (when (file-exists-p (concat dir base "js"))
305 (add-to-list 'scripts (concat path base "js")))
306 (when (file-exists-p (concat dir base "css"))
307 (add-to-list 'sheets (concat path base "css")))
308 (when (file-exists-p (concat dir base "html"))
309 (add-to-list 'snippets (concat dir base "html"))))))
310 (org-deck--find-extensions))
311 (if (not (string-match-p "^[[:space:]]*$" theme))
312 (add-to-list 'sheets
313 (if (file-name-directory theme) theme
314 (format "%sthemes/style/%s" prefix theme))))
315 (if (not (string-match-p "^[[:space:]]*$" transition))
316 (add-to-list
317 'sheets
318 (if (file-name-directory transition) transition
319 (format "%sthemes/transition/%s" prefix transition))))
320 (list :scripts (nreverse scripts) :sheets (nreverse sheets)
321 :snippets snippets)))
323 (defun org-deck-inner-template (contents info)
324 "Return body of document string after HTML conversion.
325 CONTENTS is the transcoded contents string. INFO is a plist
326 holding export options."
327 (concat contents "\n"))
329 (defun org-deck-headline (headline contents info)
330 (let ((org-html-toplevel-hlevel 2)
331 (class (or (org-element-property :HTML_CONTAINER_CLASS headline) ""))
332 (level (org-export-get-relative-level headline info)))
333 (when (and (= 1 level) (not (string-match-p "\\<slide\\>" class)))
334 (org-element-put-property headline :HTML_CONTAINER_CLASS (concat class " slide")))
335 (org-html-headline headline contents info)))
337 (defun org-deck-item (item contents info)
338 "Transcode an ITEM element from Org to HTML.
339 CONTENTS holds the contents of the item. INFO is a plist holding
340 contextual information.
341 If the containing headline has the property :slide, then
342 the \"slide\" class will be added to the to the list element,
343 which will make the list into a \"build\"."
344 (let ((text (org-html-item item contents info)))
345 (if (org-export-get-node-property :STEP item t)
346 (replace-regexp-in-string "^<li>" "<li class='slide'>" text)
347 text)))
349 (defun org-deck-template-alist (info)
350 (list
351 `("title" . ,(car (plist-get info :title)))
352 `("author" . ,(car (plist-get info :author)))
353 `("email" . ,(plist-get info :email))
354 `("date" . ,(nth 0 (plist-get info :date)))
355 `("file" . ,(plist-get info :input-file))))
357 (defun org-deck-template (contents info)
358 "Return complete document string after HTML conversion.
359 CONTENTS is the transcoded contents string. INFO is a plist
360 holding export options."
361 (let ((pkg-info (org-deck--get-packages info)))
362 (mapconcat
363 'identity
364 (list
365 "<!DOCTYPE html>"
366 (let ((lang (plist-get info :language)))
367 (mapconcat
368 (lambda (x)
369 (apply
370 'format
371 "<!--%s <html class='no-js %s' lang='%s'> %s<![endif]-->"
373 (list `("[if lt IE 7]>" "ie6" ,lang "")
374 `("[if IE 7]>" "ie7" ,lang "")
375 `("[if IE 8]>" "ie8" ,lang "")
376 `("[if gt IE 8]><!-->" "" ,lang "<!--")) "\n"))
377 "<head>"
378 (org-deck--build-meta-info info)
379 (mapconcat
380 (lambda (sheet)
381 (format
382 "<link rel='stylesheet' href='%s' type='text/css' />" sheet))
383 (plist-get pkg-info :sheets) "\n")
384 "<style type='text/css'>"
385 "#table-of-contents a {color: inherit;}"
386 "#table-of-contents ul {margin-bottom: 0;}"
387 (when (plist-get info :section-numbers)
388 "#table-of-contents ul li {list-style-type: none;}")
389 "</style>"
391 (mapconcat
392 (lambda (script)
393 (format
394 "<script src='%s' type='text/javascript'></script>" script))
395 (plist-get pkg-info :scripts) "\n")
396 (org-html--build-mathjax-config info)
397 "<script type='text/javascript'>"
398 " $(document).ready(function () { $.deck('.slide'); });"
399 "</script>"
400 (org-html--build-head info)
401 org-deck-title-page-style
402 "</head>"
403 "<body>"
404 "<header class='deck-status'>"
405 (org-fill-template
406 org-deck-header-template (org-deck-template-alist info))
407 "</header>"
408 "<div class='deck-container'>"
409 ;; title page
410 (org-fill-template
411 org-deck-title-page-template (org-deck-template-alist info))
412 ;; toc page
413 (let ((depth (plist-get info :with-toc)))
414 (when depth (org-deck-toc depth info)))
415 contents
416 (mapconcat
417 (lambda (snippet)
418 (with-temp-buffer (insert-file-contents snippet)
419 (buffer-string)))
420 (plist-get pkg-info :snippets) "\n")
421 "<footer class='deck-status'>"
422 (org-fill-template
423 org-deck-footer-template (org-deck-template-alist info))
424 "</footer>"
425 "</div>"
426 "</body>"
427 "</html>\n") "\n")))
429 (defun org-deck--build-meta-info (info)
430 "Return meta tags for exported document.
431 INFO is a plist used as a communication channel."
432 (let* ((title (org-export-data (plist-get info :title) info))
433 (author (and (plist-get info :with-author)
434 (let ((auth (plist-get info :author)))
435 (and auth (org-export-data auth info)))))
436 (date (and (plist-get info :with-date)
437 (let ((date (plist-get info :date)))
438 (and date (org-export-data date info)))))
439 (description (plist-get info :description))
440 (keywords (plist-get info :keywords)))
441 (mapconcat
442 'identity
443 (list
444 (format "<title>%s</title>" title)
445 (format "<meta charset='%s' />"
446 (or (and org-html-coding-system
447 (fboundp 'coding-system-get)
448 (coding-system-get
449 org-html-coding-system 'mime-charset))
450 "iso-8859-1"))
451 (mapconcat
452 (lambda (attr)
453 (when (< 0 (length (car attr)))
454 (format "<meta name='%s' content='%s'/>\n"
455 (nth 1 attr) (car attr))))
456 (list '("Org-mode" "generator")
457 `(,author "author")
458 `(,description "description")
459 `(,keywords "keywords")) "")) "\n")))
460 (defun org-deck-export-as-html
461 (&optional async subtreep visible-only body-only ext-plist)
462 "Export current buffer to an HTML buffer.
464 If narrowing is active in the current buffer, only export its
465 narrowed part.
467 If a region is active, export that region.
469 A non-nil optional argument ASYNC means the process should happen
470 asynchronously. The resulting buffer should be accessible
471 through the `org-export-stack' interface.
473 When optional argument SUBTREEP is non-nil, export the sub-tree
474 at point, extracting information from the headline properties
475 first.
477 When optional argument VISIBLE-ONLY is non-nil, don't export
478 contents of hidden elements.
480 When optional argument BODY-ONLY is non-nil, only write code
481 between \"<body>\" and \"</body>\" tags.
483 EXT-PLIST, when provided, is a property list with external
484 parameters overriding Org default settings, but still inferior to
485 file-local settings.
487 Export is done in a buffer named \"*Org deck.js Export*\", which
488 will be displayed when `org-export-show-temporary-export-buffer'
489 is non-nil."
490 (interactive)
491 (if async
492 (org-export-async-start
493 (lambda (output)
494 (with-current-buffer (get-buffer-create "*Org deck.js Export*")
495 (erase-buffer)
496 (insert output)
497 (goto-char (point-min))
498 (nxml-mode)
499 (org-export-add-to-stack (current-buffer) 'deck)))
500 `(org-export-as 'deck ,subtreep ,visible-only ,body-only ',ext-plist))
501 (let ((outbuf (org-export-to-buffer
502 'deck "*Org deck.js Export*"
503 subtreep visible-only body-only ext-plist)))
504 ;; Set major mode.
505 (with-current-buffer outbuf (nxml-mode))
506 (when org-export-show-temporary-export-buffer
507 (switch-to-buffer-other-window outbuf)))))
509 (defun org-deck-export-to-html
510 (&optional async subtreep visible-only body-only ext-plist)
511 "Export current buffer to a deck.js HTML file.
513 If narrowing is active in the current buffer, only export its
514 narrowed part.
516 If a region is active, export that region.
518 A non-nil optional argument ASYNC means the process should happen
519 asynchronously. The resulting file should be accessible through
520 the `org-export-stack' interface.
522 When optional argument SUBTREEP is non-nil, export the sub-tree
523 at point, extracting information from the headline properties
524 first.
526 When optional argument VISIBLE-ONLY is non-nil, don't export
527 contents of hidden elements.
529 When optional argument BODY-ONLY is non-nil, only write code
530 between \"<body>\" and \"</body>\" tags.
532 EXT-PLIST, when provided, is a property list with external
533 parameters overriding Org default settings, but still inferior to
534 file-local settings.
536 Return output file's name."
537 (interactive)
538 (let* ((extension (concat "." org-html-extension))
539 (file (org-export-output-file-name extension subtreep))
540 (org-export-coding-system org-html-coding-system))
541 (if async
542 (org-export-async-start
543 (lambda (f) (org-export-add-to-stack f 'deck))
544 (let ((org-export-coding-system org-html-coding-system))
545 `(expand-file-name
546 (org-export-to-file
547 'deck ,file ,subtreep ,visible-only ,body-only ',ext-plist))))
548 (let ((org-export-coding-system org-html-coding-system))
549 (org-export-to-file
550 'deck file subtreep visible-only body-only ext-plist)))))
552 (defun org-deck-publish-to-html (plist filename pub-dir)
553 "Publish an org file to deck.js HTML Presentation.
554 FILENAME is the filename of the Org file to be published. PLIST
555 is the property list for the given project. PUB-DIR is the
556 publishing directory. Returns output file name."
557 (org-publish-org-to 'deck filename ".html" plist pub-dir))
559 (provide 'ox-deck)