├── .gitignore ├── README.md ├── changelog └── changelog.org ├── documents └── tips.org ├── org2web-config.el ├── org2web-devtools.el ├── org2web-el2org.el ├── org2web-export.el ├── org2web-resource.el ├── org2web-template.el ├── org2web-util.el ├── org2web-vars.el ├── org2web-webserver.el ├── org2web.el ├── themes ├── default │ ├── resources │ │ ├── css │ │ │ ├── main.css │ │ │ └── prettify.css │ │ ├── img │ │ │ └── search.png │ │ └── js │ │ │ └── main.js │ └── templates │ │ ├── about.mustache │ │ ├── category-index.mustache │ │ ├── container.mustache │ │ ├── footer.mustache │ │ ├── header.mustache │ │ ├── index.mustache │ │ ├── nav.mustache │ │ ├── post.mustache │ │ ├── summary-index.mustache │ │ └── summary.mustache ├── killjs │ └── templates │ │ ├── footer.mustache │ │ └── header.mustache └── worg │ ├── resources │ ├── css │ │ └── main.css │ └── img │ │ └── horse.jpg │ └── templates │ └── nav.mustache └── uploaders └── common ├── git-simple.mustache ├── git.mustache └── rclone.mustache /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.elc 3 | /index.org 4 | *.html 5 | /org2web.org 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - [org2web README](#org697befd) 2 | - [Installation](#orgdc8ac05) 3 | - [Configuration](#org317e73f) 4 | - [Publication](#orgfce7a55) 5 | - [Dependencies](#orgbe671e3) 6 | - [Known issues](#org60e1ee1) 7 | 8 | 9 | 10 | 11 | # org2web README 12 | 13 | org2web is the new name of org-webpage, the reason of renaming org-webpage to org2web is: 14 | 15 | org2web is a static site generator based on [org-mode](http://orgmode.org/), which code derived from Kelvin H's [org-page](https://github.com/kelvinh/org-page). 16 | 17 | The main differents of two projects are as follow: 18 | 19 | 1. org2web's core **don't hard code git**, its process is like below: 20 | 21 | 22 | [ Org files in repository] [ Website project configure ] 23 | 24 | | | 25 | < Export > < Generate > 26 | | | 27 | 28 | [ HTML files ] [ Uploader ] <- ( Uploader is a bash script ) 29 | 30 | | | 31 | | | 32 | +-------------+-------------+ 33 | | 34 | | 35 | < Run Uploader > <- ( For example: git uploader, rclone uploader or others ) 36 | | 37 | | 38 | 39 | [ REMOTE ] 40 | 41 | 2. org2web's default config is \`org-publish-project-alist' style alist, which can manage multi-site configs in an emacs session easily. 42 | 3. org-website find theme-files from a **themes-list** in sequence and same theme-file first found will be used. User can set **fallback theme** with the help of this feature. 43 | 4. org-website include a tiny emacs web server, which can be used to test publish. 44 | 5. org-website can use other uploaders to upload website, for example: rclone. 45 | 6. … 46 | 47 | 48 | 49 | 50 | ## Installation 51 | 52 | org2web is now available from the famous emacs package repo [melpa](http://melpa.milkbox.net/) so the recommended way is to install it through emacs package management system. For more info about installation, please see **tips.org** in the "doc" folder. 53 | 54 | 55 | 56 | 57 | ## Configuration 58 | 59 | org2web use variable \`org2web-projects' to store all projects's configures, user can add a project with the help of \`add-to-list' function, but the easiest way is using \`org2web-add-project' function. 60 | 61 | The follow code is [my website](http://tumashu.github.com)'s [config](https://github.com/tumashu/tumashu.github.com/blob/source/eh-website.el), you can adjust and paste it to your `.emacs` file: 62 | 63 | (add-to-list 'load-path "path/to/org2web") ; Only needed if you install org2web manually 64 | 65 | (require 'org2web) 66 | 67 | (org2web-add-project 68 | '("tumashu.github.com" 69 | :repository-directory "~/project/emacs-packages/tumashu.github.com" 70 | :remote (git "https://github.com/tumashu/tumashu.github.com.git" "master") 71 | ;; you can use `rclone` with `:remote (rclone "remote-name" "/remote/path/location")` instead. 72 | :site-domain "http://tumashu.github.com/" 73 | :site-main-title "Tumashu 的个人小站" 74 | :site-sub-title "(九天十地,太上忘情!!!)" 75 | :theme (worg) 76 | :source-browse-url ("Github" "https://github.com/tumashu/tumashu.github.com") 77 | :personal-avatar "/media/img/horse.jpg" 78 | :personal-duoshuo-shortname "tumashu-website" 79 | :web-server-port 7654)) 80 | 81 | [pyim](https://github.com/tumashu/pyim) 's org2web [config](https://github.com/tumashu/pyim/blob/master/pyim-devtools.el) is a more complex example. 82 | 83 | You can find more config options and theirs default values by commands: 84 | 85 | C-h v org2web-projects 86 | C-h v org2web-config-fallback 87 | 88 | 89 | 90 | 91 | ## Publication 92 | 93 | M-x org2web-publish 94 | 95 | 96 | 97 | 98 | ## Dependencies 99 | 100 | 1. [emacs](http://www.gnu.org/software/emacs/): this is an "of-course" dependency 101 | 2. [org mode](http://orgmode.org/): v8.0 is required, please use `M-x org-version ` to make sure you org mode version is not less than 8.0 102 | 3. [bash](http://www.gnu.org/software/bash/): the GNU Project's shell 103 | 4. [git](http://git-scm.com): a free and open source version control system 104 | 5. [rclone](http://rclone.org/downloads/): support to other remote locations, see rclone's overview for more information. (Optional) 105 | 6. [mustache.el](https://github.com/Wilfred/mustache.el): a mustache templating library for Emacs 106 | 7. [htmlize.el](http://fly.srk.fer.hr/~hniksic/emacs/htmlize.el.cgi): a library for syntax highlighting (usually this library is shipped with emacs) 107 | 8. [dash.el](https://github.com/magnars/dash.el): a modern list library for Emacs 108 | 9. [ht.el](https://github.com/Wilfred/ht.el): a modern hash-table library for Emacs 109 | 10. [simple-httpd](https://github.com/skeeto/emacs-web-server): Extensible Emacs HTTP 1.1 server 110 | 111 | 112 | 113 | 114 | ## Known issues 115 | 116 | 1. Currently the deletion change handler has not been implemented so if you deleted some org sources, you may have to manually delete corresponding generated html files. 117 | 2. URI path change detection is not available. That is, if you make a post with the URI "/blog/2013/03/25/the-old-post-name" and then change this value in your org source, org2web would be unable to detect that this has happened. it will only publish a new html file for you so you need to delete the old html file related to the old URI manually. 118 | 119 | 120 | Converted from org2web.el by [el2org](https://github.com/tumashu/el2org) . -------------------------------------------------------------------------------- /changelog/changelog.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Change Log 2 | #+AUTHOR: Feng Shu 3 | #+EMAIL: tumashu@163.com 4 | #+DATE: 2015-04-02 四 5 | #+KEYWORDS: changelog, 更新记录 6 | #+TAGS: changelog, 更新记录 7 | #+LANGUAGE: zh-CN 8 | #+OPTIONS: H:3 num:nil toc:nil \n:nil ::t |:t ^:nil -:nil f:t *:t <:t 9 | 10 | 11 | * v0.1 12 | 13 | - Initial version, fork from org-page. 14 | - Use org-publish style config. 15 | - Increment Theme suport. 16 | -------------------------------------------------------------------------------- /documents/tips.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Tips of org2web 2 | #+AUTHOR: Feng Shu 3 | #+EMAIL: tumashu@163.com 4 | #+DATE: 2015-04-01 5 | 6 | 7 | * How to install org2web in manual way 8 | 9 | To install org2web manually you should first install all 10 | dependencies listed in *README.org*. Once this has been completed, 11 | clone the repo: 12 | 13 | #+BEGIN_EXAMPLE 14 | git clone https://github.com/tumashu/org2web.git 15 | #+END_EXAMPLE 16 | 17 | After that, please remember to add this location to your emacs' 18 | =load-path= variable so that emacs can find the package. 19 | 20 | #+BEGIN_SRC emacs-lisp 21 | (add-to-list 'load-path "/path/to/org2web") 22 | (require 'org2web) 23 | #+END_SRC 24 | 25 | NOTE: This is NOT recommended way for emacs beginner. 26 | 27 | * How to install org2web Through package management system 28 | 29 | 1. Setting melpa repository, see: http://melpa.org/#/getting-started 30 | 2. Run the following command 31 | #+BEGIN_EXAMPLE 32 | M-x package-install RET org-website RET 33 | #+END_EXAMPLE 34 | 3. Add the following to your =~/.emacs= file: 35 | #+BEGIN_EXAMPLE 36 | (require 'org2web) 37 | #+END_EXAMPLE 38 | 39 | * How to quickly add a new post 40 | #+BEGIN_EXAMPLE 41 | M-x org2web-new-post 42 | #+END_EXAMPLE 43 | 44 | This command will ask you the follow question: 45 | 1. Which project do you want post? 46 | 2. Category? 47 | 3. Filename? 48 | 49 | * How to quickly insert org-website post template 50 | 51 | #+BEGIN_EXAMPLE 52 | M-x org2web-insert-options-template 53 | #+END_EXAMPLE 54 | 55 | * How to configure the default slogan 56 | Add the follow two lines to you config alist. 57 | 58 | #+BEGIN_EXAMPLE 59 | :site-main-title "your main slogan" 60 | :site-sub-title "your sub slogan" 61 | #+END_EXAMPLE 62 | 63 | * How to add an avatar to the page? 64 | Add the follow line to you config alist. 65 | 66 | #+BEGIN_EXAMPLE 67 | :personal-avatar "URL to an image" 68 | #+END_EXAMPLE 69 | 70 | Image URL example: 71 | 1. =http:/XXXXX.com/XXXX.jpg= 72 | 2. /media/img/XXXXX.jpg 73 | 3. /assets/XXX/XXX.jpg 74 | 75 | * How to set html git remote? 76 | Add the follow line to you config alist. 77 | 78 | #+BEGIN_EXAMPLE 79 | :remote (git "" "") 80 | #+END_EXAMPLE 81 | 82 | * How to add a github link 83 | Add the follow line to you config alist. 84 | 85 | #+BEGIN_EXAMPLE 86 | :source-browse-url ("GitHub" "https://github.io//") 87 | #+END_EXAMPLE 88 | 89 | * How to do site traffic analytics with Google Analytics? 90 | 91 | Add the follow line to you config alist. 92 | 93 | #+BEGIN_EXAMPLE 94 | :personal-google-analytics-id "your google analytics id" 95 | #+END_EXAMPLE 96 | 97 | * How to disable commenting for posts under certain categories? 98 | 99 | #+BEGIN_SRC emacs-lisp 100 | (setq org2web-category-config-alist 101 | (cons '("photography" ;; category name goes here 102 | :show-comment nil) 103 | org2web-category-config-alist)) 104 | #+END_SRC 105 | 106 | Other config items: 107 | 108 | 1. =:show-meta=: show post meta info at the bottom of post? 109 | 2. =:uri-generator=: the function used to generate uri for posts 110 | under that category (however, it is not recommended to 111 | customize except you are an expert) 112 | 3. =:uri-template=: the template used to generate uri 113 | 4. =:sort-by=: how to sort posts on category index page, by 114 | 5. =:date= or by =:mod-date= (:mod-date is last modification date)? 115 | 6. =:category-index=: generate an index page for this category? 116 | -------------------------------------------------------------------------------- /org2web-config.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-config.el --- Functions dealing with org2web configure 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | 5 | ;; Author: Feng Shu 6 | ;; Keywords: convenience 7 | ;; Homepage: https://github.com/tumashu/org2web 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see . 21 | 22 | ;;; Commentary: 23 | 24 | ;; org2web-config.el contains functions used to deal with org2web configure. 25 | 26 | ;;; Code: 27 | 28 | (require 'org2web-vars) 29 | 30 | (defun org2web-get-config-option (option) 31 | "The function used to read org2web config" 32 | (when (functionp org2web-get-config-option-function) 33 | (let ((output (funcall org2web-get-config-option-function option))) 34 | ;; if "output" is a form which like (:eval myform), 35 | ;; eval myform and return the result, otherwise 36 | ;; return "output". 37 | (if (and (listp output) 38 | (eq (car output) :eval)) 39 | (eval `(progn ,@(cdr output))) 40 | output)))) 41 | 42 | (defun org2web-get-config-option-from-alist (option) 43 | "The default org2web config read function, 44 | which can read `option' from `org2web-projects' 45 | if `option' is not found, get fallback value from 46 | `org2web-config-fallback'." 47 | (let ((project-plist (cdr (assoc org2web-current-project 48 | org2web-projects)))) 49 | (if (plist-member project-plist option) 50 | (plist-get project-plist option) 51 | (plist-get org2web-config-fallback option)))) 52 | 53 | (defun org2web-get-repository-directory () 54 | "The function, which can return repository directory string." 55 | (let ((dir (org2web-get-config-option :repository-directory))) 56 | (when dir 57 | (file-name-as-directory 58 | (expand-file-name dir))))) 59 | 60 | (defun org2web-get-publishing-directory () 61 | "The function, which can return publishing directory string." 62 | (let ((dir (org2web-get-config-option :publishing-directory))) 63 | (when dir 64 | (file-name-as-directory 65 | (expand-file-name dir))))) 66 | 67 | (defun org2web-get-site-domain (&optional old-site-domain) 68 | "The function, which can return site-domain string." 69 | (let ((site-domain (if old-site-domain 70 | (org2web-get-config-option :old-site-domain) 71 | (org2web-get-config-option :site-domain)))) 72 | (when site-domain 73 | (if (or (string-prefix-p "http://" site-domain) 74 | (string-prefix-p "https://" site-domain)) 75 | (directory-file-name 76 | (file-name-as-directory site-domain)) 77 | (directory-file-name 78 | (file-name-as-directory 79 | (concat "http://" site-domain))))))) 80 | 81 | (defun org2web-get-theme-dirs (&optional root-dir theme type) 82 | "The function ,return org2web theme type paths list. 83 | 84 | org2web organizes its themes by directory: 85 | 86 | | Directory | Argument | Value | 87 | +---------------------+-------------+------------------------+ 88 | | /path/to/directory | | \"/path/to/directory\" | 89 | | \--mdo | | 'mdo | 90 | | |-- templates | | 'templates | 91 | | \- resources | | 'resources | 92 | 93 | `root-dir' and `theme' can be lists, for example: 94 | 95 | `(\"path/to/dir1\" \"path/to/dir2\" \"path/to/dir3\")' 96 | `(theme1 theme2 theme3)' 97 | 98 | At this time, `org2web-get-theme-dirs' will find *all possible* 99 | directorys by permutation way and return a list with 100 | multi path." 101 | (let* ((themes (delete-dups 102 | (if theme 103 | (list theme) 104 | `(,@(org2web-get-config-option :theme) default)))) 105 | (theme-root-dirs (delete-dups 106 | (if root-dir 107 | (list root-dir) 108 | `(,@(org2web-get-config-option :theme-root-directory) 109 | ,(concat (org2web-get-repository-directory) "themes/") 110 | ,(concat org2web-load-directory "themes/"))))) 111 | theme-dir theme-dirs) 112 | (dolist (theme themes) 113 | (dolist (root-dir theme-root-dirs) 114 | (setq theme-dir 115 | (file-name-as-directory 116 | (expand-file-name 117 | (format "%s/%s" (symbol-name theme) 118 | (if type (symbol-name type) "")) 119 | root-dir))) 120 | (when (file-directory-p theme-dir) 121 | (push theme-dir theme-dirs)))) 122 | (reverse theme-dirs))) 123 | 124 | (defun org2web-get-html-creator-string () 125 | "The function, which can return creator string." 126 | (or (org2web-get-config-option :html-creator-string) "")) 127 | 128 | (defun org2web-get-category-setting (category) 129 | "The function , which can return category config of `category'" 130 | (or (assoc category org2web-category-config-alist) 131 | `(,category 132 | :show-meta t 133 | :show-comment t 134 | :uri-generator org2web-generate-uri 135 | :uri-template ,(format "/%s/%%t/" category) 136 | :sort-by :date 137 | :category-index t))) 138 | 139 | (provide 'org2web-config) 140 | 141 | ;;; org2web-config.el ends here 142 | -------------------------------------------------------------------------------- /org2web-devtools.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-devtools.el --- Functions used to develop org2web 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | 5 | ;; Author: Feng Shu 6 | ;; Keywords: convenience 7 | ;; Homepage: https://github.com/tumashu/org2web 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see . 21 | 22 | ;;; Commentary: 23 | 24 | ;; org2web-config.el contains functions used to develop org2web. 25 | 26 | ;;; Code: 27 | (require 'org2web) 28 | 29 | (defvar org2web-devtools-repository-directory 30 | "~/project/emacs-packages/org2web/") 31 | 32 | (org2web-add-project 33 | '("org2web" 34 | :repository-directory (:eval org2web-devtools-repository-directory) 35 | :remote (git "https://github.com/tumashu/org2web.git" "gh-pages") 36 | :site-domain "http://tumashu.github.com/org2web" 37 | :site-main-title "org2web" 38 | :site-sub-title "(Static site senerator based on org mode)" 39 | :default-category "documents" 40 | :theme (worg killjs) 41 | :force-absolute-url t 42 | :source-browse-url ("GitHub" "https://github.com/tumashu/org2web") 43 | :personal-avatar "/media/img/horse.jpg" 44 | :personal-duoshuo-shortname "tumashu-website" 45 | :preparation-function org2web-el2org-preparation-function 46 | :org-export-function org2web-el2org-org-export-function 47 | :el2org-doc-sources ("org2web.el") 48 | :el2org-readme-sources ("org2web.el") 49 | :el2org-index-sources ("org2web.el") 50 | :web-server-port 6789)) 51 | 52 | (provide 'org2web-devtools) 53 | 54 | ;;; org2web-devtools.el ends here 55 | -------------------------------------------------------------------------------- /org2web-el2org.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-el2org.el --- el2org support for org2web 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | 5 | ;; Author: Feng Shu 6 | ;; Keywords: convenience 7 | ;; Homepage: https://github.com/tumashu/org2web 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see . 21 | 22 | ;;; Commentary: 23 | 24 | ;; * 说明文档 :README: 25 | 26 | ;; #+BEGIN_EXAMPLE 27 | ;; (require 'org2web-el2org) 28 | ;; #+END_EXAMPLE 29 | 30 | ;;; Code: 31 | 32 | ;; * 代码 :code: 33 | 34 | ;; ** Require 35 | (require 'el2org) 36 | 37 | (defun org2web-el2org-generate-readme (&optional project-name) 38 | (interactive) 39 | (org2web-select-project 40 | "Which project do you want to generate README.md? " project-name) 41 | (let* ((repo-dir (org2web-get-repository-repo-directory)) 42 | (el-file (concat 43 | (file-name-as-repo-directory repo-dir) 44 | (car (org2web-get-config-option :el2org-readme-sources)))) 45 | (readme-file (concat (file-name-as-repo-directory repo-dir) "README.md")) 46 | (tags (org2web-get-config-option :el2org-readme-tags))) 47 | (if (featurep 'ox-gfm) 48 | (el2org-generate-file el-file tags 'gfm readme-file) 49 | (el2org-generate-file el-file tags 'md readme-file)))) 50 | 51 | (defun org2web-el2org-generate-index (&optional project-name) 52 | (interactive) 53 | (org2web-select-project 54 | "Which project do you want to generate index.org? " project-name) 55 | (let* ((repo-dir (org2web-get-repository-repo-directory)) 56 | (el-file (concat 57 | (file-name-as-repo-directory repo-dir) 58 | (car (org2web-get-config-option :el2org-readme-sources)))) 59 | (index-file (concat (file-name-as-repo-directory repo-dir) "index.org")) 60 | (tags (org2web-get-config-option :el2org-readme-tags))) 61 | (el2org-generate-file el-file tags 'org index-file))) 62 | 63 | ;; ** org2web 导出函数(支持 el2org) 64 | 65 | (defun org2web-el2org-org-export-function () 66 | "A function with can export org file to html." 67 | (let ((org-export-headline-levels 7) 68 | (indent-tabs-mode nil) 69 | (tab-width 4)) 70 | (org-export-as 'html nil nil t nil))) 71 | 72 | (defun org2web-el2org-preparation-function () 73 | "Generate org files by el2org." 74 | ;; Orgify elisp files 75 | (let* ((repo-dir (org2web-get-repository-directory)) 76 | (tags (org2web-get-config-option :el2org-doc-tags)) 77 | (regexp-list (org2web-get-config-option :el2org-doc-sources)) 78 | (files (when regexp-list 79 | (org2web-select-matched-items 80 | (org2web-directory-files-recursively repo-dir "\\.el$") 81 | regexp-list)))) 82 | (when files 83 | (mapc 84 | #'(lambda (file) 85 | (when (file-exists-p file) 86 | (let ((org-file (concat (file-name-sans-extension file) ".org"))) 87 | (el2org-generate-file file tags 'org org-file)))) 88 | files))) 89 | 90 | ;; Generate README.md if necessary 91 | (let* ((repo-dir (org2web-get-repository-directory)) 92 | (filename (car (org2web-get-config-option :el2org-readme-sources))) 93 | (file (when filename 94 | (concat (file-name-as-directory repo-dir) filename))) 95 | (readme-file (concat (file-name-as-directory repo-dir) "README.md")) 96 | (tags (org2web-get-config-option :el2org-readme-tags))) 97 | (if (featurep 'ox-gfm) 98 | (el2org-generate-file file tags 'gfm readme-file) 99 | (el2org-generate-file file tags 'md readme-file))) 100 | 101 | ;; Generate index.org if necessary 102 | (let* ((repo-dir (org2web-get-repository-directory)) 103 | (filename (car (org2web-get-config-option :el2org-index-sources))) 104 | (file (when filename 105 | (concat (file-name-as-directory repo-dir) filename))) 106 | (index-file (concat (file-name-as-directory repo-dir) "index.org")) 107 | (tags (org2web-get-config-option :el2org-index-tags))) 108 | (el2org-generate-file file tags 'org index-file))) 109 | 110 | (define-obsolete-function-alias 'owp/el2org-preparation-function 'org2web-el2org-preparation-function "0.1") 111 | (define-obsolete-function-alias 'owp/el2org-org-export-function 'org2web-el2org-org-export-function "0.1") 112 | 113 | ;; * Footer 114 | 115 | (provide 'org2web-el2org) 116 | 117 | ;; Local Variables: 118 | ;; no-byte-compile: t 119 | ;; End: 120 | 121 | ;;; org2web-el2org.el ends here 122 | -------------------------------------------------------------------------------- /org2web-export.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-export.el --- Publication related functions required by org2web 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | ;; 2012, 2013, 2014, 2015 Kelvin Hu 5 | 6 | ;; Author: Feng Shu 7 | ;; Kelvin Hu 8 | ;; Keywords: convenience 9 | ;; Homepage: https://github.com/tumashu/org2web 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; org source publication related functions 27 | 28 | ;;; Code: 29 | 30 | (require 'format-spec) 31 | (require 'ox) 32 | (require 'ht) 33 | (require 'dash) 34 | (require 'org2web-util) 35 | (require 'org2web-vars) 36 | (require 'org2web-config) 37 | (require 'org2web-template) 38 | (require 'cl-lib) 39 | 40 | 41 | (defun org2web-publish-changes (all-files changed-files pub-root-dir) 42 | "This function is for: 43 | 1. publish changed org files to html 44 | 3. update index pages 45 | 4. regenerate tag pages 46 | `all-files' contain paths of org files, `changed-files' contain org files 47 | to be updated. 48 | 49 | This function don't handle deleted org-files." 50 | (let* ((repo-dir (org2web-get-repository-directory)) 51 | visiting uri-alist file-attr-list) 52 | (when changed-files 53 | (dolist (org-file all-files) 54 | (with-temp-buffer 55 | (let (attr-cell) 56 | (insert-file-contents org-file) 57 | (org-mode) 58 | (setq attr-cell (org2web-get-org-file-options 59 | org-file pub-root-dir)) 60 | (push attr-cell file-attr-list) 61 | (push (cons (file-relative-name org-file repo-dir) 62 | (plist-get attr-cell :relative-uri)) 63 | uri-alist)))) 64 | ;; (princ file-attr-list) 65 | ;; (princ uri-alist) 66 | (dolist (org-file all-files) 67 | (when (member org-file changed-files) 68 | (with-temp-buffer 69 | (let (attr-cell exported-content) 70 | (insert-file-contents org-file) 71 | (org-mode) 72 | ;; Deal with file links, which are likes: 73 | ;; 1. [[file:test1.org][test1]] 74 | ;; 2. [[file:./test2.org][test2]] 75 | (mapc #'(lambda (file-link) 76 | (goto-char (point-min)) 77 | (while (re-search-forward 78 | (format "\\(file:%s\\)\\|\\(file:./%s\\)" 79 | (car file-link) 80 | (car file-link)) nil t) 81 | (replace-match 82 | (format "file:%s/index.html" 83 | (cdr file-link)) nil t))) 84 | uri-alist) 85 | (setq attr-cell (org2web-get-org-file-options 86 | org-file 87 | pub-root-dir)) 88 | (setq exported-content (org2web-get-org-file-export-content 89 | org-file pub-root-dir attr-cell)) 90 | (org2web-publish-modified-file exported-content 91 | (plist-get attr-cell :pub-dir)))))) 92 | (unless (member 93 | (expand-file-name "index.org" repo-dir) 94 | all-files) 95 | (org2web-generate-default-index file-attr-list pub-root-dir)) 96 | (when (and (org2web-get-config-option :about) 97 | (not (member 98 | (expand-file-name "about.org" repo-dir) 99 | all-files))) 100 | (org2web-generate-default-about pub-root-dir)) 101 | (org2web-update-category-index file-attr-list pub-root-dir) 102 | (when (org2web-get-config-option :rss) 103 | (org2web-update-rss file-attr-list pub-root-dir)) 104 | (mapc 105 | #'(lambda (name) 106 | (org2web-update-summary file-attr-list pub-root-dir name)) 107 | (mapcar #'car (org2web-get-config-option :summary)))))) 108 | 109 | (defun org2web-get-org-file-options (org-file pub-root-dir) 110 | "Retrieve all needed options for org file opened in current buffer. 111 | PUB-ROOT-DIR is the root directory of published files." 112 | (let* ((repo-dir (org2web-get-repository-directory)) 113 | (attr-plist `(:title ,(funcall (org2web-get-config-option :get-title-function) org-file) 114 | :date ,(org2web-fix-timestamp-string 115 | (or (org2web-read-org-option "DATE") 116 | (format-time-string "%Y-%m-%d"))) 117 | :mod-date ,(if (not org-file) 118 | (format-time-string "%Y-%m-%d") 119 | (format-time-string 120 | "%Y-%m-%d" 121 | (nth 5 (file-attributes org-file)))) 122 | :description ,(or (org2web-read-org-option "DESCRIPTION") 123 | "No Description") 124 | :thumb ,(org2web-read-org-option "THUMBNAIL"))) 125 | tags authors category cat-config) 126 | (setq tags (org2web-read-org-option "TAGS")) 127 | (when tags 128 | (plist-put 129 | attr-plist :tags (delete "" (mapcar 'org2web-trim-string 130 | (split-string tags "[:,]+" t))))) 131 | (setq authors (org2web-read-org-option "AUTHOR")) 132 | (when authors 133 | (plist-put 134 | attr-plist :authors (delete "" (mapcar 'org2web-trim-string 135 | (split-string authors "[:,]+" t))))) 136 | (setq category (org2web-get-category org-file)) 137 | (plist-put attr-plist :category category) 138 | (setq cat-config (cdr (or (assoc category org2web-category-config-alist) 139 | (org2web-get-category-setting 140 | (org2web-get-config-option :default-category))))) 141 | (plist-put attr-plist :uri (funcall (plist-get cat-config :uri-generator) 142 | (plist-get cat-config :uri-template) 143 | (plist-get attr-plist :date) 144 | (plist-get attr-plist :title))) 145 | (plist-put attr-plist :relative-uri (replace-regexp-in-string 146 | "\\`/" "" 147 | (plist-get attr-plist :uri))) 148 | (plist-put attr-plist :pub-dir (file-name-as-directory 149 | (concat 150 | (file-name-as-directory pub-root-dir) 151 | (plist-get attr-plist :relative-uri)))) 152 | 153 | attr-plist)) 154 | 155 | 156 | (defun org2web-get-org-file-export-content (org-file pub-root-dir attr-plist) 157 | "Export org file to html and return html content." 158 | (let* ((repo-dir (org2web-get-repository-directory)) 159 | assets-dir post-content 160 | asset-path asset-abs-path pub-abs-path converted-path 161 | component-table tags authors category cat-config) 162 | ;; (princ attr-plist) 163 | (setq post-content (org2web-render-content nil nil org-file)) 164 | (setq assets-dir (file-name-as-directory 165 | (concat (file-name-as-directory pub-root-dir) 166 | "assets/" 167 | (plist-get attr-plist :relative-uri)))) 168 | (with-temp-buffer 169 | (insert post-content) 170 | (goto-char (point-min)) 171 | (while (re-search-forward 172 | ;;; TODO: not only links need to convert, but also inline 173 | ;;; images, may add others later 174 | ;; "]+href=\"\\([^\"]+\\)\"[^>]*>\\([^<]*\\)" nil t) 175 | "<[a-zA-Z]+[^/>]+\\(src\\|href\\)=\"\\([^\"]+\\)\"[^>]*>" nil t) 176 | (setq asset-path (match-string 2)) 177 | (when (not (or (string-prefix-p "http://" asset-path) 178 | (string-prefix-p "https://" asset-path) 179 | (string-prefix-p "mailto:" asset-path) 180 | (string-prefix-p "ftp://" asset-path) 181 | (string-prefix-p "#" asset-path) 182 | ;; TODO add more here 183 | )) 184 | (setq asset-abs-path 185 | (expand-file-name asset-path (file-name-directory org-file))) 186 | (if (not (file-exists-p asset-abs-path)) 187 | (message (concat "[WARN] File %s in hyper link does not exist, " 188 | "org file: %s.") 189 | asset-path org-file) 190 | (unless (file-directory-p assets-dir) 191 | (mkdir assets-dir t)) 192 | (copy-file asset-abs-path assets-dir t t t t) 193 | (setq pub-abs-path (concat assets-dir 194 | (file-name-nondirectory asset-path))) 195 | (unless (string-prefix-p pub-root-dir pub-abs-path) 196 | (message (concat "[WARN] The publication root directory %s is not an " 197 | "ancestor directory of assets directory %s.") 198 | pub-root-dir assets-dir)) 199 | (setq converted-path 200 | (concat "/" (file-relative-name pub-abs-path pub-root-dir))) 201 | (setq post-content 202 | (replace-regexp-in-string 203 | (regexp-quote asset-path) converted-path post-content)))))) 204 | (setq component-table (ht ("header" (org2web-render-header nil org-file)) 205 | ("nav" (org2web-render-navigation-bar nil org-file)) 206 | ("content" post-content) 207 | ("footer" (org2web-render-footer nil org-file)))) 208 | component-table)) 209 | 210 | (defun org2web-read-org-option (option) 211 | "Read option value of org file opened in current buffer. 212 | e.g: 213 | #+TITLE: this is title 214 | will return \"this is title\" if OPTION is \"TITLE\"" 215 | (let ((match-regexp (org-make-options-regexp `(,option)))) 216 | (save-excursion 217 | (goto-char (point-min)) 218 | (when (re-search-forward match-regexp nil t) 219 | (match-string-no-properties 2 nil))))) 220 | 221 | (defun org2web-generate-uri (default-uri-template creation-date title) 222 | "Generate URI of org file opened in current buffer. It will be firstly created 223 | by #+URI option, if it is nil, DEFAULT-URI-TEMPLATE will be used to generate the 224 | uri. If CREATION-DATE is nil, current date will be used. The uri template option 225 | can contain following parameters: 226 | %y: year of creation date 227 | %m: month of creation date 228 | %d: day of creation date 229 | %t: title of current buffer" 230 | (let ((uri-template (or (org2web-read-org-option "URI") 231 | default-uri-template)) 232 | (date-list (split-string (if creation-date 233 | (org2web-fix-timestamp-string creation-date) 234 | (format-time-string "%Y-%m-%d")) 235 | "-")) 236 | (encoded-title (org2web-encode-string-to-url title))) 237 | (format-spec uri-template `((?y . ,(car date-list)) 238 | (?m . ,(cadr date-list)) 239 | (?d . ,(cl-caddr date-list)) 240 | (?t . ,encoded-title))))) 241 | 242 | 243 | (defun org2web-get-file-category (org-file) 244 | "This is the default function used to get a file's category, 245 | see org2web config option 'retrieve-category-function. How to judge a 246 | file's category is based on its name and its root folder name." 247 | (let ((repo-dir (org2web-get-repository-directory)) 248 | (default-category (org2web-get-config-option :default-category)) 249 | (category-ignore-list (org2web-get-config-option :category-ignore-list))) 250 | (cond ((not org-file) 251 | (let ((cat-list `("index" "about" ,(org2web-get-config-option :default-category)))) ;; 3 default categories 252 | (dolist (f (directory-files repo-dir)) 253 | (when (and (not (equal f ".")) 254 | (not (equal f "..")) 255 | (not (equal f ".git")) 256 | (not (member f category-ignore-list)) 257 | (not (equal f default-category)) 258 | (file-directory-p 259 | (expand-file-name f repo-dir))) 260 | (setq cat-list (cons f cat-list)))) 261 | cat-list)) 262 | ((string= (expand-file-name "index.org" repo-dir) 263 | (expand-file-name org-file)) "index") 264 | ((string= (expand-file-name "about.org" repo-dir) 265 | (expand-file-name org-file)) "about") 266 | ((string= (file-name-directory (expand-file-name org-file)) 267 | repo-dir) default-category) 268 | (t (car (split-string (file-relative-name 269 | (expand-file-name org-file) repo-dir) 270 | "[/\\\\]+")))))) 271 | 272 | (defun org2web-relative-url-to-absolute (html-content) 273 | "Force convert relative url of `html-content' to absolute url." 274 | (let ((site-domain (org2web-get-site-domain)) 275 | url) 276 | (if org2web-always-use-relative-url 277 | html-content 278 | (with-temp-buffer 279 | (insert html-content) 280 | (goto-char (point-min)) 281 | (when (org2web-get-config-option :force-absolute-url) 282 | (while (re-search-forward 283 | ;;; TODO: not only links need to convert, but also inline 284 | ;;; images, may add others later 285 | ;; "]+href=\"\\([^\"]+\\)\"[^>]*>\\([^<]*\\)" nil t) 286 | "\\(<[a-zA-Z]+[^/>]+\\)\\(src\\|href\\)\\(=\"\\)\\([^\"]+\\)\\(\"[^>]*>\\)" nil t) 287 | (setq url (match-string 4)) 288 | (when (string-prefix-p "/" url) 289 | (setq url (concat 290 | (match-string 1) 291 | (match-string 2) 292 | (match-string 3) 293 | site-domain url 294 | (match-string 5))) 295 | (replace-match url)))) 296 | (buffer-string))))) 297 | 298 | (defun org2web-publish-modified-file (component-table pub-dir) 299 | "Publish org file opened in current buffer. COMPONENT-TABLE is the hash table 300 | used to render the template, PUB-DIR is the directory for published html file. 301 | If COMPONENT-TABLE is nil, the publication will be skipped." 302 | (when component-table 303 | (unless (file-directory-p pub-dir) 304 | (mkdir pub-dir t)) 305 | (org2web-string-to-file 306 | (org2web-relative-url-to-absolute 307 | (mustache-render 308 | (org2web-get-cache-create 309 | :container-template 310 | (message "Read container.mustache from file") 311 | (org2web-file-to-string (org2web-get-template-file "container.mustache"))) 312 | component-table)) 313 | (concat (file-name-as-directory pub-dir) "index.html") ;; 'html-mode ;; do NOT indent the code 314 | ))) 315 | 316 | (defun org2web-rearrange-category-sorted (file-attr-list) 317 | "Rearrange and sort attribute property lists from FILE-ATTR-LIST. Rearrange 318 | according to category, and sort according to :sort-by property defined in 319 | `org2web-category-config-alist', if category is not in `org2web-category-config-alist', 320 | by default, category which set by config option `:default-category' will be used. 321 | For sorting, later lies headmost." 322 | (let ((default-category (org2web-get-config-option :default-category)) 323 | cat-alist cat-list) 324 | (mapc 325 | #'(lambda (plist) 326 | (setq cat-list (cdr (assoc (plist-get plist :category) cat-alist))) 327 | (if cat-list 328 | (nconc cat-list (list plist)) 329 | (setq cat-alist (cons (cons (plist-get plist :category) 330 | (list plist)) 331 | cat-alist)))) 332 | file-attr-list) 333 | (mapc 334 | #'(lambda (cell) 335 | (setcdr 336 | cell 337 | (sort (cdr cell) 338 | #'(lambda (plist1 plist2) 339 | (<= (org2web-compare-standard-date 340 | (org2web-fix-timestamp-string 341 | (plist-get 342 | plist1 343 | (plist-get 344 | (cdr (or (assoc (plist-get plist1 :category) 345 | org2web-category-config-alist) 346 | (org2web-get-category-setting default-category))) 347 | :sort-by))) 348 | (org2web-fix-timestamp-string 349 | (plist-get 350 | plist2 351 | (plist-get 352 | (cdr (or (assoc (plist-get plist2 :category) 353 | org2web-category-config-alist) 354 | (org2web-get-category-setting default-category))) 355 | :sort-by)))) 356 | 0))))) 357 | cat-alist))) 358 | 359 | (defun org2web-update-category-index (file-attr-list pub-base-dir) 360 | "Update index page of different categories. FILE-ATTR-LIST is the list of all 361 | file attribute property lists. PUB-BASE-DIR is the root publication directory." 362 | (let* ((sort-alist (org2web-rearrange-category-sorted file-attr-list)) 363 | (default-category (org2web-get-config-option :default-category)) 364 | cat-dir) 365 | (mapc 366 | #'(lambda (cat-list) 367 | (unless (not (plist-get (cdr (or (assoc (car cat-list) 368 | org2web-category-config-alist) 369 | (org2web-get-category-setting default-category))) 370 | :category-index)) 371 | (setq cat-dir (file-name-as-directory 372 | (concat (file-name-as-directory pub-base-dir) 373 | (org2web-encode-string-to-url (car cat-list))))) 374 | (unless (file-directory-p cat-dir) 375 | (mkdir cat-dir t)) 376 | (org2web-string-to-file 377 | (org2web-relative-url-to-absolute 378 | (mustache-render 379 | (org2web-get-cache-create 380 | :container-template 381 | (message "Read container.mustache from file") 382 | (org2web-file-to-string (org2web-get-template-file "container.mustache"))) 383 | (ht ("header" 384 | (org2web-render-header 385 | (ht ("page-title" (concat (capitalize (car cat-list)) 386 | " Index - " 387 | (org2web-get-config-option :site-main-title))) 388 | ("author" (or user-full-name "Unknown Author"))))) 389 | ("nav" (org2web-render-navigation-bar)) 390 | ("content" 391 | (org2web-render-content 392 | "category-index.mustache" 393 | (ht ("cat-name" (capitalize (car cat-list))) 394 | ("posts" 395 | (mapcar 396 | #'(lambda (attr-plist) 397 | (let ((tags-multi (mapcar 398 | #'(lambda (tag-name) 399 | (ht ("link" (org2web-generate-summary-uri "tags" tag-name)) 400 | ("name" tag-name))) 401 | (plist-get attr-plist :tags)))) 402 | (ht ("date" 403 | (plist-get 404 | attr-plist 405 | (plist-get 406 | (cdr (or (assoc 407 | (plist-get attr-plist :category) 408 | org2web-category-config-alist) 409 | (org2web-get-category-setting default-category))) 410 | :sort-by))) 411 | ("post-uri" (plist-get attr-plist :uri)) 412 | ("post-title" (plist-get attr-plist :title)) 413 | ("tag-links" (when tags-multi 414 | (mapconcat 415 | #'(lambda (tag) 416 | (mustache-render 417 | "{{name}}" tag)) 418 | tags-multi " : ")))))) 419 | (cdr cat-list)))))) 420 | ("footer" 421 | (org2web-render-footer 422 | (ht ("show-meta" nil) 423 | ("show-comment" nil) 424 | ("author" (or user-full-name "Unknown Author")) 425 | ("google-analytics" (org2web-get-config-option :personal-google-analytics-id)) 426 | ("google-analytics-id" (org2web-get-config-option :personal-google-analytics-id)) 427 | ("creator-info" (org2web-get-html-creator-string)) 428 | ("email" (org2web-confound-email-address (or user-mail-address 429 | "Unknown Email"))))))))) 430 | (concat cat-dir "index.html") 'html-mode))) 431 | sort-alist))) 432 | 433 | (defun org2web-generate-default-index (file-attr-list pub-base-dir) 434 | "Generate default index page, only if index.org does not exist. FILE-ATTR-LIST 435 | is the list of all file attribute property lists. PUB-BASE-DIR is the root 436 | publication directory." 437 | (let ((sort-alist (org2web-rearrange-category-sorted file-attr-list)) 438 | (id 0)) 439 | (org2web-string-to-file 440 | (org2web-relative-url-to-absolute 441 | (mustache-render 442 | (org2web-get-cache-create 443 | :container-template 444 | (message "Read container.mustache from file") 445 | (org2web-file-to-string (org2web-get-template-file "container.mustache"))) 446 | (ht ("header" 447 | (org2web-render-header 448 | (ht ("page-title" (concat "Index - " (org2web-get-config-option :site-main-title))) 449 | ("author" (or user-full-name "Unknown Author"))))) 450 | ("nav" (org2web-render-navigation-bar)) 451 | ("content" 452 | (org2web-render-content 453 | "index.mustache" 454 | (ht ("categories" 455 | (mapcar 456 | #'(lambda (cell) 457 | (ht ("id" (setq id (+ id 1))) 458 | ("category" (capitalize (car cell))) 459 | ("posts" (mapcar 460 | #'(lambda (plist) 461 | (ht ("post-uri" 462 | (plist-get plist :uri)) 463 | ("post-title" 464 | (plist-get plist :title)) 465 | ("post-desc" 466 | (plist-get plist :description)) 467 | ("post-date" 468 | (plist-get plist :date)) 469 | ("post-thumb" 470 | (or (plist-get plist :thumb) "")))) 471 | (cdr cell))))) 472 | (cl-remove-if 473 | #'(lambda (cell) 474 | (string= (car cell) "about")) 475 | sort-alist)))))) 476 | ("footer" 477 | (org2web-render-footer 478 | (ht ("show-meta" nil) 479 | ("show-comment" nil) 480 | ("author" (or user-full-name "Unknown Author")) 481 | ("google-analytics" (org2web-get-config-option :personal-google-analytics-id)) 482 | ("google-analytics-id" (org2web-get-config-option :personal-google-analytics-id)) 483 | ("creator-info" (org2web-get-html-creator-string)) 484 | ("email" (org2web-confound-email-address (or user-mail-address 485 | "Unknown Email"))))))))) 486 | (concat (file-name-as-directory pub-base-dir) "index.html") 'html-mode))) 487 | 488 | (defun org2web-generate-default-about (pub-base-dir) 489 | "Generate default about page, only if about.org does not exist. PUB-BASE-DIR 490 | is the root publication directory." 491 | (let* ((about-sub-dir 492 | (replace-regexp-in-string 493 | "^/" "" 494 | (car (cdr (org2web-get-config-option :about))))) 495 | (pub-dir (file-name-as-directory 496 | (expand-file-name about-sub-dir pub-base-dir)))) 497 | (unless (file-directory-p pub-dir) 498 | (mkdir pub-dir t)) 499 | (org2web-string-to-file 500 | (org2web-relative-url-to-absolute 501 | (mustache-render 502 | (org2web-get-cache-create 503 | :container-template 504 | (message "Read container.mustache from file") 505 | (org2web-file-to-string (org2web-get-template-file "container.mustache"))) 506 | (ht ("header" 507 | (org2web-render-header 508 | (ht ("page-title" (concat "About - " (org2web-get-config-option :site-main-title))) 509 | ("author" (or user-full-name "Unknown Author"))))) 510 | ("nav" (org2web-render-navigation-bar)) 511 | ("content" 512 | (org2web-render-content 513 | "about.mustache" 514 | (ht ("author" (or user-full-name "Unknown Author"))))) 515 | ("footer" 516 | (org2web-render-footer 517 | (ht ("show-meta" nil) 518 | ("show-comment" nil) 519 | ("author" (or user-full-name "Unknown Author")) 520 | ("google-analytics" (org2web-get-config-option :personal-google-analytics-id)) 521 | ("google-analytics-id" (org2web-get-config-option :personal-google-analytics-id)) 522 | ("creator-info" (org2web-get-html-creator-string)) 523 | ("email" (org2web-confound-email-address (or user-mail-address 524 | "Unknown Email"))))))))) 525 | (concat pub-dir "index.html") 'html-mode))) 526 | 527 | (defun org2web-generate-summary-uri (summary-name summary-item-name) 528 | "Generate summary uri based on `summary-name' and `summary-item-name'." 529 | (concat "/" summary-name "/" (org2web-encode-string-to-url summary-item-name) "/")) 530 | 531 | (defun org2web-update-summary (file-attr-list pub-base-dir summary-name) 532 | "Update summary pages which name is `summary-name', FILE-ATTR-LIST 533 | is the list of all file attribute property lists. PUB-BASE-DIR is the 534 | root publication directory. 535 | 536 | TODO: improve this function." 537 | (let* ((summary-base-dir (expand-file-name 538 | (concat summary-name "/") 539 | pub-base-dir)) 540 | summary-alist summary-list summary-dir) 541 | (mapc 542 | #'(lambda (attr-plist) 543 | (mapc 544 | #'(lambda (name) 545 | (setq summary-list (assoc name summary-alist)) 546 | (unless summary-list 547 | (add-to-list 'summary-alist (setq summary-list `(,name)))) 548 | (nconc summary-list (list attr-plist))) 549 | (let* ((summary-attr (car (cdr (assoc summary-name (org2web-get-config-option :summary))))) 550 | (elem (plist-get attr-plist summary-attr))) 551 | (if (listp elem) elem (list elem))))) 552 | file-attr-list) 553 | (unless (file-directory-p summary-base-dir) 554 | (mkdir summary-base-dir t)) 555 | (org2web-string-to-file 556 | (org2web-relative-url-to-absolute 557 | (mustache-render 558 | (org2web-get-cache-create 559 | :container-template 560 | (message "Read container.mustache from file") 561 | (org2web-file-to-string (org2web-get-template-file "container.mustache"))) 562 | (ht ("header" 563 | (org2web-render-header 564 | (ht ("page-title" (concat (capitalize summary-name) 565 | " Index - " 566 | (org2web-get-config-option :site-main-title))) 567 | ("author" (or user-full-name "Unknown Author"))))) 568 | ("nav" (org2web-render-navigation-bar)) 569 | ("content" 570 | (org2web-render-content 571 | "summary-index.mustache" 572 | (ht ("summary-name" (capitalize summary-name)) 573 | ("summary" 574 | (mapcar 575 | #'(lambda (summary-list) 576 | (ht ("summary-item-name" (car summary-list)) 577 | ("summary-item-uri" (org2web-generate-summary-uri summary-name (car summary-list))) 578 | ("count" (number-to-string (length (cdr summary-list)))))) 579 | summary-alist))))) 580 | ("footer" 581 | (org2web-render-footer 582 | (ht ("show-meta" nil) 583 | ("show-comment" nil) 584 | ("author" (or user-full-name "Unknown Author")) 585 | ("google-analytics" (org2web-get-config-option :personal-google-analytics-id)) 586 | ("google-analytics-id" (org2web-get-config-option :personal-google-analytics-id)) 587 | ("creator-info" (org2web-get-html-creator-string)) 588 | ("email" (org2web-confound-email-address (or user-mail-address 589 | "Unknown Email"))))))))) 590 | (concat summary-base-dir "index.html") 'html-mode) 591 | (mapc 592 | #'(lambda (summary-list) 593 | (setq summary-dir (file-name-as-directory 594 | (concat summary-base-dir 595 | (org2web-encode-string-to-url (car summary-list))))) 596 | (unless (file-directory-p summary-dir) 597 | (mkdir summary-dir t)) 598 | (org2web-string-to-file 599 | (org2web-relative-url-to-absolute 600 | (mustache-render 601 | (org2web-get-cache-create 602 | :container-template 603 | (message "Read container.mustache from file") 604 | (org2web-file-to-string (org2web-get-template-file "container.mustache"))) 605 | (ht ("header" 606 | (org2web-render-header 607 | (ht ("page-title" (concat (capitalize summary-name) ": " (car summary-list) 608 | " - " (org2web-get-config-option :site-main-title))) 609 | ("author" (or user-full-name "Unknown Author"))))) 610 | ("nav" (org2web-render-navigation-bar)) 611 | ("content" 612 | (org2web-render-content 613 | "summary.mustache" 614 | (ht ("summary-name" (capitalize summary-name)) 615 | ("summary-item-name" (car summary-list)) 616 | ("posts" 617 | (mapcar 618 | #'(lambda (attr-plist) 619 | (ht ("post-uri" (plist-get attr-plist :uri)) 620 | ("post-title" (plist-get attr-plist :title)) 621 | ("post-date" (plist-get attr-plist :date)))) 622 | (cdr summary-list)))))) 623 | ("footer" 624 | (org2web-render-footer 625 | (ht ("show-meta" nil) 626 | ("show-comment" nil) 627 | ("author" (or user-full-name "Unknown Author")) 628 | ("google-analytics" (org2web-get-config-option :personal-google-analytics-id)) 629 | ("google-analytics-id" (org2web-get-config-option :personal-google-analytics-id)) 630 | ("creator-info" (org2web-get-html-creator-string)) 631 | ("email" (org2web-confound-email-address (or user-mail-address 632 | "Unknown Email"))))))))) 633 | (concat summary-dir "index.html") 'html-mode)) 634 | summary-alist))) 635 | 636 | (defun org2web-update-rss (file-attr-list pub-base-dir) 637 | "Update RSS. FILE-ATTR-LIST is the list of all file attribute property lists. 638 | PUB-BASE-DIR is the root publication directory." 639 | (let* ((rss-file-name 640 | (replace-regexp-in-string 641 | "^/" "" 642 | (car (cdr (org2web-get-config-option :rss))))) 643 | (rss-file 644 | (concat (file-name-as-directory pub-base-dir) rss-file-name)) 645 | (rss-base-dir 646 | (file-name-directory rss-file)) 647 | (last-10-posts 648 | (-take 10 (--sort (>= 0 (org2web-compare-standard-date 649 | (org2web-fix-timestamp-string 650 | (plist-get it :mod-date)) 651 | (org2web-fix-timestamp-string 652 | (plist-get other :mod-date)))) 653 | (--filter (not (or 654 | (string= (plist-get it :category) 655 | "index") 656 | (string= (plist-get it :category) 657 | "about"))) 658 | file-attr-list))))) 659 | (unless (file-directory-p rss-base-dir) 660 | (mkdir rss-base-dir t)) 661 | (org2web-string-to-file 662 | (org2web-relative-url-to-absolute 663 | (mustache-render 664 | org2web-rss-template 665 | (ht ("title" (org2web-get-config-option :site-main-title)) 666 | ("link" (org2web-get-site-domain)) 667 | ("description" (org2web-get-config-option :site-sub-title)) 668 | ("date" (format-time-string "%a, %d %b %Y %T %Z")) 669 | ("items" (--map (ht ("item-title" (plist-get it :title)) 670 | ("item-link" (org2web-get-full-url (plist-get it :uri))) 671 | ("item-description" (plist-get it :description)) 672 | ("item-update-date" (plist-get it :mod-date))) 673 | last-10-posts))))) 674 | rss-file))) 675 | 676 | 677 | (provide 'org2web-export) 678 | 679 | ;;; org2web-export.el ends here 680 | -------------------------------------------------------------------------------- /org2web-resource.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-resource.el --- Functions dealing with org2web theme resources 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | ;; 2012, 2013, 2014, 2015 Kelvin Hu 5 | 6 | ;; Author: Feng Shu 7 | ;; Kelvin Hu 8 | ;; Keywords: convenience 9 | ;; Homepage: https://github.com/tumashu/org2web 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; This file include functions which deal with org2web theme resources 27 | 28 | ;;; Code: 29 | 30 | (require 'format-spec) 31 | (require 'ox) 32 | (require 'ht) 33 | (require 'org2web-util) 34 | (require 'org2web-vars) 35 | (require 'org2web-config) 36 | 37 | (defun org2web-prepare-theme-resources (pub-root-dir) 38 | "Copy theme resources files to PUB-ROOT-DIR." 39 | (let ((pub-theme-dir (expand-file-name "media/" pub-root-dir)) 40 | (theme-dirs (reverse (org2web-get-theme-dirs nil nil 'resources)))) 41 | (when (file-directory-p pub-theme-dir) 42 | (delete-directory pub-theme-dir t)) 43 | (dolist (theme-dir theme-dirs) 44 | (copy-directory theme-dir pub-theme-dir t t t)))) 45 | 46 | 47 | (provide 'org2web-resource) 48 | 49 | ;;; org2web-resource.el ends here 50 | -------------------------------------------------------------------------------- /org2web-template.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-template.el --- templating system based on mustache, required by org2web 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | ;; 2012, 2013, 2014, 2015 Kelvin Hu 5 | 6 | ;; Author: Feng Shu 7 | ;; Kelvin Hu 8 | ;; Keywords: convenience 9 | ;; Homepage: https://github.com/tumashu/org2web 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; templating system based on mustache.el, to replace `format-spec'. 27 | 28 | ;;; Code: 29 | 30 | (require 'ox) 31 | ;; (require 'mustache) 32 | (autoload 'mustache-render "mustache") 33 | (require 'org2web-util) 34 | (require 'org2web-vars) 35 | (require 'org2web-config) 36 | (require 'md5) 37 | 38 | 39 | (defun org2web-get-template-file (template-file-name) 40 | "Get path of template file which name is `template-file-name'." 41 | (car (remove nil (mapcar 42 | #'(lambda (dir) 43 | (let ((file (concat (file-name-as-directory dir) 44 | template-file-name))) 45 | (when (file-exists-p file) 46 | file))) 47 | (org2web-get-theme-dirs nil nil 'templates))))) 48 | 49 | (defun org2web-get-uploader-template (uploader-template-name) 50 | "Get path of uploader template which name is `uploader-template-name'." 51 | (car (remove nil (mapcar 52 | #'(lambda (dir) 53 | (let ((file (concat (file-name-as-directory dir) 54 | uploader-template-name))) 55 | (when (file-exists-p file) 56 | file))) 57 | (list (concat (org2web-get-repository-directory) "uploaders/") 58 | (concat (file-name-as-directory org2web-load-directory) 59 | "uploaders/" 60 | (replace-regexp-in-string "/" "-" (symbol-name system-type)) 61 | "/") 62 | (concat (file-name-as-directory org2web-load-directory) 63 | "uploaders/common/") 64 | (concat (file-name-as-directory org2web-load-directory) 65 | "uploaders/")))))) 66 | 67 | (defun org2web-get-title (org-file) 68 | "Get the title of org file." 69 | (let ((title (org2web-read-org-option "TITLE"))) 70 | (if (and title (> (length title) 0)) 71 | title 72 | (file-name-base org-file)))) 73 | 74 | (defun org2web-get-category (org-file) 75 | "Get org file category presented by ORG-FILE, return all categories if 76 | ORG-FILE is nil. " 77 | (let ((func (org2web-get-config-option :retrieve-category-function))) 78 | (if (functionp func) 79 | (funcall func org-file) 80 | (funcall 'org2web-get-file-category org-file)))) 81 | 82 | (defun org2web-get-cache-item (key) 83 | "Get the item associated with KEY in `org2web-item-cache', if `org2web-item-cache' is 84 | nil or there is no item associated with KEY in it, return nil." 85 | (and org2web-item-cache 86 | (plist-get org2web-item-cache key))) 87 | 88 | (defun org2web-update-cache-item (key value) 89 | "Update the item associated with KEY in `org2web-item-cache', if `org2web-item-cache' is 90 | nil, initialize it." 91 | (if org2web-item-cache 92 | (plist-put org2web-item-cache key value) 93 | (setq org2web-item-cache `(,key ,value))) 94 | value) 95 | 96 | (defmacro org2web-get-cache-create (key &rest body) 97 | "Firstly get item from `org2web-item-cache' with KEY, if item not found, evaluate 98 | BODY and push the result into cache and return it." 99 | `(or (org2web-get-cache-item ,key) 100 | (org2web-update-cache-item ,key (funcall (lambda () ,@body))))) 101 | 102 | (defun org2web-render-header (&optional param-table org-file) 103 | "Render the header on each page. PARAM-TABLE is the hash table from mustache 104 | to render the template. If it is not set or nil, this function will try to build 105 | a hash table accordint to current buffer." 106 | (mustache-render 107 | (org2web-get-cache-create 108 | :header-template 109 | (message "Read header.mustache from file") 110 | (org2web-file-to-string (org2web-get-template-file "header.mustache"))) 111 | (or param-table 112 | (ht ("page-title" (concat (funcall (org2web-get-config-option :get-title-function) org-file) 113 | " - " (org2web-get-config-option :site-main-title))) 114 | ("author" (or (org2web-read-org-option "AUTHOR") 115 | user-full-name "Unknown Author")) 116 | ("description" (org2web-read-org-option "DESCRIPTION")) 117 | ("keywords" (org2web-read-org-option "KEYWORDS")))))) 118 | 119 | (defun org2web-render-navigation-bar (&optional param-table org-file) 120 | "Render the navigation bar on each page. it will be read firstly from 121 | `org2web-item-cache', if there is no cached content, it will be rendered 122 | and pushed into cache from template. PARAM-TABLE is the hash table for mustache 123 | to render the template. If it is not set or nil, this function will try to 124 | render from a default hash table." 125 | (let ((site-domain (org2web-get-site-domain)) 126 | (category-ignore-list (org2web-get-config-option :category-ignore-list))) 127 | (org2web-get-cache-create 128 | :nav-bar-html 129 | (message "Render navigation bar from template") 130 | (mustache-render 131 | (org2web-get-cache-create 132 | :nav-bar-template 133 | (message "Read nav.mustache from file") 134 | (org2web-file-to-string (org2web-get-template-file "nav.mustache"))) 135 | (or param-table 136 | (ht ("site-main-title" (org2web-get-config-option :site-main-title)) 137 | ("site-sub-title" (org2web-get-config-option :site-sub-title)) 138 | ("nav-categories" 139 | (mapcar 140 | #'(lambda (cat) 141 | (ht ("category-uri" 142 | (concat "/" (org2web-encode-string-to-url cat) "/")) 143 | ("category-name" (capitalize cat)))) 144 | (sort (cl-remove-if 145 | #'(lambda (cat) 146 | (or (string= cat "index") 147 | (string= cat "about") 148 | (member cat category-ignore-list))) 149 | (org2web-get-category nil)) 150 | 'string-lessp))) 151 | ("nav-summary" 152 | (mapcar 153 | #'(lambda (cat) 154 | (ht ("summary-item-uri" 155 | (concat "/" (org2web-encode-string-to-url cat) "/")) 156 | ("summary-item-name" (capitalize cat)))) 157 | (mapcar #'car (org2web-get-config-option :summary)))) 158 | ("nav-source-browse" 159 | (let ((list (org2web-get-config-option :source-browse-url))) 160 | (when list 161 | (ht ("source-browse-name" (car list)) 162 | ("source-browse-uri" (car (cdr list))))))) 163 | ("nav-about" 164 | (let ((list (org2web-get-config-option :about))) 165 | (when list 166 | (ht ("about-name" (car list)) 167 | ("about-uri" (car (cdr list))))))) 168 | ("nav-rss" 169 | (let ((list (org2web-get-config-option :rss))) 170 | (when list 171 | (ht ("rss-name" (car list)) 172 | ("rss-uri" (car (cdr list))))))) 173 | ("avatar" (org2web-get-config-option :personal-avatar)) 174 | ("site-domain" (if (string-match 175 | "\\`https?://\\(.*[a-zA-Z]\\)/?\\'" 176 | site-domain) 177 | (match-string 1 site-domain) 178 | site-domain)))))))) 179 | 180 | (defun org2web-render-content (&optional template param-table org-file) 181 | "Render the content on each page. TEMPLATE is the template name for rendering, 182 | if it is not set of nil, will use default post.mustache instead. PARAM-TABLE is 183 | similar to `org2web-render-header'." 184 | (mustache-render 185 | (org2web-get-cache-create 186 | (if template 187 | (intern (replace-regexp-in-string "\\.mustache$" "-template" template)) 188 | :post-template) 189 | (message (concat "Read " (or template "post.mustache") " from file")) 190 | (org2web-file-to-string (org2web-get-template-file 191 | (or template "post.mustache")))) 192 | (or param-table 193 | (ht ("title" (funcall (org2web-get-config-option :get-title-function) org-file)) 194 | ("content" (cl-flet ((org-html-fontify-code 195 | (code lang) 196 | (when code (org-html-encode-plain-text code)))) 197 | (let ((org-export-function (org2web-get-config-option :org-export-function))) 198 | (when (functionp org-export-function) 199 | (funcall org-export-function))))))))) 200 | 201 | (defun org2web-default-org-export () 202 | "A function with can export org file to html." 203 | (org-export-as 'html nil nil t nil)) 204 | 205 | (defun org2web-render-footer (&optional param-table org-file) 206 | "Render the footer on each page. PARAM-TABLE is similar to 207 | `org2web-render-header'." 208 | (mustache-render 209 | (org2web-get-cache-create 210 | :footer-template 211 | (message "Read footer.mustache from file") 212 | (org2web-file-to-string (org2web-get-template-file "footer.mustache"))) 213 | (or param-table 214 | (let* ((site-domain (org2web-get-site-domain)) 215 | (old-site-domain (org2web-get-site-domain t)) 216 | (title (funcall (org2web-get-config-option :get-title-function) org-file)) 217 | (default-category (org2web-get-config-option :default-category)) 218 | (date (org2web-fix-timestamp-string 219 | (or (org2web-read-org-option "DATE") 220 | (format-time-string "%Y-%m-%d")))) 221 | (tags (org2web-read-org-option "TAGS")) 222 | (tags (if tags ;; Bug: when set option `:summary' to `nil', can't deal with. 223 | (mapcar 224 | #'(lambda (tag-name) 225 | (ht ("link" (org2web-generate-summary-uri 226 | (or (car (rassoc '(:tags) (org2web-get-config-option :summary))) 227 | "tags") tag-name)) 228 | ("name" tag-name))) 229 | (delete "" (mapcar 'org2web-trim-string (split-string tags "[:,]+" t)))))) 230 | (category (org2web-get-category org-file)) 231 | (config (cdr (or (assoc category org2web-category-config-alist) 232 | (org2web-get-category-setting default-category)))) 233 | (uri (funcall (plist-get config :uri-generator) 234 | (plist-get config :uri-template) date title))) 235 | (ht ("show-meta" (plist-get config :show-meta)) 236 | ("show-comment" (and (plist-get config :show-comment) 237 | (or (org2web-get-config-option :personal-disqus-shortname) 238 | (org2web-get-config-option :personal-duoshuo-shortname)))) 239 | ("date" date) 240 | ("mod-date" (if (not org-file) 241 | (format-time-string "%Y-%m-%d") 242 | (format-time-string 243 | "%Y-%m-%d" 244 | (nth 5 (file-attributes org-file))))) 245 | ("tags" tags) 246 | ("tag-links" (if (not tags) "N/A" 247 | (mapconcat 248 | #'(lambda (tag) 249 | (mustache-render 250 | "{{name}}" tag)) 251 | tags " "))) 252 | ("author" (or (org2web-read-org-option "AUTHOR") 253 | user-full-name 254 | "Unknown Author")) 255 | ("disqus-id" uri) 256 | ("disqus-url" (org2web-get-full-url uri)) 257 | ("disqus-comment" (org2web-get-config-option :personal-disqus-shortname)) 258 | ("disqus-shortname" (org2web-get-config-option :personal-disqus-shortname)) 259 | ("duoshuo-thread-key" (md5 (concat 260 | (or old-site-domain site-domain) 261 | (replace-regexp-in-string " " "" title)))) 262 | ("duoshuo-title" title) 263 | ("duoshuo-url" (org2web-get-full-url uri)) 264 | ("duoshuo-comment" (org2web-get-config-option :personal-duoshuo-shortname)) 265 | ("duoshuo-shortname" (org2web-get-config-option :personal-duoshuo-shortname)) 266 | ("google-analytics" (org2web-get-config-option :personal-google-analytics-id)) 267 | ("google-analytics-id" (org2web-get-config-option :personal-google-analytics-id)) 268 | ("creator-info" (org2web-get-html-creator-string)) 269 | ("email" (org2web-confound-email-address (or (org2web-read-org-option "EMAIL") 270 | user-mail-address 271 | "Unknown Email")))))))) 272 | 273 | 274 | (provide 'org2web-template) 275 | 276 | ;;; org2web-template.el ends here 277 | -------------------------------------------------------------------------------- /org2web-util.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-util.el --- Common utility functions required by org2web 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | ;; 2012, 2013, 2014, 2015 Kelvin Hu 5 | 6 | ;; Author: Feng Shu 7 | ;; Kelvin Hu 8 | ;; Keywords: convenience 9 | ;; Homepage: https://github.com/tumashu/org2web 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; several utility functions 27 | 28 | ;;; Code: 29 | 30 | (require 'ht) 31 | (require 'org2web-vars) 32 | (require 'org2web-config) 33 | (require 'cl-lib) 34 | 35 | (defun org2web-directory-files-recursively (&optional directory regexp) 36 | "recursively list all the files in a directory" 37 | (let* ((directory (or directory default-directory)) 38 | (regexp (or regexp ".*")) 39 | (files (cl-delete-if 40 | #'(lambda (s) 41 | (string-match (rx bol (repeat 1 2 ".") eol) 42 | (file-name-nondirectory s))) 43 | (directory-files directory t nil t)))) 44 | (cl-loop for file in files 45 | when (string-match regexp (file-name-nondirectory file)) 46 | collect file into ret 47 | when (file-directory-p file) 48 | nconc (org2web-directory-files-recursively file regexp) into ret 49 | finally return ret))) 50 | 51 | (defun org2web-delete-directory (&rest dir-list) 52 | (dolist (dir dir-list) 53 | (when (file-directory-p dir) 54 | (delete-directory dir t)))) 55 | 56 | (defun org2web-make-directory (&rest dir-list) 57 | (dolist (dir dir-list) 58 | (make-directory dir t))) 59 | 60 | (defun org2web-get-uploader-directory (project-name &optional subdir) 61 | (expand-file-name 62 | (file-name-as-directory 63 | (concat (file-name-as-directory 64 | (concat (file-name-as-directory org2web-temporary-directory) 65 | project-name)) 66 | (or subdir ""))))) 67 | 68 | (defun org2web-read-top-n (prompt files base &optional max) 69 | (let* ((max-mini-window-height 0.9) 70 | (length (length files)) 71 | (max-line (min length (or max 20)))) 72 | (read-number 73 | (concat (let ((i 1)) 74 | (mapconcat 75 | #'(lambda (file) 76 | (prog1 (format "%2s. %s" 77 | (number-to-string i) 78 | (file-relative-name file base)) 79 | (setq i (+ i 1)))) 80 | (append (cl-subseq files 0 max-line) 81 | (when (> length max-line) 82 | '("## Hide others ... ##"))) 83 | "\n")) 84 | "\n\n" 85 | prompt)))) 86 | 87 | (defun org2web-sort-files (files) 88 | (sort files #'(lambda (a b) 89 | (time-less-p 90 | (cl-sixth (file-attributes b)) 91 | (cl-sixth (file-attributes a)))))) 92 | 93 | (defun org2web-get-random-number (n) 94 | (let ((result 0)) 95 | (dotimes (i n) 96 | (setq result 97 | (+ result (* (random 9) (expt 10 i))))) 98 | result)) 99 | 100 | (defun org2web-compare-standard-date (date1 date2) 101 | "Compare two standard ISO 8601 format dates, format is as below: 102 | 2012-08-17 103 | 1. if date1 is earlier than date2, returns 1 104 | 2. if equal, returns 0 105 | 3. if date2 is earlier than date1, returns -1" 106 | (let* ((date-list1 (parse-time-string date1)) 107 | (year1 (nth 5 date-list1)) 108 | (month1 (nth 4 date-list1)) 109 | (day1 (nth 3 date-list1)) 110 | (date-list2 (parse-time-string date2)) 111 | (year2 (nth 5 date-list2)) 112 | (month2 (nth 4 date-list2)) 113 | (day2 (nth 3 date-list2))) 114 | (cond ((< year1 year2) 1) 115 | ((> year1 year2) -1) 116 | (t (cond ((< month1 month2) 1) 117 | ((> month1 month2) -1) 118 | (t (cond ((< day1 day2) 1) 119 | ((> day1 day2) -1) 120 | (t 0)))))))) 121 | 122 | (defun org2web-remove-matched-items (list regexp-list) 123 | "Remove all items of `list', which match any regexp of `regexp-list'." 124 | (dolist (regexp regexp-list) 125 | (setq list 126 | (cl-remove-if 127 | #'(lambda (x) 128 | (string-match-p regexp x)) list))) 129 | list) 130 | 131 | (defun org2web-select-matched-items (list regexp-list) 132 | "Select all items of `list', which match any regexp of `regexp-list'." 133 | (let (output) 134 | (dolist (regexp regexp-list) 135 | (setq output 136 | (append output 137 | (remove nil (mapcar 138 | #'(lambda (x) 139 | (when (string-match-p regexp x) 140 | x)) list))))) 141 | (cl-delete-duplicates output))) 142 | 143 | (defun org2web-fix-timestamp-string (date-string) 144 | "This is a piece of code copied from Xah Lee (I modified a little): 145 | Returns yyyy-mm-dd format of date-string 146 | For examples: 147 | [Nov. 28, 1994] => [1994-11-28] 148 | [November 28, 1994] => [1994-11-28] 149 | [11/28/1994] => [1994-11-28] 150 | Any \"day of week\", or \"time\" info, or any other parts of the string, are 151 | discarded. 152 | Code detail: URL `http://xahlee.org/emacs/elisp_parse_time.html'" 153 | (let ((date-str date-string) 154 | date-list year month date yyyy mm dd) 155 | (setq date-str (replace-regexp-in-string "^ *\\(.+\\) *$" "\\1" date-str)) 156 | (cond 157 | ;; USA convention of mm/dd/yyyy 158 | ((string-match 159 | "^\\([0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9][0-9][0-9]\\)$" 160 | date-str) 161 | (concat (match-string 3 date-str) "-" (match-string 1 date-str) "-" 162 | (match-string 2 date-str))) 163 | ((string-match 164 | "^\\([0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9][0-9][0-9]\\)$" 165 | date-str) 166 | (concat (match-string 3 date-str) "-" (match-string 1 date-str) "-" 167 | (match-string 2 date-str))) 168 | ;; some ISO 8601. yyyy-mm-dd 169 | ((string-match 170 | "^\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)$\ 171 | T[0-9][0-9]:[0-9][0-9]" date-str) 172 | (concat (match-string 1 date-str) "-" (match-string 2 date-str) "-" 173 | (match-string 3 date-str))) 174 | ((string-match 175 | "^\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)$" 176 | date-str) 177 | (concat (match-string 1 date-str) "-" (match-string 2 date-str) "-" 178 | (match-string 3 date-str))) 179 | ((string-match "^\\([0-9][0-9][0-9][0-9]\\)-\\([0-9][0-9]\\)$" date-str) 180 | (concat (match-string 1 date-str) "-" (match-string 2 date-str))) 181 | ((string-match "^\\([0-9][0-9][0-9][0-9]\\)$" date-str) 182 | (match-string 1 date-str)) 183 | (t (progn 184 | (setq date-str 185 | (replace-regexp-in-string "January " "Jan. " date-str)) 186 | (setq date-str 187 | (replace-regexp-in-string "February " "Feb. " date-str)) 188 | (setq date-str 189 | (replace-regexp-in-string "March " "Mar. " date-str)) 190 | (setq date-str 191 | (replace-regexp-in-string "April " "Apr. " date-str)) 192 | (setq date-str 193 | (replace-regexp-in-string "May " "May. " date-str)) 194 | (setq date-str 195 | (replace-regexp-in-string "June " "Jun. " date-str)) 196 | (setq date-str 197 | (replace-regexp-in-string "July " "Jul. " date-str)) 198 | (setq date-str 199 | (replace-regexp-in-string "August " "Aug. " date-str)) 200 | (setq date-str 201 | (replace-regexp-in-string "September " "Sep. " date-str)) 202 | (setq date-str 203 | (replace-regexp-in-string "October " "Oct. " date-str)) 204 | (setq date-str 205 | (replace-regexp-in-string "November " "Nov. " date-str)) 206 | (setq date-str 207 | (replace-regexp-in-string "December " "Dec. " date-str)) 208 | (setq date-str 209 | (replace-regexp-in-string " 1st," " 1" date-str)) 210 | (setq date-str 211 | (replace-regexp-in-string " 2nd," " 2" date-str)) 212 | (setq date-str 213 | (replace-regexp-in-string " 3rd," " 3" date-str)) 214 | (setq date-str 215 | (replace-regexp-in-string "\\([0-9]\\)th," "\\1" date-str)) 216 | (setq date-str 217 | (replace-regexp-in-string " 1st " " 1 " date-str)) 218 | (setq date-str 219 | (replace-regexp-in-string " 2nd " " 2 " date-str)) 220 | (setq date-str 221 | (replace-regexp-in-string " 3rd " " 3 " date-str)) 222 | (setq date-str 223 | (replace-regexp-in-string "\\([0-9]\\)th " "\\1 " date-str)) 224 | (setq date-list (parse-time-string date-str)) 225 | (setq year (nth 5 date-list)) 226 | (setq month (nth 4 date-list)) 227 | (setq date (nth 3 date-list)) 228 | (setq yyyy (number-to-string year)) 229 | (setq mm (if month (format "%02d" month) "")) 230 | (setq dd (if date (format "%02d" date) "")) 231 | (concat yyyy "-" mm "-" dd)))))) 232 | 233 | (defun org2web-confound-email-address (email) 234 | "Confound email to prevent spams using simple rule: 235 | replace . with , @ with , e.g. 236 | name@domain.com => name domain com" 237 | (if (not (org2web-get-config-option :confound-email)) email 238 | (replace-regexp-in-string 239 | " +" " " (replace-regexp-in-string 240 | "@" " " (replace-regexp-in-string "\\." " " email))))) 241 | 242 | (defun org2web-string-suffix-p (str1 str2 &optional ignore-case) 243 | "Return non-nil if STR1 is a suffix of STR2. 244 | If IGNORE-CASE is non-nil, the comparison is done without paying attention 245 | to case differences." 246 | (let ((pos (- (length str2) (length str1)))) 247 | (if (< pos 0) nil (eq t (compare-strings str1 nil nil 248 | str2 pos nil ignore-case))))) 249 | 250 | (defun org2web-trim-string-left (str) 251 | "Remove whitespace at the beginning of STR." 252 | (if (string-match "\\`[ \t\n\r]+" str) 253 | (replace-match "" t t str) 254 | str)) 255 | 256 | (defun org2web-trim-string-right (str) 257 | "Remove whitespace at the end of STR." 258 | (if (string-match "[ \t\n\r]+\\'" str) 259 | (replace-match "" t t str) 260 | str)) 261 | 262 | (defun org2web-trim-string (str) 263 | "Remove whitespace at the beginning and end of STR. 264 | The function is copied from https://github.com/magnars/s.el, because I do not 265 | want to make org2web depend on other libraries, so I copied the function here, 266 | so do `org2web-trim-string-left' and `org2web-trim-string-right'." 267 | (org2web-trim-string-left (org2web-trim-string-right str))) 268 | 269 | (defun org2web-encode-string-to-url (string) 270 | "Encode STRING to legal URL. Why we do not use `url-encode-url' to encode the 271 | string, is that `url-encode-url' will convert all not allowed characters into 272 | encoded ones, like %3E, but we do NOT want this kind of url." 273 | (downcase (replace-regexp-in-string "[ :/\\]+" "-" string))) 274 | 275 | (defun org2web-get-full-url (uri) 276 | "Get the full url of URI, by joining site-domain with URI." 277 | (concat (replace-regexp-in-string "/?$" "" (org2web-get-site-domain)) uri)) 278 | 279 | (defun org2web-file-to-string (file) 280 | "Read the content of FILE and return it as a string." 281 | (with-temp-buffer 282 | (insert-file-contents file) 283 | (buffer-string))) 284 | 285 | (defun org2web-string-to-file (string file &optional mode) 286 | "Write STRING into FILE, only when FILE is writable. If MODE is a valid major 287 | mode, format the string with MODE's format settings." 288 | (with-temp-buffer 289 | (insert string) 290 | (set-buffer-file-coding-system 'utf-8-unix) 291 | (when (and mode (functionp mode)) 292 | (funcall mode) 293 | (flush-lines "^[ \\t]*$" (point-min) (point-max)) 294 | (delete-trailing-whitespace (point-min) (point-max)) 295 | (indent-region (point-min) (point-max))) 296 | (when (file-writable-p file) 297 | (write-region (point-min) (point-max) file)))) 298 | 299 | (defun org2web-convert-plist-to-hashtable (plist) 300 | "Convert normal property list PLIST into hash table, keys of PLIST should be 301 | in format :key, and it will be converted into \"key\" in hash table. This is an 302 | alternative to `ht-from-plist'." 303 | (let ((h (ht-create))) 304 | (dolist (pair (ht/group-pairs plist) h) 305 | (let ((key (substring (symbol-name (car pair)) 1)) 306 | (value (cadr pair))) 307 | (ht-set h key value))))) 308 | 309 | 310 | (provide 'org2web-util) 311 | 312 | ;;; org2web-util.el ends here 313 | -------------------------------------------------------------------------------- /org2web-vars.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-vars.el --- Variable configurations required by org2web 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | ;; 2012, 2013, 2014, 2015 Kelvin Hu 5 | 6 | ;; Author: Feng Shu 7 | ;; Kelvin Hu 8 | ;; Keywords: convenience 9 | ;; Homepage: https://github.com/tumashu/org2web 10 | 11 | ;; This program is free software; you can redistribute it and/or modify 12 | ;; it under the terms of the GNU General Public License as published by 13 | ;; the Free Software Foundation, either version 3 of the License, or 14 | ;; (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;; org2web-vars.el contains almost all variable definitions and configurations. 27 | 28 | ;;; Code: 29 | 30 | (require 'ox) 31 | (require 'ht) 32 | 33 | 34 | (defgroup org2web nil 35 | "Options for generating static pages using org2web." 36 | :tag "Org static page generator" 37 | :group 'org) 38 | 39 | (defcustom org2web-default-project nil 40 | "If set, `org2web-publish' will directly publish this project 41 | and `org2web-new-post' will directly add new post to this project." 42 | :group 'org2web 43 | :type 'string) 44 | 45 | (defcustom org2web-temporary-directory "~/.emacs.d/org2web/tmp/" 46 | "Temporary directory used by org2web." 47 | :group 'org2web 48 | :type 'string) 49 | 50 | (defcustom org2web-terminal-emulater 51 | (or (executable-find "x-terminal-emulator") 52 | (executable-find "gnome-terminal") 53 | (executable-find "konsole") 54 | (executable-find "rxvt-unicode") 55 | (executable-find "rxvt") 56 | (executable-find "xterm")) 57 | "Terminal emulater used by org2web update script." 58 | :group 'org2web 59 | :type 'string) 60 | 61 | (defcustom org2web-uploaders 62 | '((git :requires ("bash" "git") 63 | :template "git.mustache" 64 | :template-settings 65 | (lambda (remote export-dir history-dir publish-dir partial-update) 66 | (ht ("export-dir" export-dir) 67 | ("history-dir" history-dir) 68 | ("publish-dir" publish-dir) 69 | ("remote-url" (nth 1 remote)) 70 | ("remote-branch" (nth 2 remote)) 71 | ("partial-update" (if partial-update "yes" "no")))) 72 | :support-partial-update t 73 | :help-info "You should install 'git' in linux system or 'msysgit' in window system!") 74 | (git-simple :requires ("bash" "git") 75 | :template "git-simple.mustache" 76 | :template-settings 77 | (lambda (remote export-dir history-dir publish-dir partial-update) 78 | (ht ("export-dir" export-dir) 79 | ("history-dir" history-dir) 80 | ("publish-dir" publish-dir) 81 | ("remote-url" (nth 1 remote)) 82 | ("remote-branch" (nth 2 remote)) 83 | ("partial-update" (if partial-update "yes" "no")))) 84 | :support-partial-update t 85 | :help-info "You should install 'git' in linux system or 'msysgit' in window system!") 86 | (rclone :requires ("bash" "rclone") 87 | :template "rclone.mustache" 88 | :template-settings 89 | (lambda (remote export-dir history-dir publish-dir partial-update) 90 | (ht ("export-dir" export-dir) 91 | ("publish-dir" publish-dir) 92 | ("remote-name" (nth 1 remote)) 93 | ("remote-path" (nth 2 remote)))) 94 | :support-partial-update nil 95 | :help-info "You should install 'rclone': 96 | 1. Install Go from 'https://golang.org/dl/' 97 | 2. Install rclone with command 'go get github.com/ncw/rclone' 98 | or download and install rclone from 'http://rclone.org/downloads/' directly.")) 99 | "The uploader configure of org2web." 100 | :group 'org2web) 101 | 102 | (defcustom org2web-projects nil 103 | "Association list to control org2web publishing behavior. 104 | 105 | Each element of the alist is a org2web project. The CAR of 106 | each element is a string, uniquely identifying the project. The 107 | CDR of each element is a well-formed property list with an even 108 | number of elements, alternating keys and values, specifying 109 | parameters for the publishing process. 110 | 111 | \(:property value :property value ... ) 112 | 113 | Most properties are optional, but some should always be set: 114 | 115 | 116 | `:repository-directory' 117 | 118 | The repository directory, which containing publishing org files. 119 | 1. Type: string 120 | 2. Example1: \"~/.emacs.d/projects/tumashu.github.com/\" 121 | 122 | 123 | `:ignore' 124 | 125 | Ignore files in repository directory when publish. 126 | 1. Type: regexp list 127 | 2. Example1: (\"regexp1\" \"regexp2\") 128 | 129 | 130 | `:publishing-directory' 131 | 132 | Directory (possibly remote) where html output files will be 133 | published. 134 | 135 | 136 | `:remote' 137 | 138 | A remote, published html file by org2web will be push/upload to. 139 | 1. Type: list 140 | 2. Example1: (git \"https://github.com/tumashu/org2web.git\" \"gh-pages\") 141 | 142 | 143 | `:site-domain' 144 | 145 | The domain name of entire site, it is recommended to assign with prefix 146 | http:// or https://, http will be considered if not assigned. 147 | 1. Type: string 148 | 2. Example1: \"http://tumashu.github.com\" 149 | 150 | `:old-site-domain' 151 | 152 | The domain name which is used before `:site-domain', It may be useful to comment 153 | system, such as: duoshuo. 154 | 1. Type: string 155 | 2. Example1: \"http://tumashu.github.com\" 156 | 157 | 158 | `:site-main-title' 159 | 160 | The main title of entire site. 161 | 1. Type: string 162 | 2. Example1: \"Tumashu's website\" 163 | 164 | 165 | `:site-sub-title' 166 | 167 | The subtitle of entire site. 168 | 1. Type: string 169 | 2. Example1: \"======> My personal blog site.\" 170 | 171 | 172 | `:preparation-function' 173 | 174 | Function to be called before publishing this project. This may also 175 | be a list of functions. 176 | 1. Type: function 177 | 2. Example: eh-convert-el-to-org 178 | 179 | 180 | `:theme-root-directory' 181 | 182 | The root directory list that stores themes for page rendering. By default, it 183 | points to the directory `themes' in org2web installation directory. 184 | 1. Type: list 185 | 2. Example1: (\"/path/to/dir1\" \"/path/to/dir2\" \"/path/to/dir3\" ...) 186 | 3. Example2: nil 187 | 188 | When set this option to `nil', org2web will find two paths as fallback: 189 | 1. /themes 190 | 2. /themes 191 | 192 | 193 | `:theme' 194 | 195 | The theme used for page generation. 196 | 1. Type: list 197 | 2. Example1: (worg killjs) 198 | 3. Example2: nil 199 | 200 | When set this option to `nil', default theme will be used. 201 | 202 | 203 | `:source-browse-url' 204 | 205 | The personal github link. 206 | 1. Type: list 207 | 2. Example1: (\"GitHub\" \"https://github.com/tumashu/org2web\") 208 | 209 | 210 | `:personal-avatar' 211 | 212 | The link to an avatar image. 213 | 1. Type: string 214 | 2. Example1: \"/media/img/horse.jpg\" 215 | 2. Example2: \"http://tumashu.github.com/org2web/media/img/horse.jpg\" 216 | 217 | 218 | `:personal-disqus-shortname' 219 | 220 | The personal disqus shortname. 221 | 1. Type: string 222 | 2. Example1: \"my-disqus-name\" 223 | 224 | 225 | `:personal-duoshuo-shortname' 226 | 227 | The personal duoshuo shortname. 228 | 1. Type: string 229 | 2. Example1: \"my-duoshuo-name\" 230 | 231 | 232 | `:personal-google-analytics-id' 233 | 234 | Personal google analytics id. 235 | The personal duoshuo shortname. 236 | 1. Type: string 237 | 2. Example1: \"my-google-analytics-id\" 238 | 239 | 240 | `:confound-email' 241 | 242 | Determine whether email addresses should be confounded or not. 243 | 1. Type: boolean 244 | 2. Example1: t 245 | 246 | When set this option to `t', \"myname@163.com\" will be converted to \"myname 163 com\" 247 | 248 | 249 | `:force-absolute-url' 250 | 251 | Force convert relative url to absolute url in html files by append site domain. 252 | 1. Type: boolean 253 | 2. Example1: t 254 | 255 | When set this option to `t', all url like \"/path/to/file.html\" will be 256 | converted to \"http://yourdomain.com/path/to/file.html\". 257 | 258 | 259 | `:default-category' 260 | 261 | If org fils don't set category, default category will be used. 262 | 1. Type: string 263 | 2. Example1: \"blog\" 264 | 3. Example2: \"wiki\" 265 | 4. Example3: \"documents\" 266 | 267 | `:about' 268 | 269 | About page of org-website. 270 | 1. Type: list 271 | 2. Example1: (\"About\" \"/about/\") 272 | 273 | 274 | `:rss' 275 | 276 | RSS page of org-website. 277 | 1. Type: list 278 | 2. Example1: (\"RSS\" \"/rss.xml\") 279 | 280 | 281 | `:summary' 282 | 283 | A summary is a statistic page, Which can be used show pages 284 | based on \"tags\" , \"data\" , \"author\" and so on. 285 | it is similar Micorsoft Excel pivot table feature. 286 | 1. Type: alist 287 | 2. Example1: ((\"tags\" :tags) (\"date\" :date) (\"authors\" :authors)) 288 | 2. Example2: ((\"按标签分类\" :tags) (\"按时间分类\" :date) (\"按作者分类\" :authors)) 289 | 290 | 291 | `:category-ignore-list' 292 | 293 | Ignore subdirs/categories for navigation. 294 | 1. Type: list 295 | 2. Example1: (\"themes\" \"assets\") 296 | 297 | Names in this list will not showed in webpage navbar. 298 | 299 | 300 | `:get-title-function' 301 | 302 | A function used to retrieve an org file's Title, it has no parameter and 303 | run org file buffer. 304 | 1. Type: function 305 | 2. Example1: org2web-get-title 306 | 307 | 308 | `:retrieve-category-function' 309 | 310 | A function used to retrieve an org file's category, its parameter is the 311 | org file's path, if parameter is nil, it should return all categories. 312 | 1. Type: function 313 | 2. Example1: org2web-get-file-category 314 | 315 | 316 | `:org-export-function' 317 | 318 | Set the default function by which org2web export org file to html. 319 | 1. Type: function 320 | 2. Example1: org2web-default-org-export 321 | 322 | 323 | `:html-creator-string' 324 | 325 | Information about the creator of the HTML document. 326 | 1. Type: string 327 | 2. Example1: \"This is an example creator string\" 328 | 329 | `:web-server-docroot' 330 | 331 | org2web can start a web server to test publish, this 332 | set the server document root. 333 | 1. Type: string 334 | 2. Example1: \"~/.emacs.d/org-website-server/org2web/\" 335 | 336 | 337 | `:web-server-port' 338 | 339 | org2web can start a web server to test publish, this 340 | set the server port. 341 | 1. Type: number 342 | 2. Example1: 9876 343 | 344 | 345 | `:el2org-doc-sources' 346 | 347 | Emacs-lisp files from which org files will be generated with the 348 | help of el2org: 349 | 1. Type: regexp-list 350 | 2. Example: (\"org2web.el\" \"org2web-*.el\") 351 | 352 | 353 | `:el2org-doc-tags' 354 | 355 | The content with these tags will be considered when generate org files 356 | from `:el2org-doc-source'. 357 | 1. Type: list 358 | 2. Example: (\"tag1\" \"tag2\" \"tag3\") 359 | 360 | 361 | 362 | `:el2org-readme-sources' 363 | 364 | Emacs-lisp or org file from which README.md will be generated with the 365 | help of el2org: 366 | 1. Type: file name list 367 | 2. Example: (\"org2web.el\") 368 | 369 | NOTE: At the moment, *only* use the *first* element of list, NEED improve. 370 | 371 | 372 | `:el2org-readme-tags' 373 | 374 | The content with these tags will be considered when generate README.md 375 | from `:el2org-readme-source'. 376 | 1. Type: list 377 | 2. Example: (\"tag1\" \"tag2\" \"tag3\") 378 | 379 | 380 | 381 | `:el2org-index-sources' 382 | 383 | Emacs-lisp or org file from which index.org will be generated with the 384 | help of el2org: 385 | 1. Type: file name list 386 | 2. Example: (\"org2web.el\") 387 | 388 | NOTE: At the moment, *only* use the *first* element of list, NEED improve. 389 | 390 | 391 | 392 | `:el2org-index-tags' 393 | 394 | The content with these tags will be considered when generate index.org 395 | from `:el2org-index-source'. 396 | 1. Type: list 397 | 2. Example: (\"tag1\" \"tag2\" \"tag3\") 398 | 399 | 400 | You can see fallback value of above option in `org2web-config-fallback' 401 | 402 | Note: Advanced user can use (:eval form) to config *All* org2web config options, 403 | for example, set `:repository-directory' to: 404 | 405 | (:eval (concat \"~/.emacs.d/project/\" \"tumashu.github.com/\")) 406 | 407 | This feature is very useful in certain case." 408 | :group 'org2web 409 | :type 'alist) 410 | 411 | (defcustom org2web-get-config-option-function 412 | 'org2web-get-config-option-from-alist 413 | "The function used to get config option." 414 | :group 'org2web 415 | :type 'function) 416 | 417 | (defconst org2web-temp-buffer-name "*Org Page Output*" 418 | "Name of the temporary buffer used by org2web.") 419 | 420 | (defconst org2web-load-directory 421 | (cond 422 | (load-file-name (file-name-directory load-file-name)) 423 | ((symbol-file 'org2web-temp-buffer-name) 424 | (file-name-directory (symbol-file 'org2web-temp-buffer-name))) 425 | ((string= (file-name-nondirectory buffer-file-name) "org2web-vars.el") 426 | (file-name-directory buffer-file-name)) 427 | (t nil)) 428 | "The directory where org2web is loaded from.") 429 | 430 | (defvar org2web-category-config-alist 431 | '(("blog" 432 | :show-meta t 433 | :show-comment t 434 | :uri-generator org2web-generate-uri 435 | :uri-template "/blog/%y/%m/%d/%t/" 436 | :sort-by :date ;; how to sort the posts 437 | :category-index t) ;; generate category index or not 438 | ("index" 439 | :show-meta nil 440 | :show-comment nil 441 | :uri-generator org2web-generate-uri 442 | :uri-template "/" 443 | :sort-by :date 444 | :category-index nil) 445 | ("about" 446 | :show-meta nil 447 | :show-comment nil 448 | :uri-generator org2web-generate-uri 449 | :uri-template "/about/" 450 | :sort-by :date 451 | :category-index nil)) 452 | "Configurations for different categories, can and should be customized.") 453 | 454 | (defvar org2web-current-project nil) 455 | (defvar org2web-last-project nil) 456 | (defvar org2web-buffer-name " *org2web buffer*") 457 | 458 | (defvar org2web-publish-to-repository nil) 459 | (defvar org2web-always-use-relative-url nil 460 | "Always use relative url in exported html files, this is useful for 461 | test publish.") 462 | 463 | (defvar org2web-item-cache nil 464 | "The cache for general purpose.") 465 | 466 | (defconst org2web-rss-template " 467 | 468 | 469 | {{title}} 470 | {{link}} 471 | {{description}} 472 | {{date}} 473 | {{date}} 474 | http://www.rssboard.org/rss-specification 475 | org2web static site generator \ 476 | (https://github.com/tumashu/org2web) 477 | {{#items}} 478 | 479 | {{item-title}} 480 | {{item-link}} 481 | {{item-description}} 482 | {{item-update-date}} 483 | {{item-link}} 484 | 485 | {{/items}} 486 | 487 | " 488 | "Template for RSS rendering.") 489 | 490 | (defvar org2web-config-fallback 491 | `(:repository-directory 492 | nil 493 | :ignore ("-pkg\\.org$" "-autoloads\\.org" "#\\..*" "\\.dir-local\\.*") 494 | :publishing-directory nil 495 | :remote nil 496 | :site-domain nil 497 | :old-site-domain nil 498 | :site-main-title "org2web" 499 | :site-sub-title "static site generator" 500 | :theme-root-directory nil 501 | :theme (default) 502 | :source-browse-url nil 503 | :personal-avatar nil 504 | :personal-disqus-shortname nil 505 | :personal-duoshuo-shortname nil 506 | :personal-google-analytics-id nil 507 | :default-category "blog" 508 | :about ("About" "/about/") 509 | :rss ("RSS" "/rss.xml") 510 | :category-ignore-list ("themes" "assets" "uploaders") 511 | :summary (("tags" :tags)) 512 | :confound-email t 513 | :force-absolute-url t 514 | :preparation-function nil 515 | :get-title-function org2web-get-title 516 | :retrieve-category-function org2web-get-file-category 517 | :org-export-function org2web-default-org-export 518 | :web-server-docroot "~/.emacs.d/org2web/www" 519 | :web-server-port 9876 520 | :el2org-doc-sources nil 521 | :el2org-readme-sources nil 522 | :el2org-index-sources nil 523 | :el2org-doc-tags ("README" "devel" "doc" "code") 524 | :el2org-readme-tags ("README") 525 | :el2org-index-tags ("README") 526 | :html-creator-string ,(format "Emacs %s\ 527 | (Org mode %s)" 528 | (format "%s.x" emacs-major-version) 529 | (if (fboundp 'org-version) 530 | (replace-regexp-in-string "\\..*" ".x" (org-version)) 531 | "Unknown Version")) 532 | "If User don't set an option, org2web will use fallback value of this option.")) 533 | 534 | 535 | (provide 'org2web-vars) 536 | 537 | ;;; org2web-vars.el ends here 538 | -------------------------------------------------------------------------------- /org2web-webserver.el: -------------------------------------------------------------------------------- 1 | ;;; org2web-webserver.el --- Test web server required by org2web 2 | 3 | ;; Copyright (C) 2015 Feng Shu 4 | 5 | ;; Author: Feng Shu 6 | ;; Keywords: convenience 7 | ;; Homepage: https://github.com/tumashu/org2web 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see . 21 | 22 | ;;; Commentary: 23 | 24 | ;; org2web-webserver.el is a web server used to test org2web. 25 | 26 | ;;; Code: 27 | (require 'cl-lib) 28 | (require 'url-util) 29 | (require 'browse-url) 30 | (require 'simple-httpd) 31 | (require 'org2web-vars) 32 | (require 'org2web-config) 33 | 34 | (defvar org2web-webserver-last-docroot nil) 35 | (defvar org2web-webserver-last-port nil) 36 | (defvar org2web-current-project) 37 | 38 | (defun org2web-webserver-start (docroot port) 39 | (org2web-webserver-stop) 40 | (httpd-log `(start ,(format "org2web: start webserver at %s" 41 | (current-time-string)))) 42 | (make-network-process 43 | :name (or org2web-current-project "org2web-webserver") 44 | :service port 45 | :server t 46 | :host httpd-host 47 | :family httpd-ip-family 48 | :filter `(lambda (proc string) 49 | (let ((httpd-root ,docroot)) ; *override* `http-root' variable 50 | (httpd--filter proc string))) 51 | :filter-multibyte nil 52 | :coding 'utf-8-unix ; *should* be ISO-8859-1 but that doesn't work 53 | :log 'httpd--log)) 54 | 55 | (defun org2web-webserver-stop () 56 | (interactive) 57 | (let ((name (or org2web-current-project "org2web-webserver"))) 58 | (when (process-status name) 59 | (delete-process name) 60 | (httpd-log `(stop ,(format "org2web: stop webserver at %s" 61 | (current-time-string))))))) 62 | 63 | (defun org2web-webserver-browse (&optional docroot port) 64 | (interactive) 65 | (org2web-webserver-stop) 66 | (let ((docroot (or docroot org2web-webserver-last-docroot) ) 67 | (port (or port org2web-webserver-last-port))) 68 | (when (and docroot port) 69 | (progn 70 | (org2web-webserver-start docroot port) 71 | (setq org2web-webserver-last-docroot docroot) 72 | (setq org2web-webserver-last-port port) 73 | (browse-url-default-browser 74 | (format "http://localhost:%s" port)))))) 75 | 76 | (provide 'org2web-webserver) 77 | 78 | ;;; org2web-webserver.el ends here 79 | -------------------------------------------------------------------------------- /org2web.el: -------------------------------------------------------------------------------- 1 | ;;; org2web.el --- static site generator based on org mode 2 | 3 | ;; * Header 4 | ;; Copyright (C) 2016 Feng Shu, Jorge Javier Araya Navarro 5 | ;; 2015 Feng Shu 6 | ;; 2012, 2013, 2014, 2015 Kelvin Hu 7 | 8 | ;; Author: Feng Shu 9 | ;; Jorge Javier Araya Navarro 10 | ;; Kelvin Hu 11 | ;; Keywords: org-mode, convenience, beautify 12 | ;; Homepage: https://github.com/tumashu/org2web 13 | ;; Package-Requires: ((cl-lib "1.0") (ht "1.5") (mustache "0.22") (htmlize "1.47") (org "8.0") (dash "2.0.0") (el2org "0.10") (simple-httpd "0.1")) 14 | 15 | ;; This program is free software; you can redistribute it and/or modify 16 | ;; it under the terms of the GNU General Public License as published by 17 | ;; the Free Software Foundation, either version 3 of the License, or 18 | ;; (at your option) any later version. 19 | 20 | ;; This program is distributed in the hope that it will be useful, 21 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | ;; GNU General Public License for more details. 24 | 25 | ;; You should have received a copy of the GNU General Public License 26 | ;; along with this program. If not, see . 27 | 28 | ;;; Commentary: 29 | 30 | ;; * org2web README :README: 31 | ;; org2web is the new name of org-webpage, the reason of renaming org-webpage to org2web 32 | ;; is: [[https://github.com/purcell/package-lint/issues/75]] 33 | 34 | ;; org2web is a static site generator based on [[http://orgmode.org/][org-mode]], 35 | ;; which code derived from Kelvin H's [[https://github.com/kelvinh/org-page][org-page]]. 36 | 37 | ;; The main differents of two projects are as follow: 38 | 39 | ;; 1. org2web's core *don't hard code git*, its process is like below: 40 | ;; #+BEGIN_EXAMPLE 41 | 42 | ;; [ Org files in repository] [ Website project configure ] 43 | 44 | ;; | | 45 | ;; < Export > < Generate > 46 | ;; | | 47 | 48 | ;; [ HTML files ] [ Uploader ] <- ( Uploader is a bash script ) 49 | 50 | ;; | | 51 | ;; | | 52 | ;; +-------------+-------------+ 53 | ;; | 54 | ;; | 55 | ;; < Run Uploader > <- ( For example: git uploader, rclone uploader or others ) 56 | ;; | 57 | ;; | 58 | 59 | ;; [ REMOTE ] 60 | 61 | ;; #+END_EXAMPLE 62 | 63 | ;; 2. org2web's default config is `org-publish-project-alist' style alist, 64 | ;; which can manage multi-site configs in an emacs session easily. 65 | ;; 3. org-website find theme-files from a *themes-list* in sequence and same theme-file 66 | ;; first found will be used. User can set *fallback theme* with the help of this feature. 67 | ;; 4. org-website include a tiny emacs web server, which can be used to test publish. 68 | ;; 5. org-website can use other uploaders to upload website, for example: rclone. 69 | ;; 6. ... 70 | 71 | ;; ** Installation 72 | 73 | ;; org2web is now available from the famous emacs package repo [[http://melpa.milkbox.net/][melpa]] 74 | ;; so the recommended way is to install it through emacs package 75 | ;; management system. For more info about installation, please see 76 | ;; *tips.org* in the "doc" folder. 77 | 78 | ;; ** Configuration 79 | ;; org2web use variable `org2web-projects' to store all projects's configures, user 80 | ;; can add a project with the help of `add-to-list' function, but the easiest way is 81 | ;; using `org2web-add-project' function. 82 | 83 | ;; The follow code is [[http://tumashu.github.com][my website]]'s [[https://github.com/tumashu/tumashu.github.com/blob/source/eh-website.el][config]], 84 | ;; you can adjust and paste it to your =.emacs= file: 85 | 86 | ;; #+BEGIN_EXAMPLE 87 | ;; (add-to-list 'load-path "path/to/org2web") ; Only needed if you install org2web manually 88 | 89 | ;; (require 'org2web) 90 | 91 | ;; (org2web-add-project 92 | ;; '("tumashu.github.com" 93 | ;; :repository-directory "~/project/emacs-packages/tumashu.github.com" 94 | ;; :remote (git "https://github.com/tumashu/tumashu.github.com.git" "master") 95 | ;; ;; you can use `rclone` with `:remote (rclone "remote-name" "/remote/path/location")` instead. 96 | ;; :site-domain "http://tumashu.github.com/" 97 | ;; :site-main-title "Tumashu 的个人小站" 98 | ;; :site-sub-title "(九天十地,太上忘情!!!)" 99 | ;; :theme (worg) 100 | ;; :source-browse-url ("Github" "https://github.com/tumashu/tumashu.github.com") 101 | ;; :personal-avatar "/media/img/horse.jpg" 102 | ;; :personal-duoshuo-shortname "tumashu-website" 103 | ;; :web-server-port 7654)) 104 | ;; #+END_EXAMPLE 105 | 106 | ;; [[https://github.com/tumashu/pyim][pyim]] 's org2web [[https://github.com/tumashu/pyim/blob/master/pyim-devtools.el][config]] is a more complex example. 107 | 108 | ;; You can find more config options and theirs default values by commands: 109 | 110 | ;; #+BEGIN_EXAMPLE 111 | ;; C-h v org2web-projects 112 | ;; C-h v org2web-config-fallback 113 | ;; #+END_EXAMPLE 114 | 115 | ;; ** Publication 116 | 117 | ;; #+BEGIN_EXAMPLE 118 | ;; M-x org2web-publish 119 | ;; #+END_EXAMPLE 120 | 121 | ;; ** Dependencies 122 | 123 | ;; 1. [[http://www.gnu.org/software/emacs/][emacs]]: this is an "of-course" dependency 124 | ;; 2. [[http://orgmode.org/][org mode]]: v8.0 is required, please use =M-x org-version = to make sure you org mode version is not less than 8.0 125 | ;; 3. [[http://www.gnu.org/software/bash/][bash]]: the GNU Project's shell 126 | ;; 4. [[http://git-scm.com][git]]: a free and open source version control system 127 | ;; 5. [[http://rclone.org/downloads/][rclone]]: support to other remote locations, see rclone's overview for more information. (Optional) 128 | ;; 6. [[https://github.com/Wilfred/mustache.el][mustache.el]]: a mustache templating library for Emacs 129 | ;; 7. [[http://fly.srk.fer.hr/~hniksic/emacs/htmlize.el.cgi][htmlize.el]]: a library for syntax highlighting (usually this library is shipped with emacs) 130 | ;; 8. [[https://github.com/magnars/dash.el][dash.el]]: a modern list library for Emacs 131 | ;; 9. [[https://github.com/Wilfred/ht.el][ht.el]]: a modern hash-table library for Emacs 132 | ;; 10. [[https://github.com/skeeto/emacs-web-server][simple-httpd]]: Extensible Emacs HTTP 1.1 server 133 | 134 | ;; ** Known issues 135 | 136 | ;; 1. Currently the deletion change handler has not been implemented so 137 | ;; if you deleted some org sources, you may have to manually delete 138 | ;; corresponding generated html files. 139 | ;; 2. URI path change detection is not available. That is, if you make a 140 | ;; post with the URI "/blog/2013/03/25/the-old-post-name" and then 141 | ;; change this value in your org source, org2web would be unable to 142 | ;; detect that this has happened. it will only publish a new html 143 | ;; file for you so you need to delete the old html file related to 144 | ;; the old URI manually. 145 | 146 | 147 | ;;; Code: 148 | 149 | ;; * 代码说明 :code: 150 | 151 | (require 'cl-lib) 152 | (require 'ox) 153 | (require 'ht) 154 | (require 'org2web-util) 155 | (require 'org2web-vars) 156 | (require 'org2web-config) 157 | (require 'org2web-resource) 158 | (require 'org2web-export) 159 | (require 'org2web-webserver) 160 | (require 'org2web-el2org) 161 | 162 | 163 | (defconst org2web-version "0.1") 164 | 165 | ;;;###autoload 166 | (defun org2web-add-project (project-config) 167 | "Add `project-config' to `org2web-projects'" 168 | (if (listp project-config) 169 | (let ((project-name (car project-config))) 170 | (when (stringp project-name) 171 | (setq org2web-projects 172 | (remove (assoc project-name org2web-projects) 173 | org2web-projects))) 174 | (add-to-list 'org2web-projects project-config)) 175 | (message "Invalid project config!"))) 176 | 177 | ;;;###autoload 178 | (defun org2web-select-project (prompt &optional project-name) 179 | "Let user select a project then return its name." 180 | (setq org2web-current-project nil) 181 | (setq project-name 182 | (or project-name 183 | org2web-default-project 184 | (completing-read prompt 185 | (delete-dups 186 | (mapcar 'car org2web-projects)) 187 | nil t nil nil org2web-last-project))) 188 | (setq org2web-current-project project-name 189 | org2web-last-project project-name) 190 | project-name) 191 | 192 | ;;;###autoload 193 | (defun org2web-publish (&optional project-name publishing-directory job-number update-top-n) 194 | (interactive) 195 | (setq project-name (org2web-select-project "Which project do you want to publish? " project-name)) 196 | (setq org2web-item-cache nil) 197 | 198 | (org2web-verify-configuration) 199 | (let* ((remote (org2web-get-config-option :remote)) 200 | (uploader-config (cdr (assoc (nth 0 remote) org2web-uploaders))) 201 | (support-partial-update (plist-get uploader-config :support-partial-update)) 202 | (jobs (if support-partial-update 203 | '((1 . "Full publish") 204 | (2 . "Partial publish") 205 | (3 . "Test full publish") 206 | (4 . "Test partial publish") 207 | (5 . "Upload latest publish")) 208 | '((1 . "Full publish") 209 | (2 . "Test full publish") 210 | (3 . "Upload latest publish")))) 211 | (job-used (completing-read 212 | "Which job do you want to active: " 213 | (mapcar #'cdr jobs))) 214 | (job-number (car (rassoc job-used jobs))) 215 | (test-publish (if support-partial-update 216 | (or (= job-number 3) 217 | (= job-number 4)) 218 | (= job-number 2))) 219 | (partial-update (and support-partial-update 220 | (or (= job-number 2) 221 | (= job-number 4)))) 222 | (upload-latest-publish (if support-partial-update 223 | (= job-number 5) 224 | (= job-number 3))) 225 | (repo-dir (org2web-get-repository-directory)) 226 | (publish-root-dir (org2web-get-uploader-directory project-name)) 227 | (export-dir (org2web-get-uploader-directory project-name "export")) 228 | (history-dir (org2web-get-uploader-directory project-name "history")) 229 | (publishing-directory 230 | (when publishing-directory 231 | (expand-file-name publishing-directory))) 232 | (publish-dir 233 | (or publishing-directory 234 | (org2web-get-uploader-directory project-name "publish") 235 | (org2web-get-publishing-directory))) 236 | (test-publish-dir (org2web-get-uploader-directory project-name "test-publish")) 237 | (uploader-file (concat publish-root-dir "org2web-uploader.sh")) 238 | (site-domain (org2web-get-site-domain)) 239 | (preparation-function (org2web-get-config-option :preparation-function)) 240 | (repo-files 241 | (unless upload-latest-publish 242 | (when preparation-function 243 | (run-hooks 'preparation-function)) 244 | (org2web-sort-files (org2web-remove-matched-items 245 | (org2web-directory-files-recursively repo-dir "\\.org$") 246 | (org2web-get-config-option :ignore))))) 247 | (length-repo-files (length repo-files)) 248 | (update-top-n 249 | (cond ((and partial-update (numberp update-top-n)) update-top-n) 250 | (partial-update (org2web-read-top-n 251 | "org2web will update TOP (N) org-files, Please type N: " 252 | repo-files repo-dir)))) 253 | (changed-files (if (numberp update-top-n) 254 | (cl-subseq repo-files 0 (min update-top-n length-repo-files)) 255 | repo-files))) 256 | 257 | (if upload-latest-publish 258 | (org2web-delete-directory 259 | history-dir publish-dir test-publish-dir) 260 | (org2web-delete-directory publish-root-dir) 261 | (org2web-make-directory export-dir)) 262 | 263 | (org2web-make-directory 264 | history-dir publish-dir test-publish-dir) 265 | 266 | (if test-publish 267 | (let ((org2web-always-use-relative-url t) ; Local test website, can't use absolute path. 268 | (port (or (org2web-get-config-option :web-server-port) 269 | (org2web-get-random-number 4)))) 270 | (org2web-prepare-theme-resources test-publish-dir) 271 | (org2web-publish-changes repo-files changed-files test-publish-dir) 272 | (org2web-webserver-browse test-publish-dir port)) 273 | (unless upload-latest-publish 274 | (org2web-prepare-theme-resources export-dir) 275 | (org2web-publish-changes repo-files changed-files export-dir)) 276 | (org2web-generate-and-run-uploader 277 | uploader-file remote export-dir history-dir publish-dir partial-update)))) 278 | 279 | (defun org2web-generate-and-run-uploader (uploader-file remote export-dir history-dir publish-dir partial-update) 280 | "Generate shell script UPLOADER-FILE then RUN it, the uploader is used to upload html files 281 | generated by org2web to REMOTE." 282 | (if (not (and uploader-file remote export-dir history-dir publish-dir)) 283 | (message "Can't generate org2web uploader file.") 284 | (let* ((uploader-name (nth 0 remote)) 285 | (uploader-names (mapcar #'car org2web-uploaders)) 286 | (uploader-config (cdr (assoc uploader-name org2web-uploaders))) 287 | (uploader-requires (plist-get uploader-config :requires)) 288 | (uploader-help-info (plist-get uploader-config :help-info)) 289 | (uploader-template 290 | (org2web-file-to-string 291 | (org2web-get-uploader-template 292 | (or (plist-get uploader-config :template) 293 | (concat (symbol-name uploader-name) ".mustache"))))) 294 | (uploader-template-settings 295 | (plist-get uploader-config :template-settings))) 296 | (if (not (member uploader-name uploader-names)) 297 | (message (format "Uploader name %S in :remote is not recognized, please choice one from %S." 298 | uploader-name uploader-names)) 299 | (if (cl-some #'(lambda (x) 300 | (not (executable-find x))) 301 | uploader-requires) 302 | (message uploader-help-info) 303 | ;; Generate uploader file 304 | (org2web-string-to-file 305 | (mustache-render 306 | uploader-template 307 | (funcall uploader-template-settings ; it's a function. 308 | remote export-dir history-dir publish-dir partial-update)) 309 | uploader-file) 310 | ;; Run Uploader file 311 | (if (and (file-exists-p uploader-file) 312 | (executable-find "bash")) 313 | (cond 314 | ((string-equal system-type "windows-nt") 315 | (w32-shell-execute "open" (replace-regexp-in-string "/" "\\" uploader-file t t))) 316 | ((and (string-equal system-type "gnu/linux") 317 | org2web-terminal-emulater) 318 | (start-process-shell-command 319 | "org2web-uploader-script" 320 | nil 321 | (format "%s -e 'bash %s'" 322 | org2web-terminal-emulater 323 | uploader-file)))) 324 | (message "Can't run org2web uploader, user should install 'bash' correctly."))))))) 325 | 326 | (defun org2web-verify-configuration () 327 | "Ensure all required configuration fields are properly configured, include: 328 | 1. `:repository-directory': 329 | 2. `:site-domain': 330 | 3. `:personal-disqus-shortname': 331 | 4. `:personal-duoshuo-shortname': 332 | 5. `:site-main-title': [optional] (but customization recommanded) 333 | 6. `:site-sub-title': [optional] (but customization recommanded) 334 | 7. `:personal-github-link': [optional] (but customization recommended) 335 | 8. `:personal-google-analytics-id': [optional] (but customization recommended) 336 | 9. `:theme': [optional]" 337 | (unless (member org2web-current-project 338 | (mapcar 'car org2web-projects)) 339 | (error "Can't find project: \"%s\"" org2web-current-project)) 340 | (let ((repo-dir (org2web-get-repository-directory)) 341 | (site-domain (org2web-get-site-domain))) 342 | (unless (and repo-dir (file-directory-p repo-dir)) 343 | (error "Repository directory is not properly configured.")) 344 | (unless site-domain 345 | (error "Site domain is not properly configured.")))) 346 | 347 | (defun org2web-generate-readme (save-dir) 348 | "Generate README for `org2web-new-repository'. SAVE-DIR is the directory where to 349 | save generated README." 350 | (org2web-string-to-file 351 | (concat 352 | (format "Personal site of %s, managed by emacs, org mode, git and org2web." 353 | (or user-full-name "[Author]")) 354 | "\n\n" 355 | "This git repository is generated by org2web \"org2web-new-repository\" \ 356 | function, it is only used for demonstrating how the git branches and directory \ 357 | structure are organized by org2web.") 358 | (expand-file-name "README" save-dir))) 359 | 360 | (defun org2web-generate-index (save-dir) 361 | "Generate index.org for `org2web-new-repository'. SAVE-DIR is the directory where 362 | to save generated index.org." 363 | (org2web-string-to-file 364 | (concat "#+TITLE: Index" "\n\n" 365 | (format "This is the home page of %s." 366 | (or user-full-name "[Author]"))) 367 | (expand-file-name "index.org" save-dir))) 368 | 369 | (defun org2web-generate-about (save-dir) 370 | "Generate about.org for `org2web-new-repository'. SAVE-DIR is the directory where 371 | to save generated about.org." 372 | (org2web-string-to-file 373 | (concat "#+TITLE: About" "\n\n" 374 | (format "* About %s" (or user-full-name "[Author]")) "\n\n" 375 | " This file is automatically generated by org2web.") 376 | (expand-file-name "about.org" save-dir))) 377 | 378 | (defun org2web-insert-options-template (&optional title uri 379 | keywords tags description) 380 | "Insert a template into current buffer with information for exporting. 381 | 382 | TITLE: the title of this post 383 | URI: the uri of this post, usually looks like: /2013/12/27/the-post-title, 384 | the following parameters could be used: 385 | %y: to represent the year of creation date 386 | %m: to represent the month of creation date 387 | %d: to represent the day of creation date 388 | KEYWORDS: the keywords of this post, used by search engine 389 | TAGS: the tags of this post, should be separated by comma and space 390 | DESCRIPTION: the description of this post, it will be displayed in RSS feed 391 | 392 | Note that this function does not verify the input parameters, it is users' 393 | responsibility to guarantee these parameters are valid." 394 | (interactive 395 | (let* ((i (read-string "Title: ")) 396 | (u (read-string "URI(%y, %m and %d can be used to represent year, \ 397 | month and day): " (unless (string= i "") 398 | (format-spec "/%c/%y/%m/%d/%t" 399 | `((?c . ,(org2web-get-config-option :default-category)) 400 | (?y . "%y") 401 | (?m . "%m") 402 | (?d . "%d") 403 | (?t . ,(org2web-encode-string-to-url i))))))) 404 | (k (read-string "Keywords(separated by comma and space [, ]): ")) 405 | (a (read-string "Tags(separated by comma and space [, ]): ")) 406 | (d (read-string "Description: "))) 407 | (list i u k a d))) 408 | (if (not (bolp)) (newline)) 409 | (insert (format 410 | (replace-regexp-in-string 411 | "#\\+\\+" "#+" 412 | "# -*- coding: utf-8-unix; -*- 413 | #++TITLE: %s 414 | #++AUTHOR: %s 415 | #++EMAIL: %s 416 | #++DATE: %s 417 | 418 | # #++URI: %s 419 | # #++KEYWORDS: %s 420 | # #++TAGS: %s 421 | # #++DESCRIPTION: %s 422 | 423 | #++LANGUAGE: %s 424 | #++OPTIONS: H:%d num:%s toc:%s \\n:%s ::%s |:%s ^:%s -:%s f:%s *:%s <:%s 425 | ") 426 | (if (string= title "") (buffer-name) title) 427 | (user-full-name) 428 | user-mail-address 429 | (format-time-string (substring (car org-time-stamp-formats) 1 -1)) 430 | (if (string= uri "") "" uri) 431 | (if (string= keywords "") 432 | "" 433 | keywords) 434 | (if (string= tags "") "" tags) 435 | (if (string= description "") 436 | "" 437 | description) 438 | org-export-default-language 439 | 7 ;; Set default level to 7 instead of `org-export-headline-levels' 440 | nil ;; org-export-with-section-numbers 441 | nil ;; org-export-with-toc 442 | org-export-preserve-breaks 443 | ;; org-export-html-expand 444 | org-export-with-fixed-width 445 | org-export-with-tables 446 | nil ;; org-export-with-sub-superscripts 447 | nil ;; org-export-with-special-strings 448 | org-export-with-footnotes 449 | org-export-with-emphasize 450 | org-export-with-timestamps))) 451 | 452 | ;;;###autoload 453 | (defun org2web-new-post (&optional project-name category filename insert-fallback-template) 454 | "Setup a new post. 455 | 456 | PROJECT-NAME: which project do you want to export 457 | CATEGORY: this post belongs to 458 | FILENAME: the file name of this post 459 | 460 | Note that this function does not verify the category and filename, it is users' 461 | responsibility to guarantee the two parameters are valid." 462 | (interactive 463 | (let* ((p (org2web-select-project "Which project do you want post? ")) 464 | (c (read-string (format "Category of \"%s\" project: " p) 465 | (org2web-get-config-option :default-category))) 466 | (f (read-string (format "Filename of \"%s\" project: " p) "new-post.org" p)) 467 | (d (yes-or-no-p "Insert fallback template? "))) 468 | (list p c f d))) 469 | (if (string= category "") 470 | (setq category (org2web-get-config-option :default-category))) 471 | (if (string= filename "") 472 | (setq filename "new-post.org")) 473 | (unless (org2web-string-suffix-p ".org" filename) 474 | (setq filename (concat filename ".org"))) 475 | (let* ((repo-dir (org2web-get-repository-directory)) 476 | (dir (concat (file-name-as-directory repo-dir) 477 | (file-name-as-directory category))) 478 | (path (concat dir filename))) 479 | (if (file-exists-p path) 480 | (error "Post `%s' already exists." path)) 481 | (unless (file-directory-p dir) 482 | (mkdir dir t)) 483 | (switch-to-buffer (find-file path)) 484 | (if (and (not insert-fallback-template) 485 | (called-interactively-p 'any)) 486 | (call-interactively 'org2web-insert-options-template) 487 | (org2web-insert-options-template "" 488 | (format "/%s/%%y/%%m/%%d/%%t/ Or /%s/%%t/" 489 | category category) 490 | "keyword1, keyword2, keyword3" 491 | "tag1, tag2, tag3" 492 | "")) 493 | (save-buffer))) 494 | 495 | 496 | 497 | 498 | ;; * Footer 499 | (provide 'org2web) 500 | 501 | ;;; org2web.el ends here 502 | -------------------------------------------------------------------------------- /themes/default/resources/css/main.css: -------------------------------------------------------------------------------- 1 | /****************************************************** 2 | * I copied these styles from markdotto.com, who is 3 | * the creator of bootstrap, and I also did some 4 | * modification. Thanks Mark for the beautiful theme, 5 | * so I reserved the @mdo sign below. 6 | *****************************************************/ 7 | 8 | /* 9 | __ 10 | __ /\ \ 11 | /'_`\_ ___ ___ \_\ \ ___ 12 | /'/'_` \ /' __` __`\ /'_` \ / __`\ 13 | /\ \ \L\ \/\ \/\ \/\ \/\ \L\ \/\ \L\ \ 14 | \ \ `\__,_\ \_\ \_\ \_\ \___,_\ \____/ 15 | \ `\_____\\/_/\/_/\/_/\/__,_ /\/___/ 16 | `\/_____/ 17 | 18 | */ 19 | 20 | 21 | 22 | /* Body resets 23 | -------------------------------------------------- */ 24 | 25 | * { 26 | -webkit-box-sizing: border-box; 27 | -moz-box-sizing: border-box; 28 | box-sizing: border-box; 29 | } 30 | html, body { 31 | margin: 0; 32 | padding: 0; 33 | } 34 | html { 35 | font-size: 62.5%; 36 | } 37 | body { 38 | padding: 20px; 39 | font-family: Menlo, Monaco, monospace; 40 | font-size: 14px; 41 | line-height: 1.5; 42 | color: #666; 43 | background-color: #fff; 44 | } 45 | p { 46 | margin: 0 0 20px; 47 | } 48 | h1, h2, h3 { 49 | position: relative; 50 | margin: 30px 0 20px; 51 | font-size: 18px; 52 | font-weight: bold; 53 | line-height: 1.1; 54 | color: #111; 55 | text-rendering: optimizeLegibility; 56 | } 57 | h1:before, h2:before, h3:before { 58 | position: absolute; 59 | top: auto; 60 | color: #ccc; 61 | display: none; 62 | } 63 | h1:before { 64 | content: "*"; 65 | left: -1.25em; 66 | } 67 | h2:before { 68 | content: "**"; 69 | left: -2em; 70 | } 71 | h3:before { 72 | content: "***"; 73 | left: -2.5em; 74 | } 75 | i, em, b, strong { 76 | padding-left: .1em; 77 | padding-right: .1em; 78 | } 79 | b, strong { 80 | font-weight: bold; 81 | color: #333; 82 | } 83 | b:before, b:after, strong:before, strong:after { 84 | content: "*"; 85 | } 86 | i:before, i:after, em:before, em:after { 87 | content: "/"; 88 | } 89 | hr { 90 | margin: 0; 91 | border: 0; 92 | } 93 | hr:after { 94 | display: block; 95 | margin: 20px 0; 96 | content: "----------------------------------------------------------------"; 97 | color: #ccc; 98 | } 99 | ul, ol { 100 | padding: 0; 101 | margin: 0 0 20px; 102 | } 103 | code, pre { 104 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 105 | } 106 | code { 107 | color: #d14; 108 | } 109 | code:before, code:after { 110 | content: "="; 111 | color: #ccc; 112 | } 113 | pre { 114 | display: block; 115 | margin: 0 0 14px; 116 | padding: 15px; 117 | border-radius: 3px 3px 3px 3px; 118 | font-size: 18px; 119 | line-height: 28px; 120 | background-color: #f5f5f5; 121 | white-space: pre; 122 | white-space: pre-wrap; 123 | word-break: break-all; 124 | word-wrap: break-word; 125 | } 126 | blockquote { 127 | padding: 0 0 0 20px; 128 | margin: 0 30px 20px -22.5px; 129 | color: #999; 130 | border-left: 8px solid #e5e5e5; 131 | } 132 | blockquote p:last-child { 133 | margin-bottom: 0; 134 | } 135 | img { 136 | display: block; 137 | margin: 0; 138 | border-radius: 5px; 139 | } 140 | a { 141 | font-weight: inherit; 142 | line-height: inherit; 143 | color: #000; 144 | text-decoration: none; 145 | } 146 | sup a:before { 147 | content: "["; 148 | } 149 | sup a:after { 150 | content: "]"; 151 | } 152 | a:hover { 153 | text-decoration: underline; 154 | } 155 | table { 156 | line-height: 2.5; 157 | margin-bottom: 15px; 158 | max-width: 100%; 159 | width: 100%; 160 | } 161 | table td, table th { 162 | padding: 0 15px; 163 | } 164 | table th.left, table td.left { 165 | text-align: left; 166 | } 167 | table th.right, table td.right { 168 | text-align: right; 169 | } 170 | 171 | span.tags-links { 172 | display:block; 173 | float:right; 174 | text-align:right; 175 | } 176 | 177 | /******************************************************/ 178 | /* body */ 179 | .container { 180 | margin: 0 auto; 181 | max-width: 770px; 182 | } 183 | /* head */ 184 | .masthead { 185 | color: #888; 186 | } 187 | .masthead:after { 188 | display: block; 189 | content: '----------------------------------------------------------------'; 190 | margin: 10px 0; 191 | color: #ccc; 192 | } 193 | .masthead a { 194 | color: #333; 195 | } 196 | .masthead ul { 197 | list-style: none; 198 | } 199 | .masthead li { 200 | display: inline-block; 201 | } 202 | .masthead li + li:before { 203 | display: inline-block; 204 | content: "\00B7 \00a0"; 205 | color: #ccc; 206 | } 207 | /* searchform*/ 208 | #searchform { 209 | position: absolute; 210 | top: 54px; 211 | right: 30.4%; 212 | } 213 | #searchform #s { 214 | background: url("/media/img/search.png") no-repeat 5px 6px; 215 | box-shadow: inset 0 1px 1px rgba(0,0,0,0.1); 216 | border: 1px solid #ddd; 217 | border-radius: 2px; 218 | -moz-border-radius: 2px; 219 | width: 90px; 220 | height: 30px; 221 | line-height: 12px; 222 | padding: 4px 10px 4px 28px; 223 | 224 | -webkit-transition-duration: 400ms; 225 | -webkit-transition-property: width, background; 226 | -webkit-transition-timing-function: ease; 227 | -moz-transition-duration: 400ms; 228 | -moz-transition-property: width, background; 229 | -moz-transition-timing-function: ease; 230 | -o-transition-duration: 400ms; 231 | -o-transition-property: width, background; 232 | -o-transition-timing-function: ease; 233 | } 234 | #searchform #s:focus { 235 | background-color: #f9f9f9; 236 | width: 160px; 237 | } 238 | .masthead img.avatar { 239 | position: absolute; 240 | width: 200px; 241 | top: 100px; 242 | right: 29%; 243 | background-color: #fff; 244 | } 245 | /* post */ 246 | .post { 247 | position: relative; 248 | } 249 | .post:after { 250 | display: block; 251 | content: "----------------------------------------------------------------"; 252 | color: #ccc; 253 | } 254 | .post img { 255 | max-width: 100%; 256 | } 257 | .post li { 258 | list-style: none outside none; 259 | } 260 | .post ul > li:before { 261 | content: "- "; 262 | margin-left: -1.25em; 263 | color: #ccc; 264 | } 265 | .post ol { 266 | counter-reset: o-list; 267 | } 268 | .post ol > li:before { 269 | content: counter(o-list) ") "; 270 | counter-increment: o-list; 271 | margin-left: -1.85em; 272 | color: #ccc; 273 | } 274 | 275 | /* special for li in pre */ 276 | .post pre li { 277 | list-style-type: decimal; 278 | } 279 | 280 | .post pre li:before { 281 | display: none; 282 | } 283 | 284 | /* meta info */ 285 | .post-info { 286 | display: inline-block; 287 | margin: -10px 0 -10px; 288 | color: #999; 289 | } 290 | .post-info + .post-info:before { 291 | content: "\2223 \00a0"; 292 | } 293 | .post-meta:after { 294 | content: "----------------------------------------------------------------"; 295 | display: block; 296 | margin-top: -1px; 297 | color: #ccc; 298 | } 299 | 300 | /* table of content */ 301 | #table-of-contents { 302 | position: fixed; 303 | right: 0em; 304 | top: 0em; 305 | border:1px solid #e1e1e8; 306 | -webkit-box-shadow: 0 0 1em #777777; 307 | -moz-box-shadow: 0 0 1em #777777; 308 | -webkit-border-bottom-left-radius: 5px; 309 | -moz-border-radius-bottomleft: 5px; 310 | text-align: right; 311 | /* ensure doesn't flow off the screen when expanded */ 312 | max-height: 80%; 313 | overflow: auto; 314 | z-index: 200; 315 | } 316 | 317 | #table-of-contents h2 { 318 | max-width: 20em; 319 | font-weight: normal; 320 | padding-left: 0.5em; 321 | padding-top: 0.05em; 322 | padding-bottom: 0.05em; 323 | } 324 | 325 | #table-of-contents ul { 326 | margin-left: 14pt; 327 | margin-bottom: 10pt; 328 | padding: 0 329 | } 330 | 331 | #table-of-contents li { 332 | padding: 0; 333 | margin: 1px; 334 | list-style: none; 335 | } 336 | 337 | #table-of-contents ul>:first-child { 338 | color: blue; 339 | } 340 | 341 | #table-of-contents #text-table-of-contents { 342 | display: none; 343 | text-align: left; 344 | } 345 | 346 | #table-of-contents:hover #text-table-of-contents { 347 | display: block; 348 | padding: 0.5em; 349 | margin-top: -1.5em; 350 | } 351 | 352 | /* footer */ 353 | .footer { 354 | margin: 80px 0 0; 355 | text-align: center; 356 | font-size: 15px; 357 | color: #999; 358 | } 359 | .footer > p { 360 | margin: 0; 361 | line-height: 1.5; 362 | } 363 | .footpara { 364 | display: inline; 365 | } 366 | 367 | /* Responsive 368 | -------------------------------------------------- */ 369 | 370 | @media (min-width: 768px) { 371 | /* Increase body padding and font-sizes */ 372 | body { 373 | padding: 30px; 374 | font-size: 18px; 375 | line-height: 1.75; 376 | } 377 | h1:before, h2:before, h3:before { 378 | display: inline; 379 | } 380 | } 381 | 382 | @media (min-width: 1024px) { 383 | body { 384 | padding: 30px; 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /themes/default/resources/css/prettify.css: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. 2 | * 3 | * This version is slight modified based on the original version. 4 | * 5 | * Name: Stanley Ng 6 | * Email: stanleyhlng@googlegroups.com 7 | * 8 | * Reference: 9 | * http://code.google.com/p/google-code-prettify/source/browse/trunk/src/prettify.css 10 | */ 11 | .pln { 12 | color: #bd3613; } 13 | 14 | .str { 15 | color: #269186; } 16 | 17 | .kwd { 18 | color: #859900; } 19 | 20 | .com { 21 | color: #586175; 22 | font-style: italic; } 23 | 24 | .typ { 25 | color: #b58900; } 26 | 27 | .lit { 28 | color: #2aa198; } 29 | 30 | .pun { 31 | color: #839496; } 32 | 33 | .opn { 34 | color: #839496; } 35 | 36 | .clo { 37 | color: #839496; } 38 | 39 | .tag { 40 | color: #268bd2; } 41 | 42 | .atn { 43 | color: #586175; } 44 | 45 | .atv { 46 | color: #2aa198; } 47 | 48 | .dec { 49 | color: #268bd2; } 50 | 51 | .var { 52 | color: #268bd2; } 53 | 54 | .fun { 55 | color: #FF0000; } 56 | 57 | /* Put a border around prettyprinted code snippets. */ 58 | pre.prettyprint { 59 | background-color: #042029; 60 | padding: 10px; 61 | border: 1px solid #E1E1E8; } 62 | 63 | /* Specify class=linenums on a pre to get line numbering */ 64 | ol.linenums { 65 | color: #4c666c; 66 | margin: 0 0 0 40px; } 67 | 68 | ol.linenums li { 69 | line-height: 18px; 70 | padding-left: 12px; } -------------------------------------------------------------------------------- /themes/default/resources/img/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tumashu/org2web/0e343770b9785f6150afc98153cd00a316d88d01/themes/default/resources/img/search.png -------------------------------------------------------------------------------- /themes/default/resources/js/main.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | /******************************************************************* 3 | * 1. replace css class "src" and "example" with "prettyprint", for 4 | * prettify.js to render 5 | * 2. replace detail language css class, e.g. "src-scheme" to 6 | * "lang-scheme" per the description of prettify.js 7 | ******************************************************************/ 8 | var $blocks = $('pre.src'); 9 | $blocks.each(function(index) { 10 | var self = $(this); 11 | var classes = self.removeClass('src').attr('class').split(/\s+/); 12 | $.each(classes, function(idx, cls) { 13 | if (cls.substring(0, 4) === 'src-') { 14 | var lang = cls.substring(4); 15 | self.removeClass(cls).addClass('lang-' + lang); 16 | } 17 | }); 18 | self.addClass('prettyprint'); 19 | }); 20 | $('pre.example').removeClass('example').addClass('prettyprint'); 21 | 22 | /******************************************************************* 23 | * 1. remove all org exported line number spans 24 | * 2. add css class "linenums" to code block per the description of 25 | * prettify.js 26 | ******************************************************************/ 27 | var $lines = $('span.linenr'); 28 | var $linedBlocks = $lines.parent(); 29 | $lines.remove(); 30 | $linedBlocks.each(function(index) { 31 | $(this).addClass('linenums'); 32 | }); 33 | 34 | /******************************************************************* 35 | * pretty print all code blocks 36 | ******************************************************************/ 37 | prettyPrint(); 38 | }); 39 | -------------------------------------------------------------------------------- /themes/default/templates/about.mustache: -------------------------------------------------------------------------------- 1 |
2 |

About {{author}}

3 |
4 |

{{author}} is a little lazy, he/she leaves nothing here. :-p

5 |
6 |

This page is automatically generated by org2web.

7 |
8 |
-------------------------------------------------------------------------------- /themes/default/templates/category-index.mustache: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{cat-name}} Index

4 |
    5 | {{#posts}} 6 |
  • 7 | 8 | {{date}}  »  {{post-title}} 9 | 10 | {{#tag-links}} 11 | {{{tag-links}}} 12 | {{/tag-links}} 13 |
  • 14 | {{/posts}} 15 |
16 |
17 |
-------------------------------------------------------------------------------- /themes/default/templates/container.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{{header}}} 4 | 5 | {{{nav}}} 6 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /themes/default/templates/footer.mustache: -------------------------------------------------------------------------------- 1 |
{{! footer}} 2 | {{#show-meta}} 3 | 9 | {{/show-meta}} 10 | {{#show-comment}} 11 |
12 |

Comments

13 | {{#disqus-comment}} 14 |
15 | 28 | 29 | comments powered by Disqus 30 | {{/disqus-comment}} 31 | {{#duoshuo-comment}} 32 |
34 | 45 | {{/duoshuo-comment}} 46 |
47 | {{/show-comment}} 48 | 49 | 50 | 51 | 52 | {{#google-analytics}} 53 | 64 | {{/google-analytics}} 65 | 74 |
75 | -------------------------------------------------------------------------------- /themes/default/templates/header.mustache: -------------------------------------------------------------------------------- 1 | 2 | {{page-title}} 3 | 4 | 5 | {{#description}} 6 | 7 | {{/description}} 8 | {{#keywords}} 9 | 10 | {{/keywords}} 11 | 12 | 13 | -------------------------------------------------------------------------------- /themes/default/templates/index.mustache: -------------------------------------------------------------------------------- 1 | {{#categories}} 2 |
3 |

{{category}}

4 |
5 | 10 |
11 |
12 | {{/categories}} -------------------------------------------------------------------------------- /themes/default/templates/nav.mustache: -------------------------------------------------------------------------------- 1 |
{{! nav }} 2 |
3 |

{{site-main-title}}

4 |

{{site-sub-title}}

5 | 22 |
23 | 24 | 25 |
26 | {{#avatar}} 27 | 28 | {{/avatar}} 29 |
30 |
31 | -------------------------------------------------------------------------------- /themes/default/templates/post.mustache: -------------------------------------------------------------------------------- 1 |
{{! content, do NOT indent}} 2 |
3 | {{#title}} 4 |

{{title}}

5 | {{/title}} 6 | {{{content}}} 7 |
8 |
-------------------------------------------------------------------------------- /themes/default/templates/summary-index.mustache: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{summary-name}} Index

4 | 9 |
10 |
-------------------------------------------------------------------------------- /themes/default/templates/summary.mustache: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{summary-name}}: {{summary-item-name}}

4 |
    5 | {{#posts}} 6 |
  • {{post-date}} » {{post-title}}
  • 7 | {{/posts}} 8 |
9 |
10 |
-------------------------------------------------------------------------------- /themes/killjs/templates/footer.mustache: -------------------------------------------------------------------------------- 1 |
{{! footer}} 2 | {{#show-meta}} 3 | 9 | {{/show-meta}} 10 | {{#show-comment}} 11 |
12 |

Comments

13 | {{#disqus-comment}} 14 |
15 | 28 | 29 | comments powered by Disqus 30 | {{/disqus-comment}} 31 | {{#duoshuo-comment}} 32 |
34 | 45 | {{/duoshuo-comment}} 46 |
47 | {{/show-comment}} 48 | {{#google-analytics}} 49 | 60 | {{/google-analytics}} 61 | 70 |
71 | -------------------------------------------------------------------------------- /themes/killjs/templates/header.mustache: -------------------------------------------------------------------------------- 1 | 2 | {{page-title}} 3 | 4 | 5 | {{#description}} 6 | 7 | {{/description}} 8 | {{#keywords}} 9 | 10 | {{/keywords}} 11 | 12 | -------------------------------------------------------------------------------- /themes/worg/resources/css/main.css: -------------------------------------------------------------------------------- 1 | /* @import url(http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans+Mono|Droid+Serif); */ 2 | @media all{ 3 | 4 | * { 5 | -webkit-box-sizing: border-box; 6 | -moz-box-sizing: border-box; 7 | box-sizing: border-box; 8 | } 9 | 10 | html { 11 | margin: 0; 12 | font: 300 .9em/1.6em "Droid Serif", Cambria, Georgia, "DejaVu Serif", serif; 13 | overflow: scroll; 14 | overflow-y: scroll; 15 | overflow:-moz-scrollbars-vertical; 16 | } 17 | 18 | body { 19 | font-size: 12pt; 20 | line-height: 18pt; 21 | color: black; 22 | margin-top: 0; 23 | 24 | } 25 | body #content { 26 | padding-top: 0px; 27 | max-width: 80%; 28 | min-width: 700px; 29 | margin-left: 20px; 30 | background-color: white; 31 | padding: 2em; 32 | /* box-shadow: 3px 3px 5px #888; */ 33 | } 34 | body .title { 35 | margin-left: 0px; 36 | font-size: 22pt; 37 | } 38 | 39 | #org-div-home-and-up{ 40 | position: fixed; 41 | right: 0; 42 | top: 4em; 43 | } 44 | 45 | /* TOC inspired by http://jashkenas.github.com/coffee-script */ 46 | #table-of-contents { 47 | font-size: 10pt; 48 | position: fixed; 49 | right: 0em; 50 | top: 0em; 51 | background: white; 52 | line-height: 12pt; 53 | text-align: right; 54 | box-shadow: 0 0 1em #777777; 55 | -webkit-box-shadow: 0 0 1em #777777; 56 | -moz-box-shadow: 0 0 1em #777777; 57 | -webkit-border-bottom-left-radius: 5px; 58 | -moz-border-radius-bottomleft: 5px; 59 | /* ensure doesn't flow off the screen when expanded */ 60 | max-height: 80%; 61 | overflow: auto; } 62 | #table-of-contents h2 { 63 | font-size: 13pt; 64 | max-width: 9em; 65 | border: 0; 66 | font-weight: normal; 67 | padding-left: 0.5em; 68 | padding-right: 0.5em; 69 | padding-top: 0.35em; 70 | padding-bottom: 0.35em; } 71 | #table-of-contents #text-table-of-contents { 72 | display: none; 73 | text-align: left; } 74 | #table-of-contents:hover #text-table-of-contents { 75 | display: block; 76 | padding: 0.5em; 77 | margin-top: -1.5em; } 78 | 79 | #license { 80 | /* padding: .3em; */ 81 | /* border: 1px solid gray; */ 82 | background-color: #eeeeee; 83 | } 84 | 85 | h1 { 86 | /* 87 | font-family:Sans; 88 | font-weight:bold; */ 89 | font-family:sans-serif; 90 | font-size: 1.45em; 91 | font-weight: bold; 92 | padding: 0 0 30px 0; 93 | margin-top: 30px; 94 | margin-bottom: 10px; 95 | margin-right: 7%; 96 | /* color: #6C5D4F; */ 97 | color: bold; 98 | } 99 | 100 | h2 { 101 | font-family:sans-serif; 102 | font-size:1.35em; 103 | line-height:16px; 104 | padding:10px 0 10px 0; 105 | color: black; 106 | border-bottom: 1px solid #ddd; 107 | } 108 | 109 | .outline-text-2 { 110 | margin-left: 1.3em; 111 | } 112 | 113 | h3 { 114 | font-family:sans-serif; 115 | font-size:1.3em; 116 | color: grey; 117 | margin-left: 0.8em; 118 | } 119 | 120 | .outline-text-3 { 121 | margin-left: 3.5em; 122 | } 123 | 124 | h4 { 125 | font-family:sans-serif; 126 | font-size:1.2em; 127 | margin-left: 2.6em; 128 | color: #A5573E; 129 | } 130 | 131 | .outline-text-4 { 132 | margin-left: 6.5em; 133 | } 134 | 135 | h5 { 136 | font-family:sans-serif; 137 | font-size:1.0em; 138 | margin-left: 6.3em; 139 | } 140 | 141 | .outline-text-5 { 142 | margin-left: 10.0em; 143 | } 144 | 145 | h6 { 146 | font-family:sans-serif; 147 | font-size:1.0em; 148 | margin-left: 10.0em; 149 | } 150 | 151 | .outline-text-6 { 152 | margin-left: 14.8em; 153 | } 154 | 155 | h7 { 156 | font-family:sans-serif; 157 | font-size:1.0em; 158 | margin-left: 14.8em; 159 | } 160 | 161 | .outline-text-7 { 162 | margin-left: 24.8em; 163 | } 164 | 165 | a {text-decoration: none; font-weight: 400;} 166 | a:visited {text-decoration: none; font-weight: 400;} 167 | a:hover {text-decoration: underline;} 168 | 169 | .todo { 170 | color: #CA0000; 171 | } 172 | 173 | .done { 174 | color: #006666; 175 | } 176 | 177 | .timestamp-kwd { 178 | color: #444; 179 | } 180 | 181 | .tag { 182 | 183 | } 184 | 185 | li { 186 | margin: 0.4em; 187 | } 188 | 189 | code { 190 | font-size: 100%; 191 | color: black; 192 | border: 1px solid #DEDEDE; 193 | padding: 0px 0.2em; 194 | } 195 | 196 | span.tags-links { 197 | display:block; 198 | float:right; 199 | text-align:right; 200 | } 201 | 202 | img { 203 | border: none; 204 | } 205 | 206 | .share img { 207 | opacity: .4; 208 | -moz-opacity: .4; 209 | filter: alpha(opacity=40); 210 | } 211 | 212 | .share img:hover { 213 | opacity: 1; 214 | -moz-opacity: 1; 215 | filter: alpha(opacity=100); 216 | } 217 | 218 | /* pre {border: 1px solid #555; */ 219 | /* background: #EEE; */ 220 | /* font-size: 9pt; */ 221 | /* padding: 1em; */ 222 | /* } */ 223 | 224 | /* pre { */ 225 | /* color: #e5e5e5; */ 226 | /* background-color: #000000; */ 227 | /* padding: 1.4em; */ 228 | /* border: 2px solid gray; */ 229 | /* } */ 230 | 231 | /* pre { */ 232 | /* background-color: #2b2b2b; */ 233 | /* border: 4px solid gray; */ 234 | /* color: #EEE; */ 235 | /* overflow: auto; */ 236 | /* padding: 1em; */ 237 | /* } */ 238 | 239 | pre { 240 | font-family: Droid Sans Mono, Monaco, Consolas, "Lucida Console", monospace; 241 | color: black; 242 | font-size: 90%; 243 | background-color: #ffffff; 244 | padding: 1.2em; 245 | border: 2px solid #dddddd; 246 | overflow: auto; 247 | } 248 | 249 | /* body */ 250 | .container { 251 | margin: 0 auto; 252 | margin-left: 30px; 253 | max-width: 80%; 254 | } 255 | 256 | .content-and-footer { 257 | margin: 0 auto; 258 | } 259 | 260 | /* head */ 261 | .masthead { 262 | color: #ada; 263 | } 264 | 265 | .masthead hr { 266 | height: 2px; 267 | border: 0; 268 | box-shadow: inset 0 12px 12px -12px rgba(0,0,0,0.5); 269 | } 270 | 271 | .masthead ul { 272 | padding: 3ex 3ex; 273 | border: 1px solid #aaa; 274 | background: white; 275 | color: black !important; 276 | list-style-type: disc; 277 | position: absolute; 278 | right: 1%; 279 | top: 20%; 280 | z-index:-10; 281 | } 282 | 283 | .masthead p { 284 | margin-top: 0; 285 | margin-left: 0; 286 | } 287 | 288 | .masthead h1 { 289 | font-size: 40px; 290 | font-weight: bold; 291 | margin: 40px 0 0 -10px; 292 | color: black; 293 | } 294 | 295 | .masthead span { 296 | font-size: 17px; 297 | font-weight: normal; 298 | color: gray; 299 | } 300 | 301 | .masthead h1:before { 302 | content: ""; 303 | } 304 | 305 | .masthead:before { 306 | display: block; 307 | margin: -5px 0 0 -40px; 308 | color: #cfc; 309 | } 310 | 311 | .masthead:after { 312 | display: block; 313 | margin: -5px 0 0 -40px; 314 | color: black; 315 | } 316 | .masthead a { 317 | color: black; 318 | } 319 | 320 | .masthead p { 321 | color: gray; 322 | } 323 | 324 | .masthead li { 325 | } 326 | 327 | /* 328 | .masthead li + li:before { 329 | display: inline-block; 330 | content: "\00B7 \00a0"; 331 | color: #cfc; 332 | } */ 333 | 334 | /* searchform*/ 335 | #searchform { 336 | position: absolute; 337 | top: 30px; 338 | right: 1%; 339 | } 340 | 341 | #searchform #s { 342 | background: url("/media/img/search.png") no-repeat 5px 6px; 343 | box-shadow: inset 0 1px 1px rgba(0,0,0,0.1); 344 | border: 1px solid #dfd; 345 | border-radius: 2px; 346 | -moz-border-radius: 2px; 347 | width: 150px; 348 | height: 30px; 349 | line-height: 12px; 350 | padding: 4px 10px 4px 28px; 351 | 352 | -webkit-transition-duration: 400ms; 353 | -webkit-transition-property: width, background; 354 | -webkit-transition-timing-function: ease; 355 | -moz-transition-duration: 400ms; 356 | -moz-transition-property: width, background; 357 | -moz-transition-timing-function: ease; 358 | -o-transition-duration: 400ms; 359 | -o-transition-property: width, background; 360 | -o-transition-timing-function: ease; 361 | } 362 | 363 | #searchform #s:focus { 364 | background-color: #f9f9f9; 365 | width: 160px; 366 | } 367 | 368 | .masthead img.avatar { 369 | position: fixed; 370 | width: 150px; 371 | right: 2%; 372 | bottom: 2%; 373 | background-color: #fff; 374 | } 375 | 376 | /* post */ 377 | .post { 378 | position: relative; 379 | } 380 | 381 | .post-title{ 382 | font-size: 16px; 383 | color: gray; 384 | text-align: left; 385 | font-weight: normal; 386 | margin: -25px 0 50px 0; 387 | } 388 | 389 | .post-title:before { 390 | font-size: 18px; 391 | margin-left: -2px; 392 | content: "(c ͡|Q ͜ʖ ͡o)-c[_] "; 393 | content: "(c ͡|Q ͜ʖ ͡o)╦╤─"; 394 | content: "̿' ̿'\̵͇̿̿\з=( ͡ °_̯͡° )=ε/̵͇̿̿/'̿'̿ ̿ "; 395 | content: " (•̪̀●́)=ε/̵͇̿̿/'̿̿ ̿ ̿̿ N --------{---(@ "; 396 | content: "╭∩╮(ô¿ô)╭∩╮ "; 397 | content: "٩๏̯͡๏۶(̅_̅_̅_̅(̅_̅_̅_̅_̅_̅_̅_̅_̅̅_̅()ڪے~ "; 398 | content: "̿ ̿̿'̿̿\̵͇̿̿\=(•̪●)=/̵͇̿̿/'̿̿ ̿ ̿ "; 399 | content: "¯¯̿̿¯̿̿'̿̿̿̿̿̿̿'̿̿'̿̿̿̿̿'̿̿̿)͇̿̿)̿̿̿̿ '̿̿̿̿̿̿\̵͇̿̿\=(•̪̀●́)=o/̵͇̿̿/'̿̿ ̿ ̿̿ "; 400 | content: "‹’’›(Ͼ˳Ͽ)‹’’›"; 401 | content: "ѧѦ ѧ ︵͡︵ ̢ ̱ ̧̱ι̵̱̊ι̶̨̱ ̶̱ ︵ Ѧѧ ︵͡ ︵ ѧ Ѧ ̵̗̊o̵̖ ︵ ѦѦ ѧ"; 402 | content: "✯╾━╤デ╦︻✯ "; 403 | content: "¸¸♬·¯·♩¸¸♪·¯·♫¸¸ "; 404 | content: "Ø)xxxxx[╣╗╗╕╕╕=======───────────────── "; 405 | content: "இڿڰۣ-ڰۣ— "; 406 | content: "( ͡° ͜ʖ ͡°)-︻デ┳═ー "; 407 | content: "ᕦ(ò_ó*)ᕤ  `--- "; 408 | content: "סּﭖסּﭾ﴿ `--- "; 409 | content: "٩(̾●̮̮̃̾•̃̾)۶  `--- "; 410 | content: "٩(- ̮̮̃-̃)۶  `--- "; 411 | content: "٩๏̯͡๏۶  `--- "; 412 | } 413 | 414 | .post-title:after { 415 | font-size: 18px; 416 | content: " ---"; 417 | } 418 | 419 | .post img { 420 | max-width: 100%; 421 | } 422 | 423 | .post li { 424 | list-style: none outside none; 425 | } 426 | 427 | .post li pre { 428 | margin-left: 1.3em; 429 | } 430 | 431 | .post li p { 432 | display: inline; 433 | } 434 | 435 | .post ul > li:before { 436 | content: "- "; 437 | /* margin-left: -1.25em; */ 438 | } 439 | 440 | .post ol { 441 | counter-reset: o-list; 442 | } 443 | 444 | .post ol > li:before { 445 | content: counter(o-list) ") "; 446 | counter-increment: o-list; 447 | /* margin-left: -1.25em; */ 448 | } 449 | 450 | /* special for li in pre */ 451 | .post pre li { 452 | list-style-type: decimal; 453 | } 454 | 455 | .post pre li:before { 456 | display: none; 457 | } 458 | 459 | /* meta info */ 460 | .post-info { 461 | display: inline-block; 462 | margin: -10px 0 -10px; 463 | } 464 | 465 | .post-info + .post-info:before { 466 | content: "\2223 \00a0"; 467 | } 468 | 469 | .post-meta:after { 470 | margin: -1px 0 0 -40px; 471 | display: block; 472 | } 473 | 474 | 475 | /* footer */ 476 | .footer { 477 | margin: 80px 0 0; 478 | text-align: center; 479 | font-size: 15px; 480 | } 481 | .footer > p { 482 | margin: 0; 483 | line-height: 1.5; 484 | } 485 | .footpara { 486 | display: inline; 487 | } 488 | 489 | .org-info-box { 490 | clear:both; 491 | margin-left:auto; 492 | margin-right:auto; 493 | padding:0.7em; 494 | /* border:1px solid #CCC; */ 495 | /* border-radius:10px; */ 496 | /* -moz-border-radius:10px; */ 497 | } 498 | .org-info-box img { 499 | float:left; 500 | margin:0em 0.5em 0em 0em; 501 | } 502 | .org-info-box p { 503 | margin:0em; 504 | padding:0em; 505 | } 506 | 507 | 508 | .builtin { 509 | /* font-lock-builtin-face */ 510 | color: #f4a460; 511 | } 512 | .comment { 513 | /* font-lock-comment-face */ 514 | color: #737373; 515 | } 516 | .comment-delimiter { 517 | /* font-lock-comment-delimiter-face */ 518 | color: #666666; 519 | } 520 | .constant { 521 | /* font-lock-constant-face */ 522 | color: #db7093; 523 | } 524 | .doc { 525 | /* font-lock-doc-face */ 526 | color: #b3b3b3; 527 | } 528 | .function-name { 529 | /* font-lock-function-name-face */ 530 | color: #5f9ea0; 531 | } 532 | .headline { 533 | /* headline-face */ 534 | color: #ffffff; 535 | background-color: #000000; 536 | font-weight: bold; 537 | } 538 | .keyword { 539 | /* font-lock-keyword-face */ 540 | color: #4682b4; 541 | } 542 | .negation-char { 543 | } 544 | .regexp-grouping-backslash { 545 | } 546 | .regexp-grouping-construct { 547 | } 548 | .string { 549 | /* font-lock-string-face */ 550 | color: #ccc79a; 551 | } 552 | .todo-comment { 553 | /* todo-comment-face */ 554 | color: #ffffff; 555 | background-color: #000000; 556 | font-weight: bold; 557 | } 558 | .variable-name { 559 | /* font-lock-variable-name-face */ 560 | color: #ff6a6a; 561 | } 562 | .warning { 563 | /* font-lock-warning-face */ 564 | color: #ffffff; 565 | background-color: #cd5c5c; 566 | font-weight: bold; 567 | } 568 | pre.a { 569 | color: inherit; 570 | background-color: inherit; 571 | font: inherit; 572 | text-decoration: inherit; 573 | } 574 | pre.a:hover { 575 | text-decoration: underline; 576 | } 577 | 578 | /* Styles for org-info.js */ 579 | 580 | .org-info-js_info-navigation 581 | { 582 | border-style:none; 583 | } 584 | 585 | #org-info-js_console-label 586 | { 587 | font-size:10px; 588 | font-weight:bold; 589 | white-space:nowrap; 590 | } 591 | 592 | .org-info-js_search-highlight 593 | { 594 | background-color:#ffff00; 595 | color:#000000; 596 | font-weight:bold; 597 | } 598 | 599 | #org-info-js-window 600 | { 601 | border-bottom:1px solid black; 602 | padding-bottom:10px; 603 | margin-bottom:10px; 604 | } 605 | 606 | .org-info-search-highlight 607 | { 608 | background-color:#adefef; /* same color as emacs default */ 609 | color:#000000; 610 | font-weight:bold; 611 | } 612 | 613 | .org-bbdb-company { 614 | /* bbdb-company */ 615 | font-style: italic; 616 | } 617 | .org-bbdb-field-name { 618 | } 619 | .org-bbdb-field-value { 620 | } 621 | .org-bbdb-name { 622 | /* bbdb-name */ 623 | text-decoration: underline; 624 | } 625 | .org-bold { 626 | /* bold */ 627 | font-weight: bold; 628 | } 629 | .org-bold-italic { 630 | /* bold-italic */ 631 | font-weight: bold; 632 | font-style: italic; 633 | } 634 | .org-border { 635 | /* border */ 636 | background-color: #000000; 637 | } 638 | .org-buffer-menu-buffer { 639 | /* buffer-menu-buffer */ 640 | font-weight: bold; 641 | } 642 | .org-builtin { 643 | /* font-lock-builtin-face */ 644 | color: #da70d6; 645 | } 646 | .org-button { 647 | /* button */ 648 | text-decoration: underline; 649 | } 650 | .org-c-nonbreakable-space { 651 | /* c-nonbreakable-space-face */ 652 | background-color: #ff0000; 653 | font-weight: bold; 654 | } 655 | .org-calendar-today { 656 | /* calendar-today */ 657 | text-decoration: underline; 658 | } 659 | .org-comment { 660 | /* font-lock-comment-face */ 661 | color: #b22222; 662 | } 663 | .org-comment-delimiter { 664 | /* font-lock-comment-delimiter-face */ 665 | color: #b22222; 666 | } 667 | .org-constant { 668 | /* font-lock-constant-face */ 669 | color: #5f9ea0; 670 | } 671 | .org-cursor { 672 | /* cursor */ 673 | background-color: #000000; 674 | } 675 | .org-default { 676 | /* default */ 677 | color: #000000; 678 | background-color: #ffffff; 679 | } 680 | .org-diary { 681 | /* diary */ 682 | color: #ff0000; 683 | } 684 | .org-doc { 685 | /* font-lock-doc-face */ 686 | color: #bc8f8f; 687 | } 688 | .org-escape-glyph { 689 | /* escape-glyph */ 690 | color: #a52a2a; 691 | } 692 | .org-file-name-shadow { 693 | /* file-name-shadow */ 694 | color: #7f7f7f; 695 | } 696 | .org-fixed-pitch { 697 | } 698 | .org-fringe { 699 | /* fringe */ 700 | background-color: #f2f2f2; 701 | } 702 | .org-function-name { 703 | /* font-lock-function-name-face */ 704 | color: #0000ff; 705 | } 706 | .org-header-line { 707 | /* header-line */ 708 | color: #333333; 709 | background-color: #e5e5e5; 710 | } 711 | .org-help-argument-name { 712 | /* help-argument-name */ 713 | font-style: italic; 714 | } 715 | .org-highlight { 716 | /* highlight */ 717 | background-color: #b4eeb4; 718 | } 719 | .org-holiday { 720 | /* holiday */ 721 | background-color: #ffc0cb; 722 | } 723 | .org-info-header-node { 724 | /* info-header-node */ 725 | color: #a52a2a; 726 | font-weight: bold; 727 | font-style: italic; 728 | } 729 | .org-info-header-xref { 730 | /* info-header-xref */ 731 | color: #0000ff; 732 | text-decoration: underline; 733 | } 734 | .org-info-menu-header { 735 | /* info-menu-header */ 736 | font-weight: bold; 737 | } 738 | .org-info-menu-star { 739 | /* info-menu-star */ 740 | color: #ff0000; 741 | } 742 | .org-info-node { 743 | /* info-node */ 744 | color: #a52a2a; 745 | font-weight: bold; 746 | font-style: italic; 747 | } 748 | .org-info-title-1 { 749 | /* info-title-1 */ 750 | font-size: 172%; 751 | font-weight: bold; 752 | } 753 | .org-info-title-2 { 754 | /* info-title-2 */ 755 | font-size: 144%; 756 | font-weight: bold; 757 | } 758 | .org-info-title-3 { 759 | /* info-title-3 */ 760 | font-size: 120%; 761 | font-weight: bold; 762 | } 763 | .org-info-title-4 { 764 | /* info-title-4 */ 765 | font-weight: bold; 766 | } 767 | .org-info-xref { 768 | /* info-xref */ 769 | color: #0000ff; 770 | text-decoration: underline; 771 | } 772 | .org-isearch { 773 | /* isearch */ 774 | color: #b0e2ff; 775 | background-color: #cd00cd; 776 | } 777 | .org-italic { 778 | /* italic */ 779 | font-style: italic; 780 | } 781 | .org-keyword { 782 | /* font-lock-keyword-face */ 783 | color: #a020f0; 784 | } 785 | .org-lazy-highlight { 786 | /* lazy-highlight */ 787 | background-color: #afeeee; 788 | } 789 | .org-link { 790 | /* link */ 791 | color: #0000ff; 792 | text-decoration: underline; 793 | } 794 | .org-link-visited { 795 | /* link-visited */ 796 | color: #8b008b; 797 | text-decoration: underline; 798 | } 799 | .org-match { 800 | /* match */ 801 | background-color: #ffff00; 802 | } 803 | .org-menu { 804 | } 805 | .org-message-cited-text { 806 | /* message-cited-text */ 807 | color: #ff0000; 808 | } 809 | .org-message-header-cc { 810 | /* message-header-cc */ 811 | color: #191970; 812 | } 813 | .org-message-header-name { 814 | /* message-header-name */ 815 | color: #6495ed; 816 | } 817 | .org-message-header-newsgroups { 818 | /* message-header-newsgroups */ 819 | color: #00008b; 820 | font-weight: bold; 821 | font-style: italic; 822 | } 823 | .org-message-header-other { 824 | /* message-header-other */ 825 | color: #4682b4; 826 | } 827 | .org-message-header-subject { 828 | /* message-header-subject */ 829 | color: #000080; 830 | font-weight: bold; 831 | } 832 | .org-message-header-to { 833 | /* message-header-to */ 834 | color: #191970; 835 | font-weight: bold; 836 | } 837 | .org-message-header-xheader { 838 | /* message-header-xheader */ 839 | color: #0000ff; 840 | } 841 | .org-message-mml { 842 | /* message-mml */ 843 | color: #228b22; 844 | } 845 | .org-message-separator { 846 | /* message-separator */ 847 | color: #a52a2a; 848 | } 849 | .org-minibuffer-prompt { 850 | /* minibuffer-prompt */ 851 | color: #0000cd; 852 | } 853 | .org-mm-uu-extract { 854 | /* mm-uu-extract */ 855 | color: #006400; 856 | background-color: #ffffe0; 857 | } 858 | .org-mode-line { 859 | /* mode-line */ 860 | color: #000000; 861 | background-color: #bfbfbf; 862 | } 863 | .org-mode-line-buffer-id { 864 | /* mode-line-buffer-id */ 865 | font-weight: bold; 866 | } 867 | .org-mode-line-highlight { 868 | } 869 | .org-mode-line-inactive { 870 | /* mode-line-inactive */ 871 | color: #333333; 872 | background-color: #e5e5e5; 873 | } 874 | .org-mouse { 875 | /* mouse */ 876 | background-color: #000000; 877 | } 878 | .org-negation-char { 879 | } 880 | .org-next-error { 881 | /* next-error */ 882 | background-color: #eedc82; 883 | } 884 | .org-nobreak-space { 885 | /* nobreak-space */ 886 | color: #a52a2a; 887 | text-decoration: underline; 888 | } 889 | .org-org-agenda-date { 890 | /* org-agenda-date */ 891 | color: #0000ff; 892 | } 893 | .org-org-agenda-date-weekend { 894 | /* org-agenda-date-weekend */ 895 | color: #0000ff; 896 | font-weight: bold; 897 | } 898 | .org-org-agenda-restriction-lock { 899 | /* org-agenda-restriction-lock */ 900 | background-color: #ffff00; 901 | } 902 | .org-org-agenda-structure { 903 | /* org-agenda-structure */ 904 | color: #0000ff; 905 | } 906 | .org-org-archived { 907 | /* org-archived */ 908 | color: #7f7f7f; 909 | } 910 | .org-org-code { 911 | /* org-code */ 912 | color: #7f7f7f; 913 | } 914 | .org-org-column { 915 | /* org-column */ 916 | background-color: #e5e5e5; 917 | } 918 | .org-org-column-title { 919 | /* org-column-title */ 920 | background-color: #e5e5e5; 921 | font-weight: bold; 922 | text-decoration: underline; 923 | } 924 | .org-org-date { 925 | /* org-date */ 926 | color: #a020f0; 927 | text-decoration: underline; 928 | } 929 | .org-org-done { 930 | /* org-done */ 931 | color: #228b22; 932 | font-weight: bold; 933 | } 934 | .org-org-drawer { 935 | /* org-drawer */ 936 | color: #0000ff; 937 | } 938 | .org-org-ellipsis { 939 | /* org-ellipsis */ 940 | color: #b8860b; 941 | text-decoration: underline; 942 | } 943 | .org-org-formula { 944 | /* org-formula */ 945 | color: #b22222; 946 | } 947 | .org-org-headline-done { 948 | /* org-headline-done */ 949 | color: #bc8f8f; 950 | } 951 | .org-org-hide { 952 | /* org-hide */ 953 | color: #e5e5e5; 954 | } 955 | .org-org-latex-and-export-specials { 956 | /* org-latex-and-export-specials */ 957 | color: #8b4513; 958 | } 959 | .org-org-level-1 { 960 | /* org-level-1 */ 961 | color: #0000ff; 962 | } 963 | .org-org-level-2 { 964 | /* org-level-2 */ 965 | color: #b8860b; 966 | } 967 | .org-org-level-3 { 968 | /* org-level-3 */ 969 | color: #a020f0; 970 | } 971 | .org-org-level-4 { 972 | /* org-level-4 */ 973 | color: #b22222; 974 | } 975 | .org-org-level-5 { 976 | /* org-level-5 */ 977 | color: #228b22; 978 | } 979 | .org-org-level-6 { 980 | /* org-level-6 */ 981 | color: #5f9ea0; 982 | } 983 | .org-org-level-7 { 984 | /* org-level-7 */ 985 | color: #da70d6; 986 | } 987 | .org-org-level-8 { 988 | /* org-level-8 */ 989 | color: #bc8f8f; 990 | } 991 | .org-org-link { 992 | /* org-link */ 993 | color: #a020f0; 994 | text-decoration: underline; 995 | } 996 | .org-org-property-value { 997 | } 998 | .org-org-scheduled-previously { 999 | /* org-scheduled-previously */ 1000 | color: #b22222; 1001 | } 1002 | .org-org-scheduled-today { 1003 | /* org-scheduled-today */ 1004 | color: #006400; 1005 | } 1006 | .org-org-sexp-date { 1007 | /* org-sexp-date */ 1008 | color: #a020f0; 1009 | } 1010 | .org-org-special-keyword { 1011 | /* org-special-keyword */ 1012 | color: #bc8f8f; 1013 | } 1014 | .org-org-table { 1015 | /* org-table */ 1016 | color: #0000ff; 1017 | } 1018 | .org-org-tag { 1019 | /* org-tag */ 1020 | font-weight: bold; 1021 | } 1022 | .org-org-target { 1023 | /* org-target */ 1024 | text-decoration: underline; 1025 | } 1026 | .org-org-time-grid { 1027 | /* org-time-grid */ 1028 | color: #b8860b; 1029 | } 1030 | .org-org-todo { 1031 | /* org-todo */ 1032 | color: #ff0000; 1033 | } 1034 | .org-org-upcoming-deadline { 1035 | /* org-upcoming-deadline */ 1036 | color: #b22222; 1037 | } 1038 | .org-org-verbatim { 1039 | /* org-verbatim */ 1040 | color: #7f7f7f; 1041 | text-decoration: underline; 1042 | } 1043 | .org-org-warning { 1044 | /* org-warning */ 1045 | color: #ff0000; 1046 | font-weight: bold; 1047 | } 1048 | .org-outline-1 { 1049 | /* outline-1 */ 1050 | color: #0000ff; 1051 | } 1052 | .org-outline-2 { 1053 | /* outline-2 */ 1054 | color: #b8860b; 1055 | } 1056 | .org-outline-3 { 1057 | /* outline-3 */ 1058 | color: #a020f0; 1059 | } 1060 | .org-outline-4 { 1061 | /* outline-4 */ 1062 | color: #b22222; 1063 | } 1064 | .org-outline-5 { 1065 | /* outline-5 */ 1066 | color: #228b22; 1067 | } 1068 | .org-outline-6 { 1069 | /* outline-6 */ 1070 | color: #5f9ea0; 1071 | } 1072 | .org-outline-7 { 1073 | /* outline-7 */ 1074 | color: #da70d6; 1075 | } 1076 | .org-outline-8 { 1077 | /* outline-8 */ 1078 | color: #bc8f8f; 1079 | } 1080 | .outline-text-1, .outline-text-2, .outline-text-3, .outline-text-4, .outline-text-5, .outline-text-6 { 1081 | /* Add more spacing between section. Padding, so that folding with org-info.js works as expected. */ 1082 | 1083 | } 1084 | 1085 | .org-preprocessor { 1086 | /* font-lock-preprocessor-face */ 1087 | color: #da70d6; 1088 | } 1089 | .org-query-replace { 1090 | /* query-replace */ 1091 | color: #b0e2ff; 1092 | background-color: #cd00cd; 1093 | } 1094 | .org-regexp-grouping-backslash { 1095 | /* font-lock-regexp-grouping-backslash */ 1096 | font-weight: bold; 1097 | } 1098 | .org-regexp-grouping-construct { 1099 | /* font-lock-regexp-grouping-construct */ 1100 | font-weight: bold; 1101 | } 1102 | .org-region { 1103 | /* region */ 1104 | background-color: #eedc82; 1105 | } 1106 | .org-rmail-highlight { 1107 | } 1108 | .org-scroll-bar { 1109 | /* scroll-bar */ 1110 | background-color: #bfbfbf; 1111 | } 1112 | .org-secondary-selection { 1113 | /* secondary-selection */ 1114 | background-color: #ffff00; 1115 | } 1116 | .org-shadow { 1117 | /* shadow */ 1118 | color: #7f7f7f; 1119 | } 1120 | .org-show-paren-match { 1121 | /* show-paren-match */ 1122 | background-color: #40e0d0; 1123 | } 1124 | .org-show-paren-mismatch { 1125 | /* show-paren-mismatch */ 1126 | color: #ffffff; 1127 | background-color: #a020f0; 1128 | } 1129 | .org-string { 1130 | /* font-lock-string-face */ 1131 | color: #bc8f8f; 1132 | } 1133 | .org-texinfo-heading { 1134 | /* texinfo-heading */ 1135 | color: #0000ff; 1136 | } 1137 | .org-tool-bar { 1138 | /* tool-bar */ 1139 | color: #000000; 1140 | background-color: #bfbfbf; 1141 | } 1142 | .org-tooltip { 1143 | /* tooltip */ 1144 | color: #000000; 1145 | background-color: #ffffe0; 1146 | } 1147 | .org-trailing-whitespace { 1148 | /* trailing-whitespace */ 1149 | background-color: #ff0000; 1150 | } 1151 | .org-type { 1152 | /* font-lock-type-face */ 1153 | color: #228b22; 1154 | } 1155 | .org-underline { 1156 | /* underline */ 1157 | text-decoration: underline; 1158 | } 1159 | .org-variable-name { 1160 | /* font-lock-variable-name-face */ 1161 | color: #b8860b; 1162 | } 1163 | .org-variable-pitch { 1164 | } 1165 | .org-vertical-border { 1166 | } 1167 | .org-warning { 1168 | /* font-lock-warning-face */ 1169 | color: #ff0000; 1170 | font-weight: bold; 1171 | } 1172 | .rss_box {} 1173 | .rss_title, rss_title a {} 1174 | .rss_items {} 1175 | .rss_item a:link, .rss_item a:visited, .rss_item a:active {} 1176 | .rss_item a:hover {} 1177 | .rss_date {} 1178 | 1179 | #show_source {margin: 0; padding: 0;} 1180 | 1181 | #postamble { 1182 | font-size: 75%; 1183 | min-width: 700px; 1184 | max-width: 80%; 1185 | margin-left: 20px; 1186 | margin-top: 10px; 1187 | padding: 2em; 1188 | /* border: 1px solid gray; */ 1189 | border: 0px; 1190 | background-color: #ffffff; 1191 | z-index: -1000; 1192 | } 1193 | 1194 | @media screen 1195 | { 1196 | #table-of-contents { 1197 | float: right; 1198 | border: 1px solid #CCC; 1199 | max-width: 50%; 1200 | overflow: auto; 1201 | } 1202 | } 1203 | } /* END OF @media all */ 1204 | 1205 | @media screen 1206 | { 1207 | #table-of-contents { 1208 | float: right; 1209 | border: 1px solid #CCC; 1210 | max-width: 50%; 1211 | overflow: auto; 1212 | } 1213 | } /* END OF @media screen */ 1214 | 1215 | @media screen and (min-width:1000px) { 1216 | 1217 | .post li{ 1218 | margin-left: -1.2em; 1219 | } 1220 | 1221 | .post li pre { 1222 | margin-left: 0em; 1223 | } 1224 | 1225 | .post li p { 1226 | display: inline; 1227 | } 1228 | 1229 | .post li { 1230 | list-style: none outside none; 1231 | } 1232 | 1233 | .post ul > li:before { 1234 | content: "- "; 1235 | margin-left: -1.25em; 1236 | } 1237 | 1238 | .post ol { 1239 | counter-reset: o-list; 1240 | } 1241 | 1242 | .post ol > li:before { 1243 | content: counter(o-list) ") "; 1244 | counter-increment: o-list; 1245 | margin-left: -1.25em; 1246 | } 1247 | 1248 | /* special for li in pre */ 1249 | .post pre li { 1250 | list-style-type: decimal; 1251 | } 1252 | 1253 | .post pre li:before { 1254 | display: none; 1255 | } 1256 | } /* END OF @media (min-width:800px) */ 1257 | -------------------------------------------------------------------------------- /themes/worg/resources/img/horse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tumashu/org2web/0e343770b9785f6150afc98153cd00a316d88d01/themes/worg/resources/img/horse.jpg -------------------------------------------------------------------------------- /themes/worg/templates/nav.mustache: -------------------------------------------------------------------------------- 1 |
{{! nav }} 2 |
3 |

{{site-main-title}} {{site-sub-title}}

4 | 21 |
22 | 23 | 24 |
25 | {{#avatar}} 26 | 27 | {{/avatar}} 28 |
29 |
30 | -------------------------------------------------------------------------------- /uploaders/common/git-simple.mustache: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | git_cmd=`which git` 6 | export_dir='{{export-dir}}' 7 | history_dir='{{history-dir}}' 8 | publish_dir='{{publish-dir}}' 9 | git_url='{{remote-url}}' 10 | git_branch='{{remote-branch}}' 11 | partial_update='{{partial-update}}' 12 | 13 | 14 | echo '###########################################################' 15 | echo '## org2web uploader using git-simple ##' 16 | echo '###########################################################' 17 | 18 | echo '' 19 | echo "Export Directory: $export_dir" 20 | echo "Publish Directory: $publish_dir" 21 | echo "History Directory: $history_dir" 22 | 23 | echo '' 24 | echo "Git Remote url: $git_url" 25 | echo "Git Remote branch: $git_branch" 26 | echo '' 27 | 28 | $git_cmd clone --depth=1 --branch "$git_branch" "$git_url" "$history_dir" 29 | 30 | cd "$publish_dir" 31 | cp -r "$history_dir"/.git "$publish_dir" 32 | 33 | $git_cmd config core.autocrlf false 34 | 35 | if [[ $partial_update = "yes" ]] 36 | then 37 | $git_cmd reset --hard 38 | echo "NOTE: Enable Partial upload, reset to HEAD with command: '$git_cmd reset --hard' ..." 39 | fi 40 | 41 | cp -r "$export_dir"/* "$publish_dir" 42 | 43 | $git_cmd add --all . 44 | $git_cmd commit -m 'Update published html files, committed by org2web.' 45 | 46 | $git_cmd push origin $git_branch 47 | 48 | exit 0 49 | -------------------------------------------------------------------------------- /uploaders/common/git.mustache: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | git_cmd=`which git` 6 | export_dir='{{export-dir}}' 7 | history_dir='{{history-dir}}' 8 | publish_dir='{{publish-dir}}' 9 | git_url='{{remote-url}}' 10 | git_branch='{{remote-branch}}' 11 | partial_update='{{partial-update}}' 12 | 13 | 14 | echo '###########################################################' 15 | echo '## org2web uploader using git ##' 16 | echo '###########################################################' 17 | 18 | echo '' 19 | echo "Export Directory: $export_dir" 20 | echo "Publish Directory: $publish_dir" 21 | echo "History Directory: $history_dir" 22 | 23 | echo '' 24 | echo "Git Remote url: $git_url" 25 | echo "Git Remote branch: $git_branch" 26 | 27 | echo '' 28 | if [[ $git_branch = "master" ]] 29 | then 30 | read -p "Type \"branch-name\" to run this uploader: " -r 31 | if [[ ! $REPLY = "master" ]] 32 | then 33 | exit 1 34 | fi 35 | else 36 | read -p 'Run this uploader? [y/n]' -n 1 -r 37 | if [[ ! $REPLY = "y" ]] 38 | then 39 | exit 1 40 | fi 41 | fi 42 | 43 | echo '' 44 | echo '' 45 | echo '###########################################################' 46 | echo '## Building history directory ... ##' 47 | echo '###########################################################' 48 | $git_cmd clone --depth=1 --branch "$git_branch" "$git_url" "$history_dir" 49 | 50 | echo '' 51 | echo '' 52 | echo '###########################################################' 53 | echo '## Generate publish directory and git add/commit ... ##' 54 | echo '###########################################################' 55 | 56 | cd "$publish_dir" 57 | 58 | cp -r "$history_dir"/.git "$publish_dir" 59 | 60 | $git_cmd config core.autocrlf false 61 | 62 | if [[ $partial_update = "yes" ]] 63 | then 64 | $git_cmd reset --hard 65 | echo "NOTE: Enable Partial upload, reset to HEAD with command: '$git_cmd reset --hard' ..." 66 | fi 67 | 68 | cp -r "$export_dir"/* "$publish_dir" 69 | 70 | $git_cmd add --all . 71 | $git_cmd commit -m 'Update published html files, committed by org2web.' 72 | $git_cmd --no-pager diff HEAD~1 --stat 73 | 74 | echo '' 75 | echo '' 76 | echo '###########################################################' 77 | echo '## Run git push ... ##' 78 | echo '###########################################################' 79 | 80 | read -p "Push to: Remote: $git_url 81 | Branch: $git_branch? [y/n] " -n 1 -r 82 | 83 | if [[ ! $REPLY = "y" ]] 84 | then 85 | exit 1 86 | fi 87 | 88 | echo '' 89 | $git_cmd push origin $git_branch 90 | 91 | echo '' 92 | echo '' 93 | echo 'Press any key to continue...' 94 | read -s -n 1 any_key 95 | exit 0 96 | -------------------------------------------------------------------------------- /uploaders/common/rclone.mustache: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo '###########################################################' 6 | echo '## org2web uploader using rclone ##' 7 | echo '###########################################################' 8 | 9 | echo '' 10 | echo 'Export Directory: {{export-dir}}' 11 | echo 'Remote location name: {{remote-name}}' 12 | echo 'Remote location path: {{remote-path}}' 13 | 14 | # check if rclone even exists 15 | echo 'Checking of rclone is installed and can be found in $PATH' 16 | command -v rclone >/dev/null 2>&1 || { 17 | echo >&2 "I require rclone but it's not installed."; 18 | echo >&2 "Install Go from https://golang.org/dl/ and then"; 19 | echo >&2 "Install rclone with:"; 20 | echo >&2 " go get github.com/ncw/rclone"; 21 | echo >&2 "An alternative to this would be downloading rclone"; 22 | echo >&2 "directly from http://rclone.org/downloads/. Aborting upload."; 23 | exit 1; 24 | } 25 | 26 | # Just upload the stuff, for now 27 | echo 'Uploading' 28 | if [ "{{remote-name}}" == "local" ]; then 29 | rclone sync '{{export-dir}}' '{{remote-path}}' 30 | else 31 | rclone sync '{{export-dir}}' {{remote-name}}:'{{remote-path}}' 32 | fi 33 | --------------------------------------------------------------------------------