├── .gitignore ├── README.md ├── example ├── o-blog-v2.org └── sample.org ├── js └── o-blog-bootstrap.js ├── less └── o-blog.less ├── lisp ├── o-blog-backend-markdown.el ├── o-blog-backend-org.el ├── o-blog-backend.el ├── o-blog-entry.el ├── o-blog-framework.el ├── o-blog-i18n.el ├── o-blog-obsolete.el ├── o-blog-tag.el ├── o-blog-utils.el └── o-blog.el ├── site ├── functions.graphml ├── src │ ├── 7696122.png │ ├── _elements │ │ ├── About.snippet.txt │ │ ├── Copyright.snippet.txt │ │ ├── Footer.Archives.txt │ │ ├── Navigation-footer.snippet.txt │ │ └── Navigation.snippet.txt │ ├── bjonnh.png │ ├── common-lisp.png │ ├── djcbsoftware.png │ ├── doc │ │ ├── bootstrap.txt │ │ ├── classes.txt │ │ ├── functions-tn.png │ │ ├── functions.png │ │ ├── javascript.txt │ │ ├── publication.txt │ │ ├── quickstart.txt │ │ ├── templates-tn.png │ │ └── templates.png │ ├── ergoemacs.png │ ├── from-the-cloud.png │ ├── hdurer.png │ ├── hillenius.png │ ├── index.txt │ ├── kankanan.png │ ├── lorem │ │ ├── 2-columns.txt │ │ ├── full.txt │ │ └── multi-columns.txt │ ├── o-blog.conf │ ├── popineau.png │ ├── testimonials.txt │ └── underspecified.png ├── templates.graphml └── williamdenman.png └── templates ├── blog_archives.html ├── blog_index_category.html ├── blog_index_month.html ├── blog_index_year.html ├── blog_post-by-tags.html ├── blog_post.html ├── blog_rss.html ├── blog_sitemap.html ├── blog_static.html ├── blog_static_no_title.html ├── blog_tags-details.html ├── blog_tags.html ├── debug.html ├── debug_blog.html ├── debug_post.html ├── debug_posts.html ├── debug_snippets.html ├── debug_static.html ├── debug_tag.html ├── debug_tags.html ├── index_archives.html ├── index_category.html ├── index_month.html ├── index_year.html ├── nav_breadcrumb.html ├── nav_tag-cloud.html ├── page_footer.html ├── page_header.html ├── page_redirect.html ├── plugin_analytics.html ├── plugin_disqus.html ├── plugin_qrcode.html ├── sitemap_post.html ├── sitemap_static.html └── style ├── bootstrap ├── css │ └── bootstrap.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── js │ └── bootstrap.min.js ├── font-awesome ├── css │ └── font-awesome.min.css └── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff ├── jquery-2.1.1.min.js ├── jquery-2.1.1.min.map ├── o-blog-bootstrap.js ├── o-blog-bootstrap.min.js └── o-blog-load.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | /Font-Awesome 2 | /bootstrap* 3 | /css 4 | /example/out* 5 | /node_modules 6 | /templates-* 7 | /example-* 8 | /site/out -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Warning 4 | 5 | Please note at July 2nd 2014 `o-blog` has switch to version 2 which is 6 | compatible with version 1. Meanwhile you might experience some issues. You 7 | can still use `o-blog 1` by using the `o-blog-v1` branch instead of 8 | `master`. 9 | 10 | # Major change 11 | 12 | Now `o-blog` supports several source files: 13 | 14 | * org-mode 15 | * markdown 16 | 17 | # Demo 18 | 19 | Please have a look for a demo site and documentation: 20 | 21 | * `o-blog-v2` http://renard.github.com/o-blog-v2 22 | * Old `o-blog-v1` http://renard.github.com/o-blog 23 | -------------------------------------------------------------------------------- /js/o-blog-bootstrap.js: -------------------------------------------------------------------------------- 1 | function equalHeight(group) { 2 | tallest = 0; 3 | group.each(function() { 4 | thisHeight = $(this).height(); 5 | if(thisHeight > tallest) { 6 | tallest = thisHeight; 7 | } 8 | }); 9 | group.each(function() { $(this).height(tallest); }); 10 | } 11 | 12 | function ob_load_tags() { 13 | /* 14 | * Load all tag information into each '.ob-tagcloud' classes. 15 | * 16 | * Define a tagcloud as: 17 | * 18 | * 19 | * Where: 20 | * - `source` is the path to the JSON structure 21 | * - `path-to-root` is the path to the site root directory. 22 | * 23 | * Json structure is like: 24 | * { "tags" : [ 25 | * { "size" : "220.00", "path" : "tags/admin.html", "tag" : "Admin" }, 26 | * { ... } 27 | * ] } 28 | */ 29 | $.each($('.ob-tagcloud'), function(i, element) { 30 | var source = $(element).data('source') || "tags.js"; 31 | var path_to_root = $(element).data('path-to-root') || "."; 32 | $.getJSON(source, function(json) { 33 | var tags_list = []; 34 | $.each(json.tags, function(i, tag) { 35 | tags_list.push('
  • ' 38 | + tag.tag + '
  • '); 39 | }); 40 | $(element).replaceWith(''); 41 | }); 42 | }); 43 | } 44 | 45 | 46 | function ob_load_articles(callback) { 47 | /* 48 | * Load all articles information into each '.ob-articles' classes. 49 | * 50 | * Define a article as: 51 | * 54 | * 55 | * Where: 56 | * - `source` is the path to the JSON structure 57 | * - `path-to-root` is the path to the site root directory. 58 | * - `category` is the article category to handle 59 | * - `excerpt` if true add article excerpt, insert only titles otherwise 60 | * - `limit` is the article limit count 61 | * 62 | * Json structure is like: 63 | * { 64 | * "articles": { 65 | * "cat1": [ 66 | * { 67 | * "excerpt": "article excerpt", 68 | * "path": "cat1/2014/07/03_article1.html", 69 | * "title": "Article 1 in category 1 title" 70 | * }, ... 71 | * ], 72 | * "cat2": [ 73 | * { 74 | * "excerpt": "article excerpt", 75 | * "path": "cat2/2014/07/03_article1.html", 76 | * "title": "Article 1 in category 2 title" 77 | * }, ... 78 | * ], ... 79 | * } 80 | * } 81 | */ 82 | $.each($('.ob-articles'), function(i, element) { 83 | var source = $(element).data('source') || "articles.js"; 84 | var path_to_root = $(element).data('path-to-root') || "."; 85 | var limit = $(element).data('limit') || 10; 86 | var excerpt = $(element).data('excerpt') || false; 87 | var category = $(element).data('category'); 88 | 89 | $.getJSON(source).done(function(json) { 90 | var articles_list = []; 91 | $.each(json.articles[category], function(i, art_data) { 92 | if (i >= limit) return false; 93 | if (excerpt) { 94 | articles_list.push('

    ' 96 | + art_data.title + '

    ' 97 | + art_data.excerpt + '

    '); 98 | } else { 99 | articles_list.push('
  • ' + 101 | art_data.title + '
  • '); 102 | } 103 | }); 104 | if (excerpt) { 105 | $(element).replaceWith('
    ' 106 | + articles_list.join(' ') + '
    '); 107 | } else { 108 | $(element).replaceWith(''); 109 | } 110 | if ($.isFunction(callback)) { 111 | callback.call(); 112 | } 113 | }); 114 | }); 115 | } 116 | 117 | 118 | function init_menu_dropdown() { 119 | 120 | /* find active tab */ 121 | setTimeout(function() { 122 | $('.navbar .navbar-collapse > ul li a[href="' + 123 | path_to_root + '/' + ob_this_page + '"]') 124 | .parent().addClass('active') 125 | .parent().parent().addClass('active');}, 126 | 1); 127 | 128 | // Build nav bar 129 | var navbarUl = $('.navbar .navbar-collapse > ul'); 130 | navbarUl.addClass('nav').addClass('navbar-nav'); 131 | 132 | /* create the top menu bar */ 133 | var dropdown = navbarUl.find('li ul'); 134 | dropdown.parent().addClass('dropdown'); 135 | /* find sub menu items */ 136 | //dropdown.parent().findaddClass("dropdown-menu"); 137 | 138 | var dropdown_link = dropdown.parent().find('> a'); 139 | dropdown_link.addClass('dropdown-toggle') 140 | .attr("data-toggle", "dropdown") 141 | .find('b.caret').remove(); 142 | dropdown_link.append(' '); 143 | 144 | dropdown.addClass("dropdown-menu"); 145 | 146 | /* Add divider class if menu item is empty */ 147 | dropdown.parent().find('.dropdown-menu li').each(function() { 148 | if ( $(this).text() == '') $(this).addClass('divider') 149 | }); 150 | } 151 | 152 | 153 | 154 | $(document).ready( 155 | function() { 156 | //Load articles 157 | ob_load_articles(init_menu_dropdown); 158 | setTimeout(init_menu_dropdown, 50); 159 | 160 | 161 | /* Compute page min height */ 162 | $('div#page').css('min-height', $(window).innerHeight() - 163 | $('#footer').outerHeight() - 164 | $('div.navbar-fixed-top.navbar').outerHeight() - 165 | parseInt($('div#page').css('padding-top')) - 166 | parseInt($('div#page').css('padding-bottom'))); 167 | 168 | 169 | /* Make sure each .thumbnail is the same height for each row */ 170 | setTimeout(function() { 171 | $(".row").each(function() { 172 | equalHeight($(this).find(".thumbnail")); 173 | equalHeight($(this).find(".src")); 174 | }); 175 | }, 300); 176 | 177 | 178 | ob_load_tags(); 179 | }); 180 | 181 | -------------------------------------------------------------------------------- /less/o-blog.less: -------------------------------------------------------------------------------- 1 | /* @media (min-width: 768px) { */ 2 | /* @import "font-Yanone-Kaffeesatz.less"; */ 3 | /* @import "font-Ubuntu-Mono.less"; */ 4 | /* @import "font-Andika.less"; */ 5 | /* @monoFontFamily: "Ubuntu Mono", Menlo, Monaco, Consolas, "Courier New"; */ 6 | /* @sansFontFamily: "Andika", "Helvetica Neue", Helvetica, Arial, sans-serif; */ 7 | 8 | /* } */ 9 | @import "../bootstrap/less/bootstrap.less"; 10 | /* @import "../bootstrap/less/responsive.less"; */ 11 | 12 | @import "o-blog-default.less"; 13 | 14 | @ob-background: #2e3436; 15 | @ob-foreground: #eeeeec; 16 | @black: #000; 17 | @blue: #049cdb; 18 | 19 | .text-left() {text-align: left;} 20 | .text-center() {text-align: center;} 21 | .text-right() {text-align: right;} 22 | 23 | 24 | /* /\* @media screen and (min-width: 768px) { *\/ */ 25 | /* .equal, .equal > div[class*='col-'] { */ 26 | /* display: -webkit-box; */ 27 | /* display: -moz-box; */ 28 | /* display: -ms-flexbox; */ 29 | /* display: -webkit-flex; */ 30 | /* display: flex; */ 31 | /* flex:1 1 auto; */ 32 | /* } */ 33 | /* /\* } *\/ */ 34 | 35 | 36 | .buffer-color() { 37 | background: @ob-background; 38 | color: @ob-foreground; 39 | } 40 | 41 | 42 | .buffer() { 43 | .resizable(both); 44 | .box-shadow(0 3px 7px rgba(0,0,0,0.3)); 45 | border: 1px solid @gray-lighter; 46 | .buffer-color(); 47 | } 48 | 49 | 50 | .navtags(@font-size-base) { 51 | nav { 52 | &.tags{ 53 | ul { 54 | font-size: @font-size-base; 55 | margin: 2em auto; 56 | line-height: 2; 57 | .text-center(); 58 | li { 59 | display: inline; 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | 67 | html { 68 | height: 100%; 69 | background: @black; 70 | } 71 | 72 | 73 | .icon-white { 74 | opacity: 0.5; 75 | } 76 | 77 | 78 | 79 | .subnav-fixed { 80 | position: fixed; 81 | top: @navbar-height; 82 | left: 0; 83 | right: 0; 84 | z-index: 999; 85 | border-color: #D5D5D5; 86 | border-width: 0 0 1px; 87 | /* .border-radius(4px); */ 88 | -webkit-box-shadow: inset 0 1px 0 #fff, 0 1px 5px rgba(0,0,0,.1); 89 | -moz-box-shadow: inset 0 1px 0 #fff, 0 1px 5px rgba(0,0,0,.1); 90 | box-shadow: inset 0 1px 0 #fff, 0 1px 5px rgba(0,0,0,.1); 91 | 92 | ul.breadcrumb { 93 | margin: 0; 94 | padding: 0; 95 | 96 | li { 97 | margin: 0; 98 | padding: 0; 99 | } 100 | } 101 | } 102 | 103 | .subnav { 104 | width: 100%; 105 | /*height: @navbar-height - 9;*/ 106 | 107 | background-color: #EEE; 108 | #gradient > .vertical(#f5f5f5, #eeeeee); 109 | border: 1px solid #E5E5E5; 110 | /* .border-radius(4px); */ 111 | } 112 | 113 | 114 | #page { 115 | 116 | padding-top: 3 * @navbar-height; 117 | marging-top: @navbar-height; 118 | 119 | 120 | .article-header{ 121 | 122 | .date { 123 | border: 1px solid @gray-light; 124 | display: inline-block; 125 | width: 3em; 126 | .text-center(); 127 | a { 128 | display: block; 129 | color: fade(@linkColor, 50%); 130 | } 131 | .date-d { 132 | font-size: 1.5em; 133 | } 134 | .date-m { 135 | text-transform: uppercase; 136 | font-size: 1em; 137 | font-weight: bolder; 138 | } 139 | .date-y { 140 | background: @gray-light; 141 | font-size: 1em; 142 | font-weight: bolder; 143 | } 144 | } 145 | 146 | .page-header { 147 | time { 148 | div { 149 | padding: 1em; 150 | display: inline-block; 151 | .text-center(); 152 | border-radius: @pager-border-radius; 153 | background-color: @pager-bg; 154 | border: 1px solid @pager-border; 155 | vertical-align: middle; 156 | a { 157 | color: @gray-dark; 158 | } 159 | .day { 160 | display: block; 161 | font-size: 20pt; 162 | font-weight: 900; 163 | line-height: 1; 164 | } 165 | .month { 166 | display: block; 167 | font-size: 20pt; 168 | font-weight: 100; 169 | line-height: 1; 170 | } 171 | .year { 172 | display: block; 173 | font-size: 10pt; 174 | font-weight: 500; 175 | line-height: 1; 176 | } 177 | } 178 | h1 { 179 | display: inline; 180 | vertical-align: middle; 181 | margin-left: 1em; 182 | } 183 | } 184 | } 185 | 186 | 187 | 188 | .pager { 189 | margin-bottom: 0; 190 | margin-top: @baseLineHeight; 191 | } 192 | } 193 | 194 | 195 | .article-content { 196 | @outlineShift: 1em; 197 | .outline-1 { margin-left: 1 * @outlineShift; } 198 | .outline-2 { margin-left: 2 * @outlineShift; } 199 | .outline-3 { margin-left: 3 * @outlineShift; } 200 | .outline-4 { margin-left: 4 * @outlineShift; } 201 | .outline-5 { margin-left: 5 * @outlineShift; } 202 | .outline-6 { margin-left: 6 * @outlineShift; } 203 | 204 | 205 | .alert-heading { 206 | font-weight: bolder; 207 | } 208 | 209 | img {.center-block()} 210 | 211 | .show-grid [class*="span"] { 212 | background-color: @gray-lighter; 213 | /* .border-radius(3px); */ 214 | .show-grid [class*="span"] { 215 | background-color: darken(@gray-lighter, 10%); 216 | .show-grid [class*="span"] { 217 | background-color: darken(@gray-lighter, 20%); 218 | } 219 | 220 | } 221 | } 222 | 223 | p { 224 | text-align:justify; 225 | } 226 | .src pre { 227 | #gradient > .vertical(@start-color: lighten(#2e3436, 10%); @end-color: #2e3436); 228 | color: #eeeeee; 229 | } 230 | 231 | #footnotes { 232 | min-height: @baseLineHeight; 233 | padding-left: @baseLineHeight; 234 | padding-right: @baseLineHeight; 235 | margin-bottom: @baseLineHeight; 236 | background: lighten(#f5f5f5, 2%); 237 | border: 1px solid #eee; 238 | border: 1px solid rgba(0,0,0,.05); 239 | /* .border-radius(4px); */ 240 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 241 | font-size: 75%; 242 | 243 | h2 { 244 | &.footnotes {font-size: 90%;} 245 | } 246 | div { 247 | &.text-footnotes { 248 | p { 249 | font-size: 90%; 250 | &.footnote { 251 | a { 252 | &.footnum {} 253 | } 254 | } 255 | } 256 | } 257 | } 258 | 259 | } 260 | 261 | } 262 | 263 | .modal { 264 | .resizable(both) 265 | } 266 | 267 | .modal-body { 268 | ol.linenums { 269 | margin-left: 4 * @baseFontSize; 270 | } 271 | } 272 | 273 | pre.src, pre.example, .modal-body > pre { 274 | .buffer(); 275 | span { 276 | &.coderef-off { } 277 | &.linenr { 278 | font-style: italic; 279 | font-weight: bold; 280 | } 281 | &.code-highlighted {background: darkolivegreen;} 282 | 283 | } 284 | } 285 | 286 | 287 | .navtags(350%); 288 | 289 | 290 | .article-footer { 291 | &.well { 292 | background: lighten(#f5f5f5, 1%); 293 | 294 | h1 { 295 | font-size: 1.0em; 296 | margin: 0; 297 | } 298 | .tag-cloud ul { 299 | margin-bottom: 0; 300 | li { 301 | display: inline; 302 | padding-right: 0.5em; 303 | margin-left: 0.5em; 304 | font-size: 0.80 * @baseFontSize / (18/13); 305 | line-height: 0.92 * @baseLineHeight / (18/13); 306 | a { 307 | color: lighten(#f5f5f5, 1%); 308 | } 309 | /* a:hover { */ 310 | /* zoom: 1; */ 311 | /* } */ 312 | } 313 | } 314 | } 315 | } 316 | 317 | 318 | } 319 | 320 | .footer-font-size() { 321 | font-size: 0.85 * @baseFontSize / (18/13); 322 | line-height: 0.95 * @baseLineHeight / (18/13); 323 | } 324 | 325 | #footer { 326 | #gradient > .vertical(lighten(@black, 15%), @black); 327 | color: darken(@gray, 2%); 328 | 329 | .footer-font-size(); 330 | 331 | padding-top: @baseLineHeight * 3 / (18/13); 332 | 333 | a { 334 | color: lighten(@gray, 15%); 335 | } 336 | 337 | h1 { 338 | color: darken(@blue, 20%); 339 | a { 340 | color: darken(@blue, 20%); 341 | } 342 | /* a:hover { */ 343 | /* zoom: 1.1; */ 344 | /* } */ 345 | } 346 | 347 | .copyright { 348 | opacity: 0.35; 349 | margin-top: 3em; 350 | p { 351 | font-size: 80%; 352 | } 353 | } 354 | 355 | ul { 356 | list-style-type: none; 357 | margin: 0; 358 | padding-left: 0; 359 | li { 360 | .footer-font-size(); 361 | } 362 | } 363 | 364 | .navtags(100%); 365 | 366 | } 367 | 368 | 369 | .multicolumn(@cols, @gap) { 370 | -moz-column-count: @cols; 371 | -moz-column-gap: @gap; 372 | -webkit-column-count: @cols; 373 | -webkit-column-gap: @gap; 374 | column-count: @cols; 375 | column-gap: @gap; 376 | } 377 | 378 | .multicolumn3 { 379 | .multicolumn(3, 20px) 380 | } 381 | 382 | #toc { 383 | a { 384 | font-size: 75%; 385 | line-height: 15px; 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /lisp/o-blog-backend-markdown.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-backend-markdown.el --- Markdown backend for o-blog 2 | 3 | ;; Copyright © 2013 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2013-08-22 8 | ;; Last changed: 2014-07-04 22:26:13 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | (eval-when-compile 20 | (require 'eieio nil t) 21 | (require 'o-blog-backend nil t) 22 | (require 'markdown-mode nil t)) 23 | 24 | 25 | 26 | (defclass ob:backend:markdown (ob:backend) 27 | () 28 | "Object type handeling o-blog files in markdown.") 29 | 30 | (defmethod ob:find-files ((self ob:backend:markdown)) 31 | (set-slot-value self 'source-files 32 | (ob:find-files-1 self "txt"))) 33 | 34 | 35 | (defmethod ob:parse-config ((self ob:backend:markdown) &optional file) 36 | "Parse global configuration file." 37 | (let* ((file (or file 38 | (when (file-exists-p "o-blog.conf") 39 | "o-blog.conf"))) 40 | (lines (when file 41 | (split-string 42 | (with-temp-buffer 43 | (insert-file-contents file) 44 | (buffer-string)) 45 | "\n")))) 46 | (when lines 47 | (save-match-data 48 | (loop for line in lines 49 | when (string-match "^\\s-*\\([^#]+?\\)\\s-*=\\s-*\\(.+?\\)\\s-*$" line) 50 | do (let ((k (intern (match-string 1 line))) 51 | (v (match-string 2 line))) 52 | (when (slot-exists-p self k) 53 | (set-slot-value self k v)))))))) 54 | 55 | 56 | 57 | (defun ob:parse-tags (tags) 58 | "Parse TAGS and generate a list of `ob:tags'." 59 | (let ((tags (if (listp tags) 60 | tags (split-string tags "\\s-?,\\s-")))) 61 | (loop for tag in tags 62 | for td = (ob:replace-in-string tag '(("_" " ") ("@" "-"))) 63 | for ts = (ob:sanitize-string td) 64 | collect (ob:tag tag :display td :safe ts)))) 65 | 66 | 67 | (defmethod ob:parse-entries ((self ob:backend:markdown)) 68 | (loop for f in (ob:get 'source-files self) 69 | do (with-temp-buffer 70 | (insert-file-contents f) 71 | (let* ((headers (ob:markdown:get-headers)) 72 | (type (intern (plist-get headers 'type))) 73 | (types (intern (format "%ss" type))) 74 | (obj (funcall (intern (format "ob:%s" type)) 75 | f 76 | :file f 77 | :source (buffer-substring-no-properties 78 | (point-min) (point-max)))) 79 | (obj-list (ob:get types self))) 80 | 81 | (when (slot-exists-p obj 'timestamp) 82 | (set-slot-value obj 'timestamp 83 | (nth 5 (file-attributes f))) 84 | (ob:entry:compute-dates obj)) 85 | 86 | (when (slot-exists-p obj 'tags) 87 | (set-slot-value obj 'tags 88 | (ob:parse-tags (plist-get headers 'tags)))) 89 | 90 | (when (slot-exists-p obj 'category) 91 | (set-slot-value obj 'category 92 | (ob:category:init 93 | (ob:category 94 | (plist-get headers 'category))))) 95 | 96 | ;; Add rest of header parameters to object 97 | (loop for header in headers by #'cddr 98 | when (and 99 | (slot-exists-p obj header) 100 | (not (member header '(tags category)))) 101 | do (set-slot-value 102 | obj header (plist-get headers header))) 103 | 104 | (if (eq 'page type) 105 | (ob:entry:set-path obj 106 | (concat 107 | (ob:sanitize-string 108 | (or (plist-get headers 'page) 109 | (ob:get 'title obj))) 110 | ".html")) 111 | (ob:entry:set-path obj)) 112 | 113 | (set-slot-value self types (append obj-list (list obj))) 114 | 115 | (set-slot-value obj 'files-to-copy 116 | (ob:markdown:get-images obj)))))) 117 | 118 | 119 | 120 | 121 | (defmethod ob:convert-entry ((self ob:backend:markdown) entry) 122 | "" 123 | (with-temp-buffer 124 | (insert (ob:get 'source entry)) 125 | (ob:framework-expand "<\\([a-z][a-z0-9-]*\\)\\([^>]+\\)?>" "" "" "" "#") 126 | (let ((buff-src (current-buffer)) 127 | html) 128 | (with-temp-buffer 129 | (let ((buff-out (current-buffer))) 130 | (with-current-buffer buff-src 131 | (call-process-region (point-min) (point-max) "pandoc" nil buff-out)) 132 | (setf html (buffer-substring-no-properties (point-min)(point-max))))) 133 | (with-temp-buffer 134 | (insert html) 135 | 136 | (set-slot-value 137 | entry 'html (buffer-substring-no-properties (point-min)(point-max)))) 138 | (ob:get-post-excerpt entry)))) 139 | 140 | 141 | (defun ob:markdown:get-images (object) 142 | "" 143 | (with-temp-buffer 144 | (insert (ob:get 'source object)) 145 | (goto-char (point-min)) 146 | (let (files) 147 | ;; images 148 | (save-match-data 149 | (while (search-forward-regexp "\\]\\(([^)]+)\\)" nil t) 150 | (let ((link (match-string-no-properties 1))) 151 | (with-temp-buffer 152 | (insert link) 153 | (goto-char (point-min)) 154 | (save-match-data 155 | (while (search-forward-regexp "(\\(.+?\\)\\(?: \"[^\"]*\"\\)?)" 156 | nil t) 157 | (let ((file (match-string-no-properties 1))) 158 | (when (file-exists-p file) 159 | (push file files))))))))) 160 | files))) 161 | 162 | 163 | (defun ob:markdown:get-headers () 164 | "Return all headers from a markdown file. 165 | 166 | Headers must be defined within the first Comment block enclosed 167 | into \"\"." 168 | (save-excursion 169 | (save-restriction 170 | (save-match-data 171 | (widen) 172 | (goto-char (point-min)) 173 | (let* ((comment-start "") 175 | (start (when (search-forward comment-start nil t) 176 | (backward-char (length comment-start)) 177 | (point))) 178 | (end (when (search-forward comment-end nil t) 179 | (point)))) 180 | (when (and start end) 181 | 182 | (loop for line in (split-string 183 | (buffer-substring-no-properties start end) 184 | "\n") 185 | when (string-match 186 | "^\\s-*\\([^#]+?\\)\\s-*\\(?:[=:]\\)\\s-*\\(.+?\\)\\s-*$" line) 187 | nconc (let ((k (intern (match-string 1 line))) 188 | (v (match-string 2 line))) 189 | (list k v))))))))) 190 | 191 | 192 | 193 | (provide 'o-blog-backend-markdown) 194 | 195 | ;; o-blog-backend-markdown.el ends here 196 | -------------------------------------------------------------------------------- /lisp/o-blog-backend-org.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-backend-org.el --- org-mode backend for o-blog 2 | 3 | ;; Copyright © 2012 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2012-12-04 8 | ;; Last changed: 2014-07-07 19:31:20 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | (eval-when-compile 20 | (require 'eieio nil t) 21 | (require 'o-blog-backend nil t) 22 | (require 'ox) 23 | (require 'org-archive) 24 | (require 'org-table)) 25 | 26 | 27 | (defclass ob:backend:org (ob:backend) 28 | ((articles-filter :initarg :articles-filter 29 | :type string 30 | :initform "+TODO=\"DONE\"" 31 | :documentation "") 32 | (pages-filter :initarg :pages-filter 33 | :type string 34 | :initform "+PAGE={.+\.html}" 35 | :documentation "") 36 | (snippets-filter :initarg :snippets-filter 37 | :type string 38 | :initform "+SNIPPET={.+}" 39 | :documentation "")) 40 | "Object type handeling o-blog files in org-mode.") 41 | 42 | 43 | 44 | (defmethod ob:find-files ((self ob:backend:org) &optional file) 45 | "" 46 | (ob:find-files-1 self '("org"))) 47 | 48 | (defmethod ob:org-get-header ((self ob:backend:org) header &optional all) 49 | "Return value of HEADER option as a string from current org-buffer. If ALL is 50 | T, returns all occurrences of HEADER in a list." 51 | (ob:with-source-buffer 52 | self 53 | (save-excursion 54 | (save-restriction 55 | (save-match-data 56 | (widen) 57 | (goto-char (point-min)) 58 | (let (values) 59 | (while (re-search-forward 60 | (format "^#\\+%s:?[ \t]*\\(.*\\)" 61 | (replace-regexp-in-string 62 | "-" "[_-]" (symbol-name header))) nil t) 63 | (add-to-list 'values (substring-no-properties (match-string 1)))) 64 | (if all 65 | values 66 | (car values)))))))) 67 | 68 | (defmethod ob:org-get-entry-text ((self ob:backend:org)) 69 | "Return entry text from point with not properties. 70 | 71 | Please note that a blank line _MUST_ be present between entry 72 | headers and body." 73 | (save-excursion 74 | (save-restriction 75 | (save-match-data 76 | (org-narrow-to-subtree) 77 | (let ((text (buffer-string))) 78 | (with-temp-buffer 79 | (insert text) 80 | (goto-char (point-min)) 81 | (org-mode) 82 | (while (<= 2 (save-match-data (funcall outline-level))) 83 | (org-promote-subtree)) 84 | (goto-char (point-min)) 85 | (when (search-forward-regexp "^\\s-*$" nil t) 86 | (goto-char (match-end 0))) 87 | (save-excursion 88 | (insert "#+OPTIONS: H:7 num:nil toc:nil d:nil todo:nil <:nil pri:nil tags:nil\n\n")) 89 | (buffer-substring-no-properties (point) (point-max)))))))) 90 | 91 | 92 | (defmethod ob:parse-config ((self ob:backend:org)) 93 | "Par o-blog configuration directely from org header." 94 | (loop for slot in (object-slots self) 95 | for value = (ob:org-get-header self slot) 96 | when value 97 | do (set-slot-value self slot value)) 98 | ;; Set some imutalbe values. 99 | (let ((file (ob:get-name self))) 100 | (set-slot-value self 'index-file file) 101 | (set-slot-value self 'source-files (list file)) 102 | (set-slot-value self 'source-dir (file-name-directory file))) 103 | self) 104 | 105 | 106 | (defmethod ob:get-tags-list ((self ob:backend:org)) 107 | "" 108 | (loop for tn in (org-get-local-tags) 109 | for td = (ob:replace-in-string tn '(("_" " ") ("@" "-"))) 110 | for ts = (ob:sanitize-string td) 111 | collect (ob:tag tn :display td :safe ts))) 112 | 113 | (defmethod ob:parse-entry ((self ob:backend:org) marker type) 114 | "Parse an org-mode entry at position defined by MARKER." 115 | (save-excursion 116 | (with-current-buffer (marker-buffer marker) 117 | (goto-char (marker-position marker)) 118 | (when (search-forward-regexp org-complex-heading-regexp 119 | (point-at-eol) 120 | t) 121 | (let* ((type (or type 'article)) 122 | (class (intern (format "ob:%s" type))) 123 | (entry (funcall class marker))) 124 | 125 | (set-slot-value 126 | entry 'title 127 | (match-string-no-properties 4)) 128 | 129 | (when (slot-exists-p entry 'timestamp) 130 | (set-slot-value 131 | entry 'timestamp 132 | (apply 'encode-time 133 | (org-parse-time-string 134 | (or (org-entry-get (point) "CLOSED") 135 | (time-stamp-string 136 | "%:y-%02m-%02d %02H:%02M:%02S %u"))))) 137 | (ob:entry:compute-dates entry)) 138 | 139 | (when (slot-exists-p entry 'tags) 140 | (set-slot-value entry 'tags 141 | (ob:get-tags-list self))) 142 | 143 | (when (slot-exists-p entry 'category) 144 | (let ((cat (or (org-entry-get (point) "category") 145 | (car (last (org-get-outline-path)))))) 146 | (set-slot-value entry 147 | 'category 148 | (ob:category:init (ob:category cat))))) 149 | 150 | (when (slot-exists-p entry 'template) 151 | (let ((template (org-entry-get (point) "template"))) 152 | (when template 153 | (set-slot-value entry 'template template)))) 154 | 155 | (if (eq 'page type) 156 | (ob:entry:set-path entry 157 | (org-entry-get (point) "PAGE")) 158 | (ob:entry:set-path entry)) 159 | 160 | (set-slot-value 161 | entry 'source 162 | (ob:org-get-entry-text self)) 163 | 164 | entry))))) 165 | 166 | 167 | (defmethod ob:parse-entries-1 ((self ob:backend:org) type) 168 | "Collect all entries defined by TYPE from current org tree 169 | using `ob:parse-entry'." 170 | (let ((markers (org-map-entries 171 | 'point-marker 172 | (slot-value self (intern (format "%ss-filter" type))) 173 | 'file-with-archives))) 174 | (loop for marker in markers 175 | collect (ob:parse-entry self marker type) into items 176 | finally return (loop for item in (sort items (ob:get 'posts-sorter self)) 177 | for id = 0 then (incf id) 178 | do (set-slot-value item 'id id) 179 | and collect item)))) 180 | 181 | (defmethod ob:parse-entries ((self ob:backend:org)) 182 | "Parse all entries (articles, pages and snippets from current org tree." 183 | (ob:with-source-buffer 184 | self 185 | (loop for type in '(article page snippet) 186 | for class-type = (intern (format "%ss" type)) 187 | do (set-slot-value self class-type (ob:parse-entries-1 self type))) 188 | self)) 189 | 190 | 191 | (defmethod ob:org-fix-html-level-numbering ((self ob:backend:org)) 192 | "Promote every org-generated heading level by one in current buffer." 193 | (save-excursion 194 | (goto-char (point-min)) 195 | (save-match-data 196 | (while 197 | ;;(rx (group "foo") ":" (backref 1)) 198 | ;; as defined in `org-html-level-start' 199 | (re-search-forward "\n
    \n\\(.*?\\\)\n\\(
    \n\\)?" nil t) 200 | (let ((container (match-string-no-properties 1)) 201 | (level (1- (string-to-int (match-string-no-properties 2)))) 202 | (title-class (match-string-no-properties 3)) 203 | (title (match-string-no-properties 4)) 204 | (text (match-string-no-properties 5)) 205 | (text-id (match-string-no-properties 6))) 206 | (goto-char (match-beginning 0)) 207 | ;;(narrow-to-region (match-beginning 0) (match-end 0)) 208 | (delete-region (match-beginning 0) (match-end 0)) 209 | (insert 210 | (format 211 | "
    \n" 212 | container level) 213 | (format 214 | "%s\n" level title-class title level)) 215 | (when text 216 | (insert (format 217 | "
    \n" 218 | level text-id)))))))) 219 | 220 | 221 | (defmethod ob:org-fix-icons ((self ob:backend:org)) 222 | "Convert all \"icon-...\" to \"\" 223 | in current-buffer." 224 | (save-excursion 225 | (goto-char (point-min)) 226 | (save-match-data 227 | (while 228 | (re-search-forward "\\(icon-\\(?:[[:alpha:]]\\|[- ]\\)+\\)" nil t) 229 | (let ((icon (match-string-no-properties 1))) 230 | (goto-char (match-beginning 0)) 231 | (delete-region (match-beginning 0) (match-end 0)) 232 | (insert (format 233 | "" icon))))))) 234 | 235 | (defmethod ob:org-fix-html ((self ob:backend:org) html) 236 | "Perform some html fixes on org html export." 237 | (with-temp-buffer 238 | (insert html) 239 | (goto-char (point-min)) 240 | ;; Org export html wrapped within
    ...
    241 | ;; remove that div. 242 | (save-excursion 243 | (save-match-data 244 | (when (search-forward "
    " nil t) 245 | (delete-region (point-at-bol) (point-at-eol)) 246 | (goto-char (point-max)) 247 | (delete-region (point-at-bol) (point-at-eol))))) 248 | (ob:org-fix-html-level-numbering self) 249 | (ob:org-fix-icons self) 250 | (buffer-substring-no-properties (point-min) (point-max)))) 251 | 252 | 253 | (defmethod ob:convert-entry ((self ob:backend:org) entry) 254 | "Convert ENTRY to html using `org-mode' syntax." 255 | (with-temp-buffer 256 | ;; insert dummy comment in order to make inline html work. 257 | (insert (oref entry source)) 258 | (org-mode) 259 | (goto-char (point-min)) 260 | (ob:org-fix-org) 261 | (ob:org:publish-linked-files self entry) 262 | (ob:framework-expand) 263 | (ob:framework-expand "<\\([^:][^/ \n\t>]+\\)\\([^>]*\\)?>" "") 264 | 265 | ;; exporting block with ditaa is kinda messy since it requires a real 266 | ;; file (does not work with a temp-buffer which is not associated to any 267 | ;; file). 268 | (let* ((saved-file 269 | (when 270 | (re-search-forward "^#\\+BEGIN_SRC:?[ \t]+\\(ditaa\\)" nil t) 271 | (format "/%s/%s/%s.src.org" 272 | default-directory 273 | (ob:get 'publish-dir BLOG) 274 | ;; variable inherited from `ob-parse-entry' 275 | (ob:get 'htmlfile entry)))) 276 | (default-directory 277 | (if saved-file 278 | (file-name-directory saved-file) 279 | default-directory)) 280 | (org-confirm-babel-evaluate nil) 281 | ret) 282 | (when saved-file 283 | (unless (featurep 'ob-ditaa) 284 | (require 'ob-ditaa)) 285 | (ob:write-file saved-file)) 286 | (let ((html (substring-no-properties 287 | ;; `org-export-as-html' arguments has changed on new 288 | ;; org-version, then again with the new exporter. 289 | ;; First try old function signatures, then on failure 290 | ;; use new argument call. 291 | (or 292 | (ignore-errors (org-export-as-html nil nil nil 'string t)) 293 | (ignore-errors (org-export-as-html nil nil 'string t)) 294 | (org-export-as 'html nil nil t nil))))) 295 | ;;(message "Txt: %S" (buffer-substring-no-properties (point-min) (point-max))) 296 | ;;(message ": %S" (buffer-substring-no-properties (point-min) (point-max))) 297 | (set-slot-value entry 'html 298 | (ob:org-fix-html self html))) 299 | (when saved-file 300 | (delete-file saved-file)) 301 | (ob:get-post-excerpt entry) 302 | entry))) 303 | 304 | 305 | 306 | 307 | ;;(defmethod ob:org-fix-org ((self ob:backend:org)) 308 | (defun ob:org-fix-org () 309 | "Fix some org syntax." 310 | ;; This is a VERY ugly trick to ensure backward compatibility. 311 | (let* 312 | ((compute-bootstrap-grid-args 313 | (lambda (a1 a2) 314 | (let* ((i2c 315 | (lambda (x) 316 | (let ((x (if x (string-to-int x) 0))) 317 | (cond 318 | ((= 0 x) "") 319 | ((< 0 x) (format "col-sm-%d" x)) 320 | ((> 0 x) (format "col-sm-offset-%d" (- x)))))))) 321 | (format "%s %s" 322 | (funcall i2c a1) 323 | (funcall i2c a2))))) 324 | (subst-list 325 | '( 326 | ;; alerts 327 | ("^#\\+BEGIN_O_BLOG_ALERT:?[ \t]+\\(info\\|success\\|warning\\|error\\)[ \t]*\\(.*\\)" 328 | ("
    " 329 | (when (match-string 2) 330 | (mapconcat 331 | 'identity 332 | (list "

    " 333 | (match-string 2) "

    ") 334 | ""))) 335 | "^#\\+END_O_BLOG_ALERT" "
    ") 336 | ;; comment 337 | ("^#\\+BEGIN_COMMENT" 338 | "\n") 341 | ;; Hero unit 342 | ("^#\\+BEGIN_O_BLOG_HERO_UNIT" 343 | "
    \n" 344 | "^#\\+END_O_BLOG_HERO_UNIT" 345 | "
    \n") 346 | ;; Hero unit 347 | ("^#\\+BEGIN_O_BLOG_PAGE_HEADER" 348 | "
    \n" 349 | "^#\\+END_O_BLOG_PAGE_HEADER" 350 | "
    \n") 351 | ;; Well 352 | ("^#\\+BEGIN_O_BLOG_WELL" 353 | "
    \n" 354 | "^#\\+END_O_BLOG_WELL" 355 | "
    \n") 356 | ;;grid 357 | ("^#\\+BEGIN_O_BLOG_ROW:?[ \t]+\\(.*\\)" 358 | (let* ((args (when (stringp (match-string 1)) 359 | (split-string (match-string-no-properties 1))))) 360 | (format "
    " 361 | (or (nth 2 args) "") 362 | (funcall compute-bootstrap-grid-args 363 | (nth 0 args) 364 | (nth 1 args)) 365 | args)) 366 | "^#\\+END_O_BLOG_ROW" 367 | "
    ") 368 | ;; grid column 369 | ("^#\\+O_BLOG_ROW_COLUMN:?[ \t]+\\(-?[0-9]+\\)\\([ \t]+\\(-?[0-9]+\\)\\)?" 370 | (let* ((args (when (stringp (match-string 1)) 371 | (split-string (match-string-no-properties 1))))) 372 | (format "
    " 373 | (funcall compute-bootstrap-grid-args 374 | (nth 0 args) 375 | (nth 1 args)) 376 | args))) 377 | ;;badge or label 378 | ("\\([^,]\\)\\[\\[ob-\\(badge\\|label\\):\\(default\\|success\\|warning\\|important\\|info\\|inverse\\)\\]\\[\\(.+?\\)\\]\\]" 379 | (let* ((first (match-string 1)) 380 | (type (match-string 2)) 381 | (style (match-string 3)) 382 | (data (match-string 4))) 383 | (format "%s%s" 384 | first type type style data))) 385 | ;; progress 386 | ("\\([^,]\\)\\[\\[ob-progress:\\(info\\|success\\|warning\\|danger\\),?\\([^]]+\\)?\\]\\[\\(.+?\\)\\]\\]" 387 | (let ((first (match-string 1)) 388 | (style (match-string 2)) 389 | (value (match-string 4)) 390 | (extra (loop for elm in (split-string (or (match-string 3) "") ",") 391 | collect (if (string= elm "striped") 392 | (format "progress-%s" elm) 393 | elm) 394 | into ret 395 | finally return (mapconcat 'identity ret " ")))) 396 | (format 397 | "%s
    " 398 | first 399 | style 400 | extra 401 | value))) 402 | ;; modal source 403 | ("^#\\+O_BLOG_SOURCE:?[ \t]+\\(.+?\\)\\([ \t]+\\(.+\\)\\)?$" 404 | (let* ((src-file (match-string 1)) 405 | (src-file-name (file-name-nondirectory src-file)) 406 | (src-file-safe (ob:sanitize-string src-file-name)) 407 | (mode (match-string 3))) 408 | (format "
    ×

    %s

    %s
    " 409 | src-file-safe src-file-name src-file-safe 410 | src-file-name 411 | (with-temp-buffer 412 | (insert-file-contents src-file) 413 | (if mode 414 | (setq func (intern (format "%s-mode" mode))) 415 | (setq func (assoc-default src-file auto-mode-alist 416 | 'string-match))) 417 | (if (functionp func) 418 | (funcall func) 419 | (warn (concat "Mode %s not found for %s. " 420 | "Consider installing it. " 421 | "No syntax highlight would be bone this time.") 422 | mode src-file)) 423 | ;; Unfortunately rainbow-delimiter-mode does not work fine. 424 | ;; See https://github.com/jlr/rainbow-delimiters/issues/5 425 | (font-lock-fontify-buffer) 426 | (htmlize-region-for-paste (point-min) (point-max)))))) 427 | ))) 428 | (loop for (re_start sub_start re_end sub_end) 429 | in subst-list 430 | do (save-match-data 431 | (save-excursion 432 | (goto-char (point-min)) 433 | (while (re-search-forward re_start nil t) 434 | (beginning-of-line) 435 | (insert 436 | "#+BEGIN_HTML\n" 437 | (ob:string-template sub_start) 438 | "\n#+END_HTML\n") 439 | (delete-region (point) (point-at-eol)) 440 | (when re_end 441 | (save-excursion 442 | (unless 443 | (re-search-forward re_end nil t) 444 | (error "%s not found in %s@%s." re_end 445 | (buffer-file-name) 446 | (point))) 447 | (beginning-of-line) 448 | (insert 449 | "\n#+BEGIN_HTML\n" 450 | (ob:string-template sub_end) 451 | "\n#+END_HTML\n") 452 | (delete-region (point) (point-at-eol)))))))))) 453 | 454 | 455 | (defmethod ob:org:publish-linked-files((self ob:backend:org) entry) 456 | "Copy files (defined by \"file:\" link prefix) to page related directory." 457 | (save-match-data 458 | (save-excursion 459 | (goto-char (point-min)) 460 | (let ((htmlfile (ob:get 'htmlfile entry)) 461 | (page (slot-exists-p entry 'page)) 462 | (filepath (ob:get 'filepath entry)) 463 | (title (ob:get 'title entry)) 464 | ret) 465 | (while (re-search-forward "\\(\\[file:\\)\\([^]]+\\)\\(\\]\\)" nil t) 466 | (let ((prefix (match-string-no-properties 1)) 467 | (file (match-string-no-properties 2)) 468 | (suffix (match-string-no-properties 3))) 469 | 470 | (when (file-exists-p file) 471 | (replace-match 472 | (if page 473 | (format "%s%s/%s/%s%s" 474 | prefix 475 | (ob:get 'path-to-root entry) 476 | (or (file-name-directory htmlfile) ".") 477 | (file-name-nondirectory file) suffix) 478 | 479 | (format "%s%s/%s/%s/%s%s" 480 | prefix 481 | (ob:get 'path-to-root entry) 482 | (file-relative-name "." filepath) 483 | (file-name-sans-extension htmlfile) 484 | (file-name-nondirectory file) suffix )) 485 | 486 | (add-to-list 'ret file))))) 487 | 488 | (when ret 489 | (unless page 490 | ;; create a redirection page as index.html into files' directory 491 | (with-temp-buffer 492 | (insert 493 | (mapconcat 'identity 494 | `(,(format "* Redirect from (%s)" title) 495 | ":PROPERTIES:" 496 | ,(format ":PAGE: %s/index.html" (file-name-sans-extension htmlfile)) 497 | ":TEMPLATE: page_redirect.html" 498 | ":END:") 499 | "\n")) 500 | (org-mode) 501 | (goto-char (point-min)) 502 | (let ((redir (ob:parse-entry self (point-marker) 'page))) 503 | (message "REDIR: %S" redir) 504 | (set-slot-value redir 'path-to-root 505 | (concat "../" (ob:get 'path-to-root entry))) 506 | (set-slot-value self 'pages 507 | (append (ob:get 'pages self) 508 | (list redir)))))) 509 | 510 | ;; copy all files into their target directory. 511 | (message "Copy file: %S" ret) 512 | 513 | (loop for f in ret 514 | do (let ((target 515 | (if page 516 | (format "%s/%s" 517 | (ob:blog-publish-dir BLOG) 518 | (file-name-nondirectory f)) 519 | (format "%s/%s/%s" 520 | (ob:blog-publish-dir BLOG) 521 | ;; file path is nil when exporting static page? 522 | ;;(or filepath ".") 523 | (file-name-sans-extension htmlfile) 524 | (file-name-nondirectory f))))) 525 | (mkdir (file-name-directory target) t) 526 | (message "COPY %s -> %s" f target) 527 | (ob-do-copy f target)))))))) 528 | 529 | 530 | (provide 'o-blog-backend-org) 531 | 532 | ;; o-blog-backend-org.el ends here 533 | -------------------------------------------------------------------------------- /lisp/o-blog-backend.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-backend.el --- Base class for o-blog backends 2 | 3 | ;; Copyright © 2012 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2012-12-04 8 | ;; Last changed: 2014-07-07 23:26:14 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | 20 | (eval-when-compile 21 | (require 'find-func nil t) 22 | (require 'eieio nil t)) 23 | 24 | 25 | (defclass ob:backend nil 26 | ((index-file :initarg :index-file 27 | :type string 28 | :documentation "Path to o-blog index file.") 29 | (source-dir :initarg :source-dir 30 | :type string 31 | :documentation "Path to publishing 32 | directory (relative to o-blog configuration file 33 | path).") 34 | (source-files :initarg :file-name 35 | :type list 36 | :documentation "List of o-blog source files") 37 | (publish-dir :initarg :publish-dir 38 | :initform "out" 39 | :type string 40 | :documentation "Path to publishing 41 | directory (relative to o-blog configuration file 42 | path).") 43 | (style-dir :initarg :style-dir 44 | :initform "../templates/style" 45 | :type string 46 | :documentation "Path to style 47 | directory (relative to publish-dir).") 48 | (template-dir :initarg :publish-dir 49 | :initform (expand-file-name 50 | (concat (file-name-directory 51 | (find-library-name "o-blog")) 52 | "../templates")) 53 | :type string 54 | :documentation "Path to publishing 55 | directory (relative to o-blog configuration file 56 | path).") 57 | (articles :initarg :articles 58 | :type list 59 | :initform nil 60 | :documentation "List of ob:article") 61 | (pages :initarg :pages 62 | :type list 63 | :initform nil 64 | :documentation "List of ob:page") 65 | (snippets :initarg :snippets 66 | :type list 67 | :initform nil 68 | :documentation "List of ob:snippet") 69 | (tags :initarg :tags 70 | :type list 71 | :initarg nil 72 | :documentation "List of ob:tag") 73 | (title :initarg :title 74 | :type string 75 | :documentation "Site title") 76 | (description :initarg :description 77 | :type string 78 | :documentation "Site description") 79 | (url :initarg :url 80 | :type string 81 | :documentation "Site url") 82 | 83 | (posts-sorter :initarg :posts-sorter 84 | :initform ob:entry:sort-by-date 85 | :type symbol 86 | :documentation) 87 | 88 | ) 89 | 90 | "Object type handeling o-blog backend. All file or directory 91 | paths are relative to the o-blog configuration file." 92 | :abstract t) 93 | 94 | (defmethod ob:publish ((self ob:backend)) 95 | "Publish a new blog. 96 | 97 | Some global variables are set: 98 | - `BLOG': the full `ob:backend' child class. 99 | - `POSTS': a list of all sorted posts. 100 | - `TAGS': a list of all tags as `ob:tags' class. 101 | 102 | " 103 | (ob:find-files self) 104 | (ob:parse-config self) 105 | (ob:parse-entries self) 106 | (ob:compute-tags self) 107 | 108 | (let* ((BLOG self) 109 | (PAGES (ob:get 'pages BLOG)) 110 | (POSTS (ob:get 'articles BLOG)) 111 | (ALL-POSTS POSTS) 112 | (TAGS (ob:get 'tags BLOG))) 113 | 114 | ;; Convert each entry to HTML format 115 | (loop for type in '(snippets articles pages) 116 | do (loop for entry in (slot-value BLOG type) 117 | with id = 0 118 | do (progn 119 | (when (eq type 'articles) 120 | (set-slot-value entry 'id id) 121 | (setf id (1+ id))) 122 | (ob:convert-entry BLOG entry)))) 123 | 124 | ;; Publish both articles static pages 125 | (loop for type in '(articles pages) 126 | do (loop for POST in (slot-value BLOG type) 127 | do (ob:entry:publish POST))) 128 | 129 | 130 | ;; publish tags 131 | (let ((PATH-TO-ROOT "..")) 132 | (let ((FILE "tags/index.html")) 133 | (ob:eval-template-to-file "blog_tags.html" 134 | (format "%s/%s" 135 | (ob:get 'publish-dir BLOG) 136 | FILE))) 137 | (loop for TAG in TAGS 138 | do 139 | (let ((FILE (format "tags/%s.html" (ob:get 'safe TAG)))) 140 | (ob:eval-template-to-file "blog_tags-details.html" 141 | (format "%s/%s" 142 | (ob:get 'publish-dir BLOG) 143 | FILE)) 144 | (let ((POSTS (ob:get-posts (lambda (x) 145 | (member (ob:tags-safe TAG) 146 | (mapcar 'ob:tags-safe 147 | (ob:post-tags x))))))) 148 | (ob:eval-template-to-file "blog_rss.html" 149 | (format "%s/tags/%s.xml" 150 | (ob:get 'publish-dir BLOG) 151 | (ob:get 'safe TAG))))))) 152 | ;; Write tags JSON 153 | (with-temp-file 154 | (format "%s/%s" (ob:get 'publish-dir BLOG) "tags.js") 155 | (insert "{\"tags\":[") 156 | (if (not TAGS) 157 | (insert " ") 158 | (loop for TAG in TAGS 159 | do (insert 160 | (format 161 | "{\"size\":\"%.2f\",\"path\":\"tags/%s.html\",\"tag\":\"%s\"}," 162 | (ob:get 'size TAG) (ob:get 'safe TAG) (ob:get 'display TAG)))) 163 | ;; remove last comma 164 | (delete-char -1)) 165 | (insert "]}")) 166 | 167 | ;; Write articles JSON 168 | (with-temp-file 169 | (format "%s/%s" (ob:get 'publish-dir BLOG) "articles.js") 170 | (insert "{\"articles\":{ ") 171 | (message "LEN: %s / %s: %s" 172 | (length (ob:get-posts nil nil nil 'category)) 173 | (length (ob:get-posts)) 174 | (ob:get-posts nil nil nil 'category)) 175 | 176 | (loop for CATEGORY in (ob:get-posts nil nil nil 'category) 177 | do (progn 178 | (insert (format "\"%s\":[ " (ob:get 'safe CATEGORY))) 179 | (loop for article in 180 | (ob:get-posts (lambda (x) 181 | (equal CATEGORY 182 | (ob:get 'category x)))) 183 | do (insert 184 | (format 185 | "{\"title\":%S,\"path\":%S,\"excerpt\":%S}," 186 | (ob:get 'title article) 187 | (ob:get 'htmlfile article) 188 | (ob:get 'excerpt article) 189 | ))) 190 | ;; remove last comma 191 | (delete-char -1) 192 | (insert "],"))) 193 | ;; remove last comma 194 | (delete-char -1) 195 | (insert "}}")) 196 | 197 | 198 | (let ((BREADCRUMB "Archives")) 199 | (let ((FILE "archives.html")) 200 | (ob:eval-template-to-file "blog_archives.html" 201 | (format "%s/%s" 202 | (ob:get 'publish-dir BLOG) 203 | FILE)))) 204 | 205 | (ob:eval-template-to-file "blog_rss.html" 206 | (format "%s/index.xml" 207 | (ob:get 'publish-dir BLOG))) 208 | 209 | (ob:eval-template-to-file "blog_sitemap.html" 210 | (format "%s/sitemap.xml" 211 | (ob:get 'publish-dir BLOG))) 212 | 213 | (loop for CATEGORY in (ob:get-posts nil nil nil 'category) 214 | with PATH-TO-ROOT = ".." 215 | do 216 | (loop for YEAR in (ob:get-posts 217 | (lambda (x) 218 | (equal CATEGORY (ob:get 'category x))) 219 | nil nil 'year) 220 | with PATH-TO-ROOT = "../.." 221 | do 222 | (loop for MONTH in (ob:get-posts 223 | (lambda (x) 224 | (and 225 | (equal CATEGORY (ob:get 'category x)) 226 | (= YEAR (ob:get 'year x)))) 227 | nil nil 'month) 228 | with PATH-TO-ROOT = "../../.." 229 | do (ob-process-index "blog_index_month.html" CATEGORY YEAR MONTH)) 230 | and do (ob-process-index "blog_index_year.html" CATEGORY YEAR)) 231 | and do (unless (equal "." CATEGORY) 232 | (ob:eval-template-to-file "blog_rss.html" 233 | (format "%s/%s/index.xml" 234 | (ob:get 'publish-dir BLOG) 235 | (ob:get 'safe CATEGORY))) 236 | (ob-process-index "blog_index_category.html" CATEGORY))) 237 | ) 238 | (ob:publish-style self) 239 | self) 240 | 241 | (defun ob-process-index (template &optional category year month) 242 | "Low-level function for `ob-write-index'. 243 | 244 | Template is read from TEMPLATE file. 245 | 246 | If provided CATEGORY YEAR and MONTH are used to select articles." 247 | (let* ((FILE (format "%s/index.html" 248 | (cond 249 | ((and category year month) 250 | (format "%s/%.4d/%.2d" 251 | (ob:get 'safe category) 252 | year month)) 253 | ((and category year) 254 | (format "%s/%.4d" 255 | (ob:get 'safe category) 256 | year)) 257 | (t (ob:get 'safe category))))) 258 | (fp (format "%s/%s" (ob:get 'publish-dir BLOG) FILE)) 259 | 260 | (POSTS (ob:get-posts 261 | (lambda (x) 262 | (and 263 | (if category (equal category (ob:get 'category x)) t) 264 | (if year (= year (ob:get 'year x)) t) 265 | (if month (= month (ob:get 'month x)) t)))))) 266 | (ob:eval-template-to-file template fp))) 267 | 268 | 269 | 270 | 271 | 272 | ;; Useful functions / macros 273 | 274 | (defmacro ob:with-source-buffer (self &rest body) 275 | "Like `with-current-buffer'" 276 | `(let ((file (ob:get-name ,self))) 277 | ;; Make sure we are in the o-blog org file 278 | (with-current-buffer (or (get-file-buffer file) 279 | (find-file-noselect file)) 280 | ,@body))) 281 | 282 | 283 | (defmethod ob:must-override-1 ((self ob:backend) method) 284 | "Generate a warning when METHOD is not overridden in subclass." 285 | (message "Method `%s' is not defined in class `%s'." 286 | method (object-class self))) 287 | 288 | (defmethod ob:find-files-1 ((self ob:backend) extension 289 | &optional dir ignore-dir original-dir) 290 | "List all files under and `ob:backend' `source-dir' or DIR 291 | whose extensions are defined in EXTENSION list. 292 | 293 | IGNORE-DIR is a list of directory to ignore, by default (\"..\" 294 | \".\" \".git\") 295 | 296 | ORIGINAL-DIR is used to keep trace of source directory when 297 | called recursively." 298 | (let* ((dir (or dir (ob:get-source-directory self))) 299 | (original-dir (or original-dir dir)) 300 | (extension (if (listp extension) extension (list extension))) 301 | (ignore-dir (or ignore-dir '(".." "." ".git")))) 302 | (loop for file in (directory-files dir nil nil t) 303 | for file-path = (format "%s%s" 304 | (file-name-as-directory dir) file) 305 | when (and 306 | (file-directory-p file-path) 307 | (not (member file ignore-dir))) 308 | nconc (ob:find-files-1 self extension file-path ignore-dir original-dir) 309 | when (member (file-name-extension file) extension) 310 | collect (file-relative-name file-path original-dir)))) 311 | 312 | 313 | ;; 314 | ;; Methods that must be overridden 315 | ;; 316 | (defmethod ob:find-files ((self ob:backend)) 317 | "Generic method for finding files. This method MUST be 318 | overriden in subclasses." 319 | (ob:must-override-1 self 'ob:find-files)) 320 | 321 | (defmethod ob:parse-config ((self ob:backend)) 322 | "Parse blog configuration. This method MUST be 323 | overriden in subclasses." 324 | (ob:must-override-1 self 'ob:parse-config)) 325 | 326 | 327 | ;; Basic primitives 328 | (defmethod ob:get-configuration-file ((self ob:backend)) 329 | "Return o-blog configuration file, which is SELF instance name." 330 | (ob:get-name self)) 331 | 332 | (defmethod ob:get-source-directory ((self ob:backend)) 333 | "Return o-blog source directory from ob:backend SELF object." 334 | (file-name-as-directory 335 | (format "%s%s" 336 | (file-name-as-directory 337 | (file-name-directory (ob:get-configuration-file self))) 338 | (if (and 339 | (slot-exists-p self 'source-dir) 340 | (slot-boundp self 'source-dir)) 341 | (oref self source-dir) 342 | "")))) 343 | 344 | 345 | (defmethod ob:get-all-posts ((self ob:backend)) 346 | "Generic method for finding files. This method MUST be 347 | overriden in subclasses." 348 | (ob:must-override-1 self 'ob:get-all-posts)) 349 | 350 | 351 | (defmethod ob:compute-tags ((self ob:backend) &optional min_r max_r) 352 | "Return a sorted list of all ob:tags by name. 353 | 354 | Compute tag occurrence and their HTML percentage value. 355 | 356 | MIN_R and MAX_R are the minimum and maximum percentage value. If 357 | not provided 80 and 220 are used. This means ob:size is always 358 | within MIN_R and MAX_R inclusive." 359 | (let* ((tags-art (loop for article in (append (slot-value self 'articles)) 360 | append (slot-value article 'tags))) 361 | (tags-page (loop for page in (append (slot-value self 'pages)) 362 | append (slot-value page 'tags))) 363 | 364 | (tags (sort (append tags-art tags-page) 365 | #'(lambda (a b) (string< (ob:get-name a) 366 | (ob:get-name b))))) 367 | (min_r (or min_r 80)) 368 | (max_r (or max_r 220)) 369 | (min_f (length tags)) 370 | (max_f 0)) 371 | (set-slot-value 372 | self 'tags 373 | (loop for (count tag) in 374 | ;; Here extract uniq tags and count occurrences 375 | ;; (such as uniq -c does) 376 | ;; Each item of returned list is 377 | ;; (VALUE COUNT) 378 | ;; See http://stackoverflow.com/a/6055795 379 | (loop for (i . j) on tags 380 | with k = 1 381 | when (and 382 | j 383 | (string= (ob:get-name i) 384 | (ob:get-name (car j)))) 385 | do (incf k) 386 | else 387 | collect (progn 388 | (when (> k max_f) (setf max_f k)) 389 | (when (< k min_f) (setf min_f k)) 390 | (list k i)) 391 | and do (setf k 1)) 392 | do (progn 393 | (set-slot-value tag 'count count) 394 | ;; This is the tricky part 395 | ;; Formula is: 396 | ;; % = min_r + (count - min_f) * (max_r - min_r) / (max_f - min_f) 397 | ;; the `max' is on purpose in case of max_f = min_f 398 | (set-slot-value tag 'size 399 | (+ min_r 400 | (/ 401 | (* (- count min_f) (- max_r min_r)) 402 | (max 1.0 (float (- max_f min_f))))))) 403 | collect tag)))) 404 | 405 | 406 | ;; 407 | ;; Publishing functions 408 | ;; 409 | 410 | 411 | 412 | ;; (defmethod ob:publish ((self ob:backend)) 413 | ;; "" 414 | ;; ;; Publish both articles static pages 415 | ;; (loop for type in '(articles pages) 416 | ;; do (loop for POST in (slot-value blog type) 417 | ;; do (ob:entry:publish POST))) 418 | 419 | ;; ;; publish tags 420 | ;; (with-temp-buffer 421 | ;; (ob:insert-template "page_tags.html") 422 | ;; (ob:write-file (format "%s/tags/index.html" 423 | ;; (oref blog publish-dir)))) 424 | 425 | ;; ;; publish tags 426 | ;; (with-temp-buffer 427 | ;; (ob:insert-template "page_tags.html") 428 | ;; (ob:write-file (format "%s/tags/index.html" 429 | ;; (oref blog publish-dir)))) 430 | ;; (loop for TAG in TAGS 431 | ;; do 432 | ;; (ob-write-index-to-file "blog_tags-details.html" 433 | ;; (format "%s/tags/%s.html" 434 | ;; (ob:blog-publish-dir BLOG) 435 | ;; (ob:tags-safe TAG))))) 436 | 437 | 438 | (defun ob:get-posts (&optional predicate count sortfunc collect) 439 | "Return posts (from `POSTS' list of `ob:entry' as defined in 440 | `o-blog-publish') matching PREDICATE. Limit to COUNT results if 441 | defined and sorted using SORTFUNC. 442 | 443 | PREDICATE is a function run for each post with the post itself as 444 | argument. If PREDICATE is nil, no filter would be done on posts. 445 | 446 | SORTFUNC is used a `sort' PREDICATE. 447 | 448 | If COLLECT is defined, only returns the COLLECT slot of 449 | `ob:entry' class. 450 | 451 | Examples: 452 | 453 | - Getting last 10 posts: 454 | \(ob:get-posts nil 10\) 455 | 456 | - Getting post from January 2012: 457 | \(ob:get-posts 458 | \(lambda \(x\) 459 | \(and \(= 2012 \(ob:get 'year x\)\) 460 | \(= 1 \(ob:get 'month x\)\)\)\)\) 461 | 462 | - getting all categories 463 | \(ob:get-posts nil nil nil 'category\) 464 | 465 | " 466 | (let* ((posts (if predicate 467 | (loop for post in POSTS 468 | when (funcall predicate post) 469 | collect post) 470 | POSTS)) 471 | (len (length posts))) 472 | (when (and count (> count 0) (< count len)) 473 | (setq posts (butlast posts (- len count)))) 474 | (when sortfunc 475 | (setq posts (sort posts sortfunc))) 476 | (when collect 477 | (setq posts 478 | (loop for post in posts 479 | with ret = nil 480 | collect (ob:get collect post) 481 | into ret 482 | finally return (delete-dups ret)))) 483 | posts)) 484 | 485 | 486 | 487 | 488 | (defun ob:get-last-post (&optional category nth) 489 | "Get the NTH last post in from CATEGORY or \"blog\" if not defined." 490 | (let ((POSTS ALL-POSTS) 491 | (nth (or nth 0))) 492 | (nth nth (ob:get-posts (lambda (x) 493 | (equal (or category "blog") 494 | (ob:get-name (ob:get 'category x)))))))) 495 | 496 | 497 | (defun ob:get-snippet (name &optional slot blog) 498 | "" 499 | (let* ((blog (or 500 | blog 501 | (when (boundp 'BLOG) BLOG) 502 | (when (boundp 'blog) blog))) 503 | (POSTS (ob:get 'snippets blog)) 504 | (snippet (car 505 | (ob:get-posts 506 | (lambda(x) 507 | (equal name (ob:get 'title x))))))) 508 | (if (and slot snippet) 509 | (ob:get slot snippet) 510 | snippet))) 511 | 512 | 513 | 514 | 515 | (provide 'o-blog-backend) 516 | 517 | ;; o-blog-backend.el ends here 518 | -------------------------------------------------------------------------------- /lisp/o-blog-entry.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-entry.el --- 2 | 3 | ;; Copyright © 2013 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2013-01-21 8 | ;; Last changed: 2014-07-11 17:31:26 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | 20 | (eval-when-compile 21 | (require 'htmlize nil t) 22 | (require 'sgml-mode nil t) 23 | (require 'eieio nil t)) 24 | 25 | 26 | (defclass ob:entry () 27 | ((id :initarg :id 28 | :type integer 29 | :documentation "entry ID") 30 | (title :initarg :title 31 | :type string 32 | :documentation "") 33 | (source :initarg :source 34 | :type string 35 | :documentation "Article raw source") 36 | (html :initarg :html 37 | :type string 38 | :documentation "Article html result") 39 | (path :initarg :path 40 | :type string 41 | :documentation "Path to article publication") 42 | (file :initarg :file 43 | :type string 44 | :documentation "") 45 | (files-to-copy :initarg :files-to-copy 46 | :type list 47 | :documentation "List of files to copy when publishing") 48 | (tags :initarg :tags 49 | :type list 50 | :documentation "List of ob:tag") 51 | 52 | (excerpt :initarg :excerpt 53 | :type string 54 | :documentation "small entry extract") 55 | 56 | (htmlfile :initarg :htmlfile 57 | :type string 58 | :documentation "") 59 | (path-to-root :initarg :path-to-root 60 | :type string 61 | :documentation "") 62 | ) 63 | "Object type handeling o-blog entries.") 64 | 65 | 66 | 67 | 68 | ;; Class aliases 69 | 70 | (defclass ob:article (ob:entry) 71 | ((timestamp :initarg :timestamp 72 | :type list 73 | :documentation "") 74 | (year :initarg :year 75 | :type integer 76 | :documentation "") 77 | (month :initarg :month 78 | :type integer 79 | :documentation "") 80 | (day :initarg :day 81 | :type integer 82 | :documentation "") 83 | (category :initarg :category 84 | ;; :type ob:category 85 | :documentation "") 86 | (template :initarg :template 87 | :initform "blog_post.html" 88 | :type string 89 | :documentation "Template file to use for publication") 90 | ) 91 | "O-blog page article") 92 | 93 | (defmethod ob:entry:set-path ((self ob:entry)) 94 | "" 95 | (set-slot-value 96 | self 'path (format "%s" 97 | (ob:sanitize-string (oref self title)))) 98 | (set-slot-value 99 | self 'htmlfile (format "%s/%s" 100 | (oref self path) 101 | (oref self path)))) 102 | 103 | (defmethod ob:entry:compute-dates ((self ob:article)) 104 | "" 105 | (let ((timestamp (oref self timestamp))) 106 | (when timestamp 107 | (loop for (p f) in '((year "%Y") (month "%m") (day "%d")) 108 | do (set-slot-value 109 | self p 110 | (string-to-number 111 | (format-time-string f timestamp))))))) 112 | 113 | (defmethod ob:entry:set-path ((self ob:article)) 114 | "" 115 | (set-slot-value 116 | self 'path (format "%s/%.4d/%.2d" 117 | (ob:sanitize-string (oref (oref self category) safe)) 118 | (oref self year) 119 | (oref self month))) 120 | (set-slot-value 121 | self 'file (format "%.2d_%s.html" 122 | (oref self day) 123 | (ob:sanitize-string (oref self title)))) 124 | (set-slot-value 125 | self 'htmlfile (if (string= "." (oref self path)) 126 | (oref self file) 127 | (format "%s/%s" 128 | (oref self path) 129 | (oref self file)))) 130 | (set-slot-value 131 | self 'path-to-root (file-relative-name 132 | "." 133 | (oref self path)))) 134 | 135 | (defmethod ob:get-post-excerpt ((self ob:entry) &optional words ellipsis) 136 | "Return the first WORDS from POST html content. 137 | 138 | The return string would be unformatted plain text postfixed by 139 | ELLIPSIS if defined.." 140 | (when (slot-exists-p self 'excerpt) 141 | (with-temp-buffer 142 | (insert (ob:get 'html self)) 143 | (let ((words (or words 20)) 144 | (ellipsis (or ellipsis "…")) 145 | (html2text-remove-tag-list 146 | (loop for tag in html-tag-alist 147 | collect (car tag)))) 148 | (html2text) 149 | (goto-char (point-min)) 150 | ;; remove all comments 151 | (save-excursion 152 | (save-match-data 153 | (while (search-forward-regexp "")) 157 | (delete-region start (point)))))) 158 | (save-excursion 159 | (save-match-data 160 | (while (search-forward-regexp "\\(\\s-*\n\\s-*\\)" nil t) 161 | (delete-region (- (point) (length (match-string 0))) (point)) 162 | (insert " ")))) 163 | (loop for x from 0 below words do (forward-word)) 164 | (set-slot-value self 'excerpt 165 | (concat 166 | (buffer-substring-no-properties 167 | (point-min) (point)) 168 | ellipsis)))))) 169 | 170 | 171 | (defmethod ob:entry:publish ((self ob:entry) &optional blog-obj) 172 | "" 173 | (let ((blog-obj (or blog-obj 174 | (when (boundp 'BLOG) BLOG) 175 | (when (boundp 'blog) blog))) 176 | (FILE (oref self htmlfile))) 177 | (unless (ob:backend-child-p blog-obj) 178 | (error "`ob:entry:publish': blog-obj is not an `ob:backend' child class.")) 179 | (when (slot-exists-p self 'template) 180 | (with-temp-buffer 181 | (ob:insert-template (oref self template)) 182 | (message "Write to: %s" 183 | (expand-file-name (format "%s/%s" 184 | (oref BLOG publish-dir) 185 | FILE))) 186 | (ob:write-file (format "%s/%s" 187 | (oref BLOG publish-dir) 188 | FILE))) 189 | 190 | (loop for file in (ob:get 'files-to-copy self) 191 | do (ob-do-copy (format "%s/%s" 192 | (or 193 | (file-name-directory (ob:get-name self)) ".") 194 | file) 195 | (format (format "%s/%s" 196 | (oref BLOG publish-dir) 197 | (oref self path))))) 198 | 199 | ;; (with-temp-buffer 200 | ;; (insert (oref self html)) 201 | ;; (ob:write-file (format "%s/%s.txt" 202 | ;; (oref BLOG publish-dir) 203 | ;; (oref self htmlfile)))) 204 | ))) 205 | 206 | (defclass ob:page (ob:entry) 207 | ((template :initarg :template 208 | :initform "blog_static.html" 209 | :type string 210 | :documentation "Template file to use for publication")) 211 | "O-blog page class") 212 | 213 | (defmethod ob:entry:set-path ((self ob:page) page) 214 | "" 215 | (set-slot-value self 'path ".") 216 | (set-slot-value self 'file page) 217 | (set-slot-value 218 | self 'htmlfile (if (string= "." (oref self path)) 219 | (oref self file) 220 | (format "%s/%s" 221 | (oref self path) 222 | (oref self file)))) 223 | (set-slot-value 224 | self 'path-to-root (file-relative-name 225 | "." 226 | (oref self path)))) 227 | 228 | (defclass ob:snippet (ob:entry) 229 | nil 230 | "O-blog snippet class") 231 | 232 | 233 | 234 | (defun ob:entry:get (value &optional entry) 235 | "" 236 | (let ((entry (or entry 237 | (when (boundp 'POST) POST)))) 238 | (slot-value entry value))) 239 | 240 | 241 | (defun ob:entry:sort-by-date (a b) 242 | "Sort both A and B posts by date (newer posts first)." 243 | (> (float-time (ob:get 'timestamp a)) 244 | (float-time (ob:get 'timestamp b)))) 245 | 246 | (defun ob:get-post-by-id (id) 247 | "Return post which id is ID" 248 | (when (>= id 0) 249 | (nth id POSTS))) 250 | 251 | 252 | 253 | (provide 'o-blog-entry) 254 | 255 | ;; o-blog-entry.el ends here 256 | -------------------------------------------------------------------------------- /lisp/o-blog-framework.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-framework.el --- Base class for o-blog frameworks 2 | 3 | ;; Copyright © 2013 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2013-06-05 8 | ;; Last changed: 2014-07-07 03:47:36 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | (eval-when-compile 20 | (require 'eieio nil t)) 21 | 22 | 23 | (defclass ob:framework-component nil 24 | ((backends :initarg :backends 25 | :type (or symbol list) 26 | :documentation "List of supported backends") 27 | (start :initarg :start 28 | :type (or string list) 29 | :documentation "Opening replacement from suitable for 30 | `ob:string-template'") 31 | (end :initarg :end 32 | :type (or null string list) 33 | :documentation "Closing replacement from suitable for 34 | `ob:string-template'. If nil, no closing tag is required") 35 | (alias :initarg :alias 36 | :type (or null symbol) 37 | :documentation "ob:framework-component name alias")) 38 | 39 | "Component used by ob:compute-framework") 40 | 41 | 42 | (setq ob:framework-components 43 | (list 44 | (ob:framework-component 45 | 'lead :backends '(org) 46 | :start "

    " 47 | :end "

    ") 48 | 49 | (ob:framework-component 50 | 'mark :backends '(org) 51 | :start "" 52 | :end "") 53 | 54 | (ob:framework-component 55 | 'del :backends '(org) 56 | :start "" 57 | :end "") 58 | 59 | (ob:framework-component 60 | 'kbd :backends '(org) 61 | :start "" 62 | :end "") 63 | 64 | 65 | (ob:framework-component 66 | 'right :backends '(org) 67 | :start "

    " 68 | :end "

    ") 69 | 70 | (ob:framework-component 71 | 'left :backends '(org) 72 | :start "

    " 73 | :end "

    ") 74 | 75 | (ob:framework-component 76 | 'justify :backends '(org) 77 | :start "

    " 78 | :end "

    ") 79 | 80 | (ob:framework-component 81 | 'center :backends '(org) 82 | :start "

    " 83 | :end "

    ") 84 | 85 | (ob:framework-component 86 | 'nowrap :backends '(org) 87 | :start "

    " 88 | :end "

    ") 89 | 90 | 91 | 92 | 93 | (ob:framework-component 94 | 'jumbotron :backends '(org) 95 | :start "
    " 96 | :end "
    ") 97 | (ob:framework-component 98 | 'page-header :backends '(org) 99 | :start '(format 100 | "

    %s%s

    " 101 | title 102 | (if (boundp 'subtitle) 103 | (format " %s" subtitle) 104 | ""))) 105 | (ob:framework-component 106 | 'caption :backends '(org) 107 | :start '(format 108 | "
    %s" 109 | (if (boundp 'title) 110 | (format "

    %s

    " title) 111 | "")) 112 | :end "
    ") 113 | (ob:framework-component 114 | 'thumbnail :backends '(org) 115 | :start "
    " 116 | :end "
    ") 117 | 118 | (ob:framework-component 119 | 'glyphicon :backends '(org) 120 | :start '("")) 121 | (ob:framework-component 122 | 'icon :backends '(org) 123 | :start '("")) 124 | (ob:framework-component 125 | 'row :backends '(org) 126 | :start (format "
    " 127 | (if (boundp 'equal) " equal" "")) 128 | :end "
    ") 129 | (ob:framework-component 130 | 'col :backends '(org) 131 | :start '("
    ") 143 | :end "
    ") 144 | (ob:framework-component 145 | 'panel :backends '(org) 146 | :start '("
    ") 149 | :end "
    ") 150 | (ob:framework-component 151 | 'panel-heading :backends '(org) 152 | :start '("
    " 153 | (when (boundp 'title) 154 | (format "

    %s

    " title))) 155 | :end "
    ") 156 | (ob:framework-component 157 | 'panel-body :backends '(org) 158 | :start '("
    ") 159 | :end "
    ") 160 | (ob:framework-component 161 | 'panel-footer :backends '(org) 162 | :start '("
    ") 163 | :end "
    ") 164 | (ob:framework-component 165 | 'label :backends '(org) 166 | :start '(format "" 167 | (if (boundp 'mod) mod "default")) 168 | :end "") 169 | (ob:framework-component 170 | 'badge :backends '(org) 171 | :start "" 172 | :end "") 173 | (ob:framework-component 174 | 'alert :backends '(org) 175 | :start '(format "
    " 176 | (if (boundp 'mod) mod "warning")) 177 | :end "
    ") 178 | (ob:framework-component 179 | 'well :backends '(org) 180 | :start '(format "
    " 181 | (if (boundp 'mod) mod "lg")) 182 | :end "
    ") 183 | (ob:framework-component 184 | 'thumbnail :backends '(org) 185 | :start "
    " 186 | :end "
    ") 187 | 188 | (ob:framework-component 189 | 'table :backends '(org) 190 | :start '(format "" 191 | (loop for v in '(striped bordered hover condensed) 192 | with out = '() 193 | when (boundp v) 194 | collect (format "table-%s" v) into out 195 | finally return (mapconcat 'identity out " "))) 196 | 197 | :end "
    ") 198 | 199 | (ob:framework-component 200 | 'tr :backends '(org) 201 | :start '(format "" (if (boundp 'mod) (format " class=\"%s\"" mod) "")) 202 | :end "") 203 | 204 | (ob:framework-component 205 | 'td :backends '(org) 206 | :start '(format "" (if (boundp 'mod) (format " class=\"%s\"" mod) "")) 207 | :end "") 208 | 209 | (ob:framework-component 210 | 'source :backends '(org) 211 | :start '(let* ((cur-point (point)) 212 | (end-point (unless (boundp 'src-file) 213 | (save-match-data 214 | (search-forward (format "" tag))))) 215 | (content (when end-point 216 | (buffer-substring-no-properties (1+ cur-point) 217 | (- end-point (length (format "" tag)))))) 218 | (func (when (and (boundp 'mode) mode) 219 | (setq func (intern (format "%s-mode" mode))))) 220 | html) 221 | 222 | (with-temp-buffer 223 | (if (boundp 'src-file) 224 | (progn 225 | (insert-file-contents src-file) 226 | (unless func 227 | (setq func (assoc-default src-file auto-mode-alist 228 | 'string-match)))) 229 | (insert content)) 230 | (if (functionp func) 231 | (funcall func) 232 | (warn (concat "Mode %s not found for %s. " 233 | "Consider installing it. " 234 | "No syntax highlight would be bone this time.") 235 | mode (if (boundp 'src-file) src-file "inline"))) 236 | ;; Unfortunately rainbow-delimiter-mode does not work fine. 237 | ;; See https://github.com/jlr/rainbow-delimiters/issues/5 238 | (font-lock-fontify-buffer) 239 | (setf html (htmlize-region-for-paste (point-min) (point-max)))) 240 | 241 | (when end-point 242 | (delete-region cur-point (- end-point (length (format "" tag))))) 243 | (format "
    %s
    " (or mode "") html))) 244 | (ob:framework-component 'src :backends '(org) :alias 'source) 245 | 246 | (ob:framework-component 247 | 'columns :backends '(org) 248 | :start '(format "
    " 249 | (if (boundp 'width) 250 | (format "column-width:%s;-webkit-column-width:%s;-moz-column-width:%s" width width width) "") 251 | (if (boundp 'count) (format " column-count:%s;-webkit-column-count:%s;-moz-column-count:%s;" count count count) "")) 252 | :end "
    ") 253 | 254 | 255 | )) 256 | 257 | (defun ob:framework-expand (&optional re-start re-end prefix suffix comment) 258 | "Expand framework widgets using RE-START and RE-END to delimit notations, 259 | convert widget to their HTML notation. COMMENT is an escape 260 | string to prevent widget expansion. 261 | 262 | RE-START is a 2-group regexp. First group is the widget name, 263 | second one is a list of parameters. 264 | 265 | RE-END is passed to `format' with widget name as parameter. 266 | " 267 | (let ((prefix (or prefix "@@html:")) 268 | (suffix (or suffix "@@")) 269 | (re-start (or re-start "^#\\+\\(?:begin_\\)?\\([^ \n\t:]+\\):?\\([^\n>]+\\)?$")) 270 | (re-end (or re-end "^#\\+end_%s")) 271 | (items (loop for c in ob:framework-components 272 | collect (cons (ob:get-name c) c))) 273 | (comment (concat "\\s-" (or comment ",")))) 274 | 275 | (save-match-data 276 | (save-excursion 277 | (save-restriction 278 | (widen) 279 | (goto-char (point-min)) 280 | ;; Lookup for a XML-like tag. 281 | ;; Make sure tag does not start with ":" since "<:nil" is a valid 282 | ;; org-mode option 283 | (while (re-search-forward re-start nil t) 284 | (let* (;; Memorize match as XML string and match positions 285 | (xml 286 | (format "<%s%s>" 287 | (match-string-no-properties 1) 288 | (or (match-string-no-properties 2) ""))) 289 | (beg (+ (match-beginning 0) 290 | (if (string= "," (match-string 1)) 1 0))) 291 | (end (match-end 0)) 292 | ;; Make sure XML fragment is valid. If so, xml-parsed 293 | ;; should be not nil and look like: 294 | ;; 295 | ;; (tag ((attr1 . "value1") (attrN . "valueN"))) 296 | (xml-parsed 297 | (with-temp-buffer 298 | (insert xml) 299 | (unless (search-backward "/>" nil t 1) 300 | (delete-backward-char 1) 301 | (insert "/>")) 302 | (libxml-parse-xml-region (point-min) (point-max)))) 303 | (tag (car xml-parsed)) 304 | (attr (cadr xml-parsed)) 305 | (replacement (cdr (assoc tag items))) 306 | ;; , is used to comment out tagging 307 | (commented 308 | (save-match-data 309 | (save-excursion 310 | (beginning-of-line) 311 | (when (re-search-forward comment (point-at-eol) t) 312 | (delete-backward-char 1) t))))) 313 | 314 | (when (ob:get 'alias replacement) 315 | (setq replacement 316 | (cdr (assoc (ob:get 'alias replacement) items)))) 317 | 318 | (when (and replacement (not commented)) 319 | ;; Convert the attribute list to variables 320 | (cl-progv 321 | (mapcar #'car attr) (mapcar #'cdr attr) 322 | ;; Replace first tag 323 | (narrow-to-region beg end) 324 | (delete-region (point-min) (point-max)) 325 | (widen) 326 | (insert 327 | prefix 328 | (ob:string-template (ob:get 'start replacement)) 329 | suffix) 330 | 331 | ;; If tag must be closed, look up for closing tag 332 | (when (ob:get 'end replacement) 333 | (save-match-data 334 | (save-excursion 335 | (unless (re-search-forward (format re-end tag) nil t) 336 | (error "Unclosed %s tag (%s)" tag (format re-end tag))) 337 | (narrow-to-region (match-beginning 0) (match-end 0)) 338 | (delete-region (point-min) (point-max)) 339 | (insert 340 | prefix 341 | (ob:string-template (ob:get 'end replacement)) 342 | suffix) 343 | (widen))))))))))))) 344 | 345 | 346 | 347 | 348 | (defclass ob:framework nil 349 | () 350 | "") 351 | 352 | 353 | 354 | 355 | ;; 356 | ;; Methods that must be overridden 357 | ;; 358 | (defmethod ob:compute-framework ((self ob:framework)) 359 | "Compute framework specific extentions. This method MUST be 360 | overriden in subclasses." 361 | (message "Method `%s' is not defined in class `%s'." 362 | 'ob:compute-framework (object-class self))) 363 | 364 | 365 | 366 | (provide 'o-blog-framework) 367 | 368 | ;; o-blog-framework.el ends here 369 | -------------------------------------------------------------------------------- /lisp/o-blog-i18n.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-i18n.el --- Internationalization for o-blog. 2 | 3 | ;; Copyright © 2012 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2012-04-10 8 | ;; Last changed: 2013-03-28 23:15:46 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | 20 | (defvar o-blog-i18n 21 | '(("en" 22 | :posted-on "Posted on" 23 | :post-timestamp "%A %B, %d %Y at %H:%M:%S" 24 | :related-tags "related tags" 25 | :home "Home" 26 | :tags "Tags" 27 | :archives "Archives" 28 | :rss "RSS" 29 | :about "About" 30 | :links "Links" 31 | :redirect-header "Redirect" 32 | :redirect-text "Oups! you shouldn't be here. Please wait for redirection." 33 | :redirect-link "Click here for homepage." 34 | :powered-by "Powered by" 35 | :debug-blog "Dump information for blog" 36 | :debug-post "Dump information for post" 37 | :debug-tag "Dump information for tag" 38 | :debug-property "Property" 39 | :debug-value "Value" 40 | :debug-blog-header "Blog" 41 | :debug-posts-header "Posts" 42 | :debug-static-pages-header "Static pages" 43 | :debug-snippet-header "Snippets" 44 | :debug-tags-header "Tags") 45 | ("fr" 46 | :posted-on "Posté le" 47 | :post-timestamp "%A %d %B %Y à %H:%M:%S" 48 | :related-tags "Tags associés" 49 | :home "Accueil" 50 | :tags "Tags" 51 | :archives "Archives" 52 | :rss "RSS" 53 | :about "À propos" 54 | :links "Liens" 55 | :redirect-header "Redirection" 56 | :redirect-text "Oups! Vous ne devriez pas être ici. Merci de patienter pour une redirection." 57 | :redirect-link "Cliquez ici pour l'accueil." 58 | :powered-by "Réalisé avec" 59 | :debug-blog "Contenu du blog" 60 | :debug-post "Contenu des articles" 61 | :debug-tag "Contenu des tags" 62 | :debug-property "Propriété" 63 | :debug-value "Valeur" 64 | :debug-blog-header "Blog" 65 | :debug-posts-header "Articles" 66 | :debug-static-pages-header "Pages statiques" 67 | :debug-snippet-header "Fragments" 68 | :debug-tags-header "Tags") 69 | ("de" 70 | :posted-on "Veröffentlicht am" 71 | :post-timestamp "%d.%m.%Y um %H:%M" 72 | :related-tags "Zugewiesene Schlagworte" 73 | :home "Start" 74 | :tags "Schlagworte" 75 | :archives "Archiv" 76 | :rss "RSS" 77 | :about "Über" 78 | :links "Links" 79 | :redirect-header "Umleitung" 80 | :redirect-text "O! Du solltest nicht hier sein. Bitte warte auf die Umleitung." 81 | :redirect-link "Hier klicken, um zum Start zu kommen." 82 | :powered-by "Bereitgestellt mit Hilfe von" 83 | :debug-blog "Beispielinformation für den Blog" 84 | :debug-post "Beispielinformation für einen Beitrag" 85 | :debug-tag "Beispielinformation für ein Schlagwort" 86 | :debug-property "Eigenschaft" 87 | :debug-value "Wert" 88 | :debug-blog-header "Blog" 89 | :debug-posts-header "Artikel" 90 | :debug-static-pages-header "Statische Seiten" 91 | :debug-snippet-header "Schnipsel" 92 | :debug-tags-header "Schlagworte") 93 | ("zh-CN" ;; simplified Chinese, traditional Chinese may use zh-TW or zh-HK 94 | :posted-on "发布于" 95 | :post-timestamp "%Y-%m-%d %H:%M:%S" 96 | :related-tags "相关标签" 97 | :home "首页" 98 | :tags "标签" 99 | :archives "归档" 100 | :rss "RSS" 101 | :about "关于" 102 | :links "链接" 103 | :powered-by "功能取自" 104 | :debug-blog "导出博客信息" 105 | :debug-post "导出博文信息" 106 | :debug-tag "导出标签信息" 107 | :debug-property "属性" 108 | :debug-value "值" 109 | :debug-blog-header "博客" 110 | :debug-posts-header "博文" 111 | :debug-static-pages-header "固定页面" 112 | :debug-snippet-header "网页片断" 113 | :debug-tags-header "标签" )) 114 | 115 | "Translation ALIST used by `ob:gettext'. Each item consists of 116 | language as `car' and translation items as `cdr'. 117 | 118 | The translation items are used in following templates: 119 | 120 | - :posted-on blog_post: blog_post.html 121 | - :post-timestamp: blog_post.html 122 | - :home: nav_links.html, nav_breadcrumb.html 123 | - :tags: nav_links.html, page_footer.html 124 | - :archives: nav_links.html, page_footer.html 125 | - :rss: nav_links.html 126 | - :about: page_footer.html 127 | - :links: page_footer.html 128 | - :powered-by: page_footer.html 129 | - :debug-blog: debug_blog.html 130 | - :debug-post: debug_post.html 131 | - :debug-tag: debug_tag.html 132 | - :debug-blog-header: debug.html 133 | - :debug-posts-header: debug.html 134 | - :debug-static-pages-header: debug.html 135 | - :debug-snippet-header: debug.html 136 | - :debug-tags-header: debug.html 137 | - :debug-property: debug_blog.html, debug_post.html, debug_tag.html 138 | - :debug-value: debug_blog.html, debug_post.html, debug_tag.html") 139 | 140 | ;;;###autoload 141 | (defun ob:gettext (text &optional lang) 142 | "Return part of `o-blog-i18n' that matches TEXT in LANG. 143 | 144 | If LANG is not defined, the blog \"#+LANGUAGE:\" header would be 145 | used. If not found, English (en) is used as a fall-back." 146 | (let* ((lang (or lang 147 | ;;(when (boundp 'BLOG) (ob:blog-language BLOG)) 148 | "en")) 149 | (default-text-list (cdr (assoc "en" o-blog-i18n))) 150 | (text-list (or 151 | (cdr (assoc lang o-blog-i18n)) 152 | default-text-list))) 153 | (or (plist-get text-list text) 154 | (plist-get text-list default-text-list)))) 155 | 156 | 157 | 158 | (provide 'o-blog-i18n) 159 | -------------------------------------------------------------------------------- /lisp/o-blog-obsolete.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-obsolete.el --- Obsolete functions 2 | 3 | ;; Copyright © 2013 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2013-03-30 8 | ;; Last changed: 2013-06-05 00:43:51 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | 20 | (eval-when-compile 21 | (require 'cl)) 22 | 23 | (defcustom ob:obsolete-warn nil 24 | "Display warning message about obsolete functions. Can be a 25 | performance killer.") 26 | 27 | (defun ob:make-obsolete (structure slot) 28 | "Create o-blog obsolete functions wrapper." 29 | (let* ((orig (if (listp slot) (car slot) slot)) 30 | (new (if (listp slot) (cadr slot) slot)) 31 | (func (intern (format "ob:%s-%s" structure orig)))) 32 | (fset func 33 | `(lambda (X) 34 | "Access to deprecated o-blog structure." 35 | (when ob:obsolete-warn 36 | (warn 37 | "`%s' is obsolete consider using `ob:get' instead: (ob:get '%s %s)." 38 | (quote ,func) (quote ,new) (upcase (symbol-name (quote ,structure))))) 39 | (ob:get (quote ,new) X))) 40 | (make-obsolete func 'ob:get))) 41 | 42 | 43 | (loop for s in '(file buffer publish-dir template-dir style-dir 44 | assets-dir posts-filter static-filter snippet-filter title 45 | description url language post-build-shell default-category 46 | disqus analytics filename-sanitizer posts-sorter posts-filepath 47 | posts-htmlfile) 48 | do (ob:make-obsolete 'blog s)) 49 | 50 | (loop for s in '(id title timestamp year month day category tags 51 | template filepath filename htmlfile path-to-root content 52 | (content-html html) sitemap) 53 | do (ob:make-obsolete 'post s)) 54 | 55 | (loop for s in '((name display) safe count size) 56 | do (ob:make-obsolete 'tags s)) 57 | 58 | (loop for s in '((name display) safe) 59 | do (ob:make-obsolete 'category s)) 60 | 61 | 62 | (provide 'o-blog-obsolete) 63 | 64 | ;; o-blog-obsolete.el ends here 65 | -------------------------------------------------------------------------------- /lisp/o-blog-tag.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-tag.el --- 2 | 3 | ;; Copyright © 2013 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2013-02-09 8 | ;; Last changed: 2013-03-29 20:16:56 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | 20 | (defclass ob:category () 21 | ((display :initarg :display 22 | :type string 23 | :documentation "Displayed tag name string") 24 | (safe :initarg :safe 25 | :type string 26 | :documentation "HTML safe tag form")) 27 | "") 28 | 29 | (defmethod ob:category:init ((self ob:category)) 30 | "" 31 | (let ((name (ob:get-name self))) 32 | (set-slot-value self 'display name) 33 | (set-slot-value self 'safe (ob:sanitize-string name))) 34 | self) 35 | 36 | 37 | 38 | (defclass ob:tag (ob:category) 39 | ((count :initarg :count 40 | :type integer 41 | :documentation "Tag occurance in all articles") 42 | (size :initarg :size 43 | :type float 44 | :documentation "Tag html size")) 45 | "") 46 | 47 | (defun ob:category:get (value &optional entry) 48 | "" 49 | (let ((entry (or entry 50 | (when (boundp 'POST) 51 | (ob:entry:get 'category))))) 52 | (slot-value entry value))) 53 | 54 | (provide 'o-blog-tag) 55 | 56 | ;; o-blog-tag.el ends here 57 | -------------------------------------------------------------------------------- /lisp/o-blog-utils.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog-utils.el --- Some generic function used in o-blog. 2 | 3 | ;; Copyright © 2013 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2013-01-22 8 | ;; Last changed: 2014-07-07 01:24:06 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | 19 | (defun ob:replace-in-string (string replacement-list) 20 | "Perform a mass `replace-regexp-in-string' against STRING for 21 | all \(regexp rep\) items from REPLACEMENT-LIST and return the 22 | result string." 23 | (loop for (regexp rep) in replacement-list 24 | do (setf string (replace-regexp-in-string regexp rep string))) 25 | string) 26 | 27 | (defun ob:sanitize-string (s) 28 | "Sanitize string S by: 29 | 30 | - converting all charcters ton pure ASCII 31 | - replacing non alphanumerical chars to \"-\" 32 | - down-casing all letters 33 | - trimming leading and tailing \"-\"" 34 | (loop for c across s 35 | with cd 36 | with gc 37 | with ret 38 | do (progn 39 | (setf gc (get-char-code-property c 'general-category)) 40 | (setf cd (get-char-code-property c 'decomposition))) 41 | if (or (member gc '(Lu Ll Nd)) (= ?- c)) 42 | collect (downcase 43 | (char-to-string (if cd (car cd) c))) 44 | into ret 45 | else if (member gc '(Zs)) 46 | collect "-" into ret 47 | finally return (ob:replace-in-string 48 | (mapconcat 'identity ret "") 49 | '(("--+" "-") 50 | ("^-+\\|-+$" ""))))) 51 | 52 | (defun ob:write-file (file) 53 | "Write current buffer to FILE. Ensure FILE directories are present." 54 | (mkdir (file-name-directory file) t) 55 | (let ((buffer (current-buffer))) 56 | (with-temp-file file 57 | (insert (with-current-buffer buffer (buffer-string)))))) 58 | 59 | (defun ob:eval-lisp() 60 | "Eval embeded lisp code defined by tags in html fragment 61 | when publishing a page." 62 | (save-excursion 63 | (save-restriction 64 | (save-match-data 65 | ;; needed for thing-at-point 66 | (html-mode) 67 | (beginning-of-buffer) 68 | (let ((open-tag "\\|{lisp}\\|\\[lisp\\]") 69 | (close-tag "\\|{/lisp}\\|\\[/lisp\\]") 70 | beg end sexp) 71 | (while (search-forward-regexp open-tag nil t) 72 | (setq beg (- (point) (length (match-string 0)))) 73 | (when (search-forward-regexp close-tag nil t) 74 | (setq end (point)) 75 | (backward-char (length (match-string 0))) 76 | (backward-sexp) 77 | (setq sexp (substring-no-properties (thing-at-point 'sexp))) 78 | ;; In some exporters (pandoc) " are replaced with " which 79 | ;; breaks lisp interpolation. 80 | (with-temp-buffer 81 | (insert sexp) 82 | (goto-char (point-min)) 83 | (while (search-forward """ nil t) 84 | (replace-match "\"" nil t)) 85 | (setq sexp (buffer-string))) 86 | (narrow-to-region beg end) 87 | (delete-region (point-min) (point-max)) 88 | (insert 89 | (save-match-data 90 | (condition-case err 91 | (let ((object (eval (read sexp)))) 92 | (cond 93 | ;; result is a string 94 | ((stringp object) object) 95 | ;; a list 96 | ((and (listp object) 97 | (not (eq object nil))) 98 | (let ((string (pp-to-string object))) 99 | (substring string 0 (1- (length string))))) 100 | ;; a number 101 | ((numberp object) 102 | (number-to-string object)) 103 | ;; nil 104 | ((eq object nil) "") 105 | ;; otherwise 106 | (t (pp-to-string object)))) 107 | ;; error handler 108 | (error 109 | (format "Lisp error in %s: %s" (buffer-file-name) err))))) 110 | (goto-char (point-min)) 111 | (widen)))))))) 112 | 113 | (defun ob:format-date (date &optional format locale) 114 | "Format DATE using FORMAT and LOCALE. 115 | 116 | DATE can heither be string suitable for `parse-time-string' or a 117 | list of interger using `current-time' format. 118 | 119 | FORMAT is a `format-time-string' compatible definition. If not 120 | set ISO8601 \"%Y-%m-%dT%TZ\" format would be used." 121 | (let* ((date (cond 122 | ((stringp date) 123 | (apply 'encode-time 124 | (parse-time-string date))) 125 | ((listp date) 126 | date))) 127 | (format (or format "%Y-%m-%dT%TZ")) 128 | (system-time-locale locale)) 129 | (format-time-string format date))) 130 | 131 | (defun ob:path-to-root () 132 | "Return path to site root from `PATH-TO-ROOT' or `POST' 133 | path-to-root slot." 134 | (cond 135 | ((boundp 'PATH-TO-ROOT) PATH-TO-ROOT) 136 | ((boundp 'POST) (ob:entry:get 'path-to-root POST)) 137 | (t "."))) 138 | 139 | (defun ob:get (slot &optional object) 140 | "Try to get SLOT from OBJECT. 141 | 142 | If object is `nil' try to get SLOT from: 143 | 144 | - TAG 145 | - CATEGORY 146 | - POST 147 | - BLOG" 148 | (if object 149 | (when (and 150 | (slot-exists-p object slot) 151 | (slot-boundp object slot)) 152 | (slot-value object slot)) 153 | (loop for o in '(TAG CATEGORY POST BLOG) 154 | when (and (boundp o) 155 | (slot-exists-p (eval o) slot) 156 | (slot-boundp (eval o) slot)) 157 | return (slot-value (eval o) slot)))) 158 | 159 | (defun ob:get-post-by-id (id) 160 | "Return post which id is ID" 161 | (let ((POSTS (or (when (boundp 'POSTS) POSTS) 162 | (ob:get 'articles BLOG)))) 163 | (when (>= id 0) 164 | (nth id POSTS)))) 165 | 166 | 167 | (defun ob:get-name (object) 168 | "Return OBJECT class name." 169 | (if (boundp 'object-name) 170 | (aref object object-name) 171 | (eieio-object-name-string object))) 172 | 173 | 174 | ;; FIXME: do no use ob-bck-end 175 | (defun ob:insert-template (template &optional ob-bck-end) 176 | "Return the lisp evaluated (see `ob:eval-lisp') content of 177 | TEMPLATE (relative from `ob:backend' `template' slot) as a 178 | string." 179 | (insert 180 | (with-temp-buffer 181 | (insert-file-contents 182 | (format "%s/%s" (ob:get 'template-dir ob-bck-end) template)) 183 | (ob:eval-lisp) 184 | (buffer-string)))) 185 | 186 | (defun ob:eval-template-to-file (template file) 187 | "Evaluate TEMPLATE an write it to FILE." 188 | (with-temp-buffer 189 | (ob:insert-template template) 190 | (ob:write-file file))) 191 | 192 | (defun ob:get-next-post (&optional count) 193 | "Retrieve next post" 194 | (when (and (boundp 'POST) 195 | (boundp 'POSTS)) 196 | (let* ((count (or count 1)) 197 | (current POST) 198 | (category (ob:get 'category POST)) 199 | (cat-posts (ob:get-posts 200 | (lambda (x) 201 | (equal category (ob:get 'category x))))) 202 | (current-idx (loop for i below (length cat-posts) 203 | until (equal (nth i cat-posts) current) 204 | finally return i)) 205 | (wanted-id (+ current-idx count))) 206 | (when (and (>= wanted-id 0) 207 | (< wanted-id (length cat-posts))) 208 | (nth wanted-id cat-posts))))) 209 | 210 | (defun ob:get-prev-post (&optional count) 211 | (ob:get-next-post (or count -1))) 212 | 213 | 214 | 215 | 216 | 217 | 218 | (defun ob:lesser (a b) 219 | "Emulate `<' in templates." 220 | (< a b)) 221 | 222 | (defun ob:lesser-or-equal (a b) 223 | "Emulate `<=' in templates." 224 | (<= a b)) 225 | 226 | (defun ob:greater (a b) 227 | "Emulate `>' in templates." 228 | (> a b)) 229 | 230 | (defun ob:greater-or-equal (a b) 231 | "Emulate `>=' in templates." 232 | (>= a b)) 233 | 234 | 235 | 236 | (defun ob:string-template (template) 237 | "Evaluate TEMPLATE and return a string. 238 | - strings are return as it 239 | - symbols are evaluated 240 | - lists are concatenated or evaluated depending on their first element." 241 | (cond 242 | ((stringp template) template) 243 | ((symbolp template) 244 | (if (boundp template) 245 | (if (eq nil template) 246 | "" 247 | (format "%s" (symbol-value template))) 248 | (symbol-name template))) 249 | ((listp template) 250 | (if (and (symbolp (car template)) 251 | (fboundp (car template))) 252 | (eval template) 253 | (mapconcat 'identity 254 | (loop for i in template 255 | collect (ob:string-template i)) 256 | ""))) 257 | (t (format "%s" template)))) 258 | 259 | 260 | (defun ob-do-copy (src dst &optional copyf args) 261 | "Copy SRC into DST. If `dired-do-sync' is found it would be 262 | preferred. Otherwise, `copy-directory' or `copy-files' would be 263 | used. 264 | 265 | A copy function COPYF and its arguments ARGS could be specified." 266 | (let* ((dirp (file-directory-p src)) 267 | (copyf (cond 268 | (copyf copyf) 269 | ((functionp 'dired-do-sync) 'dired-do-sync) 270 | (dirp 'copy-directory) 271 | (t 'copy-file))) 272 | (args (or args 273 | (when (eq 'copy-file copyf) '(t t t))))) 274 | (when (file-exists-p src) 275 | (message "Copying %s -> %s using %s" src dst copyf) 276 | (apply copyf src dst args)))) 277 | 278 | (defun ob:publish-style (object) 279 | "Publish OBJECT styles such as CSS, JavaScript and Fonts." 280 | (ob-do-copy 281 | (ob:get 'style-dir object) 282 | (ob:get 'publish-dir object))) 283 | 284 | 285 | 286 | 287 | (provide 'o-blog-utils) 288 | 289 | ;; o-blog-utils.el ends here 290 | -------------------------------------------------------------------------------- /lisp/o-blog.el: -------------------------------------------------------------------------------- 1 | ;;; o-blog.el --- Standalone orgmode blog exporter 2 | 3 | ;; Copyright © 2012 Sébastien Gross 4 | 5 | ;; Author: Sébastien Gross 6 | ;; Keywords: emacs, 7 | ;; Created: 2012-12-03 8 | ;; Last changed: 2014-02-14 00:55:18 9 | ;; Licence: WTFPL, grab your copy here: http://sam.zoy.org/wtfpl/ 10 | 11 | ;; This file is NOT part of GNU Emacs. 12 | 13 | ;;; Commentary: 14 | ;; 15 | 16 | 17 | ;;; Code: 18 | (eval-when-compile 19 | (require 'eieio) 20 | (require 'o-blog-utils) 21 | (require 'o-blog-tag) 22 | (require 'o-blog-entry) 23 | (require 'o-blog-i18n) 24 | (require 'o-blog-backend) 25 | (require 'o-blog-framework) 26 | (require 'o-blog-obsolete)) 27 | 28 | (defun o-blog-version() 29 | "return current o-blog version." 30 | "o-blog v2.0") 31 | 32 | 33 | (defun o-blog-guess-backend-from-file (file) 34 | "Try to guess o-blog back from FILE or current buffer." 35 | (with-current-buffer (or (get-file-buffer file) 36 | (find-file-noselect file)) 37 | ;; Assume all mode end with "-mode" 38 | (intern (substring 39 | (symbol-name major-mode) 0 -5)))) 40 | 41 | (defun o-blog-publish-example () 42 | "" 43 | (interactive) 44 | (o-blog-publish 45 | (expand-file-name 46 | (concat (file-name-directory 47 | (find-library-name "o-blog")) 48 | "../example/sample.org")))) 49 | 50 | 51 | ;;;###autoload 52 | (defun o-blog-publish(&optional file backend) 53 | "Publish FILE using o-blog BACKEND. 54 | 55 | If FILE is not provided, try to guess FILE and BACKEND from 56 | current buffer." 57 | (interactive) 58 | (let* ((file 59 | (or 60 | file 61 | (buffer-file-name) 62 | (read-file-name "O-blog file to publish: " nil nil t))) 63 | (backend (or 64 | backend 65 | (o-blog-guess-backend-from-file file))) 66 | (default-directory 67 | (let ((dir (file-name-directory file))) 68 | (if (member backend '(org)) 69 | dir 70 | (loop until (or (string= "/" dir) 71 | (file-exists-p (format "%s/o-blog.conf" dir))) 72 | do (setf dir (file-name-directory (directory-file-name dir))))) 73 | dir)) 74 | (classfct (intern (format "ob:backend:%s" backend)))) 75 | 76 | (when (member backend '(markdown)) 77 | (setf file (format "%s/o-blog.conf" default-directory))) 78 | 79 | (let ((lib (intern (format "o-blog-backend-%s" backend)))) 80 | (unless (featurep lib) 81 | (require lib))) 82 | 83 | (ob:publish (funcall classfct file)))) 84 | ;; (let* ((blog (funcall classfct file)) 85 | ;; ;; Just an alias for backward compatibility 86 | ;; ;; TODO: Remove me 87 | ;; (BLOG blog)) 88 | ;; (ob:parse-config blog) 89 | ;; (ob:parse-entries blog) 90 | ;; (loop for type in '(articles pages snippets) 91 | ;; do (loop for entry in (slot-value blog type) 92 | ;; do (ob:convert-entry blog entry))) 93 | 94 | ;; ;;blog 95 | ;; (ob:compute-tags blog) 96 | ;; (ob:publish blog) 97 | ;; blog))) 98 | 99 | (defun o-blog-publish-async-processes-sentinel (proc change) 100 | "Sentinel in charge of cleaning `org-publish-blog-async' on success." 101 | (when (eq (process-status proc) 'exit) 102 | (let ((status (process-exit-status proc)) 103 | (cmd (process-get proc :cmd)) 104 | (cmd-buf (process-get proc :cmd-buf))) 105 | (if (not (eq 0 status)) 106 | (progn 107 | (when (process-buffer proc) 108 | (set-window-buffer (selected-window) cmd-buf)) 109 | (error "o-blog ERROR: %s" cmd)) 110 | (message "o-blog OK: %s" cmd)) 111 | ;;(when cmd-buf (kill-buffer cmd-buf)) 112 | ))) 113 | 114 | ;;;###autoload 115 | (defun o-blog-publish-async (file) 116 | "Publish FILE synchronously using BACKEND." 117 | (let* ((cmd-line (append command-line-args 118 | `("--batch" 119 | "-l" ,(concat (file-name-as-directory 120 | user-emacs-directory) 121 | "init.el") 122 | ;;,@ob-async-opts 123 | "--eval" 124 | ,(format "(o-blog-publish \"%s\")" 125 | file)))) 126 | (cmd-cli (mapconcat 'shell-quote-argument cmd-line " ")) 127 | (cmd-buf (get-buffer-create (format "o-blog build %s" file))) 128 | (proc (progn 129 | (with-current-buffer cmd-buf 130 | (insert (format "Run: %s\n\n" cmd-cli))) 131 | (apply 'start-process (car cmd-line) 132 | cmd-buf (car cmd-line) (cdr cmd-line))))) 133 | ;;(set-window-buffer (selected-window) cmd-buf) 134 | (message "Run: %s" cmd-cli) 135 | (process-put proc :cmd (format "Build %s" file)) 136 | (process-put proc :cmd-buf cmd-buf) 137 | (set-process-sentinel proc 'o-blog-publish-async-processes-sentinel))) 138 | 139 | 140 | (defun ob:parse-blog-config (&optional file type) 141 | "" 142 | (interactive) 143 | 144 | (let* ((type (or (intern (format "ob:blog:%s" type)) 'ob:blog)) 145 | (file (or 146 | file 147 | (read-file-name "Blog configuration file: " nil nil t))) 148 | (blog (funcall type file)) 149 | (lines (split-string 150 | (with-temp-buffer 151 | (insert-file-contents file) 152 | (buffer-string)) 153 | "\n"))) 154 | (save-match-data 155 | (loop for line in lines 156 | when (string-match "^\\s-*\\([^#]+?\\)\\s-*=\\s-*\\(.+?\\)\\s-*$" line) 157 | do (let ((k (intern (match-string 1 line))) 158 | (v (match-string 2 line))) 159 | (when (slot-exists-p blog k) 160 | (set-slot-value blog k v))))) 161 | blog)) 162 | 163 | 164 | 165 | 166 | 167 | (provide 'o-blog) 168 | 169 | ;; o-blog.el ends here 170 | -------------------------------------------------------------------------------- /site/src/7696122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/7696122.png -------------------------------------------------------------------------------- /site/src/_elements/About.snippet.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 6 | This is a sample blog to explain how [o-blog](https://github.com/renard/o-blog) works. 7 | 8 | -------------------------------------------------------------------------------- /site/src/_elements/Copyright.snippet.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 6 | 7 | 8 | Copyright Sébastien Gross 9 | -------------------------------------------------------------------------------- /site/src/_elements/Footer.Archives.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 6 | 7 |
    8 |
    Blog
    9 | 10 |
    11 |
    12 |
    Lorem
    13 | 14 |
    15 | -------------------------------------------------------------------------------- /site/src/_elements/Navigation-footer.snippet.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 6 | - [ Home]({lisp}(ob:path-to-root){/lisp}/index.html) 7 | - [ Quickstart]({lisp}(ob:path-to-root){/lisp}/quickstart.html) 8 | - [ Bootstrap]({lisp}(ob:path-to-root){/lisp}/bootstrap-items.html) 9 | - [ Page classes]({lisp}(ob:path-to-root){/lisp}/page-classes.html) 10 | - [ Publication]({lisp}(ob:path-to-root){/lisp}/publication.html) 11 | -------------------------------------------------------------------------------- /site/src/_elements/Navigation.snippet.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 6 | - [ Home]({lisp}(ob:path-to-root){/lisp}/index.html) 7 | - [ Documentation](#) 8 | - [Quickstart]({lisp}(ob:path-to-root){/lisp}/quickstart.html) 9 | - [Bootstrap]({lisp}(ob:path-to-root){/lisp}/bootstrap-items.html) 10 | - 11 | - [Page classes]({lisp}(ob:path-to-root){/lisp}/page-classes.html) 12 | - [Publication]({lisp}(ob:path-to-root){/lisp}/publication.html) 13 | - 14 | - [Javascript]({lisp}(ob:path-to-root){/lisp}/javascript.html) 15 | - [ Blog](#) 16 | - [ Lorem](#) 17 | - [ Tags](#) 18 | - [Cloud]({lisp}(ob:path-to-root){/lisp}/tags/index.html) 19 | - [Details]({lisp}(ob:path-to-root){/lisp}/tags.html) 20 | - [ Testimonial]({lisp}(ob:path-to-root){/lisp}/testimonials.html) 21 | 22 | 23 | 27 | -------------------------------------------------------------------------------- /site/src/bjonnh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/bjonnh.png -------------------------------------------------------------------------------- /site/src/common-lisp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/common-lisp.png -------------------------------------------------------------------------------- /site/src/djcbsoftware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/djcbsoftware.png -------------------------------------------------------------------------------- /site/src/doc/bootstrap.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 9 | 10 | # Inline syntax 11 | 12 | 13 | ## Highlight 14 | 15 | 16 | 17 | For highlighting a run of text due to its relevance in another context, use 18 | the `` tag. 19 | 20 | You can use the mark tag to highlight text. 21 | 22 | 23 | 24 | You can use the mark tag to highlight text. 25 | 26 | 27 | 28 | 29 | 30 | ## Deleted 31 | 32 | 33 | 34 | For indicating blocks of text that have been deleted use the `` tag. 35 | 36 | This line of text is meant to be treated as deleted text. 37 | 38 | 39 | 40 | This line of text is meant to be treated as deleted text. 41 | 42 | 43 | 44 | 45 | 46 | ## Glyphicon 47 | 48 | 49 | See [Bootstrap official documentation](http://getbootstrap.com/components/#glyphicons) 50 | for further information about icons 51 | 52 | This is a cloud: 53 | 54 | 55 | 56 | This is a cloud: 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ## Icon 65 | 66 | 67 | 68 | See [Bootstrap official documentation](http://getbootstrap.com/components/#glyphicons) 69 | for further information about icons 70 | 71 | This is a cloud in icon: 72 | 73 | 74 | 75 | This is a cloud in icon: 76 | 77 | 78 | 79 | 80 | 81 | ## Label 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | ## Badge 113 | 114 | 115 | 42 116 | 117 | 118 | 119 | 42 120 | 121 | 122 | 123 | 124 | 125 | # Text formating 126 | 127 | ## lead 128 | 129 | 130 | 131 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 132 | 133 | 134 | 135 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 136 | 137 | 138 | 139 | 140 | 141 | ## Alignements 142 | 143 | 144 | 145 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 146 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 147 |
    Cras justo odio, dapibus ac facilisis in, egestas eget quam.
    148 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 149 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 150 | 151 | 152 | 153 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 154 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 155 |
    Cras justo odio, dapibus ac facilisis in, egestas eget quam.
    156 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 157 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 158 | 159 | 160 |
    161 | 162 | 163 | 164 | 165 | ## Jumbotron 166 | 167 | 168 | 169 | 170 |

    Hello, world!

    171 |
    172 | 173 | 174 | 175 | 176 |

    Hello, world!

    177 |
    178 | 179 | 180 |
    181 | 182 | 183 | ## Page header 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | ## Caption 197 | 198 | 199 | 200 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 201 | 202 | 203 | 204 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 205 | 206 | 207 | 208 | 209 | 210 | 211 | ## Grid 212 | 213 | ## Panel 214 | 215 | 216 | 217 | 218 | 219 | 220 | This text is inside the pannel heading 221 | 222 | 223 | This text here is inside the panel component 224 | 225 | 226 | This text is inside the pannel 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | This text is inside the pannel heading 235 | 236 | 237 | This text here is inside the panel component 238 | 239 | 240 | This text is inside the pannel 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | ## Alert 249 | 250 | 251 | 252 | Success 253 | Info 254 | Warning 255 | Danger 256 | 257 | 258 | 259 | Success 260 | Info 261 | Warning 262 | Danger 263 | 264 | 265 | 266 | 267 | 268 | ## Well 269 | 270 | 271 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 272 | 273 | 274 | 275 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 276 | 277 | 278 | 279 | 280 | # thumbnail 281 | 282 | 283 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 284 | 285 | 286 | 287 | Cras justo odio, dapibus ac facilisis in, egestas eget quam. 288 | 289 | 290 | 291 | 292 | 293 | ## Table 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 |
    col 1col 2
    col 1col 2
    col 1col 2
    col 1col 2
    303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 |
    col 1col 2
    col 1col 2
    col 1col 2
    col 1col 2
    312 | 313 | 314 |
    315 | 316 | 317 | ## Source 318 | 319 | 320 | 321 | 322 | (defmacro ob:with-source-buffer (self &rest body) 323 | "Like `with-current-buffer'" 324 | `(let ((file (ob:get-name ,self))) 325 | ;; Make sure we are in the o-blog org file 326 | (with-current-buffer (or (get-file-buffer file) 327 | (find-file-noselect file)) 328 | ,@body))) 329 | 330 | 331 | 332 | 333 | 334 | (defmacro ob:with-source-buffer (self &rest body) 335 | "Like `with-current-buffer'" 336 | `(let ((file (ob:get-name ,self))) 337 | ;; Make sure we are in the o-blog org file 338 | (with-current-buffer (or (get-file-buffer file) 339 | (find-file-noselect file)) 340 | ,@body))) 341 | 342 | 343 | 344 | 345 | 346 | -------------------------------------------------------------------------------- /site/src/doc/classes.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 8 | 9 | 10 | With `o-blog` you can create three kind of page classes regarding your 11 | needs. This article will describe the page classes and how to use them. 12 | 13 | 14 | # Snippets 15 | 16 | A *snippet* is not a page on its own. Snippets are used to fill some parts of 17 | the templates or any other page using the `ob:get-snippet` *lisp* 18 | function. A snippet takes only 2 parameters in the file headers: 19 | 20 | * `title: Snippet Name`: The insternal name of the snippet. 21 | * `type: snippet`: this line defines the file as a snippet. 22 | 23 | After the headers, you can put anything you want. The snippet content is 24 | processed during the publication and inserted into the pages that require 25 | that snippet. 26 | 27 | To include a snippet into a page: 28 | 29 | 30 | 31 | 32 | 33 | (ob:get-snippet "About") 34 | 35 | 36 | 37 | 38 | 39 | (ob:get-snippet "About" 'html) 40 | 41 | 42 | 43 | 44 | # Pages 45 | 46 | A *page* is for a static HTML page output. This class is useful for single 47 | pages such as a site homepage. A *page* takes 3 parameters in file 48 | headers: 49 | 50 | * `title: Page classes`: The page title 51 | * `type: page`: this line defines the files as a single page. 52 | * `template: blog_static_no_title.html`: This line defines which template to 53 | use for the publication. 54 | 55 | After the header, you can write your page just like you want. 56 | 57 | # Articles 58 | 59 | An *article* is the main page class you need for a blog. An article (or blog 60 | post) takes several parameters in file headers: 61 | 62 | * `title: Page classes`: The page title 63 | * `type: article`: this line defines the files as a blog post. 64 | * `category: blog`: this defines in which category this post should go. 65 | * `tags: comma separated, tags, list`: a list of tags related to the post. 66 | 67 | The *article* page will be stored using the scheme: 68 | `category/year/month/day-title.html`. Within each directory an `index.html` 69 | is also created for easy navigation. 70 | 71 | When publishing articles, both *tags* and *archives* pages are also generated. 72 | 73 | -------------------------------------------------------------------------------- /site/src/doc/functions-tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/doc/functions-tn.png -------------------------------------------------------------------------------- /site/src/doc/functions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/doc/functions.png -------------------------------------------------------------------------------- /site/src/doc/javascript.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 8 | 9 | 10 | `o-blog` uses some javascript to handle some parts of the page, such as the 11 | menu builder or the tag-cloud generator. This article explains you how to 12 | use them. 13 | 14 | 15 | # Tag cloud 16 | 17 | A tag cloud is generated for all articles and pages tags. The tag size 18 | depends on its frequency. During the export process, a *JSON* file `tags.js` 19 | is generated in the publishing directory. You can display it in any part of 20 | an HTML page using an HTML tag of class `ob-tagcloud`. 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 36 | 37 | 38 | 39 | 40 | The JavaScript widget takes 2 arguments in `data-*` attributes: 41 | 42 | * `source` is the path to the JSON structure. 43 | * `path-to-root` is the path to the site root directory. 44 | 45 | # Articles 46 | 47 | Same as the tag cloud, an article *JSON* file is generated during the export 48 | process in `articles.js` into the publishing directory. You can display 49 | articles list anywhere in the HTML page using an HTML tag of class 50 | `ob-articles`. 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 |
    72 | 73 |
    74 | 75 | 76 | 77 | 78 | 85 | 86 | 87 |
    88 | 89 | The JavaScript widget takes 2 arguments in `data-*` attributes: 90 | 91 | * `source` is the path to the JSON structure. 92 | * `path-to-root` is the path to the site root directory. 93 | * `category` is the article category to handle. 94 | * `excerpt` (optional) if true add article excerpt, insert only titles otherwise. 95 | * `limit` (optional) is the article limit count. 96 | -------------------------------------------------------------------------------- /site/src/doc/publication.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 8 | 9 | 10 | 11 | This article deals about `o-blog` publication order. This will explain how 12 | things works under the hood. 13 | 14 | 15 | # Functions 16 | 17 | 18 | 19 | [![publication](functions-tn.png "Class")](functions.png) 20 | 21 | 22 | Functions callout herarchy during publication. 23 | 24 | 25 | 26 | 27 | # Templates 28 | 29 | 30 | 31 | [![publication](templates-tn.png "Class")](templates.png) 32 | 33 | 34 | Templates callout herarchy during publication. 35 | 36 | 37 | 38 | The templates are fully compatibles with `o-blog 1` templates. 39 | -------------------------------------------------------------------------------- /site/src/doc/quickstart.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 8 | 9 | 10 | 11 | Starting a new blog from scratch is pretty easy. This article show you how 12 | to create a brand new site using o-blog and *pandoc*. Follow the 13 | step-by-step guide. If you want you can still use *org-mode* as in version 1. 14 | 15 | 16 | # Required software 17 | 18 | You need: 19 | 20 | * [Emacs](http://www.gnu.org/software/emacs/) (version 24 is highly recommended) 21 | * [o-blog](https://github.com/renard/o-blog) Version 2 22 | * [pandoc](http://johnmacfarlane.net/pandoc/) (at least version 1.12.4.2) 23 | 24 | The `o-blog` installation is pretty straightforward if you are using 25 | [el-get](https://github.com/dimitri/el-get). Meanwhile be sure you are using 26 | version 2. You can go to the `o-blog` directory (M-x el-get-cd o-blog 27 | RET) and switch to branch `o-blog-v2` (M-! git checkout o-blog-v2 28 | RET). 29 | 30 | You you install `o-blog` manualy from *git* you need run and put in your 31 | this snippet to your `~/.emacs.d/init.el` file: 32 | 33 | 34 | 35 | 36 | 37 | cd ~/.emacs.d 38 | git clone https://github.com/renard/o-blog.git 39 | git checkout o-blog-v2 40 | 41 | 42 | 43 | 44 | 45 | 46 | (add-to-list 'load-path "~/.emacs.d/o-blog/lisp") 47 | (require 'o-blog) 48 | 49 | 50 | 51 | 52 | 53 | 54 | # Starting a blog 55 | 56 | Now you need to choose a directory where you site source will reside. This 57 | an be anywhere on your computer. Let's use `~/Documents/sites/example.com` 58 | as a root directory. In that file you first need to create a configuration 59 | file named `o-blog.conf` which contains: 60 | 61 | 62 | publish-dir = out 63 | template-dir = ~/.emacs.d/o-blog/templates/ 64 | style-dir = ~/.emacs.d/o-blog/templates/style/ 65 | 66 | 67 | Where: 68 | 69 | * `publish-dir` is the path where the site would be published to. 70 | * `template-dir` is the path where the template are located. We will use 71 | `o-blog` defaults. 72 | * `style-dir` is the path where the styles (*CSS* and *JS*) are located. We 73 | will use `o-blog` defaults as well. 74 | 75 | 76 | ## Adding snippets 77 | 78 | The default templates require some snippets to be defined. Those snippets are: 79 | 80 | * `About`: as defined in the `page_footer.html` file. 81 | * `Copyright`: as defined in the `page_footer.html` file. 82 | * `Navigation Footer`: as defined in the `page_footer.html` file. 83 | * `Navigation`: as defined in the `page_header.html` file. 84 | 85 | Snippets are a bunch of text that may change from one site to an other. This 86 | allow you to use the same templates for many sites by just changing some files. 87 | 88 | You can put a snippet anywhere in the site source tree as long as you define 89 | them using `type: snippet` in their file header. A good practice is to put 90 | them all in a single directory such as `_elements` at the site root. 91 | 92 | For exampe the `About` you need to create the file 93 | `_elements/About.snippet.txt` which contains following lines: 94 | 95 | 96 | % -*- markdown -*- 97 | 101 | This is a sample blog to explain how [o-blog](https://github.com/renard/o-blog) works. 102 | 103 | 104 | The most important lines are: 105 | 106 | * `title: About`: which defines the snippet name. 107 | * `type: snippet`: which defines the current file as a snippet. 108 | 109 | 110 | Please note that file headers are enclosed within `` 111 | comments. Note the slash `/` at the end of the comment delimeter. 112 | 113 | 114 | 115 | 116 | `Copyright` snippet 117 | 118 | 119 | 120 | % -*- markdown -*- 121 | 125 | Copyright Sébastien Gross 126 | 127 | 128 | 129 | `_elements/Copyright.snippet.txt` 130 | 131 | 132 | 133 | 134 | 135 | `Navigation Footer` snippet 136 | 137 | 138 | 139 | % -*- markdown -*- 140 | 144 | - [ {lisp }(ob:gettext :home){/lisp}]({lisp }(ob:path-to-root){/lisp}/index.html) 145 | 146 | 147 | 148 | `_elements/Navigation-footer.snippet.txt` 149 | 150 | 151 | 152 | 153 | 154 | `Navigation` snippet 155 | 156 | 157 | 158 | - [ Home]({lisp }(ob:path-to-root){/lisp}/index.html) 159 | - [ Documentation](#) 160 | - [Quickstart]({lisp }(ob:path-to-root){/lisp}/quickstart.html) 161 | - [Bootstrap]({lisp }(ob:path-to-root){/lisp}/bootstrap-items.html) 162 | 163 | 164 | 165 | `_elements/Navigation.snippet.txt` 166 | 167 | 168 | 169 | ## Creating one page 170 | 171 | Now you can edit the `index.txt` which contains: 172 | 173 | 174 | 175 | 176 | % -*- markdown -*- 177 | 182 | 183 | 184 |

    Hello, world!

    185 |
    186 | 187 | 188 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 189 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 190 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 191 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 192 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat 193 | non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 194 | 195 | 196 | 197 |
    198 | 199 | `index.txt` 200 | 201 |
    202 | 203 | You can publish using M-x o-blog-publish RET. You will get 204 | something similar to: 205 | 206 | 207 | 208 | 209 | 210 |

    Hello, world!

    211 |
    212 | 213 | 214 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 215 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 216 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 217 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 218 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat 219 | non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 220 | 221 |
    222 |
    223 | -------------------------------------------------------------------------------- /site/src/doc/templates-tn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/doc/templates-tn.png -------------------------------------------------------------------------------- /site/src/doc/templates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/doc/templates.png -------------------------------------------------------------------------------- /site/src/ergoemacs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/ergoemacs.png -------------------------------------------------------------------------------- /site/src/from-the-cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/from-the-cloud.png -------------------------------------------------------------------------------- /site/src/hdurer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/hdurer.png -------------------------------------------------------------------------------- /site/src/hillenius.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/hillenius.png -------------------------------------------------------------------------------- /site/src/index.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 8 | 9 | 10 | Fork me on GitHub 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | * Emacs 26 | * pandoc or org-mode 27 | * el-get or git 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | If you are using el-get it would be very easy to install. All you need to do is to add o-blog to el-get-sources and type: 36 | 37 | `M-x el-get-install o-blog` 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Only one single command: 46 | 47 | `M-x o-blog-publish` 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Please note that this site is for 59 | [o-blog 2](https://github.com/renard/o-blog). Old 60 | [o-blog](../o-blog/index.html) versions is still available under the 61 | [o-blog-v1](https://github.com/renard/o-blog/tree/o-blog-v1) 62 | branch. [o-blog 2](https://github.com/renard/o-blog/tree/o-blog-v1) has been 63 | designed to be fully compatible with [o-blog](../o-blog/index.html) but if 64 | you have compatibility problems please 65 | [open an issue on Github](https://github.com/renard/o-blog/issues). 66 | 67 | 68 | -------------------------------------------------------------------------------- /site/src/kankanan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/kankanan.png -------------------------------------------------------------------------------- /site/src/lorem/2-columns.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 8 | 9 | 10 | The full version of *Lorem ipsum* has been taken from [Wikipedia template](http://en.wikipedia.org/wiki/Template:Lorem_ipsum). 11 | 12 | 13 | 14 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 15 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 16 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 17 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 18 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat 19 | non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 20 | 21 | Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, 22 | turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis 23 | sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac 24 | tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam 25 | tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id 26 | tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec 27 | fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, 28 | commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum 29 | aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget 30 | erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam 31 | consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, 32 | nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac 33 | habitasse platea dictumst. 34 | 35 | Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit 36 | mauris, sed placerat ipsum urna sed risus. In convallis tellus a 37 | mauris. Curabitur non elit ut libero tristique sodales. Mauris a 38 | lacus. Donec mattis semper leo. In hac habitasse platea dictumst. Vivamus 39 | facilisis diam at odio. Mauris dictum, nisi eget consequat elementum, lacus 40 | ligula molestie metus, non feugiat orci magna ac sem. Donec turpis. Donec 41 | vitae metus. Morbi tristique neque eu mauris. Quisque gravida ipsum non 42 | sapien. Proin turpis lacus, scelerisque vitae, elementum at, lobortis ac, 43 | quam. Aliquam dictum eleifend risus. In hac habitasse platea dictumst. Etiam 44 | sit amet diam. Suspendisse odio. Suspendisse nunc. In semper bibendum 45 | libero. 46 | 47 | Proin nonummy, lacus eget pulvinar lacinia, pede felis dignissim leo, vitae 48 | tristique magna lacus sit amet eros. Nullam ornare. Praesent odio ligula, 49 | dapibus sed, tincidunt eget, dictum ac, nibh. Nam quis lacus. Nunc eleifend 50 | molestie velit. Morbi lobortis quam eu velit. Donec euismod vestibulum 51 | massa. Donec non lectus. Aliquam commodo lacus sit amet nulla. Cras 52 | dignissim elit et augue. Nullam non diam. Pellentesque habitant morbi 53 | tristique senectus et netus et malesuada fames ac turpis egestas. In hac 54 | habitasse platea dictumst. Aenean vestibulum. Sed lobortis elit quis 55 | lectus. Nunc sed lacus at augue bibendum dapibus. 56 | 57 | Aliquam vehicula sem ut pede. Cras purus lectus, egestas eu, vehicula at, 58 | imperdiet sed, nibh. Morbi consectetuer luctus felis. Donec vitae 59 | nisi. Aliquam tincidunt feugiat elit. Duis sed elit ut turpis ullamcorper 60 | feugiat. Praesent pretium, mauris sed fermentum hendrerit, nulla lorem 61 | iaculis magna, pulvinar scelerisque urna tellus a justo. Suspendisse 62 | pulvinar massa in metus. Duis quis quam. Proin justo. Curabitur ac 63 | sapien. Nam erat. Praesent ut quam. 64 | 65 | Vivamus commodo, augue et laoreet euismod, sem sapien tempor dolor, ac 66 | egestas sem ligula quis lacus. Donec vestibulum tortor ac lacus. Sed posuere 67 | vestibulum nisl. Curabitur eleifend fermentum justo. Nullam 68 | imperdiet. Integer sit amet mauris imperdiet risus sollicitudin rutrum. Ut 69 | vitae turpis. Nulla facilisi. Quisque tortor velit, scelerisque et, 70 | facilisis vel, tempor sed, urna. Vivamus nulla elit, vestibulum eget, semper 71 | et, scelerisque eget, lacus. Pellentesque viverra purus. Quisque elit. Donec 72 | ut dolor. 73 | 74 | Duis volutpat elit et erat. In at nulla at nisl condimentum aliquet. Quisque 75 | elementum pharetra lacus. Nunc gravida arcu eget nunc. Nulla iaculis egestas 76 | magna. Aliquam erat volutpat. Sed pellentesque orci. Etiam lacus lorem, 77 | iaculis sit amet, pharetra quis, imperdiet sit amet, lectus. Integer quis 78 | elit ac mi aliquam pretium. Nullam mauris orci, porttitor eget, sollicitudin 79 | non, vulputate id, risus. Donec varius enim nec sem. Nam aliquam lacinia 80 | enim. Quisque eget lorem eu purus dignissim ultricies. Fusce porttitor 81 | hendrerit ante. Mauris urna diam, cursus id, mattis eget, tempus sit amet, 82 | risus. Curabitur eu felis. Sed eu mi. Nullam lectus mauris, luctus a, mattis 83 | ac, tempus non, leo. Cras mi nulla, rhoncus id, laoreet ut, ultricies id, 84 | odio. 85 | 86 | Donec imperdiet. Vestibulum auctor tortor at orci. Integer semper, nisi eget 87 | suscipit eleifend, erat nisl hendrerit justo, eget vestibulum lorem justo ac 88 | leo. Integer sem velit, pharetra in, fringilla eu, fermentum id, 89 | felis. Vestibulum sed felis. In elit. Praesent et pede vel ante dapibus 90 | condimentum. Donec magna. Quisque id risus. Mauris vulputate pellentesque 91 | leo. Duis vulputate, ligula at venenatis tincidunt, orci nunc interdum leo, 92 | ac egestas elit sem ut lacus. Etiam non diam quis arcu egestas 93 | commodo. Curabitur nec massa ac massa gravida condimentum. Aenean id 94 | libero. Pellentesque vitae tellus. Fusce lectus est, accumsan ac, bibendum 95 | sed, porta eget, augue. Etiam faucibus. Quisque tempus purus eu ante. 96 | 97 | Vestibulum sapien nisl, ornare auctor, consectetuer quis, posuere tristique, 98 | odio. Fusce ultrices ullamcorper odio. Ut augue nulla, interdum at, 99 | adipiscing non, tristique eget, neque. Pellentesque habitant morbi tristique 100 | senectus et netus et malesuada fames ac turpis egestas. Ut pede est, 101 | condimentum id, scelerisque ac, malesuada non, quam. Proin eu ligula ac 102 | sapien suscipit blandit. Suspendisse euismod. Ut accumsan, neque id gravida 103 | luctus, arcu pede sodales felis, vel blandit massa arcu eget ligula. Aenean 104 | sed turpis. Pellentesque habitant morbi tristique senectus et netus et 105 | malesuada fames ac turpis egestas. Donec sem eros, ornare ut, commodo eu, 106 | tempor nec, risus. Donec laoreet dapibus ligula. Praesent orci leo, bibendum 107 | nec, ornare et, nonummy in, elit. Donec interdum feugiat leo. Vestibulum 108 | ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; 109 | Pellentesque feugiat ullamcorper ipsum. Donec convallis tincidunt urna. 110 | 111 | Suspendisse et orci et arcu porttitor pellentesque. Sed lacus nunc, 112 | fermentum vel, vehicula in, imperdiet eget, urna. Nam consectetuer euismod 113 | nunc. Nulla dignissim posuere nulla. Integer iaculis lacinia massa. Nullam 114 | sapien augue, condimentum vel, venenatis id, rhoncus pellentesque, 115 | sapien. Donec sed ipsum ultrices turpis consectetuer imperdiet. Duis et 116 | ipsum ac nisl laoreet commodo. Mauris eu est. Suspendisse id turpis quis 117 | orci euismod consequat. Donec tellus mi, luctus sit amet, ultrices a, 118 | convallis eu, lorem. Proin faucibus convallis elit. Maecenas rhoncus arcu at 119 | arcu. Proin libero. Proin adipiscing. In quis lorem vitae elit consectetuer 120 | pretium. Nullam ligula urna, adipiscing nec, iaculis ut, elementum non, 121 | turpis. Fusce pulvinar. 122 | 123 | 124 | -------------------------------------------------------------------------------- /site/src/lorem/full.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 8 | 9 | 10 | The full version of *Lorem ipsum* has been taken from [Wikipedia template](http://en.wikipedia.org/wiki/Template:Lorem_ipsum). 11 | 12 | 13 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 14 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 15 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 16 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 17 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat 18 | non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 19 | 20 | Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, 21 | turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis 22 | sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac 23 | tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam 24 | tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id 25 | tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec 26 | fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, 27 | commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum 28 | aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget 29 | erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam 30 | consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, 31 | nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac 32 | habitasse platea dictumst. 33 | 34 | Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit 35 | mauris, sed placerat ipsum urna sed risus. In convallis tellus a 36 | mauris. Curabitur non elit ut libero tristique sodales. Mauris a 37 | lacus. Donec mattis semper leo. In hac habitasse platea dictumst. Vivamus 38 | facilisis diam at odio. Mauris dictum, nisi eget consequat elementum, lacus 39 | ligula molestie metus, non feugiat orci magna ac sem. Donec turpis. Donec 40 | vitae metus. Morbi tristique neque eu mauris. Quisque gravida ipsum non 41 | sapien. Proin turpis lacus, scelerisque vitae, elementum at, lobortis ac, 42 | quam. Aliquam dictum eleifend risus. In hac habitasse platea dictumst. Etiam 43 | sit amet diam. Suspendisse odio. Suspendisse nunc. In semper bibendum 44 | libero. 45 | 46 | Proin nonummy, lacus eget pulvinar lacinia, pede felis dignissim leo, vitae 47 | tristique magna lacus sit amet eros. Nullam ornare. Praesent odio ligula, 48 | dapibus sed, tincidunt eget, dictum ac, nibh. Nam quis lacus. Nunc eleifend 49 | molestie velit. Morbi lobortis quam eu velit. Donec euismod vestibulum 50 | massa. Donec non lectus. Aliquam commodo lacus sit amet nulla. Cras 51 | dignissim elit et augue. Nullam non diam. Pellentesque habitant morbi 52 | tristique senectus et netus et malesuada fames ac turpis egestas. In hac 53 | habitasse platea dictumst. Aenean vestibulum. Sed lobortis elit quis 54 | lectus. Nunc sed lacus at augue bibendum dapibus. 55 | 56 | Aliquam vehicula sem ut pede. Cras purus lectus, egestas eu, vehicula at, 57 | imperdiet sed, nibh. Morbi consectetuer luctus felis. Donec vitae 58 | nisi. Aliquam tincidunt feugiat elit. Duis sed elit ut turpis ullamcorper 59 | feugiat. Praesent pretium, mauris sed fermentum hendrerit, nulla lorem 60 | iaculis magna, pulvinar scelerisque urna tellus a justo. Suspendisse 61 | pulvinar massa in metus. Duis quis quam. Proin justo. Curabitur ac 62 | sapien. Nam erat. Praesent ut quam. 63 | 64 | Vivamus commodo, augue et laoreet euismod, sem sapien tempor dolor, ac 65 | egestas sem ligula quis lacus. Donec vestibulum tortor ac lacus. Sed posuere 66 | vestibulum nisl. Curabitur eleifend fermentum justo. Nullam 67 | imperdiet. Integer sit amet mauris imperdiet risus sollicitudin rutrum. Ut 68 | vitae turpis. Nulla facilisi. Quisque tortor velit, scelerisque et, 69 | facilisis vel, tempor sed, urna. Vivamus nulla elit, vestibulum eget, semper 70 | et, scelerisque eget, lacus. Pellentesque viverra purus. Quisque elit. Donec 71 | ut dolor. 72 | 73 | Duis volutpat elit et erat. In at nulla at nisl condimentum aliquet. Quisque 74 | elementum pharetra lacus. Nunc gravida arcu eget nunc. Nulla iaculis egestas 75 | magna. Aliquam erat volutpat. Sed pellentesque orci. Etiam lacus lorem, 76 | iaculis sit amet, pharetra quis, imperdiet sit amet, lectus. Integer quis 77 | elit ac mi aliquam pretium. Nullam mauris orci, porttitor eget, sollicitudin 78 | non, vulputate id, risus. Donec varius enim nec sem. Nam aliquam lacinia 79 | enim. Quisque eget lorem eu purus dignissim ultricies. Fusce porttitor 80 | hendrerit ante. Mauris urna diam, cursus id, mattis eget, tempus sit amet, 81 | risus. Curabitur eu felis. Sed eu mi. Nullam lectus mauris, luctus a, mattis 82 | ac, tempus non, leo. Cras mi nulla, rhoncus id, laoreet ut, ultricies id, 83 | odio. 84 | 85 | Donec imperdiet. Vestibulum auctor tortor at orci. Integer semper, nisi eget 86 | suscipit eleifend, erat nisl hendrerit justo, eget vestibulum lorem justo ac 87 | leo. Integer sem velit, pharetra in, fringilla eu, fermentum id, 88 | felis. Vestibulum sed felis. In elit. Praesent et pede vel ante dapibus 89 | condimentum. Donec magna. Quisque id risus. Mauris vulputate pellentesque 90 | leo. Duis vulputate, ligula at venenatis tincidunt, orci nunc interdum leo, 91 | ac egestas elit sem ut lacus. Etiam non diam quis arcu egestas 92 | commodo. Curabitur nec massa ac massa gravida condimentum. Aenean id 93 | libero. Pellentesque vitae tellus. Fusce lectus est, accumsan ac, bibendum 94 | sed, porta eget, augue. Etiam faucibus. Quisque tempus purus eu ante. 95 | 96 | Vestibulum sapien nisl, ornare auctor, consectetuer quis, posuere tristique, 97 | odio. Fusce ultrices ullamcorper odio. Ut augue nulla, interdum at, 98 | adipiscing non, tristique eget, neque. Pellentesque habitant morbi tristique 99 | senectus et netus et malesuada fames ac turpis egestas. Ut pede est, 100 | condimentum id, scelerisque ac, malesuada non, quam. Proin eu ligula ac 101 | sapien suscipit blandit. Suspendisse euismod. Ut accumsan, neque id gravida 102 | luctus, arcu pede sodales felis, vel blandit massa arcu eget ligula. Aenean 103 | sed turpis. Pellentesque habitant morbi tristique senectus et netus et 104 | malesuada fames ac turpis egestas. Donec sem eros, ornare ut, commodo eu, 105 | tempor nec, risus. Donec laoreet dapibus ligula. Praesent orci leo, bibendum 106 | nec, ornare et, nonummy in, elit. Donec interdum feugiat leo. Vestibulum 107 | ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; 108 | Pellentesque feugiat ullamcorper ipsum. Donec convallis tincidunt urna. 109 | 110 | Suspendisse et orci et arcu porttitor pellentesque. Sed lacus nunc, 111 | fermentum vel, vehicula in, imperdiet eget, urna. Nam consectetuer euismod 112 | nunc. Nulla dignissim posuere nulla. Integer iaculis lacinia massa. Nullam 113 | sapien augue, condimentum vel, venenatis id, rhoncus pellentesque, 114 | sapien. Donec sed ipsum ultrices turpis consectetuer imperdiet. Duis et 115 | ipsum ac nisl laoreet commodo. Mauris eu est. Suspendisse id turpis quis 116 | orci euismod consequat. Donec tellus mi, luctus sit amet, ultrices a, 117 | convallis eu, lorem. Proin faucibus convallis elit. Maecenas rhoncus arcu at 118 | arcu. Proin libero. Proin adipiscing. In quis lorem vitae elit consectetuer 119 | pretium. Nullam ligula urna, adipiscing nec, iaculis ut, elementum non, 120 | turpis. Fusce pulvinar. 121 | -------------------------------------------------------------------------------- /site/src/lorem/multi-columns.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 8 | 9 | 10 | The full version of *Lorem ipsum* has been taken from [Wikipedia template](http://en.wikipedia.org/wiki/Template:Lorem_ipsum). 11 | 12 | 13 | 14 | 15 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 16 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 17 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 18 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 19 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat 20 | non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 21 | 22 | Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, 23 | turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis 24 | sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac 25 | tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam 26 | tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id 27 | tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec 28 | fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, 29 | commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum 30 | aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget 31 | erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam 32 | consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, 33 | nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac 34 | habitasse platea dictumst. 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Fusce convallis, mauris imperdiet gravida bibendum, nisl turpis suscipit 43 | mauris, sed placerat ipsum urna sed risus. In convallis tellus a 44 | mauris. Curabitur non elit ut libero tristique sodales. Mauris a 45 | lacus. Donec mattis semper leo. In hac habitasse platea dictumst. Vivamus 46 | facilisis diam at odio. Mauris dictum, nisi eget consequat elementum, lacus 47 | ligula molestie metus, non feugiat orci magna ac sem. Donec turpis. Donec 48 | vitae metus. Morbi tristique neque eu mauris. Quisque gravida ipsum non 49 | sapien. Proin turpis lacus, scelerisque vitae, elementum at, lobortis ac, 50 | quam. Aliquam dictum eleifend risus. In hac habitasse platea dictumst. Etiam 51 | sit amet diam. Suspendisse odio. Suspendisse nunc. In semper bibendum 52 | libero. 53 | 54 | Proin nonummy, lacus eget pulvinar lacinia, pede felis dignissim leo, vitae 55 | tristique magna lacus sit amet eros. Nullam ornare. Praesent odio ligula, 56 | dapibus sed, tincidunt eget, dictum ac, nibh. Nam quis lacus. Nunc eleifend 57 | molestie velit. Morbi lobortis quam eu velit. Donec euismod vestibulum 58 | massa. Donec non lectus. Aliquam commodo lacus sit amet nulla. Cras 59 | dignissim elit et augue. Nullam non diam. Pellentesque habitant morbi 60 | tristique senectus et netus et malesuada fames ac turpis egestas. In hac 61 | habitasse platea dictumst. Aenean vestibulum. Sed lobortis elit quis 62 | lectus. Nunc sed lacus at augue bibendum dapibus. 63 | 64 | 65 | 66 | 67 | 68 | Aliquam vehicula sem ut pede. Cras purus lectus, egestas eu, vehicula at, 69 | imperdiet sed, nibh. Morbi consectetuer luctus felis. Donec vitae 70 | nisi. Aliquam tincidunt feugiat elit. Duis sed elit ut turpis ullamcorper 71 | feugiat. Praesent pretium, mauris sed fermentum hendrerit, nulla lorem 72 | iaculis magna, pulvinar scelerisque urna tellus a justo. Suspendisse 73 | pulvinar massa in metus. Duis quis quam. Proin justo. Curabitur ac 74 | sapien. Nam erat. Praesent ut quam. 75 | 76 | Vivamus commodo, augue et laoreet euismod, sem sapien tempor dolor, ac 77 | egestas sem ligula quis lacus. Donec vestibulum tortor ac lacus. Sed posuere 78 | vestibulum nisl. Curabitur eleifend fermentum justo. Nullam 79 | imperdiet. Integer sit amet mauris imperdiet risus sollicitudin rutrum. Ut 80 | vitae turpis. Nulla facilisi. Quisque tortor velit, scelerisque et, 81 | facilisis vel, tempor sed, urna. Vivamus nulla elit, vestibulum eget, semper 82 | et, scelerisque eget, lacus. Pellentesque viverra purus. Quisque elit. Donec 83 | ut dolor. 84 | 85 | 86 | 87 | 88 | 89 | Duis volutpat elit et erat. In at nulla at nisl condimentum aliquet. Quisque 90 | elementum pharetra lacus. Nunc gravida arcu eget nunc. Nulla iaculis egestas 91 | magna. Aliquam erat volutpat. Sed pellentesque orci. Etiam lacus lorem, 92 | iaculis sit amet, pharetra quis, imperdiet sit amet, lectus. Integer quis 93 | elit ac mi aliquam pretium. Nullam mauris orci, porttitor eget, sollicitudin 94 | non, vulputate id, risus. Donec varius enim nec sem. Nam aliquam lacinia 95 | enim. Quisque eget lorem eu purus dignissim ultricies. Fusce porttitor 96 | hendrerit ante. Mauris urna diam, cursus id, mattis eget, tempus sit amet, 97 | risus. Curabitur eu felis. Sed eu mi. Nullam lectus mauris, luctus a, mattis 98 | ac, tempus non, leo. Cras mi nulla, rhoncus id, laoreet ut, ultricies id, 99 | odio. 100 | 101 | Donec imperdiet. Vestibulum auctor tortor at orci. Integer semper, nisi eget 102 | suscipit eleifend, erat nisl hendrerit justo, eget vestibulum lorem justo ac 103 | leo. Integer sem velit, pharetra in, fringilla eu, fermentum id, 104 | felis. Vestibulum sed felis. In elit. Praesent et pede vel ante dapibus 105 | condimentum. Donec magna. Quisque id risus. Mauris vulputate pellentesque 106 | leo. Duis vulputate, ligula at venenatis tincidunt, orci nunc interdum leo, 107 | ac egestas elit sem ut lacus. Etiam non diam quis arcu egestas 108 | commodo. Curabitur nec massa ac massa gravida condimentum. Aenean id 109 | libero. Pellentesque vitae tellus. Fusce lectus est, accumsan ac, bibendum 110 | sed, porta eget, augue. Etiam faucibus. Quisque tempus purus eu ante. 111 | 112 | Vestibulum sapien nisl, ornare auctor, consectetuer quis, posuere tristique, 113 | odio. Fusce ultrices ullamcorper odio. Ut augue nulla, interdum at, 114 | adipiscing non, tristique eget, neque. Pellentesque habitant morbi tristique 115 | senectus et netus et malesuada fames ac turpis egestas. Ut pede est, 116 | condimentum id, scelerisque ac, malesuada non, quam. Proin eu ligula ac 117 | sapien suscipit blandit. Suspendisse euismod. Ut accumsan, neque id gravida 118 | luctus, arcu pede sodales felis, vel blandit massa arcu eget ligula. Aenean 119 | sed turpis. Pellentesque habitant morbi tristique senectus et netus et 120 | malesuada fames ac turpis egestas. Donec sem eros, ornare ut, commodo eu, 121 | tempor nec, risus. Donec laoreet dapibus ligula. Praesent orci leo, bibendum 122 | nec, ornare et, nonummy in, elit. Donec interdum feugiat leo. Vestibulum 123 | ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; 124 | Pellentesque feugiat ullamcorper ipsum. Donec convallis tincidunt urna. 125 | 126 | Suspendisse et orci et arcu porttitor pellentesque. Sed lacus nunc, 127 | fermentum vel, vehicula in, imperdiet eget, urna. Nam consectetuer euismod 128 | nunc. Nulla dignissim posuere nulla. Integer iaculis lacinia massa. Nullam 129 | sapien augue, condimentum vel, venenatis id, rhoncus pellentesque, 130 | sapien. Donec sed ipsum ultrices turpis consectetuer imperdiet. Duis et 131 | ipsum ac nisl laoreet commodo. Mauris eu est. Suspendisse id turpis quis 132 | orci euismod consequat. Donec tellus mi, luctus sit amet, ultrices a, 133 | convallis eu, lorem. Proin faucibus convallis elit. Maecenas rhoncus arcu at 134 | arcu. Proin libero. Proin adipiscing. In quis lorem vitae elit consectetuer 135 | pretium. Nullam ligula urna, adipiscing nec, iaculis ut, elementum non, 136 | turpis. Fusce pulvinar. 137 | 138 | 139 | -------------------------------------------------------------------------------- /site/src/o-blog.conf: -------------------------------------------------------------------------------- 1 | title = o-blog 2 | description = Standalone site generator for Emacs. 3 | publish-dir = ../out 4 | template-dir = ../../templates/ 5 | style-dir = ../../templates/style/ 6 | url=http://renard.github.io/o-blog -------------------------------------------------------------------------------- /site/src/popineau.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/popineau.png -------------------------------------------------------------------------------- /site/src/testimonials.txt: -------------------------------------------------------------------------------- 1 | % -*- markdown -*- 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | [![thumbnail](7696122.png "")](http://7696122.github.io) 15 | 16 |
    17 | http://7696122.github.io 18 |
    19 | 20 |
    21 | 22 | 23 | 24 | [![thumbnail](bjonnh.png "")](http://bjonnh.net/blog/) 25 | 26 |
    27 | http://bjonnh.net/blog/ 28 |
    29 | 30 |
    31 | 32 | 33 | 34 | [![thumbnail](common-lisp.png "")](http://common-lisp.net) 35 | 36 |
    37 | http://common-lisp.net 38 |
    39 | 40 |
    41 | 42 | 43 |
    44 | 45 | 46 | 47 | 48 | [![thumbnail](djcbsoftware.png "")](http://www.djcbsoftware.nl) 49 | 50 |
    51 | http://www.djcbsoftware.nl 52 |
    53 | 54 |
    55 | 56 | 57 | 58 | [![thumbnail](ergoemacs.png "")](http://ergoemacs.github.io) 59 | 60 |
    61 | http://ergoemacs.github.io 62 |
    63 | 64 |
    65 | 66 | 67 | 68 | [![thumbnail](from-the-cloud.png "")](http://from-the-cloud.com) 69 | 70 |
    71 | http://from-the-cloud.com 72 |
    73 | 74 |
    75 | 76 |
    77 | 78 | 79 | 80 | 81 | [![thumbnail](hdurer.png "")](http://blog.hdurer.net) 82 | 83 |
    84 | http://blog.hdurer.net 85 |
    86 | 87 |
    88 | 89 | 90 | 91 | [![thumbnail](hillenius.png "")](http://www.hillenius.com/) 92 | 93 |
    94 | http://www.hillenius.com/ 95 |
    96 | 97 |
    98 | 99 | 100 | 101 | [![thumbnail](kankanan.png "")](http://blog.kankanan.com) 102 | 103 |
    104 | http://blog.kankanan.com 105 |
    106 | 107 |
    108 | 109 | 110 |
    111 | 112 | 113 | 114 | 115 | [![thumbnail](popineau.png "")](http://semantic.supelec.fr/popineau/) 116 | 117 |
    118 | http://semantic.supelec.fr/popineau/ 119 |
    120 | 121 |
    122 | 123 | 124 | 125 | [![thumbnail](underspecified.png "")](http://underspecified.com) 126 | 127 |
    128 | http://underspecified.com 129 |
    130 | 131 |
    132 | 133 | 134 | 135 | [![thumbnail](williamdenman.png "")](http://www.williamdenman.ca/index.html) 136 | 137 |
    138 | http://www.williamdenman.ca/ 139 |
    140 | 141 |
    142 | 143 | 144 |
    145 | 146 | 147 | 148 | 149 | 150 | * [http://ionio.gr/~aribezas/index.html](http://ionio.gr/~aribezas/index.html) 151 | * [http://www.gibiris.org/eo-blog/index.html](http://www.gibiris.org/eo-blog/index.html) 152 | * [http://www.morediff.info/index.html](http://www.morediff.info/index.html) 153 | * [http://www.trick.email/index.html](http://www.trick.email/index.html) 154 | * [http://limist.com/](http://limist.com/) 155 | * [http://www.verona.se](http://www.verona.se) 156 | * [http://yagnesh.org](http://yagnesh.org) 157 | * [http://notes.secretsauce.net](http://notes.secretsauce.net) 158 | * [http://zwz.github.io/index.html](http://zwz.github.io/index.html) 159 | * [http://mauthesis.com](http://mauthesis.com) 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | > “o-blog, great static site generator from emacs Org-mode.” 168 | > 169 | > -- Pierre-Yves Ritschard [\@pyr](https://twitter.com/pyr) ([OpenBSD](http://openbds.org) relayd original author) 170 | > 171 | > -- Sergey Konoplev [\@gray_hemp](http://twitter.com/gray_hemp) ([PostgreSQL Consultant](http://PostgreSQL-Consulting.com)) 172 | > 173 | > -- Olivier Berger [\@olberger](http://twitter.com/olberger) ([APRIL administrator](http://www.april.org)) 174 | > 175 | > -- Dimitri Fontaine [\@tapoueh](http://twitter.com/tapoueh) ([el-get and other](http://tapoueh.org/projects.html) author) 176 | > 177 | > -- Julien Danjou [\@juldanjou](http://twitter.com/juldanjou) ([Awesome window manager](http://awesome.naquadah.org/) and [other](http://julien.danjou.info/software/) author) 178 | > 179 | > -- Daniel Farina [\@danfarina](http://twitter.com/danfarina) (member of the [Heroku](http://www.heroku.com/) Department of Data) 180 | > 181 | > [https://twitter.com/pyr/status/160466727195521024](https://twitter.com/pyr/status/160466727195521024) 182 | 183 | 184 | 185 | 186 | 187 | > “Thank you so much for the truly excellent elisp app. I am learning many 188 | > things from it.” 189 | > 190 | > -- [priyadarshan](https://github.com/priyadarshan) 191 | > 192 | > [https://github.com/renard/o-blog/issues/24](https://github.com/renard/o-blog/issues/24) 193 | 194 | 195 | > “o-blog looks awesome!” 196 | > 197 | > -- [Dirk-Jan C. Binnema](https://github.com/djcb) (Author of [mu4e](http://www.djcbsoftware.nl/code/mu4e) and [emacs-fu](http://emacs-fu.blogspot.com)) 198 | > 199 | >[https://github.com/renard/o-blog/issues/27](https://github.com/renard/o-blog/issues/27) 200 | 201 | > “o-blog, a cool static blog generator from org-mode markup 202 | > with some nice Bootstrap integration.” 203 | > 204 | > -- [Fabián Ezequiel Gallina](http://from-the-cloud.com/en/random/2012/06/17_sites-back.html) 205 | 206 | 207 | > “o-blog is amazing. After using Wordpress seemingly since the dawn of 208 | > time, then Jekyll for a couple of years, this is like a spring breeze 209 | > after a long, cold winter.” 210 | > 211 | > -- [Farasha Euker](https://github.com/fravashi) 212 | > 213 | > [https://github.com/renard/o-blog/issues/33](https://github.com/renard/o-blog/issues/33) 214 | 215 | 216 | -------------------------------------------------------------------------------- /site/src/underspecified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/src/underspecified.png -------------------------------------------------------------------------------- /site/williamdenman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/site/williamdenman.png -------------------------------------------------------------------------------- /templates/blog_archives.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 | 7 |
    8 |
    9 | (ob:insert-template "page_footer.html") 10 | -------------------------------------------------------------------------------- /templates/blog_index_category.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 | 7 |
    8 |
    9 | (ob:insert-template "page_footer.html") 10 | -------------------------------------------------------------------------------- /templates/blog_index_month.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 | 7 |
    8 |
    9 | (ob:insert-template "page_footer.html") 10 | -------------------------------------------------------------------------------- /templates/blog_index_year.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 | 7 |
    8 |
    9 | (ob:insert-template "page_footer.html") 10 | -------------------------------------------------------------------------------- /templates/blog_post-by-tags.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 |
    5 | 8 |
    9 | 31 |
    32 |
    33 | (ob:insert-template "page_footer.html") 34 | -------------------------------------------------------------------------------- /templates/blog_post.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 | 5 | 6 | 7 |
    8 | 31 | 52 |
    53 | 54 |
    55 |
    56 | (ob:post-content-html POST) 57 |
    58 | 59 |
    60 | (ob:gettext :posted-on) . 61 | 71 |
    72 |
    73 | (ob:insert-template "plugin_disqus.html") 74 | (ob:insert-template "page_footer.html") 75 | -------------------------------------------------------------------------------- /templates/blog_rss.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | <lisp>(ob:get 'title BLOG)</lisp> 4 | 5 | 6 | (ob:format-date (ob:get 'date (car (ob:get-posts nil 1)))) 7 | (ob:get 'url BLOG)index.xml 8 | 9 | (loop for p in (ob:get-posts nil 10) 10 | do (progn 11 | (insert "" 12 | (format "%s" (ob:get 'title p)) 13 | (format "%s" (ob:get 'author p)) 14 | (format "" (ob:get 'url BLOG) (ob:get 'htmlfile p)) 15 | (format "%s" (ob:format-date (ob:get 'timestamp p))) 16 | (format "%s" (ob:format-date (ob:get 'timestamp p))) 17 | (format "%s" (ob:get 'htmlfile p))) 18 | 19 | (loop for tag in (ob:get 'tags p) 20 | do (insert (format "" 21 | (ob:get 'safe tag) (ob:get-name tag) (ob:get-name tag)))) 22 | 23 | (insert 24 | (format "%s" (htmlize-protect-string(ob:get 'html p))) 25 | ""))) 26 | 27 | 28 | -------------------------------------------------------------------------------- /templates/blog_sitemap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | (ob:get 'url BLOG)/ 5 | (ob:format-date (ob:get 'timestamp (car (ob:get-posts nil 0)))) 6 | daily 7 | 1.0 8 | 9 | 10 | (loop for POST in PAGES 11 | do (when (not (ob:get 'sitemap POST)) 12 | (ob:insert-template "sitemap_static.html"))) 13 | 14 | 15 | (loop for POST in POSTS 16 | do (when (not (ob:get 'sitemap POST)) 17 | (ob:insert-template "sitemap_post.html"))) 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /templates/blog_static.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 |
    5 |
    6 |

    (ob:post-title POST)

    7 |
    8 |
    9 |
    10 |
    11 | 12 | (ob:post-content-html POST) 13 | 14 |
    15 |
    16 | (ob:insert-template "page_footer.html") 17 | -------------------------------------------------------------------------------- /templates/blog_static_no_title.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 | 5 | (ob:post-content-html POST) 6 | 7 |
    8 |
    9 | (ob:insert-template "page_footer.html") 10 | -------------------------------------------------------------------------------- /templates/blog_tags-details.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 | 18 | (ob:insert-template "page_footer.html") 19 | -------------------------------------------------------------------------------- /templates/blog_tags.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 |
    4 |
    5 | 8 |
    9 | 12 |
    13 |
    14 | (ob:insert-template "page_footer.html") 15 | -------------------------------------------------------------------------------- /templates/debug.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 |
    3 | 4 | 16 | 17 | 38 | 39 |
    40 |

    (ob:gettext :debug-blog-header)

    41 | (ob:insert-template "debug_blog.html") 42 |

    (ob:gettext :debug-posts-header)

    43 | (ob:insert-template "debug_posts.html") 44 |

    (ob:gettext :debug-static-pages-header)

    45 | (ob:insert-template "debug_static.html") 46 |

    (ob:gettext :debug-snippets-header)

    47 | (ob:insert-template "debug_snippets.html") 48 |

    (ob:gettext :debug-tags-header)

    49 | (ob:insert-template "debug_tags.html") 50 |
    51 | (ob:insert-template "page_footer.html") 52 | -------------------------------------------------------------------------------- /templates/debug_blog.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
    (ob:gettext :debug-blog)
    (ob:gettext :debug-property)(ob:gettext :debug-value)
    file
    (htmlize-protect-string (format "%S" (ob:blog-file BLOG)))
    buffer
    (htmlize-protect-string (format "%S" (ob:blog-buffer BLOG)))
    publish-dir
    (htmlize-protect-string (format "%S" (ob:blog-publish-dir BLOG)))
    template-dir
    (htmlize-protect-string (format "%S" (ob:blog-template-dir BLOG)))
    style-dir
    (htmlize-protect-string (format "%S" (ob:blog-style-dir BLOG)))
    posts-filter
    (htmlize-protect-string (format "%S" (ob:blog-posts-filter BLOG)))
    static-filter
    (htmlize-protect-string (format "%S" (ob:blog-static-filter BLOG)))
    snippet-filter
    (htmlize-protect-string (format "%S" (ob:blog-snippet-filter BLOG)))
    title
    (htmlize-protect-string (format "%S" (ob:blog-title BLOG)))
    description
    (htmlize-protect-string (format "%S" (ob:blog-description BLOG)))
    post-build-shell
    (htmlize-protect-string (format "%S" (ob:blog-post-build-shell BLOG)))
    19 |
    20 | -------------------------------------------------------------------------------- /templates/debug_post.html: -------------------------------------------------------------------------------- 1 |
    2 |

    (ob:post-title POST)

    3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
    (ob:gettext :debug-post) (ob:post-title POST)
    (ob:gettext :debug-property)(ob:gettext :debug-value)
    id
    (htmlize-protect-string (format "%S" (ob:post-id POST)))
    title
    (htmlize-protect-string (format "%S" (ob:post-title POST)))
    timespamp
    (htmlize-protect-string (format "%S" (ob:post-timestamp POST)))
    year
    (htmlize-protect-string (format "%S" (ob:post-year POST)))
    month
    (htmlize-protect-string (format "%S" (ob:post-month POST)))
    day
    (htmlize-protect-string (format "%S" (ob:post-day POST)))
    category
    (htmlize-protect-string (format "%S" (ob:post-category POST)))
    tags
    (htmlize-protect-string (format "%S" (ob:post-tags POST)))
    template
    (htmlize-protect-string (format "%S" (ob:post-template POST)))
    filepath
    (htmlize-protect-string (format "%S" (ob:post-filepath POST)))
    filename
    (htmlize-protect-string (format "%S" (ob:post-filename POST)))
    htmlfile
    (htmlize-protect-string (format "%S" (ob:post-htmlfile POST)))
    path-to-root
    (htmlize-protect-string (format "%S" (ob:post-path-to-root POST)))
    sitemap
    (htmlize-protect-string (format "%S" (ob:post-sitemap POST)))
    content
    (htmlize-protect-string (format "%S" (ob:post-content POST)))
    content-html
    (htmlize-protect-string (format "%S" (ob:post-content-html POST)))
    25 |
    26 | -------------------------------------------------------------------------------- /templates/debug_posts.html: -------------------------------------------------------------------------------- 1 |
    2 | (loop for POST in POSTS 3 | do (ob:insert-template "debug_post.html")) 4 | 5 |
    6 | -------------------------------------------------------------------------------- /templates/debug_snippets.html: -------------------------------------------------------------------------------- 1 |
    2 | (loop for POST in SNIPPETS 3 | do (ob:insert-template "debug_post.html")) 4 | 5 |
    6 | -------------------------------------------------------------------------------- /templates/debug_static.html: -------------------------------------------------------------------------------- 1 |
    2 | (loop for POST in STATIC 3 | do (ob:insert-template "debug_post.html")) 4 | 5 |
    6 | -------------------------------------------------------------------------------- /templates/debug_tag.html: -------------------------------------------------------------------------------- 1 |
    2 |

    (ob:tags-name TAG)

    3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    (ob:gettext :debug-tag) (htmlize-protect-string (format "%S" (ob:tags-name TAG)))
    (ob:gettext :debug-property)(ob:gettext :debug-value)
    name
    (htmlize-protect-string (format "%S" (ob:tags-name TAG)))
    safe
    (htmlize-protect-string (format "%S" (ob:tags-safe TAG)))
    count
    (htmlize-protect-string (format "%S" (ob:tags-count TAG)))
    size
    (htmlize-protect-string (format "%S" (ob:tags-size TAG)))
    13 |
    14 | -------------------------------------------------------------------------------- /templates/debug_tags.html: -------------------------------------------------------------------------------- 1 |
    2 | (loop for TAG in TAGS 3 | do (ob:insert-template "debug_tag.html")) 4 | 5 |
    6 | -------------------------------------------------------------------------------- /templates/index_archives.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 | (loop for CATEGORY in (ob:get-posts nil nil nil 'category) 4 | do (progn 5 | (insert (format "
    • %s" (ob:path-to-root) (ob:category-safe CATEGORY) (ob:category-name CATEGORY))) 6 | (let ((POSTS (ob:get-posts (lambda(x) (equal (ob:category-name CATEGORY) (ob:category-name (ob:post-category x))))))) 7 | (ob:insert-template "index_category.html") 8 | ) 9 | (insert "
    • "))) 10 |
      11 |
    12 | -------------------------------------------------------------------------------- /templates/index_category.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 | (loop for YEAR in (ob:get-posts nil nil nil 'year) 4 | do (progn 5 | (insert (format "
    • %.2d" (ob:path-to-root) (ob:category-safe CATEGORY) YEAR YEAR)) 6 | (let ((POSTS (ob:get-posts (lambda(x) (= YEAR (ob:post-year x)))))) 7 | (ob:insert-template "index_year.html") 8 | ) 9 | (insert "
    • "))) 10 |
      11 |
    12 | -------------------------------------------------------------------------------- /templates/index_month.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 | (loop for post in POSTS 4 | do (insert (format "
    • %s %s
    • " 5 | (ob:format-date (ob:post-timestamp post) "%d") 6 | (ob:path-to-root) 7 | (or (ob:post-htmlfile post) "htmlfile") 8 | (or (ob:post-title post) "title")))) 9 |
      10 |
    11 | -------------------------------------------------------------------------------- /templates/index_year.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 | (loop for MONTH in (ob:get-posts nil nil nil 'month) 4 | do (progn 5 | (insert (format "
    • %.2d" (ob:path-to-root) (ob:category-safe CATEGORY) YEAR MONTH MONTH)) 6 | (let ((POSTS (ob:get-posts (lambda(x) (= MONTH (ob:post-month x)))))) 7 | (ob:insert-template "index_month.html") 8 | ) 9 | (insert "
    • "))) 10 |
      11 |
    12 | -------------------------------------------------------------------------------- /templates/nav_breadcrumb.html: -------------------------------------------------------------------------------- 1 | 63 | -------------------------------------------------------------------------------- /templates/nav_tag-cloud.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 | (loop for tag in TAGS 4 | do (insert (format "
    • %s
    • " (ob:tags-size tag) (ob:path-to-root) (ob:tags-safe tag) (ob:tags-name tag)))) 5 |
      6 |
    7 | -------------------------------------------------------------------------------- /templates/page_footer.html: -------------------------------------------------------------------------------- 1 |
    2 | 34 | 38 | 39 | 40 | 41 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /templates/page_header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <lisp>(if (boundp 'POST) (ob:post-title POST) (ob:blog-titleBLOG))</lisp> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 26 | 27 | 32 |
    33 | -------------------------------------------------------------------------------- /templates/page_redirect.html: -------------------------------------------------------------------------------- 1 | (ob:insert-template "page_header.html") 2 | 21 | 24 | (ob:insert-template "page_footer.html") 25 | -------------------------------------------------------------------------------- /templates/plugin_analytics.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/plugin_disqus.html: -------------------------------------------------------------------------------- 1 |
    2 | 13 | 14 | blog comments powered by Disqus 15 | -------------------------------------------------------------------------------- /templates/plugin_qrcode.html: -------------------------------------------------------------------------------- 1 |
    2 | " 5 | alt="qr-code" /> 6 |
    7 | -------------------------------------------------------------------------------- /templates/sitemap_post.html: -------------------------------------------------------------------------------- 1 | 2 | (format "%s/%s" (ob:get 'url BLOG) (ob:get 'htmlfile POST)) 3 | (format "%s" (ob:format-date (ob:get 'timestamp POST))) 4 | weekly 5 | 0.6 6 | 7 | -------------------------------------------------------------------------------- /templates/sitemap_static.html: -------------------------------------------------------------------------------- 1 | 2 | (format "%s/%s" (ob:get 'url BLOG) (ob:get 'htmlfile POST)) 3 | (format "%s" (ob:format-date (ob:get 'timestamp POST))) 4 | weekly 5 | 0.8 6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/style/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/templates/style/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /templates/style/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/templates/style/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /templates/style/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/templates/style/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /templates/style/font-awesome/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.1.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-square:before,.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"} -------------------------------------------------------------------------------- /templates/style/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/templates/style/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /templates/style/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/templates/style/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /templates/style/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/templates/style/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /templates/style/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renard/o-blog/e466c59478feddc8126c43c1b98550474af484c0/templates/style/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /templates/style/o-blog-bootstrap.js: -------------------------------------------------------------------------------- 1 | $(document).ready( 2 | function() { 3 | // Build nav bar 4 | var navbarUl = $('.navbar .navbar-collapse > ul'); 5 | navbarUl.addClass('nav').addClass('navbar-nav'); 6 | 7 | /* create the top menu bar */ 8 | var dropdown = navbarUl.find('li ul') 9 | dropdown.parent().addClass('dropdown') 10 | /* find sub menu items */ 11 | //dropdown.parent().findaddClass("dropdown-menu"); 12 | 13 | /* and add dropdown features */ 14 | dropdown.parent().find('> a').addClass('dropdown-toggle') 15 | .attr("data-toggle", "dropdown") 16 | .append(' '); 17 | 18 | dropdown.addClass("dropdown-menu"); 19 | 20 | /* Add divider class if menu item is empty */ 21 | dropdown.parent().find('.dropdown-menu li').each(function() { 22 | if ( $(this).text() == '') $(this).addClass('divider') 23 | }); 24 | 25 | /* Compute page min height */ 26 | $('div#page').css('min-height', $(window).innerHeight() - 27 | $('#footer').outerHeight() - 28 | $('div.navbar-fixed-top.navbar').outerHeight() - 29 | parseInt($('div#page').css('padding-top')) - 30 | parseInt($('div#page').css('padding-bottom'))); 31 | 32 | 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /templates/style/o-blog-bootstrap.min.js: -------------------------------------------------------------------------------- 1 | function equalHeight(e){tallest=0,e.each(function(){thisHeight=$(this).height(),thisHeight>tallest&&(tallest=thisHeight)}),e.each(function(){$(this).height(tallest)})}function ob_load_tags(){$.each($(".ob-tagcloud"),function(e,t){var n=$(t).data("source")||"tags.js",r=$(t).data("path-to-root")||".";$.getJSON(n,function(e){var n=[];$.each(e.tags,function(e,t){n.push('
  • '+t.tag+"
  • ")}),$(t).replaceWith("
      "+n.join(" ")+"
    ")})})}function ob_load_articles(e){$.each($(".ob-articles"),function(t,n){var r=$(n).data("source")||"articles.js",i=$(n).data("path-to-root")||".",s=$(n).data("limit")||10,o=$(n).data("excerpt")||!1,u=$(n).data("category");$.getJSON(r).done(function(t){var r=[];$.each(t.articles[u],function(e,t){if(e>=s)return!1;o?r.push('

    '+t.title+"

    "+t.excerpt+"

    "):r.push('
  • '+t.title+"
  • ")}),o?$(n).replaceWith('
    '+r.join(" ")+"
    "):$(n).replaceWith("
      "+r.join(" ")+"
    "),$.isFunction(e)&&e.call()})})}function init_menu_dropdown(){setTimeout(function(){$('.navbar .navbar-collapse > ul li a[href="'+path_to_root+"/"+ob_this_page+'"]').parent().addClass("active").parent().parent().addClass("active")},1);var e=$(".navbar .navbar-collapse > ul");e.addClass("nav").addClass("navbar-nav");var t=e.find("li ul");t.parent().addClass("dropdown");var n=t.parent().find("> a");n.addClass("dropdown-toggle").attr("data-toggle","dropdown").find("b.caret").remove(),n.append(' '),t.addClass("dropdown-menu"),t.parent().find(".dropdown-menu li").each(function(){$(this).text()==""&&$(this).addClass("divider")})}$(document).ready(function(){ob_load_articles(init_menu_dropdown),setTimeout(init_menu_dropdown,50),$("div#page").css("min-height",$(window).innerHeight()-$("#footer").outerHeight()-$("div.navbar-fixed-top.navbar").outerHeight()-parseInt($("div#page").css("padding-top"))-parseInt($("div#page").css("padding-bottom"))),setTimeout(function(){$(".row").each(function(){equalHeight($(this).find(".thumbnail")),equalHeight($(this).find(".src"))})},300),ob_load_tags()}); -------------------------------------------------------------------------------- /templates/style/o-blog-load.min.js: -------------------------------------------------------------------------------- 1 | (function(e){e.getUrlVar=function(e){var t=(new RegExp(e+"=([^&]*)","i")).exec(window.location.search);return t&&unescape(t[1])||""}})(jQuery),$(document).ready(function(){var e=$.getUrlVar("p");$("#page article .article-content").load(e)}); --------------------------------------------------------------------------------