diff --git a/composer.json b/composer.json
index 87e47f8..7c8a282 100644
--- a/composer.json
+++ b/composer.json
@@ -6,13 +6,14 @@
"php": "5.3"
}
},
- "require": {
+ "require": {
"ircmaxell/password-compat": "1.*",
"michelf/php-markdown": "1.*",
+ "michelf/php-smartypants": "1.*",
"suin/php-rss-writer": "1.*",
"kanti/hub-updater": "0.*",
"jbroadway/urlify": "^1.0"
- },
+ },
"autoload": {
"files": [
"system/includes/dispatch.php",
@@ -22,4 +23,4 @@
"system/includes/opml.php"
]
}
-}
+}
\ No newline at end of file
diff --git a/composer.lock b/composer.lock
index b55d54d..6473346 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "250bd035a1d26dd3e3e01e504833c798",
+ "content-hash": "7ffa800520a3ea4639996d588bc30b23",
"packages": [
{
"name": "ircmaxell/password-compat",
@@ -199,6 +199,56 @@
],
"time": "2019-12-02T02:32:27+00:00"
},
+ {
+ "name": "michelf/php-smartypants",
+ "version": "1.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/michelf/php-smartypants.git",
+ "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
+ "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Michelf": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Michel Fortin",
+ "email": "michel.fortin@michelf.ca",
+ "homepage": "https://michelf.ca/",
+ "role": "Developer"
+ },
+ {
+ "name": "John Gruber",
+ "homepage": "https://daringfireball.net/"
+ }
+ ],
+ "description": "PHP SmartyPants",
+ "homepage": "https://michelf.ca/projects/php-smartypants/",
+ "keywords": [
+ "dashes",
+ "quotes",
+ "spaces",
+ "typographer",
+ "typography"
+ ],
+ "time": "2016-12-13T01:01:17+00:00"
+ },
{
"name": "suin/php-rss-writer",
"version": "1.3.2",
diff --git a/config/config.ini.example b/config/config.ini.example
index f593661..b5fbc73 100644
--- a/config/config.ini.example
+++ b/config/config.ini.example
@@ -135,5 +135,14 @@ cache.timestamp = "false"
; Set the theme here
views.root = "themes/twentyfifteen"
+; Use "smart" transforms of straight quotes into curly quotes, dashes into en- and em-dashes, etc.
+smart = "true"
+
+; customize smart quote configuration
+;doublequote.open = "”"
+;doublequote.close = "”"
+;singlequote.open = "’"
+;singlequote.close = "’"
+
; Framework config. No need to edit.
views.layout = "layout"
diff --git a/system/includes/functions.php b/system/includes/functions.php
index c54801b..49061b6 100644
--- a/system/includes/functions.php
+++ b/system/includes/functions.php
@@ -2,6 +2,7 @@
if (!defined('HTMLY')) die('HTMLy');
use \Michelf\MarkdownExtra;
+use \Michelf\SmartyPants;
use \Suin\RSSWriter\Feed;
use \Suin\RSSWriter\Channel;
use \Suin\RSSWriter\Item;
@@ -417,7 +418,7 @@ function get_posts($posts, $page = 1, $perpage = 0)
}
// Get the contents and convert it to HTML
- $post->body = MarkdownExtra::defaultTransform(remove_html_comments($content));
+ $post->body = markdown_to_html(remove_html_comments($content));
// Convert image tags to figures
if (config('fig.captions') == 'true') {
@@ -751,7 +752,7 @@ function get_category_info($category)
$desc->title = get_content_tag('t', $content, $category);
// Get the contents and convert it to HTML
- $desc->body = MarkdownExtra::defaultTransform(remove_html_comments($content));
+ $desc->body = markdown_to_html(remove_html_comments($content));
$desc->description = get_content_tag("d", $content, get_description($desc->body));
@@ -1016,7 +1017,7 @@ function get_author($name)
$author->name = get_content_tag('t', $content, $author);
// Get the contents and convert it to HTML
- $author->about = MarkdownExtra::defaultTransform(remove_html_comments($content));
+ $author->about = markdown_to_html(remove_html_comments($content));
$tmp[] = $author;
}
@@ -1072,7 +1073,7 @@ function get_static_post($static = null)
$post->title = get_content_tag('t', $content, 'Untitled static page: ' . format_date($post->lastMod, 'l, j F Y, H:i'));
// Get the contents and convert it to HTML
- $post->body = MarkdownExtra::defaultTransform(remove_html_comments($content));
+ $post->body = markdown_to_html(remove_html_comments($content));
if (config('views.counter') == 'true') {
$post->views = get_views($post->file);
@@ -1158,7 +1159,7 @@ function get_static_sub_post($static, $sub_static = null)
$post->title = get_content_tag('t', $content, 'Untitled static subpage: ' . format_date($post->lastMod, 'l, j F Y, H:i'));
// Get the contents and convert it to HTML
- $post->body = MarkdownExtra::defaultTransform(remove_html_comments($content));
+ $post->body = markdown_to_html(remove_html_comments($content));
$post->views = get_views($post->file);
@@ -1221,7 +1222,7 @@ function get_frontpage()
$front->title = get_content_tag('t', $content, 'Welcome');
$front->url = site_url() . 'front';
// Get the contents and convert it to HTML
- $front->body = MarkdownExtra::defaultTransform(remove_html_comments($content));
+ $front->body = markdown_to_html(remove_html_comments($content));
} else {
$front->title = 'Welcome';
$front->url = site_url() . 'front';
@@ -2476,7 +2477,7 @@ function menu($class = null)
function get_title_from_file($v)
{
// Get the contents and convert it to HTML
- $content = MarkdownExtra::defaultTransform(file_get_contents($v));
+ $content = markdown_to_html(file_get_contents($v));
$filename= pathinfo($v, PATHINFO_FILENAME);
@@ -3242,6 +3243,31 @@ function remove_html_comments($content)
return preg_replace($patterns, '', $content);
}
+// Transform markdown to HTML with PHP::MarkdownExtra och PHP::SmartyPants
+function markdown_to_html($markdown)
+{
+ $html = MarkdownExtra::defaultTransform($markdown);
+
+ if (config('smart') == 'true') {
+ $parser = new SmartyPants(2);
+ if (config('doublequote.open') !== null) {
+ $parser->smart_doublequote_open = config('doublequote.open');
+ }
+ if (config('doublequote.close') !== null) {
+ $parser->smart_doublequote_close = config('doublequote.close');
+ }
+ if (config('singlequote.open') !== null) {
+ $parser->smart_singlequote_open = config('singlequote.open');
+ }
+ if (config('singlequote.close') !== null) {
+ $parser->smart_singlequote_open = config('singlequote.close');
+ }
+ $html = $parser->transform($html);
+ }
+
+ return $html;
+}
+
// Google recaptcha
function isCaptcha($reCaptchaResponse)
{
diff --git a/system/vendor/composer/autoload_classmap.php b/system/vendor/composer/autoload_classmap.php
index 21753fd..57a752a 100644
--- a/system/vendor/composer/autoload_classmap.php
+++ b/system/vendor/composer/autoload_classmap.php
@@ -12,6 +12,8 @@ return array(
'Michelf\\Markdown' => $vendorDir . '/michelf/php-markdown/Michelf/Markdown.php',
'Michelf\\MarkdownExtra' => $vendorDir . '/michelf/php-markdown/Michelf/MarkdownExtra.php',
'Michelf\\MarkdownInterface' => $vendorDir . '/michelf/php-markdown/Michelf/MarkdownInterface.php',
+ 'Michelf\\SmartyPants' => $vendorDir . '/michelf/php-smartypants/Michelf/SmartyPants.php',
+ 'Michelf\\SmartyPantsTypographer' => $vendorDir . '/michelf/php-smartypants/Michelf/SmartyPantsTypographer.php',
'Suin\\RSSWriter\\Channel' => $vendorDir . '/suin/php-rss-writer/src/Suin/RSSWriter/Channel.php',
'Suin\\RSSWriter\\ChannelInterface' => $vendorDir . '/suin/php-rss-writer/src/Suin/RSSWriter/ChannelInterface.php',
'Suin\\RSSWriter\\Feed' => $vendorDir . '/suin/php-rss-writer/src/Suin/RSSWriter/Feed.php',
diff --git a/system/vendor/composer/autoload_namespaces.php b/system/vendor/composer/autoload_namespaces.php
index 6cd4d04..1fb22eb 100644
--- a/system/vendor/composer/autoload_namespaces.php
+++ b/system/vendor/composer/autoload_namespaces.php
@@ -8,4 +8,5 @@ $baseDir = dirname(dirname($vendorDir));
return array(
'URLify' => array($vendorDir . '/jbroadway/urlify'),
'Suin\\RSSWriter' => array($vendorDir . '/suin/php-rss-writer/src'),
+ 'Michelf' => array($vendorDir . '/michelf/php-smartypants'),
);
diff --git a/system/vendor/composer/autoload_static.php b/system/vendor/composer/autoload_static.php
index b537df5..a5c96de 100644
--- a/system/vendor/composer/autoload_static.php
+++ b/system/vendor/composer/autoload_static.php
@@ -52,6 +52,13 @@ class ComposerStaticInitd88c6c25320034df85dd42f1462fbda7
0 => __DIR__ . '/..' . '/suin/php-rss-writer/src',
),
),
+ 'M' =>
+ array (
+ 'Michelf' =>
+ array (
+ 0 => __DIR__ . '/..' . '/michelf/php-smartypants',
+ ),
+ ),
);
public static $classMap = array (
@@ -61,6 +68,8 @@ class ComposerStaticInitd88c6c25320034df85dd42f1462fbda7
'Michelf\\Markdown' => __DIR__ . '/..' . '/michelf/php-markdown/Michelf/Markdown.php',
'Michelf\\MarkdownExtra' => __DIR__ . '/..' . '/michelf/php-markdown/Michelf/MarkdownExtra.php',
'Michelf\\MarkdownInterface' => __DIR__ . '/..' . '/michelf/php-markdown/Michelf/MarkdownInterface.php',
+ 'Michelf\\SmartyPants' => __DIR__ . '/..' . '/michelf/php-smartypants/Michelf/SmartyPants.php',
+ 'Michelf\\SmartyPantsTypographer' => __DIR__ . '/..' . '/michelf/php-smartypants/Michelf/SmartyPantsTypographer.php',
'Suin\\RSSWriter\\Channel' => __DIR__ . '/..' . '/suin/php-rss-writer/src/Suin/RSSWriter/Channel.php',
'Suin\\RSSWriter\\ChannelInterface' => __DIR__ . '/..' . '/suin/php-rss-writer/src/Suin/RSSWriter/ChannelInterface.php',
'Suin\\RSSWriter\\Feed' => __DIR__ . '/..' . '/suin/php-rss-writer/src/Suin/RSSWriter/Feed.php',
diff --git a/system/vendor/composer/installed.json b/system/vendor/composer/installed.json
index 7bc9df3..716858e 100644
--- a/system/vendor/composer/installed.json
+++ b/system/vendor/composer/installed.json
@@ -200,6 +200,58 @@
"markdown"
]
},
+ {
+ "name": "michelf/php-smartypants",
+ "version": "1.8.1",
+ "version_normalized": "1.8.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/michelf/php-smartypants.git",
+ "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
+ "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "time": "2016-12-13T01:01:17+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "Michelf": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Michel Fortin",
+ "email": "michel.fortin@michelf.ca",
+ "homepage": "https://michelf.ca/",
+ "role": "Developer"
+ },
+ {
+ "name": "John Gruber",
+ "homepage": "https://daringfireball.net/"
+ }
+ ],
+ "description": "PHP SmartyPants",
+ "homepage": "https://michelf.ca/projects/php-smartypants/",
+ "keywords": [
+ "dashes",
+ "quotes",
+ "spaces",
+ "typographer",
+ "typography"
+ ]
+ },
{
"name": "suin/php-rss-writer",
"version": "1.3.2",
diff --git a/system/vendor/michelf/php-smartypants/License.md b/system/vendor/michelf/php-smartypants/License.md
new file mode 100644
index 0000000..20aad72
--- /dev/null
+++ b/system/vendor/michelf/php-smartypants/License.md
@@ -0,0 +1,36 @@
+PHP SmartyPants Lib
+Copyright (c) 2005-2016 Michel Fortin
+
or tags.
+
+ $prev_token_last_char = ""; # This is a cheat, used to get some context
+ # for one-character tokens that consist of
+ # just a quote char. What we do is remember
+ # the last character of the previous text
+ # token, to use as context to curl single-
+ # character quote tokens correctly.
+
+ foreach ($tokens as $cur_token) {
+ if ($cur_token[0] == "tag") {
+ # Don't mess with quotes inside tags.
+ $result .= $cur_token[1];
+ if (preg_match('@<(/?)(?:'.$this->tags_to_skip.')[\s>]@', $cur_token[1], $matches)) {
+ $in_pre = isset($matches[1]) && $matches[1] == '/' ? 0 : 1;
+ }
+ } else {
+ $t = $cur_token[1];
+ $last_char = substr($t, -1); # Remember last char of this token before processing.
+ if (! $in_pre) {
+ $t = $this->educate($t, $prev_token_last_char);
+ }
+ $prev_token_last_char = $last_char;
+ $result .= $t;
+ }
+ }
+
+ return $result;
+ }
+
+
+ function decodeEntitiesInConfiguration() {
+ #
+ # Utility function that converts entities in configuration variables to
+ # UTF-8 characters.
+ #
+ $output_config_vars = array(
+ 'smart_doublequote_open',
+ 'smart_doublequote_close',
+ 'smart_singlequote_open',
+ 'smart_singlequote_close',
+ 'backtick_doublequote_open',
+ 'backtick_doublequote_close',
+ 'backtick_singlequote_open',
+ 'backtick_singlequote_close',
+ 'em_dash',
+ 'en_dash',
+ 'ellipsis',
+ );
+ foreach ($output_config_vars as $var) {
+ $this->$var = html_entity_decode($this->$var);
+ }
+ }
+
+
+ protected function educate($t, $prev_token_last_char) {
+ $t = $this->processEscapes($t);
+
+ if ($this->convert_quot) {
+ $t = preg_replace('/"/', '"', $t);
+ }
+
+ if ($this->do_dashes) {
+ if ($this->do_dashes == 1) $t = $this->educateDashes($t);
+ if ($this->do_dashes == 2) $t = $this->educateDashesOldSchool($t);
+ if ($this->do_dashes == 3) $t = $this->educateDashesOldSchoolInverted($t);
+ }
+
+ if ($this->do_ellipses) $t = $this->educateEllipses($t);
+
+ # Note: backticks need to be processed before quotes.
+ if ($this->do_backticks) {
+ $t = $this->educateBackticks($t);
+ if ($this->do_backticks == 2) $t = $this->educateSingleBackticks($t);
+ }
+
+ if ($this->do_quotes) {
+ if ($t == "'") {
+ # Special case: single-character ' token
+ if (preg_match('/\S/', $prev_token_last_char)) {
+ $t = $this->smart_singlequote_close;
+ }
+ else {
+ $t = $this->smart_singlequote_open;
+ }
+ }
+ else if ($t == '"') {
+ # Special case: single-character " token
+ if (preg_match('/\S/', $prev_token_last_char)) {
+ $t = $this->smart_doublequote_close;
+ }
+ else {
+ $t = $this->smart_doublequote_open;
+ }
+ }
+ else {
+ # Normal case:
+ $t = $this->educateQuotes($t);
+ }
+ }
+
+ if ($this->do_stupefy) $t = $this->stupefyEntities($t);
+
+ return $t;
+ }
+
+
+ protected function educateQuotes($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with "educated" curly quote HTML entities.
+ #
+ # Example input: "Isn't this fun?"
+ # Example output: “Isn’t this fun?”
+ #
+ $dq_open = $this->smart_doublequote_open;
+ $dq_close = $this->smart_doublequote_close;
+ $sq_open = $this->smart_singlequote_open;
+ $sq_close = $this->smart_singlequote_close;
+
+ # Make our own "punctuation" character class, because the POSIX-style
+ # [:PUNCT:] is only available in Perl 5.6 or later:
+ $punct_class = "[!\"#\\$\\%'()*+,-.\\/:;<=>?\\@\\[\\\\\]\\^_`{|}~]";
+
+ # Special case if the very first character is a quote
+ # followed by punctuation at a non-word-break. Close the quotes by brute force:
+ $_ = preg_replace(
+ array("/^'(?=$punct_class\\B)/", "/^\"(?=$punct_class\\B)/"),
+ array($sq_close, $dq_close), $_);
+
+ # Special case for double sets of quotes, e.g.:
+ # He said, "'Quoted' words in a larger quote."
+ $_ = preg_replace(
+ array("/\"'(?=\w)/", "/'\"(?=\w)/"),
+ array($dq_open.$sq_open, $sq_open.$dq_open), $_);
+
+ # Special case for decade abbreviations (the '80s):
+ $_ = preg_replace("/'(?=\\d{2}s)/", $sq_close, $_);
+
+ $close_class = '[^\ \t\r\n\[\{\(\-]';
+ $dec_dashes = '&\#8211;|&\#8212;';
+
+ # Get most opening single quotes:
+ $_ = preg_replace("{
+ (
+ \\s | # a whitespace char, or
+ | # a non-breaking space entity, or
+ -- | # dashes, or
+ &[mn]dash; | # named dash entities
+ $dec_dashes | # or decimal entities
+ &\\#x201[34]; # or hex
+ )
+ ' # the quote
+ (?=\\w) # followed by a word character
+ }x", '\1'.$sq_open, $_);
+ # Single closing quotes:
+ $_ = preg_replace("{
+ ($close_class)?
+ '
+ (?(1)| # If $1 captured, then do nothing;
+ (?=\\s | s\\b) # otherwise, positive lookahead for a whitespace
+ ) # char or an 's' at a word ending position. This
+ # is a special case to handle something like:
+ # \"Custer's Last Stand.\"
+ }xi", '\1'.$sq_close, $_);
+
+ # Any remaining single quotes should be opening ones:
+ $_ = str_replace("'", $sq_open, $_);
+
+
+ # Get most opening double quotes:
+ $_ = preg_replace("{
+ (
+ \\s | # a whitespace char, or
+ | # a non-breaking space entity, or
+ -- | # dashes, or
+ &[mn]dash; | # named dash entities
+ $dec_dashes | # or decimal entities
+ &\\#x201[34]; # or hex
+ )
+ \" # the quote
+ (?=\\w) # followed by a word character
+ }x", '\1'.$dq_open, $_);
+
+ # Double closing quotes:
+ $_ = preg_replace("{
+ ($close_class)?
+ \"
+ (?(1)|(?=\\s)) # If $1 captured, then do nothing;
+ # if not, then make sure the next char is whitespace.
+ }x", '\1'.$dq_close, $_);
+
+ # Any remaining quotes should be opening ones.
+ $_ = str_replace('"', $dq_open, $_);
+
+ return $_;
+ }
+
+
+ protected function educateBackticks($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with ``backticks'' -style double quotes
+ # translated into HTML curly quote entities.
+ #
+ # Example input: ``Isn't this fun?''
+ # Example output: “Isn't this fun?”
+ #
+
+ $_ = str_replace(array("``", "''",),
+ array($this->backtick_doublequote_open,
+ $this->backtick_doublequote_close), $_);
+ return $_;
+ }
+
+
+ protected function educateSingleBackticks($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with `backticks' -style single quotes
+ # translated into HTML curly quote entities.
+ #
+ # Example input: `Isn't this fun?'
+ # Example output: ‘Isn’t this fun?’
+ #
+
+ $_ = str_replace(array("`", "'",),
+ array($this->backtick_singlequote_open,
+ $this->backtick_singlequote_close), $_);
+ return $_;
+ }
+
+
+ protected function educateDashes($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with each instance of "--" translated to
+ # an em-dash HTML entity.
+ #
+
+ $_ = str_replace('--', $this->em_dash, $_);
+ return $_;
+ }
+
+
+ protected function educateDashesOldSchool($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with each instance of "--" translated to
+ # an en-dash HTML entity, and each "---" translated to
+ # an em-dash HTML entity.
+ #
+
+ # em en
+ $_ = str_replace(array("---", "--",),
+ array($this->em_dash, $this->en_dash), $_);
+ return $_;
+ }
+
+
+ protected function educateDashesOldSchoolInverted($_) {
+ #
+ # Parameter: String.
+ #
+ # Returns: The string, with each instance of "--" translated to
+ # an em-dash HTML entity, and each "---" translated to
+ # an en-dash HTML entity. Two reasons why: First, unlike the
+ # en- and em-dash syntax supported by
+ # EducateDashesOldSchool(), it's compatible with existing
+ # entries written before SmartyPants 1.1, back when "--" was
+ # only used for em-dashes. Second, em-dashes are more
+ # common than en-dashes, and so it sort of makes sense that
+ # the shortcut should be shorter to type. (Thanks to Aaron
+ # Swartz for the idea.)
+ #
+
+ # en em
+ $_ = str_replace(array("---", "--",),
+ array($this->en_dash, $this->em_dash), $_);
+ return $_;
+ }
+
+
+ protected function educateEllipses($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with each instance of "..." translated to
+ # an ellipsis HTML entity. Also converts the case where
+ # there are spaces between the dots.
+ #
+ # Example input: Huh...?
+ # Example output: Huh…?
+ #
+
+ $_ = str_replace(array("...", ". . .",), $this->ellipsis, $_);
+ return $_;
+ }
+
+
+ protected function stupefyEntities($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with each SmartyPants HTML entity translated to
+ # its ASCII counterpart.
+ #
+ # Example input: “Hello — world.”
+ # Example output: "Hello -- world."
+ #
+
+ # en-dash em-dash
+ $_ = str_replace(array('–', '—'),
+ array('-', '--'), $_);
+
+ # single quote open close
+ $_ = str_replace(array('‘', '’'), "'", $_);
+
+ # double quote open close
+ $_ = str_replace(array('“', '”'), '"', $_);
+
+ $_ = str_replace('…', '...', $_); # ellipsis
+
+ return $_;
+ }
+
+
+ protected function processEscapes($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with after processing the following backslash
+ # escape sequences. This is useful if you want to force a "dumb"
+ # quote or other character to appear.
+ #
+ # Escape Value
+ # ------ -----
+ # \\ \
+ # \" "
+ # \' '
+ # \. .
+ # \- -
+ # \` `
+ #
+ $_ = str_replace(
+ array('\\\\', '\"', "\'", '\.', '\-', '\`'),
+ array('\', '"', ''', '.', '-', '`'), $_);
+
+ return $_;
+ }
+
+
+ protected function tokenizeHTML($str) {
+ #
+ # Parameter: String containing HTML markup.
+ # Returns: An array of the tokens comprising the input
+ # string. Each token is either a tag (possibly with nested,
+ # tags contained therein, such as , or a
+ # run of text between tags. Each element of the array is a
+ # two-element array; the first is either 'tag' or 'text';
+ # the second is the actual value.
+ #
+ #
+ # Regular expression derived from the _tokenize() subroutine in
+ # Brad Choate's MTRegex plugin.
+ #
+ #
+ $index = 0;
+ $tokens = array();
+
+ $match = '(?s:)|'. # comment
+ '(?s:<\?.*?\?>)|'. # processing instruction
+ # regular tags
+ '(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)';
+
+ $parts = preg_split("{($match)}", $str, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ foreach ($parts as $part) {
+ if (++$index % 2 && $part != '')
+ $tokens[] = array('text', $part);
+ else
+ $tokens[] = array('tag', $part);
+ }
+ return $tokens;
+ }
+
+}
diff --git a/system/vendor/michelf/php-smartypants/Michelf/SmartyPantsTypographer.inc.php b/system/vendor/michelf/php-smartypants/Michelf/SmartyPantsTypographer.inc.php
new file mode 100644
index 0000000..9b3d274
--- /dev/null
+++ b/system/vendor/michelf/php-smartypants/Michelf/SmartyPantsTypographer.inc.php
@@ -0,0 +1,10 @@
+
+#
+# Original SmartyPants
+# Copyright (c) 2003-2004 John Gruber
+#
+#
+namespace Michelf;
+
+
+#
+# SmartyPants Typographer Parser Class
+#
+class SmartyPantsTypographer extends \Michelf\SmartyPants {
+
+ ### Configuration Variables ###
+
+ # Options to specify which transformations to make:
+ public $do_comma_quotes = 0;
+ public $do_guillemets = 0;
+ public $do_geresh_gershayim = 0;
+ public $do_space_emdash = 0;
+ public $do_space_endash = 0;
+ public $do_space_colon = 0;
+ public $do_space_semicolon = 0;
+ public $do_space_marks = 0;
+ public $do_space_frenchquote = 0;
+ public $do_space_thousand = 0;
+ public $do_space_unit = 0;
+
+ # Quote characters for replacing ASCII approximations
+ public $doublequote_low = "„"; // replacement for ,,
+ public $guillemet_leftpointing = "«"; // replacement for <<
+ public $guillemet_rightpointing = "»"; // replacement for >>
+ public $geresh = "׳";
+ public $gershayim = "״";
+
+ # Space characters for different places:
+ # Space around em-dashes. "He_—_or she_—_should change that."
+ public $space_emdash = " ";
+ # Space around en-dashes. "He_–_or she_–_should change that."
+ public $space_endash = " ";
+ # Space before a colon. "He said_: here it is."
+ public $space_colon = " ";
+ # Space before a semicolon. "That's what I said_; that's what he said."
+ public $space_semicolon = " ";
+ # Space before a question mark and an exclamation mark: "¡_Holà_! What_?"
+ public $space_marks = " ";
+ # Space inside french quotes. "Voici la «_chose_» qui m'a attaqué."
+ public $space_frenchquote = " ";
+ # Space as thousand separator. "On compte 10_000 maisons sur cette liste."
+ public $space_thousand = " ";
+ # Space before a unit abreviation. "This 12_kg of matter costs 10_$."
+ public $space_unit = " ";
+
+
+ # Expression of a space (breakable or not):
+ public $space = '(?: | | |*160;|*[aA]0;)';
+
+
+ ### Parser Implementation ###
+
+ public function __construct($attr = SmartyPants::ATTR_DEFAULT) {
+ #
+ # Initialize a SmartyPantsTypographer_Parser with certain attributes.
+ #
+ # Parser attributes:
+ # 0 : do nothing
+ # 1 : set all, except dash spacing
+ # 2 : set all, except dash spacing, using old school en- and em- dash shortcuts
+ # 3 : set all, except dash spacing, using inverted old school en and em- dash shortcuts
+ #
+ # Punctuation:
+ # q -> quotes
+ # b -> backtick quotes (``double'' only)
+ # B -> backtick quotes (``double'' and `single')
+ # c -> comma quotes (,,double`` only)
+ # g -> guillemets (<> only)
+ # d -> dashes
+ # D -> old school dashes
+ # i -> inverted old school dashes
+ # e -> ellipses
+ # w -> convert " entities to " for Dreamweaver users
+ #
+ # Spacing:
+ # : -> colon spacing +-
+ # ; -> semicolon spacing +-
+ # m -> question and exclamation marks spacing +-
+ # h -> em-dash spacing +-
+ # H -> en-dash spacing +-
+ # f -> french quote spacing +-
+ # t -> thousand separator spacing -
+ # u -> unit spacing +-
+ # (you can add a plus sign after some of these options denoted by + to
+ # add the space when it is not already present, or you can add a minus
+ # sign to completly remove any space present)
+ #
+ # Initialize inherited SmartyPants parser.
+ parent::__construct($attr);
+
+ if ($attr == "1" || $attr == "2" || $attr == "3") {
+ # Do everything, turn all options on.
+ $this->do_comma_quotes = 1;
+ $this->do_guillemets = 1;
+ $this->do_geresh_gershayim = 1;
+ $this->do_space_emdash = 1;
+ $this->do_space_endash = 1;
+ $this->do_space_colon = 1;
+ $this->do_space_semicolon = 1;
+ $this->do_space_marks = 1;
+ $this->do_space_frenchquote = 1;
+ $this->do_space_thousand = 1;
+ $this->do_space_unit = 1;
+ }
+ else if ($attr == "-1") {
+ # Special "stupefy" mode.
+ $this->do_stupefy = 1;
+ }
+ else {
+ $chars = preg_split('//', $attr);
+ foreach ($chars as $c){
+ if ($c == "c") { $current =& $this->do_comma_quotes; }
+ else if ($c == "g") { $current =& $this->do_guillemets; }
+ else if ($c == "G") { $current =& $this->do_geresh_gershayim; }
+ else if ($c == ":") { $current =& $this->do_space_colon; }
+ else if ($c == ";") { $current =& $this->do_space_semicolon; }
+ else if ($c == "m") { $current =& $this->do_space_marks; }
+ else if ($c == "h") { $current =& $this->do_space_emdash; }
+ else if ($c == "H") { $current =& $this->do_space_endash; }
+ else if ($c == "f") { $current =& $this->do_space_frenchquote; }
+ else if ($c == "t") { $current =& $this->do_space_thousand; }
+ else if ($c == "u") { $current =& $this->do_space_unit; }
+ else if ($c == "+") {
+ $current = 2;
+ unset($current);
+ }
+ else if ($c == "-") {
+ $current = -1;
+ unset($current);
+ }
+ else {
+ # Unknown attribute option, ignore.
+ }
+ $current = 1;
+ }
+ }
+ }
+
+
+ function decodeEntitiesInConfiguration() {
+ parent::decodeEntitiesInConfiguration();
+ $output_config_vars = array(
+ 'doublequote_low',
+ 'guillemet_leftpointing',
+ 'guillemet_rightpointing',
+ 'space_emdash',
+ 'space_endash',
+ 'space_colon',
+ 'space_semicolon',
+ 'space_marks',
+ 'space_frenchquote',
+ 'space_thousand',
+ 'space_unit',
+ );
+ foreach ($output_config_vars as $var) {
+ $this->$var = html_entity_decode($this->$var);
+ }
+ }
+
+
+ function educate($t, $prev_token_last_char) {
+ # must happen before regular smart quotes
+ if ($this->do_geresh_gershayim) $t = $this->educateGereshGershayim($t);
+
+ $t = parent::educate($t, $prev_token_last_char);
+
+ if ($this->do_comma_quotes) $t = $this->educateCommaQuotes($t);
+ if ($this->do_guillemets) $t = $this->educateGuillemets($t);
+
+ if ($this->do_space_emdash) $t = $this->spaceEmDash($t);
+ if ($this->do_space_endash) $t = $this->spaceEnDash($t);
+ if ($this->do_space_colon) $t = $this->spaceColon($t);
+ if ($this->do_space_semicolon) $t = $this->spaceSemicolon($t);
+ if ($this->do_space_marks) $t = $this->spaceMarks($t);
+ if ($this->do_space_frenchquote) $t = $this->spaceFrenchQuotes($t);
+ if ($this->do_space_thousand) $t = $this->spaceThousandSeparator($t);
+ if ($this->do_space_unit) $t = $this->spaceUnit($t);
+
+ return $t;
+ }
+
+
+ protected function educateCommaQuotes($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with ,,comma,, -style double quotes
+ # translated into HTML curly quote entities.
+ #
+ # Example input: ,,Isn't this fun?,,
+ # Example output: „Isn't this fun?„
+ #
+ # Note: this is meant to be used alongside with backtick quotes; there is
+ # no language that use only lower quotations alone mark like in the example.
+ #
+ $_ = str_replace(",,", $this->doublequote_low, $_);
+ return $_;
+ }
+
+
+ protected function educateGuillemets($_) {
+ #
+ # Parameter: String.
+ # Returns: The string, with << guillemets >> -style quotes
+ # translated into HTML guillemets entities.
+ #
+ # Example input: << Isn't this fun? >>
+ # Example output: „ Isn't this fun? „
+ #
+ $_ = preg_replace("/(?:<|<){2}/", $this->guillemet_leftpointing, $_);
+ $_ = preg_replace("/(?:>|>){2}/", $this->guillemet_rightpointing, $_);
+ return $_;
+ }
+
+
+ protected function educateGereshGershayim($_) {
+ #
+ # Parameter: String, UTF-8 encoded.
+ # Returns: The string, where simple a or double quote surrounded by
+ # two hebrew characters is replaced into a typographic
+ # geresh or gershayim punctuation mark.
+ #
+ # Example input: צה"ל / צ'ארלס
+ # Example output: צה״ל / צ׳ארלס
+ #
+ // surrounding code points can be U+0590 to U+05BF and U+05D0 to U+05F2
+ // encoded in UTF-8: D6.90 to D6.BF and D7.90 to D7.B2
+ $_ = preg_replace('/(?<=\xD6[\x90-\xBF]|\xD7[\x90-\xB2])\'(?=\xD6[\x90-\xBF]|\xD7[\x90-\xB2])/', $this->geresh, $_);
+ $_ = preg_replace('/(?<=\xD6[\x90-\xBF]|\xD7[\x90-\xB2])"(?=\xD6[\x90-\xBF]|\xD7[\x90-\xB2])/', $this->gershayim, $_);
+ return $_;
+ }
+
+
+ protected function spaceFrenchQuotes($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # inside french-style quotes, only french quotes.
+ #
+ # Example input: Quotes in « French », »German« and »Finnish» style.
+ # Example output: Quotes in «_French_», »German« and »Finnish» style.
+ #
+ $opt = ( $this->do_space_frenchquote == 2 ? '?' : '' );
+ $chr = ( $this->do_space_frenchquote != -1 ? $this->space_frenchquote : '' );
+
+ # Characters allowed immediatly outside quotes.
+ $outside_char = $this->space . '|\s|[.,:;!?\[\](){}|@*~=+-]|¡|¿';
+
+ $_ = preg_replace(
+ "/(^|$outside_char)(«|«|›|‹)$this->space$opt/",
+ "\\1\\2$chr", $_);
+ $_ = preg_replace(
+ "/$this->space$opt(»|»|‹|›)($outside_char|$)/",
+ "$chr\\1\\2", $_);
+ return $_;
+ }
+
+
+ protected function spaceColon($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # before colons.
+ #
+ # Example input: Ingredients : fun.
+ # Example output: Ingredients_: fun.
+ #
+ $opt = ( $this->do_space_colon == 2 ? '?' : '' );
+ $chr = ( $this->do_space_colon != -1 ? $this->space_colon : '' );
+
+ $_ = preg_replace("/$this->space$opt(:)(\\s|$)/m",
+ "$chr\\1\\2", $_);
+ return $_;
+ }
+
+
+ protected function spaceSemicolon($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # before semicolons.
+ #
+ # Example input: There he goes ; there she goes.
+ # Example output: There he goes_; there she goes.
+ #
+ $opt = ( $this->do_space_semicolon == 2 ? '?' : '' );
+ $chr = ( $this->do_space_semicolon != -1 ? $this->space_semicolon : '' );
+
+ $_ = preg_replace("/$this->space(;)(?=\\s|$)/m",
+ " \\1", $_);
+ $_ = preg_replace("/((?:^|\\s)(?>[^&;\\s]+|?[a-zA-Z0-9]+;)*)".
+ " $opt(;)(?=\\s|$)/m",
+ "\\1$chr\\2", $_);
+ return $_;
+ }
+
+
+ protected function spaceMarks($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # around question and exclamation marks.
+ #
+ # Example input: ¡ Holà ! What ?
+ # Example output: ¡_Holà_! What_?
+ #
+ $opt = ( $this->do_space_marks == 2 ? '?' : '' );
+ $chr = ( $this->do_space_marks != -1 ? $this->space_marks : '' );
+
+ // Regular marks.
+ $_ = preg_replace("/$this->space$opt([?!]+)/", "$chr\\1", $_);
+
+ // Inverted marks.
+ $imarks = "(?:¡|¡|¡|[Aa]1;|¿|¿|¿|[Bb][Ff];)";
+ $_ = preg_replace("/($imarks+)$this->space$opt/", "\\1$chr", $_);
+
+ return $_;
+ }
+
+
+ protected function spaceEmDash($_) {
+ #
+ # Parameters: String, two replacement characters separated by a hyphen (`-`),
+ # and forcing flag.
+ #
+ # Returns: The string, with appropriates spaces replaced
+ # around dashes.
+ #
+ # Example input: Then — without any plan — the fun happend.
+ # Example output: Then_—_without any plan_—_the fun happend.
+ #
+ $opt = ( $this->do_space_emdash == 2 ? '?' : '' );
+ $chr = ( $this->do_space_emdash != -1 ? $this->space_emdash : '' );
+ $_ = preg_replace("/$this->space$opt(—|—)$this->space$opt/",
+ "$chr\\1$chr", $_);
+ return $_;
+ }
+
+
+ protected function spaceEnDash($_) {
+ #
+ # Parameters: String, two replacement characters separated by a hyphen (`-`),
+ # and forcing flag.
+ #
+ # Returns: The string, with appropriates spaces replaced
+ # around dashes.
+ #
+ # Example input: Then — without any plan — the fun happend.
+ # Example output: Then_—_without any plan_—_the fun happend.
+ #
+ $opt = ( $this->do_space_endash == 2 ? '?' : '' );
+ $chr = ( $this->do_space_endash != -1 ? $this->space_endash : '' );
+ $_ = preg_replace("/$this->space$opt(–|–)$this->space$opt/",
+ "$chr\\1$chr", $_);
+ return $_;
+ }
+
+
+ protected function spaceThousandSeparator($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # inside numbers (thousand separator in french).
+ #
+ # Example input: Il y a 10 000 insectes amusants dans ton jardin.
+ # Example output: Il y a 10_000 insectes amusants dans ton jardin.
+ #
+ $chr = ( $this->do_space_thousand != -1 ? $this->space_thousand : '' );
+ $_ = preg_replace('/([0-9]) ([0-9])/', "\\1$chr\\2", $_);
+ return $_;
+ }
+
+
+ protected $units = '
+ ### Metric units (with prefixes)
+ (?:
+ p |
+ µ | µ | &\#0*181; | &\#[xX]0*[Bb]5; |
+ [mcdhkMGT]
+ )?
+ (?:
+ [mgstAKNJWCVFSTHBL]|mol|cd|rad|Hz|Pa|Wb|lm|lx|Bq|Gy|Sv|kat|
+ Ω | Ohm | Ω | &\#0*937; | &\#[xX]0*3[Aa]9;
+ )|
+ ### Computers units (KB, Kb, TB, Kbps)
+ [kKMGT]?(?:[oBb]|[oBb]ps|flops)|
+ ### Money
+ ¢ | ¢ | &\#0*162; | &\#[xX]0*[Aa]2; |
+ M?(?:
+ £ | £ | &\#0*163; | &\#[xX]0*[Aa]3; |
+ ¥ | ¥ | &\#0*165; | &\#[xX]0*[Aa]5; |
+ € | € | &\#0*8364; | &\#[xX]0*20[Aa][Cc]; |
+ $
+ )|
+ ### Other units
+ (?: ° | ° | &\#0*176; | &\#[xX]0*[Bb]0; ) [CF]? |
+ %|pt|pi|M?px|em|en|gal|lb|[NSEOW]|[NS][EOW]|ha|mbar
+ '; //x
+
+ protected function spaceUnit($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # before unit symbols.
+ #
+ # Example input: Get 3 mol of fun for 3 $.
+ # Example output: Get 3_mol of fun for 3_$.
+ #
+ $opt = ( $this->do_space_unit == 2 ? '?' : '' );
+ $chr = ( $this->do_space_unit != -1 ? $this->space_unit : '' );
+
+ $_ = preg_replace('/
+ (?:([0-9])[ ]'.$opt.') # Number followed by space.
+ ('.$this->units.') # Unit.
+ (?![a-zA-Z0-9]) # Negative lookahead for other unit characters.
+ /x',
+ "\\1$chr\\2", $_);
+
+ return $_;
+ }
+
+
+ protected function spaceAbbr($_) {
+ #
+ # Parameters: String, replacement character, and forcing flag.
+ # Returns: The string, with appropriates spaces replaced
+ # around abbreviations.
+ #
+ # Example input: Fun i.e. something pleasant.
+ # Example output: Fun i.e._something pleasant.
+ #
+ $opt = ( $this->do_space_abbr == 2 ? '?' : '' );
+
+ $_ = preg_replace("/(^|\s)($this->abbr_after) $opt/m",
+ "\\1\\2$this->space_abbr", $_);
+ $_ = preg_replace("/( )$opt($this->abbr_sp_before)(?![a-zA-Z'])/m",
+ "\\1$this->space_abbr\\2", $_);
+ return $_;
+ }
+
+
+ protected function stupefyEntities($_) {
+ #
+ # Adding angle quotes and lower quotes to SmartyPants's stupefy mode.
+ #
+ $_ = parent::stupefyEntities($_);
+
+ $_ = str_replace(array('„', '«', '»'), '"', $_);
+
+ return $_;
+ }
+
+
+ protected function processEscapes($_) {
+ #
+ # Adding a few more escapes to SmartyPants's escapes:
+ #
+ # Escape Value
+ # ------ -----
+ # \, ,
+ # \< <
+ # \> >
+ #
+ $_ = parent::processEscapes($_);
+
+ $_ = str_replace(
+ array('\,', '\<', '\>', '\<', '\>'),
+ array(',', '<', '>', '<', '>'), $_);
+
+ return $_;
+ }
+}
diff --git a/system/vendor/michelf/php-smartypants/Readme.md b/system/vendor/michelf/php-smartypants/Readme.md
new file mode 100644
index 0000000..5d67695
--- /dev/null
+++ b/system/vendor/michelf/php-smartypants/Readme.md
@@ -0,0 +1,246 @@
+PHP SmartyPants
+===============
+
+PHP SmartyPants Lib 1.8.1 - 12 Dec 2016
+
+by Michel Fortin
+
+
+Original SmartyPants by John Gruber
+
+
+
+Introduction
+------------
+
+This is a library package that includes the PHP SmartyPants and its
+sibling PHP SmartyPants Typographer with additional features.
+
+SmartyPants is a free web typography prettifyier tool for web writers. It
+easily translates plain ASCII punctuation characters into "smart" typographic
+punctuation HTML entities.
+
+PHP SmartyPants is a port to PHP of the original SmartyPants written
+in Perl by John Gruber.
+
+SmartyPants can perform the following transformations:
+
+* Straight quotes (`"` and `'`) into “curly” quote HTML entities
+* Backtick-style quotes (` ``like this'' `) into “curly” quote HTML
+ entities
+* Dashes (`--` and `---`) into en- and em-dash entities
+* Three consecutive dots (`...`) into an ellipsis entity
+
+SmartyPants Typographer can perform additional transformations:
+
+* French guillemets done using (`<<` and `>>`) into true « guillemets »
+ HTML entities.
+* Comma-style quotes (` ,,like this`` ` or ` ''like this,, `) into their
+ curly equivalent.
+* Replace existing spaces with non-break spaces around punctuation marks
+ where appropriate, can also add or remove them if configured to.
+* Replace existing spaces with non-break spaces for spaces used as
+ a thousand separator and between a number and the unit symbol that
+ follows it (for most common units).
+
+This means you can write, edit, and save using plain old ASCII straight
+quotes, plain dashes, and plain dots, but your published posts (and
+final HTML output) will appear with smart quotes, em-dashes, proper
+ellipses, and proper no-break spaces (with Typographer).
+
+SmartyPants does not modify characters within ``, ``,
+``, or `