]> git.quilime.com - plog.git/commitdiff
First Checkin
authorGabriel Dunne <gdunne@quilime.com>
Fri, 9 Jul 2010 06:45:21 +0000 (23:45 -0700)
committerGabriel Dunne <gdunne@quilime.com>
Fri, 9 Jul 2010 06:45:21 +0000 (23:45 -0700)
24 files changed:
content/cat1/test_post [new file with mode: 0644]
content/cat2/test_post [new file with mode: 0644]
lib/data.php [new file with mode: 0644]
lib/init.php [new file with mode: 0644]
lib/markdown.php [new file with mode: 0755]
lib/output.php [new file with mode: 0644]
lib/static.php [new file with mode: 0644]
lib/template.php [new file with mode: 0644]
pages/about [new file with mode: 0644]
pages/links [new file with mode: 0644]
public/.htaccess [new file with mode: 0644]
public/css/style.css [new file with mode: 0644]
public/index.php [new file with mode: 0644]
scripts/content [new file with mode: 0755]
templates/default.html.tpl [new file with mode: 0644]
templates/default.json.tpl [new file with mode: 0644]
templates/default.rss.tpl [new file with mode: 0644]
templates/default.txt.tpl [new file with mode: 0644]
templates/entry.html.tpl [new file with mode: 0644]
templates/footer.html.tpl [new file with mode: 0644]
templates/head-inc.html.tpl [new file with mode: 0644]
templates/nav.html.tpl [new file with mode: 0644]
templates/page.html.tpl [new file with mode: 0644]
templates/single.html.tpl [new file with mode: 0644]

diff --git a/content/cat1/test_post b/content/cat1/test_post
new file mode 100644 (file)
index 0000000..4a6a223
--- /dev/null
@@ -0,0 +1,20 @@
+title = test post
+date = 2010-07-07
+tags = test post tag1 tag2
+--
+
+This is a test post. Hello world!
+
+*ooo*  
+**ahh**  
+***eee***  
+
+some inline code `some code` woo
+    
+    fn(test) { code };
+    some more <pre> code
+    // a comment perhaps
+
+line    
+------
+break
diff --git a/content/cat2/test_post b/content/cat2/test_post
new file mode 100644 (file)
index 0000000..63f03f9
--- /dev/null
@@ -0,0 +1,8 @@
+title = another test post
+date = 2010-07-01
+tags = with different tags
+--
+
+this is an older test post!
+
+`some code`
diff --git a/lib/data.php b/lib/data.php
new file mode 100644 (file)
index 0000000..6d04a9e
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * get data
+ */
+function get_data( $sources = array(),  $params  = array())
+{
+       // if no set sources, grab everything in the content folder
+       if (sizeof($sources) == 0)
+               $sources = get_content_folders();
+
+       $result = array();
+       $result_total = 0;
+       foreach ($sources as $dir) {
+
+               if (is_array($dir) && isset($dir['basename']))
+                       $dir = $dir['basename'];
+
+               $contents = glob(LOCAL_ROOT . CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . '/*');
+
+               if (sizeof($contents) <= 0) continue;
+
+               foreach ($contents as $f) {
+                       if (is_file($f)) {
+
+                               if ($params['name']) {
+                                       if (basename($f) != $params['name'])
+                                               continue;
+                               }
+
+                               $parsed = parse_file($f);
+
+                               if ($parsed['draft'])
+                                       continue;
+
+                               $result[] = $parsed;
+                               $result_total++;
+                       }
+               }
+       }
+
+       // order by date
+       foreach ($result as $key => $row) {
+           $time[$key] = $row['timestamp'];
+       }
+       if ($time)
+               array_multisort($time, SORT_DESC, $result);
+
+       return array($result, $result_total);
+}
+
+
+/**
+ * parse data file
+ */
+function parse_file($f)
+{
+       $pathparts = explode("/", dirname($f));
+
+       $file_contents = explode("\n", file_get_contents($f, FILE_USE_INCLUDE_PATH));
+       $cc = "";
+       $content = "";
+       $conf = true;
+       foreach ( $file_contents as $fc ) {
+               if ($fc == CONFIG_DELIMITER) {
+                       $conf = false;
+                       continue;
+               }
+               if ($conf) $cc .= $fc . "\n";
+               else $content .= $fc . "\n";
+       }
+
+       $config = parse_ini_string($cc);
+
+       $res = $config;
+
+       $res['url'] = $res['is_page'] == 1 ? get_base_dir() . '/' . basename($f) . '/' : get_base_dir() . '/' . $pathparts[sizeof($pathparts)-1] . '/' . basename($f);
+       $res['timestamp'] = date('U', strtotime( $config['date'] ? $config['date'] : filemtime($f)));
+       $res['cat'] = $pathparts[sizeof($pathparts)-1];
+       $res['content'] = Markdown($content);
+       $res['tags'] = explode(' ', $config['tags']);
+
+       return $res;
+}
+
+
+/**
+ * get content folders
+ */
+function get_content_folders()
+{
+       $folders = glob(LOCAL_ROOT . CONTENT_DIR . DIRECTORY_SEPARATOR . '/*', GLOB_ONLYDIR);
+       $content_folders = array();
+       foreach($folders as $folder)
+               $content_folders[] = array(
+                       'basename' => basename($folder),
+                       'title' => basename($folder),
+                       'url' => get_base_dir() . '/' . basename($folder) . '/'
+                       );
+       return $content_folders;
+}
+
+
+/**
+ * get pages
+ */
+function get_pages()
+{
+       $page_files = glob(LOCAL_ROOT . PAGE_DIR . DIRECTORY_SEPARATOR . '/*');
+       $pages = array();
+       foreach($page_files as $page) {
+               $arr = parse_file($page);
+               $arr['is_page'] = 1;
+               $pages[] = $arr;
+       }
+       return $pages;
+}
+
+?>
diff --git a/lib/init.php b/lib/init.php
new file mode 100644 (file)
index 0000000..e45c464
--- /dev/null
@@ -0,0 +1,21 @@
+<?php 
+
+    putenv("TZ=America/Los_Angeles");
+    
+       ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . dirname(realpath(__FILE__)));
+
+       define ('SITE_TITLE', 'plog');
+       define ('LOCAL_ROOT', '/local/plog/root/');
+       define ('WEB_ROOT', 'http://webroot.com/');
+       
+       define ('CONTENT_DIR', 'content');
+       define ('TEMPLATE_DIR', 'templates');
+       define ('PAGE_DIR', 'pages');   
+       define ('CONFIG_DELIMITER', '--');      
+       
+    require_once 'data.php';
+    require_once 'output.php';
+    require_once 'markdown.php';       
+    require_once 'template.php';       
+
+?>
\ No newline at end of file
diff --git a/lib/markdown.php b/lib/markdown.php
new file mode 100755 (executable)
index 0000000..dd1b9e5
--- /dev/null
@@ -0,0 +1,1732 @@
+<?php
+#
+# Markdown  -  A text-to-HTML conversion tool for web writers
+#
+# PHP Markdown
+# Copyright (c) 2004-2009 Michel Fortin  
+# <http://michelf.com/projects/php-markdown/>
+#
+# Original Markdown
+# Copyright (c) 2004-2006 John Gruber  
+# <http://daringfireball.net/projects/markdown/>
+#
+
+
+define( 'MARKDOWN_VERSION',  "1.0.1n" ); # Sat 10 Oct 2009
+
+
+#
+# Global default settings:
+#
+
+# Change to ">" for HTML output
+@define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX',  " />");
+
+# Define the width of a tab for code blocks.
+@define( 'MARKDOWN_TAB_WIDTH',     4 );
+
+
+#
+# WordPress settings:
+#
+
+# Change to false to remove Markdown from posts and/or comments.
+@define( 'MARKDOWN_WP_POSTS',      true );
+@define( 'MARKDOWN_WP_COMMENTS',   true );
+
+
+
+### Standard Function Interface ###
+
+@define( 'MARKDOWN_PARSER_CLASS',  'Markdown_Parser' );
+
+function Markdown($text) {
+#
+# Initialize the parser and return the result of its transform method.
+#
+       # Setup static parser variable.
+       static $parser;
+       if (!isset($parser)) {
+               $parser_class = MARKDOWN_PARSER_CLASS;
+               $parser = new $parser_class;
+       }
+
+       # Transform text using parser.
+       return $parser->transform($text);
+}
+
+
+### WordPress Plugin Interface ###
+
+/*
+Plugin Name: Markdown
+Plugin URI: http://michelf.com/projects/php-markdown/
+Description: <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.com/projects/php-markdown/">More...</a>
+Version: 1.0.1n
+Author: Michel Fortin
+Author URI: http://michelf.com/
+*/
+
+if (isset($wp_version)) {
+       # More details about how it works here:
+       # <http://michelf.com/weblog/2005/wordpress-text-flow-vs-markdown/>
+       
+       # Post content and excerpts
+       # - Remove WordPress paragraph generator.
+       # - Run Markdown on excerpt, then remove all tags.
+       # - Add paragraph tag around the excerpt, but remove it for the excerpt rss.
+       if (MARKDOWN_WP_POSTS) {
+               remove_filter('the_content',     'wpautop');
+        remove_filter('the_content_rss', 'wpautop');
+               remove_filter('the_excerpt',     'wpautop');
+               add_filter('the_content',     'Markdown', 6);
+        add_filter('the_content_rss', 'Markdown', 6);
+               add_filter('get_the_excerpt', 'Markdown', 6);
+               add_filter('get_the_excerpt', 'trim', 7);
+               add_filter('the_excerpt',     'mdwp_add_p');
+               add_filter('the_excerpt_rss', 'mdwp_strip_p');
+               
+               remove_filter('content_save_pre',  'balanceTags', 50);
+               remove_filter('excerpt_save_pre',  'balanceTags', 50);
+               add_filter('the_content',         'balanceTags', 50);
+               add_filter('get_the_excerpt', 'balanceTags', 9);
+       }
+       
+       # Comments
+       # - Remove WordPress paragraph generator.
+       # - Remove WordPress auto-link generator.
+       # - Scramble important tags before passing them to the kses filter.
+       # - Run Markdown on excerpt then remove paragraph tags.
+       if (MARKDOWN_WP_COMMENTS) {
+               remove_filter('comment_text', 'wpautop', 30);
+               remove_filter('comment_text', 'make_clickable');
+               add_filter('pre_comment_content', 'Markdown', 6);
+               add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
+               add_filter('pre_comment_content', 'mdwp_show_tags', 12);
+               add_filter('get_comment_text',    'Markdown', 6);
+               add_filter('get_comment_excerpt', 'Markdown', 6);
+               add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
+       
+               global $mdwp_hidden_tags, $mdwp_placeholders;
+               $mdwp_hidden_tags = explode(' ',
+                       '<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>');
+               $mdwp_placeholders = explode(' ', str_rot13(
+                       'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '.
+                       'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli'));
+       }
+       
+       function mdwp_add_p($text) {
+               if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) {
+                       $text = '<p>'.$text.'</p>';
+                       $text = preg_replace('{\n{2,}}', "</p>\n\n<p>", $text);
+               }
+               return $text;
+       }
+       
+       function mdwp_strip_p($t) { return preg_replace('{</?p>}i', '', $t); }
+
+       function mdwp_hide_tags($text) {
+               global $mdwp_hidden_tags, $mdwp_placeholders;
+               return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text);
+       }
+       function mdwp_show_tags($text) {
+               global $mdwp_hidden_tags, $mdwp_placeholders;
+               return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text);
+       }
+}
+
+
+### bBlog Plugin Info ###
+
+function identify_modifier_markdown() {
+       return array(
+               'name'                  => 'markdown',
+               'type'                  => 'modifier',
+               'nicename'              => 'Markdown',
+               'description'   => 'A text-to-HTML conversion tool for web writers',
+               'authors'               => 'Michel Fortin and John Gruber',
+               'licence'               => 'BSD-like',
+               'version'               => MARKDOWN_VERSION,
+               'help'                  => '<a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.com/projects/php-markdown/">More...</a>'
+       );
+}
+
+
+### Smarty Modifier Interface ###
+
+function smarty_modifier_markdown($text) {
+       return Markdown($text);
+}
+
+
+### Textile Compatibility Mode ###
+
+# Rename this file to "classTextile.php" and it can replace Textile everywhere.
+
+if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
+       # Try to include PHP SmartyPants. Should be in the same directory.
+       @include_once 'smartypants.php';
+       # Fake Textile class. It calls Markdown instead.
+       class Textile {
+               function TextileThis($text, $lite='', $encode='') {
+                       if ($lite == '' && $encode == '')    $text = Markdown($text);
+                       if (function_exists('SmartyPants'))  $text = SmartyPants($text);
+                       return $text;
+               }
+               # Fake restricted version: restrictions are not supported for now.
+               function TextileRestricted($text, $lite='', $noimage='') {
+                       return $this->TextileThis($text, $lite);
+               }
+               # Workaround to ensure compatibility with TextPattern 4.0.3.
+               function blockLite($text) { return $text; }
+       }
+}
+
+
+
+#
+# Markdown Parser Class
+#
+
+class Markdown_Parser {
+
+       # Regex to match balanced [brackets].
+       # Needed to insert a maximum bracked depth while converting to PHP.
+       var $nested_brackets_depth = 6;
+       var $nested_brackets_re;
+       
+       var $nested_url_parenthesis_depth = 4;
+       var $nested_url_parenthesis_re;
+
+       # Table of hash values for escaped characters:
+       var $escape_chars = '\`*_{}[]()>#+-.!';
+       var $escape_chars_re;
+
+       # Change to ">" for HTML output.
+       var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
+       var $tab_width = MARKDOWN_TAB_WIDTH;
+       
+       # Change to `true` to disallow markup or entities.
+       var $no_markup = false;
+       var $no_entities = false;
+       
+       # Predefined urls and titles for reference links and images.
+       var $predef_urls = array();
+       var $predef_titles = array();
+
+
+       function Markdown_Parser() {
+       #
+       # Constructor function. Initialize appropriate member variables.
+       #
+               $this->_initDetab();
+               $this->prepareItalicsAndBold();
+       
+               $this->nested_brackets_re = 
+                       str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
+                       str_repeat('\])*', $this->nested_brackets_depth);
+       
+               $this->nested_url_parenthesis_re = 
+                       str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
+                       str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
+               
+               $this->escape_chars_re = '['.preg_quote($this->escape_chars).']';
+               
+               # Sort document, block, and span gamut in ascendent priority order.
+               asort($this->document_gamut);
+               asort($this->block_gamut);
+               asort($this->span_gamut);
+       }
+
+
+       # Internal hashes used during transformation.
+       var $urls = array();
+       var $titles = array();
+       var $html_hashes = array();
+       
+       # Status flag to avoid invalid nesting.
+       var $in_anchor = false;
+       
+       
+       function setup() {
+       #
+       # Called before the transformation process starts to setup parser 
+       # states.
+       #
+               # Clear global hashes.
+               $this->urls = $this->predef_urls;
+               $this->titles = $this->predef_titles;
+               $this->html_hashes = array();
+               
+               $in_anchor = false;
+       }
+       
+       function teardown() {
+       #
+       # Called after the transformation process to clear any variable 
+       # which may be taking up memory unnecessarly.
+       #
+               $this->urls = array();
+               $this->titles = array();
+               $this->html_hashes = array();
+       }
+
+
+       function transform($text) {
+       #
+       # Main function. Performs some preprocessing on the input text
+       # and pass it through the document gamut.
+       #
+               $this->setup();
+       
+               # Remove UTF-8 BOM and marker character in input, if present.
+               $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
+
+               # Standardize line endings:
+               #   DOS to Unix and Mac to Unix
+               $text = preg_replace('{\r\n?}', "\n", $text);
+
+               # Make sure $text ends with a couple of newlines:
+               $text .= "\n\n";
+
+               # Convert all tabs to spaces.
+               $text = $this->detab($text);
+
+               # Turn block-level HTML blocks into hash entries
+               $text = $this->hashHTMLBlocks($text);
+
+               # Strip any lines consisting only of spaces and tabs.
+               # This makes subsequent regexen easier to write, because we can
+               # match consecutive blank lines with /\n+/ instead of something
+               # contorted like /[ ]*\n+/ .
+               $text = preg_replace('/^[ ]+$/m', '', $text);
+
+               # Run document gamut methods.
+               foreach ($this->document_gamut as $method => $priority) {
+                       $text = $this->$method($text);
+               }
+               
+               $this->teardown();
+
+               return $text . "\n";
+       }
+       
+       var $document_gamut = array(
+               # Strip link definitions, store in hashes.
+               "stripLinkDefinitions" => 20,
+               
+               "runBasicBlockGamut"   => 30,
+               );
+
+
+       function stripLinkDefinitions($text) {
+       #
+       # Strips link definitions from text, stores the URLs and titles in
+       # hash references.
+       #
+               $less_than_tab = $this->tab_width - 1;
+
+               # Link defs are in the form: ^[id]: url "optional title"
+               $text = preg_replace_callback('{
+                                                       ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
+                                                         [ ]*
+                                                         \n?                           # maybe *one* newline
+                                                         [ ]*
+                                                       (?:
+                                                         <(.+?)>                       # url = $2
+                                                       |
+                                                         (\S+?)                        # url = $3
+                                                       )
+                                                         [ ]*
+                                                         \n?                           # maybe one newline
+                                                         [ ]*
+                                                       (?:
+                                                               (?<=\s)                 # lookbehind for whitespace
+                                                               ["(]
+                                                               (.*?)                   # title = $4
+                                                               [")]
+                                                               [ ]*
+                                                       )?      # title is optional
+                                                       (?:\n+|\Z)
+                       }xm',
+                       array(&$this, '_stripLinkDefinitions_callback'),
+                       $text);
+               return $text;
+       }
+       function _stripLinkDefinitions_callback($matches) {
+               $link_id = strtolower($matches[1]);
+               $url = $matches[2] == '' ? $matches[3] : $matches[2];
+               $this->urls[$link_id] = $url;
+               $this->titles[$link_id] =& $matches[4];
+               return ''; # String that will replace the block
+       }
+
+
+       function hashHTMLBlocks($text) {
+               if ($this->no_markup)  return $text;
+
+               $less_than_tab = $this->tab_width - 1;
+
+               # Hashify HTML blocks:
+               # We only want to do this for block-level HTML tags, such as headers,
+               # lists, and tables. That's because we still want to wrap <p>s around
+               # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
+               # phrase emphasis, and spans. The list of tags we're looking for is
+               # hard-coded:
+               #
+               # *  List "a" is made of tags which can be both inline or block-level.
+               #    These will be treated block-level when the start tag is alone on 
+               #    its line, otherwise they're not matched here and will be taken as 
+               #    inline later.
+               # *  List "b" is made of tags which are always block-level;
+               #
+               $block_tags_a_re = 'ins|del';
+               $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
+                                                  'script|noscript|form|fieldset|iframe|math';
+
+               # Regular expression for the content of a block tag.
+               $nested_tags_level = 4;
+               $attr = '
+                       (?>                             # optional tag attributes
+                         \s                    # starts with whitespace
+                         (?>
+                               [^>"/]+         # text outside quotes
+                         |
+                               /+(?!>)         # slash not followed by ">"
+                         |
+                               "[^"]*"         # text inside double quotes (tolerate ">")
+                         |
+                               \'[^\']*\'      # text inside single quotes (tolerate ">")
+                         )*
+                       )?      
+                       ';
+               $content =
+                       str_repeat('
+                               (?>
+                                 [^<]+                 # content without tag
+                               |
+                                 <\2                   # nested opening tag
+                                       '.$attr.'       # attributes
+                                       (?>
+                                         />
+                                       |
+                                         >', $nested_tags_level).      # end of opening tag
+                                         '.*?'.                                        # last level nested tag content
+                       str_repeat('
+                                         </\2\s*>      # closing nested tag
+                                       )
+                                 |                             
+                                       <(?!/\2\s*>     # other tags with a different name
+                                 )
+                               )*',
+                               $nested_tags_level);
+               $content2 = str_replace('\2', '\3', $content);
+
+               # First, look for nested blocks, e.g.:
+               #       <div>
+               #               <div>
+               #               tags for inner block must be indented.
+               #               </div>
+               #       </div>
+               #
+               # The outermost tags must start at the left margin for this to match, and
+               # the inner nested divs must be indented.
+               # We need to do this before the next, more liberal match, because the next
+               # match will start at the first `<div>` and stop at the first `</div>`.
+               $text = preg_replace_callback('{(?>
+                       (?>
+                               (?<=\n\n)               # Starting after a blank line
+                               |                               # or
+                               \A\n?                   # the beginning of the doc
+                       )
+                       (                                               # save in $1
+
+                         # Match from `\n<tag>` to `</tag>\n`, handling nested tags 
+                         # in between.
+                                       
+                                               [ ]{0,'.$less_than_tab.'}
+                                               <('.$block_tags_b_re.')# start tag = $2
+                                               '.$attr.'>                      # attributes followed by > and \n
+                                               '.$content.'            # content, support nesting
+                                               </\2>                           # the matching end tag
+                                               [ ]*                            # trailing spaces/tabs
+                                               (?=\n+|\Z)      # followed by a newline or end of document
+
+                       | # Special version for tags of group a.
+
+                                               [ ]{0,'.$less_than_tab.'}
+                                               <('.$block_tags_a_re.')# start tag = $3
+                                               '.$attr.'>[ ]*\n        # attributes followed by >
+                                               '.$content2.'           # content, support nesting
+                                               </\3>                           # the matching end tag
+                                               [ ]*                            # trailing spaces/tabs
+                                               (?=\n+|\Z)      # followed by a newline or end of document
+                                       
+                       | # Special case just for <hr />. It was easier to make a special 
+                         # case than to make the other regex more complicated.
+                       
+                                               [ ]{0,'.$less_than_tab.'}
+                                               <(hr)                           # start tag = $2
+                                               '.$attr.'                       # attributes
+                                               /?>                                     # the matching end tag
+                                               [ ]*
+                                               (?=\n{2,}|\Z)           # followed by a blank line or end of document
+                       
+                       | # Special case for standalone HTML comments:
+                       
+                                       [ ]{0,'.$less_than_tab.'}
+                                       (?s:
+                                               <!-- .*? -->
+                                       )
+                                       [ ]*
+                                       (?=\n{2,}|\Z)           # followed by a blank line or end of document
+                       
+                       | # PHP and ASP-style processor instructions (<? and <%)
+                       
+                                       [ ]{0,'.$less_than_tab.'}
+                                       (?s:
+                                               <([?%])                 # $2
+                                               .*?
+                                               \2>
+                                       )
+                                       [ ]*
+                                       (?=\n{2,}|\Z)           # followed by a blank line or end of document
+                                       
+                       )
+                       )}Sxmi',
+                       array(&$this, '_hashHTMLBlocks_callback'),
+                       $text);
+
+               return $text;
+       }
+       function _hashHTMLBlocks_callback($matches) {
+               $text = $matches[1];
+               $key  = $this->hashBlock($text);
+               return "\n\n$key\n\n";
+       }
+       
+       
+       function hashPart($text, $boundary = 'X') {
+       #
+       # Called whenever a tag must be hashed when a function insert an atomic 
+       # element in the text stream. Passing $text to through this function gives
+       # a unique text-token which will be reverted back when calling unhash.
+       #
+       # The $boundary argument specify what character should be used to surround
+       # the token. By convension, "B" is used for block elements that needs not
+       # to be wrapped into paragraph tags at the end, ":" is used for elements
+       # that are word separators and "X" is used in the general case.
+       #
+               # Swap back any tag hash found in $text so we do not have to `unhash`
+               # multiple times at the end.
+               $text = $this->unhash($text);
+               
+               # Then hash the block.
+               static $i = 0;
+               $key = "$boundary\x1A" . ++$i . $boundary;
+               $this->html_hashes[$key] = $text;
+               return $key; # String that will replace the tag.
+       }
+
+
+       function hashBlock($text) {
+       #
+       # Shortcut function for hashPart with block-level boundaries.
+       #
+               return $this->hashPart($text, 'B');
+       }
+
+
+       var $block_gamut = array(
+       #
+       # These are all the transformations that form block-level
+       # tags like paragraphs, headers, and list items.
+       #
+               "doHeaders"         => 10,
+               "doHorizontalRules" => 20,
+               
+               "doLists"           => 40,
+               "doCodeBlocks"      => 50,
+               "doBlockQuotes"     => 60,
+               );
+
+       function runBlockGamut($text) {
+       #
+       # Run block gamut tranformations.
+       #
+               # We need to escape raw HTML in Markdown source before doing anything 
+               # else. This need to be done for each block, and not only at the 
+               # begining in the Markdown function since hashed blocks can be part of
+               # list items and could have been indented. Indented blocks would have 
+               # been seen as a code block in a previous pass of hashHTMLBlocks.
+               $text = $this->hashHTMLBlocks($text);
+               
+               return $this->runBasicBlockGamut($text);
+       }
+       
+       function runBasicBlockGamut($text) {
+       #
+       # Run block gamut tranformations, without hashing HTML blocks. This is 
+       # useful when HTML blocks are known to be already hashed, like in the first
+       # whole-document pass.
+       #
+               foreach ($this->block_gamut as $method => $priority) {
+                       $text = $this->$method($text);
+               }
+               
+               # Finally form paragraph and restore hashed blocks.
+               $text = $this->formParagraphs($text);
+
+               return $text;
+       }
+       
+       
+       function doHorizontalRules($text) {
+               # Do Horizontal Rules:
+               return preg_replace(
+                       '{
+                               ^[ ]{0,3}       # Leading space
+                               ([-*_])         # $1: First marker
+                               (?>                     # Repeated marker group
+                                       [ ]{0,2}        # Zero, one, or two spaces.
+                                       \1                      # Marker character
+                               ){2,}           # Group repeated at least twice
+                               [ ]*            # Tailing spaces
+                               $                       # End of line.
+                       }mx',
+                       "\n".$this->hashBlock("<hr$this->empty_element_suffix")."\n", 
+                       $text);
+       }
+
+
+       var $span_gamut = array(
+       #
+       # These are all the transformations that occur *within* block-level
+       # tags like paragraphs, headers, and list items.
+       #
+               # Process character escapes, code spans, and inline HTML
+               # in one shot.
+               "parseSpan"           => -30,
+
+               # Process anchor and image tags. Images must come first,
+               # because ![foo][f] looks like an anchor.
+               "doImages"            =>  10,
+               "doAnchors"           =>  20,
+               
+               # Make links out of things like `<http://example.com/>`
+               # Must come after doAnchors, because you can use < and >
+               # delimiters in inline links like [this](<url>).
+               "doAutoLinks"         =>  30,
+               "encodeAmpsAndAngles" =>  40,
+
+               "doItalicsAndBold"    =>  50,
+               "doHardBreaks"        =>  60,
+               );
+
+       function runSpanGamut($text) {
+       #
+       # Run span gamut tranformations.
+       #
+               foreach ($this->span_gamut as $method => $priority) {
+                       $text = $this->$method($text);
+               }
+
+               return $text;
+       }
+       
+       
+       function doHardBreaks($text) {
+               # Do hard breaks:
+               return preg_replace_callback('/ {2,}\n/', 
+                       array(&$this, '_doHardBreaks_callback'), $text);
+       }
+       function _doHardBreaks_callback($matches) {
+               return $this->hashPart("<br$this->empty_element_suffix\n");
+       }
+
+
+       function doAnchors($text) {
+       #
+       # Turn Markdown link shortcuts into XHTML <a> tags.
+       #
+               if ($this->in_anchor) return $text;
+               $this->in_anchor = true;
+               
+               #
+               # First, handle reference-style links: [link text] [id]
+               #
+               $text = preg_replace_callback('{
+                       (                                       # wrap whole match in $1
+                         \[
+                               ('.$this->nested_brackets_re.') # link text = $2
+                         \]
+
+                         [ ]?                          # one optional space
+                         (?:\n[ ]*)?           # one optional newline followed by spaces
+
+                         \[
+                               (.*?)           # id = $3
+                         \]
+                       )
+                       }xs',
+                       array(&$this, '_doAnchors_reference_callback'), $text);
+
+               #
+               # Next, inline-style links: [link text](url "optional title")
+               #
+               $text = preg_replace_callback('{
+                       (                               # wrap whole match in $1
+                         \[
+                               ('.$this->nested_brackets_re.') # link text = $2
+                         \]
+                         \(                    # literal paren
+                               [ \n]*
+                               (?:
+                                       <(.+?)> # href = $3
+                               |
+                                       ('.$this->nested_url_parenthesis_re.')  # href = $4
+                               )
+                               [ \n]*
+                               (                       # $5
+                                 ([\'"])       # quote char = $6
+                                 (.*?)         # Title = $7
+                                 \6            # matching quote
+                                 [ \n]*        # ignore any spaces/tabs between closing quote and )
+                               )?                      # title is optional
+                         \)
+                       )
+                       }xs',
+                       array(&$this, '_doAnchors_inline_callback'), $text);
+
+               #
+               # Last, handle reference-style shortcuts: [link text]
+               # These must come last in case you've also got [link text][1]
+               # or [link text](/foo)
+               #
+               $text = preg_replace_callback('{
+                       (                                       # wrap whole match in $1
+                         \[
+                               ([^\[\]]+)              # link text = $2; can\'t contain [ or ]
+                         \]
+                       )
+                       }xs',
+                       array(&$this, '_doAnchors_reference_callback'), $text);
+
+               $this->in_anchor = false;
+               return $text;
+       }
+       function _doAnchors_reference_callback($matches) {
+               $whole_match =  $matches[1];
+               $link_text   =  $matches[2];
+               $link_id     =& $matches[3];
+
+               if ($link_id == "") {
+                       # for shortcut links like [this][] or [this].
+                       $link_id = $link_text;
+               }
+               
+               # lower-case and turn embedded newlines into spaces
+               $link_id = strtolower($link_id);
+               $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
+
+               if (isset($this->urls[$link_id])) {
+                       $url = $this->urls[$link_id];
+                       $url = $this->encodeAttribute($url);
+                       
+                       $result = "<a href=\"$url\"";
+                       if ( isset( $this->titles[$link_id] ) ) {
+                               $title = $this->titles[$link_id];
+                               $title = $this->encodeAttribute($title);
+                               $result .=  " title=\"$title\"";
+                       }
+               
+                       $link_text = $this->runSpanGamut($link_text);
+                       $result .= ">$link_text</a>";
+                       $result = $this->hashPart($result);
+               }
+               else {
+                       $result = $whole_match;
+               }
+               return $result;
+       }
+       function _doAnchors_inline_callback($matches) {
+               $whole_match    =  $matches[1];
+               $link_text              =  $this->runSpanGamut($matches[2]);
+               $url                    =  $matches[3] == '' ? $matches[4] : $matches[3];
+               $title                  =& $matches[7];
+
+               $url = $this->encodeAttribute($url);
+
+               $result = "<a href=\"$url\"";
+               if (isset($title)) {
+                       $title = $this->encodeAttribute($title);
+                       $result .=  " title=\"$title\"";
+               }
+               
+               $link_text = $this->runSpanGamut($link_text);
+               $result .= ">$link_text</a>";
+
+               return $this->hashPart($result);
+       }
+
+
+       function doImages($text) {
+       #
+       # Turn Markdown image shortcuts into <img> tags.
+       #
+               #
+               # First, handle reference-style labeled images: ![alt text][id]
+               #
+               $text = preg_replace_callback('{
+                       (                               # wrap whole match in $1
+                         !\[
+                               ('.$this->nested_brackets_re.')         # alt text = $2
+                         \]
+
+                         [ ]?                          # one optional space
+                         (?:\n[ ]*)?           # one optional newline followed by spaces
+
+                         \[
+                               (.*?)           # id = $3
+                         \]
+
+                       )
+                       }xs', 
+                       array(&$this, '_doImages_reference_callback'), $text);
+
+               #
+               # Next, handle inline images:  ![alt text](url "optional title")
+               # Don't forget: encode * and _
+               #
+               $text = preg_replace_callback('{
+                       (                               # wrap whole match in $1
+                         !\[
+                               ('.$this->nested_brackets_re.')         # alt text = $2
+                         \]
+                         \s?                   # One optional whitespace character
+                         \(                    # literal paren
+                               [ \n]*
+                               (?:
+                                       <(\S*)> # src url = $3
+                               |
+                                       ('.$this->nested_url_parenthesis_re.')  # src url = $4
+                               )
+                               [ \n]*
+                               (                       # $5
+                                 ([\'"])       # quote char = $6
+                                 (.*?)         # title = $7
+                                 \6            # matching quote
+                                 [ \n]*
+                               )?                      # title is optional
+                         \)
+                       )
+                       }xs',
+                       array(&$this, '_doImages_inline_callback'), $text);
+
+               return $text;
+       }
+       function _doImages_reference_callback($matches) {
+               $whole_match = $matches[1];
+               $alt_text    = $matches[2];
+               $link_id     = strtolower($matches[3]);
+
+               if ($link_id == "") {
+                       $link_id = strtolower($alt_text); # for shortcut links like ![this][].
+               }
+
+               $alt_text = $this->encodeAttribute($alt_text);
+               if (isset($this->urls[$link_id])) {
+                       $url = $this->encodeAttribute($this->urls[$link_id]);
+                       $result = "<img src=\"$url\" alt=\"$alt_text\"";
+                       if (isset($this->titles[$link_id])) {
+                               $title = $this->titles[$link_id];
+                               $title = $this->encodeAttribute($title);
+                               $result .=  " title=\"$title\"";
+                       }
+                       $result .= $this->empty_element_suffix;
+                       $result = $this->hashPart($result);
+               }
+               else {
+                       # If there's no such link ID, leave intact:
+                       $result = $whole_match;
+               }
+
+               return $result;
+       }
+       function _doImages_inline_callback($matches) {
+               $whole_match    = $matches[1];
+               $alt_text               = $matches[2];
+               $url                    = $matches[3] == '' ? $matches[4] : $matches[3];
+               $title                  =& $matches[7];
+
+               $alt_text = $this->encodeAttribute($alt_text);
+               $url = $this->encodeAttribute($url);
+               $result = "<img src=\"$url\" alt=\"$alt_text\"";
+               if (isset($title)) {
+                       $title = $this->encodeAttribute($title);
+                       $result .=  " title=\"$title\""; # $title already quoted
+               }
+               $result .= $this->empty_element_suffix;
+
+               return $this->hashPart($result);
+       }
+
+
+       function doHeaders($text) {
+               # Setext-style headers:
+               #         Header 1
+               #         ========
+               #  
+               #         Header 2
+               #         --------
+               #
+               $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
+                       array(&$this, '_doHeaders_callback_setext'), $text);
+
+               # atx-style headers:
+               #       # Header 1
+               #       ## Header 2
+               #       ## Header 2 with closing hashes ##
+               #       ...
+               #       ###### Header 6
+               #
+               $text = preg_replace_callback('{
+                               ^(\#{1,6})      # $1 = string of #\'s
+                               [ ]*
+                               (.+?)           # $2 = Header text
+                               [ ]*
+                               \#*                     # optional closing #\'s (not counted)
+                               \n+
+                       }xm',
+                       array(&$this, '_doHeaders_callback_atx'), $text);
+
+               return $text;
+       }
+       function _doHeaders_callback_setext($matches) {
+               # Terrible hack to check we haven't found an empty list item.
+               if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
+                       return $matches[0];
+               
+               $level = $matches[2]{0} == '=' ? 1 : 2;
+               $block = "<h$level>".$this->runSpanGamut($matches[1])."</h$level>";
+               return "\n" . $this->hashBlock($block) . "\n\n";
+       }
+       function _doHeaders_callback_atx($matches) {
+               $level = strlen($matches[1]);
+               $block = "<h$level>".$this->runSpanGamut($matches[2])."</h$level>";
+               return "\n" . $this->hashBlock($block) . "\n\n";
+       }
+
+
+       function doLists($text) {
+       #
+       # Form HTML ordered (numbered) and unordered (bulleted) lists.
+       #
+               $less_than_tab = $this->tab_width - 1;
+
+               # Re-usable patterns to match list item bullets and number markers:
+               $marker_ul_re  = '[*+-]';
+               $marker_ol_re  = '\d+[.]';
+               $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
+
+               $markers_relist = array(
+                       $marker_ul_re => $marker_ol_re,
+                       $marker_ol_re => $marker_ul_re,
+                       );
+
+               foreach ($markers_relist as $marker_re => $other_marker_re) {
+                       # Re-usable pattern to match any entirel ul or ol list:
+                       $whole_list_re = '
+                               (                                                               # $1 = whole list
+                                 (                                                             # $2
+                                       ([ ]{0,'.$less_than_tab.'})     # $3 = number of spaces
+                                       ('.$marker_re.')                        # $4 = first list item marker
+                                       [ ]+
+                                 )
+                                 (?s:.+?)
+                                 (                                                             # $5
+                                         \z
+                                       |
+                                         \n{2,}
+                                         (?=\S)
+                                         (?!                                           # Negative lookahead for another list item marker
+                                               [ ]*
+                                               '.$marker_re.'[ ]+
+                                         )
+                                       |
+                                         (?=                                           # Lookahead for another kind of list
+                                           \n
+                                               \3                                              # Must have the same indentation
+                                               '.$other_marker_re.'[ ]+
+                                         )
+                                 )
+                               )
+                       '; // mx
+                       
+                       # We use a different prefix before nested lists than top-level lists.
+                       # See extended comment in _ProcessListItems().
+               
+                       if ($this->list_level) {
+                               $text = preg_replace_callback('{
+                                               ^
+                                               '.$whole_list_re.'
+                                       }mx',
+                                       array(&$this, '_doLists_callback'), $text);
+                       }
+                       else {
+                               $text = preg_replace_callback('{
+                                               (?:(?<=\n)\n|\A\n?) # Must eat the newline
+                                               '.$whole_list_re.'
+                                       }mx',
+                                       array(&$this, '_doLists_callback'), $text);
+                       }
+               }
+
+               return $text;
+       }
+       function _doLists_callback($matches) {
+               # Re-usable patterns to match list item bullets and number markers:
+               $marker_ul_re  = '[*+-]';
+               $marker_ol_re  = '\d+[.]';
+               $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
+               
+               $list = $matches[1];
+               $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol";
+               
+               $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re );
+               
+               $list .= "\n";
+               $result = $this->processListItems($list, $marker_any_re);
+               
+               $result = $this->hashBlock("<$list_type>\n" . $result . "</$list_type>");
+               return "\n". $result ."\n\n";
+       }
+
+       var $list_level = 0;
+
+       function processListItems($list_str, $marker_any_re) {
+       #
+       #       Process the contents of a single ordered or unordered list, splitting it
+       #       into individual list items.
+       #
+               # The $this->list_level global keeps track of when we're inside a list.
+               # Each time we enter a list, we increment it; when we leave a list,
+               # we decrement. If it's zero, we're not in a list anymore.
+               #
+               # We do this because when we're not inside a list, we want to treat
+               # something like this:
+               #
+               #               I recommend upgrading to version
+               #               8. Oops, now this line is treated
+               #               as a sub-list.
+               #
+               # As a single paragraph, despite the fact that the second line starts
+               # with a digit-period-space sequence.
+               #
+               # Whereas when we're inside a list (or sub-list), that line will be
+               # treated as the start of a sub-list. What a kludge, huh? This is
+               # an aspect of Markdown's syntax that's hard to parse perfectly
+               # without resorting to mind-reading. Perhaps the solution is to
+               # change the syntax rules such that sub-lists must start with a
+               # starting cardinal number; e.g. "1." or "a.".
+               
+               $this->list_level++;
+
+               # trim trailing blank lines:
+               $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
+
+               $list_str = preg_replace_callback('{
+                       (\n)?                                                   # leading line = $1
+                       (^[ ]*)                                                 # leading whitespace = $2
+                       ('.$marker_any_re.'                             # list marker and space = $3
+                               (?:[ ]+|(?=\n)) # space only required if item is not empty
+                       )
+                       ((?s:.*?))                                              # list item text   = $4
+                       (?:(\n+(?=\n))|\n)                              # tailing blank line = $5
+                       (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
+                       }xm',
+                       array(&$this, '_processListItems_callback'), $list_str);
+
+               $this->list_level--;
+               return $list_str;
+       }
+       function _processListItems_callback($matches) {
+               $item = $matches[4];
+               $leading_line =& $matches[1];
+               $leading_space =& $matches[2];
+               $marker_space = $matches[3];
+               $tailing_blank_line =& $matches[5];
+
+               if ($leading_line || $tailing_blank_line || 
+                       preg_match('/\n{2,}/', $item))
+               {
+                       # Replace marker with the appropriate whitespace indentation
+                       $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
+                       $item = $this->runBlockGamut($this->outdent($item)."\n");
+               }
+               else {
+                       # Recursion for sub-lists:
+                       $item = $this->doLists($this->outdent($item));
+                       $item = preg_replace('/\n+$/', '', $item);
+                       $item = $this->runSpanGamut($item);
+               }
+
+               return "<li>" . $item . "</li>\n";
+       }
+
+
+       function doCodeBlocks($text) {
+       #
+       #       Process Markdown `<pre><code>` blocks.
+       #
+               $text = preg_replace_callback('{
+                               (?:\n\n|\A\n?)
+                               (                   # $1 = the code block -- one or more lines, starting with a space/tab
+                                 (?>
+                                       [ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
+                                       .*\n+
+                                 )+
+                               )
+                               ((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
+                       }xm',
+                       array(&$this, '_doCodeBlocks_callback'), $text);
+
+               return $text;
+       }
+       function _doCodeBlocks_callback($matches) {
+               $codeblock = $matches[1];
+
+               $codeblock = $this->outdent($codeblock);
+               $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
+
+               # trim leading newlines and trailing newlines
+               $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
+
+               $codeblock = "<pre><code>$codeblock\n</code></pre>";
+               return "\n\n".$this->hashBlock($codeblock)."\n\n";
+       }
+
+
+       function makeCodeSpan($code) {
+       #
+       # Create a code span markup for $code. Called from handleSpanToken.
+       #
+               $code = htmlspecialchars(trim($code), ENT_NOQUOTES);
+               return $this->hashPart("<code>$code</code>");
+       }
+
+
+       var $em_relist = array(
+               ''  => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![.,:;]\s)',
+               '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
+               '_' => '(?<=\S|^)(?<!_)_(?!_)',
+               );
+       var $strong_relist = array(
+               ''   => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![.,:;]\s)',
+               '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
+               '__' => '(?<=\S|^)(?<!_)__(?!_)',
+               );
+       var $em_strong_relist = array(
+               ''    => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![.,:;]\s)',
+               '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
+               '___' => '(?<=\S|^)(?<!_)___(?!_)',
+               );
+       var $em_strong_prepared_relist;
+       
+       function prepareItalicsAndBold() {
+       #
+       # Prepare regular expressions for searching emphasis tokens in any
+       # context.
+       #
+               foreach ($this->em_relist as $em => $em_re) {
+                       foreach ($this->strong_relist as $strong => $strong_re) {
+                               # Construct list of allowed token expressions.
+                               $token_relist = array();
+                               if (isset($this->em_strong_relist["$em$strong"])) {
+                                       $token_relist[] = $this->em_strong_relist["$em$strong"];
+                               }
+                               $token_relist[] = $em_re;
+                               $token_relist[] = $strong_re;
+                               
+                               # Construct master expression from list.
+                               $token_re = '{('. implode('|', $token_relist) .')}';
+                               $this->em_strong_prepared_relist["$em$strong"] = $token_re;
+                       }
+               }
+       }
+       
+       function doItalicsAndBold($text) {
+               $token_stack = array('');
+               $text_stack = array('');
+               $em = '';
+               $strong = '';
+               $tree_char_em = false;
+               
+               while (1) {
+                       #
+                       # Get prepared regular expression for seraching emphasis tokens
+                       # in current context.
+                       #
+                       $token_re = $this->em_strong_prepared_relist["$em$strong"];
+                       
+                       #
+                       # Each loop iteration search for the next emphasis token. 
+                       # Each token is then passed to handleSpanToken.
+                       #
+                       $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
+                       $text_stack[0] .= $parts[0];
+                       $token =& $parts[1];
+                       $text =& $parts[2];
+                       
+                       if (empty($token)) {
+                               # Reached end of text span: empty stack without emitting.
+                               # any more emphasis.
+                               while ($token_stack[0]) {
+                                       $text_stack[1] .= array_shift($token_stack);
+                                       $text_stack[0] .= array_shift($text_stack);
+                               }
+                               break;
+                       }
+                       
+                       $token_len = strlen($token);
+                       if ($tree_char_em) {
+                               # Reached closing marker while inside a three-char emphasis.
+                               if ($token_len == 3) {
+                                       # Three-char closing marker, close em and strong.
+                                       array_shift($token_stack);
+                                       $span = array_shift($text_stack);
+                                       $span = $this->runSpanGamut($span);
+                                       $span = "<strong><em>$span</em></strong>";
+                                       $text_stack[0] .= $this->hashPart($span);
+                                       $em = '';
+                                       $strong = '';
+                               } else {
+                                       # Other closing marker: close one em or strong and
+                                       # change current token state to match the other
+                                       $token_stack[0] = str_repeat($token{0}, 3-$token_len);
+                                       $tag = $token_len == 2 ? "strong" : "em";
+                                       $span = $text_stack[0];
+                                       $span = $this->runSpanGamut($span);
+                                       $span = "<$tag>$span</$tag>";
+                                       $text_stack[0] = $this->hashPart($span);
+                                       $$tag = ''; # $$tag stands for $em or $strong
+                               }
+                               $tree_char_em = false;
+                       } else if ($token_len == 3) {
+                               if ($em) {
+                                       # Reached closing marker for both em and strong.
+                                       # Closing strong marker:
+                                       for ($i = 0; $i < 2; ++$i) {
+                                               $shifted_token = array_shift($token_stack);
+                                               $tag = strlen($shifted_token) == 2 ? "strong" : "em";
+                                               $span = array_shift($text_stack);
+                                               $span = $this->runSpanGamut($span);
+                                               $span = "<$tag>$span</$tag>";
+                                               $text_stack[0] .= $this->hashPart($span);
+                                               $$tag = ''; # $$tag stands for $em or $strong
+                                       }
+                               } else {
+                                       # Reached opening three-char emphasis marker. Push on token 
+                                       # stack; will be handled by the special condition above.
+                                       $em = $token{0};
+                                       $strong = "$em$em";
+                                       array_unshift($token_stack, $token);
+                                       array_unshift($text_stack, '');
+                                       $tree_char_em = true;
+                               }
+                       } else if ($token_len == 2) {
+                               if ($strong) {
+                                       # Unwind any dangling emphasis marker:
+                                       if (strlen($token_stack[0]) == 1) {
+                                               $text_stack[1] .= array_shift($token_stack);
+                                               $text_stack[0] .= array_shift($text_stack);
+                                       }
+                                       # Closing strong marker:
+                                       array_shift($token_stack);
+                                       $span = array_shift($text_stack);
+                                       $span = $this->runSpanGamut($span);
+                                       $span = "<strong>$span</strong>";
+                                       $text_stack[0] .= $this->hashPart($span);
+                                       $strong = '';
+                               } else {
+                                       array_unshift($token_stack, $token);
+                                       array_unshift($text_stack, '');
+                                       $strong = $token;
+                               }
+                       } else {
+                               # Here $token_len == 1
+                               if ($em) {
+                                       if (strlen($token_stack[0]) == 1) {
+                                               # Closing emphasis marker:
+                                               array_shift($token_stack);
+                                               $span = array_shift($text_stack);
+                                               $span = $this->runSpanGamut($span);
+                                               $span = "<em>$span</em>";
+                                               $text_stack[0] .= $this->hashPart($span);
+                                               $em = '';
+                                       } else {
+                                               $text_stack[0] .= $token;
+                                       }
+                               } else {
+                                       array_unshift($token_stack, $token);
+                                       array_unshift($text_stack, '');
+                                       $em = $token;
+                               }
+                       }
+               }
+               return $text_stack[0];
+       }
+
+
+       function doBlockQuotes($text) {
+               $text = preg_replace_callback('/
+                         (                                                             # Wrap whole match in $1
+                               (?>
+                                 ^[ ]*>[ ]?                    # ">" at the start of a line
+                                       .+\n                                    # rest of the first line
+                                 (.+\n)*                                       # subsequent consecutive lines
+                                 \n*                                           # blanks
+                               )+
+                         )
+                       /xm',
+                       array(&$this, '_doBlockQuotes_callback'), $text);
+
+               return $text;
+       }
+       function _doBlockQuotes_callback($matches) {
+               $bq = $matches[1];
+               # trim one level of quoting - trim whitespace-only lines
+               $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
+               $bq = $this->runBlockGamut($bq);                # recurse
+
+               $bq = preg_replace('/^/m', "  ", $bq);
+               # These leading spaces cause problem with <pre> content, 
+               # so we need to fix that:
+               $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx', 
+                       array(&$this, '_doBlockQuotes_callback2'), $bq);
+
+               return "\n". $this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";
+       }
+       function _doBlockQuotes_callback2($matches) {
+               $pre = $matches[1];
+               $pre = preg_replace('/^  /m', '', $pre);
+               return $pre;
+       }
+
+
+       function formParagraphs($text) {
+       #
+       #       Params:
+       #               $text - string to process with html <p> tags
+       #
+               # Strip leading and trailing lines:
+               $text = preg_replace('/\A\n+|\n+\z/', '', $text);
+
+               $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
+
+               #
+               # Wrap <p> tags and unhashify HTML blocks
+               #
+               foreach ($grafs as $key => $value) {
+                       if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
+                               # Is a paragraph.
+                               $value = $this->runSpanGamut($value);
+                               $value = preg_replace('/^([ ]*)/', "<p>", $value);
+                               $value .= "</p>";
+                               $grafs[$key] = $this->unhash($value);
+                       }
+                       else {
+                               # Is a block.
+                               # Modify elements of @grafs in-place...
+                               $graf = $value;
+                               $block = $this->html_hashes[$graf];
+                               $graf = $block;
+//                             if (preg_match('{
+//                                     \A
+//                                     (                                                       # $1 = <div> tag
+//                                       <div  \s+
+//                                       [^>]*
+//                                       \b
+//                                       markdown\s*=\s*  ([\'"])      #       $2 = attr quote char
+//                                       1
+//                                       \2
+//                                       [^>]*
+//                                       >
+//                                     )
+//                                     (                                                       # $3 = contents
+//                                     .*
+//                                     )
+//                                     (</div>)                                        # $4 = closing tag
+//                                     \z
+//                                     }xs', $block, $matches))
+//                             {
+//                                     list(, $div_open, , $div_content, $div_close) = $matches;
+//
+//                                     # We can't call Markdown(), because that resets the hash;
+//                                     # that initialization code should be pulled into its own sub, though.
+//                                     $div_content = $this->hashHTMLBlocks($div_content);
+//                                     
+//                                     # Run document gamut methods on the content.
+//                                     foreach ($this->document_gamut as $method => $priority) {
+//                                             $div_content = $this->$method($div_content);
+//                                     }
+//
+//                                     $div_open = preg_replace(
+//                                             '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open);
+//
+//                                     $graf = $div_open . "\n" . $div_content . "\n" . $div_close;
+//                             }
+                               $grafs[$key] = $graf;
+                       }
+               }
+
+               return implode("\n\n", $grafs);
+       }
+
+
+       function encodeAttribute($text) {
+       #
+       # Encode text for a double-quoted HTML attribute. This function
+       # is *not* suitable for attributes enclosed in single quotes.
+       #
+               $text = $this->encodeAmpsAndAngles($text);
+               $text = str_replace('"', '&quot;', $text);
+               return $text;
+       }
+       
+       
+       function encodeAmpsAndAngles($text) {
+       #
+       # Smart processing for ampersands and angle brackets that need to 
+       # be encoded. Valid character entities are left alone unless the
+       # no-entities mode is set.
+       #
+               if ($this->no_entities) {
+                       $text = str_replace('&', '&amp;', $text);
+               } else {
+                       # Ampersand-encoding based entirely on Nat Irons's Amputator
+                       # MT plugin: <http://bumppo.net/projects/amputator/>
+                       $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', 
+                                                               '&amp;', $text);;
+               }
+               # Encode remaining <'s
+               $text = str_replace('<', '&lt;', $text);
+
+               return $text;
+       }
+
+
+       function doAutoLinks($text) {
+               $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', 
+                       array(&$this, '_doAutoLinks_url_callback'), $text);
+
+               # Email addresses: <address@domain.foo>
+               $text = preg_replace_callback('{
+                       <
+                       (?:mailto:)?
+                       (
+                               (?:
+                                       [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+
+                               |
+                                       ".*?"
+                               )
+                               \@
+                               (?:
+                                       [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
+                               |
+                                       \[[\d.a-fA-F:]+\]       # IPv4 & IPv6
+                               )
+                       )
+                       >
+                       }xi',
+                       array(&$this, '_doAutoLinks_email_callback'), $text);
+
+               return $text;
+       }
+       function _doAutoLinks_url_callback($matches) {
+               $url = $this->encodeAttribute($matches[1]);
+               $link = "<a href=\"$url\">$url</a>";
+               return $this->hashPart($link);
+       }
+       function _doAutoLinks_email_callback($matches) {
+               $address = $matches[1];
+               $link = $this->encodeEmailAddress($address);
+               return $this->hashPart($link);
+       }
+
+
+       function encodeEmailAddress($addr) {
+       #
+       #       Input: an email address, e.g. "foo@example.com"
+       #
+       #       Output: the email address as a mailto link, with each character
+       #               of the address encoded as either a decimal or hex entity, in
+       #               the hopes of foiling most address harvesting spam bots. E.g.:
+       #
+       #         <p><a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
+       #        &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
+       #        &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
+       #        &#101;&#46;&#x63;&#111;&#x6d;</a></p>
+       #
+       #       Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
+       #   With some optimizations by Milian Wolff.
+       #
+               $addr = "mailto:" . $addr;
+               $chars = preg_split('/(?<!^)(?!$)/', $addr);
+               $seed = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed.
+               
+               foreach ($chars as $key => $char) {
+                       $ord = ord($char);
+                       # Ignore non-ascii chars.
+                       if ($ord < 128) {
+                               $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
+                               # roughly 10% raw, 45% hex, 45% dec
+                               # '@' *must* be encoded. I insist.
+                               if ($r > 90 && $char != '@') /* do nothing */;
+                               else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';';
+                               else              $chars[$key] = '&#'.$ord.';';
+                       }
+               }
+               
+               $addr = implode('', $chars);
+               $text = implode('', array_slice($chars, 7)); # text without `mailto:`
+               $addr = "<a href=\"$addr\">$text</a>";
+
+               return $addr;
+       }
+
+
+       function parseSpan($str) {
+       #
+       # Take the string $str and parse it into tokens, hashing embeded HTML,
+       # escaped characters and handling code spans.
+       #
+               $output = '';
+               
+               $span_re = '{
+                               (
+                                       \\\\'.$this->escape_chars_re.'
+                               |
+                                       (?<![`\\\\])
+                                       `+                                              # code span marker
+                       '.( $this->no_markup ? '' : '
+                               |
+                                       <!--    .*?     -->             # comment
+                               |
+                                       <\?.*?\?> | <%.*?%>             # processing instruction
+                               |
+                                       <[/!$]?[-a-zA-Z0-9:_]+  # regular tags
+                                       (?>
+                                               \s
+                                               (?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
+                                       )?
+                                       >
+                       ').'
+                               )
+                               }xs';
+
+               while (1) {
+                       #
+                       # Each loop iteration seach for either the next tag, the next 
+                       # openning code span marker, or the next escaped character. 
+                       # Each token is then passed to handleSpanToken.
+                       #
+                       $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
+                       
+                       # Create token from text preceding tag.
+                       if ($parts[0] != "") {
+                               $output .= $parts[0];
+                       }
+                       
+                       # Check if we reach the end.
+                       if (isset($parts[1])) {
+                               $output .= $this->handleSpanToken($parts[1], $parts[2]);
+                               $str = $parts[2];
+                       }
+                       else {
+                               break;
+                       }
+               }
+               
+               return $output;
+       }
+       
+       
+       function handleSpanToken($token, &$str) {
+       #
+       # Handle $token provided by parseSpan by determining its nature and 
+       # returning the corresponding value that should replace it.
+       #
+               switch ($token{0}) {
+                       case "\\":
+                               return $this->hashPart("&#". ord($token{1}). ";");
+                       case "`":
+                               # Search for end marker in remaining text.
+                               if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', 
+                                       $str, $matches))
+                               {
+                                       $str = $matches[2];
+                                       $codespan = $this->makeCodeSpan($matches[1]);
+                                       return $this->hashPart($codespan);
+                               }
+                               return $token; // return as text since no ending marker found.
+                       default:
+                               return $this->hashPart($token);
+               }
+       }
+
+
+       function outdent($text) {
+       #
+       # Remove one level of line-leading tabs or spaces
+       #
+               return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text);
+       }
+
+
+       # String length function for detab. `_initDetab` will create a function to 
+       # hanlde UTF-8 if the default function does not exist.
+       var $utf8_strlen = 'mb_strlen';
+       
+       function detab($text) {
+       #
+       # Replace tabs with the appropriate amount of space.
+       #
+               # For each line we separate the line in blocks delemited by
+               # tab characters. Then we reconstruct every line by adding the 
+               # appropriate number of space between each blocks.
+               
+               $text = preg_replace_callback('/^.*\t.*$/m',
+                       array(&$this, '_detab_callback'), $text);
+
+               return $text;
+       }
+       function _detab_callback($matches) {
+               $line = $matches[0];
+               $strlen = $this->utf8_strlen; # strlen function for UTF-8.
+               
+               # Split in blocks.
+               $blocks = explode("\t", $line);
+               # Add each blocks to the line.
+               $line = $blocks[0];
+               unset($blocks[0]); # Do not add first block twice.
+               foreach ($blocks as $block) {
+                       # Calculate amount of space, insert spaces, insert block.
+                       $amount = $this->tab_width - 
+                               $strlen($line, 'UTF-8') % $this->tab_width;
+                       $line .= str_repeat(" ", $amount) . $block;
+               }
+               return $line;
+       }
+       function _initDetab() {
+       #
+       # Check for the availability of the function in the `utf8_strlen` property
+       # (initially `mb_strlen`). If the function is not available, create a 
+       # function that will loosely count the number of UTF-8 characters with a
+       # regular expression.
+       #
+               if (function_exists($this->utf8_strlen)) return;
+               $this->utf8_strlen = create_function('$text', 'return preg_match_all(
+                       "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", 
+                       $text, $m);');
+       }
+
+
+       function unhash($text) {
+       #
+       # Swap back in all the tags hashed by _HashHTMLBlocks.
+       #
+               return preg_replace_callback('/(.)\x1A[0-9]+\1/', 
+                       array(&$this, '_unhash_callback'), $text);
+       }
+       function _unhash_callback($matches) {
+               return $this->html_hashes[$matches[0]];
+       }
+
+}
+
+/*
+
+PHP Markdown
+============
+
+Description
+-----------
+
+This is a PHP translation of the original Markdown formatter written in
+Perl by John Gruber.
+
+Markdown is a text-to-HTML filter; it translates an easy-to-read /
+easy-to-write structured text format into HTML. Markdown's text format
+is most similar to that of plain text email, and supports features such
+as headers, *emphasis*, code blocks, blockquotes, and links.
+
+Markdown's syntax is designed not as a generic markup language, but
+specifically to serve as a front-end to (X)HTML. You can use span-level
+HTML tags anywhere in a Markdown document, and you can use block level
+HTML tags (like <div> and <table> as well).
+
+For more information about Markdown's syntax, see:
+
+<http://daringfireball.net/projects/markdown/>
+
+
+Bugs
+----
+
+To file bug reports please send email to:
+
+<michel.fortin@michelf.com>
+
+Please include with your report: (1) the example input; (2) the output you
+expected; (3) the output Markdown actually produced.
+
+
+Version History
+--------------- 
+
+See the readme file for detailed release notes for this version.
+
+
+Copyright and License
+---------------------
+
+PHP Markdown
+Copyright (c) 2004-2009 Michel Fortin  
+<http://michelf.com/>  
+All rights reserved.
+
+Based on Markdown
+Copyright (c) 2003-2006 John Gruber   
+<http://daringfireball.net/>   
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+*      Redistributions of source code must retain the above copyright notice,
+       this list of conditions and the following disclaimer.
+
+*      Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+
+*      Neither the name "Markdown" nor the names of its contributors may
+       be used to endorse or promote products derived from this software
+       without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as
+is" and any express or implied warranties, including, but not limited
+to, the implied warranties of merchantability and fitness for a
+particular purpose are disclaimed. In no event shall the copyright owner
+or contributors be liable for any direct, indirect, incidental, special,
+exemplary, or consequential damages (including, but not limited to,
+procurement of substitute goods or services; loss of use, data, or
+profits; or business interruption) however caused and on any theory of
+liability, whether in contract, strict liability, or tort (including
+negligence or otherwise) arising in any way out of the use of this
+software, even if advised of the possibility of such damage.
+
+*/
+?>
\ No newline at end of file
diff --git a/lib/output.php b/lib/output.php
new file mode 100644 (file)
index 0000000..e70c913
--- /dev/null
@@ -0,0 +1,153 @@
+<?php
+
+
+function get_template_instance()
+{
+    $t = new Template();
+    $t->template_dir = join(DIRECTORY_SEPARATOR, array(dirname(__FILE__), '..', TEMPLATE_DIR));
+       $t->template_cache_dir = join(DIRECTORY_SEPARATOR, array(dirname(__FILE__), '..', TEMPLATE_DIR, 'cache'));
+//     $t->caching = 0;
+       
+    return $t;
+}
+
+
+function parse_format($format, $default)
+{
+    $types = array('html' => 'text/html',
+                   'text' => 'text/plain',
+                                  'rss'  => 'application/rss+xml',
+                   'atom' => 'application/atom+xml',
+                   'json' => 'text/json',
+                   'js'   => 'application/x-javascript',
+                   'xspf' => 'application/xspf+xml',
+                   'xml'  => 'text/xml',
+                   'jpg'  => 'image/jpeg',
+                   'png'  => 'image/png',
+                   'm3u'  => 'audio/x-mpegurl');
+    $format = empty($format) ? $default : $format;
+    return array($format, $types[$format]);
+}
+
+
+if( !function_exists('parse_ini_string') ) {
+    function parse_ini_string( $string ) {
+        $array = Array();
+        $lines = explode("\n", $string );
+        foreach( $lines as $line ) {
+            $statement = preg_match("/^(?!;)(?P<key>[\w+\.\-]+?)\s*=\s*(?P<value>.+?)\s*$/", $line, $match );
+            if( $statement ) {
+                $key    = $match[ 'key' ];
+                $value    = $match[ 'value' ];
+                # Remove quote
+                if( preg_match( "/^\".*\"$/", $value ) || preg_match( "/^'.*'$/", $value ) ) {
+                    $value = mb_substr( $value, 1, mb_strlen( $value ) - 2 );
+                }
+                $array[ $key ] = $value;
+            }
+        }
+        return $array;
+    }
+}
+
+
+/**
+* @param    int     $seconds    Number of seconds to convert into a human-readable timestamp
+* @return   tring   Human-readable approximate timestamp like "2 hours"
+*/
+function approximate_time($seconds)
+{
+   switch(true)
+   {
+       case abs($seconds) <= 90:
+           return 'moments';
+
+       case abs($seconds) <= 90 * 60:
+           return round(abs($seconds) / 60).' minutes';
+
+       case abs($seconds) <= 36 * 60 * 60:
+           return round(abs($seconds) / (60 * 60)).' hours';
+
+       default:
+           return round(abs($seconds) / (24 * 60 * 60)).' days';
+   }
+}
+    
+
+/**
+* @param    int     $time   Unix timestamp
+* @return   string  Relative time string like "2 hours earlier"
+*/
+function get_relative_time($time)
+{
+   $diff = $time - time();
+   return approximate_time($diff) . ($diff < 0 ? ' ago' : ' from now');
+}
+
+
+function get_rss_feed( $url )
+{
+   $ch = curl_init();
+   curl_setopt($ch, CURLOPT_URL, $url);
+   curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+   $feed = curl_exec($ch);
+   curl_close($ch);
+   
+   $xml = new SimpleXMLElement($feed);
+   return $xml;        
+}
+
+
+function die_with_code($code, $message)
+{
+   header("HTTP/1.1 {$code}");
+   die($message);
+}   
+
+
+function get_base_dir()
+{
+    if(php_sapi_name() == 'cli') return CLI_BASE_DIRECTORY;
+    return rtrim(dirname($_SERVER['SCRIPT_NAME']), DIRECTORY_SEPARATOR);
+}
+
+
+function get_base_href()
+{
+    if(php_sapi_name() == 'cli')    return '';
+    $query_pos = strpos($_SERVER['REQUEST_URI'], '?');
+    return ($query_pos === false) ? $_SERVER['REQUEST_URI']
+                                  : substr($_SERVER['REQUEST_URI'], 0, $query_pos);
+}
+
+
+function get_url_parts()
+{        
+       $parts = explode('/', substr($_SERVER['SCRIPT_URL'], strlen(get_base_dir() . '/')));        
+       return $parts[0] ? $parts : 0;
+}
+
+
+function get_url()
+{
+$path_info = pathinfo($_SERVER['SCRIPT_URL']);
+$path_info['url'] = $_SERVER['SCRIPT_URL'];
+    return $path_info; //substr($_SERVER['SCRIPT_URL'], strlen(get_base_dir() . '/'));
+}
+
+
+function get_domain_name()
+{
+    if(php_sapi_name() == 'cli') return CLI_DOMAIN_NAME;
+    return $_SERVER['SERVER_NAME'];
+}
+
+
+function get_url_domain($url)
+{
+    $parsed = parse_url($url);
+    return $parsed['host'];
+}
+
+
+?>
diff --git a/lib/static.php b/lib/static.php
new file mode 100644 (file)
index 0000000..ea0082e
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+       include 'init.php';
+       
+       list($entries, $total_entries) = get_inline_entries(array('log', 'code'));
+
+       echo '<ol>';
+       foreach ($entries as $e)
+       {
+               echo '<li><a href="'.$e['url'].'">'.$e['title'].' - '.$e['date'].'</a></li>';
+       }
+       echo '</ol>';
+       
+               
+
+
+
+?>
\ No newline at end of file
diff --git a/lib/template.php b/lib/template.php
new file mode 100644 (file)
index 0000000..abbb3b7
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+class Template
+{
+       
+       var $template_dir = 'templates';
+       var $template_cache_dir = 'cache';
+       var $cachr_file = null;
+       var $response_format = 'html';
+       var $caching = 1;
+       
+       var $_tpl_vars = array();
+
+       function __construct()
+       {
+       }
+       
+       
+    function assign($tpl_var, $value = null)
+    {
+        if (is_array($tpl_var)) {
+            foreach ($tpl_var as $key => $val)
+                if ($key != '')
+                    $this->_tpl_vars[$key] = $val;
+        } else {
+            if ($tpl_var != '')
+                $this->_tpl_vars[$tpl_var] = $value;
+        }
+    }
+
+
+       public function include_template ($template, $vars = array())
+       {
+               $this->_tpl_vars = array_merge($this->_tpl_vars, $vars);
+               $this->render($template);
+       }
+       
+
+       public function render( $template )
+       {
+               extract( $this->_tpl_vars );
+               
+               
+               if (is_file( $this->template_dir . DIRECTORY_SEPARATOR . $template ))
+                       include( $this->template_dir . DIRECTORY_SEPARATOR . $template );
+               else
+                       include( $this->template_dir . DIRECTORY_SEPARATOR . 'default.' . $this->response_format . '.tpl');
+                       
+
+       }
+       
+       
+       function ob_file_callback($buffer)
+       {
+//       fwrite($this->cache_file, $buffer);
+       }
+       
+       
+       public function title()
+       {
+               return "";
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/pages/about b/pages/about
new file mode 100644 (file)
index 0000000..bdfa8cd
--- /dev/null
@@ -0,0 +1,3 @@
+title = about
+--
+all about the about
\ No newline at end of file
diff --git a/pages/links b/pages/links
new file mode 100644 (file)
index 0000000..42637b7
--- /dev/null
@@ -0,0 +1,4 @@
+title = links
+--
++ [quilime](http://quilime.com) &rarr;
++ [gabriel dunne](http://gabrieldunne.com) &rarr;
\ No newline at end of file
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644 (file)
index 0000000..a57877b
--- /dev/null
@@ -0,0 +1,9 @@
+
+
+<IfModule mod_rewrite.c>
+RewriteEngine On
+RewriteBase /
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule . index.php [L]
+</IfModule>
diff --git a/public/css/style.css b/public/css/style.css
new file mode 100644 (file)
index 0000000..9488175
--- /dev/null
@@ -0,0 +1,92 @@
+body { margin:80px 100px 50px 40px; }
+html, body, table { font-family: sans-serif; font-size:12px; line-height:1.4em; color:#44d ; }
+
+.nav { position:absolute; z-index:1; top:20px; left:30px;  }
+.nav ul { margin-left:1em; }
+.nav h1 { margin-bottom:50px;}
+
+#head { position:absolute; top: 20px; left: 160px; }
+
+/*selection*/
+::-moz-selection {background: #08f !important; color:#fff;} 
+::selection {background: #08f !important; color:#fff;} 
+
+/*links*/
+a { color:#000; border:0; padding:0.2em 0.1em 0 0;  }
+a img { border:0; }
+a.mute { text-decoration:none !important; }
+a:hover, a.mute:hover { color:#d15; text-decoration:none; }
+
+/*headings*/
+h1, h2, h3, h4, h5, h6 {  font-size:1em; }
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { }
+h1 { margin:0 0 3em 0; }
+h2 { margin:0 0 0.5em 0;  }
+h3 { margin:0 0 0 0; }
+
+ol li { list-style-type:decimal; }
+
+blockquote { font-family:times; background:#000; color:#aaa;
+margin:0; font-size:15px; line-height:1.4em; padding:2em 4em; font-style:italic; line-height:1.45em; max-width:600px;}
+
+#content  p:first-child { margin-top:0; }
+
+p { max-width:720px; }
+
+ul, li { margin:0; padding:0; list-style-type:none; }
+table { margin:0; padding:0; border:0;  }
+table .column { padding-right:100px; min-width:120px; max-width:400px; }
+table .column h2 a { text-decoration:none; }
+table h2, table h3 { margin-bottom:2em;}
+table .video li, table .image li { margin-bottom:2em; }
+table .reader li, table .bookmarks li { margin-bottom:1em; padding-bottom:1em;  border-bottom:1px dotted #ddd; }
+.reader_links li, .bookmark_links li { padding-top:.75em; margin-bottom:.75em; }
+.bookmark_links li a { display:block; }
+.bookmark_links li span { font-style: italic; color:#444; }
+.image li a { background:none;}
+
+table.archive td {padding-right:20px; }
+
+.c_pop { position:absolute; background:#fff; display:none; border:5px outset #000; padding:1em 2em; z-index:5; } 
+
+.func { font-weight:bold; color:#444; }
+
+#home_arrow { position: absolute; top:11px; left:23px; margin-left:250px; text-decoration:none; }
+
+#content { margin-left:120px; min-width:500px; position:absolute; top:20px; padding-bottom:200px; z-index:10;}
+    #content p {  }
+    ul.inline_content {}
+    ul.inline_content li .content { } 
+    ul.inline_content li { margin:0 0 150px 0; }
+    ul.inline_content li h3 { margin-bottom:1em; border-bottom:1px solid #ddd; padding-bottom:4px; }
+    ul.inline_content ol li { margin:0; padding:0; border:0;}
+    ul.thumbnails    { }
+    ul.thumbnails li { display:inline-block; margin: 0px 60px 60px 0px; }
+    ul.thumbnails .thumbnail { width:160px; height:120px; text-align:center; background:#888; }
+    ul.thumbnails a:hover img { 
+        filter:alpha(opacity=75);
+        -moz-opacity:0.75;
+        -khtml-opacity: 0.75;
+        opacity: 0.75;
+    }     
+    ul.thumbnails a { text-decoration:none;  }   
+    ul.thumbnails .title {  }
+    
+    .date { color:#ccc;}    
+    .func { margin-bottom:2em; }
+    .func a { background:#daa;  padding:0.2em 1em 0.02em 1em; font-size:9px; text-transform:uppercase;}
+
+    .text { font-family:serif; font-size:15px; line-height:2.2em;  margin-top:80px;}
+    .indent { text-indent:3em; }
+    
+.entry { margin-bottom:100px; }
+.entry h2 { border-bottom:1px solid #ddd; padding-bottom:4px; }
+.entry h2 .title-date { font-weight:normal; color:#bbb; }
+.entry .metadata { margin-top:20px; font-style:italic; color:#aaa; }
+.entry .metadata .tags li { display:inline-block; margin-right:1em; }
+    
+.caption { font-style:italic; margin-top:5px; color:#444; }
+.more, .home { text-decoration:none; color:#000; font-weight:bold; }
+
+#footer {  margin-top:120px; }    
+#footer .copy a { text-decoration:none; }
diff --git a/public/index.php b/public/index.php
new file mode 100644 (file)
index 0000000..204b88c
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+    require_once '../lib/init.php';
+
+    $url_parts = get_url();
+
+       list($response_format, $response_mime_type) = parse_format($url_parts['extension'], 'html');    
+       
+       $t = get_template_instance();   
+       $t->response_format = $response_format;
+       $t->assign('view', $_GET['v']);
+               
+       # content folder
+       if (is_dir(LOCAL_ROOT . CONTENT_DIR . DIRECTORY_SEPARATOR . $url_parts['url']) && $url_parts['url'] != "/") {
+               list($data, $total) = get_data(array($url_parts['url']));
+               $t->assign('data', $data);              
+       }
+       # single file
+       else if (is_file( LOCAL_ROOT . CONTENT_DIR . $url_parts['dirname'] . DIRECTORY_SEPARATOR . $url_parts['filename'])) {
+               $t->assign('single', true);
+               $t->assign('data', parse_file(LOCAL_ROOT . CONTENT_DIR . $url_parts['dirname'] . DIRECTORY_SEPARATOR . $url_parts['filename']));
+               $template = 'single.'.$response_format.'.tpl';          
+       }
+       # page
+       else if (is_file( LOCAL_ROOT . PAGE_DIR . DIRECTORY_SEPARATOR . $url_parts['filename'] )) {
+               $t->assign('data', parse_file(LOCAL_ROOT . PAGE_DIR . DIRECTORY_SEPARATOR . $url_parts['filename']));
+               $template = 'page.' . $response_format . '.tpl';
+       }
+       # direct template
+       else if (is_file( LOCAL_ROOT . TEMPLATE_DIR . DIRECTORY_SEPARATOR . $url_parts['filename'] .'.'. $response_format . '.tpl')) {
+               $template = $url_parts['filename'] . '.' . $response_format . '.tpl';           
+       }
+       # default (index)
+       else {
+               list($data, $total) = get_data();
+               $t->assign('data', $data);              
+       }
+       
+       
+       # render template
+       $t->assign('total', $total);    
+    header("Content-Type: {$response_mime_type}; charset=UTF-8");
+       $t->render($template);
+?>
diff --git a/scripts/content b/scripts/content
new file mode 100755 (executable)
index 0000000..bbff300
--- /dev/null
@@ -0,0 +1,22 @@
+DATE=$(date +%Y"-"%m"-"%d)
+FOLDER=$1
+
+echo -n "prepend filename with current date? (y/n) "
+read -e USE_DATE
+
+if $USE_DATE
+       then
+       FILE=$DATE_$2   
+else
+       FILE=$2
+fi
+
+if test -d content/$FOLDER
+       then
+       echo "dir already exists..."
+else
+       mkdir content/$FOLDER
+fi
+
+echo -e "title = $2\ndate = $DATE\ndraft = true\n--\n\n" > content/$FOLDER/$FILE
+emacs content/$FOLDER/$FILE
\ No newline at end of file
diff --git a/templates/default.html.tpl b/templates/default.html.tpl
new file mode 100644 (file)
index 0000000..f41a3de
--- /dev/null
@@ -0,0 +1,24 @@
+<html>
+<head>
+    
+    <? $this->include_template('head-inc.html.tpl') ?>
+    
+    <title><?=SITE_TITLE?><?=$this->title();?></title>
+    
+</head>
+<body>
+    
+    <? $this->include_template('nav.html.tpl') ?>
+    
+<div id="content">
+
+       <? foreach($data as $entry): ?>
+       <? $this->include_template('entry.html.tpl', array('data' => $entry)); ?>
+       <? endforeach; ?>       
+       
+</div>
+
+       <? $this->include_template('footer.html.tpl') ?>
+
+</body>
+</html>
diff --git a/templates/default.json.tpl b/templates/default.json.tpl
new file mode 100644 (file)
index 0000000..712719b
--- /dev/null
@@ -0,0 +1,5 @@
+{
+    "entries": <? print json_encode($entries); ?>,
+    "count": <?=$total?>, 
+    "total": <?=$total?>
+}
diff --git a/templates/default.rss.tpl b/templates/default.rss.tpl
new file mode 100644 (file)
index 0000000..21cd06e
--- /dev/null
@@ -0,0 +1,24 @@
+<? echo '<?xml version="1.0" encoding="UTF-8"?>'; ?>
+<rss version="2.0">
+       
+       <channel>
+               
+       <atom:link rel="hub" href="<?=WEB_ROOT?>" xmlns:atom="http://www.w3.org/2005/Atom"/>
+
+       <title><?=SITE_TITLE?></title>
+       <link><?=WEB_ROOT?></link>
+       <description></description>
+
+       <? foreach($data as $entry): ?>
+       <item>
+               <title><?=$entry['title']?></title>
+               <description><?=htmlentities($entry['content']);?></description>
+               <link><?=$entry['url']?></link>
+               <guid><?=$entry['url']?></guid>
+               <pubDate><?=date('r', $entry['timestamp'])?></pubDate>          
+       </item>
+       <? endforeach; ?>               
+
+       </channel>
+       
+</rss>
diff --git a/templates/default.txt.tpl b/templates/default.txt.tpl
new file mode 100644 (file)
index 0000000..bfef63c
--- /dev/null
@@ -0,0 +1,26 @@
+title : <?=SITE_TITLE?><?=$this->title()."\n"; ?>
+author : gabriel dunne
+email : gdunne at quilime dot com
+copyright : 1997 - <?=date('Y');?>
+
+
+== entries ==
+
+<? foreach($data as $entry): ?>
+
+<?=$entry['title'];?> - <?=get_relative_time($entry['timestamp']);?>
+
+================================================================================
+<?=trim(wordwrap(strip_tags($entry['content'], '<img>'), 80, "\n"));?>
+
+----------
+posted <?=get_relative_time($entry['timestamp']);?> (<?=date("Y-m-d", $entry['timestamp']);?>) in <?=$entry['cat'];?>
+
+<? foreach($entry['tags'] as $tag) : ?>#<?=$tag?> <? endforeach; ?>
+
+
+
+
+
+
+<? endforeach; ?>
\ No newline at end of file
diff --git a/templates/entry.html.tpl b/templates/entry.html.tpl
new file mode 100644 (file)
index 0000000..d0f1a4a
--- /dev/null
@@ -0,0 +1,23 @@
+<div class="entry">
+
+       <h2>
+               <a title="posted on <?=$data['date']?>" href="<?=$data['url']?>"><?=$data['title']?></a>
+               <span class="title-date" title="<?=date("F d, Y", $data['timestamp'])?>"><?=get_relative_time($data['timestamp']);?></span>             
+       </h2>
+
+       <div class="content">
+               <?=$data['content']?>
+       </div>
+
+       <? if ($single) :?>
+       <div class="metadata">
+               posted <?=date("F d, Y", $data['timestamp'])?> in <a href="/<?=$data['cat'];?>/"><?=$data['cat'];?></a><br/>
+               <ul class="tags">
+               <? foreach($data['tags'] as $tag) : ?>
+               <li>#<?=$tag?></li>
+               <? endforeach; ?>
+               </ul>
+       </div>
+       <? endif; ?>
+
+</div>
diff --git a/templates/footer.html.tpl b/templates/footer.html.tpl
new file mode 100644 (file)
index 0000000..4ff0285
--- /dev/null
@@ -0,0 +1,2 @@
+
+<!-- footer -->
diff --git a/templates/head-inc.html.tpl b/templates/head-inc.html.tpl
new file mode 100644 (file)
index 0000000..413686a
--- /dev/null
@@ -0,0 +1,4 @@
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel="alternate" type="application/rss+xml" title="RSS" href="<?= get_base_dir(); ?>/.rss" />
+<link rel="stylesheet" href="<?= get_base_dir(); ?>/css/style.css" type="text/css">
diff --git a/templates/nav.html.tpl b/templates/nav.html.tpl
new file mode 100644 (file)
index 0000000..53d8efd
--- /dev/null
@@ -0,0 +1,23 @@
+<ul class="nav">          
+
+       <li><h2><a href="/"><?=SITE_TITLE;?></a></h2></li>
+       
+       <br />
+       
+       <h3>categories</h3>
+       
+       <? $categories = get_content_folders(); ?>
+       <? foreach($categories as $cat) : ?>
+       <li><a href="<?=$cat['url']?>"><?=$cat['title']?></a></li>
+       <? endforeach; ?>
+       
+       <br />
+       
+       <h3>pages</h3>
+       
+       <? $pages = get_pages(); ?>
+       <? foreach($pages as $page) : ?>
+       <li><a href="<?=$page['url']?>"><?=$page['title']?></a></li>
+       <? endforeach; ?>
+
+</ul>  
diff --git a/templates/page.html.tpl b/templates/page.html.tpl
new file mode 100644 (file)
index 0000000..346fdef
--- /dev/null
@@ -0,0 +1,24 @@
+<html>
+<head>
+    
+    <? $this->include_template('head-inc.html.tpl') ?>
+    
+    <title><?=SITE_TITLE?><?=$this->title();?></title>
+    
+</head>
+<body>
+    
+    <? $this->include_template('nav.html.tpl') ?>
+    
+<div id="content">
+       
+       <h1><?=$data['title']?></h1>
+
+       <?=$data['content'];?>
+       
+</div>
+
+       <? $this->include_template('footer.html.tpl') ?>
+
+</body>
+</html>
diff --git a/templates/single.html.tpl b/templates/single.html.tpl
new file mode 100644 (file)
index 0000000..ccb8623
--- /dev/null
@@ -0,0 +1,20 @@
+<html>
+<head>
+    
+    <? $this->include_template('head-inc.html.tpl') ?>
+    
+    <title><?=SITE_TITLE?><?=$this->title(); ?></title>
+    
+</head>
+<body>
+    
+    <? $this->include_template('nav.html.tpl') ?>
+    
+       <div id="content">
+               <? $this->include_template('entry.html.tpl', array('data' => $data)); ?>        
+       </div>
+
+       <? $this->include_template('footer.html.tpl') ?>
+
+</body>
+</html>