;;; ox-ehtml.el -- export of Org-mode to editable HTML
;; Copyright (C) 2012-2013 Eric Schulte
;; Author: Eric Schulte
;; Keywords: org web-server javascript html
;; This file is not (yet) part of GNU Emacs.
;; However, it is distributed under the same license.
;; GNU Emacs 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, or (at your option)
;; any later version.
;; GNU Emacs 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; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Code:
(require 'ox-html)
(require 'ox-org)
(require 'org-ehtml-util)
(defvar org-ehtml-style
(concat
""))
(defvar org-ehtml-jquery
"https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js")
(defvar org-ehtml-js
(file-contents (expand-file-name "ox-ehtml.js" org-ehtml-base)))
(defun org-ehtml-scripts ()
(concat
""
"\n"))
(defvar org-ehtml-wrap-template
(concat
"%html-text
"
""
"%org-text
"))
(defcustom org-ehtml-everything-editable nil
"Set to a true value to make everything exported by org-ehtml editable."
:group 'org-export-ehtml
:type 'boolean)
(defcustom org-ehtml-editable-headlines nil
"Set to a true value to make headines exported by org-ehtml editable."
:group 'org-export-ehtml
:type 'boolean)
(defcustom org-ehtml-editable-types
'(paragraph plain-list table verbatim quote-block verse-block)
"Types of elements whose children should not be editable."
:group 'org-export-ehtml
:type '(repeat symbol))
(defvar org-ehtml-headline nil
"Used to pass headline from `org-ehtml-format-headline-wrap' to
`org-ehtml-format-headine-function'.")
(defvar org-ehtml-info nil
"Used to pass info from `org-ehtml-format-headline-wrap' to
`org-ehtml-format-headine-function'.")
(defun org-ehtml-format-headine-function (&rest args)
(let*
((headline org-ehtml-headline)
(info org-ehtml-info)
(html (apply #'org-html-format-headline args))
(begin (number-to-string (org-element-property :begin headline)))
(contents-begin (org-element-property :contents-begin headline))
(end (number-to-string (if contents-begin
contents-begin
(org-element-property :end headline))))
(org (org-org-headline headline "" info)))
(org-fill-template org-ehtml-wrap-template
`(("html-text" . ,html)
("org-text" . ,org)
("begin" . ,begin)
("end" . ,end)))))
(defun org-ehtml-format-headline-wrap (headline contents info)
(if org-ehtml-editable-headlines
(let ((org-html-format-headline-function
#'org-ehtml-format-headine-function)
(org-ehtml-headline headline)
(org-ehtml-info info))
(org-html-headline headline contents info))
(org-html-headline headline contents info)))
(defun org-ehtml-editable-p (element info)
(let ((parent (org-export-get-parent element)))
(cond ((eq (car element) 'headline) org-ehtml-editable-headlines)
((eq (car parent) 'headline)
(or org-ehtml-everything-editable
(member "EDITABLE" (org-export-get-tags parent info))))
((eq (car parent) 'org-data)
(or org-ehtml-everything-editable
(cl-some
(lambda (keyword)
(let ((key (plist-get (cadr keyword) :key))
(val (plist-get (cadr keyword) :value)))
(and (string= "PROPERTY" key)
(string-match "editable \\(.+\\)" val)
(car (read-from-string (match-string 1 val))))))
(cddr (cl-caddr parent)))))
((member (car parent) org-ehtml-editable-types) nil)
(t (org-ehtml-editable-p parent info)))))
(defmacro def-ehtml-wrap (html-function)
"Defines and returns an ehtml-wrapped version of HTML-FUNCTION."
(let ((fname (intern (replace-regexp-in-string
"org-html-" "org-ehtml-"
(symbol-name html-function)))))
`(defun ,fname (element contents info)
,(format "Editable html wrapper around `%s'." html-function)
(let* ((original-contents (cl-copy-seq contents))
(original-info (cl-copy-seq info))
(html-text (,html-function element contents info))
(org-text (or (org-element-interpret-data element)
original-contents
(error "no org-text found for %s" (car element)))))
(if (org-ehtml-editable-p element info)
(org-fill-template org-ehtml-wrap-template
`(("html-text" . ,html-text)
("org-text" . ,org-text)
("begin" . ,(number-to-string
(plist-get (cadr element) :begin)))
("end" . ,(number-to-string
(plist-get (cadr element) :end)))))
html-text)))))
(org-export-define-derived-backend 'ehtml 'html
:menu-entry
'(?e "Export to Editable HTML"
((?H "To Temporary buffer" org-ehtml-export-as-html)
(?h "To file" org-ehtml-export-to-html)
(?o "To file and open"
(lambda (a s v b)
(if a (org-ehtml-export-to-html t s v b)
(org-open-file (org-ehtml-export-to-html nil s v b)))))))
:options-alist
'((:ehtml-everything-editable "HTML_EVERYTHING_EDITABLE" nil
org-ehtml-everything-editable)
(:ehtml-editable-headlines "HTML_EDITABLE_HEADLINES" nil
org-ehtml-editable-headlines)
(:ehtml-editable-types nil nil org-ehtml-editable-types))
:translate-alist
`((headline . org-ehtml-format-headline-wrap)
(paragraph . ,(def-ehtml-wrap org-html-paragraph))
(plain-list . ,(def-ehtml-wrap org-html-plain-list))
(table . ,(def-ehtml-wrap org-html-table))
(verbatim . ,(def-ehtml-wrap org-html-verbatim))
(quote-block . ,(def-ehtml-wrap org-html-quote-block))
;; (src-block . ,(def-ehtml-wrap org-html-src-block))
(verse-block . ,(def-ehtml-wrap org-html-verse-block))))
(defun org-ehtml-export-as-html
(&optional async subtreep visible-only body-only ext-plist)
"Export current buffer to an HTML buffer.
Based on `org-html-export-as-html'.'"
(interactive)
(org-export-to-buffer 'ehtml "*Org eHTML Export*"
async subtreep visible-only body-only ext-plist (lambda () (nxml-mode))))
(defun org-ehtml-export-to-html
(&optional async subtreep visible-only body-only ext-plist)
"Export current buffer to an editable HTML file.
Based on `org-html-export-to-html'.'"
(interactive)
(let* ((extension (concat "." org-html-extension))
(file (org-export-output-file-name extension subtreep))
(org-export-coding-system org-html-coding-system)
;; custom headers
(org-html-style-default (concat org-html-style-default "\n"
org-ehtml-style))
(org-html-scripts (concat org-html-scripts "\n"
(org-ehtml-scripts))))
(org-export-to-file 'ehtml file
async subtreep visible-only body-only ext-plist)))
(defun org-ehtml-export-file (file)
"Export FILE's contents to editable HTML."
(save-window-excursion
(find-file file)
(org-ehtml-export-to-html)))
(defun org-ehtml-cached (file)
"Export FILE to editable HTML if no previous export exists.
If a previous HTML export of FILE exists but is older than FILE
re-export."
(cl-flet ((age (f)
(float-time
(time-subtract (current-time)
(nth 5 (or (file-attributes (file-truename f))
(file-attributes f)))))))
(let* ((dir (file-name-directory file))
(base (file-name-sans-extension file))
(html (concat base ".html"))
(org (concat base ".org")))
(if (and (file-exists-p org)
(or (not (file-exists-p html)) (> (age html) (age org))))
(expand-file-name (org-ehtml-export-file org) dir)
html))))
(provide 'ox-ehtml)
;;; ox-ehtml.el ends here