├── library ├── .gitkeep ├── you can also │ └── nest directories │ │ └── binary files are OK too.jpg ├── code snippets (expand me!) │ ├── SQL.sql │ ├── CSS.css │ ├── Ruby.rb │ ├── JavaScript.js │ ├── PHP.php │ ├── Python.py │ ├── XML.xml │ ├── Bash.sh │ └── Scheme.scm ├── index.md ├── Sample HTML document.html └── Sample Markdown document.md ├── plugins ├── index.html └── PasteBin.php ├── static ├── index.html ├── css │ ├── index.html │ ├── dark │ │ ├── index.html │ │ ├── codemirror-tomorrow-night-bright.css │ │ └── prettify-dark.css │ ├── custom-styles │ │ ├── .gitkeep │ │ └── github.css │ ├── custom.css │ ├── prettify.css │ ├── codemirror.css │ ├── main_dark.css │ └── main.css ├── fonts │ └── index.html ├── img │ ├── index.html │ ├── logo.png │ ├── noise.png │ ├── favicon.ico │ ├── favicon.png │ ├── screenshot.png │ └── screenshot_dark.png ├── js │ ├── index.html │ └── prettify.js └── webfonts │ ├── fa-regular-400.eot │ ├── fa-regular-400.ttf │ ├── fa-solid-900.eot │ ├── fa-solid-900.ttf │ ├── fa-solid-900.woff │ ├── fa-solid-900.woff2 │ ├── fa-regular-400.woff │ └── fa-regular-400.woff2 ├── views ├── index.html ├── index.php ├── uhoh.php ├── layout.php ├── login.php ├── tree.php └── render.php ├── docker ├── logs │ └── .gitkeep ├── .env.example ├── Dockerfile └── docker-compose.yml ├── renderers ├── index.html ├── HTML.php ├── Markdown.php ├── Markdown │ └── MarkdownInterface.php └── Wikitten │ └── MarkdownExtra.php ├── routing.php ├── .gitignore ├── .htaccess ├── README.md ├── index.php ├── LICENSE ├── CHANGELOG.md ├── config.php.example ├── default.php ├── DEV.md ├── login.php └── wiki.php /library/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docker/logs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /renderers/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/css/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/fonts/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/img/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/js/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/css/dark/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/css/custom-styles/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docker/.env.example: -------------------------------------------------------------------------------- 1 | HTTP_PORT=9000 2 | LIBRARY_FOLDER=../library 3 | -------------------------------------------------------------------------------- /renderers/HTML.php: -------------------------------------------------------------------------------- 1 | 4 |

Welcome to Wikitten!

5 |

6 | You're looking at this page because you haven't created a file yet. 7 |

8 | -------------------------------------------------------------------------------- /routing.php: -------------------------------------------------------------------------------- 1 | 4 | 16 |

Uh oh!

17 |

18 | An error ocurred: 19 |

20 | -------------------------------------------------------------------------------- /library/code snippets (expand me!)/SQL.sql: -------------------------------------------------------------------------------- 1 | -- Just a random table from a project of mine 2 | 3 | SET NAMES utf8; 4 | SET foreign_key_checks = 0; 5 | SET time_zone = 'SYSTEM'; 6 | SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO'; 7 | 8 | DROP TABLE IF EXISTS `files`; 9 | CREATE TABLE `files` ( 10 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 11 | `id_parent` int(10) unsigned NOT NULL, 12 | `mime_type` varchar(255) NOT NULL, 13 | `name` varchar(255) NOT NULL, 14 | `title` varchar(255) NOT NULL DEFAULT 'n/a', 15 | `created` datetime NOT NULL, 16 | `meta` text NOT NULL, 17 | PRIMARY KEY (`id`), 18 | KEY `fk_files_files1` (`id_parent`) 19 | ) ENGINE=MyISAM DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /renderers/Markdown/MarkdownInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2004-2018 Michel Fortin 8 | * @copyright (Original Markdown) 2004-2006 John Gruber 9 | */ 10 | 11 | namespace Michelf; 12 | 13 | /** 14 | * Markdown Parser Interface 15 | */ 16 | interface MarkdownInterface { 17 | /** 18 | * Initialize the parser and return the result of its transform method. 19 | * This will work fine for derived classes too. 20 | * 21 | * @api 22 | * 23 | * @param string $text 24 | * @return string 25 | */ 26 | public static function defaultTransform($text); 27 | 28 | /** 29 | * Main function. Performs some preprocessing on the input text 30 | * and pass it through the document gamut. 31 | * 32 | * @api 33 | * 34 | * @param string $text 35 | * @return string 36 | */ 37 | public function transform($text); 38 | } 39 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | $value) { 10 | if (isset($script_name[$key]) && $script_name[$key] == $value) { 11 | $app_dir[] = $script_name[$key]; 12 | } 13 | } 14 | 15 | define('APP_DIR', rtrim(implode('/', $app_dir), "/")); 16 | 17 | $https = false; 18 | if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") { 19 | $https = true; 20 | } 21 | 22 | define('BASE_URL', "http" . ($https ? "s" : "") . "://" . $_SERVER['HTTP_HOST'] . APP_DIR); 23 | 24 | unset($config_file, $request_uri, $script_name, $app_dir, $https); 25 | 26 | 27 | if (defined('ACCESS_USER') && defined('ACCESS_PASSWORD')) { 28 | require_once __DIR__ . DIRECTORY_SEPARATOR . 'login.php'; 29 | Login::instance()->dispatch(); 30 | } 31 | 32 | require_once __DIR__ . DIRECTORY_SEPARATOR . 'wiki.php'; 33 | 34 | Wiki::instance()->dispatch(); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Victor Stanciu (http://victorstanciu.ro) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /static/css/prettify.css: -------------------------------------------------------------------------------- 1 | .com{color:#93a1a1}.lit{color:#195f91}.clo,.opn,.pun{color:#93a1a1}.fun{color:#dc322f}.atv,.str{color:#D14}.kwd,.prettyprint .tag{color:#1e347b}.atn,.dec,.typ,.var{color:teal}.prettyprint{padding:8px;background-color:#f7f7f9;border:1px solid #e1e1e8}.prettyprint.linenums{-webkit-box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0;-moz-box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0;box-shadow:inset 40px 0 0 #fbfbfc,inset 41px 0 0 #ececf0}ol.linenums li{padding-left:12px;color:#bebec5;line-height:20px;text-shadow:0 1px 0 #fff}.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.clo,.opn,.pun{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.kwd,.tag,.typ{font-weight:700}.str{color:#060}.kwd{color:#006}.com{color:#600;font-style:italic}.typ{color:#404}.lit{color:#044}.clo,.opn,.pun{color:#440}.tag{color:#006}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px}ol.linenums{margin:0 0 0 33px;padding-left:5px}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:decimal}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} -------------------------------------------------------------------------------- /library/code snippets (expand me!)/CSS.css: -------------------------------------------------------------------------------- 1 | /* Some random code from the Bootstrap framework (http://twitter.github.com/bootstrap/) */ 2 | 3 | .control-group.info input:focus, 4 | .control-group.info select:focus, 5 | .control-group.info textarea:focus { 6 | border-color: #2d6987; 7 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; 8 | -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; 9 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; 10 | } 11 | 12 | .control-group.info .input-prepend .add-on, 13 | .control-group.info .input-append .add-on { 14 | color: #3a87ad; 15 | background-color: #d9edf7; 16 | border-color: #3a87ad; 17 | } 18 | 19 | input:focus:invalid, 20 | textarea:focus:invalid, 21 | select:focus:invalid { 22 | color: #b94a48; 23 | border-color: #ee5f5b; 24 | } 25 | 26 | input:focus:invalid:focus, 27 | textarea:focus:invalid:focus, 28 | select:focus:invalid:focus { 29 | border-color: #e9322d; 30 | -webkit-box-shadow: 0 0 6px #f8b9b7; 31 | -moz-box-shadow: 0 0 6px #f8b9b7; 32 | box-shadow: 0 0 6px #f8b9b7; 33 | } 34 | 35 | .form-actions { 36 | padding: 19px 20px 20px; 37 | margin-top: 20px; 38 | margin-bottom: 20px; 39 | background-color: #f5f5f5; 40 | border-top: 1px solid #e5e5e5; 41 | *zoom: 1; 42 | } -------------------------------------------------------------------------------- /library/code snippets (expand me!)/Ruby.rb: -------------------------------------------------------------------------------- 1 | # I snatched this class from the GitLab repository (https://github.com/gitlabhq/gitlabhq/tree/master/app/observers) 2 | 3 | class ActivityObserver < ActiveRecord::Observer 4 | observe :issue, :merge_request, :note, :milestone 5 | 6 | def after_create(record) 7 | event_author_id = record.author_id 8 | 9 | # Skip status notes 10 | if record.kind_of?(Note) && record.note.include?("_Status changed to ") 11 | return true 12 | end 13 | 14 | if event_author_id 15 | Event.create( 16 | project: record.project, 17 | target_id: record.id, 18 | target_type: record.class.name, 19 | action: Event.determine_action(record), 20 | author_id: event_author_id 21 | ) 22 | end 23 | end 24 | 25 | def after_close(record, transition) 26 | Event.create( 27 | project: record.project, 28 | target_id: record.id, 29 | target_type: record.class.name, 30 | action: Event::CLOSED, 31 | author_id: record.author_id_of_changes 32 | ) 33 | end 34 | 35 | def after_reopen(record, transition) 36 | Event.create( 37 | project: record.project, 38 | target_id: record.id, 39 | target_type: record.class.name, 40 | action: Event::REOPENED, 41 | author_id: record.author_id_of_changes 42 | ) 43 | end 44 | end -------------------------------------------------------------------------------- /library/code snippets (expand me!)/JavaScript.js: -------------------------------------------------------------------------------- 1 | // this is a sample snippet taken from the jQuery source: 2 | 3 | jQuery.fn.extend({ 4 | css: function( name, value ) { 5 | return jQuery.access( this, function( elem, name, value ) { 6 | var styles, len, 7 | map = {}, 8 | i = 0; 9 | 10 | if ( jQuery.isArray( name ) ) { 11 | styles = getStyles( elem ); 12 | len = name.length; 13 | 14 | for ( ; i < len; i++ ) { 15 | map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); 16 | } 17 | 18 | return map; 19 | } 20 | 21 | return value !== undefined ? 22 | jQuery.style( elem, name, value ) : 23 | jQuery.css( elem, name ); 24 | }, name, value, arguments.length > 1 ); 25 | }, 26 | show: function() { 27 | return showHide( this, true ); 28 | }, 29 | hide: function() { 30 | return showHide( this ); 31 | }, 32 | toggle: function( state ) { 33 | var bool = typeof state === "boolean"; 34 | 35 | return this.each(function() { 36 | if ( bool ? state : isHidden( this ) ) { 37 | jQuery( this ).show(); 38 | } else { 39 | jQuery( this ).hide(); 40 | } 41 | }); 42 | } 43 | }); -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | So far, nothing. 11 | 12 | ## [1.3.0] 2019-09-11 13 | 14 | - Update markdown rendered library to 1.8.0 15 | - Mode definitions to from index.php to default.php 16 | - Implement custom markdown styles 17 | - Refactor internal markdown links 18 | 19 | ## [1.2.0] 2019-08-27 20 | 21 | - Merge of the [Basic login system](https://github.com/devaneando/Wikitten/pull/90) PR. 22 | 23 | ## [1.1.0] 2019-08-24 24 | 25 | - Major refactor of the docker with upgrade to PHP 7.3. 26 | - Merge of [Bootstrap 4 update](https://github.com/devaneando/Wikitten/pull/93) PR. 27 | 28 | ## [1.0.0] 2019-08-24 29 | 30 | First stable version with the work of [Victor Stanciu](vic.stanciu@gmail.com). 31 | 32 | Transfer of the repository to [Eduardo Fernandes](edu.fernandes.pt@gmail.com) 33 | 34 | [Unreleased]: https://github.com/devaneando/Wikitten/compare/v1.3.0...HEAD 35 | [1.3.0]: https://github.com/devaneando/Wikitten/compare/v1.2.0...v1.3.0 36 | [1.2.0]: https://github.com/devaneando/Wikitten/compare/v1.1.0...v1.2.0 37 | [1.1.0]: https://github.com/devaneando/Wikitten/compare/v1.0.0...v1.1.0 38 | [1.0.0]: https://github.com/devaneando/Wikitten/releases/tag/v1.0.0 39 | -------------------------------------------------------------------------------- /config.php.example: -------------------------------------------------------------------------------- 1 | array(), 'files' => array()); 35 | 36 | $items = scandir($dir); 37 | foreach ($items as $item) { 38 | if (in_array($item, $this->_ignore)) { 39 | continue; 40 | } 41 | 42 | $path = $dir . DIRECTORY_SEPARATOR . $item; 43 | if (is_dir($path)) { 44 | $return['directories'][$item] = $this->_getTree($path); 45 | continue; 46 | } 47 | 48 | $return['files'][$item] = $item; 49 | } 50 | 51 | uksort($return['directories'], "strnatcasecmp"); 52 | uksort($return['files'], "strnatcasecmp"); 53 | 54 | return array_merge($return['directories'], $return['files']); 55 | } -------------------------------------------------------------------------------- /library/code snippets (expand me!)/Python.py: -------------------------------------------------------------------------------- 1 | # This is some code I copied from the bottle.py web framework (http://bottlepy.org) 2 | 3 | import base64, cgi, email.utils, functools, hmac, imp, itertools, mimetypes,\ 4 | os, re, subprocess, sys, tempfile, threading, time, urllib, warnings 5 | 6 | from datetime import date as datedate, datetime, timedelta 7 | from tempfile import TemporaryFile 8 | from traceback import format_exc, print_exc 9 | 10 | try: from json import dumps as json_dumps, loads as json_lds 11 | except ImportError: # pragma: no cover 12 | try: from simplejson import dumps as json_dumps, loads as json_lds 13 | except ImportError: 14 | try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds 15 | except ImportError: 16 | def json_dumps(data): 17 | raise ImportError("JSON support requires Python 2.6 or simplejson.") 18 | json_lds = json_dumps 19 | 20 | # We now try to fix 2.5/2.6/3.1/3.2 incompatibilities. 21 | # It ain't pretty but it works... Sorry for the mess. 22 | 23 | py = sys.version_info 24 | py3k = py >= (3,0,0) 25 | py25 = py < (2,6,0) 26 | py31 = (3,1,0) <= py < (3,2,0) 27 | 28 | # Workaround for the missing "as" keyword in py3k. 29 | def _e(): return sys.exc_info()[1] 30 | 31 | # Workaround for the "print is a keyword/function" Python 2/3 dilemma 32 | # and a fallback for mod_wsgi (resticts stdout/err attribute access) 33 | try: 34 | _stdout, _stderr = sys.stdout.write, sys.stderr.write 35 | except IOError: 36 | _stdout = lambda x: sys.stdout.write(x) 37 | _stderr = lambda x: sys.stderr.write(x) -------------------------------------------------------------------------------- /static/css/dark/codemirror-tomorrow-night-bright.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Name: Tomorrow Night - Bright 4 | Author: Chris Kempson 5 | 6 | Port done by Gerard Braad 7 | 8 | */ 9 | 10 | .cm-s-tomorrow-night-bright.CodeMirror {background: #000000; color: #eaeaea;} 11 | .cm-s-tomorrow-night-bright div.CodeMirror-selected {background: #424242 !important;} 12 | .cm-s-tomorrow-night-bright .CodeMirror-gutters {background: #000000; border-right: 0px;} 13 | .cm-s-tomorrow-night-bright .CodeMirror-guttermarker { color: #e78c45; } 14 | .cm-s-tomorrow-night-bright .CodeMirror-guttermarker-subtle { color: #777; } 15 | .cm-s-tomorrow-night-bright .CodeMirror-linenumber {color: #424242;} 16 | .cm-s-tomorrow-night-bright .CodeMirror-cursor {border-left: 1px solid #6A6A6A !important;} 17 | 18 | .cm-s-tomorrow-night-bright span.cm-comment {color: #d27b53;} 19 | .cm-s-tomorrow-night-bright span.cm-atom {color: #a16a94;} 20 | .cm-s-tomorrow-night-bright span.cm-number {color: #a16a94;} 21 | 22 | .cm-s-tomorrow-night-bright span.cm-property, .cm-s-tomorrow-night-bright span.cm-attribute {color: #99cc99;} 23 | .cm-s-tomorrow-night-bright span.cm-keyword {color: #d54e53;} 24 | .cm-s-tomorrow-night-bright span.cm-string {color: #e7c547;} 25 | 26 | .cm-s-tomorrow-night-bright span.cm-variable {color: #b9ca4a;} 27 | .cm-s-tomorrow-night-bright span.cm-variable-2 {color: #7aa6da;} 28 | .cm-s-tomorrow-night-bright span.cm-def {color: #e78c45;} 29 | .cm-s-tomorrow-night-bright span.cm-bracket {color: #eaeaea;} 30 | .cm-s-tomorrow-night-bright span.cm-tag {color: #d54e53;} 31 | .cm-s-tomorrow-night-bright span.cm-link {color: #a16a94;} 32 | .cm-s-tomorrow-night-bright span.cm-error {background: #d54e53; color: #6A6A6A;} 33 | 34 | .cm-s-tomorrow-night-bright .CodeMirror-activeline-background {background: #2a2a2a !important;} 35 | .cm-s-tomorrow-night-bright .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} 36 | -------------------------------------------------------------------------------- /static/css/dark/prettify-dark.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | .prettyprint { 4 | background: #1d1f21; 5 | font-family: Menlo, 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Consolas, monospace; 6 | font-size: 12px; 7 | line-height: 1.5; 8 | border: 1px solid #ccc; 9 | padding: 10px; 10 | } 11 | 12 | .pln { 13 | color: #c5c8c6; 14 | } 15 | 16 | @media screen { 17 | .str { 18 | color: #b5bd68; 19 | } 20 | 21 | .kwd { 22 | color: #b294bb; 23 | } 24 | 25 | .com { 26 | color: #969896; 27 | } 28 | 29 | .typ { 30 | color: #81a2be; 31 | } 32 | 33 | .lit { 34 | color: #de935f; 35 | } 36 | 37 | .pun { 38 | color: #c5c8c6; 39 | } 40 | 41 | .opn { 42 | color: #c5c8c6; 43 | } 44 | 45 | .clo { 46 | color: #c5c8c6; 47 | } 48 | 49 | .tag { 50 | color: #cc6666; 51 | } 52 | 53 | .atn { 54 | color: #de935f; 55 | } 56 | 57 | .atv { 58 | color: #8abeb7; 59 | } 60 | 61 | .dec { 62 | color: #de935f; 63 | } 64 | 65 | .var { 66 | color: #cc6666; 67 | } 68 | 69 | .fun { 70 | color: #81a2be; 71 | } 72 | } 73 | @media print, projection { 74 | .str { 75 | color: #006600; 76 | } 77 | 78 | .kwd { 79 | color: #006; 80 | font-weight: bold; 81 | } 82 | 83 | .com { 84 | color: #600; 85 | font-style: italic; 86 | } 87 | 88 | .typ { 89 | color: #404; 90 | font-weight: bold; 91 | } 92 | 93 | .lit { 94 | color: #004444; 95 | } 96 | 97 | .pun, .opn, .clo { 98 | color: #444400; 99 | } 100 | 101 | .tag { 102 | color: #006; 103 | font-weight: bold; 104 | } 105 | 106 | .atn { 107 | color: #440044; 108 | } 109 | 110 | .atv { 111 | color: #006600; 112 | } 113 | } 114 | /* Specify class=linenums on a pre to get line numbering */ 115 | ol.linenums { 116 | margin-top: 0; 117 | margin-bottom: 0; 118 | } 119 | 120 | /* IE indents via margin-left */ 121 | li.L0, 122 | li.L1, 123 | li.L2, 124 | li.L3, 125 | li.L4, 126 | li.L5, 127 | li.L6, 128 | li.L7, 129 | li.L8, 130 | li.L9 { 131 | /* */ 132 | } 133 | 134 | /* Alternate shading for lines */ 135 | li.L1, 136 | li.L3, 137 | li.L5, 138 | li.L7, 139 | li.L9 { 140 | /* */ 141 | } 142 | -------------------------------------------------------------------------------- /library/code snippets (expand me!)/XML.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | When a man loves a woman 7 | Percy Sledge 8 | USA 9 | Atlantic 10 | 8.70 11 | 1987 12 | 13 | 14 | Bridge of Spies 15 | T'Pau 16 | UK 17 | Siren 18 | 7.90 19 | 1987 20 | 21 | 22 | Private Dancer 23 | Tina Turner 24 | UK 25 | Capitol 26 | 8.90 27 | 1983 28 | 29 | 30 | Midt om natten 31 | Kim Larsen 32 | EU 33 | Medley 34 | 7.80 35 | 1983 36 | 37 | 38 | Pavarotti Gala Concert 39 | Luciano Pavarotti 40 | UK 41 | DECCA 42 | 9.90 43 | 1991 44 | 45 | 46 | The dock of the bay 47 | Otis Redding 48 | USA 49 | Atlantic 50 | 7.90 51 | 1987 52 | 53 | 54 | Picture book 55 | Simply Red 56 | EU 57 | Elektra 58 | 7.20 59 | 1985 60 | 61 | 62 | Red 63 | The Communards 64 | UK 65 | London 66 | 7.80 67 | 1987 68 | 69 | 70 | Unchain my heart 71 | Joe Cocker 72 | USA 73 | EMI 74 | 8.20 75 | 1987 76 | 77 | 78 | -------------------------------------------------------------------------------- /DEV.md: -------------------------------------------------------------------------------- 1 | # Development notes 2 | 3 | ## Updating the CodeMirror syntax highlighter package for Wikitten 4 | 5 | The code mirror js file needs combining for Wikitten into one file with the dependencies in the right order. 6 | 7 | Here are the instructions on how this has been achieved previously: 8 | 9 | ```shell 10 | ## Clone CodeMirror repo 11 | git clone git@github.com:codemirror/CodeMirror.git 12 | cd CodeMirror 13 | 14 | ## Build lib/codemirror.js 15 | npm install 16 | npm run build 17 | 18 | ## Make a tmp workspace 19 | mkdir -p _all/_files 20 | mkdir -p _all/_deps 21 | 22 | ## Copy in main lib 23 | cd _all 24 | cp ../lib/codemirror.js . 25 | cp ../lib/codemirror.css . 26 | 27 | ## Copy in all modes for syntax highlighting 28 | cd _files 29 | cp ../../mode/*/*.js . 30 | 31 | ## Locate test files 32 | grep -i "MT(" * 33 | 34 | ## Remove test files (watch output above for new test files) 35 | rm -rf gss_test.js less_test.js mscgen_test.js msgenny_test.js scss_test.js test.js xu_test.js 36 | 37 | ## Copy in deps for some of the modes 38 | cd ../_deps/ 39 | cp ../../addon/edit/matchbrackets.js . 40 | cp ../../addon/mode/simple.js . 41 | 42 | ## Combine all addons and modes with main lib file 43 | cat *.js >> ../codemirror.js 44 | cd ../_files 45 | cat *.js >> ../codemirror.js 46 | 47 | ## Copy to Wikitten project 48 | cp ../codemirror.js ../../../Wikitten/static/js/codemirror.min.js 49 | cp ../codemirror.css ../../../Wikitten/static/css/codemirror.css 50 | ``` 51 | 52 | **Please test this new codemirror.min.js before minifying to resolve any new dependencies on addon js files.** 53 | 54 | (look in chrome dev tools console for js errors) 55 | 56 | 57 | 58 | Now there are no errors in the console: 59 | 60 | ![screenshot from 2015-05-29 15 09 21](https://cloud.githubusercontent.com/assets/1327332/7884325/d2f26e70-0614-11e5-9904-eec02e34eb0e.png) 61 | 62 | and syntax highlighting seems to be working for php and other languages, now minify locally and not relying on a web based compiler: 63 | 64 | ```shell 65 | ## Install uglify-js 66 | sudo npm install uglify-js -g 67 | 68 | ## Run the minifier 69 | uglifyjs --compress --mangle --output test.js -- Wikitten/static/js/codemirror.min.js 70 | 71 | ## Copy resulting file into the project 72 | cd Wikitten/static/js 73 | mv codemirror.min.js codemirror.js 74 | cp ../../../test.js codemirror.min.js 75 | ``` 76 | 77 | Test the new minified version to make sure highlighting is still working. -------------------------------------------------------------------------------- /library/code snippets (expand me!)/Bash.sh: -------------------------------------------------------------------------------- 1 | # My own .bashrc file 2 | 3 | # If not running interactively, don't do anything 4 | [ -z "$PS1" ] && return 5 | 6 | alias ls='ls --color=always -F --group-directories-first' 7 | alias lo="ls -lha --color=always -F --group-directories-first | awk '{k=0;for(i=0;i<=8;i++)k+=((substr(\$1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf(\"%0o \",k);print}'" 8 | alias fact="elinks -dump randomfunfacts.com | sed -n '/^| /p' | tr -d \|" 9 | 10 | if [ -f /etc/bashrc ]; then 11 | . /etc/bashrc # --> Read /etc/bashrc, if present. 12 | fi 13 | 14 | shopt -s cdspell # autocorrects cd misspellings 15 | shopt -s checkwinsize # update the value of LINES and COLUMNS after each command if altered 16 | 17 | date 18 | 19 | export PS1="\[\e[1;32m\][\t] \u:\$(pwd)\n$ \[\e[m\]" 20 | 21 | function ask() 22 | { 23 | echo -n "$@" '[y/n] ' ; read ans 24 | case "$ans" in 25 | y*|Y*) return 0 ;; 26 | *) return 1 ;; 27 | esac 28 | } 29 | 30 | function extract() 31 | { 32 | if [ -f $1 ] ; then 33 | case $1 in 34 | *.tar.bz2) tar xvjf $1 ;; 35 | *.tar.gz) tar xvzf $1 ;; 36 | *.bz2) bunzip2 $1 ;; 37 | *.rar) unrar x $1 ;; 38 | *.gz) gunzip $1 ;; 39 | *.tar) tar xvf $1 ;; 40 | *.tbz2) tar xvjf $1 ;; 41 | *.tgz) tar xvzf $1 ;; 42 | *.zip) unzip $1 ;; 43 | *.Z) uncompress $1 ;; 44 | *.7z) 7z x $1 ;; 45 | *) echo "'$1' cannot be extracted via >extract<" ;; 46 | esac 47 | else 48 | echo "'$1' is not a valid file!" 49 | fi 50 | } 51 | 52 | # Creates an archive (*.tar.gz) from given directory. 53 | function maketar() { tar cvzf "${1%%/}.tar.gz" "${1%%/}/"; } 54 | 55 | # Create a ZIP archive of a file or folder. 56 | function makezip() { zip -r "${1%%/}.zip" "$1" ; } 57 | 58 | function my_ps() { ps $@ -u $USER -o pid,%cpu,%mem,bsdtime,command ; } 59 | function pp() { my_ps f | awk '!/awk/ && $0~var' var=${1:-".*"} ; } 60 | function killps() # kill by process name 61 | { 62 | local pid pname sig="-TERM" # default signal 63 | if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then 64 | echo "Usage: killps [-SIGNAL] pattern" 65 | return; 66 | fi 67 | if [ $# = 2 ]; then sig=$1 ; fi 68 | for pid in $(my_ps| awk '!/awk/ && $0~pat { print $1 }' pat=${!#} ) 69 | do 70 | pname=$(my_ps | awk '$1~var { print $5 }' var=$pid ) 71 | if ask "Kill process $pid <$pname> with signal $sig?" 72 | then kill $sig $pid 73 | fi 74 | done 75 | } 76 | 77 | function dataurl() 78 | { 79 | local mimeType=$(file -b --mime-type "$1") 80 | if [[ $mimeType == text/* ]]; then 81 | mimeType="${mimeType};charset=utf-8" 82 | fi 83 | echo "data:${mimeType};base64,$(openssl base64 -in "$1" | tr -d '\n')" 84 | } 85 | -------------------------------------------------------------------------------- /library/code snippets (expand me!)/Scheme.scm: -------------------------------------------------------------------------------- 1 | ; This is a code sample taken from http://cs.gmu.edu/~white/CS363/Scheme/SchemeSamples.html 2 | ; 3 | ; Card program in Scheme 4 | ; 5 | ; 6 | ; each card is represented as a list. For example, 7 | ; the queen of spades is (Q S) 8 | 9 | ; The hand itself is stored in 13 'stacks', from the 10 | ; Ace stack to King. To make this searchable, the 11 | ; name of the stack is stored with the stack. For 12 | ; example (5 (K D) (2 C)) means that the stack associated 13 | ; with 5 has a K of diamonds on top of a two of clubs. 14 | ; 15 | ; So, an entire hand might look something like: 16 | ; ((A (J C) (2 H) (3 S)(4 D)) (2 (5 D) (Q D) (7 H) (2 C)) ... (K ...) ) 17 | ; 18 | 19 | 20 | (define (clockpatience cards) 21 | (let ((hand (deal cards 'K))) 22 | (play (topcard 'K hand) 'K hand 1) 23 | ) 24 | ) 25 | 26 | (define (initialhand) 27 | '( (A)(2)(3)(4)(5)(6)(7)(8)(9)(T)(J)(Q)(K)) 28 | ) 29 | 30 | (define (sample) 31 | '( (T S)(Q C)(8 S)(8 D)(Q H)(2 D)(3 H)(K H)(9 H)(2 H)(T H)(K S)(K C) 32 | (9 D)(J H)(7 H)(J D)(2 S)(Q S)(T D)(2 C)(4 H)(5 H)(A D)(4 D)(5 D) 33 | (6 D)(4 S)(9 S)(5 S)(7 S)(J S)(8 H)(3 D)(8 C)(3 S)(4 C)(6 S)(9 C) 34 | (A S)(7 C)(A H)(6 H)(K D)(J C)(7 D)(A C)(5 C)(T C)(Q D)(6 C)(3 C)) 35 | ) 36 | 37 | 38 | (define (deal cards rank) 39 | (cond ((null? cards) (initialhand) ) 40 | (else (addstack rank (car cards) (deal (cdr cards) (nextrank rank)))) 41 | ) 42 | ) 43 | 44 | (define (nextrank rank) ; this function just facilitates dealing 45 | (cond ((eq? rank 'K) 'Q) 46 | ((eq? rank 'Q) 'J) 47 | ((eq? rank 'J) 'T) 48 | ((eq? rank 'T) '9) 49 | ((eq? rank '9) '8) 50 | ((eq? rank '8) '7) 51 | ((eq? rank '7) '6) 52 | ((eq? rank '6) '5) 53 | ((eq? rank '5) '4) 54 | ((eq? rank '4) '3) 55 | ((eq? rank '3) '2) 56 | ((eq? rank '2) 'A) 57 | ((eq? rank 'A) 'K) 58 | ) 59 | ) 60 | 61 | (define (addstack rank card hand) ; find correct pile and then push 62 | (cond ((eq? rank (car (car hand))) 63 | (cons (pushstack card (car hand)) (cdr hand)) 64 | ) 65 | (else (cons (car hand)(addstack rank card (cdr hand)))) 66 | ) 67 | ) 68 | 69 | (define (pushstack card st) 70 | (cons (car st) (cons card (cdr st))) 71 | ) 72 | 73 | (define (play currentcard rank hand ctr ) 74 | ; the heart of the program. The 'hand' hold 75 | ; the current card distribution as stacks 76 | ; 'currentcard' is the current card we are operating 77 | ; on - we always start with King's pile 78 | (let ((newhand (removecard rank hand))) 79 | (let ((card (topcard (car currentcard) newhand)) ) 80 | (cond ((null? card) (cons currentcard (list ctr))) 81 | (else (play card (car currentcard) newhand (+ ctr 1))) 82 | ) 83 | ) 84 | ) 85 | ) 86 | 87 | (define (topcard rank hand) ; see what the top card on correct pile is 88 | (cond ((eq? rank (car (car hand))) ; or null if the pile is empty 89 | (topstack (car hand)) ) 90 | (else (topcard rank (cdr hand))) 91 | ) 92 | ) 93 | 94 | (define (topstack st) 95 | (cond ((null? (cdr st)) '() ) ; return null if we are out of cards 96 | (else (car (cdr st))) 97 | ) 98 | ) 99 | 100 | (define (removecard rank hand) ;remove card from some stack 101 | (cond ((eq? rank (car (car hand))) 102 | (cons (popstack (car hand)) (cdr hand)) 103 | ) 104 | (else (cons (car hand)(removecard rank (cdr hand)))) 105 | ) 106 | ) 107 | 108 | (define (popstack st) 109 | (cond ( (null? (cdr st)) st ) 110 | (else (cons (car st) (cdr (cdr st)))) 111 | ) 112 | ) 113 | -------------------------------------------------------------------------------- /login.php: -------------------------------------------------------------------------------- 1 | doLogout(); 22 | 23 | header("location: " . BASE_URL); 24 | exit; 25 | } 26 | } 27 | 28 | /** 29 | * Check if the user is logged 30 | * @return boolean 31 | */ 32 | private function isLogged() 33 | { 34 | if ($_SESSION['ACCESS_USER'] !== ACCESS_USER || $_SESSION['ACCESS_PASSWORD'] !== ACCESS_PASSWORD) { 35 | return false; 36 | } 37 | 38 | return true; 39 | } 40 | 41 | /** 42 | * Do the login 43 | * @param string $ip IP address 44 | * @param string $username Username 45 | * @param string $password Password 46 | * @return boolean 47 | */ 48 | private function doLogin($ip, $username, $password) 49 | { 50 | // Check the access to this function, using logs and ip 51 | //--> to be implemented 52 | 53 | // Check credentials 54 | if ($username !== ACCESS_USER || $password !== ACCESS_PASSWORD) { 55 | return false; 56 | } 57 | 58 | $_SESSION['ACCESS_USER'] = $username; 59 | $_SESSION['ACCESS_PASSWORD'] = $password; 60 | 61 | return true; 62 | } 63 | 64 | /** 65 | * Logout from the password protected area 66 | * @return boolean Always true 67 | */ 68 | private function doLogout() 69 | { 70 | $_SESSION['ACCESS_USER'] = ''; 71 | $_SESSION['ACCESS_PASSWORD'] = ''; 72 | 73 | return true; 74 | } 75 | 76 | /** 77 | * Get the IP address of the visitor 78 | * @return string 79 | */ 80 | private function getRealIpAddr() 81 | { 82 | if (!empty($_SERVER['HTTP_CLIENT_IP'])) { //check ip from share internet 83 | return $_SERVER['HTTP_CLIENT_IP']; 84 | } 85 | if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { //to check ip is pass from proxy 86 | return $_SERVER['HTTP_X_FORWARDED_FOR']; 87 | } 88 | 89 | return $_SERVER['REMOTE_ADDR']; 90 | } 91 | 92 | /** 93 | * Render the login page, if the authentication is not performed 94 | */ 95 | public function dispatch() 96 | { 97 | // Stop if user is aready logged in (exception from negative first) 98 | if ($this->isLogged()) { 99 | return true; 100 | } 101 | 102 | $ip = $this->getRealIpAddr(); 103 | 104 | $username = isset($_POST['username']) ? htmlspecialchars($_POST['username']) : null; 105 | $password = isset($_POST['password']) ? htmlspecialchars($_POST['password']) : null; 106 | 107 | if (isset($_POST['login'])) { 108 | if (!$username || !$password) { 109 | $error = 'Please complete both fields.'; 110 | } else { 111 | if (!$this->doLogin($ip, $username, $password)) { 112 | $error = "Wrong password."; 113 | } else { 114 | return true; 115 | } 116 | } 117 | } 118 | 119 | // Show the login layout and stop 120 | $layout = __DIR__ . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'login.php'; 121 | include($layout); 122 | 123 | exit; 124 | } 125 | 126 | /** 127 | * Singleton 128 | * @return Login 129 | */ 130 | public static function instance() 131 | { 132 | static $instance; 133 | if (!($instance instanceof self)) { 134 | $instance = new self(); 135 | } 136 | return $instance; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /views/layout.php: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <?php echo e(APP_NAME) ?> 19 | 20 | <?php echo e($page['title']) ?> - <?php echo e(APP_NAME) ?> 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
60 | 61 | 65 | 66 |
67 |
68 |
69 |
70 | 76 | 77 | Logout 78 |
79 |
80 |
81 |
82 | 83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /views/login.php: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <?php echo e(APP_NAME) ?> 19 | 20 | <?php echo e($page['title']) ?> - <?php echo e(APP_NAME) ?> 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
56 | 57 | 61 | 62 | 63 |
64 |
65 |
66 |
67 |
68 |

69 | 70 |
71 | 72 |
73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | 81 | 82 |
83 | 84 |
85 | 86 |
87 | 88 | 89 | 90 | 91 | 92 |
93 | 94 |
95 | 96 | 97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /static/css/codemirror.css: -------------------------------------------------------------------------------- 1 | .CodeMirror{font-family:monospace;height:300px;color:#000}.CodeMirror-lines{padding:4px 0}.CodeMirror pre{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-animate-fat-cursor{width:auto;border:0;-webkit-animation:blink 1.06s steps(1) infinite;-moz-animation:blink 1.06s steps(1) infinite;animation:blink 1.06s steps(1) infinite;background-color:#7e7}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:-20px;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:blue}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:red}.cm-invalidchar{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0f0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#f22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-30px;margin-right:-30px;padding-bottom:30px;height:100%;outline:none;position:relative}.CodeMirror-sizer{position:relative;border-right:30px solid transparent}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-30px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;overflow:auto}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:none}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line > span::selection,.CodeMirror-line > span > span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line > span::-moz-selection,.CodeMirror-line > span > span::-moz-selection{background:#d7d4f0}.cm-searching{background:#ffa;background:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:none} -------------------------------------------------------------------------------- /static/css/main_dark.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #222; 3 | font-size: 13px; 4 | } 5 | .clear { 6 | clear: both; 7 | font-size: 0; 8 | line-height: 0; 9 | display: block; 10 | } 11 | :focus { 12 | outline: none !important; 13 | } 14 | ::-moz-focus-inner { 15 | border:0 !important; 16 | } 17 | #main > .inner { 18 | padding: 20px 0; 19 | } 20 | #content { 21 | border-radius: 4px; 22 | padding: 3px; 23 | background-color: #222; 24 | } 25 | #content .inner { 26 | background: #282828; 27 | padding: 20px; 28 | border-radius: 4px; 29 | border: 1px solid #333; 30 | } 31 | .CodeMirror { 32 | height: auto; 33 | } 34 | .CodeMirror-scroll { 35 | overflow-y: hidden; 36 | overflow-x: auto; 37 | } 38 | code, pre { 39 | font-family: monospace; 40 | background: #333; 41 | color: #3498db; 42 | } 43 | #render h1:first-child, 44 | #render h2:first-child { 45 | margin-top: 0; 46 | margin-bottom: 20px; 47 | } 48 | #render h3 { 49 | line-height: 34px; 50 | margin-top: 20px; 51 | } 52 | #render table { 53 | width: auto; 54 | } 55 | #render img { 56 | max-width: 100%; 57 | } 58 | #render :last-child { 59 | margin-bottom: 0; 60 | } 61 | form { 62 | margin-bottom: 0; 63 | } 64 | .form-actions { 65 | margin: 20px -20px -20px -20px; 66 | border-radius: 0 0 4px 4px; 67 | text-align: right; 68 | } 69 | #submit-edits { 70 | margin: 10px; 71 | } 72 | #sidebar { 73 | padding: 3px; 74 | background-color: #222; 75 | text-shadow: 0 1px 0 #333; 76 | border-radius: 4px; 77 | margin-bottom: 5px; 78 | } 79 | #sidebar .inner { 80 | padding: 20px; 81 | border: 1px solid #333; 82 | background-color: #282828; 83 | border-radius: 4px; 84 | } 85 | #sidebar h2, .breadcrumbs { 86 | line-height: 20px; 87 | background: #333; 88 | margin: -21px -21px 20px -21px; 89 | padding: 0 20px; 90 | line-height: 30px; 91 | border-radius: 4px 4px 0 0; 92 | } 93 | #sidebar h2 span { 94 | border-radius: 4px 4px 0 0; 95 | background: #282828; 96 | font-size: 15px; 97 | line-height: 18px; 98 | padding: 5px 10px; 99 | } 100 | #sidebar ul { 101 | margin-bottom: 0; 102 | } 103 | #sidebar li ul { 104 | padding-left: 20px; 105 | display: none; 106 | margin-top: 5px; 107 | } 108 | #sidebar li.directory.open > ul { 109 | display: block; 110 | } 111 | #sidebar li { 112 | padding: 5px 0; 113 | } 114 | #sidebar ul li:last-child { 115 | padding-bottom: 0; 116 | } 117 | #sidebar li.directory { 118 | font-weight: bold; 119 | } 120 | #sidebar li.directory > a { 121 | color: #eee; 122 | } 123 | #sidebar li.directory > a:hover { 124 | text-decoration: none; 125 | } 126 | #sidebar li.file { 127 | font-weight: normal; 128 | word-wrap: break-word; 129 | } 130 | #sidebar li li.file { 131 | padding-left: 10px; 132 | word-wrap: break-word; 133 | } 134 | #sidebar li.file.active > a { 135 | font-weight: bold; 136 | } 137 | #sidebar li a, #sidebar li a:hover { 138 | display: block; 139 | text-decoration: none; 140 | } 141 | .breadcrumb { 142 | background: none; 143 | margin-bottom: 0; 144 | padding-top: 0; 145 | padding-bottom: 0; 146 | } 147 | .breadcrumbs li { 148 | color: #ccc; 149 | } 150 | .breadcrumbs a { 151 | color: white; 152 | } 153 | .breadcrumbs a:hover { 154 | text-decoration: none; 155 | text-shadow: 0 0 3px #656565; 156 | } 157 | .breadcrumbs .glyphicon { 158 | font-size: 85%; 159 | margin-right: 3px; 160 | } 161 | .btn-black { 162 | padding: 4px 8px; 163 | margin: 2px 0; 164 | font-size: 85%; 165 | background: #555; 166 | color: #eee; 167 | border-radius: 3px; 168 | } 169 | .btn-black:hover { 170 | background: #333; 171 | color: #eee; 172 | } 173 | .directory-path { 174 | color: #7B848C; 175 | font-weight: normal; 176 | } 177 | 178 | #logo { 179 | position: fixed; 180 | bottom: 20px; 181 | left: -90px; 182 | 183 | color: #444; 184 | } 185 | #logo img { 186 | width: 75px; 187 | } 188 | .bubble { 189 | position: absolute; 190 | 191 | width: 150px; 192 | padding: 10px; 193 | background: #FFFFFF; 194 | border: #7B848C solid 3px; 195 | -webkit-border-radius: 15px; 196 | -moz-border-radius: 15px; 197 | border-radius: 15px; 198 | font-weight: bold; 199 | } 200 | .bubble:after { 201 | content: ""; 202 | position: absolute; 203 | top: 22px; 204 | left: -10px; 205 | border-style: solid; 206 | border-width: 10px 10px 10px 0; 207 | border-color: transparent #FFFFFF; 208 | display: block; 209 | width: 0; 210 | z-index: 1; 211 | } 212 | .bubble:before { 213 | content: ""; 214 | position: absolute; 215 | top: 20px; 216 | left: -15px; 217 | border-style: solid; 218 | border-width: 12px 12px 12px 0; 219 | border-color: transparent #7B848C; 220 | display: block; 221 | width: 0; 222 | z-index: 0; 223 | } 224 | #logo .bubble { 225 | right: -190px; 226 | bottom: -90px; 227 | 228 | -webkit-transition: bottom .2s; 229 | -moz-transition: bottom .2s; 230 | -o-transition: bottom .2s; 231 | transition: bottom .2s; 232 | } 233 | #logo:hover .bubble { 234 | bottom: 40px; 235 | } 236 | #tree-filter-clear-query { 237 | cursor: pointer; 238 | } 239 | #tree { 240 | padding: 0; 241 | } 242 | #tree li { 243 | list-style: none; 244 | } 245 | #tree, 246 | #tree-filter-results { 247 | margin-top: 10px; 248 | } 249 | #tree-filter-results li a:focus:before, 250 | #tree li a:focus:before { 251 | content: "↪"; 252 | float: left; 253 | margin-left: -13px; 254 | font-weight: bold; 255 | color: #333; 256 | } 257 | #tree-filter-clear-query { 258 | color: white; 259 | } 260 | -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #EBEEF1 url(../img/noise.png); 3 | font-size: 13px; 4 | } 5 | .clear { 6 | clear: both; 7 | font-size: 0; 8 | line-height: 0; 9 | display: block; 10 | } 11 | a, a:focus { 12 | color: #0068DE; 13 | text-decoration: none; 14 | } 15 | :focus { 16 | outline: none !important; 17 | } 18 | ::-moz-focus-inner { 19 | border:0 !important; 20 | } 21 | #main > .inner { 22 | padding: 20px 0; 23 | } 24 | #content { 25 | border-radius: 4px; 26 | padding: 3px; 27 | background-color: #C1CFDC; 28 | } 29 | #content .inner { 30 | background: white; 31 | padding: 20px; 32 | border-radius: 4px; 33 | border: 1px solid #BBB; 34 | } 35 | .CodeMirror { 36 | height: auto; 37 | } 38 | .CodeMirror-scroll { 39 | overflow-y: hidden; 40 | overflow-x: auto; 41 | } 42 | code, pre { 43 | font-family: monospace; 44 | } 45 | #render h1:first-child, 46 | #render h2:first-child { 47 | margin-top: 0; 48 | margin-bottom: 20px; 49 | } 50 | #render h3 { 51 | line-height: 34px; 52 | margin-top: 20px; 53 | } 54 | #render table { 55 | width: auto; 56 | } 57 | #render img { 58 | max-width: 100%; 59 | } 60 | #render :last-child { 61 | margin-bottom: 0; 62 | } 63 | form { 64 | margin-bottom: 0; 65 | } 66 | .form-actions { 67 | margin: 20px -20px -20px -20px; 68 | border-radius: 0 0 4px 4px; 69 | text-align: right; 70 | } 71 | #submit-edits { 72 | margin: 10px; 73 | } 74 | #sidebar { 75 | padding: 3px; 76 | background-color: #C1CFDC; 77 | text-shadow: 0 1px 0 #FFFFFF; 78 | border-radius: 4px; 79 | margin-bottom: 5px; 80 | } 81 | #sidebar .inner { 82 | padding: 20px; 83 | border: 1px solid #BBB; 84 | background-color: #EFF2F4; 85 | border-radius: 4px; 86 | } 87 | #sidebar h2, .breadcrumbs { 88 | line-height: 20px; 89 | background: #7B848C; 90 | margin: -21px -21px 20px -21px; 91 | padding: 0 20px; 92 | line-height: 30px; 93 | border: 1px solid #666; 94 | border-radius: 4px 4px 0 0; 95 | } 96 | #sidebar h2 span { 97 | border-radius: 4px 4px 0 0; 98 | background: #EFF2F4; 99 | font-size: 15px; 100 | line-height: 18px; 101 | padding: 5px 10px; 102 | } 103 | #sidebar ul { 104 | margin-bottom: 0; 105 | } 106 | #sidebar li ul { 107 | padding-left: 20px; 108 | display: none; 109 | margin-top: 5px; 110 | } 111 | #sidebar li.directory.open > ul { 112 | display: block; 113 | } 114 | #sidebar li { 115 | padding: 5px 0; 116 | } 117 | #sidebar ul li:last-child { 118 | padding-bottom: 0; 119 | } 120 | #sidebar li.directory { 121 | font-weight: bold; 122 | } 123 | #sidebar li.directory > a { 124 | color: #333; 125 | } 126 | #sidebar li.directory > a:hover { 127 | text-decoration: none; 128 | } 129 | #sidebar li.file { 130 | font-weight: normal; 131 | word-wrap: break-word; 132 | } 133 | #sidebar li li.file { 134 | padding-left: 10px; 135 | word-wrap: break-word; 136 | } 137 | #sidebar li.file.active > a { 138 | font-weight: bold; 139 | } 140 | #sidebar li a, #sidebar li a:hover { 141 | display: block; 142 | text-decoration: none; 143 | } 144 | .breadcrumb { 145 | background: none; 146 | margin-bottom: 0; 147 | padding-top: 0; 148 | padding-bottom: 0; 149 | } 150 | .breadcrumbs li { 151 | color: white; 152 | } 153 | .breadcrumbs a { 154 | color: white; 155 | } 156 | .breadcrumbs a:hover { 157 | text-decoration: none; 158 | text-shadow: 0 0 3px #656565; 159 | } 160 | .breadcrumbs .glyphicon { 161 | font-size: 85%; 162 | margin-right: 3px; 163 | } 164 | 165 | .breadcrumb-item + .breadcrumb-item::before { 166 | color: white; 167 | } 168 | 169 | .btn-black { 170 | padding: 4px 8px; 171 | margin: 2px 0; 172 | font-size: 85%; 173 | background: #555; 174 | color: #eee; 175 | border-radius: 3px; 176 | } 177 | .btn-black:hover { 178 | background: #333; 179 | color: #eee; 180 | } 181 | .directory-path { 182 | color: #7B848C; 183 | font-weight: normal; 184 | } 185 | 186 | #logo { 187 | position: fixed; 188 | bottom: 20px; 189 | left: -90px; 190 | 191 | color: #444; 192 | } 193 | #logo img { 194 | width: 75px; 195 | } 196 | .bubble { 197 | position: absolute; 198 | 199 | width: 150px; 200 | padding: 10px; 201 | background: #FFFFFF; 202 | border: #7B848C solid 3px; 203 | -webkit-border-radius: 15px; 204 | -moz-border-radius: 15px; 205 | border-radius: 15px; 206 | font-weight: bold; 207 | } 208 | .bubble:after { 209 | content: ""; 210 | position: absolute; 211 | top: 22px; 212 | left: -10px; 213 | border-style: solid; 214 | border-width: 10px 10px 10px 0; 215 | border-color: transparent #FFFFFF; 216 | display: block; 217 | width: 0; 218 | z-index: 1; 219 | } 220 | .bubble:before { 221 | content: ""; 222 | position: absolute; 223 | top: 20px; 224 | left: -15px; 225 | border-style: solid; 226 | border-width: 12px 12px 12px 0; 227 | border-color: transparent #7B848C; 228 | display: block; 229 | width: 0; 230 | z-index: 0; 231 | } 232 | #logo .bubble { 233 | right: -190px; 234 | bottom: -90px; 235 | 236 | -webkit-transition: bottom .2s; 237 | -moz-transition: bottom .2s; 238 | -o-transition: bottom .2s; 239 | transition: bottom .2s; 240 | } 241 | #logo:hover .bubble { 242 | bottom: 40px; 243 | } 244 | #tree-filter-clear-query { 245 | cursor: pointer; 246 | } 247 | #tree { 248 | padding: 0; 249 | } 250 | #tree li { 251 | list-style: none; 252 | } 253 | #tree, 254 | #tree-filter-results { 255 | margin-top: 10px; 256 | } 257 | #tree-filter-results li a:focus:before, 258 | #tree li a:focus:before { 259 | content: "↪"; 260 | float: left; 261 | margin-left: -13px; 262 | font-weight: bold; 263 | color: #333; 264 | } 265 | -------------------------------------------------------------------------------- /views/tree.php: -------------------------------------------------------------------------------- 1 | '; 14 | 15 | foreach ($array as $key => $item) { 16 | if (is_array($item)) { 17 | $open = $step !== false && (isset($parts[$step]) && $key == $parts[$step]); 18 | 19 | $t .= '
  • '; 20 | $t .= '  ' . $key . ''; 21 | $t .= tree($item, "$parent/$key", $parts, $open ? $step + 1 : false); 22 | $t .= '
  • '; 23 | } else { 24 | $selected = (isset($parts[$step]) && $item == $parts[$step]); 25 | $t .= '
  • '.$item.'
  • '; 26 | } 27 | } 28 | 29 | $t .= ''; 30 | 31 | return $t; 32 | } 33 | ?> 34 | 35 |
    36 | 37 |
    38 | 41 |
    42 |
    43 | 44 |
      45 | 46 | _getTree(), BASE_URL, isset($parts) ? $parts : array()); ?> 47 | 48 | 159 | -------------------------------------------------------------------------------- /views/render.php: -------------------------------------------------------------------------------- 1 | 4 | 43 | 44 | 45 | 46 | 47 | 48 |
      49 | 50 |
      51 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
      67 | 68 |
      69 | Editing is enabled. Use the "Save changes" button below the editor to commit modifications to this file. 70 |
      71 | 72 | 73 |
      "> 74 | 75 | 76 | 77 | 78 |
      79 | 80 |
      81 | 82 |
      83 |
      84 | 85 | 170 | 171 | -------------------------------------------------------------------------------- /library/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | "tags": ["wiki", "wikitten", "personal wiki"], 3 | "author": "Victor Stanciu", 4 | "maintainer": "Eduardo Fernandes" 5 | --- 6 | 7 | # Hello there! Welcome to your personal wiki! 8 | 9 | `Wikitten` is a small, fast, PHP wiki that [I][1] made because I really needed a place to store my notes, snippets, ideas, and so on. I've tried a lot of personal wikis and note-taking applications in the past, but since I have peculiar needs, none of them really suited me, so I rolled my own. 10 | 11 | [1]: https://github.com/devaneando/wikitten 12 | 13 | The page you are looking at right now is part of the actual wiki, and is written using the [Markdown](http://daringfireball.net/projects/markdown/syntax) syntax. If you're not familiar with Markdown go ahead, press the `Toggle source` button in the upper right corner, or check out the [sample document](Sample%20Markdown%20document.md) in the sidebar. By the way, if you're reading the source, noticed how I linked to another page within the wiki? 14 | 15 | Now, there are other Markdown-powered wikis out there, and I've tried some of them, but I wanted something that I could use to store my code snippets too, so `syntax highlighting` was a must. Expand the `Code snippets` folder in the sidebar and take a look at some of the supported file types. I also needed something light enough that I could sync on Dropbox, because I access my notes and snippets on multiple machines. 16 | 17 | ## Requirements 18 | 19 | - PHP `5.3+` 20 | - The Apache webserver (with `mod_rewrite`) 21 | 22 | or 23 | 24 | - PHP `5.4` 25 | - Inbuilt webserver `php -S 0.0.0.0:8000 routing.php` 26 | 27 | or 28 | 29 | - PHP `7.0+` 30 | - Inbuilt webserver `php -S 0.0.0.0:8000 routing.php` 31 | 32 | ### Installation 33 | 34 | - [Download](https://github.com/devaneando/Wikitten/archive/master.zip) the latest version or clone the [repository on GitHub](https://github.com/devaneando/Wikitten) 35 | - After extracting the archive, drop the files somewhere in your DocumentRoot, or make a separate Apache [VirtualHost](http://httpd.apache.org/docs/2.2/mod/core.html#virtualhost). 36 | - That's it. There's a `library` directory in the installation folder. Everything you place in there will be rendered by the wiki. If there's an `index.md` file (such as the one you are reading now) in that folder, it will be served by default when accessing the wiki. 37 | 38 | ### Docker 39 | 40 | You can also run the wiki using [Docker](https://github.com/devaneando/Wikitten/wiki/Docker-instructions) 41 | 42 | ### Configure Wikitten 43 | 44 | You are able to configure Wikitten by using the config file. 45 | First, copy the `config.php.example` to `config.php` and you are ready to change the settings. 46 | Some options are disabled with a comment but can be enabled by removing `//` from the option line. 47 | 48 | - `define('APP_NAME', 'My Wiki');` - Set the Wiki title 49 | - `define('DEFAULT_FILE', 'index.md');` - Choose the file that should be loaded as the homepage, must be located in library folder 50 | - `define('LIBRARY', '/path/to/wiki/library');` - Set a custom path to the library 51 | - `define('ENABLE_EDITING', true);` - Enable the in-page editing of any files 52 | - `define('USE_PAGE_METADATA', true);` - Enable the JSON Front Matter (meta data), see below for more details 53 | - `define('USE_DARK_THEME', true);` - Enable the dark theme (see below for a screenshot) 54 | - `define('USE_WIKITTEN_LOGO', false);` - Disable the Wikitten logo on the left bottom 55 | - `define('ACCESS_USER', 'Wikitten');` - Will required to log in with to view the documents 56 | - `define('ACCESS_PASSWORD', 'Wikitten');` - Will required to log in with to view the documents 57 | - `define('EXTERNAL_LINK_TARGET', '_blank');` - Will append `target="_blank"` to all external links in markdown documents. If you want to disable this, just change the value to `self`. 58 | - `define('INTERNAL_WIKI_LINK', true);`- Will change markdown links to behave like a wiki so if you click a link to another markdown document, it will open in Wikitten in its real path. 59 | 60 | ### JSON Front Matter (meta data) 61 | 62 | Wikitten content can also be tagged using a simple but powerful JSON Front Matter system, akin to [Jekyll's YAML Front Matter](https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter). Defining a custom title, tags, or other 63 | relevant data for a specific page is just a matter of adding a special header at the start of your files, like so: 64 | 65 | --- 66 | "title": "My Custom Page Title", 67 | "tags": ["my", "custom", "tags"], 68 | "author": "Bob" 69 | --- 70 | 71 | # Hello, world! 72 | 73 | This is my cool wiki page. 74 | 75 | Wikitten will intelligently grab this data, and use it for things like meta keywords, the 76 | page title, and maybe eventually search indexing. All the information provided in this 77 | header is passed as-is to the views, so future components and plugins may also make use of it. 78 | 79 | **Note:** The JSON header is expected to be a JSON hash, but to simplify things, Wikitten lets you leave out the starting an ending `{ }` brackets if you want. Everything else in the JSON syntax still applies: 80 | 81 | - Strings (i.e: `title` must be written within double quotes: `"title"`) 82 | - Values must be seperated with a comma character, even if its the only value in a line. 83 | 84 | ### Dark Theme 85 | 86 | If you are working until midnight it can be a pain to look at bright white backgrounds. That's why Wikitten offers a Dark Theme which can be enabled in the config.php file with the `define('USE_DARK_THEME', true);` option. 87 | 88 | It looks like this: 89 | ![Screenshot Dark Mode](static/img/screenshot_dark.png) 90 | 91 | ### Customize CSS 92 | 93 | You can add a new Markdown Style by creating your custom file in the `static/css/custom-styles/` folder. 94 | 95 | The css must address to the `#render` element as shown in the `static/css/custom-styles/github.css` file. 96 | 97 | You can use the `static/css/custom-styles/github.css` file as basis to create a new customized one. 98 | 99 | After having your custom file, edit the `config.php` file and define your custom file name in `CUSTOM_MARKDOWN_STYLESHEET`. 100 | 101 | ```php 102 | // Enable a custom stylesheet 103 | define('CUSTOM_MARKDOWN_STYLESHEET', 'github.css'); 104 | ``` 105 | 106 | **Attention!!!** Do not use the full path to the file. Use only the filename. 107 | 108 | ### Roadmap 109 | 110 | Some of the features I plan to implement next: 111 | 112 | - [Pastebin](http://pastebin.com/) API integration. I think it would be cool to share snippets on Pastebin (or a similar service) with a single click 113 | - Creating / updating files directly through the web interface. Other wikis place great accent on creating and editing pages in the browser, but since I have my trusty code editor open non-stop anyway, I prefer to update my files manually for now. 114 | - Search in files 115 | 116 | ### Special thanks go to: 117 | 118 | - [Michel Fortin](http://michelf.ca/home/), for the [PHP Markdown parser](http://michelf.ca/projects/php-markdown/). 119 | - [Marijn Haverbeke](http://marijnhaverbeke.nl/), for [CodeMirror](http://codemirror.net/), a JavaScript code editor. 120 | - Twitter, for the [Bootstrap](http://twitter.github.com/bootstrap/) CSS framework. 121 | - All Vectors, for the [free cat vector](http://www.allvectors.com/cats-vector/) silhouette I used in making the logo. 122 | - [Sindre Sorhus](https://sindresorhus.com/) for the Github flavored [Markdown Style](https://github.com/sindresorhus/github-markdown-css). 123 | -------------------------------------------------------------------------------- /library/Sample HTML document.html: -------------------------------------------------------------------------------- 1 |
      This is a sample HTML document. Unlike Markdown documents, HTML is simply displayed as-is, so make sure you don't add tags that would interfere with the HTML of the wiki itself.
      2 | 3 |

      This is 2nd level heading

      4 |

      This is a test paragraph.

      5 |

      This is 3rd level heading

      6 |

      This is a test paragraph.

      7 |

      This is 4th level heading

      8 |

      This is a test paragraph.

      9 |
      This is 5th level heading
      10 |

      This is a test paragraph.

      11 |
      This is 6th level heading
      12 |

      This is a test paragraph.

      13 |

      Basic block level elements

      14 |

      This is a normal paragraph (p element). To add some length to it, let us mention that this page was primarily written for testing the effect of user style sheets. You can use it for various other purposes as well, like just checking how your browser displays various HTML elements.

      15 |

      This is another paragraph. I think it needs to be added that the set of elements tested is not exhaustive in any sense. I have selected those elements for which it can make sense to write user style sheet rules, in my opionion.

      16 |
      This is a div element. Authors may use such elements instead of paragraph markup for various reasons. (End of div.)
      17 |
      18 |

      This is a block quotation containing a single paragraph. Well, not quite, since this is not really quoted text, but I hope you understand the point. After all, this page does not use HTML markup very normally anyway.

      19 |
      20 |

      Lists

      21 |

      This is a paragraph before an unnumbered list (ul). Note that the spacing between a paragraph and a list before or after that is hard to tune in a user style sheet. You can't guess which paragraphs are logically related to a list, e.g. as a "list header".

      22 |
        23 |
      • One.
      • 24 |
      • Two.
      • 25 |
      • Three. Well, probably this list item should be longer. Note that for short items lists look better if they are compactly presented, whereas for long items, it would be better to have more vertical spacing between items.
      • 26 |
      • Four. This is the last item in this list. Let us terminate the list now without making any more fuss about it.
      • 27 |
      28 |

      This is a paragraph before a numbered list (ol). Note that the spacing between a paragraph and a list before or after that is hard to tune in a user style sheet. You can't guess which paragraphs are logically related to a list, e.g. as a "list header".

      29 |
        30 |
      1. One.
      2. 31 |
      3. Two.
      4. 32 |
      5. Three. Well, probably this list item should be longer. Note that if items are short, lists look better if they are compactly presented, whereas for long items, it would be better to have more vertical spacing between items.
      6. 33 |
      7. Four. This is the last item in this list. Let us terminate the list now without making any more fuss about it.
      8. 34 |
      35 |

      This is a paragraph before a definition list (dl). In principle, such a list should consist of terms and associated definitions. But many authors use dl elements for fancy "layout" things. Usually the effect is not too bad, if you design user style sheet rules for dl which are suitable for real definition lists.

      36 |
      recursion
      see recursion
      recursion, indirect
      see indirect recursion
      indirect recursion
      see recursion, indirect
      term
      a word or other expression taken into specific use in a well-defined meaning, which is often defined rather rigorously, even formally, and may differ quite a lot from an everyday meaning
      37 |

      Text-level markup

      38 |
        39 |
      • CSS (an abbreviation; abbr markup used)
      • 40 |
      • radar (an acronym; acronym markup used)
      • 41 |
      • bolded (b markup used - just bolding with unspecified semantics)
      • 42 |
      • big thing (big markup used)
      • 43 |
      • large size (font size=6 markup used)
      • 44 |
      • Courier font (font face=Courier markup used)
      • 45 |
      • red text (font color=red markup used)
      • 46 |
      • Origin of Species (a book title; cite markup used)
      • 47 |
      • a[i] = b[i] + c[i); (computer code; code markup used)
      • 48 |
      • here we have some deleted text (del markup used)
      • 49 |
      • an octet is an entity consisting of eight bits (dfn markup used for the term being defined)
      • 50 |
      • this is very simple (em markup used for emphasizing a word)
      • 51 |
      • Homo sapiens (should appear in italics; i markup used)
      • 52 |
      • here we have some inserted text (ins markup used)
      • 53 |
      • type yes when prompted for an answer (kbd markup used for text indicating keyboard input)
      • 54 |
      • Hello! (q markup used for quotation)
      • 55 |
      • He said: She said Hello! (a quotation inside a quotation)
      • 56 |
      • you may get the message Core dumped at times (samp markup used for sample output)
      • 57 |
      • this is not that important (small markup used)
      • 58 |
      • overstruck (strike markup used; note: s is a nonstandard synonym for strike)
      • 59 |
      • this is highlighted text (strong markup used)
      • 60 |
      • In order to test how subscripts and superscripts (sub and sup markup) work inside running text, we need some dummy text around constructs like x1 and H2O (where subscripts occur). So here is some fill so that you will (hopefully) see whether and how badly the subscripts and superscripts mess up vertical spacing between lines. Now superscripts: Mlle, 1st, and then some mathematical notations: ex, sin2x, and some nested superscripts (exponents) too: ex2 and f(x)g(x)a+b+c (where 2 and a+b+c should appear as exponents of exponents).
      • 61 |
      • text in monospace font (tt markup used)
      • 62 |
      • underlined text (u markup used)
      • 63 |
      • the command cat filename displays the file specified by the filename (var markup used to indicate a word as a variable).
      • 64 |
      65 |

      Some of the elements tested above are typically displayed in a monospace font, often using the same presentation for all of them. This tests whether that is the case on your browser:

      66 |
        67 |
      • This is sample text inside code markup
      • 68 |
      • This is sample text inside kbd markup
      • 69 |
      • This is sample text inside samp markup
      • 70 |
      • This is sample text inside tt markup
      • 71 |
      72 |

      Links

      73 | 77 |

      This is a text paragraph that contains some inline links. Generally, inline links (as opposite to e.g. links lists) are problematic from the usability perspective, but they may have use as “incidental”, less relevant links.

      78 |

      Tables

      79 |

      The first row and the first column contain table header cells (th elements) only; other cells are data cells (td elements), with align="right" attributes:

      80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
      Country Total area Land area
      Denmark43,07042,370
      Finland337,030305,470
      Iceland103,000100,250
      Norway324,220307,860
      Sweden449,964410,928
      -------------------------------------------------------------------------------- /plugins/PasteBin.php: -------------------------------------------------------------------------------- 1 | label array 85 | * 86 | * @var array 87 | */ 88 | public static $pastePrivacyTypes = array( 89 | self::PASTE_PRIVACY_PUBLIC => 'Public', 90 | self::PASTE_PRIVACY_UNLISTED => 'Unlisted', 91 | self::PASTE_PRIVACY_PRIVATE => 'Private', 92 | ); 93 | 94 | /** 95 | * API paste expire date type => label array 96 | * 97 | * @var array 98 | */ 99 | public static $pasteExpireTypes = array( 100 | self::PASTE_EXPIRE_NEVER => 'Never', 101 | self::PASTE_EXPIRE_10M => '10 Minutes', 102 | self::PASTE_EXPIRE_1H => '1 Hour', 103 | self::PASTE_EXPIRE_1D => '1 Day', 104 | self::PASTE_EXPIRE_1W => '1 Week', 105 | self::PASTE_EXPIRE_2W => '2 Weeks', 106 | self::PASTE_EXPIRE_1M => '1 Month', 107 | ); 108 | 109 | /** 110 | * Default options to be used when making cURL requests. 111 | * 112 | * @var array 113 | */ 114 | public static $curlOptions = array( 115 | CURLOPT_CONNECTTIMEOUT => 10, 116 | CURLOPT_RETURNTRANSFER => true, 117 | CURLOPT_TIMEOUT => 60, 118 | CURLOPT_VERBOSE => 1, 119 | CURLOPT_NOBODY => 0, 120 | CURLOPT_ENCODING => 'UTF-8', 121 | ); 122 | 123 | 124 | /** 125 | * Instantiate a new PasteBin object. 126 | * 127 | * @param string $_apiKey 128 | */ 129 | public function __construct($_apiKey = false) 130 | { 131 | if ($_apiKey) { 132 | $this->setApiKey($_apiKey); 133 | } 134 | } 135 | 136 | /** 137 | * Set the API key used to make requests. 138 | * 139 | * @param string $_apiKey 140 | */ 141 | public function setApiKey($_apiKey) 142 | { 143 | $this->apiDevKey = $_apiKey; 144 | } 145 | 146 | /** 147 | * Get the API key. 148 | * 149 | * @return string 150 | */ 151 | public function getApiKey() 152 | { 153 | return $this->apiDevKey; 154 | } 155 | 156 | /** 157 | * Return last call error, whether it was a cURL error or API error. 158 | * If no error exists, false is returned. 159 | * 160 | * @return string|boolean 161 | */ 162 | public function getError() 163 | { 164 | if ($this->curlError) { 165 | return $this->curlError; 166 | } elseif ($this->apiError) { 167 | return $this->apiError; 168 | } 169 | 170 | return false; 171 | } 172 | 173 | /** 174 | * Create a new Paste. If everything is OK the return value will be the 175 | * URL of the newly created Paste. If there was an error when the request 176 | * was sent, the return value will be false. Check the error message if 177 | * this is the case. 178 | * 179 | * @param string $_code 180 | * @param string $_visibility 181 | * @param string $_name 182 | * @param string $_expire 183 | * @param string $_format 184 | * @return string|boolean The URL for the newly created Paste 185 | */ 186 | public function createPaste($_code, $_visibility = false, $_name = false, 187 | $_expire = false, $_format = false) 188 | { 189 | $params = array( 190 | self::API_FIELD_OPTION => self::API_OPTION_CREATE, 191 | self::API_FIELD_DEV_KEY => $this->getApiKey(), 192 | self::API_FIELD_PASTE_CODE => $_code, 193 | self::API_FIELD_PASTE_PRIVATE => $this->_preparePrivateValue($_visibility), 194 | self::API_FIELD_PASTE_NAME => (string)$_name, 195 | self::API_FIELD_PASTE_EXPIRE_DATE => $this->_prepareExpireValue($_expire), 196 | self::API_FIELD_PASTE_FORMAT => $this->_prepareFormatValue($_format), 197 | ); 198 | 199 | return $this->_makePostRequest(self::API_POST_URL, $params); 200 | } 201 | 202 | /** 203 | * Make a cURL request using the POST method. 204 | * 205 | * @param string $_url 206 | * @param array $_params 207 | * @return string|boolean 208 | */ 209 | protected function _makePostRequest($_url, $_params) 210 | { 211 | return $this->_makeRequest($_url, $_params, 'post'); 212 | } 213 | 214 | /** 215 | * Make a cURL request by specifying the url, params and the method. 216 | * 217 | * @param string $_url 218 | * @param array $_params 219 | * @param string $_type 220 | * @return string|boolean 221 | */ 222 | protected function _makeRequest($_url, $_params, $_type = 'get') 223 | { 224 | $ch = $this->_prepareRequest($_url, $_type, $_params); 225 | 226 | $result = curl_exec($ch); 227 | if (!$result) { 228 | $this->curlError = curl_error($ch); 229 | return false; 230 | } 231 | 232 | curl_close($ch); 233 | 234 | return $this->_parseResponse($result); 235 | } 236 | 237 | /** 238 | * Prepare the cURL request based on the http method type. 239 | * 240 | * @param string $_type 241 | * @return cURL handle 242 | */ 243 | protected function _prepareRequest($_url, $_type, $_params = array()) 244 | { 245 | $ch = $this->_initRequest(); 246 | if ($ch) { 247 | curl_setopt($ch, CURLOPT_URL, $_url); 248 | 249 | switch (strtolower($_type)) { 250 | case 'post': 251 | curl_setopt($ch, CURLOPT_POST, 1); 252 | 253 | $postfields = $this->_encodeParams($_params); 254 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields); 255 | break; 256 | 257 | case 'get': 258 | default: 259 | // this is set as default, but just to be sure 260 | curl_setopt($ch, CURLOPT_HTTPGET, 1); 261 | } 262 | } 263 | 264 | return $ch; 265 | } 266 | 267 | /** 268 | * Prepare request params to be sent through cURL. 269 | * 270 | * @param array $_params 271 | * @return string 272 | */ 273 | protected function _encodeParams(array $_params) 274 | { 275 | $data = array(); 276 | foreach ($_params as $label => $value) { 277 | $data[] = $label .'=' . urlencode($value); 278 | } 279 | 280 | return implode('&', $data); 281 | } 282 | 283 | /** 284 | * Test if the privacy value is among valid ones, 285 | * else return the default value. 286 | * 287 | * @param string $_value 288 | * @return string 289 | */ 290 | protected function _preparePrivateValue($_value) 291 | { 292 | if (in_array($_value, array_keys(self::$pastePrivacyTypes))) { 293 | return $_value; 294 | } 295 | 296 | // default unlisted 297 | return self::PASTE_PRIVACY_UNLISTED; 298 | } 299 | 300 | /** 301 | * Test if the expire date value is among the valid ones, 302 | * else return the default value. 303 | * 304 | * @param string $_value 305 | * @return string 306 | */ 307 | protected function _prepareExpireValue($_value) 308 | { 309 | if (in_array($_value, array_keys(self::$pasteExpireTypes))) { 310 | return $_value; 311 | } 312 | 313 | // default 10 minutes 314 | return self::PASTE_EXPIRE_10M; 315 | } 316 | 317 | /** 318 | * Set proper format of the code. 319 | * 320 | * @param string $_value 321 | * @return string 322 | */ 323 | protected function _prepareFormatValue($_value) 324 | { 325 | return $_value ? $_value : 'text'; 326 | } 327 | 328 | /** 329 | * Initiates the cURL request used by other methods to send cURL requests. 330 | * 331 | * @return cURL handle|false 332 | */ 333 | protected function _initRequest() 334 | { 335 | $ch = curl_init(); 336 | curl_setopt_array($ch, self::$curlOptions); 337 | 338 | return $ch; 339 | } 340 | 341 | /** 342 | * Interpret the response received from the API. 343 | * Check if there is an error returned or not. 344 | * 345 | * @param string $_response 346 | * @return boolean|string 347 | */ 348 | protected function _parseResponse($_response) 349 | { 350 | if (preg_match('/Bad API request, (.*)/', $_response, $match)) { 351 | $this->apiError = ucfirst($match[1]); 352 | return false; 353 | } 354 | 355 | return $_response; 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /static/js/prettify.js: -------------------------------------------------------------------------------- 1 | !function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a= 3 | b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;ah[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com", 11 | /^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+ 12 | s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/, 13 | q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d= 14 | c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/, 21 | V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-", 22 | /^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/], 23 | ["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}), 24 | ["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q, 25 | hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]); 26 | p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="
      "+a+"
      ";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1}); 27 | return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;iisExternal($url)) { 68 | return $element; 69 | } 70 | if (false !== strpos($element, 'target="')) { 71 | $element = preg_replace('/target="[^\"]+"/', 'target="' . EXTERNAL_LINK_TARGET . '"', $element); 72 | } else { 73 | $element = str_replace('a href=', 'a target="' . EXTERNAL_LINK_TARGET . '" href=', $element); 74 | } 75 | 76 | return $element; 77 | } 78 | 79 | /** 80 | * Add missing path to a given URI 81 | * 82 | * @param string $url The URL/URI to be fixed 83 | * 84 | * @return string The fixed $url 85 | */ 86 | protected static function fixUri($url) 87 | { 88 | // Nothing to do 89 | if (true !== INTERNAL_WIKI_LINK || true === self::isExternal($url) || '/' === $url[0]) { 90 | return $url; 91 | } 92 | 93 | // Just append current uri to the $url 94 | if (false === strpos($url, '/')) { 95 | return self::getCurrentUriPath() . $url; 96 | } 97 | 98 | // Get the filename, if there is any 99 | $lastDocument = []; 100 | $isFolder = (true === empty(preg_match("/\/[^\/]+$/", $url, $lastDocument))); 101 | $fileName = ltrim(((0 < count($lastDocument))? $lastDocument[0]: ''), '/'); 102 | 103 | // Remove the file part of the $url 104 | $givenUrl = trim($url, '/'); 105 | $givenUrl = (true === empty($givenUrl))? []: explode('/', $givenUrl); 106 | if (false === $isFolder && 0 < count($givenUrl)) { 107 | array_pop($givenUrl); 108 | } 109 | 110 | // Remove the '.' and go up a level for '..' 111 | $parentCount = 0; 112 | for ($i = count($givenUrl)-1; $i >= 0; $i--) { 113 | if (true === empty($givenUrl[$i]) || '.' === trim($givenUrl[$i])) { 114 | unset($givenUrl[$i]); 115 | continue; 116 | } elseif ('..' === trim($givenUrl[$i])) { 117 | $parentCount++; 118 | unset($givenUrl[$i]); 119 | continue; 120 | } 121 | } 122 | 123 | $givenUrl = join('/', $givenUrl); 124 | $givenUrl = true === empty($givenUrl)? '/': $givenUrl; 125 | $givenUrl .= '/' !== $givenUrl[strlen($givenUrl)-1]? '/': ''; 126 | $givenUrl .= trim((('/' !== $givenUrl[0])? '/' . $fileName: $fileName), '/'); 127 | 128 | // If there are no levels to go up, return the fixed URI. 129 | if (0 >= $parentCount) { 130 | return self::getCurrentUriPath() . $givenUrl; 131 | } 132 | 133 | $currentUri = trim(self::getCurrentUriPath(), '/'); 134 | $currentUri = (true === empty($currentUri))? []: explode('/', $currentUri); 135 | // Remove empty slices generated by the explode function 136 | for ($i = count($currentUri)-1; $i >= 0; $i--) { 137 | if (true === empty($currentUri[$i])) { 138 | unset($currentUri[$i]); 139 | } 140 | } 141 | 142 | for ($i = 0; $i < $parentCount; $i++) { 143 | array_pop($currentUri); 144 | } 145 | 146 | $currentUri = join('/', $currentUri); 147 | $currentUri = ('/' !== $currentUri[0])? '/' . $currentUri: $currentUri; 148 | 149 | return rtrim($currentUri, '/') . '/' . trim($givenUrl, '/'); 150 | } 151 | 152 | /** 153 | * Fix bar URI in links generated by markdown conversion 154 | * 155 | * @param string $element The HTML element 156 | * @param string $url The URL/URI to be fixed 157 | * 158 | * @return string The fixed element 159 | */ 160 | protected function fixInternalWikiLinks($element, $url) 161 | { 162 | // Nothing to do 163 | if (true !== INTERNAL_WIKI_LINK) { 164 | return $element; 165 | } 166 | 167 | $url = self::fixUri($url); 168 | $element = preg_replace('/href=".*"/', "href=\"${url}\"", $element); 169 | $element = preg_replace('/src=".*"/', "src=\"${url}\"", $element); 170 | 171 | return $element; 172 | } 173 | 174 | /** 175 | * Fix bar URI in links already existing in paragraphs before markdown conversion 176 | * 177 | * @param string $text The paragraph text 178 | * 179 | * @return string The fixed paragraph 180 | */ 181 | protected function fixParagraphLinks($text) 182 | { 183 | // Nothing to do 184 | if (true !== INTERNAL_WIKI_LINK) { 185 | return $text; 186 | } 187 | if (false === empty(preg_match('/src="([^"]+)+"/', $text, $matches))) { 188 | $matches = []; 189 | $text = preg_replace_callback('/src="([^"]+)+"/i', function($matches) { 190 | $url = 'src="' . $this->fixUri($matches[1]) . '"'; 191 | 192 | return $url; 193 | }, $text); 194 | } 195 | 196 | if (false === empty(preg_match('/href="([^"]+)+"/', $text, $matches))) { 197 | $matches = []; 198 | $text = preg_replace_callback('/href="([^"]+)+"/i', function($matches) { 199 | $url = 'href="' . $this->fixUri($matches[1]) . '"'; 200 | 201 | return $url; 202 | }, $text); 203 | } 204 | 205 | return $text; 206 | } 207 | 208 | /** 209 | * Callback for reference anchors 210 | * @param array $matches 211 | * @return string 212 | */ 213 | protected function _doAnchors_reference_callback($matches) { 214 | $whole_match = $matches[1]; 215 | $link_text = $matches[2]; 216 | $link_id =& $matches[3]; 217 | 218 | if ($link_id == "") { 219 | // for shortcut links like [this][] or [this]. 220 | $link_id = $link_text; 221 | } 222 | 223 | // lower-case and turn embedded newlines into spaces 224 | $link_id = strtolower($link_id); 225 | $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); 226 | 227 | if (isset($this->urls[$link_id])) { 228 | $url = $this->urls[$link_id]; 229 | $url = $this->encodeURLAttribute($url); 230 | 231 | $result = "titles[$link_id] ) ) { 233 | $title = $this->titles[$link_id]; 234 | $title = $this->encodeAttribute($title); 235 | $result .= " title=\"$title\""; 236 | } 237 | if (isset($this->ref_attr[$link_id])) 238 | $result .= $this->ref_attr[$link_id]; 239 | 240 | $link_text = $this->runSpanGamut($link_text); 241 | $result .= ">$link_text"; 242 | $result = $this->addTargetToAnchors($result, $url); 243 | $result = $this->fixInternalWikiLinks($result, $url); 244 | $result = $this->hashPart($result); 245 | } else { 246 | $result = $whole_match; 247 | } 248 | return $result; 249 | } 250 | 251 | /** 252 | * Callback for inline anchors 253 | * @param array $matches 254 | * @return string 255 | */ 256 | protected function _doAnchors_inline_callback($matches) { 257 | $whole_match = $matches[1]; 258 | $link_text = $this->runSpanGamut($matches[2]); 259 | $url = $matches[3] == '' ? $matches[4] : $matches[3]; 260 | $title =& $matches[7]; 261 | $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]); 262 | 263 | // if the URL was of the form it got caught by the HTML 264 | // tag parser and hashed. Need to reverse the process before using the URL. 265 | $unhashed = $this->unhash($url); 266 | if ($unhashed != $url) 267 | $url = preg_replace('/^<(.*)>$/', '\1', $unhashed); 268 | 269 | $url = $this->encodeURLAttribute($url); 270 | 271 | $result = "encodeAttribute($title); 274 | $result .= " title=\"$title\""; 275 | } 276 | $result .= $attr; 277 | 278 | $link_text = $this->runSpanGamut($link_text); 279 | $result .= ">$link_text"; 280 | $result = $this->addTargetToAnchors($result, $url); 281 | $result = $this->fixInternalWikiLinks($result, $url); 282 | 283 | return $this->hashPart($result); 284 | } 285 | 286 | /** 287 | * Callback for referenced images 288 | * @param array $matches 289 | * @return string 290 | */ 291 | protected function _doImages_reference_callback($matches) 292 | { 293 | $whole_match = $matches[1]; 294 | $alt_text = $matches[2]; 295 | $link_id = strtolower($matches[3]); 296 | 297 | if ($link_id == "") { 298 | $link_id = strtolower($alt_text); // for shortcut links like ![this][]. 299 | } 300 | 301 | $alt_text = $this->encodeAttribute($alt_text); 302 | if (isset($this->urls[$link_id])) { 303 | $url = $this->encodeURLAttribute($this->urls[$link_id]); 304 | $result = "\"$alt_text\"";titles[$link_id])) { 306 | $title = $this->titles[$link_id]; 307 | $title = $this->encodeAttribute($title); 308 | $result .= " title=\"$title\""; 309 | } 310 | if (isset($this->ref_attr[$link_id])) 311 | $result .= $this->ref_attr[$link_id]; 312 | $result .= $this->empty_element_suffix; 313 | $result = $this->hashPart($result); 314 | } 315 | else { 316 | // If there's no such link ID, leave intact: 317 | $result = $whole_match; 318 | } 319 | 320 | return $result; 321 | } 322 | 323 | /** 324 | * Callback for inline images 325 | * @param array $matches 326 | * @return string 327 | */ 328 | protected function _doImages_inline_callback($matches) 329 | { 330 | $whole_match = $matches[1]; 331 | $alt_text = $matches[2]; 332 | $url = $matches[3] == '' ? $matches[4] : $matches[3]; 333 | $title =& $matches[7]; 334 | $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]); 335 | 336 | $alt_text = $this->encodeAttribute($alt_text); 337 | $url = $this->encodeURLAttribute($url); 338 | $result = "\"$alt_text\"";encodeAttribute($title); 341 | $result .= " title=\"$title\""; // $title already quoted 342 | } 343 | $result .= $attr; 344 | $result .= $this->empty_element_suffix; 345 | $result = $this->fixInternalWikiLinks($result, $url); 346 | 347 | return $this->hashPart($result); 348 | } 349 | 350 | /** 351 | * Parse text into paragraphs 352 | * @param string $text String to process in paragraphs 353 | * @param boolean $wrap_in_p Whether paragraphs should be wrapped in

      tags 354 | * @return string HTML output 355 | */ 356 | protected function formParagraphs($text, $wrap_in_p = true) { 357 | // Strip leading and trailing lines: 358 | $text = preg_replace('/\A\n+|\n+\z/', '', $text); 359 | $text = $this->fixParagraphLinks($text); 360 | 361 | $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); 362 | 363 | // Wrap

      tags and unhashify HTML blocks 364 | foreach ($grafs as $key => $value) { 365 | $value = trim($this->runSpanGamut($value)); 366 | 367 | // Check if this should be enclosed in a paragraph. 368 | // Clean tag hashes & block tag hashes are left alone. 369 | $is_p = $wrap_in_p && !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value); 370 | 371 | if ($is_p) { 372 | $value = "

      $value

      "; 373 | } 374 | $grafs[$key] = $value; 375 | } 376 | 377 | // Join grafs in one text, then unhash HTML tags. 378 | $text = implode("\n\n", $grafs); 379 | 380 | // Finish by removing any tag hashes still present in $text. 381 | $text = $this->unhash($text); 382 | 383 | return $text; 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /wiki.php: -------------------------------------------------------------------------------- 1 | 'Markdown', 10 | 'markdown' => 'Markdown', 11 | 'mdown' => 'Markdown', 12 | 'htm' => 'HTML', 13 | 'html' => 'HTML' 14 | ); 15 | protected $_ignore = "/^\..*|^CVS$/"; // Match dotfiles and CVS 16 | protected $_force_unignore = false; // always show these files (false to disable) 17 | 18 | protected $_action; 19 | 20 | protected $_default_page_data = array( 21 | 'title' => false, // will use APP_NAME by default 22 | 'description' => 'Wikitten is a small, fast, PHP wiki.', 23 | 'tags' => array('wikitten', 'wiki'), 24 | 'page' => '' 25 | ); 26 | 27 | /** 28 | * @param string $extension 29 | * @return string|callable 30 | */ 31 | protected function _getRenderer($extension) 32 | { 33 | if (!isset($this->_renderers[$extension])) { 34 | return false; 35 | } 36 | 37 | $renderer = $this->_renderers[$extension]; 38 | 39 | require_once __DIR__ . DIRECTORY_SEPARATOR . 'renderers' . DIRECTORY_SEPARATOR . "$renderer.php"; 40 | 41 | return $renderer; 42 | } 43 | 44 | protected function _render($page) 45 | { 46 | $fullPath = LIBRARY . DIRECTORY_SEPARATOR . $page; 47 | $path = realpath(LIBRARY . DIRECTORY_SEPARATOR . $page); 48 | $parts = explode('/', $page); 49 | 50 | $not_found = function () use ($page) { 51 | $page = htmlspecialchars($page, ENT_QUOTES); 52 | throw new Exception("Page '$page' was not found"); 53 | }; 54 | 55 | if (!$this->_pathIsSafe($fullPath)) { 56 | $not_found(); 57 | } 58 | 59 | // Handle directories by showing a neat listing of its 60 | // contents 61 | if (is_dir($path)) { 62 | if (!file_exists($path)) { 63 | $not_found(); 64 | } 65 | 66 | if (file_exists($path . DIRECTORY_SEPARATOR . 'index.md')) { 67 | return $this->_render('index.md'); 68 | } 69 | 70 | // Get a printable version of the actual folder name: 71 | $dir_name = htmlspecialchars(end($parts), ENT_QUOTES, 'UTF-8'); 72 | 73 | // Get a printable version of the rest of the path, 74 | // so that we can display it with a different appearance: 75 | $rest_parts = array_slice($parts, 0, count($parts) - 1); 76 | $rest_parts = htmlspecialchars(join("/", $rest_parts), ENT_QUOTES, 'UTF-8'); 77 | 78 | // Pass this to the render view, cleverly disguised as just 79 | // another page, so we can make use of the tree, breadcrumb, 80 | // etc. 81 | $page_data = $this->_default_page_data; 82 | $page_data['title'] = 'Listing: ' . $dir_name; 83 | 84 | $files = scandir($path); 85 | $list = "

      I'm just an empty folder

      \n"; 86 | if (2 < count($files)) { 87 | $list = "

      I'm a folder and I have

        \n"; 88 | foreach ($files as $file) { 89 | if (preg_match('/^\..*$/', $file)) { 90 | continue; 91 | } 92 | $list .= "
      • ${file}
      • \n"; 93 | } 94 | $list .= "
      \n"; 95 | } 96 | 97 | $this->_view('render', array( 98 | 'parts' => $parts, 99 | 'page' => $page_data, 100 | 'html' => $list, 101 | 'is_dir' => true 102 | )); 103 | return; 104 | } 105 | 106 | if (ENABLE_EDITING) { 107 | $extension = substr($fullPath, strrpos($fullPath, '.') + 1, 20); 108 | if (false === $extension || false === $this->_getRenderer($extension)) { 109 | $not_found(); 110 | } elseif (!file_exists($fullPath)) { 111 | // Pass this to the render view, cleverly disguised as just 112 | // another page, so we can make use of the tree, breadcrumb, 113 | // etc. 114 | $_page = htmlspecialchars($page, ENT_QUOTES); 115 | $page_data = $this->_default_page_data; 116 | $page_data['title'] = 'Page not found: ' . $_page; 117 | 118 | return $this->_view('render', array( 119 | 'parts' => $parts, 120 | 'page' => $page_data, 121 | 'html' => 122 | "

      Page '$_page' not found

      " 123 | . "
      " 124 | . "
      " 125 | . "" 126 | . "" 127 | . "
      " 128 | , 129 | 'is_dir' => false 130 | )); 131 | } 132 | } else { 133 | $not_found(); 134 | } 135 | 136 | $finfo = finfo_open(FILEINFO_MIME); 137 | $mime_type = trim(finfo_file($finfo, $path)); 138 | if (substr($mime_type, 0, strlen('text/plain')) != 'text/plain' 139 | && substr($mime_type, 0, strlen('inode/x-empty')) != 'inode/x-empty' 140 | ) { 141 | // not an ASCII file, send it directly to the browser 142 | $file = fopen($path, 'rb'); 143 | 144 | header("Content-Type: $mime_type"); 145 | header("Content-Length: " . filesize($path)); 146 | 147 | fpassthru($file); 148 | exit(); 149 | } 150 | 151 | $source = file_get_contents($path); 152 | $extension = pathinfo($path, PATHINFO_EXTENSION); 153 | $renderer = $this->_getRenderer($extension); 154 | $page_data = $this->_default_page_data; 155 | 156 | // Extract the JSON header, if the feature is enabled: 157 | if (USE_PAGE_METADATA) { 158 | list($source, $meta_data) = $this->_extractJsonFrontMatter($source); 159 | $page_data = array_merge($page_data, $meta_data); 160 | } 161 | 162 | // We need to know the source file in case editing is enabled: 163 | $page_data['file'] = $page; 164 | 165 | $html = false; 166 | if ($renderer && $renderer == 'HTML') { 167 | $html = $renderer($source); 168 | } 169 | if ($renderer && $renderer == 'Markdown') { 170 | $html = \Wikitten\MarkdownExtra::defaultTransform($source); 171 | } 172 | 173 | if (empty(trim($html))) { 174 | $html = "

      This page is empty

      \n"; 175 | $source = $parts[0]; 176 | } 177 | 178 | $this->_view('render', array( 179 | 'html' => $html, 180 | 'source' => $source, 181 | 'extension' => $extension, 182 | 'parts' => $parts, 183 | 'page' => $page_data, 184 | 'is_dir' => false, 185 | 'use_pastebin' => $this->_usePasteBin() 186 | )); 187 | } 188 | 189 | protected function _usePasteBin() 190 | { 191 | return defined('ENABLE_PASTEBIN') && ENABLE_PASTEBIN && defined('PASTEBIN_API_KEY') && PASTEBIN_API_KEY; 192 | } 193 | 194 | /** 195 | * Given a file path, verifies if the file is safe to touch, 196 | * given permissions, if it's within the library, etc. 197 | * 198 | * @param string $path 199 | * @return bool 200 | */ 201 | protected function _pathIsSafe($path) 202 | { 203 | if ($path && strpos($path, LIBRARY) === 0) { 204 | return true; 205 | } 206 | 207 | return false; 208 | } 209 | 210 | /** 211 | * Given a string with a page's source, attempts to locate a 212 | * section of JSON Front Matter in the heading, and returns 213 | * the remaining source, and an array of extracted meta data. 214 | * 215 | * JSON Front Matter will only be considered when present 216 | * within two lines consisting of three dashes: 217 | * 218 | * --- 219 | * { "title": "hello world" } 220 | * --- 221 | * 222 | * Additionally, the opening and closing brackets may be dropped, 223 | * and this method will still interpret the content as a hash: 224 | * 225 | * --- 226 | * "title": "hello, world", 227 | * "tags": ["hello", "world"] 228 | * --- 229 | * 230 | * @param string $source 231 | * @return array array($remaining_source, $meta_data) 232 | */ 233 | protected function _extractJsonFrontMatter($source) 234 | { 235 | static $front_matter_regex = "/^---[\r\n](.*)[\r\n]---[\r\n](.*)/s"; 236 | 237 | $source = ltrim($source); 238 | $meta_data = array(); 239 | 240 | if (preg_match($front_matter_regex, $source, $matches)) { 241 | $json = trim($matches[1]); 242 | $source = trim($matches[2]); 243 | 244 | // Locate or append starting and ending brackets, 245 | // if necessary. I lazily only check the first 246 | // character for a bracket, so that it'll work 247 | // even if the user includes a hash in the last 248 | // line: 249 | if ($json[0] != '{') { 250 | $json = '{' . $json . '}'; 251 | } 252 | 253 | // Decode & validate the JSON payload: 254 | $meta_data = json_decode($json, true, 512); 255 | 256 | // Check for errors: 257 | if ($meta_data === null) { 258 | $error = json_last_error(); 259 | $message = 'There was an error parsing the JSON Front Matter for this page'; 260 | 261 | // todo: Better error information? 262 | if ($error == JSON_ERROR_SYNTAX) { 263 | $message .= ': Incorrect JSON syntax (missing comma, or double-quotes?)'; 264 | } 265 | 266 | throw new RuntimeException($message); 267 | } 268 | } 269 | 270 | return array($source, $meta_data); 271 | } 272 | 273 | protected function _view($view, $variables = array()) 274 | { 275 | extract($variables); 276 | 277 | $content = __DIR__ . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . "$view.php"; 278 | 279 | if (!isset($layout)) { 280 | $layout = __DIR__ . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'layout.php'; 281 | } 282 | 283 | if (file_exists($content)) { 284 | ob_start(); 285 | 286 | include($content); 287 | $content = ob_get_contents(); 288 | ob_end_clean(); 289 | 290 | if ($layout) { 291 | include $layout; 292 | } else { 293 | echo $content; 294 | } 295 | } else { 296 | throw new Exception("View $view not found"); 297 | } 298 | } 299 | 300 | protected function _getTree($dir = LIBRARY) 301 | { 302 | $return = array('directories' => array(), 'files' => array()); 303 | 304 | $items = scandir($dir); 305 | foreach ($items as $item) { 306 | if (preg_match($this->_ignore, $item)) { 307 | if ($this->_force_unignore === false || !preg_match($this->_force_unignore, $item)) { 308 | continue; 309 | } 310 | } 311 | 312 | $path = $dir . DIRECTORY_SEPARATOR . $item; 313 | if (is_dir($path)) { 314 | $return['directories'][$item] = $this->_getTree($path); 315 | continue; 316 | } 317 | 318 | $return['files'][$item] = $item; 319 | } 320 | 321 | uksort($return['directories'], "strnatcasecmp"); 322 | uksort($return['files'], "strnatcasecmp"); 323 | 324 | return $return['directories'] + $return['files']; 325 | } 326 | 327 | public function dispatch() 328 | { 329 | if (!function_exists("finfo_open")) { 330 | die("

      Please enable the PHP Extension FileInfo.dll by uncommenting or adding the following line:

      ;extension=php_fileinfo.dll # You can just uncomment by removing the semicolon (;) in the front.
      "); 331 | } 332 | $action = $this->_getAction(); 333 | $actionMethod = "{$action}Action"; 334 | 335 | if ($action === null || !method_exists($this, $actionMethod)) { 336 | $this->_404(); 337 | } 338 | 339 | $this->$actionMethod(); 340 | } 341 | 342 | protected function _getAction() 343 | { 344 | if (isset($_REQUEST['a'])) { 345 | $action = $_REQUEST['a']; 346 | 347 | if (in_array("{$action}Action", get_class_methods(get_class($this)))) { 348 | $this->_action = $action; 349 | } 350 | } else { 351 | $this->_action = 'index'; 352 | } 353 | return $this->_action; 354 | } 355 | 356 | protected function _json($data = array()) 357 | { 358 | header("Content-type: text/x-json"); 359 | echo(is_string($data) ? $data : json_encode($data)); 360 | exit(); 361 | } 362 | 363 | protected function _isXMLHttpRequest() 364 | { 365 | if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { 366 | return true; 367 | } 368 | 369 | if (function_exists('apache_request_headers')) { 370 | $headers = apache_request_headers(); 371 | if ($headers['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { 372 | return true; 373 | } 374 | } 375 | 376 | return false; 377 | } 378 | 379 | protected function _404($message = 'Page not found.') 380 | { 381 | header('HTTP/1.0 404 Not Found', true); 382 | $page_data = $this->_default_page_data; 383 | $page_data['title'] = 'Not Found'; 384 | 385 | $this->_view('uhoh', array( 386 | 'error' => $message, 387 | 'parts' => array('Uh-oh'), 388 | 'page' => $page_data 389 | )); 390 | 391 | exit; 392 | } 393 | 394 | public function indexAction() 395 | { 396 | $request = parse_url($_SERVER['REQUEST_URI']); 397 | $page = str_replace("###" . APP_DIR . "/", "", "###" . urldecode($request['path'])); 398 | 399 | if (!$page) { 400 | if (file_exists(LIBRARY . DIRECTORY_SEPARATOR . DEFAULT_FILE)) { 401 | $this->_render(DEFAULT_FILE); 402 | return; 403 | } 404 | 405 | $this->_view('index', array( 406 | 'page' => $this->_default_page_data 407 | )); 408 | return; 409 | } 410 | 411 | try { 412 | $this->_render($page); 413 | } catch (Exception $e) { 414 | $this->_404($e->getMessage()); 415 | } 416 | } 417 | 418 | /** 419 | * /?a=edit 420 | * If ENABLE_EDITING is true, handles file editing through 421 | * the web interface. 422 | */ 423 | public function editAction() 424 | { 425 | // Bail out early if editing isn't even enabled, or 426 | // we don't get the right request method && params 427 | // NOTE: $_POST['source'] may be empty if the user just deletes 428 | // everything, but it should always be set. 429 | if (!ENABLE_EDITING || $_SERVER['REQUEST_METHOD'] != 'POST' 430 | || empty($_POST['ref']) || !isset($_POST['source']) 431 | ) { 432 | $this->_404(); 433 | } 434 | 435 | $ref = $_POST['ref']; 436 | $source = $_POST['source']; 437 | $file = base64_decode($ref); 438 | $path = realpath(LIBRARY . DIRECTORY_SEPARATOR . $file); 439 | 440 | // Check if the file is safe to work with, otherwise just 441 | // give back a generic 404 aswell, so we don't allow blind 442 | // scanning of files: 443 | // @todo: we CAN give back a more informative error message 444 | // for files that aren't writable... 445 | if (!$this->_pathIsSafe($path) && !is_writable($path)) { 446 | $this->_404(); 447 | } 448 | 449 | // Check if empty 450 | if(trim($source)){ 451 | // Save the changes, and redirect back to the same page 452 | file_put_contents($path, $source); 453 | }else{ 454 | // Delete file and redirect too (but it will return 404) 455 | unlink($path); 456 | } 457 | 458 | $redirect_url = BASE_URL . "/$file"; 459 | header("HTTP/1.0 302 Found", true); 460 | header("Location: $redirect_url"); 461 | 462 | exit(); 463 | } 464 | 465 | /** 466 | * Handle createion of PasteBin pastes 467 | * @return string JSON response 468 | */ 469 | public function createPasteBinAction() 470 | { 471 | if (!$this->_usePasteBin()) { 472 | $this->_404(); 473 | } 474 | 475 | if ($_SERVER['REQUEST_METHOD'] == 'POST') { 476 | if (isset($_POST['ref'])) { 477 | $file = base64_decode($_POST['ref']); 478 | $path = realpath(LIBRARY . DIRECTORY_SEPARATOR . $file); 479 | 480 | if (!$this->_pathIsSafe($path)) { 481 | $this->_404(); 482 | } else { 483 | $content = file_get_contents($path); 484 | $name = pathinfo($path, PATHINFO_BASENAME); 485 | 486 | require_once PLUGINS . DIRECTORY_SEPARATOR . 'PasteBin.php'; 487 | 488 | $response = array(); 489 | 490 | $pastebin = new PasteBin(PASTEBIN_API_KEY); 491 | 492 | /** 493 | * @todo Add/improve autodetection of file format 494 | */ 495 | 496 | $url = $pastebin->createPaste($content, PasteBin::PASTE_PRIVACY_PUBLIC, $name, PasteBin::PASTE_EXPIRE_1W); 497 | if ($url) { 498 | $response['status'] = 'ok'; 499 | $response['url'] = $url; 500 | } else { 501 | $response['status'] = 'fail'; 502 | $response['error'] = $pastebin->getError(); 503 | } 504 | 505 | header('Content-Type: application/json'); 506 | echo json_encode($response); 507 | exit(); 508 | } 509 | } 510 | } 511 | 512 | exit(); 513 | } 514 | 515 | /** 516 | * Singleton 517 | * @return Wiki 518 | */ 519 | public static function instance() 520 | { 521 | static $instance; 522 | if (!($instance instanceof self)) { 523 | $instance = new self(); 524 | } 525 | return $instance; 526 | } 527 | 528 | public function createAction() 529 | { 530 | $request = parse_url($_SERVER['REQUEST_URI']); 531 | $page = str_replace("###" . APP_DIR . "/", "", "###" . urldecode($request['path'])); 532 | 533 | $filepath = LIBRARY . urldecode($request['path']); 534 | $content = "# " . htmlspecialchars($page, ENT_QUOTES, 'UTF-8'); 535 | // if feature not enabled, go to 404 536 | if (!ENABLE_EDITING || file_exists($filepath)) { 537 | $this->_404(); 538 | } 539 | 540 | // Create subdirectory recursively, if neccessary 541 | mkdir(dirname($filepath), 0755, true); 542 | 543 | // Save default content, and redirect back to the new page 544 | file_put_contents($filepath, $content); 545 | if (file_exists($filepath)) { 546 | // Redirect to new page 547 | $redirect_url = BASE_URL . "/$page"; 548 | header("HTTP/1.0 302 Found", true); 549 | header("Location: $redirect_url"); 550 | 551 | exit(); 552 | } else { 553 | $this->_404(); 554 | } 555 | } 556 | } 557 | -------------------------------------------------------------------------------- /static/css/custom-styles/github.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: octicons-link; 3 | src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff'); 4 | } 5 | 6 | #render .octicon { 7 | display: inline-block; 8 | fill: currentColor; 9 | vertical-align: text-bottom; 10 | } 11 | 12 | #render .anchor { 13 | float: left; 14 | line-height: 1; 15 | margin-left: -20px; 16 | padding-right: 4px; 17 | } 18 | 19 | #render .anchor:focus { 20 | outline: none; 21 | } 22 | 23 | #render h1 .octicon-link, 24 | #render h2 .octicon-link, 25 | #render h3 .octicon-link, 26 | #render h4 .octicon-link, 27 | #render h5 .octicon-link, 28 | #render h6 .octicon-link { 29 | color: #1b1f23; 30 | vertical-align: middle; 31 | visibility: hidden; 32 | } 33 | 34 | #render h1:hover .anchor, 35 | #render h2:hover .anchor, 36 | #render h3:hover .anchor, 37 | #render h4:hover .anchor, 38 | #render h5:hover .anchor, 39 | #render h6:hover .anchor { 40 | text-decoration: none; 41 | } 42 | 43 | #render h1:hover .anchor .octicon-link, 44 | #render h2:hover .anchor .octicon-link, 45 | #render h3:hover .anchor .octicon-link, 46 | #render h4:hover .anchor .octicon-link, 47 | #render h5:hover .anchor .octicon-link, 48 | #render h6:hover .anchor .octicon-link { 49 | visibility: visible; 50 | } 51 | 52 | #render { 53 | -ms-text-size-adjust: 100%; 54 | -webkit-text-size-adjust: 100%; 55 | color: #24292e; 56 | line-height: 1.5; 57 | font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol; 58 | font-size: 16px; 59 | line-height: 1.5; 60 | word-wrap: break-word; 61 | } 62 | 63 | #render .pl-c { 64 | color: #6a737d; 65 | } 66 | 67 | #render .pl-c1, 68 | #render .pl-s .pl-v { 69 | color: #005cc5; 70 | } 71 | 72 | #render .pl-e, 73 | #render .pl-en { 74 | color: #6f42c1; 75 | } 76 | 77 | #render .pl-s .pl-s1, 78 | #render .pl-smi { 79 | color: #24292e; 80 | } 81 | 82 | #render .pl-ent { 83 | color: #22863a; 84 | } 85 | 86 | #render .pl-k { 87 | color: #d73a49; 88 | } 89 | 90 | #render .pl-pds, 91 | #render .pl-s, 92 | #render .pl-s .pl-pse .pl-s1, 93 | #render .pl-sr, 94 | #render .pl-sr .pl-cce, 95 | #render .pl-sr .pl-sra, 96 | #render .pl-sr .pl-sre { 97 | color: #032f62; 98 | } 99 | 100 | #render .pl-smw, 101 | #render .pl-v { 102 | color: #e36209; 103 | } 104 | 105 | #render .pl-bu { 106 | color: #b31d28; 107 | } 108 | 109 | #render .pl-ii { 110 | background-color: #b31d28; 111 | color: #fafbfc; 112 | } 113 | 114 | #render .pl-c2 { 115 | background-color: #d73a49; 116 | color: #fafbfc; 117 | } 118 | 119 | #render .pl-c2:before { 120 | content: "^M"; 121 | } 122 | 123 | #render .pl-sr .pl-cce { 124 | color: #22863a; 125 | font-weight: 700; 126 | } 127 | 128 | #render .pl-ml { 129 | color: #735c0f; 130 | } 131 | 132 | #render .pl-mh, 133 | #render .pl-mh .pl-en, 134 | #render .pl-ms { 135 | color: #005cc5; 136 | font-weight: 700; 137 | } 138 | 139 | #render .pl-mi { 140 | color: #24292e; 141 | font-style: italic; 142 | } 143 | 144 | #render .pl-mb { 145 | color: #24292e; 146 | font-weight: 700; 147 | } 148 | 149 | #render .pl-md { 150 | background-color: #ffeef0; 151 | color: #b31d28; 152 | } 153 | 154 | #render .pl-mi1 { 155 | background-color: #f0fff4; 156 | color: #22863a; 157 | } 158 | 159 | #render .pl-mc { 160 | background-color: #ffebda; 161 | color: #e36209; 162 | } 163 | 164 | #render .pl-mi2 { 165 | background-color: #005cc5; 166 | color: #f6f8fa; 167 | } 168 | 169 | #render .pl-mdr { 170 | color: #6f42c1; 171 | font-weight: 700; 172 | } 173 | 174 | #render .pl-ba { 175 | color: #586069; 176 | } 177 | 178 | #render .pl-sg { 179 | color: #959da5; 180 | } 181 | 182 | #render .pl-corl { 183 | color: #032f62; 184 | text-decoration: underline; 185 | } 186 | 187 | #render details { 188 | display: block; 189 | } 190 | 191 | #render summary { 192 | display: list-item; 193 | } 194 | 195 | #render a { 196 | background-color: transparent; 197 | } 198 | 199 | #render a:active, 200 | #render a:hover { 201 | outline-width: 0; 202 | } 203 | 204 | #render strong { 205 | font-weight: inherit; 206 | font-weight: bolder; 207 | } 208 | 209 | #render h1 { 210 | font-size: 2em; 211 | margin: .67em 0; 212 | } 213 | 214 | #render img { 215 | border-style: none; 216 | } 217 | 218 | #render code, 219 | #render kbd, 220 | #render pre { 221 | font-family: monospace,monospace; 222 | font-size: 1em; 223 | } 224 | 225 | #render hr { 226 | box-sizing: content-box; 227 | height: 0; 228 | overflow: visible; 229 | } 230 | 231 | #render input { 232 | font: inherit; 233 | margin: 0; 234 | } 235 | 236 | #render input { 237 | overflow: visible; 238 | } 239 | 240 | #render [type=checkbox] { 241 | box-sizing: border-box; 242 | padding: 0; 243 | } 244 | 245 | #render * { 246 | box-sizing: border-box; 247 | } 248 | 249 | #render input { 250 | font-family: inherit; 251 | font-size: inherit; 252 | line-height: inherit; 253 | } 254 | 255 | #render a { 256 | color: #0366d6; 257 | text-decoration: none; 258 | } 259 | 260 | #render a:hover { 261 | text-decoration: underline; 262 | } 263 | 264 | #render strong { 265 | font-weight: 600; 266 | } 267 | 268 | #render hr { 269 | background: transparent; 270 | border: 0; 271 | border-bottom: 1px solid #dfe2e5; 272 | height: 0; 273 | margin: 15px 0; 274 | overflow: hidden; 275 | } 276 | 277 | #render hr:before { 278 | content: ""; 279 | display: table; 280 | } 281 | 282 | #render hr:after { 283 | clear: both; 284 | content: ""; 285 | display: table; 286 | } 287 | 288 | #render table { 289 | border-collapse: collapse; 290 | border-spacing: 0; 291 | } 292 | 293 | #render td, 294 | #render th { 295 | padding: 0; 296 | } 297 | 298 | #render details summary { 299 | cursor: pointer; 300 | } 301 | 302 | #render h1, 303 | #render h2, 304 | #render h3, 305 | #render h4, 306 | #render h5, 307 | #render h6 { 308 | margin-bottom: 0; 309 | margin-top: 0; 310 | } 311 | 312 | #render h1 { 313 | font-size: 32px; 314 | } 315 | 316 | #render h1, 317 | #render h2 { 318 | font-weight: 600; 319 | } 320 | 321 | #render h2 { 322 | font-size: 24px; 323 | } 324 | 325 | #render h3 { 326 | font-size: 20px; 327 | } 328 | 329 | #render h3, 330 | #render h4 { 331 | font-weight: 600; 332 | } 333 | 334 | #render h4 { 335 | font-size: 16px; 336 | } 337 | 338 | #render h5 { 339 | font-size: 14px; 340 | } 341 | 342 | #render h5, 343 | #render h6 { 344 | font-weight: 600; 345 | } 346 | 347 | #render h6 { 348 | font-size: 12px; 349 | } 350 | 351 | #render p { 352 | margin-bottom: 10px; 353 | margin-top: 0; 354 | } 355 | 356 | #render blockquote { 357 | margin: 0; 358 | } 359 | 360 | #render ol, 361 | #render ul { 362 | margin-bottom: 0; 363 | margin-top: 0; 364 | padding-left: 0; 365 | } 366 | 367 | #render ol ol, 368 | #render ul ol { 369 | list-style-type: lower-roman; 370 | } 371 | 372 | #render ol ol ol, 373 | #render ol ul ol, 374 | #render ul ol ol, 375 | #render ul ul ol { 376 | list-style-type: lower-alpha; 377 | } 378 | 379 | #render dd { 380 | margin-left: 0; 381 | } 382 | 383 | #render code, 384 | #render pre { 385 | font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; 386 | font-size: 12px; 387 | } 388 | 389 | #render pre { 390 | margin-bottom: 0; 391 | margin-top: 0; 392 | } 393 | 394 | #render input::-webkit-inner-spin-button, 395 | #render input::-webkit-outer-spin-button { 396 | -webkit-appearance: none; 397 | appearance: none; 398 | margin: 0; 399 | } 400 | 401 | #render .border { 402 | border: 1px solid #e1e4e8!important; 403 | } 404 | 405 | #render .border-0 { 406 | border: 0!important; 407 | } 408 | 409 | #render .border-bottom { 410 | border-bottom: 1px solid #e1e4e8!important; 411 | } 412 | 413 | #render .rounded-1 { 414 | border-radius: 3px!important; 415 | } 416 | 417 | #render .bg-white { 418 | background-color: #fff!important; 419 | } 420 | 421 | #render .bg-gray-light { 422 | background-color: #fafbfc!important; 423 | } 424 | 425 | #render .text-gray-light { 426 | color: #6a737d!important; 427 | } 428 | 429 | #render .mb-0 { 430 | margin-bottom: 0!important; 431 | } 432 | 433 | #render .my-2 { 434 | margin-bottom: 8px!important; 435 | margin-top: 8px!important; 436 | } 437 | 438 | #render .pl-0 { 439 | padding-left: 0!important; 440 | } 441 | 442 | #render .py-0 { 443 | padding-bottom: 0!important; 444 | padding-top: 0!important; 445 | } 446 | 447 | #render .pl-1 { 448 | padding-left: 4px!important; 449 | } 450 | 451 | #render .pl-2 { 452 | padding-left: 8px!important; 453 | } 454 | 455 | #render .py-2 { 456 | padding-bottom: 8px!important; 457 | padding-top: 8px!important; 458 | } 459 | 460 | #render .pl-3, 461 | #render .px-3 { 462 | padding-left: 16px!important; 463 | } 464 | 465 | #render .px-3 { 466 | padding-right: 16px!important; 467 | } 468 | 469 | #render .pl-4 { 470 | padding-left: 24px!important; 471 | } 472 | 473 | #render .pl-5 { 474 | padding-left: 32px!important; 475 | } 476 | 477 | #render .pl-6 { 478 | padding-left: 40px!important; 479 | } 480 | 481 | #render .f6 { 482 | font-size: 12px!important; 483 | } 484 | 485 | #render .lh-condensed { 486 | line-height: 1.25!important; 487 | } 488 | 489 | #render .text-bold { 490 | font-weight: 600!important; 491 | } 492 | 493 | #render:before { 494 | content: ""; 495 | display: table; 496 | } 497 | 498 | #render:after { 499 | clear: both; 500 | content: ""; 501 | display: table; 502 | } 503 | 504 | #render>:first-child { 505 | margin-top: 0!important; 506 | } 507 | 508 | #render>:last-child { 509 | margin-bottom: 0!important; 510 | } 511 | 512 | #render a:not([href]) { 513 | color: inherit; 514 | text-decoration: none; 515 | } 516 | 517 | #render blockquote, 518 | #render dl, 519 | #render ol, 520 | #render p, 521 | #render pre, 522 | #render table, 523 | #render ul { 524 | margin-bottom: 16px; 525 | margin-top: 0; 526 | } 527 | 528 | #render hr { 529 | background-color: #e1e4e8; 530 | border: 0; 531 | height: .25em; 532 | margin: 24px 0; 533 | padding: 0; 534 | } 535 | 536 | #render blockquote { 537 | border-left: .25em solid #dfe2e5; 538 | color: #6a737d; 539 | padding: 0 1em; 540 | } 541 | 542 | #render blockquote>:first-child { 543 | margin-top: 0; 544 | } 545 | 546 | #render blockquote>:last-child { 547 | margin-bottom: 0; 548 | } 549 | 550 | #render kbd { 551 | background-color: #fafbfc; 552 | border: 1px solid #c6cbd1; 553 | border-bottom-color: #959da5; 554 | border-radius: 3px; 555 | box-shadow: inset 0 -1px 0 #959da5; 556 | color: #444d56; 557 | display: inline-block; 558 | font-size: 11px; 559 | line-height: 10px; 560 | padding: 3px 5px; 561 | vertical-align: middle; 562 | } 563 | 564 | #render h1, 565 | #render h2, 566 | #render h3, 567 | #render h4, 568 | #render h5, 569 | #render h6 { 570 | font-weight: 600; 571 | line-height: 1.25; 572 | margin-bottom: 16px; 573 | margin-top: 24px; 574 | } 575 | 576 | #render h1 { 577 | font-size: 2em; 578 | } 579 | 580 | #render h1, 581 | #render h2 { 582 | border-bottom: 1px solid #eaecef; 583 | padding-bottom: .3em; 584 | } 585 | 586 | #render h2 { 587 | font-size: 1.5em; 588 | } 589 | 590 | #render h3 { 591 | font-size: 1.25em; 592 | } 593 | 594 | #render h4 { 595 | font-size: 1em; 596 | } 597 | 598 | #render h5 { 599 | font-size: .875em; 600 | } 601 | 602 | #render h6 { 603 | color: #6a737d; 604 | font-size: .85em; 605 | } 606 | 607 | #render ol, 608 | #render ul { 609 | padding-left: 2em; 610 | } 611 | 612 | #render ol ol, 613 | #render ol ul, 614 | #render ul ol, 615 | #render ul ul { 616 | margin-bottom: 0; 617 | margin-top: 0; 618 | } 619 | 620 | #render li { 621 | word-wrap: break-all; 622 | } 623 | 624 | #render li>p { 625 | margin-top: 16px; 626 | } 627 | 628 | #render li+li { 629 | margin-top: .25em; 630 | } 631 | 632 | #render dl { 633 | padding: 0; 634 | } 635 | 636 | #render dl dt { 637 | font-size: 1em; 638 | font-style: italic; 639 | font-weight: 600; 640 | margin-top: 16px; 641 | padding: 0; 642 | } 643 | 644 | #render dl dd { 645 | margin-bottom: 16px; 646 | padding: 0 16px; 647 | } 648 | 649 | #render table { 650 | display: block; 651 | overflow: auto; 652 | width: 100%; 653 | } 654 | 655 | #render table th { 656 | font-weight: 600; 657 | } 658 | 659 | #render table td, 660 | #render table th { 661 | border: 1px solid #dfe2e5; 662 | padding: 6px 13px; 663 | } 664 | 665 | #render table tr { 666 | background-color: #fff; 667 | border-top: 1px solid #c6cbd1; 668 | } 669 | 670 | #render table tr:nth-child(2n) { 671 | background-color: #f6f8fa; 672 | } 673 | 674 | #render img { 675 | background-color: #fff; 676 | box-sizing: content-box; 677 | max-width: 100%; 678 | } 679 | 680 | #render img[align=right] { 681 | padding-left: 20px; 682 | } 683 | 684 | #render img[align=left] { 685 | padding-right: 20px; 686 | } 687 | 688 | #render code { 689 | background-color: rgba(27,31,35,.05); 690 | border-radius: 3px; 691 | font-size: 85%; 692 | margin: 0; 693 | padding: .2em .4em; 694 | } 695 | 696 | #render pre { 697 | word-wrap: normal; 698 | } 699 | 700 | #render pre>code { 701 | background: transparent; 702 | border: 0; 703 | font-size: 100%; 704 | margin: 0; 705 | padding: 0; 706 | white-space: pre; 707 | word-break: normal; 708 | } 709 | 710 | #render .highlight { 711 | margin-bottom: 16px; 712 | } 713 | 714 | #render .highlight pre { 715 | margin-bottom: 0; 716 | word-break: normal; 717 | } 718 | 719 | #render .highlight pre, 720 | #render pre { 721 | background-color: #f6f8fa; 722 | border-radius: 3px; 723 | font-size: 85%; 724 | line-height: 1.45; 725 | overflow: auto; 726 | padding: 16px; 727 | } 728 | 729 | #render pre code { 730 | background-color: transparent; 731 | border: 0; 732 | display: inline; 733 | line-height: inherit; 734 | margin: 0; 735 | max-width: auto; 736 | overflow: visible; 737 | padding: 0; 738 | word-wrap: normal; 739 | } 740 | 741 | #render .commit-tease-sha { 742 | color: #444d56; 743 | display: inline-block; 744 | font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; 745 | font-size: 90%; 746 | } 747 | 748 | #render .blob-wrapper { 749 | border-bottom-left-radius: 3px; 750 | border-bottom-right-radius: 3px; 751 | overflow-x: auto; 752 | overflow-y: hidden; 753 | } 754 | 755 | #render .blob-wrapper-embedded { 756 | max-height: 240px; 757 | overflow-y: auto; 758 | } 759 | 760 | #render .blob-num { 761 | -moz-user-select: none; 762 | -ms-user-select: none; 763 | -webkit-user-select: none; 764 | color: rgba(27,31,35,.3); 765 | cursor: pointer; 766 | font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; 767 | font-size: 12px; 768 | line-height: 20px; 769 | min-width: 50px; 770 | padding-left: 10px; 771 | padding-right: 10px; 772 | text-align: right; 773 | user-select: none; 774 | vertical-align: top; 775 | white-space: nowrap; 776 | width: 1%; 777 | } 778 | 779 | #render .blob-num:hover { 780 | color: rgba(27,31,35,.6); 781 | } 782 | 783 | #render .blob-num:before { 784 | content: attr(data-line-number); 785 | } 786 | 787 | #render .blob-code { 788 | line-height: 20px; 789 | padding-left: 10px; 790 | padding-right: 10px; 791 | position: relative; 792 | vertical-align: top; 793 | } 794 | 795 | #render .blob-code-inner { 796 | color: #24292e; 797 | font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; 798 | font-size: 12px; 799 | overflow: visible; 800 | white-space: pre; 801 | word-wrap: normal; 802 | } 803 | 804 | #render .pl-token.active, 805 | #render .pl-token:hover { 806 | background: #ffea7f; 807 | cursor: pointer; 808 | } 809 | 810 | #render kbd { 811 | background-color: #fafbfc; 812 | border: 1px solid #d1d5da; 813 | border-bottom-color: #c6cbd1; 814 | border-radius: 3px; 815 | box-shadow: inset 0 -1px 0 #c6cbd1; 816 | color: #444d56; 817 | display: inline-block; 818 | font: 11px SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace; 819 | line-height: 10px; 820 | padding: 3px 5px; 821 | vertical-align: middle; 822 | } 823 | 824 | #render :checked+.radio-label { 825 | border-color: #0366d6; 826 | position: relative; 827 | z-index: 1; 828 | } 829 | 830 | #render .tab-size[data-tab-size="1"] { 831 | -moz-tab-size: 1; 832 | tab-size: 1; 833 | } 834 | 835 | #render .tab-size[data-tab-size="2"] { 836 | -moz-tab-size: 2; 837 | tab-size: 2; 838 | } 839 | 840 | #render .tab-size[data-tab-size="3"] { 841 | -moz-tab-size: 3; 842 | tab-size: 3; 843 | } 844 | 845 | #render .tab-size[data-tab-size="4"] { 846 | -moz-tab-size: 4; 847 | tab-size: 4; 848 | } 849 | 850 | #render .tab-size[data-tab-size="5"] { 851 | -moz-tab-size: 5; 852 | tab-size: 5; 853 | } 854 | 855 | #render .tab-size[data-tab-size="6"] { 856 | -moz-tab-size: 6; 857 | tab-size: 6; 858 | } 859 | 860 | #render .tab-size[data-tab-size="7"] { 861 | -moz-tab-size: 7; 862 | tab-size: 7; 863 | } 864 | 865 | #render .tab-size[data-tab-size="8"] { 866 | -moz-tab-size: 8; 867 | tab-size: 8; 868 | } 869 | 870 | #render .tab-size[data-tab-size="9"] { 871 | -moz-tab-size: 9; 872 | tab-size: 9; 873 | } 874 | 875 | #render .tab-size[data-tab-size="10"] { 876 | -moz-tab-size: 10; 877 | tab-size: 10; 878 | } 879 | 880 | #render .tab-size[data-tab-size="11"] { 881 | -moz-tab-size: 11; 882 | tab-size: 11; 883 | } 884 | 885 | #render .tab-size[data-tab-size="12"] { 886 | -moz-tab-size: 12; 887 | tab-size: 12; 888 | } 889 | 890 | #render .task-list-item { 891 | list-style-type: none; 892 | } 893 | 894 | #render .task-list-item+.task-list-item { 895 | margin-top: 3px; 896 | } 897 | 898 | #render .task-list-item input { 899 | margin: 0 .2em .25em -1.6em; 900 | vertical-align: middle; 901 | } 902 | 903 | #render hr { 904 | border-bottom-color: #eee; 905 | } 906 | 907 | #render .pl-0 { 908 | padding-left: 0!important; 909 | } 910 | 911 | #render .pl-1 { 912 | padding-left: 4px!important; 913 | } 914 | 915 | #render .pl-2 { 916 | padding-left: 8px!important; 917 | } 918 | 919 | #render .pl-3 { 920 | padding-left: 16px!important; 921 | } 922 | 923 | #render .pl-4 { 924 | padding-left: 24px!important; 925 | } 926 | 927 | #render .pl-5 { 928 | padding-left: 32px!important; 929 | } 930 | 931 | #render .pl-6 { 932 | padding-left: 40px!important; 933 | } 934 | 935 | #render .pl-7 { 936 | padding-left: 48px!important; 937 | } 938 | 939 | #render .pl-8 { 940 | padding-left: 64px!important; 941 | } 942 | 943 | #render .pl-9 { 944 | padding-left: 80px!important; 945 | } 946 | 947 | #render .pl-10 { 948 | padding-left: 96px!important; 949 | } 950 | 951 | #render .pl-11 { 952 | padding-left: 112px!important; 953 | } 954 | 955 | #render .pl-12 { 956 | padding-left: 128px!important; 957 | } 958 | -------------------------------------------------------------------------------- /library/Sample Markdown document.md: -------------------------------------------------------------------------------- 1 | # Markdown: Syntax 2 | 3 | - [Overview](#overview) 4 | - [Philosophy](#philosophy) 5 | - [Inline HTML](#html) 6 | - [Automatic Escaping for Special Characters](#autoescape) 7 | - [Block Elements](#block) 8 | - [Paragraphs and Line Breaks](#p) 9 | - [Headers](#header) 10 | - [Blockquotes](#blockquote) 11 | - [Lists](#list) 12 | - [Code Blocks](#precode) 13 | - [Horizontal Rules](#hr) 14 | - [Span Elements](#span) 15 | - [Links](#link) 16 | - [Emphasis](#em) 17 | - [Code](#code) 18 | - [Images](#img) 19 | - [Miscellaneous](#misc) 20 | - [Backslash Escapes](#backslash) 21 | - [Automatic Links](#autolink) 22 | 23 | --- 24 | 25 |

      Overview

      26 | 27 |

      Philosophy

      28 | 29 | Markdown is intended to be as easy-to-read and easy-to-write as is feasible. 30 | 31 | Readability, however, is emphasized above all else. A Markdown-formatted 32 | document should be publishable as-is, as plain text, without looking 33 | like it's been marked up with tags or formatting instructions. While 34 | Markdown's syntax has been influenced by several existing text-to-HTML 35 | filters -- including [Setext][1], [atx][2], [Textile][3], [reStructuredText][4], 36 | [Grutatext][5], and [EtText][6] -- the single biggest source of 37 | inspiration for Markdown's syntax is the format of plain text email. 38 | 39 | [1]: http://docutils.sourceforge.net/mirror/setext.html 40 | [2]: http://www.aaronsw.com/2002/atx/ 41 | [3]: http://textism.com/tools/textile/ 42 | [4]: http://docutils.sourceforge.net/rst.html 43 | [5]: http://www.triptico.com/software/grutatxt.html 44 | [6]: http://ettext.taint.org/doc/ 45 | 46 | To this end, Markdown's syntax is comprised entirely of punctuation 47 | characters, which punctuation characters have been carefully chosen so 48 | as to look like what they mean. E.g., asterisks around a word actually 49 | look like \*emphasis\*. Markdown lists look like, well, lists. Even 50 | blockquotes look like quoted passages of text, assuming you've ever 51 | used email. 52 | 53 |

      Inline HTML

      54 | 55 | Markdown's syntax is intended for one purpose: to be used as a 56 | format for _writing_ for the web. 57 | 58 | Markdown is not a replacement for HTML, or even close to it. Its 59 | syntax is very small, corresponding only to a very small subset of 60 | HTML tags. The idea is _not_ to create a syntax that makes it easier 61 | to insert HTML tags. In my opinion, HTML tags are already easy to 62 | insert. The idea for Markdown is to make it easy to read, write, and 63 | edit prose. HTML is a _publishing_ format; Markdown is a _writing_ 64 | format. Thus, Markdown's formatting syntax only addresses issues that 65 | can be conveyed in plain text. 66 | 67 | For any markup that is not covered by Markdown's syntax, you simply 68 | use HTML itself. There's no need to preface it or delimit it to 69 | indicate that you're switching from Markdown to HTML; you just use 70 | the tags. 71 | 72 | The only restrictions are that block-level HTML elements -- e.g. `
      `, 73 | ``, `
      `, `

      `, etc. -- must be separated from surrounding 74 | content by blank lines, and the start and end tags of the block should 75 | not be indented with tabs or spaces. Markdown is smart enough not 76 | to add extra (unwanted) `

      ` tags around HTML block-level tags. 77 | 78 | For example, to add an HTML table to a Markdown article: 79 | 80 | This is a regular paragraph. 81 | 82 |

      83 | 84 | 85 | 86 |
      Foo
      87 | 88 | This is another regular paragraph. 89 | 90 | Note that Markdown formatting syntax is not processed within block-level 91 | HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an 92 | HTML block. 93 | 94 | Span-level HTML tags -- e.g. ``, ``, or `` -- can be 95 | used anywhere in a Markdown paragraph, list item, or header. If you 96 | want, you can even use HTML tags instead of Markdown formatting; e.g. if 97 | you'd prefer to use HTML `` or `` tags instead of Markdown's 98 | link or image syntax, go right ahead. 99 | 100 | Unlike block-level HTML tags, Markdown syntax _is_ processed within 101 | span-level tags. 102 | 103 |

      Automatic Escaping for Special Characters

      104 | 105 | In HTML, there are two characters that demand special treatment: `<` 106 | and `&`. Left angle brackets are used to start tags; ampersands are 107 | used to denote HTML entities. If you want to use them as literal 108 | characters, you must escape them as entities, e.g. `<`, and 109 | `&`. 110 | 111 | Ampersands in particular are bedeviling for web writers. If you want to 112 | write about 'AT&T', you need to write '`AT&T`'. You even need to 113 | escape ampersands within URLs. Thus, if you want to link to: 114 | 115 | http://images.google.com/images?num=30&q=larry+bird 116 | 117 | you need to encode the URL as: 118 | 119 | http://images.google.com/images?num=30&q=larry+bird 120 | 121 | in your anchor tag `href` attribute. Needless to say, this is easy to 122 | forget, and is probably the single most common source of HTML validation 123 | errors in otherwise well-marked-up web sites. 124 | 125 | Markdown allows you to use these characters naturally, taking care of 126 | all the necessary escaping for you. If you use an ampersand as part of 127 | an HTML entity, it remains unchanged; otherwise it will be translated 128 | into `&`. 129 | 130 | So, if you want to include a copyright symbol in your article, you can write: 131 | 132 | © 133 | 134 | and Markdown will leave it alone. But if you write: 135 | 136 | AT&T 137 | 138 | Markdown will translate it to: 139 | 140 | AT&T 141 | 142 | Similarly, because Markdown supports [inline HTML](#html), if you use 143 | angle brackets as delimiters for HTML tags, Markdown will treat them as 144 | such. But if you write: 145 | 146 | 4 < 5 147 | 148 | Markdown will translate it to: 149 | 150 | 4 < 5 151 | 152 | However, inside Markdown code spans and blocks, angle brackets and 153 | ampersands are _always_ encoded automatically. This makes it easy to use 154 | Markdown to write about HTML code. (As opposed to raw HTML, which is a 155 | terrible format for writing about HTML syntax, because every single `<` 156 | and `&` in your example code needs to be escaped.) 157 | 158 | --- 159 | 160 |

      Block Elements

      161 | 162 |

      Paragraphs and Line Breaks

      163 | 164 | A paragraph is simply one or more consecutive lines of text, separated 165 | by one or more blank lines. (A blank line is any line that looks like a 166 | blank line -- a line containing nothing but spaces or tabs is considered 167 | blank.) Normal paragraphs should not be indented with spaces or tabs. 168 | 169 | The implication of the "one or more consecutive lines of text" rule is 170 | that Markdown supports "hard-wrapped" text paragraphs. This differs 171 | significantly from most other text-to-HTML formatters (including Movable 172 | Type's "Convert Line Breaks" option) which translate every line break 173 | character in a paragraph into a `
      ` tag. 174 | 175 | When you _do_ want to insert a `
      ` break tag using Markdown, you 176 | end a line with two or more spaces, then type return. 177 | 178 | Yes, this takes a tad more effort to create a `
      `, but a simplistic 179 | "every line break is a `
      `" rule wouldn't work for Markdown. 180 | Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l] 181 | work best -- and look better -- when you format them with hard breaks. 182 | 183 | [bq]: #blockquote 184 | [l]: #list 185 | 186 | 187 | 188 | Markdown supports two styles of headers, [Setext][1] and [atx][2]. 189 | 190 | Setext-style headers are "underlined" using equal signs (for first-level 191 | headers) and dashes (for second-level headers). For example: 192 | 193 | This is an H1 194 | ============= 195 | 196 | This is an H2 197 | ------------- 198 | 199 | Any number of underlining `=`'s or `-`'s will work. 200 | 201 | Atx-style headers use 1-6 hash characters at the start of the line, 202 | corresponding to header levels 1-6. For example: 203 | 204 | # This is an H1 205 | 206 | ## This is an H2 207 | 208 | ###### This is an H6 209 | 210 | Optionally, you may "close" atx-style headers. This is purely 211 | cosmetic -- you can use this if you think it looks better. The 212 | closing hashes don't even need to match the number of hashes 213 | used to open the header. (The number of opening hashes 214 | determines the header level.) : 215 | 216 | # This is an H1 # 217 | 218 | ## This is an H2 ## 219 | 220 | ### This is an H3 ###### 221 | 222 |

      Blockquotes

      223 | 224 | Markdown uses email-style `>` characters for blockquoting. If you're 225 | familiar with quoting passages of text in an email message, then you 226 | know how to create a blockquote in Markdown. It looks best if you hard 227 | wrap the text and put a `>` before every line: 228 | 229 | > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, 230 | > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. 231 | > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. 232 | > 233 | > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse 234 | > id sem consectetuer libero luctus adipiscing. 235 | 236 | Markdown allows you to be lazy and only put the `>` before the first 237 | line of a hard-wrapped paragraph: 238 | 239 | > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, 240 | consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. 241 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. 242 | 243 | > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse 244 | id sem consectetuer libero luctus adipiscing. 245 | 246 | Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by 247 | adding additional levels of `>`: 248 | 249 | > This is the first level of quoting. 250 | > 251 | > > This is nested blockquote. 252 | > 253 | > Back to the first level. 254 | 255 | Blockquotes can contain other Markdown elements, including headers, lists, 256 | and code blocks: 257 | 258 | > ## This is a header. 259 | > 260 | > 1. This is the first list item. 261 | > 2. This is the second list item. 262 | > 263 | > Here's some example code: 264 | > 265 | > return shell_exec("echo $input | $markdown_script"); 266 | 267 | Any decent text editor should make email-style quoting easy. For 268 | example, with BBEdit, you can make a selection and choose Increase 269 | Quote Level from the Text menu. 270 | 271 |

      Lists

      272 | 273 | Markdown supports ordered (numbered) and unordered (bulleted) lists. 274 | 275 | Unordered lists use asterisks, pluses, and hyphens -- interchangably 276 | -- as list markers: 277 | 278 | * Red 279 | * Green 280 | * Blue 281 | 282 | is equivalent to: 283 | 284 | + Red 285 | + Green 286 | + Blue 287 | 288 | and: 289 | 290 | - Red 291 | - Green 292 | - Blue 293 | 294 | Ordered lists use numbers followed by periods: 295 | 296 | 1. Bird 297 | 2. McHale 298 | 3. Parish 299 | 300 | It's important to note that the actual numbers you use to mark the 301 | list have no effect on the HTML output Markdown produces. The HTML 302 | Markdown produces from the above list is: 303 | 304 |
        305 |
      1. Bird
      2. 306 |
      3. McHale
      4. 307 |
      5. Parish
      6. 308 |
      309 | 310 | If you instead wrote the list in Markdown like this: 311 | 312 | 1. Bird 313 | 1. McHale 314 | 1. Parish 315 | 316 | or even: 317 | 318 | 3. Bird 319 | 1. McHale 320 | 8. Parish 321 | 322 | you'd get the exact same HTML output. The point is, if you want to, 323 | you can use ordinal numbers in your ordered Markdown lists, so that 324 | the numbers in your source match the numbers in your published HTML. 325 | But if you want to be lazy, you don't have to. 326 | 327 | If you do use lazy list numbering, however, you should still start the 328 | list with the number 1. At some point in the future, Markdown may support 329 | starting ordered lists at an arbitrary number. 330 | 331 | List markers typically start at the left margin, but may be indented by 332 | up to three spaces. List markers must be followed by one or more spaces 333 | or a tab. 334 | 335 | To make lists look nice, you can wrap items with hanging indents: 336 | 337 | * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 338 | Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, 339 | viverra nec, fringilla in, laoreet vitae, risus. 340 | * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. 341 | Suspendisse id sem consectetuer libero luctus adipiscing. 342 | 343 | But if you want to be lazy, you don't have to: 344 | 345 | * Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 346 | Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, 347 | viverra nec, fringilla in, laoreet vitae, risus. 348 | * Donec sit amet nisl. Aliquam semper ipsum sit amet velit. 349 | Suspendisse id sem consectetuer libero luctus adipiscing. 350 | 351 | If list items are separated by blank lines, Markdown will wrap the 352 | items in `

      ` tags in the HTML output. For example, this input: 353 | 354 | * Bird 355 | * Magic 356 | 357 | will turn into: 358 | 359 |

        360 |
      • Bird
      • 361 |
      • Magic
      • 362 |
      363 | 364 | But this: 365 | 366 | * Bird 367 | 368 | * Magic 369 | 370 | will turn into: 371 | 372 |
        373 |
      • Bird

      • 374 |
      • Magic

      • 375 |
      376 | 377 | List items may consist of multiple paragraphs. Each subsequent 378 | paragraph in a list item must be indented by either 4 spaces 379 | or one tab: 380 | 381 | 1. This is a list item with two paragraphs. Lorem ipsum dolor 382 | sit amet, consectetuer adipiscing elit. Aliquam hendrerit 383 | mi posuere lectus. 384 | 385 | Vestibulum enim wisi, viverra nec, fringilla in, laoreet 386 | vitae, risus. Donec sit amet nisl. Aliquam semper ipsum 387 | sit amet velit. 388 | 389 | 2. Suspendisse id sem consectetuer libero luctus adipiscing. 390 | 391 | It looks nice if you indent every line of the subsequent 392 | paragraphs, but here again, Markdown will allow you to be 393 | lazy: 394 | 395 | * This is a list item with two paragraphs. 396 | 397 | This is the second paragraph in the list item. You're 398 | only required to indent the first line. Lorem ipsum dolor 399 | sit amet, consectetuer adipiscing elit. 400 | 401 | * Another item in the same list. 402 | 403 | To put a blockquote within a list item, the blockquote's `>` 404 | delimiters need to be indented: 405 | 406 | * A list item with a blockquote: 407 | 408 | > This is a blockquote 409 | > inside a list item. 410 | 411 | To put a code block within a list item, the code block needs 412 | to be indented _twice_ -- 8 spaces or two tabs: 413 | 414 | * A list item with a code block: 415 | 416 | 417 | 418 | It's worth noting that it's possible to trigger an ordered list by 419 | accident, by writing something like this: 420 | 421 | 1986. What a great season. 422 | 423 | In other words, a _number-period-space_ sequence at the beginning of a 424 | line. To avoid this, you can backslash-escape the period: 425 | 426 | 1986\. What a great season. 427 | 428 |

      Code Blocks

      429 | 430 | Pre-formatted code blocks are used for writing about programming or 431 | markup source code. Rather than forming normal paragraphs, the lines 432 | of a code block are interpreted literally. Markdown wraps a code block 433 | in both `
      ` and `` tags.
      434 | 
      435 | To produce a code block in Markdown, simply indent every line of the
      436 | block by at least 4 spaces or 1 tab or use three backticks ` ``` `. For example, given this input:
      437 | 
      438 |     This is a normal paragraph:
      439 | 
      440 |         This is a code block.
      441 | 
      442 | or
      443 | 
      444 |     This is a normal paragraph:
      445 |     ```
      446 |     This is a code block.
      447 |     ```
      448 | 
      449 | Markdown will generate:
      450 | 
      451 |     

      This is a normal paragraph:

      452 | 453 |
      This is a code block.
      454 | 455 | One level of indentation -- 4 spaces or 1 tab -- is removed from each 456 | line of the code block. For example, this: 457 | 458 | Here is an example of AppleScript: 459 | 460 | tell application "Foo" 461 | beep 462 | end tell 463 | 464 | will turn into: 465 | 466 |

      Here is an example of AppleScript:

      467 | 468 |
      
      469 |         tell application "Foo"
      470 |             beep
      471 |         end tell
      472 |     
      473 | 474 | A code block continues until it reaches a line that is not indented 475 | (or the end of the article). 476 | 477 | Within a code block, ampersands (`&`) and angle brackets (`<` and `>`) 478 | are automatically converted into HTML entities. This makes it very 479 | easy to include example HTML source code using Markdown -- just paste 480 | it and indent it, and Markdown will handle the hassle of encoding the 481 | ampersands and angle brackets. For example, this: 482 | 483 | 486 | 487 | will turn into: 488 | 489 |
      <div class="footer">
      490 |         &copy; 2004 Foo Corporation
      491 |     </div>
      492 |     
      493 | 494 | Regular Markdown syntax is not processed within code blocks. E.g., 495 | asterisks are just literal asterisks within a code block. This means 496 | it's also easy to use Markdown to write about Markdown's own syntax. 497 | 498 |

      Horizontal Rules

      499 | 500 | You can produce a horizontal rule tag (`
      `) by placing three or 501 | more hyphens, asterisks, or underscores on a line by themselves. If you 502 | wish, you may use spaces between the hyphens or asterisks. Each of the 503 | following lines will produce a horizontal rule: 504 | 505 | * * * 506 | 507 | *** 508 | 509 | ***** 510 | 511 | - - - 512 | 513 | --------------------------------------- 514 | 515 | --- 516 | 517 |

      Span Elements

      518 | 519 | 520 | 521 | Markdown supports two style of links: _inline_ and _reference_. 522 | 523 | In both styles, the link text is delimited by [square brackets]. 524 | 525 | To create an inline link, use a set of regular parentheses immediately 526 | after the link text's closing square bracket. Inside the parentheses, 527 | put the URL where you want the link to point, along with an _optional_ 528 | title for the link, surrounded in quotes. For example: 529 | 530 | This is [an example](http://example.com/ "Title") inline link. 531 | 532 | [This link](http://example.net/) has no title attribute. 533 | 534 | Will produce: 535 | 536 |

      This is 537 | an example inline link.

      538 | 539 |

      This link has no 540 | title attribute.

      541 | 542 | If you're referring to a local resource on the same server, you can 543 | use relative paths: 544 | 545 | See my [About](/about/) page for details. 546 | 547 | Reference-style links use a second set of square brackets, inside 548 | which you place a label of your choosing to identify the link: 549 | 550 | This is [an example][id] reference-style link. 551 | 552 | You can optionally use a space to separate the sets of brackets: 553 | 554 | This is [an example] [id] reference-style link. 555 | 556 | Then, anywhere in the document, you define your link label like this, 557 | on a line by itself: 558 | 559 | [id]: http://example.com/ "Optional Title Here" 560 | 561 | That is: 562 | 563 | - Square brackets containing the link identifier (optionally 564 | indented from the left margin using up to three spaces); 565 | - followed by a colon; 566 | - followed by one or more spaces (or tabs); 567 | - followed by the URL for the link; 568 | - optionally followed by a title attribute for the link, enclosed 569 | in double or single quotes, or enclosed in parentheses. 570 | 571 | The following three link definitions are equivalent: 572 | 573 | [foo]: http://example.com/ "Optional Title Here" 574 | [foo]: http://example.com/ "Optional Title Here" 575 | [foo]: http://example.com/ "Optional Title Here" 576 | 577 | **Note:** There is a known bug in Markdown.pl 1.0.1 which prevents 578 | single quotes from being used to delimit link titles. 579 | 580 | The link URL may, optionally, be surrounded by angle brackets: 581 | 582 | [id]: "Optional Title Here" 583 | 584 | You can put the title attribute on the next line and use extra spaces 585 | or tabs for padding, which tends to look better with longer URLs: 586 | 587 | [id]: http://example.com/longish/path/to/resource/here 588 | "Optional Title Here" 589 | 590 | Link definitions are only used for creating links during Markdown 591 | processing, and are stripped from your document in the HTML output. 592 | 593 | Link definition names may consist of letters, numbers, spaces, and 594 | punctuation -- but they are _not_ case sensitive. E.g. these two 595 | links: 596 | 597 | [link text][a] 598 | [link text][a] 599 | 600 | are equivalent. 601 | 602 | The _implicit link name_ shortcut allows you to omit the name of the 603 | link, in which case the link text itself is used as the name. 604 | Just use an empty set of square brackets -- e.g., to link the word 605 | "Google" to the google.com web site, you could simply write: 606 | 607 | [Google][] 608 | 609 | And then define the link: 610 | 611 | [google]: http://google.com/ 612 | 613 | Because link names may contain spaces, this shortcut even works for 614 | multiple words in the link text: 615 | 616 | Visit [Daring Fireball][] for more information. 617 | 618 | And then define the link: 619 | 620 | [daring fireball]: http://daringfireball.net/ 621 | 622 | Link definitions can be placed anywhere in your Markdown document. I 623 | tend to put them immediately after each paragraph in which they're 624 | used, but if you want, you can put them all at the end of your 625 | document, sort of like footnotes. 626 | 627 | Here's an example of reference links in action: 628 | 629 | I get 10 times more traffic from [Google] [1] than from 630 | [Yahoo] [2] or [MSN] [3]. 631 | 632 | [1]: http://google.com/ "Google" 633 | [2]: http://search.yahoo.com/ "Yahoo Search" 634 | [3]: http://search.msn.com/ "MSN Search" 635 | 636 | Using the implicit link name shortcut, you could instead write: 637 | 638 | I get 10 times more traffic from [Google][] than from 639 | [Yahoo][] or [MSN][]. 640 | 641 | [google]: http://google.com/ "Google" 642 | [yahoo]: http://search.yahoo.com/ "Yahoo Search" 643 | [msn]: http://search.msn.com/ "MSN Search" 644 | 645 | Both of the above examples will produce the following HTML output: 646 | 647 |

      I get 10 times more traffic from Google than from 649 | Yahoo 650 | or MSN.

      651 | 652 | For comparison, here is the same paragraph written using 653 | Markdown's inline link style: 654 | 655 | I get 10 times more traffic from [Google](http://google.com/ "Google") 656 | than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or 657 | [MSN](http://search.msn.com/ "MSN Search"). 658 | 659 | The point of reference-style links is not that they're easier to 660 | write. The point is that with reference-style links, your document 661 | source is vastly more readable. Compare the above examples: using 662 | reference-style links, the paragraph itself is only 81 characters 663 | long; with inline-style links, it's 176 characters; and as raw HTML, 664 | it's 234 characters. In the raw HTML, there's more markup than there 665 | is text. 666 | 667 | With Markdown's reference-style links, a source document much more 668 | closely resembles the final output, as rendered in a browser. By 669 | allowing you to move the markup-related metadata out of the paragraph, 670 | you can add links without interrupting the narrative flow of your 671 | prose. 672 | 673 |

      Emphasis

      674 | 675 | Markdown treats asterisks (`*`) and underscores (`_`) as indicators of 676 | emphasis. Text wrapped with one `*` or `_` will be wrapped with an 677 | HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML 678 | `` tag. E.g., this input: 679 | 680 | *single asterisks* 681 | 682 | _single underscores_ 683 | 684 | **double asterisks** 685 | 686 | __double underscores__ 687 | 688 | will produce: 689 | 690 | single asterisks 691 | 692 | single underscores 693 | 694 | double asterisks 695 | 696 | double underscores 697 | 698 | You can use whichever style you prefer; the lone restriction is that 699 | the same character must be used to open and close an emphasis span. 700 | 701 | Emphasis can be used in the middle of a word: 702 | 703 | un*frigging*believable 704 | 705 | But if you surround an `*` or `_` with spaces, it'll be treated as a 706 | literal asterisk or underscore. 707 | 708 | To produce a literal asterisk or underscore at a position where it 709 | would otherwise be used as an emphasis delimiter, you can backslash 710 | escape it: 711 | 712 | \*this text is surrounded by literal asterisks\* 713 | 714 |

      Code

      715 | 716 | To indicate a span of code, wrap it with backtick quotes (`` ` ``). 717 | Unlike a pre-formatted code block, a code span indicates code within a 718 | normal paragraph. For example: 719 | 720 | Use the `printf()` function. 721 | 722 | will produce: 723 | 724 |

      Use the printf() function.

      725 | 726 | To include a literal backtick character within a code span, you can use 727 | multiple backticks as the opening and closing delimiters: 728 | 729 | ``There is a literal backtick (`) here.`` 730 | 731 | which will produce this: 732 | 733 |

      There is a literal backtick (`) here.

      734 | 735 | The backtick delimiters surrounding a code span may include spaces -- 736 | one after the opening, one before the closing. This allows you to place 737 | literal backtick characters at the beginning or end of a code span: 738 | 739 | A single backtick in a code span: `` ` `` 740 | 741 | A backtick-delimited string in a code span: `` `foo` `` 742 | 743 | will produce: 744 | 745 |

      A single backtick in a code span: `

      746 | 747 |

      A backtick-delimited string in a code span: `foo`

      748 | 749 | With a code span, ampersands and angle brackets are encoded as HTML 750 | entities automatically, which makes it easy to include example HTML 751 | tags. Markdown will turn this: 752 | 753 | Please don't use any `` tags. 754 | 755 | into: 756 | 757 |

      Please don't use any <blink> tags.

      758 | 759 | You can write this: 760 | 761 | `—` is the decimal-encoded equivalent of `—`. 762 | 763 | to produce: 764 | 765 |

      &#8212; is the decimal-encoded 766 | equivalent of &mdash;.

      767 | 768 |

      Images

      769 | 770 | Admittedly, it's fairly difficult to devise a "natural" syntax for 771 | placing images into a plain text document format. 772 | 773 | Markdown uses an image syntax that is intended to resemble the syntax 774 | for links, allowing for two styles: _inline_ and _reference_. 775 | 776 | Inline image syntax looks like this: 777 | 778 | ![Alt text](/path/to/img.jpg) 779 | 780 | ![Alt text](/path/to/img.jpg "Optional title") 781 | 782 | That is: 783 | 784 | - An exclamation mark: `!`; 785 | - followed by a set of square brackets, containing the `alt` 786 | attribute text for the image; 787 | - followed by a set of parentheses, containing the URL or path to 788 | the image, and an optional `title` attribute enclosed in double 789 | or single quotes. 790 | 791 | Reference-style image syntax looks like this: 792 | 793 | ![Alt text][id] 794 | 795 | Where "id" is the name of a defined image reference. Image references 796 | are defined using syntax identical to link references: 797 | 798 | [id]: url/to/image "Optional title attribute" 799 | 800 | As of this writing, Markdown has no syntax for specifying the 801 | dimensions of an image; if this is important to you, you can simply 802 | use regular HTML `` tags. 803 | 804 | --- 805 | 806 |

      Miscellaneous

      807 | 808 | 809 | 810 | Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this: 811 | 812 | 813 | 814 | Markdown will turn this into: 815 | 816 | http://example.com/ 817 | 818 | Automatic links for email addresses work similarly, except that 819 | Markdown will also perform a bit of randomized decimal and hex 820 | entity-encoding to help obscure your address from address-harvesting 821 | spambots. For example, Markdown will turn this: 822 | 823 | 824 | 825 | into something like this: 826 | 827 | address@exa 830 | mple.com 831 | 832 | which will render in a browser as a clickable link to "address@example.com". 833 | 834 | (This sort of entity-encoding trick will indeed fool many, if not 835 | most, address-harvesting bots, but it definitely won't fool all of 836 | them. It's better than nothing, but an address published in this way 837 | will probably eventually start receiving spam.) 838 | 839 |

      Backslash Escapes

      840 | 841 | Markdown allows you to use backslash escapes to generate literal 842 | characters which would otherwise have special meaning in Markdown's 843 | formatting syntax. For example, if you wanted to surround a word 844 | with literal asterisks (instead of an HTML `` tag), you can use 845 | backslashes before the asterisks, like this: 846 | 847 | \*literal asterisks\* 848 | 849 | Markdown provides backslash escapes for the following characters: 850 | 851 | \ backslash 852 | ` backtick 853 | * asterisk 854 | _ underscore 855 | {} curly braces 856 | [] square brackets 857 | () parentheses 858 | # hash mark 859 | + plus sign 860 | - minus sign (hyphen) 861 | . dot 862 | ! exclamation mark 863 | --------------------------------------------------------------------------------