summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <n.goaziou@gmail.com>2012-09-09 13:06:36 +0200
committerNicolas Goaziou <n.goaziou@gmail.com>2012-09-09 13:13:57 +0200
commit113ca8767a15ef135cc7f0a53d7d420fbd011392 (patch)
tree58e928110faf70f9dc0f776b925a94c4bf4c0bea
parent659be3775861c0e3bbfa2df2c3071af1a4f0ad4d (diff)
downloadorg-mode-113ca8767a15ef135cc7f0a53d7d420fbd011392.tar.gz
org-export: Define new interface for `org-export-dispatch'
* contrib/lisp/org-export.el (org-export-define-backend): Add :menu-entry keyword. (org-export-define-derived-backend): Add :menu-entry and :sub-menu-entry keywords. (org-export-dispatch-menu-entries): New variable. (org-export-dispatch): Define a new interface. (org-export-dispatch-ui): Rewrite function. (org-export-dispatch-action): New function. Every back-end defined through `org-export-define-backend' or `org-export-define-derived-backend' can specify a menu entry for `org-export-dispatch'. Navigating the menu is now a two levels process.
-rw-r--r--contrib/lisp/org-export.el480
1 files changed, 278 insertions, 202 deletions
diff --git a/contrib/lisp/org-export.el b/contrib/lisp/org-export.el
index e094569..b6f9c69 100644
--- a/contrib/lisp/org-export.el
+++ b/contrib/lisp/org-export.el
@@ -756,71 +756,54 @@ keywords are understood:
shouldn't make a back-end test, as it may prevent back-ends
derived from this one to behave properly.
+ :menu-entry
+
+ Menu entry for the export dispatcher. It should be a list
+ like:
+
+ \(KEY DESCRIPTION ACTION-OR-MENU)
+
+ where :
+
+ KEY is a free character selecting the back-end.
+ DESCRIPTION is a string naming the back-end.
+ ACTION-OR-MENU is either a function or an alist.
+
+ If it is an action, it will be called with three arguments:
+ SUBTREEP, VISIBLE-ONLY and BODY-ONLY. See `org-export-as'
+ for further explanations.
+
+ If it is an alist, associations should follow the
+ pattern:
+
+ \(KEY DESCRIPTION ACTION)
+
+ where KEY, DESCRIPTION and ACTION are described above.
+
+ Valid values include:
+
+ \(?m \"My Special Back-end\" my-special-export-function)
+
+ or
+
+ \(?l \"Export to LaTeX\"
+ \((?b \"TEX (buffer)\" org-e-latex-export-as-latex)
+ \(?l \"TEX (file)\" org-e-latex-export-to-latex)
+ \(?p \"PDF file\" org-e-latex-export-to-pdf)
+ \(?o \"PDF file and open\"
+ \(lambda (subtree visible body-only)
+ \(org-open-file
+ \(org-e-latex-export-to-pdf subtree visible body-only))))))
+
:options-alist
Alist between back-end specific properties introduced in
communication channel and how their value are acquired. See
`org-export-options-alist' for more information about
- structure of the values.
-
-As an example, here is how the `e-ascii' back-end is defined:
-
-\(org-export-define-backend e-ascii
- \((bold . org-e-ascii-bold)
- \(center-block . org-e-ascii-center-block)
- \(clock . org-e-ascii-clock)
- \(code . org-e-ascii-code)
- \(drawer . org-e-ascii-drawer)
- \(dynamic-block . org-e-ascii-dynamic-block)
- \(entity . org-e-ascii-entity)
- \(example-block . org-e-ascii-example-block)
- \(export-block . org-e-ascii-export-block)
- \(export-snippet . org-e-ascii-export-snippet)
- \(fixed-width . org-e-ascii-fixed-width)
- \(footnote-definition . org-e-ascii-footnote-definition)
- \(footnote-reference . org-e-ascii-footnote-reference)
- \(headline . org-e-ascii-headline)
- \(horizontal-rule . org-e-ascii-horizontal-rule)
- \(inline-src-block . org-e-ascii-inline-src-block)
- \(inlinetask . org-e-ascii-inlinetask)
- \(italic . org-e-ascii-italic)
- \(item . org-e-ascii-item)
- \(keyword . org-e-ascii-keyword)
- \(latex-environment . org-e-ascii-latex-environment)
- \(latex-fragment . org-e-ascii-latex-fragment)
- \(line-break . org-e-ascii-line-break)
- \(link . org-e-ascii-link)
- \(paragraph . org-e-ascii-paragraph)
- \(plain-list . org-e-ascii-plain-list)
- \(plain-text . org-e-ascii-plain-text)
- \(planning . org-e-ascii-planning)
- \(property-drawer . org-e-ascii-property-drawer)
- \(quote-block . org-e-ascii-quote-block)
- \(quote-section . org-e-ascii-quote-section)
- \(radio-target . org-e-ascii-radio-target)
- \(section . org-e-ascii-section)
- \(special-block . org-e-ascii-special-block)
- \(src-block . org-e-ascii-src-block)
- \(statistics-cookie . org-e-ascii-statistics-cookie)
- \(strike-through . org-e-ascii-strike-through)
- \(subscript . org-e-ascii-subscript)
- \(superscript . org-e-ascii-superscript)
- \(table . org-e-ascii-table)
- \(table-cell . org-e-ascii-table-cell)
- \(table-row . org-e-ascii-table-row)
- \(target . org-e-ascii-target)
- \(template . org-e-ascii-template)
- \(timestamp . org-e-ascii-timestamp)
- \(underline . org-e-ascii-underline)
- \(verbatim . org-e-ascii-verbatim)
- \(verse-block . org-e-ascii-verse-block))
- :export-block \"ASCII\"
- :filters-alist ((:filter-headline . org-e-ascii-filter-headline-blank-lines)
- \(:filter-section . org-e-ascii-filter-headline-blank-lines))
- :options-alist ((:ascii-charset nil nil org-e-ascii-charset)))"
+ structure of the values."
(declare (debug (&define name sexp [&rest [keywordp sexp]] defbody))
(indent 1))
- (let (filters options export-block)
+ (let (export-block filters menu-entry options)
(while (keywordp (car body))
(case (pop body)
(:export-block (let ((names (pop body)))
@@ -828,6 +811,7 @@ As an example, here is how the `e-ascii' back-end is defined:
(if (consp names) (mapcar 'upcase names)
(list (upcase names))))))
(:filters-alist (setq filters (pop body)))
+ (:menu-entry (setq menu-entry (pop body)))
(:options-alist (setq options (pop body)))
(t (pop body))))
`(progn
@@ -853,6 +837,11 @@ See `org-export-filters-alist' for more information."))
(add-to-list 'org-element-block-name-alist
`(,name . org-element-export-block-parser)))
',export-block))
+ ;; Add an entry for back-end in `org-export-dispatch'.
+ ,(when menu-entry
+ (let ((menu (assq (car menu-entry) org-export-dispatch-menu-entries)))
+ (if menu `(setcdr ',menu ',(cdr menu-entry))
+ `(push ',menu-entry org-export-dispatch-menu-entries))))
;; Splice in the body, if any.
,@body)))
@@ -877,7 +866,13 @@ keywords are understood:
Alist of filters that will overwrite or complete filters
defined in PARENT back-end. See `org-export-filters-alist'
- for more a list of allowed filters.
+ for a list of allowed filters.
+
+ :menu-entry
+
+ Menu entry for the export dispatcher. See
+ `org-export-define-backend' for more information about the
+ expected value.
:options-alist
@@ -886,6 +881,28 @@ keywords are understood:
`org-export-options-alist' for more information about
structure of the values.
+ :sub-menu-entry
+
+ Append entries to an existing menu in the export dispatcher.
+ The associated value should be a list whose CAR is the
+ character selecting the menu to expand and CDR a list of
+ entries following the pattern:
+
+ \(KEY DESCRIPTION ACTION)
+
+ where KEY is a free character triggering the action,
+ DESCRIPTION is a string defining the action, and ACTION is
+ a function that will be called with three arguments:
+ SUBTREEP, VISIBLE-ONLY and BODY-ONLY. See `org-export-as'
+ for further explanations.
+
+ Valid values include:
+
+ \(?l (?P \"As PDF file (Beamer)\" org-e-beamer-export-to-pdf)
+ \(?O \"As PDF file and open (Beamer)\"
+ \(lambda (s v b)
+ \(org-open-file (org-e-beamer-export-to-pdf s v b)))))
+
:translate-alist
Alist of element and object types and transcoders that will
@@ -905,7 +922,7 @@ The back-end could then be called with, for example:
\(org-export-to-buffer 'my-latex \"*Test my-latex*\")"
(declare (debug (&define name sexp [&rest [keywordp sexp]] def-body))
(indent 2))
- (let (filters options translate export-block)
+ (let (export-block filters menu-entry options sub-menu-entry translate)
(while (keywordp (car body))
(case (pop body)
(:export-block (let ((names (pop body)))
@@ -913,7 +930,9 @@ The back-end could then be called with, for example:
(if (consp names) (mapcar 'upcase names)
(list (upcase names))))))
(:filters-alist (setq filters (pop body)))
+ (:menu-entry (setq menu-entry (pop body)))
(:options-alist (setq options (pop body)))
+ (:sub-menu-entry (setq sub-menu-entry (pop body)))
(:translate-alist (setq translate (pop body)))
(t (pop body))))
`(progn
@@ -951,6 +970,15 @@ structure of the values."
(symbol-value
(intern (format "org-%s-translate-alist" parent)))))
"Alist between element or object types and translators.")
+ ;; Add an entry for back-end in `org-export-dispatch'.
+ ,(when menu-entry
+ (let ((menu (assq (car menu-entry) org-export-dispatch-menu-entries)))
+ (if menu `(setcdr ',menu ',(cdr menu-entry))
+ `(push ',menu-entry org-export-dispatch-menu-entries))))
+ ,(when sub-menu-entry
+ (let ((menu (assq (car sub-menu-entry)
+ org-export-dispatch-menu-entries)))
+ (when menu `(nconc ',(nth 2 menu) ',(cdr sub-menu-entry)))))
;; Splice in the body, if any.
,@body)))
@@ -4328,8 +4356,14 @@ to `:default' encoding. If it fails, return S."
;;
;; `org-export-dispatch' is the standard interactive way to start an
;; export process. It uses `org-export-dispatch-ui' as a subroutine
-;; for its interface. Most commons back-ends should have an entry in
-;; it.
+;; for its interface, which, in turn, delegates response to key
+;; pressed to `org-export-dispatch-action'.
+
+(defvar org-export-dispatch-menu-entries nil
+ "List of menu entries available for `org-export-dispatch'.
+This variable shouldn't be set directly. Set-up :menu-entry
+keyword in either `org-export-define-backend' or
+`org-export-define-derived-backend' instead.")
;;;###autoload
(defun org-export-dispatch ()
@@ -4343,77 +4377,30 @@ to switch to one or the other.
Return an error if key pressed has no associated command."
(interactive)
- (let* ((input (org-export-dispatch-ui
- (if (listp org-export-initial-scope) org-export-initial-scope
- (list org-export-initial-scope))
- org-export-dispatch-use-expert-ui))
- (raw-key (car input))
+ (let* ((input (org-export-dispatch-ui (list org-export-initial-scope)
+ nil
+ org-export-dispatch-use-expert-ui))
+ (action (car input))
(optns (cdr input)))
- ;; Translate "C-a", "C-b"... into "a", "b"... Then take action
- ;; depending on user's key pressed.
- (case (if (< raw-key 27) (+ raw-key 96) raw-key)
- ;; Allow to quit with "q" key.
- (?q nil)
- ;; Export with `e-ascii' back-end.
- ((?A ?N ?U)
- (org-e-ascii-export-as-ascii
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns)
- `(:ascii-charset ,(case raw-key (?A 'ascii) (?N 'latin1) (t 'utf-8)))))
- ((?a ?n ?u)
- (org-e-ascii-export-to-ascii
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns)
- `(:ascii-charset ,(case raw-key (?a 'ascii) (?n 'latin1) (t 'utf-8)))))
- ;; Export with `e-latex' back-end.
- (?L (org-e-latex-export-as-latex
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns)))
- (?l
- (org-e-latex-export-to-latex
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns)))
- (?p
- (org-e-latex-export-to-pdf
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns)))
- (?d
- (org-open-file
- (org-e-latex-export-to-pdf
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns))))
- ;; Export with `e-html' back-end.
- (?H
- (org-e-html-export-as-html
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns)))
- (?h
- (org-e-html-export-to-html
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns)))
- (?b
- (org-open-file
- (org-e-html-export-to-html
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns))))
- ;; Export with `e-odt' back-end.
- (?o
- (org-e-odt-export-to-odt
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns)))
- (?O
- (org-open-file
- (org-e-odt-export-to-odt
- (memq 'subtree optns) (memq 'visible optns) (memq 'body optns))
- 'system))
- ;; Publishing facilities
- (?F
- (org-e-publish-current-file (memq 'force optns)))
- (?P
+ (case action
+ ;; First handle special hard-coded actions.
+ (publish-current-file (org-e-publish-current-file (memq 'force optns)))
+ (publish-current-project
(org-e-publish-current-project (memq 'force optns)))
- (?X
- (let ((project
- (assoc (org-icompleting-read
- "Publish project: " org-e-publish-project-alist nil t)
- org-e-publish-project-alist)))
- (org-e-publish project (memq 'force optns))))
- (?E
- (org-e-publish-all (memq 'force optns)))
- ;; Undefined command.
- (t (error "No command associated with key %s"
- (char-to-string raw-key))))))
-
-(defun org-export-dispatch-ui (options expertp)
+ (publish-choose-project
+ (org-e-publish (assoc (org-icompleting-read
+ "Publish project: "
+ org-e-publish-project-alist nil t)
+ org-e-publish-project-alist)
+ (memq 'force optns)))
+ (publish-all (org-e-publish-all (memq 'force optns)))
+ (otherwise
+ (funcall action
+ (memq 'subtree optns)
+ (memq 'visible optns)
+ (memq 'body optns))))))
+
+(defun org-export-dispatch-ui (options first-key expertp)
"Handle interface for `org-export-dispatch'.
OPTIONS is a list containing current interactive options set for
@@ -4423,85 +4410,174 @@ export. It can contain any of the following symbols:
`visible' restricts export to visible part of buffer.
`force' force publishing files.
+FIRST-KEY is the key pressed to select the first level menu. It
+is nil when this menu hasn't been selected yet.
+
EXPERTP, when non-nil, triggers expert UI. In that case, no help
buffer is provided, but indications about currently active
options are given in the prompt. Moreover, \[?] allows to switch
-back to standard interface.
-
-Return value is a list with key pressed as CAR and a list of
-final interactive export options as CDR."
- (let ((help
- (format "---- (Options) -------------------------------------------
-
-\[1] Body only: %s [2] Export scope: %s
-\[3] Visible only: %s [4] Force publishing: %s
-
-
---- (ASCII/Latin-1/UTF-8 Export) -------------------------
-
-\[a/n/u] to TXT file [A/N/U] to temporary buffer
-
---- (HTML Export) ----------------------------------------
-
-\[h] to HTML file [b] ... and open it
-\[H] to temporary buffer
-
---- (LaTeX Export) ---------------------------------------
-
-\[l] to TEX file [L] to temporary buffer
-\[p] to PDF file [d] ... and open it
-
---- (ODF Export) -----------------------------------------
-
-\[o] to ODT file [O] ... and open it
-
---- (Publish) --------------------------------------------
-
-\[F] current file [P] current project
-\[X] a project [E] every project"
- (if (memq 'body options) "On " "Off")
- (if (memq 'subtree options) "Subtree" "Buffer ")
- (if (memq 'visible options) "On " "Off")
- (if (memq 'force options) "On " "Off")))
- (standard-prompt "Export command: ")
- (expert-prompt (format "Export command (%s%s%s%s): "
- (if (memq 'body options) "b" "-")
- (if (memq 'subtree options) "s" "-")
- (if (memq 'visible options) "v" "-")
- (if (memq 'force options) "f" "-")))
- (handle-keypress
- (function
- ;; Read a character from command input, toggling interactive
- ;; options when applicable. PROMPT is the displayed prompt,
- ;; as a string.
- (lambda (prompt)
- (let ((key (read-char-exclusive prompt)))
- (cond
- ;; Ignore non-standard characters (i.e. "M-a").
- ((not (characterp key)) (org-export-dispatch-ui options expertp))
- ;; Help key: Switch back to standard interface if
- ;; expert UI was active.
- ((eq key ??) (org-export-dispatch-ui options nil))
- ;; Toggle export options.
- ((memq key '(?1 ?2 ?3 ?4))
- (org-export-dispatch-ui
- (let ((option (case key (?1 'body) (?2 'subtree) (?3 'visible)
- (?4 'force))))
- (if (memq option options) (remq option options)
- (cons option options)))
- expertp))
- ;; Action selected: Send key and options back to
- ;; `org-export-dispatch'.
- (t (cons key options))))))))
+back to standard interface."
+ (let* ((fontify-key
+ (lambda (key &optional access-key)
+ ;; Fontify KEY string. Optional argument ACCESS-KEY, when
+ ;; non-nil is the required first-level key to activate
+ ;; KEY. When its value is t, activate KEY independently
+ ;; on the first key, if any. A nil value means KEY will
+ ;; only be activated at first level.
+ (if (or (eq access-key t) (eq access-key first-key))
+ (org-add-props key nil 'face 'org-warning)
+ (org-no-properties key))))
+ ;; Make sure order of menu doesn't depend on the order in
+ ;; which back-ends are loaded.
+ (backends (sort (copy-sequence org-export-dispatch-menu-entries)
+ (lambda (a b) (< (car a) (car b)))))
+ ;; Compute a list of allowed keys based on the first key
+ ;; pressed, if any. Some keys (?1, ?2, ?3, ?4 and ?q) are
+ ;; always available.
+ (allowed-keys
+ (nconc (list ?1 ?2 ?3 ?4)
+ (mapcar 'car
+ (if (not first-key) backends
+ (nth 2 (assq first-key backends))))
+ (cond ((eq first-key ?P) (list ?f ?p ?x ?a))
+ ((not first-key) (list ?P)))
+ (when expertp (list ??))
+ (list ?q)))
+ ;; Build the help menu for standard UI.
+ (help
+ (unless expertp
+ (concat
+ ;; Options are hard-coded.
+ (format "Options
+ [%s] Body only: %s [%s] Visible only: %s
+ [%s] Export scope: %s [%s] Force publishing: %s\n\n"
+ (funcall fontify-key "1" t)
+ (if (memq 'body options) "On " "Off")
+ (funcall fontify-key "2" t)
+ (if (memq 'visible options) "On " "Off")
+ (funcall fontify-key "3" t)
+ (if (memq 'subtree options) "Subtree" "Buffer ")
+ (funcall fontify-key "4" t)
+ (if (memq 'force options) "On " "Off"))
+ ;; Display registered back-end entries.
+ (mapconcat
+ (lambda (entry)
+ (let ((top-key (car entry)))
+ (concat
+ (format "[%s] %s\n"
+ (funcall fontify-key (char-to-string top-key))
+ (nth 1 entry))
+ (let ((sub-menu (nth 2 entry)))
+ (unless (functionp sub-menu)
+ ;; Split sub-menu into two columns.
+ (let ((index -1))
+ (concat
+ (mapconcat
+ (lambda (sub-entry)
+ (incf index)
+ (format (if (zerop (mod index 2)) " [%s] %-24s"
+ "[%s] %s\n")
+ (funcall fontify-key
+ (char-to-string (car sub-entry))
+ top-key)
+ (nth 1 sub-entry)))
+ sub-menu "")
+ (when (zerop (mod index 2)) "\n"))))))))
+ backends "\n")
+ ;; Publishing menu is hard-coded.
+ (format "\n[%s] Publish
+ [%s] Current file [%s] Current project
+ [%s] Choose project [%s] All projects\n\n"
+ (funcall fontify-key "P")
+ (funcall fontify-key "f" ?P)
+ (funcall fontify-key "p" ?P)
+ (funcall fontify-key "x" ?P)
+ (funcall fontify-key "a" ?P))
+ (format "\[%s] %s"
+ (funcall fontify-key "q" t)
+ (if first-key "Main menu" "Exit")))))
+ ;; Build prompts for both standard and expert UI.
+ (standard-prompt (unless expertp "Export command: "))
+ (expert-prompt
+ (when expertp
+ (format
+ "Export command (Options: %s%s%s%s) [%s]: "
+ (if (memq 'body options) (funcall fontify-key "b" t) "-")
+ (if (memq 'subtree options) (funcall fontify-key "s" t) "-")
+ (if (memq 'visible options) (funcall fontify-key "v" t) "-")
+ (if (memq 'force options) (funcall fontify-key "f" t) "-")
+ (concat allowed-keys)))))
;; With expert UI, just read key with a fancy prompt. In standard
;; UI, display an intrusive help buffer.
- (if expertp (funcall handle-keypress expert-prompt)
+ (if expertp
+ (org-export-dispatch-action
+ expert-prompt allowed-keys backends options first-key expertp)
(save-window-excursion
(delete-other-windows)
- (with-output-to-temp-buffer "*Org Export/Publishing Help*" (princ help))
+ (with-current-buffer (get-buffer-create "*Org Export/Publishing Help*")
+ (erase-buffer)
+ (save-excursion (insert help)))
(org-fit-window-to-buffer
- (get-buffer-window "*Org Export/Publishing Help*"))
- (funcall handle-keypress standard-prompt)))))
+ (display-buffer "*Org Export/Publishing Help*"))
+ (org-export-dispatch-action
+ standard-prompt allowed-keys backends options first-key expertp)))))
+
+(defun org-export-dispatch-action
+ (prompt allowed-keys backends options first-key expertp)
+ "Read a character from command input and act accordingly.
+
+PROMPT is the displayed prompt, as a string. ALLOWED-KEYS is
+a list of characters available at a given step in the process.
+BACKENDS is a list of menu entries. OPTIONS, FIRST-KEY and
+EXPERTP are the same as defined in `org-export-dispatch-ui',
+which see.
+
+Toggle export options when required. Otherwise, return value is
+a list with action as CAR and a list of interactive export
+options as CDR."
+ (let ((key (let ((k (read-char-exclusive prompt)))
+ ;; Translate "C-a", "C-b"... into "a", "b"... Then take action
+ ;; depending on user's key pressed.
+ (if (< k 27) (+ k 96) k))))
+ (cond
+ ;; Ignore non-standard characters (i.e. "M-a") and
+ ;; undefined associations.
+ ((not (memq key allowed-keys))
+ (org-export-dispatch-ui options first-key expertp))
+ ;; q key at first level aborts export. At second
+ ;; level, cancel first key instead.
+ ((eq key ?q) (if (not first-key) (error "Export aborted")
+ (org-export-dispatch-ui options nil expertp)))
+ ;; Help key: Switch back to standard interface if
+ ;; expert UI was active.
+ ((eq key ??) (org-export-dispatch-ui options first-key nil))
+ ;; Toggle export options.
+ ((memq key '(?1 ?2 ?3 ?4))
+ (org-export-dispatch-ui
+ (let ((option (case key (?1 'body) (?2 'visible) (?3 'subtree)
+ (?4 'force))))
+ (if (memq option options) (remq option options)
+ (cons option options)))
+ first-key expertp))
+ ;; Action selected: Send key and options back to
+ ;; `org-export-dispatch'.
+ ((or first-key
+ (and (eq first-key ?P) (memq key '(?f ?p ?x ?a)))
+ (functionp (nth 2 (assq key backends))))
+ (cons (cond
+ ((not first-key) (nth 2 (assq key backends)))
+ ;; Publishing actions are hard-coded. Send a special
+ ;; signal to `org-export-dispatch'.
+ ((eq first-key ?P)
+ (case key
+ (?f 'publish-current-file)
+ (?p 'publish-current-project)
+ (?x 'publish-choose-project)
+ (?a 'publish-all)))
+ (t (nth 2 (assq key (nth 2 (assq first-key backends))))))
+ options))
+ ;; Otherwise, enter sub-menu.
+ (t (org-export-dispatch-ui options key expertp)))))
(provide 'org-export)