├── content ├── blog │ ├── index.md │ └── my-first-post.md ├── 404.md ├── index.md └── about.md ├── templates └── sharper │ ├── footer.html │ ├── index.html │ ├── post.html │ ├── blog.html │ ├── header.html │ ├── extra.html │ └── assets │ ├── css │ ├── normalize.min.css │ └── styles.css │ └── js │ ├── prettify.js │ └── bootstrap.min.js ├── plugins └── markdown_syntax │ ├── base.php │ ├── markdown_syntax.php │ └── parsedown │ └── Parsedown.php ├── .htaccess ├── config.php ├── index.php ├── README.md ├── install.php └── libraries └── Swifost └── Swifost.php /content/blog/index.md: -------------------------------------------------------------------------------- 1 | Title: Blog 2 | Template: blog 3 | 4 | --separator-- -------------------------------------------------------------------------------- /content/404.md: -------------------------------------------------------------------------------- 1 | Title: Error 404 2 | Robots: noindex, nofollow 3 | 4 | --separator-- 5 | 6 | ## Error 404 7 | Sorry buddy, the page you are looking for seems to not be available. -------------------------------------------------------------------------------- /content/index.md: -------------------------------------------------------------------------------- 1 | Title: Welcome 2 | Description: Welcome to your new Swifost powered website. 3 | Robots: index, follow 4 | Template: index 5 | 6 | --separator-- 7 | 8 | ## Welcome to Swifost 9 | 10 | Welcome to your new Swifost powered website. 11 | 12 | 13 | ### Documentation 14 | For more information have a look at the Swifost documentation at [http://swifost.com/documentation.html](http://swifost.com/documentation.html) 15 | -------------------------------------------------------------------------------- /templates/sharper/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | runDeed("template_footer"); ?> 11 | 12 | -------------------------------------------------------------------------------- /plugins/markdown_syntax/base.php: -------------------------------------------------------------------------------- 1 | text($content); 18 | } 19 | -------------------------------------------------------------------------------- /templates/sharper/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | Information 7 |
8 | 9 |
10 | runDeed("template_content_before"); ?> 11 | 12 | runDeed("template_content_after"); ?> 13 |
14 | -------------------------------------------------------------------------------- /templates/sharper/post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | runDeed("template_content_before"); ?> 5 |

6 | 7 |
8 | runDeed("template_content_after"); ?> 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /plugins/markdown_syntax/markdown_syntax.php: -------------------------------------------------------------------------------- 1 | createFilter("content", "markdown"); -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | ############################### 2 | # Swifost > htaccess settings # 3 | ############################### 4 | 5 | # Charset utf-8 6 | AddDefaultCharset UTF-8 7 | 8 | # Do not show the directory listing 9 | Options -Indexes 10 | 11 | # PHP 5, Apache 1 and 2. 12 | 13 | php_flag magic_quotes_gpc off 14 | php_flag magic_quotes_sybase off 15 | php_flag register_globals off 16 | 17 | 18 | 19 | # Configuration rewrite rules. 20 | 21 | RewriteEngine on 22 | 23 | RewriteBase /%url%/ 24 | RewriteCond %{REQUEST_FILENAME} !-f 25 | RewriteCond %{REQUEST_FILENAME} !-d 26 | RewriteRule ^(.*)$ index.php [QSA,L] 27 | 28 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | "UTF-8", 4 | "timezone" => "America/Bogota", 5 | "title" => "Title site", 6 | "description" => "Description site", 7 | "keywords" => "keywords, here", 8 | "url" => "", 9 | "email" => "test@test.com", 10 | "template" => "sharper", 11 | "social" => array( 12 | "facebook" => "", 13 | "twitter" => "", 14 | "instagram" => "", 15 | "googleplus" => "", 16 | "youtube" => "", 17 | "souncloud" => "", 18 | "linkedin" => "", 19 | "dribbble" => "", 20 | "behance" => "", 21 | "codepen" => "", 22 | "github" => "", 23 | "bitbucket" => "", 24 | "stackoverflow" => "" 25 | ), 26 | "plugins" => array( 27 | "markdown_syntax" 28 | ), 29 | ); 30 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | ready( 27 | "config.php", // File of configuration 28 | "install.php", // File of install 29 | "index.php", // File base 30 | "install", // Parameter GET > install 31 | "ready" // request done of parameter GET > install 32 | ); 33 | -------------------------------------------------------------------------------- /templates/sharper/blog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | All posts 7 |
8 | 9 | runDeed("template_content_before"); ?> 10 | getPages(CONTENT_PATH . "/blog/", "date", "DESC", array("404","index")); 12 | foreach($posts as $post) { 13 | echo "\n"; 14 | echo '
'; 15 | echo ''.$post["title"].' 16 | 17 | '.$post["content-summary"]; 18 | echo 'Read more »'; 19 | echo '
'; 20 | echo "\n"; 21 | } 22 | ?> 23 | 24 | runDeed("theme_content_after"); ?> 25 | 26 | 27 | -------------------------------------------------------------------------------- /content/about.md: -------------------------------------------------------------------------------- 1 | Title: About 2 | Description: Lorem ipsum dolor sit amet 3 | Keywords: about, lorem, ipsum 4 | 5 | --separator-- 6 | 7 | ## About 8 | 9 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 10 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 11 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 12 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 13 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 14 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 15 | 16 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 17 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 18 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 19 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 20 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 21 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. -------------------------------------------------------------------------------- /templates/sharper/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <?php Swifost::go($page["title"]); ?> 6 | 7 | 8 | 9 | "/> 10 | "> 11 | "> 12 | "/> 13 | runDeed("template_meta"); ?> 14 | /templates/sharper/assets/css/normalize.min.css"> 15 | /templates/sharper/assets/css/styles.css"> 16 | 20 | runDeed("template_header"); ?> 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Swifost 2 | ==== 3 | 4 | [![Join the chat at https://gitter.im/jofpin/swifost](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jofpin/swifost?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | [![Stack Share](http://img.shields.io/badge/tech-stack-0690fa.svg?style=flat)](http://stackshare.io/jofpin/swifost) 6 | 7 | 8 | Lightweight static site generator in PHP without a database 9 | 10 | ``` 11 | Version: 1.0.0 12 | ``` 13 | 14 | ### Important requirement 15 | 16 | You can boot into a PHP version 5.3.0 or higher server 17 | 18 | ### History 19 | 20 | When creating Swifost , my goal was to keep it simple and simplification in the development of plugins with easy API and creating templates . 21 | 22 | There are too many CMS online, but all provide publication of content and file management, easy Swifost wants to evolve the development of plugins with few lines of code. 23 | 24 | ### Open source 25 | 26 | Because we love the web, our generation loves to create open source systems, that will make the web evolve faster. That's why Swifost is an open source project and it would be interesting for the development of projects in php, and make development easier, for everyone ! 27 | 28 | ### Documentation 29 | For more information have a look at the Swifost documentation at [http://swifost.com/documentation.html](http://swifost.com/documentation.html) 30 | 31 | ------------- 32 | 33 | Copyright, 2015 by [Jose Pino](http://twitter.com/jofpin) 34 | 35 | ------------- 36 | -------------------------------------------------------------------------------- /templates/sharper/extra.html: -------------------------------------------------------------------------------- 1 |
2 | 7 |

8 |

9 | 10 | 11 | "> 12 | "> 13 | "> 14 | "> 15 |
16 | 17 | -------------------------------------------------------------------------------- /templates/sharper/assets/css/normalize.min.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} 2 | -------------------------------------------------------------------------------- /content/blog/my-first-post.md: -------------------------------------------------------------------------------- 1 | Title: My first post 2 | Description: Lorem ipsum dolor sit amet 3 | Keywords: Lorem, ipsum 4 | Date: 15/07/2015 5 | Template: post 6 | 7 | --separator-- 8 | 9 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 10 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 11 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 12 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 13 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 14 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 15 | 16 | --summary-- 17 | 18 | # Lorem ipsum 19 | ## Lorem ipsum 20 | ### Lorem ipsum 21 | #### Lorem ipsum 22 | ##### Lorem ipsum 23 | 24 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 25 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 26 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 27 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 28 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 29 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 30 | 31 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod 32 | tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 33 | quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo 34 | consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse 35 | cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 36 | proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 37 | 38 | ### Table 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Lorem ipsumLorem ipsum
Lorem ipsumLorem ipsum
Lorem ipsumLorem ipsum
Lorem ipsumLorem ipsum
49 | 50 | 51 | ### Code 52 | `Lorem ipsum` -------------------------------------------------------------------------------- /templates/sharper/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | @import url(http://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css); 2 | @import url(http://fonts.googleapis.com/css?family=Montserrat|Open+Sans:400|Roboto:400,400italic,700,700italic,500,300,900|RobotoDraft|Roboto+Slab:700,300|Lato:300,400,700,900|Ubuntu); 3 | 4 | :root { 5 | font-size: 16px; 6 | } 7 | 8 | body { 9 | font-family: "Roboto", sans-serif; 10 | -webkit-touch-callout: none; 11 | -webkit-tap-highlight-color: transparent 12 | } 13 | 14 | ::-webkit-scrollbar { 15 | -webkit-appearance: none; 16 | width: .4em; 17 | background-color: #f5f5f5; 18 | } 19 | 20 | ::-webkit-scrollbar-thumb { 21 | background: #ccc; 22 | } 23 | h1, h2, h3, h4, h5, h6 { 24 | font-family: "Roboto Slab", serif; 25 | line-height: 1.8; 26 | } 27 | a { 28 | text-decoration: none; 29 | } 30 | a { 31 | text-decoration: none; 32 | font-family: "Roboto" ,sans-serif; 33 | color: #3498db; 34 | } 35 | a:hover { 36 | color: #2980b9; 37 | } 38 | table { 39 | margin: 10px; 40 | border: solid 1px #777; 41 | } 42 | 43 | table, td, th {padding: 4px 8px;} 44 | table, td { 45 | border: solid 1px rgba(0,0,0,0.05); 46 | background: #eee; 47 | } 48 | tr:nth-child(odd) td { 49 | background: #fff; 50 | } 51 | th { 52 | background: #567EBB; 53 | color: #fff; 54 | width: 10em; 55 | font-family: "Roboto", sans-serif; 56 | font-weight: 400; 57 | } 58 | code { 59 | border-radius: 4px; 60 | font-family: "Open sans", fixed; 61 | font-size: 0.9em; 62 | margin: 0 0.25em; 63 | padding: .2em .7em; 64 | color: rgba(255,255,255,0.85); 65 | text-transform: none; 66 | letter-spacing: 0.025em; 67 | background: #252835; 68 | } 69 | 70 | pre { 71 | -webkit-overflow-scrolling: touch; 72 | margin: 0 0 2em 0; 73 | white-space: pre-wrap; 74 | } 75 | pre code { 76 | border-radius: 2px; 77 | display: block; 78 | line-height: 1.5em; 79 | padding: 1em 1.5em; 80 | overflow-x: auto; 81 | } 82 | .Sharper-wrapper { 83 | max-width: 40rem; 84 | margin: 0 auto; 85 | padding: 1rem; 86 | } 87 | .Sharper-wrapper header { 88 | text-align: center; 89 | padding-bottom: 3rem; 90 | line-height: 1.8; 91 | } 92 | .Sharper-wrapper header nav { 93 | display: inline-block; 94 | margin: 0; 95 | margin-bottom: 6rem; 96 | line-height: 1.8; 97 | } 98 | .Sharper-wrapper header nav a { 99 | display: inline-block; 100 | padding: 0.5rem 1rem; 101 | text-decoration: none; 102 | text-transform: uppercase; 103 | font-size: 0.8rem; 104 | font-family: "RobotoDraft", sans-serif; 105 | font-weight: 500; 106 | letter-spacing: 0.03rem; 107 | border-radius: 2px; 108 | line-height: 1.8; 109 | -webkit-transition: all 100ms ease-in; 110 | -moz-transition: all 100ms ease-in; 111 | -ms-transition: all 100ms ease-in; 112 | -o-transition: all 100ms ease-in; 113 | transition: all 100ms ease-in; 114 | } 115 | .Sharper-wrapper header h1 { 116 | margin: 0; 117 | font-family: "RobotoDraft", sans-serif; 118 | font-size: 3.5rem; 119 | font-weight: 300; 120 | letter-spacing: -0.1rem; 121 | line-height: 1.8; 122 | /* text-transform: uppercase; */ 123 | } 124 | .Sharper-wrapper header p { 125 | margin: 0; 126 | margin-bottom: 6rem; 127 | font-family: "Roboto", sans-serif; 128 | line-height: 1.8; 129 | font-weight: 300; 130 | } 131 | .Sharper-wrapper hr { 132 | border: 0; 133 | border-top: 2px solid #ddd; 134 | } 135 | .Sharper-wrapper .Sharper-post { 136 | margin: 2em auto; 137 | border-bottom: 2px solid #f5f5f5; 138 | margin: 2.70rem auto; 139 | padding-bottom: 2.70rem; 140 | } 141 | 142 | .Sharper-wrapper .Sharper-post--title { 143 | line-height: 1.8; 144 | font-size: 1.80rem; 145 | font-weight: 600; 146 | color: #404040; 147 | font-family: "Roboto Slab", serif; 148 | } 149 | .Sharper-wrapper .Sharper-post--title:hover { 150 | color: #3498db; 151 | } 152 | 153 | .Sharper-wrapper .Sharper-post--title:active { 154 | color: #2980b9; 155 | text-decoration: underline; 156 | } 157 | .Sharper-wrapper time { 158 | text-transform: uppercase; 159 | font-weight: 300; 160 | font-size: 0.9rem; 161 | line-height: 1.8; 162 | } 163 | .Sharper-wrapper p { 164 | font-size: 1rem; 165 | line-height: 1.8; 166 | } 167 | 168 | 169 | .Sharper-post--read { 170 | max-width: 600px; 171 | margin: 3rem auto; 172 | overflow: hidden; 173 | } 174 | 175 | .Sharper-post--read---title { 176 | font-family: "Roboto Slab", sans-serif; 177 | font-size: 2.80rem; 178 | margin-bottom: 8px; 179 | line-height: 1.8; 180 | } 181 | 182 | .Sharper-post--read---content { 183 | line-height: 1.8; 184 | } 185 | .Sharper-color--white { 186 | background: #fff; 187 | color: #555; 188 | border-top: .3em solid #567EBB; 189 | } 190 | .Sharper-color--white header > p { 191 | color: #999; 192 | } 193 | .Sharper-color--white header > nav.Sharper-menu a { 194 | color: #aaa; 195 | } 196 | .Sharper-color--white header > nav.Sharper-menu a:hover { 197 | background-color: #567EBB; 198 | color: #fff; 199 | } 200 | .Sharper-color--white header > nav.Sharper-menu a:active, .Sharper-color--white header > nav.Sharper-menu a.active { 201 | background-color: #4d71a9; 202 | color: #fff; 203 | } 204 | .Sharper-color--white hr { 205 | border-color: #f5f5f5; 206 | } 207 | 208 | .Sharper-line { 209 | position: relative; 210 | text-align: center; 211 | margin: 5px auto; 212 | color: #c0c5c5; 213 | font-family: "RobotoDraft", sans-serif; 214 | font-size: 13px; 215 | font-weight: 500; 216 | text-transform: uppercase; 217 | z-index: 1; 218 | line-height: 1.8; 219 | } 220 | .Sharper-line .Sharper-line--text { 221 | position: relative; 222 | margin: .1em auto; 223 | display: inline-block; 224 | background: #f8f8f8; 225 | padding: 0 15px; 226 | border-radius: 2px; 227 | z-index: 2; 228 | zoom: 1; 229 | -webkit-user-select: none; 230 | -moz-user-select: none; 231 | -ms-user-select: none; 232 | -o-user-select: none; 233 | user-select: none; 234 | } 235 | .Sharper-line .Sharper-line--unique { 236 | position: absolute; 237 | right: 10px; 238 | top: 10px; 239 | left: 10px; 240 | display: block; 241 | border-bottom: 2px solid #f8f8f8; 242 | } 243 | 244 | .Sharper-color--white time { 245 | font-size: 13px; 246 | color: #c0c5c5; 247 | background-color: #f8f8f8; 248 | border-radius: 2px; 249 | font-style: italic; 250 | padding: .2em .7em; 251 | display: table; 252 | font-weight: 400; 253 | -webkit-transition: all .2s ease; 254 | -moz-transition: all .2s ease; 255 | -ms-transition: all .2s ease; 256 | -o-transition: all .2s ease; 257 | transition: all .2s ease; 258 | } 259 | 260 | 261 | a.btn-read-more { 262 | font-size: .85em; 263 | color: #fff; 264 | background-color: #567EBB; 265 | display: inline-block; 266 | font-family: "Roboto", sans-serif; 267 | font-style: italic; 268 | font-weight: 500; 269 | padding: .4em 12px; 270 | -webkit-transition: all .2s ease; 271 | -moz-transition: all .2s ease; 272 | -ms-transition: all .2s ease; 273 | -o-transition: all .2s ease; 274 | transition: all .2s ease; 275 | border-radius: .2em; 276 | line-height: 1.8; 277 | } 278 | a.btn-read-more:hover { 279 | color: #567EBB; 280 | background-color: #fff; 281 | } 282 | 283 | .Sharper-social { 284 | width: 35px; 285 | height: 35px; 286 | background-color: #f5f5f5; 287 | text-align: center; 288 | vertical-align: middle; 289 | display: inline-block; 290 | cursor: pointer; 291 | outline: none; 292 | line-height: 1.8; 293 | margin-left: 5px; 294 | margin-top: -3em; 295 | border: 2px solid #fff; 296 | border-radius: 5px; 297 | -webkit-transition: background-color .2s linear, color .2s linear; 298 | -moz-transition: background-color .2s linear, color .2s linear; 299 | -ms-transition: background-color .2s linear, color .2s linear; 300 | -o-transition: background-color .2s linear, color .2s linear; 301 | transition: background-color .2s linear, color .2s linear; 302 | } 303 | .Sharper-social .fa { 304 | color: #fff; 305 | line-height: 2.33em; 306 | font-size: 1em; 307 | } 308 | .Sharper-social.facebook { 309 | background-color: #3b5998; 310 | } 311 | .Sharper-social.facebook:hover { 312 | background-color: #fff; 313 | -webkit-transition: all .2s ease-in-out; 314 | -moz-transition: all .2s ease-in-out; 315 | -ms-transition: all .2s ease-in-out; 316 | -o-transition: all .2s ease-in-out; 317 | transition: all .2s ease-in-out; 318 | } 319 | .Sharper-social.facebook:hover .fa-facebook { 320 | color: #3b5998; 321 | } 322 | .Sharper-social.twitter { 323 | background-color: #00aced; 324 | } 325 | .Sharper-social.twitter:hover { 326 | background-color: #fff; 327 | } 328 | .Sharper-social.twitter:hover .fa-twitter { 329 | color: #00aced; 330 | } 331 | .Sharper-social.linkedin { 332 | background-color: #0976b4; 333 | } 334 | .Sharper-social.linkedin:hover { 335 | background-color: #fff; 336 | } 337 | .Sharper-social.linkedin:hover .fa-linkedin { 338 | color: #0976b4; 339 | } 340 | 341 | .Sharper-social.googleplus { 342 | background-color: #dd4b39; 343 | } 344 | .Sharper-social.googleplus:hover { 345 | background-color: #fff; 346 | } 347 | .Sharper-social.googleplus:hover .fa-google-plus { 348 | color: #dd4b39; 349 | } 350 | 351 | .Sharper-social.youtube { 352 | background-color: #E35044; 353 | } 354 | .Sharper-social.youtube:hover { 355 | background-color: #fff; 356 | } 357 | .Sharper-social.youtube:hover .fa-youtube { 358 | color: #E35044; 359 | } 360 | 361 | .Sharper-social.instagram { 362 | background-color: #0b96e5; 363 | } 364 | .Sharper-social.instagram:hover { 365 | background-color: #fff; 366 | } 367 | .Sharper-social.instagram:hover .fa-instagram { 368 | color: #517fa4; 369 | } 370 | .Sharper-social.github { 371 | background-color: #444; 372 | } 373 | .Sharper-social.github:hover { 374 | background-color: #fff; 375 | } 376 | .Sharper-social.github:hover .fa-github { 377 | color: #444; 378 | } 379 | .Sharper-social.codepen { 380 | background-color: #333; 381 | } 382 | .Sharper-social.codepen:hover { 383 | background-color: #fff; 384 | } 385 | .Sharper-social.codepen:hover .fa-codepen { 386 | color: #333; 387 | } 388 | 389 | .Sharper-Footer { 390 | position: relative; 391 | margin: 2rem 0 0 0; 392 | padding: 3em 0; 393 | border-top: 1px solid #EBF2F6; 394 | font-family: "Open Sans", sans-serif; 395 | font-size: 14px; 396 | line-height: 1.7em; 397 | color: #BBC7CC; 398 | width: 100%; 399 | text-align: center; 400 | background: #F7FAFB; 401 | } 402 | 403 | .Sharper-Footer a { 404 | color: #BBC7CC; 405 | font-family: "roboto", sans-serif; 406 | font-weight: 500; 407 | } 408 | 409 | .Sharper-Footer a:hover { 410 | color: #50585D; 411 | } 412 | 413 | .Sharper-Footer--inner { 414 | position: relative; 415 | width: 80%; 416 | max-width: 700px; 417 | margin: 0 auto; 418 | } 419 | 420 | @media screen and (max-width: 48em) and (min-width: 42em) { 421 | code { 422 | font-size: 12px; 423 | } 424 | .Sharper-post--read---title { 425 | font-size: 2.37rem; 426 | } 427 | .Sharper-wrapper header h1 { 428 | font-size: 2.60rem; 429 | } 430 | } 431 | 432 | @media screen and (max-width: 32em) { 433 | code { 434 | font-size: 11px; 435 | } 436 | .Sharper-post--read---title { 437 | font-size: 1.67rem; 438 | } 439 | h1 { 440 | font-size: 1.70em; 441 | } 442 | .Sharper-wrapper header h1 { 443 | font-size: 2rem; 444 | } 445 | } -------------------------------------------------------------------------------- /templates/sharper/assets/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;ithis.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); -------------------------------------------------------------------------------- /install.php: -------------------------------------------------------------------------------- 1 | "UTF-8", 82 | "timezone" => "'.$add_timezone.'", 83 | "title" => "'.$add_title.'", 84 | "description" => "'.$add_description.'", 85 | "keywords" => "'.$add_keywords.'", 86 | "url" => "'.$add_url.'", 87 | "email" => "'.$add_email.'", 88 | "template" => "sharper", 89 | "social" => array( 90 | "facebook" => "'.$add_facebook.'", 91 | "twitter" => "'.$add_twitter.'", 92 | "instagram" => "", 93 | "googleplus" => "'.$add_googleplus.'", 94 | "youtube" => "'.$add_youtube.'", 95 | "souncloud" => "", 96 | "linkedin" => "'.$add_linkedin.'", 97 | "dribbble" => "", 98 | "behance" => "", 99 | "codepen" => "", 100 | "github" => "'.$add_github.'", 101 | "bitbucket" => "", 102 | "stackoverflow" => "" 103 | ), 104 | "plugins" => array( 105 | "markdown_syntax" 106 | ), 107 | ); 108 | '); 109 | 110 | // Write in file .htaccess 111 | $htaccess = file_get_contents(".htaccess"); 112 | $savedChanges = str_replace("/%url%/", $rewriteBase, $htaccess); 113 | 114 | $writeHtaccess = fopen (".htaccess", "w"); 115 | fwrite($writeHtaccess, $savedChanges); 116 | fclose($writeHtaccess); 117 | 118 | // Redirect for install done 119 | Swifost::redirect("index.php?install=ready"); 120 | } 121 | ?> 122 | 123 | 124 | 125 | 126 | Swifost › Installation 127 | 128 | 129 | "/> 130 | 131 | 132 | 133 | 134 | 137 | 141 | 142 | 143 | 144 |
145 | 146 |
147 |
148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 |
159 |

160 | http://swifost.com 161 |
162 |
163 |
164 | 165 |
166 |
167 |

Swifost Installation

168 |

State permits required

169 |
170 | 174 |
175 | 176 |
177 |
178 |

Install script writable

179 |
180 |
'; 181 | 182 | } else { 183 | echo ' 184 |
185 |
186 | 187 |
188 |
189 |

Install script not writable

190 |
191 |
'; 192 | } 193 | 194 | if (version_compare(PHP_VERSION, "5.3.0", "<")) { 195 | echo ' 196 |
197 |
198 | 199 |
200 |
201 |

PHP 5.3 or greater is required

202 |
203 |
'; 204 | } else { 205 | echo ' 206 |
207 |
208 | 209 |
210 |
211 |

PHP Version '.PHP_VERSION.'

212 |
213 |
'; 214 | } 215 | 216 | if (function_exists("apache_get_modules")) { 217 | if (!in_array("mod_rewrite", apache_get_modules())) { 218 | echo ' 219 |
220 |
221 | 222 |
223 |
224 |

The module Apache Mod Rewrite available

225 |
226 |
'; 227 | } else { 228 | echo ' 229 |
230 |
231 | 232 |
233 |
234 |

The Module Mod Rewrite is installed

235 |
236 |
'; 237 | } 238 | } else { 239 | echo ' 240 |
241 |
242 | 243 |
244 |
245 |

Module Mod Rewrite is installed

246 |
247 |
'; 248 | } 249 | 250 | if (is_writable(".htaccess")) { 251 | echo ' 252 |
253 |
254 | 255 |
256 |
257 |

You can run .htaccess successfully

258 |
259 |
'; 260 | } else { 261 | echo ' 262 |
263 |
264 | 265 |
266 |
267 |

You can not run .htaccess error

268 |
269 |
'; 270 | } 271 | 272 | foreach ($directoryArray as $dir) { 273 | if (is_writable($dir."/")) { 274 | echo ' 275 |
276 |
277 | 278 |
279 |
280 |

The permissions on '.$dir.' are available

281 |
282 |
'; 283 | } else { 284 | echo ' 285 |
286 |
287 | 288 |
289 |
290 |

You can not read anything '.$dir.' not writable

291 |
292 |
'; 293 | } 294 | } 295 | 296 | ?> 297 | 300 | Continue 301 | 304 |

Do not meet the requirements to install Swifost.

305 | 306 |
307 | 308 |
309 | 310 |
311 |

Information

312 |

Read this information before or after

313 |
314 |
315 | 316 |

For more information have a look at the Swifost documentation at http://swifost.com/documentation.html 317 | 318 | Continue 319 |

320 |
321 | 322 |
323 | 324 |
325 |

Facts page

326 |

This information is important

327 |
328 |
329 |
330 | 419 |
420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 |
434 |
435 | 436 |
437 | 438 | 466 | 467 | 468 | -------------------------------------------------------------------------------- /libraries/Swifost/Swifost.php: -------------------------------------------------------------------------------- 1 | "Title", 79 | "description" => "Description", 80 | "keywords" => "Keywords", 81 | "author" => "Author", 82 | "date" => "Date", 83 | "robots" => "Robots", 84 | "category" => "Category", 85 | "tags" => "Tags", 86 | "image" => "Image", 87 | "template" => "Template" 88 | ); 89 | 90 | /** 91 | * Actions 92 | * 93 | * @var array 94 | */ 95 | private static $actions = array(); 96 | 97 | /** 98 | * Filters 99 | * 100 | * @var array 101 | */ 102 | private static $filters = array(); 103 | 104 | /** 105 | * Plugins global 106 | * 107 | * @var array 108 | */ 109 | private static $plugins = array(); 110 | 111 | 112 | /** 113 | * push method making method chaining possible right off the bat. 114 | * 115 | * @example Swifost::push(); 116 | * 117 | * @access public 118 | */ 119 | public static function push() { 120 | return new static(); 121 | } 122 | 123 | /** 124 | * Running Swifost cms 125 | * 126 | * @example Swifost::push()->run($road); 127 | * 128 | * @param string $road config > road 129 | * @access public 130 | */ 131 | public function run($road) { 132 | 133 | // Load config file 134 | $this->loaderConfig($road); 135 | 136 | // Default timezone 137 | @ini_set("date.timezone", static::$config["timezone"]); 138 | if (function_exists("date_default_timezone_set")) { 139 | date_default_timezone_set(static::$config["timezone"]); 140 | } else { 141 | putenv("TZ=".static::$config["timezone"]); 142 | } 143 | 144 | /** 145 | * Filter URL to prevent Cross-site scripting (XSS) 146 | */ 147 | $this->runFilterUrl(); 148 | 149 | /** 150 | * Send default setHeader and set internal encoding 151 | */ 152 | static::setHeader("Content-Type: text/html; charset=".static::$config["charset"]); 153 | function_exists("mb_language") and mb_language("uni"); 154 | function_exists("mb_regex_encoding") and mb_regex_encoding(static::$config["charset"]); 155 | function_exists("mb_internal_encoding") and mb_internal_encoding(static::$config["charset"]); 156 | 157 | /** 158 | * Gets the current configuration setting of magic_quotes_gpc 159 | * and kill magic quotes 160 | * Reference: http://stackoverflow.com/questions/30346277/securing-mysql-database-query-against-sql-injections/30347087#30347087 161 | */ 162 | if (get_magic_quotes_gpc()) { 163 | function stripSlashesGPC(&$value) { 164 | $value = stripslashes($value); 165 | } 166 | array_walk_recursive($_GET, "stripSlashesGPC"); 167 | array_walk_recursive($_POST, "stripSlashesGPC"); 168 | array_walk_recursive($_REQUEST, "stripslashesGPC"); 169 | array_walk_recursive($_COOKIE, "stripSlashesGPC"); 170 | } 171 | 172 | // Start session 173 | !session_id() and @session_start(); 174 | 175 | // Load all plugins of Swifost 176 | $this->loaderPlugins(); 177 | $this->runDeed("plugins_loaded"); 178 | 179 | // Get current page for requested url 180 | $page = $this->getPage($this->getUrl()); 181 | 182 | // Load page title, description & keywords 183 | empty($page["title"]) and $page["title"] = static::$config["title"]; 184 | empty($page["description"]) and $page["description"] = static::$config["description"]; 185 | empty($page["keywords"]) and $page["keywords"] = static::$config["keywords"]; 186 | 187 | $page = $page; 188 | $config = self::$config; 189 | 190 | // Loading template 191 | $this->runDeed("before_render"); 192 | require TEMPLATES_PATH ."/". $config["template"] . "/". ($template = !empty($page["template"]) ? $page["template"] : "index") .".html"; 193 | $this->runDeed("after_render"); 194 | } 195 | 196 | /** 197 | * Runnig aplication Swifost (config, install sucess) 198 | * 199 | * @example Swifost::push()->ready("config.php", install.php", "index.php", "install", "ready"); 200 | * 201 | * @access public 202 | * @param string $fileConfig, $fileInstall, $redirect, $param and $result 203 | * 204 | */ 205 | public function ready($fileConfig, $fileInstall, $redirect, $param, $result) { 206 | if (file_exists($fileInstall)) { 207 | if (isset($_GET[$param]) && $_GET[$param] == $result) { 208 | // Try to delete install file if not DELETE MANUALLY !!! 209 | @unlink($fileInstall); 210 | 211 | // Redirect to main page 212 | Swifost::redirect($redirect); 213 | 214 | } else { 215 | include $fileInstall; 216 | } 217 | } else { 218 | // Run Swifost Application 219 | self::push()->run($fileConfig); 220 | } 221 | } 222 | 223 | /** 224 | * Insert text or html! 225 | * Pull "echo or print" in the trash! 226 | * Add a (go) into your life with php :D 227 | * 228 | * @example Swifost::go("content here"); 229 | * 230 | * @access public 231 | * @param string $string 232 | * @return string 233 | */ 234 | public static function go($string) { 235 | return print($string); 236 | } 237 | 238 | /** 239 | * Customization and replacement of header() by setHeader() 240 | * For the use and support of the redirect() function 241 | * 242 | * @example setHeader("X-Frame-Options: DENY"); 243 | * @example setHeader("Location: http://example.com"); 244 | * 245 | * @access public 246 | * @param mixed $hds string or array with headers to send. 247 | * @param mixed $hds is headers 248 | */ 249 | public static function setHeader($hds) { 250 | foreach ((array) $hds as $header) { 251 | // Run Header 252 | header((string) $header); 253 | } 254 | } 255 | 256 | /** 257 | * This is simple happy code. 258 | * This gives you a push requests sexy GET and POST. 259 | * support for functions get() & post() 260 | * Returns value from core using dot notation. 261 | * If the key does not exist in the array, the default value will be returned instead. :D 262 | * 263 | * 264 | * @param array $array to extract from (Array) 265 | * @param string $path path (Array) 266 | * @param mixed $core Default (Array) 267 | */ 268 | private static function request($array, $path, $core = null) { 269 | // segments > segms Get segments from path 270 | $segms = explode(".", $path); 271 | foreach ($segms as $segm) { 272 | // Checking 273 | if ( ! is_array($array) || !isset($array[$segm])) { 274 | return $core; 275 | } 276 | $array = $array[$segm]; 277 | } 278 | 279 | return $array; 280 | } 281 | 282 | /** 283 | * POST 284 | * 285 | * @example $var = Swifost::post("test"); 286 | * 287 | * @param string $param (parameter) > Key 288 | */ 289 | public static function post($param) { 290 | return static::request($_POST, $param); 291 | } 292 | 293 | /** 294 | * GET 295 | * 296 | * @example $var = Swifost::get("test"); 297 | * 298 | * @param string $param (parameter) > Key 299 | */ 300 | public static function get($param) { 301 | return static::request($_GET, $param); 302 | } 303 | 304 | /** 305 | * Set one or multiple headers. 306 | * Redirects page sexy to a page specified by the $url argument. 307 | * 308 | * @example Swifost::redirect("http://example.com/"); 309 | * @example Swifost::redirect("test"); 310 | * 311 | * @param string $url_value The URL 312 | * @param boolean $new_tab if true, open the url in a new tab 313 | * @param integer $status Status Code 314 | * @param integer $delay Delay 315 | */ 316 | public function redirect($url_value, $new_tab = FALSE, $status = 302, $delay = null) { 317 | // Redefine Vars 318 | $status = (int) $status; 319 | $url_value = (string) $url_value; 320 | 321 | // Values of status code 322 | $msg_value = array(); 323 | $msg_value[301] = "301 Moved Permanently"; 324 | $msg_value[302] = "302 Found"; 325 | 326 | // Send headers game 327 | if (headers_sent()) { 328 | if ($new_tab == TRUE) { 329 | static::go("\n"); 330 | } else { 331 | static::go("\n"); 332 | } 333 | 334 | } else { 335 | // Redirection header 336 | static::setHeader("HTTP/1.1 " . $status . " " . static::get($msg_value, $status, 302)); 337 | // Delay execution 338 | if ($delay !== null) sleep((int) $delay); 339 | // Redirect ok 340 | static::setHeader("Location: $url_value"); 341 | } 342 | } 343 | 344 | /** 345 | * Get Url 346 | * 347 | * @example $var = Swifost::push()->getUrl(); 348 | * 349 | * @access public 350 | * @return string 351 | */ 352 | public function getUrl() { 353 | // Get request url and script url 354 | $url = ""; 355 | $script = (isset($_SERVER["PHP_SELF"])) ? $_SERVER["PHP_SELF"] : ""; 356 | $request = (isset($_SERVER["REQUEST_URI"])) ? $_SERVER["REQUEST_URI"] : ""; 357 | 358 | // Get our url path and trim the / of the left and the right 359 | if ($request != $script) $url = trim(preg_replace("/". str_replace("/", "\/", str_replace("index.php", "", $script)) ."/", "", $request, 1), "/"); 360 | $url = preg_replace("/\?.*/", "", $url); // Strip query string 361 | 362 | return $url; 363 | } 364 | 365 | /** 366 | * Get Uri Segments (Url segs) 367 | * 368 | * @example $var = Swifost::push()->getUriSegs(); 369 | * 370 | * @access public 371 | * @return array 372 | */ 373 | public function getUriSegs() { 374 | return explode("/", $this->getUrl()); 375 | } 376 | 377 | /** 378 | * Get Uri Segment 379 | * 380 | * @example $var = Swifost::push()->segetUrl(1); 381 | * 382 | * @access public 383 | * @return string 384 | */ 385 | public function segetUrl($segment) { 386 | $segments = $this->getUriSegs(); 387 | return isset($segments[$segment]) ? $segments[$segment] : null; 388 | } 389 | 390 | /** 391 | * Create safe url, to sanitize (Cross-site scripting ). Secure secure..! <3 392 | * 393 | * @example $var = Swifost::push()->filterUrl($url); 394 | * 395 | * @return string 396 | */ 397 | public function filterUrl($string) { 398 | $string = str_replace("/","", $string); 399 | $string = str_replace("\\","", $string); 400 | $string = preg_replace("/[^-a-zA-Z0-9_]/", "", $string); 401 | $string = trim($string); 402 | $string = rawurldecode($string); 403 | $string = str_replace(array('--','"','!','@','#','$','%','^','*','(',')','+','{','}','|',':','"','<','>', 404 | '[',']','\\',';',"'",',','*','+','~','`','laquo','raquo',']>','‘','’','“','”','–','—'), 405 | array('-','-','','','','','','','','','','','','','','','','','','','','','','','','','','',''), 406 | $string); 407 | $string = str_replace("--", "-", $string); 408 | $string = rtrim($string, "-"); 409 | $string = str_replace("..", "", $string); 410 | $string = str_replace("//", "", $string); 411 | $string = preg_replace("/^\//", "", $string); 412 | $string = preg_replace("/^\./", "", $string); 413 | 414 | // Return string 415 | return $string; 416 | } 417 | 418 | /** 419 | * Sanitize URL to prevent XSS - Cross-site scripting 420 | * 421 | * @example Swifost::push()->runFilterUrl(); 422 | * 423 | * @access public 424 | * @return void 425 | */ 426 | public function runFilterUrl() { 427 | $_GET = array_map(array($this, "filterUrl"), $_GET); 428 | } 429 | 430 | 431 | /** 432 | * Get - page 433 | * 434 | * @example $var = Swifost::push()->getPage("about"); 435 | * 436 | * @access public 437 | * @param string $url Url 438 | * @return array 439 | */ 440 | public function getPage($url) { 441 | // Page headers 442 | $dataPage = $this->dataPage; 443 | 444 | // Get the file path 445 | if($url) $file = CONTENT_PATH . "/" . $url; else $file = CONTENT_PATH . "/" ."index"; 446 | 447 | // Load the file 448 | if(is_dir($file)) $file = CONTENT_PATH . "/" . $url ."/index.md"; else $file .= ".md"; 449 | 450 | if (file_exists($file)) { 451 | $content = file_get_contents($file); 452 | } else { 453 | $content = file_get_contents(CONTENT_PATH . "/" . "404.md"); 454 | static::setHeader($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); 455 | } 456 | 457 | $_dataPage = explode(self::SEPARATE, $content); 458 | 459 | foreach ($dataPage as $field => $regex) { 460 | if (preg_match('/^[ \t\/*#@]*' . preg_quote($regex, "/") . ":(.*)$/mi", $_dataPage[0], $match) && $match[1]) { 461 | $page[$field] = trim($match[1]); 462 | } else { 463 | $page[$field] = ""; 464 | } 465 | } 466 | 467 | $url = str_replace(CONTENT_PATH, static::$config["url"], $file); 468 | $url = str_replace("index.md", "", $url); 469 | $url = str_replace(".md", "", $url); 470 | $url = str_replace("\\", "/", $url); 471 | $url = rtrim($url, "/"); 472 | $pages["url"] = $url; 473 | 474 | $_content = $this->parser($content); 475 | if(is_array($_content)) { 476 | $page["content-summary"] = $_content["content-summary"]; 477 | $page["content"] = $_content["content-all"]; 478 | } else { 479 | $page["content-summary"] = $_content; 480 | $page["content"] = $_content; 481 | } 482 | 483 | $page["slug"] = basename($file, ".md"); 484 | 485 | return $page; 486 | } 487 | 488 | /** 489 | * Get pages 490 | * 491 | * @example $var = Swifost::push()->getPages(CONTENT_PATH . "/profile/"); 492 | * 493 | * @access public 494 | * @param string $url > Url 495 | * @param int $limit > Limit of pages 496 | * @param array $ignore > Pages to ignore 497 | * @param string $byOrder > Order by 498 | * @param string $typeOrder > Order type 499 | * @return array 500 | */ 501 | public function getPages($url, $byOrder = "date", $typeOrder = "DESC", $ignore = array("404"), $limit = null) { 502 | 503 | // Page headers 504 | $dataPage = $this->dataPage; 505 | 506 | $pages = $this->getFiles($url); 507 | 508 | foreach($pages as $key => $page) { 509 | 510 | if (!in_array(basename($page, ".md"), $ignore)) { 511 | 512 | $content = file_get_contents($page); 513 | 514 | $_dataPage = explode(self::SEPARATE, $content); 515 | 516 | foreach ($dataPage as $field => $regex) { 517 | if (preg_match("/^[ \t\/*#@]*" . preg_quote($regex, "/") . ":(.*)$/mi", $_dataPage[0], $match) && $match[1]) { 518 | $_pages[$key][ $field ] = trim($match[1]); 519 | } else { 520 | $_pages[$key][$field] = ""; 521 | } 522 | } 523 | 524 | $url = str_replace(CONTENT_PATH, self::$config["url"], $page); 525 | $url = str_replace("index.md", "", $url); 526 | $url = str_replace(".md", "", $url); 527 | $url = str_replace("\\", "/", $url); 528 | $url = rtrim($url, "/"); 529 | $_pages[$key]["url"] = $url; 530 | 531 | $_content = $this->parser($content); 532 | if(is_array($_content)) { 533 | $_pages[$key]["content-summary"] = $_content["content-summary"]; 534 | $_pages[$key]["content"] = $_content["content-all"]; 535 | } else { 536 | $_pages[$key]["content-summary"] = $_content; 537 | $_pages[$key]["content"] = $_content; 538 | } 539 | 540 | $_pages[$key]["slug"] = basename($page, ".md"); 541 | 542 | } 543 | } 544 | 545 | $_pages = $this->subvalSort($_pages, $byOrder, $typeOrder); 546 | 547 | if($limit != null) $_pages = array_slice($_pages, null, $limit); 548 | 549 | // Return pages 550 | return $_pages; 551 | } 552 | 553 | /** 554 | * Get list of files in directory recursive 555 | * 556 | * @example $var = Swifost::push()->getFiles("directory"); 557 | * @example $var = Swifost::push()->getFiles("directory", "txt"); 558 | * @example $var = Swifost::push()->getFiles("directory", array("txt", "log")); 559 | * 560 | * @access public 561 | * @param string $folder Folder 562 | * @param mixed $type Files types 563 | * @return array 564 | */ 565 | public static function getFiles($folder, $type = null) { 566 | $data = array(); 567 | if (is_dir($folder)) { 568 | $iterator = new RecursiveDirectoryIterator($folder); 569 | foreach (new RecursiveIteratorIterator($iterator) as $file) { 570 | if ($type !== null) { 571 | if (is_array($type)) { 572 | $file_ext = substr(strrchr($file->getFilename(), '.'), 1); 573 | if (in_array($file_ext, $type)) { 574 | if (strpos($file->getFilename(), $file_ext, 1)) { 575 | $data[] = $file->getPathName(); 576 | } 577 | } 578 | } else { 579 | if (strpos($file->getFilename(), $type, 1)) { 580 | $data[] = $file->getPathName(); 581 | } 582 | } 583 | } else { 584 | if ($file->getFilename() !== "." && $file->getFilename() !== "..") $data[] = $file->getPathName(); 585 | } 586 | } 587 | 588 | return $data; 589 | } else { 590 | return false; 591 | } 592 | } 593 | 594 | /** 595 | * obEval 596 | */ 597 | protected static function obEval($value) { 598 | ob_start(); 599 | eval($value[1]); 600 | $value = ob_get_contents(); 601 | ob_end_clean(); 602 | 603 | return $value; 604 | } 605 | 606 | /** 607 | * evalPHP 608 | */ 609 | protected static function evalPHP($string) { 610 | return preg_replace_callback("/\{php\}(.*?)\{\/php\}/ms","self::obEval", $string); 611 | } 612 | 613 | /** 614 | * Content Parser 615 | * 616 | * @param string $content Content to parse 617 | * @return string $content Formatted content 618 | */ 619 | protected function parser($content) { 620 | // Parse Content after Headers 621 | $_content = ""; 622 | $i = 0; 623 | foreach (explode(self::SEPARATE, $content) as $c) { 624 | ($i++!=0) and $_content .= $c; 625 | } 626 | 627 | $content = $_content; 628 | 629 | // Parse --url-- 630 | $content = str_replace("--url--", static::$config["url"], $_content); 631 | 632 | // Parse --version-- 633 | $content = str_replace("--version--", self::VERSION, $content); 634 | 635 | // Parse --software-- 636 | $content = str_replace("--software--", self::SOFTWARE, $content); 637 | 638 | // Parse --author-- 639 | $content = str_replace("--author--", self::AUTHOR, $content); 640 | 641 | // Parse --summary-- 642 | $summary = strpos($content, "--summary--"); 643 | if ($summary === false) { 644 | $content = $this->applyFilter("content", $content); 645 | } else { 646 | $content = explode("--summary--", $content); 647 | $content["content-summary"] = $this->applyFilter("content", $content[0]); 648 | $content["content-all"] = $this->applyFilter("content", $content[0].$content[1]); 649 | } 650 | 651 | $content = self::evalPHP($content); 652 | 653 | // Return content 654 | return $content; 655 | } 656 | 657 | /** 658 | * Load Config 659 | */ 660 | protected function loaderConfig($pathFile) { 661 | if (file_exists($pathFile)) static::$config = require $pathFile; 662 | else 663 | die("Hey, where is the configuration file ?"); 664 | } 665 | 666 | /** 667 | * Load Plugins 668 | */ 669 | protected function loaderPlugins() { 670 | foreach (static::$config["plugins"] as $plugin) { 671 | include_once PLUGINS_PATH ."/". $plugin."/".$plugin.".php"; 672 | } 673 | } 674 | 675 | /** 676 | * Hooks a function on to a specific action. 677 | * 678 | * // Hooks a function "newLink" on to a "footer" action. 679 | * Swifost::push()->createDeed('footer', "href", 10); 680 | * 681 | * function href() { 682 | * echo 'My link'; 683 | * } 684 | * 685 | * @access public 686 | * @param string $action_name Action name 687 | * @param mixed $added_function Added function 688 | * @param integer $priority Priority. Default is 10 689 | * @param array $args Arguments 690 | */ 691 | public function createDeed($action_name, $added_function, $priority = 10, array $args = null) { 692 | // Hooks a function on to a specific action. 693 | static::$actions[] = array( 694 | "action_name" => (string) $action_name, 695 | "function" => $added_function, 696 | "priority" => (int) $priority, 697 | "args" => $args 698 | ); 699 | } 700 | 701 | /** 702 | * Run functions hooked on a specific action hook. 703 | * 704 | * // Run functions hooked on a "footer" action hook. 705 | * @example Swifost::push()->runDeed('footer'); 706 | * 707 | * @access public 708 | * @param string $action_name Action name 709 | * @param array $args Arguments 710 | * @param boolean $return Return data or not. Default is false 711 | * @return mixed 712 | */ 713 | public function runDeed($action_name, $args = array(), $return = false) { 714 | // Redefine arguments 715 | $action_name = (string) $action_name; 716 | $return = (bool) $return; 717 | 718 | // Run action 719 | if (count(static::$actions) > 0) { 720 | 721 | // Sort actions by priority 722 | $actions = $this->subvalSort(static::$actions, "priority"); 723 | 724 | // Loop through $actions array 725 | foreach ($actions as $action) { 726 | 727 | // Execute specific action 728 | if ($action["action_name"] == $action_name) { 729 | 730 | // isset arguments ? 731 | if (isset($args)) { 732 | 733 | // Return or Render specific action results ? 734 | if ($return) { 735 | return call_user_func_array($action["function"], $args); 736 | } else { 737 | call_user_func_array($action["function"], $args); 738 | } 739 | 740 | } else { 741 | 742 | if ($return) { 743 | return call_user_func_array($action["function"], $action["args"]); 744 | } else { 745 | call_user_func_array($action["function"], $action["args"]); 746 | } 747 | 748 | } 749 | 750 | } 751 | 752 | } 753 | 754 | } 755 | 756 | } 757 | 758 | /** 759 | * Apply filters 760 | * 761 | * @example Swifost::push()->applyFilter("content", $content); 762 | * 763 | * @access public 764 | * @param string $nameFilter The name of the filter hook. 765 | * @param mixed $value The value on which the filters hooked. 766 | * @return mixed 767 | */ 768 | public function applyFilter($nameFilter, $value) { 769 | // Redefine arguments 770 | $nameFilter = (string) $nameFilter; 771 | 772 | $args = array_slice(func_get_args(), 2); 773 | 774 | if (!isset(static::$filters[$nameFilter])) { 775 | return $value; 776 | } 777 | 778 | foreach (static::$filters[$nameFilter] as $priority => $functions) { 779 | if ( ! is_null($functions)) { 780 | foreach ($functions as $function) { 781 | $allArgs = array_merge(array($value), $args); 782 | $functionName = $function["function"]; 783 | $acceptArgs = $function["acceptArgs"]; 784 | if ($acceptArgs == 1) { 785 | $theArgs = array($value); 786 | } elseif ($acceptArgs > 1) { 787 | $theArgs = array_slice($allArgs, 0, $acceptArgs); 788 | } elseif ($acceptArgs == 0) { 789 | $theArgs = null; 790 | } else { 791 | $theArgs = $allArgs; 792 | } 793 | $value = call_user_func_array($functionName, $theArgs); 794 | } 795 | } 796 | } 797 | 798 | // Return value 799 | return $value; 800 | } 801 | 802 | /** 803 | * Create filter 804 | * 805 | * @example Swifost::push()->createFilter("content", "replace"); 806 | * 807 | * ------------------------------------------------ 808 | * 809 | * function replace($content) { 810 | * return preg_replace(array('/\[b\](.*?)\[\/b\]/ms'), array('\1'), $content); 811 | * } 812 | * 813 | * @access public 814 | * @param integer $priority > Function to add priority, default is 10. 815 | * @param integer $acceptArgs > The number of arguments the function accept default is 1. 816 | * @param string $nameFilter > The name of the filter to hook the $createFunction to. 817 | * @param string $createFunction > The name of the function to be called when the filter is applied. 818 | * @return boolean 819 | */ 820 | public function createFilter($nameFilter, $addFunc, $priority = 10, $acceptArgs = 1) { 821 | $nameFilter = (string) $nameFilter; 822 | $addFunc = $addFunc; 823 | $priority = (int) $priority; 824 | $acceptArgs = (int) $acceptArgs; 825 | 826 | // Thanks to WordPress 827 | if (isset(static::$filters[$nameFilter]["$priority"])) { 828 | foreach (static::$filters[$nameFilter]["$priority"] as $filter) { 829 | if ($filter["function"] == $addFunc) { 830 | return true; 831 | } 832 | } 833 | } 834 | 835 | static::$filters[$nameFilter]["$priority"][] = array("function" => $addFunc, "acceptArgs" => $acceptArgs); 836 | 837 | // Sorting 838 | ksort(static::$filters[$nameFilter]["$priority"]); 839 | 840 | return true; 841 | } 842 | 843 | /** 844 | * HashToken Random generator for all. 845 | * 846 | * @example Swifost::hashToken(); 847 | * 848 | * @access public 849 | * @return string 850 | */ 851 | public static function hashToken($numberValue) { 852 | // hash pernalize value $numberValue 853 | $string = substr(md5(uniqid(mt_rand(), true)), 0, $numberValue); 854 | 855 | // Return hash 856 | return $string; 857 | } 858 | 859 | /** 860 | * Generate and store a unique token which can be used to help prevent 861 | * Information: (http://wikipedia.org/wiki/Cross_Site_Request_Forgery). 862 | * 863 | * 864 | * You can insert this token into your forms as a hidden field: 865 | * 866 | * @example $token = Swifost::push()->createToken(); 867 | * @example 868 | * @example use formToken(); 869 | * 870 | * This provides a basic, but effective, method of preventing CSRF attacks. 871 | * 872 | * @param boolean $newToken force a new token to be generated?. Default is false 873 | * @return string 874 | */ 875 | public function createToken($newToken = false) { 876 | // Get the current token 877 | if (isset($_SESSION[(string) self::$csrf_token])) $token = $_SESSION[(string) self::$csrf_token]; else $token = null; 878 | 879 | // Generate a new token (random) 880 | if ($newToken === true or ! $token) { 881 | 882 | // Generate a new unique token 883 | $token = static::hashToken(16); 884 | 885 | // Store the new token 886 | $_SESSION[(string) self::$csrf_token] = $token; 887 | } 888 | 889 | // Return 890 | return $token; 891 | } 892 | 893 | /** 894 | * Add input to implement CSRF 895 | * 896 | * Hidden field: 897 | * 898 | * @example $var = Swifost::push()->formToken(); 899 | * 900 | * True is token automatic: 901 | * @example $var = Swifost::push()->formToken(true); 902 | * 903 | * False is token static: 904 | * @example $var = Swifost::push()->formToken(false); 905 | * 906 | * This provides a basic, but effective, method of preventing CSRF attacks. 907 | * 908 | * @param $stateToken 909 | * @return string 910 | */ 911 | public function formToken($stateToken) { 912 | 913 | // Generate option Token 914 | $csrf_token = self::push()->createToken($stateToken); 915 | 916 | // CSRF name for default 917 | $csrf_name = static::$csrf_token; 918 | 919 | // CSRF input 920 | $string = ''; 921 | 922 | return $string; 923 | } 924 | 925 | /** 926 | * Check that the given token matches the currently stored security token. 927 | * 928 | * @example Implementation 929 | * ------------------------------------------------- 930 | * if (Swifost::push()->checkToken($token)) { 931 | * // So so so so... 932 | * } 933 | * ------------------------------------------------- 934 | * 935 | * @param string $token token to check 936 | * @return boolean 937 | */ 938 | public function checkToken($token) { 939 | return self::push()->createToken() === $token; 940 | } 941 | 942 | 943 | /** 944 | * This is to prevent (null) character between ascii characters. 945 | * remove Invisible Characters! 946 | * Seriously this help too! 947 | * function support for xssClean(); 948 | * 949 | * @param string $string 950 | */ 951 | private static function cleanChar($string) { 952 | // no displayables 953 | $noyables = array('/\x0b/','/\x0c/','/%1[0-9a-f]/','/[\x00-\x08]/','/[\x0e-\x1f]/','/%0[0-8bcef]/'); 954 | 955 | do { 956 | $clean = $string; 957 | $string = preg_replace($noyables, "", $string); 958 | } while ($clean != $string); 959 | return $string; 960 | } 961 | 962 | /** 963 | * Convert special characters to HTML entities. 964 | * Trick to avoid the injections and XSS execution (Sanitize). 965 | * This function is xssClean() support! 966 | * 967 | * 968 | * @param boolean $encode Encode existing entities 969 | * @param string $value String to convert 970 | * @return string 971 | */ 972 | private static function cleanString($value, $encode = true) { 973 | return htmlentities((string) $value, ENT_QUOTES, "utf-8", $encode); 974 | } 975 | 976 | /** 977 | * Sanitize data to prevent (XSS) - Cross-site scripting! 978 | * XSS information: (http://wikipedia.org/wiki/Cross-site_scripting) 979 | * 980 | * @example Swifost::push()->xssClean(""); 981 | * 982 | * @param string $string 983 | */ 984 | public function xssClean($string) { 985 | // Convert html to plain text & Remove invisible characters 986 | $string = static::cleanChar($string); 987 | $string = static::cleanString($string); 988 | 989 | return $string; 990 | } 991 | 992 | /** 993 | * Subval sort 994 | * 995 | * @example $var = Swifost::push()->subvalSort($old_array, "sort"); 996 | * 997 | * @access public 998 | * @param array $a Array 999 | * @param string $subkey Key 1000 | * @param string $order Order type DESC or ASC 1001 | * @return array 1002 | */ 1003 | public function subvalSort($a, $subkey, $order = null) { 1004 | if (count($a) != 0 || (!empty($a))) { 1005 | foreach ($a as $k => $v) $b[$k] = function_exists("mb_strtolower") ? mb_strtolower($v[$subkey]) : strtolower($v[$subkey]); 1006 | if ($order == null || $order == "ASC") asort($b); else if ($order == "DESC") arsort($b); 1007 | foreach ($b as $key => $val) $c[] = $a[$key]; 1008 | 1009 | return $c; 1010 | } 1011 | } 1012 | 1013 | } -------------------------------------------------------------------------------- /plugins/markdown_syntax/parsedown/Parsedown.php: -------------------------------------------------------------------------------- 1 | DefinitionData = array(); 27 | 28 | # standardize line breaks 29 | $text = str_replace(array("\r\n", "\r"), "\n", $text); 30 | 31 | # remove surrounding line breaks 32 | $text = trim($text, "\n"); 33 | 34 | # split text into lines 35 | $lines = explode("\n", $text); 36 | 37 | # iterate through lines to identify blocks 38 | $markup = $this->lines($lines); 39 | 40 | # trim line breaks 41 | $markup = trim($markup, "\n"); 42 | 43 | return $markup; 44 | } 45 | 46 | # 47 | # Setters 48 | # 49 | 50 | function setBreaksEnabled($breaksEnabled) 51 | { 52 | $this->breaksEnabled = $breaksEnabled; 53 | 54 | return $this; 55 | } 56 | 57 | protected $breaksEnabled; 58 | 59 | function setMarkupEscaped($markupEscaped) 60 | { 61 | $this->markupEscaped = $markupEscaped; 62 | 63 | return $this; 64 | } 65 | 66 | protected $markupEscaped; 67 | 68 | function setUrlsLinked($urlsLinked) 69 | { 70 | $this->urlsLinked = $urlsLinked; 71 | 72 | return $this; 73 | } 74 | 75 | protected $urlsLinked = true; 76 | 77 | # 78 | # Lines 79 | # 80 | 81 | protected $BlockTypes = array( 82 | '#' => array('Header'), 83 | '*' => array('Rule', 'List'), 84 | '+' => array('List'), 85 | '-' => array('SetextHeader', 'Table', 'Rule', 'List'), 86 | '0' => array('List'), 87 | '1' => array('List'), 88 | '2' => array('List'), 89 | '3' => array('List'), 90 | '4' => array('List'), 91 | '5' => array('List'), 92 | '6' => array('List'), 93 | '7' => array('List'), 94 | '8' => array('List'), 95 | '9' => array('List'), 96 | ':' => array('Table'), 97 | '<' => array('Comment', 'Markup'), 98 | '=' => array('SetextHeader'), 99 | '>' => array('Quote'), 100 | '[' => array('Reference'), 101 | '_' => array('Rule'), 102 | '`' => array('FencedCode'), 103 | '|' => array('Table'), 104 | '~' => array('FencedCode'), 105 | ); 106 | 107 | # ~ 108 | 109 | protected $unmarkedBlockTypes = array( 110 | 'Code', 111 | ); 112 | 113 | # 114 | # Blocks 115 | # 116 | 117 | private function lines(array $lines) 118 | { 119 | $CurrentBlock = null; 120 | 121 | foreach ($lines as $line) 122 | { 123 | if (chop($line) === '') 124 | { 125 | if (isset($CurrentBlock)) 126 | { 127 | $CurrentBlock['interrupted'] = true; 128 | } 129 | 130 | continue; 131 | } 132 | 133 | if (strpos($line, "\t") !== false) 134 | { 135 | $parts = explode("\t", $line); 136 | 137 | $line = $parts[0]; 138 | 139 | unset($parts[0]); 140 | 141 | foreach ($parts as $part) 142 | { 143 | $shortage = 4 - mb_strlen($line, 'utf-8') % 4; 144 | 145 | $line .= str_repeat(' ', $shortage); 146 | $line .= $part; 147 | } 148 | } 149 | 150 | $indent = 0; 151 | 152 | while (isset($line[$indent]) and $line[$indent] === ' ') 153 | { 154 | $indent ++; 155 | } 156 | 157 | $text = $indent > 0 ? substr($line, $indent) : $line; 158 | 159 | # ~ 160 | 161 | $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); 162 | 163 | # ~ 164 | 165 | if (isset($CurrentBlock['incomplete'])) 166 | { 167 | $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); 168 | 169 | if (isset($Block)) 170 | { 171 | $CurrentBlock = $Block; 172 | 173 | continue; 174 | } 175 | else 176 | { 177 | if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) 178 | { 179 | $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); 180 | } 181 | 182 | unset($CurrentBlock['incomplete']); 183 | } 184 | } 185 | 186 | # ~ 187 | 188 | $marker = $text[0]; 189 | 190 | # ~ 191 | 192 | $blockTypes = $this->unmarkedBlockTypes; 193 | 194 | if (isset($this->BlockTypes[$marker])) 195 | { 196 | foreach ($this->BlockTypes[$marker] as $blockType) 197 | { 198 | $blockTypes []= $blockType; 199 | } 200 | } 201 | 202 | # 203 | # ~ 204 | 205 | foreach ($blockTypes as $blockType) 206 | { 207 | $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); 208 | 209 | if (isset($Block)) 210 | { 211 | $Block['type'] = $blockType; 212 | 213 | if ( ! isset($Block['identified'])) 214 | { 215 | $Blocks []= $CurrentBlock; 216 | 217 | $Block['identified'] = true; 218 | } 219 | 220 | if (method_exists($this, 'block'.$blockType.'Continue')) 221 | { 222 | $Block['incomplete'] = true; 223 | } 224 | 225 | $CurrentBlock = $Block; 226 | 227 | continue 2; 228 | } 229 | } 230 | 231 | # ~ 232 | 233 | if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) 234 | { 235 | $CurrentBlock['element']['text'] .= "\n".$text; 236 | } 237 | else 238 | { 239 | $Blocks []= $CurrentBlock; 240 | 241 | $CurrentBlock = $this->paragraph($Line); 242 | 243 | $CurrentBlock['identified'] = true; 244 | } 245 | } 246 | 247 | # ~ 248 | 249 | if (isset($CurrentBlock['incomplete']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) 250 | { 251 | $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); 252 | } 253 | 254 | # ~ 255 | 256 | $Blocks []= $CurrentBlock; 257 | 258 | unset($Blocks[0]); 259 | 260 | # ~ 261 | 262 | $markup = ''; 263 | 264 | foreach ($Blocks as $Block) 265 | { 266 | if (isset($Block['hidden'])) 267 | { 268 | continue; 269 | } 270 | 271 | $markup .= "\n"; 272 | $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); 273 | } 274 | 275 | $markup .= "\n"; 276 | 277 | # ~ 278 | 279 | return $markup; 280 | } 281 | 282 | # 283 | # Code 284 | 285 | protected function blockCode($Line, $Block = null) 286 | { 287 | if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) 288 | { 289 | return; 290 | } 291 | 292 | if ($Line['indent'] >= 4) 293 | { 294 | $text = substr($Line['body'], 4); 295 | 296 | $Block = array( 297 | 'element' => array( 298 | 'name' => 'pre', 299 | 'handler' => 'element', 300 | 'text' => array( 301 | 'name' => 'code', 302 | 'text' => $text, 303 | ), 304 | ), 305 | ); 306 | 307 | return $Block; 308 | } 309 | } 310 | 311 | protected function blockCodeContinue($Line, $Block) 312 | { 313 | if ($Line['indent'] >= 4) 314 | { 315 | if (isset($Block['interrupted'])) 316 | { 317 | $Block['element']['text']['text'] .= "\n"; 318 | 319 | unset($Block['interrupted']); 320 | } 321 | 322 | $Block['element']['text']['text'] .= "\n"; 323 | 324 | $text = substr($Line['body'], 4); 325 | 326 | $Block['element']['text']['text'] .= $text; 327 | 328 | return $Block; 329 | } 330 | } 331 | 332 | protected function blockCodeComplete($Block) 333 | { 334 | $text = $Block['element']['text']['text']; 335 | 336 | $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); 337 | 338 | $Block['element']['text']['text'] = $text; 339 | 340 | return $Block; 341 | } 342 | 343 | # 344 | # Comment 345 | 346 | protected function blockComment($Line) 347 | { 348 | if ($this->markupEscaped) 349 | { 350 | return; 351 | } 352 | 353 | if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') 354 | { 355 | $Block = array( 356 | 'markup' => $Line['body'], 357 | ); 358 | 359 | if (preg_match('/-->$/', $Line['text'])) 360 | { 361 | $Block['closed'] = true; 362 | } 363 | 364 | return $Block; 365 | } 366 | } 367 | 368 | protected function blockCommentContinue($Line, array $Block) 369 | { 370 | if (isset($Block['closed'])) 371 | { 372 | return; 373 | } 374 | 375 | $Block['markup'] .= "\n" . $Line['body']; 376 | 377 | if (preg_match('/-->$/', $Line['text'])) 378 | { 379 | $Block['closed'] = true; 380 | } 381 | 382 | return $Block; 383 | } 384 | 385 | # 386 | # Fenced Code 387 | 388 | protected function blockFencedCode($Line) 389 | { 390 | if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches)) 391 | { 392 | $Element = array( 393 | 'name' => 'code', 394 | 'text' => '', 395 | ); 396 | 397 | if (isset($matches[2])) 398 | { 399 | $class = 'language-'.$matches[2]; 400 | 401 | $Element['attributes'] = array( 402 | 'class' => $class, 403 | ); 404 | } 405 | 406 | $Block = array( 407 | 'char' => $Line['text'][0], 408 | 'element' => array( 409 | 'name' => 'pre', 410 | 'handler' => 'element', 411 | 'text' => $Element, 412 | ), 413 | ); 414 | 415 | return $Block; 416 | } 417 | } 418 | 419 | protected function blockFencedCodeContinue($Line, $Block) 420 | { 421 | if (isset($Block['complete'])) 422 | { 423 | return; 424 | } 425 | 426 | if (isset($Block['interrupted'])) 427 | { 428 | $Block['element']['text']['text'] .= "\n"; 429 | 430 | unset($Block['interrupted']); 431 | } 432 | 433 | if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) 434 | { 435 | $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); 436 | 437 | $Block['complete'] = true; 438 | 439 | return $Block; 440 | } 441 | 442 | $Block['element']['text']['text'] .= "\n".$Line['body'];; 443 | 444 | return $Block; 445 | } 446 | 447 | protected function blockFencedCodeComplete($Block) 448 | { 449 | $text = $Block['element']['text']['text']; 450 | 451 | $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); 452 | 453 | $Block['element']['text']['text'] = $text; 454 | 455 | return $Block; 456 | } 457 | 458 | # 459 | # Header 460 | 461 | protected function blockHeader($Line) 462 | { 463 | if (isset($Line['text'][1])) 464 | { 465 | $level = 1; 466 | 467 | while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') 468 | { 469 | $level ++; 470 | } 471 | 472 | if ($level > 6) 473 | { 474 | return; 475 | } 476 | 477 | $text = trim($Line['text'], '# '); 478 | 479 | $Block = array( 480 | 'element' => array( 481 | 'name' => 'h' . min(6, $level), 482 | 'text' => $text, 483 | 'handler' => 'line', 484 | ), 485 | ); 486 | 487 | return $Block; 488 | } 489 | } 490 | 491 | # 492 | # List 493 | 494 | protected function blockList($Line) 495 | { 496 | list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); 497 | 498 | if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) 499 | { 500 | $Block = array( 501 | 'indent' => $Line['indent'], 502 | 'pattern' => $pattern, 503 | 'element' => array( 504 | 'name' => $name, 505 | 'handler' => 'elements', 506 | ), 507 | ); 508 | 509 | $Block['li'] = array( 510 | 'name' => 'li', 511 | 'handler' => 'li', 512 | 'text' => array( 513 | $matches[2], 514 | ), 515 | ); 516 | 517 | $Block['element']['text'] []= & $Block['li']; 518 | 519 | return $Block; 520 | } 521 | } 522 | 523 | protected function blockListContinue($Line, array $Block) 524 | { 525 | if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) 526 | { 527 | if (isset($Block['interrupted'])) 528 | { 529 | $Block['li']['text'] []= ''; 530 | 531 | unset($Block['interrupted']); 532 | } 533 | 534 | unset($Block['li']); 535 | 536 | $text = isset($matches[1]) ? $matches[1] : ''; 537 | 538 | $Block['li'] = array( 539 | 'name' => 'li', 540 | 'handler' => 'li', 541 | 'text' => array( 542 | $text, 543 | ), 544 | ); 545 | 546 | $Block['element']['text'] []= & $Block['li']; 547 | 548 | return $Block; 549 | } 550 | 551 | if ($Line['text'][0] === '[' and $this->blockReference($Line)) 552 | { 553 | return $Block; 554 | } 555 | 556 | if ( ! isset($Block['interrupted'])) 557 | { 558 | $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); 559 | 560 | $Block['li']['text'] []= $text; 561 | 562 | return $Block; 563 | } 564 | 565 | if ($Line['indent'] > 0) 566 | { 567 | $Block['li']['text'] []= ''; 568 | 569 | $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); 570 | 571 | $Block['li']['text'] []= $text; 572 | 573 | unset($Block['interrupted']); 574 | 575 | return $Block; 576 | } 577 | } 578 | 579 | # 580 | # Quote 581 | 582 | protected function blockQuote($Line) 583 | { 584 | if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) 585 | { 586 | $Block = array( 587 | 'element' => array( 588 | 'name' => 'blockquote', 589 | 'handler' => 'lines', 590 | 'text' => (array) $matches[1], 591 | ), 592 | ); 593 | 594 | return $Block; 595 | } 596 | } 597 | 598 | protected function blockQuoteContinue($Line, array $Block) 599 | { 600 | if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) 601 | { 602 | if (isset($Block['interrupted'])) 603 | { 604 | $Block['element']['text'] []= ''; 605 | 606 | unset($Block['interrupted']); 607 | } 608 | 609 | $Block['element']['text'] []= $matches[1]; 610 | 611 | return $Block; 612 | } 613 | 614 | if ( ! isset($Block['interrupted'])) 615 | { 616 | $Block['element']['text'] []= $Line['text']; 617 | 618 | return $Block; 619 | } 620 | } 621 | 622 | # 623 | # Rule 624 | 625 | protected function blockRule($Line) 626 | { 627 | if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) 628 | { 629 | $Block = array( 630 | 'element' => array( 631 | 'name' => 'hr' 632 | ), 633 | ); 634 | 635 | return $Block; 636 | } 637 | } 638 | 639 | # 640 | # Setext 641 | 642 | protected function blockSetextHeader($Line, array $Block = null) 643 | { 644 | if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) 645 | { 646 | return; 647 | } 648 | 649 | if (chop($Line['text'], $Line['text'][0]) === '') 650 | { 651 | $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; 652 | 653 | return $Block; 654 | } 655 | } 656 | 657 | # 658 | # Markup 659 | 660 | protected function blockMarkup($Line) 661 | { 662 | if ($this->markupEscaped) 663 | { 664 | return; 665 | } 666 | 667 | if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) 668 | { 669 | if (in_array($matches[1], $this->textLevelElements)) 670 | { 671 | return; 672 | } 673 | 674 | $Block = array( 675 | 'name' => $matches[1], 676 | 'depth' => 0, 677 | 'markup' => $Line['text'], 678 | ); 679 | 680 | $length = strlen($matches[0]); 681 | 682 | $remainder = substr($Line['text'], $length); 683 | 684 | if (trim($remainder) === '') 685 | { 686 | if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) 687 | { 688 | $Block['closed'] = true; 689 | 690 | $Block['void'] = true; 691 | } 692 | } 693 | else 694 | { 695 | if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) 696 | { 697 | return; 698 | } 699 | 700 | if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) 701 | { 702 | $Block['closed'] = true; 703 | } 704 | } 705 | 706 | return $Block; 707 | } 708 | } 709 | 710 | protected function blockMarkupContinue($Line, array $Block) 711 | { 712 | if (isset($Block['closed'])) 713 | { 714 | return; 715 | } 716 | 717 | if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open 718 | { 719 | $Block['depth'] ++; 720 | } 721 | 722 | if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close 723 | { 724 | if ($Block['depth'] > 0) 725 | { 726 | $Block['depth'] --; 727 | } 728 | else 729 | { 730 | $Block['closed'] = true; 731 | } 732 | } 733 | 734 | if (isset($Block['interrupted'])) 735 | { 736 | $Block['markup'] .= "\n"; 737 | 738 | unset($Block['interrupted']); 739 | } 740 | 741 | $Block['markup'] .= "\n".$Line['body']; 742 | 743 | return $Block; 744 | } 745 | 746 | # 747 | # Reference 748 | 749 | protected function blockReference($Line) 750 | { 751 | if (preg_match('/^\[(.+?)\]:[ ]*?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) 752 | { 753 | $id = strtolower($matches[1]); 754 | 755 | $Data = array( 756 | 'url' => $matches[2], 757 | 'title' => null, 758 | ); 759 | 760 | if (isset($matches[3])) 761 | { 762 | $Data['title'] = $matches[3]; 763 | } 764 | 765 | $this->DefinitionData['Reference'][$id] = $Data; 766 | 767 | $Block = array( 768 | 'hidden' => true, 769 | ); 770 | 771 | return $Block; 772 | } 773 | } 774 | 775 | # 776 | # Table 777 | 778 | protected function blockTable($Line, array $Block = null) 779 | { 780 | if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) 781 | { 782 | return; 783 | } 784 | 785 | if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') 786 | { 787 | $alignments = array(); 788 | 789 | $divider = $Line['text']; 790 | 791 | $divider = trim($divider); 792 | $divider = trim($divider, '|'); 793 | 794 | $dividerCells = explode('|', $divider); 795 | 796 | foreach ($dividerCells as $dividerCell) 797 | { 798 | $dividerCell = trim($dividerCell); 799 | 800 | if ($dividerCell === '') 801 | { 802 | continue; 803 | } 804 | 805 | $alignment = null; 806 | 807 | if ($dividerCell[0] === ':') 808 | { 809 | $alignment = 'left'; 810 | } 811 | 812 | if (substr($dividerCell, - 1) === ':') 813 | { 814 | $alignment = $alignment === 'left' ? 'center' : 'right'; 815 | } 816 | 817 | $alignments []= $alignment; 818 | } 819 | 820 | # ~ 821 | 822 | $HeaderElements = array(); 823 | 824 | $header = $Block['element']['text']; 825 | 826 | $header = trim($header); 827 | $header = trim($header, '|'); 828 | 829 | $headerCells = explode('|', $header); 830 | 831 | foreach ($headerCells as $index => $headerCell) 832 | { 833 | $headerCell = trim($headerCell); 834 | 835 | $HeaderElement = array( 836 | 'name' => 'th', 837 | 'text' => $headerCell, 838 | 'handler' => 'line', 839 | ); 840 | 841 | if (isset($alignments[$index])) 842 | { 843 | $alignment = $alignments[$index]; 844 | 845 | $HeaderElement['attributes'] = array( 846 | 'style' => 'text-align: '.$alignment.';', 847 | ); 848 | } 849 | 850 | $HeaderElements []= $HeaderElement; 851 | } 852 | 853 | # ~ 854 | 855 | $Block = array( 856 | 'alignments' => $alignments, 857 | 'identified' => true, 858 | 'element' => array( 859 | 'name' => 'table', 860 | 'handler' => 'elements', 861 | ), 862 | ); 863 | 864 | $Block['element']['text'] []= array( 865 | 'name' => 'thead', 866 | 'handler' => 'elements', 867 | ); 868 | 869 | $Block['element']['text'] []= array( 870 | 'name' => 'tbody', 871 | 'handler' => 'elements', 872 | 'text' => array(), 873 | ); 874 | 875 | $Block['element']['text'][0]['text'] []= array( 876 | 'name' => 'tr', 877 | 'handler' => 'elements', 878 | 'text' => $HeaderElements, 879 | ); 880 | 881 | return $Block; 882 | } 883 | } 884 | 885 | protected function blockTableContinue($Line, array $Block) 886 | { 887 | if (isset($Block['interrupted'])) 888 | { 889 | return; 890 | } 891 | 892 | if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) 893 | { 894 | $Elements = array(); 895 | 896 | $row = $Line['text']; 897 | 898 | $row = trim($row); 899 | $row = trim($row, '|'); 900 | 901 | preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); 902 | 903 | foreach ($matches[0] as $index => $cell) 904 | { 905 | $cell = trim($cell); 906 | 907 | $Element = array( 908 | 'name' => 'td', 909 | 'handler' => 'line', 910 | 'text' => $cell, 911 | ); 912 | 913 | if (isset($Block['alignments'][$index])) 914 | { 915 | $Element['attributes'] = array( 916 | 'style' => 'text-align: '.$Block['alignments'][$index].';', 917 | ); 918 | } 919 | 920 | $Elements []= $Element; 921 | } 922 | 923 | $Element = array( 924 | 'name' => 'tr', 925 | 'handler' => 'elements', 926 | 'text' => $Elements, 927 | ); 928 | 929 | $Block['element']['text'][1]['text'] []= $Element; 930 | 931 | return $Block; 932 | } 933 | } 934 | 935 | # 936 | # ~ 937 | # 938 | 939 | protected function paragraph($Line) 940 | { 941 | $Block = array( 942 | 'element' => array( 943 | 'name' => 'p', 944 | 'text' => $Line['text'], 945 | 'handler' => 'line', 946 | ), 947 | ); 948 | 949 | return $Block; 950 | } 951 | 952 | # 953 | # Inline Elements 954 | # 955 | 956 | protected $InlineTypes = array( 957 | '"' => array('SpecialCharacter'), 958 | '!' => array('Image'), 959 | '&' => array('SpecialCharacter'), 960 | '*' => array('Emphasis'), 961 | ':' => array('Url'), 962 | '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), 963 | '>' => array('SpecialCharacter'), 964 | '[' => array('Link'), 965 | '_' => array('Emphasis'), 966 | '`' => array('Code'), 967 | '~' => array('Strikethrough'), 968 | '\\' => array('EscapeSequence'), 969 | ); 970 | 971 | # ~ 972 | 973 | protected $inlineMarkerList = '!"*_&[:<>`~\\'; 974 | 975 | # 976 | # ~ 977 | # 978 | 979 | public function line($text) 980 | { 981 | $markup = ''; 982 | 983 | $unexaminedText = $text; 984 | 985 | $markerPosition = 0; 986 | 987 | while ($excerpt = strpbrk($unexaminedText, $this->inlineMarkerList)) 988 | { 989 | $marker = $excerpt[0]; 990 | 991 | $markerPosition += strpos($unexaminedText, $marker); 992 | 993 | $Excerpt = array('text' => $excerpt, 'context' => $text); 994 | 995 | foreach ($this->InlineTypes[$marker] as $inlineType) 996 | { 997 | $Inline = $this->{'inline'.$inlineType}($Excerpt); 998 | 999 | if ( ! isset($Inline)) 1000 | { 1001 | continue; 1002 | } 1003 | 1004 | if (isset($Inline['position']) and $Inline['position'] > $markerPosition) # position is ahead of marker 1005 | { 1006 | continue; 1007 | } 1008 | 1009 | if ( ! isset($Inline['position'])) 1010 | { 1011 | $Inline['position'] = $markerPosition; 1012 | } 1013 | 1014 | $unmarkedText = substr($text, 0, $Inline['position']); 1015 | 1016 | $markup .= $this->unmarkedText($unmarkedText); 1017 | 1018 | $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); 1019 | 1020 | $text = substr($text, $Inline['position'] + $Inline['extent']); 1021 | 1022 | $unexaminedText = $text; 1023 | 1024 | $markerPosition = 0; 1025 | 1026 | continue 2; 1027 | } 1028 | 1029 | $unexaminedText = substr($excerpt, 1); 1030 | 1031 | $markerPosition ++; 1032 | } 1033 | 1034 | $markup .= $this->unmarkedText($text); 1035 | 1036 | return $markup; 1037 | } 1038 | 1039 | # 1040 | # ~ 1041 | # 1042 | 1043 | protected function inlineCode($Excerpt) 1044 | { 1045 | $marker = $Excerpt['text'][0]; 1046 | 1047 | if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]), 1055 | 'element' => array( 1056 | 'name' => 'code', 1057 | 'text' => $text, 1058 | ), 1059 | ); 1060 | } 1061 | } 1062 | 1063 | protected function inlineEmailTag($Excerpt) 1064 | { 1065 | if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) 1066 | { 1067 | $url = $matches[1]; 1068 | 1069 | if ( ! isset($matches[2])) 1070 | { 1071 | $url = 'mailto:' . $url; 1072 | } 1073 | 1074 | return array( 1075 | 'extent' => strlen($matches[0]), 1076 | 'element' => array( 1077 | 'name' => 'a', 1078 | 'text' => $matches[1], 1079 | 'attributes' => array( 1080 | 'href' => $url, 1081 | ), 1082 | ), 1083 | ); 1084 | } 1085 | } 1086 | 1087 | protected function inlineEmphasis($Excerpt) 1088 | { 1089 | if ( ! isset($Excerpt['text'][1])) 1090 | { 1091 | return; 1092 | } 1093 | 1094 | $marker = $Excerpt['text'][0]; 1095 | 1096 | if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) 1097 | { 1098 | $emphasis = 'strong'; 1099 | } 1100 | elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) 1101 | { 1102 | $emphasis = 'em'; 1103 | } 1104 | else 1105 | { 1106 | return; 1107 | } 1108 | 1109 | return array( 1110 | 'extent' => strlen($matches[0]), 1111 | 'element' => array( 1112 | 'name' => $emphasis, 1113 | 'handler' => 'line', 1114 | 'text' => $matches[1], 1115 | ), 1116 | ); 1117 | } 1118 | 1119 | protected function inlineEscapeSequence($Excerpt) 1120 | { 1121 | if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) 1122 | { 1123 | return array( 1124 | 'markup' => $Excerpt['text'][1], 1125 | 'extent' => 2, 1126 | ); 1127 | } 1128 | } 1129 | 1130 | protected function inlineImage($Excerpt) 1131 | { 1132 | if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') 1133 | { 1134 | return; 1135 | } 1136 | 1137 | $Excerpt['text']= substr($Excerpt['text'], 1); 1138 | 1139 | $Link = $this->inlineLink($Excerpt); 1140 | 1141 | if ($Link === null) 1142 | { 1143 | return; 1144 | } 1145 | 1146 | $Inline = array( 1147 | 'extent' => $Link['extent'] + 1, 1148 | 'element' => array( 1149 | 'name' => 'img', 1150 | 'attributes' => array( 1151 | 'src' => $Link['element']['attributes']['href'], 1152 | 'alt' => $Link['element']['text'], 1153 | ), 1154 | ), 1155 | ); 1156 | 1157 | $Inline['element']['attributes'] += $Link['element']['attributes']; 1158 | 1159 | unset($Inline['element']['attributes']['href']); 1160 | 1161 | return $Inline; 1162 | } 1163 | 1164 | protected function inlineLink($Excerpt) 1165 | { 1166 | $Element = array( 1167 | 'name' => 'a', 1168 | 'handler' => 'line', 1169 | 'text' => null, 1170 | 'attributes' => array( 1171 | 'href' => null, 1172 | 'title' => null, 1173 | ), 1174 | ); 1175 | 1176 | $extent = 0; 1177 | 1178 | $remainder = $Excerpt['text']; 1179 | 1180 | if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches)) 1181 | { 1182 | $Element['text'] = $matches[1]; 1183 | 1184 | $extent += strlen($matches[0]); 1185 | 1186 | $remainder = substr($remainder, $extent); 1187 | } 1188 | else 1189 | { 1190 | return; 1191 | } 1192 | 1193 | if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches)) 1194 | { 1195 | $Element['attributes']['href'] = $matches[1]; 1196 | 1197 | if (isset($matches[2])) 1198 | { 1199 | $Element['attributes']['title'] = substr($matches[2], 1, - 1); 1200 | } 1201 | 1202 | $extent += strlen($matches[0]); 1203 | } 1204 | else 1205 | { 1206 | if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) 1207 | { 1208 | $definition = strlen($matches[1]) ? $matches[1] : $Element['text']; 1209 | $definition = strtolower($definition); 1210 | 1211 | $extent += strlen($matches[0]); 1212 | } 1213 | else 1214 | { 1215 | $definition = strtolower($Element['text']); 1216 | } 1217 | 1218 | if ( ! isset($this->DefinitionData['Reference'][$definition])) 1219 | { 1220 | return; 1221 | } 1222 | 1223 | $Definition = $this->DefinitionData['Reference'][$definition]; 1224 | 1225 | $Element['attributes']['href'] = $Definition['url']; 1226 | $Element['attributes']['title'] = $Definition['title']; 1227 | } 1228 | 1229 | $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']); 1230 | 1231 | return array( 1232 | 'extent' => $extent, 1233 | 'element' => $Element, 1234 | ); 1235 | } 1236 | 1237 | protected function inlineMarkup($Excerpt) 1238 | { 1239 | if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false) 1240 | { 1241 | return; 1242 | } 1243 | 1244 | if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches)) 1245 | { 1246 | return array( 1247 | 'markup' => $matches[0], 1248 | 'extent' => strlen($matches[0]), 1249 | ); 1250 | } 1251 | 1252 | if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) 1253 | { 1254 | return array( 1255 | 'markup' => $matches[0], 1256 | 'extent' => strlen($matches[0]), 1257 | ); 1258 | } 1259 | 1260 | if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) 1261 | { 1262 | return array( 1263 | 'markup' => $matches[0], 1264 | 'extent' => strlen($matches[0]), 1265 | ); 1266 | } 1267 | } 1268 | 1269 | protected function inlineSpecialCharacter($Excerpt) 1270 | { 1271 | if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text'])) 1272 | { 1273 | return array( 1274 | 'markup' => '&', 1275 | 'extent' => 1, 1276 | ); 1277 | } 1278 | 1279 | $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); 1280 | 1281 | if (isset($SpecialCharacter[$Excerpt['text'][0]])) 1282 | { 1283 | return array( 1284 | 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', 1285 | 'extent' => 1, 1286 | ); 1287 | } 1288 | } 1289 | 1290 | protected function inlineStrikethrough($Excerpt) 1291 | { 1292 | if ( ! isset($Excerpt['text'][1])) 1293 | { 1294 | return; 1295 | } 1296 | 1297 | if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) 1298 | { 1299 | return array( 1300 | 'extent' => strlen($matches[0]), 1301 | 'element' => array( 1302 | 'name' => 'del', 1303 | 'text' => $matches[1], 1304 | 'handler' => 'line', 1305 | ), 1306 | ); 1307 | } 1308 | } 1309 | 1310 | protected function inlineUrl($Excerpt) 1311 | { 1312 | if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') 1313 | { 1314 | return; 1315 | } 1316 | 1317 | if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) 1318 | { 1319 | $Inline = array( 1320 | 'extent' => strlen($matches[0][0]), 1321 | 'position' => $matches[0][1], 1322 | 'element' => array( 1323 | 'name' => 'a', 1324 | 'text' => $matches[0][0], 1325 | 'attributes' => array( 1326 | 'href' => $matches[0][0], 1327 | ), 1328 | ), 1329 | ); 1330 | 1331 | return $Inline; 1332 | } 1333 | } 1334 | 1335 | protected function inlineUrlTag($Excerpt) 1336 | { 1337 | if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) 1338 | { 1339 | $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]); 1340 | 1341 | return array( 1342 | 'extent' => strlen($matches[0]), 1343 | 'element' => array( 1344 | 'name' => 'a', 1345 | 'text' => $url, 1346 | 'attributes' => array( 1347 | 'href' => $url, 1348 | ), 1349 | ), 1350 | ); 1351 | } 1352 | } 1353 | 1354 | # ~ 1355 | 1356 | protected function unmarkedText($text) 1357 | { 1358 | if ($this->breaksEnabled) 1359 | { 1360 | $text = preg_replace('/[ ]*\n/', "
\n", $text); 1361 | } 1362 | else 1363 | { 1364 | $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text); 1365 | $text = str_replace(" \n", "\n", $text); 1366 | } 1367 | 1368 | return $text; 1369 | } 1370 | 1371 | # 1372 | # Handlers 1373 | # 1374 | 1375 | protected function element(array $Element) 1376 | { 1377 | $markup = '<'.$Element['name']; 1378 | 1379 | if (isset($Element['attributes'])) 1380 | { 1381 | foreach ($Element['attributes'] as $name => $value) 1382 | { 1383 | if ($value === null) 1384 | { 1385 | continue; 1386 | } 1387 | 1388 | $markup .= ' '.$name.'="'.$value.'"'; 1389 | } 1390 | } 1391 | 1392 | if (isset($Element['text'])) 1393 | { 1394 | $markup .= '>'; 1395 | 1396 | if (isset($Element['handler'])) 1397 | { 1398 | $markup .= $this->{$Element['handler']}($Element['text']); 1399 | } 1400 | else 1401 | { 1402 | $markup .= $Element['text']; 1403 | } 1404 | 1405 | $markup .= ''; 1406 | } 1407 | else 1408 | { 1409 | $markup .= ' />'; 1410 | } 1411 | 1412 | return $markup; 1413 | } 1414 | 1415 | protected function elements(array $Elements) 1416 | { 1417 | $markup = ''; 1418 | 1419 | foreach ($Elements as $Element) 1420 | { 1421 | $markup .= "\n" . $this->element($Element); 1422 | } 1423 | 1424 | $markup .= "\n"; 1425 | 1426 | return $markup; 1427 | } 1428 | 1429 | # ~ 1430 | 1431 | protected function li($lines) 1432 | { 1433 | $markup = $this->lines($lines); 1434 | 1435 | $trimmedMarkup = trim($markup); 1436 | 1437 | if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '

') 1438 | { 1439 | $markup = $trimmedMarkup; 1440 | $markup = substr($markup, 3); 1441 | 1442 | $position = strpos($markup, "

"); 1443 | 1444 | $markup = substr_replace($markup, '', $position, 4); 1445 | } 1446 | 1447 | return $markup; 1448 | } 1449 | 1450 | # 1451 | # Deprecated Methods 1452 | # 1453 | 1454 | function parse($text) 1455 | { 1456 | $markup = $this->text($text); 1457 | 1458 | return $markup; 1459 | } 1460 | 1461 | # 1462 | # Static Methods 1463 | # 1464 | 1465 | static function instance($name = 'default') 1466 | { 1467 | if (isset(self::$instances[$name])) 1468 | { 1469 | return self::$instances[$name]; 1470 | } 1471 | 1472 | $instance = new self(); 1473 | 1474 | self::$instances[$name] = $instance; 1475 | 1476 | return $instance; 1477 | } 1478 | 1479 | private static $instances = array(); 1480 | 1481 | # 1482 | # Fields 1483 | # 1484 | 1485 | protected $DefinitionData; 1486 | 1487 | # 1488 | # Read-Only 1489 | 1490 | protected $specialCharacters = array( 1491 | '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', 1492 | ); 1493 | 1494 | protected $StrongRegex = array( 1495 | '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', 1496 | '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', 1497 | ); 1498 | 1499 | protected $EmRegex = array( 1500 | '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', 1501 | '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', 1502 | ); 1503 | 1504 | protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; 1505 | 1506 | protected $voidElements = array( 1507 | 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 1508 | ); 1509 | 1510 | protected $textLevelElements = array( 1511 | 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', 1512 | 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', 1513 | 'i', 'rp', 'del', 'code', 'strike', 'marquee', 1514 | 'q', 'rt', 'ins', 'font', 'strong', 1515 | 's', 'tt', 'sub', 'mark', 1516 | 'u', 'xm', 'sup', 'nobr', 1517 | 'var', 'ruby', 1518 | 'wbr', 'span', 1519 | 'time', 1520 | ); 1521 | } --------------------------------------------------------------------------------